From f22f928ec929c425c5aa402b5cc4d6d1fa4be238 Mon Sep 17 00:00:00 2001 From: Alexey Perevalov Date: Thu, 22 Mar 2018 21:17:22 +0300 Subject: [PATCH 0001/2380] migration: introduce postcopy-blocktime capability Right now it could be used on destination side to enable vCPU blocktime calculation for postcopy live migration. vCPU blocktime - it's time since vCPU thread was put into interruptible sleep, till memory page was copied and thread awake. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Alexey Perevalov Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-Id: <1521742647-25550-2-git-send-email-a.perevalov@samsung.com> Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 9 +++++++++ migration/migration.h | 1 + qapi/migration.json | 6 +++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index 52a5092add..bf2e8db9c7 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1541,6 +1541,15 @@ bool migrate_zero_blocks(void) return s->enabled_capabilities[MIGRATION_CAPABILITY_ZERO_BLOCKS]; } +bool migrate_postcopy_blocktime(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME]; +} + bool migrate_use_compression(void) { MigrationState *s; diff --git a/migration/migration.h b/migration/migration.h index 8d2f320c48..46a50bc789 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -230,6 +230,7 @@ int migrate_compress_level(void); int migrate_compress_threads(void); int migrate_decompress_threads(void); bool migrate_use_events(void); +bool migrate_postcopy_blocktime(void); /* Sending on the return path - generic and then for each message type */ void migrate_send_rp_shut(MigrationIncomingState *mis, diff --git a/qapi/migration.json b/qapi/migration.json index 9d0bf82cf4..24bfc190ba 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -354,16 +354,20 @@ # # @x-multifd: Use more than one fd for migration (since 2.11) # +# # @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps. # (since 2.12) # +# @postcopy-blocktime: Calculate downtime for postcopy live migration +# (since 2.13) +# # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', 'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram', 'block', 'return-path', 'pause-before-switchover', 'x-multifd', - 'dirty-bitmaps' ] } + 'dirty-bitmaps', 'postcopy-blocktime' ] } ## # @MigrationCapabilityStatus: From 2a4c42f18c987496c2c48764d4785a9d6448874a Mon Sep 17 00:00:00 2001 From: Alexey Perevalov Date: Thu, 22 Mar 2018 21:17:23 +0300 Subject: [PATCH 0002/2380] migration: add postcopy blocktime ctx into MigrationIncomingState This patch adds request to kernel space for UFFD_FEATURE_THREAD_ID, in case this feature is provided by kernel. PostcopyBlocktimeContext is encapsulated inside postcopy-ram.c, due to it being a postcopy-only feature. Also it defines PostcopyBlocktimeContext's instance live time. Information from PostcopyBlocktimeContext instance will be provided much after postcopy migration end, instance of PostcopyBlocktimeContext will live till QEMU exit, but part of it (vcpu_addr, page_fault_vcpu_time) used only during calculation, will be released when postcopy ended or failed. To enable postcopy blocktime calculation on destination, need to request proper compatibility (Patch for documentation will be at the tail of the patch set). As an example following command enable that capability, assume QEMU was started with -chardev socket,id=charmonitor,path=/var/lib/migrate-vm-monitor.sock option to control it [root@host]#printf "{\"execute\" : \"qmp_capabilities\"}\r\n \ {\"execute\": \"migrate-set-capabilities\" , \"arguments\": { \"capabilities\": [ { \"capability\": \"postcopy-blocktime\", \"state\": true } ] } }" | nc -U /var/lib/migrate-vm-monitor.sock Or just with HMP (qemu) migrate_set_capability postcopy-blocktime on Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Alexey Perevalov Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-Id: <1521742647-25550-3-git-send-email-a.perevalov@samsung.com> Signed-off-by: Dr. David Alan Gilbert --- migration/migration.h | 8 ++++++ migration/postcopy-ram.c | 61 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/migration/migration.h b/migration/migration.h index 46a50bc789..6d9aaeb480 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -22,6 +22,8 @@ #include "hw/qdev.h" #include "io/channel.h" +struct PostcopyBlocktimeContext; + /* State for the incoming migration */ struct MigrationIncomingState { QEMUFile *from_src_file; @@ -65,6 +67,12 @@ struct MigrationIncomingState { /* The coroutine we should enter (back) after failover */ Coroutine *migration_incoming_co; QemuSemaphore colo_incoming_sem; + + /* + * PostcopyBlocktimeContext to keep information for postcopy + * live migration, to calculate vCPU block time + * */ + struct PostcopyBlocktimeContext *blocktime_ctx; }; MigrationIncomingState *migration_incoming_get_current(void); diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 4a0b33b373..eddba05b57 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -90,6 +90,54 @@ int postcopy_notify(enum PostcopyNotifyReason reason, Error **errp) #include #include +typedef struct PostcopyBlocktimeContext { + /* time when page fault initiated per vCPU */ + uint32_t *page_fault_vcpu_time; + /* page address per vCPU */ + uintptr_t *vcpu_addr; + uint32_t total_blocktime; + /* blocktime per vCPU */ + uint32_t *vcpu_blocktime; + /* point in time when last page fault was initiated */ + uint32_t last_begin; + /* number of vCPU are suspended */ + int smp_cpus_down; + uint64_t start_time; + + /* + * Handler for exit event, necessary for + * releasing whole blocktime_ctx + */ + Notifier exit_notifier; +} PostcopyBlocktimeContext; + +static void destroy_blocktime_context(struct PostcopyBlocktimeContext *ctx) +{ + g_free(ctx->page_fault_vcpu_time); + g_free(ctx->vcpu_addr); + g_free(ctx->vcpu_blocktime); + g_free(ctx); +} + +static void migration_exit_cb(Notifier *n, void *data) +{ + PostcopyBlocktimeContext *ctx = container_of(n, PostcopyBlocktimeContext, + exit_notifier); + destroy_blocktime_context(ctx); +} + +static struct PostcopyBlocktimeContext *blocktime_context_new(void) +{ + PostcopyBlocktimeContext *ctx = g_new0(PostcopyBlocktimeContext, 1); + ctx->page_fault_vcpu_time = g_new0(uint32_t, smp_cpus); + ctx->vcpu_addr = g_new0(uintptr_t, smp_cpus); + ctx->vcpu_blocktime = g_new0(uint32_t, smp_cpus); + + ctx->exit_notifier.notify = migration_exit_cb; + ctx->start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + qemu_add_exit_notifier(&ctx->exit_notifier); + return ctx; +} /** * receive_ufd_features: check userfault fd features, to request only supported @@ -182,6 +230,19 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis) } } +#ifdef UFFD_FEATURE_THREAD_ID + if (migrate_postcopy_blocktime() && mis && + UFFD_FEATURE_THREAD_ID & supported_features) { + /* kernel supports that feature */ + /* don't create blocktime_context if it exists */ + if (!mis->blocktime_ctx) { + mis->blocktime_ctx = blocktime_context_new(); + } + + asked_features |= UFFD_FEATURE_THREAD_ID; + } +#endif + /* * request features, even if asked_features is 0, due to * kernel expects UFFD_API before UFFDIO_REGISTER, per From 575b0b332ea25986fd348c45a91e642bca912137 Mon Sep 17 00:00:00 2001 From: Alexey Perevalov Date: Thu, 22 Mar 2018 21:17:24 +0300 Subject: [PATCH 0003/2380] migration: calculate vCPU blocktime on dst side This patch provides blocktime calculation per vCPU, as a summary and as a overlapped value for all vCPUs. This approach was suggested by Peter Xu, as an improvements of previous approch where QEMU kept tree with faulted page address and cpus bitmask in it. Now QEMU is keeping array with faulted page address as value and vCPU as index. It helps to find proper vCPU at UFFD_COPY time. Also it keeps list for blocktime per vCPU (could be traced with page_fault_addr) Blocktime will not calculated if postcopy_blocktime field of MigrationIncomingState wasn't initialized. Signed-off-by: Alexey Perevalov Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-Id: <1521742647-25550-4-git-send-email-a.perevalov@samsung.com> Signed-off-by: Dr. David Alan Gilbert --- migration/postcopy-ram.c | 151 ++++++++++++++++++++++++++++++++++++++- migration/trace-events | 5 +- 2 files changed, 154 insertions(+), 2 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index eddba05b57..6b34a7148e 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -636,6 +636,148 @@ int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, return 0; } +static int get_mem_fault_cpu_index(uint32_t pid) +{ + CPUState *cpu_iter; + + CPU_FOREACH(cpu_iter) { + if (cpu_iter->thread_id == pid) { + trace_get_mem_fault_cpu_index(cpu_iter->cpu_index, pid); + return cpu_iter->cpu_index; + } + } + trace_get_mem_fault_cpu_index(-1, pid); + return -1; +} + +static uint32_t get_low_time_offset(PostcopyBlocktimeContext *dc) +{ + int64_t start_time_offset = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - + dc->start_time; + return start_time_offset < 1 ? 1 : start_time_offset & UINT32_MAX; +} + +/* + * This function is being called when pagefault occurs. It + * tracks down vCPU blocking time. + * + * @addr: faulted host virtual address + * @ptid: faulted process thread id + * @rb: ramblock appropriate to addr + */ +static void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, + RAMBlock *rb) +{ + int cpu, already_received; + MigrationIncomingState *mis = migration_incoming_get_current(); + PostcopyBlocktimeContext *dc = mis->blocktime_ctx; + uint32_t low_time_offset; + + if (!dc || ptid == 0) { + return; + } + cpu = get_mem_fault_cpu_index(ptid); + if (cpu < 0) { + return; + } + + low_time_offset = get_low_time_offset(dc); + if (dc->vcpu_addr[cpu] == 0) { + atomic_inc(&dc->smp_cpus_down); + } + + atomic_xchg(&dc->last_begin, low_time_offset); + atomic_xchg(&dc->page_fault_vcpu_time[cpu], low_time_offset); + atomic_xchg(&dc->vcpu_addr[cpu], addr); + + /* check it here, not at the begining of the function, + * due to, check could accur early than bitmap_set in + * qemu_ufd_copy_ioctl */ + already_received = ramblock_recv_bitmap_test(rb, (void *)addr); + if (already_received) { + atomic_xchg(&dc->vcpu_addr[cpu], 0); + atomic_xchg(&dc->page_fault_vcpu_time[cpu], 0); + atomic_dec(&dc->smp_cpus_down); + } + trace_mark_postcopy_blocktime_begin(addr, dc, dc->page_fault_vcpu_time[cpu], + cpu, already_received); +} + +/* + * This function just provide calculated blocktime per cpu and trace it. + * Total blocktime is calculated in mark_postcopy_blocktime_end. + * + * + * Assume we have 3 CPU + * + * S1 E1 S1 E1 + * -----***********------------xxx***************------------------------> CPU1 + * + * S2 E2 + * ------------****************xxx---------------------------------------> CPU2 + * + * S3 E3 + * ------------------------****xxx********-------------------------------> CPU3 + * + * We have sequence S1,S2,E1,S3,S1,E2,E3,E1 + * S2,E1 - doesn't match condition due to sequence S1,S2,E1 doesn't include CPU3 + * S3,S1,E2 - sequence includes all CPUs, in this case overlap will be S1,E2 - + * it's a part of total blocktime. + * S1 - here is last_begin + * Legend of the picture is following: + * * - means blocktime per vCPU + * x - means overlapped blocktime (total blocktime) + * + * @addr: host virtual address + */ +static void mark_postcopy_blocktime_end(uintptr_t addr) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + PostcopyBlocktimeContext *dc = mis->blocktime_ctx; + int i, affected_cpu = 0; + bool vcpu_total_blocktime = false; + uint32_t read_vcpu_time, low_time_offset; + + if (!dc) { + return; + } + + low_time_offset = get_low_time_offset(dc); + /* lookup cpu, to clear it, + * that algorithm looks straighforward, but it's not + * optimal, more optimal algorithm is keeping tree or hash + * where key is address value is a list of */ + for (i = 0; i < smp_cpus; i++) { + uint32_t vcpu_blocktime = 0; + + read_vcpu_time = atomic_fetch_add(&dc->page_fault_vcpu_time[i], 0); + if (atomic_fetch_add(&dc->vcpu_addr[i], 0) != addr || + read_vcpu_time == 0) { + continue; + } + atomic_xchg(&dc->vcpu_addr[i], 0); + vcpu_blocktime = low_time_offset - read_vcpu_time; + affected_cpu += 1; + /* we need to know is that mark_postcopy_end was due to + * faulted page, another possible case it's prefetched + * page and in that case we shouldn't be here */ + if (!vcpu_total_blocktime && + atomic_fetch_add(&dc->smp_cpus_down, 0) == smp_cpus) { + vcpu_total_blocktime = true; + } + /* continue cycle, due to one page could affect several vCPUs */ + dc->vcpu_blocktime[i] += vcpu_blocktime; + } + + atomic_sub(&dc->smp_cpus_down, affected_cpu); + if (vcpu_total_blocktime) { + dc->total_blocktime += low_time_offset - atomic_fetch_add( + &dc->last_begin, 0); + } + trace_mark_postcopy_blocktime_end(addr, dc, dc->total_blocktime, + affected_cpu); +} + /* * Handle faults detected by the USERFAULT markings */ @@ -742,7 +884,12 @@ static void *postcopy_ram_fault_thread(void *opaque) rb_offset &= ~(qemu_ram_pagesize(rb) - 1); trace_postcopy_ram_fault_thread_request(msg.arg.pagefault.address, qemu_ram_get_idstr(rb), - rb_offset); + rb_offset, + msg.arg.pagefault.feat.ptid); + mark_postcopy_blocktime_begin( + (uintptr_t)(msg.arg.pagefault.address), + msg.arg.pagefault.feat.ptid, rb); + /* * Send the request to the source - we want to request one * of our host page sizes (which is >= TPS) @@ -890,6 +1037,8 @@ static int qemu_ufd_copy_ioctl(int userfault_fd, void *host_addr, if (!ret) { ramblock_recv_bitmap_set_range(rb, host_addr, pagesize / qemu_target_page_size()); + mark_postcopy_blocktime_end((uintptr_t)host_addr); + } return ret; } diff --git a/migration/trace-events b/migration/trace-events index a180d7b008..368bc4b663 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -115,6 +115,8 @@ process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d" process_incoming_migration_co_postcopy_end_main(void) "" migration_set_incoming_channel(void *ioc, const char *ioctype) "ioc=%p ioctype=%s" migration_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname, void *err) "ioc=%p ioctype=%s hostname=%s err=%p" +mark_postcopy_blocktime_begin(uint64_t addr, void *dd, uint32_t time, int cpu, int received) "addr: 0x%" PRIx64 ", dd: %p, time: %u, cpu: %d, already_received: %d" +mark_postcopy_blocktime_end(uint64_t addr, void *dd, uint32_t time, int affected_cpu) "addr: 0x%" PRIx64 ", dd: %p, time: %u, affected_cpu: %d" # migration/rdma.c qemu_rdma_accept_incoming_migration(void) "" @@ -193,7 +195,7 @@ postcopy_ram_fault_thread_exit(void) "" postcopy_ram_fault_thread_fds_core(int baseufd, int quitfd) "ufd: %d quitfd: %d" postcopy_ram_fault_thread_fds_extra(size_t index, const char *name, int fd) "%zd/%s: %d" postcopy_ram_fault_thread_quit(void) "" -postcopy_ram_fault_thread_request(uint64_t hostaddr, const char *ramblock, size_t offset) "Request for HVA=0x%" PRIx64 " rb=%s offset=0x%zx" +postcopy_ram_fault_thread_request(uint64_t hostaddr, const char *ramblock, size_t offset, uint32_t pid) "Request for HVA=0x%" PRIx64 " rb=%s offset=0x%zx pid=%u" postcopy_ram_incoming_cleanup_closeuf(void) "" postcopy_ram_incoming_cleanup_entry(void) "" postcopy_ram_incoming_cleanup_exit(void) "" @@ -206,6 +208,7 @@ save_xbzrle_page_skipping(void) "" save_xbzrle_page_overflow(void) "" ram_save_iterate_big_wait(uint64_t milliconds, int iterations) "big wait: %" PRIu64 " milliseconds, %d iterations" ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRIu64 +get_mem_fault_cpu_index(int cpu, uint32_t pid) "cpu: %d, pid: %u" # migration/exec.c migration_exec_outgoing(const char *cmd) "cmd=%s" From 9ed01779e8984b71cf62e4732de8d05ff091df36 Mon Sep 17 00:00:00 2001 From: Alexey Perevalov Date: Thu, 22 Mar 2018 21:17:25 +0300 Subject: [PATCH 0004/2380] migration: postcopy_blocktime documentation Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Alexey Perevalov Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-Id: <1521742647-25550-5-git-send-email-a.perevalov@samsung.com> Signed-off-by: Dr. David Alan Gilbert --- docs/devel/migration.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index e32b087f6e..9342a8af06 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -401,6 +401,20 @@ will now cause the transition from precopy to postcopy. It can be issued immediately after migration is started or any time later on. Issuing it after the end of a migration is harmless. +Blocktime is a postcopy live migration metric, intended to show how +long the vCPU was in state of interruptable sleep due to pagefault. +That metric is calculated both for all vCPUs as overlapped value, and +separately for each vCPU. These values are calculated on destination +side. To enable postcopy blocktime calculation, enter following +command on destination monitor: + +``migrate_set_capability postcopy-blocktime on`` + +Postcopy blocktime can be retrieved by query-migrate qmp command. +postcopy-blocktime value of qmp command will show overlapped blocking +time for all vCPU, postcopy-vcpu-blocktime will show list of blocking +time per vCPU. + .. note:: During the postcopy phase, the bandwidth limits set using ``migrate_set_speed`` is ignored (to avoid delaying requested pages that From 346f3dab04ee2391e521fd276883e63959eb9ea9 Mon Sep 17 00:00:00 2001 From: Alexey Perevalov Date: Thu, 22 Mar 2018 21:17:26 +0300 Subject: [PATCH 0005/2380] migration: add blocktime calculation into migration-test This patch just requests blocktime calculation, and check it in case when UFFD_FEATURE_THREAD_ID feature is set on the host. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Alexey Perevalov Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-Id: <1521742647-25550-6-git-send-email-a.perevalov@samsung.com> Signed-off-by: Dr. David Alan Gilbert --- tests/migration-test.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/migration-test.c b/tests/migration-test.c index 422bf1afdf..dde7c464c3 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -26,6 +26,7 @@ const unsigned start_address = 1024 * 1024; const unsigned end_address = 100 * 1024 * 1024; bool got_stop; +static bool uffd_feature_thread_id; #if defined(__linux__) #include @@ -55,6 +56,7 @@ static bool ufd_version_check(void) g_test_message("Skipping test: UFFDIO_API failed"); return false; } + uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; ioctl_mask = (__u64)1 << _UFFDIO_REGISTER | (__u64)1 << _UFFDIO_UNREGISTER; @@ -223,6 +225,16 @@ static uint64_t get_migration_pass(QTestState *who) return result; } +static void read_blocktime(QTestState *who) +{ + QDict *rsp, *rsp_return; + + rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); + rsp_return = qdict_get_qdict(rsp, "return"); + g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); + QDECREF(rsp); +} + static void wait_for_migration_complete(QTestState *who) { while (true) { @@ -533,6 +545,7 @@ static void test_migrate(void) migrate_set_capability(from, "postcopy-ram", "true"); migrate_set_capability(to, "postcopy-ram", "true"); + migrate_set_capability(to, "postcopy-blocktime", "true"); /* We want to pick a speed slow enough that the test completes * quickly, but that it doesn't complete precopy even on a slow @@ -559,6 +572,9 @@ static void test_migrate(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); + if (uffd_feature_thread_id) { + read_blocktime(to); + } g_free(uri); test_migrate_end(from, to, true); From 65ace060455122a461cdc9302238b914084bcd42 Mon Sep 17 00:00:00 2001 From: Alexey Perevalov Date: Thu, 22 Mar 2018 21:17:27 +0300 Subject: [PATCH 0006/2380] migration: add postcopy total blocktime into query-migrate Postcopy total blocktime is available on destination side only. But query-migrate was possible only for source. This patch adds ability to call query-migrate on destination. To be able to see postcopy blocktime, need to request postcopy-blocktime capability. The query-migrate command will show following sample result: {"return": "postcopy-vcpu-blocktime": [115, 100], "status": "completed", "postcopy-blocktime": 100 }} postcopy_vcpu_blocktime contains list, where the first item is the first vCPU in QEMU. This patch has a drawback, it combines states of incoming and outgoing migration. Ongoing migration state will overwrite incoming state. Looks like better to separate query-migrate for incoming and outgoing migration or add parameter to indicate type of migration. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Alexey Perevalov Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-Id: <1521742647-25550-7-git-send-email-a.perevalov@samsung.com> Signed-off-by: Dr. David Alan Gilbert --- hmp.c | 15 +++++++++++ migration/migration.c | 42 +++++++++++++++++++++++++++--- migration/migration.h | 4 +++ migration/postcopy-ram.c | 56 ++++++++++++++++++++++++++++++++++++++++ migration/trace-events | 1 + qapi/migration.json | 11 +++++++- 6 files changed, 124 insertions(+), 5 deletions(-) diff --git a/hmp.c b/hmp.c index a25c7bd9a8..898e25f3e1 100644 --- a/hmp.c +++ b/hmp.c @@ -274,6 +274,21 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->cpu_throttle_percentage); } + if (info->has_postcopy_blocktime) { + monitor_printf(mon, "postcopy blocktime: %u\n", + info->postcopy_blocktime); + } + + if (info->has_postcopy_vcpu_blocktime) { + Visitor *v; + char *str; + v = string_output_visitor_new(false, &str); + visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime, NULL); + visit_complete(v, &str); + monitor_printf(mon, "postcopy vcpu blocktime: %s\n", str); + g_free(str); + visit_free(v); + } qapi_free_MigrationInfo(info); qapi_free_MigrationCapabilityStatusList(caps); } diff --git a/migration/migration.c b/migration/migration.c index bf2e8db9c7..0bdb28e144 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -630,14 +630,15 @@ static void populate_disk_info(MigrationInfo *info) } } -MigrationInfo *qmp_query_migrate(Error **errp) +static void fill_source_migration_info(MigrationInfo *info) { - MigrationInfo *info = g_malloc0(sizeof(*info)); MigrationState *s = migrate_get_current(); switch (s->state) { case MIGRATION_STATUS_NONE: /* no migration has happened ever */ + /* do not overwrite destination migration status */ + return; break; case MIGRATION_STATUS_SETUP: info->has_status = true; @@ -688,8 +689,6 @@ MigrationInfo *qmp_query_migrate(Error **errp) break; } info->status = s->state; - - return info; } /** @@ -753,6 +752,41 @@ static bool migrate_caps_check(bool *cap_list, return true; } +static void fill_destination_migration_info(MigrationInfo *info) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + + switch (mis->state) { + case MIGRATION_STATUS_NONE: + return; + break; + case MIGRATION_STATUS_SETUP: + case MIGRATION_STATUS_CANCELLING: + case MIGRATION_STATUS_CANCELLED: + case MIGRATION_STATUS_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_ACTIVE: + case MIGRATION_STATUS_FAILED: + case MIGRATION_STATUS_COLO: + info->has_status = true; + break; + case MIGRATION_STATUS_COMPLETED: + info->has_status = true; + fill_destination_postcopy_migration_info(info); + break; + } + info->status = mis->state; +} + +MigrationInfo *qmp_query_migrate(Error **errp) +{ + MigrationInfo *info = g_malloc0(sizeof(*info)); + + fill_destination_migration_info(info); + fill_source_migration_info(info); + + return info; +} + void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, Error **errp) { diff --git a/migration/migration.h b/migration/migration.h index 6d9aaeb480..7c69598c54 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -77,6 +77,10 @@ struct MigrationIncomingState { MigrationIncomingState *migration_incoming_get_current(void); void migration_incoming_state_destroy(void); +/* + * Functions to work with blocktime context + */ +void fill_destination_postcopy_migration_info(MigrationInfo *info); #define TYPE_MIGRATION "migration" diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 6b34a7148e..8ceeaa2a93 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -139,6 +139,55 @@ static struct PostcopyBlocktimeContext *blocktime_context_new(void) return ctx; } +static uint32List *get_vcpu_blocktime_list(PostcopyBlocktimeContext *ctx) +{ + uint32List *list = NULL, *entry = NULL; + int i; + + for (i = smp_cpus - 1; i >= 0; i--) { + entry = g_new0(uint32List, 1); + entry->value = ctx->vcpu_blocktime[i]; + entry->next = list; + list = entry; + } + + return list; +} + +/* + * This function just populates MigrationInfo from postcopy's + * blocktime context. It will not populate MigrationInfo, + * unless postcopy-blocktime capability was set. + * + * @info: pointer to MigrationInfo to populate + */ +void fill_destination_postcopy_migration_info(MigrationInfo *info) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + PostcopyBlocktimeContext *bc = mis->blocktime_ctx; + + if (!bc) { + return; + } + + info->has_postcopy_blocktime = true; + info->postcopy_blocktime = bc->total_blocktime; + info->has_postcopy_vcpu_blocktime = true; + info->postcopy_vcpu_blocktime = get_vcpu_blocktime_list(bc); +} + +static uint32_t get_postcopy_total_blocktime(void) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + PostcopyBlocktimeContext *bc = mis->blocktime_ctx; + + if (!bc) { + return 0; + } + + return bc->total_blocktime; +} + /** * receive_ufd_features: check userfault fd features, to request only supported * features in the future. @@ -512,6 +561,9 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) munmap(mis->postcopy_tmp_zero_page, mis->largest_page_size); mis->postcopy_tmp_zero_page = NULL; } + trace_postcopy_ram_incoming_cleanup_blocktime( + get_postcopy_total_blocktime()); + trace_postcopy_ram_incoming_cleanup_exit(); return 0; } @@ -1157,6 +1209,10 @@ void *postcopy_get_tmp_page(MigrationIncomingState *mis) #else /* No target OS support, stubs just fail */ +void fill_destination_postcopy_migration_info(MigrationInfo *info) +{ +} + bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) { error_report("%s: No OS support", __func__); diff --git a/migration/trace-events b/migration/trace-events index 368bc4b663..d6be74b7a7 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -200,6 +200,7 @@ postcopy_ram_incoming_cleanup_closeuf(void) "" postcopy_ram_incoming_cleanup_entry(void) "" postcopy_ram_incoming_cleanup_exit(void) "" postcopy_ram_incoming_cleanup_join(void) "" +postcopy_ram_incoming_cleanup_blocktime(uint64_t total) "total blocktime %" PRIu64 postcopy_request_shared_page(const char *sharer, const char *rb, uint64_t rb_offset) "for %s in %s offset 0x%"PRIx64 postcopy_request_shared_page_present(const char *sharer, const char *rb, uint64_t rb_offset) "%s already %s offset 0x%"PRIx64 postcopy_wake_shared(uint64_t client_addr, const char *rb) "at 0x%"PRIx64" in %s" diff --git a/qapi/migration.json b/qapi/migration.json index 24bfc190ba..f3974c6807 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -155,6 +155,13 @@ # @error-desc: the human readable error description string, when # @status is 'failed'. Clients should not attempt to parse the # error strings. (Since 2.7) +# +# @postcopy-blocktime: total time when all vCPU were blocked during postcopy +# live migration (Since 2.13) +# +# @postcopy-vcpu-blocktime: list of the postcopy blocktime per vCPU (Since 2.13) +# + # # Since: 0.14.0 ## @@ -167,7 +174,9 @@ '*downtime': 'int', '*setup-time': 'int', '*cpu-throttle-percentage': 'int', - '*error-desc': 'str'} } + '*error-desc': 'str', + '*postcopy-blocktime' : 'uint32', + '*postcopy-vcpu-blocktime': ['uint32']} } ## # @query-migrate: From 263a289ae61c8344a417a95b0142650fdff3af56 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Fri, 30 Mar 2018 15:51:19 +0800 Subject: [PATCH 0007/2380] migration: stop compressing page in migration thread As compression is a heavy work, do not do it in migration thread, instead, we post it out as a normal page Reviewed-by: Wei Wang Reviewed-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Xiao Guangrong Message-Id: <20180330075128.26919-2-xiaoguangrong@tencent.com> Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 0e90efa092..409c847a76 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1137,7 +1137,7 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, int pages = -1; uint64_t bytes_xmit = 0; uint8_t *p; - int ret, blen; + int ret; RAMBlock *block = pss->block; ram_addr_t offset = pss->page << TARGET_PAGE_BITS; @@ -1167,23 +1167,23 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, if (block != rs->last_sent_block) { flush_compressed_data(rs); pages = save_zero_page(rs, block, offset); - if (pages == -1) { - /* Make sure the first page is sent out before other pages */ - bytes_xmit = save_page_header(rs, rs->f, block, offset | - RAM_SAVE_FLAG_COMPRESS_PAGE); - blen = qemu_put_compression_data(rs->f, p, TARGET_PAGE_SIZE, - migrate_compress_level()); - if (blen > 0) { - ram_counters.transferred += bytes_xmit + blen; - ram_counters.normal++; - pages = 1; - } else { - qemu_file_set_error(rs->f, blen); - error_report("compressed data failed!"); - } - } if (pages > 0) { ram_release_pages(block->idstr, offset, pages); + } else { + /* + * Make sure the first page is sent out before other pages. + * + * we post it as normal page as compression will take much + * CPU resource. + */ + ram_counters.transferred += save_page_header(rs, rs->f, block, + offset | RAM_SAVE_FLAG_PAGE); + qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, + migrate_release_ram() & + migration_in_postcopy()); + ram_counters.transferred += TARGET_PAGE_SIZE; + ram_counters.normal++; + pages = 1; } } else { pages = save_zero_page(rs, block, offset); From dcaf446ebda5d87e05eb41cdbafb7ae4a7cc4a62 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Fri, 30 Mar 2018 15:51:20 +0800 Subject: [PATCH 0008/2380] migration: stop compression to allocate and free memory frequently Current code uses compress2() to compress memory which manages memory internally, that causes huge memory is allocated and freed very frequently More worse, frequently returning memory to kernel will flush TLBs and trigger invalidation callbacks on mmu-notification which interacts with KVM MMU, that dramatically reduce the performance of VM So, we maintain the memory by ourselves and reuse it for each compression Reviewed-by: Peter Xu Reviewed-by: Jiang Biao Signed-off-by: Xiao Guangrong Message-Id: <20180330075128.26919-3-xiaoguangrong@tencent.com> Signed-off-by: Dr. David Alan Gilbert --- migration/qemu-file.c | 39 ++++++++++++++++++++++++++++++++------- migration/qemu-file.h | 6 ++++-- migration/ram.c | 41 ++++++++++++++++++++++++++++++++--------- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index bb63c779cc..bafe3a0c0d 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -658,8 +658,32 @@ uint64_t qemu_get_be64(QEMUFile *f) return v; } -/* Compress size bytes of data start at p with specific compression - * level and store the compressed data to the buffer of f. +/* return the size after compression, or negative value on error */ +static int qemu_compress_data(z_stream *stream, uint8_t *dest, size_t dest_len, + const uint8_t *source, size_t source_len) +{ + int err; + + err = deflateReset(stream); + if (err != Z_OK) { + return -1; + } + + stream->avail_in = source_len; + stream->next_in = (uint8_t *)source; + stream->avail_out = dest_len; + stream->next_out = dest; + + err = deflate(stream, Z_FINISH); + if (err != Z_STREAM_END) { + return -1; + } + + return stream->next_out - dest; +} + +/* Compress size bytes of data start at p and store the compressed + * data to the buffer of f. * * When f is not writable, return -1 if f has no space to save the * compressed data. @@ -667,9 +691,8 @@ uint64_t qemu_get_be64(QEMUFile *f) * do fflush first, if f still has no space to save the compressed * data, return -1. */ - -ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, - int level) +ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, + const uint8_t *p, size_t size) { ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t); @@ -683,8 +706,10 @@ ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, return -1; } } - if (compress2(f->buf + f->buf_index + sizeof(int32_t), (uLongf *)&blen, - (Bytef *)p, size, level) != Z_OK) { + + blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t), + blen, p, size); + if (blen < 0) { error_report("Compress Failed!"); return 0; } diff --git a/migration/qemu-file.h b/migration/qemu-file.h index f4f356ab12..2ccfcfb2a8 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -25,6 +25,8 @@ #ifndef MIGRATION_QEMU_FILE_H #define MIGRATION_QEMU_FILE_H +#include + /* Read a chunk of data from a file at the given position. The pos argument * can be ignored if the file is only be used for streaming. The number of * bytes actually read should be returned. @@ -132,8 +134,8 @@ bool qemu_file_is_writable(QEMUFile *f); size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); -ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, - int level); +ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, + const uint8_t *p, size_t size); int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); /* diff --git a/migration/ram.c b/migration/ram.c index 409c847a76..a21514a469 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -269,6 +269,7 @@ struct CompressParam { QemuCond cond; RAMBlock *block; ram_addr_t offset; + z_stream stream; }; typedef struct CompressParam CompressParam; @@ -299,7 +300,7 @@ static QemuThread *decompress_threads; static QemuMutex decomp_done_lock; static QemuCond decomp_done_cond; -static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, +static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, ram_addr_t offset); static void *do_data_compress(void *opaque) @@ -316,7 +317,7 @@ static void *do_data_compress(void *opaque) param->block = NULL; qemu_mutex_unlock(¶m->mutex); - do_compress_ram_page(param->file, block, offset); + do_compress_ram_page(param->file, ¶m->stream, block, offset); qemu_mutex_lock(&comp_done_lock); param->done = true; @@ -357,10 +358,19 @@ static void compress_threads_save_cleanup(void) terminate_compression_threads(); thread_count = migrate_compress_threads(); for (i = 0; i < thread_count; i++) { + /* + * we use it as a indicator which shows if the thread is + * properly init'd or not + */ + if (!comp_param[i].file) { + break; + } 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); + deflateEnd(&comp_param[i].stream); + qemu_fclose(comp_param[i].file); + comp_param[i].file = NULL; } qemu_mutex_destroy(&comp_done_lock); qemu_cond_destroy(&comp_done_cond); @@ -370,12 +380,12 @@ static void compress_threads_save_cleanup(void) comp_param = NULL; } -static void compress_threads_save_setup(void) +static int compress_threads_save_setup(void) { int i, thread_count; if (!migrate_use_compression()) { - return; + return 0; } thread_count = migrate_compress_threads(); compress_threads = g_new0(QemuThread, thread_count); @@ -383,6 +393,11 @@ static void compress_threads_save_setup(void) qemu_cond_init(&comp_done_cond); qemu_mutex_init(&comp_done_lock); for (i = 0; i < thread_count; i++) { + if (deflateInit(&comp_param[i].stream, + migrate_compress_level()) != Z_OK) { + goto exit; + } + /* comp_param[i].file is just used as a dummy buffer to save data, * set its ops to empty. */ @@ -395,6 +410,11 @@ static void compress_threads_save_setup(void) do_data_compress, comp_param + i, QEMU_THREAD_JOINABLE); } + return 0; + +exit: + compress_threads_save_cleanup(); + return -1; } /* Multiple fd's */ @@ -1031,7 +1051,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) return pages; } -static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, +static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, ram_addr_t offset) { RAMState *rs = ram_state; @@ -1040,8 +1060,7 @@ static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, bytes_sent = save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); - blen = qemu_put_compression_data(f, p, TARGET_PAGE_SIZE, - migrate_compress_level()); + blen = qemu_put_compression_data(f, stream, p, TARGET_PAGE_SIZE); if (blen < 0) { bytes_sent = 0; qemu_file_set_error(migrate_get_current()->to_dst_file, blen); @@ -2214,9 +2233,14 @@ static int ram_save_setup(QEMUFile *f, void *opaque) RAMState **rsp = opaque; RAMBlock *block; + if (compress_threads_save_setup()) { + return -1; + } + /* migration has already setup the bitmap, reuse it. */ if (!migration_in_colo_state()) { if (ram_init_all(rsp) != 0) { + compress_threads_save_cleanup(); return -1; } } @@ -2236,7 +2260,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque) } rcu_read_unlock(); - compress_threads_save_setup(); ram_control_before_iterate(f, RAM_CONTROL_SETUP); ram_control_after_iterate(f, RAM_CONTROL_SETUP); From 797ca154b4c68dbd8e93382f714388ab311f672d Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Fri, 30 Mar 2018 15:51:21 +0800 Subject: [PATCH 0009/2380] migration: stop decompression to allocate and free memory frequently Current code uses uncompress() to decompress memory which manages memory internally, that causes huge memory is allocated and freed very frequently, more worse, frequently returning memory to kernel will flush TLBs So, we maintain the memory by ourselves and reuse it for each decompression Reviewed-by: Peter Xu Reviewed-by: Jiang Biao Signed-off-by: Xiao Guangrong Message-Id: <20180330075128.26919-4-xiaoguangrong@tencent.com> Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 112 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 30 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index a21514a469..fb24b2f32f 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -281,6 +281,7 @@ struct DecompressParam { void *des; uint8_t *compbuf; int len; + z_stream stream; }; typedef struct DecompressParam DecompressParam; @@ -2524,6 +2525,31 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) } } +/* return the size after decompression, or negative value on error */ +static int +qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, + const uint8_t *source, size_t source_len) +{ + int err; + + err = inflateReset(stream); + if (err != Z_OK) { + return -1; + } + + stream->avail_in = source_len; + stream->next_in = (uint8_t *)source; + stream->avail_out = dest_len; + stream->next_out = dest; + + err = inflate(stream, Z_NO_FLUSH); + if (err != Z_STREAM_END) { + return -1; + } + + return stream->total_out; +} + static void *do_data_decompress(void *opaque) { DecompressParam *param = opaque; @@ -2540,13 +2566,13 @@ static void *do_data_decompress(void *opaque) qemu_mutex_unlock(¶m->mutex); pagesize = TARGET_PAGE_SIZE; - /* 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 + /* qemu_uncompress_data() will return failed in some case, + * especially when the page is dirtied 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 *)des, &pagesize, - (const Bytef *)param->compbuf, len); + qemu_uncompress_data(¶m->stream, des, pagesize, param->compbuf, + len); qemu_mutex_lock(&decomp_done_lock); param->done = true; @@ -2581,30 +2607,6 @@ static void wait_for_decompress_done(void) qemu_mutex_unlock(&decomp_done_lock); } -static void compress_threads_load_setup(void) -{ - int i, thread_count; - - if (!migrate_use_compression()) { - return; - } - thread_count = migrate_decompress_threads(); - decompress_threads = g_new0(QemuThread, thread_count); - decomp_param = g_new0(DecompressParam, thread_count); - qemu_mutex_init(&decomp_done_lock); - qemu_cond_init(&decomp_done_cond); - 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)); - decomp_param[i].done = true; - decomp_param[i].quit = false; - qemu_thread_create(decompress_threads + i, "decompress", - do_data_decompress, decomp_param + i, - QEMU_THREAD_JOINABLE); - } -} - static void compress_threads_load_cleanup(void) { int i, thread_count; @@ -2614,16 +2616,30 @@ static void compress_threads_load_cleanup(void) } thread_count = migrate_decompress_threads(); for (i = 0; i < thread_count; i++) { + /* + * we use it as a indicator which shows if the thread is + * properly init'd or not + */ + if (!decomp_param[i].compbuf) { + break; + } + qemu_mutex_lock(&decomp_param[i].mutex); decomp_param[i].quit = true; qemu_cond_signal(&decomp_param[i].cond); qemu_mutex_unlock(&decomp_param[i].mutex); } for (i = 0; i < thread_count; i++) { + if (!decomp_param[i].compbuf) { + break; + } + qemu_thread_join(decompress_threads + i); qemu_mutex_destroy(&decomp_param[i].mutex); qemu_cond_destroy(&decomp_param[i].cond); + inflateEnd(&decomp_param[i].stream); g_free(decomp_param[i].compbuf); + decomp_param[i].compbuf = NULL; } g_free(decompress_threads); g_free(decomp_param); @@ -2631,6 +2647,39 @@ static void compress_threads_load_cleanup(void) decomp_param = NULL; } +static int compress_threads_load_setup(void) +{ + int i, thread_count; + + if (!migrate_use_compression()) { + return 0; + } + + thread_count = migrate_decompress_threads(); + decompress_threads = g_new0(QemuThread, thread_count); + decomp_param = g_new0(DecompressParam, thread_count); + qemu_mutex_init(&decomp_done_lock); + qemu_cond_init(&decomp_done_cond); + for (i = 0; i < thread_count; i++) { + if (inflateInit(&decomp_param[i].stream) != Z_OK) { + goto exit; + } + + decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); + qemu_mutex_init(&decomp_param[i].mutex); + qemu_cond_init(&decomp_param[i].cond); + decomp_param[i].done = true; + decomp_param[i].quit = false; + qemu_thread_create(decompress_threads + i, "decompress", + do_data_decompress, decomp_param + i, + QEMU_THREAD_JOINABLE); + } + return 0; +exit: + compress_threads_load_cleanup(); + return -1; +} + static void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len) { @@ -2670,8 +2719,11 @@ static void decompress_data_with_multi_threads(QEMUFile *f, */ static int ram_load_setup(QEMUFile *f, void *opaque) { + if (compress_threads_load_setup()) { + return -1; + } + xbzrle_load_setup(); - compress_threads_load_setup(); ramblock_recv_map_init(); return 0; } From 34ab9e9743aeaf265929d930747f101fa5c76fea Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Fri, 30 Mar 2018 15:51:22 +0800 Subject: [PATCH 0010/2380] migration: detect compression and decompression errors Currently the page being compressed is allowed to be updated by the VM on the source QEMU, correspondingly the destination QEMU just ignores the decompression error. However, we completely miss the chance to catch real errors, then the VM is corrupted silently To make the migration more robuster, we copy the page to a buffer first to avoid it being written by VM, then detect and handle the errors of both compression and decompression errors properly Reviewed-by: Peter Xu Signed-off-by: Xiao Guangrong Message-Id: <20180330075128.26919-5-xiaoguangrong@tencent.com> Signed-off-by: Dr. David Alan Gilbert --- migration/qemu-file.c | 4 ++-- migration/ram.c | 56 ++++++++++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index bafe3a0c0d..0463f4c321 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -710,9 +710,9 @@ ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t), blen, p, size); if (blen < 0) { - error_report("Compress Failed!"); - return 0; + return -1; } + qemu_put_be32(f, blen); if (f->ops->writev_buffer) { add_to_iovec(f, f->buf + f->buf_index, blen, false); diff --git a/migration/ram.c b/migration/ram.c index fb24b2f32f..72cb8dfb66 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -269,7 +269,10 @@ struct CompressParam { QemuCond cond; RAMBlock *block; ram_addr_t offset; + + /* internally used fields */ z_stream stream; + uint8_t *originbuf; }; typedef struct CompressParam CompressParam; @@ -296,13 +299,14 @@ static QemuCond comp_done_cond; /* The empty QEMUFileOps will be used by file in CompressParam */ static const QEMUFileOps empty_ops = { }; +static QEMUFile *decomp_file; static DecompressParam *decomp_param; static QemuThread *decompress_threads; static QemuMutex decomp_done_lock; static QemuCond decomp_done_cond; static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, - ram_addr_t offset); + ram_addr_t offset, uint8_t *source_buf); static void *do_data_compress(void *opaque) { @@ -318,7 +322,8 @@ static void *do_data_compress(void *opaque) param->block = NULL; qemu_mutex_unlock(¶m->mutex); - do_compress_ram_page(param->file, ¶m->stream, block, offset); + do_compress_ram_page(param->file, ¶m->stream, block, offset, + param->originbuf); qemu_mutex_lock(&comp_done_lock); param->done = true; @@ -370,6 +375,7 @@ static void compress_threads_save_cleanup(void) qemu_mutex_destroy(&comp_param[i].mutex); qemu_cond_destroy(&comp_param[i].cond); deflateEnd(&comp_param[i].stream); + g_free(comp_param[i].originbuf); qemu_fclose(comp_param[i].file); comp_param[i].file = NULL; } @@ -394,8 +400,14 @@ static int compress_threads_save_setup(void) qemu_cond_init(&comp_done_cond); qemu_mutex_init(&comp_done_lock); for (i = 0; i < thread_count; i++) { + comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE); + if (!comp_param[i].originbuf) { + goto exit; + } + if (deflateInit(&comp_param[i].stream, migrate_compress_level()) != Z_OK) { + g_free(comp_param[i].originbuf); goto exit; } @@ -1053,7 +1065,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) } static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, - ram_addr_t offset) + ram_addr_t offset, uint8_t *source_buf) { RAMState *rs = ram_state; int bytes_sent, blen; @@ -1061,7 +1073,14 @@ static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, bytes_sent = save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); - blen = qemu_put_compression_data(f, stream, p, TARGET_PAGE_SIZE); + + /* + * copy it to a internal buffer to avoid it being modified by VM + * so that we can catch up the error during compression and + * decompression + */ + memcpy(source_buf, p, TARGET_PAGE_SIZE); + blen = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE); if (blen < 0) { bytes_sent = 0; qemu_file_set_error(migrate_get_current()->to_dst_file, blen); @@ -2555,7 +2574,7 @@ static void *do_data_decompress(void *opaque) DecompressParam *param = opaque; unsigned long pagesize; uint8_t *des; - int len; + int len, ret; qemu_mutex_lock(¶m->mutex); while (!param->quit) { @@ -2566,13 +2585,13 @@ static void *do_data_decompress(void *opaque) qemu_mutex_unlock(¶m->mutex); pagesize = TARGET_PAGE_SIZE; - /* qemu_uncompress_data() will return failed in some case, - * especially when the page is dirtied 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. - */ - qemu_uncompress_data(¶m->stream, des, pagesize, param->compbuf, - len); + + ret = qemu_uncompress_data(¶m->stream, des, pagesize, + param->compbuf, len); + if (ret < 0) { + error_report("decompress data failed"); + qemu_file_set_error(decomp_file, ret); + } qemu_mutex_lock(&decomp_done_lock); param->done = true; @@ -2589,12 +2608,12 @@ static void *do_data_decompress(void *opaque) return NULL; } -static void wait_for_decompress_done(void) +static int wait_for_decompress_done(void) { int idx, thread_count; if (!migrate_use_compression()) { - return; + return 0; } thread_count = migrate_decompress_threads(); @@ -2605,6 +2624,7 @@ static void wait_for_decompress_done(void) } } qemu_mutex_unlock(&decomp_done_lock); + return qemu_file_get_error(decomp_file); } static void compress_threads_load_cleanup(void) @@ -2645,9 +2665,10 @@ static void compress_threads_load_cleanup(void) g_free(decomp_param); decompress_threads = NULL; decomp_param = NULL; + decomp_file = NULL; } -static int compress_threads_load_setup(void) +static int compress_threads_load_setup(QEMUFile *f) { int i, thread_count; @@ -2660,6 +2681,7 @@ static int compress_threads_load_setup(void) decomp_param = g_new0(DecompressParam, thread_count); qemu_mutex_init(&decomp_done_lock); qemu_cond_init(&decomp_done_cond); + decomp_file = f; for (i = 0; i < thread_count; i++) { if (inflateInit(&decomp_param[i].stream) != Z_OK) { goto exit; @@ -2719,7 +2741,7 @@ static void decompress_data_with_multi_threads(QEMUFile *f, */ static int ram_load_setup(QEMUFile *f, void *opaque) { - if (compress_threads_load_setup()) { + if (compress_threads_load_setup(f)) { return -1; } @@ -3074,7 +3096,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) } } - wait_for_decompress_done(); + ret |= wait_for_decompress_done(); rcu_read_unlock(); trace_ram_load_complete(ret, seq_iter); return ret; From 059ff0fb29dd3a56ac2843676915efc279938c6b Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Fri, 30 Mar 2018 15:51:23 +0800 Subject: [PATCH 0011/2380] migration: introduce control_save_page() Abstract the common function control_save_page() to cleanup the code, no logic is changed Reviewed-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Xiao Guangrong Message-Id: <20180330075128.26919-6-xiaoguangrong@tencent.com> Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 174 +++++++++++++++++++++++++----------------------- 1 file changed, 89 insertions(+), 85 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 72cb8dfb66..79c7958993 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -974,6 +974,44 @@ static void ram_release_pages(const char *rbname, uint64_t offset, int pages) ram_discard_range(rbname, offset, pages << TARGET_PAGE_BITS); } +/* + * @pages: the number of pages written by the control path, + * < 0 - error + * > 0 - number of pages written + * + * Return true if the pages has been saved, otherwise false is returned. + */ +static bool control_save_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, + int *pages) +{ + uint64_t bytes_xmit = 0; + int ret; + + *pages = -1; + ret = ram_control_save_page(rs->f, block->offset, offset, TARGET_PAGE_SIZE, + &bytes_xmit); + if (ret == RAM_SAVE_CONTROL_NOT_SUPP) { + return false; + } + + if (bytes_xmit) { + ram_counters.transferred += bytes_xmit; + *pages = 1; + } + + if (ret == RAM_SAVE_CONTROL_DELAYED) { + return true; + } + + if (bytes_xmit > 0) { + ram_counters.normal++; + } else if (bytes_xmit == 0) { + ram_counters.duplicate++; + } + + return true; +} + /** * ram_save_page: send the given page to the stream * @@ -990,56 +1028,36 @@ static void ram_release_pages(const char *rbname, uint64_t offset, int pages) static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) { int pages = -1; - uint64_t bytes_xmit; - ram_addr_t current_addr; uint8_t *p; - int ret; bool send_async = true; RAMBlock *block = pss->block; ram_addr_t offset = pss->page << TARGET_PAGE_BITS; + ram_addr_t current_addr = block->offset + offset; p = block->host + offset; trace_ram_save_page(block->idstr, (uint64_t)offset, p); - /* In doubt sent page as normal */ - bytes_xmit = 0; - ret = ram_control_save_page(rs->f, block->offset, - offset, TARGET_PAGE_SIZE, &bytes_xmit); - if (bytes_xmit) { - ram_counters.transferred += bytes_xmit; - pages = 1; + if (control_save_page(rs, block, offset, &pages)) { + return pages; } XBZRLE_cache_lock(); - - current_addr = block->offset + offset; - - if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { - if (ret != RAM_SAVE_CONTROL_DELAYED) { - if (bytes_xmit > 0) { - ram_counters.normal++; - } else if (bytes_xmit == 0) { - ram_counters.duplicate++; - } - } - } else { - pages = save_zero_page(rs, block, offset); - if (pages > 0) { - /* Must let xbzrle know, otherwise a previous (now 0'd) cached - * page would be stale + pages = save_zero_page(rs, block, offset); + if (pages > 0) { + /* Must let xbzrle know, otherwise a previous (now 0'd) cached + * page would be stale + */ + xbzrle_cache_zero_page(rs, current_addr); + ram_release_pages(block->idstr, offset, pages); + } else if (!rs->ram_bulk_stage && + !migration_in_postcopy() && migrate_use_xbzrle()) { + pages = save_xbzrle_page(rs, &p, current_addr, block, + offset, last_stage); + if (!last_stage) { + /* Can't send this cached data async, since the cache page + * might get updated before it gets to the wire */ - xbzrle_cache_zero_page(rs, current_addr); - ram_release_pages(block->idstr, offset, pages); - } else if (!rs->ram_bulk_stage && - !migration_in_postcopy() && migrate_use_xbzrle()) { - pages = save_xbzrle_page(rs, &p, current_addr, block, - offset, last_stage); - 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; - } + send_async = false; } } @@ -1174,63 +1192,49 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) { int pages = -1; - uint64_t bytes_xmit = 0; uint8_t *p; - int ret; RAMBlock *block = pss->block; ram_addr_t offset = pss->page << TARGET_PAGE_BITS; p = block->host + offset; - ret = ram_control_save_page(rs->f, block->offset, - offset, TARGET_PAGE_SIZE, &bytes_xmit); - if (bytes_xmit) { - ram_counters.transferred += bytes_xmit; - pages = 1; + if (control_save_page(rs, block, offset, &pages)) { + return pages; } - if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { - if (ret != RAM_SAVE_CONTROL_DELAYED) { - if (bytes_xmit > 0) { - ram_counters.normal++; - } else if (bytes_xmit == 0) { - ram_counters.duplicate++; - } + + /* 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 != rs->last_sent_block) { + flush_compressed_data(rs); + pages = save_zero_page(rs, block, offset); + if (pages > 0) { + ram_release_pages(block->idstr, offset, pages); + } else { + /* + * Make sure the first page is sent out before other pages. + * + * we post it as normal page as compression will take much + * CPU resource. + */ + ram_counters.transferred += save_page_header(rs, rs->f, block, + offset | RAM_SAVE_FLAG_PAGE); + qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, + migrate_release_ram() & + migration_in_postcopy()); + ram_counters.transferred += TARGET_PAGE_SIZE; + ram_counters.normal++; + pages = 1; } } 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 != rs->last_sent_block) { - flush_compressed_data(rs); - pages = save_zero_page(rs, block, offset); - if (pages > 0) { - ram_release_pages(block->idstr, offset, pages); - } else { - /* - * Make sure the first page is sent out before other pages. - * - * we post it as normal page as compression will take much - * CPU resource. - */ - ram_counters.transferred += save_page_header(rs, rs->f, block, - offset | RAM_SAVE_FLAG_PAGE); - qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, - migrate_release_ram() & - migration_in_postcopy()); - ram_counters.transferred += TARGET_PAGE_SIZE; - ram_counters.normal++; - pages = 1; - } + pages = save_zero_page(rs, block, offset); + if (pages == -1) { + pages = compress_page_with_multi_thread(rs, block, offset); } else { - pages = save_zero_page(rs, block, offset); - if (pages == -1) { - pages = compress_page_with_multi_thread(rs, block, offset); - } else { - ram_release_pages(block->idstr, offset, pages); - } + ram_release_pages(block->idstr, offset, pages); } } From 1faa5665c0f1df2eff291454a3a85625a3bc93dd Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Fri, 30 Mar 2018 15:51:24 +0800 Subject: [PATCH 0012/2380] migration: move some code to ram_save_host_page Move some code from ram_save_target_page() to ram_save_host_page() to make it be more readable for latter patches that dramatically clean ram_save_target_page() up Reviewed-by: Peter Xu Signed-off-by: Xiao Guangrong Message-Id: <20180330075128.26919-7-xiaoguangrong@tencent.com> Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 79c7958993..c3628b020e 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1483,38 +1483,23 @@ err: * Returns the number of pages written * * @rs: current RAM state - * @ms: current migration state * @pss: data about the page we want to send * @last_stage: if we are at the completion stage */ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) { - int res = 0; - - /* Check the pages is dirty and if it is send it */ - if (migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { - /* - * If xbzrle is on, stop using the data compression after first - * round of migration even if compression is enabled. In theory, - * xbzrle can do better than compression. - */ - if (migrate_use_compression() && - (rs->ram_bulk_stage || !migrate_use_xbzrle())) { - res = ram_save_compressed_page(rs, pss, last_stage); - } else { - res = ram_save_page(rs, pss, last_stage); - } - - if (res < 0) { - return res; - } - if (pss->block->unsentmap) { - clear_bit(pss->page, pss->block->unsentmap); - } + /* + * If xbzrle is on, stop using the data compression after first + * round of migration even if compression is enabled. In theory, + * xbzrle can do better than compression. + */ + if (migrate_use_compression() && + (rs->ram_bulk_stage || !migrate_use_xbzrle())) { + return ram_save_compressed_page(rs, pss, last_stage); } - return res; + return ram_save_page(rs, pss, last_stage); } /** @@ -1543,12 +1528,22 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS; do { + /* Check the pages is dirty and if it is send it */ + if (!migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { + pss->page++; + continue; + } + tmppages = ram_save_target_page(rs, pss, last_stage); if (tmppages < 0) { return tmppages; } pages += tmppages; + if (pss->block->unsentmap) { + clear_bit(pss->page, pss->block->unsentmap); + } + pss->page++; } while ((pss->page & (pagesize_bits - 1)) && offset_in_ramblock(pss->block, pss->page << TARGET_PAGE_BITS)); From a8ec91f941c5f83123796331c09333d3557eb5fc Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Fri, 30 Mar 2018 15:51:25 +0800 Subject: [PATCH 0013/2380] migration: move calling control_save_page to the common place The function is called by both ram_save_page and ram_save_target_page, so move it to the common caller to cleanup the code Reviewed-by: Peter Xu Signed-off-by: Xiao Guangrong Message-Id: <20180330075128.26919-8-xiaoguangrong@tencent.com> Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index c3628b020e..e0caf7182b 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1037,10 +1037,6 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) p = block->host + offset; trace_ram_save_page(block->idstr, (uint64_t)offset, p); - if (control_save_page(rs, block, offset, &pages)) { - return pages; - } - XBZRLE_cache_lock(); pages = save_zero_page(rs, block, offset); if (pages > 0) { @@ -1198,10 +1194,6 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, p = block->host + offset; - if (control_save_page(rs, block, offset, &pages)) { - return pages; - } - /* 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 @@ -1489,6 +1481,14 @@ err: static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) { + RAMBlock *block = pss->block; + ram_addr_t offset = pss->page << TARGET_PAGE_BITS; + int res; + + if (control_save_page(rs, block, offset, &res)) { + return res; + } + /* * If xbzrle is on, stop using the data compression after first * round of migration even if compression is enabled. In theory, From d7400a3409982a52ac451cd3ca9caee9db670ca7 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Fri, 30 Mar 2018 15:51:26 +0800 Subject: [PATCH 0014/2380] migration: move calling save_zero_page to the common place save_zero_page() is always our first approach to try, move it to the common place before calling ram_save_compressed_page and ram_save_page Reviewed-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Xiao Guangrong Message-Id: <20180330075128.26919-9-xiaoguangrong@tencent.com> Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 105 +++++++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index e0caf7182b..97917542c5 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1038,15 +1038,8 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) trace_ram_save_page(block->idstr, (uint64_t)offset, p); XBZRLE_cache_lock(); - pages = save_zero_page(rs, block, offset); - if (pages > 0) { - /* Must let xbzrle know, otherwise a previous (now 0'd) cached - * page would be stale - */ - xbzrle_cache_zero_page(rs, current_addr); - ram_release_pages(block->idstr, offset, pages); - } else if (!rs->ram_bulk_stage && - !migration_in_postcopy() && migrate_use_xbzrle()) { + if (!rs->ram_bulk_stage && !migration_in_postcopy() && + migrate_use_xbzrle()) { pages = save_xbzrle_page(rs, &p, current_addr, block, offset, last_stage); if (!last_stage) { @@ -1194,40 +1187,23 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, p = block->host + offset; - /* 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 != rs->last_sent_block) { - flush_compressed_data(rs); - pages = save_zero_page(rs, block, offset); - if (pages > 0) { - ram_release_pages(block->idstr, offset, pages); - } else { - /* - * Make sure the first page is sent out before other pages. - * - * we post it as normal page as compression will take much - * CPU resource. - */ - ram_counters.transferred += save_page_header(rs, rs->f, block, - offset | RAM_SAVE_FLAG_PAGE); - qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, - migrate_release_ram() & - migration_in_postcopy()); - ram_counters.transferred += TARGET_PAGE_SIZE; - ram_counters.normal++; - pages = 1; - } + /* + * Make sure the first page is sent out before other pages. + * + * we post it as normal page as compression will take much + * CPU resource. + */ + ram_counters.transferred += save_page_header(rs, rs->f, block, + offset | RAM_SAVE_FLAG_PAGE); + qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, + migrate_release_ram() & + migration_in_postcopy()); + ram_counters.transferred += TARGET_PAGE_SIZE; + ram_counters.normal++; + pages = 1; } else { - pages = save_zero_page(rs, block, offset); - if (pages == -1) { - pages = compress_page_with_multi_thread(rs, block, offset); - } else { - ram_release_pages(block->idstr, offset, pages); - } + pages = compress_page_with_multi_thread(rs, block, offset); } return pages; @@ -1469,6 +1445,24 @@ err: return -1; } +static bool save_page_use_compression(RAMState *rs) +{ + if (!migrate_use_compression()) { + return false; + } + + /* + * If xbzrle is on, stop using the data compression after first + * round of migration even if compression is enabled. In theory, + * xbzrle can do better than compression. + */ + if (rs->ram_bulk_stage || !migrate_use_xbzrle()) { + return true; + } + + return false; +} + /** * ram_save_target_page: save one target page * @@ -1490,12 +1484,31 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, } /* - * If xbzrle is on, stop using the data compression after first - * round of migration even if compression is enabled. In theory, - * xbzrle can do better than compression. + * 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 (migrate_use_compression() && - (rs->ram_bulk_stage || !migrate_use_xbzrle())) { + if (block != rs->last_sent_block && save_page_use_compression(rs)) { + flush_compressed_data(rs); + } + + res = save_zero_page(rs, block, offset); + if (res > 0) { + /* Must let xbzrle know, otherwise a previous (now 0'd) cached + * page would be stale + */ + if (!save_page_use_compression(rs)) { + XBZRLE_cache_lock(); + xbzrle_cache_zero_page(rs, block->offset + offset); + XBZRLE_cache_unlock(); + } + ram_release_pages(block->idstr, offset, res); + return res; + } + + if (save_page_use_compression(rs)) { return ram_save_compressed_page(rs, pss, last_stage); } From 65dacaa04fa7e6104cbcee9251c7845355769a10 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Fri, 30 Mar 2018 15:51:27 +0800 Subject: [PATCH 0015/2380] migration: introduce save_normal_page() It directly sends the page to the stream neither checking zero nor using xbzrle or compression Reviewed-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Xiao Guangrong Message-Id: <20180330075128.26919-10-xiaoguangrong@tencent.com> Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 50 +++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 97917542c5..2eb4c0bf49 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1012,6 +1012,34 @@ static bool control_save_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, return true; } +/* + * directly send the page to the stream + * + * Returns the number of pages written. + * + * @rs: current RAM state + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @buf: the page to be sent + * @async: send to page asyncly + */ +static int save_normal_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, + uint8_t *buf, bool async) +{ + ram_counters.transferred += save_page_header(rs, rs->f, block, + offset | RAM_SAVE_FLAG_PAGE); + if (async) { + qemu_put_buffer_async(rs->f, buf, TARGET_PAGE_SIZE, + migrate_release_ram() & + migration_in_postcopy()); + } else { + qemu_put_buffer(rs->f, buf, TARGET_PAGE_SIZE); + } + ram_counters.transferred += TARGET_PAGE_SIZE; + ram_counters.normal++; + return 1; +} + /** * ram_save_page: send the given page to the stream * @@ -1052,18 +1080,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) /* XBZRLE overflow or normal page */ if (pages == -1) { - ram_counters.transferred += - save_page_header(rs, rs->f, block, offset | RAM_SAVE_FLAG_PAGE); - if (send_async) { - qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, - migrate_release_ram() & - migration_in_postcopy()); - } else { - qemu_put_buffer(rs->f, p, TARGET_PAGE_SIZE); - } - ram_counters.transferred += TARGET_PAGE_SIZE; - pages = 1; - ram_counters.normal++; + pages = save_normal_page(rs, block, offset, p, send_async); } XBZRLE_cache_unlock(); @@ -1194,14 +1211,7 @@ static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, * we post it as normal page as compression will take much * CPU resource. */ - ram_counters.transferred += save_page_header(rs, rs->f, block, - offset | RAM_SAVE_FLAG_PAGE); - qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, - migrate_release_ram() & - migration_in_postcopy()); - ram_counters.transferred += TARGET_PAGE_SIZE; - ram_counters.normal++; - pages = 1; + pages = save_normal_page(rs, block, offset, p, true); } else { pages = compress_page_with_multi_thread(rs, block, offset); } From da3f56cb2e767016d3f204837a77caf35b463f90 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Fri, 30 Mar 2018 15:51:28 +0800 Subject: [PATCH 0016/2380] migration: remove ram_save_compressed_page() Now, we can reuse the path in ram_save_page() to post the page out as normal, then the only thing remained in ram_save_compressed_page() is compression that we can move it out to the caller Reviewed-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Xiao Guangrong Message-Id: <20180330075128.26919-11-xiaoguangrong@tencent.com> Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 45 ++++++++------------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 2eb4c0bf49..912810c18e 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1184,41 +1184,6 @@ static int compress_page_with_multi_thread(RAMState *rs, RAMBlock *block, return pages; } -/** - * ram_save_compressed_page: compress the given page and send it to the stream - * - * Returns the number of pages written. - * - * @rs: current RAM state - * @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 - */ -static int ram_save_compressed_page(RAMState *rs, PageSearchStatus *pss, - bool last_stage) -{ - int pages = -1; - uint8_t *p; - RAMBlock *block = pss->block; - ram_addr_t offset = pss->page << TARGET_PAGE_BITS; - - p = block->host + offset; - - if (block != rs->last_sent_block) { - /* - * Make sure the first page is sent out before other pages. - * - * we post it as normal page as compression will take much - * CPU resource. - */ - pages = save_normal_page(rs, block, offset, p, true); - } else { - pages = compress_page_with_multi_thread(rs, block, offset); - } - - return pages; -} - /** * find_dirty_block: find the next dirty page and update any state * associated with the search process. @@ -1518,8 +1483,14 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, return res; } - if (save_page_use_compression(rs)) { - return ram_save_compressed_page(rs, pss, last_stage); + /* + * Make sure the first page is sent out before other pages. + * + * we post it as normal page as compression will take much + * CPU resource. + */ + if (block == rs->last_sent_block && save_page_use_compression(rs)) { + res = compress_page_with_multi_thread(rs, block, offset); } return ram_save_page(rs, pss, last_stage); From 14ec3cbd7c1e31dca4d23f028100c8f43e156573 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 26 Apr 2018 11:04:38 +0100 Subject: [PATCH 0017/2380] device_tree: Increase FDT_MAX_SIZE to 1 MiB It is not uncommon for a contemporary FDT to be larger than 64 KiB, leading to failures loading the device tree from sysfs: qemu-system-aarch64: qemu_fdt_setprop: Couldn't set ...: FDT_ERR_NOSPACE Hence increase the limit to 1 MiB, like on PPC. For reference, the largest arm64 DTB created from the Linux sources is ca. 75 KiB large (100 KiB when built with symbols/fixup support). Cc: qemu-stable@nongnu.org Signed-off-by: Geert Uytterhoeven Message-id: 1523541337-23919-1-git-send-email-geert+renesas@glider.be Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- device_tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/device_tree.c b/device_tree.c index 19458b32bf..52c3358a55 100644 --- a/device_tree.c +++ b/device_tree.c @@ -29,7 +29,7 @@ #include -#define FDT_MAX_SIZE 0x10000 +#define FDT_MAX_SIZE 0x100000 void *create_device_tree(int *sizep) { From 75ed2c02484101d5b4995c56e78a894ac244ce05 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 26 Apr 2018 11:04:38 +0100 Subject: [PATCH 0018/2380] arm: always start from first_cpu when registering loader cpu reset callback if arm_load_kernel() were passed non first_cpu, QEMU would end up with partially set do_cpu_reset() callback leaving some CPUs without it. Make sure that do_cpu_reset() is registered for all CPUs by enumerating CPUs from first_cpu. (In practice every board that we have was passing us the first CPU as the boot CPU, either directly or indirectly, so this wasn't causing incorrect behaviour.) Signed-off-by: Igor Mammedov Reviewed-by: Peter Maydell [PMM: added a note that this isn't a behaviour change] Signed-off-by: Peter Maydell --- hw/arm/boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 26184bcd7c..9ae6ab2689 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -1188,7 +1188,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) * 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)) { + for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); } } From 145772707fe80395b87c244ccf5699a756f1946b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 26 Apr 2018 11:04:38 +0100 Subject: [PATCH 0019/2380] target/arm: Remove stale TODO comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove a stale TODO comment -- we have now made the arm_ldl_ptw() and arm_ldq_ptw() functions propagate physical memory read errors out to their callers. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180419142151.9862-1-peter.maydell@linaro.org --- target/arm/helper.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index b14fdab140..3ad0371aeb 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -8680,13 +8680,7 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx, return addr; } -/* All loads done in the course of a page table walk go through here. - * TODO: rather than ignoring errors from physical memory reads (which - * are external aborts in ARM terminology) we should propagate this - * error out so that we can turn it into a Data Abort if this walk - * was being done for a CPU load/store or an address translation instruction - * (but not if it was for a debug access). - */ +/* All loads done in the course of a page table walk go through here. */ static uint32_t arm_ldl_ptw(CPUState *cs, hwaddr addr, bool is_secure, ARMMMUIdx mmu_idx, ARMMMUFaultInfo *fi) { From 4818bad98c8212fbbb0525d10761b6b65279ab92 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 26 Apr 2018 11:04:38 +0100 Subject: [PATCH 0020/2380] target/arm: Use v7m_stack_read() for reading the frame signature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 95695effe8caa552b8f2 we changed the v7M/v8M stack pop code to use a new v7m_stack_read() function that checks whether the read should fail due to an MPU or bus abort. We missed one call though, the one which reads the signature word for the callee-saved register part of the frame. Correct the omission. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20180419142106.9694-1-peter.maydell@linaro.org --- target/arm/helper.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 3ad0371aeb..0fa02c31e1 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6913,7 +6913,6 @@ static bool v7m_push_stack(ARMCPU *cpu) static void do_v7m_exception_exit(ARMCPU *cpu) { CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); uint32_t excret; uint32_t xpsr; bool ufault = false; @@ -7112,9 +7111,11 @@ static void do_v7m_exception_exit(ARMCPU *cpu) ((excret & R_V7M_EXCRET_ES_MASK) == 0 || (excret & R_V7M_EXCRET_DCRS_MASK) == 0)) { uint32_t expected_sig = 0xfefa125b; - uint32_t actual_sig = ldl_phys(cs->as, frameptr); + uint32_t actual_sig; - if (expected_sig != actual_sig) { + pop_ok = v7m_stack_read(cpu, &actual_sig, frameptr, mmu_idx); + + if (pop_ok && expected_sig != actual_sig) { /* Take a SecureFault on the current stack */ env->v7m.sfsr |= R_V7M_SFSR_INVIS_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); @@ -7125,7 +7126,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) return; } - pop_ok = + pop_ok = pop_ok && v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) && v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) && v7m_stack_read(cpu, &env->regs[5], frameptr + 0xc, mmu_idx) && From ccbc0e338486b21cb0eb52e52cd309bbbe6a7507 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 26 Apr 2018 11:04:38 +0100 Subject: [PATCH 0021/2380] target/arm: Check PMCNTEN for whether PMCCNTR is enabled Signed-off-by: Aaron Lindsay Reviewed-by: Peter Maydell Message-id: 1523997485-1905-2-git-send-email-alindsay@codeaurora.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 0fa02c31e1..1526724d64 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -994,7 +994,7 @@ static inline bool arm_ccnt_enabled(CPUARMState *env) { /* This does not support checking PMCCFILTR_EL0 register */ - if (!(env->cp15.c9_pmcr & PMCRE)) { + if (!(env->cp15.c9_pmcr & PMCRE) || !(env->cp15.c9_pmcnten & (1 << 31))) { return false; } From 169c893874977eee8303a6dad4a3f25c5464858f Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0022/2380] target/arm: Treat PMCCNTR as alias of PMCCNTR_EL0 They share the same underlying state Signed-off-by: Aaron Lindsay Reviewed-by: Peter Maydell Message-id: 1523997485-1905-3-git-send-email-alindsay@codeaurora.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 1526724d64..7bf4afa719 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -1318,7 +1318,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr), .writefn = pmselr_write, .raw_writefn = raw_write, }, { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, - .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_IO, + .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, .readfn = pmccntr_read, .writefn = pmccntr_write32, .accessfn = pmreg_access_ccntr }, { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, From 7ece99b17e832065236c07a158dfac62619ef99b Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0023/2380] target/arm: Mask PMU register writes based on PMCR_EL0.N This is in preparation for enabling counters other than PMCCNTR Signed-off-by: Aaron Lindsay Reviewed-by: Peter Maydell Message-id: 1523997485-1905-5-git-send-email-alindsay@codeaurora.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 7bf4afa719..3238aacaa6 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -52,11 +52,6 @@ typedef struct V8M_SAttributes { static void v8m_security_lookup(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, V8M_SAttributes *sattrs); - -/* Definitions for the PMCCNTR and PMCR registers */ -#define PMCRD 0x8 -#define PMCRC 0x4 -#define PMCRE 0x1 #endif static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) @@ -906,6 +901,24 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { REGINFO_SENTINEL }; +/* Definitions for the PMU registers */ +#define PMCRN_MASK 0xf800 +#define PMCRN_SHIFT 11 +#define PMCRD 0x8 +#define PMCRC 0x4 +#define PMCRE 0x1 + +static inline uint32_t pmu_num_counters(CPUARMState *env) +{ + return (env->cp15.c9_pmcr & PMCRN_MASK) >> PMCRN_SHIFT; +} + +/* Bits allowed to be set/cleared for PMCNTEN* and PMINTEN* */ +static inline uint64_t pmu_counter_mask(CPUARMState *env) +{ + return (1 << 31) | ((1 << pmu_num_counters(env)) - 1); +} + static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -1113,14 +1126,14 @@ static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri, static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - value &= (1 << 31); + value &= pmu_counter_mask(env); env->cp15.c9_pmcnten |= value; } static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - value &= (1 << 31); + value &= pmu_counter_mask(env); env->cp15.c9_pmcnten &= ~value; } @@ -1168,14 +1181,14 @@ static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { /* We have no event counters so only the C bit can be changed */ - value &= (1 << 31); + value &= pmu_counter_mask(env); env->cp15.c9_pminten |= value; } static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - value &= (1 << 31); + value &= pmu_counter_mask(env); env->cp15.c9_pminten &= ~value; } From d5a5e4c93dae0dc3feb402cf7ee78d846da1a7e1 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0024/2380] target/arm: Fetch GICv3 state directly from CPUARMState This eliminates the need for fetching it from el_change_hook_opaque, and allows for supporting multiple el_change_hooks without having to hack something together to find the registered opaque belonging to GICv3. Signed-off-by: Aaron Lindsay Reviewed-by: Peter Maydell Message-id: 1523997485-1905-6-git-send-email-alindsay@codeaurora.org Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 10 ++-------- target/arm/cpu.h | 10 ---------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 26f5eeda94..cb9a3a542d 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -29,11 +29,7 @@ void gicv3_set_gicv3state(CPUState *cpu, GICv3CPUState *s) static GICv3CPUState *icc_cs_from_env(CPUARMState *env) { - /* Given the CPU, find the right GICv3CPUState struct. - * Since we registered the CPU interface with the EL change hook as - * the opaque pointer, we can just directly get from the CPU to it. - */ - return arm_get_el_change_hook_opaque(arm_env_get_cpu(env)); + return env->gicv3state; } static bool gicv3_use_ns_bank(CPUARMState *env) @@ -2615,9 +2611,7 @@ void gicv3_init_cpuif(GICv3State *s) * it might be with code translated by CPU 0 but run by CPU 1, in * which case we'd get the wrong value. * So instead we define the regs with no ri->opaque info, and - * get back to the GICv3CPUState from the ARMCPU by reading back - * the opaque pointer from the el_change_hook, which we're going - * to need to register anyway. + * get back to the GICv3CPUState from the CPUARMState. */ define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); if (arm_feature(&cpu->env, ARM_FEATURE_EL2) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 19a0c03f9b..6bd8ff5917 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2902,16 +2902,6 @@ static inline AddressSpace *arm_addressspace(CPUState *cs, MemTxAttrs attrs) void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHook *hook, void *opaque); -/** - * arm_get_el_change_hook_opaque: - * Return the opaque data that will be used by the el_change_hook - * for this CPU. - */ -static inline void *arm_get_el_change_hook_opaque(ARMCPU *cpu) -{ - return cpu->el_change_hook_opaque; -} - /** * aa32_vfp_dreg: * Return a pointer to the Dn register within env in 32-bit mode. From 08267487c99e8150382420936ab72c1e0ad74ce3 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0025/2380] target/arm: Support multiple EL change hooks Signed-off-by: Aaron Lindsay Message-id: 1523997485-1905-7-git-send-email-alindsay@codeaurora.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu.c | 21 ++++++++++++++++----- target/arm/cpu.h | 20 ++++++++++---------- target/arm/internals.h | 7 ++++--- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 022d8c5787..1f689f6817 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -55,13 +55,15 @@ static bool arm_cpu_has_work(CPUState *cs) | CPU_INTERRUPT_EXITTB); } -void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHook *hook, +void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void *opaque) { - /* We currently only support registering a single hook function */ - assert(!cpu->el_change_hook); - cpu->el_change_hook = hook; - cpu->el_change_hook_opaque = opaque; + ARMELChangeHook *entry = g_new0(ARMELChangeHook, 1); + + entry->hook = hook; + entry->opaque = opaque; + + QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node); } static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) @@ -552,6 +554,8 @@ static void arm_cpu_initfn(Object *obj) cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); + QLIST_INIT(&cpu->el_change_hooks); + #ifndef CONFIG_USER_ONLY /* Our inbound IRQ and FIQ lines */ if (kvm_enabled()) { @@ -713,7 +717,14 @@ static void arm_cpu_post_init(Object *obj) static void arm_cpu_finalizefn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMELChangeHook *hook, *next; + g_hash_table_destroy(cpu->cp_regs); + + QLIST_FOREACH_SAFE(hook, &cpu->el_change_hooks, node, next) { + QLIST_REMOVE(hook, node); + g_free(hook); + } } static void arm_cpu_realizefn(DeviceState *dev, Error **errp) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 6bd8ff5917..b3def5afad 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -632,12 +632,17 @@ typedef struct CPUARMState { } CPUARMState; /** - * ARMELChangeHook: + * ARMELChangeHookFn: * type of a function which can be registered via arm_register_el_change_hook() * to get callbacks when the CPU changes its exception level or mode. */ -typedef void ARMELChangeHook(ARMCPU *cpu, void *opaque); - +typedef void ARMELChangeHookFn(ARMCPU *cpu, void *opaque); +typedef struct ARMELChangeHook ARMELChangeHook; +struct ARMELChangeHook { + ARMELChangeHookFn *hook; + void *opaque; + QLIST_ENTRY(ARMELChangeHook) node; +}; /* These values map onto the return values for * QEMU_PSCI_0_2_FN_AFFINITY_INFO */ @@ -826,8 +831,7 @@ struct ARMCPU { */ bool cfgend; - ARMELChangeHook *el_change_hook; - void *el_change_hook_opaque; + QLIST_HEAD(, ARMELChangeHook) el_change_hooks; int32_t node_id; /* NUMA node this CPU belongs to */ @@ -2894,12 +2898,8 @@ static inline AddressSpace *arm_addressspace(CPUState *cs, MemTxAttrs attrs) * CPU changes exception level or mode. The hook function will be * passed a pointer to the ARMCPU and the opaque data pointer passed * to this function when the hook was registered. - * - * Note that we currently only support registering a single hook function, - * and will assert if this function is called twice. - * This facility is intended for the use of the GICv3 emulation. */ -void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHook *hook, +void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void *opaque); /** diff --git a/target/arm/internals.h b/target/arm/internals.h index 8ce944b7a0..6358c2ad5a 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -727,11 +727,12 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr); -/* Call the EL change hook if one has been registered */ +/* Call any registered EL change hooks */ static inline void arm_call_el_change_hook(ARMCPU *cpu) { - if (cpu->el_change_hook) { - cpu->el_change_hook(cpu, cpu->el_change_hook_opaque); + ARMELChangeHook *hook, *next; + QLIST_FOREACH_SAFE(hook, &cpu->el_change_hooks, node, next) { + hook->hook(cpu, hook->opaque); } } From b5c53d1b3886387874f8c8582b205aeb3e4c3df6 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0026/2380] target/arm: Add pre-EL change hooks Because the design of the PMU requires that the counter values be converted between their delta and guest-visible forms for mode filtering, an additional hook which occurs before the EL is changed is necessary. Signed-off-by: Aaron Lindsay Message-id: 1523997485-1905-8-git-send-email-alindsay@codeaurora.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu.c | 16 ++++++++++++++++ target/arm/cpu.h | 22 +++++++++++++++++++--- target/arm/helper.c | 14 ++++++++------ target/arm/internals.h | 7 +++++++ target/arm/op_helper.c | 8 ++++++++ 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 1f689f6817..d175c5e94f 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -55,6 +55,17 @@ static bool arm_cpu_has_work(CPUState *cs) | CPU_INTERRUPT_EXITTB); } +void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, + void *opaque) +{ + ARMELChangeHook *entry = g_new0(ARMELChangeHook, 1); + + entry->hook = hook; + entry->opaque = opaque; + + QLIST_INSERT_HEAD(&cpu->pre_el_change_hooks, entry, node); +} + void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void *opaque) { @@ -554,6 +565,7 @@ static void arm_cpu_initfn(Object *obj) cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); + QLIST_INIT(&cpu->pre_el_change_hooks); QLIST_INIT(&cpu->el_change_hooks); #ifndef CONFIG_USER_ONLY @@ -721,6 +733,10 @@ static void arm_cpu_finalizefn(Object *obj) g_hash_table_destroy(cpu->cp_regs); + QLIST_FOREACH_SAFE(hook, &cpu->pre_el_change_hooks, node, next) { + QLIST_REMOVE(hook, node); + g_free(hook); + } QLIST_FOREACH_SAFE(hook, &cpu->el_change_hooks, node, next) { QLIST_REMOVE(hook, node); g_free(hook); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index b3def5afad..b9b47f4b22 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -831,6 +831,7 @@ struct ARMCPU { */ bool cfgend; + QLIST_HEAD(, ARMELChangeHook) pre_el_change_hooks; QLIST_HEAD(, ARMELChangeHook) el_change_hooks; int32_t node_id; /* NUMA node this CPU belongs to */ @@ -2893,14 +2894,29 @@ static inline AddressSpace *arm_addressspace(CPUState *cs, MemTxAttrs attrs) #endif /** - * arm_register_el_change_hook: - * Register a hook function which will be called back whenever this + * arm_register_pre_el_change_hook: + * Register a hook function which will be called immediately before this * CPU changes exception level or mode. The hook function will be * passed a pointer to the ARMCPU and the opaque data pointer passed * to this function when the hook was registered. + * + * Note that if a pre-change hook is called, any registered post-change hooks + * are guaranteed to subsequently be called. */ -void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, +void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void *opaque); +/** + * arm_register_el_change_hook: + * Register a hook function which will be called immediately after this + * CPU changes exception level or mode. The hook function will be + * passed a pointer to the ARMCPU and the opaque data pointer passed + * to this function when the hook was registered. + * + * Note that any registered hooks registered here are guaranteed to be called + * if pre-change hooks have been. + */ +void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void + *opaque); /** * aa32_vfp_dreg: diff --git a/target/arm/helper.c b/target/arm/helper.c index 3238aacaa6..81e88f255b 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -8249,6 +8249,14 @@ void arm_cpu_do_interrupt(CPUState *cs) return; } + /* Hooks may change global state so BQL should be held, also the + * BQL needs to be held for any modification of + * cs->interrupt_request. + */ + g_assert(qemu_mutex_iothread_locked()); + + arm_call_pre_el_change_hook(cpu); + assert(!excp_is_internal(cs->exception_index)); if (arm_el_is_aa64(env, new_el)) { arm_cpu_do_interrupt_aarch64(cs); @@ -8256,12 +8264,6 @@ void arm_cpu_do_interrupt(CPUState *cs) arm_cpu_do_interrupt_aarch32(cs); } - /* Hooks may change global state so BQL should be held, also the - * BQL needs to be held for any modification of - * cs->interrupt_request. - */ - g_assert(qemu_mutex_iothread_locked()); - arm_call_el_change_hook(cpu); if (!kvm_enabled()) { diff --git a/target/arm/internals.h b/target/arm/internals.h index 6358c2ad5a..dc9357766c 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -728,6 +728,13 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, MemTxResult response, uintptr_t retaddr); /* Call any registered EL change hooks */ +static inline void arm_call_pre_el_change_hook(ARMCPU *cpu) +{ + ARMELChangeHook *hook, *next; + QLIST_FOREACH_SAFE(hook, &cpu->pre_el_change_hooks, node, next) { + hook->hook(cpu, hook->opaque); + } +} static inline void arm_call_el_change_hook(ARMCPU *cpu) { ARMELChangeHook *hook, *next; diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index 84f08bf815..f728f25e4b 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -511,6 +511,10 @@ void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask) /* Write the CPSR for a 32-bit exception return */ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) { + qemu_mutex_lock_iothread(); + arm_call_pre_el_change_hook(arm_env_get_cpu(env)); + qemu_mutex_unlock_iothread(); + cpsr_write(env, val, CPSR_ERET_MASK, CPSRWriteExceptionReturn); /* Generated code has already stored the new PC value, but @@ -1028,6 +1032,10 @@ void HELPER(exception_return)(CPUARMState *env) goto illegal_return; } + qemu_mutex_lock_iothread(); + arm_call_pre_el_change_hook(arm_env_get_cpu(env)); + qemu_mutex_unlock_iothread(); + if (!return_to_aa64) { env->aarch64 = 0; /* We do a raw CPSR write because aarch64_sync_64_to_32() From e69ad9df6c2fe98713e5391a9161da9c6d7a9f22 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0027/2380] target/arm: Allow EL change hooks to do IO During code generation, surround CPSR writes and exception returns which call the EL change hooks with gen_io_start/end. The immediate need is for the PMU to access the clock and icount during EL change to support mode filtering. Signed-off-by: Aaron Lindsay Message-id: 1523997485-1905-9-git-send-email-alindsay@codeaurora.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 6 ++++++ target/arm/translate.c | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index c91329249d..bff4e13bf6 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -1930,7 +1930,13 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_exception_return(cpu_env); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; return; diff --git a/target/arm/translate.c b/target/arm/translate.c index db1ce6510a..9bc2ce1a0b 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -4548,7 +4548,13 @@ static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr) * appropriately depending on the new Thumb bit, so it must * be called after storing the new PC. */ + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_cpsr_write_eret(cpu_env, cpsr); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } tcg_temp_free_i32(cpsr); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; @@ -9843,7 +9849,13 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (exc_return) { /* Restore CPSR from SPSR. */ tmp = load_cpu_field(spsr); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_cpsr_write_eret(cpu_env, tmp); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } tcg_temp_free_i32(tmp); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; From ac57fd24cd864d42e7551f82266bc0930bd39547 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0028/2380] target/arm: Fix bitmask for PMCCFILTR writes It was shifted to the left one bit too few. Signed-off-by: Aaron Lindsay Reviewed-by: Peter Maydell Message-id: 1523997485-1905-10-git-send-email-alindsay@codeaurora.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 81e88f255b..85c289f3b9 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -1119,7 +1119,7 @@ static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { pmccntr_sync(env); - env->cp15.pmccfiltr_el0 = value & 0x7E000000; + env->cp15.pmccfiltr_el0 = value & 0xfc000000; pmccntr_sync(env); } From e4e91a217c17fff4045dd4b423cdcb471b3d6a0e Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0029/2380] target/arm: Make PMOVSCLR and PMUSERENR 64 bits wide This is a bug fix to ensure 64-bit reads of these registers don't read adjacent data. Signed-off-by: Aaron Lindsay Message-id: 1523997485-1905-13-git-send-email-alindsay@codeaurora.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu.h | 4 ++-- target/arm/helper.c | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index b9b47f4b22..44e6b77151 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -367,8 +367,8 @@ typedef struct CPUARMState { uint32_t c9_data; uint64_t c9_pmcr; /* performance monitor control register */ uint64_t c9_pmcnten; /* perf monitor counter enables */ - uint32_t c9_pmovsr; /* perf monitor overflow status */ - uint32_t c9_pmuserenr; /* perf monitor user enable */ + uint64_t c9_pmovsr; /* perf monitor overflow status */ + uint64_t c9_pmuserenr; /* perf monitor user enable */ uint64_t c9_pmselr; /* perf monitor counter selection register */ uint64_t c9_pminten; /* perf monitor interrupt enables */ union { /* Memory attribute redirection */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 85c289f3b9..52a88e0297 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -1305,7 +1305,8 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenclr_write }, { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, - .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), + .access = PL0_RW, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), .accessfn = pmreg_access, .writefn = pmovsr_write, .raw_writefn = raw_write }, @@ -1360,7 +1361,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .accessfn = pmreg_access_xevcntr }, { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, .access = PL0_R | PL1_RW, .accessfn = access_tpm, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmuserenr), .resetvalue = 0, .writefn = pmuserenr_write, .raw_writefn = raw_write }, { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64, From b901a172455cfbb96cc36e916ebc65f0bfed8109 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0030/2380] hw/arm/raspi: Don't bother setting default_cpu_type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 210f47840dd62, we changed the bcm2836 SoC object to always create a CPU of the correct type for that SoC model. This makes the default_cpu_type settings in the MachineClass structs for the raspi2 and raspi3 boards redundant. We didn't change those at the time because it would have meant a temporary regression in a corner case of error handling if the user requested a non-existing CPU type. The -cpu parse handling changes in 2278b93941d42c3 mean that it no longer implicitly depends on default_cpu_type for this to work, so we can now delete the redundant default_cpu_type fields. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180420155547.9497-1-peter.maydell@linaro.org --- hw/arm/raspi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 955a7c4e80..66899c28dc 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -226,7 +226,6 @@ static void raspi2_machine_init(MachineClass *mc) mc->no_parallel = 1; mc->no_floppy = 1; mc->no_cdrom = 1; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); mc->max_cpus = BCM283X_NCPUS; mc->min_cpus = BCM283X_NCPUS; mc->default_cpus = BCM283X_NCPUS; @@ -249,7 +248,6 @@ static void raspi3_machine_init(MachineClass *mc) mc->no_parallel = 1; mc->no_floppy = 1; mc->no_cdrom = 1; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"); mc->max_cpus = BCM283X_NCPUS; mc->min_cpus = BCM283X_NCPUS; mc->default_cpus = BCM283X_NCPUS; From eb7d1f176381466660a730f14eff3d521686aa31 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0031/2380] hw/arm/highbank: don't make sysram 'nomigrate' Currently we use memory_region_init_ram_nomigrate() to create the "highbank.sysram" memory region, and we don't manually register it with vmstate_register_ram(). This currently means that its contents are migrated but as a ram block whose name is the empty string; in future it may mean they are not migrated at all. Use memory_region_init_ram() instead. Note that this is a cross-version migration compatibility break for the "highbank" and "midway" machines. Signed-off-by: Peter Maydell Message-id: 20180420124835.7268-2-peter.maydell@linaro.org --- hw/arm/highbank.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 1742cf6f6c..88326d1bfd 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -291,7 +291,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) memory_region_add_subregion(sysmem, 0, dram); sysram = g_new(MemoryRegion, 1); - memory_region_init_ram_nomigrate(sysram, NULL, "highbank.sysram", 0x8000, + memory_region_init_ram(sysram, NULL, "highbank.sysram", 0x8000, &error_fatal); memory_region_add_subregion(sysmem, 0xfff88000, sysram); if (bios_name != NULL) { From 44cf837d38c3af178711781ffdc61776763e40d1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0032/2380] hw/arm/aspeed: don't make 'boot_rom' region 'nomigrate' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we use memory_region_init_ram_nomigrate() to create the "aspeed.boot_rom" memory region, and we don't manually register it with vmstate_register_ram(). This currently means that its contents are migrated but as a ram block whose name is the empty string; in future it may mean they are not migrated at all. Use memory_region_init_ram() instead. Note that would be a cross-version migration compatibility break for the "palmetto-bmc", "ast2500-evb" and "romulus-bmc" machines, but migration is currently broken for them. Signed-off-by: Peter Maydell Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-id: 20180420124835.7268-3-peter.maydell@linaro.org --- hw/arm/aspeed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 7088c907bd..aecb3c1e75 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -225,7 +225,7 @@ static void aspeed_board_init(MachineState *machine, * SoC and 128MB for the AST2500 SoC, which is twice as big as * needed by the flash modules of the Aspeed machines. */ - memory_region_init_rom_nomigrate(boot_rom, OBJECT(bmc), "aspeed.boot_rom", + memory_region_init_rom(boot_rom, OBJECT(bmc), "aspeed.boot_rom", fl->size, &error_abort); memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, boot_rom); From a2e9989c96aaa0e0d993e7b44eb6c2da2f9214ac Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0033/2380] hw/arm/aspeed_soc: don't use vmstate_register_ram_global for SRAM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we use vmstate_register_ram_global() for the SRAM; this is not a good idea for devices, because it means that you can only ever create one instance of the device, as the second instance would get a RAM block name clash. Instead, use memory_region_init_ram(), which automatically registers the RAM block with a local-to-the-device name. Note that this would be a cross-version migration compatibility break for the "palmetto-bmc", "ast2500-evb" and "romulus-bmc" machines, but migration is currently broken for them. Signed-off-by: Peter Maydell Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-id: 20180420124835.7268-4-peter.maydell@linaro.org --- hw/arm/aspeed_soc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 30d25f8b06..407f10d0d4 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -186,13 +186,12 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) } /* SRAM */ - memory_region_init_ram_nomigrate(&s->sram, OBJECT(dev), "aspeed.sram", + memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram", sc->info->sram_size, &err); if (err) { error_propagate(errp, err); return; } - vmstate_register_ram_global(&s->sram); memory_region_add_subregion(get_system_memory(), ASPEED_SOC_SRAM_BASE, &s->sram); From 6b892b2f79796989c78d6977e4c906448cda69c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 26 Apr 2018 11:04:39 +0100 Subject: [PATCH 0034/2380] timer/aspeed: fix vmstate version id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 1d3e65aa7ac5 ("hw/timer: Add value matching support to aspeed_timer") increased the vmstate version of aspeed.timer because the state had changed, but it also bumped the version of the VMSTATE_STRUCT_ARRAY under the aspeed.timerctrl which did not need to. Change back this version to fix migration. Signed-off-by: Cédric Le Goater Message-id: 20180423101433.17759-1-clg@kaod.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/timer/aspeed_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 50acbf530a..1e31e22b6f 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -504,7 +504,7 @@ static const VMStateDescription vmstate_aspeed_timer_state = { VMSTATE_UINT32(ctrl, AspeedTimerCtrlState), VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState), VMSTATE_STRUCT_ARRAY(timers, AspeedTimerCtrlState, - ASPEED_TIMER_NR_TIMERS, 2, vmstate_aspeed_timer, + ASPEED_TIMER_NR_TIMERS, 1, vmstate_aspeed_timer, AspeedTimer), VMSTATE_END_OF_LIST() } From fbf32752663878947de455ff57cb5b9318f14bec Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu Date: Thu, 26 Apr 2018 11:04:40 +0100 Subject: [PATCH 0035/2380] xilinx_spips: Correct SNOOP_NONE state when flushing the txfifo SNOOP_NONE state handle is moved above in the if ladder, as it's same as SNOOP_STRIPPING during data cycles. Signed-off-by: Sai Pavan Boddu Reviewed-by: Francisco Iglesias Message-id: 1524119244-1240-1-git-send-email-saipava@xilinx.com Signed-off-by: Peter Maydell --- hw/ssi/xilinx_spips.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 426f971311..03f5faee4b 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -616,7 +616,8 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) if (fifo8_is_empty(&s->tx_fifo)) { xilinx_spips_update_ixr(s); return; - } else if (s->snoop_state == SNOOP_STRIPING) { + } else if (s->snoop_state == SNOOP_STRIPING || + s->snoop_state == SNOOP_NONE) { for (i = 0; i < num_effective_busses(s); ++i) { tx_rx[i] = fifo8_pop(&s->tx_fifo); } From 569df5b6e118128995c70d266668f11825c8fec8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 26 Apr 2018 11:48:20 +0100 Subject: [PATCH 0036/2380] Open 2.13 development tree Unfortunately I forgot to do this before applying the merge in commit 8e383d19b44863556, so that commit will incorrectly claim to be 2.12 even though it isn't in the official 2.12 release. Oops. Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d8b698973a..119521432e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.0 +2.12.50 From 47c4f85a0c27888e12af827471cfef87deb49821 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:37 +0100 Subject: [PATCH 0037/2380] hw/char/serial: Allow disconnected chardevs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the serial.c realize code has an explicit check that it is not connected to a disconnected backend (ie one with a NULL chardev). This isn't what we want -- you should be able to create a serial device even if it isn't attached to anything. Remove the check. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Acked-by: Thomas Huth Message-id: 20180420145249.32435-2-peter.maydell@linaro.org --- hw/char/serial.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/char/serial.c b/hw/char/serial.c index eb72191ee7..2c080c9862 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -923,11 +923,6 @@ static int serial_be_change(void *opaque) void serial_realize_core(SerialState *s, Error **errp) { - if (!qemu_chr_fe_backend_connected(&s->chr)) { - error_setg(errp, "Can't create serial device, empty char device"); - return; - } - s->modem_status_poll = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) serial_update_msl, s); s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) fifo_timeout_int, s); From c221287f8ff4e46fffad11bb2a6bc99442e845be Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:38 +0100 Subject: [PATCH 0038/2380] hw/arm/fsl-imx*: Don't create "null" chardevs for serial devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following commit 12051d82f004024, UART devices should handle being passed a NULL pointer chardev, so we don't need to create "null" backends in board code. Remove the code that does this and updates serial_hds[]. (fsl-imx7.c was already written this way.) Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180420145249.32435-3-peter.maydell@linaro.org --- hw/arm/fsl-imx25.c | 12 +----------- hw/arm/fsl-imx31.c | 12 +----------- hw/arm/fsl-imx6.c | 13 +------------ 3 files changed, 3 insertions(+), 34 deletions(-) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index cb988a6c25..d7d064e5ce 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -118,17 +118,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) }; if (i < MAX_SERIAL_PORTS) { - Chardev *chr; - - chr = serial_hds[i]; - - if (!chr) { - char label[20]; - snprintf(label, sizeof(label), "imx31.uart%d", i); - chr = qemu_chr_new(label, "null"); - } - - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); } object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 3eee83d547..e6c788049d 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -107,17 +107,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) }; if (i < MAX_SERIAL_PORTS) { - Chardev *chr; - - chr = serial_hds[i]; - - if (!chr) { - char label[20]; - snprintf(label, sizeof(label), "imx31.uart%d", i); - chr = qemu_chr_new(label, "null"); - } - - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); } object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 9dfbc9a8c4..ea14de33c6 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -189,18 +189,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) }; if (i < MAX_SERIAL_PORTS) { - Chardev *chr; - - chr = serial_hds[i]; - - if (!chr) { - char *label = g_strdup_printf("imx6.uart%d", i + 1); - chr = qemu_chr_new(label, "null"); - g_free(label); - serial_hds[i] = chr; - } - - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); } object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); From 87364af8fe0c9def2e9de8e63f6b66135834318c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:39 +0100 Subject: [PATCH 0039/2380] hw/mips/boston.c: Don't create "null" chardevs for serial devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following commit 12051d82f004024, UART devices should handle being passed a NULL pointer chardev, so we don't need to create "null" backends in board code. Remove the code that does this and updates serial_hds[]. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Tested-by: Philippe Mathieu-Daudé Message-id: 20180420145249.32435-4-peter.maydell@linaro.org --- hw/mips/boston.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index fb23161b33..14f0f6673b 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -505,10 +505,6 @@ static void boston_mach_init(MachineState *machine) "boston-platregs", 0x1000); memory_region_add_subregion_overlap(sys_mem, 0x17ffd000, platreg, 0); - if (!serial_hds[0]) { - serial_hds[0] = qemu_chr_new("serial0", "null"); - } - s->uart = serial_mm_init(sys_mem, 0x17ffe000, 2, get_cps_irq(s->cps, 3), 10000000, serial_hds[0], DEVICE_NATIVE_ENDIAN); From 6afd0e297ff7f16caf879251adcce7b1d07acd05 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:40 +0100 Subject: [PATCH 0040/2380] hw/mips/mips_malta: Don't create "null" chardevs for serial devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following commit 12051d82f004024, UART devices should handle being passed a NULL pointer chardev, so we don't need to create "null" backends in board code. Remove the code that does this and updates serial_hds[]. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Tested-by: Philippe Mathieu-Daudé Message-id: 20180420145249.32435-5-peter.maydell@linaro.org --- hw/mips/mips_malta.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index f6513a4fd5..49fe7a0a72 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1056,11 +1056,6 @@ void mips_malta_init(MachineState *machine) /* FPGA */ - /* Make sure the second serial port is associated with a device. */ - if (!serial_hds[2]) { - serial_hds[2] = qemu_chr_new("fpga-uart", "null"); - } - /* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */ malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hds[2]); From b82fcd30325815254eea7197337b3e15e0a46938 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:41 +0100 Subject: [PATCH 0041/2380] hw/xtensa/xtfpga.c: Don't create "null" chardevs for serial devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following commit 12051d82f004024, UART devices should handle being passed a NULL pointer chardev, so we don't need to create "null" backends in board code. Remove the code that does this and updates serial_hds[]. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Tested-by: Philippe Mathieu-Daudé Message-id: 20180420145249.32435-6-peter.maydell@linaro.org --- hw/xtensa/xtfpga.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 70686a2eb1..9db99e1f7e 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -278,10 +278,6 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) xtensa_get_extint(env, 1), nd_table); } - if (!serial_hds[0]) { - serial_hds[0] = qemu_chr_new("serial0", "null"); - } - serial_mm_init(system_io, 0x0d050020, 2, xtensa_get_extint(env, 0), 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); From a8d78cd0ab6204b6ee8897e30d0fbb9c8979ddca Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:42 +0100 Subject: [PATCH 0042/2380] vl.c: Provide accessor function serial_hd() for serial_hds[] array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide an accessor function serial_hd() to return the Chardev (if any) associated with the numbered serial port. This will be used to replace direct accesses to the serial_hds[] array, so that calling code doesn't need to care about the size of that array. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Tested-by: Philippe Mathieu-Daudé Message-id: 20180420145249.32435-7-peter.maydell@linaro.org --- include/sysemu/sysemu.h | 3 +++ vl.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 2b42151c63..bd5b55c514 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -163,6 +163,9 @@ void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); extern Chardev *serial_hds[MAX_SERIAL_PORTS]; +/* Return the Chardev for serial port i, or NULL if none */ +Chardev *serial_hd(int i); + /* parallel ports */ #define MAX_PARALLEL_PORTS 3 diff --git a/vl.c b/vl.c index fce1fd12d8..6daf026da6 100644 --- a/vl.c +++ b/vl.c @@ -2516,6 +2516,15 @@ static int serial_parse(const char *devname) return 0; } +Chardev *serial_hd(int i) +{ + assert(i >= 0); + if (i < ARRAY_SIZE(serial_hds)) { + return serial_hds[i]; + } + return NULL; +} + static int parallel_parse(const char *devname) { static int index = 0; From 9bca0edb282de0007a4f068d9d20f3e3c3aadef7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:43 +0100 Subject: [PATCH 0043/2380] Change references to serial_hds[] to serial_hd() Change all the uses of serial_hds[] to go via the new serial_hd() function. Code change produced with: find hw -name '*.[ch]' | xargs sed -i -e 's/serial_hds\[\([^]]*\)\]/serial_hd(\1)/g' Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Message-id: 20180420145249.32435-8-peter.maydell@linaro.org --- hw/arm/allwinner-a10.c | 4 ++-- hw/arm/aspeed_soc.c | 4 ++-- hw/arm/bcm2835_peripherals.c | 4 ++-- hw/arm/digic.c | 2 +- hw/arm/fsl-imx25.c | 2 +- hw/arm/fsl-imx31.c | 2 +- hw/arm/fsl-imx6.c | 4 ++-- hw/arm/fsl-imx7.c | 2 +- hw/arm/highbank.c | 2 +- hw/arm/integratorcp.c | 4 ++-- hw/arm/kzm.c | 4 ++-- hw/arm/mps2-tz.c | 2 +- hw/arm/mps2.c | 4 ++-- hw/arm/msf2-soc.c | 4 ++-- hw/arm/musicpal.c | 8 ++++---- hw/arm/omap1.c | 6 +++--- hw/arm/omap2.c | 10 +++++----- hw/arm/pxa2xx.c | 16 ++++++++-------- hw/arm/realview.c | 8 ++++---- hw/arm/stellaris.c | 2 +- hw/arm/stm32f205_soc.c | 2 +- hw/arm/strongarm.c | 2 +- hw/arm/versatilepb.c | 8 ++++---- hw/arm/vexpress.c | 8 ++++---- hw/arm/virt.c | 4 ++-- hw/arm/xilinx_zynq.c | 4 ++-- hw/arm/xlnx-zynqmp.c | 2 +- hw/char/exynos4210_uart.c | 2 +- hw/char/serial-isa.c | 4 ++-- hw/char/xen_console.c | 2 +- hw/cris/axis_dev88.c | 2 +- hw/hppa/machine.c | 4 ++-- hw/isa/isa-superio.c | 4 ++-- hw/lm32/lm32_boards.c | 8 ++++---- hw/lm32/milkymist.c | 4 ++-- hw/m68k/mcf5206.c | 4 ++-- hw/m68k/mcf5208.c | 6 +++--- hw/microblaze/petalogix_ml605_mmu.c | 2 +- hw/microblaze/petalogix_s3adsp1800_mmu.c | 2 +- hw/mips/boston.c | 2 +- hw/mips/mips_jazz.c | 8 ++++---- hw/mips/mips_malta.c | 2 +- hw/mips/mips_mipssim.c | 4 ++-- hw/misc/macio/macio.c | 4 ++-- hw/moxie/moxiesim.c | 4 ++-- hw/nios2/10m50_devboard.c | 2 +- hw/openrisc/openrisc_sim.c | 2 +- hw/ppc/e500.c | 12 ++++++------ hw/ppc/ppc405_uc.c | 16 ++++++++-------- hw/ppc/ppc440_bamboo.c | 8 ++++---- hw/ppc/sam460ex.c | 8 ++++---- hw/ppc/spapr.c | 4 ++-- hw/ppc/virtex_ml507.c | 2 +- hw/riscv/sifive_e.c | 4 ++-- hw/riscv/sifive_u.c | 4 ++-- hw/riscv/spike.c | 4 ++-- hw/riscv/virt.c | 2 +- hw/sh4/r2d.c | 2 +- hw/sh4/sh7750.c | 4 ++-- hw/sparc/leon3.c | 4 ++-- hw/sparc/sun4m.c | 4 ++-- hw/sparc64/niagara.c | 4 ++-- hw/sparc64/sun4u.c | 2 +- hw/xtensa/sim.c | 4 ++-- hw/xtensa/xtfpga.c | 2 +- 65 files changed, 143 insertions(+), 143 deletions(-) diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 5dbbacb7e8..c5fbc654f2 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -108,9 +108,9 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, AW_A10_SATA_BASE); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, s->irq[56]); - /* FIXME use a qdev chardev prop instead of serial_hds[] */ + /* FIXME use a qdev chardev prop instead of serial_hd() */ serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1], - 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); } static void aw_a10_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 407f10d0d4..1955a892f4 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -228,11 +228,11 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, ASPEED_SOC_SCU_BASE); /* UART - attach an 8250 to the IO space as our UART5 */ - if (serial_hds[0]) { + if (serial_hd(0)) { qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]); serial_mm_init(get_system_memory(), ASPEED_SOC_IOMEM_BASE + ASPEED_SOC_UART_5_BASE, 2, - uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN); + uart5, 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN); } /* I2C */ diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 13b63970d7..6be7660e8c 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -166,7 +166,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic)); /* UART0 */ - qdev_prop_set_chr(DEVICE(s->uart0), "chardev", serial_hds[0]); + qdev_prop_set_chr(DEVICE(s->uart0), "chardev", serial_hd(0)); object_property_set_bool(OBJECT(s->uart0), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -179,7 +179,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, INTERRUPT_UART)); /* AUX / UART1 */ - qdev_prop_set_chr(DEVICE(&s->aux), "chardev", serial_hds[1]); + qdev_prop_set_chr(DEVICE(&s->aux), "chardev", serial_hd(1)); object_property_set_bool(OBJECT(&s->aux), true, "realized", &err); if (err) { diff --git a/hw/arm/digic.c b/hw/arm/digic.c index 6184020985..726abb9b48 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -85,7 +85,7 @@ static void digic_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i)); } - qdev_prop_set_chr(DEVICE(&s->uart), "chardev", serial_hds[0]); + qdev_prop_set_chr(DEVICE(&s->uart), "chardev", serial_hd(0)); object_property_set_bool(OBJECT(&s->uart), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index d7d064e5ce..9731833fa5 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -118,7 +118,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) }; if (i < MAX_SERIAL_PORTS) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); } object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index e6c788049d..8509915200 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -107,7 +107,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) }; if (i < MAX_SERIAL_PORTS) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); } object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index ea14de33c6..535ad5888b 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -189,7 +189,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) }; if (i < MAX_SERIAL_PORTS) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); } object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); @@ -438,7 +438,7 @@ static void fsl_imx6_class_init(ObjectClass *oc, void *data) dc->realize = fsl_imx6_realize; dc->desc = "i.MX6 SOC"; - /* Reason: Uses serial_hds[] in the realize() function */ + /* Reason: Uses serial_hd() in the realize() function */ dc->user_creatable = false; } diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 390b4310e6..2848d76d3c 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -391,7 +391,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) if (i < MAX_SERIAL_PORTS) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); } object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 88326d1bfd..6d42fce2c3 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -342,7 +342,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0xfff34000); sysbus_connect_irq(busdev, 0, pic[18]); - pl011_create(0xfff36000, pic[20], serial_hds[0]); + pl011_create(0xfff36000, pic[20], serial_hd(0)); dev = qdev_create(NULL, TYPE_HIGHBANK_REGISTERS); qdev_init_nofail(dev); diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 58b40efc19..4eceebb9ea 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -631,8 +631,8 @@ static void integratorcp_init(MachineState *machine) sysbus_create_varargs("integrator_pit", 0x13000000, pic[5], pic[6], pic[7], NULL); sysbus_create_simple("pl031", 0x15000000, pic[8]); - pl011_create(0x16000000, pic[1], serial_hds[0]); - pl011_create(0x17000000, pic[2], serial_hds[1]); + pl011_create(0x16000000, pic[1], serial_hd(0)); + pl011_create(0x17000000, pic[2], serial_hd(1)); icp = sysbus_create_simple(TYPE_ICP_CONTROL_REGS, 0xcb000000, qdev_get_gpio_in(sic, 3)); sysbus_create_simple("pl050_keyboard", 0x18000000, pic[3]); diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index f9c2228e31..864c7bd411 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -121,10 +121,10 @@ static void kzm_init(MachineState *machine) qdev_get_gpio_in(DEVICE(&s->soc.avic), 52)); } - if (serial_hds[2]) { /* touchscreen */ + if (serial_hd(2)) { /* touchscreen */ serial_mm_init(get_system_memory(), KZM_FPGA_ADDR+0x10, 0, qdev_get_gpio_in(DEVICE(&s->soc.avic), 52), - 14745600, serial_hds[2], DEVICE_NATIVE_ENDIAN); + 14745600, serial_hd(2), DEVICE_NATIVE_ENDIAN); } kzm_binfo.ram_size = machine->ram_size; diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 8c86cffa9e..4ae4a5cb2a 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -172,7 +172,7 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, { CMSDKAPBUART *uart = opaque; int i = uart - &mms->uart[0]; - Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL; + Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hd(i) : NULL; int rxirqno = i * 2; int txirqno = i * 2 + 1; int combirqno = i + 10; diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 694fb36866..eb550fad34 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -230,7 +230,7 @@ static void mps2_common_init(MachineState *machine) static const hwaddr uartbase[] = {0x40004000, 0x40005000, 0x40006000, 0x40007000, 0x40009000}; - Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL; + Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hd(i) : NULL; /* RX irq number; TX irq is always one greater */ static const int uartirq[] = {0, 2, 4, 18, 20}; qemu_irq txovrint = NULL, rxovrint = NULL; @@ -270,7 +270,7 @@ static void mps2_common_init(MachineState *machine) static const hwaddr uartbase[] = {0x40004000, 0x40005000, 0x4002c000, 0x4002d000, 0x4002e000}; - Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL; + Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hd(i) : NULL; Object *txrx_orgate; DeviceState *txrx_orgate_dev; diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index f68df56b97..75c44adf7d 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -138,10 +138,10 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) system_clock_scale = NANOSECONDS_PER_SECOND / s->m3clk; for (i = 0; i < MSF2_NUM_UARTS; i++) { - if (serial_hds[i]) { + if (serial_hd(i)) { serial_mm_init(get_system_memory(), uart_addr[i], 2, qdev_get_gpio_in(armv7m, uart_irq[i]), - 115200, serial_hds[i], DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(i), DEVICE_NATIVE_ENDIAN); } } diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 38d7322a19..c807010e83 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -1610,13 +1610,13 @@ static void musicpal_init(MachineState *machine) pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], pic[MP_TIMER4_IRQ], NULL); - if (serial_hds[0]) { + if (serial_hd(0)) { serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ], - 1825000, serial_hds[0], DEVICE_NATIVE_ENDIAN); + 1825000, serial_hd(0), DEVICE_NATIVE_ENDIAN); } - if (serial_hds[1]) { + if (serial_hd(1)) { serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ], - 1825000, serial_hds[1], DEVICE_NATIVE_ENDIAN); + 1825000, serial_hd(1), DEVICE_NATIVE_ENDIAN); } /* Register flash */ diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index b3a23a83d1..24673abfca 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -3963,21 +3963,21 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, omap_findclk(s, "uart1_ck"), s->drq[OMAP_DMA_UART1_TX], s->drq[OMAP_DMA_UART1_RX], "uart1", - serial_hds[0]); + serial_hd(0)); s->uart[1] = omap_uart_init(0xfffb0800, qdev_get_gpio_in(s->ih[1], OMAP_INT_UART2), omap_findclk(s, "uart2_ck"), omap_findclk(s, "uart2_ck"), s->drq[OMAP_DMA_UART2_TX], s->drq[OMAP_DMA_UART2_RX], "uart2", - serial_hds[0] ? serial_hds[1] : NULL); + serial_hd(0) ? serial_hd(1) : NULL); s->uart[2] = omap_uart_init(0xfffb9800, qdev_get_gpio_in(s->ih[0], OMAP_INT_UART3), omap_findclk(s, "uart3_ck"), omap_findclk(s, "uart3_ck"), s->drq[OMAP_DMA_UART3_TX], s->drq[OMAP_DMA_UART3_RX], "uart3", - serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL); + serial_hd(0) && serial_hd(1) ? serial_hd(2) : NULL); s->dpll[0] = omap_dpll_init(system_memory, 0xfffecf00, omap_findclk(s, "dpll1")); diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 647b119ba9..80663533e1 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -2349,7 +2349,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, s->drq[OMAP24XX_DMA_UART1_TX], s->drq[OMAP24XX_DMA_UART1_RX], "uart1", - serial_hds[0]); + serial_hd(0)); s->uart[1] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 20), qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_UART2_IRQ), @@ -2358,7 +2358,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, s->drq[OMAP24XX_DMA_UART2_TX], s->drq[OMAP24XX_DMA_UART2_RX], "uart2", - serial_hds[0] ? serial_hds[1] : NULL); + serial_hd(0) ? serial_hd(1) : NULL); s->uart[2] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 21), qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_UART3_IRQ), @@ -2367,7 +2367,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, s->drq[OMAP24XX_DMA_UART3_TX], s->drq[OMAP24XX_DMA_UART3_RX], "uart3", - serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL); + serial_hd(0) && serial_hd(1) ? serial_hd(2) : NULL); s->gptimer[0] = omap_gp_timer_init(omap_l4ta(s->l4, 7), qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER1), @@ -2519,8 +2519,8 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, omap_sti_init(omap_l4ta(s->l4, 18), sysmem, 0x54000000, qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_STI), omap_findclk(s, "emul_ck"), - serial_hds[0] && serial_hds[1] && serial_hds[2] ? - serial_hds[3] : NULL); + serial_hd(0) && serial_hd(1) && serial_hd(2) ? + serial_hd(3) : NULL); s->eac = omap_eac_init(omap_l4ta(s->l4, 32), qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_EAC_IRQ), diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 5805a2c858..928a0431d6 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -2106,21 +2106,21 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); for (i = 0; pxa270_serial[i].io_base; i++) { - if (serial_hds[i]) { + if (serial_hd(i)) { serial_mm_init(address_space, pxa270_serial[i].io_base, 2, qdev_get_gpio_in(s->pic, pxa270_serial[i].irqn), - 14857000 / 16, serial_hds[i], + 14857000 / 16, serial_hd(i), DEVICE_NATIVE_ENDIAN); } else { break; } } - if (serial_hds[i]) + if (serial_hd(i)) s->fir = pxa2xx_fir_init(address_space, 0x40800000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), - serial_hds[i]); + serial_hd(i)); s->lcd = pxa2xx_lcdc_init(address_space, 0x44000000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); @@ -2231,21 +2231,21 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); for (i = 0; pxa255_serial[i].io_base; i++) { - if (serial_hds[i]) { + if (serial_hd(i)) { serial_mm_init(address_space, pxa255_serial[i].io_base, 2, qdev_get_gpio_in(s->pic, pxa255_serial[i].irqn), - 14745600 / 16, serial_hds[i], + 14745600 / 16, serial_hd(i), DEVICE_NATIVE_ENDIAN); } else { break; } } - if (serial_hds[i]) + if (serial_hd(i)) s->fir = pxa2xx_fir_init(address_space, 0x40800000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), - serial_hds[i]); + serial_hd(i)); s->lcd = pxa2xx_lcdc_init(address_space, 0x44000000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 2139a62e25..cd585d9469 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -195,10 +195,10 @@ static void realview_init(MachineState *machine, sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]); sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]); - pl011_create(0x10009000, pic[12], serial_hds[0]); - pl011_create(0x1000a000, pic[13], serial_hds[1]); - pl011_create(0x1000b000, pic[14], serial_hds[2]); - pl011_create(0x1000c000, pic[15], serial_hds[3]); + pl011_create(0x10009000, pic[12], serial_hd(0)); + pl011_create(0x1000a000, pic[13], serial_hd(1)); + pl011_create(0x1000b000, pic[14], serial_hd(2)); + pl011_create(0x1000c000, pic[15], serial_hd(3)); /* DMA controller is optional, apparently. */ sysbus_create_simple("pl081", 0x10030000, pic[24]); diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index de7c0fc4a6..e886f54976 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1353,7 +1353,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) if (board->dc2 & (1 << i)) { pl011_luminary_create(0x4000c000 + i * 0x1000, qdev_get_gpio_in(nvic, uart_irq[i]), - serial_hds[i]); + serial_hd(i)); } } if (board->dc2 & (1 << 4)) { diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 1cd6374e07..f59418e7d0 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -136,7 +136,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) for (i = 0; i < STM_NUM_USARTS; i++) { dev = DEVICE(&(s->usart[i])); qdev_prop_set_chr(dev, "chardev", - i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL); + i < MAX_SERIAL_PORTS ? serial_hd(i) : NULL); object_property_set_bool(OBJECT(&s->usart[i]), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 4cdb3a670b..ec2627374d 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -1622,7 +1622,7 @@ StrongARMState *sa1110_init(MemoryRegion *sysmem, for (i = 0; sa_serial[i].io_base; i++) { DeviceState *dev = qdev_create(NULL, TYPE_STRONGARM_UART); - qdev_prop_set_chr(dev, "chardev", serial_hds[i]); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, sa_serial[i].io_base); diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index 418792cd02..e01e3192ff 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -283,10 +283,10 @@ static void versatile_init(MachineState *machine, int board_id) n--; } - pl011_create(0x101f1000, pic[12], serial_hds[0]); - pl011_create(0x101f2000, pic[13], serial_hds[1]); - pl011_create(0x101f3000, pic[14], serial_hds[2]); - pl011_create(0x10009000, sic[6], serial_hds[3]); + pl011_create(0x101f1000, pic[12], serial_hd(0)); + pl011_create(0x101f2000, pic[13], serial_hd(1)); + pl011_create(0x101f3000, pic[14], serial_hd(2)); + pl011_create(0x10009000, sic[6], serial_hd(3)); sysbus_create_simple("pl080", 0x10130000, pic[17]); sysbus_create_simple("sp804", 0x101e2000, pic[4]); diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 9fad79177a..f1e33c8a36 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -622,10 +622,10 @@ static void vexpress_common_init(MachineState *machine) sysbus_create_simple("pl050_keyboard", map[VE_KMI0], pic[12]); sysbus_create_simple("pl050_mouse", map[VE_KMI1], pic[13]); - pl011_create(map[VE_UART0], pic[5], serial_hds[0]); - pl011_create(map[VE_UART1], pic[6], serial_hds[1]); - pl011_create(map[VE_UART2], pic[7], serial_hds[2]); - pl011_create(map[VE_UART3], pic[8], serial_hds[3]); + pl011_create(map[VE_UART0], pic[5], serial_hd(0)); + pl011_create(map[VE_UART1], pic[6], serial_hd(1)); + pl011_create(map[VE_UART2], pic[7], serial_hd(2)); + pl011_create(map[VE_UART3], pic[8], serial_hd(3)); sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]); sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 94dcb125d3..a18291c5d5 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1371,11 +1371,11 @@ static void machvirt_init(MachineState *machine) fdt_add_pmu_nodes(vms); - create_uart(vms, pic, VIRT_UART, sysmem, serial_hds[0]); + create_uart(vms, pic, VIRT_UART, sysmem, serial_hd(0)); if (vms->secure) { create_secure_ram(vms, secure_sysmem); - create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hds[1]); + create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hd(1)); } create_rtc(vms, pic); diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 0f76333770..899a26326f 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -236,8 +236,8 @@ static void zynq_init(MachineState *machine) sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]); sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]); - cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hds[0]); - cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hds[1]); + cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hd(0)); + cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hd(1)); sysbus_create_varargs("cadence_ttc", 0xF8001000, pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL); diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 465796e97c..505253e0d2 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -374,7 +374,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { error_propagate(errp, err); diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 3957e78abf..c2bba03362 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -600,7 +600,7 @@ DeviceState *exynos4210_uart_create(hwaddr addr, MAX_SERIAL_PORTS); exit(1); } - chr = serial_hds[channel]; + chr = serial_hd(channel); if (!chr) { snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel); chr = qemu_chr_new(label, "null"); diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index d7c5cc11fe..eb5996159d 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -141,8 +141,8 @@ void serial_hds_isa_init(ISABus *bus, int from, int to) assert(to <= MAX_SERIAL_PORTS); for (i = from; i < to; ++i) { - if (serial_hds[i]) { - serial_isa_init(bus, i, serial_hds[i]); + if (serial_hd(i)) { + serial_isa_init(bus, i, serial_hd(i)); } } } diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index 5e68326c19..bdfaa40ed3 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -201,7 +201,7 @@ static int con_init(struct XenDevice *xendev) /* no Xen override, use qemu output device */ if (output == NULL) { if (con->xendev.dev) { - qemu_chr_fe_init(&con->chr, serial_hds[con->xendev.dev], + qemu_chr_fe_init(&con->chr, serial_hd(con->xendev.dev), &error_abort); } } else { diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index 9ccc4350a5..409f3d581a 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -337,7 +337,7 @@ void axisdev88_init(MachineState *machine) sysbus_create_varargs("etraxfs,timer", 0x3005e000, irq[0x1b], nmi[1], NULL); for (i = 0; i < 4; i++) { - etraxfs_ser_create(0x30026000 + i * 0x2000, irq[0x14 + i], serial_hds[i]); + etraxfs_ser_create(0x30026000 + i * 0x2000, irq[0x14 + i], serial_hd(i)); } if (kernel_filename) { diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 19033e268d..a1d6b0ebfb 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -108,10 +108,10 @@ static void machine_hppa_init(MachineState *machine) mc146818_rtc_init(isa_bus, 2000, rtc_irq); /* Serial code setup. */ - if (serial_hds[0]) { + if (serial_hd(0)) { uint32_t addr = DINO_UART_HPA + 0x800; serial_mm_init(addr_space, addr, 0, serial_irq, - 115200, serial_hds[0], DEVICE_BIG_ENDIAN); + 115200, serial_hd(0), DEVICE_BIG_ENDIAN); } /* SCSI disk setup. */ diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index b95608a003..76286c81a1 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -81,8 +81,8 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) break; } if (!k->serial.is_enabled || k->serial.is_enabled(sio, i)) { - /* FIXME use a qdev chardev prop instead of serial_hds[] */ - chr = serial_hds[i]; + /* FIXME use a qdev chardev prop instead of serial_hd() */ + chr = serial_hd(i); if (chr == NULL || chr->be) { name = g_strdup_printf("discarding-serial%d", i); chr = qemu_chr_new(name, "null"); diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index 527bcc229c..907e875d02 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -125,12 +125,12 @@ static void lm32_evr_init(MachineState *machine) irq[i] = qdev_get_gpio_in(env->pic_state, i); } - lm32_uart_create(uart0_base, irq[uart0_irq], serial_hds[0]); + lm32_uart_create(uart0_base, irq[uart0_irq], serial_hd(0)); sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]); sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]); /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(serial_hds[1]); + env->juart_state = lm32_juart_init(serial_hd(1)); reset_info->bootstrap_pc = flash_base; @@ -217,13 +217,13 @@ static void lm32_uclinux_init(MachineState *machine) irq[i] = qdev_get_gpio_in(env->pic_state, i); } - lm32_uart_create(uart0_base, irq[uart0_irq], serial_hds[0]); + lm32_uart_create(uart0_base, irq[uart0_irq], serial_hd(0)); sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]); sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]); sysbus_create_simple("lm32-timer", timer2_base, irq[timer2_irq]); /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(serial_hds[1]); + env->juart_state = lm32_juart_init(serial_hd(1)); reset_info->bootstrap_pc = flash_base; diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index 85d64fe58d..f9688e059e 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -151,7 +151,7 @@ milkymist_init(MachineState *machine) } g_free(bios_filename); - milkymist_uart_create(0x60000000, irq[0], serial_hds[0]); + milkymist_uart_create(0x60000000, irq[0], serial_hd(0)); milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3], 80000000, 0x10014d31, 0x0000041f, 0x00000001); milkymist_hpdmc_create(0x60002000); @@ -167,7 +167,7 @@ milkymist_init(MachineState *machine) 0x20000000, 0x1000, 0x20020000, 0x2000); /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(serial_hds[1]); + env->juart_state = lm32_juart_init(serial_hd(1)); if (kernel_filename) { uint64_t entry; diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index bd8e993c58..6ad1e4bd2d 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -543,8 +543,8 @@ qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, M68kCPU *cpu) pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14); s->timer[0] = m5206_timer_init(pic[9]); s->timer[1] = m5206_timer_init(pic[10]); - s->uart[0] = mcf_uart_init(pic[12], serial_hds[0]); - s->uart[1] = mcf_uart_init(pic[13], serial_hds[1]); + s->uart[0] = mcf_uart_init(pic[12], serial_hd(0)); + s->uart[1] = mcf_uart_init(pic[13], serial_hd(1)); s->cpu = cpu; m5206_mbar_reset(s); diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index fac0d09cbc..7aca58542e 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -247,9 +247,9 @@ static void mcf5208evb_init(MachineState *machine) /* Internal peripherals. */ pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); - mcf_uart_mm_init(0xfc060000, pic[26], serial_hds[0]); - mcf_uart_mm_init(0xfc064000, pic[27], serial_hds[1]); - mcf_uart_mm_init(0xfc068000, pic[28], serial_hds[2]); + mcf_uart_mm_init(0xfc060000, pic[26], serial_hd(0)); + mcf_uart_mm_init(0xfc064000, pic[27], serial_hd(1)); + mcf_uart_mm_init(0xfc068000, pic[28], serial_hd(2)); mcf5208_sys_init(address_space_mem, pic); diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index b664dc0f9c..cf6bf3f32a 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -125,7 +125,7 @@ petalogix_ml605_init(MachineState *machine) } serial_mm_init(address_space_mem, UART16550_BASEADDR + 0x1000, 2, - irq[UART16550_IRQ], 115200, serial_hds[0], + irq[UART16550_IRQ], 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); /* 2 timers at irq 2 @ 100 Mhz. */ diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 5cb4deb69e..1186002a76 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -103,7 +103,7 @@ petalogix_s3adsp1800_init(MachineState *machine) } xilinx_uartlite_create(UARTLITE_BASEADDR, irq[UARTLITE_IRQ], - serial_hds[0]); + serial_hd(0)); /* 2 timers at irq 2 @ 62 Mhz. */ dev = qdev_create(NULL, "xlnx.xps-timer"); diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 14f0f6673b..5302e5c885 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -507,7 +507,7 @@ static void boston_mach_init(MachineState *machine) s->uart = serial_mm_init(sys_mem, 0x17ffe000, 2, get_cps_irq(s->cps, 3), 10000000, - serial_hds[0], DEVICE_NATIVE_ENDIAN); + serial_hd(0), DEVICE_NATIVE_ENDIAN); lcd = g_new(MemoryRegion, 1); memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8); diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index 7223085547..90cb306f53 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -303,15 +303,15 @@ static void mips_jazz_init(MachineState *machine, memory_region_add_subregion(address_space, 0x80005000, i8042); /* Serial ports */ - if (serial_hds[0]) { + if (serial_hd(0)) { serial_mm_init(address_space, 0x80006000, 0, qdev_get_gpio_in(rc4030, 8), 8000000/16, - serial_hds[0], DEVICE_NATIVE_ENDIAN); + serial_hd(0), DEVICE_NATIVE_ENDIAN); } - if (serial_hds[1]) { + if (serial_hd(1)) { serial_mm_init(address_space, 0x80007000, 0, qdev_get_gpio_in(rc4030, 9), 8000000/16, - serial_hds[1], DEVICE_NATIVE_ENDIAN); + serial_hd(1), DEVICE_NATIVE_ENDIAN); } /* Parallel port */ diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 49fe7a0a72..af70ecffc0 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1057,7 +1057,7 @@ void mips_malta_init(MachineState *machine) /* FPGA */ /* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */ - malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hds[2]); + malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hd(2)); /* Load firmware in flash / BIOS. */ dinfo = drive_get(IF_PFLASH, 0, fl_idx); diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c index e0ba5efc84..241faa1d0f 100644 --- a/hw/mips/mips_mipssim.c +++ b/hw/mips/mips_mipssim.c @@ -213,8 +213,8 @@ mips_mipssim_init(MachineState *machine) /* A single 16450 sits at offset 0x3f8. It is attached to MIPS CPU INT2, which is interrupt 4. */ - if (serial_hds[0]) - serial_init(0x3f8, env->irq[4], 115200, serial_hds[0], + if (serial_hd(0)) + serial_init(0x3f8, env->irq[4], 115200, serial_hd(0), get_system_io()); if (nd_table[0].used) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index b74a6572b0..a0cefe5719 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -118,8 +118,8 @@ static void macio_common_realize(PCIDevice *d, Error **errp) qdev_prop_set_uint32(DEVICE(&s->escc), "disabled", 0); qdev_prop_set_uint32(DEVICE(&s->escc), "frequency", ESCC_CLOCK); qdev_prop_set_uint32(DEVICE(&s->escc), "it_shift", 4); - qdev_prop_set_chr(DEVICE(&s->escc), "chrA", serial_hds[0]); - qdev_prop_set_chr(DEVICE(&s->escc), "chrB", serial_hds[1]); + qdev_prop_set_chr(DEVICE(&s->escc), "chrA", serial_hd(0)); + qdev_prop_set_chr(DEVICE(&s->escc), "chrB", serial_hd(1)); qdev_prop_set_uint32(DEVICE(&s->escc), "chnBtype", escc_serial); qdev_prop_set_uint32(DEVICE(&s->escc), "chnAtype", escc_serial); object_property_set_bool(OBJECT(&s->escc), true, "realized", &err); diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c index 0bbf770795..d41247dbdc 100644 --- a/hw/moxie/moxiesim.c +++ b/hw/moxie/moxiesim.c @@ -141,9 +141,9 @@ static void moxiesim_init(MachineState *machine) } /* A single 16450 sits at offset 0x3f8. */ - if (serial_hds[0]) { + if (serial_hd(0)) { serial_mm_init(address_space_mem, 0x3f8, 0, env->irq[4], - 8000000/16, serial_hds[0], DEVICE_LITTLE_ENDIAN); + 8000000/16, serial_hd(0), DEVICE_LITTLE_ENDIAN); } } diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c index 42053b2ca9..36b49a420c 100644 --- a/hw/nios2/10m50_devboard.c +++ b/hw/nios2/10m50_devboard.c @@ -92,7 +92,7 @@ static void nios2_10m50_ghrd_init(MachineState *machine) /* Register: Altera 16550 UART */ serial_mm_init(address_space_mem, 0xf8001600, 2, irq[1], 115200, - serial_hds[0], DEVICE_NATIVE_ENDIAN); + serial_hd(0), DEVICE_NATIVE_ENDIAN); /* Register: Timer sys_clk_timer */ dev = qdev_create(NULL, "ALTR.timer"); diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index c755f11efd..a495a84a41 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -164,7 +164,7 @@ static void openrisc_sim_init(MachineState *machine) } serial_mm_init(get_system_memory(), 0x90000000, 0, serial_irq, - 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); openrisc_load_kernel(ram_size, kernel_filename); } diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 9a85a41362..2ddab7ed24 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -456,12 +456,12 @@ static int ppce500_load_device_tree(MachineState *machine, * device it finds in the dt as serial output device. And we generate * devices in reverse order to the dt. */ - if (serial_hds[1]) { + if (serial_hd(1)) { dt_serial_create(fdt, MPC8544_SERIAL1_REGS_OFFSET, soc, mpic, "serial1", 1, false); } - if (serial_hds[0]) { + if (serial_hd(0)) { dt_serial_create(fdt, MPC8544_SERIAL0_REGS_OFFSET, soc, mpic, "serial0", 0, true); } @@ -875,16 +875,16 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) mpicdev = ppce500_init_mpic(machine, params, ccsr_addr_space, irqs); /* Serial */ - if (serial_hds[0]) { + if (serial_hd(0)) { serial_mm_init(ccsr_addr_space, MPC8544_SERIAL0_REGS_OFFSET, 0, qdev_get_gpio_in(mpicdev, 42), 399193, - serial_hds[0], DEVICE_BIG_ENDIAN); + serial_hd(0), DEVICE_BIG_ENDIAN); } - if (serial_hds[1]) { + if (serial_hd(1)) { serial_mm_init(ccsr_addr_space, MPC8544_SERIAL1_REGS_OFFSET, 0, qdev_get_gpio_in(mpicdev, 42), 399193, - serial_hds[1], DEVICE_BIG_ENDIAN); + serial_hd(1), DEVICE_BIG_ENDIAN); } /* General Utility device */ diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 205ebcea93..34f8d57b07 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -1660,14 +1660,14 @@ CPUPPCState *ppc405cr_init(MemoryRegion *address_space_mem, dma_irqs[3] = pic[23]; ppc405_dma_init(env, dma_irqs); /* Serial ports */ - if (serial_hds[0] != NULL) { + if (serial_hd(0) != NULL) { serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], + PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } - if (serial_hds[1] != NULL) { + if (serial_hd(1) != NULL) { serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], + PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } /* IIC controller */ @@ -2023,14 +2023,14 @@ CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem, /* GPIO */ ppc405_gpio_init(0xef600700); /* Serial ports */ - if (serial_hds[0] != NULL) { + if (serial_hd(0) != NULL) { serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], + PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } - if (serial_hds[1] != NULL) { + if (serial_hd(1) != NULL) { serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], + PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } /* OCM */ diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 8641986a71..44e6a0c21b 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -238,14 +238,14 @@ static void bamboo_init(MachineState *machine) get_system_io(), 0, PPC440EP_PCI_IOLEN); memory_region_add_subregion(get_system_memory(), PPC440EP_PCI_IO, isa); - if (serial_hds[0] != NULL) { + if (serial_hd(0) != NULL) { serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], + PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } - if (serial_hds[1] != NULL) { + if (serial_hd(1) != NULL) { serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], + PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index dfff262f96..a48e6e6fce 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -522,14 +522,14 @@ static void sam460ex_init(MachineState *machine) /* SoC has 4 UARTs * but board has only one wired and two are present in fdt */ - if (serial_hds[0] != NULL) { + if (serial_hd(0) != NULL) { serial_mm_init(address_space_mem, 0x4ef600300, 0, uic[1][1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], + PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } - if (serial_hds[1] != NULL) { + if (serial_hd(1) != NULL) { serial_mm_init(address_space_mem, 0x4ef600400, 0, uic[0][1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], + PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index a81570e7c8..b0ecfaca9e 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2590,8 +2590,8 @@ static void spapr_machine_init(MachineState *machine) spapr->vio_bus = spapr_vio_bus_init(); for (i = 0; i < MAX_SERIAL_PORTS; i++) { - if (serial_hds[i]) { - spapr_vty_create(spapr->vio_bus, serial_hds[i]); + if (serial_hd(i)) { + spapr_vty_create(spapr->vio_bus, serial_hd(i)); } } diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 77a1778e07..a80cbdd7ee 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -251,7 +251,7 @@ static void virtex_init(MachineState *machine) } serial_mm_init(address_space_mem, UART16550_BASEADDR, 2, irq[UART16550_IRQ], - 115200, serial_hds[0], DEVICE_LITTLE_ENDIAN); + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); /* 2 timers at irq 2 @ 62 Mhz. */ dev = qdev_create(NULL, "xlnx.xps-timer"); diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 19eca36ff4..487244890e 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -162,13 +162,13 @@ static void riscv_sifive_e_init(MachineState *machine) sifive_mmio_emulate(sys_mem, "riscv.sifive.e.gpio0", memmap[SIFIVE_E_GPIO0].base, memmap[SIFIVE_E_GPIO0].size); sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART0].base, - serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART0_IRQ]); + serial_hd(0), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART0_IRQ]); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi0", memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0", memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size); /* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base, - serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART1_IRQ]); */ + serial_hd(1), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART1_IRQ]); */ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1", memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1", diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 1c2deefa6c..66616bacd7 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -296,9 +296,9 @@ static void riscv_sifive_u_init(MachineState *machine) SIFIVE_U_PLIC_CONTEXT_STRIDE, memmap[SIFIVE_U_PLIC].size); sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART0].base, - serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]); + serial_hd(0), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]); /* sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART1].base, - serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */ + serial_hd(1), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */ sifive_clint_create(memmap[SIFIVE_U_CLINT].base, memmap[SIFIVE_U_CLINT].size, smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 2d1f114d40..62857e4fa0 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -233,7 +233,7 @@ static void spike_v1_10_0_board_init(MachineState *machine) s->fdt, s->fdt_size); /* initialize HTIF using symbols found in load_kernel */ - htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]); + htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hd(0)); /* Core Local Interruptor (timer and IPI) */ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, @@ -330,7 +330,7 @@ static void spike_v1_09_1_board_init(MachineState *machine) config_string, config_string_len); /* initialize HTIF using symbols found in load_kernel */ - htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]); + htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hd(0)); /* Core Local Interruptor (timer and IPI) */ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index e2c214e86a..4f69eb2cff 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -382,7 +382,7 @@ static void riscv_virt_board_init(MachineState *machine) serial_mm_init(system_memory, memmap[VIRT_UART0].base, 0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193, - serial_hds[0], DEVICE_LITTLE_ENDIAN); + serial_hd(0), DEVICE_LITTLE_ENDIAN); } static int riscv_virt_board_sysbus_device_init(SysBusDevice *sysbusdev) diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 458ed83297..6b01d6eed8 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -271,7 +271,7 @@ static void r2d_init(MachineState *machine) busdev = SYS_BUS_DEVICE(dev); qdev_prop_set_uint32(dev, "vram-size", SM501_VRAM_SIZE); qdev_prop_set_uint32(dev, "base", 0x10000000); - qdev_prop_set_ptr(dev, "chr-state", serial_hds[2]); + qdev_prop_set_ptr(dev, "chr-state", serial_hd(2)); qdev_init_nofail(dev); sysbus_mmio_map(busdev, 0, 0x10000000); sysbus_mmio_map(busdev, 1, 0x13e00000); diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 166e4bd947..5a7d47d31e 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -773,7 +773,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) cpu->env.intc_handle = &s->intc; sh_serial_init(sysmem, 0x1fe00000, - 0, s->periph_freq, serial_hds[0], + 0, s->periph_freq, serial_hd(0), s->intc.irqs[SCI1_ERI], s->intc.irqs[SCI1_RXI], s->intc.irqs[SCI1_TXI], @@ -781,7 +781,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) NULL); sh_serial_init(sysmem, 0x1fe80000, SH_SERIAL_FEAT_SCIF, - s->periph_freq, serial_hds[1], + s->periph_freq, serial_hd(1), s->intc.irqs[SCIF_ERI], s->intc.irqs[SCIF_RXI], s->intc.irqs[SCIF_TXI], diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index bba3aa3dee..98fa6adae0 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -206,8 +206,8 @@ static void leon3_generic_hw_init(MachineState *machine) grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6); /* Allocate uart */ - if (serial_hds[0]) { - grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]); + if (serial_hd(0)) { + grlib_apbuart_create(0x80000100, serial_hd(0), cpu_irqs[3]); } } diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 6471aca25d..0ee779fafe 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -943,8 +943,8 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, qdev_prop_set_uint32(dev, "disabled", 0); qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); qdev_prop_set_uint32(dev, "it_shift", 1); - qdev_prop_set_chr(dev, "chrB", serial_hds[1]); - qdev_prop_set_chr(dev, "chrA", serial_hds[0]); + qdev_prop_set_chr(dev, "chrB", serial_hd(1)); + qdev_prop_set_chr(dev, "chrA", serial_hd(0)); qdev_prop_set_uint32(dev, "chnBtype", escc_serial); qdev_prop_set_uint32(dev, "chnAtype", escc_serial); qdev_init_nofail(dev); diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 1874477ef6..22c4655fde 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -156,9 +156,9 @@ static void niagara_init(MachineState *machine) exit(1); } } - if (serial_hds[0]) { + if (serial_hd(0)) { serial_mm_init(sysmem, NIAGARA_UART_BASE, 0, NULL, 115200, - serial_hds[0], DEVICE_BIG_ENDIAN); + serial_hd(0), DEVICE_BIG_ENDIAN); } empty_slot_init(NIAGARA_IOBBASE, NIAGARA_IOBSIZE); sun4v_rtc_init(NIAGARA_RTC_BASE); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 2044a52ded..9b441f704b 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -295,7 +295,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) i = 0; if (s->console_serial_base) { serial_mm_init(pci_address_space(pci_dev), s->console_serial_base, - 0, NULL, 115200, serial_hds[i], DEVICE_BIG_ENDIAN); + 0, NULL, 115200, serial_hd(i), DEVICE_BIG_ENDIAN); i++; } serial_hds_isa_init(s->isa_bus, i, MAX_SERIAL_PORTS); diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 5c0ba231d1..b6ccb3cd4a 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -90,8 +90,8 @@ static void xtensa_sim_init(MachineState *machine) get_system_memory()); } - if (serial_hds[0]) { - xtensa_sim_open_console(serial_hds[0]); + if (serial_hd(0)) { + xtensa_sim_open_console(serial_hd(0)); } if (kernel_filename) { uint64_t elf_entry; diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 9db99e1f7e..63734c70ec 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -279,7 +279,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) } serial_mm_init(system_io, 0x0d050020, 2, xtensa_get_extint(env, 0), - 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); dinfo = drive_get(IF_PFLASH, 0, 0); if (dinfo) { From fc38a1120c2fabb76546ef8980e6d18b5fb7e843 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:44 +0100 Subject: [PATCH 0044/2380] Remove checks on MAX_SERIAL_PORTS that are just bounds checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove checks on MAX_SERIAL_PORTS that were just checking whether they were within bounds for the serial_hds[] array and falling back to NULL if not. This isn't needed with the serial_hd() function, which returns NULL for all indexes beyond what the user set up. Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180420145249.32435-9-peter.maydell@linaro.org --- hw/arm/fsl-imx25.c | 4 +--- hw/arm/fsl-imx31.c | 4 +--- hw/arm/fsl-imx6.c | 4 +--- hw/arm/fsl-imx7.c | 4 +--- hw/arm/mps2-tz.c | 3 +-- hw/arm/mps2.c | 6 ++---- hw/arm/stm32f205_soc.c | 3 +-- 7 files changed, 8 insertions(+), 20 deletions(-) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 9731833fa5..37056f9e34 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -117,9 +117,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) { FSL_IMX25_UART5_ADDR, FSL_IMX25_UART5_IRQ } }; - if (i < MAX_SERIAL_PORTS) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); - } + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 8509915200..891850cf18 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -106,9 +106,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) { FSL_IMX31_UART2_ADDR, FSL_IMX31_UART2_IRQ }, }; - if (i < MAX_SERIAL_PORTS) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); - } + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 535ad5888b..4f51bd9eb5 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -188,9 +188,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) { FSL_IMX6_UART5_ADDR, FSL_IMX6_UART5_IRQ }, }; - if (i < MAX_SERIAL_PORTS) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); - } + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 2848d76d3c..26c1d27f7c 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -390,9 +390,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) }; - if (i < MAX_SERIAL_PORTS) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); - } + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &error_abort); diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 4ae4a5cb2a..8dc8bfd4ab 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -172,7 +172,6 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, { CMSDKAPBUART *uart = opaque; int i = uart - &mms->uart[0]; - Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hd(i) : NULL; int rxirqno = i * 2; int txirqno = i * 2 + 1; int combirqno = i + 10; @@ -182,7 +181,7 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, init_sysbus_child(OBJECT(mms), name, uart, sizeof(mms->uart[0]), TYPE_CMSDK_APB_UART); - qdev_prop_set_chr(DEVICE(uart), "chardev", uartchr); + qdev_prop_set_chr(DEVICE(uart), "chardev", serial_hd(i)); qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", SYSCLK_FRQ); object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal); s = SYS_BUS_DEVICE(uart); diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index eb550fad34..c3946da317 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -230,7 +230,6 @@ static void mps2_common_init(MachineState *machine) static const hwaddr uartbase[] = {0x40004000, 0x40005000, 0x40006000, 0x40007000, 0x40009000}; - Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hd(i) : NULL; /* RX irq number; TX irq is always one greater */ static const int uartirq[] = {0, 2, 4, 18, 20}; qemu_irq txovrint = NULL, rxovrint = NULL; @@ -245,7 +244,7 @@ static void mps2_common_init(MachineState *machine) qdev_get_gpio_in(armv7m, uartirq[i]), txovrint, rxovrint, NULL, - uartchr, SYSCLK_FRQ); + serial_hd(i), SYSCLK_FRQ); } break; } @@ -270,7 +269,6 @@ static void mps2_common_init(MachineState *machine) static const hwaddr uartbase[] = {0x40004000, 0x40005000, 0x4002c000, 0x4002d000, 0x4002e000}; - Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hd(i) : NULL; Object *txrx_orgate; DeviceState *txrx_orgate_dev; @@ -287,7 +285,7 @@ static void mps2_common_init(MachineState *machine) qdev_get_gpio_in(orgate_dev, i * 2), qdev_get_gpio_in(orgate_dev, i * 2 + 1), NULL, - uartchr, SYSCLK_FRQ); + serial_hd(i), SYSCLK_FRQ); } break; } diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index f59418e7d0..2b2135d382 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -135,8 +135,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) /* Attach UART (uses USART registers) and USART controllers */ for (i = 0; i < STM_NUM_USARTS; i++) { dev = DEVICE(&(s->usart[i])); - qdev_prop_set_chr(dev, "chardev", - i < MAX_SERIAL_PORTS ? serial_hd(i) : NULL); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->usart[i]), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); From 97274d0c05d45ec8dbddfaac74d8b292ae8154a2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:45 +0100 Subject: [PATCH 0045/2380] hw/char/exynos4210_uart.c: Remove unneeded handling of NULL chardev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The handling of NULL chardevs in exynos4210_uart_create() is now all unnecessary: we don't need to create 'null' chardevs, and we don't need to enforce a bounds check on serial_hd(). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180420145249.32435-10-peter.maydell@linaro.org --- hw/char/exynos4210_uart.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index c2bba03362..a5a285655f 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -589,28 +589,8 @@ DeviceState *exynos4210_uart_create(hwaddr addr, DeviceState *dev; SysBusDevice *bus; - const char chr_name[] = "serial"; - char label[ARRAY_SIZE(chr_name) + 1]; - dev = qdev_create(NULL, TYPE_EXYNOS4210_UART); - if (!chr) { - if (channel >= MAX_SERIAL_PORTS) { - error_report("Only %d serial ports are supported by QEMU", - MAX_SERIAL_PORTS); - exit(1); - } - chr = serial_hd(channel); - if (!chr) { - snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel); - chr = qemu_chr_new(label, "null"); - if (!(chr)) { - error_report("Can't assign serial port to UART%d", channel); - exit(1); - } - } - } - qdev_prop_set_chr(dev, "chardev", chr); qdev_prop_set_uint32(dev, "channel", channel); qdev_prop_set_uint32(dev, "rx-size", fifo_size); From def337ffda34d331404bd7f1a42726b71500df22 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:46 +0100 Subject: [PATCH 0046/2380] serial-isa: Use MAX_ISA_SERIAL_PORTS instead of MAX_SERIAL_PORTS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ISA serial port handling in serial-isa.c imposes a limit of 4 serial ports. This is because we only know of 4 IO port and IRQ settings for them, and is unrelated to the generic MAX_SERIAL_PORTS limit, though they happen to both be set at 4 currently. Use a new MAX_ISA_SERIAL_PORTS wherever that is the correct limit to be checking against. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Tested-by: Philippe Mathieu-Daudé Message-id: 20180420145249.32435-11-peter.maydell@linaro.org --- hw/char/serial-isa.c | 10 +++++----- hw/i386/pc.c | 2 +- hw/mips/mips_r4k.c | 2 +- hw/ppc/pnv.c | 2 +- hw/sparc64/sun4u.c | 2 +- include/hw/char/serial.h | 3 +++ 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index eb5996159d..116b7b2e69 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -39,10 +39,10 @@ typedef struct ISASerialState { SerialState state; } ISASerialState; -static const int isa_serial_io[MAX_SERIAL_PORTS] = { +static const int isa_serial_io[MAX_ISA_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; -static const int isa_serial_irq[MAX_SERIAL_PORTS] = { +static const int isa_serial_irq[MAX_ISA_SERIAL_PORTS] = { 4, 3, 4, 3 }; @@ -56,9 +56,9 @@ static void serial_isa_realizefn(DeviceState *dev, Error **errp) if (isa->index == -1) { isa->index = index; } - if (isa->index >= MAX_SERIAL_PORTS) { + if (isa->index >= MAX_ISA_SERIAL_PORTS) { error_setg(errp, "Max. supported number of ISA serial ports is %d.", - MAX_SERIAL_PORTS); + MAX_ISA_SERIAL_PORTS); return; } if (isa->iobase == -1) { @@ -138,7 +138,7 @@ void serial_hds_isa_init(ISABus *bus, int from, int to) int i; assert(from >= 0); - assert(to <= MAX_SERIAL_PORTS); + assert(to <= MAX_ISA_SERIAL_PORTS); for (i = from; i < to; ++i) { if (serial_hd(i)) { diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d36bac8c89..b297a5d63b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1524,7 +1524,7 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport) qemu_irq *a20_line; ISADevice *i8042, *port92, *vmmouse; - serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); + serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS); parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); for (i = 0; i < MAX_FD; i++) { diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index aeadc4a340..e04b49d3c5 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -274,7 +274,7 @@ void mips_r4k_init(MachineState *machine) pit = i8254_pit_init(isa_bus, 0x40, 0, NULL); - serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); + serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS); isa_vga_init(isa_bus); diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 98ee3c607a..549cfccdcb 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -648,7 +648,7 @@ static void pnv_init(MachineState *machine) pnv->isa_bus = pnv_isa_create(pnv->chips[0]); /* Create serial port */ - serial_hds_isa_init(pnv->isa_bus, 0, MAX_SERIAL_PORTS); + serial_hds_isa_init(pnv->isa_bus, 0, MAX_ISA_SERIAL_PORTS); /* Create an RTC ISA device too */ mc146818_rtc_init(pnv->isa_bus, 2000, NULL); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 9b441f704b..1bede85370 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -298,7 +298,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) 0, NULL, 115200, serial_hd(i), DEVICE_BIG_ENDIAN); i++; } - serial_hds_isa_init(s->isa_bus, i, MAX_SERIAL_PORTS); + serial_hds_isa_init(s->isa_bus, i, MAX_ISA_SERIAL_PORTS); /* Parallel ports */ parallel_hds_isa_init(s->isa_bus, MAX_PARALLEL_PORTS); diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index c4daf11a14..0acfbbc382 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -95,6 +95,9 @@ SerialState *serial_mm_init(MemoryRegion *address_space, Chardev *chr, enum device_endian end); /* serial-isa.c */ + +#define MAX_ISA_SERIAL_PORTS 4 + #define TYPE_ISA_SERIAL "isa-serial" void serial_hds_isa_init(ISABus *bus, int from, int to); From 2cd4f8acb0e3416c7431d0e48d03f1a4c4a64cc1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:47 +0100 Subject: [PATCH 0047/2380] superio: Don't use MAX_SERIAL_PORTS for serial port limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The superio device has a limit on the number of serial ports it supports which is really only there because it has a fixed-size array serial[]. This limit isn't related particularly to the global MAX_SERIAL_PORTS limit, so use a different #define for it. (In practice the users of superio only ever want 2 serial ports.) Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20180420145249.32435-12-peter.maydell@linaro.org --- include/hw/isa/superio.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/hw/isa/superio.h b/include/hw/isa/superio.h index f9ba29aa30..345f006081 100644 --- a/include/hw/isa/superio.h +++ b/include/hw/isa/superio.h @@ -22,13 +22,15 @@ #define ISA_SUPERIO_CLASS(klass) \ OBJECT_CLASS_CHECK(ISASuperIOClass, (klass), TYPE_ISA_SUPERIO) +#define SUPERIO_MAX_SERIAL_PORTS 4 + typedef struct ISASuperIODevice { /*< private >*/ ISADevice parent_obj; /*< public >*/ ISADevice *parallel[MAX_PARALLEL_PORTS]; - ISADevice *serial[MAX_SERIAL_PORTS]; + ISADevice *serial[SUPERIO_MAX_SERIAL_PORTS]; ISADevice *floppy; ISADevice *kbc; ISADevice *ide; From 6af2692e86f9fdfb3d3d291c2708e81c3125d8e5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:48 +0100 Subject: [PATCH 0048/2380] vl.c: Remove compile time limit on number of serial ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having a fixed sized global serial_hds[] array, use a local dynamically reallocated one, so we don't have a compile time limit on how many serial ports a system has. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Tested-by: Philippe Mathieu-Daudé Message-id: 20180420145249.32435-13-peter.maydell@linaro.org --- include/sysemu/sysemu.h | 2 -- vl.c | 15 +++++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index bd5b55c514..989cbc2b7b 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -161,8 +161,6 @@ void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); #define MAX_SERIAL_PORTS 4 -extern Chardev *serial_hds[MAX_SERIAL_PORTS]; - /* Return the Chardev for serial port i, or NULL if none */ Chardev *serial_hd(int i); diff --git a/vl.c b/vl.c index 6daf026da6..a8a98c5a37 100644 --- a/vl.c +++ b/vl.c @@ -154,7 +154,8 @@ QEMUClockType rtc_clock; int vga_interface_type = VGA_NONE; static DisplayOptions dpy; int no_frame; -Chardev *serial_hds[MAX_SERIAL_PORTS]; +static int num_serial_hds = 0; +static Chardev **serial_hds = NULL; Chardev *parallel_hds[MAX_PARALLEL_PORTS]; Chardev *virtcon_hds[MAX_VIRTIO_CONSOLES]; Chardev *sclp_hds[MAX_SCLP_CONSOLES]; @@ -2496,30 +2497,28 @@ static int foreach_device_config(int type, int (*func)(const char *cmdline)) static int serial_parse(const char *devname) { - static int index = 0; + int index = num_serial_hds; char label[32]; if (strcmp(devname, "none") == 0) return 0; - if (index == MAX_SERIAL_PORTS) { - error_report("too many serial ports"); - exit(1); - } snprintf(label, sizeof(label), "serial%d", index); + serial_hds = g_renew(Chardev *, serial_hds, index + 1); + serial_hds[index] = qemu_chr_new(label, devname); if (!serial_hds[index]) { error_report("could not connect serial device" " to character backend '%s'", devname); return -1; } - index++; + num_serial_hds++; return 0; } Chardev *serial_hd(int i) { assert(i >= 0); - if (i < ARRAY_SIZE(serial_hds)) { + if (i < num_serial_hds) { return serial_hds[i]; } return NULL; From b8846a4d6352b2a1d2012f8b3b9115640524aeda Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 Apr 2018 15:52:49 +0100 Subject: [PATCH 0049/2380] vl.c: new function serial_max_hds() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a new function serial_max_hds() which returns the number of serial ports defined by the user. This is needed only by spapr. This allows us to remove the MAX_SERIAL_PORTS define. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Tested-by: Philippe Mathieu-Daudé Message-id: 20180420145249.32435-14-peter.maydell@linaro.org --- hw/ppc/spapr.c | 2 +- include/sysemu/sysemu.h | 6 ++++-- vl.c | 5 +++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b0ecfaca9e..92194a9a53 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2589,7 +2589,7 @@ static void spapr_machine_init(MachineState *machine) /* Set up VIO bus */ spapr->vio_bus = spapr_vio_bus_init(); - for (i = 0; i < MAX_SERIAL_PORTS; i++) { + for (i = 0; i < serial_max_hds(); i++) { if (serial_hd(i)) { spapr_vty_create(spapr->vio_bus, serial_hd(i)); } diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 989cbc2b7b..544ab77a2b 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -159,10 +159,12 @@ void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); /* serial ports */ -#define MAX_SERIAL_PORTS 4 - /* Return the Chardev for serial port i, or NULL if none */ Chardev *serial_hd(int i); +/* return the number of serial ports defined by the user. serial_hd(i) + * will always return NULL for any i which is greater than or equal to this. + */ +int serial_max_hds(void); /* parallel ports */ diff --git a/vl.c b/vl.c index a8a98c5a37..616956adf1 100644 --- a/vl.c +++ b/vl.c @@ -2524,6 +2524,11 @@ Chardev *serial_hd(int i) return NULL; } +int serial_max_hds(void) +{ + return num_serial_hds; +} + static int parallel_parse(const char *devname) { static int index = 0; From 5ac067a24a85fec57d2d87b2d12ae4ffa6aa2d9e Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 8 Mar 2018 18:07:26 +0000 Subject: [PATCH 0050/2380] checkpatch: Add xendevicemodel_handle to the list of types This avoids checkpatch misparsing (as statements) long function definitions or declarations, which sometimes start with constructs like this: static inline int xendevicemodel_relocate_memory( xendevicemodel_handle *dmod, domid_t domid, ... The type xendevicemodel_handle does not conform to Qemu CODING_STYLE, which would suggest CamelCase. However, it is a type defined by the Xen Project in xen.git. It would be possible to introduce a typedef to allow the qemu code to refer to it by a differently-spelled name, but that would obfuscate more than it would clarify. CC: Eric Blake CC: Paolo Bonzini CC: Daniel P. Berrange Signed-off-by: Ian Jackson Reviewed-by: Eric Blake --- scripts/checkpatch.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index d52207a3cc..5b8735defb 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -266,6 +266,7 @@ our @typeList = ( qr{target_(?:u)?long}, qr{hwaddr}, qr{xml${Ident}}, + qr{xendevicemodel_handle}, ); # This can be modified by sub possible. Since it can be empty, be careful From 7a64c17f3b3015bf741593a019538275c764455f Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 9 Mar 2018 12:02:50 +0000 Subject: [PATCH 0051/2380] AccelClass: Introduce accel_setup_post This is called just before os_setup_post. Currently none of the accelerators provide this hook, but the Xen one is going to provide one in a moment. Signed-off-by: Ian Jackson Reviewed-by: Eduardo Habkost --- accel/accel.c | 9 +++++++++ include/sysemu/accel.h | 3 +++ vl.c | 1 + 3 files changed, 13 insertions(+) diff --git a/accel/accel.c b/accel/accel.c index 93e2434c87..9cfab115d0 100644 --- a/accel/accel.c +++ b/accel/accel.c @@ -126,6 +126,15 @@ void accel_register_compat_props(AccelState *accel) register_compat_props_array(class->global_props); } +void accel_setup_post(MachineState *ms) +{ + AccelState *accel = ms->accelerator; + AccelClass *acc = ACCEL_GET_CLASS(accel); + if (acc->setup_post) { + acc->setup_post(ms, accel); + } +} + static void register_accel_types(void) { type_register_static(&accel_type); diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h index 5a632cee1d..637358f430 100644 --- a/include/sysemu/accel.h +++ b/include/sysemu/accel.h @@ -40,6 +40,7 @@ typedef struct AccelClass { const char *name; int (*available)(void); int (*init_machine)(MachineState *ms); + void (*setup_post)(MachineState *ms, AccelState *accel); bool *allowed; /* * Array of global properties that would be applied when specific @@ -68,5 +69,7 @@ extern unsigned long tcg_tb_size; void configure_accelerator(MachineState *ms); /* Register accelerator specific global properties */ void accel_register_compat_props(AccelState *accel); +/* Called just before os_setup_post (ie just before drop OS privs) */ +void accel_setup_post(MachineState *ms); #endif diff --git a/vl.c b/vl.c index 616956adf1..d37e8576d6 100644 --- a/vl.c +++ b/vl.c @@ -4742,6 +4742,7 @@ int main(int argc, char **argv, char **envp) vm_start(); } + accel_setup_post(current_machine); os_setup_post(); main_loop(); From 58ea9a7a025243d3c34f869d4c5ea7c174235c4e Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Mon, 25 Sep 2017 16:01:48 +0100 Subject: [PATCH 0052/2380] xen: link against xentoolcore Xen libraries in 4.10 include a new xentoolcore library. This contains the xentoolcore_restrict_all function which we are about to want to use. Signed-off-by: Ian Jackson Acked-by: Stefano Stabellini --- configure | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 0a19b033bc..5cf9dde235 100755 --- a/configure +++ b/configure @@ -2189,6 +2189,9 @@ if test "$xen" != "no" ; then xen=yes xen_pc="xencontrol xenstore xenguest xenforeignmemory xengnttab" xen_pc="$xen_pc xenevtchn xendevicemodel" + if $pkg_config --exists xentoolcore; then + xen_pc="$xen_pc xentoolcore" + fi QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags $xen_pc)" libs_softmmu="$($pkg_config --libs $xen_pc) $libs_softmmu" LDFLAGS="$($pkg_config --libs $xen_pc) $LDFLAGS" @@ -2220,18 +2223,20 @@ EOF cat > $TMPC < +#include int main(void) { xenforeignmemory_handle *xfmem; xfmem = xenforeignmemory_open(0, 0); xenforeignmemory_map2(xfmem, 0, 0, 0, 0, 0, 0, 0); + xentoolcore_restrict_all(0); return 0; } EOF - compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs" + compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs -lxentoolcore" then - xen_stable_libs="-lxendevicemodel $xen_stable_libs" + xen_stable_libs="-lxendevicemodel $xen_stable_libs -lxentoolcore" xen_ctrl_version=41000 xen=yes elif From 0ef4d87da58dfe0e0e466414701c103b7a44df65 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 15 Sep 2017 16:03:14 +0100 Subject: [PATCH 0053/2380] xen: restrict: use xentoolcore_restrict_all And insist that it works. Drop individual use of xendevicemodel_restrict and xenforeignmemory_restrict. These are not actually effective in this version of qemu, because qemu has a large number of fds open onto various Xen control devices. The restriction arrangements are still not right, because the restriction needs to be done very late - after qemu has opened all of its control fds. xentoolcore_restrict_all and xentoolcore.h are available in Xen 4.10 and later, only. Provide a compatibility stub. And drop the compatibility stubs for the old functions. Signed-off-by: Ian Jackson Reviewed-by: Anthony PERARD Acked-by: Stefano Stabellini --- include/hw/xen/xen_common.h | 46 +++++++++---------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 64a978e4e0..1766bb9fb5 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -91,6 +91,16 @@ static inline void *xenforeignmemory_map2(xenforeignmemory_handle *h, return xenforeignmemory_map(h, dom, prot, pages, arr, err); } +static inline int xentoolcore_restrict_all(domid_t domid) +{ + errno = ENOTTY; + return -1; +} + +#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 41000 */ + +#include + #endif #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 @@ -218,20 +228,6 @@ static inline int xendevicemodel_set_mem_type( return xc_hvm_set_mem_type(dmod, domid, mem_type, first_pfn, nr); } -static inline int xendevicemodel_restrict( - xendevicemodel_handle *dmod, domid_t domid) -{ - errno = ENOTTY; - return -1; -} - -static inline int xenforeignmemory_restrict( - xenforeignmemory_handle *fmem, domid_t domid) -{ - errno = ENOTTY; - return -1; -} - #else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40900 */ #undef XC_WANT_COMPAT_DEVICEMODEL_API @@ -290,28 +286,8 @@ static inline int xen_modified_memory(domid_t domid, uint64_t first_pfn, static inline int xen_restrict(domid_t domid) { int rc; - - /* Attempt to restrict devicemodel operations */ - rc = xendevicemodel_restrict(xen_dmod, domid); + rc = xentoolcore_restrict_all(domid); trace_xen_domid_restrict(rc ? errno : 0); - - if (rc < 0) { - /* - * If errno is ENOTTY then restriction is not implemented so - * there's no point in trying to restrict other types of - * operation, but it should not be treated as a failure. - */ - if (errno == ENOTTY) { - return 0; - } - - return rc; - } - - /* Restrict foreignmemory operations */ - rc = xenforeignmemory_restrict(xen_fmem, domid); - trace_xen_domid_restrict(rc ? errno : 0); - return rc; } From 4564e63f80ace744093157782c3db45fc50a4836 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 15 Sep 2017 16:02:24 +0100 Subject: [PATCH 0054/2380] xen: defer call to xen_restrict until just before os_setup_post We need to restrict *all* the control fds that qemu opens. Looking in /proc/PID/fd shows there are many; their allocation seems scattered throughout Xen support code in qemu. We must postpone the restrict call until roughly the same time as qemu changes its uid, chroots (if applicable), and so on. There doesn't seem to be an appropriate hook already. The RunState change hook fires at different times depending on exactly what mode qemu is operating in. And it appears that no-one but the Xen code wants a hook at this phase of execution. So, introduce a bare call to a new function xen_setup_post, just before os_setup_post. Also provide the appropriate stub for when Xen compilation is disabled. We do the restriction before rather than after os_setup_post, because xen_restrict may need to open /dev/null, and os_setup_post might have called chroot. Currently this does not work with migration, because when running as the Xen device model qemu needs to signal to the toolstack that it is ready. It currently does this using xenstore, and for incoming migration (but not for ordinary startup) that happens after os_setup_post. It is correct that this happens late: we want the incoming migration stream to be processed by a restricted qemu. The fix for this will be to do the startup notification a different way, without using xenstore. (QMP is probably a reasonable choice.) So for now this restriction feature cannot be used in conjunction with migration. (Note that this is not a regression in this patch, because previously the -xen-restrict-domid call was, in fact, simply ineffective!) We will revisit this in the Xen 4.11 release cycle. Signed-off-by: Ian Jackson CC: Paolo Bonzini (maintainer:X86) CC: Richard Henderson (maintainer:X86) CC: Eduardo Habkost (maintainer:X86) CC: Michael S. Tsirkin (supporter:PC) Acked-by: Anthony PERARD --- hw/i386/xen/xen-hvm.c | 8 -------- hw/xen/xen-common.c | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index f24b7d4923..9c3b6b312e 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -1254,14 +1254,6 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } - if (xen_domid_restrict) { - rc = xen_restrict(xen_domid); - if (rc < 0) { - error_report("failed to restrict: error %d", errno); - goto err; - } - } - xen_create_ioreq_server(xen_domid, &state->ioservid); state->exit.notify = xen_exit_notifier; diff --git a/hw/xen/xen-common.c b/hw/xen/xen-common.c index 83099dd1b1..454777c587 100644 --- a/hw/xen/xen-common.c +++ b/hw/xen/xen-common.c @@ -117,6 +117,19 @@ static void xen_change_state_handler(void *opaque, int running, } } +static void xen_setup_post(MachineState *ms, AccelState *accel) +{ + int rc; + + if (xen_domid_restrict) { + rc = xen_restrict(xen_domid); + if (rc < 0) { + perror("xen: failed to restrict"); + exit(1); + } + } +} + static int xen_init(MachineState *ms) { xen_xc = xc_interface_open(0, 0, 0); @@ -165,6 +178,7 @@ static void xen_accel_class_init(ObjectClass *oc, void *data) AccelClass *ac = ACCEL_CLASS(oc); ac->name = "Xen"; ac->init_machine = xen_init; + ac->setup_post = xen_setup_post; ac->allowed = &xen_allowed; ac->global_props = xen_compat_props; } From 74aae6bff4fec3a655a2da7053ac2edbaf51e7b6 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 15 Sep 2017 17:50:47 +0100 Subject: [PATCH 0055/2380] xen: destroy_hvm_domain: Move reason into a variable We are going to want to reuse this. No functional change. Signed-off-by: Ian Jackson Reviewed-by: Anthony PERARD Acked-by: Stefano Stabellini --- hw/i386/xen/xen-hvm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 9c3b6b312e..3590d99934 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -1387,12 +1387,13 @@ void destroy_hvm_domain(bool reboot) xc_interface *xc_handle; int sts; + unsigned int reason = reboot ? SHUTDOWN_reboot : SHUTDOWN_poweroff; + xc_handle = xc_interface_open(0, 0, 0); if (xc_handle == NULL) { fprintf(stderr, "Cannot acquire xenctrl handle\n"); } else { - sts = xc_domain_shutdown(xc_handle, xen_domid, - reboot ? SHUTDOWN_reboot : SHUTDOWN_poweroff); + sts = xc_domain_shutdown(xc_handle, xen_domid, reason); if (sts != 0) { fprintf(stderr, "xc_domain_shutdown failed to issue %s, " "sts %d, %s\n", reboot ? "reboot" : "poweroff", From 85f3c64da3235ae9f7c66a5264170f5101145b30 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 3 Oct 2017 18:51:05 +0100 Subject: [PATCH 0056/2380] xen: move xc_interface compatibility fallback further up the file We are going to want to use the dummy xendevicemodel_handle type in new stub functions in the CONFIG_XEN_CTRL_INTERFACE_VERSION < 41000 section. So we need to provide that definition, or (as applicable) include the appropriate header, earlier in the file. (Ideally the newer compatibility layers would be at the bottom of the file, so that they can naturally benefit from the compatibility layers for earlier version. But that's rather too much for this series.) No functional change. Signed-off-by: Ian Jackson Acked-by: Anthony PERARD Acked-by: Stefano Stabellini --- include/hw/xen/xen_common.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 1766bb9fb5..60c4ebb6cb 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -78,6 +78,17 @@ static inline void *xenforeignmemory_map(xc_interface *h, uint32_t dom, extern xenforeignmemory_handle *xen_fmem; +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 + +typedef xc_interface xendevicemodel_handle; + +#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40900 */ + +#undef XC_WANT_COMPAT_DEVICEMODEL_API +#include + +#endif + #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41000 #define XEN_COMPAT_PHYSMAP @@ -105,8 +116,6 @@ static inline int xentoolcore_restrict_all(domid_t domid) #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 -typedef xc_interface xendevicemodel_handle; - static inline xendevicemodel_handle *xendevicemodel_open( struct xentoollog_logger *logger, unsigned int open_flags) { @@ -228,11 +237,6 @@ static inline int xendevicemodel_set_mem_type( return xc_hvm_set_mem_type(dmod, domid, mem_type, first_pfn, nr); } -#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40900 */ - -#undef XC_WANT_COMPAT_DEVICEMODEL_API -#include - #endif extern xendevicemodel_handle *xen_dmod; From 6b47c2aa780c98cea7a39cbe36c30d3923e5163e Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 15 Sep 2017 17:51:52 +0100 Subject: [PATCH 0057/2380] xen: destroy_hvm_domain: Try xendevicemodel_shutdown xc_interface_open etc. is not going to work if we have dropped privilege, but xendevicemodel_shutdown will if everything is new enough. xendevicemodel_shutdown is only availabe in Xen 4.10 and later, so provide a stub for earlier versions. Signed-off-by: Ian Jackson Reviewed-by: Anthony PERARD --- hw/i386/xen/xen-hvm.c | 12 ++++++++++++ include/hw/xen/xen_common.h | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 3590d99934..fb727bc6c2 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -1386,9 +1386,21 @@ void destroy_hvm_domain(bool reboot) { xc_interface *xc_handle; int sts; + int rc; unsigned int reason = reboot ? SHUTDOWN_reboot : SHUTDOWN_poweroff; + if (xen_dmod) { + rc = xendevicemodel_shutdown(xen_dmod, xen_domid, reason); + if (!rc) { + return; + } + if (errno != ENOTTY /* old Xen */) { + perror("xendevicemodel_shutdown failed"); + } + /* well, try the old thing then */ + } + xc_handle = xc_interface_open(0, 0, 0); if (xc_handle == NULL) { fprintf(stderr, "Cannot acquire xenctrl handle\n"); diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 60c4ebb6cb..4bd30a386b 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -108,6 +108,13 @@ static inline int xentoolcore_restrict_all(domid_t domid) return -1; } +static inline int xendevicemodel_shutdown(xendevicemodel_handle *dmod, + domid_t domid, unsigned int reason) +{ + errno = ENOTTY; + return -1; +} + #else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 41000 */ #include From f0a2171bf9f35b0430e18676a688b2c985f8917a Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 16 Apr 2018 15:08:03 +0100 Subject: [PATCH 0058/2380] os-posix: cleanup: Replace fprintfs with error_report in change_process_uid I'm going to be editing this function and it makes sense to clean up this style problem in advance. Signed-off-by: Ian Jackson CC: Paolo Bonzini CC: Markus Armbruster CC: Daniel P. Berrange CC: Michael Tokarev Reviewed-by: Peter Maydell Reviewed-by: Thomas Huth --- os-posix.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/os-posix.c b/os-posix.c index b9c2343b1e..560db955cb 100644 --- a/os-posix.c +++ b/os-posix.c @@ -167,20 +167,20 @@ static void change_process_uid(void) { if (user_pwd) { if (setgid(user_pwd->pw_gid) < 0) { - fprintf(stderr, "Failed to setgid(%d)\n", user_pwd->pw_gid); + error_report("Failed to setgid(%d)", user_pwd->pw_gid); exit(1); } if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { - fprintf(stderr, "Failed to initgroups(\"%s\", %d)\n", - user_pwd->pw_name, user_pwd->pw_gid); + error_report("Failed to initgroups(\"%s\", %d)", + user_pwd->pw_name, user_pwd->pw_gid); exit(1); } if (setuid(user_pwd->pw_uid) < 0) { - fprintf(stderr, "Failed to setuid(%d)\n", user_pwd->pw_uid); + error_report("Failed to setuid(%d)", user_pwd->pw_uid); exit(1); } if (setuid(0) != -1) { - fprintf(stderr, "Dropping privileges failed\n"); + error_report("Dropping privileges failed"); exit(1); } } From 2c42f1e80103cb926c0703d4c1ac1fb9c3e2c600 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 15 Sep 2017 18:10:44 +0100 Subject: [PATCH 0059/2380] os-posix: Provide new -runas : facility This allows the caller to specify a uid and gid to use, even if there is no corresponding password entry. This will be useful in certain Xen configurations. We don't support just -runas because: (i) deprivileging without calling setgroups would be ineffective (ii) given only a uid we don't know what gid we ought to use (since uids may eppear in multiple passwd file entries with different gids). Signed-off-by: Ian Jackson Reviewed-by: Anthony PERARD CC: Paolo Bonzini CC: Markus Armbruster CC: Daniel P. Berrange CC: Michael Tokarev Reviewed-by: Markus Armbruster --- os-posix.c | 77 +++++++++++++++++++++++++++++++++++++++++-------- qemu-options.hx | 3 +- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/os-posix.c b/os-posix.c index 560db955cb..0f59566639 100644 --- a/os-posix.c +++ b/os-posix.c @@ -41,7 +41,14 @@ #include #endif -static struct passwd *user_pwd; +/* + * Must set all three of these at once. + * Legal combinations are unset by name by uid + */ +static struct passwd *user_pwd; /* NULL non-NULL NULL */ +static uid_t user_uid = (uid_t)-1; /* -1 -1 >=0 */ +static gid_t user_gid = (gid_t)-1; /* -1 -1 >=0 */ + static const char *chroot_dir; static int daemonize; static int daemon_pipe; @@ -127,6 +134,33 @@ void os_set_proc_name(const char *s) #endif } + +static bool os_parse_runas_uid_gid(const char *optarg) +{ + unsigned long lv; + const char *ep; + uid_t got_uid; + gid_t got_gid; + int rc; + + rc = qemu_strtoul(optarg, &ep, 0, &lv); + got_uid = lv; /* overflow here is ID in C99 */ + if (rc || *ep != ':' || got_uid != lv || got_uid == (uid_t)-1) { + return false; + } + + rc = qemu_strtoul(ep + 1, 0, 0, &lv); + got_gid = lv; /* overflow here is ID in C99 */ + if (rc || got_gid != lv || got_gid == (gid_t)-1) { + return false; + } + + user_pwd = NULL; + user_uid = got_uid; + user_gid = got_gid; + return true; +} + /* * Parse OS specific command line options. * return 0 if option handled, -1 otherwise @@ -144,8 +178,13 @@ void os_parse_cmd_args(int index, const char *optarg) #endif case QEMU_OPTION_runas: user_pwd = getpwnam(optarg); - if (!user_pwd) { - fprintf(stderr, "User \"%s\" doesn't exist\n", optarg); + if (user_pwd) { + user_uid = -1; + user_gid = -1; + } else if (!os_parse_runas_uid_gid(optarg)) { + error_report("User \"%s\" doesn't exist" + " (and is not :)", + optarg); exit(1); } break; @@ -165,18 +204,32 @@ void os_parse_cmd_args(int index, const char *optarg) static void change_process_uid(void) { - if (user_pwd) { - if (setgid(user_pwd->pw_gid) < 0) { - error_report("Failed to setgid(%d)", user_pwd->pw_gid); + assert((user_uid == (uid_t)-1) || user_pwd == NULL); + assert((user_uid == (uid_t)-1) == + (user_gid == (gid_t)-1)); + + if (user_pwd || user_uid != (uid_t)-1) { + gid_t intended_gid = user_pwd ? user_pwd->pw_gid : user_gid; + uid_t intended_uid = user_pwd ? user_pwd->pw_uid : user_uid; + if (setgid(intended_gid) < 0) { + error_report("Failed to setgid(%d)", intended_gid); exit(1); } - if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { - error_report("Failed to initgroups(\"%s\", %d)", - user_pwd->pw_name, user_pwd->pw_gid); - exit(1); + if (user_pwd) { + if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { + error_report("Failed to initgroups(\"%s\", %d)", + user_pwd->pw_name, user_pwd->pw_gid); + exit(1); + } + } else { + if (setgroups(1, &user_gid) < 0) { + error_report("Failed to setgroups(1, [%d])", + user_gid); + exit(1); + } } - if (setuid(user_pwd->pw_uid) < 0) { - error_report("Failed to setuid(%d)", user_pwd->pw_uid); + if (setuid(intended_uid) < 0) { + error_report("Failed to setuid(%d)", intended_uid); exit(1); } if (setuid(0) != -1) { diff --git a/qemu-options.hx b/qemu-options.hx index ca4e412f2f..5fbf966292 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3765,7 +3765,8 @@ ETEXI #ifndef _WIN32 DEF("runas", HAS_ARG, QEMU_OPTION_runas, \ - "-runas user change to user id user just before starting the VM\n", + "-runas user change to user id user just before starting the VM\n" \ + " user can be numeric uid:gid instead\n", QEMU_ARCH_ALL) #endif STEXI From 2cbf8903530b936964dd3af7e2e5bf85c3955d5c Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Mon, 23 Oct 2017 10:27:27 +0100 Subject: [PATCH 0060/2380] xen: Use newly added dmops for mapping VGA memory Xen unstable (to be in 4.11) has two new dmops, relocate_memory and pin_memory_cacheattr. Use these to set up the VGA memory, replacing the previous calls to libxc. This allows the VGA console to work properly when QEMU is running restricted (-xen-domid-restrict). Wrapper functions are provided to allow QEMU to work with older versions of Xen. Tweak the error handling while making this change: * Report pin_memory_cacheattr errors. * Report errors even when DEBUG_HVM is not set. This is useful for trying to understand why VGA is not working, since otherwise it just fails silently. * Fix the return values when an error occurs. The functions now consistently return -1 and set errno. CC: Ian Jackson Signed-off-by: Ross Lagerwall Reviewed-by: Ian Jackson Signed-off-by: Ian Jackson Acked-by: Anthony PERARD --- configure | 19 +++++++++++++++ hw/i386/xen/xen-hvm.c | 46 ++++++++++++++++++++----------------- include/hw/xen/xen_common.h | 32 ++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 21 deletions(-) diff --git a/configure b/configure index 5cf9dde235..aa35aef14b 100755 --- a/configure +++ b/configure @@ -2221,6 +2221,25 @@ EOF # Xen unstable elif cat > $TMPC < +int main(void) { + xendevicemodel_handle *xd; + + xd = xendevicemodel_open(0, 0); + xendevicemodel_pin_memory_cacheattr(xd, 0, 0, 0, 0); + + return 0; +} +EOF + compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs -lxentoolcore" + then + xen_stable_libs="-lxendevicemodel $xen_stable_libs -lxentoolcore" + xen_ctrl_version=41100 + xen=yes + elif + cat > $TMPC < #include diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index fb727bc6c2..caa563be3d 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -347,7 +347,7 @@ static int xen_add_to_physmap(XenIOState *state, MemoryRegion *mr, hwaddr offset_within_region) { - unsigned long i = 0; + unsigned long nr_pages; int rc = 0; XenPhysmap *physmap = NULL; hwaddr pfn, start_gpfn; @@ -396,22 +396,26 @@ go_physmap: pfn = phys_offset >> TARGET_PAGE_BITS; start_gpfn = start_addr >> TARGET_PAGE_BITS; - for (i = 0; i < size >> TARGET_PAGE_BITS; i++) { - unsigned long idx = pfn + i; - xen_pfn_t gpfn = start_gpfn + i; + nr_pages = size >> TARGET_PAGE_BITS; + rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, nr_pages, pfn, + start_gpfn); + if (rc) { + int saved_errno = errno; - rc = xen_xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn); - if (rc) { - DPRINTF("add_to_physmap MFN %"PRI_xen_pfn" to PFN %" - PRI_xen_pfn" failed: %d (errno: %d)\n", idx, gpfn, rc, errno); - return -rc; - } + error_report("relocate_memory %lu pages from GFN %"HWADDR_PRIx + " to GFN %"HWADDR_PRIx" failed: %s", + nr_pages, pfn, start_gpfn, strerror(saved_errno)); + errno = saved_errno; + return -1; } - xc_domain_pin_memory_cacheattr(xen_xc, xen_domid, + rc = xendevicemodel_pin_memory_cacheattr(xen_dmod, xen_domid, start_addr >> TARGET_PAGE_BITS, (start_addr + size - 1) >> TARGET_PAGE_BITS, XEN_DOMCTL_MEM_CACHEATTR_WB); + if (rc) { + error_report("pin_memory_cacheattr failed: %s", strerror(errno)); + } return xen_save_physmap(state, physmap); } @@ -419,7 +423,6 @@ static int xen_remove_from_physmap(XenIOState *state, hwaddr start_addr, ram_addr_t size) { - unsigned long i = 0; int rc = 0; XenPhysmap *physmap = NULL; hwaddr phys_offset = 0; @@ -438,16 +441,17 @@ static int xen_remove_from_physmap(XenIOState *state, size >>= TARGET_PAGE_BITS; start_addr >>= TARGET_PAGE_BITS; phys_offset >>= TARGET_PAGE_BITS; - for (i = 0; i < size; i++) { - xen_pfn_t idx = start_addr + i; - xen_pfn_t gpfn = phys_offset + i; + rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, size, start_addr, + phys_offset); + if (rc) { + int saved_errno = errno; - rc = xen_xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn); - if (rc) { - fprintf(stderr, "add_to_physmap MFN %"PRI_xen_pfn" to PFN %" - PRI_xen_pfn" failed: %d (errno: %d)\n", idx, gpfn, rc, errno); - return -rc; - } + error_report("relocate_memory "RAM_ADDR_FMT" pages" + " from GFN %"HWADDR_PRIx + " to GFN %"HWADDR_PRIx" failed: %s", + size, start_addr, phys_offset, strerror(saved_errno)); + errno = saved_errno; + return -1; } QLIST_REMOVE(physmap, list); diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 4bd30a386b..2eed6fcba3 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -89,6 +89,38 @@ typedef xc_interface xendevicemodel_handle; #endif +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41100 + +static inline int xendevicemodel_relocate_memory( + xendevicemodel_handle *dmod, domid_t domid, uint32_t size, uint64_t src_gfn, + uint64_t dst_gfn) +{ + uint32_t i; + int rc; + + for (i = 0; i < size; i++) { + unsigned long idx = src_gfn + i; + xen_pfn_t gpfn = dst_gfn + i; + + rc = xc_domain_add_to_physmap(xen_xc, domid, XENMAPSPACE_gmfn, idx, + gpfn); + if (rc) { + return rc; + } + } + + return 0; +} + +static inline int xendevicemodel_pin_memory_cacheattr( + xendevicemodel_handle *dmod, domid_t domid, uint64_t start, uint64_t end, + uint32_t type) +{ + return xc_domain_pin_memory_cacheattr(xen_xc, domid, start, end, type); +} + +#endif /* CONFIG_XEN_CTRL_INTERFACE_VERSION < 41100 */ + #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41000 #define XEN_COMPAT_PHYSMAP From 1c4010fa09971bcb8c696aa1df350263a2bd0298 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 9 Mar 2018 16:08:55 +0000 Subject: [PATCH 0061/2380] xen: Remove now-obsolete xen_xc_domain_add_to_physmap The last user was just removed; remove this function, accordingly. Signed-off-by: Ian Jackson Acked-by: Anthony PERARD --- include/hw/xen/xen_common.h | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 2eed6fcba3..5f1402b494 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -645,28 +645,6 @@ static inline int xen_set_ioreq_server_state(domid_t dom, #endif -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40600 -static inline int xen_xc_domain_add_to_physmap(xc_interface *xch, uint32_t domid, - unsigned int space, - unsigned long idx, - xen_pfn_t gpfn) -{ - return xc_domain_add_to_physmap(xch, domid, space, idx, gpfn); -} -#else -static inline int xen_xc_domain_add_to_physmap(xc_interface *xch, uint32_t domid, - unsigned int space, - unsigned long idx, - xen_pfn_t gpfn) -{ - /* In Xen 4.6 rc is -1 and errno contains the error value. */ - int rc = xc_domain_add_to_physmap(xch, domid, space, idx, gpfn); - if (rc == -1) - return errno; - return rc; -} -#endif - #ifdef CONFIG_XEN_PV_DOMAIN_BUILD #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40700 static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref, From 4473348adf6640ca12b95bc29d1b06796994afb2 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Mon, 5 Mar 2018 10:07:46 +0000 Subject: [PATCH 0062/2380] xen: Expect xenstore write to fail when restricted Saving the current state to xenstore may fail when running restricted (in particular, after a migration). Therefore, don't report the error or exit when running restricted. Toolstacks that want to allow running QEMU restricted should instead make use of QMP events to listen for state changes. CC: Ian Jackson Signed-off-by: Ross Lagerwall Reviewed-by: Ian Jackson Acked-by: Anthony PERARD --- hw/xen/xen-common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/xen/xen-common.c b/hw/xen/xen-common.c index 454777c587..6ec14c73ca 100644 --- a/hw/xen/xen-common.c +++ b/hw/xen/xen-common.c @@ -101,7 +101,12 @@ static void xenstore_record_dm_state(struct xs_handle *xs, const char *state) } snprintf(path, sizeof (path), "device-model/%u/state", xen_domid); - if (!xs_write(xs, XBT_NULL, path, state, strlen(state))) { + /* + * This call may fail when running restricted so don't make it fatal in + * that case. Toolstacks should instead use QMP to listen for state changes. + */ + if (!xs_write(xs, XBT_NULL, path, state, strlen(state)) && + !xen_domid_restrict) { error_report("error recording dm state"); exit(1); } From 22cd4f4835b2053271e737b1679927b9b8aa4252 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 16 Apr 2018 15:15:51 +0100 Subject: [PATCH 0063/2380] os-posix: cleanup: Replace fprintf with error_report in remaining call sites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ian Jackson CC: Paolo Bonzini CC: Markus Armbruster CC: Daniel P. Berrange CC: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth --- os-posix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os-posix.c b/os-posix.c index 0f59566639..a2ba50d23f 100644 --- a/os-posix.c +++ b/os-posix.c @@ -129,7 +129,7 @@ void os_set_proc_name(const char *s) exit(1); } #else - fprintf(stderr, "Change of process name not supported by your OS\n"); + error_report("Change of process name not supported by your OS"); exit(1); #endif } @@ -243,7 +243,7 @@ static void change_root(void) { if (chroot_dir) { if (chroot(chroot_dir) < 0) { - fprintf(stderr, "chroot failed\n"); + error_report("chroot failed"); exit(1); } if (chdir("/")) { From a7aaec148e27193cc6f7d33d2f18f81eed011a5c Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 16 Apr 2018 15:16:23 +0100 Subject: [PATCH 0064/2380] os-posix: cleanup: Replace perror with error_report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit perror() is defined to fprintf(stderr,...). HACKING says fprintf(stderr,...) is wrong. So perror() is too. Signed-off-by: Ian Jackson CC: Paolo Bonzini CC: Markus Armbruster CC: Daniel P. Berrange CC: Michael Tokarev CC: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- os-posix.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/os-posix.c b/os-posix.c index a2ba50d23f..24eb7007dc 100644 --- a/os-posix.c +++ b/os-posix.c @@ -125,7 +125,7 @@ void os_set_proc_name(const char *s) /* Could rewrite argv[0] too, but that's a bit more complicated. This simple way is enough for `top'. */ if (prctl(PR_SET_NAME, name)) { - perror("unable to change process name"); + error_report("unable to change process name: %s", strerror(errno)); exit(1); } #else @@ -247,7 +247,7 @@ static void change_root(void) exit(1); } if (chdir("/")) { - perror("not able to chdir to /"); + error_report("not able to chdir to /: %s", strerror(errno)); exit(1); } } @@ -309,7 +309,7 @@ void os_setup_post(void) if (daemonize) { if (chdir("/")) { - perror("not able to chdir to /"); + error_report("not able to chdir to /: %s", strerror(errno)); exit(1); } TFR(fd = qemu_open("/dev/null", O_RDWR)); @@ -383,7 +383,7 @@ int os_mlock(void) ret = mlockall(MCL_CURRENT | MCL_FUTURE); if (ret < 0) { - perror("mlockall"); + error_report("mlockall: %s", strerror(errno)); } return ret; From 8bbe05d77360b73c1834808023016a778ccf55ca Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 25 Sep 2017 16:41:03 +0100 Subject: [PATCH 0065/2380] configure: do_compiler: Dump some extra info under bash This makes it much easier to find a particular thing in config.log. We have to use the ${BASH_LINENO[*]} syntax which is a syntax error in other shells, so test what shell we are running and use eval. The extra output is only printed if configure is run with bash. On systems where /bin/sh is not bash, it is necessary to say bash ./configure to get the extra debug info in the log. Suggested-by: Eric Blake Signed-off-by: Ian Jackson CC: Kent R. Spillner CC: Janosch Frank CC: Thomas Huth CC: Peter Maydell CC: Paolo Bonzini Tested-by: Thomas Huth --- configure | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure b/configure index aa35aef14b..f9ba9eaae7 100755 --- a/configure +++ b/configure @@ -60,6 +60,11 @@ do_compiler() { # is compiler binary to execute. local compiler="$1" shift + if test -n "$BASH_VERSION"; then eval ' + echo >>config.log " +funcs: ${FUNCNAME[*]} +lines: ${BASH_LINENO[*]}" + '; fi echo $compiler "$@" >> config.log $compiler "$@" >> config.log 2>&1 || return $? # Test passed. If this is an --enable-werror build, rerun From 1bc3117abad28d6465ecdb2c944b22943df0e4f3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 20 Apr 2018 10:48:19 +0200 Subject: [PATCH 0066/2380] vnc: fix use-after-free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When vnc_client_read() return value is -1 vs is not valid any more. Fixes: d49b87f0d1e0520443a990fc610d0f02bc63c556 Reported-by: Philippe Mathieu-Daudé Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Tested-by: Marc-André Lureau Tested-by: Philippe Mathieu-Daudé Message-id: 20180420084820.3873-1-kraxel@redhat.com --- ui/vnc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index e164eb798c..5526e54f48 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1539,13 +1539,14 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, VncState *vs = opaque; if (condition & G_IO_IN) { if (vnc_client_read(vs) < 0) { - goto end; + /* vs is free()ed here */ + return TRUE; } } if (condition & G_IO_OUT) { vnc_client_write(vs); } -end: + if (vs->disconnecting) { if (vs->ioc_tag != 0) { g_source_remove(vs->ioc_tag); From 62f27922b3f1e0253a6755d2c711cd0bc1e79f18 Mon Sep 17 00:00:00 2001 From: Elie Tournier Date: Fri, 13 Apr 2018 14:58:40 +0100 Subject: [PATCH 0067/2380] qapi: Parameter gl of DisplayType now accept an enum v2: Rebase on top of master v3: Fix the json format (Eric Blake) Fix a comparison issue (Gerd Hoffmann) Signed-off-by: Elie Tournier Message-id: 20180413135842.21325-2-tournier.elie@gmail.com Signed-off-by: Gerd Hoffmann --- qapi/ui.json | 20 +++++++++++++++++++- vl.c | 10 +++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/qapi/ui.json b/qapi/ui.json index 5d01ad4304..3ad7835992 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1019,6 +1019,24 @@ { 'struct' : 'DisplayGTK', 'data' : { '*grab-on-hover' : 'bool' } } + ## + # @DisplayGLMode: + # + # Display OpenGL mode. + # + # @off: Disable OpenGL (default). + # @on: Use OpenGL, pick context type automatically. + # Would better be named 'auto' but is called 'on' for backward + # compatibility with bool type. + # @core: Use OpenGL with Core (desktop) Context. + # @es: Use OpenGL with ES (embedded systems) Context. + # + # Since: 2.13 + # + ## + { 'enum' : 'DisplayGLMode', + 'data' : [ 'off', 'on', 'core', 'es' ] } + ## # @DisplayType: # @@ -1048,7 +1066,7 @@ 'base' : { 'type' : 'DisplayType', '*full-screen' : 'bool', '*window-close' : 'bool', - '*gl' : 'bool' }, + '*gl' : 'DisplayGLMode' }, 'discriminator' : 'type', 'data' : { 'default' : 'DisplayNoOpts', 'none' : 'DisplayNoOpts', diff --git a/vl.c b/vl.c index 616956adf1..f7804f7a18 100644 --- a/vl.c +++ b/vl.c @@ -2143,9 +2143,9 @@ static void parse_display(const char *p) opts = nextopt; dpy.has_gl = true; if (strstart(opts, "on", &nextopt)) { - dpy.gl = true; + dpy.gl = DISPLAYGL_MODE_ON; } else if (strstart(opts, "off", &nextopt)) { - dpy.gl = false; + dpy.gl = DISPLAYGL_MODE_OFF; } else { goto invalid_sdl_args; } @@ -2186,9 +2186,9 @@ static void parse_display(const char *p) opts = nextopt; dpy.has_gl = true; if (strstart(opts, "on", &nextopt)) { - dpy.gl = true; + dpy.gl = DISPLAYGL_MODE_ON; } else if (strstart(opts, "off", &nextopt)) { - dpy.gl = false; + dpy.gl = DISPLAYGL_MODE_OFF; } else { goto invalid_gtk_args; } @@ -4356,7 +4356,7 @@ int main(int argc, char **argv, char **envp) qemu_display_early_init(&dpy); qemu_console_early_init(); - if (dpy.has_gl && dpy.gl && display_opengl == 0) { + if (dpy.has_gl && dpy.gl != DISPLAYGL_MODE_OFF && display_opengl == 0) { #if defined(CONFIG_OPENGL) error_report("OpenGL is not supported by the display"); #else From 844fd50dbbcfc9e401895274bf4fb8da8e8d3f64 Mon Sep 17 00:00:00 2001 From: Elie Tournier Date: Fri, 13 Apr 2018 14:58:41 +0100 Subject: [PATCH 0068/2380] sdl: Move DisplayOptions global to sdl2_console Suggested-by: Gerd Hoffmann Signed-off-by: Elie Tournier Message-id: 20180413135842.21325-3-tournier.elie@gmail.com Signed-off-by: Gerd Hoffmann --- include/ui/sdl2.h | 1 + ui/sdl2.c | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h index 51084e6320..f43eecdbd6 100644 --- a/include/ui/sdl2.h +++ b/include/ui/sdl2.h @@ -14,6 +14,7 @@ struct sdl2_console { DisplayChangeListener dcl; DisplaySurface *surface; + DisplayOptions *opts; SDL_Texture *texture; SDL_Window *real_window; SDL_Renderer *real_renderer; diff --git a/ui/sdl2.c b/ui/sdl2.c index 83b917fa37..da037248c2 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -32,7 +32,6 @@ static int sdl2_num_outputs; static struct sdl2_console *sdl2_console; -static DisplayOptions *opts; static SDL_Surface *guest_sprite_surface; static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ @@ -566,7 +565,7 @@ static void handle_windowevent(SDL_Event *ev) break; case SDL_WINDOWEVENT_CLOSE: if (qemu_console_is_graphic(scon->dcl.con)) { - if (opts->has_window_close && !opts->window_close) { + if (scon->opts->has_window_close && !scon->opts->window_close) { allow_close = false; } if (allow_close) { @@ -613,7 +612,7 @@ void sdl2_poll_events(struct sdl2_console *scon) handle_textinput(ev); break; case SDL_QUIT: - if (opts->has_window_close && !opts->window_close) { + if (scon->opts->has_window_close && !scon->opts->window_close) { allow_close = false; } if (allow_close) { @@ -770,7 +769,6 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) SDL_SysWMinfo info; assert(o->type == DISPLAY_TYPE_SDL); - opts = o; #ifdef __linux__ /* on Linux, SDL may use fbcon|directfb|svgalib when run without @@ -806,6 +804,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) return; } sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs); + sdl2_console->opts = o; for (i = 0; i < sdl2_num_outputs; i++) { QemuConsole *con = qemu_console_lookup_by_index(i); assert(con != NULL); @@ -846,7 +845,8 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) g_free(filename); } - if (opts->has_full_screen && opts->full_screen) { + if (sdl2_console->opts->has_full_screen && + sdl2_console->opts->full_screen) { gui_fullscreen = 1; sdl_grab_start(0); } From 4d309c96635b7a961e3e86e2605a97ef945aeee2 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:45 +0000 Subject: [PATCH 0069/2380] uninorth: trivial style fixups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes sure we keep patchew/checkpatch happy during the remainder of this patchset. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 66991da975..710818e355 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -272,7 +272,6 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic, UNINState *d; /* Uninorth AGP bus */ - dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); @@ -302,16 +301,23 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic, static void unin_main_pci_host_realize(PCIDevice *d, Error **errp) { - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer + /* cache_line_size */ + d->config[0x0C] = 0x08; + /* latency_timer */ + d->config[0x0D] = 0x10; + /* capabilities_pointer */ + d->config[0x34] = 0x00; } static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp) { - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - // d->config[0x34] = 0x80; // capabilities_pointer + /* cache_line_size */ + d->config[0x0C] = 0x08; + /* latency_timer */ + d->config[0x0D] = 0x10; + /* capabilities_pointer + d->config[0x34] = 0x80; */ + /* * Set kMacRISCPCIAddressSelect (0x48) register to indicate PCI * memory space with base 0x80000000, size 0x10000000 for Apple's @@ -333,9 +339,12 @@ static void u3_agp_pci_host_realize(PCIDevice *d, Error **errp) static void unin_internal_pci_host_realize(PCIDevice *d, Error **errp) { - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer + /* cache_line_size */ + d->config[0x0C] = 0x08; + /* latency_timer */ + d->config[0x0D] = 0x10; + /* capabilities_pointer */ + d->config[0x34] = 0x00; } static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) From 3e0204e15e93e0358339a0feff8f0ec7fe513e20 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:46 +0000 Subject: [PATCH 0070/2380] uninorth: remove second set of uninorth token registers Commit 593c181160: "PPC: Newworld: Add second uninorth control register set" added a second set of uninorth registers at 0xf3000000. Testing MacOS 9.2 to MacOS X 10.4 reveals no accesses to this address and I can't find any reference to it in Apple's Core99.cpp source so I'm assuming that this was the result of another bug that has now been fixed. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/ppc/mac_newworld.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 2f5b6f651a..39944203a4 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -147,7 +147,6 @@ static void ppc_core99_init(MachineState *machine) qemu_irq *pic, **openpic_irqs; MemoryRegion *isa = g_new(MemoryRegion, 1); MemoryRegion *unin_memory = g_new(MemoryRegion, 1); - MemoryRegion *unin2_memory = g_new(MemoryRegion, 1); int linux_boot, i, j, k; MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); hwaddr kernel_base, initrd_base, cmdline_base = 0; @@ -282,9 +281,6 @@ static void ppc_core99_init(MachineState *machine) memory_region_init_io(unin_memory, NULL, &unin_ops, token, "unin", 0x1000); memory_region_add_subregion(get_system_memory(), 0xf8000000, unin_memory); - memory_region_init_io(unin2_memory, NULL, &unin_ops, token, "unin", 0x1000); - memory_region_add_subregion(get_system_memory(), 0xf3000000, unin2_memory); - openpic_irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); openpic_irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); From 0203459943ebcb366f68ff65f2191c18ee7533d9 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:47 +0000 Subject: [PATCH 0071/2380] uninorth: QOMify PCI and AGP host bridges Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 79 +++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 47 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 710818e355..1d4d3f5705 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -129,72 +129,61 @@ static const MemoryRegionOps unin_data_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static int pci_unin_main_init_device(SysBusDevice *dev) +static void pci_unin_main_init(Object *obj) { - PCIHostState *h; + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); /* Use values found on a real PowerMac */ /* Uninorth main bus */ - h = PCI_HOST_BRIDGE(dev); - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, dev, + obj, "pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, obj, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - - return 0; + sysbus_init_mmio(sbd, &h->conf_mem); + sysbus_init_mmio(sbd, &h->data_mem); } - -static int pci_u3_agp_init_device(SysBusDevice *dev) +static void pci_u3_agp_init(Object *obj) { - PCIHostState *h; + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); /* Uninorth U3 AGP bus */ - h = PCI_HOST_BRIDGE(dev); - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, dev, + obj, "pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, obj, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - - return 0; + sysbus_init_mmio(sbd, &h->conf_mem); + sysbus_init_mmio(sbd, &h->data_mem); } -static int pci_unin_agp_init_device(SysBusDevice *dev) +static void pci_unin_agp_init(Object *obj) { - PCIHostState *h; + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); /* Uninorth AGP bus */ - h = PCI_HOST_BRIDGE(dev); - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); + obj, "pci-conf-idx", 0x1000); memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, - dev, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - return 0; + obj, "pci-conf-data", 0x1000); + sysbus_init_mmio(sbd, &h->conf_mem); + sysbus_init_mmio(sbd, &h->data_mem); } -static int pci_unin_internal_init_device(SysBusDevice *dev) +static void pci_unin_internal_init(Object *obj) { - PCIHostState *h; + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); /* Uninorth internal bus */ - h = PCI_HOST_BRIDGE(dev); - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); + obj, "pci-conf-idx", 0x1000); memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, - dev, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - return 0; + obj, "pci-conf-data", 0x1000); + sysbus_init_mmio(sbd, &h->conf_mem); + sysbus_init_mmio(sbd, &h->data_mem); } PCIBus *pci_pmac_init(qemu_irq *pic, @@ -461,10 +450,8 @@ static const TypeInfo unin_internal_pci_host_info = { static void pci_unin_main_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = pci_unin_main_init_device; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -472,15 +459,14 @@ static const TypeInfo pci_unin_main_info = { .name = TYPE_UNI_NORTH_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(UNINState), + .instance_init = pci_unin_main_init, .class_init = pci_unin_main_class_init, }; static void pci_u3_agp_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = pci_u3_agp_init_device; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -488,15 +474,14 @@ static const TypeInfo pci_u3_agp_info = { .name = TYPE_U3_AGP_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(UNINState), + .instance_init = pci_u3_agp_init, .class_init = pci_u3_agp_class_init, }; static void pci_unin_agp_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = pci_unin_agp_init_device; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -504,15 +489,14 @@ static const TypeInfo pci_unin_agp_info = { .name = TYPE_UNI_NORTH_AGP_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(UNINState), + .instance_init = pci_unin_agp_init, .class_init = pci_unin_agp_class_init, }; static void pci_unin_internal_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = pci_unin_internal_init_device; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -520,6 +504,7 @@ static const TypeInfo pci_unin_internal_info = { .name = TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(UNINState), + .instance_init = pci_unin_internal_init, .class_init = pci_unin_internal_class_init, }; From 72941bb76aeeb63d24850bb7fdbb126b7190fd13 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:48 +0000 Subject: [PATCH 0072/2380] uninorth: remove stray PCIBus realize from mac_newworld.c After QOMification this is clearly no longer needed (and possibly hasn't been for some time). Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/ppc/mac_newworld.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 39944203a4..4ff1c293a8 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -350,7 +350,6 @@ static void ppc_core99_init(MachineState *machine) pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io()); machine_arch = ARCH_MAC99; } - object_property_set_bool(OBJECT(pci_bus), true, "realized", &error_abort); machine->usb |= defaults_enabled() && !machine->usb_disabled; From 5d2eaa02501c9a75a221caa443553d3cc6077cfd Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:49 +0000 Subject: [PATCH 0073/2380] uninorth: move uninorth definitions into uninorth.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mark Cave-Ayland [dwg: Added hw/hw.h #include as suggested by Philippe Mathieu-Daudé] Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 22 +-------------- include/hw/pci-host/uninorth.h | 51 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 include/hw/pci-host/uninorth.h diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 1d4d3f5705..600d675573 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -26,31 +26,11 @@ #include "hw/ppc/mac.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" +#include "hw/pci-host/uninorth.h" #include "trace.h" static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e }; -#define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost" -#define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost" -#define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost" -#define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost" - -#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) -#define UNI_NORTH_AGP_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) -#define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) -#define U3_AGP_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_U3_AGP_HOST_BRIDGE) - -typedef struct UNINState { - PCIHostState parent_obj; - - MemoryRegion pci_mmio; - MemoryRegion pci_hole; -} UNINState; - static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num) { return (irq_num + (pci_dev->devfn >> 3)) & 3; diff --git a/include/hw/pci-host/uninorth.h b/include/hw/pci-host/uninorth.h new file mode 100644 index 0000000000..0366376a3b --- /dev/null +++ b/include/hw/pci-host/uninorth.h @@ -0,0 +1,51 @@ +/* + * QEMU Uninorth PCI host (for all Mac99 and newer machines) + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef UNINORTH_H +#define UNINORTH_H + +#include "hw/hw.h" + +#define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost" +#define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost" +#define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost" +#define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost" + +#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) +#define UNI_NORTH_AGP_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) +#define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) +#define U3_AGP_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_U3_AGP_HOST_BRIDGE) + +typedef struct UNINState { + PCIHostState parent_obj; + + MemoryRegion pci_mmio; + MemoryRegion pci_hole; +} UNINState; + +#endif /* UNINORTH_H */ From 0f4b5415c31ed1fee02f5826fe0d2d585806fa95 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:50 +0000 Subject: [PATCH 0074/2380] uninorth: alter pci_pmac_init() and pci_pmac_u3_init() to return uninorth device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is in preparation for moving the device wiring into the New World machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 16 ++++++++-------- hw/ppc/mac.h | 10 ++++++---- hw/ppc/mac_newworld.c | 10 ++++++++-- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 600d675573..b081e3c153 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -166,9 +166,9 @@ static void pci_unin_internal_init(Object *obj) sysbus_init_mmio(sbd, &h->data_mem); } -PCIBus *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) +UNINState *pci_pmac_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io) { DeviceState *dev; SysBusDevice *s; @@ -228,12 +228,12 @@ PCIBus *pci_pmac_init(qemu_irq *pic, sysbus_mmio_map(s, 1, 0xf4c00000); #endif - return h->bus; + return d; } -PCIBus *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) +UNINState *pci_pmac_u3_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io) { DeviceState *dev; SysBusDevice *s; @@ -265,7 +265,7 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic, pci_create_simple(h->bus, 11 << 3, "u3-agp"); - return h->bus; + return d; } static void unin_main_pci_host_realize(PCIDevice *d, Error **errp) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index a02f797598..fcf13cb757 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -31,6 +31,8 @@ #include "hw/ide/internal.h" #include "hw/input/adb.h" #include "hw/misc/mos6522.h" +#include "hw/pci/pci_host.h" +#include "hw/pci-host/uninorth.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 @@ -86,12 +88,12 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, MemoryRegion *address_space_io); /* UniNorth PCI */ -PCIBus *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); -PCIBus *pci_pmac_u3_init(qemu_irq *pic, +UNINState *pci_pmac_init(qemu_irq *pic, MemoryRegion *address_space_mem, MemoryRegion *address_space_io); +UNINState *pci_pmac_u3_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io); /* Mac NVRAM */ #define TYPE_MACIO_NVRAM "macio-nvram" diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 4ff1c293a8..ccf34ee36c 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -151,6 +151,7 @@ static void ppc_core99_init(MachineState *machine) MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); hwaddr kernel_base, initrd_base, cmdline_base = 0; long kernel_size, initrd_size; + UNINState *uninorth_pci; PCIBus *pci_bus; NewWorldMacIOState *macio; MACIOIDEState *macio_ide; @@ -344,10 +345,12 @@ static void ppc_core99_init(MachineState *machine) if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { /* 970 gets a U3 bus */ - pci_bus = pci_pmac_u3_init(pic, get_system_memory(), get_system_io()); + uninorth_pci = pci_pmac_u3_init(pic, get_system_memory(), + get_system_io()); machine_arch = ARCH_MAC99_U3; } else { - pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io()); + uninorth_pci = pci_pmac_init(pic, get_system_memory(), + get_system_io()); machine_arch = ARCH_MAC99; } @@ -360,6 +363,9 @@ static void ppc_core99_init(MachineState *machine) tbfreq = TBFREQ; } + /* init basic PC hardware */ + pci_bus = PCI_HOST_BRIDGE(uninorth_pci)->bus; + /* MacIO */ macio = NEWWORLD_MACIO(pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO)); dev = DEVICE(macio); From a5ed75fe2e6625b2ab9ed0694d7a5c95a74b84f7 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 22:01:54 +0000 Subject: [PATCH 0075/2380] heathrow: remove obsolete heathow_init() function Instead wire up heathrow to the CPU and grackle PCI host using qdev GPIOs. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/intc/heathrow_pic.c | 23 +++++------------------ hw/ppc/mac.h | 4 ---- hw/ppc/mac_oldworld.c | 20 ++++++++++++-------- include/hw/intc/heathrow_pic.h | 2 +- 4 files changed, 18 insertions(+), 31 deletions(-) diff --git a/hw/intc/heathrow_pic.c b/hw/intc/heathrow_pic.c index 393fdd7326..b8b997deca 100644 --- a/hw/intc/heathrow_pic.c +++ b/hw/intc/heathrow_pic.c @@ -172,29 +172,16 @@ static void heathrow_init(Object *obj) HeathrowState *s = HEATHROW(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + /* only 1 CPU */ + qdev_init_gpio_out(DEVICE(obj), s->irqs, 1); + + qdev_init_gpio_in(DEVICE(obj), heathrow_set_irq, HEATHROW_NUM_IRQS); + memory_region_init_io(&s->mem, OBJECT(s), &heathrow_ops, s, "heathrow-pic", 0x1000); sysbus_init_mmio(sbd, &s->mem); } -DeviceState *heathrow_pic_init(int nb_cpus, qemu_irq **irqs, - qemu_irq **pic_irqs) -{ - DeviceState *d; - HeathrowState *s; - - d = qdev_create(NULL, TYPE_HEATHROW); - qdev_init_nofail(d); - - s = HEATHROW(d); - /* only 1 CPU */ - s->irqs = irqs[0]; - - *pic_irqs = qemu_allocate_irqs(heathrow_set_irq, s, HEATHROW_NUM_IRQS); - - return d; -} - static void heathrow_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index fcf13cb757..d661515e9d 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -77,10 +77,6 @@ void macio_ide_register_dma(MACIOIDEState *ide); void macio_init(PCIDevice *dev, MemoryRegion *pic_mem); -/* Heathrow PIC */ -DeviceState *heathrow_pic_init(int nb_cpus, qemu_irq **irqs, - qemu_irq **pic_irqs); - /* Grackle PCI */ #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 10e291ca22..9bd4ece16d 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -84,7 +84,7 @@ static void ppc_heathrow_init(MachineState *machine) PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; - qemu_irq *pic, **heathrow_irqs; + qemu_irq *pic; int linux_boot, i; MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); @@ -227,16 +227,15 @@ static void ppc_heathrow_init(MachineState *machine) memory_region_add_subregion(sysmem, 0xfe000000, isa); /* XXX: we register only 1 output pin for heathrow PIC */ - heathrow_irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); - heathrow_irqs[0] = - g_malloc0(smp_cpus * sizeof(qemu_irq) * 1); + pic_dev = qdev_create(NULL, TYPE_HEATHROW); + qdev_init_nofail(pic_dev); + /* Connect the heathrow PIC outputs to the 6xx bus */ for (i = 0; i < smp_cpus; i++) { switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_6xx: - heathrow_irqs[i] = heathrow_irqs[0] + (i * 1); - heathrow_irqs[i][0] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; + qdev_connect_gpio_out(pic_dev, 0, + ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]); break; default: error_report("Bus model not supported on OldWorld Mac machine"); @@ -244,6 +243,11 @@ static void ppc_heathrow_init(MachineState *machine) } } + pic = g_new0(qemu_irq, HEATHROW_NUM_IRQS); + for (i = 0; i < HEATHROW_NUM_IRQS; i++) { + pic[i] = qdev_get_gpio_in(pic_dev, i); + } + /* Timebase Frequency */ if (kvm_enabled()) { tbfreq = kvmppc_get_tbfreq(); @@ -256,7 +260,7 @@ static void ppc_heathrow_init(MachineState *machine) error_report("Only 6xx bus is supported on heathrow machine"); exit(1); } - pic_dev = heathrow_pic_init(1, heathrow_irqs, &pic); + pci_bus = pci_grackle_init(0xfec00000, pic, get_system_memory(), get_system_io()); diff --git a/include/hw/intc/heathrow_pic.h b/include/hw/intc/heathrow_pic.h index bc3ffaab87..56c2ef339f 100644 --- a/include/hw/intc/heathrow_pic.h +++ b/include/hw/intc/heathrow_pic.h @@ -41,7 +41,7 @@ typedef struct HeathrowState { MemoryRegion mem; HeathrowPICState pics[2]; - qemu_irq *irqs; + qemu_irq irqs[1]; } HeathrowState; #define HEATHROW_NUM_IRQS 64 From b0318ec10b2a97cac0cdce50a693a11f882c8549 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 22:01:55 +0000 Subject: [PATCH 0076/2380] grackle: general tidy-up and QOMify This is the first step towards removing the old-style pci_grackle_init() function. Following on from the previous commit we can now pass the heathrow device as an object link and wire up the heathrow IRQs via qdev GPIOs. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/grackle.c | 102 +++++++++++++++++++++++++++--------------- hw/ppc/mac.h | 2 +- hw/ppc/mac_oldworld.c | 2 +- 3 files changed, 67 insertions(+), 39 deletions(-) diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 033588b7d2..f8935246c3 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -27,6 +27,8 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/mac.h" #include "hw/pci/pci.h" +#include "hw/intc/heathrow_pic.h" +#include "qapi/error.h" #include "trace.h" #define GRACKLE_PCI_HOST_BRIDGE(obj) \ @@ -35,6 +37,8 @@ typedef struct GrackleState { PCIHostState parent_obj; + HeathrowState *pic; + qemu_irq irqs[4]; MemoryRegion pci_mmio; MemoryRegion pci_hole; } GrackleState; @@ -47,13 +51,22 @@ static int pci_grackle_map_irq(PCIDevice *pci_dev, int irq_num) static void pci_grackle_set_irq(void *opaque, int irq_num, int level) { - qemu_irq *pic = opaque; + GrackleState *s = opaque; trace_grackle_set_irq(irq_num, level); - qemu_set_irq(pic[irq_num + 0x15], level); + qemu_set_irq(s->irqs[irq_num], level); } -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, +static void grackle_init_irqs(GrackleState *s) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s->irqs); i++) { + s->irqs[i] = qdev_get_gpio_in(DEVICE(s->pic), 0x15 + i); + } +} + +PCIBus *pci_grackle_init(uint32_t base, DeviceState *pic_dev, MemoryRegion *address_space_mem, MemoryRegion *address_space_io) { @@ -63,60 +76,75 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, GrackleState *d; dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); phb = PCI_HOST_BRIDGE(dev); d = GRACKLE_PCI_HOST_BRIDGE(dev); - memory_region_init(&d->pci_mmio, OBJECT(s), "pci-mmio", 0x100000000ULL); - memory_region_init_alias(&d->pci_hole, OBJECT(s), "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x7e000000ULL); memory_region_add_subregion(address_space_mem, 0x80000000ULL, &d->pci_hole); - phb->bus = pci_register_root_bus(dev, NULL, - pci_grackle_set_irq, - pci_grackle_map_irq, - pic, - &d->pci_mmio, - address_space_io, - 0, 4, TYPE_PCI_BUS); - - pci_create_simple(phb->bus, 0, "grackle"); - qdev_init_nofail(dev); - sysbus_mmio_map(s, 0, base); sysbus_mmio_map(s, 1, base + 0x00200000); return phb->bus; } -static int pci_grackle_init_device(SysBusDevice *dev) +static void grackle_realize(DeviceState *dev, Error **errp) { - PCIHostState *phb; + GrackleState *s = GRACKLE_PCI_HOST_BRIDGE(dev); + PCIHostState *phb = PCI_HOST_BRIDGE(dev); - phb = PCI_HOST_BRIDGE(dev); + phb->bus = pci_register_root_bus(dev, NULL, + pci_grackle_set_irq, + pci_grackle_map_irq, + s, + &s->pci_mmio, + get_system_io(), + 0, 4, TYPE_PCI_BUS); - memory_region_init_io(&phb->conf_mem, OBJECT(dev), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&phb->data_mem, OBJECT(dev), &pci_host_data_le_ops, - dev, "pci-data-idx", 0x1000); - sysbus_init_mmio(dev, &phb->conf_mem); - sysbus_init_mmio(dev, &phb->data_mem); - - return 0; + pci_create_simple(phb->bus, 0, "grackle"); + grackle_init_irqs(s); } -static void grackle_pci_host_realize(PCIDevice *d, Error **errp) +static void grackle_init(Object *obj) +{ + GrackleState *s = GRACKLE_PCI_HOST_BRIDGE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PCIHostState *phb = PCI_HOST_BRIDGE(obj); + + memory_region_init(&s->pci_mmio, OBJECT(s), "pci-mmio", 0x100000000ULL); + memory_region_init_alias(&s->pci_hole, OBJECT(s), "pci-hole", &s->pci_mmio, + 0x80000000ULL, 0x7e000000ULL); + + memory_region_init_io(&phb->conf_mem, obj, &pci_host_conf_le_ops, + DEVICE(obj), "pci-conf-idx", 0x1000); + memory_region_init_io(&phb->data_mem, obj, &pci_host_data_le_ops, + DEVICE(obj), "pci-data-idx", 0x1000); + + object_property_add_link(obj, "pic", TYPE_HEATHROW, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + sysbus_init_mmio(sbd, &phb->conf_mem); + sysbus_init_mmio(sbd, &phb->data_mem); +} + +static void grackle_pci_realize(PCIDevice *d, Error **errp) { d->config[0x09] = 0x01; } static void grackle_pci_class_init(ObjectClass *klass, void *data) { - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->realize = grackle_pci_host_realize; + k->realize = grackle_pci_realize; k->vendor_id = PCI_VENDOR_ID_MOTOROLA; k->device_id = PCI_DEVICE_ID_MOTOROLA_MPC106; k->revision = 0x00; @@ -139,26 +167,26 @@ static const TypeInfo grackle_pci_info = { }, }; -static void pci_grackle_class_init(ObjectClass *klass, void *data) +static void grackle_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = pci_grackle_init_device; + dc->realize = grackle_realize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } -static const TypeInfo grackle_pci_host_info = { +static const TypeInfo grackle_host_info = { .name = TYPE_GRACKLE_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(GrackleState), - .class_init = pci_grackle_class_init, + .instance_init = grackle_init, + .class_init = grackle_class_init, }; static void grackle_register_types(void) { type_register_static(&grackle_pci_info); - type_register_static(&grackle_pci_host_info); + type_register_static(&grackle_host_info); } type_init(grackle_register_types) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index d661515e9d..695557b8bf 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -79,7 +79,7 @@ void macio_init(PCIDevice *dev, /* Grackle PCI */ #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, +PCIBus *pci_grackle_init(uint32_t base, DeviceState *pic_dev, MemoryRegion *address_space_mem, MemoryRegion *address_space_io); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 9bd4ece16d..d48abfef07 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -261,7 +261,7 @@ static void ppc_heathrow_init(MachineState *machine) exit(1); } - pci_bus = pci_grackle_init(0xfec00000, pic, + pci_bus = pci_grackle_init(0xfec00000, pic_dev, get_system_memory(), get_system_io()); pci_vga_init(pci_bus); From a773e64a8fd2a3ef97d6e405dbfb28c17660136d Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 22:01:56 +0000 Subject: [PATCH 0077/2380] grackle: remove deprecated pci_grackle_init() function Instead wire up the grackle device inside the Mac Old World machine. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/grackle.c | 28 +--------------------------- hw/ppc/mac.h | 3 --- hw/ppc/mac_oldworld.c | 21 ++++++++++++++++++--- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index f8935246c3..e4583d493b 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -66,33 +66,6 @@ static void grackle_init_irqs(GrackleState *s) } } -PCIBus *pci_grackle_init(uint32_t base, DeviceState *pic_dev, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) -{ - DeviceState *dev; - SysBusDevice *s; - PCIHostState *phb; - GrackleState *d; - - dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); - object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", - &error_abort); - qdev_init_nofail(dev); - - s = SYS_BUS_DEVICE(dev); - phb = PCI_HOST_BRIDGE(dev); - d = GRACKLE_PCI_HOST_BRIDGE(dev); - - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); - - sysbus_mmio_map(s, 0, base); - sysbus_mmio_map(s, 1, base + 0x00200000); - - return phb->bus; -} - static void grackle_realize(DeviceState *dev, Error **errp) { GrackleState *s = GRACKLE_PCI_HOST_BRIDGE(dev); @@ -132,6 +105,7 @@ static void grackle_init(Object *obj) sysbus_init_mmio(sbd, &phb->conf_mem); sysbus_init_mmio(sbd, &phb->data_mem); + sysbus_init_mmio(sbd, &s->pci_hole); } static void grackle_pci_realize(PCIDevice *d, Error **errp) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 695557b8bf..93f25d7acb 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -79,9 +79,6 @@ void macio_init(PCIDevice *dev, /* Grackle PCI */ #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" -PCIBus *pci_grackle_init(uint32_t base, DeviceState *pic_dev, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); /* UniNorth PCI */ UNINState *pci_pmac_init(qemu_irq *pic, diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index d48abfef07..f0246f43d3 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -34,6 +34,7 @@ #include "net/net.h" #include "hw/isa/isa.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" @@ -55,6 +56,8 @@ #define NDRV_VGA_FILENAME "qemu_vga.ndrv" +#define GRACKLE_BASE 0xfec00000 + static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) { @@ -94,6 +97,7 @@ static void ppc_heathrow_init(MachineState *machine) PCIBus *pci_bus; OldWorldMacIOState *macio; MACIOIDEState *macio_ide; + SysBusDevice *s; DeviceState *dev, *pic_dev; BusState *adb_bus; int bios_size, ndrv_size; @@ -261,9 +265,20 @@ static void ppc_heathrow_init(MachineState *machine) exit(1); } - pci_bus = pci_grackle_init(0xfec00000, pic_dev, - get_system_memory(), - get_system_io()); + /* Grackle PCI host bridge */ + dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, GRACKLE_BASE); + sysbus_mmio_map(s, 1, GRACKLE_BASE + 0x200000); + /* PCI hole */ + memory_region_add_subregion(get_system_memory(), 0x80000000ULL, + sysbus_mmio_get_region(s, 2)); + + pci_bus = PCI_HOST_BRIDGE(dev)->bus; + pci_vga_init(pci_bus); for (i = 0; i < nb_nics; i++) { From a94e5f998bf353e848a9ae7c679b06fff36b4698 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 22:01:57 +0000 Subject: [PATCH 0078/2380] grackle: move PCI IO (ISA) memory region into the grackle device This simplifies the Old World machine to simply mapping the ISA memory region into the main address space. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/grackle.c | 7 ++++++- hw/ppc/mac_oldworld.c | 9 +++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index e4583d493b..4810a4de79 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -41,6 +41,7 @@ typedef struct GrackleState { qemu_irq irqs[4]; MemoryRegion pci_mmio; MemoryRegion pci_hole; + MemoryRegion pci_io; } GrackleState; /* Don't know if this matches real hardware, but it agrees with OHW. */ @@ -76,7 +77,7 @@ static void grackle_realize(DeviceState *dev, Error **errp) pci_grackle_map_irq, s, &s->pci_mmio, - get_system_io(), + &s->pci_io, 0, 4, TYPE_PCI_BUS); pci_create_simple(phb->bus, 0, "grackle"); @@ -90,6 +91,9 @@ static void grackle_init(Object *obj) PCIHostState *phb = PCI_HOST_BRIDGE(obj); memory_region_init(&s->pci_mmio, OBJECT(s), "pci-mmio", 0x100000000ULL); + memory_region_init_io(&s->pci_io, OBJECT(s), &unassigned_io_ops, obj, + "pci-isa-mmio", 0x00200000); + memory_region_init_alias(&s->pci_hole, OBJECT(s), "pci-hole", &s->pci_mmio, 0x80000000ULL, 0x7e000000ULL); @@ -106,6 +110,7 @@ static void grackle_init(Object *obj) sysbus_init_mmio(sbd, &phb->conf_mem); sysbus_init_mmio(sbd, &phb->data_mem); sysbus_init_mmio(sbd, &s->pci_hole); + sysbus_init_mmio(sbd, &s->pci_io); } static void grackle_pci_realize(PCIDevice *d, Error **errp) diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index f0246f43d3..f9e63b8d83 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -91,7 +91,6 @@ static void ppc_heathrow_init(MachineState *machine) int linux_boot, i; MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); - MemoryRegion *isa = g_new(MemoryRegion, 1); uint32_t kernel_base, initrd_base, cmdline_base = 0; int32_t kernel_size, initrd_size; PCIBus *pci_bus; @@ -225,11 +224,6 @@ static void ppc_heathrow_init(MachineState *machine) } } - /* Register 2 MB of ISA IO space */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x00200000); - memory_region_add_subregion(sysmem, 0xfe000000, isa); - /* XXX: we register only 1 output pin for heathrow PIC */ pic_dev = qdev_create(NULL, TYPE_HEATHROW); qdev_init_nofail(pic_dev); @@ -276,6 +270,9 @@ static void ppc_heathrow_init(MachineState *machine) /* PCI hole */ memory_region_add_subregion(get_system_memory(), 0x80000000ULL, sysbus_mmio_get_region(s, 2)); + /* Register 2 MB of ISA IO space */ + memory_region_add_subregion(get_system_memory(), 0xfe000000, + sysbus_mmio_get_region(s, 3)); pci_bus = PCI_HOST_BRIDGE(dev)->bus; From ab1244b53d8781e1f2c3dcbdd890afe1b817ca02 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 22:01:58 +0000 Subject: [PATCH 0079/2380] mac_oldworld: remove pics IRQ array and wire up macio to heathrow directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce constants for the pre-defined Old World IRQs to help keep things readable. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/ppc/mac.h | 8 ++++++++ hw/ppc/mac_oldworld.c | 27 ++++++++++++++------------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 93f25d7acb..c5a33e96cb 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -47,6 +47,14 @@ #define ESCC_CLOCK 3686400 +/* Old World IRQs */ +#define OLDWORLD_CUDA_IRQ 0x12 +#define OLDWORLD_ESCCB_IRQ 0x10 +#define OLDWORLD_ESCCA_IRQ 0xf +#define OLDWORLD_IDE0_IRQ 0xd +#define OLDWORLD_IDE0_DMA_IRQ 0x2 +#define OLDWORLD_IDE1_IRQ 0xe +#define OLDWORLD_IDE1_DMA_IRQ 0x3 /* MacIO */ #define TYPE_MACIO_IDE "macio-ide" diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index f9e63b8d83..2bbcefa076 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -87,7 +87,6 @@ static void ppc_heathrow_init(MachineState *machine) PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; - qemu_irq *pic; int linux_boot, i; MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); @@ -241,11 +240,6 @@ static void ppc_heathrow_init(MachineState *machine) } } - pic = g_new0(qemu_irq, HEATHROW_NUM_IRQS); - for (i = 0; i < HEATHROW_NUM_IRQS; i++) { - pic[i] = qdev_get_gpio_in(pic_dev, i); - } - /* Timebase Frequency */ if (kvm_enabled()) { tbfreq = kvmppc_get_tbfreq(); @@ -287,13 +281,20 @@ static void ppc_heathrow_init(MachineState *machine) /* MacIO */ macio = OLDWORLD_MACIO(pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO)); dev = DEVICE(macio); - qdev_connect_gpio_out(dev, 0, pic[0x12]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x10]); /* ESCC-B */ - qdev_connect_gpio_out(dev, 2, pic[0x0F]); /* ESCC-A */ - qdev_connect_gpio_out(dev, 3, pic[0x0D]); /* IDE-0 */ - qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE-0 DMA */ - qdev_connect_gpio_out(dev, 5, pic[0x0E]); /* IDE-1 */ - qdev_connect_gpio_out(dev, 6, pic[0x03]); /* IDE-1 DMA */ + qdev_connect_gpio_out(dev, 0, + qdev_get_gpio_in(pic_dev, OLDWORLD_CUDA_IRQ)); + qdev_connect_gpio_out(dev, 1, + qdev_get_gpio_in(pic_dev, OLDWORLD_ESCCB_IRQ)); + qdev_connect_gpio_out(dev, 2, + qdev_get_gpio_in(pic_dev, OLDWORLD_ESCCA_IRQ)); + qdev_connect_gpio_out(dev, 3, + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_IRQ)); + qdev_connect_gpio_out(dev, 4, + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_DMA_IRQ)); + qdev_connect_gpio_out(dev, 5, + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_IRQ)); + qdev_connect_gpio_out(dev, 6, + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_DMA_IRQ)); qdev_prop_set_uint64(dev, "frequency", tbfreq); object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", &error_abort); From 20d2514ad88057b8ef400cc4f1484d9160a9781a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 22:01:59 +0000 Subject: [PATCH 0080/2380] mac_oldworld: move wiring of macio IRQs to macio_oldworld_realize() Since the macio device has a link to the PIC device, we can now wire up the IRQs directly via qdev GPIOs rather than having to use an intermediate array. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/macio.c | 37 +++++++++++++++++++++-------------- hw/ppc/mac_oldworld.c | 14 ------------- include/hw/misc/macio/macio.h | 1 - 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index a0cefe5719..dac7bcd15e 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -152,10 +152,9 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) { MacIOState *s = MACIO(d); OldWorldMacIOState *os = OLDWORLD_MACIO(d); + DeviceState *pic_dev = DEVICE(os->pic); Error *err = NULL; SysBusDevice *sysbus_dev; - int i; - int cur_irq = 0; macio_common_realize(d, &err); if (err) { @@ -164,11 +163,14 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) } sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + OLDWORLD_CUDA_IRQ)); sysbus_dev = SYS_BUS_DEVICE(&s->escc); - sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]); - sysbus_connect_irq(sysbus_dev, 1, os->irqs[cur_irq++]); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + OLDWORLD_ESCCB_IRQ)); + sysbus_connect_irq(sysbus_dev, 1, qdev_get_gpio_in(pic_dev, + OLDWORLD_ESCCA_IRQ)); object_property_set_bool(OBJECT(&os->nvram), true, "realized", &err); if (err) { @@ -186,15 +188,22 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) sysbus_mmio_get_region(sysbus_dev, 0)); /* IDE buses */ - for (i = 0; i < ARRAY_SIZE(os->ide); i++) { - qemu_irq irq0 = os->irqs[cur_irq++]; - qemu_irq irq1 = os->irqs[cur_irq++]; + macio_realize_ide(s, &os->ide[0], + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_IRQ), + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_DMA_IRQ), + 0x16, &err); + if (err) { + error_propagate(errp, err); + return; + } - macio_realize_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4), &err); - if (err) { - error_propagate(errp, err); - return; - } + macio_realize_ide(s, &os->ide[1], + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_IRQ), + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_DMA_IRQ), + 0x1a, &err); + if (err) { + error_propagate(errp, err); + return; } } @@ -219,8 +228,6 @@ static void macio_oldworld_init(Object *obj) DeviceState *dev; int i; - qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs)); - object_property_add_link(obj, "pic", TYPE_HEATHROW, (Object **) &os->pic, qdev_prop_allow_set_link_before_realize, diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 2bbcefa076..4608bab014 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -281,20 +281,6 @@ static void ppc_heathrow_init(MachineState *machine) /* MacIO */ macio = OLDWORLD_MACIO(pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO)); dev = DEVICE(macio); - qdev_connect_gpio_out(dev, 0, - qdev_get_gpio_in(pic_dev, OLDWORLD_CUDA_IRQ)); - qdev_connect_gpio_out(dev, 1, - qdev_get_gpio_in(pic_dev, OLDWORLD_ESCCB_IRQ)); - qdev_connect_gpio_out(dev, 2, - qdev_get_gpio_in(pic_dev, OLDWORLD_ESCCA_IRQ)); - qdev_connect_gpio_out(dev, 3, - qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_IRQ)); - qdev_connect_gpio_out(dev, 4, - qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_DMA_IRQ)); - qdev_connect_gpio_out(dev, 5, - qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_IRQ)); - qdev_connect_gpio_out(dev, 6, - qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_DMA_IRQ)); qdev_prop_set_uint64(dev, "frequency", tbfreq); object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", &error_abort); diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 4528282b36..64a2584a77 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -56,7 +56,6 @@ typedef struct OldWorldMacIOState { /*< public >*/ HeathrowState *pic; - qemu_irq irqs[7]; MacIONVRAMState nvram; MACIOIDEState ide[2]; From 132e9906d64beb8873ca5efe028a052dec6c4550 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:51 +0000 Subject: [PATCH 0081/2380] uninorth: move PCI mmio memory region initialisation into init function Whilst we are here, rename the memory regions to better reflect whether they belong to either a PCI or an AGP bus. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index b081e3c153..5b8fc3aa16 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -111,29 +111,39 @@ static const MemoryRegionOps unin_data_ops = { static void pci_unin_main_init(Object *obj) { + UNINState *s = UNI_NORTH_PCI_HOST_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj); /* Use values found on a real PowerMac */ /* Uninorth main bus */ memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - obj, "pci-conf-idx", 0x1000); + obj, "unin-pci-conf-idx", 0x1000); memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, obj, - "pci-conf-data", 0x1000); + "unin-pci-conf-data", 0x1000); + + memory_region_init(&s->pci_mmio, OBJECT(s), "unin-pci-mmio", + 0x100000000ULL); + sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); } static void pci_u3_agp_init(Object *obj) { + UNINState *s = U3_AGP_HOST_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj); /* Uninorth U3 AGP bus */ memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - obj, "pci-conf-idx", 0x1000); + obj, "unin-pci-conf-idx", 0x1000); memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, obj, - "pci-conf-data", 0x1000); + "unin-pci-conf-data", 0x1000); + + memory_region_init(&s->pci_mmio, OBJECT(s), "unin-pci-mmio", + 0x100000000ULL); + sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); } @@ -145,9 +155,9 @@ static void pci_unin_agp_init(Object *obj) /* Uninorth AGP bus */ memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - obj, "pci-conf-idx", 0x1000); + obj, "unin-agp-conf-idx", 0x1000); memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, - obj, "pci-conf-data", 0x1000); + obj, "unin-agp-conf-data", 0x1000); sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); } @@ -159,9 +169,9 @@ static void pci_unin_internal_init(Object *obj) /* Uninorth internal bus */ memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - obj, "pci-conf-idx", 0x1000); + obj, "unin-pci-conf-idx", 0x1000); memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, - obj, "pci-conf-data", 0x1000); + obj, "unin-pci-conf-data", 0x1000); sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); } @@ -182,7 +192,6 @@ UNINState *pci_pmac_init(qemu_irq *pic, s = SYS_BUS_DEVICE(dev); h = PCI_HOST_BRIDGE(s); d = UNI_NORTH_PCI_HOST_BRIDGE(dev); - memory_region_init(&d->pci_mmio, OBJECT(d), "pci-mmio", 0x100000000ULL); memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, 0x80000000ULL, 0x10000000ULL); memory_region_add_subregion(address_space_mem, 0x80000000ULL, @@ -247,7 +256,6 @@ UNINState *pci_pmac_u3_init(qemu_irq *pic, h = PCI_HOST_BRIDGE(dev); d = U3_AGP_HOST_BRIDGE(dev); - memory_region_init(&d->pci_mmio, OBJECT(d), "pci-mmio", 0x100000000ULL); memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, 0x80000000ULL, 0x70000000ULL); memory_region_add_subregion(address_space_mem, 0x80000000ULL, From 0b065209549fdd503fe109b09d78500bb05c9f7f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:52 +0000 Subject: [PATCH 0082/2380] uninorth: introduce temporary pic_irqs device property This is in preparation for moving the PCI bus wiring inside the uninorth host bridge devices. In the future it will be possible to remove this once the PICs have been switched to use qdev GPIOs. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 25 +++++++++++++++++++++++-- include/hw/pci-host/uninorth.h | 1 + 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 5b8fc3aa16..fc59698f06 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -188,6 +188,7 @@ UNINState *pci_pmac_init(qemu_irq *pic, /* Use values found on a real PowerMac */ /* Uninorth main bus */ dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); + qdev_prop_set_ptr(dev, "pic-irqs", pic); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); h = PCI_HOST_BRIDGE(s); @@ -199,7 +200,7 @@ UNINState *pci_pmac_init(qemu_irq *pic, h->bus = pci_register_root_bus(dev, NULL, pci_unin_set_irq, pci_unin_map_irq, - pic, + d->pic_irqs, &d->pci_mmio, address_space_io, PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); @@ -220,6 +221,7 @@ UNINState *pci_pmac_init(qemu_irq *pic, /* Uninorth AGP bus */ pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); + qdev_prop_set_ptr(dev, "pic-irqs", pic); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, 0xf0800000); @@ -251,6 +253,7 @@ UNINState *pci_pmac_u3_init(qemu_irq *pic, /* Uninorth AGP bus */ dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); + qdev_prop_set_ptr(dev, "pic-irqs", pic); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); h = PCI_HOST_BRIDGE(dev); @@ -263,7 +266,7 @@ UNINState *pci_pmac_u3_init(qemu_irq *pic, h->bus = pci_register_root_bus(dev, NULL, pci_unin_set_irq, pci_unin_map_irq, - pic, + d->pic_irqs, &d->pci_mmio, address_space_io, PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); @@ -436,10 +439,16 @@ static const TypeInfo unin_internal_pci_host_info = { }, }; +static Property pci_unin_main_properties[] = { + DEFINE_PROP_PTR("pic-irqs", UNINState, pic_irqs), + DEFINE_PROP_END_OF_LIST(), +}; + static void pci_unin_main_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->props = pci_unin_main_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -451,10 +460,16 @@ static const TypeInfo pci_unin_main_info = { .class_init = pci_unin_main_class_init, }; +static Property pci_u3_agp_properties[] = { + DEFINE_PROP_PTR("pic-irqs", UNINState, pic_irqs), + DEFINE_PROP_END_OF_LIST(), +}; + static void pci_u3_agp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->props = pci_u3_agp_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -466,10 +481,16 @@ static const TypeInfo pci_u3_agp_info = { .class_init = pci_u3_agp_class_init, }; +static Property pci_unin_agp_class_properties[] = { + DEFINE_PROP_PTR("pic-irqs", UNINState, pic_irqs), + DEFINE_PROP_END_OF_LIST(), +}; + static void pci_unin_agp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->props = pci_unin_agp_class_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } diff --git a/include/hw/pci-host/uninorth.h b/include/hw/pci-host/uninorth.h index 0366376a3b..e4fa11c145 100644 --- a/include/hw/pci-host/uninorth.h +++ b/include/hw/pci-host/uninorth.h @@ -44,6 +44,7 @@ typedef struct UNINState { PCIHostState parent_obj; + void *pic_irqs; MemoryRegion pci_mmio; MemoryRegion pci_hole; } UNINState; From 32cde6154cd252bfa23d05f43a165797e2430ff4 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:53 +0000 Subject: [PATCH 0083/2380] uninorth: move PCI host bridge bus initialisation into device realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the IO address space is fixed to use the standard system IO address space then we can also use the opportunity to remove the address_space_io parameter from pci_pmac_init() and pci_pmac_u3_init(). Note we also move the default mac99 PCI bus to the end of the initialisation list so that it becomes the default destination for any devices specified via -device without an explicit PCI bus provided. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 117 ++++++++++++++++++++++++----------------- hw/ppc/mac.h | 6 +-- hw/ppc/mac_newworld.c | 6 +-- 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index fc59698f06..426b3c4e33 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -109,6 +109,27 @@ static const MemoryRegionOps unin_data_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void pci_unin_main_realize(DeviceState *dev, Error **errp) +{ + UNINState *s = UNI_NORTH_PCI_HOST_BRIDGE(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + + h->bus = pci_register_root_bus(dev, NULL, + pci_unin_set_irq, pci_unin_map_irq, + s->pic_irqs, + &s->pci_mmio, + get_system_io(), + PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); + + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); + + /* DEC 21154 bridge */ +#if 0 + /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */ + pci_create_simple(h->bus, PCI_DEVFN(12, 0), "dec-21154"); +#endif +} + static void pci_unin_main_init(Object *obj) { UNINState *s = UNI_NORTH_PCI_HOST_BRIDGE(obj); @@ -129,6 +150,21 @@ static void pci_unin_main_init(Object *obj) sysbus_init_mmio(sbd, &h->data_mem); } +static void pci_u3_agp_realize(DeviceState *dev, Error **errp) +{ + UNINState *s = U3_AGP_HOST_BRIDGE(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + + h->bus = pci_register_root_bus(dev, NULL, + pci_unin_set_irq, pci_unin_map_irq, + s->pic_irqs, + &s->pci_mmio, + get_system_io(), + PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); + + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "u3-agp"); +} + static void pci_u3_agp_init(Object *obj) { UNINState *s = U3_AGP_HOST_BRIDGE(obj); @@ -148,6 +184,19 @@ static void pci_u3_agp_init(Object *obj) sysbus_init_mmio(sbd, &h->data_mem); } +static void pci_unin_agp_realize(DeviceState *dev, Error **errp) +{ + UNINState *s = UNI_NORTH_AGP_HOST_BRIDGE(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + + h->bus = pci_register_root_bus(dev, NULL, + pci_unin_set_irq, pci_unin_map_irq, + s->pic_irqs, + &s->pci_mmio, + get_system_io(), + PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); +} + static void pci_unin_agp_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); @@ -177,49 +226,14 @@ static void pci_unin_internal_init(Object *obj) } UNINState *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) + MemoryRegion *address_space_mem) { DeviceState *dev; SysBusDevice *s; - PCIHostState *h; UNINState *d; /* Use values found on a real PowerMac */ - /* Uninorth main bus */ - dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); - qdev_prop_set_ptr(dev, "pic-irqs", pic); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - h = PCI_HOST_BRIDGE(s); - d = UNI_NORTH_PCI_HOST_BRIDGE(dev); - memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x10000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); - - h->bus = pci_register_root_bus(dev, NULL, - pci_unin_set_irq, pci_unin_map_irq, - d->pic_irqs, - &d->pci_mmio, - address_space_io, - PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); - -#if 0 - pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north"); -#endif - - sysbus_mmio_map(s, 0, 0xf2800000); - sysbus_mmio_map(s, 1, 0xf2c00000); - - /* DEC 21154 bridge */ -#if 0 - /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */ - pci_create_simple(h->bus, PCI_DEVFN(12, 0), "dec-21154"); -#endif - /* Uninorth AGP bus */ - pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); qdev_prop_set_ptr(dev, "pic-irqs", pic); qdev_init_nofail(dev); @@ -239,16 +253,28 @@ UNINState *pci_pmac_init(qemu_irq *pic, sysbus_mmio_map(s, 1, 0xf4c00000); #endif + /* Uninorth main bus */ + dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); + qdev_prop_set_ptr(dev, "pic-irqs", pic); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + d = UNI_NORTH_PCI_HOST_BRIDGE(dev); + memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, + 0x80000000ULL, 0x10000000ULL); + memory_region_add_subregion(address_space_mem, 0x80000000ULL, + &d->pci_hole); + + sysbus_mmio_map(s, 0, 0xf2800000); + sysbus_mmio_map(s, 1, 0xf2c00000); + return d; } UNINState *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) + MemoryRegion *address_space_mem) { DeviceState *dev; SysBusDevice *s; - PCIHostState *h; UNINState *d; /* Uninorth AGP bus */ @@ -256,7 +282,6 @@ UNINState *pci_pmac_u3_init(qemu_irq *pic, qdev_prop_set_ptr(dev, "pic-irqs", pic); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); - h = PCI_HOST_BRIDGE(dev); d = U3_AGP_HOST_BRIDGE(dev); memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, @@ -264,18 +289,9 @@ UNINState *pci_pmac_u3_init(qemu_irq *pic, memory_region_add_subregion(address_space_mem, 0x80000000ULL, &d->pci_hole); - h->bus = pci_register_root_bus(dev, NULL, - pci_unin_set_irq, pci_unin_map_irq, - d->pic_irqs, - &d->pci_mmio, - address_space_io, - PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); - sysbus_mmio_map(s, 0, 0xf0800000); sysbus_mmio_map(s, 1, 0xf0c00000); - pci_create_simple(h->bus, 11 << 3, "u3-agp"); - return d; } @@ -448,6 +464,7 @@ static void pci_unin_main_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = pci_unin_main_realize; dc->props = pci_unin_main_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -469,6 +486,7 @@ static void pci_u3_agp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = pci_u3_agp_realize; dc->props = pci_u3_agp_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -490,6 +508,7 @@ static void pci_unin_agp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = pci_unin_agp_realize; dc->props = pci_unin_agp_class_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index c5a33e96cb..1ab2a3b354 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -90,11 +90,9 @@ void macio_init(PCIDevice *dev, /* UniNorth PCI */ UNINState *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); + MemoryRegion *address_space_mem); UNINState *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); + MemoryRegion *address_space_mem); /* Mac NVRAM */ #define TYPE_MACIO_NVRAM "macio-nvram" diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index ccf34ee36c..3367d7bb93 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -345,12 +345,10 @@ static void ppc_core99_init(MachineState *machine) if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { /* 970 gets a U3 bus */ - uninorth_pci = pci_pmac_u3_init(pic, get_system_memory(), - get_system_io()); + uninorth_pci = pci_pmac_u3_init(pic, get_system_memory()); machine_arch = ARCH_MAC99_U3; } else { - uninorth_pci = pci_pmac_init(pic, get_system_memory(), - get_system_io()); + uninorth_pci = pci_pmac_init(pic, get_system_memory()); machine_arch = ARCH_MAC99; } From c1d66d378c6dd1f112b753c98a308688dd0af24e Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:54 +0000 Subject: [PATCH 0084/2380] uninorth: fix PCI and AGP bus mixup Somewhere in the history of time, the initialisation of the PCI buses for the AGP and PCI host bridges got mixed up in that the PCI host bridge was creating an instance of the AGP PCI bus, and the AGP PCI bus was missing. Swap the PCI host bridge over to use the correct PCI bus (including setting the kMacRISCPCIAddressSelect register used by MacOS X) and add the missing reference to the AGP PCI bus. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 426b3c4e33..1f6752c294 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -121,7 +121,7 @@ static void pci_unin_main_realize(DeviceState *dev, Error **errp) get_system_io(), PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); - pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-pci"); /* DEC 21154 bridge */ #if 0 @@ -195,6 +195,8 @@ static void pci_unin_agp_realize(DeviceState *dev, Error **errp) &s->pci_mmio, get_system_io(), PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); + + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); } static void pci_unin_agp_init(Object *obj) @@ -303,16 +305,6 @@ static void unin_main_pci_host_realize(PCIDevice *d, Error **errp) d->config[0x0D] = 0x10; /* capabilities_pointer */ d->config[0x34] = 0x00; -} - -static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp) -{ - /* cache_line_size */ - d->config[0x0C] = 0x08; - /* latency_timer */ - d->config[0x0D] = 0x10; - /* capabilities_pointer - d->config[0x34] = 0x80; */ /* * Set kMacRISCPCIAddressSelect (0x48) register to indicate PCI @@ -325,6 +317,16 @@ static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp) d->config[0x4b] = 0x1; } +static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp) +{ + /* cache_line_size */ + d->config[0x0C] = 0x08; + /* latency_timer */ + d->config[0x0D] = 0x10; + /* capabilities_pointer + d->config[0x34] = 0x80; */ +} + static void u3_agp_pci_host_realize(PCIDevice *d, Error **errp) { /* cache line size */ From 1ff861d289bf2bef65cb5ef20303583bd72ec930 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:55 +0000 Subject: [PATCH 0085/2380] uninorth: enable internal PCI host bridge Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 1f6752c294..ccde332fa9 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -213,6 +213,21 @@ static void pci_unin_agp_init(Object *obj) sysbus_init_mmio(sbd, &h->data_mem); } +static void pci_unin_internal_realize(DeviceState *dev, Error **errp) +{ + UNINState *s = UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + + h->bus = pci_register_root_bus(dev, NULL, + pci_unin_set_irq, pci_unin_map_irq, + s->pic_irqs, + &s->pci_mmio, + get_system_io(), + PCI_DEVFN(14, 0), 4, TYPE_PCI_BUS); + + pci_create_simple(h->bus, PCI_DEVFN(14, 0), "uni-north-internal-pci"); +} + static void pci_unin_internal_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); @@ -244,16 +259,12 @@ UNINState *pci_pmac_init(qemu_irq *pic, sysbus_mmio_map(s, 1, 0xf0c00000); /* Uninorth internal bus */ -#if 0 - /* XXX: not needed for now */ - pci_create_simple(h->bus, PCI_DEVFN(14, 0), - "uni-north-internal-pci"); dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE); + qdev_prop_set_ptr(dev, "pic-irqs", pic); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, 0xf4800000); sysbus_mmio_map(s, 1, 0xf4c00000); -#endif /* Uninorth main bus */ dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); @@ -523,10 +534,17 @@ static const TypeInfo pci_unin_agp_info = { .class_init = pci_unin_agp_class_init, }; +static Property pci_unin_internal_class_properties[] = { + DEFINE_PROP_PTR("pic-irqs", UNINState, pic_irqs), + DEFINE_PROP_END_OF_LIST(), +}; + static void pci_unin_internal_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = pci_unin_internal_realize; + dc->props = pci_unin_internal_class_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } From 7b19318bee746628b8cd9795d7a944c26779d60f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:56 +0000 Subject: [PATCH 0086/2380] uninorth: remove obsolete pci_pmac_init() function Instead wire up the PCI/AGP host bridges in mac_newworld.c. Now this is complete it is possible to move the initialisation of the PCI hole alias into pci_unin_main_init(). Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 46 +++++------------------------------------- hw/ppc/mac_newworld.c | 30 ++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index ccde332fa9..8e4e9b3d35 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -146,8 +146,13 @@ static void pci_unin_main_init(Object *obj) memory_region_init(&s->pci_mmio, OBJECT(s), "unin-pci-mmio", 0x100000000ULL); + memory_region_init_alias(&s->pci_hole, OBJECT(s), + "unin-pci-hole", &s->pci_mmio, + 0x80000000ULL, 0x10000000ULL); + sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); + sysbus_init_mmio(sbd, &s->pci_hole); } static void pci_u3_agp_realize(DeviceState *dev, Error **errp) @@ -242,47 +247,6 @@ static void pci_unin_internal_init(Object *obj) sysbus_init_mmio(sbd, &h->data_mem); } -UNINState *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem) -{ - DeviceState *dev; - SysBusDevice *s; - UNINState *d; - - /* Use values found on a real PowerMac */ - /* Uninorth AGP bus */ - dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); - qdev_prop_set_ptr(dev, "pic-irqs", pic); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, 0xf0800000); - sysbus_mmio_map(s, 1, 0xf0c00000); - - /* Uninorth internal bus */ - dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE); - qdev_prop_set_ptr(dev, "pic-irqs", pic); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, 0xf4800000); - sysbus_mmio_map(s, 1, 0xf4c00000); - - /* Uninorth main bus */ - dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); - qdev_prop_set_ptr(dev, "pic-irqs", pic); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - d = UNI_NORTH_PCI_HOST_BRIDGE(dev); - memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x10000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); - - sysbus_mmio_map(s, 0, 0xf2800000); - sysbus_mmio_map(s, 1, 0xf2c00000); - - return d; -} - UNINState *pci_pmac_u3_init(qemu_irq *pic, MemoryRegion *address_space_mem) { diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 3367d7bb93..3033fc0d7e 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -348,7 +348,35 @@ static void ppc_core99_init(MachineState *machine) uninorth_pci = pci_pmac_u3_init(pic, get_system_memory()); machine_arch = ARCH_MAC99_U3; } else { - uninorth_pci = pci_pmac_init(pic, get_system_memory()); + /* Use values found on a real PowerMac */ + /* Uninorth AGP bus */ + dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); + qdev_prop_set_ptr(dev, "pic-irqs", pic); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, 0xf0800000); + sysbus_mmio_map(s, 1, 0xf0c00000); + + /* Uninorth internal bus */ + dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE); + qdev_prop_set_ptr(dev, "pic-irqs", pic); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, 0xf4800000); + sysbus_mmio_map(s, 1, 0xf4c00000); + + /* Uninorth main bus */ + dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); + qdev_prop_set_ptr(dev, "pic-irqs", pic); + qdev_init_nofail(dev); + uninorth_pci = UNI_NORTH_PCI_HOST_BRIDGE(dev); + s = SYS_BUS_DEVICE(dev); + /* PCI hole */ + memory_region_add_subregion(get_system_memory(), 0x80000000ULL, + sysbus_mmio_get_region(s, 2)); + sysbus_mmio_map(s, 0, 0xf2800000); + sysbus_mmio_map(s, 1, 0xf2c00000); + machine_arch = ARCH_MAC99; } From 8ce3f743c78f422ff87da76553c9421391f3adbf Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:57 +0000 Subject: [PATCH 0087/2380] uninorth: remove obsolete pci_pmac_u3_init() function Instead wire up the PCI/AGP host bridges in mac_newworld.c. Now this is complete it is possible to move the initialisation of the PCI hole alias into pci_u3_agp_init(). Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 30 +++++------------------------- hw/ppc/mac_newworld.c | 13 ++++++++++++- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 8e4e9b3d35..ec6e529d66 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -185,8 +185,13 @@ static void pci_u3_agp_init(Object *obj) memory_region_init(&s->pci_mmio, OBJECT(s), "unin-pci-mmio", 0x100000000ULL); + memory_region_init_alias(&s->pci_hole, OBJECT(s), + "unin-pci-hole", &s->pci_mmio, + 0x80000000ULL, 0x70000000ULL); + sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); + sysbus_init_mmio(sbd, &s->pci_hole); } static void pci_unin_agp_realize(DeviceState *dev, Error **errp) @@ -247,31 +252,6 @@ static void pci_unin_internal_init(Object *obj) sysbus_init_mmio(sbd, &h->data_mem); } -UNINState *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem) -{ - DeviceState *dev; - SysBusDevice *s; - UNINState *d; - - /* Uninorth AGP bus */ - dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); - qdev_prop_set_ptr(dev, "pic-irqs", pic); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - d = U3_AGP_HOST_BRIDGE(dev); - - memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x70000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); - - sysbus_mmio_map(s, 0, 0xf0800000); - sysbus_mmio_map(s, 1, 0xf0c00000); - - return d; -} - static void unin_main_pci_host_realize(PCIDevice *d, Error **errp) { /* cache_line_size */ diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 3033fc0d7e..2360b24a12 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -345,7 +345,18 @@ static void ppc_core99_init(MachineState *machine) if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { /* 970 gets a U3 bus */ - uninorth_pci = pci_pmac_u3_init(pic, get_system_memory()); + /* Uninorth AGP bus */ + dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); + qdev_prop_set_ptr(dev, "pic-irqs", pic); + qdev_init_nofail(dev); + uninorth_pci = U3_AGP_HOST_BRIDGE(dev); + s = SYS_BUS_DEVICE(dev); + /* PCI hole */ + memory_region_add_subregion(get_system_memory(), 0x80000000ULL, + sysbus_mmio_get_region(s, 2)); + sysbus_mmio_map(s, 0, 0xf0800000); + sysbus_mmio_map(s, 1, 0xf0c00000); + machine_arch = ARCH_MAC99_U3; } else { /* Use values found on a real PowerMac */ From e7755cc1142db474bfa47247a92c59996af0502a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:58 +0000 Subject: [PATCH 0088/2380] uninorth: use object link to pass OpenPIC object to uninorth Now that the OpenPIC is wired up via the board, we can now remove our temporary PIC qdev pointer property and replace it with an object link instead. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 73 ++++++++++++++++++++-------------- hw/ppc/mac_newworld.c | 12 ++++-- include/hw/pci-host/uninorth.h | 5 ++- 3 files changed, 55 insertions(+), 35 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index ec6e529d66..e2278fd0f0 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -38,10 +38,10 @@ static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num) static void pci_unin_set_irq(void *opaque, int irq_num, int level) { - qemu_irq *pic = opaque; + UNINState *s = opaque; trace_unin_set_irq(unin_irq_line[irq_num], level); - qemu_set_irq(pic[unin_irq_line[irq_num]], level); + qemu_set_irq(s->irqs[irq_num], level); } static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr) @@ -109,6 +109,15 @@ static const MemoryRegionOps unin_data_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void pci_unin_init_irqs(UNINState *s) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s->irqs); i++) { + s->irqs[i] = qdev_get_gpio_in(DEVICE(s->pic), unin_irq_line[i]); + } +} + static void pci_unin_main_realize(DeviceState *dev, Error **errp) { UNINState *s = UNI_NORTH_PCI_HOST_BRIDGE(dev); @@ -116,12 +125,13 @@ static void pci_unin_main_realize(DeviceState *dev, Error **errp) h->bus = pci_register_root_bus(dev, NULL, pci_unin_set_irq, pci_unin_map_irq, - s->pic_irqs, + s, &s->pci_mmio, get_system_io(), PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-pci"); + pci_unin_init_irqs(s); /* DEC 21154 bridge */ #if 0 @@ -150,6 +160,11 @@ static void pci_unin_main_init(Object *obj) "unin-pci-hole", &s->pci_mmio, 0x80000000ULL, 0x10000000ULL); + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); sysbus_init_mmio(sbd, &s->pci_hole); @@ -162,12 +177,13 @@ static void pci_u3_agp_realize(DeviceState *dev, Error **errp) h->bus = pci_register_root_bus(dev, NULL, pci_unin_set_irq, pci_unin_map_irq, - s->pic_irqs, + s, &s->pci_mmio, get_system_io(), PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); pci_create_simple(h->bus, PCI_DEVFN(11, 0), "u3-agp"); + pci_unin_init_irqs(s); } static void pci_u3_agp_init(Object *obj) @@ -189,6 +205,11 @@ static void pci_u3_agp_init(Object *obj) "unin-pci-hole", &s->pci_mmio, 0x80000000ULL, 0x70000000ULL); + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); sysbus_init_mmio(sbd, &s->pci_hole); @@ -201,16 +222,18 @@ static void pci_unin_agp_realize(DeviceState *dev, Error **errp) h->bus = pci_register_root_bus(dev, NULL, pci_unin_set_irq, pci_unin_map_irq, - s->pic_irqs, + s, &s->pci_mmio, get_system_io(), PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); + pci_unin_init_irqs(s); } static void pci_unin_agp_init(Object *obj) { + UNINState *s = UNI_NORTH_AGP_HOST_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj); @@ -219,6 +242,12 @@ static void pci_unin_agp_init(Object *obj) obj, "unin-agp-conf-idx", 0x1000); memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, obj, "unin-agp-conf-data", 0x1000); + + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); } @@ -230,16 +259,18 @@ static void pci_unin_internal_realize(DeviceState *dev, Error **errp) h->bus = pci_register_root_bus(dev, NULL, pci_unin_set_irq, pci_unin_map_irq, - s->pic_irqs, + s, &s->pci_mmio, get_system_io(), PCI_DEVFN(14, 0), 4, TYPE_PCI_BUS); pci_create_simple(h->bus, PCI_DEVFN(14, 0), "uni-north-internal-pci"); + pci_unin_init_irqs(s); } static void pci_unin_internal_init(Object *obj) { + UNINState *s = UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj); @@ -248,6 +279,12 @@ static void pci_unin_internal_init(Object *obj) obj, "unin-pci-conf-idx", 0x1000); memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, obj, "unin-pci-conf-data", 0x1000); + + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); } @@ -412,17 +449,11 @@ static const TypeInfo unin_internal_pci_host_info = { }, }; -static Property pci_unin_main_properties[] = { - DEFINE_PROP_PTR("pic-irqs", UNINState, pic_irqs), - DEFINE_PROP_END_OF_LIST(), -}; - static void pci_unin_main_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pci_unin_main_realize; - dc->props = pci_unin_main_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -434,17 +465,11 @@ static const TypeInfo pci_unin_main_info = { .class_init = pci_unin_main_class_init, }; -static Property pci_u3_agp_properties[] = { - DEFINE_PROP_PTR("pic-irqs", UNINState, pic_irqs), - DEFINE_PROP_END_OF_LIST(), -}; - static void pci_u3_agp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pci_u3_agp_realize; - dc->props = pci_u3_agp_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -456,17 +481,11 @@ static const TypeInfo pci_u3_agp_info = { .class_init = pci_u3_agp_class_init, }; -static Property pci_unin_agp_class_properties[] = { - DEFINE_PROP_PTR("pic-irqs", UNINState, pic_irqs), - DEFINE_PROP_END_OF_LIST(), -}; - static void pci_unin_agp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pci_unin_agp_realize; - dc->props = pci_unin_agp_class_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -478,17 +497,11 @@ static const TypeInfo pci_unin_agp_info = { .class_init = pci_unin_agp_class_init, }; -static Property pci_unin_internal_class_properties[] = { - DEFINE_PROP_PTR("pic-irqs", UNINState, pic_irqs), - DEFINE_PROP_END_OF_LIST(), -}; - static void pci_unin_internal_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pci_unin_internal_realize; - dc->props = pci_unin_internal_class_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 2360b24a12..e05aa26c3d 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -347,7 +347,8 @@ static void ppc_core99_init(MachineState *machine) /* 970 gets a U3 bus */ /* Uninorth AGP bus */ dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); - qdev_prop_set_ptr(dev, "pic-irqs", pic); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); qdev_init_nofail(dev); uninorth_pci = U3_AGP_HOST_BRIDGE(dev); s = SYS_BUS_DEVICE(dev); @@ -362,7 +363,8 @@ static void ppc_core99_init(MachineState *machine) /* Use values found on a real PowerMac */ /* Uninorth AGP bus */ dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); - qdev_prop_set_ptr(dev, "pic-irqs", pic); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, 0xf0800000); @@ -370,7 +372,8 @@ static void ppc_core99_init(MachineState *machine) /* Uninorth internal bus */ dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE); - qdev_prop_set_ptr(dev, "pic-irqs", pic); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, 0xf4800000); @@ -378,7 +381,8 @@ static void ppc_core99_init(MachineState *machine) /* Uninorth main bus */ dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); - qdev_prop_set_ptr(dev, "pic-irqs", pic); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); qdev_init_nofail(dev); uninorth_pci = UNI_NORTH_PCI_HOST_BRIDGE(dev); s = SYS_BUS_DEVICE(dev); diff --git a/include/hw/pci-host/uninorth.h b/include/hw/pci-host/uninorth.h index e4fa11c145..e048fd56e8 100644 --- a/include/hw/pci-host/uninorth.h +++ b/include/hw/pci-host/uninorth.h @@ -27,6 +27,8 @@ #include "hw/hw.h" +#include "hw/ppc/openpic.h" + #define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost" #define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost" #define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost" @@ -44,7 +46,8 @@ typedef struct UNINState { PCIHostState parent_obj; - void *pic_irqs; + OpenPICState *pic; + qemu_irq irqs[4]; MemoryRegion pci_mmio; MemoryRegion pci_hole; } UNINState; From e226efbb262468241c2c8828373a84ffd93992ac Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:30:59 +0000 Subject: [PATCH 0089/2380] uninorth: move PCI IO (ISA) memory region into the uninorth device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do this for both the uninorth main and uninorth u3 AGP buses, using the main PCI bus for each machine (this ensures the IO addresses still match those used by OpenBIOS). Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 14 ++++++++++---- hw/ppc/mac_newworld.c | 12 ++++++------ include/hw/pci-host/uninorth.h | 1 + 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index e2278fd0f0..3a29a4410e 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -127,7 +127,7 @@ static void pci_unin_main_realize(DeviceState *dev, Error **errp) pci_unin_set_irq, pci_unin_map_irq, s, &s->pci_mmio, - get_system_io(), + &s->pci_io, PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-pci"); @@ -155,6 +155,8 @@ static void pci_unin_main_init(Object *obj) memory_region_init(&s->pci_mmio, OBJECT(s), "unin-pci-mmio", 0x100000000ULL); + memory_region_init_io(&s->pci_io, OBJECT(s), &unassigned_io_ops, obj, + "unin-pci-isa-mmio", 0x00800000); memory_region_init_alias(&s->pci_hole, OBJECT(s), "unin-pci-hole", &s->pci_mmio, @@ -168,6 +170,7 @@ static void pci_unin_main_init(Object *obj) sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); sysbus_init_mmio(sbd, &s->pci_hole); + sysbus_init_mmio(sbd, &s->pci_io); } static void pci_u3_agp_realize(DeviceState *dev, Error **errp) @@ -179,7 +182,7 @@ static void pci_u3_agp_realize(DeviceState *dev, Error **errp) pci_unin_set_irq, pci_unin_map_irq, s, &s->pci_mmio, - get_system_io(), + &s->pci_io, PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); pci_create_simple(h->bus, PCI_DEVFN(11, 0), "u3-agp"); @@ -200,6 +203,8 @@ static void pci_u3_agp_init(Object *obj) memory_region_init(&s->pci_mmio, OBJECT(s), "unin-pci-mmio", 0x100000000ULL); + memory_region_init_io(&s->pci_io, OBJECT(s), &unassigned_io_ops, obj, + "unin-pci-isa-mmio", 0x00800000); memory_region_init_alias(&s->pci_hole, OBJECT(s), "unin-pci-hole", &s->pci_mmio, @@ -213,6 +218,7 @@ static void pci_u3_agp_init(Object *obj) sysbus_init_mmio(sbd, &h->conf_mem); sysbus_init_mmio(sbd, &h->data_mem); sysbus_init_mmio(sbd, &s->pci_hole); + sysbus_init_mmio(sbd, &s->pci_io); } static void pci_unin_agp_realize(DeviceState *dev, Error **errp) @@ -224,7 +230,7 @@ static void pci_unin_agp_realize(DeviceState *dev, Error **errp) pci_unin_set_irq, pci_unin_map_irq, s, &s->pci_mmio, - get_system_io(), + &s->pci_io, PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); @@ -261,7 +267,7 @@ static void pci_unin_internal_realize(DeviceState *dev, Error **errp) pci_unin_set_irq, pci_unin_map_irq, s, &s->pci_mmio, - get_system_io(), + &s->pci_io, PCI_DEVFN(14, 0), 4, TYPE_PCI_BUS); pci_create_simple(h->bus, PCI_DEVFN(14, 0), "uni-north-internal-pci"); diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index e05aa26c3d..bd7ffdb0fb 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -145,7 +145,6 @@ static void ppc_core99_init(MachineState *machine) CPUPPCState *env = NULL; char *filename; qemu_irq *pic, **openpic_irqs; - MemoryRegion *isa = g_new(MemoryRegion, 1); MemoryRegion *unin_memory = g_new(MemoryRegion, 1); int linux_boot, i, j, k; MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); @@ -273,11 +272,6 @@ static void ppc_core99_init(MachineState *machine) } } - /* Register 8 MB of ISA IO space */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x00800000); - memory_region_add_subregion(get_system_memory(), 0xf2000000, isa); - /* UniN init: XXX should be a real device */ memory_region_init_io(unin_memory, NULL, &unin_ops, token, "unin", 0x1000); memory_region_add_subregion(get_system_memory(), 0xf8000000, unin_memory); @@ -355,6 +349,9 @@ static void ppc_core99_init(MachineState *machine) /* PCI hole */ memory_region_add_subregion(get_system_memory(), 0x80000000ULL, sysbus_mmio_get_region(s, 2)); + /* Register 8 MB of ISA IO space */ + memory_region_add_subregion(get_system_memory(), 0xf2000000, + sysbus_mmio_get_region(s, 3)); sysbus_mmio_map(s, 0, 0xf0800000); sysbus_mmio_map(s, 1, 0xf0c00000); @@ -389,6 +386,9 @@ static void ppc_core99_init(MachineState *machine) /* PCI hole */ memory_region_add_subregion(get_system_memory(), 0x80000000ULL, sysbus_mmio_get_region(s, 2)); + /* Register 8 MB of ISA IO space */ + memory_region_add_subregion(get_system_memory(), 0xf2000000, + sysbus_mmio_get_region(s, 3)); sysbus_mmio_map(s, 0, 0xf2800000); sysbus_mmio_map(s, 1, 0xf2c00000); diff --git a/include/hw/pci-host/uninorth.h b/include/hw/pci-host/uninorth.h index e048fd56e8..a953b5e9b1 100644 --- a/include/hw/pci-host/uninorth.h +++ b/include/hw/pci-host/uninorth.h @@ -50,6 +50,7 @@ typedef struct UNINState { qemu_irq irqs[4]; MemoryRegion pci_mmio; MemoryRegion pci_hole; + MemoryRegion pci_io; } UNINState; #endif /* UNINORTH_H */ From c90c393c2dca764bf2a062b3769ac0de32f5fe28 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 6 Mar 2018 20:31:00 +0000 Subject: [PATCH 0090/2380] uninorth: rename UNINState to UNINHostState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing UNINState actually represents the PCI/AGP host bridge stage so rename it accordingly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 32 ++++++++++++++++---------------- hw/ppc/mac.h | 8 ++++---- hw/ppc/mac_newworld.c | 2 +- include/hw/pci-host/uninorth.h | 12 ++++++------ 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 3a29a4410e..fada0ffd5f 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -38,7 +38,7 @@ static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num) static void pci_unin_set_irq(void *opaque, int irq_num, int level) { - UNINState *s = opaque; + UNINHostState *s = opaque; trace_unin_set_irq(unin_irq_line[irq_num], level); qemu_set_irq(s->irqs[irq_num], level); @@ -81,7 +81,7 @@ static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr) static void unin_data_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) { - UNINState *s = opaque; + UNINHostState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); trace_unin_data_write(addr, len, val); pci_data_write(phb->bus, @@ -92,7 +92,7 @@ static void unin_data_write(void *opaque, hwaddr addr, static uint64_t unin_data_read(void *opaque, hwaddr addr, unsigned len) { - UNINState *s = opaque; + UNINHostState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); uint32_t val; @@ -109,7 +109,7 @@ static const MemoryRegionOps unin_data_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void pci_unin_init_irqs(UNINState *s) +static void pci_unin_init_irqs(UNINHostState *s) { int i; @@ -120,7 +120,7 @@ static void pci_unin_init_irqs(UNINState *s) static void pci_unin_main_realize(DeviceState *dev, Error **errp) { - UNINState *s = UNI_NORTH_PCI_HOST_BRIDGE(dev); + UNINHostState *s = UNI_NORTH_PCI_HOST_BRIDGE(dev); PCIHostState *h = PCI_HOST_BRIDGE(dev); h->bus = pci_register_root_bus(dev, NULL, @@ -142,7 +142,7 @@ static void pci_unin_main_realize(DeviceState *dev, Error **errp) static void pci_unin_main_init(Object *obj) { - UNINState *s = UNI_NORTH_PCI_HOST_BRIDGE(obj); + UNINHostState *s = UNI_NORTH_PCI_HOST_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj); @@ -175,7 +175,7 @@ static void pci_unin_main_init(Object *obj) static void pci_u3_agp_realize(DeviceState *dev, Error **errp) { - UNINState *s = U3_AGP_HOST_BRIDGE(dev); + UNINHostState *s = U3_AGP_HOST_BRIDGE(dev); PCIHostState *h = PCI_HOST_BRIDGE(dev); h->bus = pci_register_root_bus(dev, NULL, @@ -191,7 +191,7 @@ static void pci_u3_agp_realize(DeviceState *dev, Error **errp) static void pci_u3_agp_init(Object *obj) { - UNINState *s = U3_AGP_HOST_BRIDGE(obj); + UNINHostState *s = U3_AGP_HOST_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj); @@ -223,7 +223,7 @@ static void pci_u3_agp_init(Object *obj) static void pci_unin_agp_realize(DeviceState *dev, Error **errp) { - UNINState *s = UNI_NORTH_AGP_HOST_BRIDGE(dev); + UNINHostState *s = UNI_NORTH_AGP_HOST_BRIDGE(dev); PCIHostState *h = PCI_HOST_BRIDGE(dev); h->bus = pci_register_root_bus(dev, NULL, @@ -239,7 +239,7 @@ static void pci_unin_agp_realize(DeviceState *dev, Error **errp) static void pci_unin_agp_init(Object *obj) { - UNINState *s = UNI_NORTH_AGP_HOST_BRIDGE(obj); + UNINHostState *s = UNI_NORTH_AGP_HOST_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj); @@ -260,7 +260,7 @@ static void pci_unin_agp_init(Object *obj) static void pci_unin_internal_realize(DeviceState *dev, Error **errp) { - UNINState *s = UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(dev); + UNINHostState *s = UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(dev); PCIHostState *h = PCI_HOST_BRIDGE(dev); h->bus = pci_register_root_bus(dev, NULL, @@ -276,7 +276,7 @@ static void pci_unin_internal_realize(DeviceState *dev, Error **errp) static void pci_unin_internal_init(Object *obj) { - UNINState *s = UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj); + UNINHostState *s = UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj); @@ -466,7 +466,7 @@ static void pci_unin_main_class_init(ObjectClass *klass, void *data) static const TypeInfo pci_unin_main_info = { .name = TYPE_UNI_NORTH_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), + .instance_size = sizeof(UNINHostState), .instance_init = pci_unin_main_init, .class_init = pci_unin_main_class_init, }; @@ -482,7 +482,7 @@ static void pci_u3_agp_class_init(ObjectClass *klass, void *data) static const TypeInfo pci_u3_agp_info = { .name = TYPE_U3_AGP_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), + .instance_size = sizeof(UNINHostState), .instance_init = pci_u3_agp_init, .class_init = pci_u3_agp_class_init, }; @@ -498,7 +498,7 @@ static void pci_unin_agp_class_init(ObjectClass *klass, void *data) static const TypeInfo pci_unin_agp_info = { .name = TYPE_UNI_NORTH_AGP_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), + .instance_size = sizeof(UNINHostState), .instance_init = pci_unin_agp_init, .class_init = pci_unin_agp_class_init, }; @@ -514,7 +514,7 @@ static void pci_unin_internal_class_init(ObjectClass *klass, void *data) static const TypeInfo pci_unin_internal_info = { .name = TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), + .instance_size = sizeof(UNINHostState), .instance_init = pci_unin_internal_init, .class_init = pci_unin_internal_class_init, }; diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 1ab2a3b354..892dd03789 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -89,10 +89,10 @@ void macio_init(PCIDevice *dev, #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" /* UniNorth PCI */ -UNINState *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem); -UNINState *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem); +UNINHostState *pci_pmac_init(qemu_irq *pic, + MemoryRegion *address_space_mem); +UNINHostState *pci_pmac_u3_init(qemu_irq *pic, + MemoryRegion *address_space_mem); /* Mac NVRAM */ #define TYPE_MACIO_NVRAM "macio-nvram" diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index bd7ffdb0fb..29bd3838bf 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -150,7 +150,7 @@ static void ppc_core99_init(MachineState *machine) MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); hwaddr kernel_base, initrd_base, cmdline_base = 0; long kernel_size, initrd_size; - UNINState *uninorth_pci; + UNINHostState *uninorth_pci; PCIBus *pci_bus; NewWorldMacIOState *macio; MACIOIDEState *macio_ide; diff --git a/include/hw/pci-host/uninorth.h b/include/hw/pci-host/uninorth.h index a953b5e9b1..f0e6836c76 100644 --- a/include/hw/pci-host/uninorth.h +++ b/include/hw/pci-host/uninorth.h @@ -35,15 +35,15 @@ #define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost" #define UNI_NORTH_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) + OBJECT_CHECK(UNINHostState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) #define UNI_NORTH_AGP_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) + OBJECT_CHECK(UNINHostState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) #define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) + OBJECT_CHECK(UNINHostState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) #define U3_AGP_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_U3_AGP_HOST_BRIDGE) + OBJECT_CHECK(UNINHostState, (obj), TYPE_U3_AGP_HOST_BRIDGE) -typedef struct UNINState { +typedef struct UNINHostState { PCIHostState parent_obj; OpenPICState *pic; @@ -51,6 +51,6 @@ typedef struct UNINState { MemoryRegion pci_mmio; MemoryRegion pci_hole; MemoryRegion pci_io; -} UNINState; +} UNINHostState; #endif /* UNINORTH_H */ From a2f04333a340282c0842e76c055297cb7ba56f0f Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Fri, 23 Feb 2018 17:29:56 +0000 Subject: [PATCH 0091/2380] ppc: Fix size of ppc64 xer register The normal gdb definition of the XER registers is only 32 bit, and that's what the current version of power64-core.xml also says (seems copied from gdb's). But qemu's idea of the XER register is target_ulong (in CPUPPCState, ppc_gdb_register_len and ppc_cpu_gdb_read_register) That mismatch leads to the following message when attaching with gdb: Truncated register 32 in remote 'g' packet (and following on that qemu stops responding). The simple fix is to say the truth in the .xml file. But the better fix is to actually make it 32bit on the wire, as old gdbs don't support XML files for describing registers. Also the XER state in qemu doesn't seem to use the high 32 bits, so sending it off to gdb doesn't seem worthwhile. Signed-off-by: Michael Matz Signed-off-by: David Gibson --- target/ppc/gdbstub.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 7a338136a8..b6f6693583 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -37,10 +37,10 @@ static int ppc_gdb_register_len_apple(int n) case 65+32: /* msr */ case 67+32: /* lr */ case 68+32: /* ctr */ - case 69+32: /* xer */ case 70+32: /* fpscr */ return 8; case 66+32: /* cr */ + case 69+32: /* xer */ return 4; default: return 0; @@ -61,6 +61,8 @@ static int ppc_gdb_register_len(int n) return 8; case 66: /* cr */ + case 69: + /* xer */ return 4; case 64: /* nip */ @@ -70,8 +72,6 @@ static int ppc_gdb_register_len(int n) /* lr */ case 68: /* ctr */ - case 69: - /* xer */ return sizeof(target_ulong); case 70: /* fpscr */ @@ -152,7 +152,7 @@ int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) gdb_get_regl(mem_buf, env->ctr); break; case 69: - gdb_get_regl(mem_buf, env->xer); + gdb_get_reg32(mem_buf, env->xer); break; case 70: gdb_get_reg32(mem_buf, env->fpscr); @@ -208,7 +208,7 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) gdb_get_reg64(mem_buf, env->ctr); break; case 69 + 32: - gdb_get_reg64(mem_buf, env->xer); + gdb_get_reg32(mem_buf, env->xer); break; case 70 + 32: gdb_get_reg64(mem_buf, env->fpscr); @@ -259,7 +259,7 @@ int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->ctr = ldtul_p(mem_buf); break; case 69: - env->xer = ldtul_p(mem_buf); + env->xer = ldl_p(mem_buf); break; case 70: /* fpscr */ @@ -309,7 +309,7 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) env->ctr = ldq_p(mem_buf); break; case 69 + 32: - env->xer = ldq_p(mem_buf); + env->xer = ldl_p(mem_buf); break; case 70 + 32: /* fpscr */ From 99d45f8fbd6756c2495bcb554dcbf50585f1f7b0 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 26 Mar 2018 01:54:28 +0200 Subject: [PATCH 0092/2380] target/ppc: Fix reserved bit mask of dstst instruction According to the Vector/SIMD extension documentation bit 6 that is currently masked is valid (listed as transient bit) but bits 7 and 8 should be reserved instead. Fix the mask to match this. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- target/ppc/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 3457d29f8e..b0d79a3fb9 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -6561,7 +6561,7 @@ GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x00000001, PPC_CACHE), GEN_HANDLER_E(dcbtls, 0x1F, 0x06, 0x05, 0x02000001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZ), GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC), -GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x02000001, PPC_ALTIVEC), +GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x01800001, PPC_ALTIVEC), GEN_HANDLER(dss, 0x1F, 0x16, 0x19, 0x019FF801, PPC_ALTIVEC), GEN_HANDLER(icbi, 0x1F, 0x16, 0x1E, 0x03E00001, PPC_CACHE_ICBI), GEN_HANDLER(dcba, 0x1F, 0x16, 0x17, 0x03E00001, PPC_CACHE_DCBA), From 8a4fd427fe8236bb4f6993702112f058b5d80507 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 29 Mar 2018 18:34:22 +1100 Subject: [PATCH 0093/2380] spapr: Introduce pseries-2.13 machine type Signed-off-by: David Gibson --- hw/ppc/spapr.c | 27 +++++++++++++++++++++++---- include/hw/compat.h | 2 ++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 92194a9a53..b92dad2273 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3985,18 +3985,37 @@ static const TypeInfo spapr_machine_info = { type_init(spapr_machine_register_##suffix) /* - * pseries-2.12 + * pseries-2.13 */ -static void spapr_machine_2_12_instance_options(MachineState *machine) +static void spapr_machine_2_13_instance_options(MachineState *machine) { } -static void spapr_machine_2_12_class_options(MachineClass *mc) +static void spapr_machine_2_13_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(2_12, "2.12", true); +DEFINE_SPAPR_MACHINE(2_13, "2.13", true); + +/* + * pseries-2.12 + */ +#define SPAPR_COMPAT_2_12 \ + HW_COMPAT_2_12 + +static void spapr_machine_2_12_instance_options(MachineState *machine) +{ + spapr_machine_2_13_instance_options(machine); +} + +static void spapr_machine_2_12_class_options(MachineClass *mc) +{ + spapr_machine_2_13_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_12); +} + +DEFINE_SPAPR_MACHINE(2_12, "2.12", false); static void spapr_machine_2_12_sxxm_instance_options(MachineState *machine) { diff --git a/include/hw/compat.h b/include/hw/compat.h index 13242b831a..4681c2719a 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -1,6 +1,8 @@ #ifndef HW_COMPAT_H #define HW_COMPAT_H +#define HW_COMPAT_2_12 + #define HW_COMPAT_2_11 \ {\ .driver = "hpet",\ From 0de6e2a3ca2e1215a2d62d8d796589d27eca91d0 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 3 Apr 2018 14:55:11 +1000 Subject: [PATCH 0094/2380] Make qemu_mempath_getpagesize() accept NULL qemu_mempath_getpagesize() gets the effective (host side) page size for a block of memory backed by an mmap()ed file on the host. It requires the mem_path parameter to be non-NULL. This ends up meaning all the callers need a different case for handling anonymous memory (for memory-backend-ram or default memory with -mem-path is not specified). We can make all those callers a little simpler by having qemu_mempath_getpagesize() accept NULL, and treat that as the anonymous memory case. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Acked-by: Paolo Bonzini --- exec.c | 21 ++++++--------------- target/ppc/kvm.c | 8 ++------ util/mmap-alloc.c | 24 +++++++++++++----------- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/exec.c b/exec.c index 02b1efebb7..b38b004563 100644 --- a/exec.c +++ b/exec.c @@ -1488,19 +1488,14 @@ void ram_block_dump(Monitor *mon) */ static int find_max_supported_pagesize(Object *obj, void *opaque) { - char *mem_path; long *hpsize_min = opaque; if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) { - mem_path = object_property_get_str(obj, "mem-path", NULL); - if (mem_path) { - long hpsize = qemu_mempath_getpagesize(mem_path); - g_free(mem_path); - if (hpsize < *hpsize_min) { - *hpsize_min = hpsize; - } - } else { - *hpsize_min = getpagesize(); + char *mem_path = object_property_get_str(obj, "mem-path", NULL); + long hpsize = qemu_mempath_getpagesize(mem_path); + g_free(mem_path); + if (hpsize < *hpsize_min) { + *hpsize_min = hpsize; } } @@ -1513,11 +1508,7 @@ long qemu_getrampagesize(void) long mainrampagesize; Object *memdev_root; - if (mem_path) { - mainrampagesize = qemu_mempath_getpagesize(mem_path); - } else { - mainrampagesize = getpagesize(); - } + mainrampagesize = qemu_mempath_getpagesize(mem_path); /* it's possible we have memory-backend objects with * hugepage-backed RAM. these may get mapped into system diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 79a436a384..e24fa50dc9 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -499,12 +499,8 @@ bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path) char *mempath = object_property_get_str(mem_obj, "mem-path", NULL); long pagesize; - if (mempath) { - pagesize = qemu_mempath_getpagesize(mempath); - g_free(mempath); - } else { - pagesize = getpagesize(); - } + pagesize = qemu_mempath_getpagesize(mempath); + g_free(mempath); return pagesize >= max_cpu_page_size; } diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 2fd8cbcc6f..fd329eccd8 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -50,19 +50,21 @@ size_t qemu_mempath_getpagesize(const char *mem_path) struct statfs fs; int ret; - do { - ret = statfs(mem_path, &fs); - } while (ret != 0 && errno == EINTR); + if (mem_path) { + do { + ret = statfs(mem_path, &fs); + } while (ret != 0 && errno == EINTR); - if (ret != 0) { - fprintf(stderr, "Couldn't statfs() memory path: %s\n", - strerror(errno)); - exit(1); - } + if (ret != 0) { + fprintf(stderr, "Couldn't statfs() memory path: %s\n", + strerror(errno)); + exit(1); + } - if (fs.f_type == HUGETLBFS_MAGIC) { - /* It's hugepage, return the huge page size */ - return fs.f_bsize; + if (fs.f_type == HUGETLBFS_MAGIC) { + /* It's hugepage, return the huge page size */ + return fs.f_bsize; + } } #ifdef __sparc__ /* SPARC Linux needs greater alignment than the pagesize */ From 2b10808539d7ace3d9b1226a71a68e2431ef2176 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 3 Apr 2018 15:05:45 +1000 Subject: [PATCH 0095/2380] Add host_memory_backend_pagesize() helper There are a couple places (one generic, one target specific) where we need to get the host page size associated with a particular memory backend. I have some upcoming code which will add another place which wants this. So, for convenience, add a helper function to calculate this. host_memory_backend_pagesize() returns the host pagesize for a given HostMemoryBackend object. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Acked-by: Paolo Bonzini --- backends/hostmem.c | 18 ++++++++++++++++++ exec.c | 5 ++--- include/sysemu/hostmem.h | 2 ++ target/ppc/kvm.c | 6 +----- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/backends/hostmem.c b/backends/hostmem.c index f61093654e..6a0c474222 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -18,6 +18,7 @@ #include "qapi/visitor.h" #include "qemu/config-file.h" #include "qom/object_interfaces.h" +#include "qemu/mmap-alloc.h" #ifdef CONFIG_NUMA #include @@ -262,6 +263,23 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend) return backend->is_mapped; } +#ifdef __linux__ +size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) +{ + Object *obj = OBJECT(memdev); + char *path = object_property_get_str(obj, "mem-path", NULL); + size_t pagesize = qemu_mempath_getpagesize(path); + + g_free(path); + return pagesize; +} +#else +size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) +{ + return getpagesize(); +} +#endif + static void host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) { diff --git a/exec.c b/exec.c index b38b004563..c7fcefa851 100644 --- a/exec.c +++ b/exec.c @@ -1491,9 +1491,8 @@ static int find_max_supported_pagesize(Object *obj, void *opaque) long *hpsize_min = opaque; if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) { - char *mem_path = object_property_get_str(obj, "mem-path", NULL); - long hpsize = qemu_mempath_getpagesize(mem_path); - g_free(mem_path); + long hpsize = host_memory_backend_pagesize(MEMORY_BACKEND(obj)); + if (hpsize < *hpsize_min) { *hpsize_min = hpsize; } diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index 47bc9846ac..bc36899bb8 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -68,4 +68,6 @@ MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend, void host_memory_backend_set_mapped(HostMemoryBackend *backend, bool mapped); bool host_memory_backend_is_mapped(HostMemoryBackend *backend); +size_t host_memory_backend_pagesize(HostMemoryBackend *memdev); + #endif diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index e24fa50dc9..f62f7ac288 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -496,11 +496,7 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path) { Object *mem_obj = object_resolve_path(obj_path, NULL); - char *mempath = object_property_get_str(mem_obj, "mem-path", NULL); - long pagesize; - - pagesize = qemu_mempath_getpagesize(mempath); - g_free(mempath); + long pagesize = host_memory_backend_pagesize(MEMORY_BACKEND(mem_obj)); return pagesize >= max_cpu_page_size; } From 1d36c75a9ebf6bddbdb46693052a48ef134f3699 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 11 Apr 2018 19:46:06 +0200 Subject: [PATCH 0096/2380] spapr: drop useless sanity check in spapr_irq_alloc*() Both spapr_irq_alloc() and spapr_irq_alloc_block() have an errp parameter, but they don't use it if XICS hasn't been initialized yet. This is doubly wrong: - all callers do pass a non-null Error **, ie, they expect an error to be propagated in case of failure - XICS obviously needs to be initialized before anything starts allocating IRQs So this patch turns the check into an assert. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b92dad2273..5a1c1e25e1 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3719,9 +3719,8 @@ int spapr_irq_alloc(sPAPRMachineState *spapr, int irq_hint, bool lsi, ICSState *ics = spapr->ics; int irq; - if (!ics) { - return -1; - } + assert(ics); + if (irq_hint) { if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) { error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); @@ -3753,9 +3752,7 @@ int spapr_irq_alloc_block(sPAPRMachineState *spapr, int num, bool lsi, ICSState *ics = spapr->ics; int i, first = -1; - if (!ics) { - return -1; - } + assert(ics); /* * MSIMesage::data is used for storing VIRQ so From e47f1d2786c3d01a7894a493aafe0efa6b64453c Mon Sep 17 00:00:00 2001 From: Serhii Popovych Date: Wed, 11 Apr 2018 14:42:00 -0400 Subject: [PATCH 0097/2380] Revert "spapr: Don't allow memory hotplug to memory less nodes" This reverts commit b556854bd8524c26b8be98ab1bfdf0826831e793. Leave change @node type from uint32_t to to int from reverted commit because node < 0 is always false. Note that implementing capability or some trick to detect if guest kernel does not support hot-add to memory: this returns previous behavour where memory added to first non-empty node. Signed-off-by: Serhii Popovych Signed-off-by: David Gibson --- hw/ppc/spapr.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 5a1c1e25e1..fc86ba6934 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3478,28 +3478,6 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, return; } - /* - * Currently PowerPC kernel doesn't allow hot-adding memory to - * memory-less node, but instead will silently add the memory - * to the first node that has some memory. This causes two - * unexpected behaviours for the user. - * - * - Memory gets hotplugged to a different node than what the user - * specified. - * - Since pc-dimm subsystem in QEMU still thinks that memory belongs - * to memory-less node, a reboot will set things accordingly - * and the previously hotplugged memory now ends in the right node. - * This appears as if some memory moved from one node to another. - * - * So until kernel starts supporting memory hotplug to memory-less - * nodes, just prevent such attempts upfront in QEMU. - */ - if (nb_numa_nodes && !numa_info[node].node_mem) { - error_setg(errp, "Can't hotplug memory to memory-less node %d", - node); - return; - } - spapr_memory_plug(hotplug_dev, dev, node, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_plug(hotplug_dev, dev, errp); From b2692d5fed8b0549d70426d75c9ad4734dc557c8 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 11 Apr 2018 17:01:20 +0200 Subject: [PATCH 0098/2380] spapr: drop useless dynamic sysbus device sanity check Since commit 7da79a167aa11, the machine class init function registers dynamic sysbus device types it supports. Passing an unsupported device type on the command line causes QEMU to exit with an error message just after machine init. It is hence not needed to do the same sanity check at machine reset. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index fc86ba6934..d35a88ca80 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1440,21 +1440,6 @@ void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr) } } -static void find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) -{ - bool matched = false; - - if (object_dynamic_cast(OBJECT(sbdev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { - matched = true; - } - - if (!matched) { - error_report("Device %s is not supported by this machine yet.", - qdev_fw_name(DEVICE(sbdev))); - exit(1); - } -} - static int spapr_reset_drcs(Object *child, void *opaque) { sPAPRDRConnector *drc = @@ -1478,9 +1463,6 @@ static void spapr_machine_reset(void) void *fdt; int rc; - /* Check for unknown sysbus devices */ - foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); - spapr_caps_reset(spapr); first_ppc_cpu = POWERPC_CPU(first_cpu); From e850da556d69363e6846ab00ddcbf7cb55203981 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 21 Mar 2018 15:30:13 +1100 Subject: [PATCH 0099/2380] target/ppc: Standardize instance_init and realize function names Because of the various hooks called some variant on "init" - and the rather greater number that used to exist, I'm always wondering when a function called simply "*_init" or "*_initfn" will be called. To make it easier on myself, and maybe others, rename the instance_init hooks for ppc cpus to *_instance_init(). While we're at it rename the realize time hooks to *_realize() (from *_realizefn()) which seems to be the more common current convention. Signed-off-by: David Gibson Reviewed-by: Greg Kurz --- target/ppc/translate_init.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 391b94b97d..56b80a204a 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -9726,7 +9726,7 @@ static inline bool ppc_cpu_is_valid(PowerPCCPUClass *pcc) #endif } -static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) +static void ppc_cpu_realize(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); PowerPCCPU *cpu = POWERPC_CPU(dev); @@ -9952,7 +9952,7 @@ unrealize: cpu_exec_unrealizefn(cs); } -static void ppc_cpu_unrealizefn(DeviceState *dev, Error **errp) +static void ppc_cpu_unrealize(DeviceState *dev, Error **errp) { PowerPCCPU *cpu = POWERPC_CPU(dev); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); @@ -10438,7 +10438,7 @@ static bool ppc_cpu_is_big_endian(CPUState *cs) } #endif -static void ppc_cpu_initfn(Object *obj) +static void ppc_cpu_instance_init(Object *obj) { CPUState *cs = CPU(obj); PowerPCCPU *cpu = POWERPC_CPU(obj); @@ -10561,9 +10561,9 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - device_class_set_parent_realize(dc, ppc_cpu_realizefn, + device_class_set_parent_realize(dc, ppc_cpu_realize, &pcc->parent_realize); - device_class_set_parent_unrealize(dc, ppc_cpu_unrealizefn, + device_class_set_parent_unrealize(dc, ppc_cpu_unrealize, &pcc->parent_unrealize); pcc->pvr_match = ppc_pvr_match_default; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_always; @@ -10623,7 +10623,7 @@ static const TypeInfo ppc_cpu_type_info = { .name = TYPE_POWERPC_CPU, .parent = TYPE_CPU, .instance_size = sizeof(PowerPCCPU), - .instance_init = ppc_cpu_initfn, + .instance_init = ppc_cpu_instance_init, .abstract = true, .class_size = sizeof(PowerPCCPUClass), .class_init = ppc_cpu_class_init, From 197600ecc4f81b9be5e233d8a1cbf42a48cdd371 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 23 Mar 2018 12:55:04 +1100 Subject: [PATCH 0100/2380] target/ppc: Simplify cpu valid check in ppc_cpu_realize The #if isn't necessary, because there's a suitable one inside ppc_cpu_is_valid(). We've already filtered for suitable cpu models in the functions that search and register them. So by the time we get to realize having an invalid one indicates a code error, not a user error, so an assert() is more appropriate than error_setg(). Signed-off-by: David Gibson Reviewed-by: Thomas Huth Reviewed-by: Greg Kurz --- target/ppc/translate_init.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 56b80a204a..2ae718242a 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -9749,14 +9749,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) } } -#if defined(TARGET_PPCEMB) - if (!ppc_cpu_is_valid(pcc)) { - error_setg(errp, "CPU does not possess a BookE or 4xx MMU. " - "Please use qemu-system-ppc or qemu-system-ppc64 instead " - "or choose another CPU model."); - goto unrealize; - } -#endif + assert(ppc_cpu_is_valid(pcc)); create_ppc_opcodes(cpu, &local_err); if (local_err != NULL) { From 644a2c99a90b95957fd56fc3b9f8908ac9e90702 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 22 Mar 2018 16:18:40 +1100 Subject: [PATCH 0101/2380] target/ppc: Pass cpu instead of env to ppc_create_page_sizes_prop() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As a rule we prefer to pass PowerPCCPU instead of CPUPPCState, and this change will make some things simpler later on. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Cédric Le Goater --- hw/ppc/fdt.c | 5 +++-- hw/ppc/pnv.c | 4 ++-- hw/ppc/spapr.c | 4 ++-- include/hw/ppc/fdt.h | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/ppc/fdt.c b/hw/ppc/fdt.c index 2ffc5866e4..2721603ffa 100644 --- a/hw/ppc/fdt.c +++ b/hw/ppc/fdt.c @@ -13,9 +13,10 @@ #include "hw/ppc/fdt.h" #if defined(TARGET_PPC64) -size_t ppc_create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, - size_t maxsize) +size_t ppc_create_page_sizes_prop(PowerPCCPU *cpu, uint32_t *prop, + size_t maxsize) { + CPUPPCState *env = &cpu->env; size_t maxcells = maxsize / sizeof(uint32_t); int i, j, count; uint32_t *p = prop; diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 549cfccdcb..3220ef8f79 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -209,8 +209,8 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); } - page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop, - sizeof(page_sizes_prop)); + page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop, + sizeof(page_sizes_prop)); if (page_sizes_prop_size) { _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", page_sizes_prop, page_sizes_prop_size))); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d35a88ca80..a4b3722656 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -581,8 +581,8 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); } - page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop, - sizeof(page_sizes_prop)); + page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop, + sizeof(page_sizes_prop)); if (page_sizes_prop_size) { _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", page_sizes_prop, page_sizes_prop_size))); diff --git a/include/hw/ppc/fdt.h b/include/hw/ppc/fdt.h index bd5b0a8c3d..a8cd85069f 100644 --- a/include/hw/ppc/fdt.h +++ b/include/hw/ppc/fdt.h @@ -23,7 +23,7 @@ } \ } while (0) -size_t ppc_create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, +size_t ppc_create_page_sizes_prop(PowerPCCPU *cpu, uint32_t *prop, size_t maxsize); #endif /* PPC_FDT_H */ From 8fe08fac1939815950b74fb7eb17101320450ca7 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 22 Mar 2018 16:49:28 +1100 Subject: [PATCH 0102/2380] target/ppc: Avoid taking "env" parameter to mmu-hash64 functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In most cases we prefer to pass a PowerPCCPU rather than the (embedded) CPUPPCState. For ppc_hash64_update_{rmls,vrma}() change to take "cpu" instead of "env". For ppc_hash64_set_{dsi,isi}() remove the redundant "env" parameter. In theory this makes more work for the functions, but since "cs", "cpu" and "env" are related by at most constant offsets, the compiler should be able to optimize out the difference at effectively zero cost. helper_*() functions are left alone - since they're more closely tied to the TCG generated code, passing "env" is still the standard there. While we're there, fix an incorrect indentation. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Cédric Le Goater --- target/ppc/mmu-hash64.c | 35 +++++++++++++++++++---------------- target/ppc/mmu-hash64.h | 4 ++-- target/ppc/translate_init.c | 4 ++-- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index c9b72b7429..a87fa7c83f 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -633,9 +633,9 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, return 0; } -static void ppc_hash64_set_isi(CPUState *cs, CPUPPCState *env, - uint64_t error_code) +static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code) { + CPUPPCState *env = &POWERPC_CPU(cs)->env; bool vpm; if (msr_ir) { @@ -659,9 +659,9 @@ static void ppc_hash64_set_isi(CPUState *cs, CPUPPCState *env, env->error_code = error_code; } -static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar, - uint64_t dsisr) +static void ppc_hash64_set_dsi(CPUState *cs, uint64_t dar, uint64_t dsisr) { + CPUPPCState *env = &POWERPC_CPU(cs)->env; bool vpm; if (msr_dr) { @@ -741,13 +741,13 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, } else { /* The access failed, generate the approriate interrupt */ if (rwx == 2) { - ppc_hash64_set_isi(cs, env, SRR1_PROTFAULT); + ppc_hash64_set_isi(cs, SRR1_PROTFAULT); } else { int dsisr = DSISR_PROTFAULT; if (rwx == 1) { dsisr |= DSISR_ISSTORE; } - ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + ppc_hash64_set_dsi(cs, eaddr, dsisr); } return 1; } @@ -783,7 +783,7 @@ skip_slb_search: /* 3. Check for segment level no-execute violation */ if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { - ppc_hash64_set_isi(cs, env, SRR1_NOEXEC_GUARD); + ppc_hash64_set_isi(cs, SRR1_NOEXEC_GUARD); return 1; } @@ -791,13 +791,13 @@ skip_slb_search: ptex = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte, &apshift); if (ptex == -1) { if (rwx == 2) { - ppc_hash64_set_isi(cs, env, SRR1_NOPTE); + ppc_hash64_set_isi(cs, SRR1_NOPTE); } else { int dsisr = DSISR_NOPTE; if (rwx == 1) { dsisr |= DSISR_ISSTORE; } - ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + ppc_hash64_set_dsi(cs, eaddr, dsisr); } return 1; } @@ -824,7 +824,7 @@ skip_slb_search: if (PAGE_EXEC & ~amr_prot) { srr1 |= SRR1_IAMR; /* Access violates virt pg class key prot */ } - ppc_hash64_set_isi(cs, env, srr1); + ppc_hash64_set_isi(cs, srr1); } else { int dsisr = 0; if (need_prot[rwx] & ~pp_prot) { @@ -836,7 +836,7 @@ skip_slb_search: if (need_prot[rwx] & ~amr_prot) { dsisr |= DSISR_AMR; } - ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + ppc_hash64_set_dsi(cs, eaddr, dsisr); } return 1; } @@ -942,8 +942,9 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex, cpu->env.tlb_need_flush = TLB_NEED_GLOBAL_FLUSH | TLB_NEED_LOCAL_FLUSH; } -void ppc_hash64_update_rmls(CPUPPCState *env) +void ppc_hash64_update_rmls(PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; uint64_t lpcr = env->spr[SPR_LPCR]; /* @@ -976,8 +977,9 @@ void ppc_hash64_update_rmls(CPUPPCState *env) } } -void ppc_hash64_update_vrma(CPUPPCState *env) +void ppc_hash64_update_vrma(PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; const struct ppc_one_seg_page_size *sps = NULL; target_ulong esid, vsid, lpcr; ppc_slb_t *slb = &env->vrma_slb; @@ -1002,7 +1004,7 @@ void ppc_hash64_update_vrma(CPUPPCState *env) vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); esid = SLB_ESID_V; - for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { const struct ppc_one_seg_page_size *sps1 = &env->sps.sps[i]; if (!sps1->page_shift) { @@ -1028,6 +1030,7 @@ void ppc_hash64_update_vrma(CPUPPCState *env) void helper_store_lpcr(CPUPPCState *env, target_ulong val) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); uint64_t lpcr = 0; /* Filter out bits */ @@ -1089,6 +1092,6 @@ void helper_store_lpcr(CPUPPCState *env, target_ulong val) ; } env->spr[SPR_LPCR] = lpcr; - ppc_hash64_update_rmls(env); - ppc_hash64_update_vrma(env); + ppc_hash64_update_rmls(cpu); + ppc_hash64_update_vrma(cpu); } diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index d297b97d37..95a8c330d6 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -17,8 +17,8 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong pte0, target_ulong pte1); unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, uint64_t pte0, uint64_t pte1); -void ppc_hash64_update_vrma(CPUPPCState *env); -void ppc_hash64_update_rmls(CPUPPCState *env); +void ppc_hash64_update_vrma(PowerPCCPU *cpu); +void ppc_hash64_update_rmls(PowerPCCPU *cpu); #endif /* diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 2ae718242a..29bd6f3654 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8975,8 +8975,8 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull; /* Update some env bits based on new LPCR value */ - ppc_hash64_update_rmls(env); - ppc_hash64_update_vrma(env); + ppc_hash64_update_rmls(cpu); + ppc_hash64_update_vrma(cpu); /* Tell KVM that we're in PAPR mode */ if (kvm_enabled()) { From dc71b55956b45a4aa6f280b57a3088d169bfc636 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 23 Mar 2018 11:24:28 +1100 Subject: [PATCH 0103/2380] target/ppc: Remove fallback 64k pagesize information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU definitions for cpus with the 64-bit hash MMU can include a table of available pagesizes. If this isn't supplied ppc_cpu_instance_init() will fill it in a fallback table based on the POWERPC_MMU_64K bit in mmu_model. However, it turns out all the cpus which support 64K pages already include an explicit table of page sizes, so there's no point to the fallback table including 64k pages. That removes the only place which tests POWERPC_MMU_64K, so we can remove it. Which in turn allows some logic to be removed from kvm_fixup_page_sizes(). Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- target/ppc/cpu-qom.h | 4 ---- target/ppc/kvm.c | 7 ------- target/ppc/translate_init.c | 20 ++------------------ 3 files changed, 2 insertions(+), 29 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index deaa46a14b..9bbb05cf62 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -70,7 +70,6 @@ enum powerpc_mmu_t { #define POWERPC_MMU_64 0x00010000 #define POWERPC_MMU_1TSEG 0x00020000 #define POWERPC_MMU_AMR 0x00040000 -#define POWERPC_MMU_64K 0x00080000 #define POWERPC_MMU_V3 0x00100000 /* ISA V3.00 MMU Support */ /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, @@ -78,15 +77,12 @@ enum powerpc_mmu_t { POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002, /* Architecture 2.06 variant */ POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG - | POWERPC_MMU_64K | POWERPC_MMU_AMR | 0x00000003, /* Architecture 2.07 variant */ POWERPC_MMU_2_07 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG - | POWERPC_MMU_64K | POWERPC_MMU_AMR | 0x00000004, /* Architecture 3.00 variant */ POWERPC_MMU_3_00 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG - | POWERPC_MMU_64K | POWERPC_MMU_AMR | POWERPC_MMU_V3 | 0x00000005, }; diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index f62f7ac288..660467aec6 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -425,7 +425,6 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) static bool has_smmu_info; CPUPPCState *env = &cpu->env; int iq, ik, jq, jk; - bool has_64k_pages = false; /* We only handle page sizes for 64-bit server guests for now */ if (!(env->mmu_model & POWERPC_MMU_64)) { @@ -471,9 +470,6 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) ksps->enc[jk].page_shift)) { continue; } - if (ksps->enc[jk].page_shift == 16) { - has_64k_pages = true; - } qsps->enc[jq].page_shift = ksps->enc[jk].page_shift; qsps->enc[jq].pte_enc = ksps->enc[jk].pte_enc; if (++jq >= PPC_PAGE_SIZES_MAX_SZ) { @@ -488,9 +484,6 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) if (!(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) { env->mmu_model &= ~POWERPC_MMU_1TSEG; } - if (!has_64k_pages) { - env->mmu_model &= ~POWERPC_MMU_64K; - } } bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path) diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 29bd6f3654..99be6fcd68 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -10469,7 +10469,7 @@ static void ppc_cpu_instance_init(Object *obj) env->sps = *pcc->sps; } else if (env->mmu_model & POWERPC_MMU_64) { /* Use default sets of page sizes. We don't support MPSS */ - static const struct ppc_segment_page_sizes defsps_4k = { + static const struct ppc_segment_page_sizes defsps = { .sps = { { .page_shift = 12, /* 4K */ .slb_enc = 0, @@ -10481,23 +10481,7 @@ static void ppc_cpu_instance_init(Object *obj) }, }, }; - static const struct ppc_segment_page_sizes defsps_64k = { - .sps = { - { .page_shift = 12, /* 4K */ - .slb_enc = 0, - .enc = { { .page_shift = 12, .pte_enc = 0 } } - }, - { .page_shift = 16, /* 64K */ - .slb_enc = 0x110, - .enc = { { .page_shift = 16, .pte_enc = 1 } } - }, - { .page_shift = 24, /* 16M */ - .slb_enc = 0x100, - .enc = { { .page_shift = 24, .pte_enc = 0 } } - }, - }, - }; - env->sps = (env->mmu_model & POWERPC_MMU_64K) ? defsps_64k : defsps_4k; + env->sps = defsps; } #endif /* defined(TARGET_PPC64) */ } From a059471d2536dad316dfc24dd03790d0cd597081 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 23 Mar 2018 13:07:48 +1100 Subject: [PATCH 0104/2380] target/ppc: Move page size setup to helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initialization of the env->sps structure at the end of instance_init is specific to the 64-bit hash MMU, so move the code into a helper function in mmu-hash64.c. We also create a corresponding function to be called at finalize time - it's empty for now, but we'll need it shortly. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- target/ppc/mmu-hash64.c | 29 +++++++++++++++++++++++++++++ target/ppc/mmu-hash64.h | 11 +++++++++++ target/ppc/translate_init.c | 29 +++++++++-------------------- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index a87fa7c83f..4cb7d1cf07 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -1095,3 +1095,32 @@ void helper_store_lpcr(CPUPPCState *env, target_ulong val) ppc_hash64_update_rmls(cpu); ppc_hash64_update_vrma(cpu); } + +void ppc_hash64_init(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + + if (pcc->sps) { + env->sps = *pcc->sps; + } else if (env->mmu_model & POWERPC_MMU_64) { + /* Use default sets of page sizes. We don't support MPSS */ + static const struct ppc_segment_page_sizes defsps = { + .sps = { + { .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 } } + }, + { .page_shift = 24, /* 16M */ + .slb_enc = 0x100, + .enc = { { .page_shift = 24, .pte_enc = 0 } } + }, + }, + }; + env->sps = defsps; + } +} + +void ppc_hash64_finalize(PowerPCCPU *cpu) +{ +} diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 95a8c330d6..074ded4c27 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -19,6 +19,8 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, uint64_t pte0, uint64_t pte1); void ppc_hash64_update_vrma(PowerPCCPU *cpu); void ppc_hash64_update_rmls(PowerPCCPU *cpu); +void ppc_hash64_init(PowerPCCPU *cpu); +void ppc_hash64_finalize(PowerPCCPU *cpu); #endif /* @@ -136,4 +138,13 @@ static inline uint64_t ppc_hash64_hpte1(PowerPCCPU *cpu, #endif /* CONFIG_USER_ONLY */ +#if defined(CONFIG_USER_ONLY) || !defined(TARGET_PPC64) +static inline void ppc_hash64_init(PowerPCCPU *cpu) +{ +} +static inline void ppc_hash64_finalize(PowerPCCPU *cpu) +{ +} +#endif + #endif /* MMU_HASH64_H */ diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 99be6fcd68..aa63a5dcb3 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -10464,26 +10464,14 @@ static void ppc_cpu_instance_init(Object *obj) env->has_hv_mode = !!(env->msr_mask & MSR_HVB); #endif -#if defined(TARGET_PPC64) - if (pcc->sps) { - env->sps = *pcc->sps; - } else if (env->mmu_model & POWERPC_MMU_64) { - /* Use default sets of page sizes. We don't support MPSS */ - static const struct ppc_segment_page_sizes defsps = { - .sps = { - { .page_shift = 12, /* 4K */ - .slb_enc = 0, - .enc = { { .page_shift = 12, .pte_enc = 0 } } - }, - { .page_shift = 24, /* 16M */ - .slb_enc = 0x100, - .enc = { { .page_shift = 24, .pte_enc = 0 } } - }, - }, - }; - env->sps = defsps; - } -#endif /* defined(TARGET_PPC64) */ + ppc_hash64_init(cpu); +} + +static void ppc_cpu_instance_finalize(Object *obj) +{ + PowerPCCPU *cpu = POWERPC_CPU(obj); + + ppc_hash64_finalize(cpu); } static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr) @@ -10601,6 +10589,7 @@ static const TypeInfo ppc_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(PowerPCCPU), .instance_init = ppc_cpu_instance_init, + .instance_finalize = ppc_cpu_instance_finalize, .abstract = true, .class_size = sizeof(PowerPCCPUClass), .class_init = ppc_cpu_class_init, From b07c59f7c8df6efc24576f07622b61ad115468e6 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 23 Mar 2018 13:31:52 +1100 Subject: [PATCH 0105/2380] target/ppc: Split page size information into a separate allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit env->sps contains page size encoding information as an embedded structure. Since this information is specific to 64-bit hash MMUs, split it out into a separately allocated structure, to reduce the basic env size for other cpus. Along the way we make a few other cleanups: * Rename to PPCHash64Options which is more in line with qemu name conventions, and reflects that we're going to merge some more hash64 mmu specific details in there in future. Also rename its substructures to match qemu conventions. * Move structure definitions to the mmu-hash64.[ch] files. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Cédric Le Goater --- hw/ppc/fdt.c | 4 +-- target/ppc/cpu-qom.h | 4 +-- target/ppc/cpu.h | 26 +++------------- target/ppc/kvm.c | 4 +-- target/ppc/mmu-hash64.c | 61 +++++++++++++++++++++++++++---------- target/ppc/mmu-hash64.h | 22 +++++++++++++ target/ppc/translate_init.c | 36 ++-------------------- 7 files changed, 80 insertions(+), 77 deletions(-) diff --git a/hw/ppc/fdt.c b/hw/ppc/fdt.c index 2721603ffa..0828ad7254 100644 --- a/hw/ppc/fdt.c +++ b/hw/ppc/fdt.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "target/ppc/cpu.h" +#include "target/ppc/mmu-hash64.h" #include "hw/ppc/fdt.h" @@ -16,13 +17,12 @@ size_t ppc_create_page_sizes_prop(PowerPCCPU *cpu, uint32_t *prop, size_t maxsize) { - CPUPPCState *env = &cpu->env; size_t maxcells = maxsize / sizeof(uint32_t); int i, j, count; uint32_t *p = prop; for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - struct ppc_one_seg_page_size *sps = &env->sps.sps[i]; + PPCHash64SegmentPageSizes *sps = &cpu->hash64_opts->sps[i]; if (!sps->page_shift) { break; diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 9bbb05cf62..3e5ef7375f 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -160,7 +160,7 @@ enum powerpc_input_t { PPC_FLAGS_INPUT_RCPU, }; -struct ppc_segment_page_sizes; +typedef struct PPCHash64Options PPCHash64Options; /** * PowerPCCPUClass: @@ -194,7 +194,7 @@ typedef struct PowerPCCPUClass { uint32_t flags; int bfd_mach; uint32_t l1_dcache_size, l1_icache_size; - const struct ppc_segment_page_sizes *sps; + const PPCHash64Options *hash64_opts; struct ppc_radix_page_info *radix_page_info; void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index c621a6bd5e..1c5c33ca11 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -327,11 +327,13 @@ union ppc_tlb_t { #define TLB_MAS 3 #endif +typedef struct PPCHash64SegmentPageSizes PPCHash64SegmentPageSizes; + typedef struct ppc_slb_t ppc_slb_t; struct ppc_slb_t { uint64_t esid; uint64_t vsid; - const struct ppc_one_seg_page_size *sps; + const PPCHash64SegmentPageSizes *sps; }; #define MAX_SLB_ENTRIES 64 @@ -948,28 +950,8 @@ enum { #define DBELL_PROCIDTAG_MASK PPC_BITMASK(44, 63) -/*****************************************************************************/ -/* Segment page size information, used by recent hash MMUs - * The format of this structure mirrors kvm_ppc_smmu_info - */ - #define PPC_PAGE_SIZES_MAX_SZ 8 -struct ppc_one_page_size { - uint32_t page_shift; /* Page shift (or 0) */ - uint32_t pte_enc; /* Encoding in the HPTE (>>12) */ -}; - -struct ppc_one_seg_page_size { - uint32_t page_shift; /* Base page shift of segment (or 0) */ - uint32_t slb_enc; /* SLB encoding for BookS */ - struct ppc_one_page_size enc[PPC_PAGE_SIZES_MAX_SZ]; -}; - -struct ppc_segment_page_sizes { - struct ppc_one_seg_page_size sps[PPC_PAGE_SIZES_MAX_SZ]; -}; - struct ppc_radix_page_info { uint32_t count; uint32_t entries[PPC_PAGE_SIZES_MAX_SZ]; @@ -1106,7 +1088,6 @@ struct CPUPPCState { uint64_t insns_flags; uint64_t insns_flags2; #if defined(TARGET_PPC64) - struct ppc_segment_page_sizes sps; ppc_slb_t vrma_slb; target_ulong rmls; bool ci_large_pages; @@ -1227,6 +1208,7 @@ struct PowerPCCPU { PPCVirtualHypervisor *vhyp; Object *intc; int32_t node_id; /* NUMA node this CPU belongs to */ + PPCHash64Options *hash64_opts; /* Fields related to migration compatibility hacks */ bool pre_2_8_migration; diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 660467aec6..c67254acca 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -442,7 +442,7 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) } /* Convert to QEMU form */ - memset(&env->sps, 0, sizeof(env->sps)); + memset(cpu->hash64_opts->sps, 0, sizeof(*cpu->hash64_opts->sps)); /* If we have HV KVM, we need to forbid CI large pages if our * host page size is smaller than 64K. @@ -456,7 +456,7 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) * the selected CPU has with the capabilities that KVM supports. */ for (ik = iq = 0; ik < KVM_PPC_PAGE_SIZES_MAX_SZ; ik++) { - struct ppc_one_seg_page_size *qsps = &env->sps.sps[iq]; + PPCHash64SegmentPageSizes *qsps = &cpu->hash64_opts->sps[iq]; struct kvm_ppc_one_seg_page_size *ksps = &smmu_info.sps[ik]; if (!kvm_valid_page_size(smmu_info.flags, max_cpu_page_size, diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 4cb7d1cf07..6758afd9de 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -148,7 +148,7 @@ int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, { CPUPPCState *env = &cpu->env; ppc_slb_t *slb = &env->slb[slot]; - const struct ppc_one_seg_page_size *sps = NULL; + const PPCHash64SegmentPageSizes *sps = NULL; int i; if (slot >= env->slb_nr) { @@ -165,7 +165,7 @@ int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, } for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const struct ppc_one_seg_page_size *sps1 = &env->sps.sps[i]; + const PPCHash64SegmentPageSizes *sps1 = &cpu->hash64_opts->sps[i]; if (!sps1->page_shift) { break; @@ -451,8 +451,8 @@ void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, false, n * HASH_PTE_SIZE_64); } -static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps, - uint64_t pte0, uint64_t pte1) +static unsigned hpte_page_shift(const PPCHash64SegmentPageSizes *sps, + uint64_t pte0, uint64_t pte1) { int i; @@ -466,7 +466,7 @@ static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps, } for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const struct ppc_one_page_size *ps = &sps->enc[i]; + const PPCHash64PageSize *ps = &sps->enc[i]; uint64_t mask; if (!ps->page_shift) { @@ -489,7 +489,7 @@ static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps, } static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, - const struct ppc_one_seg_page_size *sps, + const PPCHash64SegmentPageSizes *sps, target_ulong ptem, ppc_hash_pte64_t *pte, unsigned *pshift) { @@ -543,7 +543,7 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, CPUPPCState *env = &cpu->env; hwaddr hash, ptex; uint64_t vsid, epnmask, epn, ptem; - const struct ppc_one_seg_page_size *sps = slb->sps; + const PPCHash64SegmentPageSizes *sps = slb->sps; /* The SLB store path should prevent any bad page size encodings * getting in there, so: */ @@ -552,7 +552,7 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, /* If ISL is set in LPCR we need to clamp the page size to 4K */ if (env->spr[SPR_LPCR] & LPCR_ISL) { /* We assume that when using TCG, 4k is first entry of SPS */ - sps = &env->sps.sps[0]; + sps = &cpu->hash64_opts->sps[0]; assert(sps->page_shift == 12); } @@ -605,7 +605,6 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, uint64_t pte0, uint64_t pte1) { - CPUPPCState *env = &cpu->env; int i; if (!(pte0 & HPTE64_V_LARGE)) { @@ -617,7 +616,7 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, * this gives an unambiguous result. */ for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const struct ppc_one_seg_page_size *sps = &env->sps.sps[i]; + const PPCHash64SegmentPageSizes *sps = &cpu->hash64_opts->sps[i]; unsigned shift; if (!sps->page_shift) { @@ -980,7 +979,7 @@ void ppc_hash64_update_rmls(PowerPCCPU *cpu) void ppc_hash64_update_vrma(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; - const struct ppc_one_seg_page_size *sps = NULL; + const PPCHash64SegmentPageSizes *sps = NULL; target_ulong esid, vsid, lpcr; ppc_slb_t *slb = &env->vrma_slb; uint32_t vrmasd; @@ -1005,7 +1004,7 @@ void ppc_hash64_update_vrma(PowerPCCPU *cpu) esid = SLB_ESID_V; for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const struct ppc_one_seg_page_size *sps1 = &env->sps.sps[i]; + const PPCHash64SegmentPageSizes *sps1 = &cpu->hash64_opts->sps[i]; if (!sps1->page_shift) { break; @@ -1101,11 +1100,12 @@ void ppc_hash64_init(PowerPCCPU *cpu) CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - if (pcc->sps) { - env->sps = *pcc->sps; + if (pcc->hash64_opts) { + cpu->hash64_opts = g_memdup(pcc->hash64_opts, + sizeof(*cpu->hash64_opts)); } else if (env->mmu_model & POWERPC_MMU_64) { /* Use default sets of page sizes. We don't support MPSS */ - static const struct ppc_segment_page_sizes defsps = { + static const PPCHash64Options defopts = { .sps = { { .page_shift = 12, /* 4K */ .slb_enc = 0, @@ -1117,10 +1117,39 @@ void ppc_hash64_init(PowerPCCPU *cpu) }, }, }; - env->sps = defsps; + cpu->hash64_opts = g_memdup(&defopts, sizeof(*cpu->hash64_opts)); } } void ppc_hash64_finalize(PowerPCCPU *cpu) { + g_free(cpu->hash64_opts); } + +const PPCHash64Options ppc_hash64_opts_POWER7 = { + .sps = { + { + .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 }, + { .page_shift = 16, .pte_enc = 0x7 }, + { .page_shift = 24, .pte_enc = 0x38 }, }, + }, + { + .page_shift = 16, /* 64K */ + .slb_enc = SLB_VSID_64K, + .enc = { { .page_shift = 16, .pte_enc = 0x1 }, + { .page_shift = 24, .pte_enc = 0x8 }, }, + }, + { + .page_shift = 24, /* 16M */ + .slb_enc = SLB_VSID_16M, + .enc = { { .page_shift = 24, .pte_enc = 0 }, }, + }, + { + .page_shift = 34, /* 16G */ + .slb_enc = SLB_VSID_16G, + .enc = { { .page_shift = 34, .pte_enc = 0x3 }, }, + }, + } +}; diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 074ded4c27..957bd68e33 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -136,6 +136,28 @@ static inline uint64_t ppc_hash64_hpte1(PowerPCCPU *cpu, return ldq_p(&(hptes[i].pte1)); } +/* + * MMU Options + */ + +struct PPCHash64PageSize { + uint32_t page_shift; /* Page shift (or 0) */ + uint32_t pte_enc; /* Encoding in the HPTE (>>12) */ +}; +typedef struct PPCHash64PageSize PPCHash64PageSize; + +struct PPCHash64SegmentPageSizes { + uint32_t page_shift; /* Base page shift of segment (or 0) */ + uint32_t slb_enc; /* SLB encoding for BookS */ + PPCHash64PageSize enc[PPC_PAGE_SIZES_MAX_SZ]; +}; + +struct PPCHash64Options { + PPCHash64SegmentPageSizes sps[PPC_PAGE_SIZES_MAX_SZ]; +}; + +extern const PPCHash64Options ppc_hash64_opts_POWER7; + #endif /* CONFIG_USER_ONLY */ #if defined(CONFIG_USER_ONLY) || !defined(TARGET_PPC64) diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index aa63a5dcb3..040d6fbac3 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8368,36 +8368,6 @@ static Property powerpc_servercpu_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -#ifdef CONFIG_SOFTMMU -static const struct ppc_segment_page_sizes POWER7_POWER8_sps = { - .sps = { - { - .page_shift = 12, /* 4K */ - .slb_enc = 0, - .enc = { { .page_shift = 12, .pte_enc = 0 }, - { .page_shift = 16, .pte_enc = 0x7 }, - { .page_shift = 24, .pte_enc = 0x38 }, }, - }, - { - .page_shift = 16, /* 64K */ - .slb_enc = SLB_VSID_64K, - .enc = { { .page_shift = 16, .pte_enc = 0x1 }, - { .page_shift = 24, .pte_enc = 0x8 }, }, - }, - { - .page_shift = 24, /* 16M */ - .slb_enc = SLB_VSID_16M, - .enc = { { .page_shift = 24, .pte_enc = 0 }, }, - }, - { - .page_shift = 34, /* 16G */ - .slb_enc = SLB_VSID_16G, - .enc = { { .page_shift = 34, .pte_enc = 0x3 }, }, - }, - } -}; -#endif /* CONFIG_SOFTMMU */ - static void init_proc_POWER7(CPUPPCState *env) { /* Common Registers */ @@ -8526,7 +8496,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; - pcc->sps = &POWER7_POWER8_sps; + pcc->hash64_opts = &ppc_hash64_opts_POWER7; #endif pcc->excp_model = POWERPC_EXCP_POWER7; pcc->bus_model = PPC_FLAGS_INPUT_POWER7; @@ -8698,7 +8668,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->mmu_model = POWERPC_MMU_2_07; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; - pcc->sps = &POWER7_POWER8_sps; + pcc->hash64_opts = &ppc_hash64_opts_POWER7; #endif pcc->excp_model = POWERPC_EXCP_POWER8; pcc->bus_model = PPC_FLAGS_INPUT_POWER7; @@ -8893,7 +8863,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; /* segment page size remain the same */ - pcc->sps = &POWER7_POWER8_sps; + pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->radix_page_info = &POWER9_radix_page_info; #endif pcc->excp_model = POWERPC_EXCP_POWER8; From 21e405f1ecd16a9036d838222f2212ec10370059 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 23 Mar 2018 13:59:20 +1100 Subject: [PATCH 0106/2380] target/ppc: Make hash64_opts field mandatory for 64-bit hash MMUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently some cpus set the hash64_opts field in the class structure, with specific details of their variant of the 64-bit hash mmu. For the remaining cpus with that mmu, ppc_hash64_realize() fills in defaults. But there are only a couple of cpus that use those fallbacks, so just have them to set the has64_opts field instead, simplifying the logic. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- target/ppc/mmu-hash64.c | 36 ++++++++++++++++++------------------ target/ppc/mmu-hash64.h | 1 + target/ppc/translate_init.c | 2 ++ 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 6758afd9de..2809c31170 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -1100,25 +1100,12 @@ void ppc_hash64_init(PowerPCCPU *cpu) CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - if (pcc->hash64_opts) { - cpu->hash64_opts = g_memdup(pcc->hash64_opts, - sizeof(*cpu->hash64_opts)); - } else if (env->mmu_model & POWERPC_MMU_64) { - /* Use default sets of page sizes. We don't support MPSS */ - static const PPCHash64Options defopts = { - .sps = { - { .page_shift = 12, /* 4K */ - .slb_enc = 0, - .enc = { { .page_shift = 12, .pte_enc = 0 } } - }, - { .page_shift = 24, /* 16M */ - .slb_enc = 0x100, - .enc = { { .page_shift = 24, .pte_enc = 0 } } - }, - }, - }; - cpu->hash64_opts = g_memdup(&defopts, sizeof(*cpu->hash64_opts)); + if (!pcc->hash64_opts) { + assert(!(env->mmu_model & POWERPC_MMU_64)); + return; } + + cpu->hash64_opts = g_memdup(pcc->hash64_opts, sizeof(*cpu->hash64_opts)); } void ppc_hash64_finalize(PowerPCCPU *cpu) @@ -1126,6 +1113,19 @@ void ppc_hash64_finalize(PowerPCCPU *cpu) g_free(cpu->hash64_opts); } +const PPCHash64Options ppc_hash64_opts_basic = { + .sps = { + { .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 } } + }, + { .page_shift = 24, /* 16M */ + .slb_enc = 0x100, + .enc = { { .page_shift = 24, .pte_enc = 0 } } + }, + }, +}; + const PPCHash64Options ppc_hash64_opts_POWER7 = { .sps = { { diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 957bd68e33..341c1524c2 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -156,6 +156,7 @@ struct PPCHash64Options { PPCHash64SegmentPageSizes sps[PPC_PAGE_SIZES_MAX_SZ]; }; +extern const PPCHash64Options ppc_hash64_opts_basic; extern const PPCHash64Options ppc_hash64_opts_POWER7; #endif /* CONFIG_USER_ONLY */ diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 040d6fbac3..ae005b2a54 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8242,6 +8242,7 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data) pcc->mmu_model = POWERPC_MMU_64B; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; + pcc->hash64_opts = &ppc_hash64_opts_basic; #endif pcc->excp_model = POWERPC_EXCP_970; pcc->bus_model = PPC_FLAGS_INPUT_970; @@ -8319,6 +8320,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) pcc->mmu_model = POWERPC_MMU_2_03; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; + pcc->hash64_opts = &ppc_hash64_opts_basic; #endif pcc->excp_model = POWERPC_EXCP_970; pcc->bus_model = PPC_FLAGS_INPUT_970; From 58969eeece99abd6d31d530ad371e789419ec9bf Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 23 Mar 2018 14:11:07 +1100 Subject: [PATCH 0107/2380] target/ppc: Move 1T segment and AMR options to PPCHash64Options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently env->mmu_model is a bit of an unholy mess of an enum of distinct MMU types, with various flag bits as well. This makes which bits of the field should be compared pretty confusing. Make a start on cleaning that up by moving two of the flags bits - POWERPC_MMU_1TSEG and POWERPC_MMU_AMR - which are specific to the 64-bit hash MMU into a new flags field in PPCHash64Options structure. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/pnv.c | 3 ++- hw/ppc/spapr.c | 2 +- target/ppc/cpu-qom.h | 11 +++-------- target/ppc/kvm.c | 4 ++-- target/ppc/mmu-hash64.c | 6 ++++-- target/ppc/mmu-hash64.h | 8 ++++++++ 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 3220ef8f79..0e49c5e9b8 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -36,6 +36,7 @@ #include "monitor/monitor.h" #include "hw/intc/intc.h" #include "hw/ipmi/ipmi.h" +#include "target/ppc/mmu-hash64.h" #include "hw/ppc/xics.h" #include "hw/ppc/pnv_xscom.h" @@ -187,7 +188,7 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0))); } - if (env->mmu_model & POWERPC_MMU_1TSEG) { + if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) { _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", segs, sizeof(segs)))); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index a4b3722656..9c26dc37e1 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -557,7 +557,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0))); } - if (env->mmu_model & POWERPC_MMU_1TSEG) { + if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) { _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", segs, sizeof(segs)))); } diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 3e5ef7375f..2bd58b2a84 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -68,22 +68,17 @@ enum powerpc_mmu_t { /* PowerPC 601 MMU model (specific BATs format) */ POWERPC_MMU_601 = 0x0000000A, #define POWERPC_MMU_64 0x00010000 -#define POWERPC_MMU_1TSEG 0x00020000 -#define POWERPC_MMU_AMR 0x00040000 #define POWERPC_MMU_V3 0x00100000 /* ISA V3.00 MMU Support */ /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, /* Architecture 2.03 and later (has LPCR) */ POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002, /* Architecture 2.06 variant */ - POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG - | POWERPC_MMU_AMR | 0x00000003, + POWERPC_MMU_2_06 = POWERPC_MMU_64 | 0x00000003, /* Architecture 2.07 variant */ - POWERPC_MMU_2_07 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG - | POWERPC_MMU_AMR | 0x00000004, + POWERPC_MMU_2_07 = POWERPC_MMU_64 | 0x00000004, /* Architecture 3.00 variant */ - POWERPC_MMU_3_00 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG - | POWERPC_MMU_AMR | POWERPC_MMU_V3 + POWERPC_MMU_3_00 = POWERPC_MMU_64 | POWERPC_MMU_V3 | 0x00000005, }; #define POWERPC_MMU_VER(x) ((x) & (POWERPC_MMU_64 | 0xFFFF)) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index c67254acca..b53af75ecf 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -302,7 +302,7 @@ static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu, /* HV KVM has backing store size restrictions */ info->flags = KVM_PPC_PAGE_SIZES_REAL; - if (env->mmu_model & POWERPC_MMU_1TSEG) { + if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) { info->flags |= KVM_PPC_1T_SEGMENTS; } @@ -482,7 +482,7 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) } env->slb_nr = smmu_info.slb_size; if (!(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) { - env->mmu_model &= ~POWERPC_MMU_1TSEG; + cpu->hash64_opts->flags &= ~PPC_HASH64_1TSEG; } } diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 2809c31170..c9ee55e1ea 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -160,7 +160,7 @@ int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, if (vsid & (SLB_VSID_B & ~SLB_VSID_B_1T)) { return -1; /* Bad segment size */ } - if ((vsid & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { + if ((vsid & SLB_VSID_B) && !(ppc_hash64_has(cpu, PPC_HASH64_1TSEG))) { return -1; /* 1T segment on MMU that doesn't support it */ } @@ -369,7 +369,7 @@ static int ppc_hash64_amr_prot(PowerPCCPU *cpu, ppc_hash_pte64_t pte) int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; /* Only recent MMUs implement Virtual Page Class Key Protection */ - if (!(env->mmu_model & POWERPC_MMU_AMR)) { + if (!ppc_hash64_has(cpu, PPC_HASH64_AMR)) { return prot; } @@ -1114,6 +1114,7 @@ void ppc_hash64_finalize(PowerPCCPU *cpu) } const PPCHash64Options ppc_hash64_opts_basic = { + .flags = 0, .sps = { { .page_shift = 12, /* 4K */ .slb_enc = 0, @@ -1127,6 +1128,7 @@ const PPCHash64Options ppc_hash64_opts_basic = { }; const PPCHash64Options ppc_hash64_opts_POWER7 = { + .flags = PPC_HASH64_1TSEG | PPC_HASH64_AMR, .sps = { { .page_shift = 12, /* 4K */ diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 341c1524c2..b2b5d25238 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -153,12 +153,20 @@ struct PPCHash64SegmentPageSizes { }; struct PPCHash64Options { +#define PPC_HASH64_1TSEG 0x00001 +#define PPC_HASH64_AMR 0x00002 + unsigned flags; PPCHash64SegmentPageSizes sps[PPC_PAGE_SIZES_MAX_SZ]; }; extern const PPCHash64Options ppc_hash64_opts_basic; extern const PPCHash64Options ppc_hash64_opts_POWER7; +static inline bool ppc_hash64_has(PowerPCCPU *cpu, unsigned feature) +{ + return !!(cpu->hash64_opts->flags & feature); +} + #endif /* CONFIG_USER_ONLY */ #if defined(CONFIG_USER_ONLY) || !defined(TARGET_PPC64) From 26cd35b8613881c410d5226e6dc56e7bfb4b83d1 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 23 Mar 2018 14:32:48 +1100 Subject: [PATCH 0108/2380] target/ppc: Fold ci_large_pages flag into PPCHash64Options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ci_large_pages boolean in CPUPPCState is only relevant to 64-bit hash MMU machines, indicating whether it's possible to map large (> 4kiB) pages as cache-inhibitied (i.e. for IO, rather than memory). Fold it as another flag into the PPCHash64Options structure. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/spapr.c | 3 +-- target/ppc/cpu.h | 1 - target/ppc/kvm.c | 6 +++++- target/ppc/mmu-hash64.c | 2 +- target/ppc/mmu-hash64.h | 1 + target/ppc/translate_init.c | 3 --- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 9c26dc37e1..abf38c62e8 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -263,7 +263,6 @@ static void spapr_populate_pa_features(sPAPRMachineState *spapr, void *fdt, int offset, bool legacy_guest) { - CPUPPCState *env = &cpu->env; uint8_t pa_features_206[] = { 6, 0, 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; uint8_t pa_features_207[] = { 24, 0, @@ -315,7 +314,7 @@ static void spapr_populate_pa_features(sPAPRMachineState *spapr, return; } - if (env->ci_large_pages) { + if (ppc_hash64_has(cpu, PPC_HASH64_CI_LARGEPAGE)) { /* * Note: we keep CI large pages off by default because a 64K capable * guest provisioned with large pages might otherwise try to map a qemu diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 1c5c33ca11..c0c44fb91d 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1090,7 +1090,6 @@ struct CPUPPCState { #if defined(TARGET_PPC64) ppc_slb_t vrma_slb; target_ulong rmls; - bool ci_large_pages; #endif #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index b53af75ecf..25f93dc708 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -448,7 +448,11 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) * host page size is smaller than 64K. */ if (smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL) { - env->ci_large_pages = getpagesize() >= 0x10000; + if (getpagesize() >= 0x10000) { + cpu->hash64_opts->flags |= PPC_HASH64_CI_LARGEPAGE; + } else { + cpu->hash64_opts->flags &= ~PPC_HASH64_CI_LARGEPAGE; + } } /* diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index c9ee55e1ea..f341714550 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -1128,7 +1128,7 @@ const PPCHash64Options ppc_hash64_opts_basic = { }; const PPCHash64Options ppc_hash64_opts_POWER7 = { - .flags = PPC_HASH64_1TSEG | PPC_HASH64_AMR, + .flags = PPC_HASH64_1TSEG | PPC_HASH64_AMR | PPC_HASH64_CI_LARGEPAGE, .sps = { { .page_shift = 12, /* 4K */ diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index b2b5d25238..f1babb0afc 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -155,6 +155,7 @@ struct PPCHash64SegmentPageSizes { struct PPCHash64Options { #define PPC_HASH64_1TSEG 0x00001 #define PPC_HASH64_AMR 0x00002 +#define PPC_HASH64_CI_LARGEPAGE 0x00004 unsigned flags; PPCHash64SegmentPageSizes sps[PPC_PAGE_SIZES_MAX_SZ]; }; diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index ae005b2a54..a925cf5cd3 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8392,7 +8392,6 @@ static void init_proc_POWER7(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->slb_nr = 32; #endif - env->ci_large_pages = true; env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8547,7 +8546,6 @@ static void init_proc_POWER8(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->slb_nr = 32; #endif - env->ci_large_pages = true; env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8748,7 +8746,6 @@ static void init_proc_POWER9(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->slb_nr = 32; #endif - env->ci_large_pages = true; env->dcache_line_size = 128; env->icache_line_size = 128; From ca79b3b7fd0757ceb6436d4407e26c8b511a4080 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 23 Mar 2018 16:42:45 +1100 Subject: [PATCH 0109/2380] target/ppc: Remove unnecessary POWERPC_MMU_V3 flag from mmu_model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only place we test this flag is in conjunction with ppc64_use_proc_tbl(). That checks for the LPCR_UPRT bit, which we already ensure can't be set except on a machine with a v3 MMU (i.e. POWER9). Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- target/ppc/cpu-qom.h | 4 +--- target/ppc/mmu-hash64.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 2bd58b2a84..ef96d42cf2 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -68,7 +68,6 @@ enum powerpc_mmu_t { /* PowerPC 601 MMU model (specific BATs format) */ POWERPC_MMU_601 = 0x0000000A, #define POWERPC_MMU_64 0x00010000 -#define POWERPC_MMU_V3 0x00100000 /* ISA V3.00 MMU Support */ /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, /* Architecture 2.03 and later (has LPCR) */ @@ -78,8 +77,7 @@ enum powerpc_mmu_t { /* Architecture 2.07 variant */ POWERPC_MMU_2_07 = POWERPC_MMU_64 | 0x00000004, /* Architecture 3.00 variant */ - POWERPC_MMU_3_00 = POWERPC_MMU_64 | POWERPC_MMU_V3 - | 0x00000005, + POWERPC_MMU_3_00 = POWERPC_MMU_64 | 0x00000005, }; #define POWERPC_MMU_VER(x) ((x) & (POWERPC_MMU_64 | 0xFFFF)) #define POWERPC_MMU_VER_64B POWERPC_MMU_VER(POWERPC_MMU_64B) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index f341714550..df26a03c15 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -761,7 +761,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, slb = slb_lookup(cpu, eaddr); if (!slb) { /* No entry found, check if in-memory segment tables are in use */ - if ((env->mmu_model & POWERPC_MMU_V3) && ppc64_use_proc_tbl(cpu)) { + if (ppc64_use_proc_tbl(cpu)) { /* TODO - Unsupported */ error_report("Segment Table Support Unimplemented"); exit(1); From 0941d728a4636f68523d99a729e24ee12c36d440 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 23 Mar 2018 16:48:43 +1100 Subject: [PATCH 0110/2380] target/ppc: Get rid of POWERPC_MMU_VER() macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These macros were introduced to deal with the fact that the mmu_model field has bit flags mixed in with what's otherwise an enum of various mmu types. We've now eliminated all those flags except for one, and that one - POWERPC_MMU_64 - is already included/compared in the MMU_VER macros. So, we can get rid of those macros and just directly compare mmu_model values in the places it was used. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- target/ppc/cpu-qom.h | 6 ------ target/ppc/kvm.c | 8 ++++---- target/ppc/mmu-hash64.c | 12 ++++++------ target/ppc/mmu_helper.c | 24 ++++++++++++------------ target/ppc/translate.c | 12 ++++++------ 5 files changed, 28 insertions(+), 34 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index ef96d42cf2..433a71e484 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -79,12 +79,6 @@ enum powerpc_mmu_t { /* Architecture 3.00 variant */ POWERPC_MMU_3_00 = POWERPC_MMU_64 | 0x00000005, }; -#define POWERPC_MMU_VER(x) ((x) & (POWERPC_MMU_64 | 0xFFFF)) -#define POWERPC_MMU_VER_64B POWERPC_MMU_VER(POWERPC_MMU_64B) -#define POWERPC_MMU_VER_2_03 POWERPC_MMU_VER(POWERPC_MMU_2_03) -#define POWERPC_MMU_VER_2_06 POWERPC_MMU_VER(POWERPC_MMU_2_06) -#define POWERPC_MMU_VER_2_07 POWERPC_MMU_VER(POWERPC_MMU_2_07) -#define POWERPC_MMU_VER_3_00 POWERPC_MMU_VER(POWERPC_MMU_3_00) /*****************************************************************************/ /* Exception model */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 25f93dc708..246b9eab2a 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -306,8 +306,8 @@ static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu, info->flags |= KVM_PPC_1T_SEGMENTS; } - if (POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_06 || - POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_07) { + if (env->mmu_model == POWERPC_MMU_2_06 || + env->mmu_model == POWERPC_MMU_2_07) { info->slb_size = 32; } else { info->slb_size = 64; @@ -321,8 +321,8 @@ static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu, i++; /* 64K on MMU 2.06 and later */ - if (POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_06 || - POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_07) { + if (env->mmu_model == POWERPC_MMU_2_06 || + env->mmu_model == POWERPC_MMU_2_07) { info->sps[i].page_shift = 16; info->sps[i].slb_enc = 0x110; info->sps[i].enc[0].page_shift = 16; diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index df26a03c15..a5570c8774 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -1033,8 +1033,8 @@ void helper_store_lpcr(CPUPPCState *env, target_ulong val) uint64_t lpcr = 0; /* Filter out bits */ - switch (POWERPC_MMU_VER(env->mmu_model)) { - case POWERPC_MMU_VER_64B: /* 970 */ + switch (env->mmu_model) { + case POWERPC_MMU_64B: /* 970 */ if (val & 0x40) { lpcr |= LPCR_LPES0; } @@ -1060,26 +1060,26 @@ void helper_store_lpcr(CPUPPCState *env, target_ulong val) * to dig HRMOR out of HID5 */ break; - case POWERPC_MMU_VER_2_03: /* P5p */ + case POWERPC_MMU_2_03: /* P5p */ lpcr = val & (LPCR_RMLS | LPCR_ILE | LPCR_LPES0 | LPCR_LPES1 | LPCR_RMI | LPCR_HDICE); break; - case POWERPC_MMU_VER_2_06: /* P7 */ + case POWERPC_MMU_2_06: /* P7 */ lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 | LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE); break; - case POWERPC_MMU_VER_2_07: /* P8 */ + case POWERPC_MMU_2_07: /* P8 */ lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 | LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE); break; - case POWERPC_MMU_VER_3_00: /* P9 */ + case POWERPC_MMU_3_00: /* P9 */ lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 5568d1642b..8075b7149a 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -1266,7 +1266,7 @@ static void mmu6xx_dump_mmu(FILE *f, fprintf_function cpu_fprintf, void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) { - switch (POWERPC_MMU_VER(env->mmu_model)) { + switch (env->mmu_model) { case POWERPC_MMU_BOOKE: mmubooke_dump_mmu(f, cpu_fprintf, env); break; @@ -1278,13 +1278,13 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) mmu6xx_dump_mmu(f, cpu_fprintf, env); break; #if defined(TARGET_PPC64) - case POWERPC_MMU_VER_64B: - case POWERPC_MMU_VER_2_03: - case POWERPC_MMU_VER_2_06: - case POWERPC_MMU_VER_2_07: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env)); break; - case POWERPC_MMU_VER_3_00: + case POWERPC_MMU_3_00: if (ppc64_radix_guest(ppc_env_get_cpu(env))) { /* TODO - Unsupported */ } else { @@ -1423,14 +1423,14 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) CPUPPCState *env = &cpu->env; mmu_ctx_t ctx; - switch (POWERPC_MMU_VER(env->mmu_model)) { + switch (env->mmu_model) { #if defined(TARGET_PPC64) - case POWERPC_MMU_VER_64B: - case POWERPC_MMU_VER_2_03: - case POWERPC_MMU_VER_2_06: - case POWERPC_MMU_VER_2_07: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: return ppc_hash64_get_phys_page_debug(cpu, addr); - case POWERPC_MMU_VER_3_00: + case POWERPC_MMU_3_00: if (ppc64_radix_guest(ppc_env_get_cpu(env))) { return ppc_radix64_get_phys_page_debug(cpu, addr); } else { diff --git a/target/ppc/translate.c b/target/ppc/translate.c index b0d79a3fb9..3beaa1e2f0 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7121,17 +7121,17 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, if (env->spr_cb[SPR_LPCR].name) cpu_fprintf(f, " LPCR " TARGET_FMT_lx "\n", env->spr[SPR_LPCR]); - switch (POWERPC_MMU_VER(env->mmu_model)) { + switch (env->mmu_model) { case POWERPC_MMU_32B: case POWERPC_MMU_601: case POWERPC_MMU_SOFT_6xx: case POWERPC_MMU_SOFT_74xx: #if defined(TARGET_PPC64) - case POWERPC_MMU_VER_64B: - case POWERPC_MMU_VER_2_03: - case POWERPC_MMU_VER_2_06: - case POWERPC_MMU_VER_2_07: - case POWERPC_MMU_VER_3_00: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: + case POWERPC_MMU_3_00: #endif if (env->spr_cb[SPR_SDR1].name) { /* SDR1 Exists */ cpu_fprintf(f, " SDR1 " TARGET_FMT_lx " ", env->spr[SPR_SDR1]); From 67d7d66f27c49a87c6f28ccff814f5d7eaaccec6 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 29 Mar 2018 18:29:38 +1100 Subject: [PATCH 0111/2380] target/ppc: Fold slb_nr into PPCHash64Options The env->slb_nr field gives the size of the SLB (Segment Lookaside Buffer). This is another static-after-initialization parameter of the specific version of the 64-bit hash MMU in the CPU. So, this patch folds the field into PPCHash64Options with the other hash MMU options. This is a bit more complicated that the things previously put in there, because slb_nr was foolishly included in the migration stream. So we need some of the usual dance to handle backwards compatible migration. Signed-off-by: David Gibson Reviewed-by: Greg Kurz --- hw/ppc/pnv.c | 2 +- hw/ppc/spapr.c | 11 ++++++++--- target/ppc/cpu.h | 3 ++- target/ppc/kvm.c | 2 +- target/ppc/machine.c | 23 ++++++++++++++++++++--- target/ppc/mmu-hash64.c | 15 +++++++++------ target/ppc/mmu-hash64.h | 1 + target/ppc/translate_init.c | 17 ++--------------- 8 files changed, 44 insertions(+), 30 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 0e49c5e9b8..0314881316 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -180,7 +180,7 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); - _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size))); _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index abf38c62e8..8c2e3ccb89 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -547,8 +547,8 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); - _FDT((fdt_setprop_cell(fdt, offset, "slb-size", env->slb_nr))); - _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr))); + _FDT((fdt_setprop_cell(fdt, offset, "slb-size", cpu->hash64_opts->slb_size))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size))); _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); @@ -3958,7 +3958,12 @@ DEFINE_SPAPR_MACHINE(2_13, "2.13", true); * pseries-2.12 */ #define SPAPR_COMPAT_2_12 \ - HW_COMPAT_2_12 + HW_COMPAT_2_12 \ + { \ + .driver = TYPE_POWERPC_CPU, \ + .property = "pre-2.13-migration", \ + .value = "on", \ + }, static void spapr_machine_2_12_instance_options(MachineState *machine) { diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index c0c44fb91d..8c9e03f54d 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1025,7 +1025,6 @@ struct CPUPPCState { #if defined(TARGET_PPC64) /* PowerPC 64 SLB area */ ppc_slb_t slb[MAX_SLB_ENTRIES]; - int32_t slb_nr; /* tcg TLB needs flush (deferred slb inval instruction typically) */ #endif /* segment registers */ @@ -1216,6 +1215,8 @@ struct PowerPCCPU { uint64_t mig_insns_flags2; uint32_t mig_nb_BATs; bool pre_2_10_migration; + bool pre_2_13_migration; + int32_t mig_slb_nr; }; static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 246b9eab2a..6de59c5b21 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -484,7 +484,7 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) break; } } - env->slb_nr = smmu_info.slb_size; + cpu->hash64_opts->slb_size = smmu_info.slb_size; if (!(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) { cpu->hash64_opts->flags &= ~PPC_HASH64_1TSEG; } diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 0634cdb295..3d6434a006 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -18,6 +18,9 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) unsigned int i, j; target_ulong sdr1; uint32_t fpscr; +#if defined(TARGET_PPC64) + int32_t slb_nr; +#endif target_ulong xer; for (i = 0; i < 32; i++) @@ -49,7 +52,7 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_sbe32s(f, &env->access_type); #if defined(TARGET_PPC64) qemu_get_betls(f, &env->spr[SPR_ASR]); - qemu_get_sbe32s(f, &env->slb_nr); + qemu_get_sbe32s(f, &slb_nr); #endif qemu_get_betls(f, &sdr1); for (i = 0; i < 32; i++) @@ -146,6 +149,15 @@ static bool cpu_pre_2_8_migration(void *opaque, int version_id) return cpu->pre_2_8_migration; } +#if defined(TARGET_PPC64) +static bool cpu_pre_2_13_migration(void *opaque, int version_id) +{ + PowerPCCPU *cpu = opaque; + + return cpu->pre_2_13_migration; +} +#endif + static int cpu_pre_save(void *opaque) { PowerPCCPU *cpu = opaque; @@ -203,6 +215,11 @@ static int cpu_pre_save(void *opaque) cpu->mig_insns_flags2 = env->insns_flags2 & insns_compat_mask2; cpu->mig_nb_BATs = env->nb_BATs; } + if (cpu->pre_2_13_migration) { + if (cpu->hash64_opts) { + cpu->mig_slb_nr = cpu->hash64_opts->slb_size; + } + } return 0; } @@ -478,7 +495,7 @@ static int slb_post_load(void *opaque, int version_id) /* We've pulled in the raw esid and vsid values from the migration * stream, but we need to recompute the page size pointers */ - for (i = 0; i < env->slb_nr; i++) { + for (i = 0; i < cpu->hash64_opts->slb_size; i++) { if (ppc_store_slb(cpu, i, env->slb[i].esid, env->slb[i].vsid) < 0) { /* Migration source had bad values in its SLB */ return -1; @@ -495,7 +512,7 @@ static const VMStateDescription vmstate_slb = { .needed = slb_needed, .post_load = slb_post_load, .fields = (VMStateField[]) { - VMSTATE_INT32_EQUAL(env.slb_nr, PowerPCCPU, NULL), + VMSTATE_INT32_TEST(mig_slb_nr, PowerPCCPU, cpu_pre_2_13_migration), VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), VMSTATE_END_OF_LIST() } diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index a5570c8774..7e0adecfd9 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -52,7 +52,7 @@ static ppc_slb_t *slb_lookup(PowerPCCPU *cpu, target_ulong eaddr) esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V; esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V; - for (n = 0; n < env->slb_nr; n++) { + for (n = 0; n < cpu->hash64_opts->slb_size; n++) { ppc_slb_t *slb = &env->slb[n]; LOG_SLB("%s: slot %d %016" PRIx64 " %016" @@ -80,7 +80,7 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu) cpu_synchronize_state(CPU(cpu)); cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n"); - for (i = 0; i < env->slb_nr; i++) { + for (i = 0; i < cpu->hash64_opts->slb_size; i++) { slbe = env->slb[i].esid; slbv = env->slb[i].vsid; if (slbe == 0 && slbv == 0) { @@ -93,10 +93,11 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu) void helper_slbia(CPUPPCState *env) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); int n; /* XXX: Warning: slbia never invalidates the first segment */ - for (n = 1; n < env->slb_nr; n++) { + for (n = 1; n < cpu->hash64_opts->slb_size; n++) { ppc_slb_t *slb = &env->slb[n]; if (slb->esid & SLB_ESID_V) { @@ -151,7 +152,7 @@ int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, const PPCHash64SegmentPageSizes *sps = NULL; int i; - if (slot >= env->slb_nr) { + if (slot >= cpu->hash64_opts->slb_size) { return -1; /* Bad slot number */ } if (esid & ~(SLB_ESID_ESID | SLB_ESID_V)) { @@ -202,7 +203,7 @@ static int ppc_load_slb_esid(PowerPCCPU *cpu, target_ulong rb, int slot = rb & 0xfff; ppc_slb_t *slb = &env->slb[slot]; - if (slot >= env->slb_nr) { + if (slot >= cpu->hash64_opts->slb_size) { return -1; } @@ -217,7 +218,7 @@ static int ppc_load_slb_vsid(PowerPCCPU *cpu, target_ulong rb, int slot = rb & 0xfff; ppc_slb_t *slb = &env->slb[slot]; - if (slot >= env->slb_nr) { + if (slot >= cpu->hash64_opts->slb_size) { return -1; } @@ -1115,6 +1116,7 @@ void ppc_hash64_finalize(PowerPCCPU *cpu) const PPCHash64Options ppc_hash64_opts_basic = { .flags = 0, + .slb_size = 64, .sps = { { .page_shift = 12, /* 4K */ .slb_enc = 0, @@ -1129,6 +1131,7 @@ const PPCHash64Options ppc_hash64_opts_basic = { const PPCHash64Options ppc_hash64_opts_POWER7 = { .flags = PPC_HASH64_1TSEG | PPC_HASH64_AMR | PPC_HASH64_CI_LARGEPAGE, + .slb_size = 32, .sps = { { .page_shift = 12, /* 4K */ diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index f1babb0afc..d5fc03441d 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -157,6 +157,7 @@ struct PPCHash64Options { #define PPC_HASH64_AMR 0x00002 #define PPC_HASH64_CI_LARGEPAGE 0x00004 unsigned flags; + unsigned slb_size; PPCHash64SegmentPageSizes sps[PPC_PAGE_SIZES_MAX_SZ]; }; diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index a925cf5cd3..bb79d23b50 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8195,9 +8195,6 @@ static void init_proc_970(CPUPPCState *env) gen_spr_970_dbg(env); /* env variables */ -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 64; -#endif env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8272,9 +8269,6 @@ static void init_proc_power5plus(CPUPPCState *env) gen_spr_power5p_ear(env); /* env variables */ -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 64; -#endif env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8389,9 +8383,6 @@ static void init_proc_POWER7(CPUPPCState *env) gen_spr_power7_book4(env); /* env variables */ -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 32; -#endif env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8543,9 +8534,6 @@ static void init_proc_POWER8(CPUPPCState *env) gen_spr_power8_rpr(env); /* env variables */ -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 32; -#endif env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8743,9 +8731,6 @@ static void init_proc_POWER9(CPUPPCState *env) KVM_REG_PPC_PSSCR, 0); /* env variables */ -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 32; -#endif env->dcache_line_size = 128; env->icache_line_size = 128; @@ -10486,6 +10471,8 @@ static Property ppc_cpu_properties[] = { DEFINE_PROP_BOOL("pre-2.8-migration", PowerPCCPU, pre_2_8_migration, false), DEFINE_PROP_BOOL("pre-2.10-migration", PowerPCCPU, pre_2_10_migration, false), + DEFINE_PROP_BOOL("pre-2.13-migration", PowerPCCPU, pre_2_13_migration, + false), DEFINE_PROP_END_OF_LIST(), }; From da9f80fbad21319194e73355dea8a1cff6a574e4 Mon Sep 17 00:00:00 2001 From: Serhii Popovych Date: Wed, 11 Apr 2018 14:41:59 -0400 Subject: [PATCH 0112/2380] spapr: Add ibm,max-associativity-domains property Now recent kernels (i.e. since linux-stable commit a346137e9142 ("powerpc/numa: Use ibm,max-associativity-domains to discover possible nodes") support this property to mark initially memory-less NUMA nodes as "possible" to allow further memory hot-add to them. Advertise this property for pSeries machines to let guest kernels detect maximum supported node configuration and benefit from kernel side change when hot-add memory to specific, possibly empty before, NUMA node. Signed-off-by: Serhii Popovych Signed-off-by: David Gibson --- hw/ppc/spapr.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 8c2e3ccb89..2203b6f46d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -909,6 +909,13 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) 0, cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE), cpu_to_be32(max_cpus / smp_threads), }; + uint32_t maxdomains[] = { + cpu_to_be32(4), + cpu_to_be32(0), + cpu_to_be32(0), + cpu_to_be32(0), + cpu_to_be32(nb_numa_nodes ? nb_numa_nodes - 1 : 0), + }; _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas")); @@ -945,6 +952,9 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) _FDT(fdt_setprop(fdt, rtas, "ibm,associativity-reference-points", refpoints, sizeof(refpoints))); + _FDT(fdt_setprop(fdt, rtas, "ibm,max-associativity-domains", + maxdomains, sizeof(maxdomains))); + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)); _FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate", From 03f048090ee73bfb18b16a5dc462b5411120fb4d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 18 Apr 2018 16:28:02 +0200 Subject: [PATCH 0113/2380] ppc: e500: switch E500 based machines to full machine definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert PPCE500Params to PCCE500MachineClass which it essentially is, and introduce PCCE500MachineState to keep track of E500 specific state instead of adding global variables or extra parameters to functions when we need to keep data beyond machine init (i.e. make it look like typical fully defined machine). It's pretty shallow conversion instead of currently used trivial DEFINE_MACHINE() macro. It adds extra 60LOC of boilerplate code of full machine definition. The patch on top[1] will use PCCE500MachineState to keep track of platform_bus device and add E500Plate specific machine class to use HOTPLUG_HANDLER for explicitly initializing dynamic sysbus devices at the time they are added instead of delaying it to machine done time by platform_bus_init_notify() which is being removed. 1) <1523551221-11612-3-git-send-email-imammedo@redhat.com> Signed-off-by: Igor Mammedov Suggested-by: David Gibson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/ppc/e500.c | 119 ++++++++++++++++++++++++--------------------- hw/ppc/e500.h | 29 ++++++++--- hw/ppc/e500plat.c | 64 ++++++++++++++---------- hw/ppc/mpc8544ds.c | 47 +++++++++++------- 4 files changed, 156 insertions(+), 103 deletions(-) diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 2ddab7ed24..3e0923cfba 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -221,14 +221,14 @@ static void sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque) } } -static void platform_bus_create_devtree(PPCE500Params *params, void *fdt, - const char *mpic) +static void platform_bus_create_devtree(const PPCE500MachineClass *pmc, + void *fdt, const char *mpic) { - gchar *node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base); + gchar *node = g_strdup_printf("/platform@%"PRIx64, pmc->platform_bus_base); const char platcomp[] = "qemu,platform\0simple-bus"; - uint64_t addr = params->platform_bus_base; - uint64_t size = params->platform_bus_size; - int irq_start = params->platform_bus_first_irq; + uint64_t addr = pmc->platform_bus_base; + uint64_t size = pmc->platform_bus_size; + int irq_start = pmc->platform_bus_first_irq; PlatformBusDevice *pbus; DeviceState *dev; @@ -265,8 +265,7 @@ static void platform_bus_create_devtree(PPCE500Params *params, void *fdt, g_free(node); } -static int ppce500_load_device_tree(MachineState *machine, - PPCE500Params *params, +static int ppce500_load_device_tree(PPCE500MachineState *pms, hwaddr addr, hwaddr initrd_base, hwaddr initrd_size, @@ -274,6 +273,8 @@ static int ppce500_load_device_tree(MachineState *machine, hwaddr kernel_size, bool dry_run) { + MachineState *machine = MACHINE(pms); + const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); CPUPPCState *env = first_cpu->env_ptr; int ret = -1; uint64_t mem_reg_property[] = { 0, cpu_to_be64(machine->ram_size) }; @@ -295,12 +296,12 @@ static int ppce500_load_device_tree(MachineState *machine, int len; uint32_t pci_ranges[14] = { - 0x2000000, 0x0, params->pci_mmio_bus_base, - params->pci_mmio_base >> 32, params->pci_mmio_base, + 0x2000000, 0x0, pmc->pci_mmio_bus_base, + pmc->pci_mmio_base >> 32, pmc->pci_mmio_base, 0x0, 0x20000000, 0x1000000, 0x0, 0x0, - params->pci_pio_base >> 32, params->pci_pio_base, + pmc->pci_pio_base >> 32, pmc->pci_pio_base, 0x0, 0x10000, }; QemuOpts *machine_opts = qemu_get_machine_opts(); @@ -391,7 +392,7 @@ static int ppce500_load_device_tree(MachineState *machine, for (i = smp_cpus - 1; i >= 0; i--) { CPUState *cpu; char cpu_name[128]; - uint64_t cpu_release_addr = params->spin_base + (i * 0x20); + uint64_t cpu_release_addr = pmc->spin_base + (i * 0x20); cpu = qemu_get_cpu(i); if (cpu == NULL) { @@ -425,7 +426,7 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_add_subnode(fdt, "/aliases"); /* XXX These should go into their respective devices' code */ - snprintf(soc, sizeof(soc), "/soc@%"PRIx64, params->ccsrbar_base); + snprintf(soc, sizeof(soc), "/soc@%"PRIx64, pmc->ccsrbar_base); qemu_fdt_add_subnode(fdt, soc); qemu_fdt_setprop_string(fdt, soc, "device_type", "soc"); qemu_fdt_setprop(fdt, soc, "compatible", compatible_sb, @@ -433,7 +434,7 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cell(fdt, soc, "#address-cells", 1); qemu_fdt_setprop_cell(fdt, soc, "#size-cells", 1); qemu_fdt_setprop_cells(fdt, soc, "ranges", 0x0, - params->ccsrbar_base >> 32, params->ccsrbar_base, + pmc->ccsrbar_base >> 32, pmc->ccsrbar_base, MPC8544_CCSRBAR_SIZE); /* XXX should contain a reasonable value */ qemu_fdt_setprop_cell(fdt, soc, "bus-frequency", 0); @@ -493,7 +494,7 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cell(fdt, msi, "linux,phandle", msi_ph); snprintf(pci, sizeof(pci), "/pci@%llx", - params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET); + pmc->ccsrbar_base + MPC8544_PCI_REGS_OFFSET); qemu_fdt_add_subnode(fdt, pci); qemu_fdt_setprop_cell(fdt, pci, "cell-index", 0); qemu_fdt_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); @@ -501,7 +502,7 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, 0x0, 0x7); pci_map = pci_map_create(fdt, qemu_fdt_get_phandle(fdt, mpic), - params->pci_first_slot, params->pci_nr_slots, + pmc->pci_first_slot, pmc->pci_nr_slots, &len); qemu_fdt_setprop(fdt, pci, "interrupt-map", pci_map, len); qemu_fdt_setprop_phandle(fdt, pci, "interrupt-parent", mpic); @@ -513,8 +514,8 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cell(fdt, pci, "fsl,msi", msi_ph); qemu_fdt_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); qemu_fdt_setprop_cells(fdt, pci, "reg", - (params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET) >> 32, - (params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET), + (pmc->ccsrbar_base + MPC8544_PCI_REGS_OFFSET) >> 32, + (pmc->ccsrbar_base + MPC8544_PCI_REGS_OFFSET), 0, 0x1000); qemu_fdt_setprop_cell(fdt, pci, "clock-frequency", 66666666); qemu_fdt_setprop_cell(fdt, pci, "#interrupt-cells", 1); @@ -522,15 +523,15 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3); qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci); - if (params->has_mpc8xxx_gpio) { + if (pmc->has_mpc8xxx_gpio) { create_dt_mpc8xxx_gpio(fdt, soc, mpic); } - if (params->has_platform_bus) { - platform_bus_create_devtree(params, fdt, mpic); + if (pmc->has_platform_bus) { + platform_bus_create_devtree(pmc, fdt, mpic); } - params->fixup_devtree(params, fdt); + pmc->fixup_devtree(fdt); if (toplevel_compat) { qemu_fdt_setprop(fdt, "/", "compatible", toplevel_compat, @@ -551,8 +552,7 @@ out: } typedef struct DeviceTreeParams { - MachineState *machine; - PPCE500Params params; + PPCE500MachineState *machine; hwaddr addr; hwaddr initrd_base; hwaddr initrd_size; @@ -564,7 +564,7 @@ typedef struct DeviceTreeParams { static void ppce500_reset_device_tree(void *opaque) { DeviceTreeParams *p = opaque; - ppce500_load_device_tree(p->machine, &p->params, p->addr, p->initrd_base, + ppce500_load_device_tree(p->machine, p->addr, p->initrd_base, p->initrd_size, p->kernel_base, p->kernel_size, false); } @@ -575,8 +575,7 @@ static void ppce500_init_notify(Notifier *notifier, void *data) ppce500_reset_device_tree(p); } -static int ppce500_prep_device_tree(MachineState *machine, - PPCE500Params *params, +static int ppce500_prep_device_tree(PPCE500MachineState *machine, hwaddr addr, hwaddr initrd_base, hwaddr initrd_size, @@ -585,7 +584,6 @@ static int ppce500_prep_device_tree(MachineState *machine, { DeviceTreeParams *p = g_new(DeviceTreeParams, 1); p->machine = machine; - p->params = *params; p->addr = addr; p->initrd_base = initrd_base; p->initrd_size = initrd_size; @@ -597,9 +595,8 @@ static int ppce500_prep_device_tree(MachineState *machine, qemu_add_machine_init_done_notifier(&p->notifier); /* Issue the device tree loader once, so that we get the size of the blob */ - return ppce500_load_device_tree(machine, params, addr, initrd_base, - initrd_size, kernel_base, kernel_size, - true); + return ppce500_load_device_tree(machine, addr, initrd_base, initrd_size, + kernel_base, kernel_size, true); } /* Create -kernel TLB entries for BookE. */ @@ -685,17 +682,19 @@ static void ppce500_cpu_reset(void *opaque) mmubooke_create_initial_mapping(env); } -static DeviceState *ppce500_init_mpic_qemu(PPCE500Params *params, +static DeviceState *ppce500_init_mpic_qemu(PPCE500MachineState *pms, qemu_irq **irqs) { DeviceState *dev; SysBusDevice *s; int i, j, k; + MachineState *machine = MACHINE(pms); + const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); dev = qdev_create(NULL, TYPE_OPENPIC); - object_property_add_child(qdev_get_machine(), "pic", OBJECT(dev), + object_property_add_child(OBJECT(machine), "pic", OBJECT(dev), &error_fatal); - qdev_prop_set_uint32(dev, "model", params->mpic_version); + qdev_prop_set_uint32(dev, "model", pmc->mpic_version); qdev_prop_set_uint32(dev, "nb_cpus", smp_cpus); qdev_init_nofail(dev); @@ -711,7 +710,7 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500Params *params, return dev; } -static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, +static DeviceState *ppce500_init_mpic_kvm(const PPCE500MachineClass *pmc, qemu_irq **irqs, Error **errp) { Error *err = NULL; @@ -719,7 +718,7 @@ static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, CPUState *cs; dev = qdev_create(NULL, TYPE_KVM_OPENPIC); - qdev_prop_set_uint32(dev, "model", params->mpic_version); + qdev_prop_set_uint32(dev, "model", pmc->mpic_version); object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err) { @@ -739,11 +738,12 @@ static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, return dev; } -static DeviceState *ppce500_init_mpic(MachineState *machine, - PPCE500Params *params, +static DeviceState *ppce500_init_mpic(PPCE500MachineState *pms, MemoryRegion *ccsr, qemu_irq **irqs) { + MachineState *machine = MACHINE(pms); + const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); DeviceState *dev = NULL; SysBusDevice *s; @@ -751,7 +751,7 @@ static DeviceState *ppce500_init_mpic(MachineState *machine, Error *err = NULL; if (machine_kernel_irqchip_allowed(machine)) { - dev = ppce500_init_mpic_kvm(params, irqs, &err); + dev = ppce500_init_mpic_kvm(pmc, irqs, &err); } if (machine_kernel_irqchip_required(machine) && !dev) { error_reportf_err(err, @@ -761,7 +761,7 @@ static DeviceState *ppce500_init_mpic(MachineState *machine, } if (!dev) { - dev = ppce500_init_mpic_qemu(params, irqs); + dev = ppce500_init_mpic_qemu(pms, irqs); } s = SYS_BUS_DEVICE(dev); @@ -778,10 +778,12 @@ static void ppce500_power_off(void *opaque, int line, int on) } } -void ppce500_init(MachineState *machine, PPCE500Params *params) +void ppce500_init(MachineState *machine) { MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); + PPCE500MachineState *pms = PPCE500_MACHINE(machine); + const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(machine); PCIBus *pci_bus; CPUPPCState *env = NULL; uint64_t loadaddr; @@ -835,8 +837,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; - env->mpic_iack = params->ccsrbar_base + - MPC8544_MPIC_REGS_OFFSET + 0xa0; + env->mpic_iack = pmc->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500); @@ -869,10 +870,10 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) qdev_init_nofail(dev); ccsr = CCSR(dev); ccsr_addr_space = &ccsr->ccsr_space; - memory_region_add_subregion(address_space_mem, params->ccsrbar_base, + memory_region_add_subregion(address_space_mem, pmc->ccsrbar_base, ccsr_addr_space); - mpicdev = ppce500_init_mpic(machine, params, ccsr_addr_space, irqs); + mpicdev = ppce500_init_mpic(pms, ccsr_addr_space, irqs); /* Serial */ if (serial_hd(0)) { @@ -898,7 +899,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) dev = qdev_create(NULL, "e500-pcihost"); object_property_add_child(qdev_get_machine(), "pci-host", OBJECT(dev), &error_abort); - qdev_prop_set_uint32(dev, "first_slot", params->pci_first_slot); + qdev_prop_set_uint32(dev, "first_slot", pmc->pci_first_slot); qdev_prop_set_uint32(dev, "first_pin_irq", pci_irq_nrs[0]); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); @@ -921,9 +922,9 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) } /* Register spinning region */ - sysbus_create_simple("e500-spin", params->spin_base, NULL); + sysbus_create_simple("e500-spin", pmc->spin_base, NULL); - if (params->has_mpc8xxx_gpio) { + if (pmc->has_mpc8xxx_gpio) { qemu_irq poweroff_irq; dev = qdev_create(NULL, "mpc8xxx_gpio"); @@ -939,21 +940,21 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) } /* Platform Bus Device */ - if (params->has_platform_bus) { + if (pmc->has_platform_bus) { dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE); dev->id = TYPE_PLATFORM_BUS_DEVICE; - qdev_prop_set_uint32(dev, "num_irqs", params->platform_bus_num_irqs); - qdev_prop_set_uint32(dev, "mmio_size", params->platform_bus_size); + qdev_prop_set_uint32(dev, "num_irqs", pmc->platform_bus_num_irqs); + qdev_prop_set_uint32(dev, "mmio_size", pmc->platform_bus_size); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); - for (i = 0; i < params->platform_bus_num_irqs; i++) { - int irqn = params->platform_bus_first_irq + i; + for (i = 0; i < pmc->platform_bus_num_irqs; i++) { + int irqn = pmc->platform_bus_first_irq + i; sysbus_connect_irq(s, i, qdev_get_gpio_in(mpicdev, irqn)); } memory_region_add_subregion(address_space_mem, - params->platform_bus_base, + pmc->platform_bus_base, sysbus_mmio_get_region(s, 0)); } @@ -1056,7 +1057,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) exit(1); } - dt_size = ppce500_prep_device_tree(machine, params, dt_base, + dt_size = ppce500_prep_device_tree(pms, dt_base, initrd_base, initrd_size, kernel_base, kernel_size); if (dt_size < 0) { @@ -1085,9 +1086,17 @@ static const TypeInfo e500_ccsr_info = { .instance_init = e500_ccsr_initfn, }; +static const TypeInfo ppce500_info = { + .name = TYPE_PPCE500_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .class_size = sizeof(PPCE500MachineClass), +}; + static void e500_register_types(void) { type_register_static(&e500_ccsr_info); + type_register_static(&ppce500_info); } type_init(e500_register_types) diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 70ba1d8f4f..621403bd24 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -3,12 +3,21 @@ #include "hw/boards.h" -typedef struct PPCE500Params { - int pci_first_slot; - int pci_nr_slots; +typedef struct PPCE500MachineState { + /*< private >*/ + MachineState parent_obj; + +} PPCE500MachineState; + +typedef struct PPCE500MachineClass { + /*< private >*/ + MachineClass parent_class; /* required -- must at least add toplevel board compatible */ - void (*fixup_devtree)(struct PPCE500Params *params, void *fdt); + void (*fixup_devtree)(void *fdt); + + int pci_first_slot; + int pci_nr_slots; int mpic_version; bool has_mpc8xxx_gpio; @@ -22,10 +31,18 @@ typedef struct PPCE500Params { hwaddr pci_mmio_base; hwaddr pci_mmio_bus_base; hwaddr spin_base; -} PPCE500Params; +} PPCE500MachineClass; -void ppce500_init(MachineState *machine, PPCE500Params *params); +void ppce500_init(MachineState *machine); hwaddr booke206_page_size_to_tlb(uint64_t size); +#define TYPE_PPCE500_MACHINE "ppce500-base-machine" +#define PPCE500_MACHINE(obj) \ + OBJECT_CHECK(PPCE500MachineState, (obj), TYPE_PPCE500_MACHINE) +#define PPCE500_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PPCE500MachineClass, obj, TYPE_PPCE500_MACHINE) +#define PPCE500_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PPCE500MachineClass, klass, TYPE_PPCE500_MACHINE) + #endif diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 81d03e1038..f69aadb666 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -21,7 +21,7 @@ #include "hw/ppc/openpic.h" #include "kvm_ppc.h" -static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) +static void e500plat_fixup_devtree(void *fdt) { const char model[] = "QEMU ppce500"; const char compatible[] = "fsl,qemu-e500"; @@ -33,40 +33,54 @@ static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) static void e500plat_init(MachineState *machine) { - PPCE500Params params = { - .pci_first_slot = 0x1, - .pci_nr_slots = PCI_SLOT_MAX - 1, - .fixup_devtree = e500plat_fixup_devtree, - .mpic_version = OPENPIC_MODEL_FSL_MPIC_42, - .has_mpc8xxx_gpio = true, - .has_platform_bus = true, - .platform_bus_base = 0xf00000000ULL, - .platform_bus_size = (128ULL * 1024 * 1024), - .platform_bus_first_irq = 5, - .platform_bus_num_irqs = 10, - .ccsrbar_base = 0xFE0000000ULL, - .pci_pio_base = 0xFE1000000ULL, - .pci_mmio_base = 0xC00000000ULL, - .pci_mmio_bus_base = 0xE0000000ULL, - .spin_base = 0xFEF000000ULL, - }; - + PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(machine); /* Older KVM versions don't support EPR which breaks guests when we announce MPIC variants that support EPR. Revert to an older one for those */ if (kvm_enabled() && !kvmppc_has_cap_epr()) { - params.mpic_version = OPENPIC_MODEL_FSL_MPIC_20; + pmc->mpic_version = OPENPIC_MODEL_FSL_MPIC_20; } - ppce500_init(machine, ¶ms); + ppce500_init(machine); } -static void e500plat_machine_init(MachineClass *mc) +#define TYPE_E500PLAT_MACHINE MACHINE_TYPE_NAME("ppce500") + +static void e500plat_machine_class_init(ObjectClass *oc, void *data) { + PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + pmc->pci_first_slot = 0x1; + pmc->pci_nr_slots = PCI_SLOT_MAX - 1; + pmc->fixup_devtree = e500plat_fixup_devtree; + pmc->mpic_version = OPENPIC_MODEL_FSL_MPIC_42; + pmc->has_mpc8xxx_gpio = true; + pmc->has_platform_bus = true; + pmc->platform_bus_base = 0xf00000000ULL; + pmc->platform_bus_size = (128ULL * 1024 * 1024); + pmc->platform_bus_first_irq = 5; + pmc->platform_bus_num_irqs = 10; + pmc->ccsrbar_base = 0xFE0000000ULL; + pmc->pci_pio_base = 0xFE1000000ULL; + pmc->pci_mmio_base = 0xC00000000ULL; + pmc->pci_mmio_bus_base = 0xE0000000ULL; + pmc->spin_base = 0xFEF000000ULL; + mc->desc = "generic paravirt e500 platform"; mc->init = e500plat_init; mc->max_cpus = 32; - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ETSEC_COMMON); mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); -} + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ETSEC_COMMON); + } -DEFINE_MACHINE("ppce500", e500plat_machine_init) +static const TypeInfo e500plat_info = { + .name = TYPE_E500PLAT_MACHINE, + .parent = TYPE_PPCE500_MACHINE, + .class_init = e500plat_machine_class_init, +}; + +static void e500plat_register_types(void) +{ + type_register_static(&e500plat_info); +} +type_init(e500plat_register_types) diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index 1717953ec7..ab30a2a99e 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -18,7 +18,7 @@ #include "qemu/error-report.h" #include "cpu.h" -static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) +static void mpc8544ds_fixup_devtree(void *fdt) { const char model[] = "MPC8544DS"; const char compatible[] = "MPC8544DS\0MPC85xxDS"; @@ -30,33 +30,46 @@ static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) static void mpc8544ds_init(MachineState *machine) { - PPCE500Params params = { - .pci_first_slot = 0x11, - .pci_nr_slots = 2, - .fixup_devtree = mpc8544ds_fixup_devtree, - .mpic_version = OPENPIC_MODEL_FSL_MPIC_20, - .ccsrbar_base = 0xE0000000ULL, - .pci_mmio_base = 0xC0000000ULL, - .pci_mmio_bus_base = 0xC0000000ULL, - .pci_pio_base = 0xE1000000ULL, - .spin_base = 0xEF000000ULL, - }; - if (machine->ram_size > 0xc0000000) { error_report("The MPC8544DS board only supports up to 3GB of RAM"); exit(1); } - ppce500_init(machine, ¶ms); + ppce500_init(machine); } - -static void ppce500_machine_init(MachineClass *mc) +static void e500plat_machine_class_init(ObjectClass *oc, void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); + + pmc->pci_first_slot = 0x11; + pmc->pci_nr_slots = 2; + pmc->fixup_devtree = mpc8544ds_fixup_devtree; + pmc->mpic_version = OPENPIC_MODEL_FSL_MPIC_20; + pmc->ccsrbar_base = 0xE0000000ULL; + pmc->pci_mmio_base = 0xC0000000ULL; + pmc->pci_mmio_bus_base = 0xC0000000ULL; + pmc->pci_pio_base = 0xE1000000ULL; + pmc->spin_base = 0xEF000000ULL; + mc->desc = "mpc8544ds"; mc->init = mpc8544ds_init; mc->max_cpus = 15; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); } -DEFINE_MACHINE("mpc8544ds", ppce500_machine_init) +#define TYPE_MPC8544DS_MACHINE MACHINE_TYPE_NAME("mpc8544ds") + +static const TypeInfo mpc8544ds_info = { + .name = TYPE_MPC8544DS_MACHINE, + .parent = TYPE_PPCE500_MACHINE, + .class_init = e500plat_machine_class_init, +}; + +static void mpc8544ds_register_types(void) +{ + type_register_static(&mpc8544ds_info); +} + +type_init(mpc8544ds_register_types) From a324d6f166970f8f6a82c61ffd2356fbda81c8f4 Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Thu, 19 Apr 2018 12:17:35 +0530 Subject: [PATCH 0114/2380] spapr: Support ibm,dynamic-memory-v2 property The new property ibm,dynamic-memory-v2 allows memory to be represented in a more compact manner in device tree. Signed-off-by: Bharata B Rao Signed-off-by: David Gibson --- docs/specs/ppc-spapr-hotplug.txt | 19 +++ hw/ppc/spapr.c | 232 ++++++++++++++++++++++++------- include/hw/ppc/spapr_ovec.h | 1 + 3 files changed, 203 insertions(+), 49 deletions(-) diff --git a/docs/specs/ppc-spapr-hotplug.txt b/docs/specs/ppc-spapr-hotplug.txt index f57e2a09c6..cc7833108e 100644 --- a/docs/specs/ppc-spapr-hotplug.txt +++ b/docs/specs/ppc-spapr-hotplug.txt @@ -387,4 +387,23 @@ Each LMB list entry consists of the following elements: - A 32bit flags word. The bit at bit position 0x00000008 defines whether the LMB is assigned to the the partition as of boot time. +ibm,dynamic-memory-v2 + +This property describes the dynamically reconfigurable memory. This is +an alternate and newer way to describe dyanamically reconfigurable memory. +It is a property encoded array that has an integer N (the number of +LMB set entries) followed by N LMB set entries. There is an LMB set entry +for each sequential group of LMBs that share common attributes. + +Each LMB set entry consists of the following elements: + +- Number of sequential LMBs in the entry represented by a 32bit integer. +- Logical address of the first LMB in the set encoded as a 64bit integer. +- DRC index of the first LMB in the set. +- Associativity list index that is used as an index into + ibm,associativity-lookup-arrays property described earlier. This + is used to retrieve the right associativity list to be used for all + the LMBs in this set. +- A 32bit flags word that applies to all the LMBs in the set. + [1] http://thread.gmane.org/gmane.linux.ports.ppc.embedded/75350/focus=106867 diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 2203b6f46d..b35aff5d81 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -668,63 +668,137 @@ static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr) return -1; } -/* - * Adds ibm,dynamic-reconfiguration-memory node. - * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation - * of this device tree node. - */ -static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) +struct sPAPRDrconfCellV2 { + uint32_t seq_lmbs; + uint64_t base_addr; + uint32_t drc_index; + uint32_t aa_index; + uint32_t flags; +} QEMU_PACKED; + +typedef struct DrconfCellQueue { + struct sPAPRDrconfCellV2 cell; + QSIMPLEQ_ENTRY(DrconfCellQueue) entry; +} DrconfCellQueue; + +static DrconfCellQueue * +spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr, + uint32_t drc_index, uint32_t aa_index, + uint32_t flags) { - MachineState *machine = MACHINE(spapr); - int ret, i, offset; + DrconfCellQueue *elem; + + elem = g_malloc0(sizeof(*elem)); + elem->cell.seq_lmbs = cpu_to_be32(seq_lmbs); + elem->cell.base_addr = cpu_to_be64(base_addr); + elem->cell.drc_index = cpu_to_be32(drc_index); + elem->cell.aa_index = cpu_to_be32(aa_index); + elem->cell.flags = cpu_to_be32(flags); + + return elem; +} + +/* ibm,dynamic-memory-v2 */ +static int spapr_populate_drmem_v2(sPAPRMachineState *spapr, void *fdt, + int offset, MemoryDeviceInfoList *dimms) +{ + uint8_t *int_buf, *cur_index, buf_len; + int ret; + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint64_t addr, cur_addr, size; + uint32_t nr_boot_lmbs = (spapr->hotplug_memory.base / lmb_size); + uint64_t mem_end = spapr->hotplug_memory.base + + memory_region_size(&spapr->hotplug_memory.mr); + uint32_t node, nr_entries = 0; + sPAPRDRConnector *drc; + DrconfCellQueue *elem, *next; + MemoryDeviceInfoList *info; + QSIMPLEQ_HEAD(, DrconfCellQueue) drconf_queue + = QSIMPLEQ_HEAD_INITIALIZER(drconf_queue); + + /* Entry to cover RAM and the gap area */ + elem = spapr_get_drconf_cell(nr_boot_lmbs, 0, 0, -1, + SPAPR_LMB_FLAGS_RESERVED | + SPAPR_LMB_FLAGS_DRC_INVALID); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + + cur_addr = spapr->hotplug_memory.base; + for (info = dimms; info; info = info->next) { + PCDIMMDeviceInfo *di = info->value->u.dimm.data; + + addr = di->addr; + size = di->size; + node = di->node; + + /* Entry for hot-pluggable area */ + if (cur_addr < addr) { + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size); + g_assert(drc); + elem = spapr_get_drconf_cell((addr - cur_addr) / lmb_size, + cur_addr, spapr_drc_index(drc), -1, 0); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + } + + /* Entry for DIMM */ + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr / lmb_size); + g_assert(drc); + elem = spapr_get_drconf_cell(size / lmb_size, addr, + spapr_drc_index(drc), node, + SPAPR_LMB_FLAGS_ASSIGNED); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + cur_addr = addr + size; + } + + /* Entry for remaining hotpluggable area */ + if (cur_addr < mem_end) { + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size); + g_assert(drc); + elem = spapr_get_drconf_cell((mem_end - cur_addr) / lmb_size, + cur_addr, spapr_drc_index(drc), -1, 0); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + } + + buf_len = nr_entries * sizeof(struct sPAPRDrconfCellV2) + sizeof(uint32_t); + int_buf = cur_index = g_malloc0(buf_len); + *(uint32_t *)int_buf = cpu_to_be32(nr_entries); + cur_index += sizeof(nr_entries); + + QSIMPLEQ_FOREACH_SAFE(elem, &drconf_queue, entry, next) { + memcpy(cur_index, &elem->cell, sizeof(elem->cell)); + cur_index += sizeof(elem->cell); + QSIMPLEQ_REMOVE(&drconf_queue, elem, DrconfCellQueue, entry); + g_free(elem); + } + + ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory-v2", int_buf, buf_len); + g_free(int_buf); + if (ret < 0) { + return -1; + } + return 0; +} + +/* ibm,dynamic-memory */ +static int spapr_populate_drmem_v1(sPAPRMachineState *spapr, void *fdt, + int offset, MemoryDeviceInfoList *dimms) +{ + int i, ret; uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; - uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)}; uint32_t hotplug_lmb_start = spapr->hotplug_memory.base / lmb_size; uint32_t nr_lmbs = (spapr->hotplug_memory.base + memory_region_size(&spapr->hotplug_memory.mr)) / lmb_size; uint32_t *int_buf, *cur_index, buf_len; - int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; - MemoryDeviceInfoList *dimms = NULL; - - /* - * Don't create the node if there is no hotpluggable memory - */ - if (machine->ram_size == machine->maxram_size) { - return 0; - } /* * Allocate enough buffer size to fit in ibm,dynamic-memory - * or ibm,associativity-lookup-arrays */ - buf_len = MAX(nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1, nr_nodes * 4 + 2) - * sizeof(uint32_t); + buf_len = (nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1) * sizeof(uint32_t); cur_index = int_buf = g_malloc0(buf_len); - - offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory"); - - ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size, - sizeof(prop_lmb_size)); - if (ret < 0) { - goto out; - } - - ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff); - if (ret < 0) { - goto out; - } - - ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0); - if (ret < 0) { - goto out; - } - - if (hotplug_lmb_start) { - dimms = qmp_pc_dimm_device_list(); - } - - /* ibm,dynamic-memory */ int_buf[0] = cpu_to_be32(nr_lmbs); cur_index++; for (i = 0; i < nr_lmbs; i++) { @@ -764,13 +838,71 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE; } - qapi_free_MemoryDeviceInfoList(dimms); ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory", int_buf, buf_len); + g_free(int_buf); if (ret < 0) { - goto out; + return -1; + } + return 0; +} + +/* + * Adds ibm,dynamic-reconfiguration-memory node. + * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation + * of this device tree node. + */ +static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) +{ + MachineState *machine = MACHINE(spapr); + int ret, i, offset; + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)}; + uint32_t *int_buf, *cur_index, buf_len; + int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; + MemoryDeviceInfoList *dimms = NULL; + + /* + * Don't create the node if there is no hotpluggable memory + */ + if (machine->ram_size == machine->maxram_size) { + return 0; + } + + offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory"); + + ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size, + sizeof(prop_lmb_size)); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0); + if (ret < 0) { + return ret; + } + + /* ibm,dynamic-memory or ibm,dynamic-memory-v2 */ + dimms = qmp_pc_dimm_device_list(); + if (spapr_ovec_test(spapr->ov5_cas, OV5_DRMEM_V2)) { + ret = spapr_populate_drmem_v2(spapr, fdt, offset, dimms); + } else { + ret = spapr_populate_drmem_v1(spapr, fdt, offset, dimms); + } + qapi_free_MemoryDeviceInfoList(dimms); + + if (ret < 0) { + return ret; } /* ibm,associativity-lookup-arrays */ + buf_len = (nr_nodes * 4 + 2) * sizeof(uint32_t); + cur_index = int_buf = g_malloc0(buf_len); + cur_index = int_buf; int_buf[0] = cpu_to_be32(nr_nodes); int_buf[1] = cpu_to_be32(4); /* Number of entries per associativity list */ @@ -787,8 +919,8 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) } ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf, (cur_index - int_buf) * sizeof(uint32_t)); -out: g_free(int_buf); + return ret; } @@ -2491,6 +2623,9 @@ static void spapr_machine_init(MachineState *machine) spapr_ovec_set(spapr->ov5, OV5_HPT_RESIZE); } + /* advertise support for ibm,dyamic-memory-v2 */ + spapr_ovec_set(spapr->ov5, OV5_DRMEM_V2); + /* init CPUs */ spapr_init_cpus(spapr); @@ -2918,7 +3053,6 @@ static void spapr_instance_init(Object *obj) " place of standard EPOW events when possible" " (required for memory hot-unplug support)", NULL); - ppc_compat_add_property(obj, "max-cpu-compat", &spapr->max_compat_pvr, "Maximum permitted CPU compatibility mode", &error_fatal); diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h index bf25e5d954..0f2d8d715d 100644 --- a/include/hw/ppc/spapr_ovec.h +++ b/include/hw/ppc/spapr_ovec.h @@ -51,6 +51,7 @@ typedef struct sPAPROptionVector sPAPROptionVector; #define OV5_FORM1_AFFINITY OV_BIT(5, 0) #define OV5_HP_EVT OV_BIT(6, 5) #define OV5_HPT_RESIZE OV_BIT(6, 7) +#define OV5_DRMEM_V2 OV_BIT(22, 0) #define OV5_XIVE_BOTH OV_BIT(23, 0) #define OV5_XIVE_EXPLOIT OV_BIT(23, 1) /* 1=exploitation 0=legacy */ From 4550f6a5da691fa45e801f391e947ff4236d1fa6 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 13 Apr 2018 14:54:34 +1000 Subject: [PATCH 0115/2380] target/ppc: Don't bother with MSR_EP in cpu_ppc_set_papr() cpu_ppc_set_papr() removes the EP and HV bits from the MSR mask. While removing the HV bit makes sense (a cpu in PAPR mode should never be emulated in hypervisor mode), the EP bit is just bizarre. Although it's true that a papr mode guest shouldn't be able to change the exception prefix, the MSR[EP] bit doesn't even exist on the cpus supported for PAPR mode, so it's pointless to do anything with it here. Signed-off-by: David Gibson Reviewed-by: Thomas Huth --- target/ppc/translate_init.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index bb79d23b50..808f6c1a08 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8874,12 +8874,11 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) cpu->vhyp = vhyp; - /* PAPR always has exception vectors in RAM not ROM. To ensure this, - * MSR[IP] should never be set. - * - * We also disallow setting of MSR_HV + /* + * With a virtual hypervisor mode we never allow the CPU to go + * hypervisor mode itself */ - env->msr_mask &= ~((1ull << MSR_EP) | MSR_HVB); + env->msr_mask &= ~MSR_HVB; /* Set emulated LPCR to not send interrupts to hypervisor. Note that * under KVM, the actual HW LPCR will be set differently by KVM itself, From 88f42c6773c0c09f5c38d5eb0cd6e8b7aed4dfeb Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 5 Apr 2018 15:49:23 +1000 Subject: [PATCH 0116/2380] spapr: Set compatibility mode before the rest of spapr_cpu_reset() Although the order doesn't really matter at the moment, it's possible other initializastions could depend on the compatiblity mode, so make sure we set it first in spapr_cpu_reset(). While we're at it drop the test against first_cpu. Setting the compat mode to the value it already has is redundant, but harmless, so we might as well make a small simplification to the code. Signed-off-by: David Gibson Reviewed-by: Greg Kurz --- hw/ppc/spapr_cpu_core.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 94afeb399e..01dbc69424 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -31,6 +31,11 @@ static void spapr_cpu_reset(void *opaque) cpu_reset(cs); + /* Set compatibility mode to match the boot CPU, which was either set + * by the machine reset code or by CAS. This should never fail. + */ + ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort); + /* All CPUs start halted. CPU0 is unhalted from the machine level * reset code and the rest are explicitly started up by the guest * using an RTAS call */ @@ -45,12 +50,6 @@ static void spapr_cpu_reset(void *opaque) env->spr[SPR_LPCR] &= ~pcc->lpcr_pm; } - /* Set compatibility mode to match the boot CPU, which was either set - * by the machine reset code or by CAS. This should never fail. - */ - if (cs != first_cpu) { - ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort); - } } static void spapr_cpu_destroy(PowerPCCPU *cpu) From 6233b679cae8741890f981c9dd6570d47715141e Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 19 Apr 2018 17:19:11 +1000 Subject: [PATCH 0117/2380] Clear mem_path if we fall back to anonymous RAM allocation If the -mem-path option is set, we attempt to map the guest's RAM from a file in the given path; it's usually used to back guest RAM with hugepages. If we're unable to (e.g. not enough free hugepages) then we fall back to allocating normal anonymous pages. This behaviour can be surprising, but a comment in allocate_system_memory_nonnuma() suggests it's legacy behaviour we can't change. What really isn't ok, though, is that in this case we leave mem_path set. That means functions which attempt to determine the pagesize of main RAM can erroneously think it is hugepage based on the requested path, even though it's not. This is particular bad for the pseries machine type. KVM HV limitations mean the guest can't use pagesizes larger than the host page size used to back RAM. That means that such a fallback, rather than merely giving poorer performance than expected will cause the guest to freeze up early in boot as it attempts to use large page mappings that can't work. This patch addresses the problem by clearing the mem_path variable when we fall back to anonymous pages, meaning that subsequent attempts to determine the RAM page size will get an accurate result. Signed-off-by: David Gibson --- numa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/numa.c b/numa.c index 1116c90af9..78a869e598 100644 --- a/numa.c +++ b/numa.c @@ -469,6 +469,7 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner, /* Legacy behavior: if allocation failed, fall back to * regular RAM allocation. */ + mem_path = NULL; memory_region_init_ram_nomigrate(mr, owner, name, ram_size, &error_fatal); } #else From 0ee86bb6c5beb6498488850104f7557c376d0bef Mon Sep 17 00:00:00 2001 From: Jason Andryuk Date: Fri, 20 Apr 2018 14:32:19 -0400 Subject: [PATCH 0118/2380] ccid: Fix dwProtocols advertisement of T=0 Commit d7d218ef02d87c637d20d64da8f575d434ff6f78 attempted to change dwProtocols to only advertise support for T=0 and not T=1. The change was incorrect as it changed 0x00000003 to 0x00010000. lsusb -v in a linux guest shows: "dwProtocols 65536 (Invalid values detected)", though the smart card could still be accessed. Windows 7 does not detect inserted smart cards and logs the the following Error in the Event Logs: Source: Smart Card Service Event ID: 610 Smart Card Reader 'QEMU QEMU USB CCID 0' rejected IOCTL SET_PROTOCOL: Incorrect function. If this error persists, your smart card or reader may not be functioning correctly Command Header: 03 00 00 00 Setting to 0x00000001 fixes the Windows issue. Signed-off-by: Jason Andryuk Message-id: 20180420183219.20722-1-jandryuk@gmail.com Cc: qemu-stable@nongnu.org Signed-off-by: Gerd Hoffmann --- hw/usb/dev-smartcard-reader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index e6468057a0..cabb564788 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -329,8 +329,8 @@ static const uint8_t qemu_ccid_descriptor[] = { */ 0x07, /* u8 bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */ - 0x00, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/ - 0x01, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */ + 0x01, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/ + 0x00, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */ /* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */ 0xa0, 0x0f, 0x00, 0x00, /* u32 dwMaximumClock; */ From 9d8fa0df49af16a208fa961c2968fba4daffcc07 Mon Sep 17 00:00:00 2001 From: John Thomson Date: Thu, 5 Apr 2018 23:20:46 +1000 Subject: [PATCH 0119/2380] Fix libusb-1.0.22 deprecated libusb_set_debug with libusb_set_option libusb-1.0.22 marked libusb_set_debug deprecated it is replaced with libusb_set_option(libusb_context, LIBUSB_OPTION_LOG_LEVEL, libusb_log_level); details here: https://github.com/libusb/libusb/commit/539f22e2fd916558d11ab9a66f10f461c5593168 Warning here: CC hw/usb/host-libusb.o /builds/xen/src/qemu-xen/hw/usb/host-libusb.c: In function 'usb_host_init': /builds/xen/src/qemu-xen/hw/usb/host-libusb.c:250:5: error: 'libusb_set_debug' is deprecated: Use libusb_set_option instead [-Werror=deprecated-declarations] libusb_set_debug(ctx, loglevel); ^~~~~~~~~~~~~~~~ In file included from /builds/xen/src/qemu-xen/hw/usb/host-libusb.c:40:0: /usr/include/libusb-1.0/libusb.h:1300:18: note: declared here void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); ^~~~~~~~~~~~~~~~ cc1: all warnings being treated as errors make: *** [/builds/xen/src/qemu-xen/rules.mak:66: hw/usb/host-libusb.o] Error 1 make: Leaving directory '/builds/xen/src/xen/tools/qemu-xen-build' Signed-off-by: John Thomson Message-id: 20180405132046.4968-1-git@johnthomson.fastmail.com.au Signed-off-by: Gerd Hoffmann --- hw/usb/host-libusb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 1b0be071cc..dc0a8fe295 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -247,7 +247,11 @@ static int usb_host_init(void) if (rc != 0) { return -1; } +#if LIBUSB_API_VERSION >= 0x01000106 + libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, loglevel); +#else libusb_set_debug(ctx, loglevel); +#endif #ifdef CONFIG_WIN32 /* FIXME: add support for Windows. */ #else From 0f5c642d49c3a843bd8b8790d7014fd84bcf6405 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Tue, 3 Apr 2018 12:34:37 +0200 Subject: [PATCH 0120/2380] ccid-card: include libcacard.h only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When trying to build with latest libcacard-2.5.1, I hit the following error: In file included from hw/usb/ccid-card-passthru.c:12:0: /usr/include/cacard/vscard_common.h:26:2: error: #warning "Only can be included directly" [-Werror=cpp] #warning "Only can be included directly" While it was fixed in libcacard upstream (so that individual files can be included directly), it doesn't make much sense. Let's switch to including the main libcacard.h and also require at least libcacard-2.5.1 which introduced it. It's available since late 2015. Signed-off-by: Michal Privoznik Reviewed-by: Marc-André Lureau Message-id: 3c36db1dc0702763ebb7966cc27428ed67d43804.1522751624.git.mprivozn@redhat.com [ kraxel: fix include path ] Signed-off-by: Gerd Hoffmann --- configure | 2 +- hw/usb/ccid-card-emulated.c | 5 +---- hw/usb/ccid-card-passthru.c | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/configure b/configure index 0a19b033bc..0a4f0da58f 100755 --- a/configure +++ b/configure @@ -4449,7 +4449,7 @@ fi # check for smartcard support if test "$smartcard" != "no"; then - if $pkg_config libcacard; then + if $pkg_config --atleast-version=2.5.1 libcacard; then libcacard_cflags=$($pkg_config --cflags libcacard) libcacard_libs=$($pkg_config --libs libcacard) smartcard="yes" diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index ea42e4681d..5c8b3c9907 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -27,10 +27,7 @@ */ #include "qemu/osdep.h" -#include -#include -#include -#include +#include #include "qemu/thread.h" #include "qemu/main-loop.h" diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index b7dd3602dc..7684db0cb3 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -9,7 +9,7 @@ */ #include "qemu/osdep.h" -#include +#include #include "chardev/char-fe.h" #include "qemu/error-report.h" #include "qemu/sockets.h" From 4867e47cb637c6f3549786f1be70793112f96713 Mon Sep 17 00:00:00 2001 From: Elie Tournier Date: Fri, 13 Apr 2018 14:58:42 +0100 Subject: [PATCH 0121/2380] sdl: Allow OpenGL ES context creation Signed-off-by: Elie Tournier Message-id: 20180413135842.21325-4-tournier.elie@gmail.com [ kraxel: fix indent ] Signed-off-by: Gerd Hoffmann --- qemu-options.hx | 2 +- ui/sdl2-gl.c | 19 +++++++++++++++++-- vl.c | 4 ++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index ca4e412f2f..333dd1f1c8 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1240,7 +1240,7 @@ ETEXI DEF("display", HAS_ARG, QEMU_OPTION_display, "-display sdl[,frame=on|off][,alt_grab=on|off][,ctrl_grab=on|off]\n" - " [,window_close=on|off][,gl=on|off]\n" + " [,window_close=on|off][,gl=on|core|es|off]\n" "-display gtk[,grab_on_hover=on|off][,gl=on|off]|\n" "-display vnc=[,]\n" "-display curses\n" diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index c3683e6b65..83b71853d1 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -140,12 +140,27 @@ QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl, SDL_GL_MakeCurrent(scon->real_window, scon->winctx); SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, - SDL_GL_CONTEXT_PROFILE_CORE); + if (scon->opts->gl == DISPLAYGL_MODE_ON || + scon->opts->gl == DISPLAYGL_MODE_CORE) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_CORE); + } else if (scon->opts->gl == DISPLAYGL_MODE_ES) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_ES); + } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, params->major_ver); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, params->minor_ver); ctx = SDL_GL_CreateContext(scon->real_window); + + /* If SDL fail to create a GL context and we use the "on" flag, + * then try to fallback to GLES. + */ + if (!ctx && scon->opts->gl == DISPLAYGL_MODE_ON) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_ES); + ctx = SDL_GL_CreateContext(scon->real_window); + } return (QEMUGLContext)ctx; } diff --git a/vl.c b/vl.c index f7804f7a18..916d2a80a7 100644 --- a/vl.c +++ b/vl.c @@ -2144,6 +2144,10 @@ static void parse_display(const char *p) dpy.has_gl = true; if (strstart(opts, "on", &nextopt)) { dpy.gl = DISPLAYGL_MODE_ON; + } else if (strstart(opts, "core", &nextopt)) { + dpy.gl = DISPLAYGL_MODE_CORE; + } else if (strstart(opts, "es", &nextopt)) { + dpy.gl = DISPLAYGL_MODE_ES; } else if (strstart(opts, "off", &nextopt)) { dpy.gl = DISPLAYGL_MODE_OFF; } else { From 7cd0afe69f3330a104b1462c01156dd8525b9bdd Mon Sep 17 00:00:00 2001 From: Tina Zhang Date: Fri, 27 Apr 2018 17:11:05 +0800 Subject: [PATCH 0122/2380] console: introduce dpy_gfx_update_full dpy_gfx_update_full is used to do the whole display surface update. This function is proposed by Gerd Hoffmann. Signed-off-by: Tina Zhang Message-id: 1524820266-27079-2-git-send-email-tina.zhang@intel.com Signed-off-by: Gerd Hoffmann --- include/ui/console.h | 1 + ui/console.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/include/ui/console.h b/include/ui/console.h index 37a8d68d29..981b519dde 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -291,6 +291,7 @@ bool dpy_ui_info_supported(QemuConsole *con); int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info); void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h); +void dpy_gfx_update_full(QemuConsole *con); void dpy_gfx_replace_surface(QemuConsole *con, DisplaySurface *surface); void dpy_text_cursor(QemuConsole *con, int x, int y); diff --git a/ui/console.c b/ui/console.c index 3fb2f4e09f..b02510cdca 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1574,6 +1574,16 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) } } +void dpy_gfx_update_full(QemuConsole *con) +{ + if (!con->surface) { + return; + } + dpy_gfx_update(con, 0, 0, + surface_width(con->surface), + surface_height(con->surface)); +} + void dpy_gfx_replace_surface(QemuConsole *con, DisplaySurface *surface) { From 8983e3e35033ecb9234725c2bba65f020824969b Mon Sep 17 00:00:00 2001 From: Tina Zhang Date: Fri, 27 Apr 2018 17:11:06 +0800 Subject: [PATCH 0123/2380] ui: introduce vfio_display_reset During guest OS reboot, guest framebuffer is invalid. It will cause bugs, if the invalid guest framebuffer is still used by host. This patch is to introduce vfio_display_reset which is invoked during vfio display reset. This vfio_display_reset function is used to release the invalid display resource, disable scanout mode and replace the invalid surface with QemuConsole's DisplaySurafce. This patch can fix the GPU hang issue caused by gd_egl_draw during guest OS reboot. Changes v3->v4: - Move dma-buf based display check into the vfio_display_reset(). (Gerd) Changes v2->v3: - Limit vfio_display_reset to dma-buf based vfio display. (Gerd) Changes v1->v2: - Use dpy_gfx_update_full() update screen after reset. (Gerd) - Remove dpy_gfx_switch_surface(). (Gerd) Signed-off-by: Tina Zhang Message-id: 1524820266-27079-3-git-send-email-tina.zhang@intel.com Signed-off-by: Gerd Hoffmann --- hw/vfio/display.c | 11 +++++++++++ hw/vfio/pci.c | 4 ++++ hw/vfio/pci.h | 1 + 3 files changed, 16 insertions(+) diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 7d727ce910..59c0e5d1d7 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -198,6 +198,17 @@ static void vfio_display_dmabuf_exit(VFIODisplay *dpy) } /* ---------------------------------------------------------------------- */ +void vfio_display_reset(VFIOPCIDevice *vdev) +{ + if (!vdev || !vdev->dpy || !vdev->dpy->con || + !vdev->dpy->dmabuf.primary) { + return; + } + + dpy_gl_scanout_disable(vdev->dpy->con); + vfio_display_dmabuf_exit(vdev->dpy); + dpy_gfx_update_full(vdev->dpy->con); +} static void vfio_display_region_update(void *opaque) { diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b9bc6cd310..4947fe39a2 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3103,6 +3103,10 @@ static void vfio_pci_reset(DeviceState *dev) vfio_pci_pre_reset(vdev); + if (vdev->display != ON_OFF_AUTO_OFF) { + vfio_display_reset(vdev); + } + if (vdev->resetfn && !vdev->resetfn(vdev)) { goto post_reset; } diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 629c875701..59ab7757a3 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -176,6 +176,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, struct vfio_region_info *info, Error **errp); +void vfio_display_reset(VFIOPCIDevice *vdev); int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); void vfio_display_finalize(VFIOPCIDevice *vdev); From b02ebad1dc3132672a2a1ade2997c78441947e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 25 Apr 2018 11:02:18 +0100 Subject: [PATCH 0124/2380] linux-user: set minimum uname for RISC-V MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As support for RISC-V was only merged into the mainline kernel at 4.15 it is unlikely that glibc will be happy with a reported kernel version of 3.8.0. Indeed when I testing binaries created by the current Debian Sid compiler the tests failed with: FATAL: kernel too old Bump the version to the minimum a RISC-V glibc would expect: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/riscv/configure.ac Signed-off-by: Alex Bennée Reviewed-by: Palmer Dabbelt Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180425100218.24785-1-alex.bennee@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/riscv/target_syscall.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/riscv/target_syscall.h b/linux-user/riscv/target_syscall.h index d4e109a27f..ee81d8bc88 100644 --- a/linux-user/riscv/target_syscall.h +++ b/linux-user/riscv/target_syscall.h @@ -45,7 +45,7 @@ struct target_pt_regs { #else #define UNAME_MACHINE "riscv64" #endif -#define UNAME_MINIMUM_RELEASE "3.8.0" +#define UNAME_MINIMUM_RELEASE "4.15.0" #define TARGET_MINSIGSTKSZ 2048 #define TARGET_MLOCKALL_MCL_CURRENT 1 From 2b3f64cbf3e00f5042d120b4c23ed66078431f8c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 19 Apr 2018 13:57:40 +0100 Subject: [PATCH 0125/2380] linux-user: Fix getdents emulation for 64 bit guest on 32 bit host Currently we mishandle emulation of the getdents syscall for the case of a 64 bit guest on a 32 bit host -- it defaults into the 'host and guest same size' codepath and generates incorrect structures in the guest buffer. We can't easily handle the 64-on-32 case using the host getdents syscall, because the guest struct dirent is bigger than the host struct dirent, and we might find the host syscall has handed us back more records than we can fit in the guest buffer after conversion. Instead, always emulate 64-on-32 getdents with the host getdents64. This avoids the buffer-overrun problem because a dirent64 struct is always the same size on any host and always larger than any architecture's dirent struct. Reported-by: Henry Wertz Signed-off-by: Peter Maydell Message-Id: <20180419125740.2695-1-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 643b8833de..404be44ad5 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -259,10 +259,22 @@ static int gettid(void) { return -ENOSYS; } #endif -#if defined(TARGET_NR_getdents) && defined(__NR_getdents) + +/* For the 64-bit guest on 32-bit host case we must emulate + * getdents using getdents64, because otherwise the host + * might hand us back more dirent records than we can fit + * into the guest buffer after structure format conversion. + * Otherwise we emulate getdents with getdents if the host has it. + */ +#if defined(__NR_getdents) && HOST_LONG_BITS >= TARGET_ABI_BITS +#define EMULATE_GETDENTS_WITH_GETDENTS +#endif + +#if defined(TARGET_NR_getdents) && defined(EMULATE_GETDENTS_WITH_GETDENTS) _syscall3(int, sys_getdents, uint, fd, struct linux_dirent *, dirp, uint, count); #endif -#if !defined(__NR_getdents) || \ +#if (defined(TARGET_NR_getdents) && \ + !defined(EMULATE_GETDENTS_WITH_GETDENTS)) || \ (defined(TARGET_NR_getdents64) && defined(__NR_getdents64)) _syscall3(int, sys_getdents64, uint, fd, struct linux_dirent64 *, dirp, uint, count); #endif @@ -10163,7 +10175,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_getdents case TARGET_NR_getdents: -#ifdef __NR_getdents +#ifdef EMULATE_GETDENTS_WITH_GETDENTS #if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64 { struct target_dirent *target_dirp; From befb7447a08744ea74ed1a73a03541762d9016aa Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:16 +0200 Subject: [PATCH 0126/2380] linux-user: create a dummy per arch signal.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a signal-common.h for future use by these new files and use it in the existing signal.c Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-2-laurent@vivier.eu> --- linux-user/Makefile.objs | 2 +- linux-user/aarch64/signal.c | 18 ++++++++++++ linux-user/alpha/signal.c | 18 ++++++++++++ linux-user/arm/signal.c | 18 ++++++++++++ linux-user/cris/signal.c | 18 ++++++++++++ linux-user/hppa/signal.c | 18 ++++++++++++ linux-user/i386/signal.c | 18 ++++++++++++ linux-user/m68k/signal.c | 18 ++++++++++++ linux-user/microblaze/signal.c | 18 ++++++++++++ linux-user/mips/signal.c | 18 ++++++++++++ linux-user/mips64/signal.c | 18 ++++++++++++ linux-user/nios2/signal.c | 18 ++++++++++++ linux-user/openrisc/signal.c | 18 ++++++++++++ linux-user/ppc/signal.c | 18 ++++++++++++ linux-user/riscv/signal.c | 18 ++++++++++++ linux-user/s390x/signal.c | 18 ++++++++++++ linux-user/sh4/signal.c | 18 ++++++++++++ linux-user/signal-common.h | 50 ++++++++++++++++++++++++++++++++++ linux-user/signal.c | 41 ++++++++-------------------- linux-user/sparc/signal.c | 18 ++++++++++++ linux-user/sparc64/signal.c | 18 ++++++++++++ linux-user/tilegx/signal.c | 18 ++++++++++++ linux-user/x86_64/signal.c | 18 ++++++++++++ linux-user/xtensa/signal.c | 18 ++++++++++++ 24 files changed, 440 insertions(+), 31 deletions(-) create mode 100644 linux-user/aarch64/signal.c create mode 100644 linux-user/alpha/signal.c create mode 100644 linux-user/arm/signal.c create mode 100644 linux-user/cris/signal.c create mode 100644 linux-user/hppa/signal.c create mode 100644 linux-user/i386/signal.c create mode 100644 linux-user/m68k/signal.c create mode 100644 linux-user/microblaze/signal.c create mode 100644 linux-user/mips/signal.c create mode 100644 linux-user/mips64/signal.c create mode 100644 linux-user/nios2/signal.c create mode 100644 linux-user/openrisc/signal.c create mode 100644 linux-user/ppc/signal.c create mode 100644 linux-user/riscv/signal.c create mode 100644 linux-user/s390x/signal.c create mode 100644 linux-user/sh4/signal.c create mode 100644 linux-user/signal-common.h create mode 100644 linux-user/sparc/signal.c create mode 100644 linux-user/sparc64/signal.c create mode 100644 linux-user/tilegx/signal.c create mode 100644 linux-user/x86_64/signal.c create mode 100644 linux-user/xtensa/signal.c diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs index 8c93058100..811a7f5ce5 100644 --- a/linux-user/Makefile.objs +++ b/linux-user/Makefile.objs @@ -1,6 +1,6 @@ obj-y = main.o syscall.o strace.o mmap.o signal.o \ elfload.o linuxload.o uaccess.o uname.o \ - safe-syscall.o + safe-syscall.o $(TARGET_ABI_DIR)/signal.o obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_I386) += vm86.o diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/aarch64/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/alpha/signal.c b/linux-user/alpha/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/alpha/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/arm/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/cris/signal.c b/linux-user/cris/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/cris/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/hppa/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/i386/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/m68k/signal.c b/linux-user/m68k/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/m68k/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/microblaze/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/mips/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/mips64/signal.c b/linux-user/mips64/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/mips64/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/nios2/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/openrisc/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/ppc/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/riscv/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/s390x/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/sh4/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h new file mode 100644 index 0000000000..838b6e9198 --- /dev/null +++ b/linux-user/signal-common.h @@ -0,0 +1,50 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ + +#ifndef SIGNAL_COMMON_H +#define SIGNAL_COMMON_H +extern struct target_sigaltstack target_sigaltstack_used; + +static inline int on_sig_stack(unsigned long sp) +{ + return (sp - target_sigaltstack_used.ss_sp + < target_sigaltstack_used.ss_size); +} + +static inline int sas_ss_flags(unsigned long sp) +{ + return (target_sigaltstack_used.ss_size == 0 ? SS_DISABLE + : on_sig_stack(sp) ? SS_ONSTACK : 0); +} + +static inline void target_sigemptyset(target_sigset_t *set) +{ + memset(set, 0, sizeof(*set)); +} + +void host_to_target_sigset_internal(target_sigset_t *d, + const sigset_t *s); +void target_to_host_sigset_internal(sigset_t *d, + const target_sigset_t *s); +void tswap_siginfo(target_siginfo_t *tinfo, + const target_siginfo_t *info); +void set_sigmask(const sigset_t *set); +void force_sig(int sig); +void force_sigsegv(int oldsig); +#endif diff --git a/linux-user/signal.c b/linux-user/signal.c index b283270391..0f956740ef 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -25,8 +25,9 @@ #include "qemu-common.h" #include "target_signal.h" #include "trace.h" +#include "signal-common.h" -static struct target_sigaltstack target_sigaltstack_used = { +struct target_sigaltstack target_sigaltstack_used = { .ss_sp = 0, .ss_size = 0, .ss_flags = TARGET_SS_DISABLE, @@ -82,18 +83,6 @@ static uint8_t host_to_target_signal_table[_NSIG] = { }; static uint8_t target_to_host_signal_table[_NSIG]; -static inline int on_sig_stack(unsigned long sp) -{ - return (sp - target_sigaltstack_used.ss_sp - < target_sigaltstack_used.ss_size); -} - -static inline int sas_ss_flags(unsigned long sp) -{ - return (target_sigaltstack_used.ss_size == 0 ? SS_DISABLE - : on_sig_stack(sp) ? SS_ONSTACK : 0); -} - int host_to_target_signal(int sig) { if (sig < 0 || sig >= _NSIG) @@ -108,11 +97,6 @@ int target_to_host_signal(int sig) return target_to_host_signal_table[sig]; } -static inline void target_sigemptyset(target_sigset_t *set) -{ - memset(set, 0, sizeof(*set)); -} - static inline void target_sigaddset(target_sigset_t *set, int signum) { signum--; @@ -127,8 +111,8 @@ static inline int target_sigismember(const target_sigset_t *set, int signum) return ((set->sig[signum / TARGET_NSIG_BPW] & mask) != 0); } -static void host_to_target_sigset_internal(target_sigset_t *d, - const sigset_t *s) +void host_to_target_sigset_internal(target_sigset_t *d, + const sigset_t *s) { int i; target_sigemptyset(d); @@ -149,8 +133,8 @@ void host_to_target_sigset(target_sigset_t *d, const sigset_t *s) d->sig[i] = tswapal(d1.sig[i]); } -static void target_to_host_sigset_internal(sigset_t *d, - const target_sigset_t *s) +void target_to_host_sigset_internal(sigset_t *d, + const target_sigset_t *s) { int i; sigemptyset(d); @@ -257,7 +241,7 @@ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) /* Just set the guest's signal mask to the specified value; the * caller is assumed to have called block_signals() already. */ -static void set_sigmask(const sigset_t *set) +void set_sigmask(const sigset_t *set) { TaskState *ts = (TaskState *)thread_cpu->opaque; @@ -344,8 +328,8 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, tinfo->si_code = deposit32(si_code, 16, 16, si_type); } -static void tswap_siginfo(target_siginfo_t *tinfo, - const target_siginfo_t *info) +void tswap_siginfo(target_siginfo_t *tinfo, + const target_siginfo_t *info) { int si_type = extract32(info->si_code, 16, 16); int si_code = sextract32(info->si_code, 0, 16); @@ -515,7 +499,7 @@ void signal_init(void) * also forces the signal to "not blocked, not ignored", but for QEMU * that work is done in process_pending_signals(). */ -static void force_sig(int sig) +void force_sig(int sig) { CPUState *cpu = thread_cpu; CPUArchState *env = cpu->env_ptr; @@ -534,7 +518,7 @@ static void force_sig(int sig) * at the point of failure. */ #if !defined(TARGET_RISCV) -static void force_sigsegv(int oldsig) +void force_sigsegv(int oldsig) { if (oldsig == SIGSEGV) { /* Make sure we don't try to deliver the signal again; this will @@ -7301,9 +7285,6 @@ badframe: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } - -#else -#error Target needs to add support for signal handling #endif static void handle_pending_signal(CPUArchState *cpu_env, int sig, diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/sparc/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/sparc64/signal.c b/linux-user/sparc64/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/sparc64/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/tilegx/signal.c b/linux-user/tilegx/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/tilegx/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/x86_64/signal.c b/linux-user/x86_64/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/x86_64/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c new file mode 100644 index 0000000000..02ca338b6c --- /dev/null +++ b/linux-user/xtensa/signal.c @@ -0,0 +1,18 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ From f0352f13fec910172f9068b909cc4b62abf2080b Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:17 +0200 Subject: [PATCH 0127/2380] linux-user: move aarch64 signal.c parts to aarch64 directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to aarch64/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-3-laurent@vivier.eu> --- linux-user/aarch64/signal.c | 568 +++++++++++++++++++++++++++++ linux-user/aarch64/target_signal.h | 5 + linux-user/signal.c | 567 +--------------------------- 3 files changed, 574 insertions(+), 566 deletions(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index 02ca338b6c..d90e10a113 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -16,3 +16,571 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + uint64_t fault_address; + /* AArch64 registers */ + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; + /* 4K reserved for FP/SIMD state and future expansion */ + char __reserved[4096] __attribute__((__aligned__(16))); +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + target_sigset_t tuc_sigmask; + /* glibc uses a 1024-bit sigset_t */ + char __unused[1024 / 8 - sizeof(target_sigset_t)]; + /* last for future expansion */ + struct target_sigcontext tuc_mcontext; +}; + +/* + * Header to be used at the beginning of structures extending the user + * context. Such structures must be placed after the rt_sigframe on the stack + * and be 16-byte aligned. The last structure must be a dummy one with the + * magic and size set to 0. + */ +struct target_aarch64_ctx { + uint32_t magic; + uint32_t size; +}; + +#define TARGET_FPSIMD_MAGIC 0x46508001 + +struct target_fpsimd_context { + struct target_aarch64_ctx head; + uint32_t fpsr; + uint32_t fpcr; + uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */ +}; + +#define TARGET_EXTRA_MAGIC 0x45585401 + +struct target_extra_context { + struct target_aarch64_ctx head; + uint64_t datap; /* 16-byte aligned pointer to extra space cast to __u64 */ + uint32_t size; /* size in bytes of the extra space */ + uint32_t reserved[3]; +}; + +#define TARGET_SVE_MAGIC 0x53564501 + +struct target_sve_context { + struct target_aarch64_ctx head; + uint16_t vl; + uint16_t reserved[3]; + /* The actual SVE data immediately follows. It is layed out + * according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of + * the original struct pointer. + */ +}; + +#define TARGET_SVE_VQ_BYTES 16 + +#define TARGET_SVE_SIG_ZREG_SIZE(VQ) ((VQ) * TARGET_SVE_VQ_BYTES) +#define TARGET_SVE_SIG_PREG_SIZE(VQ) ((VQ) * (TARGET_SVE_VQ_BYTES / 8)) + +#define TARGET_SVE_SIG_REGS_OFFSET \ + QEMU_ALIGN_UP(sizeof(struct target_sve_context), TARGET_SVE_VQ_BYTES) +#define TARGET_SVE_SIG_ZREG_OFFSET(VQ, N) \ + (TARGET_SVE_SIG_REGS_OFFSET + TARGET_SVE_SIG_ZREG_SIZE(VQ) * (N)) +#define TARGET_SVE_SIG_PREG_OFFSET(VQ, N) \ + (TARGET_SVE_SIG_ZREG_OFFSET(VQ, 32) + TARGET_SVE_SIG_PREG_SIZE(VQ) * (N)) +#define TARGET_SVE_SIG_FFR_OFFSET(VQ) \ + (TARGET_SVE_SIG_PREG_OFFSET(VQ, 16)) +#define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \ + (TARGET_SVE_SIG_PREG_OFFSET(VQ, 17)) + +struct target_rt_sigframe { + struct target_siginfo info; + struct target_ucontext uc; +}; + +struct target_rt_frame_record { + uint64_t fp; + uint64_t lr; + uint32_t tramp[2]; +}; + +static void target_setup_general_frame(struct target_rt_sigframe *sf, + CPUARMState *env, target_sigset_t *set) +{ + int i; + + __put_user(0, &sf->uc.tuc_flags); + __put_user(0, &sf->uc.tuc_link); + + __put_user(target_sigaltstack_used.ss_sp, &sf->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(env->xregs[31]), &sf->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, &sf->uc.tuc_stack.ss_size); + + for (i = 0; i < 31; i++) { + __put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); + } + __put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); + __put_user(env->pc, &sf->uc.tuc_mcontext.pc); + __put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate); + + __put_user(env->exception.vaddress, &sf->uc.tuc_mcontext.fault_address); + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]); + } +} + +static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd, + CPUARMState *env) +{ + int i; + + __put_user(TARGET_FPSIMD_MAGIC, &fpsimd->head.magic); + __put_user(sizeof(struct target_fpsimd_context), &fpsimd->head.size); + __put_user(vfp_get_fpsr(env), &fpsimd->fpsr); + __put_user(vfp_get_fpcr(env), &fpsimd->fpcr); + + for (i = 0; i < 32; i++) { + uint64_t *q = aa64_vfp_qreg(env, i); +#ifdef TARGET_WORDS_BIGENDIAN + __put_user(q[0], &fpsimd->vregs[i * 2 + 1]); + __put_user(q[1], &fpsimd->vregs[i * 2]); +#else + __put_user(q[0], &fpsimd->vregs[i * 2]); + __put_user(q[1], &fpsimd->vregs[i * 2 + 1]); +#endif + } +} + +static void target_setup_extra_record(struct target_extra_context *extra, + uint64_t datap, uint32_t extra_size) +{ + __put_user(TARGET_EXTRA_MAGIC, &extra->head.magic); + __put_user(sizeof(struct target_extra_context), &extra->head.size); + __put_user(datap, &extra->datap); + __put_user(extra_size, &extra->size); +} + +static void target_setup_end_record(struct target_aarch64_ctx *end) +{ + __put_user(0, &end->magic); + __put_user(0, &end->size); +} + +static void target_setup_sve_record(struct target_sve_context *sve, + CPUARMState *env, int vq, int size) +{ + int i, j; + + __put_user(TARGET_SVE_MAGIC, &sve->head.magic); + __put_user(size, &sve->head.size); + __put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl); + + /* Note that SVE regs are stored as a byte stream, with each byte element + * at a subsequent address. This corresponds to a little-endian store + * of our 64-bit hunks. + */ + for (i = 0; i < 32; ++i) { + uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i); + for (j = 0; j < vq * 2; ++j) { + __put_user_e(env->vfp.zregs[i].d[j], z + j, le); + } + } + for (i = 0; i <= 16; ++i) { + uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i); + for (j = 0; j < vq; ++j) { + uint64_t r = env->vfp.pregs[i].p[j >> 2]; + __put_user_e(r >> ((j & 3) * 16), p + j, le); + } + } +} + +static void target_restore_general_frame(CPUARMState *env, + struct target_rt_sigframe *sf) +{ + sigset_t set; + uint64_t pstate; + int i; + + target_to_host_sigset(&set, &sf->uc.tuc_sigmask); + set_sigmask(&set); + + for (i = 0; i < 31; i++) { + __get_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); + } + + __get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); + __get_user(env->pc, &sf->uc.tuc_mcontext.pc); + __get_user(pstate, &sf->uc.tuc_mcontext.pstate); + pstate_write(env, pstate); +} + +static void target_restore_fpsimd_record(CPUARMState *env, + struct target_fpsimd_context *fpsimd) +{ + uint32_t fpsr, fpcr; + int i; + + __get_user(fpsr, &fpsimd->fpsr); + vfp_set_fpsr(env, fpsr); + __get_user(fpcr, &fpsimd->fpcr); + vfp_set_fpcr(env, fpcr); + + for (i = 0; i < 32; i++) { + uint64_t *q = aa64_vfp_qreg(env, i); +#ifdef TARGET_WORDS_BIGENDIAN + __get_user(q[0], &fpsimd->vregs[i * 2 + 1]); + __get_user(q[1], &fpsimd->vregs[i * 2]); +#else + __get_user(q[0], &fpsimd->vregs[i * 2]); + __get_user(q[1], &fpsimd->vregs[i * 2 + 1]); +#endif + } +} + +static void target_restore_sve_record(CPUARMState *env, + struct target_sve_context *sve, int vq) +{ + int i, j; + + /* Note that SVE regs are stored as a byte stream, with each byte element + * at a subsequent address. This corresponds to a little-endian load + * of our 64-bit hunks. + */ + for (i = 0; i < 32; ++i) { + uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i); + for (j = 0; j < vq * 2; ++j) { + __get_user_e(env->vfp.zregs[i].d[j], z + j, le); + } + } + for (i = 0; i <= 16; ++i) { + uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i); + for (j = 0; j < vq; ++j) { + uint16_t r; + __get_user_e(r, p + j, le); + if (j & 3) { + env->vfp.pregs[i].p[j >> 2] |= (uint64_t)r << ((j & 3) * 16); + } else { + env->vfp.pregs[i].p[j >> 2] = r; + } + } + } +} + +static int target_restore_sigframe(CPUARMState *env, + struct target_rt_sigframe *sf) +{ + struct target_aarch64_ctx *ctx, *extra = NULL; + struct target_fpsimd_context *fpsimd = NULL; + struct target_sve_context *sve = NULL; + uint64_t extra_datap = 0; + bool used_extra = false; + bool err = false; + int vq = 0, sve_size = 0; + + target_restore_general_frame(env, sf); + + ctx = (struct target_aarch64_ctx *)sf->uc.tuc_mcontext.__reserved; + while (ctx) { + uint32_t magic, size, extra_size; + + __get_user(magic, &ctx->magic); + __get_user(size, &ctx->size); + switch (magic) { + case 0: + if (size != 0) { + err = true; + goto exit; + } + if (used_extra) { + ctx = NULL; + } else { + ctx = extra; + used_extra = true; + } + continue; + + case TARGET_FPSIMD_MAGIC: + if (fpsimd || size != sizeof(struct target_fpsimd_context)) { + err = true; + goto exit; + } + fpsimd = (struct target_fpsimd_context *)ctx; + break; + + case TARGET_SVE_MAGIC: + if (arm_feature(env, ARM_FEATURE_SVE)) { + vq = (env->vfp.zcr_el[1] & 0xf) + 1; + sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); + if (!sve && size == sve_size) { + sve = (struct target_sve_context *)ctx; + break; + } + } + err = true; + goto exit; + + case TARGET_EXTRA_MAGIC: + if (extra || size != sizeof(struct target_extra_context)) { + err = true; + goto exit; + } + __get_user(extra_datap, + &((struct target_extra_context *)ctx)->datap); + __get_user(extra_size, + &((struct target_extra_context *)ctx)->size); + extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0); + break; + + default: + /* Unknown record -- we certainly didn't generate it. + * Did we in fact get out of sync? + */ + err = true; + goto exit; + } + ctx = (void *)ctx + size; + } + + /* Require FPSIMD always. */ + if (fpsimd) { + target_restore_fpsimd_record(env, fpsimd); + } else { + err = true; + } + + /* SVE data, if present, overwrites FPSIMD data. */ + if (sve) { + target_restore_sve_record(env, sve, vq); + } + + exit: + unlock_user(extra, extra_datap, 0); + return err; +} + +static abi_ulong get_sigframe(struct target_sigaction *ka, + CPUARMState *env, int size) +{ + abi_ulong sp; + + sp = env->xregs[31]; + + /* + * This is the X/Open sanctioned signal stack switching. + */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + sp = (sp - size) & ~15; + + return sp; +} + +typedef struct { + int total_size; + int extra_base; + int extra_size; + int std_end_ofs; + int extra_ofs; + int extra_end_ofs; +} target_sigframe_layout; + +static int alloc_sigframe_space(int this_size, target_sigframe_layout *l) +{ + /* Make sure there will always be space for the end marker. */ + const int std_size = sizeof(struct target_rt_sigframe) + - sizeof(struct target_aarch64_ctx); + int this_loc = l->total_size; + + if (l->extra_base) { + /* Once we have begun an extra space, all allocations go there. */ + l->extra_size += this_size; + } else if (this_size + this_loc > std_size) { + /* This allocation does not fit in the standard space. */ + /* Allocate the extra record. */ + l->extra_ofs = this_loc; + l->total_size += sizeof(struct target_extra_context); + + /* Allocate the standard end record. */ + l->std_end_ofs = l->total_size; + l->total_size += sizeof(struct target_aarch64_ctx); + + /* Allocate the requested record. */ + l->extra_base = this_loc = l->total_size; + l->extra_size = this_size; + } + l->total_size += this_size; + + return this_loc; +} + +static void target_setup_frame(int usig, struct target_sigaction *ka, + target_siginfo_t *info, target_sigset_t *set, + CPUARMState *env) +{ + target_sigframe_layout layout = { + /* Begin with the size pointing to the reserved space. */ + .total_size = offsetof(struct target_rt_sigframe, + uc.tuc_mcontext.__reserved), + }; + int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0; + struct target_rt_sigframe *frame; + struct target_rt_frame_record *fr; + abi_ulong frame_addr, return_addr; + + /* FPSIMD record is always in the standard space. */ + fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context), + &layout); + + /* SVE state needs saving only if it exists. */ + if (arm_feature(env, ARM_FEATURE_SVE)) { + vq = (env->vfp.zcr_el[1] & 0xf) + 1; + sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); + sve_ofs = alloc_sigframe_space(sve_size, &layout); + } + + if (layout.extra_ofs) { + /* Reserve space for the extra end marker. The standard end marker + * will have been allocated when we allocated the extra record. + */ + layout.extra_end_ofs + = alloc_sigframe_space(sizeof(struct target_aarch64_ctx), &layout); + } else { + /* Reserve space for the standard end marker. + * Do not use alloc_sigframe_space because we cheat + * std_size therein to reserve space for this. + */ + layout.std_end_ofs = layout.total_size; + layout.total_size += sizeof(struct target_aarch64_ctx); + } + + /* We must always provide at least the standard 4K reserved space, + * even if we don't use all of it (this is part of the ABI) + */ + layout.total_size = MAX(layout.total_size, + sizeof(struct target_rt_sigframe)); + + /* Reserve space for the return code. On a real system this would + * be within the VDSO. So, despite the name this is not a "real" + * record within the frame. + */ + fr_ofs = layout.total_size; + layout.total_size += sizeof(struct target_rt_frame_record); + + frame_addr = get_sigframe(ka, env, layout.total_size); + trace_user_setup_frame(env, frame_addr); + frame = lock_user(VERIFY_WRITE, frame_addr, layout.total_size, 0); + if (!frame) { + goto give_sigsegv; + } + + target_setup_general_frame(frame, env, set); + target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env); + target_setup_end_record((void *)frame + layout.std_end_ofs); + if (layout.extra_ofs) { + target_setup_extra_record((void *)frame + layout.extra_ofs, + frame_addr + layout.extra_base, + layout.extra_size); + target_setup_end_record((void *)frame + layout.extra_end_ofs); + } + if (sve_ofs) { + target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size); + } + + /* Set up the stack frame for unwinding. */ + fr = (void *)frame + fr_ofs; + __put_user(env->xregs[29], &fr->fp); + __put_user(env->xregs[30], &fr->lr); + + if (ka->sa_flags & TARGET_SA_RESTORER) { + return_addr = ka->sa_restorer; + } else { + /* + * mov x8,#__NR_rt_sigreturn; svc #0 + * Since these are instructions they need to be put as little-endian + * regardless of target default or current CPU endianness. + */ + __put_user_e(0xd2801168, &fr->tramp[0], le); + __put_user_e(0xd4000001, &fr->tramp[1], le); + return_addr = frame_addr + fr_ofs + + offsetof(struct target_rt_frame_record, tramp); + } + env->xregs[0] = usig; + env->xregs[31] = frame_addr; + env->xregs[29] = frame_addr + fr_ofs; + env->pc = ka->_sa_handler; + env->xregs[30] = return_addr; + if (info) { + tswap_siginfo(&frame->info, info); + env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->xregs[2] = frame_addr + offsetof(struct target_rt_sigframe, uc); + } + + unlock_user(frame, frame_addr, layout.total_size); + return; + + give_sigsegv: + unlock_user(frame, frame_addr, layout.total_size); + force_sigsegv(usig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, target_sigset_t *set, + CPUARMState *env) +{ + target_setup_frame(sig, ka, info, set, env); +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *env) +{ + target_setup_frame(sig, ka, 0, set, env); +} + +long do_rt_sigreturn(CPUARMState *env) +{ + struct target_rt_sigframe *frame = NULL; + abi_ulong frame_addr = env->xregs[31]; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (frame_addr & 15) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + if (target_restore_sigframe(env, frame)) { + goto badframe; + } + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_sigreturn(CPUARMState *env) +{ + return do_rt_sigreturn(env); +} diff --git a/linux-user/aarch64/target_signal.h b/linux-user/aarch64/target_signal.h index e66367cac1..af1f1320e2 100644 --- a/linux-user/aarch64/target_signal.h +++ b/linux-user/aarch64/target_signal.h @@ -26,4 +26,9 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) return state->xregs[31]; } +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *env); +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, target_sigset_t *set, + CPUARMState *env); #endif /* AARCH64_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 0f956740ef..fcfa023d42 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1383,572 +1383,7 @@ badframe: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_AARCH64) - -struct target_sigcontext { - uint64_t fault_address; - /* AArch64 registers */ - uint64_t regs[31]; - uint64_t sp; - uint64_t pc; - uint64_t pstate; - /* 4K reserved for FP/SIMD state and future expansion */ - char __reserved[4096] __attribute__((__aligned__(16))); -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - target_sigset_t tuc_sigmask; - /* glibc uses a 1024-bit sigset_t */ - char __unused[1024 / 8 - sizeof(target_sigset_t)]; - /* last for future expansion */ - struct target_sigcontext tuc_mcontext; -}; - -/* - * Header to be used at the beginning of structures extending the user - * context. Such structures must be placed after the rt_sigframe on the stack - * and be 16-byte aligned. The last structure must be a dummy one with the - * magic and size set to 0. - */ -struct target_aarch64_ctx { - uint32_t magic; - uint32_t size; -}; - -#define TARGET_FPSIMD_MAGIC 0x46508001 - -struct target_fpsimd_context { - struct target_aarch64_ctx head; - uint32_t fpsr; - uint32_t fpcr; - uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */ -}; - -#define TARGET_EXTRA_MAGIC 0x45585401 - -struct target_extra_context { - struct target_aarch64_ctx head; - uint64_t datap; /* 16-byte aligned pointer to extra space cast to __u64 */ - uint32_t size; /* size in bytes of the extra space */ - uint32_t reserved[3]; -}; - -#define TARGET_SVE_MAGIC 0x53564501 - -struct target_sve_context { - struct target_aarch64_ctx head; - uint16_t vl; - uint16_t reserved[3]; - /* The actual SVE data immediately follows. It is layed out - * according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of - * the original struct pointer. - */ -}; - -#define TARGET_SVE_VQ_BYTES 16 - -#define TARGET_SVE_SIG_ZREG_SIZE(VQ) ((VQ) * TARGET_SVE_VQ_BYTES) -#define TARGET_SVE_SIG_PREG_SIZE(VQ) ((VQ) * (TARGET_SVE_VQ_BYTES / 8)) - -#define TARGET_SVE_SIG_REGS_OFFSET \ - QEMU_ALIGN_UP(sizeof(struct target_sve_context), TARGET_SVE_VQ_BYTES) -#define TARGET_SVE_SIG_ZREG_OFFSET(VQ, N) \ - (TARGET_SVE_SIG_REGS_OFFSET + TARGET_SVE_SIG_ZREG_SIZE(VQ) * (N)) -#define TARGET_SVE_SIG_PREG_OFFSET(VQ, N) \ - (TARGET_SVE_SIG_ZREG_OFFSET(VQ, 32) + TARGET_SVE_SIG_PREG_SIZE(VQ) * (N)) -#define TARGET_SVE_SIG_FFR_OFFSET(VQ) \ - (TARGET_SVE_SIG_PREG_OFFSET(VQ, 16)) -#define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \ - (TARGET_SVE_SIG_PREG_OFFSET(VQ, 17)) - -struct target_rt_sigframe { - struct target_siginfo info; - struct target_ucontext uc; -}; - -struct target_rt_frame_record { - uint64_t fp; - uint64_t lr; - uint32_t tramp[2]; -}; - -static void target_setup_general_frame(struct target_rt_sigframe *sf, - CPUARMState *env, target_sigset_t *set) -{ - int i; - - __put_user(0, &sf->uc.tuc_flags); - __put_user(0, &sf->uc.tuc_link); - - __put_user(target_sigaltstack_used.ss_sp, &sf->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->xregs[31]), &sf->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &sf->uc.tuc_stack.ss_size); - - for (i = 0; i < 31; i++) { - __put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); - } - __put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); - __put_user(env->pc, &sf->uc.tuc_mcontext.pc); - __put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate); - - __put_user(env->exception.vaddress, &sf->uc.tuc_mcontext.fault_address); - - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]); - } -} - -static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd, - CPUARMState *env) -{ - int i; - - __put_user(TARGET_FPSIMD_MAGIC, &fpsimd->head.magic); - __put_user(sizeof(struct target_fpsimd_context), &fpsimd->head.size); - __put_user(vfp_get_fpsr(env), &fpsimd->fpsr); - __put_user(vfp_get_fpcr(env), &fpsimd->fpcr); - - for (i = 0; i < 32; i++) { - uint64_t *q = aa64_vfp_qreg(env, i); -#ifdef TARGET_WORDS_BIGENDIAN - __put_user(q[0], &fpsimd->vregs[i * 2 + 1]); - __put_user(q[1], &fpsimd->vregs[i * 2]); -#else - __put_user(q[0], &fpsimd->vregs[i * 2]); - __put_user(q[1], &fpsimd->vregs[i * 2 + 1]); -#endif - } -} - -static void target_setup_extra_record(struct target_extra_context *extra, - uint64_t datap, uint32_t extra_size) -{ - __put_user(TARGET_EXTRA_MAGIC, &extra->head.magic); - __put_user(sizeof(struct target_extra_context), &extra->head.size); - __put_user(datap, &extra->datap); - __put_user(extra_size, &extra->size); -} - -static void target_setup_end_record(struct target_aarch64_ctx *end) -{ - __put_user(0, &end->magic); - __put_user(0, &end->size); -} - -static void target_setup_sve_record(struct target_sve_context *sve, - CPUARMState *env, int vq, int size) -{ - int i, j; - - __put_user(TARGET_SVE_MAGIC, &sve->head.magic); - __put_user(size, &sve->head.size); - __put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl); - - /* Note that SVE regs are stored as a byte stream, with each byte element - * at a subsequent address. This corresponds to a little-endian store - * of our 64-bit hunks. - */ - for (i = 0; i < 32; ++i) { - uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i); - for (j = 0; j < vq * 2; ++j) { - __put_user_e(env->vfp.zregs[i].d[j], z + j, le); - } - } - for (i = 0; i <= 16; ++i) { - uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i); - for (j = 0; j < vq; ++j) { - uint64_t r = env->vfp.pregs[i].p[j >> 2]; - __put_user_e(r >> ((j & 3) * 16), p + j, le); - } - } -} - -static void target_restore_general_frame(CPUARMState *env, - struct target_rt_sigframe *sf) -{ - sigset_t set; - uint64_t pstate; - int i; - - target_to_host_sigset(&set, &sf->uc.tuc_sigmask); - set_sigmask(&set); - - for (i = 0; i < 31; i++) { - __get_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); - } - - __get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); - __get_user(env->pc, &sf->uc.tuc_mcontext.pc); - __get_user(pstate, &sf->uc.tuc_mcontext.pstate); - pstate_write(env, pstate); -} - -static void target_restore_fpsimd_record(CPUARMState *env, - struct target_fpsimd_context *fpsimd) -{ - uint32_t fpsr, fpcr; - int i; - - __get_user(fpsr, &fpsimd->fpsr); - vfp_set_fpsr(env, fpsr); - __get_user(fpcr, &fpsimd->fpcr); - vfp_set_fpcr(env, fpcr); - - for (i = 0; i < 32; i++) { - uint64_t *q = aa64_vfp_qreg(env, i); -#ifdef TARGET_WORDS_BIGENDIAN - __get_user(q[0], &fpsimd->vregs[i * 2 + 1]); - __get_user(q[1], &fpsimd->vregs[i * 2]); -#else - __get_user(q[0], &fpsimd->vregs[i * 2]); - __get_user(q[1], &fpsimd->vregs[i * 2 + 1]); -#endif - } -} - -static void target_restore_sve_record(CPUARMState *env, - struct target_sve_context *sve, int vq) -{ - int i, j; - - /* Note that SVE regs are stored as a byte stream, with each byte element - * at a subsequent address. This corresponds to a little-endian load - * of our 64-bit hunks. - */ - for (i = 0; i < 32; ++i) { - uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i); - for (j = 0; j < vq * 2; ++j) { - __get_user_e(env->vfp.zregs[i].d[j], z + j, le); - } - } - for (i = 0; i <= 16; ++i) { - uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i); - for (j = 0; j < vq; ++j) { - uint16_t r; - __get_user_e(r, p + j, le); - if (j & 3) { - env->vfp.pregs[i].p[j >> 2] |= (uint64_t)r << ((j & 3) * 16); - } else { - env->vfp.pregs[i].p[j >> 2] = r; - } - } - } -} - -static int target_restore_sigframe(CPUARMState *env, - struct target_rt_sigframe *sf) -{ - struct target_aarch64_ctx *ctx, *extra = NULL; - struct target_fpsimd_context *fpsimd = NULL; - struct target_sve_context *sve = NULL; - uint64_t extra_datap = 0; - bool used_extra = false; - bool err = false; - int vq = 0, sve_size = 0; - - target_restore_general_frame(env, sf); - - ctx = (struct target_aarch64_ctx *)sf->uc.tuc_mcontext.__reserved; - while (ctx) { - uint32_t magic, size, extra_size; - - __get_user(magic, &ctx->magic); - __get_user(size, &ctx->size); - switch (magic) { - case 0: - if (size != 0) { - err = true; - goto exit; - } - if (used_extra) { - ctx = NULL; - } else { - ctx = extra; - used_extra = true; - } - continue; - - case TARGET_FPSIMD_MAGIC: - if (fpsimd || size != sizeof(struct target_fpsimd_context)) { - err = true; - goto exit; - } - fpsimd = (struct target_fpsimd_context *)ctx; - break; - - case TARGET_SVE_MAGIC: - if (arm_feature(env, ARM_FEATURE_SVE)) { - vq = (env->vfp.zcr_el[1] & 0xf) + 1; - sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); - if (!sve && size == sve_size) { - sve = (struct target_sve_context *)ctx; - break; - } - } - err = true; - goto exit; - - case TARGET_EXTRA_MAGIC: - if (extra || size != sizeof(struct target_extra_context)) { - err = true; - goto exit; - } - __get_user(extra_datap, - &((struct target_extra_context *)ctx)->datap); - __get_user(extra_size, - &((struct target_extra_context *)ctx)->size); - extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0); - break; - - default: - /* Unknown record -- we certainly didn't generate it. - * Did we in fact get out of sync? - */ - err = true; - goto exit; - } - ctx = (void *)ctx + size; - } - - /* Require FPSIMD always. */ - if (fpsimd) { - target_restore_fpsimd_record(env, fpsimd); - } else { - err = true; - } - - /* SVE data, if present, overwrites FPSIMD data. */ - if (sve) { - target_restore_sve_record(env, sve, vq); - } - - exit: - unlock_user(extra, extra_datap, 0); - return err; -} - -static abi_ulong get_sigframe(struct target_sigaction *ka, - CPUARMState *env, int size) -{ - abi_ulong sp; - - sp = env->xregs[31]; - - /* - * This is the X/Open sanctioned signal stack switching. - */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - sp = (sp - size) & ~15; - - return sp; -} - -typedef struct { - int total_size; - int extra_base; - int extra_size; - int std_end_ofs; - int extra_ofs; - int extra_end_ofs; -} target_sigframe_layout; - -static int alloc_sigframe_space(int this_size, target_sigframe_layout *l) -{ - /* Make sure there will always be space for the end marker. */ - const int std_size = sizeof(struct target_rt_sigframe) - - sizeof(struct target_aarch64_ctx); - int this_loc = l->total_size; - - if (l->extra_base) { - /* Once we have begun an extra space, all allocations go there. */ - l->extra_size += this_size; - } else if (this_size + this_loc > std_size) { - /* This allocation does not fit in the standard space. */ - /* Allocate the extra record. */ - l->extra_ofs = this_loc; - l->total_size += sizeof(struct target_extra_context); - - /* Allocate the standard end record. */ - l->std_end_ofs = l->total_size; - l->total_size += sizeof(struct target_aarch64_ctx); - - /* Allocate the requested record. */ - l->extra_base = this_loc = l->total_size; - l->extra_size = this_size; - } - l->total_size += this_size; - - return this_loc; -} - -static void target_setup_frame(int usig, struct target_sigaction *ka, - target_siginfo_t *info, target_sigset_t *set, - CPUARMState *env) -{ - target_sigframe_layout layout = { - /* Begin with the size pointing to the reserved space. */ - .total_size = offsetof(struct target_rt_sigframe, - uc.tuc_mcontext.__reserved), - }; - int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0; - struct target_rt_sigframe *frame; - struct target_rt_frame_record *fr; - abi_ulong frame_addr, return_addr; - - /* FPSIMD record is always in the standard space. */ - fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context), - &layout); - - /* SVE state needs saving only if it exists. */ - if (arm_feature(env, ARM_FEATURE_SVE)) { - vq = (env->vfp.zcr_el[1] & 0xf) + 1; - sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); - sve_ofs = alloc_sigframe_space(sve_size, &layout); - } - - if (layout.extra_ofs) { - /* Reserve space for the extra end marker. The standard end marker - * will have been allocated when we allocated the extra record. - */ - layout.extra_end_ofs - = alloc_sigframe_space(sizeof(struct target_aarch64_ctx), &layout); - } else { - /* Reserve space for the standard end marker. - * Do not use alloc_sigframe_space because we cheat - * std_size therein to reserve space for this. - */ - layout.std_end_ofs = layout.total_size; - layout.total_size += sizeof(struct target_aarch64_ctx); - } - - /* We must always provide at least the standard 4K reserved space, - * even if we don't use all of it (this is part of the ABI) - */ - layout.total_size = MAX(layout.total_size, - sizeof(struct target_rt_sigframe)); - - /* Reserve space for the return code. On a real system this would - * be within the VDSO. So, despite the name this is not a "real" - * record within the frame. - */ - fr_ofs = layout.total_size; - layout.total_size += sizeof(struct target_rt_frame_record); - - frame_addr = get_sigframe(ka, env, layout.total_size); - trace_user_setup_frame(env, frame_addr); - frame = lock_user(VERIFY_WRITE, frame_addr, layout.total_size, 0); - if (!frame) { - goto give_sigsegv; - } - - target_setup_general_frame(frame, env, set); - target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env); - target_setup_end_record((void *)frame + layout.std_end_ofs); - if (layout.extra_ofs) { - target_setup_extra_record((void *)frame + layout.extra_ofs, - frame_addr + layout.extra_base, - layout.extra_size); - target_setup_end_record((void *)frame + layout.extra_end_ofs); - } - if (sve_ofs) { - target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size); - } - - /* Set up the stack frame for unwinding. */ - fr = (void *)frame + fr_ofs; - __put_user(env->xregs[29], &fr->fp); - __put_user(env->xregs[30], &fr->lr); - - if (ka->sa_flags & TARGET_SA_RESTORER) { - return_addr = ka->sa_restorer; - } else { - /* - * mov x8,#__NR_rt_sigreturn; svc #0 - * Since these are instructions they need to be put as little-endian - * regardless of target default or current CPU endianness. - */ - __put_user_e(0xd2801168, &fr->tramp[0], le); - __put_user_e(0xd4000001, &fr->tramp[1], le); - return_addr = frame_addr + fr_ofs - + offsetof(struct target_rt_frame_record, tramp); - } - env->xregs[0] = usig; - env->xregs[31] = frame_addr; - env->xregs[29] = frame_addr + fr_ofs; - env->pc = ka->_sa_handler; - env->xregs[30] = return_addr; - if (info) { - tswap_siginfo(&frame->info, info); - env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->xregs[2] = frame_addr + offsetof(struct target_rt_sigframe, uc); - } - - unlock_user(frame, frame_addr, layout.total_size); - return; - - give_sigsegv: - unlock_user(frame, frame_addr, layout.total_size); - force_sigsegv(usig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, target_sigset_t *set, - CPUARMState *env) -{ - target_setup_frame(sig, ka, info, set, env); -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *env) -{ - target_setup_frame(sig, ka, 0, set, env); -} - -long do_rt_sigreturn(CPUARMState *env) -{ - struct target_rt_sigframe *frame = NULL; - abi_ulong frame_addr = env->xregs[31]; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (frame_addr & 15) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - if (target_restore_sigframe(env, frame)) { - goto badframe; - } - - if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, get_sp_from_cpustate(env)) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - - badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_sigreturn(CPUARMState *env) -{ - return do_rt_sigreturn(env); -} - -#elif defined(TARGET_ARM) +#elif defined(TARGET_ARM) && !defined(TARGET_AARCH64) struct target_sigcontext { abi_ulong trap_no; From 5f7645975d23b90a28067f258b79887aa30ada8e Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:18 +0200 Subject: [PATCH 0128/2380] linux-user: move arm signal.c parts to arm directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to arm/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-4-laurent@vivier.eu> --- linux-user/arm/signal.c | 754 +++++++++++++++++++++++++++++++++ linux-user/arm/target_signal.h | 6 +- linux-user/signal.c | 751 -------------------------------- 3 files changed, 759 insertions(+), 752 deletions(-) diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index 02ca338b6c..0c1ec53025 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -16,3 +16,757 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_ulong trap_no; + abi_ulong error_code; + abi_ulong oldmask; + abi_ulong arm_r0; + abi_ulong arm_r1; + abi_ulong arm_r2; + abi_ulong arm_r3; + abi_ulong arm_r4; + abi_ulong arm_r5; + abi_ulong arm_r6; + abi_ulong arm_r7; + abi_ulong arm_r8; + abi_ulong arm_r9; + abi_ulong arm_r10; + abi_ulong arm_fp; + abi_ulong arm_ip; + abi_ulong arm_sp; + abi_ulong arm_lr; + abi_ulong arm_pc; + abi_ulong arm_cpsr; + abi_ulong fault_address; +}; + +struct target_ucontext_v1 { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +struct target_ucontext_v2 { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ + char __unused[128 - sizeof(target_sigset_t)]; + abi_ulong tuc_regspace[128] __attribute__((__aligned__(8))); +}; + +struct target_user_vfp { + uint64_t fpregs[32]; + abi_ulong fpscr; +}; + +struct target_user_vfp_exc { + abi_ulong fpexc; + abi_ulong fpinst; + abi_ulong fpinst2; +}; + +struct target_vfp_sigframe { + abi_ulong magic; + abi_ulong size; + struct target_user_vfp ufp; + struct target_user_vfp_exc ufp_exc; +} __attribute__((__aligned__(8))); + +struct target_iwmmxt_sigframe { + abi_ulong magic; + abi_ulong size; + uint64_t regs[16]; + /* Note that not all the coprocessor control registers are stored here */ + uint32_t wcssf; + uint32_t wcasf; + uint32_t wcgr0; + uint32_t wcgr1; + uint32_t wcgr2; + uint32_t wcgr3; +} __attribute__((__aligned__(8))); + +#define TARGET_VFP_MAGIC 0x56465001 +#define TARGET_IWMMXT_MAGIC 0x12ef842a + +struct sigframe_v1 +{ + struct target_sigcontext sc; + abi_ulong extramask[TARGET_NSIG_WORDS-1]; + abi_ulong retcode; +}; + +struct sigframe_v2 +{ + struct target_ucontext_v2 uc; + abi_ulong retcode; +}; + +struct rt_sigframe_v1 +{ + abi_ulong pinfo; + abi_ulong puc; + struct target_siginfo info; + struct target_ucontext_v1 uc; + abi_ulong retcode; +}; + +struct rt_sigframe_v2 +{ + struct target_siginfo info; + struct target_ucontext_v2 uc; + abi_ulong retcode; +}; + +#define TARGET_CONFIG_CPU_32 1 + +/* + * For ARM syscalls, we encode the syscall number into the instruction. + */ +#define SWI_SYS_SIGRETURN (0xef000000|(TARGET_NR_sigreturn + ARM_SYSCALL_BASE)) +#define SWI_SYS_RT_SIGRETURN (0xef000000|(TARGET_NR_rt_sigreturn + ARM_SYSCALL_BASE)) + +/* + * For Thumb syscalls, we pass the syscall number via r7. We therefore + * need two 16-bit instructions. + */ +#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_sigreturn)) +#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_rt_sigreturn)) + +static const abi_ulong retcodes[4] = { + SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, + SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN +}; + + +static inline int valid_user_regs(CPUARMState *regs) +{ + return 1; +} + +static void +setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ + CPUARMState *env, abi_ulong mask) +{ + __put_user(env->regs[0], &sc->arm_r0); + __put_user(env->regs[1], &sc->arm_r1); + __put_user(env->regs[2], &sc->arm_r2); + __put_user(env->regs[3], &sc->arm_r3); + __put_user(env->regs[4], &sc->arm_r4); + __put_user(env->regs[5], &sc->arm_r5); + __put_user(env->regs[6], &sc->arm_r6); + __put_user(env->regs[7], &sc->arm_r7); + __put_user(env->regs[8], &sc->arm_r8); + __put_user(env->regs[9], &sc->arm_r9); + __put_user(env->regs[10], &sc->arm_r10); + __put_user(env->regs[11], &sc->arm_fp); + __put_user(env->regs[12], &sc->arm_ip); + __put_user(env->regs[13], &sc->arm_sp); + __put_user(env->regs[14], &sc->arm_lr); + __put_user(env->regs[15], &sc->arm_pc); +#ifdef TARGET_CONFIG_CPU_32 + __put_user(cpsr_read(env), &sc->arm_cpsr); +#endif + + __put_user(/* current->thread.trap_no */ 0, &sc->trap_no); + __put_user(/* current->thread.error_code */ 0, &sc->error_code); + __put_user(/* current->thread.address */ 0, &sc->fault_address); + __put_user(mask, &sc->oldmask); +} + +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) +{ + unsigned long sp = regs->regs[13]; + + /* + * This is the X/Open sanctioned signal stack switching. + */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + /* + * ATPCS B01 mandates 8-byte alignment + */ + return (sp - framesize) & ~7; +} + +static void +setup_return(CPUARMState *env, struct target_sigaction *ka, + abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr) +{ + abi_ulong handler = ka->_sa_handler; + abi_ulong retcode; + int thumb = handler & 1; + uint32_t cpsr = cpsr_read(env); + + cpsr &= ~CPSR_IT; + if (thumb) { + cpsr |= CPSR_T; + } else { + cpsr &= ~CPSR_T; + } + + if (ka->sa_flags & TARGET_SA_RESTORER) { + retcode = ka->sa_restorer; + } else { + unsigned int idx = thumb; + + if (ka->sa_flags & TARGET_SA_SIGINFO) { + idx += 2; + } + + __put_user(retcodes[idx], rc); + + retcode = rc_addr + thumb; + } + + env->regs[0] = usig; + env->regs[13] = frame_addr; + env->regs[14] = retcode; + env->regs[15] = handler & (thumb ? ~1 : ~3); + cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr); +} + +static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) +{ + int i; + struct target_vfp_sigframe *vfpframe; + vfpframe = (struct target_vfp_sigframe *)regspace; + __put_user(TARGET_VFP_MAGIC, &vfpframe->magic); + __put_user(sizeof(*vfpframe), &vfpframe->size); + for (i = 0; i < 32; i++) { + __put_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]); + } + __put_user(vfp_get_fpscr(env), &vfpframe->ufp.fpscr); + __put_user(env->vfp.xregs[ARM_VFP_FPEXC], &vfpframe->ufp_exc.fpexc); + __put_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst); + __put_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2); + return (abi_ulong*)(vfpframe+1); +} + +static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace, + CPUARMState *env) +{ + int i; + struct target_iwmmxt_sigframe *iwmmxtframe; + iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; + __put_user(TARGET_IWMMXT_MAGIC, &iwmmxtframe->magic); + __put_user(sizeof(*iwmmxtframe), &iwmmxtframe->size); + for (i = 0; i < 16; i++) { + __put_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); + } + __put_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); + return (abi_ulong*)(iwmmxtframe+1); +} + +static void setup_sigframe_v2(struct target_ucontext_v2 *uc, + target_sigset_t *set, CPUARMState *env) +{ + struct target_sigaltstack stack; + int i; + abi_ulong *regspace; + + /* Clear all the bits of the ucontext we don't use. */ + memset(uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext)); + + memset(&stack, 0, sizeof(stack)); + __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp); + __put_user(target_sigaltstack_used.ss_size, &stack.ss_size); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags); + memcpy(&uc->tuc_stack, &stack, sizeof(stack)); + + setup_sigcontext(&uc->tuc_mcontext, env, set->sig[0]); + /* Save coprocessor signal frame. */ + regspace = uc->tuc_regspace; + if (arm_feature(env, ARM_FEATURE_VFP)) { + regspace = setup_sigframe_v2_vfp(regspace, env); + } + if (arm_feature(env, ARM_FEATURE_IWMMXT)) { + regspace = setup_sigframe_v2_iwmmxt(regspace, env); + } + + /* Write terminating magic word */ + __put_user(0, regspace); + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &uc->tuc_sigmask.sig[i]); + } +} + +/* compare linux/arch/arm/kernel/signal.c:setup_frame() */ +static void setup_frame_v1(int usig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *regs) +{ + struct sigframe_v1 *frame; + abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); + int i; + + trace_user_setup_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto sigsegv; + } + + setup_sigcontext(&frame->sc, regs, set->sig[0]); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->extramask[i - 1]); + } + + setup_return(regs, ka, &frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct sigframe_v1, retcode)); + + unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + force_sigsegv(usig); +} + +static void setup_frame_v2(int usig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *regs) +{ + struct sigframe_v2 *frame; + abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); + + trace_user_setup_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto sigsegv; + } + + setup_sigframe_v2(&frame->uc, set, regs); + + setup_return(regs, ka, &frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct sigframe_v2, retcode)); + + unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + force_sigsegv(usig); +} + +void setup_frame(int usig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *regs) +{ + if (get_osversion() >= 0x020612) { + setup_frame_v2(usig, ka, set, regs); + } else { + setup_frame_v1(usig, ka, set, regs); + } +} + +/* compare linux/arch/arm/kernel/signal.c:setup_rt_frame() */ +static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUARMState *env) +{ + struct rt_sigframe_v1 *frame; + abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); + struct target_sigaltstack stack; + int i; + abi_ulong info_addr, uc_addr; + + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto sigsegv; + } + + info_addr = frame_addr + offsetof(struct rt_sigframe_v1, info); + __put_user(info_addr, &frame->pinfo); + uc_addr = frame_addr + offsetof(struct rt_sigframe_v1, uc); + __put_user(uc_addr, &frame->puc); + tswap_siginfo(&frame->info, info); + + /* Clear all the bits of the ucontext we don't use. */ + memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext)); + + memset(&stack, 0, sizeof(stack)); + __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp); + __put_user(target_sigaltstack_used.ss_size, &stack.ss_size); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags); + memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack)); + + setup_sigcontext(&frame->uc.tuc_mcontext, env, set->sig[0]); + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + setup_return(env, ka, &frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct rt_sigframe_v1, retcode)); + + env->regs[1] = info_addr; + env->regs[2] = uc_addr; + + unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + force_sigsegv(usig); +} + +static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUARMState *env) +{ + struct rt_sigframe_v2 *frame; + abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); + abi_ulong info_addr, uc_addr; + + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto sigsegv; + } + + info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info); + uc_addr = frame_addr + offsetof(struct rt_sigframe_v2, uc); + tswap_siginfo(&frame->info, info); + + setup_sigframe_v2(&frame->uc, set, env); + + setup_return(env, ka, &frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct rt_sigframe_v2, retcode)); + + env->regs[1] = info_addr; + env->regs[2] = uc_addr; + + unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + force_sigsegv(usig); +} + +void setup_rt_frame(int usig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUARMState *env) +{ + if (get_osversion() >= 0x020612) { + setup_rt_frame_v2(usig, ka, info, set, env); + } else { + setup_rt_frame_v1(usig, ka, info, set, env); + } +} + +static int +restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) +{ + int err = 0; + uint32_t cpsr; + + __get_user(env->regs[0], &sc->arm_r0); + __get_user(env->regs[1], &sc->arm_r1); + __get_user(env->regs[2], &sc->arm_r2); + __get_user(env->regs[3], &sc->arm_r3); + __get_user(env->regs[4], &sc->arm_r4); + __get_user(env->regs[5], &sc->arm_r5); + __get_user(env->regs[6], &sc->arm_r6); + __get_user(env->regs[7], &sc->arm_r7); + __get_user(env->regs[8], &sc->arm_r8); + __get_user(env->regs[9], &sc->arm_r9); + __get_user(env->regs[10], &sc->arm_r10); + __get_user(env->regs[11], &sc->arm_fp); + __get_user(env->regs[12], &sc->arm_ip); + __get_user(env->regs[13], &sc->arm_sp); + __get_user(env->regs[14], &sc->arm_lr); + __get_user(env->regs[15], &sc->arm_pc); +#ifdef TARGET_CONFIG_CPU_32 + __get_user(cpsr, &sc->arm_cpsr); + cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); +#endif + + err |= !valid_user_regs(env); + + return err; +} + +static long do_sigreturn_v1(CPUARMState *env) +{ + abi_ulong frame_addr; + struct sigframe_v1 *frame = NULL; + target_sigset_t set; + sigset_t host_set; + int i; + + /* + * Since we stacked the signal on a 64-bit boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + frame_addr = env->regs[13]; + trace_user_do_sigreturn(env, frame_addr); + if (frame_addr & 7) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + __get_user(set.sig[0], &frame->sc.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(set.sig[i], &frame->extramask[i - 1]); + } + + target_to_host_sigset_internal(&host_set, &set); + set_sigmask(&host_set); + + if (restore_sigcontext(env, &frame->sc)) { + goto badframe; + } + +#if 0 + /* Send SIGTRAP if we're single-stepping */ + if (ptrace_cancel_bpt(current)) + send_sig(SIGTRAP, current, 1); +#endif + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) +{ + int i; + abi_ulong magic, sz; + uint32_t fpscr, fpexc; + struct target_vfp_sigframe *vfpframe; + vfpframe = (struct target_vfp_sigframe *)regspace; + + __get_user(magic, &vfpframe->magic); + __get_user(sz, &vfpframe->size); + if (magic != TARGET_VFP_MAGIC || sz != sizeof(*vfpframe)) { + return 0; + } + for (i = 0; i < 32; i++) { + __get_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]); + } + __get_user(fpscr, &vfpframe->ufp.fpscr); + vfp_set_fpscr(env, fpscr); + __get_user(fpexc, &vfpframe->ufp_exc.fpexc); + /* Sanitise FPEXC: ensure VFP is enabled, FPINST2 is invalid + * and the exception flag is cleared + */ + fpexc |= (1 << 30); + fpexc &= ~((1 << 31) | (1 << 28)); + env->vfp.xregs[ARM_VFP_FPEXC] = fpexc; + __get_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst); + __get_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2); + return (abi_ulong*)(vfpframe + 1); +} + +static abi_ulong *restore_sigframe_v2_iwmmxt(CPUARMState *env, + abi_ulong *regspace) +{ + int i; + abi_ulong magic, sz; + struct target_iwmmxt_sigframe *iwmmxtframe; + iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; + + __get_user(magic, &iwmmxtframe->magic); + __get_user(sz, &iwmmxtframe->size); + if (magic != TARGET_IWMMXT_MAGIC || sz != sizeof(*iwmmxtframe)) { + return 0; + } + for (i = 0; i < 16; i++) { + __get_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); + } + __get_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); + return (abi_ulong*)(iwmmxtframe + 1); +} + +static int do_sigframe_return_v2(CPUARMState *env, + target_ulong context_addr, + struct target_ucontext_v2 *uc) +{ + sigset_t host_set; + abi_ulong *regspace; + + target_to_host_sigset(&host_set, &uc->tuc_sigmask); + set_sigmask(&host_set); + + if (restore_sigcontext(env, &uc->tuc_mcontext)) + return 1; + + /* Restore coprocessor signal frame */ + regspace = uc->tuc_regspace; + if (arm_feature(env, ARM_FEATURE_VFP)) { + regspace = restore_sigframe_v2_vfp(env, regspace); + if (!regspace) { + return 1; + } + } + if (arm_feature(env, ARM_FEATURE_IWMMXT)) { + regspace = restore_sigframe_v2_iwmmxt(env, regspace); + if (!regspace) { + return 1; + } + } + + if (do_sigaltstack(context_addr + + offsetof(struct target_ucontext_v2, tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) { + return 1; + } + +#if 0 + /* Send SIGTRAP if we're single-stepping */ + if (ptrace_cancel_bpt(current)) + send_sig(SIGTRAP, current, 1); +#endif + + return 0; +} + +static long do_sigreturn_v2(CPUARMState *env) +{ + abi_ulong frame_addr; + struct sigframe_v2 *frame = NULL; + + /* + * Since we stacked the signal on a 64-bit boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + frame_addr = env->regs[13]; + trace_user_do_sigreturn(env, frame_addr); + if (frame_addr & 7) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + if (do_sigframe_return_v2(env, + frame_addr + + offsetof(struct sigframe_v2, uc), + &frame->uc)) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_sigreturn(CPUARMState *env) +{ + if (get_osversion() >= 0x020612) { + return do_sigreturn_v2(env); + } else { + return do_sigreturn_v1(env); + } +} + +static long do_rt_sigreturn_v1(CPUARMState *env) +{ + abi_ulong frame_addr; + struct rt_sigframe_v1 *frame = NULL; + sigset_t host_set; + + /* + * Since we stacked the signal on a 64-bit boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + frame_addr = env->regs[13]; + trace_user_do_rt_sigreturn(env, frame_addr); + if (frame_addr & 7) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask); + set_sigmask(&host_set); + + if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { + goto badframe; + } + + if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe_v1, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) + goto badframe; + +#if 0 + /* Send SIGTRAP if we're single-stepping */ + if (ptrace_cancel_bpt(current)) + send_sig(SIGTRAP, current, 1); +#endif + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +static long do_rt_sigreturn_v2(CPUARMState *env) +{ + abi_ulong frame_addr; + struct rt_sigframe_v2 *frame = NULL; + + /* + * Since we stacked the signal on a 64-bit boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + frame_addr = env->regs[13]; + trace_user_do_rt_sigreturn(env, frame_addr); + if (frame_addr & 7) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + if (do_sigframe_return_v2(env, + frame_addr + + offsetof(struct rt_sigframe_v2, uc), + &frame->uc)) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUARMState *env) +{ + if (get_osversion() >= 0x020612) { + return do_rt_sigreturn_v2(env); + } else { + return do_rt_sigreturn_v1(env); + } +} diff --git a/linux-user/arm/target_signal.h b/linux-user/arm/target_signal.h index cbbeb09f4d..4b542c324f 100644 --- a/linux-user/arm/target_signal.h +++ b/linux-user/arm/target_signal.h @@ -26,5 +26,9 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) return state->regs[13]; } - +void setup_frame(int usig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *regs); +void setup_rt_frame(int usig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUARMState *env); #endif /* ARM_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index fcfa023d42..c99eae15d6 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1383,757 +1383,6 @@ badframe: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_ARM) && !defined(TARGET_AARCH64) - -struct target_sigcontext { - abi_ulong trap_no; - abi_ulong error_code; - abi_ulong oldmask; - abi_ulong arm_r0; - abi_ulong arm_r1; - abi_ulong arm_r2; - abi_ulong arm_r3; - abi_ulong arm_r4; - abi_ulong arm_r5; - abi_ulong arm_r6; - abi_ulong arm_r7; - abi_ulong arm_r8; - abi_ulong arm_r9; - abi_ulong arm_r10; - abi_ulong arm_fp; - abi_ulong arm_ip; - abi_ulong arm_sp; - abi_ulong arm_lr; - abi_ulong arm_pc; - abi_ulong arm_cpsr; - abi_ulong fault_address; -}; - -struct target_ucontext_v1 { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_ucontext_v2 { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ - char __unused[128 - sizeof(target_sigset_t)]; - abi_ulong tuc_regspace[128] __attribute__((__aligned__(8))); -}; - -struct target_user_vfp { - uint64_t fpregs[32]; - abi_ulong fpscr; -}; - -struct target_user_vfp_exc { - abi_ulong fpexc; - abi_ulong fpinst; - abi_ulong fpinst2; -}; - -struct target_vfp_sigframe { - abi_ulong magic; - abi_ulong size; - struct target_user_vfp ufp; - struct target_user_vfp_exc ufp_exc; -} __attribute__((__aligned__(8))); - -struct target_iwmmxt_sigframe { - abi_ulong magic; - abi_ulong size; - uint64_t regs[16]; - /* Note that not all the coprocessor control registers are stored here */ - uint32_t wcssf; - uint32_t wcasf; - uint32_t wcgr0; - uint32_t wcgr1; - uint32_t wcgr2; - uint32_t wcgr3; -} __attribute__((__aligned__(8))); - -#define TARGET_VFP_MAGIC 0x56465001 -#define TARGET_IWMMXT_MAGIC 0x12ef842a - -struct sigframe_v1 -{ - struct target_sigcontext sc; - abi_ulong extramask[TARGET_NSIG_WORDS-1]; - abi_ulong retcode; -}; - -struct sigframe_v2 -{ - struct target_ucontext_v2 uc; - abi_ulong retcode; -}; - -struct rt_sigframe_v1 -{ - abi_ulong pinfo; - abi_ulong puc; - struct target_siginfo info; - struct target_ucontext_v1 uc; - abi_ulong retcode; -}; - -struct rt_sigframe_v2 -{ - struct target_siginfo info; - struct target_ucontext_v2 uc; - abi_ulong retcode; -}; - -#define TARGET_CONFIG_CPU_32 1 - -/* - * For ARM syscalls, we encode the syscall number into the instruction. - */ -#define SWI_SYS_SIGRETURN (0xef000000|(TARGET_NR_sigreturn + ARM_SYSCALL_BASE)) -#define SWI_SYS_RT_SIGRETURN (0xef000000|(TARGET_NR_rt_sigreturn + ARM_SYSCALL_BASE)) - -/* - * For Thumb syscalls, we pass the syscall number via r7. We therefore - * need two 16-bit instructions. - */ -#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_sigreturn)) -#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_rt_sigreturn)) - -static const abi_ulong retcodes[4] = { - SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, - SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN -}; - - -static inline int valid_user_regs(CPUARMState *regs) -{ - return 1; -} - -static void -setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ - CPUARMState *env, abi_ulong mask) -{ - __put_user(env->regs[0], &sc->arm_r0); - __put_user(env->regs[1], &sc->arm_r1); - __put_user(env->regs[2], &sc->arm_r2); - __put_user(env->regs[3], &sc->arm_r3); - __put_user(env->regs[4], &sc->arm_r4); - __put_user(env->regs[5], &sc->arm_r5); - __put_user(env->regs[6], &sc->arm_r6); - __put_user(env->regs[7], &sc->arm_r7); - __put_user(env->regs[8], &sc->arm_r8); - __put_user(env->regs[9], &sc->arm_r9); - __put_user(env->regs[10], &sc->arm_r10); - __put_user(env->regs[11], &sc->arm_fp); - __put_user(env->regs[12], &sc->arm_ip); - __put_user(env->regs[13], &sc->arm_sp); - __put_user(env->regs[14], &sc->arm_lr); - __put_user(env->regs[15], &sc->arm_pc); -#ifdef TARGET_CONFIG_CPU_32 - __put_user(cpsr_read(env), &sc->arm_cpsr); -#endif - - __put_user(/* current->thread.trap_no */ 0, &sc->trap_no); - __put_user(/* current->thread.error_code */ 0, &sc->error_code); - __put_user(/* current->thread.address */ 0, &sc->fault_address); - __put_user(mask, &sc->oldmask); -} - -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) -{ - unsigned long sp = regs->regs[13]; - - /* - * This is the X/Open sanctioned signal stack switching. - */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - /* - * ATPCS B01 mandates 8-byte alignment - */ - return (sp - framesize) & ~7; -} - -static void -setup_return(CPUARMState *env, struct target_sigaction *ka, - abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr) -{ - abi_ulong handler = ka->_sa_handler; - abi_ulong retcode; - int thumb = handler & 1; - uint32_t cpsr = cpsr_read(env); - - cpsr &= ~CPSR_IT; - if (thumb) { - cpsr |= CPSR_T; - } else { - cpsr &= ~CPSR_T; - } - - if (ka->sa_flags & TARGET_SA_RESTORER) { - retcode = ka->sa_restorer; - } else { - unsigned int idx = thumb; - - if (ka->sa_flags & TARGET_SA_SIGINFO) { - idx += 2; - } - - __put_user(retcodes[idx], rc); - - retcode = rc_addr + thumb; - } - - env->regs[0] = usig; - env->regs[13] = frame_addr; - env->regs[14] = retcode; - env->regs[15] = handler & (thumb ? ~1 : ~3); - cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr); -} - -static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) -{ - int i; - struct target_vfp_sigframe *vfpframe; - vfpframe = (struct target_vfp_sigframe *)regspace; - __put_user(TARGET_VFP_MAGIC, &vfpframe->magic); - __put_user(sizeof(*vfpframe), &vfpframe->size); - for (i = 0; i < 32; i++) { - __put_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]); - } - __put_user(vfp_get_fpscr(env), &vfpframe->ufp.fpscr); - __put_user(env->vfp.xregs[ARM_VFP_FPEXC], &vfpframe->ufp_exc.fpexc); - __put_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst); - __put_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2); - return (abi_ulong*)(vfpframe+1); -} - -static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace, - CPUARMState *env) -{ - int i; - struct target_iwmmxt_sigframe *iwmmxtframe; - iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; - __put_user(TARGET_IWMMXT_MAGIC, &iwmmxtframe->magic); - __put_user(sizeof(*iwmmxtframe), &iwmmxtframe->size); - for (i = 0; i < 16; i++) { - __put_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); - } - __put_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); - return (abi_ulong*)(iwmmxtframe+1); -} - -static void setup_sigframe_v2(struct target_ucontext_v2 *uc, - target_sigset_t *set, CPUARMState *env) -{ - struct target_sigaltstack stack; - int i; - abi_ulong *regspace; - - /* Clear all the bits of the ucontext we don't use. */ - memset(uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext)); - - memset(&stack, 0, sizeof(stack)); - __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp); - __put_user(target_sigaltstack_used.ss_size, &stack.ss_size); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags); - memcpy(&uc->tuc_stack, &stack, sizeof(stack)); - - setup_sigcontext(&uc->tuc_mcontext, env, set->sig[0]); - /* Save coprocessor signal frame. */ - regspace = uc->tuc_regspace; - if (arm_feature(env, ARM_FEATURE_VFP)) { - regspace = setup_sigframe_v2_vfp(regspace, env); - } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - regspace = setup_sigframe_v2_iwmmxt(regspace, env); - } - - /* Write terminating magic word */ - __put_user(0, regspace); - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &uc->tuc_sigmask.sig[i]); - } -} - -/* compare linux/arch/arm/kernel/signal.c:setup_frame() */ -static void setup_frame_v1(int usig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *regs) -{ - struct sigframe_v1 *frame; - abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); - int i; - - trace_user_setup_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - setup_sigcontext(&frame->sc, regs, set->sig[0]); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - setup_return(regs, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct sigframe_v1, retcode)); - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - force_sigsegv(usig); -} - -static void setup_frame_v2(int usig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *regs) -{ - struct sigframe_v2 *frame; - abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); - - trace_user_setup_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - setup_sigframe_v2(&frame->uc, set, regs); - - setup_return(regs, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct sigframe_v2, retcode)); - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - force_sigsegv(usig); -} - -static void setup_frame(int usig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *regs) -{ - if (get_osversion() >= 0x020612) { - setup_frame_v2(usig, ka, set, regs); - } else { - setup_frame_v1(usig, ka, set, regs); - } -} - -/* compare linux/arch/arm/kernel/signal.c:setup_rt_frame() */ -static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUARMState *env) -{ - struct rt_sigframe_v1 *frame; - abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); - struct target_sigaltstack stack; - int i; - abi_ulong info_addr, uc_addr; - - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - info_addr = frame_addr + offsetof(struct rt_sigframe_v1, info); - __put_user(info_addr, &frame->pinfo); - uc_addr = frame_addr + offsetof(struct rt_sigframe_v1, uc); - __put_user(uc_addr, &frame->puc); - tswap_siginfo(&frame->info, info); - - /* Clear all the bits of the ucontext we don't use. */ - memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext)); - - memset(&stack, 0, sizeof(stack)); - __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp); - __put_user(target_sigaltstack_used.ss_size, &stack.ss_size); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags); - memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack)); - - setup_sigcontext(&frame->uc.tuc_mcontext, env, set->sig[0]); - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - setup_return(env, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe_v1, retcode)); - - env->regs[1] = info_addr; - env->regs[2] = uc_addr; - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - force_sigsegv(usig); -} - -static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUARMState *env) -{ - struct rt_sigframe_v2 *frame; - abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); - abi_ulong info_addr, uc_addr; - - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info); - uc_addr = frame_addr + offsetof(struct rt_sigframe_v2, uc); - tswap_siginfo(&frame->info, info); - - setup_sigframe_v2(&frame->uc, set, env); - - setup_return(env, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe_v2, retcode)); - - env->regs[1] = info_addr; - env->regs[2] = uc_addr; - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - force_sigsegv(usig); -} - -static void setup_rt_frame(int usig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUARMState *env) -{ - if (get_osversion() >= 0x020612) { - setup_rt_frame_v2(usig, ka, info, set, env); - } else { - setup_rt_frame_v1(usig, ka, info, set, env); - } -} - -static int -restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) -{ - int err = 0; - uint32_t cpsr; - - __get_user(env->regs[0], &sc->arm_r0); - __get_user(env->regs[1], &sc->arm_r1); - __get_user(env->regs[2], &sc->arm_r2); - __get_user(env->regs[3], &sc->arm_r3); - __get_user(env->regs[4], &sc->arm_r4); - __get_user(env->regs[5], &sc->arm_r5); - __get_user(env->regs[6], &sc->arm_r6); - __get_user(env->regs[7], &sc->arm_r7); - __get_user(env->regs[8], &sc->arm_r8); - __get_user(env->regs[9], &sc->arm_r9); - __get_user(env->regs[10], &sc->arm_r10); - __get_user(env->regs[11], &sc->arm_fp); - __get_user(env->regs[12], &sc->arm_ip); - __get_user(env->regs[13], &sc->arm_sp); - __get_user(env->regs[14], &sc->arm_lr); - __get_user(env->regs[15], &sc->arm_pc); -#ifdef TARGET_CONFIG_CPU_32 - __get_user(cpsr, &sc->arm_cpsr); - cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); -#endif - - err |= !valid_user_regs(env); - - return err; -} - -static long do_sigreturn_v1(CPUARMState *env) -{ - abi_ulong frame_addr; - struct sigframe_v1 *frame = NULL; - target_sigset_t set; - sigset_t host_set; - int i; - - /* - * Since we stacked the signal on a 64-bit boundary, - * then 'sp' should be word aligned here. If it's - * not, then the user is trying to mess with us. - */ - frame_addr = env->regs[13]; - trace_user_do_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - __get_user(set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(set.sig[i], &frame->extramask[i - 1]); - } - - target_to_host_sigset_internal(&host_set, &set); - set_sigmask(&host_set); - - if (restore_sigcontext(env, &frame->sc)) { - goto badframe; - } - -#if 0 - /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt(current)) - send_sig(SIGTRAP, current, 1); -#endif - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) -{ - int i; - abi_ulong magic, sz; - uint32_t fpscr, fpexc; - struct target_vfp_sigframe *vfpframe; - vfpframe = (struct target_vfp_sigframe *)regspace; - - __get_user(magic, &vfpframe->magic); - __get_user(sz, &vfpframe->size); - if (magic != TARGET_VFP_MAGIC || sz != sizeof(*vfpframe)) { - return 0; - } - for (i = 0; i < 32; i++) { - __get_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]); - } - __get_user(fpscr, &vfpframe->ufp.fpscr); - vfp_set_fpscr(env, fpscr); - __get_user(fpexc, &vfpframe->ufp_exc.fpexc); - /* Sanitise FPEXC: ensure VFP is enabled, FPINST2 is invalid - * and the exception flag is cleared - */ - fpexc |= (1 << 30); - fpexc &= ~((1 << 31) | (1 << 28)); - env->vfp.xregs[ARM_VFP_FPEXC] = fpexc; - __get_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst); - __get_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2); - return (abi_ulong*)(vfpframe + 1); -} - -static abi_ulong *restore_sigframe_v2_iwmmxt(CPUARMState *env, - abi_ulong *regspace) -{ - int i; - abi_ulong magic, sz; - struct target_iwmmxt_sigframe *iwmmxtframe; - iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; - - __get_user(magic, &iwmmxtframe->magic); - __get_user(sz, &iwmmxtframe->size); - if (magic != TARGET_IWMMXT_MAGIC || sz != sizeof(*iwmmxtframe)) { - return 0; - } - for (i = 0; i < 16; i++) { - __get_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); - } - __get_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); - return (abi_ulong*)(iwmmxtframe + 1); -} - -static int do_sigframe_return_v2(CPUARMState *env, - target_ulong context_addr, - struct target_ucontext_v2 *uc) -{ - sigset_t host_set; - abi_ulong *regspace; - - target_to_host_sigset(&host_set, &uc->tuc_sigmask); - set_sigmask(&host_set); - - if (restore_sigcontext(env, &uc->tuc_mcontext)) - return 1; - - /* Restore coprocessor signal frame */ - regspace = uc->tuc_regspace; - if (arm_feature(env, ARM_FEATURE_VFP)) { - regspace = restore_sigframe_v2_vfp(env, regspace); - if (!regspace) { - return 1; - } - } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - regspace = restore_sigframe_v2_iwmmxt(env, regspace); - if (!regspace) { - return 1; - } - } - - if (do_sigaltstack(context_addr - + offsetof(struct target_ucontext_v2, tuc_stack), - 0, get_sp_from_cpustate(env)) == -EFAULT) { - return 1; - } - -#if 0 - /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt(current)) - send_sig(SIGTRAP, current, 1); -#endif - - return 0; -} - -static long do_sigreturn_v2(CPUARMState *env) -{ - abi_ulong frame_addr; - struct sigframe_v2 *frame = NULL; - - /* - * Since we stacked the signal on a 64-bit boundary, - * then 'sp' should be word aligned here. If it's - * not, then the user is trying to mess with us. - */ - frame_addr = env->regs[13]; - trace_user_do_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - if (do_sigframe_return_v2(env, - frame_addr - + offsetof(struct sigframe_v2, uc), - &frame->uc)) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_sigreturn(CPUARMState *env) -{ - if (get_osversion() >= 0x020612) { - return do_sigreturn_v2(env); - } else { - return do_sigreturn_v1(env); - } -} - -static long do_rt_sigreturn_v1(CPUARMState *env) -{ - abi_ulong frame_addr; - struct rt_sigframe_v1 *frame = NULL; - sigset_t host_set; - - /* - * Since we stacked the signal on a 64-bit boundary, - * then 'sp' should be word aligned here. If it's - * not, then the user is trying to mess with us. - */ - frame_addr = env->regs[13]; - trace_user_do_rt_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask); - set_sigmask(&host_set); - - if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { - goto badframe; - } - - if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe_v1, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) - goto badframe; - -#if 0 - /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt(current)) - send_sig(SIGTRAP, current, 1); -#endif - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -static long do_rt_sigreturn_v2(CPUARMState *env) -{ - abi_ulong frame_addr; - struct rt_sigframe_v2 *frame = NULL; - - /* - * Since we stacked the signal on a 64-bit boundary, - * then 'sp' should be word aligned here. If it's - * not, then the user is trying to mess with us. - */ - frame_addr = env->regs[13]; - trace_user_do_rt_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - if (do_sigframe_return_v2(env, - frame_addr - + offsetof(struct rt_sigframe_v2, uc), - &frame->uc)) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUARMState *env) -{ - if (get_osversion() >= 0x020612) { - return do_rt_sigreturn_v2(env); - } else { - return do_rt_sigreturn_v1(env); - } -} - #elif defined(TARGET_SPARC) #define __SUNOS_MAXWIN 31 From 0f22162a4df4b477eb1a5a3c2b9906ace8128a4c Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:19 +0200 Subject: [PATCH 0129/2380] linux-user: move sh4 signal.c parts to sh4 directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to sh4/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-5-laurent@vivier.eu> --- linux-user/sh4/signal.c | 332 +++++++++++++++++++++++++++++++++ linux-user/sh4/target_signal.h | 5 + linux-user/signal.c | 328 -------------------------------- 3 files changed, 337 insertions(+), 328 deletions(-) diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index 02ca338b6c..5ce182aff7 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -16,3 +16,335 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +/* + * code and data structures from linux kernel: + * include/asm-sh/sigcontext.h + * arch/sh/kernel/signal.c + */ + +struct target_sigcontext { + target_ulong oldmask; + + /* CPU registers */ + target_ulong sc_gregs[16]; + target_ulong sc_pc; + target_ulong sc_pr; + target_ulong sc_sr; + target_ulong sc_gbr; + target_ulong sc_mach; + target_ulong sc_macl; + + /* FPU registers */ + target_ulong sc_fpregs[16]; + target_ulong sc_xfpregs[16]; + unsigned int sc_fpscr; + unsigned int sc_fpul; + unsigned int sc_ownedfp; +}; + +struct target_sigframe +{ + struct target_sigcontext sc; + target_ulong extramask[TARGET_NSIG_WORDS-1]; + uint16_t retcode[3]; +}; + + +struct target_ucontext { + target_ulong tuc_flags; + struct target_ucontext *tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +struct target_rt_sigframe +{ + struct target_siginfo info; + struct target_ucontext uc; + uint16_t retcode[3]; +}; + + +#define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */ +#define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) SH3/4 */ + +static abi_ulong get_sigframe(struct target_sigaction *ka, + unsigned long sp, size_t frame_size) +{ + if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + return (sp - frame_size) & -8ul; +} + +/* Notice when we're in the middle of a gUSA region and reset. + Note that this will only occur for !parallel_cpus, as we will + translate such sequences differently in a parallel context. */ +static void unwind_gusa(CPUSH4State *regs) +{ + /* If the stack pointer is sufficiently negative, and we haven't + completed the sequence, then reset to the entry to the region. */ + /* ??? The SH4 kernel checks for and address above 0xC0000000. + However, the page mappings in qemu linux-user aren't as restricted + and we wind up with the normal stack mapped above 0xF0000000. + That said, there is no reason why the kernel should be allowing + a gUSA region that spans 1GB. Use a tighter check here, for what + can actually be enabled by the immediate move. */ + if (regs->gregs[15] >= -128u && regs->pc < regs->gregs[0]) { + /* Reset the PC to before the gUSA region, as computed from + R0 = region end, SP = -(region size), plus one more for the + insn that actually initializes SP to the region size. */ + regs->pc = regs->gregs[0] + regs->gregs[15] - 2; + + /* Reset the SP to the saved version in R1. */ + regs->gregs[15] = regs->gregs[1]; + } +} + +static void setup_sigcontext(struct target_sigcontext *sc, + CPUSH4State *regs, unsigned long mask) +{ + int i; + +#define COPY(x) __put_user(regs->x, &sc->sc_##x) + COPY(gregs[0]); COPY(gregs[1]); + COPY(gregs[2]); COPY(gregs[3]); + COPY(gregs[4]); COPY(gregs[5]); + COPY(gregs[6]); COPY(gregs[7]); + COPY(gregs[8]); COPY(gregs[9]); + COPY(gregs[10]); COPY(gregs[11]); + COPY(gregs[12]); COPY(gregs[13]); + COPY(gregs[14]); COPY(gregs[15]); + COPY(gbr); COPY(mach); + COPY(macl); COPY(pr); + COPY(sr); COPY(pc); +#undef COPY + + for (i=0; i<16; i++) { + __put_user(regs->fregs[i], &sc->sc_fpregs[i]); + } + __put_user(regs->fpscr, &sc->sc_fpscr); + __put_user(regs->fpul, &sc->sc_fpul); + + /* non-iBCS2 extensions.. */ + __put_user(mask, &sc->oldmask); +} + +static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc) +{ + int i; + +#define COPY(x) __get_user(regs->x, &sc->sc_##x) + COPY(gregs[0]); COPY(gregs[1]); + COPY(gregs[2]); COPY(gregs[3]); + COPY(gregs[4]); COPY(gregs[5]); + COPY(gregs[6]); COPY(gregs[7]); + COPY(gregs[8]); COPY(gregs[9]); + COPY(gregs[10]); COPY(gregs[11]); + COPY(gregs[12]); COPY(gregs[13]); + COPY(gregs[14]); COPY(gregs[15]); + COPY(gbr); COPY(mach); + COPY(macl); COPY(pr); + COPY(sr); COPY(pc); +#undef COPY + + for (i=0; i<16; i++) { + __get_user(regs->fregs[i], &sc->sc_fpregs[i]); + } + __get_user(regs->fpscr, &sc->sc_fpscr); + __get_user(regs->fpul, &sc->sc_fpul); + + regs->tra = -1; /* disable syscall checks */ + regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUSH4State *regs) +{ + struct target_sigframe *frame; + abi_ulong frame_addr; + int i; + + unwind_gusa(regs); + + frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); + trace_user_setup_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + setup_sigcontext(&frame->sc, regs, set->sig[0]); + + for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { + __put_user(set->sig[i + 1], &frame->extramask[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + regs->pr = (unsigned long) ka->sa_restorer; + } else { + /* Generate return code (system call to sigreturn) */ + abi_ulong retcode_addr = frame_addr + + offsetof(struct target_sigframe, retcode); + __put_user(MOVW(2), &frame->retcode[0]); + __put_user(TRAP_NOARG, &frame->retcode[1]); + __put_user((TARGET_NR_sigreturn), &frame->retcode[2]); + regs->pr = (unsigned long) retcode_addr; + } + + /* Set up registers for signal handler */ + regs->gregs[15] = frame_addr; + regs->gregs[4] = sig; /* Arg for signal handler */ + regs->gregs[5] = 0; + regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc); + regs->pc = (unsigned long) ka->_sa_handler; + regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUSH4State *regs) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + int i; + + unwind_gusa(regs); + + frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); + trace_user_setup_rt_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + tswap_siginfo(&frame->info, info); + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, (unsigned long *)&frame->uc.tuc_link); + __put_user((unsigned long)target_sigaltstack_used.ss_sp, + &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(regs->gregs[15]), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, + &frame->uc.tuc_stack.ss_size); + setup_sigcontext(&frame->uc.tuc_mcontext, + regs, set->sig[0]); + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + regs->pr = (unsigned long) ka->sa_restorer; + } else { + /* Generate return code (system call to sigreturn) */ + abi_ulong retcode_addr = frame_addr + + offsetof(struct target_rt_sigframe, retcode); + __put_user(MOVW(2), &frame->retcode[0]); + __put_user(TRAP_NOARG, &frame->retcode[1]); + __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]); + regs->pr = (unsigned long) retcode_addr; + } + + /* Set up registers for signal handler */ + regs->gregs[15] = frame_addr; + regs->gregs[4] = sig; /* Arg for signal handler */ + regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info); + regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc); + regs->pc = (unsigned long) ka->_sa_handler; + regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} + +long do_sigreturn(CPUSH4State *regs) +{ + struct target_sigframe *frame; + abi_ulong frame_addr; + sigset_t blocked; + target_sigset_t target_set; + int i; + int err = 0; + + frame_addr = regs->gregs[15]; + trace_user_do_sigreturn(regs, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + __get_user(target_set.sig[0], &frame->sc.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } + + if (err) + goto badframe; + + target_to_host_sigset_internal(&blocked, &target_set); + set_sigmask(&blocked); + + restore_sigcontext(regs, &frame->sc); + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUSH4State *regs) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + sigset_t blocked; + + frame_addr = regs->gregs[15]; + trace_user_do_rt_sigreturn(regs, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask); + set_sigmask(&blocked); + + restore_sigcontext(regs, &frame->uc.tuc_mcontext); + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, get_sp_from_cpustate(regs)) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h index cbf23b6a31..a2a93fa04a 100644 --- a/linux-user/sh4/target_signal.h +++ b/linux-user/sh4/target_signal.h @@ -26,5 +26,10 @@ static inline abi_ulong get_sp_from_cpustate(CPUSH4State *state) return state->gregs[15]; } +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUSH4State *regs); +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUSH4State *regs); #endif /* SH4_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index c99eae15d6..5429c9aa38 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -2364,334 +2364,6 @@ badframe: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_SH4) - -/* - * code and data structures from linux kernel: - * include/asm-sh/sigcontext.h - * arch/sh/kernel/signal.c - */ - -struct target_sigcontext { - target_ulong oldmask; - - /* CPU registers */ - target_ulong sc_gregs[16]; - target_ulong sc_pc; - target_ulong sc_pr; - target_ulong sc_sr; - target_ulong sc_gbr; - target_ulong sc_mach; - target_ulong sc_macl; - - /* FPU registers */ - target_ulong sc_fpregs[16]; - target_ulong sc_xfpregs[16]; - unsigned int sc_fpscr; - unsigned int sc_fpul; - unsigned int sc_ownedfp; -}; - -struct target_sigframe -{ - struct target_sigcontext sc; - target_ulong extramask[TARGET_NSIG_WORDS-1]; - uint16_t retcode[3]; -}; - - -struct target_ucontext { - target_ulong tuc_flags; - struct target_ucontext *tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_rt_sigframe -{ - struct target_siginfo info; - struct target_ucontext uc; - uint16_t retcode[3]; -}; - - -#define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */ -#define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) SH3/4 */ - -static abi_ulong get_sigframe(struct target_sigaction *ka, - unsigned long sp, size_t frame_size) -{ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - return (sp - frame_size) & -8ul; -} - -/* Notice when we're in the middle of a gUSA region and reset. - Note that this will only occur for !parallel_cpus, as we will - translate such sequences differently in a parallel context. */ -static void unwind_gusa(CPUSH4State *regs) -{ - /* If the stack pointer is sufficiently negative, and we haven't - completed the sequence, then reset to the entry to the region. */ - /* ??? The SH4 kernel checks for and address above 0xC0000000. - However, the page mappings in qemu linux-user aren't as restricted - and we wind up with the normal stack mapped above 0xF0000000. - That said, there is no reason why the kernel should be allowing - a gUSA region that spans 1GB. Use a tighter check here, for what - can actually be enabled by the immediate move. */ - if (regs->gregs[15] >= -128u && regs->pc < regs->gregs[0]) { - /* Reset the PC to before the gUSA region, as computed from - R0 = region end, SP = -(region size), plus one more for the - insn that actually initializes SP to the region size. */ - regs->pc = regs->gregs[0] + regs->gregs[15] - 2; - - /* Reset the SP to the saved version in R1. */ - regs->gregs[15] = regs->gregs[1]; - } -} - -static void setup_sigcontext(struct target_sigcontext *sc, - CPUSH4State *regs, unsigned long mask) -{ - int i; - -#define COPY(x) __put_user(regs->x, &sc->sc_##x) - COPY(gregs[0]); COPY(gregs[1]); - COPY(gregs[2]); COPY(gregs[3]); - COPY(gregs[4]); COPY(gregs[5]); - COPY(gregs[6]); COPY(gregs[7]); - COPY(gregs[8]); COPY(gregs[9]); - COPY(gregs[10]); COPY(gregs[11]); - COPY(gregs[12]); COPY(gregs[13]); - COPY(gregs[14]); COPY(gregs[15]); - COPY(gbr); COPY(mach); - COPY(macl); COPY(pr); - COPY(sr); COPY(pc); -#undef COPY - - for (i=0; i<16; i++) { - __put_user(regs->fregs[i], &sc->sc_fpregs[i]); - } - __put_user(regs->fpscr, &sc->sc_fpscr); - __put_user(regs->fpul, &sc->sc_fpul); - - /* non-iBCS2 extensions.. */ - __put_user(mask, &sc->oldmask); -} - -static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc) -{ - int i; - -#define COPY(x) __get_user(regs->x, &sc->sc_##x) - COPY(gregs[0]); COPY(gregs[1]); - COPY(gregs[2]); COPY(gregs[3]); - COPY(gregs[4]); COPY(gregs[5]); - COPY(gregs[6]); COPY(gregs[7]); - COPY(gregs[8]); COPY(gregs[9]); - COPY(gregs[10]); COPY(gregs[11]); - COPY(gregs[12]); COPY(gregs[13]); - COPY(gregs[14]); COPY(gregs[15]); - COPY(gbr); COPY(mach); - COPY(macl); COPY(pr); - COPY(sr); COPY(pc); -#undef COPY - - for (i=0; i<16; i++) { - __get_user(regs->fregs[i], &sc->sc_fpregs[i]); - } - __get_user(regs->fpscr, &sc->sc_fpscr); - __get_user(regs->fpul, &sc->sc_fpul); - - regs->tra = -1; /* disable syscall checks */ - regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUSH4State *regs) -{ - struct target_sigframe *frame; - abi_ulong frame_addr; - int i; - - unwind_gusa(regs); - - frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); - trace_user_setup_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - setup_sigcontext(&frame->sc, regs, set->sig[0]); - - for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { - __put_user(set->sig[i + 1], &frame->extramask[i]); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - regs->pr = (unsigned long) ka->sa_restorer; - } else { - /* Generate return code (system call to sigreturn) */ - abi_ulong retcode_addr = frame_addr + - offsetof(struct target_sigframe, retcode); - __put_user(MOVW(2), &frame->retcode[0]); - __put_user(TRAP_NOARG, &frame->retcode[1]); - __put_user((TARGET_NR_sigreturn), &frame->retcode[2]); - regs->pr = (unsigned long) retcode_addr; - } - - /* Set up registers for signal handler */ - regs->gregs[15] = frame_addr; - regs->gregs[4] = sig; /* Arg for signal handler */ - regs->gregs[5] = 0; - regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc); - regs->pc = (unsigned long) ka->_sa_handler; - regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); - - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUSH4State *regs) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - int i; - - unwind_gusa(regs); - - frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); - trace_user_setup_rt_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - tswap_siginfo(&frame->info, info); - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, (unsigned long *)&frame->uc.tuc_link); - __put_user((unsigned long)target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(regs->gregs[15]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - setup_sigcontext(&frame->uc.tuc_mcontext, - regs, set->sig[0]); - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - regs->pr = (unsigned long) ka->sa_restorer; - } else { - /* Generate return code (system call to sigreturn) */ - abi_ulong retcode_addr = frame_addr + - offsetof(struct target_rt_sigframe, retcode); - __put_user(MOVW(2), &frame->retcode[0]); - __put_user(TRAP_NOARG, &frame->retcode[1]); - __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]); - regs->pr = (unsigned long) retcode_addr; - } - - /* Set up registers for signal handler */ - regs->gregs[15] = frame_addr; - regs->gregs[4] = sig; /* Arg for signal handler */ - regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info); - regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc); - regs->pc = (unsigned long) ka->_sa_handler; - regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); - - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} - -long do_sigreturn(CPUSH4State *regs) -{ - struct target_sigframe *frame; - abi_ulong frame_addr; - sigset_t blocked; - target_sigset_t target_set; - int i; - int err = 0; - - frame_addr = regs->gregs[15]; - trace_user_do_sigreturn(regs, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - __get_user(target_set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - - if (err) - goto badframe; - - target_to_host_sigset_internal(&blocked, &target_set); - set_sigmask(&blocked); - - restore_sigcontext(regs, &frame->sc); - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUSH4State *regs) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - sigset_t blocked; - - frame_addr = regs->gregs[15]; - trace_user_do_rt_sigreturn(regs, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask); - set_sigmask(&blocked); - - restore_sigcontext(regs, &frame->uc.tuc_mcontext); - - if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, get_sp_from_cpustate(regs)) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} #elif defined(TARGET_MICROBLAZE) struct target_sigcontext { From f9fb3ba359637f73f1c2009b1447a994e5becee8 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:20 +0200 Subject: [PATCH 0130/2380] linux-user: move microblaze signal.c parts to microblaze directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to microblaze/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-6-laurent@vivier.eu> --- linux-user/microblaze/signal.c | 230 ++++++++++++++++++++++++++ linux-user/microblaze/target_signal.h | 6 +- linux-user/signal.c | 227 ------------------------- 3 files changed, 235 insertions(+), 228 deletions(-) diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index 02ca338b6c..5572baa7dc 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -16,3 +16,233 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + struct target_pt_regs regs; /* needs to be first */ + uint32_t oldmask; +}; + +struct target_stack_t { + abi_ulong ss_sp; + int ss_flags; + unsigned int ss_size; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + struct target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + uint32_t tuc_extramask[TARGET_NSIG_WORDS - 1]; +}; + +/* Signal frames. */ +struct target_signal_frame { + struct target_ucontext uc; + uint32_t extramask[TARGET_NSIG_WORDS - 1]; + uint32_t tramp[2]; +}; + +struct rt_signal_frame { + siginfo_t info; + ucontext_t uc; + uint32_t tramp[2]; +}; + +static void setup_sigcontext(struct target_sigcontext *sc, CPUMBState *env) +{ + __put_user(env->regs[0], &sc->regs.r0); + __put_user(env->regs[1], &sc->regs.r1); + __put_user(env->regs[2], &sc->regs.r2); + __put_user(env->regs[3], &sc->regs.r3); + __put_user(env->regs[4], &sc->regs.r4); + __put_user(env->regs[5], &sc->regs.r5); + __put_user(env->regs[6], &sc->regs.r6); + __put_user(env->regs[7], &sc->regs.r7); + __put_user(env->regs[8], &sc->regs.r8); + __put_user(env->regs[9], &sc->regs.r9); + __put_user(env->regs[10], &sc->regs.r10); + __put_user(env->regs[11], &sc->regs.r11); + __put_user(env->regs[12], &sc->regs.r12); + __put_user(env->regs[13], &sc->regs.r13); + __put_user(env->regs[14], &sc->regs.r14); + __put_user(env->regs[15], &sc->regs.r15); + __put_user(env->regs[16], &sc->regs.r16); + __put_user(env->regs[17], &sc->regs.r17); + __put_user(env->regs[18], &sc->regs.r18); + __put_user(env->regs[19], &sc->regs.r19); + __put_user(env->regs[20], &sc->regs.r20); + __put_user(env->regs[21], &sc->regs.r21); + __put_user(env->regs[22], &sc->regs.r22); + __put_user(env->regs[23], &sc->regs.r23); + __put_user(env->regs[24], &sc->regs.r24); + __put_user(env->regs[25], &sc->regs.r25); + __put_user(env->regs[26], &sc->regs.r26); + __put_user(env->regs[27], &sc->regs.r27); + __put_user(env->regs[28], &sc->regs.r28); + __put_user(env->regs[29], &sc->regs.r29); + __put_user(env->regs[30], &sc->regs.r30); + __put_user(env->regs[31], &sc->regs.r31); + __put_user(env->sregs[SR_PC], &sc->regs.pc); +} + +static void restore_sigcontext(struct target_sigcontext *sc, CPUMBState *env) +{ + __get_user(env->regs[0], &sc->regs.r0); + __get_user(env->regs[1], &sc->regs.r1); + __get_user(env->regs[2], &sc->regs.r2); + __get_user(env->regs[3], &sc->regs.r3); + __get_user(env->regs[4], &sc->regs.r4); + __get_user(env->regs[5], &sc->regs.r5); + __get_user(env->regs[6], &sc->regs.r6); + __get_user(env->regs[7], &sc->regs.r7); + __get_user(env->regs[8], &sc->regs.r8); + __get_user(env->regs[9], &sc->regs.r9); + __get_user(env->regs[10], &sc->regs.r10); + __get_user(env->regs[11], &sc->regs.r11); + __get_user(env->regs[12], &sc->regs.r12); + __get_user(env->regs[13], &sc->regs.r13); + __get_user(env->regs[14], &sc->regs.r14); + __get_user(env->regs[15], &sc->regs.r15); + __get_user(env->regs[16], &sc->regs.r16); + __get_user(env->regs[17], &sc->regs.r17); + __get_user(env->regs[18], &sc->regs.r18); + __get_user(env->regs[19], &sc->regs.r19); + __get_user(env->regs[20], &sc->regs.r20); + __get_user(env->regs[21], &sc->regs.r21); + __get_user(env->regs[22], &sc->regs.r22); + __get_user(env->regs[23], &sc->regs.r23); + __get_user(env->regs[24], &sc->regs.r24); + __get_user(env->regs[25], &sc->regs.r25); + __get_user(env->regs[26], &sc->regs.r26); + __get_user(env->regs[27], &sc->regs.r27); + __get_user(env->regs[28], &sc->regs.r28); + __get_user(env->regs[29], &sc->regs.r29); + __get_user(env->regs[30], &sc->regs.r30); + __get_user(env->regs[31], &sc->regs.r31); + __get_user(env->sregs[SR_PC], &sc->regs.pc); +} + +static abi_ulong get_sigframe(struct target_sigaction *ka, + CPUMBState *env, int frame_size) +{ + abi_ulong sp = env->regs[1]; + + if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !on_sig_stack(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + return ((sp - frame_size) & -8UL); +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUMBState *env) +{ + struct target_signal_frame *frame; + abi_ulong frame_addr; + int i; + + frame_addr = get_sigframe(ka, env, sizeof *frame); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto badframe; + + /* Save the mask. */ + __put_user(set->sig[0], &frame->uc.tuc_mcontext.oldmask); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->extramask[i - 1]); + } + + setup_sigcontext(&frame->uc.tuc_mcontext, env); + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + /* minus 8 is offset to cater for "rtsd r15,8" offset */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + env->regs[15] = ((unsigned long)ka->sa_restorer)-8; + } else { + uint32_t t; + /* Note, these encodings are _big endian_! */ + /* addi r12, r0, __NR_sigreturn */ + t = 0x31800000UL | TARGET_NR_sigreturn; + __put_user(t, frame->tramp + 0); + /* brki r14, 0x8 */ + t = 0xb9cc0008UL; + __put_user(t, frame->tramp + 1); + + /* Return from sighandler will jump to the tramp. + Negative 8 offset because return is rtsd r15, 8 */ + env->regs[15] = frame_addr + offsetof(struct target_signal_frame, tramp) + - 8; + } + + /* Set up registers for signal handler */ + env->regs[1] = frame_addr; + /* Signal handler args: */ + env->regs[5] = sig; /* Arg 0: signum */ + env->regs[6] = 0; + /* arg 1: sigcontext */ + env->regs[7] = frame_addr += offsetof(typeof(*frame), uc); + + /* Offset of 4 to handle microblaze rtid r14, 0 */ + env->sregs[SR_PC] = (unsigned long)ka->_sa_handler; + + unlock_user_struct(frame, frame_addr, 1); + return; +badframe: + force_sigsegv(sig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUMBState *env) +{ + fprintf(stderr, "Microblaze setup_rt_frame: not implemented\n"); +} + +long do_sigreturn(CPUMBState *env) +{ + struct target_signal_frame *frame; + abi_ulong frame_addr; + target_sigset_t target_set; + sigset_t set; + int i; + + frame_addr = env->regs[R_SP]; + trace_user_do_sigreturn(env, frame_addr); + /* Make sure the guest isn't playing games. */ + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) + goto badframe; + + /* Restore blocked signals */ + __get_user(target_set.sig[0], &frame->uc.tuc_mcontext.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + restore_sigcontext(&frame->uc.tuc_mcontext, env); + /* We got here through a sigreturn syscall, our path back is via an + rtb insn so setup r14 for that. */ + env->regs[14] = env->sregs[SR_PC]; + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUMBState *env) +{ + trace_user_do_rt_sigreturn(env, 0); + fprintf(stderr, "Microblaze do_rt_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h index 642865f12e..02c5cc56fa 100644 --- a/linux-user/microblaze/target_signal.h +++ b/linux-user/microblaze/target_signal.h @@ -26,5 +26,9 @@ static inline abi_ulong get_sp_from_cpustate(CPUMBState *state) return state->regs[1]; } - +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUMBState *env); +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUMBState *env); #endif /* MICROBLAZE_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 5429c9aa38..546311221b 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -2364,233 +2364,6 @@ badframe: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_MICROBLAZE) - -struct target_sigcontext { - struct target_pt_regs regs; /* needs to be first */ - uint32_t oldmask; -}; - -struct target_stack_t { - abi_ulong ss_sp; - int ss_flags; - unsigned int ss_size; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - struct target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - uint32_t tuc_extramask[TARGET_NSIG_WORDS - 1]; -}; - -/* Signal frames. */ -struct target_signal_frame { - struct target_ucontext uc; - uint32_t extramask[TARGET_NSIG_WORDS - 1]; - uint32_t tramp[2]; -}; - -struct rt_signal_frame { - siginfo_t info; - ucontext_t uc; - uint32_t tramp[2]; -}; - -static void setup_sigcontext(struct target_sigcontext *sc, CPUMBState *env) -{ - __put_user(env->regs[0], &sc->regs.r0); - __put_user(env->regs[1], &sc->regs.r1); - __put_user(env->regs[2], &sc->regs.r2); - __put_user(env->regs[3], &sc->regs.r3); - __put_user(env->regs[4], &sc->regs.r4); - __put_user(env->regs[5], &sc->regs.r5); - __put_user(env->regs[6], &sc->regs.r6); - __put_user(env->regs[7], &sc->regs.r7); - __put_user(env->regs[8], &sc->regs.r8); - __put_user(env->regs[9], &sc->regs.r9); - __put_user(env->regs[10], &sc->regs.r10); - __put_user(env->regs[11], &sc->regs.r11); - __put_user(env->regs[12], &sc->regs.r12); - __put_user(env->regs[13], &sc->regs.r13); - __put_user(env->regs[14], &sc->regs.r14); - __put_user(env->regs[15], &sc->regs.r15); - __put_user(env->regs[16], &sc->regs.r16); - __put_user(env->regs[17], &sc->regs.r17); - __put_user(env->regs[18], &sc->regs.r18); - __put_user(env->regs[19], &sc->regs.r19); - __put_user(env->regs[20], &sc->regs.r20); - __put_user(env->regs[21], &sc->regs.r21); - __put_user(env->regs[22], &sc->regs.r22); - __put_user(env->regs[23], &sc->regs.r23); - __put_user(env->regs[24], &sc->regs.r24); - __put_user(env->regs[25], &sc->regs.r25); - __put_user(env->regs[26], &sc->regs.r26); - __put_user(env->regs[27], &sc->regs.r27); - __put_user(env->regs[28], &sc->regs.r28); - __put_user(env->regs[29], &sc->regs.r29); - __put_user(env->regs[30], &sc->regs.r30); - __put_user(env->regs[31], &sc->regs.r31); - __put_user(env->sregs[SR_PC], &sc->regs.pc); -} - -static void restore_sigcontext(struct target_sigcontext *sc, CPUMBState *env) -{ - __get_user(env->regs[0], &sc->regs.r0); - __get_user(env->regs[1], &sc->regs.r1); - __get_user(env->regs[2], &sc->regs.r2); - __get_user(env->regs[3], &sc->regs.r3); - __get_user(env->regs[4], &sc->regs.r4); - __get_user(env->regs[5], &sc->regs.r5); - __get_user(env->regs[6], &sc->regs.r6); - __get_user(env->regs[7], &sc->regs.r7); - __get_user(env->regs[8], &sc->regs.r8); - __get_user(env->regs[9], &sc->regs.r9); - __get_user(env->regs[10], &sc->regs.r10); - __get_user(env->regs[11], &sc->regs.r11); - __get_user(env->regs[12], &sc->regs.r12); - __get_user(env->regs[13], &sc->regs.r13); - __get_user(env->regs[14], &sc->regs.r14); - __get_user(env->regs[15], &sc->regs.r15); - __get_user(env->regs[16], &sc->regs.r16); - __get_user(env->regs[17], &sc->regs.r17); - __get_user(env->regs[18], &sc->regs.r18); - __get_user(env->regs[19], &sc->regs.r19); - __get_user(env->regs[20], &sc->regs.r20); - __get_user(env->regs[21], &sc->regs.r21); - __get_user(env->regs[22], &sc->regs.r22); - __get_user(env->regs[23], &sc->regs.r23); - __get_user(env->regs[24], &sc->regs.r24); - __get_user(env->regs[25], &sc->regs.r25); - __get_user(env->regs[26], &sc->regs.r26); - __get_user(env->regs[27], &sc->regs.r27); - __get_user(env->regs[28], &sc->regs.r28); - __get_user(env->regs[29], &sc->regs.r29); - __get_user(env->regs[30], &sc->regs.r30); - __get_user(env->regs[31], &sc->regs.r31); - __get_user(env->sregs[SR_PC], &sc->regs.pc); -} - -static abi_ulong get_sigframe(struct target_sigaction *ka, - CPUMBState *env, int frame_size) -{ - abi_ulong sp = env->regs[1]; - - if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !on_sig_stack(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - return ((sp - frame_size) & -8UL); -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUMBState *env) -{ - struct target_signal_frame *frame; - abi_ulong frame_addr; - int i; - - frame_addr = get_sigframe(ka, env, sizeof *frame); - trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto badframe; - - /* Save the mask. */ - __put_user(set->sig[0], &frame->uc.tuc_mcontext.oldmask); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - setup_sigcontext(&frame->uc.tuc_mcontext, env); - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - /* minus 8 is offset to cater for "rtsd r15,8" offset */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - env->regs[15] = ((unsigned long)ka->sa_restorer)-8; - } else { - uint32_t t; - /* Note, these encodings are _big endian_! */ - /* addi r12, r0, __NR_sigreturn */ - t = 0x31800000UL | TARGET_NR_sigreturn; - __put_user(t, frame->tramp + 0); - /* brki r14, 0x8 */ - t = 0xb9cc0008UL; - __put_user(t, frame->tramp + 1); - - /* Return from sighandler will jump to the tramp. - Negative 8 offset because return is rtsd r15, 8 */ - env->regs[15] = frame_addr + offsetof(struct target_signal_frame, tramp) - - 8; - } - - /* Set up registers for signal handler */ - env->regs[1] = frame_addr; - /* Signal handler args: */ - env->regs[5] = sig; /* Arg 0: signum */ - env->regs[6] = 0; - /* arg 1: sigcontext */ - env->regs[7] = frame_addr += offsetof(typeof(*frame), uc); - - /* Offset of 4 to handle microblaze rtid r14, 0 */ - env->sregs[SR_PC] = (unsigned long)ka->_sa_handler; - - unlock_user_struct(frame, frame_addr, 1); - return; -badframe: - force_sigsegv(sig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUMBState *env) -{ - fprintf(stderr, "Microblaze setup_rt_frame: not implemented\n"); -} - -long do_sigreturn(CPUMBState *env) -{ - struct target_signal_frame *frame; - abi_ulong frame_addr; - target_sigset_t target_set; - sigset_t set; - int i; - - frame_addr = env->regs[R_SP]; - trace_user_do_sigreturn(env, frame_addr); - /* Make sure the guest isn't playing games. */ - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) - goto badframe; - - /* Restore blocked signals */ - __get_user(target_set.sig[0], &frame->uc.tuc_mcontext.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - restore_sigcontext(&frame->uc.tuc_mcontext, env); - /* We got here through a sigreturn syscall, our path back is via an - rtb insn so setup r14 for that. */ - env->regs[14] = env->sregs[SR_PC]; - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUMBState *env) -{ - trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "Microblaze do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - #elif defined(TARGET_CRIS) struct target_sigcontext { From 6aa72d7e3ce39c02f808436dbef5533fdb037e1b Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:21 +0200 Subject: [PATCH 0131/2380] linux-user: move cris signal.c parts to cris directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to cris/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-7-laurent@vivier.eu> --- linux-user/cris/signal.c | 171 ++++++++++++++++++++++++++++++++ linux-user/cris/target_signal.h | 6 +- linux-user/signal.c | 168 ------------------------------- 3 files changed, 176 insertions(+), 169 deletions(-) diff --git a/linux-user/cris/signal.c b/linux-user/cris/signal.c index 02ca338b6c..322d9db1a7 100644 --- a/linux-user/cris/signal.c +++ b/linux-user/cris/signal.c @@ -16,3 +16,174 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + struct target_pt_regs regs; /* needs to be first */ + uint32_t oldmask; + uint32_t usp; /* usp before stacking this gunk on it */ +}; + +/* Signal frames. */ +struct target_signal_frame { + struct target_sigcontext sc; + uint32_t extramask[TARGET_NSIG_WORDS - 1]; + uint16_t retcode[4]; /* Trampoline code. */ +}; + +struct rt_signal_frame { + siginfo_t *pinfo; + void *puc; + siginfo_t info; + ucontext_t uc; + uint16_t retcode[4]; /* Trampoline code. */ +}; + +static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) +{ + __put_user(env->regs[0], &sc->regs.r0); + __put_user(env->regs[1], &sc->regs.r1); + __put_user(env->regs[2], &sc->regs.r2); + __put_user(env->regs[3], &sc->regs.r3); + __put_user(env->regs[4], &sc->regs.r4); + __put_user(env->regs[5], &sc->regs.r5); + __put_user(env->regs[6], &sc->regs.r6); + __put_user(env->regs[7], &sc->regs.r7); + __put_user(env->regs[8], &sc->regs.r8); + __put_user(env->regs[9], &sc->regs.r9); + __put_user(env->regs[10], &sc->regs.r10); + __put_user(env->regs[11], &sc->regs.r11); + __put_user(env->regs[12], &sc->regs.r12); + __put_user(env->regs[13], &sc->regs.r13); + __put_user(env->regs[14], &sc->usp); + __put_user(env->regs[15], &sc->regs.acr); + __put_user(env->pregs[PR_MOF], &sc->regs.mof); + __put_user(env->pregs[PR_SRP], &sc->regs.srp); + __put_user(env->pc, &sc->regs.erp); +} + +static void restore_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) +{ + __get_user(env->regs[0], &sc->regs.r0); + __get_user(env->regs[1], &sc->regs.r1); + __get_user(env->regs[2], &sc->regs.r2); + __get_user(env->regs[3], &sc->regs.r3); + __get_user(env->regs[4], &sc->regs.r4); + __get_user(env->regs[5], &sc->regs.r5); + __get_user(env->regs[6], &sc->regs.r6); + __get_user(env->regs[7], &sc->regs.r7); + __get_user(env->regs[8], &sc->regs.r8); + __get_user(env->regs[9], &sc->regs.r9); + __get_user(env->regs[10], &sc->regs.r10); + __get_user(env->regs[11], &sc->regs.r11); + __get_user(env->regs[12], &sc->regs.r12); + __get_user(env->regs[13], &sc->regs.r13); + __get_user(env->regs[14], &sc->usp); + __get_user(env->regs[15], &sc->regs.acr); + __get_user(env->pregs[PR_MOF], &sc->regs.mof); + __get_user(env->pregs[PR_SRP], &sc->regs.srp); + __get_user(env->pc, &sc->regs.erp); +} + +static abi_ulong get_sigframe(CPUCRISState *env, int framesize) +{ + abi_ulong sp; + /* Align the stack downwards to 4. */ + sp = (env->regs[R_SP] & ~3); + return sp - framesize; +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUCRISState *env) +{ + struct target_signal_frame *frame; + abi_ulong frame_addr; + int i; + + frame_addr = get_sigframe(env, sizeof *frame); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto badframe; + + /* + * The CRIS signal return trampoline. A real linux/CRIS kernel doesn't + * use this trampoline anymore but it sets it up for GDB. + * In QEMU, using the trampoline simplifies things a bit so we use it. + * + * This is movu.w __NR_sigreturn, r9; break 13; + */ + __put_user(0x9c5f, frame->retcode+0); + __put_user(TARGET_NR_sigreturn, + frame->retcode + 1); + __put_user(0xe93d, frame->retcode + 2); + + /* Save the mask. */ + __put_user(set->sig[0], &frame->sc.oldmask); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->extramask[i - 1]); + } + + setup_sigcontext(&frame->sc, env); + + /* Move the stack and setup the arguments for the handler. */ + env->regs[R_SP] = frame_addr; + env->regs[10] = sig; + env->pc = (unsigned long) ka->_sa_handler; + /* Link SRP so the guest returns through the trampoline. */ + env->pregs[PR_SRP] = frame_addr + offsetof(typeof(*frame), retcode); + + unlock_user_struct(frame, frame_addr, 1); + return; +badframe: + force_sigsegv(sig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUCRISState *env) +{ + fprintf(stderr, "CRIS setup_rt_frame: not implemented\n"); +} + +long do_sigreturn(CPUCRISState *env) +{ + struct target_signal_frame *frame; + abi_ulong frame_addr; + target_sigset_t target_set; + sigset_t set; + int i; + + frame_addr = env->regs[R_SP]; + trace_user_do_sigreturn(env, frame_addr); + /* Make sure the guest isn't playing games. */ + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) { + goto badframe; + } + + /* Restore blocked signals */ + __get_user(target_set.sig[0], &frame->sc.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + restore_sigcontext(&frame->sc, env); + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUCRISState *env) +{ + trace_user_do_rt_sigreturn(env, 0); + fprintf(stderr, "CRIS do_rt_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} diff --git a/linux-user/cris/target_signal.h b/linux-user/cris/target_signal.h index 664621bbcd..19c0d7b539 100644 --- a/linux-user/cris/target_signal.h +++ b/linux-user/cris/target_signal.h @@ -26,5 +26,9 @@ static inline abi_ulong get_sp_from_cpustate(CPUCRISState *state) return state->regs[14]; } - +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUCRISState *env); +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUCRISState *env); #endif /* CRIS_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 546311221b..3f3fd05822 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -2364,174 +2364,6 @@ badframe: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_CRIS) - -struct target_sigcontext { - struct target_pt_regs regs; /* needs to be first */ - uint32_t oldmask; - uint32_t usp; /* usp before stacking this gunk on it */ -}; - -/* Signal frames. */ -struct target_signal_frame { - struct target_sigcontext sc; - uint32_t extramask[TARGET_NSIG_WORDS - 1]; - uint16_t retcode[4]; /* Trampoline code. */ -}; - -struct rt_signal_frame { - siginfo_t *pinfo; - void *puc; - siginfo_t info; - ucontext_t uc; - uint16_t retcode[4]; /* Trampoline code. */ -}; - -static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) -{ - __put_user(env->regs[0], &sc->regs.r0); - __put_user(env->regs[1], &sc->regs.r1); - __put_user(env->regs[2], &sc->regs.r2); - __put_user(env->regs[3], &sc->regs.r3); - __put_user(env->regs[4], &sc->regs.r4); - __put_user(env->regs[5], &sc->regs.r5); - __put_user(env->regs[6], &sc->regs.r6); - __put_user(env->regs[7], &sc->regs.r7); - __put_user(env->regs[8], &sc->regs.r8); - __put_user(env->regs[9], &sc->regs.r9); - __put_user(env->regs[10], &sc->regs.r10); - __put_user(env->regs[11], &sc->regs.r11); - __put_user(env->regs[12], &sc->regs.r12); - __put_user(env->regs[13], &sc->regs.r13); - __put_user(env->regs[14], &sc->usp); - __put_user(env->regs[15], &sc->regs.acr); - __put_user(env->pregs[PR_MOF], &sc->regs.mof); - __put_user(env->pregs[PR_SRP], &sc->regs.srp); - __put_user(env->pc, &sc->regs.erp); -} - -static void restore_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) -{ - __get_user(env->regs[0], &sc->regs.r0); - __get_user(env->regs[1], &sc->regs.r1); - __get_user(env->regs[2], &sc->regs.r2); - __get_user(env->regs[3], &sc->regs.r3); - __get_user(env->regs[4], &sc->regs.r4); - __get_user(env->regs[5], &sc->regs.r5); - __get_user(env->regs[6], &sc->regs.r6); - __get_user(env->regs[7], &sc->regs.r7); - __get_user(env->regs[8], &sc->regs.r8); - __get_user(env->regs[9], &sc->regs.r9); - __get_user(env->regs[10], &sc->regs.r10); - __get_user(env->regs[11], &sc->regs.r11); - __get_user(env->regs[12], &sc->regs.r12); - __get_user(env->regs[13], &sc->regs.r13); - __get_user(env->regs[14], &sc->usp); - __get_user(env->regs[15], &sc->regs.acr); - __get_user(env->pregs[PR_MOF], &sc->regs.mof); - __get_user(env->pregs[PR_SRP], &sc->regs.srp); - __get_user(env->pc, &sc->regs.erp); -} - -static abi_ulong get_sigframe(CPUCRISState *env, int framesize) -{ - abi_ulong sp; - /* Align the stack downwards to 4. */ - sp = (env->regs[R_SP] & ~3); - return sp - framesize; -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUCRISState *env) -{ - struct target_signal_frame *frame; - abi_ulong frame_addr; - int i; - - frame_addr = get_sigframe(env, sizeof *frame); - trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto badframe; - - /* - * The CRIS signal return trampoline. A real linux/CRIS kernel doesn't - * use this trampoline anymore but it sets it up for GDB. - * In QEMU, using the trampoline simplifies things a bit so we use it. - * - * This is movu.w __NR_sigreturn, r9; break 13; - */ - __put_user(0x9c5f, frame->retcode+0); - __put_user(TARGET_NR_sigreturn, - frame->retcode + 1); - __put_user(0xe93d, frame->retcode + 2); - - /* Save the mask. */ - __put_user(set->sig[0], &frame->sc.oldmask); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - setup_sigcontext(&frame->sc, env); - - /* Move the stack and setup the arguments for the handler. */ - env->regs[R_SP] = frame_addr; - env->regs[10] = sig; - env->pc = (unsigned long) ka->_sa_handler; - /* Link SRP so the guest returns through the trampoline. */ - env->pregs[PR_SRP] = frame_addr + offsetof(typeof(*frame), retcode); - - unlock_user_struct(frame, frame_addr, 1); - return; -badframe: - force_sigsegv(sig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUCRISState *env) -{ - fprintf(stderr, "CRIS setup_rt_frame: not implemented\n"); -} - -long do_sigreturn(CPUCRISState *env) -{ - struct target_signal_frame *frame; - abi_ulong frame_addr; - target_sigset_t target_set; - sigset_t set; - int i; - - frame_addr = env->regs[R_SP]; - trace_user_do_sigreturn(env, frame_addr); - /* Make sure the guest isn't playing games. */ - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) { - goto badframe; - } - - /* Restore blocked signals */ - __get_user(target_set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - restore_sigcontext(&frame->sc, env); - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUCRISState *env) -{ - trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "CRIS do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - #elif defined(TARGET_NIOS2) #define MCONTEXT_VERSION 2 From f7cd3e678cda9c91f99a045e8d7d37247a558346 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:22 +0200 Subject: [PATCH 0132/2380] linux-user: move nios2 signal.c parts to nios2 directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to nios2/signal.c, except adding includes and exporting setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-8-laurent@vivier.eu> --- linux-user/nios2/signal.c | 236 +++++++++++++++++++++++++++++++ linux-user/nios2/target_signal.h | 4 + linux-user/signal.c | 234 ------------------------------ 3 files changed, 240 insertions(+), 234 deletions(-) diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c index 02ca338b6c..816eed90f1 100644 --- a/linux-user/nios2/signal.c +++ b/linux-user/nios2/signal.c @@ -16,3 +16,239 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +#define MCONTEXT_VERSION 2 + +struct target_sigcontext { + int version; + unsigned long gregs[32]; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +struct target_rt_sigframe { + struct target_siginfo info; + struct target_ucontext uc; +}; + +static unsigned long sigsp(unsigned long sp, struct target_sigaction *ka) +{ + if (unlikely((ka->sa_flags & SA_ONSTACK)) && !sas_ss_flags(sp)) { +#ifdef CONFIG_STACK_GROWSUP + return target_sigaltstack_used.ss_sp; +#else + return target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; +#endif + } + return sp; +} + +static int rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env) +{ + unsigned long *gregs = uc->tuc_mcontext.gregs; + + __put_user(MCONTEXT_VERSION, &uc->tuc_mcontext.version); + __put_user(env->regs[1], &gregs[0]); + __put_user(env->regs[2], &gregs[1]); + __put_user(env->regs[3], &gregs[2]); + __put_user(env->regs[4], &gregs[3]); + __put_user(env->regs[5], &gregs[4]); + __put_user(env->regs[6], &gregs[5]); + __put_user(env->regs[7], &gregs[6]); + __put_user(env->regs[8], &gregs[7]); + __put_user(env->regs[9], &gregs[8]); + __put_user(env->regs[10], &gregs[9]); + __put_user(env->regs[11], &gregs[10]); + __put_user(env->regs[12], &gregs[11]); + __put_user(env->regs[13], &gregs[12]); + __put_user(env->regs[14], &gregs[13]); + __put_user(env->regs[15], &gregs[14]); + __put_user(env->regs[16], &gregs[15]); + __put_user(env->regs[17], &gregs[16]); + __put_user(env->regs[18], &gregs[17]); + __put_user(env->regs[19], &gregs[18]); + __put_user(env->regs[20], &gregs[19]); + __put_user(env->regs[21], &gregs[20]); + __put_user(env->regs[22], &gregs[21]); + __put_user(env->regs[23], &gregs[22]); + __put_user(env->regs[R_RA], &gregs[23]); + __put_user(env->regs[R_FP], &gregs[24]); + __put_user(env->regs[R_GP], &gregs[25]); + __put_user(env->regs[R_EA], &gregs[27]); + __put_user(env->regs[R_SP], &gregs[28]); + + return 0; +} + +static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc, + int *pr2) +{ + int temp; + abi_ulong off, frame_addr = env->regs[R_SP]; + unsigned long *gregs = uc->tuc_mcontext.gregs; + int err; + + /* Always make any pending restarted system calls return -EINTR */ + /* current->restart_block.fn = do_no_restart_syscall; */ + + __get_user(temp, &uc->tuc_mcontext.version); + if (temp != MCONTEXT_VERSION) { + return 1; + } + + /* restore passed registers */ + __get_user(env->regs[1], &gregs[0]); + __get_user(env->regs[2], &gregs[1]); + __get_user(env->regs[3], &gregs[2]); + __get_user(env->regs[4], &gregs[3]); + __get_user(env->regs[5], &gregs[4]); + __get_user(env->regs[6], &gregs[5]); + __get_user(env->regs[7], &gregs[6]); + __get_user(env->regs[8], &gregs[7]); + __get_user(env->regs[9], &gregs[8]); + __get_user(env->regs[10], &gregs[9]); + __get_user(env->regs[11], &gregs[10]); + __get_user(env->regs[12], &gregs[11]); + __get_user(env->regs[13], &gregs[12]); + __get_user(env->regs[14], &gregs[13]); + __get_user(env->regs[15], &gregs[14]); + __get_user(env->regs[16], &gregs[15]); + __get_user(env->regs[17], &gregs[16]); + __get_user(env->regs[18], &gregs[17]); + __get_user(env->regs[19], &gregs[18]); + __get_user(env->regs[20], &gregs[19]); + __get_user(env->regs[21], &gregs[20]); + __get_user(env->regs[22], &gregs[21]); + __get_user(env->regs[23], &gregs[22]); + /* gregs[23] is handled below */ + /* Verify, should this be settable */ + __get_user(env->regs[R_FP], &gregs[24]); + /* Verify, should this be settable */ + __get_user(env->regs[R_GP], &gregs[25]); + /* Not really necessary no user settable bits */ + __get_user(temp, &gregs[26]); + __get_user(env->regs[R_EA], &gregs[27]); + + __get_user(env->regs[R_RA], &gregs[23]); + __get_user(env->regs[R_SP], &gregs[28]); + + off = offsetof(struct target_rt_sigframe, uc.tuc_stack); + err = do_sigaltstack(frame_addr + off, 0, get_sp_from_cpustate(env)); + if (err == -EFAULT) { + return 1; + } + + *pr2 = env->regs[2]; + return 0; +} + +static void *get_sigframe(struct target_sigaction *ka, CPUNios2State *env, + size_t frame_size) +{ + unsigned long usp; + + /* Default to using normal stack. */ + usp = env->regs[R_SP]; + + /* This is the X/Open sanctioned signal stack switching. */ + usp = sigsp(usp, ka); + + /* Verify, is it 32 or 64 bit aligned */ + return (void *)((usp - frame_size) & -8UL); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, + CPUNios2State *env) +{ + struct target_rt_sigframe *frame; + int i, err = 0; + + frame = get_sigframe(ka, env, sizeof(*frame)); + + if (ka->sa_flags & SA_SIGINFO) { + tswap_siginfo(&frame->info, info); + } + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(env->regs[R_SP]), &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); + err |= rt_setup_ucontext(&frame->uc, env); + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user((abi_ulong)set->sig[i], + (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); + } + + if (err) { + goto give_sigsegv; + } + + /* Set up to return from userspace; jump to fixed address sigreturn + trampoline on kuser page. */ + env->regs[R_RA] = (unsigned long) (0x1044); + + /* Set up registers for signal handler */ + env->regs[R_SP] = (unsigned long) frame; + env->regs[4] = (unsigned long) sig; + env->regs[5] = (unsigned long) &frame->info; + env->regs[6] = (unsigned long) &frame->uc; + env->regs[R_EA] = (unsigned long) ka->_sa_handler; + return; + +give_sigsegv: + if (sig == TARGET_SIGSEGV) { + ka->_sa_handler = TARGET_SIG_DFL; + } + force_sigsegv(sig); + return; +} + +long do_sigreturn(CPUNios2State *env) +{ + trace_user_do_sigreturn(env, 0); + fprintf(stderr, "do_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} + +long do_rt_sigreturn(CPUNios2State *env) +{ + /* Verify, can we follow the stack back */ + abi_ulong frame_addr = env->regs[R_SP]; + struct target_rt_sigframe *frame; + sigset_t set; + int rval; + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + do_sigprocmask(SIG_SETMASK, &set, NULL); + + if (rt_restore_ucontext(env, &frame->uc, &rval)) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return rval; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} diff --git a/linux-user/nios2/target_signal.h b/linux-user/nios2/target_signal.h index 23a8267696..aa78519025 100644 --- a/linux-user/nios2/target_signal.h +++ b/linux-user/nios2/target_signal.h @@ -23,4 +23,8 @@ static inline abi_ulong get_sp_from_cpustate(CPUNios2State *state) return state->regs[R_SP]; } +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, + CPUNios2State *env); #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 3f3fd05822..e943980ade 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -2364,240 +2364,6 @@ badframe: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_NIOS2) - -#define MCONTEXT_VERSION 2 - -struct target_sigcontext { - int version; - unsigned long gregs[32]; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_rt_sigframe { - struct target_siginfo info; - struct target_ucontext uc; -}; - -static unsigned long sigsp(unsigned long sp, struct target_sigaction *ka) -{ - if (unlikely((ka->sa_flags & SA_ONSTACK)) && !sas_ss_flags(sp)) { -#ifdef CONFIG_STACK_GROWSUP - return target_sigaltstack_used.ss_sp; -#else - return target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; -#endif - } - return sp; -} - -static int rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env) -{ - unsigned long *gregs = uc->tuc_mcontext.gregs; - - __put_user(MCONTEXT_VERSION, &uc->tuc_mcontext.version); - __put_user(env->regs[1], &gregs[0]); - __put_user(env->regs[2], &gregs[1]); - __put_user(env->regs[3], &gregs[2]); - __put_user(env->regs[4], &gregs[3]); - __put_user(env->regs[5], &gregs[4]); - __put_user(env->regs[6], &gregs[5]); - __put_user(env->regs[7], &gregs[6]); - __put_user(env->regs[8], &gregs[7]); - __put_user(env->regs[9], &gregs[8]); - __put_user(env->regs[10], &gregs[9]); - __put_user(env->regs[11], &gregs[10]); - __put_user(env->regs[12], &gregs[11]); - __put_user(env->regs[13], &gregs[12]); - __put_user(env->regs[14], &gregs[13]); - __put_user(env->regs[15], &gregs[14]); - __put_user(env->regs[16], &gregs[15]); - __put_user(env->regs[17], &gregs[16]); - __put_user(env->regs[18], &gregs[17]); - __put_user(env->regs[19], &gregs[18]); - __put_user(env->regs[20], &gregs[19]); - __put_user(env->regs[21], &gregs[20]); - __put_user(env->regs[22], &gregs[21]); - __put_user(env->regs[23], &gregs[22]); - __put_user(env->regs[R_RA], &gregs[23]); - __put_user(env->regs[R_FP], &gregs[24]); - __put_user(env->regs[R_GP], &gregs[25]); - __put_user(env->regs[R_EA], &gregs[27]); - __put_user(env->regs[R_SP], &gregs[28]); - - return 0; -} - -static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc, - int *pr2) -{ - int temp; - abi_ulong off, frame_addr = env->regs[R_SP]; - unsigned long *gregs = uc->tuc_mcontext.gregs; - int err; - - /* Always make any pending restarted system calls return -EINTR */ - /* current->restart_block.fn = do_no_restart_syscall; */ - - __get_user(temp, &uc->tuc_mcontext.version); - if (temp != MCONTEXT_VERSION) { - return 1; - } - - /* restore passed registers */ - __get_user(env->regs[1], &gregs[0]); - __get_user(env->regs[2], &gregs[1]); - __get_user(env->regs[3], &gregs[2]); - __get_user(env->regs[4], &gregs[3]); - __get_user(env->regs[5], &gregs[4]); - __get_user(env->regs[6], &gregs[5]); - __get_user(env->regs[7], &gregs[6]); - __get_user(env->regs[8], &gregs[7]); - __get_user(env->regs[9], &gregs[8]); - __get_user(env->regs[10], &gregs[9]); - __get_user(env->regs[11], &gregs[10]); - __get_user(env->regs[12], &gregs[11]); - __get_user(env->regs[13], &gregs[12]); - __get_user(env->regs[14], &gregs[13]); - __get_user(env->regs[15], &gregs[14]); - __get_user(env->regs[16], &gregs[15]); - __get_user(env->regs[17], &gregs[16]); - __get_user(env->regs[18], &gregs[17]); - __get_user(env->regs[19], &gregs[18]); - __get_user(env->regs[20], &gregs[19]); - __get_user(env->regs[21], &gregs[20]); - __get_user(env->regs[22], &gregs[21]); - __get_user(env->regs[23], &gregs[22]); - /* gregs[23] is handled below */ - /* Verify, should this be settable */ - __get_user(env->regs[R_FP], &gregs[24]); - /* Verify, should this be settable */ - __get_user(env->regs[R_GP], &gregs[25]); - /* Not really necessary no user settable bits */ - __get_user(temp, &gregs[26]); - __get_user(env->regs[R_EA], &gregs[27]); - - __get_user(env->regs[R_RA], &gregs[23]); - __get_user(env->regs[R_SP], &gregs[28]); - - off = offsetof(struct target_rt_sigframe, uc.tuc_stack); - err = do_sigaltstack(frame_addr + off, 0, get_sp_from_cpustate(env)); - if (err == -EFAULT) { - return 1; - } - - *pr2 = env->regs[2]; - return 0; -} - -static void *get_sigframe(struct target_sigaction *ka, CPUNios2State *env, - size_t frame_size) -{ - unsigned long usp; - - /* Default to using normal stack. */ - usp = env->regs[R_SP]; - - /* This is the X/Open sanctioned signal stack switching. */ - usp = sigsp(usp, ka); - - /* Verify, is it 32 or 64 bit aligned */ - return (void *)((usp - frame_size) & -8UL); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, - CPUNios2State *env) -{ - struct target_rt_sigframe *frame; - int i, err = 0; - - frame = get_sigframe(ka, env, sizeof(*frame)); - - if (ka->sa_flags & SA_SIGINFO) { - tswap_siginfo(&frame->info, info); - } - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->regs[R_SP]), &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); - err |= rt_setup_ucontext(&frame->uc, env); - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user((abi_ulong)set->sig[i], - (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); - } - - if (err) { - goto give_sigsegv; - } - - /* Set up to return from userspace; jump to fixed address sigreturn - trampoline on kuser page. */ - env->regs[R_RA] = (unsigned long) (0x1044); - - /* Set up registers for signal handler */ - env->regs[R_SP] = (unsigned long) frame; - env->regs[4] = (unsigned long) sig; - env->regs[5] = (unsigned long) &frame->info; - env->regs[6] = (unsigned long) &frame->uc; - env->regs[R_EA] = (unsigned long) ka->_sa_handler; - return; - -give_sigsegv: - if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; - } - force_sigsegv(sig); - return; -} - -long do_sigreturn(CPUNios2State *env) -{ - trace_user_do_sigreturn(env, 0); - fprintf(stderr, "do_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -long do_rt_sigreturn(CPUNios2State *env) -{ - /* Verify, can we follow the stack back */ - abi_ulong frame_addr = env->regs[R_SP]; - struct target_rt_sigframe *frame; - sigset_t set; - int rval; - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - do_sigprocmask(SIG_SETMASK, &set, NULL); - - if (rt_restore_ucontext(env, &frame->uc, &rval)) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return rval; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return 0; -} -/* TARGET_NIOS2 */ - #elif defined(TARGET_OPENRISC) struct target_sigcontext { From 17853172f7c3abaa6f1fa65c9c01104539da3a03 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:23 +0200 Subject: [PATCH 0133/2380] linux-user: move openrisc signal.c parts to openrisc directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to openrisc/signal.c, except adding includes and exporting setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-9-laurent@vivier.eu> --- linux-user/openrisc/signal.c | 213 ++++++++++++++++++++++++++++ linux-user/openrisc/target_signal.h | 4 +- linux-user/signal.c | 211 --------------------------- 3 files changed, 216 insertions(+), 212 deletions(-) diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c index 02ca338b6c..0276808b59 100644 --- a/linux-user/openrisc/signal.c +++ b/linux-user/openrisc/signal.c @@ -16,3 +16,216 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + struct target_pt_regs regs; + abi_ulong oldmask; + abi_ulong usp; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +struct target_rt_sigframe { + abi_ulong pinfo; + uint64_t puc; + struct target_siginfo info; + struct target_sigcontext sc; + struct target_ucontext uc; + unsigned char retcode[16]; /* trampoline code */ +}; + +/* This is the asm-generic/ucontext.h version */ +#if 0 +static int restore_sigcontext(CPUOpenRISCState *regs, + struct target_sigcontext *sc) +{ + unsigned int err = 0; + unsigned long old_usp; + + /* Alwys make any pending restarted system call return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + /* restore the regs from &sc->regs (same as sc, since regs is first) + * (sc is already checked for VERIFY_READ since the sigframe was + * checked in sys_sigreturn previously) + */ + + if (copy_from_user(regs, &sc, sizeof(struct target_pt_regs))) { + goto badframe; + } + + /* make sure the U-flag is set so user-mode cannot fool us */ + + regs->sr &= ~SR_SM; + + /* restore the old USP as it was before we stacked the sc etc. + * (we cannot just pop the sigcontext since we aligned the sp and + * stuff after pushing it) + */ + + __get_user(old_usp, &sc->usp); + phx_signal("old_usp 0x%lx", old_usp); + + __PHX__ REALLY /* ??? */ + wrusp(old_usp); + regs->gpr[1] = old_usp; + + /* TODO: the other ports use regs->orig_XX to disable syscall checks + * after this completes, but we don't use that mechanism. maybe we can + * use it now ? + */ + + return err; + +badframe: + return 1; +} +#endif + +/* Set up a signal frame. */ + +static void setup_sigcontext(struct target_sigcontext *sc, + CPUOpenRISCState *regs, + unsigned long mask) +{ + unsigned long usp = cpu_get_gpr(regs, 1); + + /* copy the regs. they are first in sc so we can use sc directly */ + + /*copy_to_user(&sc, regs, sizeof(struct target_pt_regs));*/ + + /* Set the frametype to CRIS_FRAME_NORMAL for the execution of + the signal handler. The frametype will be restored to its previous + value in restore_sigcontext. */ + /*regs->frametype = CRIS_FRAME_NORMAL;*/ + + /* then some other stuff */ + __put_user(mask, &sc->oldmask); + __put_user(usp, &sc->usp); +} + +static inline unsigned long align_sigframe(unsigned long sp) +{ + return sp & ~3UL; +} + +static inline abi_ulong get_sigframe(struct target_sigaction *ka, + CPUOpenRISCState *regs, + size_t frame_size) +{ + unsigned long sp = cpu_get_gpr(regs, 1); + int onsigstack = on_sig_stack(sp); + + /* redzone */ + /* This is the X/Open sanctioned signal stack switching. */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + sp = align_sigframe(sp - frame_size); + + /* + * If we are on the alternate signal stack and would overflow it, don't. + * Return an always-bogus address instead so we will die with SIGSEGV. + */ + + if (onsigstack && !likely(on_sig_stack(sp))) { + return -1L; + } + + return sp; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUOpenRISCState *env) +{ + int err = 0; + abi_ulong frame_addr; + unsigned long return_ip; + struct target_rt_sigframe *frame; + abi_ulong info_addr, uc_addr; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + info_addr = frame_addr + offsetof(struct target_rt_sigframe, info); + __put_user(info_addr, &frame->pinfo); + uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); + __put_user(uc_addr, &frame->puc); + + if (ka->sa_flags & SA_SIGINFO) { + tswap_siginfo(&frame->info, info); + } + + /*err |= __clear_user(&frame->uc, offsetof(ucontext_t, uc_mcontext));*/ + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, + &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(cpu_get_gpr(env, 1)), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, + &frame->uc.tuc_stack.ss_size); + setup_sigcontext(&frame->sc, env, set->sig[0]); + + /*err |= copy_to_user(frame->uc.tuc_sigmask, set, sizeof(*set));*/ + + /* trampoline - the desired return ip is the retcode itself */ + return_ip = (unsigned long)&frame->retcode; + /* This is l.ori r11,r0,__NR_sigreturn, l.sys 1 */ + __put_user(0xa960, (short *)(frame->retcode + 0)); + __put_user(TARGET_NR_rt_sigreturn, (short *)(frame->retcode + 2)); + __put_user(0x20000001, (unsigned long *)(frame->retcode + 4)); + __put_user(0x15000000, (unsigned long *)(frame->retcode + 8)); + + if (err) { + goto give_sigsegv; + } + + /* TODO what is the current->exec_domain stuff and invmap ? */ + + /* Set up registers for signal handler */ + env->pc = (unsigned long)ka->_sa_handler; /* what we enter NOW */ + cpu_set_gpr(env, 9, (unsigned long)return_ip); /* what we enter LATER */ + cpu_set_gpr(env, 3, (unsigned long)sig); /* arg 1: signo */ + cpu_set_gpr(env, 4, (unsigned long)&frame->info); /* arg 2: (siginfo_t*) */ + cpu_set_gpr(env, 5, (unsigned long)&frame->uc); /* arg 3: ucontext */ + + /* actually move the usp to reflect the stacked frame */ + cpu_set_gpr(env, 1, (unsigned long)frame); + + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} + +long do_sigreturn(CPUOpenRISCState *env) +{ + trace_user_do_sigreturn(env, 0); + fprintf(stderr, "do_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} + +long do_rt_sigreturn(CPUOpenRISCState *env) +{ + trace_user_do_rt_sigreturn(env, 0); + fprintf(stderr, "do_rt_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h index 95a733e15a..6c47ddf74e 100644 --- a/linux-user/openrisc/target_signal.h +++ b/linux-user/openrisc/target_signal.h @@ -23,5 +23,7 @@ static inline abi_ulong get_sp_from_cpustate(CPUOpenRISCState *state) return cpu_get_gpr(state, 1); } - +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUOpenRISCState *env); #endif /* OPENRISC_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index e943980ade..dd85209ef9 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -2364,217 +2364,6 @@ badframe: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_OPENRISC) - -struct target_sigcontext { - struct target_pt_regs regs; - abi_ulong oldmask; - abi_ulong usp; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_rt_sigframe { - abi_ulong pinfo; - uint64_t puc; - struct target_siginfo info; - struct target_sigcontext sc; - struct target_ucontext uc; - unsigned char retcode[16]; /* trampoline code */ -}; - -/* This is the asm-generic/ucontext.h version */ -#if 0 -static int restore_sigcontext(CPUOpenRISCState *regs, - struct target_sigcontext *sc) -{ - unsigned int err = 0; - unsigned long old_usp; - - /* Alwys make any pending restarted system call return -EINTR */ - current_thread_info()->restart_block.fn = do_no_restart_syscall; - - /* restore the regs from &sc->regs (same as sc, since regs is first) - * (sc is already checked for VERIFY_READ since the sigframe was - * checked in sys_sigreturn previously) - */ - - if (copy_from_user(regs, &sc, sizeof(struct target_pt_regs))) { - goto badframe; - } - - /* make sure the U-flag is set so user-mode cannot fool us */ - - regs->sr &= ~SR_SM; - - /* restore the old USP as it was before we stacked the sc etc. - * (we cannot just pop the sigcontext since we aligned the sp and - * stuff after pushing it) - */ - - __get_user(old_usp, &sc->usp); - phx_signal("old_usp 0x%lx", old_usp); - - __PHX__ REALLY /* ??? */ - wrusp(old_usp); - regs->gpr[1] = old_usp; - - /* TODO: the other ports use regs->orig_XX to disable syscall checks - * after this completes, but we don't use that mechanism. maybe we can - * use it now ? - */ - - return err; - -badframe: - return 1; -} -#endif - -/* Set up a signal frame. */ - -static void setup_sigcontext(struct target_sigcontext *sc, - CPUOpenRISCState *regs, - unsigned long mask) -{ - unsigned long usp = cpu_get_gpr(regs, 1); - - /* copy the regs. they are first in sc so we can use sc directly */ - - /*copy_to_user(&sc, regs, sizeof(struct target_pt_regs));*/ - - /* Set the frametype to CRIS_FRAME_NORMAL for the execution of - the signal handler. The frametype will be restored to its previous - value in restore_sigcontext. */ - /*regs->frametype = CRIS_FRAME_NORMAL;*/ - - /* then some other stuff */ - __put_user(mask, &sc->oldmask); - __put_user(usp, &sc->usp); -} - -static inline unsigned long align_sigframe(unsigned long sp) -{ - return sp & ~3UL; -} - -static inline abi_ulong get_sigframe(struct target_sigaction *ka, - CPUOpenRISCState *regs, - size_t frame_size) -{ - unsigned long sp = cpu_get_gpr(regs, 1); - int onsigstack = on_sig_stack(sp); - - /* redzone */ - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - sp = align_sigframe(sp - frame_size); - - /* - * If we are on the alternate signal stack and would overflow it, don't. - * Return an always-bogus address instead so we will die with SIGSEGV. - */ - - if (onsigstack && !likely(on_sig_stack(sp))) { - return -1L; - } - - return sp; -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUOpenRISCState *env) -{ - int err = 0; - abi_ulong frame_addr; - unsigned long return_ip; - struct target_rt_sigframe *frame; - abi_ulong info_addr, uc_addr; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - info_addr = frame_addr + offsetof(struct target_rt_sigframe, info); - __put_user(info_addr, &frame->pinfo); - uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); - __put_user(uc_addr, &frame->puc); - - if (ka->sa_flags & SA_SIGINFO) { - tswap_siginfo(&frame->info, info); - } - - /*err |= __clear_user(&frame->uc, offsetof(ucontext_t, uc_mcontext));*/ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(cpu_get_gpr(env, 1)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - setup_sigcontext(&frame->sc, env, set->sig[0]); - - /*err |= copy_to_user(frame->uc.tuc_sigmask, set, sizeof(*set));*/ - - /* trampoline - the desired return ip is the retcode itself */ - return_ip = (unsigned long)&frame->retcode; - /* This is l.ori r11,r0,__NR_sigreturn, l.sys 1 */ - __put_user(0xa960, (short *)(frame->retcode + 0)); - __put_user(TARGET_NR_rt_sigreturn, (short *)(frame->retcode + 2)); - __put_user(0x20000001, (unsigned long *)(frame->retcode + 4)); - __put_user(0x15000000, (unsigned long *)(frame->retcode + 8)); - - if (err) { - goto give_sigsegv; - } - - /* TODO what is the current->exec_domain stuff and invmap ? */ - - /* Set up registers for signal handler */ - env->pc = (unsigned long)ka->_sa_handler; /* what we enter NOW */ - cpu_set_gpr(env, 9, (unsigned long)return_ip); /* what we enter LATER */ - cpu_set_gpr(env, 3, (unsigned long)sig); /* arg 1: signo */ - cpu_set_gpr(env, 4, (unsigned long)&frame->info); /* arg 2: (siginfo_t*) */ - cpu_set_gpr(env, 5, (unsigned long)&frame->uc); /* arg 3: ucontext */ - - /* actually move the usp to reflect the stacked frame */ - cpu_set_gpr(env, 1, (unsigned long)frame); - - return; - -give_sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} - -long do_sigreturn(CPUOpenRISCState *env) -{ - trace_user_do_sigreturn(env, 0); - fprintf(stderr, "do_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -long do_rt_sigreturn(CPUOpenRISCState *env) -{ - trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} -/* TARGET_OPENRISC */ - #elif defined(TARGET_S390X) #define __NUM_GPRS 16 From 4c4c73e36911cd666905163be1cb5767a349b986 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:24 +0200 Subject: [PATCH 0134/2380] linux-user: move s390x signal.c parts to s390x directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to s390x/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). Signed-off-by: Laurent Vivier Acked-by: Cornelia Huck Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-10-laurent@vivier.eu> --- linux-user/s390x/signal.c | 309 +++++++++++++++++++++++++++++++ linux-user/s390x/target_signal.h | 6 +- linux-user/signal.c | 306 ------------------------------ 3 files changed, 314 insertions(+), 307 deletions(-) diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index 02ca338b6c..a204a85e4a 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -16,3 +16,312 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +#define __NUM_GPRS 16 +#define __NUM_FPRS 16 +#define __NUM_ACRS 16 + +#define S390_SYSCALL_SIZE 2 +#define __SIGNAL_FRAMESIZE 160 /* FIXME: 31-bit mode -> 96 */ + +#define _SIGCONTEXT_NSIG 64 +#define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */ +#define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW) +#define _SIGMASK_COPY_SIZE (sizeof(unsigned long)*_SIGCONTEXT_NSIG_WORDS) +#define PSW_ADDR_AMODE 0x0000000000000000UL /* 0x80000000UL for 31-bit */ +#define S390_SYSCALL_OPCODE ((uint16_t)0x0a00) + +typedef struct { + target_psw_t psw; + target_ulong gprs[__NUM_GPRS]; + unsigned int acrs[__NUM_ACRS]; +} target_s390_regs_common; + +typedef struct { + unsigned int fpc; + double fprs[__NUM_FPRS]; +} target_s390_fp_regs; + +typedef struct { + target_s390_regs_common regs; + target_s390_fp_regs fpregs; +} target_sigregs; + +struct target_sigcontext { + target_ulong oldmask[_SIGCONTEXT_NSIG_WORDS]; + target_sigregs *sregs; +}; + +typedef struct { + uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; + struct target_sigcontext sc; + target_sigregs sregs; + int signo; + uint8_t retcode[S390_SYSCALL_SIZE]; +} sigframe; + +struct target_ucontext { + target_ulong tuc_flags; + struct target_ucontext *tuc_link; + target_stack_t tuc_stack; + target_sigregs tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +typedef struct { + uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; + uint8_t retcode[S390_SYSCALL_SIZE]; + struct target_siginfo info; + struct target_ucontext uc; +} rt_sigframe; + +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUS390XState *env, size_t frame_size) +{ + abi_ulong sp; + + /* Default to using normal stack */ + sp = env->regs[15]; + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa_flags & TARGET_SA_ONSTACK) { + if (!sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + + target_sigaltstack_used.ss_size; + } + } + + /* This is the legacy signal stack switching. */ + else if (/* FIXME !user_mode(regs) */ 0 && + !(ka->sa_flags & TARGET_SA_RESTORER) && + ka->sa_restorer) { + sp = (abi_ulong) ka->sa_restorer; + } + + return (sp - frame_size) & -8ul; +} + +static void save_sigregs(CPUS390XState *env, target_sigregs *sregs) +{ + int i; + //save_access_regs(current->thread.acrs); FIXME + + /* Copy a 'clean' PSW mask to the user to avoid leaking + information about whether PER is currently on. */ + __put_user(env->psw.mask, &sregs->regs.psw.mask); + __put_user(env->psw.addr, &sregs->regs.psw.addr); + for (i = 0; i < 16; i++) { + __put_user(env->regs[i], &sregs->regs.gprs[i]); + } + for (i = 0; i < 16; i++) { + __put_user(env->aregs[i], &sregs->regs.acrs[i]); + } + /* + * We have to store the fp registers to current->thread.fp_regs + * to merge them with the emulated registers. + */ + //save_fp_regs(¤t->thread.fp_regs); FIXME + for (i = 0; i < 16; i++) { + __put_user(get_freg(env, i)->ll, &sregs->fpregs.fprs[i]); + } +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUS390XState *env) +{ + sigframe *frame; + abi_ulong frame_addr; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + __put_user(set->sig[0], &frame->sc.oldmask[0]); + + save_sigregs(env, &frame->sregs); + + __put_user((abi_ulong)(unsigned long)&frame->sregs, + (abi_ulong *)&frame->sc.sregs); + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + env->regs[14] = (unsigned long) + ka->sa_restorer | PSW_ADDR_AMODE; + } else { + env->regs[14] = (frame_addr + offsetof(sigframe, retcode)) + | PSW_ADDR_AMODE; + __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, + (uint16_t *)(frame->retcode)); + } + + /* Set up backchain. */ + __put_user(env->regs[15], (abi_ulong *) frame); + + /* Set up registers for signal handler */ + env->regs[15] = frame_addr; + env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; + + env->regs[2] = sig; //map_signal(sig); + env->regs[3] = frame_addr += offsetof(typeof(*frame), sc); + + /* We forgot to include these in the sigcontext. + To avoid breaking binary compatibility, they are passed as args. */ + env->regs[4] = 0; // FIXME: no clue... current->thread.trap_no; + env->regs[5] = 0; // FIXME: no clue... current->thread.prot_addr; + + /* Place signal number on stack to allow backtrace from handler. */ + __put_user(env->regs[2], &frame->signo); + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUS390XState *env) +{ + int i; + rt_sigframe *frame; + abi_ulong frame_addr; + + frame_addr = get_sigframe(ka, env, sizeof *frame); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + tswap_siginfo(&frame->info, info); + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); + save_sigregs(env, &frame->uc.tuc_mcontext); + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user((abi_ulong)set->sig[i], + (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + env->regs[14] = (unsigned long) ka->sa_restorer | PSW_ADDR_AMODE; + } else { + env->regs[14] = (unsigned long) frame->retcode | PSW_ADDR_AMODE; + __put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn, + (uint16_t *)(frame->retcode)); + } + + /* Set up backchain. */ + __put_user(env->regs[15], (abi_ulong *) frame); + + /* Set up registers for signal handler */ + env->regs[15] = frame_addr; + env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; + + env->regs[2] = sig; //map_signal(sig); + env->regs[3] = frame_addr + offsetof(typeof(*frame), info); + env->regs[4] = frame_addr + offsetof(typeof(*frame), uc); + return; + +give_sigsegv: + force_sigsegv(sig); +} + +static int +restore_sigregs(CPUS390XState *env, target_sigregs *sc) +{ + int err = 0; + int i; + + for (i = 0; i < 16; i++) { + __get_user(env->regs[i], &sc->regs.gprs[i]); + } + + __get_user(env->psw.mask, &sc->regs.psw.mask); + trace_user_s390x_restore_sigregs(env, (unsigned long long)sc->regs.psw.addr, + (unsigned long long)env->psw.addr); + __get_user(env->psw.addr, &sc->regs.psw.addr); + /* FIXME: 31-bit -> | PSW_ADDR_AMODE */ + + for (i = 0; i < 16; i++) { + __get_user(env->aregs[i], &sc->regs.acrs[i]); + } + for (i = 0; i < 16; i++) { + __get_user(get_freg(env, i)->ll, &sc->fpregs.fprs[i]); + } + + return err; +} + +long do_sigreturn(CPUS390XState *env) +{ + sigframe *frame; + abi_ulong frame_addr = env->regs[15]; + target_sigset_t target_set; + sigset_t set; + + trace_user_do_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + __get_user(target_set.sig[0], &frame->sc.oldmask[0]); + + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); /* ~_BLOCKABLE? */ + + if (restore_sigregs(env, &frame->sregs)) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUS390XState *env) +{ + rt_sigframe *frame; + abi_ulong frame_addr = env->regs[15]; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + + set_sigmask(&set); /* ~_BLOCKABLE? */ + + if (restore_sigregs(env, &frame->uc.tuc_mcontext)) { + goto badframe; + } + + if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.tuc_stack), 0, + get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/s390x/target_signal.h b/linux-user/s390x/target_signal.h index 6f7b6abafe..9e3733bb38 100644 --- a/linux-user/s390x/target_signal.h +++ b/linux-user/s390x/target_signal.h @@ -23,5 +23,9 @@ static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state) return state->regs[15]; } - +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUS390XState *env); +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUS390XState *env); #endif /* S390X_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index dd85209ef9..32a9405e84 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -2364,312 +2364,6 @@ badframe: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_S390X) - -#define __NUM_GPRS 16 -#define __NUM_FPRS 16 -#define __NUM_ACRS 16 - -#define S390_SYSCALL_SIZE 2 -#define __SIGNAL_FRAMESIZE 160 /* FIXME: 31-bit mode -> 96 */ - -#define _SIGCONTEXT_NSIG 64 -#define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */ -#define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW) -#define _SIGMASK_COPY_SIZE (sizeof(unsigned long)*_SIGCONTEXT_NSIG_WORDS) -#define PSW_ADDR_AMODE 0x0000000000000000UL /* 0x80000000UL for 31-bit */ -#define S390_SYSCALL_OPCODE ((uint16_t)0x0a00) - -typedef struct { - target_psw_t psw; - target_ulong gprs[__NUM_GPRS]; - unsigned int acrs[__NUM_ACRS]; -} target_s390_regs_common; - -typedef struct { - unsigned int fpc; - double fprs[__NUM_FPRS]; -} target_s390_fp_regs; - -typedef struct { - target_s390_regs_common regs; - target_s390_fp_regs fpregs; -} target_sigregs; - -struct target_sigcontext { - target_ulong oldmask[_SIGCONTEXT_NSIG_WORDS]; - target_sigregs *sregs; -}; - -typedef struct { - uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; - struct target_sigcontext sc; - target_sigregs sregs; - int signo; - uint8_t retcode[S390_SYSCALL_SIZE]; -} sigframe; - -struct target_ucontext { - target_ulong tuc_flags; - struct target_ucontext *tuc_link; - target_stack_t tuc_stack; - target_sigregs tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -typedef struct { - uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; - uint8_t retcode[S390_SYSCALL_SIZE]; - struct target_siginfo info; - struct target_ucontext uc; -} rt_sigframe; - -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUS390XState *env, size_t frame_size) -{ - abi_ulong sp; - - /* Default to using normal stack */ - sp = env->regs[15]; - - /* This is the X/Open sanctioned signal stack switching. */ - if (ka->sa_flags & TARGET_SA_ONSTACK) { - if (!sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + - target_sigaltstack_used.ss_size; - } - } - - /* This is the legacy signal stack switching. */ - else if (/* FIXME !user_mode(regs) */ 0 && - !(ka->sa_flags & TARGET_SA_RESTORER) && - ka->sa_restorer) { - sp = (abi_ulong) ka->sa_restorer; - } - - return (sp - frame_size) & -8ul; -} - -static void save_sigregs(CPUS390XState *env, target_sigregs *sregs) -{ - int i; - //save_access_regs(current->thread.acrs); FIXME - - /* Copy a 'clean' PSW mask to the user to avoid leaking - information about whether PER is currently on. */ - __put_user(env->psw.mask, &sregs->regs.psw.mask); - __put_user(env->psw.addr, &sregs->regs.psw.addr); - for (i = 0; i < 16; i++) { - __put_user(env->regs[i], &sregs->regs.gprs[i]); - } - for (i = 0; i < 16; i++) { - __put_user(env->aregs[i], &sregs->regs.acrs[i]); - } - /* - * We have to store the fp registers to current->thread.fp_regs - * to merge them with the emulated registers. - */ - //save_fp_regs(¤t->thread.fp_regs); FIXME - for (i = 0; i < 16; i++) { - __put_user(get_freg(env, i)->ll, &sregs->fpregs.fprs[i]); - } -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUS390XState *env) -{ - sigframe *frame; - abi_ulong frame_addr; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - __put_user(set->sig[0], &frame->sc.oldmask[0]); - - save_sigregs(env, &frame->sregs); - - __put_user((abi_ulong)(unsigned long)&frame->sregs, - (abi_ulong *)&frame->sc.sregs); - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - env->regs[14] = (unsigned long) - ka->sa_restorer | PSW_ADDR_AMODE; - } else { - env->regs[14] = (frame_addr + offsetof(sigframe, retcode)) - | PSW_ADDR_AMODE; - __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, - (uint16_t *)(frame->retcode)); - } - - /* Set up backchain. */ - __put_user(env->regs[15], (abi_ulong *) frame); - - /* Set up registers for signal handler */ - env->regs[15] = frame_addr; - env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; - - env->regs[2] = sig; //map_signal(sig); - env->regs[3] = frame_addr += offsetof(typeof(*frame), sc); - - /* We forgot to include these in the sigcontext. - To avoid breaking binary compatibility, they are passed as args. */ - env->regs[4] = 0; // FIXME: no clue... current->thread.trap_no; - env->regs[5] = 0; // FIXME: no clue... current->thread.prot_addr; - - /* Place signal number on stack to allow backtrace from handler. */ - __put_user(env->regs[2], &frame->signo); - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - force_sigsegv(sig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUS390XState *env) -{ - int i; - rt_sigframe *frame; - abi_ulong frame_addr; - - frame_addr = get_sigframe(ka, env, sizeof *frame); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - tswap_siginfo(&frame->info, info); - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); - save_sigregs(env, &frame->uc.tuc_mcontext); - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user((abi_ulong)set->sig[i], - (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - env->regs[14] = (unsigned long) ka->sa_restorer | PSW_ADDR_AMODE; - } else { - env->regs[14] = (unsigned long) frame->retcode | PSW_ADDR_AMODE; - __put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn, - (uint16_t *)(frame->retcode)); - } - - /* Set up backchain. */ - __put_user(env->regs[15], (abi_ulong *) frame); - - /* Set up registers for signal handler */ - env->regs[15] = frame_addr; - env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; - - env->regs[2] = sig; //map_signal(sig); - env->regs[3] = frame_addr + offsetof(typeof(*frame), info); - env->regs[4] = frame_addr + offsetof(typeof(*frame), uc); - return; - -give_sigsegv: - force_sigsegv(sig); -} - -static int -restore_sigregs(CPUS390XState *env, target_sigregs *sc) -{ - int err = 0; - int i; - - for (i = 0; i < 16; i++) { - __get_user(env->regs[i], &sc->regs.gprs[i]); - } - - __get_user(env->psw.mask, &sc->regs.psw.mask); - trace_user_s390x_restore_sigregs(env, (unsigned long long)sc->regs.psw.addr, - (unsigned long long)env->psw.addr); - __get_user(env->psw.addr, &sc->regs.psw.addr); - /* FIXME: 31-bit -> | PSW_ADDR_AMODE */ - - for (i = 0; i < 16; i++) { - __get_user(env->aregs[i], &sc->regs.acrs[i]); - } - for (i = 0; i < 16; i++) { - __get_user(get_freg(env, i)->ll, &sc->fpregs.fprs[i]); - } - - return err; -} - -long do_sigreturn(CPUS390XState *env) -{ - sigframe *frame; - abi_ulong frame_addr = env->regs[15]; - target_sigset_t target_set; - sigset_t set; - - trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - __get_user(target_set.sig[0], &frame->sc.oldmask[0]); - - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); /* ~_BLOCKABLE? */ - - if (restore_sigregs(env, &frame->sregs)) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUS390XState *env) -{ - rt_sigframe *frame; - abi_ulong frame_addr = env->regs[15]; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - - set_sigmask(&set); /* ~_BLOCKABLE? */ - - if (restore_sigregs(env, &frame->uc.tuc_mcontext)) { - goto badframe; - } - - if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.tuc_stack), 0, - get_sp_from_cpustate(env)) == -EFAULT) { - goto badframe; - } - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - #elif defined(TARGET_PPC) /* Size of dummy stack frame allocated when calling signal handler. From 4495abcc3bd5e7181c12dc1ec3855173df995151 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:25 +0200 Subject: [PATCH 0135/2380] linux-user: move m68k signal.c parts to m68k directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to m68k/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-11-laurent@vivier.eu> --- linux-user/m68k/signal.c | 410 ++++++++++++++++++++++++++++++++ linux-user/m68k/target_signal.h | 6 +- linux-user/signal.c | 407 ------------------------------- 3 files changed, 415 insertions(+), 408 deletions(-) diff --git a/linux-user/m68k/signal.c b/linux-user/m68k/signal.c index 02ca338b6c..fc72468a81 100644 --- a/linux-user/m68k/signal.c +++ b/linux-user/m68k/signal.c @@ -16,3 +16,413 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_ulong sc_mask; + abi_ulong sc_usp; + abi_ulong sc_d0; + abi_ulong sc_d1; + abi_ulong sc_a0; + abi_ulong sc_a1; + unsigned short sc_sr; + abi_ulong sc_pc; +}; + +struct target_sigframe +{ + abi_ulong pretcode; + int sig; + int code; + abi_ulong psc; + char retcode[8]; + abi_ulong extramask[TARGET_NSIG_WORDS-1]; + struct target_sigcontext sc; +}; + +typedef int target_greg_t; +#define TARGET_NGREG 18 +typedef target_greg_t target_gregset_t[TARGET_NGREG]; + +typedef struct target_fpregset { + int f_fpcntl[3]; + int f_fpregs[8*3]; +} target_fpregset_t; + +struct target_mcontext { + int version; + target_gregset_t gregs; + target_fpregset_t fpregs; +}; + +#define TARGET_MCONTEXT_VERSION 2 + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_mcontext tuc_mcontext; + abi_long tuc_filler[80]; + target_sigset_t tuc_sigmask; +}; + +struct target_rt_sigframe +{ + abi_ulong pretcode; + int sig; + abi_ulong pinfo; + abi_ulong puc; + char retcode[8]; + struct target_siginfo info; + struct target_ucontext uc; +}; + +static void setup_sigcontext(struct target_sigcontext *sc, CPUM68KState *env, + abi_ulong mask) +{ + uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env); + __put_user(mask, &sc->sc_mask); + __put_user(env->aregs[7], &sc->sc_usp); + __put_user(env->dregs[0], &sc->sc_d0); + __put_user(env->dregs[1], &sc->sc_d1); + __put_user(env->aregs[0], &sc->sc_a0); + __put_user(env->aregs[1], &sc->sc_a1); + __put_user(sr, &sc->sc_sr); + __put_user(env->pc, &sc->sc_pc); +} + +static void +restore_sigcontext(CPUM68KState *env, struct target_sigcontext *sc) +{ + int temp; + + __get_user(env->aregs[7], &sc->sc_usp); + __get_user(env->dregs[0], &sc->sc_d0); + __get_user(env->dregs[1], &sc->sc_d1); + __get_user(env->aregs[0], &sc->sc_a0); + __get_user(env->aregs[1], &sc->sc_a1); + __get_user(env->pc, &sc->sc_pc); + __get_user(temp, &sc->sc_sr); + cpu_m68k_set_ccr(env, temp); +} + +/* + * Determine which stack to use.. + */ +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUM68KState *regs, + size_t frame_size) +{ + unsigned long sp; + + sp = regs->aregs[7]; + + /* This is the X/Open sanctioned signal stack switching. */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + return ((sp - frame_size) & -8UL); +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUM68KState *env) +{ + struct target_sigframe *frame; + abi_ulong frame_addr; + abi_ulong retcode_addr; + abi_ulong sc_addr; + int i; + + frame_addr = get_sigframe(ka, env, sizeof *frame); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + __put_user(sig, &frame->sig); + + sc_addr = frame_addr + offsetof(struct target_sigframe, sc); + __put_user(sc_addr, &frame->psc); + + setup_sigcontext(&frame->sc, env, set->sig[0]); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->extramask[i - 1]); + } + + /* Set up to return from userspace. */ + + retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); + __put_user(retcode_addr, &frame->pretcode); + + /* moveq #,d0; trap #0 */ + + __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16), + (uint32_t *)(frame->retcode)); + + /* Set up to return from userspace */ + + env->aregs[7] = frame_addr; + env->pc = ka->_sa_handler; + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); +} + +static inline void target_rt_save_fpu_state(struct target_ucontext *uc, + CPUM68KState *env) +{ + int i; + target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; + + __put_user(env->fpcr, &fpregs->f_fpcntl[0]); + __put_user(env->fpsr, &fpregs->f_fpcntl[1]); + /* fpiar is not emulated */ + + for (i = 0; i < 8; i++) { + uint32_t high = env->fregs[i].d.high << 16; + __put_user(high, &fpregs->f_fpregs[i * 3]); + __put_user(env->fregs[i].d.low, + (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); + } +} + +static inline int target_rt_setup_ucontext(struct target_ucontext *uc, + CPUM68KState *env) +{ + target_greg_t *gregs = uc->tuc_mcontext.gregs; + uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env); + + __put_user(TARGET_MCONTEXT_VERSION, &uc->tuc_mcontext.version); + __put_user(env->dregs[0], &gregs[0]); + __put_user(env->dregs[1], &gregs[1]); + __put_user(env->dregs[2], &gregs[2]); + __put_user(env->dregs[3], &gregs[3]); + __put_user(env->dregs[4], &gregs[4]); + __put_user(env->dregs[5], &gregs[5]); + __put_user(env->dregs[6], &gregs[6]); + __put_user(env->dregs[7], &gregs[7]); + __put_user(env->aregs[0], &gregs[8]); + __put_user(env->aregs[1], &gregs[9]); + __put_user(env->aregs[2], &gregs[10]); + __put_user(env->aregs[3], &gregs[11]); + __put_user(env->aregs[4], &gregs[12]); + __put_user(env->aregs[5], &gregs[13]); + __put_user(env->aregs[6], &gregs[14]); + __put_user(env->aregs[7], &gregs[15]); + __put_user(env->pc, &gregs[16]); + __put_user(sr, &gregs[17]); + + target_rt_save_fpu_state(uc, env); + + return 0; +} + +static inline void target_rt_restore_fpu_state(CPUM68KState *env, + struct target_ucontext *uc) +{ + int i; + target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; + uint32_t fpcr; + + __get_user(fpcr, &fpregs->f_fpcntl[0]); + cpu_m68k_set_fpcr(env, fpcr); + __get_user(env->fpsr, &fpregs->f_fpcntl[1]); + /* fpiar is not emulated */ + + for (i = 0; i < 8; i++) { + uint32_t high; + __get_user(high, &fpregs->f_fpregs[i * 3]); + env->fregs[i].d.high = high >> 16; + __get_user(env->fregs[i].d.low, + (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); + } +} + +static inline int target_rt_restore_ucontext(CPUM68KState *env, + struct target_ucontext *uc) +{ + int temp; + target_greg_t *gregs = uc->tuc_mcontext.gregs; + + __get_user(temp, &uc->tuc_mcontext.version); + if (temp != TARGET_MCONTEXT_VERSION) + goto badframe; + + /* restore passed registers */ + __get_user(env->dregs[0], &gregs[0]); + __get_user(env->dregs[1], &gregs[1]); + __get_user(env->dregs[2], &gregs[2]); + __get_user(env->dregs[3], &gregs[3]); + __get_user(env->dregs[4], &gregs[4]); + __get_user(env->dregs[5], &gregs[5]); + __get_user(env->dregs[6], &gregs[6]); + __get_user(env->dregs[7], &gregs[7]); + __get_user(env->aregs[0], &gregs[8]); + __get_user(env->aregs[1], &gregs[9]); + __get_user(env->aregs[2], &gregs[10]); + __get_user(env->aregs[3], &gregs[11]); + __get_user(env->aregs[4], &gregs[12]); + __get_user(env->aregs[5], &gregs[13]); + __get_user(env->aregs[6], &gregs[14]); + __get_user(env->aregs[7], &gregs[15]); + __get_user(env->pc, &gregs[16]); + __get_user(temp, &gregs[17]); + cpu_m68k_set_ccr(env, temp); + + target_rt_restore_fpu_state(env, uc); + + return 0; + +badframe: + return 1; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUM68KState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + abi_ulong retcode_addr; + abi_ulong info_addr; + abi_ulong uc_addr; + int err = 0; + int i; + + frame_addr = get_sigframe(ka, env, sizeof *frame); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + __put_user(sig, &frame->sig); + + info_addr = frame_addr + offsetof(struct target_rt_sigframe, info); + __put_user(info_addr, &frame->pinfo); + + uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); + __put_user(uc_addr, &frame->puc); + + tswap_siginfo(&frame->info, info); + + /* Create the ucontext */ + + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, + &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(env->aregs[7]), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, + &frame->uc.tuc_stack.ss_size); + err |= target_rt_setup_ucontext(&frame->uc, env); + + if (err) + goto give_sigsegv; + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + /* Set up to return from userspace. */ + + retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); + __put_user(retcode_addr, &frame->pretcode); + + /* moveq #,d0; notb d0; trap #0 */ + + __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16), + (uint32_t *)(frame->retcode + 0)); + __put_user(0x4e40, (uint16_t *)(frame->retcode + 4)); + + if (err) + goto give_sigsegv; + + /* Set up to return from userspace */ + + env->aregs[7] = frame_addr; + env->pc = ka->_sa_handler; + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} + +long do_sigreturn(CPUM68KState *env) +{ + struct target_sigframe *frame; + abi_ulong frame_addr = env->aregs[7] - 4; + target_sigset_t target_set; + sigset_t set; + int i; + + trace_user_do_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + + /* set blocked signals */ + + __get_user(target_set.sig[0], &frame->sc.sc_mask); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } + + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + /* restore registers */ + + restore_sigcontext(env, &frame->sc); + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUM68KState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr = env->aregs[7] - 4; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + /* restore registers */ + + if (target_rt_restore_ucontext(env, &frame->uc)) + goto badframe; + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) + goto badframe; + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/m68k/target_signal.h b/linux-user/m68k/target_signal.h index 9d2d7343f8..29e0c01191 100644 --- a/linux-user/m68k/target_signal.h +++ b/linux-user/m68k/target_signal.h @@ -26,5 +26,9 @@ static inline abi_ulong get_sp_from_cpustate(CPUM68KState *state) return state->aregs[7]; } - +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUM68KState *env); +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUM68KState *env); #endif /* M68K_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 32a9405e84..16b1caad8b 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3032,413 +3032,6 @@ sigsegv: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_M68K) - -struct target_sigcontext { - abi_ulong sc_mask; - abi_ulong sc_usp; - abi_ulong sc_d0; - abi_ulong sc_d1; - abi_ulong sc_a0; - abi_ulong sc_a1; - unsigned short sc_sr; - abi_ulong sc_pc; -}; - -struct target_sigframe -{ - abi_ulong pretcode; - int sig; - int code; - abi_ulong psc; - char retcode[8]; - abi_ulong extramask[TARGET_NSIG_WORDS-1]; - struct target_sigcontext sc; -}; - -typedef int target_greg_t; -#define TARGET_NGREG 18 -typedef target_greg_t target_gregset_t[TARGET_NGREG]; - -typedef struct target_fpregset { - int f_fpcntl[3]; - int f_fpregs[8*3]; -} target_fpregset_t; - -struct target_mcontext { - int version; - target_gregset_t gregs; - target_fpregset_t fpregs; -}; - -#define TARGET_MCONTEXT_VERSION 2 - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_mcontext tuc_mcontext; - abi_long tuc_filler[80]; - target_sigset_t tuc_sigmask; -}; - -struct target_rt_sigframe -{ - abi_ulong pretcode; - int sig; - abi_ulong pinfo; - abi_ulong puc; - char retcode[8]; - struct target_siginfo info; - struct target_ucontext uc; -}; - -static void setup_sigcontext(struct target_sigcontext *sc, CPUM68KState *env, - abi_ulong mask) -{ - uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env); - __put_user(mask, &sc->sc_mask); - __put_user(env->aregs[7], &sc->sc_usp); - __put_user(env->dregs[0], &sc->sc_d0); - __put_user(env->dregs[1], &sc->sc_d1); - __put_user(env->aregs[0], &sc->sc_a0); - __put_user(env->aregs[1], &sc->sc_a1); - __put_user(sr, &sc->sc_sr); - __put_user(env->pc, &sc->sc_pc); -} - -static void -restore_sigcontext(CPUM68KState *env, struct target_sigcontext *sc) -{ - int temp; - - __get_user(env->aregs[7], &sc->sc_usp); - __get_user(env->dregs[0], &sc->sc_d0); - __get_user(env->dregs[1], &sc->sc_d1); - __get_user(env->aregs[0], &sc->sc_a0); - __get_user(env->aregs[1], &sc->sc_a1); - __get_user(env->pc, &sc->sc_pc); - __get_user(temp, &sc->sc_sr); - cpu_m68k_set_ccr(env, temp); -} - -/* - * Determine which stack to use.. - */ -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUM68KState *regs, - size_t frame_size) -{ - unsigned long sp; - - sp = regs->aregs[7]; - - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - return ((sp - frame_size) & -8UL); -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUM68KState *env) -{ - struct target_sigframe *frame; - abi_ulong frame_addr; - abi_ulong retcode_addr; - abi_ulong sc_addr; - int i; - - frame_addr = get_sigframe(ka, env, sizeof *frame); - trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - __put_user(sig, &frame->sig); - - sc_addr = frame_addr + offsetof(struct target_sigframe, sc); - __put_user(sc_addr, &frame->psc); - - setup_sigcontext(&frame->sc, env, set->sig[0]); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - /* Set up to return from userspace. */ - - retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); - __put_user(retcode_addr, &frame->pretcode); - - /* moveq #,d0; trap #0 */ - - __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16), - (uint32_t *)(frame->retcode)); - - /* Set up to return from userspace */ - - env->aregs[7] = frame_addr; - env->pc = ka->_sa_handler; - - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - force_sigsegv(sig); -} - -static inline void target_rt_save_fpu_state(struct target_ucontext *uc, - CPUM68KState *env) -{ - int i; - target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; - - __put_user(env->fpcr, &fpregs->f_fpcntl[0]); - __put_user(env->fpsr, &fpregs->f_fpcntl[1]); - /* fpiar is not emulated */ - - for (i = 0; i < 8; i++) { - uint32_t high = env->fregs[i].d.high << 16; - __put_user(high, &fpregs->f_fpregs[i * 3]); - __put_user(env->fregs[i].d.low, - (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); - } -} - -static inline int target_rt_setup_ucontext(struct target_ucontext *uc, - CPUM68KState *env) -{ - target_greg_t *gregs = uc->tuc_mcontext.gregs; - uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env); - - __put_user(TARGET_MCONTEXT_VERSION, &uc->tuc_mcontext.version); - __put_user(env->dregs[0], &gregs[0]); - __put_user(env->dregs[1], &gregs[1]); - __put_user(env->dregs[2], &gregs[2]); - __put_user(env->dregs[3], &gregs[3]); - __put_user(env->dregs[4], &gregs[4]); - __put_user(env->dregs[5], &gregs[5]); - __put_user(env->dregs[6], &gregs[6]); - __put_user(env->dregs[7], &gregs[7]); - __put_user(env->aregs[0], &gregs[8]); - __put_user(env->aregs[1], &gregs[9]); - __put_user(env->aregs[2], &gregs[10]); - __put_user(env->aregs[3], &gregs[11]); - __put_user(env->aregs[4], &gregs[12]); - __put_user(env->aregs[5], &gregs[13]); - __put_user(env->aregs[6], &gregs[14]); - __put_user(env->aregs[7], &gregs[15]); - __put_user(env->pc, &gregs[16]); - __put_user(sr, &gregs[17]); - - target_rt_save_fpu_state(uc, env); - - return 0; -} - -static inline void target_rt_restore_fpu_state(CPUM68KState *env, - struct target_ucontext *uc) -{ - int i; - target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; - uint32_t fpcr; - - __get_user(fpcr, &fpregs->f_fpcntl[0]); - cpu_m68k_set_fpcr(env, fpcr); - __get_user(env->fpsr, &fpregs->f_fpcntl[1]); - /* fpiar is not emulated */ - - for (i = 0; i < 8; i++) { - uint32_t high; - __get_user(high, &fpregs->f_fpregs[i * 3]); - env->fregs[i].d.high = high >> 16; - __get_user(env->fregs[i].d.low, - (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); - } -} - -static inline int target_rt_restore_ucontext(CPUM68KState *env, - struct target_ucontext *uc) -{ - int temp; - target_greg_t *gregs = uc->tuc_mcontext.gregs; - - __get_user(temp, &uc->tuc_mcontext.version); - if (temp != TARGET_MCONTEXT_VERSION) - goto badframe; - - /* restore passed registers */ - __get_user(env->dregs[0], &gregs[0]); - __get_user(env->dregs[1], &gregs[1]); - __get_user(env->dregs[2], &gregs[2]); - __get_user(env->dregs[3], &gregs[3]); - __get_user(env->dregs[4], &gregs[4]); - __get_user(env->dregs[5], &gregs[5]); - __get_user(env->dregs[6], &gregs[6]); - __get_user(env->dregs[7], &gregs[7]); - __get_user(env->aregs[0], &gregs[8]); - __get_user(env->aregs[1], &gregs[9]); - __get_user(env->aregs[2], &gregs[10]); - __get_user(env->aregs[3], &gregs[11]); - __get_user(env->aregs[4], &gregs[12]); - __get_user(env->aregs[5], &gregs[13]); - __get_user(env->aregs[6], &gregs[14]); - __get_user(env->aregs[7], &gregs[15]); - __get_user(env->pc, &gregs[16]); - __get_user(temp, &gregs[17]); - cpu_m68k_set_ccr(env, temp); - - target_rt_restore_fpu_state(env, uc); - - return 0; - -badframe: - return 1; -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUM68KState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - abi_ulong retcode_addr; - abi_ulong info_addr; - abi_ulong uc_addr; - int err = 0; - int i; - - frame_addr = get_sigframe(ka, env, sizeof *frame); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - __put_user(sig, &frame->sig); - - info_addr = frame_addr + offsetof(struct target_rt_sigframe, info); - __put_user(info_addr, &frame->pinfo); - - uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); - __put_user(uc_addr, &frame->puc); - - tswap_siginfo(&frame->info, info); - - /* Create the ucontext */ - - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->aregs[7]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - err |= target_rt_setup_ucontext(&frame->uc, env); - - if (err) - goto give_sigsegv; - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - /* Set up to return from userspace. */ - - retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); - __put_user(retcode_addr, &frame->pretcode); - - /* moveq #,d0; notb d0; trap #0 */ - - __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16), - (uint32_t *)(frame->retcode + 0)); - __put_user(0x4e40, (uint16_t *)(frame->retcode + 4)); - - if (err) - goto give_sigsegv; - - /* Set up to return from userspace */ - - env->aregs[7] = frame_addr; - env->pc = ka->_sa_handler; - - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} - -long do_sigreturn(CPUM68KState *env) -{ - struct target_sigframe *frame; - abi_ulong frame_addr = env->aregs[7] - 4; - target_sigset_t target_set; - sigset_t set; - int i; - - trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - - /* set blocked signals */ - - __get_user(target_set.sig[0], &frame->sc.sc_mask); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - /* restore registers */ - - restore_sigcontext(env, &frame->sc); - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUM68KState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr = env->aregs[7] - 4; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - /* restore registers */ - - if (target_rt_restore_ucontext(env, &frame->uc)) - goto badframe; - - if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, get_sp_from_cpustate(env)) == -EFAULT) - goto badframe; - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - #elif defined(TARGET_ALPHA) struct target_sigcontext { From da04107af373ec980a821798717c81a79eef93ff Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:26 +0200 Subject: [PATCH 0136/2380] linux-user: move alpha signal.c parts to alpha directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to alpha/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-12-laurent@vivier.eu> --- linux-user/alpha/signal.c | 262 +++++++++++++++++++++++++++++++ linux-user/alpha/target_signal.h | 5 + linux-user/signal.c | 259 ------------------------------ 3 files changed, 267 insertions(+), 259 deletions(-) diff --git a/linux-user/alpha/signal.c b/linux-user/alpha/signal.c index 02ca338b6c..a8c718f2c6 100644 --- a/linux-user/alpha/signal.c +++ b/linux-user/alpha/signal.c @@ -16,3 +16,265 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_long sc_onstack; + abi_long sc_mask; + abi_long sc_pc; + abi_long sc_ps; + abi_long sc_regs[32]; + abi_long sc_ownedfp; + abi_long sc_fpregs[32]; + abi_ulong sc_fpcr; + abi_ulong sc_fp_control; + abi_ulong sc_reserved1; + abi_ulong sc_reserved2; + abi_ulong sc_ssize; + abi_ulong sc_sbase; + abi_ulong sc_traparg_a0; + abi_ulong sc_traparg_a1; + abi_ulong sc_traparg_a2; + abi_ulong sc_fp_trap_pc; + abi_ulong sc_fp_trigger_sum; + abi_ulong sc_fp_trigger_inst; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + abi_ulong tuc_osf_sigmask; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_sigframe { + struct target_sigcontext sc; + unsigned int retcode[3]; +}; + +struct target_rt_sigframe { + target_siginfo_t info; + struct target_ucontext uc; + unsigned int retcode[3]; +}; + +#define INSN_MOV_R30_R16 0x47fe0410 +#define INSN_LDI_R0 0x201f0000 +#define INSN_CALLSYS 0x00000083 + +static void setup_sigcontext(struct target_sigcontext *sc, CPUAlphaState *env, + abi_ulong frame_addr, target_sigset_t *set) +{ + int i; + + __put_user(on_sig_stack(frame_addr), &sc->sc_onstack); + __put_user(set->sig[0], &sc->sc_mask); + __put_user(env->pc, &sc->sc_pc); + __put_user(8, &sc->sc_ps); + + for (i = 0; i < 31; ++i) { + __put_user(env->ir[i], &sc->sc_regs[i]); + } + __put_user(0, &sc->sc_regs[31]); + + for (i = 0; i < 31; ++i) { + __put_user(env->fir[i], &sc->sc_fpregs[i]); + } + __put_user(0, &sc->sc_fpregs[31]); + __put_user(cpu_alpha_load_fpcr(env), &sc->sc_fpcr); + + __put_user(0, &sc->sc_traparg_a0); /* FIXME */ + __put_user(0, &sc->sc_traparg_a1); /* FIXME */ + __put_user(0, &sc->sc_traparg_a2); /* FIXME */ +} + +static void restore_sigcontext(CPUAlphaState *env, + struct target_sigcontext *sc) +{ + uint64_t fpcr; + int i; + + __get_user(env->pc, &sc->sc_pc); + + for (i = 0; i < 31; ++i) { + __get_user(env->ir[i], &sc->sc_regs[i]); + } + for (i = 0; i < 31; ++i) { + __get_user(env->fir[i], &sc->sc_fpregs[i]); + } + + __get_user(fpcr, &sc->sc_fpcr); + cpu_alpha_store_fpcr(env, fpcr); +} + +static inline abi_ulong get_sigframe(struct target_sigaction *sa, + CPUAlphaState *env, + unsigned long framesize) +{ + abi_ulong sp = env->ir[IR_SP]; + + /* This is the X/Open sanctioned signal stack switching. */ + if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + return (sp - framesize) & -32; +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUAlphaState *env) +{ + abi_ulong frame_addr, r26; + struct target_sigframe *frame; + int err = 0; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + setup_sigcontext(&frame->sc, env, frame_addr, set); + + if (ka->sa_restorer) { + r26 = ka->sa_restorer; + } else { + __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); + __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn, + &frame->retcode[1]); + __put_user(INSN_CALLSYS, &frame->retcode[2]); + /* imb() */ + r26 = frame_addr + offsetof(struct target_sigframe, retcode); + } + + unlock_user_struct(frame, frame_addr, 1); + + if (err) { +give_sigsegv: + force_sigsegv(sig); + return; + } + + env->ir[IR_RA] = r26; + env->ir[IR_PV] = env->pc = ka->_sa_handler; + env->ir[IR_A0] = sig; + env->ir[IR_A1] = 0; + env->ir[IR_A2] = frame_addr + offsetof(struct target_sigframe, sc); + env->ir[IR_SP] = frame_addr; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUAlphaState *env) +{ + abi_ulong frame_addr, r26; + struct target_rt_sigframe *frame; + int i, err = 0; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + tswap_siginfo(&frame->info, info); + + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + __put_user(set->sig[0], &frame->uc.tuc_osf_sigmask); + __put_user(target_sigaltstack_used.ss_sp, + &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(env->ir[IR_SP]), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, + &frame->uc.tuc_stack.ss_size); + setup_sigcontext(&frame->uc.tuc_mcontext, env, frame_addr, set); + for (i = 0; i < TARGET_NSIG_WORDS; ++i) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + if (ka->sa_restorer) { + r26 = ka->sa_restorer; + } else { + __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); + __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn, + &frame->retcode[1]); + __put_user(INSN_CALLSYS, &frame->retcode[2]); + /* imb(); */ + r26 = frame_addr + offsetof(struct target_sigframe, retcode); + } + + if (err) { +give_sigsegv: + force_sigsegv(sig); + return; + } + + env->ir[IR_RA] = r26; + env->ir[IR_PV] = env->pc = ka->_sa_handler; + env->ir[IR_A0] = sig; + env->ir[IR_A1] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->ir[IR_A2] = frame_addr + offsetof(struct target_rt_sigframe, uc); + env->ir[IR_SP] = frame_addr; +} + +long do_sigreturn(CPUAlphaState *env) +{ + struct target_sigcontext *sc; + abi_ulong sc_addr = env->ir[IR_A0]; + target_sigset_t target_set; + sigset_t set; + + if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) { + goto badframe; + } + + target_sigemptyset(&target_set); + __get_user(target_set.sig[0], &sc->sc_mask); + + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + restore_sigcontext(env, sc); + unlock_user_struct(sc, sc_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUAlphaState *env) +{ + abi_ulong frame_addr = env->ir[IR_A0]; + struct target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.tuc_stack), + 0, env->ir[IR_SP]) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/alpha/target_signal.h b/linux-user/alpha/target_signal.h index f1ed00d50e..42343a1ae6 100644 --- a/linux-user/alpha/target_signal.h +++ b/linux-user/alpha/target_signal.h @@ -55,4 +55,9 @@ static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state) #define TARGET_GEN_SUBRNG6 -24 #define TARGET_GEN_SUBRNG7 -25 +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUAlphaState *env); +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUAlphaState *env); #endif /* ALPHA_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 16b1caad8b..7c2a963e7c 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3032,265 +3032,6 @@ sigsegv: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_ALPHA) - -struct target_sigcontext { - abi_long sc_onstack; - abi_long sc_mask; - abi_long sc_pc; - abi_long sc_ps; - abi_long sc_regs[32]; - abi_long sc_ownedfp; - abi_long sc_fpregs[32]; - abi_ulong sc_fpcr; - abi_ulong sc_fp_control; - abi_ulong sc_reserved1; - abi_ulong sc_reserved2; - abi_ulong sc_ssize; - abi_ulong sc_sbase; - abi_ulong sc_traparg_a0; - abi_ulong sc_traparg_a1; - abi_ulong sc_traparg_a2; - abi_ulong sc_fp_trap_pc; - abi_ulong sc_fp_trigger_sum; - abi_ulong sc_fp_trigger_inst; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - abi_ulong tuc_osf_sigmask; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; -}; - -struct target_sigframe { - struct target_sigcontext sc; - unsigned int retcode[3]; -}; - -struct target_rt_sigframe { - target_siginfo_t info; - struct target_ucontext uc; - unsigned int retcode[3]; -}; - -#define INSN_MOV_R30_R16 0x47fe0410 -#define INSN_LDI_R0 0x201f0000 -#define INSN_CALLSYS 0x00000083 - -static void setup_sigcontext(struct target_sigcontext *sc, CPUAlphaState *env, - abi_ulong frame_addr, target_sigset_t *set) -{ - int i; - - __put_user(on_sig_stack(frame_addr), &sc->sc_onstack); - __put_user(set->sig[0], &sc->sc_mask); - __put_user(env->pc, &sc->sc_pc); - __put_user(8, &sc->sc_ps); - - for (i = 0; i < 31; ++i) { - __put_user(env->ir[i], &sc->sc_regs[i]); - } - __put_user(0, &sc->sc_regs[31]); - - for (i = 0; i < 31; ++i) { - __put_user(env->fir[i], &sc->sc_fpregs[i]); - } - __put_user(0, &sc->sc_fpregs[31]); - __put_user(cpu_alpha_load_fpcr(env), &sc->sc_fpcr); - - __put_user(0, &sc->sc_traparg_a0); /* FIXME */ - __put_user(0, &sc->sc_traparg_a1); /* FIXME */ - __put_user(0, &sc->sc_traparg_a2); /* FIXME */ -} - -static void restore_sigcontext(CPUAlphaState *env, - struct target_sigcontext *sc) -{ - uint64_t fpcr; - int i; - - __get_user(env->pc, &sc->sc_pc); - - for (i = 0; i < 31; ++i) { - __get_user(env->ir[i], &sc->sc_regs[i]); - } - for (i = 0; i < 31; ++i) { - __get_user(env->fir[i], &sc->sc_fpregs[i]); - } - - __get_user(fpcr, &sc->sc_fpcr); - cpu_alpha_store_fpcr(env, fpcr); -} - -static inline abi_ulong get_sigframe(struct target_sigaction *sa, - CPUAlphaState *env, - unsigned long framesize) -{ - abi_ulong sp = env->ir[IR_SP]; - - /* This is the X/Open sanctioned signal stack switching. */ - if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - return (sp - framesize) & -32; -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUAlphaState *env) -{ - abi_ulong frame_addr, r26; - struct target_sigframe *frame; - int err = 0; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - setup_sigcontext(&frame->sc, env, frame_addr, set); - - if (ka->sa_restorer) { - r26 = ka->sa_restorer; - } else { - __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); - __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn, - &frame->retcode[1]); - __put_user(INSN_CALLSYS, &frame->retcode[2]); - /* imb() */ - r26 = frame_addr + offsetof(struct target_sigframe, retcode); - } - - unlock_user_struct(frame, frame_addr, 1); - - if (err) { -give_sigsegv: - force_sigsegv(sig); - return; - } - - env->ir[IR_RA] = r26; - env->ir[IR_PV] = env->pc = ka->_sa_handler; - env->ir[IR_A0] = sig; - env->ir[IR_A1] = 0; - env->ir[IR_A2] = frame_addr + offsetof(struct target_sigframe, sc); - env->ir[IR_SP] = frame_addr; -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUAlphaState *env) -{ - abi_ulong frame_addr, r26; - struct target_rt_sigframe *frame; - int i, err = 0; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - tswap_siginfo(&frame->info, info); - - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(set->sig[0], &frame->uc.tuc_osf_sigmask); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->ir[IR_SP]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - setup_sigcontext(&frame->uc.tuc_mcontext, env, frame_addr, set); - for (i = 0; i < TARGET_NSIG_WORDS; ++i) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - if (ka->sa_restorer) { - r26 = ka->sa_restorer; - } else { - __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); - __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn, - &frame->retcode[1]); - __put_user(INSN_CALLSYS, &frame->retcode[2]); - /* imb(); */ - r26 = frame_addr + offsetof(struct target_sigframe, retcode); - } - - if (err) { -give_sigsegv: - force_sigsegv(sig); - return; - } - - env->ir[IR_RA] = r26; - env->ir[IR_PV] = env->pc = ka->_sa_handler; - env->ir[IR_A0] = sig; - env->ir[IR_A1] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->ir[IR_A2] = frame_addr + offsetof(struct target_rt_sigframe, uc); - env->ir[IR_SP] = frame_addr; -} - -long do_sigreturn(CPUAlphaState *env) -{ - struct target_sigcontext *sc; - abi_ulong sc_addr = env->ir[IR_A0]; - target_sigset_t target_set; - sigset_t set; - - if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) { - goto badframe; - } - - target_sigemptyset(&target_set); - __get_user(target_set.sig[0], &sc->sc_mask); - - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - restore_sigcontext(env, sc); - unlock_user_struct(sc, sc_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUAlphaState *env) -{ - abi_ulong frame_addr = env->ir[IR_A0]; - struct target_rt_sigframe *frame; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - restore_sigcontext(env, &frame->uc.tuc_mcontext); - if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, - uc.tuc_stack), - 0, env->ir[IR_SP]) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - #elif defined(TARGET_TILEGX) struct target_sigcontext { From ea14059a3678b69f0b102573088c02a9ce7aed9a Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:27 +0200 Subject: [PATCH 0137/2380] linux-user: move tilegx signal.c parts to tilegx directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to tilegx/signal.c, except adding includes and exporting setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180424192635.6027-13-laurent@vivier.eu> --- linux-user/signal.c | 165 ----------------------------- linux-user/tilegx/signal.c | 168 ++++++++++++++++++++++++++++++ linux-user/tilegx/target_signal.h | 4 +- 3 files changed, 171 insertions(+), 166 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 7c2a963e7c..5a3e5bff5e 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3032,171 +3032,6 @@ sigsegv: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_TILEGX) - -struct target_sigcontext { - union { - /* General-purpose registers. */ - abi_ulong gregs[56]; - struct { - abi_ulong __gregs[53]; - abi_ulong tp; /* Aliases gregs[TREG_TP]. */ - abi_ulong sp; /* Aliases gregs[TREG_SP]. */ - abi_ulong lr; /* Aliases gregs[TREG_LR]. */ - }; - }; - abi_ulong pc; /* Program counter. */ - abi_ulong ics; /* In Interrupt Critical Section? */ - abi_ulong faultnum; /* Fault number. */ - abi_ulong pad[5]; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_rt_sigframe { - unsigned char save_area[16]; /* caller save area */ - struct target_siginfo info; - struct target_ucontext uc; - abi_ulong retcode[2]; -}; - -#define INSN_MOVELI_R10_139 0x00045fe551483000ULL /* { moveli r10, 139 } */ -#define INSN_SWINT1 0x286b180051485000ULL /* { swint1 } */ - - -static void setup_sigcontext(struct target_sigcontext *sc, - CPUArchState *env, int signo) -{ - int i; - - for (i = 0; i < TILEGX_R_COUNT; ++i) { - __put_user(env->regs[i], &sc->gregs[i]); - } - - __put_user(env->pc, &sc->pc); - __put_user(0, &sc->ics); - __put_user(signo, &sc->faultnum); -} - -static void restore_sigcontext(CPUTLGState *env, struct target_sigcontext *sc) -{ - int i; - - for (i = 0; i < TILEGX_R_COUNT; ++i) { - __get_user(env->regs[i], &sc->gregs[i]); - } - - __get_user(env->pc, &sc->pc); -} - -static abi_ulong get_sigframe(struct target_sigaction *ka, CPUArchState *env, - size_t frame_size) -{ - unsigned long sp = env->regs[TILEGX_R_SP]; - - if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size))) { - return -1UL; - } - - if ((ka->sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - sp -= frame_size; - sp &= -16UL; - return sp; -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUArchState *env) -{ - abi_ulong frame_addr; - struct target_rt_sigframe *frame; - unsigned long restorer; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - /* Always write at least the signal number for the stack backtracer. */ - if (ka->sa_flags & TARGET_SA_SIGINFO) { - /* At sigreturn time, restore the callee-save registers too. */ - tswap_siginfo(&frame->info, info); - /* regs->flags |= PT_FLAGS_RESTORE_REGS; FIXME: we can skip it? */ - } else { - __put_user(info->si_signo, &frame->info.si_signo); - } - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->regs[TILEGX_R_SP]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); - setup_sigcontext(&frame->uc.tuc_mcontext, env, info->si_signo); - - if (ka->sa_flags & TARGET_SA_RESTORER) { - restorer = (unsigned long) ka->sa_restorer; - } else { - __put_user(INSN_MOVELI_R10_139, &frame->retcode[0]); - __put_user(INSN_SWINT1, &frame->retcode[1]); - restorer = frame_addr + offsetof(struct target_rt_sigframe, retcode); - } - env->pc = (unsigned long) ka->_sa_handler; - env->regs[TILEGX_R_SP] = (unsigned long) frame; - env->regs[TILEGX_R_LR] = restorer; - env->regs[0] = (unsigned long) sig; - env->regs[1] = (unsigned long) &frame->info; - env->regs[2] = (unsigned long) &frame->uc; - /* regs->flags |= PT_FLAGS_CALLER_SAVES; FIXME: we can skip it? */ - - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - force_sigsegv(sig); -} - -long do_rt_sigreturn(CPUTLGState *env) -{ - abi_ulong frame_addr = env->regs[TILEGX_R_SP]; - struct target_rt_sigframe *frame; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - restore_sigcontext(env, &frame->uc.tuc_mcontext); - if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, - uc.tuc_stack), - 0, env->regs[TILEGX_R_SP]) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - - - badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - #elif defined(TARGET_RISCV) /* Signal handler invocation must be transparent for the code being diff --git a/linux-user/tilegx/signal.c b/linux-user/tilegx/signal.c index 02ca338b6c..8f54f54f95 100644 --- a/linux-user/tilegx/signal.c +++ b/linux-user/tilegx/signal.c @@ -16,3 +16,171 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + union { + /* General-purpose registers. */ + abi_ulong gregs[56]; + struct { + abi_ulong __gregs[53]; + abi_ulong tp; /* Aliases gregs[TREG_TP]. */ + abi_ulong sp; /* Aliases gregs[TREG_SP]. */ + abi_ulong lr; /* Aliases gregs[TREG_LR]. */ + }; + }; + abi_ulong pc; /* Program counter. */ + abi_ulong ics; /* In Interrupt Critical Section? */ + abi_ulong faultnum; /* Fault number. */ + abi_ulong pad[5]; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +struct target_rt_sigframe { + unsigned char save_area[16]; /* caller save area */ + struct target_siginfo info; + struct target_ucontext uc; + abi_ulong retcode[2]; +}; + +#define INSN_MOVELI_R10_139 0x00045fe551483000ULL /* { moveli r10, 139 } */ +#define INSN_SWINT1 0x286b180051485000ULL /* { swint1 } */ + + +static void setup_sigcontext(struct target_sigcontext *sc, + CPUArchState *env, int signo) +{ + int i; + + for (i = 0; i < TILEGX_R_COUNT; ++i) { + __put_user(env->regs[i], &sc->gregs[i]); + } + + __put_user(env->pc, &sc->pc); + __put_user(0, &sc->ics); + __put_user(signo, &sc->faultnum); +} + +static void restore_sigcontext(CPUTLGState *env, struct target_sigcontext *sc) +{ + int i; + + for (i = 0; i < TILEGX_R_COUNT; ++i) { + __get_user(env->regs[i], &sc->gregs[i]); + } + + __get_user(env->pc, &sc->pc); +} + +static abi_ulong get_sigframe(struct target_sigaction *ka, CPUArchState *env, + size_t frame_size) +{ + unsigned long sp = env->regs[TILEGX_R_SP]; + + if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size))) { + return -1UL; + } + + if ((ka->sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + sp -= frame_size; + sp &= -16UL; + return sp; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUArchState *env) +{ + abi_ulong frame_addr; + struct target_rt_sigframe *frame; + unsigned long restorer; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + /* Always write at least the signal number for the stack backtracer. */ + if (ka->sa_flags & TARGET_SA_SIGINFO) { + /* At sigreturn time, restore the callee-save registers too. */ + tswap_siginfo(&frame->info, info); + /* regs->flags |= PT_FLAGS_RESTORE_REGS; FIXME: we can skip it? */ + } else { + __put_user(info->si_signo, &frame->info.si_signo); + } + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(env->regs[TILEGX_R_SP]), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); + setup_sigcontext(&frame->uc.tuc_mcontext, env, info->si_signo); + + if (ka->sa_flags & TARGET_SA_RESTORER) { + restorer = (unsigned long) ka->sa_restorer; + } else { + __put_user(INSN_MOVELI_R10_139, &frame->retcode[0]); + __put_user(INSN_SWINT1, &frame->retcode[1]); + restorer = frame_addr + offsetof(struct target_rt_sigframe, retcode); + } + env->pc = (unsigned long) ka->_sa_handler; + env->regs[TILEGX_R_SP] = (unsigned long) frame; + env->regs[TILEGX_R_LR] = restorer; + env->regs[0] = (unsigned long) sig; + env->regs[1] = (unsigned long) &frame->info; + env->regs[2] = (unsigned long) &frame->uc; + /* regs->flags |= PT_FLAGS_CALLER_SAVES; FIXME: we can skip it? */ + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); +} + +long do_rt_sigreturn(CPUTLGState *env) +{ + abi_ulong frame_addr = env->regs[TILEGX_R_SP]; + struct target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.tuc_stack), + 0, env->regs[TILEGX_R_SP]) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/tilegx/target_signal.h b/linux-user/tilegx/target_signal.h index f64551a8cf..132d7781fe 100644 --- a/linux-user/tilegx/target_signal.h +++ b/linux-user/tilegx/target_signal.h @@ -25,5 +25,7 @@ static inline abi_ulong get_sp_from_cpustate(CPUTLGState *state) return state->regs[TILEGX_R_SP]; } - +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUArchState *env); #endif /* TILEGX_TARGET_SIGNAL_H */ From 9c3221c192275a16080381e82a3d72c843f52520 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:28 +0200 Subject: [PATCH 0138/2380] linux-user: move riscv signal.c parts to riscv directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to riscv/signal.c, except adding includes and exporting setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-14-laurent@vivier.eu> --- linux-user/riscv/signal.c | 200 +++++++++++++++++++++++++++++++ linux-user/riscv/target_signal.h | 3 + linux-user/signal.c | 197 ------------------------------ 3 files changed, 203 insertions(+), 197 deletions(-) diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index 02ca338b6c..718f3a5679 100644 --- a/linux-user/riscv/signal.c +++ b/linux-user/riscv/signal.c @@ -16,3 +16,203 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +/* Signal handler invocation must be transparent for the code being + interrupted. Complete CPU (hart) state is saved on entry and restored + before returning from the handler. Process sigmask is also saved to block + signals while the handler is running. The handler gets its own stack, + which also doubles as storage for the CPU state and sigmask. + + The code below is qemu re-implementation of arch/riscv/kernel/signal.c */ + +struct target_sigcontext { + abi_long pc; + abi_long gpr[31]; /* x0 is not present, so all offsets must be -1 */ + uint64_t fpr[32]; + uint32_t fcsr; +}; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */ + +struct target_ucontext { + unsigned long uc_flags; + struct target_ucontext *uc_link; + target_stack_t uc_stack; + struct target_sigcontext uc_mcontext; + target_sigset_t uc_sigmask; +}; + +struct target_rt_sigframe { + uint32_t tramp[2]; /* not in kernel, which uses VDSO instead */ + struct target_siginfo info; + struct target_ucontext uc; +}; + +static abi_ulong get_sigframe(struct target_sigaction *ka, + CPURISCVState *regs, size_t framesize) +{ + abi_ulong sp = regs->gpr[xSP]; + int onsigstack = on_sig_stack(sp); + + /* redzone */ + /* This is the X/Open sanctioned signal stack switching. */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + sp -= framesize; + sp &= ~3UL; /* align sp on 4-byte boundary */ + + /* If we are on the alternate signal stack and would overflow it, don't. + Return an always-bogus address instead so we will die with SIGSEGV. */ + if (onsigstack && !likely(on_sig_stack(sp))) { + return -1L; + } + + return sp; +} + +static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env) +{ + int i; + + __put_user(env->pc, &sc->pc); + + for (i = 1; i < 32; i++) { + __put_user(env->gpr[i], &sc->gpr[i - 1]); + } + for (i = 0; i < 32; i++) { + __put_user(env->fpr[i], &sc->fpr[i]); + } + + uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/ + __put_user(fcsr, &sc->fcsr); +} + +static void setup_ucontext(struct target_ucontext *uc, + CPURISCVState *env, target_sigset_t *set) +{ + abi_ulong ss_sp = (target_ulong)target_sigaltstack_used.ss_sp; + abi_ulong ss_flags = sas_ss_flags(env->gpr[xSP]); + abi_ulong ss_size = target_sigaltstack_used.ss_size; + + __put_user(0, &(uc->uc_flags)); + __put_user(0, &(uc->uc_link)); + + __put_user(ss_sp, &(uc->uc_stack.ss_sp)); + __put_user(ss_flags, &(uc->uc_stack.ss_flags)); + __put_user(ss_size, &(uc->uc_stack.ss_size)); + + int i; + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &(uc->uc_sigmask.sig[i])); + } + + setup_sigcontext(&uc->uc_mcontext, env); +} + +static inline void install_sigtramp(uint32_t *tramp) +{ + __put_user(0x08b00893, tramp + 0); /* li a7, 139 = __NR_rt_sigreturn */ + __put_user(0x00000073, tramp + 1); /* ecall */ +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPURISCVState *env) +{ + abi_ulong frame_addr; + struct target_rt_sigframe *frame; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto badframe; + } + + setup_ucontext(&frame->uc, env, set); + tswap_siginfo(&frame->info, info); + install_sigtramp(frame->tramp); + + env->pc = ka->_sa_handler; + env->gpr[xSP] = frame_addr; + env->gpr[xA0] = sig; + env->gpr[xA1] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->gpr[xA2] = frame_addr + offsetof(struct target_rt_sigframe, uc); + env->gpr[xRA] = frame_addr + offsetof(struct target_rt_sigframe, tramp); + + return; + +badframe: + unlock_user_struct(frame, frame_addr, 1); + if (sig == TARGET_SIGSEGV) { + ka->_sa_handler = TARGET_SIG_DFL; + } + force_sig(TARGET_SIGSEGV); +} + +static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc) +{ + int i; + + __get_user(env->pc, &sc->pc); + + for (i = 1; i < 32; ++i) { + __get_user(env->gpr[i], &sc->gpr[i - 1]); + } + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i], &sc->fpr[i]); + } + + uint32_t fcsr; + __get_user(fcsr, &sc->fcsr); + csr_write_helper(env, fcsr, CSR_FCSR); +} + +static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc) +{ + sigset_t blocked; + target_sigset_t target_set; + int i; + + target_sigemptyset(&target_set); + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &(uc->uc_sigmask.sig[i])); + } + + target_to_host_sigset_internal(&blocked, &target_set); + set_sigmask(&blocked); + + restore_sigcontext(env, &uc->uc_mcontext); +} + +long do_rt_sigreturn(CPURISCVState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + + frame_addr = env->gpr[xSP]; + trace_user_do_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + restore_ucontext(env, &frame->uc); + + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.uc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} diff --git a/linux-user/riscv/target_signal.h b/linux-user/riscv/target_signal.h index ce77f752e3..6ac8a88de6 100644 --- a/linux-user/riscv/target_signal.h +++ b/linux-user/riscv/target_signal.h @@ -20,4 +20,7 @@ static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state) return state->gpr[xSP]; } +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPURISCVState *env); #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 5a3e5bff5e..b42fe4ff93 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3032,203 +3032,6 @@ sigsegv: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_RISCV) - -/* Signal handler invocation must be transparent for the code being - interrupted. Complete CPU (hart) state is saved on entry and restored - before returning from the handler. Process sigmask is also saved to block - signals while the handler is running. The handler gets its own stack, - which also doubles as storage for the CPU state and sigmask. - - The code below is qemu re-implementation of arch/riscv/kernel/signal.c */ - -struct target_sigcontext { - abi_long pc; - abi_long gpr[31]; /* x0 is not present, so all offsets must be -1 */ - uint64_t fpr[32]; - uint32_t fcsr; -}; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */ - -struct target_ucontext { - unsigned long uc_flags; - struct target_ucontext *uc_link; - target_stack_t uc_stack; - struct target_sigcontext uc_mcontext; - target_sigset_t uc_sigmask; -}; - -struct target_rt_sigframe { - uint32_t tramp[2]; /* not in kernel, which uses VDSO instead */ - struct target_siginfo info; - struct target_ucontext uc; -}; - -static abi_ulong get_sigframe(struct target_sigaction *ka, - CPURISCVState *regs, size_t framesize) -{ - abi_ulong sp = regs->gpr[xSP]; - int onsigstack = on_sig_stack(sp); - - /* redzone */ - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - sp -= framesize; - sp &= ~3UL; /* align sp on 4-byte boundary */ - - /* If we are on the alternate signal stack and would overflow it, don't. - Return an always-bogus address instead so we will die with SIGSEGV. */ - if (onsigstack && !likely(on_sig_stack(sp))) { - return -1L; - } - - return sp; -} - -static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env) -{ - int i; - - __put_user(env->pc, &sc->pc); - - for (i = 1; i < 32; i++) { - __put_user(env->gpr[i], &sc->gpr[i - 1]); - } - for (i = 0; i < 32; i++) { - __put_user(env->fpr[i], &sc->fpr[i]); - } - - uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/ - __put_user(fcsr, &sc->fcsr); -} - -static void setup_ucontext(struct target_ucontext *uc, - CPURISCVState *env, target_sigset_t *set) -{ - abi_ulong ss_sp = (target_ulong)target_sigaltstack_used.ss_sp; - abi_ulong ss_flags = sas_ss_flags(env->gpr[xSP]); - abi_ulong ss_size = target_sigaltstack_used.ss_size; - - __put_user(0, &(uc->uc_flags)); - __put_user(0, &(uc->uc_link)); - - __put_user(ss_sp, &(uc->uc_stack.ss_sp)); - __put_user(ss_flags, &(uc->uc_stack.ss_flags)); - __put_user(ss_size, &(uc->uc_stack.ss_size)); - - int i; - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &(uc->uc_sigmask.sig[i])); - } - - setup_sigcontext(&uc->uc_mcontext, env); -} - -static inline void install_sigtramp(uint32_t *tramp) -{ - __put_user(0x08b00893, tramp + 0); /* li a7, 139 = __NR_rt_sigreturn */ - __put_user(0x00000073, tramp + 1); /* ecall */ -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPURISCVState *env) -{ - abi_ulong frame_addr; - struct target_rt_sigframe *frame; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto badframe; - } - - setup_ucontext(&frame->uc, env, set); - tswap_siginfo(&frame->info, info); - install_sigtramp(frame->tramp); - - env->pc = ka->_sa_handler; - env->gpr[xSP] = frame_addr; - env->gpr[xA0] = sig; - env->gpr[xA1] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->gpr[xA2] = frame_addr + offsetof(struct target_rt_sigframe, uc); - env->gpr[xRA] = frame_addr + offsetof(struct target_rt_sigframe, tramp); - - return; - -badframe: - unlock_user_struct(frame, frame_addr, 1); - if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; - } - force_sig(TARGET_SIGSEGV); -} - -static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc) -{ - int i; - - __get_user(env->pc, &sc->pc); - - for (i = 1; i < 32; ++i) { - __get_user(env->gpr[i], &sc->gpr[i - 1]); - } - for (i = 0; i < 32; ++i) { - __get_user(env->fpr[i], &sc->fpr[i]); - } - - uint32_t fcsr; - __get_user(fcsr, &sc->fcsr); - csr_write_helper(env, fcsr, CSR_FCSR); -} - -static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc) -{ - sigset_t blocked; - target_sigset_t target_set; - int i; - - target_sigemptyset(&target_set); - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &(uc->uc_sigmask.sig[i])); - } - - target_to_host_sigset_internal(&blocked, &target_set); - set_sigmask(&blocked); - - restore_sigcontext(env, &uc->uc_mcontext); -} - -long do_rt_sigreturn(CPURISCVState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - - frame_addr = env->gpr[xSP]; - trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - restore_ucontext(env, &frame->uc); - - if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, - uc.uc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return 0; -} - #elif defined(TARGET_HPPA) struct target_sigcontext { From aca77d5e596e49b12da2504693dd7109519d0830 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:29 +0200 Subject: [PATCH 0139/2380] linux-user: move hppa signal.c parts to hppa directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to hppa/signal.c, except adding includes and exporting setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-15-laurent@vivier.eu> --- linux-user/hppa/signal.c | 192 ++++++++++++++++++++++++++++++++ linux-user/hppa/target_signal.h | 3 + linux-user/signal.c | 189 ------------------------------- 3 files changed, 195 insertions(+), 189 deletions(-) diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c index 02ca338b6c..585af3a37f 100644 --- a/linux-user/hppa/signal.c +++ b/linux-user/hppa/signal.c @@ -16,3 +16,195 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_ulong sc_flags; + abi_ulong sc_gr[32]; + uint64_t sc_fr[32]; + abi_ulong sc_iasq[2]; + abi_ulong sc_iaoq[2]; + abi_ulong sc_sar; +}; + +struct target_ucontext { + abi_uint tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + abi_uint pad[1]; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_rt_sigframe { + abi_uint tramp[9]; + target_siginfo_t info; + struct target_ucontext uc; + /* hidden location of upper halves of pa2.0 64-bit gregs */ +}; + +static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env) +{ + int flags = 0; + int i; + + /* ??? if on_sig_stack, flags |= 1 (PARISC_SC_FLAG_ONSTACK). */ + + if (env->iaoq_f < TARGET_PAGE_SIZE) { + /* In the gateway page, executing a syscall. */ + flags |= 2; /* PARISC_SC_FLAG_IN_SYSCALL */ + __put_user(env->gr[31], &sc->sc_iaoq[0]); + __put_user(env->gr[31] + 4, &sc->sc_iaoq[1]); + } else { + __put_user(env->iaoq_f, &sc->sc_iaoq[0]); + __put_user(env->iaoq_b, &sc->sc_iaoq[1]); + } + __put_user(0, &sc->sc_iasq[0]); + __put_user(0, &sc->sc_iasq[1]); + __put_user(flags, &sc->sc_flags); + + __put_user(cpu_hppa_get_psw(env), &sc->sc_gr[0]); + for (i = 1; i < 32; ++i) { + __put_user(env->gr[i], &sc->sc_gr[i]); + } + + __put_user((uint64_t)env->fr0_shadow << 32, &sc->sc_fr[0]); + for (i = 1; i < 32; ++i) { + __put_user(env->fr[i], &sc->sc_fr[i]); + } + + __put_user(env->cr[CR_SAR], &sc->sc_sar); +} + +static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) +{ + target_ulong psw; + int i; + + __get_user(psw, &sc->sc_gr[0]); + cpu_hppa_put_psw(env, psw); + + for (i = 1; i < 32; ++i) { + __get_user(env->gr[i], &sc->sc_gr[i]); + } + for (i = 0; i < 32; ++i) { + __get_user(env->fr[i], &sc->sc_fr[i]); + } + cpu_hppa_loaded_fr0(env); + + __get_user(env->iaoq_f, &sc->sc_iaoq[0]); + __get_user(env->iaoq_b, &sc->sc_iaoq[1]); + __get_user(env->cr[CR_SAR], &sc->sc_sar); +} + +/* No, this doesn't look right, but it's copied straight from the kernel. */ +#define PARISC_RT_SIGFRAME_SIZE32 \ + ((sizeof(struct target_rt_sigframe) + 48 + 64) & -64) + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUArchState *env) +{ + abi_ulong frame_addr, sp, haddr; + struct target_rt_sigframe *frame; + int i; + + sp = env->gr[30]; + if (ka->sa_flags & TARGET_SA_ONSTACK) { + if (sas_ss_flags(sp) == 0) { + sp = (target_sigaltstack_used.ss_sp + 0x7f) & ~0x3f; + } + } + frame_addr = QEMU_ALIGN_UP(sp, 64); + sp = frame_addr + PARISC_RT_SIGFRAME_SIZE32; + + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + tswap_siginfo(&frame->info, info); + frame->uc.tuc_flags = 0; + frame->uc.tuc_link = 0; + + __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, + &frame->uc.tuc_stack.ss_size); + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + setup_sigcontext(&frame->uc.tuc_mcontext, env); + + __put_user(0x34190000, frame->tramp + 0); /* ldi 0,%r25 */ + __put_user(0x3414015a, frame->tramp + 1); /* ldi __NR_rt_sigreturn,%r20 */ + __put_user(0xe4008200, frame->tramp + 2); /* be,l 0x100(%sr2,%r0) */ + __put_user(0x08000240, frame->tramp + 3); /* nop */ + + unlock_user_struct(frame, frame_addr, 1); + + env->gr[2] = h2g(frame->tramp); + env->gr[30] = sp; + env->gr[26] = sig; + env->gr[25] = h2g(&frame->info); + env->gr[24] = h2g(&frame->uc); + + haddr = ka->_sa_handler; + if (haddr & 2) { + /* Function descriptor. */ + target_ulong *fdesc, dest; + + haddr &= -4; + if (!lock_user_struct(VERIFY_READ, fdesc, haddr, 1)) { + goto give_sigsegv; + } + __get_user(dest, fdesc); + __get_user(env->gr[19], fdesc + 1); + unlock_user_struct(fdesc, haddr, 1); + haddr = dest; + } + env->iaoq_f = haddr; + env->iaoq_b = haddr + 4; + return; + + give_sigsegv: + force_sigsegv(sig); +} + +long do_rt_sigreturn(CPUArchState *env) +{ + abi_ulong frame_addr = env->gr[30] - PARISC_RT_SIGFRAME_SIZE32; + struct target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + unlock_user_struct(frame, frame_addr, 0); + + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.tuc_stack), + 0, env->gr[30]) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/hppa/target_signal.h b/linux-user/hppa/target_signal.h index e115890b48..f19aed93dd 100644 --- a/linux-user/hppa/target_signal.h +++ b/linux-user/hppa/target_signal.h @@ -26,4 +26,7 @@ static inline abi_ulong get_sp_from_cpustate(CPUHPPAState *state) return state->gr[30]; } +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUArchState *env); #endif /* HPPA_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index b42fe4ff93..58bbb7693c 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3032,195 +3032,6 @@ sigsegv: return -TARGET_QEMU_ESIGRETURN; } -#elif defined(TARGET_HPPA) - -struct target_sigcontext { - abi_ulong sc_flags; - abi_ulong sc_gr[32]; - uint64_t sc_fr[32]; - abi_ulong sc_iasq[2]; - abi_ulong sc_iaoq[2]; - abi_ulong sc_sar; -}; - -struct target_ucontext { - abi_uint tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - abi_uint pad[1]; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; -}; - -struct target_rt_sigframe { - abi_uint tramp[9]; - target_siginfo_t info; - struct target_ucontext uc; - /* hidden location of upper halves of pa2.0 64-bit gregs */ -}; - -static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env) -{ - int flags = 0; - int i; - - /* ??? if on_sig_stack, flags |= 1 (PARISC_SC_FLAG_ONSTACK). */ - - if (env->iaoq_f < TARGET_PAGE_SIZE) { - /* In the gateway page, executing a syscall. */ - flags |= 2; /* PARISC_SC_FLAG_IN_SYSCALL */ - __put_user(env->gr[31], &sc->sc_iaoq[0]); - __put_user(env->gr[31] + 4, &sc->sc_iaoq[1]); - } else { - __put_user(env->iaoq_f, &sc->sc_iaoq[0]); - __put_user(env->iaoq_b, &sc->sc_iaoq[1]); - } - __put_user(0, &sc->sc_iasq[0]); - __put_user(0, &sc->sc_iasq[1]); - __put_user(flags, &sc->sc_flags); - - __put_user(cpu_hppa_get_psw(env), &sc->sc_gr[0]); - for (i = 1; i < 32; ++i) { - __put_user(env->gr[i], &sc->sc_gr[i]); - } - - __put_user((uint64_t)env->fr0_shadow << 32, &sc->sc_fr[0]); - for (i = 1; i < 32; ++i) { - __put_user(env->fr[i], &sc->sc_fr[i]); - } - - __put_user(env->cr[CR_SAR], &sc->sc_sar); -} - -static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) -{ - target_ulong psw; - int i; - - __get_user(psw, &sc->sc_gr[0]); - cpu_hppa_put_psw(env, psw); - - for (i = 1; i < 32; ++i) { - __get_user(env->gr[i], &sc->sc_gr[i]); - } - for (i = 0; i < 32; ++i) { - __get_user(env->fr[i], &sc->sc_fr[i]); - } - cpu_hppa_loaded_fr0(env); - - __get_user(env->iaoq_f, &sc->sc_iaoq[0]); - __get_user(env->iaoq_b, &sc->sc_iaoq[1]); - __get_user(env->cr[CR_SAR], &sc->sc_sar); -} - -/* No, this doesn't look right, but it's copied straight from the kernel. */ -#define PARISC_RT_SIGFRAME_SIZE32 \ - ((sizeof(struct target_rt_sigframe) + 48 + 64) & -64) - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUArchState *env) -{ - abi_ulong frame_addr, sp, haddr; - struct target_rt_sigframe *frame; - int i; - - sp = env->gr[30]; - if (ka->sa_flags & TARGET_SA_ONSTACK) { - if (sas_ss_flags(sp) == 0) { - sp = (target_sigaltstack_used.ss_sp + 0x7f) & ~0x3f; - } - } - frame_addr = QEMU_ALIGN_UP(sp, 64); - sp = frame_addr + PARISC_RT_SIGFRAME_SIZE32; - - trace_user_setup_rt_frame(env, frame_addr); - - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - tswap_siginfo(&frame->info, info); - frame->uc.tuc_flags = 0; - frame->uc.tuc_link = 0; - - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - setup_sigcontext(&frame->uc.tuc_mcontext, env); - - __put_user(0x34190000, frame->tramp + 0); /* ldi 0,%r25 */ - __put_user(0x3414015a, frame->tramp + 1); /* ldi __NR_rt_sigreturn,%r20 */ - __put_user(0xe4008200, frame->tramp + 2); /* be,l 0x100(%sr2,%r0) */ - __put_user(0x08000240, frame->tramp + 3); /* nop */ - - unlock_user_struct(frame, frame_addr, 1); - - env->gr[2] = h2g(frame->tramp); - env->gr[30] = sp; - env->gr[26] = sig; - env->gr[25] = h2g(&frame->info); - env->gr[24] = h2g(&frame->uc); - - haddr = ka->_sa_handler; - if (haddr & 2) { - /* Function descriptor. */ - target_ulong *fdesc, dest; - - haddr &= -4; - if (!lock_user_struct(VERIFY_READ, fdesc, haddr, 1)) { - goto give_sigsegv; - } - __get_user(dest, fdesc); - __get_user(env->gr[19], fdesc + 1); - unlock_user_struct(fdesc, haddr, 1); - haddr = dest; - } - env->iaoq_f = haddr; - env->iaoq_b = haddr + 4; - return; - - give_sigsegv: - force_sigsegv(sig); -} - -long do_rt_sigreturn(CPUArchState *env) -{ - abi_ulong frame_addr = env->gr[30] - PARISC_RT_SIGFRAME_SIZE32; - struct target_rt_sigframe *frame; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - restore_sigcontext(env, &frame->uc.tuc_mcontext); - unlock_user_struct(frame, frame_addr, 0); - - if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, - uc.tuc_stack), - 0, env->gr[30]) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - - badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - #elif defined(TARGET_XTENSA) struct target_sigcontext { From 3612667cbb58a7bb508c861ec053363b7f5b2241 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:30 +0200 Subject: [PATCH 0140/2380] linux-user: move xtensa signal.c parts to xtensa directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to xtensa/signal.c, except adding includes and exporting setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-16-laurent@vivier.eu> --- linux-user/signal.c | 247 ----------------------------- linux-user/xtensa/signal.c | 250 ++++++++++++++++++++++++++++++ linux-user/xtensa/target_signal.h | 3 + 3 files changed, 253 insertions(+), 247 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 58bbb7693c..b9ad4c14a3 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3031,253 +3031,6 @@ sigsegv: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } - -#elif defined(TARGET_XTENSA) - -struct target_sigcontext { - abi_ulong sc_pc; - abi_ulong sc_ps; - abi_ulong sc_lbeg; - abi_ulong sc_lend; - abi_ulong sc_lcount; - abi_ulong sc_sar; - abi_ulong sc_acclo; - abi_ulong sc_acchi; - abi_ulong sc_a[16]; - abi_ulong sc_xtregs; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; -}; - -struct target_rt_sigframe { - target_siginfo_t info; - struct target_ucontext uc; - /* TODO: xtregs */ - uint8_t retcode[6]; - abi_ulong window[4]; -}; - -static abi_ulong get_sigframe(struct target_sigaction *sa, - CPUXtensaState *env, - unsigned long framesize) -{ - abi_ulong sp = env->regs[1]; - - /* This is the X/Open sanctioned signal stack switching. */ - if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - return (sp - framesize) & -16; -} - -static int flush_window_regs(CPUXtensaState *env) -{ - uint32_t wb = env->sregs[WINDOW_BASE]; - uint32_t ws = xtensa_replicate_windowstart(env) >> (wb + 1); - unsigned d = ctz32(ws) + 1; - unsigned i; - int ret = 0; - - for (i = d; i < env->config->nareg / 4; i += d) { - uint32_t ssp, osp; - unsigned j; - - ws >>= d; - xtensa_rotate_window(env, d); - - if (ws & 0x1) { - ssp = env->regs[5]; - d = 1; - } else if (ws & 0x2) { - ssp = env->regs[9]; - ret |= get_user_ual(osp, env->regs[1] - 12); - osp -= 32; - d = 2; - } else if (ws & 0x4) { - ssp = env->regs[13]; - ret |= get_user_ual(osp, env->regs[1] - 12); - osp -= 48; - d = 3; - } else { - g_assert_not_reached(); - } - - for (j = 0; j < 4; ++j) { - ret |= put_user_ual(env->regs[j], ssp - 16 + j * 4); - } - for (j = 4; j < d * 4; ++j) { - ret |= put_user_ual(env->regs[j], osp - 16 + j * 4); - } - } - xtensa_rotate_window(env, d); - g_assert(env->sregs[WINDOW_BASE] == wb); - return ret == 0; -} - -static int setup_sigcontext(struct target_rt_sigframe *frame, - CPUXtensaState *env) -{ - struct target_sigcontext *sc = &frame->uc.tuc_mcontext; - int i; - - __put_user(env->pc, &sc->sc_pc); - __put_user(env->sregs[PS], &sc->sc_ps); - __put_user(env->sregs[LBEG], &sc->sc_lbeg); - __put_user(env->sregs[LEND], &sc->sc_lend); - __put_user(env->sregs[LCOUNT], &sc->sc_lcount); - if (!flush_window_regs(env)) { - return 0; - } - for (i = 0; i < 16; ++i) { - __put_user(env->regs[i], sc->sc_a + i); - } - __put_user(0, &sc->sc_xtregs); - /* TODO: xtregs */ - return 1; -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUXtensaState *env) -{ - abi_ulong frame_addr; - struct target_rt_sigframe *frame; - uint32_t ra; - int i; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - if (ka->sa_flags & SA_SIGINFO) { - tswap_siginfo(&frame->info, info); - } - - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->regs[1]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - if (!setup_sigcontext(frame, env)) { - unlock_user_struct(frame, frame_addr, 0); - goto give_sigsegv; - } - for (i = 0; i < TARGET_NSIG_WORDS; ++i) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - if (ka->sa_flags & TARGET_SA_RESTORER) { - ra = ka->sa_restorer; - } else { - ra = frame_addr + offsetof(struct target_rt_sigframe, retcode); -#ifdef TARGET_WORDS_BIGENDIAN - /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ - __put_user(0x22, &frame->retcode[0]); - __put_user(0x0a, &frame->retcode[1]); - __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); - /* Generate instruction: SYSCALL */ - __put_user(0x00, &frame->retcode[3]); - __put_user(0x05, &frame->retcode[4]); - __put_user(0x00, &frame->retcode[5]); -#else - /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ - __put_user(0x22, &frame->retcode[0]); - __put_user(0xa0, &frame->retcode[1]); - __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); - /* Generate instruction: SYSCALL */ - __put_user(0x00, &frame->retcode[3]); - __put_user(0x50, &frame->retcode[4]); - __put_user(0x00, &frame->retcode[5]); -#endif - } - env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT); - if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER)) { - env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT); - } - memset(env->regs, 0, sizeof(env->regs)); - env->pc = ka->_sa_handler; - env->regs[1] = frame_addr; - env->sregs[WINDOW_BASE] = 0; - env->sregs[WINDOW_START] = 1; - - env->regs[4] = (ra & 0x3fffffff) | 0x40000000; - env->regs[6] = sig; - env->regs[7] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->regs[8] = frame_addr + offsetof(struct target_rt_sigframe, uc); - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - force_sigsegv(sig); - return; -} - -static void restore_sigcontext(CPUXtensaState *env, - struct target_rt_sigframe *frame) -{ - struct target_sigcontext *sc = &frame->uc.tuc_mcontext; - uint32_t ps; - int i; - - __get_user(env->pc, &sc->sc_pc); - __get_user(ps, &sc->sc_ps); - __get_user(env->sregs[LBEG], &sc->sc_lbeg); - __get_user(env->sregs[LEND], &sc->sc_lend); - __get_user(env->sregs[LCOUNT], &sc->sc_lcount); - - env->sregs[WINDOW_BASE] = 0; - env->sregs[WINDOW_START] = 1; - env->sregs[PS] = deposit32(env->sregs[PS], - PS_CALLINC_SHIFT, - PS_CALLINC_LEN, - extract32(ps, PS_CALLINC_SHIFT, - PS_CALLINC_LEN)); - for (i = 0; i < 16; ++i) { - __get_user(env->regs[i], sc->sc_a + i); - } - /* TODO: xtregs */ -} - -long do_rt_sigreturn(CPUXtensaState *env) -{ - abi_ulong frame_addr = env->regs[1]; - struct target_rt_sigframe *frame; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - restore_sigcontext(env, frame); - - if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, get_sp_from_cpustate(env)) == -TARGET_EFAULT) { - goto badframe; - } - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} #endif static void handle_pending_signal(CPUArchState *cpu_env, int sig, diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c index 02ca338b6c..1e98910c1b 100644 --- a/linux-user/xtensa/signal.c +++ b/linux-user/xtensa/signal.c @@ -16,3 +16,253 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_ulong sc_pc; + abi_ulong sc_ps; + abi_ulong sc_lbeg; + abi_ulong sc_lend; + abi_ulong sc_lcount; + abi_ulong sc_sar; + abi_ulong sc_acclo; + abi_ulong sc_acchi; + abi_ulong sc_a[16]; + abi_ulong sc_xtregs; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_rt_sigframe { + target_siginfo_t info; + struct target_ucontext uc; + /* TODO: xtregs */ + uint8_t retcode[6]; + abi_ulong window[4]; +}; + +static abi_ulong get_sigframe(struct target_sigaction *sa, + CPUXtensaState *env, + unsigned long framesize) +{ + abi_ulong sp = env->regs[1]; + + /* This is the X/Open sanctioned signal stack switching. */ + if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + return (sp - framesize) & -16; +} + +static int flush_window_regs(CPUXtensaState *env) +{ + uint32_t wb = env->sregs[WINDOW_BASE]; + uint32_t ws = xtensa_replicate_windowstart(env) >> (wb + 1); + unsigned d = ctz32(ws) + 1; + unsigned i; + int ret = 0; + + for (i = d; i < env->config->nareg / 4; i += d) { + uint32_t ssp, osp; + unsigned j; + + ws >>= d; + xtensa_rotate_window(env, d); + + if (ws & 0x1) { + ssp = env->regs[5]; + d = 1; + } else if (ws & 0x2) { + ssp = env->regs[9]; + ret |= get_user_ual(osp, env->regs[1] - 12); + osp -= 32; + d = 2; + } else if (ws & 0x4) { + ssp = env->regs[13]; + ret |= get_user_ual(osp, env->regs[1] - 12); + osp -= 48; + d = 3; + } else { + g_assert_not_reached(); + } + + for (j = 0; j < 4; ++j) { + ret |= put_user_ual(env->regs[j], ssp - 16 + j * 4); + } + for (j = 4; j < d * 4; ++j) { + ret |= put_user_ual(env->regs[j], osp - 16 + j * 4); + } + } + xtensa_rotate_window(env, d); + g_assert(env->sregs[WINDOW_BASE] == wb); + return ret == 0; +} + +static int setup_sigcontext(struct target_rt_sigframe *frame, + CPUXtensaState *env) +{ + struct target_sigcontext *sc = &frame->uc.tuc_mcontext; + int i; + + __put_user(env->pc, &sc->sc_pc); + __put_user(env->sregs[PS], &sc->sc_ps); + __put_user(env->sregs[LBEG], &sc->sc_lbeg); + __put_user(env->sregs[LEND], &sc->sc_lend); + __put_user(env->sregs[LCOUNT], &sc->sc_lcount); + if (!flush_window_regs(env)) { + return 0; + } + for (i = 0; i < 16; ++i) { + __put_user(env->regs[i], sc->sc_a + i); + } + __put_user(0, &sc->sc_xtregs); + /* TODO: xtregs */ + return 1; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUXtensaState *env) +{ + abi_ulong frame_addr; + struct target_rt_sigframe *frame; + uint32_t ra; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + if (ka->sa_flags & SA_SIGINFO) { + tswap_siginfo(&frame->info, info); + } + + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, + &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(env->regs[1]), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, + &frame->uc.tuc_stack.ss_size); + if (!setup_sigcontext(frame, env)) { + unlock_user_struct(frame, frame_addr, 0); + goto give_sigsegv; + } + for (i = 0; i < TARGET_NSIG_WORDS; ++i) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + if (ka->sa_flags & TARGET_SA_RESTORER) { + ra = ka->sa_restorer; + } else { + ra = frame_addr + offsetof(struct target_rt_sigframe, retcode); +#ifdef TARGET_WORDS_BIGENDIAN + /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ + __put_user(0x22, &frame->retcode[0]); + __put_user(0x0a, &frame->retcode[1]); + __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); + /* Generate instruction: SYSCALL */ + __put_user(0x00, &frame->retcode[3]); + __put_user(0x05, &frame->retcode[4]); + __put_user(0x00, &frame->retcode[5]); +#else + /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ + __put_user(0x22, &frame->retcode[0]); + __put_user(0xa0, &frame->retcode[1]); + __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); + /* Generate instruction: SYSCALL */ + __put_user(0x00, &frame->retcode[3]); + __put_user(0x50, &frame->retcode[4]); + __put_user(0x00, &frame->retcode[5]); +#endif + } + env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT); + if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER)) { + env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT); + } + memset(env->regs, 0, sizeof(env->regs)); + env->pc = ka->_sa_handler; + env->regs[1] = frame_addr; + env->sregs[WINDOW_BASE] = 0; + env->sregs[WINDOW_START] = 1; + + env->regs[4] = (ra & 0x3fffffff) | 0x40000000; + env->regs[6] = sig; + env->regs[7] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->regs[8] = frame_addr + offsetof(struct target_rt_sigframe, uc); + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); + return; +} + +static void restore_sigcontext(CPUXtensaState *env, + struct target_rt_sigframe *frame) +{ + struct target_sigcontext *sc = &frame->uc.tuc_mcontext; + uint32_t ps; + int i; + + __get_user(env->pc, &sc->sc_pc); + __get_user(ps, &sc->sc_ps); + __get_user(env->sregs[LBEG], &sc->sc_lbeg); + __get_user(env->sregs[LEND], &sc->sc_lend); + __get_user(env->sregs[LCOUNT], &sc->sc_lcount); + + env->sregs[WINDOW_BASE] = 0; + env->sregs[WINDOW_START] = 1; + env->sregs[PS] = deposit32(env->sregs[PS], + PS_CALLINC_SHIFT, + PS_CALLINC_LEN, + extract32(ps, PS_CALLINC_SHIFT, + PS_CALLINC_LEN)); + for (i = 0; i < 16; ++i) { + __get_user(env->regs[i], sc->sc_a + i); + } + /* TODO: xtregs */ +} + +long do_rt_sigreturn(CPUXtensaState *env) +{ + abi_ulong frame_addr = env->regs[1]; + struct target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, frame); + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, get_sp_from_cpustate(env)) == -TARGET_EFAULT) { + goto badframe; + } + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/xtensa/target_signal.h b/linux-user/xtensa/target_signal.h index c6962e70af..f6545903a4 100644 --- a/linux-user/xtensa/target_signal.h +++ b/linux-user/xtensa/target_signal.h @@ -25,4 +25,7 @@ static inline abi_ulong get_sp_from_cpustate(CPUXtensaState *state) return state->regs[1]; } +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUXtensaState *env); #endif From a075f313c57f1d5f06aa344429747f98434f2962 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:31 +0200 Subject: [PATCH 0141/2380] linux-user: move i386/x86_64 signal.c parts to i386 directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to i386/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). x86_64/signal.c includes i386/signal.c Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-17-laurent@vivier.eu> --- linux-user/i386/signal.c | 584 ++++++++++++++++++++++++++++++ linux-user/i386/target_signal.h | 5 + linux-user/signal.c | 582 +---------------------------- linux-user/x86_64/signal.c | 2 + linux-user/x86_64/target_signal.h | 3 + 5 files changed, 595 insertions(+), 581 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 02ca338b6c..4a190e6435 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -16,3 +16,587 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +/* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */ + +struct target_fpreg { + uint16_t significand[4]; + uint16_t exponent; +}; + +struct target_fpxreg { + uint16_t significand[4]; + uint16_t exponent; + uint16_t padding[3]; +}; + +struct target_xmmreg { + uint32_t element[4]; +}; + +struct target_fpstate_32 { + /* Regular FPU environment */ + uint32_t cw; + uint32_t sw; + uint32_t tag; + uint32_t ipoff; + uint32_t cssel; + uint32_t dataoff; + uint32_t datasel; + struct target_fpreg st[8]; + uint16_t status; + uint16_t magic; /* 0xffff = regular FPU data only */ + + /* FXSR FPU environment */ + uint32_t _fxsr_env[6]; /* FXSR FPU env is ignored */ + uint32_t mxcsr; + uint32_t reserved; + struct target_fpxreg fxsr_st[8]; /* FXSR FPU reg data is ignored */ + struct target_xmmreg xmm[8]; + uint32_t padding[56]; +}; + +struct target_fpstate_64 { + /* FXSAVE format */ + uint16_t cw; + uint16_t sw; + uint16_t twd; + uint16_t fop; + uint64_t rip; + uint64_t rdp; + uint32_t mxcsr; + uint32_t mxcsr_mask; + uint32_t st_space[32]; + uint32_t xmm_space[64]; + uint32_t reserved[24]; +}; + +#ifndef TARGET_X86_64 +# define target_fpstate target_fpstate_32 +#else +# define target_fpstate target_fpstate_64 +#endif + +struct target_sigcontext_32 { + uint16_t gs, __gsh; + uint16_t fs, __fsh; + uint16_t es, __esh; + uint16_t ds, __dsh; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t trapno; + uint32_t err; + uint32_t eip; + uint16_t cs, __csh; + uint32_t eflags; + uint32_t esp_at_signal; + uint16_t ss, __ssh; + uint32_t fpstate; /* pointer */ + uint32_t oldmask; + uint32_t cr2; +}; + +struct target_sigcontext_64 { + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rbx; + uint64_t rdx; + uint64_t rax; + uint64_t rcx; + uint64_t rsp; + uint64_t rip; + + uint64_t eflags; + + uint16_t cs; + uint16_t gs; + uint16_t fs; + uint16_t ss; + + uint64_t err; + uint64_t trapno; + uint64_t oldmask; + uint64_t cr2; + + uint64_t fpstate; /* pointer */ + uint64_t padding[8]; +}; + +#ifndef TARGET_X86_64 +# define target_sigcontext target_sigcontext_32 +#else +# define target_sigcontext target_sigcontext_64 +#endif + +/* see Linux/include/uapi/asm-generic/ucontext.h */ +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +#ifndef TARGET_X86_64 +struct sigframe { + abi_ulong pretcode; + int sig; + struct target_sigcontext sc; + struct target_fpstate fpstate; + abi_ulong extramask[TARGET_NSIG_WORDS-1]; + char retcode[8]; +}; + +struct rt_sigframe { + abi_ulong pretcode; + int sig; + abi_ulong pinfo; + abi_ulong puc; + struct target_siginfo info; + struct target_ucontext uc; + struct target_fpstate fpstate; + char retcode[8]; +}; + +#else + +struct rt_sigframe { + abi_ulong pretcode; + struct target_ucontext uc; + struct target_siginfo info; + struct target_fpstate fpstate; +}; + +#endif + +/* + * Set up a signal frame. + */ + +/* XXX: save x87 state */ +static void setup_sigcontext(struct target_sigcontext *sc, + struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask, + abi_ulong fpstate_addr) +{ + CPUState *cs = CPU(x86_env_get_cpu(env)); +#ifndef TARGET_X86_64 + uint16_t magic; + + /* already locked in setup_frame() */ + __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs); + __put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs); + __put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es); + __put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds); + __put_user(env->regs[R_EDI], &sc->edi); + __put_user(env->regs[R_ESI], &sc->esi); + __put_user(env->regs[R_EBP], &sc->ebp); + __put_user(env->regs[R_ESP], &sc->esp); + __put_user(env->regs[R_EBX], &sc->ebx); + __put_user(env->regs[R_EDX], &sc->edx); + __put_user(env->regs[R_ECX], &sc->ecx); + __put_user(env->regs[R_EAX], &sc->eax); + __put_user(cs->exception_index, &sc->trapno); + __put_user(env->error_code, &sc->err); + __put_user(env->eip, &sc->eip); + __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs); + __put_user(env->eflags, &sc->eflags); + __put_user(env->regs[R_ESP], &sc->esp_at_signal); + __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss); + + cpu_x86_fsave(env, fpstate_addr, 1); + fpstate->status = fpstate->sw; + magic = 0xffff; + __put_user(magic, &fpstate->magic); + __put_user(fpstate_addr, &sc->fpstate); + + /* non-iBCS2 extensions.. */ + __put_user(mask, &sc->oldmask); + __put_user(env->cr[2], &sc->cr2); +#else + __put_user(env->regs[R_EDI], &sc->rdi); + __put_user(env->regs[R_ESI], &sc->rsi); + __put_user(env->regs[R_EBP], &sc->rbp); + __put_user(env->regs[R_ESP], &sc->rsp); + __put_user(env->regs[R_EBX], &sc->rbx); + __put_user(env->regs[R_EDX], &sc->rdx); + __put_user(env->regs[R_ECX], &sc->rcx); + __put_user(env->regs[R_EAX], &sc->rax); + + __put_user(env->regs[8], &sc->r8); + __put_user(env->regs[9], &sc->r9); + __put_user(env->regs[10], &sc->r10); + __put_user(env->regs[11], &sc->r11); + __put_user(env->regs[12], &sc->r12); + __put_user(env->regs[13], &sc->r13); + __put_user(env->regs[14], &sc->r14); + __put_user(env->regs[15], &sc->r15); + + __put_user(cs->exception_index, &sc->trapno); + __put_user(env->error_code, &sc->err); + __put_user(env->eip, &sc->rip); + + __put_user(env->eflags, &sc->eflags); + __put_user(env->segs[R_CS].selector, &sc->cs); + __put_user((uint16_t)0, &sc->gs); + __put_user((uint16_t)0, &sc->fs); + __put_user(env->segs[R_SS].selector, &sc->ss); + + __put_user(mask, &sc->oldmask); + __put_user(env->cr[2], &sc->cr2); + + /* fpstate_addr must be 16 byte aligned for fxsave */ + assert(!(fpstate_addr & 0xf)); + + cpu_x86_fxsave(env, fpstate_addr); + __put_user(fpstate_addr, &sc->fpstate); +#endif +} + +/* + * Determine which stack to use.. + */ + +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size) +{ + unsigned long esp; + + /* Default to using normal stack */ + esp = env->regs[R_ESP]; +#ifdef TARGET_X86_64 + esp -= 128; /* this is the redzone */ +#endif + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa_flags & TARGET_SA_ONSTACK) { + if (sas_ss_flags(esp) == 0) { + esp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + } else { +#ifndef TARGET_X86_64 + /* This is the legacy signal stack switching. */ + if ((env->segs[R_SS].selector & 0xffff) != __USER_DS && + !(ka->sa_flags & TARGET_SA_RESTORER) && + ka->sa_restorer) { + esp = (unsigned long) ka->sa_restorer; + } +#endif + } + +#ifndef TARGET_X86_64 + return (esp - frame_size) & -8ul; +#else + return ((esp - frame_size) & (~15ul)) - 8; +#endif +} + +#ifndef TARGET_X86_64 +/* compare linux/arch/i386/kernel/signal.c:setup_frame() */ +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUX86State *env) +{ + abi_ulong frame_addr; + struct sigframe *frame; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto give_sigsegv; + + __put_user(sig, &frame->sig); + + setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0], + frame_addr + offsetof(struct sigframe, fpstate)); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->extramask[i - 1]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + __put_user(ka->sa_restorer, &frame->pretcode); + } else { + uint16_t val16; + abi_ulong retcode_addr; + retcode_addr = frame_addr + offsetof(struct sigframe, retcode); + __put_user(retcode_addr, &frame->pretcode); + /* This is popl %eax ; movl $,%eax ; int $0x80 */ + val16 = 0xb858; + __put_user(val16, (uint16_t *)(frame->retcode+0)); + __put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2)); + val16 = 0x80cd; + __put_user(val16, (uint16_t *)(frame->retcode+6)); + } + + /* Set up registers for signal handler */ + env->regs[R_ESP] = frame_addr; + env->eip = ka->_sa_handler; + + cpu_x86_load_seg(env, R_DS, __USER_DS); + cpu_x86_load_seg(env, R_ES, __USER_DS); + cpu_x86_load_seg(env, R_SS, __USER_DS); + cpu_x86_load_seg(env, R_CS, __USER_CS); + env->eflags &= ~TF_MASK; + + unlock_user_struct(frame, frame_addr, 1); + + return; + +give_sigsegv: + force_sigsegv(sig); +} +#endif + +/* compare linux/arch/x86/kernel/signal.c:setup_rt_frame() */ +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUX86State *env) +{ + abi_ulong frame_addr; +#ifndef TARGET_X86_64 + abi_ulong addr; +#endif + struct rt_sigframe *frame; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto give_sigsegv; + + /* These fields are only in rt_sigframe on 32 bit */ +#ifndef TARGET_X86_64 + __put_user(sig, &frame->sig); + addr = frame_addr + offsetof(struct rt_sigframe, info); + __put_user(addr, &frame->pinfo); + addr = frame_addr + offsetof(struct rt_sigframe, uc); + __put_user(addr, &frame->puc); +#endif + if (ka->sa_flags & TARGET_SA_SIGINFO) { + tswap_siginfo(&frame->info, info); + } + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, + &frame->uc.tuc_stack.ss_size); + setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env, + set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate)); + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ +#ifndef TARGET_X86_64 + if (ka->sa_flags & TARGET_SA_RESTORER) { + __put_user(ka->sa_restorer, &frame->pretcode); + } else { + uint16_t val16; + addr = frame_addr + offsetof(struct rt_sigframe, retcode); + __put_user(addr, &frame->pretcode); + /* This is movl $,%eax ; int $0x80 */ + __put_user(0xb8, (char *)(frame->retcode+0)); + __put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1)); + val16 = 0x80cd; + __put_user(val16, (uint16_t *)(frame->retcode+5)); + } +#else + /* XXX: Would be slightly better to return -EFAULT here if test fails + assert(ka->sa_flags & TARGET_SA_RESTORER); */ + __put_user(ka->sa_restorer, &frame->pretcode); +#endif + + /* Set up registers for signal handler */ + env->regs[R_ESP] = frame_addr; + env->eip = ka->_sa_handler; + +#ifndef TARGET_X86_64 + env->regs[R_EAX] = sig; + env->regs[R_EDX] = (unsigned long)&frame->info; + env->regs[R_ECX] = (unsigned long)&frame->uc; +#else + env->regs[R_EAX] = 0; + env->regs[R_EDI] = sig; + env->regs[R_ESI] = (unsigned long)&frame->info; + env->regs[R_EDX] = (unsigned long)&frame->uc; +#endif + + cpu_x86_load_seg(env, R_DS, __USER_DS); + cpu_x86_load_seg(env, R_ES, __USER_DS); + cpu_x86_load_seg(env, R_CS, __USER_CS); + cpu_x86_load_seg(env, R_SS, __USER_DS); + env->eflags &= ~TF_MASK; + + unlock_user_struct(frame, frame_addr, 1); + + return; + +give_sigsegv: + force_sigsegv(sig); +} + +static int +restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) +{ + unsigned int err = 0; + abi_ulong fpstate_addr; + unsigned int tmpflags; + +#ifndef TARGET_X86_64 + cpu_x86_load_seg(env, R_GS, tswap16(sc->gs)); + cpu_x86_load_seg(env, R_FS, tswap16(sc->fs)); + cpu_x86_load_seg(env, R_ES, tswap16(sc->es)); + cpu_x86_load_seg(env, R_DS, tswap16(sc->ds)); + + env->regs[R_EDI] = tswapl(sc->edi); + env->regs[R_ESI] = tswapl(sc->esi); + env->regs[R_EBP] = tswapl(sc->ebp); + env->regs[R_ESP] = tswapl(sc->esp); + env->regs[R_EBX] = tswapl(sc->ebx); + env->regs[R_EDX] = tswapl(sc->edx); + env->regs[R_ECX] = tswapl(sc->ecx); + env->regs[R_EAX] = tswapl(sc->eax); + + env->eip = tswapl(sc->eip); +#else + env->regs[8] = tswapl(sc->r8); + env->regs[9] = tswapl(sc->r9); + env->regs[10] = tswapl(sc->r10); + env->regs[11] = tswapl(sc->r11); + env->regs[12] = tswapl(sc->r12); + env->regs[13] = tswapl(sc->r13); + env->regs[14] = tswapl(sc->r14); + env->regs[15] = tswapl(sc->r15); + + env->regs[R_EDI] = tswapl(sc->rdi); + env->regs[R_ESI] = tswapl(sc->rsi); + env->regs[R_EBP] = tswapl(sc->rbp); + env->regs[R_EBX] = tswapl(sc->rbx); + env->regs[R_EDX] = tswapl(sc->rdx); + env->regs[R_EAX] = tswapl(sc->rax); + env->regs[R_ECX] = tswapl(sc->rcx); + env->regs[R_ESP] = tswapl(sc->rsp); + + env->eip = tswapl(sc->rip); +#endif + + cpu_x86_load_seg(env, R_CS, lduw_p(&sc->cs) | 3); + cpu_x86_load_seg(env, R_SS, lduw_p(&sc->ss) | 3); + + tmpflags = tswapl(sc->eflags); + env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); + // regs->orig_eax = -1; /* disable syscall checks */ + + fpstate_addr = tswapl(sc->fpstate); + if (fpstate_addr != 0) { + if (!access_ok(VERIFY_READ, fpstate_addr, + sizeof(struct target_fpstate))) + goto badframe; +#ifndef TARGET_X86_64 + cpu_x86_frstor(env, fpstate_addr, 1); +#else + cpu_x86_fxrstor(env, fpstate_addr); +#endif + } + + return err; +badframe: + return 1; +} + +/* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */ +#ifndef TARGET_X86_64 +long do_sigreturn(CPUX86State *env) +{ + struct sigframe *frame; + abi_ulong frame_addr = env->regs[R_ESP] - 8; + target_sigset_t target_set; + sigset_t set; + int i; + + trace_user_do_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + /* set blocked signals */ + __get_user(target_set.sig[0], &frame->sc.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } + + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + /* restore registers */ + if (restore_sigcontext(env, &frame->sc)) + goto badframe; + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} +#endif + +long do_rt_sigreturn(CPUX86State *env) +{ + abi_ulong frame_addr; + struct rt_sigframe *frame; + sigset_t set; + + frame_addr = env->regs[R_ESP] - sizeof(abi_ulong); + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { + goto badframe; + } + + if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe, uc.tuc_stack), 0, + get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/i386/target_signal.h b/linux-user/i386/target_signal.h index 837e90fc4c..ec5a3e3373 100644 --- a/linux-user/i386/target_signal.h +++ b/linux-user/i386/target_signal.h @@ -26,4 +26,9 @@ static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) return state->regs[R_ESP]; } +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUX86State *env); +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUX86State *env); #endif /* I386_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index b9ad4c14a3..830f778262 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -803,587 +803,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, return ret; } -#if defined(TARGET_I386) -/* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */ - -struct target_fpreg { - uint16_t significand[4]; - uint16_t exponent; -}; - -struct target_fpxreg { - uint16_t significand[4]; - uint16_t exponent; - uint16_t padding[3]; -}; - -struct target_xmmreg { - uint32_t element[4]; -}; - -struct target_fpstate_32 { - /* Regular FPU environment */ - uint32_t cw; - uint32_t sw; - uint32_t tag; - uint32_t ipoff; - uint32_t cssel; - uint32_t dataoff; - uint32_t datasel; - struct target_fpreg st[8]; - uint16_t status; - uint16_t magic; /* 0xffff = regular FPU data only */ - - /* FXSR FPU environment */ - uint32_t _fxsr_env[6]; /* FXSR FPU env is ignored */ - uint32_t mxcsr; - uint32_t reserved; - struct target_fpxreg fxsr_st[8]; /* FXSR FPU reg data is ignored */ - struct target_xmmreg xmm[8]; - uint32_t padding[56]; -}; - -struct target_fpstate_64 { - /* FXSAVE format */ - uint16_t cw; - uint16_t sw; - uint16_t twd; - uint16_t fop; - uint64_t rip; - uint64_t rdp; - uint32_t mxcsr; - uint32_t mxcsr_mask; - uint32_t st_space[32]; - uint32_t xmm_space[64]; - uint32_t reserved[24]; -}; - -#ifndef TARGET_X86_64 -# define target_fpstate target_fpstate_32 -#else -# define target_fpstate target_fpstate_64 -#endif - -struct target_sigcontext_32 { - uint16_t gs, __gsh; - uint16_t fs, __fsh; - uint16_t es, __esh; - uint16_t ds, __dsh; - uint32_t edi; - uint32_t esi; - uint32_t ebp; - uint32_t esp; - uint32_t ebx; - uint32_t edx; - uint32_t ecx; - uint32_t eax; - uint32_t trapno; - uint32_t err; - uint32_t eip; - uint16_t cs, __csh; - uint32_t eflags; - uint32_t esp_at_signal; - uint16_t ss, __ssh; - uint32_t fpstate; /* pointer */ - uint32_t oldmask; - uint32_t cr2; -}; - -struct target_sigcontext_64 { - uint64_t r8; - uint64_t r9; - uint64_t r10; - uint64_t r11; - uint64_t r12; - uint64_t r13; - uint64_t r14; - uint64_t r15; - - uint64_t rdi; - uint64_t rsi; - uint64_t rbp; - uint64_t rbx; - uint64_t rdx; - uint64_t rax; - uint64_t rcx; - uint64_t rsp; - uint64_t rip; - - uint64_t eflags; - - uint16_t cs; - uint16_t gs; - uint16_t fs; - uint16_t ss; - - uint64_t err; - uint64_t trapno; - uint64_t oldmask; - uint64_t cr2; - - uint64_t fpstate; /* pointer */ - uint64_t padding[8]; -}; - -#ifndef TARGET_X86_64 -# define target_sigcontext target_sigcontext_32 -#else -# define target_sigcontext target_sigcontext_64 -#endif - -/* see Linux/include/uapi/asm-generic/ucontext.h */ -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -#ifndef TARGET_X86_64 -struct sigframe { - abi_ulong pretcode; - int sig; - struct target_sigcontext sc; - struct target_fpstate fpstate; - abi_ulong extramask[TARGET_NSIG_WORDS-1]; - char retcode[8]; -}; - -struct rt_sigframe { - abi_ulong pretcode; - int sig; - abi_ulong pinfo; - abi_ulong puc; - struct target_siginfo info; - struct target_ucontext uc; - struct target_fpstate fpstate; - char retcode[8]; -}; - -#else - -struct rt_sigframe { - abi_ulong pretcode; - struct target_ucontext uc; - struct target_siginfo info; - struct target_fpstate fpstate; -}; - -#endif - -/* - * Set up a signal frame. - */ - -/* XXX: save x87 state */ -static void setup_sigcontext(struct target_sigcontext *sc, - struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask, - abi_ulong fpstate_addr) -{ - CPUState *cs = CPU(x86_env_get_cpu(env)); -#ifndef TARGET_X86_64 - uint16_t magic; - - /* already locked in setup_frame() */ - __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs); - __put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs); - __put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es); - __put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds); - __put_user(env->regs[R_EDI], &sc->edi); - __put_user(env->regs[R_ESI], &sc->esi); - __put_user(env->regs[R_EBP], &sc->ebp); - __put_user(env->regs[R_ESP], &sc->esp); - __put_user(env->regs[R_EBX], &sc->ebx); - __put_user(env->regs[R_EDX], &sc->edx); - __put_user(env->regs[R_ECX], &sc->ecx); - __put_user(env->regs[R_EAX], &sc->eax); - __put_user(cs->exception_index, &sc->trapno); - __put_user(env->error_code, &sc->err); - __put_user(env->eip, &sc->eip); - __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs); - __put_user(env->eflags, &sc->eflags); - __put_user(env->regs[R_ESP], &sc->esp_at_signal); - __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss); - - cpu_x86_fsave(env, fpstate_addr, 1); - fpstate->status = fpstate->sw; - magic = 0xffff; - __put_user(magic, &fpstate->magic); - __put_user(fpstate_addr, &sc->fpstate); - - /* non-iBCS2 extensions.. */ - __put_user(mask, &sc->oldmask); - __put_user(env->cr[2], &sc->cr2); -#else - __put_user(env->regs[R_EDI], &sc->rdi); - __put_user(env->regs[R_ESI], &sc->rsi); - __put_user(env->regs[R_EBP], &sc->rbp); - __put_user(env->regs[R_ESP], &sc->rsp); - __put_user(env->regs[R_EBX], &sc->rbx); - __put_user(env->regs[R_EDX], &sc->rdx); - __put_user(env->regs[R_ECX], &sc->rcx); - __put_user(env->regs[R_EAX], &sc->rax); - - __put_user(env->regs[8], &sc->r8); - __put_user(env->regs[9], &sc->r9); - __put_user(env->regs[10], &sc->r10); - __put_user(env->regs[11], &sc->r11); - __put_user(env->regs[12], &sc->r12); - __put_user(env->regs[13], &sc->r13); - __put_user(env->regs[14], &sc->r14); - __put_user(env->regs[15], &sc->r15); - - __put_user(cs->exception_index, &sc->trapno); - __put_user(env->error_code, &sc->err); - __put_user(env->eip, &sc->rip); - - __put_user(env->eflags, &sc->eflags); - __put_user(env->segs[R_CS].selector, &sc->cs); - __put_user((uint16_t)0, &sc->gs); - __put_user((uint16_t)0, &sc->fs); - __put_user(env->segs[R_SS].selector, &sc->ss); - - __put_user(mask, &sc->oldmask); - __put_user(env->cr[2], &sc->cr2); - - /* fpstate_addr must be 16 byte aligned for fxsave */ - assert(!(fpstate_addr & 0xf)); - - cpu_x86_fxsave(env, fpstate_addr); - __put_user(fpstate_addr, &sc->fpstate); -#endif -} - -/* - * Determine which stack to use.. - */ - -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size) -{ - unsigned long esp; - - /* Default to using normal stack */ - esp = env->regs[R_ESP]; -#ifdef TARGET_X86_64 - esp -= 128; /* this is the redzone */ -#endif - - /* This is the X/Open sanctioned signal stack switching. */ - if (ka->sa_flags & TARGET_SA_ONSTACK) { - if (sas_ss_flags(esp) == 0) { - esp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - } else { -#ifndef TARGET_X86_64 - /* This is the legacy signal stack switching. */ - if ((env->segs[R_SS].selector & 0xffff) != __USER_DS && - !(ka->sa_flags & TARGET_SA_RESTORER) && - ka->sa_restorer) { - esp = (unsigned long) ka->sa_restorer; - } -#endif - } - -#ifndef TARGET_X86_64 - return (esp - frame_size) & -8ul; -#else - return ((esp - frame_size) & (~15ul)) - 8; -#endif -} - -#ifndef TARGET_X86_64 -/* compare linux/arch/i386/kernel/signal.c:setup_frame() */ -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUX86State *env) -{ - abi_ulong frame_addr; - struct sigframe *frame; - int i; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_frame(env, frame_addr); - - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto give_sigsegv; - - __put_user(sig, &frame->sig); - - setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0], - frame_addr + offsetof(struct sigframe, fpstate)); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - __put_user(ka->sa_restorer, &frame->pretcode); - } else { - uint16_t val16; - abi_ulong retcode_addr; - retcode_addr = frame_addr + offsetof(struct sigframe, retcode); - __put_user(retcode_addr, &frame->pretcode); - /* This is popl %eax ; movl $,%eax ; int $0x80 */ - val16 = 0xb858; - __put_user(val16, (uint16_t *)(frame->retcode+0)); - __put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2)); - val16 = 0x80cd; - __put_user(val16, (uint16_t *)(frame->retcode+6)); - } - - /* Set up registers for signal handler */ - env->regs[R_ESP] = frame_addr; - env->eip = ka->_sa_handler; - - cpu_x86_load_seg(env, R_DS, __USER_DS); - cpu_x86_load_seg(env, R_ES, __USER_DS); - cpu_x86_load_seg(env, R_SS, __USER_DS); - cpu_x86_load_seg(env, R_CS, __USER_CS); - env->eflags &= ~TF_MASK; - - unlock_user_struct(frame, frame_addr, 1); - - return; - -give_sigsegv: - force_sigsegv(sig); -} -#endif - -/* compare linux/arch/x86/kernel/signal.c:setup_rt_frame() */ -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUX86State *env) -{ - abi_ulong frame_addr; -#ifndef TARGET_X86_64 - abi_ulong addr; -#endif - struct rt_sigframe *frame; - int i; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto give_sigsegv; - - /* These fields are only in rt_sigframe on 32 bit */ -#ifndef TARGET_X86_64 - __put_user(sig, &frame->sig); - addr = frame_addr + offsetof(struct rt_sigframe, info); - __put_user(addr, &frame->pinfo); - addr = frame_addr + offsetof(struct rt_sigframe, uc); - __put_user(addr, &frame->puc); -#endif - if (ka->sa_flags & TARGET_SA_SIGINFO) { - tswap_siginfo(&frame->info, info); - } - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env, - set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate)); - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ -#ifndef TARGET_X86_64 - if (ka->sa_flags & TARGET_SA_RESTORER) { - __put_user(ka->sa_restorer, &frame->pretcode); - } else { - uint16_t val16; - addr = frame_addr + offsetof(struct rt_sigframe, retcode); - __put_user(addr, &frame->pretcode); - /* This is movl $,%eax ; int $0x80 */ - __put_user(0xb8, (char *)(frame->retcode+0)); - __put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1)); - val16 = 0x80cd; - __put_user(val16, (uint16_t *)(frame->retcode+5)); - } -#else - /* XXX: Would be slightly better to return -EFAULT here if test fails - assert(ka->sa_flags & TARGET_SA_RESTORER); */ - __put_user(ka->sa_restorer, &frame->pretcode); -#endif - - /* Set up registers for signal handler */ - env->regs[R_ESP] = frame_addr; - env->eip = ka->_sa_handler; - -#ifndef TARGET_X86_64 - env->regs[R_EAX] = sig; - env->regs[R_EDX] = (unsigned long)&frame->info; - env->regs[R_ECX] = (unsigned long)&frame->uc; -#else - env->regs[R_EAX] = 0; - env->regs[R_EDI] = sig; - env->regs[R_ESI] = (unsigned long)&frame->info; - env->regs[R_EDX] = (unsigned long)&frame->uc; -#endif - - cpu_x86_load_seg(env, R_DS, __USER_DS); - cpu_x86_load_seg(env, R_ES, __USER_DS); - cpu_x86_load_seg(env, R_CS, __USER_CS); - cpu_x86_load_seg(env, R_SS, __USER_DS); - env->eflags &= ~TF_MASK; - - unlock_user_struct(frame, frame_addr, 1); - - return; - -give_sigsegv: - force_sigsegv(sig); -} - -static int -restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) -{ - unsigned int err = 0; - abi_ulong fpstate_addr; - unsigned int tmpflags; - -#ifndef TARGET_X86_64 - cpu_x86_load_seg(env, R_GS, tswap16(sc->gs)); - cpu_x86_load_seg(env, R_FS, tswap16(sc->fs)); - cpu_x86_load_seg(env, R_ES, tswap16(sc->es)); - cpu_x86_load_seg(env, R_DS, tswap16(sc->ds)); - - env->regs[R_EDI] = tswapl(sc->edi); - env->regs[R_ESI] = tswapl(sc->esi); - env->regs[R_EBP] = tswapl(sc->ebp); - env->regs[R_ESP] = tswapl(sc->esp); - env->regs[R_EBX] = tswapl(sc->ebx); - env->regs[R_EDX] = tswapl(sc->edx); - env->regs[R_ECX] = tswapl(sc->ecx); - env->regs[R_EAX] = tswapl(sc->eax); - - env->eip = tswapl(sc->eip); -#else - env->regs[8] = tswapl(sc->r8); - env->regs[9] = tswapl(sc->r9); - env->regs[10] = tswapl(sc->r10); - env->regs[11] = tswapl(sc->r11); - env->regs[12] = tswapl(sc->r12); - env->regs[13] = tswapl(sc->r13); - env->regs[14] = tswapl(sc->r14); - env->regs[15] = tswapl(sc->r15); - - env->regs[R_EDI] = tswapl(sc->rdi); - env->regs[R_ESI] = tswapl(sc->rsi); - env->regs[R_EBP] = tswapl(sc->rbp); - env->regs[R_EBX] = tswapl(sc->rbx); - env->regs[R_EDX] = tswapl(sc->rdx); - env->regs[R_EAX] = tswapl(sc->rax); - env->regs[R_ECX] = tswapl(sc->rcx); - env->regs[R_ESP] = tswapl(sc->rsp); - - env->eip = tswapl(sc->rip); -#endif - - cpu_x86_load_seg(env, R_CS, lduw_p(&sc->cs) | 3); - cpu_x86_load_seg(env, R_SS, lduw_p(&sc->ss) | 3); - - tmpflags = tswapl(sc->eflags); - env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); - // regs->orig_eax = -1; /* disable syscall checks */ - - fpstate_addr = tswapl(sc->fpstate); - if (fpstate_addr != 0) { - if (!access_ok(VERIFY_READ, fpstate_addr, - sizeof(struct target_fpstate))) - goto badframe; -#ifndef TARGET_X86_64 - cpu_x86_frstor(env, fpstate_addr, 1); -#else - cpu_x86_fxrstor(env, fpstate_addr); -#endif - } - - return err; -badframe: - return 1; -} - -/* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */ -#ifndef TARGET_X86_64 -long do_sigreturn(CPUX86State *env) -{ - struct sigframe *frame; - abi_ulong frame_addr = env->regs[R_ESP] - 8; - target_sigset_t target_set; - sigset_t set; - int i; - - trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - /* set blocked signals */ - __get_user(target_set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - /* restore registers */ - if (restore_sigcontext(env, &frame->sc)) - goto badframe; - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} -#endif - -long do_rt_sigreturn(CPUX86State *env) -{ - abi_ulong frame_addr; - struct rt_sigframe *frame; - sigset_t set; - - frame_addr = env->regs[R_ESP] - sizeof(abi_ulong); - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { - goto badframe; - } - - if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe, uc.tuc_stack), 0, - get_sp_from_cpustate(env)) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#elif defined(TARGET_SPARC) +#if defined(TARGET_SPARC) #define __SUNOS_MAXWIN 31 diff --git a/linux-user/x86_64/signal.c b/linux-user/x86_64/signal.c index 02ca338b6c..a509a38045 100644 --- a/linux-user/x86_64/signal.c +++ b/linux-user/x86_64/signal.c @@ -16,3 +16,5 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#define I386_TARGET_SIGNAL_H /* to only include x86_64/target_signal.h */ +#include "../i386/signal.c" diff --git a/linux-user/x86_64/target_signal.h b/linux-user/x86_64/target_signal.h index 1e95f4a684..26f96944e5 100644 --- a/linux-user/x86_64/target_signal.h +++ b/linux-user/x86_64/target_signal.h @@ -26,4 +26,7 @@ static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) return state->regs[R_ESP]; } +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUX86State *env); #endif /* X86_64_TARGET_SIGNAL_H */ From 9f172adb35123a093aec8feb74de0e126ae2138e Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:32 +0200 Subject: [PATCH 0142/2380] linux-user: move sparc/sparc64 signal.c parts to sparc directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to sparc/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). sparc64/signal.c includes sparc/signal.c Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-18-laurent@vivier.eu> --- linux-user/signal.c | 604 +--------------------------- linux-user/sparc/signal.c | 606 +++++++++++++++++++++++++++++ linux-user/sparc/target_signal.h | 6 +- linux-user/sparc64/signal.c | 2 + linux-user/sparc64/target_signal.h | 6 +- 5 files changed, 619 insertions(+), 605 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 830f778262..27c3769c5e 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -803,609 +803,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, return ret; } -#if defined(TARGET_SPARC) - -#define __SUNOS_MAXWIN 31 - -/* This is what SunOS does, so shall I. */ -struct target_sigcontext { - abi_ulong sigc_onstack; /* state to restore */ - - abi_ulong sigc_mask; /* sigmask to restore */ - abi_ulong sigc_sp; /* stack pointer */ - abi_ulong sigc_pc; /* program counter */ - abi_ulong sigc_npc; /* next program counter */ - abi_ulong sigc_psr; /* for condition codes etc */ - abi_ulong sigc_g1; /* User uses these two registers */ - abi_ulong sigc_o0; /* within the trampoline code. */ - - /* Now comes information regarding the users window set - * at the time of the signal. - */ - abi_ulong sigc_oswins; /* outstanding windows */ - - /* stack ptrs for each regwin buf */ - char *sigc_spbuf[__SUNOS_MAXWIN]; - - /* Windows to restore after signal */ - struct { - abi_ulong locals[8]; - abi_ulong ins[8]; - } sigc_wbuf[__SUNOS_MAXWIN]; -}; -/* A Sparc stack frame */ -struct sparc_stackf { - abi_ulong locals[8]; - abi_ulong ins[8]; - /* It's simpler to treat fp and callers_pc as elements of ins[] - * since we never need to access them ourselves. - */ - char *structptr; - abi_ulong xargs[6]; - abi_ulong xxargs[1]; -}; - -typedef struct { - struct { - abi_ulong psr; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong u_regs[16]; /* globals and ins */ - } si_regs; - int si_mask; -} __siginfo_t; - -typedef struct { - abi_ulong si_float_regs[32]; - unsigned long si_fsr; - unsigned long si_fpqdepth; - struct { - unsigned long *insn_addr; - unsigned long insn; - } si_fpqueue [16]; -} qemu_siginfo_fpu_t; - - -struct target_signal_frame { - struct sparc_stackf ss; - __siginfo_t info; - abi_ulong fpu_save; - abi_ulong insns[2] __attribute__ ((aligned (8))); - abi_ulong extramask[TARGET_NSIG_WORDS - 1]; - abi_ulong extra_size; /* Should be 0 */ - qemu_siginfo_fpu_t fpu_state; -}; -struct target_rt_signal_frame { - struct sparc_stackf ss; - siginfo_t info; - abi_ulong regs[20]; - sigset_t mask; - abi_ulong fpu_save; - unsigned int insns[2]; - stack_t stack; - unsigned int extra_size; /* Should be 0 */ - qemu_siginfo_fpu_t fpu_state; -}; - -#define UREG_O0 16 -#define UREG_O6 22 -#define UREG_I0 0 -#define UREG_I1 1 -#define UREG_I2 2 -#define UREG_I3 3 -#define UREG_I4 4 -#define UREG_I5 5 -#define UREG_I6 6 -#define UREG_I7 7 -#define UREG_L0 8 -#define UREG_FP UREG_I6 -#define UREG_SP UREG_O6 - -static inline abi_ulong get_sigframe(struct target_sigaction *sa, - CPUSPARCState *env, - unsigned long framesize) -{ - abi_ulong sp; - - sp = env->regwptr[UREG_FP]; - - /* This is the X/Open sanctioned signal stack switching. */ - if (sa->sa_flags & TARGET_SA_ONSTACK) { - if (!on_sig_stack(sp) - && !((target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size) & 7)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - } - return sp - framesize; -} - -static int -setup___siginfo(__siginfo_t *si, CPUSPARCState *env, abi_ulong mask) -{ - int err = 0, i; - - __put_user(env->psr, &si->si_regs.psr); - __put_user(env->pc, &si->si_regs.pc); - __put_user(env->npc, &si->si_regs.npc); - __put_user(env->y, &si->si_regs.y); - for (i=0; i < 8; i++) { - __put_user(env->gregs[i], &si->si_regs.u_regs[i]); - } - for (i=0; i < 8; i++) { - __put_user(env->regwptr[UREG_I0 + i], &si->si_regs.u_regs[i+8]); - } - __put_user(mask, &si->si_mask); - return err; -} - -#if 0 -static int -setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ - CPUSPARCState *env, unsigned long mask) -{ - int err = 0; - - __put_user(mask, &sc->sigc_mask); - __put_user(env->regwptr[UREG_SP], &sc->sigc_sp); - __put_user(env->pc, &sc->sigc_pc); - __put_user(env->npc, &sc->sigc_npc); - __put_user(env->psr, &sc->sigc_psr); - __put_user(env->gregs[1], &sc->sigc_g1); - __put_user(env->regwptr[UREG_O0], &sc->sigc_o0); - - return err; -} -#endif -#define NF_ALIGNEDSZ (((sizeof(struct target_signal_frame) + 7) & (~7))) - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUSPARCState *env) -{ - abi_ulong sf_addr; - struct target_signal_frame *sf; - int sigframe_size, err, i; - - /* 1. Make sure everything is clean */ - //synchronize_user_stack(); - - sigframe_size = NF_ALIGNEDSZ; - sf_addr = get_sigframe(ka, env, sigframe_size); - trace_user_setup_frame(env, sf_addr); - - sf = lock_user(VERIFY_WRITE, sf_addr, - sizeof(struct target_signal_frame), 0); - if (!sf) { - goto sigsegv; - } -#if 0 - if (invalid_frame_pointer(sf, sigframe_size)) - goto sigill_and_return; -#endif - /* 2. Save the current process state */ - err = setup___siginfo(&sf->info, env, set->sig[0]); - __put_user(0, &sf->extra_size); - - //save_fpu_state(regs, &sf->fpu_state); - //__put_user(&sf->fpu_state, &sf->fpu_save); - - __put_user(set->sig[0], &sf->info.si_mask); - for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { - __put_user(set->sig[i + 1], &sf->extramask[i]); - } - - for (i = 0; i < 8; i++) { - __put_user(env->regwptr[i + UREG_L0], &sf->ss.locals[i]); - } - for (i = 0; i < 8; i++) { - __put_user(env->regwptr[i + UREG_I0], &sf->ss.ins[i]); - } - if (err) - goto sigsegv; - - /* 3. signal handler back-trampoline and parameters */ - env->regwptr[UREG_FP] = sf_addr; - env->regwptr[UREG_I0] = sig; - env->regwptr[UREG_I1] = sf_addr + - offsetof(struct target_signal_frame, info); - env->regwptr[UREG_I2] = sf_addr + - offsetof(struct target_signal_frame, info); - - /* 4. signal handler */ - env->pc = ka->_sa_handler; - env->npc = (env->pc + 4); - /* 5. return to kernel instructions */ - if (ka->ka_restorer) { - env->regwptr[UREG_I7] = ka->ka_restorer; - } else { - uint32_t val32; - - env->regwptr[UREG_I7] = sf_addr + - offsetof(struct target_signal_frame, insns) - 2 * 4; - - /* mov __NR_sigreturn, %g1 */ - val32 = 0x821020d8; - __put_user(val32, &sf->insns[0]); - - /* t 0x10 */ - val32 = 0x91d02010; - __put_user(val32, &sf->insns[1]); - if (err) - goto sigsegv; - - /* Flush instruction space. */ - // flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); - // tb_flush(env); - } - unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); - return; -#if 0 -sigill_and_return: - force_sig(TARGET_SIGILL); -#endif -sigsegv: - unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); - force_sigsegv(sig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUSPARCState *env) -{ - fprintf(stderr, "setup_rt_frame: not implemented\n"); -} - -long do_sigreturn(CPUSPARCState *env) -{ - abi_ulong sf_addr; - struct target_signal_frame *sf; - uint32_t up_psr, pc, npc; - target_sigset_t set; - sigset_t host_set; - int err=0, i; - - sf_addr = env->regwptr[UREG_FP]; - trace_user_do_sigreturn(env, sf_addr); - if (!lock_user_struct(VERIFY_READ, sf, sf_addr, 1)) { - goto segv_and_exit; - } - - /* 1. Make sure we are not getting garbage from the user */ - - if (sf_addr & 3) - goto segv_and_exit; - - __get_user(pc, &sf->info.si_regs.pc); - __get_user(npc, &sf->info.si_regs.npc); - - if ((pc | npc) & 3) { - goto segv_and_exit; - } - - /* 2. Restore the state */ - __get_user(up_psr, &sf->info.si_regs.psr); - - /* User can only change condition codes and FPU enabling in %psr. */ - env->psr = (up_psr & (PSR_ICC /* | PSR_EF */)) - | (env->psr & ~(PSR_ICC /* | PSR_EF */)); - - env->pc = pc; - env->npc = npc; - __get_user(env->y, &sf->info.si_regs.y); - for (i=0; i < 8; i++) { - __get_user(env->gregs[i], &sf->info.si_regs.u_regs[i]); - } - for (i=0; i < 8; i++) { - __get_user(env->regwptr[i + UREG_I0], &sf->info.si_regs.u_regs[i+8]); - } - - /* FIXME: implement FPU save/restore: - * __get_user(fpu_save, &sf->fpu_save); - * if (fpu_save) - * err |= restore_fpu_state(env, fpu_save); - */ - - /* This is pretty much atomic, no amount locking would prevent - * the races which exist anyways. - */ - __get_user(set.sig[0], &sf->info.si_mask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(set.sig[i], &sf->extramask[i - 1]); - } - - target_to_host_sigset_internal(&host_set, &set); - set_sigmask(&host_set); - - if (err) { - goto segv_and_exit; - } - unlock_user_struct(sf, sf_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -segv_and_exit: - unlock_user_struct(sf, sf_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUSPARCState *env) -{ - trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) -#define SPARC_MC_TSTATE 0 -#define SPARC_MC_PC 1 -#define SPARC_MC_NPC 2 -#define SPARC_MC_Y 3 -#define SPARC_MC_G1 4 -#define SPARC_MC_G2 5 -#define SPARC_MC_G3 6 -#define SPARC_MC_G4 7 -#define SPARC_MC_G5 8 -#define SPARC_MC_G6 9 -#define SPARC_MC_G7 10 -#define SPARC_MC_O0 11 -#define SPARC_MC_O1 12 -#define SPARC_MC_O2 13 -#define SPARC_MC_O3 14 -#define SPARC_MC_O4 15 -#define SPARC_MC_O5 16 -#define SPARC_MC_O6 17 -#define SPARC_MC_O7 18 -#define SPARC_MC_NGREG 19 - -typedef abi_ulong target_mc_greg_t; -typedef target_mc_greg_t target_mc_gregset_t[SPARC_MC_NGREG]; - -struct target_mc_fq { - abi_ulong *mcfq_addr; - uint32_t mcfq_insn; -}; - -struct target_mc_fpu { - union { - uint32_t sregs[32]; - uint64_t dregs[32]; - //uint128_t qregs[16]; - } mcfpu_fregs; - abi_ulong mcfpu_fsr; - abi_ulong mcfpu_fprs; - abi_ulong mcfpu_gsr; - struct target_mc_fq *mcfpu_fq; - unsigned char mcfpu_qcnt; - unsigned char mcfpu_qentsz; - unsigned char mcfpu_enab; -}; -typedef struct target_mc_fpu target_mc_fpu_t; - -typedef struct { - target_mc_gregset_t mc_gregs; - target_mc_greg_t mc_fp; - target_mc_greg_t mc_i7; - target_mc_fpu_t mc_fpregs; -} target_mcontext_t; - -struct target_ucontext { - struct target_ucontext *tuc_link; - abi_ulong tuc_flags; - target_sigset_t tuc_sigmask; - target_mcontext_t tuc_mcontext; -}; - -/* A V9 register window */ -struct target_reg_window { - abi_ulong locals[8]; - abi_ulong ins[8]; -}; - -#define TARGET_STACK_BIAS 2047 - -/* {set, get}context() needed for 64-bit SparcLinux userland. */ -void sparc64_set_context(CPUSPARCState *env) -{ - abi_ulong ucp_addr; - struct target_ucontext *ucp; - target_mc_gregset_t *grp; - abi_ulong pc, npc, tstate; - abi_ulong fp, i7, w_addr; - unsigned int i; - - ucp_addr = env->regwptr[UREG_I0]; - if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1)) { - goto do_sigsegv; - } - grp = &ucp->tuc_mcontext.mc_gregs; - __get_user(pc, &((*grp)[SPARC_MC_PC])); - __get_user(npc, &((*grp)[SPARC_MC_NPC])); - if ((pc | npc) & 3) { - goto do_sigsegv; - } - if (env->regwptr[UREG_I1]) { - target_sigset_t target_set; - sigset_t set; - - if (TARGET_NSIG_WORDS == 1) { - __get_user(target_set.sig[0], &ucp->tuc_sigmask.sig[0]); - } else { - abi_ulong *src, *dst; - src = ucp->tuc_sigmask.sig; - dst = target_set.sig; - for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { - __get_user(*dst, src); - } - } - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - } - env->pc = pc; - env->npc = npc; - __get_user(env->y, &((*grp)[SPARC_MC_Y])); - __get_user(tstate, &((*grp)[SPARC_MC_TSTATE])); - env->asi = (tstate >> 24) & 0xff; - cpu_put_ccr(env, tstate >> 32); - cpu_put_cwp64(env, tstate & 0x1f); - __get_user(env->gregs[1], (&(*grp)[SPARC_MC_G1])); - __get_user(env->gregs[2], (&(*grp)[SPARC_MC_G2])); - __get_user(env->gregs[3], (&(*grp)[SPARC_MC_G3])); - __get_user(env->gregs[4], (&(*grp)[SPARC_MC_G4])); - __get_user(env->gregs[5], (&(*grp)[SPARC_MC_G5])); - __get_user(env->gregs[6], (&(*grp)[SPARC_MC_G6])); - __get_user(env->gregs[7], (&(*grp)[SPARC_MC_G7])); - __get_user(env->regwptr[UREG_I0], (&(*grp)[SPARC_MC_O0])); - __get_user(env->regwptr[UREG_I1], (&(*grp)[SPARC_MC_O1])); - __get_user(env->regwptr[UREG_I2], (&(*grp)[SPARC_MC_O2])); - __get_user(env->regwptr[UREG_I3], (&(*grp)[SPARC_MC_O3])); - __get_user(env->regwptr[UREG_I4], (&(*grp)[SPARC_MC_O4])); - __get_user(env->regwptr[UREG_I5], (&(*grp)[SPARC_MC_O5])); - __get_user(env->regwptr[UREG_I6], (&(*grp)[SPARC_MC_O6])); - __get_user(env->regwptr[UREG_I7], (&(*grp)[SPARC_MC_O7])); - - __get_user(fp, &(ucp->tuc_mcontext.mc_fp)); - __get_user(i7, &(ucp->tuc_mcontext.mc_i7)); - - w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; - if (put_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), - abi_ulong) != 0) { - goto do_sigsegv; - } - if (put_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), - abi_ulong) != 0) { - goto do_sigsegv; - } - /* FIXME this does not match how the kernel handles the FPU in - * its sparc64_set_context implementation. In particular the FPU - * is only restored if fenab is non-zero in: - * __get_user(fenab, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_enab)); - */ - __get_user(env->fprs, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fprs)); - { - uint32_t *src = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; - for (i = 0; i < 64; i++, src++) { - if (i & 1) { - __get_user(env->fpr[i/2].l.lower, src); - } else { - __get_user(env->fpr[i/2].l.upper, src); - } - } - } - __get_user(env->fsr, - &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fsr)); - __get_user(env->gsr, - &(ucp->tuc_mcontext.mc_fpregs.mcfpu_gsr)); - unlock_user_struct(ucp, ucp_addr, 0); - return; -do_sigsegv: - unlock_user_struct(ucp, ucp_addr, 0); - force_sig(TARGET_SIGSEGV); -} - -void sparc64_get_context(CPUSPARCState *env) -{ - abi_ulong ucp_addr; - struct target_ucontext *ucp; - target_mc_gregset_t *grp; - target_mcontext_t *mcp; - abi_ulong fp, i7, w_addr; - int err; - unsigned int i; - target_sigset_t target_set; - sigset_t set; - - ucp_addr = env->regwptr[UREG_I0]; - if (!lock_user_struct(VERIFY_WRITE, ucp, ucp_addr, 0)) { - goto do_sigsegv; - } - - mcp = &ucp->tuc_mcontext; - grp = &mcp->mc_gregs; - - /* Skip over the trap instruction, first. */ - env->pc = env->npc; - env->npc += 4; - - /* If we're only reading the signal mask then do_sigprocmask() - * is guaranteed not to fail, which is important because we don't - * have any way to signal a failure or restart this operation since - * this is not a normal syscall. - */ - err = do_sigprocmask(0, NULL, &set); - assert(err == 0); - host_to_target_sigset_internal(&target_set, &set); - if (TARGET_NSIG_WORDS == 1) { - __put_user(target_set.sig[0], - (abi_ulong *)&ucp->tuc_sigmask); - } else { - abi_ulong *src, *dst; - src = target_set.sig; - dst = ucp->tuc_sigmask.sig; - for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { - __put_user(*src, dst); - } - if (err) - goto do_sigsegv; - } - - /* XXX: tstate must be saved properly */ - // __put_user(env->tstate, &((*grp)[SPARC_MC_TSTATE])); - __put_user(env->pc, &((*grp)[SPARC_MC_PC])); - __put_user(env->npc, &((*grp)[SPARC_MC_NPC])); - __put_user(env->y, &((*grp)[SPARC_MC_Y])); - __put_user(env->gregs[1], &((*grp)[SPARC_MC_G1])); - __put_user(env->gregs[2], &((*grp)[SPARC_MC_G2])); - __put_user(env->gregs[3], &((*grp)[SPARC_MC_G3])); - __put_user(env->gregs[4], &((*grp)[SPARC_MC_G4])); - __put_user(env->gregs[5], &((*grp)[SPARC_MC_G5])); - __put_user(env->gregs[6], &((*grp)[SPARC_MC_G6])); - __put_user(env->gregs[7], &((*grp)[SPARC_MC_G7])); - __put_user(env->regwptr[UREG_I0], &((*grp)[SPARC_MC_O0])); - __put_user(env->regwptr[UREG_I1], &((*grp)[SPARC_MC_O1])); - __put_user(env->regwptr[UREG_I2], &((*grp)[SPARC_MC_O2])); - __put_user(env->regwptr[UREG_I3], &((*grp)[SPARC_MC_O3])); - __put_user(env->regwptr[UREG_I4], &((*grp)[SPARC_MC_O4])); - __put_user(env->regwptr[UREG_I5], &((*grp)[SPARC_MC_O5])); - __put_user(env->regwptr[UREG_I6], &((*grp)[SPARC_MC_O6])); - __put_user(env->regwptr[UREG_I7], &((*grp)[SPARC_MC_O7])); - - w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; - fp = i7 = 0; - if (get_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), - abi_ulong) != 0) { - goto do_sigsegv; - } - if (get_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), - abi_ulong) != 0) { - goto do_sigsegv; - } - __put_user(fp, &(mcp->mc_fp)); - __put_user(i7, &(mcp->mc_i7)); - - { - uint32_t *dst = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; - for (i = 0; i < 64; i++, dst++) { - if (i & 1) { - __put_user(env->fpr[i/2].l.lower, dst); - } else { - __put_user(env->fpr[i/2].l.upper, dst); - } - } - } - __put_user(env->fsr, &(mcp->mc_fpregs.mcfpu_fsr)); - __put_user(env->gsr, &(mcp->mc_fpregs.mcfpu_gsr)); - __put_user(env->fprs, &(mcp->mc_fpregs.mcfpu_fprs)); - - if (err) - goto do_sigsegv; - unlock_user_struct(ucp, ucp_addr, 1); - return; -do_sigsegv: - unlock_user_struct(ucp, ucp_addr, 1); - force_sig(TARGET_SIGSEGV); -} -#endif -#elif defined(TARGET_MIPS) || defined(TARGET_MIPS64) +#if defined(TARGET_MIPS) || defined(TARGET_MIPS64) # if defined(TARGET_ABI_MIPSO32) struct target_sigcontext { diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 02ca338b6c..c823e61cee 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -16,3 +16,609 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +#define __SUNOS_MAXWIN 31 + +/* This is what SunOS does, so shall I. */ +struct target_sigcontext { + abi_ulong sigc_onstack; /* state to restore */ + + abi_ulong sigc_mask; /* sigmask to restore */ + abi_ulong sigc_sp; /* stack pointer */ + abi_ulong sigc_pc; /* program counter */ + abi_ulong sigc_npc; /* next program counter */ + abi_ulong sigc_psr; /* for condition codes etc */ + abi_ulong sigc_g1; /* User uses these two registers */ + abi_ulong sigc_o0; /* within the trampoline code. */ + + /* Now comes information regarding the users window set + * at the time of the signal. + */ + abi_ulong sigc_oswins; /* outstanding windows */ + + /* stack ptrs for each regwin buf */ + char *sigc_spbuf[__SUNOS_MAXWIN]; + + /* Windows to restore after signal */ + struct { + abi_ulong locals[8]; + abi_ulong ins[8]; + } sigc_wbuf[__SUNOS_MAXWIN]; +}; +/* A Sparc stack frame */ +struct sparc_stackf { + abi_ulong locals[8]; + abi_ulong ins[8]; + /* It's simpler to treat fp and callers_pc as elements of ins[] + * since we never need to access them ourselves. + */ + char *structptr; + abi_ulong xargs[6]; + abi_ulong xxargs[1]; +}; + +typedef struct { + struct { + abi_ulong psr; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong u_regs[16]; /* globals and ins */ + } si_regs; + int si_mask; +} __siginfo_t; + +typedef struct { + abi_ulong si_float_regs[32]; + unsigned long si_fsr; + unsigned long si_fpqdepth; + struct { + unsigned long *insn_addr; + unsigned long insn; + } si_fpqueue [16]; +} qemu_siginfo_fpu_t; + + +struct target_signal_frame { + struct sparc_stackf ss; + __siginfo_t info; + abi_ulong fpu_save; + abi_ulong insns[2] __attribute__ ((aligned (8))); + abi_ulong extramask[TARGET_NSIG_WORDS - 1]; + abi_ulong extra_size; /* Should be 0 */ + qemu_siginfo_fpu_t fpu_state; +}; +struct target_rt_signal_frame { + struct sparc_stackf ss; + siginfo_t info; + abi_ulong regs[20]; + sigset_t mask; + abi_ulong fpu_save; + unsigned int insns[2]; + stack_t stack; + unsigned int extra_size; /* Should be 0 */ + qemu_siginfo_fpu_t fpu_state; +}; + +#define UREG_O0 16 +#define UREG_O6 22 +#define UREG_I0 0 +#define UREG_I1 1 +#define UREG_I2 2 +#define UREG_I3 3 +#define UREG_I4 4 +#define UREG_I5 5 +#define UREG_I6 6 +#define UREG_I7 7 +#define UREG_L0 8 +#define UREG_FP UREG_I6 +#define UREG_SP UREG_O6 + +static inline abi_ulong get_sigframe(struct target_sigaction *sa, + CPUSPARCState *env, + unsigned long framesize) +{ + abi_ulong sp; + + sp = env->regwptr[UREG_FP]; + + /* This is the X/Open sanctioned signal stack switching. */ + if (sa->sa_flags & TARGET_SA_ONSTACK) { + if (!on_sig_stack(sp) + && !((target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size) & 7)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + } + return sp - framesize; +} + +static int +setup___siginfo(__siginfo_t *si, CPUSPARCState *env, abi_ulong mask) +{ + int err = 0, i; + + __put_user(env->psr, &si->si_regs.psr); + __put_user(env->pc, &si->si_regs.pc); + __put_user(env->npc, &si->si_regs.npc); + __put_user(env->y, &si->si_regs.y); + for (i=0; i < 8; i++) { + __put_user(env->gregs[i], &si->si_regs.u_regs[i]); + } + for (i=0; i < 8; i++) { + __put_user(env->regwptr[UREG_I0 + i], &si->si_regs.u_regs[i+8]); + } + __put_user(mask, &si->si_mask); + return err; +} + +#if 0 +static int +setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ + CPUSPARCState *env, unsigned long mask) +{ + int err = 0; + + __put_user(mask, &sc->sigc_mask); + __put_user(env->regwptr[UREG_SP], &sc->sigc_sp); + __put_user(env->pc, &sc->sigc_pc); + __put_user(env->npc, &sc->sigc_npc); + __put_user(env->psr, &sc->sigc_psr); + __put_user(env->gregs[1], &sc->sigc_g1); + __put_user(env->regwptr[UREG_O0], &sc->sigc_o0); + + return err; +} +#endif +#define NF_ALIGNEDSZ (((sizeof(struct target_signal_frame) + 7) & (~7))) + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUSPARCState *env) +{ + abi_ulong sf_addr; + struct target_signal_frame *sf; + int sigframe_size, err, i; + + /* 1. Make sure everything is clean */ + //synchronize_user_stack(); + + sigframe_size = NF_ALIGNEDSZ; + sf_addr = get_sigframe(ka, env, sigframe_size); + trace_user_setup_frame(env, sf_addr); + + sf = lock_user(VERIFY_WRITE, sf_addr, + sizeof(struct target_signal_frame), 0); + if (!sf) { + goto sigsegv; + } +#if 0 + if (invalid_frame_pointer(sf, sigframe_size)) + goto sigill_and_return; +#endif + /* 2. Save the current process state */ + err = setup___siginfo(&sf->info, env, set->sig[0]); + __put_user(0, &sf->extra_size); + + //save_fpu_state(regs, &sf->fpu_state); + //__put_user(&sf->fpu_state, &sf->fpu_save); + + __put_user(set->sig[0], &sf->info.si_mask); + for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { + __put_user(set->sig[i + 1], &sf->extramask[i]); + } + + for (i = 0; i < 8; i++) { + __put_user(env->regwptr[i + UREG_L0], &sf->ss.locals[i]); + } + for (i = 0; i < 8; i++) { + __put_user(env->regwptr[i + UREG_I0], &sf->ss.ins[i]); + } + if (err) + goto sigsegv; + + /* 3. signal handler back-trampoline and parameters */ + env->regwptr[UREG_FP] = sf_addr; + env->regwptr[UREG_I0] = sig; + env->regwptr[UREG_I1] = sf_addr + + offsetof(struct target_signal_frame, info); + env->regwptr[UREG_I2] = sf_addr + + offsetof(struct target_signal_frame, info); + + /* 4. signal handler */ + env->pc = ka->_sa_handler; + env->npc = (env->pc + 4); + /* 5. return to kernel instructions */ + if (ka->ka_restorer) { + env->regwptr[UREG_I7] = ka->ka_restorer; + } else { + uint32_t val32; + + env->regwptr[UREG_I7] = sf_addr + + offsetof(struct target_signal_frame, insns) - 2 * 4; + + /* mov __NR_sigreturn, %g1 */ + val32 = 0x821020d8; + __put_user(val32, &sf->insns[0]); + + /* t 0x10 */ + val32 = 0x91d02010; + __put_user(val32, &sf->insns[1]); + if (err) + goto sigsegv; + + /* Flush instruction space. */ + // flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); + // tb_flush(env); + } + unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); + return; +#if 0 +sigill_and_return: + force_sig(TARGET_SIGILL); +#endif +sigsegv: + unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); + force_sigsegv(sig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUSPARCState *env) +{ + fprintf(stderr, "setup_rt_frame: not implemented\n"); +} + +long do_sigreturn(CPUSPARCState *env) +{ + abi_ulong sf_addr; + struct target_signal_frame *sf; + uint32_t up_psr, pc, npc; + target_sigset_t set; + sigset_t host_set; + int err=0, i; + + sf_addr = env->regwptr[UREG_FP]; + trace_user_do_sigreturn(env, sf_addr); + if (!lock_user_struct(VERIFY_READ, sf, sf_addr, 1)) { + goto segv_and_exit; + } + + /* 1. Make sure we are not getting garbage from the user */ + + if (sf_addr & 3) + goto segv_and_exit; + + __get_user(pc, &sf->info.si_regs.pc); + __get_user(npc, &sf->info.si_regs.npc); + + if ((pc | npc) & 3) { + goto segv_and_exit; + } + + /* 2. Restore the state */ + __get_user(up_psr, &sf->info.si_regs.psr); + + /* User can only change condition codes and FPU enabling in %psr. */ + env->psr = (up_psr & (PSR_ICC /* | PSR_EF */)) + | (env->psr & ~(PSR_ICC /* | PSR_EF */)); + + env->pc = pc; + env->npc = npc; + __get_user(env->y, &sf->info.si_regs.y); + for (i=0; i < 8; i++) { + __get_user(env->gregs[i], &sf->info.si_regs.u_regs[i]); + } + for (i=0; i < 8; i++) { + __get_user(env->regwptr[i + UREG_I0], &sf->info.si_regs.u_regs[i+8]); + } + + /* FIXME: implement FPU save/restore: + * __get_user(fpu_save, &sf->fpu_save); + * if (fpu_save) + * err |= restore_fpu_state(env, fpu_save); + */ + + /* This is pretty much atomic, no amount locking would prevent + * the races which exist anyways. + */ + __get_user(set.sig[0], &sf->info.si_mask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(set.sig[i], &sf->extramask[i - 1]); + } + + target_to_host_sigset_internal(&host_set, &set); + set_sigmask(&host_set); + + if (err) { + goto segv_and_exit; + } + unlock_user_struct(sf, sf_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +segv_and_exit: + unlock_user_struct(sf, sf_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUSPARCState *env) +{ + trace_user_do_rt_sigreturn(env, 0); + fprintf(stderr, "do_rt_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} + +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) +#define SPARC_MC_TSTATE 0 +#define SPARC_MC_PC 1 +#define SPARC_MC_NPC 2 +#define SPARC_MC_Y 3 +#define SPARC_MC_G1 4 +#define SPARC_MC_G2 5 +#define SPARC_MC_G3 6 +#define SPARC_MC_G4 7 +#define SPARC_MC_G5 8 +#define SPARC_MC_G6 9 +#define SPARC_MC_G7 10 +#define SPARC_MC_O0 11 +#define SPARC_MC_O1 12 +#define SPARC_MC_O2 13 +#define SPARC_MC_O3 14 +#define SPARC_MC_O4 15 +#define SPARC_MC_O5 16 +#define SPARC_MC_O6 17 +#define SPARC_MC_O7 18 +#define SPARC_MC_NGREG 19 + +typedef abi_ulong target_mc_greg_t; +typedef target_mc_greg_t target_mc_gregset_t[SPARC_MC_NGREG]; + +struct target_mc_fq { + abi_ulong *mcfq_addr; + uint32_t mcfq_insn; +}; + +struct target_mc_fpu { + union { + uint32_t sregs[32]; + uint64_t dregs[32]; + //uint128_t qregs[16]; + } mcfpu_fregs; + abi_ulong mcfpu_fsr; + abi_ulong mcfpu_fprs; + abi_ulong mcfpu_gsr; + struct target_mc_fq *mcfpu_fq; + unsigned char mcfpu_qcnt; + unsigned char mcfpu_qentsz; + unsigned char mcfpu_enab; +}; +typedef struct target_mc_fpu target_mc_fpu_t; + +typedef struct { + target_mc_gregset_t mc_gregs; + target_mc_greg_t mc_fp; + target_mc_greg_t mc_i7; + target_mc_fpu_t mc_fpregs; +} target_mcontext_t; + +struct target_ucontext { + struct target_ucontext *tuc_link; + abi_ulong tuc_flags; + target_sigset_t tuc_sigmask; + target_mcontext_t tuc_mcontext; +}; + +/* A V9 register window */ +struct target_reg_window { + abi_ulong locals[8]; + abi_ulong ins[8]; +}; + +#define TARGET_STACK_BIAS 2047 + +/* {set, get}context() needed for 64-bit SparcLinux userland. */ +void sparc64_set_context(CPUSPARCState *env) +{ + abi_ulong ucp_addr; + struct target_ucontext *ucp; + target_mc_gregset_t *grp; + abi_ulong pc, npc, tstate; + abi_ulong fp, i7, w_addr; + unsigned int i; + + ucp_addr = env->regwptr[UREG_I0]; + if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1)) { + goto do_sigsegv; + } + grp = &ucp->tuc_mcontext.mc_gregs; + __get_user(pc, &((*grp)[SPARC_MC_PC])); + __get_user(npc, &((*grp)[SPARC_MC_NPC])); + if ((pc | npc) & 3) { + goto do_sigsegv; + } + if (env->regwptr[UREG_I1]) { + target_sigset_t target_set; + sigset_t set; + + if (TARGET_NSIG_WORDS == 1) { + __get_user(target_set.sig[0], &ucp->tuc_sigmask.sig[0]); + } else { + abi_ulong *src, *dst; + src = ucp->tuc_sigmask.sig; + dst = target_set.sig; + for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { + __get_user(*dst, src); + } + } + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + } + env->pc = pc; + env->npc = npc; + __get_user(env->y, &((*grp)[SPARC_MC_Y])); + __get_user(tstate, &((*grp)[SPARC_MC_TSTATE])); + env->asi = (tstate >> 24) & 0xff; + cpu_put_ccr(env, tstate >> 32); + cpu_put_cwp64(env, tstate & 0x1f); + __get_user(env->gregs[1], (&(*grp)[SPARC_MC_G1])); + __get_user(env->gregs[2], (&(*grp)[SPARC_MC_G2])); + __get_user(env->gregs[3], (&(*grp)[SPARC_MC_G3])); + __get_user(env->gregs[4], (&(*grp)[SPARC_MC_G4])); + __get_user(env->gregs[5], (&(*grp)[SPARC_MC_G5])); + __get_user(env->gregs[6], (&(*grp)[SPARC_MC_G6])); + __get_user(env->gregs[7], (&(*grp)[SPARC_MC_G7])); + __get_user(env->regwptr[UREG_I0], (&(*grp)[SPARC_MC_O0])); + __get_user(env->regwptr[UREG_I1], (&(*grp)[SPARC_MC_O1])); + __get_user(env->regwptr[UREG_I2], (&(*grp)[SPARC_MC_O2])); + __get_user(env->regwptr[UREG_I3], (&(*grp)[SPARC_MC_O3])); + __get_user(env->regwptr[UREG_I4], (&(*grp)[SPARC_MC_O4])); + __get_user(env->regwptr[UREG_I5], (&(*grp)[SPARC_MC_O5])); + __get_user(env->regwptr[UREG_I6], (&(*grp)[SPARC_MC_O6])); + __get_user(env->regwptr[UREG_I7], (&(*grp)[SPARC_MC_O7])); + + __get_user(fp, &(ucp->tuc_mcontext.mc_fp)); + __get_user(i7, &(ucp->tuc_mcontext.mc_i7)); + + w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; + if (put_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), + abi_ulong) != 0) { + goto do_sigsegv; + } + if (put_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), + abi_ulong) != 0) { + goto do_sigsegv; + } + /* FIXME this does not match how the kernel handles the FPU in + * its sparc64_set_context implementation. In particular the FPU + * is only restored if fenab is non-zero in: + * __get_user(fenab, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_enab)); + */ + __get_user(env->fprs, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fprs)); + { + uint32_t *src = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; + for (i = 0; i < 64; i++, src++) { + if (i & 1) { + __get_user(env->fpr[i/2].l.lower, src); + } else { + __get_user(env->fpr[i/2].l.upper, src); + } + } + } + __get_user(env->fsr, + &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fsr)); + __get_user(env->gsr, + &(ucp->tuc_mcontext.mc_fpregs.mcfpu_gsr)); + unlock_user_struct(ucp, ucp_addr, 0); + return; +do_sigsegv: + unlock_user_struct(ucp, ucp_addr, 0); + force_sig(TARGET_SIGSEGV); +} + +void sparc64_get_context(CPUSPARCState *env) +{ + abi_ulong ucp_addr; + struct target_ucontext *ucp; + target_mc_gregset_t *grp; + target_mcontext_t *mcp; + abi_ulong fp, i7, w_addr; + int err; + unsigned int i; + target_sigset_t target_set; + sigset_t set; + + ucp_addr = env->regwptr[UREG_I0]; + if (!lock_user_struct(VERIFY_WRITE, ucp, ucp_addr, 0)) { + goto do_sigsegv; + } + + mcp = &ucp->tuc_mcontext; + grp = &mcp->mc_gregs; + + /* Skip over the trap instruction, first. */ + env->pc = env->npc; + env->npc += 4; + + /* If we're only reading the signal mask then do_sigprocmask() + * is guaranteed not to fail, which is important because we don't + * have any way to signal a failure or restart this operation since + * this is not a normal syscall. + */ + err = do_sigprocmask(0, NULL, &set); + assert(err == 0); + host_to_target_sigset_internal(&target_set, &set); + if (TARGET_NSIG_WORDS == 1) { + __put_user(target_set.sig[0], + (abi_ulong *)&ucp->tuc_sigmask); + } else { + abi_ulong *src, *dst; + src = target_set.sig; + dst = ucp->tuc_sigmask.sig; + for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { + __put_user(*src, dst); + } + if (err) + goto do_sigsegv; + } + + /* XXX: tstate must be saved properly */ + // __put_user(env->tstate, &((*grp)[SPARC_MC_TSTATE])); + __put_user(env->pc, &((*grp)[SPARC_MC_PC])); + __put_user(env->npc, &((*grp)[SPARC_MC_NPC])); + __put_user(env->y, &((*grp)[SPARC_MC_Y])); + __put_user(env->gregs[1], &((*grp)[SPARC_MC_G1])); + __put_user(env->gregs[2], &((*grp)[SPARC_MC_G2])); + __put_user(env->gregs[3], &((*grp)[SPARC_MC_G3])); + __put_user(env->gregs[4], &((*grp)[SPARC_MC_G4])); + __put_user(env->gregs[5], &((*grp)[SPARC_MC_G5])); + __put_user(env->gregs[6], &((*grp)[SPARC_MC_G6])); + __put_user(env->gregs[7], &((*grp)[SPARC_MC_G7])); + __put_user(env->regwptr[UREG_I0], &((*grp)[SPARC_MC_O0])); + __put_user(env->regwptr[UREG_I1], &((*grp)[SPARC_MC_O1])); + __put_user(env->regwptr[UREG_I2], &((*grp)[SPARC_MC_O2])); + __put_user(env->regwptr[UREG_I3], &((*grp)[SPARC_MC_O3])); + __put_user(env->regwptr[UREG_I4], &((*grp)[SPARC_MC_O4])); + __put_user(env->regwptr[UREG_I5], &((*grp)[SPARC_MC_O5])); + __put_user(env->regwptr[UREG_I6], &((*grp)[SPARC_MC_O6])); + __put_user(env->regwptr[UREG_I7], &((*grp)[SPARC_MC_O7])); + + w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; + fp = i7 = 0; + if (get_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), + abi_ulong) != 0) { + goto do_sigsegv; + } + if (get_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), + abi_ulong) != 0) { + goto do_sigsegv; + } + __put_user(fp, &(mcp->mc_fp)); + __put_user(i7, &(mcp->mc_i7)); + + { + uint32_t *dst = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; + for (i = 0; i < 64; i++, dst++) { + if (i & 1) { + __put_user(env->fpr[i/2].l.lower, dst); + } else { + __put_user(env->fpr[i/2].l.upper, dst); + } + } + } + __put_user(env->fsr, &(mcp->mc_fpregs.mcfpu_fsr)); + __put_user(env->gsr, &(mcp->mc_fpregs.mcfpu_gsr)); + __put_user(env->fprs, &(mcp->mc_fpregs.mcfpu_fprs)); + + if (err) + goto do_sigsegv; + unlock_user_struct(ucp, ucp_addr, 1); + return; +do_sigsegv: + unlock_user_struct(ucp, ucp_addr, 1); + force_sig(TARGET_SIGSEGV); +} +#endif diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h index e445e2b463..ea2c68c900 100644 --- a/linux-user/sparc/target_signal.h +++ b/linux-user/sparc/target_signal.h @@ -33,5 +33,9 @@ static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) return state->regwptr[UREG_FP]; } - +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUSPARCState *env); +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUSPARCState *env); #endif /* SPARC_TARGET_SIGNAL_H */ diff --git a/linux-user/sparc64/signal.c b/linux-user/sparc64/signal.c index 02ca338b6c..c263eb0f08 100644 --- a/linux-user/sparc64/signal.c +++ b/linux-user/sparc64/signal.c @@ -16,3 +16,5 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#define SPARC_TARGET_SIGNAL_H /* to only include sparc64/target_signal.h */ +#include "../sparc/signal.c" diff --git a/linux-user/sparc64/target_signal.h b/linux-user/sparc64/target_signal.h index 4449457baf..7f10db9ede 100644 --- a/linux-user/sparc64/target_signal.h +++ b/linux-user/sparc64/target_signal.h @@ -33,5 +33,9 @@ static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) return state->regwptr[UREG_FP]; } - +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUSPARCState *env); +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUSPARCState *env); #endif /* SPARC64_TARGET_SIGNAL_H */ From 8949bef18b9f8731d8aa99d2e5fcf03d52f11412 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:33 +0200 Subject: [PATCH 0143/2380] linux-user: move mips/mips64 signal.c parts to mips directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to mips/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). mips64/signal.c includes mips/signal.c Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-19-laurent@vivier.eu> --- linux-user/mips/signal.c | 382 ++++++++++++++++++++++++++++++ linux-user/mips/target_signal.h | 9 +- linux-user/mips64/signal.c | 2 + linux-user/mips64/target_signal.h | 4 +- linux-user/signal.c | 381 +---------------------------- 5 files changed, 396 insertions(+), 382 deletions(-) diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c index 02ca338b6c..adeb5a4241 100644 --- a/linux-user/mips/signal.c +++ b/linux-user/mips/signal.c @@ -16,3 +16,385 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +# if defined(TARGET_ABI_MIPSO32) +struct target_sigcontext { + uint32_t sc_regmask; /* Unused */ + uint32_t sc_status; + uint64_t sc_pc; + uint64_t sc_regs[32]; + uint64_t sc_fpregs[32]; + uint32_t sc_ownedfp; /* Unused */ + uint32_t sc_fpc_csr; + uint32_t sc_fpc_eir; /* Unused */ + uint32_t sc_used_math; + uint32_t sc_dsp; /* dsp status, was sc_ssflags */ + uint32_t pad0; + uint64_t sc_mdhi; + uint64_t sc_mdlo; + target_ulong sc_hi1; /* Was sc_cause */ + target_ulong sc_lo1; /* Was sc_badvaddr */ + target_ulong sc_hi2; /* Was sc_sigset[4] */ + target_ulong sc_lo2; + target_ulong sc_hi3; + target_ulong sc_lo3; +}; +# else /* N32 || N64 */ +struct target_sigcontext { + uint64_t sc_regs[32]; + uint64_t sc_fpregs[32]; + uint64_t sc_mdhi; + uint64_t sc_hi1; + uint64_t sc_hi2; + uint64_t sc_hi3; + uint64_t sc_mdlo; + uint64_t sc_lo1; + uint64_t sc_lo2; + uint64_t sc_lo3; + uint64_t sc_pc; + uint32_t sc_fpc_csr; + uint32_t sc_used_math; + uint32_t sc_dsp; + uint32_t sc_reserved; +}; +# endif /* O32 */ + +struct sigframe { + uint32_t sf_ass[4]; /* argument save space for o32 */ + uint32_t sf_code[2]; /* signal trampoline */ + struct target_sigcontext sf_sc; + target_sigset_t sf_mask; +}; + +struct target_ucontext { + target_ulong tuc_flags; + target_ulong tuc_link; + target_stack_t tuc_stack; + target_ulong pad0; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_rt_sigframe { + uint32_t rs_ass[4]; /* argument save space for o32 */ + uint32_t rs_code[2]; /* signal trampoline */ + struct target_siginfo rs_info; + struct target_ucontext rs_uc; +}; + +/* Install trampoline to jump back from signal handler */ +static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall) +{ + int err = 0; + + /* + * Set up the return code ... + * + * li v0, __NR__foo_sigreturn + * syscall + */ + + __put_user(0x24020000 + syscall, tramp + 0); + __put_user(0x0000000c , tramp + 1); + return err; +} + +static inline void setup_sigcontext(CPUMIPSState *regs, + struct target_sigcontext *sc) +{ + int i; + + __put_user(exception_resume_pc(regs), &sc->sc_pc); + regs->hflags &= ~MIPS_HFLAG_BMASK; + + __put_user(0, &sc->sc_regs[0]); + for (i = 1; i < 32; ++i) { + __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); + } + + __put_user(regs->active_tc.HI[0], &sc->sc_mdhi); + __put_user(regs->active_tc.LO[0], &sc->sc_mdlo); + + /* Rather than checking for dsp existence, always copy. The storage + would just be garbage otherwise. */ + __put_user(regs->active_tc.HI[1], &sc->sc_hi1); + __put_user(regs->active_tc.HI[2], &sc->sc_hi2); + __put_user(regs->active_tc.HI[3], &sc->sc_hi3); + __put_user(regs->active_tc.LO[1], &sc->sc_lo1); + __put_user(regs->active_tc.LO[2], &sc->sc_lo2); + __put_user(regs->active_tc.LO[3], &sc->sc_lo3); + { + uint32_t dsp = cpu_rddsp(0x3ff, regs); + __put_user(dsp, &sc->sc_dsp); + } + + __put_user(1, &sc->sc_used_math); + + for (i = 0; i < 32; ++i) { + __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); + } +} + +static inline void +restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc) +{ + int i; + + __get_user(regs->CP0_EPC, &sc->sc_pc); + + __get_user(regs->active_tc.HI[0], &sc->sc_mdhi); + __get_user(regs->active_tc.LO[0], &sc->sc_mdlo); + + for (i = 1; i < 32; ++i) { + __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); + } + + __get_user(regs->active_tc.HI[1], &sc->sc_hi1); + __get_user(regs->active_tc.HI[2], &sc->sc_hi2); + __get_user(regs->active_tc.HI[3], &sc->sc_hi3); + __get_user(regs->active_tc.LO[1], &sc->sc_lo1); + __get_user(regs->active_tc.LO[2], &sc->sc_lo2); + __get_user(regs->active_tc.LO[3], &sc->sc_lo3); + { + uint32_t dsp; + __get_user(dsp, &sc->sc_dsp); + cpu_wrdsp(dsp, 0x3ff, regs); + } + + for (i = 0; i < 32; ++i) { + __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); + } +} + +/* + * Determine which stack to use.. + */ +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size) +{ + unsigned long sp; + + /* Default to using normal stack */ + sp = regs->active_tc.gpr[29]; + + /* + * FPU emulator may have its own trampoline active just + * above the user stack, 16-bytes before the next lowest + * 16 byte boundary. Try to avoid trashing it. + */ + sp -= 32; + + /* This is the X/Open sanctioned signal stack switching. */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + + return (sp - frame_size) & ~7; +} + +static void mips_set_hflags_isa_mode_from_pc(CPUMIPSState *env) +{ + if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { + env->hflags &= ~MIPS_HFLAG_M16; + env->hflags |= (env->active_tc.PC & 1) << MIPS_HFLAG_M16_SHIFT; + env->active_tc.PC &= ~(target_ulong) 1; + } +} + +# if defined(TARGET_ABI_MIPSO32) +/* compare linux/arch/mips/kernel/signal.c:setup_frame() */ +void setup_frame(int sig, struct target_sigaction * ka, + target_sigset_t *set, CPUMIPSState *regs) +{ + struct sigframe *frame; + abi_ulong frame_addr; + int i; + + frame_addr = get_sigframe(ka, regs, sizeof(*frame)); + trace_user_setup_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + install_sigtramp(frame->sf_code, TARGET_NR_sigreturn); + + setup_sigcontext(regs, &frame->sf_sc); + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->sf_mask.sig[i]); + } + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = 0 (should be cause) + * a2 = pointer to struct sigcontext + * + * $25 and PC point to the signal handler, $29 points to the + * struct sigframe. + */ + regs->active_tc.gpr[ 4] = sig; + regs->active_tc.gpr[ 5] = 0; + regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc); + regs->active_tc.gpr[29] = frame_addr; + regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code); + /* The original kernel code sets CP0_EPC to the handler + * since it returns to userland using eret + * we cannot do this here, and we must set PC directly */ + regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler; + mips_set_hflags_isa_mode_from_pc(regs); + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); +} + +long do_sigreturn(CPUMIPSState *regs) +{ + struct sigframe *frame; + abi_ulong frame_addr; + sigset_t blocked; + target_sigset_t target_set; + int i; + + frame_addr = regs->active_tc.gpr[29]; + trace_user_do_sigreturn(regs, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->sf_mask.sig[i]); + } + + target_to_host_sigset_internal(&blocked, &target_set); + set_sigmask(&blocked); + + restore_sigcontext(regs, &frame->sf_sc); + +#if 0 + /* + * Don't let your children do this ... + */ + __asm__ __volatile__( + "move\t$29, %0\n\t" + "j\tsyscall_exit" + :/* no outputs */ + :"r" (®s)); + /* Unreached */ +#endif + + regs->active_tc.PC = regs->CP0_EPC; + mips_set_hflags_isa_mode_from_pc(regs); + /* I am not sure this is right, but it seems to work + * maybe a problem with nested signals ? */ + regs->CP0_EPC = 0; + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} +# endif /* O32 */ + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUMIPSState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn); + + tswap_siginfo(&frame->rs_info, info); + + __put_user(0, &frame->rs_uc.tuc_flags); + __put_user(0, &frame->rs_uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, &frame->rs_uc.tuc_stack.ss_sp); + __put_user(target_sigaltstack_used.ss_size, &frame->rs_uc.tuc_stack.ss_size); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), + &frame->rs_uc.tuc_stack.ss_flags); + + setup_sigcontext(env, &frame->rs_uc.tuc_mcontext); + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]); + } + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = pointer to siginfo_t + * a2 = pointer to ucontext_t + * + * $25 and PC point to the signal handler, $29 points to the + * struct sigframe. + */ + env->active_tc.gpr[ 4] = sig; + env->active_tc.gpr[ 5] = frame_addr + + offsetof(struct target_rt_sigframe, rs_info); + env->active_tc.gpr[ 6] = frame_addr + + offsetof(struct target_rt_sigframe, rs_uc); + env->active_tc.gpr[29] = frame_addr; + env->active_tc.gpr[31] = frame_addr + + offsetof(struct target_rt_sigframe, rs_code); + /* The original kernel code sets CP0_EPC to the handler + * since it returns to userland using eret + * we cannot do this here, and we must set PC directly */ + env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler; + mips_set_hflags_isa_mode_from_pc(env); + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} + +long do_rt_sigreturn(CPUMIPSState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + sigset_t blocked; + + frame_addr = env->active_tc.gpr[29]; + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); + set_sigmask(&blocked); + + restore_sigcontext(env, &frame->rs_uc.tuc_mcontext); + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, rs_uc.tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) + goto badframe; + + env->active_tc.PC = env->CP0_EPC; + mips_set_hflags_isa_mode_from_pc(env); + /* I am not sure this is right, but it seems to work + * maybe a problem with nested signals ? */ + env->CP0_EPC = 0; + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/mips/target_signal.h b/linux-user/mips/target_signal.h index 8dd27cef35..22ab3e4a94 100644 --- a/linux-user/mips/target_signal.h +++ b/linux-user/mips/target_signal.h @@ -26,5 +26,12 @@ static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) return state->active_tc.gpr[29]; } - +# if defined(TARGET_ABI_MIPSO32) +/* compare linux/arch/mips/kernel/signal.c:setup_frame() */ +void setup_frame(int sig, struct target_sigaction * ka, + target_sigset_t *set, CPUMIPSState *regs); +#endif +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUMIPSState *env); #endif /* MIPS_TARGET_SIGNAL_H */ diff --git a/linux-user/mips64/signal.c b/linux-user/mips64/signal.c index 02ca338b6c..4ed0ed90b3 100644 --- a/linux-user/mips64/signal.c +++ b/linux-user/mips64/signal.c @@ -16,3 +16,5 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#define MIPS_TARGET_SIGNAL_H /* to only include mips64/target_signal.h */ +#include "../mips/signal.c" diff --git a/linux-user/mips64/target_signal.h b/linux-user/mips64/target_signal.h index 67ef5a18f4..70dfe40978 100644 --- a/linux-user/mips64/target_signal.h +++ b/linux-user/mips64/target_signal.h @@ -26,5 +26,7 @@ static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) return state->active_tc.gpr[29]; } - +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUMIPSState *env); #endif /* MIPS64_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 27c3769c5e..18acf6a4b0 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -803,386 +803,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, return ret; } -#if defined(TARGET_MIPS) || defined(TARGET_MIPS64) - -# if defined(TARGET_ABI_MIPSO32) -struct target_sigcontext { - uint32_t sc_regmask; /* Unused */ - uint32_t sc_status; - uint64_t sc_pc; - uint64_t sc_regs[32]; - uint64_t sc_fpregs[32]; - uint32_t sc_ownedfp; /* Unused */ - uint32_t sc_fpc_csr; - uint32_t sc_fpc_eir; /* Unused */ - uint32_t sc_used_math; - uint32_t sc_dsp; /* dsp status, was sc_ssflags */ - uint32_t pad0; - uint64_t sc_mdhi; - uint64_t sc_mdlo; - target_ulong sc_hi1; /* Was sc_cause */ - target_ulong sc_lo1; /* Was sc_badvaddr */ - target_ulong sc_hi2; /* Was sc_sigset[4] */ - target_ulong sc_lo2; - target_ulong sc_hi3; - target_ulong sc_lo3; -}; -# else /* N32 || N64 */ -struct target_sigcontext { - uint64_t sc_regs[32]; - uint64_t sc_fpregs[32]; - uint64_t sc_mdhi; - uint64_t sc_hi1; - uint64_t sc_hi2; - uint64_t sc_hi3; - uint64_t sc_mdlo; - uint64_t sc_lo1; - uint64_t sc_lo2; - uint64_t sc_lo3; - uint64_t sc_pc; - uint32_t sc_fpc_csr; - uint32_t sc_used_math; - uint32_t sc_dsp; - uint32_t sc_reserved; -}; -# endif /* O32 */ - -struct sigframe { - uint32_t sf_ass[4]; /* argument save space for o32 */ - uint32_t sf_code[2]; /* signal trampoline */ - struct target_sigcontext sf_sc; - target_sigset_t sf_mask; -}; - -struct target_ucontext { - target_ulong tuc_flags; - target_ulong tuc_link; - target_stack_t tuc_stack; - target_ulong pad0; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; -}; - -struct target_rt_sigframe { - uint32_t rs_ass[4]; /* argument save space for o32 */ - uint32_t rs_code[2]; /* signal trampoline */ - struct target_siginfo rs_info; - struct target_ucontext rs_uc; -}; - -/* Install trampoline to jump back from signal handler */ -static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall) -{ - int err = 0; - - /* - * Set up the return code ... - * - * li v0, __NR__foo_sigreturn - * syscall - */ - - __put_user(0x24020000 + syscall, tramp + 0); - __put_user(0x0000000c , tramp + 1); - return err; -} - -static inline void setup_sigcontext(CPUMIPSState *regs, - struct target_sigcontext *sc) -{ - int i; - - __put_user(exception_resume_pc(regs), &sc->sc_pc); - regs->hflags &= ~MIPS_HFLAG_BMASK; - - __put_user(0, &sc->sc_regs[0]); - for (i = 1; i < 32; ++i) { - __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); - } - - __put_user(regs->active_tc.HI[0], &sc->sc_mdhi); - __put_user(regs->active_tc.LO[0], &sc->sc_mdlo); - - /* Rather than checking for dsp existence, always copy. The storage - would just be garbage otherwise. */ - __put_user(regs->active_tc.HI[1], &sc->sc_hi1); - __put_user(regs->active_tc.HI[2], &sc->sc_hi2); - __put_user(regs->active_tc.HI[3], &sc->sc_hi3); - __put_user(regs->active_tc.LO[1], &sc->sc_lo1); - __put_user(regs->active_tc.LO[2], &sc->sc_lo2); - __put_user(regs->active_tc.LO[3], &sc->sc_lo3); - { - uint32_t dsp = cpu_rddsp(0x3ff, regs); - __put_user(dsp, &sc->sc_dsp); - } - - __put_user(1, &sc->sc_used_math); - - for (i = 0; i < 32; ++i) { - __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); - } -} - -static inline void -restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc) -{ - int i; - - __get_user(regs->CP0_EPC, &sc->sc_pc); - - __get_user(regs->active_tc.HI[0], &sc->sc_mdhi); - __get_user(regs->active_tc.LO[0], &sc->sc_mdlo); - - for (i = 1; i < 32; ++i) { - __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); - } - - __get_user(regs->active_tc.HI[1], &sc->sc_hi1); - __get_user(regs->active_tc.HI[2], &sc->sc_hi2); - __get_user(regs->active_tc.HI[3], &sc->sc_hi3); - __get_user(regs->active_tc.LO[1], &sc->sc_lo1); - __get_user(regs->active_tc.LO[2], &sc->sc_lo2); - __get_user(regs->active_tc.LO[3], &sc->sc_lo3); - { - uint32_t dsp; - __get_user(dsp, &sc->sc_dsp); - cpu_wrdsp(dsp, 0x3ff, regs); - } - - for (i = 0; i < 32; ++i) { - __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); - } -} - -/* - * Determine which stack to use.. - */ -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size) -{ - unsigned long sp; - - /* Default to using normal stack */ - sp = regs->active_tc.gpr[29]; - - /* - * FPU emulator may have its own trampoline active just - * above the user stack, 16-bytes before the next lowest - * 16 byte boundary. Try to avoid trashing it. - */ - sp -= 32; - - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - return (sp - frame_size) & ~7; -} - -static void mips_set_hflags_isa_mode_from_pc(CPUMIPSState *env) -{ - if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { - env->hflags &= ~MIPS_HFLAG_M16; - env->hflags |= (env->active_tc.PC & 1) << MIPS_HFLAG_M16_SHIFT; - env->active_tc.PC &= ~(target_ulong) 1; - } -} - -# if defined(TARGET_ABI_MIPSO32) -/* compare linux/arch/mips/kernel/signal.c:setup_frame() */ -static void setup_frame(int sig, struct target_sigaction * ka, - target_sigset_t *set, CPUMIPSState *regs) -{ - struct sigframe *frame; - abi_ulong frame_addr; - int i; - - frame_addr = get_sigframe(ka, regs, sizeof(*frame)); - trace_user_setup_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - install_sigtramp(frame->sf_code, TARGET_NR_sigreturn); - - setup_sigcontext(regs, &frame->sf_sc); - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->sf_mask.sig[i]); - } - - /* - * Arguments to signal handler: - * - * a0 = signal number - * a1 = 0 (should be cause) - * a2 = pointer to struct sigcontext - * - * $25 and PC point to the signal handler, $29 points to the - * struct sigframe. - */ - regs->active_tc.gpr[ 4] = sig; - regs->active_tc.gpr[ 5] = 0; - regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc); - regs->active_tc.gpr[29] = frame_addr; - regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code); - /* The original kernel code sets CP0_EPC to the handler - * since it returns to userland using eret - * we cannot do this here, and we must set PC directly */ - regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler; - mips_set_hflags_isa_mode_from_pc(regs); - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - force_sigsegv(sig); -} - -long do_sigreturn(CPUMIPSState *regs) -{ - struct sigframe *frame; - abi_ulong frame_addr; - sigset_t blocked; - target_sigset_t target_set; - int i; - - frame_addr = regs->active_tc.gpr[29]; - trace_user_do_sigreturn(regs, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->sf_mask.sig[i]); - } - - target_to_host_sigset_internal(&blocked, &target_set); - set_sigmask(&blocked); - - restore_sigcontext(regs, &frame->sf_sc); - -#if 0 - /* - * Don't let your children do this ... - */ - __asm__ __volatile__( - "move\t$29, %0\n\t" - "j\tsyscall_exit" - :/* no outputs */ - :"r" (®s)); - /* Unreached */ -#endif - - regs->active_tc.PC = regs->CP0_EPC; - mips_set_hflags_isa_mode_from_pc(regs); - /* I am not sure this is right, but it seems to work - * maybe a problem with nested signals ? */ - regs->CP0_EPC = 0; - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} -# endif /* O32 */ - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUMIPSState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - int i; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn); - - tswap_siginfo(&frame->rs_info, info); - - __put_user(0, &frame->rs_uc.tuc_flags); - __put_user(0, &frame->rs_uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->rs_uc.tuc_stack.ss_sp); - __put_user(target_sigaltstack_used.ss_size, &frame->rs_uc.tuc_stack.ss_size); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->rs_uc.tuc_stack.ss_flags); - - setup_sigcontext(env, &frame->rs_uc.tuc_mcontext); - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]); - } - - /* - * Arguments to signal handler: - * - * a0 = signal number - * a1 = pointer to siginfo_t - * a2 = pointer to ucontext_t - * - * $25 and PC point to the signal handler, $29 points to the - * struct sigframe. - */ - env->active_tc.gpr[ 4] = sig; - env->active_tc.gpr[ 5] = frame_addr - + offsetof(struct target_rt_sigframe, rs_info); - env->active_tc.gpr[ 6] = frame_addr - + offsetof(struct target_rt_sigframe, rs_uc); - env->active_tc.gpr[29] = frame_addr; - env->active_tc.gpr[31] = frame_addr - + offsetof(struct target_rt_sigframe, rs_code); - /* The original kernel code sets CP0_EPC to the handler - * since it returns to userland using eret - * we cannot do this here, and we must set PC directly */ - env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler; - mips_set_hflags_isa_mode_from_pc(env); - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} - -long do_rt_sigreturn(CPUMIPSState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - sigset_t blocked; - - frame_addr = env->active_tc.gpr[29]; - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); - set_sigmask(&blocked); - - restore_sigcontext(env, &frame->rs_uc.tuc_mcontext); - - if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, rs_uc.tuc_stack), - 0, get_sp_from_cpustate(env)) == -EFAULT) - goto badframe; - - env->active_tc.PC = env->CP0_EPC; - mips_set_hflags_isa_mode_from_pc(env); - /* I am not sure this is right, but it seems to work - * maybe a problem with nested signals ? */ - env->CP0_EPC = 0; - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#elif defined(TARGET_PPC) +#if defined(TARGET_PPC) /* Size of dummy stack frame allocated when calling signal handler. See arch/powerpc/include/asm/ptrace.h. */ From 9340eddae83f2e1398bbb7333feef51c53470579 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:34 +0200 Subject: [PATCH 0144/2380] linux-user: move ppc/ppc64 signal.c parts to ppc directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from signal.c to ppc/signal.c, except adding includes and exporting setup_frame() and setup_rt_frame(). Signed-off-by: Laurent Vivier Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-20-laurent@vivier.eu> --- linux-user/ppc/signal.c | 671 +++++++++++++++++++++++++++++++++ linux-user/ppc/target_signal.h | 8 +- linux-user/signal.c | 669 -------------------------------- 3 files changed, 678 insertions(+), 670 deletions(-) diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index 02ca338b6c..15148d54a9 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -16,3 +16,674 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "target_signal.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +/* Size of dummy stack frame allocated when calling signal handler. + See arch/powerpc/include/asm/ptrace.h. */ +#if defined(TARGET_PPC64) +#define SIGNAL_FRAMESIZE 128 +#else +#define SIGNAL_FRAMESIZE 64 +#endif + +/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; + on 64-bit PPC, sigcontext and mcontext are one and the same. */ +struct target_mcontext { + target_ulong mc_gregs[48]; + /* Includes fpscr. */ + uint64_t mc_fregs[33]; +#if defined(TARGET_PPC64) + /* Pointer to the vector regs */ + target_ulong v_regs; +#else + target_ulong mc_pad[2]; +#endif + /* We need to handle Altivec and SPE at the same time, which no + kernel needs to do. Fortunately, the kernel defines this bit to + be Altivec-register-large all the time, rather than trying to + twiddle it based on the specific platform. */ + union { + /* SPE vector registers. One extra for SPEFSCR. */ + uint32_t spe[33]; + /* Altivec vector registers. The packing of VSCR and VRSAVE + varies depending on whether we're PPC64 or not: PPC64 splits + them apart; PPC32 stuffs them together. + We also need to account for the VSX registers on PPC64 + */ +#if defined(TARGET_PPC64) +#define QEMU_NVRREG (34 + 16) + /* On ppc64, this mcontext structure is naturally *unaligned*, + * or rather it is aligned on a 8 bytes boundary but not on + * a 16 bytes one. This pad fixes it up. This is also why the + * vector regs are referenced by the v_regs pointer above so + * any amount of padding can be added here + */ + target_ulong pad; +#else + /* On ppc32, we are already aligned to 16 bytes */ +#define QEMU_NVRREG 33 +#endif + /* We cannot use ppc_avr_t here as we do *not* want the implied + * 16-bytes alignment that would result from it. This would have + * the effect of making the whole struct target_mcontext aligned + * which breaks the layout of struct target_ucontext on ppc64. + */ + uint64_t altivec[QEMU_NVRREG][2]; +#undef QEMU_NVRREG + } mc_vregs; +}; + +/* See arch/powerpc/include/asm/sigcontext.h. */ +struct target_sigcontext { + target_ulong _unused[4]; + int32_t signal; +#if defined(TARGET_PPC64) + int32_t pad0; +#endif + target_ulong handler; + target_ulong oldmask; + target_ulong regs; /* struct pt_regs __user * */ +#if defined(TARGET_PPC64) + struct target_mcontext mcontext; +#endif +}; + +/* Indices for target_mcontext.mc_gregs, below. + See arch/powerpc/include/asm/ptrace.h for details. */ +enum { + TARGET_PT_R0 = 0, + TARGET_PT_R1 = 1, + TARGET_PT_R2 = 2, + TARGET_PT_R3 = 3, + TARGET_PT_R4 = 4, + TARGET_PT_R5 = 5, + TARGET_PT_R6 = 6, + TARGET_PT_R7 = 7, + TARGET_PT_R8 = 8, + TARGET_PT_R9 = 9, + TARGET_PT_R10 = 10, + TARGET_PT_R11 = 11, + TARGET_PT_R12 = 12, + TARGET_PT_R13 = 13, + TARGET_PT_R14 = 14, + TARGET_PT_R15 = 15, + TARGET_PT_R16 = 16, + TARGET_PT_R17 = 17, + TARGET_PT_R18 = 18, + TARGET_PT_R19 = 19, + TARGET_PT_R20 = 20, + TARGET_PT_R21 = 21, + TARGET_PT_R22 = 22, + TARGET_PT_R23 = 23, + TARGET_PT_R24 = 24, + TARGET_PT_R25 = 25, + TARGET_PT_R26 = 26, + TARGET_PT_R27 = 27, + TARGET_PT_R28 = 28, + TARGET_PT_R29 = 29, + TARGET_PT_R30 = 30, + TARGET_PT_R31 = 31, + TARGET_PT_NIP = 32, + TARGET_PT_MSR = 33, + TARGET_PT_ORIG_R3 = 34, + TARGET_PT_CTR = 35, + TARGET_PT_LNK = 36, + TARGET_PT_XER = 37, + TARGET_PT_CCR = 38, + /* Yes, there are two registers with #39. One is 64-bit only. */ + TARGET_PT_MQ = 39, + TARGET_PT_SOFTE = 39, + TARGET_PT_TRAP = 40, + TARGET_PT_DAR = 41, + TARGET_PT_DSISR = 42, + TARGET_PT_RESULT = 43, + TARGET_PT_REGS_COUNT = 44 +}; + + +struct target_ucontext { + target_ulong tuc_flags; + target_ulong tuc_link; /* ucontext_t __user * */ + struct target_sigaltstack tuc_stack; +#if !defined(TARGET_PPC64) + int32_t tuc_pad[7]; + target_ulong tuc_regs; /* struct mcontext __user * + points to uc_mcontext field */ +#endif + target_sigset_t tuc_sigmask; +#if defined(TARGET_PPC64) + target_sigset_t unused[15]; /* Allow for uc_sigmask growth */ + struct target_sigcontext tuc_sigcontext; +#else + int32_t tuc_maskext[30]; + int32_t tuc_pad2[3]; + struct target_mcontext tuc_mcontext; +#endif +}; + +/* See arch/powerpc/kernel/signal_32.c. */ +struct target_sigframe { + struct target_sigcontext sctx; + struct target_mcontext mctx; + int32_t abigap[56]; +}; + +#if defined(TARGET_PPC64) + +#define TARGET_TRAMP_SIZE 6 + +struct target_rt_sigframe { + /* sys_rt_sigreturn requires the ucontext be the first field */ + struct target_ucontext uc; + target_ulong _unused[2]; + uint32_t trampoline[TARGET_TRAMP_SIZE]; + target_ulong pinfo; /* struct siginfo __user * */ + target_ulong puc; /* void __user * */ + struct target_siginfo info; + /* 64 bit ABI allows for 288 bytes below sp before decrementing it. */ + char abigap[288]; +} __attribute__((aligned(16))); + +#else + +struct target_rt_sigframe { + struct target_siginfo info; + struct target_ucontext uc; + int32_t abigap[56]; +}; + +#endif + +#if defined(TARGET_PPC64) + +struct target_func_ptr { + target_ulong entry; + target_ulong toc; +}; + +#endif + +/* We use the mc_pad field for the signal return trampoline. */ +#define tramp mc_pad + +/* See arch/powerpc/kernel/signal.c. */ +static target_ulong get_sigframe(struct target_sigaction *ka, + CPUPPCState *env, + int frame_size) +{ + target_ulong oldsp; + + oldsp = env->gpr[1]; + + if ((ka->sa_flags & TARGET_SA_ONSTACK) && + (sas_ss_flags(oldsp) == 0)) { + oldsp = (target_sigaltstack_used.ss_sp + + target_sigaltstack_used.ss_size); + } + + return (oldsp - frame_size) & ~0xFUL; +} + +#if ((defined(TARGET_WORDS_BIGENDIAN) && defined(HOST_WORDS_BIGENDIAN)) || \ + (!defined(HOST_WORDS_BIGENDIAN) && !defined(TARGET_WORDS_BIGENDIAN))) +#define PPC_VEC_HI 0 +#define PPC_VEC_LO 1 +#else +#define PPC_VEC_HI 1 +#define PPC_VEC_LO 0 +#endif + + +static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) +{ + target_ulong msr = env->msr; + int i; + target_ulong ccr = 0; + + /* In general, the kernel attempts to be intelligent about what it + needs to save for Altivec/FP/SPE registers. We don't care that + much, so we just go ahead and save everything. */ + + /* Save general registers. */ + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + __put_user(env->gpr[i], &frame->mc_gregs[i]); + } + __put_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); + __put_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); + __put_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); + __put_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); + + for (i = 0; i < ARRAY_SIZE(env->crf); i++) { + ccr |= env->crf[i] << (32 - ((i + 1) * 4)); + } + __put_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); + + /* Save Altivec registers if necessary. */ + if (env->insns_flags & PPC_ALTIVEC) { + uint32_t *vrsave; + for (i = 0; i < ARRAY_SIZE(env->avr); i++) { + ppc_avr_t *avr = &env->avr[i]; + ppc_avr_t *vreg = (ppc_avr_t *)&frame->mc_vregs.altivec[i]; + + __put_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]); + __put_user(avr->u64[PPC_VEC_LO], &vreg->u64[1]); + } + /* Set MSR_VR in the saved MSR value to indicate that + frame->mc_vregs contains valid data. */ + msr |= MSR_VR; +#if defined(TARGET_PPC64) + vrsave = (uint32_t *)&frame->mc_vregs.altivec[33]; + /* 64-bit needs to put a pointer to the vectors in the frame */ + __put_user(h2g(frame->mc_vregs.altivec), &frame->v_regs); +#else + vrsave = (uint32_t *)&frame->mc_vregs.altivec[32]; +#endif + __put_user((uint32_t)env->spr[SPR_VRSAVE], vrsave); + } + + /* Save VSX second halves */ + if (env->insns_flags2 & PPC2_VSX) { + uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; + for (i = 0; i < ARRAY_SIZE(env->vsr); i++) { + __put_user(env->vsr[i], &vsregs[i]); + } + } + + /* Save floating point registers. */ + if (env->insns_flags & PPC_FLOAT) { + for (i = 0; i < ARRAY_SIZE(env->fpr); i++) { + __put_user(env->fpr[i], &frame->mc_fregs[i]); + } + __put_user((uint64_t) env->fpscr, &frame->mc_fregs[32]); + } + + /* Save SPE registers. The kernel only saves the high half. */ + if (env->insns_flags & PPC_SPE) { +#if defined(TARGET_PPC64) + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + __put_user(env->gpr[i] >> 32, &frame->mc_vregs.spe[i]); + } +#else + for (i = 0; i < ARRAY_SIZE(env->gprh); i++) { + __put_user(env->gprh[i], &frame->mc_vregs.spe[i]); + } +#endif + /* Set MSR_SPE in the saved MSR value to indicate that + frame->mc_vregs contains valid data. */ + msr |= MSR_SPE; + __put_user(env->spe_fscr, &frame->mc_vregs.spe[32]); + } + + /* Store MSR. */ + __put_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); +} + +static void encode_trampoline(int sigret, uint32_t *tramp) +{ + /* Set up the sigreturn trampoline: li r0,sigret; sc. */ + if (sigret) { + __put_user(0x38000000 | sigret, &tramp[0]); + __put_user(0x44000002, &tramp[1]); + } +} + +static void restore_user_regs(CPUPPCState *env, + struct target_mcontext *frame, int sig) +{ + target_ulong save_r2 = 0; + target_ulong msr; + target_ulong ccr; + + int i; + + if (!sig) { + save_r2 = env->gpr[2]; + } + + /* Restore general registers. */ + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + __get_user(env->gpr[i], &frame->mc_gregs[i]); + } + __get_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); + __get_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); + __get_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); + __get_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); + __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); + + for (i = 0; i < ARRAY_SIZE(env->crf); i++) { + env->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf; + } + + if (!sig) { + env->gpr[2] = save_r2; + } + /* Restore MSR. */ + __get_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); + + /* If doing signal return, restore the previous little-endian mode. */ + if (sig) + env->msr = (env->msr & ~(1ull << MSR_LE)) | (msr & (1ull << MSR_LE)); + + /* Restore Altivec registers if necessary. */ + if (env->insns_flags & PPC_ALTIVEC) { + ppc_avr_t *v_regs; + uint32_t *vrsave; +#if defined(TARGET_PPC64) + uint64_t v_addr; + /* 64-bit needs to recover the pointer to the vectors from the frame */ + __get_user(v_addr, &frame->v_regs); + v_regs = g2h(v_addr); +#else + v_regs = (ppc_avr_t *)frame->mc_vregs.altivec; +#endif + for (i = 0; i < ARRAY_SIZE(env->avr); i++) { + ppc_avr_t *avr = &env->avr[i]; + ppc_avr_t *vreg = &v_regs[i]; + + __get_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]); + __get_user(avr->u64[PPC_VEC_LO], &vreg->u64[1]); + } + /* Set MSR_VEC in the saved MSR value to indicate that + frame->mc_vregs contains valid data. */ +#if defined(TARGET_PPC64) + vrsave = (uint32_t *)&v_regs[33]; +#else + vrsave = (uint32_t *)&v_regs[32]; +#endif + __get_user(env->spr[SPR_VRSAVE], vrsave); + } + + /* Restore VSX second halves */ + if (env->insns_flags2 & PPC2_VSX) { + uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; + for (i = 0; i < ARRAY_SIZE(env->vsr); i++) { + __get_user(env->vsr[i], &vsregs[i]); + } + } + + /* Restore floating point registers. */ + if (env->insns_flags & PPC_FLOAT) { + uint64_t fpscr; + for (i = 0; i < ARRAY_SIZE(env->fpr); i++) { + __get_user(env->fpr[i], &frame->mc_fregs[i]); + } + __get_user(fpscr, &frame->mc_fregs[32]); + env->fpscr = (uint32_t) fpscr; + } + + /* Save SPE registers. The kernel only saves the high half. */ + if (env->insns_flags & PPC_SPE) { +#if defined(TARGET_PPC64) + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + uint32_t hi; + + __get_user(hi, &frame->mc_vregs.spe[i]); + env->gpr[i] = ((uint64_t)hi << 32) | ((uint32_t) env->gpr[i]); + } +#else + for (i = 0; i < ARRAY_SIZE(env->gprh); i++) { + __get_user(env->gprh[i], &frame->mc_vregs.spe[i]); + } +#endif + __get_user(env->spe_fscr, &frame->mc_vregs.spe[32]); + } +} + +#if !defined(TARGET_PPC64) +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUPPCState *env) +{ + struct target_sigframe *frame; + struct target_sigcontext *sc; + target_ulong frame_addr, newsp; + int err = 0; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) + goto sigsegv; + sc = &frame->sctx; + + __put_user(ka->_sa_handler, &sc->handler); + __put_user(set->sig[0], &sc->oldmask); + __put_user(set->sig[1], &sc->_unused[3]); + __put_user(h2g(&frame->mctx), &sc->regs); + __put_user(sig, &sc->signal); + + /* Save user regs. */ + save_user_regs(env, &frame->mctx); + + /* Construct the trampoline code on the stack. */ + encode_trampoline(TARGET_NR_sigreturn, (uint32_t *)&frame->mctx.tramp); + + /* The kernel checks for the presence of a VDSO here. We don't + emulate a vdso, so use a sigreturn system call. */ + env->lr = (target_ulong) h2g(frame->mctx.tramp); + + /* Turn off all fp exceptions. */ + env->fpscr = 0; + + /* Create a stack frame for the caller of the handler. */ + newsp = frame_addr - SIGNAL_FRAMESIZE; + err |= put_user(env->gpr[1], newsp, target_ulong); + + if (err) + goto sigsegv; + + /* Set up registers for signal handler. */ + env->gpr[1] = newsp; + env->gpr[3] = sig; + env->gpr[4] = frame_addr + offsetof(struct target_sigframe, sctx); + + env->nip = (target_ulong) ka->_sa_handler; + + /* Signal handlers are entered in big-endian mode. */ + env->msr &= ~(1ull << MSR_LE); + + unlock_user_struct(frame, frame_addr, 1); + return; + +sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} +#endif /* !defined(TARGET_PPC64) */ + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUPPCState *env) +{ + struct target_rt_sigframe *rt_sf; + uint32_t *trampptr = 0; + struct target_mcontext *mctx = 0; + target_ulong rt_sf_addr, newsp = 0; + int i, err = 0; +#if defined(TARGET_PPC64) + struct target_sigcontext *sc = 0; + struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; +#endif + + rt_sf_addr = get_sigframe(ka, env, sizeof(*rt_sf)); + if (!lock_user_struct(VERIFY_WRITE, rt_sf, rt_sf_addr, 1)) + goto sigsegv; + + tswap_siginfo(&rt_sf->info, info); + + __put_user(0, &rt_sf->uc.tuc_flags); + __put_user(0, &rt_sf->uc.tuc_link); + __put_user((target_ulong)target_sigaltstack_used.ss_sp, + &rt_sf->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(env->gpr[1]), + &rt_sf->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, + &rt_sf->uc.tuc_stack.ss_size); +#if !defined(TARGET_PPC64) + __put_user(h2g (&rt_sf->uc.tuc_mcontext), + &rt_sf->uc.tuc_regs); +#endif + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &rt_sf->uc.tuc_sigmask.sig[i]); + } + +#if defined(TARGET_PPC64) + mctx = &rt_sf->uc.tuc_sigcontext.mcontext; + trampptr = &rt_sf->trampoline[0]; + + sc = &rt_sf->uc.tuc_sigcontext; + __put_user(h2g(mctx), &sc->regs); + __put_user(sig, &sc->signal); +#else + mctx = &rt_sf->uc.tuc_mcontext; + trampptr = (uint32_t *)&rt_sf->uc.tuc_mcontext.tramp; +#endif + + save_user_regs(env, mctx); + encode_trampoline(TARGET_NR_rt_sigreturn, trampptr); + + /* The kernel checks for the presence of a VDSO here. We don't + emulate a vdso, so use a sigreturn system call. */ + env->lr = (target_ulong) h2g(trampptr); + + /* Turn off all fp exceptions. */ + env->fpscr = 0; + + /* Create a stack frame for the caller of the handler. */ + newsp = rt_sf_addr - (SIGNAL_FRAMESIZE + 16); + err |= put_user(env->gpr[1], newsp, target_ulong); + + if (err) + goto sigsegv; + + /* Set up registers for signal handler. */ + env->gpr[1] = newsp; + env->gpr[3] = (target_ulong) sig; + env->gpr[4] = (target_ulong) h2g(&rt_sf->info); + env->gpr[5] = (target_ulong) h2g(&rt_sf->uc); + env->gpr[6] = (target_ulong) h2g(rt_sf); + +#if defined(TARGET_PPC64) + if (get_ppc64_abi(image) < 2) { + /* ELFv1 PPC64 function pointers are pointers to OPD entries. */ + struct target_func_ptr *handler = + (struct target_func_ptr *)g2h(ka->_sa_handler); + env->nip = tswapl(handler->entry); + env->gpr[2] = tswapl(handler->toc); + } else { + /* ELFv2 PPC64 function pointers are entry points, but R12 + * must also be set */ + env->nip = tswapl((target_ulong) ka->_sa_handler); + env->gpr[12] = env->nip; + } +#else + env->nip = (target_ulong) ka->_sa_handler; +#endif + + /* Signal handlers are entered in big-endian mode. */ + env->msr &= ~(1ull << MSR_LE); + + unlock_user_struct(rt_sf, rt_sf_addr, 1); + return; + +sigsegv: + unlock_user_struct(rt_sf, rt_sf_addr, 1); + force_sigsegv(sig); + +} + +#if !defined(TARGET_PPC64) +long do_sigreturn(CPUPPCState *env) +{ + struct target_sigcontext *sc = NULL; + struct target_mcontext *sr = NULL; + target_ulong sr_addr = 0, sc_addr; + sigset_t blocked; + target_sigset_t set; + + sc_addr = env->gpr[1] + SIGNAL_FRAMESIZE; + if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) + goto sigsegv; + +#if defined(TARGET_PPC64) + set.sig[0] = sc->oldmask + ((uint64_t)(sc->_unused[3]) << 32); +#else + __get_user(set.sig[0], &sc->oldmask); + __get_user(set.sig[1], &sc->_unused[3]); +#endif + target_to_host_sigset_internal(&blocked, &set); + set_sigmask(&blocked); + + __get_user(sr_addr, &sc->regs); + if (!lock_user_struct(VERIFY_READ, sr, sr_addr, 1)) + goto sigsegv; + restore_user_regs(env, sr, 1); + + unlock_user_struct(sr, sr_addr, 1); + unlock_user_struct(sc, sc_addr, 1); + return -TARGET_QEMU_ESIGRETURN; + +sigsegv: + unlock_user_struct(sr, sr_addr, 1); + unlock_user_struct(sc, sc_addr, 1); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} +#endif /* !defined(TARGET_PPC64) */ + +/* See arch/powerpc/kernel/signal_32.c. */ +static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) +{ + struct target_mcontext *mcp; + target_ulong mcp_addr; + sigset_t blocked; + target_sigset_t set; + + if (copy_from_user(&set, h2g(ucp) + offsetof(struct target_ucontext, tuc_sigmask), + sizeof (set))) + return 1; + +#if defined(TARGET_PPC64) + mcp_addr = h2g(ucp) + + offsetof(struct target_ucontext, tuc_sigcontext.mcontext); +#else + __get_user(mcp_addr, &ucp->tuc_regs); +#endif + + if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1)) + return 1; + + target_to_host_sigset_internal(&blocked, &set); + set_sigmask(&blocked); + restore_user_regs(env, mcp, sig); + + unlock_user_struct(mcp, mcp_addr, 1); + return 0; +} + +long do_rt_sigreturn(CPUPPCState *env) +{ + struct target_rt_sigframe *rt_sf = NULL; + target_ulong rt_sf_addr; + + rt_sf_addr = env->gpr[1] + SIGNAL_FRAMESIZE + 16; + if (!lock_user_struct(VERIFY_READ, rt_sf, rt_sf_addr, 1)) + goto sigsegv; + + if (do_setcontext(&rt_sf->uc, env, 1)) + goto sigsegv; + + do_sigaltstack(rt_sf_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, env->gpr[1]); + + unlock_user_struct(rt_sf, rt_sf_addr, 1); + return -TARGET_QEMU_ESIGRETURN; + +sigsegv: + unlock_user_struct(rt_sf, rt_sf_addr, 1); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/ppc/target_signal.h b/linux-user/ppc/target_signal.h index 865c52f3e8..5e293e3b03 100644 --- a/linux-user/ppc/target_signal.h +++ b/linux-user/ppc/target_signal.h @@ -26,5 +26,11 @@ static inline abi_ulong get_sp_from_cpustate(CPUPPCState *state) return state->gpr[1]; } - +#if !defined(TARGET_PPC64) +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUPPCState *env); +#endif +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUPPCState *env); #endif /* PPC_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 18acf6a4b0..7f435c4606 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -803,675 +803,6 @@ int do_sigaction(int sig, const struct target_sigaction *act, return ret; } -#if defined(TARGET_PPC) - -/* Size of dummy stack frame allocated when calling signal handler. - See arch/powerpc/include/asm/ptrace.h. */ -#if defined(TARGET_PPC64) -#define SIGNAL_FRAMESIZE 128 -#else -#define SIGNAL_FRAMESIZE 64 -#endif - -/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; - on 64-bit PPC, sigcontext and mcontext are one and the same. */ -struct target_mcontext { - target_ulong mc_gregs[48]; - /* Includes fpscr. */ - uint64_t mc_fregs[33]; -#if defined(TARGET_PPC64) - /* Pointer to the vector regs */ - target_ulong v_regs; -#else - target_ulong mc_pad[2]; -#endif - /* We need to handle Altivec and SPE at the same time, which no - kernel needs to do. Fortunately, the kernel defines this bit to - be Altivec-register-large all the time, rather than trying to - twiddle it based on the specific platform. */ - union { - /* SPE vector registers. One extra for SPEFSCR. */ - uint32_t spe[33]; - /* Altivec vector registers. The packing of VSCR and VRSAVE - varies depending on whether we're PPC64 or not: PPC64 splits - them apart; PPC32 stuffs them together. - We also need to account for the VSX registers on PPC64 - */ -#if defined(TARGET_PPC64) -#define QEMU_NVRREG (34 + 16) - /* On ppc64, this mcontext structure is naturally *unaligned*, - * or rather it is aligned on a 8 bytes boundary but not on - * a 16 bytes one. This pad fixes it up. This is also why the - * vector regs are referenced by the v_regs pointer above so - * any amount of padding can be added here - */ - target_ulong pad; -#else - /* On ppc32, we are already aligned to 16 bytes */ -#define QEMU_NVRREG 33 -#endif - /* We cannot use ppc_avr_t here as we do *not* want the implied - * 16-bytes alignment that would result from it. This would have - * the effect of making the whole struct target_mcontext aligned - * which breaks the layout of struct target_ucontext on ppc64. - */ - uint64_t altivec[QEMU_NVRREG][2]; -#undef QEMU_NVRREG - } mc_vregs; -}; - -/* See arch/powerpc/include/asm/sigcontext.h. */ -struct target_sigcontext { - target_ulong _unused[4]; - int32_t signal; -#if defined(TARGET_PPC64) - int32_t pad0; -#endif - target_ulong handler; - target_ulong oldmask; - target_ulong regs; /* struct pt_regs __user * */ -#if defined(TARGET_PPC64) - struct target_mcontext mcontext; -#endif -}; - -/* Indices for target_mcontext.mc_gregs, below. - See arch/powerpc/include/asm/ptrace.h for details. */ -enum { - TARGET_PT_R0 = 0, - TARGET_PT_R1 = 1, - TARGET_PT_R2 = 2, - TARGET_PT_R3 = 3, - TARGET_PT_R4 = 4, - TARGET_PT_R5 = 5, - TARGET_PT_R6 = 6, - TARGET_PT_R7 = 7, - TARGET_PT_R8 = 8, - TARGET_PT_R9 = 9, - TARGET_PT_R10 = 10, - TARGET_PT_R11 = 11, - TARGET_PT_R12 = 12, - TARGET_PT_R13 = 13, - TARGET_PT_R14 = 14, - TARGET_PT_R15 = 15, - TARGET_PT_R16 = 16, - TARGET_PT_R17 = 17, - TARGET_PT_R18 = 18, - TARGET_PT_R19 = 19, - TARGET_PT_R20 = 20, - TARGET_PT_R21 = 21, - TARGET_PT_R22 = 22, - TARGET_PT_R23 = 23, - TARGET_PT_R24 = 24, - TARGET_PT_R25 = 25, - TARGET_PT_R26 = 26, - TARGET_PT_R27 = 27, - TARGET_PT_R28 = 28, - TARGET_PT_R29 = 29, - TARGET_PT_R30 = 30, - TARGET_PT_R31 = 31, - TARGET_PT_NIP = 32, - TARGET_PT_MSR = 33, - TARGET_PT_ORIG_R3 = 34, - TARGET_PT_CTR = 35, - TARGET_PT_LNK = 36, - TARGET_PT_XER = 37, - TARGET_PT_CCR = 38, - /* Yes, there are two registers with #39. One is 64-bit only. */ - TARGET_PT_MQ = 39, - TARGET_PT_SOFTE = 39, - TARGET_PT_TRAP = 40, - TARGET_PT_DAR = 41, - TARGET_PT_DSISR = 42, - TARGET_PT_RESULT = 43, - TARGET_PT_REGS_COUNT = 44 -}; - - -struct target_ucontext { - target_ulong tuc_flags; - target_ulong tuc_link; /* ucontext_t __user * */ - struct target_sigaltstack tuc_stack; -#if !defined(TARGET_PPC64) - int32_t tuc_pad[7]; - target_ulong tuc_regs; /* struct mcontext __user * - points to uc_mcontext field */ -#endif - target_sigset_t tuc_sigmask; -#if defined(TARGET_PPC64) - target_sigset_t unused[15]; /* Allow for uc_sigmask growth */ - struct target_sigcontext tuc_sigcontext; -#else - int32_t tuc_maskext[30]; - int32_t tuc_pad2[3]; - struct target_mcontext tuc_mcontext; -#endif -}; - -/* See arch/powerpc/kernel/signal_32.c. */ -struct target_sigframe { - struct target_sigcontext sctx; - struct target_mcontext mctx; - int32_t abigap[56]; -}; - -#if defined(TARGET_PPC64) - -#define TARGET_TRAMP_SIZE 6 - -struct target_rt_sigframe { - /* sys_rt_sigreturn requires the ucontext be the first field */ - struct target_ucontext uc; - target_ulong _unused[2]; - uint32_t trampoline[TARGET_TRAMP_SIZE]; - target_ulong pinfo; /* struct siginfo __user * */ - target_ulong puc; /* void __user * */ - struct target_siginfo info; - /* 64 bit ABI allows for 288 bytes below sp before decrementing it. */ - char abigap[288]; -} __attribute__((aligned(16))); - -#else - -struct target_rt_sigframe { - struct target_siginfo info; - struct target_ucontext uc; - int32_t abigap[56]; -}; - -#endif - -#if defined(TARGET_PPC64) - -struct target_func_ptr { - target_ulong entry; - target_ulong toc; -}; - -#endif - -/* We use the mc_pad field for the signal return trampoline. */ -#define tramp mc_pad - -/* See arch/powerpc/kernel/signal.c. */ -static target_ulong get_sigframe(struct target_sigaction *ka, - CPUPPCState *env, - int frame_size) -{ - target_ulong oldsp; - - oldsp = env->gpr[1]; - - if ((ka->sa_flags & TARGET_SA_ONSTACK) && - (sas_ss_flags(oldsp) == 0)) { - oldsp = (target_sigaltstack_used.ss_sp - + target_sigaltstack_used.ss_size); - } - - return (oldsp - frame_size) & ~0xFUL; -} - -#if ((defined(TARGET_WORDS_BIGENDIAN) && defined(HOST_WORDS_BIGENDIAN)) || \ - (!defined(HOST_WORDS_BIGENDIAN) && !defined(TARGET_WORDS_BIGENDIAN))) -#define PPC_VEC_HI 0 -#define PPC_VEC_LO 1 -#else -#define PPC_VEC_HI 1 -#define PPC_VEC_LO 0 -#endif - - -static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) -{ - target_ulong msr = env->msr; - int i; - target_ulong ccr = 0; - - /* In general, the kernel attempts to be intelligent about what it - needs to save for Altivec/FP/SPE registers. We don't care that - much, so we just go ahead and save everything. */ - - /* Save general registers. */ - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - __put_user(env->gpr[i], &frame->mc_gregs[i]); - } - __put_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); - __put_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); - __put_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); - __put_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); - - for (i = 0; i < ARRAY_SIZE(env->crf); i++) { - ccr |= env->crf[i] << (32 - ((i + 1) * 4)); - } - __put_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); - - /* Save Altivec registers if necessary. */ - if (env->insns_flags & PPC_ALTIVEC) { - uint32_t *vrsave; - for (i = 0; i < ARRAY_SIZE(env->avr); i++) { - ppc_avr_t *avr = &env->avr[i]; - ppc_avr_t *vreg = (ppc_avr_t *)&frame->mc_vregs.altivec[i]; - - __put_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]); - __put_user(avr->u64[PPC_VEC_LO], &vreg->u64[1]); - } - /* Set MSR_VR in the saved MSR value to indicate that - frame->mc_vregs contains valid data. */ - msr |= MSR_VR; -#if defined(TARGET_PPC64) - vrsave = (uint32_t *)&frame->mc_vregs.altivec[33]; - /* 64-bit needs to put a pointer to the vectors in the frame */ - __put_user(h2g(frame->mc_vregs.altivec), &frame->v_regs); -#else - vrsave = (uint32_t *)&frame->mc_vregs.altivec[32]; -#endif - __put_user((uint32_t)env->spr[SPR_VRSAVE], vrsave); - } - - /* Save VSX second halves */ - if (env->insns_flags2 & PPC2_VSX) { - uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; - for (i = 0; i < ARRAY_SIZE(env->vsr); i++) { - __put_user(env->vsr[i], &vsregs[i]); - } - } - - /* Save floating point registers. */ - if (env->insns_flags & PPC_FLOAT) { - for (i = 0; i < ARRAY_SIZE(env->fpr); i++) { - __put_user(env->fpr[i], &frame->mc_fregs[i]); - } - __put_user((uint64_t) env->fpscr, &frame->mc_fregs[32]); - } - - /* Save SPE registers. The kernel only saves the high half. */ - if (env->insns_flags & PPC_SPE) { -#if defined(TARGET_PPC64) - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - __put_user(env->gpr[i] >> 32, &frame->mc_vregs.spe[i]); - } -#else - for (i = 0; i < ARRAY_SIZE(env->gprh); i++) { - __put_user(env->gprh[i], &frame->mc_vregs.spe[i]); - } -#endif - /* Set MSR_SPE in the saved MSR value to indicate that - frame->mc_vregs contains valid data. */ - msr |= MSR_SPE; - __put_user(env->spe_fscr, &frame->mc_vregs.spe[32]); - } - - /* Store MSR. */ - __put_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); -} - -static void encode_trampoline(int sigret, uint32_t *tramp) -{ - /* Set up the sigreturn trampoline: li r0,sigret; sc. */ - if (sigret) { - __put_user(0x38000000 | sigret, &tramp[0]); - __put_user(0x44000002, &tramp[1]); - } -} - -static void restore_user_regs(CPUPPCState *env, - struct target_mcontext *frame, int sig) -{ - target_ulong save_r2 = 0; - target_ulong msr; - target_ulong ccr; - - int i; - - if (!sig) { - save_r2 = env->gpr[2]; - } - - /* Restore general registers. */ - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - __get_user(env->gpr[i], &frame->mc_gregs[i]); - } - __get_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); - __get_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); - __get_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); - __get_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); - __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); - - for (i = 0; i < ARRAY_SIZE(env->crf); i++) { - env->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf; - } - - if (!sig) { - env->gpr[2] = save_r2; - } - /* Restore MSR. */ - __get_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); - - /* If doing signal return, restore the previous little-endian mode. */ - if (sig) - env->msr = (env->msr & ~(1ull << MSR_LE)) | (msr & (1ull << MSR_LE)); - - /* Restore Altivec registers if necessary. */ - if (env->insns_flags & PPC_ALTIVEC) { - ppc_avr_t *v_regs; - uint32_t *vrsave; -#if defined(TARGET_PPC64) - uint64_t v_addr; - /* 64-bit needs to recover the pointer to the vectors from the frame */ - __get_user(v_addr, &frame->v_regs); - v_regs = g2h(v_addr); -#else - v_regs = (ppc_avr_t *)frame->mc_vregs.altivec; -#endif - for (i = 0; i < ARRAY_SIZE(env->avr); i++) { - ppc_avr_t *avr = &env->avr[i]; - ppc_avr_t *vreg = &v_regs[i]; - - __get_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]); - __get_user(avr->u64[PPC_VEC_LO], &vreg->u64[1]); - } - /* Set MSR_VEC in the saved MSR value to indicate that - frame->mc_vregs contains valid data. */ -#if defined(TARGET_PPC64) - vrsave = (uint32_t *)&v_regs[33]; -#else - vrsave = (uint32_t *)&v_regs[32]; -#endif - __get_user(env->spr[SPR_VRSAVE], vrsave); - } - - /* Restore VSX second halves */ - if (env->insns_flags2 & PPC2_VSX) { - uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; - for (i = 0; i < ARRAY_SIZE(env->vsr); i++) { - __get_user(env->vsr[i], &vsregs[i]); - } - } - - /* Restore floating point registers. */ - if (env->insns_flags & PPC_FLOAT) { - uint64_t fpscr; - for (i = 0; i < ARRAY_SIZE(env->fpr); i++) { - __get_user(env->fpr[i], &frame->mc_fregs[i]); - } - __get_user(fpscr, &frame->mc_fregs[32]); - env->fpscr = (uint32_t) fpscr; - } - - /* Save SPE registers. The kernel only saves the high half. */ - if (env->insns_flags & PPC_SPE) { -#if defined(TARGET_PPC64) - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - uint32_t hi; - - __get_user(hi, &frame->mc_vregs.spe[i]); - env->gpr[i] = ((uint64_t)hi << 32) | ((uint32_t) env->gpr[i]); - } -#else - for (i = 0; i < ARRAY_SIZE(env->gprh); i++) { - __get_user(env->gprh[i], &frame->mc_vregs.spe[i]); - } -#endif - __get_user(env->spe_fscr, &frame->mc_vregs.spe[32]); - } -} - -#if !defined(TARGET_PPC64) -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUPPCState *env) -{ - struct target_sigframe *frame; - struct target_sigcontext *sc; - target_ulong frame_addr, newsp; - int err = 0; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) - goto sigsegv; - sc = &frame->sctx; - - __put_user(ka->_sa_handler, &sc->handler); - __put_user(set->sig[0], &sc->oldmask); - __put_user(set->sig[1], &sc->_unused[3]); - __put_user(h2g(&frame->mctx), &sc->regs); - __put_user(sig, &sc->signal); - - /* Save user regs. */ - save_user_regs(env, &frame->mctx); - - /* Construct the trampoline code on the stack. */ - encode_trampoline(TARGET_NR_sigreturn, (uint32_t *)&frame->mctx.tramp); - - /* The kernel checks for the presence of a VDSO here. We don't - emulate a vdso, so use a sigreturn system call. */ - env->lr = (target_ulong) h2g(frame->mctx.tramp); - - /* Turn off all fp exceptions. */ - env->fpscr = 0; - - /* Create a stack frame for the caller of the handler. */ - newsp = frame_addr - SIGNAL_FRAMESIZE; - err |= put_user(env->gpr[1], newsp, target_ulong); - - if (err) - goto sigsegv; - - /* Set up registers for signal handler. */ - env->gpr[1] = newsp; - env->gpr[3] = sig; - env->gpr[4] = frame_addr + offsetof(struct target_sigframe, sctx); - - env->nip = (target_ulong) ka->_sa_handler; - - /* Signal handlers are entered in big-endian mode. */ - env->msr &= ~(1ull << MSR_LE); - - unlock_user_struct(frame, frame_addr, 1); - return; - -sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} -#endif /* !defined(TARGET_PPC64) */ - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUPPCState *env) -{ - struct target_rt_sigframe *rt_sf; - uint32_t *trampptr = 0; - struct target_mcontext *mctx = 0; - target_ulong rt_sf_addr, newsp = 0; - int i, err = 0; -#if defined(TARGET_PPC64) - struct target_sigcontext *sc = 0; - struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; -#endif - - rt_sf_addr = get_sigframe(ka, env, sizeof(*rt_sf)); - if (!lock_user_struct(VERIFY_WRITE, rt_sf, rt_sf_addr, 1)) - goto sigsegv; - - tswap_siginfo(&rt_sf->info, info); - - __put_user(0, &rt_sf->uc.tuc_flags); - __put_user(0, &rt_sf->uc.tuc_link); - __put_user((target_ulong)target_sigaltstack_used.ss_sp, - &rt_sf->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->gpr[1]), - &rt_sf->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &rt_sf->uc.tuc_stack.ss_size); -#if !defined(TARGET_PPC64) - __put_user(h2g (&rt_sf->uc.tuc_mcontext), - &rt_sf->uc.tuc_regs); -#endif - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &rt_sf->uc.tuc_sigmask.sig[i]); - } - -#if defined(TARGET_PPC64) - mctx = &rt_sf->uc.tuc_sigcontext.mcontext; - trampptr = &rt_sf->trampoline[0]; - - sc = &rt_sf->uc.tuc_sigcontext; - __put_user(h2g(mctx), &sc->regs); - __put_user(sig, &sc->signal); -#else - mctx = &rt_sf->uc.tuc_mcontext; - trampptr = (uint32_t *)&rt_sf->uc.tuc_mcontext.tramp; -#endif - - save_user_regs(env, mctx); - encode_trampoline(TARGET_NR_rt_sigreturn, trampptr); - - /* The kernel checks for the presence of a VDSO here. We don't - emulate a vdso, so use a sigreturn system call. */ - env->lr = (target_ulong) h2g(trampptr); - - /* Turn off all fp exceptions. */ - env->fpscr = 0; - - /* Create a stack frame for the caller of the handler. */ - newsp = rt_sf_addr - (SIGNAL_FRAMESIZE + 16); - err |= put_user(env->gpr[1], newsp, target_ulong); - - if (err) - goto sigsegv; - - /* Set up registers for signal handler. */ - env->gpr[1] = newsp; - env->gpr[3] = (target_ulong) sig; - env->gpr[4] = (target_ulong) h2g(&rt_sf->info); - env->gpr[5] = (target_ulong) h2g(&rt_sf->uc); - env->gpr[6] = (target_ulong) h2g(rt_sf); - -#if defined(TARGET_PPC64) - if (get_ppc64_abi(image) < 2) { - /* ELFv1 PPC64 function pointers are pointers to OPD entries. */ - struct target_func_ptr *handler = - (struct target_func_ptr *)g2h(ka->_sa_handler); - env->nip = tswapl(handler->entry); - env->gpr[2] = tswapl(handler->toc); - } else { - /* ELFv2 PPC64 function pointers are entry points, but R12 - * must also be set */ - env->nip = tswapl((target_ulong) ka->_sa_handler); - env->gpr[12] = env->nip; - } -#else - env->nip = (target_ulong) ka->_sa_handler; -#endif - - /* Signal handlers are entered in big-endian mode. */ - env->msr &= ~(1ull << MSR_LE); - - unlock_user_struct(rt_sf, rt_sf_addr, 1); - return; - -sigsegv: - unlock_user_struct(rt_sf, rt_sf_addr, 1); - force_sigsegv(sig); - -} - -#if !defined(TARGET_PPC64) -long do_sigreturn(CPUPPCState *env) -{ - struct target_sigcontext *sc = NULL; - struct target_mcontext *sr = NULL; - target_ulong sr_addr = 0, sc_addr; - sigset_t blocked; - target_sigset_t set; - - sc_addr = env->gpr[1] + SIGNAL_FRAMESIZE; - if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) - goto sigsegv; - -#if defined(TARGET_PPC64) - set.sig[0] = sc->oldmask + ((uint64_t)(sc->_unused[3]) << 32); -#else - __get_user(set.sig[0], &sc->oldmask); - __get_user(set.sig[1], &sc->_unused[3]); -#endif - target_to_host_sigset_internal(&blocked, &set); - set_sigmask(&blocked); - - __get_user(sr_addr, &sc->regs); - if (!lock_user_struct(VERIFY_READ, sr, sr_addr, 1)) - goto sigsegv; - restore_user_regs(env, sr, 1); - - unlock_user_struct(sr, sr_addr, 1); - unlock_user_struct(sc, sc_addr, 1); - return -TARGET_QEMU_ESIGRETURN; - -sigsegv: - unlock_user_struct(sr, sr_addr, 1); - unlock_user_struct(sc, sc_addr, 1); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} -#endif /* !defined(TARGET_PPC64) */ - -/* See arch/powerpc/kernel/signal_32.c. */ -static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) -{ - struct target_mcontext *mcp; - target_ulong mcp_addr; - sigset_t blocked; - target_sigset_t set; - - if (copy_from_user(&set, h2g(ucp) + offsetof(struct target_ucontext, tuc_sigmask), - sizeof (set))) - return 1; - -#if defined(TARGET_PPC64) - mcp_addr = h2g(ucp) + - offsetof(struct target_ucontext, tuc_sigcontext.mcontext); -#else - __get_user(mcp_addr, &ucp->tuc_regs); -#endif - - if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1)) - return 1; - - target_to_host_sigset_internal(&blocked, &set); - set_sigmask(&blocked); - restore_user_regs(env, mcp, sig); - - unlock_user_struct(mcp, mcp_addr, 1); - return 0; -} - -long do_rt_sigreturn(CPUPPCState *env) -{ - struct target_rt_sigframe *rt_sf = NULL; - target_ulong rt_sf_addr; - - rt_sf_addr = env->gpr[1] + SIGNAL_FRAMESIZE + 16; - if (!lock_user_struct(VERIFY_READ, rt_sf, rt_sf_addr, 1)) - goto sigsegv; - - if (do_setcontext(&rt_sf->uc, env, 1)) - goto sigsegv; - - do_sigaltstack(rt_sf_addr - + offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, env->gpr[1]); - - unlock_user_struct(rt_sf, rt_sf_addr, 1); - return -TARGET_QEMU_ESIGRETURN; - -sigsegv: - unlock_user_struct(rt_sf, rt_sf_addr, 1); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} -#endif - static void handle_pending_signal(CPUArchState *cpu_env, int sig, struct emulated_sigtable *k) { From cb6ac802efea73605288f5d18e28611cbc97d9d2 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Apr 2018 21:26:35 +0200 Subject: [PATCH 0145/2380] linux-user: define TARGET_ARCH_HAS_SETUP_FRAME Instead of calling setup_frame() conditionally to a list of known targets, define TARGET_ARCH_HAS_SETUP_FRAME if the target provides the function and call it only if the macro is defined. Move declarations of setup_frame() and setup_rt_frame() to linux-user/signal-common.h Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180424192635.6027-21-laurent@vivier.eu> --- linux-user/aarch64/target_signal.h | 6 +----- linux-user/alpha/target_signal.h | 6 +----- linux-user/arm/target_signal.h | 6 +----- linux-user/cris/target_signal.h | 6 +----- linux-user/hppa/target_signal.h | 4 ---- linux-user/i386/target_signal.h | 6 +----- linux-user/m68k/target_signal.h | 6 +----- linux-user/microblaze/target_signal.h | 6 +----- linux-user/mips/target_signal.h | 8 ++------ linux-user/mips64/target_signal.h | 4 ---- linux-user/nios2/target_signal.h | 5 ----- linux-user/openrisc/target_signal.h | 4 ---- linux-user/ppc/target_signal.h | 6 +----- linux-user/riscv/target_signal.h | 4 ---- linux-user/s390x/target_signal.h | 6 +----- linux-user/sh4/target_signal.h | 7 +------ linux-user/signal-common.h | 7 +++++++ linux-user/signal.c | 17 +++++++---------- linux-user/sparc/target_signal.h | 6 +----- linux-user/sparc64/target_signal.h | 6 +----- linux-user/tilegx/target_signal.h | 4 ---- linux-user/x86_64/target_signal.h | 4 ---- linux-user/xtensa/target_signal.h | 4 ---- 23 files changed, 28 insertions(+), 110 deletions(-) diff --git a/linux-user/aarch64/target_signal.h b/linux-user/aarch64/target_signal.h index af1f1320e2..0b7ae25120 100644 --- a/linux-user/aarch64/target_signal.h +++ b/linux-user/aarch64/target_signal.h @@ -26,9 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) return state->xregs[31]; } -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *env); -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, target_sigset_t *set, - CPUARMState *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* AARCH64_TARGET_SIGNAL_H */ diff --git a/linux-user/alpha/target_signal.h b/linux-user/alpha/target_signal.h index 42343a1ae6..4e912e1cf9 100644 --- a/linux-user/alpha/target_signal.h +++ b/linux-user/alpha/target_signal.h @@ -55,9 +55,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state) #define TARGET_GEN_SUBRNG6 -24 #define TARGET_GEN_SUBRNG7 -25 -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUAlphaState *env); -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUAlphaState *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* ALPHA_TARGET_SIGNAL_H */ diff --git a/linux-user/arm/target_signal.h b/linux-user/arm/target_signal.h index 4b542c324f..d6a03ec87d 100644 --- a/linux-user/arm/target_signal.h +++ b/linux-user/arm/target_signal.h @@ -26,9 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) return state->regs[13]; } -void setup_frame(int usig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *regs); -void setup_rt_frame(int usig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUARMState *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* ARM_TARGET_SIGNAL_H */ diff --git a/linux-user/cris/target_signal.h b/linux-user/cris/target_signal.h index 19c0d7b539..74ff2f3382 100644 --- a/linux-user/cris/target_signal.h +++ b/linux-user/cris/target_signal.h @@ -26,9 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUCRISState *state) return state->regs[14]; } -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUCRISState *env); -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUCRISState *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* CRIS_TARGET_SIGNAL_H */ diff --git a/linux-user/hppa/target_signal.h b/linux-user/hppa/target_signal.h index f19aed93dd..f28b4bf6e8 100644 --- a/linux-user/hppa/target_signal.h +++ b/linux-user/hppa/target_signal.h @@ -25,8 +25,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUHPPAState *state) { return state->gr[30]; } - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUArchState *env); #endif /* HPPA_TARGET_SIGNAL_H */ diff --git a/linux-user/i386/target_signal.h b/linux-user/i386/target_signal.h index ec5a3e3373..6ad4089482 100644 --- a/linux-user/i386/target_signal.h +++ b/linux-user/i386/target_signal.h @@ -26,9 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) return state->regs[R_ESP]; } -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUX86State *env); -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUX86State *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* I386_TARGET_SIGNAL_H */ diff --git a/linux-user/m68k/target_signal.h b/linux-user/m68k/target_signal.h index 29e0c01191..ff303f2b3c 100644 --- a/linux-user/m68k/target_signal.h +++ b/linux-user/m68k/target_signal.h @@ -26,9 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUM68KState *state) return state->aregs[7]; } -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUM68KState *env); -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUM68KState *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* M68K_TARGET_SIGNAL_H */ diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h index 02c5cc56fa..9fe4048292 100644 --- a/linux-user/microblaze/target_signal.h +++ b/linux-user/microblaze/target_signal.h @@ -26,9 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUMBState *state) return state->regs[1]; } -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUMBState *env); -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUMBState *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* MICROBLAZE_TARGET_SIGNAL_H */ diff --git a/linux-user/mips/target_signal.h b/linux-user/mips/target_signal.h index 22ab3e4a94..d36f5da0a0 100644 --- a/linux-user/mips/target_signal.h +++ b/linux-user/mips/target_signal.h @@ -26,12 +26,8 @@ static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) return state->active_tc.gpr[29]; } -# if defined(TARGET_ABI_MIPSO32) +#if defined(TARGET_ABI_MIPSO32) /* compare linux/arch/mips/kernel/signal.c:setup_frame() */ -void setup_frame(int sig, struct target_sigaction * ka, - target_sigset_t *set, CPUMIPSState *regs); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUMIPSState *env); #endif /* MIPS_TARGET_SIGNAL_H */ diff --git a/linux-user/mips64/target_signal.h b/linux-user/mips64/target_signal.h index 70dfe40978..c074e1592f 100644 --- a/linux-user/mips64/target_signal.h +++ b/linux-user/mips64/target_signal.h @@ -25,8 +25,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) { return state->active_tc.gpr[29]; } - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUMIPSState *env); #endif /* MIPS64_TARGET_SIGNAL_H */ diff --git a/linux-user/nios2/target_signal.h b/linux-user/nios2/target_signal.h index aa78519025..f4db4d6d62 100644 --- a/linux-user/nios2/target_signal.h +++ b/linux-user/nios2/target_signal.h @@ -22,9 +22,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUNios2State *state) { return state->regs[R_SP]; } - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, - CPUNios2State *env); #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h index 6c47ddf74e..2a4e00b035 100644 --- a/linux-user/openrisc/target_signal.h +++ b/linux-user/openrisc/target_signal.h @@ -22,8 +22,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUOpenRISCState *state) { return cpu_get_gpr(state, 1); } - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUOpenRISCState *env); #endif /* OPENRISC_TARGET_SIGNAL_H */ diff --git a/linux-user/ppc/target_signal.h b/linux-user/ppc/target_signal.h index 5e293e3b03..e3bf1d2856 100644 --- a/linux-user/ppc/target_signal.h +++ b/linux-user/ppc/target_signal.h @@ -27,10 +27,6 @@ static inline abi_ulong get_sp_from_cpustate(CPUPPCState *state) } #if !defined(TARGET_PPC64) -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUPPCState *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUPPCState *env); #endif /* PPC_TARGET_SIGNAL_H */ diff --git a/linux-user/riscv/target_signal.h b/linux-user/riscv/target_signal.h index 6ac8a88de6..9dac002c0d 100644 --- a/linux-user/riscv/target_signal.h +++ b/linux-user/riscv/target_signal.h @@ -19,8 +19,4 @@ static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state) { return state->gpr[xSP]; } - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPURISCVState *env); #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/s390x/target_signal.h b/linux-user/s390x/target_signal.h index 9e3733bb38..4e99f8fadd 100644 --- a/linux-user/s390x/target_signal.h +++ b/linux-user/s390x/target_signal.h @@ -23,9 +23,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state) return state->regs[15]; } -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUS390XState *env); -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUS390XState *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* S390X_TARGET_SIGNAL_H */ diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h index a2a93fa04a..e7b18a6db4 100644 --- a/linux-user/sh4/target_signal.h +++ b/linux-user/sh4/target_signal.h @@ -26,10 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUSH4State *state) return state->gregs[15]; } -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUSH4State *regs); -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUSH4State *regs); - +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* SH4_TARGET_SIGNAL_H */ diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h index 838b6e9198..fbb8d4365c 100644 --- a/linux-user/signal-common.h +++ b/linux-user/signal-common.h @@ -47,4 +47,11 @@ void tswap_siginfo(target_siginfo_t *tinfo, void set_sigmask(const sigset_t *set); void force_sig(int sig); void force_sigsegv(int oldsig); +#if defined(TARGET_ARCH_HAS_SETUP_FRAME) +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUArchState *env); +#endif +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUArchState *env); #endif diff --git a/linux-user/signal.c b/linux-user/signal.c index 7f435c4606..a3022c2f04 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -873,18 +873,15 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, } #endif /* prepare the stack frame of the virtual CPU */ -#if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) \ - || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \ - || defined(TARGET_PPC64) || defined(TARGET_HPPA) \ - || defined(TARGET_NIOS2) || defined(TARGET_X86_64) \ - || defined(TARGET_RISCV) || defined(TARGET_XTENSA) +#if defined(TARGET_ARCH_HAS_SETUP_FRAME) + if (sa->sa_flags & TARGET_SA_SIGINFO) { + setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env); + } else { + setup_frame(sig, sa, &target_old_set, cpu_env); + } +#else /* These targets do not have traditional signals. */ setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env); -#else - if (sa->sa_flags & TARGET_SA_SIGINFO) - setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env); - else - setup_frame(sig, sa, &target_old_set, cpu_env); #endif if (sa->sa_flags & TARGET_SA_RESETHAND) { sa->_sa_handler = TARGET_SIG_DFL; diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h index ea2c68c900..467abea49e 100644 --- a/linux-user/sparc/target_signal.h +++ b/linux-user/sparc/target_signal.h @@ -33,9 +33,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) return state->regwptr[UREG_FP]; } -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUSPARCState *env); -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUSPARCState *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* SPARC_TARGET_SIGNAL_H */ diff --git a/linux-user/sparc64/target_signal.h b/linux-user/sparc64/target_signal.h index 7f10db9ede..14b01d9632 100644 --- a/linux-user/sparc64/target_signal.h +++ b/linux-user/sparc64/target_signal.h @@ -33,9 +33,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) return state->regwptr[UREG_FP]; } -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUSPARCState *env); -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUSPARCState *env); +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* SPARC64_TARGET_SIGNAL_H */ diff --git a/linux-user/tilegx/target_signal.h b/linux-user/tilegx/target_signal.h index 132d7781fe..a74fa37aac 100644 --- a/linux-user/tilegx/target_signal.h +++ b/linux-user/tilegx/target_signal.h @@ -24,8 +24,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUTLGState *state) { return state->regs[TILEGX_R_SP]; } - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUArchState *env); #endif /* TILEGX_TARGET_SIGNAL_H */ diff --git a/linux-user/x86_64/target_signal.h b/linux-user/x86_64/target_signal.h index 26f96944e5..6b01b5acb7 100644 --- a/linux-user/x86_64/target_signal.h +++ b/linux-user/x86_64/target_signal.h @@ -25,8 +25,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) { return state->regs[R_ESP]; } - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUX86State *env); #endif /* X86_64_TARGET_SIGNAL_H */ diff --git a/linux-user/xtensa/target_signal.h b/linux-user/xtensa/target_signal.h index f6545903a4..4376b2e538 100644 --- a/linux-user/xtensa/target_signal.h +++ b/linux-user/xtensa/target_signal.h @@ -24,8 +24,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUXtensaState *state) { return state->regs[1]; } - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUXtensaState *env); #endif From cd71c0896454d75e23518bbcec76abdf3cfa0772 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:33 +0200 Subject: [PATCH 0146/2380] linux-user: create a dummy per arch cpu_loop.c Create a cpu_loop-common.h for future use by these new files and use it in the existing main.c Introduce target_cpu_copy_regs(): declare the function in cpu_loop-common.h and an empty function for each target, to move all the cpu_loop prologues to this function. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411185651.21351-2-laurent@vivier.eu> --- linux-user/Makefile.objs | 3 ++- linux-user/aarch64/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/alpha/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/arm/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/cpu_loop-common.h | 37 ++++++++++++++++++++++++++++++++ linux-user/cris/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/hppa/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/i386/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/m68k/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/main.c | 17 +++------------ linux-user/microblaze/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/mips/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/mips64/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/nios2/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/openrisc/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/ppc/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/riscv/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/s390x/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/sh4/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/sparc/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/sparc64/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/tilegx/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/x86_64/cpu_loop.c | 26 ++++++++++++++++++++++ linux-user/xtensa/cpu_loop.c | 26 ++++++++++++++++++++++ 24 files changed, 588 insertions(+), 15 deletions(-) create mode 100644 linux-user/aarch64/cpu_loop.c create mode 100644 linux-user/alpha/cpu_loop.c create mode 100644 linux-user/arm/cpu_loop.c create mode 100644 linux-user/cpu_loop-common.h create mode 100644 linux-user/cris/cpu_loop.c create mode 100644 linux-user/hppa/cpu_loop.c create mode 100644 linux-user/i386/cpu_loop.c create mode 100644 linux-user/m68k/cpu_loop.c create mode 100644 linux-user/microblaze/cpu_loop.c create mode 100644 linux-user/mips/cpu_loop.c create mode 100644 linux-user/mips64/cpu_loop.c create mode 100644 linux-user/nios2/cpu_loop.c create mode 100644 linux-user/openrisc/cpu_loop.c create mode 100644 linux-user/ppc/cpu_loop.c create mode 100644 linux-user/riscv/cpu_loop.c create mode 100644 linux-user/s390x/cpu_loop.c create mode 100644 linux-user/sh4/cpu_loop.c create mode 100644 linux-user/sparc/cpu_loop.c create mode 100644 linux-user/sparc64/cpu_loop.c create mode 100644 linux-user/tilegx/cpu_loop.c create mode 100644 linux-user/x86_64/cpu_loop.c create mode 100644 linux-user/xtensa/cpu_loop.c diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs index 811a7f5ce5..59a5c17354 100644 --- a/linux-user/Makefile.objs +++ b/linux-user/Makefile.objs @@ -1,6 +1,7 @@ obj-y = main.o syscall.o strace.o mmap.o signal.o \ elfload.o linuxload.o uaccess.o uname.o \ - safe-syscall.o $(TARGET_ABI_DIR)/signal.o + safe-syscall.o $(TARGET_ABI_DIR)/signal.o \ + $(TARGET_ABI_DIR)/cpu_loop.o obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_I386) += vm86.o diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/aarch64/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/alpha/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/arm/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/cpu_loop-common.h b/linux-user/cpu_loop-common.h new file mode 100644 index 0000000000..ffe3fe9ad5 --- /dev/null +++ b/linux-user/cpu_loop-common.h @@ -0,0 +1,37 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#ifndef CPU_LOOP_COMMON_H +#define CPU_LOOP_COMMON_H + +#include "exec/log.h" + +#define EXCP_DUMP(env, fmt, ...) \ +do { \ + CPUState *cs = ENV_GET_CPU(env); \ + fprintf(stderr, fmt , ## __VA_ARGS__); \ + cpu_dump_state(cs, stderr, fprintf, 0); \ + if (qemu_log_separate()) { \ + qemu_log(fmt, ## __VA_ARGS__); \ + log_cpu_state(cs, 0); \ + } \ +} while (0) + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs); +#endif diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/cris/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/hppa/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/i386/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/m68k/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/main.c b/linux-user/main.c index 8907a84114..09045f877c 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -33,9 +33,9 @@ #include "qemu/timer.h" #include "qemu/envlist.h" #include "elf.h" -#include "exec/log.h" #include "trace/control.h" #include "target_elf.h" +#include "cpu_loop-common.h" char *exec_path; @@ -50,17 +50,6 @@ unsigned long mmap_min_addr; unsigned long guest_base; int have_guest_base; -#define EXCP_DUMP(env, fmt, ...) \ -do { \ - CPUState *cs = ENV_GET_CPU(env); \ - fprintf(stderr, fmt , ## __VA_ARGS__); \ - cpu_dump_state(cs, stderr, fprintf, 0); \ - if (qemu_log_separate()) { \ - qemu_log(fmt, ## __VA_ARGS__); \ - log_cpu_state(cs, 0); \ - } \ -} while (0) - /* * When running 32-on-64 we should make sure we can fit all of the possible * guest address space into a contiguous chunk of virtual host memory. @@ -4736,6 +4725,8 @@ int main(int argc, char **argv, char **envp) tcg_prologue_init(tcg_ctx); tcg_region_init(); + target_cpu_copy_regs(env, regs); + #if defined(TARGET_I386) env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; env->hflags |= HF_PE_MASK | HF_CPL_MASK; @@ -5125,8 +5116,6 @@ int main(int argc, char **argv, char **envp) env->sregs[WINDOW_START] = regs->windowstart; env->pc = regs->pc; } -#else -#error unsupported target CPU #endif #if defined(TARGET_ARM) || defined(TARGET_M68K) diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/microblaze/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/mips/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/mips64/cpu_loop.c b/linux-user/mips64/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/mips64/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/nios2/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/openrisc/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/ppc/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/riscv/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/s390x/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/sh4/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/sparc/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/sparc64/cpu_loop.c b/linux-user/sparc64/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/sparc64/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/tilegx/cpu_loop.c b/linux-user/tilegx/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/tilegx/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/x86_64/cpu_loop.c b/linux-user/x86_64/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/x86_64/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c new file mode 100644 index 0000000000..b7700a5561 --- /dev/null +++ b/linux-user/xtensa/cpu_loop.c @@ -0,0 +1,26 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ +} From 3f8258c1c805e186dc12f32f4b132356ae0ed9ba Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:34 +0200 Subject: [PATCH 0147/2380] linux-user: move i386/x86_64 cpu loop to i386 directory No code change, only move code from main.c to i386/cpu_loop.c. Include i386/cpu_loop.c in x86_64/cpu_loop.c to avoid to duplicate code. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411185651.21351-3-laurent@vivier.eu> --- linux-user/i386/cpu_loop.c | 343 ++++++++++++++++++++++++++++++++++ linux-user/main.c | 348 +---------------------------------- linux-user/x86_64/cpu_loop.c | 8 +- 3 files changed, 345 insertions(+), 354 deletions(-) diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index b7700a5561..2374abfd0b 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -21,6 +21,349 @@ #include "qemu.h" #include "cpu_loop-common.h" +/***********************************************************/ +/* CPUX86 core interface */ + +uint64_t cpu_get_tsc(CPUX86State *env) +{ + return cpu_get_host_ticks(); +} + +static void write_dt(void *ptr, unsigned long addr, unsigned long limit, + int flags) +{ + unsigned int e1, e2; + uint32_t *p; + e1 = (addr << 16) | (limit & 0xffff); + e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); + e2 |= flags; + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); +} + +static uint64_t *idt_table; +#ifdef TARGET_X86_64 +static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, + uint64_t addr, unsigned int sel) +{ + uint32_t *p, e1, e2; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); + p[2] = tswap32(addr >> 32); + p[3] = 0; +} +/* only dpl matters as we do only user space emulation */ +static void set_idt(int n, unsigned int dpl) +{ + set_gate64(idt_table + n * 2, 0, dpl, 0, 0); +} +#else +static void set_gate(void *ptr, unsigned int type, unsigned int dpl, + uint32_t addr, unsigned int sel) +{ + uint32_t *p, e1, e2; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); +} + +/* only dpl matters as we do only user space emulation */ +static void set_idt(int n, unsigned int dpl) +{ + set_gate(idt_table + n, 0, dpl, 0, 0); +} +#endif + +void cpu_loop(CPUX86State *env) +{ + CPUState *cs = CPU(x86_env_get_cpu(env)); + int trapnr; + abi_ulong pc; + abi_ulong ret; + target_siginfo_t info; + + for(;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch(trapnr) { + case 0x80: + /* linux syscall from int $0x80 */ + ret = do_syscall(env, + env->regs[R_EAX], + env->regs[R_EBX], + env->regs[R_ECX], + env->regs[R_EDX], + env->regs[R_ESI], + env->regs[R_EDI], + env->regs[R_EBP], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->eip -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[R_EAX] = ret; + } + break; +#ifndef TARGET_ABI32 + case EXCP_SYSCALL: + /* linux syscall from syscall instruction */ + ret = do_syscall(env, + env->regs[R_EAX], + env->regs[R_EDI], + env->regs[R_ESI], + env->regs[R_EDX], + env->regs[10], + env->regs[8], + env->regs[9], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->eip -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[R_EAX] = ret; + } + break; +#endif + case EXCP0B_NOSEG: + case EXCP0C_STACK: + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP0D_GPF: + /* XXX: potential problem if ABI32 */ +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_fault(env); + } else +#endif + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP0E_PAGE: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + if (!(env->error_code & 1)) + info.si_code = TARGET_SEGV_MAPERR; + else + info.si_code = TARGET_SEGV_ACCERR; + info._sifields._sigfault._addr = env->cr[2]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP00_DIVZ: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + /* division by zero */ + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTDIV; + info._sifields._sigfault._addr = env->eip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP01_DB: + case EXCP03_INT3: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + if (trapnr == EXCP01_DB) { + info.si_code = TARGET_TRAP_BRKPT; + info._sifields._sigfault._addr = env->eip; + } else { + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + } + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP04_INTO: + case EXCP05_BOUND: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP06_ILLOP: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->eip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + pc = env->segs[R_CS].base + env->eip; + EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", + (long)pc, trapnr); + abort(); + } + process_pending_signals(env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; + env->hflags |= HF_PE_MASK | HF_CPL_MASK; + if (env->features[FEAT_1_EDX] & CPUID_SSE) { + env->cr[4] |= CR4_OSFXSR_MASK; + env->hflags |= HF_OSFXSR_MASK; + } +#ifndef TARGET_ABI32 + /* enable 64 bit mode if possible */ + if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) { + fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); + exit(EXIT_FAILURE); + } + env->cr[4] |= CR4_PAE_MASK; + env->efer |= MSR_EFER_LMA | MSR_EFER_LME; + env->hflags |= HF_LMA_MASK; +#endif + + /* flags setup : we activate the IRQs by default as in user mode */ + env->eflags |= IF_MASK; + + /* linux register setup */ +#ifndef TARGET_ABI32 + env->regs[R_EAX] = regs->rax; + env->regs[R_EBX] = regs->rbx; + env->regs[R_ECX] = regs->rcx; + env->regs[R_EDX] = regs->rdx; + env->regs[R_ESI] = regs->rsi; + env->regs[R_EDI] = regs->rdi; + env->regs[R_EBP] = regs->rbp; + env->regs[R_ESP] = regs->rsp; + env->eip = regs->rip; +#else + env->regs[R_EAX] = regs->eax; + env->regs[R_EBX] = regs->ebx; + env->regs[R_ECX] = regs->ecx; + env->regs[R_EDX] = regs->edx; + env->regs[R_ESI] = regs->esi; + env->regs[R_EDI] = regs->edi; + env->regs[R_EBP] = regs->ebp; + env->regs[R_ESP] = regs->esp; + env->eip = regs->eip; +#endif + + /* linux interrupt setup */ +#ifndef TARGET_ABI32 + env->idt.limit = 511; +#else + env->idt.limit = 255; +#endif + env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1), + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + idt_table = g2h(env->idt.base); + set_idt(0, 0); + set_idt(1, 0); + set_idt(2, 0); + set_idt(3, 3); + set_idt(4, 3); + set_idt(5, 0); + set_idt(6, 0); + set_idt(7, 0); + set_idt(8, 0); + set_idt(9, 0); + set_idt(10, 0); + set_idt(11, 0); + set_idt(12, 0); + set_idt(13, 0); + set_idt(14, 0); + set_idt(15, 0); + set_idt(16, 0); + set_idt(17, 0); + set_idt(18, 0); + set_idt(19, 0); + set_idt(0x80, 3); + + /* linux segment setup */ + { + uint64_t *gdt_table; + env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES, + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; + gdt_table = g2h(env->gdt.base); +#ifdef TARGET_ABI32 + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); +#else + /* 64 bit code segment */ + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + DESC_L_MASK | + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); +#endif + write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); + } + cpu_x86_load_seg(env, R_CS, __USER_CS); + cpu_x86_load_seg(env, R_SS, __USER_DS); +#ifdef TARGET_ABI32 + cpu_x86_load_seg(env, R_DS, __USER_DS); + cpu_x86_load_seg(env, R_ES, __USER_DS); + cpu_x86_load_seg(env, R_FS, __USER_DS); + cpu_x86_load_seg(env, R_GS, __USER_DS); + /* This hack makes Wine work... */ + env->segs[R_FS].selector = 0; +#else + cpu_x86_load_seg(env, R_DS, 0); + cpu_x86_load_seg(env, R_ES, 0); + cpu_x86_load_seg(env, R_FS, 0); + cpu_x86_load_seg(env, R_GS, 0); +#endif } diff --git a/linux-user/main.c b/linux-user/main.c index 09045f877c..5271473d47 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,238 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_I386 -/***********************************************************/ -/* CPUX86 core interface */ - -uint64_t cpu_get_tsc(CPUX86State *env) -{ - return cpu_get_host_ticks(); -} - -static void write_dt(void *ptr, unsigned long addr, unsigned long limit, - int flags) -{ - unsigned int e1, e2; - uint32_t *p; - e1 = (addr << 16) | (limit & 0xffff); - e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); - e2 |= flags; - p = ptr; - p[0] = tswap32(e1); - p[1] = tswap32(e2); -} - -static uint64_t *idt_table; -#ifdef TARGET_X86_64 -static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, - uint64_t addr, unsigned int sel) -{ - uint32_t *p, e1, e2; - e1 = (addr & 0xffff) | (sel << 16); - e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); - p = ptr; - p[0] = tswap32(e1); - p[1] = tswap32(e2); - p[2] = tswap32(addr >> 32); - p[3] = 0; -} -/* only dpl matters as we do only user space emulation */ -static void set_idt(int n, unsigned int dpl) -{ - set_gate64(idt_table + n * 2, 0, dpl, 0, 0); -} -#else -static void set_gate(void *ptr, unsigned int type, unsigned int dpl, - uint32_t addr, unsigned int sel) -{ - uint32_t *p, e1, e2; - e1 = (addr & 0xffff) | (sel << 16); - e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); - p = ptr; - p[0] = tswap32(e1); - p[1] = tswap32(e2); -} - -/* only dpl matters as we do only user space emulation */ -static void set_idt(int n, unsigned int dpl) -{ - set_gate(idt_table + n, 0, dpl, 0, 0); -} -#endif - -void cpu_loop(CPUX86State *env) -{ - CPUState *cs = CPU(x86_env_get_cpu(env)); - int trapnr; - abi_ulong pc; - abi_ulong ret; - target_siginfo_t info; - - for(;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch(trapnr) { - case 0x80: - /* linux syscall from int $0x80 */ - ret = do_syscall(env, - env->regs[R_EAX], - env->regs[R_EBX], - env->regs[R_ECX], - env->regs[R_EDX], - env->regs[R_ESI], - env->regs[R_EDI], - env->regs[R_EBP], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->eip -= 2; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[R_EAX] = ret; - } - break; -#ifndef TARGET_ABI32 - case EXCP_SYSCALL: - /* linux syscall from syscall instruction */ - ret = do_syscall(env, - env->regs[R_EAX], - env->regs[R_EDI], - env->regs[R_ESI], - env->regs[R_EDX], - env->regs[10], - env->regs[8], - env->regs[9], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->eip -= 2; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[R_EAX] = ret; - } - break; -#endif - case EXCP0B_NOSEG: - case EXCP0C_STACK: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP0D_GPF: - /* XXX: potential problem if ABI32 */ -#ifndef TARGET_X86_64 - if (env->eflags & VM_MASK) { - handle_vm86_fault(env); - } else -#endif - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP0E_PAGE: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - if (!(env->error_code & 1)) - info.si_code = TARGET_SEGV_MAPERR; - else - info.si_code = TARGET_SEGV_ACCERR; - info._sifields._sigfault._addr = env->cr[2]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP00_DIVZ: -#ifndef TARGET_X86_64 - if (env->eflags & VM_MASK) { - handle_vm86_trap(env, trapnr); - } else -#endif - { - /* division by zero */ - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_INTDIV; - info._sifields._sigfault._addr = env->eip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP01_DB: - case EXCP03_INT3: -#ifndef TARGET_X86_64 - if (env->eflags & VM_MASK) { - handle_vm86_trap(env, trapnr); - } else -#endif - { - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - if (trapnr == EXCP01_DB) { - info.si_code = TARGET_TRAP_BRKPT; - info._sifields._sigfault._addr = env->eip; - } else { - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - } - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP04_INTO: - case EXCP05_BOUND: -#ifndef TARGET_X86_64 - if (env->eflags & VM_MASK) { - handle_vm86_trap(env, trapnr); - } else -#endif - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP06_ILLOP: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->eip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - pc = env->segs[R_CS].base + env->eip; - EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", - (long)pc, trapnr); - abort(); - } - process_pending_signals(env); - } -} -#endif - #ifdef TARGET_ARM #define get_user_code_u32(x, gaddr, env) \ @@ -4727,121 +4495,7 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); -#if defined(TARGET_I386) - env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; - env->hflags |= HF_PE_MASK | HF_CPL_MASK; - if (env->features[FEAT_1_EDX] & CPUID_SSE) { - env->cr[4] |= CR4_OSFXSR_MASK; - env->hflags |= HF_OSFXSR_MASK; - } -#ifndef TARGET_ABI32 - /* enable 64 bit mode if possible */ - if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) { - fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); - exit(EXIT_FAILURE); - } - env->cr[4] |= CR4_PAE_MASK; - env->efer |= MSR_EFER_LMA | MSR_EFER_LME; - env->hflags |= HF_LMA_MASK; -#endif - - /* flags setup : we activate the IRQs by default as in user mode */ - env->eflags |= IF_MASK; - - /* linux register setup */ -#ifndef TARGET_ABI32 - env->regs[R_EAX] = regs->rax; - env->regs[R_EBX] = regs->rbx; - env->regs[R_ECX] = regs->rcx; - env->regs[R_EDX] = regs->rdx; - env->regs[R_ESI] = regs->rsi; - env->regs[R_EDI] = regs->rdi; - env->regs[R_EBP] = regs->rbp; - env->regs[R_ESP] = regs->rsp; - env->eip = regs->rip; -#else - env->regs[R_EAX] = regs->eax; - env->regs[R_EBX] = regs->ebx; - env->regs[R_ECX] = regs->ecx; - env->regs[R_EDX] = regs->edx; - env->regs[R_ESI] = regs->esi; - env->regs[R_EDI] = regs->edi; - env->regs[R_EBP] = regs->ebp; - env->regs[R_ESP] = regs->esp; - env->eip = regs->eip; -#endif - - /* linux interrupt setup */ -#ifndef TARGET_ABI32 - env->idt.limit = 511; -#else - env->idt.limit = 255; -#endif - env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1), - PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - idt_table = g2h(env->idt.base); - set_idt(0, 0); - set_idt(1, 0); - set_idt(2, 0); - set_idt(3, 3); - set_idt(4, 3); - set_idt(5, 0); - set_idt(6, 0); - set_idt(7, 0); - set_idt(8, 0); - set_idt(9, 0); - set_idt(10, 0); - set_idt(11, 0); - set_idt(12, 0); - set_idt(13, 0); - set_idt(14, 0); - set_idt(15, 0); - set_idt(16, 0); - set_idt(17, 0); - set_idt(18, 0); - set_idt(19, 0); - set_idt(0x80, 3); - - /* linux segment setup */ - { - uint64_t *gdt_table; - env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES, - PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; - gdt_table = g2h(env->gdt.base); -#ifdef TARGET_ABI32 - write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | - (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); -#else - /* 64 bit code segment */ - write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | - DESC_L_MASK | - (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); -#endif - write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | - (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); - } - cpu_x86_load_seg(env, R_CS, __USER_CS); - cpu_x86_load_seg(env, R_SS, __USER_DS); -#ifdef TARGET_ABI32 - cpu_x86_load_seg(env, R_DS, __USER_DS); - cpu_x86_load_seg(env, R_ES, __USER_DS); - cpu_x86_load_seg(env, R_FS, __USER_DS); - cpu_x86_load_seg(env, R_GS, __USER_DS); - /* This hack makes Wine work... */ - env->segs[R_FS].selector = 0; -#else - cpu_x86_load_seg(env, R_DS, 0); - cpu_x86_load_seg(env, R_ES, 0); - cpu_x86_load_seg(env, R_FS, 0); - cpu_x86_load_seg(env, R_GS, 0); -#endif -#elif defined(TARGET_AARCH64) +#if defined(TARGET_AARCH64) { int i; diff --git a/linux-user/x86_64/cpu_loop.c b/linux-user/x86_64/cpu_loop.c index b7700a5561..8b5af8ea1f 100644 --- a/linux-user/x86_64/cpu_loop.c +++ b/linux-user/x86_64/cpu_loop.c @@ -17,10 +17,4 @@ * along with this program; if not, see . */ -#include "qemu/osdep.h" -#include "qemu.h" -#include "cpu_loop-common.h" - -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) -{ -} +#include "../i386/cpu_loop.c" From 3c439b1762e6ec80f3f96a6fef53474c5a6f3757 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:35 +0200 Subject: [PATCH 0148/2380] linux-user: move aarch64 cpu loop to aarch64 directory No code change, only move code from main.c to aarch64/cpu_loop.c and duplicate some macro defined for both arm and aarch64. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411185651.21351-4-laurent@vivier.eu> --- linux-user/aarch64/cpu_loop.c | 156 ++++++++++++++++++++++++++++++++++ linux-user/main.c | 109 +----------------------- 2 files changed, 158 insertions(+), 107 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index b7700a5561..c97a646546 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -21,6 +21,162 @@ #include "qemu.h" #include "cpu_loop-common.h" +#define get_user_code_u32(x, gaddr, env) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_code_u16(x, gaddr, env) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + +#define get_user_data_u32(x, gaddr, env) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_data_u16(x, gaddr, env) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + +#define put_user_data_u32(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap32(__x); \ + } \ + put_user_u32(__x, (gaddr)); \ + }) + +#define put_user_data_u16(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap16(__x); \ + } \ + put_user_u16(__x, (gaddr)); \ + }) + +/* AArch64 main loop */ +void cpu_loop(CPUARMState *env) +{ + CPUState *cs = CPU(arm_env_get_cpu(env)); + int trapnr, sig; + abi_long ret; + target_siginfo_t info; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_SWI: + ret = do_syscall(env, + env->xregs[8], + env->xregs[0], + env->xregs[1], + env->xregs[2], + env->xregs[3], + env->xregs[4], + env->xregs[5], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->xregs[0] = ret; + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_UDEF: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_PREFETCH_ABORT: + case EXCP_DATA_ABORT: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->exception.vaddress; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_DEBUG: + case EXCP_BKPT: + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_SEMIHOST: + env->xregs[0] = do_arm_semihosting(env); + break; + case EXCP_YIELD: + /* nothing to do here for user-mode, just resume guest code */ + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); + abort(); + } + process_pending_signals(env); + /* Exception return on AArch64 always clears the exclusive monitor, + * so any return to running guest code implies this. + */ + env->exclusive_addr = -1; + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + int i; + + if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { + fprintf(stderr, + "The selected ARM CPU does not support 64 bit mode\n"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < 31; i++) { + env->xregs[i] = regs->regs[i]; + } + env->pc = regs->pc; + env->xregs[31] = regs->sp; +#ifdef TARGET_WORDS_BIGENDIAN + env->cp15.sctlr_el[1] |= SCTLR_E0E; + for (i = 1; i < 4; ++i) { + env->cp15.sctlr_el[i] |= SCTLR_EE; + } +#endif + + ts->stack_base = info->start_stack; + ts->heap_base = info->brk; + /* This will be filled in on the first SYS_HEAPINFO call. */ + ts->heap_limit = 0; } diff --git a/linux-user/main.c b/linux-user/main.c index 5271473d47..b4f08b226c 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -555,89 +555,6 @@ void cpu_loop(CPUARMState *env) process_pending_signals(env); } } - -#else - -/* AArch64 main loop */ -void cpu_loop(CPUARMState *env) -{ - CPUState *cs = CPU(arm_env_get_cpu(env)); - int trapnr, sig; - abi_long ret; - target_siginfo_t info; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_SWI: - ret = do_syscall(env, - env->xregs[8], - env->xregs[0], - env->xregs[1], - env->xregs[2], - env->xregs[3], - env->xregs[4], - env->xregs[5], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->xregs[0] = ret; - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_UDEF: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_PREFETCH_ABORT: - case EXCP_DATA_ABORT: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->exception.vaddress; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_DEBUG: - case EXCP_BKPT: - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_SEMIHOST: - env->xregs[0] = do_arm_semihosting(env); - break; - case EXCP_YIELD: - /* nothing to do here for user-mode, just resume guest code */ - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); - abort(); - } - process_pending_signals(env); - /* Exception return on AArch64 always clears the exclusive monitor, - * so any return to running guest code implies this. - */ - env->exclusive_addr = -1; - } -} #endif /* ndef TARGET_ABI32 */ #endif @@ -4495,29 +4412,7 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); -#if defined(TARGET_AARCH64) - { - int i; - - if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { - fprintf(stderr, - "The selected ARM CPU does not support 64 bit mode\n"); - exit(EXIT_FAILURE); - } - - for (i = 0; i < 31; i++) { - env->xregs[i] = regs->regs[i]; - } - env->pc = regs->pc; - env->xregs[31] = regs->sp; -#ifdef TARGET_WORDS_BIGENDIAN - env->cp15.sctlr_el[1] |= SCTLR_E0E; - for (i = 1; i < 4; ++i) { - env->cp15.sctlr_el[i] |= SCTLR_EE; - } -#endif - } -#elif defined(TARGET_ARM) +#if defined(TARGET_ARM) && !defined(TARGET_AARCH64) { int i; cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, @@ -4772,7 +4667,7 @@ int main(int argc, char **argv, char **envp) } #endif -#if defined(TARGET_ARM) || defined(TARGET_M68K) +#if (defined(TARGET_ARM) && !defined(TARGET_AARCH64)) || defined(TARGET_M68K) ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ From d9673512267d081f01fe1e450ac0891ba7d005aa Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:36 +0200 Subject: [PATCH 0149/2380] linux-user: move arm cpu loop to arm directory No code change, only move code from main.c to arm/cpu_loop.c and duplicate some macro defined for both arm and aarch64. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411185651.21351-5-laurent@vivier.eu> --- linux-user/arm/cpu_loop.c | 430 +++++++++++++++++++++++++++++++++++++ linux-user/main.c | 433 +------------------------------------- 2 files changed, 432 insertions(+), 431 deletions(-) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index b7700a5561..d911929bf6 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -19,8 +19,438 @@ #include "qemu/osdep.h" #include "qemu.h" +#include "elf.h" #include "cpu_loop-common.h" +#define get_user_code_u32(x, gaddr, env) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_code_u16(x, gaddr, env) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + +#define get_user_data_u32(x, gaddr, env) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_data_u16(x, gaddr, env) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + +#define put_user_data_u32(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap32(__x); \ + } \ + put_user_u32(__x, (gaddr)); \ + }) + +#define put_user_data_u16(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap16(__x); \ + } \ + put_user_u16(__x, (gaddr)); \ + }) + +/* Commpage handling -- there is no commpage for AArch64 */ + +/* + * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt + * Input: + * r0 = pointer to oldval + * r1 = pointer to newval + * r2 = pointer to target value + * + * Output: + * r0 = 0 if *ptr was changed, non-0 if no exchange happened + * C set if *ptr was changed, clear if no exchange happened + * + * Note segv's in kernel helpers are a bit tricky, we can set the + * data address sensibly but the PC address is just the entry point. + */ +static void arm_kernel_cmpxchg64_helper(CPUARMState *env) +{ + uint64_t oldval, newval, val; + uint32_t addr, cpsr; + target_siginfo_t info; + + /* Based on the 32 bit code in do_kernel_trap */ + + /* XXX: This only works between threads, not between processes. + It's probably possible to implement this with native host + operations. However things like ldrex/strex are much harder so + there's not much point trying. */ + start_exclusive(); + cpsr = cpsr_read(env); + addr = env->regs[2]; + + if (get_user_u64(oldval, env->regs[0])) { + env->exception.vaddress = env->regs[0]; + goto segv; + }; + + if (get_user_u64(newval, env->regs[1])) { + env->exception.vaddress = env->regs[1]; + goto segv; + }; + + if (get_user_u64(val, addr)) { + env->exception.vaddress = addr; + goto segv; + } + + if (val == oldval) { + val = newval; + + if (put_user_u64(val, addr)) { + env->exception.vaddress = addr; + goto segv; + }; + + env->regs[0] = 0; + cpsr |= CPSR_C; + } else { + env->regs[0] = -1; + cpsr &= ~CPSR_C; + } + cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); + end_exclusive(); + return; + +segv: + end_exclusive(); + /* We get the PC of the entry address - which is as good as anything, + on a real kernel what you get depends on which mode it uses. */ + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->exception.vaddress; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); +} + +/* Handle a jump to the kernel code page. */ +static int +do_kernel_trap(CPUARMState *env) +{ + uint32_t addr; + uint32_t cpsr; + uint32_t val; + + switch (env->regs[15]) { + case 0xffff0fa0: /* __kernel_memory_barrier */ + /* ??? No-op. Will need to do better for SMP. */ + break; + case 0xffff0fc0: /* __kernel_cmpxchg */ + /* XXX: This only works between threads, not between processes. + It's probably possible to implement this with native host + operations. However things like ldrex/strex are much harder so + there's not much point trying. */ + start_exclusive(); + cpsr = cpsr_read(env); + addr = env->regs[2]; + /* FIXME: This should SEGV if the access fails. */ + if (get_user_u32(val, addr)) + val = ~env->regs[0]; + if (val == env->regs[0]) { + val = env->regs[1]; + /* FIXME: Check for segfaults. */ + put_user_u32(val, addr); + env->regs[0] = 0; + cpsr |= CPSR_C; + } else { + env->regs[0] = -1; + cpsr &= ~CPSR_C; + } + cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); + end_exclusive(); + break; + case 0xffff0fe0: /* __kernel_get_tls */ + env->regs[0] = cpu_get_tls(env); + break; + case 0xffff0f60: /* __kernel_cmpxchg64 */ + arm_kernel_cmpxchg64_helper(env); + break; + + default: + return 1; + } + /* Jump back to the caller. */ + addr = env->regs[14]; + if (addr & 1) { + env->thumb = 1; + addr &= ~1; + } + env->regs[15] = addr; + + return 0; +} + +void cpu_loop(CPUARMState *env) +{ + CPUState *cs = CPU(arm_env_get_cpu(env)); + int trapnr; + unsigned int n, insn; + target_siginfo_t info; + uint32_t addr; + abi_ulong ret; + + for(;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch(trapnr) { + case EXCP_UDEF: + case EXCP_NOCP: + case EXCP_INVSTATE: + { + TaskState *ts = cs->opaque; + uint32_t opcode; + int rc; + + /* we handle the FPU emulation here, as Linux */ + /* we get the opcode */ + /* FIXME - what to do if get_user() fails? */ + get_user_code_u32(opcode, env->regs[15], env); + + rc = EmulateAll(opcode, &ts->fpa, env); + if (rc == 0) { /* illegal instruction */ + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->regs[15]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } else if (rc < 0) { /* FP exception */ + int arm_fpe=0; + + /* translate softfloat flags to FPSR flags */ + if (-rc & float_flag_invalid) + arm_fpe |= BIT_IOC; + if (-rc & float_flag_divbyzero) + arm_fpe |= BIT_DZC; + if (-rc & float_flag_overflow) + arm_fpe |= BIT_OFC; + if (-rc & float_flag_underflow) + arm_fpe |= BIT_UFC; + if (-rc & float_flag_inexact) + arm_fpe |= BIT_IXC; + + FPSR fpsr = ts->fpa.fpsr; + //printf("fpsr 0x%x, arm_fpe 0x%x\n",fpsr,arm_fpe); + + if (fpsr & (arm_fpe << 16)) { /* exception enabled? */ + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + + /* ordered by priority, least first */ + if (arm_fpe & BIT_IXC) info.si_code = TARGET_FPE_FLTRES; + if (arm_fpe & BIT_UFC) info.si_code = TARGET_FPE_FLTUND; + if (arm_fpe & BIT_OFC) info.si_code = TARGET_FPE_FLTOVF; + if (arm_fpe & BIT_DZC) info.si_code = TARGET_FPE_FLTDIV; + if (arm_fpe & BIT_IOC) info.si_code = TARGET_FPE_FLTINV; + + info._sifields._sigfault._addr = env->regs[15]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } else { + env->regs[15] += 4; + } + + /* accumulate unenabled exceptions */ + if ((!(fpsr & BIT_IXE)) && (arm_fpe & BIT_IXC)) + fpsr |= BIT_IXC; + if ((!(fpsr & BIT_UFE)) && (arm_fpe & BIT_UFC)) + fpsr |= BIT_UFC; + if ((!(fpsr & BIT_OFE)) && (arm_fpe & BIT_OFC)) + fpsr |= BIT_OFC; + if ((!(fpsr & BIT_DZE)) && (arm_fpe & BIT_DZC)) + fpsr |= BIT_DZC; + if ((!(fpsr & BIT_IOE)) && (arm_fpe & BIT_IOC)) + fpsr |= BIT_IOC; + ts->fpa.fpsr=fpsr; + } else { /* everything OK */ + /* increment PC */ + env->regs[15] += 4; + } + } + break; + case EXCP_SWI: + case EXCP_BKPT: + { + env->eabi = 1; + /* system call */ + if (trapnr == EXCP_BKPT) { + if (env->thumb) { + /* FIXME - what to do if get_user() fails? */ + get_user_code_u16(insn, env->regs[15], env); + n = insn & 0xff; + env->regs[15] += 2; + } else { + /* FIXME - what to do if get_user() fails? */ + get_user_code_u32(insn, env->regs[15], env); + n = (insn & 0xf) | ((insn >> 4) & 0xff0); + env->regs[15] += 4; + } + } else { + if (env->thumb) { + /* FIXME - what to do if get_user() fails? */ + get_user_code_u16(insn, env->regs[15] - 2, env); + n = insn & 0xff; + } else { + /* FIXME - what to do if get_user() fails? */ + get_user_code_u32(insn, env->regs[15] - 4, env); + n = insn & 0xffffff; + } + } + + if (n == ARM_NR_cacheflush) { + /* nop */ + } else if (n == ARM_NR_semihosting + || n == ARM_NR_thumb_semihosting) { + env->regs[0] = do_arm_semihosting (env); + } else if (n == 0 || n >= ARM_SYSCALL_BASE || env->thumb) { + /* linux syscall */ + if (env->thumb || n == 0) { + n = env->regs[7]; + } else { + n -= ARM_SYSCALL_BASE; + env->eabi = 0; + } + if ( n > ARM_NR_BASE) { + switch (n) { + case ARM_NR_cacheflush: + /* nop */ + break; + case ARM_NR_set_tls: + cpu_set_tls(env, env->regs[0]); + env->regs[0] = 0; + break; + case ARM_NR_breakpoint: + env->regs[15] -= env->thumb ? 2 : 4; + goto excp_debug; + default: + gemu_log("qemu: Unsupported ARM syscall: 0x%x\n", + n); + env->regs[0] = -TARGET_ENOSYS; + break; + } + } else { + ret = do_syscall(env, + n, + env->regs[0], + env->regs[1], + env->regs[2], + env->regs[3], + env->regs[4], + env->regs[5], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->regs[15] -= env->thumb ? 2 : 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[0] = ret; + } + } + } else { + goto error; + } + } + break; + case EXCP_SEMIHOST: + env->regs[0] = do_arm_semihosting(env); + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_PREFETCH_ABORT: + case EXCP_DATA_ABORT: + addr = env->exception.vaddress; + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = addr; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_DEBUG: + excp_debug: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_KERNEL_TRAP: + if (do_kernel_trap(env)) + goto error; + break; + case EXCP_YIELD: + /* nothing to do here for user-mode, just resume guest code */ + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + error: + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); + abort(); + } + process_pending_signals(env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + int i; + + cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, + CPSRWriteByInstr); + for(i = 0; i < 16; i++) { + env->regs[i] = regs->uregs[i]; + } +#ifdef TARGET_WORDS_BIGENDIAN + /* Enable BE8. */ + if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 + && (info->elf_flags & EF_ARM_BE8)) { + env->uncached_cpsr |= CPSR_E; + env->cp15.sctlr_el[1] |= SCTLR_E0E; + } else { + env->cp15.sctlr_el[1] |= SCTLR_B; + } +#endif + + ts->stack_base = info->start_stack; + ts->heap_base = info->brk; + /* This will be filled in on the first SYS_HEAPINFO call. */ + ts->heap_limit = 0; } diff --git a/linux-user/main.c b/linux-user/main.c index b4f08b226c..45d1588958 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,416 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_ARM - -#define get_user_code_u32(x, gaddr, env) \ - ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && bswap_code(arm_sctlr_b(env))) { \ - (x) = bswap32(x); \ - } \ - __r; \ - }) - -#define get_user_code_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && bswap_code(arm_sctlr_b(env))) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define get_user_data_u32(x, gaddr, env) \ - ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap32(x); \ - } \ - __r; \ - }) - -#define get_user_data_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define put_user_data_u32(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap32(__x); \ - } \ - put_user_u32(__x, (gaddr)); \ - }) - -#define put_user_data_u16(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap16(__x); \ - } \ - put_user_u16(__x, (gaddr)); \ - }) - -#ifdef TARGET_ABI32 -/* Commpage handling -- there is no commpage for AArch64 */ - -/* - * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt - * Input: - * r0 = pointer to oldval - * r1 = pointer to newval - * r2 = pointer to target value - * - * Output: - * r0 = 0 if *ptr was changed, non-0 if no exchange happened - * C set if *ptr was changed, clear if no exchange happened - * - * Note segv's in kernel helpers are a bit tricky, we can set the - * data address sensibly but the PC address is just the entry point. - */ -static void arm_kernel_cmpxchg64_helper(CPUARMState *env) -{ - uint64_t oldval, newval, val; - uint32_t addr, cpsr; - target_siginfo_t info; - - /* Based on the 32 bit code in do_kernel_trap */ - - /* XXX: This only works between threads, not between processes. - It's probably possible to implement this with native host - operations. However things like ldrex/strex are much harder so - there's not much point trying. */ - start_exclusive(); - cpsr = cpsr_read(env); - addr = env->regs[2]; - - if (get_user_u64(oldval, env->regs[0])) { - env->exception.vaddress = env->regs[0]; - goto segv; - }; - - if (get_user_u64(newval, env->regs[1])) { - env->exception.vaddress = env->regs[1]; - goto segv; - }; - - if (get_user_u64(val, addr)) { - env->exception.vaddress = addr; - goto segv; - } - - if (val == oldval) { - val = newval; - - if (put_user_u64(val, addr)) { - env->exception.vaddress = addr; - goto segv; - }; - - env->regs[0] = 0; - cpsr |= CPSR_C; - } else { - env->regs[0] = -1; - cpsr &= ~CPSR_C; - } - cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); - end_exclusive(); - return; - -segv: - end_exclusive(); - /* We get the PC of the entry address - which is as good as anything, - on a real kernel what you get depends on which mode it uses. */ - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->exception.vaddress; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); -} - -/* Handle a jump to the kernel code page. */ -static int -do_kernel_trap(CPUARMState *env) -{ - uint32_t addr; - uint32_t cpsr; - uint32_t val; - - switch (env->regs[15]) { - case 0xffff0fa0: /* __kernel_memory_barrier */ - /* ??? No-op. Will need to do better for SMP. */ - break; - case 0xffff0fc0: /* __kernel_cmpxchg */ - /* XXX: This only works between threads, not between processes. - It's probably possible to implement this with native host - operations. However things like ldrex/strex are much harder so - there's not much point trying. */ - start_exclusive(); - cpsr = cpsr_read(env); - addr = env->regs[2]; - /* FIXME: This should SEGV if the access fails. */ - if (get_user_u32(val, addr)) - val = ~env->regs[0]; - if (val == env->regs[0]) { - val = env->regs[1]; - /* FIXME: Check for segfaults. */ - put_user_u32(val, addr); - env->regs[0] = 0; - cpsr |= CPSR_C; - } else { - env->regs[0] = -1; - cpsr &= ~CPSR_C; - } - cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); - end_exclusive(); - break; - case 0xffff0fe0: /* __kernel_get_tls */ - env->regs[0] = cpu_get_tls(env); - break; - case 0xffff0f60: /* __kernel_cmpxchg64 */ - arm_kernel_cmpxchg64_helper(env); - break; - - default: - return 1; - } - /* Jump back to the caller. */ - addr = env->regs[14]; - if (addr & 1) { - env->thumb = 1; - addr &= ~1; - } - env->regs[15] = addr; - - return 0; -} - -void cpu_loop(CPUARMState *env) -{ - CPUState *cs = CPU(arm_env_get_cpu(env)); - int trapnr; - unsigned int n, insn; - target_siginfo_t info; - uint32_t addr; - abi_ulong ret; - - for(;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch(trapnr) { - case EXCP_UDEF: - case EXCP_NOCP: - case EXCP_INVSTATE: - { - TaskState *ts = cs->opaque; - uint32_t opcode; - int rc; - - /* we handle the FPU emulation here, as Linux */ - /* we get the opcode */ - /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(opcode, env->regs[15], env); - - rc = EmulateAll(opcode, &ts->fpa, env); - if (rc == 0) { /* illegal instruction */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->regs[15]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else if (rc < 0) { /* FP exception */ - int arm_fpe=0; - - /* translate softfloat flags to FPSR flags */ - if (-rc & float_flag_invalid) - arm_fpe |= BIT_IOC; - if (-rc & float_flag_divbyzero) - arm_fpe |= BIT_DZC; - if (-rc & float_flag_overflow) - arm_fpe |= BIT_OFC; - if (-rc & float_flag_underflow) - arm_fpe |= BIT_UFC; - if (-rc & float_flag_inexact) - arm_fpe |= BIT_IXC; - - FPSR fpsr = ts->fpa.fpsr; - //printf("fpsr 0x%x, arm_fpe 0x%x\n",fpsr,arm_fpe); - - if (fpsr & (arm_fpe << 16)) { /* exception enabled? */ - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - - /* ordered by priority, least first */ - if (arm_fpe & BIT_IXC) info.si_code = TARGET_FPE_FLTRES; - if (arm_fpe & BIT_UFC) info.si_code = TARGET_FPE_FLTUND; - if (arm_fpe & BIT_OFC) info.si_code = TARGET_FPE_FLTOVF; - if (arm_fpe & BIT_DZC) info.si_code = TARGET_FPE_FLTDIV; - if (arm_fpe & BIT_IOC) info.si_code = TARGET_FPE_FLTINV; - - info._sifields._sigfault._addr = env->regs[15]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else { - env->regs[15] += 4; - } - - /* accumulate unenabled exceptions */ - if ((!(fpsr & BIT_IXE)) && (arm_fpe & BIT_IXC)) - fpsr |= BIT_IXC; - if ((!(fpsr & BIT_UFE)) && (arm_fpe & BIT_UFC)) - fpsr |= BIT_UFC; - if ((!(fpsr & BIT_OFE)) && (arm_fpe & BIT_OFC)) - fpsr |= BIT_OFC; - if ((!(fpsr & BIT_DZE)) && (arm_fpe & BIT_DZC)) - fpsr |= BIT_DZC; - if ((!(fpsr & BIT_IOE)) && (arm_fpe & BIT_IOC)) - fpsr |= BIT_IOC; - ts->fpa.fpsr=fpsr; - } else { /* everything OK */ - /* increment PC */ - env->regs[15] += 4; - } - } - break; - case EXCP_SWI: - case EXCP_BKPT: - { - env->eabi = 1; - /* system call */ - if (trapnr == EXCP_BKPT) { - if (env->thumb) { - /* FIXME - what to do if get_user() fails? */ - get_user_code_u16(insn, env->regs[15], env); - n = insn & 0xff; - env->regs[15] += 2; - } else { - /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(insn, env->regs[15], env); - n = (insn & 0xf) | ((insn >> 4) & 0xff0); - env->regs[15] += 4; - } - } else { - if (env->thumb) { - /* FIXME - what to do if get_user() fails? */ - get_user_code_u16(insn, env->regs[15] - 2, env); - n = insn & 0xff; - } else { - /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(insn, env->regs[15] - 4, env); - n = insn & 0xffffff; - } - } - - if (n == ARM_NR_cacheflush) { - /* nop */ - } else if (n == ARM_NR_semihosting - || n == ARM_NR_thumb_semihosting) { - env->regs[0] = do_arm_semihosting (env); - } else if (n == 0 || n >= ARM_SYSCALL_BASE || env->thumb) { - /* linux syscall */ - if (env->thumb || n == 0) { - n = env->regs[7]; - } else { - n -= ARM_SYSCALL_BASE; - env->eabi = 0; - } - if ( n > ARM_NR_BASE) { - switch (n) { - case ARM_NR_cacheflush: - /* nop */ - break; - case ARM_NR_set_tls: - cpu_set_tls(env, env->regs[0]); - env->regs[0] = 0; - break; - case ARM_NR_breakpoint: - env->regs[15] -= env->thumb ? 2 : 4; - goto excp_debug; - default: - gemu_log("qemu: Unsupported ARM syscall: 0x%x\n", - n); - env->regs[0] = -TARGET_ENOSYS; - break; - } - } else { - ret = do_syscall(env, - n, - env->regs[0], - env->regs[1], - env->regs[2], - env->regs[3], - env->regs[4], - env->regs[5], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->regs[15] -= env->thumb ? 2 : 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[0] = ret; - } - } - } else { - goto error; - } - } - break; - case EXCP_SEMIHOST: - env->regs[0] = do_arm_semihosting(env); - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_PREFETCH_ABORT: - case EXCP_DATA_ABORT: - addr = env->exception.vaddress; - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = addr; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_DEBUG: - excp_debug: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_KERNEL_TRAP: - if (do_kernel_trap(env)) - goto error; - break; - case EXCP_YIELD: - /* nothing to do here for user-mode, just resume guest code */ - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - error: - EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); - abort(); - } - process_pending_signals(env); - } -} -#endif /* ndef TARGET_ABI32 */ - -#endif - #ifdef TARGET_SPARC #define SPARC64_STACK_BIAS 2047 @@ -4412,26 +4002,7 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); -#if defined(TARGET_ARM) && !defined(TARGET_AARCH64) - { - int i; - cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, - CPSRWriteByInstr); - for(i = 0; i < 16; i++) { - env->regs[i] = regs->uregs[i]; - } -#ifdef TARGET_WORDS_BIGENDIAN - /* Enable BE8. */ - if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 - && (info->elf_flags & EF_ARM_BE8)) { - env->uncached_cpsr |= CPSR_E; - env->cp15.sctlr_el[1] |= SCTLR_E0E; - } else { - env->cp15.sctlr_el[1] |= SCTLR_B; - } -#endif - } -#elif defined(TARGET_SPARC) +#if defined(TARGET_SPARC) { int i; env->pc = regs->pc; @@ -4667,7 +4238,7 @@ int main(int argc, char **argv, char **envp) } #endif -#if (defined(TARGET_ARM) && !defined(TARGET_AARCH64)) || defined(TARGET_M68K) +#if defined(TARGET_M68K) ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ From d0a28415e674ec0fee9375a800fb2250e6e0612a Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:37 +0200 Subject: [PATCH 0150/2380] linux-user: move sparc/sparc64 cpu loop to sparc directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from main.c to sparc/cpu_loop.c. Include sparc/cpu_loop.c in sparc64/cpu_loop.c to avoid to duplicate code. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180411185651.21351-6-laurent@vivier.eu> --- linux-user/main.c | 288 +--------------------------------- linux-user/sparc/cpu_loop.c | 280 +++++++++++++++++++++++++++++++++ linux-user/sparc64/cpu_loop.c | 8 +- 3 files changed, 282 insertions(+), 294 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 45d1588958..4816ec54bb 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,281 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_SPARC -#define SPARC64_STACK_BIAS 2047 - -//#define DEBUG_WIN - -/* WARNING: dealing with register windows _is_ complicated. More info - can be found at http://www.sics.se/~psm/sparcstack.html */ -static inline int get_reg_index(CPUSPARCState *env, int cwp, int index) -{ - index = (index + cwp * 16) % (16 * env->nwindows); - /* wrap handling : if cwp is on the last window, then we use the - registers 'after' the end */ - if (index < 8 && env->cwp == env->nwindows - 1) - index += 16 * env->nwindows; - return index; -} - -/* save the register window 'cwp1' */ -static inline void save_window_offset(CPUSPARCState *env, int cwp1) -{ - unsigned int i; - abi_ulong sp_ptr; - - sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; -#ifdef TARGET_SPARC64 - if (sp_ptr & 3) - sp_ptr += SPARC64_STACK_BIAS; -#endif -#if defined(DEBUG_WIN) - printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n", - sp_ptr, cwp1); -#endif - for(i = 0; i < 16; i++) { - /* FIXME - what to do if put_user() fails? */ - put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); - sp_ptr += sizeof(abi_ulong); - } -} - -static void save_window(CPUSPARCState *env) -{ -#ifndef TARGET_SPARC64 - unsigned int new_wim; - new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) & - ((1LL << env->nwindows) - 1); - save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); - env->wim = new_wim; -#else - save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); - env->cansave++; - env->canrestore--; -#endif -} - -static void restore_window(CPUSPARCState *env) -{ -#ifndef TARGET_SPARC64 - unsigned int new_wim; -#endif - unsigned int i, cwp1; - abi_ulong sp_ptr; - -#ifndef TARGET_SPARC64 - new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) & - ((1LL << env->nwindows) - 1); -#endif - - /* restore the invalid window */ - cwp1 = cpu_cwp_inc(env, env->cwp + 1); - sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; -#ifdef TARGET_SPARC64 - if (sp_ptr & 3) - sp_ptr += SPARC64_STACK_BIAS; -#endif -#if defined(DEBUG_WIN) - printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n", - sp_ptr, cwp1); -#endif - for(i = 0; i < 16; i++) { - /* FIXME - what to do if get_user() fails? */ - get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); - sp_ptr += sizeof(abi_ulong); - } -#ifdef TARGET_SPARC64 - env->canrestore++; - if (env->cleanwin < env->nwindows - 1) - env->cleanwin++; - env->cansave--; -#else - env->wim = new_wim; -#endif -} - -static void flush_windows(CPUSPARCState *env) -{ - int offset, cwp1; - - offset = 1; - for(;;) { - /* if restore would invoke restore_window(), then we can stop */ - cwp1 = cpu_cwp_inc(env, env->cwp + offset); -#ifndef TARGET_SPARC64 - if (env->wim & (1 << cwp1)) - break; -#else - if (env->canrestore == 0) - break; - env->cansave++; - env->canrestore--; -#endif - save_window_offset(env, cwp1); - offset++; - } - cwp1 = cpu_cwp_inc(env, env->cwp + 1); -#ifndef TARGET_SPARC64 - /* set wim so that restore will reload the registers */ - env->wim = 1 << cwp1; -#endif -#if defined(DEBUG_WIN) - printf("flush_windows: nb=%d\n", offset - 1); -#endif -} - -void cpu_loop (CPUSPARCState *env) -{ - CPUState *cs = CPU(sparc_env_get_cpu(env)); - int trapnr; - abi_long ret; - target_siginfo_t info; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - /* Compute PSR before exposing state. */ - if (env->cc_op != CC_OP_FLAGS) { - cpu_get_psr(env); - } - - switch (trapnr) { -#ifndef TARGET_SPARC64 - case 0x88: - case 0x90: -#else - case 0x110: - case 0x16d: -#endif - ret = do_syscall (env, env->gregs[1], - env->regwptr[0], env->regwptr[1], - env->regwptr[2], env->regwptr[3], - env->regwptr[4], env->regwptr[5], - 0, 0); - if (ret == -TARGET_ERESTARTSYS || ret == -TARGET_QEMU_ESIGRETURN) { - break; - } - if ((abi_ulong)ret >= (abi_ulong)(-515)) { -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) - env->xcc |= PSR_CARRY; -#else - env->psr |= PSR_CARRY; -#endif - ret = -ret; - } else { -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) - env->xcc &= ~PSR_CARRY; -#else - env->psr &= ~PSR_CARRY; -#endif - } - env->regwptr[0] = ret; - /* next instruction */ - env->pc = env->npc; - env->npc = env->npc + 4; - break; - case 0x83: /* flush windows */ -#ifdef TARGET_ABI32 - case 0x103: -#endif - flush_windows(env); - /* next instruction */ - env->pc = env->npc; - env->npc = env->npc + 4; - break; -#ifndef TARGET_SPARC64 - case TT_WIN_OVF: /* window overflow */ - save_window(env); - break; - case TT_WIN_UNF: /* window underflow */ - restore_window(env); - break; - case TT_TFAULT: - case TT_DFAULT: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->mmuregs[4]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; -#else - case TT_SPILL: /* window overflow */ - save_window(env); - break; - case TT_FILL: /* window underflow */ - restore_window(env); - break; - case TT_TFAULT: - case TT_DFAULT: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - if (trapnr == TT_DFAULT) - info._sifields._sigfault._addr = env->dmmu.mmuregs[4]; - else - info._sifields._sigfault._addr = cpu_tsptr(env)->tpc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; -#ifndef TARGET_ABI32 - case 0x16e: - flush_windows(env); - sparc64_get_context(env); - break; - case 0x16f: - flush_windows(env); - sparc64_set_context(env); - break; -#endif -#endif - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case TT_ILL_INSN: - { - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - printf ("Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - } -} - -#endif - #ifdef TARGET_PPC static inline uint64_t cpu_ppc_get_tb(CPUPPCState *env) { @@ -4002,18 +3727,7 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); -#if defined(TARGET_SPARC) - { - int i; - env->pc = regs->pc; - env->npc = regs->npc; - env->y = regs->y; - for(i = 0; i < 8; i++) - env->gregs[i] = regs->u_regs[i]; - for(i = 0; i < 8; i++) - env->regwptr[i] = regs->u_regs[i + 8]; - } -#elif defined(TARGET_PPC) +#if defined(TARGET_PPC) { int i; diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index b7700a5561..7c4796ca23 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -21,6 +21,286 @@ #include "qemu.h" #include "cpu_loop-common.h" +#define SPARC64_STACK_BIAS 2047 + +//#define DEBUG_WIN + +/* WARNING: dealing with register windows _is_ complicated. More info + can be found at http://www.sics.se/~psm/sparcstack.html */ +static inline int get_reg_index(CPUSPARCState *env, int cwp, int index) +{ + index = (index + cwp * 16) % (16 * env->nwindows); + /* wrap handling : if cwp is on the last window, then we use the + registers 'after' the end */ + if (index < 8 && env->cwp == env->nwindows - 1) + index += 16 * env->nwindows; + return index; +} + +/* save the register window 'cwp1' */ +static inline void save_window_offset(CPUSPARCState *env, int cwp1) +{ + unsigned int i; + abi_ulong sp_ptr; + + sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; +#ifdef TARGET_SPARC64 + if (sp_ptr & 3) + sp_ptr += SPARC64_STACK_BIAS; +#endif +#if defined(DEBUG_WIN) + printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n", + sp_ptr, cwp1); +#endif + for(i = 0; i < 16; i++) { + /* FIXME - what to do if put_user() fails? */ + put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); + sp_ptr += sizeof(abi_ulong); + } +} + +static void save_window(CPUSPARCState *env) +{ +#ifndef TARGET_SPARC64 + unsigned int new_wim; + new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) & + ((1LL << env->nwindows) - 1); + save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); + env->wim = new_wim; +#else + save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); + env->cansave++; + env->canrestore--; +#endif +} + +static void restore_window(CPUSPARCState *env) +{ +#ifndef TARGET_SPARC64 + unsigned int new_wim; +#endif + unsigned int i, cwp1; + abi_ulong sp_ptr; + +#ifndef TARGET_SPARC64 + new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) & + ((1LL << env->nwindows) - 1); +#endif + + /* restore the invalid window */ + cwp1 = cpu_cwp_inc(env, env->cwp + 1); + sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; +#ifdef TARGET_SPARC64 + if (sp_ptr & 3) + sp_ptr += SPARC64_STACK_BIAS; +#endif +#if defined(DEBUG_WIN) + printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n", + sp_ptr, cwp1); +#endif + for(i = 0; i < 16; i++) { + /* FIXME - what to do if get_user() fails? */ + get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); + sp_ptr += sizeof(abi_ulong); + } +#ifdef TARGET_SPARC64 + env->canrestore++; + if (env->cleanwin < env->nwindows - 1) + env->cleanwin++; + env->cansave--; +#else + env->wim = new_wim; +#endif +} + +static void flush_windows(CPUSPARCState *env) +{ + int offset, cwp1; + + offset = 1; + for(;;) { + /* if restore would invoke restore_window(), then we can stop */ + cwp1 = cpu_cwp_inc(env, env->cwp + offset); +#ifndef TARGET_SPARC64 + if (env->wim & (1 << cwp1)) + break; +#else + if (env->canrestore == 0) + break; + env->cansave++; + env->canrestore--; +#endif + save_window_offset(env, cwp1); + offset++; + } + cwp1 = cpu_cwp_inc(env, env->cwp + 1); +#ifndef TARGET_SPARC64 + /* set wim so that restore will reload the registers */ + env->wim = 1 << cwp1; +#endif +#if defined(DEBUG_WIN) + printf("flush_windows: nb=%d\n", offset - 1); +#endif +} + +void cpu_loop (CPUSPARCState *env) +{ + CPUState *cs = CPU(sparc_env_get_cpu(env)); + int trapnr; + abi_long ret; + target_siginfo_t info; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + /* Compute PSR before exposing state. */ + if (env->cc_op != CC_OP_FLAGS) { + cpu_get_psr(env); + } + + switch (trapnr) { +#ifndef TARGET_SPARC64 + case 0x88: + case 0x90: +#else + case 0x110: + case 0x16d: +#endif + ret = do_syscall (env, env->gregs[1], + env->regwptr[0], env->regwptr[1], + env->regwptr[2], env->regwptr[3], + env->regwptr[4], env->regwptr[5], + 0, 0); + if (ret == -TARGET_ERESTARTSYS || ret == -TARGET_QEMU_ESIGRETURN) { + break; + } + if ((abi_ulong)ret >= (abi_ulong)(-515)) { +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + env->xcc |= PSR_CARRY; +#else + env->psr |= PSR_CARRY; +#endif + ret = -ret; + } else { +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + env->xcc &= ~PSR_CARRY; +#else + env->psr &= ~PSR_CARRY; +#endif + } + env->regwptr[0] = ret; + /* next instruction */ + env->pc = env->npc; + env->npc = env->npc + 4; + break; + case 0x83: /* flush windows */ +#ifdef TARGET_ABI32 + case 0x103: +#endif + flush_windows(env); + /* next instruction */ + env->pc = env->npc; + env->npc = env->npc + 4; + break; +#ifndef TARGET_SPARC64 + case TT_WIN_OVF: /* window overflow */ + save_window(env); + break; + case TT_WIN_UNF: /* window underflow */ + restore_window(env); + break; + case TT_TFAULT: + case TT_DFAULT: + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->mmuregs[4]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; +#else + case TT_SPILL: /* window overflow */ + save_window(env); + break; + case TT_FILL: /* window underflow */ + restore_window(env); + break; + case TT_TFAULT: + case TT_DFAULT: + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + if (trapnr == TT_DFAULT) + info._sifields._sigfault._addr = env->dmmu.mmuregs[4]; + else + info._sifields._sigfault._addr = cpu_tsptr(env)->tpc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; +#ifndef TARGET_ABI32 + case 0x16e: + flush_windows(env); + sparc64_get_context(env); + break; + case 0x16f: + flush_windows(env); + sparc64_set_context(env); + break; +#endif +#endif + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case TT_ILL_INSN: + { + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPC; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + printf ("Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + int i; + env->pc = regs->pc; + env->npc = regs->npc; + env->y = regs->y; + for(i = 0; i < 8; i++) + env->gregs[i] = regs->u_regs[i]; + for(i = 0; i < 8; i++) + env->regwptr[i] = regs->u_regs[i + 8]; } diff --git a/linux-user/sparc64/cpu_loop.c b/linux-user/sparc64/cpu_loop.c index b7700a5561..4fd44e1b1e 100644 --- a/linux-user/sparc64/cpu_loop.c +++ b/linux-user/sparc64/cpu_loop.c @@ -17,10 +17,4 @@ * along with this program; if not, see . */ -#include "qemu/osdep.h" -#include "qemu.h" -#include "cpu_loop-common.h" - -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) -{ -} +#include "../sparc/cpu_loop.c" From 11400516243274d23ede7d96bb766174e1e96cc9 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:38 +0200 Subject: [PATCH 0151/2380] linux-user: move ppc/ppc64 cpu loop to ppc directory No code change, only move code from main.c to ppc/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411185651.21351-7-laurent@vivier.eu> --- linux-user/main.c | 560 +------------------------------------- linux-user/ppc/cpu_loop.c | 553 +++++++++++++++++++++++++++++++++++++ 2 files changed, 554 insertions(+), 559 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 4816ec54bb..2340320818 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,547 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_PPC -static inline uint64_t cpu_ppc_get_tb(CPUPPCState *env) -{ - return cpu_get_host_ticks(); -} - -uint64_t cpu_ppc_load_tbl(CPUPPCState *env) -{ - return cpu_ppc_get_tb(env); -} - -uint32_t cpu_ppc_load_tbu(CPUPPCState *env) -{ - return cpu_ppc_get_tb(env) >> 32; -} - -uint64_t cpu_ppc_load_atbl(CPUPPCState *env) -{ - return cpu_ppc_get_tb(env); -} - -uint32_t cpu_ppc_load_atbu(CPUPPCState *env) -{ - return cpu_ppc_get_tb(env) >> 32; -} - -uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env) -__attribute__ (( alias ("cpu_ppc_load_tbu") )); - -uint32_t cpu_ppc601_load_rtcl(CPUPPCState *env) -{ - return cpu_ppc_load_tbl(env) & 0x3FFFFF80; -} - -/* XXX: to be fixed */ -int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) -{ - return -1; -} - -int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) -{ - return -1; -} - -static int do_store_exclusive(CPUPPCState *env) -{ - target_ulong addr; - target_ulong page_addr; - target_ulong val, val2 __attribute__((unused)) = 0; - int flags; - int segv = 0; - - addr = env->reserve_ea; - page_addr = addr & TARGET_PAGE_MASK; - start_exclusive(); - mmap_lock(); - flags = page_get_flags(page_addr); - if ((flags & PAGE_READ) == 0) { - segv = 1; - } else { - int reg = env->reserve_info & 0x1f; - int size = env->reserve_info >> 5; - int stored = 0; - - if (addr == env->reserve_addr) { - switch (size) { - case 1: segv = get_user_u8(val, addr); break; - case 2: segv = get_user_u16(val, addr); break; - case 4: segv = get_user_u32(val, addr); break; -#if defined(TARGET_PPC64) - case 8: segv = get_user_u64(val, addr); break; - case 16: { - segv = get_user_u64(val, addr); - if (!segv) { - segv = get_user_u64(val2, addr + 8); - } - break; - } -#endif - default: abort(); - } - if (!segv && val == env->reserve_val) { - val = env->gpr[reg]; - switch (size) { - case 1: segv = put_user_u8(val, addr); break; - case 2: segv = put_user_u16(val, addr); break; - case 4: segv = put_user_u32(val, addr); break; -#if defined(TARGET_PPC64) - case 8: segv = put_user_u64(val, addr); break; - case 16: { - if (val2 == env->reserve_val2) { - if (msr_le) { - val2 = val; - val = env->gpr[reg+1]; - } else { - val2 = env->gpr[reg+1]; - } - segv = put_user_u64(val, addr); - if (!segv) { - segv = put_user_u64(val2, addr + 8); - } - } - break; - } -#endif - default: abort(); - } - if (!segv) { - stored = 1; - } - } - } - env->crf[0] = (stored << 1) | xer_so; - env->reserve_addr = (target_ulong)-1; - } - if (!segv) { - env->nip += 4; - } - mmap_unlock(); - end_exclusive(); - return segv; -} - -void cpu_loop(CPUPPCState *env) -{ - CPUState *cs = CPU(ppc_env_get_cpu(env)); - target_siginfo_t info; - int trapnr; - target_ulong ret; - - for(;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch(trapnr) { - case POWERPC_EXCP_NONE: - /* Just go on */ - break; - case POWERPC_EXCP_CRITICAL: /* Critical input */ - cpu_abort(cs, "Critical interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_MCHECK: /* Machine check exception */ - cpu_abort(cs, "Machine check exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_DSI: /* Data storage exception */ - /* XXX: check this. Seems bugged */ - switch (env->error_code & 0xFF000000) { - case 0x40000000: - case 0x42000000: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - break; - case 0x04000000: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLADR; - break; - case 0x08000000: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_ACCERR; - break; - default: - /* Let's send a regular segfault... */ - EXCP_DUMP(env, "Invalid segfault errno (%02x)\n", - env->error_code); - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - break; - } - info._sifields._sigfault._addr = env->spr[SPR_DAR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_ISI: /* Instruction storage exception */ - /* XXX: check this */ - switch (env->error_code & 0xFF000000) { - case 0x40000000: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - break; - case 0x10000000: - case 0x08000000: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_ACCERR; - break; - default: - /* Let's send a regular segfault... */ - EXCP_DUMP(env, "Invalid segfault errno (%02x)\n", - env->error_code); - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - break; - } - info._sifields._sigfault._addr = env->nip - 4; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_EXTERNAL: /* External input */ - cpu_abort(cs, "External interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_ALIGN: /* Alignment exception */ - /* XXX: check this */ - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_BUS_ADRALN; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_PROGRAM: /* Program exception */ - case POWERPC_EXCP_HV_EMU: /* HV emulation */ - /* XXX: check this */ - switch (env->error_code & ~0xF) { - case POWERPC_EXCP_FP: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - switch (env->error_code & 0xF) { - case POWERPC_EXCP_FP_OX: - info.si_code = TARGET_FPE_FLTOVF; - break; - case POWERPC_EXCP_FP_UX: - info.si_code = TARGET_FPE_FLTUND; - break; - case POWERPC_EXCP_FP_ZX: - case POWERPC_EXCP_FP_VXZDZ: - info.si_code = TARGET_FPE_FLTDIV; - break; - case POWERPC_EXCP_FP_XX: - info.si_code = TARGET_FPE_FLTRES; - break; - case POWERPC_EXCP_FP_VXSOFT: - info.si_code = TARGET_FPE_FLTINV; - break; - case POWERPC_EXCP_FP_VXSNAN: - case POWERPC_EXCP_FP_VXISI: - case POWERPC_EXCP_FP_VXIDI: - case POWERPC_EXCP_FP_VXIMZ: - case POWERPC_EXCP_FP_VXVC: - case POWERPC_EXCP_FP_VXSQRT: - case POWERPC_EXCP_FP_VXCVI: - info.si_code = TARGET_FPE_FLTSUB; - break; - default: - EXCP_DUMP(env, "Unknown floating point exception (%02x)\n", - env->error_code); - break; - } - break; - case POWERPC_EXCP_INVAL: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - switch (env->error_code & 0xF) { - case POWERPC_EXCP_INVAL_INVAL: - info.si_code = TARGET_ILL_ILLOPC; - break; - case POWERPC_EXCP_INVAL_LSWX: - info.si_code = TARGET_ILL_ILLOPN; - break; - case POWERPC_EXCP_INVAL_SPR: - info.si_code = TARGET_ILL_PRVREG; - break; - case POWERPC_EXCP_INVAL_FP: - info.si_code = TARGET_ILL_COPROC; - break; - default: - EXCP_DUMP(env, "Unknown invalid operation (%02x)\n", - env->error_code & 0xF); - info.si_code = TARGET_ILL_ILLADR; - break; - } - break; - case POWERPC_EXCP_PRIV: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - switch (env->error_code & 0xF) { - case POWERPC_EXCP_PRIV_OPC: - info.si_code = TARGET_ILL_PRVOPC; - break; - case POWERPC_EXCP_PRIV_REG: - info.si_code = TARGET_ILL_PRVREG; - break; - default: - EXCP_DUMP(env, "Unknown privilege violation (%02x)\n", - env->error_code & 0xF); - info.si_code = TARGET_ILL_PRVOPC; - break; - } - break; - case POWERPC_EXCP_TRAP: - cpu_abort(cs, "Tried to call a TRAP\n"); - break; - default: - /* Should not happen ! */ - cpu_abort(cs, "Unknown program exception (%02x)\n", - env->error_code); - break; - } - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_SYSCALL: /* System call exception */ - cpu_abort(cs, "Syscall exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_DECR: /* Decrementer exception */ - cpu_abort(cs, "Decrementer interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ - cpu_abort(cs, "Fix interval timer interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ - cpu_abort(cs, "Watchdog timer interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_DTLB: /* Data TLB error */ - cpu_abort(cs, "Data TLB exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_ITLB: /* Instruction TLB error */ - cpu_abort(cs, "Instruction TLB exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavail. */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_EFPDI: /* Embedded floating-point data IRQ */ - cpu_abort(cs, "Embedded floating-point data IRQ not handled\n"); - break; - case POWERPC_EXCP_EFPRI: /* Embedded floating-point round IRQ */ - cpu_abort(cs, "Embedded floating-point round IRQ not handled\n"); - break; - case POWERPC_EXCP_EPERFM: /* Embedded performance monitor IRQ */ - cpu_abort(cs, "Performance monitor exception not handled\n"); - break; - case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - cpu_abort(cs, "Doorbell interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ - cpu_abort(cs, "Doorbell critical interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_RESET: /* System reset exception */ - cpu_abort(cs, "Reset interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_DSEG: /* Data segment exception */ - cpu_abort(cs, "Data segment exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - cpu_abort(cs, "Instruction segment exception " - "while in user mode. Aborting\n"); - break; - /* PowerPC 64 with hypervisor mode support */ - case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ - cpu_abort(cs, "Hypervisor decrementer interrupt " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_TRACE: /* Trace exception */ - /* Nothing to do: - * we use this exception to emulate step-by-step execution mode. - */ - break; - /* PowerPC 64 with hypervisor mode support */ - case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - cpu_abort(cs, "Hypervisor data storage exception " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_HISI: /* Hypervisor instruction storage excp */ - cpu_abort(cs, "Hypervisor instruction storage exception " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - cpu_abort(cs, "Hypervisor data segment exception " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment excp */ - cpu_abort(cs, "Hypervisor instruction segment exception " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_PIT: /* Programmable interval timer IRQ */ - cpu_abort(cs, "Programmable interval timer interrupt " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_IO: /* IO error exception */ - cpu_abort(cs, "IO error exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_RUNM: /* Run mode exception */ - cpu_abort(cs, "Run mode exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_EMUL: /* Emulation trap exception */ - cpu_abort(cs, "Emulation trap exception not handled\n"); - break; - case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - cpu_abort(cs, "Instruction fetch TLB exception " - "while in user-mode. Aborting"); - break; - case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - cpu_abort(cs, "Data load TLB exception while in user-mode. " - "Aborting"); - break; - case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - cpu_abort(cs, "Data store TLB exception while in user-mode. " - "Aborting"); - break; - case POWERPC_EXCP_FPA: /* Floating-point assist exception */ - cpu_abort(cs, "Floating-point assist exception not handled\n"); - break; - case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ - cpu_abort(cs, "Instruction address breakpoint exception " - "not handled\n"); - break; - case POWERPC_EXCP_SMI: /* System management interrupt */ - cpu_abort(cs, "System management interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_THERM: /* Thermal interrupt */ - cpu_abort(cs, "Thermal interrupt interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_PERFM: /* Embedded performance monitor IRQ */ - cpu_abort(cs, "Performance monitor exception not handled\n"); - break; - case POWERPC_EXCP_VPUA: /* Vector assist exception */ - cpu_abort(cs, "Vector assist exception not handled\n"); - break; - case POWERPC_EXCP_SOFTP: /* Soft patch exception */ - cpu_abort(cs, "Soft patch exception not handled\n"); - break; - case POWERPC_EXCP_MAINT: /* Maintenance exception */ - cpu_abort(cs, "Maintenance exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_STOP: /* stop translation */ - /* We did invalidate the instruction cache. Go on */ - break; - case POWERPC_EXCP_BRANCH: /* branch instruction: */ - /* We just stopped because of a branch. Go on */ - break; - case POWERPC_EXCP_SYSCALL_USER: - /* system call in user-mode emulation */ - /* WARNING: - * PPC ABI uses overflow flag in cr0 to signal an error - * in syscalls. - */ - env->crf[0] &= ~0x1; - env->nip += 4; - ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4], - env->gpr[5], env->gpr[6], env->gpr[7], - env->gpr[8], 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->nip -= 4; - break; - } - if (ret == (target_ulong)(-TARGET_QEMU_ESIGRETURN)) { - /* Returning from a successful sigreturn syscall. - Avoid corrupting register state. */ - break; - } - if (ret > (target_ulong)(-515)) { - env->crf[0] |= 0x1; - ret = -ret; - } - env->gpr[3] = ret; - break; - case POWERPC_EXCP_STCX: - if (do_store_exclusive(env)) { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - cpu_abort(cs, "Unknown exception 0x%x. Aborting\n", trapnr); - break; - } - process_pending_signals(env); - } -} -#endif - #ifdef TARGET_MIPS # ifdef TARGET_ABI_MIPSO32 @@ -3727,24 +3186,7 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); -#if defined(TARGET_PPC) - { - int i; - -#if defined(TARGET_PPC64) - int flag = (env->insns_flags2 & PPC2_BOOKE206) ? MSR_CM : MSR_SF; -#if defined(TARGET_ABI32) - env->msr &= ~((target_ulong)1 << flag); -#else - env->msr |= (target_ulong)1 << flag; -#endif -#endif - env->nip = regs->nip; - for(i = 0; i < 32; i++) { - env->gpr[i] = regs->gpr[i]; - } - } -#elif defined(TARGET_M68K) +#if defined(TARGET_M68K) { env->pc = regs->pc; env->dregs[0] = regs->d0; diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index b7700a5561..2fb516cb00 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -21,6 +21,559 @@ #include "qemu.h" #include "cpu_loop-common.h" +static inline uint64_t cpu_ppc_get_tb(CPUPPCState *env) +{ + return cpu_get_host_ticks(); +} + +uint64_t cpu_ppc_load_tbl(CPUPPCState *env) +{ + return cpu_ppc_get_tb(env); +} + +uint32_t cpu_ppc_load_tbu(CPUPPCState *env) +{ + return cpu_ppc_get_tb(env) >> 32; +} + +uint64_t cpu_ppc_load_atbl(CPUPPCState *env) +{ + return cpu_ppc_get_tb(env); +} + +uint32_t cpu_ppc_load_atbu(CPUPPCState *env) +{ + return cpu_ppc_get_tb(env) >> 32; +} + +uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env) +__attribute__ (( alias ("cpu_ppc_load_tbu") )); + +uint32_t cpu_ppc601_load_rtcl(CPUPPCState *env) +{ + return cpu_ppc_load_tbl(env) & 0x3FFFFF80; +} + +/* XXX: to be fixed */ +int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) +{ + return -1; +} + +int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) +{ + return -1; +} + +static int do_store_exclusive(CPUPPCState *env) +{ + target_ulong addr; + target_ulong page_addr; + target_ulong val, val2 __attribute__((unused)) = 0; + int flags; + int segv = 0; + + addr = env->reserve_ea; + page_addr = addr & TARGET_PAGE_MASK; + start_exclusive(); + mmap_lock(); + flags = page_get_flags(page_addr); + if ((flags & PAGE_READ) == 0) { + segv = 1; + } else { + int reg = env->reserve_info & 0x1f; + int size = env->reserve_info >> 5; + int stored = 0; + + if (addr == env->reserve_addr) { + switch (size) { + case 1: segv = get_user_u8(val, addr); break; + case 2: segv = get_user_u16(val, addr); break; + case 4: segv = get_user_u32(val, addr); break; +#if defined(TARGET_PPC64) + case 8: segv = get_user_u64(val, addr); break; + case 16: { + segv = get_user_u64(val, addr); + if (!segv) { + segv = get_user_u64(val2, addr + 8); + } + break; + } +#endif + default: abort(); + } + if (!segv && val == env->reserve_val) { + val = env->gpr[reg]; + switch (size) { + case 1: segv = put_user_u8(val, addr); break; + case 2: segv = put_user_u16(val, addr); break; + case 4: segv = put_user_u32(val, addr); break; +#if defined(TARGET_PPC64) + case 8: segv = put_user_u64(val, addr); break; + case 16: { + if (val2 == env->reserve_val2) { + if (msr_le) { + val2 = val; + val = env->gpr[reg+1]; + } else { + val2 = env->gpr[reg+1]; + } + segv = put_user_u64(val, addr); + if (!segv) { + segv = put_user_u64(val2, addr + 8); + } + } + break; + } +#endif + default: abort(); + } + if (!segv) { + stored = 1; + } + } + } + env->crf[0] = (stored << 1) | xer_so; + env->reserve_addr = (target_ulong)-1; + } + if (!segv) { + env->nip += 4; + } + mmap_unlock(); + end_exclusive(); + return segv; +} + +void cpu_loop(CPUPPCState *env) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + target_siginfo_t info; + int trapnr; + target_ulong ret; + + for(;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch(trapnr) { + case POWERPC_EXCP_NONE: + /* Just go on */ + break; + case POWERPC_EXCP_CRITICAL: /* Critical input */ + cpu_abort(cs, "Critical interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + cpu_abort(cs, "Machine check exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + /* XXX: check this. Seems bugged */ + switch (env->error_code & 0xFF000000) { + case 0x40000000: + case 0x42000000: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + break; + case 0x04000000: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLADR; + break; + case 0x08000000: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_ACCERR; + break; + default: + /* Let's send a regular segfault... */ + EXCP_DUMP(env, "Invalid segfault errno (%02x)\n", + env->error_code); + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + break; + } + info._sifields._sigfault._addr = env->spr[SPR_DAR]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + /* XXX: check this */ + switch (env->error_code & 0xFF000000) { + case 0x40000000: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + break; + case 0x10000000: + case 0x08000000: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_ACCERR; + break; + default: + /* Let's send a regular segfault... */ + EXCP_DUMP(env, "Invalid segfault errno (%02x)\n", + env->error_code); + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + break; + } + info._sifields._sigfault._addr = env->nip - 4; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_EXTERNAL: /* External input */ + cpu_abort(cs, "External interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + /* XXX: check this */ + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_BUS_ADRALN; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + case POWERPC_EXCP_HV_EMU: /* HV emulation */ + /* XXX: check this */ + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_FP: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + switch (env->error_code & 0xF) { + case POWERPC_EXCP_FP_OX: + info.si_code = TARGET_FPE_FLTOVF; + break; + case POWERPC_EXCP_FP_UX: + info.si_code = TARGET_FPE_FLTUND; + break; + case POWERPC_EXCP_FP_ZX: + case POWERPC_EXCP_FP_VXZDZ: + info.si_code = TARGET_FPE_FLTDIV; + break; + case POWERPC_EXCP_FP_XX: + info.si_code = TARGET_FPE_FLTRES; + break; + case POWERPC_EXCP_FP_VXSOFT: + info.si_code = TARGET_FPE_FLTINV; + break; + case POWERPC_EXCP_FP_VXSNAN: + case POWERPC_EXCP_FP_VXISI: + case POWERPC_EXCP_FP_VXIDI: + case POWERPC_EXCP_FP_VXIMZ: + case POWERPC_EXCP_FP_VXVC: + case POWERPC_EXCP_FP_VXSQRT: + case POWERPC_EXCP_FP_VXCVI: + info.si_code = TARGET_FPE_FLTSUB; + break; + default: + EXCP_DUMP(env, "Unknown floating point exception (%02x)\n", + env->error_code); + break; + } + break; + case POWERPC_EXCP_INVAL: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + switch (env->error_code & 0xF) { + case POWERPC_EXCP_INVAL_INVAL: + info.si_code = TARGET_ILL_ILLOPC; + break; + case POWERPC_EXCP_INVAL_LSWX: + info.si_code = TARGET_ILL_ILLOPN; + break; + case POWERPC_EXCP_INVAL_SPR: + info.si_code = TARGET_ILL_PRVREG; + break; + case POWERPC_EXCP_INVAL_FP: + info.si_code = TARGET_ILL_COPROC; + break; + default: + EXCP_DUMP(env, "Unknown invalid operation (%02x)\n", + env->error_code & 0xF); + info.si_code = TARGET_ILL_ILLADR; + break; + } + break; + case POWERPC_EXCP_PRIV: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + switch (env->error_code & 0xF) { + case POWERPC_EXCP_PRIV_OPC: + info.si_code = TARGET_ILL_PRVOPC; + break; + case POWERPC_EXCP_PRIV_REG: + info.si_code = TARGET_ILL_PRVREG; + break; + default: + EXCP_DUMP(env, "Unknown privilege violation (%02x)\n", + env->error_code & 0xF); + info.si_code = TARGET_ILL_PRVOPC; + break; + } + break; + case POWERPC_EXCP_TRAP: + cpu_abort(cs, "Tried to call a TRAP\n"); + break; + default: + /* Should not happen ! */ + cpu_abort(cs, "Unknown program exception (%02x)\n", + env->error_code); + break; + } + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_SYSCALL: /* System call exception */ + cpu_abort(cs, "Syscall exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_DECR: /* Decrementer exception */ + cpu_abort(cs, "Decrementer interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + cpu_abort(cs, "Fix interval timer interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + cpu_abort(cs, "Watchdog timer interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + cpu_abort(cs, "Data TLB exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + cpu_abort(cs, "Instruction TLB exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavail. */ + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data IRQ */ + cpu_abort(cs, "Embedded floating-point data IRQ not handled\n"); + break; + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round IRQ */ + cpu_abort(cs, "Embedded floating-point round IRQ not handled\n"); + break; + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor IRQ */ + cpu_abort(cs, "Performance monitor exception not handled\n"); + break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + cpu_abort(cs, "Doorbell interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + cpu_abort(cs, "Doorbell critical interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + cpu_abort(cs, "Reset interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DSEG: /* Data segment exception */ + cpu_abort(cs, "Data segment exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + cpu_abort(cs, "Instruction segment exception " + "while in user mode. Aborting\n"); + break; + /* PowerPC 64 with hypervisor mode support */ + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + cpu_abort(cs, "Hypervisor decrementer interrupt " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_TRACE: /* Trace exception */ + /* Nothing to do: + * we use this exception to emulate step-by-step execution mode. + */ + break; + /* PowerPC 64 with hypervisor mode support */ + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + cpu_abort(cs, "Hypervisor data storage exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage excp */ + cpu_abort(cs, "Hypervisor instruction storage exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + cpu_abort(cs, "Hypervisor data segment exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment excp */ + cpu_abort(cs, "Hypervisor instruction segment exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_PIT: /* Programmable interval timer IRQ */ + cpu_abort(cs, "Programmable interval timer interrupt " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_IO: /* IO error exception */ + cpu_abort(cs, "IO error exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_RUNM: /* Run mode exception */ + cpu_abort(cs, "Run mode exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_EMUL: /* Emulation trap exception */ + cpu_abort(cs, "Emulation trap exception not handled\n"); + break; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + cpu_abort(cs, "Instruction fetch TLB exception " + "while in user-mode. Aborting"); + break; + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + cpu_abort(cs, "Data load TLB exception while in user-mode. " + "Aborting"); + break; + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + cpu_abort(cs, "Data store TLB exception while in user-mode. " + "Aborting"); + break; + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + cpu_abort(cs, "Floating-point assist exception not handled\n"); + break; + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + cpu_abort(cs, "Instruction address breakpoint exception " + "not handled\n"); + break; + case POWERPC_EXCP_SMI: /* System management interrupt */ + cpu_abort(cs, "System management interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + cpu_abort(cs, "Thermal interrupt interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_PERFM: /* Embedded performance monitor IRQ */ + cpu_abort(cs, "Performance monitor exception not handled\n"); + break; + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + cpu_abort(cs, "Vector assist exception not handled\n"); + break; + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + cpu_abort(cs, "Soft patch exception not handled\n"); + break; + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + cpu_abort(cs, "Maintenance exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_STOP: /* stop translation */ + /* We did invalidate the instruction cache. Go on */ + break; + case POWERPC_EXCP_BRANCH: /* branch instruction: */ + /* We just stopped because of a branch. Go on */ + break; + case POWERPC_EXCP_SYSCALL_USER: + /* system call in user-mode emulation */ + /* WARNING: + * PPC ABI uses overflow flag in cr0 to signal an error + * in syscalls. + */ + env->crf[0] &= ~0x1; + env->nip += 4; + ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4], + env->gpr[5], env->gpr[6], env->gpr[7], + env->gpr[8], 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->nip -= 4; + break; + } + if (ret == (target_ulong)(-TARGET_QEMU_ESIGRETURN)) { + /* Returning from a successful sigreturn syscall. + Avoid corrupting register state. */ + break; + } + if (ret > (target_ulong)(-515)) { + env->crf[0] |= 0x1; + ret = -ret; + } + env->gpr[3] = ret; + break; + case POWERPC_EXCP_STCX: + if (do_store_exclusive(env)) { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + cpu_abort(cs, "Unknown exception 0x%x. Aborting\n", trapnr); + break; + } + process_pending_signals(env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + int i; + +#if defined(TARGET_PPC64) + int flag = (env->insns_flags2 & PPC2_BOOKE206) ? MSR_CM : MSR_SF; +#if defined(TARGET_ABI32) + env->msr &= ~((target_ulong)1 << flag); +#else + env->msr |= (target_ulong)1 << flag; +#endif +#endif + env->nip = regs->nip; + for(i = 0; i < 32; i++) { + env->gpr[i] = regs->gpr[i]; + } } From 58908ef60376bc67b447d4c764444a4b0660fa23 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:39 +0200 Subject: [PATCH 0152/2380] linux-user: move mips/mips64 cpu loop to mips directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from main.c to mips/cpu_loop.c. Include mips/cpu_loop.c in mips64/cpu_loop.c to avoid to duplicate code. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180411185651.21351-8-laurent@vivier.eu> --- linux-user/main.c | 725 ----------------------------------- linux-user/mips/cpu_loop.c | 723 ++++++++++++++++++++++++++++++++++ linux-user/mips64/cpu_loop.c | 8 +- 3 files changed, 724 insertions(+), 732 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 2340320818..a760c19379 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,705 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_MIPS - -# ifdef TARGET_ABI_MIPSO32 -# define MIPS_SYS(name, args) args, -static const uint8_t mips_syscall_args[] = { - MIPS_SYS(sys_syscall , 8) /* 4000 */ - MIPS_SYS(sys_exit , 1) - MIPS_SYS(sys_fork , 0) - MIPS_SYS(sys_read , 3) - MIPS_SYS(sys_write , 3) - MIPS_SYS(sys_open , 3) /* 4005 */ - MIPS_SYS(sys_close , 1) - MIPS_SYS(sys_waitpid , 3) - MIPS_SYS(sys_creat , 2) - MIPS_SYS(sys_link , 2) - MIPS_SYS(sys_unlink , 1) /* 4010 */ - MIPS_SYS(sys_execve , 0) - MIPS_SYS(sys_chdir , 1) - MIPS_SYS(sys_time , 1) - MIPS_SYS(sys_mknod , 3) - MIPS_SYS(sys_chmod , 2) /* 4015 */ - MIPS_SYS(sys_lchown , 3) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_stat */ - MIPS_SYS(sys_lseek , 3) - MIPS_SYS(sys_getpid , 0) /* 4020 */ - MIPS_SYS(sys_mount , 5) - MIPS_SYS(sys_umount , 1) - MIPS_SYS(sys_setuid , 1) - MIPS_SYS(sys_getuid , 0) - MIPS_SYS(sys_stime , 1) /* 4025 */ - MIPS_SYS(sys_ptrace , 4) - MIPS_SYS(sys_alarm , 1) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_fstat */ - MIPS_SYS(sys_pause , 0) - MIPS_SYS(sys_utime , 2) /* 4030 */ - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_access , 2) - MIPS_SYS(sys_nice , 1) - MIPS_SYS(sys_ni_syscall , 0) /* 4035 */ - MIPS_SYS(sys_sync , 0) - MIPS_SYS(sys_kill , 2) - MIPS_SYS(sys_rename , 2) - MIPS_SYS(sys_mkdir , 2) - MIPS_SYS(sys_rmdir , 1) /* 4040 */ - MIPS_SYS(sys_dup , 1) - MIPS_SYS(sys_pipe , 0) - MIPS_SYS(sys_times , 1) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_brk , 1) /* 4045 */ - MIPS_SYS(sys_setgid , 1) - MIPS_SYS(sys_getgid , 0) - MIPS_SYS(sys_ni_syscall , 0) /* was signal(2) */ - MIPS_SYS(sys_geteuid , 0) - MIPS_SYS(sys_getegid , 0) /* 4050 */ - MIPS_SYS(sys_acct , 0) - MIPS_SYS(sys_umount2 , 2) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ioctl , 3) - MIPS_SYS(sys_fcntl , 3) /* 4055 */ - MIPS_SYS(sys_ni_syscall , 2) - MIPS_SYS(sys_setpgid , 2) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_olduname , 1) - MIPS_SYS(sys_umask , 1) /* 4060 */ - MIPS_SYS(sys_chroot , 1) - MIPS_SYS(sys_ustat , 2) - MIPS_SYS(sys_dup2 , 2) - MIPS_SYS(sys_getppid , 0) - MIPS_SYS(sys_getpgrp , 0) /* 4065 */ - MIPS_SYS(sys_setsid , 0) - MIPS_SYS(sys_sigaction , 3) - MIPS_SYS(sys_sgetmask , 0) - MIPS_SYS(sys_ssetmask , 1) - MIPS_SYS(sys_setreuid , 2) /* 4070 */ - MIPS_SYS(sys_setregid , 2) - MIPS_SYS(sys_sigsuspend , 0) - MIPS_SYS(sys_sigpending , 1) - MIPS_SYS(sys_sethostname , 2) - MIPS_SYS(sys_setrlimit , 2) /* 4075 */ - MIPS_SYS(sys_getrlimit , 2) - MIPS_SYS(sys_getrusage , 2) - MIPS_SYS(sys_gettimeofday, 2) - MIPS_SYS(sys_settimeofday, 2) - MIPS_SYS(sys_getgroups , 2) /* 4080 */ - MIPS_SYS(sys_setgroups , 2) - MIPS_SYS(sys_ni_syscall , 0) /* old_select */ - MIPS_SYS(sys_symlink , 2) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_lstat */ - MIPS_SYS(sys_readlink , 3) /* 4085 */ - MIPS_SYS(sys_uselib , 1) - MIPS_SYS(sys_swapon , 2) - MIPS_SYS(sys_reboot , 3) - MIPS_SYS(old_readdir , 3) - MIPS_SYS(old_mmap , 6) /* 4090 */ - MIPS_SYS(sys_munmap , 2) - MIPS_SYS(sys_truncate , 2) - MIPS_SYS(sys_ftruncate , 2) - MIPS_SYS(sys_fchmod , 2) - MIPS_SYS(sys_fchown , 3) /* 4095 */ - MIPS_SYS(sys_getpriority , 2) - MIPS_SYS(sys_setpriority , 3) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_statfs , 2) - MIPS_SYS(sys_fstatfs , 2) /* 4100 */ - MIPS_SYS(sys_ni_syscall , 0) /* was ioperm(2) */ - MIPS_SYS(sys_socketcall , 2) - MIPS_SYS(sys_syslog , 3) - MIPS_SYS(sys_setitimer , 3) - MIPS_SYS(sys_getitimer , 2) /* 4105 */ - MIPS_SYS(sys_newstat , 2) - MIPS_SYS(sys_newlstat , 2) - MIPS_SYS(sys_newfstat , 2) - MIPS_SYS(sys_uname , 1) - MIPS_SYS(sys_ni_syscall , 0) /* 4110 was iopl(2) */ - MIPS_SYS(sys_vhangup , 0) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_idle() */ - MIPS_SYS(sys_ni_syscall , 0) /* was sys_vm86 */ - MIPS_SYS(sys_wait4 , 4) - MIPS_SYS(sys_swapoff , 1) /* 4115 */ - MIPS_SYS(sys_sysinfo , 1) - MIPS_SYS(sys_ipc , 6) - MIPS_SYS(sys_fsync , 1) - MIPS_SYS(sys_sigreturn , 0) - MIPS_SYS(sys_clone , 6) /* 4120 */ - MIPS_SYS(sys_setdomainname, 2) - MIPS_SYS(sys_newuname , 1) - MIPS_SYS(sys_ni_syscall , 0) /* sys_modify_ldt */ - MIPS_SYS(sys_adjtimex , 1) - MIPS_SYS(sys_mprotect , 3) /* 4125 */ - MIPS_SYS(sys_sigprocmask , 3) - MIPS_SYS(sys_ni_syscall , 0) /* was create_module */ - MIPS_SYS(sys_init_module , 5) - MIPS_SYS(sys_delete_module, 1) - MIPS_SYS(sys_ni_syscall , 0) /* 4130 was get_kernel_syms */ - MIPS_SYS(sys_quotactl , 0) - MIPS_SYS(sys_getpgid , 1) - MIPS_SYS(sys_fchdir , 1) - MIPS_SYS(sys_bdflush , 2) - MIPS_SYS(sys_sysfs , 3) /* 4135 */ - MIPS_SYS(sys_personality , 1) - MIPS_SYS(sys_ni_syscall , 0) /* for afs_syscall */ - MIPS_SYS(sys_setfsuid , 1) - MIPS_SYS(sys_setfsgid , 1) - MIPS_SYS(sys_llseek , 5) /* 4140 */ - MIPS_SYS(sys_getdents , 3) - MIPS_SYS(sys_select , 5) - MIPS_SYS(sys_flock , 2) - MIPS_SYS(sys_msync , 3) - MIPS_SYS(sys_readv , 3) /* 4145 */ - MIPS_SYS(sys_writev , 3) - MIPS_SYS(sys_cacheflush , 3) - MIPS_SYS(sys_cachectl , 3) - MIPS_SYS(sys_sysmips , 4) - MIPS_SYS(sys_ni_syscall , 0) /* 4150 */ - MIPS_SYS(sys_getsid , 1) - MIPS_SYS(sys_fdatasync , 0) - MIPS_SYS(sys_sysctl , 1) - MIPS_SYS(sys_mlock , 2) - MIPS_SYS(sys_munlock , 2) /* 4155 */ - MIPS_SYS(sys_mlockall , 1) - MIPS_SYS(sys_munlockall , 0) - MIPS_SYS(sys_sched_setparam, 2) - MIPS_SYS(sys_sched_getparam, 2) - MIPS_SYS(sys_sched_setscheduler, 3) /* 4160 */ - MIPS_SYS(sys_sched_getscheduler, 1) - MIPS_SYS(sys_sched_yield , 0) - MIPS_SYS(sys_sched_get_priority_max, 1) - MIPS_SYS(sys_sched_get_priority_min, 1) - MIPS_SYS(sys_sched_rr_get_interval, 2) /* 4165 */ - MIPS_SYS(sys_nanosleep, 2) - MIPS_SYS(sys_mremap , 5) - MIPS_SYS(sys_accept , 3) - MIPS_SYS(sys_bind , 3) - MIPS_SYS(sys_connect , 3) /* 4170 */ - MIPS_SYS(sys_getpeername , 3) - MIPS_SYS(sys_getsockname , 3) - MIPS_SYS(sys_getsockopt , 5) - MIPS_SYS(sys_listen , 2) - MIPS_SYS(sys_recv , 4) /* 4175 */ - MIPS_SYS(sys_recvfrom , 6) - MIPS_SYS(sys_recvmsg , 3) - MIPS_SYS(sys_send , 4) - MIPS_SYS(sys_sendmsg , 3) - MIPS_SYS(sys_sendto , 6) /* 4180 */ - MIPS_SYS(sys_setsockopt , 5) - MIPS_SYS(sys_shutdown , 2) - MIPS_SYS(sys_socket , 3) - MIPS_SYS(sys_socketpair , 4) - MIPS_SYS(sys_setresuid , 3) /* 4185 */ - MIPS_SYS(sys_getresuid , 3) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_query_module */ - MIPS_SYS(sys_poll , 3) - MIPS_SYS(sys_nfsservctl , 3) - MIPS_SYS(sys_setresgid , 3) /* 4190 */ - MIPS_SYS(sys_getresgid , 3) - MIPS_SYS(sys_prctl , 5) - MIPS_SYS(sys_rt_sigreturn, 0) - MIPS_SYS(sys_rt_sigaction, 4) - MIPS_SYS(sys_rt_sigprocmask, 4) /* 4195 */ - MIPS_SYS(sys_rt_sigpending, 2) - MIPS_SYS(sys_rt_sigtimedwait, 4) - MIPS_SYS(sys_rt_sigqueueinfo, 3) - MIPS_SYS(sys_rt_sigsuspend, 0) - MIPS_SYS(sys_pread64 , 6) /* 4200 */ - MIPS_SYS(sys_pwrite64 , 6) - MIPS_SYS(sys_chown , 3) - MIPS_SYS(sys_getcwd , 2) - MIPS_SYS(sys_capget , 2) - MIPS_SYS(sys_capset , 2) /* 4205 */ - MIPS_SYS(sys_sigaltstack , 2) - MIPS_SYS(sys_sendfile , 4) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_mmap2 , 6) /* 4210 */ - MIPS_SYS(sys_truncate64 , 4) - MIPS_SYS(sys_ftruncate64 , 4) - MIPS_SYS(sys_stat64 , 2) - MIPS_SYS(sys_lstat64 , 2) - MIPS_SYS(sys_fstat64 , 2) /* 4215 */ - MIPS_SYS(sys_pivot_root , 2) - MIPS_SYS(sys_mincore , 3) - MIPS_SYS(sys_madvise , 3) - MIPS_SYS(sys_getdents64 , 3) - MIPS_SYS(sys_fcntl64 , 3) /* 4220 */ - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_gettid , 0) - MIPS_SYS(sys_readahead , 5) - MIPS_SYS(sys_setxattr , 5) - MIPS_SYS(sys_lsetxattr , 5) /* 4225 */ - MIPS_SYS(sys_fsetxattr , 5) - MIPS_SYS(sys_getxattr , 4) - MIPS_SYS(sys_lgetxattr , 4) - MIPS_SYS(sys_fgetxattr , 4) - MIPS_SYS(sys_listxattr , 3) /* 4230 */ - MIPS_SYS(sys_llistxattr , 3) - MIPS_SYS(sys_flistxattr , 3) - MIPS_SYS(sys_removexattr , 2) - MIPS_SYS(sys_lremovexattr, 2) - MIPS_SYS(sys_fremovexattr, 2) /* 4235 */ - MIPS_SYS(sys_tkill , 2) - MIPS_SYS(sys_sendfile64 , 5) - MIPS_SYS(sys_futex , 6) - MIPS_SYS(sys_sched_setaffinity, 3) - MIPS_SYS(sys_sched_getaffinity, 3) /* 4240 */ - MIPS_SYS(sys_io_setup , 2) - MIPS_SYS(sys_io_destroy , 1) - MIPS_SYS(sys_io_getevents, 5) - MIPS_SYS(sys_io_submit , 3) - MIPS_SYS(sys_io_cancel , 3) /* 4245 */ - MIPS_SYS(sys_exit_group , 1) - MIPS_SYS(sys_lookup_dcookie, 3) - MIPS_SYS(sys_epoll_create, 1) - MIPS_SYS(sys_epoll_ctl , 4) - MIPS_SYS(sys_epoll_wait , 3) /* 4250 */ - MIPS_SYS(sys_remap_file_pages, 5) - MIPS_SYS(sys_set_tid_address, 1) - MIPS_SYS(sys_restart_syscall, 0) - MIPS_SYS(sys_fadvise64_64, 7) - MIPS_SYS(sys_statfs64 , 3) /* 4255 */ - MIPS_SYS(sys_fstatfs64 , 2) - MIPS_SYS(sys_timer_create, 3) - MIPS_SYS(sys_timer_settime, 4) - MIPS_SYS(sys_timer_gettime, 2) - MIPS_SYS(sys_timer_getoverrun, 1) /* 4260 */ - MIPS_SYS(sys_timer_delete, 1) - MIPS_SYS(sys_clock_settime, 2) - MIPS_SYS(sys_clock_gettime, 2) - MIPS_SYS(sys_clock_getres, 2) - MIPS_SYS(sys_clock_nanosleep, 4) /* 4265 */ - MIPS_SYS(sys_tgkill , 3) - MIPS_SYS(sys_utimes , 2) - MIPS_SYS(sys_mbind , 4) - MIPS_SYS(sys_ni_syscall , 0) /* sys_get_mempolicy */ - MIPS_SYS(sys_ni_syscall , 0) /* 4270 sys_set_mempolicy */ - MIPS_SYS(sys_mq_open , 4) - MIPS_SYS(sys_mq_unlink , 1) - MIPS_SYS(sys_mq_timedsend, 5) - MIPS_SYS(sys_mq_timedreceive, 5) - MIPS_SYS(sys_mq_notify , 2) /* 4275 */ - MIPS_SYS(sys_mq_getsetattr, 3) - MIPS_SYS(sys_ni_syscall , 0) /* sys_vserver */ - MIPS_SYS(sys_waitid , 4) - MIPS_SYS(sys_ni_syscall , 0) /* available, was setaltroot */ - MIPS_SYS(sys_add_key , 5) - MIPS_SYS(sys_request_key, 4) - MIPS_SYS(sys_keyctl , 5) - MIPS_SYS(sys_set_thread_area, 1) - MIPS_SYS(sys_inotify_init, 0) - MIPS_SYS(sys_inotify_add_watch, 3) /* 4285 */ - MIPS_SYS(sys_inotify_rm_watch, 2) - MIPS_SYS(sys_migrate_pages, 4) - MIPS_SYS(sys_openat, 4) - MIPS_SYS(sys_mkdirat, 3) - MIPS_SYS(sys_mknodat, 4) /* 4290 */ - MIPS_SYS(sys_fchownat, 5) - MIPS_SYS(sys_futimesat, 3) - MIPS_SYS(sys_fstatat64, 4) - MIPS_SYS(sys_unlinkat, 3) - MIPS_SYS(sys_renameat, 4) /* 4295 */ - MIPS_SYS(sys_linkat, 5) - MIPS_SYS(sys_symlinkat, 3) - MIPS_SYS(sys_readlinkat, 4) - MIPS_SYS(sys_fchmodat, 3) - MIPS_SYS(sys_faccessat, 3) /* 4300 */ - MIPS_SYS(sys_pselect6, 6) - MIPS_SYS(sys_ppoll, 5) - MIPS_SYS(sys_unshare, 1) - MIPS_SYS(sys_splice, 6) - MIPS_SYS(sys_sync_file_range, 7) /* 4305 */ - MIPS_SYS(sys_tee, 4) - MIPS_SYS(sys_vmsplice, 4) - MIPS_SYS(sys_move_pages, 6) - MIPS_SYS(sys_set_robust_list, 2) - MIPS_SYS(sys_get_robust_list, 3) /* 4310 */ - MIPS_SYS(sys_kexec_load, 4) - MIPS_SYS(sys_getcpu, 3) - MIPS_SYS(sys_epoll_pwait, 6) - MIPS_SYS(sys_ioprio_set, 3) - MIPS_SYS(sys_ioprio_get, 2) - MIPS_SYS(sys_utimensat, 4) - MIPS_SYS(sys_signalfd, 3) - MIPS_SYS(sys_ni_syscall, 0) /* was timerfd */ - MIPS_SYS(sys_eventfd, 1) - MIPS_SYS(sys_fallocate, 6) /* 4320 */ - MIPS_SYS(sys_timerfd_create, 2) - MIPS_SYS(sys_timerfd_gettime, 2) - MIPS_SYS(sys_timerfd_settime, 4) - MIPS_SYS(sys_signalfd4, 4) - MIPS_SYS(sys_eventfd2, 2) /* 4325 */ - MIPS_SYS(sys_epoll_create1, 1) - MIPS_SYS(sys_dup3, 3) - MIPS_SYS(sys_pipe2, 2) - MIPS_SYS(sys_inotify_init1, 1) - MIPS_SYS(sys_preadv, 5) /* 4330 */ - MIPS_SYS(sys_pwritev, 5) - MIPS_SYS(sys_rt_tgsigqueueinfo, 4) - MIPS_SYS(sys_perf_event_open, 5) - MIPS_SYS(sys_accept4, 4) - MIPS_SYS(sys_recvmmsg, 5) /* 4335 */ - MIPS_SYS(sys_fanotify_init, 2) - MIPS_SYS(sys_fanotify_mark, 6) - MIPS_SYS(sys_prlimit64, 4) - MIPS_SYS(sys_name_to_handle_at, 5) - MIPS_SYS(sys_open_by_handle_at, 3) /* 4340 */ - MIPS_SYS(sys_clock_adjtime, 2) - MIPS_SYS(sys_syncfs, 1) - MIPS_SYS(sys_sendmmsg, 4) - MIPS_SYS(sys_setns, 2) - MIPS_SYS(sys_process_vm_readv, 6) /* 345 */ - MIPS_SYS(sys_process_vm_writev, 6) - MIPS_SYS(sys_kcmp, 5) - MIPS_SYS(sys_finit_module, 3) - MIPS_SYS(sys_sched_setattr, 2) - MIPS_SYS(sys_sched_getattr, 3) /* 350 */ - MIPS_SYS(sys_renameat2, 5) - MIPS_SYS(sys_seccomp, 3) - MIPS_SYS(sys_getrandom, 3) - MIPS_SYS(sys_memfd_create, 2) - MIPS_SYS(sys_bpf, 3) /* 355 */ - MIPS_SYS(sys_execveat, 5) - MIPS_SYS(sys_userfaultfd, 1) - MIPS_SYS(sys_membarrier, 2) - MIPS_SYS(sys_mlock2, 3) - MIPS_SYS(sys_copy_file_range, 6) /* 360 */ - MIPS_SYS(sys_preadv2, 6) - MIPS_SYS(sys_pwritev2, 6) -}; -# undef MIPS_SYS -# endif /* O32 */ - -static int do_store_exclusive(CPUMIPSState *env) -{ - target_ulong addr; - target_ulong page_addr; - target_ulong val; - int flags; - int segv = 0; - int reg; - int d; - - addr = env->lladdr; - page_addr = addr & TARGET_PAGE_MASK; - start_exclusive(); - mmap_lock(); - flags = page_get_flags(page_addr); - if ((flags & PAGE_READ) == 0) { - segv = 1; - } else { - reg = env->llreg & 0x1f; - d = (env->llreg & 0x20) != 0; - if (d) { - segv = get_user_s64(val, addr); - } else { - segv = get_user_s32(val, addr); - } - if (!segv) { - if (val != env->llval) { - env->active_tc.gpr[reg] = 0; - } else { - if (d) { - segv = put_user_u64(env->llnewval, addr); - } else { - segv = put_user_u32(env->llnewval, addr); - } - if (!segv) { - env->active_tc.gpr[reg] = 1; - } - } - } - } - env->lladdr = -1; - if (!segv) { - env->active_tc.PC += 4; - } - mmap_unlock(); - end_exclusive(); - return segv; -} - -/* Break codes */ -enum { - BRK_OVERFLOW = 6, - BRK_DIVZERO = 7 -}; - -static int do_break(CPUMIPSState *env, target_siginfo_t *info, - unsigned int code) -{ - int ret = -1; - - switch (code) { - case BRK_OVERFLOW: - case BRK_DIVZERO: - info->si_signo = TARGET_SIGFPE; - info->si_errno = 0; - info->si_code = (code == BRK_OVERFLOW) ? FPE_INTOVF : FPE_INTDIV; - queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); - ret = 0; - break; - default: - info->si_signo = TARGET_SIGTRAP; - info->si_errno = 0; - queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); - ret = 0; - break; - } - - return ret; -} - -void cpu_loop(CPUMIPSState *env) -{ - CPUState *cs = CPU(mips_env_get_cpu(env)); - target_siginfo_t info; - int trapnr; - abi_long ret; -# ifdef TARGET_ABI_MIPSO32 - unsigned int syscall_num; -# endif - - for(;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch(trapnr) { - case EXCP_SYSCALL: - env->active_tc.PC += 4; -# ifdef TARGET_ABI_MIPSO32 - syscall_num = env->active_tc.gpr[2] - 4000; - if (syscall_num >= sizeof(mips_syscall_args)) { - ret = -TARGET_ENOSYS; - } else { - int nb_args; - abi_ulong sp_reg; - abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0; - - nb_args = mips_syscall_args[syscall_num]; - sp_reg = env->active_tc.gpr[29]; - switch (nb_args) { - /* these arguments are taken from the stack */ - case 8: - if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) { - goto done_syscall; - } - case 7: - if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) { - goto done_syscall; - } - case 6: - if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) { - goto done_syscall; - } - case 5: - if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) { - goto done_syscall; - } - default: - break; - } - ret = do_syscall(env, env->active_tc.gpr[2], - env->active_tc.gpr[4], - env->active_tc.gpr[5], - env->active_tc.gpr[6], - env->active_tc.gpr[7], - arg5, arg6, arg7, arg8); - } -done_syscall: -# else - ret = do_syscall(env, env->active_tc.gpr[2], - env->active_tc.gpr[4], env->active_tc.gpr[5], - env->active_tc.gpr[6], env->active_tc.gpr[7], - env->active_tc.gpr[8], env->active_tc.gpr[9], - env->active_tc.gpr[10], env->active_tc.gpr[11]); -# endif /* O32 */ - if (ret == -TARGET_ERESTARTSYS) { - env->active_tc.PC -= 4; - break; - } - if (ret == -TARGET_QEMU_ESIGRETURN) { - /* Returning from a successful sigreturn syscall. - Avoid clobbering register state. */ - break; - } - if ((abi_ulong)ret >= (abi_ulong)-1133) { - env->active_tc.gpr[7] = 1; /* error flag */ - ret = -ret; - } else { - env->active_tc.gpr[7] = 0; /* error flag */ - } - env->active_tc.gpr[2] = ret; - break; - case EXCP_TLBL: - case EXCP_TLBS: - case EXCP_AdEL: - case EXCP_AdES: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->CP0_BadVAddr; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_CpU: - case EXCP_RI: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_SC: - if (do_store_exclusive(env)) { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->active_tc.PC; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_DSPDIS: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - /* The code below was inspired by the MIPS Linux kernel trap - * handling code in arch/mips/kernel/traps.c. - */ - case EXCP_BREAK: - { - abi_ulong trap_instr; - unsigned int code; - - if (env->hflags & MIPS_HFLAG_M16) { - if (env->insn_flags & ASE_MICROMIPS) { - /* microMIPS mode */ - ret = get_user_u16(trap_instr, env->active_tc.PC); - if (ret != 0) { - goto error; - } - - if ((trap_instr >> 10) == 0x11) { - /* 16-bit instruction */ - code = trap_instr & 0xf; - } else { - /* 32-bit instruction */ - abi_ulong instr_lo; - - ret = get_user_u16(instr_lo, - env->active_tc.PC + 2); - if (ret != 0) { - goto error; - } - trap_instr = (trap_instr << 16) | instr_lo; - code = ((trap_instr >> 6) & ((1 << 20) - 1)); - /* Unfortunately, microMIPS also suffers from - the old assembler bug... */ - if (code >= (1 << 10)) { - code >>= 10; - } - } - } else { - /* MIPS16e mode */ - ret = get_user_u16(trap_instr, env->active_tc.PC); - if (ret != 0) { - goto error; - } - code = (trap_instr >> 6) & 0x3f; - } - } else { - ret = get_user_u32(trap_instr, env->active_tc.PC); - if (ret != 0) { - goto error; - } - - /* As described in the original Linux kernel code, the - * below checks on 'code' are to work around an old - * assembly bug. - */ - code = ((trap_instr >> 6) & ((1 << 20) - 1)); - if (code >= (1 << 10)) { - code >>= 10; - } - } - - if (do_break(env, &info, code) != 0) { - goto error; - } - } - break; - case EXCP_TRAP: - { - abi_ulong trap_instr; - unsigned int code = 0; - - if (env->hflags & MIPS_HFLAG_M16) { - /* microMIPS mode */ - abi_ulong instr[2]; - - ret = get_user_u16(instr[0], env->active_tc.PC) || - get_user_u16(instr[1], env->active_tc.PC + 2); - - trap_instr = (instr[0] << 16) | instr[1]; - } else { - ret = get_user_u32(trap_instr, env->active_tc.PC); - } - - if (ret != 0) { - goto error; - } - - /* The immediate versions don't provide a code. */ - if (!(trap_instr & 0xFC000000)) { - if (env->hflags & MIPS_HFLAG_M16) { - /* microMIPS mode */ - code = ((trap_instr >> 12) & ((1 << 4) - 1)); - } else { - code = ((trap_instr >> 6) & ((1 << 10) - 1)); - } - } - - if (do_break(env, &info, code) != 0) { - goto error; - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: -error: - EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); - abort(); - } - process_pending_signals(env); - } -} -#endif - #ifdef TARGET_NIOS2 void cpu_loop(CPUNios2State *env) @@ -3244,32 +2545,6 @@ int main(int argc, char **argv, char **envp) env->regs[31] = regs->r31; env->sregs[SR_PC] = regs->pc; } -#elif defined(TARGET_MIPS) - { - int i; - - for(i = 0; i < 32; i++) { - env->active_tc.gpr[i] = regs->regs[i]; - } - env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; - if (regs->cp0_epc & 1) { - env->hflags |= MIPS_HFLAG_M16; - } - if (((info->elf_flags & EF_MIPS_NAN2008) != 0) != - ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) { - if ((env->active_fpu.fcr31_rw_bitmask & - (1 << FCR31_NAN2008)) == 0) { - fprintf(stderr, "ELF binary's NaN mode not supported by CPU\n"); - exit(1); - } - if ((info->elf_flags & EF_MIPS_NAN2008) != 0) { - env->active_fpu.fcr31 |= (1 << FCR31_NAN2008); - } else { - env->active_fpu.fcr31 &= ~(1 << FCR31_NAN2008); - } - restore_snan_bit_mode(env); - } - } #elif defined(TARGET_NIOS2) { env->regs[0] = 0; diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index b7700a5561..084ad6a041 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -20,7 +20,730 @@ #include "qemu/osdep.h" #include "qemu.h" #include "cpu_loop-common.h" +#include "elf.h" + +# ifdef TARGET_ABI_MIPSO32 +# define MIPS_SYS(name, args) args, +static const uint8_t mips_syscall_args[] = { + MIPS_SYS(sys_syscall , 8) /* 4000 */ + MIPS_SYS(sys_exit , 1) + MIPS_SYS(sys_fork , 0) + MIPS_SYS(sys_read , 3) + MIPS_SYS(sys_write , 3) + MIPS_SYS(sys_open , 3) /* 4005 */ + MIPS_SYS(sys_close , 1) + MIPS_SYS(sys_waitpid , 3) + MIPS_SYS(sys_creat , 2) + MIPS_SYS(sys_link , 2) + MIPS_SYS(sys_unlink , 1) /* 4010 */ + MIPS_SYS(sys_execve , 0) + MIPS_SYS(sys_chdir , 1) + MIPS_SYS(sys_time , 1) + MIPS_SYS(sys_mknod , 3) + MIPS_SYS(sys_chmod , 2) /* 4015 */ + MIPS_SYS(sys_lchown , 3) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_ni_syscall , 0) /* was sys_stat */ + MIPS_SYS(sys_lseek , 3) + MIPS_SYS(sys_getpid , 0) /* 4020 */ + MIPS_SYS(sys_mount , 5) + MIPS_SYS(sys_umount , 1) + MIPS_SYS(sys_setuid , 1) + MIPS_SYS(sys_getuid , 0) + MIPS_SYS(sys_stime , 1) /* 4025 */ + MIPS_SYS(sys_ptrace , 4) + MIPS_SYS(sys_alarm , 1) + MIPS_SYS(sys_ni_syscall , 0) /* was sys_fstat */ + MIPS_SYS(sys_pause , 0) + MIPS_SYS(sys_utime , 2) /* 4030 */ + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_access , 2) + MIPS_SYS(sys_nice , 1) + MIPS_SYS(sys_ni_syscall , 0) /* 4035 */ + MIPS_SYS(sys_sync , 0) + MIPS_SYS(sys_kill , 2) + MIPS_SYS(sys_rename , 2) + MIPS_SYS(sys_mkdir , 2) + MIPS_SYS(sys_rmdir , 1) /* 4040 */ + MIPS_SYS(sys_dup , 1) + MIPS_SYS(sys_pipe , 0) + MIPS_SYS(sys_times , 1) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_brk , 1) /* 4045 */ + MIPS_SYS(sys_setgid , 1) + MIPS_SYS(sys_getgid , 0) + MIPS_SYS(sys_ni_syscall , 0) /* was signal(2) */ + MIPS_SYS(sys_geteuid , 0) + MIPS_SYS(sys_getegid , 0) /* 4050 */ + MIPS_SYS(sys_acct , 0) + MIPS_SYS(sys_umount2 , 2) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_ioctl , 3) + MIPS_SYS(sys_fcntl , 3) /* 4055 */ + MIPS_SYS(sys_ni_syscall , 2) + MIPS_SYS(sys_setpgid , 2) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_olduname , 1) + MIPS_SYS(sys_umask , 1) /* 4060 */ + MIPS_SYS(sys_chroot , 1) + MIPS_SYS(sys_ustat , 2) + MIPS_SYS(sys_dup2 , 2) + MIPS_SYS(sys_getppid , 0) + MIPS_SYS(sys_getpgrp , 0) /* 4065 */ + MIPS_SYS(sys_setsid , 0) + MIPS_SYS(sys_sigaction , 3) + MIPS_SYS(sys_sgetmask , 0) + MIPS_SYS(sys_ssetmask , 1) + MIPS_SYS(sys_setreuid , 2) /* 4070 */ + MIPS_SYS(sys_setregid , 2) + MIPS_SYS(sys_sigsuspend , 0) + MIPS_SYS(sys_sigpending , 1) + MIPS_SYS(sys_sethostname , 2) + MIPS_SYS(sys_setrlimit , 2) /* 4075 */ + MIPS_SYS(sys_getrlimit , 2) + MIPS_SYS(sys_getrusage , 2) + MIPS_SYS(sys_gettimeofday, 2) + MIPS_SYS(sys_settimeofday, 2) + MIPS_SYS(sys_getgroups , 2) /* 4080 */ + MIPS_SYS(sys_setgroups , 2) + MIPS_SYS(sys_ni_syscall , 0) /* old_select */ + MIPS_SYS(sys_symlink , 2) + MIPS_SYS(sys_ni_syscall , 0) /* was sys_lstat */ + MIPS_SYS(sys_readlink , 3) /* 4085 */ + MIPS_SYS(sys_uselib , 1) + MIPS_SYS(sys_swapon , 2) + MIPS_SYS(sys_reboot , 3) + MIPS_SYS(old_readdir , 3) + MIPS_SYS(old_mmap , 6) /* 4090 */ + MIPS_SYS(sys_munmap , 2) + MIPS_SYS(sys_truncate , 2) + MIPS_SYS(sys_ftruncate , 2) + MIPS_SYS(sys_fchmod , 2) + MIPS_SYS(sys_fchown , 3) /* 4095 */ + MIPS_SYS(sys_getpriority , 2) + MIPS_SYS(sys_setpriority , 3) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_statfs , 2) + MIPS_SYS(sys_fstatfs , 2) /* 4100 */ + MIPS_SYS(sys_ni_syscall , 0) /* was ioperm(2) */ + MIPS_SYS(sys_socketcall , 2) + MIPS_SYS(sys_syslog , 3) + MIPS_SYS(sys_setitimer , 3) + MIPS_SYS(sys_getitimer , 2) /* 4105 */ + MIPS_SYS(sys_newstat , 2) + MIPS_SYS(sys_newlstat , 2) + MIPS_SYS(sys_newfstat , 2) + MIPS_SYS(sys_uname , 1) + MIPS_SYS(sys_ni_syscall , 0) /* 4110 was iopl(2) */ + MIPS_SYS(sys_vhangup , 0) + MIPS_SYS(sys_ni_syscall , 0) /* was sys_idle() */ + MIPS_SYS(sys_ni_syscall , 0) /* was sys_vm86 */ + MIPS_SYS(sys_wait4 , 4) + MIPS_SYS(sys_swapoff , 1) /* 4115 */ + MIPS_SYS(sys_sysinfo , 1) + MIPS_SYS(sys_ipc , 6) + MIPS_SYS(sys_fsync , 1) + MIPS_SYS(sys_sigreturn , 0) + MIPS_SYS(sys_clone , 6) /* 4120 */ + MIPS_SYS(sys_setdomainname, 2) + MIPS_SYS(sys_newuname , 1) + MIPS_SYS(sys_ni_syscall , 0) /* sys_modify_ldt */ + MIPS_SYS(sys_adjtimex , 1) + MIPS_SYS(sys_mprotect , 3) /* 4125 */ + MIPS_SYS(sys_sigprocmask , 3) + MIPS_SYS(sys_ni_syscall , 0) /* was create_module */ + MIPS_SYS(sys_init_module , 5) + MIPS_SYS(sys_delete_module, 1) + MIPS_SYS(sys_ni_syscall , 0) /* 4130 was get_kernel_syms */ + MIPS_SYS(sys_quotactl , 0) + MIPS_SYS(sys_getpgid , 1) + MIPS_SYS(sys_fchdir , 1) + MIPS_SYS(sys_bdflush , 2) + MIPS_SYS(sys_sysfs , 3) /* 4135 */ + MIPS_SYS(sys_personality , 1) + MIPS_SYS(sys_ni_syscall , 0) /* for afs_syscall */ + MIPS_SYS(sys_setfsuid , 1) + MIPS_SYS(sys_setfsgid , 1) + MIPS_SYS(sys_llseek , 5) /* 4140 */ + MIPS_SYS(sys_getdents , 3) + MIPS_SYS(sys_select , 5) + MIPS_SYS(sys_flock , 2) + MIPS_SYS(sys_msync , 3) + MIPS_SYS(sys_readv , 3) /* 4145 */ + MIPS_SYS(sys_writev , 3) + MIPS_SYS(sys_cacheflush , 3) + MIPS_SYS(sys_cachectl , 3) + MIPS_SYS(sys_sysmips , 4) + MIPS_SYS(sys_ni_syscall , 0) /* 4150 */ + MIPS_SYS(sys_getsid , 1) + MIPS_SYS(sys_fdatasync , 0) + MIPS_SYS(sys_sysctl , 1) + MIPS_SYS(sys_mlock , 2) + MIPS_SYS(sys_munlock , 2) /* 4155 */ + MIPS_SYS(sys_mlockall , 1) + MIPS_SYS(sys_munlockall , 0) + MIPS_SYS(sys_sched_setparam, 2) + MIPS_SYS(sys_sched_getparam, 2) + MIPS_SYS(sys_sched_setscheduler, 3) /* 4160 */ + MIPS_SYS(sys_sched_getscheduler, 1) + MIPS_SYS(sys_sched_yield , 0) + MIPS_SYS(sys_sched_get_priority_max, 1) + MIPS_SYS(sys_sched_get_priority_min, 1) + MIPS_SYS(sys_sched_rr_get_interval, 2) /* 4165 */ + MIPS_SYS(sys_nanosleep, 2) + MIPS_SYS(sys_mremap , 5) + MIPS_SYS(sys_accept , 3) + MIPS_SYS(sys_bind , 3) + MIPS_SYS(sys_connect , 3) /* 4170 */ + MIPS_SYS(sys_getpeername , 3) + MIPS_SYS(sys_getsockname , 3) + MIPS_SYS(sys_getsockopt , 5) + MIPS_SYS(sys_listen , 2) + MIPS_SYS(sys_recv , 4) /* 4175 */ + MIPS_SYS(sys_recvfrom , 6) + MIPS_SYS(sys_recvmsg , 3) + MIPS_SYS(sys_send , 4) + MIPS_SYS(sys_sendmsg , 3) + MIPS_SYS(sys_sendto , 6) /* 4180 */ + MIPS_SYS(sys_setsockopt , 5) + MIPS_SYS(sys_shutdown , 2) + MIPS_SYS(sys_socket , 3) + MIPS_SYS(sys_socketpair , 4) + MIPS_SYS(sys_setresuid , 3) /* 4185 */ + MIPS_SYS(sys_getresuid , 3) + MIPS_SYS(sys_ni_syscall , 0) /* was sys_query_module */ + MIPS_SYS(sys_poll , 3) + MIPS_SYS(sys_nfsservctl , 3) + MIPS_SYS(sys_setresgid , 3) /* 4190 */ + MIPS_SYS(sys_getresgid , 3) + MIPS_SYS(sys_prctl , 5) + MIPS_SYS(sys_rt_sigreturn, 0) + MIPS_SYS(sys_rt_sigaction, 4) + MIPS_SYS(sys_rt_sigprocmask, 4) /* 4195 */ + MIPS_SYS(sys_rt_sigpending, 2) + MIPS_SYS(sys_rt_sigtimedwait, 4) + MIPS_SYS(sys_rt_sigqueueinfo, 3) + MIPS_SYS(sys_rt_sigsuspend, 0) + MIPS_SYS(sys_pread64 , 6) /* 4200 */ + MIPS_SYS(sys_pwrite64 , 6) + MIPS_SYS(sys_chown , 3) + MIPS_SYS(sys_getcwd , 2) + MIPS_SYS(sys_capget , 2) + MIPS_SYS(sys_capset , 2) /* 4205 */ + MIPS_SYS(sys_sigaltstack , 2) + MIPS_SYS(sys_sendfile , 4) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_mmap2 , 6) /* 4210 */ + MIPS_SYS(sys_truncate64 , 4) + MIPS_SYS(sys_ftruncate64 , 4) + MIPS_SYS(sys_stat64 , 2) + MIPS_SYS(sys_lstat64 , 2) + MIPS_SYS(sys_fstat64 , 2) /* 4215 */ + MIPS_SYS(sys_pivot_root , 2) + MIPS_SYS(sys_mincore , 3) + MIPS_SYS(sys_madvise , 3) + MIPS_SYS(sys_getdents64 , 3) + MIPS_SYS(sys_fcntl64 , 3) /* 4220 */ + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_gettid , 0) + MIPS_SYS(sys_readahead , 5) + MIPS_SYS(sys_setxattr , 5) + MIPS_SYS(sys_lsetxattr , 5) /* 4225 */ + MIPS_SYS(sys_fsetxattr , 5) + MIPS_SYS(sys_getxattr , 4) + MIPS_SYS(sys_lgetxattr , 4) + MIPS_SYS(sys_fgetxattr , 4) + MIPS_SYS(sys_listxattr , 3) /* 4230 */ + MIPS_SYS(sys_llistxattr , 3) + MIPS_SYS(sys_flistxattr , 3) + MIPS_SYS(sys_removexattr , 2) + MIPS_SYS(sys_lremovexattr, 2) + MIPS_SYS(sys_fremovexattr, 2) /* 4235 */ + MIPS_SYS(sys_tkill , 2) + MIPS_SYS(sys_sendfile64 , 5) + MIPS_SYS(sys_futex , 6) + MIPS_SYS(sys_sched_setaffinity, 3) + MIPS_SYS(sys_sched_getaffinity, 3) /* 4240 */ + MIPS_SYS(sys_io_setup , 2) + MIPS_SYS(sys_io_destroy , 1) + MIPS_SYS(sys_io_getevents, 5) + MIPS_SYS(sys_io_submit , 3) + MIPS_SYS(sys_io_cancel , 3) /* 4245 */ + MIPS_SYS(sys_exit_group , 1) + MIPS_SYS(sys_lookup_dcookie, 3) + MIPS_SYS(sys_epoll_create, 1) + MIPS_SYS(sys_epoll_ctl , 4) + MIPS_SYS(sys_epoll_wait , 3) /* 4250 */ + MIPS_SYS(sys_remap_file_pages, 5) + MIPS_SYS(sys_set_tid_address, 1) + MIPS_SYS(sys_restart_syscall, 0) + MIPS_SYS(sys_fadvise64_64, 7) + MIPS_SYS(sys_statfs64 , 3) /* 4255 */ + MIPS_SYS(sys_fstatfs64 , 2) + MIPS_SYS(sys_timer_create, 3) + MIPS_SYS(sys_timer_settime, 4) + MIPS_SYS(sys_timer_gettime, 2) + MIPS_SYS(sys_timer_getoverrun, 1) /* 4260 */ + MIPS_SYS(sys_timer_delete, 1) + MIPS_SYS(sys_clock_settime, 2) + MIPS_SYS(sys_clock_gettime, 2) + MIPS_SYS(sys_clock_getres, 2) + MIPS_SYS(sys_clock_nanosleep, 4) /* 4265 */ + MIPS_SYS(sys_tgkill , 3) + MIPS_SYS(sys_utimes , 2) + MIPS_SYS(sys_mbind , 4) + MIPS_SYS(sys_ni_syscall , 0) /* sys_get_mempolicy */ + MIPS_SYS(sys_ni_syscall , 0) /* 4270 sys_set_mempolicy */ + MIPS_SYS(sys_mq_open , 4) + MIPS_SYS(sys_mq_unlink , 1) + MIPS_SYS(sys_mq_timedsend, 5) + MIPS_SYS(sys_mq_timedreceive, 5) + MIPS_SYS(sys_mq_notify , 2) /* 4275 */ + MIPS_SYS(sys_mq_getsetattr, 3) + MIPS_SYS(sys_ni_syscall , 0) /* sys_vserver */ + MIPS_SYS(sys_waitid , 4) + MIPS_SYS(sys_ni_syscall , 0) /* available, was setaltroot */ + MIPS_SYS(sys_add_key , 5) + MIPS_SYS(sys_request_key, 4) + MIPS_SYS(sys_keyctl , 5) + MIPS_SYS(sys_set_thread_area, 1) + MIPS_SYS(sys_inotify_init, 0) + MIPS_SYS(sys_inotify_add_watch, 3) /* 4285 */ + MIPS_SYS(sys_inotify_rm_watch, 2) + MIPS_SYS(sys_migrate_pages, 4) + MIPS_SYS(sys_openat, 4) + MIPS_SYS(sys_mkdirat, 3) + MIPS_SYS(sys_mknodat, 4) /* 4290 */ + MIPS_SYS(sys_fchownat, 5) + MIPS_SYS(sys_futimesat, 3) + MIPS_SYS(sys_fstatat64, 4) + MIPS_SYS(sys_unlinkat, 3) + MIPS_SYS(sys_renameat, 4) /* 4295 */ + MIPS_SYS(sys_linkat, 5) + MIPS_SYS(sys_symlinkat, 3) + MIPS_SYS(sys_readlinkat, 4) + MIPS_SYS(sys_fchmodat, 3) + MIPS_SYS(sys_faccessat, 3) /* 4300 */ + MIPS_SYS(sys_pselect6, 6) + MIPS_SYS(sys_ppoll, 5) + MIPS_SYS(sys_unshare, 1) + MIPS_SYS(sys_splice, 6) + MIPS_SYS(sys_sync_file_range, 7) /* 4305 */ + MIPS_SYS(sys_tee, 4) + MIPS_SYS(sys_vmsplice, 4) + MIPS_SYS(sys_move_pages, 6) + MIPS_SYS(sys_set_robust_list, 2) + MIPS_SYS(sys_get_robust_list, 3) /* 4310 */ + MIPS_SYS(sys_kexec_load, 4) + MIPS_SYS(sys_getcpu, 3) + MIPS_SYS(sys_epoll_pwait, 6) + MIPS_SYS(sys_ioprio_set, 3) + MIPS_SYS(sys_ioprio_get, 2) + MIPS_SYS(sys_utimensat, 4) + MIPS_SYS(sys_signalfd, 3) + MIPS_SYS(sys_ni_syscall, 0) /* was timerfd */ + MIPS_SYS(sys_eventfd, 1) + MIPS_SYS(sys_fallocate, 6) /* 4320 */ + MIPS_SYS(sys_timerfd_create, 2) + MIPS_SYS(sys_timerfd_gettime, 2) + MIPS_SYS(sys_timerfd_settime, 4) + MIPS_SYS(sys_signalfd4, 4) + MIPS_SYS(sys_eventfd2, 2) /* 4325 */ + MIPS_SYS(sys_epoll_create1, 1) + MIPS_SYS(sys_dup3, 3) + MIPS_SYS(sys_pipe2, 2) + MIPS_SYS(sys_inotify_init1, 1) + MIPS_SYS(sys_preadv, 5) /* 4330 */ + MIPS_SYS(sys_pwritev, 5) + MIPS_SYS(sys_rt_tgsigqueueinfo, 4) + MIPS_SYS(sys_perf_event_open, 5) + MIPS_SYS(sys_accept4, 4) + MIPS_SYS(sys_recvmmsg, 5) /* 4335 */ + MIPS_SYS(sys_fanotify_init, 2) + MIPS_SYS(sys_fanotify_mark, 6) + MIPS_SYS(sys_prlimit64, 4) + MIPS_SYS(sys_name_to_handle_at, 5) + MIPS_SYS(sys_open_by_handle_at, 3) /* 4340 */ + MIPS_SYS(sys_clock_adjtime, 2) + MIPS_SYS(sys_syncfs, 1) + MIPS_SYS(sys_sendmmsg, 4) + MIPS_SYS(sys_setns, 2) + MIPS_SYS(sys_process_vm_readv, 6) /* 345 */ + MIPS_SYS(sys_process_vm_writev, 6) + MIPS_SYS(sys_kcmp, 5) + MIPS_SYS(sys_finit_module, 3) + MIPS_SYS(sys_sched_setattr, 2) + MIPS_SYS(sys_sched_getattr, 3) /* 350 */ + MIPS_SYS(sys_renameat2, 5) + MIPS_SYS(sys_seccomp, 3) + MIPS_SYS(sys_getrandom, 3) + MIPS_SYS(sys_memfd_create, 2) + MIPS_SYS(sys_bpf, 3) /* 355 */ + MIPS_SYS(sys_execveat, 5) + MIPS_SYS(sys_userfaultfd, 1) + MIPS_SYS(sys_membarrier, 2) + MIPS_SYS(sys_mlock2, 3) + MIPS_SYS(sys_copy_file_range, 6) /* 360 */ + MIPS_SYS(sys_preadv2, 6) + MIPS_SYS(sys_pwritev2, 6) +}; +# undef MIPS_SYS +# endif /* O32 */ + +static int do_store_exclusive(CPUMIPSState *env) +{ + target_ulong addr; + target_ulong page_addr; + target_ulong val; + int flags; + int segv = 0; + int reg; + int d; + + addr = env->lladdr; + page_addr = addr & TARGET_PAGE_MASK; + start_exclusive(); + mmap_lock(); + flags = page_get_flags(page_addr); + if ((flags & PAGE_READ) == 0) { + segv = 1; + } else { + reg = env->llreg & 0x1f; + d = (env->llreg & 0x20) != 0; + if (d) { + segv = get_user_s64(val, addr); + } else { + segv = get_user_s32(val, addr); + } + if (!segv) { + if (val != env->llval) { + env->active_tc.gpr[reg] = 0; + } else { + if (d) { + segv = put_user_u64(env->llnewval, addr); + } else { + segv = put_user_u32(env->llnewval, addr); + } + if (!segv) { + env->active_tc.gpr[reg] = 1; + } + } + } + } + env->lladdr = -1; + if (!segv) { + env->active_tc.PC += 4; + } + mmap_unlock(); + end_exclusive(); + return segv; +} + +/* Break codes */ +enum { + BRK_OVERFLOW = 6, + BRK_DIVZERO = 7 +}; + +static int do_break(CPUMIPSState *env, target_siginfo_t *info, + unsigned int code) +{ + int ret = -1; + + switch (code) { + case BRK_OVERFLOW: + case BRK_DIVZERO: + info->si_signo = TARGET_SIGFPE; + info->si_errno = 0; + info->si_code = (code == BRK_OVERFLOW) ? FPE_INTOVF : FPE_INTDIV; + queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); + ret = 0; + break; + default: + info->si_signo = TARGET_SIGTRAP; + info->si_errno = 0; + queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); + ret = 0; + break; + } + + return ret; +} + +void cpu_loop(CPUMIPSState *env) +{ + CPUState *cs = CPU(mips_env_get_cpu(env)); + target_siginfo_t info; + int trapnr; + abi_long ret; +# ifdef TARGET_ABI_MIPSO32 + unsigned int syscall_num; +# endif + + for(;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch(trapnr) { + case EXCP_SYSCALL: + env->active_tc.PC += 4; +# ifdef TARGET_ABI_MIPSO32 + syscall_num = env->active_tc.gpr[2] - 4000; + if (syscall_num >= sizeof(mips_syscall_args)) { + ret = -TARGET_ENOSYS; + } else { + int nb_args; + abi_ulong sp_reg; + abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0; + + nb_args = mips_syscall_args[syscall_num]; + sp_reg = env->active_tc.gpr[29]; + switch (nb_args) { + /* these arguments are taken from the stack */ + case 8: + if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) { + goto done_syscall; + } + case 7: + if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) { + goto done_syscall; + } + case 6: + if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) { + goto done_syscall; + } + case 5: + if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) { + goto done_syscall; + } + default: + break; + } + ret = do_syscall(env, env->active_tc.gpr[2], + env->active_tc.gpr[4], + env->active_tc.gpr[5], + env->active_tc.gpr[6], + env->active_tc.gpr[7], + arg5, arg6, arg7, arg8); + } +done_syscall: +# else + ret = do_syscall(env, env->active_tc.gpr[2], + env->active_tc.gpr[4], env->active_tc.gpr[5], + env->active_tc.gpr[6], env->active_tc.gpr[7], + env->active_tc.gpr[8], env->active_tc.gpr[9], + env->active_tc.gpr[10], env->active_tc.gpr[11]); +# endif /* O32 */ + if (ret == -TARGET_ERESTARTSYS) { + env->active_tc.PC -= 4; + break; + } + if (ret == -TARGET_QEMU_ESIGRETURN) { + /* Returning from a successful sigreturn syscall. + Avoid clobbering register state. */ + break; + } + if ((abi_ulong)ret >= (abi_ulong)-1133) { + env->active_tc.gpr[7] = 1; /* error flag */ + ret = -ret; + } else { + env->active_tc.gpr[7] = 0; /* error flag */ + } + env->active_tc.gpr[2] = ret; + break; + case EXCP_TLBL: + case EXCP_TLBS: + case EXCP_AdEL: + case EXCP_AdES: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->CP0_BadVAddr; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_CpU: + case EXCP_RI: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_SC: + if (do_store_exclusive(env)) { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->active_tc.PC; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_DSPDIS: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPC; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + /* The code below was inspired by the MIPS Linux kernel trap + * handling code in arch/mips/kernel/traps.c. + */ + case EXCP_BREAK: + { + abi_ulong trap_instr; + unsigned int code; + + if (env->hflags & MIPS_HFLAG_M16) { + if (env->insn_flags & ASE_MICROMIPS) { + /* microMIPS mode */ + ret = get_user_u16(trap_instr, env->active_tc.PC); + if (ret != 0) { + goto error; + } + + if ((trap_instr >> 10) == 0x11) { + /* 16-bit instruction */ + code = trap_instr & 0xf; + } else { + /* 32-bit instruction */ + abi_ulong instr_lo; + + ret = get_user_u16(instr_lo, + env->active_tc.PC + 2); + if (ret != 0) { + goto error; + } + trap_instr = (trap_instr << 16) | instr_lo; + code = ((trap_instr >> 6) & ((1 << 20) - 1)); + /* Unfortunately, microMIPS also suffers from + the old assembler bug... */ + if (code >= (1 << 10)) { + code >>= 10; + } + } + } else { + /* MIPS16e mode */ + ret = get_user_u16(trap_instr, env->active_tc.PC); + if (ret != 0) { + goto error; + } + code = (trap_instr >> 6) & 0x3f; + } + } else { + ret = get_user_u32(trap_instr, env->active_tc.PC); + if (ret != 0) { + goto error; + } + + /* As described in the original Linux kernel code, the + * below checks on 'code' are to work around an old + * assembly bug. + */ + code = ((trap_instr >> 6) & ((1 << 20) - 1)); + if (code >= (1 << 10)) { + code >>= 10; + } + } + + if (do_break(env, &info, code) != 0) { + goto error; + } + } + break; + case EXCP_TRAP: + { + abi_ulong trap_instr; + unsigned int code = 0; + + if (env->hflags & MIPS_HFLAG_M16) { + /* microMIPS mode */ + abi_ulong instr[2]; + + ret = get_user_u16(instr[0], env->active_tc.PC) || + get_user_u16(instr[1], env->active_tc.PC + 2); + + trap_instr = (instr[0] << 16) | instr[1]; + } else { + ret = get_user_u32(trap_instr, env->active_tc.PC); + } + + if (ret != 0) { + goto error; + } + + /* The immediate versions don't provide a code. */ + if (!(trap_instr & 0xFC000000)) { + if (env->hflags & MIPS_HFLAG_M16) { + /* microMIPS mode */ + code = ((trap_instr >> 12) & ((1 << 4) - 1)); + } else { + code = ((trap_instr >> 6) & ((1 << 10) - 1)); + } + } + + if (do_break(env, &info, code) != 0) { + goto error; + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: +error: + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); + abort(); + } + process_pending_signals(env); + } +} void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + int i; + + for(i = 0; i < 32; i++) { + env->active_tc.gpr[i] = regs->regs[i]; + } + env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; + if (regs->cp0_epc & 1) { + env->hflags |= MIPS_HFLAG_M16; + } + if (((info->elf_flags & EF_MIPS_NAN2008) != 0) != + ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) { + if ((env->active_fpu.fcr31_rw_bitmask & + (1 << FCR31_NAN2008)) == 0) { + fprintf(stderr, "ELF binary's NaN mode not supported by CPU\n"); + exit(1); + } + if ((info->elf_flags & EF_MIPS_NAN2008) != 0) { + env->active_fpu.fcr31 |= (1 << FCR31_NAN2008); + } else { + env->active_fpu.fcr31 &= ~(1 << FCR31_NAN2008); + } + restore_snan_bit_mode(env); + } } diff --git a/linux-user/mips64/cpu_loop.c b/linux-user/mips64/cpu_loop.c index b7700a5561..858bc5be78 100644 --- a/linux-user/mips64/cpu_loop.c +++ b/linux-user/mips64/cpu_loop.c @@ -17,10 +17,4 @@ * along with this program; if not, see . */ -#include "qemu/osdep.h" -#include "qemu.h" -#include "cpu_loop-common.h" - -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) -{ -} +#include "../mips/cpu_loop.c" From 0ec0f01c9d95acdc82fb9fec35bb0d64f877bdaf Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:40 +0200 Subject: [PATCH 0153/2380] linux-user: move nios2 cpu loop to nios2 directory No code change, only move code from main.c to nios2/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411185651.21351-9-laurent@vivier.eu> --- linux-user/main.c | 133 ------------------------------------ linux-user/nios2/cpu_loop.c | 126 ++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 133 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index a760c19379..88f807549f 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,109 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_NIOS2 - -void cpu_loop(CPUNios2State *env) -{ - CPUState *cs = ENV_GET_CPU(env); - Nios2CPU *cpu = NIOS2_CPU(cs); - target_siginfo_t info; - int trapnr, gdbsig, ret; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - gdbsig = 0; - - switch (trapnr) { - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_TRAP: - if (env->regs[R_AT] == 0) { - abi_long ret; - qemu_log_mask(CPU_LOG_INT, "\nSyscall\n"); - - ret = do_syscall(env, env->regs[2], - env->regs[4], env->regs[5], env->regs[6], - env->regs[7], env->regs[8], env->regs[9], - 0, 0); - - if (env->regs[2] == 0) { /* FIXME: syscall 0 workaround */ - ret = 0; - } - - env->regs[2] = abs(ret); - /* Return value is 0..4096 */ - env->regs[7] = (ret > 0xfffffffffffff000ULL); - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[CR_STATUS] &= ~0x3; - env->regs[R_EA] = env->regs[R_PC] + 4; - env->regs[R_PC] += 4; - break; - } else { - qemu_log_mask(CPU_LOG_INT, "\nTrap\n"); - - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[CR_STATUS] &= ~0x3; - env->regs[R_EA] = env->regs[R_PC] + 4; - env->regs[R_PC] = cpu->exception_addr; - - gdbsig = TARGET_SIGTRAP; - break; - } - case 0xaa: - switch (env->regs[R_PC]) { - /*case 0x1000:*/ /* TODO:__kuser_helper_version */ - case 0x1004: /* __kuser_cmpxchg */ - start_exclusive(); - if (env->regs[4] & 0x3) { - goto kuser_fail; - } - ret = get_user_u32(env->regs[2], env->regs[4]); - if (ret) { - end_exclusive(); - goto kuser_fail; - } - env->regs[2] -= env->regs[5]; - if (env->regs[2] == 0) { - put_user_u32(env->regs[6], env->regs[4]); - } - end_exclusive(); - env->regs[R_PC] = env->regs[R_RA]; - break; - /*case 0x1040:*/ /* TODO:__kuser_sigtramp */ - default: - ; -kuser_fail: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* TODO: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->regs[R_PC]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - default: - EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", - trapnr); - gdbsig = TARGET_SIGILL; - break; - } - if (gdbsig) { - gdb_handlesig(cs, gdbsig); - if (gdbsig != TARGET_SIGTRAP) { - exit(EXIT_FAILURE); - } - } - - process_pending_signals(env); - } -} - -#endif /* TARGET_NIOS2 */ - #ifdef TARGET_OPENRISC void cpu_loop(CPUOpenRISCState *env) @@ -2545,36 +2442,6 @@ int main(int argc, char **argv, char **envp) env->regs[31] = regs->r31; env->sregs[SR_PC] = regs->pc; } -#elif defined(TARGET_NIOS2) - { - env->regs[0] = 0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = regs->r14; - env->regs[15] = regs->r15; - /* TODO: unsigned long orig_r2; */ - env->regs[R_RA] = regs->ra; - env->regs[R_FP] = regs->fp; - env->regs[R_SP] = regs->sp; - env->regs[R_GP] = regs->gp; - env->regs[CR_ESTATUS] = regs->estatus; - env->regs[R_EA] = regs->ea; - /* TODO: unsigned long orig_r7; */ - - /* Emulate eret when starting thread. */ - env->regs[R_PC] = regs->ea; - } #elif defined(TARGET_OPENRISC) { int i; diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c index b7700a5561..dac7a06181 100644 --- a/linux-user/nios2/cpu_loop.c +++ b/linux-user/nios2/cpu_loop.c @@ -21,6 +21,132 @@ #include "qemu.h" #include "cpu_loop-common.h" +void cpu_loop(CPUNios2State *env) +{ + CPUState *cs = ENV_GET_CPU(env); + Nios2CPU *cpu = NIOS2_CPU(cs); + target_siginfo_t info; + int trapnr, gdbsig, ret; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + gdbsig = 0; + + switch (trapnr) { + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_TRAP: + if (env->regs[R_AT] == 0) { + abi_long ret; + qemu_log_mask(CPU_LOG_INT, "\nSyscall\n"); + + ret = do_syscall(env, env->regs[2], + env->regs[4], env->regs[5], env->regs[6], + env->regs[7], env->regs[8], env->regs[9], + 0, 0); + + if (env->regs[2] == 0) { /* FIXME: syscall 0 workaround */ + ret = 0; + } + + env->regs[2] = abs(ret); + /* Return value is 0..4096 */ + env->regs[7] = (ret > 0xfffffffffffff000ULL); + env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; + env->regs[CR_STATUS] &= ~0x3; + env->regs[R_EA] = env->regs[R_PC] + 4; + env->regs[R_PC] += 4; + break; + } else { + qemu_log_mask(CPU_LOG_INT, "\nTrap\n"); + + env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; + env->regs[CR_STATUS] &= ~0x3; + env->regs[R_EA] = env->regs[R_PC] + 4; + env->regs[R_PC] = cpu->exception_addr; + + gdbsig = TARGET_SIGTRAP; + break; + } + case 0xaa: + switch (env->regs[R_PC]) { + /*case 0x1000:*/ /* TODO:__kuser_helper_version */ + case 0x1004: /* __kuser_cmpxchg */ + start_exclusive(); + if (env->regs[4] & 0x3) { + goto kuser_fail; + } + ret = get_user_u32(env->regs[2], env->regs[4]); + if (ret) { + end_exclusive(); + goto kuser_fail; + } + env->regs[2] -= env->regs[5]; + if (env->regs[2] == 0) { + put_user_u32(env->regs[6], env->regs[4]); + } + end_exclusive(); + env->regs[R_PC] = env->regs[R_RA]; + break; + /*case 0x1040:*/ /* TODO:__kuser_sigtramp */ + default: + ; +kuser_fail: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* TODO: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->regs[R_PC]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + default: + EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", + trapnr); + gdbsig = TARGET_SIGILL; + break; + } + if (gdbsig) { + gdb_handlesig(cs, gdbsig); + if (gdbsig != TARGET_SIGTRAP) { + exit(EXIT_FAILURE); + } + } + + process_pending_signals(env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + env->regs[0] = 0; + env->regs[1] = regs->r1; + env->regs[2] = regs->r2; + env->regs[3] = regs->r3; + env->regs[4] = regs->r4; + env->regs[5] = regs->r5; + env->regs[6] = regs->r6; + env->regs[7] = regs->r7; + env->regs[8] = regs->r8; + env->regs[9] = regs->r9; + env->regs[10] = regs->r10; + env->regs[11] = regs->r11; + env->regs[12] = regs->r12; + env->regs[13] = regs->r13; + env->regs[14] = regs->r14; + env->regs[15] = regs->r15; + /* TODO: unsigned long orig_r2; */ + env->regs[R_RA] = regs->ra; + env->regs[R_FP] = regs->fp; + env->regs[R_SP] = regs->sp; + env->regs[R_GP] = regs->gp; + env->regs[CR_ESTATUS] = regs->estatus; + env->regs[R_EA] = regs->ea; + /* TODO: unsigned long orig_r7; */ + + /* Emulate eret when starting thread. */ + env->regs[R_PC] = regs->ea; } From 1ef7bca2e784d3fc3206416723aaeb06bb9d8302 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:41 +0200 Subject: [PATCH 0154/2380] linux-user: move openrisc cpu loop to openrisc directory No code change, only move code from main.c to openrisc/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411185651.21351-10-laurent@vivier.eu> --- linux-user/main.c | 96 ---------------------------------- linux-user/openrisc/cpu_loop.c | 89 +++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 96 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 88f807549f..af26a17c46 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,92 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_OPENRISC - -void cpu_loop(CPUOpenRISCState *env) -{ - CPUState *cs = CPU(openrisc_env_get_cpu(env)); - int trapnr; - abi_long ret; - target_siginfo_t info; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_SYSCALL: - env->pc += 4; /* 0xc00; */ - ret = do_syscall(env, - cpu_get_gpr(env, 11), /* return value */ - cpu_get_gpr(env, 3), /* r3 - r7 are params */ - cpu_get_gpr(env, 4), - cpu_get_gpr(env, 5), - cpu_get_gpr(env, 6), - cpu_get_gpr(env, 7), - cpu_get_gpr(env, 8), 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - cpu_set_gpr(env, 11, ret); - } - break; - case EXCP_DPF: - case EXCP_IPF: - case EXCP_RANGE: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_ALIGN: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_BUS_ADRALN; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_ILLEGAL: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_FPE: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_INTERRUPT: - /* We processed the pending cpu work above. */ - break; - case EXCP_DEBUG: - trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); - if (trapnr) { - info.si_signo = trapnr; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - g_assert_not_reached(); - } - process_pending_signals(env); - } -} - -#endif /* TARGET_OPENRISC */ - #ifdef TARGET_SH4 void cpu_loop(CPUSH4State *env) { @@ -2442,16 +2356,6 @@ int main(int argc, char **argv, char **envp) env->regs[31] = regs->r31; env->sregs[SR_PC] = regs->pc; } -#elif defined(TARGET_OPENRISC) - { - int i; - - for (i = 0; i < 32; i++) { - cpu_set_gpr(env, i, regs->gpr[i]); - } - env->pc = regs->pc; - cpu_set_sr(env, regs->sr); - } #elif defined(TARGET_RISCV) { env->pc = regs->sepc; diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index b7700a5561..6c6ea871e1 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -21,6 +21,95 @@ #include "qemu.h" #include "cpu_loop-common.h" +void cpu_loop(CPUOpenRISCState *env) +{ + CPUState *cs = CPU(openrisc_env_get_cpu(env)); + int trapnr; + abi_long ret; + target_siginfo_t info; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_SYSCALL: + env->pc += 4; /* 0xc00; */ + ret = do_syscall(env, + cpu_get_gpr(env, 11), /* return value */ + cpu_get_gpr(env, 3), /* r3 - r7 are params */ + cpu_get_gpr(env, 4), + cpu_get_gpr(env, 5), + cpu_get_gpr(env, 6), + cpu_get_gpr(env, 7), + cpu_get_gpr(env, 8), 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + cpu_set_gpr(env, 11, ret); + } + break; + case EXCP_DPF: + case EXCP_IPF: + case EXCP_RANGE: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ALIGN: + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_BUS_ADRALN; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ILLEGAL: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPC; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_FPE: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_INTERRUPT: + /* We processed the pending cpu work above. */ + break; + case EXCP_DEBUG: + trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); + if (trapnr) { + info.si_signo = trapnr; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + int i; + + for (i = 0; i < 32; i++) { + cpu_set_gpr(env, i, regs->gpr[i]); + } + env->pc = regs->pc; + cpu_set_sr(env, regs->sr); } From c37dcb4fa881998363b21a31212dc7a4bb80880d Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:42 +0200 Subject: [PATCH 0155/2380] linux-user: move sh4 cpu loop to sh4 directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from main.c to sh4/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180411185651.21351-11-laurent@vivier.eu> --- linux-user/main.c | 90 --------------------------------------- linux-user/sh4/cpu_loop.c | 85 ++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 90 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index af26a17c46..d7fee3e3db 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,87 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_SH4 -void cpu_loop(CPUSH4State *env) -{ - CPUState *cs = CPU(sh_env_get_cpu(env)); - int trapnr, ret; - target_siginfo_t info; - - while (1) { - bool arch_interrupt = true; - - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case 0x160: - env->pc += 2; - ret = do_syscall(env, - env->gregs[3], - env->gregs[4], - env->gregs[5], - env->gregs[6], - env->gregs[7], - env->gregs[0], - env->gregs[1], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 2; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->gregs[0] = ret; - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else { - arch_interrupt = false; - } - } - break; - case 0xa0: - case 0xc0: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->tea; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - arch_interrupt = false; - break; - default: - printf ("Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - - /* Most of the traps imply an exception or interrupt, which - implies an REI instruction has been executed. Which means - that LDST (aka LOK_ADDR) should be cleared. But there are - a few exceptions for traps internal to QEMU. */ - if (arch_interrupt) { - env->lock_addr = -1; - } - } -} -#endif - #ifdef TARGET_CRIS void cpu_loop(CPUCRISState *env) { @@ -2361,15 +2280,6 @@ int main(int argc, char **argv, char **envp) env->pc = regs->sepc; env->gpr[xSP] = regs->sp; } -#elif defined(TARGET_SH4) - { - int i; - - for(i = 0; i < 16; i++) { - env->gregs[i] = regs->regs[i]; - } - env->pc = regs->pc; - } #elif defined(TARGET_ALPHA) { int i; diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index b7700a5561..418833ea25 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -21,6 +21,91 @@ #include "qemu.h" #include "cpu_loop-common.h" +void cpu_loop(CPUSH4State *env) +{ + CPUState *cs = CPU(sh_env_get_cpu(env)); + int trapnr, ret; + target_siginfo_t info; + + while (1) { + bool arch_interrupt = true; + + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case 0x160: + env->pc += 2; + ret = do_syscall(env, + env->gregs[3], + env->gregs[4], + env->gregs[5], + env->gregs[6], + env->gregs[7], + env->gregs[0], + env->gregs[1], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->gregs[0] = ret; + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } else { + arch_interrupt = false; + } + } + break; + case 0xa0: + case 0xc0: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->tea; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + arch_interrupt = false; + break; + default: + printf ("Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + + /* Most of the traps imply an exception or interrupt, which + implies an REI instruction has been executed. Which means + that LDST (aka LOK_ADDR) should be cleared. But there are + a few exceptions for traps internal to QEMU. */ + if (arch_interrupt) { + env->lock_addr = -1; + } + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + int i; + + for(i = 0; i < 16; i++) { + env->gregs[i] = regs->regs[i]; + } + env->pc = regs->pc; } From 8dd14a9b79a3fd21651ecfc31526c32c0965f90f Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:43 +0200 Subject: [PATCH 0156/2380] linux-user: move cris cpu loop to cris directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from main.c to cris/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180411185651.21351-12-laurent@vivier.eu> --- linux-user/cris/cpu_loop.c | 89 +++++++++++++++++++++++++++++++++++++ linux-user/main.c | 90 -------------------------------------- 2 files changed, 89 insertions(+), 90 deletions(-) diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c index b7700a5561..1c5eca9f83 100644 --- a/linux-user/cris/cpu_loop.c +++ b/linux-user/cris/cpu_loop.c @@ -21,6 +21,95 @@ #include "qemu.h" #include "cpu_loop-common.h" +void cpu_loop(CPUCRISState *env) +{ + CPUState *cs = CPU(cris_env_get_cpu(env)); + int trapnr, ret; + target_siginfo_t info; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case 0xaa: + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->pregs[PR_EDA]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_BREAK: + ret = do_syscall(env, + env->regs[9], + env->regs[10], + env->regs[11], + env->regs[12], + env->regs[13], + env->pregs[7], + env->pregs[11], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[10] = ret; + } + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + printf ("Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + + env->regs[0] = regs->r0; + env->regs[1] = regs->r1; + env->regs[2] = regs->r2; + env->regs[3] = regs->r3; + env->regs[4] = regs->r4; + env->regs[5] = regs->r5; + env->regs[6] = regs->r6; + env->regs[7] = regs->r7; + env->regs[8] = regs->r8; + env->regs[9] = regs->r9; + env->regs[10] = regs->r10; + env->regs[11] = regs->r11; + env->regs[12] = regs->r12; + env->regs[13] = regs->r13; + env->regs[14] = info->start_stack; + env->regs[15] = regs->acr; + env->pc = regs->erp; } diff --git a/linux-user/main.c b/linux-user/main.c index d7fee3e3db..9e01325d6a 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,76 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_CRIS -void cpu_loop(CPUCRISState *env) -{ - CPUState *cs = CPU(cris_env_get_cpu(env)); - int trapnr, ret; - target_siginfo_t info; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case 0xaa: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->pregs[PR_EDA]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_BREAK: - ret = do_syscall(env, - env->regs[9], - env->regs[10], - env->regs[11], - env->regs[12], - env->regs[13], - env->pregs[7], - env->pregs[11], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 2; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[10] = ret; - } - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - printf ("Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - } -} -#endif - #ifdef TARGET_MICROBLAZE void cpu_loop(CPUMBState *env) { @@ -2290,26 +2220,6 @@ int main(int argc, char **argv, char **envp) env->ir[IR_SP] = regs->usp; env->pc = regs->pc; } -#elif defined(TARGET_CRIS) - { - env->regs[0] = regs->r0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = info->start_stack; - env->regs[15] = regs->acr; - env->pc = regs->erp; - } #elif defined(TARGET_S390X) { int i; From 82e8e64553d2ca1590e7cebed84450a633cb97f8 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:44 +0200 Subject: [PATCH 0157/2380] linux-user: move microblaze cpu loop to microblaze directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from main.c to microblaze/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180411185651.21351-13-laurent@vivier.eu> --- linux-user/main.c | 155 ------------------------------- linux-user/microblaze/cpu_loop.c | 150 ++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 155 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 9e01325d6a..9e49c8a30c 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,125 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_MICROBLAZE -void cpu_loop(CPUMBState *env) -{ - CPUState *cs = CPU(mb_env_get_cpu(env)); - int trapnr, ret; - target_siginfo_t info; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case 0xaa: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_BREAK: - /* Return address is 4 bytes after the call. */ - env->regs[14] += 4; - env->sregs[SR_PC] = env->regs[14]; - ret = do_syscall(env, - env->regs[12], - env->regs[5], - env->regs[6], - env->regs[7], - env->regs[8], - env->regs[9], - env->regs[10], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - /* Wind back to before the syscall. */ - env->sregs[SR_PC] -= 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[3] = ret; - } - /* All syscall exits result in guest r14 being equal to the - * PC we return to, because the kernel syscall exit "rtbd" does - * this. (This is true even for sigreturn(); note that r14 is - * not a userspace-usable register, as the kernel may clobber it - * at any point.) - */ - env->regs[14] = env->sregs[SR_PC]; - break; - case EXCP_HW_EXCP: - env->regs[17] = env->sregs[SR_PC] + 4; - if (env->iflags & D_FLAG) { - env->sregs[SR_ESR] |= 1 << 12; - env->sregs[SR_PC] -= 4; - /* FIXME: if branch was immed, replay the imm as well. */ - } - - env->iflags &= ~(IMM_FLAG | D_FLAG); - - switch (env->sregs[SR_ESR] & 31) { - case ESR_EC_DIVZERO: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_FLTDIV; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case ESR_EC_FPU: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - if (env->sregs[SR_FSR] & FSR_IO) { - info.si_code = TARGET_FPE_FLTINV; - } - if (env->sregs[SR_FSR] & FSR_DZ) { - info.si_code = TARGET_FPE_FLTDIV; - } - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - default: - printf ("Unhandled hw-exception: 0x%x\n", - env->sregs[SR_ESR] & ESR_EC_MASK); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - break; - } - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - printf ("Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - } -} -#endif - #ifdef TARGET_M68K void cpu_loop(CPUM68KState *env) @@ -2169,42 +2050,6 @@ int main(int argc, char **argv, char **envp) env->sr = regs->sr; ts->sim_syscalls = 1; } -#elif defined(TARGET_MICROBLAZE) - { - env->regs[0] = regs->r0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = regs->r14; - env->regs[15] = regs->r15; - env->regs[16] = regs->r16; - env->regs[17] = regs->r17; - env->regs[18] = regs->r18; - env->regs[19] = regs->r19; - env->regs[20] = regs->r20; - env->regs[21] = regs->r21; - env->regs[22] = regs->r22; - env->regs[23] = regs->r23; - env->regs[24] = regs->r24; - env->regs[25] = regs->r25; - env->regs[26] = regs->r26; - env->regs[27] = regs->r27; - env->regs[28] = regs->r28; - env->regs[29] = regs->r29; - env->regs[30] = regs->r30; - env->regs[31] = regs->r31; - env->sregs[SR_PC] = regs->pc; - } #elif defined(TARGET_RISCV) { env->pc = regs->sepc; diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index b7700a5561..5ffb83dea2 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -21,6 +21,156 @@ #include "qemu.h" #include "cpu_loop-common.h" +void cpu_loop(CPUMBState *env) +{ + CPUState *cs = CPU(mb_env_get_cpu(env)); + int trapnr, ret; + target_siginfo_t info; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case 0xaa: + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_BREAK: + /* Return address is 4 bytes after the call. */ + env->regs[14] += 4; + env->sregs[SR_PC] = env->regs[14]; + ret = do_syscall(env, + env->regs[12], + env->regs[5], + env->regs[6], + env->regs[7], + env->regs[8], + env->regs[9], + env->regs[10], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + /* Wind back to before the syscall. */ + env->sregs[SR_PC] -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[3] = ret; + } + /* All syscall exits result in guest r14 being equal to the + * PC we return to, because the kernel syscall exit "rtbd" does + * this. (This is true even for sigreturn(); note that r14 is + * not a userspace-usable register, as the kernel may clobber it + * at any point.) + */ + env->regs[14] = env->sregs[SR_PC]; + break; + case EXCP_HW_EXCP: + env->regs[17] = env->sregs[SR_PC] + 4; + if (env->iflags & D_FLAG) { + env->sregs[SR_ESR] |= 1 << 12; + env->sregs[SR_PC] -= 4; + /* FIXME: if branch was immed, replay the imm as well. */ + } + + env->iflags &= ~(IMM_FLAG | D_FLAG); + + switch (env->sregs[SR_ESR] & 31) { + case ESR_EC_DIVZERO: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_FLTDIV; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case ESR_EC_FPU: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + if (env->sregs[SR_FSR] & FSR_IO) { + info.si_code = TARGET_FPE_FLTINV; + } + if (env->sregs[SR_FSR] & FSR_DZ) { + info.si_code = TARGET_FPE_FLTDIV; + } + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + default: + printf ("Unhandled hw-exception: 0x%x\n", + env->sregs[SR_ESR] & ESR_EC_MASK); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + break; + } + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + printf ("Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + env->regs[0] = regs->r0; + env->regs[1] = regs->r1; + env->regs[2] = regs->r2; + env->regs[3] = regs->r3; + env->regs[4] = regs->r4; + env->regs[5] = regs->r5; + env->regs[6] = regs->r6; + env->regs[7] = regs->r7; + env->regs[8] = regs->r8; + env->regs[9] = regs->r9; + env->regs[10] = regs->r10; + env->regs[11] = regs->r11; + env->regs[12] = regs->r12; + env->regs[13] = regs->r13; + env->regs[14] = regs->r14; + env->regs[15] = regs->r15; + env->regs[16] = regs->r16; + env->regs[17] = regs->r17; + env->regs[18] = regs->r18; + env->regs[19] = regs->r19; + env->regs[20] = regs->r20; + env->regs[21] = regs->r21; + env->regs[22] = regs->r22; + env->regs[23] = regs->r23; + env->regs[24] = regs->r24; + env->regs[25] = regs->r25; + env->regs[26] = regs->r26; + env->regs[27] = regs->r27; + env->regs[28] = regs->r28; + env->regs[29] = regs->r29; + env->regs[30] = regs->r30; + env->regs[31] = regs->r31; + env->sregs[SR_PC] = regs->pc; } From ff9803b13b5663f70299b769e3e6059d2ea31e80 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:45 +0200 Subject: [PATCH 0158/2380] linux-user: move m68k cpu loop to m68k directory No code change, only move code from main.c to m68k/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411185651.21351-14-laurent@vivier.eu> --- linux-user/m68k/cpu_loop.c | 144 +++++++++++++++++++++++++++++++++++ linux-user/main.c | 150 +------------------------------------ 2 files changed, 145 insertions(+), 149 deletions(-) diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index b7700a5561..b4d3d8af3d 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -21,6 +21,150 @@ #include "qemu.h" #include "cpu_loop-common.h" +void cpu_loop(CPUM68KState *env) +{ + CPUState *cs = CPU(m68k_env_get_cpu(env)); + int trapnr; + unsigned int n; + target_siginfo_t info; + TaskState *ts = cs->opaque; + + for(;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch(trapnr) { + case EXCP_ILLEGAL: + { + if (ts->sim_syscalls) { + uint16_t nr; + get_user_u16(nr, env->pc + 2); + env->pc += 4; + do_m68k_simcall(env, nr); + } else { + goto do_sigill; + } + } + break; + case EXCP_HALT_INSN: + /* Semihosing syscall. */ + env->pc += 4; + do_m68k_semihosting(env, env->dregs[0]); + break; + case EXCP_LINEA: + case EXCP_LINEF: + case EXCP_UNSUPPORTED: + do_sigill: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_CHK: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTOVF; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_DIV0: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTDIV; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_TRAP0: + { + abi_long ret; + ts->sim_syscalls = 0; + n = env->dregs[0]; + env->pc += 2; + ret = do_syscall(env, + n, + env->dregs[1], + env->dregs[2], + env->dregs[3], + env->dregs[4], + env->dregs[5], + env->aregs[0], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->dregs[0] = ret; + } + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_ACCESS: + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->mmu.ar; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); + abort(); + } + process_pending_signals(env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + + env->pc = regs->pc; + env->dregs[0] = regs->d0; + env->dregs[1] = regs->d1; + env->dregs[2] = regs->d2; + env->dregs[3] = regs->d3; + env->dregs[4] = regs->d4; + env->dregs[5] = regs->d5; + env->dregs[6] = regs->d6; + env->dregs[7] = regs->d7; + env->aregs[0] = regs->a0; + env->aregs[1] = regs->a1; + env->aregs[2] = regs->a2; + env->aregs[3] = regs->a3; + env->aregs[4] = regs->a4; + env->aregs[5] = regs->a5; + env->aregs[6] = regs->a6; + env->aregs[7] = regs->usp; + env->sr = regs->sr; + + ts->sim_syscalls = 1; + ts->stack_base = info->start_stack; + ts->heap_base = info->brk; + /* This will be filled in on the first SYS_HEAPINFO call. */ + ts->heap_limit = 0; } diff --git a/linux-user/main.c b/linux-user/main.c index 9e49c8a30c..7c223a5629 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,125 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_M68K - -void cpu_loop(CPUM68KState *env) -{ - CPUState *cs = CPU(m68k_env_get_cpu(env)); - int trapnr; - unsigned int n; - target_siginfo_t info; - TaskState *ts = cs->opaque; - - for(;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch(trapnr) { - case EXCP_ILLEGAL: - { - if (ts->sim_syscalls) { - uint16_t nr; - get_user_u16(nr, env->pc + 2); - env->pc += 4; - do_m68k_simcall(env, nr); - } else { - goto do_sigill; - } - } - break; - case EXCP_HALT_INSN: - /* Semihosing syscall. */ - env->pc += 4; - do_m68k_semihosting(env, env->dregs[0]); - break; - case EXCP_LINEA: - case EXCP_LINEF: - case EXCP_UNSUPPORTED: - do_sigill: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_CHK: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_INTOVF; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_DIV0: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_INTDIV; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_TRAP0: - { - abi_long ret; - ts->sim_syscalls = 0; - n = env->dregs[0]; - env->pc += 2; - ret = do_syscall(env, - n, - env->dregs[1], - env->dregs[2], - env->dregs[3], - env->dregs[4], - env->dregs[5], - env->aregs[0], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 2; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->dregs[0] = ret; - } - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_ACCESS: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->mmu.ar; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); - abort(); - } - process_pending_signals(env); - } -} -#endif /* TARGET_M68K */ - #ifdef TARGET_ALPHA void cpu_loop(CPUAlphaState *env) { @@ -2028,29 +1909,7 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); -#if defined(TARGET_M68K) - { - env->pc = regs->pc; - env->dregs[0] = regs->d0; - env->dregs[1] = regs->d1; - env->dregs[2] = regs->d2; - env->dregs[3] = regs->d3; - env->dregs[4] = regs->d4; - env->dregs[5] = regs->d5; - env->dregs[6] = regs->d6; - env->dregs[7] = regs->d7; - env->aregs[0] = regs->a0; - env->aregs[1] = regs->a1; - env->aregs[2] = regs->a2; - env->aregs[3] = regs->a3; - env->aregs[4] = regs->a4; - env->aregs[5] = regs->a5; - env->aregs[6] = regs->a6; - env->aregs[7] = regs->usp; - env->sr = regs->sr; - ts->sim_syscalls = 1; - } -#elif defined(TARGET_RISCV) +#if defined(TARGET_RISCV) { env->pc = regs->sepc; env->gpr[xSP] = regs->sp; @@ -2105,13 +1964,6 @@ int main(int argc, char **argv, char **envp) } #endif -#if defined(TARGET_M68K) - ts->stack_base = info->start_stack; - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; -#endif - if (gdbstub_port) { if (gdbserver_start(gdbstub_port) < 0) { fprintf(stderr, "qemu: could not open gdbserver on port %d\n", From e256aefe0ddefca4618d75951f948890f9b1f186 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:46 +0200 Subject: [PATCH 0159/2380] linux-user: move alpha cpu loop to alpha directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from main.c to alpha/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180411185651.21351-15-laurent@vivier.eu> --- linux-user/alpha/cpu_loop.c | 199 +++++++++++++++++++++++++++++++++++ linux-user/main.c | 204 ------------------------------------ 2 files changed, 199 insertions(+), 204 deletions(-) diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index b7700a5561..b87fcaea87 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -21,6 +21,205 @@ #include "qemu.h" #include "cpu_loop-common.h" +void cpu_loop(CPUAlphaState *env) +{ + CPUState *cs = CPU(alpha_env_get_cpu(env)); + int trapnr; + target_siginfo_t info; + abi_long sysret; + + while (1) { + bool arch_interrupt = true; + + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_RESET: + fprintf(stderr, "Reset requested. Exit\n"); + exit(EXIT_FAILURE); + break; + case EXCP_MCHK: + fprintf(stderr, "Machine check exception. Exit\n"); + exit(EXIT_FAILURE); + break; + case EXCP_SMP_INTERRUPT: + case EXCP_CLK_INTERRUPT: + case EXCP_DEV_INTERRUPT: + fprintf(stderr, "External interrupt. Exit\n"); + exit(EXIT_FAILURE); + break; + case EXCP_MMFAULT: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID + ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR); + info._sifields._sigfault._addr = env->trap_arg0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_UNALIGN: + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_BUS_ADRALN; + info._sifields._sigfault._addr = env->trap_arg0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_OPCDEC: + do_sigill: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPC; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ARITH: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_FLTINV; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_FEN: + /* No-op. Linux simply re-enables the FPU. */ + break; + case EXCP_CALL_PAL: + switch (env->error_code) { + case 0x80: + /* BPT */ + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case 0x81: + /* BUGCHK */ + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case 0x83: + /* CALLSYS */ + trapnr = env->ir[IR_V0]; + sysret = do_syscall(env, trapnr, + env->ir[IR_A0], env->ir[IR_A1], + env->ir[IR_A2], env->ir[IR_A3], + env->ir[IR_A4], env->ir[IR_A5], + 0, 0); + if (sysret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + break; + } + if (sysret == -TARGET_QEMU_ESIGRETURN) { + break; + } + /* Syscall writes 0 to V0 to bypass error check, similar + to how this is handled internal to Linux kernel. + (Ab)use trapnr temporarily as boolean indicating error. */ + trapnr = (env->ir[IR_V0] != 0 && sysret < 0); + env->ir[IR_V0] = (trapnr ? -sysret : sysret); + env->ir[IR_A3] = trapnr; + break; + case 0x86: + /* IMB */ + /* ??? We can probably elide the code using page_unprotect + that is checking for self-modifying code. Instead we + could simply call tb_flush here. Until we work out the + changes required to turn off the extra write protection, + this can be a no-op. */ + break; + case 0x9E: + /* RDUNIQUE */ + /* Handled in the translator for usermode. */ + abort(); + case 0x9F: + /* WRUNIQUE */ + /* Handled in the translator for usermode. */ + abort(); + case 0xAA: + /* GENTRAP */ + info.si_signo = TARGET_SIGFPE; + switch (env->ir[IR_A0]) { + case TARGET_GEN_INTOVF: + info.si_code = TARGET_FPE_INTOVF; + break; + case TARGET_GEN_INTDIV: + info.si_code = TARGET_FPE_INTDIV; + break; + case TARGET_GEN_FLTOVF: + info.si_code = TARGET_FPE_FLTOVF; + break; + case TARGET_GEN_FLTUND: + info.si_code = TARGET_FPE_FLTUND; + break; + case TARGET_GEN_FLTINV: + info.si_code = TARGET_FPE_FLTINV; + break; + case TARGET_GEN_FLTINE: + info.si_code = TARGET_FPE_FLTRES; + break; + case TARGET_GEN_ROPRAND: + info.si_code = 0; + break; + default: + info.si_signo = TARGET_SIGTRAP; + info.si_code = 0; + break; + } + info.si_errno = 0; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + default: + goto do_sigill; + } + break; + case EXCP_DEBUG: + info.si_signo = gdb_handlesig(cs, TARGET_SIGTRAP); + if (info.si_signo) { + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } else { + arch_interrupt = false; + } + break; + case EXCP_INTERRUPT: + /* Just indicate that signals should be handled asap. */ + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + arch_interrupt = false; + break; + default: + printf ("Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + + /* Most of the traps imply a transition through PALcode, which + implies an REI instruction has been executed. Which means + that RX and LOCK_ADDR should be cleared. But there are a + few exceptions for traps internal to QEMU. */ + if (arch_interrupt) { + env->flags &= ~ENV_FLAG_RX_FLAG; + env->lock_addr = -1; + } + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + int i; + + for(i = 0; i < 28; i++) { + env->ir[i] = ((abi_ulong *)regs)[i]; + } + env->ir[IR_SP] = regs->usp; + env->pc = regs->pc; } diff --git a/linux-user/main.c b/linux-user/main.c index 7c223a5629..37cf3a7d6f 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,200 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_ALPHA -void cpu_loop(CPUAlphaState *env) -{ - CPUState *cs = CPU(alpha_env_get_cpu(env)); - int trapnr; - target_siginfo_t info; - abi_long sysret; - - while (1) { - bool arch_interrupt = true; - - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_RESET: - fprintf(stderr, "Reset requested. Exit\n"); - exit(EXIT_FAILURE); - break; - case EXCP_MCHK: - fprintf(stderr, "Machine check exception. Exit\n"); - exit(EXIT_FAILURE); - break; - case EXCP_SMP_INTERRUPT: - case EXCP_CLK_INTERRUPT: - case EXCP_DEV_INTERRUPT: - fprintf(stderr, "External interrupt. Exit\n"); - exit(EXIT_FAILURE); - break; - case EXCP_MMFAULT: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID - ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR); - info._sifields._sigfault._addr = env->trap_arg0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_UNALIGN: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_BUS_ADRALN; - info._sifields._sigfault._addr = env->trap_arg0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_OPCDEC: - do_sigill: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_ARITH: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_FLTINV; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_FEN: - /* No-op. Linux simply re-enables the FPU. */ - break; - case EXCP_CALL_PAL: - switch (env->error_code) { - case 0x80: - /* BPT */ - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case 0x81: - /* BUGCHK */ - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case 0x83: - /* CALLSYS */ - trapnr = env->ir[IR_V0]; - sysret = do_syscall(env, trapnr, - env->ir[IR_A0], env->ir[IR_A1], - env->ir[IR_A2], env->ir[IR_A3], - env->ir[IR_A4], env->ir[IR_A5], - 0, 0); - if (sysret == -TARGET_ERESTARTSYS) { - env->pc -= 4; - break; - } - if (sysret == -TARGET_QEMU_ESIGRETURN) { - break; - } - /* Syscall writes 0 to V0 to bypass error check, similar - to how this is handled internal to Linux kernel. - (Ab)use trapnr temporarily as boolean indicating error. */ - trapnr = (env->ir[IR_V0] != 0 && sysret < 0); - env->ir[IR_V0] = (trapnr ? -sysret : sysret); - env->ir[IR_A3] = trapnr; - break; - case 0x86: - /* IMB */ - /* ??? We can probably elide the code using page_unprotect - that is checking for self-modifying code. Instead we - could simply call tb_flush here. Until we work out the - changes required to turn off the extra write protection, - this can be a no-op. */ - break; - case 0x9E: - /* RDUNIQUE */ - /* Handled in the translator for usermode. */ - abort(); - case 0x9F: - /* WRUNIQUE */ - /* Handled in the translator for usermode. */ - abort(); - case 0xAA: - /* GENTRAP */ - info.si_signo = TARGET_SIGFPE; - switch (env->ir[IR_A0]) { - case TARGET_GEN_INTOVF: - info.si_code = TARGET_FPE_INTOVF; - break; - case TARGET_GEN_INTDIV: - info.si_code = TARGET_FPE_INTDIV; - break; - case TARGET_GEN_FLTOVF: - info.si_code = TARGET_FPE_FLTOVF; - break; - case TARGET_GEN_FLTUND: - info.si_code = TARGET_FPE_FLTUND; - break; - case TARGET_GEN_FLTINV: - info.si_code = TARGET_FPE_FLTINV; - break; - case TARGET_GEN_FLTINE: - info.si_code = TARGET_FPE_FLTRES; - break; - case TARGET_GEN_ROPRAND: - info.si_code = 0; - break; - default: - info.si_signo = TARGET_SIGTRAP; - info.si_code = 0; - break; - } - info.si_errno = 0; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - default: - goto do_sigill; - } - break; - case EXCP_DEBUG: - info.si_signo = gdb_handlesig(cs, TARGET_SIGTRAP); - if (info.si_signo) { - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else { - arch_interrupt = false; - } - break; - case EXCP_INTERRUPT: - /* Just indicate that signals should be handled asap. */ - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - arch_interrupt = false; - break; - default: - printf ("Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - - /* Most of the traps imply a transition through PALcode, which - implies an REI instruction has been executed. Which means - that RX and LOCK_ADDR should be cleared. But there are a - few exceptions for traps internal to QEMU. */ - if (arch_interrupt) { - env->flags &= ~ENV_FLAG_RX_FLAG; - env->lock_addr = -1; - } - } -} -#endif /* TARGET_ALPHA */ - #ifdef TARGET_S390X /* s390x masks the fault address it reports in si_addr for SIGSEGV and SIGBUS */ @@ -1914,16 +1720,6 @@ int main(int argc, char **argv, char **envp) env->pc = regs->sepc; env->gpr[xSP] = regs->sp; } -#elif defined(TARGET_ALPHA) - { - int i; - - for(i = 0; i < 28; i++) { - env->ir[i] = ((abi_ulong *)regs)[i]; - } - env->ir[IR_SP] = regs->usp; - env->pc = regs->pc; - } #elif defined(TARGET_S390X) { int i; From a5fd8ee1f7e8e4d62c0362c04834bb9267810311 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:47 +0200 Subject: [PATCH 0160/2380] linux-user: move s390x cpu loop to s390x directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from main.c to s390x/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Acked-by: Cornelia Huck Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180411185651.21351-16-laurent@vivier.eu> --- linux-user/main.c | 146 ------------------------------------ linux-user/s390x/cpu_loop.c | 139 ++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 146 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 37cf3a7d6f..7f6cfa5548 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,143 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_S390X - -/* s390x masks the fault address it reports in si_addr for SIGSEGV and SIGBUS */ -#define S390X_FAIL_ADDR_MASK -4096LL - -void cpu_loop(CPUS390XState *env) -{ - CPUState *cs = CPU(s390_env_get_cpu(env)); - int trapnr, n, sig; - target_siginfo_t info; - target_ulong addr; - abi_long ret; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_INTERRUPT: - /* Just indicate that signals should be handled asap. */ - break; - - case EXCP_SVC: - n = env->int_svc_code; - if (!n) { - /* syscalls > 255 */ - n = env->regs[1]; - } - env->psw.addr += env->int_svc_ilen; - ret = do_syscall(env, n, env->regs[2], env->regs[3], - env->regs[4], env->regs[5], - env->regs[6], env->regs[7], 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->psw.addr -= env->int_svc_ilen; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[2] = ret; - } - break; - - case EXCP_DEBUG: - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - n = TARGET_TRAP_BRKPT; - goto do_signal_pc; - } - break; - case EXCP_PGM: - n = env->int_pgm_code; - switch (n) { - case PGM_OPERATION: - case PGM_PRIVILEGED: - sig = TARGET_SIGILL; - n = TARGET_ILL_ILLOPC; - goto do_signal_pc; - case PGM_PROTECTION: - case PGM_ADDRESSING: - sig = TARGET_SIGSEGV; - /* XXX: check env->error_code */ - n = TARGET_SEGV_MAPERR; - addr = env->__excp_addr & S390X_FAIL_ADDR_MASK; - goto do_signal; - case PGM_EXECUTE: - case PGM_SPECIFICATION: - case PGM_SPECIAL_OP: - case PGM_OPERAND: - do_sigill_opn: - sig = TARGET_SIGILL; - n = TARGET_ILL_ILLOPN; - goto do_signal_pc; - - case PGM_FIXPT_OVERFLOW: - sig = TARGET_SIGFPE; - n = TARGET_FPE_INTOVF; - goto do_signal_pc; - case PGM_FIXPT_DIVIDE: - sig = TARGET_SIGFPE; - n = TARGET_FPE_INTDIV; - goto do_signal_pc; - - case PGM_DATA: - n = (env->fpc >> 8) & 0xff; - if (n == 0xff) { - /* compare-and-trap */ - goto do_sigill_opn; - } else { - /* An IEEE exception, simulated or otherwise. */ - if (n & 0x80) { - n = TARGET_FPE_FLTINV; - } else if (n & 0x40) { - n = TARGET_FPE_FLTDIV; - } else if (n & 0x20) { - n = TARGET_FPE_FLTOVF; - } else if (n & 0x10) { - n = TARGET_FPE_FLTUND; - } else if (n & 0x08) { - n = TARGET_FPE_FLTRES; - } else { - /* ??? Quantum exception; BFP, DFP error. */ - goto do_sigill_opn; - } - sig = TARGET_SIGFPE; - goto do_signal_pc; - } - - default: - fprintf(stderr, "Unhandled program exception: %#x\n", n); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - break; - - do_signal_pc: - addr = env->psw.addr; - do_signal: - info.si_signo = sig; - info.si_errno = 0; - info.si_code = n; - info._sifields._sigfault._addr = addr; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - } -} - -#endif /* TARGET_S390X */ - #ifdef TARGET_TILEGX static void gen_sigill_reg(CPUTLGState *env) @@ -1720,15 +1583,6 @@ int main(int argc, char **argv, char **envp) env->pc = regs->sepc; env->gpr[xSP] = regs->sp; } -#elif defined(TARGET_S390X) - { - int i; - for (i = 0; i < 16; i++) { - env->regs[i] = regs->gprs[i]; - } - env->psw.mask = regs->psw.mask; - env->psw.addr = regs->psw.addr; - } #elif defined(TARGET_TILEGX) { int i; diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index b7700a5561..99f5f1594f 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -21,6 +21,145 @@ #include "qemu.h" #include "cpu_loop-common.h" +/* s390x masks the fault address it reports in si_addr for SIGSEGV and SIGBUS */ +#define S390X_FAIL_ADDR_MASK -4096LL + +void cpu_loop(CPUS390XState *env) +{ + CPUState *cs = CPU(s390_env_get_cpu(env)); + int trapnr, n, sig; + target_siginfo_t info; + target_ulong addr; + abi_long ret; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_INTERRUPT: + /* Just indicate that signals should be handled asap. */ + break; + + case EXCP_SVC: + n = env->int_svc_code; + if (!n) { + /* syscalls > 255 */ + n = env->regs[1]; + } + env->psw.addr += env->int_svc_ilen; + ret = do_syscall(env, n, env->regs[2], env->regs[3], + env->regs[4], env->regs[5], + env->regs[6], env->regs[7], 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->psw.addr -= env->int_svc_ilen; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[2] = ret; + } + break; + + case EXCP_DEBUG: + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) { + n = TARGET_TRAP_BRKPT; + goto do_signal_pc; + } + break; + case EXCP_PGM: + n = env->int_pgm_code; + switch (n) { + case PGM_OPERATION: + case PGM_PRIVILEGED: + sig = TARGET_SIGILL; + n = TARGET_ILL_ILLOPC; + goto do_signal_pc; + case PGM_PROTECTION: + case PGM_ADDRESSING: + sig = TARGET_SIGSEGV; + /* XXX: check env->error_code */ + n = TARGET_SEGV_MAPERR; + addr = env->__excp_addr & S390X_FAIL_ADDR_MASK; + goto do_signal; + case PGM_EXECUTE: + case PGM_SPECIFICATION: + case PGM_SPECIAL_OP: + case PGM_OPERAND: + do_sigill_opn: + sig = TARGET_SIGILL; + n = TARGET_ILL_ILLOPN; + goto do_signal_pc; + + case PGM_FIXPT_OVERFLOW: + sig = TARGET_SIGFPE; + n = TARGET_FPE_INTOVF; + goto do_signal_pc; + case PGM_FIXPT_DIVIDE: + sig = TARGET_SIGFPE; + n = TARGET_FPE_INTDIV; + goto do_signal_pc; + + case PGM_DATA: + n = (env->fpc >> 8) & 0xff; + if (n == 0xff) { + /* compare-and-trap */ + goto do_sigill_opn; + } else { + /* An IEEE exception, simulated or otherwise. */ + if (n & 0x80) { + n = TARGET_FPE_FLTINV; + } else if (n & 0x40) { + n = TARGET_FPE_FLTDIV; + } else if (n & 0x20) { + n = TARGET_FPE_FLTOVF; + } else if (n & 0x10) { + n = TARGET_FPE_FLTUND; + } else if (n & 0x08) { + n = TARGET_FPE_FLTRES; + } else { + /* ??? Quantum exception; BFP, DFP error. */ + goto do_sigill_opn; + } + sig = TARGET_SIGFPE; + goto do_signal_pc; + } + + default: + fprintf(stderr, "Unhandled program exception: %#x\n", n); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + break; + + do_signal_pc: + addr = env->psw.addr; + do_signal: + info.si_signo = sig; + info.si_errno = 0; + info.si_code = n; + info._sifields._sigfault._addr = addr; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + int i; + for (i = 0; i < 16; i++) { + env->regs[i] = regs->gprs[i]; + } + env->psw.mask = regs->psw.mask; + env->psw.addr = regs->psw.addr; } From 9397e5649771d36c2a10e0cbebb5cf04ad02967c Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:48 +0200 Subject: [PATCH 0161/2380] linux-user: move tilegx cpu loop to tilegx directory No code change, only move code from main.c to tilegx/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411185651.21351-17-laurent@vivier.eu> --- linux-user/main.c | 267 ----------------------------------- linux-user/tilegx/cpu_loop.c | 260 ++++++++++++++++++++++++++++++++++ 2 files changed, 260 insertions(+), 267 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 7f6cfa5548..32922110f1 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,262 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_TILEGX - -static void gen_sigill_reg(CPUTLGState *env) -{ - target_siginfo_t info; - - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_PRVREG; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); -} - -static void do_signal(CPUTLGState *env, int signo, int sigcode) -{ - target_siginfo_t info; - - info.si_signo = signo; - info.si_errno = 0; - info._sifields._sigfault._addr = env->pc; - - if (signo == TARGET_SIGSEGV) { - /* The passed in sigcode is a dummy; check for a page mapping - and pass either MAPERR or ACCERR. */ - target_ulong addr = env->excaddr; - info._sifields._sigfault._addr = addr; - if (page_check_range(addr, 1, PAGE_VALID) < 0) { - sigcode = TARGET_SEGV_MAPERR; - } else { - sigcode = TARGET_SEGV_ACCERR; - } - } - info.si_code = sigcode; - - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); -} - -static void gen_sigsegv_maperr(CPUTLGState *env, target_ulong addr) -{ - env->excaddr = addr; - do_signal(env, TARGET_SIGSEGV, 0); -} - -static void set_regval(CPUTLGState *env, uint8_t reg, uint64_t val) -{ - if (unlikely(reg >= TILEGX_R_COUNT)) { - switch (reg) { - case TILEGX_R_SN: - case TILEGX_R_ZERO: - return; - case TILEGX_R_IDN0: - case TILEGX_R_IDN1: - case TILEGX_R_UDN0: - case TILEGX_R_UDN1: - case TILEGX_R_UDN2: - case TILEGX_R_UDN3: - gen_sigill_reg(env); - return; - default: - g_assert_not_reached(); - } - } - env->regs[reg] = val; -} - -/* - * Compare the 8-byte contents of the CmpValue SPR with the 8-byte value in - * memory at the address held in the first source register. If the values are - * not equal, then no memory operation is performed. If the values are equal, - * the 8-byte quantity from the second source register is written into memory - * at the address held in the first source register. In either case, the result - * of the instruction is the value read from memory. The compare and write to - * memory are atomic and thus can be used for synchronization purposes. This - * instruction only operates for addresses aligned to a 8-byte boundary. - * Unaligned memory access causes an Unaligned Data Reference interrupt. - * - * Functional Description (64-bit) - * uint64_t memVal = memoryReadDoubleWord (rf[SrcA]); - * rf[Dest] = memVal; - * if (memVal == SPR[CmpValueSPR]) - * memoryWriteDoubleWord (rf[SrcA], rf[SrcB]); - * - * Functional Description (32-bit) - * uint64_t memVal = signExtend32 (memoryReadWord (rf[SrcA])); - * rf[Dest] = memVal; - * if (memVal == signExtend32 (SPR[CmpValueSPR])) - * memoryWriteWord (rf[SrcA], rf[SrcB]); - * - * - * This function also processes exch and exch4 which need not process SPR. - */ -static void do_exch(CPUTLGState *env, bool quad, bool cmp) -{ - target_ulong addr; - target_long val, sprval; - - start_exclusive(); - - addr = env->atomic_srca; - if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { - goto sigsegv_maperr; - } - - if (cmp) { - if (quad) { - sprval = env->spregs[TILEGX_SPR_CMPEXCH]; - } else { - sprval = sextract64(env->spregs[TILEGX_SPR_CMPEXCH], 0, 32); - } - } - - if (!cmp || val == sprval) { - target_long valb = env->atomic_srcb; - if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { - goto sigsegv_maperr; - } - } - - set_regval(env, env->atomic_dstr, val); - end_exclusive(); - return; - - sigsegv_maperr: - end_exclusive(); - gen_sigsegv_maperr(env, addr); -} - -static void do_fetch(CPUTLGState *env, int trapnr, bool quad) -{ - int8_t write = 1; - target_ulong addr; - target_long val, valb; - - start_exclusive(); - - addr = env->atomic_srca; - valb = env->atomic_srcb; - if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { - goto sigsegv_maperr; - } - - switch (trapnr) { - case TILEGX_EXCP_OPCODE_FETCHADD: - case TILEGX_EXCP_OPCODE_FETCHADD4: - valb += val; - break; - case TILEGX_EXCP_OPCODE_FETCHADDGEZ: - valb += val; - if (valb < 0) { - write = 0; - } - break; - case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: - valb += val; - if ((int32_t)valb < 0) { - write = 0; - } - break; - case TILEGX_EXCP_OPCODE_FETCHAND: - case TILEGX_EXCP_OPCODE_FETCHAND4: - valb &= val; - break; - case TILEGX_EXCP_OPCODE_FETCHOR: - case TILEGX_EXCP_OPCODE_FETCHOR4: - valb |= val; - break; - default: - g_assert_not_reached(); - } - - if (write) { - if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { - goto sigsegv_maperr; - } - } - - set_regval(env, env->atomic_dstr, val); - end_exclusive(); - return; - - sigsegv_maperr: - end_exclusive(); - gen_sigsegv_maperr(env, addr); -} - -void cpu_loop(CPUTLGState *env) -{ - CPUState *cs = CPU(tilegx_env_get_cpu(env)); - int trapnr; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case TILEGX_EXCP_SYSCALL: - { - abi_ulong ret = do_syscall(env, env->regs[TILEGX_R_NR], - env->regs[0], env->regs[1], - env->regs[2], env->regs[3], - env->regs[4], env->regs[5], - env->regs[6], env->regs[7]); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 8; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[TILEGX_R_RE] = ret; - env->regs[TILEGX_R_ERR] = TILEGX_IS_ERRNO(ret) ? -ret : 0; - } - break; - } - case TILEGX_EXCP_OPCODE_EXCH: - do_exch(env, true, false); - break; - case TILEGX_EXCP_OPCODE_EXCH4: - do_exch(env, false, false); - break; - case TILEGX_EXCP_OPCODE_CMPEXCH: - do_exch(env, true, true); - break; - case TILEGX_EXCP_OPCODE_CMPEXCH4: - do_exch(env, false, true); - break; - case TILEGX_EXCP_OPCODE_FETCHADD: - case TILEGX_EXCP_OPCODE_FETCHADDGEZ: - case TILEGX_EXCP_OPCODE_FETCHAND: - case TILEGX_EXCP_OPCODE_FETCHOR: - do_fetch(env, trapnr, true); - break; - case TILEGX_EXCP_OPCODE_FETCHADD4: - case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: - case TILEGX_EXCP_OPCODE_FETCHAND4: - case TILEGX_EXCP_OPCODE_FETCHOR4: - do_fetch(env, trapnr, false); - break; - case TILEGX_EXCP_SIGNAL: - do_signal(env, env->signo, env->sigcode); - break; - case TILEGX_EXCP_REG_IDN_ACCESS: - case TILEGX_EXCP_REG_UDN_ACCESS: - gen_sigill_reg(env); - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - fprintf(stderr, "trapnr is %d[0x%x].\n", trapnr, trapnr); - g_assert_not_reached(); - } - process_pending_signals(env); - } -} - -#endif - #ifdef TARGET_RISCV void cpu_loop(CPURISCVState *env) @@ -1583,17 +1327,6 @@ int main(int argc, char **argv, char **envp) env->pc = regs->sepc; env->gpr[xSP] = regs->sp; } -#elif defined(TARGET_TILEGX) - { - int i; - for (i = 0; i < TILEGX_R_COUNT; i++) { - env->regs[i] = regs->regs[i]; - } - for (i = 0; i < TILEGX_SPR_COUNT; i++) { - env->spregs[i] = 0; - } - env->pc = regs->pc; - } #elif defined(TARGET_HPPA) { int i; diff --git a/linux-user/tilegx/cpu_loop.c b/linux-user/tilegx/cpu_loop.c index b7700a5561..4f39eb9ad3 100644 --- a/linux-user/tilegx/cpu_loop.c +++ b/linux-user/tilegx/cpu_loop.c @@ -21,6 +21,266 @@ #include "qemu.h" #include "cpu_loop-common.h" +static void gen_sigill_reg(CPUTLGState *env) +{ + target_siginfo_t info; + + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_PRVREG; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); +} + +static void do_signal(CPUTLGState *env, int signo, int sigcode) +{ + target_siginfo_t info; + + info.si_signo = signo; + info.si_errno = 0; + info._sifields._sigfault._addr = env->pc; + + if (signo == TARGET_SIGSEGV) { + /* The passed in sigcode is a dummy; check for a page mapping + and pass either MAPERR or ACCERR. */ + target_ulong addr = env->excaddr; + info._sifields._sigfault._addr = addr; + if (page_check_range(addr, 1, PAGE_VALID) < 0) { + sigcode = TARGET_SEGV_MAPERR; + } else { + sigcode = TARGET_SEGV_ACCERR; + } + } + info.si_code = sigcode; + + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); +} + +static void gen_sigsegv_maperr(CPUTLGState *env, target_ulong addr) +{ + env->excaddr = addr; + do_signal(env, TARGET_SIGSEGV, 0); +} + +static void set_regval(CPUTLGState *env, uint8_t reg, uint64_t val) +{ + if (unlikely(reg >= TILEGX_R_COUNT)) { + switch (reg) { + case TILEGX_R_SN: + case TILEGX_R_ZERO: + return; + case TILEGX_R_IDN0: + case TILEGX_R_IDN1: + case TILEGX_R_UDN0: + case TILEGX_R_UDN1: + case TILEGX_R_UDN2: + case TILEGX_R_UDN3: + gen_sigill_reg(env); + return; + default: + g_assert_not_reached(); + } + } + env->regs[reg] = val; +} + +/* + * Compare the 8-byte contents of the CmpValue SPR with the 8-byte value in + * memory at the address held in the first source register. If the values are + * not equal, then no memory operation is performed. If the values are equal, + * the 8-byte quantity from the second source register is written into memory + * at the address held in the first source register. In either case, the result + * of the instruction is the value read from memory. The compare and write to + * memory are atomic and thus can be used for synchronization purposes. This + * instruction only operates for addresses aligned to a 8-byte boundary. + * Unaligned memory access causes an Unaligned Data Reference interrupt. + * + * Functional Description (64-bit) + * uint64_t memVal = memoryReadDoubleWord (rf[SrcA]); + * rf[Dest] = memVal; + * if (memVal == SPR[CmpValueSPR]) + * memoryWriteDoubleWord (rf[SrcA], rf[SrcB]); + * + * Functional Description (32-bit) + * uint64_t memVal = signExtend32 (memoryReadWord (rf[SrcA])); + * rf[Dest] = memVal; + * if (memVal == signExtend32 (SPR[CmpValueSPR])) + * memoryWriteWord (rf[SrcA], rf[SrcB]); + * + * + * This function also processes exch and exch4 which need not process SPR. + */ +static void do_exch(CPUTLGState *env, bool quad, bool cmp) +{ + target_ulong addr; + target_long val, sprval; + + start_exclusive(); + + addr = env->atomic_srca; + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto sigsegv_maperr; + } + + if (cmp) { + if (quad) { + sprval = env->spregs[TILEGX_SPR_CMPEXCH]; + } else { + sprval = sextract64(env->spregs[TILEGX_SPR_CMPEXCH], 0, 32); + } + } + + if (!cmp || val == sprval) { + target_long valb = env->atomic_srcb; + if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { + goto sigsegv_maperr; + } + } + + set_regval(env, env->atomic_dstr, val); + end_exclusive(); + return; + + sigsegv_maperr: + end_exclusive(); + gen_sigsegv_maperr(env, addr); +} + +static void do_fetch(CPUTLGState *env, int trapnr, bool quad) +{ + int8_t write = 1; + target_ulong addr; + target_long val, valb; + + start_exclusive(); + + addr = env->atomic_srca; + valb = env->atomic_srcb; + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto sigsegv_maperr; + } + + switch (trapnr) { + case TILEGX_EXCP_OPCODE_FETCHADD: + case TILEGX_EXCP_OPCODE_FETCHADD4: + valb += val; + break; + case TILEGX_EXCP_OPCODE_FETCHADDGEZ: + valb += val; + if (valb < 0) { + write = 0; + } + break; + case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: + valb += val; + if ((int32_t)valb < 0) { + write = 0; + } + break; + case TILEGX_EXCP_OPCODE_FETCHAND: + case TILEGX_EXCP_OPCODE_FETCHAND4: + valb &= val; + break; + case TILEGX_EXCP_OPCODE_FETCHOR: + case TILEGX_EXCP_OPCODE_FETCHOR4: + valb |= val; + break; + default: + g_assert_not_reached(); + } + + if (write) { + if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { + goto sigsegv_maperr; + } + } + + set_regval(env, env->atomic_dstr, val); + end_exclusive(); + return; + + sigsegv_maperr: + end_exclusive(); + gen_sigsegv_maperr(env, addr); +} + +void cpu_loop(CPUTLGState *env) +{ + CPUState *cs = CPU(tilegx_env_get_cpu(env)); + int trapnr; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case TILEGX_EXCP_SYSCALL: + { + abi_ulong ret = do_syscall(env, env->regs[TILEGX_R_NR], + env->regs[0], env->regs[1], + env->regs[2], env->regs[3], + env->regs[4], env->regs[5], + env->regs[6], env->regs[7]); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 8; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[TILEGX_R_RE] = ret; + env->regs[TILEGX_R_ERR] = TILEGX_IS_ERRNO(ret) ? -ret : 0; + } + break; + } + case TILEGX_EXCP_OPCODE_EXCH: + do_exch(env, true, false); + break; + case TILEGX_EXCP_OPCODE_EXCH4: + do_exch(env, false, false); + break; + case TILEGX_EXCP_OPCODE_CMPEXCH: + do_exch(env, true, true); + break; + case TILEGX_EXCP_OPCODE_CMPEXCH4: + do_exch(env, false, true); + break; + case TILEGX_EXCP_OPCODE_FETCHADD: + case TILEGX_EXCP_OPCODE_FETCHADDGEZ: + case TILEGX_EXCP_OPCODE_FETCHAND: + case TILEGX_EXCP_OPCODE_FETCHOR: + do_fetch(env, trapnr, true); + break; + case TILEGX_EXCP_OPCODE_FETCHADD4: + case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: + case TILEGX_EXCP_OPCODE_FETCHAND4: + case TILEGX_EXCP_OPCODE_FETCHOR4: + do_fetch(env, trapnr, false); + break; + case TILEGX_EXCP_SIGNAL: + do_signal(env, env->signo, env->sigcode); + break; + case TILEGX_EXCP_REG_IDN_ACCESS: + case TILEGX_EXCP_REG_UDN_ACCESS: + gen_sigill_reg(env); + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + fprintf(stderr, "trapnr is %d[0x%x].\n", trapnr, trapnr); + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + int i; + for (i = 0; i < TILEGX_R_COUNT; i++) { + env->regs[i] = regs->regs[i]; + } + for (i = 0; i < TILEGX_SPR_COUNT; i++) { + env->spregs[i] = 0; + } + env->pc = regs->pc; } From 5a0b6d22862531f533ae3cf7e382a3f6d0495ab4 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:49 +0200 Subject: [PATCH 0162/2380] linux-user: move riscv cpu loop to riscv directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from main.c to riscv/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Reviewed-by: Michael Clark Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180411185651.21351-18-laurent@vivier.eu> --- linux-user/main.c | 101 +----------------------------------- linux-user/riscv/cpu_loop.c | 92 ++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 100 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 32922110f1..834ec0bfe5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,100 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_RISCV - -void cpu_loop(CPURISCVState *env) -{ - CPUState *cs = CPU(riscv_env_get_cpu(env)); - int trapnr, signum, sigcode; - target_ulong sigaddr; - target_ulong ret; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - signum = 0; - sigcode = 0; - sigaddr = 0; - - switch (trapnr) { - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - case RISCV_EXCP_U_ECALL: - env->pc += 4; - if (env->gpr[xA7] == TARGET_NR_arch_specific_syscall + 15) { - /* riscv_flush_icache_syscall is a no-op in QEMU as - self-modifying code is automatically detected */ - ret = 0; - } else { - ret = do_syscall(env, - env->gpr[xA7], - env->gpr[xA0], - env->gpr[xA1], - env->gpr[xA2], - env->gpr[xA3], - env->gpr[xA4], - env->gpr[xA5], - 0, 0); - } - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->gpr[xA0] = ret; - } - if (cs->singlestep_enabled) { - goto gdbstep; - } - break; - case RISCV_EXCP_ILLEGAL_INST: - signum = TARGET_SIGILL; - sigcode = TARGET_ILL_ILLOPC; - break; - case RISCV_EXCP_BREAKPOINT: - signum = TARGET_SIGTRAP; - sigcode = TARGET_TRAP_BRKPT; - sigaddr = env->pc; - break; - case RISCV_EXCP_INST_PAGE_FAULT: - case RISCV_EXCP_LOAD_PAGE_FAULT: - case RISCV_EXCP_STORE_PAGE_FAULT: - signum = TARGET_SIGSEGV; - sigcode = TARGET_SEGV_MAPERR; - break; - case EXCP_DEBUG: - gdbstep: - signum = gdb_handlesig(cs, TARGET_SIGTRAP); - sigcode = TARGET_TRAP_BRKPT; - break; - default: - EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", - trapnr); - exit(EXIT_FAILURE); - } - - if (signum) { - target_siginfo_t info = { - .si_signo = signum, - .si_errno = 0, - .si_code = sigcode, - ._sifields._sigfault._addr = sigaddr - }; - queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); - } - - process_pending_signals(env); - } -} - -#endif /* TARGET_RISCV */ - #ifdef TARGET_HPPA static abi_ulong hppa_lws(CPUHPPAState *env) @@ -1322,12 +1228,7 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); -#if defined(TARGET_RISCV) - { - env->pc = regs->sepc; - env->gpr[xSP] = regs->sp; - } -#elif defined(TARGET_HPPA) +#if defined(TARGET_HPPA) { int i; for (i = 1; i < 32; i++) { diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index b7700a5561..f137d39d7e 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -21,6 +21,98 @@ #include "qemu.h" #include "cpu_loop-common.h" +void cpu_loop(CPURISCVState *env) +{ + CPUState *cs = CPU(riscv_env_get_cpu(env)); + int trapnr, signum, sigcode; + target_ulong sigaddr; + target_ulong ret; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + signum = 0; + sigcode = 0; + sigaddr = 0; + + switch (trapnr) { + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + case RISCV_EXCP_U_ECALL: + env->pc += 4; + if (env->gpr[xA7] == TARGET_NR_arch_specific_syscall + 15) { + /* riscv_flush_icache_syscall is a no-op in QEMU as + self-modifying code is automatically detected */ + ret = 0; + } else { + ret = do_syscall(env, + env->gpr[xA7], + env->gpr[xA0], + env->gpr[xA1], + env->gpr[xA2], + env->gpr[xA3], + env->gpr[xA4], + env->gpr[xA5], + 0, 0); + } + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->gpr[xA0] = ret; + } + if (cs->singlestep_enabled) { + goto gdbstep; + } + break; + case RISCV_EXCP_ILLEGAL_INST: + signum = TARGET_SIGILL; + sigcode = TARGET_ILL_ILLOPC; + break; + case RISCV_EXCP_BREAKPOINT: + signum = TARGET_SIGTRAP; + sigcode = TARGET_TRAP_BRKPT; + sigaddr = env->pc; + break; + case RISCV_EXCP_INST_PAGE_FAULT: + case RISCV_EXCP_LOAD_PAGE_FAULT: + case RISCV_EXCP_STORE_PAGE_FAULT: + signum = TARGET_SIGSEGV; + sigcode = TARGET_SEGV_MAPERR; + break; + case EXCP_DEBUG: + gdbstep: + signum = gdb_handlesig(cs, TARGET_SIGTRAP); + sigcode = TARGET_TRAP_BRKPT; + break; + default: + EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", + trapnr); + exit(EXIT_FAILURE); + } + + if (signum) { + target_siginfo_t info = { + .si_signo = signum, + .si_errno = 0, + .si_code = sigcode, + ._sifields._sigfault._addr = sigaddr + }; + queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); + } + + process_pending_signals(env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + env->pc = regs->sepc; + env->gpr[xSP] = regs->sp; } From 1d8d0b4ec7b39735e4c3e078c7772f0cd240a0f8 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:50 +0200 Subject: [PATCH 0163/2380] linux-user: move hppa cpu loop to hppa directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change, only move code from main.c to hppa/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180411185651.21351-19-laurent@vivier.eu> --- linux-user/hppa/cpu_loop.c | 185 +++++++++++++++++++++++++++++++++++ linux-user/main.c | 194 +------------------------------------ 2 files changed, 186 insertions(+), 193 deletions(-) diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index b7700a5561..0301c766c6 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -21,6 +21,191 @@ #include "qemu.h" #include "cpu_loop-common.h" +static abi_ulong hppa_lws(CPUHPPAState *env) +{ + uint32_t which = env->gr[20]; + abi_ulong addr = env->gr[26]; + abi_ulong old = env->gr[25]; + abi_ulong new = env->gr[24]; + abi_ulong size, ret; + + switch (which) { + default: + return -TARGET_ENOSYS; + + case 0: /* elf32 atomic 32bit cmpxchg */ + if ((addr & 3) || !access_ok(VERIFY_WRITE, addr, 4)) { + return -TARGET_EFAULT; + } + old = tswap32(old); + new = tswap32(new); + ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new); + ret = tswap32(ret); + break; + + case 2: /* elf32 atomic "new" cmpxchg */ + size = env->gr[23]; + if (size >= 4) { + return -TARGET_ENOSYS; + } + if (((addr | old | new) & ((1 << size) - 1)) + || !access_ok(VERIFY_WRITE, addr, 1 << size) + || !access_ok(VERIFY_READ, old, 1 << size) + || !access_ok(VERIFY_READ, new, 1 << size)) { + return -TARGET_EFAULT; + } + /* Note that below we use host-endian loads so that the cmpxchg + can be host-endian as well. */ + switch (size) { + case 0: + old = *(uint8_t *)g2h(old); + new = *(uint8_t *)g2h(new); + ret = atomic_cmpxchg((uint8_t *)g2h(addr), old, new); + ret = ret != old; + break; + case 1: + old = *(uint16_t *)g2h(old); + new = *(uint16_t *)g2h(new); + ret = atomic_cmpxchg((uint16_t *)g2h(addr), old, new); + ret = ret != old; + break; + case 2: + old = *(uint32_t *)g2h(old); + new = *(uint32_t *)g2h(new); + ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new); + ret = ret != old; + break; + case 3: + { + uint64_t o64, n64, r64; + o64 = *(uint64_t *)g2h(old); + n64 = *(uint64_t *)g2h(new); +#ifdef CONFIG_ATOMIC64 + r64 = atomic_cmpxchg__nocheck((uint64_t *)g2h(addr), o64, n64); + ret = r64 != o64; +#else + start_exclusive(); + r64 = *(uint64_t *)g2h(addr); + ret = 1; + if (r64 == o64) { + *(uint64_t *)g2h(addr) = n64; + ret = 0; + } + end_exclusive(); +#endif + } + break; + } + break; + } + + env->gr[28] = ret; + return 0; +} + +void cpu_loop(CPUHPPAState *env) +{ + CPUState *cs = CPU(hppa_env_get_cpu(env)); + target_siginfo_t info; + abi_ulong ret; + int trapnr; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_SYSCALL: + ret = do_syscall(env, env->gr[20], + env->gr[26], env->gr[25], + env->gr[24], env->gr[23], + env->gr[22], env->gr[21], 0, 0); + switch (ret) { + default: + env->gr[28] = ret; + /* We arrived here by faking the gateway page. Return. */ + env->iaoq_f = env->gr[31]; + env->iaoq_b = env->gr[31] + 4; + break; + case -TARGET_ERESTARTSYS: + case -TARGET_QEMU_ESIGRETURN: + break; + } + break; + case EXCP_SYSCALL_LWS: + env->gr[21] = hppa_lws(env); + /* We arrived here by faking the gateway page. Return. */ + env->iaoq_f = env->gr[31]; + env->iaoq_b = env->gr[31] + 4; + break; + case EXCP_ITLB_MISS: + case EXCP_DTLB_MISS: + case EXCP_NA_ITLB_MISS: + case EXCP_NA_DTLB_MISS: + case EXCP_IMP: + case EXCP_DMP: + case EXCP_DMB: + case EXCP_PAGE_REF: + case EXCP_DMAR: + case EXCP_DMPI: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_ACCERR; + info._sifields._sigfault._addr = env->cr[CR_IOR]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_UNALIGN: + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = env->cr[CR_IOR]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ILL: + case EXCP_PRIV_OPR: + case EXCP_PRIV_REG: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->iaoq_f; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_OVERFLOW: + case EXCP_COND: + case EXCP_ASSIST: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = env->iaoq_f; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_DEBUG: + trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); + if (trapnr) { + info.si_signo = trapnr; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, trapnr, QEMU_SI_FAULT, &info); + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + default: + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + int i; + for (i = 1; i < 32; i++) { + env->gr[i] = regs->gr[i]; + } + env->iaoq_f = regs->iaoq[0]; + env->iaoq_b = regs->iaoq[1]; } diff --git a/linux-user/main.c b/linux-user/main.c index 834ec0bfe5..aa48b048a7 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,189 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_HPPA - -static abi_ulong hppa_lws(CPUHPPAState *env) -{ - uint32_t which = env->gr[20]; - abi_ulong addr = env->gr[26]; - abi_ulong old = env->gr[25]; - abi_ulong new = env->gr[24]; - abi_ulong size, ret; - - switch (which) { - default: - return -TARGET_ENOSYS; - - case 0: /* elf32 atomic 32bit cmpxchg */ - if ((addr & 3) || !access_ok(VERIFY_WRITE, addr, 4)) { - return -TARGET_EFAULT; - } - old = tswap32(old); - new = tswap32(new); - ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new); - ret = tswap32(ret); - break; - - case 2: /* elf32 atomic "new" cmpxchg */ - size = env->gr[23]; - if (size >= 4) { - return -TARGET_ENOSYS; - } - if (((addr | old | new) & ((1 << size) - 1)) - || !access_ok(VERIFY_WRITE, addr, 1 << size) - || !access_ok(VERIFY_READ, old, 1 << size) - || !access_ok(VERIFY_READ, new, 1 << size)) { - return -TARGET_EFAULT; - } - /* Note that below we use host-endian loads so that the cmpxchg - can be host-endian as well. */ - switch (size) { - case 0: - old = *(uint8_t *)g2h(old); - new = *(uint8_t *)g2h(new); - ret = atomic_cmpxchg((uint8_t *)g2h(addr), old, new); - ret = ret != old; - break; - case 1: - old = *(uint16_t *)g2h(old); - new = *(uint16_t *)g2h(new); - ret = atomic_cmpxchg((uint16_t *)g2h(addr), old, new); - ret = ret != old; - break; - case 2: - old = *(uint32_t *)g2h(old); - new = *(uint32_t *)g2h(new); - ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new); - ret = ret != old; - break; - case 3: - { - uint64_t o64, n64, r64; - o64 = *(uint64_t *)g2h(old); - n64 = *(uint64_t *)g2h(new); -#ifdef CONFIG_ATOMIC64 - r64 = atomic_cmpxchg__nocheck((uint64_t *)g2h(addr), o64, n64); - ret = r64 != o64; -#else - start_exclusive(); - r64 = *(uint64_t *)g2h(addr); - ret = 1; - if (r64 == o64) { - *(uint64_t *)g2h(addr) = n64; - ret = 0; - } - end_exclusive(); -#endif - } - break; - } - break; - } - - env->gr[28] = ret; - return 0; -} - -void cpu_loop(CPUHPPAState *env) -{ - CPUState *cs = CPU(hppa_env_get_cpu(env)); - target_siginfo_t info; - abi_ulong ret; - int trapnr; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_SYSCALL: - ret = do_syscall(env, env->gr[20], - env->gr[26], env->gr[25], - env->gr[24], env->gr[23], - env->gr[22], env->gr[21], 0, 0); - switch (ret) { - default: - env->gr[28] = ret; - /* We arrived here by faking the gateway page. Return. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; - break; - case -TARGET_ERESTARTSYS: - case -TARGET_QEMU_ESIGRETURN: - break; - } - break; - case EXCP_SYSCALL_LWS: - env->gr[21] = hppa_lws(env); - /* We arrived here by faking the gateway page. Return. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; - break; - case EXCP_ITLB_MISS: - case EXCP_DTLB_MISS: - case EXCP_NA_ITLB_MISS: - case EXCP_NA_DTLB_MISS: - case EXCP_IMP: - case EXCP_DMP: - case EXCP_DMB: - case EXCP_PAGE_REF: - case EXCP_DMAR: - case EXCP_DMPI: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_ACCERR; - info._sifields._sigfault._addr = env->cr[CR_IOR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_UNALIGN: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = env->cr[CR_IOR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_ILL: - case EXCP_PRIV_OPR: - case EXCP_PRIV_REG: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->iaoq_f; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_OVERFLOW: - case EXCP_COND: - case EXCP_ASSIST: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = env->iaoq_f; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_DEBUG: - trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); - if (trapnr) { - info.si_signo = trapnr; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, trapnr, QEMU_SI_FAULT, &info); - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - default: - g_assert_not_reached(); - } - process_pending_signals(env); - } -} - -#endif /* TARGET_HPPA */ - #ifdef TARGET_XTENSA static void xtensa_rfw(CPUXtensaState *env) @@ -1228,16 +1045,7 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); -#if defined(TARGET_HPPA) - { - int i; - for (i = 1; i < 32; i++) { - env->gr[i] = regs->gr[i]; - } - env->iaoq_f = regs->iaoq[0]; - env->iaoq_b = regs->iaoq[1]; - } -#elif defined(TARGET_XTENSA) +#if defined(TARGET_XTENSA) { int i; for (i = 0; i < 16; ++i) { From de6e89b81fb41870d977656c2a23226c06b68655 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 20:56:51 +0200 Subject: [PATCH 0164/2380] linux-user: move xtensa cpu loop to xtensa directory No code change, only move code from main.c to xtensa/cpu_loop.c. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411185651.21351-20-laurent@vivier.eu> --- linux-user/main.c | 250 ----------------------------------- linux-user/xtensa/cpu_loop.c | 241 +++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+), 250 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index aa48b048a7..32347545c9 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,245 +149,6 @@ void fork_end(int child) } } -#ifdef TARGET_XTENSA - -static void xtensa_rfw(CPUXtensaState *env) -{ - xtensa_restore_owb(env); - env->pc = env->sregs[EPC1]; -} - -static void xtensa_rfwu(CPUXtensaState *env) -{ - env->sregs[WINDOW_START] |= (1 << env->sregs[WINDOW_BASE]); - xtensa_rfw(env); -} - -static void xtensa_rfwo(CPUXtensaState *env) -{ - env->sregs[WINDOW_START] &= ~(1 << env->sregs[WINDOW_BASE]); - xtensa_rfw(env); -} - -static void xtensa_overflow4(CPUXtensaState *env) -{ - put_user_ual(env->regs[0], env->regs[5] - 16); - put_user_ual(env->regs[1], env->regs[5] - 12); - put_user_ual(env->regs[2], env->regs[5] - 8); - put_user_ual(env->regs[3], env->regs[5] - 4); - xtensa_rfwo(env); -} - -static void xtensa_underflow4(CPUXtensaState *env) -{ - get_user_ual(env->regs[0], env->regs[5] - 16); - get_user_ual(env->regs[1], env->regs[5] - 12); - get_user_ual(env->regs[2], env->regs[5] - 8); - get_user_ual(env->regs[3], env->regs[5] - 4); - xtensa_rfwu(env); -} - -static void xtensa_overflow8(CPUXtensaState *env) -{ - put_user_ual(env->regs[0], env->regs[9] - 16); - get_user_ual(env->regs[0], env->regs[1] - 12); - put_user_ual(env->regs[1], env->regs[9] - 12); - put_user_ual(env->regs[2], env->regs[9] - 8); - put_user_ual(env->regs[3], env->regs[9] - 4); - put_user_ual(env->regs[4], env->regs[0] - 32); - put_user_ual(env->regs[5], env->regs[0] - 28); - put_user_ual(env->regs[6], env->regs[0] - 24); - put_user_ual(env->regs[7], env->regs[0] - 20); - xtensa_rfwo(env); -} - -static void xtensa_underflow8(CPUXtensaState *env) -{ - get_user_ual(env->regs[0], env->regs[9] - 16); - get_user_ual(env->regs[1], env->regs[9] - 12); - get_user_ual(env->regs[2], env->regs[9] - 8); - get_user_ual(env->regs[7], env->regs[1] - 12); - get_user_ual(env->regs[3], env->regs[9] - 4); - get_user_ual(env->regs[4], env->regs[7] - 32); - get_user_ual(env->regs[5], env->regs[7] - 28); - get_user_ual(env->regs[6], env->regs[7] - 24); - get_user_ual(env->regs[7], env->regs[7] - 20); - xtensa_rfwu(env); -} - -static void xtensa_overflow12(CPUXtensaState *env) -{ - put_user_ual(env->regs[0], env->regs[13] - 16); - get_user_ual(env->regs[0], env->regs[1] - 12); - put_user_ual(env->regs[1], env->regs[13] - 12); - put_user_ual(env->regs[2], env->regs[13] - 8); - put_user_ual(env->regs[3], env->regs[13] - 4); - put_user_ual(env->regs[4], env->regs[0] - 48); - put_user_ual(env->regs[5], env->regs[0] - 44); - put_user_ual(env->regs[6], env->regs[0] - 40); - put_user_ual(env->regs[7], env->regs[0] - 36); - put_user_ual(env->regs[8], env->regs[0] - 32); - put_user_ual(env->regs[9], env->regs[0] - 28); - put_user_ual(env->regs[10], env->regs[0] - 24); - put_user_ual(env->regs[11], env->regs[0] - 20); - xtensa_rfwo(env); -} - -static void xtensa_underflow12(CPUXtensaState *env) -{ - get_user_ual(env->regs[0], env->regs[13] - 16); - get_user_ual(env->regs[1], env->regs[13] - 12); - get_user_ual(env->regs[2], env->regs[13] - 8); - get_user_ual(env->regs[11], env->regs[1] - 12); - get_user_ual(env->regs[3], env->regs[13] - 4); - get_user_ual(env->regs[4], env->regs[11] - 48); - get_user_ual(env->regs[5], env->regs[11] - 44); - get_user_ual(env->regs[6], env->regs[11] - 40); - get_user_ual(env->regs[7], env->regs[11] - 36); - get_user_ual(env->regs[8], env->regs[11] - 32); - get_user_ual(env->regs[9], env->regs[11] - 28); - get_user_ual(env->regs[10], env->regs[11] - 24); - get_user_ual(env->regs[11], env->regs[11] - 20); - xtensa_rfwu(env); -} - -void cpu_loop(CPUXtensaState *env) -{ - CPUState *cs = CPU(xtensa_env_get_cpu(env)); - target_siginfo_t info; - abi_ulong ret; - int trapnr; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - env->sregs[PS] &= ~PS_EXCM; - switch (trapnr) { - case EXCP_INTERRUPT: - break; - - case EXC_WINDOW_OVERFLOW4: - xtensa_overflow4(env); - break; - case EXC_WINDOW_UNDERFLOW4: - xtensa_underflow4(env); - break; - case EXC_WINDOW_OVERFLOW8: - xtensa_overflow8(env); - break; - case EXC_WINDOW_UNDERFLOW8: - xtensa_underflow8(env); - break; - case EXC_WINDOW_OVERFLOW12: - xtensa_overflow12(env); - break; - case EXC_WINDOW_UNDERFLOW12: - xtensa_underflow12(env); - break; - - case EXC_USER: - switch (env->sregs[EXCCAUSE]) { - case ILLEGAL_INSTRUCTION_CAUSE: - case PRIVILEGED_CAUSE: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = - env->sregs[EXCCAUSE] == ILLEGAL_INSTRUCTION_CAUSE ? - TARGET_ILL_ILLOPC : TARGET_ILL_PRVOPC; - info._sifields._sigfault._addr = env->sregs[EPC1]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - - case SYSCALL_CAUSE: - env->pc += 3; - ret = do_syscall(env, env->regs[2], - env->regs[6], env->regs[3], - env->regs[4], env->regs[5], - env->regs[8], env->regs[9], 0, 0); - switch (ret) { - default: - env->regs[2] = ret; - break; - - case -TARGET_ERESTARTSYS: - env->pc -= 3; - break; - - case -TARGET_QEMU_ESIGRETURN: - break; - } - break; - - case ALLOCA_CAUSE: - env->sregs[PS] = deposit32(env->sregs[PS], - PS_OWB_SHIFT, - PS_OWB_LEN, - env->sregs[WINDOW_BASE]); - - switch (env->regs[0] & 0xc0000000) { - case 0x00000000: - case 0x40000000: - xtensa_rotate_window(env, -1); - xtensa_underflow4(env); - break; - - case 0x80000000: - xtensa_rotate_window(env, -2); - xtensa_underflow8(env); - break; - - case 0xc0000000: - xtensa_rotate_window(env, -3); - xtensa_underflow12(env); - break; - } - break; - - case INTEGER_DIVIDE_BY_ZERO_CAUSE: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_INTDIV; - info._sifields._sigfault._addr = env->sregs[EPC1]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - - case LOAD_PROHIBITED_CAUSE: - case STORE_PROHIBITED_CAUSE: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_ACCERR; - info._sifields._sigfault._addr = env->sregs[EXCVADDR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - - default: - fprintf(stderr, "exccause = %d\n", env->sregs[EXCCAUSE]); - g_assert_not_reached(); - } - break; - case EXCP_DEBUG: - trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); - if (trapnr) { - info.si_signo = trapnr; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, trapnr, QEMU_SI_FAULT, &info); - } - break; - case EXC_DEBUG: - default: - fprintf(stderr, "trapnr = %d\n", trapnr); - g_assert_not_reached(); - } - process_pending_signals(env); - } -} - -#endif /* TARGET_XTENSA */ - __thread CPUState *thread_cpu; bool qemu_cpu_is_self(CPUState *cpu) @@ -1045,17 +806,6 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); -#if defined(TARGET_XTENSA) - { - int i; - for (i = 0; i < 16; ++i) { - env->regs[i] = regs->areg[i]; - } - env->sregs[WINDOW_START] = regs->windowstart; - env->pc = regs->pc; - } -#endif - if (gdbstub_port) { if (gdbserver_start(gdbstub_port) < 0) { fprintf(stderr, "qemu: could not open gdbserver on port %d\n", diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index b7700a5561..d142988ebe 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -21,6 +21,247 @@ #include "qemu.h" #include "cpu_loop-common.h" +static void xtensa_rfw(CPUXtensaState *env) +{ + xtensa_restore_owb(env); + env->pc = env->sregs[EPC1]; +} + +static void xtensa_rfwu(CPUXtensaState *env) +{ + env->sregs[WINDOW_START] |= (1 << env->sregs[WINDOW_BASE]); + xtensa_rfw(env); +} + +static void xtensa_rfwo(CPUXtensaState *env) +{ + env->sregs[WINDOW_START] &= ~(1 << env->sregs[WINDOW_BASE]); + xtensa_rfw(env); +} + +static void xtensa_overflow4(CPUXtensaState *env) +{ + put_user_ual(env->regs[0], env->regs[5] - 16); + put_user_ual(env->regs[1], env->regs[5] - 12); + put_user_ual(env->regs[2], env->regs[5] - 8); + put_user_ual(env->regs[3], env->regs[5] - 4); + xtensa_rfwo(env); +} + +static void xtensa_underflow4(CPUXtensaState *env) +{ + get_user_ual(env->regs[0], env->regs[5] - 16); + get_user_ual(env->regs[1], env->regs[5] - 12); + get_user_ual(env->regs[2], env->regs[5] - 8); + get_user_ual(env->regs[3], env->regs[5] - 4); + xtensa_rfwu(env); +} + +static void xtensa_overflow8(CPUXtensaState *env) +{ + put_user_ual(env->regs[0], env->regs[9] - 16); + get_user_ual(env->regs[0], env->regs[1] - 12); + put_user_ual(env->regs[1], env->regs[9] - 12); + put_user_ual(env->regs[2], env->regs[9] - 8); + put_user_ual(env->regs[3], env->regs[9] - 4); + put_user_ual(env->regs[4], env->regs[0] - 32); + put_user_ual(env->regs[5], env->regs[0] - 28); + put_user_ual(env->regs[6], env->regs[0] - 24); + put_user_ual(env->regs[7], env->regs[0] - 20); + xtensa_rfwo(env); +} + +static void xtensa_underflow8(CPUXtensaState *env) +{ + get_user_ual(env->regs[0], env->regs[9] - 16); + get_user_ual(env->regs[1], env->regs[9] - 12); + get_user_ual(env->regs[2], env->regs[9] - 8); + get_user_ual(env->regs[7], env->regs[1] - 12); + get_user_ual(env->regs[3], env->regs[9] - 4); + get_user_ual(env->regs[4], env->regs[7] - 32); + get_user_ual(env->regs[5], env->regs[7] - 28); + get_user_ual(env->regs[6], env->regs[7] - 24); + get_user_ual(env->regs[7], env->regs[7] - 20); + xtensa_rfwu(env); +} + +static void xtensa_overflow12(CPUXtensaState *env) +{ + put_user_ual(env->regs[0], env->regs[13] - 16); + get_user_ual(env->regs[0], env->regs[1] - 12); + put_user_ual(env->regs[1], env->regs[13] - 12); + put_user_ual(env->regs[2], env->regs[13] - 8); + put_user_ual(env->regs[3], env->regs[13] - 4); + put_user_ual(env->regs[4], env->regs[0] - 48); + put_user_ual(env->regs[5], env->regs[0] - 44); + put_user_ual(env->regs[6], env->regs[0] - 40); + put_user_ual(env->regs[7], env->regs[0] - 36); + put_user_ual(env->regs[8], env->regs[0] - 32); + put_user_ual(env->regs[9], env->regs[0] - 28); + put_user_ual(env->regs[10], env->regs[0] - 24); + put_user_ual(env->regs[11], env->regs[0] - 20); + xtensa_rfwo(env); +} + +static void xtensa_underflow12(CPUXtensaState *env) +{ + get_user_ual(env->regs[0], env->regs[13] - 16); + get_user_ual(env->regs[1], env->regs[13] - 12); + get_user_ual(env->regs[2], env->regs[13] - 8); + get_user_ual(env->regs[11], env->regs[1] - 12); + get_user_ual(env->regs[3], env->regs[13] - 4); + get_user_ual(env->regs[4], env->regs[11] - 48); + get_user_ual(env->regs[5], env->regs[11] - 44); + get_user_ual(env->regs[6], env->regs[11] - 40); + get_user_ual(env->regs[7], env->regs[11] - 36); + get_user_ual(env->regs[8], env->regs[11] - 32); + get_user_ual(env->regs[9], env->regs[11] - 28); + get_user_ual(env->regs[10], env->regs[11] - 24); + get_user_ual(env->regs[11], env->regs[11] - 20); + xtensa_rfwu(env); +} + +void cpu_loop(CPUXtensaState *env) +{ + CPUState *cs = CPU(xtensa_env_get_cpu(env)); + target_siginfo_t info; + abi_ulong ret; + int trapnr; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + env->sregs[PS] &= ~PS_EXCM; + switch (trapnr) { + case EXCP_INTERRUPT: + break; + + case EXC_WINDOW_OVERFLOW4: + xtensa_overflow4(env); + break; + case EXC_WINDOW_UNDERFLOW4: + xtensa_underflow4(env); + break; + case EXC_WINDOW_OVERFLOW8: + xtensa_overflow8(env); + break; + case EXC_WINDOW_UNDERFLOW8: + xtensa_underflow8(env); + break; + case EXC_WINDOW_OVERFLOW12: + xtensa_overflow12(env); + break; + case EXC_WINDOW_UNDERFLOW12: + xtensa_underflow12(env); + break; + + case EXC_USER: + switch (env->sregs[EXCCAUSE]) { + case ILLEGAL_INSTRUCTION_CAUSE: + case PRIVILEGED_CAUSE: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = + env->sregs[EXCCAUSE] == ILLEGAL_INSTRUCTION_CAUSE ? + TARGET_ILL_ILLOPC : TARGET_ILL_PRVOPC; + info._sifields._sigfault._addr = env->sregs[EPC1]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + + case SYSCALL_CAUSE: + env->pc += 3; + ret = do_syscall(env, env->regs[2], + env->regs[6], env->regs[3], + env->regs[4], env->regs[5], + env->regs[8], env->regs[9], 0, 0); + switch (ret) { + default: + env->regs[2] = ret; + break; + + case -TARGET_ERESTARTSYS: + env->pc -= 3; + break; + + case -TARGET_QEMU_ESIGRETURN: + break; + } + break; + + case ALLOCA_CAUSE: + env->sregs[PS] = deposit32(env->sregs[PS], + PS_OWB_SHIFT, + PS_OWB_LEN, + env->sregs[WINDOW_BASE]); + + switch (env->regs[0] & 0xc0000000) { + case 0x00000000: + case 0x40000000: + xtensa_rotate_window(env, -1); + xtensa_underflow4(env); + break; + + case 0x80000000: + xtensa_rotate_window(env, -2); + xtensa_underflow8(env); + break; + + case 0xc0000000: + xtensa_rotate_window(env, -3); + xtensa_underflow12(env); + break; + } + break; + + case INTEGER_DIVIDE_BY_ZERO_CAUSE: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTDIV; + info._sifields._sigfault._addr = env->sregs[EPC1]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + + case LOAD_PROHIBITED_CAUSE: + case STORE_PROHIBITED_CAUSE: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_ACCERR; + info._sifields._sigfault._addr = env->sregs[EXCVADDR]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + + default: + fprintf(stderr, "exccause = %d\n", env->sregs[EXCCAUSE]); + g_assert_not_reached(); + } + break; + case EXCP_DEBUG: + trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); + if (trapnr) { + info.si_signo = trapnr; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, trapnr, QEMU_SI_FAULT, &info); + } + break; + case EXC_DEBUG: + default: + fprintf(stderr, "trapnr = %d\n", trapnr); + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + int i; + for (i = 0; i < 16; ++i) { + env->regs[i] = regs->areg[i]; + } + env->sregs[WINDOW_START] = regs->windowstart; + env->pc = regs->pc; } From 62aaa5146476911aea1fbe6fbf919d06bba8ab5d Mon Sep 17 00:00:00 2001 From: Christophe Lyon Date: Mon, 16 Apr 2018 11:18:25 +0200 Subject: [PATCH 0165/2380] linux-user: Add ARM get_tls syscall support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mickaël Guêné Signed-off-by: Christophe Lyon Reviewed-by: Peter Maydell Message-Id: <20180416091845.7315-1-christophe.lyon@st.com> [lv: moved the change to linux-user/arm/cpu_loop.c] Signed-off-by: Laurent Vivier --- linux-user/arm/cpu_loop.c | 3 +++ linux-user/arm/target_syscall.h | 1 + 2 files changed, 4 insertions(+) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index d911929bf6..26928fbbb2 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -347,6 +347,9 @@ void cpu_loop(CPUARMState *env) case ARM_NR_breakpoint: env->regs[15] -= env->thumb ? 2 : 4; goto excp_debug; + case ARM_NR_get_tls: + env->regs[0] = cpu_get_tls(env); + break; default: gemu_log("qemu: Unsupported ARM syscall: 0x%x\n", n); diff --git a/linux-user/arm/target_syscall.h b/linux-user/arm/target_syscall.h index 94e2a42cb2..afc0772e19 100644 --- a/linux-user/arm/target_syscall.h +++ b/linux-user/arm/target_syscall.h @@ -16,6 +16,7 @@ struct target_pt_regs { #define ARM_NR_breakpoint (ARM_NR_BASE + 1) #define ARM_NR_cacheflush (ARM_NR_BASE + 2) #define ARM_NR_set_tls (ARM_NR_BASE + 5) +#define ARM_NR_get_tls (ARM_NR_BASE + 6) #define ARM_NR_semihosting 0x123456 #define ARM_NR_thumb_semihosting 0xAB From 7a9cb3ad3370cd98b84f9df252012ecf7a5a5b67 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 29 Mar 2018 13:22:56 +0200 Subject: [PATCH 0166/2380] s390x: introduce 2.13 compat machine Reviewed-by: Thomas Huth Reviewed-by: David Hildenbrand Acked-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- hw/s390x/s390-virtio-ccw.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 435f7c99e7..c84098dd17 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -671,6 +671,9 @@ bool css_migration_enabled(void) } \ type_init(ccw_machine_register_##suffix) +#define CCW_COMPAT_2_12 \ + HW_COMPAT_2_12 + #define CCW_COMPAT_2_11 \ HW_COMPAT_2_11 \ {\ @@ -756,14 +759,26 @@ bool css_migration_enabled(void) .value = "0",\ }, +static void ccw_machine_2_13_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_2_13_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(2_13, "2.13", true); + static void ccw_machine_2_12_instance_options(MachineState *machine) { + ccw_machine_2_13_instance_options(machine); } static void ccw_machine_2_12_class_options(MachineClass *mc) { + ccw_machine_2_13_class_options(mc); + SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_12); } -DEFINE_CCW_MACHINE(2_12, "2.12", true); +DEFINE_CCW_MACHINE(2_12, "2.12", false); static void ccw_machine_2_11_instance_options(MachineState *machine) { From 98e43b71b25330566d902fbdc8fe35041c2decbe Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 8 Mar 2018 12:04:29 +0100 Subject: [PATCH 0167/2380] s390x/sclp: extend SCLP event masks to 64 bits Extend the SCLP event masks to 64 bits. Notice that using any of the new bits results in a state that cannot be migrated to an older version. Signed-off-by: Claudio Imbrenda Message-Id: <1520507069-22179-1-git-send-email-imbrenda@linux.vnet.ibm.com> Acked-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- hw/s390x/event-facility.c | 50 +++++++++++++++++++++++++------ include/hw/s390x/event-facility.h | 2 +- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 9c24bc6f7c..0dea6cccae 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -26,11 +26,23 @@ typedef struct SCLPEventsBus { BusState qbus; } SCLPEventsBus; +/* we need to save 32 bit chunks for compatibility */ +#ifdef HOST_WORDS_BIGENDIAN +#define RECV_MASK_LOWER 1 +#define RECV_MASK_UPPER 0 +#else /* little endian host */ +#define RECV_MASK_LOWER 0 +#define RECV_MASK_UPPER 1 +#endif + struct SCLPEventFacility { SysBusDevice parent_obj; SCLPEventsBus sbus; /* guest's receive mask */ - sccb_mask_t receive_mask; + union { + uint32_t receive_mask_pieces[2]; + sccb_mask_t receive_mask; + }; /* * when false, we keep the same broken, backwards compatible behaviour as * before, allowing only masks of size exactly 4; when true, we implement @@ -262,7 +274,7 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) case SCLP_SELECTIVE_READ: copy_mask((uint8_t *)&sclp_active_selection_mask, (uint8_t *)&red->mask, sizeof(sclp_active_selection_mask), ef->mask_length); - sclp_active_selection_mask = be32_to_cpu(sclp_active_selection_mask); + sclp_active_selection_mask = be64_to_cpu(sclp_active_selection_mask); if (!sclp_cp_receive_mask || (sclp_active_selection_mask & ~sclp_cp_receive_mask)) { sccb->h.response_code = @@ -294,21 +306,22 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) } /* - * Note: We currently only support masks up to 4 byte length; - * the remainder is filled up with zeroes. Linux uses - * a 4 byte mask length. + * Note: We currently only support masks up to 8 byte length; + * the remainder is filled up with zeroes. Older Linux + * kernels use a 4 byte mask length, newer ones can use both + * 8 or 4 depending on what is available on the host. */ /* keep track of the guest's capability masks */ copy_mask((uint8_t *)&tmp_mask, WEM_CP_RECEIVE_MASK(we_mask, mask_length), sizeof(tmp_mask), mask_length); - ef->receive_mask = be32_to_cpu(tmp_mask); + ef->receive_mask = be64_to_cpu(tmp_mask); /* return the SCLP's capability masks to the guest */ - tmp_mask = cpu_to_be32(get_host_receive_mask(ef)); + tmp_mask = cpu_to_be64(get_host_receive_mask(ef)); copy_mask(WEM_RECEIVE_MASK(we_mask, mask_length), (uint8_t *)&tmp_mask, mask_length, sizeof(tmp_mask)); - tmp_mask = cpu_to_be32(get_host_send_mask(ef)); + tmp_mask = cpu_to_be64(get_host_send_mask(ef)); copy_mask(WEM_SEND_MASK(we_mask, mask_length), (uint8_t *)&tmp_mask, mask_length, sizeof(tmp_mask)); @@ -369,6 +382,13 @@ static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code) } } +static bool vmstate_event_facility_mask64_needed(void *opaque) +{ + SCLPEventFacility *ef = opaque; + + return (ef->receive_mask & 0xFFFFFFFF) != 0; +} + static bool vmstate_event_facility_mask_length_needed(void *opaque) { SCLPEventFacility *ef = opaque; @@ -376,6 +396,17 @@ static bool vmstate_event_facility_mask_length_needed(void *opaque) return ef->allow_all_mask_sizes; } +static const VMStateDescription vmstate_event_facility_mask64 = { + .name = "vmstate-event-facility/mask64", + .version_id = 0, + .minimum_version_id = 0, + .needed = vmstate_event_facility_mask64_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(receive_mask_pieces[RECV_MASK_LOWER], SCLPEventFacility), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_event_facility_mask_length = { .name = "vmstate-event-facility/mask_length", .version_id = 0, @@ -392,10 +423,11 @@ static const VMStateDescription vmstate_event_facility = { .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { - VMSTATE_UINT32(receive_mask, SCLPEventFacility), + VMSTATE_UINT32(receive_mask_pieces[RECV_MASK_UPPER], SCLPEventFacility), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * []) { + &vmstate_event_facility_mask64, &vmstate_event_facility_mask_length, NULL } diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index 5698e5e96c..8b39a80bb9 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -73,7 +73,7 @@ typedef struct WriteEventMask { #define WEM_RECEIVE_MASK(wem, mask_len) ((wem)->masks + 2 * (mask_len)) #define WEM_SEND_MASK(wem, mask_len) ((wem)->masks + 3 * (mask_len)) -typedef uint32_t sccb_mask_t; +typedef uint64_t sccb_mask_t; typedef struct EventBufferHeader { uint16_t length; From c96f2c2a165acf92ff987ebc9e225452250c5083 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Mon, 9 Apr 2018 12:15:10 +0200 Subject: [PATCH 0168/2380] vfio-ccw: introduce vfio_ccw_get_device() A recent patch fixed leaks of the dynamically allocated vcdev->vdev.name field in vfio_ccw_realize(), but we now have three freeing sites for it. This is unfortunate and seems to indicate something is wrong with its life cycle. The root issue is that vcdev->vdev.name is set before vfio_get_device() is called, which theoretically prevents to call vfio_put_device() to do the freeing. Well actually, we could call it anyway because vfio_put_base_device() is a nop if the device isn't attached, but this would be confusing. This patch hence moves all the logic of attaching the device, including the "already attached" check, to a separate vfio_ccw_get_device() function, counterpart of vfio_put_device(). While here, vfio_put_device() is renamed to vfio_ccw_put_device() for consistency. Signed-off-by: Greg Kurz Message-Id: <152326891065.266543.9487977590811413472.stgit@bahia.lan> Signed-off-by: Cornelia Huck --- hw/vfio/ccw.c | 56 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index fe34b50769..e67392c5f9 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -292,12 +292,43 @@ static void vfio_ccw_put_region(VFIOCCWDevice *vcdev) g_free(vcdev->io_region); } -static void vfio_put_device(VFIOCCWDevice *vcdev) +static void vfio_ccw_put_device(VFIOCCWDevice *vcdev) { g_free(vcdev->vdev.name); vfio_put_base_device(&vcdev->vdev); } +static void vfio_ccw_get_device(VFIOGroup *group, VFIOCCWDevice *vcdev, + Error **errp) +{ + char *name = g_strdup_printf("%x.%x.%04x", vcdev->cdev.hostid.cssid, + vcdev->cdev.hostid.ssid, + vcdev->cdev.hostid.devid); + VFIODevice *vbasedev; + + QLIST_FOREACH(vbasedev, &group->device_list, next) { + if (strcmp(vbasedev->name, name) == 0) { + error_setg(errp, "vfio: subchannel %s has already been attached", + name); + goto out_err; + } + } + + if (vfio_get_device(group, vcdev->cdev.mdevid, &vcdev->vdev, errp)) { + goto out_err; + } + + vcdev->vdev.ops = &vfio_ccw_ops; + vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW; + vcdev->vdev.name = name; + vcdev->vdev.dev = &vcdev->cdev.parent_obj.parent_obj; + + return; + +out_err: + g_free(name); +} + static VFIOGroup *vfio_ccw_get_group(S390CCWDevice *cdev, Error **errp) { char *tmp, group_path[PATH_MAX]; @@ -327,7 +358,6 @@ static VFIOGroup *vfio_ccw_get_group(S390CCWDevice *cdev, Error **errp) static void vfio_ccw_realize(DeviceState *dev, Error **errp) { - VFIODevice *vbasedev; VFIOGroup *group; CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); @@ -348,22 +378,8 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) goto out_group_err; } - vcdev->vdev.ops = &vfio_ccw_ops; - vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW; - vcdev->vdev.name = g_strdup_printf("%x.%x.%04x", cdev->hostid.cssid, - cdev->hostid.ssid, cdev->hostid.devid); - vcdev->vdev.dev = dev; - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (strcmp(vbasedev->name, vcdev->vdev.name) == 0) { - error_setg(&err, "vfio: subchannel %s has already been attached", - vcdev->vdev.name); - g_free(vcdev->vdev.name); - goto out_device_err; - } - } - - if (vfio_get_device(group, cdev->mdevid, &vcdev->vdev, &err)) { - g_free(vcdev->vdev.name); + vfio_ccw_get_device(group, vcdev, &err); + if (err) { goto out_device_err; } @@ -382,7 +398,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) out_notifier_err: vfio_ccw_put_region(vcdev); out_region_err: - vfio_put_device(vcdev); + vfio_ccw_put_device(vcdev); out_device_err: vfio_put_group(group); out_group_err: @@ -403,7 +419,7 @@ static void vfio_ccw_unrealize(DeviceState *dev, Error **errp) vfio_ccw_unregister_io_notifier(vcdev); vfio_ccw_put_region(vcdev); - vfio_put_device(vcdev); + vfio_ccw_put_device(vcdev); vfio_put_group(group); if (cdc->unrealize) { From e7c3246162398677eb96f55f267de00809ea213d Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 12 Apr 2018 11:35:21 +0200 Subject: [PATCH 0169/2380] s390x/kvm: cleanup calls to cpu_synchronize_state() We have a call to cpu_synchronize_state() on every kvm_arch_handle_exit(). Let's remove the ones that are no longer needed. Remaining places (for s390x) are in - target/s390x/sigp.c, on the target CPU - target/s390x/cpu.c:s390_cpu_get_crash_info() While at it, use kvm_cpu_synchronize_state() instead of cpu_synchronize_state() in KVM code. (suggested by Thomas Huth) Signed-off-by: David Hildenbrand Message-Id: <20180412093521.2469-1-david@redhat.com> Acked-by: Christian Borntraeger Reviewed-by: Thomas Huth Signed-off-by: Cornelia Huck --- hw/s390x/s390-pci-inst.c | 8 -------- target/s390x/kvm.c | 20 +------------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 3fcc330fe3..02a815fd31 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -155,8 +155,6 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) S390pciState *s = s390_get_phb(); int i; - cpu_synchronize_state(CPU(cpu)); - if (env->psw.mask & PSW_MASK_PSTATE) { s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); return 0; @@ -389,8 +387,6 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) uint32_t fh; uint8_t pcias; - cpu_synchronize_state(CPU(cpu)); - if (env->psw.mask & PSW_MASK_PSTATE) { s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); return 0; @@ -487,8 +483,6 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) uint32_t fh; uint8_t pcias; - cpu_synchronize_state(CPU(cpu)); - if (env->psw.mask & PSW_MASK_PSTATE) { s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); return 0; @@ -620,8 +614,6 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) S390IOTLBEntry entry; hwaddr start, end; - cpu_synchronize_state(CPU(cpu)); - if (env->psw.mask & PSW_MASK_PSTATE) { s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); return 0; diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index fb59d92def..12b90cf5c5 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -1081,7 +1081,6 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, uint32_t code; int r = 0; - cpu_synchronize_state(CPU(cpu)); sccb = env->regs[ipbh0 & 0xf]; code = env->regs[(ipbh0 & 0xf0) >> 4]; @@ -1101,8 +1100,6 @@ static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) int rc = 0; uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16; - cpu_synchronize_state(CPU(cpu)); - switch (ipa1) { case PRIV_B2_XSCH: ioinst_handle_xsch(cpu, env->regs[1], RA_IGNORED); @@ -1248,7 +1245,6 @@ static int kvm_stpcifc_service_call(S390CPU *cpu, struct kvm_run *run) uint8_t ar; if (s390_has_feat(S390_FEAT_ZPCI)) { - cpu_synchronize_state(CPU(cpu)); fiba = get_base_disp_rxy(cpu, run, &ar); return stpcifc_service_call(cpu, r1, fiba, ar, RA_IGNORED); @@ -1266,7 +1262,6 @@ static int kvm_sic_service_call(S390CPU *cpu, struct kvm_run *run) uint16_t mode; int r; - cpu_synchronize_state(CPU(cpu)); mode = env->regs[r1] & 0xffff; isc = (env->regs[r3] >> 27) & 0x7; r = css_do_sic(env, isc, mode); @@ -1297,7 +1292,6 @@ static int kvm_pcistb_service_call(S390CPU *cpu, struct kvm_run *run) uint8_t ar; if (s390_has_feat(S390_FEAT_ZPCI)) { - cpu_synchronize_state(CPU(cpu)); gaddr = get_base_disp_rsy(cpu, run, &ar); return pcistb_service_call(cpu, r1, r3, gaddr, ar, RA_IGNORED); @@ -1313,7 +1307,6 @@ static int kvm_mpcifc_service_call(S390CPU *cpu, struct kvm_run *run) uint8_t ar; if (s390_has_feat(S390_FEAT_ZPCI)) { - cpu_synchronize_state(CPU(cpu)); fiba = get_base_disp_rxy(cpu, run, &ar); return mpcifc_service_call(cpu, r1, fiba, ar, RA_IGNORED); @@ -1401,7 +1394,6 @@ static int handle_hypercall(S390CPU *cpu, struct kvm_run *run) CPUS390XState *env = &cpu->env; int ret; - cpu_synchronize_state(CPU(cpu)); ret = s390_virtio_hypercall(env); if (ret == -EINVAL) { kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION); @@ -1416,7 +1408,6 @@ 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); @@ -1429,7 +1420,6 @@ static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run) { uint64_t r1, r3; - cpu_synchronize_state(CPU(cpu)); r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; r3 = run->s390_sieic.ipa & 0x000f; handle_diag_308(&cpu->env, r1, r3, RA_IGNORED); @@ -1440,8 +1430,6 @@ static int handle_sw_breakpoint(S390CPU *cpu, struct kvm_run *run) CPUS390XState *env = &cpu->env; unsigned long pc; - cpu_synchronize_state(CPU(cpu)); - pc = env->psw.addr - sw_bp_ilen; if (kvm_find_sw_breakpoint(CPU(cpu), pc)) { env->psw.addr = pc; @@ -1493,8 +1481,6 @@ static int kvm_s390_handle_sigp(S390CPU *cpu, uint8_t ipa1, uint32_t ipb) int ret; uint8_t order; - cpu_synchronize_state(CPU(cpu)); - /* get order code */ order = decode_basedisp_rs(env, ipb, NULL) & SIGP_ORDER_MASK; @@ -1556,7 +1542,6 @@ static int handle_oper_loop(S390CPU *cpu, struct kvm_run *run) CPUState *cs = CPU(cpu); PSW oldpsw, newpsw; - cpu_synchronize_state(cs); newpsw.mask = ldq_phys(cs->as, cpu->env.psa + offsetof(LowCore, program_new_psw)); newpsw.addr = ldq_phys(cs->as, cpu->env.psa + @@ -1609,7 +1594,6 @@ static int handle_intercept(S390CPU *cpu) break; case ICPT_WAITPSW: /* disabled wait, since enabled wait is handled in kernel */ - cpu_synchronize_state(cs); s390_handle_wait(cpu); r = EXCP_HALTED; break; @@ -1651,8 +1635,6 @@ static int handle_tsch(S390CPU *cpu) struct kvm_run *run = cs->kvm_run; int ret; - cpu_synchronize_state(cs); - ret = ioinst_handle_tsch(cpu, cpu->env.regs[1], run->s390_tsch.ipb, RA_IGNORED); if (ret < 0) { @@ -1778,7 +1760,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) qemu_mutex_lock_iothread(); - cpu_synchronize_state(cs); + kvm_cpu_synchronize_state(cs); switch (run->exit_reason) { case KVM_EXIT_S390_SIEIC: From 052888f043bacb18231046045b1f9cd946703170 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 26 Apr 2018 16:59:54 +0200 Subject: [PATCH 0170/2380] hw/s390x: Allow to configure the consoles with the "-serial" parameter The consoles ("sclpconsole" and "sclplmconsole") can only be configured with "-device" and "-chardev" so far. Other machines use the convenience option "-serial" to configure the default consoles, even for virtual consoles like spapr-vty on the pseries machine. So let's support this option on s390x, too. This way we can easily enable the serial console here again with "-nodefaults", for example: qemu-system-s390x -no-shutdown -nographic -nodefaults -serial mon:stdio ... which is way shorter than typing: qemu-system-s390x -no-shutdown -nographic -nodefaults \ -chardev stdio,id=c1,mux=on -device sclpconsole,chardev=c1 \ -mon chardev=c1 The -serial parameter can also be used if you only want to see the QEMU monitor on stdio without using -nodefaults, but not the console output. That's something that is pretty impossible with the current code today: qemu-system-s390x -no-shutdown -nographic -serial none While we're at it, this patch also maps the second -serial option to the "sclplmconsole", so that there is now an easy way to configure this second console on s390x, too, for example: qemu-system-s390x -no-shutdown -nographic -serial null -serial mon:stdio Additionally, the new code is also smaller than the old one and we have less s390x-specific code in vl.c :-) I've also checked that migration still works as expected by migrating a guest with console output back and forth between a qemu-system-s390x that has this patch and an instance without this patch. Signed-off-by: Thomas Huth Message-Id: <1524754794-28005-1-git-send-email-thuth@redhat.com> Reviewed-by: David Hildenbrand Signed-off-by: Cornelia Huck --- hw/s390x/event-facility.c | 14 +++++++++ hw/s390x/s390-virtio-ccw.c | 19 ++++++++++-- include/hw/boards.h | 1 - include/hw/s390x/event-facility.h | 2 ++ tests/boot-serial-test.c | 3 +- vl.c | 50 ------------------------------- 6 files changed, 34 insertions(+), 55 deletions(-) diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 0dea6cccae..ee5b83448b 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -543,3 +543,17 @@ static void register_types(void) } type_init(register_types) + +BusState *sclp_get_event_facility_bus(void) +{ + Object *busobj; + SCLPEventsBus *sbus; + + busobj = object_resolve_path_type("", TYPE_SCLP_EVENTS_BUS, NULL); + sbus = OBJECT_CHECK(SCLPEventsBus, busobj, TYPE_SCLP_EVENTS_BUS); + if (!sbus) { + return NULL; + } + + return &sbus->qbus; +} diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index c84098dd17..100dfdc96d 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -288,6 +288,15 @@ static void s390_create_virtio_net(BusState *bus, const char *name) } } +static void s390_create_sclpconsole(const char *type, Chardev *chardev) +{ + DeviceState *dev; + + dev = qdev_create(sclp_get_event_facility_bus(), type); + qdev_prop_set_chr(dev, "chardev", chardev); + qdev_init_nofail(dev); +} + static void ccw_init(MachineState *machine) { int ret; @@ -346,6 +355,14 @@ static void ccw_init(MachineState *machine) /* Create VirtIO network adapters */ s390_create_virtio_net(BUS(css_bus), "virtio-net-ccw"); + /* init consoles */ + if (serial_hd(0)) { + s390_create_sclpconsole("sclpconsole", serial_hd(0)); + } + if (serial_hd(1)) { + s390_create_sclpconsole("sclplmconsole", serial_hd(1)); + } + /* Register savevm handler for guest TOD clock */ register_savevm_live(NULL, "todclock", 0, 1, &savevm_gtod, NULL); } @@ -470,10 +487,8 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->no_floppy = 1; - mc->no_serial = 1; mc->no_parallel = 1; mc->no_sdcard = 1; - mc->use_sclp = 1; mc->max_cpus = S390_MAX_CPUS; mc->has_hotpluggable_cpus = true; mc->get_hotplug_handler = s390_get_hotplug_handler; diff --git a/include/hw/boards.h b/include/hw/boards.h index a609239112..5c5eee55e6 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -180,7 +180,6 @@ struct MachineClass { unsigned int no_serial:1, no_parallel:1, use_virtcon:1, - use_sclp:1, no_floppy:1, no_cdrom:1, no_sdcard:1, diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index 8b39a80bb9..6cf71cec38 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -210,4 +210,6 @@ typedef struct SCLPEventFacilityClass { bool (*event_pending)(SCLPEventFacility *ef); } SCLPEventFacilityClass; +BusState *sclp_get_event_facility_bus(void); + #endif diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c index 011525d8cf..4d6815c3e0 100644 --- a/tests/boot-serial-test.c +++ b/tests/boot-serial-test.c @@ -96,8 +96,7 @@ static testdef_t tests[] = { { "sparc", "SS-4", "", "MB86904" }, { "sparc", "SS-600MP", "", "TMS390Z55" }, { "sparc64", "sun4u", "", "UltraSPARC" }, - { "s390x", "s390-ccw-virtio", - "-nodefaults -device sclpconsole,chardev=serial0", "virtio device" }, + { "s390x", "s390-ccw-virtio", "", "virtio device" }, { "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 }, { "microblaze", "petalogix-s3adsp1800", "", "TT", sizeof(kernel_pls3adsp1800), kernel_pls3adsp1800 }, diff --git a/vl.c b/vl.c index 7487535dca..806eec2ef6 100644 --- a/vl.c +++ b/vl.c @@ -133,7 +133,6 @@ int main(int argc, char **argv) #include "sysemu/iothread.h" #define MAX_VIRTIO_CONSOLES 1 -#define MAX_SCLP_CONSOLES 1 static const char *data_dir[16]; static int data_dir_idx; @@ -158,7 +157,6 @@ static int num_serial_hds = 0; static Chardev **serial_hds = NULL; Chardev *parallel_hds[MAX_PARALLEL_PORTS]; Chardev *virtcon_hds[MAX_VIRTIO_CONSOLES]; -Chardev *sclp_hds[MAX_SCLP_CONSOLES]; int win2k_install_hack = 0; int singlestep = 0; int smp_cpus; @@ -210,7 +208,6 @@ static int has_defaults = 1; static int default_serial = 1; static int default_parallel = 1; static int default_virtcon = 1; -static int default_sclp = 1; static int default_monitor = 1; static int default_floppy = 1; static int default_cdrom = 1; @@ -2588,39 +2585,6 @@ static int virtcon_parse(const char *devname) return 0; } -static int sclp_parse(const char *devname) -{ - QemuOptsList *device = qemu_find_opts("device"); - static int index = 0; - char label[32]; - QemuOpts *dev_opts; - - if (strcmp(devname, "none") == 0) { - return 0; - } - if (index == MAX_SCLP_CONSOLES) { - error_report("too many sclp consoles"); - exit(1); - } - - assert(arch_type == QEMU_ARCH_S390X); - - dev_opts = qemu_opts_create(device, NULL, 0, NULL); - qemu_opt_set(dev_opts, "driver", "sclpconsole", &error_abort); - - snprintf(label, sizeof(label), "sclpcon%d", index); - sclp_hds[index] = qemu_chr_new(label, devname); - if (!sclp_hds[index]) { - error_report("could not connect sclp console" - " to character backend '%s'", devname); - return -1; - } - qemu_opt_set(dev_opts, "chardev", label, &error_abort); - - index++; - return 0; -} - static int debugcon_parse(const char *devname) { QemuOpts *opts; @@ -4254,9 +4218,6 @@ int main(int argc, char **argv, char **envp) if (!has_defaults || !machine_class->use_virtcon) { default_virtcon = 0; } - if (!has_defaults || !machine_class->use_sclp) { - default_sclp = 0; - } if (!has_defaults || machine_class->no_floppy) { default_floppy = 0; } @@ -4303,16 +4264,11 @@ int main(int argc, char **argv, char **envp) add_device_config(DEV_SERIAL, "mon:stdio"); } else if (default_virtcon && default_monitor) { add_device_config(DEV_VIRTCON, "mon:stdio"); - } else if (default_sclp && default_monitor) { - add_device_config(DEV_SCLP, "mon:stdio"); } else { if (default_serial) add_device_config(DEV_SERIAL, "stdio"); if (default_virtcon) add_device_config(DEV_VIRTCON, "stdio"); - if (default_sclp) { - add_device_config(DEV_SCLP, "stdio"); - } if (default_monitor) monitor_parse("stdio", "readline", false); } @@ -4325,9 +4281,6 @@ int main(int argc, char **argv, char **envp) monitor_parse("vc:80Cx24C", "readline", false); if (default_virtcon) add_device_config(DEV_VIRTCON, "vc:80Cx24C"); - if (default_sclp) { - add_device_config(DEV_SCLP, "vc:80Cx24C"); - } } #if defined(CONFIG_VNC) @@ -4577,9 +4530,6 @@ int main(int argc, char **argv, char **envp) exit(1); if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0) exit(1); - if (foreach_device_config(DEV_SCLP, sclp_parse) < 0) { - exit(1); - } if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) exit(1); From 355d4d1c00e708907ff391c24ca708f1c9c06bf0 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Wed, 18 Apr 2018 09:41:53 +0300 Subject: [PATCH 0171/2380] m68k: fix subx mem, mem instruction This patch fixes decrement of the pointers for subx mem, mem instructions. Without the patch pointers are decremented by OS_* constant value instead of retrieving the corresponding data size and using it as a decrement. Signed-off-by: Pavel Dovgalyuk Reviewed-by: Laurent Vivier Message-Id: <20180418064152.24606.71975.stgit@pasha-VirtualBox> Signed-off-by: Laurent Vivier --- target/m68k/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 6beaf9ed66..e407ba2db3 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -3166,11 +3166,11 @@ DISAS_INSN(subx_mem) opsize = insn_opsize(insn); addr_src = AREG(insn, 0); - tcg_gen_subi_i32(addr_src, addr_src, opsize); + tcg_gen_subi_i32(addr_src, addr_src, opsize_bytes(opsize)); src = gen_load(s, opsize, addr_src, 1, IS_USER(s)); addr_dest = AREG(insn, 9); - tcg_gen_subi_i32(addr_dest, addr_dest, opsize); + tcg_gen_subi_i32(addr_dest, addr_dest, opsize_bytes(opsize)); dest = gen_load(s, opsize, addr_dest, 1, IS_USER(s)); gen_subx(s, src, dest, opsize); From 59b1a90b0b5dc6b368364e9e1d40184eb4506c39 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 14 Apr 2018 18:06:34 +0200 Subject: [PATCH 0172/2380] target-microblaze: Respect MSR.PVR as read-only Respect MSR.PVR as read-only. We were wrongly overwriting the PVR bit. Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 7628b0e25b..f739751930 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -424,7 +424,7 @@ static inline void msr_write(DisasContext *dc, TCGv v) /* PVR bit is not writable. */ tcg_gen_andi_tl(t, v, ~MSR_PVR); tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); - tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], v); + tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t); tcg_temp_free(t); } From 5153bb897a3d41d52c5b7187e9e40d5c26b04d57 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 14 Apr 2018 18:58:56 +0200 Subject: [PATCH 0173/2380] target-microblaze: Fix trap checks for FPU insns Fix trap checks for FPU insns when extended FPU insns are enabled. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index f739751930..ec12fed49d 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1412,7 +1412,7 @@ static void dec_fpu(DisasContext *dc) if ((dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && (dc->cpu->cfg.use_fpu != 1)) { + && !dc->cpu->cfg.use_fpu) { tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; From df1e528aad265a5c6ad7c84ef2861a5e4b2913bf Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 20:45:14 +0200 Subject: [PATCH 0174/2380] target-microblaze: Don't clobber the IMM reg for ld/st reversed Do not clobber the IMM register on reversed load/stores. Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index ec12fed49d..100883e2cc 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -952,7 +952,6 @@ static void dec_load(DisasContext *dc) tcg_gen_sub_tl(low, tcg_const_tl(3), low); tcg_gen_andi_tl(t, t, ~3); tcg_gen_or_tl(t, t, low); - tcg_gen_mov_tl(env_imm, t); tcg_temp_free(low); break; } @@ -1104,7 +1103,6 @@ static void dec_store(DisasContext *dc) tcg_gen_sub_tl(low, tcg_const_tl(3), low); tcg_gen_andi_tl(t, t, ~3); tcg_gen_or_tl(t, t, low); - tcg_gen_mov_tl(env_imm, t); tcg_temp_free(low); break; } From bd9e66086b93a0a908a70a2679819d2d080d87b4 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sun, 15 Apr 2018 23:05:22 +0200 Subject: [PATCH 0175/2380] target-microblaze: mmu: Make TLBSX write-only Make TLBSX write-only and guest-error log reads from it. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/mmu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index a0f06758f8..8391811900 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -182,7 +182,7 @@ done: uint32_t mmu_read(CPUMBState *env, uint32_t rn) { unsigned int i; - uint32_t r; + uint32_t r = 0; if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); @@ -211,6 +211,9 @@ uint32_t mmu_read(CPUMBState *env, uint32_t rn) } r = env->mmu.regs[rn]; break; + case MMU_R_TLBSX: + qemu_log_mask(LOG_GUEST_ERROR, "TLBSX is write-only.\n"); + break; default: r = env->mmu.regs[rn]; break; From fce6a8eceb07e27f0cdea87427f4e560dfa0b1c8 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sun, 15 Apr 2018 23:21:06 +0200 Subject: [PATCH 0176/2380] target-microblaze: mmu: Make the TLBX MISS bit read-only Make the TLBX MISS bit read-only. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/mmu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 8391811900..9d5e6aa8a5 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -273,6 +273,10 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) env->mmu.regs[rn] = v; } break; + case MMU_R_TLBX: + /* Bit 31 is read-only. */ + env->mmu.regs[rn] = deposit32(env->mmu.regs[rn], 0, 31, v); + break; case MMU_R_TLBSX: { struct microblaze_mmu_lookup lu; From 981348af5c3c72335d95f6877abf702d80176eb3 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 30 Apr 2018 19:01:55 +0200 Subject: [PATCH 0177/2380] m68k: Fix floatx80_lognp1 (Coverity CID1390587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit return the result of packFloatx80() instead of dropping it. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180430170156.1860-1-laurent@vivier.eu> --- target/m68k/softfloat.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c index dffb371c71..2c069a5efb 100644 --- a/target/m68k/softfloat.c +++ b/target/m68k/softfloat.c @@ -334,7 +334,8 @@ floatx80 floatx80_lognp1(floatx80 a, float_status *status) if (aSign && aExp >= one_exp) { if (aExp == one_exp && aSig == one_sig) { float_raise(float_flag_divbyzero, status); - packFloatx80(aSign, floatx80_infinity.high, floatx80_infinity.low); + return packFloatx80(aSign, floatx80_infinity.high, + floatx80_infinity.low); } float_raise(float_flag_invalid, status); return floatx80_default_nan(status); From 6361d2984ce88912976a34e1797a5ad5139c649b Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 30 Apr 2018 19:01:56 +0200 Subject: [PATCH 0178/2380] m68k: remove dead code (Coverity CID1390617) floatx80_sin() and floatx80_cos() are derived from one sincos() function. They have both unused code coming from their common origin. Remove it. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Message-Id: <20180430170156.1860-2-laurent@vivier.eu> --- target/m68k/softfloat.c | 43 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c index 2c069a5efb..e41b07d042 100644 --- a/target/m68k/softfloat.c +++ b/target/m68k/softfloat.c @@ -1497,7 +1497,7 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) int32_t compact, l, n, j; floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2; float32 posneg1, twoto63; - flag adjn, endflag; + flag endflag; aSig = extractFloatx80Frac(a); aExp = extractFloatx80Exp(a); @@ -1515,8 +1515,6 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) return packFloatx80(aSign, 0, 0); } - adjn = 0; - user_rnd_mode = status->float_rounding_mode; user_rnd_prec = status->floatx80_rounding_precision; status->float_rounding_mode = float_round_nearest_even; @@ -1591,14 +1589,8 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) status->float_rounding_mode = user_rnd_mode; status->floatx80_rounding_precision = user_rnd_prec; - if (adjn) { - /* COSTINY */ - a = floatx80_sub(fp0, float32_to_floatx80( - make_float32(0x00800000), status), status); - } else { - /* SINTINY */ - a = floatx80_move(a, status); - } + /* SINTINY */ + a = floatx80_move(a, status); float_raise(float_flag_inexact, status); return a; @@ -1616,7 +1608,7 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) status); /* FP0 IS R = (X-Y1)-Y2 */ sincont: - if ((n + adjn) & 1) { + if (n & 1) { /* COSPOLY */ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ @@ -1629,7 +1621,7 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) xExp = extractFloatx80Exp(fp0); xSig = extractFloatx80Frac(fp0); - if (((n + adjn) >> 1) & 1) { + if ((n >> 1) & 1) { xSign ^= 1; posneg1 = make_float32(0xBF800000); /* -1 */ } else { @@ -1681,7 +1673,7 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) xExp = extractFloatx80Exp(fp0); xSig = extractFloatx80Frac(fp0); - xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */ + xSign ^= (n >> 1) & 1; /* X IS NOW R'= SGN*R */ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ @@ -1744,7 +1736,7 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) int32_t compact, l, n, j; floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2; float32 posneg1, twoto63; - flag adjn, endflag; + flag endflag; aSig = extractFloatx80Frac(a); aExp = extractFloatx80Exp(a); @@ -1762,8 +1754,6 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) return packFloatx80(0, one_exp, one_sig); } - adjn = 1; - user_rnd_mode = status->float_rounding_mode; user_rnd_prec = status->floatx80_rounding_precision; status->float_rounding_mode = float_round_nearest_even; @@ -1837,15 +1827,10 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) status->float_rounding_mode = user_rnd_mode; status->floatx80_rounding_precision = user_rnd_prec; - if (adjn) { - /* COSTINY */ - a = floatx80_sub(fp0, float32_to_floatx80( - make_float32(0x00800000), status), - status); - } else { - /* SINTINY */ - a = floatx80_move(a, status); - } + /* COSTINY */ + a = floatx80_sub(fp0, float32_to_floatx80( + make_float32(0x00800000), status), + status); float_raise(float_flag_inexact, status); return a; @@ -1863,7 +1848,7 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) status); /* FP0 IS R = (X-Y1)-Y2 */ sincont: - if ((n + adjn) & 1) { + if ((n + 1) & 1) { /* COSPOLY */ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ @@ -1876,7 +1861,7 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) xExp = extractFloatx80Exp(fp0); xSig = extractFloatx80Frac(fp0); - if (((n + adjn) >> 1) & 1) { + if (((n + 1) >> 1) & 1) { xSign ^= 1; posneg1 = make_float32(0xBF800000); /* -1 */ } else { @@ -1927,7 +1912,7 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) xExp = extractFloatx80Exp(fp0); xSig = extractFloatx80Frac(fp0); - xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */ + xSign ^= ((n + 1) >> 1) & 1; /* X IS NOW R'= SGN*R */ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ From 83dc62f6ed6b4f2afce71d579a630441cca0957c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Sun, 29 Apr 2018 09:40:02 +0200 Subject: [PATCH 0179/2380] hw/m68k/mcf5208: Fix trivial typo in board description It's the MCF5208 evaluation board, not the MCF5206 eval board. Signed-off-by: Thomas Huth Reviewed-by: Laurent Vivier Message-Id: <20180429094002.3293c9de@thl530.multi.box> Signed-off-by: Laurent Vivier --- hw/m68k/mcf5208.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 7aca58542e..ae3dcc98c3 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -315,7 +315,7 @@ static void mcf5208evb_init(MachineState *machine) static void mcf5208evb_machine_init(MachineClass *mc) { - mc->desc = "MCF5206EVB"; + mc->desc = "MCF5208EVB"; mc->init = mcf5208evb_init; mc->is_default = 1; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5208"); From d103021269ca9307ed7ca0d845d2b9e6c387509a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Apr 2018 07:18:01 -1000 Subject: [PATCH 0180/2380] tcg: Document INDEX_mul[us]h_* Signed-off-by: Richard Henderson --- tcg/README | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tcg/README b/tcg/README index bb2ea5121b..a5237a9edb 100644 --- a/tcg/README +++ b/tcg/README @@ -431,6 +431,14 @@ double-word product T0. The later is returned in two single-word outputs. Similar to mulu2, except the two inputs T1 and T2 are signed. +* mulsh_i32/i64 t0, t1, t2 +* muluh_i32/i64 t0, t1, t2 + +Provide the high part of a signed or unsigned multiply, respectively. +If mulu2/muls2 are not provided by the backend, the tcg-op generator +can obtain the same results can be obtained by emitting a pair of +opcodes, mul+muluh/mulsh. + ********* Memory Barrier support * mb <$arg> From 3f814b803797c007abfe5c4041de754e01723031 Mon Sep 17 00:00:00 2001 From: Henry Wertz Date: Tue, 17 Apr 2018 12:06:23 -1000 Subject: [PATCH 0181/2380] tcg/arm: Fix memory barrier encoding I found with qemu 2.11.x or newer that I would get an illegal instruction error running some Intel binaries on my ARM chromebook. On investigation, I found it was quitting on memory barriers. qemu instruction: mb $0x31 was translating as: 0x604050cc: 5bf07ff5 blpl #0x600250a8 After patch it gives: 0x604050cc: f57ff05b dmb ish In short, I found INSN_DMB_ISH (memory barrier for ARMv7) appeared to be correct based on online docs, but due to some endian-related shenanigans it had to be byte-swapped to suit qemu; it appears INSN_DMB_MCR (memory barrier for ARMv6) also should be byte swapped (and this patch does so). I have not checked for correctness of aarch64's barrier instruction. Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Henry Wertz Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.inc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcg/arm/tcg-target.inc.c b/tcg/arm/tcg-target.inc.c index dc83f3e5be..56a32a470f 100644 --- a/tcg/arm/tcg-target.inc.c +++ b/tcg/arm/tcg-target.inc.c @@ -159,8 +159,8 @@ typedef enum { INSN_STRD_IMM = 0x004000f0, INSN_STRD_REG = 0x000000f0, - INSN_DMB_ISH = 0x5bf07ff5, - INSN_DMB_MCR = 0xba0f07ee, + INSN_DMB_ISH = 0xf57ff05b, + INSN_DMB_MCR = 0xee070fba, /* Architected nop introduced in v6k. */ /* ??? This is an MSR (imm) 0,0,0 insn. Anyone know if this From 9a938d86b04025ac605db0ea9819e5896bf576ec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Apr 2018 11:35:42 -1000 Subject: [PATCH 0182/2380] tcg: Allow wider vectors for cmp and mul In db432672, we allow wide inputs for operations such as add. However, in 212be173 and 3774030a we didn't do the same for compare and multiply. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/tcg-op-vec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index 70ec889bc1..2ca219734d 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -355,8 +355,8 @@ void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, TCGType type = rt->base_type; int can; - tcg_debug_assert(at->base_type == type); - tcg_debug_assert(bt->base_type == type); + tcg_debug_assert(at->base_type >= type); + tcg_debug_assert(bt->base_type >= type); can = tcg_can_emit_vec_op(INDEX_op_cmp_vec, type, vece); if (can > 0) { vec_gen_4(INDEX_op_cmp_vec, type, vece, ri, ai, bi, cond); @@ -377,8 +377,8 @@ void tcg_gen_mul_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) TCGType type = rt->base_type; int can; - tcg_debug_assert(at->base_type == type); - tcg_debug_assert(bt->base_type == type); + tcg_debug_assert(at->base_type >= type); + tcg_debug_assert(bt->base_type >= type); can = tcg_can_emit_vec_op(INDEX_op_mul_vec, type, vece); if (can > 0) { vec_gen_3(INDEX_op_mul_vec, type, vece, ri, ai, bi); From 5bfa803448638a45542441fd6b7cc1241403ea72 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 22 Feb 2018 18:17:57 -0800 Subject: [PATCH 0183/2380] tcg: Improve TCGv_ptr support Drop TCGV_PTR_TO_NAT and TCGV_NAT_TO_PTR internal macros. Add tcg_temp_local_new_ptr, tcg_gen_brcondi_ptr, tcg_gen_ext_i32_ptr, tcg_gen_trunc_i64_ptr, tcg_gen_extu_ptr_i64, tcg_gen_trunc_ptr_i32. Use inlines instead of macros where possible. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/hppa/translate.c | 16 +------- tcg/tcg-op.h | 91 +++++++++++++++++++++++++++++++---------- tcg/tcg.c | 31 +------------- tcg/tcg.h | 86 ++++++++++++++++++++++++-------------- 4 files changed, 130 insertions(+), 94 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index c532889b1f..cdc397308b 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -151,13 +151,7 @@ #define tcg_gen_qemu_ld_reg tcg_gen_qemu_ld_i64 #define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i64 #define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i64 -#if UINTPTR_MAX == UINT32_MAX -# define tcg_gen_trunc_reg_ptr(p, r) \ - tcg_gen_trunc_i64_i32(TCGV_PTR_TO_NAT(p), r) -#else -# define tcg_gen_trunc_reg_ptr(p, r) \ - tcg_gen_mov_i64(TCGV_PTR_TO_NAT(p), r) -#endif +#define tcg_gen_trunc_reg_ptr tcg_gen_trunc_i64_ptr #else #define TCGv_reg TCGv_i32 #define tcg_temp_new tcg_temp_new_i32 @@ -251,13 +245,7 @@ #define tcg_gen_qemu_ld_reg tcg_gen_qemu_ld_i32 #define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i32 #define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i32 -#if UINTPTR_MAX == UINT32_MAX -# define tcg_gen_trunc_reg_ptr(p, r) \ - tcg_gen_mov_i32(TCGV_PTR_TO_NAT(p), r) -#else -# define tcg_gen_trunc_reg_ptr(p, r) \ - tcg_gen_extu_i32_i64(TCGV_PTR_TO_NAT(p), r) -#endif +#define tcg_gen_trunc_reg_ptr tcg_gen_ext_i32_ptr #endif /* TARGET_REGISTER_BITS */ typedef struct DisasCond { diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 75bb55aeac..5d2c91a1b6 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -1137,25 +1137,74 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #endif #if UINTPTR_MAX == UINT32_MAX -# define tcg_gen_ld_ptr(R, A, O) \ - tcg_gen_ld_i32(TCGV_PTR_TO_NAT(R), (A), (O)) -# define tcg_gen_discard_ptr(A) \ - tcg_gen_discard_i32(TCGV_PTR_TO_NAT(A)) -# define tcg_gen_add_ptr(R, A, B) \ - tcg_gen_add_i32(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), TCGV_PTR_TO_NAT(B)) -# define tcg_gen_addi_ptr(R, A, B) \ - tcg_gen_addi_i32(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), (B)) -# define tcg_gen_ext_i32_ptr(R, A) \ - tcg_gen_mov_i32(TCGV_PTR_TO_NAT(R), (A)) +# define PTR i32 +# define NAT TCGv_i32 #else -# define tcg_gen_ld_ptr(R, A, O) \ - tcg_gen_ld_i64(TCGV_PTR_TO_NAT(R), (A), (O)) -# define tcg_gen_discard_ptr(A) \ - tcg_gen_discard_i64(TCGV_PTR_TO_NAT(A)) -# define tcg_gen_add_ptr(R, A, B) \ - tcg_gen_add_i64(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), TCGV_PTR_TO_NAT(B)) -# define tcg_gen_addi_ptr(R, A, B) \ - tcg_gen_addi_i64(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), (B)) -# define tcg_gen_ext_i32_ptr(R, A) \ - tcg_gen_ext_i32_i64(TCGV_PTR_TO_NAT(R), (A)) -#endif /* UINTPTR_MAX == UINT32_MAX */ +# define PTR i64 +# define NAT TCGv_i64 +#endif + +static inline void tcg_gen_ld_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) +{ + glue(tcg_gen_ld_,PTR)((NAT)r, a, o); +} + +static inline void tcg_gen_discard_ptr(TCGv_ptr a) +{ + glue(tcg_gen_discard_,PTR)((NAT)a); +} + +static inline void tcg_gen_add_ptr(TCGv_ptr r, TCGv_ptr a, TCGv_ptr b) +{ + glue(tcg_gen_add_,PTR)((NAT)r, (NAT)a, (NAT)b); +} + +static inline void tcg_gen_addi_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t b) +{ + glue(tcg_gen_addi_,PTR)((NAT)r, (NAT)a, b); +} + +static inline void tcg_gen_brcondi_ptr(TCGCond cond, TCGv_ptr a, + intptr_t b, TCGLabel *label) +{ + glue(tcg_gen_brcondi_,PTR)(cond, (NAT)a, b, label); +} + +static inline void tcg_gen_ext_i32_ptr(TCGv_ptr r, TCGv_i32 a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_mov_i32((NAT)r, a); +#else + tcg_gen_ext_i32_i64((NAT)r, a); +#endif +} + +static inline void tcg_gen_trunc_i64_ptr(TCGv_ptr r, TCGv_i64 a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_extrl_i64_i32((NAT)r, a); +#else + tcg_gen_mov_i64((NAT)r, a); +#endif +} + +static inline void tcg_gen_extu_ptr_i64(TCGv_i64 r, TCGv_ptr a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_extu_i32_i64(r, (NAT)a); +#else + tcg_gen_mov_i64(r, (NAT)a); +#endif +} + +static inline void tcg_gen_trunc_ptr_i32(TCGv_i32 r, TCGv_ptr a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_mov_i32(r, (NAT)a); +#else + tcg_gen_extrl_i64_i32(r, (NAT)a); +#endif +} + +#undef PTR +#undef NAT diff --git a/tcg/tcg.c b/tcg/tcg.c index bb24526c93..b5e706bc49 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -980,7 +980,7 @@ TCGTemp *tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, return ts; } -static TCGTemp *tcg_temp_new_internal(TCGType type, int temp_local) +TCGTemp *tcg_temp_new_internal(TCGType type, bool temp_local) { TCGContext *s = tcg_ctx; TCGTemp *ts; @@ -1025,18 +1025,6 @@ static TCGTemp *tcg_temp_new_internal(TCGType type, int temp_local) return ts; } -TCGv_i32 tcg_temp_new_internal_i32(int temp_local) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, temp_local); - return temp_tcgv_i32(t); -} - -TCGv_i64 tcg_temp_new_internal_i64(int temp_local) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, temp_local); - return temp_tcgv_i64(t); -} - TCGv_vec tcg_temp_new_vec(TCGType type) { TCGTemp *t; @@ -1072,7 +1060,7 @@ TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match) return temp_tcgv_vec(t); } -static void tcg_temp_free_internal(TCGTemp *ts) +void tcg_temp_free_internal(TCGTemp *ts) { TCGContext *s = tcg_ctx; int k, idx; @@ -1093,21 +1081,6 @@ static void tcg_temp_free_internal(TCGTemp *ts) set_bit(idx, s->free_temps[k].l); } -void tcg_temp_free_i32(TCGv_i32 arg) -{ - tcg_temp_free_internal(tcgv_i32_temp(arg)); -} - -void tcg_temp_free_i64(TCGv_i64 arg) -{ - tcg_temp_free_internal(tcgv_i64_temp(arg)); -} - -void tcg_temp_free_vec(TCGv_vec arg) -{ - tcg_temp_free_internal(tcgv_vec_temp(arg)); -} - TCGv_i32 tcg_const_i32(int32_t val) { TCGv_i32 t0; diff --git a/tcg/tcg.h b/tcg/tcg.h index 30896ca304..eb0d4f6ca7 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -890,15 +890,30 @@ void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size); TCGTemp *tcg_global_mem_new_internal(TCGType, TCGv_ptr, intptr_t, const char *); - -TCGv_i32 tcg_temp_new_internal_i32(int temp_local); -TCGv_i64 tcg_temp_new_internal_i64(int temp_local); +TCGTemp *tcg_temp_new_internal(TCGType, bool); +void tcg_temp_free_internal(TCGTemp *); TCGv_vec tcg_temp_new_vec(TCGType type); TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match); -void tcg_temp_free_i32(TCGv_i32 arg); -void tcg_temp_free_i64(TCGv_i64 arg); -void tcg_temp_free_vec(TCGv_vec arg); +static inline void tcg_temp_free_i32(TCGv_i32 arg) +{ + tcg_temp_free_internal(tcgv_i32_temp(arg)); +} + +static inline void tcg_temp_free_i64(TCGv_i64 arg) +{ + tcg_temp_free_internal(tcgv_i64_temp(arg)); +} + +static inline void tcg_temp_free_ptr(TCGv_ptr arg) +{ + tcg_temp_free_internal(tcgv_ptr_temp(arg)); +} + +static inline void tcg_temp_free_vec(TCGv_vec arg) +{ + tcg_temp_free_internal(tcgv_vec_temp(arg)); +} static inline TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t offset, const char *name) @@ -909,12 +924,14 @@ static inline TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t offset, static inline TCGv_i32 tcg_temp_new_i32(void) { - return tcg_temp_new_internal_i32(0); + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, false); + return temp_tcgv_i32(t); } static inline TCGv_i32 tcg_temp_local_new_i32(void) { - return tcg_temp_new_internal_i32(1); + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, true); + return temp_tcgv_i32(t); } static inline TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t offset, @@ -926,12 +943,33 @@ static inline TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t offset, static inline TCGv_i64 tcg_temp_new_i64(void) { - return tcg_temp_new_internal_i64(0); + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, false); + return temp_tcgv_i64(t); } static inline TCGv_i64 tcg_temp_local_new_i64(void) { - return tcg_temp_new_internal_i64(1); + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, true); + return temp_tcgv_i64(t); +} + +static inline TCGv_ptr tcg_global_mem_new_ptr(TCGv_ptr reg, intptr_t offset, + const char *name) +{ + TCGTemp *t = tcg_global_mem_new_internal(TCG_TYPE_PTR, reg, offset, name); + return temp_tcgv_ptr(t); +} + +static inline TCGv_ptr tcg_temp_new_ptr(void) +{ + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_PTR, false); + return temp_tcgv_ptr(t); +} + +static inline TCGv_ptr tcg_temp_local_new_ptr(void) +{ + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_PTR, true); + return temp_tcgv_ptr(t); } #if defined(CONFIG_DEBUG_TCG) @@ -1009,26 +1047,6 @@ do {\ abort();\ } while (0) -#if UINTPTR_MAX == UINT32_MAX -static inline TCGv_ptr TCGV_NAT_TO_PTR(TCGv_i32 n) { return (TCGv_ptr)n; } -static inline TCGv_i32 TCGV_PTR_TO_NAT(TCGv_ptr n) { return (TCGv_i32)n; } - -#define tcg_const_ptr(V) TCGV_NAT_TO_PTR(tcg_const_i32((intptr_t)(V))) -#define tcg_global_mem_new_ptr(R, O, N) \ - TCGV_NAT_TO_PTR(tcg_global_mem_new_i32((R), (O), (N))) -#define tcg_temp_new_ptr() TCGV_NAT_TO_PTR(tcg_temp_new_i32()) -#define tcg_temp_free_ptr(T) tcg_temp_free_i32(TCGV_PTR_TO_NAT(T)) -#else -static inline TCGv_ptr TCGV_NAT_TO_PTR(TCGv_i64 n) { return (TCGv_ptr)n; } -static inline TCGv_i64 TCGV_PTR_TO_NAT(TCGv_ptr n) { return (TCGv_i64)n; } - -#define tcg_const_ptr(V) TCGV_NAT_TO_PTR(tcg_const_i64((intptr_t)(V))) -#define tcg_global_mem_new_ptr(R, O, N) \ - TCGV_NAT_TO_PTR(tcg_global_mem_new_i64((R), (O), (N))) -#define tcg_temp_new_ptr() TCGV_NAT_TO_PTR(tcg_temp_new_i64()) -#define tcg_temp_free_ptr(T) tcg_temp_free_i64(TCGV_PTR_TO_NAT(T)) -#endif - bool tcg_op_supported(TCGOpcode op); void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args); @@ -1052,6 +1070,14 @@ TCGv_vec tcg_const_ones_vec(TCGType); TCGv_vec tcg_const_zeros_vec_matching(TCGv_vec); TCGv_vec tcg_const_ones_vec_matching(TCGv_vec); +#if UINTPTR_MAX == UINT32_MAX +# define tcg_const_ptr(x) ((TCGv_ptr)tcg_const_i32((intptr_t)(x))) +# define tcg_const_local_ptr(x) ((TCGv_ptr)tcg_const_local_i32((intptr_t)(x))) +#else +# define tcg_const_ptr(x) ((TCGv_ptr)tcg_const_i64((intptr_t)(x))) +# define tcg_const_local_ptr(x) ((TCGv_ptr)tcg_const_local_i64((intptr_t)(x))) +#endif + TCGLabel *gen_new_label(void); /** From 6001f7729e12dd1d810291e4cbf83cee8e07441d Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 30 Apr 2018 01:58:40 +0200 Subject: [PATCH 0184/2380] tcg: workaround branch instruction overflow in tcg_out_qemu_ld/st ppc64 uses a BC instruction to call the tcg_out_qemu_ld/st slow path. BC instruction uses a relative address encoded on 14 bits. The slow path functions are added at the end of the generated instructions buffer, in the reverse order of the callers. So more we have slow path functions more the distance between the caller (BC) and the function increases. This patch changes the behavior to generate the functions in the same order of the callers. Cc: qemu-stable@nongnu.org Fixes: 15fa08f845 ("tcg: Dynamically allocate TCGOps") Signed-off-by: Laurent Vivier Message-Id: <20180429235840.16659-1-lvivier@redhat.com> Signed-off-by: Richard Henderson --- tcg/tcg-ldst.inc.c | 8 ++++---- tcg/tcg.c | 2 +- tcg/tcg.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tcg/tcg-ldst.inc.c b/tcg/tcg-ldst.inc.c index 0e14cf4357..47f41b921b 100644 --- a/tcg/tcg-ldst.inc.c +++ b/tcg/tcg-ldst.inc.c @@ -30,7 +30,7 @@ typedef struct TCGLabelQemuLdst { TCGReg datahi_reg; /* reg index for high word to be loaded or stored */ tcg_insn_unit *raddr; /* gen code addr of the next IR of qemu_ld/st IR */ tcg_insn_unit *label_ptr[2]; /* label pointers to be updated */ - struct TCGLabelQemuLdst *next; + QSIMPLEQ_ENTRY(TCGLabelQemuLdst) next; } TCGLabelQemuLdst; @@ -46,7 +46,7 @@ static bool tcg_out_ldst_finalize(TCGContext *s) TCGLabelQemuLdst *lb; /* qemu_ld/st slow paths */ - for (lb = s->ldst_labels; lb != NULL; lb = lb->next) { + QSIMPLEQ_FOREACH(lb, &s->ldst_labels, next) { if (lb->is_ld) { tcg_out_qemu_ld_slow_path(s, lb); } else { @@ -72,7 +72,7 @@ static inline TCGLabelQemuLdst *new_ldst_label(TCGContext *s) { TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); - l->next = s->ldst_labels; - s->ldst_labels = l; + QSIMPLEQ_INSERT_TAIL(&s->ldst_labels, l, next); + return l; } diff --git a/tcg/tcg.c b/tcg/tcg.c index b5e706bc49..551caf1c53 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -3297,7 +3297,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) s->code_ptr = tb->tc.ptr; #ifdef TCG_TARGET_NEED_LDST_LABELS - s->ldst_labels = NULL; + QSIMPLEQ_INIT(&s->ldst_labels); #endif #ifdef TCG_TARGET_NEED_POOL_LABELS s->pool_labels = NULL; diff --git a/tcg/tcg.h b/tcg/tcg.h index eb0d4f6ca7..75fbad128b 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -699,7 +699,7 @@ struct TCGContext { /* These structures are private to tcg-target.inc.c. */ #ifdef TCG_TARGET_NEED_LDST_LABELS - struct TCGLabelQemuLdst *ldst_labels; + QSIMPLEQ_HEAD(ldst_labels, TCGLabelQemuLdst) ldst_labels; #endif #ifdef TCG_TARGET_NEED_POOL_LABELS struct TCGLabelPoolData *pool_labels; From 8f9c64bfa584ab66b8bcbfc162efa179f7f1cd41 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 2 May 2018 08:59:24 +0200 Subject: [PATCH 0185/2380] 9p: add trace event for v9fs_setattr() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't print the tv_nsec part of atime and mtime, to stay below the 10 argument limit of trace events. Signed-off-by: Greg Kurz Reviewed-by: Philippe Mathieu-Daudé --- hw/9pfs/9p.c | 5 +++++ hw/9pfs/trace-events | 2 ++ 2 files changed, 7 insertions(+) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 48fa48e720..d74302deeb 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1195,6 +1195,10 @@ static void coroutine_fn v9fs_setattr(void *opaque) goto out_nofid; } + trace_v9fs_setattr(pdu->tag, pdu->id, fid, + v9iattr.valid, v9iattr.mode, v9iattr.uid, v9iattr.gid, + v9iattr.size, v9iattr.atime_sec, v9iattr.mtime_sec); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -EINVAL; @@ -1259,6 +1263,7 @@ static void coroutine_fn v9fs_setattr(void *opaque) } } err = offset; + trace_v9fs_setattr_return(pdu->tag, pdu->id); out: put_fid(pdu, fidp); out_nofid: diff --git a/hw/9pfs/trace-events b/hw/9pfs/trace-events index 1aee350c42..881e4c4dd8 100644 --- a/hw/9pfs/trace-events +++ b/hw/9pfs/trace-events @@ -46,3 +46,5 @@ v9fs_xattrwalk_return(uint16_t tag, uint8_t id, int64_t size) "tag %d id %d size v9fs_xattrcreate(uint16_t tag, uint8_t id, int32_t fid, char* name, uint64_t size, int flags) "tag %d id %d fid %d name %s size %"PRIu64" flags %d" v9fs_readlink(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" v9fs_readlink_return(uint16_t tag, uint8_t id, char* target) "tag %d id %d name %s" +v9fs_setattr(uint16_t tag, uint8_t id, int32_t fid, int32_t valid, int32_t mode, int32_t uid, int32_t gid, int64_t size, int64_t atime_sec, int64_t mtime_sec) "tag %u id %u fid %d iattr={valid %d mode %d uid %d gid %d size %"PRId64" atime=%"PRId64" mtime=%"PRId64" }" +v9fs_setattr_return(uint16_t tag, uint8_t id) "tag %u id %u" From e4f869621203955761cf274c87d5595e9facd319 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 13 Apr 2018 16:22:13 +0200 Subject: [PATCH 0186/2380] pc-bios/s390-ccw: size_t should be unsigned "size_t" should be an unsigned type according to the C standard. Thus we should also use this convention in the s390-ccw firmware to avoid confusion. I checked the sources, and apart from one spot in libc.c, the code should all be fine with this change. Buglink: https://bugs.launchpad.net/qemu/+bug/1753437 Reviewed-by: Christian Borntraeger Reviewed-by: Halil Pasic Reviewed-by: Collin Walling Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/libc.c | 2 +- pc-bios/s390-ccw/libc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pc-bios/s390-ccw/libc.c b/pc-bios/s390-ccw/libc.c index 38ea77d7aa..a786566c4c 100644 --- a/pc-bios/s390-ccw/libc.c +++ b/pc-bios/s390-ccw/libc.c @@ -63,7 +63,7 @@ uint64_t atoui(const char *str) */ char *uitoa(uint64_t num, char *str, size_t len) { - size_t num_idx = 1; /* account for NUL */ + long num_idx = 1; /* account for NUL */ uint64_t tmp = num; IPL_assert(str != NULL, "uitoa: no space allocated to store string"); diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h index 63ece70c6b..818517ff5d 100644 --- a/pc-bios/s390-ccw/libc.h +++ b/pc-bios/s390-ccw/libc.h @@ -12,7 +12,7 @@ #ifndef S390_CCW_LIBC_H #define S390_CCW_LIBC_H -typedef long size_t; +typedef unsigned long size_t; typedef int bool; typedef unsigned char uint8_t; typedef unsigned short uint16_t; From 6df2a829dfacfbf10a78199ad4b023a7ea65d9cd Mon Sep 17 00:00:00 2001 From: Collin Walling Date: Mon, 16 Apr 2018 12:56:07 -0400 Subject: [PATCH 0187/2380] pc-bios/s390-ccw: rename MAX_TABLE_ENTRIES to MAX_BOOT_ENTRIES The MAX_TABLE_ENTRIES constant has a name that is too generic. As we want to declare a limit for boot menu entries, let's rename it to a more fitting MAX_BOOT_ENTRIES and set its value to 31 (30 boot entries and 1 default entry). Also we move it from bootmap.h to s390-ccw.h to make it available for menu.c in a later patch. Signed-off-by: Collin Walling Reviewed-by: Thomas Huth Reviewed-by: Janosch Frank Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/bootmap.c | 6 +++--- pc-bios/s390-ccw/bootmap.h | 2 -- pc-bios/s390-ccw/s390-ccw.h | 2 ++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 9287b7a70f..b767fa2afc 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -297,7 +297,7 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr, } debug_print_int("loadparm", loadparm); - IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" + IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" " maximum number of boot entries allowed"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); @@ -585,7 +585,7 @@ static void ipl_scsi(void) read_block(mbr->pt.blockno, sec, "Error reading Program Table"); IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); - while (program_table_entries <= MAX_TABLE_ENTRIES) { + while (program_table_entries < MAX_BOOT_ENTRIES) { if (!prog_table->entry[program_table_entries].scsi.blockno) { break; } @@ -600,7 +600,7 @@ static void ipl_scsi(void) } debug_print_int("loadparm", loadparm); - IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" + IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" " maximum number of boot entries allowed"); zipl_run(&prog_table->entry[loadparm].scsi); /* no return */ diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 07eb600b00..732c1112ce 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -57,8 +57,6 @@ typedef union BootMapPointer { ExtEckdBlockPtr xeckd; } __attribute__ ((packed)) BootMapPointer; -#define MAX_TABLE_ENTRIES 30 - /* aka Program Table */ typedef struct BootMapTable { uint8_t magic[4]; diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index fd18da22c6..2c9e6017b7 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -94,6 +94,8 @@ bool menu_is_enabled_zipl(void); int menu_get_enum_boot_index(int entries); bool menu_is_enabled_enum(void); +#define MAX_BOOT_ENTRIES 31 + static inline void fill_hex(char *out, unsigned char val) { const char hex[] = "0123456789abcdef"; From 074afe60d4c8167dcfaee7aca1065c6360449eaa Mon Sep 17 00:00:00 2001 From: Collin Walling Date: Mon, 16 Apr 2018 12:56:08 -0400 Subject: [PATCH 0188/2380] pc-bios/s390-ccw: fix loadparm initialization and int conversion Rename the loadparm char array in main.c to loadparm_str and increased the size by one byte to account for a null termination when converting the loadparm string to an int via atoui. We also allow the boot menu to be enabled when loadparm is set to an empty string or a series of spaces. Signed-off-by: Collin Walling Reported-by: Vasily Gorbik Reviewed-by: Thomas Huth Reviewed-by: Janosch Frank Signed-off-by: Thomas Huth --- hw/s390x/ipl.c | 4 ++++ pc-bios/s390-ccw/main.c | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index fb554ab156..150f6c0582 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -373,6 +373,10 @@ int s390_ipl_set_loadparm(uint8_t *loadparm) loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]]; } + if (i < 8) { + memset(loadparm + i, 0x40, 8 - i); /* fill with EBCDIC spaces */ + } + g_free(lp); return 0; } diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 9d9f8cf4d3..26f9adf84a 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -15,11 +15,11 @@ char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); static SubChannelId blk_schid = { .one = 1 }; IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); -static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +static char loadparm_str[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; QemuIplParameters qipl; #define LOADPARM_PROMPT "PROMPT " -#define LOADPARM_EMPTY "........" +#define LOADPARM_EMPTY " " #define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL) /* @@ -45,7 +45,7 @@ void panic(const char *string) unsigned int get_loadparm_index(void) { - return atoui(loadparm); + return atoui(loadparm_str); } static bool find_dev(Schib *schib, int dev_no) @@ -80,13 +80,13 @@ static bool find_dev(Schib *schib, int dev_no) static void menu_setup(void) { - if (memcmp(loadparm, LOADPARM_PROMPT, 8) == 0) { + if (memcmp(loadparm_str, LOADPARM_PROMPT, 8) == 0) { menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0); return; } /* If loadparm was set to any other value, then do not enable menu */ - if (memcmp(loadparm, LOADPARM_EMPTY, 8) != 0) { + if (memcmp(loadparm_str, LOADPARM_EMPTY, 8) != 0) { return; } @@ -116,8 +116,8 @@ static void virtio_setup(void) */ enable_mss_facility(); - sclp_get_loadparm_ascii(loadparm); - memcpy(ldp + 10, loadparm, 8); + sclp_get_loadparm_ascii(loadparm_str); + memcpy(ldp + 10, loadparm_str, 8); sclp_print(ldp); memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); From 7385e947fc65a44dd05abb86c874beb915c1989c Mon Sep 17 00:00:00 2001 From: Collin Walling Date: Mon, 16 Apr 2018 12:56:09 -0400 Subject: [PATCH 0189/2380] pc-bios/s390-ccw: fix non-sequential boot entries (eckd) zIPL boot menu entries can be non-sequential. Let's account for this issue for the s390 zIPL boot menu. Since this boot menu is actually an imitation and is not completely capable of everything the real zIPL menu can do, let's also print a different banner to the user. Signed-off-by: Collin Walling Reported-by: Vasily Gorbik Reviewed-by: Thomas Huth Reviewed-by: Janosch Frank Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/menu.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index 96eec81e84..aaf5d61ae6 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -158,7 +158,7 @@ static void boot_menu_prompt(bool retry) } } -static int get_boot_index(int entries) +static int get_boot_index(bool *valid_entries) { int boot_index; bool retry = false; @@ -168,7 +168,8 @@ static int get_boot_index(int entries) boot_menu_prompt(retry); boot_index = get_index(); retry = true; - } while (boot_index < 0 || boot_index >= entries); + } while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES || + !valid_entries[boot_index]); sclp_print("\nBooting entry #"); sclp_print(uitoa(boot_index, tmp, sizeof(tmp))); @@ -176,7 +177,8 @@ static int get_boot_index(int entries) return boot_index; } -static void zipl_println(const char *data, size_t len) +/* Returns the entry number that was printed */ +static int zipl_print_entry(const char *data, size_t len) { char buf[len + 2]; @@ -185,12 +187,15 @@ static void zipl_println(const char *data, size_t len) buf[len + 1] = '\0'; sclp_print(buf); + + return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf); } int menu_get_zipl_boot_index(const char *menu_data) { size_t len; - int entries; + int entry; + bool valid_entries[MAX_BOOT_ENTRIES] = {false}; uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET); uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET); @@ -202,19 +207,25 @@ int menu_get_zipl_boot_index(const char *menu_data) timeout = zipl_timeout * 1000; } - /* Print and count all menu items, including the banner */ - for (entries = 0; *menu_data; entries++) { + /* Print banner */ + sclp_print("s390-ccw zIPL Boot Menu\n\n"); + menu_data += strlen(menu_data) + 1; + + /* Print entries */ + while (*menu_data) { len = strlen(menu_data); - zipl_println(menu_data, len); + entry = zipl_print_entry(menu_data, len); menu_data += len + 1; - if (entries < 2) { + valid_entries[entry] = true; + + if (entry == 0) { sclp_print("\n"); } } sclp_print("\n"); - return get_boot_index(entries - 1); /* subtract 1 to exclude banner */ + return get_boot_index(valid_entries); } From 622b39178057289a1c8c1b5148f513e658e90ea1 Mon Sep 17 00:00:00 2001 From: Collin Walling Date: Mon, 16 Apr 2018 12:56:10 -0400 Subject: [PATCH 0190/2380] pc-bios/s390-ccw: fix non-sequential boot entries (enum) zIPL boot menu entries can be non-sequential. Let's account for this issue for the s390 enumerated boot menu. Since we can no longer print a range of available entries to the user, we have to present a list of each available entry. An example of this menu: s390-ccw Enumerated Boot Menu. [0] default [1] [2] [7] [8] [9] [11] [12] Please choose: Signed-off-by: Collin Walling Reported-by: Vasily Gorbik Reviewed-by: Thomas Huth Reviewed-by: Janosch Frank Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/bootmap.c | 12 +++++++----- pc-bios/s390-ccw/menu.c | 29 ++++++++++++++++++++--------- pc-bios/s390-ccw/s390-ccw.h | 2 +- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index b767fa2afc..e41e715624 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -565,6 +565,8 @@ static void ipl_scsi(void) int program_table_entries = 0; BootMapTable *prog_table = (void *)sec; unsigned int loadparm = get_loadparm_index(); + bool valid_entries[MAX_BOOT_ENTRIES] = {false}; + size_t i; /* Grab the MBR */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); @@ -585,18 +587,18 @@ static void ipl_scsi(void) read_block(mbr->pt.blockno, sec, "Error reading Program Table"); IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); - while (program_table_entries < MAX_BOOT_ENTRIES) { - if (!prog_table->entry[program_table_entries].scsi.blockno) { - break; + for (i = 0; i < MAX_BOOT_ENTRIES; i++) { + if (prog_table->entry[i].scsi.blockno) { + valid_entries[i] = true; + program_table_entries++; } - program_table_entries++; } debug_print_int("program table entries", program_table_entries); IPL_assert(program_table_entries != 0, "Empty Program Table"); if (menu_is_enabled_enum()) { - loadparm = menu_get_enum_boot_index(program_table_entries); + loadparm = menu_get_enum_boot_index(valid_entries); } debug_print_int("loadparm", loadparm); diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index aaf5d61ae6..82a4ae6315 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -228,19 +228,30 @@ int menu_get_zipl_boot_index(const char *menu_data) return get_boot_index(valid_entries); } - -int menu_get_enum_boot_index(int entries) +int menu_get_enum_boot_index(bool *valid_entries) { - char tmp[4]; + char tmp[3]; + int i; - sclp_print("s390x Enumerated Boot Menu.\n\n"); + sclp_print("s390-ccw Enumerated Boot Menu.\n\n"); - sclp_print(uitoa(entries, tmp, sizeof(tmp))); - sclp_print(" entries detected. Select from boot index 0 to "); - sclp_print(uitoa(entries - 1, tmp, sizeof(tmp))); - sclp_print(".\n\n"); + for (i = 0; i < MAX_BOOT_ENTRIES; i++) { + if (valid_entries[i]) { + if (i < 10) { + sclp_print(" "); + } + sclp_print("["); + sclp_print(uitoa(i, tmp, sizeof(tmp))); + sclp_print("]"); + if (i == 0) { + sclp_print(" default\n"); + } + sclp_print("\n"); + } + } - return get_boot_index(entries); + sclp_print("\n"); + return get_boot_index(valid_entries); } void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 2c9e6017b7..a1bdb4cc2a 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -91,7 +91,7 @@ void zipl_load(void); void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); int menu_get_zipl_boot_index(const char *menu_data); bool menu_is_enabled_zipl(void); -int menu_get_enum_boot_index(int entries); +int menu_get_enum_boot_index(bool *valid_entries); bool menu_is_enabled_enum(void); #define MAX_BOOT_ENTRIES 31 From 0c18822953011ec0a3038c8a5eca1803b72a213e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 17 Apr 2018 07:36:10 +0200 Subject: [PATCH 0191/2380] pc-bios/s390-ccw/net: Split up net_load() into init, load and release parts When we want to support pxelinux-style network booting later, we've got to do several TFTP transfers - and we do not want to apply for a new IP address via DHCP each time. So split up net_load into three parts: 1. net_init(), which initializes virtio-net, gets an IP address via DHCP and prints out the related information. 2. The tftp_load call is now moved directly into the main() function 3. A new net_release() function which should tear down the network stack before we are done in the firmware. This will make it easier to extend the code in the next patches. Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/netmain.c | 63 ++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index d86d46b03f..8fa9e6c945 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -128,13 +128,13 @@ static void seed_rng(uint8_t mac[]) srand(seed); } -static int tftp_load(filename_ip_t *fnip, void *buffer, int len, - unsigned int retries, int ip_vers) +static int tftp_load(filename_ip_t *fnip, void *buffer, int len) { tftp_err_t tftp_err; int rc; - rc = tftp(fnip, buffer, len, retries, &tftp_err, 1, 1428, ip_vers); + rc = tftp(fnip, buffer, len, DEFAULT_TFTP_RETRIES, &tftp_err, 1, 1428, + ip_version); if (rc > 0) { printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, @@ -199,20 +199,19 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len, return rc; } -static int net_load(char *buffer, int len) +static int net_init(filename_ip_t *fn_ip) { - filename_ip_t fn_ip; uint8_t mac[6]; int rc; - memset(&fn_ip, 0, sizeof(filename_ip_t)); + memset(fn_ip, 0, sizeof(filename_ip_t)); rc = virtio_net_init(mac); if (rc < 0) { puts("Could not initialize network device"); return -101; } - fn_ip.fd = rc; + fn_ip->fd = rc; printf(" Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -220,10 +219,10 @@ static int net_load(char *buffer, int len) set_mac_address(mac); /* init ethernet layer */ seed_rng(mac); - rc = dhcp(&fn_ip, DEFAULT_BOOT_RETRIES); + rc = dhcp(fn_ip, DEFAULT_BOOT_RETRIES); if (rc >= 0) { if (ip_version == 4) { - set_ipv4_address(fn_ip.own_ip); + set_ipv4_address(fn_ip->own_ip); } } else { puts("Could not get IP address"); @@ -232,18 +231,18 @@ static int net_load(char *buffer, int len) if (ip_version == 4) { printf(" Using IPv4 address: %d.%d.%d.%d\n", - (fn_ip.own_ip >> 24) & 0xFF, (fn_ip.own_ip >> 16) & 0xFF, - (fn_ip.own_ip >> 8) & 0xFF, fn_ip.own_ip & 0xFF); + (fn_ip->own_ip >> 24) & 0xFF, (fn_ip->own_ip >> 16) & 0xFF, + (fn_ip->own_ip >> 8) & 0xFF, fn_ip->own_ip & 0xFF); } else if (ip_version == 6) { char ip6_str[40]; - ipv6_to_str(fn_ip.own_ip6.addr, ip6_str); + ipv6_to_str(fn_ip->own_ip6.addr, ip6_str); printf(" Using IPv6 address: %s\n", ip6_str); } if (rc == -2) { printf("ARP request to TFTP server (%d.%d.%d.%d) failed\n", - (fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF, - (fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF); + (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, + (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); return -102; } if (rc == -4 || rc == -3) { @@ -251,28 +250,31 @@ static int net_load(char *buffer, int len) return -107; } + printf(" Using TFTP server: "); if (ip_version == 4) { - printf(" Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n", - fn_ip.filename, - (fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF, - (fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF); + printf("%d.%d.%d.%d\n", + (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, + (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); } else if (ip_version == 6) { char ip6_str[40]; - printf(" Requesting file \"%s\" via TFTP from ", fn_ip.filename); - ipv6_to_str(fn_ip.server_ip6.addr, ip6_str); + ipv6_to_str(fn_ip->server_ip6.addr, ip6_str); printf("%s\n", ip6_str); } - /* Do the TFTP load and print error message if necessary */ - rc = tftp_load(&fn_ip, buffer, len, DEFAULT_TFTP_RETRIES, ip_version); - - if (ip_version == 4) { - dhcp_send_release(fn_ip.fd); + if (strlen((char *)fn_ip->filename) > 0) { + printf(" Bootfile name: '%s'\n", fn_ip->filename); } return rc; } +static void net_release(filename_ip_t *fn_ip) +{ + if (ip_version == 4) { + dhcp_send_release(fn_ip->fd); + } +} + void panic(const char *string) { sclp_print(string); @@ -344,6 +346,7 @@ static void virtio_setup(void) void main(void) { + filename_ip_t fn_ip; int rc; sclp_setup(); @@ -351,7 +354,15 @@ void main(void) virtio_setup(); - rc = net_load(NULL, (long)_start); + rc = net_init(&fn_ip); + if (rc) { + panic("Network initialization failed. Halting.\n"); + } + + rc = tftp_load(&fn_ip, NULL, (long)_start); + + net_release(&fn_ip); + if (rc > 0) { sclp_print("Network loading done, starting kernel...\n"); asm volatile (" lpsw 0(%0) " : : "r"(0) : "memory"); From 9a848adf45d6732e62551decb3c0255173090767 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 20 Apr 2018 11:30:42 +0200 Subject: [PATCH 0192/2380] pc-bios/s390-ccw/net: Use diag308 to reset machine before jumping to the OS The netboot firmware so far simply jumped directly into the OS kernel after the download has been completed. This, however, bears the risk that the virtio-net device still might be active in the background and incoming packets are still placed into the buffers - which could destroy memory of the now-running Linux kernel in case it did not take over the device fast enough. Also the SCLP console is not put into a well-defined state here. We should hand over the system in a clean state when jumping into the kernel, so let's use the same mechanism as it's done in the main s390-ccw firmware and reset the machine with diag308 into a clean state before jumping into the OS kernel code. To be able to share the code with the main s390-ccw firmware, the related functions are now extracted from bootmap.c into a new file called jump2ipl.c. Since we now also set the boot device schid at address 184 for the network boot device, this patch also slightly changes the way how we detect the entry points for non-ELF binary images: The code now looks for the "S390EP" magic first and then jumps to 0x10000 in case it has been found. This is necessary for booting from network devices, since the normal kernel code (where the PSW at ddress 0 points to) tries to do a block load from the boot device. This of course fails for a virtio-net device and causes the kernel to abort with a panic-PSW silently. Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/Makefile | 4 +- pc-bios/s390-ccw/bootmap.c | 63 +------------------------ pc-bios/s390-ccw/bootmap.h | 4 -- pc-bios/s390-ccw/jump2ipl.c | 91 ++++++++++++++++++++++++++++++++++++ pc-bios/s390-ccw/netboot.mak | 3 +- pc-bios/s390-ccw/netmain.c | 11 ++++- pc-bios/s390-ccw/s390-ccw.h | 4 ++ 7 files changed, 111 insertions(+), 69 deletions(-) create mode 100644 pc-bios/s390-ccw/jump2ipl.c diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 1712c2d95d..439e3cc9c9 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -9,7 +9,9 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) .PHONY : all clean build-all -OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o libc.o menu.o +OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \ + virtio.o virtio-scsi.o virtio-blkdev.o libc.o + QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS)) QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index e41e715624..7aef65ab67 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -29,14 +29,6 @@ /* Scratch space */ static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE))); -typedef struct ResetInfo { - uint32_t ipl_mask; - uint32_t ipl_addr; - uint32_t ipl_continue; -} ResetInfo; - -static ResetInfo save; - const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; @@ -57,53 +49,6 @@ static inline bool is_iso_vd_valid(IsoVolDesc *vd) vd->type <= VOL_DESC_TYPE_PARTITION; } -static void jump_to_IPL_2(void) -{ - ResetInfo *current = 0; - - void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue; - *current = save; - ipl(); /* should not return */ -} - -static void jump_to_IPL_code(uint64_t address) -{ - /* store the subsystem information _after_ the bootmap was loaded */ - write_subsystem_identification(); - - /* prevent unknown IPL types in the guest */ - if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { - iplb.pbt = S390_IPL_TYPE_CCW; - set_iplb(&iplb); - } - - /* - * The IPL PSW is at address 0. We also must not overwrite the - * content of non-BIOS memory after we loaded the guest, so we - * save the original content and restore it in jump_to_IPL_2. - */ - ResetInfo *current = 0; - - save = *current; - current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2; - current->ipl_continue = address & 0x7fffffff; - - debug_print_int("set IPL addr to", current->ipl_continue); - - /* Ensure the guest output starts fresh */ - sclp_print("\n"); - - /* - * HACK ALERT. - * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 - * can then use r15 as its stack pointer. - */ - asm volatile("lghi 1,1\n\t" - "diag 1,1,0x308\n\t" - : : : "1", "memory"); - panic("\n! IPL returns !\n"); -} - /*********************************************************************** * IPL an ECKD DASD (CDL or LDL/CMS format) */ @@ -729,13 +674,7 @@ static void load_iso_bc_entry(IsoBcSection *load) (void *)((uint64_t)bswap16(s.load_segment)), blks_to_load); - /* Trying to get PSW at zero address */ - if (*((uint64_t *)0) & IPL_PSW_MASK) { - jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); - } - - /* Try default linux start address */ - jump_to_IPL_code(KERN_IMAGE_START); + jump_to_low_kernel(); } static uint32_t find_iso_bc(void) diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 732c1112ce..a085212077 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -353,10 +353,6 @@ static inline uint32_t iso_733_to_u32(uint64_t x) #define ISO_SECTOR_SIZE 2048 /* El Torito specifies boot image size in 512 byte blocks */ #define ET_SECTOR_SHIFT 2 -#define KERN_IMAGE_START 0x010000UL -#define PSW_MASK_64 0x0000000100000000ULL -#define PSW_MASK_32 0x0000000080000000ULL -#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) #define ISO_PRIMARY_VD_SECTOR 16 diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c new file mode 100644 index 0000000000..266f1502b9 --- /dev/null +++ b/pc-bios/s390-ccw/jump2ipl.c @@ -0,0 +1,91 @@ +/* + * QEMU s390-ccw firmware - jump to IPL code + * + * 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 "libc.h" +#include "s390-ccw.h" + +#define KERN_IMAGE_START 0x010000UL +#define PSW_MASK_64 0x0000000100000000ULL +#define PSW_MASK_32 0x0000000080000000ULL +#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) + +typedef struct ResetInfo { + uint32_t ipl_mask; + uint32_t ipl_addr; + uint32_t ipl_continue; +} ResetInfo; + +static ResetInfo save; + +static void jump_to_IPL_2(void) +{ + ResetInfo *current = 0; + + void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue; + *current = save; + ipl(); /* should not return */ +} + +void jump_to_IPL_code(uint64_t address) +{ + /* store the subsystem information _after_ the bootmap was loaded */ + write_subsystem_identification(); + + /* prevent unknown IPL types in the guest */ + if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { + iplb.pbt = S390_IPL_TYPE_CCW; + set_iplb(&iplb); + } + + /* + * The IPL PSW is at address 0. We also must not overwrite the + * content of non-BIOS memory after we loaded the guest, so we + * save the original content and restore it in jump_to_IPL_2. + */ + ResetInfo *current = 0; + + save = *current; + current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2; + current->ipl_continue = address & 0x7fffffff; + + debug_print_int("set IPL addr to", current->ipl_continue); + + /* Ensure the guest output starts fresh */ + sclp_print("\n"); + + /* + * HACK ALERT. + * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 + * can then use r15 as its stack pointer. + */ + asm volatile("lghi 1,1\n\t" + "diag 1,1,0x308\n\t" + : : : "1", "memory"); + panic("\n! IPL returns !\n"); +} + +void jump_to_low_kernel(void) +{ + /* + * If it looks like a Linux binary, i.e. there is the "S390EP" magic from + * arch/s390/kernel/head.S here, then let's jump to the well-known Linux + * kernel start address (when jumping to the PSW-at-zero address instead, + * the kernel startup code fails when we booted from a network device). + */ + if (!memcmp((char *)0x10008, "S390EP", 6)) { + jump_to_IPL_code(KERN_IMAGE_START); + } + + /* Trying to get PSW at zero address */ + if (*((uint64_t *)0) & IPL_PSW_MASK) { + jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); + } + + /* No other option left, so use the Linux kernel start address */ + jump_to_IPL_code(KERN_IMAGE_START); +} diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak index a25d238144..4f64128c6c 100644 --- a/pc-bios/s390-ccw/netboot.mak +++ b/pc-bios/s390-ccw/netboot.mak @@ -1,7 +1,8 @@ SLOF_DIR := $(SRC_PATH)/roms/SLOF -NETOBJS := start.o sclp.o virtio.o virtio-net.o netmain.o libnet.a libc.a +NETOBJS := start.o sclp.o virtio.o virtio-net.o jump2ipl.o netmain.o \ + libnet.a libc.a LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include LIBNET_INC := -I$(SLOF_DIR)/lib/libnet diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 8fa9e6c945..69a82c0d56 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -283,6 +283,15 @@ void panic(const char *string) } } +void write_subsystem_identification(void) +{ + SubChannelId *schid = (SubChannelId *) 184; + uint32_t *zeroes = (uint32_t *) 188; + + *schid = net_schid; + *zeroes = 0; +} + static bool find_net_dev(Schib *schib, int dev_no) { int i, r; @@ -365,7 +374,7 @@ void main(void) if (rc > 0) { sclp_print("Network loading done, starting kernel...\n"); - asm volatile (" lpsw 0(%0) " : : "r"(0) : "memory"); + jump_to_low_kernel(); } panic("Failed to load OS from network\n"); diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index a1bdb4cc2a..9828aa233d 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -87,6 +87,10 @@ ulong get_second(void); /* bootmap.c */ void zipl_load(void); +/* jump2ipl.c */ +void jump_to_IPL_code(uint64_t address); +void jump_to_low_kernel(void); + /* menu.c */ void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); int menu_get_zipl_boot_index(const char *menu_data); From c4942ee94271052b68125ec8c06e8c71a967aad3 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 18 Apr 2018 14:02:37 +0200 Subject: [PATCH 0193/2380] pc-bios/s390-ccw/net: Add support for .INS config files The .INS config files can normally be found on CD-ROM ISO images, so by supporting these files, it is now possible to boot directly when the TFTP server is set up with the contents of such an CD-ROM image. Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/netmain.c | 100 +++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 69a82c0d56..600024155b 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -39,8 +39,12 @@ extern char _start[]; +#define KERNEL_ADDR ((void *)0L) +#define KERNEL_MAX_SIZE ((long)_start) + char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE))); IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); +static char cfgbuf[2048]; static SubChannelId net_schid = { .one = 1 }; static int ip_version = 4; @@ -136,9 +140,15 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len) rc = tftp(fnip, buffer, len, DEFAULT_TFTP_RETRIES, &tftp_err, 1, 1428, ip_version); - if (rc > 0) { - printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, - rc / 1024); + if (rc < 0) { + /* Make sure that error messages are put into a new line */ + printf("\n "); + } + + if (rc > 1024) { + printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, rc / 1024); + } else if (rc > 0) { + printf(" TFTP: Received %s (%d Bytes)\n", fnip->filename, rc); } else if (rc == -1) { puts("unknown TFTP error"); } else if (rc == -2) { @@ -275,6 +285,83 @@ static void net_release(filename_ip_t *fn_ip) } } +/** + * Load via information from a .INS file (which can be found on CD-ROMs + * for example) + */ +static int handle_ins_cfg(filename_ip_t *fn_ip, char *cfg, int cfgsize) +{ + char *ptr; + int rc = -1, llen; + void *destaddr; + char *insbuf = cfg; + + ptr = strchr(insbuf, '\n'); + if (!ptr) { + puts("Does not seem to be a valid .INS file"); + return -1; + } + + *ptr = 0; + printf("\nParsing .INS file:\n %s\n", &insbuf[2]); + + insbuf = ptr + 1; + while (*insbuf && insbuf < cfg + cfgsize) { + ptr = strchr(insbuf, '\n'); + if (ptr) { + *ptr = 0; + } + llen = strlen(insbuf); + if (!llen) { + insbuf = ptr + 1; + continue; + } + ptr = strchr(insbuf, ' '); + if (!ptr) { + puts("Missing space separator in .INS file"); + return -1; + } + *ptr = 0; + strncpy((char *)fn_ip->filename, insbuf, sizeof(fn_ip->filename)); + destaddr = (char *)atol(ptr + 1); + rc = tftp_load(fn_ip, destaddr, (long)_start - (long)destaddr); + if (rc <= 0) { + break; + } + insbuf += llen + 1; + } + + return rc; +} + +static int net_try_direct_tftp_load(filename_ip_t *fn_ip) +{ + int rc; + void *loadaddr = (void *)0x2000; /* Load right after the low-core */ + + rc = tftp_load(fn_ip, loadaddr, KERNEL_MAX_SIZE - (long)loadaddr); + if (rc < 0) { + return rc; + } else if (rc < 8) { + printf("'%s' is too small (%i bytes only).\n", fn_ip->filename, rc); + return -1; + } + + /* Check whether it is a configuration file instead of a kernel */ + if (rc < sizeof(cfgbuf) - 1) { + memcpy(cfgbuf, loadaddr, rc); + cfgbuf[rc] = 0; /* Make sure that it is NUL-terminated */ + if (!strncmp("* ", cfgbuf, 2)) { + return handle_ins_cfg(fn_ip, cfgbuf, rc); + } + } + + /* Move kernel to right location */ + memmove(KERNEL_ADDR, loadaddr, rc); + + return rc; +} + void panic(const char *string) { sclp_print(string); @@ -356,7 +443,7 @@ static void virtio_setup(void) void main(void) { filename_ip_t fn_ip; - int rc; + int rc, fnlen; sclp_setup(); sclp_print("Network boot starting...\n"); @@ -368,7 +455,10 @@ void main(void) panic("Network initialization failed. Halting.\n"); } - rc = tftp_load(&fn_ip, NULL, (long)_start); + fnlen = strlen((char *)fn_ip.filename); + if (fnlen > 0 && fn_ip.filename[fnlen - 1] != '/') { + rc = net_try_direct_tftp_load(&fn_ip); + } net_release(&fn_ip); From 63d8b5ace31c1e1f3996fe4cd551d6d377594d5a Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Wed, 2 May 2018 14:52:21 +0200 Subject: [PATCH 0194/2380] s390-ccw: force diag 308 subcode to unsigned long We currently pass an integer as the subcode parameter. However, the upper bits of the register containing the subcode need to be 0, which is not guaranteed unless we explicitly specify the subcode to be an unsigned long value. Fixes: d046c51dad3 ("pc-bios/s390-ccw: Get device address via diag 308/6") Cc: qemu-stable@nongnu.org Signed-off-by: Cornelia Huck Acked-by: Christian Borntraeger Tested-by: Thomas Huth Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/iplb.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index 5357a36d51..ded20c834e 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -101,10 +101,11 @@ static inline bool manage_iplb(IplParameterBlock *iplb, bool store) { register unsigned long addr asm("0") = (unsigned long) iplb; register unsigned long rc asm("1") = 0; + unsigned long subcode = store ? 6 : 5; asm volatile ("diag %0,%2,0x308\n" : "+d" (addr), "+d" (rc) - : "d" (store ? 6 : 5) + : "d" (subcode) : "memory", "cc"); return rc == 0x01; } From 312185cf35b5d8454a84c337ef9aadade877fb88 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 2 May 2018 15:42:57 +0200 Subject: [PATCH 0195/2380] pc-bios/s390: Update firmware images s390-ccw.img contains fixes for the boot menu, and s390-netboot.img contains the support for .INS files and the patch for resetting the machine with diag308. Signed-off-by: Thomas Huth --- pc-bios/s390-ccw.img | Bin 30520 -> 34568 bytes pc-bios/s390-netboot.img | Bin 83856 -> 87872 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index fdd6809c707274be47b21fac102c41942f1379d7..450a076dc0ad9f2babc7f2e3005ea05a1082a808 100644 GIT binary patch literal 34568 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 diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img index 31f3d141cde6320d696e6945046c75d140488df3..ef561efd2e222e03138705e9cf30d9e125a7970e 100644 GIT binary patch literal 87872 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 literal 83856 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 From a99856cd7f30b8c64cfaf97791dc5d462dabad6e Mon Sep 17 00:00:00 2001 From: Christophe Lyon Date: Mon, 30 Apr 2018 10:03:41 +0200 Subject: [PATCH 0196/2380] Remove CONFIG_USE_FDPIC. We want to avoid code disabled by default, because it ends up less tested. This patch removes all instances of #ifdef CONFIG_USE_FDPIC, most of which can be safely kept. For the ones that should be conditionally executed, we define elf_is_fdpic(). Without this patch, defining CONFIG_USE_FDPIC would prevent QEMU from building precisely because elf_is_fdpic is not defined. Signed-off-by: Christophe Lyon Reviewed-by: Peter Maydell Message-Id: <20180430080404.7323-2-christophe.lyon@st.com> Signed-off-by: Laurent Vivier --- linux-user/elfload.c | 18 +++++++----------- linux-user/qemu.h | 4 ++-- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index c77ed1bb01..bbe93b03b6 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1681,7 +1681,12 @@ static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot) } } -#ifdef CONFIG_USE_FDPIC +/* Default implementation, always false. */ +static int elf_is_fdpic(struct elfhdr *exec) +{ + return 0; +} + static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong sp) { uint16_t n; @@ -1706,7 +1711,6 @@ static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong s return sp; } -#endif static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, struct elfhdr *exec, @@ -1725,7 +1729,6 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, sp = p; -#ifdef CONFIG_USE_FDPIC /* Needs to be before we load the env/argc/... */ if (elf_is_fdpic(exec)) { /* Need 4 byte alignment for these structs */ @@ -1737,7 +1740,6 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, sp = loader_build_fdpic_loadmap(interp_info, sp); } } -#endif u_platform = 0; k_platform = ELF_PLATFORM; @@ -2153,10 +2155,8 @@ static void load_elf_image(const char *image_name, int image_fd, } bswap_phdr(phdr, ehdr->e_phnum); -#ifdef CONFIG_USE_FDPIC info->nsegs = 0; info->pt_dynamic_addr = 0; -#endif mmap_lock(); @@ -2173,9 +2173,7 @@ static void load_elf_image(const char *image_name, int image_fd, if (a > hiaddr) { hiaddr = a; } -#ifdef CONFIG_USE_FDPIC ++info->nsegs; -#endif } } @@ -2200,8 +2198,7 @@ static void load_elf_image(const char *image_name, int image_fd, } load_bias = load_addr - loaddr; -#ifdef CONFIG_USE_FDPIC - { + if (elf_is_fdpic(ehdr)) { struct elf32_fdpic_loadseg *loadsegs = info->loadsegs = g_malloc(sizeof(*loadsegs) * info->nsegs); @@ -2219,7 +2216,6 @@ static void load_elf_image(const char *image_name, int image_fd, } } } -#endif info->load_bias = load_bias; info->load_addr = load_addr; diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 192a0d2fef..da3b51724c 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -51,13 +51,13 @@ struct image_info { abi_ulong file_string; uint32_t elf_flags; int personality; -#ifdef CONFIG_USE_FDPIC + + /* The fields below are used in FDPIC mode. */ abi_ulong loadmap_addr; uint16_t nsegs; void *loadsegs; abi_ulong pt_dynamic_addr; struct image_info *other_info; -#endif }; #ifdef TARGET_I386 From cf58affecc67e1b22bdbd3dc90e19ad2d4b1ce3d Mon Sep 17 00:00:00 2001 From: Christophe Lyon Date: Mon, 30 Apr 2018 10:03:42 +0200 Subject: [PATCH 0197/2380] linux-user: ARM-FDPIC: Identify ARM FDPIC binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define an ARM-specific version of elf_is_fdpic: FDPIC ELF objects are identified with e_ident[EI_OSABI] == ELFOSABI_ARM_FDPIC. Co-Authored-By: Mickaël Guêné Signed-off-by: Christophe Lyon Reviewed-by: Peter Maydell Message-Id: <20180430080404.7323-3-christophe.lyon@st.com> Signed-off-by: Laurent Vivier --- include/elf.h | 1 + linux-user/elfload.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/elf.h b/include/elf.h index c0dc9bb5fd..934dbbd6b3 100644 --- a/include/elf.h +++ b/include/elf.h @@ -1483,6 +1483,7 @@ typedef struct elf64_shdr { #define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ #define ELFOSABI_MODESTO 11 /* Novell Modesto. */ #define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM_FDPIC 65 /* ARM FDPIC */ #define ELFOSABI_ARM 97 /* ARM */ #define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ diff --git a/linux-user/elfload.c b/linux-user/elfload.c index bbe93b03b6..76d7718b3d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1681,11 +1681,18 @@ static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot) } } +#ifdef TARGET_ARM +static int elf_is_fdpic(struct elfhdr *exec) +{ + return exec->e_ident[EI_OSABI] == ELFOSABI_ARM_FDPIC; +} +#else /* Default implementation, always false. */ static int elf_is_fdpic(struct elfhdr *exec) { return 0; } +#endif static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong sp) { From 3cb10cfafd83b9d7b3867b7deae29b8da3b5fe39 Mon Sep 17 00:00:00 2001 From: Christophe Lyon Date: Mon, 30 Apr 2018 10:03:43 +0200 Subject: [PATCH 0198/2380] linux-user: ARM-FDPIC: Add support of FDPIC for ARM. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add FDPIC info into image_info structure since interpreter info is on stack and needs to be saved to be accessed later on. Co-Authored-By: Mickaël Guêné Signed-off-by: Christophe Lyon Reviewed-by: Peter Maydell Message-Id: <20180430080404.7323-4-christophe.lyon@st.com> Signed-off-by: Laurent Vivier --- linux-user/elfload.c | 29 +++++++++++++++++++++++++++++ linux-user/qemu.h | 9 +++++++++ 2 files changed, 38 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 76d7718b3d..36d52194bc 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -78,6 +78,11 @@ enum { */ #define personality(pers) (pers & PER_MASK) +int info_is_fdpic(struct image_info *info) +{ + return info->personality == PER_LINUX_FDPIC; +} + /* this flag is uneffective under linux too, should be deleted */ #ifndef MAP_DENYWRITE #define MAP_DENYWRITE 0 @@ -287,6 +292,25 @@ static inline void init_thread(struct target_pt_regs *regs, /* For uClinux PIC binaries. */ /* XXX: Linux does this only on ARM with no MMU (do we care ?) */ regs->uregs[10] = infop->start_data; + + /* Support ARM FDPIC. */ + if (info_is_fdpic(infop)) { + /* As described in the ABI document, r7 points to the loadmap info + * prepared by the kernel. If an interpreter is needed, r8 points + * to the interpreter loadmap and r9 points to the interpreter + * PT_DYNAMIC info. If no interpreter is needed, r8 is zero, and + * r9 points to the main program PT_DYNAMIC info. + */ + regs->uregs[7] = infop->loadmap_addr; + if (infop->interpreter_loadmap_addr) { + /* Executable is dynamically loaded. */ + regs->uregs[8] = infop->interpreter_loadmap_addr; + regs->uregs[9] = infop->interpreter_pt_dynamic_addr; + } else { + regs->uregs[8] = 0; + regs->uregs[9] = infop->pt_dynamic_addr; + } + } } #define ELF_NREG 18 @@ -1745,6 +1769,11 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, if (interp_info) { interp_info->other_info = info; sp = loader_build_fdpic_loadmap(interp_info, sp); + info->interpreter_loadmap_addr = interp_info->loadmap_addr; + info->interpreter_pt_dynamic_addr = interp_info->pt_dynamic_addr; + } else { + info->interpreter_loadmap_addr = 0; + info->interpreter_pt_dynamic_addr = 0; } } diff --git a/linux-user/qemu.h b/linux-user/qemu.h index da3b51724c..c55c8e294b 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -57,6 +57,8 @@ struct image_info { uint16_t nsegs; void *loadsegs; abi_ulong pt_dynamic_addr; + abi_ulong interpreter_loadmap_addr; + abi_ulong interpreter_pt_dynamic_addr; struct image_info *other_info; }; @@ -183,6 +185,13 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, struct target_pt_regs * regs, struct image_info *infop, struct linux_binprm *); +/* Returns true if the image uses the FDPIC ABI. If this is the case, + * we have to provide some information (loadmap, pt_dynamic_info) such + * that the program can be relocated adequately. This is also useful + * when handling signals. + */ +int info_is_fdpic(struct image_info *info); + uint32_t get_elf_eflags(int fd); int load_elf_binary(struct linux_binprm *bprm, struct image_info *info); int load_flt_binary(struct linux_binprm *bprm, struct image_info *info); From e8fa72957419c11984608062c7dcb204a6003a06 Mon Sep 17 00:00:00 2001 From: Christophe Lyon Date: Mon, 30 Apr 2018 10:03:44 +0200 Subject: [PATCH 0199/2380] linux-user: ARM-FDPIC: Add support for signals for FDPIC targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FDPIC restorer needs to deal with a function descriptor, hence we have to extend 'retcode' such that it can hold the instructions needed to perform this. The restorer sequence uses the same thumbness as the exception handler (mainly to support Thumb-only architectures). Co-Authored-By: Mickaël Guêné Signed-off-by: Christophe Lyon Reviewed-by: Peter Maydell Message-Id: <20180430080404.7323-5-christophe.lyon@st.com> [lv: moved the change to linux-user/arm/signal.c] Signed-off-by: Laurent Vivier --- linux-user/arm/signal.c | 105 ++++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 16 deletions(-) diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index 0c1ec53025..eb72be5dd0 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -102,13 +102,13 @@ struct sigframe_v1 { struct target_sigcontext sc; abi_ulong extramask[TARGET_NSIG_WORDS-1]; - abi_ulong retcode; + abi_ulong retcode[4]; }; struct sigframe_v2 { struct target_ucontext_v2 uc; - abi_ulong retcode; + abi_ulong retcode[4]; }; struct rt_sigframe_v1 @@ -117,14 +117,14 @@ struct rt_sigframe_v1 abi_ulong puc; struct target_siginfo info; struct target_ucontext_v1 uc; - abi_ulong retcode; + abi_ulong retcode[4]; }; struct rt_sigframe_v2 { struct target_siginfo info; struct target_ucontext_v2 uc; - abi_ulong retcode; + abi_ulong retcode[4]; }; #define TARGET_CONFIG_CPU_32 1 @@ -147,6 +147,21 @@ static const abi_ulong retcodes[4] = { SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN }; +/* + * Stub needed to make sure the FD register (r9) contains the right + * value. + */ +static const unsigned long sigreturn_fdpic_codes[3] = { + 0xe59fc004, /* ldr r12, [pc, #4] to read function descriptor */ + 0xe59c9004, /* ldr r9, [r12, #4] to setup GOT */ + 0xe59cf000 /* ldr pc, [r12] to jump into restorer */ +}; + +static const unsigned long sigreturn_fdpic_thumb_codes[3] = { + 0xc008f8df, /* ldr r12, [pc, #8] to read function descriptor */ + 0x9004f8dc, /* ldr r9, [r12, #4] to setup GOT */ + 0xf000f8dc /* ldr pc, [r12] to jump into restorer */ +}; static inline int valid_user_regs(CPUARMState *regs) { @@ -200,13 +215,33 @@ get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) return (sp - framesize) & ~7; } -static void +static int setup_return(CPUARMState *env, struct target_sigaction *ka, abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr) { - abi_ulong handler = ka->_sa_handler; + abi_ulong handler = 0; + abi_ulong handler_fdpic_GOT = 0; abi_ulong retcode; - int thumb = handler & 1; + + int thumb; + int is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info); + + if (is_fdpic) { + /* In FDPIC mode, ka->_sa_handler points to a function + * descriptor (FD). The first word contains the address of the + * handler. The second word contains the value of the PIC + * register (r9). */ + abi_ulong funcdesc_ptr = ka->_sa_handler; + if (get_user_ual(handler, funcdesc_ptr) + || get_user_ual(handler_fdpic_GOT, funcdesc_ptr + 4)) { + return 1; + } + } else { + handler = ka->_sa_handler; + } + + thumb = handler & 1; + uint32_t cpsr = cpsr_read(env); cpsr &= ~CPSR_IT; @@ -217,7 +252,28 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, } if (ka->sa_flags & TARGET_SA_RESTORER) { - retcode = ka->sa_restorer; + if (is_fdpic) { + /* For FDPIC we ensure that the restorer is called with a + * correct r9 value. For that we need to write code on + * the stack that sets r9 and jumps back to restorer + * value. + */ + if (thumb) { + __put_user(sigreturn_fdpic_thumb_codes[0], rc); + __put_user(sigreturn_fdpic_thumb_codes[1], rc + 1); + __put_user(sigreturn_fdpic_thumb_codes[2], rc + 2); + __put_user((abi_ulong)ka->sa_restorer, rc + 3); + } else { + __put_user(sigreturn_fdpic_codes[0], rc); + __put_user(sigreturn_fdpic_codes[1], rc + 1); + __put_user(sigreturn_fdpic_codes[2], rc + 2); + __put_user((abi_ulong)ka->sa_restorer, rc + 3); + } + + retcode = rc_addr + thumb; + } else { + retcode = ka->sa_restorer; + } } else { unsigned int idx = thumb; @@ -231,10 +287,15 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, } env->regs[0] = usig; + if (is_fdpic) { + env->regs[9] = handler_fdpic_GOT; + } env->regs[13] = frame_addr; env->regs[14] = retcode; env->regs[15] = handler & (thumb ? ~1 : ~3); cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr); + + return 0; } static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) @@ -327,12 +388,15 @@ static void setup_frame_v1(int usig, struct target_sigaction *ka, __put_user(set->sig[i], &frame->extramask[i - 1]); } - setup_return(regs, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct sigframe_v1, retcode)); + if (setup_return(regs, ka, frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct sigframe_v1, retcode))) { + goto sigsegv; + } unlock_user_struct(frame, frame_addr, 1); return; sigsegv: + unlock_user_struct(frame, frame_addr, 1); force_sigsegv(usig); } @@ -349,12 +413,15 @@ static void setup_frame_v2(int usig, struct target_sigaction *ka, setup_sigframe_v2(&frame->uc, set, regs); - setup_return(regs, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct sigframe_v2, retcode)); + if (setup_return(regs, ka, frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct sigframe_v2, retcode))) { + goto sigsegv; + } unlock_user_struct(frame, frame_addr, 1); return; sigsegv: + unlock_user_struct(frame, frame_addr, 1); force_sigsegv(usig); } @@ -404,8 +471,10 @@ static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } - setup_return(env, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe_v1, retcode)); + if (setup_return(env, ka, frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct rt_sigframe_v1, retcode))) { + goto sigsegv; + } env->regs[1] = info_addr; env->regs[2] = uc_addr; @@ -413,6 +482,7 @@ static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, unlock_user_struct(frame, frame_addr, 1); return; sigsegv: + unlock_user_struct(frame, frame_addr, 1); force_sigsegv(usig); } @@ -435,8 +505,10 @@ static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, setup_sigframe_v2(&frame->uc, set, env); - setup_return(env, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe_v2, retcode)); + if (setup_return(env, ka, frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct rt_sigframe_v2, retcode))) { + goto sigsegv; + } env->regs[1] = info_addr; env->regs[2] = uc_addr; @@ -444,6 +516,7 @@ static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, unlock_user_struct(frame, frame_addr, 1); return; sigsegv: + unlock_user_struct(frame, frame_addr, 1); force_sigsegv(usig); } From 465e237bf7cb6a9d8f9f137508125a14efcce1d6 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 11 Apr 2018 21:23:47 +0200 Subject: [PATCH 0200/2380] linux-user: introduce target_sigsp() and target_save_altstack() Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180411192347.30228-1-laurent@vivier.eu> --- linux-user/aarch64/signal.c | 13 ++----------- linux-user/alpha/signal.c | 17 ++++++----------- linux-user/arm/signal.c | 17 ++++------------- linux-user/hppa/signal.c | 14 ++++---------- linux-user/i386/signal.c | 12 +++--------- linux-user/m68k/signal.c | 15 +++------------ linux-user/microblaze/signal.c | 4 +--- linux-user/mips/signal.c | 15 ++------------- linux-user/nios2/signal.c | 21 ++------------------- linux-user/openrisc/signal.c | 14 +++----------- linux-user/ppc/signal.c | 15 ++------------- linux-user/riscv/signal.c | 28 +++++++++------------------- linux-user/s390x/signal.c | 12 +++--------- linux-user/sh4/signal.c | 11 ++--------- linux-user/signal-common.h | 15 ++++----------- linux-user/signal.c | 32 ++++++++++++++++++++++++++++++++ linux-user/sparc/signal.c | 28 +++++++++++++++++++--------- linux-user/tilegx/signal.c | 13 +++---------- linux-user/xtensa/signal.c | 15 ++++----------- 19 files changed, 108 insertions(+), 203 deletions(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index d90e10a113..f95dc61dfb 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -120,9 +120,7 @@ static void target_setup_general_frame(struct target_rt_sigframe *sf, __put_user(0, &sf->uc.tuc_flags); __put_user(0, &sf->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &sf->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->xregs[31]), &sf->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &sf->uc.tuc_stack.ss_size); + target_save_altstack(&sf->uc.tuc_stack, env); for (i = 0; i < 31; i++) { __put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); @@ -372,14 +370,7 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, { abi_ulong sp; - sp = env->xregs[31]; - - /* - * This is the X/Open sanctioned signal stack switching. - */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } + sp = target_sigsp(get_sp_from_cpustate(env), ka); sp = (sp - size) & ~15; diff --git a/linux-user/alpha/signal.c b/linux-user/alpha/signal.c index a8c718f2c6..f24de02c6f 100644 --- a/linux-user/alpha/signal.c +++ b/linux-user/alpha/signal.c @@ -117,12 +117,10 @@ static inline abi_ulong get_sigframe(struct target_sigaction *sa, CPUAlphaState *env, unsigned long framesize) { - abi_ulong sp = env->ir[IR_SP]; + abi_ulong sp; + + sp = target_sigsp(get_sp_from_cpustate(env), sa); - /* This is the X/Open sanctioned signal stack switching. */ - if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } return (sp - framesize) & -32; } @@ -187,12 +185,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); __put_user(set->sig[0], &frame->uc.tuc_osf_sigmask); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->ir[IR_SP]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); + + target_save_altstack(&frame->uc.tuc_stack, env); + setup_sigcontext(&frame->uc.tuc_mcontext, env, frame_addr, set); for (i = 0; i < TARGET_NSIG_WORDS; ++i) { __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index eb72be5dd0..59b5b65ed1 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -201,14 +201,9 @@ setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ static inline abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) { - unsigned long sp = regs->regs[13]; + unsigned long sp; - /* - * This is the X/Open sanctioned signal stack switching. - */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } + sp = target_sigsp(get_sp_from_cpustate(regs), ka); /* * ATPCS B01 mandates 8-byte alignment */ @@ -346,9 +341,7 @@ static void setup_sigframe_v2(struct target_ucontext_v2 *uc, memset(uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext)); memset(&stack, 0, sizeof(stack)); - __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp); - __put_user(target_sigaltstack_used.ss_size, &stack.ss_size); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags); + target_save_altstack(&stack, env); memcpy(&uc->tuc_stack, &stack, sizeof(stack)); setup_sigcontext(&uc->tuc_mcontext, env, set->sig[0]); @@ -461,9 +454,7 @@ static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext)); memset(&stack, 0, sizeof(stack)); - __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp); - __put_user(target_sigaltstack_used.ss_size, &stack.ss_size); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags); + target_save_altstack(&stack, env); memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack)); setup_sigcontext(&frame->uc.tuc_mcontext, env, set->sig[0]); diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c index 585af3a37f..6e7a295aee 100644 --- a/linux-user/hppa/signal.c +++ b/linux-user/hppa/signal.c @@ -113,11 +113,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, struct target_rt_sigframe *frame; int i; - sp = env->gr[30]; - if (ka->sa_flags & TARGET_SA_ONSTACK) { - if (sas_ss_flags(sp) == 0) { - sp = (target_sigaltstack_used.ss_sp + 0x7f) & ~0x3f; - } + sp = get_sp_from_cpustate(env); + if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { + sp = (target_sigaltstack_used.ss_sp + 0x7f) & ~0x3f; } frame_addr = QEMU_ALIGN_UP(sp, 64); sp = frame_addr + PARISC_RT_SIGFRAME_SIZE32; @@ -132,11 +130,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, frame->uc.tuc_flags = 0; frame->uc.tuc_link = 0; - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); + target_save_altstack(&frame->uc.tuc_stack, env); for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 4a190e6435..e9a23a2dec 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -283,16 +283,14 @@ get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size) unsigned long esp; /* Default to using normal stack */ - esp = env->regs[R_ESP]; + esp = get_sp_from_cpustate(env); #ifdef TARGET_X86_64 esp -= 128; /* this is the redzone */ #endif /* This is the X/Open sanctioned signal stack switching. */ if (ka->sa_flags & TARGET_SA_ONSTACK) { - if (sas_ss_flags(esp) == 0) { - esp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } + esp = target_sigsp(esp, ka); } else { #ifndef TARGET_X86_64 /* This is the legacy signal stack switching. */ @@ -404,11 +402,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); + target_save_altstack(&frame->uc.tuc_stack, env); setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env, set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate)); diff --git a/linux-user/m68k/signal.c b/linux-user/m68k/signal.c index fc72468a81..5dd8bb5f99 100644 --- a/linux-user/m68k/signal.c +++ b/linux-user/m68k/signal.c @@ -117,14 +117,10 @@ static inline abi_ulong get_sigframe(struct target_sigaction *ka, CPUM68KState *regs, size_t frame_size) { - unsigned long sp; + abi_ulong sp; - sp = regs->aregs[7]; + sp = target_sigsp(get_sp_from_cpustate(regs), ka); - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } return ((sp - frame_size) & -8UL); } @@ -318,12 +314,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->aregs[7]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); + target_save_altstack(&frame->uc.tuc_stack, env); err |= target_rt_setup_ucontext(&frame->uc, env); if (err) diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index 5572baa7dc..fada0f1495 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -133,9 +133,7 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, { abi_ulong sp = env->regs[1]; - if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !on_sig_stack(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } + sp = target_sigsp(sp, ka); return ((sp - frame_size) & -8UL); } diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c index adeb5a4241..ed9849c7f6 100644 --- a/linux-user/mips/signal.c +++ b/linux-user/mips/signal.c @@ -179,20 +179,12 @@ get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size) { unsigned long sp; - /* Default to using normal stack */ - sp = regs->active_tc.gpr[29]; - /* * FPU emulator may have its own trampoline active just * above the user stack, 16-bytes before the next lowest * 16 byte boundary. Try to avoid trashing it. */ - sp -= 32; - - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } + sp = target_sigsp(get_sp_from_cpustate(regs) - 32, ka); return (sp - frame_size) & ~7; } @@ -323,10 +315,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(0, &frame->rs_uc.tuc_flags); __put_user(0, &frame->rs_uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->rs_uc.tuc_stack.ss_sp); - __put_user(target_sigaltstack_used.ss_size, &frame->rs_uc.tuc_stack.ss_size); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->rs_uc.tuc_stack.ss_flags); + target_save_altstack(&frame->rs_uc.tuc_stack, env); setup_sigcontext(env, &frame->rs_uc.tuc_mcontext); diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c index 816eed90f1..9a0b36e5ad 100644 --- a/linux-user/nios2/signal.c +++ b/linux-user/nios2/signal.c @@ -42,18 +42,6 @@ struct target_rt_sigframe { struct target_ucontext uc; }; -static unsigned long sigsp(unsigned long sp, struct target_sigaction *ka) -{ - if (unlikely((ka->sa_flags & SA_ONSTACK)) && !sas_ss_flags(sp)) { -#ifdef CONFIG_STACK_GROWSUP - return target_sigaltstack_used.ss_sp; -#else - return target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; -#endif - } - return sp; -} - static int rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env) { unsigned long *gregs = uc->tuc_mcontext.gregs; @@ -158,11 +146,8 @@ static void *get_sigframe(struct target_sigaction *ka, CPUNios2State *env, { unsigned long usp; - /* Default to using normal stack. */ - usp = env->regs[R_SP]; - /* This is the X/Open sanctioned signal stack switching. */ - usp = sigsp(usp, ka); + usp = target_sigsp(get_sp_from_cpustate(env), ka); /* Verify, is it 32 or 64 bit aligned */ return (void *)((usp - frame_size) & -8UL); @@ -185,9 +170,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->regs[R_SP]), &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); + target_save_altstack(&frame->uc.tuc_stack, env); err |= rt_setup_ucontext(&frame->uc, env); for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user((abi_ulong)set->sig[i], diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c index 0276808b59..ecf2897ccd 100644 --- a/linux-user/openrisc/signal.c +++ b/linux-user/openrisc/signal.c @@ -124,14 +124,11 @@ static inline abi_ulong get_sigframe(struct target_sigaction *ka, CPUOpenRISCState *regs, size_t frame_size) { - unsigned long sp = cpu_get_gpr(regs, 1); + unsigned long sp = get_sp_from_cpustate(regs); int onsigstack = on_sig_stack(sp); /* redzone */ - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } + sp = target_sigsp(sp, ka); sp = align_sigframe(sp - frame_size); @@ -175,12 +172,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, /*err |= __clear_user(&frame->uc, offsetof(ucontext_t, uc_mcontext));*/ __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(cpu_get_gpr(env, 1)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); + target_save_altstack(&frame->uc.tuc_stack, env); setup_sigcontext(&frame->sc, env, set->sig[0]); /*err |= copy_to_user(frame->uc.tuc_sigmask, set, sizeof(*set));*/ diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index 15148d54a9..cacc9afb5a 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -217,13 +217,7 @@ static target_ulong get_sigframe(struct target_sigaction *ka, { target_ulong oldsp; - oldsp = env->gpr[1]; - - if ((ka->sa_flags & TARGET_SA_ONSTACK) && - (sas_ss_flags(oldsp) == 0)) { - oldsp = (target_sigaltstack_used.ss_sp - + target_sigaltstack_used.ss_size); - } + oldsp = target_sigsp(get_sp_from_cpustate(env), ka); return (oldsp - frame_size) & ~0xFUL; } @@ -515,12 +509,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(0, &rt_sf->uc.tuc_flags); __put_user(0, &rt_sf->uc.tuc_link); - __put_user((target_ulong)target_sigaltstack_used.ss_sp, - &rt_sf->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->gpr[1]), - &rt_sf->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &rt_sf->uc.tuc_stack.ss_size); + target_save_altstack(&rt_sf->uc.tuc_stack, env); #if !defined(TARGET_PPC64) __put_user(h2g (&rt_sf->uc.tuc_mcontext), &rt_sf->uc.tuc_regs); diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index 718f3a5679..ef599e319a 100644 --- a/linux-user/riscv/signal.c +++ b/linux-user/riscv/signal.c @@ -54,24 +54,20 @@ struct target_rt_sigframe { static abi_ulong get_sigframe(struct target_sigaction *ka, CPURISCVState *regs, size_t framesize) { - abi_ulong sp = regs->gpr[xSP]; - int onsigstack = on_sig_stack(sp); - - /* redzone */ - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - sp -= framesize; - sp &= ~3UL; /* align sp on 4-byte boundary */ + abi_ulong sp = get_sp_from_cpustate(regs); /* If we are on the alternate signal stack and would overflow it, don't. Return an always-bogus address instead so we will die with SIGSEGV. */ - if (onsigstack && !likely(on_sig_stack(sp))) { + if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize))) { return -1L; } + /* This is the X/Open sanctioned signal stack switching. */ + sp = target_sigsp(sp, ka) - framesize; + + /* XXX: kernel aligns with 0xf ? */ + sp &= ~3UL; /* align sp on 4-byte boundary */ + return sp; } @@ -95,16 +91,10 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env) static void setup_ucontext(struct target_ucontext *uc, CPURISCVState *env, target_sigset_t *set) { - abi_ulong ss_sp = (target_ulong)target_sigaltstack_used.ss_sp; - abi_ulong ss_flags = sas_ss_flags(env->gpr[xSP]); - abi_ulong ss_size = target_sigaltstack_used.ss_size; - __put_user(0, &(uc->uc_flags)); __put_user(0, &(uc->uc_link)); - __put_user(ss_sp, &(uc->uc_stack.ss_sp)); - __put_user(ss_flags, &(uc->uc_stack.ss_flags)); - __put_user(ss_size, &(uc->uc_stack.ss_size)); + target_save_altstack(&uc->uc_stack, env); int i; for (i = 0; i < TARGET_NSIG_WORDS; i++) { diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index a204a85e4a..e35cbe6870 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -86,14 +86,11 @@ get_sigframe(struct target_sigaction *ka, CPUS390XState *env, size_t frame_size) abi_ulong sp; /* Default to using normal stack */ - sp = env->regs[15]; + sp = get_sp_from_cpustate(env); /* This is the X/Open sanctioned signal stack switching. */ if (ka->sa_flags & TARGET_SA_ONSTACK) { - if (!sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + - target_sigaltstack_used.ss_size; - } + sp = target_sigsp(sp, ka); } /* This is the legacy signal stack switching. */ @@ -205,10 +202,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); + target_save_altstack(&frame->uc.tuc_stack, env); save_sigregs(env, &frame->uc.tuc_mcontext); for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user((abi_ulong)set->sig[i], diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index 5ce182aff7..2a5378e16e 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -78,9 +78,7 @@ struct target_rt_sigframe static abi_ulong get_sigframe(struct target_sigaction *ka, unsigned long sp, size_t frame_size) { - if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } + sp = target_sigsp(sp, ka); return (sp - frame_size) & -8ul; } @@ -238,12 +236,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); __put_user(0, (unsigned long *)&frame->uc.tuc_link); - __put_user((unsigned long)target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(regs->gregs[15]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); + target_save_altstack(&frame->uc.tuc_stack, regs); setup_sigcontext(&frame->uc.tuc_mcontext, regs, set->sig[0]); for(i = 0; i < TARGET_NSIG_WORDS; i++) { diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h index fbb8d4365c..51030a9306 100644 --- a/linux-user/signal-common.h +++ b/linux-user/signal-common.h @@ -21,17 +21,10 @@ #define SIGNAL_COMMON_H extern struct target_sigaltstack target_sigaltstack_used; -static inline int on_sig_stack(unsigned long sp) -{ - return (sp - target_sigaltstack_used.ss_sp - < target_sigaltstack_used.ss_size); -} - -static inline int sas_ss_flags(unsigned long sp) -{ - return (target_sigaltstack_used.ss_size == 0 ? SS_DISABLE - : on_sig_stack(sp) ? SS_ONSTACK : 0); -} +int on_sig_stack(unsigned long sp); +int sas_ss_flags(unsigned long sp); +abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka); +void target_save_altstack(target_stack_t *uss, CPUArchState *env); static inline void target_sigemptyset(target_sigset_t *set) { diff --git a/linux-user/signal.c b/linux-user/signal.c index a3022c2f04..01de433e3a 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -249,6 +249,38 @@ void set_sigmask(const sigset_t *set) } #endif +/* sigaltstack management */ + +int on_sig_stack(unsigned long sp) +{ + return (sp - target_sigaltstack_used.ss_sp + < target_sigaltstack_used.ss_size); +} + +int sas_ss_flags(unsigned long sp) +{ + return (target_sigaltstack_used.ss_size == 0 ? SS_DISABLE + : on_sig_stack(sp) ? SS_ONSTACK : 0); +} + +abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka) +{ + /* + * This is the X/Open sanctioned signal stack switching. + */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { + return target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + return sp; +} + +void target_save_altstack(target_stack_t *uss, CPUArchState *env) +{ + __put_user(target_sigaltstack_used.ss_sp, &uss->ss_sp); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &uss->ss_flags); + __put_user(target_sigaltstack_used.ss_size, &uss->ss_size); +} + /* siginfo conversion */ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index c823e61cee..45e922f328 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -123,18 +123,28 @@ static inline abi_ulong get_sigframe(struct target_sigaction *sa, CPUSPARCState *env, unsigned long framesize) { - abi_ulong sp; + abi_ulong sp = get_sp_from_cpustate(env); - sp = env->regwptr[UREG_FP]; + /* + * If we are on the alternate signal stack and would overflow it, don't. + * Return an always-bogus address instead so we will die with SIGSEGV. + */ + if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize))) { + return -1; + } /* This is the X/Open sanctioned signal stack switching. */ - if (sa->sa_flags & TARGET_SA_ONSTACK) { - if (!on_sig_stack(sp) - && !((target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size) & 7)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - } - return sp - framesize; + sp = target_sigsp(sp, sa) - framesize; + + /* Always align the stack frame. This handles two cases. First, + * sigaltstack need not be mindful of platform specific stack + * alignment. Second, if we took this signal because the stack + * is not aligned properly, we'd like to take the signal cleanly + * and report that. + */ + sp &= ~15UL; + + return sp; } static int diff --git a/linux-user/tilegx/signal.c b/linux-user/tilegx/signal.c index 8f54f54f95..d0ed3de569 100644 --- a/linux-user/tilegx/signal.c +++ b/linux-user/tilegx/signal.c @@ -86,17 +86,13 @@ static void restore_sigcontext(CPUTLGState *env, struct target_sigcontext *sc) static abi_ulong get_sigframe(struct target_sigaction *ka, CPUArchState *env, size_t frame_size) { - unsigned long sp = env->regs[TILEGX_R_SP]; + unsigned long sp = get_sp_from_cpustate(env); if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size))) { return -1UL; } - if ((ka->sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - sp -= frame_size; + sp = target_sigsp(sp, ka) - frame_size; sp &= -16UL; return sp; } @@ -127,10 +123,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->regs[TILEGX_R_SP]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); + target_save_altstack(&frame->uc.tuc_stack, env); setup_sigcontext(&frame->uc.tuc_mcontext, env, info->si_signo); if (ka->sa_flags & TARGET_SA_RESTORER) { diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c index 1e98910c1b..3e483efc61 100644 --- a/linux-user/xtensa/signal.c +++ b/linux-user/xtensa/signal.c @@ -55,12 +55,10 @@ static abi_ulong get_sigframe(struct target_sigaction *sa, CPUXtensaState *env, unsigned long framesize) { - abi_ulong sp = env->regs[1]; + abi_ulong sp; + + sp = target_sigsp(get_sp_from_cpustate(env), sa); - /* This is the X/Open sanctioned signal stack switching. */ - if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } return (sp - framesize) & -16; } @@ -152,12 +150,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->regs[1]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); + target_save_altstack(&frame->uc.tuc_stack, env); if (!setup_sigcontext(frame, env)) { unlock_user_struct(frame, frame_addr, 0); goto give_sigsegv; From 7f254c5cb80bc478794a4c3d7fe5d503b033be13 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 2 May 2018 23:57:30 +0200 Subject: [PATCH 0201/2380] linux-user: remove useless padding in flock64 structure Since commit 8efb2ed5ec ("linux-user: Correct signedness of target_flock l_start and l_len fields"), flock64 structure uses abi_llong for l_start and l_len in place of "unsigned long long" this should force them to be aligned accordingly to the target rules. So we can remove the padding field and the QEMU_PACKED attribute. I have compared the result of the following program before and after the change: cat -> flock64_dump <l_type p/d &((struct target_flock64 *)0)->l_whence p/d &((struct target_flock64 *)0)->l_start p/d &((struct target_flock64 *)0)->l_len p/d &((struct target_flock64 *)0)->l_pid quit EOF for file in build/all/*-linux-user/qemu-* ; do echo $file gdb -batch -nx -x flock64_dump $file 2> /dev/null done The sizeof() changes because we remove the QEMU_PACKED. The new size is 32 (except for i386 and m68k) and this is the real size of "struct flock64" on the target architecture. The following architectures differ: aarch64_be, aarch64, alpha, armeb, arm, cris, hppa, nios2, or1k, riscv32, riscv64, s390x. For a subset of these architectures, I have checked with the following program the new structure is the correct one: #include #define __USE_LARGEFILE64 #include int main(void) { printf("struct flock64 %d\n", sizeof(struct flock64)); printf("l_type %d\n", &((struct flock64 *)0)->l_type); printf("l_whence %d\n", &((struct flock64 *)0)->l_whence); printf("l_start %d\n", &((struct flock64 *)0)->l_start); printf("l_len %d\n", &((struct flock64 *)0)->l_len); printf("l_pid %d\n", &((struct flock64 *)0)->l_pid); } [I have checked aarch64, alpha, hppa, s390x] For ARM, the target_flock64 becomes the EABI definition, so we need to define the OABI one in place of the EABI one and use it when it is needed. I have also fixed the alignment value for sh4 (to align llong on 4 bytes) (see c2e3dee6e0 "linux-user: Define target alignment size") [We should check alignment properties for cris, nios2 and or1k] Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180502215730.28162-1-laurent@vivier.eu> --- include/exec/user/abitypes.h | 2 +- linux-user/arm/target_structs.h | 7 +++++++ linux-user/syscall.c | 14 +++++++------- linux-user/syscall_defs.h | 25 ++++--------------------- 4 files changed, 19 insertions(+), 29 deletions(-) diff --git a/include/exec/user/abitypes.h b/include/exec/user/abitypes.h index ba188608c2..743b8bb9ea 100644 --- a/include/exec/user/abitypes.h +++ b/include/exec/user/abitypes.h @@ -15,7 +15,7 @@ #define ABI_LLONG_ALIGNMENT 2 #endif -#if defined(TARGET_I386) && !defined(TARGET_X86_64) +#if (defined(TARGET_I386) && !defined(TARGET_X86_64)) || defined(TARGET_SH4) #define ABI_LLONG_ALIGNMENT 4 #endif diff --git a/linux-user/arm/target_structs.h b/linux-user/arm/target_structs.h index 0bf034cc25..9a3dbce03d 100644 --- a/linux-user/arm/target_structs.h +++ b/linux-user/arm/target_structs.h @@ -49,4 +49,11 @@ struct target_shmid_ds { abi_ulong __unused5; }; +struct target_oabi_flock64 { + abi_short l_type; + abi_short l_whence; + abi_llong l_start; + abi_llong l_len; + abi_int l_pid; +} QEMU_PACKED; #endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 404be44ad5..e4825747f9 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6600,10 +6600,10 @@ typedef abi_long from_flock64_fn(struct flock64 *fl, abi_ulong target_addr); typedef abi_long to_flock64_fn(abi_ulong target_addr, const struct flock64 *fl); #if defined(TARGET_ARM) && TARGET_ABI_BITS == 32 -static inline abi_long copy_from_user_eabi_flock64(struct flock64 *fl, +static inline abi_long copy_from_user_oabi_flock64(struct flock64 *fl, abi_ulong target_flock_addr) { - struct target_eabi_flock64 *target_fl; + struct target_oabi_flock64 *target_fl; short l_type; if (!lock_user_struct(VERIFY_READ, target_fl, target_flock_addr, 1)) { @@ -6620,10 +6620,10 @@ static inline abi_long copy_from_user_eabi_flock64(struct flock64 *fl, return 0; } -static inline abi_long copy_to_user_eabi_flock64(abi_ulong target_flock_addr, +static inline abi_long copy_to_user_oabi_flock64(abi_ulong target_flock_addr, const struct flock64 *fl) { - struct target_eabi_flock64 *target_fl; + struct target_oabi_flock64 *target_fl; short l_type; if (!lock_user_struct(VERIFY_WRITE, target_fl, target_flock_addr, 0)) { @@ -11629,9 +11629,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, to_flock64_fn *copyto = copy_to_user_flock64; #ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) { - copyfrom = copy_from_user_eabi_flock64; - copyto = copy_to_user_eabi_flock64; + if (!((CPUARMState *)cpu_env)->eabi) { + copyfrom = copy_from_user_oabi_flock64; + copyto = copy_to_user_oabi_flock64; } #endif diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 23f5bccf0e..361bb83a29 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2649,29 +2649,12 @@ struct target_flock { }; struct target_flock64 { - short l_type; - short l_whence; -#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) \ - || defined(TARGET_SPARC) || defined(TARGET_HPPA) \ - || defined(TARGET_MICROBLAZE) || defined(TARGET_TILEGX) \ - || defined(TARGET_XTENSA) - int __pad; -#endif + abi_short l_type; + abi_short l_whence; abi_llong l_start; abi_llong l_len; - int l_pid; -} QEMU_PACKED; - -#ifdef TARGET_ARM -struct target_eabi_flock64 { - short l_type; - short l_whence; - int __pad; - abi_llong l_start; - abi_llong l_len; - int l_pid; -} QEMU_PACKED; -#endif + abi_int l_pid; +}; struct target_f_owner_ex { int type; /* Owner type of ID. */ From 1bad4957c8c4d5367961cb3d5287b2f25a1d6847 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Mon, 30 Apr 2018 23:02:17 +0300 Subject: [PATCH 0202/2380] hw/rdma: Fix possible munmap call on a NULL pointer Coverity CID 1390620: we call munmap() on a NULL pointer. Reported-by: Peter Maydell Signed-off-by: Marcel Apfelbaum Reviewed-by: Yuval Shaia Message-Id: <20180430200223.4119-2-marcel.apfelbaum@gmail.com> --- hw/rdma/vmw/pvrdma_cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/rdma/vmw/pvrdma_cmd.c b/hw/rdma/vmw/pvrdma_cmd.c index 99019d8741..f9dd78cb27 100644 --- a/hw/rdma/vmw/pvrdma_cmd.c +++ b/hw/rdma/vmw/pvrdma_cmd.c @@ -232,7 +232,7 @@ static int create_mr(PVRDMADev *dev, union pvrdma_cmd_req *req, cmd->start, cmd->length, host_virt, cmd->access_flags, &resp->mr_handle, &resp->lkey, &resp->rkey); - if (!resp->hdr.err) { + if (host_virt && !resp->hdr.err) { munmap(host_virt, cmd->length); } From b0197cf80a903aa1e401cc7ebdf8be8bc307297b Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Mon, 30 Apr 2018 23:02:18 +0300 Subject: [PATCH 0203/2380] hw/rdma: Fix possible usage of a NULL pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity CID 1390586; The cq handle is provided by the guest and cannot be trusted to be previuosly allocated. Fix it by exiting the completion flow. Reported-by: Peter Maydell Signed-off-by: Marcel Apfelbaum Reviewed-by: Yuval Shaia Message-Id: <20180430200223.4119-3-marcel.apfelbaum@gmail.com> Reviewed-by: Philippe Mathieu-Daudé --- hw/rdma/vmw/pvrdma_qp_ops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c index 750ade6c31..99bb51111e 100644 --- a/hw/rdma/vmw/pvrdma_qp_ops.c +++ b/hw/rdma/vmw/pvrdma_qp_ops.c @@ -216,6 +216,7 @@ void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle) cq = rdma_rm_get_cq(dev_res, cq_handle); if (!cq) { pr_dbg("Invalid CQ# %d\n", cq_handle); + return; } rdma_backend_poll_cq(dev_res, &cq->backend_cq); From b9e34872b95af26076e2b456fd1c3e9dd65f3b19 Mon Sep 17 00:00:00 2001 From: Yuval Shaia Date: Mon, 30 Apr 2018 23:02:19 +0300 Subject: [PATCH 0204/2380] hw/rdma: Delete port's pkey table Support for PKEY is not yet implemented. Removing the unneeded table until a support will be added. Signed-off-by: Yuval Shaia Reviewed-by: Marcel Apfelbaum Message-Id: <20180430200223.4119-4-marcel.apfelbaum@gmail.com> --- hw/rdma/rdma_rm_defs.h | 3 +-- hw/rdma/vmw/pvrdma_main.c | 15 --------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/hw/rdma/rdma_rm_defs.h b/hw/rdma/rdma_rm_defs.h index fc646da61f..45503f14e0 100644 --- a/hw/rdma/rdma_rm_defs.h +++ b/hw/rdma/rdma_rm_defs.h @@ -21,7 +21,7 @@ #define MAX_PORTS 1 #define MAX_PORT_GIDS 1 #define MAX_PORT_PKEYS 1 -#define MAX_PKEYS 1 +#define MAX_PKEYS MAX_PORT_PKEYS #define MAX_GIDS 2048 #define MAX_UCS 512 #define MAX_MR_SIZE (1UL << 27) @@ -87,7 +87,6 @@ typedef struct RdmaRmQP { typedef struct RdmaRmPort { union ibv_gid gid_tbl[MAX_PORT_GIDS]; enum ibv_port_state state; - int *pkey_tbl; /* TODO: Not yet supported */ } RdmaRmPort; typedef struct RdmaDeviceResources { diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c index c552248c90..994220b58e 100644 --- a/hw/rdma/vmw/pvrdma_main.c +++ b/hw/rdma/vmw/pvrdma_main.c @@ -275,15 +275,6 @@ static void init_dsr_dev_caps(PVRDMADev *dev) pr_dbg("Initialized\n"); } -static void free_ports(PVRDMADev *dev) -{ - int i; - - for (i = 0; i < MAX_PORTS; i++) { - g_free(dev->rdma_dev_res.ports[i].gid_tbl); - } -} - static void init_ports(PVRDMADev *dev, Error **errp) { int i; @@ -292,10 +283,6 @@ static void init_ports(PVRDMADev *dev, Error **errp) for (i = 0; i < MAX_PORTS; i++) { dev->rdma_dev_res.ports[i].state = IBV_PORT_DOWN; - - dev->rdma_dev_res.ports[i].pkey_tbl = - g_malloc0(sizeof(*dev->rdma_dev_res.ports[i].pkey_tbl) * - MAX_PORT_PKEYS); } } @@ -622,8 +609,6 @@ static void pvrdma_exit(PCIDevice *pdev) pvrdma_qp_ops_fini(); - free_ports(dev); - rdma_rm_fini(&dev->rdma_dev_res); rdma_backend_fini(&dev->backend_dev); From c387e8a4ecee76479f4b83b58e2e8ab854ef74ee Mon Sep 17 00:00:00 2001 From: Yuval Shaia Date: Mon, 30 Apr 2018 23:02:20 +0300 Subject: [PATCH 0205/2380] hw/rdma: Fix possible out of bounds access to GID table Array size is MAX_PORT_GIDS, let's make sure the given index is in range. While there limit device table size to 1. Reported-by: Peter Maydell Signed-off-by: Yuval Shaia Reviewed-by: Marcel Apfelbaum Message-Id: <20180430200223.4119-5-marcel.apfelbaum@gmail.com> --- hw/rdma/rdma_rm_defs.h | 2 +- hw/rdma/vmw/pvrdma_cmd.c | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/rdma/rdma_rm_defs.h b/hw/rdma/rdma_rm_defs.h index 45503f14e0..4d22a20e4c 100644 --- a/hw/rdma/rdma_rm_defs.h +++ b/hw/rdma/rdma_rm_defs.h @@ -20,9 +20,9 @@ #define MAX_PORTS 1 #define MAX_PORT_GIDS 1 +#define MAX_GIDS MAX_PORT_GIDS #define MAX_PORT_PKEYS 1 #define MAX_PKEYS MAX_PORT_PKEYS -#define MAX_GIDS 2048 #define MAX_UCS 512 #define MAX_MR_SIZE (1UL << 27) #define MAX_QP 1024 diff --git a/hw/rdma/vmw/pvrdma_cmd.c b/hw/rdma/vmw/pvrdma_cmd.c index f9dd78cb27..14255d609f 100644 --- a/hw/rdma/vmw/pvrdma_cmd.c +++ b/hw/rdma/vmw/pvrdma_cmd.c @@ -576,7 +576,7 @@ static int create_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, pr_dbg("index=%d\n", cmd->index); - if (cmd->index > MAX_PORT_GIDS) { + if (cmd->index >= MAX_PORT_GIDS) { return -EINVAL; } @@ -603,7 +603,11 @@ static int destroy_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_destroy_bind *cmd = &req->destroy_bind; - pr_dbg("clear index %d\n", cmd->index); + pr_dbg("index=%d\n", cmd->index); + + if (cmd->index >= MAX_PORT_GIDS) { + return -EINVAL; + } memset(dev->rdma_dev_res.ports[0].gid_tbl[cmd->index].raw, 0, sizeof(dev->rdma_dev_res.ports[0].gid_tbl[cmd->index].raw)); From 350929172b0205dccf19c29b93ac41a406ef740d Mon Sep 17 00:00:00 2001 From: Yuval Shaia Date: Mon, 30 Apr 2018 23:02:21 +0300 Subject: [PATCH 0206/2380] hw/rdma: Fix possible out of bounds access to regs array Coverity (CID1390589, CID1390608). Array size is RDMA_BAR1_REGS_SIZE, let's make sure the given address is in range. While there also: 1. Adjust the size of this bar to reasonable size 2. Report the size of the array with sizeof(array) Reported-by: Peter Maydell Signed-off-by: Yuval Shaia Reviewed-by: Marcel Apfelbaum Message-Id: <20180430200223.4119-6-marcel.apfelbaum@gmail.com> --- hw/rdma/vmw/pvrdma.h | 6 +++--- hw/rdma/vmw/pvrdma_main.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/rdma/vmw/pvrdma.h b/hw/rdma/vmw/pvrdma.h index 8c173cb824..0b46dc5a9b 100644 --- a/hw/rdma/vmw/pvrdma.h +++ b/hw/rdma/vmw/pvrdma.h @@ -31,7 +31,7 @@ #define RDMA_REG_BAR_IDX 1 #define RDMA_UAR_BAR_IDX 2 #define RDMA_BAR0_MSIX_SIZE (16 * 1024) -#define RDMA_BAR1_REGS_SIZE 256 +#define RDMA_BAR1_REGS_SIZE 64 #define RDMA_BAR2_UAR_SIZE (0x1000 * MAX_UCS) /* each uc gets page */ /* MSIX */ @@ -86,7 +86,7 @@ static inline int get_reg_val(PVRDMADev *dev, hwaddr addr, uint32_t *val) { int idx = addr >> 2; - if (idx > RDMA_BAR1_REGS_SIZE) { + if (idx >= RDMA_BAR1_REGS_SIZE) { return -EINVAL; } @@ -99,7 +99,7 @@ static inline int set_reg_val(PVRDMADev *dev, hwaddr addr, uint32_t val) { int idx = addr >> 2; - if (idx > RDMA_BAR1_REGS_SIZE) { + if (idx >= RDMA_BAR1_REGS_SIZE) { return -EINVAL; } diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c index 994220b58e..3ed7409763 100644 --- a/hw/rdma/vmw/pvrdma_main.c +++ b/hw/rdma/vmw/pvrdma_main.c @@ -449,14 +449,14 @@ static void init_bars(PCIDevice *pdev) /* BAR 1 - Registers */ memset(&dev->regs_data, 0, sizeof(dev->regs_data)); memory_region_init_io(&dev->regs, OBJECT(dev), ®s_ops, dev, - "pvrdma-regs", RDMA_BAR1_REGS_SIZE); + "pvrdma-regs", sizeof(dev->regs_data)); pci_register_bar(pdev, RDMA_REG_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, &dev->regs); /* BAR 2 - UAR */ memset(&dev->uar_data, 0, sizeof(dev->uar_data)); memory_region_init_io(&dev->uar, OBJECT(dev), &uar_ops, dev, "rdma-uar", - RDMA_BAR2_UAR_SIZE); + sizeof(dev->uar_data)); pci_register_bar(pdev, RDMA_UAR_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, &dev->uar); } From 6c080b9ea6efbc277cfa7afc7d4f9d8cc3a9020b Mon Sep 17 00:00:00 2001 From: Yuval Shaia Date: Mon, 30 Apr 2018 23:02:22 +0300 Subject: [PATCH 0207/2380] hw/rdma: Delete duplicate definition of MAX_RM_TBL_NAME By a mistake this constant was defined twice - remove the duplication. Signed-off-by: Yuval Shaia Reviewed-by: Marcel Apfelbaum Message-Id: <20180430200223.4119-7-marcel.apfelbaum@gmail.com> --- hw/rdma/rdma_rm.c | 2 -- hw/rdma/rdma_rm_defs.h | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/rdma/rdma_rm.c b/hw/rdma/rdma_rm.c index 51a47d7292..415da15efe 100644 --- a/hw/rdma/rdma_rm.c +++ b/hw/rdma/rdma_rm.c @@ -21,8 +21,6 @@ #include "rdma_backend.h" #include "rdma_rm.h" -#define MAX_RM_TBL_NAME 16 - /* Page directory and page tables */ #define PG_DIR_SZ { TARGET_PAGE_SIZE / sizeof(__u64) } #define PG_TBL_SZ { TARGET_PAGE_SIZE / sizeof(__u64) } diff --git a/hw/rdma/rdma_rm_defs.h b/hw/rdma/rdma_rm_defs.h index 4d22a20e4c..226011176d 100644 --- a/hw/rdma/rdma_rm_defs.h +++ b/hw/rdma/rdma_rm_defs.h @@ -34,9 +34,9 @@ #define MAX_QP_INIT_RD_ATOM 16 #define MAX_AH 64 -#define MAX_RMRESTBL_NAME_SZ 16 +#define MAX_RM_TBL_NAME 16 typedef struct RdmaRmResTbl { - char name[MAX_RMRESTBL_NAME_SZ]; + char name[MAX_RM_TBL_NAME]; QemuMutex lock; unsigned long *bitmap; size_t tbl_sz; From d18a530d8589257cd81960284df8730ef869cabe Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Mon, 30 Apr 2018 23:02:23 +0300 Subject: [PATCH 0208/2380] hw/rdma: Fix possible out of bounds access to port GID index Make sure the backend GID index is less then port's gid table length. Signed-off-by: Marcel Apfelbaum Reviewed-by: Yuval Shaia Message-Id: <20180430200223.4119-8-marcel.apfelbaum@gmail.com> --- hw/rdma/rdma_backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/rdma/rdma_backend.c b/hw/rdma/rdma_backend.c index 5c7b3d8949..e9ced6f9ef 100644 --- a/hw/rdma/rdma_backend.c +++ b/hw/rdma/rdma_backend.c @@ -774,7 +774,7 @@ int rdma_backend_init(RdmaBackendDev *backend_dev, goto out_destroy_comm_channel; } - if (backend_dev->backend_gid_idx > port_attr.gid_tbl_len) { + if (backend_dev->backend_gid_idx >= port_attr.gid_tbl_len) { error_setg(errp, "Invalid backend_gid_idx, should be less than %d", port_attr.gid_tbl_len); goto out_destroy_comm_channel; From fe355cbda28b5e3dfc6a70b902343b84c466aa17 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Apr 2018 11:45:23 +0300 Subject: [PATCH 0209/2380] MAINTAINERS: update Marcel Apfelbaum email Use my gmail account for maintainer tasks. Signed-off-by: Marcel Apfelbaum Message-Id: <20180426084523.10565-1-marcel@redhat.com> Reviewed-by: Yuval Shaia --- MAINTAINERS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 24b70169bc..459e3594e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -909,7 +909,7 @@ X86 Machines ------------ PC M: Michael S. Tsirkin -M: Marcel Apfelbaum +M: Marcel Apfelbaum S: Supported F: include/hw/i386/ F: hw/i386/ @@ -959,7 +959,7 @@ F: include/hw/timer/mc146818rtc* Machine core M: Eduardo Habkost -M: Marcel Apfelbaum +M: Marcel Apfelbaum S: Supported F: hw/core/machine.c F: hw/core/null-machine.c @@ -1033,7 +1033,7 @@ F: hw/ipack/ PCI M: Michael S. Tsirkin -M: Marcel Apfelbaum +M: Marcel Apfelbaum S: Supported F: include/hw/pci/* F: hw/misc/pci-testdev.c @@ -2075,7 +2075,7 @@ F: docs/block-replication.txt PVRDMA M: Yuval Shaia -M: Marcel Apfelbaum +M: Marcel Apfelbaum S: Maintained F: hw/rdma/* F: hw/rdma/vmw/* From ef0d74212ab5101565313f172b8a5baafe9cd0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 24 Apr 2018 13:30:41 +0200 Subject: [PATCH 0210/2380] target/ppc: return a nil HPT base address on sPAPR machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit e57ca75ce3b2 ("target/ppc: Manage external HPT via virtual hypervisor") exported a set of methods to manipulate the HPT from the core hash MMU. But SPR_SDR1 is still used under some circumstances to get the base address of the HPT, which is incorrect for the sPAPR machines. Only the logging should be impacted. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- target/ppc/mmu-hash64.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index d5fc03441d..f6349ccdb3 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -102,6 +102,9 @@ void ppc_hash64_finalize(PowerPCCPU *cpu); static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) { + if (cpu->vhyp) { + return 0; + } return cpu->env.spr[SPR_SDR1] & SDR_64_HTABORG; } From 4a7518e0fdaa20525730ae0709a4afa0960a6c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 24 Apr 2018 13:30:42 +0200 Subject: [PATCH 0211/2380] target/ppc: add basic support for PTCR on POWER9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Partition Table Control Register (PTCR) is a hypervisor privileged SPR. It contains the host real address of the Partition Table and its size. Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Signed-off-by: David Gibson --- target/ppc/cpu.h | 2 ++ target/ppc/helper.h | 1 + target/ppc/misc_helper.c | 12 ++++++++++++ target/ppc/mmu-book3s-v3.h | 6 ++++++ target/ppc/mmu_helper.c | 29 +++++++++++++++++++++++++++++ target/ppc/translate.c | 3 +++ target/ppc/translate_init.c | 18 ++++++++++++++++++ 7 files changed, 71 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 8c9e03f54d..2f619f39d3 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1295,6 +1295,7 @@ int ppc_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); +void ppc_store_ptcr(CPUPPCState *env, target_ulong value); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr (CPUPPCState *env, target_ulong value); @@ -1585,6 +1586,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_BOOKE_GIVOR13 (0x1BC) #define SPR_BOOKE_GIVOR14 (0x1BD) #define SPR_TIR (0x1BE) +#define SPR_PTCR (0x1D0) #define SPR_BOOKE_SPEFSCR (0x200) #define SPR_Exxx_BBEAR (0x201) #define SPR_Exxx_BBTAR (0x202) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 5b739179b8..19453c6813 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -709,6 +709,7 @@ DEF_HELPER_FLAGS_1(load_601_rtcu, TCG_CALL_NO_RWG, tl, env) #if !defined(CONFIG_USER_ONLY) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_2(store_ptcr, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 0e4217821b..8c8cba5cc6 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -88,6 +88,18 @@ void helper_store_sdr1(CPUPPCState *env, target_ulong val) } } +#if defined(TARGET_PPC64) +void helper_store_ptcr(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + if (env->spr[SPR_PTCR] != val) { + ppc_store_ptcr(env, val); + tlb_flush(CPU(cpu)); + } +} +#endif /* defined(TARGET_PPC64) */ + void helper_store_pidr(CPUPPCState *env, target_ulong val) { PowerPCCPU *cpu = ppc_env_get_cpu(env); diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index 56095dab52..fdf80987d7 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -22,6 +22,12 @@ #ifndef CONFIG_USER_ONLY +/* + * Partition table definitions + */ +#define PTCR_PATB 0x0FFFFFFFFFFFF000ULL /* Partition Table Base */ +#define PTCR_PATS 0x000000000000001FULL /* Partition Table Size */ + /* Partition Table Entry Fields */ #define PATBE1_GR 0x8000000000000000 diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 8075b7149a..98ce17985b 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -2028,6 +2028,35 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) env->spr[SPR_SDR1] = value; } +#if defined(TARGET_PPC64) +void ppc_store_ptcr(CPUPPCState *env, target_ulong value) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + target_ulong ptcr_mask = PTCR_PATB | PTCR_PATS; + target_ulong patbsize = value & PTCR_PATS; + + qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); + + assert(!cpu->vhyp); + assert(env->mmu_model & POWERPC_MMU_3_00); + + if (value & ~ptcr_mask) { + error_report("Invalid bits 0x"TARGET_FMT_lx" set in PTCR", + value & ~ptcr_mask); + value &= ptcr_mask; + } + + if (patbsize > 24) { + error_report("Invalid Partition Table size 0x" TARGET_FMT_lx + " stored in PTCR", patbsize); + return; + } + + env->spr[SPR_PTCR] = value; +} + +#endif /* defined(TARGET_PPC64) */ + /* Segment registers load and store */ target_ulong helper_load_sr(CPUPPCState *env, target_ulong sr_num) { diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 3beaa1e2f0..2a4140f420 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7136,6 +7136,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, if (env->spr_cb[SPR_SDR1].name) { /* SDR1 Exists */ cpu_fprintf(f, " SDR1 " TARGET_FMT_lx " ", env->spr[SPR_SDR1]); } + if (env->spr_cb[SPR_PTCR].name) { /* PTCR Exists */ + cpu_fprintf(f, " PTCR " TARGET_FMT_lx " ", env->spr[SPR_PTCR]); + } cpu_fprintf(f, " DAR " TARGET_FMT_lx " DSISR " TARGET_FMT_lx "\n", env->spr[SPR_DAR], env->spr[SPR_DSISR]); break; diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 808f6c1a08..c83c910a29 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -420,6 +420,11 @@ static void spr_write_hior(DisasContext *ctx, int sprn, int gprn) tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); tcg_temp_free(t0); } +static void spr_write_ptcr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_ptcr(cpu_env, cpu_gpr[gprn]); +} + #endif #endif @@ -8167,6 +8172,18 @@ static void gen_spr_power8_rpr(CPUPPCState *env) #endif } +static void gen_spr_power9_mmu(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + /* Partition Table Control */ + spr_register_hv(env, SPR_PTCR, "PTCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_ptcr, + 0x00000000); +#endif +} + static void init_proc_book3s_common(CPUPPCState *env) { gen_spr_ne_601(env); @@ -8719,6 +8736,7 @@ static void init_proc_POWER9(CPUPPCState *env) gen_spr_power8_ic(env); gen_spr_power8_book4(env); gen_spr_power8_rpr(env); + gen_spr_power9_mmu(env); /* POWER9 Specific registers */ spr_register_kvm(env, SPR_TIDR, "TIDR", NULL, NULL, From 090052aa08dbc774e55bc71a3058f24c8959586d Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 3 May 2018 16:52:40 +1000 Subject: [PATCH 0212/2380] spapr: Remove support for explicitly allocated RMAs Current POWER cpus allow for a VRMA, a special mapping which describes a guest's view of memory when in real mode (MMU off, from the guest's point of view). Older cpus didn't have that which meant that to support a guest a special host-contiguous region of memory was needed to give the guest its Real Mode Area (RMA). KVM used to provide special calls to allocate a contiguous RMA for those cases. This was useful in the early days of KVM on Power to allow it to be tested on PowerPC 970 chips as used in Macintosh G5 machines. Now, those machines are so old as to be almost irrelevant. The normal qemu deprecation process would require this to be marked deprecated then removed in 2 releases. However, this can only be used with corresponding support in the host kernel - which was dropped years ago (in c17b98cf "KVM: PPC: Book3S HV: Remove code for PPC970 processors" of 2014-12-03 to be precise). Therefore it should be ok to drop this immediately. Just to be clear this only affects *KVM HV* guests with PowerPC 970, and those already require an ancient host kernel. TCG and KVM PR guests with PowerPC 970 should still work. Signed-off-by: David Gibson Acked-by: Thomas Huth --- hw/ppc/spapr.c | 61 ++++++++++++++------------------------------ target/ppc/kvm.c | 42 ------------------------------ target/ppc/kvm_ppc.h | 6 ----- 3 files changed, 19 insertions(+), 90 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b35aff5d81..ed9b6a9535 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2508,9 +2508,6 @@ static void spapr_machine_init(MachineState *machine) int i; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *rma_region; - void *rma = NULL; - hwaddr rma_alloc_size; hwaddr node0_size = spapr_node0_size(machine); long load_limit, fw_size; char *filename; @@ -2549,40 +2546,28 @@ static void spapr_machine_init(MachineState *machine) exit(1); } - /* Allocate RMA if necessary */ - rma_alloc_size = kvmppc_alloc_rma(&rma); + spapr->rma_size = node0_size; - if (rma_alloc_size == -1) { - error_report("Unable to create RMA"); - exit(1); + /* With KVM, we don't actually know whether KVM supports an + * unbounded RMA (PR KVM) or is limited by the hash table size + * (HV KVM using VRMA), so we always assume the latter + * + * In that case, we also limit the initial allocations for RTAS + * etc... to 256M since we have no way to know what the VRMA size + * is going to be as it depends on the size of the hash table + * which isn't determined yet. + */ + if (kvm_enabled()) { + spapr->vrma_adjust = 1; + spapr->rma_size = MIN(spapr->rma_size, 0x10000000); } - if (rma_alloc_size && (rma_alloc_size < node0_size)) { - spapr->rma_size = rma_alloc_size; - } else { - spapr->rma_size = node0_size; - - /* With KVM, we don't actually know whether KVM supports an - * unbounded RMA (PR KVM) or is limited by the hash table size - * (HV KVM using VRMA), so we always assume the latter - * - * In that case, we also limit the initial allocations for RTAS - * etc... to 256M since we have no way to know what the VRMA size - * is going to be as it depends on the size of the hash table - * isn't determined yet. - */ - if (kvm_enabled()) { - spapr->vrma_adjust = 1; - spapr->rma_size = MIN(spapr->rma_size, 0x10000000); - } - - /* Actually we don't support unbounded RMA anymore since we - * added proper emulation of HV mode. The max we can get is - * 16G which also happens to be what we configure for PAPR - * mode so make sure we don't do anything bigger than that - */ - spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull); - } + /* Actually we don't support unbounded RMA anymore since we added + * proper emulation of HV mode. The max we can get is 16G which + * also happens to be what we configure for PAPR mode so make sure + * we don't do anything bigger than that + */ + spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull); if (spapr->rma_size > node0_size) { error_report("Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")", @@ -2643,14 +2628,6 @@ static void spapr_machine_init(MachineState *machine) machine->ram_size); memory_region_add_subregion(sysmem, 0, ram); - if (rma_alloc_size && rma) { - rma_region = g_new(MemoryRegion, 1); - memory_region_init_ram_ptr(rma_region, NULL, "ppc_spapr.rma", - rma_alloc_size, rma); - vmstate_register_ram_global(rma_region); - memory_region_add_subregion(sysmem, 0, rma_region); - } - /* initialize hotplug memory address space */ if (machine->ram_size < machine->maxram_size) { ram_addr_t hotplug_mem_size = machine->maxram_size - machine->ram_size; diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 6de59c5b21..e8be10a9a8 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -72,7 +72,6 @@ static int cap_segstate; static int cap_booke_sregs; static int cap_ppc_smt; static int cap_ppc_smt_possible; -static int cap_ppc_rma; static int cap_spapr_tce; static int cap_spapr_tce_64; static int cap_spapr_multitce; @@ -133,7 +132,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_segstate = kvm_check_extension(s, KVM_CAP_PPC_SEGSTATE); cap_booke_sregs = kvm_check_extension(s, KVM_CAP_PPC_BOOKE_SREGS); cap_ppc_smt_possible = kvm_vm_check_extension(s, KVM_CAP_PPC_SMT_POSSIBLE); - cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA); cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE); cap_spapr_tce_64 = kvm_check_extension(s, KVM_CAP_SPAPR_TCE_64); cap_spapr_multitce = kvm_check_extension(s, KVM_CAP_SPAPR_MULTITCE); @@ -2159,52 +2157,12 @@ void kvmppc_hint_smt_possible(Error **errp) #ifdef TARGET_PPC64 -off_t kvmppc_alloc_rma(void **rma) -{ - off_t size; - int fd; - struct kvm_allocate_rma ret; - - /* If cap_ppc_rma == 0, contiguous RMA allocation is not supported - * if cap_ppc_rma == 1, contiguous RMA allocation is supported, but - * not necessary on this hardware - * if cap_ppc_rma == 2, contiguous RMA allocation is needed on this hardware - * - * FIXME: We should allow the user to force contiguous RMA - * allocation in the cap_ppc_rma==1 case. - */ - if (cap_ppc_rma < 2) { - return 0; - } - - fd = kvm_vm_ioctl(kvm_state, KVM_ALLOCATE_RMA, &ret); - if (fd < 0) { - fprintf(stderr, "KVM: Error on KVM_ALLOCATE_RMA: %s\n", - strerror(errno)); - return -1; - } - - size = MIN(ret.rma_size, 256ul << 20); - - *rma = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (*rma == MAP_FAILED) { - fprintf(stderr, "KVM: Error mapping RMA: %s\n", strerror(errno)); - return -1; - }; - - return size; -} - uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) { struct kvm_ppc_smmu_info info; long rampagesize, best_page_shift; int i; - if (cap_ppc_rma >= 2) { - return current_size; - } - /* Find the largest hardware supported page size that's less than * or equal to the (logical) backing page size of guest RAM */ kvm_get_smmu_info(POWERPC_CPU(first_cpu), &info); diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 4d2789eef6..e2840e1d33 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -37,7 +37,6 @@ target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, bool radix, bool gtse, uint64_t proc_tbl); #ifndef CONFIG_USER_ONLY -off_t kvmppc_alloc_rma(void **rma); bool kvmppc_spapr_use_multitce(void); int kvmppc_spapr_enable_inkernel_multitce(void); void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, @@ -188,11 +187,6 @@ static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, } #ifndef CONFIG_USER_ONLY -static inline off_t kvmppc_alloc_rma(void **rma) -{ - return 0; -} - static inline bool kvmppc_spapr_use_multitce(void) { return false; From 5ad553154d1137fbee9ec92b991e52123fa9bcd8 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 5 Apr 2018 16:43:59 +1000 Subject: [PATCH 0213/2380] target/ppc: Add ppc_store_lpcr() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are some fields in the cpu state which need to be updated when the LPCR register is changed, which is done by ppc_hash64_update_rmls() and ppc_hash64_update_vrma(). Code which alters env->spr[SPR_LPCR] needs to call them afterwards to make sure the state is up to date. That's easy to get wrong. The normal way of dealing with sitautions like that is to use a helper which both updates the basic register value and the derived state. So, do that. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Reviewed-by: Greg Kurz --- target/ppc/mmu-hash64.c | 15 +++++++++++---- target/ppc/mmu-hash64.h | 3 +-- target/ppc/translate_init.c | 6 +----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 7e0adecfd9..a1db20e3a8 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -942,7 +942,7 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex, cpu->env.tlb_need_flush = TLB_NEED_GLOBAL_FLUSH | TLB_NEED_LOCAL_FLUSH; } -void ppc_hash64_update_rmls(PowerPCCPU *cpu) +static void ppc_hash64_update_rmls(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; uint64_t lpcr = env->spr[SPR_LPCR]; @@ -977,7 +977,7 @@ void ppc_hash64_update_rmls(PowerPCCPU *cpu) } } -void ppc_hash64_update_vrma(PowerPCCPU *cpu) +static void ppc_hash64_update_vrma(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; const PPCHash64SegmentPageSizes *sps = NULL; @@ -1028,9 +1028,9 @@ void ppc_hash64_update_vrma(PowerPCCPU *cpu) slb->sps = sps; } -void helper_store_lpcr(CPUPPCState *env, target_ulong val) +void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUPPCState *env = &cpu->env; uint64_t lpcr = 0; /* Filter out bits */ @@ -1096,6 +1096,13 @@ void helper_store_lpcr(CPUPPCState *env, target_ulong val) ppc_hash64_update_vrma(cpu); } +void helper_store_lpcr(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + ppc_store_lpcr(cpu, val); +} + void ppc_hash64_init(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index f6349ccdb3..53dcec5b93 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -17,8 +17,7 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong pte0, target_ulong pte1); unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, uint64_t pte0, uint64_t pte1); -void ppc_hash64_update_vrma(PowerPCCPU *cpu); -void ppc_hash64_update_rmls(PowerPCCPU *cpu); +void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val); void ppc_hash64_init(PowerPCCPU *cpu); void ppc_hash64_finalize(PowerPCCPU *cpu); #endif diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index c83c910a29..3fd380dad6 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8940,15 +8940,11 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) /* We should be followed by a CPU reset but update the active value * just in case... */ - env->spr[SPR_LPCR] = lpcr->default_value; + ppc_store_lpcr(cpu, lpcr->default_value); /* Set a full AMOR so guest can use the AMR as it sees fit */ env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull; - /* Update some env bits based on new LPCR value */ - ppc_hash64_update_rmls(cpu); - ppc_hash64_update_vrma(cpu); - /* Tell KVM that we're in PAPR mode */ if (kvm_enabled()) { kvmppc_set_papr(cpu); From cf116ad4703a37b66122d97f139afb2321b9c40e Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 1 May 2018 15:43:58 +1000 Subject: [PATCH 0214/2380] spapr: Clean up rtas_start_cpu() & rtas_stop_self() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes several minor cleanups to these functions: * Follow usual convention of an early exit on error, rather than having most of the body in an if * Clearer naming of cpu and cpu_. Now callcpu is the cpu from which the RTAS call is invoked, newcpu is the cpu which we're starting * Use cpu_synchronize_state() instead of kvm_cpu_synchronize_state() directly * Remove pointless comment describing what cpu_synchronize_state() does * Use ppc_store_lpcr() instead of directly writing the register field Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/spapr_rtas.c | 76 ++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 0ec5fa4cfe..b251c130cb 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -32,7 +32,7 @@ #include "hw/qdev.h" #include "sysemu/device_tree.h" #include "sysemu/cpus.h" -#include "sysemu/kvm.h" +#include "sysemu/hw_accel.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" @@ -45,6 +45,7 @@ #include "qemu/cutils.h" #include "trace.h" #include "hw/ppc/fdt.h" +#include "target/ppc/mmu-hash64.h" static void rtas_display_character(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, @@ -140,13 +141,15 @@ static void spapr_cpu_set_endianness(PowerPCCPU *cpu) } } -static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr, +static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { target_ulong id, start, r3; - PowerPCCPU *cpu; + PowerPCCPU *newcpu; + CPUPPCState *env; + PowerPCCPUClass *pcc; if (nargs != 3 || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -157,41 +160,37 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr, start = rtas_ld(args, 1); r3 = rtas_ld(args, 2); - cpu = spapr_find_cpu(id); - if (cpu != NULL) { - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - - if (!cs->halted) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - /* This will make sure qemu state is up to date with kvm, and - * mark it dirty so our changes get flushed back before the - * new cpu enters */ - kvm_cpu_synchronize_state(cs); - - env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); - - /* Enable Power-saving mode Exit Cause exceptions for the new CPU */ - env->spr[SPR_LPCR] |= pcc->lpcr_pm; - - env->nip = start; - env->gpr[3] = r3; - cs->halted = 0; - spapr_cpu_set_endianness(cpu); - spapr_cpu_update_tb_offset(cpu); - - qemu_cpu_kick(cs); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); + newcpu = spapr_find_cpu(id); + if (!newcpu) { + /* Didn't find a matching cpu */ + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } - /* Didn't find a matching cpu */ - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + env = &newcpu->env; + pcc = POWERPC_CPU_GET_CLASS(newcpu); + + if (!CPU(newcpu)->halted) { + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); + return; + } + + cpu_synchronize_state(CPU(newcpu)); + + env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); + spapr_cpu_set_endianness(newcpu); + spapr_cpu_update_tb_offset(newcpu); + /* Enable Power-saving mode Exit Cause exceptions for the new CPU */ + ppc_store_lpcr(newcpu, env->spr[SPR_LPCR] | pcc->lpcr_pm); + + env->nip = start; + env->gpr[3] = r3; + + CPU(newcpu)->halted = 0; + + qemu_cpu_kick(CPU(newcpu)); + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, @@ -203,13 +202,12 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - cs->halted = 1; - qemu_cpu_kick(cs); - /* Disable Power-saving mode Exit Cause exceptions for the CPU. * This could deliver an interrupt on a dying CPU and crash the * guest */ - env->spr[SPR_LPCR] &= ~pcc->lpcr_pm; + ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); + cs->halted = 1; + qemu_cpu_kick(cs); } static inline int sysparm_st(target_ulong addr, target_ulong len, From 982489180b72a41f008bbcf761ba1ed3f5f916ba Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 1 May 2018 16:05:09 +1000 Subject: [PATCH 0215/2380] spapr: Remove unhelpful helpers from rtas_start_cpu() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rtas_start_cpu() calls spapr_cpu_update_tb_offset() and spapr_cpu_set_endianness() to initialize certain things in the new cpu's state. This is the only caller of those helpers, and they're each only a few lines long, so we might as well just fold them into the caller. In addition, those helpers initialize state on the new cpu to match that of the first cpu. That will generally work, but might be at least logically incorrect if the first cpu has been set offline by the guest. So, instead base the state on that of the cpu invoking the RTAS call, which is obviously active already. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/spapr_rtas.c | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index b251c130cb..df073447c5 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -120,27 +120,6 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); } -/* - * Set the timebase offset of the CPU to that of first CPU. - * This helps hotplugged CPU to have the correct timebase offset. - */ -static void spapr_cpu_update_tb_offset(PowerPCCPU *cpu) -{ - PowerPCCPU *fcpu = POWERPC_CPU(first_cpu); - - cpu->env.tb_env->tb_offset = fcpu->env.tb_env->tb_offset; -} - -static void spapr_cpu_set_endianness(PowerPCCPU *cpu) -{ - PowerPCCPU *fcpu = POWERPC_CPU(first_cpu); - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(fcpu); - - if (!pcc->interrupts_big_endian(fcpu)) { - cpu->env.spr[SPR_LPCR] |= LPCR_ILE; - } -} - static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, @@ -150,6 +129,7 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, PowerPCCPU *newcpu; CPUPPCState *env; PowerPCCPUClass *pcc; + target_ulong lpcr; if (nargs != 3 || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -178,10 +158,20 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, cpu_synchronize_state(CPU(newcpu)); env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); - spapr_cpu_set_endianness(newcpu); - spapr_cpu_update_tb_offset(newcpu); + /* Enable Power-saving mode Exit Cause exceptions for the new CPU */ - ppc_store_lpcr(newcpu, env->spr[SPR_LPCR] | pcc->lpcr_pm); + lpcr = env->spr[SPR_LPCR] | pcc->lpcr_pm; + if (!pcc->interrupts_big_endian(callcpu)) { + lpcr |= LPCR_ILE; + } + ppc_store_lpcr(newcpu, lpcr); + + /* + * Set the timebase offset of the new CPU to that of the invoking + * CPU. This helps hotplugged CPU to have the correct timebase + * offset. + */ + newcpu->env.tb_env->tb_offset = callcpu->env.tb_env->tb_offset; env->nip = start; env->gpr[3] = r3; From 84369f639ec0ae08b5238f9abe383e8cd4998e98 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 1 May 2018 16:22:49 +1000 Subject: [PATCH 0216/2380] spapr: Make a helper to set up cpu entry point state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under PAPR, only the boot CPU is active when the system starts. Other cpus must be explicitly activated using an RTAS call. The entry state for the boot and secondary cpus isn't identical, but it has some things in common. We're going to add a bit more common setup later, too, so to simplify make a helper which sets up the common entry state for both boot and secondary cpu threads. Signed-off-by: David Gibson Signed-off-by: Cédric Le Goater Tested-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/spapr.c | 4 +--- hw/ppc/spapr_cpu_core.c | 9 +++++++++ hw/ppc/spapr_rtas.c | 6 ++---- include/hw/ppc/spapr_cpu_core.h | 3 +++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index ed9b6a9535..535d8276df 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1668,10 +1668,8 @@ static void spapr_machine_reset(void) g_free(fdt); /* Set up the entry state */ - first_ppc_cpu->env.gpr[3] = fdt_addr; + spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, fdt_addr); first_ppc_cpu->env.gpr[5] = 0; - first_cpu->halted = 0; - first_ppc_cpu->env.nip = SPAPR_ENTRY_POINT; spapr->cas_reboot = false; } diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 01dbc69424..a98c7b04c6 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -52,6 +52,15 @@ static void spapr_cpu_reset(void *opaque) } +void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) +{ + CPUPPCState *env = &cpu->env; + + env->nip = nip; + env->gpr[3] = r3; + CPU(cpu)->halted = 0; +} + static void spapr_cpu_destroy(PowerPCCPU *cpu) { qemu_unregister_reset(spapr_cpu_reset, cpu); diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index df073447c5..840d198a8d 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -37,6 +37,7 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "hw/ppc/spapr_rtas.h" +#include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/ppc.h" #include "hw/boards.h" @@ -173,10 +174,7 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, */ newcpu->env.tb_env->tb_offset = callcpu->env.tb_env->tb_offset; - env->nip = start; - env->gpr[3] = r3; - - CPU(newcpu)->halted = 0; + spapr_cpu_set_entry_state(newcpu, start, r3); qemu_cpu_kick(CPU(newcpu)); diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h index 1129f344aa..47dcfda12b 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -12,6 +12,7 @@ #include "hw/qdev.h" #include "hw/cpu/core.h" #include "target/ppc/cpu-qom.h" +#include "target/ppc/cpu.h" #define TYPE_SPAPR_CPU_CORE "spapr-cpu-core" #define SPAPR_CPU_CORE(obj) \ @@ -38,4 +39,6 @@ typedef struct sPAPRCPUCoreClass { } sPAPRCPUCoreClass; const char *spapr_get_cpu_core_type(const char *cpu_type); +void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3); + #endif From 295b6c26aca97c5f6f6609f62d958af6af848454 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 1 May 2018 15:14:52 +1000 Subject: [PATCH 0217/2380] spapr: Clean up LPCR updates from hypercalls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are several places in spapr_hcall.c where we need to update the LPCR value on all CPUs. We do this with the set_spr() helper. That's not really correct because this directly sets the SPR value, without going through the ppc_store_lpcr() helper which may need to update state based on the LPCR change. In fact, set_spr() is only ever used for the LPCR, so replace it with an explicit LPCR updated which uses the right low-level helper. While we're there, move the CPU_FOREACH() which was in every one of the callers into the new helper: set_all_lpcrs(). Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater --- hw/ppc/spapr_hcall.c | 50 ++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 16bccdd5c0..ca9702e667 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -15,32 +15,35 @@ #include "hw/ppc/spapr_ovec.h" #include "mmu-book3s-v3.h" -struct SPRSyncState { - int spr; +struct LPCRSyncState { target_ulong value; target_ulong mask; }; -static void do_spr_sync(CPUState *cs, run_on_cpu_data arg) +static void do_lpcr_sync(CPUState *cs, run_on_cpu_data arg) { - struct SPRSyncState *s = arg.host_ptr; + struct LPCRSyncState *s = arg.host_ptr; PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + target_ulong lpcr; cpu_synchronize_state(cs); - env->spr[s->spr] &= ~s->mask; - env->spr[s->spr] |= s->value; + lpcr = env->spr[SPR_LPCR]; + lpcr &= ~s->mask; + lpcr |= s->value; + ppc_store_lpcr(cpu, lpcr); } -static void set_spr(CPUState *cs, int spr, target_ulong value, - target_ulong mask) +static void set_all_lpcrs(target_ulong value, target_ulong mask) { - struct SPRSyncState s = { - .spr = spr, + CPUState *cs; + struct LPCRSyncState s = { .value = value, .mask = mask }; - run_on_cpu(cs, do_spr_sync, RUN_ON_CPU_HOST_PTR(&s)); + CPU_FOREACH(cs) { + run_on_cpu(cs, do_lpcr_sync, RUN_ON_CPU_HOST_PTR(&s)); + } } static bool has_spr(PowerPCCPU *cpu, int spr) @@ -1235,8 +1238,6 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, target_ulong value1, target_ulong value2) { - CPUState *cs; - if (value1) { return H_P3; } @@ -1246,16 +1247,12 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, switch (mflags) { case H_SET_MODE_ENDIAN_BIG: - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, 0, LPCR_ILE); - } + set_all_lpcrs(0, LPCR_ILE); spapr_pci_switch_vga(true); return H_SUCCESS; case H_SET_MODE_ENDIAN_LITTLE: - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, LPCR_ILE, LPCR_ILE); - } + set_all_lpcrs(LPCR_ILE, LPCR_ILE); spapr_pci_switch_vga(false); return H_SUCCESS; } @@ -1268,7 +1265,6 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, target_ulong value1, target_ulong value2) { - CPUState *cs; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); if (!(pcc->insns_flags2 & PPC2_ISA207S)) { @@ -1285,9 +1281,7 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, return H_UNSUPPORTED_FLAG; } - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, mflags << LPCR_AIL_SHIFT, LPCR_AIL); - } + set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL); return H_SUCCESS; } @@ -1364,7 +1358,6 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, target_ulong opcode, target_ulong *args) { - CPUState *cs; target_ulong flags = args[0]; target_ulong proc_tbl = args[1]; target_ulong page_size = args[2]; @@ -1422,12 +1415,9 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, spapr->patb_entry = cproc; /* Save new process table */ /* Update the UPRT and GTSE bits in the LPCR for all cpus */ - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, - ((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) | - ((flags & FLAG_GTSE) ? LPCR_GTSE : 0), - LPCR_UPRT | LPCR_GTSE); - } + set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) | + ((flags & FLAG_GTSE) ? LPCR_GTSE : 0), + LPCR_UPRT | LPCR_GTSE); if (kvm_enabled()) { return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX, From f00bed9521cee4d67c4937b51de692e0bcf9efef Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 1 May 2018 16:08:50 +1000 Subject: [PATCH 0218/2380] target/ppc: Delay initialization of LPCR_UPRT for secondary cpus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cpu_ppc_set_papr() the UPRT and GTSE bits of the LPCR default value are initialized based on on ppc64_radix_guest(). Which seems reasonable, except that ppc64_radix_guest() is based on spapr->patb_entry which is only set up in spapr_machine_reset, called _after_ cpu_ppc_set_papr() for boot cpus. Well, and the fact that modifying the SPR default value for an instance rather than a class is kind of yucky. The initialization here is really only necessary or valid for hotplugged cpus; the base cpu initialization already sets a value that's good enough for the boot cpus until the guest uses an hcall to configure it's preferred MMU mode. So, move this initialization to the rtas_start_cpu() path, at which point ppc64_radix_guest() will have a sensible value, to make sure secondary cpus come up in an MMU mode matching the existing cpus. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater --- hw/ppc/spapr_rtas.c | 12 ++++++++++++ target/ppc/translate_init.c | 16 ---------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 840d198a8d..652233bdf1 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -47,6 +47,7 @@ #include "trace.h" #include "hw/ppc/fdt.h" #include "target/ppc/mmu-hash64.h" +#include "target/ppc/mmu-book3s-v3.h" static void rtas_display_character(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, @@ -165,6 +166,17 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, if (!pcc->interrupts_big_endian(callcpu)) { lpcr |= LPCR_ILE; } + if (env->mmu_model == POWERPC_MMU_3_00) { + /* + * New cpus are expected to start in the same radix/hash mode + * as the existing CPUs + */ + if (ppc64_radix_guest(callcpu)) { + lpcr |= LPCR_UPRT | LPCR_GTSE; + } else { + lpcr &= ~(LPCR_UPRT | LPCR_GTSE); + } + } ppc_store_lpcr(newcpu, lpcr); /* diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 3fd380dad6..d92a84c622 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8914,22 +8914,6 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) lpcr->default_value &= ~LPCR_RMLS; lpcr->default_value |= 1ull << LPCR_RMLS_SHIFT; - if (env->mmu_model == POWERPC_MMU_3_00) { - /* By default we choose legacy mode and switch to new hash or radix - * when a register process table hcall is made. So disable process - * tables and guest translation shootdown by default - * - * Hot-plugged CPUs inherit from the guest radix setting under - * KVM but not under TCG. Update the default LPCR to keep new - * CPUs in sync when radix is enabled. - */ - if (ppc64_radix_guest(cpu)) { - lpcr->default_value |= LPCR_UPRT | LPCR_GTSE; - } else { - lpcr->default_value &= ~(LPCR_UPRT | LPCR_GTSE); - } - } - /* Only enable Power-saving mode Exit Cause exceptions on the boot * CPU. The RTAS command start-cpu will enable them on secondaries. */ From da20aed12a983dcaa6ccef80c0175f438e2780e7 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 5 Apr 2018 16:02:51 +1000 Subject: [PATCH 0219/2380] spapr: Move PAPR mode cpu setup fully to spapr code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_ppc_set_papr() does several things: 1) it sets up the virtual hypervisor interface 2) it prevents the cpu from ever entering hypervisor mode 3) it tells KVM that we're emulating a cpu in PAPR mode and 4) it configures the LPCR and AMOR (hypervisor privileged registers) so that TCG will behave correctly for PAPR guests, without attempting to emulate the cpu in hypervisor mode (1) & (2) make sense for any virtual hypervisor (if another one ever exists). (3) belongs more properly in the machine type specific to a PAPR guest, so move it to spapr_cpu_init(). While we're at it, remove an ugly test on kvm_enabled() by making kvmppc_set_papr() a safe no-op on non-KVM. (4) also belongs more properly in the machine type specific code. (4) is done by mangling the default values of the SPRs, so that they will be set correctly at reset time. Manipulating usually-static parameters of the cpu model like this is kind of ugly, especially since the values used really have more to do with the platform than the cpu. The spapr code already has places for PAPR specific initializations of register state in spapr_cpu_reset(), so move this handling there. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater --- hw/ppc/spapr_cpu_core.c | 36 ++++++++++++++++++++++++++++--- target/ppc/cpu.h | 2 +- target/ppc/kvm.c | 4 ++++ target/ppc/translate_init.c | 42 +------------------------------------ 4 files changed, 39 insertions(+), 45 deletions(-) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index a98c7b04c6..a52ddade5e 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -28,6 +28,7 @@ static void spapr_cpu_reset(void *opaque) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + target_ulong lpcr; cpu_reset(cs); @@ -43,13 +44,42 @@ static void spapr_cpu_reset(void *opaque) env->spr[SPR_HIOR] = 0; + lpcr = env->spr[SPR_LPCR]; + + /* Set emulated LPCR to not send interrupts to hypervisor. Note that + * under KVM, the actual HW LPCR will be set differently by KVM itself, + * the settings below ensure proper operations with TCG in absence of + * a real hypervisor. + * + * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for + * real mode accesses, which thankfully defaults to 0 and isn't + * accessible in guest mode. + */ + lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV); + lpcr |= LPCR_LPES0 | LPCR_LPES1; + + /* Set RMLS to the max (ie, 16G) */ + lpcr &= ~LPCR_RMLS; + lpcr |= 1ull << LPCR_RMLS_SHIFT; + + /* Only enable Power-saving mode Exit Cause exceptions on the boot + * CPU. The RTAS command start-cpu will enable them on secondaries. + */ + if (cs == first_cpu) { + lpcr |= pcc->lpcr_pm; + } + /* Disable Power-saving mode Exit Cause exceptions for the CPU. * This can cause issues when rebooting the guest if a secondary * is awaken */ if (cs != first_cpu) { - env->spr[SPR_LPCR] &= ~pcc->lpcr_pm; + lpcr &= ~pcc->lpcr_pm; } + ppc_store_lpcr(cpu, lpcr); + + /* Set a full AMOR so guest can use the AMR as it sees fit */ + env->spr[SPR_AMOR] = 0xffffffffffffffffull; } void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) @@ -74,8 +104,8 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); - /* Enable PAPR mode in TCG or KVM */ - cpu_ppc_set_papr(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); + cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); + kvmppc_set_papr(cpu); qemu_register_reset(spapr_cpu_reset, cpu); spapr_cpu_reset(cpu); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 2f619f39d3..7ccd2f460e 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1332,7 +1332,7 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val); void store_booke_tsr (CPUPPCState *env, target_ulong val); void ppc_tlb_invalidate_all (CPUPPCState *env); void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); -void cpu_ppc_set_papr(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); +void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); #endif #endif diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index e8be10a9a8..cbe13b18d1 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2088,6 +2088,10 @@ void kvmppc_set_papr(PowerPCCPU *cpu) CPUState *cs = CPU(cpu); int ret; + if (!kvm_enabled()) { + return; + } + ret = kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_PAPR, 0); if (ret) { error_report("This vCPU type or KVM version does not support PAPR"); diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index d92a84c622..118631efbe 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8882,13 +8882,9 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) } #if !defined(CONFIG_USER_ONLY) -void cpu_ppc_set_papr(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) +void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) { - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; - ppc_spr_t *lpcr = &env->spr_cb[SPR_LPCR]; - ppc_spr_t *amor = &env->spr_cb[SPR_AMOR]; - CPUState *cs = CPU(cpu); cpu->vhyp = vhyp; @@ -8897,42 +8893,6 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) * hypervisor mode itself */ env->msr_mask &= ~MSR_HVB; - - /* Set emulated LPCR to not send interrupts to hypervisor. Note that - * under KVM, the actual HW LPCR will be set differently by KVM itself, - * the settings below ensure proper operations with TCG in absence of - * a real hypervisor. - * - * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for - * real mode accesses, which thankfully defaults to 0 and isn't - * accessible in guest mode. - */ - lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV); - lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1; - - /* Set RMLS to the max (ie, 16G) */ - lpcr->default_value &= ~LPCR_RMLS; - lpcr->default_value |= 1ull << LPCR_RMLS_SHIFT; - - /* Only enable Power-saving mode Exit Cause exceptions on the boot - * CPU. The RTAS command start-cpu will enable them on secondaries. - */ - if (cs == first_cpu) { - lpcr->default_value |= pcc->lpcr_pm; - } - - /* We should be followed by a CPU reset but update the active value - * just in case... - */ - ppc_store_lpcr(cpu, lpcr->default_value); - - /* Set a full AMOR so guest can use the AMR as it sees fit */ - env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull; - - /* Tell KVM that we're in PAPR mode */ - if (kvm_enabled()) { - kvmppc_set_papr(cpu); - } } #endif /* !defined(CONFIG_USER_ONLY) */ From 47a9b5515475d91cb70791fe55182cf2fead135d Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 5 Apr 2018 16:27:18 +1000 Subject: [PATCH 0220/2380] spapr: Clean up handling of LPCR power-saving exit bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To prevent spurious wakeups on cpus that are supposed to be disabled, we need to clear the LPCR bits which control certain wakeup events. spapr_cpu_reset() has separate cases here for boot and non-boot (initially inactive) cpus. rtas_start_cpu() then turns the LPCR bits on when the non-boot cpus are activated. But explicit checks against first_cpu are not how we usually do things: instead spapr_cpu_reset() generally sets things up for non-boot (inactive) cpus, then spapr_machine_reset() and/or rtas_start_cpu() override as necessary. So, do that instead. Because the LPCR activation is identical for boot cpus and non-boot cpus just activated with rtas_start_cpu() we can put the code common in spapr_cpu_set_entry_state(). Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater --- hw/ppc/spapr_cpu_core.c | 22 +++++++--------------- hw/ppc/spapr_rtas.c | 2 +- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index a52ddade5e..f3e9b879b2 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -54,28 +54,17 @@ static void spapr_cpu_reset(void *opaque) * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for * real mode accesses, which thankfully defaults to 0 and isn't * accessible in guest mode. + * + * Disable Power-saving mode Exit Cause exceptions for the CPU, so + * we don't get spurious wakups before an RTAS start-cpu call. */ - lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV); + lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm); lpcr |= LPCR_LPES0 | LPCR_LPES1; /* Set RMLS to the max (ie, 16G) */ lpcr &= ~LPCR_RMLS; lpcr |= 1ull << LPCR_RMLS_SHIFT; - /* Only enable Power-saving mode Exit Cause exceptions on the boot - * CPU. The RTAS command start-cpu will enable them on secondaries. - */ - if (cs == first_cpu) { - lpcr |= pcc->lpcr_pm; - } - - /* Disable Power-saving mode Exit Cause exceptions for the CPU. - * This can cause issues when rebooting the guest if a secondary - * is awaken */ - if (cs != first_cpu) { - lpcr &= ~pcc->lpcr_pm; - } - ppc_store_lpcr(cpu, lpcr); /* Set a full AMOR so guest can use the AMR as it sees fit */ @@ -84,11 +73,14 @@ static void spapr_cpu_reset(void *opaque) void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) { + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; env->nip = nip; env->gpr[3] = r3; CPU(cpu)->halted = 0; + /* Enable Power-saving mode Exit Cause exceptions */ + ppc_store_lpcr(cpu, env->spr[SPR_LPCR] | pcc->lpcr_pm); } static void spapr_cpu_destroy(PowerPCCPU *cpu) diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 652233bdf1..7f9738daed 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -162,7 +162,7 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); /* Enable Power-saving mode Exit Cause exceptions for the new CPU */ - lpcr = env->spr[SPR_LPCR] | pcc->lpcr_pm; + lpcr = env->spr[SPR_LPCR]; if (!pcc->interrupts_big_endian(callcpu)) { lpcr |= LPCR_ILE; } From 0662946aa6d3129e7974d4484fc94ab2a5b15d4e Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 3 May 2018 21:24:39 +0100 Subject: [PATCH 0221/2380] uninorth: create new uninorth device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 4e46dcdbd3 "PPC: Newworld: Add uninorth token register" added a TODO which was to convert the uninorth registers hack to a proper device. Move these registers to a new uninorth device, removing the old hacks from mac_newworld.c. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/pci-host/trace-events | 2 ++ hw/pci-host/uninorth.c | 58 ++++++++++++++++++++++++++++++++++ hw/ppc/mac_newworld.c | 41 ++++-------------------- hw/ppc/trace-events | 4 --- include/hw/pci-host/uninorth.h | 11 +++++++ 5 files changed, 77 insertions(+), 39 deletions(-) diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events index 341a87a702..dd7a398e96 100644 --- a/hw/pci-host/trace-events +++ b/hw/pci-host/trace-events @@ -18,3 +18,5 @@ unin_set_irq(int irq_num, int level) "setting INT %d = %d" unin_get_config_reg(uint32_t reg, uint32_t addr, uint32_t retval) "converted config space accessor 0x%"PRIx32 "/0x%"PRIx32 " -> 0x%"PRIx32 unin_data_write(uint64_t addr, unsigned len, uint64_t val) "write addr 0x%"PRIx64 " len %d val 0x%"PRIx64 unin_data_read(uint64_t addr, unsigned len, uint64_t val) "read addr 0x%"PRIx64 " len %d val 0x%"PRIx64 +unin_write(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 +unin_read(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index fada0ffd5f..ba76b84dbc 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -519,6 +519,62 @@ static const TypeInfo pci_unin_internal_info = { .class_init = pci_unin_internal_class_init, }; +/* UniN device */ +static void unin_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + trace_unin_write(addr, value); + if (addr == 0x0) { + *(int *)opaque = value; + } +} + +static uint64_t unin_read(void *opaque, hwaddr addr, unsigned size) +{ + uint32_t value; + + value = 0; + switch (addr) { + case 0: + value = *(int *)opaque; + } + + trace_unin_read(addr, value); + + return value; +} + +static const MemoryRegionOps unin_ops = { + .read = unin_read, + .write = unin_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void unin_init(Object *obj) +{ + UNINState *s = UNI_NORTH(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->mem, obj, &unin_ops, &s->token, "unin", 0x1000); + + sysbus_init_mmio(sbd, &s->mem); +} + +static void unin_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +static const TypeInfo unin_info = { + .name = TYPE_UNI_NORTH, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(UNINState), + .instance_init = unin_init, + .class_init = unin_class_init, +}; + static void unin_register_types(void) { type_register_static(&unin_main_pci_host_info); @@ -530,6 +586,8 @@ static void unin_register_types(void) type_register_static(&pci_u3_agp_info); type_register_static(&pci_unin_agp_info); type_register_static(&pci_unin_internal_info); + + type_register_static(&unin_info); } type_init(unin_register_types) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 29bd3838bf..9a382f992d 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -82,36 +82,6 @@ #define NDRV_VGA_FILENAME "qemu_vga.ndrv" -/* UniN device */ -static void unin_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - trace_mac99_uninorth_write(addr, value); - if (addr == 0x0) { - *(int*)opaque = value; - } -} - -static uint64_t unin_read(void *opaque, hwaddr addr, unsigned size) -{ - uint32_t value; - - value = 0; - switch (addr) { - case 0: - value = *(int*)opaque; - } - - trace_mac99_uninorth_read(addr, value); - - return value; -} - -static const MemoryRegionOps unin_ops = { - .read = unin_read, - .write = unin_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) @@ -145,7 +115,6 @@ static void ppc_core99_init(MachineState *machine) CPUPPCState *env = NULL; char *filename; qemu_irq *pic, **openpic_irqs; - MemoryRegion *unin_memory = g_new(MemoryRegion, 1); int linux_boot, i, j, k; MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); hwaddr kernel_base, initrd_base, cmdline_base = 0; @@ -164,7 +133,6 @@ static void ppc_core99_init(MachineState *machine) int machine_arch; SysBusDevice *s; DeviceState *dev, *pic_dev; - int *token = g_new(int, 1); hwaddr nvram_addr = 0xFFF04000; uint64_t tbfreq; @@ -272,9 +240,12 @@ static void ppc_core99_init(MachineState *machine) } } - /* UniN init: XXX should be a real device */ - memory_region_init_io(unin_memory, NULL, &unin_ops, token, "unin", 0x1000); - memory_region_add_subregion(get_system_memory(), 0xf8000000, unin_memory); + /* UniN init */ + dev = qdev_create(NULL, TYPE_UNI_NORTH); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + memory_region_add_subregion(get_system_memory(), 0xf8000000, + sysbus_mmio_get_region(s, 0)); openpic_irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); openpic_irqs[0] = diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 66ec7eda6e..dc5e65aee9 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -92,10 +92,6 @@ rs6000mc_size_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" rs6000mc_size_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x" rs6000mc_parity_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" -# hw/ppc/mac_newworld.c -mac99_uninorth_write(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 -mac99_uninorth_read(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 - # hw/ppc/ppc4xx_pci.c ppc4xx_pci_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" ppc4xx_pci_set_irq(int irq_num) "PCI irq %d" diff --git a/include/hw/pci-host/uninorth.h b/include/hw/pci-host/uninorth.h index f0e6836c76..f6654bad9b 100644 --- a/include/hw/pci-host/uninorth.h +++ b/include/hw/pci-host/uninorth.h @@ -53,4 +53,15 @@ typedef struct UNINHostState { MemoryRegion pci_io; } UNINHostState; +typedef struct UNINState { + SysBusDevice parent_obj; + + MemoryRegion mem; + int token[1]; +} UNINState; + +#define TYPE_UNI_NORTH "uni-north" +#define UNI_NORTH(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH) + #endif /* UNINORTH_H */ From 040b27c0b9e593573736d1f5bb804675f02b8816 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 3 May 2018 21:24:40 +0100 Subject: [PATCH 0222/2380] mac_newworld: remove pics IRQ array and wire up macio to OpenPIC directly Introduce constants for the pre-defined New World IRQs to help keep things readable. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/ppc/mac.h | 9 +++++++++ hw/ppc/mac_newworld.c | 29 +++++++++++++++-------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 892dd03789..22a7efbed6 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -56,6 +56,15 @@ #define OLDWORLD_IDE1_IRQ 0xe #define OLDWORLD_IDE1_DMA_IRQ 0x3 +/* New World IRQs */ +#define NEWWORLD_CUDA_IRQ 0x19 +#define NEWWORLD_ESCCB_IRQ 0x24 +#define NEWWORLD_ESCCA_IRQ 0x25 +#define NEWWORLD_IDE0_IRQ 0xd +#define NEWWORLD_IDE0_DMA_IRQ 0x2 +#define NEWWORLD_IDE1_IRQ 0xe +#define NEWWORLD_IDE1_DMA_IRQ 0x3 + /* MacIO */ #define TYPE_MACIO_IDE "macio-ide" #define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 9a382f992d..6a070f13bd 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -114,7 +114,7 @@ static void ppc_core99_init(MachineState *machine) PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; - qemu_irq *pic, **openpic_irqs; + qemu_irq **openpic_irqs; int linux_boot, i, j, k; MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); hwaddr kernel_base, initrd_base, cmdline_base = 0; @@ -291,8 +291,6 @@ static void ppc_core99_init(MachineState *machine) } } - pic = g_new0(qemu_irq, 64); - pic_dev = qdev_create(NULL, TYPE_OPENPIC); qdev_prop_set_uint32(pic_dev, "model", OPENPIC_MODEL_KEYLARGO); qdev_init_nofail(pic_dev); @@ -304,10 +302,6 @@ static void ppc_core99_init(MachineState *machine) } } - for (i = 0; i < 64; i++) { - pic[i] = qdev_get_gpio_in(pic_dev, i); - } - if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { /* 970 gets a U3 bus */ /* Uninorth AGP bus */ @@ -381,13 +375,20 @@ static void ppc_core99_init(MachineState *machine) /* MacIO */ macio = NEWWORLD_MACIO(pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO)); dev = DEVICE(macio); - qdev_connect_gpio_out(dev, 0, pic[0x19]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x24]); /* ESCC-B */ - qdev_connect_gpio_out(dev, 2, pic[0x25]); /* ESCC-A */ - qdev_connect_gpio_out(dev, 3, pic[0x0d]); /* IDE */ - qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE DMA */ - qdev_connect_gpio_out(dev, 5, pic[0x0e]); /* IDE */ - qdev_connect_gpio_out(dev, 6, pic[0x03]); /* IDE DMA */ + qdev_connect_gpio_out(dev, 0, + qdev_get_gpio_in(pic_dev, NEWWORLD_CUDA_IRQ)); + qdev_connect_gpio_out(dev, 1, + qdev_get_gpio_in(pic_dev, NEWWORLD_ESCCB_IRQ)); + qdev_connect_gpio_out(dev, 2, + qdev_get_gpio_in(pic_dev, NEWWORLD_ESCCA_IRQ)); + qdev_connect_gpio_out(dev, 3, + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_IRQ)); + qdev_connect_gpio_out(dev, 4, + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_DMA_IRQ)); + qdev_connect_gpio_out(dev, 5, + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_IRQ)); + qdev_connect_gpio_out(dev, 6, + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_DMA_IRQ)); qdev_prop_set_uint64(dev, "frequency", tbfreq); object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", &error_abort); From 0fcd2a814aa331f87fd099171ae03a61311bdfee Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 3 May 2018 21:24:41 +0100 Subject: [PATCH 0223/2380] mac_newworld: move wiring of macio IRQs to macio_newworld_realize() Since the macio device has a link to the PIC device, we can now wire up the IRQs directly via qdev GPIOs rather than having to use an intermediate array. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/macio.c | 37 +++++++++++++++++++++-------------- hw/ppc/mac_newworld.c | 14 ------------- include/hw/misc/macio/macio.h | 1 - 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index dac7bcd15e..79621eb879 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -279,11 +279,10 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) { MacIOState *s = MACIO(d); NewWorldMacIOState *ns = NEWWORLD_MACIO(d); + DeviceState *pic_dev = DEVICE(ns->pic); Error *err = NULL; SysBusDevice *sysbus_dev; MemoryRegion *timer_memory = NULL; - int i; - int cur_irq = 0; macio_common_realize(d, &err); if (err) { @@ -292,11 +291,14 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) } sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + NEWWORLD_CUDA_IRQ)); sysbus_dev = SYS_BUS_DEVICE(&s->escc); - sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]); - sysbus_connect_irq(sysbus_dev, 1, ns->irqs[cur_irq++]); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + NEWWORLD_ESCCB_IRQ)); + sysbus_connect_irq(sysbus_dev, 1, qdev_get_gpio_in(pic_dev, + NEWWORLD_ESCCA_IRQ)); /* OpenPIC */ sysbus_dev = SYS_BUS_DEVICE(ns->pic); @@ -304,15 +306,22 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) sysbus_mmio_get_region(sysbus_dev, 0)); /* IDE buses */ - for (i = 0; i < ARRAY_SIZE(ns->ide); i++) { - qemu_irq irq0 = ns->irqs[cur_irq++]; - qemu_irq irq1 = ns->irqs[cur_irq++]; + macio_realize_ide(s, &ns->ide[0], + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_IRQ), + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_DMA_IRQ), + 0x16, &err); + if (err) { + error_propagate(errp, err); + return; + } - macio_realize_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4), &err); - if (err) { - error_propagate(errp, err); - return; - } + macio_realize_ide(s, &ns->ide[1], + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_IRQ), + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_DMA_IRQ), + 0x1a, &err); + if (err) { + error_propagate(errp, err); + return; } /* Timer */ @@ -328,8 +337,6 @@ static void macio_newworld_init(Object *obj) NewWorldMacIOState *ns = NEWWORLD_MACIO(obj); int i; - qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs)); - object_property_add_link(obj, "pic", TYPE_OPENPIC, (Object **) &ns->pic, qdev_prop_allow_set_link_before_realize, diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 6a070f13bd..744acdfd2e 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -375,20 +375,6 @@ static void ppc_core99_init(MachineState *machine) /* MacIO */ macio = NEWWORLD_MACIO(pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO)); dev = DEVICE(macio); - qdev_connect_gpio_out(dev, 0, - qdev_get_gpio_in(pic_dev, NEWWORLD_CUDA_IRQ)); - qdev_connect_gpio_out(dev, 1, - qdev_get_gpio_in(pic_dev, NEWWORLD_ESCCB_IRQ)); - qdev_connect_gpio_out(dev, 2, - qdev_get_gpio_in(pic_dev, NEWWORLD_ESCCA_IRQ)); - qdev_connect_gpio_out(dev, 3, - qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_IRQ)); - qdev_connect_gpio_out(dev, 4, - qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_DMA_IRQ)); - qdev_connect_gpio_out(dev, 5, - qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_IRQ)); - qdev_connect_gpio_out(dev, 6, - qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_DMA_IRQ)); qdev_prop_set_uint64(dev, "frequency", tbfreq); object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", &error_abort); diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 64a2584a77..838eaf1db0 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -71,7 +71,6 @@ typedef struct NewWorldMacIOState { /*< public >*/ OpenPICState *pic; - qemu_irq irqs[7]; MACIOIDEState ide[2]; } NewWorldMacIOState; From bce009645b9f1d59195518e35747c8ea30f985f7 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 3 May 2018 23:16:29 +0200 Subject: [PATCH 0224/2380] target/ppc: always set PPC_MEM_TLBIE in pre 2.8 migration hack The pseries-2.7 and older machine types require CPUPPCState::insns_flags to be strictly equal between source and destination. This checking is abusive and breaks migration of KVM guests when the host CPU models are different, even if they are compatible enough to allow the guest to run transparently. This buggy behaviour was fixed for pseries-2.8 and we added some hacks to allow backward migration of older machine types. These hacks assume that the CPU belongs to the POWER8 family, which was true for most KVM based setup we cared about at the time. But now POWER9 systems are coming, and backward migration of pre 2.8 guests running in POWER8 architected mode from a POWER9 host to a POWER8 host is broken: qemu-system-ppc64: error while loading state for instance 0x0 of device 'cpu' qemu-system-ppc64: load of migration failed: Invalid argument This happens because POWER9 doesn't set PPC_MEM_TLBIE in insns_flags, while POWER8 does. Let's force PPC_MEM_TLBIE in the migration hack to fix the issue. This is an acceptable hack because these old machine types only support CPU models that do set PPC_MEM_TLBIE. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- target/ppc/machine.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 3d6434a006..ba1b9e531f 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -212,6 +212,11 @@ static int cpu_pre_save(void *opaque) ; cpu->mig_msr_mask = env->msr_mask & ~metamask; cpu->mig_insns_flags = env->insns_flags & insns_compat_mask; + /* CPU models supported by old machines all have PPC_MEM_TLBIE, + * so we set it unconditionally to allow backward migration from + * a POWER9 host to a POWER8 host. + */ + cpu->mig_insns_flags |= PPC_MEM_TLBIE; cpu->mig_insns_flags2 = env->insns_flags2 & insns_compat_mask2; cpu->mig_nb_BATs = env->nb_BATs; } From aef19c04bf88e0f5f936301e6c29b239e488fbc6 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 3 May 2018 23:16:48 +0200 Subject: [PATCH 0225/2380] spapr: don't migrate "spapr_option_vector_ov5_cas" to pre 2.8 machines a324d6f16697 "spapr: Support ibm,dynamic-memory-v2 property" added a new feature in the set of CAS-negotiatable options. This causes the CAS-negotiated options subsection to be migrated, even for old machine types that don't know about it, and breaks backward migration to QEMU 2.7 and older versions: qemu-system-ppc64: error while loading state for instance 0x0 of device 'spapr' qemu-system-ppc64: load of migration failed: No such file or directory Since this feature only affects boot time behaviour, it should be filtered out when we decide to migrate CAS-negotiated options, like we already do with OV5_FORM1_AFFINITY and OV5_DRCONF_MEMORY. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 535d8276df..19997f66c0 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1849,10 +1849,12 @@ static bool spapr_ov5_cas_needed(void *opaque) * * Thus, for any cases where the set of available CAS-negotiatable * options extends beyond OV5_FORM1_AFFINITY and OV5_DRCONF_MEMORY, we - * include the CAS-negotiated options in the migration stream. + * include the CAS-negotiated options in the migration stream, unless + * if they affect boot time behaviour only. */ spapr_ovec_set(ov5_mask, OV5_FORM1_AFFINITY); spapr_ovec_set(ov5_mask, OV5_DRCONF_MEMORY); + spapr_ovec_set(ov5_mask, OV5_DRMEM_V2); /* spapr_ovec_diff returns true if bits were removed. we avoid using * the mask itself since in the future it's possible "legacy" bits may be From 0550b1206a91d66051a21441a02c4ff126b531fe Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 3 May 2018 23:16:59 +0200 Subject: [PATCH 0226/2380] spapr: don't advertise radix GTSE if max-compat-cpu < power9 On a POWER9 host, if a guest runs in pre POWER9 compat mode, it necessarily uses the hash MMU mode. In this case, we shouldn't advertise radix GTSE in the ibm,arch-vec-5-platform-support DT property as the current code does. The first reason is that it doesn't make sense, and the second one is that causes the CAS-negotiated options subsection to be migrated. This breaks backward migration to QEMU 2.7 and older versions on POWER8 hosts: qemu-system-ppc64: error while loading state for instance 0x0 of device 'spapr' qemu-system-ppc64: load of migration failed: No such file or directory This patch hence initialize CPUs a bit earlier so that we can check the requested compat mode, and don't set OV5_MMU_RADIX_GTSE for power8 and older. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 19997f66c0..32ab3c43b6 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2512,6 +2512,7 @@ static void spapr_machine_init(MachineState *machine) long load_limit, fw_size; char *filename; Error *resize_hpt_err = NULL; + PowerPCCPU *first_ppc_cpu; msi_nonbroken = true; @@ -2592,11 +2593,6 @@ static void spapr_machine_init(MachineState *machine) } spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY); - if (!kvm_enabled() || kvmppc_has_cap_mmu_radix()) { - /* KVM and TCG always allow GTSE with radix... */ - spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE); - } - /* ... but not with hash (currently). */ /* advertise support for dedicated HP event source to guests */ if (spapr->use_hotplug_event_source) { @@ -2614,6 +2610,15 @@ static void spapr_machine_init(MachineState *machine) /* init CPUs */ spapr_init_cpus(spapr); + first_ppc_cpu = POWERPC_CPU(first_cpu); + if ((!kvm_enabled() || kvmppc_has_cap_mmu_radix()) && + ppc_check_compat(first_ppc_cpu, CPU_POWERPC_LOGICAL_3_00, 0, + spapr->max_compat_pvr)) { + /* KVM and TCG always allow GTSE with radix... */ + spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE); + } + /* ... but not with hash (currently). */ + if (kvm_enabled()) { /* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */ kvmppc_enable_logical_ci_hcalls(); From 46cfbf13b06d551072ed17fbfca67c103edf814f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 26 Apr 2018 17:28:05 +0200 Subject: [PATCH 0227/2380] qobject: Use qobject_to() instead of type cast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The proper way to convert from (abstract) QObject to a (concrete) subtype is qobject_to(). Look for offenders that type cast instead: $ git-grep '(Q[A-Z][a-z]* \*)' hmp.c: qmp_device_add((QDict *)qdict, NULL, &err); include/qapi/qmp/qobject.h: return (QObject *)obj; qobject/qobject.c:static void (*qdestroy[QTYPE__MAX])(QObject *) = { tests/check-qdict.c: dst = (QDict *)qdict_crumple(src, &error_abort); The first two cast away const, the third isn't a type cast. Fix the fourth. Signed-off-by: Markus Armbruster Message-Id: <20180426152805.8469-1-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Marc-André Lureau --- tests/check-qdict.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/check-qdict.c b/tests/check-qdict.c index 2e73c2f86e..08d4303e6a 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -657,7 +657,7 @@ static void qdict_crumple_test_empty(void) src = qdict_new(); - dst = (QDict *)qdict_crumple(src, &error_abort); + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); g_assert_cmpint(qdict_size(dst), ==, 0); From 7ee9edfdb117da47c86c9764d90f0be11a648666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 19 Apr 2018 17:01:41 +0200 Subject: [PATCH 0228/2380] qobject: Ensure base is at offset 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All QObject types have the base QObject as their first field. This allows the simplification of qobject_to(). Signed-off-by: Marc-André Lureau Reviewed-by: Eric Blake Message-Id: <20180419150145.24795-2-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Commit message paragraph on type casts dropped, to avoid giving the impression type casting would be okay] Signed-off-by: Markus Armbruster --- include/qapi/qmp/qobject.h | 5 ++--- qobject/qobject.c | 9 +++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index e022707578..5206ff9ee1 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -61,9 +61,8 @@ struct QObject { QEMU_BUILD_BUG_MSG(QTYPE__MAX != 7, "The QTYPE_CAST_TO_* list needs to be extended"); -#define qobject_to(type, obj) ({ \ - QObject *_tmp = qobject_check_type(obj, glue(QTYPE_CAST_TO_, type)); \ - _tmp ? container_of(_tmp, type, base) : (type *)NULL; }) +#define qobject_to(type, obj) \ + ((type *)qobject_check_type(obj, glue(QTYPE_CAST_TO_, type))) /* Initialize an object to default values */ static inline void qobject_init(QObject *obj, QType type) diff --git a/qobject/qobject.c b/qobject/qobject.c index 23600aa1c1..87649c5be5 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -16,6 +16,15 @@ #include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" +QEMU_BUILD_BUG_MSG( + offsetof(QNull, base) != 0 || + offsetof(QNum, base) != 0 || + offsetof(QString, base) != 0 || + offsetof(QDict, base) != 0 || + offsetof(QList, base) != 0 || + offsetof(QBool, base) != 0, + "base qobject must be at offset 0"); + static void (*qdestroy[QTYPE__MAX])(QObject *) = { [QTYPE_NONE] = NULL, /* No such object exists */ [QTYPE_QNULL] = NULL, /* qnull_ is indestructible */ From 3d3eacaeccaab718ea0e2ddaa578bfae9e311c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 19 Apr 2018 17:01:42 +0200 Subject: [PATCH 0229/2380] qobject: use a QObjectBase_ struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By moving the base fields to a QObjectBase_, QObject can be a type which also has a 'base' field. This allows writing a generic QOBJECT() macro that will work with any QObject type, including QObject itself. The container_of() macro ensures that the object to cast has a QObjectBase_ base field, giving some type safety guarantees. QObject must have no members but QObjectBase_ base, or else QOBJECT() breaks. QObjectBase_ is not a typedef and uses a trailing underscore to make it obvious it is not for normal use and to avoid potential abuse. Signed-off-by: Marc-André Lureau Reviewed-by: Eric Blake Message-Id: <20180419150145.24795-3-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- include/qapi/qmp/qbool.h | 2 +- include/qapi/qmp/qdict.h | 2 +- include/qapi/qmp/qlist.h | 2 +- include/qapi/qmp/qnull.h | 2 +- include/qapi/qmp/qnum.h | 2 +- include/qapi/qmp/qobject.h | 31 ++++++++++++++++++++----------- include/qapi/qmp/qstring.h | 2 +- qobject/qobject.c | 12 ++++++------ tests/check-qdict.c | 6 +++--- 9 files changed, 35 insertions(+), 26 deletions(-) diff --git a/include/qapi/qmp/qbool.h b/include/qapi/qmp/qbool.h index b9a44a1bfe..5f61e38e64 100644 --- a/include/qapi/qmp/qbool.h +++ b/include/qapi/qmp/qbool.h @@ -17,7 +17,7 @@ #include "qapi/qmp/qobject.h" struct QBool { - QObject base; + struct QObjectBase_ base; bool value; }; diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index 2cc3e906f7..921a28d2d3 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -25,7 +25,7 @@ typedef struct QDictEntry { } QDictEntry; struct QDict { - QObject base; + struct QObjectBase_ base; size_t size; QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX]; }; diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h index 5c673acb06..8d2c32ca28 100644 --- a/include/qapi/qmp/qlist.h +++ b/include/qapi/qmp/qlist.h @@ -22,7 +22,7 @@ typedef struct QListEntry { } QListEntry; struct QList { - QObject base; + struct QObjectBase_ base; QTAILQ_HEAD(,QListEntry) head; }; diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h index c992ee2ae1..e8ea2c315a 100644 --- a/include/qapi/qmp/qnull.h +++ b/include/qapi/qmp/qnull.h @@ -16,7 +16,7 @@ #include "qapi/qmp/qobject.h" struct QNull { - QObject base; + struct QObjectBase_ base; }; extern QNull qnull_; diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h index 3e47475b2c..45bf02a036 100644 --- a/include/qapi/qmp/qnum.h +++ b/include/qapi/qmp/qnum.h @@ -45,7 +45,7 @@ typedef enum { * convert under the hood. */ struct QNum { - QObject base; + struct QObjectBase_ base; QNumKind kind; union { int64_t i64; diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index 5206ff9ee1..a713c0165b 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -34,13 +34,21 @@ #include "qapi/qapi-builtin-types.h" -struct QObject { +/* Not for use outside include/qapi/qmp/ */ +struct QObjectBase_ { QType type; size_t refcnt; }; -/* Get the 'base' part of an object */ -#define QOBJECT(obj) (&(obj)->base) +/* this struct must have no other members than base */ +struct QObject { + struct QObjectBase_ base; +}; + +#define QOBJECT(obj) ({ \ + typeof(obj) _obj = (obj); \ + _obj ? container_of(&(_obj)->base, QObject, base) : NULL; \ +}) /* High-level interface for qobject_incref() */ #define QINCREF(obj) \ @@ -68,8 +76,8 @@ QEMU_BUILD_BUG_MSG(QTYPE__MAX != 7, static inline void qobject_init(QObject *obj, QType type) { assert(QTYPE_NONE < type && type < QTYPE__MAX); - obj->refcnt = 1; - obj->type = type; + obj->base.refcnt = 1; + obj->base.type = type; } /** @@ -77,8 +85,9 @@ static inline void qobject_init(QObject *obj, QType type) */ static inline void qobject_incref(QObject *obj) { - if (obj) - obj->refcnt++; + if (obj) { + obj->base.refcnt++; + } } /** @@ -101,8 +110,8 @@ void qobject_destroy(QObject *obj); */ static inline void qobject_decref(QObject *obj) { - assert(!obj || obj->refcnt); - if (obj && --obj->refcnt == 0) { + assert(!obj || obj->base.refcnt); + if (obj && --obj->base.refcnt == 0) { qobject_destroy(obj); } } @@ -112,8 +121,8 @@ static inline void qobject_decref(QObject *obj) */ static inline QType qobject_type(const QObject *obj) { - assert(QTYPE_NONE < obj->type && obj->type < QTYPE__MAX); - return obj->type; + assert(QTYPE_NONE < obj->base.type && obj->base.type < QTYPE__MAX); + return obj->base.type; } /** diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h index 30ae260a7f..b3b3d444d2 100644 --- a/include/qapi/qmp/qstring.h +++ b/include/qapi/qmp/qstring.h @@ -16,7 +16,7 @@ #include "qapi/qmp/qobject.h" struct QString { - QObject base; + struct QObjectBase_ base; char *string; size_t length; size_t capacity; diff --git a/qobject/qobject.c b/qobject/qobject.c index 87649c5be5..cf4b7e229e 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -37,9 +37,9 @@ static void (*qdestroy[QTYPE__MAX])(QObject *) = { void qobject_destroy(QObject *obj) { - assert(!obj->refcnt); - assert(QTYPE_QNULL < obj->type && obj->type < QTYPE__MAX); - qdestroy[obj->type](obj); + assert(!obj->base.refcnt); + assert(QTYPE_QNULL < obj->base.type && obj->base.type < QTYPE__MAX); + qdestroy[obj->base.type](obj); } @@ -62,11 +62,11 @@ bool qobject_is_equal(const QObject *x, const QObject *y) return true; } - if (!x || !y || x->type != y->type) { + if (!x || !y || x->base.type != y->base.type) { return false; } - assert(QTYPE_NONE < x->type && x->type < QTYPE__MAX); + assert(QTYPE_NONE < x->base.type && x->base.type < QTYPE__MAX); - return qis_equal[x->type](x, y); + return qis_equal[x->base.type](x, y); } diff --git a/tests/check-qdict.c b/tests/check-qdict.c index 08d4303e6a..07bb8f4564 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -570,11 +570,11 @@ static void qdict_join_test(void) } /* Check the references */ - g_assert(qdict_get(dict1, "foo")->refcnt == 1); - g_assert(qdict_get(dict1, "bar")->refcnt == 1); + g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); + g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); if (!overwrite) { - g_assert(qdict_get(dict2, "foo")->refcnt == 1); + g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); } /* Clean up */ From cb3e7f08aeaab0ab13e629ce8496dca150a449ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 19 Apr 2018 17:01:43 +0200 Subject: [PATCH 0230/2380] qobject: Replace qobject_incref/QINCREF qobject_decref/QDECREF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we can safely call QOBJECT() on QObject * as well as its subtypes, we can have macros qobject_ref() / qobject_unref() that work everywhere instead of having to use QINCREF() / QDECREF() for QObject and qobject_incref() / qobject_decref() for its subtypes. The replacement is mechanical, except I broke a long line, and added a cast in monitor_qmp_cleanup_req_queue_locked(). Unlike qobject_decref(), qobject_unref() doesn't accept void *. Note that the new macros evaluate their argument exactly once, thus no need to shout them. Signed-off-by: Marc-André Lureau Reviewed-by: Eric Blake Message-Id: <20180419150145.24795-4-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Rebased, semantic conflict resolved, commit message improved] Signed-off-by: Markus Armbruster --- block.c | 78 +++++++++++----------- block/blkdebug.c | 4 +- block/blkverify.c | 4 +- block/crypto.c | 4 +- block/gluster.c | 4 +- block/iscsi.c | 2 +- block/nbd.c | 4 +- block/nfs.c | 4 +- block/null.c | 2 +- block/nvme.c | 2 +- block/parallels.c | 4 +- block/qapi.c | 2 +- block/qcow.c | 8 +-- block/qcow2.c | 8 +-- block/qed.c | 4 +- block/quorum.c | 2 +- block/rbd.c | 14 ++-- block/sheepdog.c | 12 ++-- block/snapshot.c | 4 +- block/ssh.c | 4 +- block/vdi.c | 2 +- block/vhdx.c | 4 +- block/vpc.c | 4 +- block/vvfat.c | 2 +- block/vxhs.c | 2 +- blockdev.c | 16 ++--- docs/devel/qapi-code-gen.txt | 2 +- hw/i386/acpi-build.c | 12 ++-- hw/ppc/spapr_drc.c | 2 +- hw/usb/xen-usb.c | 4 +- include/qapi/qmp/qnull.h | 2 +- include/qapi/qmp/qobject.h | 40 +++++------ migration/migration.c | 4 +- migration/qjson.c | 2 +- monitor.c | 50 +++++++------- qapi/qapi-dealloc-visitor.c | 4 +- qapi/qmp-dispatch.c | 6 +- qapi/qobject-input-visitor.c | 8 +-- qapi/qobject-output-visitor.c | 8 +-- qemu-img.c | 18 ++--- qemu-io.c | 6 +- qga/main.c | 12 ++-- qmp.c | 4 +- qobject/json-parser.c | 10 +-- qobject/qdict.c | 38 +++++------ qobject/qjson.c | 2 +- qobject/qlist.c | 4 +- qom/object.c | 16 ++--- qom/object_interfaces.c | 2 +- scripts/coccinelle/qobject.cocci | 8 +-- scripts/qapi/events.py | 2 +- target/ppc/translate_init.c | 2 +- target/s390x/cpu_models.c | 2 +- tests/ahci-test.c | 6 +- tests/check-qdict.c | 100 ++++++++++++++-------------- tests/check-qjson.c | 84 +++++++++++------------ tests/check-qlist.c | 8 +-- tests/check-qlit.c | 10 +-- tests/check-qnull.c | 10 +-- tests/check-qnum.c | 28 ++++---- tests/check-qobject.c | 2 +- tests/check-qstring.c | 10 +-- tests/cpu-plug-test.c | 4 +- tests/device-introspect-test.c | 24 +++---- tests/drive_del-test.c | 4 +- tests/libqos/libqos.c | 8 +-- tests/libqos/pci-pc.c | 2 +- tests/libqtest.c | 24 +++---- tests/machine-none-test.c | 2 +- tests/migration-test.c | 26 ++++---- tests/numa-test.c | 16 ++--- tests/pvpanic-test.c | 2 +- tests/q35-test.c | 2 +- tests/qmp-test.c | 38 +++++------ tests/qom-test.c | 8 +-- tests/tco-test.c | 12 ++-- tests/test-char.c | 2 +- tests/test-keyval.c | 82 +++++++++++------------ tests/test-netfilter.c | 26 ++++---- tests/test-qemu-opts.c | 14 ++-- tests/test-qga.c | 76 ++++++++++----------- tests/test-qmp-cmds.c | 24 +++---- tests/test-qmp-event.c | 2 +- tests/test-qobject-input-visitor.c | 10 +-- tests/test-qobject-output-visitor.c | 18 ++--- tests/test-visitor-serialization.c | 6 +- tests/test-x86-cpuid-compat.c | 14 ++-- tests/tmp105-test.c | 4 +- tests/vhost-user-test.c | 6 +- tests/virtio-net-test.c | 6 +- tests/vmgenid-test.c | 2 +- tests/wdt_ib700-test.c | 14 ++-- util/keyval.c | 12 ++-- util/qemu-config.c | 4 +- 94 files changed, 609 insertions(+), 613 deletions(-) diff --git a/block.c b/block.c index a2caadf0a0..55a79845be 100644 --- a/block.c +++ b/block.c @@ -1227,9 +1227,9 @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp); if (ret < 0) { - QDECREF(bs->explicit_options); + qobject_unref(bs->explicit_options); bs->explicit_options = NULL; - QDECREF(bs->options); + qobject_unref(bs->options); bs->options = NULL; bdrv_unref(bs); return NULL; @@ -1460,7 +1460,7 @@ static QDict *parse_json_filename(const char *filename, Error **errp) options = qobject_to(QDict, options_obj); if (!options) { - qobject_decref(options_obj); + qobject_unref(options_obj); error_setg(errp, "Invalid JSON object given"); return NULL; } @@ -1490,7 +1490,7 @@ static void parse_json_protocol(QDict *options, const char **pfilename, /* Options given in the filename have lower priority than options * specified directly */ qdict_join(options, json_options, false); - QDECREF(json_options); + qobject_unref(json_options); *pfilename = NULL; } @@ -2273,7 +2273,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, if (reference || qdict_haskey(options, "file.filename")) { backing_filename[0] = '\0'; } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { - QDECREF(options); + qobject_unref(options); goto free_exit; } else { bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX, @@ -2281,7 +2281,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, if (local_err) { ret = -EINVAL; error_propagate(errp, local_err); - QDECREF(options); + qobject_unref(options); goto free_exit; } } @@ -2289,7 +2289,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, if (!bs->drv || !bs->drv->supports_backing) { ret = -EINVAL; error_setg(errp, "Driver doesn't support backing files"); - QDECREF(options); + qobject_unref(options); goto free_exit; } @@ -2323,7 +2323,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, free_exit: g_free(backing_filename); - QDECREF(tmp_parent_options); + qobject_unref(tmp_parent_options); return ret; } @@ -2356,7 +2356,7 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, error_setg(errp, "A block device must be specified for \"%s\"", bdref_key); } - QDECREF(image_options); + qobject_unref(image_options); goto done; } @@ -2449,7 +2449,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) obj = NULL; fail: - qobject_decref(obj); + qobject_unref(obj); visit_free(v); return bs; } @@ -2519,7 +2519,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, } out: - QDECREF(snapshot_options); + qobject_unref(snapshot_options); g_free(tmp_filename); return bs_snapshot; } @@ -2530,7 +2530,7 @@ out: * options is a QDict of options to pass to the block drivers, or NULL for an * empty set of options. The reference to the QDict belongs to the block layer * after the call (even on failure), so if the caller intends to reuse the - * dictionary, it needs to use QINCREF() before calling bdrv_open. + * dictionary, it needs to use qobject_ref() before calling bdrv_open. * * If *pbs is NULL, a new BDS will be created with a pointer to it stored there. * If it is not NULL, the referenced BDS will be reused. @@ -2561,7 +2561,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, if (reference) { bool options_non_empty = options ? qdict_size(options) : false; - QDECREF(options); + qobject_unref(options); if (filename || options_non_empty) { error_setg(errp, "Cannot reference an existing block device with " @@ -2752,7 +2752,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, bdrv_parent_cb_change_media(bs, true); - QDECREF(options); + qobject_unref(options); /* For snapshot=on, create a temporary qcow2 overlay. bs points to the * temporary snapshot afterwards. */ @@ -2776,10 +2776,10 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, fail: blk_unref(file); - QDECREF(snapshot_options); - QDECREF(bs->explicit_options); - QDECREF(bs->options); - QDECREF(options); + qobject_unref(snapshot_options); + qobject_unref(bs->explicit_options); + qobject_unref(bs->options); + qobject_unref(options); bs->options = NULL; bs->explicit_options = NULL; bdrv_unref(bs); @@ -2788,8 +2788,8 @@ fail: close_and_fail: bdrv_unref(bs); - QDECREF(snapshot_options); - QDECREF(options); + qobject_unref(snapshot_options); + qobject_unref(options); error_propagate(errp, local_err); return NULL; } @@ -2884,7 +2884,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, old_options = qdict_clone_shallow(bs->explicit_options); } bdrv_join_options(bs, options, old_options); - QDECREF(old_options); + qobject_unref(old_options); explicit_options = qdict_clone_shallow(options); @@ -2899,13 +2899,13 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, qemu_opts_absorb_qdict(opts, options_copy, NULL); update_flags_from_options(&flags, opts); qemu_opts_del(opts); - QDECREF(options_copy); + qobject_unref(options_copy); } /* Old values are used for options that aren't set yet */ old_options = qdict_clone_shallow(bs->options); bdrv_join_options(bs, options, old_options); - QDECREF(old_options); + qobject_unref(old_options); /* bdrv_open_inherit() sets and clears some additional flags internally */ flags &= ~BDRV_O_PROTOCOL; @@ -2917,8 +2917,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, bs_entry = g_new0(BlockReopenQueueEntry, 1); QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry); } else { - QDECREF(bs_entry->state.options); - QDECREF(bs_entry->state.explicit_options); + qobject_unref(bs_entry->state.options); + qobject_unref(bs_entry->state.explicit_options); } bs_entry->state.bs = bs; @@ -3008,9 +3008,9 @@ cleanup: if (ret && bs_entry->prepared) { bdrv_reopen_abort(&bs_entry->state); } else if (ret) { - QDECREF(bs_entry->state.explicit_options); + qobject_unref(bs_entry->state.explicit_options); } - QDECREF(bs_entry->state.options); + qobject_unref(bs_entry->state.options); g_free(bs_entry); } g_free(bs_queue); @@ -3253,7 +3253,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) } /* set BDS specific flags now */ - QDECREF(bs->explicit_options); + qobject_unref(bs->explicit_options); bs->explicit_options = reopen_state->explicit_options; bs->open_flags = reopen_state->flags; @@ -3296,7 +3296,7 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state) drv->bdrv_reopen_abort(reopen_state); } - QDECREF(reopen_state->explicit_options); + qobject_unref(reopen_state->explicit_options); bdrv_abort_perm_update(reopen_state->bs); } @@ -3343,11 +3343,11 @@ static void bdrv_close(BlockDriverState *bs) bs->total_sectors = 0; bs->encrypted = false; bs->sg = false; - QDECREF(bs->options); - QDECREF(bs->explicit_options); + qobject_unref(bs->options); + qobject_unref(bs->explicit_options); bs->options = NULL; bs->explicit_options = NULL; - QDECREF(bs->full_open_options); + qobject_unref(bs->full_open_options); bs->full_open_options = NULL; bdrv_release_named_dirty_bitmaps(bs); @@ -5134,7 +5134,7 @@ static bool append_open_options(QDict *d, BlockDriverState *bs) continue; } - qobject_incref(qdict_entry_value(entry)); + qobject_ref(qdict_entry_value(entry)); qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry)); found_any = true; } @@ -5174,21 +5174,21 @@ void bdrv_refresh_filename(BlockDriverState *bs) * information before refreshing it */ bs->exact_filename[0] = '\0'; if (bs->full_open_options) { - QDECREF(bs->full_open_options); + qobject_unref(bs->full_open_options); bs->full_open_options = NULL; } opts = qdict_new(); append_open_options(opts, bs); drv->bdrv_refresh_filename(bs, opts); - QDECREF(opts); + qobject_unref(opts); } else if (bs->file) { /* Try to reconstruct valid information from the underlying file */ bool has_open_options; bs->exact_filename[0] = '\0'; if (bs->full_open_options) { - QDECREF(bs->full_open_options); + qobject_unref(bs->full_open_options); bs->full_open_options = NULL; } @@ -5207,12 +5207,12 @@ void bdrv_refresh_filename(BlockDriverState *bs) * suffices without querying the (exact_)filename of this BDS. */ if (bs->file->bs->full_open_options) { qdict_put_str(opts, "driver", drv->format_name); - QINCREF(bs->file->bs->full_open_options); + qobject_ref(bs->file->bs->full_open_options); qdict_put(opts, "file", bs->file->bs->full_open_options); bs->full_open_options = opts; } else { - QDECREF(opts); + qobject_unref(opts); } } else if (!bs->full_open_options && qdict_size(bs->options)) { /* There is no underlying file BDS (at least referenced by BDS.file), @@ -5246,7 +5246,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) QString *json = qobject_to_json(QOBJECT(bs->full_open_options)); snprintf(bs->filename, sizeof(bs->filename), "json:%s", qstring_get_str(json)); - QDECREF(json); + qobject_unref(json); } } diff --git a/block/blkdebug.c b/block/blkdebug.c index 589712475a..689703d386 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -845,12 +845,12 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) opts = qdict_new(); qdict_put_str(opts, "driver", "blkdebug"); - QINCREF(bs->file->bs->full_open_options); + qobject_ref(bs->file->bs->full_open_options); qdict_put(opts, "image", bs->file->bs->full_open_options); for (e = qdict_first(options); e; e = qdict_next(options, e)) { if (strcmp(qdict_entry_key(e), "x-image")) { - qobject_incref(qdict_entry_value(e)); + qobject_ref(qdict_entry_value(e)); qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); } } diff --git a/block/blkverify.c b/block/blkverify.c index 331365be33..3cffcb1ca6 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -291,9 +291,9 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) QDict *opts = qdict_new(); qdict_put_str(opts, "driver", "blkverify"); - QINCREF(bs->file->bs->full_open_options); + qobject_ref(bs->file->bs->full_open_options); qdict_put(opts, "raw", bs->file->bs->full_open_options); - QINCREF(s->test_file->bs->full_open_options); + qobject_ref(s->test_file->bs->full_open_options); qdict_put(opts, "test", s->test_file->bs->full_open_options); bs->full_open_options = opts; diff --git a/block/crypto.c b/block/crypto.c index bc6c7e3795..7e7ad2d2a6 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -305,7 +305,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, ret = 0; cleanup: - QDECREF(cryptoopts); + qobject_unref(cryptoopts); qapi_free_QCryptoBlockOpenOptions(open_opts); return ret; } @@ -635,7 +635,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, fail: bdrv_unref(bs); qapi_free_QCryptoBlockCreateOptions(create_opts); - QDECREF(cryptoopts); + qobject_unref(cryptoopts); return ret; } diff --git a/block/gluster.c b/block/gluster.c index 4adc1a875b..55be566f6d 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -650,7 +650,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, } gsconf = NULL; - QDECREF(backing_options); + qobject_unref(backing_options); backing_options = NULL; g_free(str); str = NULL; @@ -663,7 +663,7 @@ out: qapi_free_SocketAddress(gsconf); qemu_opts_del(opts); g_free(str); - QDECREF(backing_options); + qobject_unref(backing_options); errno = EINVAL; return -errno; } diff --git a/block/iscsi.c b/block/iscsi.c index f5aecfc883..d19ae0e398 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2143,7 +2143,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt } else { ret = iscsi_open(bs, bs_options, 0, NULL); } - QDECREF(bs_options); + qobject_unref(bs_options); if (ret != 0) { goto out; diff --git a/block/nbd.c b/block/nbd.c index 1e2b3ba2d3..3e1693cc55 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -293,8 +293,8 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, } done: - QDECREF(addr); - qobject_decref(crumpled_addr); + qobject_unref(addr); + qobject_unref(crumpled_addr); visit_free(iv); return saddr; } diff --git a/block/nfs.c b/block/nfs.c index 2577df4b26..66fddf12d4 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -567,7 +567,7 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); visit_free(v); - qobject_decref(crumpled); + qobject_unref(crumpled); if (local_err) { return NULL; @@ -683,7 +683,7 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts, ret = 0; out: - QDECREF(options); + qobject_unref(options); qapi_free_BlockdevCreateOptions(create_options); return ret; } diff --git a/block/null.c b/block/null.c index 806a8631e4..700a2d0857 100644 --- a/block/null.c +++ b/block/null.c @@ -244,7 +244,7 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs, static void null_refresh_filename(BlockDriverState *bs, QDict *opts) { - QINCREF(opts); + qobject_ref(opts); qdict_del(opts, "filename"); if (!qdict_size(opts)) { diff --git a/block/nvme.c b/block/nvme.c index c4f3a7bc94..e192da9ee1 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1073,7 +1073,7 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state, static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) { - QINCREF(opts); + qobject_ref(opts); qdict_del(opts, "filename"); if (!qdict_size(opts)) { diff --git a/block/parallels.c b/block/parallels.c index 799215e079..045810d00f 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -651,7 +651,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, qdict_put_str(qdict, "file", bs->node_name); qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); + qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; @@ -682,7 +682,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, ret = 0; done: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; diff --git a/block/qapi.c b/block/qapi.c index 04c6fc69b9..e12968fec8 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -773,7 +773,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, visit_complete(v, &obj); data = qdict_get(qobject_to(QDict, obj), "data"); dump_qobject(func_fprintf, f, 1, data); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); } diff --git a/block/qcow.c b/block/qcow.c index f92891676c..4b2f7db74c 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -315,7 +315,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - QDECREF(encryptopts); + qobject_unref(encryptopts); qapi_free_QCryptoBlockOpenOptions(crypto_opts); qemu_co_mutex_init(&s->lock); return 0; @@ -326,7 +326,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, g_free(s->cluster_cache); g_free(s->cluster_data); qcrypto_block_free(s->crypto); - QDECREF(encryptopts); + qobject_unref(encryptopts); qapi_free_QCryptoBlockOpenOptions(crypto_opts); return ret; } @@ -995,7 +995,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, qdict_put_str(qdict, "file", bs->node_name); qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); + qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; @@ -1025,7 +1025,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, ret = 0; fail: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; diff --git a/block/qcow2.c b/block/qcow2.c index ef68772aca..2f36e632f9 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1063,7 +1063,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, ret = 0; fail: - QDECREF(encryptopts); + qobject_unref(encryptopts); qemu_opts_del(opts); opts = NULL; return ret; @@ -2183,7 +2183,7 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, qemu_co_mutex_lock(&s->lock); ret = qcow2_do_open(bs, options, flags, &local_err); qemu_co_mutex_unlock(&s->lock); - QDECREF(options); + qobject_unref(options); if (local_err) { error_propagate(errp, local_err); error_prepend(errp, "Could not reopen qcow2 layer: "); @@ -3139,7 +3139,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt /* Now get the QAPI type BlockdevCreateOptions */ qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); + qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; @@ -3168,7 +3168,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt ret = 0; finish: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; diff --git a/block/qed.c b/block/qed.c index 35ff505066..1db8eaf241 100644 --- a/block/qed.c +++ b/block/qed.c @@ -763,7 +763,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, qdict_put_str(qdict, "file", bs->node_name); qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); + qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; @@ -789,7 +789,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, ret = bdrv_qed_co_create(create_options, errp); fail: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; diff --git a/block/quorum.c b/block/quorum.c index cfe484a945..862cea366d 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -1082,7 +1082,7 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) children = qlist_new(); for (i = 0; i < s->num_children; i++) { - QINCREF(s->children[i]->bs->full_open_options); + qobject_ref(s->children[i]->bs->full_open_options); qlist_append(children, s->children[i]->bs->full_open_options); } diff --git a/block/rbd.c b/block/rbd.c index c9359d0ad8..a14b42fcde 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -226,7 +226,7 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, done: g_free(buf); - QDECREF(keypairs); + qobject_unref(keypairs); return; } @@ -275,17 +275,17 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json, key = qstring_get_str(name); ret = rados_conf_set(cluster, key, qstring_get_str(value)); - QDECREF(value); + qobject_unref(value); if (ret < 0) { error_setg_errno(errp, -ret, "invalid conf option %s", key); - QDECREF(name); + qobject_unref(name); ret = -EINVAL; break; } - QDECREF(name); + qobject_unref(name); } - QDECREF(keypairs); + qobject_unref(keypairs); return ret; } @@ -449,7 +449,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, } exit: - QDECREF(options); + qobject_unref(options); qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -664,7 +664,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); visit_free(v); - qobject_decref(crumpled); + qobject_unref(crumpled); if (local_err) { error_propagate(errp, local_err); diff --git a/block/sheepdog.c b/block/sheepdog.c index 387f59c8aa..07529f4b1b 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -567,8 +567,8 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp) done: visit_free(iv); - qobject_decref(crumpled_server); - QDECREF(server); + qobject_unref(crumpled_server); + qobject_unref(server); return saddr; } @@ -1883,7 +1883,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size, if (local_err) { error_propagate(errp, local_err); - qobject_decref(obj); + qobject_unref(obj); return -EINVAL; } @@ -1901,7 +1901,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size, ret = sd_prealloc(bs, 0, size, errp); fail: bdrv_unref(bs); - QDECREF(qdict); + qobject_unref(qdict); return ret; } @@ -2226,7 +2226,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); - qobject_decref(crumpled); + qobject_unref(crumpled); if (local_err) { error_propagate(errp, local_err); @@ -2252,7 +2252,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, ret = sd_co_create(create_options, errp); fail: qapi_free_BlockdevCreateOptions(create_options); - QDECREF(qdict); + qobject_unref(qdict); return ret; } diff --git a/block/snapshot.c b/block/snapshot.c index eacc1f19a2..2953d96c06 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -214,7 +214,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, bdrv_ref(file); qdict_extract_subqdict(options, &file_options, "file."); - QDECREF(file_options); + qobject_unref(file_options); qdict_put_str(options, "file", bdrv_get_node_name(file)); drv->bdrv_close(bs); @@ -223,7 +223,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, ret = bdrv_snapshot_goto(file, snapshot_id, errp); open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); - QDECREF(options); + qobject_unref(options); if (open_ret < 0) { bdrv_unref(file); bs->drv = NULL; diff --git a/block/ssh.c b/block/ssh.c index ab3acf0c22..412a1bfc17 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -638,7 +638,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) v = qobject_input_visitor_new(crumpled); visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); visit_free(v); - qobject_decref(crumpled); + qobject_unref(crumpled); if (local_err) { error_propagate(errp, local_err); @@ -917,7 +917,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, ret = ssh_co_create(create_options, errp); out: - QDECREF(uri_options); + qobject_unref(uri_options); qapi_free_BlockdevCreateOptions(create_options); return ret; } diff --git a/block/vdi.c b/block/vdi.c index 4a2d1ff88d..96a22b8e83 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -951,7 +951,7 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, /* Create the vdi image (format layer) */ ret = vdi_co_do_create(create_options, block_size, errp); done: - QDECREF(qdict); + qobject_unref(qdict); qapi_free_BlockdevCreateOptions(create_options); bdrv_unref(bs_file); return ret; diff --git a/block/vhdx.c b/block/vhdx.c index 6ac0424f61..c3a4220a35 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -2003,7 +2003,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, qdict_put_str(qdict, "file", bs->node_name); qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); + qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; @@ -2049,7 +2049,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, ret = vhdx_co_create(create_options, errp); fail: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; diff --git a/block/vpc.c b/block/vpc.c index 44f99a4d1b..0ebfcd3cc8 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -1119,7 +1119,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, qdict_put_str(qdict, "file", bs->node_name); qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); + qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; @@ -1157,7 +1157,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, ret = vpc_co_create(create_options, errp); fail: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; diff --git a/block/vvfat.c b/block/vvfat.c index 1569783b0f..662dca0114 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -3179,7 +3179,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) qdict_put_str(options, "write-target.driver", "qcow"); s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs, &child_vvfat_qcow, false, errp); - QDECREF(options); + qobject_unref(options); if (!s->qcow) { ret = -EINVAL; goto err; diff --git a/block/vxhs.c b/block/vxhs.c index 75cc6c8672..55ae1a666e 100644 --- a/block/vxhs.c +++ b/block/vxhs.c @@ -396,7 +396,7 @@ static int vxhs_open(BlockDriverState *bs, QDict *options, out: g_free(of_vsa_addr); - QDECREF(backing_options); + qobject_unref(backing_options); qemu_opts_del(tcp_opts); qemu_opts_del(opts); g_free(cacert); diff --git a/blockdev.c b/blockdev.c index c31bf3d98d..3808b1fc00 100644 --- a/blockdev.c +++ b/blockdev.c @@ -576,7 +576,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, blk_rs->read_only = read_only; blk_rs->detect_zeroes = detect_zeroes; - QDECREF(bs_opts); + qobject_unref(bs_opts); } else { if (file && !*file) { file = NULL; @@ -632,16 +632,16 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, err_no_bs_opts: qemu_opts_del(opts); - QDECREF(interval_dict); - QDECREF(interval_list); + qobject_unref(interval_dict); + qobject_unref(interval_list); return blk; early_err: qemu_opts_del(opts); - QDECREF(interval_dict); - QDECREF(interval_list); + qobject_unref(interval_dict); + qobject_unref(interval_list); err_no_opts: - QDECREF(bs_opts); + qobject_unref(bs_opts); return NULL; } @@ -1130,7 +1130,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) fail: qemu_opts_del(legacy_opts); - QDECREF(bs_opts); + qobject_unref(bs_opts); return dinfo; } @@ -4022,7 +4022,7 @@ void hmp_drive_add_node(Monitor *mon, const char *optstr) qdict = qemu_opts_to_qdict(opts, NULL); if (!qdict_get_try_str(qdict, "node-name")) { - QDECREF(qdict); + qobject_unref(qdict); error_report("'node-name' needs to be specified"); goto out; } diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index a569d24745..b9b6eabd08 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -1340,7 +1340,7 @@ Example: emit(EXAMPLE_QAPI_EVENT_MY_EVENT, qmp, &err); error_propagate(errp, err); - QDECREF(qmp); + qobject_unref(qmp); } const QEnumLookup example_QAPIEvent_lookup = { diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 3cf2a1679c..c634dcad1d 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -198,21 +198,21 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) } else { pm->s3_disabled = false; } - qobject_decref(o); + qobject_unref(o); o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_DISABLED, NULL); if (o) { pm->s4_disabled = qnum_get_uint(qobject_to(QNum, o)); } else { pm->s4_disabled = false; } - qobject_decref(o); + qobject_unref(o); o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_VAL, NULL); if (o) { pm->s4_val = qnum_get_uint(qobject_to(QNum, o)); } else { pm->s4_val = false; } - qobject_decref(o); + qobject_unref(o); pm->pcihp_bridge_en = object_property_get_bool(obj, "acpi-pci-hotplug-with-bridge-support", @@ -570,7 +570,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, } } aml_append(parent_scope, method); - qobject_decref(bsel); + qobject_unref(bsel); } /** @@ -2614,12 +2614,12 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) return false; } mcfg->mcfg_base = qnum_get_uint(qobject_to(QNum, o)); - qobject_decref(o); + qobject_unref(o); o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL); assert(o); mcfg->mcfg_size = qnum_get_uint(qobject_to(QNum, o)); - qobject_decref(o); + qobject_unref(o); return true; } diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index aa251133de..8a045d6b93 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -305,7 +305,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, if (!drc->fdt) { visit_type_null(v, NULL, &null, errp); - QDECREF(null); + qobject_unref(null); return; } diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 3beeb0d170..b3a90c0e68 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -763,7 +763,7 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, if (!usbif->ports[port - 1].dev) { goto err; } - QDECREF(qdict); + qobject_unref(qdict); speed = usbif->ports[port - 1].dev->speed; switch (speed) { case USB_SPEED_LOW: @@ -796,7 +796,7 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, return; err: - QDECREF(qdict); + qobject_unref(qdict); xen_pv_printf(&usbif->xendev, 0, "device %s could not be opened\n", busid); } diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h index e8ea2c315a..75b29c6a39 100644 --- a/include/qapi/qmp/qnull.h +++ b/include/qapi/qmp/qnull.h @@ -23,7 +23,7 @@ extern QNull qnull_; static inline QNull *qnull(void) { - QINCREF(&qnull_); + qobject_ref(&qnull_); return &qnull_; } diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index a713c0165b..e20006faf5 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -15,17 +15,17 @@ * ------------------------------------ * * - Returning references: A function that returns an object may - * return it as either a weak or a strong reference. If the reference - * is strong, you are responsible for calling QDECREF() on the reference - * when you are done. + * return it as either a weak or a strong reference. If the + * reference is strong, you are responsible for calling + * qobject_unref() on the reference when you are done. * * If the reference is weak, the owner of the reference may free it at * any time in the future. Before storing the reference anywhere, you - * should call QINCREF() to make the reference strong. + * should call qobject_ref() to make the reference strong. * * - Transferring ownership: when you transfer ownership of a reference * by calling a function, you are no longer responsible for calling - * QDECREF() when the reference is no longer needed. In other words, + * qobject_unref() when the reference is no longer needed. In other words, * when the function returns you must behave as if the reference to the * passed object was weak. */ @@ -50,14 +50,6 @@ struct QObject { _obj ? container_of(&(_obj)->base, QObject, base) : NULL; \ }) -/* High-level interface for qobject_incref() */ -#define QINCREF(obj) \ - qobject_incref(QOBJECT(obj)) - -/* High-level interface for qobject_decref() */ -#define QDECREF(obj) \ - qobject_decref(obj ? QOBJECT(obj) : NULL) - /* Required for qobject_to() */ #define QTYPE_CAST_TO_QNull QTYPE_QNULL #define QTYPE_CAST_TO_QNum QTYPE_QNUM @@ -80,10 +72,7 @@ static inline void qobject_init(QObject *obj, QType type) obj->base.type = type; } -/** - * qobject_incref(): Increment QObject's reference count - */ -static inline void qobject_incref(QObject *obj) +static inline void qobject_ref_impl(QObject *obj) { if (obj) { obj->base.refcnt++; @@ -104,11 +93,7 @@ bool qobject_is_equal(const QObject *x, const QObject *y); */ void qobject_destroy(QObject *obj); -/** - * qobject_decref(): Decrement QObject's reference count, deallocate - * when it reaches zero - */ -static inline void qobject_decref(QObject *obj) +static inline void qobject_unref_impl(QObject *obj) { assert(!obj || obj->base.refcnt); if (obj && --obj->base.refcnt == 0) { @@ -116,6 +101,17 @@ static inline void qobject_decref(QObject *obj) } } +/** + * qobject_ref(): Increment QObject's reference count + */ +#define qobject_ref(obj) qobject_ref_impl(QOBJECT(obj)) + +/** + * qobject_unref(): Decrement QObject's reference count, deallocate + * when it reaches zero + */ +#define qobject_unref(obj) qobject_unref_impl(QOBJECT(obj)) + /** * qobject_type(): Return the QObject's type */ diff --git a/migration/migration.c b/migration/migration.c index 0bdb28e144..35f2781b03 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1040,14 +1040,14 @@ void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) /* TODO Rewrite "" to null instead */ if (params->has_tls_creds && params->tls_creds->type == QTYPE_QNULL) { - QDECREF(params->tls_creds->u.n); + qobject_unref(params->tls_creds->u.n); params->tls_creds->type = QTYPE_QSTRING; params->tls_creds->u.s = strdup(""); } /* TODO Rewrite "" to null instead */ if (params->has_tls_hostname && params->tls_hostname->type == QTYPE_QNULL) { - QDECREF(params->tls_hostname->u.n); + qobject_unref(params->tls_hostname->u.n); params->tls_hostname->type = QTYPE_QSTRING; params->tls_hostname->u.s = strdup(""); } diff --git a/migration/qjson.c b/migration/qjson.c index 9d7f6eb9eb..e9889bdcb0 100644 --- a/migration/qjson.c +++ b/migration/qjson.c @@ -109,6 +109,6 @@ void qjson_finish(QJSON *json) void qjson_destroy(QJSON *json) { - QDECREF(json->str); + qobject_unref(json->str); g_free(json); } diff --git a/monitor.c b/monitor.c index 39f8ee17ba..4f43eee2bb 100644 --- a/monitor.c +++ b/monitor.c @@ -329,8 +329,8 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, static void qmp_request_free(QMPRequest *req) { - qobject_decref(req->id); - qobject_decref(req->req); + qobject_unref(req->id); + qobject_unref(req->req); g_free(req); } @@ -346,7 +346,7 @@ static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon) static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon) { while (!g_queue_is_empty(mon->qmp.qmp_responses)) { - qobject_decref(g_queue_pop_head(mon->qmp.qmp_responses)); + qobject_unref((QObject *)g_queue_pop_head(mon->qmp.qmp_responses)); } } @@ -391,14 +391,14 @@ static void monitor_flush_locked(Monitor *mon) rc = qemu_chr_fe_write(&mon->chr, (const uint8_t *) buf, len); if ((rc < 0 && errno != EAGAIN) || (rc == len)) { /* all flushed or error */ - QDECREF(mon->outbuf); + qobject_unref(mon->outbuf); mon->outbuf = qstring_new(); return; } if (rc > 0) { /* partial write */ QString *tmp = qstring_from_str(buf + rc); - QDECREF(mon->outbuf); + qobject_unref(mon->outbuf); mon->outbuf = tmp; } if (mon->out_watch == 0) { @@ -482,7 +482,7 @@ static void monitor_json_emitter_raw(Monitor *mon, qstring_append_chr(json, '\n'); monitor_puts(mon, qstring_get_str(json)); - QDECREF(json); + qobject_unref(json); } static void monitor_json_emitter(Monitor *mon, QObject *data) @@ -494,9 +494,9 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) * caller won't free the data (which will be finally freed in * responder thread). */ - qobject_incref(data); + qobject_ref(data); qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - g_queue_push_tail(mon->qmp.qmp_responses, (void *)data); + g_queue_push_tail(mon->qmp.qmp_responses, data); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); qemu_bh_schedule(mon_global.qmp_respond_bh); } else { @@ -546,7 +546,7 @@ static void monitor_qmp_bh_responder(void *opaque) break; } monitor_json_emitter_raw(response.mon, response.data); - qobject_decref(response.data); + qobject_unref(response.data); } } @@ -613,9 +613,9 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) * last send. Store event for sending when timer fires, * replacing a prior stored event if any. */ - QDECREF(evstate->qdict); + qobject_unref(evstate->qdict); evstate->qdict = qdict; - QINCREF(evstate->qdict); + qobject_ref(evstate->qdict); } else { /* * Last send was (at least) evconf->rate ns ago. @@ -630,7 +630,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) evstate = g_new(MonitorQAPIEventState, 1); evstate->event = event; evstate->data = data; - QINCREF(evstate->data); + qobject_ref(evstate->data); evstate->qdict = NULL; evstate->timer = timer_new_ns(event_clock_type, monitor_qapi_event_handler, @@ -660,12 +660,12 @@ static void monitor_qapi_event_handler(void *opaque) int64_t now = qemu_clock_get_ns(event_clock_type); monitor_qapi_event_emit(evstate->event, evstate->qdict); - QDECREF(evstate->qdict); + qobject_unref(evstate->qdict); evstate->qdict = NULL; timer_mod_ns(evstate->timer, now + evconf->rate); } else { g_hash_table_remove(monitor_qapi_event_state, evstate); - QDECREF(evstate->data); + qobject_unref(evstate->data); timer_free(evstate->timer); g_free(evstate); } @@ -747,7 +747,7 @@ static void monitor_data_destroy(Monitor *mon) json_message_parser_destroy(&mon->qmp.parser); } readline_free(mon->rs); - QDECREF(mon->outbuf); + qobject_unref(mon->outbuf); qemu_mutex_destroy(&mon->out_lock); qemu_mutex_destroy(&mon->qmp.qmp_queue_lock); monitor_qmp_cleanup_req_queue_locked(mon); @@ -3362,7 +3362,7 @@ static QDict *monitor_parse_arguments(Monitor *mon, return qdict; fail: - QDECREF(qdict); + qobject_unref(qdict); g_free(key); return NULL; } @@ -3387,7 +3387,7 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline) } cmd->cmd(mon, qdict); - QDECREF(qdict); + qobject_unref(qdict); } static void cmd_completion(Monitor *mon, const char *name, const char *list) @@ -4049,15 +4049,15 @@ static void monitor_qmp_respond(Monitor *mon, QObject *rsp, if (rsp) { if (id) { /* This is for the qdict below. */ - qobject_incref(id); + qobject_ref(id); qdict_put_obj(qobject_to(QDict, rsp), "id", id); } monitor_json_emitter(mon, rsp); } - qobject_decref(id); - qobject_decref(rsp); + qobject_unref(id); + qobject_unref(rsp); } /* @@ -4080,7 +4080,7 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) { QString *req_json = qobject_to_json(req); trace_handle_qmp_command(mon, qstring_get_str(req_json)); - QDECREF(req_json); + qobject_unref(req_json); } old_mon = cur_mon; @@ -4098,7 +4098,7 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) monitor_resume(mon); } - qobject_decref(req); + qobject_unref(req); } /* @@ -4190,7 +4190,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) goto err; } - qobject_incref(id); + qobject_ref(id); qdict_del(qdict, "id"); req_obj = g_new0(QMPRequest, 1); @@ -4245,7 +4245,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) err: monitor_qmp_respond(mon, NULL, err, NULL); - qobject_decref(req); + qobject_unref(req); } static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size) @@ -4364,7 +4364,7 @@ static void monitor_qmp_event(void *opaque, int event) monitor_qmp_caps_reset(mon); data = get_qmp_greeting(mon); monitor_json_emitter(mon, data); - qobject_decref(data); + qobject_unref(data); mon_refcount++; break; case CHR_EVENT_CLOSED: diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index fd23803166..6b24afd367 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -99,7 +99,7 @@ static void qapi_dealloc_type_anything(Visitor *v, const char *name, QObject **obj, Error **errp) { if (obj) { - qobject_decref(*obj); + qobject_unref(*obj); } } @@ -107,7 +107,7 @@ static void qapi_dealloc_type_null(Visitor *v, const char *name, QNull **obj, Error **errp) { if (obj) { - QDECREF(*obj); + qobject_unref(*obj); } } diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index dd05907265..f9377b27fd 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -105,7 +105,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, args = qdict_new(); } else { args = qdict_get_qdict(dict, "arguments"); - QINCREF(args); + qobject_ref(args); } cmd->fn(args, &ret, &local_err); @@ -117,7 +117,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, ret = QOBJECT(qdict_new()); } - QDECREF(args); + qobject_unref(args); return ret; } @@ -166,7 +166,7 @@ QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request) } else if (ret) { qdict_put_obj(rsp, "return", ret); } else { - QDECREF(rsp); + qobject_unref(rsp); return NULL; } diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index a7569d5dce..7a290c4a3f 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -588,7 +588,7 @@ static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj, return; } - qobject_incref(qobj); + qobject_ref(qobj); *obj = qobj; } @@ -652,7 +652,7 @@ static void qobject_input_free(Visitor *v) qobject_input_stack_object_free(tos); } - qobject_decref(qiv->root); + qobject_unref(qiv->root); if (qiv->errname) { g_string_free(qiv->errname, TRUE); } @@ -678,7 +678,7 @@ static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) v->visitor.free = qobject_input_free; v->root = obj; - qobject_incref(obj); + qobject_ref(obj); return v; } @@ -744,7 +744,7 @@ Visitor *qobject_input_visitor_new_str(const char *str, } v = qobject_input_visitor_new_keyval(QOBJECT(args)); } - QDECREF(args); + qobject_unref(args); return v; } diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index 877e37eeb8..3a933b489b 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -188,7 +188,7 @@ static void qobject_output_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) { QObjectOutputVisitor *qov = to_qov(v); - qobject_incref(*obj); + qobject_ref(*obj); qobject_output_add_obj(qov, name, *obj); } @@ -201,7 +201,7 @@ static void qobject_output_type_null(Visitor *v, const char *name, /* Finish building, and return the root object. * The root object is never null. The caller becomes the object's - * owner, and should use qobject_decref() when done with it. */ + * owner, and should use qobject_unref() when done with it. */ static void qobject_output_complete(Visitor *v, void *opaque) { QObjectOutputVisitor *qov = to_qov(v); @@ -210,7 +210,7 @@ static void qobject_output_complete(Visitor *v, void *opaque) assert(qov->root && QSLIST_EMPTY(&qov->stack)); assert(opaque == qov->result); - qobject_incref(qov->root); + qobject_ref(qov->root); *qov->result = qov->root; qov->result = NULL; } @@ -226,7 +226,7 @@ static void qobject_output_free(Visitor *v) g_free(e); } - qobject_decref(qov->root); + qobject_unref(qov->root); g_free(qov); } diff --git a/qemu-img.c b/qemu-img.c index 855fa52514..ea62d2d61e 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -279,7 +279,7 @@ static BlockBackend *img_open_opts(const char *optstr, if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE) && !qdict_get_bool(options, BDRV_OPT_FORCE_SHARE)) { error_report("--force-share/-U conflicts with image options"); - QDECREF(options); + qobject_unref(options); return NULL; } qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); @@ -561,9 +561,9 @@ static void dump_json_image_check(ImageCheck *check, bool quiet) str = qobject_to_json_pretty(obj); assert(str != NULL); qprintf(quiet, "%s\n", qstring_get_str(str)); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); - QDECREF(str); + qobject_unref(str); } static void dump_human_image_check(ImageCheck *check, bool quiet) @@ -2384,9 +2384,9 @@ static void dump_json_image_info_list(ImageInfoList *list) str = qobject_to_json_pretty(obj); assert(str != NULL); printf("%s\n", qstring_get_str(str)); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); - QDECREF(str); + qobject_unref(str); } static void dump_json_image_info(ImageInfo *info) @@ -2400,9 +2400,9 @@ static void dump_json_image_info(ImageInfo *info) str = qobject_to_json_pretty(obj); assert(str != NULL); printf("%s\n", qstring_get_str(str)); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); - QDECREF(str); + qobject_unref(str); } static void dump_human_image_info_list(ImageInfoList *list) @@ -4457,9 +4457,9 @@ static void dump_json_block_measure_info(BlockMeasureInfo *info) str = qobject_to_json_pretty(obj); assert(str != NULL); printf("%s\n", qstring_get_str(str)); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); - QDECREF(str); + qobject_unref(str); } static int img_measure(int argc, char **argv) diff --git a/qemu-io.c b/qemu-io.c index e692c555e0..72fee0d8b7 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -86,7 +86,7 @@ static int openfile(char *name, int flags, bool writethrough, bool force_share, if (qemuio_blk) { error_report("file open already, try 'help close'"); - QDECREF(opts); + qobject_unref(opts); return 1; } @@ -97,7 +97,7 @@ static int openfile(char *name, int flags, bool writethrough, bool force_share, if (qdict_haskey(opts, BDRV_OPT_FORCE_SHARE) && !qdict_get_bool(opts, BDRV_OPT_FORCE_SHARE)) { error_report("-U conflicts with image options"); - QDECREF(opts); + qobject_unref(opts); return 1; } qdict_put_bool(opts, BDRV_OPT_FORCE_SHARE, true); @@ -243,7 +243,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv) } else if (optind == argc) { openfile(NULL, flags, writethrough, force_share, opts); } else { - QDECREF(opts); + qobject_unref(opts); qemuio_command_usage(&open_cmd); } return 0; diff --git a/qga/main.c b/qga/main.c index df1888edc1..1e1cec708f 100644 --- a/qga/main.c +++ b/qga/main.c @@ -563,7 +563,7 @@ static int send_response(GAState *s, QObject *payload) response_qstr = qstring_new(); qstring_append_chr(response_qstr, QGA_SENTINEL_BYTE); qstring_append(response_qstr, qstring_get_str(payload_qstr)); - QDECREF(payload_qstr); + qobject_unref(payload_qstr); } else { response_qstr = payload_qstr; } @@ -571,7 +571,7 @@ static int send_response(GAState *s, QObject *payload) qstring_append_chr(response_qstr, '\n'); buf = qstring_get_str(response_qstr); status = ga_channel_write_all(s->channel, buf, strlen(buf)); - QDECREF(response_qstr); + qobject_unref(response_qstr); if (status != G_IO_STATUS_NORMAL) { return -EIO; } @@ -592,7 +592,7 @@ static void process_command(GAState *s, QDict *req) if (ret < 0) { g_warning("error sending response: %s", strerror(-ret)); } - qobject_decref(rsp); + qobject_unref(rsp); } } @@ -609,7 +609,7 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens) g_debug("process_event: called"); qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err)); if (err || !qdict) { - QDECREF(qdict); + qobject_unref(qdict); qdict = qdict_new(); if (!err) { g_warning("failed to parse event: unknown error"); @@ -626,7 +626,7 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens) process_command(s, qdict); } else { if (!qdict_haskey(qdict, "error")) { - QDECREF(qdict); + qobject_unref(qdict); qdict = qdict_new(); g_warning("unrecognized payload format"); error_setg(&err, QERR_UNSUPPORTED); @@ -639,7 +639,7 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens) } } - QDECREF(qdict); + qobject_unref(qdict); } /* false return signals GAChannel to close the current client connection */ diff --git a/qmp.c b/qmp.c index f72261667f..9e95b889ff 100644 --- a/qmp.c +++ b/qmp.c @@ -710,7 +710,7 @@ void qmp_object_add(const char *type, const char *id, error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); return; } - QINCREF(pdict); + qobject_ref(pdict); } else { pdict = qdict_new(); } @@ -721,7 +721,7 @@ void qmp_object_add(const char *type, const char *id, if (obj) { object_unref(obj); } - QDECREF(pdict); + qobject_unref(pdict); } void qmp_object_del(const char *id, Error **errp) diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 769b960c9f..a5aa790d62 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -222,7 +222,7 @@ static QString *qstring_from_escaped_str(JSONParserContext *ctxt, return str; out: - QDECREF(str); + qobject_unref(str); return NULL; } @@ -311,12 +311,12 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) qdict_put_obj(dict, qstring_get_str(key), value); - QDECREF(key); + qobject_unref(key); return 0; out: - QDECREF(key); + qobject_unref(key); return -1; } @@ -371,7 +371,7 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) return QOBJECT(dict); out: - QDECREF(dict); + qobject_unref(dict); return NULL; } @@ -435,7 +435,7 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) return QOBJECT(list); out: - QDECREF(list); + qobject_unref(list); return NULL; } diff --git a/qobject/qdict.c b/qobject/qdict.c index d1997a0d8a..2e9bd53e22 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -123,7 +123,7 @@ void qdict_put_obj(QDict *qdict, const char *key, QObject *value) entry = qdict_find(qdict, key, bucket); if (entry) { /* replace key's value */ - qobject_decref(entry->value); + qobject_unref(entry->value); entry->value = value; } else { /* allocate a new entry */ @@ -373,7 +373,7 @@ QDict *qdict_clone_shallow(const QDict *src) for (i = 0; i < QDICT_BUCKET_MAX; i++) { QLIST_FOREACH(entry, &src->table[i], next) { - qobject_incref(entry->value); + qobject_ref(entry->value); qdict_put_obj(dest, entry->key, entry->value); } } @@ -390,7 +390,7 @@ static void qentry_destroy(QDictEntry *e) assert(e->key != NULL); assert(e->value != NULL); - qobject_decref(e->value); + qobject_unref(e->value); g_free(e->key); g_free(e); } @@ -480,7 +480,7 @@ void qdict_copy_default(QDict *dst, QDict *src, const char *key) val = qdict_get(src, key); if (val) { - qobject_incref(val); + qobject_ref(val); qdict_put_obj(dst, key, val); } } @@ -526,7 +526,7 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) qdict_flatten_qlist(qobject_to(QList, value), target, new_key); } else { /* All other types are moved to the target unchanged. */ - qobject_incref(value); + qobject_ref(value); qdict_put_obj(target, new_key, value); } @@ -566,7 +566,7 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) delete = true; } else if (prefix) { /* All other objects are moved to the target unchanged. */ - qobject_incref(value); + qobject_ref(value); qdict_put_obj(target, new_key, value); delete = true; } @@ -610,7 +610,7 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) while (entry != NULL) { next = qdict_next(src, entry); if (strstart(entry->key, start, &p)) { - qobject_incref(entry->value); + qobject_ref(entry->value); qdict_put_obj(*dst, p, entry->value); qdict_del(src, entry->key); } @@ -684,7 +684,7 @@ void qdict_array_split(QDict *src, QList **dst) qdict_extract_subqdict(src, &subqdict, prefix); assert(qdict_size(subqdict) > 0); } else { - qobject_incref(subqobj); + qobject_ref(subqobj); qdict_del(src, indexstr); } @@ -894,7 +894,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); } - qobject_incref(ent->value); + qobject_ref(ent->value); qdict_put_obj(child_dict, suffix, ent->value); } else { if (child) { @@ -902,7 +902,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) prefix); goto error; } - qobject_incref(ent->value); + qobject_ref(ent->value); qdict_put_obj(two_level, prefix, ent->value); } @@ -924,11 +924,11 @@ QObject *qdict_crumple(const QDict *src, Error **errp) qdict_put_obj(multi_level, ent->key, child); } else { - qobject_incref(ent->value); + qobject_ref(ent->value); qdict_put_obj(multi_level, ent->key, ent->value); } } - QDECREF(two_level); + qobject_unref(two_level); two_level = NULL; /* Step 3: detect if we need to turn our dict into list */ @@ -951,10 +951,10 @@ QObject *qdict_crumple(const QDict *src, Error **errp) goto error; } - qobject_incref(child); + qobject_ref(child); qlist_append_obj(qobject_to(QList, dst), child); } - QDECREF(multi_level); + qobject_unref(multi_level); multi_level = NULL; } else { dst = QOBJECT(multi_level); @@ -964,9 +964,9 @@ QObject *qdict_crumple(const QDict *src, Error **errp) error: g_free(prefix); - QDECREF(multi_level); - QDECREF(two_level); - qobject_decref(dst); + qobject_unref(multi_level); + qobject_unref(two_level); + qobject_unref(dst); return NULL; } @@ -1055,7 +1055,7 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite) next = qdict_next(src, entry); if (overwrite || !qdict_haskey(dest, entry->key)) { - qobject_incref(entry->value); + qobject_ref(entry->value); qdict_put_obj(dest, entry->key, entry->value); qdict_del(src, entry->key); } @@ -1088,7 +1088,7 @@ bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) } qobj = qdict_get(qdict, renames->from); - qobject_incref(qobj); + qobject_ref(qobj); qdict_put_obj(qdict, renames->to, qobj); qdict_del(qdict, renames->from); } diff --git a/qobject/qjson.c b/qobject/qjson.c index 655d38adf1..9816a65c7d 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -104,7 +104,7 @@ static void to_json_dict_iter(const char *key, QObject *obj, void *opaque) qkey = qstring_from_str(key); to_json(QOBJECT(qkey), s->str, s->pretty, s->indent); - QDECREF(qkey); + qobject_unref(qkey); qstring_append(s->str, ": "); to_json(obj, s->str, s->pretty, s->indent); diff --git a/qobject/qlist.c b/qobject/qlist.c index 954fe98375..37c1c167f1 100644 --- a/qobject/qlist.c +++ b/qobject/qlist.c @@ -39,7 +39,7 @@ static void qlist_copy_elem(QObject *obj, void *opaque) { QList *dst = opaque; - qobject_incref(obj); + qobject_ref(obj); qlist_append_obj(dst, obj); } @@ -196,7 +196,7 @@ void qlist_destroy_obj(QObject *obj) QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) { QTAILQ_REMOVE(&qlist->head, entry, next); - qobject_decref(entry->value); + qobject_unref(entry->value); g_free(entry); } diff --git a/qom/object.c b/qom/object.c index 467795189c..76a89af99b 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1129,7 +1129,7 @@ void object_property_set_str(Object *obj, const char *value, QString *qstr = qstring_from_str(value); object_property_set_qobject(obj, QOBJECT(qstr), name, errp); - QDECREF(qstr); + qobject_unref(qstr); } char *object_property_get_str(Object *obj, const char *name, @@ -1147,7 +1147,7 @@ char *object_property_get_str(Object *obj, const char *name, error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "string"); } - qobject_decref(ret); + qobject_unref(ret); return retval; } @@ -1187,7 +1187,7 @@ void object_property_set_bool(Object *obj, bool value, QBool *qbool = qbool_from_bool(value); object_property_set_qobject(obj, QOBJECT(qbool), name, errp); - QDECREF(qbool); + qobject_unref(qbool); } bool object_property_get_bool(Object *obj, const char *name, @@ -1208,7 +1208,7 @@ bool object_property_get_bool(Object *obj, const char *name, retval = qbool_get_bool(qbool); } - qobject_decref(ret); + qobject_unref(ret); return retval; } @@ -1218,7 +1218,7 @@ void object_property_set_int(Object *obj, int64_t value, QNum *qnum = qnum_from_int(value); object_property_set_qobject(obj, QOBJECT(qnum), name, errp); - QDECREF(qnum); + qobject_unref(qnum); } int64_t object_property_get_int(Object *obj, const char *name, @@ -1238,7 +1238,7 @@ int64_t object_property_get_int(Object *obj, const char *name, retval = -1; } - qobject_decref(ret); + qobject_unref(ret); return retval; } @@ -1248,7 +1248,7 @@ void object_property_set_uint(Object *obj, uint64_t value, QNum *qnum = qnum_from_uint(value); object_property_set_qobject(obj, QOBJECT(qnum), name, errp); - QDECREF(qnum); + qobject_unref(qnum); } uint64_t object_property_get_uint(Object *obj, const char *name, @@ -1267,7 +1267,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name, retval = 0; } - qobject_decref(ret); + qobject_unref(ret); return retval; } diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 2f76e1f36d..980ffc2ada 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -140,7 +140,7 @@ Object *user_creatable_add_opts(QemuOpts *opts, Error **errp) qemu_opts_set_id(opts, (char *) id); qemu_opt_set(opts, "qom-type", type, &error_abort); g_free(type); - QDECREF(pdict); + qobject_unref(pdict); return obj; } diff --git a/scripts/coccinelle/qobject.cocci b/scripts/coccinelle/qobject.cocci index 47bcafe9a9..9fee9c0d9a 100644 --- a/scripts/coccinelle/qobject.cocci +++ b/scripts/coccinelle/qobject.cocci @@ -3,11 +3,11 @@ expression Obj, Key, E; @@ ( -- qobject_incref(QOBJECT(E)); -+ QINCREF(E); +- qobject_ref(QOBJECT(E)); ++ qobject_ref(E); | -- qobject_decref(QOBJECT(E)); -+ QDECREF(E); +- qobject_unref(QOBJECT(E)); ++ qobject_unref(E); | - qdict_put_obj(Obj, Key, QOBJECT(E)); + qdict_put(Obj, Key, E); diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 3dc523cf39..4426861ff1 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -142,7 +142,7 @@ out: ''') ret += mcgen(''' error_propagate(errp, err); - QDECREF(qmp); + qobject_unref(qmp); } ''') return ret diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 808f6c1a08..85708fe3bb 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8347,7 +8347,7 @@ static void getset_compat_deprecated(Object *obj, Visitor *v, const char *name, "use max-cpu-compat machine property instead"); } visit_type_null(v, name, &null, NULL); - QDECREF(null); + qobject_unref(null); } static const PropertyInfo ppc_compat_deprecated_propinfo = { diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 2741b6803f..e10035aaa8 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -551,7 +551,7 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, } if (!qdict_size(qdict)) { - QDECREF(qdict); + qobject_unref(qdict); } else { info->props = QOBJECT(qdict); info->has_props = true; diff --git a/tests/ahci-test.c b/tests/ahci-test.c index fb3cd84d07..1a7b761304 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1566,7 +1566,7 @@ static void atapi_wait_tray(bool open) } else { g_assert(!qdict_get_bool(data, "tray-open")); } - QDECREF(rsp); + qobject_unref(rsp); } static void test_atapi_tray(void) @@ -1596,7 +1596,7 @@ static void test_atapi_tray(void) "'arguments': {'id': 'cd0'}}"); atapi_wait_tray(true); rsp = qmp_receive(); - QDECREF(rsp); + qobject_unref(rsp); qmp_discard_response("{'execute': 'blockdev-remove-medium', " "'arguments': {'id': 'cd0'}}"); @@ -1623,7 +1623,7 @@ static void test_atapi_tray(void) "'arguments': {'id': 'cd0'}}"); atapi_wait_tray(false); rsp = qmp_receive(); - QDECREF(rsp); + qobject_unref(rsp); /* Now, to convince ATAPI we understand the media has changed... */ ahci_atapi_test_ready(ahci, port, false, SENSE_NOT_READY); diff --git a/tests/check-qdict.c b/tests/check-qdict.c index 07bb8f4564..eba5d3528e 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -34,7 +34,7 @@ static void qdict_new_test(void) g_assert(qdict->base.refcnt == 1); g_assert(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT); - QDECREF(qdict); + qobject_unref(qdict); } static void qdict_put_obj_test(void) @@ -54,7 +54,7 @@ static void qdict_put_obj_test(void) qn = qobject_to(QNum, ent->value); g_assert_cmpint(qnum_get_int(qn), ==, num); - QDECREF(qdict); + qobject_unref(qdict); } static void qdict_destroy_simple_test(void) @@ -65,7 +65,7 @@ static void qdict_destroy_simple_test(void) qdict_put_int(qdict, "num", 0); qdict_put_str(qdict, "str", "foo"); - QDECREF(qdict); + qobject_unref(qdict); } static void qdict_get_test(void) @@ -84,7 +84,7 @@ static void qdict_get_test(void) qn = qobject_to(QNum, obj); g_assert_cmpint(qnum_get_int(qn), ==, value); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_get_int_test(void) @@ -99,7 +99,7 @@ static void qdict_get_int_test(void) ret = qdict_get_int(tests_dict, key); g_assert(ret == value); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_get_try_int_test(void) @@ -121,7 +121,7 @@ static void qdict_get_try_int_test(void) ret = qdict_get_try_int(tests_dict, "string", -42); g_assert_cmpuint(ret, ==, -42); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_get_str_test(void) @@ -137,7 +137,7 @@ static void qdict_get_str_test(void) g_assert(p != NULL); g_assert(strcmp(p, str) == 0); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_get_try_str_test(void) @@ -153,7 +153,7 @@ static void qdict_get_try_str_test(void) g_assert(p != NULL); g_assert(strcmp(p, str) == 0); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_defaults_test(void) @@ -174,8 +174,8 @@ static void qdict_defaults_test(void) qdict_copy_default(copy, dict, "bar"); g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); - QDECREF(copy); - QDECREF(dict); + qobject_unref(copy); + qobject_unref(dict); } static void qdict_haskey_not_test(void) @@ -183,7 +183,7 @@ static void qdict_haskey_not_test(void) QDict *tests_dict = qdict_new(); g_assert(qdict_haskey(tests_dict, "test") == 0); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_haskey_test(void) @@ -194,7 +194,7 @@ static void qdict_haskey_test(void) qdict_put_int(tests_dict, key, 0); g_assert(qdict_haskey(tests_dict, key) == 1); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_del_test(void) @@ -210,7 +210,7 @@ static void qdict_del_test(void) g_assert(qdict_size(tests_dict) == 0); g_assert(qdict_haskey(tests_dict, key) == 0); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qobject_to_qdict_test(void) @@ -218,7 +218,7 @@ static void qobject_to_qdict_test(void) QDict *tests_dict = qdict_new(); g_assert(qobject_to(QDict, QOBJECT(tests_dict)) == tests_dict); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_iterapi_test(void) @@ -250,7 +250,7 @@ static void qdict_iterapi_test(void) g_assert(count == qdict_size(tests_dict)); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_flatten_test(void) @@ -325,7 +325,7 @@ static void qdict_flatten_test(void) g_assert(qdict_size(dict3) == 8); - QDECREF(dict3); + qobject_unref(dict3); } static void qdict_array_split_test(void) @@ -390,31 +390,31 @@ static void qdict_array_split_test(void) g_assert(int1); g_assert(qlist_empty(test_list)); - QDECREF(test_list); + qobject_unref(test_list); g_assert(qdict_get_int(dict1, "a") == 42); g_assert(qdict_get_int(dict1, "b") == 23); g_assert(qdict_size(dict1) == 2); - QDECREF(dict1); + qobject_unref(dict1); g_assert(qdict_get_int(dict2, "x") == 0); g_assert(qdict_size(dict2) == 1); - QDECREF(dict2); + qobject_unref(dict2); g_assert_cmpint(qnum_get_int(int1), ==, 66); - QDECREF(int1); + qobject_unref(int1); g_assert(qdict_get_int(test_dict, "4.y") == 1); g_assert(qdict_get_int(test_dict, "o.o") == 7); g_assert(qdict_size(test_dict) == 2); - QDECREF(test_dict); + qobject_unref(test_dict); /* * Test the split of @@ -455,18 +455,18 @@ static void qdict_array_split_test(void) g_assert(int1); g_assert(qlist_empty(test_list)); - QDECREF(test_list); + qobject_unref(test_list); g_assert_cmpint(qnum_get_int(int1), ==, 42); - QDECREF(int1); + qobject_unref(int1); g_assert(qdict_get_int(test_dict, "1") == 23); g_assert(qdict_get_int(test_dict, "1.x") == 84); g_assert(qdict_size(test_dict) == 2); - QDECREF(test_dict); + qobject_unref(test_dict); } static void qdict_array_entries_test(void) @@ -493,7 +493,7 @@ static void qdict_array_entries_test(void) g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); - QDECREF(dict); + qobject_unref(dict); dict = qdict_new(); qdict_put_int(dict, "1", 0); @@ -509,7 +509,7 @@ static void qdict_array_entries_test(void) qdict_put_int(dict, "2.c", 0); g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); - QDECREF(dict); + qobject_unref(dict); } static void qdict_join_test(void) @@ -587,8 +587,8 @@ static void qdict_join_test(void) } while (overwrite ^= true); - QDECREF(dict1); - QDECREF(dict2); + qobject_unref(dict1); + qobject_unref(dict2); } static void qdict_crumple_test_recursive(void) @@ -634,21 +634,21 @@ static void qdict_crumple_test_recursive(void) g_assert_cmpint(qdict_size(rule), ==, 2); g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); - QDECREF(rule); + qobject_unref(rule); rule = qobject_to(QDict, qlist_pop(rules)); g_assert(rule); g_assert_cmpint(qdict_size(rule), ==, 2); g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); - QDECREF(rule); + qobject_unref(rule); /* With recursive crumpling, we should see all names unescaped */ g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); - QDECREF(src); - QDECREF(dst); + qobject_unref(src); + qobject_unref(dst); } static void qdict_crumple_test_empty(void) @@ -661,8 +661,8 @@ static void qdict_crumple_test_empty(void) g_assert_cmpint(qdict_size(dst), ==, 0); - QDECREF(src); - QDECREF(dst); + qobject_unref(src); + qobject_unref(dst); } static int qdict_count_entries(QDict *dict) @@ -704,7 +704,7 @@ static void qdict_rename_keys_test(void) g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); g_assert_cmpint(qdict_count_entries(copy), ==, 5); - QDECREF(copy); + qobject_unref(copy); /* Simple rename of all entries */ renames = (QDictRenames[]) { @@ -731,7 +731,7 @@ static void qdict_rename_keys_test(void) g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); g_assert_cmpint(qdict_count_entries(copy), ==, 5); - QDECREF(copy); + qobject_unref(copy); /* Renames are processed top to bottom */ renames = (QDictRenames[]) { @@ -754,7 +754,7 @@ static void qdict_rename_keys_test(void) g_assert(!qdict_haskey(copy, "tmp")); g_assert_cmpint(qdict_count_entries(copy), ==, 5); - QDECREF(copy); + qobject_unref(copy); /* Conflicting rename */ renames = (QDictRenames[]) { @@ -775,7 +775,7 @@ static void qdict_rename_keys_test(void) g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); g_assert_cmpint(qdict_count_entries(copy), ==, 5); - QDECREF(copy); + qobject_unref(copy); /* Renames in an empty dict */ renames = (QDictRenames[]) { @@ -783,13 +783,13 @@ static void qdict_rename_keys_test(void) { NULL , NULL } }; - QDECREF(dict); + qobject_unref(dict); dict = qdict_new(); qdict_rename_keys(dict, renames, &error_abort); g_assert(qdict_first(dict) == NULL); - QDECREF(dict); + qobject_unref(dict); } static void qdict_crumple_test_bad_inputs(void) @@ -806,7 +806,7 @@ static void qdict_crumple_test_bad_inputs(void) g_assert(error != NULL); error_free(error); error = NULL; - QDECREF(src); + qobject_unref(src); src = qdict_new(); /* rule can't be both a list and a dict */ @@ -817,7 +817,7 @@ static void qdict_crumple_test_bad_inputs(void) g_assert(error != NULL); error_free(error); error = NULL; - QDECREF(src); + qobject_unref(src); src = qdict_new(); /* The input should be flat, ie no dicts or lists */ @@ -828,7 +828,7 @@ static void qdict_crumple_test_bad_inputs(void) g_assert(error != NULL); error_free(error); error = NULL; - QDECREF(src); + qobject_unref(src); src = qdict_new(); /* List indexes must not have gaps */ @@ -839,7 +839,7 @@ static void qdict_crumple_test_bad_inputs(void) g_assert(error != NULL); error_free(error); error = NULL; - QDECREF(src); + qobject_unref(src); src = qdict_new(); /* List indexes must be in %zu format */ @@ -850,7 +850,7 @@ static void qdict_crumple_test_bad_inputs(void) g_assert(error != NULL); error_free(error); error = NULL; - QDECREF(src); + qobject_unref(src); } /* @@ -871,7 +871,7 @@ static void qdict_put_exists_test(void) g_assert(qdict_size(tests_dict) == 1); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_get_not_exists_test(void) @@ -879,7 +879,7 @@ static void qdict_get_not_exists_test(void) QDict *tests_dict = qdict_new(); g_assert(qdict_get(tests_dict, "foo") == NULL); - QDECREF(tests_dict); + qobject_unref(tests_dict); } /* @@ -951,7 +951,7 @@ static void qdict_stress_test(void) g_assert(strcmp(str1, str2) == 0); - QDECREF(value); + qobject_unref(value); } // Delete everything @@ -962,14 +962,14 @@ static void qdict_stress_test(void) break; qdict_del(qdict, key); - QDECREF(value); + qobject_unref(value); g_assert(qdict_haskey(qdict, key) == 0); } fclose(test_file); g_assert(qdict_size(qdict) == 0); - QDECREF(qdict); + qobject_unref(qdict); } int main(int argc, char **argv) diff --git a/tests/check-qjson.c b/tests/check-qjson.c index 997f4d3d2c..da582df3e9 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -67,10 +67,10 @@ static void escaped_string(void) if (test_cases[i].skip == 0) { str = qobject_to_json(obj); g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded); - qobject_decref(obj); + qobject_unref(obj); } - QDECREF(str); + qobject_unref(str); } } @@ -99,9 +99,9 @@ static void simple_string(void) str = qobject_to_json(obj); g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); - qobject_decref(obj); + qobject_unref(obj); - QDECREF(str); + qobject_unref(str); } } @@ -127,7 +127,7 @@ static void single_quote_string(void) g_assert(str); g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); - QDECREF(str); + qobject_unref(str); } } @@ -823,7 +823,7 @@ static void utf8_string(void) } else { g_assert(!obj); } - qobject_decref(obj); + qobject_unref(obj); obj = QOBJECT(qstring_from_str(utf8_in)); str = qobject_to_json(obj); @@ -833,8 +833,8 @@ static void utf8_string(void) } else { g_assert(!str); } - QDECREF(str); - qobject_decref(obj); + qobject_unref(str); + qobject_unref(obj); /* * Disabled, because qobject_from_json() is buggy, and I can't @@ -869,7 +869,7 @@ static void vararg_string(void) g_assert(str); g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); - QDECREF(str); + qobject_unref(str); } } @@ -904,10 +904,10 @@ static void simple_number(void) str = qobject_to_json(QOBJECT(qnum)); g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); - QDECREF(str); + qobject_unref(str); } - QDECREF(qnum); + qobject_unref(qnum); } } @@ -928,8 +928,8 @@ static void large_number(void) str = qobject_to_json(QOBJECT(qnum)); g_assert_cmpstr(qstring_get_str(str), ==, maxu64); - QDECREF(str); - QDECREF(qnum); + qobject_unref(str); + qobject_unref(qnum); qnum = qobject_to(QNum, qobject_from_json(gtu64, &error_abort)); g_assert(qnum); @@ -939,8 +939,8 @@ static void large_number(void) str = qobject_to_json(QOBJECT(qnum)); g_assert_cmpstr(qstring_get_str(str), ==, gtu64); - QDECREF(str); - QDECREF(qnum); + qobject_unref(str); + qobject_unref(qnum); qnum = qobject_to(QNum, qobject_from_json(lti64, &error_abort)); g_assert(qnum); @@ -950,8 +950,8 @@ static void large_number(void) str = qobject_to_json(QOBJECT(qnum)); g_assert_cmpstr(qstring_get_str(str), ==, "-9223372036854775808"); - QDECREF(str); - QDECREF(qnum); + qobject_unref(str); + qobject_unref(qnum); } static void float_number(void) @@ -983,10 +983,10 @@ static void float_number(void) str = qobject_to_json(obj); g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); - QDECREF(str); + qobject_unref(str); } - QDECREF(qnum); + qobject_unref(qnum); } } @@ -1001,16 +1001,16 @@ static void vararg_number(void) qnum = qobject_to(QNum, qobject_from_jsonf("%d", value)); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, value); - QDECREF(qnum); + qobject_unref(qnum); qnum = qobject_to(QNum, qobject_from_jsonf("%lld", value_ll)); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, value_ll); - QDECREF(qnum); + qobject_unref(qnum); qnum = qobject_to(QNum, qobject_from_jsonf("%f", valuef)); g_assert(qnum_get_double(qnum) == valuef); - QDECREF(qnum); + qobject_unref(qnum); } static void keyword_literal(void) @@ -1027,9 +1027,9 @@ static void keyword_literal(void) str = qobject_to_json(obj); g_assert(strcmp(qstring_get_str(str), "true") == 0); - QDECREF(str); + qobject_unref(str); - QDECREF(qbool); + qobject_unref(qbool); obj = qobject_from_json("false", &error_abort); qbool = qobject_to(QBool, obj); @@ -1038,20 +1038,20 @@ static void keyword_literal(void) str = qobject_to_json(obj); g_assert(strcmp(qstring_get_str(str), "false") == 0); - QDECREF(str); + qobject_unref(str); - QDECREF(qbool); + qobject_unref(qbool); qbool = qobject_to(QBool, qobject_from_jsonf("%i", false)); g_assert(qbool); g_assert(qbool_get_bool(qbool) == false); - QDECREF(qbool); + qobject_unref(qbool); /* Test that non-zero values other than 1 get collapsed to true */ qbool = qobject_to(QBool, qobject_from_jsonf("%i", 2)); g_assert(qbool); g_assert(qbool_get_bool(qbool) == true); - QDECREF(qbool); + qobject_unref(qbool); obj = qobject_from_json("null", &error_abort); g_assert(obj != NULL); @@ -1060,8 +1060,8 @@ static void keyword_literal(void) null = qnull(); g_assert(QOBJECT(null) == obj); - qobject_decref(obj); - QDECREF(null); + qobject_unref(obj); + qobject_unref(null); } static void simple_dict(void) @@ -1101,12 +1101,12 @@ static void simple_dict(void) g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); str = qobject_to_json(obj); - qobject_decref(obj); + qobject_unref(obj); obj = qobject_from_json(qstring_get_str(str), &error_abort); g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - qobject_decref(obj); - QDECREF(str); + qobject_unref(obj); + qobject_unref(str); } } @@ -1158,7 +1158,7 @@ static void large_dict(void) obj = qobject_from_json(gstr->str, &error_abort); g_assert(obj != NULL); - qobject_decref(obj); + qobject_unref(obj); g_string_free(gstr, true); } @@ -1210,12 +1210,12 @@ static void simple_list(void) g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); str = qobject_to_json(obj); - qobject_decref(obj); + qobject_unref(obj); obj = qobject_from_json(qstring_get_str(str), &error_abort); g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - qobject_decref(obj); - QDECREF(str); + qobject_unref(obj); + qobject_unref(str); } } @@ -1272,13 +1272,13 @@ static void simple_whitespace(void) g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); str = qobject_to_json(obj); - qobject_decref(obj); + qobject_unref(obj); obj = qobject_from_json(qstring_get_str(str), &error_abort); g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - qobject_decref(obj); - QDECREF(str); + qobject_unref(obj); + qobject_unref(str); } } @@ -1301,7 +1301,7 @@ static void simple_varargs(void) obj = qobject_from_jsonf("[%d, 2, %p]", 1, embedded_obj); g_assert(qlit_equal_qobject(&decoded, obj)); - qobject_decref(obj); + qobject_unref(obj); } static void empty_input(void) @@ -1410,7 +1410,7 @@ static void limits_nesting(void) obj = qobject_from_json(make_nest(buf, max_nesting), &error_abort); g_assert(obj != NULL); - qobject_decref(obj); + qobject_unref(obj); obj = qobject_from_json(make_nest(buf, max_nesting + 1), &err); error_free_or_abort(&err); diff --git a/tests/check-qlist.c b/tests/check-qlist.c index a1c69ed648..ece83e293d 100644 --- a/tests/check-qlist.c +++ b/tests/check-qlist.c @@ -29,7 +29,7 @@ static void qlist_new_test(void) g_assert(qlist->base.refcnt == 1); g_assert(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST); - QDECREF(qlist); + qobject_unref(qlist); } static void qlist_append_test(void) @@ -47,7 +47,7 @@ static void qlist_append_test(void) g_assert(entry != NULL); g_assert(entry->value == QOBJECT(qi)); - QDECREF(qlist); + qobject_unref(qlist); } static void qobject_to_qlist_test(void) @@ -58,7 +58,7 @@ static void qobject_to_qlist_test(void) g_assert(qobject_to(QList, QOBJECT(qlist)) == qlist); - QDECREF(qlist); + qobject_unref(qlist); } static int iter_called; @@ -96,7 +96,7 @@ static void qlist_iter_test(void) g_assert(iter_called == iter_max); - QDECREF(qlist); + qobject_unref(qlist); } int main(int argc, char **argv) diff --git a/tests/check-qlit.c b/tests/check-qlit.c index 96bbb06f2c..bd6798d912 100644 --- a/tests/check-qlit.c +++ b/tests/check-qlit.c @@ -62,7 +62,7 @@ static void qlit_equal_qobject_test(void) qdict_put(qobject_to(QDict, qobj), "bee", qlist_new()); g_assert(!qlit_equal_qobject(&qlit, qobj)); - qobject_decref(qobj); + qobject_unref(qobj); } static void qobject_from_qlit_test(void) @@ -79,15 +79,15 @@ static void qobject_from_qlit_test(void) bee = qdict_get_qlist(qdict, "bee"); obj = qlist_pop(bee); g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 43); - qobject_decref(obj); + qobject_unref(obj); obj = qlist_pop(bee); g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 44); - qobject_decref(obj); + qobject_unref(obj); obj = qlist_pop(bee); g_assert(qbool_get_bool(qobject_to(QBool, obj))); - qobject_decref(obj); + qobject_unref(obj); - qobject_decref(qobj); + qobject_unref(qobj); } int main(int argc, char **argv) diff --git a/tests/check-qnull.c b/tests/check-qnull.c index afa4400da1..ebf21db83c 100644 --- a/tests/check-qnull.c +++ b/tests/check-qnull.c @@ -30,7 +30,7 @@ static void qnull_ref_test(void) g_assert(obj == QOBJECT(&qnull_)); g_assert(qnull_.base.refcnt == 2); g_assert(qobject_type(obj) == QTYPE_QNULL); - qobject_decref(obj); + qobject_unref(obj); g_assert(qnull_.base.refcnt == 1); } @@ -49,10 +49,10 @@ static void qnull_visit_test(void) g_assert(qnull_.base.refcnt == 1); obj = QOBJECT(qnull()); v = qobject_input_visitor_new(obj); - qobject_decref(obj); + qobject_unref(obj); visit_type_null(v, NULL, &null, &error_abort); g_assert(obj == QOBJECT(&qnull_)); - QDECREF(null); + qobject_unref(null); visit_free(v); null = NULL; @@ -60,8 +60,8 @@ static void qnull_visit_test(void) visit_type_null(v, NULL, &null, &error_abort); visit_complete(v, &obj); g_assert(obj == QOBJECT(&qnull_)); - QDECREF(null); - qobject_decref(obj); + qobject_unref(null); + qobject_unref(obj); visit_free(v); g_assert(qnull_.base.refcnt == 1); diff --git a/tests/check-qnum.c b/tests/check-qnum.c index 9187da734b..4105015872 100644 --- a/tests/check-qnum.c +++ b/tests/check-qnum.c @@ -35,7 +35,7 @@ static void qnum_from_int_test(void) g_assert_cmpint(qn->base.refcnt, ==, 1); g_assert_cmpint(qobject_type(QOBJECT(qn)), ==, QTYPE_QNUM); - QDECREF(qn); + qobject_unref(qn); } static void qnum_from_uint_test(void) @@ -50,7 +50,7 @@ static void qnum_from_uint_test(void) g_assert(qn->base.refcnt == 1); g_assert(qobject_type(QOBJECT(qn)) == QTYPE_QNUM); - QDECREF(qn); + qobject_unref(qn); } static void qnum_from_double_test(void) @@ -65,7 +65,7 @@ static void qnum_from_double_test(void) g_assert_cmpint(qn->base.refcnt, ==, 1); g_assert_cmpint(qobject_type(QOBJECT(qn)), ==, QTYPE_QNUM); - QDECREF(qn); + qobject_unref(qn); } static void qnum_from_int64_test(void) @@ -76,7 +76,7 @@ static void qnum_from_int64_test(void) qn = qnum_from_int(value); g_assert_cmpint((int64_t) qn->u.i64, ==, value); - QDECREF(qn); + qobject_unref(qn); } static void qnum_get_int_test(void) @@ -87,7 +87,7 @@ static void qnum_get_int_test(void) qn = qnum_from_int(value); g_assert_cmpint(qnum_get_int(qn), ==, value); - QDECREF(qn); + qobject_unref(qn); } static void qnum_get_uint_test(void) @@ -100,25 +100,25 @@ static void qnum_get_uint_test(void) qn = qnum_from_uint(value); g_assert(qnum_get_try_uint(qn, &val)); g_assert_cmpuint(val, ==, value); - QDECREF(qn); + qobject_unref(qn); qn = qnum_from_int(value); g_assert(qnum_get_try_uint(qn, &val)); g_assert_cmpuint(val, ==, value); - QDECREF(qn); + qobject_unref(qn); /* invalid cases */ qn = qnum_from_int(-1); g_assert(!qnum_get_try_uint(qn, &val)); - QDECREF(qn); + qobject_unref(qn); qn = qnum_from_uint(-1ULL); g_assert(!qnum_get_try_int(qn, &ival)); - QDECREF(qn); + qobject_unref(qn); qn = qnum_from_double(0.42); g_assert(!qnum_get_try_uint(qn, &val)); - QDECREF(qn); + qobject_unref(qn); } static void qobject_to_qnum_test(void) @@ -127,11 +127,11 @@ static void qobject_to_qnum_test(void) qn = qnum_from_int(0); g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); - QDECREF(qn); + qobject_unref(qn); qn = qnum_from_double(0); g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); - QDECREF(qn); + qobject_unref(qn); } static void qnum_to_string_test(void) @@ -143,13 +143,13 @@ static void qnum_to_string_test(void) tmp = qnum_to_string(qn); g_assert_cmpstr(tmp, ==, "123456"); g_free(tmp); - QDECREF(qn); + qobject_unref(qn); qn = qnum_from_double(0.42); tmp = qnum_to_string(qn); g_assert_cmpstr(tmp, ==, "0.42"); g_free(tmp); - QDECREF(qn); + qobject_unref(qn); } int main(int argc, char **argv) diff --git a/tests/check-qobject.c b/tests/check-qobject.c index 7629b8071b..5cb08fcb63 100644 --- a/tests/check-qobject.c +++ b/tests/check-qobject.c @@ -80,7 +80,7 @@ static void do_free_all(int _, ...) va_start(ap, _); while ((obj = va_arg(ap, QObject *)) != NULL) { - qobject_decref(obj); + qobject_unref(obj); } va_end(ap); } diff --git a/tests/check-qstring.c b/tests/check-qstring.c index 9c4dd3f94f..f11a7a8605 100644 --- a/tests/check-qstring.c +++ b/tests/check-qstring.c @@ -31,7 +31,7 @@ static void qstring_from_str_test(void) g_assert(strcmp(str, qstring->string) == 0); g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING); - QDECREF(qstring); + qobject_unref(qstring); } static void qstring_get_str_test(void) @@ -44,7 +44,7 @@ static void qstring_get_str_test(void) ret_str = qstring_get_str(qstring); g_assert(strcmp(ret_str, str) == 0); - QDECREF(qstring); + qobject_unref(qstring); } static void qstring_append_chr_test(void) @@ -59,7 +59,7 @@ static void qstring_append_chr_test(void) qstring_append_chr(qstring, str[i]); g_assert(strcmp(str, qstring_get_str(qstring)) == 0); - QDECREF(qstring); + qobject_unref(qstring); } static void qstring_from_substr_test(void) @@ -70,7 +70,7 @@ static void qstring_from_substr_test(void) g_assert(qs != NULL); g_assert(strcmp(qstring_get_str(qs), "tualiza") == 0); - QDECREF(qs); + qobject_unref(qs); } @@ -81,7 +81,7 @@ static void qobject_to_qstring_test(void) qstring = qstring_from_str("foo"); g_assert(qobject_to(QString, QOBJECT(qstring)) == qstring); - QDECREF(qstring); + qobject_unref(qstring); } int main(int argc, char **argv) diff --git a/tests/cpu-plug-test.c b/tests/cpu-plug-test.c index 8b5ab1fd02..5f39ba0df3 100644 --- a/tests/cpu-plug-test.c +++ b/tests/cpu-plug-test.c @@ -42,7 +42,7 @@ static void test_plug_with_cpu_add(gconstpointer data) " 'arguments': { 'id': %d } }", i); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } qtest_end(); @@ -66,7 +66,7 @@ static void test_plug_without_cpu_add(gconstpointer data) s->sockets * s->cores * s->threads); g_assert(response); g_assert(qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); qtest_end(); g_free(args); diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index a01321aced..0b4f221c29 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -40,8 +40,8 @@ static QList *qom_list_types(const char *implements, bool abstract) " 'arguments': %p }", args); g_assert(qdict_haskey(resp, "return")); ret = qdict_get_qlist(resp, "return"); - QINCREF(ret); - QDECREF(resp); + qobject_ref(ret); + qobject_unref(resp); return ret; } @@ -54,7 +54,7 @@ static QDict *qom_type_index(QList *types) QLIST_FOREACH_ENTRY(types, e) { QDict *d = qobject_to(QDict, qlist_entry_obj(e)); const char *name = qdict_get_str(d, "name"); - QINCREF(d); + qobject_ref(d); qdict_put(index, name, d); } return index; @@ -108,7 +108,7 @@ static void test_one_device(const char *type) resp = qmp("{'execute': 'device-list-properties'," " 'arguments': {'typename': %s}}", type); - QDECREF(resp); + qobject_unref(resp); help = hmp("device_add \"%s,help\"", type); g_free(help); @@ -129,7 +129,7 @@ static void test_device_intro_list(void) qtest_start(common_args); types = device_type_list(true); - QDECREF(types); + qobject_unref(types); help = hmp("device_add help"); g_free(help); @@ -157,8 +157,8 @@ static void test_qom_list_parents(const char *parent) g_assert(qom_has_parent(index, name, parent)); } - QDECREF(types); - QDECREF(index); + qobject_unref(types); + qobject_unref(index); } static void test_qom_list_fields(void) @@ -187,8 +187,8 @@ static void test_qom_list_fields(void) test_qom_list_parents("device"); test_qom_list_parents("sys-bus-device"); - QDECREF(all_types); - QDECREF(non_abstract); + qobject_unref(all_types); + qobject_unref(non_abstract); qtest_end(); } @@ -222,7 +222,7 @@ static void test_device_intro_concrete(void) test_one_device(type); } - QDECREF(types); + qobject_unref(types); qtest_end(); } @@ -255,8 +255,8 @@ static void test_abstract_interfaces(void) g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract")); } - QDECREF(all_types); - QDECREF(index); + qobject_unref(all_types); + qobject_unref(index); qtest_end(); } diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c index 313030a14c..852fefc8f3 100644 --- a/tests/drive_del-test.c +++ b/tests/drive_del-test.c @@ -41,7 +41,7 @@ static void device_del(void) response = qmp_receive(); g_assert(response); g_assert(qdict_haskey(response, "return")); - QDECREF(response); + qobject_unref(response); } static void test_drive_without_dev(void) @@ -78,7 +78,7 @@ static void test_after_failed_device_add(void) g_assert(response); error = qdict_get_qdict(response, "error"); g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, "GenericError"); - QDECREF(response); + qobject_unref(response); /* Delete the drive */ drive_del(); diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index 5124e982c1..013ca68581 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -100,14 +100,14 @@ void migrate(QOSState *from, QOSState *to, const char *uri) sub = qdict_get_qdict(rsp, "return"); g_assert(qdict_haskey(sub, "running")); running = qdict_get_bool(sub, "running"); - QDECREF(rsp); + qobject_unref(rsp); /* Issue the migrate command. */ rsp = qtest_qmp(from->qts, "{ 'execute': 'migrate', 'arguments': { 'uri': %s }}", uri); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); /* Wait for STOP event, but only if we were running: */ if (running) { @@ -132,12 +132,12 @@ void migrate(QOSState *from, QOSState *to, const char *uri) /* "setup", "active", "completed", "failed", "cancelled" */ if (strcmp(st, "completed") == 0) { - QDECREF(rsp); + qobject_unref(rsp); break; } if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0)) { - QDECREF(rsp); + qobject_unref(rsp); g_usleep(5000); continue; } diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index a2daf6103d..a7803308b7 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -170,7 +170,7 @@ void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) g_free(cmd); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); diff --git a/tests/libqtest.c b/tests/libqtest.c index 6f33a37667..43fb97e035 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -517,8 +517,8 @@ void qmp_fd_sendv(int fd, const char *fmt, va_list ap) /* Send QMP request */ socket_send(fd, str, qstring_get_length(qstr)); - QDECREF(qstr); - qobject_decref(qobj); + qobject_unref(qstr); + qobject_unref(qobj); } } @@ -585,7 +585,7 @@ void qtest_async_qmp(QTestState *s, const char *fmt, ...) void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap) { QDict *response = qtest_qmpv(s, fmt, ap); - QDECREF(response); + qobject_unref(response); } void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...) @@ -596,7 +596,7 @@ void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...) va_start(ap, fmt); response = qtest_qmpv(s, fmt, ap); va_end(ap); - QDECREF(response); + qobject_unref(response); } QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event) @@ -609,7 +609,7 @@ QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event) (strcmp(qdict_get_str(response, "event"), event) == 0)) { return response; } - QDECREF(response); + qobject_unref(response); } } @@ -618,7 +618,7 @@ void qtest_qmp_eventwait(QTestState *s, const char *event) QDict *response; response = qtest_qmp_eventwait_ref(s, event); - QDECREF(response); + qobject_unref(response); } char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap) @@ -634,12 +634,12 @@ char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap) ret = g_strdup(qdict_get_try_str(resp, "return")); while (ret == NULL && qdict_get_try_str(resp, "event")) { /* Ignore asynchronous QMP events */ - QDECREF(resp); + qobject_unref(resp); resp = qtest_qmp_receive(s); ret = g_strdup(qdict_get_try_str(resp, "return")); } g_assert(ret); - QDECREF(resp); + qobject_unref(resp); g_free(cmd); return ret; } @@ -1021,7 +1021,7 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine)) } qtest_end(); - QDECREF(response); + qobject_unref(response); } /* @@ -1050,7 +1050,7 @@ void qtest_qmp_device_add(const char *driver, const char *id, const char *fmt, g_assert(response); g_assert(!qdict_haskey(response, "event")); /* We don't expect any events */ g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } /* @@ -1095,6 +1095,6 @@ void qtest_qmp_device_del(const char *id) g_assert(event); g_assert_cmpstr(qdict_get_str(event, "event"), ==, "DEVICE_DELETED"); - QDECREF(response1); - QDECREF(response2); + qobject_unref(response1); + qobject_unref(response2); } diff --git a/tests/machine-none-test.c b/tests/machine-none-test.c index efdd4be986..f286557b3e 100644 --- a/tests/machine-none-test.c +++ b/tests/machine-none-test.c @@ -88,7 +88,7 @@ static void test_machine_cpu_cli(void) response = qmp("{ 'execute': 'quit' }"); g_assert(qdict_haskey(response, "return")); - QDECREF(response); + qobject_unref(response); qtest_quit(global_qtest); } diff --git a/tests/migration-test.c b/tests/migration-test.c index dde7c464c3..b99661b773 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -195,7 +195,7 @@ static QDict *wait_command(QTestState *who, const char *command) if (!strcmp(event_string, "STOP")) { got_stop = true; } - QDECREF(response); + qobject_unref(response); response = qtest_qmp_receive(who); } return response; @@ -221,7 +221,7 @@ static uint64_t get_migration_pass(QTestState *who) rsp_ram = qdict_get_qdict(rsp_return, "ram"); result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0); } - QDECREF(rsp); + qobject_unref(rsp); return result; } @@ -232,7 +232,7 @@ static void read_blocktime(QTestState *who) rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); rsp_return = qdict_get_qdict(rsp, "return"); g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); - QDECREF(rsp); + qobject_unref(rsp); } static void wait_for_migration_complete(QTestState *who) @@ -247,7 +247,7 @@ static void wait_for_migration_complete(QTestState *who) status = qdict_get_str(rsp_return, "status"); completed = strcmp(status, "completed") == 0; g_assert_cmpstr(status, !=, "failed"); - QDECREF(rsp); + qobject_unref(rsp); if (completed) { return; } @@ -334,7 +334,7 @@ static void migrate_check_parameter(QTestState *who, const char *parameter, qdict_get_try_int(rsp_return, parameter, -1)); g_assert_cmpstr(result, ==, value); g_free(result); - QDECREF(rsp); + qobject_unref(rsp); } static void migrate_set_parameter(QTestState *who, const char *parameter, @@ -349,7 +349,7 @@ static void migrate_set_parameter(QTestState *who, const char *parameter, rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); migrate_check_parameter(who, parameter, value); } @@ -367,7 +367,7 @@ static void migrate_set_capability(QTestState *who, const char *capability, rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); } static void migrate(QTestState *who, const char *uri) @@ -381,7 +381,7 @@ static void migrate(QTestState *who, const char *uri) rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); } static void migrate_start_postcopy(QTestState *who) @@ -390,7 +390,7 @@ static void migrate_start_postcopy(QTestState *who) rsp = wait_command(who, "{ 'execute': 'migrate-start-postcopy' }"); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); } static void test_migrate_start(QTestState **from, QTestState **to, @@ -503,7 +503,7 @@ static void deprecated_set_downtime(QTestState *who, const double value) rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); result_int = value * 1000L; expected = g_strdup_printf("%" PRId64, result_int); migrate_check_parameter(who, "downtime-limit", expected); @@ -520,7 +520,7 @@ static void deprecated_set_speed(QTestState *who, const char *value) rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); migrate_check_parameter(who, "max-bandwidth", value); } @@ -597,7 +597,7 @@ static void test_baddest(void) g_assert(!strcmp(status, "setup") || !(strcmp(status, "failed"))); failed = !strcmp(status, "failed"); - QDECREF(rsp); + qobject_unref(rsp); } while (!failed); /* Is the machine currently running? */ @@ -606,7 +606,7 @@ static void test_baddest(void) rsp_return = qdict_get_qdict(rsp, "return"); g_assert(qdict_haskey(rsp_return, "running")); g_assert(qdict_get_bool(rsp_return, "running")); - QDECREF(rsp); + qobject_unref(rsp); test_migrate_end(from, to, false); } diff --git a/tests/numa-test.c b/tests/numa-test.c index 0f861d8176..169213fc1c 100644 --- a/tests/numa-test.c +++ b/tests/numa-test.c @@ -111,10 +111,10 @@ static void test_query_cpus(const void *data) } else { g_assert_cmpint(node, ==, 1); } - qobject_decref(e); + qobject_unref(e); } - QDECREF(resp); + qobject_unref(resp); qtest_end(); g_free(cli); } @@ -164,10 +164,10 @@ static void pc_numa_cpu(const void *data) } else { g_assert(false); } - qobject_decref(e); + qobject_unref(e); } - QDECREF(resp); + qobject_unref(resp); qtest_end(); g_free(cli); } @@ -209,10 +209,10 @@ static void spapr_numa_cpu(const void *data) } else { g_assert(false); } - qobject_decref(e); + qobject_unref(e); } - QDECREF(resp); + qobject_unref(resp); qtest_end(); g_free(cli); } @@ -252,10 +252,10 @@ static void aarch64_numa_cpu(const void *data) } else { g_assert(false); } - qobject_decref(e); + qobject_unref(e); } - QDECREF(resp); + qobject_unref(resp); qtest_end(); g_free(cli); } diff --git a/tests/pvpanic-test.c b/tests/pvpanic-test.c index ebdf32c2e2..7461a7254f 100644 --- a/tests/pvpanic-test.c +++ b/tests/pvpanic-test.c @@ -28,7 +28,7 @@ static void test_panic(void) data = qdict_get_qdict(response, "data"); g_assert(qdict_haskey(data, "action")); g_assert_cmpstr(qdict_get_str(data, "action"), ==, "pause"); - QDECREF(response); + qobject_unref(response); } int main(int argc, char **argv) diff --git a/tests/q35-test.c b/tests/q35-test.c index 3eaedf4b24..7ea7acc9d8 100644 --- a/tests/q35-test.c +++ b/tests/q35-test.c @@ -109,7 +109,7 @@ static void test_smram_lock(void) response = qmp("{'execute': 'system_reset', 'arguments': {} }"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); /* check open is settable again */ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 772058fc4c..88f867f8c0 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -52,27 +52,27 @@ static void test_malformed(QTestState *qts) /* Not even a dictionary */ resp = qtest_qmp(qts, "null"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - QDECREF(resp); + qobject_unref(resp); /* No "execute" key */ resp = qtest_qmp(qts, "{}"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - QDECREF(resp); + qobject_unref(resp); /* "execute" isn't a string */ resp = qtest_qmp(qts, "{ 'execute': true }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - QDECREF(resp); + qobject_unref(resp); /* "arguments" isn't a dictionary */ resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - QDECREF(resp); + qobject_unref(resp); /* extra key */ resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - QDECREF(resp); + qobject_unref(resp); } static void test_qmp_protocol(void) @@ -90,12 +90,12 @@ static void test_qmp_protocol(void) test_version(qdict_get(q, "version")); capabilities = qdict_get_qlist(q, "capabilities"); g_assert(capabilities && qlist_empty(capabilities)); - QDECREF(resp); + qobject_unref(resp); /* Test valid command before handshake */ resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); - QDECREF(resp); + qobject_unref(resp); /* Test malformed commands before handshake */ test_malformed(qts); @@ -104,17 +104,17 @@ static void test_qmp_protocol(void) resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); ret = qdict_get_qdict(resp, "return"); g_assert(ret && !qdict_size(ret)); - QDECREF(resp); + qobject_unref(resp); /* Test repeated handshake */ resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); - QDECREF(resp); + qobject_unref(resp); /* Test valid command */ resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); test_version(qdict_get(resp, "return")); - QDECREF(resp); + qobject_unref(resp); /* Test malformed commands */ test_malformed(qts); @@ -124,13 +124,13 @@ static void test_qmp_protocol(void) ret = qdict_get_qdict(resp, "return"); g_assert(ret); g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1"); - QDECREF(resp); + qobject_unref(resp); /* Test command failure with 'id' */ resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); - QDECREF(resp); + qobject_unref(resp); qtest_quit(qts); } @@ -159,21 +159,21 @@ static void test_qmp_oob(void) qstr = qobject_to(QString, entry->value); g_assert(qstr); g_assert_cmpstr(qstring_get_str(qstr), ==, "oob"); - QDECREF(resp); + qobject_unref(resp); /* Try a fake capability, it should fail. */ resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities', " " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }"); g_assert(qdict_haskey(resp, "error")); - QDECREF(resp); + qobject_unref(resp); /* Now, enable OOB in current QMP session, it should succeed. */ resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities', " " 'arguments': { 'enable': [ 'oob' ] } }"); g_assert(qdict_haskey(resp, "return")); - QDECREF(resp); + qobject_unref(resp); /* * Try any command that does not support OOB but with OOB flag. We @@ -183,7 +183,7 @@ static void test_qmp_oob(void) "{ 'execute': 'query-cpus'," " 'control': { 'run-oob': true } }"); g_assert(qdict_haskey(resp, "error")); - QDECREF(resp); + qobject_unref(resp); /* * First send the "x-oob-test" command with lock=true and @@ -210,7 +210,7 @@ static void test_qmp_oob(void) !g_strcmp0(cmd_id, "unlock-cmd")) { acks++; } - QDECREF(resp); + qobject_unref(resp); } qtest_quit(qts); @@ -271,7 +271,7 @@ static void test_query(const void *data) -1, &error_abort), ==, expected_error_class); } - QDECREF(resp); + qobject_unref(resp); qtest_end(); } @@ -321,7 +321,7 @@ static void qmp_schema_init(QmpSchema *schema) visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); visit_free(qiv); - QDECREF(resp); + qobject_unref(resp); qtest_end(); schema->hash = g_hash_table_new(g_str_hash, g_str_equal); diff --git a/tests/qom-test.c b/tests/qom-test.c index a34ff6ba53..e6f712cbd3 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -57,7 +57,7 @@ static void test_properties(const char *path, bool recurse) g_assert(response); if (!recurse) { - QDECREF(response); + qobject_unref(response); return; } @@ -82,10 +82,10 @@ static void test_properties(const char *path, bool recurse) path, prop); /* qom-get may fail but should not, e.g., segfault. */ g_assert(tmp); - QDECREF(tmp); + qobject_unref(tmp); } } - QDECREF(response); + qobject_unref(response); } static void test_machine(gconstpointer data) @@ -101,7 +101,7 @@ static void test_machine(gconstpointer data) response = qmp("{ 'execute': 'quit' }"); g_assert(qdict_haskey(response, "return")); - QDECREF(response); + qobject_unref(response); qtest_end(); g_free(args); diff --git a/tests/tco-test.c b/tests/tco-test.c index aee17af3c1..9945fb8469 100644 --- a/tests/tco-test.c +++ b/tests/tco-test.c @@ -241,8 +241,8 @@ static QDict *get_watchdog_action(void) QDict *data; data = qdict_get_qdict(ev, "data"); - QINCREF(data); - QDECREF(ev); + qobject_ref(data); + qobject_unref(ev); return data; } @@ -265,7 +265,7 @@ static void test_tco_second_timeout_pause(void) clock_step(ticks * TCO_TICK_NSEC * 2); ad = get_watchdog_action(); g_assert(!strcmp(qdict_get_str(ad, "action"), "pause")); - QDECREF(ad); + qobject_unref(ad); stop_tco(&td); test_end(&td); @@ -290,7 +290,7 @@ static void test_tco_second_timeout_reset(void) clock_step(ticks * TCO_TICK_NSEC * 2); ad = get_watchdog_action(); g_assert(!strcmp(qdict_get_str(ad, "action"), "reset")); - QDECREF(ad); + qobject_unref(ad); stop_tco(&td); test_end(&td); @@ -315,7 +315,7 @@ static void test_tco_second_timeout_shutdown(void) clock_step(ticks * TCO_TICK_NSEC * 2); ad = get_watchdog_action(); g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown")); - QDECREF(ad); + qobject_unref(ad); stop_tco(&td); test_end(&td); @@ -340,7 +340,7 @@ static void test_tco_second_timeout_none(void) clock_step(ticks * TCO_TICK_NSEC * 2); ad = get_watchdog_action(); g_assert(!strcmp(qdict_get_str(ad, "action"), "none")); - QDECREF(ad); + qobject_unref(ad); stop_tco(&td); test_end(&td); diff --git a/tests/test-char.c b/tests/test-char.c index 306c728335..1880d36783 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -322,7 +322,7 @@ static void char_socket_test_common(Chardev *chr) qdict = qobject_to(QDict, addr); port = qdict_get_str(qdict, "port"); tmp = g_strdup_printf("tcp:127.0.0.1:%s", port); - QDECREF(qdict); + qobject_unref(qdict); qemu_chr_fe_init(&be, chr, &error_abort); qemu_chr_fe_set_handlers(&be, socket_can_read, socket_read, diff --git a/tests/test-keyval.c b/tests/test-keyval.c index 029f05202a..63cb14629b 100644 --- a/tests/test-keyval.c +++ b/tests/test-keyval.c @@ -30,7 +30,7 @@ static void test_keyval_parse(void) /* Nothing */ qdict = keyval_parse("", NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 0); - QDECREF(qdict); + qobject_unref(qdict); /* Empty key (qemu_opts_parse() accepts this) */ qdict = keyval_parse("=val", NULL, &err); @@ -70,7 +70,7 @@ static void test_keyval_parse(void) qdict = keyval_parse(params + 2, NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(qdict, long_key + 1), ==, "v"); - QDECREF(qdict); + qobject_unref(qdict); /* Long key fragment */ qdict = keyval_parse(params, NULL, &error_abort); @@ -79,7 +79,7 @@ static void test_keyval_parse(void) g_assert(sub_qdict); g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(sub_qdict, long_key + 1), ==, "v"); - QDECREF(qdict); + qobject_unref(qdict); g_free(params); /* Crap after valid key */ @@ -92,13 +92,13 @@ static void test_keyval_parse(void) g_assert_cmpuint(qdict_size(qdict), ==, 2); g_assert_cmpstr(qdict_get_try_str(qdict, "a"), ==, "3"); g_assert_cmpstr(qdict_get_try_str(qdict, "b"), ==, "2,x"); - QDECREF(qdict); + qobject_unref(qdict); /* Even when it doesn't in qemu_opts_parse() */ qdict = keyval_parse("id=foo,id=bar", NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "bar"); - QDECREF(qdict); + qobject_unref(qdict); /* Dotted keys */ qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, &error_abort); @@ -111,7 +111,7 @@ static void test_keyval_parse(void) g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(sub_qdict, "c"), ==, "2"); g_assert_cmpstr(qdict_get_try_str(qdict, "d"), ==, "3"); - QDECREF(qdict); + qobject_unref(qdict); /* Inconsistent dotted keys */ qdict = keyval_parse("a.b=1,a=2", NULL, &err); @@ -125,7 +125,7 @@ static void test_keyval_parse(void) qdict = keyval_parse("x=y,", NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, "y"); - QDECREF(qdict); + qobject_unref(qdict); /* Except when it isn't */ qdict = keyval_parse(",", NULL, &err); @@ -136,13 +136,13 @@ static void test_keyval_parse(void) qdict = keyval_parse("x=,,id=bar", NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, ",id=bar"); - QDECREF(qdict); + qobject_unref(qdict); /* Anti-social ID is left to caller (qemu_opts_parse() rejects it) */ qdict = keyval_parse("id=666", NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "666"); - QDECREF(qdict); + qobject_unref(qdict); /* Implied value not supported (unlike qemu_opts_parse()) */ qdict = keyval_parse("an,noaus,noaus=", NULL, &err); @@ -160,7 +160,7 @@ static void test_keyval_parse(void) g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "an"); g_assert_cmpstr(qdict_get_try_str(qdict, "aus"), ==, "off"); g_assert_cmpstr(qdict_get_try_str(qdict, "noaus"), ==, ""); - QDECREF(qdict); + qobject_unref(qdict); /* Implied dotted key */ qdict = keyval_parse("val", "eins.zwei", &error_abort); @@ -169,7 +169,7 @@ static void test_keyval_parse(void) g_assert(sub_qdict); g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(sub_qdict, "zwei"), ==, "val"); - QDECREF(qdict); + qobject_unref(qdict); /* Implied key with empty value (qemu_opts_parse() accepts this) */ qdict = keyval_parse(",", "implied", &err); @@ -198,7 +198,7 @@ static void check_list012(QList *qlist) qstr = qobject_to(QString, qlist_pop(qlist)); g_assert(qstr); g_assert_cmpstr(qstring_get_str(qstr), ==, expected[i]); - QDECREF(qstr); + qobject_unref(qstr); } g_assert(qlist_empty(qlist)); } @@ -218,14 +218,14 @@ static void test_keyval_parse_list(void) NULL, &error_abort); g_assert_cmpint(qdict_size(qdict), ==, 1); check_list012(qdict_get_qlist(qdict, "list")); - QDECREF(qdict); + qobject_unref(qdict); /* Multiple indexes, last one wins */ qdict = keyval_parse("list.1=goner,list.0=null,list.01=eins,list.2=zwei", NULL, &error_abort); g_assert_cmpint(qdict_size(qdict), ==, 1); check_list012(qdict_get_qlist(qdict, "list")); - QDECREF(qdict); + qobject_unref(qdict); /* List at deeper nesting */ qdict = keyval_parse("a.list.1=eins,a.list.00=null,a.list.2=zwei", @@ -234,7 +234,7 @@ static void test_keyval_parse_list(void) sub_qdict = qdict_get_qdict(qdict, "a"); g_assert_cmpint(qdict_size(sub_qdict), ==, 1); check_list012(qdict_get_qlist(sub_qdict, "list")); - QDECREF(qdict); + qobject_unref(qdict); /* Inconsistent dotted keys: both list and dictionary */ qdict = keyval_parse("a.b.c=1,a.b.0=2", NULL, &err); @@ -262,7 +262,7 @@ static void test_keyval_visit_bool(void) qdict = keyval_parse("bool1=on,bool2=off", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_bool(v, "bool1", &b, &error_abort); g_assert(b); @@ -274,7 +274,7 @@ static void test_keyval_visit_bool(void) qdict = keyval_parse("bool1=offer", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_bool(v, "bool1", &b, &err); error_free_or_abort(&err); @@ -292,7 +292,7 @@ static void test_keyval_visit_number(void) /* Lower limit zero */ qdict = keyval_parse("number1=0", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &error_abort); g_assert_cmpuint(u, ==, 0); @@ -304,7 +304,7 @@ static void test_keyval_visit_number(void) qdict = keyval_parse("number1=18446744073709551615,number2=-1", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &error_abort); g_assert_cmphex(u, ==, UINT64_MAX); @@ -318,7 +318,7 @@ static void test_keyval_visit_number(void) qdict = keyval_parse("number1=18446744073709551616", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &err); error_free_or_abort(&err); @@ -329,7 +329,7 @@ static void test_keyval_visit_number(void) qdict = keyval_parse("number1=-18446744073709551616", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &err); error_free_or_abort(&err); @@ -340,7 +340,7 @@ static void test_keyval_visit_number(void) qdict = keyval_parse("number1=0x2a,number2=052", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &error_abort); g_assert_cmpuint(u, ==, 42); @@ -354,7 +354,7 @@ static void test_keyval_visit_number(void) qdict = keyval_parse("number1=3.14,number2=08", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &err); error_free_or_abort(&err); @@ -374,7 +374,7 @@ static void test_keyval_visit_size(void) /* Lower limit zero */ qdict = keyval_parse("sz1=0", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmpuint(sz, ==, 0); @@ -390,7 +390,7 @@ static void test_keyval_visit_size(void) "sz3=9007199254740993", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmphex(sz, ==, 0x1fffffffffffff); @@ -407,7 +407,7 @@ static void test_keyval_visit_size(void) "sz2=9223372036854775295", /* 7ffffffffffffdff */ NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmphex(sz, ==, 0x7ffffffffffffc00); @@ -422,7 +422,7 @@ static void test_keyval_visit_size(void) "sz2=18446744073709550591", /* fffffffffffffbff */ NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmphex(sz, ==, 0xfffffffffffff800); @@ -437,7 +437,7 @@ static void test_keyval_visit_size(void) "sz2=18446744073709550592", /* fffffffffffffc00 */ NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &err); error_free_or_abort(&err); @@ -450,7 +450,7 @@ static void test_keyval_visit_size(void) qdict = keyval_parse("sz1=8b,sz2=1.5k,sz3=2M,sz4=0.1G,sz5=16777215T", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmpuint(sz, ==, 8); @@ -469,7 +469,7 @@ static void test_keyval_visit_size(void) /* Beyond limit with suffix */ qdict = keyval_parse("sz1=16777216T", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &err); error_free_or_abort(&err); @@ -479,7 +479,7 @@ static void test_keyval_visit_size(void) /* Trailing crap */ qdict = keyval_parse("sz1=16E,sz2=16Gi", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &err); error_free_or_abort(&err); @@ -498,7 +498,7 @@ static void test_keyval_visit_dict(void) qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_start_struct(v, "a", NULL, 0, &error_abort); visit_start_struct(v, "b", NULL, 0, &error_abort); @@ -516,7 +516,7 @@ static void test_keyval_visit_dict(void) qdict = keyval_parse("a.b=", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_start_struct(v, "a", NULL, 0, &error_abort); visit_type_int(v, "c", &i, &err); /* a.c missing */ @@ -539,7 +539,7 @@ static void test_keyval_visit_list(void) qdict = keyval_parse("a.0=,a.1=I,a.2.0=II", NULL, &error_abort); /* TODO empty list */ v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_start_list(v, "a", NULL, 0, &error_abort); visit_type_str(v, NULL, &s, &error_abort); @@ -562,7 +562,7 @@ static void test_keyval_visit_list(void) qdict = keyval_parse("a.0=,b.0.0=head", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_start_list(v, "a", NULL, 0, &error_abort); visit_check_list(v, &err); /* a[0] unexpected */ @@ -591,7 +591,7 @@ static void test_keyval_visit_optional(void) qdict = keyval_parse("a.b=1", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_optional(v, "b", &present); g_assert(!present); /* b missing */ @@ -627,7 +627,7 @@ static void test_keyval_visit_alternate(void) */ qdict = keyval_parse("a=1,b=2,c=on", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_AltStrObj(v, "a", &aso, &error_abort); g_assert_cmpint(aso->type, ==, QTYPE_QSTRING); @@ -651,19 +651,19 @@ static void test_keyval_visit_any(void) qdict = keyval_parse("a.0=null,a.1=1", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_any(v, "a", &any, &error_abort); qlist = qobject_to(QList, any); g_assert(qlist); qstr = qobject_to(QString, qlist_pop(qlist)); g_assert_cmpstr(qstring_get_str(qstr), ==, "null"); - QDECREF(qstr); + qobject_unref(qstr); qstr = qobject_to(QString, qlist_pop(qlist)); g_assert_cmpstr(qstring_get_str(qstr), ==, "1"); g_assert(qlist_empty(qlist)); - QDECREF(qstr); - qobject_decref(any); + qobject_unref(qstr); + qobject_unref(any); visit_check_struct(v, &error_abort); visit_end_struct(v, NULL); visit_free(v); diff --git a/tests/test-netfilter.c b/tests/test-netfilter.c index 95f7839aef..e47075dd06 100644 --- a/tests/test-netfilter.c +++ b/tests/test-netfilter.c @@ -29,7 +29,7 @@ static void add_one_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'object-del'," " 'arguments': {" @@ -37,7 +37,7 @@ static void add_one_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } /* add a netfilter to a netdev and then remove the netdev */ @@ -57,7 +57,7 @@ static void remove_netdev_with_one_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'netdev_del'," " 'arguments': {" @@ -65,7 +65,7 @@ static void remove_netdev_with_one_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); /* add back the netdev */ response = qmp("{'execute': 'netdev_add'," @@ -75,7 +75,7 @@ static void remove_netdev_with_one_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } /* add multi(2) netfilters to a netdev and then remove them */ @@ -95,7 +95,7 @@ static void add_multi_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'object-add'," " 'arguments': {" @@ -109,7 +109,7 @@ static void add_multi_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'object-del'," " 'arguments': {" @@ -117,7 +117,7 @@ static void add_multi_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'object-del'," " 'arguments': {" @@ -125,7 +125,7 @@ static void add_multi_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } /* add multi(2) netfilters to a netdev and then remove the netdev */ @@ -145,7 +145,7 @@ static void remove_netdev_with_multi_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'object-add'," " 'arguments': {" @@ -159,7 +159,7 @@ static void remove_netdev_with_multi_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'netdev_del'," " 'arguments': {" @@ -167,7 +167,7 @@ static void remove_netdev_with_multi_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); /* add back the netdev */ response = qmp("{'execute': 'netdev_add'," @@ -177,7 +177,7 @@ static void remove_netdev_with_multi_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } int main(int argc, char **argv) diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c index 2c422abcd4..77dd72b403 100644 --- a/tests/test-qemu-opts.c +++ b/tests/test-qemu-opts.c @@ -887,7 +887,7 @@ static void test_opts_to_qdict_basic(void) g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); g_assert_false(qdict_haskey(dict, "number2")); - QDECREF(dict); + qobject_unref(dict); qemu_opts_del(opts); } @@ -914,7 +914,7 @@ static void test_opts_to_qdict_filtered(void) g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); g_assert_false(qdict_haskey(dict, "number2")); g_assert_false(qdict_haskey(dict, "bool1")); - QDECREF(dict); + qobject_unref(dict); dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false); g_assert(dict != NULL); @@ -924,7 +924,7 @@ static void test_opts_to_qdict_filtered(void) g_assert_false(qdict_haskey(dict, "str3")); g_assert_false(qdict_haskey(dict, "number1")); g_assert_false(qdict_haskey(dict, "number2")); - QDECREF(dict); + qobject_unref(dict); /* Now delete converted options from opts */ dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true); @@ -935,7 +935,7 @@ static void test_opts_to_qdict_filtered(void) g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); g_assert_false(qdict_haskey(dict, "number2")); g_assert_false(qdict_haskey(dict, "bool1")); - QDECREF(dict); + qobject_unref(dict); dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true); g_assert(dict != NULL); @@ -945,7 +945,7 @@ static void test_opts_to_qdict_filtered(void) g_assert_false(qdict_haskey(dict, "str3")); g_assert_false(qdict_haskey(dict, "number1")); g_assert_false(qdict_haskey(dict, "number2")); - QDECREF(dict); + qobject_unref(dict); g_assert_true(QTAILQ_EMPTY(&opts->head)); @@ -978,13 +978,13 @@ static void test_opts_to_qdict_duplicates(void) dict = qemu_opts_to_qdict(opts, NULL); g_assert(dict != NULL); g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); - QDECREF(dict); + qobject_unref(dict); /* The last one still wins if entries are deleted, and both are deleted */ dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true); g_assert(dict != NULL); g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); - QDECREF(dict); + qobject_unref(dict); g_assert_true(QTAILQ_EMPTY(&opts->head)); diff --git a/tests/test-qga.c b/tests/test-qga.c index e6ab788f31..18e63cb533 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -180,7 +180,7 @@ static void test_qga_sync_delimited(gconstpointer fix) v = qdict_get_int(ret, "return"); g_assert_cmpint(r, ==, v); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_sync(gconstpointer fix) @@ -212,7 +212,7 @@ static void test_qga_sync(gconstpointer fix) v = qdict_get_int(ret, "return"); g_assert_cmpint(r, ==, v); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_ping(gconstpointer fix) @@ -224,7 +224,7 @@ static void test_qga_ping(gconstpointer fix) g_assert_nonnull(ret); qmp_assert_no_error(ret); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_invalid_args(gconstpointer fix) @@ -244,7 +244,7 @@ static void test_qga_invalid_args(gconstpointer fix) g_assert_cmpstr(class, ==, "GenericError"); g_assert_cmpstr(desc, ==, "Parameter 'foo' is unexpected"); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_invalid_cmd(gconstpointer fix) @@ -263,7 +263,7 @@ static void test_qga_invalid_cmd(gconstpointer fix) g_assert_cmpstr(class, ==, "CommandNotFound"); g_assert_cmpint(strlen(desc), >, 0); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_info(gconstpointer fix) @@ -280,7 +280,7 @@ static void test_qga_info(gconstpointer fix) version = qdict_get_try_str(val, "version"); g_assert_cmpstr(version, ==, QEMU_VERSION); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_get_vcpus(gconstpointer fix) @@ -300,7 +300,7 @@ static void test_qga_get_vcpus(gconstpointer fix) g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id")); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_get_fsinfo(gconstpointer fix) @@ -324,7 +324,7 @@ static void test_qga_get_fsinfo(gconstpointer fix) g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk")); } - QDECREF(ret); + qobject_unref(ret); } static void test_qga_get_memory_block_info(gconstpointer fix) @@ -344,7 +344,7 @@ static void test_qga_get_memory_block_info(gconstpointer fix) g_assert_cmpint(size, >, 0); } - QDECREF(ret); + qobject_unref(ret); } static void test_qga_get_memory_blocks(gconstpointer fix) @@ -369,7 +369,7 @@ static void test_qga_get_memory_blocks(gconstpointer fix) } } - QDECREF(ret); + qobject_unref(ret); } static void test_qga_network_get_interfaces(gconstpointer fix) @@ -388,7 +388,7 @@ static void test_qga_network_get_interfaces(gconstpointer fix) entry = qlist_first(list); g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_file_ops(gconstpointer fix) @@ -410,7 +410,7 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_nonnull(ret); qmp_assert_no_error(ret); id = qdict_get_int(ret, "return"); - QDECREF(ret); + qobject_unref(ret); enc = g_base64_encode(helloworld, sizeof(helloworld)); /* write */ @@ -426,7 +426,7 @@ static void test_qga_file_ops(gconstpointer fix) eof = qdict_get_bool(val, "eof"); g_assert_cmpint(count, ==, sizeof(helloworld)); g_assert_cmpint(eof, ==, 0); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* flush */ @@ -434,7 +434,7 @@ static void test_qga_file_ops(gconstpointer fix) " 'arguments': {'handle': %" PRId64 "} }", id); ret = qmp_fd(fixture->fd, cmd); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* close */ @@ -442,7 +442,7 @@ static void test_qga_file_ops(gconstpointer fix) " 'arguments': {'handle': %" PRId64 "} }", id); ret = qmp_fd(fixture->fd, cmd); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* check content */ @@ -462,7 +462,7 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_nonnull(ret); qmp_assert_no_error(ret); id = qdict_get_int(ret, "return"); - QDECREF(ret); + qobject_unref(ret); /* read */ cmd = g_strdup_printf("{'execute': 'guest-file-read'," @@ -477,7 +477,7 @@ static void test_qga_file_ops(gconstpointer fix) g_assert(eof); g_assert_cmpstr(b64, ==, enc); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); g_free(enc); @@ -493,7 +493,7 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_cmpint(count, ==, 0); g_assert(eof); g_assert_cmpstr(b64, ==, ""); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* seek */ @@ -508,7 +508,7 @@ static void test_qga_file_ops(gconstpointer fix) eof = qdict_get_bool(val, "eof"); g_assert_cmpint(count, ==, 6); g_assert(!eof); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* partial read */ @@ -527,7 +527,7 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_cmpmem(dec, count, helloworld + 6, sizeof(helloworld) - 6); g_free(dec); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* close */ @@ -535,7 +535,7 @@ static void test_qga_file_ops(gconstpointer fix) " 'arguments': {'handle': %" PRId64 "} }", id); ret = qmp_fd(fixture->fd, cmd); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); } @@ -555,7 +555,7 @@ static void test_qga_file_write_read(gconstpointer fix) g_assert_nonnull(ret); qmp_assert_no_error(ret); id = qdict_get_int(ret, "return"); - QDECREF(ret); + qobject_unref(ret); enc = g_base64_encode(helloworld, sizeof(helloworld)); /* write */ @@ -571,7 +571,7 @@ static void test_qga_file_write_read(gconstpointer fix) eof = qdict_get_bool(val, "eof"); g_assert_cmpint(count, ==, sizeof(helloworld)); g_assert_cmpint(eof, ==, 0); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* read (check implicit flush) */ @@ -586,7 +586,7 @@ static void test_qga_file_write_read(gconstpointer fix) g_assert_cmpint(count, ==, 0); g_assert(eof); g_assert_cmpstr(b64, ==, ""); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* seek to 0 */ @@ -601,7 +601,7 @@ static void test_qga_file_write_read(gconstpointer fix) eof = qdict_get_bool(val, "eof"); g_assert_cmpint(count, ==, 0); g_assert(!eof); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* read */ @@ -616,7 +616,7 @@ static void test_qga_file_write_read(gconstpointer fix) g_assert_cmpint(count, ==, sizeof(helloworld)); g_assert(eof); g_assert_cmpstr(b64, ==, enc); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); g_free(enc); @@ -625,7 +625,7 @@ static void test_qga_file_write_read(gconstpointer fix) " 'arguments': {'handle': %" PRId64 "} }", id); ret = qmp_fd(fixture->fd, cmd); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); } @@ -642,7 +642,7 @@ static void test_qga_get_time(gconstpointer fix) time = qdict_get_int(ret, "return"); g_assert_cmpint(time, >, 0); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_blacklist(gconstpointer data) @@ -661,7 +661,7 @@ static void test_qga_blacklist(gconstpointer data) desc = qdict_get_try_str(error, "desc"); g_assert_cmpstr(class, ==, "GenericError"); g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); - QDECREF(ret); + qobject_unref(ret); ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); g_assert_nonnull(ret); @@ -670,12 +670,12 @@ static void test_qga_blacklist(gconstpointer data) desc = qdict_get_try_str(error, "desc"); g_assert_cmpstr(class, ==, "GenericError"); g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); - QDECREF(ret); + qobject_unref(ret); /* check something work */ ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); qmp_assert_no_error(ret); - QDECREF(ret); + qobject_unref(ret); fixture_tear_down(&fix, NULL); } @@ -772,7 +772,7 @@ static void test_qga_fsfreeze_status(gconstpointer fix) status = qdict_get_try_str(ret, "return"); g_assert_cmpstr(status, ==, "thawed"); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_guest_exec(gconstpointer fix) @@ -795,7 +795,7 @@ static void test_qga_guest_exec(gconstpointer fix) val = qdict_get_qdict(ret, "return"); pid = qdict_get_int(val, "pid"); g_assert_cmpint(pid, >, 0); - QDECREF(ret); + qobject_unref(ret); /* wait for completion */ now = g_get_monotonic_time(); @@ -807,7 +807,7 @@ static void test_qga_guest_exec(gconstpointer fix) val = qdict_get_qdict(ret, "return"); exited = qdict_get_bool(val, "exited"); if (!exited) { - QDECREF(ret); + qobject_unref(ret); } } while (!exited && g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); @@ -822,7 +822,7 @@ static void test_qga_guest_exec(gconstpointer fix) g_assert_cmpint(len, ==, 12); g_assert_cmpstr((char *)decoded, ==, "\" test_str \""); g_free(decoded); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_guest_exec_invalid(gconstpointer fix) @@ -841,7 +841,7 @@ static void test_qga_guest_exec_invalid(gconstpointer fix) desc = qdict_get_str(error, "desc"); g_assert_cmpstr(class, ==, "GenericError"); g_assert_cmpint(strlen(desc), >, 0); - QDECREF(ret); + qobject_unref(ret); /* invalid pid */ ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec-status'," @@ -853,7 +853,7 @@ static void test_qga_guest_exec_invalid(gconstpointer fix) desc = qdict_get_str(error, "desc"); g_assert_cmpstr(class, ==, "GenericError"); g_assert_cmpint(strlen(desc), >, 0); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_guest_get_osinfo(gconstpointer data) @@ -905,7 +905,7 @@ static void test_qga_guest_get_osinfo(gconstpointer data) g_assert_nonnull(str); g_assert_cmpstr(str, ==, "unit-test"); - QDECREF(ret); + qobject_unref(ret); g_free(env[0]); fixture_tear_down(&fixture, NULL); } diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index db690cc5ae..e0ed461f58 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -106,8 +106,8 @@ static void test_dispatch_cmd(void) assert(resp != NULL); assert(!qdict_haskey(qobject_to(QDict, resp), "error")); - qobject_decref(resp); - QDECREF(req); + qobject_unref(resp); + qobject_unref(req); } /* test commands that return an error due to invalid parameters */ @@ -123,8 +123,8 @@ static void test_dispatch_cmd_failure(void) assert(resp != NULL); assert(qdict_haskey(qobject_to(QDict, resp), "error")); - qobject_decref(resp); - QDECREF(req); + qobject_unref(resp); + qobject_unref(req); /* check that with extra arguments it throws an error */ req = qdict_new(); @@ -137,8 +137,8 @@ static void test_dispatch_cmd_failure(void) assert(resp != NULL); assert(qdict_haskey(qobject_to(QDict, resp), "error")); - qobject_decref(resp); - QDECREF(req); + qobject_unref(resp); + qobject_unref(req); } static QObject *test_qmp_dispatch(QDict *req) @@ -153,8 +153,8 @@ static QObject *test_qmp_dispatch(QDict *req) assert(resp && !qdict_haskey(resp, "error")); ret = qdict_get(resp, "return"); assert(ret); - qobject_incref(ret); - qobject_decref(resp_obj); + qobject_ref(ret); + qobject_unref(resp_obj); return ret; } @@ -195,7 +195,7 @@ static void test_dispatch_cmd_io(void) assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422); assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2")); assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); - QDECREF(ret); + qobject_unref(ret); qdict_put_int(args3, "a", 66); qdict_put(req, "arguments", args3); @@ -204,9 +204,9 @@ static void test_dispatch_cmd_io(void) ret3 = qobject_to(QNum, test_qmp_dispatch(req)); g_assert(qnum_get_try_int(ret3, &val)); g_assert_cmpint(val, ==, 66); - QDECREF(ret3); + qobject_unref(ret3); - QDECREF(req); + qobject_unref(req); } /* test generated dealloc functions for generated types */ @@ -257,7 +257,7 @@ static void test_dealloc_partial(void) v = qobject_input_visitor_new(QOBJECT(ud2_dict)); visit_type_UserDefTwo(v, NULL, &ud2, &err); visit_free(v); - QDECREF(ud2_dict); + qobject_unref(ud2_dict); } /* verify that visit_type_XXX() cleans up properly on error */ diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index bb1036615f..3a7c227a1d 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -133,7 +133,7 @@ static void event_prepare(TestEventData *data, static void event_teardown(TestEventData *data, const void *unused) { - QDECREF(data->expect); + qobject_unref(data->expect); test_event_data = NULL; g_mutex_unlock(&test_event_lock); diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c index 6dc59c6211..0f4d234c3f 100644 --- a/tests/test-qobject-input-visitor.c +++ b/tests/test-qobject-input-visitor.c @@ -35,7 +35,7 @@ typedef struct TestInputVisitorData { static void visitor_input_teardown(TestInputVisitorData *data, const void *unused) { - qobject_decref(data->obj); + qobject_unref(data->obj); data->obj = NULL; if (data->qiv) { @@ -483,7 +483,7 @@ static void test_visitor_in_any(TestInputVisitorData *data, g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, -42); - qobject_decref(res); + qobject_unref(res); v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); visit_type_any(v, NULL, &res, &error_abort); @@ -505,7 +505,7 @@ static void test_visitor_in_any(TestInputVisitorData *data, qstring = qobject_to(QString, qobj); g_assert(qstring); g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); - qobject_decref(res); + qobject_unref(res); } static void test_visitor_in_null(TestInputVisitorData *data, @@ -530,7 +530,7 @@ static void test_visitor_in_null(TestInputVisitorData *data, visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_null(v, "a", &null, &error_abort); g_assert(qobject_type(QOBJECT(null)) == QTYPE_QNULL); - QDECREF(null); + qobject_unref(null); visit_type_null(v, "b", &null, &err); error_free_or_abort(&err); g_assert(!null); @@ -1262,7 +1262,7 @@ static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data, g_assert(schema); qapi_free_SchemaInfoList(schema); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); } diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c index ecf21c0f31..be635854b4 100644 --- a/tests/test-qobject-output-visitor.c +++ b/tests/test-qobject-output-visitor.c @@ -40,7 +40,7 @@ static void visitor_output_teardown(TestOutputVisitorData *data, { visit_free(data->ov); data->ov = NULL; - qobject_decref(data->obj); + qobject_unref(data->obj); data->obj = NULL; } @@ -346,7 +346,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data, g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, -42); - qobject_decref(qobj); + qobject_unref(qobj); visitor_reset(data); qdict = qdict_new(); @@ -355,7 +355,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data, qdict_put_str(qdict, "string", "foo"); qobj = QOBJECT(qdict); visit_type_any(data->ov, NULL, &qobj, &error_abort); - qobject_decref(qobj); + qobject_unref(qobj); qdict = qobject_to(QDict, visitor_get(data)); g_assert(qdict); qnum = qobject_to(QNum, qdict_get(qdict, "integer")); @@ -630,7 +630,7 @@ static void check_native_list(QObject *qobj, qvalue = qobject_to(QNum, tmp); g_assert(qnum_get_try_uint(qvalue, &val)); g_assert_cmpint(val, ==, i); - qobject_decref(qlist_pop(qlist)); + qobject_unref(qlist_pop(qlist)); } break; @@ -654,7 +654,7 @@ static void check_native_list(QObject *qobj, qvalue = qobject_to(QNum, tmp); g_assert(qnum_get_try_int(qvalue, &val)); g_assert_cmpint(val, ==, i); - qobject_decref(qlist_pop(qlist)); + qobject_unref(qlist_pop(qlist)); } break; case USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN: @@ -665,7 +665,7 @@ static void check_native_list(QObject *qobj, g_assert(tmp); qvalue = qobject_to(QBool, tmp); g_assert_cmpint(qbool_get_bool(qvalue), ==, i % 3 == 0); - qobject_decref(qlist_pop(qlist)); + qobject_unref(qlist_pop(qlist)); } break; case USER_DEF_NATIVE_LIST_UNION_KIND_STRING: @@ -678,7 +678,7 @@ static void check_native_list(QObject *qobj, qvalue = qobject_to(QString, tmp); sprintf(str, "%d", i); g_assert_cmpstr(qstring_get_str(qvalue), ==, str); - qobject_decref(qlist_pop(qlist)); + qobject_unref(qlist_pop(qlist)); } break; case USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER: @@ -695,7 +695,7 @@ static void check_native_list(QObject *qobj, g_string_printf(double_actual, "%.6f", qnum_get_double(qvalue)); g_assert_cmpstr(double_actual->str, ==, double_expected->str); - qobject_decref(qlist_pop(qlist)); + qobject_unref(qlist_pop(qlist)); g_string_free(double_expected, true); g_string_free(double_actual, true); } @@ -703,7 +703,7 @@ static void check_native_list(QObject *qobj, default: g_assert_not_reached(); } - QDECREF(qlist); + qobject_unref(qlist); } static void test_native_list(TestOutputVisitorData *data, diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index d18d90db2c..1c5a8b94ea 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -1036,10 +1036,10 @@ static void qmp_deserialize(void **native_out, void *datap, output_json = qobject_to_json(obj_orig); obj = qobject_from_json(qstring_get_str(output_json), &error_abort); - QDECREF(output_json); + qobject_unref(output_json); d->qiv = qobject_input_visitor_new(obj); - qobject_decref(obj_orig); - qobject_decref(obj); + qobject_unref(obj_orig); + qobject_unref(obj); visit(d->qiv, native_out, errp); } diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c index 02e41843fc..84ce9c71ae 100644 --- a/tests/test-x86-cpuid-compat.c +++ b/tests/test-x86-cpuid-compat.c @@ -19,7 +19,7 @@ static char *get_cpu0_qom_path(void) cpu0 = qobject_to(QDict, qlist_peek(ret)); path = g_strdup(qdict_get_str(cpu0, "qom_path")); - QDECREF(resp); + qobject_unref(resp); return path; } @@ -30,8 +30,8 @@ static QObject *qom_get(const char *path, const char *prop) " 'property': %s } }", path, prop); QObject *ret = qdict_get(resp, "return"); - qobject_incref(ret); - QDECREF(resp); + qobject_ref(ret); + qobject_unref(resp); return ret; } @@ -41,7 +41,7 @@ static bool qom_get_bool(const char *path, const char *prop) QBool *value = qobject_to(QBool, qom_get(path, prop)); bool b = qbool_get_bool(value); - QDECREF(value); + qobject_unref(value); return b; } #endif @@ -66,7 +66,7 @@ static void test_cpuid_prop(const void *data) g_assert_cmpint(val, ==, args->expected_value); qtest_end(); - QDECREF(value); + qobject_unref(value); g_free(path); } @@ -142,8 +142,8 @@ static void test_feature_flag(const void *data) g_assert(!!(value & (1U << args->bitnr)) == args->expected_value); - QDECREF(present); - QDECREF(filtered); + qobject_unref(present); + qobject_unref(filtered); g_free(path); } diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c index 66c7a0147f..d093cffe1e 100644 --- a/tests/tmp105-test.c +++ b/tests/tmp105-test.c @@ -74,7 +74,7 @@ static int qmp_tmp105_get_temperature(const char *id) "'property': 'temperature' } }", id); g_assert(qdict_haskey(response, "return")); ret = qdict_get_int(response, "return"); - QDECREF(response); + qobject_unref(response); return ret; } @@ -85,7 +85,7 @@ static void qmp_tmp105_set_temperature(const char *id, int value) response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, " "'property': 'temperature', 'value': %d } }", id, value); g_assert(qdict_haskey(response, "return")); - QDECREF(response); + qobject_unref(response); } #define TMP105_PRECISION (1000/16) diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 61d997253c..bbc8091286 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -727,7 +727,7 @@ static void test_migrate(void) rsp = qmp("{ 'execute': 'migrate_set_speed'," "'arguments': { 'value': 10 } }"); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); cmd = g_strdup_printf("{ 'execute': 'migrate'," "'arguments': { 'uri': '%s' } }", @@ -735,7 +735,7 @@ static void test_migrate(void) rsp = qmp(cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); wait_for_log_fd(s); @@ -751,7 +751,7 @@ static void test_migrate(void) rsp = qmp("{ 'execute': 'migrate_set_speed'," "'arguments': { 'value': 0 } }"); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); qmp_eventwait("STOP"); diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index 0a3c5dd257..b285a262e9 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -173,7 +173,7 @@ static void rx_stop_cont_test(QVirtioDevice *dev, qvirtqueue_kick(dev, vq, free_head); rsp = qmp("{ 'execute' : 'stop'}"); - QDECREF(rsp); + qobject_unref(rsp); ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); @@ -182,9 +182,9 @@ static void rx_stop_cont_test(QVirtioDevice *dev, * ensure the packet data gets queued in QEMU, before we do 'cont'. */ rsp = qmp("{ 'execute' : 'query-status'}"); - QDECREF(rsp); + qobject_unref(rsp); rsp = qmp("{ 'execute' : 'cont'}"); - QDECREF(rsp); + qobject_unref(rsp); qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_NET_TIMEOUT_US); memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c index 2ec274e37c..8d915c610c 100644 --- a/tests/vmgenid-test.c +++ b/tests/vmgenid-test.c @@ -125,7 +125,7 @@ static void read_guid_from_monitor(QemuUUID *guid) guid_str = qdict_get_str(rsp_ret, "guid"); g_assert(qemu_uuid_parse(guid_str, guid) == 0); } - QDECREF(rsp); + qobject_unref(rsp); } static char disk[] = "tests/vmgenid-test-disk-XXXXXX"; diff --git a/tests/wdt_ib700-test.c b/tests/wdt_ib700-test.c index 3b5bbcf007..797288d939 100644 --- a/tests/wdt_ib700-test.c +++ b/tests/wdt_ib700-test.c @@ -16,7 +16,7 @@ static void qmp_check_no_event(QTestState *s) { QDict *resp = qtest_qmp(s, "{'execute':'query-status'}"); g_assert(qdict_haskey(resp, "return")); - QDECREF(resp); + qobject_unref(resp); } static QDict *ib700_program_and_wait(QTestState *s) @@ -48,8 +48,8 @@ static QDict *ib700_program_and_wait(QTestState *s) qtest_clock_step(s, 2 * NANOSECONDS_PER_SECOND); event = qtest_qmp_eventwait_ref(s, "WATCHDOG"); data = qdict_get_qdict(event, "data"); - QINCREF(data); - QDECREF(event); + qobject_ref(data); + qobject_unref(event); return data; } @@ -62,7 +62,7 @@ static void ib700_pause(void) qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "pause")); - QDECREF(d); + qobject_unref(d); qtest_qmp_eventwait(s, "STOP"); qtest_quit(s); } @@ -75,7 +75,7 @@ static void ib700_reset(void) qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); - QDECREF(d); + qobject_unref(d); qtest_qmp_eventwait(s, "RESET"); qtest_quit(s); } @@ -89,7 +89,7 @@ static void ib700_shutdown(void) qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); - QDECREF(d); + qobject_unref(d); qtest_qmp_eventwait(s, "SHUTDOWN"); qtest_quit(s); } @@ -102,7 +102,7 @@ static void ib700_none(void) qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "none")); - QDECREF(d); + qobject_unref(d); qtest_quit(s); } diff --git a/util/keyval.c b/util/keyval.c index 1c7351a233..13def4af54 100644 --- a/util/keyval.c +++ b/util/keyval.c @@ -126,7 +126,7 @@ static int key_to_index(const char *key, const char **end) * Else, fail because we have conflicting needs on how to map * @key_in_cur. * In any case, take over the reference to @value, i.e. if the caller - * wants to hold on to a reference, it needs to QINCREF(). + * wants to hold on to a reference, it needs to qobject_ref(). * Use @key up to @key_cursor to identify the key in error messages. * On success, return the mapped value. * On failure, store an error through @errp and return NULL. @@ -143,7 +143,7 @@ static QObject *keyval_parse_put(QDict *cur, if (qobject_type(old) != (value ? QTYPE_QSTRING : QTYPE_QDICT)) { error_setg(errp, "Parameters '%.*s.*' used inconsistently", (int)(key_cursor - key), key); - QDECREF(value); + qobject_unref(value); return NULL; } if (!value) { @@ -375,10 +375,10 @@ static QObject *keyval_listify(QDict *cur, GSList *key_of_cur, Error **errp) error_setg(errp, "Parameter '%s%d' missing", key, i); g_free(key); g_free(elt); - QDECREF(list); + qobject_unref(list); return NULL; } - qobject_incref(elt[i]); + qobject_ref(elt[i]); qlist_append_obj(list, elt[i]); } @@ -404,7 +404,7 @@ QDict *keyval_parse(const char *params, const char *implied_key, while (*s) { s = keyval_parse_one(qdict, s, implied_key, errp); if (!s) { - QDECREF(qdict); + qobject_unref(qdict); return NULL; } implied_key = NULL; @@ -412,7 +412,7 @@ QDict *keyval_parse(const char *params, const char *implied_key, listified = keyval_listify(qdict, NULL, errp); if (!listified) { - QDECREF(qdict); + qobject_unref(qdict); return NULL; } assert(listified == QOBJECT(qdict)); diff --git a/util/qemu-config.c b/util/qemu-config.c index 20f7d1429d..14d84022dc 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -562,8 +562,8 @@ static void config_parse_qdict_section(QDict *options, QemuOptsList *opts, } out: - QDECREF(subqdict); - QDECREF(list); + qobject_unref(subqdict); + qobject_unref(list); } void qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, From f5a74a5a50387c6f980b2e2f94f062487a1826da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 19 Apr 2018 17:01:44 +0200 Subject: [PATCH 0231/2380] qobject: Modify qobject_ref() to return obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For convenience and clarity, make it possible to call qobject_ref() at the time when the reference is associated with a variable, or argument, by making qobject_ref() return the same pointer as given. Use that to simplify the callers. Signed-off-by: Marc-André Lureau Reviewed-by: Eric Blake Message-Id: <20180419150145.24795-5-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Useless change to qobject_ref_impl() dropped, commit message improved slightly] Signed-off-by: Markus Armbruster --- block.c | 8 ++++---- block/blkdebug.c | 7 +++---- block/blkverify.c | 8 ++++---- block/null.c | 3 +-- block/nvme.c | 3 +-- block/quorum.c | 4 ++-- include/qapi/qmp/qnull.h | 3 +-- include/qapi/qmp/qobject.h | 9 ++++++++- monitor.c | 20 +++++++------------- qapi/qobject-input-visitor.c | 6 ++---- qapi/qobject-output-visitor.c | 7 +++---- qobject/qdict.c | 33 +++++++++++---------------------- 12 files changed, 47 insertions(+), 64 deletions(-) diff --git a/block.c b/block.c index 55a79845be..676e57f562 100644 --- a/block.c +++ b/block.c @@ -5134,8 +5134,8 @@ static bool append_open_options(QDict *d, BlockDriverState *bs) continue; } - qobject_ref(qdict_entry_value(entry)); - qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry)); + qdict_put_obj(d, qdict_entry_key(entry), + qobject_ref(qdict_entry_value(entry))); found_any = true; } @@ -5207,8 +5207,8 @@ void bdrv_refresh_filename(BlockDriverState *bs) * suffices without querying the (exact_)filename of this BDS. */ if (bs->file->bs->full_open_options) { qdict_put_str(opts, "driver", drv->format_name); - qobject_ref(bs->file->bs->full_open_options); - qdict_put(opts, "file", bs->file->bs->full_open_options); + qdict_put(opts, "file", + qobject_ref(bs->file->bs->full_open_options)); bs->full_open_options = opts; } else { diff --git a/block/blkdebug.c b/block/blkdebug.c index 689703d386..053372c22e 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -845,13 +845,12 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) opts = qdict_new(); qdict_put_str(opts, "driver", "blkdebug"); - qobject_ref(bs->file->bs->full_open_options); - qdict_put(opts, "image", bs->file->bs->full_open_options); + qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options)); for (e = qdict_first(options); e; e = qdict_next(options, e)) { if (strcmp(qdict_entry_key(e), "x-image")) { - qobject_ref(qdict_entry_value(e)); - qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); + qdict_put_obj(opts, qdict_entry_key(e), + qobject_ref(qdict_entry_value(e))); } } diff --git a/block/blkverify.c b/block/blkverify.c index 3cffcb1ca6..754cc9e857 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -291,10 +291,10 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) QDict *opts = qdict_new(); qdict_put_str(opts, "driver", "blkverify"); - qobject_ref(bs->file->bs->full_open_options); - qdict_put(opts, "raw", bs->file->bs->full_open_options); - qobject_ref(s->test_file->bs->full_open_options); - qdict_put(opts, "test", s->test_file->bs->full_open_options); + qdict_put(opts, "raw", + qobject_ref(bs->file->bs->full_open_options)); + qdict_put(opts, "test", + qobject_ref(s->test_file->bs->full_open_options)); bs->full_open_options = opts; } diff --git a/block/null.c b/block/null.c index 700a2d0857..3944550f67 100644 --- a/block/null.c +++ b/block/null.c @@ -244,7 +244,6 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs, static void null_refresh_filename(BlockDriverState *bs, QDict *opts) { - qobject_ref(opts); qdict_del(opts, "filename"); if (!qdict_size(opts)) { @@ -253,7 +252,7 @@ static void null_refresh_filename(BlockDriverState *bs, QDict *opts) } qdict_put_str(opts, "driver", bs->drv->format_name); - bs->full_open_options = opts; + bs->full_open_options = qobject_ref(opts); } static BlockDriver bdrv_null_co = { diff --git a/block/nvme.c b/block/nvme.c index e192da9ee1..6f71122bf5 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1073,7 +1073,6 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state, static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) { - qobject_ref(opts); qdict_del(opts, "filename"); if (!qdict_size(opts)) { @@ -1082,7 +1081,7 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) } qdict_put_str(opts, "driver", bs->drv->format_name); - bs->full_open_options = opts; + bs->full_open_options = qobject_ref(opts); } static void nvme_refresh_limits(BlockDriverState *bs, Error **errp) diff --git a/block/quorum.c b/block/quorum.c index 862cea366d..a5051da56e 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -1082,8 +1082,8 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) children = qlist_new(); for (i = 0; i < s->num_children; i++) { - qobject_ref(s->children[i]->bs->full_open_options); - qlist_append(children, s->children[i]->bs->full_open_options); + qlist_append(children, + qobject_ref(s->children[i]->bs->full_open_options)); } opts = qdict_new(); diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h index 75b29c6a39..c1426882c5 100644 --- a/include/qapi/qmp/qnull.h +++ b/include/qapi/qmp/qnull.h @@ -23,8 +23,7 @@ extern QNull qnull_; static inline QNull *qnull(void) { - qobject_ref(&qnull_); - return &qnull_; + return qobject_ref(&qnull_); } bool qnull_is_equal(const QObject *x, const QObject *y); diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index e20006faf5..fcfd549220 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -103,8 +103,15 @@ static inline void qobject_unref_impl(QObject *obj) /** * qobject_ref(): Increment QObject's reference count + * + * Returns: the same @obj. The type of @obj will be propagated to the + * return type. */ -#define qobject_ref(obj) qobject_ref_impl(QOBJECT(obj)) +#define qobject_ref(obj) ({ \ + typeof(obj) _o = (obj); \ + qobject_ref_impl(QOBJECT(_o)); \ + _o; \ +}) /** * qobject_unref(): Decrement QObject's reference count, deallocate diff --git a/monitor.c b/monitor.c index 4f43eee2bb..46814af533 100644 --- a/monitor.c +++ b/monitor.c @@ -494,9 +494,8 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) * caller won't free the data (which will be finally freed in * responder thread). */ - qobject_ref(data); qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - g_queue_push_tail(mon->qmp.qmp_responses, data); + g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(data)); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); qemu_bh_schedule(mon_global.qmp_respond_bh); } else { @@ -614,8 +613,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) * replacing a prior stored event if any. */ qobject_unref(evstate->qdict); - evstate->qdict = qdict; - qobject_ref(evstate->qdict); + evstate->qdict = qobject_ref(qdict); } else { /* * Last send was (at least) evconf->rate ns ago. @@ -629,8 +627,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) evstate = g_new(MonitorQAPIEventState, 1); evstate->event = event; - evstate->data = data; - qobject_ref(evstate->data); + evstate->data = qobject_ref(data); evstate->qdict = NULL; evstate->timer = timer_new_ns(event_clock_type, monitor_qapi_event_handler, @@ -4048,9 +4045,7 @@ static void monitor_qmp_respond(Monitor *mon, QObject *rsp, if (rsp) { if (id) { - /* This is for the qdict below. */ - qobject_ref(id); - qdict_put_obj(qobject_to(QDict, rsp), "id", id); + qdict_put_obj(qobject_to(QDict, rsp), "id", qobject_ref(id)); } monitor_json_emitter(mon, rsp); @@ -4190,15 +4185,14 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) goto err; } - qobject_ref(id); - qdict_del(qdict, "id"); - req_obj = g_new0(QMPRequest, 1); req_obj->mon = mon; - req_obj->id = id; + req_obj->id = qobject_ref(id); req_obj->req = req; req_obj->need_resume = false; + qdict_del(qdict, "id"); + if (qmp_is_oob(qdict)) { /* Out-Of-Band (OOB) requests are executed directly in parser. */ trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(req_obj->id) diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 7a290c4a3f..da57f4cc24 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -588,8 +588,7 @@ static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj, return; } - qobject_ref(qobj); - *obj = qobj; + *obj = qobject_ref(qobj); } static void qobject_input_type_null(Visitor *v, const char *name, @@ -677,8 +676,7 @@ static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) v->visitor.optional = qobject_input_optional; v->visitor.free = qobject_input_free; - v->root = obj; - qobject_ref(obj); + v->root = qobject_ref(obj); return v; } diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index 3a933b489b..89ffd8a7bf 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -188,8 +188,8 @@ static void qobject_output_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) { QObjectOutputVisitor *qov = to_qov(v); - qobject_ref(*obj); - qobject_output_add_obj(qov, name, *obj); + + qobject_output_add_obj(qov, name, qobject_ref(*obj)); } static void qobject_output_type_null(Visitor *v, const char *name, @@ -210,8 +210,7 @@ static void qobject_output_complete(Visitor *v, void *opaque) assert(qov->root && QSLIST_EMPTY(&qov->stack)); assert(opaque == qov->result); - qobject_ref(qov->root); - *qov->result = qov->root; + *qov->result = qobject_ref(qov->root); qov->result = NULL; } diff --git a/qobject/qdict.c b/qobject/qdict.c index 2e9bd53e22..22800eeceb 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -373,8 +373,7 @@ QDict *qdict_clone_shallow(const QDict *src) for (i = 0; i < QDICT_BUCKET_MAX; i++) { QLIST_FOREACH(entry, &src->table[i], next) { - qobject_ref(entry->value); - qdict_put_obj(dest, entry->key, entry->value); + qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); } } @@ -480,8 +479,7 @@ void qdict_copy_default(QDict *dst, QDict *src, const char *key) val = qdict_get(src, key); if (val) { - qobject_ref(val); - qdict_put_obj(dst, key, val); + qdict_put_obj(dst, key, qobject_ref(val)); } } @@ -526,8 +524,7 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) qdict_flatten_qlist(qobject_to(QList, value), target, new_key); } else { /* All other types are moved to the target unchanged. */ - qobject_ref(value); - qdict_put_obj(target, new_key, value); + qdict_put_obj(target, new_key, qobject_ref(value)); } g_free(new_key); @@ -566,8 +563,7 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) delete = true; } else if (prefix) { /* All other objects are moved to the target unchanged. */ - qobject_ref(value); - qdict_put_obj(target, new_key, value); + qdict_put_obj(target, new_key, qobject_ref(value)); delete = true; } @@ -610,8 +606,7 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) while (entry != NULL) { next = qdict_next(src, entry); if (strstart(entry->key, start, &p)) { - qobject_ref(entry->value); - qdict_put_obj(*dst, p, entry->value); + qdict_put_obj(*dst, p, qobject_ref(entry->value)); qdict_del(src, entry->key); } entry = next; @@ -894,16 +889,14 @@ QObject *qdict_crumple(const QDict *src, Error **errp) qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); } - qobject_ref(ent->value); - qdict_put_obj(child_dict, suffix, ent->value); + qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); } else { if (child) { error_setg(errp, "Key %s prefix is already set as a dict", prefix); goto error; } - qobject_ref(ent->value); - qdict_put_obj(two_level, prefix, ent->value); + qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); } g_free(prefix); @@ -924,8 +917,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) qdict_put_obj(multi_level, ent->key, child); } else { - qobject_ref(ent->value); - qdict_put_obj(multi_level, ent->key, ent->value); + qdict_put_obj(multi_level, ent->key, qobject_ref(ent->value)); } } qobject_unref(two_level); @@ -951,8 +943,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) goto error; } - qobject_ref(child); - qlist_append_obj(qobject_to(QList, dst), child); + qlist_append_obj(qobject_to(QList, dst), qobject_ref(child)); } qobject_unref(multi_level); multi_level = NULL; @@ -1055,8 +1046,7 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite) next = qdict_next(src, entry); if (overwrite || !qdict_haskey(dest, entry->key)) { - qobject_ref(entry->value); - qdict_put_obj(dest, entry->key, entry->value); + qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); qdict_del(src, entry->key); } @@ -1088,8 +1078,7 @@ bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) } qobj = qdict_get(qdict, renames->from); - qobject_ref(qobj); - qdict_put_obj(qdict, renames->to, qobj); + qdict_put_obj(qdict, renames->to, qobject_ref(qobj)); qdict_del(qdict, renames->from); } From 96054f56396eaa0b9b5c681fc3e42a0004b17ade Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 27 Apr 2018 21:28:48 +0200 Subject: [PATCH 0232/2380] qapi: fill in CpuInfoFast.arch in query-cpus-fast * Commit ca230ff33f89 added the @arch field to @CpuInfoFast, but it failed to set the new field in qmp_query_cpus_fast(), when TARGET_S390X was not defined. The updated @query-cpus-fast example in "qapi-schema.json" showed "arch":"x86" only because qmp_query_cpus_fast() calls g_malloc0() to allocate @CpuInfoFast, and the CPU_INFO_ARCH_X86 enum constant is generated with value 0. All @arch values other than @s390 implied the @CpuInfoOther sub-struct for @CpuInfoFast -- at the time of writing the patch --, thus no fields other than @arch needed to be set when TARGET_S390X was not defined. Set @arch now, by copying the corresponding assignments from qmp_query_cpus(). * Commit 25fa194b7b11 added the @riscv enum constant to @CpuInfoArch (used in both @CpuInfo and @CpuInfoFast -- the return types of the @query-cpus and @query-cpus-fast commands, respectively), and assigned, in both return structures, the @CpuInfoRISCV sub-structure to the new enum value. However, qmp_query_cpus_fast() would not populate either the @arch field or the @CpuInfoRISCV sub-structure, when TARGET_RISCV was defined; only qmp_query_cpus() would. Assign @CpuInfoOther to the @riscv enum constant in @CpuInfoFast, and populate only the @arch field in qmp_query_cpus_fast(). Getting CPU state without interrupting KVM is an exceptional thing that only S390X does currently. Quoting Cornelia Huck , "s390x is exceptional in that it has state in QEMU that is actually interesting for upper layers and can be retrieved without performance penalty". See also . Cc: Cornelia Huck Cc: Eric Blake Cc: Markus Armbruster Cc: Viktor VM Mihajlovski Cc: qemu-stable@nongnu.org Fixes: ca230ff33f89bf7102cbfbc2328716da6750aaed Fixes: 25fa194b7b11901561532e435beb83d046899f7a Signed-off-by: Laszlo Ersek Reviewed-by: Eric Blake Reviewed-by: Cornelia Huck Reviewed-by: Markus Armbruster Message-Id: <20180427192852.15013-2-lersek@redhat.com> Signed-off-by: Markus Armbruster --- cpus.c | 16 +++++++++++++++- qapi/misc.json | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/cpus.c b/cpus.c index 38eba8bff3..60563a6d54 100644 --- a/cpus.c +++ b/cpus.c @@ -2218,11 +2218,25 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) info->value->props = props; } -#if defined(TARGET_S390X) +#if defined(TARGET_I386) + info->value->arch = CPU_INFO_ARCH_X86; +#elif defined(TARGET_PPC) + info->value->arch = CPU_INFO_ARCH_PPC; +#elif defined(TARGET_SPARC) + info->value->arch = CPU_INFO_ARCH_SPARC; +#elif defined(TARGET_MIPS) + info->value->arch = CPU_INFO_ARCH_MIPS; +#elif defined(TARGET_TRICORE) + info->value->arch = CPU_INFO_ARCH_TRICORE; +#elif defined(TARGET_S390X) s390_cpu = S390_CPU(cpu); env = &s390_cpu->env; info->value->arch = CPU_INFO_ARCH_S390; info->value->u.s390.cpu_state = env->cpu_state; +#elif defined(TARGET_RISCV) + info->value->arch = CPU_INFO_ARCH_RISCV; +#else + info->value->arch = CPU_INFO_ARCH_OTHER; #endif if (!cur_item) { head = cur_item = info; diff --git a/qapi/misc.json b/qapi/misc.json index 5636f4a149..104d013adb 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -573,7 +573,7 @@ 'mips': 'CpuInfoOther', 'tricore': 'CpuInfoOther', 's390': 'CpuInfoS390', - 'riscv': 'CpuInfoRISCV', + 'riscv': 'CpuInfoOther', 'other': 'CpuInfoOther' } } ## From 9a801c7d6c8fd927df915f2ac3eacd306e8fc334 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 27 Apr 2018 21:28:49 +0200 Subject: [PATCH 0233/2380] qapi: add SysEmuTarget to "common.json" We'll soon need an enumeration type that lists all the softmmu targets that QEMU (the project) supports. Introduce @SysEmuTarget to "common.json". The enum constant @x86_64 doesn't match the QAPI convention of preferring hyphen ("-") over underscore ("_"). This is intentional; the @SysEmuTarget constants are supposed to produce QEMU executable names when stringified and appended to the "qemu-system-" prefix. Put differently, the replacement text of the TARGET_NAME preprocessor macro must be possible to look up in the list of (stringified) enum constants. Like other enum types, @SysEmuTarget too can be used for discriminator fields in unions. For the @i386 constant, a C-language union member called "i386" would be generated. On mingw build hosts, "i386" is a macro however. Add "i386" to "polluted_words" at once. Cc: "Daniel P. Berrange" Cc: Eric Blake Cc: Markus Armbruster Signed-off-by: Laszlo Ersek Message-Id: <20180427192852.15013-3-lersek@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/common.json | 23 +++++++++++++++++++++++ scripts/qapi/common.py | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/qapi/common.json b/qapi/common.json index d9b14dd429..c811d04984 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -126,3 +126,26 @@ ## { 'enum': 'OffAutoPCIBAR', 'data': [ 'off', 'auto', 'bar0', 'bar1', 'bar2', 'bar3', 'bar4', 'bar5' ] } + +## +# @SysEmuTarget: +# +# The comprehensive enumeration of QEMU system emulation ("softmmu") +# targets. Run "./configure --help" in the project root directory, and +# look for the *-softmmu targets near the "--target-list" option. The +# individual target constants are not documented here, for the time +# being. +# +# Notes: The resulting QMP strings can be appended to the "qemu-system-" +# prefix to produce the corresponding QEMU executable name. This +# is true even for "qemu-system-x86_64". +# +# Since: 2.13 +## +{ 'enum' : 'SysEmuTarget', + 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', + 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', + 'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc', + 'ppc64', 'ppcemb', 'riscv32', 'riscv64', 's390x', 'sh4', + 'sh4eb', 'sparc', 'sparc64', 'tricore', 'unicore32', + 'x86_64', 'xtensa', 'xtensaeb' ] } diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 3e14bc41f2..a032cec375 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1822,7 +1822,7 @@ def c_name(name, protect=True): 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) # namespace pollution: - polluted_words = set(['unix', 'errno', 'mips', 'sparc']) + polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) name = name.translate(c_name_trans) if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words): From b47aa7b3d44dcedf7bf541563f70704f82f500d0 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 27 Apr 2018 21:28:50 +0200 Subject: [PATCH 0234/2380] qapi: change the type of TargetInfo.arch from string to enum SysEmuTarget Now that we have @SysEmuTarget, it makes sense to restrict @TargetInfo.@arch to valid sysemu targets at the schema level. Cc: "Daniel P. Berrange" Cc: Eric Blake Cc: Markus Armbruster Signed-off-by: Laszlo Ersek Reviewed-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180427192852.15013-4-lersek@redhat.com> Signed-off-by: Markus Armbruster --- arch_init.c | 4 +++- qapi/misc.json | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/arch_init.c b/arch_init.c index 6ee07478bd..9597218ced 100644 --- a/arch_init.c +++ b/arch_init.c @@ -29,6 +29,7 @@ #include "hw/pci/pci.h" #include "hw/audio/soundhw.h" #include "qapi/qapi-commands-misc.h" +#include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "hw/acpi/acpi.h" @@ -112,7 +113,8 @@ TargetInfo *qmp_query_target(Error **errp) { TargetInfo *info = g_malloc0(sizeof(*info)); - info->arch = g_strdup(TARGET_NAME); + info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1, + &error_abort); return info; } diff --git a/qapi/misc.json b/qapi/misc.json index 104d013adb..460866cf54 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -5,6 +5,8 @@ # = Miscellanea ## +{ 'include': 'common.json' } + ## # @qmp_capabilities: # @@ -2449,12 +2451,12 @@ # # Information describing the QEMU target. # -# @arch: the target architecture (eg "x86_64", "i386", etc) +# @arch: the target architecture # # Since: 1.2.0 ## { 'struct': 'TargetInfo', - 'data': { 'arch': 'str' } } + 'data': { 'arch': 'SysEmuTarget' } } ## # @query-target: From daa9d2bc6d73618bc230787ddfa821a6a6560fc2 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 27 Apr 2018 21:28:51 +0200 Subject: [PATCH 0235/2380] qapi: discriminate CpuInfoFast on SysEmuTarget, not CpuInfoArch Add a new field @target (of type @SysEmuTarget) to the output of the @query-cpus-fast command, which provides more information about the emulation target than the field @arch (of type @CpuInfoArch). Make @target the new discriminator for the @CpuInfoFast return structure. Keep @arch for compatibility. Cc: "Daniel P. Berrange" Cc: Eric Blake Cc: Markus Armbruster Signed-off-by: Laszlo Ersek Reviewed-by: Eric Blake Message-Id: <20180427192852.15013-5-lersek@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- cpus.c | 87 ++++++++++++++++++++++++++++++++++++-------------- qapi/misc.json | 62 ++++++++++++++++++++++++++--------- 2 files changed, 110 insertions(+), 39 deletions(-) diff --git a/cpus.c b/cpus.c index 60563a6d54..5bcd3ecf38 100644 --- a/cpus.c +++ b/cpus.c @@ -2187,6 +2187,59 @@ CpuInfoList *qmp_query_cpus(Error **errp) return head; } +static CpuInfoArch sysemu_target_to_cpuinfo_arch(SysEmuTarget target) +{ + /* + * The @SysEmuTarget -> @CpuInfoArch mapping below is based on the + * TARGET_ARCH -> TARGET_BASE_ARCH mapping in the "configure" script. + */ + switch (target) { + case SYS_EMU_TARGET_I386: + case SYS_EMU_TARGET_X86_64: + return CPU_INFO_ARCH_X86; + + case SYS_EMU_TARGET_PPC: + case SYS_EMU_TARGET_PPCEMB: + case SYS_EMU_TARGET_PPC64: + return CPU_INFO_ARCH_PPC; + + case SYS_EMU_TARGET_SPARC: + case SYS_EMU_TARGET_SPARC64: + return CPU_INFO_ARCH_SPARC; + + case SYS_EMU_TARGET_MIPS: + case SYS_EMU_TARGET_MIPSEL: + case SYS_EMU_TARGET_MIPS64: + case SYS_EMU_TARGET_MIPS64EL: + return CPU_INFO_ARCH_MIPS; + + case SYS_EMU_TARGET_TRICORE: + return CPU_INFO_ARCH_TRICORE; + + case SYS_EMU_TARGET_S390X: + return CPU_INFO_ARCH_S390; + + case SYS_EMU_TARGET_RISCV32: + case SYS_EMU_TARGET_RISCV64: + return CPU_INFO_ARCH_RISCV; + + default: + return CPU_INFO_ARCH_OTHER; + } +} + +static void cpustate_to_cpuinfo_s390(CpuInfoS390 *info, const CPUState *cpu) +{ +#ifdef TARGET_S390X + S390CPU *s390_cpu = S390_CPU(cpu); + CPUS390XState *env = &s390_cpu->env; + + info->cpu_state = env->cpu_state; +#else + abort(); +#endif +} + /* * fast means: we NEVER interrupt vCPU threads to retrieve * information from KVM. @@ -2196,11 +2249,9 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) MachineState *ms = MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(ms); CpuInfoFastList *head = NULL, *cur_item = NULL; + SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, + -1, &error_abort); CPUState *cpu; -#if defined(TARGET_S390X) - S390CPU *s390_cpu; - CPUS390XState *env; -#endif CPU_FOREACH(cpu) { CpuInfoFastList *info = g_malloc0(sizeof(*info)); @@ -2218,26 +2269,14 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) info->value->props = props; } -#if defined(TARGET_I386) - info->value->arch = CPU_INFO_ARCH_X86; -#elif defined(TARGET_PPC) - info->value->arch = CPU_INFO_ARCH_PPC; -#elif defined(TARGET_SPARC) - info->value->arch = CPU_INFO_ARCH_SPARC; -#elif defined(TARGET_MIPS) - info->value->arch = CPU_INFO_ARCH_MIPS; -#elif defined(TARGET_TRICORE) - info->value->arch = CPU_INFO_ARCH_TRICORE; -#elif defined(TARGET_S390X) - s390_cpu = S390_CPU(cpu); - env = &s390_cpu->env; - info->value->arch = CPU_INFO_ARCH_S390; - info->value->u.s390.cpu_state = env->cpu_state; -#elif defined(TARGET_RISCV) - info->value->arch = CPU_INFO_ARCH_RISCV; -#else - info->value->arch = CPU_INFO_ARCH_OTHER; -#endif + info->value->arch = sysemu_target_to_cpuinfo_arch(target); + info->value->target = target; + if (target == SYS_EMU_TARGET_S390X) { + cpustate_to_cpuinfo_s390(&info->value->u.s390x, cpu); + } else { + /* do nothing for @CpuInfoOther */ + } + if (!cur_item) { head = cur_item = info; } else { diff --git a/qapi/misc.json b/qapi/misc.json index 460866cf54..fa4eec7b6b 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -558,25 +558,55 @@ # @props: properties describing to which node/socket/core/thread # virtual CPU belongs to, provided if supported by board # -# @arch: architecture of the cpu, which determines which additional fields -# will be listed +# @arch: base architecture of the cpu +# +# @target: the QEMU system emulation target, which is more specific than +# @arch and determines which additional fields will be listed +# (since 2.13) # # Since: 2.12 # ## -{ 'union': 'CpuInfoFast', - 'base': {'cpu-index': 'int', 'qom-path': 'str', - 'thread-id': 'int', '*props': 'CpuInstanceProperties', - 'arch': 'CpuInfoArch' }, - 'discriminator': 'arch', - 'data': { 'x86': 'CpuInfoOther', - 'sparc': 'CpuInfoOther', - 'ppc': 'CpuInfoOther', - 'mips': 'CpuInfoOther', - 'tricore': 'CpuInfoOther', - 's390': 'CpuInfoS390', - 'riscv': 'CpuInfoOther', - 'other': 'CpuInfoOther' } } +{ 'union' : 'CpuInfoFast', + 'base' : { 'cpu-index' : 'int', + 'qom-path' : 'str', + 'thread-id' : 'int', + '*props' : 'CpuInstanceProperties', + 'arch' : 'CpuInfoArch', + 'target' : 'SysEmuTarget' }, + 'discriminator' : 'target', + 'data' : { 'aarch64' : 'CpuInfoOther', + 'alpha' : 'CpuInfoOther', + 'arm' : 'CpuInfoOther', + 'cris' : 'CpuInfoOther', + 'hppa' : 'CpuInfoOther', + 'i386' : 'CpuInfoOther', + 'lm32' : 'CpuInfoOther', + 'm68k' : 'CpuInfoOther', + 'microblaze' : 'CpuInfoOther', + 'microblazeel' : 'CpuInfoOther', + 'mips' : 'CpuInfoOther', + 'mips64' : 'CpuInfoOther', + 'mips64el' : 'CpuInfoOther', + 'mipsel' : 'CpuInfoOther', + 'moxie' : 'CpuInfoOther', + 'nios2' : 'CpuInfoOther', + 'or1k' : 'CpuInfoOther', + 'ppc' : 'CpuInfoOther', + 'ppc64' : 'CpuInfoOther', + 'ppcemb' : 'CpuInfoOther', + 'riscv32' : 'CpuInfoOther', + 'riscv64' : 'CpuInfoOther', + 's390x' : 'CpuInfoS390', + 'sh4' : 'CpuInfoOther', + 'sh4eb' : 'CpuInfoOther', + 'sparc' : 'CpuInfoOther', + 'sparc64' : 'CpuInfoOther', + 'tricore' : 'CpuInfoOther', + 'unicore32' : 'CpuInfoOther', + 'x86_64' : 'CpuInfoOther', + 'xtensa' : 'CpuInfoOther', + 'xtensaeb' : 'CpuInfoOther' } } ## # @query-cpus-fast: @@ -602,6 +632,7 @@ # }, # "qom-path": "/machine/unattached/device[0]", # "arch":"x86", +# "target":"x86_64", # "cpu-index": 0 # }, # { @@ -613,6 +644,7 @@ # }, # "qom-path": "/machine/unattached/device[2]", # "arch":"x86", +# "target":"x86_64", # "cpu-index": 1 # } # ] From 6ffa3ab453b431ec047ff1fc87120059b5266014 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 27 Apr 2018 21:28:52 +0200 Subject: [PATCH 0236/2380] qapi: deprecate CpuInfoFast.arch The TARGET_BASE_ARCH values from "configure" don't all map to the @CpuInfoArch enum constants; in particular "s390x" from the former does not match @s390 in the latter. Clients are known to rely on the @s390 constant specifically, so we can't change it silently. Instead, deprecate the @CpuInfoFast.@arch member (in favor of @CpuInfoFast.@target) using the regular deprecation process. (No deprecation reminder is added to sysemu_target_to_cpuinfo_arch(): once @CpuInfoFast.@arch is removed, the assignment expression that calls sysemu_target_to_cpuinfo_arch() from qmp_query_cpus_fast() will have to disappear; in turn the static function left without callers will also break the build, thus it'll have to go.) Cc: "Daniel P. Berrange" Cc: Eric Blake Cc: Markus Armbruster Signed-off-by: Laszlo Ersek Message-Id: <20180427192852.15013-6-lersek@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/misc.json | 8 ++++---- qemu-doc.texi | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/qapi/misc.json b/qapi/misc.json index fa4eec7b6b..f5988cc0b5 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -558,11 +558,11 @@ # @props: properties describing to which node/socket/core/thread # virtual CPU belongs to, provided if supported by board # -# @arch: base architecture of the cpu +# @arch: base architecture of the cpu; deprecated since 2.13.0 in favor +# of @target # -# @target: the QEMU system emulation target, which is more specific than -# @arch and determines which additional fields will be listed -# (since 2.13) +# @target: the QEMU system emulation target, which determines which +# additional fields will be listed (since 2.13) # # Since: 2.12 # diff --git a/qemu-doc.texi b/qemu-doc.texi index 5813d27615..0ed0f19e6b 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2949,6 +2949,11 @@ from qcow2 images. The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. +@subsection query-cpus-fast "arch" output member (since 2.13.0) + +The ``arch'' output member of the ``query-cpus-fast'' command is +replaced by the ``target'' output member. + @section System emulator devices @subsection ivshmem (since 2.6.0) From 89aa0d87634e2cb98517509dc8bdb876f26ecf8b Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 27 Apr 2018 17:20:01 +0300 Subject: [PATCH 0237/2380] nbd/client: fix nbd_negotiate_simple_meta_context Initialize received variable. Otherwise, is is possible for server to answer without any contexts, but we will set context_id to something random (received_id is not initialized too) and return 1, which is wrong. To solve it, just initialize received to false. Initialize received_id too, just to make all possible checkers happy. Bug was introduced in 78a33ab58782efdb206de14 "nbd: BLOCK_STATUS for standard get_block_status function: client part" with the whole function. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180427142002.21930-2-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake --- nbd/client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nbd/client.c b/nbd/client.c index b9e175d1c2..7f35b5c323 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -613,8 +613,8 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, { int ret; NBDOptionReply reply; - uint32_t received_id; - bool received; + uint32_t received_id = 0; + bool received = false; uint32_t export_len = strlen(export); uint32_t context_len = strlen(context); uint32_t data_len = sizeof(export_len) + export_len + From 16a2227893dc1d5cad78ed376ad1d7e300978fbe Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 27 Apr 2018 17:20:02 +0300 Subject: [PATCH 0238/2380] migration/block-dirty-bitmap: fix memory leak in dirty_bitmap_load_bits Release buf on error path too. Bug was introduced in b35ebdf076d697bc "migration: add postcopy migration of dirty bitmaps" with the whole function. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180427142002.21930-3-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake --- migration/block-dirty-bitmap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index dd04f102d8..8819aabe3a 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -600,6 +600,7 @@ static int dirty_bitmap_load_bits(QEMUFile *f, DirtyBitmapLoadState *s) ret = qemu_get_buffer(f, buf, buf_size); if (ret != buf_size) { error_report("Failed to read bitmap bits"); + g_free(buf); return -EIO; } From e475d108f1b3d3163f0affea67cdedbe5fc9752b Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 1 May 2018 10:46:53 -0500 Subject: [PATCH 0239/2380] nbd/client: Fix error messages during NBD_INFO_BLOCK_SIZE A missing space makes for poor error messages, and sizes can't go negative. Also, we missed diagnosing a server that sends a maximum block size less than the minimum. Fixes: 081dd1fe CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake Message-Id: <20180501154654.943782-1-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- nbd/client.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/nbd/client.c b/nbd/client.c index 7f35b5c323..232ff4f46d 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -435,8 +435,8 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, } be32_to_cpus(&info->min_block); if (!is_power_of_2(info->min_block)) { - error_setg(errp, "server minimum block size %" PRId32 - "is not a power of two", info->min_block); + error_setg(errp, "server minimum block size %" PRIu32 + " is not a power of two", info->min_block); nbd_send_opt_abort(ioc); return -1; } @@ -450,8 +450,8 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, be32_to_cpus(&info->opt_block); if (!is_power_of_2(info->opt_block) || info->opt_block < info->min_block) { - error_setg(errp, "server preferred block size %" PRId32 - "is not valid", info->opt_block); + error_setg(errp, "server preferred block size %" PRIu32 + " is not valid", info->opt_block); nbd_send_opt_abort(ioc); return -1; } @@ -462,6 +462,12 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, return -1; } be32_to_cpus(&info->max_block); + if (info->max_block < info->min_block) { + error_setg(errp, "server maximum block size %" PRIu32 + " is not valid", info->max_block); + nbd_send_opt_abort(ioc); + return -1; + } trace_nbd_opt_go_info_block_size(info->min_block, info->opt_block, info->max_block); break; From acfd8f7a5f92e703d2d046cbe3d510008a697194 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 3 May 2018 17:26:26 -0500 Subject: [PATCH 0240/2380] nbd/client: Relax handling of large NBD_CMD_BLOCK_STATUS reply The NBD spec is proposing a relaxation of NBD_CMD_BLOCK_STATUS where a server may have the final extent per context give a length beyond the original request, if it can easily prove that subsequent bytes have the same status, on the grounds that a client can take advantage of this information for fewer block status requests. Since qemu 2.12 as a client always sends NBD_CMD_FLAG_REQ_ONE, and rejects a server that sends extra length, the upstream NBD spec will probably limit this behavior to clients that don't request REQ_ONE semantics; but it doesn't hurt to relax qemu to always be permissive of this server behavior, even if it continues to use REQ_ONE. CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake Message-Id: <20180503222626.1303410-1-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- block/nbd-client.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/block/nbd-client.c b/block/nbd-client.c index e7caf49fbb..8d69eaaa32 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -259,14 +259,18 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, if (extent->length == 0 || (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, - client->info.min_block)) || - extent->length > orig_length) - { + client->info.min_block))) { error_setg(errp, "Protocol error: server sent status chunk with " "invalid length"); return -EINVAL; } + /* The server is allowed to send us extra information on the final + * extent; just clamp it to the length we requested. */ + if (extent->length > orig_length) { + extent->length = orig_length; + } + return 0; } From 6d9c1b8dbb3527258bbc473aa3a9b02c6a86c506 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 4 May 2018 18:05:50 +0100 Subject: [PATCH 0241/2380] hw/arm/virt: Add linux,pci-domain property This allows to pin the host controller in the Linux PCI domain space. Linux requires that property to be available consistently or not at all, in which case the domain number becomes unstable on additions/removals. Adding it here won't make a difference in practice for most setups as we only expose one controller. However, enabling Jailhouse on top may introduce another controller, and that one would like to have stable address as well. So the property is needed for the first controller as well. Signed-off-by: Jan Kiszka Message-id: 3301c5bc-7b47-1b0e-8ce4-30435057a276@web.de Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/virt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index a18291c5d5..dc0c0335a2 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1023,6 +1023,7 @@ static void create_pcie(const VirtMachineState *vms, qemu_irq *pic) qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "pci"); qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 3); qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 2); + qemu_fdt_setprop_cell(vms->fdt, nodename, "linux,pci-domain", 0); qemu_fdt_setprop_cells(vms->fdt, nodename, "bus-range", 0, nr_pcie_buses - 1); qemu_fdt_setprop(vms->fdt, nodename, "dma-coherent", NULL, 0); From 100061121c1f69a672ce7bb3e9e3781f8018f9f6 Mon Sep 17 00:00:00 2001 From: Mathew Maidment Date: Fri, 4 May 2018 18:05:50 +0100 Subject: [PATCH 0242/2380] target/arm: Correct MPUIR privilege level in register_cp_regs_for_features() conditional case The duplication of id_tlbtr_reginfo was unintentionally added within 3281af8114c6b8ead02f08b58e3c36895c1ea047 which should have been id_mpuir_reginfo. The effect was that for OMAP and StrongARM CPUs we would incorrectly UNDEF writes to MPUIR rather than NOPing them. Signed-off-by: Mathew Maidment Message-id: 20180501184933.37609-2-mathew1800@gmail.com [PMM: tweak commit message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 52a88e0297..0fef5d4d06 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5347,7 +5347,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; + id_mpuir_reginfo.access = PL1_RW; id_tlbtr_reginfo.access = PL1_RW; } if (arm_feature(env, ARM_FEATURE_V8)) { From 0c6a108ec1efd8498b281086d44604204196a9d5 Mon Sep 17 00:00:00 2001 From: Patrick Oppenlander Date: Fri, 4 May 2018 18:05:50 +0100 Subject: [PATCH 0243/2380] hw/char/cmsdk-apb-uart.c: Accept more input after character read The character frontend needs to be notified that the uart receive buffer is empty and ready to handle another character. Previously, the uart only worked correctly when receiving one character at a time. Signed-off-by: Patrick Oppenlander Message-id: CAEg67GkRTw=cXei3o9hvpxG_L4zSrNzR0bFyAgny+sSEUb_kPw@mail.gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/char/cmsdk-apb-uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/char/cmsdk-apb-uart.c b/hw/char/cmsdk-apb-uart.c index 9c0929d8a2..ddfbb25c24 100644 --- a/hw/char/cmsdk-apb-uart.c +++ b/hw/char/cmsdk-apb-uart.c @@ -157,6 +157,7 @@ static uint64_t uart_read(void *opaque, hwaddr offset, unsigned size) r = s->rxbuf; s->state &= ~R_STATE_RXFULL_MASK; cmsdk_apb_uart_update(s); + qemu_chr_fe_accept_input(&s->chr); break; case A_STATE: r = s->state; From a22cadbefd2b2ff57f5c06688f7ed06c52d6929a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 May 2018 18:05:50 +0100 Subject: [PATCH 0244/2380] hw/usb/tusb6010: Convert away from old_mmio Convert the tusb6010 device away from using the old_mmio field of MemoryRegionOps. This device is used only in the n800 and n810 boards. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180427173611.10281-2-peter.maydell@linaro.org --- hw/usb/tusb6010.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/hw/usb/tusb6010.c b/hw/usb/tusb6010.c index 2662c060ed..a2128024c1 100644 --- a/hw/usb/tusb6010.c +++ b/hw/usb/tusb6010.c @@ -641,11 +641,43 @@ static void tusb_async_writew(void *opaque, hwaddr addr, } } +static uint64_t tusb_async_readfn(void *opaque, hwaddr addr, unsigned size) +{ + switch (size) { + case 1: + return tusb_async_readb(opaque, addr); + case 2: + return tusb_async_readh(opaque, addr); + case 4: + return tusb_async_readw(opaque, addr); + default: + g_assert_not_reached(); + } +} + +static void tusb_async_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + switch (size) { + case 1: + tusb_async_writeb(opaque, addr, value); + break; + case 2: + tusb_async_writeh(opaque, addr, value); + break; + case 4: + tusb_async_writew(opaque, addr, value); + break; + default: + g_assert_not_reached(); + } +} + static const MemoryRegionOps tusb_async_ops = { - .old_mmio = { - .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, }, - .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, }, - }, + .read = tusb_async_readfn, + .write = tusb_async_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; From 50a22d0de84955692a5f31134d88c1e8fea80247 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 May 2018 18:05:50 +0100 Subject: [PATCH 0245/2380] hw/net/smc91c111: Convert away from old_mmio Convert the smc91c111 device away from using the old_mmio field of MemoryRegionOps. This device is used by several Arm board models. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180427173611.10281-3-peter.maydell@linaro.org --- hw/net/smc91c111.c | 54 +++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 3b16dcf5a1..c8cc5379b7 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -625,37 +625,33 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset) return 0; } -static void smc91c111_writew(void *opaque, hwaddr offset, - uint32_t value) +static uint64_t smc91c111_readfn(void *opaque, hwaddr addr, unsigned size) { - smc91c111_writeb(opaque, offset, value & 0xff); - smc91c111_writeb(opaque, offset + 1, value >> 8); + int i; + uint32_t val = 0; + + for (i = 0; i < size; i++) { + val |= smc91c111_readb(opaque, addr + i) << (i * 8); + } + return val; } -static void smc91c111_writel(void *opaque, hwaddr offset, - uint32_t value) +static void smc91c111_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { + int i = 0; + /* 32-bit writes to offset 0xc only actually write to the bank select - register (offset 0xe) */ - if (offset != 0xc) - smc91c111_writew(opaque, offset, value & 0xffff); - smc91c111_writew(opaque, offset + 2, value >> 16); -} + * register (offset 0xe), so skip the first two bytes we would write. + */ + if (addr == 0xc && size == 4) { + i += 2; + } -static uint32_t smc91c111_readw(void *opaque, hwaddr offset) -{ - uint32_t val; - val = smc91c111_readb(opaque, offset); - val |= smc91c111_readb(opaque, offset + 1) << 8; - return val; -} - -static uint32_t smc91c111_readl(void *opaque, hwaddr offset) -{ - uint32_t val; - val = smc91c111_readw(opaque, offset); - val |= smc91c111_readw(opaque, offset + 2) << 16; - return val; + for (; i < size; i++) { + smc91c111_writeb(opaque, addr + i, + extract32(value, i * 8, 8)); + } } static int smc91c111_can_receive_nc(NetClientState *nc) @@ -747,10 +743,10 @@ static const MemoryRegionOps smc91c111_mem_ops = { /* The special case for 32 bit writes to 0xc means we can't just * set .impl.min/max_access_size to 1, unfortunately */ - .old_mmio = { - .read = { smc91c111_readb, smc91c111_readw, smc91c111_readl, }, - .write = { smc91c111_writeb, smc91c111_writew, smc91c111_writel, }, - }, + .read = smc91c111_readfn, + .write = smc91c111_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; From 0c9492765a56c1547dc7edf56971c97685354fe4 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0246/2380] arm: boot: set boot_info starting from first_cpu Even though nothing is currently broken (since all boards use first_cpu as boot cpu), make sure that boot_info is set on all CPUs. If some board would like support heterogenuos setup (i.e. init boot_info on subset of CPUs) in future, it should add a reasonable API to do it, instead of starting assigning boot_info from some CPU and till the end of present CPUs list. Ref: "Message-ID: " Signed-off-by: Igor Mammedov Reviewed-by: Peter Maydell Message-id: 1525176522-200354-5-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell --- hw/arm/boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 9ae6ab2689..1e2be20731 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -1170,7 +1170,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) } info->is_linux = is_linux; - for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) { + for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { ARM_CPU(cs)->env.boot_info = info; } } From 8dae46970532afcf93470b00e83ca9921980efc3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0247/2380] target/arm: Tidy conditions in handle_vec_simd_shri MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The (size > 3 && !is_q) condition is identical to the preceeding test of bit 3 in immh; eliminate it. For the benefit of Coverity, assert that size is within the bounds we expect. Fixes: Coverity CID1385846 Fixes: Coverity CID1385849 Fixes: Coverity CID1385852 Fixes: Coverity CID1385857 Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Message-id: 20180501180455.11214-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index bff4e13bf6..97950dce1a 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -9019,11 +9019,7 @@ static void handle_vec_simd_shri(DisasContext *s, bool is_q, bool is_u, unallocated_encoding(s); return; } - - if (size > 3 && !is_q) { - unallocated_encoding(s); - return; - } + tcg_debug_assert(size <= 3); if (!fp_access_check(s)) { return; From a8766e3172c1671cab297c1ef4566a3c5d094822 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0248/2380] target/arm: Tidy condition in disas_simd_two_reg_misc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Path analysis shows that size == 3 && !is_q has been eliminated. Fixes: Coverity CID1385853 Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Message-id: 20180501180455.11214-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 97950dce1a..6d49f30b4a 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -11473,7 +11473,11 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) /* All 64-bit element operations can be shared with scalar 2misc */ int pass; - for (pass = 0; pass < (is_q ? 2 : 1); pass++) { + /* Coverity claims (size == 3 && !is_q) has been eliminated + * from all paths leading to here. + */ + tcg_debug_assert(is_q); + for (pass = 0; pass < 2; pass++) { TCGv_i64 tcg_op = tcg_temp_new_i64(); TCGv_i64 tcg_res = tcg_temp_new_i64(); From a82929a251481af1467569810ec309b57558f7fe Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0249/2380] hw/arm: Don't fail qtest due to missing SD card in -nodefaults mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running omap1/2 or pxa2xx based ARM machines with -nodefaults, they bail out immediately complaining about a "missing SecureDigital device". That's not how the "default" devices in vl.c are meant to work - it should be possible for a board to also start up without default devices. So let's turn the error message and exit() into a warning instead. Signed-off-by: Thomas Huth Message-id: 1525326811-3233-1-git-send-email-thuth@redhat.com Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/omap1.c | 8 ++++---- hw/arm/omap2.c | 8 ++++---- hw/arm/pxa2xx.c | 15 +++++++-------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 24673abfca..e54c1f8f99 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -30,6 +30,7 @@ #include "hw/arm/soc_dma.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" +#include "sysemu/qtest.h" #include "qemu/range.h" #include "hw/sysbus.h" #include "qemu/cutils.h" @@ -3987,12 +3988,11 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, omap_findclk(s, "dpll3")); dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - error_report("missing SecureDigital device"); - exit(1); + if (!dinfo && !qtest_enabled()) { + warn_report("missing SecureDigital device"); } s->mmc = omap_mmc_init(0xfffb7800, system_memory, - blk_by_legacy_dinfo(dinfo), + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN), &s->drq[OMAP_DMA_MMC_TX], omap_findclk(s, "mmc_ck")); diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 80663533e1..b8d0910a1f 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -25,6 +25,7 @@ #include "cpu.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" +#include "sysemu/qtest.h" #include "hw/boards.h" #include "hw/hw.h" #include "hw/arm/arm.h" @@ -2486,12 +2487,11 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, s->drq[OMAP24XX_DMA_GPMC]); dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - error_report("missing SecureDigital device"); - exit(1); + if (!dinfo && !qtest_enabled()) { + warn_report("missing SecureDigital device"); } s->mmc = omap2_mmc_init(omap_l4tao(s->l4, 9), - blk_by_legacy_dinfo(dinfo), + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MMC_IRQ), &s->drq[OMAP24XX_DMA_MMC1_TX], omap_findclk(s, "mmc_fclk"), omap_findclk(s, "mmc_iclk")); diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 928a0431d6..a2803fdee4 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -21,6 +21,7 @@ #include "chardev/char-fe.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" +#include "sysemu/qtest.h" #include "qemu/cutils.h" static struct { @@ -2095,12 +2096,11 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 121); dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - error_report("missing SecureDigital device"); - exit(1); + if (!dinfo && !qtest_enabled()) { + warn_report("missing SecureDigital device"); } s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, - blk_by_legacy_dinfo(dinfo), + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); @@ -2220,12 +2220,11 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 85); dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - error_report("missing SecureDigital device"); - exit(1); + if (!dinfo && !qtest_enabled()) { + warn_report("missing SecureDigital device"); } s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, - blk_by_legacy_dinfo(dinfo), + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); From b1e5336a9899016c53d59eba53ebf6abcc21995c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0250/2380] target/arm: Implement v8M VLLDM and VLSTM For v8M the instructions VLLDM and VLSTM support lazy saving and restoring of the secure floating-point registers. Even if the floating point extension is not implemented, these instructions must act as NOPs in Secure state, so they can be used as part of the secure-to-nonsecure call sequence. Fixes: https://bugs.launchpad.net/qemu/+bug/1768295 Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180503105730.5958-1-peter.maydell@linaro.org --- target/arm/translate.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index 9bc2ce1a0b..ad208867a7 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -10795,8 +10795,23 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) /* Coprocessor. */ if (arm_dc_feature(s, ARM_FEATURE_M)) { /* We don't currently implement M profile FP support, - * so this entire space should give a NOCP fault. + * so this entire space should give a NOCP fault, with + * the exception of the v8M VLLDM and VLSTM insns, which + * must be NOPs in Secure state and UNDEF in Nonsecure state. */ + if (arm_dc_feature(s, ARM_FEATURE_V8) && + (insn & 0xffa00f00) == 0xec200a00) { + /* 0b1110_1100_0x1x_xxxx_xxxx_1010_xxxx_xxxx + * - VLLDM, VLSTM + * We choose to UNDEF if the RAZ bits are non-zero. + */ + if (!s->v8m_secure || (insn & 0x0040f0ff)) { + goto illegal_op; + } + /* Just NOP since FP support is not implemented */ + break; + } + /* All other insns: NOCP */ gen_exception_insn(s, 4, EXCP_NOCP, syn_uncategorized(), default_exception_el(s)); break; From 527773eeef9f2225370f9c17c35074b2ed0ced92 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0251/2380] hw/arm/smmu-common: smmu base device and datatypes The patch introduces the smmu base device and class for the ARM smmu. Devices for specific versions will be derived from this base device. We also introduce some important datatypes. Signed-off-by: Eric Auger Signed-off-by: Prem Mallappa Reviewed-by: Peter Maydell Message-id: 1524665762-31355-2-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- default-configs/aarch64-softmmu.mak | 1 + hw/arm/Makefile.objs | 1 + hw/arm/smmu-common.c | 81 ++++++++++++++++++ include/hw/arm/smmu-common.h | 123 ++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 hw/arm/smmu-common.c create mode 100644 include/hw/arm/smmu-common.h diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak index 9ddccf855e..6f790f061a 100644 --- a/default-configs/aarch64-softmmu.mak +++ b/default-configs/aarch64-softmmu.mak @@ -8,3 +8,4 @@ CONFIG_DDC=y CONFIG_DPCD=y CONFIG_XLNX_ZYNQMP=y CONFIG_XLNX_ZYNQMP_ARM=y +CONFIG_ARM_SMMUV3=y diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 2885e3e234..558436f3a5 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -35,3 +35,4 @@ obj-$(CONFIG_MPS2) += mps2-tz.o obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o obj-$(CONFIG_IOTKIT) += iotkit.o obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o +obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c new file mode 100644 index 0000000000..e086ff52a5 --- /dev/null +++ b/hw/arm/smmu-common.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * Author: Prem Mallappa + * + */ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "trace.h" +#include "exec/target_page.h" +#include "qom/cpu.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" + +#include "qemu/error-report.h" +#include "hw/arm/smmu-common.h" + +static void smmu_base_realize(DeviceState *dev, Error **errp) +{ + SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev); + Error *local_err = NULL; + + sbc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + +static void smmu_base_reset(DeviceState *dev) +{ + /* will be filled later on */ +} + +static Property smmu_dev_properties[] = { + DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0), + DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, "PCI", PCIBus *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void smmu_base_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SMMUBaseClass *sbc = ARM_SMMU_CLASS(klass); + + dc->props = smmu_dev_properties; + device_class_set_parent_realize(dc, smmu_base_realize, + &sbc->parent_realize); + dc->reset = smmu_base_reset; +} + +static const TypeInfo smmu_base_info = { + .name = TYPE_ARM_SMMU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SMMUState), + .class_data = NULL, + .class_size = sizeof(SMMUBaseClass), + .class_init = smmu_base_class_init, + .abstract = true, +}; + +static void smmu_base_register_types(void) +{ + type_register_static(&smmu_base_info); +} + +type_init(smmu_base_register_types) + diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h new file mode 100644 index 0000000000..d682be82d2 --- /dev/null +++ b/include/hw/arm/smmu-common.h @@ -0,0 +1,123 @@ +/* + * ARM SMMU Support + * + * Copyright (C) 2015-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef HW_ARM_SMMU_COMMON_H +#define HW_ARM_SMMU_COMMON_H + +#include "hw/sysbus.h" +#include "hw/pci/pci.h" + +#define SMMU_PCI_BUS_MAX 256 +#define SMMU_PCI_DEVFN_MAX 256 + +#define SMMU_MAX_VA_BITS 48 + +/* + * Page table walk error types + */ +typedef enum { + SMMU_PTW_ERR_NONE, + SMMU_PTW_ERR_WALK_EABT, /* Translation walk external abort */ + SMMU_PTW_ERR_TRANSLATION, /* Translation fault */ + SMMU_PTW_ERR_ADDR_SIZE, /* Address Size fault */ + SMMU_PTW_ERR_ACCESS, /* Access fault */ + SMMU_PTW_ERR_PERMISSION, /* Permission fault */ +} SMMUPTWEventType; + +typedef struct SMMUPTWEventInfo { + SMMUPTWEventType type; + dma_addr_t addr; /* fetched address that induced an abort, if any */ +} SMMUPTWEventInfo; + +typedef struct SMMUTransTableInfo { + bool disabled; /* is the translation table disabled? */ + uint64_t ttb; /* TT base address */ + uint8_t tsz; /* input range, ie. 2^(64 -tsz)*/ + uint8_t granule_sz; /* granule page shift */ +} SMMUTransTableInfo; + +/* + * Generic structure populated by derived SMMU devices + * after decoding the configuration information and used as + * input to the page table walk + */ +typedef struct SMMUTransCfg { + int stage; /* translation stage */ + bool aa64; /* arch64 or aarch32 translation table */ + bool disabled; /* smmu is disabled */ + bool bypassed; /* translation is bypassed */ + bool aborted; /* translation is aborted */ + uint64_t ttb; /* TT base address */ + uint8_t oas; /* output address width */ + uint8_t tbi; /* Top Byte Ignore */ + uint16_t asid; + SMMUTransTableInfo tt[2]; +} SMMUTransCfg; + +typedef struct SMMUDevice { + void *smmu; + PCIBus *bus; + int devfn; + IOMMUMemoryRegion iommu; + AddressSpace as; +} SMMUDevice; + +typedef struct SMMUNotifierNode { + SMMUDevice *sdev; + QLIST_ENTRY(SMMUNotifierNode) next; +} SMMUNotifierNode; + +typedef struct SMMUPciBus { + PCIBus *bus; + SMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically alloc */ +} SMMUPciBus; + +typedef struct SMMUState { + /* */ + SysBusDevice dev; + const char *mrtypename; + MemoryRegion iomem; + + GHashTable *smmu_pcibus_by_busptr; + GHashTable *configs; /* cache for configuration data */ + GHashTable *iotlb; + SMMUPciBus *smmu_pcibus_by_bus_num[SMMU_PCI_BUS_MAX]; + PCIBus *pci_bus; + QLIST_HEAD(, SMMUNotifierNode) notifiers_list; + uint8_t bus_num; + PCIBus *primary_bus; +} SMMUState; + +typedef struct { + /* */ + SysBusDeviceClass parent_class; + + /*< public >*/ + + DeviceRealize parent_realize; + +} SMMUBaseClass; + +#define TYPE_ARM_SMMU "arm-smmu" +#define ARM_SMMU(obj) OBJECT_CHECK(SMMUState, (obj), TYPE_ARM_SMMU) +#define ARM_SMMU_CLASS(klass) \ + OBJECT_CLASS_CHECK(SMMUBaseClass, (klass), TYPE_ARM_SMMU) +#define ARM_SMMU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SMMUBaseClass, (obj), TYPE_ARM_SMMU) + +#endif /* HW_ARM_SMMU_COMMON */ From cac994ef43b128c80c56b4cd4dd9d8af0f95da3f Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0252/2380] hw/arm/smmu-common: IOMMU memory region and address space setup We set up the infrastructure to enumerate all the PCI devices attached to the SMMU and create an associated IOMMU memory region and address space. Those info are stored in SMMUDevice objects. The devices are grouped according to the PCIBus they belong to. A hash table indexed by the PCIBus pointer is used. Also an array indexed by the bus number allows to find the list of SMMUDevices. Signed-off-by: Eric Auger Signed-off-by: Prem Mallappa Reviewed-by: Peter Maydell Message-id: 1524665762-31355-3-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 69 ++++++++++++++++++++++++++++++++++++ hw/arm/trace-events | 3 ++ include/hw/arm/smmu-common.h | 8 +++++ 3 files changed, 80 insertions(+) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index e086ff52a5..3d64bcfac2 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -28,8 +28,69 @@ #include "qemu/error-report.h" #include "hw/arm/smmu-common.h" +/** + * The bus number is used for lookup when SID based invalidation occurs. + * In that case we lazily populate the SMMUPciBus array from the bus hash + * table. At the time the SMMUPciBus is created (smmu_find_add_as), the bus + * numbers may not be always initialized yet. + */ +SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num) +{ + SMMUPciBus *smmu_pci_bus = s->smmu_pcibus_by_bus_num[bus_num]; + + if (!smmu_pci_bus) { + GHashTableIter iter; + + g_hash_table_iter_init(&iter, s->smmu_pcibus_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&smmu_pci_bus)) { + if (pci_bus_num(smmu_pci_bus->bus) == bus_num) { + s->smmu_pcibus_by_bus_num[bus_num] = smmu_pci_bus; + return smmu_pci_bus; + } + } + } + return smmu_pci_bus; +} + +static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) +{ + SMMUState *s = opaque; + SMMUPciBus *sbus = g_hash_table_lookup(s->smmu_pcibus_by_busptr, bus); + SMMUDevice *sdev; + + if (!sbus) { + sbus = g_malloc0(sizeof(SMMUPciBus) + + sizeof(SMMUDevice *) * SMMU_PCI_DEVFN_MAX); + sbus->bus = bus; + g_hash_table_insert(s->smmu_pcibus_by_busptr, bus, sbus); + } + + sdev = sbus->pbdev[devfn]; + if (!sdev) { + char *name = g_strdup_printf("%s-%d-%d", + s->mrtypename, + pci_bus_num(bus), devfn); + sdev = sbus->pbdev[devfn] = g_new0(SMMUDevice, 1); + + sdev->smmu = s; + sdev->bus = bus; + sdev->devfn = devfn; + + memory_region_init_iommu(&sdev->iommu, sizeof(sdev->iommu), + s->mrtypename, + OBJECT(s), name, 1ULL << SMMU_MAX_VA_BITS); + address_space_init(&sdev->as, + MEMORY_REGION(&sdev->iommu), name); + trace_smmu_add_mr(name); + g_free(name); + } + + return &sdev->as; +} + static void smmu_base_realize(DeviceState *dev, Error **errp) { + SMMUState *s = ARM_SMMU(dev); SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev); Error *local_err = NULL; @@ -38,6 +99,14 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } + + s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL); + + if (s->primary_bus) { + pci_setup_iommu(s->primary_bus, smmu_find_add_as, s); + } else { + error_setg(errp, "SMMU is not attached to any PCI bus!"); + } } static void smmu_base_reset(DeviceState *dev) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 193063ed99..8e8b53c95d 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -2,3 +2,6 @@ # hw/arm/virt-acpi-build.c virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." + +# hw/arm/smmu-common.c +smmu_add_mr(const char *name) "%s" \ No newline at end of file diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index d682be82d2..8b947774b0 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -120,4 +120,12 @@ typedef struct { #define ARM_SMMU_GET_CLASS(obj) \ OBJECT_GET_CLASS(SMMUBaseClass, (obj), TYPE_ARM_SMMU) +/* Return the SMMUPciBus handle associated to a PCI bus number */ +SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num); + +/* Return the stream ID of an SMMU device */ +static inline uint16_t smmu_get_sid(SMMUDevice *sdev) +{ + return PCI_BUILD_BDF(pci_bus_num(sdev->bus), sdev->devfn); +} #endif /* HW_ARM_SMMU_COMMON */ From 93641948d4c85f261be1f25a0bdc2ded3476e7d9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0253/2380] hw/arm/smmu-common: VMSAv8-64 page table walk This patch implements the page table walk for VMSAv8-64. Signed-off-by: Eric Auger Signed-off-by: Prem Mallappa Message-id: 1524665762-31355-4-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 222 +++++++++++++++++++++++++++++++++++ hw/arm/smmu-internal.h | 99 ++++++++++++++++ hw/arm/trace-events | 9 +- include/hw/arm/smmu-common.h | 14 +++ 4 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 hw/arm/smmu-internal.h diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 3d64bcfac2..01c7be82b6 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -27,6 +27,228 @@ #include "qemu/error-report.h" #include "hw/arm/smmu-common.h" +#include "smmu-internal.h" + +/* VMSAv8-64 Translation */ + +/** + * get_pte - Get the content of a page table entry located at + * @base_addr[@index] + */ +static int get_pte(dma_addr_t baseaddr, uint32_t index, uint64_t *pte, + SMMUPTWEventInfo *info) +{ + int ret; + dma_addr_t addr = baseaddr + index * sizeof(*pte); + + /* TODO: guarantee 64-bit single-copy atomicity */ + ret = dma_memory_read(&address_space_memory, addr, + (uint8_t *)pte, sizeof(*pte)); + + if (ret != MEMTX_OK) { + info->type = SMMU_PTW_ERR_WALK_EABT; + info->addr = addr; + return -EINVAL; + } + trace_smmu_get_pte(baseaddr, index, addr, *pte); + return 0; +} + +/* VMSAv8-64 Translation Table Format Descriptor Decoding */ + +/** + * get_page_pte_address - returns the L3 descriptor output address, + * ie. the page frame + * ARM ARM spec: Figure D4-17 VMSAv8-64 level 3 descriptor format + */ +static inline hwaddr get_page_pte_address(uint64_t pte, int granule_sz) +{ + return PTE_ADDRESS(pte, granule_sz); +} + +/** + * get_table_pte_address - return table descriptor output address, + * ie. address of next level table + * ARM ARM Figure D4-16 VMSAv8-64 level0, level1, and level 2 descriptor formats + */ +static inline hwaddr get_table_pte_address(uint64_t pte, int granule_sz) +{ + return PTE_ADDRESS(pte, granule_sz); +} + +/** + * get_block_pte_address - return block descriptor output address and block size + * ARM ARM Figure D4-16 VMSAv8-64 level0, level1, and level 2 descriptor formats + */ +static inline hwaddr get_block_pte_address(uint64_t pte, int level, + int granule_sz, uint64_t *bsz) +{ + int n = (granule_sz - 3) * (4 - level) + 3; + + *bsz = 1 << n; + return PTE_ADDRESS(pte, n); +} + +SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova) +{ + bool tbi = extract64(iova, 55, 1) ? TBI1(cfg->tbi) : TBI0(cfg->tbi); + uint8_t tbi_byte = tbi * 8; + + if (cfg->tt[0].tsz && + !extract64(iova, 64 - cfg->tt[0].tsz, cfg->tt[0].tsz - tbi_byte)) { + /* there is a ttbr0 region and we are in it (high bits all zero) */ + return &cfg->tt[0]; + } else if (cfg->tt[1].tsz && + !extract64(iova, 64 - cfg->tt[1].tsz, cfg->tt[1].tsz - tbi_byte)) { + /* there is a ttbr1 region and we are in it (high bits all one) */ + return &cfg->tt[1]; + } else if (!cfg->tt[0].tsz) { + /* ttbr0 region is "everything not in the ttbr1 region" */ + return &cfg->tt[0]; + } else if (!cfg->tt[1].tsz) { + /* ttbr1 region is "everything not in the ttbr0 region" */ + return &cfg->tt[1]; + } + /* in the gap between the two regions, this is a Translation fault */ + return NULL; +} + +/** + * smmu_ptw_64 - VMSAv8-64 Walk of the page tables for a given IOVA + * @cfg: translation config + * @iova: iova to translate + * @perm: access type + * @tlbe: IOMMUTLBEntry (out) + * @info: handle to an error info + * + * Return 0 on success, < 0 on error. In case of error, @info is filled + * and tlbe->perm is set to IOMMU_NONE. + * Upon success, @tlbe is filled with translated_addr and entry + * permission rights. + */ +static int smmu_ptw_64(SMMUTransCfg *cfg, + dma_addr_t iova, IOMMUAccessFlags perm, + IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +{ + dma_addr_t baseaddr, indexmask; + int stage = cfg->stage; + SMMUTransTableInfo *tt = select_tt(cfg, iova); + uint8_t level, granule_sz, inputsize, stride; + + if (!tt || tt->disabled) { + info->type = SMMU_PTW_ERR_TRANSLATION; + goto error; + } + + granule_sz = tt->granule_sz; + stride = granule_sz - 3; + inputsize = 64 - tt->tsz; + level = 4 - (inputsize - 4) / stride; + indexmask = (1ULL << (inputsize - (stride * (4 - level)))) - 1; + baseaddr = extract64(tt->ttb, 0, 48); + baseaddr &= ~indexmask; + + tlbe->iova = iova; + tlbe->addr_mask = (1 << granule_sz) - 1; + + while (level <= 3) { + uint64_t subpage_size = 1ULL << level_shift(level, granule_sz); + uint64_t mask = subpage_size - 1; + uint32_t offset = iova_level_offset(iova, inputsize, level, granule_sz); + uint64_t pte; + dma_addr_t pte_addr = baseaddr + offset * sizeof(pte); + uint8_t ap; + + if (get_pte(baseaddr, offset, &pte, info)) { + goto error; + } + trace_smmu_ptw_level(level, iova, subpage_size, + baseaddr, offset, pte); + + if (is_invalid_pte(pte) || is_reserved_pte(pte, level)) { + trace_smmu_ptw_invalid_pte(stage, level, baseaddr, + pte_addr, offset, pte); + info->type = SMMU_PTW_ERR_TRANSLATION; + goto error; + } + + if (is_page_pte(pte, level)) { + uint64_t gpa = get_page_pte_address(pte, granule_sz); + + ap = PTE_AP(pte); + if (is_permission_fault(ap, perm)) { + info->type = SMMU_PTW_ERR_PERMISSION; + goto error; + } + + tlbe->translated_addr = gpa + (iova & mask); + tlbe->perm = PTE_AP_TO_PERM(ap); + trace_smmu_ptw_page_pte(stage, level, iova, + baseaddr, pte_addr, pte, gpa); + return 0; + } + if (is_block_pte(pte, level)) { + uint64_t block_size; + hwaddr gpa = get_block_pte_address(pte, level, granule_sz, + &block_size); + + ap = PTE_AP(pte); + if (is_permission_fault(ap, perm)) { + info->type = SMMU_PTW_ERR_PERMISSION; + goto error; + } + + trace_smmu_ptw_block_pte(stage, level, baseaddr, + pte_addr, pte, iova, gpa, + block_size >> 20); + + tlbe->translated_addr = gpa + (iova & mask); + tlbe->perm = PTE_AP_TO_PERM(ap); + return 0; + } + + /* table pte */ + ap = PTE_APTABLE(pte); + + if (is_permission_fault(ap, perm)) { + info->type = SMMU_PTW_ERR_PERMISSION; + goto error; + } + baseaddr = get_table_pte_address(pte, granule_sz); + level++; + } + + info->type = SMMU_PTW_ERR_TRANSLATION; + +error: + tlbe->perm = IOMMU_NONE; + return -EINVAL; +} + +/** + * smmu_ptw - Walk the page tables for an IOVA, according to @cfg + * + * @cfg: translation configuration + * @iova: iova to translate + * @perm: tentative access type + * @tlbe: returned entry + * @info: ptw event handle + * + * return 0 on success + */ +inline int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, + IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +{ + if (!cfg->aa64) { + /* + * This code path is not entered as we check this while decoding + * the configuration data in the derived SMMU model. + */ + g_assert_not_reached(); + } + + return smmu_ptw_64(cfg, iova, perm, tlbe, info); +} /** * The bus number is used for lookup when SID based invalidation occurs. diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h new file mode 100644 index 0000000000..7794d6d394 --- /dev/null +++ b/hw/arm/smmu-internal.h @@ -0,0 +1,99 @@ +/* + * ARM SMMU support - Internal API + * + * Copyright (c) 2017 Red Hat, Inc. + * Copyright (C) 2014-2016 Broadcom Corporation + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 + * 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 . + */ + +#ifndef HW_ARM_SMMU_INTERNAL_H +#define HW_ARM_SMMU_INTERNAL_H + +#define TBI0(tbi) ((tbi) & 0x1) +#define TBI1(tbi) ((tbi) & 0x2 >> 1) + +/* PTE Manipulation */ + +#define ARM_LPAE_PTE_TYPE_SHIFT 0 +#define ARM_LPAE_PTE_TYPE_MASK 0x3 + +#define ARM_LPAE_PTE_TYPE_BLOCK 1 +#define ARM_LPAE_PTE_TYPE_TABLE 3 + +#define ARM_LPAE_L3_PTE_TYPE_RESERVED 1 +#define ARM_LPAE_L3_PTE_TYPE_PAGE 3 + +#define ARM_LPAE_PTE_VALID (1 << 0) + +#define PTE_ADDRESS(pte, shift) \ + (extract64(pte, shift, 47 - shift + 1) << shift) + +#define is_invalid_pte(pte) (!(pte & ARM_LPAE_PTE_VALID)) + +#define is_reserved_pte(pte, level) \ + ((level == 3) && \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_L3_PTE_TYPE_RESERVED)) + +#define is_block_pte(pte, level) \ + ((level < 3) && \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_PTE_TYPE_BLOCK)) + +#define is_table_pte(pte, level) \ + ((level < 3) && \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_PTE_TYPE_TABLE)) + +#define is_page_pte(pte, level) \ + ((level == 3) && \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_L3_PTE_TYPE_PAGE)) + +/* access permissions */ + +#define PTE_AP(pte) \ + (extract64(pte, 6, 2)) + +#define PTE_APTABLE(pte) \ + (extract64(pte, 61, 2)) + +/* + * TODO: At the moment all transactions are considered as privileged (EL1) + * as IOMMU translation callback does not pass user/priv attributes. + */ +#define is_permission_fault(ap, perm) \ + (((perm) & IOMMU_WO) && ((ap) & 0x2)) + +#define PTE_AP_TO_PERM(ap) \ + (IOMMU_ACCESS_FLAG(true, !((ap) & 0x2))) + +/* Level Indexing */ + +static inline int level_shift(int level, int granule_sz) +{ + return granule_sz + (3 - level) * (granule_sz - 3); +} + +static inline uint64_t level_page_mask(int level, int granule_sz) +{ + return ~(MAKE_64BIT_MASK(0, level_shift(level, granule_sz))); +} + +static inline +uint64_t iova_level_offset(uint64_t iova, int inputsize, + int level, int gsz) +{ + return ((iova & MAKE_64BIT_MASK(0, inputsize)) >> level_shift(level, gsz)) & + MAKE_64BIT_MASK(0, gsz - 3); +} + +#endif diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 8e8b53c95d..52b2d5be3e 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -4,4 +4,11 @@ virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." # hw/arm/smmu-common.c -smmu_add_mr(const char *name) "%s" \ No newline at end of file +smmu_add_mr(const char *name) "%s" +smmu_page_walk(int stage, uint64_t baseaddr, int first_level, uint64_t start, uint64_t end) "stage=%d, baseaddr=0x%"PRIx64", first level=%d, start=0x%"PRIx64", end=0x%"PRIx64 +smmu_lookup_table(int level, uint64_t baseaddr, int granule_sz, uint64_t start, uint64_t end, int flags, uint64_t subpage_size) "level=%d baseaddr=0x%"PRIx64" granule=%d, start=0x%"PRIx64" end=0x%"PRIx64" flags=%d subpage_size=0x%"PRIx64 +smmu_ptw_level(int level, uint64_t iova, size_t subpage_size, uint64_t baseaddr, uint32_t offset, uint64_t pte) "level=%d iova=0x%"PRIx64" subpage_sz=0x%zx baseaddr=0x%"PRIx64" offset=%d => pte=0x%"PRIx64 +smmu_ptw_invalid_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint32_t offset, uint64_t pte) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" offset=%d pte=0x%"PRIx64 +smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t address) "stage=%d level=%d iova=0x%"PRIx64" base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" page address = 0x%"PRIx64 +smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB" +smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64 diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 8b947774b0..c41eb5c3b0 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -128,4 +128,18 @@ static inline uint16_t smmu_get_sid(SMMUDevice *sdev) { return PCI_BUILD_BDF(pci_bus_num(sdev->bus), sdev->devfn); } + +/** + * smmu_ptw - Perform the page table walk for a given iova / access flags + * pair, according to @cfg translation config + */ +int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, + IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info); + +/** + * select_tt - compute which translation table shall be used according to + * the input iova and translation config and return the TT specific info + */ +SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova); + #endif /* HW_ARM_SMMU_COMMON */ From 10a83cb9887eddb1b504ecf3b189159db949178e Mon Sep 17 00:00:00 2001 From: Prem Mallappa Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0254/2380] hw/arm/smmuv3: Skeleton This patch implements a skeleton for the smmuv3 device. Datatypes and register definitions are introduced. The MMIO region, the interrupts and the queue are initialized. Only the MMIO read operation is implemented here. Signed-off-by: Prem Mallappa Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Message-id: 1524665762-31355-5-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/Makefile.objs | 2 +- hw/arm/smmuv3-internal.h | 142 +++++++++++++++ hw/arm/smmuv3.c | 366 +++++++++++++++++++++++++++++++++++++++ hw/arm/trace-events | 3 + include/hw/arm/smmuv3.h | 87 ++++++++++ 5 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 hw/arm/smmuv3-internal.h create mode 100644 hw/arm/smmuv3.c create mode 100644 include/hw/arm/smmuv3.h diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 558436f3a5..d51fcecaf2 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -35,4 +35,4 @@ obj-$(CONFIG_MPS2) += mps2-tz.o obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o obj-$(CONFIG_IOTKIT) += iotkit.o obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o -obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o +obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h new file mode 100644 index 0000000000..8da38d46c0 --- /dev/null +++ b/hw/arm/smmuv3-internal.h @@ -0,0 +1,142 @@ +/* + * ARM SMMUv3 support - Internal API + * + * Copyright (C) 2014-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ + +#ifndef HW_ARM_SMMU_V3_INTERNAL_H +#define HW_ARM_SMMU_V3_INTERNAL_H + +#include "hw/arm/smmu-common.h" + +/* MMIO Registers */ + +REG32(IDR0, 0x0) + FIELD(IDR0, S1P, 1 , 1) + FIELD(IDR0, TTF, 2 , 2) + FIELD(IDR0, COHACC, 4 , 1) + FIELD(IDR0, ASID16, 12, 1) + FIELD(IDR0, TTENDIAN, 21, 2) + FIELD(IDR0, STALL_MODEL, 24, 2) + FIELD(IDR0, TERM_MODEL, 26, 1) + FIELD(IDR0, STLEVEL, 27, 2) + +REG32(IDR1, 0x4) + FIELD(IDR1, SIDSIZE, 0 , 6) + FIELD(IDR1, EVENTQS, 16, 5) + FIELD(IDR1, CMDQS, 21, 5) + +#define SMMU_IDR1_SIDSIZE 16 +#define SMMU_CMDQS 19 +#define SMMU_EVENTQS 19 + +REG32(IDR2, 0x8) +REG32(IDR3, 0xc) +REG32(IDR4, 0x10) +REG32(IDR5, 0x14) + FIELD(IDR5, OAS, 0, 3); + FIELD(IDR5, GRAN4K, 4, 1); + FIELD(IDR5, GRAN16K, 5, 1); + FIELD(IDR5, GRAN64K, 6, 1); + +#define SMMU_IDR5_OAS 4 + +REG32(IIDR, 0x1c) +REG32(CR0, 0x20) + FIELD(CR0, SMMU_ENABLE, 0, 1) + FIELD(CR0, EVENTQEN, 2, 1) + FIELD(CR0, CMDQEN, 3, 1) + +REG32(CR0ACK, 0x24) +REG32(CR1, 0x28) +REG32(CR2, 0x2c) +REG32(STATUSR, 0x40) +REG32(IRQ_CTRL, 0x50) + FIELD(IRQ_CTRL, GERROR_IRQEN, 0, 1) + FIELD(IRQ_CTRL, PRI_IRQEN, 1, 1) + FIELD(IRQ_CTRL, EVENTQ_IRQEN, 2, 1) + +REG32(IRQ_CTRL_ACK, 0x54) +REG32(GERROR, 0x60) + FIELD(GERROR, CMDQ_ERR, 0, 1) + FIELD(GERROR, EVENTQ_ABT_ERR, 2, 1) + FIELD(GERROR, PRIQ_ABT_ERR, 3, 1) + FIELD(GERROR, MSI_CMDQ_ABT_ERR, 4, 1) + FIELD(GERROR, MSI_EVENTQ_ABT_ERR, 5, 1) + FIELD(GERROR, MSI_PRIQ_ABT_ERR, 6, 1) + FIELD(GERROR, MSI_GERROR_ABT_ERR, 7, 1) + FIELD(GERROR, MSI_SFM_ERR, 8, 1) + +REG32(GERRORN, 0x64) + +#define A_GERROR_IRQ_CFG0 0x68 /* 64b */ +REG32(GERROR_IRQ_CFG1, 0x70) +REG32(GERROR_IRQ_CFG2, 0x74) + +#define A_STRTAB_BASE 0x80 /* 64b */ + +#define SMMU_BASE_ADDR_MASK 0xffffffffffe0 + +REG32(STRTAB_BASE_CFG, 0x88) + FIELD(STRTAB_BASE_CFG, FMT, 16, 2) + FIELD(STRTAB_BASE_CFG, SPLIT, 6 , 5) + FIELD(STRTAB_BASE_CFG, LOG2SIZE, 0 , 6) + +#define A_CMDQ_BASE 0x90 /* 64b */ +REG32(CMDQ_PROD, 0x98) +REG32(CMDQ_CONS, 0x9c) + FIELD(CMDQ_CONS, ERR, 24, 7) + +#define A_EVENTQ_BASE 0xa0 /* 64b */ +REG32(EVENTQ_PROD, 0xa8) +REG32(EVENTQ_CONS, 0xac) + +#define A_EVENTQ_IRQ_CFG0 0xb0 /* 64b */ +REG32(EVENTQ_IRQ_CFG1, 0xb8) +REG32(EVENTQ_IRQ_CFG2, 0xbc) + +#define A_IDREGS 0xfd0 + +static inline int smmu_enabled(SMMUv3State *s) +{ + return FIELD_EX32(s->cr[0], CR0, SMMU_ENABLE); +} + +/* Command Queue Entry */ +typedef struct Cmd { + uint32_t word[4]; +} Cmd; + +/* Event Queue Entry */ +typedef struct Evt { + uint32_t word[8]; +} Evt; + +static inline uint32_t smmuv3_idreg(int regoffset) +{ + /* + * Return the value of the Primecell/Corelink ID registers at the + * specified offset from the first ID register. + * These value indicate an ARM implementation of MMU600 p1 + */ + static const uint8_t smmuv3_ids[] = { + 0x04, 0, 0, 0, 0x84, 0xB4, 0xF0, 0x10, 0x0D, 0xF0, 0x05, 0xB1 + }; + return smmuv3_ids[regoffset / 4]; +} + +#endif diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c new file mode 100644 index 0000000000..b61f274393 --- /dev/null +++ b/hw/arm/smmuv3.c @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2014-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "hw/boards.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/qdev-core.h" +#include "hw/pci/pci.h" +#include "exec/address-spaces.h" +#include "trace.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" + +#include "hw/arm/smmuv3.h" +#include "smmuv3-internal.h" + +static void smmuv3_init_regs(SMMUv3State *s) +{ + /** + * IDR0: stage1 only, AArch64 only, coherent access, 16b ASID, + * multi-level stream table + */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S1P, 1); /* stage 1 supported */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TTF, 2); /* AArch64 PTW only */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, COHACC, 1); /* IO coherent */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, ASID16, 1); /* 16-bit ASID */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TTENDIAN, 2); /* little endian */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, STALL_MODEL, 1); /* No stall */ + /* terminated transaction will always be aborted/error returned */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TERM_MODEL, 1); + /* 2-level stream table supported */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, STLEVEL, 1); + + s->idr[1] = FIELD_DP32(s->idr[1], IDR1, SIDSIZE, SMMU_IDR1_SIDSIZE); + s->idr[1] = FIELD_DP32(s->idr[1], IDR1, EVENTQS, SMMU_EVENTQS); + s->idr[1] = FIELD_DP32(s->idr[1], IDR1, CMDQS, SMMU_CMDQS); + + /* 4K and 64K granule support */ + s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN4K, 1); + s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN64K, 1); + s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS); /* 44 bits */ + + s->cmdq.base = deposit64(s->cmdq.base, 0, 5, SMMU_CMDQS); + s->cmdq.prod = 0; + s->cmdq.cons = 0; + s->cmdq.entry_size = sizeof(struct Cmd); + s->eventq.base = deposit64(s->eventq.base, 0, 5, SMMU_EVENTQS); + s->eventq.prod = 0; + s->eventq.cons = 0; + s->eventq.entry_size = sizeof(struct Evt); + + s->features = 0; + s->sid_split = 0; +} + +static MemTxResult smmu_write_mmio(void *opaque, hwaddr offset, uint64_t data, + unsigned size, MemTxAttrs attrs) +{ + /* not yet implemented */ + return MEMTX_ERROR; +} + +static MemTxResult smmu_readll(SMMUv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + switch (offset) { + case A_GERROR_IRQ_CFG0: + *data = s->gerror_irq_cfg0; + return MEMTX_OK; + case A_STRTAB_BASE: + *data = s->strtab_base; + return MEMTX_OK; + case A_CMDQ_BASE: + *data = s->cmdq.base; + return MEMTX_OK; + case A_EVENTQ_BASE: + *data = s->eventq.base; + return MEMTX_OK; + default: + *data = 0; + qemu_log_mask(LOG_UNIMP, + "%s Unexpected 64-bit access to 0x%"PRIx64" (RAZ)\n", + __func__, offset); + return MEMTX_OK; + } +} + +static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + switch (offset) { + case A_IDREGS ... A_IDREGS + 0x1f: + *data = smmuv3_idreg(offset - A_IDREGS); + return MEMTX_OK; + case A_IDR0 ... A_IDR5: + *data = s->idr[(offset - A_IDR0) / 4]; + return MEMTX_OK; + case A_IIDR: + *data = s->iidr; + return MEMTX_OK; + case A_CR0: + *data = s->cr[0]; + return MEMTX_OK; + case A_CR0ACK: + *data = s->cr0ack; + return MEMTX_OK; + case A_CR1: + *data = s->cr[1]; + return MEMTX_OK; + case A_CR2: + *data = s->cr[2]; + return MEMTX_OK; + case A_STATUSR: + *data = s->statusr; + return MEMTX_OK; + case A_IRQ_CTRL: + case A_IRQ_CTRL_ACK: + *data = s->irq_ctrl; + return MEMTX_OK; + case A_GERROR: + *data = s->gerror; + return MEMTX_OK; + case A_GERRORN: + *data = s->gerrorn; + return MEMTX_OK; + case A_GERROR_IRQ_CFG0: /* 64b */ + *data = extract64(s->gerror_irq_cfg0, 0, 32); + return MEMTX_OK; + case A_GERROR_IRQ_CFG0 + 4: + *data = extract64(s->gerror_irq_cfg0, 32, 32); + return MEMTX_OK; + case A_GERROR_IRQ_CFG1: + *data = s->gerror_irq_cfg1; + return MEMTX_OK; + case A_GERROR_IRQ_CFG2: + *data = s->gerror_irq_cfg2; + return MEMTX_OK; + case A_STRTAB_BASE: /* 64b */ + *data = extract64(s->strtab_base, 0, 32); + return MEMTX_OK; + case A_STRTAB_BASE + 4: /* 64b */ + *data = extract64(s->strtab_base, 32, 32); + return MEMTX_OK; + case A_STRTAB_BASE_CFG: + *data = s->strtab_base_cfg; + return MEMTX_OK; + case A_CMDQ_BASE: /* 64b */ + *data = extract64(s->cmdq.base, 0, 32); + return MEMTX_OK; + case A_CMDQ_BASE + 4: + *data = extract64(s->cmdq.base, 32, 32); + return MEMTX_OK; + case A_CMDQ_PROD: + *data = s->cmdq.prod; + return MEMTX_OK; + case A_CMDQ_CONS: + *data = s->cmdq.cons; + return MEMTX_OK; + case A_EVENTQ_BASE: /* 64b */ + *data = extract64(s->eventq.base, 0, 32); + return MEMTX_OK; + case A_EVENTQ_BASE + 4: /* 64b */ + *data = extract64(s->eventq.base, 32, 32); + return MEMTX_OK; + case A_EVENTQ_PROD: + *data = s->eventq.prod; + return MEMTX_OK; + case A_EVENTQ_CONS: + *data = s->eventq.cons; + return MEMTX_OK; + default: + *data = 0; + qemu_log_mask(LOG_UNIMP, + "%s unhandled 32-bit access at 0x%"PRIx64" (RAZ)\n", + __func__, offset); + return MEMTX_OK; + } +} + +static MemTxResult smmu_read_mmio(void *opaque, hwaddr offset, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + SMMUState *sys = opaque; + SMMUv3State *s = ARM_SMMUV3(sys); + MemTxResult r; + + /* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */ + offset &= ~0x10000; + + switch (size) { + case 8: + r = smmu_readll(s, offset, data, attrs); + break; + case 4: + r = smmu_readl(s, offset, data, attrs); + break; + default: + r = MEMTX_ERROR; + break; + } + + trace_smmuv3_read_mmio(offset, *data, size, r); + return r; +} + +static const MemoryRegionOps smmu_mem_ops = { + .read_with_attrs = smmu_read_mmio, + .write_with_attrs = smmu_write_mmio, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +static void smmu_init_irq(SMMUv3State *s, SysBusDevice *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + sysbus_init_irq(dev, &s->irq[i]); + } +} + +static void smmu_reset(DeviceState *dev) +{ + SMMUv3State *s = ARM_SMMUV3(dev); + SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s); + + c->parent_reset(dev); + + smmuv3_init_regs(s); +} + +static void smmu_realize(DeviceState *d, Error **errp) +{ + SMMUState *sys = ARM_SMMU(d); + SMMUv3State *s = ARM_SMMUV3(sys); + SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s); + SysBusDevice *dev = SYS_BUS_DEVICE(d); + Error *local_err = NULL; + + c->parent_realize(d, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + memory_region_init_io(&sys->iomem, OBJECT(s), + &smmu_mem_ops, sys, TYPE_ARM_SMMUV3, 0x20000); + + sys->mrtypename = TYPE_SMMUV3_IOMMU_MEMORY_REGION; + + sysbus_init_mmio(dev, &sys->iomem); + + smmu_init_irq(s, dev); +} + +static const VMStateDescription vmstate_smmuv3_queue = { + .name = "smmuv3_queue", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(base, SMMUQueue), + VMSTATE_UINT32(prod, SMMUQueue), + VMSTATE_UINT32(cons, SMMUQueue), + VMSTATE_UINT8(log2size, SMMUQueue), + }, +}; + +static const VMStateDescription vmstate_smmuv3 = { + .name = "smmuv3", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(features, SMMUv3State), + VMSTATE_UINT8(sid_size, SMMUv3State), + VMSTATE_UINT8(sid_split, SMMUv3State), + + VMSTATE_UINT32_ARRAY(cr, SMMUv3State, 3), + VMSTATE_UINT32(cr0ack, SMMUv3State), + VMSTATE_UINT32(statusr, SMMUv3State), + VMSTATE_UINT32(irq_ctrl, SMMUv3State), + VMSTATE_UINT32(gerror, SMMUv3State), + VMSTATE_UINT32(gerrorn, SMMUv3State), + VMSTATE_UINT64(gerror_irq_cfg0, SMMUv3State), + VMSTATE_UINT32(gerror_irq_cfg1, SMMUv3State), + VMSTATE_UINT32(gerror_irq_cfg2, SMMUv3State), + VMSTATE_UINT64(strtab_base, SMMUv3State), + VMSTATE_UINT32(strtab_base_cfg, SMMUv3State), + VMSTATE_UINT64(eventq_irq_cfg0, SMMUv3State), + VMSTATE_UINT32(eventq_irq_cfg1, SMMUv3State), + VMSTATE_UINT32(eventq_irq_cfg2, SMMUv3State), + + VMSTATE_STRUCT(cmdq, SMMUv3State, 0, vmstate_smmuv3_queue, SMMUQueue), + VMSTATE_STRUCT(eventq, SMMUv3State, 0, vmstate_smmuv3_queue, SMMUQueue), + + VMSTATE_END_OF_LIST(), + }, +}; + +static void smmuv3_instance_init(Object *obj) +{ + /* Nothing much to do here as of now */ +} + +static void smmuv3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SMMUv3Class *c = ARM_SMMUV3_CLASS(klass); + + dc->vmsd = &vmstate_smmuv3; + device_class_set_parent_reset(dc, smmu_reset, &c->parent_reset); + c->parent_realize = dc->realize; + dc->realize = smmu_realize; +} + +static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ +} + +static const TypeInfo smmuv3_type_info = { + .name = TYPE_ARM_SMMUV3, + .parent = TYPE_ARM_SMMU, + .instance_size = sizeof(SMMUv3State), + .instance_init = smmuv3_instance_init, + .class_size = sizeof(SMMUv3Class), + .class_init = smmuv3_class_init, +}; + +static const TypeInfo smmuv3_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_SMMUV3_IOMMU_MEMORY_REGION, + .class_init = smmuv3_iommu_memory_region_class_init, +}; + +static void smmuv3_register_types(void) +{ + type_register(&smmuv3_type_info); + type_register(&smmuv3_iommu_memory_region_info); +} + +type_init(smmuv3_register_types) + diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 52b2d5be3e..983ed4b68c 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -12,3 +12,6 @@ smmu_ptw_invalid_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t address) "stage=%d level=%d iova=0x%"PRIx64" base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" page address = 0x%"PRIx64 smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB" smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64 + +#hw/arm/smmuv3.c +smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h new file mode 100644 index 0000000000..23f70363e5 --- /dev/null +++ b/include/hw/arm/smmuv3.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ + +#ifndef HW_ARM_SMMUV3_H +#define HW_ARM_SMMUV3_H + +#include "hw/arm/smmu-common.h" +#include "hw/registerfields.h" + +#define TYPE_SMMUV3_IOMMU_MEMORY_REGION "smmuv3-iommu-memory-region" + +typedef struct SMMUQueue { + uint64_t base; /* base register */ + uint32_t prod; + uint32_t cons; + uint8_t entry_size; + uint8_t log2size; +} SMMUQueue; + +typedef struct SMMUv3State { + SMMUState smmu_state; + + uint32_t features; + uint8_t sid_size; + uint8_t sid_split; + + uint32_t idr[6]; + uint32_t iidr; + uint32_t cr[3]; + uint32_t cr0ack; + uint32_t statusr; + uint32_t irq_ctrl; + uint32_t gerror; + uint32_t gerrorn; + uint64_t gerror_irq_cfg0; + uint32_t gerror_irq_cfg1; + uint32_t gerror_irq_cfg2; + uint64_t strtab_base; + uint32_t strtab_base_cfg; + uint64_t eventq_irq_cfg0; + uint32_t eventq_irq_cfg1; + uint32_t eventq_irq_cfg2; + + SMMUQueue eventq, cmdq; + + qemu_irq irq[4]; +} SMMUv3State; + +typedef enum { + SMMU_IRQ_EVTQ, + SMMU_IRQ_PRIQ, + SMMU_IRQ_CMD_SYNC, + SMMU_IRQ_GERROR, +} SMMUIrq; + +typedef struct { + /*< private >*/ + SMMUBaseClass smmu_base_class; + /*< public >*/ + + DeviceRealize parent_realize; + DeviceReset parent_reset; +} SMMUv3Class; + +#define TYPE_ARM_SMMUV3 "arm-smmuv3" +#define ARM_SMMUV3(obj) OBJECT_CHECK(SMMUv3State, (obj), TYPE_ARM_SMMUV3) +#define ARM_SMMUV3_CLASS(klass) \ + OBJECT_CLASS_CHECK(SMMUv3Class, (klass), TYPE_ARM_SMMUV3) +#define ARM_SMMUV3_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SMMUv3Class, (obj), TYPE_ARM_SMMUV3) + +#endif From 6a736033d343e0e5774849fa0eef88f2582c364a Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0255/2380] hw/arm/smmuv3: Wired IRQ and GERROR helpers We introduce some helpers to handle wired IRQs and especially GERROR interrupt. SMMU writes GERROR register on GERROR event and SW acks GERROR interrupts by setting GERRORn. The Wired interrupts are edge sensitive hence the pulse usage. Signed-off-by: Eric Auger Signed-off-by: Prem Mallappa Reviewed-by: Peter Maydell Message-id: 1524665762-31355-6-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/smmuv3-internal.h | 14 +++++++++ hw/arm/smmuv3.c | 64 ++++++++++++++++++++++++++++++++++++++++ hw/arm/trace-events | 3 ++ 3 files changed, 81 insertions(+) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index 8da38d46c0..e27c128c07 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -139,4 +139,18 @@ static inline uint32_t smmuv3_idreg(int regoffset) return smmuv3_ids[regoffset / 4]; } +static inline bool smmuv3_eventq_irq_enabled(SMMUv3State *s) +{ + return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, EVENTQ_IRQEN); +} + +static inline bool smmuv3_gerror_irq_enabled(SMMUv3State *s) +{ + return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, GERROR_IRQEN); +} + +/* public until callers get introduced */ +void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask); +void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t gerrorn); + #endif diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index b61f274393..c0cedcaba3 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -31,6 +31,70 @@ #include "hw/arm/smmuv3.h" #include "smmuv3-internal.h" +/** + * smmuv3_trigger_irq - pulse @irq if enabled and update + * GERROR register in case of GERROR interrupt + * + * @irq: irq type + * @gerror_mask: mask of gerrors to toggle (relevant if @irq is GERROR) + */ +void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask) +{ + + bool pulse = false; + + switch (irq) { + case SMMU_IRQ_EVTQ: + pulse = smmuv3_eventq_irq_enabled(s); + break; + case SMMU_IRQ_PRIQ: + qemu_log_mask(LOG_UNIMP, "PRI not yet supported\n"); + break; + case SMMU_IRQ_CMD_SYNC: + pulse = true; + break; + case SMMU_IRQ_GERROR: + { + uint32_t pending = s->gerror ^ s->gerrorn; + uint32_t new_gerrors = ~pending & gerror_mask; + + if (!new_gerrors) { + /* only toggle non pending errors */ + return; + } + s->gerror ^= new_gerrors; + trace_smmuv3_write_gerror(new_gerrors, s->gerror); + + pulse = smmuv3_gerror_irq_enabled(s); + break; + } + } + if (pulse) { + trace_smmuv3_trigger_irq(irq); + qemu_irq_pulse(s->irq[irq]); + } +} + +void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) +{ + uint32_t pending = s->gerror ^ s->gerrorn; + uint32_t toggled = s->gerrorn ^ new_gerrorn; + + if (toggled & ~pending) { + qemu_log_mask(LOG_GUEST_ERROR, + "guest toggles non pending errors = 0x%x\n", + toggled & ~pending); + } + + /* + * We do not raise any error in case guest toggles bits corresponding + * to not active IRQs (CONSTRAINED UNPREDICTABLE) + */ + s->gerrorn = new_gerrorn; + + trace_smmuv3_write_gerrorn(toggled & pending, s->gerrorn); +} + static void smmuv3_init_regs(SMMUv3State *s) { /** diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 983ed4b68c..e192baf62d 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -15,3 +15,6 @@ smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "base #hw/arm/smmuv3.c smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" +smmuv3_trigger_irq(int irq) "irq=%d" +smmuv3_write_gerror(uint32_t toggled, uint32_t gerror) "toggled=0x%x, new GERROR=0x%x" +smmuv3_write_gerrorn(uint32_t acked, uint32_t gerrorn) "acked=0x%x, new GERRORN=0x%x" From dadd1a0809b1aff8c4d5364f3714b3e0e039dcb0 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0256/2380] hw/arm/smmuv3: Queue helpers We introduce helpers to read/write into the command and event circular queues. smmuv3_write_eventq and smmuv3_cmq_consume will become static in subsequent patches. Invalidation commands are not yet dealt with. We do not cache data that need to be invalidated. This will change with vhost integration. Signed-off-by: Eric Auger Signed-off-by: Prem Mallappa Reviewed-by: Peter Maydell Message-id: 1524665762-31355-7-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/smmuv3-internal.h | 163 +++++++++++++++++++++++++++++++++++++++ hw/arm/smmuv3.c | 136 ++++++++++++++++++++++++++++++++ hw/arm/trace-events | 5 ++ 3 files changed, 304 insertions(+) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index e27c128c07..223d8406ed 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -153,4 +153,167 @@ static inline bool smmuv3_gerror_irq_enabled(SMMUv3State *s) void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask); void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t gerrorn); +/* Queue Handling */ + +#define Q_BASE(q) ((q)->base & SMMU_BASE_ADDR_MASK) +#define WRAP_MASK(q) (1 << (q)->log2size) +#define INDEX_MASK(q) (((1 << (q)->log2size)) - 1) +#define WRAP_INDEX_MASK(q) ((1 << ((q)->log2size + 1)) - 1) + +#define Q_CONS(q) ((q)->cons & INDEX_MASK(q)) +#define Q_PROD(q) ((q)->prod & INDEX_MASK(q)) + +#define Q_CONS_ENTRY(q) (Q_BASE(q) + (q)->entry_size * Q_CONS(q)) +#define Q_PROD_ENTRY(q) (Q_BASE(q) + (q)->entry_size * Q_PROD(q)) + +#define Q_CONS_WRAP(q) (((q)->cons & WRAP_MASK(q)) >> (q)->log2size) +#define Q_PROD_WRAP(q) (((q)->prod & WRAP_MASK(q)) >> (q)->log2size) + +static inline bool smmuv3_q_full(SMMUQueue *q) +{ + return ((q->cons ^ q->prod) & WRAP_INDEX_MASK(q)) == WRAP_MASK(q); +} + +static inline bool smmuv3_q_empty(SMMUQueue *q) +{ + return (q->cons & WRAP_INDEX_MASK(q)) == (q->prod & WRAP_INDEX_MASK(q)); +} + +static inline void queue_prod_incr(SMMUQueue *q) +{ + q->prod = (q->prod + 1) & WRAP_INDEX_MASK(q); +} + +static inline void queue_cons_incr(SMMUQueue *q) +{ + /* + * We have to use deposit for the CONS registers to preserve + * the ERR field in the high bits. + */ + q->cons = deposit32(q->cons, 0, q->log2size + 1, q->cons + 1); +} + +static inline bool smmuv3_cmdq_enabled(SMMUv3State *s) +{ + return FIELD_EX32(s->cr[0], CR0, CMDQEN); +} + +static inline bool smmuv3_eventq_enabled(SMMUv3State *s) +{ + return FIELD_EX32(s->cr[0], CR0, EVENTQEN); +} + +static inline void smmu_write_cmdq_err(SMMUv3State *s, uint32_t err_type) +{ + s->cmdq.cons = FIELD_DP32(s->cmdq.cons, CMDQ_CONS, ERR, err_type); +} + +void smmuv3_write_eventq(SMMUv3State *s, Evt *evt); + +/* Commands */ + +typedef enum SMMUCommandType { + SMMU_CMD_NONE = 0x00, + SMMU_CMD_PREFETCH_CONFIG , + SMMU_CMD_PREFETCH_ADDR, + SMMU_CMD_CFGI_STE, + SMMU_CMD_CFGI_STE_RANGE, + SMMU_CMD_CFGI_CD, + SMMU_CMD_CFGI_CD_ALL, + SMMU_CMD_CFGI_ALL, + SMMU_CMD_TLBI_NH_ALL = 0x10, + SMMU_CMD_TLBI_NH_ASID, + SMMU_CMD_TLBI_NH_VA, + SMMU_CMD_TLBI_NH_VAA, + SMMU_CMD_TLBI_EL3_ALL = 0x18, + SMMU_CMD_TLBI_EL3_VA = 0x1a, + SMMU_CMD_TLBI_EL2_ALL = 0x20, + SMMU_CMD_TLBI_EL2_ASID, + SMMU_CMD_TLBI_EL2_VA, + SMMU_CMD_TLBI_EL2_VAA, + SMMU_CMD_TLBI_S12_VMALL = 0x28, + SMMU_CMD_TLBI_S2_IPA = 0x2a, + SMMU_CMD_TLBI_NSNH_ALL = 0x30, + SMMU_CMD_ATC_INV = 0x40, + SMMU_CMD_PRI_RESP, + SMMU_CMD_RESUME = 0x44, + SMMU_CMD_STALL_TERM, + SMMU_CMD_SYNC, +} SMMUCommandType; + +static const char *cmd_stringify[] = { + [SMMU_CMD_PREFETCH_CONFIG] = "SMMU_CMD_PREFETCH_CONFIG", + [SMMU_CMD_PREFETCH_ADDR] = "SMMU_CMD_PREFETCH_ADDR", + [SMMU_CMD_CFGI_STE] = "SMMU_CMD_CFGI_STE", + [SMMU_CMD_CFGI_STE_RANGE] = "SMMU_CMD_CFGI_STE_RANGE", + [SMMU_CMD_CFGI_CD] = "SMMU_CMD_CFGI_CD", + [SMMU_CMD_CFGI_CD_ALL] = "SMMU_CMD_CFGI_CD_ALL", + [SMMU_CMD_CFGI_ALL] = "SMMU_CMD_CFGI_ALL", + [SMMU_CMD_TLBI_NH_ALL] = "SMMU_CMD_TLBI_NH_ALL", + [SMMU_CMD_TLBI_NH_ASID] = "SMMU_CMD_TLBI_NH_ASID", + [SMMU_CMD_TLBI_NH_VA] = "SMMU_CMD_TLBI_NH_VA", + [SMMU_CMD_TLBI_NH_VAA] = "SMMU_CMD_TLBI_NH_VAA", + [SMMU_CMD_TLBI_EL3_ALL] = "SMMU_CMD_TLBI_EL3_ALL", + [SMMU_CMD_TLBI_EL3_VA] = "SMMU_CMD_TLBI_EL3_VA", + [SMMU_CMD_TLBI_EL2_ALL] = "SMMU_CMD_TLBI_EL2_ALL", + [SMMU_CMD_TLBI_EL2_ASID] = "SMMU_CMD_TLBI_EL2_ASID", + [SMMU_CMD_TLBI_EL2_VA] = "SMMU_CMD_TLBI_EL2_VA", + [SMMU_CMD_TLBI_EL2_VAA] = "SMMU_CMD_TLBI_EL2_VAA", + [SMMU_CMD_TLBI_S12_VMALL] = "SMMU_CMD_TLBI_S12_VMALL", + [SMMU_CMD_TLBI_S2_IPA] = "SMMU_CMD_TLBI_S2_IPA", + [SMMU_CMD_TLBI_NSNH_ALL] = "SMMU_CMD_TLBI_NSNH_ALL", + [SMMU_CMD_ATC_INV] = "SMMU_CMD_ATC_INV", + [SMMU_CMD_PRI_RESP] = "SMMU_CMD_PRI_RESP", + [SMMU_CMD_RESUME] = "SMMU_CMD_RESUME", + [SMMU_CMD_STALL_TERM] = "SMMU_CMD_STALL_TERM", + [SMMU_CMD_SYNC] = "SMMU_CMD_SYNC", +}; + +static inline const char *smmu_cmd_string(SMMUCommandType type) +{ + if (type > SMMU_CMD_NONE && type < ARRAY_SIZE(cmd_stringify)) { + return cmd_stringify[type] ? cmd_stringify[type] : "UNKNOWN"; + } else { + return "INVALID"; + } +} + +/* CMDQ fields */ + +typedef enum { + SMMU_CERROR_NONE = 0, + SMMU_CERROR_ILL, + SMMU_CERROR_ABT, + SMMU_CERROR_ATC_INV_SYNC, +} SMMUCmdError; + +enum { /* Command completion notification */ + CMD_SYNC_SIG_NONE, + CMD_SYNC_SIG_IRQ, + CMD_SYNC_SIG_SEV, +}; + +#define CMD_TYPE(x) extract32((x)->word[0], 0 , 8) +#define CMD_SSEC(x) extract32((x)->word[0], 10, 1) +#define CMD_SSV(x) extract32((x)->word[0], 11, 1) +#define CMD_RESUME_AC(x) extract32((x)->word[0], 12, 1) +#define CMD_RESUME_AB(x) extract32((x)->word[0], 13, 1) +#define CMD_SYNC_CS(x) extract32((x)->word[0], 12, 2) +#define CMD_SSID(x) extract32((x)->word[0], 12, 20) +#define CMD_SID(x) ((x)->word[1]) +#define CMD_VMID(x) extract32((x)->word[1], 0 , 16) +#define CMD_ASID(x) extract32((x)->word[1], 16, 16) +#define CMD_RESUME_STAG(x) extract32((x)->word[2], 0 , 16) +#define CMD_RESP(x) extract32((x)->word[2], 11, 2) +#define CMD_LEAF(x) extract32((x)->word[2], 0 , 1) +#define CMD_STE_RANGE(x) extract32((x)->word[2], 0 , 5) +#define CMD_ADDR(x) ({ \ + uint64_t high = (uint64_t)(x)->word[3]; \ + uint64_t low = extract32((x)->word[2], 12, 20); \ + uint64_t addr = high << 32 | (low << 12); \ + addr; \ + }) + +int smmuv3_cmdq_consume(SMMUv3State *s); + #endif diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index c0cedcaba3..8f50f1565b 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -95,6 +95,46 @@ void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) trace_smmuv3_write_gerrorn(toggled & pending, s->gerrorn); } +static inline MemTxResult queue_read(SMMUQueue *q, void *data) +{ + dma_addr_t addr = Q_CONS_ENTRY(q); + + return dma_memory_read(&address_space_memory, addr, data, q->entry_size); +} + +static MemTxResult queue_write(SMMUQueue *q, void *data) +{ + dma_addr_t addr = Q_PROD_ENTRY(q); + MemTxResult ret; + + ret = dma_memory_write(&address_space_memory, addr, data, q->entry_size); + if (ret != MEMTX_OK) { + return ret; + } + + queue_prod_incr(q); + return MEMTX_OK; +} + +void smmuv3_write_eventq(SMMUv3State *s, Evt *evt) +{ + SMMUQueue *q = &s->eventq; + + if (!smmuv3_eventq_enabled(s)) { + return; + } + + if (smmuv3_q_full(q)) { + return; + } + + queue_write(q, evt); + + if (smmuv3_q_empty(q)) { + smmuv3_trigger_irq(s, SMMU_IRQ_EVTQ, 0); + } +} + static void smmuv3_init_regs(SMMUv3State *s) { /** @@ -134,6 +174,102 @@ static void smmuv3_init_regs(SMMUv3State *s) s->sid_split = 0; } +int smmuv3_cmdq_consume(SMMUv3State *s) +{ + SMMUCmdError cmd_error = SMMU_CERROR_NONE; + SMMUQueue *q = &s->cmdq; + SMMUCommandType type = 0; + + if (!smmuv3_cmdq_enabled(s)) { + return 0; + } + /* + * some commands depend on register values, typically CR0. In case those + * register values change while handling the command, spec says it + * is UNPREDICTABLE whether the command is interpreted under the new + * or old value. + */ + + while (!smmuv3_q_empty(q)) { + uint32_t pending = s->gerror ^ s->gerrorn; + Cmd cmd; + + trace_smmuv3_cmdq_consume(Q_PROD(q), Q_CONS(q), + Q_PROD_WRAP(q), Q_CONS_WRAP(q)); + + if (FIELD_EX32(pending, GERROR, CMDQ_ERR)) { + break; + } + + if (queue_read(q, &cmd) != MEMTX_OK) { + cmd_error = SMMU_CERROR_ABT; + break; + } + + type = CMD_TYPE(&cmd); + + trace_smmuv3_cmdq_opcode(smmu_cmd_string(type)); + + switch (type) { + case SMMU_CMD_SYNC: + if (CMD_SYNC_CS(&cmd) & CMD_SYNC_SIG_IRQ) { + smmuv3_trigger_irq(s, SMMU_IRQ_CMD_SYNC, 0); + } + break; + case SMMU_CMD_PREFETCH_CONFIG: + case SMMU_CMD_PREFETCH_ADDR: + case SMMU_CMD_CFGI_STE: + case SMMU_CMD_CFGI_STE_RANGE: /* same as SMMU_CMD_CFGI_ALL */ + case SMMU_CMD_CFGI_CD: + case SMMU_CMD_CFGI_CD_ALL: + case SMMU_CMD_TLBI_NH_ALL: + case SMMU_CMD_TLBI_NH_ASID: + case SMMU_CMD_TLBI_NH_VA: + case SMMU_CMD_TLBI_NH_VAA: + case SMMU_CMD_TLBI_EL3_ALL: + case SMMU_CMD_TLBI_EL3_VA: + case SMMU_CMD_TLBI_EL2_ALL: + case SMMU_CMD_TLBI_EL2_ASID: + case SMMU_CMD_TLBI_EL2_VA: + case SMMU_CMD_TLBI_EL2_VAA: + case SMMU_CMD_TLBI_S12_VMALL: + case SMMU_CMD_TLBI_S2_IPA: + case SMMU_CMD_TLBI_NSNH_ALL: + case SMMU_CMD_ATC_INV: + case SMMU_CMD_PRI_RESP: + case SMMU_CMD_RESUME: + case SMMU_CMD_STALL_TERM: + trace_smmuv3_unhandled_cmd(type); + break; + default: + cmd_error = SMMU_CERROR_ILL; + qemu_log_mask(LOG_GUEST_ERROR, + "Illegal command type: %d\n", CMD_TYPE(&cmd)); + break; + } + if (cmd_error) { + break; + } + /* + * We only increment the cons index after the completion of + * the command. We do that because the SYNC returns immediately + * and does not check the completion of previous commands + */ + queue_cons_incr(q); + } + + if (cmd_error) { + trace_smmuv3_cmdq_consume_error(smmu_cmd_string(type), cmd_error); + smmu_write_cmdq_err(s, cmd_error); + smmuv3_trigger_irq(s, SMMU_IRQ_GERROR, R_GERROR_CMDQ_ERR_MASK); + } + + trace_smmuv3_cmdq_consume_out(Q_PROD(q), Q_CONS(q), + Q_PROD_WRAP(q), Q_CONS_WRAP(q)); + + return 0; +} + static MemTxResult smmu_write_mmio(void *opaque, hwaddr offset, uint64_t data, unsigned size, MemTxAttrs attrs) { diff --git a/hw/arm/trace-events b/hw/arm/trace-events index e192baf62d..ee6e496cb4 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -18,3 +18,8 @@ smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: smmuv3_trigger_irq(int irq) "irq=%d" smmuv3_write_gerror(uint32_t toggled, uint32_t gerror) "toggled=0x%x, new GERROR=0x%x" smmuv3_write_gerrorn(uint32_t acked, uint32_t gerrorn) "acked=0x%x, new GERRORN=0x%x" +smmuv3_unhandled_cmd(uint32_t type) "Unhandled command type=%d" +smmuv3_cmdq_consume(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod=%d cons=%d prod.wrap=%d cons.wrap=%d" +smmuv3_cmdq_opcode(const char *opcode) "<--- %s" +smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d " +smmuv3_cmdq_consume_error(const char *cmd_name, uint8_t cmd_error) "Error on %s command execution: %d" From fae4be38b35dcfae48494c023454e8988c15b69a Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0257/2380] hw/arm/smmuv3: Implement MMIO write operations Now we have relevant helpers for queue and irq management, let's implement MMIO write operations. Signed-off-by: Eric Auger Signed-off-by: Prem Mallappa Reviewed-by: Peter Maydell Message-id: 1524665762-31355-8-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/smmuv3-internal.h | 8 +- hw/arm/smmuv3.c | 170 +++++++++++++++++++++++++++++++++++++-- hw/arm/trace-events | 6 ++ 3 files changed, 174 insertions(+), 10 deletions(-) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index 223d8406ed..282285d310 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -61,6 +61,8 @@ REG32(CR0, 0x20) FIELD(CR0, EVENTQEN, 2, 1) FIELD(CR0, CMDQEN, 3, 1) +#define SMMU_CR0_RESERVED 0xFFFFFC20 + REG32(CR0ACK, 0x24) REG32(CR1, 0x28) REG32(CR2, 0x2c) @@ -149,10 +151,6 @@ static inline bool smmuv3_gerror_irq_enabled(SMMUv3State *s) return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, GERROR_IRQEN); } -/* public until callers get introduced */ -void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask); -void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t gerrorn); - /* Queue Handling */ #define Q_BASE(q) ((q)->base & SMMU_BASE_ADDR_MASK) @@ -314,6 +312,6 @@ enum { /* Command completion notification */ addr; \ }) -int smmuv3_cmdq_consume(SMMUv3State *s); +#define SMMU_FEATURE_2LVL_STE (1 << 0) #endif diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 8f50f1565b..d581ada3d7 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -38,7 +38,8 @@ * @irq: irq type * @gerror_mask: mask of gerrors to toggle (relevant if @irq is GERROR) */ -void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask) +static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, + uint32_t gerror_mask) { bool pulse = false; @@ -75,7 +76,7 @@ void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask) } } -void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) +static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) { uint32_t pending = s->gerror ^ s->gerrorn; uint32_t toggled = s->gerrorn ^ new_gerrorn; @@ -174,7 +175,7 @@ static void smmuv3_init_regs(SMMUv3State *s) s->sid_split = 0; } -int smmuv3_cmdq_consume(SMMUv3State *s) +static int smmuv3_cmdq_consume(SMMUv3State *s) { SMMUCmdError cmd_error = SMMU_CERROR_NONE; SMMUQueue *q = &s->cmdq; @@ -270,11 +271,170 @@ int smmuv3_cmdq_consume(SMMUv3State *s) return 0; } +static MemTxResult smmu_writell(SMMUv3State *s, hwaddr offset, + uint64_t data, MemTxAttrs attrs) +{ + switch (offset) { + case A_GERROR_IRQ_CFG0: + s->gerror_irq_cfg0 = data; + return MEMTX_OK; + case A_STRTAB_BASE: + s->strtab_base = data; + return MEMTX_OK; + case A_CMDQ_BASE: + s->cmdq.base = data; + s->cmdq.log2size = extract64(s->cmdq.base, 0, 5); + if (s->cmdq.log2size > SMMU_CMDQS) { + s->cmdq.log2size = SMMU_CMDQS; + } + return MEMTX_OK; + case A_EVENTQ_BASE: + s->eventq.base = data; + s->eventq.log2size = extract64(s->eventq.base, 0, 5); + if (s->eventq.log2size > SMMU_EVENTQS) { + s->eventq.log2size = SMMU_EVENTQS; + } + return MEMTX_OK; + case A_EVENTQ_IRQ_CFG0: + s->eventq_irq_cfg0 = data; + return MEMTX_OK; + default: + qemu_log_mask(LOG_UNIMP, + "%s Unexpected 64-bit access to 0x%"PRIx64" (WI)\n", + __func__, offset); + return MEMTX_OK; + } +} + +static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset, + uint64_t data, MemTxAttrs attrs) +{ + switch (offset) { + case A_CR0: + s->cr[0] = data; + s->cr0ack = data & ~SMMU_CR0_RESERVED; + /* in case the command queue has been enabled */ + smmuv3_cmdq_consume(s); + return MEMTX_OK; + case A_CR1: + s->cr[1] = data; + return MEMTX_OK; + case A_CR2: + s->cr[2] = data; + return MEMTX_OK; + case A_IRQ_CTRL: + s->irq_ctrl = data; + return MEMTX_OK; + case A_GERRORN: + smmuv3_write_gerrorn(s, data); + /* + * By acknowledging the CMDQ_ERR, SW may notify cmds can + * be processed again + */ + smmuv3_cmdq_consume(s); + return MEMTX_OK; + case A_GERROR_IRQ_CFG0: /* 64b */ + s->gerror_irq_cfg0 = deposit64(s->gerror_irq_cfg0, 0, 32, data); + return MEMTX_OK; + case A_GERROR_IRQ_CFG0 + 4: + s->gerror_irq_cfg0 = deposit64(s->gerror_irq_cfg0, 32, 32, data); + return MEMTX_OK; + case A_GERROR_IRQ_CFG1: + s->gerror_irq_cfg1 = data; + return MEMTX_OK; + case A_GERROR_IRQ_CFG2: + s->gerror_irq_cfg2 = data; + return MEMTX_OK; + case A_STRTAB_BASE: /* 64b */ + s->strtab_base = deposit64(s->strtab_base, 0, 32, data); + return MEMTX_OK; + case A_STRTAB_BASE + 4: + s->strtab_base = deposit64(s->strtab_base, 32, 32, data); + return MEMTX_OK; + case A_STRTAB_BASE_CFG: + s->strtab_base_cfg = data; + if (FIELD_EX32(data, STRTAB_BASE_CFG, FMT) == 1) { + s->sid_split = FIELD_EX32(data, STRTAB_BASE_CFG, SPLIT); + s->features |= SMMU_FEATURE_2LVL_STE; + } + return MEMTX_OK; + case A_CMDQ_BASE: /* 64b */ + s->cmdq.base = deposit64(s->cmdq.base, 0, 32, data); + s->cmdq.log2size = extract64(s->cmdq.base, 0, 5); + if (s->cmdq.log2size > SMMU_CMDQS) { + s->cmdq.log2size = SMMU_CMDQS; + } + return MEMTX_OK; + case A_CMDQ_BASE + 4: /* 64b */ + s->cmdq.base = deposit64(s->cmdq.base, 32, 32, data); + return MEMTX_OK; + case A_CMDQ_PROD: + s->cmdq.prod = data; + smmuv3_cmdq_consume(s); + return MEMTX_OK; + case A_CMDQ_CONS: + s->cmdq.cons = data; + return MEMTX_OK; + case A_EVENTQ_BASE: /* 64b */ + s->eventq.base = deposit64(s->eventq.base, 0, 32, data); + s->eventq.log2size = extract64(s->eventq.base, 0, 5); + if (s->eventq.log2size > SMMU_EVENTQS) { + s->eventq.log2size = SMMU_EVENTQS; + } + return MEMTX_OK; + case A_EVENTQ_BASE + 4: + s->eventq.base = deposit64(s->eventq.base, 32, 32, data); + return MEMTX_OK; + case A_EVENTQ_PROD: + s->eventq.prod = data; + return MEMTX_OK; + case A_EVENTQ_CONS: + s->eventq.cons = data; + return MEMTX_OK; + case A_EVENTQ_IRQ_CFG0: /* 64b */ + s->eventq_irq_cfg0 = deposit64(s->eventq_irq_cfg0, 0, 32, data); + return MEMTX_OK; + case A_EVENTQ_IRQ_CFG0 + 4: + s->eventq_irq_cfg0 = deposit64(s->eventq_irq_cfg0, 32, 32, data); + return MEMTX_OK; + case A_EVENTQ_IRQ_CFG1: + s->eventq_irq_cfg1 = data; + return MEMTX_OK; + case A_EVENTQ_IRQ_CFG2: + s->eventq_irq_cfg2 = data; + return MEMTX_OK; + default: + qemu_log_mask(LOG_UNIMP, + "%s Unexpected 32-bit access to 0x%"PRIx64" (WI)\n", + __func__, offset); + return MEMTX_OK; + } +} + static MemTxResult smmu_write_mmio(void *opaque, hwaddr offset, uint64_t data, unsigned size, MemTxAttrs attrs) { - /* not yet implemented */ - return MEMTX_ERROR; + SMMUState *sys = opaque; + SMMUv3State *s = ARM_SMMUV3(sys); + MemTxResult r; + + /* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */ + offset &= ~0x10000; + + switch (size) { + case 8: + r = smmu_writell(s, offset, data, attrs); + break; + case 4: + r = smmu_writel(s, offset, data, attrs); + break; + default: + r = MEMTX_ERROR; + break; + } + + trace_smmuv3_write_mmio(offset, data, size, r); + return r; } static MemTxResult smmu_readll(SMMUv3State *s, hwaddr offset, diff --git a/hw/arm/trace-events b/hw/arm/trace-events index ee6e496cb4..b0ecf82319 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -23,3 +23,9 @@ smmuv3_cmdq_consume(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t con smmuv3_cmdq_opcode(const char *opcode) "<--- %s" smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d " smmuv3_cmdq_consume_error(const char *cmd_name, uint8_t cmd_error) "Error on %s command execution: %d" +smmuv3_update(bool is_empty, uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "q empty:%d prod:%d cons:%d p.wrap:%d p.cons:%d" +smmuv3_update_check_cmd(int error) "cmdq not enabled or error :0x%x" +smmuv3_write_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" +smmuv3_write_mmio_idr(uint64_t addr, uint64_t val) "write to RO/Unimpl reg 0x%"PRIx64" val64:0x%"PRIx64 +smmuv3_write_mmio_evtq_cons_bef_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "Before clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" +smmuv3_write_mmio_evtq_cons_after_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "after clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" From bb981004eaf4bab2c8ae4feaaf6ead8be7275044 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0258/2380] hw/arm/smmuv3: Event queue recording helper Let's introduce a helper function aiming at recording an event in the event queue. Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Message-id: 1524665762-31355-9-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/smmuv3-internal.h | 148 ++++++++++++++++++++++++++++++++++++++- hw/arm/smmuv3.c | 108 ++++++++++++++++++++++++++-- hw/arm/trace-events | 1 + 3 files changed, 249 insertions(+), 8 deletions(-) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index 282285d310..2d50300a56 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -206,8 +206,6 @@ static inline void smmu_write_cmdq_err(SMMUv3State *s, uint32_t err_type) s->cmdq.cons = FIELD_DP32(s->cmdq.cons, CMDQ_CONS, ERR, err_type); } -void smmuv3_write_eventq(SMMUv3State *s, Evt *evt); - /* Commands */ typedef enum SMMUCommandType { @@ -314,4 +312,150 @@ enum { /* Command completion notification */ #define SMMU_FEATURE_2LVL_STE (1 << 0) +/* Events */ + +typedef enum SMMUEventType { + SMMU_EVT_OK = 0x00, + SMMU_EVT_F_UUT , + SMMU_EVT_C_BAD_STREAMID , + SMMU_EVT_F_STE_FETCH , + SMMU_EVT_C_BAD_STE , + SMMU_EVT_F_BAD_ATS_TREQ , + SMMU_EVT_F_STREAM_DISABLED , + SMMU_EVT_F_TRANS_FORBIDDEN , + SMMU_EVT_C_BAD_SUBSTREAMID , + SMMU_EVT_F_CD_FETCH , + SMMU_EVT_C_BAD_CD , + SMMU_EVT_F_WALK_EABT , + SMMU_EVT_F_TRANSLATION = 0x10, + SMMU_EVT_F_ADDR_SIZE , + SMMU_EVT_F_ACCESS , + SMMU_EVT_F_PERMISSION , + SMMU_EVT_F_TLB_CONFLICT = 0x20, + SMMU_EVT_F_CFG_CONFLICT , + SMMU_EVT_E_PAGE_REQ = 0x24, +} SMMUEventType; + +static const char *event_stringify[] = { + [SMMU_EVT_OK] = "SMMU_EVT_OK", + [SMMU_EVT_F_UUT] = "SMMU_EVT_F_UUT", + [SMMU_EVT_C_BAD_STREAMID] = "SMMU_EVT_C_BAD_STREAMID", + [SMMU_EVT_F_STE_FETCH] = "SMMU_EVT_F_STE_FETCH", + [SMMU_EVT_C_BAD_STE] = "SMMU_EVT_C_BAD_STE", + [SMMU_EVT_F_BAD_ATS_TREQ] = "SMMU_EVT_F_BAD_ATS_TREQ", + [SMMU_EVT_F_STREAM_DISABLED] = "SMMU_EVT_F_STREAM_DISABLED", + [SMMU_EVT_F_TRANS_FORBIDDEN] = "SMMU_EVT_F_TRANS_FORBIDDEN", + [SMMU_EVT_C_BAD_SUBSTREAMID] = "SMMU_EVT_C_BAD_SUBSTREAMID", + [SMMU_EVT_F_CD_FETCH] = "SMMU_EVT_F_CD_FETCH", + [SMMU_EVT_C_BAD_CD] = "SMMU_EVT_C_BAD_CD", + [SMMU_EVT_F_WALK_EABT] = "SMMU_EVT_F_WALK_EABT", + [SMMU_EVT_F_TRANSLATION] = "SMMU_EVT_F_TRANSLATION", + [SMMU_EVT_F_ADDR_SIZE] = "SMMU_EVT_F_ADDR_SIZE", + [SMMU_EVT_F_ACCESS] = "SMMU_EVT_F_ACCESS", + [SMMU_EVT_F_PERMISSION] = "SMMU_EVT_F_PERMISSION", + [SMMU_EVT_F_TLB_CONFLICT] = "SMMU_EVT_F_TLB_CONFLICT", + [SMMU_EVT_F_CFG_CONFLICT] = "SMMU_EVT_F_CFG_CONFLICT", + [SMMU_EVT_E_PAGE_REQ] = "SMMU_EVT_E_PAGE_REQ", +}; + +static inline const char *smmu_event_string(SMMUEventType type) +{ + if (type < ARRAY_SIZE(event_stringify)) { + return event_stringify[type] ? event_stringify[type] : "UNKNOWN"; + } else { + return "INVALID"; + } +} + +/* Encode an event record */ +typedef struct SMMUEventInfo { + SMMUEventType type; + uint32_t sid; + bool recorded; + bool record_trans_faults; + union { + struct { + uint32_t ssid; + bool ssv; + dma_addr_t addr; + bool rnw; + bool pnu; + bool ind; + } f_uut; + struct SSIDInfo { + uint32_t ssid; + bool ssv; + } c_bad_streamid; + struct SSIDAddrInfo { + uint32_t ssid; + bool ssv; + dma_addr_t addr; + } f_ste_fetch; + struct SSIDInfo c_bad_ste; + struct { + dma_addr_t addr; + bool rnw; + } f_transl_forbidden; + struct { + uint32_t ssid; + } c_bad_substream; + struct SSIDAddrInfo f_cd_fetch; + struct SSIDInfo c_bad_cd; + struct FullInfo { + bool stall; + uint16_t stag; + uint32_t ssid; + bool ssv; + bool s2; + dma_addr_t addr; + bool rnw; + bool pnu; + bool ind; + uint8_t class; + dma_addr_t addr2; + } f_walk_eabt; + struct FullInfo f_translation; + struct FullInfo f_addr_size; + struct FullInfo f_access; + struct FullInfo f_permission; + struct SSIDInfo f_cfg_conflict; + /** + * not supported yet: + * F_BAD_ATS_TREQ + * F_BAD_ATS_TREQ + * F_TLB_CONFLICT + * E_PAGE_REQUEST + * IMPDEF_EVENTn + */ + } u; +} SMMUEventInfo; + +/* EVTQ fields */ + +#define EVT_Q_OVERFLOW (1 << 31) + +#define EVT_SET_TYPE(x, v) deposit32((x)->word[0], 0 , 8 , v) +#define EVT_SET_SSV(x, v) deposit32((x)->word[0], 11, 1 , v) +#define EVT_SET_SSID(x, v) deposit32((x)->word[0], 12, 20, v) +#define EVT_SET_SID(x, v) ((x)->word[1] = v) +#define EVT_SET_STAG(x, v) deposit32((x)->word[2], 0 , 16, v) +#define EVT_SET_STALL(x, v) deposit32((x)->word[2], 31, 1 , v) +#define EVT_SET_PNU(x, v) deposit32((x)->word[3], 1 , 1 , v) +#define EVT_SET_IND(x, v) deposit32((x)->word[3], 2 , 1 , v) +#define EVT_SET_RNW(x, v) deposit32((x)->word[3], 3 , 1 , v) +#define EVT_SET_S2(x, v) deposit32((x)->word[3], 7 , 1 , v) +#define EVT_SET_CLASS(x, v) deposit32((x)->word[3], 8 , 2 , v) +#define EVT_SET_ADDR(x, addr) \ + do { \ + (x)->word[5] = (uint32_t)(addr >> 32); \ + (x)->word[4] = (uint32_t)(addr & 0xffffffff); \ + } while (0) +#define EVT_SET_ADDR2(x, addr) \ + do { \ + deposit32((x)->word[7], 3, 29, addr >> 16); \ + deposit32((x)->word[7], 0, 16, addr & 0xffff);\ + } while (0) + +void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event); + #endif diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index d581ada3d7..cfce013ac5 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -117,23 +117,119 @@ static MemTxResult queue_write(SMMUQueue *q, void *data) return MEMTX_OK; } -void smmuv3_write_eventq(SMMUv3State *s, Evt *evt) +static MemTxResult smmuv3_write_eventq(SMMUv3State *s, Evt *evt) { SMMUQueue *q = &s->eventq; + MemTxResult r; + + if (!smmuv3_eventq_enabled(s)) { + return MEMTX_ERROR; + } + + if (smmuv3_q_full(q)) { + return MEMTX_ERROR; + } + + r = queue_write(q, evt); + if (r != MEMTX_OK) { + return r; + } + + if (smmuv3_q_empty(q)) { + smmuv3_trigger_irq(s, SMMU_IRQ_EVTQ, 0); + } + return MEMTX_OK; +} + +void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info) +{ + Evt evt; + MemTxResult r; if (!smmuv3_eventq_enabled(s)) { return; } - if (smmuv3_q_full(q)) { + EVT_SET_TYPE(&evt, info->type); + EVT_SET_SID(&evt, info->sid); + + switch (info->type) { + case SMMU_EVT_OK: return; + case SMMU_EVT_F_UUT: + EVT_SET_SSID(&evt, info->u.f_uut.ssid); + EVT_SET_SSV(&evt, info->u.f_uut.ssv); + EVT_SET_ADDR(&evt, info->u.f_uut.addr); + EVT_SET_RNW(&evt, info->u.f_uut.rnw); + EVT_SET_PNU(&evt, info->u.f_uut.pnu); + EVT_SET_IND(&evt, info->u.f_uut.ind); + break; + case SMMU_EVT_C_BAD_STREAMID: + EVT_SET_SSID(&evt, info->u.c_bad_streamid.ssid); + EVT_SET_SSV(&evt, info->u.c_bad_streamid.ssv); + break; + case SMMU_EVT_F_STE_FETCH: + EVT_SET_SSID(&evt, info->u.f_ste_fetch.ssid); + EVT_SET_SSV(&evt, info->u.f_ste_fetch.ssv); + EVT_SET_ADDR(&evt, info->u.f_ste_fetch.addr); + break; + case SMMU_EVT_C_BAD_STE: + EVT_SET_SSID(&evt, info->u.c_bad_ste.ssid); + EVT_SET_SSV(&evt, info->u.c_bad_ste.ssv); + break; + case SMMU_EVT_F_STREAM_DISABLED: + break; + case SMMU_EVT_F_TRANS_FORBIDDEN: + EVT_SET_ADDR(&evt, info->u.f_transl_forbidden.addr); + EVT_SET_RNW(&evt, info->u.f_transl_forbidden.rnw); + break; + case SMMU_EVT_C_BAD_SUBSTREAMID: + EVT_SET_SSID(&evt, info->u.c_bad_substream.ssid); + break; + case SMMU_EVT_F_CD_FETCH: + EVT_SET_SSID(&evt, info->u.f_cd_fetch.ssid); + EVT_SET_SSV(&evt, info->u.f_cd_fetch.ssv); + EVT_SET_ADDR(&evt, info->u.f_cd_fetch.addr); + break; + case SMMU_EVT_C_BAD_CD: + EVT_SET_SSID(&evt, info->u.c_bad_cd.ssid); + EVT_SET_SSV(&evt, info->u.c_bad_cd.ssv); + break; + case SMMU_EVT_F_WALK_EABT: + case SMMU_EVT_F_TRANSLATION: + case SMMU_EVT_F_ADDR_SIZE: + case SMMU_EVT_F_ACCESS: + case SMMU_EVT_F_PERMISSION: + EVT_SET_STALL(&evt, info->u.f_walk_eabt.stall); + EVT_SET_STAG(&evt, info->u.f_walk_eabt.stag); + EVT_SET_SSID(&evt, info->u.f_walk_eabt.ssid); + EVT_SET_SSV(&evt, info->u.f_walk_eabt.ssv); + EVT_SET_S2(&evt, info->u.f_walk_eabt.s2); + EVT_SET_ADDR(&evt, info->u.f_walk_eabt.addr); + EVT_SET_RNW(&evt, info->u.f_walk_eabt.rnw); + EVT_SET_PNU(&evt, info->u.f_walk_eabt.pnu); + EVT_SET_IND(&evt, info->u.f_walk_eabt.ind); + EVT_SET_CLASS(&evt, info->u.f_walk_eabt.class); + EVT_SET_ADDR2(&evt, info->u.f_walk_eabt.addr2); + break; + case SMMU_EVT_F_CFG_CONFLICT: + EVT_SET_SSID(&evt, info->u.f_cfg_conflict.ssid); + EVT_SET_SSV(&evt, info->u.f_cfg_conflict.ssv); + break; + /* rest is not implemented */ + case SMMU_EVT_F_BAD_ATS_TREQ: + case SMMU_EVT_F_TLB_CONFLICT: + case SMMU_EVT_E_PAGE_REQ: + default: + g_assert_not_reached(); } - queue_write(q, evt); - - if (smmuv3_q_empty(q)) { - smmuv3_trigger_irq(s, SMMU_IRQ_EVTQ, 0); + trace_smmuv3_record_event(smmu_event_string(info->type), info->sid); + r = smmuv3_write_eventq(s, &evt); + if (r != MEMTX_OK) { + smmuv3_trigger_irq(s, SMMU_IRQ_GERROR, R_GERROR_EVENTQ_ABT_ERR_MASK); } + info->recorded = true; } static void smmuv3_init_regs(SMMUv3State *s) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index b0ecf82319..07d03999f7 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -29,3 +29,4 @@ smmuv3_write_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: smmuv3_write_mmio_idr(uint64_t addr, uint64_t val) "write to RO/Unimpl reg 0x%"PRIx64" val64:0x%"PRIx64 smmuv3_write_mmio_evtq_cons_bef_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "Before clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" smmuv3_write_mmio_evtq_cons_after_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "after clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" +smmuv3_record_event(const char *type, uint32_t sid) "%s sid=%d" From 9bde7f0674fe0354ab58ccf25fdfd9f2f68f2b5c Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:51 +0100 Subject: [PATCH 0259/2380] hw/arm/smmuv3: Implement translate callback This patch implements the IOMMU Memory Region translate() callback. Most of the code relates to the translation configuration decoding and check (STE, CD). Signed-off-by: Eric Auger Signed-off-by: Prem Mallappa Message-id: 1524665762-31355-10-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmuv3-internal.h | 160 +++++++++++++++++ hw/arm/smmuv3.c | 358 +++++++++++++++++++++++++++++++++++++++ hw/arm/trace-events | 9 + 3 files changed, 527 insertions(+) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index 2d50300a56..a9d714b56e 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -458,4 +458,164 @@ typedef struct SMMUEventInfo { void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event); +/* Configuration Data */ + +/* STE Level 1 Descriptor */ +typedef struct STEDesc { + uint32_t word[2]; +} STEDesc; + +/* CD Level 1 Descriptor */ +typedef struct CDDesc { + uint32_t word[2]; +} CDDesc; + +/* Stream Table Entry(STE) */ +typedef struct STE { + uint32_t word[16]; +} STE; + +/* Context Descriptor(CD) */ +typedef struct CD { + uint32_t word[16]; +} CD; + +/* STE fields */ + +#define STE_VALID(x) extract32((x)->word[0], 0, 1) + +#define STE_CONFIG(x) extract32((x)->word[0], 1, 3) +#define STE_CFG_S1_ENABLED(config) (config & 0x1) +#define STE_CFG_S2_ENABLED(config) (config & 0x2) +#define STE_CFG_ABORT(config) (!(config & 0x4)) +#define STE_CFG_BYPASS(config) (config == 0x4) + +#define STE_S1FMT(x) extract32((x)->word[0], 4 , 2) +#define STE_S1CDMAX(x) extract32((x)->word[1], 27, 5) +#define STE_S1STALLD(x) extract32((x)->word[2], 27, 1) +#define STE_EATS(x) extract32((x)->word[2], 28, 2) +#define STE_STRW(x) extract32((x)->word[2], 30, 2) +#define STE_S2VMID(x) extract32((x)->word[4], 0 , 16) +#define STE_S2T0SZ(x) extract32((x)->word[5], 0 , 6) +#define STE_S2SL0(x) extract32((x)->word[5], 6 , 2) +#define STE_S2TG(x) extract32((x)->word[5], 14, 2) +#define STE_S2PS(x) extract32((x)->word[5], 16, 3) +#define STE_S2AA64(x) extract32((x)->word[5], 19, 1) +#define STE_S2HD(x) extract32((x)->word[5], 24, 1) +#define STE_S2HA(x) extract32((x)->word[5], 25, 1) +#define STE_S2S(x) extract32((x)->word[5], 26, 1) +#define STE_CTXPTR(x) \ + ({ \ + unsigned long addr; \ + addr = (uint64_t)extract32((x)->word[1], 0, 16) << 32; \ + addr |= (uint64_t)((x)->word[0] & 0xffffffc0); \ + addr; \ + }) + +#define STE_S2TTB(x) \ + ({ \ + unsigned long addr; \ + addr = (uint64_t)extract32((x)->word[7], 0, 16) << 32; \ + addr |= (uint64_t)((x)->word[6] & 0xfffffff0); \ + addr; \ + }) + +static inline int oas2bits(int oas_field) +{ + switch (oas_field) { + case 0: + return 32; + case 1: + return 36; + case 2: + return 40; + case 3: + return 42; + case 4: + return 44; + case 5: + return 48; + } + return -1; +} + +static inline int pa_range(STE *ste) +{ + int oas_field = MIN(STE_S2PS(ste), SMMU_IDR5_OAS); + + if (!STE_S2AA64(ste)) { + return 40; + } + + return oas2bits(oas_field); +} + +#define MAX_PA(ste) ((1 << pa_range(ste)) - 1) + +/* CD fields */ + +#define CD_VALID(x) extract32((x)->word[0], 30, 1) +#define CD_ASID(x) extract32((x)->word[1], 16, 16) +#define CD_TTB(x, sel) \ + ({ \ + uint64_t hi, lo; \ + hi = extract32((x)->word[(sel) * 2 + 3], 0, 19); \ + hi <<= 32; \ + lo = (x)->word[(sel) * 2 + 2] & ~0xfULL; \ + hi | lo; \ + }) + +#define CD_TSZ(x, sel) extract32((x)->word[0], (16 * (sel)) + 0, 6) +#define CD_TG(x, sel) extract32((x)->word[0], (16 * (sel)) + 6, 2) +#define CD_EPD(x, sel) extract32((x)->word[0], (16 * (sel)) + 14, 1) +#define CD_ENDI(x) extract32((x)->word[0], 15, 1) +#define CD_IPS(x) extract32((x)->word[1], 0 , 3) +#define CD_TBI(x) extract32((x)->word[1], 6 , 2) +#define CD_HD(x) extract32((x)->word[1], 10 , 1) +#define CD_HA(x) extract32((x)->word[1], 11 , 1) +#define CD_S(x) extract32((x)->word[1], 12, 1) +#define CD_R(x) extract32((x)->word[1], 13, 1) +#define CD_A(x) extract32((x)->word[1], 14, 1) +#define CD_AARCH64(x) extract32((x)->word[1], 9 , 1) + +#define CDM_VALID(x) ((x)->word[0] & 0x1) + +static inline int is_cd_valid(SMMUv3State *s, STE *ste, CD *cd) +{ + return CD_VALID(cd); +} + +/** + * tg2granule - Decodes the CD translation granule size field according + * to the ttbr in use + * @bits: TG0/1 fields + * @ttbr: ttbr index in use + */ +static inline int tg2granule(int bits, int ttbr) +{ + switch (bits) { + case 0: + return ttbr ? 0 : 12; + case 1: + return ttbr ? 14 : 16; + case 2: + return ttbr ? 12 : 14; + case 3: + return ttbr ? 16 : 0; + default: + return 0; + } +} + +static inline uint64_t l1std_l2ptr(STEDesc *desc) +{ + uint64_t hi, lo; + + hi = desc->word[1]; + lo = desc->word[0] & ~0x1fULL; + return hi << 32 | lo; +} + +#define L1STD_SPAN(stm) (extract32((stm)->word[0], 0, 4)) + #endif diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index cfce013ac5..d896834764 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -271,6 +271,361 @@ static void smmuv3_init_regs(SMMUv3State *s) s->sid_split = 0; } +static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf, + SMMUEventInfo *event) +{ + int ret; + + trace_smmuv3_get_ste(addr); + /* TODO: guarantee 64-bit single-copy atomicity */ + ret = dma_memory_read(&address_space_memory, addr, + (void *)buf, sizeof(*buf)); + if (ret != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "Cannot fetch pte at address=0x%"PRIx64"\n", addr); + event->type = SMMU_EVT_F_STE_FETCH; + event->u.f_ste_fetch.addr = addr; + return -EINVAL; + } + return 0; + +} + +/* @ssid > 0 not supported yet */ +static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid, + CD *buf, SMMUEventInfo *event) +{ + dma_addr_t addr = STE_CTXPTR(ste); + int ret; + + trace_smmuv3_get_cd(addr); + /* TODO: guarantee 64-bit single-copy atomicity */ + ret = dma_memory_read(&address_space_memory, addr, + (void *)buf, sizeof(*buf)); + if (ret != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "Cannot fetch pte at address=0x%"PRIx64"\n", addr); + event->type = SMMU_EVT_F_CD_FETCH; + event->u.f_ste_fetch.addr = addr; + return -EINVAL; + } + return 0; +} + +/* Returns <0 if the caller has no need to continue the translation */ +static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, + STE *ste, SMMUEventInfo *event) +{ + uint32_t config; + int ret = -EINVAL; + + if (!STE_VALID(ste)) { + goto bad_ste; + } + + config = STE_CONFIG(ste); + + if (STE_CFG_ABORT(config)) { + cfg->aborted = true; /* abort but don't record any event */ + return ret; + } + + if (STE_CFG_BYPASS(config)) { + cfg->bypassed = true; + return ret; + } + + if (STE_CFG_S2_ENABLED(config)) { + qemu_log_mask(LOG_UNIMP, "SMMUv3 does not support stage 2 yet\n"); + goto bad_ste; + } + + if (STE_S1CDMAX(ste) != 0) { + qemu_log_mask(LOG_UNIMP, + "SMMUv3 does not support multiple context descriptors yet\n"); + goto bad_ste; + } + + if (STE_S1STALLD(ste)) { + qemu_log_mask(LOG_UNIMP, + "SMMUv3 S1 stalling fault model not allowed yet\n"); + goto bad_ste; + } + return 0; + +bad_ste: + event->type = SMMU_EVT_C_BAD_STE; + return -EINVAL; +} + +/** + * smmu_find_ste - Return the stream table entry associated + * to the sid + * + * @s: smmuv3 handle + * @sid: stream ID + * @ste: returned stream table entry + * @event: handle to an event info + * + * Supports linear and 2-level stream table + * Return 0 on success, -EINVAL otherwise + */ +static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, + SMMUEventInfo *event) +{ + dma_addr_t addr; + int ret; + + trace_smmuv3_find_ste(sid, s->features, s->sid_split); + /* Check SID range */ + if (sid > (1 << SMMU_IDR1_SIDSIZE)) { + event->type = SMMU_EVT_C_BAD_STREAMID; + return -EINVAL; + } + if (s->features & SMMU_FEATURE_2LVL_STE) { + int l1_ste_offset, l2_ste_offset, max_l2_ste, span; + dma_addr_t strtab_base, l1ptr, l2ptr; + STEDesc l1std; + + strtab_base = s->strtab_base & SMMU_BASE_ADDR_MASK; + l1_ste_offset = sid >> s->sid_split; + l2_ste_offset = sid & ((1 << s->sid_split) - 1); + l1ptr = (dma_addr_t)(strtab_base + l1_ste_offset * sizeof(l1std)); + /* TODO: guarantee 64-bit single-copy atomicity */ + ret = dma_memory_read(&address_space_memory, l1ptr, + (uint8_t *)&l1std, sizeof(l1std)); + if (ret != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "Could not read L1PTR at 0X%"PRIx64"\n", l1ptr); + event->type = SMMU_EVT_F_STE_FETCH; + event->u.f_ste_fetch.addr = l1ptr; + return -EINVAL; + } + + span = L1STD_SPAN(&l1std); + + if (!span) { + /* l2ptr is not valid */ + qemu_log_mask(LOG_GUEST_ERROR, + "invalid sid=%d (L1STD span=0)\n", sid); + event->type = SMMU_EVT_C_BAD_STREAMID; + return -EINVAL; + } + max_l2_ste = (1 << span) - 1; + l2ptr = l1std_l2ptr(&l1std); + trace_smmuv3_find_ste_2lvl(s->strtab_base, l1ptr, l1_ste_offset, + l2ptr, l2_ste_offset, max_l2_ste); + if (l2_ste_offset > max_l2_ste) { + qemu_log_mask(LOG_GUEST_ERROR, + "l2_ste_offset=%d > max_l2_ste=%d\n", + l2_ste_offset, max_l2_ste); + event->type = SMMU_EVT_C_BAD_STE; + return -EINVAL; + } + addr = l2ptr + l2_ste_offset * sizeof(*ste); + } else { + addr = s->strtab_base + sid * sizeof(*ste); + } + + if (smmu_get_ste(s, addr, ste, event)) { + return -EINVAL; + } + + return 0; +} + +static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event) +{ + int ret = -EINVAL; + int i; + + if (!CD_VALID(cd) || !CD_AARCH64(cd)) { + goto bad_cd; + } + if (!CD_A(cd)) { + goto bad_cd; /* SMMU_IDR0.TERM_MODEL == 1 */ + } + if (CD_S(cd)) { + goto bad_cd; /* !STE_SECURE && SMMU_IDR0.STALL_MODEL == 1 */ + } + if (CD_HA(cd) || CD_HD(cd)) { + goto bad_cd; /* HTTU = 0 */ + } + + /* we support only those at the moment */ + cfg->aa64 = true; + cfg->stage = 1; + + cfg->oas = oas2bits(CD_IPS(cd)); + cfg->oas = MIN(oas2bits(SMMU_IDR5_OAS), cfg->oas); + cfg->tbi = CD_TBI(cd); + cfg->asid = CD_ASID(cd); + + trace_smmuv3_decode_cd(cfg->oas); + + /* decode data dependent on TT */ + for (i = 0; i <= 1; i++) { + int tg, tsz; + SMMUTransTableInfo *tt = &cfg->tt[i]; + + cfg->tt[i].disabled = CD_EPD(cd, i); + if (cfg->tt[i].disabled) { + continue; + } + + tsz = CD_TSZ(cd, i); + if (tsz < 16 || tsz > 39) { + goto bad_cd; + } + + tg = CD_TG(cd, i); + tt->granule_sz = tg2granule(tg, i); + if ((tt->granule_sz != 12 && tt->granule_sz != 16) || CD_ENDI(cd)) { + goto bad_cd; + } + + tt->tsz = tsz; + tt->ttb = CD_TTB(cd, i); + if (tt->ttb & ~(MAKE_64BIT_MASK(0, cfg->oas))) { + goto bad_cd; + } + trace_smmuv3_decode_cd_tt(i, tt->tsz, tt->ttb, tt->granule_sz); + } + + event->record_trans_faults = CD_R(cd); + + return 0; + +bad_cd: + event->type = SMMU_EVT_C_BAD_CD; + return ret; +} + +/** + * smmuv3_decode_config - Prepare the translation configuration + * for the @mr iommu region + * @mr: iommu memory region the translation config must be prepared for + * @cfg: output translation configuration which is populated through + * the different configuration decoding steps + * @event: must be zero'ed by the caller + * + * return < 0 if the translation needs to be aborted (@event is filled + * accordingly). Return 0 otherwise. + */ +static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, + SMMUEventInfo *event) +{ + SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); + uint32_t sid = smmu_get_sid(sdev); + SMMUv3State *s = sdev->smmu; + int ret = -EINVAL; + STE ste; + CD cd; + + if (smmu_find_ste(s, sid, &ste, event)) { + return ret; + } + + if (decode_ste(s, cfg, &ste, event)) { + return ret; + } + + if (smmu_get_cd(s, &ste, 0 /* ssid */, &cd, event)) { + return ret; + } + + return decode_cd(cfg, &cd, event); +} + +static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, + IOMMUAccessFlags flag) +{ + SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); + SMMUv3State *s = sdev->smmu; + uint32_t sid = smmu_get_sid(sdev); + SMMUEventInfo event = {.type = SMMU_EVT_OK, .sid = sid}; + SMMUPTWEventInfo ptw_info = {}; + SMMUTransCfg cfg = {}; + IOMMUTLBEntry entry = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = addr, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + int ret = 0; + + if (!smmu_enabled(s)) { + goto out; + } + + ret = smmuv3_decode_config(mr, &cfg, &event); + if (ret) { + goto out; + } + + if (cfg.aborted) { + goto out; + } + + ret = smmu_ptw(&cfg, addr, flag, &entry, &ptw_info); + if (ret) { + switch (ptw_info.type) { + case SMMU_PTW_ERR_WALK_EABT: + event.type = SMMU_EVT_F_WALK_EABT; + event.u.f_walk_eabt.addr = addr; + event.u.f_walk_eabt.rnw = flag & 0x1; + event.u.f_walk_eabt.class = 0x1; + event.u.f_walk_eabt.addr2 = ptw_info.addr; + break; + case SMMU_PTW_ERR_TRANSLATION: + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_TRANSLATION; + event.u.f_translation.addr = addr; + event.u.f_translation.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_ADDR_SIZE: + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_ADDR_SIZE; + event.u.f_addr_size.addr = addr; + event.u.f_addr_size.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_ACCESS: + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_ACCESS; + event.u.f_access.addr = addr; + event.u.f_access.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_PERMISSION: + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_PERMISSION; + event.u.f_permission.addr = addr; + event.u.f_permission.rnw = flag & 0x1; + } + break; + default: + g_assert_not_reached(); + } + } +out: + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s translation failed for iova=0x%"PRIx64"(%d)\n", + mr->parent_obj.name, addr, ret); + entry.perm = IOMMU_NONE; + smmuv3_record_event(s, &event); + } else if (!cfg.aborted) { + entry.perm = flag; + trace_smmuv3_translate(mr->parent_obj.name, sid, addr, + entry.translated_addr, entry.perm); + } + + return entry; +} + static int smmuv3_cmdq_consume(SMMUv3State *s) { SMMUCmdError cmd_error = SMMU_CERROR_NONE; @@ -795,6 +1150,9 @@ static void smmuv3_class_init(ObjectClass *klass, void *data) static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass, void *data) { + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = smmuv3_translate; } static const TypeInfo smmuv3_type_info = { diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 07d03999f7..2d92727602 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -30,3 +30,12 @@ smmuv3_write_mmio_idr(uint64_t addr, uint64_t val) "write to RO/Unimpl reg 0x%"P smmuv3_write_mmio_evtq_cons_bef_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "Before clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" smmuv3_write_mmio_evtq_cons_after_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "after clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" smmuv3_record_event(const char *type, uint32_t sid) "%s sid=%d" +smmuv3_find_ste(uint16_t sid, uint32_t features, uint16_t sid_split) "SID:0x%x features:0x%x, sid_split:0x%x" +smmuv3_find_ste_2lvl(uint64_t strtab_base, uint64_t l1ptr, int l1_ste_offset, uint64_t l2ptr, int l2_ste_offset, int max_l2_ste) "strtab_base:0x%"PRIx64" l1ptr:0x%"PRIx64" l1_off:0x%x, l2ptr:0x%"PRIx64" l2_off:0x%x max_l2_ste:%d" +smmuv3_get_ste(uint64_t addr) "STE addr: 0x%"PRIx64 +smmuv3_translate_bypass(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d bypass iova:0x%"PRIx64" is_write=%d" +smmuv3_translate_in(uint16_t sid, int pci_bus_num, uint64_t strtab_base) "SID:0x%x bus:%d strtab_base:0x%"PRIx64 +smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64 +smmuv3_translate(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm) "%s sid=%d iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x" +smmuv3_decode_cd(uint32_t oas) "oas=%d" +smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d" From 0d1ac82eced6bb641a98cd5d7f3b829635f95fac Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:52 +0100 Subject: [PATCH 0260/2380] hw/arm/smmuv3: Abort on vfio or vhost case At the moment, the SMMUv3 does not support notification on TLB invalidation. So let's log an error as soon as such notifier gets enabled. Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Message-id: 1524665762-31355-11-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/smmuv3.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index d896834764..b3026dea20 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1147,12 +1147,23 @@ static void smmuv3_class_init(ObjectClass *klass, void *data) dc->realize = smmu_realize; } +static void smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, + IOMMUNotifierFlag old, + IOMMUNotifierFlag new) +{ + if (old == IOMMU_NOTIFIER_NONE) { + warn_report("SMMUV3 does not support vhost/vfio integration yet: " + "devices of those types will not function properly"); + } +} + static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass, void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); imrc->translate = smmuv3_translate; + imrc->notify_flag_changed = smmuv3_notify_flag_changed; } static const TypeInfo smmuv3_type_info = { From b05c81d292be96e27bd61ed1f67d313eafbda4d9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:52 +0100 Subject: [PATCH 0261/2380] target/arm/kvm: Translate the MSI doorbell in kvm_arch_fixup_msi_route In case the MSI is translated by an IOMMU we need to fixup the MSI route with the translated address. Signed-off-by: Eric Auger Signed-off-by: Bharat Bhushan Message-id: 1524665762-31355-12-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/kvm.c | 38 +++++++++++++++++++++++++++++++++++++- target/arm/trace-events | 3 +++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index ecc39ac295..5141d0adc5 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -20,8 +20,10 @@ #include "sysemu/kvm.h" #include "kvm_arm.h" #include "cpu.h" +#include "trace.h" #include "internals.h" #include "hw/arm/arm.h" +#include "hw/pci/pci.h" #include "exec/memattrs.h" #include "exec/address-spaces.h" #include "hw/boards.h" @@ -649,7 +651,41 @@ int kvm_arm_vgic_probe(void) int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint64_t address, uint32_t data, PCIDevice *dev) { - return 0; + AddressSpace *as = pci_device_iommu_address_space(dev); + hwaddr xlat, len, doorbell_gpa; + MemoryRegionSection mrs; + MemoryRegion *mr; + int ret = 1; + + if (as == &address_space_memory) { + return 0; + } + + /* MSI doorbell address is translated by an IOMMU */ + + rcu_read_lock(); + mr = address_space_translate(as, address, &xlat, &len, true); + if (!mr) { + goto unlock; + } + mrs = memory_region_find(mr, xlat, 1); + if (!mrs.mr) { + goto unlock; + } + + doorbell_gpa = mrs.offset_within_address_space; + memory_region_unref(mrs.mr); + + route->u.msi.address_lo = doorbell_gpa; + route->u.msi.address_hi = doorbell_gpa >> 32; + + trace_kvm_arm_fixup_msi_route(address, doorbell_gpa); + + ret = 0; + +unlock: + rcu_read_unlock(); + return ret; } int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, diff --git a/target/arm/trace-events b/target/arm/trace-events index 9e37131115..6b759f9d4f 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -8,3 +8,6 @@ arm_gt_tval_write(int timer, uint64_t value) "gt_tval_write: timer %d value 0x%" arm_gt_ctl_write(int timer, uint64_t value) "gt_ctl_write: timer %d value 0x%" PRIx64 arm_gt_imask_toggle(int timer, int irqstate) "gt_ctl_write: timer %d IMASK toggle, new irqstate %d" arm_gt_cntvoff_write(uint64_t value) "gt_cntvoff_write: value 0x%" PRIx64 + +# target/arm/kvm.c +kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64 From 584105eab2f49132b00c4d4baa0d94e0a4baed38 Mon Sep 17 00:00:00 2001 From: Prem Mallappa Date: Fri, 4 May 2018 18:05:52 +0100 Subject: [PATCH 0262/2380] hw/arm/virt: Add SMMUv3 to the virt board Add code to instantiate an smmuv3 in virt machine. A new iommu integer member is introduced in VirtMachineState to store the type of the iommu in use. Signed-off-by: Prem Mallappa Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Message-id: 1524665762-31355-13-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 64 ++++++++++++++++++++++++++++++++++++++++++- include/hw/arm/virt.h | 10 +++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index dc0c0335a2..b085f0b9b4 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -58,6 +58,7 @@ #include "hw/smbios/smbios.h" #include "qapi/visitor.h" #include "standard-headers/linux/input.h" +#include "hw/arm/smmuv3.h" #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ @@ -141,6 +142,7 @@ static const MemMapEntry a15memmap[] = { [VIRT_FW_CFG] = { 0x09020000, 0x00000018 }, [VIRT_GPIO] = { 0x09030000, 0x00001000 }, [VIRT_SECURE_UART] = { 0x09040000, 0x00001000 }, + [VIRT_SMMU] = { 0x09050000, 0x00020000 }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, @@ -161,6 +163,7 @@ static const int a15irqmap[] = { [VIRT_SECURE_UART] = 8, [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */ + [VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */ [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */ }; @@ -942,7 +945,57 @@ static void create_pcie_irq_map(const VirtMachineState *vms, 0x7 /* PCI irq */); } -static void create_pcie(const VirtMachineState *vms, qemu_irq *pic) +static void create_smmu(const VirtMachineState *vms, qemu_irq *pic, + PCIBus *bus) +{ + char *node; + const char compat[] = "arm,smmu-v3"; + int irq = vms->irqmap[VIRT_SMMU]; + int i; + hwaddr base = vms->memmap[VIRT_SMMU].base; + hwaddr size = vms->memmap[VIRT_SMMU].size; + const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror"; + DeviceState *dev; + + if (vms->iommu != VIRT_IOMMU_SMMUV3 || !vms->iommu_phandle) { + return; + } + + dev = qdev_create(NULL, "arm-smmuv3"); + + object_property_set_link(OBJECT(dev), OBJECT(bus), "primary-bus", + &error_abort); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + for (i = 0; i < NUM_SMMU_IRQS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); + } + + node = g_strdup_printf("/smmuv3@%" PRIx64, base); + qemu_fdt_add_subnode(vms->fdt, node); + qemu_fdt_setprop(vms->fdt, node, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(vms->fdt, node, "reg", 2, base, 2, size); + + qemu_fdt_setprop_cells(vms->fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + + qemu_fdt_setprop(vms->fdt, node, "interrupt-names", irq_names, + sizeof(irq_names)); + + qemu_fdt_setprop_cell(vms->fdt, node, "clocks", vms->clock_phandle); + qemu_fdt_setprop_string(vms->fdt, node, "clock-names", "apb_pclk"); + qemu_fdt_setprop(vms->fdt, node, "dma-coherent", NULL, 0); + + qemu_fdt_setprop_cell(vms->fdt, node, "#iommu-cells", 1); + + qemu_fdt_setprop_cell(vms->fdt, node, "phandle", vms->iommu_phandle); + g_free(node); +} + +static void create_pcie(VirtMachineState *vms, qemu_irq *pic) { hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = vms->memmap[VIRT_PCIE_MMIO].size; @@ -1056,6 +1109,15 @@ static void create_pcie(const VirtMachineState *vms, qemu_irq *pic) qemu_fdt_setprop_cell(vms->fdt, nodename, "#interrupt-cells", 1); create_pcie_irq_map(vms, vms->gic_phandle, irq, nodename); + if (vms->iommu) { + vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt); + + create_smmu(vms, pic, pci->bus); + + qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map", + 0x0, vms->iommu_phandle, 0x0, 0x10000); + } + g_free(nodename); } diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index ba0c1a4faa..886372cdbb 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -38,6 +38,7 @@ #define NUM_GICV2M_SPIS 64 #define NUM_VIRTIO_TRANSPORTS 32 +#define NUM_SMMU_IRQS 4 #define ARCH_GICV3_MAINT_IRQ 9 @@ -59,6 +60,7 @@ enum { VIRT_GIC_V2M, VIRT_GIC_ITS, VIRT_GIC_REDIST, + VIRT_SMMU, VIRT_UART, VIRT_MMIO, VIRT_RTC, @@ -74,6 +76,12 @@ enum { VIRT_SECURE_MEM, }; +typedef enum VirtIOMMUType { + VIRT_IOMMU_NONE, + VIRT_IOMMU_SMMUV3, + VIRT_IOMMU_VIRTIO, +} VirtIOMMUType; + typedef struct MemMapEntry { hwaddr base; hwaddr size; @@ -97,6 +105,7 @@ typedef struct { bool its; bool virt; int32_t gic_version; + VirtIOMMUType iommu; struct arm_boot_info bootinfo; const MemMapEntry *memmap; const int *irqmap; @@ -106,6 +115,7 @@ typedef struct { uint32_t clock_phandle; uint32_t gic_phandle; uint32_t msi_phandle; + uint32_t iommu_phandle; int psci_conduit; } VirtMachineState; From a703b4f6c1ee25090384fe75074f2571d7b69e02 Mon Sep 17 00:00:00 2001 From: Prem Mallappa Date: Fri, 4 May 2018 18:05:52 +0100 Subject: [PATCH 0263/2380] hw/arm/virt-acpi-build: Add smmuv3 node in IORT table This patch builds the smmuv3 node in the ACPI IORT table. The RID space of the root complex, which spans 0x0-0x10000 maps to streamid space 0x0-0x10000 in smmuv3, which in turn maps to deviceid space 0x0-0x10000 in the ITS group. The guest must feature the IOMMU probe deferral series (https://lkml.org/lkml/2017/4/10/214) which fixes streamid multiple lookup. This bug is not related to the SMMU emulation. Signed-off-by: Prem Mallappa Signed-off-by: Eric Auger Reviewed-by: Shannon Zhao Message-id: 1524665762-31355-14-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 55 ++++++++++++++++++++++++++++++++----- include/hw/acpi/acpi-defs.h | 15 ++++++++++ 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index c7c6a57ec5..92ceee9c0f 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -393,19 +393,26 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned xsdt_tbl_offset) } static void -build_iort(GArray *table_data, BIOSLinker *linker) +build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { - int iort_start = table_data->len; + int nb_nodes, iort_start = table_data->len; AcpiIortIdMapping *idmap; AcpiIortItsGroup *its; AcpiIortTable *iort; - size_t node_size, iort_length; + AcpiIortSmmu3 *smmu; + size_t node_size, iort_length, smmu_offset = 0; AcpiIortRC *rc; iort = acpi_data_push(table_data, sizeof(*iort)); + if (vms->iommu == VIRT_IOMMU_SMMUV3) { + nb_nodes = 3; /* RC, ITS, SMMUv3 */ + } else { + nb_nodes = 2; /* RC, ITS */ + } + iort_length = sizeof(*iort); - iort->node_count = cpu_to_le32(2); /* RC and ITS nodes */ + iort->node_count = cpu_to_le32(nb_nodes); iort->node_offset = cpu_to_le32(sizeof(*iort)); /* ITS group node */ @@ -418,6 +425,34 @@ build_iort(GArray *table_data, BIOSLinker *linker) its->its_count = cpu_to_le32(1); its->identifiers[0] = 0; /* MADT translation_id */ + if (vms->iommu == VIRT_IOMMU_SMMUV3) { + int irq = vms->irqmap[VIRT_SMMU]; + + /* SMMUv3 node */ + smmu_offset = iort->node_offset + node_size; + node_size = sizeof(*smmu) + sizeof(*idmap); + iort_length += node_size; + smmu = acpi_data_push(table_data, node_size); + + smmu->type = ACPI_IORT_NODE_SMMU_V3; + smmu->length = cpu_to_le16(node_size); + smmu->mapping_count = cpu_to_le32(1); + smmu->mapping_offset = cpu_to_le32(sizeof(*smmu)); + smmu->base_address = cpu_to_le64(vms->memmap[VIRT_SMMU].base); + smmu->event_gsiv = cpu_to_le32(irq); + smmu->pri_gsiv = cpu_to_le32(irq + 1); + smmu->gerr_gsiv = cpu_to_le32(irq + 2); + smmu->sync_gsiv = cpu_to_le32(irq + 3); + + /* Identity RID mapping covering the whole input RID range */ + idmap = &smmu->id_mapping_array[0]; + idmap->input_base = 0; + idmap->id_count = cpu_to_le32(0xFFFF); + idmap->output_base = 0; + /* output IORT node is the ITS group node (the first node) */ + idmap->output_reference = cpu_to_le32(iort->node_offset); + } + /* Root Complex Node */ node_size = sizeof(*rc) + sizeof(*idmap); iort_length += node_size; @@ -438,8 +473,14 @@ build_iort(GArray *table_data, BIOSLinker *linker) idmap->input_base = 0; idmap->id_count = cpu_to_le32(0xFFFF); idmap->output_base = 0; - /* output IORT node is the ITS group node (the first node) */ - idmap->output_reference = cpu_to_le32(iort->node_offset); + + if (vms->iommu == VIRT_IOMMU_SMMUV3) { + /* output IORT node is the smmuv3 node */ + idmap->output_reference = cpu_to_le32(smmu_offset); + } else { + /* output IORT node is the ITS group node (the first node) */ + idmap->output_reference = cpu_to_le32(iort->node_offset); + } iort->length = cpu_to_le32(iort_length); @@ -777,7 +818,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) if (its_class_name() && !vmc->no_its) { acpi_add_table(table_offsets, tables_blob); - build_iort(tables_blob, tables->linker); + build_iort(tables_blob, tables->linker, vms); } /* XSDT is pointed to by RSDP */ diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 5955eb4fc0..af8e023968 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -628,6 +628,21 @@ struct AcpiIortItsGroup { } QEMU_PACKED; typedef struct AcpiIortItsGroup AcpiIortItsGroup; +struct AcpiIortSmmu3 { + ACPI_IORT_NODE_HEADER_DEF + uint64_t base_address; + uint32_t flags; + uint32_t reserved2; + uint64_t vatos_address; + uint32_t model; + uint32_t event_gsiv; + uint32_t pri_gsiv; + uint32_t gerr_gsiv; + uint32_t sync_gsiv; + AcpiIortIdMapping id_mapping_array[0]; +} QEMU_PACKED; +typedef struct AcpiIortSmmu3 AcpiIortSmmu3; + struct AcpiIortRC { ACPI_IORT_NODE_HEADER_DEF AcpiIortMemoryAccess memory_properties; From e24e3454829579eb815ec95d7b3679b0f65845b4 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 4 May 2018 18:05:52 +0100 Subject: [PATCH 0264/2380] hw/arm/virt: Introduce the iommu option ARM virt machine now exposes a new "iommu" option. The SMMUv3 IOMMU is instantiated using -machine virt,iommu=smmuv3. Signed-off-by: Eric Auger Signed-off-by: Prem Mallappa Reviewed-by: Peter Maydell Message-id: 1524665762-31355-15-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index b085f0b9b4..11b9f599ca 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1561,6 +1561,34 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp) } } +static char *virt_get_iommu(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + switch (vms->iommu) { + case VIRT_IOMMU_NONE: + return g_strdup("none"); + case VIRT_IOMMU_SMMUV3: + return g_strdup("smmuv3"); + default: + g_assert_not_reached(); + } +} + +static void virt_set_iommu(Object *obj, const char *value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + if (!strcmp(value, "smmuv3")) { + vms->iommu = VIRT_IOMMU_SMMUV3; + } else if (!strcmp(value, "none")) { + vms->iommu = VIRT_IOMMU_NONE; + } else { + error_setg(errp, "Invalid iommu value"); + error_append_hint(errp, "Valid values are none, smmuv3.\n"); + } +} + static CpuInstanceProperties virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { @@ -1693,6 +1721,14 @@ static void virt_2_12_instance_init(Object *obj) NULL); } + /* Default disallows iommu instantiation */ + vms->iommu = VIRT_IOMMU_NONE; + object_property_add_str(obj, "iommu", virt_get_iommu, virt_set_iommu, NULL); + object_property_set_description(obj, "iommu", + "Set the IOMMU type. " + "Valid values are none and smmuv3", + NULL); + vms->memmap = a15memmap; vms->irqmap = a15irqmap; } From 2a8756ed7d64f8fed6ad50fb062f7118e47c856c Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 14:30:07 +1300 Subject: [PATCH 0265/2380] RISC-V: Replace hardcoded constants with enum values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RISC-V device-tree code has a number of hard-coded constants and this change moves them into header enums. Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Michael Clark Signed-off-by: Palmer Dabbelt Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- hw/riscv/sifive_clint.c | 9 +++------ hw/riscv/sifive_u.c | 6 ++++-- hw/riscv/spike.c | 6 ++++-- hw/riscv/virt.c | 6 ++++-- include/hw/riscv/sifive_clint.h | 4 ++++ include/hw/riscv/sifive_u.h | 4 ++++ include/hw/riscv/spike.h | 4 ++++ include/hw/riscv/virt.h | 4 ++++ 8 files changed, 31 insertions(+), 12 deletions(-) diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c index 4893453b70..7cc606e065 100644 --- a/hw/riscv/sifive_clint.c +++ b/hw/riscv/sifive_clint.c @@ -26,13 +26,10 @@ #include "hw/riscv/sifive_clint.h" #include "qemu/timer.h" -/* See: riscv-pk/machine/sbi_entry.S and arch/riscv/kernel/time.c */ -#define TIMER_FREQ (10 * 1000 * 1000) - static uint64_t cpu_riscv_read_rtc(void) { - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), TIMER_FREQ, - NANOSECONDS_PER_SECOND); + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + SIFIVE_CLINT_TIMEBASE_FREQ, NANOSECONDS_PER_SECOND); } /* @@ -59,7 +56,7 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) diff = cpu->env.timecmp - rtc_r; /* back to ns (note args switched in muldiv64) */ next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(diff, NANOSECONDS_PER_SECOND, TIMER_FREQ); + muldiv64(diff, NANOSECONDS_PER_SECOND, SIFIVE_CLINT_TIMEBASE_FREQ); timer_mod(cpu->env.timer, next); } diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 66616bacd7..1bd2bde9b8 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -122,7 +122,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(nodename); qemu_fdt_add_subnode(fdt, "/cpus"); - qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", + SIFIVE_CLINT_TIMEBASE_FREQ); qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); @@ -131,7 +132,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); char *isa = riscv_isa_string(&s->soc.harts[cpu]); qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + SIFIVE_U_CLOCK_FREQ); qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 62857e4fa0..ae82f4eb63 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -115,7 +115,8 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap, g_free(nodename); qemu_fdt_add_subnode(fdt, "/cpus"); - qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", + SIFIVE_CLINT_TIMEBASE_FREQ); qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); @@ -124,7 +125,8 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap, char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); char *isa = riscv_isa_string(&s->soc.harts[cpu]); qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + SPIKE_CLOCK_FREQ); qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 4f69eb2cff..2480dad11f 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -145,7 +145,8 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, g_free(nodename); qemu_fdt_add_subnode(fdt, "/cpus"); - qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", + SIFIVE_CLINT_TIMEBASE_FREQ); qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); @@ -155,7 +156,8 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); char *isa = riscv_isa_string(&s->soc.harts[cpu]); qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + VIRT_CLOCK_FREQ); qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); diff --git a/include/hw/riscv/sifive_clint.h b/include/hw/riscv/sifive_clint.h index aaa2a58c6e..e2865be1d1 100644 --- a/include/hw/riscv/sifive_clint.h +++ b/include/hw/riscv/sifive_clint.h @@ -47,4 +47,8 @@ enum { SIFIVE_TIME_BASE = 0xBFF8 }; +enum { + SIFIVE_CLINT_TIMEBASE_FREQ = 10000000 +}; + #endif diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index 662e8a1c1a..be38aa09da 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -50,6 +50,10 @@ enum { SIFIVE_U_UART1_IRQ = 4 }; +enum { + SIFIVE_U_CLOCK_FREQ = 1000000000 +}; + #define SIFIVE_U_PLIC_HART_CONFIG "MS" #define SIFIVE_U_PLIC_NUM_SOURCES 127 #define SIFIVE_U_PLIC_NUM_PRIORITIES 7 diff --git a/include/hw/riscv/spike.h b/include/hw/riscv/spike.h index cb55a14d30..d85a64e33d 100644 --- a/include/hw/riscv/spike.h +++ b/include/hw/riscv/spike.h @@ -42,6 +42,10 @@ enum { SPIKE_DRAM }; +enum { + SPIKE_CLOCK_FREQ = 1000000000 +}; + #if defined(TARGET_RISCV32) #define SPIKE_V1_09_1_CPU TYPE_RISCV_CPU_RV32GCSU_V1_09_1 #define SPIKE_V1_10_0_CPU TYPE_RISCV_CPU_RV32GCSU_V1_10_0 diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 7525647e63..2fbe808da5 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -55,6 +55,10 @@ enum { VIRTIO_NDEV = 10 }; +enum { + VIRT_CLOCK_FREQ = 1000000000 +}; + #define VIRT_PLIC_HART_CONFIG "MS" #define VIRT_PLIC_NUM_SOURCES 127 #define VIRT_PLIC_NUM_PRIORITIES 7 From 77ff5bba315d4453ae97ff90ba7698fb1ccc077c Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 3 Mar 2018 16:23:03 +1300 Subject: [PATCH 0266/2380] RISC-V: Make virt board description match spike MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes 'qemu-system-riscv64 -machine help' output more tidy and consistent. Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Michael Clark Signed-off-by: Palmer Dabbelt Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- hw/riscv/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 2480dad11f..df06fc7207 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -407,7 +407,7 @@ static const TypeInfo riscv_virt_board_device = { static void riscv_virt_board_machine_init(MachineClass *mc) { - mc->desc = "RISC-V VirtIO Board (Privileged spec v1.10)"; + mc->desc = "RISC-V VirtIO Board (Privileged ISA v1.10)"; mc->init = riscv_virt_board_init; mc->max_cpus = 8; /* hardcoded limit in BBL */ } From 6b01e3277e0e189a8f064b94c4f761e4efadd758 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sun, 4 Mar 2018 11:15:09 +1300 Subject: [PATCH 0267/2380] RISC-V: Use ROM base address and size from memmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Another case of replacing hard coded constants, this time referring to the definition in the virt machine's memmap. Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Michael Clark Signed-off-by: Palmer Dabbelt Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- hw/riscv/virt.c | 4 ++-- include/hw/riscv/virt.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index df06fc7207..3cc9c8090b 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -341,11 +341,11 @@ static void riscv_virt_board_init(MachineState *machine) }; /* copy in the reset vector */ - copy_le32_to_phys(ROM_BASE, reset_vec, sizeof(reset_vec)); + copy_le32_to_phys(memmap[VIRT_MROM].base, reset_vec, sizeof(reset_vec)); /* copy in the device tree */ qemu_fdt_dumpdtb(s->fdt, s->fdt_size); - cpu_physical_memory_write(ROM_BASE + sizeof(reset_vec), + cpu_physical_memory_write(memmap[VIRT_MROM].base + sizeof(reset_vec), s->fdt, s->fdt_size); /* create PLIC hart topology configuration string */ diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 2fbe808da5..655e85ddbd 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -23,8 +23,6 @@ #define VIRT(obj) \ OBJECT_CHECK(RISCVVirtState, (obj), TYPE_RISCV_VIRT_BOARD) -enum { ROM_BASE = 0x1000 }; - typedef struct { /*< private >*/ SysBusDevice parent_obj; From b7938980fbd3209fd94b17c98c54ec044b762417 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sun, 4 Mar 2018 11:32:17 +1300 Subject: [PATCH 0268/2380] RISC-V: Remove identity_translate from load_elf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When load_elf is called with NULL as an argument to the address translate callback, it does an identity translation. This commit removes the redundant identity_translate callback. Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Michael Clark Signed-off-by: Palmer Dabbelt Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- hw/riscv/sifive_e.c | 7 +------ hw/riscv/sifive_u.c | 7 +------ hw/riscv/spike.c | 7 +------ hw/riscv/virt.c | 7 +------ 4 files changed, 4 insertions(+), 24 deletions(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 487244890e..3e523a0734 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -82,16 +82,11 @@ static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) } } -static uint64_t identity_translate(void *opaque, uint64_t addr) -{ - return addr; -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf(kernel_filename, identity_translate, NULL, + if (load_elf(kernel_filename, NULL, NULL, &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 1bd2bde9b8..adc6c22662 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -68,16 +68,11 @@ static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) } } -static uint64_t identity_translate(void *opaque, uint64_t addr) -{ - return addr; -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf(kernel_filename, identity_translate, NULL, + if (load_elf(kernel_filename, NULL, NULL, &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index ae82f4eb63..cf7f9bcc39 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -59,16 +59,11 @@ static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) } } -static uint64_t identity_translate(void *opaque, uint64_t addr) -{ - return addr; -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf_ram_sym(kernel_filename, identity_translate, NULL, + if (load_elf_ram_sym(kernel_filename, NULL, NULL, &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0, NULL, true, htif_symbol_callback) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 3cc9c8090b..c2aa795981 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -62,16 +62,11 @@ static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) } } -static uint64_t identity_translate(void *opaque, uint64_t addr) -{ - return addr; -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf(kernel_filename, identity_translate, NULL, + if (load_elf(kernel_filename, NULL, NULL, &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); From 42b3a4b7ccbbf419df926939b273fe3b8a6dca1f Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sun, 4 Mar 2018 13:27:37 +1300 Subject: [PATCH 0269/2380] RISC-V: Remove unused class definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes a whole lot of unnecessary boilerplate code. Machines don't need to be objects. The expansion of the SOC object model for the RISC-V machines will happen in the future as SiFive plans to add their FE310 and FU540 SOCs to QEMU. However, it seems that this present boilerplate is complete unnecessary. Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Michael Clark Signed-off-by: Palmer Dabbelt Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- hw/riscv/riscv_hart.c | 6 ------ hw/riscv/sifive_e.c | 25 ------------------------- hw/riscv/sifive_u.c | 25 ------------------------- hw/riscv/spike.c | 20 -------------------- hw/riscv/virt.c | 25 ------------------------- include/hw/riscv/sifive_e.h | 5 ----- include/hw/riscv/sifive_u.h | 5 ----- include/hw/riscv/spike.h | 7 ------- include/hw/riscv/virt.h | 5 ----- 9 files changed, 123 deletions(-) diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index 14e3c186fe..75ba7ed579 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -68,16 +68,10 @@ static void riscv_harts_class_init(ObjectClass *klass, void *data) dc->realize = riscv_harts_realize; } -static void riscv_harts_init(Object *obj) -{ - /* RISCVHartArrayState *s = SIFIVE_COREPLEX(obj); */ -} - static const TypeInfo riscv_harts_info = { .name = TYPE_RISCV_HART_ARRAY, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(RISCVHartArrayState), - .instance_init = riscv_harts_init, .class_init = riscv_harts_class_init, }; diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 3e523a0734..22dc526713 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -194,24 +194,6 @@ static void riscv_sifive_e_init(MachineState *machine) } } -static int riscv_sifive_e_sysbus_device_init(SysBusDevice *sysbusdev) -{ - return 0; -} - -static void riscv_sifive_e_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = riscv_sifive_e_sysbus_device_init; -} - -static const TypeInfo riscv_sifive_e_device = { - .name = TYPE_SIFIVE_E, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SiFiveEState), - .class_init = riscv_sifive_e_class_init, -}; - static void riscv_sifive_e_machine_init(MachineClass *mc) { mc->desc = "RISC-V Board compatible with SiFive E SDK"; @@ -220,10 +202,3 @@ static void riscv_sifive_e_machine_init(MachineClass *mc) } DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init) - -static void riscv_sifive_e_register_types(void) -{ - type_register_static(&riscv_sifive_e_device); -} - -type_init(riscv_sifive_e_register_types); diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index adc6c22662..5bb495ab9a 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -301,31 +301,6 @@ static void riscv_sifive_u_init(MachineState *machine) SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); } -static int riscv_sifive_u_sysbus_device_init(SysBusDevice *sysbusdev) -{ - return 0; -} - -static void riscv_sifive_u_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = riscv_sifive_u_sysbus_device_init; -} - -static const TypeInfo riscv_sifive_u_device = { - .name = TYPE_SIFIVE_U, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SiFiveUState), - .class_init = riscv_sifive_u_class_init, -}; - -static void riscv_sifive_u_register_types(void) -{ - type_register_static(&riscv_sifive_u_device); -} - -type_init(riscv_sifive_u_register_types); - static void riscv_sifive_u_machine_init(MachineClass *mc) { mc->desc = "RISC-V Board compatible with SiFive U SDK"; diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index cf7f9bcc39..44eab94e17 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -334,18 +334,6 @@ static void spike_v1_09_1_board_init(MachineState *machine) smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); } -static const TypeInfo spike_v_1_09_1_device = { - .name = TYPE_RISCV_SPIKE_V1_09_1_BOARD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SpikeState), -}; - -static const TypeInfo spike_v_1_10_0_device = { - .name = TYPE_RISCV_SPIKE_V1_10_0_BOARD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SpikeState), -}; - static void spike_v1_09_1_machine_init(MachineClass *mc) { mc->desc = "RISC-V Spike Board (Privileged ISA v1.9.1)"; @@ -363,11 +351,3 @@ static void spike_v1_10_0_machine_init(MachineClass *mc) DEFINE_MACHINE("spike_v1.9.1", spike_v1_09_1_machine_init) DEFINE_MACHINE("spike_v1.10", spike_v1_10_0_machine_init) - -static void riscv_spike_board_register_types(void) -{ - type_register_static(&spike_v_1_09_1_device); - type_register_static(&spike_v_1_10_0_device); -} - -type_init(riscv_spike_board_register_types); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index c2aa795981..88b9ad5093 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -382,24 +382,6 @@ static void riscv_virt_board_init(MachineState *machine) serial_hd(0), DEVICE_LITTLE_ENDIAN); } -static int riscv_virt_board_sysbus_device_init(SysBusDevice *sysbusdev) -{ - return 0; -} - -static void riscv_virt_board_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = riscv_virt_board_sysbus_device_init; -} - -static const TypeInfo riscv_virt_board_device = { - .name = TYPE_RISCV_VIRT_BOARD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(RISCVVirtState), - .class_init = riscv_virt_board_class_init, -}; - static void riscv_virt_board_machine_init(MachineClass *mc) { mc->desc = "RISC-V VirtIO Board (Privileged ISA v1.10)"; @@ -408,10 +390,3 @@ static void riscv_virt_board_machine_init(MachineClass *mc) } DEFINE_MACHINE("virt", riscv_virt_board_machine_init) - -static void riscv_virt_board_register_types(void) -{ - type_register_static(&riscv_virt_board_device); -} - -type_init(riscv_virt_board_register_types); diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index 0aebc576c1..12ad6d2ebb 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -19,11 +19,6 @@ #ifndef HW_SIFIVE_E_H #define HW_SIFIVE_E_H -#define TYPE_SIFIVE_E "riscv.sifive_e" - -#define SIFIVE_E(obj) \ - OBJECT_CHECK(SiFiveEState, (obj), TYPE_SIFIVE_E) - typedef struct SiFiveEState { /*< private >*/ SysBusDevice parent_obj; diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index be38aa09da..94a390566e 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -19,11 +19,6 @@ #ifndef HW_SIFIVE_U_H #define HW_SIFIVE_U_H -#define TYPE_SIFIVE_U "riscv.sifive_u" - -#define SIFIVE_U(obj) \ - OBJECT_CHECK(SiFiveUState, (obj), TYPE_SIFIVE_U) - typedef struct SiFiveUState { /*< private >*/ SysBusDevice parent_obj; diff --git a/include/hw/riscv/spike.h b/include/hw/riscv/spike.h index d85a64e33d..8410430614 100644 --- a/include/hw/riscv/spike.h +++ b/include/hw/riscv/spike.h @@ -19,12 +19,6 @@ #ifndef HW_SPIKE_H #define HW_SPIKE_H -#define TYPE_RISCV_SPIKE_V1_09_1_BOARD "riscv.spike_v1_9_1" -#define TYPE_RISCV_SPIKE_V1_10_0_BOARD "riscv.spike_v1_10" - -#define SPIKE(obj) \ - OBJECT_CHECK(SpikeState, (obj), TYPE_RISCV_SPIKE_BOARD) - typedef struct { /*< private >*/ SysBusDevice parent_obj; @@ -35,7 +29,6 @@ typedef struct { int fdt_size; } SpikeState; - enum { SPIKE_MROM, SPIKE_CLINT, diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 655e85ddbd..b91a4125dd 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -19,10 +19,6 @@ #ifndef HW_VIRT_H #define HW_VIRT_H -#define TYPE_RISCV_VIRT_BOARD "riscv.virt" -#define VIRT(obj) \ - OBJECT_CHECK(RISCVVirtState, (obj), TYPE_RISCV_VIRT_BOARD) - typedef struct { /*< private >*/ SysBusDevice parent_obj; @@ -45,7 +41,6 @@ enum { VIRT_DRAM }; - enum { UART0_IRQ = 10, VIRTIO_IRQ = 1, /* 1 to 8 */ From 6296a799b14142ccb813b678227ae9e6bf0ffa79 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sun, 4 Mar 2018 13:50:12 +1300 Subject: [PATCH 0270/2380] RISC-V: Include instruction hex in disassembly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was added to help debug issues using -d in_asm. It is useful to see the instruction bytes, as one can detect if one is trying to execute ASCII or device-tree magic. Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Michael Clark Signed-off-by: Palmer Dabbelt Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- disas/riscv.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 74ad16eacd..2cecf0d855 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -2769,25 +2769,6 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) char tmp[64]; const char *fmt; - if (dec->op == rv_op_illegal) { - size_t len = inst_length(dec->inst); - switch (len) { - case 2: - snprintf(buf, buflen, "(0x%04" PRIx64 ")", dec->inst); - break; - case 4: - snprintf(buf, buflen, "(0x%08" PRIx64 ")", dec->inst); - break; - case 6: - snprintf(buf, buflen, "(0x%012" PRIx64 ")", dec->inst); - break; - default: - snprintf(buf, buflen, "(0x%016" PRIx64 ")", dec->inst); - break; - } - return; - } - fmt = opcode_data[dec->op].format; while (*fmt) { switch (*fmt) { @@ -3004,6 +2985,11 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst) format_inst(buf, buflen, 16, &dec); } +#define INST_FMT_2 "%04" PRIx64 " " +#define INST_FMT_4 "%08" PRIx64 " " +#define INST_FMT_6 "%012" PRIx64 " " +#define INST_FMT_8 "%016" PRIx64 " " + static int print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) { @@ -3031,6 +3017,21 @@ print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) } } + switch (len) { + case 2: + (*info->fprintf_func)(info->stream, INST_FMT_2, inst); + break; + case 4: + (*info->fprintf_func)(info->stream, INST_FMT_4, inst); + break; + case 6: + (*info->fprintf_func)(info->stream, INST_FMT_6, inst); + break; + default: + (*info->fprintf_func)(info->stream, INST_FMT_8, inst); + break; + } + disasm_inst(buf, sizeof(buf), isa, memaddr, inst); (*info->fprintf_func)(info->stream, "%s", buf); From 1dc34be1c90b2d3006078d9d331e53a849cdecf3 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 30 Apr 2018 11:06:31 +1200 Subject: [PATCH 0271/2380] RISC-V: Fix missing break statement in disassembler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes an issue when disassembling rv128 c.sqsp, where the code erroneously fell through to c.swsp. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Cc: Peter Maydell Signed-off-by: Michael Clark Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé --- disas/riscv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/disas/riscv.c b/disas/riscv.c index 2cecf0d855..7fd1019623 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1470,8 +1470,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) if (isa == rv128) { op = rv_op_c_sqsp; } else { - op = rv_op_c_fsdsp; break; + op = rv_op_c_fsdsp; } + break; case 6: op = rv_op_c_swsp; break; case 7: if (isa == rv32) { From 4996b128745d93d859b434ff365d4e418bd9095d Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 5 Mar 2018 19:20:53 +1300 Subject: [PATCH 0272/2380] RISC-V: Make some header guards more specific MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Michael Clark Signed-off-by: Palmer Dabbelt Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- include/hw/riscv/spike.h | 4 ++-- include/hw/riscv/virt.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/hw/riscv/spike.h b/include/hw/riscv/spike.h index 8410430614..641b70da67 100644 --- a/include/hw/riscv/spike.h +++ b/include/hw/riscv/spike.h @@ -16,8 +16,8 @@ * this program. If not, see . */ -#ifndef HW_SPIKE_H -#define HW_SPIKE_H +#ifndef HW_RISCV_SPIKE_H +#define HW_RISCV_SPIKE_H typedef struct { /*< private >*/ diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index b91a4125dd..3a4f23e8d0 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -16,8 +16,8 @@ * this program. If not, see . */ -#ifndef HW_VIRT_H -#define HW_VIRT_H +#ifndef HW_RISCV_VIRT_H +#define HW_RISCV_VIRT_H typedef struct { /*< private >*/ From 5b5583806b16ca9ddc454e2a5892b1fea575e470 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 5 Mar 2018 19:24:08 +1300 Subject: [PATCH 0273/2380] RISC-V: Make virt header comment title consistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Michael Clark Signed-off-by: Palmer Dabbelt Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- include/hw/riscv/virt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 3a4f23e8d0..91163d6cbf 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -1,5 +1,5 @@ /* - * SiFive VirtIO Board + * QEMU RISC-V VirtIO machine interface * * Copyright (c) 2017 SiFive, Inc. * From 89854803ce3efb16fbc94604e652f152f5102569 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 5 Mar 2018 20:22:30 +1300 Subject: [PATCH 0274/2380] RISC-V: Remove EM_RISCV ELF_MACHINE indirection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pointless indirection. Other ports use EM_ constants directly. Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Michael Clark Signed-off-by: Palmer Dabbelt Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- hw/riscv/sifive_e.c | 2 +- hw/riscv/sifive_u.c | 2 +- hw/riscv/spike.c | 2 +- hw/riscv/virt.c | 2 +- target/riscv/cpu.h | 1 - 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 22dc526713..6fa2238185 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -88,7 +88,7 @@ static uint64_t load_kernel(const char *kernel_filename) if (load_elf(kernel_filename, NULL, NULL, &kernel_entry, NULL, &kernel_high, - 0, ELF_MACHINE, 1, 0) < 0) { + 0, EM_RISCV, 1, 0) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); exit(1); } diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 5bb495ab9a..84afed4c3b 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -74,7 +74,7 @@ static uint64_t load_kernel(const char *kernel_filename) if (load_elf(kernel_filename, NULL, NULL, &kernel_entry, NULL, &kernel_high, - 0, ELF_MACHINE, 1, 0) < 0) { + 0, EM_RISCV, 1, 0) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); exit(1); } diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 44eab94e17..9e18c618bf 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -64,7 +64,7 @@ static uint64_t load_kernel(const char *kernel_filename) uint64_t kernel_entry, kernel_high; if (load_elf_ram_sym(kernel_filename, NULL, NULL, - &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0, + &kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0, NULL, true, htif_symbol_callback) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); exit(1); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 88b9ad5093..7ef9ba26de 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -68,7 +68,7 @@ static uint64_t load_kernel(const char *kernel_filename) if (load_elf(kernel_filename, NULL, NULL, &kernel_entry, NULL, &kernel_high, - 0, ELF_MACHINE, 1, 0) < 0) { + 0, EM_RISCV, 1, 0) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); exit(1); } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 41e06ac0f9..9871e6feb1 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -34,7 +34,6 @@ #define TCG_GUEST_DEFAULT_MO 0 -#define ELF_MACHINE EM_RISCV #define CPUArchState struct CPURISCVState #include "qemu-common.h" From 8d196c43d7e247edbda7be7b1597ea184f6b498e Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 17 Mar 2018 21:15:40 -0700 Subject: [PATCH 0275/2380] RISC-V: Remove erroneous comment from translate.c Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Palmer Dabbelt Reviewed-by: Alistair Francis --- target/riscv/translate.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 808eab7f50..c3a029afef 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -280,7 +280,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, tcg_gen_andi_tl(source2, source2, 0x1F); tcg_gen_sar_tl(source1, source1, source2); break; - /* fall through to SRA */ #endif case OPC_RISC_SRA: tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); From 79f86934267135080e13e02b52c74371220d8e06 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 5 Mar 2018 13:28:00 +1300 Subject: [PATCH 0276/2380] RISC-V: Update E and I extension order Section 22.8 Subset Naming Convention of the RISC-V ISA Specification defines the canonical order for extensions in the ISA string. It is silent on the position of the E extension however E is a substitute for I so it must come early in the extension list order. A comment is added to state E and I are mutually exclusive, as the E extension will be added to the RISC-V port in the future. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Alistair Francis --- target/riscv/cpu.c | 2 +- target/riscv/cpu.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 5a527fbba0..4e5a56d4e3 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -26,7 +26,7 @@ /* RISC-V CPU definitions */ -static const char riscv_exts[26] = "IMAFDQECLBJTPVNSUHKORWXYZG"; +static const char riscv_exts[26] = "IEMAFDQCLBJTPVNSUHKORWXYZG"; const char * const riscv_int_regnames[] = { "zero", "ra ", "sp ", "gp ", "tp ", "t0 ", "t1 ", "t2 ", diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 9871e6feb1..1dcbdbe6f7 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -71,6 +71,7 @@ #define RV(x) ((target_ulong)1 << (x - 'A')) #define RVI RV('I') +#define RVE RV('E') /* E and I are mutually exclusive */ #define RVM RV('M') #define RVA RV('A') #define RVF RV('F') From 33e3bc8d77b6ce95e622bdc0fce622d35b7ee56c Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 6 Mar 2018 09:48:41 +1300 Subject: [PATCH 0277/2380] RISC-V: Hardwire satp to 0 for no-mmu case satp is WARL so it should not trap on illegal writes, rather it can be hardwired to zero and silently ignore illegal writes. It seems the RISC-V WARL behaviour is preferred to having to trap overhead versus simply reading back the value and checking if the write took (saves hundreds of cycles and more complex trap handling code). Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Alistair Francis --- target/riscv/op_helper.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 7c6068bac9..101dac1ee8 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -255,7 +255,7 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, } case CSR_SATP: /* CSR_SPTBR */ { if (!riscv_feature(env, RISCV_FEATURE_MMU)) { - goto do_illegal; + break; } if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val_to_write ^ env->sptbr)) { @@ -465,7 +465,10 @@ target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) return env->scounteren; case CSR_SCAUSE: return env->scause; - case CSR_SPTBR: + case CSR_SATP: /* CSR_SPTBR */ + if (!riscv_feature(env, RISCV_FEATURE_MMU)) { + return 0; + } if (env->priv_ver >= PRIV_VERSION_1_10_0) { return env->satp; } else { From 67185dad16284467dba9b6159f9ec9ec53689582 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Fri, 16 Mar 2018 12:12:00 -0700 Subject: [PATCH 0278/2380] RISC-V: Clear mtval/stval on exceptions without info mtval/stval must be set on all exceptions but zero is a legal value if there is no exception specific info. Placing the instruction bytes for illegal instruction exceptions in mtval/stval is an optional feature and is currently not supported by QEMU RISC-V. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Alistair Francis --- target/riscv/helper.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/riscv/helper.c b/target/riscv/helper.c index 02cbcea2b7..95889f23b9 100644 --- a/target/riscv/helper.c +++ b/target/riscv/helper.c @@ -466,6 +466,10 @@ void riscv_cpu_do_interrupt(CPUState *cs) ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); } env->sbadaddr = env->badaddr; + } else { + /* otherwise we must clear sbadaddr/stval + * todo: support populating stval on illegal instructions */ + env->sbadaddr = 0; } target_ulong s = env->mstatus; @@ -487,6 +491,10 @@ void riscv_cpu_do_interrupt(CPUState *cs) ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); } env->mbadaddr = env->badaddr; + } else { + /* otherwise we must clear mbadaddr/mtval + * todo: support populating mtval on illegal instructions */ + env->mbadaddr = 0; } target_ulong s = env->mstatus; From e21659057066f2f4d42fa51a62ff07a23a632e40 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 9 Apr 2018 12:06:30 +1200 Subject: [PATCH 0279/2380] RISC-V: Allow S-mode mxr access when priv ISA >= v1.10 The mstatus.MXR alias in sstatus should only be writable by S-mode if the privileged ISA version >= v1.10. Also MXR was masked in sstatus CSR read but not sstatus CSR writes. Now we correctly mask sstatus.mxr in both read and write. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Alistair Francis --- target/riscv/op_helper.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 101dac1ee8..f45ac7306c 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -234,7 +234,10 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, target_ulong ms = env->mstatus; target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS - | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_SD; + | SSTATUS_SUM | SSTATUS_SD; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + mask |= SSTATUS_MXR; + } ms = (ms & ~mask) | (val_to_write & mask); csr_write_helper(env, ms, CSR_MSTATUS); break; @@ -441,7 +444,7 @@ target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) case CSR_SSTATUS: { target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS - | SSTATUS_SUM | SSTATUS_SD; + | SSTATUS_SUM | SSTATUS_SD; if (env->priv_ver >= PRIV_VERSION_1_10_0) { mask |= SSTATUS_MXR; } From 8c59f5c1b5aabbad92871bf62bb302fef017e322 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 9 Apr 2018 11:33:05 +1200 Subject: [PATCH 0280/2380] RISC-V: Use [ms]counteren CSRs when priv ISA >= v1.10 Privileged ISA v1.9.1 defines mscounteren and mucounteren: * mscounteren contains a mask of counters available to S-mode * mucounteren contains a mask of counters available to U-mode Privileged ISA v1.10 defines mcounteren and scounteren: * mcounteren contains a mask of counters available to S-mode * scounteren contains a mask of counters available to U-mode mcounteren and scounteren CSR registers were implemented however they were not honoured for counter accesses when the privilege ISA was >= v1.10. This fix solves the issue by coalescing the counter enable registers. In addition the code now generates illegal instruction exceptions for accesses to the counter enabled registers depending on the privileged ISA version. - Coalesce mscounteren and mcounteren into one variable - Coalesce mucounteren and scounteren into one variable - Makes mcounteren and scounteren CSR accesses generate illegal instructions when the privileged ISA <= v1.9.1 - Makes mscounteren and mucounteren CSR accesses generate illegal instructions when the privileged ISA >= v1.10 Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/cpu.h | 6 ++-- target/riscv/op_helper.c | 62 +++++++++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 1dcbdbe6f7..34abc383e3 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -151,10 +151,8 @@ struct CPURISCVState { target_ulong mcause; target_ulong mtval; /* since: priv-1.10.0 */ - uint32_t mucounteren; - uint32_t mscounteren; - target_ulong scounteren; /* since: priv-1.10.0 */ - target_ulong mcounteren; /* since: priv-1.10.0 */ + target_ulong scounteren; + target_ulong mcounteren; target_ulong sscratch; target_ulong mscratch; diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index f45ac7306c..7416412b18 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -225,11 +225,19 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, qemu_log_mask(LOG_UNIMP, "CSR_MCYCLEH: write not implemented"); goto do_illegal; case CSR_MUCOUNTEREN: - env->mucounteren = val_to_write; - break; + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + env->scounteren = val_to_write; + break; + } else { + goto do_illegal; + } case CSR_MSCOUNTEREN: - env->mscounteren = val_to_write; - break; + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + env->mcounteren = val_to_write; + break; + } else { + goto do_illegal; + } case CSR_SSTATUS: { target_ulong ms = env->mstatus; target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE @@ -286,8 +294,12 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, env->stvec = val_to_write >> 2 << 2; break; case CSR_SCOUNTEREN: - env->scounteren = val_to_write; - break; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + env->scounteren = val_to_write; + break; + } else { + goto do_illegal; + } case CSR_SSCRATCH: env->sscratch = val_to_write; break; @@ -308,8 +320,12 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, env->mtvec = val_to_write >> 2 << 2; break; case CSR_MCOUNTEREN: - env->mcounteren = val_to_write; - break; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + env->mcounteren = val_to_write; + break; + } else { + goto do_illegal; + } case CSR_MSCRATCH: env->mscratch = val_to_write; break; @@ -347,6 +363,8 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, case CSR_PMPADDR15: pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val_to_write); break; +#endif +#if !defined(CONFIG_USER_ONLY) do_illegal: #endif default: @@ -362,8 +380,8 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) { #ifndef CONFIG_USER_ONLY - target_ulong ctr_en = env->priv == PRV_U ? env->mucounteren : - env->priv == PRV_S ? env->mscounteren : -1U; + target_ulong ctr_en = env->priv == PRV_U ? env->scounteren : + env->priv == PRV_S ? env->mcounteren : -1U; #else target_ulong ctr_en = -1; #endif @@ -438,9 +456,17 @@ target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) #endif break; case CSR_MUCOUNTEREN: - return env->mucounteren; + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + return env->scounteren; + } else { + break; /* illegal instruction */ + } case CSR_MSCOUNTEREN: - return env->mscounteren; + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + return env->mcounteren; + } else { + break; /* illegal instruction */ + } case CSR_SSTATUS: { target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS @@ -465,7 +491,11 @@ target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) case CSR_STVEC: return env->stvec; case CSR_SCOUNTEREN: - return env->scounteren; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + return env->scounteren; + } else { + break; /* illegal instruction */ + } case CSR_SCAUSE: return env->scause; case CSR_SATP: /* CSR_SPTBR */ @@ -510,7 +540,11 @@ target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) case CSR_MTVEC: return env->mtvec; case CSR_MCOUNTEREN: - return env->mcounteren; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + return env->mcounteren; + } else { + break; /* illegal instruction */ + } case CSR_MEDELEG: return env->medeleg; case CSR_MIDELEG: From 6fce529c4b3ecbff17bbd930f6beaac9a6067114 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Fri, 6 Apr 2018 12:46:19 +1200 Subject: [PATCH 0281/2380] RISC-V: Add mcycle/minstret support for -icount auto Previously the mycycle/minstret CSRs and rdcycle/rdinstret psuedo instructions would return the time as a proxy for an increasing instruction counter in the absence of having a precise instruction count. If QEMU is invoked with -icount, the mcycle/minstret CSRs and rdcycle/rdinstret psuedo instructions will return the instruction count. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Alistair Francis --- target/riscv/op_helper.c | 28 ++++++++++++++++++++++++++-- target/riscv/translate.c | 2 ++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 7416412b18..3512462f4f 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -434,25 +434,49 @@ target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) case CSR_INSTRET: case CSR_CYCLE: if (ctr_ok) { +#if !defined(CONFIG_USER_ONLY) + if (use_icount) { + return cpu_get_icount(); + } else { + return cpu_get_host_ticks(); + } +#else return cpu_get_host_ticks(); +#endif } break; #if defined(TARGET_RISCV32) case CSR_INSTRETH: case CSR_CYCLEH: if (ctr_ok) { +#if !defined(CONFIG_USER_ONLY) + if (use_icount) { + return cpu_get_icount() >> 32; + } else { + return cpu_get_host_ticks() >> 32; + } +#else return cpu_get_host_ticks() >> 32; +#endif } break; #endif #ifndef CONFIG_USER_ONLY case CSR_MINSTRET: case CSR_MCYCLE: - return cpu_get_host_ticks(); + if (use_icount) { + return cpu_get_icount(); + } else { + return cpu_get_host_ticks(); + } case CSR_MINSTRETH: case CSR_MCYCLEH: #if defined(TARGET_RISCV32) - return cpu_get_host_ticks() >> 32; + if (use_icount) { + return cpu_get_icount() >> 32; + } else { + return cpu_get_host_ticks() >> 32; + } #endif break; case CSR_MUCOUNTEREN: diff --git a/target/riscv/translate.c b/target/riscv/translate.c index c3a029afef..c0e6a044d3 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1390,6 +1390,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, break; default: tcg_gen_movi_tl(imm_rs1, rs1); + gen_io_start(); switch (opc) { case OPC_RISC_CSRRW: gen_helper_csrrw(dest, cpu_env, source1, csr_store); @@ -1413,6 +1414,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_exception_illegal(ctx); return; } + gen_io_end(); gen_set_gpr(rd, dest); /* end tb since we may be changing priv modes, to get mmu_index right */ tcg_gen_movi_tl(cpu_pc, ctx->next_pc); From 1d1ee55274860bfcc511d50d83c84394c2685ba8 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 6 Mar 2018 10:17:11 +1300 Subject: [PATCH 0282/2380] RISC-V: Make mtvec/stvec ignore vectored traps Vectored traps for asynchrounous interrupts are optional. The mtvec/stvec mode field is WARL and hence does not trap if an illegal value is written. Illegal values are ignored. Later we can add RISCV_FEATURE_VECTORED_TRAPS however until then the correct behavior for WARL (Write Any, Read Legal) fields is to drop writes to unsupported bits. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/op_helper.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 3512462f4f..af0c52a484 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -287,11 +287,12 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, env->sepc = val_to_write; break; case CSR_STVEC: - if (val_to_write & 1) { + /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ + if ((val_to_write & 3) == 0) { + env->stvec = val_to_write >> 2 << 2; + } else { qemu_log_mask(LOG_UNIMP, "CSR_STVEC: vectored traps not supported"); - goto do_illegal; } - env->stvec = val_to_write >> 2 << 2; break; case CSR_SCOUNTEREN: if (env->priv_ver >= PRIV_VERSION_1_10_0) { @@ -313,11 +314,12 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, env->mepc = val_to_write; break; case CSR_MTVEC: - if (val_to_write & 1) { + /* bits [1:0] indicate mode; 0 = direct, 1 = vectored, 2 >= reserved */ + if ((val_to_write & 3) == 0) { + env->mtvec = val_to_write >> 2 << 2; + } else { qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: vectored traps not supported"); - goto do_illegal; } - env->mtvec = val_to_write >> 2 << 2; break; case CSR_MCOUNTEREN: if (env->priv_ver >= PRIV_VERSION_1_10_0) { From b8643bd6084be1787a6dc8768a7a1983921fc945 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 6 Mar 2018 10:33:31 +1300 Subject: [PATCH 0283/2380] RISC-V: No traps on writes to misa,minstret,mcycle These fields are marked WARL (Write Any Values, Reads Legal Values) in the RISC-V Privileged Architecture Specification so instead of raising exceptions, illegal writes are silently dropped. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/op_helper.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index af0c52a484..3abf52453c 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -213,17 +213,19 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, break; } case CSR_MINSTRET: - qemu_log_mask(LOG_UNIMP, "CSR_MINSTRET: write not implemented"); - goto do_illegal; + /* minstret is WARL so unsupported writes are ignored */ + break; case CSR_MCYCLE: - qemu_log_mask(LOG_UNIMP, "CSR_MCYCLE: write not implemented"); - goto do_illegal; + /* mcycle is WARL so unsupported writes are ignored */ + break; +#if defined(TARGET_RISCV32) case CSR_MINSTRETH: - qemu_log_mask(LOG_UNIMP, "CSR_MINSTRETH: write not implemented"); - goto do_illegal; + /* minstreth is WARL so unsupported writes are ignored */ + break; case CSR_MCYCLEH: - qemu_log_mask(LOG_UNIMP, "CSR_MCYCLEH: write not implemented"); - goto do_illegal; + /* mcycleh is WARL so unsupported writes are ignored */ + break; +#endif case CSR_MUCOUNTEREN: if (env->priv_ver <= PRIV_VERSION_1_09_1) { env->scounteren = val_to_write; @@ -337,10 +339,9 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, case CSR_MBADADDR: env->mbadaddr = val_to_write; break; - case CSR_MISA: { - qemu_log_mask(LOG_UNIMP, "CSR_MISA: misa writes not supported"); - goto do_illegal; - } + case CSR_MISA: + /* misa is WARL so unsupported writes are ignored */ + break; case CSR_PMPCFG0: case CSR_PMPCFG1: case CSR_PMPCFG2: From 5aec3247c190f10654250203a1742490ae7343a2 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sun, 4 Mar 2018 11:52:13 +1300 Subject: [PATCH 0284/2380] RISC-V: Mark ROM read-only after copying in code The sifive_u machine already marks its ROM readonly however it has the wrong base address for its mask ROM. This patch fixes the sifive_u mask ROM base address. This commit makes all other boards consistently use mask_rom as the variable name for their ROMs. Boards that use device tree now check that that the device tree fits in the assigned ROM space using the new qemu_fdt_totalsize(void *fdt) interface, adding a bounds check and error message. This can detect truncation. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Alistair Francis --- hw/riscv/sifive_e.c | 20 ++++++------- hw/riscv/sifive_u.c | 51 ++++++++++++++++++--------------- hw/riscv/spike.c | 69 +++++++++++++++++++++++++++------------------ hw/riscv/virt.c | 43 +++++++++++++++------------- 4 files changed, 101 insertions(+), 82 deletions(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 6fa2238185..e4ecb7aa4b 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -74,14 +74,6 @@ static const struct MemmapEntry { [SIFIVE_E_DTIM] = { 0x80000000, 0x4000 } }; -static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) -{ - int i; - for (i = 0; i < (len >> 2); i++) { - stl_phys(&address_space_memory, pa + (i << 2), rom[i]); - } -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; @@ -112,6 +104,7 @@ static void riscv_sifive_e_init(MachineState *machine) MemoryRegion *main_mem = g_new(MemoryRegion, 1); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); MemoryRegion *xip_mem = g_new(MemoryRegion, 1); + int i; /* Initialize SOC */ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); @@ -131,7 +124,7 @@ static void riscv_sifive_e_init(MachineState *machine) memmap[SIFIVE_E_DTIM].base, main_mem); /* Mask ROM */ - memory_region_init_ram(mask_rom, NULL, "riscv.sifive.e.mrom", + memory_region_init_rom(mask_rom, NULL, "riscv.sifive.e.mrom", memmap[SIFIVE_E_MROM].size, &error_fatal); memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_MROM].base, mask_rom); @@ -185,9 +178,12 @@ static void riscv_sifive_e_init(MachineState *machine) 0x00028067, /* 0x1004: jr t0 */ }; - /* copy in the reset vector */ - copy_le32_to_phys(memmap[SIFIVE_E_MROM].base, reset_vec, sizeof(reset_vec)); - memory_region_set_readonly(mask_rom, true); + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[SIFIVE_E_MROM].base, &address_space_memory); if (machine->kernel_filename) { load_kernel(machine->kernel_filename); diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 84afed4c3b..c05dcbba95 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -47,12 +47,14 @@ #include "exec/address-spaces.h" #include "elf.h" +#include + static const struct MemmapEntry { hwaddr base; hwaddr size; } sifive_u_memmap[] = { [SIFIVE_U_DEBUG] = { 0x0, 0x100 }, - [SIFIVE_U_MROM] = { 0x1000, 0x2000 }, + [SIFIVE_U_MROM] = { 0x1000, 0x11000 }, [SIFIVE_U_CLINT] = { 0x2000000, 0x10000 }, [SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 }, [SIFIVE_U_UART0] = { 0x10013000, 0x1000 }, @@ -60,14 +62,6 @@ static const struct MemmapEntry { [SIFIVE_U_DRAM] = { 0x80000000, 0x0 }, }; -static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) -{ - int i; - for (i = 0; i < (len >> 2); i++) { - stl_phys(&address_space_memory, pa + (i << 2), rom[i]); - } -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; @@ -221,9 +215,10 @@ static void riscv_sifive_u_init(MachineState *machine) const struct MemmapEntry *memmap = sifive_u_memmap; SiFiveUState *s = g_new0(SiFiveUState, 1); - MemoryRegion *sys_memory = get_system_memory(); + MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + int i; /* Initialize SOC */ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); @@ -239,17 +234,17 @@ static void riscv_sifive_u_init(MachineState *machine) /* register RAM */ memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram", machine->ram_size, &error_fatal); - memory_region_add_subregion(sys_memory, memmap[SIFIVE_U_DRAM].base, + memory_region_add_subregion(system_memory, memmap[SIFIVE_U_DRAM].base, main_mem); /* create device tree */ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); /* boot rom */ - memory_region_init_ram(boot_rom, NULL, "riscv.sifive.u.mrom", - memmap[SIFIVE_U_MROM].base, &error_fatal); - memory_region_set_readonly(boot_rom, true); - memory_region_add_subregion(sys_memory, 0x0, boot_rom); + memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom", + memmap[SIFIVE_U_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base, + mask_rom); if (machine->kernel_filename) { load_kernel(machine->kernel_filename); @@ -272,13 +267,23 @@ static void riscv_sifive_u_init(MachineState *machine) /* dtb: */ }; - /* copy in the reset vector */ - copy_le32_to_phys(memmap[SIFIVE_U_MROM].base, reset_vec, sizeof(reset_vec)); + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[SIFIVE_U_MROM].base, &address_space_memory); /* copy in the device tree */ - qemu_fdt_dumpdtb(s->fdt, s->fdt_size); - cpu_physical_memory_write(memmap[SIFIVE_U_MROM].base + - sizeof(reset_vec), s->fdt, s->fdt_size); + if (fdt_pack(s->fdt) || fdt_totalsize(s->fdt) > + memmap[SIFIVE_U_MROM].size - sizeof(reset_vec)) { + error_report("not enough space to store device-tree"); + exit(1); + } + qemu_fdt_dumpdtb(s->fdt, fdt_totalsize(s->fdt)); + rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt), + memmap[SIFIVE_U_MROM].base + sizeof(reset_vec), + &address_space_memory); /* MMIO */ s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base, @@ -292,9 +297,9 @@ static void riscv_sifive_u_init(MachineState *machine) SIFIVE_U_PLIC_CONTEXT_BASE, SIFIVE_U_PLIC_CONTEXT_STRIDE, memmap[SIFIVE_U_PLIC].size); - sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART0].base, + sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base, serial_hd(0), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]); - /* sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART1].base, + /* sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, serial_hd(1), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */ sifive_clint_create(memmap[SIFIVE_U_CLINT].base, memmap[SIFIVE_U_CLINT].size, smp_cpus, diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 9e18c618bf..f94e2b6707 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -42,23 +42,17 @@ #include "exec/address-spaces.h" #include "elf.h" +#include + static const struct MemmapEntry { hwaddr base; hwaddr size; } spike_memmap[] = { - [SPIKE_MROM] = { 0x1000, 0x2000 }, + [SPIKE_MROM] = { 0x1000, 0x11000 }, [SPIKE_CLINT] = { 0x2000000, 0x10000 }, [SPIKE_DRAM] = { 0x80000000, 0x0 }, }; -static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) -{ - int i; - for (i = 0; i < (len >> 2); i++) { - stl_phys(&address_space_memory, pa + (i << 2), rom[i]); - } -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; @@ -173,7 +167,8 @@ static void spike_v1_10_0_board_init(MachineState *machine) SpikeState *s = g_new0(SpikeState, 1); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + int i; /* Initialize SOC */ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); @@ -196,9 +191,10 @@ static void spike_v1_10_0_board_init(MachineState *machine) create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); /* boot rom */ - memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom", - s->fdt_size + 0x2000, &error_fatal); - memory_region_add_subregion(system_memory, 0x0, boot_rom); + memory_region_init_rom(mask_rom, NULL, "riscv.spike.mrom", + memmap[SPIKE_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[SPIKE_MROM].base, + mask_rom); if (machine->kernel_filename) { load_kernel(machine->kernel_filename); @@ -221,16 +217,26 @@ static void spike_v1_10_0_board_init(MachineState *machine) /* dtb: */ }; - /* copy in the reset vector */ - copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec)); + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[SPIKE_MROM].base, &address_space_memory); /* copy in the device tree */ - qemu_fdt_dumpdtb(s->fdt, s->fdt_size); - cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec), - s->fdt, s->fdt_size); + if (fdt_pack(s->fdt) || fdt_totalsize(s->fdt) > + memmap[SPIKE_MROM].size - sizeof(reset_vec)) { + error_report("not enough space to store device-tree"); + exit(1); + } + qemu_fdt_dumpdtb(s->fdt, fdt_totalsize(s->fdt)); + rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt), + memmap[SPIKE_MROM].base + sizeof(reset_vec), + &address_space_memory); /* initialize HTIF using symbols found in load_kernel */ - htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hd(0)); + htif_mm_init(system_memory, mask_rom, &s->soc.harts[0].env, serial_hd(0)); /* Core Local Interruptor (timer and IPI) */ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, @@ -244,7 +250,8 @@ static void spike_v1_09_1_board_init(MachineState *machine) SpikeState *s = g_new0(SpikeState, 1); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + int i; /* Initialize SOC */ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); @@ -264,9 +271,10 @@ static void spike_v1_09_1_board_init(MachineState *machine) main_mem); /* boot rom */ - memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom", - 0x40000, &error_fatal); - memory_region_add_subregion(system_memory, 0x0, boot_rom); + memory_region_init_rom(mask_rom, NULL, "riscv.spike.mrom", + memmap[SPIKE_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[SPIKE_MROM].base, + mask_rom); if (machine->kernel_filename) { load_kernel(machine->kernel_filename); @@ -319,15 +327,20 @@ static void spike_v1_09_1_board_init(MachineState *machine) g_free(isa); size_t config_string_len = strlen(config_string); - /* copy in the reset vector */ - copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec)); + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[SPIKE_MROM].base, &address_space_memory); /* copy in the config string */ - cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec), - config_string, config_string_len); + rom_add_blob_fixed_as("mrom.reset", config_string, config_string_len, + memmap[SPIKE_MROM].base + sizeof(reset_vec), + &address_space_memory); /* initialize HTIF using symbols found in load_kernel */ - htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hd(0)); + htif_mm_init(system_memory, mask_rom, &s->soc.harts[0].env, serial_hd(0)); /* Core Local Interruptor (timer and IPI) */ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 7ef9ba26de..ad03113e0f 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -40,13 +40,15 @@ #include "exec/address-spaces.h" #include "elf.h" +#include + static const struct MemmapEntry { hwaddr base; hwaddr size; } virt_memmap[] = { [VIRT_DEBUG] = { 0x0, 0x100 }, - [VIRT_MROM] = { 0x1000, 0x2000 }, - [VIRT_TEST] = { 0x4000, 0x1000 }, + [VIRT_MROM] = { 0x1000, 0x11000 }, + [VIRT_TEST] = { 0x100000, 0x1000 }, [VIRT_CLINT] = { 0x2000000, 0x10000 }, [VIRT_PLIC] = { 0xc000000, 0x4000000 }, [VIRT_UART0] = { 0x10000000, 0x100 }, @@ -54,14 +56,6 @@ static const struct MemmapEntry { [VIRT_DRAM] = { 0x80000000, 0x0 }, }; -static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) -{ - int i; - for (i = 0; i < (len >> 2); i++) { - stl_phys(&address_space_memory, pa + (i << 2), rom[i]); - } -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; @@ -272,7 +266,7 @@ static void riscv_virt_board_init(MachineState *machine) RISCVVirtState *s = g_new0(RISCVVirtState, 1); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); char *plic_hart_config; size_t plic_hart_config_len; int i; @@ -299,9 +293,10 @@ static void riscv_virt_board_init(MachineState *machine) fdt = create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); /* boot rom */ - memory_region_init_ram(boot_rom, NULL, "riscv_virt_board.bootrom", - s->fdt_size + 0x2000, &error_fatal); - memory_region_add_subregion(system_memory, 0x0, boot_rom); + memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom", + memmap[VIRT_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, + mask_rom); if (machine->kernel_filename) { uint64_t kernel_entry = load_kernel(machine->kernel_filename); @@ -335,13 +330,23 @@ static void riscv_virt_board_init(MachineState *machine) /* dtb: */ }; - /* copy in the reset vector */ - copy_le32_to_phys(memmap[VIRT_MROM].base, reset_vec, sizeof(reset_vec)); + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[VIRT_MROM].base, &address_space_memory); /* copy in the device tree */ - qemu_fdt_dumpdtb(s->fdt, s->fdt_size); - cpu_physical_memory_write(memmap[VIRT_MROM].base + sizeof(reset_vec), - s->fdt, s->fdt_size); + if (fdt_pack(s->fdt) || fdt_totalsize(s->fdt) > + memmap[VIRT_MROM].size - sizeof(reset_vec)) { + error_report("not enough space to store device-tree"); + exit(1); + } + qemu_fdt_dumpdtb(s->fdt, fdt_totalsize(s->fdt)); + rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt), + memmap[VIRT_MROM].base + sizeof(reset_vec), + &address_space_memory); /* create PLIC hart topology configuration string */ plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus; From 24e8d1faea1e4a2dc59841e048390645d7804cb5 Mon Sep 17 00:00:00 2001 From: Bandan Das Date: Thu, 3 May 2018 15:20:27 -0400 Subject: [PATCH 0285/2380] usb-mtp: Add some NULL checks for issues pointed out by coverity CID 1390578: In usb_mtp_write_metadata, parent can never be NULL but just in case, add an assert CID 1390592: Check for o->format only if o !=NULL CID 1390604: Check s->data_out != NULL in usb_mtp_handle_data Signed-off-by: Bandan Das Message-id: 20180503192028.14353-2-bsd@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/dev-mtp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 6ecf70a79b..24cff640c0 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -1446,8 +1446,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) if (o == NULL) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, c->trans, 0, 0, 0, 0); - } - if (o->format != FMT_ASSOCIATION) { + } else if (o->format != FMT_ASSOCIATION) { usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, c->trans, 0, 0, 0, 0); } @@ -1660,6 +1659,7 @@ static void usb_mtp_write_metadata(MTPState *s) uint32_t next_handle = s->next_handle; assert(!s->write_pending); + assert(p != NULL); utf16_to_str(dataset->length, dataset->filename, filename); @@ -1838,7 +1838,7 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) p->status = USB_RET_STALL; return; } - if (s->data_out && !s->data_out->first) { + if ((s->data_out != NULL) && !s->data_out->first) { container_type = TYPE_DATA; } else { usb_packet_copy(p, &container, sizeof(container)); From 2392ae6bbb0a940a4fd6df29e704b09cadc14790 Mon Sep 17 00:00:00 2001 From: Bandan Das Date: Thu, 3 May 2018 15:20:28 -0400 Subject: [PATCH 0286/2380] usb-mtp: Unconditionally check for the readonly bit Currently, it's only being checked if desc is NULL and so write support breaks upon specifying desc Signed-off-by: Bandan Das Message-id: 20180503192028.14353-3-bsd@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/dev-mtp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 24cff640c0..3d59fe4944 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -1948,16 +1948,17 @@ static void usb_mtp_realize(USBDevice *dev, Error **errp) return; } s->desc = strrchr(s->root, '/'); - /* Mark store as RW */ - if (!s->readonly) { - s->flags |= (1 << MTP_FLAG_WRITABLE); - } if (s->desc && s->desc[0]) { s->desc = g_strdup(s->desc + 1); } else { s->desc = g_strdup("none"); } } + /* Mark store as RW */ + if (!s->readonly) { + s->flags |= (1 << MTP_FLAG_WRITABLE); + } + } static const VMStateDescription vmstate_usb_mtp = { From 3280ea8edede3814553aa19fa27a58daedd48ad9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 3 May 2018 08:29:32 +0200 Subject: [PATCH 0287/2380] usb-host: skip open on pending postload bh usb-host emulates a device unplug after live migration, because the device state is unknown and unplug/replug makes sure the guest re-initializes the device into a working state. This can't be done in post-load though, so post-load just schedules a bottom half which executes after vmload is complete. It can happen that the device autoscan timer hits the race window between scheduling and running the bottom half, which in turn can triggers an assert(). Fix that issue by just ignoring the usb_host_open() call in case the bottom half didn't execute yet. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1572851 Signed-off-by: Gerd Hoffmann Message-id: 20180503062932.17233-1-kraxel@redhat.com --- hw/usb/host-libusb.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index dc0a8fe295..f31e9cbbb8 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -102,6 +102,7 @@ struct USBHostDevice { /* callbacks & friends */ QEMUBH *bh_nodev; QEMUBH *bh_postld; + bool bh_postld_pending; Notifier exit; /* request queues */ @@ -870,6 +871,10 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev) int rc; Error *local_err = NULL; + if (s->bh_postld_pending) { + return -1; + } + trace_usb_host_open_started(bus_num, addr); if (s->dh != NULL) { @@ -1528,6 +1533,7 @@ static void usb_host_post_load_bh(void *opaque) if (udev->attached) { usb_device_detach(udev); } + dev->bh_postld_pending = false; usb_host_auto_check(NULL); } @@ -1539,6 +1545,7 @@ static int usb_host_post_load(void *opaque, int version_id) dev->bh_postld = qemu_bh_new(usb_host_post_load_bh, dev); } qemu_bh_schedule(dev->bh_postld); + dev->bh_postld_pending = true; return 0; } From 5bd5c27c7d284d01477c5cc022ce22438c46bf9f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 27 Apr 2018 13:55:28 +0200 Subject: [PATCH 0288/2380] qxl: fix local renderer crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure we only ask the spice local renderer for display updates in case we have a valid primary surface. Without that spice is confused and throws errors in case a display update request (triggered by screendump for example) happens in parallel to a mode switch and hits the race window where the old primary surface is gone and the new isn't establisted yet. Cc: qemu-stable@nongnu.org Fixes: https://bugzilla.redhat.com//show_bug.cgi?id=1567733 Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Message-id: 20180427115528.345-1-kraxel@redhat.com --- hw/display/qxl-render.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index e7ac4f8789..c62b9a5e75 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -169,7 +169,8 @@ void qxl_render_update(PCIQXLDevice *qxl) qemu_mutex_lock(&qxl->ssd.lock); - if (!runstate_is_running() || !qxl->guest_primary.commands) { + if (!runstate_is_running() || !qxl->guest_primary.commands || + qxl->mode == QXL_MODE_UNDEFINED) { qxl_render_update_area_unlocked(qxl); qemu_mutex_unlock(&qxl->ssd.lock); return; From 2cc0e2e8140f43ccc6aced6e47c9c2db15ce2330 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:16 +0200 Subject: [PATCH 0289/2380] pc-dimm: factor out MemoryDevice interface On the qmp level, we already have the concept of memory devices: "query-memory-devices" Right now, we only support NVDIMM and PCDIMM. We want to map other devices later into the address space of the guest. Such device could e.g. be virtio devices. These devices will have a guest memory range assigned but won't be exposed via e.g. ACPI. We want to make them look like memory device, but not glued to pc-dimm. Especially, it will not always be possible to have TYPE_PC_DIMM as a parent class (e.g. virtio devices). Let's use an interface instead. As a first part, convert handling of - qmp_pc_dimm_device_list - get_plugged_memory_size to our new model. plug/unplug stuff etc. will follow later. A memory device will have to provide the following functions: - get_addr(): Necessary, as the property "addr" can e.g. not be used for virtio devices (already defined). - get_plugged_size(): The amount this device offers to the guest as of now. - get_region_size(): Because this can later on be bigger than the plugged size. - fill_device_info(): Fill MemoryDeviceInfo, e.g. for qmp. Reviewed-by: David Gibson Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-2-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Eduardo Habkost --- hw/i386/acpi-build.c | 3 +- hw/mem/Makefile.objs | 1 + hw/mem/memory-device.c | 120 +++++++++++++++++++ hw/mem/pc-dimm.c | 120 ++++++++++--------- hw/ppc/spapr.c | 3 +- hw/ppc/spapr_hcall.c | 1 + include/hw/mem/memory-device.h | 45 +++++++ include/hw/mem/pc-dimm.h | 2 - numa.c | 3 +- qmp.c | 4 +- stubs/Makefile.objs | 2 +- stubs/{qmp_pc_dimm.c => qmp_memory_device.c} | 4 +- 12 files changed, 242 insertions(+), 66 deletions(-) create mode 100644 hw/mem/memory-device.c create mode 100644 include/hw/mem/memory-device.h rename stubs/{qmp_pc_dimm.c => qmp_memory_device.c} (61%) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index c634dcad1d..624e955e29 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -46,6 +46,7 @@ #include "hw/acpi/vmgenid.h" #include "sysemu/tpm_backend.h" #include "hw/timer/mc146818rtc_regs.h" +#include "hw/mem/memory-device.h" #include "sysemu/numa.h" /* Supported chipsets: */ @@ -2253,7 +2254,7 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog) static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base, uint64_t len, int default_node) { - MemoryDeviceInfoList *info_list = qmp_pc_dimm_device_list(); + MemoryDeviceInfoList *info_list = qmp_memory_device_list(); MemoryDeviceInfoList *info; MemoryDeviceInfo *mi; PCDIMMDeviceInfo *di; diff --git a/hw/mem/Makefile.objs b/hw/mem/Makefile.objs index f12f8b97a2..10be4df2a2 100644 --- a/hw/mem/Makefile.objs +++ b/hw/mem/Makefile.objs @@ -1,2 +1,3 @@ common-obj-$(CONFIG_MEM_HOTPLUG) += pc-dimm.o +common-obj-$(CONFIG_MEM_HOTPLUG) += memory-device.o common-obj-$(CONFIG_NVDIMM) += nvdimm.o diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c new file mode 100644 index 0000000000..6cbdaf99f3 --- /dev/null +++ b/hw/mem/memory-device.c @@ -0,0 +1,120 @@ +/* + * Memory Device Interface + * + * Copyright ProfitBricks GmbH 2012 + * Copyright (C) 2014 Red Hat Inc + * Copyright (c) 2018 Red Hat Inc + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/mem/memory-device.h" +#include "hw/qdev.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "qemu/range.h" + +static gint memory_device_addr_sort(gconstpointer a, gconstpointer b) +{ + const MemoryDeviceState *md_a = MEMORY_DEVICE(a); + const MemoryDeviceState *md_b = MEMORY_DEVICE(b); + const MemoryDeviceClass *mdc_a = MEMORY_DEVICE_GET_CLASS(a); + const MemoryDeviceClass *mdc_b = MEMORY_DEVICE_GET_CLASS(b); + const uint64_t addr_a = mdc_a->get_addr(md_a); + const uint64_t addr_b = mdc_b->get_addr(md_b); + + if (addr_a > addr_b) { + return 1; + } else if (addr_a < addr_b) { + return -1; + } + return 0; +} + +static int memory_device_build_list(Object *obj, void *opaque) +{ + GSList **list = opaque; + + if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) { + DeviceState *dev = DEVICE(obj); + if (dev->realized) { /* only realized memory devices matter */ + *list = g_slist_insert_sorted(*list, dev, memory_device_addr_sort); + } + } + + object_child_foreach(obj, memory_device_build_list, opaque); + return 0; +} + +MemoryDeviceInfoList *qmp_memory_device_list(void) +{ + GSList *devices = NULL, *item; + MemoryDeviceInfoList *list = NULL, *prev = NULL; + + object_child_foreach(qdev_get_machine(), memory_device_build_list, + &devices); + + for (item = devices; item; item = g_slist_next(item)) { + const MemoryDeviceState *md = MEMORY_DEVICE(item->data); + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(item->data); + MemoryDeviceInfoList *elem = g_new0(MemoryDeviceInfoList, 1); + MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1); + + mdc->fill_device_info(md, info); + + elem->value = info; + elem->next = NULL; + if (prev) { + prev->next = elem; + } else { + list = elem; + } + prev = elem; + } + + g_slist_free(devices); + + return list; +} + +static int memory_device_plugged_size(Object *obj, void *opaque) +{ + uint64_t *size = opaque; + + if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) { + const DeviceState *dev = DEVICE(obj); + const MemoryDeviceState *md = MEMORY_DEVICE(obj); + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); + + if (dev->realized) { + *size += mdc->get_plugged_size(md); + } + } + + object_child_foreach(obj, memory_device_plugged_size, opaque); + return 0; +} + +uint64_t get_plugged_memory_size(void) +{ + uint64_t size = 0; + + memory_device_plugged_size(qdev_get_machine(), &size); + + return size; +} + +static const TypeInfo memory_device_info = { + .name = TYPE_MEMORY_DEVICE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(MemoryDeviceClass), +}; + +static void memory_device_register_types(void) +{ + type_register_static(&memory_device_info); +} + +type_init(memory_device_register_types) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 51350d9c2d..ef330628c1 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" +#include "hw/mem/memory-device.h" #include "qapi/error.h" #include "qemu/config-file.h" #include "qapi/visitor.h" @@ -158,11 +159,6 @@ uint64_t pc_existing_dimms_capacity(Error **errp) return cap.size; } -uint64_t get_plugged_memory_size(void) -{ - return pc_existing_dimms_capacity(&error_abort); -} - static int pc_dimm_slot2bitmap(Object *obj, void *opaque) { unsigned long *bitmap = opaque; @@ -238,57 +234,6 @@ static int pc_dimm_built_list(Object *obj, void *opaque) return 0; } -MemoryDeviceInfoList *qmp_pc_dimm_device_list(void) -{ - GSList *dimms = NULL, *item; - MemoryDeviceInfoList *list = NULL, *prev = NULL; - - object_child_foreach(qdev_get_machine(), pc_dimm_built_list, &dimms); - - for (item = dimms; item; item = g_slist_next(item)) { - PCDIMMDevice *dimm = PC_DIMM(item->data); - Object *obj = OBJECT(dimm); - MemoryDeviceInfoList *elem = g_new0(MemoryDeviceInfoList, 1); - MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1); - PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1); - bool is_nvdimm = object_dynamic_cast(obj, TYPE_NVDIMM); - DeviceClass *dc = DEVICE_GET_CLASS(obj); - DeviceState *dev = DEVICE(obj); - - if (dev->id) { - di->has_id = true; - di->id = g_strdup(dev->id); - } - di->hotplugged = dev->hotplugged; - di->hotpluggable = dc->hotpluggable; - di->addr = dimm->addr; - di->slot = dimm->slot; - di->node = dimm->node; - di->size = object_property_get_uint(obj, PC_DIMM_SIZE_PROP, NULL); - di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem)); - - if (!is_nvdimm) { - info->u.dimm.data = di; - info->type = MEMORY_DEVICE_INFO_KIND_DIMM; - } else { - info->u.nvdimm.data = di; - info->type = MEMORY_DEVICE_INFO_KIND_NVDIMM; - } - elem->value = info; - elem->next = NULL; - if (prev) { - prev->next = elem; - } else { - list = elem; - } - prev = elem; - } - - g_slist_free(dimms); - - return list; -} - uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, uint64_t address_space_size, uint64_t *hint, uint64_t align, uint64_t size, @@ -445,10 +390,63 @@ static MemoryRegion *pc_dimm_get_vmstate_memory_region(PCDIMMDevice *dimm) return host_memory_backend_get_memory(dimm->hostmem, &error_abort); } +static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md) +{ + const PCDIMMDevice *dimm = PC_DIMM(md); + + return dimm->addr; +} + +static uint64_t pc_dimm_md_get_region_size(const MemoryDeviceState *md) +{ + /* dropping const here is fine as we don't touch the memory region */ + PCDIMMDevice *dimm = PC_DIMM(md); + const PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(md); + MemoryRegion *mr; + + mr = ddc->get_memory_region(dimm, &error_abort); + if (!mr) { + return 0; + } + + return memory_region_size(mr); +} + +static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md, + MemoryDeviceInfo *info) +{ + PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1); + const DeviceClass *dc = DEVICE_GET_CLASS(md); + const PCDIMMDevice *dimm = PC_DIMM(md); + const DeviceState *dev = DEVICE(md); + + if (dev->id) { + di->has_id = true; + di->id = g_strdup(dev->id); + } + di->hotplugged = dev->hotplugged; + di->hotpluggable = dc->hotpluggable; + di->addr = dimm->addr; + di->slot = dimm->slot; + di->node = dimm->node; + di->size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP, + NULL); + di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem)); + + if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) { + info->u.nvdimm.data = di; + info->type = MEMORY_DEVICE_INFO_KIND_NVDIMM; + } else { + info->u.dimm.data = di; + info->type = MEMORY_DEVICE_INFO_KIND_DIMM; + } +} + static void pc_dimm_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); + MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); dc->realize = pc_dimm_realize; dc->unrealize = pc_dimm_unrealize; @@ -457,6 +455,12 @@ static void pc_dimm_class_init(ObjectClass *oc, void *data) ddc->get_memory_region = pc_dimm_get_memory_region; ddc->get_vmstate_memory_region = pc_dimm_get_vmstate_memory_region; + + mdc->get_addr = pc_dimm_md_get_addr; + /* for a dimm plugged_size == region_size */ + mdc->get_plugged_size = pc_dimm_md_get_region_size; + mdc->get_region_size = pc_dimm_md_get_region_size; + mdc->fill_device_info = pc_dimm_md_fill_device_info; } static TypeInfo pc_dimm_info = { @@ -466,6 +470,10 @@ static TypeInfo pc_dimm_info = { .instance_init = pc_dimm_init, .class_init = pc_dimm_class_init, .class_size = sizeof(PCDIMMDeviceClass), + .interfaces = (InterfaceInfo[]) { + { TYPE_MEMORY_DEVICE }, + { } + }, }; static void pc_dimm_register_types(void) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 32ab3c43b6..640a66a21a 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -74,6 +74,7 @@ #include "hw/compat.h" #include "qemu/cutils.h" #include "hw/ppc/spapr_cpu_core.h" +#include "hw/mem/memory-device.h" #include @@ -887,7 +888,7 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) } /* ibm,dynamic-memory or ibm,dynamic-memory-v2 */ - dimms = qmp_pc_dimm_device_list(); + dimms = qmp_memory_device_list(); if (spapr_ovec_test(spapr->ov5_cas, OV5_DRMEM_V2)) { ret = spapr_populate_drmem_v2(spapr, fdt, offset, dimms); } else { diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index ca9702e667..5f6e6249ba 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -14,6 +14,7 @@ #include "kvm_ppc.h" #include "hw/ppc/spapr_ovec.h" #include "mmu-book3s-v3.h" +#include "hw/mem/memory-device.h" struct LPCRSyncState { target_ulong value; diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h new file mode 100644 index 0000000000..31f64cbab2 --- /dev/null +++ b/include/hw/mem/memory-device.h @@ -0,0 +1,45 @@ +/* + * Memory Device Interface + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef MEMORY_DEVICE_H +#define MEMORY_DEVICE_H + +#include "qom/object.h" +#include "hw/qdev.h" + +#define TYPE_MEMORY_DEVICE "memory-device" + +#define MEMORY_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(MemoryDeviceClass, (klass), TYPE_MEMORY_DEVICE) +#define MEMORY_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(MemoryDeviceClass, (obj), TYPE_MEMORY_DEVICE) +#define MEMORY_DEVICE(obj) \ + INTERFACE_CHECK(MemoryDeviceState, (obj), TYPE_MEMORY_DEVICE) + +typedef struct MemoryDeviceState { + Object parent_obj; +} MemoryDeviceState; + +typedef struct MemoryDeviceClass { + InterfaceClass parent_class; + + uint64_t (*get_addr)(const MemoryDeviceState *md); + uint64_t (*get_plugged_size)(const MemoryDeviceState *md); + uint64_t (*get_region_size)(const MemoryDeviceState *md); + void (*fill_device_info)(const MemoryDeviceState *md, + MemoryDeviceInfo *info); +} MemoryDeviceClass; + +MemoryDeviceInfoList *qmp_memory_device_list(void); +uint64_t get_plugged_memory_size(void); + +#endif diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 1fc479281c..e88073321f 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -93,9 +93,7 @@ uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); -MemoryDeviceInfoList *qmp_pc_dimm_device_list(void); uint64_t pc_existing_dimms_capacity(Error **errp); -uint64_t get_plugged_memory_size(void); void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, MemoryRegion *mr, uint64_t align, Error **errp); void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms, diff --git a/numa.c b/numa.c index 78a869e598..70b150e73a 100644 --- a/numa.c +++ b/numa.c @@ -36,6 +36,7 @@ #include "hw/boards.h" #include "sysemu/hostmem.h" #include "hw/mem/pc-dimm.h" +#include "hw/mem/memory-device.h" #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/cutils.h" @@ -521,7 +522,7 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, static void numa_stat_memory_devices(NumaNodeMem node_mem[]) { - MemoryDeviceInfoList *info_list = qmp_pc_dimm_device_list(); + MemoryDeviceInfoList *info_list = qmp_memory_device_list(); MemoryDeviceInfoList *info; PCDIMMDeviceInfo *pcdimm_info; diff --git a/qmp.c b/qmp.c index 9e95b889ff..25fdc9a5b2 100644 --- a/qmp.c +++ b/qmp.c @@ -39,7 +39,7 @@ #include "qapi/qobject-input-visitor.h" #include "hw/boards.h" #include "qom/object_interfaces.h" -#include "hw/mem/pc-dimm.h" +#include "hw/mem/memory-device.h" #include "hw/acpi/acpi_dev_interface.h" NameInfo *qmp_query_name(Error **errp) @@ -731,7 +731,7 @@ void qmp_object_del(const char *id, Error **errp) MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp) { - return qmp_pc_dimm_device_list(); + return qmp_memory_device_list(); } ACPIOSTInfoList *qmp_query_acpi_ospm_status(Error **errp) diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 2d59d84091..53d3f32cb2 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -34,7 +34,7 @@ stub-obj-y += uuid.o stub-obj-y += vm-stop.o stub-obj-y += vmstate.o stub-obj-$(CONFIG_WIN32) += fd-register.o -stub-obj-y += qmp_pc_dimm.o +stub-obj-y += qmp_memory_device.o stub-obj-y += target-monitor-defs.o stub-obj-y += target-get-monitor-def.o stub-obj-y += pc_madt_cpu_entry.o diff --git a/stubs/qmp_pc_dimm.c b/stubs/qmp_memory_device.c similarity index 61% rename from stubs/qmp_pc_dimm.c rename to stubs/qmp_memory_device.c index b6b2cca89e..85ff8f2d7e 100644 --- a/stubs/qmp_pc_dimm.c +++ b/stubs/qmp_memory_device.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" #include "qom/object.h" -#include "hw/mem/pc-dimm.h" +#include "hw/mem/memory-device.h" -MemoryDeviceInfoList *qmp_pc_dimm_device_list(void) +MemoryDeviceInfoList *qmp_memory_device_list(void) { return NULL; } From b0c14ec4efe912ae6f14a4802574f7b6b6db0648 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:17 +0200 Subject: [PATCH 0290/2380] machine: make MemoryHotplugState accessible via the machine Let's allow to query the MemoryHotplugState directly from the machine. If the pointer is NULL, the machine does not support memory devices. If the pointer is !NULL, the machine supports memory devices and the data structure contains information about the applicable physical guest address space region. This allows us to generically detect if a certain machine has support for memory devices, and to generically manage it (find free address range, plug/unplug a memory region). We will rename "MemoryHotplugState" to something more meaningful ("DeviceMemory") after we completed factoring out the pc-dimm code into MemoryDevice code. Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-3-david@redhat.com> Reviewed-by: Michael S. Tsirkin [ehabkost: rebased series, solved conflicts at spapr.c] [ehabkost: squashed fix to use g_malloc0()] Signed-off-by: Eduardo Habkost --- hw/i386/acpi-build.c | 2 +- hw/i386/pc.c | 35 ++++++++++++++++++-------------- hw/ppc/spapr.c | 43 ++++++++++++++++++++++------------------ hw/ppc/spapr_hcall.c | 2 +- hw/ppc/spapr_rtas_ddw.c | 2 +- include/hw/boards.h | 12 +++++++++++ include/hw/i386/pc.h | 1 - include/hw/mem/pc-dimm.h | 12 +---------- include/hw/ppc/spapr.h | 1 - 9 files changed, 60 insertions(+), 50 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 624e955e29..b1eddeb204 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2411,7 +2411,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) * providing _PXM method if necessary. */ if (hotplugabble_address_space_size) { - build_srat_hotpluggable_memory(table_data, pcms->hotplug_memory.base, + build_srat_hotpluggable_memory(table_data, machine->device_memory->base, hotplugabble_address_space_size, pcms->numa_nodes - 1); } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b297a5d63b..0aa7885798 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1371,6 +1371,9 @@ void pc_memory_init(PCMachineState *pcms, exit(EXIT_FAILURE); } + /* always allocate the device memory information */ + machine->device_memory = g_malloc0(sizeof(*machine->device_memory)); + /* initialize hotplug memory address space */ if (pcmc->has_reserved_memory && (machine->ram_size < machine->maxram_size)) { @@ -1390,7 +1393,7 @@ void pc_memory_init(PCMachineState *pcms, exit(EXIT_FAILURE); } - pcms->hotplug_memory.base = + machine->device_memory->base = ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30); if (pcmc->enforce_aligned_dimm) { @@ -1398,17 +1401,17 @@ void pc_memory_init(PCMachineState *pcms, hotplug_mem_size += (1ULL << 30) * machine->ram_slots; } - if ((pcms->hotplug_memory.base + hotplug_mem_size) < + if ((machine->device_memory->base + hotplug_mem_size) < hotplug_mem_size) { error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT, machine->maxram_size); exit(EXIT_FAILURE); } - memory_region_init(&pcms->hotplug_memory.mr, OBJECT(pcms), + memory_region_init(&machine->device_memory->mr, OBJECT(pcms), "hotplug-memory", hotplug_mem_size); - memory_region_add_subregion(system_memory, pcms->hotplug_memory.base, - &pcms->hotplug_memory.mr); + memory_region_add_subregion(system_memory, machine->device_memory->base, + &machine->device_memory->mr); } /* Initialize PC system firmware */ @@ -1429,13 +1432,13 @@ void pc_memory_init(PCMachineState *pcms, rom_set_fw(fw_cfg); - if (pcmc->has_reserved_memory && pcms->hotplug_memory.base) { + if (pcmc->has_reserved_memory && machine->device_memory->base) { uint64_t *val = g_malloc(sizeof(*val)); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - uint64_t res_mem_end = pcms->hotplug_memory.base; + uint64_t res_mem_end = machine->device_memory->base; if (!pcmc->broken_reserved_end) { - res_mem_end += memory_region_size(&pcms->hotplug_memory.mr); + res_mem_end += memory_region_size(&machine->device_memory->mr); } *val = cpu_to_le64(ROUND_UP(res_mem_end, 0x1ULL << 30)); fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val)); @@ -1462,12 +1465,13 @@ uint64_t pc_pci_hole64_start(void) { PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + MachineState *ms = MACHINE(pcms); uint64_t hole64_start = 0; - if (pcmc->has_reserved_memory && pcms->hotplug_memory.base) { - hole64_start = pcms->hotplug_memory.base; + if (pcmc->has_reserved_memory && ms->device_memory->base) { + hole64_start = ms->device_memory->base; if (!pcmc->broken_reserved_end) { - hole64_start += memory_region_size(&pcms->hotplug_memory.mr); + hole64_start += memory_region_size(&ms->device_memory->mr); } } else { hole64_start = 0x100000000ULL + pcms->above_4g_mem_size; @@ -1711,7 +1715,8 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, goto out; } - pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, &local_err); + pc_dimm_memory_plug(dev, MACHINE(pcms)->device_memory, mr, align, + &local_err); if (local_err) { goto out; } @@ -1779,7 +1784,7 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev, goto out; } - pc_dimm_memory_unplug(dev, &pcms->hotplug_memory, mr); + pc_dimm_memory_unplug(dev, MACHINE(pcms)->device_memory, mr); object_unparent(OBJECT(dev)); out: @@ -2072,8 +2077,8 @@ pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - PCMachineState *pcms = PC_MACHINE(obj); - int64_t value = memory_region_size(&pcms->hotplug_memory.mr); + MachineState *ms = MACHINE(obj); + int64_t value = memory_region_size(&ms->device_memory->mr); visit_type_int(v, name, &value, errp); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 640a66a21a..30d634a8a3 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -703,13 +703,14 @@ spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr, static int spapr_populate_drmem_v2(sPAPRMachineState *spapr, void *fdt, int offset, MemoryDeviceInfoList *dimms) { + MachineState *machine = MACHINE(spapr); uint8_t *int_buf, *cur_index, buf_len; int ret; uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; uint64_t addr, cur_addr, size; - uint32_t nr_boot_lmbs = (spapr->hotplug_memory.base / lmb_size); - uint64_t mem_end = spapr->hotplug_memory.base + - memory_region_size(&spapr->hotplug_memory.mr); + uint32_t nr_boot_lmbs = (machine->device_memory->base / lmb_size); + uint64_t mem_end = machine->device_memory->base + + memory_region_size(&machine->device_memory->mr); uint32_t node, nr_entries = 0; sPAPRDRConnector *drc; DrconfCellQueue *elem, *next; @@ -724,7 +725,7 @@ static int spapr_populate_drmem_v2(sPAPRMachineState *spapr, void *fdt, QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); nr_entries++; - cur_addr = spapr->hotplug_memory.base; + cur_addr = machine->device_memory->base; for (info = dimms; info; info = info->next) { PCDIMMDeviceInfo *di = info->value->u.dimm.data; @@ -787,11 +788,12 @@ static int spapr_populate_drmem_v2(sPAPRMachineState *spapr, void *fdt, static int spapr_populate_drmem_v1(sPAPRMachineState *spapr, void *fdt, int offset, MemoryDeviceInfoList *dimms) { + MachineState *machine = MACHINE(spapr); int i, ret; uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; - uint32_t hotplug_lmb_start = spapr->hotplug_memory.base / lmb_size; - uint32_t nr_lmbs = (spapr->hotplug_memory.base + - memory_region_size(&spapr->hotplug_memory.mr)) / + uint32_t hotplug_lmb_start = machine->device_memory->base / lmb_size; + uint32_t nr_lmbs = (machine->device_memory->base + + memory_region_size(&machine->device_memory->mr)) / lmb_size; uint32_t *int_buf, *cur_index, buf_len; @@ -1034,8 +1036,8 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) GString *hypertas = g_string_sized_new(256); GString *qemu_hypertas = g_string_sized_new(256); uint32_t refpoints[] = { cpu_to_be32(0x4), cpu_to_be32(0x4) }; - uint64_t max_hotplug_addr = spapr->hotplug_memory.base + - memory_region_size(&spapr->hotplug_memory.mr); + uint64_t max_hotplug_addr = MACHINE(spapr)->device_memory->base + + memory_region_size(&MACHINE(spapr)->device_memory->mr); uint32_t lrdr_capacity[] = { cpu_to_be32(max_hotplug_addr >> 32), cpu_to_be32(max_hotplug_addr & 0xffffffff), @@ -2297,7 +2299,7 @@ static void spapr_create_lmb_dr_connectors(sPAPRMachineState *spapr) for (i = 0; i < nr_lmbs; i++) { uint64_t addr; - addr = i * lmb_size + spapr->hotplug_memory.base; + addr = i * lmb_size + machine->device_memory->base; spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_LMB, addr / lmb_size); } @@ -2634,6 +2636,9 @@ static void spapr_machine_init(MachineState *machine) machine->ram_size); memory_region_add_subregion(sysmem, 0, ram); + /* always allocate the device memory information */ + machine->device_memory = g_malloc0(sizeof(*machine->device_memory)); + /* initialize hotplug memory address space */ if (machine->ram_size < machine->maxram_size) { ram_addr_t hotplug_mem_size = machine->maxram_size - machine->ram_size; @@ -2655,12 +2660,12 @@ static void spapr_machine_init(MachineState *machine) exit(1); } - spapr->hotplug_memory.base = ROUND_UP(machine->ram_size, + machine->device_memory->base = ROUND_UP(machine->ram_size, SPAPR_HOTPLUG_MEM_ALIGN); - memory_region_init(&spapr->hotplug_memory.mr, OBJECT(spapr), + memory_region_init(&machine->device_memory->mr, OBJECT(spapr), "hotplug-memory", hotplug_mem_size); - memory_region_add_subregion(sysmem, spapr->hotplug_memory.base, - &spapr->hotplug_memory.mr); + memory_region_add_subregion(sysmem, machine->device_memory->base, + &machine->device_memory->mr); } if (smc->dr_lmb_enabled) { @@ -3148,7 +3153,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, align = memory_region_get_alignment(mr); size = memory_region_size(mr); - pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, &local_err); + pc_dimm_memory_plug(dev, MACHINE(ms)->device_memory, mr, align, &local_err); if (local_err) { goto out; } @@ -3169,7 +3174,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; out_unplug: - pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr); + pc_dimm_memory_unplug(dev, MACHINE(ms)->device_memory, mr); out: error_propagate(errp, local_err); } @@ -3309,7 +3314,7 @@ void spapr_lmb_release(DeviceState *dev) * Now that all the LMBs have been removed by the guest, call the * pc-dimm unplug handler to cleanup up the pc-dimm device. */ - pc_dimm_memory_unplug(dev, &spapr->hotplug_memory, mr); + pc_dimm_memory_unplug(dev, MACHINE(spapr)->device_memory, mr); object_unparent(OBJECT(dev)); spapr_pending_dimm_unplugs_remove(spapr, ds); } @@ -4265,8 +4270,8 @@ static void phb_placement_2_7(sPAPRMachineState *spapr, uint32_t index, /* Can't just use maxram_size, because there may be an * alignment gap between normal and hotpluggable memory * regions */ - ram_top = spapr->hotplug_memory.base + - memory_region_size(&spapr->hotplug_memory.mr); + ram_top = MACHINE(spapr)->device_memory->base + + memory_region_size(&MACHINE(spapr)->device_memory->mr); } phb0_base = QEMU_ALIGN_UP(ram_top, phb0_alignment); diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 5f6e6249ba..52b8f40955 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -67,7 +67,7 @@ static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) static bool is_ram_address(sPAPRMachineState *spapr, hwaddr addr) { MachineState *machine = MACHINE(spapr); - MemoryHotplugState *hpms = &spapr->hotplug_memory; + MemoryHotplugState *hpms = machine->device_memory; if (addr < machine->ram_size) { return true; diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c index 177dcffc9b..d3666c1921 100644 --- a/hw/ppc/spapr_rtas_ddw.c +++ b/hw/ppc/spapr_rtas_ddw.c @@ -122,7 +122,7 @@ static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu, if (machine->ram_size == machine->maxram_size) { max_window_size = machine->ram_size; } else { - MemoryHotplugState *hpms = &spapr->hotplug_memory; + MemoryHotplugState *hpms = machine->device_memory; max_window_size = hpms->base + memory_region_size(&hpms->mr); } diff --git a/include/hw/boards.h b/include/hw/boards.h index 5c5eee55e6..e27c28b514 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -213,6 +213,17 @@ struct MachineClass { int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx); }; +/** + * MemoryHotplugState: + * @base: address in guest physical address space where the memory + * address space for memory devices starts + * @mr: address space container for memory devices + */ +typedef struct MemoryHotplugState { + hwaddr base; + MemoryRegion mr; +} MemoryHotplugState; + /** * MachineState: */ @@ -243,6 +254,7 @@ struct MachineState { bool enforce_config_section; bool enable_graphics; char *memory_encryption; + MemoryHotplugState *device_memory; ram_addr_t ram_size; ram_addr_t maxram_size; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index ffee8413f0..07b596ee76 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -32,7 +32,6 @@ struct PCMachineState { /* */ /* State for other subsystems/APIs: */ - MemoryHotplugState hotplug_memory; Notifier machine_done; /* Pointers to devices and objects: */ diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index e88073321f..8bda37adab 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -19,6 +19,7 @@ #include "exec/memory.h" #include "sysemu/hostmem.h" #include "hw/qdev.h" +#include "hw/boards.h" #define TYPE_PC_DIMM "pc-dimm" #define PC_DIMM(obj) \ @@ -75,17 +76,6 @@ typedef struct PCDIMMDeviceClass { MemoryRegion *(*get_vmstate_memory_region)(PCDIMMDevice *dimm); } PCDIMMDeviceClass; -/** - * MemoryHotplugState: - * @base: address in guest physical address space where hotplug memory - * address space begins. - * @mr: hotplug memory address space container - */ -typedef struct MemoryHotplugState { - hwaddr base; - MemoryRegion mr; -} MemoryHotplugState; - uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, uint64_t address_space_size, uint64_t *hint, uint64_t align, uint64_t size, diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index d60b7c6d7a..56ff02d32a 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -162,7 +162,6 @@ struct sPAPRMachineState { /*< public >*/ char *kvm_type; - MemoryHotplugState hotplug_memory; const char *icp_type; From acc7fa17e6fe96bd68ad9af04fde5091383ef25e Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:18 +0200 Subject: [PATCH 0291/2380] pc-dimm: no need to pass the memory region We can just query it ourselves. When unplugging, we should always be able to the region (as it was previously plugged). E.g. PPC already assumed that and used &error_abort. Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-4-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Eduardo Habkost --- hw/i386/pc.c | 13 ++----------- hw/mem/pc-dimm.c | 12 +++++++++--- hw/ppc/spapr.c | 9 +++------ include/hw/mem/pc-dimm.h | 5 ++--- 4 files changed, 16 insertions(+), 23 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 0aa7885798..e337c6552d 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1715,8 +1715,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, goto out; } - pc_dimm_memory_plug(dev, MACHINE(pcms)->device_memory, mr, align, - &local_err); + pc_dimm_memory_plug(dev, MACHINE(pcms)->device_memory, align, &local_err); if (local_err) { goto out; } @@ -1766,17 +1765,9 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCMachineState *pcms = PC_MACHINE(hotplug_dev); - PCDIMMDevice *dimm = PC_DIMM(dev); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr; HotplugHandlerClass *hhc; Error *local_err = NULL; - mr = ddc->get_memory_region(dimm, &local_err); - if (local_err) { - goto out; - } - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); hhc->unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); @@ -1784,7 +1775,7 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev, goto out; } - pc_dimm_memory_unplug(dev, MACHINE(pcms)->device_memory, mr); + pc_dimm_memory_unplug(dev, MACHINE(pcms)->device_memory); object_unparent(OBJECT(dev)); out: diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index ef330628c1..aeff369f6f 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -37,7 +37,7 @@ typedef struct pc_dimms_capacity { } pc_dimms_capacity; void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr, uint64_t align, Error **errp) + uint64_t align, Error **errp) { int slot; MachineState *machine = MACHINE(qdev_get_machine()); @@ -46,8 +46,14 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); Error *local_err = NULL; uint64_t existing_dimms_capacity = 0; + MemoryRegion *mr; uint64_t addr; + mr = ddc->get_memory_region(dimm, &local_err); + if (local_err) { + goto out; + } + addr = object_property_get_uint(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); if (local_err) { @@ -116,12 +122,12 @@ out: error_propagate(errp, local_err); } -void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr) +void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms) { PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); memory_region_del_subregion(&hpms->mr, mr); vmstate_unregister_ram(vmstate_mr, dev); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 30d634a8a3..d4917e89c6 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3153,7 +3153,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, align = memory_region_get_alignment(mr); size = memory_region_size(mr); - pc_dimm_memory_plug(dev, MACHINE(ms)->device_memory, mr, align, &local_err); + pc_dimm_memory_plug(dev, MACHINE(ms)->device_memory, align, &local_err); if (local_err) { goto out; } @@ -3174,7 +3174,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; out_unplug: - pc_dimm_memory_unplug(dev, MACHINE(ms)->device_memory, mr); + pc_dimm_memory_unplug(dev, MACHINE(ms)->device_memory); out: error_propagate(errp, local_err); } @@ -3292,9 +3292,6 @@ static sPAPRDIMMState *spapr_recover_pending_dimm_state(sPAPRMachineState *ms, void spapr_lmb_release(DeviceState *dev) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_hotplug_handler(dev)); - PCDIMMDevice *dimm = PC_DIMM(dev); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); /* This information will get lost if a migration occurs @@ -3314,7 +3311,7 @@ void spapr_lmb_release(DeviceState *dev) * Now that all the LMBs have been removed by the guest, call the * pc-dimm unplug handler to cleanup up the pc-dimm device. */ - pc_dimm_memory_unplug(dev, MACHINE(spapr)->device_memory, mr); + pc_dimm_memory_unplug(dev, MACHINE(spapr)->device_memory); object_unparent(OBJECT(dev)); spapr_pending_dimm_unplugs_remove(spapr, ds); } diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 8bda37adab..1d26e13cef 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -85,7 +85,6 @@ int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); uint64_t pc_existing_dimms_capacity(Error **errp); void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr, uint64_t align, Error **errp); -void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr); + uint64_t align, Error **errp); +void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms); #endif From bd6c3e4a4975ee1e5cadbc1826af9bd0ca0954c2 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:19 +0200 Subject: [PATCH 0292/2380] pc-dimm: pass in the machine and to the MemoryHotplugState We use the machine internally either way, so let's just pass it in then. Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-5-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Eduardo Habkost --- hw/i386/pc.c | 4 ++-- hw/mem/pc-dimm.c | 8 ++++---- hw/ppc/spapr.c | 6 +++--- include/hw/mem/pc-dimm.h | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index e337c6552d..e94e63dc6c 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1715,7 +1715,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, goto out; } - pc_dimm_memory_plug(dev, MACHINE(pcms)->device_memory, align, &local_err); + pc_dimm_memory_plug(dev, MACHINE(pcms), align, &local_err); if (local_err) { goto out; } @@ -1775,7 +1775,7 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev, goto out; } - pc_dimm_memory_unplug(dev, MACHINE(pcms)->device_memory); + pc_dimm_memory_unplug(dev, MACHINE(pcms)); object_unparent(OBJECT(dev)); out: diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index aeff369f6f..37b8be80a1 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -36,11 +36,11 @@ typedef struct pc_dimms_capacity { Error **errp; } pc_dimms_capacity; -void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, +void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, uint64_t align, Error **errp) { int slot; - MachineState *machine = MACHINE(qdev_get_machine()); + MemoryHotplugState *hpms = machine->device_memory; PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); @@ -122,14 +122,14 @@ out: error_propagate(errp, local_err); } -void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms) +void pc_dimm_memory_unplug(DeviceState *dev, MachineState *machine) { PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); - memory_region_del_subregion(&hpms->mr, mr); + memory_region_del_subregion(&machine->device_memory->mr, mr); vmstate_unregister_ram(vmstate_mr, dev); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d4917e89c6..4576ad63bc 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3153,7 +3153,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, align = memory_region_get_alignment(mr); size = memory_region_size(mr); - pc_dimm_memory_plug(dev, MACHINE(ms)->device_memory, align, &local_err); + pc_dimm_memory_plug(dev, MACHINE(ms), align, &local_err); if (local_err) { goto out; } @@ -3174,7 +3174,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; out_unplug: - pc_dimm_memory_unplug(dev, MACHINE(ms)->device_memory); + pc_dimm_memory_unplug(dev, MACHINE(ms)); out: error_propagate(errp, local_err); } @@ -3311,7 +3311,7 @@ void spapr_lmb_release(DeviceState *dev) * Now that all the LMBs have been removed by the guest, call the * pc-dimm unplug handler to cleanup up the pc-dimm device. */ - pc_dimm_memory_unplug(dev, MACHINE(spapr)->device_memory); + pc_dimm_memory_unplug(dev, MACHINE(spapr)); object_unparent(OBJECT(dev)); spapr_pending_dimm_unplugs_remove(spapr, ds); } diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 1d26e13cef..aa5930fbb6 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -84,7 +84,7 @@ uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); uint64_t pc_existing_dimms_capacity(Error **errp); -void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, +void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, uint64_t align, Error **errp); -void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms); +void pc_dimm_memory_unplug(DeviceState *dev, MachineState *machine); #endif From bb0831bdf45a61c83fa1def44ae391260ce2662d Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:20 +0200 Subject: [PATCH 0293/2380] pc-dimm: factor out address search into MemoryDevice code This mainly moves code, but does a handfull of optimizations: - We pass the machine instead of the address space properties - We check the hinted address directly and handle fragmented memory better - We make the search independent of pc-dimm Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-6-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Eduardo Habkost --- hw/mem/memory-device.c | 86 ++++++++++++++++++++++++++ hw/mem/pc-dimm.c | 109 +-------------------------------- include/hw/mem/memory-device.h | 3 + include/hw/mem/pc-dimm.h | 5 -- 4 files changed, 91 insertions(+), 112 deletions(-) diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index 6cbdaf99f3..a2cb85462f 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -48,6 +48,92 @@ static int memory_device_build_list(Object *obj, void *opaque) return 0; } +uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, + uint64_t align, uint64_t size, + Error **errp) +{ + uint64_t address_space_start, address_space_end; + GSList *list = NULL, *item; + uint64_t new_addr = 0; + + if (!ms->device_memory) { + error_setg(errp, "memory devices (e.g. for memory hotplug) are not " + "supported by the machine"); + return 0; + } + + if (!memory_region_size(&ms->device_memory->mr)) { + error_setg(errp, "memory devices (e.g. for memory hotplug) are not " + "enabled, please specify the maxmem option"); + return 0; + } + address_space_start = ms->device_memory->base; + address_space_end = address_space_start + + memory_region_size(&ms->device_memory->mr); + g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start); + g_assert(address_space_end >= address_space_start); + + if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) { + error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", + align); + return 0; + } + + if (QEMU_ALIGN_UP(size, align) != size) { + error_setg(errp, "backend memory size must be multiple of 0x%" + PRIx64, align); + return 0; + } + + if (hint) { + new_addr = *hint; + if (new_addr < address_space_start) { + error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 + "] at 0x%" PRIx64, new_addr, size, address_space_start); + return 0; + } else if ((new_addr + size) > address_space_end) { + error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 + "] beyond 0x%" PRIx64, new_addr, size, + address_space_end); + return 0; + } + } else { + new_addr = address_space_start; + } + + /* find address range that will fit new memory device */ + object_child_foreach(OBJECT(ms), memory_device_build_list, &list); + for (item = list; item; item = g_slist_next(item)) { + const MemoryDeviceState *md = item->data; + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(OBJECT(md)); + uint64_t md_size, md_addr; + + md_addr = mdc->get_addr(md); + md_size = mdc->get_region_size(md); + if (*errp) { + goto out; + } + + if (ranges_overlap(md_addr, md_size, new_addr, size)) { + if (hint) { + const DeviceState *d = DEVICE(md); + error_setg(errp, "address range conflicts with '%s'", d->id); + goto out; + } + new_addr = QEMU_ALIGN_UP(md_addr + md_size, align); + } + } + + if (new_addr + size > address_space_end) { + error_setg(errp, "could not find position in guest address space for " + "memory device - memory fragmented due to alignments"); + goto out; + } +out: + g_slist_free(list); + return new_addr; +} + MemoryDeviceInfoList *qmp_memory_device_list(void) { GSList *devices = NULL, *item; diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 37b8be80a1..9b70174fd9 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -23,9 +23,7 @@ #include "hw/mem/nvdimm.h" #include "hw/mem/memory-device.h" #include "qapi/error.h" -#include "qemu/config-file.h" #include "qapi/visitor.h" -#include "qemu/range.h" #include "sysemu/numa.h" #include "sysemu/kvm.h" #include "trace.h" @@ -60,10 +58,8 @@ void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, goto out; } - addr = pc_dimm_get_free_addr(hpms->base, - memory_region_size(&hpms->mr), - !addr ? NULL : &addr, align, - memory_region_size(mr), &local_err); + addr = memory_device_get_free_addr(machine, !addr ? NULL : &addr, align, + memory_region_size(mr), &local_err); if (local_err) { goto out; } @@ -211,107 +207,6 @@ out: return slot; } -static gint pc_dimm_addr_sort(gconstpointer a, gconstpointer b) -{ - PCDIMMDevice *x = PC_DIMM(a); - PCDIMMDevice *y = PC_DIMM(b); - Int128 diff = int128_sub(int128_make64(x->addr), int128_make64(y->addr)); - - if (int128_lt(diff, int128_zero())) { - return -1; - } else if (int128_gt(diff, int128_zero())) { - return 1; - } - return 0; -} - -static int pc_dimm_built_list(Object *obj, void *opaque) -{ - GSList **list = opaque; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* only realized DIMMs matter */ - *list = g_slist_insert_sorted(*list, dev, pc_dimm_addr_sort); - } - } - - object_child_foreach(obj, pc_dimm_built_list, opaque); - return 0; -} - -uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, - uint64_t address_space_size, - uint64_t *hint, uint64_t align, uint64_t size, - Error **errp) -{ - GSList *list = NULL, *item; - uint64_t new_addr, ret = 0; - uint64_t address_space_end = address_space_start + address_space_size; - - g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start); - - if (!address_space_size) { - error_setg(errp, "memory hotplug is not enabled, " - "please add maxmem option"); - goto out; - } - - if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) { - error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", - align); - goto out; - } - - if (QEMU_ALIGN_UP(size, align) != size) { - error_setg(errp, "backend memory size must be multiple of 0x%" - PRIx64, align); - goto out; - } - - assert(address_space_end > address_space_start); - object_child_foreach(qdev_get_machine(), pc_dimm_built_list, &list); - - if (hint) { - new_addr = *hint; - } else { - new_addr = address_space_start; - } - - /* find address range that will fit new DIMM */ - for (item = list; item; item = g_slist_next(item)) { - PCDIMMDevice *dimm = item->data; - uint64_t dimm_size = object_property_get_uint(OBJECT(dimm), - PC_DIMM_SIZE_PROP, - errp); - if (errp && *errp) { - goto out; - } - - if (ranges_overlap(dimm->addr, dimm_size, new_addr, size)) { - if (hint) { - DeviceState *d = DEVICE(dimm); - error_setg(errp, "address range conflicts with '%s'", d->id); - goto out; - } - new_addr = QEMU_ALIGN_UP(dimm->addr + dimm_size, align); - } - } - ret = new_addr; - - if (new_addr < address_space_start) { - error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 - "] at 0x%" PRIx64, new_addr, size, address_space_start); - } else if ((new_addr + size) > address_space_end) { - error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 - "] beyond 0x%" PRIx64, new_addr, size, address_space_end); - } - -out: - g_slist_free(list); - return ret; -} - static Property pc_dimm_properties[] = { DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0), DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0), diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h index 31f64cbab2..3427c7d424 100644 --- a/include/hw/mem/memory-device.h +++ b/include/hw/mem/memory-device.h @@ -41,5 +41,8 @@ typedef struct MemoryDeviceClass { MemoryDeviceInfoList *qmp_memory_device_list(void); uint64_t get_plugged_memory_size(void); +uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, + uint64_t align, uint64_t size, + Error **errp); #endif diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index aa5930fbb6..e37fb5d5db 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -76,11 +76,6 @@ typedef struct PCDIMMDeviceClass { MemoryRegion *(*get_vmstate_memory_region)(PCDIMMDevice *dimm); } PCDIMMDeviceClass; -uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, - uint64_t address_space_size, - uint64_t *hint, uint64_t align, uint64_t size, - Error **errp); - int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); uint64_t pc_existing_dimms_capacity(Error **errp); From 1b6d6af21bd614d61b55a28177299a5c93b95cd9 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:21 +0200 Subject: [PATCH 0294/2380] pc-dimm: factor out capacity and slot checks into MemoryDevice Move the checks into memory_device_get_free_addr(). This will check before doing any calculations if we have KVM/vhost slots left and if the total region size would be exceeded. Of course, while at it, make it independent of pc-dimm code. Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-7-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Eduardo Habkost --- hw/mem/memory-device.c | 51 ++++++++++++++++++++++++++++++++++ hw/mem/pc-dimm.c | 60 ---------------------------------------- include/hw/mem/pc-dimm.h | 1 - 3 files changed, 51 insertions(+), 61 deletions(-) diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index a2cb85462f..8535ddcb14 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -15,6 +15,8 @@ #include "qapi/error.h" #include "hw/boards.h" #include "qemu/range.h" +#include "hw/virtio/vhost.h" +#include "sysemu/kvm.h" static gint memory_device_addr_sort(gconstpointer a, gconstpointer b) { @@ -48,6 +50,50 @@ static int memory_device_build_list(Object *obj, void *opaque) return 0; } +static int memory_device_used_region_size(Object *obj, void *opaque) +{ + uint64_t *size = opaque; + + if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) { + const DeviceState *dev = DEVICE(obj); + const MemoryDeviceState *md = MEMORY_DEVICE(obj); + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); + + if (dev->realized) { + *size += mdc->get_region_size(md); + } + } + + object_child_foreach(obj, memory_device_used_region_size, opaque); + return 0; +} + +static void memory_device_check_addable(MachineState *ms, uint64_t size, + Error **errp) +{ + uint64_t used_region_size = 0; + + /* we will need a new memory slot for kvm and vhost */ + if (kvm_enabled() && !kvm_has_free_slot(ms)) { + error_setg(errp, "hypervisor has no free memory slots left"); + return; + } + if (!vhost_has_free_slot()) { + error_setg(errp, "a used vhost backend has no free memory slots left"); + return; + } + + /* will we exceed the total amount of memory specified */ + memory_device_used_region_size(OBJECT(ms), &used_region_size); + if (used_region_size + size > ms->maxram_size - ms->ram_size) { + error_setg(errp, "not enough space, currently 0x%" PRIx64 + " in use of total hot pluggable 0x" RAM_ADDR_FMT, + used_region_size, ms->maxram_size - ms->ram_size); + return; + } + +} + uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, uint64_t align, uint64_t size, Error **errp) @@ -73,6 +119,11 @@ uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start); g_assert(address_space_end >= address_space_start); + memory_device_check_addable(ms, size, errp); + if (*errp) { + return 0; + } + if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) { error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", align); diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 9b70174fd9..8aa2d36ce9 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -25,9 +25,7 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "sysemu/numa.h" -#include "sysemu/kvm.h" #include "trace.h" -#include "hw/virtio/vhost.h" typedef struct pc_dimms_capacity { uint64_t size; @@ -43,7 +41,6 @@ void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); Error *local_err = NULL; - uint64_t existing_dimms_capacity = 0; MemoryRegion *mr; uint64_t addr; @@ -64,20 +61,6 @@ void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, goto out; } - existing_dimms_capacity = pc_existing_dimms_capacity(&local_err); - if (local_err) { - goto out; - } - - if (existing_dimms_capacity + memory_region_size(mr) > - machine->maxram_size - machine->ram_size) { - error_setg(&local_err, "not enough space, currently 0x%" PRIx64 - " in use of total hot pluggable 0x" RAM_ADDR_FMT, - existing_dimms_capacity, - machine->maxram_size - machine->ram_size); - goto out; - } - object_property_set_uint(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); if (local_err) { goto out; @@ -100,17 +83,6 @@ void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, } trace_mhp_pc_dimm_assigned_slot(slot); - if (kvm_enabled() && !kvm_has_free_slot(machine)) { - error_setg(&local_err, "hypervisor has no free memory slots left"); - goto out; - } - - if (!vhost_has_free_slot()) { - error_setg(&local_err, "a used vhost backend has no free" - " memory slots left"); - goto out; - } - memory_region_add_subregion(&hpms->mr, addr - hpms->base, mr); vmstate_register_ram(vmstate_mr, dev); @@ -129,38 +101,6 @@ void pc_dimm_memory_unplug(DeviceState *dev, MachineState *machine) vmstate_unregister_ram(vmstate_mr, dev); } -static int pc_existing_dimms_capacity_internal(Object *obj, void *opaque) -{ - pc_dimms_capacity *cap = opaque; - uint64_t *size = &cap->size; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - DeviceState *dev = DEVICE(obj); - - if (dev->realized) { - (*size) += object_property_get_uint(obj, PC_DIMM_SIZE_PROP, - cap->errp); - } - - if (cap->errp && *cap->errp) { - return 1; - } - } - object_child_foreach(obj, pc_existing_dimms_capacity_internal, opaque); - return 0; -} - -uint64_t pc_existing_dimms_capacity(Error **errp) -{ - pc_dimms_capacity cap; - - cap.size = 0; - cap.errp = errp; - - pc_existing_dimms_capacity_internal(qdev_get_machine(), &cap); - return cap.size; -} - static int pc_dimm_slot2bitmap(Object *obj, void *opaque) { unsigned long *bitmap = opaque; diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index e37fb5d5db..627c8601d9 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -78,7 +78,6 @@ typedef struct PCDIMMDeviceClass { int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); -uint64_t pc_existing_dimms_capacity(Error **errp); void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, uint64_t align, Error **errp); void pc_dimm_memory_unplug(DeviceState *dev, MachineState *machine); From 18d11dc910ca2a292b3f12c6a4a7c927b0f226f4 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:22 +0200 Subject: [PATCH 0295/2380] pc-dimm: move actual plug/unplug of a memory region to MemoryDevice Registering the memory region for migration has do be done by the owner. There could be cases, where we don't want to migrate the memory. Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-8-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Eduardo Habkost --- hw/mem/memory-device.c | 18 ++++++++++++++++++ hw/mem/pc-dimm.c | 5 ++--- include/hw/mem/memory-device.h | 3 +++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index 8535ddcb14..3e04f3954e 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -243,6 +243,24 @@ uint64_t get_plugged_memory_size(void) return size; } +void memory_device_plug_region(MachineState *ms, MemoryRegion *mr, + uint64_t addr) +{ + /* we expect a previous call to memory_device_get_free_addr() */ + g_assert(ms->device_memory); + + memory_region_add_subregion(&ms->device_memory->mr, + addr - ms->device_memory->base, mr); +} + +void memory_device_unplug_region(MachineState *ms, MemoryRegion *mr) +{ + /* we expect a previous call to memory_device_get_free_addr() */ + g_assert(ms->device_memory); + + memory_region_del_subregion(&ms->device_memory->mr, mr); +} + static const TypeInfo memory_device_info = { .name = TYPE_MEMORY_DEVICE, .parent = TYPE_INTERFACE, diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 8aa2d36ce9..0119c68e01 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -36,7 +36,6 @@ void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, uint64_t align, Error **errp) { int slot; - MemoryHotplugState *hpms = machine->device_memory; PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); @@ -83,7 +82,7 @@ void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, } trace_mhp_pc_dimm_assigned_slot(slot); - memory_region_add_subregion(&hpms->mr, addr - hpms->base, mr); + memory_device_plug_region(machine, mr, addr); vmstate_register_ram(vmstate_mr, dev); out: @@ -97,7 +96,7 @@ void pc_dimm_memory_unplug(DeviceState *dev, MachineState *machine) MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); - memory_region_del_subregion(&machine->device_memory->mr, mr); + memory_device_unplug_region(machine, mr); vmstate_unregister_ram(vmstate_mr, dev); } diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h index 3427c7d424..2853b084b5 100644 --- a/include/hw/mem/memory-device.h +++ b/include/hw/mem/memory-device.h @@ -44,5 +44,8 @@ uint64_t get_plugged_memory_size(void); uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, uint64_t align, uint64_t size, Error **errp); +void memory_device_plug_region(MachineState *ms, MemoryRegion *mr, + uint64_t addr); +void memory_device_unplug_region(MachineState *ms, MemoryRegion *mr); #endif From e017da370b3e87a3041b66092de2eb2318d28a6b Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:23 +0200 Subject: [PATCH 0296/2380] machine: rename MemoryHotplugState to DeviceMemoryState Rename it to better match the new terminology. Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-9-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Eduardo Habkost --- hw/ppc/spapr_hcall.c | 6 +++--- hw/ppc/spapr_rtas_ddw.c | 5 ++--- include/hw/boards.h | 8 ++++---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 52b8f40955..022f6d8101 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -67,13 +67,13 @@ static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) static bool is_ram_address(sPAPRMachineState *spapr, hwaddr addr) { MachineState *machine = MACHINE(spapr); - MemoryHotplugState *hpms = machine->device_memory; + DeviceMemoryState *dms = machine->device_memory; if (addr < machine->ram_size) { return true; } - if ((addr >= hpms->base) - && ((addr - hpms->base) < memory_region_size(&hpms->mr))) { + if ((addr >= dms->base) + && ((addr - dms->base) < memory_region_size(&dms->mr))) { return true; } diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c index d3666c1921..329feb148f 100644 --- a/hw/ppc/spapr_rtas_ddw.c +++ b/hw/ppc/spapr_rtas_ddw.c @@ -122,9 +122,8 @@ static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu, if (machine->ram_size == machine->maxram_size) { max_window_size = machine->ram_size; } else { - MemoryHotplugState *hpms = machine->device_memory; - - max_window_size = hpms->base + memory_region_size(&hpms->mr); + max_window_size = machine->device_memory->base + + memory_region_size(&machine->device_memory->mr); } avail = SPAPR_PCI_DMA_MAX_WINDOWS - spapr_phb_get_active_win_num(sphb); diff --git a/include/hw/boards.h b/include/hw/boards.h index e27c28b514..ef7457f5dd 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -214,15 +214,15 @@ struct MachineClass { }; /** - * MemoryHotplugState: + * DeviceMemoryState: * @base: address in guest physical address space where the memory * address space for memory devices starts * @mr: address space container for memory devices */ -typedef struct MemoryHotplugState { +typedef struct DeviceMemoryState { hwaddr base; MemoryRegion mr; -} MemoryHotplugState; +} DeviceMemoryState; /** * MachineState: @@ -254,7 +254,7 @@ struct MachineState { bool enforce_config_section; bool enable_graphics; char *memory_encryption; - MemoryHotplugState *device_memory; + DeviceMemoryState *device_memory; ram_addr_t ram_size; ram_addr_t maxram_size; From f2ffbe2b7dd05a563fe81066440629824192b15a Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:24 +0200 Subject: [PATCH 0297/2380] pc: rename "hotplug memory" terminology to "device memory" Let's make it clear that we are dealing with device memory. That it can be used for memory hotplug is just a special case. Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-10-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Eduardo Habkost --- hw/i386/acpi-build.c | 2 +- hw/i386/pc.c | 25 ++++++++++++------------- include/hw/i386/pc.h | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index b1eddeb204..9bc6d97ea1 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2313,7 +2313,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine); PCMachineState *pcms = PC_MACHINE(machine); ram_addr_t hotplugabble_address_space_size = - object_property_get_int(OBJECT(pcms), PC_MACHINE_MEMHP_REGION_SIZE, + object_property_get_int(OBJECT(pcms), PC_MACHINE_DEVMEM_REGION_SIZE, NULL); srat_start = table_data->len; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index e94e63dc6c..868893d0a1 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1374,11 +1374,10 @@ void pc_memory_init(PCMachineState *pcms, /* always allocate the device memory information */ machine->device_memory = g_malloc0(sizeof(*machine->device_memory)); - /* initialize hotplug memory address space */ + /* initialize device memory address space */ if (pcmc->has_reserved_memory && (machine->ram_size < machine->maxram_size)) { - ram_addr_t hotplug_mem_size = - machine->maxram_size - machine->ram_size; + ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { error_report("unsupported amount of memory slots: %"PRIu64, @@ -1397,19 +1396,19 @@ void pc_memory_init(PCMachineState *pcms, ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30); if (pcmc->enforce_aligned_dimm) { - /* size hotplug region assuming 1G page max alignment per slot */ - hotplug_mem_size += (1ULL << 30) * machine->ram_slots; + /* size device region assuming 1G page max alignment per slot */ + device_mem_size += (1ULL << 30) * machine->ram_slots; } - if ((machine->device_memory->base + hotplug_mem_size) < - hotplug_mem_size) { + if ((machine->device_memory->base + device_mem_size) < + device_mem_size) { error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT, machine->maxram_size); exit(EXIT_FAILURE); } memory_region_init(&machine->device_memory->mr, OBJECT(pcms), - "hotplug-memory", hotplug_mem_size); + "device-memory", device_mem_size); memory_region_add_subregion(system_memory, machine->device_memory->base, &machine->device_memory->mr); } @@ -2064,9 +2063,9 @@ static HotplugHandler *pc_get_hotpug_handler(MachineState *machine, } static void -pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) +pc_machine_get_device_memory_region_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { MachineState *ms = MACHINE(obj); int64_t value = memory_region_size(&ms->device_memory->mr); @@ -2373,8 +2372,8 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) nc->nmi_monitor_handler = x86_nmi; mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; - object_class_property_add(oc, PC_MACHINE_MEMHP_REGION_SIZE, "int", - pc_machine_get_hotplug_memory_region_size, NULL, + object_class_property_add(oc, PC_MACHINE_DEVMEM_REGION_SIZE, "int", + pc_machine_get_device_memory_region_size, NULL, NULL, NULL, &error_abort); object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size", diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 07b596ee76..2e834e6ded 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -71,7 +71,7 @@ struct PCMachineState { }; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" -#define PC_MACHINE_MEMHP_REGION_SIZE "hotplug-memory-region-size" +#define PC_MACHINE_DEVMEM_REGION_SIZE "device-memory-region-size" #define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g" #define PC_MACHINE_VMPORT "vmport" #define PC_MACHINE_SMM "smm" From 0c9269a52d79aeebcfade97778ee937ab480a232 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:25 +0200 Subject: [PATCH 0298/2380] spapr: rename "hotplug memory" terminology to "device memory" Let's make it clear at relevant places that we are dealing with device memory. That it can be used for memory hotplug is just a special case. Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-11-david@redhat.com> Reviewed-by: Michael S. Tsirkin [ehabkost: rebased series, solved conflicts at spapr.c] Signed-off-by: Eduardo Habkost --- hw/ppc/spapr.c | 26 +++++++++++++------------- include/hw/ppc/spapr.h | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 4576ad63bc..a1abcba6ad 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -791,7 +791,7 @@ static int spapr_populate_drmem_v1(sPAPRMachineState *spapr, void *fdt, MachineState *machine = MACHINE(spapr); int i, ret; uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; - uint32_t hotplug_lmb_start = machine->device_memory->base / lmb_size; + uint32_t device_lmb_start = machine->device_memory->base / lmb_size; uint32_t nr_lmbs = (machine->device_memory->base + memory_region_size(&machine->device_memory->mr)) / lmb_size; @@ -808,7 +808,7 @@ static int spapr_populate_drmem_v1(sPAPRMachineState *spapr, void *fdt, uint64_t addr = i * lmb_size; uint32_t *dynamic_memory = cur_index; - if (i >= hotplug_lmb_start) { + if (i >= device_lmb_start) { sPAPRDRConnector *drc; drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, i); @@ -827,7 +827,7 @@ static int spapr_populate_drmem_v1(sPAPRMachineState *spapr, void *fdt, } else { /* * LMB information for RMA, boot time RAM and gap b/n RAM and - * hotplug memory region -- all these are marked as reserved + * device memory region -- all these are marked as reserved * and as having no valid DRC. */ dynamic_memory[0] = cpu_to_be32(addr >> 32); @@ -865,7 +865,7 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) MemoryDeviceInfoList *dimms = NULL; /* - * Don't create the node if there is no hotpluggable memory + * Don't create the node if there is no device memory */ if (machine->ram_size == machine->maxram_size) { return 0; @@ -1036,11 +1036,11 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) GString *hypertas = g_string_sized_new(256); GString *qemu_hypertas = g_string_sized_new(256); uint32_t refpoints[] = { cpu_to_be32(0x4), cpu_to_be32(0x4) }; - uint64_t max_hotplug_addr = MACHINE(spapr)->device_memory->base + + uint64_t max_device_addr = MACHINE(spapr)->device_memory->base + memory_region_size(&MACHINE(spapr)->device_memory->mr); uint32_t lrdr_capacity[] = { - cpu_to_be32(max_hotplug_addr >> 32), - cpu_to_be32(max_hotplug_addr & 0xffffffff), + cpu_to_be32(max_device_addr >> 32), + cpu_to_be32(max_device_addr & 0xffffffff), 0, cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE), cpu_to_be32(max_cpus / smp_threads), }; @@ -2641,7 +2641,7 @@ static void spapr_machine_init(MachineState *machine) /* initialize hotplug memory address space */ if (machine->ram_size < machine->maxram_size) { - ram_addr_t hotplug_mem_size = machine->maxram_size - machine->ram_size; + ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; /* * Limit the number of hotpluggable memory slots to half the number * slots that KVM supports, leaving the other half for PCI and other @@ -2661,9 +2661,9 @@ static void spapr_machine_init(MachineState *machine) } machine->device_memory->base = ROUND_UP(machine->ram_size, - SPAPR_HOTPLUG_MEM_ALIGN); + SPAPR_DEVICE_MEM_ALIGN); memory_region_init(&machine->device_memory->mr, OBJECT(spapr), - "hotplug-memory", hotplug_mem_size); + "device-memory", device_mem_size); memory_region_add_subregion(sysmem, machine->device_memory->base, &machine->device_memory->mr); } @@ -4262,11 +4262,11 @@ static void phb_placement_2_7(sPAPRMachineState *spapr, uint32_t index, hwaddr phb0_base, phb_base; int i; - /* Do we have hotpluggable memory? */ + /* Do we have device memory? */ if (MACHINE(spapr)->maxram_size > ram_top) { /* Can't just use maxram_size, because there may be an - * alignment gap between normal and hotpluggable memory - * regions */ + * alignment gap between normal and device memory regions + */ ram_top = MACHINE(spapr)->device_memory->base + memory_region_size(&MACHINE(spapr)->device_memory->mr); } diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 56ff02d32a..3388750fc7 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -747,8 +747,8 @@ int spapr_rng_populate_dt(void *fdt); */ #define SPAPR_MAX_RAM_SLOTS 32 -/* 1GB alignment for hotplug memory region */ -#define SPAPR_HOTPLUG_MEM_ALIGN (1ULL << 30) +/* 1GB alignment for device memory region */ +#define SPAPR_DEVICE_MEM_ALIGN (1ULL << 30) /* * Number of 32 bit words in each LMB list entry in ibm,dynamic-memory From 951f2269af2d1f559d3949c45659c65297946106 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Apr 2018 18:51:26 +0200 Subject: [PATCH 0299/2380] vl: allow 'maxmem' without 'slot' We will be able to have memory devices (e.g. virtio) not requiring the slot parameter (e.g. not exposed via ACPI). We still need the maxmem parameter to setup a proper memory region for device memory. And some architectures (e.g. s390x) will have to set up the maximum possible guest address space size based on the maxmem parameter. As far as I can see, all code (pc.c,spapr.c,ACPI code) should handle !slots just fine, even though maxmem is set. Signed-off-by: David Hildenbrand Message-Id: <20180423165126.15441-12-david@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Eduardo Habkost --- vl.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/vl.c b/vl.c index 806eec2ef6..12e31d1aa9 100644 --- a/vl.c +++ b/vl.c @@ -2868,7 +2868,6 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, { uint64_t sz; const char *mem_str; - const char *maxmem_str, *slots_str; const ram_addr_t default_ram_size = mc->default_ram_size; QemuOpts *opts = qemu_find_opts_singleton("memory"); Location loc; @@ -2914,9 +2913,7 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, qemu_opt_set_number(opts, "size", ram_size, &error_abort); *maxram_size = ram_size; - maxmem_str = qemu_opt_get(opts, "maxmem"); - slots_str = qemu_opt_get(opts, "slots"); - if (maxmem_str && slots_str) { + if (qemu_opt_get(opts, "maxmem")) { uint64_t slots; sz = qemu_opt_get_size(opts, "maxmem", 0); @@ -2927,13 +2924,7 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, "the initial memory size (0x" RAM_ADDR_FMT ")", sz, ram_size); exit(EXIT_FAILURE); - } else if (sz > ram_size) { - if (!slots) { - error_report("invalid value of -m option: maxmem was " - "specified, but no hotplug slots were specified"); - exit(EXIT_FAILURE); - } - } else if (slots) { + } else if (slots && sz == ram_size) { error_report("invalid value of -m option maxmem: " "memory slots were specified but maximum memory size " "(0x%" PRIx64 ") is equal to the initial memory size " @@ -2943,10 +2934,8 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, *maxram_size = sz; *ram_slots = slots; - } else if ((!maxmem_str && slots_str) || - (maxmem_str && !slots_str)) { - error_report("invalid -m option value: missing " - "'%s' option", slots_str ? "maxmem" : "slots"); + } else if (qemu_opt_get(opts, "slots")) { + error_report("invalid -m option value: missing 'maxmem' option"); exit(EXIT_FAILURE); } From b40dffdec60c2dbe54806576faa5cb85227586d1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Apr 2018 19:41:29 +0200 Subject: [PATCH 0300/2380] scripts/device-crash-test: Removed fixed CAN entries The CAN device crashes have been fixed with the commit 089eac81e1d34d202471c0a023284f47f4c5f00e already. Signed-off-by: Thomas Huth Message-Id: <1523900489-25950-1-git-send-email-thuth@redhat.com> Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Eduardo Habkost --- scripts/device-crash-test | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/device-crash-test b/scripts/device-crash-test index 5d17dc68dd..b3ce72069f 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -218,9 +218,6 @@ ERROR_WHITELIST = [ {'exitcode':-6, 'log':r"Object .* is not an instance of type e500-ccsr", 'loglevel':logging.ERROR}, {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR}, {'exitcode':-11, 'device':'isa-serial', 'loglevel':logging.ERROR, 'expected':True}, - {'exitcode':-11, 'device':'mioe3680_pci', 'loglevel':logging.ERROR, 'expected':True}, - {'exitcode':-11, 'device':'pcm3680_pci', 'loglevel':logging.ERROR, 'expected':True}, - {'exitcode':-11, 'device':'kvaser_pci', 'loglevel':logging.ERROR, 'expected':True}, # everything else (including SIGABRT and SIGSEGV) will be a fatal error: {'exitcode':None, 'fatal':True, 'loglevel':logging.FATAL}, From 2fb513d3b1e3472ff02dc00e213db65bc56506cd Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Mon, 7 May 2018 11:02:09 +0200 Subject: [PATCH 0301/2380] ppc: e500: use g_strdup_printf() instead of snprintf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu-system-ppc fails to build with GCC 8.0.1: /home/hsp/src/qemu-master/hw/ppc/e500.c: In function ‘ppce500_load_device_tree’: /home/hsp/src/qemu-master/hw/ppc/e500.c:442:37: error: ‘/pic@’ directive output may be truncated writing 5 bytes into a region of size between 1 and 128 [-Werror=format-truncation=] snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); ^~~~~ In file included from /usr/include/stdio.h:862, from /home/hsp/src/qemu-master/include/qemu/osdep.h:68, from /home/hsp/src/qemu-master/hw/ppc/e500.c:17: /usr/include/bits/stdio2.h:64:10: note: ‘__builtin___snprintf_chk’ output between 11 and 138 bytes into a destination of size 128 return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __bos (__s), __fmt, __va_arg_pack ()); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/hsp/src/qemu-master/hw/ppc/e500.c:470:39: error: ‘/global-utilities@’ directive output may be truncated writing 18 bytes into a region of size between 1 and 128 [-Werror=format-truncation=] snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, ^~~~~~~~~~~~~~~~~~ In file included from /usr/include/stdio.h:862, from /home/hsp/src/qemu-master/include/qemu/osdep.h:68, from /home/hsp/src/qemu-master/hw/ppc/e500.c:17: /usr/include/bits/stdio2.h:64:10: note: ‘__builtin___snprintf_chk’ output between 24 and 151 bytes into a destination of size 128 return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __bos (__s), __fmt, __va_arg_pack ()); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/hsp/src/qemu-master/hw/ppc/e500.c:477:36: error: ‘/msi@’ directive output may be truncated writing 5 bytes into a region of size between 0 and 127 [-Werror=format-truncation=] snprintf(msi, sizeof(msi), "/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET); ^~~~~ In file included from /usr/include/stdio.h:862, from /home/hsp/src/qemu-master/include/qemu/osdep.h:68, from /home/hsp/src/qemu-master/hw/ppc/e500.c:17: /usr/include/bits/stdio2.h:64:10: note: ‘__builtin___snprintf_chk’ output between 12 and 139 bytes into a destination of size 128 return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __bos (__s), __fmt, __va_arg_pack ()); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fix this by converting e500 to use g_strdup_printf()+g_free() instead of snprintf(). This is done globally, even for call sites that don't break build, since this is the preferred practice in QEMU. Reported-by: Howard Spoelstra Signed-off-by: Greg Kurz Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eric Blake Message-id: 152568372989.443627.900708381919207053.stgit@bahia.lan Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell --- hw/ppc/e500.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 3e0923cfba..748a8d213b 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -106,9 +106,9 @@ static void dt_serial_create(void *fdt, unsigned long long offset, const char *soc, const char *mpic, const char *alias, int idx, bool defcon) { - char ser[128]; + char *ser; - snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset); + ser = g_strdup_printf("%s/serial@%llx", soc, offset); qemu_fdt_add_subnode(fdt, ser); qemu_fdt_setprop_string(fdt, ser, "device_type", "serial"); qemu_fdt_setprop_string(fdt, ser, "compatible", "ns16550"); @@ -129,6 +129,7 @@ static void dt_serial_create(void *fdt, unsigned long long offset, qemu_fdt_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", ser); } + g_free(ser); } static void create_dt_mpc8xxx_gpio(void *fdt, const char *soc, const char *mpic) @@ -285,13 +286,13 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, uint32_t tb_freq = 400000000; int i; char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; - char soc[128]; - char mpic[128]; + char *soc; + char *mpic; uint32_t mpic_ph; uint32_t msi_ph; - char gutil[128]; - char pci[128]; - char msi[128]; + char *gutil; + char *pci; + char *msi; uint32_t *pci_map = NULL; int len; uint32_t pci_ranges[14] = @@ -391,7 +392,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { CPUState *cpu; - char cpu_name[128]; + char *cpu_name; uint64_t cpu_release_addr = pmc->spin_base + (i * 0x20); cpu = qemu_get_cpu(i); @@ -400,7 +401,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, } env = cpu->env_ptr; - snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", i); + cpu_name = g_strdup_printf("/cpus/PowerPC,8544@%x", i); qemu_fdt_add_subnode(fdt, cpu_name); qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); @@ -422,11 +423,12 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, } else { qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); } + g_free(cpu_name); } qemu_fdt_add_subnode(fdt, "/aliases"); /* XXX These should go into their respective devices' code */ - snprintf(soc, sizeof(soc), "/soc@%"PRIx64, pmc->ccsrbar_base); + soc = g_strdup_printf("/soc@%"PRIx64, pmc->ccsrbar_base); qemu_fdt_add_subnode(fdt, soc); qemu_fdt_setprop_string(fdt, soc, "device_type", "soc"); qemu_fdt_setprop(fdt, soc, "compatible", compatible_sb, @@ -439,7 +441,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, /* XXX should contain a reasonable value */ qemu_fdt_setprop_cell(fdt, soc, "bus-frequency", 0); - snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); + mpic = g_strdup_printf("%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); qemu_fdt_add_subnode(fdt, mpic); qemu_fdt_setprop_string(fdt, mpic, "device_type", "open-pic"); qemu_fdt_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); @@ -467,14 +469,15 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, soc, mpic, "serial0", 0, true); } - snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, - MPC8544_UTIL_OFFSET); + gutil = g_strdup_printf("%s/global-utilities@%llx", soc, + MPC8544_UTIL_OFFSET); qemu_fdt_add_subnode(fdt, gutil); qemu_fdt_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); qemu_fdt_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000); qemu_fdt_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); + g_free(gutil); - snprintf(msi, sizeof(msi), "/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET); + msi = g_strdup_printf("/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET); qemu_fdt_add_subnode(fdt, msi); qemu_fdt_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi"); qemu_fdt_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200); @@ -492,9 +495,10 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, 0xe7, 0x0); qemu_fdt_setprop_cell(fdt, msi, "phandle", msi_ph); qemu_fdt_setprop_cell(fdt, msi, "linux,phandle", msi_ph); + g_free(msi); - snprintf(pci, sizeof(pci), "/pci@%llx", - pmc->ccsrbar_base + MPC8544_PCI_REGS_OFFSET); + pci = g_strdup_printf("/pci@%llx", + pmc->ccsrbar_base + MPC8544_PCI_REGS_OFFSET); qemu_fdt_add_subnode(fdt, pci); qemu_fdt_setprop_cell(fdt, pci, "cell-index", 0); qemu_fdt_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); @@ -522,14 +526,17 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, qemu_fdt_setprop_cell(fdt, pci, "#size-cells", 2); qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3); qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci); + g_free(pci); if (pmc->has_mpc8xxx_gpio) { create_dt_mpc8xxx_gpio(fdt, soc, mpic); } + g_free(soc); if (pmc->has_platform_bus) { platform_bus_create_devtree(pmc, fdt, mpic); } + g_free(mpic); pmc->fixup_devtree(fdt); From ddc4115efdfa6619689fe18871aa2d37890b3463 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 24 Apr 2018 13:35:27 +0100 Subject: [PATCH 0302/2380] block/mirror: honor ratelimit again Commit b76e4458b1eb3c32e9824fe6aa51f67d2b251748 ("block/mirror: change the semantic of 'force' of block-job-cancel") accidentally removed the ratelimit in the mirror job. Reintroduce the ratelimit but keep the block-job-cancel force=true behavior that was added in commit b76e4458b1eb3c32e9824fe6aa51f67d2b251748. Note that block_job_sleep_ns() returns immediately when the job is cancelled. Therefore it's safe to unconditionally call block_job_sleep_ns() - a cancelled job does not sleep. This commit fixes the non-deterministic qemu-iotests 185 output. The test relies on the ratelimit to make the job sleep until the 'quit' command is processed. Previously the job could complete before the 'quit' command was received since there was no ratelimit. Cc: Liang Li Cc: Jeff Cody Cc: Kevin Wolf Signed-off-by: Stefan Hajnoczi Message-id: 20180424123527.19168-1-stefanha@redhat.com Signed-off-by: Jeff Cody --- block/mirror.c | 8 +++++--- tests/qemu-iotests/185.out | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 820f512c7b..9436a8d5ee 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -868,12 +868,14 @@ static void coroutine_fn mirror_run(void *opaque) } ret = 0; + + if (s->synced && !should_complete) { + delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); + } trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); + block_job_sleep_ns(&s->common, delay_ns); if (block_job_is_cancelled(&s->common) && s->common.force) { break; - } else if (!should_complete) { - delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); - block_job_sleep_ns(&s->common, delay_ns); } s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 2c4b04de73..992162f418 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -36,9 +36,9 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q {"return": {}} Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} === Start backup job and exit qemu === From eb36639f7bbc16055e551593b81365e8ae3b0b05 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 2 May 2018 00:05:08 +0200 Subject: [PATCH 0303/2380] block/mirror: Make cancel always cancel pre-READY Commit b76e4458b1eb3c32e9824fe6aa51f67d2b251748 made the mirror block job respect block-job-cancel's @force flag: With that flag set, it would now always really cancel, even post-READY. Unfortunately, it had a side effect: Without that flag set, it would now never cancel, not even before READY. Considering that is an incompatible change and not noted anywhere in the commit or the description of block-job-cancel's @force parameter, this seems unintentional and we should revert to the previous behavior, which is to immediately cancel the job when block-job-cancel is called before source and target are in sync (i.e. before the READY event). Cc: qemu-stable@nongnu.org Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1572856 Reported-by: Yanan Fu Signed-off-by: Max Reitz Reviewed-by: Eric Blake Message-id: 20180501220509.14152-2-mreitz@redhat.com Reviewed-by: Jeff Cody Signed-off-by: Jeff Cody --- block/mirror.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/block/mirror.c b/block/mirror.c index 9436a8d5ee..99da9c0858 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -874,7 +874,9 @@ static void coroutine_fn mirror_run(void *opaque) } trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); block_job_sleep_ns(&s->common, delay_ns); - if (block_job_is_cancelled(&s->common) && s->common.force) { + if (block_job_is_cancelled(&s->common) && + (!s->synced || s->common.force)) + { break; } s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); From dc885fff972c447f51572afc4c921a26b880731b Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 2 May 2018 00:05:09 +0200 Subject: [PATCH 0304/2380] iotests: Add test for cancelling a mirror job We already have an extensive mirror test (041) which does cover cancelling a mirror job, especially after it has emitted the READY event. However, it does not check what exact events are emitted after block-job-cancel is executed. More importantly, it does not use throttling to ensure that it covers the case of block-job-cancel before READY. It would be possible to add this case to 041, but considering it is already our largest test file, it makes sense to create a new file for these cases. Signed-off-by: Max Reitz Message-id: 20180501220509.14152-3-mreitz@redhat.com Signed-off-by: Jeff Cody --- tests/qemu-iotests/218 | 138 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/218.out | 30 ++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 169 insertions(+) create mode 100644 tests/qemu-iotests/218 create mode 100644 tests/qemu-iotests/218.out diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218 new file mode 100644 index 0000000000..92c331b6fb --- /dev/null +++ b/tests/qemu-iotests/218 @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# This test covers what happens when a mirror block job is cancelled +# in various phases of its existence. +# +# Note that this test only checks the emitted events (i.e. +# BLOCK_JOB_COMPLETED vs. BLOCK_JOB_CANCELLED), it does not compare +# whether the target is in sync with the source when the +# BLOCK_JOB_COMPLETED event occurs. This is covered by other tests +# (such as 041). +# +# Copyright (C) 2018 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 . +# +# Creator/Owner: Max Reitz + +import iotests +from iotests import log + +iotests.verify_platform(['linux']) + + +# Launches the VM, adds two null-co nodes (source and target), and +# starts a blockdev-mirror job on them. +# +# Either both or none of speed and buf_size must be given. + +def start_mirror(vm, speed=None, buf_size=None): + vm.launch() + + ret = vm.qmp('blockdev-add', + node_name='source', + driver='null-co', + size=1048576) + assert ret['return'] == {} + + ret = vm.qmp('blockdev-add', + node_name='target', + driver='null-co', + size=1048576) + assert ret['return'] == {} + + if speed is not None: + ret = vm.qmp('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + sync='full', + speed=speed, + buf_size=buf_size) + else: + ret = vm.qmp('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + sync='full') + + assert ret['return'] == {} + + +log('') +log('=== Cancel mirror job before convergence ===') +log('') + +log('--- force=false ---') +log('') + +with iotests.VM() as vm: + # Low speed so it does not converge + start_mirror(vm, 65536, 65536) + + log('Cancelling job') + log(vm.qmp('block-job-cancel', device='mirror', force=False)) + + log(vm.event_wait('BLOCK_JOB_CANCELLED'), + filters=[iotests.filter_qmp_event]) + +log('') +log('--- force=true ---') +log('') + +with iotests.VM() as vm: + # Low speed so it does not converge + start_mirror(vm, 65536, 65536) + + log('Cancelling job') + log(vm.qmp('block-job-cancel', device='mirror', force=True)) + + log(vm.event_wait('BLOCK_JOB_CANCELLED'), + filters=[iotests.filter_qmp_event]) + + +log('') +log('=== Cancel mirror job after convergence ===') +log('') + +log('--- force=false ---') +log('') + +with iotests.VM() as vm: + start_mirror(vm) + + log(vm.event_wait('BLOCK_JOB_READY'), + filters=[iotests.filter_qmp_event]) + + log('Cancelling job') + log(vm.qmp('block-job-cancel', device='mirror', force=False)) + + log(vm.event_wait('BLOCK_JOB_COMPLETED'), + filters=[iotests.filter_qmp_event]) + +log('') +log('--- force=true ---') +log('') + +with iotests.VM() as vm: + start_mirror(vm) + + log(vm.event_wait('BLOCK_JOB_READY'), + filters=[iotests.filter_qmp_event]) + + log('Cancelling job') + log(vm.qmp('block-job-cancel', device='mirror', force=True)) + + log(vm.event_wait('BLOCK_JOB_CANCELLED'), + filters=[iotests.filter_qmp_event]) diff --git a/tests/qemu-iotests/218.out b/tests/qemu-iotests/218.out new file mode 100644 index 0000000000..7dbf78e682 --- /dev/null +++ b/tests/qemu-iotests/218.out @@ -0,0 +1,30 @@ + +=== Cancel mirror job before convergence === + +--- force=false --- + +Cancelling job +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'} + +--- force=true --- + +Cancelling job +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'} + +=== Cancel mirror job after convergence === + +--- force=false --- + +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'} +Cancelling job +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_COMPLETED'} + +--- force=true --- + +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'} +Cancelling job +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_CANCELLED'} diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 52a80f3f9e..5daef24020 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -212,3 +212,4 @@ 211 rw auto quick 212 rw auto quick 213 rw auto quick +218 rw auto quick From a2cb9239b7610ffb00f9ced5cd7640d40b0e1ccf Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 3 May 2018 17:35:09 +0200 Subject: [PATCH 0305/2380] sheepdog: Fix sd_co_create_opts() memory leaks Both the option string for the 'redundancy' option and the SheepdogRedundancy object that is created accordingly could be leaked in error paths. This fixes the memory leaks. Reported by Coverity (CID 1390614 and 1390641). Signed-off-by: Kevin Wolf Message-id: 20180503153509.22223-1-kwolf@redhat.com Reviewed-by: Jeff Cody Signed-off-by: Jeff Cody --- block/sheepdog.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/block/sheepdog.c b/block/sheepdog.c index 07529f4b1b..fed2a04797 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1987,6 +1987,7 @@ static SheepdogRedundancy *parse_redundancy_str(const char *opt) } else { ret = qemu_strtol(n2, NULL, 10, &parity); if (ret < 0) { + g_free(redundancy); return NULL; } @@ -2183,7 +2184,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, QDict *qdict, *location_qdict; QObject *crumpled; Visitor *v; - const char *redundancy; + char *redundancy; Error *local_err = NULL; int ret; @@ -2253,6 +2254,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, fail: qapi_free_BlockdevCreateOptions(create_options); qobject_unref(qdict); + g_free(redundancy); return ret; } From 17b9751e85b9989cc841ed387794d7f1e8aa5e46 Mon Sep 17 00:00:00 2001 From: KONRAD Frederic Date: Thu, 3 May 2018 17:17:14 +0200 Subject: [PATCH 0306/2380] riscv: spike: allow base == 0 The sanity check on base doesn't allow htif to be mapped @0. Check if the symbol exists instead so we can map it where we want. Reviewed-by: Michael Clark Signed-off-by: KONRAD Frederic Signed-off-by: Michael Clark Message-Id: <1525360636-18229-2-git-send-email-frederic.konrad@adacore.com> --- hw/riscv/riscv_htif.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c index 3e17f30251..be252ec8cc 100644 --- a/hw/riscv/riscv_htif.c +++ b/hw/riscv/riscv_htif.c @@ -41,17 +41,20 @@ } while (0) static uint64_t fromhost_addr, tohost_addr; +static int address_symbol_set; void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, - uint64_t st_size) + uint64_t st_size) { if (strcmp("fromhost", st_name) == 0) { + address_symbol_set |= 1; fromhost_addr = st_value; if (st_size != 8) { error_report("HTIF fromhost must be 8 bytes"); exit(1); } } else if (strcmp("tohost", st_name) == 0) { + address_symbol_set |= 2; tohost_addr = st_value; if (st_size != 8) { error_report("HTIF tohost must be 8 bytes"); @@ -248,7 +251,7 @@ HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, htif_be_change, s, NULL, true); - if (base) { + if (address_symbol_set == 3) { memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s, TYPE_HTIF_UART, size); memory_region_add_subregion(address_space, base, &s->mmio); From 6fad7d1893f6ea926063067af957009bc320406f Mon Sep 17 00:00:00 2001 From: KONRAD Frederic Date: Thu, 3 May 2018 17:17:15 +0200 Subject: [PATCH 0307/2380] riscv: htif: increase the priority of the htif subregion The htif device is supposed to be mapped over an other subregion. So increase its priority to one to avoid any conflict. Here is the output of info mtree: Before: (qemu) info mtree address-space: memory 0000000000000000-ffffffffffffffff (prio 0, i/o): system 0000000000000000-000000000000000f (prio 0, i/o): riscv.htif.uart 0000000000000000-0000000000011fff (prio 0, ram): riscv.spike.bootrom 0000000002000000-000000000200ffff (prio 0, i/o): riscv.sifive.clint 0000000080000000-0000000087ffffff (prio 0, ram): riscv.spike.ram address-space: I/O 0000000000000000-000000000000ffff (prio 0, i/o): io address-space: cpu-memory-0 0000000000000000-ffffffffffffffff (prio 0, i/o): system 0000000000000000-000000000000000f (prio 0, i/o): riscv.htif.uart 0000000000000000-0000000000011fff (prio 0, ram): riscv.spike.bootrom 0000000002000000-000000000200ffff (prio 0, i/o): riscv.sifive.clint 0000000080000000-0000000087ffffff (prio 0, ram): riscv.spike.ram After: (qemu) info mtree address-space: memory 0000000000000000-ffffffffffffffff (prio 0, i/o): system 0000000000000000-000000000000000f (prio 1, i/o): riscv.htif.uart 0000000000000000-0000000000011fff (prio 0, ram): riscv.spike.bootrom 0000000002000000-000000000200ffff (prio 0, i/o): riscv.sifive.clint 0000000080000000-0000000087ffffff (prio 0, ram): riscv.spike.ram address-space: I/O 0000000000000000-000000000000ffff (prio 0, i/o): io address-space: cpu-memory-0 0000000000000000-ffffffffffffffff (prio 0, i/o): system 0000000000000000-000000000000000f (prio 1, i/o): riscv.htif.uart 0000000000000000-0000000000011fff (prio 0, ram): riscv.spike.bootrom 0000000002000000-000000000200ffff (prio 0, i/o): riscv.sifive.clint 0000000080000000-0000000087ffffff (prio 0, ram): riscv.spike.ram Reviewed-by: Michael Clark Signed-off-by: KONRAD Frederic Signed-off-by: Michael Clark Message-Id: <1525360636-18229-3-git-send-email-frederic.konrad@adacore.com> --- hw/riscv/riscv_htif.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c index be252ec8cc..f73512941f 100644 --- a/hw/riscv/riscv_htif.c +++ b/hw/riscv/riscv_htif.c @@ -253,8 +253,9 @@ HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, htif_be_change, s, NULL, true); if (address_symbol_set == 3) { memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s, - TYPE_HTIF_UART, size); - memory_region_add_subregion(address_space, base, &s->mmio); + TYPE_HTIF_UART, size); + memory_region_add_subregion_overlap(address_space, base, + &s->mmio, 1); } return s; From 181ce1d05c6d4f1c80f0e7ebb41e489c2b541edf Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Wed, 18 Apr 2018 09:50:44 +0200 Subject: [PATCH 0308/2380] configure: recognize more rpmbuild macros Extend the list of recognized, but ignored options from rpms %configure macro. This fixes build on hosts running SUSE Linux. Cc: qemu-stable@nongnu.org Signed-off-by: Olaf Hering Message-Id: <20180418075045.27393-1-olaf@aepfle.de> Signed-off-by: Paolo Bonzini --- configure | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure b/configure index 1443422e83..83a6080bf8 100755 --- a/configure +++ b/configure @@ -964,6 +964,8 @@ for opt do ;; --firmwarepath=*) firmwarepath="$optarg" ;; + --host=*|--build=*|\ + --disable-dependency-tracking|\ --sbindir=*|--sharedstatedir=*|\ --oldincludedir=*|--datarootdir=*|--infodir=*|--localedir=*|\ --htmldir=*|--dvidir=*|--pdfdir=*|--psdir=*) From f056158d694d2adc63ff120ca71c73ae8b14426c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 23 Apr 2018 10:45:18 +0200 Subject: [PATCH 0309/2380] cpus: Fix event order on resume of stopped guest When resume of a stopped guest immediately runs into block device errors, the BLOCK_IO_ERROR event is sent before the RESUME event. Reproducer: 1. Create a scratch image $ dd if=/dev/zero of=scratch.img bs=1M count=100 Size doesn't actually matter. 2. Prepare blkdebug configuration: $ cat >blkdebug.conf < ' Issue QMP command 'qmp_capabilities': QMP> { "execute": "qmp_capabilities" } 5. Boot the guest. 6. In the guest, write to the scratch disk, e.g. like this: # dd if=/dev/zero of=/dev/vdb count=1 Do double-check the device specified with of= is actually the scratch device! 7. Issue QMP command 'cont': QMP> { "execute": "cont" } After step 6, I get a BLOCK_IO_ERROR event followed by a STOP event. Good. After step 7, I get BLOCK_IO_ERROR, then RESUME, then STOP. Not so good; I'd expect RESUME, then BLOCK_IO_ERROR, then STOP. The funny event order confuses libvirt: virsh -r domstate DOMAIN --reason reports "paused (unknown)" rather than "paused (I/O error)". The culprit is vm_prepare_start(). /* Ensure that a STOP/RESUME pair of events is emitted if a * vmstop request was pending. The BLOCK_IO_ERROR event, for * example, according to documentation is always followed by * the STOP event. */ if (runstate_is_running()) { qapi_event_send_stop(&error_abort); res = -1; } else { replay_enable_events(); cpu_enable_ticks(); runstate_set(RUN_STATE_RUNNING); vm_state_notify(1, RUN_STATE_RUNNING); } /* We are sending this now, but the CPUs will be resumed shortly later */ qapi_event_send_resume(&error_abort); return res; When resuming a stopped guest, we take the else branch before we get to sending RESUME. vm_state_notify() runs virtio_vmstate_change(), among other things. This restarts I/O, triggering the BLOCK_IO_ERROR event. Reshuffle vm_prepare_start() to send the RESUME event earlier. Fixes RHBZ 1566153. Cc: Paolo Bonzini Signed-off-by: Markus Armbruster Message-Id: <20180423084518.2426-1-armbru@redhat.com> Signed-off-by: Paolo Bonzini --- cpus.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cpus.c b/cpus.c index 5bcd3ecf38..be3a4eb27a 100644 --- a/cpus.c +++ b/cpus.c @@ -2043,7 +2043,6 @@ int vm_stop(RunState state) int vm_prepare_start(void) { RunState requested; - int res = 0; qemu_vmstop_requested(&requested); if (runstate_is_running() && requested == RUN_STATE__MAX) { @@ -2057,17 +2056,18 @@ int vm_prepare_start(void) */ if (runstate_is_running()) { qapi_event_send_stop(&error_abort); - res = -1; - } else { - replay_enable_events(); - cpu_enable_ticks(); - runstate_set(RUN_STATE_RUNNING); - vm_state_notify(1, RUN_STATE_RUNNING); + qapi_event_send_resume(&error_abort); + return -1; } /* We are sending this now, but the CPUs will be resumed shortly later */ qapi_event_send_resume(&error_abort); - return res; + + replay_enable_events(); + cpu_enable_ticks(); + runstate_set(RUN_STATE_RUNNING); + vm_state_notify(1, RUN_STATE_RUNNING); + return 0; } void vm_start(void) From 54961aac190df28d311802364d19e18d5cda8bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 25 Apr 2018 15:18:28 +0200 Subject: [PATCH 0310/2380] cpus: tcg: fix never exiting loop on unplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 9b0605f9837b ("cpus: tcg: unregister thread with RCU, fix exiting of loop on unplug") changed the exit condition of the loop in the vCPU thread function but forgot to remove the beginning 'while (1)' statement. The resulting code : while (1) { ... } while (!cpu->unplug || cpu_can_run(cpu)); is a sequence of two distinct two while() loops, the first not exiting in case of an unplug event. Remove the first while (1) to fix CPU unplug. Signed-off-by: Cédric Le Goater Message-Id: <20180425131828.15604-1-clg@kaod.org> Cc: qemu-stable@nongnu.org Fixes: 9b0605f9837b68fd56c7fc7c96a3a1a3b983687d Signed-off-by: Paolo Bonzini Signed-off-by: Cédric Le Goater --- cpus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpus.c b/cpus.c index be3a4eb27a..d1f16296de 100644 --- a/cpus.c +++ b/cpus.c @@ -1648,7 +1648,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) /* process any pending work */ cpu->exit_request = 1; - while (1) { + do { if (cpu_can_run(cpu)) { int r; qemu_mutex_unlock_iothread(); From 82870f3c0295d36c342942b4757b2b1ddd55ab19 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 25 Apr 2018 15:01:03 +0800 Subject: [PATCH 0311/2380] checkpatch.pl: add common glib defines to typelist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise it can warn this: ERROR: space prohibited between function name and open parenthesis '(' When with things like this: typedef gboolean (*it_tree_iterator)(ITValue start, ITValue end); CC: Paolo Bonzini CC: "Daniel P. Berrangé" CC: Vladimir Sementsov-Ogievskiy CC: Fam Zheng Signed-off-by: Peter Xu Message-Id: <20180425070103.23723-1-peterx@redhat.com> Reviewed-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster Signed-off-by: Paolo Bonzini --- scripts/checkpatch.pl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 5b8735defb..e73b4efcfb 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -265,8 +265,36 @@ our @typeList = ( qr{${Ident}_handler_fn}, qr{target_(?:u)?long}, qr{hwaddr}, + # external libraries qr{xml${Ident}}, qr{xendevicemodel_handle}, + # Glib definitions + qr{gchar}, + qr{gshort}, + qr{glong}, + qr{gint}, + qr{gboolean}, + qr{guchar}, + qr{gushort}, + qr{gulong}, + qr{guint}, + qr{gfloat}, + qr{gdouble}, + qr{gpointer}, + qr{gconstpointer}, + qr{gint8}, + qr{guint8}, + qr{gint16}, + qr{guint16}, + qr{gint32}, + qr{guint32}, + qr{gint64}, + qr{guint64}, + qr{gsize}, + qr{gssize}, + qr{goffset}, + qr{gintptr}, + qr{guintptr}, ); # This can be modified by sub possible. Since it can be empty, be careful From 770dec2627b085457280490ac0543def97396fef Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 30 Apr 2018 11:44:17 +0200 Subject: [PATCH 0312/2380] qom: allow object_get_canonical_path_component without parent Just return NULL; any callers that cause a change in behavior would have caused an assertion failure before, so this is safe. Signed-off-by: Paolo Bonzini --- include/qom/object.h | 1 + qom/object.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index 96ce81bc5e..a0c78c76f7 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1302,6 +1302,7 @@ Object *object_get_internal_root(void); * * Returns: The final component in the object's canonical path. The canonical * path is the path within the composition tree starting from the root. + * %NULL if the object doesn't have a parent (and thus a canonical path). */ gchar *object_get_canonical_path_component(Object *obj); diff --git a/qom/object.c b/qom/object.c index 76a89af99b..0fc972030e 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1644,8 +1644,9 @@ gchar *object_get_canonical_path_component(Object *obj) ObjectProperty *prop = NULL; GHashTableIter iter; - g_assert(obj); - g_assert(obj->parent != NULL); + if (obj->parent == NULL) { + return NULL; + } g_hash_table_iter_init(&iter, obj->parent->properties); while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { From 29de4ec164ba2ceac4983ffd08491ea98ca8f8af Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 30 Apr 2018 11:48:18 +0200 Subject: [PATCH 0313/2380] memdev: remove "id" property The "id" property is unnecessary and can be replaced simply with object_get_canonical_path_component. This patch mostly undoes commit e1ff3c67e8 ("monitor: fix qmp/hmp query-memdev not reporting IDs of memory backends", 2017-01-12). Signed-off-by: Paolo Bonzini --- backends/hostmem.c | 26 -------------------------- include/sysemu/hostmem.h | 1 - numa.c | 2 +- qom/object_interfaces.c | 6 ------ 4 files changed, 1 insertion(+), 34 deletions(-) diff --git a/backends/hostmem.c b/backends/hostmem.c index 6a0c474222..3627e61584 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -369,24 +369,6 @@ host_memory_backend_can_be_deleted(UserCreatable *uc) } } -static char *get_id(Object *o, Error **errp) -{ - HostMemoryBackend *backend = MEMORY_BACKEND(o); - - return g_strdup(backend->id); -} - -static void set_id(Object *o, const char *str, Error **errp) -{ - HostMemoryBackend *backend = MEMORY_BACKEND(o); - - if (backend->id) { - error_setg(errp, "cannot change property value"); - return; - } - backend->id = g_strdup(str); -} - static bool host_memory_backend_get_share(Object *o, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(o); @@ -434,18 +416,11 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) &HostMemPolicy_lookup, host_memory_backend_get_policy, host_memory_backend_set_policy, &error_abort); - object_class_property_add_str(oc, "id", get_id, set_id, &error_abort); object_class_property_add_bool(oc, "share", host_memory_backend_get_share, host_memory_backend_set_share, &error_abort); } -static void host_memory_backend_finalize(Object *o) -{ - HostMemoryBackend *backend = MEMORY_BACKEND(o); - g_free(backend->id); -} - static const TypeInfo host_memory_backend_info = { .name = TYPE_MEMORY_BACKEND, .parent = TYPE_OBJECT, @@ -454,7 +429,6 @@ static const TypeInfo host_memory_backend_info = { .class_init = host_memory_backend_class_init, .instance_size = sizeof(HostMemoryBackend), .instance_init = host_memory_backend_init, - .instance_finalize = host_memory_backend_finalize, .interfaces = (InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index bc36899bb8..5beb0ef8ab 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -52,7 +52,6 @@ struct HostMemoryBackend { Object parent; /* protected */ - char *id; uint64_t size; bool merge, dump; bool prealloc, force_prealloc, is_mapped, share; diff --git a/numa.c b/numa.c index 70b150e73a..aac22a9612 100644 --- a/numa.c +++ b/numa.c @@ -580,7 +580,7 @@ static int query_memdev(Object *obj, void *opaque) m->value = g_malloc0(sizeof(*m->value)); - m->value->id = object_property_get_str(obj, "id", NULL); + m->value->id = object_get_canonical_path_component(obj); m->value->has_id = !!m->value->id; m->value->size = object_property_get_uint(obj, "size", diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 980ffc2ada..72b97a8bed 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -65,12 +65,6 @@ Object *user_creatable_add_type(const char *type, const char *id, assert(qdict); obj = object_new(type); - if (object_property_find(obj, "id", NULL)) { - object_property_set_str(obj, id, "id", &local_err); - if (local_err) { - goto out; - } - } visit_start_struct(v, NULL, NULL, 0, &local_err); if (local_err) { goto out; From 4269c82bf72f7e171a03a09b9264b0db76ae0050 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 4 Mar 2018 23:31:47 +0100 Subject: [PATCH 0314/2380] exec: move memory access declarations to a common header, inline *_phys functions For now, this reduces the text size very slightly due to the newly-added inlining: text size before: 9301965 text size after: 9300645 Later, however, the declarations in include/exec/memory_ldst.inc.h will be reused for the MemoryRegionCache slow path functions. Signed-off-by: Paolo Bonzini --- include/exec/cpu-all.h | 69 +++++-------- include/exec/memory.h | 151 ++++++++-------------------- include/exec/memory_ldst.inc.h | 71 +++++++++++++ include/exec/memory_ldst_phys.inc.h | 147 +++++++++++++++++++++++++++ memory_ldst.inc.c | 126 ----------------------- 5 files changed, 288 insertions(+), 276 deletions(-) create mode 100644 include/exec/memory_ldst.inc.h create mode 100644 include/exec/memory_ldst_phys.inc.h diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index f4fa94e966..173edd1fb4 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -168,51 +168,36 @@ extern unsigned long reserved_va; #else #include "exec/hwaddr.h" -uint32_t lduw_phys(AddressSpace *as, hwaddr addr); -uint32_t ldl_phys(AddressSpace *as, hwaddr addr); -uint64_t ldq_phys(AddressSpace *as, hwaddr addr); -void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val); -void stw_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stl_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stq_phys(AddressSpace *as, hwaddr addr, uint64_t val); -uint32_t address_space_lduw(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stw(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq(AddressSpace *as, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); +#define SUFFIX +#define ARG1 as +#define ARG1_DECL AddressSpace *as +#define TARGET_ENDIANNESS +#include "exec/memory_ldst.inc.h" -uint32_t lduw_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint32_t ldl_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint64_t ldq_phys_cached(MemoryRegionCache *cache, hwaddr addr); -void stl_phys_notdirty_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stw_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stl_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stq_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val); +#define SUFFIX _cached +#define ARG1 cache +#define ARG1_DECL MemoryRegionCache *cache +#define TARGET_ENDIANNESS +#include "exec/memory_ldst.inc.h" -uint32_t address_space_lduw_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_notdirty_cached(MemoryRegionCache *cache, hwaddr addr, - uint32_t val, MemTxAttrs attrs, MemTxResult *result); -void address_space_stw_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); +static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val) +{ + address_space_stl_notdirty(as, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +#define SUFFIX +#define ARG1 as +#define ARG1_DECL AddressSpace *as +#define TARGET_ENDIANNESS +#include "exec/memory_ldst_phys.inc.h" + +#define SUFFIX _cached +#define ARG1 cache +#define ARG1_DECL MemoryRegionCache *cache +#define TARGET_ENDIANNESS +#include "exec/memory_ldst_phys.inc.h" #endif /* page related stuff */ diff --git a/include/exec/memory.h b/include/exec/memory.h index 31eae0a640..ca361bc409 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1676,49 +1676,16 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, * @result: location to write the success/failure of the transaction; * if NULL, this information is discarded */ -uint32_t address_space_ldub(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_lduw_le(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_lduw_be(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl_le(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl_be(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq_le(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq_be(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stb(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stw_le(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stw_be(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_le(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_be(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq_le(AddressSpace *as, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq_be(AddressSpace *as, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); -uint32_t ldub_phys(AddressSpace *as, hwaddr addr); -uint32_t lduw_le_phys(AddressSpace *as, hwaddr addr); -uint32_t lduw_be_phys(AddressSpace *as, hwaddr addr); -uint32_t ldl_le_phys(AddressSpace *as, hwaddr addr); -uint32_t ldl_be_phys(AddressSpace *as, hwaddr addr); -uint64_t ldq_le_phys(AddressSpace *as, hwaddr addr); -uint64_t ldq_be_phys(AddressSpace *as, hwaddr addr); -void stb_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stw_le_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stw_be_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stl_le_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stl_be_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stq_le_phys(AddressSpace *as, hwaddr addr, uint64_t val); -void stq_be_phys(AddressSpace *as, hwaddr addr, uint64_t val); +#define SUFFIX +#define ARG1 as +#define ARG1_DECL AddressSpace *as +#include "exec/memory_ldst.inc.h" + +#define SUFFIX +#define ARG1 as +#define ARG1_DECL AddressSpace *as +#include "exec/memory_ldst_phys.inc.h" struct MemoryRegionCache { hwaddr xlat; @@ -1728,6 +1695,40 @@ struct MemoryRegionCache { #define MEMORY_REGION_CACHE_INVALID ((MemoryRegionCache) { .as = NULL }) +/* address_space_ld*_cached: load from a cached #MemoryRegion + * address_space_st*_cached: store into a cached #MemoryRegion + * + * These functions perform a load or store of the byte, word, + * longword or quad to the specified address. The address is + * a physical address in the AddressSpace, but it must lie within + * a #MemoryRegion that was mapped with address_space_cache_init. + * + * The _le suffixed functions treat the data as little endian; + * _be indicates big endian; no suffix indicates "same endianness + * as guest CPU". + * + * The "guest CPU endianness" accessors are deprecated for use outside + * target-* code; devices should be CPU-agnostic and use either the LE + * or the BE accessors. + * + * @cache: previously initialized #MemoryRegionCache to be accessed + * @addr: address within the address space + * @val: data value, for stores + * @attrs: memory transaction attributes + * @result: location to write the success/failure of the transaction; + * if NULL, this information is discarded + */ + +#define SUFFIX _cached +#define ARG1 cache +#define ARG1_DECL MemoryRegionCache *cache +#include "exec/memory_ldst.inc.h" + +#define SUFFIX _cached +#define ARG1 cache +#define ARG1_DECL MemoryRegionCache *cache +#include "exec/memory_ldst_phys.inc.h" + /* address_space_cache_init: prepare for repeated access to a physical * memory region * @@ -1772,72 +1773,6 @@ void address_space_cache_invalidate(MemoryRegionCache *cache, */ void address_space_cache_destroy(MemoryRegionCache *cache); -/* address_space_ld*_cached: load from a cached #MemoryRegion - * address_space_st*_cached: store into a cached #MemoryRegion - * - * These functions perform a load or store of the byte, word, - * longword or quad to the specified address. The address is - * a physical address in the AddressSpace, but it must lie within - * a #MemoryRegion that was mapped with address_space_cache_init. - * - * The _le suffixed functions treat the data as little endian; - * _be indicates big endian; no suffix indicates "same endianness - * as guest CPU". - * - * The "guest CPU endianness" accessors are deprecated for use outside - * target-* code; devices should be CPU-agnostic and use either the LE - * or the BE accessors. - * - * @cache: previously initialized #MemoryRegionCache to be accessed - * @addr: address within the address space - * @val: data value, for stores - * @attrs: memory transaction attributes - * @result: location to write the success/failure of the transaction; - * if NULL, this information is discarded - */ -uint32_t address_space_ldub_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_lduw_le_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_lduw_be_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl_le_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl_be_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq_le_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq_be_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stb_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stw_le_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stw_be_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_le_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_be_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq_le_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq_be_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); - -uint32_t ldub_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint32_t lduw_le_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint32_t lduw_be_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint32_t ldl_le_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint32_t ldl_be_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint64_t ldq_le_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint64_t ldq_be_phys_cached(MemoryRegionCache *cache, hwaddr addr); -void stb_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stw_le_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stw_be_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stl_le_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stl_be_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stq_le_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val); -void stq_be_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val); /* address_space_get_iotlb_entry: translate an address into an IOTLB * entry. Should be called from an RCU critical section. */ diff --git a/include/exec/memory_ldst.inc.h b/include/exec/memory_ldst.inc.h new file mode 100644 index 0000000000..272c20f02e --- /dev/null +++ b/include/exec/memory_ldst.inc.h @@ -0,0 +1,71 @@ +/* + * Physical memory access templates + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2015 Linaro, Inc. + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifdef TARGET_ENDIANNESS +extern uint32_t glue(address_space_lduw, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint32_t glue(address_space_ldl, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint64_t glue(address_space_ldq, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stw, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stl, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stq, SUFFIX)(ARG1_DECL, + hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); +#else +extern uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint32_t glue(address_space_lduw_le, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint32_t glue(address_space_lduw_be, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint32_t glue(address_space_ldl_le, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint32_t glue(address_space_ldl_be, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint64_t glue(address_space_ldq_le, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint64_t glue(address_space_ldq_be, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stb, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stw_le, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stw_be, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stl_le, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stl_be, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stq_le, SUFFIX)(ARG1_DECL, + hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, + hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); +#endif + +#undef ARG1_DECL +#undef ARG1 +#undef SUFFIX +#undef TARGET_ENDIANNESS diff --git a/include/exec/memory_ldst_phys.inc.h b/include/exec/memory_ldst_phys.inc.h new file mode 100644 index 0000000000..91f72973cb --- /dev/null +++ b/include/exec/memory_ldst_phys.inc.h @@ -0,0 +1,147 @@ +/* + * Physical memory access templates + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2015 Linaro, Inc. + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifdef TARGET_ENDIANNESS +static inline uint32_t glue(ldl_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldl, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint64_t glue(ldq_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldq, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint32_t glue(lduw_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_lduw, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stl_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stl, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stw_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stw, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stq_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) +{ + glue(address_space_stq, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} +#else +static inline uint32_t glue(ldl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldl_le, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint32_t glue(ldl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldl_be, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint64_t glue(ldq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldq_le, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint64_t glue(ldq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldq_be, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint32_t glue(ldub_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldub, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint32_t glue(lduw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_lduw_le, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint32_t glue(lduw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_lduw_be, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stl_le, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stl_be, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stb_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stb, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stw_le, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stw_be, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) +{ + glue(address_space_stq_le, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) +{ + glue(address_space_stq_be, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} +#endif + +#undef ARG1_DECL +#undef ARG1 +#undef SUFFIX +#undef TARGET_ENDIANNESS diff --git a/memory_ldst.inc.c b/memory_ldst.inc.c index 5dbff9cef8..25d6125747 100644 --- a/memory_ldst.inc.c +++ b/memory_ldst.inc.c @@ -95,24 +95,6 @@ uint32_t glue(address_space_ldl_be, SUFFIX)(ARG1_DECL, DEVICE_BIG_ENDIAN); } -uint32_t glue(ldl_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldl, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint32_t glue(ldl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldl_le, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint32_t glue(ldl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldl_be, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - /* warning: addr must be aligned */ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result, @@ -189,24 +171,6 @@ uint64_t glue(address_space_ldq_be, SUFFIX)(ARG1_DECL, DEVICE_BIG_ENDIAN); } -uint64_t glue(ldq_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldq, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint64_t glue(ldq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldq_le, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint64_t glue(ldq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldq_be, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result) { @@ -241,12 +205,6 @@ uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, return val; } -uint32_t glue(ldub_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldub, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - /* warning: addr must be aligned */ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result, @@ -323,24 +281,6 @@ uint32_t glue(address_space_lduw_be, SUFFIX)(ARG1_DECL, DEVICE_BIG_ENDIAN); } -uint32_t glue(lduw_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_lduw, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint32_t glue(lduw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_lduw_le, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint32_t glue(lduw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_lduw_be, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - /* warning: addr must be aligned. The ram page is not masked as dirty and the code inside is not invalidated. It is useful if the dirty bits are used to track modified PTEs */ @@ -380,12 +320,6 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, RCU_READ_UNLOCK(); } -void glue(stl_phys_notdirty, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stl_notdirty, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - /* warning: addr must be aligned */ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, @@ -460,24 +394,6 @@ void glue(address_space_stl_be, SUFFIX)(ARG1_DECL, result, DEVICE_BIG_ENDIAN); } -void glue(stl_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stl, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stl_le, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stl_be, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - void glue(address_space_stb, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) { @@ -509,12 +425,6 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, RCU_READ_UNLOCK(); } -void glue(stb_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stb, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - /* warning: addr must be aligned */ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, @@ -589,24 +499,6 @@ void glue(address_space_stw_be, SUFFIX)(ARG1_DECL, DEVICE_BIG_ENDIAN); } -void glue(stw_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stw, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stw_le, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stw_be, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result, enum device_endian endian) @@ -680,24 +572,6 @@ void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, DEVICE_BIG_ENDIAN); } -void glue(stq_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) -{ - glue(address_space_stq, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) -{ - glue(address_space_stq_le, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) -{ - glue(address_space_stq_be, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - #undef ARG1_DECL #undef ARG1 #undef SUFFIX From ad2804d9e47df2dab642a253502b5ceef233f450 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Apr 2018 11:39:35 +0200 Subject: [PATCH 0315/2380] exec: small changes to flatview_do_translate Prepare for extracting the IOMMU part to a separate function. Mostly cosmetic; the only semantic change is that, if there is more than one cascaded IOMMU and the second one fails to translate, *plen_out is now adjusted according to the page mask of the first IOMMU. Reviewed-by: Peter Xu Signed-off-by: Paolo Bonzini --- exec.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/exec.c b/exec.c index c7fcefa851..8354cdcaf2 100644 --- a/exec.c +++ b/exec.c @@ -476,6 +476,7 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x * would tell. It can be @NULL if we don't care about it. * @is_write: whether the translation operation is for write * @is_mmio: whether this can be MMIO, set true if it can + * @target_as: the address space targeted by the IOMMU * * This function is called from RCU critical section */ @@ -495,14 +496,14 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, hwaddr page_mask = (hwaddr)(-1); hwaddr plen = (hwaddr)(-1); - if (plen_out) { - plen = *plen_out; + if (!plen_out) { + plen_out = &plen; } for (;;) { section = address_space_translate_internal( - flatview_to_dispatch(fv), addr, &addr, - &plen, is_mmio); + flatview_to_dispatch(fv), addr, xlat, + plen_out, is_mmio); iommu_mr = memory_region_get_iommu(section->mr); if (!iommu_mr) { @@ -510,35 +511,29 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, } imrc = memory_region_get_iommu_class_nocheck(iommu_mr); + addr = *xlat; iotlb = imrc->translate(iommu_mr, addr, is_write ? IOMMU_WO : IOMMU_RO); - addr = ((iotlb.translated_addr & ~iotlb.addr_mask) - | (addr & iotlb.addr_mask)); - page_mask &= iotlb.addr_mask; - plen = MIN(plen, (addr | iotlb.addr_mask) - addr + 1); if (!(iotlb.perm & (1 << is_write))) { goto translate_fail; } + addr = ((iotlb.translated_addr & ~iotlb.addr_mask) + | (addr & iotlb.addr_mask)); + page_mask &= iotlb.addr_mask; + *plen_out = MIN(*plen_out, (addr | iotlb.addr_mask) - addr + 1); fv = address_space_to_flatview(iotlb.target_as); *target_as = iotlb.target_as; } - *xlat = addr; - - if (page_mask == (hwaddr)(-1)) { - /* Not behind an IOMMU, use default page size. */ - page_mask = ~TARGET_PAGE_MASK; - } - if (page_mask_out) { + if (page_mask == (hwaddr)(-1)) { + /* Not behind an IOMMU, use default page size. */ + page_mask = ~TARGET_PAGE_MASK; + } *page_mask_out = page_mask; } - if (plen_out) { - *plen_out = plen; - } - return *section; translate_fail: From a411c84b561baa94b28165c52f21c33517ee8f59 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 3 Mar 2018 17:24:04 +0100 Subject: [PATCH 0316/2380] exec: extract address_space_translate_iommu, fix page_mask corner case This will be used to process IOMMUs in a MemoryRegionCache. This includes a small bugfix, in that the returned page_mask is now correctly -1 if the IOMMU memory region maps the entire address space directly. Previously, address_space_get_iotlb_entry would return ~TARGET_PAGE_MASK. Reviewed-by: Peter Xu Signed-off-by: Paolo Bonzini --- exec.c | 110 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 35 deletions(-) diff --git a/exec.c b/exec.c index 8354cdcaf2..5f98106d33 100644 --- a/exec.c +++ b/exec.c @@ -461,6 +461,70 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x return section; } +/** + * address_space_translate_iommu - translate an address through an IOMMU + * memory region and then through the target address space. + * + * @iommu_mr: the IOMMU memory region that we start the translation from + * @addr: the address to be translated through the MMU + * @xlat: the translated address offset within the destination memory region. + * It cannot be %NULL. + * @plen_out: valid read/write length of the translated address. It + * cannot be %NULL. + * @page_mask_out: page mask for the translated address. This + * should only be meaningful for IOMMU translated + * addresses, since there may be huge pages that this bit + * would tell. It can be %NULL if we don't care about it. + * @is_write: whether the translation operation is for write + * @is_mmio: whether this can be MMIO, set true if it can + * @target_as: the address space targeted by the IOMMU + * + * This function is called from RCU critical section. It is the common + * part of flatview_do_translate and address_space_translate_cached. + */ +static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iommu_mr, + hwaddr *xlat, + hwaddr *plen_out, + hwaddr *page_mask_out, + bool is_write, + bool is_mmio, + AddressSpace **target_as) +{ + MemoryRegionSection *section; + hwaddr page_mask = (hwaddr)-1; + + do { + hwaddr addr = *xlat; + IOMMUMemoryRegionClass *imrc = memory_region_get_iommu_class_nocheck(iommu_mr); + IOMMUTLBEntry iotlb = imrc->translate(iommu_mr, addr, is_write ? + IOMMU_WO : IOMMU_RO); + + if (!(iotlb.perm & (1 << is_write))) { + goto unassigned; + } + + addr = ((iotlb.translated_addr & ~iotlb.addr_mask) + | (addr & iotlb.addr_mask)); + page_mask &= iotlb.addr_mask; + *plen_out = MIN(*plen_out, (addr | iotlb.addr_mask) - addr + 1); + *target_as = iotlb.target_as; + + section = address_space_translate_internal( + address_space_to_dispatch(iotlb.target_as), addr, xlat, + plen_out, is_mmio); + + iommu_mr = memory_region_get_iommu(section->mr); + } while (unlikely(iommu_mr)); + + if (page_mask_out) { + *page_mask_out = page_mask; + } + return *section; + +unassigned: + return (MemoryRegionSection) { .mr = &io_mem_unassigned }; +} + /** * flatview_do_translate - translate an address in FlatView * @@ -489,55 +553,31 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, bool is_mmio, AddressSpace **target_as) { - IOMMUTLBEntry iotlb; MemoryRegionSection *section; IOMMUMemoryRegion *iommu_mr; - IOMMUMemoryRegionClass *imrc; - hwaddr page_mask = (hwaddr)(-1); hwaddr plen = (hwaddr)(-1); if (!plen_out) { plen_out = &plen; } - for (;;) { - section = address_space_translate_internal( - flatview_to_dispatch(fv), addr, xlat, - plen_out, is_mmio); + section = address_space_translate_internal( + flatview_to_dispatch(fv), addr, xlat, + plen_out, is_mmio); - iommu_mr = memory_region_get_iommu(section->mr); - if (!iommu_mr) { - break; - } - imrc = memory_region_get_iommu_class_nocheck(iommu_mr); - - addr = *xlat; - iotlb = imrc->translate(iommu_mr, addr, is_write ? - IOMMU_WO : IOMMU_RO); - if (!(iotlb.perm & (1 << is_write))) { - goto translate_fail; - } - - addr = ((iotlb.translated_addr & ~iotlb.addr_mask) - | (addr & iotlb.addr_mask)); - page_mask &= iotlb.addr_mask; - *plen_out = MIN(*plen_out, (addr | iotlb.addr_mask) - addr + 1); - fv = address_space_to_flatview(iotlb.target_as); - *target_as = iotlb.target_as; + iommu_mr = memory_region_get_iommu(section->mr); + if (unlikely(iommu_mr)) { + return address_space_translate_iommu(iommu_mr, xlat, + plen_out, page_mask_out, + is_write, is_mmio, + target_as); } - if (page_mask_out) { - if (page_mask == (hwaddr)(-1)) { - /* Not behind an IOMMU, use default page size. */ - page_mask = ~TARGET_PAGE_MASK; - } - *page_mask_out = page_mask; + /* Not behind an IOMMU, use default page size. */ + *page_mask_out = ~TARGET_PAGE_MASK; } return *section; - -translate_fail: - return (MemoryRegionSection) { .mr = &io_mem_unassigned }; } /* Called from RCU critical section */ From 48564041a73adbbff52834f9edbe3806fceefab7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 18 Mar 2018 18:26:36 +0100 Subject: [PATCH 0317/2380] exec: reintroduce MemoryRegion caching MemoryRegionCache was reverted to "normal" address_space_* operations for 2.9, due to lack of support for IOMMUs. Reinstate the optimizations, caching only the IOMMU translation at address_cache_init but not the IOMMU lookup and target AddressSpace translation are not cached; now that MemoryRegionCache supports IOMMUs, it becomes more widely applicable too. The inlined fast path is defined in memory_ldst_cached.inc.h, while the slow path uses memory_ldst.inc.c as before. The smaller fast path causes a little code size reduction in MemoryRegionCache users: hw/virtio/virtio.o text size before: 32373 hw/virtio/virtio.o text size after: 31941 Signed-off-by: Paolo Bonzini --- exec.c | 121 +++++++++++++++++++++++--- include/exec/cpu-all.h | 6 +- include/exec/memory-internal.h | 3 + include/exec/memory.h | 58 ++++++++++-- include/exec/memory_ldst_cached.inc.h | 108 +++++++++++++++++++++++ memory.c | 4 +- 6 files changed, 280 insertions(+), 20 deletions(-) create mode 100644 include/exec/memory_ldst_cached.inc.h diff --git a/exec.c b/exec.c index 5f98106d33..ffa1099547 100644 --- a/exec.c +++ b/exec.c @@ -3641,33 +3641,130 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, hwaddr len, bool is_write) { - cache->len = len; - cache->as = as; - cache->xlat = addr; - return len; + AddressSpaceDispatch *d; + hwaddr l; + MemoryRegion *mr; + + assert(len > 0); + + l = len; + cache->fv = address_space_get_flatview(as); + d = flatview_to_dispatch(cache->fv); + cache->mrs = *address_space_translate_internal(d, addr, &cache->xlat, &l, true); + + mr = cache->mrs.mr; + memory_region_ref(mr); + if (memory_access_is_direct(mr, is_write)) { + l = flatview_extend_translation(cache->fv, addr, len, mr, + cache->xlat, l, is_write); + cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true); + } else { + cache->ptr = NULL; + } + + cache->len = l; + cache->is_write = is_write; + return l; } void address_space_cache_invalidate(MemoryRegionCache *cache, hwaddr addr, hwaddr access_len) { + assert(cache->is_write); + if (likely(cache->ptr)) { + invalidate_and_set_dirty(cache->mrs.mr, addr + cache->xlat, access_len); + } } void address_space_cache_destroy(MemoryRegionCache *cache) { - cache->as = NULL; + if (!cache->mrs.mr) { + return; + } + + if (xen_enabled()) { + xen_invalidate_map_cache_entry(cache->ptr); + } + memory_region_unref(cache->mrs.mr); + flatview_unref(cache->fv); + cache->mrs.mr = NULL; + cache->fv = NULL; +} + +/* Called from RCU critical section. This function has the same + * semantics as address_space_translate, but it only works on a + * predefined range of a MemoryRegion that was mapped with + * address_space_cache_init. + */ +static inline MemoryRegion *address_space_translate_cached( + MemoryRegionCache *cache, hwaddr addr, hwaddr *xlat, + hwaddr *plen, bool is_write) +{ + MemoryRegionSection section; + MemoryRegion *mr; + IOMMUMemoryRegion *iommu_mr; + AddressSpace *target_as; + + assert(!cache->ptr); + *xlat = addr + cache->xlat; + + mr = cache->mrs.mr; + iommu_mr = memory_region_get_iommu(mr); + if (!iommu_mr) { + /* MMIO region. */ + return mr; + } + + section = address_space_translate_iommu(iommu_mr, xlat, plen, + NULL, is_write, true, + &target_as); + return section.mr; +} + +/* Called from RCU critical section. address_space_read_cached uses this + * out of line function when the target is an MMIO or IOMMU region. + */ +void +address_space_read_cached_slow(MemoryRegionCache *cache, hwaddr addr, + void *buf, int len) +{ + hwaddr addr1, l; + MemoryRegion *mr; + + l = len; + mr = address_space_translate_cached(cache, addr, &addr1, &l, false); + flatview_read_continue(cache->fv, + addr, MEMTXATTRS_UNSPECIFIED, buf, len, + addr1, l, mr); +} + +/* Called from RCU critical section. address_space_write_cached uses this + * out of line function when the target is an MMIO or IOMMU region. + */ +void +address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, + const void *buf, int len) +{ + hwaddr addr1, l; + MemoryRegion *mr; + + l = len; + mr = address_space_translate_cached(cache, addr, &addr1, &l, true); + flatview_write_continue(cache->fv, + addr, MEMTXATTRS_UNSPECIFIED, buf, len, + addr1, l, mr); } #define ARG1_DECL MemoryRegionCache *cache #define ARG1 cache -#define SUFFIX _cached -#define TRANSLATE(addr, ...) \ - address_space_translate(cache->as, cache->xlat + (addr), __VA_ARGS__) -#define IS_DIRECT(mr, is_write) true -#define MAP_RAM(mr, ofs) qemu_map_ram_ptr((mr)->ram_block, ofs) +#define SUFFIX _cached_slow +#define TRANSLATE(...) address_space_translate_cached(cache, __VA_ARGS__) +#define IS_DIRECT(mr, is_write) memory_access_is_direct(mr, is_write) +#define MAP_RAM(mr, ofs) (cache->ptr + (ofs - cache->xlat)) #define INVALIDATE(mr, ofs, len) invalidate_and_set_dirty(mr, ofs, len) -#define RCU_READ_LOCK() rcu_read_lock() -#define RCU_READ_UNLOCK() rcu_read_unlock() +#define RCU_READ_LOCK() ((void)0) +#define RCU_READ_UNLOCK() ((void)0) #include "memory_ldst.inc.c" /* virtual memory access for debug (includes writing to ROM) */ diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 173edd1fb4..a635f532f9 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -175,7 +175,7 @@ extern unsigned long reserved_va; #define TARGET_ENDIANNESS #include "exec/memory_ldst.inc.h" -#define SUFFIX _cached +#define SUFFIX _cached_slow #define ARG1 cache #define ARG1_DECL MemoryRegionCache *cache #define TARGET_ENDIANNESS @@ -193,6 +193,10 @@ static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val #define TARGET_ENDIANNESS #include "exec/memory_ldst_phys.inc.h" +/* Inline fast path for direct RAM access. */ +#define ENDIANNESS +#include "exec/memory_ldst_cached.inc.h" + #define SUFFIX _cached #define ARG1 cache #define ARG1_DECL MemoryRegionCache *cache diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 6a5ee42d36..58399b9318 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -31,6 +31,9 @@ static inline AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as) return flatview_to_dispatch(address_space_to_flatview(as)); } +FlatView *address_space_get_flatview(AddressSpace *as); +void flatview_unref(FlatView *view); + extern const MemoryRegionOps unassigned_mem_ops; bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, diff --git a/include/exec/memory.h b/include/exec/memory.h index ca361bc409..525619a5f4 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1688,12 +1688,16 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, #include "exec/memory_ldst_phys.inc.h" struct MemoryRegionCache { + void *ptr; hwaddr xlat; hwaddr len; - AddressSpace *as; + FlatView *fv; + MemoryRegionSection mrs; + bool is_write; }; -#define MEMORY_REGION_CACHE_INVALID ((MemoryRegionCache) { .as = NULL }) +#define MEMORY_REGION_CACHE_INVALID ((MemoryRegionCache) { .mrs.mr = NULL }) + /* address_space_ld*_cached: load from a cached #MemoryRegion * address_space_st*_cached: store into a cached #MemoryRegion @@ -1719,11 +1723,40 @@ struct MemoryRegionCache { * if NULL, this information is discarded */ -#define SUFFIX _cached +#define SUFFIX _cached_slow #define ARG1 cache #define ARG1_DECL MemoryRegionCache *cache #include "exec/memory_ldst.inc.h" +/* Inline fast path for direct RAM access. */ +static inline uint8_t address_space_ldub_cached(MemoryRegionCache *cache, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len); + if (likely(cache->ptr)) { + return ldub_p(cache->ptr + addr); + } else { + return address_space_ldub_cached_slow(cache, addr, attrs, result); + } +} + +static inline void address_space_stb_cached(MemoryRegionCache *cache, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len); + if (likely(cache->ptr)) { + stb_p(cache->ptr + addr, val); + } else { + address_space_stb_cached_slow(cache, addr, val, attrs, result); + } +} + +#define ENDIANNESS _le +#include "exec/memory_ldst_cached.inc.h" + +#define ENDIANNESS _be +#include "exec/memory_ldst_cached.inc.h" + #define SUFFIX _cached #define ARG1 cache #define ARG1_DECL MemoryRegionCache *cache @@ -1860,6 +1893,13 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, MemoryRegion *mr); void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr); +/* Internal functions, part of the implementation of address_space_read_cached + * and address_space_write_cached. */ +void address_space_read_cached_slow(MemoryRegionCache *cache, + hwaddr addr, void *buf, int len); +void address_space_write_cached_slow(MemoryRegionCache *cache, + hwaddr addr, const void *buf, int len); + static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) { if (is_write) { @@ -1928,7 +1968,11 @@ address_space_read_cached(MemoryRegionCache *cache, hwaddr addr, void *buf, int len) { assert(addr < cache->len && len <= cache->len - addr); - address_space_read(cache->as, cache->xlat + addr, MEMTXATTRS_UNSPECIFIED, buf, len); + if (likely(cache->ptr)) { + memcpy(buf, cache->ptr + addr, len); + } else { + address_space_read_cached_slow(cache, addr, buf, len); + } } /** @@ -1944,7 +1988,11 @@ address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, void *buf, int len) { assert(addr < cache->len && len <= cache->len - addr); - address_space_write(cache->as, cache->xlat + addr, MEMTXATTRS_UNSPECIFIED, buf, len); + if (likely(cache->ptr)) { + memcpy(cache->ptr + addr, buf, len); + } else { + address_space_write_cached_slow(cache, addr, buf, len); + } } #endif diff --git a/include/exec/memory_ldst_cached.inc.h b/include/exec/memory_ldst_cached.inc.h new file mode 100644 index 0000000000..fd4bbb40e7 --- /dev/null +++ b/include/exec/memory_ldst_cached.inc.h @@ -0,0 +1,108 @@ +/* + * Memory access templates for MemoryRegionCache + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#define ADDRESS_SPACE_LD_CACHED(size) \ + glue(glue(address_space_ld, size), glue(ENDIANNESS, _cached)) +#define ADDRESS_SPACE_LD_CACHED_SLOW(size) \ + glue(glue(address_space_ld, size), glue(ENDIANNESS, _cached_slow)) +#define LD_P(size) \ + glue(glue(ld, size), glue(ENDIANNESS, _p)) + +static inline uint32_t ADDRESS_SPACE_LD_CACHED(l)(MemoryRegionCache *cache, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 4 <= cache->len - addr); + if (likely(cache->ptr)) { + return LD_P(l)(cache->ptr + addr); + } else { + return ADDRESS_SPACE_LD_CACHED_SLOW(l)(cache, addr, attrs, result); + } +} + +static inline uint64_t ADDRESS_SPACE_LD_CACHED(q)(MemoryRegionCache *cache, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 8 <= cache->len - addr); + if (likely(cache->ptr)) { + return LD_P(q)(cache->ptr + addr); + } else { + return ADDRESS_SPACE_LD_CACHED_SLOW(q)(cache, addr, attrs, result); + } +} + +static inline uint32_t ADDRESS_SPACE_LD_CACHED(uw)(MemoryRegionCache *cache, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 2 <= cache->len - addr); + if (likely(cache->ptr)) { + return LD_P(uw)(cache->ptr + addr); + } else { + return ADDRESS_SPACE_LD_CACHED_SLOW(uw)(cache, addr, attrs, result); + } +} + +#undef ADDRESS_SPACE_LD_CACHED +#undef ADDRESS_SPACE_LD_CACHED_SLOW +#undef LD_P + +#define ADDRESS_SPACE_ST_CACHED(size) \ + glue(glue(address_space_st, size), glue(ENDIANNESS, _cached)) +#define ADDRESS_SPACE_ST_CACHED_SLOW(size) \ + glue(glue(address_space_st, size), glue(ENDIANNESS, _cached_slow)) +#define ST_P(size) \ + glue(glue(st, size), glue(ENDIANNESS, _p)) + +static inline void ADDRESS_SPACE_ST_CACHED(l)(MemoryRegionCache *cache, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 4 <= cache->len - addr); + if (likely(cache->ptr)) { + ST_P(l)(cache->ptr + addr, val); + } else { + ADDRESS_SPACE_ST_CACHED_SLOW(l)(cache, addr, val, attrs, result); + } +} + +static inline void ADDRESS_SPACE_ST_CACHED(w)(MemoryRegionCache *cache, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 2 <= cache->len - addr); + if (likely(cache->ptr)) { + ST_P(w)(cache->ptr + addr, val); + } else { + ADDRESS_SPACE_ST_CACHED_SLOW(w)(cache, addr, val, attrs, result); + } +} + +static inline void ADDRESS_SPACE_ST_CACHED(q)(MemoryRegionCache *cache, + hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 8 <= cache->len - addr); + if (likely(cache->ptr)) { + ST_P(q)(cache->ptr + addr, val); + } else { + ADDRESS_SPACE_ST_CACHED_SLOW(q)(cache, addr, val, attrs, result); + } +} + +#undef ADDRESS_SPACE_ST_CACHED +#undef ADDRESS_SPACE_ST_CACHED_SLOW +#undef ST_P + +#undef ENDIANNESS diff --git a/memory.c b/memory.c index e70b64b8b9..fc7f9b782b 100644 --- a/memory.c +++ b/memory.c @@ -298,7 +298,7 @@ static bool flatview_ref(FlatView *view) return atomic_fetch_inc_nonzero(&view->ref) > 0; } -static void flatview_unref(FlatView *view) +void flatview_unref(FlatView *view) { if (atomic_fetch_dec(&view->ref) == 1) { trace_flatview_destroy_rcu(view, view->root); @@ -822,7 +822,7 @@ static void address_space_add_del_ioeventfds(AddressSpace *as, } } -static FlatView *address_space_get_flatview(AddressSpace *as) +FlatView *address_space_get_flatview(AddressSpace *as) { FlatView *view; From d820fa5b62d2ae2311702bf307f1b91dc338fd25 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 12 Apr 2018 13:34:44 +0800 Subject: [PATCH 0318/2380] qemu-thread: always keep the posix wrapper layer We will conditionally have a wrapper layer depending on whether the host has the PTHREAD_SETNAME capability. It complicates stuff. Let's keep the wrapper there; we opt out the pthread_setname_np() call only. Signed-off-by: Peter Xu Message-Id: <20180412053444.17801-1-peterx@redhat.com> Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- util/qemu-thread-posix.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index b789cf32e9..a1c34ba6f2 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -482,7 +482,6 @@ static void __attribute__((constructor)) qemu_thread_atexit_init(void) } -#ifdef CONFIG_PTHREAD_SETNAME_NP typedef struct { void *(*start_routine)(void *); void *arg; @@ -495,16 +494,18 @@ static void *qemu_thread_start(void *args) void *(*start_routine)(void *) = qemu_thread_args->start_routine; void *arg = qemu_thread_args->arg; +#ifdef CONFIG_PTHREAD_SETNAME_NP /* Attempt to set the threads name; note that this is for debug, so * we're not going to fail if we can't set it. */ - pthread_setname_np(pthread_self(), qemu_thread_args->name); + if (name_threads && qemu_thread_args->name) { + pthread_setname_np(pthread_self(), qemu_thread_args->name); + } +#endif g_free(qemu_thread_args->name); g_free(qemu_thread_args); return start_routine(arg); } -#endif - void qemu_thread_create(QemuThread *thread, const char *name, void *(*start_routine)(void*), @@ -513,6 +514,7 @@ void qemu_thread_create(QemuThread *thread, const char *name, sigset_t set, oldset; int err; pthread_attr_t attr; + QemuThreadArgs *qemu_thread_args; err = pthread_attr_init(&attr); if (err) { @@ -527,22 +529,13 @@ void qemu_thread_create(QemuThread *thread, const char *name, sigfillset(&set); pthread_sigmask(SIG_SETMASK, &set, &oldset); -#ifdef CONFIG_PTHREAD_SETNAME_NP - if (name_threads) { - QemuThreadArgs *qemu_thread_args; - qemu_thread_args = g_new0(QemuThreadArgs, 1); - qemu_thread_args->name = g_strdup(name); - qemu_thread_args->start_routine = start_routine; - qemu_thread_args->arg = arg; + qemu_thread_args = g_new0(QemuThreadArgs, 1); + qemu_thread_args->name = g_strdup(name); + qemu_thread_args->start_routine = start_routine; + qemu_thread_args->arg = arg; - err = pthread_create(&thread->thread, &attr, - qemu_thread_start, qemu_thread_args); - } else -#endif - { - err = pthread_create(&thread->thread, &attr, - start_routine, arg); - } + err = pthread_create(&thread->thread, &attr, + qemu_thread_start, qemu_thread_args); if (err) error_exit(err, __func__); From 7afcfd32a65688401cba337eb99f65dc24d9750e Mon Sep 17 00:00:00 2001 From: Roman Kagan Date: Fri, 13 Apr 2018 17:33:54 +0300 Subject: [PATCH 0319/2380] update-linux-headers: drop hyperv.h As of mainline linux commit 5a485803221777013944cbd1a7cd5c62efba3ffa "x86/hyper-v: move hyperv.h out of uapi" by Vitaly Kuznetsov, no linux uapi header includes it, so we no longer need to create a stub for it. Cc: Vitaly Kuznetsov Signed-off-by: Roman Kagan Message-Id: <20180413143354.17614-1-rkagan@virtuozzo.com> Signed-off-by: Paolo Bonzini --- scripts/update-linux-headers.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 5b1d8dcdf4..b65c03f0ae 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -118,9 +118,6 @@ for arch in $ARCHLIST; do cp "$tmpdir/include/asm/unistd-common.h" "$output/linux-headers/asm-arm/" fi if [ $arch = x86 ]; then - cat <<-EOF >"$output/include/standard-headers/asm-x86/hyperv.h" - /* this is a temporary placeholder until kvm_para.h stops including it */ -EOF cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/" cp "$tmpdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" @@ -144,9 +141,6 @@ else cp "$linux/COPYING" "$output/linux-headers" fi -cat <$output/linux-headers/asm-x86/hyperv.h -#include "standard-headers/asm-x86/hyperv.h" -EOF cat <$output/linux-headers/linux/virtio_config.h #include "standard-headers/linux/virtio_config.h" EOF From 20efc49ed625585809401d8293ad9267927a6a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 16 Apr 2018 12:17:41 +0100 Subject: [PATCH 0320/2380] accel: use g_strsplit for parsing accelerator names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of re-using the get_opt_name() method from QemuOpts to split a string on ':', just use g_strsplit(). Signed-off-by: Daniel P. Berrangé Message-Id: <20180416111743.8473-2-berrange@redhat.com> Signed-off-by: Paolo Bonzini Signed-off-by: Daniel P. Berrangé --- accel/accel.c | 16 +++++++--------- include/qemu/option.h | 1 - util/qemu-option.c | 3 ++- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/accel/accel.c b/accel/accel.c index 9cfab115d0..966b2d8f53 100644 --- a/accel/accel.c +++ b/accel/accel.c @@ -70,8 +70,8 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) void configure_accelerator(MachineState *ms) { - const char *accel, *p; - char buf[10]; + const char *accel; + char **accel_list, **tmp; int ret; bool accel_initialised = false; bool init_failed = false; @@ -83,13 +83,10 @@ void configure_accelerator(MachineState *ms) accel = "tcg"; } - p = accel; - while (!accel_initialised && *p != '\0') { - if (*p == ':') { - p++; - } - p = get_opt_name(buf, sizeof(buf), p, ':'); - acc = accel_find(buf); + accel_list = g_strsplit(accel, ":", 0); + + for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { + acc = accel_find(*tmp); if (!acc) { continue; } @@ -107,6 +104,7 @@ void configure_accelerator(MachineState *ms) accel_initialised = true; } } + g_strfreev(accel_list); if (!accel_initialised) { if (!init_failed) { diff --git a/include/qemu/option.h b/include/qemu/option.h index 306fdb5f7a..1cfe5cbc2d 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -28,7 +28,6 @@ #include "qemu/queue.h" -const char *get_opt_name(char *buf, int buf_size, const char *p, char delim); const char *get_opt_value(char *buf, int buf_size, const char *p); void parse_option_size(const char *name, const char *value, diff --git a/util/qemu-option.c b/util/qemu-option.c index d0756fda58..baca40fb94 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -49,7 +49,8 @@ * The return value is the position of the delimiter/zero byte after the option * name in p. */ -const char *get_opt_name(char *buf, int buf_size, const char *p, char delim) +static const char *get_opt_name(char *buf, int buf_size, const char *p, + char delim) { char *q; From e652714f98f22e8882e88e3d563b025c5b00feec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 16 Apr 2018 12:17:42 +0100 Subject: [PATCH 0321/2380] opts: don't silently truncate long parameter keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing QemuOpts parsing code uses a fixed size 128 byte buffer for storing the parameter keys. If a key exceeded this size it was silently truncate and no error reported to the user. This behaviour was reasonable & harmless because traditionally the key names are all statically declared, and it was known that no code was declaring a key longer than 127 bytes. This assumption, however, ceased to be valid once the block layer added support for dot-separate compound keys. This syntax allows for keys that can be arbitrarily long, limited only by the number of block drivers you can stack up. With this usage, silently truncating the key name can never lead to correct behaviour. Hopefully such truncation would turn into an error, when the block code then tried to extract options later, but there's no guarantee that will happen. It is conceivable that an option specified by the user may be truncated and then ignored. This could have serious consequences, possibly even leading to security problems if the ignored option set a security relevant parameter. If the operating system didn't limit the user's argv when spawning QEMU, the code should honour whatever length arguments were given without imposing its own length restrictions. This patch thus changes the code to use a heap allocated buffer for storing the keys during parsing, lifting the arbitrary length restriction. Signed-off-by: Daniel P. Berrangé Message-Id: <20180416111743.8473-3-berrange@redhat.com> Signed-off-by: Paolo Bonzini Signed-off-by: Daniel P. Berrangé --- tests/test-qemu-opts.c | 18 ----------------- util/qemu-option.c | 44 +++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c index 77dd72b403..7092e216f7 100644 --- a/tests/test-qemu-opts.c +++ b/tests/test-qemu-opts.c @@ -459,8 +459,6 @@ static void test_opts_parse(void) { Error *err = NULL; QemuOpts *opts; - char long_key[129]; - char *params; /* Nothing */ opts = qemu_opts_parse(&opts_list_03, "", false, &error_abort); @@ -471,22 +469,6 @@ static void test_opts_parse(void) g_assert_cmpuint(opts_count(opts), ==, 1); g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); - /* Long key */ - memset(long_key, 'a', 127); - long_key[127] = 'z'; - long_key[128] = 0; - params = g_strdup_printf("%s=v", long_key); - opts = qemu_opts_parse(&opts_list_03, params + 1, NULL, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpstr(qemu_opt_get(opts, long_key + 1), ==, "v"); - - /* Overlong key gets truncated */ - opts = qemu_opts_parse(&opts_list_03, params, NULL, &error_abort); - g_assert(opts_count(opts) == 1); - long_key[127] = 0; - g_assert_cmpstr(qemu_opt_get(opts, long_key), ==, "v"); - g_free(params); - /* Multiple keys, last one wins */ opts = qemu_opts_parse(&opts_list_03, "a=1,b=2,,x,a=3", false, &error_abort); diff --git a/util/qemu-option.c b/util/qemu-option.c index baca40fb94..fa1a9f17fc 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -43,27 +43,23 @@ * first byte of the option name) * * The option name is delimited by delim (usually , or =) or the string end - * and is copied into buf. If the option name is longer than buf_size, it is - * truncated. buf is always zero terminated. + * and is copied into option. The caller is responsible for free'ing option + * when no longer required. * * The return value is the position of the delimiter/zero byte after the option * name in p. */ -static const char *get_opt_name(char *buf, int buf_size, const char *p, - char delim) +static const char *get_opt_name(const char *p, char **option, char delim) { - char *q; + char *offset = strchr(p, delim); - q = buf; - while (*p != '\0' && *p != delim) { - if (q && (q - buf) < buf_size - 1) - *q++ = *p; - p++; + if (offset) { + *option = g_strndup(p, offset - p); + return offset; + } else { + *option = g_strdup(p); + return p + strlen(p); } - if (q) - *q = '\0'; - - return p; } /* @@ -758,7 +754,8 @@ void qemu_opts_print(QemuOpts *opts, const char *separator) static void opts_do_parse(QemuOpts *opts, const char *params, const char *firstname, bool prepend, Error **errp) { - char option[128], value[1024]; + char *option = NULL; + char value[1024]; const char *p,*pe,*pc; Error *local_err = NULL; @@ -769,11 +766,11 @@ static void opts_do_parse(QemuOpts *opts, const char *params, /* found "foo,more" */ if (p == params && firstname) { /* implicitly named first option */ - pstrcpy(option, sizeof(option), firstname); + option = g_strdup(firstname); p = get_opt_value(value, sizeof(value), p); } else { /* option without value, probably a flag */ - p = get_opt_name(option, sizeof(option), p, ','); + p = get_opt_name(p, &option, ','); if (strncmp(option, "no", 2) == 0) { memmove(option, option+2, strlen(option+2)+1); pstrcpy(value, sizeof(value), "off"); @@ -783,10 +780,8 @@ static void opts_do_parse(QemuOpts *opts, const char *params, } } else { /* found "foo=bar,more" */ - p = get_opt_name(option, sizeof(option), p, '='); - if (*p != '=') { - break; - } + p = get_opt_name(p, &option, '='); + assert(*p == '='); p++; p = get_opt_value(value, sizeof(value), p); } @@ -795,13 +790,18 @@ static void opts_do_parse(QemuOpts *opts, const char *params, opt_set(opts, option, value, prepend, &local_err); if (local_err) { error_propagate(errp, local_err); - return; + goto cleanup; } } if (*p != ',') { break; } + g_free(option); + option = NULL; } + + cleanup: + g_free(option); } /** From 950c4e6c94b15cd0d8b63891dddd7a8dbf458e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 16 Apr 2018 12:17:43 +0100 Subject: [PATCH 0322/2380] opts: don't silently truncate long option values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing QemuOpts parsing code uses a fixed size 1024 byte buffer for storing the option values. If a value exceeded this size it was silently truncated and no error reported to the user. Long option values is not a common scenario, but it is conceivable that they will happen. eg if the user has a very deeply nested filesystem it would be possible to come up with a disk path that was > 1024 bytes. Most of the time if such data was silently truncated, the user would get an error about opening a non-existant disk. If they're unlucky though, QEMU might use a completely different disk image from another VM, which could be considered a security issue. Another example program was in using the -smbios command line arg with very large data blobs. In this case the silent truncation will be providing semantically incorrect data to the guest OS for SMBIOS tables. If the operating system didn't limit the user's argv when spawning QEMU, the code should honour whatever length arguments were given without imposing its own length restrictions. This patch thus changes the code to use a heap allocated buffer for storing the values during parsing, lifting the arbitrary length restriction. Signed-off-by: Daniel P. Berrangé Message-Id: <20180416111743.8473-4-berrange@redhat.com> Signed-off-by: Paolo Bonzini Signed-off-by: Daniel P. Berrangé --- hw/i386/multiboot.c | 33 +++++++----- include/qemu/option.h | 2 +- util/qemu-option.c | 113 ++++++++++++++++++++++-------------------- 3 files changed, 82 insertions(+), 66 deletions(-) diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 5bc0a2cddb..7a2953e26f 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -291,12 +291,16 @@ int load_multiboot(FWCfgState *fw_cfg, cmdline_len = strlen(kernel_filename) + 1; cmdline_len += strlen(kernel_cmdline) + 1; if (initrd_filename) { - const char *r = initrd_filename; + const char *r = get_opt_value(initrd_filename, NULL); cmdline_len += strlen(r) + 1; mbs.mb_mods_avail = 1; - while (*(r = get_opt_value(NULL, 0, r))) { - mbs.mb_mods_avail++; - r++; + while (1) { + mbs.mb_mods_avail++; + r = get_opt_value(r, NULL); + if (!*r) { + break; + } + r++; } } @@ -313,7 +317,8 @@ int load_multiboot(FWCfgState *fw_cfg, if (initrd_filename) { const char *next_initrd; - char not_last, tmpbuf[strlen(initrd_filename) + 1]; + char not_last; + char *one_file = NULL; mbs.offset_mods = mbs.mb_buf_size; @@ -322,24 +327,26 @@ int load_multiboot(FWCfgState *fw_cfg, int mb_mod_length; uint32_t offs = mbs.mb_buf_size; - next_initrd = get_opt_value(tmpbuf, sizeof(tmpbuf), initrd_filename); + next_initrd = get_opt_value(initrd_filename, &one_file); not_last = *next_initrd; /* if a space comes after the module filename, treat everything after that as parameters */ - hwaddr c = mb_add_cmdline(&mbs, tmpbuf); - if ((next_space = strchr(tmpbuf, ' '))) + hwaddr c = mb_add_cmdline(&mbs, one_file); + next_space = strchr(one_file, ' '); + if (next_space) { *next_space = '\0'; - mb_debug("multiboot loading module: %s", tmpbuf); - mb_mod_length = get_image_size(tmpbuf); + } + mb_debug("multiboot loading module: %s", one_file); + mb_mod_length = get_image_size(one_file); if (mb_mod_length < 0) { - error_report("Failed to open file '%s'", tmpbuf); + error_report("Failed to open file '%s'", one_file); exit(1); } mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_mod_length + mbs.mb_buf_size); mbs.mb_buf = g_realloc(mbs.mb_buf, mbs.mb_buf_size); - load_image(tmpbuf, (unsigned char *)mbs.mb_buf + offs); + load_image(one_file, (unsigned char *)mbs.mb_buf + offs); mb_add_mod(&mbs, mbs.mb_buf_phys + offs, mbs.mb_buf_phys + offs + mb_mod_length, c); @@ -347,6 +354,8 @@ int load_multiboot(FWCfgState *fw_cfg, (char *)mbs.mb_buf + offs, (char *)mbs.mb_buf + offs + mb_mod_length, c); initrd_filename = next_initrd+1; + g_free(one_file); + one_file = NULL; } while (not_last); } diff --git a/include/qemu/option.h b/include/qemu/option.h index 1cfe5cbc2d..3dfb4493cc 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -28,7 +28,7 @@ #include "qemu/queue.h" -const char *get_opt_value(char *buf, int buf_size, const char *p); +const char *get_opt_value(const char *p, char **value); void parse_option_size(const char *name, const char *value, uint64_t *ret, Error **errp); diff --git a/util/qemu-option.c b/util/qemu-option.c index fa1a9f17fc..58d1c23893 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -70,25 +70,37 @@ static const char *get_opt_name(const char *p, char **option, char delim) * delimiter is fixed to be comma which starts a new option. To specify an * option value that contains commas, double each comma. */ -const char *get_opt_value(char *buf, int buf_size, const char *p) +const char *get_opt_value(const char *p, char **value) { - char *q; + size_t capacity = 0, length; + const char *offset; - q = buf; - while (*p != '\0') { - if (*p == ',') { - if (*(p + 1) != ',') - break; - p++; + *value = NULL; + while (1) { + offset = strchr(p, ','); + if (!offset) { + offset = p + strlen(p); } - if (q && (q - buf) < buf_size - 1) - *q++ = *p; - p++; - } - if (q) - *q = '\0'; - return p; + length = offset - p; + if (*offset != '\0' && *(offset + 1) == ',') { + length++; + } + if (value) { + *value = g_renew(char, *value, capacity + length + 1); + strncpy(*value + capacity, p, length); + (*value)[capacity + length] = '\0'; + } + capacity += length; + if (*offset == '\0' || + *(offset + 1) != ',') { + break; + } + + p += (offset - p) + 2; + } + + return offset; } static void parse_option_bool(const char *name, const char *value, bool *ret, @@ -162,50 +174,43 @@ void parse_option_size(const char *name, const char *value, bool has_help_option(const char *param) { - size_t buflen = strlen(param) + 1; - char *buf = g_malloc(buflen); const char *p = param; bool result = false; - while (*p) { - p = get_opt_value(buf, buflen, p); + while (*p && !result) { + char *value; + + p = get_opt_value(p, &value); if (*p) { p++; } - if (is_help_option(buf)) { - result = true; - goto out; - } + result = is_help_option(value); + g_free(value); } -out: - g_free(buf); return result; } -bool is_valid_option_list(const char *param) +bool is_valid_option_list(const char *p) { - size_t buflen = strlen(param) + 1; - char *buf = g_malloc(buflen); - const char *p = param; - bool result = true; + char *value = NULL; + bool result = false; while (*p) { - p = get_opt_value(buf, buflen, p); - if (*p && !*++p) { - result = false; + p = get_opt_value(p, &value); + if ((*p && !*++p) || + (!*value || *value == ',')) { goto out; } - if (!*buf || *buf == ',') { - result = false; - goto out; - } + g_free(value); + value = NULL; } + result = true; out: - g_free(buf); + g_free(value); return result; } @@ -487,7 +492,7 @@ int qemu_opt_unset(QemuOpts *opts, const char *name) } } -static void opt_set(QemuOpts *opts, const char *name, const char *value, +static void opt_set(QemuOpts *opts, const char *name, char *value, bool prepend, Error **errp) { QemuOpt *opt; @@ -496,6 +501,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value, desc = find_desc_by_name(opts->list->desc, name); if (!desc && !opts_accepts_any(opts)) { + g_free(value); error_setg(errp, QERR_INVALID_PARAMETER, name); return; } @@ -509,8 +515,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value, QTAILQ_INSERT_TAIL(&opts->head, opt, next); } opt->desc = desc; - opt->str = g_strdup(value); - assert(opt->str); + opt->str = value; qemu_opt_parse(opt, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -521,7 +526,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value, void qemu_opt_set(QemuOpts *opts, const char *name, const char *value, Error **errp) { - opt_set(opts, name, value, false, errp); + opt_set(opts, name, g_strdup(value), false, errp); } void qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, @@ -755,7 +760,7 @@ static void opts_do_parse(QemuOpts *opts, const char *params, const char *firstname, bool prepend, Error **errp) { char *option = NULL; - char value[1024]; + char *value = NULL; const char *p,*pe,*pc; Error *local_err = NULL; @@ -767,15 +772,15 @@ static void opts_do_parse(QemuOpts *opts, const char *params, if (p == params && firstname) { /* implicitly named first option */ option = g_strdup(firstname); - p = get_opt_value(value, sizeof(value), p); + p = get_opt_value(p, &value); } else { /* option without value, probably a flag */ p = get_opt_name(p, &option, ','); if (strncmp(option, "no", 2) == 0) { memmove(option, option+2, strlen(option+2)+1); - pstrcpy(value, sizeof(value), "off"); + value = g_strdup("off"); } else { - pstrcpy(value, sizeof(value), "on"); + value = g_strdup("on"); } } } else { @@ -783,11 +788,12 @@ static void opts_do_parse(QemuOpts *opts, const char *params, p = get_opt_name(p, &option, '='); assert(*p == '='); p++; - p = get_opt_value(value, sizeof(value), p); + p = get_opt_value(p, &value); } if (strcmp(option, "id") != 0) { /* store and parse */ opt_set(opts, option, value, prepend, &local_err); + value = NULL; if (local_err) { error_propagate(errp, local_err); goto cleanup; @@ -797,11 +803,13 @@ static void opts_do_parse(QemuOpts *opts, const char *params, break; } g_free(option); - option = NULL; + g_free(value); + option = value = NULL; } cleanup: g_free(option); + g_free(value); } /** @@ -820,7 +828,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, bool permit_abbrev, bool defaults, Error **errp) { const char *firstname; - char value[1024], *id = NULL; + char *id = NULL; const char *p; QemuOpts *opts; Error *local_err = NULL; @@ -829,11 +837,9 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, firstname = permit_abbrev ? list->implied_opt_name : NULL; if (strncmp(params, "id=", 3) == 0) { - get_opt_value(value, sizeof(value), params+3); - id = value; + get_opt_value(params + 3, &id); } else if ((p = strstr(params, ",id=")) != NULL) { - get_opt_value(value, sizeof(value), p+4); - id = value; + get_opt_value(p + 4, &id); } /* @@ -845,6 +851,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, */ assert(!defaults || list->merge_lists); opts = qemu_opts_create(list, id, !defaults, &local_err); + g_free(id); if (opts == NULL) { error_propagate(errp, local_err); return NULL; From bf3175b49952628f96d72d1247d8bb3aa5c2466c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 27 Apr 2018 15:11:26 +0200 Subject: [PATCH 0323/2380] target/i386: sev: fix memory leaks Reported by Coverity. Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index c01167143f..2395171acf 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -430,7 +430,8 @@ static int sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, size_t *cert_chain_len) { - guchar *pdh_data, *cert_chain_data; + guchar *pdh_data = NULL; + guchar *cert_chain_data = NULL; struct sev_user_data_pdh_cert_export export = {}; int err, r; @@ -471,8 +472,9 @@ e_free: SevCapability * sev_get_capabilities(void) { - SevCapability *cap; - guchar *pdh_data, *cert_chain_data; + SevCapability *cap = NULL; + guchar *pdh_data = NULL; + guchar *cert_chain_data = NULL; size_t pdh_len = 0, cert_chain_len = 0; uint32_t ebx; int fd; @@ -486,7 +488,7 @@ sev_get_capabilities(void) if (sev_get_pdh_info(fd, &pdh_data, &pdh_len, &cert_chain_data, &cert_chain_len)) { - return NULL; + goto out; } cap = g_new0(SevCapability, 1); @@ -502,9 +504,9 @@ sev_get_capabilities(void) */ cap->reduced_phys_bits = 1; +out: g_free(pdh_data); g_free(cert_chain_data); - close(fd); return cap; } @@ -530,7 +532,7 @@ sev_launch_start(SEVState *s) { gsize sz; int ret = 1; - int fw_error; + int fw_error, rc; QSevGuestInfo *sev = s->sev_info; struct kvm_sev_launch_start *start; guchar *session = NULL, *dh_cert = NULL; @@ -543,7 +545,7 @@ sev_launch_start(SEVState *s) &error_abort); if (sev->session_file) { if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { - return 1; + goto out; } start->session_uaddr = (unsigned long)session; start->session_len = sz; @@ -551,18 +553,18 @@ sev_launch_start(SEVState *s) if (sev->dh_cert_file) { if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { - return 1; + goto out; } start->dh_uaddr = (unsigned long)dh_cert; start->dh_len = sz; } trace_kvm_sev_launch_start(start->policy, session, dh_cert); - ret = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); - if (ret < 0) { + rc = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); + if (rc < 0) { error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); - return 1; + goto out; } object_property_set_int(OBJECT(sev), start->handle, "handle", @@ -570,12 +572,13 @@ sev_launch_start(SEVState *s) sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE); s->handle = start->handle; s->policy = start->policy; + ret = 0; +out: g_free(start); g_free(session); g_free(dh_cert); - - return 0; + return ret; } static int @@ -712,7 +715,7 @@ sev_guest_init(const char *id) uint32_t host_cbitpos; struct sev_user_data_status status = {}; - s = g_new0(SEVState, 1); + sev_state = s = g_new0(SEVState, 1); s->sev_info = lookup_sev_guest_info(id); if (!s->sev_info) { error_report("%s: '%s' is not a valid '%s' object", @@ -720,7 +723,6 @@ sev_guest_init(const char *id) goto err; } - sev_state = s; s->state = SEV_STATE_UNINIT; host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); From 454012997e0818e371750893c87c28c155ef14e3 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 4 May 2018 17:13:10 +0200 Subject: [PATCH 0324/2380] qemu-options: Mark -virtioconsole as deprecated The qemu-doc already states that this option is only maintained for backward compatibility and "-device virtconsole" should be used instead. So let's take the next step and mark this option officially as deprecated. Reviewed-by: Markus Armbruster Signed-off-by: Thomas Huth Message-Id: <1525446790-16139-1-git-send-email-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- qemu-doc.texi | 5 +++++ qemu-options.hx | 5 +---- vl.c | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/qemu-doc.texi b/qemu-doc.texi index 0ed0f19e6b..4add403bf1 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2931,6 +2931,11 @@ The @code{-localtime} option has been replaced by @code{-rtc base=localtime}. The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}. +@subsection -virtioconsole (since 2.13.0) + +Option @option{-virtioconsole} has been replaced by +@option{-device virtconsole}. + @section qemu-img command line arguments @subsection convert -s (since 2.0.0) diff --git a/qemu-options.hx b/qemu-options.hx index c611766390..091ded6e0c 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3675,10 +3675,7 @@ STEXI @item -virtioconsole @var{c} @findex -virtioconsole Set virtio console. - -This option is maintained for backward compatibility. - -Please use @code{-device virtconsole} for the new way of invocation. +This option is deprecated, please use @option{-device virtconsole} instead. ETEXI DEF("show-cursor", 0, QEMU_OPTION_show_cursor, \ diff --git a/vl.c b/vl.c index 12e31d1aa9..b928e3e439 100644 --- a/vl.c +++ b/vl.c @@ -3587,6 +3587,8 @@ int main(int argc, char **argv, char **envp) } break; case QEMU_OPTION_virtiocon: + warn_report("This option is deprecated, " + "use '-device virtconsole' instead"); add_device_config(DEV_VIRTCON, optarg); default_virtcon = 0; if (strncmp(optarg, "mon:", 4) == 0) { From 281b95231d79a7a7ecc93a51ebb8e68b1ce7f1e5 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 4 May 2018 19:01:06 +0200 Subject: [PATCH 0325/2380] qemu-options: Remove remainders of the -tdf option The -tdf options has been removed with d07aa197c5a1556449361a0cbb5108e2, but apparently I forgot to remove the corresponding two lines from qemu-options.hx, so this option is still "available" and just silently ignored. Kill it now for good. Reviewed-by: Markus Armbruster Signed-off-by: Thomas Huth Message-Id: <1525453270-23074-2-git-send-email-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- qemu-options.hx | 3 --- 1 file changed, 3 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index 091ded6e0c..50c906c85b 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3921,9 +3921,6 @@ DEF("no-kvm-pit-reinjection", 0, QEMU_OPTION_no_kvm_pit_reinjection, HXCOMM Deprecated by -machine kernel_irqchip=on|off property DEF("no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip, "", QEMU_ARCH_I386) -HXCOMM Deprecated (ignored) -DEF("tdf", 0, QEMU_OPTION_tdf,"", QEMU_ARCH_ALL) - DEF("msg", HAS_ARG, QEMU_OPTION_msg, "-msg timestamp[=on|off]\n" " change the format of messages\n" From 1217d6ca2bf28c0febe1bd7d5b3fa912bbf6af2a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 4 May 2018 19:01:07 +0200 Subject: [PATCH 0326/2380] qemu-options: Bail out on unsupported options instead of silently ignoring them The dangling remainder of the -tdf option revealed a deficiency in our option parsing: Options that have been declared, but are not supported in the switch-case statement in vl.c and not handled in the OS-specifc os_parse_cmd_args() functions are currently silently ignored. We should rather tell the users that they specified something that we can not handle, so let's print an error message and exit instead. Reported-by: Markus Armbruster Signed-off-by: Thomas Huth Message-Id: <1525453270-23074-3-git-send-email-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- include/qemu-common.h | 2 +- os-posix.c | 6 +++++- os-win32.c | 4 ++-- vl.c | 5 ++++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/qemu-common.h b/include/qemu-common.h index 8a4f63c9de..85f4749aef 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -137,7 +137,7 @@ char *qemu_find_file(int type, const char *name); /* OS specific functions */ void os_setup_early_signal_handling(void); char *os_find_datadir(void); -void os_parse_cmd_args(int index, const char *optarg); +int os_parse_cmd_args(int index, const char *optarg); #include "qemu/module.h" diff --git a/os-posix.c b/os-posix.c index 24eb7007dc..9ce6f74513 100644 --- a/os-posix.c +++ b/os-posix.c @@ -165,7 +165,7 @@ static bool os_parse_runas_uid_gid(const char *optarg) * Parse OS specific command line options. * return 0 if option handled, -1 otherwise */ -void os_parse_cmd_args(int index, const char *optarg) +int os_parse_cmd_args(int index, const char *optarg) { switch (index) { #ifdef CONFIG_SLIRP @@ -199,7 +199,11 @@ void os_parse_cmd_args(int index, const char *optarg) fips_set_state(true); break; #endif + default: + return -1; } + + return 0; } static void change_process_uid(void) diff --git a/os-win32.c b/os-win32.c index 586a7c7d49..0674f94b57 100644 --- a/os-win32.c +++ b/os-win32.c @@ -93,9 +93,9 @@ void os_set_line_buffering(void) * Parse OS specific command line options. * return 0 if option handled, -1 otherwise */ -void os_parse_cmd_args(int index, const char *optarg) +int os_parse_cmd_args(int index, const char *optarg) { - return; + return -1; } int qemu_create_pidfile(const char *filename) diff --git a/vl.c b/vl.c index b928e3e439..a23acb2861 100644 --- a/vl.c +++ b/vl.c @@ -4033,7 +4033,10 @@ int main(int argc, char **argv, char **envp) } break; default: - os_parse_cmd_args(popt->index, optarg); + if (os_parse_cmd_args(popt->index, optarg)) { + error_report("Option not supported in this build"); + exit(1); + } } } } From ec910caba92ea6db882a3b3d2d992caea6a1c160 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 4 May 2018 19:01:08 +0200 Subject: [PATCH 0327/2380] qemu-options: Remove deprecated -no-kvm-pit-reinjection Deprecated since the beginning when it was added for compatibility with the ancient qemu-kvm fork of QEMU, and it even printed out the deprecation warning since right from the start (i.e. QEMU v1.3.0), so it's really time to remove this now. Reviewed-by: Markus Armbruster Signed-off-by: Thomas Huth Message-Id: <1525453270-23074-4-git-send-email-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- qemu-doc.texi | 5 ----- qemu-options.hx | 4 ---- vl.c | 12 ------------ 3 files changed, 21 deletions(-) diff --git a/qemu-doc.texi b/qemu-doc.texi index 4add403bf1..85a7a397d9 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2786,11 +2786,6 @@ which is the default. @section System emulator command line arguments -@subsection -no-kvm-pit-reinjection (since 1.3.0) - -The ``-no-kvm-pit-reinjection'' argument is now a -synonym for setting ``-global kvm-pit.lost_tick_policy=discard''. - @subsection -no-kvm-irqchip (since 1.3.0) The ``-no-kvm-irqchip'' argument is now a synonym for diff --git a/qemu-options.hx b/qemu-options.hx index 50c906c85b..dfff698a93 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3914,10 +3914,6 @@ ETEXI HXCOMM Deprecated by -machine accel=tcg property DEF("no-kvm", 0, QEMU_OPTION_no_kvm, "", QEMU_ARCH_I386) -HXCOMM Deprecated by kvm-pit driver properties -DEF("no-kvm-pit-reinjection", 0, QEMU_OPTION_no_kvm_pit_reinjection, - "", QEMU_ARCH_I386) - HXCOMM Deprecated by -machine kernel_irqchip=on|off property DEF("no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip, "", QEMU_ARCH_I386) diff --git a/vl.c b/vl.c index a23acb2861..23d55a42f7 100644 --- a/vl.c +++ b/vl.c @@ -3696,18 +3696,6 @@ int main(int argc, char **argv, char **envp) olist = qemu_find_opts("machine"); qemu_opts_parse_noisily(olist, "accel=tcg", false); break; - case QEMU_OPTION_no_kvm_pit_reinjection: { - static GlobalProperty kvm_pit_lost_tick_policy = { - .driver = "kvm-pit", - .property = "lost_tick_policy", - .value = "discard", - }; - - warn_report("deprecated, replaced by " - "-global kvm-pit.lost_tick_policy=discard"); - qdev_prop_register_global(&kvm_pit_lost_tick_policy); - break; - } case QEMU_OPTION_accel: accel_opts = qemu_opts_parse_noisily(qemu_find_opts("accel"), optarg, true); From d8ab6cb7985e6151b9dd0025aa8a9ee998bdc958 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 4 May 2018 19:01:09 +0200 Subject: [PATCH 0328/2380] qemu-options: Remove deprecated -no-kvm-irqchip We've never documented this option in our qemu-doc, so apart from the users that already used the old qemu-kvm fork before, most users should not be aware of this option at all. It's been marked as deprecated in the source code for a long time already, and officially marked as deprecated in the documentation since QEMU v2.10, so it should be fine to remove this now. Reviewed-by: Markus Armbruster Signed-off-by: Thomas Huth Message-Id: <1525453270-23074-5-git-send-email-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- qemu-doc.texi | 5 ----- qemu-options.hx | 3 --- vl.c | 5 ----- 3 files changed, 13 deletions(-) diff --git a/qemu-doc.texi b/qemu-doc.texi index 85a7a397d9..bde542b065 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2786,11 +2786,6 @@ which is the default. @section System emulator command line arguments -@subsection -no-kvm-irqchip (since 1.3.0) - -The ``-no-kvm-irqchip'' argument is now a synonym for -setting ``-machine kernel_irqchip=off''. - @subsection -no-kvm (since 1.3.0) The ``-no-kvm'' argument is now a synonym for setting diff --git a/qemu-options.hx b/qemu-options.hx index dfff698a93..b2fefd12b6 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3914,9 +3914,6 @@ ETEXI HXCOMM Deprecated by -machine accel=tcg property DEF("no-kvm", 0, QEMU_OPTION_no_kvm, "", QEMU_ARCH_I386) -HXCOMM Deprecated by -machine kernel_irqchip=on|off property -DEF("no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip, "", QEMU_ARCH_I386) - DEF("msg", HAS_ARG, QEMU_OPTION_msg, "-msg timestamp[=on|off]\n" " change the format of messages\n" diff --git a/vl.c b/vl.c index 23d55a42f7..b9f6b42779 100644 --- a/vl.c +++ b/vl.c @@ -3138,11 +3138,6 @@ int main(int argc, char **argv, char **envp) exit(1); } switch(popt->index) { - case QEMU_OPTION_no_kvm_irqchip: { - olist = qemu_find_opts("machine"); - qemu_opts_parse_noisily(olist, "kernel_irqchip=off", false); - break; - } case QEMU_OPTION_cpu: /* hw initialization will check this */ cpu_model = optarg; From 45b47130f4b7b53eb1437e8301bfeb4e73490413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 4 May 2018 17:00:24 +0100 Subject: [PATCH 0329/2380] qemu-doc: provide details of supported build platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Describe the policy the project uses to decide which OS are supported as build platforms. This will: - Allow maintainers to determine when the minimum version of a 3rd party piece of software can be increased without negatively impacting supported platforms. - Allow tailoring of CI environments to match the intended supported build platforms. Signed-off-by: Daniel P. Berrangé Message-Id: <20180504160026.14017-2-berrange@redhat.com> Signed-off-by: Paolo Bonzini Signed-off-by: Daniel P. Berrangé --- qemu-doc.texi | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/qemu-doc.texi b/qemu-doc.texi index bde542b065..715bd336b4 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -39,6 +39,7 @@ * QEMU User space emulator:: * Implementation notes:: * Deprecated features:: +* Supported build platforms:: * License:: * Index:: @end menu @@ -2976,6 +2977,73 @@ The ``xlnx-zcu102'' machine has the same features and capabilites in QEMU. In order to prevent QEMU from automatically opening an image's backing chain, use ``"backing": null'' instead. +@node Supported build platforms +@appendix Supported build platforms + +QEMU aims to support building and executing on multiple host OS platforms. +This appendix outlines which platforms are the major build targets. These +platforms are used as the basis for deciding upon the minimum required +versions of 3rd party software QEMU depends on. The supported platforms +are the targets for automated testing performed by the project when patches +are submitted for review, and tested before and after merge. + +If a platform is not listed here, it does not imply that QEMU won't work. +If an unlisted platform has comparable software versions to a listed platform, +there is every expectation that it will work. Bug reports are welcome for +problems encountered on unlisted platforms unless they are clearly older +vintage than what is described here. + +Note that when considering software versions shipped in distros as support +targets, QEMU considers only the version number, and assumes the features in +that distro match the upstream release with the same version. In other words, +if a distro backports extra features to the software in their distro, QEMU +upstream code will not add explicit support for those backports, unless the +feature is auto-detectable in a manner that works for the upstream releases +too. + +The Repology site @url{https://repology.org} is a useful resource to identify +currently shipped versions of software in various operating systems, though +it does not cover all distros listed below. + +@section Linux OS + +For distributions with frequent, short-lifetime releases, the project will +aim to support all versions that are not end of life by their respective +vendors. For the purposes of identifying supported software versions, the +project will look at Fedora, Ubuntu, and openSUSE distros. Other short- +lifetime distros will be assumed to ship similar software versions. + +For distributions with long-lifetime releases, the project will aim to support +the most recent major version at all times. Support for the previous major +version will be dropped 2 years after the new major version is released. For +the purposes of identifying supported software versions, the project will look +at RHEL, Debian, Ubuntu LTS, and SLES distros. Other long-lifetime distros will +be assumed to ship similar software versions. + +@section Windows + +The project supports building with current versions of the MinGW toolchain, +hosted on Linux. + +@section macOS + +The project supports building with the two most recent versions of macOS, with +the current homebrew package set available. + +@section FreeBSD + +The project aims to support the all the versions which are not end of life. + +@section NetBSD + +The project aims to support the most recent major version at all times. Support +for the previous major version will be dropped 2 years after the new major +version is released. + +@section OpenBSD + +The project aims to support the all the versions which are not end of life. + @node License @appendix License From a666409f0df5dce113a5bd2c4c144a0792f2a4a3 Mon Sep 17 00:00:00 2001 From: KONRAD Frederic Date: Thu, 3 May 2018 17:17:16 +0200 Subject: [PATCH 0330/2380] riscv: requires libfdt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling on a machine without libfdt installed the configure script should try to get libfdt from the git or should die because otherwise CONFIG_LIBFDT is not set and the build process end in an error in the link phase.. eg: hw/riscv/virt.o: In function `riscv_virt_board_init': qemu/src/hw/riscv/virt.c:317: undefined reference to `qemu_fdt_setprop_cell' qemu/src/hw/riscv/virt.c:319: undefined reference to `qemu_fdt_setprop_cell' qemu/src/hw/riscv/virt.c:345: undefined reference to `qemu_fdt_dumpdtb' collect2: error: ld returned 1 exit status make[1]: *** [qemu-system-riscv64] Error 1 make: *** [subdir-riscv64-softmmu] Error 2 Cc: qemu-stable@nongnu.org Reviewed-by: Bastian Koppelmann Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Clark Signed-off-by: KONRAD Frederic Signed-off-by: Michael Clark Message-Id: <1525360636-18229-4-git-send-email-frederic.konrad@adacore.com> --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 1443422e83..479e9a787e 100755 --- a/configure +++ b/configure @@ -3761,7 +3761,7 @@ fi fdt_required=no for target in $target_list; do case $target in - aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu) + aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu) fdt_required=yes ;; esac From 7eb30ef0ba2eb59e7430d4848ae8d4bf4e50f768 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 May 2018 16:34:31 +0100 Subject: [PATCH 0331/2380] tcg/i386: Fix dup_vec in non-AVX2 codepath The VPUNPCKLD* instructions are all "non-destructive source", indicated by "NDS" in the encoding string in the x86 ISA manual. This means that they take two source operands, one of which is encoded in the VEX.vvvv field. We were incorrectly treating them as if they were destructive-source and passing 0 as the 'v' argument of tcg_out_vex_modrm(). This meant we were always using %xmm0 as one of the source operands, causing incorrect results if the register allocator happened to want to use something else. For instance the input AArch64 insn: DUP v26.16b, w21 which becomes TCG IR ops: dup_vec v128,e8,tmp2,x21 st_vec v128,e8,tmp2,env,$0xa40 was assembled to: 0x607c568c: c4 c1 7a 7e 86 e8 00 00 vmovq 0xe8(%r14), %xmm0 0x607c5694: 00 0x607c5695: c5 f9 60 c8 vpunpcklbw %xmm0, %xmm0, %xmm1 0x607c5699: c5 f9 61 c9 vpunpcklwd %xmm1, %xmm0, %xmm1 0x607c569d: c5 f9 70 c9 00 vpshufd $0, %xmm1, %xmm1 0x607c56a2: c4 c1 7a 7f 8e 40 0a 00 vmovdqu %xmm1, 0xa40(%r14) 0x607c56aa: 00 when the vpunpcklwd insn should be "%xmm1, %xmm1, %xmm1". This resulted in our incorrectly setting the output vector to q26=0000320000003200:0000320000003200 when given an input of x21 == 0000000002803200 rather than the expected all-zeroes. Pass the correct source register number to tcg_out_vex_modrm() for these insns. Fixes: 770c2fc7bb70804a Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Message-Id: <20180504153431.5169-1-peter.maydell@linaro.org> Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.inc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index d7e59e79c5..5357909fff 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -854,11 +854,11 @@ static void tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, switch (vece) { case MO_8: /* ??? With zero in a register, use PSHUFB. */ - tcg_out_vex_modrm(s, OPC_PUNPCKLBW, r, 0, a); + tcg_out_vex_modrm(s, OPC_PUNPCKLBW, r, a, a); a = r; /* FALLTHRU */ case MO_16: - tcg_out_vex_modrm(s, OPC_PUNPCKLWD, r, 0, a); + tcg_out_vex_modrm(s, OPC_PUNPCKLWD, r, a, a); a = r; /* FALLTHRU */ case MO_32: @@ -867,7 +867,7 @@ static void tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, tcg_out8(s, 0); break; case MO_64: - tcg_out_vex_modrm(s, OPC_PUNPCKLQDQ, r, 0, a); + tcg_out_vex_modrm(s, OPC_PUNPCKLQDQ, r, a, a); break; default: g_assert_not_reached(); From abebf92597186be2bc48d487235da28b1127860f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 May 2018 19:18:59 +0000 Subject: [PATCH 0332/2380] tcg: Limit the number of ops in a TB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 6001f7729e12 we partially attempt to address the branch displacement overflow caused by 15fa08f845. However, gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/vqtbX.c is a testcase that contains a TB so large as to overflow anyway. The limit here of 8000 ops produces a maximum output TB size of 24112 bytes on a ppc64le host with that test case. This is still much less than the maximum forward branch distance of 32764 bytes. Cc: qemu-stable@nongnu.org Fixes: 15fa08f845 ("tcg: Dynamically allocate TCGOps") Reviewed-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tcg.c | 3 +++ tcg/tcg.h | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index 551caf1c53..6eeebe0624 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -866,6 +866,7 @@ void tcg_func_start(TCGContext *s) /* No temps have been previously allocated for size or locality. */ memset(s->free_temps, 0, sizeof(s->free_temps)); + s->nb_ops = 0; s->nb_labels = 0; s->current_frame_offset = s->frame_start; @@ -1956,6 +1957,7 @@ void tcg_op_remove(TCGContext *s, TCGOp *op) { QTAILQ_REMOVE(&s->ops, op, link); QTAILQ_INSERT_TAIL(&s->free_ops, op, link); + s->nb_ops--; #ifdef CONFIG_PROFILER atomic_set(&s->prof.del_op_count, s->prof.del_op_count + 1); @@ -1975,6 +1977,7 @@ static TCGOp *tcg_op_alloc(TCGOpcode opc) } memset(op, 0, offsetof(TCGOp, link)); op->opc = opc; + s->nb_ops++; return op; } diff --git a/tcg/tcg.h b/tcg/tcg.h index 75fbad128b..88378be310 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -655,6 +655,7 @@ struct TCGContext { int nb_globals; int nb_temps; int nb_indirects; + int nb_ops; /* goto_tb support */ tcg_insn_unit *code_buf; @@ -844,7 +845,12 @@ static inline TCGOp *tcg_last_op(void) /* Test for whether to terminate the TB for using too many opcodes. */ static inline bool tcg_op_buf_full(void) { - return false; + /* This is not a hard limit, it merely stops translation when + * we have produced "enough" opcodes. We want to limit TB size + * such that a RISC host can reasonably use a 16-bit signed + * branch within the TB. + */ + return tcg_ctx->nb_ops >= 8000; } /* pool based memory allocation */ From 33572269a54ba6339ce00537abfa434e4ffc95c2 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 10 Apr 2018 11:11:04 -0400 Subject: [PATCH 0333/2380] target/riscv: avoid integer overflow in next_page PC check If the PC is in the last page of the address space, next_page_start overflows to 0. Fix it. Reported-by: Richard Henderson Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Reviewed-by: Michael Clark Acked-by: Bastian Koppelmann Cc: Michael Clark Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/riscv/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index c0e6a044d3..a98033ca77 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1850,11 +1850,11 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) CPURISCVState *env = cs->env_ptr; DisasContext ctx; target_ulong pc_start; - target_ulong next_page_start; + target_ulong page_start; int num_insns; int max_insns; pc_start = tb->pc; - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; ctx.pc = pc_start; /* once we have GDB, the rest of the translate.c implementation should be @@ -1904,7 +1904,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) if (cs->singlestep_enabled) { break; } - if (ctx.pc >= next_page_start) { + if (ctx.pc - page_start >= TARGET_PAGE_SIZE) { break; } if (tcg_op_buf_full()) { From 3ac5e413c00511d10d69b8b55fc06cc0c54bd45f Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 10 Apr 2018 10:46:45 -0400 Subject: [PATCH 0334/2380] target/cris: avoid integer overflow in next_page PC check If the PC is in the last page of the address space, next_page_start overflows to 0. Fix it. Reviewed-by: Richard Henderson Cc: "Edgar E. Iglesias" Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/cris/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/cris/translate.c b/target/cris/translate.c index f51a731db9..64b9ec6649 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -3091,7 +3091,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) unsigned int insn_len; struct DisasContext ctx; struct DisasContext *dc = &ctx; - uint32_t next_page_start; + uint32_t page_start; target_ulong npc; int num_insns; int max_insns; @@ -3138,7 +3138,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) dc->cpustate_changed = 0; - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { @@ -3234,7 +3234,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } while (!dc->is_jmp && !dc->cpustate_changed && !tcg_op_buf_full() && !singlestep - && (dc->pc < next_page_start) + && (dc->pc - page_start < TARGET_PAGE_SIZE) && num_insns < max_insns); if (dc->clear_locked_irq) { From 4302303d3c1f6f40b92791702f74d783a51ae60c Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 10 Apr 2018 10:46:58 -0400 Subject: [PATCH 0335/2380] target/lm32: avoid integer overflow in next_page PC check If the PC is in the last page of the address space, next_page_start overflows to 0. Fix it. Reviewed-by: Richard Henderson Acked-by: Michael Walle Cc: Michael Walle Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/lm32/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/lm32/translate.c b/target/lm32/translate.c index 2e1c5e6d01..fdd206a860 100644 --- a/target/lm32/translate.c +++ b/target/lm32/translate.c @@ -1055,7 +1055,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) LM32CPU *cpu = lm32_env_get_cpu(env); struct DisasContext ctx, *dc = &ctx; uint32_t pc_start; - uint32_t next_page_start; + uint32_t page_start; int num_insns; int max_insns; @@ -1075,7 +1075,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) pc_start &= ~3; } - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { @@ -1115,7 +1115,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep - && (dc->pc < next_page_start) + && (dc->pc - page_start < TARGET_PAGE_SIZE) && num_insns < max_insns); if (tb_cflags(tb) & CF_LAST_IO) { From 4e8b44b6c2350e02ca8e410d40022122b4038bd5 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 10 Apr 2018 10:47:12 -0400 Subject: [PATCH 0336/2380] target/xtensa: avoid integer overflow in next_page PC check If the PC is in the last page of the address space, next_page_start overflows to 0. Fix it. Reviewed-by: Richard Henderson Acked-by: Max Filippov Cc: Max Filippov Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/xtensa/translate.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 4f6d03059f..aad496347d 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -1061,8 +1061,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) int insn_count = 0; int max_insns = tb_cflags(tb) & CF_COUNT_MASK; uint32_t pc_start = tb->pc; - uint32_t next_page_start = - (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + uint32_t page_start = pc_start & TARGET_PAGE_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; @@ -1162,9 +1161,9 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) } } while (dc.is_jmp == DISAS_NEXT && insn_count < max_insns && - dc.pc < next_page_start && - dc.pc + xtensa_insn_len(env, &dc) <= next_page_start && - !tcg_op_buf_full()); + dc.pc - page_start < TARGET_PAGE_SIZE && + dc.pc - page_start + xtensa_insn_len(env, &dc) <= TARGET_PAGE_SIZE + && !tcg_op_buf_full()); done: reset_sar_tracker(&dc); if (dc.icount) { From 818c187781a0e9dcfd0cbaffd90c9485f2edcccd Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 10 Apr 2018 10:47:28 -0400 Subject: [PATCH 0337/2380] target/unicore32: avoid integer overflow in next_page PC check If the PC is in the last page of the address space, next_page_start overflows to 0. Fix it. Reviewed-by: Richard Henderson Cc: Guan Xuetao Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/unicore32/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/unicore32/translate.c b/target/unicore32/translate.c index 5b51f2166d..abe2ea8592 100644 --- a/target/unicore32/translate.c +++ b/target/unicore32/translate.c @@ -1875,7 +1875,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) CPUUniCore32State *env = cs->env_ptr; DisasContext dc1, *dc = &dc1; target_ulong pc_start; - uint32_t next_page_start; + uint32_t page_start; int num_insns; int max_insns; @@ -1894,7 +1894,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) cpu_F1s = tcg_temp_new_i32(); cpu_F0d = tcg_temp_new_i64(); cpu_F1d = tcg_temp_new_i64(); - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { @@ -1951,7 +1951,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && - dc->pc < next_page_start && + dc->pc - page_start < TARGET_PAGE_SIZE && num_insns < max_insns); if (tb_cflags(tb) & CF_LAST_IO) { From 5c433a7aac1a34c199ed5a7a07f4f71349562a8f Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 10 Apr 2018 10:47:41 -0400 Subject: [PATCH 0338/2380] target/tilegx: avoid integer overflow in next_page PC check If the PC is in the last page of the address space, next_page_start overflows to 0. Fix it. Reviewed-by: Richard Henderson Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/tilegx/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/tilegx/translate.c b/target/tilegx/translate.c index d63bf5bba3..6c53c5e767 100644 --- a/target/tilegx/translate.c +++ b/target/tilegx/translate.c @@ -2375,7 +2375,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) DisasContext ctx; DisasContext *dc = &ctx; uint64_t pc_start = tb->pc; - uint64_t next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + uint64_t page_start = pc_start & TARGET_PAGE_MASK; int num_insns = 0; int max_insns = tb_cflags(tb) & CF_COUNT_MASK; @@ -2415,7 +2415,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } dc->pc += TILEGX_BUNDLE_SIZE_IN_BYTES; if (num_insns >= max_insns - || dc->pc >= next_page_start + || (dc->pc - page_start >= TARGET_PAGE_SIZE) || tcg_op_buf_full()) { /* Ending the TB due to TB size or page boundary. Set PC. */ tcg_gen_movi_tl(cpu_pc, dc->pc); From 56371527f3f8d33be651046700d72489f4df505f Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 10 Apr 2018 10:47:51 -0400 Subject: [PATCH 0339/2380] target/microblaze: avoid integer overflow in next_page PC check If the PC is in the last page of the address space, next_page_start overflows to 0. Fix it. Reviewed-by: Richard Henderson Cc: "Edgar E. Iglesias" Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/microblaze/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 100883e2cc..0872dc9ded 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1635,7 +1635,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) uint32_t pc_start; struct DisasContext ctx; struct DisasContext *dc = &ctx; - uint32_t next_page_start, org_flags; + uint32_t page_start, org_flags; target_ulong npc; int num_insns; int max_insns; @@ -1661,7 +1661,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) cpu_abort(cs, "Microblaze: unaligned PC=%x\n", pc_start); } - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { @@ -1747,7 +1747,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } while (!dc->is_jmp && !dc->cpustate_changed && !tcg_op_buf_full() && !singlestep - && (dc->pc < next_page_start) + && (dc->pc - page_start < TARGET_PAGE_SIZE) && num_insns < max_insns); npc = dc->pc; From bfe7ad5be77a6a8925a7ab1628452c8942222102 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 10 Apr 2018 11:09:52 -0400 Subject: [PATCH 0340/2380] target/arm: avoid integer overflow in next_page PC check If the PC is in the last page of the address space, next_page_start overflows to 0. Fix it. Reviewed-by: Richard Henderson Cc: Peter Maydell Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/arm/translate.c | 11 +++++------ target/arm/translate.h | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index ad208867a7..0f6629f745 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -9930,7 +9930,7 @@ static bool thumb_insn_is_16bit(DisasContext *s, uint32_t insn) return false; } - if ((insn >> 11) == 0x1e && (s->pc < s->next_page_start - 3)) { + if ((insn >> 11) == 0x1e && s->pc - s->page_start < TARGET_PAGE_SIZE - 3) { /* 0b1111_0xxx_xxxx_xxxx : BL/BLX prefix, and the suffix * is not on the next page; we merge this into a 32-bit * insn. @@ -12301,8 +12301,7 @@ static int arm_tr_init_disas_context(DisasContextBase *dcbase, dc->is_ldex = false; dc->ss_same_el = false; /* Can't be true since EL_d must be AArch64 */ - dc->next_page_start = - (dc->base.pc_first & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + dc->page_start = dc->base.pc_first & TARGET_PAGE_MASK; /* If architectural single step active, limit to 1. */ if (is_singlestepping(dc)) { @@ -12312,7 +12311,7 @@ static int arm_tr_init_disas_context(DisasContextBase *dcbase, /* ARM is a fixed-length ISA. Bound the number of insns to execute to those left on the page. */ if (!dc->thumb) { - int bound = (dc->next_page_start - dc->base.pc_first) / 4; + int bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; max_insns = MIN(max_insns, bound); } @@ -12584,8 +12583,8 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * but isn't very efficient). */ if (dc->base.is_jmp == DISAS_NEXT - && (dc->pc >= dc->next_page_start - || (dc->pc >= dc->next_page_start - 3 + && (dc->pc - dc->page_start >= TARGET_PAGE_SIZE + || (dc->pc - dc->page_start >= TARGET_PAGE_SIZE - 3 && insn_crosses_page(env, dc)))) { dc->base.is_jmp = DISAS_TOO_MANY; } diff --git a/target/arm/translate.h b/target/arm/translate.h index 4428c98e2e..37a1bba056 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -9,7 +9,7 @@ typedef struct DisasContext { DisasContextBase base; target_ulong pc; - target_ulong next_page_start; + target_ulong page_start; uint32_t insn; /* Nonzero if this instruction has been conditionally skipped. */ int condjmp; From 071bd2b628cedbb640be8ca6c25d1949b2e6fe4a Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 10 Apr 2018 11:12:26 -0400 Subject: [PATCH 0341/2380] target/s390x: avoid integer overflow in next_page PC check If the PC is in the last page of the address space, next_page_start overflows to 0. Fix it. Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Reviewed-by: David Hildenbrand Acked-by: Cornelia Huck Cc: Cornelia Huck Cc: Alexander Graf Cc: David Hildenbrand Cc: qemu-s390x@nongnu.org Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/s390x/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 7d39ab350d..44449f11ab 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -6163,7 +6163,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) CPUS390XState *env = cs->env_ptr; DisasContext dc; target_ulong pc_start; - uint64_t next_page_start; + uint64_t page_start; int num_insns, max_insns; ExitStatus status; bool do_debug; @@ -6181,7 +6181,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) dc.ex_value = tb->cs_base; do_debug = dc.singlestep_enabled = cs->singlestep_enabled; - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; @@ -6218,7 +6218,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) /* If we reach a page boundary, are single stepping, or exhaust instruction count, stop generation. */ if (status == NO_EXIT - && (dc.pc >= next_page_start + && (dc.pc - page_start >= TARGET_PAGE_SIZE || tcg_op_buf_full() || num_insns >= max_insns || singlestep From 6cd79443d33e6ba6b4c5b787eb713ca1cec56328 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 10 Apr 2018 11:13:54 -0400 Subject: [PATCH 0342/2380] target/mips: avoid integer overflow in next_page PC check If the PC is in the last page of the address space, next_page_start overflows to 0. Fix it. Reviewed-by: Richard Henderson Cc: Aurelien Jarno Cc: Yongbok Kim Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/mips/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/mips/translate.c b/target/mips/translate.c index d05ee67e63..d8e717dacf 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -20202,14 +20202,14 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) CPUMIPSState *env = cs->env_ptr; DisasContext ctx; target_ulong pc_start; - target_ulong next_page_start; + target_ulong page_start; int num_insns; int max_insns; int insn_bytes; int is_slot; pc_start = tb->pc; - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; ctx.pc = pc_start; ctx.saved_pc = -1; ctx.singlestep_enabled = cs->singlestep_enabled; @@ -20320,7 +20320,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) break; } - if (ctx.pc >= next_page_start) { + if (ctx.pc - page_start >= TARGET_PAGE_SIZE) { break; } From b542683d77b4f56cef0221b267c341616d87bce9 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Mon, 19 Feb 2018 20:51:58 -0500 Subject: [PATCH 0343/2380] translator: merge max_insns into DisasContextBase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While at it, use int for both num_insns and max_insns to make sure we have same-type comparisons. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Clark Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 21 ++++++++++----------- include/exec/translator.h | 8 ++++---- target/alpha/translate.c | 6 ++---- target/arm/translate-a64.c | 8 +++----- target/arm/translate.c | 9 +++------ target/hppa/translate.c | 7 ++----- target/i386/translate.c | 5 +---- target/ppc/translate.c | 5 ++--- 8 files changed, 27 insertions(+), 42 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 23c6602cd9..0f9dca9113 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -34,8 +34,6 @@ void translator_loop_temp_check(DisasContextBase *db) void translator_loop(const TranslatorOps *ops, DisasContextBase *db, CPUState *cpu, TranslationBlock *tb) { - int max_insns; - /* Initialize DisasContext */ db->tb = tb; db->pc_first = tb->pc; @@ -45,18 +43,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, db->singlestep_enabled = cpu->singlestep_enabled; /* Instruction counting */ - max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; + db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; + if (db->max_insns == 0) { + db->max_insns = CF_COUNT_MASK; } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; + if (db->max_insns > TCG_MAX_INSNS) { + db->max_insns = TCG_MAX_INSNS; } if (db->singlestep_enabled || singlestep) { - max_insns = 1; + db->max_insns = 1; } - max_insns = ops->init_disas_context(db, cpu, max_insns); + ops->init_disas_context(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ /* Reset the temp count so that we can identify leaks */ @@ -95,7 +93,8 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, update db->pc_next and db->is_jmp to indicate what should be done next -- either exiting this loop or locate the start of the next instruction. */ - if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) { + if (db->num_insns == db->max_insns + && (tb_cflags(db->tb) & CF_LAST_IO)) { /* Accept I/O on the last instruction. */ gen_io_start(); ops->translate_insn(db, cpu); @@ -111,7 +110,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, /* Stop translation if the output buffer is full, or we have executed all of the allowed instructions. */ - if (tcg_op_buf_full() || db->num_insns >= max_insns) { + if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { db->is_jmp = DISAS_TOO_MANY; break; } diff --git a/include/exec/translator.h b/include/exec/translator.h index e2dc2a04ae..71e7b2c347 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -58,6 +58,7 @@ typedef enum DisasJumpType { * disassembly). * @is_jmp: What instruction to disassemble next. * @num_insns: Number of translated instructions (including current). + * @max_insns: Maximum number of instructions to be translated in this TB. * @singlestep_enabled: "Hardware" single stepping enabled. * * Architecture-agnostic disassembly context. @@ -67,7 +68,8 @@ typedef struct DisasContextBase { target_ulong pc_first; target_ulong pc_next; DisasJumpType is_jmp; - unsigned int num_insns; + int num_insns; + int max_insns; bool singlestep_enabled; } DisasContextBase; @@ -76,7 +78,6 @@ typedef struct DisasContextBase { * @init_disas_context: * Initialize the target-specific portions of DisasContext struct. * The generic DisasContextBase has already been initialized. - * Return max_insns, modified as necessary by db->tb->flags. * * @tb_start: * Emit any code required before the start of the main loop, @@ -106,8 +107,7 @@ typedef struct DisasContextBase { * Print instruction disassembly to log. */ typedef struct TranslatorOps { - int (*init_disas_context)(DisasContextBase *db, CPUState *cpu, - int max_insns); + void (*init_disas_context)(DisasContextBase *db, CPUState *cpu); void (*tb_start)(DisasContextBase *db, CPUState *cpu); void (*insn_start)(DisasContextBase *db, CPUState *cpu); bool (*breakpoint_check)(DisasContextBase *db, CPUState *cpu, diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 73a1b5e63e..15eca71d49 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -2919,8 +2919,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) return ret; } -static int alpha_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cpu, int max_insns) +static void alpha_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUAlphaState *env = cpu->env_ptr; @@ -2959,8 +2958,7 @@ static int alpha_tr_init_disas_context(DisasContextBase *dcbase, mask = TARGET_PAGE_MASK; } bound = -(ctx->base.pc_first | mask) / 4; - - return MIN(max_insns, bound); + ctx->base.max_insns = MIN(ctx->base.max_insns, bound); } static void alpha_tr_tb_start(DisasContextBase *db, CPUState *cpu) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 6d49f30b4a..1e7c150514 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -13224,8 +13224,8 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) free_tmp_a64(s); } -static int aarch64_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cpu, int max_insns) +static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, + CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cpu->env_ptr; @@ -13288,11 +13288,9 @@ static int aarch64_tr_init_disas_context(DisasContextBase *dcbase, if (dc->ss_active) { bound = 1; } - max_insns = MIN(max_insns, bound); + dc->base.max_insns = MIN(dc->base.max_insns, bound); init_tmp_a64_array(dc); - - return max_insns; } static void aarch64_tr_tb_start(DisasContextBase *db, CPUState *cpu) diff --git a/target/arm/translate.c b/target/arm/translate.c index 0f6629f745..731cf327a1 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -12243,8 +12243,7 @@ static bool insn_crosses_page(CPUARMState *env, DisasContext *s) return !thumb_insn_is_16bit(s, insn); } -static int arm_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cs, int max_insns) +static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cs->env_ptr; @@ -12305,14 +12304,14 @@ static int arm_tr_init_disas_context(DisasContextBase *dcbase, /* If architectural single step active, limit to 1. */ if (is_singlestepping(dc)) { - max_insns = 1; + dc->base.max_insns = 1; } /* ARM is a fixed-length ISA. Bound the number of insns to execute to those left on the page. */ if (!dc->thumb) { int bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; - max_insns = MIN(max_insns, bound); + dc->base.max_insns = MIN(dc->base.max_insns, bound); } cpu_F0s = tcg_temp_new_i32(); @@ -12323,8 +12322,6 @@ static int arm_tr_init_disas_context(DisasContextBase *dcbase, cpu_V1 = cpu_F1d; /* FIXME: cpu_M0 can probably be the same as cpu_V0. */ cpu_M0 = tcg_temp_new_i64(); - - return max_insns; } static void arm_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index cdc397308b..5320b217de 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4669,8 +4669,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) return gen_illegal(ctx); } -static int hppa_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cs, int max_insns) +static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); int bound; @@ -4700,14 +4699,12 @@ static int hppa_tr_init_disas_context(DisasContextBase *dcbase, /* Bound the number of instructions by those left on the page. */ bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; - bound = MIN(max_insns, bound); + ctx->base.max_insns = MIN(ctx->base.max_insns, bound); ctx->ntempr = 0; ctx->ntempl = 0; memset(ctx->tempr, 0, sizeof(ctx->tempr)); memset(ctx->templ, 0, sizeof(ctx->templ)); - - return bound; } static void hppa_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) diff --git a/target/i386/translate.c b/target/i386/translate.c index c9ed8dc709..b0f69838f2 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -8402,8 +8402,7 @@ void tcg_x86_init(void) } } -static int i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu, - int max_insns) +static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); CPUX86State *env = cpu->env_ptr; @@ -8470,8 +8469,6 @@ static int i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu, cpu_ptr0 = tcg_temp_new_ptr(); cpu_ptr1 = tcg_temp_new_ptr(); cpu_cc_srcT = tcg_temp_local_new(); - - return max_insns; } static void i386_tr_tb_start(DisasContextBase *db, CPUState *cpu) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 2a4140f420..7972e6b410 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7215,8 +7215,7 @@ void ppc_cpu_dump_statistics(CPUState *cs, FILE*f, #endif } -static int ppc_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cs, int max_insns) +static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUPPCState *env = cs->env_ptr; @@ -7281,7 +7280,7 @@ static int ppc_tr_init_disas_context(DisasContextBase *dcbase, #endif bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; - return MIN(max_insns, bound); + ctx->base.max_insns = MIN(ctx->base.max_insns, bound); } static void ppc_tr_tb_start(DisasContextBase *db, CPUState *cs) From fd1b3d38649470191ffbe67429a0c4b1a95116a0 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Thu, 15 Feb 2018 21:15:25 -0500 Subject: [PATCH 0344/2380] target/sh4: convert to TranslatorOps This was fairly straightforward since it had already been converted to DisasContextBase; just had to add TARGET_TOO_MANY to the switch in tb_stop. Reviewed-by: Richard Henderson Cc: Aurelien Jarno Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/sh4/translate.c | 169 +++++++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 84 deletions(-) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 012156b97b..58bdfeb4fb 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -2258,126 +2258,127 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) } #endif -void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +static void sh4_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { + DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUSH4State *env = cs->env_ptr; - DisasContext ctx; - target_ulong pc_start; - int num_insns; - int max_insns; + int bound; - pc_start = tb->pc; - ctx.base.pc_next = pc_start; - ctx.tbflags = (uint32_t)tb->flags; - ctx.envflags = tb->flags & TB_FLAG_ENVFLAGS_MASK; - ctx.base.is_jmp = DISAS_NEXT; - ctx.memidx = (ctx.tbflags & (1u << SR_MD)) == 0 ? 1 : 0; + ctx->tbflags = (uint32_t)ctx->base.tb->flags; + ctx->envflags = ctx->base.tb->flags & TB_FLAG_ENVFLAGS_MASK; + ctx->memidx = (ctx->tbflags & (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 */ - ctx.base.tb = tb; - ctx.base.singlestep_enabled = cs->singlestep_enabled; - ctx.features = env->features; - ctx.has_movcal = (ctx.tbflags & TB_FLAG_PENDING_MOVCA); - ctx.gbank = ((ctx.tbflags & (1 << SR_MD)) && - (ctx.tbflags & (1 << SR_RB))) * 0x10; - ctx.fbank = ctx.tbflags & FPSCR_FR ? 0x10 : 0; - - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - max_insns = MIN(max_insns, TCG_MAX_INSNS); + ctx->delayed_pc = -1; /* use delayed pc from env pointer */ + ctx->features = env->features; + ctx->has_movcal = (ctx->tbflags & TB_FLAG_PENDING_MOVCA); + ctx->gbank = ((ctx->tbflags & (1 << SR_MD)) && + (ctx->tbflags & (1 << SR_RB))) * 0x10; + ctx->fbank = ctx->tbflags & FPSCR_FR ? 0x10 : 0; /* Since the ISA is fixed-width, we can bound by the number of instructions remaining on the page. */ - num_insns = -(ctx.base.pc_next | TARGET_PAGE_MASK) / 2; - max_insns = MIN(max_insns, num_insns); - - /* Single stepping means just that. */ - if (ctx.base.singlestep_enabled || singlestep) { - max_insns = 1; - } - - gen_tb_start(tb); - num_insns = 0; + bound = -(ctx->base.pc_next | TARGET_PAGE_MASK) / 2; + ctx->base.max_insns = MIN(ctx->base.max_insns, bound); +} +static void sh4_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) +{ #ifdef CONFIG_USER_ONLY - if (ctx.tbflags & GUSA_MASK) { - num_insns = decode_gusa(&ctx, env, &max_insns); + DisasContext *ctx = container_of(dcbase, DisasContext, base); + CPUSH4State *env = cs->env_ptr; + + if (ctx->tbflags & GUSA_MASK) { + ctx->base.num_insns = decode_gusa(ctx, env, &ctx->base.max_insns); } #endif +} - while (ctx.base.is_jmp == DISAS_NEXT - && num_insns < max_insns - && !tcg_op_buf_full()) { - tcg_gen_insn_start(ctx.base.pc_next, ctx.envflags); - num_insns++; +static void sh4_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); - if (unlikely(cpu_breakpoint_test(cs, ctx.base.pc_next, BP_ANY))) { - /* We have hit a breakpoint - make sure PC is up-to-date */ - gen_save_cpu_state(&ctx, true); - gen_helper_debug(cpu_env); - ctx.base.is_jmp = DISAS_NORETURN; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - ctx.base.pc_next += 2; - break; - } + tcg_gen_insn_start(ctx->base.pc_next, ctx->envflags); +} - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } +static bool sh4_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); - ctx.opcode = cpu_lduw_code(env, ctx.base.pc_next); - decode_opc(&ctx); - ctx.base.pc_next += 2; - } - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); - } + /* We have hit a breakpoint - make sure PC is up-to-date */ + gen_save_cpu_state(ctx, true); + gen_helper_debug(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx->base.pc_next += 2; + return true; +} - if (ctx.tbflags & GUSA_EXCLUSIVE) { +static void sh4_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + CPUSH4State *env = cs->env_ptr; + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); + decode_opc(ctx); + ctx->base.pc_next += 2; +} + +static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + if (ctx->tbflags & GUSA_EXCLUSIVE) { /* Ending the region of exclusivity. Clear the bits. */ - ctx.envflags &= ~GUSA_MASK; + ctx->envflags &= ~GUSA_MASK; } - switch (ctx.base.is_jmp) { + switch (ctx->base.is_jmp) { case DISAS_STOP: - gen_save_cpu_state(&ctx, true); - if (ctx.base.singlestep_enabled) { + gen_save_cpu_state(ctx, true); + if (ctx->base.singlestep_enabled) { gen_helper_debug(cpu_env); } else { tcg_gen_exit_tb(0); } break; case DISAS_NEXT: - gen_save_cpu_state(&ctx, false); - gen_goto_tb(&ctx, 0, ctx.base.pc_next); + case DISAS_TOO_MANY: + gen_save_cpu_state(ctx, false); + gen_goto_tb(ctx, 0, ctx->base.pc_next); break; case DISAS_NORETURN: break; default: g_assert_not_reached(); } +} - gen_tb_end(tb, num_insns); +static void sh4_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +{ + qemu_log("IN:\n"); /* , lookup_symbol(dcbase->pc_first)); */ + log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); +} - tb->size = ctx.base.pc_next - pc_start; - tb->icount = num_insns; +static const TranslatorOps sh4_tr_ops = { + .init_disas_context = sh4_tr_init_disas_context, + .tb_start = sh4_tr_tb_start, + .insn_start = sh4_tr_insn_start, + .breakpoint_check = sh4_tr_breakpoint_check, + .translate_insn = sh4_tr_translate_insn, + .tb_stop = sh4_tr_tb_stop, + .disas_log = sh4_tr_disas_log, +}; -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - qemu_log("IN:\n"); /* , lookup_symbol(pc_start)); */ - log_target_disas(cs, pc_start, ctx.base.pc_next - pc_start); - qemu_log("\n"); - qemu_log_unlock(); - } -#endif +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +{ + DisasContext ctx; + + translator_loop(&sh4_tr_ops, &ctx.base, cs, tb); } void restore_state_to_opc(CPUSH4State *env, TranslationBlock *tb, From c5e6ccdf6c8d32d3a45d9dca4d6847dcff741882 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Wed, 14 Feb 2018 20:52:14 -0500 Subject: [PATCH 0345/2380] target/sparc: convert to DisasJumpType Reviewed-by: Richard Henderson Cc: Mark Cave-Ayland Cc: Artyom Tarasenko Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/sparc/translate.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 5aa367a182..03c6510717 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -30,6 +30,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/translator.h" #include "exec/log.h" #include "asi.h" @@ -69,7 +70,7 @@ typedef struct DisasContext { target_ulong pc; /* current Program Counter: integer or DYNAMIC_PC */ target_ulong npc; /* next PC: integer or DYNAMIC_PC or JUMP_PC */ target_ulong jump_pc[2]; /* used when JUMP_PC pc value is used */ - int is_br; + DisasJumpType is_jmp; int mem_idx; bool fpu_enabled; bool address_mask_32bit; @@ -995,7 +996,7 @@ static void gen_branch_a(DisasContext *dc, target_ulong pc1) gen_set_label(l1); gen_goto_tb(dc, 1, npc + 4, npc + 8); - dc->is_br = 1; + dc->is_jmp = DISAS_NORETURN; } static void gen_branch_n(DisasContext *dc, target_ulong pc1) @@ -1078,7 +1079,7 @@ static void gen_exception(DisasContext *dc, int which) t = tcg_const_i32(which); gen_helper_raise_exception(cpu_env, t); tcg_temp_free_i32(t); - dc->is_br = 1; + dc->is_jmp = DISAS_NORETURN; } static void gen_check_align(TCGv addr, int mask) @@ -3351,7 +3352,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) if (cond == 8) { /* An unconditional trap ends the TB. */ - dc->is_br = 1; + dc->is_jmp = DISAS_NORETURN; goto jmp_insn; } else { /* A conditional trap falls through to the next insn. */ @@ -4331,7 +4332,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) save_state(dc); gen_op_next_insn(); tcg_gen_exit_tb(0); - dc->is_br = 1; + dc->is_jmp = DISAS_NORETURN; break; case 0x6: /* V9 wrfprs */ tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); @@ -4340,7 +4341,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) save_state(dc); gen_op_next_insn(); tcg_gen_exit_tb(0); - dc->is_br = 1; + dc->is_jmp = DISAS_NORETURN; break; case 0xf: /* V9 sir, nop if user */ #if !defined(CONFIG_USER_ONLY) @@ -4468,7 +4469,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) save_state(dc); gen_op_next_insn(); tcg_gen_exit_tb(0); - dc->is_br = 1; + dc->is_jmp = DISAS_NORETURN; #endif } break; @@ -4624,7 +4625,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) save_state(dc); gen_op_next_insn(); tcg_gen_exit_tb(0); - dc->is_br = 1; + dc->is_jmp = DISAS_NORETURN; break; case 1: // htstate // XXX gen_op_wrhtstate(); @@ -5690,7 +5691,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) } else if (dc->npc == JUMP_PC) { /* we can do a static jump */ gen_branch2(dc, dc->jump_pc[0], dc->jump_pc[1], cpu_cond); - dc->is_br = 1; + dc->is_jmp = DISAS_NORETURN; } else { dc->pc = dc->npc; dc->npc = dc->npc + 4; @@ -5752,6 +5753,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) pc_start = tb->pc; dc->pc = pc_start; last_pc = dc->pc; + dc->is_jmp = DISAS_NEXT; dc->npc = (target_ulong) tb->cs_base; dc->cc_op = CC_OP_DYNAMIC; dc->mem_idx = tb->flags & TB_FLAG_MMU_MASK; @@ -5796,7 +5798,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) } gen_helper_debug(cpu_env); tcg_gen_exit_tb(0); - dc->is_br = 1; + dc->is_jmp = DISAS_NORETURN; goto exit_gen_loop; } @@ -5808,8 +5810,9 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) disas_sparc_insn(dc, insn); - if (dc->is_br) + if (dc->is_jmp == DISAS_NORETURN) { break; + } /* if the next PC is different, we abort now */ if (dc->pc != (last_pc + 4)) break; @@ -5830,7 +5833,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) if (tb_cflags(tb) & CF_LAST_IO) { gen_io_end(); } - if (!dc->is_br) { + if (dc->is_jmp != DISAS_NORETURN) { if (dc->pc != DYNAMIC_PC && (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) { /* static PC and NPC: we can use direct chaining */ From af00be490b30d7f576d12ac7b2bc5406ca6fda3f Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Wed, 14 Feb 2018 21:16:30 -0500 Subject: [PATCH 0346/2380] target/sparc: convert to DisasContextBase Notes: - pc and npc are left unmodified, since they can point to out-of-TB jump targets. - Got rid of last_pc in gen_intermediate_code(), using base.pc_next instead. Only update pc_next (1) on a breakpoint (so that tb->size includes the insn), and (2) after reading the current instruction from memory. This allows us to use base.pc_next in the BP check, which is what the translator loop does. Reviewed-by: Richard Henderson Cc: Mark Cave-Ayland Cc: Artyom Tarasenko Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/sparc/translate.c | 92 ++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 03c6510717..889c43976d 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -67,14 +67,13 @@ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; #include "exec/gen-icount.h" typedef struct DisasContext { + DisasContextBase base; target_ulong pc; /* current Program Counter: integer or DYNAMIC_PC */ target_ulong npc; /* next PC: integer or DYNAMIC_PC or JUMP_PC */ target_ulong jump_pc[2]; /* used when JUMP_PC pc value is used */ - DisasJumpType is_jmp; int mem_idx; bool fpu_enabled; bool address_mask_32bit; - bool singlestep; #ifndef CONFIG_USER_ONLY bool supervisor; #ifdef TARGET_SPARC64 @@ -83,7 +82,6 @@ typedef struct DisasContext { #endif uint32_t cc_op; /* current CC operation */ - struct TranslationBlock *tb; sparc_def_t *def; TCGv_i32 t32[3]; TCGv ttl[5]; @@ -342,13 +340,13 @@ static inline TCGv gen_dest_gpr(DisasContext *dc, int reg) static inline bool use_goto_tb(DisasContext *s, target_ulong pc, target_ulong npc) { - if (unlikely(s->singlestep)) { + if (unlikely(s->base.singlestep_enabled || singlestep)) { return false; } #ifndef CONFIG_USER_ONLY - return (pc & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK) && - (npc & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK); + return (pc & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK) && + (npc & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK); #else return true; #endif @@ -362,7 +360,7 @@ static inline void gen_goto_tb(DisasContext *s, int tb_num, tcg_gen_goto_tb(tb_num); tcg_gen_movi_tl(cpu_pc, pc); tcg_gen_movi_tl(cpu_npc, npc); - tcg_gen_exit_tb((uintptr_t)s->tb + tb_num); + tcg_gen_exit_tb((uintptr_t)s->base.tb + tb_num); } else { /* jump to another page: currently not optimized */ tcg_gen_movi_tl(cpu_pc, pc); @@ -996,7 +994,7 @@ static void gen_branch_a(DisasContext *dc, target_ulong pc1) gen_set_label(l1); gen_goto_tb(dc, 1, npc + 4, npc + 8); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; } static void gen_branch_n(DisasContext *dc, target_ulong pc1) @@ -1079,7 +1077,7 @@ static void gen_exception(DisasContext *dc, int which) t = tcg_const_i32(which); gen_helper_raise_exception(cpu_env, t); tcg_temp_free_i32(t); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; } static void gen_check_align(TCGv addr, int mask) @@ -2442,7 +2440,7 @@ static void gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) default: /* ??? In theory, this should be raise DAE_invalid_asi. But the SS-20 roms do ldstuba [%l0] #ASI_M_CTL, %o1. */ - if (tb_cflags(dc->tb) & CF_PARALLEL) { + if (tb_cflags(dc->base.tb) & CF_PARALLEL) { gen_helper_exit_atomic(cpu_env); } else { TCGv_i32 r_asi = tcg_const_i32(da.asi); @@ -3352,7 +3350,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) if (cond == 8) { /* An unconditional trap ends the TB. */ - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; goto jmp_insn; } else { /* A conditional trap falls through to the next insn. */ @@ -4332,7 +4330,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) save_state(dc); gen_op_next_insn(); tcg_gen_exit_tb(0); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; break; case 0x6: /* V9 wrfprs */ tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); @@ -4341,7 +4339,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) save_state(dc); gen_op_next_insn(); tcg_gen_exit_tb(0); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; break; case 0xf: /* V9 sir, nop if user */ #if !defined(CONFIG_USER_ONLY) @@ -4469,7 +4467,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) save_state(dc); gen_op_next_insn(); tcg_gen_exit_tb(0); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; #endif } break; @@ -4625,7 +4623,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) save_state(dc); gen_op_next_insn(); tcg_gen_exit_tb(0); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; break; case 1: // htstate // XXX gen_op_wrhtstate(); @@ -5691,7 +5689,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) } else if (dc->npc == JUMP_PC) { /* we can do a static jump */ gen_branch2(dc, dc->jump_pc[0], dc->jump_pc[1], cpu_cond); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; } else { dc->pc = dc->npc; dc->npc = dc->npc + 4; @@ -5742,25 +5740,25 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) { CPUSPARCState *env = cs->env_ptr; - target_ulong pc_start, last_pc; DisasContext dc1, *dc = &dc1; - int num_insns; int max_insns; unsigned int insn; memset(dc, 0, sizeof(DisasContext)); - dc->tb = tb; - pc_start = tb->pc; - dc->pc = pc_start; - last_pc = dc->pc; - dc->is_jmp = DISAS_NEXT; + dc->base.tb = tb; + dc->base.pc_first = tb->pc; + dc->base.pc_next = tb->pc; + dc->base.is_jmp = DISAS_NEXT; + dc->base.num_insns = 0; + dc->base.singlestep_enabled = cs->singlestep_enabled; + + dc->pc = dc->base.pc_first; dc->npc = (target_ulong) tb->cs_base; dc->cc_op = CC_OP_DYNAMIC; dc->mem_idx = tb->flags & TB_FLAG_MMU_MASK; dc->def = &env->def; dc->fpu_enabled = tb_fpu_enabled(tb->flags); dc->address_mask_32bit = tb_am_enabled(tb->flags); - dc->singlestep = (cs->singlestep_enabled || singlestep); #ifndef CONFIG_USER_ONLY dc->supervisor = (tb->flags & TB_FLAG_SUPER) != 0; #endif @@ -5772,7 +5770,6 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) #endif #endif - num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; @@ -5780,6 +5777,9 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) if (max_insns > TCG_MAX_INSNS) { max_insns = TCG_MAX_INSNS; } + if (dc->base.singlestep_enabled || singlestep) { + max_insns = 1; + } gen_tb_start(tb); do { @@ -5789,51 +5789,48 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) } else { tcg_gen_insn_start(dc->pc, dc->npc); } - num_insns++; - last_pc = dc->pc; + dc->base.num_insns++; - if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { - if (dc->pc != pc_start) { + if (unlikely(cpu_breakpoint_test(cs, dc->base.pc_next, BP_ANY))) { + if (dc->pc != dc->base.pc_first) { save_state(dc); } gen_helper_debug(cpu_env); tcg_gen_exit_tb(0); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; + dc->base.pc_next += 4; goto exit_gen_loop; } - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { + if (dc->base.num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { gen_io_start(); } insn = cpu_ldl_code(env, dc->pc); + dc->base.pc_next += 4; disas_sparc_insn(dc, insn); - if (dc->is_jmp == DISAS_NORETURN) { + if (dc->base.is_jmp == DISAS_NORETURN) { break; } /* if the next PC is different, we abort now */ - if (dc->pc != (last_pc + 4)) + if (dc->pc != dc->base.pc_next) { break; + } /* if we reach a page boundary, we stop generation so that the PC of a TT_TFAULT exception is always in the right page */ if ((dc->pc & (TARGET_PAGE_SIZE - 1)) == 0) break; - /* if single step mode, we generate only one instruction and - generate an exception */ - if (dc->singlestep) { - break; - } } while (!tcg_op_buf_full() && - (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32) && - num_insns < max_insns); + (dc->pc - dc->base.pc_first) < (TARGET_PAGE_SIZE - 32) && + dc->base.num_insns < max_insns); exit_gen_loop: if (tb_cflags(tb) & CF_LAST_IO) { gen_io_end(); } - if (dc->is_jmp != DISAS_NORETURN) { + if (dc->base.is_jmp != DISAS_NORETURN) { if (dc->pc != DYNAMIC_PC && (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) { /* static PC and NPC: we can use direct chaining */ @@ -5846,18 +5843,19 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) tcg_gen_exit_tb(0); } } - gen_tb_end(tb, num_insns); + gen_tb_end(tb, dc->base.num_insns); - tb->size = last_pc + 4 - pc_start; - tb->icount = num_insns; + tb->size = dc->base.pc_next - dc->base.pc_first; + tb->icount = dc->base.num_insns; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { + && qemu_log_in_addr_range(dc->base.pc_first)) { qemu_log_lock(); qemu_log("--------------\n"); - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, last_pc + 4 - pc_start); + qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); + log_target_disas(cs, dc->base.pc_first, + dc->base.pc_next - dc->base.pc_first); qemu_log("\n"); qemu_log_unlock(); } From 6e61bc941025345ab01c48d116bef60bb8990406 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Thu, 15 Feb 2018 17:50:16 -0500 Subject: [PATCH 0347/2380] target/sparc: convert to TranslatorOps Notes: - Moved the cross-page check from the end of translate_insn to init_disas_context. Reviewed-by: Richard Henderson Tested-by: Mark Cave-Ayland Cc: Mark Cave-Ayland Cc: Artyom Tarasenko Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/sparc/translate.c | 178 +++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 90 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 889c43976d..40b2eaad39 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5737,99 +5737,91 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) } } -void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) +static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { + DisasContext *dc = container_of(dcbase, DisasContext, base); CPUSPARCState *env = cs->env_ptr; - DisasContext dc1, *dc = &dc1; - int max_insns; - unsigned int insn; - - memset(dc, 0, sizeof(DisasContext)); - dc->base.tb = tb; - dc->base.pc_first = tb->pc; - dc->base.pc_next = tb->pc; - dc->base.is_jmp = DISAS_NEXT; - dc->base.num_insns = 0; - dc->base.singlestep_enabled = cs->singlestep_enabled; + int bound; dc->pc = dc->base.pc_first; - dc->npc = (target_ulong) tb->cs_base; + dc->npc = (target_ulong)dc->base.tb->cs_base; dc->cc_op = CC_OP_DYNAMIC; - dc->mem_idx = tb->flags & TB_FLAG_MMU_MASK; + dc->mem_idx = dc->base.tb->flags & TB_FLAG_MMU_MASK; dc->def = &env->def; - dc->fpu_enabled = tb_fpu_enabled(tb->flags); - dc->address_mask_32bit = tb_am_enabled(tb->flags); + dc->fpu_enabled = tb_fpu_enabled(dc->base.tb->flags); + dc->address_mask_32bit = tb_am_enabled(dc->base.tb->flags); #ifndef CONFIG_USER_ONLY - dc->supervisor = (tb->flags & TB_FLAG_SUPER) != 0; + dc->supervisor = (dc->base.tb->flags & TB_FLAG_SUPER) != 0; #endif #ifdef TARGET_SPARC64 dc->fprs_dirty = 0; - dc->asi = (tb->flags >> TB_FLAG_ASI_SHIFT) & 0xff; + dc->asi = (dc->base.tb->flags >> TB_FLAG_ASI_SHIFT) & 0xff; #ifndef CONFIG_USER_ONLY - dc->hypervisor = (tb->flags & TB_FLAG_HYPER) != 0; + dc->hypervisor = (dc->base.tb->flags & TB_FLAG_HYPER) != 0; #endif #endif + /* + * if we reach a page boundary, we stop generation so that the + * PC of a TT_TFAULT exception is always in the right page + */ + bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + dc->base.max_insns = MIN(dc->base.max_insns, bound); +} - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; +static void sparc_tr_tb_start(DisasContextBase *db, CPUState *cs) +{ +} + +static void sparc_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + if (dc->npc & JUMP_PC) { + assert(dc->jump_pc[1] == dc->pc + 4); + tcg_gen_insn_start(dc->pc, dc->jump_pc[0] | JUMP_PC); + } else { + tcg_gen_insn_start(dc->pc, dc->npc); } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; +} + +static bool sparc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + if (dc->pc != dc->base.pc_first) { + save_state(dc); } - if (dc->base.singlestep_enabled || singlestep) { - max_insns = 1; + gen_helper_debug(cpu_env); + tcg_gen_exit_tb(0); + dc->base.is_jmp = DISAS_NORETURN; + /* update pc_next so that the current instruction is included in tb->size */ + dc->base.pc_next += 4; + return true; +} + +static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUSPARCState *env = cs->env_ptr; + unsigned int insn; + + insn = cpu_ldl_code(env, dc->pc); + dc->base.pc_next += 4; + disas_sparc_insn(dc, insn); + + if (dc->base.is_jmp == DISAS_NORETURN) { + return; } - - gen_tb_start(tb); - do { - if (dc->npc & JUMP_PC) { - assert(dc->jump_pc[1] == dc->pc + 4); - tcg_gen_insn_start(dc->pc, dc->jump_pc[0] | JUMP_PC); - } else { - tcg_gen_insn_start(dc->pc, dc->npc); - } - dc->base.num_insns++; - - if (unlikely(cpu_breakpoint_test(cs, dc->base.pc_next, BP_ANY))) { - if (dc->pc != dc->base.pc_first) { - save_state(dc); - } - gen_helper_debug(cpu_env); - tcg_gen_exit_tb(0); - dc->base.is_jmp = DISAS_NORETURN; - dc->base.pc_next += 4; - goto exit_gen_loop; - } - - if (dc->base.num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } - - insn = cpu_ldl_code(env, dc->pc); - dc->base.pc_next += 4; - - disas_sparc_insn(dc, insn); - - if (dc->base.is_jmp == DISAS_NORETURN) { - break; - } - /* if the next PC is different, we abort now */ - if (dc->pc != dc->base.pc_next) { - break; - } - /* if we reach a page boundary, we stop generation so that the - PC of a TT_TFAULT exception is always in the right page */ - if ((dc->pc & (TARGET_PAGE_SIZE - 1)) == 0) - break; - } while (!tcg_op_buf_full() && - (dc->pc - dc->base.pc_first) < (TARGET_PAGE_SIZE - 32) && - dc->base.num_insns < max_insns); - - exit_gen_loop: - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); + if (dc->pc != dc->base.pc_next) { + dc->base.is_jmp = DISAS_TOO_MANY; } +} + +static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + if (dc->base.is_jmp != DISAS_NORETURN) { if (dc->pc != DYNAMIC_PC && (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) { @@ -5843,23 +5835,29 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) tcg_gen_exit_tb(0); } } - gen_tb_end(tb, dc->base.num_insns); +} - tb->size = dc->base.pc_next - dc->base.pc_first; - tb->icount = dc->base.num_insns; +static void sparc_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); +} -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(dc->base.pc_first)) { - qemu_log_lock(); - qemu_log("--------------\n"); - qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); - log_target_disas(cs, dc->base.pc_first, - dc->base.pc_next - dc->base.pc_first); - qemu_log("\n"); - qemu_log_unlock(); - } -#endif +static const TranslatorOps sparc_tr_ops = { + .init_disas_context = sparc_tr_init_disas_context, + .tb_start = sparc_tr_tb_start, + .insn_start = sparc_tr_insn_start, + .breakpoint_check = sparc_tr_breakpoint_check, + .translate_insn = sparc_tr_translate_insn, + .tb_stop = sparc_tr_tb_stop, + .disas_log = sparc_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +{ + DisasContext dc = {}; + + translator_loop(&sparc_tr_ops, &dc.base, cs, tb); } void sparc_tcg_init(void) From cd314a7d0190a03122ca0606ecf71b4b873a22c6 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Wed, 11 Apr 2018 11:44:28 -0400 Subject: [PATCH 0348/2380] target/mips: use lookup_and_goto_ptr on BS_STOP The TB after BS_STOP is not fixed (e.g. helper_mtc0_hwrena changes hflags, which ends up changing the TB flags via cpu_get_tb_cpu_state). This requires a full lookup (i.e. with flags) via lookup_and_goto_ptr instead of gen_goto_tb, since the latter only looks at the PC for in-page goto's. Fix it. Reported-by: Richard Henderson Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/mips/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/mips/translate.c b/target/mips/translate.c index d8e717dacf..69137d0b3f 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -20343,7 +20343,8 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } else { switch (ctx.bstate) { case BS_STOP: - gen_goto_tb(&ctx, 0, ctx.pc); + gen_save_pc(ctx.pc); + tcg_gen_lookup_and_goto_ptr(); break; case BS_NONE: save_cpu_state(&ctx, 0); From b28425babc2ad4b90cd87d07a1809d3322b9c065 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Thu, 15 Feb 2018 19:36:03 -0500 Subject: [PATCH 0349/2380] target/mips: convert to DisasJumpType MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Notes: - BS_EXCP in generate_exception_err and after hen_helper_wait becomes DISAS_NORETURN, because we do not return after raising an exception. - Some uses of BS_EXCP are misleading in that they're used only as a "not BS_STOP" exit condition, i.e. they have nothing to do with an actual exception. For those cases, define and use DISAS_EXIT, which is clearer. With this and the above change, BS_EXCP goes away completely. - fix a comment typo (s/intetrupt/interrupt/). Suggested-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Cc: Aurelien Jarno Cc: Yongbok Kim Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/mips/translate.c | 186 ++++++++++++++++++++-------------------- 1 file changed, 91 insertions(+), 95 deletions(-) diff --git a/target/mips/translate.c b/target/mips/translate.c index 69137d0b3f..83a484822e 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -36,6 +36,7 @@ #include "target/mips/trace.h" #include "trace-tcg.h" +#include "exec/translator.h" #include "exec/log.h" #define MIPS_DEBUG_DISAS 0 @@ -1439,7 +1440,7 @@ typedef struct DisasContext { int mem_idx; TCGMemOp default_tcg_memop_mask; uint32_t hflags, saved_hflags; - int bstate; + DisasJumpType is_jmp; target_ulong btarget; bool ulri; int kscrexist; @@ -1460,13 +1461,8 @@ typedef struct DisasContext { bool abs2008; } DisasContext; -enum { - BS_NONE = 0, /* We go out of the TB without reaching a branch or an - * exception condition */ - BS_STOP = 1, /* We want to stop translation for any reason */ - BS_BRANCH = 2, /* We reached a branch condition */ - BS_EXCP = 3, /* We reached an exception condition */ -}; +#define DISAS_STOP DISAS_TARGET_0 +#define DISAS_EXIT DISAS_TARGET_1 static const char * const regnames[] = { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", @@ -1639,7 +1635,7 @@ static inline void generate_exception_err(DisasContext *ctx, int excp, int err) gen_helper_raise_exception_err(cpu_env, texcp, terr); tcg_temp_free_i32(terr); tcg_temp_free_i32(texcp); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_NORETURN; } static inline void generate_exception(DisasContext *ctx, int excp) @@ -5334,10 +5330,10 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_io_end(); } /* Break the TB to be able to take timer interrupts immediately - after reading count. BS_STOP isn't sufficient, we need to ensure - we break completely out of translated code. */ + after reading count. DISAS_STOP isn't sufficient, we need to + ensure we break completely out of translated code. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; rn = "Count"; break; /* 6,7 are implementation dependent */ @@ -5905,7 +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; + ctx->is_jmp = DISAS_STOP; break; case 2: CP0_CHECK(ctx->sc); @@ -5966,7 +5962,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "HWREna"; break; default: @@ -6028,30 +6024,30 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: save_cpu_state(ctx, 1); gen_helper_mtc0_status(cpu_env, arg); - /* BS_STOP isn't good enough here, hflags may have changed. */ + /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; rn = "Status"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "IntCtl"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "SRSCtl"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "SRSMap"; break; default: @@ -6063,11 +6059,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: save_cpu_state(ctx, 1); gen_helper_mtc0_cause(cpu_env, arg); - /* Stop translation as we may have triggered an interrupt. BS_STOP - * isn't sufficient, we need to ensure we break out of translated - * code to check for pending interrupts. */ + /* Stop translation as we may have triggered an interrupt. + * DISAS_STOP isn't sufficient, we need to ensure we break out of + * translated code to check for pending interrupts. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; rn = "Cause"; break; default: @@ -6105,7 +6101,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config0(cpu_env, arg); rn = "Config"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case 1: /* ignored, read only */ @@ -6115,24 +6111,24 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config2(cpu_env, arg); rn = "Config2"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case 3: gen_helper_mtc0_config3(cpu_env, arg); rn = "Config3"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case 4: gen_helper_mtc0_config4(cpu_env, arg); rn = "Config4"; - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case 5: gen_helper_mtc0_config5(cpu_env, arg); rn = "Config5"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; /* 6,7 are implementation dependent */ case 6: @@ -6221,35 +6217,35 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ - /* BS_STOP isn't good enough here, hflags may have changed. */ + /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; rn = "Debug"; break; case 1: // gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */ rn = "TraceControl"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; goto cp0_unimplemented; case 2: // gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */ rn = "TraceControl2"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; goto cp0_unimplemented; case 3: /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; // gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */ rn = "UserTraceData"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; goto cp0_unimplemented; case 4: // gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "TraceBPC"; goto cp0_unimplemented; default: @@ -6309,7 +6305,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: gen_helper_mtc0_errctl(cpu_env, arg); - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "ErrCtl"; break; default: @@ -6402,10 +6398,10 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* For simplicity assume that all writes can cause interrupts. */ if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { gen_io_end(); - /* BS_STOP isn't sufficient, we need to ensure we break out of + /* DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; } return; @@ -6686,10 +6682,10 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_io_end(); } /* Break the TB to be able to take timer interrupts immediately - after reading count. BS_STOP isn't sufficient, we need to ensure - we break completely out of translated code. */ + after reading count. DISAS_STOP isn't sufficient, we need to + ensure we break completely out of translated code. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; rn = "Count"; break; /* 6,7 are implementation dependent */ @@ -7301,7 +7297,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "HWREna"; break; default: @@ -7337,7 +7333,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case 10: switch (sel) { @@ -7360,37 +7356,37 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case 12: switch (sel) { case 0: save_cpu_state(ctx, 1); gen_helper_mtc0_status(cpu_env, arg); - /* BS_STOP isn't good enough here, hflags may have changed. */ + /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; rn = "Status"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "IntCtl"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "SRSCtl"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "SRSMap"; break; default: @@ -7402,11 +7398,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: save_cpu_state(ctx, 1); gen_helper_mtc0_cause(cpu_env, arg); - /* Stop translation as we may have triggered an intetrupt. BS_STOP - * isn't sufficient, we need to ensure we break out of translated - * code to check for pending interrupts. */ + /* Stop translation as we may have triggered an interrupt. + * DISAS_STOP isn't sufficient, we need to ensure we break out of + * translated code to check for pending interrupts. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; rn = "Cause"; break; default: @@ -7444,7 +7440,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config0(cpu_env, arg); rn = "Config"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case 1: /* ignored, read only */ @@ -7454,13 +7450,13 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config2(cpu_env, arg); rn = "Config2"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case 3: gen_helper_mtc0_config3(cpu_env, arg); rn = "Config3"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case 4: /* currently ignored */ @@ -7470,7 +7466,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config5(cpu_env, arg); rn = "Config5"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; /* 6,7 are implementation dependent */ default: @@ -7549,33 +7545,33 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ - /* BS_STOP isn't good enough here, hflags may have changed. */ + /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; rn = "Debug"; break; case 1: // gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "TraceControl"; goto cp0_unimplemented; case 2: // gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "TraceControl2"; goto cp0_unimplemented; case 3: // gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "UserTraceData"; goto cp0_unimplemented; case 4: // gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "TraceBPC"; goto cp0_unimplemented; default: @@ -7635,7 +7631,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: gen_helper_mtc0_errctl(cpu_env, arg); - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; rn = "ErrCtl"; break; default: @@ -7728,10 +7724,10 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* For simplicity assume that all writes can cause interrupts. */ if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { gen_io_end(); - /* BS_STOP isn't sufficient, we need to ensure we break out of + /* DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; } return; @@ -8142,7 +8138,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, tcg_temp_free_i32(fs_tmp); } /* Stop translation as we may have changed hflags */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; /* COP2: Not implemented. */ case 4: @@ -8301,7 +8297,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, check_insn(ctx, ISA_MIPS2); gen_helper_eret(cpu_env); } - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; } break; case OPC_DERET: @@ -8316,7 +8312,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, generate_exception_end(ctx, EXCP_RI); } else { gen_helper_deret(cpu_env); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; } break; case OPC_WAIT: @@ -8331,7 +8327,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, save_cpu_state(ctx, 1); ctx->pc -= 4; gen_helper_wait(cpu_env); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_NORETURN; break; default: die: @@ -8756,7 +8752,7 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) tcg_temp_free_i32(fs_tmp); } /* Stop translation as we may have changed hflags */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; #if defined(TARGET_MIPS64) case OPC_DMFC1: @@ -10764,10 +10760,10 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) } gen_store_gpr(t0, rt); /* Break the TB to be able to take timer interrupts immediately - after reading count. BS_STOP isn't sufficient, we need to ensure + after reading count. DISAS_STOP isn't sufficient, we need to ensure we break completely out of translated code. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; break; case 3: gen_helper_rdhwr_ccres(t0, cpu_env); @@ -10817,7 +10813,7 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) static inline void clear_branch_hflags(DisasContext *ctx) { ctx->hflags &= ~MIPS_HFLAG_BMASK; - if (ctx->bstate == BS_NONE) { + if (ctx->is_jmp == DISAS_NEXT) { save_cpu_state(ctx, 0); } else { /* it is not safe to save ctx->hflags as hflags may be changed @@ -10832,7 +10828,7 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK; /* Branches completion */ clear_branch_hflags(ctx); - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; /* FIXME: Need to clear can_do_io. */ switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) { case MIPS_HFLAG_FBNSLOT: @@ -13574,7 +13570,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) gen_helper_di(t0, cpu_env); gen_store_gpr(t0, rs); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; tcg_temp_free(t0); } break; @@ -13586,10 +13582,10 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) save_cpu_state(ctx, 1); gen_helper_ei(t0, cpu_env); gen_store_gpr(t0, rs); - /* BS_STOP isn't sufficient, we need to ensure we break out + /* DISAS_STOP isn't sufficient, we need to ensure we break out of translated code to check for pending interrupts. */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; tcg_temp_free(t0); } break; @@ -14745,7 +14741,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) /* SYNCI */ /* Break the TB to be able to sync copied instructions immediately */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; } else { /* TNEI */ mips32_op = OPC_TNEI; @@ -14776,7 +14772,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) check_insn_opc_removed(ctx, ISA_MIPS32R6); /* Break the TB to be able to sync copied instructions immediately */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case BC2F: case BC2T: @@ -19601,7 +19597,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) check_insn(ctx, ISA_MIPS32R2); /* Break the TB to be able to sync copied instructions immediately */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case OPC_BPOSGE32: /* MIPS DSP branch */ #if defined(TARGET_MIPS64) @@ -19704,17 +19700,17 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) gen_store_gpr(t0, rt); /* Stop translation as we may have switched the execution mode. */ - ctx->bstate = BS_STOP; + ctx->is_jmp = DISAS_STOP; break; case OPC_EI: check_insn(ctx, ISA_MIPS32R2); save_cpu_state(ctx, 1); gen_helper_ei(t0, cpu_env); gen_store_gpr(t0, rt); - /* BS_STOP isn't sufficient, we need to ensure we break out - of translated code to check for pending interrupts. */ + /* DISAS_STOP isn't sufficient, we need to ensure we break + out of translated code to check for pending interrupts */ gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + ctx->is_jmp = DISAS_EXIT; break; default: /* Invalid */ MIPS_INVAL("mfmc0"); @@ -20216,7 +20212,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) ctx.insn_flags = env->insn_flags; ctx.CP0_Config1 = env->CP0_Config1; ctx.tb = tb; - ctx.bstate = BS_NONE; + ctx.is_jmp = DISAS_NEXT; ctx.btarget = 0; ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; @@ -20257,13 +20253,13 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags); gen_tb_start(tb); - while (ctx.bstate == BS_NONE) { + while (ctx.is_jmp == DISAS_NEXT) { tcg_gen_insn_start(ctx.pc, ctx.hflags & MIPS_HFLAG_BMASK, ctx.btarget); num_insns++; if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { save_cpu_state(&ctx, 1); - ctx.bstate = BS_BRANCH; + ctx.is_jmp = DISAS_NORETURN; gen_helper_raise_exception_debug(cpu_env); /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be @@ -20337,23 +20333,23 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (tb_cflags(tb) & CF_LAST_IO) { gen_io_end(); } - if (cs->singlestep_enabled && ctx.bstate != BS_BRANCH) { - save_cpu_state(&ctx, ctx.bstate != BS_EXCP); + if (cs->singlestep_enabled && ctx.is_jmp != DISAS_NORETURN) { + save_cpu_state(&ctx, ctx.is_jmp != DISAS_EXIT); gen_helper_raise_exception_debug(cpu_env); } else { - switch (ctx.bstate) { - case BS_STOP: + switch (ctx.is_jmp) { + case DISAS_STOP: gen_save_pc(ctx.pc); tcg_gen_lookup_and_goto_ptr(); break; - case BS_NONE: + case DISAS_NEXT: save_cpu_state(&ctx, 0); gen_goto_tb(&ctx, 0, ctx.pc); break; - case BS_EXCP: + case DISAS_EXIT: tcg_gen_exit_tb(0); break; - case BS_BRANCH: + case DISAS_NORETURN: default: break; } From eeb3bba8477cebc46c482ef37d565d54e771c2d3 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Thu, 15 Feb 2018 20:00:49 -0500 Subject: [PATCH 0350/2380] target/mips: convert to DisasContextBase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Cc: Aurelien Jarno Cc: Yongbok Kim Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/mips/translate.c | 346 ++++++++++++++++++++-------------------- 1 file changed, 175 insertions(+), 171 deletions(-) diff --git a/target/mips/translate.c b/target/mips/translate.c index 83a484822e..31c7425cfe 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -1430,17 +1430,15 @@ static TCGv_i64 msa_wr_d[64]; } while(0) typedef struct DisasContext { - struct TranslationBlock *tb; - target_ulong pc, saved_pc; + DisasContextBase base; + target_ulong saved_pc; uint32_t opcode; - int singlestep_enabled; int insn_flags; int32_t CP0_Config1; /* Routine used to access memory */ int mem_idx; TCGMemOp default_tcg_memop_mask; uint32_t hflags, saved_hflags; - DisasJumpType is_jmp; target_ulong btarget; bool ulri; int kscrexist; @@ -1517,8 +1515,9 @@ static const char * const msaregnames[] = { if (MIPS_DEBUG_DISAS) { \ qemu_log_mask(CPU_LOG_TB_IN_ASM, \ TARGET_FMT_lx ": %08x Invalid %s %03x %03x %03x\n", \ - ctx->pc, ctx->opcode, op, ctx->opcode >> 26, \ - ctx->opcode & 0x3F, ((ctx->opcode >> 16) & 0x1F)); \ + ctx->base.pc_next, ctx->opcode, op, \ + ctx->opcode >> 26, ctx->opcode & 0x3F, \ + ((ctx->opcode >> 16) & 0x1F)); \ } \ } while (0) @@ -1594,9 +1593,9 @@ static inline void gen_save_pc(target_ulong 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 (do_save_pc && ctx->base.pc_next != ctx->saved_pc) { + gen_save_pc(ctx->base.pc_next); + ctx->saved_pc = ctx->base.pc_next; } if (ctx->hflags != ctx->saved_hflags) { tcg_gen_movi_i32(hflags, ctx->hflags); @@ -1635,7 +1634,7 @@ static inline void generate_exception_err(DisasContext *ctx, int excp, int err) gen_helper_raise_exception_err(cpu_env, texcp, terr); tcg_temp_free_i32(terr); tcg_temp_free_i32(texcp); - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } static inline void generate_exception(DisasContext *ctx, int excp) @@ -2126,7 +2125,7 @@ static void gen_base_offset_addr (DisasContext *ctx, TCGv addr, static target_ulong pc_relative_pc (DisasContext *ctx) { - target_ulong pc = ctx->pc; + target_ulong pc = ctx->base.pc_next; if (ctx->hflags & MIPS_HFLAG_BMASK) { int branch_bytes = ctx->hflags & MIPS_HFLAG_BDS16 ? 2 : 4; @@ -4275,12 +4274,12 @@ static void gen_trap (DisasContext *ctx, uint32_t opc, static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) { - if (unlikely(ctx->singlestep_enabled)) { + if (unlikely(ctx->base.singlestep_enabled)) { return false; } #ifndef CONFIG_USER_ONLY - return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); + return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); #else return true; #endif @@ -4291,10 +4290,10 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); gen_save_pc(dest); - tcg_gen_exit_tb((uintptr_t)ctx->tb + n); + tcg_gen_exit_tb((uintptr_t)ctx->base.tb + n); } else { gen_save_pc(dest); - if (ctx->singlestep_enabled) { + if (ctx->base.singlestep_enabled) { save_cpu_state(ctx, 0); gen_helper_raise_exception_debug(cpu_env); } @@ -4317,7 +4316,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS LOG_DISAS("Branch in delay / forbidden slot at PC 0x" - TARGET_FMT_lx "\n", ctx->pc); + TARGET_FMT_lx "\n", ctx->base.pc_next); #endif generate_exception_end(ctx, EXCP_RI); goto out; @@ -4335,7 +4334,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, gen_load_gpr(t1, rt); bcond_compute = 1; } - btgt = ctx->pc + insn_bytes + offset; + btgt = ctx->base.pc_next + insn_bytes + offset; break; case OPC_BGEZ: case OPC_BGEZAL: @@ -4354,7 +4353,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, gen_load_gpr(t0, rs); bcond_compute = 1; } - btgt = ctx->pc + insn_bytes + offset; + btgt = ctx->base.pc_next + insn_bytes + offset; break; case OPC_BPOSGE32: #if defined(TARGET_MIPS64) @@ -4364,13 +4363,14 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, tcg_gen_andi_tl(t0, cpu_dspctrl, 0x3F); #endif bcond_compute = 1; - btgt = ctx->pc + insn_bytes + offset; + btgt = ctx->base.pc_next + insn_bytes + offset; break; case OPC_J: case OPC_JAL: case OPC_JALX: /* Jump to immediate */ - btgt = ((ctx->pc + insn_bytes) & (int32_t)0xF0000000) | (uint32_t)offset; + btgt = ((ctx->base.pc_next + insn_bytes) & (int32_t)0xF0000000) | + (uint32_t)offset; break; case OPC_JR: case OPC_JALR: @@ -4416,19 +4416,19 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, /* Handle as an unconditional branch to get correct delay slot checking. */ blink = 31; - btgt = ctx->pc + insn_bytes + delayslot_size; + btgt = ctx->base.pc_next + insn_bytes + delayslot_size; ctx->hflags |= MIPS_HFLAG_B; break; case OPC_BLTZALL: /* 0 < 0 likely */ - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 8); + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 8); /* Skip the instruction in the delay slot */ - ctx->pc += 4; + ctx->base.pc_next += 4; goto out; case OPC_BNEL: /* rx != rx likely */ case OPC_BGTZL: /* 0 > 0 likely */ case OPC_BLTZL: /* 0 < 0 likely */ /* Skip the instruction in the delay slot */ - ctx->pc += 4; + ctx->base.pc_next += 4; goto out; case OPC_J: ctx->hflags |= MIPS_HFLAG_B; @@ -4540,7 +4540,8 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, int post_delay = insn_bytes + delayslot_size; int lowbit = !!(ctx->hflags & MIPS_HFLAG_M16); - tcg_gen_movi_tl(cpu_gpr[blink], ctx->pc + post_delay + lowbit); + tcg_gen_movi_tl(cpu_gpr[blink], + ctx->base.pc_next + post_delay + lowbit); } out: @@ -5322,18 +5323,18 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: /* Mark as an IO operation because we read the time. */ - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_mfc0_count(arg, cpu_env); - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } /* Break the TB to be able to take timer interrupts immediately after reading count. DISAS_STOP isn't sufficient, we need to ensure we break completely out of translated code. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Count"; break; /* 6,7 are implementation dependent */ @@ -5729,7 +5730,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) if (sel != 0) check_insn(ctx, ISA_MIPS32); - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } @@ -5901,7 +5902,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->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 2: CP0_CHECK(ctx->sc); @@ -5962,7 +5963,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "HWREna"; break; default: @@ -6025,29 +6026,29 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) save_cpu_state(ctx, 1); gen_helper_mtc0_status(cpu_env, arg); /* DISAS_STOP isn't good enough here, hflags may have changed. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Status"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "IntCtl"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "SRSCtl"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "SRSMap"; break; default: @@ -6062,8 +6063,8 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* Stop translation as we may have triggered an interrupt. * DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Cause"; break; default: @@ -6101,7 +6102,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config0(cpu_env, arg); rn = "Config"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 1: /* ignored, read only */ @@ -6111,24 +6112,24 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config2(cpu_env, arg); rn = "Config2"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 3: gen_helper_mtc0_config3(cpu_env, arg); rn = "Config3"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 4: gen_helper_mtc0_config4(cpu_env, arg); rn = "Config4"; - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 5: gen_helper_mtc0_config5(cpu_env, arg); rn = "Config5"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; /* 6,7 are implementation dependent */ case 6: @@ -6218,34 +6219,34 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ /* DISAS_STOP isn't good enough here, hflags may have changed. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Debug"; break; case 1: // gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */ rn = "TraceControl"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; case 2: // gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */ rn = "TraceControl2"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; case 3: /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; // gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */ rn = "UserTraceData"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; case 4: // gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "TraceBPC"; goto cp0_unimplemented; default: @@ -6305,7 +6306,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: gen_helper_mtc0_errctl(cpu_env, arg); - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "ErrCtl"; break; default: @@ -6396,12 +6397,12 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) trace_mips_translate_c0("mtc0", rn, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); /* DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; } return; @@ -6674,18 +6675,18 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: /* Mark as an IO operation because we read the time. */ - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_mfc0_count(arg, cpu_env); - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } /* Break the TB to be able to take timer interrupts immediately after reading count. DISAS_STOP isn't sufficient, we need to ensure we break completely out of translated code. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Count"; break; /* 6,7 are implementation dependent */ @@ -7067,7 +7068,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) if (sel != 0) check_insn(ctx, ISA_MIPS64); - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } @@ -7297,7 +7298,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "HWREna"; break; default: @@ -7333,7 +7334,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 10: switch (sel) { @@ -7356,7 +7357,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 12: switch (sel) { @@ -7364,29 +7365,29 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) save_cpu_state(ctx, 1); gen_helper_mtc0_status(cpu_env, arg); /* DISAS_STOP isn't good enough here, hflags may have changed. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Status"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "IntCtl"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "SRSCtl"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "SRSMap"; break; default: @@ -7401,8 +7402,8 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* Stop translation as we may have triggered an interrupt. * DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Cause"; break; default: @@ -7440,7 +7441,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config0(cpu_env, arg); rn = "Config"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 1: /* ignored, read only */ @@ -7450,13 +7451,13 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config2(cpu_env, arg); rn = "Config2"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 3: gen_helper_mtc0_config3(cpu_env, arg); rn = "Config3"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 4: /* currently ignored */ @@ -7466,7 +7467,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config5(cpu_env, arg); rn = "Config5"; /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; /* 6,7 are implementation dependent */ default: @@ -7546,32 +7547,32 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ /* DISAS_STOP isn't good enough here, hflags may have changed. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Debug"; break; case 1: // gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "TraceControl"; goto cp0_unimplemented; case 2: // gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "TraceControl2"; goto cp0_unimplemented; case 3: // gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "UserTraceData"; goto cp0_unimplemented; case 4: // gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "TraceBPC"; goto cp0_unimplemented; default: @@ -7631,7 +7632,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: gen_helper_mtc0_errctl(cpu_env, arg); - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "ErrCtl"; break; default: @@ -7722,12 +7723,12 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) trace_mips_translate_c0("dmtc0", rn, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); /* DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; } return; @@ -8138,7 +8139,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, tcg_temp_free_i32(fs_tmp); } /* Stop translation as we may have changed hflags */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; /* COP2: Not implemented. */ case 4: @@ -8297,7 +8298,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, check_insn(ctx, ISA_MIPS2); gen_helper_eret(cpu_env); } - ctx->is_jmp = DISAS_EXIT; + ctx->base.is_jmp = DISAS_EXIT; } break; case OPC_DERET: @@ -8312,7 +8313,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, generate_exception_end(ctx, EXCP_RI); } else { gen_helper_deret(cpu_env); - ctx->is_jmp = DISAS_EXIT; + ctx->base.is_jmp = DISAS_EXIT; } break; case OPC_WAIT: @@ -8323,11 +8324,11 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, goto die; } /* If we get an exception, we want to restart at next instruction */ - ctx->pc += 4; + ctx->base.pc_next += 4; save_cpu_state(ctx, 1); - ctx->pc -= 4; + ctx->base.pc_next -= 4; gen_helper_wait(cpu_env); - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; break; default: die: @@ -8354,7 +8355,7 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, if (cc != 0) check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); - btarget = ctx->pc + 4 + offset; + btarget = ctx->base.pc_next + 4 + offset; switch (op) { case OPC_BC1F: @@ -8457,7 +8458,7 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx - "\n", ctx->pc); + "\n", ctx->base.pc_next); #endif generate_exception_end(ctx, EXCP_RI); goto out; @@ -8466,7 +8467,7 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, gen_load_fpr64(ctx, t0, ft); tcg_gen_andi_i64(t0, t0, 1); - btarget = addr_add(ctx, ctx->pc + 4, offset); + btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); switch (op) { case OPC_BC1EQZ: @@ -8752,7 +8753,7 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) tcg_temp_free_i32(fs_tmp); } /* Stop translation as we may have changed hflags */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; #if defined(TARGET_MIPS64) case OPC_DMFC1: @@ -10751,19 +10752,19 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) gen_store_gpr(t0, rt); break; case 2: - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_rdhwr_cc(t0, cpu_env); - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } gen_store_gpr(t0, rt); /* Break the TB to be able to take timer interrupts immediately after reading count. DISAS_STOP isn't sufficient, we need to ensure we break completely out of translated code. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; break; case 3: gen_helper_rdhwr_ccres(t0, cpu_env); @@ -10813,7 +10814,7 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) static inline void clear_branch_hflags(DisasContext *ctx) { ctx->hflags &= ~MIPS_HFLAG_BMASK; - if (ctx->is_jmp == DISAS_NEXT) { + if (ctx->base.is_jmp == DISAS_NEXT) { save_cpu_state(ctx, 0); } else { /* it is not safe to save ctx->hflags as hflags may be changed @@ -10828,11 +10829,11 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK; /* Branches completion */ clear_branch_hflags(ctx); - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; /* FIXME: Need to clear can_do_io. */ switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) { case MIPS_HFLAG_FBNSLOT: - gen_goto_tb(ctx, 0, ctx->pc + insn_bytes); + gen_goto_tb(ctx, 0, ctx->base.pc_next + insn_bytes); break; case MIPS_HFLAG_B: /* unconditional branch */ @@ -10851,7 +10852,7 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) TCGLabel *l1 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); - gen_goto_tb(ctx, 1, ctx->pc + insn_bytes); + gen_goto_tb(ctx, 1, ctx->base.pc_next + insn_bytes); gen_set_label(l1); gen_goto_tb(ctx, 0, ctx->btarget); } @@ -10874,7 +10875,7 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) } else { tcg_gen_mov_tl(cpu_PC, btarget); } - if (ctx->singlestep_enabled) { + if (ctx->base.singlestep_enabled) { save_cpu_state(ctx, 0); gen_helper_raise_exception_debug(cpu_env); } @@ -10899,7 +10900,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx - "\n", ctx->pc); + "\n", ctx->base.pc_next); #endif generate_exception_end(ctx, EXCP_RI); goto out; @@ -10913,10 +10914,10 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); if (rs <= rt && rs == 0) { /* OPC_BEQZALC, OPC_BNEZALC */ - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4 + m16_lowbit); } break; case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ @@ -10924,23 +10925,23 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); break; case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ if (rs == 0 || rs == rt) { /* OPC_BLEZALC, OPC_BGEZALC */ /* OPC_BGTZALC, OPC_BLTZALC */ - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4 + m16_lowbit); } gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); break; case OPC_BC: case OPC_BALC: - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); break; case OPC_BEQZC: case OPC_BNEZC: @@ -10948,7 +10949,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, /* OPC_BEQZC, OPC_BNEZC */ gen_load_gpr(t0, rs); bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); } else { /* OPC_JIC, OPC_JIALC */ TCGv tbase = tcg_temp_new(); @@ -10971,13 +10972,13 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, /* Uncoditional compact branch */ switch (opc) { case OPC_JIALC: - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4 + m16_lowbit); /* Fallthrough */ case OPC_JIC: ctx->hflags |= MIPS_HFLAG_BR; break; case OPC_BALC: - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4 + m16_lowbit); /* Fallthrough */ case OPC_BC: ctx->hflags |= MIPS_HFLAG_B; @@ -11602,7 +11603,7 @@ static void decode_i64_mips16 (DisasContext *ctx, static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) { - int extend = cpu_lduw_code(env, ctx->pc + 2); + int extend = cpu_lduw_code(env, ctx->base.pc_next + 2); int op, rx, ry, funct, sa; int16_t imm, offset; @@ -11842,7 +11843,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) /* No delay slot, so just process as a normal instruction */ break; case M16_OPC_JAL: - offset = cpu_lduw_code(env, ctx->pc + 2); + offset = cpu_lduw_code(env, ctx->base.pc_next + 2); offset = (((ctx->opcode & 0x1f) << 21) | ((ctx->opcode >> 5) & 0x1f) << 16 | offset) << 2; @@ -13570,7 +13571,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) gen_helper_di(t0, cpu_env); gen_store_gpr(t0, rs); /* Stop translation as we may have switched the execution mode */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; tcg_temp_free(t0); } break; @@ -13584,8 +13585,8 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) gen_store_gpr(t0, rs); /* DISAS_STOP isn't sufficient, we need to ensure we break out of translated code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; tcg_temp_free(t0); } break; @@ -13940,7 +13941,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) uint32_t op, minor, minor2, mips32_op; uint32_t cond, fmt, cc; - insn = cpu_lduw_code(env, ctx->pc + 2); + insn = cpu_lduw_code(env, ctx->base.pc_next + 2); ctx->opcode = (ctx->opcode << 16) | insn; rt = (ctx->opcode >> 21) & 0x1f; @@ -14741,7 +14742,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) /* SYNCI */ /* Break the TB to be able to sync copied instructions immediately */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; } else { /* TNEI */ mips32_op = OPC_TNEI; @@ -14772,7 +14773,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) check_insn_opc_removed(ctx, ISA_MIPS32R6); /* Break the TB to be able to sync copied instructions immediately */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case BC2F: case BC2T: @@ -15135,16 +15136,16 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) /* PCREL: ADDIUPC, AUIPC, ALUIPC, LWPC */ switch ((ctx->opcode >> 16) & 0x1f) { case ADDIUPC_00 ... ADDIUPC_07: - gen_pcrel(ctx, OPC_ADDIUPC, ctx->pc & ~0x3, rt); + gen_pcrel(ctx, OPC_ADDIUPC, ctx->base.pc_next & ~0x3, rt); break; case AUIPC: - gen_pcrel(ctx, OPC_AUIPC, ctx->pc, rt); + gen_pcrel(ctx, OPC_AUIPC, ctx->base.pc_next, rt); break; case ALUIPC: - gen_pcrel(ctx, OPC_ALUIPC, ctx->pc, rt); + gen_pcrel(ctx, OPC_ALUIPC, ctx->base.pc_next, rt); break; case LWPC_08 ... LWPC_0F: - gen_pcrel(ctx, R6_OPC_LWPC, ctx->pc & ~0x3, rt); + gen_pcrel(ctx, R6_OPC_LWPC, ctx->base.pc_next & ~0x3, rt); break; default: generate_exception(ctx, EXCP_RI); @@ -15276,8 +15277,8 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) uint32_t op; /* make sure instructions are on a halfword boundary */ - if (ctx->pc & 0x1) { - env->CP0_BadVAddr = ctx->pc; + if (ctx->base.pc_next & 0x1) { + env->CP0_BadVAddr = ctx->base.pc_next; generate_exception_end(ctx, EXCP_AdEL); return 2; } @@ -18503,7 +18504,7 @@ static void gen_msa_branch(CPUMIPSState *env, DisasContext *ctx, uint32_t op1) break; } - ctx->btarget = ctx->pc + (s16 << 2) + 4; + ctx->btarget = ctx->base.pc_next + (s16 << 2) + 4; ctx->hflags |= MIPS_HFLAG_BC; ctx->hflags |= MIPS_HFLAG_BDS32; @@ -19524,8 +19525,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) int16_t imm; /* make sure instructions are on a word boundary */ - if (ctx->pc & 0x3) { - env->CP0_BadVAddr = ctx->pc; + if (ctx->base.pc_next & 0x3) { + env->CP0_BadVAddr = ctx->base.pc_next; generate_exception_err(ctx, EXCP_AdEL, EXCP_INST_NOTAVAIL); return; } @@ -19536,7 +19537,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); tcg_gen_movi_i32(hflags, ctx->hflags & ~MIPS_HFLAG_BMASK); - gen_goto_tb(ctx, 1, ctx->pc + 4); + gen_goto_tb(ctx, 1, ctx->base.pc_next + 4); gen_set_label(l1); } @@ -19597,7 +19598,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) check_insn(ctx, ISA_MIPS32R2); /* Break the TB to be able to sync copied instructions immediately */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case OPC_BPOSGE32: /* MIPS DSP branch */ #if defined(TARGET_MIPS64) @@ -19700,7 +19701,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) gen_store_gpr(t0, rt); /* Stop translation as we may have switched the execution mode. */ - ctx->is_jmp = DISAS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case OPC_EI: check_insn(ctx, ISA_MIPS32R2); @@ -19709,8 +19710,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) gen_store_gpr(t0, rt); /* DISAS_STOP isn't sufficient, we need to ensure we break out of translated code to check for pending interrupts */ - gen_save_pc(ctx->pc + 4); - ctx->is_jmp = DISAS_EXIT; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; break; default: /* Invalid */ MIPS_INVAL("mfmc0"); @@ -20184,7 +20185,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) break; case OPC_PCREL: check_insn(ctx, ISA_MIPS32R6); - gen_pcrel(ctx, ctx->opcode, ctx->pc, rs); + gen_pcrel(ctx, ctx->opcode, ctx->base.pc_next, rs); break; default: /* Invalid */ MIPS_INVAL("major opcode"); @@ -20197,22 +20198,22 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) { CPUMIPSState *env = cs->env_ptr; DisasContext ctx; - target_ulong pc_start; target_ulong page_start; - int num_insns; int max_insns; int insn_bytes; int is_slot; - pc_start = tb->pc; - page_start = pc_start & TARGET_PAGE_MASK; - ctx.pc = pc_start; + ctx.base.tb = tb; + ctx.base.pc_first = tb->pc; + ctx.base.pc_next = tb->pc; + ctx.base.is_jmp = DISAS_NEXT; + ctx.base.singlestep_enabled = cs->singlestep_enabled; + ctx.base.num_insns = 0; + + page_start = ctx.base.pc_first & TARGET_PAGE_MASK; ctx.saved_pc = -1; - ctx.singlestep_enabled = cs->singlestep_enabled; ctx.insn_flags = env->insn_flags; ctx.CP0_Config1 = env->CP0_Config1; - ctx.tb = tb; - ctx.is_jmp = DISAS_NEXT; ctx.btarget = 0; ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; @@ -20226,7 +20227,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift; ctx.cmgcr = (env->CP0_Config3 >> CP0C3_CMGCR) & 1; /* Restore delay slot state from the tb context. */ - ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */ + ctx.hflags = (uint32_t)ctx.base.tb->flags; /* FIXME: maybe use 64 bits? */ ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)); @@ -20242,7 +20243,6 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) #endif ctx.default_tcg_memop_mask = (ctx.insn_flags & ISA_MIPS32R6) ? MO_UNALN : MO_ALIGN; - num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; @@ -20253,36 +20253,37 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags); gen_tb_start(tb); - while (ctx.is_jmp == DISAS_NEXT) { - tcg_gen_insn_start(ctx.pc, ctx.hflags & MIPS_HFLAG_BMASK, ctx.btarget); - num_insns++; + while (ctx.base.is_jmp == DISAS_NEXT) { + tcg_gen_insn_start(ctx.base.pc_next, ctx.hflags & MIPS_HFLAG_BMASK, + ctx.btarget); + ctx.base.num_insns++; - if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { + if (unlikely(cpu_breakpoint_test(cs, ctx.base.pc_next, BP_ANY))) { save_cpu_state(&ctx, 1); - ctx.is_jmp = DISAS_NORETURN; + ctx.base.is_jmp = DISAS_NORETURN; gen_helper_raise_exception_debug(cpu_env); /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that the logic setting tb->size below does the right thing. */ - ctx.pc += 4; + ctx.base.pc_next += 4; goto done_generating; } - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { + if (ctx.base.num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { gen_io_start(); } is_slot = ctx.hflags & MIPS_HFLAG_BMASK; if (!(ctx.hflags & MIPS_HFLAG_M16)) { - ctx.opcode = cpu_ldl_code(env, ctx.pc); + ctx.opcode = cpu_ldl_code(env, ctx.base.pc_next); insn_bytes = 4; decode_opc(env, &ctx); } else if (ctx.insn_flags & ASE_MICROMIPS) { - ctx.opcode = cpu_lduw_code(env, ctx.pc); + ctx.opcode = cpu_lduw_code(env, ctx.base.pc_next); insn_bytes = decode_micromips_opc(env, &ctx); } else if (ctx.insn_flags & ASE_MIPS16) { - ctx.opcode = cpu_lduw_code(env, ctx.pc); + ctx.opcode = cpu_lduw_code(env, ctx.base.pc_next); insn_bytes = decode_mips16_opc(env, &ctx); } else { generate_exception_end(&ctx, EXCP_RI); @@ -20306,17 +20307,18 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (is_slot) { gen_branch(&ctx, insn_bytes); } - ctx.pc += insn_bytes; + ctx.base.pc_next += insn_bytes; /* Execute a branch and its delay slot as a single instruction. This is what GDB expects and is consistent with what the hardware does (e.g. if a delay slot instruction faults, the reported PC is the PC of the branch). */ - if (cs->singlestep_enabled && (ctx.hflags & MIPS_HFLAG_BMASK) == 0) { + if (ctx.base.singlestep_enabled && + (ctx.hflags & MIPS_HFLAG_BMASK) == 0) { break; } - if (ctx.pc - page_start >= TARGET_PAGE_SIZE) { + if (ctx.base.pc_next - page_start >= TARGET_PAGE_SIZE) { break; } @@ -20324,8 +20326,9 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) break; } - if (num_insns >= max_insns) + if (ctx.base.num_insns >= max_insns) { break; + } if (singlestep) break; @@ -20333,18 +20336,18 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (tb_cflags(tb) & CF_LAST_IO) { gen_io_end(); } - if (cs->singlestep_enabled && ctx.is_jmp != DISAS_NORETURN) { - save_cpu_state(&ctx, ctx.is_jmp != DISAS_EXIT); + if (ctx.base.singlestep_enabled && ctx.base.is_jmp != DISAS_NORETURN) { + save_cpu_state(&ctx, ctx.base.is_jmp != DISAS_EXIT); gen_helper_raise_exception_debug(cpu_env); } else { - switch (ctx.is_jmp) { + switch (ctx.base.is_jmp) { case DISAS_STOP: - gen_save_pc(ctx.pc); + gen_save_pc(ctx.base.pc_next); tcg_gen_lookup_and_goto_ptr(); break; case DISAS_NEXT: save_cpu_state(&ctx, 0); - gen_goto_tb(&ctx, 0, ctx.pc); + gen_goto_tb(&ctx, 0, ctx.base.pc_next); break; case DISAS_EXIT: tcg_gen_exit_tb(0); @@ -20355,18 +20358,19 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } } done_generating: - gen_tb_end(tb, num_insns); + gen_tb_end(tb, ctx.base.num_insns); - tb->size = ctx.pc - pc_start; - tb->icount = num_insns; + tb->size = ctx.base.pc_next - ctx.base.pc_first; + tb->icount = ctx.base.num_insns; #ifdef DEBUG_DISAS LOG_DISAS("\n"); if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { + && qemu_log_in_addr_range(ctx.base.pc_first)) { qemu_log_lock(); - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, ctx.pc - pc_start); + qemu_log("IN: %s\n", lookup_symbol(ctx.base.pc_first)); + log_target_disas(cs, ctx.base.pc_first, + ctx.base.pc_next - ctx.base.pc_first); qemu_log("\n"); qemu_log_unlock(); } From 12be92588cf26a192f1b62846906983fc1e102a7 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Thu, 15 Feb 2018 20:45:46 -0500 Subject: [PATCH 0351/2380] target/mips: use *ctx for DisasContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No changes to the logic here; this is just to make the diff that follows easier to read. While at it, remove the unnecessary 'struct' in 'struct TranslationBlock'. Note that checkpatch complains with a false positive: ERROR: space prohibited after that '&' (ctx:WxW) #75: FILE: target/mips/translate.c:20220: + ctx->kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; ^ Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Cc: Aurelien Jarno Cc: Yongbok Kim Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/mips/translate.c | 161 ++++++++++++++++++++-------------------- 1 file changed, 81 insertions(+), 80 deletions(-) diff --git a/target/mips/translate.c b/target/mips/translate.c index 31c7425cfe..7a931ea3e7 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -20194,55 +20194,56 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) } } -void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) { CPUMIPSState *env = cs->env_ptr; - DisasContext ctx; + DisasContext ctx1; + DisasContext *ctx = &ctx1; target_ulong page_start; int max_insns; int insn_bytes; int is_slot; - ctx.base.tb = tb; - ctx.base.pc_first = tb->pc; - ctx.base.pc_next = tb->pc; - ctx.base.is_jmp = DISAS_NEXT; - ctx.base.singlestep_enabled = cs->singlestep_enabled; - ctx.base.num_insns = 0; + ctx->base.tb = tb; + ctx->base.pc_first = tb->pc; + ctx->base.pc_next = tb->pc; + ctx->base.is_jmp = DISAS_NEXT; + ctx->base.singlestep_enabled = cs->singlestep_enabled; + ctx->base.num_insns = 0; - page_start = ctx.base.pc_first & TARGET_PAGE_MASK; - ctx.saved_pc = -1; - ctx.insn_flags = env->insn_flags; - ctx.CP0_Config1 = env->CP0_Config1; - ctx.btarget = 0; - ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; - ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; - 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.eva = (env->CP0_Config5 >> CP0C5_EVA) & 1; - ctx.sc = (env->CP0_Config3 >> CP0C3_SC) & 1; - ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift; - ctx.cmgcr = (env->CP0_Config3 >> CP0C3_CMGCR) & 1; + page_start = ctx->base.pc_first & TARGET_PAGE_MASK; + ctx->saved_pc = -1; + ctx->insn_flags = env->insn_flags; + ctx->CP0_Config1 = env->CP0_Config1; + ctx->btarget = 0; + ctx->kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; + ctx->rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; + 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->eva = (env->CP0_Config5 >> CP0C5_EVA) & 1; + ctx->sc = (env->CP0_Config3 >> CP0C3_SC) & 1; + ctx->CP0_LLAddr_shift = env->CP0_LLAddr_shift; + ctx->cmgcr = (env->CP0_Config3 >> CP0C3_CMGCR) & 1; /* Restore delay slot state from the tb context. */ - ctx.hflags = (uint32_t)ctx.base.tb->flags; /* FIXME: maybe use 64 bits? */ - ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; - ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || + ctx->hflags = (uint32_t)ctx->base.tb->flags; /* FIXME: maybe use 64 bits? */ + ctx->ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; + ctx->ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)); - ctx.vp = (env->CP0_Config5 >> CP0C5_VP) & 1; - ctx.mrp = (env->CP0_Config5 >> CP0C5_MRP) & 1; - ctx.nan2008 = (env->active_fpu.fcr31 >> FCR31_NAN2008) & 1; - ctx.abs2008 = (env->active_fpu.fcr31 >> FCR31_ABS2008) & 1; - restore_cpu_state(env, &ctx); + ctx->vp = (env->CP0_Config5 >> CP0C5_VP) & 1; + ctx->mrp = (env->CP0_Config5 >> CP0C5_MRP) & 1; + ctx->nan2008 = (env->active_fpu.fcr31 >> FCR31_NAN2008) & 1; + ctx->abs2008 = (env->active_fpu.fcr31 >> FCR31_ABS2008) & 1; + restore_cpu_state(env, ctx); #ifdef CONFIG_USER_ONLY - ctx.mem_idx = MIPS_HFLAG_UM; + ctx->mem_idx = MIPS_HFLAG_UM; #else - ctx.mem_idx = hflags_mmu_index(ctx.hflags); + ctx->mem_idx = hflags_mmu_index(ctx->hflags); #endif - ctx.default_tcg_memop_mask = (ctx.insn_flags & ISA_MIPS32R6) ? - MO_UNALN : MO_ALIGN; + ctx->default_tcg_memop_mask = (ctx->insn_flags & ISA_MIPS32R6) ? + MO_UNALN : MO_ALIGN; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; @@ -20251,74 +20252,74 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) max_insns = TCG_MAX_INSNS; } - LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags); + LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx->mem_idx, ctx->hflags); gen_tb_start(tb); - while (ctx.base.is_jmp == DISAS_NEXT) { - tcg_gen_insn_start(ctx.base.pc_next, ctx.hflags & MIPS_HFLAG_BMASK, - ctx.btarget); - ctx.base.num_insns++; + while (ctx->base.is_jmp == DISAS_NEXT) { + tcg_gen_insn_start(ctx->base.pc_next, ctx->hflags & MIPS_HFLAG_BMASK, + ctx->btarget); + ctx->base.num_insns++; - if (unlikely(cpu_breakpoint_test(cs, ctx.base.pc_next, BP_ANY))) { - save_cpu_state(&ctx, 1); - ctx.base.is_jmp = DISAS_NORETURN; + if (unlikely(cpu_breakpoint_test(cs, ctx->base.pc_next, BP_ANY))) { + save_cpu_state(ctx, 1); + ctx->base.is_jmp = DISAS_NORETURN; gen_helper_raise_exception_debug(cpu_env); /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that the logic setting tb->size below does the right thing. */ - ctx.base.pc_next += 4; + ctx->base.pc_next += 4; goto done_generating; } - if (ctx.base.num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { + if (ctx->base.num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { gen_io_start(); } - is_slot = ctx.hflags & MIPS_HFLAG_BMASK; - if (!(ctx.hflags & MIPS_HFLAG_M16)) { - ctx.opcode = cpu_ldl_code(env, ctx.base.pc_next); + is_slot = ctx->hflags & MIPS_HFLAG_BMASK; + if (!(ctx->hflags & MIPS_HFLAG_M16)) { + ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); insn_bytes = 4; - decode_opc(env, &ctx); - } else if (ctx.insn_flags & ASE_MICROMIPS) { - ctx.opcode = cpu_lduw_code(env, ctx.base.pc_next); - insn_bytes = decode_micromips_opc(env, &ctx); - } else if (ctx.insn_flags & ASE_MIPS16) { - ctx.opcode = cpu_lduw_code(env, ctx.base.pc_next); - insn_bytes = decode_mips16_opc(env, &ctx); + decode_opc(env, ctx); + } else if (ctx->insn_flags & ASE_MICROMIPS) { + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); + insn_bytes = decode_micromips_opc(env, ctx); + } else if (ctx->insn_flags & ASE_MIPS16) { + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); + insn_bytes = decode_mips16_opc(env, ctx); } else { - generate_exception_end(&ctx, EXCP_RI); + generate_exception_end(ctx, EXCP_RI); break; } - if (ctx.hflags & MIPS_HFLAG_BMASK) { - if (!(ctx.hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32 | + if (ctx->hflags & MIPS_HFLAG_BMASK) { + if (!(ctx->hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32 | MIPS_HFLAG_FBNSLOT))) { /* force to generate branch as there is neither delay nor forbidden slot */ is_slot = 1; } - if ((ctx.hflags & MIPS_HFLAG_M16) && - (ctx.hflags & MIPS_HFLAG_FBNSLOT)) { + if ((ctx->hflags & MIPS_HFLAG_M16) && + (ctx->hflags & MIPS_HFLAG_FBNSLOT)) { /* Force to generate branch as microMIPS R6 doesn't restrict branches in the forbidden slot. */ is_slot = 1; } } if (is_slot) { - gen_branch(&ctx, insn_bytes); + gen_branch(ctx, insn_bytes); } - ctx.base.pc_next += insn_bytes; + ctx->base.pc_next += insn_bytes; /* Execute a branch and its delay slot as a single instruction. This is what GDB expects and is consistent with what the hardware does (e.g. if a delay slot instruction faults, the reported PC is the PC of the branch). */ - if (ctx.base.singlestep_enabled && - (ctx.hflags & MIPS_HFLAG_BMASK) == 0) { + if (ctx->base.singlestep_enabled && + (ctx->hflags & MIPS_HFLAG_BMASK) == 0) { break; } - if (ctx.base.pc_next - page_start >= TARGET_PAGE_SIZE) { + if (ctx->base.pc_next - page_start >= TARGET_PAGE_SIZE) { break; } @@ -20326,7 +20327,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) break; } - if (ctx.base.num_insns >= max_insns) { + if (ctx->base.num_insns >= max_insns) { break; } @@ -20336,18 +20337,18 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (tb_cflags(tb) & CF_LAST_IO) { gen_io_end(); } - if (ctx.base.singlestep_enabled && ctx.base.is_jmp != DISAS_NORETURN) { - save_cpu_state(&ctx, ctx.base.is_jmp != DISAS_EXIT); + if (ctx->base.singlestep_enabled && ctx->base.is_jmp != DISAS_NORETURN) { + save_cpu_state(ctx, ctx->base.is_jmp != DISAS_EXIT); gen_helper_raise_exception_debug(cpu_env); } else { - switch (ctx.base.is_jmp) { + switch (ctx->base.is_jmp) { case DISAS_STOP: - gen_save_pc(ctx.base.pc_next); + gen_save_pc(ctx->base.pc_next); tcg_gen_lookup_and_goto_ptr(); break; case DISAS_NEXT: - save_cpu_state(&ctx, 0); - gen_goto_tb(&ctx, 0, ctx.base.pc_next); + save_cpu_state(ctx, 0); + gen_goto_tb(ctx, 0, ctx->base.pc_next); break; case DISAS_EXIT: tcg_gen_exit_tb(0); @@ -20358,19 +20359,19 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } } done_generating: - gen_tb_end(tb, ctx.base.num_insns); + gen_tb_end(tb, ctx->base.num_insns); - tb->size = ctx.base.pc_next - ctx.base.pc_first; - tb->icount = ctx.base.num_insns; + tb->size = ctx->base.pc_next - ctx->base.pc_first; + tb->icount = ctx->base.num_insns; #ifdef DEBUG_DISAS LOG_DISAS("\n"); if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(ctx.base.pc_first)) { + && qemu_log_in_addr_range(ctx->base.pc_first)) { qemu_log_lock(); - qemu_log("IN: %s\n", lookup_symbol(ctx.base.pc_first)); - log_target_disas(cs, ctx.base.pc_first, - ctx.base.pc_next - ctx.base.pc_first); + qemu_log("IN: %s\n", lookup_symbol(ctx->base.pc_first)); + log_target_disas(cs, ctx->base.pc_first, + ctx->base.pc_next - ctx->base.pc_first); qemu_log("\n"); qemu_log_unlock(); } From 18f440edfb974feaff8490d4861844b5a2b7a3b5 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Thu, 15 Feb 2018 20:52:07 -0500 Subject: [PATCH 0352/2380] target/mips: convert to TranslatorOps Notes: - DISAS_TOO_MANY replaces the former "break" in the translation loop. However, care must be taken not to overwrite a previous condition in is_jmp; that's why in translate_insn we first check is_jmp and return if it's != DISAS_NEXT. - Added an assert in translate_insn, before exiting due to an exception, to make sure that is_jmp is set to DISAS_NORETURN (the exception generation function always sets it.) - Added an assert for the default case in is_jmp's switch. Reviewed-by: Richard Henderson Cc: Aurelien Jarno Cc: Yongbok Kim Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/mips/translate.c | 237 ++++++++++++++++++++-------------------- 1 file changed, 118 insertions(+), 119 deletions(-) diff --git a/target/mips/translate.c b/target/mips/translate.c index 7a931ea3e7..9eff842c39 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -1432,6 +1432,7 @@ static TCGv_i64 msa_wr_d[64]; typedef struct DisasContext { DisasContextBase base; target_ulong saved_pc; + target_ulong page_start; uint32_t opcode; int insn_flags; int32_t CP0_Config1; @@ -20194,24 +20195,12 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) } } -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { + DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUMIPSState *env = cs->env_ptr; - DisasContext ctx1; - DisasContext *ctx = &ctx1; - target_ulong page_start; - int max_insns; - int insn_bytes; - int is_slot; - ctx->base.tb = tb; - ctx->base.pc_first = tb->pc; - ctx->base.pc_next = tb->pc; - ctx->base.is_jmp = DISAS_NEXT; - ctx->base.singlestep_enabled = cs->singlestep_enabled; - ctx->base.num_insns = 0; - - page_start = ctx->base.pc_first & TARGET_PAGE_MASK; + ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; ctx->saved_pc = -1; ctx->insn_flags = env->insn_flags; ctx->CP0_Config1 = env->CP0_Config1; @@ -20244,99 +20233,102 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) #endif ctx->default_tcg_memop_mask = (ctx->insn_flags & ISA_MIPS32R6) ? MO_UNALN : MO_ALIGN; - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; + + LOG_DISAS("\ntb %p idx %d hflags %04x\n", ctx->base.tb, ctx->mem_idx, + ctx->hflags); +} + +static void mips_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) +{ +} + +static void mips_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + tcg_gen_insn_start(ctx->base.pc_next, ctx->hflags & MIPS_HFLAG_BMASK, + ctx->btarget); +} + +static bool mips_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + save_cpu_state(ctx, 1); + ctx->base.is_jmp = DISAS_NORETURN; + gen_helper_raise_exception_debug(cpu_env); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx->base.pc_next += 4; + return true; +} + +static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + CPUMIPSState *env = cs->env_ptr; + DisasContext *ctx = container_of(dcbase, DisasContext, base); + int insn_bytes; + int is_slot; + + is_slot = ctx->hflags & MIPS_HFLAG_BMASK; + if (!(ctx->hflags & MIPS_HFLAG_M16)) { + ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); + insn_bytes = 4; + decode_opc(env, ctx); + } else if (ctx->insn_flags & ASE_MICROMIPS) { + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); + insn_bytes = decode_micromips_opc(env, ctx); + } else if (ctx->insn_flags & ASE_MIPS16) { + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); + insn_bytes = decode_mips16_opc(env, ctx); + } else { + generate_exception_end(ctx, EXCP_RI); + g_assert(ctx->base.is_jmp == DISAS_NORETURN); + return; } - LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx->mem_idx, ctx->hflags); - gen_tb_start(tb); - while (ctx->base.is_jmp == DISAS_NEXT) { - tcg_gen_insn_start(ctx->base.pc_next, ctx->hflags & MIPS_HFLAG_BMASK, - ctx->btarget); - ctx->base.num_insns++; - - if (unlikely(cpu_breakpoint_test(cs, ctx->base.pc_next, BP_ANY))) { - save_cpu_state(ctx, 1); - ctx->base.is_jmp = DISAS_NORETURN; - gen_helper_raise_exception_debug(cpu_env); - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - ctx->base.pc_next += 4; - goto done_generating; + if (ctx->hflags & MIPS_HFLAG_BMASK) { + if (!(ctx->hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32 | + MIPS_HFLAG_FBNSLOT))) { + /* force to generate branch as there is neither delay nor + forbidden slot */ + is_slot = 1; } - - if (ctx->base.num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); + if ((ctx->hflags & MIPS_HFLAG_M16) && + (ctx->hflags & MIPS_HFLAG_FBNSLOT)) { + /* Force to generate branch as microMIPS R6 doesn't restrict + branches in the forbidden slot. */ + is_slot = 1; } - - is_slot = ctx->hflags & MIPS_HFLAG_BMASK; - if (!(ctx->hflags & MIPS_HFLAG_M16)) { - ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); - insn_bytes = 4; - decode_opc(env, ctx); - } else if (ctx->insn_flags & ASE_MICROMIPS) { - ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); - insn_bytes = decode_micromips_opc(env, ctx); - } else if (ctx->insn_flags & ASE_MIPS16) { - ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); - insn_bytes = decode_mips16_opc(env, ctx); - } else { - generate_exception_end(ctx, EXCP_RI); - break; - } - - if (ctx->hflags & MIPS_HFLAG_BMASK) { - if (!(ctx->hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32 | - MIPS_HFLAG_FBNSLOT))) { - /* force to generate branch as there is neither delay nor - forbidden slot */ - is_slot = 1; - } - if ((ctx->hflags & MIPS_HFLAG_M16) && - (ctx->hflags & MIPS_HFLAG_FBNSLOT)) { - /* Force to generate branch as microMIPS R6 doesn't restrict - branches in the forbidden slot. */ - is_slot = 1; - } - } - if (is_slot) { - gen_branch(ctx, insn_bytes); - } - ctx->base.pc_next += insn_bytes; - - /* Execute a branch and its delay slot as a single instruction. - This is what GDB expects and is consistent with what the - hardware does (e.g. if a delay slot instruction faults, the - reported PC is the PC of the branch). */ - if (ctx->base.singlestep_enabled && - (ctx->hflags & MIPS_HFLAG_BMASK) == 0) { - break; - } - - if (ctx->base.pc_next - page_start >= TARGET_PAGE_SIZE) { - break; - } - - if (tcg_op_buf_full()) { - break; - } - - if (ctx->base.num_insns >= max_insns) { - break; - } - - if (singlestep) - break; } - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); + if (is_slot) { + gen_branch(ctx, insn_bytes); } + ctx->base.pc_next += insn_bytes; + + if (ctx->base.is_jmp != DISAS_NEXT) { + return; + } + /* Execute a branch and its delay slot as a single instruction. + This is what GDB expects and is consistent with what the + hardware does (e.g. if a delay slot instruction faults, the + reported PC is the PC of the branch). */ + if (ctx->base.singlestep_enabled && + (ctx->hflags & MIPS_HFLAG_BMASK) == 0) { + ctx->base.is_jmp = DISAS_TOO_MANY; + } + if (ctx->base.pc_next - ctx->page_start >= TARGET_PAGE_SIZE) { + ctx->base.is_jmp = DISAS_TOO_MANY; + } +} + +static void mips_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + if (ctx->base.singlestep_enabled && ctx->base.is_jmp != DISAS_NORETURN) { save_cpu_state(ctx, ctx->base.is_jmp != DISAS_EXIT); gen_helper_raise_exception_debug(cpu_env); @@ -20347,6 +20339,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) tcg_gen_lookup_and_goto_ptr(); break; case DISAS_NEXT: + case DISAS_TOO_MANY: save_cpu_state(ctx, 0); gen_goto_tb(ctx, 0, ctx->base.pc_next); break; @@ -20354,28 +20347,34 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) tcg_gen_exit_tb(0); break; case DISAS_NORETURN: - default: break; + default: + g_assert_not_reached(); } } -done_generating: - gen_tb_end(tb, ctx->base.num_insns); +} - tb->size = ctx->base.pc_next - ctx->base.pc_first; - tb->icount = ctx->base.num_insns; +static void mips_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); +} -#ifdef DEBUG_DISAS - LOG_DISAS("\n"); - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(ctx->base.pc_first)) { - qemu_log_lock(); - qemu_log("IN: %s\n", lookup_symbol(ctx->base.pc_first)); - log_target_disas(cs, ctx->base.pc_first, - ctx->base.pc_next - ctx->base.pc_first); - qemu_log("\n"); - qemu_log_unlock(); - } -#endif +static const TranslatorOps mips_tr_ops = { + .init_disas_context = mips_tr_init_disas_context, + .tb_start = mips_tr_tb_start, + .insn_start = mips_tr_insn_start, + .breakpoint_check = mips_tr_breakpoint_check, + .translate_insn = mips_tr_translate_insn, + .tb_stop = mips_tr_tb_stop, + .disas_log = mips_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +{ + DisasContext ctx; + + translator_loop(&mips_tr_ops, &ctx.base, cs, tb); } static void fpu_dump_state(CPUMIPSState *env, FILE *f, fprintf_function fpu_fprintf, From 21a8eced9bc61633dc37c8f15739c3e04bc3d70f Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Fri, 16 Feb 2018 17:43:21 -0500 Subject: [PATCH 0353/2380] target/s390x: convert to DisasJumpType The only non-trivial modification is the use of DISAS_TOO_MANY in the same way is used by the generic translation loop. Acked-by: Cornelia Huck Reviewed-by: David Hildenbrand Reviewed-by: Richard Henderson Cc: David Hildenbrand Cc: Cornelia Huck Cc: Alexander Graf Cc: qemu-s390x@nongnu.org Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/s390x/translate.c | 1267 +++++++++++++++++++------------------- 1 file changed, 632 insertions(+), 635 deletions(-) diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 44449f11ab..d08c109fe1 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -42,6 +42,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/translator.h" #include "exec/log.h" @@ -73,9 +74,6 @@ typedef struct { } u; } DisasCompare; -/* is_jmp field values */ -#define DISAS_EXCP DISAS_TARGET_0 - #ifdef DEBUG_INLINE_BRANCHES static uint64_t inline_branch_hit[CC_OP_MAX]; static uint64_t inline_branch_miss[CC_OP_MAX]; @@ -1091,26 +1089,24 @@ typedef struct { #define SPEC_r2_f128 16 /* Return values from translate_one, indicating the state of the TB. */ -typedef enum { - /* Continue the TB. */ - NO_EXIT, - /* We have emitted one or more goto_tb. No fixup required. */ - EXIT_GOTO_TB, - /* We are not using a goto_tb (for whatever reason), but have updated - the PC (for whatever reason), so there's no need to do it again on - exiting the TB. */ - EXIT_PC_UPDATED, - /* We have updated the PC and CC values. */ - EXIT_PC_CC_UPDATED, - /* We are exiting the TB, but have neither emitted a goto_tb, nor - updated the PC for the next instruction to be executed. */ - EXIT_PC_STALE, - /* We are exiting the TB to the main loop. */ - EXIT_PC_STALE_NOCHAIN, - /* We are ending the TB with a noreturn function call, e.g. longjmp. - No following code will be executed. */ - EXIT_NORETURN, -} ExitStatus; + +/* We are not using a goto_tb (for whatever reason), but have updated + the PC (for whatever reason), so there's no need to do it again on + exiting the TB. */ +#define DISAS_PC_UPDATED DISAS_TARGET_0 + +/* We have emitted one or more goto_tb. No fixup required. */ +#define DISAS_GOTO_TB DISAS_TARGET_1 + +/* We have updated the PC and CC values. */ +#define DISAS_PC_CC_UPDATED DISAS_TARGET_2 + +/* We are exiting the TB, but have neither emitted a goto_tb, nor + updated the PC for the next instruction to be executed. */ +#define DISAS_PC_STALE DISAS_TARGET_3 + +/* We are exiting the TB to the main loop. */ +#define DISAS_PC_STALE_NOCHAIN DISAS_TARGET_4 struct DisasInsn { unsigned opc:16; @@ -1125,7 +1121,7 @@ struct DisasInsn { void (*help_prep)(DisasContext *, DisasFields *, DisasOps *); void (*help_wout)(DisasContext *, DisasFields *, DisasOps *); void (*help_cout)(DisasContext *, DisasOps *); - ExitStatus (*help_op)(DisasContext *, DisasOps *); + DisasJumpType (*help_op)(DisasContext *, DisasOps *); uint64_t data; }; @@ -1147,11 +1143,11 @@ static void help_l2_shift(DisasContext *s, DisasFields *f, } } -static ExitStatus help_goto_direct(DisasContext *s, uint64_t dest) +static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) { if (dest == s->next_pc) { per_branch(s, true); - return NO_EXIT; + return DISAS_NEXT; } if (use_goto_tb(s, dest)) { update_cc_op(s); @@ -1159,31 +1155,31 @@ static ExitStatus help_goto_direct(DisasContext *s, uint64_t dest) tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); tcg_gen_exit_tb((uintptr_t)s->tb); - return EXIT_GOTO_TB; + return DISAS_GOTO_TB; } else { tcg_gen_movi_i64(psw_addr, dest); per_branch(s, false); - return EXIT_PC_UPDATED; + return DISAS_PC_UPDATED; } } -static ExitStatus help_branch(DisasContext *s, DisasCompare *c, - bool is_imm, int imm, TCGv_i64 cdest) +static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, + bool is_imm, int imm, TCGv_i64 cdest) { - ExitStatus ret; + DisasJumpType ret; uint64_t dest = s->pc + 2 * imm; TCGLabel *lab; /* Take care of the special cases first. */ if (c->cond == TCG_COND_NEVER) { - ret = NO_EXIT; + ret = DISAS_NEXT; goto egress; } if (is_imm) { if (dest == s->next_pc) { /* Branch to next. */ per_branch(s, true); - ret = NO_EXIT; + ret = DISAS_NEXT; goto egress; } if (c->cond == TCG_COND_ALWAYS) { @@ -1193,13 +1189,13 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, } else { if (!cdest) { /* E.g. bcr %r0 -> no branch. */ - ret = NO_EXIT; + ret = DISAS_NEXT; goto egress; } if (c->cond == TCG_COND_ALWAYS) { tcg_gen_mov_i64(psw_addr, cdest); per_branch(s, false); - ret = EXIT_PC_UPDATED; + ret = DISAS_PC_UPDATED; goto egress; } } @@ -1228,7 +1224,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, tcg_gen_movi_i64(psw_addr, dest); tcg_gen_exit_tb((uintptr_t)s->tb + 1); - ret = EXIT_GOTO_TB; + ret = DISAS_GOTO_TB; } else { /* Fallthru can use goto_tb, but taken branch cannot. */ /* Store taken branch destination before the brcond. This @@ -1256,7 +1252,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, tcg_gen_movi_i64(psw_addr, dest); } per_breaking_event(s); - ret = EXIT_PC_UPDATED; + ret = DISAS_PC_UPDATED; } } else { /* Fallthru cannot use goto_tb. This by itself is vanishingly rare. @@ -1290,7 +1286,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, } tcg_temp_free_i64(next); - ret = EXIT_PC_UPDATED; + ret = DISAS_PC_UPDATED; } egress: @@ -1302,7 +1298,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, /* The operations. These perform the bulk of the work for any insn, usually after the operands have been loaded and output initialized. */ -static ExitStatus op_abs(DisasContext *s, DisasOps *o) +static DisasJumpType op_abs(DisasContext *s, DisasOps *o) { TCGv_i64 z, n; z = tcg_const_i64(0); @@ -1311,35 +1307,35 @@ static ExitStatus op_abs(DisasContext *s, DisasOps *o) tcg_gen_movcond_i64(TCG_COND_LT, o->out, o->in2, z, n, o->in2); tcg_temp_free_i64(n); tcg_temp_free_i64(z); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_absf32(DisasContext *s, DisasOps *o) +static DisasJumpType op_absf32(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in2, 0x7fffffffull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_absf64(DisasContext *s, DisasOps *o) +static DisasJumpType op_absf64(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in2, 0x7fffffffffffffffull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_absf128(DisasContext *s, DisasOps *o) +static DisasJumpType op_absf128(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in1, 0x7fffffffffffffffull); tcg_gen_mov_i64(o->out2, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_add(DisasContext *s, DisasOps *o) +static DisasJumpType op_add(DisasContext *s, DisasOps *o) { tcg_gen_add_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_addc(DisasContext *s, DisasOps *o) +static DisasJumpType op_addc(DisasContext *s, DisasOps *o) { DisasCompare cmp; TCGv_i64 carry; @@ -1363,10 +1359,10 @@ static ExitStatus op_addc(DisasContext *s, DisasOps *o) tcg_gen_add_i64(o->out, o->out, carry); tcg_temp_free_i64(carry); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_asi(DisasContext *s, DisasOps *o) +static DisasJumpType op_asi(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); @@ -1384,35 +1380,35 @@ static ExitStatus op_asi(DisasContext *s, DisasOps *o) if (!s390_has_feat(S390_FEAT_STFLE_45)) { tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), s->insn->data); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_aeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_aeb(DisasContext *s, DisasOps *o) { gen_helper_aeb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_adb(DisasContext *s, DisasOps *o) +static DisasJumpType op_adb(DisasContext *s, DisasOps *o) { gen_helper_adb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_axb(DisasContext *s, DisasOps *o) +static DisasJumpType op_axb(DisasContext *s, DisasOps *o) { gen_helper_axb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_and(DisasContext *s, DisasOps *o) +static DisasJumpType op_and(DisasContext *s, DisasOps *o) { tcg_gen_and_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_andi(DisasContext *s, DisasOps *o) +static DisasJumpType op_andi(DisasContext *s, DisasOps *o) { int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; @@ -1426,10 +1422,10 @@ static ExitStatus op_andi(DisasContext *s, DisasOps *o) /* Produce the CC from only the bits manipulated. */ tcg_gen_andi_i64(cc_dst, o->out, mask); set_cc_nz_u64(s, cc_dst); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ni(DisasContext *s, DisasOps *o) +static DisasJumpType op_ni(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); @@ -1447,28 +1443,28 @@ static ExitStatus op_ni(DisasContext *s, DisasOps *o) if (!s390_has_feat(S390_FEAT_INTERLOCKED_ACCESS_2)) { tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), s->insn->data); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_bas(DisasContext *s, DisasOps *o) +static DisasJumpType op_bas(DisasContext *s, DisasOps *o) { tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->next_pc)); if (o->in2) { tcg_gen_mov_i64(psw_addr, o->in2); per_branch(s, false); - return EXIT_PC_UPDATED; + return DISAS_PC_UPDATED; } else { - return NO_EXIT; + return DISAS_NEXT; } } -static ExitStatus op_basi(DisasContext *s, DisasOps *o) +static DisasJumpType op_basi(DisasContext *s, DisasOps *o) { tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->next_pc)); return help_goto_direct(s, s->pc + 2 * get_field(s->fields, i2)); } -static ExitStatus op_bc(DisasContext *s, DisasOps *o) +static DisasJumpType op_bc(DisasContext *s, DisasOps *o) { int m1 = get_field(s->fields, m1); bool is_imm = have_field(s->fields, i2); @@ -1487,14 +1483,14 @@ static ExitStatus op_bc(DisasContext *s, DisasOps *o) /* FIXME: perform checkpoint-synchronisation */ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); } - return NO_EXIT; + return DISAS_NEXT; } disas_jcc(s, &c, m1); return help_branch(s, &c, is_imm, imm, o->in2); } -static ExitStatus op_bct32(DisasContext *s, DisasOps *o) +static DisasJumpType op_bct32(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); bool is_imm = have_field(s->fields, i2); @@ -1518,7 +1514,7 @@ static ExitStatus op_bct32(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->in2); } -static ExitStatus op_bcth(DisasContext *s, DisasOps *o) +static DisasJumpType op_bcth(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int imm = get_field(s->fields, i2); @@ -1542,7 +1538,7 @@ static ExitStatus op_bcth(DisasContext *s, DisasOps *o) return help_branch(s, &c, 1, imm, o->in2); } -static ExitStatus op_bct64(DisasContext *s, DisasOps *o) +static DisasJumpType op_bct64(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); bool is_imm = have_field(s->fields, i2); @@ -1561,7 +1557,7 @@ static ExitStatus op_bct64(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->in2); } -static ExitStatus op_bx32(DisasContext *s, DisasOps *o) +static DisasJumpType op_bx32(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -1587,7 +1583,7 @@ static ExitStatus op_bx32(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->in2); } -static ExitStatus op_bx64(DisasContext *s, DisasOps *o) +static DisasJumpType op_bx64(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -1613,7 +1609,7 @@ static ExitStatus op_bx64(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->in2); } -static ExitStatus op_cj(DisasContext *s, DisasOps *o) +static DisasJumpType op_cj(DisasContext *s, DisasOps *o) { int imm, m3 = get_field(s->fields, m3); bool is_imm; @@ -1639,186 +1635,186 @@ static ExitStatus op_cj(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->out); } -static ExitStatus op_ceb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ceb(DisasContext *s, DisasOps *o) { gen_helper_ceb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cdb(DisasContext *s, DisasOps *o) { gen_helper_cdb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cxb(DisasContext *s, DisasOps *o) { gen_helper_cxb(cc_op, cpu_env, o->out, o->out2, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cfeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cfeb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cfeb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f32(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cfdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cfdb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cfdb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f64(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cfxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cfxb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cfxb(o->out, cpu_env, o->in1, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f128(s, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cgeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cgeb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cgeb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f32(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cgdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cgdb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cgdb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f64(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cgxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cgxb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cgxb(o->out, cpu_env, o->in1, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f128(s, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clfeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clfeb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clfeb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f32(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clfdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clfdb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clfdb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f64(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clfxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clfxb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clfxb(o->out, cpu_env, o->in1, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f128(s, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clgeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clgeb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clgeb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f32(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clgdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clgdb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clgdb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f64(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clgxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clgxb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clgxb(o->out, cpu_env, o->in1, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f128(s, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cegb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cegb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cegb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cdgb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cdgb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cdgb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cxgb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cxgb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cxgb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_celgb(DisasContext *s, DisasOps *o) +static DisasJumpType op_celgb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_celgb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cdlgb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cdlgb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cdlgb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cxlgb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cxlgb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cxlgb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cksm(DisasContext *s, DisasOps *o) +static DisasJumpType op_cksm(DisasContext *s, DisasOps *o) { int r2 = get_field(s->fields, r2); TCGv_i64 len = tcg_temp_new_i64(); @@ -1831,10 +1827,10 @@ static ExitStatus op_cksm(DisasContext *s, DisasOps *o) tcg_gen_sub_i64(regs[r2 + 1], regs[r2 + 1], len); tcg_temp_free_i64(len); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clc(DisasContext *s, DisasOps *o) +static DisasJumpType op_clc(DisasContext *s, DisasOps *o) { int l = get_field(s->fields, l1); TCGv_i32 vl; @@ -1861,13 +1857,13 @@ static ExitStatus op_clc(DisasContext *s, DisasOps *o) gen_helper_clc(cc_op, cpu_env, vl, o->addr1, o->in2); tcg_temp_free_i32(vl); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } gen_op_update2_cc_i64(s, CC_OP_LTUGTU_64, cc_src, cc_dst); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clcl(DisasContext *s, DisasOps *o) +static DisasJumpType op_clcl(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r2 = get_field(s->fields, r2); @@ -1876,7 +1872,7 @@ static ExitStatus op_clcl(DisasContext *s, DisasOps *o) /* r1 and r2 must be even. */ if (r1 & 1 || r2 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -1885,10 +1881,10 @@ static ExitStatus op_clcl(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clcle(DisasContext *s, DisasOps *o) +static DisasJumpType op_clcle(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -1897,7 +1893,7 @@ static ExitStatus op_clcle(DisasContext *s, DisasOps *o) /* r1 and r3 must be even. */ if (r1 & 1 || r3 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -1906,10 +1902,10 @@ static ExitStatus op_clcle(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clclu(DisasContext *s, DisasOps *o) +static DisasJumpType op_clclu(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -1918,7 +1914,7 @@ static ExitStatus op_clclu(DisasContext *s, DisasOps *o) /* r1 and r3 must be even. */ if (r1 & 1 || r3 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -1927,10 +1923,10 @@ static ExitStatus op_clclu(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clm(DisasContext *s, DisasOps *o) +static DisasJumpType op_clm(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); TCGv_i32 t1 = tcg_temp_new_i32(); @@ -1939,28 +1935,28 @@ static ExitStatus op_clm(DisasContext *s, DisasOps *o) set_cc_static(s); tcg_temp_free_i32(t1); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clst(DisasContext *s, DisasOps *o) +static DisasJumpType op_clst(DisasContext *s, DisasOps *o) { gen_helper_clst(o->in1, cpu_env, regs[0], o->in1, o->in2); set_cc_static(s); return_low128(o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cps(DisasContext *s, DisasOps *o) +static DisasJumpType op_cps(DisasContext *s, DisasOps *o) { TCGv_i64 t = tcg_temp_new_i64(); tcg_gen_andi_i64(t, o->in1, 0x8000000000000000ull); tcg_gen_andi_i64(o->out, o->in2, 0x7fffffffffffffffull); tcg_gen_or_i64(o->out, o->out, t); tcg_temp_free_i64(t); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cs(DisasContext *s, DisasOps *o) +static DisasJumpType op_cs(DisasContext *s, DisasOps *o) { int d2 = get_field(s->fields, d2); int b2 = get_field(s->fields, b2); @@ -1982,10 +1978,10 @@ static ExitStatus op_cs(DisasContext *s, DisasOps *o) tcg_temp_free_i64(cc); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cdsg(DisasContext *s, DisasOps *o) +static DisasJumpType op_cdsg(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -2008,10 +2004,10 @@ static ExitStatus op_cdsg(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t_r3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_csst(DisasContext *s, DisasOps *o) +static DisasJumpType op_csst(DisasContext *s, DisasOps *o) { int r3 = get_field(s->fields, r3); TCGv_i32 t_r3 = tcg_const_i32(r3); @@ -2024,11 +2020,11 @@ static ExitStatus op_csst(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t_r3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_csp(DisasContext *s, DisasOps *o) +static DisasJumpType op_csp(DisasContext *s, DisasOps *o) { TCGMemOp mop = s->insn->data; TCGv_i64 addr, old, cc; @@ -2069,11 +2065,11 @@ static ExitStatus op_csp(DisasContext *s, DisasOps *o) gen_helper_purge(cpu_env); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_cvd(DisasContext *s, DisasOps *o) +static DisasJumpType op_cvd(DisasContext *s, DisasOps *o) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i32 t2 = tcg_temp_new_i32(); @@ -2082,10 +2078,10 @@ static ExitStatus op_cvd(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t2); tcg_gen_qemu_st64(t1, o->in2, get_mem_index(s)); tcg_temp_free_i64(t1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ct(DisasContext *s, DisasOps *o) +static DisasJumpType op_ct(DisasContext *s, DisasOps *o) { int m3 = get_field(s->fields, m3); TCGLabel *lab = gen_new_label(); @@ -2101,10 +2097,10 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o) gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cuXX(DisasContext *s, DisasOps *o) +static DisasJumpType op_cuXX(DisasContext *s, DisasOps *o) { int m3 = get_field(s->fields, m3); int r1 = get_field(s->fields, r1); @@ -2114,7 +2110,7 @@ static ExitStatus op_cuXX(DisasContext *s, DisasOps *o) /* R1 and R2 must both be even. */ if ((r1 | r2) & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } if (!s390_has_feat(S390_FEAT_ETF3_ENH)) { m3 = 0; @@ -2151,11 +2147,11 @@ static ExitStatus op_cuXX(DisasContext *s, DisasOps *o) tcg_temp_free_i32(tr2); tcg_temp_free_i32(chk); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_diag(DisasContext *s, DisasOps *o) +static DisasJumpType op_diag(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -2167,78 +2163,78 @@ static ExitStatus op_diag(DisasContext *s, DisasOps *o) tcg_temp_free_i32(func_code); tcg_temp_free_i32(r3); tcg_temp_free_i32(r1); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_divs32(DisasContext *s, DisasOps *o) +static DisasJumpType op_divs32(DisasContext *s, DisasOps *o) { gen_helper_divs32(o->out2, cpu_env, o->in1, o->in2); return_low128(o->out); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_divu32(DisasContext *s, DisasOps *o) +static DisasJumpType op_divu32(DisasContext *s, DisasOps *o) { gen_helper_divu32(o->out2, cpu_env, o->in1, o->in2); return_low128(o->out); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_divs64(DisasContext *s, DisasOps *o) +static DisasJumpType op_divs64(DisasContext *s, DisasOps *o) { gen_helper_divs64(o->out2, cpu_env, o->in1, o->in2); return_low128(o->out); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_divu64(DisasContext *s, DisasOps *o) +static DisasJumpType op_divu64(DisasContext *s, DisasOps *o) { gen_helper_divu64(o->out2, cpu_env, o->out, o->out2, o->in2); return_low128(o->out); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_deb(DisasContext *s, DisasOps *o) +static DisasJumpType op_deb(DisasContext *s, DisasOps *o) { gen_helper_deb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ddb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ddb(DisasContext *s, DisasOps *o) { gen_helper_ddb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_dxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_dxb(DisasContext *s, DisasOps *o) { gen_helper_dxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ear(DisasContext *s, DisasOps *o) +static DisasJumpType op_ear(DisasContext *s, DisasOps *o) { int r2 = get_field(s->fields, r2); tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, aregs[r2])); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ecag(DisasContext *s, DisasOps *o) +static DisasJumpType op_ecag(DisasContext *s, DisasOps *o) { /* No cache information provided. */ tcg_gen_movi_i64(o->out, -1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_efpc(DisasContext *s, DisasOps *o) +static DisasJumpType op_efpc(DisasContext *s, DisasOps *o) { tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, fpc)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_epsw(DisasContext *s, DisasOps *o) +static DisasJumpType op_epsw(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r2 = get_field(s->fields, r2); @@ -2253,10 +2249,10 @@ static ExitStatus op_epsw(DisasContext *s, DisasOps *o) } tcg_temp_free_i64(t); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ex(DisasContext *s, DisasOps *o) +static DisasJumpType op_ex(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); TCGv_i32 ilen; @@ -2265,7 +2261,7 @@ static ExitStatus op_ex(DisasContext *s, DisasOps *o) /* Nested EXECUTE is not allowed. */ if (unlikely(s->ex_value)) { gen_program_exception(s, PGM_EXECUTE); - return EXIT_NORETURN; + return DISAS_NORETURN; } update_psw_addr(s); @@ -2285,35 +2281,35 @@ static ExitStatus op_ex(DisasContext *s, DisasOps *o) tcg_temp_free_i64(v1); } - return EXIT_PC_CC_UPDATED; + return DISAS_PC_CC_UPDATED; } -static ExitStatus op_fieb(DisasContext *s, DisasOps *o) +static DisasJumpType op_fieb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_fieb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_fidb(DisasContext *s, DisasOps *o) +static DisasJumpType op_fidb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_fidb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_fixb(DisasContext *s, DisasOps *o) +static DisasJumpType op_fixb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_fixb(o->out, cpu_env, o->in1, o->in2, m3); return_low128(o->out2); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_flogr(DisasContext *s, DisasOps *o) +static DisasJumpType op_flogr(DisasContext *s, DisasOps *o) { /* We'll use the original input for cc computation, since we get to compare that against 0, which ought to be better than comparing @@ -2330,10 +2326,10 @@ static ExitStatus op_flogr(DisasContext *s, DisasOps *o) tcg_gen_movi_i64(o->out2, 0x8000000000000000ull); tcg_gen_shr_i64(o->out2, o->out2, o->out); tcg_gen_andc_i64(o->out2, cc_dst, o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_icm(DisasContext *s, DisasOps *o) +static DisasJumpType op_icm(DisasContext *s, DisasOps *o) { int m3 = get_field(s->fields, m3); int pos, len, base = s->insn->data; @@ -2390,18 +2386,18 @@ static ExitStatus op_icm(DisasContext *s, DisasOps *o) tcg_gen_movi_i64(tmp, ccm); gen_op_update2_cc_i64(s, CC_OP_ICM, tmp, o->out); tcg_temp_free_i64(tmp); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_insi(DisasContext *s, DisasOps *o) +static DisasJumpType op_insi(DisasContext *s, DisasOps *o) { int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; tcg_gen_deposit_i64(o->out, o->in1, o->in2, shift, size); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ipm(DisasContext *s, DisasOps *o) +static DisasJumpType op_ipm(DisasContext *s, DisasOps *o) { TCGv_i64 t1; @@ -2417,11 +2413,11 @@ static ExitStatus op_ipm(DisasContext *s, DisasOps *o) tcg_gen_shli_i64(t1, t1, 28); tcg_gen_or_i64(o->out, o->out, t1); tcg_temp_free_i64(t1); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_idte(DisasContext *s, DisasOps *o) +static DisasJumpType op_idte(DisasContext *s, DisasOps *o) { TCGv_i32 m4; @@ -2433,10 +2429,10 @@ static ExitStatus op_idte(DisasContext *s, DisasOps *o) } gen_helper_idte(cpu_env, o->in1, o->in2, m4); tcg_temp_free_i32(m4); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ipte(DisasContext *s, DisasOps *o) +static DisasJumpType op_ipte(DisasContext *s, DisasOps *o) { TCGv_i32 m4; @@ -2448,18 +2444,18 @@ static ExitStatus op_ipte(DisasContext *s, DisasOps *o) } gen_helper_ipte(cpu_env, o->in1, o->in2, m4); tcg_temp_free_i32(m4); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_iske(DisasContext *s, DisasOps *o) +static DisasJumpType op_iske(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_iske(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_msa(DisasContext *s, DisasOps *o) +static DisasJumpType op_msa(DisasContext *s, DisasOps *o) { int r1 = have_field(s->fields, r1) ? get_field(s->fields, r1) : 0; int r2 = have_field(s->fields, r2) ? get_field(s->fields, r2) : 0; @@ -2470,7 +2466,7 @@ static ExitStatus op_msa(DisasContext *s, DisasOps *o) case S390_FEAT_TYPE_KMCTR: if (r3 & 1 || !r3) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } /* FALL THROUGH */ case S390_FEAT_TYPE_PPNO: @@ -2480,7 +2476,7 @@ static ExitStatus op_msa(DisasContext *s, DisasOps *o) case S390_FEAT_TYPE_KM: if (r1 & 1 || !r1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } /* FALL THROUGH */ case S390_FEAT_TYPE_KMAC: @@ -2488,7 +2484,7 @@ static ExitStatus op_msa(DisasContext *s, DisasOps *o) case S390_FEAT_TYPE_KLMD: if (r2 & 1 || !r2) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } /* FALL THROUGH */ case S390_FEAT_TYPE_PCKMO: @@ -2508,31 +2504,31 @@ static ExitStatus op_msa(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t_r2); tcg_temp_free_i32(t_r3); tcg_temp_free_i32(type); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_keb(DisasContext *s, DisasOps *o) +static DisasJumpType op_keb(DisasContext *s, DisasOps *o) { gen_helper_keb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_kdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_kdb(DisasContext *s, DisasOps *o) { gen_helper_kdb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_kxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_kxb(DisasContext *s, DisasOps *o) { gen_helper_kxb(cc_op, cpu_env, o->out, o->out2, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_laa(DisasContext *s, DisasOps *o) +static DisasJumpType op_laa(DisasContext *s, DisasOps *o) { /* The real output is indeed the original value in memory; recompute the addition for the computation of CC. */ @@ -2540,10 +2536,10 @@ static ExitStatus op_laa(DisasContext *s, DisasOps *o) s->insn->data | MO_ALIGN); /* However, we need to recompute the addition for setting CC. */ tcg_gen_add_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lan(DisasContext *s, DisasOps *o) +static DisasJumpType op_lan(DisasContext *s, DisasOps *o) { /* The real output is indeed the original value in memory; recompute the addition for the computation of CC. */ @@ -2551,10 +2547,10 @@ static ExitStatus op_lan(DisasContext *s, DisasOps *o) s->insn->data | MO_ALIGN); /* However, we need to recompute the operation for setting CC. */ tcg_gen_and_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lao(DisasContext *s, DisasOps *o) +static DisasJumpType op_lao(DisasContext *s, DisasOps *o) { /* The real output is indeed the original value in memory; recompute the addition for the computation of CC. */ @@ -2562,10 +2558,10 @@ static ExitStatus op_lao(DisasContext *s, DisasOps *o) s->insn->data | MO_ALIGN); /* However, we need to recompute the operation for setting CC. */ tcg_gen_or_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lax(DisasContext *s, DisasOps *o) +static DisasJumpType op_lax(DisasContext *s, DisasOps *o) { /* The real output is indeed the original value in memory; recompute the addition for the computation of CC. */ @@ -2573,96 +2569,96 @@ static ExitStatus op_lax(DisasContext *s, DisasOps *o) s->insn->data | MO_ALIGN); /* However, we need to recompute the operation for setting CC. */ tcg_gen_xor_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ldeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ldeb(DisasContext *s, DisasOps *o) { gen_helper_ldeb(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ledb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ledb(DisasContext *s, DisasOps *o) { gen_helper_ledb(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ldxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ldxb(DisasContext *s, DisasOps *o) { gen_helper_ldxb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lexb(DisasContext *s, DisasOps *o) +static DisasJumpType op_lexb(DisasContext *s, DisasOps *o) { gen_helper_lexb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lxdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_lxdb(DisasContext *s, DisasOps *o) { gen_helper_lxdb(o->out, cpu_env, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lxeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_lxeb(DisasContext *s, DisasOps *o) { gen_helper_lxeb(o->out, cpu_env, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_llgt(DisasContext *s, DisasOps *o) +static DisasJumpType op_llgt(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in2, 0x7fffffff); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld8s(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld8s(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld8s(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld8u(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld8u(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld8u(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld16s(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld16s(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld16s(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld16u(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld16u(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld16u(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld32s(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld32s(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld32s(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld32u(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld32u(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld32u(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld64(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld64(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld64(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lat(DisasContext *s, DisasOps *o) +static DisasJumpType op_lat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); store_reg32_i64(get_field(s->fields, r1), o->in2); @@ -2670,10 +2666,10 @@ static ExitStatus op_lat(DisasContext *s, DisasOps *o) tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab); gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lgat(DisasContext *s, DisasOps *o) +static DisasJumpType op_lgat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); tcg_gen_qemu_ld64(o->out, o->in2, get_mem_index(s)); @@ -2681,10 +2677,10 @@ static ExitStatus op_lgat(DisasContext *s, DisasOps *o) tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lfhat(DisasContext *s, DisasOps *o) +static DisasJumpType op_lfhat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); store_reg32h_i64(get_field(s->fields, r1), o->in2); @@ -2692,10 +2688,10 @@ static ExitStatus op_lfhat(DisasContext *s, DisasOps *o) tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab); gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_llgfat(DisasContext *s, DisasOps *o) +static DisasJumpType op_llgfat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); tcg_gen_qemu_ld32u(o->out, o->in2, get_mem_index(s)); @@ -2703,10 +2699,10 @@ static ExitStatus op_llgfat(DisasContext *s, DisasOps *o) tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_llgtat(DisasContext *s, DisasOps *o) +static DisasJumpType op_llgtat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); tcg_gen_andi_i64(o->out, o->in2, 0x7fffffff); @@ -2714,10 +2710,10 @@ static ExitStatus op_llgtat(DisasContext *s, DisasOps *o) tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_loc(DisasContext *s, DisasOps *o) +static DisasJumpType op_loc(DisasContext *s, DisasOps *o) { DisasCompare c; @@ -2744,11 +2740,11 @@ static ExitStatus op_loc(DisasContext *s, DisasOps *o) tcg_temp_free_i64(z); } - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_lctl(DisasContext *s, DisasOps *o) +static DisasJumpType op_lctl(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -2757,10 +2753,10 @@ static ExitStatus op_lctl(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return EXIT_PC_STALE_NOCHAIN; + return DISAS_PC_STALE_NOCHAIN; } -static ExitStatus op_lctlg(DisasContext *s, DisasOps *o) +static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -2769,26 +2765,26 @@ static ExitStatus op_lctlg(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return EXIT_PC_STALE_NOCHAIN; + return DISAS_PC_STALE_NOCHAIN; } -static ExitStatus op_lra(DisasContext *s, DisasOps *o) +static DisasJumpType op_lra(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_lra(o->out, cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lpp(DisasContext *s, DisasOps *o) +static DisasJumpType op_lpp(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_st_i64(o->in2, cpu_env, offsetof(CPUS390XState, pp)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lpsw(DisasContext *s, DisasOps *o) +static DisasJumpType op_lpsw(DisasContext *s, DisasOps *o) { TCGv_i64 t1, t2; @@ -2805,10 +2801,10 @@ static ExitStatus op_lpsw(DisasContext *s, DisasOps *o) gen_helper_load_psw(cpu_env, t1, t2); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); - return EXIT_NORETURN; + return DISAS_NORETURN; } -static ExitStatus op_lpswe(DisasContext *s, DisasOps *o) +static DisasJumpType op_lpswe(DisasContext *s, DisasOps *o) { TCGv_i64 t1, t2; @@ -2823,21 +2819,21 @@ static ExitStatus op_lpswe(DisasContext *s, DisasOps *o) gen_helper_load_psw(cpu_env, t1, t2); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); - return EXIT_NORETURN; + return DISAS_NORETURN; } #endif -static ExitStatus op_lam(DisasContext *s, DisasOps *o) +static DisasJumpType op_lam(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); gen_helper_lam(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lm32(DisasContext *s, DisasOps *o) +static DisasJumpType op_lm32(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -2849,7 +2845,7 @@ static ExitStatus op_lm32(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); store_reg32_i64(r1, t1); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } /* First load the values of the first and last registers to trigger @@ -2865,7 +2861,7 @@ static ExitStatus op_lm32(DisasContext *s, DisasOps *o) if (((r1 + 1) & 15) == r3) { tcg_temp_free(t2); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } /* Then load the remaining registers. Page fault can't occur. */ @@ -2880,10 +2876,10 @@ static ExitStatus op_lm32(DisasContext *s, DisasOps *o) tcg_temp_free(t2); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lmh(DisasContext *s, DisasOps *o) +static DisasJumpType op_lmh(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -2895,7 +2891,7 @@ static ExitStatus op_lmh(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); store_reg32h_i64(r1, t1); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } /* First load the values of the first and last registers to trigger @@ -2911,7 +2907,7 @@ static ExitStatus op_lmh(DisasContext *s, DisasOps *o) if (((r1 + 1) & 15) == r3) { tcg_temp_free(t2); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } /* Then load the remaining registers. Page fault can't occur. */ @@ -2926,10 +2922,10 @@ static ExitStatus op_lmh(DisasContext *s, DisasOps *o) tcg_temp_free(t2); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lm64(DisasContext *s, DisasOps *o) +static DisasJumpType op_lm64(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -2938,7 +2934,7 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o) /* Only one register to read. */ if (unlikely(r1 == r3)) { tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } /* First load the values of the first and last registers to trigger @@ -2954,7 +2950,7 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o) /* Only two registers to read. */ if (((r1 + 1) & 15) == r3) { tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } /* Then load the remaining registers. Page fault can't occur. */ @@ -2967,10 +2963,10 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o) } tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lpd(DisasContext *s, DisasOps *o) +static DisasJumpType op_lpd(DisasContext *s, DisasOps *o) { TCGv_i64 a1, a2; TCGMemOp mop = s->insn->data; @@ -2980,7 +2976,7 @@ static ExitStatus op_lpd(DisasContext *s, DisasOps *o) update_psw_addr(s); update_cc_op(s); gen_exception(EXCP_ATOMIC); - return EXIT_NORETURN; + return DISAS_NORETURN; } /* In a serial context, perform the two loads ... */ @@ -2993,10 +2989,10 @@ static ExitStatus op_lpd(DisasContext *s, DisasOps *o) /* ... and indicate that we performed them while interlocked. */ gen_op_movi_cc(s, 0); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lpq(DisasContext *s, DisasOps *o) +static DisasJumpType op_lpq(DisasContext *s, DisasOps *o) { if (tb_cflags(s->tb) & CF_PARALLEL) { gen_helper_lpq_parallel(o->out, cpu_env, o->in2); @@ -3004,41 +3000,41 @@ static ExitStatus op_lpq(DisasContext *s, DisasOps *o) gen_helper_lpq(o->out, cpu_env, o->in2); } return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_lura(DisasContext *s, DisasOps *o) +static DisasJumpType op_lura(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_lura(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lurag(DisasContext *s, DisasOps *o) +static DisasJumpType op_lurag(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_lurag(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_lzrb(DisasContext *s, DisasOps *o) +static DisasJumpType op_lzrb(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in2, -256); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mov2(DisasContext *s, DisasOps *o) +static DisasJumpType op_mov2(DisasContext *s, DisasOps *o) { o->out = o->in2; o->g_out = o->g_in2; o->in2 = NULL; o->g_in2 = false; - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mov2e(DisasContext *s, DisasOps *o) +static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o) { int b2 = get_field(s->fields, b2); TCGv ar1 = tcg_temp_new_i64(); @@ -3070,10 +3066,10 @@ static ExitStatus op_mov2e(DisasContext *s, DisasOps *o) tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[1])); tcg_temp_free_i64(ar1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_movx(DisasContext *s, DisasOps *o) +static DisasJumpType op_movx(DisasContext *s, DisasOps *o) { o->out = o->in1; o->out2 = o->in2; @@ -3082,26 +3078,26 @@ static ExitStatus op_movx(DisasContext *s, DisasOps *o) o->in1 = NULL; o->in2 = NULL; o->g_in1 = o->g_in2 = false; - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvc(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvc(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_mvc(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvcin(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcin(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_mvcin(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvcl(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcl(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r2 = get_field(s->fields, r2); @@ -3110,7 +3106,7 @@ static ExitStatus op_mvcl(DisasContext *s, DisasOps *o) /* r1 and r2 must be even. */ if (r1 & 1 || r2 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -3119,10 +3115,10 @@ static ExitStatus op_mvcl(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvcle(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcle(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -3131,7 +3127,7 @@ static ExitStatus op_mvcle(DisasContext *s, DisasOps *o) /* r1 and r3 must be even. */ if (r1 & 1 || r3 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -3140,10 +3136,10 @@ static ExitStatus op_mvcle(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvclu(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvclu(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -3152,7 +3148,7 @@ static ExitStatus op_mvclu(DisasContext *s, DisasOps *o) /* r1 and r3 must be even. */ if (r1 & 1 || r3 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -3161,151 +3157,151 @@ static ExitStatus op_mvclu(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvcos(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcos(DisasContext *s, DisasOps *o) { int r3 = get_field(s->fields, r3); gen_helper_mvcos(cc_op, cpu_env, o->addr1, o->in2, regs[r3]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_mvcp(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcp(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, l1); check_privileged(s); gen_helper_mvcp(cc_op, cpu_env, regs[r1], o->addr1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvcs(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcs(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, l1); check_privileged(s); gen_helper_mvcs(cc_op, cpu_env, regs[r1], o->addr1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_mvn(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvn(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_mvn(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvo(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvo(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_mvo(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvpg(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvpg(DisasContext *s, DisasOps *o) { gen_helper_mvpg(cc_op, cpu_env, regs[0], o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvst(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvst(DisasContext *s, DisasOps *o) { gen_helper_mvst(o->in1, cpu_env, regs[0], o->in1, o->in2); set_cc_static(s); return_low128(o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvz(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvz(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_mvz(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mul(DisasContext *s, DisasOps *o) +static DisasJumpType op_mul(DisasContext *s, DisasOps *o) { tcg_gen_mul_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mul128(DisasContext *s, DisasOps *o) +static DisasJumpType op_mul128(DisasContext *s, DisasOps *o) { tcg_gen_mulu2_i64(o->out2, o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_meeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_meeb(DisasContext *s, DisasOps *o) { gen_helper_meeb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mdeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_mdeb(DisasContext *s, DisasOps *o) { gen_helper_mdeb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_mdb(DisasContext *s, DisasOps *o) { gen_helper_mdb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_mxb(DisasContext *s, DisasOps *o) { gen_helper_mxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mxdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_mxdb(DisasContext *s, DisasOps *o) { gen_helper_mxdb(o->out, cpu_env, o->out, o->out2, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_maeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_maeb(DisasContext *s, DisasOps *o) { TCGv_i64 r3 = load_freg32_i64(get_field(s->fields, r3)); gen_helper_maeb(o->out, cpu_env, o->in1, o->in2, r3); tcg_temp_free_i64(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_madb(DisasContext *s, DisasOps *o) +static DisasJumpType op_madb(DisasContext *s, DisasOps *o) { int r3 = get_field(s->fields, r3); gen_helper_madb(o->out, cpu_env, o->in1, o->in2, fregs[r3]); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mseb(DisasContext *s, DisasOps *o) +static DisasJumpType op_mseb(DisasContext *s, DisasOps *o) { TCGv_i64 r3 = load_freg32_i64(get_field(s->fields, r3)); gen_helper_mseb(o->out, cpu_env, o->in1, o->in2, r3); tcg_temp_free_i64(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_msdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_msdb(DisasContext *s, DisasOps *o) { int r3 = get_field(s->fields, r3); gen_helper_msdb(o->out, cpu_env, o->in1, o->in2, fregs[r3]); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_nabs(DisasContext *s, DisasOps *o) +static DisasJumpType op_nabs(DisasContext *s, DisasOps *o) { TCGv_i64 z, n; z = tcg_const_i64(0); @@ -3314,78 +3310,78 @@ static ExitStatus op_nabs(DisasContext *s, DisasOps *o) tcg_gen_movcond_i64(TCG_COND_GE, o->out, o->in2, z, n, o->in2); tcg_temp_free_i64(n); tcg_temp_free_i64(z); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_nabsf32(DisasContext *s, DisasOps *o) +static DisasJumpType op_nabsf32(DisasContext *s, DisasOps *o) { tcg_gen_ori_i64(o->out, o->in2, 0x80000000ull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_nabsf64(DisasContext *s, DisasOps *o) +static DisasJumpType op_nabsf64(DisasContext *s, DisasOps *o) { tcg_gen_ori_i64(o->out, o->in2, 0x8000000000000000ull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_nabsf128(DisasContext *s, DisasOps *o) +static DisasJumpType op_nabsf128(DisasContext *s, DisasOps *o) { tcg_gen_ori_i64(o->out, o->in1, 0x8000000000000000ull); tcg_gen_mov_i64(o->out2, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_nc(DisasContext *s, DisasOps *o) +static DisasJumpType op_nc(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_nc(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_neg(DisasContext *s, DisasOps *o) +static DisasJumpType op_neg(DisasContext *s, DisasOps *o) { tcg_gen_neg_i64(o->out, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_negf32(DisasContext *s, DisasOps *o) +static DisasJumpType op_negf32(DisasContext *s, DisasOps *o) { tcg_gen_xori_i64(o->out, o->in2, 0x80000000ull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_negf64(DisasContext *s, DisasOps *o) +static DisasJumpType op_negf64(DisasContext *s, DisasOps *o) { tcg_gen_xori_i64(o->out, o->in2, 0x8000000000000000ull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_negf128(DisasContext *s, DisasOps *o) +static DisasJumpType op_negf128(DisasContext *s, DisasOps *o) { tcg_gen_xori_i64(o->out, o->in1, 0x8000000000000000ull); tcg_gen_mov_i64(o->out2, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_oc(DisasContext *s, DisasOps *o) +static DisasJumpType op_oc(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_oc(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_or(DisasContext *s, DisasOps *o) +static DisasJumpType op_or(DisasContext *s, DisasOps *o) { tcg_gen_or_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ori(DisasContext *s, DisasOps *o) +static DisasJumpType op_ori(DisasContext *s, DisasOps *o) { int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; @@ -3398,10 +3394,10 @@ static ExitStatus op_ori(DisasContext *s, DisasOps *o) /* Produce the CC from only the bits manipulated. */ tcg_gen_andi_i64(cc_dst, o->out, mask); set_cc_nz_u64(s, cc_dst); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_oi(DisasContext *s, DisasOps *o) +static DisasJumpType op_oi(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); @@ -3419,18 +3415,18 @@ static ExitStatus op_oi(DisasContext *s, DisasOps *o) if (!s390_has_feat(S390_FEAT_INTERLOCKED_ACCESS_2)) { tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), s->insn->data); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pack(DisasContext *s, DisasOps *o) +static DisasJumpType op_pack(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_pack(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pka(DisasContext *s, DisasOps *o) +static DisasJumpType op_pka(DisasContext *s, DisasOps *o) { int l2 = get_field(s->fields, l2) + 1; TCGv_i32 l; @@ -3438,15 +3434,15 @@ static ExitStatus op_pka(DisasContext *s, DisasOps *o) /* The length must not exceed 32 bytes. */ if (l2 > 32) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } l = tcg_const_i32(l2); gen_helper_pka(cpu_env, o->addr1, o->in2, l); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pku(DisasContext *s, DisasOps *o) +static DisasJumpType op_pku(DisasContext *s, DisasOps *o) { int l2 = get_field(s->fields, l2) + 1; TCGv_i32 l; @@ -3454,30 +3450,30 @@ static ExitStatus op_pku(DisasContext *s, DisasOps *o) /* The length must be even and should not exceed 64 bytes. */ if ((l2 & 1) || (l2 > 64)) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } l = tcg_const_i32(l2); gen_helper_pku(cpu_env, o->addr1, o->in2, l); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_popcnt(DisasContext *s, DisasOps *o) +static DisasJumpType op_popcnt(DisasContext *s, DisasOps *o) { gen_helper_popcnt(o->out, o->in2); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_ptlb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ptlb(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_ptlb(cpu_env); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_risbg(DisasContext *s, DisasOps *o) +static DisasJumpType op_risbg(DisasContext *s, DisasOps *o) { int i3 = get_field(s->fields, i3); int i4 = get_field(s->fields, i4); @@ -3535,7 +3531,7 @@ static ExitStatus op_risbg(DisasContext *s, DisasOps *o) /* In some cases we can implement this with extract. */ if (imask == 0 && pos == 0 && len > 0 && len <= rot) { tcg_gen_extract_i64(o->out, o->in2, 64 - rot, len); - return NO_EXIT; + return DISAS_NEXT; } /* In some cases we can implement this with deposit. */ @@ -3564,10 +3560,10 @@ static ExitStatus op_risbg(DisasContext *s, DisasOps *o) tcg_gen_andi_i64(o->out, o->out, imask); tcg_gen_or_i64(o->out, o->out, o->in2); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rosbg(DisasContext *s, DisasOps *o) +static DisasJumpType op_rosbg(DisasContext *s, DisasOps *o) { int i3 = get_field(s->fields, i3); int i4 = get_field(s->fields, i4); @@ -3617,28 +3613,28 @@ static ExitStatus op_rosbg(DisasContext *s, DisasOps *o) /* Set the CC. */ tcg_gen_andi_i64(cc_dst, o->out, mask); set_cc_nz_u64(s, cc_dst); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rev16(DisasContext *s, DisasOps *o) +static DisasJumpType op_rev16(DisasContext *s, DisasOps *o) { tcg_gen_bswap16_i64(o->out, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rev32(DisasContext *s, DisasOps *o) +static DisasJumpType op_rev32(DisasContext *s, DisasOps *o) { tcg_gen_bswap32_i64(o->out, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rev64(DisasContext *s, DisasOps *o) +static DisasJumpType op_rev64(DisasContext *s, DisasOps *o) { tcg_gen_bswap64_i64(o->out, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rll32(DisasContext *s, DisasOps *o) +static DisasJumpType op_rll32(DisasContext *s, DisasOps *o) { TCGv_i32 t1 = tcg_temp_new_i32(); TCGv_i32 t2 = tcg_temp_new_i32(); @@ -3650,34 +3646,34 @@ static ExitStatus op_rll32(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); tcg_temp_free_i32(to); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rll64(DisasContext *s, DisasOps *o) +static DisasJumpType op_rll64(DisasContext *s, DisasOps *o) { tcg_gen_rotl_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_rrbe(DisasContext *s, DisasOps *o) +static DisasJumpType op_rrbe(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_rrbe(cc_op, cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sacf(DisasContext *s, DisasOps *o) +static DisasJumpType op_sacf(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sacf(cpu_env, o->in2); /* Addressing mode has changed, so end the block. */ - return EXIT_PC_STALE; + return DISAS_PC_STALE; } #endif -static ExitStatus op_sam(DisasContext *s, DisasOps *o) +static DisasJumpType op_sam(DisasContext *s, DisasOps *o) { int sam = s->insn->data; TCGv_i64 tsam; @@ -3700,7 +3696,7 @@ static ExitStatus op_sam(DisasContext *s, DisasOps *o) documents that Bad Things Happen two bytes before the end. */ if (s->pc & ~mask) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } s->next_pc &= mask; @@ -3709,64 +3705,64 @@ static ExitStatus op_sam(DisasContext *s, DisasOps *o) tcg_temp_free_i64(tsam); /* Always exit the TB, since we (may have) changed execution mode. */ - return EXIT_PC_STALE; + return DISAS_PC_STALE; } -static ExitStatus op_sar(DisasContext *s, DisasOps *o) +static DisasJumpType op_sar(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); tcg_gen_st32_i64(o->in2, cpu_env, offsetof(CPUS390XState, aregs[r1])); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_seb(DisasContext *s, DisasOps *o) +static DisasJumpType op_seb(DisasContext *s, DisasOps *o) { gen_helper_seb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_sdb(DisasContext *s, DisasOps *o) { gen_helper_sdb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_sxb(DisasContext *s, DisasOps *o) { gen_helper_sxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sqeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_sqeb(DisasContext *s, DisasOps *o) { gen_helper_sqeb(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sqdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_sqdb(DisasContext *s, DisasOps *o) { gen_helper_sqdb(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sqxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_sqxb(DisasContext *s, DisasOps *o) { gen_helper_sqxb(o->out, cpu_env, o->in1, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_servc(DisasContext *s, DisasOps *o) +static DisasJumpType op_servc(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_servc(cc_op, cpu_env, o->in2, o->in1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sigp(DisasContext *s, DisasOps *o) +static DisasJumpType op_sigp(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -3775,11 +3771,11 @@ static ExitStatus op_sigp(DisasContext *s, DisasOps *o) set_cc_static(s); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_soc(DisasContext *s, DisasOps *o) +static DisasJumpType op_soc(DisasContext *s, DisasOps *o) { DisasCompare c; TCGv_i64 a, h; @@ -3821,10 +3817,10 @@ static ExitStatus op_soc(DisasContext *s, DisasOps *o) tcg_temp_free_i64(a); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sla(DisasContext *s, DisasOps *o) +static DisasJumpType op_sla(DisasContext *s, DisasOps *o) { uint64_t sign = 1ull << s->insn->data; enum cc_op cco = s->insn->data == 31 ? CC_OP_SLA_32 : CC_OP_SLA_64; @@ -3835,40 +3831,40 @@ static ExitStatus op_sla(DisasContext *s, DisasOps *o) tcg_gen_andi_i64(o->out, o->out, ~sign); tcg_gen_andi_i64(o->in1, o->in1, sign); tcg_gen_or_i64(o->out, o->out, o->in1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sll(DisasContext *s, DisasOps *o) +static DisasJumpType op_sll(DisasContext *s, DisasOps *o) { tcg_gen_shl_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sra(DisasContext *s, DisasOps *o) +static DisasJumpType op_sra(DisasContext *s, DisasOps *o) { tcg_gen_sar_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_srl(DisasContext *s, DisasOps *o) +static DisasJumpType op_srl(DisasContext *s, DisasOps *o) { tcg_gen_shr_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sfpc(DisasContext *s, DisasOps *o) +static DisasJumpType op_sfpc(DisasContext *s, DisasOps *o) { gen_helper_sfpc(cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sfas(DisasContext *s, DisasOps *o) +static DisasJumpType op_sfas(DisasContext *s, DisasOps *o) { gen_helper_sfas(cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_srnm(DisasContext *s, DisasOps *o) +static DisasJumpType op_srnm(DisasContext *s, DisasOps *o) { int b2 = get_field(s->fields, b2); int d2 = get_field(s->fields, d2); @@ -3905,10 +3901,10 @@ static ExitStatus op_srnm(DisasContext *s, DisasOps *o) /* Then install the new FPC to set the rounding mode in fpu_status. */ gen_helper_sfpc(cpu_env, t2); tcg_temp_free_i64(t2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_spm(DisasContext *s, DisasOps *o) +static DisasJumpType op_spm(DisasContext *s, DisasOps *o) { tcg_gen_extrl_i64_i32(cc_op, o->in1); tcg_gen_extract_i32(cc_op, cc_op, 28, 2); @@ -3916,10 +3912,10 @@ static ExitStatus op_spm(DisasContext *s, DisasOps *o) tcg_gen_shri_i64(o->in1, o->in1, 24); tcg_gen_deposit_i64(psw_mask, psw_mask, o->in1, PSW_SHIFT_MASK_PM, 4); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ectg(DisasContext *s, DisasOps *o) +static DisasJumpType op_ectg(DisasContext *s, DisasOps *o) { int b1 = get_field(s->fields, b1); int d1 = get_field(s->fields, d1); @@ -3946,49 +3942,49 @@ static ExitStatus op_ectg(DisasContext *s, DisasOps *o) tcg_gen_mov_i64(regs[1], o->in2); tcg_temp_free_i64(tmp); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_spka(DisasContext *s, DisasOps *o) +static DisasJumpType op_spka(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_shri_i64(o->in2, o->in2, 4); tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, PSW_SHIFT_KEY, 4); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sske(DisasContext *s, DisasOps *o) +static DisasJumpType op_sske(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sske(cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ssm(DisasContext *s, DisasOps *o) +static DisasJumpType op_ssm(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, 56, 8); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return EXIT_PC_STALE_NOCHAIN; + return DISAS_PC_STALE_NOCHAIN; } -static ExitStatus op_stap(DisasContext *s, DisasOps *o) +static DisasJumpType op_stap(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, core_id)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stck(DisasContext *s, DisasOps *o) +static DisasJumpType op_stck(DisasContext *s, DisasOps *o) { gen_helper_stck(o->out, cpu_env); /* ??? We don't implement clock states. */ gen_op_movi_cc(s, 0); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stcke(DisasContext *s, DisasOps *o) +static DisasJumpType op_stcke(DisasContext *s, DisasOps *o) { TCGv_i64 c1 = tcg_temp_new_i64(); TCGv_i64 c2 = tcg_temp_new_i64(); @@ -4012,31 +4008,31 @@ static ExitStatus op_stcke(DisasContext *s, DisasOps *o) tcg_temp_free_i64(todpr); /* ??? We don't implement clock states. */ gen_op_movi_cc(s, 0); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sckc(DisasContext *s, DisasOps *o) +static DisasJumpType op_sckc(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sckc(cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sckpf(DisasContext *s, DisasOps *o) +static DisasJumpType op_sckpf(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sckpf(cpu_env, regs[0]); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stckc(DisasContext *s, DisasOps *o) +static DisasJumpType op_stckc(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stckc(o->out, cpu_env); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stctg(DisasContext *s, DisasOps *o) +static DisasJumpType op_stctg(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -4044,10 +4040,10 @@ static ExitStatus op_stctg(DisasContext *s, DisasOps *o) gen_helper_stctg(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stctl(DisasContext *s, DisasOps *o) +static DisasJumpType op_stctl(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -4055,186 +4051,186 @@ static ExitStatus op_stctl(DisasContext *s, DisasOps *o) gen_helper_stctl(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stidp(DisasContext *s, DisasOps *o) +static DisasJumpType op_stidp(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, cpuid)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_spt(DisasContext *s, DisasOps *o) +static DisasJumpType op_spt(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_spt(cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stfl(DisasContext *s, DisasOps *o) +static DisasJumpType op_stfl(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stfl(cpu_env); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stpt(DisasContext *s, DisasOps *o) +static DisasJumpType op_stpt(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stpt(o->out, cpu_env); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stsi(DisasContext *s, DisasOps *o) +static DisasJumpType op_stsi(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stsi(cc_op, cpu_env, o->in2, regs[0], regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_spx(DisasContext *s, DisasOps *o) +static DisasJumpType op_spx(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_spx(cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_xsch(DisasContext *s, DisasOps *o) +static DisasJumpType op_xsch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_xsch(cpu_env, regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_csch(DisasContext *s, DisasOps *o) +static DisasJumpType op_csch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_csch(cpu_env, regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_hsch(DisasContext *s, DisasOps *o) +static DisasJumpType op_hsch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_hsch(cpu_env, regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_msch(DisasContext *s, DisasOps *o) +static DisasJumpType op_msch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_msch(cpu_env, regs[1], o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rchp(DisasContext *s, DisasOps *o) +static DisasJumpType op_rchp(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_rchp(cpu_env, regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rsch(DisasContext *s, DisasOps *o) +static DisasJumpType op_rsch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_rsch(cpu_env, regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sal(DisasContext *s, DisasOps *o) +static DisasJumpType op_sal(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sal(cpu_env, regs[1]); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_schm(DisasContext *s, DisasOps *o) +static DisasJumpType op_schm(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_schm(cpu_env, regs[1], regs[2], o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_siga(DisasContext *s, DisasOps *o) +static DisasJumpType op_siga(DisasContext *s, DisasOps *o) { check_privileged(s); /* From KVM code: Not provided, set CC = 3 for subchannel not operational */ gen_op_movi_cc(s, 3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stcps(DisasContext *s, DisasOps *o) +static DisasJumpType op_stcps(DisasContext *s, DisasOps *o) { check_privileged(s); /* The instruction is suppressed if not provided. */ - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ssch(DisasContext *s, DisasOps *o) +static DisasJumpType op_ssch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_ssch(cpu_env, regs[1], o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stsch(DisasContext *s, DisasOps *o) +static DisasJumpType op_stsch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stsch(cpu_env, regs[1], o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stcrw(DisasContext *s, DisasOps *o) +static DisasJumpType op_stcrw(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stcrw(cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tpi(DisasContext *s, DisasOps *o) +static DisasJumpType op_tpi(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_tpi(cc_op, cpu_env, o->addr1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tsch(DisasContext *s, DisasOps *o) +static DisasJumpType op_tsch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_tsch(cpu_env, regs[1], o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_chsc(DisasContext *s, DisasOps *o) +static DisasJumpType op_chsc(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_chsc(cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stpx(DisasContext *s, DisasOps *o) +static DisasJumpType op_stpx(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, psa)); tcg_gen_andi_i64(o->out, o->out, 0x7fffe000); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stnosm(DisasContext *s, DisasOps *o) +static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) { uint64_t i2 = get_field(s->fields, i2); TCGv_i64 t; @@ -4257,66 +4253,66 @@ static ExitStatus op_stnosm(DisasContext *s, DisasOps *o) } /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return EXIT_PC_STALE_NOCHAIN; + return DISAS_PC_STALE_NOCHAIN; } -static ExitStatus op_stura(DisasContext *s, DisasOps *o) +static DisasJumpType op_stura(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stura(cpu_env, o->in2, o->in1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sturg(DisasContext *s, DisasOps *o) +static DisasJumpType op_sturg(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sturg(cpu_env, o->in2, o->in1); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_stfle(DisasContext *s, DisasOps *o) +static DisasJumpType op_stfle(DisasContext *s, DisasOps *o) { gen_helper_stfle(cc_op, cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_st8(DisasContext *s, DisasOps *o) +static DisasJumpType op_st8(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st8(o->in1, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_st16(DisasContext *s, DisasOps *o) +static DisasJumpType op_st16(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st16(o->in1, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_st32(DisasContext *s, DisasOps *o) +static DisasJumpType op_st32(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st32(o->in1, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_st64(DisasContext *s, DisasOps *o) +static DisasJumpType op_st64(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st64(o->in1, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stam(DisasContext *s, DisasOps *o) +static DisasJumpType op_stam(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); gen_helper_stam(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stcm(DisasContext *s, DisasOps *o) +static DisasJumpType op_stcm(DisasContext *s, DisasOps *o) { int m3 = get_field(s->fields, m3); int pos, base = s->insn->data; @@ -4362,10 +4358,10 @@ static ExitStatus op_stcm(DisasContext *s, DisasOps *o) break; } tcg_temp_free_i64(tmp); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stm(DisasContext *s, DisasOps *o) +static DisasJumpType op_stm(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -4386,10 +4382,10 @@ static ExitStatus op_stm(DisasContext *s, DisasOps *o) } tcg_temp_free_i64(tsize); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stmh(DisasContext *s, DisasOps *o) +static DisasJumpType op_stmh(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -4410,20 +4406,20 @@ static ExitStatus op_stmh(DisasContext *s, DisasOps *o) tcg_temp_free_i64(t); tcg_temp_free_i64(t4); tcg_temp_free_i64(t32); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stpq(DisasContext *s, DisasOps *o) +static DisasJumpType op_stpq(DisasContext *s, DisasOps *o) { if (tb_cflags(s->tb) & CF_PARALLEL) { gen_helper_stpq_parallel(cpu_env, o->in2, o->out2, o->out); } else { gen_helper_stpq(cpu_env, o->in2, o->out2, o->out); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_srst(DisasContext *s, DisasOps *o) +static DisasJumpType op_srst(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4433,10 +4429,10 @@ static ExitStatus op_srst(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_srstu(DisasContext *s, DisasOps *o) +static DisasJumpType op_srstu(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4446,16 +4442,16 @@ static ExitStatus op_srstu(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sub(DisasContext *s, DisasOps *o) +static DisasJumpType op_sub(DisasContext *s, DisasOps *o) { tcg_gen_sub_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_subb(DisasContext *s, DisasOps *o) +static DisasJumpType op_subb(DisasContext *s, DisasOps *o) { DisasCompare cmp; TCGv_i64 borrow; @@ -4478,10 +4474,10 @@ static ExitStatus op_subb(DisasContext *s, DisasOps *o) tcg_gen_sub_i64(o->out, o->out, borrow); tcg_temp_free_i64(borrow); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_svc(DisasContext *s, DisasOps *o) +static DisasJumpType op_svc(DisasContext *s, DisasOps *o) { TCGv_i32 t; @@ -4497,104 +4493,104 @@ static ExitStatus op_svc(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t); gen_exception(EXCP_SVC); - return EXIT_NORETURN; + return DISAS_NORETURN; } -static ExitStatus op_tam(DisasContext *s, DisasOps *o) +static DisasJumpType op_tam(DisasContext *s, DisasOps *o) { int cc = 0; cc |= (s->tb->flags & FLAG_MASK_64) ? 2 : 0; cc |= (s->tb->flags & FLAG_MASK_32) ? 1 : 0; gen_op_movi_cc(s, cc); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tceb(DisasContext *s, DisasOps *o) +static DisasJumpType op_tceb(DisasContext *s, DisasOps *o) { gen_helper_tceb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tcdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_tcdb(DisasContext *s, DisasOps *o) { gen_helper_tcdb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tcxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_tcxb(DisasContext *s, DisasOps *o) { gen_helper_tcxb(cc_op, cpu_env, o->out, o->out2, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_testblock(DisasContext *s, DisasOps *o) +static DisasJumpType op_testblock(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_testblock(cc_op, cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tprot(DisasContext *s, DisasOps *o) +static DisasJumpType op_tprot(DisasContext *s, DisasOps *o) { gen_helper_tprot(cc_op, cpu_env, o->addr1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_tp(DisasContext *s, DisasOps *o) +static DisasJumpType op_tp(DisasContext *s, DisasOps *o) { TCGv_i32 l1 = tcg_const_i32(get_field(s->fields, l1) + 1); gen_helper_tp(cc_op, cpu_env, o->addr1, l1); tcg_temp_free_i32(l1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tr(DisasContext *s, DisasOps *o) +static DisasJumpType op_tr(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_tr(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tre(DisasContext *s, DisasOps *o) +static DisasJumpType op_tre(DisasContext *s, DisasOps *o) { gen_helper_tre(o->out, cpu_env, o->out, o->out2, o->in2); return_low128(o->out2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_trt(DisasContext *s, DisasOps *o) +static DisasJumpType op_trt(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_trt(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_trtr(DisasContext *s, DisasOps *o) +static DisasJumpType op_trtr(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_trtr(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_trXX(DisasContext *s, DisasOps *o) +static DisasJumpType op_trXX(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4622,28 +4618,28 @@ static ExitStatus op_trXX(DisasContext *s, DisasOps *o) tcg_temp_free_i32(sizes); tcg_temp_free_i32(tst); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ts(DisasContext *s, DisasOps *o) +static DisasJumpType op_ts(DisasContext *s, DisasOps *o) { TCGv_i32 t1 = tcg_const_i32(0xff); tcg_gen_atomic_xchg_i32(t1, o->in2, t1, get_mem_index(s), MO_UB); tcg_gen_extract_i32(cc_op, t1, 7, 1); tcg_temp_free_i32(t1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_unpk(DisasContext *s, DisasOps *o) +static DisasJumpType op_unpk(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_unpk(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_unpka(DisasContext *s, DisasOps *o) +static DisasJumpType op_unpka(DisasContext *s, DisasOps *o) { int l1 = get_field(s->fields, l1) + 1; TCGv_i32 l; @@ -4651,16 +4647,16 @@ static ExitStatus op_unpka(DisasContext *s, DisasOps *o) /* The length must not exceed 32 bytes. */ if (l1 > 32) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } l = tcg_const_i32(l1); gen_helper_unpka(cc_op, cpu_env, o->addr1, l, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_unpku(DisasContext *s, DisasOps *o) +static DisasJumpType op_unpku(DisasContext *s, DisasOps *o) { int l1 = get_field(s->fields, l1) + 1; TCGv_i32 l; @@ -4668,17 +4664,17 @@ static ExitStatus op_unpku(DisasContext *s, DisasOps *o) /* The length must be even and should not exceed 64 bytes. */ if ((l1 & 1) || (l1 > 64)) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } l = tcg_const_i32(l1); gen_helper_unpku(cc_op, cpu_env, o->addr1, l, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_xc(DisasContext *s, DisasOps *o) +static DisasJumpType op_xc(DisasContext *s, DisasOps *o) { int d1 = get_field(s->fields, d1); int d2 = get_field(s->fields, d2); @@ -4719,7 +4715,7 @@ static ExitStatus op_xc(DisasContext *s, DisasOps *o) tcg_gen_qemu_st8(o->in2, o->addr1, get_mem_index(s)); } gen_op_movi_cc(s, 0); - return NO_EXIT; + return DISAS_NEXT; } /* But in general we'll defer to a helper. */ @@ -4728,16 +4724,16 @@ static ExitStatus op_xc(DisasContext *s, DisasOps *o) gen_helper_xc(cc_op, cpu_env, t32, o->addr1, o->in2); tcg_temp_free_i32(t32); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_xor(DisasContext *s, DisasOps *o) +static DisasJumpType op_xor(DisasContext *s, DisasOps *o) { tcg_gen_xor_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_xori(DisasContext *s, DisasOps *o) +static DisasJumpType op_xori(DisasContext *s, DisasOps *o) { int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; @@ -4750,10 +4746,10 @@ static ExitStatus op_xori(DisasContext *s, DisasOps *o) /* Produce the CC from only the bits manipulated. */ tcg_gen_andi_i64(cc_dst, o->out, mask); set_cc_nz_u64(s, cc_dst); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_xi(DisasContext *s, DisasOps *o) +static DisasJumpType op_xi(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); @@ -4771,25 +4767,25 @@ static ExitStatus op_xi(DisasContext *s, DisasOps *o) if (!s390_has_feat(S390_FEAT_INTERLOCKED_ACCESS_2)) { tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), s->insn->data); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_zero(DisasContext *s, DisasOps *o) +static DisasJumpType op_zero(DisasContext *s, DisasOps *o) { o->out = tcg_const_i64(0); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_zero2(DisasContext *s, DisasOps *o) +static DisasJumpType op_zero2(DisasContext *s, DisasOps *o) { o->out = tcg_const_i64(0); o->out2 = o->out; o->g_out2 = true; - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_clp(DisasContext *s, DisasOps *o) +static DisasJumpType op_clp(DisasContext *s, DisasOps *o) { TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4797,10 +4793,10 @@ static ExitStatus op_clp(DisasContext *s, DisasOps *o) gen_helper_clp(cpu_env, r2); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pcilg(DisasContext *s, DisasOps *o) +static DisasJumpType op_pcilg(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4810,10 +4806,10 @@ static ExitStatus op_pcilg(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pcistg(DisasContext *s, DisasOps *o) +static DisasJumpType op_pcistg(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4823,10 +4819,10 @@ static ExitStatus op_pcistg(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stpcifc(DisasContext *s, DisasOps *o) +static DisasJumpType op_stpcifc(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2)); @@ -4836,17 +4832,17 @@ static ExitStatus op_stpcifc(DisasContext *s, DisasOps *o) tcg_temp_free_i32(ar); tcg_temp_free_i32(r1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sic(DisasContext *s, DisasOps *o) +static DisasJumpType op_sic(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sic(cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rpcit(DisasContext *s, DisasOps *o) +static DisasJumpType op_rpcit(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4856,10 +4852,10 @@ static ExitStatus op_rpcit(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pcistb(DisasContext *s, DisasOps *o) +static DisasJumpType op_pcistb(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -4871,10 +4867,10 @@ static ExitStatus op_pcistb(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mpcifc(DisasContext *s, DisasOps *o) +static DisasJumpType op_mpcifc(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2)); @@ -4884,7 +4880,7 @@ static ExitStatus op_mpcifc(DisasContext *s, DisasOps *o) tcg_temp_free_i32(ar); tcg_temp_free_i32(r1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #endif @@ -6028,10 +6024,10 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, return info; } -static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) +static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) { const DisasInsn *insn; - ExitStatus ret = NO_EXIT; + DisasJumpType ret = DISAS_NEXT; DisasFields f; DisasOps o; @@ -6043,7 +6039,7 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%02x%02x\n", f.op, f.op2); gen_illegal_opcode(s); - return EXIT_NORETURN; + return DISAS_NORETURN; } #ifndef CONFIG_USER_ONLY @@ -6090,7 +6086,7 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) } if (excp) { gen_program_exception(s, excp); - return EXIT_NORETURN; + return DISAS_NORETURN; } } @@ -6144,7 +6140,7 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) #ifndef CONFIG_USER_ONLY if (s->tb->flags & FLAG_MASK_PER) { /* An exception might be triggered, save PSW if not already done. */ - if (ret == NO_EXIT || ret == EXIT_PC_STALE) { + if (ret == DISAS_NEXT || ret == DISAS_PC_STALE) { tcg_gen_movi_i64(psw_addr, s->next_pc); } @@ -6165,7 +6161,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) target_ulong pc_start; uint64_t page_start; int num_insns, max_insns; - ExitStatus status; + DisasJumpType status; bool do_debug; pc_start = tb->pc; @@ -6199,7 +6195,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) num_insns++; if (unlikely(cpu_breakpoint_test(cs, dc.pc, BP_ANY))) { - status = EXIT_PC_STALE; + status = DISAS_PC_STALE; do_debug = true; /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be @@ -6217,39 +6213,40 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) /* If we reach a page boundary, are single stepping, or exhaust instruction count, stop generation. */ - if (status == NO_EXIT + if (status == DISAS_NEXT && (dc.pc - page_start >= TARGET_PAGE_SIZE || tcg_op_buf_full() || num_insns >= max_insns || singlestep || cs->singlestep_enabled || dc.ex_value)) { - status = EXIT_PC_STALE; + status = DISAS_TOO_MANY; } - } while (status == NO_EXIT); + } while (status == DISAS_NEXT); if (tb_cflags(tb) & CF_LAST_IO) { gen_io_end(); } switch (status) { - case EXIT_GOTO_TB: - case EXIT_NORETURN: + case DISAS_GOTO_TB: + case DISAS_NORETURN: break; - case EXIT_PC_STALE: - case EXIT_PC_STALE_NOCHAIN: + case DISAS_TOO_MANY: + case DISAS_PC_STALE: + case DISAS_PC_STALE_NOCHAIN: update_psw_addr(&dc); /* FALLTHRU */ - case EXIT_PC_UPDATED: + case DISAS_PC_UPDATED: /* Next TB starts off with CC_OP_DYNAMIC, so make sure the cc op type is in env */ update_cc_op(&dc); /* FALLTHRU */ - case EXIT_PC_CC_UPDATED: + case DISAS_PC_CC_UPDATED: /* Exit the TB, either by raising a debug exception or by return. */ if (do_debug) { gen_exception(EXCP_DEBUG); - } else if (use_exit_tb(&dc) || status == EXIT_PC_STALE_NOCHAIN) { + } else if (use_exit_tb(&dc) || status == DISAS_PC_STALE_NOCHAIN) { tcg_gen_exit_tb(0); } else { tcg_gen_lookup_and_goto_ptr(); From eccf741ab8ee9f2011bdc6d6e47917fab6bebb94 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Fri, 16 Feb 2018 17:59:44 -0500 Subject: [PATCH 0354/2380] target/s390x: convert to DisasContextBase Notes: - Did not convert {num,max}_insns and is_jmp, since the corresponding code will go away in the next patch. - Avoided a checkpatch error in use_exit_tb. - As suggested by David, (1) Drop ctx.pc and use ctx.base.pc_next instead, and (2) Rename ctx.next_pc to ctx.pc_tmp and add a comment about it. Acked-by: Cornelia Huck Suggested-by: David Hildenbrand Reviewed-by: David Hildenbrand Reviewed-by: Richard Henderson Cc: David Hildenbrand Cc: Cornelia Huck Cc: Alexander Graf Cc: qemu-s390x@nongnu.org Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/s390x/translate.c | 148 ++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 72 deletions(-) diff --git a/target/s390x/translate.c b/target/s390x/translate.c index d08c109fe1..5094128e9c 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -52,14 +52,18 @@ typedef struct DisasInsn DisasInsn; typedef struct DisasFields DisasFields; struct DisasContext { - struct TranslationBlock *tb; + DisasContextBase base; const DisasInsn *insn; DisasFields *fields; uint64_t ex_value; - uint64_t pc, next_pc; + /* + * During translate_one(), pc_tmp is used to determine the instruction + * to be executed after base.pc_next - e.g. next sequential instruction + * or a branch target. + */ + uint64_t pc_tmp; uint32_t ilen; enum cc_op cc_op; - bool singlestep_enabled; }; /* Information carried about a condition to be evaluated. */ @@ -81,8 +85,8 @@ static uint64_t inline_branch_miss[CC_OP_MAX]; static uint64_t pc_to_link_info(DisasContext *s, uint64_t pc) { - if (!(s->tb->flags & FLAG_MASK_64)) { - if (s->tb->flags & FLAG_MASK_32) { + if (!(s->base.tb->flags & FLAG_MASK_64)) { + if (s->base.tb->flags & FLAG_MASK_32) { return pc | 0x80000000; } } @@ -188,16 +192,16 @@ static void return_low128(TCGv_i64 dest) static void update_psw_addr(DisasContext *s) { /* psw.addr */ - tcg_gen_movi_i64(psw_addr, s->pc); + tcg_gen_movi_i64(psw_addr, s->base.pc_next); } static void per_branch(DisasContext *s, bool to_next) { #ifndef CONFIG_USER_ONLY - tcg_gen_movi_i64(gbea, s->pc); + tcg_gen_movi_i64(gbea, s->base.pc_next); - if (s->tb->flags & FLAG_MASK_PER) { - TCGv_i64 next_pc = to_next ? tcg_const_i64(s->next_pc) : psw_addr; + if (s->base.tb->flags & FLAG_MASK_PER) { + TCGv_i64 next_pc = to_next ? tcg_const_i64(s->pc_tmp) : psw_addr; gen_helper_per_branch(cpu_env, gbea, next_pc); if (to_next) { tcg_temp_free_i64(next_pc); @@ -210,16 +214,16 @@ static void per_branch_cond(DisasContext *s, TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2) { #ifndef CONFIG_USER_ONLY - if (s->tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & FLAG_MASK_PER) { TCGLabel *lab = gen_new_label(); tcg_gen_brcond_i64(tcg_invert_cond(cond), arg1, arg2, lab); - tcg_gen_movi_i64(gbea, s->pc); + tcg_gen_movi_i64(gbea, s->base.pc_next); gen_helper_per_branch(cpu_env, gbea, psw_addr); gen_set_label(lab); } else { - TCGv_i64 pc = tcg_const_i64(s->pc); + TCGv_i64 pc = tcg_const_i64(s->base.pc_next); tcg_gen_movcond_i64(cond, gbea, arg1, arg2, gbea, pc); tcg_temp_free_i64(pc); } @@ -228,7 +232,7 @@ static void per_branch_cond(DisasContext *s, TCGCond cond, static void per_breaking_event(DisasContext *s) { - tcg_gen_movi_i64(gbea, s->pc); + tcg_gen_movi_i64(gbea, s->base.pc_next); } static void update_cc_op(DisasContext *s) @@ -250,11 +254,11 @@ static inline uint64_t ld_code4(CPUS390XState *env, uint64_t pc) static int get_mem_index(DisasContext *s) { - if (!(s->tb->flags & FLAG_MASK_DAT)) { + if (!(s->base.tb->flags & FLAG_MASK_DAT)) { return MMU_REAL_IDX; } - switch (s->tb->flags & FLAG_MASK_ASC) { + switch (s->base.tb->flags & FLAG_MASK_ASC) { case PSW_ASC_PRIMARY >> FLAG_MASK_PSW_SHIFT: return MMU_PRIMARY_IDX; case PSW_ASC_SECONDARY >> FLAG_MASK_PSW_SHIFT: @@ -319,7 +323,7 @@ static inline void gen_trap(DisasContext *s) #ifndef CONFIG_USER_ONLY static void check_privileged(DisasContext *s) { - if (s->tb->flags & FLAG_MASK_PSTATE) { + if (s->base.tb->flags & FLAG_MASK_PSTATE) { gen_program_exception(s, PGM_PRIVILEGED); } } @@ -328,7 +332,7 @@ static void check_privileged(DisasContext *s) static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2) { TCGv_i64 tmp = tcg_temp_new_i64(); - bool need_31 = !(s->tb->flags & FLAG_MASK_64); + bool need_31 = !(s->base.tb->flags & FLAG_MASK_64); /* Note that d2 is limited to 20 bits, signed. If we crop negative displacements early we create larger immedate addends. */ @@ -541,9 +545,9 @@ static void gen_op_calc_cc(DisasContext *s) static bool use_exit_tb(DisasContext *s) { - return (s->singlestep_enabled || - (tb_cflags(s->tb) & CF_LAST_IO) || - (s->tb->flags & FLAG_MASK_PER)); + return s->base.singlestep_enabled || + (tb_cflags(s->base.tb) & CF_LAST_IO) || + (s->base.tb->flags & FLAG_MASK_PER); } static bool use_goto_tb(DisasContext *s, uint64_t dest) @@ -552,8 +556,8 @@ static bool use_goto_tb(DisasContext *s, uint64_t dest) return false; } #ifndef CONFIG_USER_ONLY - return (dest & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK) || - (dest & TARGET_PAGE_MASK) == (s->pc & TARGET_PAGE_MASK); + return (dest & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK) || + (dest & TARGET_PAGE_MASK) == (s->base.pc_next & TARGET_PAGE_MASK); #else return true; #endif @@ -1145,7 +1149,7 @@ static void help_l2_shift(DisasContext *s, DisasFields *f, static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) { - if (dest == s->next_pc) { + if (dest == s->pc_tmp) { per_branch(s, true); return DISAS_NEXT; } @@ -1154,7 +1158,7 @@ static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) per_breaking_event(s); tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb((uintptr_t)s->tb); + tcg_gen_exit_tb((uintptr_t)s->base.tb); return DISAS_GOTO_TB; } else { tcg_gen_movi_i64(psw_addr, dest); @@ -1167,7 +1171,7 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, bool is_imm, int imm, TCGv_i64 cdest) { DisasJumpType ret; - uint64_t dest = s->pc + 2 * imm; + uint64_t dest = s->base.pc_next + 2 * imm; TCGLabel *lab; /* Take care of the special cases first. */ @@ -1176,7 +1180,7 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, goto egress; } if (is_imm) { - if (dest == s->next_pc) { + if (dest == s->pc_tmp) { /* Branch to next. */ per_branch(s, true); ret = DISAS_NEXT; @@ -1200,7 +1204,7 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, } } - if (use_goto_tb(s, s->next_pc)) { + if (use_goto_tb(s, s->pc_tmp)) { if (is_imm && use_goto_tb(s, dest)) { /* Both exits can use goto_tb. */ update_cc_op(s); @@ -1214,15 +1218,15 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, /* Branch not taken. */ tcg_gen_goto_tb(0); - tcg_gen_movi_i64(psw_addr, s->next_pc); - tcg_gen_exit_tb((uintptr_t)s->tb + 0); + tcg_gen_movi_i64(psw_addr, s->pc_tmp); + tcg_gen_exit_tb((uintptr_t)s->base.tb + 0); /* Branch taken. */ gen_set_label(lab); per_breaking_event(s); tcg_gen_goto_tb(1); tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb((uintptr_t)s->tb + 1); + tcg_gen_exit_tb((uintptr_t)s->base.tb + 1); ret = DISAS_GOTO_TB; } else { @@ -1244,8 +1248,8 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, /* Branch not taken. */ update_cc_op(s); tcg_gen_goto_tb(0); - tcg_gen_movi_i64(psw_addr, s->next_pc); - tcg_gen_exit_tb((uintptr_t)s->tb + 0); + tcg_gen_movi_i64(psw_addr, s->pc_tmp); + tcg_gen_exit_tb((uintptr_t)s->base.tb + 0); gen_set_label(lab); if (is_imm) { @@ -1259,7 +1263,7 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, Most commonly we're single-stepping or some other condition that disables all use of goto_tb. Just update the PC and exit. */ - TCGv_i64 next = tcg_const_i64(s->next_pc); + TCGv_i64 next = tcg_const_i64(s->pc_tmp); if (is_imm) { cdest = tcg_const_i64(dest); } @@ -1448,7 +1452,7 @@ static DisasJumpType op_ni(DisasContext *s, DisasOps *o) static DisasJumpType op_bas(DisasContext *s, DisasOps *o) { - tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->next_pc)); + tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->pc_tmp)); if (o->in2) { tcg_gen_mov_i64(psw_addr, o->in2); per_branch(s, false); @@ -1460,8 +1464,8 @@ static DisasJumpType op_bas(DisasContext *s, DisasOps *o) static DisasJumpType op_basi(DisasContext *s, DisasOps *o) { - tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->next_pc)); - return help_goto_direct(s, s->pc + 2 * get_field(s->fields, i2)); + tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->pc_tmp)); + return help_goto_direct(s, s->base.pc_next + 2 * get_field(s->fields, i2)); } static DisasJumpType op_bc(DisasContext *s, DisasOps *o) @@ -1994,7 +1998,7 @@ static DisasJumpType op_cdsg(DisasContext *s, DisasOps *o) addr = get_address(s, 0, b2, d2); t_r1 = tcg_const_i32(r1); t_r3 = tcg_const_i32(r3); - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_cdsg_parallel(cpu_env, addr, t_r1, t_r3); } else { gen_helper_cdsg(cpu_env, addr, t_r1, t_r3); @@ -2012,7 +2016,7 @@ static DisasJumpType op_csst(DisasContext *s, DisasOps *o) int r3 = get_field(s->fields, r3); TCGv_i32 t_r3 = tcg_const_i32(r3); - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_csst_parallel(cc_op, cpu_env, t_r3, o->in1, o->in2); } else { gen_helper_csst(cc_op, cpu_env, t_r3, o->in1, o->in2); @@ -2972,7 +2976,7 @@ static DisasJumpType op_lpd(DisasContext *s, DisasOps *o) TCGMemOp mop = s->insn->data; /* In a parallel context, stop the world and single step. */ - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { update_psw_addr(s); update_cc_op(s); gen_exception(EXCP_ATOMIC); @@ -2994,7 +2998,7 @@ static DisasJumpType op_lpd(DisasContext *s, DisasOps *o) static DisasJumpType op_lpq(DisasContext *s, DisasOps *o) { - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_lpq_parallel(o->out, cpu_env, o->in2); } else { gen_helper_lpq(o->out, cpu_env, o->in2); @@ -3044,7 +3048,7 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o) o->in2 = NULL; o->g_in2 = false; - switch (s->tb->flags & FLAG_MASK_ASC) { + switch (s->base.tb->flags & FLAG_MASK_ASC) { case PSW_ASC_PRIMARY >> FLAG_MASK_PSW_SHIFT: tcg_gen_movi_i64(ar1, 0); break; @@ -3694,11 +3698,11 @@ static DisasJumpType op_sam(DisasContext *s, DisasOps *o) /* Bizarre but true, we check the address of the current insn for the specification exception, not the next to be executed. Thus the PoO documents that Bad Things Happen two bytes before the end. */ - if (s->pc & ~mask) { + if (s->base.pc_next & ~mask) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - s->next_pc &= mask; + s->pc_tmp &= mask; tsam = tcg_const_i64(sam); tcg_gen_deposit_i64(psw_mask, psw_mask, tsam, 31, 2); @@ -4411,7 +4415,7 @@ static DisasJumpType op_stmh(DisasContext *s, DisasOps *o) static DisasJumpType op_stpq(DisasContext *s, DisasOps *o) { - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_stpq_parallel(cpu_env, o->in2, o->out2, o->out); } else { gen_helper_stpq(cpu_env, o->in2, o->out2, o->out); @@ -4500,8 +4504,8 @@ static DisasJumpType op_tam(DisasContext *s, DisasOps *o) { int cc = 0; - cc |= (s->tb->flags & FLAG_MASK_64) ? 2 : 0; - cc |= (s->tb->flags & FLAG_MASK_32) ? 1 : 0; + cc |= (s->base.tb->flags & FLAG_MASK_64) ? 2 : 0; + cc |= (s->base.tb->flags & FLAG_MASK_32) ? 1 : 0; gen_op_movi_cc(s, cc); return DISAS_NEXT; } @@ -5625,7 +5629,7 @@ static void in2_a2(DisasContext *s, DisasFields *f, DisasOps *o) static void in2_ri2(DisasContext *s, DisasFields *f, DisasOps *o) { - o->in2 = tcg_const_i64(s->pc + (int64_t)get_field(f, i2) * 2); + o->in2 = tcg_const_i64(s->base.pc_next + (int64_t)get_field(f, i2) * 2); } #define SPEC_in2_ri2 0 @@ -5926,7 +5930,7 @@ static void extract_field(DisasFields *o, const DisasField *f, uint64_t insn) static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, DisasFields *f) { - uint64_t insn, pc = s->pc; + uint64_t insn, pc = s->base.pc_next; int op, op2, ilen; const DisasInsn *info; @@ -5958,7 +5962,7 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, g_assert_not_reached(); } } - s->next_pc = s->pc + ilen; + s->pc_tmp = s->base.pc_next + ilen; s->ilen = ilen; /* We can't actually determine the insn format until we've looked up @@ -6043,8 +6047,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } #ifndef CONFIG_USER_ONLY - if (s->tb->flags & FLAG_MASK_PER) { - TCGv_i64 addr = tcg_const_i64(s->pc); + if (s->base.tb->flags & FLAG_MASK_PER) { + TCGv_i64 addr = tcg_const_i64(s->base.pc_next); gen_helper_per_ifetch(cpu_env, addr); tcg_temp_free_i64(addr); } @@ -6138,10 +6142,10 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } #ifndef CONFIG_USER_ONLY - if (s->tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & FLAG_MASK_PER) { /* An exception might be triggered, save PSW if not already done. */ if (ret == DISAS_NEXT || ret == DISAS_PC_STALE) { - tcg_gen_movi_i64(psw_addr, s->next_pc); + tcg_gen_movi_i64(psw_addr, s->pc_tmp); } /* Call the helper to check for a possible PER exception. */ @@ -6150,7 +6154,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) #endif /* Advance to the next instruction. */ - s->pc = s->next_pc; + s->base.pc_next = s->pc_tmp; return ret; } @@ -6158,26 +6162,25 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) { CPUS390XState *env = cs->env_ptr; DisasContext dc; - target_ulong pc_start; uint64_t page_start; int num_insns, max_insns; DisasJumpType status; bool do_debug; - pc_start = tb->pc; - + dc.base.pc_first = tb->pc; /* 31-bit mode */ if (!(tb->flags & FLAG_MASK_64)) { - pc_start &= 0x7fffffff; + dc.base.pc_first &= 0x7fffffff; } + dc.base.pc_next = dc.base.pc_first; + dc.base.tb = tb; + dc.base.singlestep_enabled = cs->singlestep_enabled; - dc.tb = tb; - dc.pc = pc_start; dc.cc_op = CC_OP_DYNAMIC; - dc.ex_value = tb->cs_base; - do_debug = dc.singlestep_enabled = cs->singlestep_enabled; + dc.ex_value = dc.base.tb->cs_base; + do_debug = cs->singlestep_enabled; - page_start = pc_start & TARGET_PAGE_MASK; + page_start = dc.base.pc_first & TARGET_PAGE_MASK; num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; @@ -6191,17 +6194,17 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) gen_tb_start(tb); do { - tcg_gen_insn_start(dc.pc, dc.cc_op); + tcg_gen_insn_start(dc.base.pc_next, dc.cc_op); num_insns++; - if (unlikely(cpu_breakpoint_test(cs, dc.pc, BP_ANY))) { + if (unlikely(cpu_breakpoint_test(cs, dc.base.pc_next, BP_ANY))) { status = DISAS_PC_STALE; do_debug = true; /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that the logic setting tb->size below does the right thing. */ - dc.pc += 2; + dc.base.pc_next += 2; break; } @@ -6214,11 +6217,11 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) /* If we reach a page boundary, are single stepping, or exhaust instruction count, stop generation. */ if (status == DISAS_NEXT - && (dc.pc - page_start >= TARGET_PAGE_SIZE + && (dc.base.pc_next - page_start >= TARGET_PAGE_SIZE || tcg_op_buf_full() || num_insns >= max_insns || singlestep - || cs->singlestep_enabled + || dc.base.singlestep_enabled || dc.ex_value)) { status = DISAS_TOO_MANY; } @@ -6258,19 +6261,20 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) gen_tb_end(tb, num_insns); - tb->size = dc.pc - pc_start; + tb->size = dc.base.pc_next - dc.base.pc_first; tb->icount = num_insns; #if defined(S390X_DEBUG_DISAS) if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { + && qemu_log_in_addr_range(dc.base.pc_first)) { qemu_log_lock(); if (unlikely(dc.ex_value)) { /* ??? Unfortunately log_target_disas can't use host memory. */ qemu_log("IN: EXECUTE %016" PRIx64 "\n", dc.ex_value); } else { - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc.pc - pc_start); + qemu_log("IN: %s\n", lookup_symbol(dc.base.pc_first)); + log_target_disas(cs, dc.base.pc_first, + dc.base.pc_next - dc.base.pc_first); qemu_log("\n"); } qemu_log_unlock(); From c88691aaddf364f25ea5bc7c99671d1903960466 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Wed, 28 Feb 2018 20:06:56 -0500 Subject: [PATCH 0355/2380] target/s390x: convert to TranslatorOps Note: I looked into dropping dc->do_debug. However, I don't see an easy way to do it given that TOO_MANY is also valid when we just translate more than max_insns. Thus, the check for do_debug in "case DISAS_PC_CC_UPDATED" would still need additional state to know whether or not we came from breakpoint_check. Acked-by: Cornelia Huck Reviewed-by: David Hildenbrand Reviewed-by: Richard Henderson Tested-by: David Hildenbrand Cc: David Hildenbrand Cc: Cornelia Huck Cc: Alexander Graf Cc: qemu-s390x@nongnu.org Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/s390x/translate.c | 178 +++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 90 deletions(-) diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 5094128e9c..82309faa11 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -64,6 +64,7 @@ struct DisasContext { uint64_t pc_tmp; uint32_t ilen; enum cc_op cc_op; + bool do_debug; }; /* Information carried about a condition to be evaluated. */ @@ -6158,98 +6159,87 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) return ret; } -void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +static void s390x_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + /* 31-bit mode */ + if (!(dc->base.tb->flags & FLAG_MASK_64)) { + dc->base.pc_first &= 0x7fffffff; + dc->base.pc_next = dc->base.pc_first; + } + + dc->cc_op = CC_OP_DYNAMIC; + dc->ex_value = dc->base.tb->cs_base; + dc->do_debug = dc->base.singlestep_enabled; +} + +static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs) +{ +} + +static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + tcg_gen_insn_start(dc->base.pc_next, dc->cc_op); +} + +static bool s390x_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + dc->base.is_jmp = DISAS_PC_STALE; + dc->do_debug = true; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size does the right thing. */ + dc->base.pc_next += 2; + return true; +} + +static void s390x_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { CPUS390XState *env = cs->env_ptr; - DisasContext dc; - uint64_t page_start; - int num_insns, max_insns; - DisasJumpType status; - bool do_debug; + DisasContext *dc = container_of(dcbase, DisasContext, base); - dc.base.pc_first = tb->pc; - /* 31-bit mode */ - if (!(tb->flags & FLAG_MASK_64)) { - dc.base.pc_first &= 0x7fffffff; - } - dc.base.pc_next = dc.base.pc_first; - dc.base.tb = tb; - dc.base.singlestep_enabled = cs->singlestep_enabled; + dc->base.is_jmp = translate_one(env, dc); + if (dc->base.is_jmp == DISAS_NEXT) { + uint64_t page_start; - dc.cc_op = CC_OP_DYNAMIC; - dc.ex_value = dc.base.tb->cs_base; - do_debug = cs->singlestep_enabled; - - page_start = dc.base.pc_first & TARGET_PAGE_MASK; - - num_insns = 0; - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; - } - - gen_tb_start(tb); - - do { - tcg_gen_insn_start(dc.base.pc_next, dc.cc_op); - num_insns++; - - if (unlikely(cpu_breakpoint_test(cs, dc.base.pc_next, BP_ANY))) { - status = DISAS_PC_STALE; - do_debug = true; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - dc.base.pc_next += 2; - break; + page_start = dc->base.pc_first & TARGET_PAGE_MASK; + if (dc->base.pc_next - page_start >= TARGET_PAGE_SIZE || dc->ex_value) { + dc->base.is_jmp = DISAS_TOO_MANY; } - - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } - - status = translate_one(env, &dc); - - /* If we reach a page boundary, are single stepping, - or exhaust instruction count, stop generation. */ - if (status == DISAS_NEXT - && (dc.base.pc_next - page_start >= TARGET_PAGE_SIZE - || tcg_op_buf_full() - || num_insns >= max_insns - || singlestep - || dc.base.singlestep_enabled - || dc.ex_value)) { - status = DISAS_TOO_MANY; - } - } while (status == DISAS_NEXT); - - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); } +} - switch (status) { +static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + switch (dc->base.is_jmp) { case DISAS_GOTO_TB: case DISAS_NORETURN: break; case DISAS_TOO_MANY: case DISAS_PC_STALE: case DISAS_PC_STALE_NOCHAIN: - update_psw_addr(&dc); + update_psw_addr(dc); /* FALLTHRU */ case DISAS_PC_UPDATED: /* Next TB starts off with CC_OP_DYNAMIC, so make sure the cc op type is in env */ - update_cc_op(&dc); + update_cc_op(dc); /* FALLTHRU */ case DISAS_PC_CC_UPDATED: /* Exit the TB, either by raising a debug exception or by return. */ - if (do_debug) { + if (dc->do_debug) { gen_exception(EXCP_DEBUG); - } else if (use_exit_tb(&dc) || status == DISAS_PC_STALE_NOCHAIN) { + } else if (use_exit_tb(dc) || + dc->base.is_jmp == DISAS_PC_STALE_NOCHAIN) { tcg_gen_exit_tb(0); } else { tcg_gen_lookup_and_goto_ptr(); @@ -6258,28 +6248,36 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) default: g_assert_not_reached(); } +} - gen_tb_end(tb, num_insns); +static void s390x_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); - tb->size = dc.base.pc_next - dc.base.pc_first; - tb->icount = num_insns; - -#if defined(S390X_DEBUG_DISAS) - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(dc.base.pc_first)) { - qemu_log_lock(); - if (unlikely(dc.ex_value)) { - /* ??? Unfortunately log_target_disas can't use host memory. */ - qemu_log("IN: EXECUTE %016" PRIx64 "\n", dc.ex_value); - } else { - qemu_log("IN: %s\n", lookup_symbol(dc.base.pc_first)); - log_target_disas(cs, dc.base.pc_first, - dc.base.pc_next - dc.base.pc_first); - qemu_log("\n"); - } - qemu_log_unlock(); + if (unlikely(dc->ex_value)) { + /* ??? Unfortunately log_target_disas can't use host memory. */ + qemu_log("IN: EXECUTE %016" PRIx64, dc->ex_value); + } else { + qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); + log_target_disas(cs, dc->base.pc_first, dc->base.tb->size); } -#endif +} + +static const TranslatorOps s390x_tr_ops = { + .init_disas_context = s390x_tr_init_disas_context, + .tb_start = s390x_tr_tb_start, + .insn_start = s390x_tr_insn_start, + .breakpoint_check = s390x_tr_breakpoint_check, + .translate_insn = s390x_tr_translate_insn, + .tb_stop = s390x_tr_tb_stop, + .disas_log = s390x_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +{ + DisasContext dc; + + translator_loop(&s390x_tr_ops, &dc.base, cs, tb); } void restore_state_to_opc(CPUS390XState *env, TranslationBlock *tb, From 1ffa4bced09840ffbb0802260492d3a22c5701d3 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Sat, 17 Feb 2018 19:32:26 -0500 Subject: [PATCH 0356/2380] target/openrisc: convert to DisasContextBase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While at it, set is_jmp to DISAS_NORETURN when generating an exception. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Cc: Stafford Horne Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/openrisc/translate.c | 93 ++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 2747b24cf0..b37414fb27 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -36,7 +36,8 @@ #include "exec/log.h" #define LOG_DIS(str, ...) \ - qemu_log_mask(CPU_LOG_TB_IN_ASM, "%08x: " str, dc->pc, ## __VA_ARGS__) + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%08x: " str, dc->base.pc_next, \ + ## __VA_ARGS__) /* is_jmp field values */ #define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ @@ -44,13 +45,10 @@ #define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ typedef struct DisasContext { - TranslationBlock *tb; - target_ulong pc; - uint32_t is_jmp; + DisasContextBase base; uint32_t mem_idx; uint32_t tb_flags; uint32_t delayed_branch; - bool singlestep_enabled; } DisasContext; static TCGv cpu_sr; @@ -126,9 +124,9 @@ static void gen_exception(DisasContext *dc, unsigned int excp) static void gen_illegal_exception(DisasContext *dc) { - tcg_gen_movi_tl(cpu_pc, dc->pc); + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_ILLEGAL); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; } /* not used yet, open it when we need or64. */ @@ -166,12 +164,12 @@ static void check_ov64s(DisasContext *dc) static inline bool use_goto_tb(DisasContext *dc, target_ulong dest) { - if (unlikely(dc->singlestep_enabled)) { + if (unlikely(dc->base.singlestep_enabled)) { return false; } #ifndef CONFIG_USER_ONLY - return (dc->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); + return (dc->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); #else return true; #endif @@ -182,10 +180,10 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) if (use_goto_tb(dc, dest)) { tcg_gen_movi_tl(cpu_pc, dest); tcg_gen_goto_tb(n); - tcg_gen_exit_tb((uintptr_t)dc->tb + n); + tcg_gen_exit_tb((uintptr_t)dc->base.tb + n); } else { tcg_gen_movi_tl(cpu_pc, dest); - if (dc->singlestep_enabled) { + if (dc->base.singlestep_enabled) { gen_exception(dc, EXCP_DEBUG); } tcg_gen_exit_tb(0); @@ -194,16 +192,16 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) static void gen_jump(DisasContext *dc, int32_t n26, uint32_t reg, uint32_t op0) { - target_ulong tmp_pc = dc->pc + n26 * 4; + target_ulong tmp_pc = dc->base.pc_next + n26 * 4; switch (op0) { case 0x00: /* l.j */ tcg_gen_movi_tl(jmp_pc, tmp_pc); break; case 0x01: /* l.jal */ - tcg_gen_movi_tl(cpu_R[9], dc->pc + 8); + tcg_gen_movi_tl(cpu_R[9], dc->base.pc_next + 8); /* Optimize jal being used to load the PC for PIC. */ - if (tmp_pc == dc->pc + 8) { + if (tmp_pc == dc->base.pc_next + 8) { return; } tcg_gen_movi_tl(jmp_pc, tmp_pc); @@ -211,7 +209,7 @@ static void gen_jump(DisasContext *dc, int32_t n26, uint32_t reg, uint32_t op0) case 0x03: /* l.bnf */ case 0x04: /* l.bf */ { - TCGv t_next = tcg_const_tl(dc->pc + 8); + TCGv t_next = tcg_const_tl(dc->base.pc_next + 8); TCGv t_true = tcg_const_tl(tmp_pc); TCGv t_zero = tcg_const_tl(0); @@ -227,7 +225,7 @@ static void gen_jump(DisasContext *dc, int32_t n26, uint32_t reg, uint32_t op0) tcg_gen_mov_tl(jmp_pc, cpu_R[reg]); break; case 0x12: /* l.jalr */ - tcg_gen_movi_tl(cpu_R[9], (dc->pc + 8)); + tcg_gen_movi_tl(cpu_R[9], (dc->base.pc_next + 8)); tcg_gen_mov_tl(jmp_pc, cpu_R[reg]); break; default: @@ -795,7 +793,7 @@ static void dec_misc(DisasContext *dc, uint32_t insn) return; } gen_helper_rfe(cpu_env); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_UPDATE; #endif } break; @@ -1254,15 +1252,16 @@ static void dec_sys(DisasContext *dc, uint32_t insn) switch (op0) { case 0x000: /* l.sys */ LOG_DIS("l.sys %d\n", K16); - tcg_gen_movi_tl(cpu_pc, dc->pc); + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_SYSCALL); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; break; case 0x100: /* l.trap */ LOG_DIS("l.trap %d\n", K16); - tcg_gen_movi_tl(cpu_pc, dc->pc); + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_TRAP); + dc->base.is_jmp = DISAS_NORETURN; break; case 0x300: /* l.csync */ @@ -1479,7 +1478,7 @@ static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) { uint32_t op0; uint32_t insn; - insn = cpu_ldl_code(&cpu->env, dc->pc); + insn = cpu_ldl_code(&cpu->env, dc->base.pc_next); op0 = extract32(insn, 26, 6); switch (op0) { @@ -1532,14 +1531,15 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) int max_insns; pc_start = tb->pc; - dc->tb = tb; - dc->is_jmp = DISAS_NEXT; - dc->pc = pc_start; + dc->base.tb = tb; + dc->base.singlestep_enabled = cs->singlestep_enabled; + dc->base.pc_next = pc_start; + dc->base.is_jmp = DISAS_NEXT; + dc->mem_idx = cpu_mmu_index(&cpu->env, false); - dc->tb_flags = tb->flags; + dc->tb_flags = dc->base.tb->flags; dc->delayed_branch = (dc->tb_flags & TB_FLAGS_DFLAG) != 0; - dc->singlestep_enabled = cs->singlestep_enabled; next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; num_insns = 0; @@ -1570,19 +1570,19 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } do { - tcg_gen_insn_start(dc->pc, (dc->delayed_branch ? 1 : 0) + tcg_gen_insn_start(dc->base.pc_next, (dc->delayed_branch ? 1 : 0) | (num_insns ? 2 : 0)); num_insns++; - if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { - tcg_gen_movi_tl(cpu_pc, dc->pc); + if (unlikely(cpu_breakpoint_test(cs, dc->base.pc_next, BP_ANY))) { + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_DEBUG); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that the logic setting tb->size below does the right thing. */ - dc->pc += 4; + dc->base.pc_next += 4; break; } @@ -1590,7 +1590,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) gen_io_start(); } disas_openrisc_insn(dc, cpu); - dc->pc = dc->pc + 4; + dc->base.pc_next += 4; /* delay slot */ if (dc->delayed_branch) { @@ -1598,15 +1598,15 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (!dc->delayed_branch) { tcg_gen_mov_tl(cpu_pc, jmp_pc); tcg_gen_discard_tl(jmp_pc); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_UPDATE; break; } } - } while (!dc->is_jmp + } while (!dc->base.is_jmp && !tcg_op_buf_full() - && !cs->singlestep_enabled + && !dc->base.singlestep_enabled && !singlestep - && (dc->pc < next_page_start) + && (dc->base.pc_next < next_page_start) && num_insns < max_insns); if (tb_cflags(tb) & CF_LAST_IO) { @@ -1617,35 +1617,34 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) tcg_gen_movi_i32(cpu_dflag, dc->delayed_branch != 0); } - tcg_gen_movi_tl(cpu_ppc, dc->pc - 4); - if (dc->is_jmp == DISAS_NEXT) { - dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_tl(cpu_pc, dc->pc); + tcg_gen_movi_tl(cpu_ppc, dc->base.pc_next - 4); + if (dc->base.is_jmp == DISAS_NEXT) { + dc->base.is_jmp = DISAS_UPDATE; + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); } - if (unlikely(cs->singlestep_enabled)) { + if (unlikely(dc->base.singlestep_enabled)) { gen_exception(dc, EXCP_DEBUG); } else { - switch (dc->is_jmp) { + switch (dc->base.is_jmp) { case DISAS_NEXT: - gen_goto_tb(dc, 0, dc->pc); + gen_goto_tb(dc, 0, dc->base.pc_next); break; default: + case DISAS_NORETURN: case DISAS_JUMP: + case DISAS_TB_JUMP: break; case DISAS_UPDATE: /* indicate that the hash table must be used to find the next TB */ tcg_gen_exit_tb(0); break; - case DISAS_TB_JUMP: - /* nothing more to generate */ - break; } } gen_tb_end(tb, num_insns); - tb->size = dc->pc - pc_start; + tb->size = dc->base.pc_next - pc_start; tb->icount = num_insns; if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) From a4fd3ec3c7299f6a0f99e89aeb8a52c6538ca27b Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Sat, 17 Feb 2018 20:04:40 -0500 Subject: [PATCH 0357/2380] target/openrisc: convert to TranslatorOps Notes: - Changed the num_insns test in insn_start to check for dc->base.num_insns > 1, since when tb_start is first called in a TB, base.num_insns is already set to 1. - Removed DISAS_NEXT from the switch in tb_stop; use DISAS_TOO_MANY instead. - Added an assert_not_reached on tb_stop for DISAS_NEXT and the default case. - Merged the two separate log_target_disas calls into the disas_log op. Reviewed-by: Richard Henderson Cc: Stafford Horne Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/openrisc/translate.c | 165 +++++++++++++++++------------------- 1 file changed, 80 insertions(+), 85 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index b37414fb27..7cf29cd5b0 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1520,46 +1520,22 @@ static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) } } -void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +static void openrisc_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) { + DisasContext *dc = container_of(dcb, DisasContext, base); CPUOpenRISCState *env = cs->env_ptr; - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - struct DisasContext ctx, *dc = &ctx; - uint32_t pc_start; - uint32_t next_page_start; - int num_insns; - int max_insns; + int bound; - pc_start = tb->pc; - - dc->base.tb = tb; - dc->base.singlestep_enabled = cs->singlestep_enabled; - dc->base.pc_next = pc_start; - dc->base.is_jmp = DISAS_NEXT; - - dc->mem_idx = cpu_mmu_index(&cpu->env, false); + dc->mem_idx = cpu_mmu_index(env, false); dc->tb_flags = dc->base.tb->flags; dc->delayed_branch = (dc->tb_flags & TB_FLAGS_DFLAG) != 0; + bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + dc->base.max_insns = MIN(dc->base.max_insns, bound); +} - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - num_insns = 0; - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; - } - - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - qemu_log("----------------\n"); - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - } - - gen_tb_start(tb); +static void openrisc_tr_tb_start(DisasContextBase *db, CPUState *cs) +{ + DisasContext *dc = container_of(db, DisasContext, base); /* Allow the TCG optimizer to see that R0 == 0, when it's true, which is the common case. */ @@ -1568,50 +1544,55 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } else { cpu_R[0] = cpu_R0; } +} - do { - tcg_gen_insn_start(dc->base.pc_next, (dc->delayed_branch ? 1 : 0) - | (num_insns ? 2 : 0)); - num_insns++; +static void openrisc_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); - if (unlikely(cpu_breakpoint_test(cs, dc->base.pc_next, BP_ANY))) { - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - gen_exception(dc, EXCP_DEBUG); - dc->base.is_jmp = DISAS_NORETURN; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - dc->base.pc_next += 4; - break; + tcg_gen_insn_start(dc->base.pc_next, (dc->delayed_branch ? 1 : 0) + | (dc->base.num_insns > 1 ? 2 : 0)); +} + +static bool openrisc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + gen_exception(dc, EXCP_DEBUG); + dc->base.is_jmp = DISAS_NORETURN; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->base.pc_next += 4; + return true; +} + +static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + + disas_openrisc_insn(dc, cpu); + dc->base.pc_next += 4; + + /* delay slot */ + if (dc->delayed_branch) { + dc->delayed_branch--; + if (!dc->delayed_branch) { + tcg_gen_mov_tl(cpu_pc, jmp_pc); + tcg_gen_discard_tl(jmp_pc); + dc->base.is_jmp = DISAS_UPDATE; + return; } - - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } - disas_openrisc_insn(dc, cpu); - dc->base.pc_next += 4; - - /* delay slot */ - if (dc->delayed_branch) { - dc->delayed_branch--; - if (!dc->delayed_branch) { - tcg_gen_mov_tl(cpu_pc, jmp_pc); - tcg_gen_discard_tl(jmp_pc); - dc->base.is_jmp = DISAS_UPDATE; - break; - } - } - } while (!dc->base.is_jmp - && !tcg_op_buf_full() - && !dc->base.singlestep_enabled - && !singlestep - && (dc->base.pc_next < next_page_start) - && num_insns < max_insns); - - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); } +} + +static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); if ((dc->tb_flags & TB_FLAGS_DFLAG ? 1 : 0) != (dc->delayed_branch != 0)) { tcg_gen_movi_i32(cpu_dflag, dc->delayed_branch != 0); @@ -1626,10 +1607,9 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) gen_exception(dc, EXCP_DEBUG); } else { switch (dc->base.is_jmp) { - case DISAS_NEXT: + case DISAS_TOO_MANY: gen_goto_tb(dc, 0, dc->base.pc_next); break; - default: case DISAS_NORETURN: case DISAS_JUMP: case DISAS_TB_JUMP: @@ -1639,20 +1619,35 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) to find the next TB */ tcg_gen_exit_tb(0); break; + default: + g_assert_not_reached(); } } +} - gen_tb_end(tb, num_insns); +static void openrisc_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *s = container_of(dcbase, DisasContext, base); - tb->size = dc->base.pc_next - pc_start; - tb->icount = num_insns; + qemu_log("IN: %s\n", lookup_symbol(s->base.pc_first)); + log_target_disas(cs, s->base.pc_first, s->base.tb->size); +} - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - log_target_disas(cs, pc_start, tb->size); - qemu_log("\n"); - qemu_log_unlock(); - } +static const TranslatorOps openrisc_tr_ops = { + .init_disas_context = openrisc_tr_init_disas_context, + .tb_start = openrisc_tr_tb_start, + .insn_start = openrisc_tr_insn_start, + .breakpoint_check = openrisc_tr_breakpoint_check, + .translate_insn = openrisc_tr_translate_insn, + .tb_stop = openrisc_tr_tb_stop, + .disas_log = openrisc_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +{ + DisasContext ctx; + + translator_loop(&openrisc_tr_ops, &ctx.base, cs, tb); } void openrisc_cpu_dump_state(CPUState *cs, FILE *f, From b2e32021e73349db1b2925e994b7bc4d238f5315 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 13 Feb 2018 18:27:54 -0500 Subject: [PATCH 0358/2380] target/riscv: convert to DisasJumpType Reviewed-by: Bastian Koppelmann Reviewed-by: Richard Henderson Cc: Michael Clark Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/riscv/translate.c | 72 ++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index a98033ca77..1fee5b51dc 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -26,6 +26,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#include "exec/translator.h" #include "exec/log.h" #include "instmap.h" @@ -46,7 +47,7 @@ typedef struct DisasContext { uint32_t flags; uint32_t mem_idx; int singlestep_enabled; - int bstate; + DisasJumpType is_jmp; /* Remember the rounding mode encoded in the previous fp instruction, which we have already installed into env->fp_status. Or -1 for no previous fp instruction. Note that we exit the TB when writing @@ -55,13 +56,6 @@ typedef struct DisasContext { int frm; } DisasContext; -enum { - BS_NONE = 0, /* When seen outside of translation while loop, indicates - need to exit tb due to end of page. */ - BS_STOP = 1, /* Need to exit tb for syscall, sret, etc. */ - BS_BRANCH = 2, /* Need to exit tb for branch, jal, etc. */ -}; - /* convert riscv funct3 to qemu memop for load/store */ static const int tcg_memop_lookup[8] = { [0 ... 7] = -1, @@ -88,7 +82,7 @@ static void generate_exception(DisasContext *ctx, int excp) TCGv_i32 helper_tmp = tcg_const_i32(excp); gen_helper_raise_exception(cpu_env, helper_tmp); tcg_temp_free_i32(helper_tmp); - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; } static void generate_exception_mbadaddr(DisasContext *ctx, int excp) @@ -98,7 +92,7 @@ static void generate_exception_mbadaddr(DisasContext *ctx, int excp) TCGv_i32 helper_tmp = tcg_const_i32(excp); gen_helper_raise_exception(cpu_env, helper_tmp); tcg_temp_free_i32(helper_tmp); - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; } static void gen_exception_debug(void) @@ -531,7 +525,7 @@ static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, } gen_goto_tb(ctx, 0, ctx->pc + imm); /* must use this for safety */ - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; } static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, @@ -562,7 +556,7 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_set_label(misaligned); gen_exception_inst_addr_mis(ctx); } - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; break; default: @@ -616,7 +610,7 @@ static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc, } else { gen_goto_tb(ctx, 0, ctx->pc + bimm); } - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; } static void gen_load(DisasContext *ctx, uint32_t opc, int rd, int rs1, @@ -1344,12 +1338,12 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, /* always generates U-level ECALL, fixed in do_interrupt handler */ generate_exception(ctx, RISCV_EXCP_U_ECALL); tcg_gen_exit_tb(0); /* no chaining */ - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; break; case 0x1: /* EBREAK */ generate_exception(ctx, RISCV_EXCP_BREAKPOINT); tcg_gen_exit_tb(0); /* no chaining */ - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; break; #ifndef CONFIG_USER_ONLY case 0x002: /* URET */ @@ -1359,7 +1353,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, if (riscv_has_ext(env, RVS)) { gen_helper_sret(cpu_pc, cpu_env, cpu_pc); tcg_gen_exit_tb(0); /* no chaining */ - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; } else { gen_exception_illegal(ctx); } @@ -1370,7 +1364,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, case 0x302: /* MRET */ gen_helper_mret(cpu_pc, cpu_env, cpu_pc); tcg_gen_exit_tb(0); /* no chaining */ - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; break; case 0x7b2: /* DRET */ gen_exception_illegal(ctx); @@ -1419,7 +1413,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, /* end tb since we may be changing priv modes, to get mmu_index right */ tcg_gen_movi_tl(cpu_pc, ctx->next_pc); tcg_gen_exit_tb(0); /* no chaining */ - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; break; } tcg_temp_free(source1); @@ -1812,7 +1806,7 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) * however we need to end the translation block */ tcg_gen_movi_tl(cpu_pc, ctx->next_pc); tcg_gen_exit_tb(0); - ctx->bstate = BS_BRANCH; + ctx->is_jmp = DISAS_NORETURN; } else { /* FENCE is a full memory barrier. */ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); @@ -1862,7 +1856,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) ctx.singlestep_enabled = cs->singlestep_enabled; ctx.tb = tb; - ctx.bstate = BS_NONE; + ctx.is_jmp = DISAS_NEXT; ctx.flags = tb->flags; ctx.mem_idx = tb->flags & TB_FLAGS_MMU_MASK; ctx.frm = -1; /* unknown rounding mode */ @@ -1877,13 +1871,13 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) } gen_tb_start(tb); - while (ctx.bstate == BS_NONE) { + while (ctx.is_jmp == DISAS_NEXT) { tcg_gen_insn_start(ctx.pc); num_insns++; if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { tcg_gen_movi_tl(cpu_pc, ctx.pc); - ctx.bstate = BS_BRANCH; + ctx.is_jmp = DISAS_NORETURN; gen_exception_debug(); /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be @@ -1901,31 +1895,20 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) decode_opc(env, &ctx); ctx.pc = ctx.next_pc; - if (cs->singlestep_enabled) { - break; + if (ctx.is_jmp == DISAS_NEXT && + (cs->singlestep_enabled || + ctx.pc - page_start >= TARGET_PAGE_SIZE || + tcg_op_buf_full() || + num_insns >= max_insns || + singlestep)) { + ctx.is_jmp = DISAS_TOO_MANY; } - if (ctx.pc - page_start >= TARGET_PAGE_SIZE) { - break; - } - if (tcg_op_buf_full()) { - break; - } - if (num_insns >= max_insns) { - break; - } - if (singlestep) { - break; - } - } if (tb->cflags & CF_LAST_IO) { gen_io_end(); } - switch (ctx.bstate) { - case BS_STOP: - gen_goto_tb(&ctx, 0, ctx.pc); - break; - case BS_NONE: /* handle end of page - DO NOT CHAIN. See gen_goto_tb. */ + switch (ctx.is_jmp) { + case DISAS_TOO_MANY: tcg_gen_movi_tl(cpu_pc, ctx.pc); if (cs->singlestep_enabled) { gen_exception_debug(); @@ -1933,9 +1916,10 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) tcg_gen_exit_tb(0); } break; - case BS_BRANCH: /* ops using BS_BRANCH generate own exit seq */ - default: + case DISAS_NORETURN: break; + default: + g_assert_not_reached(); } done_generating: gen_tb_end(tb, num_insns); From 0114db1c827919f22bfd86006e787fafb9d6256b Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 13 Feb 2018 18:28:36 -0500 Subject: [PATCH 0359/2380] target/riscv: convert to DisasContextBase Notes: - Did not convert {num,max}_insns, since the corresponding code will go away in the next patch. - ctx->pc becomes ctx->base.pc_next, and ctx->next_pc becomes ctx->pc_succ_insn. While at it, convert the remaining tb->cflags readers to tb_cflags(). Reviewed-by: Richard Henderson Cc: Michael Clark Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/riscv/translate.c | 129 +++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 65 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 1fee5b51dc..68979abfd7 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -40,14 +40,12 @@ static TCGv load_val; #include "exec/gen-icount.h" typedef struct DisasContext { - struct TranslationBlock *tb; - target_ulong pc; - target_ulong next_pc; + DisasContextBase base; + /* pc_succ_insn points to the instruction following base.pc_next */ + target_ulong pc_succ_insn; uint32_t opcode; uint32_t flags; uint32_t mem_idx; - int singlestep_enabled; - DisasJumpType is_jmp; /* Remember the rounding mode encoded in the previous fp instruction, which we have already installed into env->fp_status. Or -1 for no previous fp instruction. Note that we exit the TB when writing @@ -78,21 +76,21 @@ static const int tcg_memop_lookup[8] = { static void generate_exception(DisasContext *ctx, int excp) { - tcg_gen_movi_tl(cpu_pc, ctx->pc); + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); TCGv_i32 helper_tmp = tcg_const_i32(excp); gen_helper_raise_exception(cpu_env, helper_tmp); tcg_temp_free_i32(helper_tmp); - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } static void generate_exception_mbadaddr(DisasContext *ctx, int excp) { - tcg_gen_movi_tl(cpu_pc, ctx->pc); + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); TCGv_i32 helper_tmp = tcg_const_i32(excp); gen_helper_raise_exception(cpu_env, helper_tmp); tcg_temp_free_i32(helper_tmp); - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } static void gen_exception_debug(void) @@ -114,12 +112,12 @@ static void gen_exception_inst_addr_mis(DisasContext *ctx) static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) { - if (unlikely(ctx->singlestep_enabled)) { + if (unlikely(ctx->base.singlestep_enabled)) { return false; } #ifndef CONFIG_USER_ONLY - return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); + return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); #else return true; #endif @@ -131,10 +129,10 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) /* chaining is only allowed when the jump is to the same page */ tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->tb + n); + tcg_gen_exit_tb((uintptr_t)ctx->base.tb + n); } else { tcg_gen_movi_tl(cpu_pc, dest); - if (ctx->singlestep_enabled) { + if (ctx->base.singlestep_enabled) { gen_exception_debug(); } else { tcg_gen_exit_tb(0); @@ -513,7 +511,7 @@ static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, target_ulong next_pc; /* check misaligned: */ - next_pc = ctx->pc + imm; + next_pc = ctx->base.pc_next + imm; if (!riscv_has_ext(env, RVC)) { if ((next_pc & 0x3) != 0) { gen_exception_inst_addr_mis(ctx); @@ -521,11 +519,11 @@ static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, } } if (rd != 0) { - tcg_gen_movi_tl(cpu_gpr[rd], ctx->next_pc); + tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn); } - gen_goto_tb(ctx, 0, ctx->pc + imm); /* must use this for safety */ - ctx->is_jmp = DISAS_NORETURN; + gen_goto_tb(ctx, 0, ctx->base.pc_next + imm); /* must use this for safety */ + ctx->base.is_jmp = DISAS_NORETURN; } static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, @@ -548,7 +546,7 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, } if (rd != 0) { - tcg_gen_movi_tl(cpu_gpr[rd], ctx->next_pc); + tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn); } tcg_gen_exit_tb(0); @@ -556,7 +554,7 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_set_label(misaligned); gen_exception_inst_addr_mis(ctx); } - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; break; default: @@ -602,15 +600,15 @@ static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc, tcg_temp_free(source1); tcg_temp_free(source2); - gen_goto_tb(ctx, 1, ctx->next_pc); + gen_goto_tb(ctx, 1, ctx->pc_succ_insn); gen_set_label(l); /* branch taken */ - if (!riscv_has_ext(env, RVC) && ((ctx->pc + bimm) & 0x3)) { + if (!riscv_has_ext(env, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) { /* misaligned */ gen_exception_inst_addr_mis(ctx); } else { - gen_goto_tb(ctx, 0, ctx->pc + bimm); + gen_goto_tb(ctx, 0, ctx->base.pc_next + bimm); } - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } static void gen_load(DisasContext *ctx, uint32_t opc, int rd, int rs1, @@ -836,7 +834,7 @@ static void gen_atomic(DisasContext *ctx, uint32_t opc, if (rl) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } - if (tb_cflags(ctx->tb) & CF_PARALLEL) { + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { l1 = gen_new_label(); gen_set_label(l1); } else { @@ -853,7 +851,7 @@ static void gen_atomic(DisasContext *ctx, uint32_t opc, tcg_gen_qemu_ld_tl(dat, src1, ctx->mem_idx, mop); tcg_gen_movcond_tl(cond, src2, dat, src2, dat, src2); - if (tb_cflags(ctx->tb) & CF_PARALLEL) { + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { /* Parallel context. Make this operation atomic by verifying that the memory didn't change while we computed the result. */ tcg_gen_atomic_cmpxchg_tl(src2, src1, dat, src2, ctx->mem_idx, mop); @@ -1317,7 +1315,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, rs1_pass = tcg_temp_new(); imm_rs1 = tcg_temp_new(); gen_get_gpr(source1, rs1); - tcg_gen_movi_tl(cpu_pc, ctx->pc); + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); tcg_gen_movi_tl(rs1_pass, rs1); tcg_gen_movi_tl(csr_store, csr); /* copy into temp reg to feed to helper */ @@ -1338,12 +1336,12 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, /* always generates U-level ECALL, fixed in do_interrupt handler */ generate_exception(ctx, RISCV_EXCP_U_ECALL); tcg_gen_exit_tb(0); /* no chaining */ - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; break; case 0x1: /* EBREAK */ generate_exception(ctx, RISCV_EXCP_BREAKPOINT); tcg_gen_exit_tb(0); /* no chaining */ - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; break; #ifndef CONFIG_USER_ONLY case 0x002: /* URET */ @@ -1353,7 +1351,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, if (riscv_has_ext(env, RVS)) { gen_helper_sret(cpu_pc, cpu_env, cpu_pc); tcg_gen_exit_tb(0); /* no chaining */ - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } else { gen_exception_illegal(ctx); } @@ -1364,13 +1362,13 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, case 0x302: /* MRET */ gen_helper_mret(cpu_pc, cpu_env, cpu_pc); tcg_gen_exit_tb(0); /* no chaining */ - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; break; case 0x7b2: /* DRET */ gen_exception_illegal(ctx); break; case 0x105: /* WFI */ - tcg_gen_movi_tl(cpu_pc, ctx->next_pc); + tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); gen_helper_wfi(cpu_env); break; case 0x104: /* SFENCE.VM */ @@ -1411,9 +1409,9 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_io_end(); gen_set_gpr(rd, dest); /* end tb since we may be changing priv modes, to get mmu_index right */ - tcg_gen_movi_tl(cpu_pc, ctx->next_pc); + tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); tcg_gen_exit_tb(0); /* no chaining */ - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; break; } tcg_temp_free(source1); @@ -1731,7 +1729,7 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) break; /* NOP */ } tcg_gen_movi_tl(cpu_gpr[rd], (sextract64(ctx->opcode, 12, 20) << 12) + - ctx->pc); + ctx->base.pc_next); break; case OPC_RISC_JAL: imm = GET_JAL_IMM(ctx->opcode); @@ -1804,9 +1802,9 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) if (ctx->opcode & 0x1000) { /* FENCE_I is a no-op in QEMU, * however we need to end the translation block */ - tcg_gen_movi_tl(cpu_pc, ctx->next_pc); + tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); tcg_gen_exit_tb(0); - ctx->is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } else { /* FENCE is a full memory barrier. */ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); @@ -1830,11 +1828,11 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx) if (!riscv_has_ext(env, RVC)) { gen_exception_illegal(ctx); } else { - ctx->next_pc = ctx->pc + 2; + ctx->pc_succ_insn = ctx->base.pc_next + 2; decode_RV32_64C(env, ctx); } } else { - ctx->next_pc = ctx->pc + 4; + ctx->pc_succ_insn = ctx->base.pc_next + 4; decode_RV32_64G(env, ctx); } } @@ -1843,26 +1841,26 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) { CPURISCVState *env = cs->env_ptr; DisasContext ctx; - target_ulong pc_start; target_ulong page_start; int num_insns; int max_insns; - pc_start = tb->pc; - page_start = pc_start & TARGET_PAGE_MASK; - ctx.pc = pc_start; + ctx.base.pc_first = tb->pc; + ctx.base.pc_next = ctx.base.pc_first; /* once we have GDB, the rest of the translate.c implementation should be ready for singlestep */ - ctx.singlestep_enabled = cs->singlestep_enabled; + ctx.base.singlestep_enabled = cs->singlestep_enabled; + ctx.base.tb = tb; + ctx.base.is_jmp = DISAS_NEXT; - ctx.tb = tb; - ctx.is_jmp = DISAS_NEXT; + page_start = ctx.base.pc_first & TARGET_PAGE_MASK; + ctx.pc_succ_insn = ctx.base.pc_first; ctx.flags = tb->flags; ctx.mem_idx = tb->flags & TB_FLAGS_MMU_MASK; ctx.frm = -1; /* unknown rounding mode */ num_insns = 0; - max_insns = tb->cflags & CF_COUNT_MASK; + max_insns = tb_cflags(ctx.base.tb) & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; } @@ -1871,45 +1869,45 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) } gen_tb_start(tb); - while (ctx.is_jmp == DISAS_NEXT) { - tcg_gen_insn_start(ctx.pc); + while (ctx.base.is_jmp == DISAS_NEXT) { + tcg_gen_insn_start(ctx.base.pc_next); num_insns++; - if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { - tcg_gen_movi_tl(cpu_pc, ctx.pc); - ctx.is_jmp = DISAS_NORETURN; + if (unlikely(cpu_breakpoint_test(cs, ctx.base.pc_next, BP_ANY))) { + tcg_gen_movi_tl(cpu_pc, ctx.base.pc_next); + ctx.base.is_jmp = DISAS_NORETURN; gen_exception_debug(); /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that the logic setting tb->size below does the right thing. */ - ctx.pc += 4; + ctx.base.pc_next += 4; goto done_generating; } - if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { + if (num_insns == max_insns && (tb_cflags(ctx.base.tb) & CF_LAST_IO)) { gen_io_start(); } - ctx.opcode = cpu_ldl_code(env, ctx.pc); + ctx.opcode = cpu_ldl_code(env, ctx.base.pc_next); decode_opc(env, &ctx); - ctx.pc = ctx.next_pc; + ctx.base.pc_next = ctx.pc_succ_insn; - if (ctx.is_jmp == DISAS_NEXT && + if (ctx.base.is_jmp == DISAS_NEXT && (cs->singlestep_enabled || - ctx.pc - page_start >= TARGET_PAGE_SIZE || + ctx.base.pc_next - page_start >= TARGET_PAGE_SIZE || tcg_op_buf_full() || num_insns >= max_insns || singlestep)) { - ctx.is_jmp = DISAS_TOO_MANY; + ctx.base.is_jmp = DISAS_TOO_MANY; } } - if (tb->cflags & CF_LAST_IO) { + if (tb_cflags(ctx.base.tb) & CF_LAST_IO) { gen_io_end(); } - switch (ctx.is_jmp) { + switch (ctx.base.is_jmp) { case DISAS_TOO_MANY: - tcg_gen_movi_tl(cpu_pc, ctx.pc); + tcg_gen_movi_tl(cpu_pc, ctx.base.pc_next); if (cs->singlestep_enabled) { gen_exception_debug(); } else { @@ -1923,14 +1921,15 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) } done_generating: gen_tb_end(tb, num_insns); - tb->size = ctx.pc - pc_start; + tb->size = ctx.base.pc_next - ctx.base.pc_first; tb->icount = num_insns; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, ctx.pc - pc_start); + && qemu_log_in_addr_range(ctx.base.pc_first)) { + qemu_log("IN: %s\n", lookup_symbol(ctx.base.pc_first)); + log_target_disas(cs, ctx.base.pc_first, + ctx.base.pc_next - ctx.base.pc_first); qemu_log("\n"); } #endif From 5b4f1d2db9fb0e24549054ee58c7a7d8f84ddd6e Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Fri, 6 Apr 2018 13:42:27 -0400 Subject: [PATCH 0360/2380] target/riscv: convert to TranslatorOps Reviewed-by: Richard Henderson Reviewed-by: Michael Clark Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- target/riscv/translate.c | 148 ++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 73 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 68979abfd7..1788668c6f 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1837,78 +1837,71 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx) } } -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { - CPURISCVState *env = cs->env_ptr; - DisasContext ctx; - target_ulong page_start; - int num_insns; - int max_insns; + DisasContext *ctx = container_of(dcbase, DisasContext, base); - ctx.base.pc_first = tb->pc; - ctx.base.pc_next = ctx.base.pc_first; - /* once we have GDB, the rest of the translate.c implementation should be - ready for singlestep */ - ctx.base.singlestep_enabled = cs->singlestep_enabled; - ctx.base.tb = tb; - ctx.base.is_jmp = DISAS_NEXT; + ctx->pc_succ_insn = ctx->base.pc_first; + ctx->flags = ctx->base.tb->flags; + ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; + ctx->frm = -1; /* unknown rounding mode */ +} - page_start = ctx.base.pc_first & TARGET_PAGE_MASK; - ctx.pc_succ_insn = ctx.base.pc_first; - ctx.flags = tb->flags; - ctx.mem_idx = tb->flags & TB_FLAGS_MMU_MASK; - ctx.frm = -1; /* unknown rounding mode */ +static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) +{ +} - num_insns = 0; - max_insns = tb_cflags(ctx.base.tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; - } - gen_tb_start(tb); +static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); - while (ctx.base.is_jmp == DISAS_NEXT) { - tcg_gen_insn_start(ctx.base.pc_next); - num_insns++; + tcg_gen_insn_start(ctx->base.pc_next); +} - if (unlikely(cpu_breakpoint_test(cs, ctx.base.pc_next, BP_ANY))) { - tcg_gen_movi_tl(cpu_pc, ctx.base.pc_next); - ctx.base.is_jmp = DISAS_NORETURN; - gen_exception_debug(); - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - ctx.base.pc_next += 4; - goto done_generating; - } +static bool riscv_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, + const CPUBreakpoint *bp) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); - if (num_insns == max_insns && (tb_cflags(ctx.base.tb) & CF_LAST_IO)) { - gen_io_start(); - } + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + ctx->base.is_jmp = DISAS_NORETURN; + gen_exception_debug(); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx->base.pc_next += 4; + return true; +} - ctx.opcode = cpu_ldl_code(env, ctx.base.pc_next); - decode_opc(env, &ctx); - ctx.base.pc_next = ctx.pc_succ_insn; - if (ctx.base.is_jmp == DISAS_NEXT && - (cs->singlestep_enabled || - ctx.base.pc_next - page_start >= TARGET_PAGE_SIZE || - tcg_op_buf_full() || - num_insns >= max_insns || - singlestep)) { - ctx.base.is_jmp = DISAS_TOO_MANY; +static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + CPURISCVState *env = cpu->env_ptr; + + ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); + decode_opc(env, ctx); + ctx->base.pc_next = ctx->pc_succ_insn; + + if (ctx->base.is_jmp == DISAS_NEXT) { + target_ulong page_start; + + page_start = ctx->base.pc_first & TARGET_PAGE_MASK; + if (ctx->base.pc_next - page_start >= TARGET_PAGE_SIZE) { + ctx->base.is_jmp = DISAS_TOO_MANY; } } - if (tb_cflags(ctx.base.tb) & CF_LAST_IO) { - gen_io_end(); - } - switch (ctx.base.is_jmp) { +} + +static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + switch (ctx->base.is_jmp) { case DISAS_TOO_MANY: - tcg_gen_movi_tl(cpu_pc, ctx.base.pc_next); - if (cs->singlestep_enabled) { + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + if (ctx->base.singlestep_enabled) { gen_exception_debug(); } else { tcg_gen_exit_tb(0); @@ -1919,20 +1912,29 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) default: g_assert_not_reached(); } -done_generating: - gen_tb_end(tb, num_insns); - tb->size = ctx.base.pc_next - ctx.base.pc_first; - tb->icount = num_insns; +} -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(ctx.base.pc_first)) { - qemu_log("IN: %s\n", lookup_symbol(ctx.base.pc_first)); - log_target_disas(cs, ctx.base.pc_first, - ctx.base.pc_next - ctx.base.pc_first); - qemu_log("\n"); - } -#endif +static void riscv_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); +} + +static const TranslatorOps riscv_tr_ops = { + .init_disas_context = riscv_tr_init_disas_context, + .tb_start = riscv_tr_tb_start, + .insn_start = riscv_tr_insn_start, + .breakpoint_check = riscv_tr_breakpoint_check, + .translate_insn = riscv_tr_translate_insn, + .tb_stop = riscv_tr_tb_stop, + .disas_log = riscv_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +{ + DisasContext ctx; + + translator_loop(&riscv_tr_ops, &ctx.base, cs, tb); } void riscv_translate_init(void) From 23d702d898bdd8e6772d83ea9789767ed589e17e Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 24 Apr 2018 09:52:40 +0100 Subject: [PATCH 0361/2380] blockjob: drop block_job_pause/resume_all() Commit 8119334918e86f45877cfc139192d54f2449a239 ("block: Don't block_job_pause_all() in bdrv_drain_all()") removed the only callers of block_job_pause/resume_all(). Pausing and resuming now happens in child_job_drained_begin/end() so it's no longer necessary to globally pause/resume jobs. Signed-off-by: Stefan Hajnoczi Reviewed-by: John Snow Reviewed-by: Alberto Garcia Message-id: 20180424085240.5798-1-stefanha@redhat.com Signed-off-by: Stefan Hajnoczi --- blockjob.c | 27 --------------------------- include/block/blockjob_int.h | 14 -------------- 2 files changed, 41 deletions(-) diff --git a/blockjob.c b/blockjob.c index 27f957e571..dfffad921a 100644 --- a/blockjob.c +++ b/blockjob.c @@ -988,19 +988,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return job; } -void block_job_pause_all(void) -{ - BlockJob *job = NULL; - while ((job = block_job_next(job))) { - AioContext *aio_context = blk_get_aio_context(job->blk); - - aio_context_acquire(aio_context); - block_job_ref(job); - block_job_pause(job); - aio_context_release(aio_context); - } -} - void block_job_early_fail(BlockJob *job) { assert(job->status == BLOCK_JOB_STATUS_CREATED); @@ -1078,20 +1065,6 @@ void coroutine_fn block_job_pause_point(BlockJob *job) } } -void block_job_resume_all(void) -{ - BlockJob *job, *next; - - QLIST_FOREACH_SAFE(job, &block_jobs, job_list, next) { - AioContext *aio_context = blk_get_aio_context(job->blk); - - aio_context_acquire(aio_context); - block_job_resume(job); - block_job_unref(job); - aio_context_release(aio_context); - } -} - /* * Conditionally enter a block_job pending a call to fn() while * under the block_job_lock critical section. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 642adce68b..d5a515de9b 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -168,20 +168,6 @@ void block_job_sleep_ns(BlockJob *job, int64_t ns); */ void block_job_yield(BlockJob *job); -/** - * block_job_pause_all: - * - * Asynchronously pause all jobs. - */ -void block_job_pause_all(void); - -/** - * block_job_resume_all: - * - * Resume all block jobs. Must be paired with a preceding block_job_pause_all. - */ -void block_job_resume_all(void); - /** * block_job_early_fail: * @bs: The block device. From b37c4aacf7c7261127828c2d1100618dd8702deb Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Apr 2018 13:46:47 +0100 Subject: [PATCH 0362/2380] checkpatch: add a --strict check for utf-8 in commit logs Some find using utf-8 in commit logs inappropriate. Some patch commit logs contain unintended utf-8 characters when doing things like copy/pasting compilation output. Look for the start of any commit log by skipping initial lines that look like email headers and "From: " lines. Stop looking for utf-8 at the first signature line. Signed-off-by: Joe Perches Reviewed-by: Markus Armbruster Reviewed-by: Thomas Huth Signed-off-by: Stefan Hajnoczi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Message-id: 20180430124651.10340-2-stefanha@redhat.com Suggested-by: Andrew Morton Cc: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit 15662b3e8644905032c2e26808401a487d4e90c1) Signed-off-by: Stefan Hajnoczi Conflicts: QEMU does not have CHK(), use WARN() instead. QEMU WARN() only takes one argument, drop the 'type' value in the first argument. Signed-off-by: Stefan Hajnoczi --- scripts/checkpatch.pl | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 5b8735defb..c667d085ae 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -224,9 +224,8 @@ our $NonptrType; our $Type; our $Declare; -our $UTF8 = qr { - [\x09\x0A\x0D\x20-\x7E] # ASCII - | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte +our $NON_ASCII_UTF8 = qr{ + [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates @@ -235,6 +234,11 @@ our $UTF8 = qr { | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 }x; +our $UTF8 = qr{ + [\x09\x0A\x0D\x20-\x7E] # ASCII + | $NON_ASCII_UTF8 +}x; + # There are still some false positives, but this catches most # common cases. our $typeTypedefs = qr{(?x: @@ -1179,6 +1183,9 @@ sub process { my $signoff = 0; my $is_patch = 0; + my $in_header_lines = 1; + my $in_commit_log = 0; #Scanning lines before patch + our @report = (); our $cnt_lines = 0; our $cnt_error = 0; @@ -1331,7 +1338,6 @@ sub process { if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@; - } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@; @@ -1370,6 +1376,8 @@ sub process { if ($line =~ /^\s*signed-off-by:/i) { # This is a signoff, if ugly, so do not double report. $signoff++; + $in_commit_log = 0; + if (!($line =~ /^\s*Signed-off-by:/)) { ERROR("The correct form is \"Signed-off-by\"\n" . $herecurr); @@ -1398,6 +1406,20 @@ sub process { ERROR("Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); } +# Check if it's the start of a commit log +# (not a header line and we haven't seen the patch filename) + if ($in_header_lines && $realfile =~ /^$/ && + $rawline !~ /^(commit\b|from\b|\w+:).+$/i) { + $in_header_lines = 0; + $in_commit_log = 1; + } + +# Still not yet in a patch, check for any UTF-8 + if ($in_commit_log && $realfile =~ /^$/ && + $rawline =~ /$NON_ASCII_UTF8/) { + WARN("8-bit UTF-8 used in possible commit log\n" . $herecurr); + } + # ignore non-hunk lines and lines being removed next if (!$hunk_line || $line =~ /^-/); From bf139a06144db84f420576d73da7a08b52d09bb5 Mon Sep 17 00:00:00 2001 From: Pasi Savanainen Date: Mon, 30 Apr 2018 13:46:48 +0100 Subject: [PATCH 0363/2380] checkpatch: check utf-8 content from a commit log when it's missing from charset Check that a commit log doesn't contain UTF-8 when a mail header explicitly defines a different charset, like 'Content-Type: text/plain; charset="us-ascii"' Signed-off-by: Pasi Savanainen Reviewed-by: Markus Armbruster Reviewed-by: Thomas Huth Signed-off-by: Stefan Hajnoczi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Message-id: 20180430124651.10340-3-stefanha@redhat.com Cc: Joe Perches Cc: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit fa64205df9dfd7b7662cc64a7e82115c00e428e5) Signed-off-by: Stefan Hajnoczi Reviewed-by: Thomas Huth Signed-off-by: Stefan Hajnoczi --- scripts/checkpatch.pl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index c667d085ae..25bf43bad0 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1186,6 +1186,8 @@ sub process { my $in_header_lines = 1; my $in_commit_log = 0; #Scanning lines before patch + my $non_utf8_charset = 0; + our @report = (); our $cnt_lines = 0; our $cnt_error = 0; @@ -1414,8 +1416,15 @@ sub process { $in_commit_log = 1; } -# Still not yet in a patch, check for any UTF-8 - if ($in_commit_log && $realfile =~ /^$/ && +# Check if there is UTF-8 in a commit log when a mail header has explicitly +# declined it, i.e defined some charset where it is missing. + if ($in_header_lines && + $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && + $1 !~ /utf-8/i) { + $non_utf8_charset = 1; + } + + if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && $rawline =~ /$NON_ASCII_UTF8/) { WARN("8-bit UTF-8 used in possible commit log\n" . $herecurr); } From 5fc7e4047ebafc9f04f75a5d697234f7f0816455 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Apr 2018 13:46:49 +0100 Subject: [PATCH 0364/2380] checkpatch: ignore email headers better There are some patches created by git format-patch that when scanned by checkpatch report errors on lines like To: address.tld This is a checkpatch false positive. Improve the logic a bit to ignore folded email headers to avoid emitting these messages. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Reviewed-by: Markus Armbruster Reviewed-by: Thomas Huth Signed-off-by: Stefan Hajnoczi Message-id: 20180430124651.10340-4-stefanha@redhat.com (cherry picked from commit 29ee1b0c67e0dd7dea8dd718e8326076bce5b6fe) Signed-off-by: Stefan Hajnoczi Reviewed-by: Thomas Huth Signed-off-by: Stefan Hajnoczi --- scripts/checkpatch.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 25bf43bad0..20d5b62586 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1183,7 +1183,7 @@ sub process { my $signoff = 0; my $is_patch = 0; - my $in_header_lines = 1; + my $in_header_lines = $file ? 0 : 1; my $in_commit_log = 0; #Scanning lines before patch my $non_utf8_charset = 0; @@ -1411,7 +1411,8 @@ sub process { # Check if it's the start of a commit log # (not a header line and we haven't seen the patch filename) if ($in_header_lines && $realfile =~ /^$/ && - $rawline !~ /^(commit\b|from\b|\w+:).+$/i) { + !($rawline =~ /^\s+\S/ || + $rawline =~ /^(commit\b|from\b|[\w-]+:).*$/i)) { $in_header_lines = 0; $in_commit_log = 1; } From 4be6131e32990bc27a2018b805210ed2c80cb5fc Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Apr 2018 13:46:50 +0100 Subject: [PATCH 0365/2380] checkpatch: emit a warning on file add/move/delete Whenever files are added, moved, or deleted, the MAINTAINERS file patterns can be out of sync or outdated. To try to keep MAINTAINERS more up-to-date, add a one-time warning whenever a patch does any of those. Signed-off-by: Joe Perches Acked-by: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Reviewed-by: Markus Armbruster Reviewed-by: Thomas Huth Signed-off-by: Stefan Hajnoczi Message-id: 20180430124651.10340-5-stefanha@redhat.com (cherry picked from commit 13f1937ef33950b1112049972249e6191b82e6c9) Signed-off-by: Stefan Hajnoczi Reviewed-by: Thomas Huth Conflicts: QEMU WARN() only takes one argument, drop the 'type' value in the first argument. Signed-off-by: Stefan Hajnoczi --- scripts/checkpatch.pl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 20d5b62586..84bdf53af7 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1185,7 +1185,7 @@ sub process { my $in_header_lines = $file ? 0 : 1; my $in_commit_log = 0; #Scanning lines before patch - + my $reported_maintainer_file = 0; my $non_utf8_charset = 0; our @report = (); @@ -1390,6 +1390,16 @@ sub process { } } +# Check for added, moved or deleted files + if (!$reported_maintainer_file && !$in_commit_log && + ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || + $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || + ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && + (defined($1) || defined($2))))) { + $reported_maintainer_file = 1; + WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); + } + # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("patch seems to be corrupt (line wrapped?)\n" . From 1a6fad0c3bdeb320ef78dd8ce966e637ff6d356b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Apr 2018 13:46:51 +0100 Subject: [PATCH 0366/2380] checkpatch: reduce MAINTAINERS update message frequency When files are being added/moved/deleted and a patch contains an update to the MAINTAINERS file, assume it's to update the MAINTAINERS file correctly and do not emit the "does MAINTAINERS need updating?" message. Reported by many people. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Reviewed-by: Markus Armbruster Reviewed-by: Thomas Huth Signed-off-by: Stefan Hajnoczi Message-id: 20180430124651.10340-6-stefanha@redhat.com (cherry picked from e0d975b1b439c4fef58fbc306c542c94f48bb849) Signed-off-by: Stefan Hajnoczi Reviewed-by: Thomas Huth Signed-off-by: Stefan Hajnoczi --- scripts/checkpatch.pl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 84bdf53af7..5506502cf4 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1390,6 +1390,12 @@ sub process { } } +# Check if MAINTAINERS is being updated. If so, there's probably no need to +# emit the "does MAINTAINERS need updating?" message on file add/move/delete + if ($line =~ /^\s*MAINTAINERS\s*\|/) { + $reported_maintainer_file = 1; + } + # Check for added, moved or deleted files if (!$reported_maintainer_file && !$in_commit_log && ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || From 0e07629297c9725e764b301dc03ab17cc1fb59e4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 9 May 2018 06:38:20 +0200 Subject: [PATCH 0367/2380] MAINTAINERS: Add trace-events and qemu-option-trace.texi to tracing section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "trace-events" and "qemu-option-trace.texi" files in the top directory are currently "unmaintained" according to scripts/get_maintainer.pl. They obviously belong to the Tracing section, so add an entry for them there. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-id: 1525840700-30635-1-git-send-email-thuth@redhat.com Signed-off-by: Stefan Hajnoczi --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 459e3594e1..e187b1f18f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1656,6 +1656,8 @@ Tracing M: Stefan Hajnoczi S: Maintained F: trace/ +F: trace-events +F: qemu-option-trace.texi F: scripts/tracetool.py F: scripts/tracetool/ F: docs/devel/tracing.txt From 7a35383afd66458ff11fa5c72a1599f3a24f3073 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 May 2018 18:10:56 +0100 Subject: [PATCH 0368/2380] hw/arm/iotkit.c: fix minor memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity (CID1390573) spots that we forgot to free the gpioname strings in a loop in the iotkit realize function. Correct the error. This isn't a significant leak, because this function only ever runs once. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Message-id: 20180427110137.19304-1-peter.maydell@linaro.org --- hw/arm/iotkit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index c5f0a5b98a..234185e8f7 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -517,6 +517,7 @@ static void iotkit_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i)); qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0, qdev_get_gpio_in(devs, 0)); + g_free(gpioname); } iotkit_forward_sec_resp_cfg(s); From 1839189bbf89889076aadf0c793c1b57977b28d7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 May 2018 18:10:56 +0100 Subject: [PATCH 0369/2380] softfloat: Handle default NaN mode after pickNaNMulAdd, not before MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is implementation defined whether a multiply-add of (0,inf,qnan) or (inf,0,qnan) raises InvalidaOperation or not, so we let the target-specific pickNaNMulAdd function handle this. This means that we must do the "return the default NaN in default NaN mode" check after the call, not before. Correct the ordering, and restore the comment from the old propagateFloat64MulAddNaN() that warned about this corner case. This fixes a regression from 2.11 for Arm guests where we would incorrectly fail to set the Invalid flag for these cases. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 20180504100547.14621-1-peter.maydell@linaro.org --- fpu/softfloat.c | 52 ++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 70e0c40a1c..8401b37bd4 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -602,34 +602,42 @@ static FloatParts pick_nan(FloatParts a, FloatParts b, float_status *s) static FloatParts pick_nan_muladd(FloatParts a, FloatParts b, FloatParts c, bool inf_zero, float_status *s) { + int which; + if (is_snan(a.cls) || is_snan(b.cls) || is_snan(c.cls)) { s->float_exception_flags |= float_flag_invalid; } - if (s->default_nan_mode) { - a.cls = float_class_dnan; - } else { - switch (pickNaNMulAdd(is_qnan(a.cls), is_snan(a.cls), - is_qnan(b.cls), is_snan(b.cls), - is_qnan(c.cls), is_snan(c.cls), - inf_zero, s)) { - case 0: - break; - case 1: - a = b; - break; - case 2: - a = c; - break; - case 3: - a.cls = float_class_dnan; - return a; - default: - g_assert_not_reached(); - } + which = pickNaNMulAdd(is_qnan(a.cls), is_snan(a.cls), + is_qnan(b.cls), is_snan(b.cls), + is_qnan(c.cls), is_snan(c.cls), + inf_zero, s); - a.cls = float_class_msnan; + if (s->default_nan_mode) { + /* Note that this check is after pickNaNMulAdd so that function + * has an opportunity to set the Invalid flag. + */ + a.cls = float_class_dnan; + return a; } + + switch (which) { + case 0: + break; + case 1: + a = b; + break; + case 2: + a = c; + break; + case 3: + a.cls = float_class_dnan; + return a; + default: + g_assert_not_reached(); + } + a.cls = float_class_msnan; + return a; } From 38aefb578dcf918359249ae5b29183255db809c2 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 May 2018 18:10:56 +0100 Subject: [PATCH 0370/2380] pc: simplify MachineClass::get_hotplug_handler handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default MachineClass::get_hotplug_handler is NULL and concrete board should set it to it's own handler. Considering there isn't any default handler, drop saving empty MachineClass::get_hotplug_handler in child class and make PC code consistent with spapr/s390x boards. We can bring this back when actual usecase surfaces and do it consistently across boards that use get_hotplug_handler(). Suggested-by: David Gibson Signed-off-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eduardo Habkost Message-id: 1525691524-32265-2-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell --- hw/i386/pc.c | 6 +----- include/hw/i386/pc.h | 8 -------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 868893d0a1..b2374febbb 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -2051,15 +2051,12 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev, static HotplugHandler *pc_get_hotpug_handler(MachineState *machine, DeviceState *dev) { - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine); - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { return HOTPLUG_HANDLER(machine); } - return pcmc->get_hotplug_handler ? - pcmc->get_hotplug_handler(machine, dev) : NULL; + return NULL; } static void @@ -2339,7 +2336,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); - pcmc->get_hotplug_handler = mc->get_hotplug_handler; pcmc->pci_enabled = true; pcmc->has_acpi_build = true; pcmc->rsdp_in_ram = true; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 2e834e6ded..2a98e3ad68 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -83,10 +83,6 @@ struct PCMachineState { /** * PCMachineClass: * - * Methods: - * - * @get_hotplug_handler: pointer to parent class callback @get_hotplug_handler - * * Compat fields: * * @enforce_aligned_dimm: check that DIMM's address/size is aligned by @@ -106,10 +102,6 @@ struct PCMachineClass { /*< public >*/ - /* Methods: */ - HotplugHandler *(*get_hotplug_handler)(MachineState *machine, - DeviceState *dev); - /* Device configuration: */ bool pci_enabled; bool kvmclock_enabled; From a3fc8396352e945f9d14cac0237ebf9d91745969 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 May 2018 18:10:56 +0100 Subject: [PATCH 0371/2380] platform-bus-device: use device plug callback instead of machine_done notifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit platform-bus were using machine_done notifier to get and map (assign irq/mmio resources) dynamically added sysbus devices after all '-device' options had been processed. That however creates non obvious dependencies on ordering of machine_done notifiers and requires carefull line juggling to keep it working. For example see comment above create_platform_bus() and 'straitforward' arm_load_kernel() had to converted to machine_done notifier and that lead to yet another machine_done notifier to keep it working arm_register_platform_bus_fdt_creator(). Instead of hiding resource assignment in platform-bus-device to magically initialize sysbus devices, use device plug callback and assign resources explicitly at board level at the moment each -device option is being processed. That adds a bunch of machine declaration boiler plate to e500plat board, similar to ARM/x86 but gets rid of hidden machine_done notifier and would allow to remove the dependent notifiers in ARM code simplifying it and making code flow easier to follow. Signed-off-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Acked-by: David Gibson Message-id: 1525691524-32265-3-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell --- hw/arm/sysbus-fdt.c | 3 --- hw/arm/virt.c | 31 +++++++++++++++++++++++++++++++ hw/core/platform-bus.c | 29 +++++------------------------ hw/ppc/e500.c | 38 +++++++++++++++++--------------------- hw/ppc/e500.h | 5 +++++ hw/ppc/e500plat.c | 31 +++++++++++++++++++++++++++++++ include/hw/arm/virt.h | 1 + include/hw/platform-bus.h | 4 ++-- 8 files changed, 92 insertions(+), 50 deletions(-) diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c index d68e3dcdbd..80ff70e1ed 100644 --- a/hw/arm/sysbus-fdt.c +++ b/hw/arm/sysbus-fdt.c @@ -506,9 +506,6 @@ static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params) dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE); pbus = PLATFORM_BUS_DEVICE(dev); - /* We can only create dt nodes for dynamic devices when they're ready */ - assert(pbus->done_gathering); - PlatformBusFDTData data = { .fdt = fdt, .irq_start = irq_start, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 11b9f599ca..1f54223f19 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1150,6 +1150,7 @@ static void create_platform_bus(VirtMachineState *vms, qemu_irq *pic) qdev_prop_set_uint32(dev, "mmio_size", platform_bus_params.platform_bus_size); qdev_init_nofail(dev); + vms->platform_bus_dev = dev; s = SYS_BUS_DEVICE(dev); for (i = 0; i < platform_bus_params.platform_bus_num_irqs; i++) { @@ -1627,9 +1628,33 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) return ms->possible_cpus; } +static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + + if (vms->platform_bus_dev) { + if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) { + platform_bus_link_device(PLATFORM_BUS_DEVICE(vms->platform_bus_dev), + SYS_BUS_DEVICE(dev)); + } + } +} + +static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, + DeviceState *dev) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) { + return HOTPLUG_HANDLER(machine); + } + + return NULL; +} + static void virt_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); mc->init = machvirt_init; /* Start max_cpus at the maximum QEMU supports. We'll further restrict @@ -1648,6 +1673,8 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = virt_cpu_index_to_props; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"); mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; + mc->get_hotplug_handler = virt_machine_get_hotplug_handler; + hc->plug = virt_machine_device_plug_cb; } static const TypeInfo virt_machine_info = { @@ -1657,6 +1684,10 @@ static const TypeInfo virt_machine_info = { .instance_size = sizeof(VirtMachineState), .class_size = sizeof(VirtMachineClass), .class_init = virt_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, }; static void machvirt_machine_init(void) diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index 33d32fbf22..807cb5ccda 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -103,7 +103,6 @@ static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus) { bitmap_zero(pbus->used_irqs, pbus->num_irqs); foreach_dynamic_sysbus_device(platform_bus_count_irqs, pbus); - pbus->done_gathering = true; } static void platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev, @@ -163,12 +162,11 @@ static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, } /* - * For each sysbus device, look for unassigned IRQ lines as well as - * unassociated MMIO regions. Connect them to the platform bus if available. + * Look for unassigned IRQ lines as well as unassociated MMIO regions. + * Connect them to the platform bus if available. */ -static void link_sysbus_device(SysBusDevice *sbdev, void *opaque) +void platform_bus_link_device(PlatformBusDevice *pbus, SysBusDevice *sbdev) { - PlatformBusDevice *pbus = opaque; int i; for (i = 0; sysbus_has_irq(sbdev, i); i++) { @@ -180,19 +178,6 @@ static void link_sysbus_device(SysBusDevice *sbdev, void *opaque) } } -static void platform_bus_init_notify(Notifier *notifier, void *data) -{ - PlatformBusDevice *pb = container_of(notifier, PlatformBusDevice, notifier); - - /* - * Generate a bitmap of used IRQ lines, as the user might have specified - * them on the command line. - */ - plaform_bus_refresh_irqs(pb); - - foreach_dynamic_sysbus_device(link_sysbus_device, pb); -} - static void platform_bus_realize(DeviceState *dev, Error **errp) { PlatformBusDevice *pbus; @@ -211,12 +196,8 @@ static void platform_bus_realize(DeviceState *dev, Error **errp) sysbus_init_irq(d, &pbus->irqs[i]); } - /* - * Register notifier that allows us to gather dangling devices once the - * machine is completely assembled - */ - pbus->notifier.notify = platform_bus_init_notify; - qemu_add_machine_init_done_notifier(&pbus->notifier); + /* some devices might be initialized before so update used IRQs map */ + plaform_bus_refresh_irqs(pbus); } static Property platform_bus_properties[] = { diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 748a8d213b..826053edc8 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -222,16 +222,15 @@ static void sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque) } } -static void platform_bus_create_devtree(const PPCE500MachineClass *pmc, +static void platform_bus_create_devtree(PPCE500MachineState *pms, void *fdt, const char *mpic) { + const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); gchar *node = g_strdup_printf("/platform@%"PRIx64, pmc->platform_bus_base); const char platcomp[] = "qemu,platform\0simple-bus"; uint64_t addr = pmc->platform_bus_base; uint64_t size = pmc->platform_bus_size; int irq_start = pmc->platform_bus_first_irq; - PlatformBusDevice *pbus; - DeviceState *dev; /* Create a /platform node that we can put all devices into */ @@ -246,22 +245,17 @@ static void platform_bus_create_devtree(const PPCE500MachineClass *pmc, qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic); - dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE); - pbus = PLATFORM_BUS_DEVICE(dev); + /* Create dt nodes for dynamic devices */ + PlatformDevtreeData data = { + .fdt = fdt, + .mpic = mpic, + .irq_start = irq_start, + .node = node, + .pbus = pms->pbus_dev, + }; - /* We can only create dt nodes for dynamic devices when they're ready */ - if (pbus->done_gathering) { - PlatformDevtreeData data = { - .fdt = fdt, - .mpic = mpic, - .irq_start = irq_start, - .node = node, - .pbus = pbus, - }; - - /* Loop through all dynamic sysbus devices and create nodes for them */ - foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data); - } + /* Loop through all dynamic sysbus devices and create nodes for them */ + foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data); g_free(node); } @@ -533,8 +527,8 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, } g_free(soc); - if (pmc->has_platform_bus) { - platform_bus_create_devtree(pmc, fdt, mpic); + if (pms->pbus_dev) { + platform_bus_create_devtree(pms, fdt, mpic); } g_free(mpic); @@ -953,8 +947,9 @@ void ppce500_init(MachineState *machine) qdev_prop_set_uint32(dev, "num_irqs", pmc->platform_bus_num_irqs); qdev_prop_set_uint32(dev, "mmio_size", pmc->platform_bus_size); qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); + pms->pbus_dev = PLATFORM_BUS_DEVICE(dev); + s = SYS_BUS_DEVICE(pms->pbus_dev); for (i = 0; i < pmc->platform_bus_num_irqs; i++) { int irqn = pmc->platform_bus_first_irq + i; sysbus_connect_irq(s, i, qdev_get_gpio_in(mpicdev, irqn)); @@ -1097,6 +1092,7 @@ static const TypeInfo ppce500_info = { .name = TYPE_PPCE500_MACHINE, .parent = TYPE_MACHINE, .abstract = true, + .instance_size = sizeof(PPCE500MachineState), .class_size = sizeof(PPCE500MachineClass), }; diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 621403bd24..3fd9f825ca 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -2,11 +2,16 @@ #define PPCE500_H #include "hw/boards.h" +#include "hw/platform-bus.h" typedef struct PPCE500MachineState { /*< private >*/ MachineState parent_obj; + /* points to instance of TYPE_PLATFORM_BUS_DEVICE if + * board supports dynamic sysbus devices + */ + PlatformBusDevice *pbus_dev; } PPCE500MachineState; typedef struct PPCE500MachineClass { diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index f69aadb666..1a469ba69f 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -43,13 +43,40 @@ static void e500plat_init(MachineState *machine) ppce500_init(machine); } +static void e500plat_machine_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + PPCE500MachineState *pms = PPCE500_MACHINE(hotplug_dev); + + if (pms->pbus_dev) { + if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) { + platform_bus_link_device(pms->pbus_dev, SYS_BUS_DEVICE(dev)); + } + } +} + +static +HotplugHandler *e500plat_machine_get_hotpug_handler(MachineState *machine, + DeviceState *dev) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) { + return HOTPLUG_HANDLER(machine); + } + + return NULL; +} + #define TYPE_E500PLAT_MACHINE MACHINE_TYPE_NAME("ppce500") static void e500plat_machine_class_init(ObjectClass *oc, void *data) { PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); + mc->get_hotplug_handler = e500plat_machine_get_hotpug_handler; + hc->plug = e500plat_machine_device_plug_cb; + pmc->pci_first_slot = 0x1; pmc->pci_nr_slots = PCI_SLOT_MAX - 1; pmc->fixup_devtree = e500plat_fixup_devtree; @@ -77,6 +104,10 @@ static const TypeInfo e500plat_info = { .name = TYPE_E500PLAT_MACHINE, .parent = TYPE_PPCE500_MACHINE, .class_init = e500plat_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static void e500plat_register_types(void) diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 886372cdbb..4ac7ef6a37 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -99,6 +99,7 @@ typedef struct { typedef struct { MachineState parent; Notifier machine_done; + DeviceState *platform_bus_dev; FWCfgState *fw_cfg; bool secure; bool highmem; diff --git a/include/hw/platform-bus.h b/include/hw/platform-bus.h index a00775cba6..19e20c57ce 100644 --- a/include/hw/platform-bus.h +++ b/include/hw/platform-bus.h @@ -37,8 +37,6 @@ typedef struct PlatformBusDevice PlatformBusDevice; struct PlatformBusDevice { /*< private >*/ SysBusDevice parent_obj; - Notifier notifier; - bool done_gathering; /*< public >*/ uint32_t mmio_size; @@ -54,4 +52,6 @@ int platform_bus_get_irqn(PlatformBusDevice *platform_bus, SysBusDevice *sbdev, hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev, int n); +void platform_bus_link_device(PlatformBusDevice *pbus, SysBusDevice *sbdev); + #endif /* HW_PLATFORM_BUS_H */ From 3b77f6c353a4bb8a66dab8a46668a7ccccc9de03 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 May 2018 18:10:56 +0100 Subject: [PATCH 0372/2380] arm/boot: split load_dtb() from arm_load_kernel() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit load_dtb() depends on arm_load_kernel() to figure out place in RAM where it should be loaded, but it's not required for arm_load_kernel() to work. Sometimes it's neccesary for devices added with -device/device_add to be enumerated in DTB as well, which's lead to [1] and surrounding commits to add 2 more machine_done notifiers with non obvious ordering to make dynamic sysbus devices initialization happen in the right order. However instead of moving whole arm_load_kernel() in to machine_done, it's sufficient to move only load_dtb() into virt_machine_done() notifier and remove ArmLoadKernelNotifier/ /PlatformBusFDTNotifierParams notifiers, which saves us ~90LOC and simplifies code flow quite a bit. Later would allow to consolidate DTB generation within one function for 'mach-virt' board and make it reentrant so it could generate updated DTB in device hotplug secenarios. While at it rename load_dtb() to arm_load_dtb() since it's public now. Add additional field skip_dtb_autoload to struct arm_boot_info to allow manual DTB load later in mach-virt and to avoid touching all other boards to explicitly call arm_load_dtb(). 1) (ac9d32e hw/arm/boot: arm_load_kernel implemented as a machine init done notifier) Signed-off-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Andrew Jones Message-id: 1525691524-32265-4-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell --- hw/arm/boot.c | 72 ++++++++++--------------------------- hw/arm/sysbus-fdt.c | 61 +++---------------------------- hw/arm/virt.c | 64 ++++++++++++++++----------------- include/hw/arm/arm.h | 45 +++++++++++++++++------ include/hw/arm/sysbus-fdt.h | 37 ++++--------------- 5 files changed, 94 insertions(+), 185 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 1e2be20731..9496f331a8 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -36,8 +36,8 @@ #define ARM64_TEXT_OFFSET_OFFSET 8 #define ARM64_MAGIC_OFFSET 56 -static AddressSpace *arm_boot_address_space(ARMCPU *cpu, - const struct arm_boot_info *info) +AddressSpace *arm_boot_address_space(ARMCPU *cpu, + const struct arm_boot_info *info) { /* Return the address space to use for bootloader reads and writes. * We prefer the secure address space if the CPU has it and we're @@ -486,29 +486,8 @@ static void fdt_add_psci_node(void *fdt) qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn); } -/** - * load_dtb() - load a device tree binary image into memory - * @addr: the address to load the image at - * @binfo: struct describing the boot environment - * @addr_limit: upper limit of the available memory area at @addr - * @as: address space to load image to - * - * Load a device tree supplied by the machine or by the user with the - * '-dtb' command line option, and put it at offset @addr in target - * memory. - * - * If @addr_limit contains a meaningful value (i.e., it is strictly greater - * than @addr), the device tree is only loaded if its size does not exceed - * the limit. - * - * Returns: the size of the device tree image on success, - * 0 if the image size exceeds the limit, - * -1 on errors. - * - * Note: Must not be called unless have_dtb(binfo) is true. - */ -static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, - hwaddr addr_limit, AddressSpace *as) +int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, + hwaddr addr_limit, AddressSpace *as) { void *fdt = NULL; int size, rc; @@ -935,7 +914,7 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, return size; } -static void arm_load_kernel_notify(Notifier *notifier, void *data) +void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) { CPUState *cs; int kernel_size; @@ -945,11 +924,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) int elf_machine; hwaddr entry; static const ARMInsnFixup *primary_loader; - ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier, - notifier, notifier); - ARMCPU *cpu = n->cpu; - struct arm_boot_info *info = - container_of(n, struct arm_boot_info, load_kernel_notifier); AddressSpace *as = arm_boot_address_space(cpu, info); /* The board code is not supposed to set secure_board_setup unless @@ -959,6 +933,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) assert(!(info->secure_board_setup && kvm_enabled())); info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); + info->dtb_limit = 0; /* Load the kernel. */ if (!info->kernel_filename || info->firmware_loaded) { @@ -968,9 +943,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) * the kernel is supposed to be loaded by the bootloader), copy the * DTB to the base of RAM for the bootloader to pick up. */ - if (load_dtb(info->loader_start, info, 0, as) < 0) { - exit(1); - } + info->dtb_start = info->loader_start; } if (info->kernel_filename) { @@ -1050,15 +1023,14 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) */ if (elf_low_addr > info->loader_start || elf_high_addr < info->loader_start) { - /* Pass elf_low_addr as address limit to load_dtb if it may be + /* Set elf_low_addr as address limit for arm_load_dtb if it may be * pointing into RAM, otherwise pass '0' (no limit) */ if (elf_low_addr < info->loader_start) { elf_low_addr = 0; } - if (load_dtb(info->loader_start, info, elf_low_addr, as) < 0) { - exit(1); - } + info->dtb_start = info->loader_start; + info->dtb_limit = elf_low_addr; } } entry = elf_entry; @@ -1116,7 +1088,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) */ if (have_dtb(info)) { hwaddr align; - hwaddr dtb_start; if (elf_machine == EM_AARCH64) { /* @@ -1136,11 +1107,9 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) } /* Place the DTB after the initrd in memory with alignment. */ - dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, align); - if (load_dtb(dtb_start, info, 0, as) < 0) { - exit(1); - } - fixupcontext[FIXUP_ARGPTR] = dtb_start; + info->dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, + align); + fixupcontext[FIXUP_ARGPTR] = info->dtb_start; } else { fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR; if (info->ram_size >= (1ULL << 32)) { @@ -1173,15 +1142,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { ARM_CPU(cs)->env.boot_info = info; } -} - -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 @@ -1191,6 +1151,12 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); } + + if (!info->skip_dtb_autoload && have_dtb(info)) { + if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as) < 0) { + exit(1); + } + } } static const TypeInfo arm_linux_boot_if_info = { diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c index 80ff70e1ed..e4c492ea44 100644 --- a/hw/arm/sysbus-fdt.c +++ b/hw/arm/sysbus-fdt.c @@ -49,15 +49,6 @@ typedef struct PlatformBusFDTData { PlatformBusDevice *pbus; } PlatformBusFDTData; -/* - * struct used when calling the machine init done notifier - * that constructs the fdt nodes of platform bus devices - */ -typedef struct PlatformBusFDTNotifierParams { - Notifier notifier; - ARMPlatformBusFDTParams *fdt_params; -} PlatformBusFDTNotifierParams; - /* struct that associates a device type name and a node creation function */ typedef struct NodeCreationPair { const char *typename; @@ -453,42 +444,17 @@ static void add_fdt_node(SysBusDevice *sbdev, void *opaque) exit(1); } -/** - * add_all_platform_bus_fdt_nodes - create all the platform bus nodes - * - * builds the parent platform bus node and all the nodes of dynamic - * sysbus devices attached to it. - */ -static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params) +void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr, + hwaddr bus_size, int irq_start) { const char platcomp[] = "qemu,platform\0simple-bus"; PlatformBusDevice *pbus; DeviceState *dev; gchar *node; - uint64_t addr, size; - int irq_start, dtb_size; - struct arm_boot_info *info = fdt_params->binfo; - const ARMPlatformBusSystemParams *params = fdt_params->system_params; - const char *intc = fdt_params->intc; - void *fdt = info->get_dtb(info, &dtb_size); - - /* - * If the user provided a dtb, we assume the dynamic sysbus nodes - * already are integrated there. This corresponds to a use case where - * the dynamic sysbus nodes are complex and their generation is not yet - * supported. In that case the user can take charge of the guest dt - * while qemu takes charge of the qom stuff. - */ - if (info->dtb_filename) { - return; - } assert(fdt); - node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base); - addr = params->platform_bus_base; - size = params->platform_bus_size; - irq_start = params->platform_bus_first_irq; + node = g_strdup_printf("/platform@%"PRIx64, addr); /* Create a /platform node that we can put all devices into */ qemu_fdt_add_subnode(fdt, node); @@ -499,7 +465,7 @@ static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params) */ qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1); qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1); - qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size); + qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, bus_size); qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc); @@ -518,22 +484,3 @@ static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params) g_free(node); } - -static void platform_bus_fdt_notify(Notifier *notifier, void *data) -{ - PlatformBusFDTNotifierParams *p = DO_UPCAST(PlatformBusFDTNotifierParams, - notifier, notifier); - - add_all_platform_bus_fdt_nodes(p->fdt_params); - g_free(p->fdt_params); - g_free(p); -} - -void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params) -{ - PlatformBusFDTNotifierParams *p = g_new(PlatformBusFDTNotifierParams, 1); - - p->fdt_params = fdt_params; - p->notifier.notify = platform_bus_fdt_notify; - qemu_add_machine_init_done_notifier(&p->notifier); -} diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 1f54223f19..6710be226f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -94,8 +94,6 @@ #define PLATFORM_BUS_NUM_IRQS 64 -static ARMPlatformBusSystemParams platform_bus_params; - /* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means * RAM can go up to the 256GB mark, leaving 256GB of the physical * address space unallocated and free for future use between 256G and 512G. @@ -1126,40 +1124,23 @@ static void create_platform_bus(VirtMachineState *vms, qemu_irq *pic) DeviceState *dev; SysBusDevice *s; int i; - ARMPlatformBusFDTParams *fdt_params = g_new(ARMPlatformBusFDTParams, 1); MemoryRegion *sysmem = get_system_memory(); - platform_bus_params.platform_bus_base = vms->memmap[VIRT_PLATFORM_BUS].base; - platform_bus_params.platform_bus_size = vms->memmap[VIRT_PLATFORM_BUS].size; - platform_bus_params.platform_bus_first_irq = vms->irqmap[VIRT_PLATFORM_BUS]; - platform_bus_params.platform_bus_num_irqs = PLATFORM_BUS_NUM_IRQS; - - fdt_params->system_params = &platform_bus_params; - fdt_params->binfo = &vms->bootinfo; - fdt_params->intc = "/intc"; - /* - * register a machine init done notifier that creates the device tree - * nodes of the platform bus and its children dynamic sysbus devices - */ - arm_register_platform_bus_fdt_creator(fdt_params); - dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE); dev->id = TYPE_PLATFORM_BUS_DEVICE; - qdev_prop_set_uint32(dev, "num_irqs", - platform_bus_params.platform_bus_num_irqs); - qdev_prop_set_uint32(dev, "mmio_size", - platform_bus_params.platform_bus_size); + qdev_prop_set_uint32(dev, "num_irqs", PLATFORM_BUS_NUM_IRQS); + qdev_prop_set_uint32(dev, "mmio_size", vms->memmap[VIRT_PLATFORM_BUS].size); qdev_init_nofail(dev); vms->platform_bus_dev = dev; - s = SYS_BUS_DEVICE(dev); - for (i = 0; i < platform_bus_params.platform_bus_num_irqs; i++) { - int irqn = platform_bus_params.platform_bus_first_irq + i; + s = SYS_BUS_DEVICE(dev); + for (i = 0; i < PLATFORM_BUS_NUM_IRQS; i++) { + int irqn = vms->irqmap[VIRT_PLATFORM_BUS] + i; sysbus_connect_irq(s, i, pic[irqn]); } memory_region_add_subregion(sysmem, - platform_bus_params.platform_bus_base, + vms->memmap[VIRT_PLATFORM_BUS].base, sysbus_mmio_get_region(s, 0)); } @@ -1230,6 +1211,26 @@ void virt_machine_done(Notifier *notifier, void *data) { VirtMachineState *vms = container_of(notifier, VirtMachineState, machine_done); + ARMCPU *cpu = ARM_CPU(first_cpu); + struct arm_boot_info *info = &vms->bootinfo; + AddressSpace *as = arm_boot_address_space(cpu, info); + + /* + * If the user provided a dtb, we assume the dynamic sysbus nodes + * already are integrated there. This corresponds to a use case where + * the dynamic sysbus nodes are complex and their generation is not yet + * supported. In that case the user can take charge of the guest dt + * while qemu takes charge of the qom stuff. + */ + if (info->dtb_filename == NULL) { + platform_bus_add_all_fdt_nodes(vms->fdt, "/intc", + vms->memmap[VIRT_PLATFORM_BUS].base, + vms->memmap[VIRT_PLATFORM_BUS].size, + vms->irqmap[VIRT_PLATFORM_BUS]); + } + if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as) < 0) { + exit(1); + } virt_acpi_setup(vms); virt_build_smbios(vms); @@ -1457,8 +1458,7 @@ static void machvirt_init(MachineState *machine) vms->fw_cfg = create_fw_cfg(vms, &address_space_memory); rom_set_fw(vms->fw_cfg); - vms->machine_done.notify = virt_machine_done; - qemu_add_machine_init_done_notifier(&vms->machine_done); + create_platform_bus(vms, pic); vms->bootinfo.ram_size = machine->ram_size; vms->bootinfo.kernel_filename = machine->kernel_filename; @@ -1468,16 +1468,12 @@ static void machvirt_init(MachineState *machine) vms->bootinfo.board_id = -1; vms->bootinfo.loader_start = vms->memmap[VIRT_MEM].base; vms->bootinfo.get_dtb = machvirt_dtb; + vms->bootinfo.skip_dtb_autoload = true; vms->bootinfo.firmware_loaded = firmware_loaded; arm_load_kernel(ARM_CPU(first_cpu), &vms->bootinfo); - /* - * arm_load_kernel machine init done notifier registration must - * happen before the platform_bus_create call. In this latter, - * another notifier is registered which adds platform bus nodes. - * Notifiers are executed in registration reverse order. - */ - create_platform_bus(vms, pic); + vms->machine_done.notify = virt_machine_done; + qemu_add_machine_init_done_notifier(&vms->machine_done); } static bool virt_get_secure(Object *obj, Error **errp) diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index ce769bde6a..70fa2287e2 100644 --- a/include/hw/arm/arm.h +++ b/include/hw/arm/arm.h @@ -39,15 +39,6 @@ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, */ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size); -/* - * struct used as a parameter of the arm_load_kernel machine init - * done notifier - */ -typedef struct { - Notifier notifier; /* actual notifier */ - ARMCPU *cpu; /* handle to the first cpu object */ -} ArmLoadKernelNotifier; - /* arm_boot.c */ struct arm_boot_info { uint64_t ram_size; @@ -56,6 +47,13 @@ struct arm_boot_info { const char *initrd_filename; const char *dtb_filename; hwaddr loader_start; + hwaddr dtb_start; + hwaddr dtb_limit; + /* If set to True, arm_load_kernel() will not load DTB. + * It allows board to load DTB manually later. + * (default: False) + */ + bool skip_dtb_autoload; /* multicore boards that use the default secondary core boot functions * need to put the address of the secondary boot code, the boot reg, * and the GIC address in the next 3 values, respectively. boards that @@ -94,8 +92,6 @@ struct arm_boot_info { * the user it should implement this hook. */ void (*modify_dtb)(const struct arm_boot_info *info, void *fdt); - /* machine init done notifier executing arm_load_dtb */ - ArmLoadKernelNotifier load_kernel_notifier; /* Used internally by arm_boot.c */ int is_linux; hwaddr initrd_start; @@ -143,6 +139,33 @@ struct arm_boot_info { */ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info); +AddressSpace *arm_boot_address_space(ARMCPU *cpu, + const struct arm_boot_info *info); + +/** + * arm_load_dtb() - load a device tree binary image into memory + * @addr: the address to load the image at + * @binfo: struct describing the boot environment + * @addr_limit: upper limit of the available memory area at @addr + * @as: address space to load image to + * + * Load a device tree supplied by the machine or by the user with the + * '-dtb' command line option, and put it at offset @addr in target + * memory. + * + * If @addr_limit contains a meaningful value (i.e., it is strictly greater + * than @addr), the device tree is only loaded if its size does not exceed + * the limit. + * + * Returns: the size of the device tree image on success, + * 0 if the image size exceeds the limit, + * -1 on errors. + * + * Note: Must not be called unless have_dtb(binfo) is true. + */ +int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, + hwaddr addr_limit, AddressSpace *as); + /* Write a secure board setup routine with a dummy handler for SMCs */ void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, const struct arm_boot_info *info, diff --git a/include/hw/arm/sysbus-fdt.h b/include/hw/arm/sysbus-fdt.h index e15bb81807..340c382cdd 100644 --- a/include/hw/arm/sysbus-fdt.h +++ b/include/hw/arm/sysbus-fdt.h @@ -24,37 +24,14 @@ #ifndef HW_ARM_SYSBUS_FDT_H #define HW_ARM_SYSBUS_FDT_H -#include "hw/arm/arm.h" -#include "qemu-common.h" -#include "hw/sysbus.h" - -/* - * struct that contains dimensioning parameters of the platform bus - */ -typedef struct { - hwaddr platform_bus_base; /* start address of the bus */ - hwaddr platform_bus_size; /* size of the bus */ - int platform_bus_first_irq; /* first hwirq assigned to the bus */ - int platform_bus_num_irqs; /* number of hwirq assigned to the bus */ -} ARMPlatformBusSystemParams; - -/* - * struct that contains all relevant info to build the fdt nodes of - * platform bus and attached dynamic sysbus devices - * in the future might be augmented with additional info - * such as PHY, CLK handles ... - */ -typedef struct { - const ARMPlatformBusSystemParams *system_params; - struct arm_boot_info *binfo; - const char *intc; /* parent interrupt controller name */ -} ARMPlatformBusFDTParams; +#include "exec/hwaddr.h" /** - * arm_register_platform_bus_fdt_creator - register a machine init done - * notifier that creates the device tree nodes of the platform bus and - * associated dynamic sysbus devices + * platform_bus_add_all_fdt_nodes - create all the platform bus nodes + * + * builds the parent platform bus node and all the nodes of dynamic + * sysbus devices attached to it. */ -void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params); - +void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr, + hwaddr bus_size, int irq_start); #endif From debbdc00182eb83db5d8acc241feb37c81bc63ec Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 May 2018 18:10:56 +0100 Subject: [PATCH 0373/2380] make sure that we aren't overwriting mc->get_hotplug_handler by accident MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Eduardo Habkost Signed-off-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Message-id: 1525691524-32265-5-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 1 + hw/i386/pc.c | 1 + hw/ppc/e500plat.c | 1 + hw/ppc/spapr.c | 1 + hw/s390x/s390-virtio-ccw.c | 1 + 5 files changed, 5 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 6710be226f..a3a28e20e8 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1669,6 +1669,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = virt_cpu_index_to_props; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"); mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; + assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = virt_machine_get_hotplug_handler; hc->plug = virt_machine_device_plug_cb; } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b2374febbb..d768930d02 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -2350,6 +2350,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->acpi_data_size = 0x20000 + 0x8000; pcmc->save_tsc_khz = true; pcmc->linuxboot_dma_enabled = true; + assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = pc_get_hotpug_handler; mc->cpu_index_to_instance_props = pc_cpu_index_to_props; mc->get_default_cpu_node_id = pc_get_default_cpu_node_id; diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 1a469ba69f..d8e3f2066e 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -74,6 +74,7 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); + assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = e500plat_machine_get_hotpug_handler; hc->plug = e500plat_machine_device_plug_cb; diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index a1abcba6ad..ebf30dd60b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3980,6 +3980,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->kvm_type = spapr_kvm_type; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_SPAPR_PCI_HOST_BRIDGE); mc->pci_allow_0_address = true; + assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = spapr_get_hotplug_handler; hc->pre_plug = spapr_machine_device_pre_plug; hc->plug = spapr_machine_device_plug; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 100dfdc96d..5796e24bd8 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -491,6 +491,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->no_sdcard = 1; mc->max_cpus = S390_MAX_CPUS; mc->has_hotpluggable_cpus = true; + assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = s390_get_hotplug_handler; mc->cpu_index_to_instance_props = s390_cpu_index_to_props; mc->possible_cpu_arch_ids = s390_possible_cpu_arch_ids; From cd95fc28fb6d8afced0d70ce52c294d0761a9daa Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0374/2380] atomic.h: Work around gcc spurious "unused value" warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some versions of gcc produce a spurious warning if the result of __atomic_compare_echange_n() is not used and the type involved is a signed 8 bit value: error: value computed is not used [-Werror=unused-value] This has been seen on at least gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 Work around this by using an explicit cast to void to indicate that we don't care about the return value. We don't currently use our atomic_cmpxchg() macro on any signed 8 bit types, but the upcoming support for the Arm v8.1-Atomics will require it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé --- include/qemu/atomic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index d73c9e14d7..9ed39effd3 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -187,7 +187,7 @@ /* Returns the eventual value, failed or not */ #define atomic_cmpxchg__nocheck(ptr, old, new) ({ \ typeof_strip_qual(*ptr) _old = (old); \ - __atomic_compare_exchange_n(ptr, &_old, new, false, \ + (void)__atomic_compare_exchange_n(ptr, &_old, new, false, \ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ _old; \ }) From b87fb8cd9f9a0ba599ff79e7bf03222da02e5724 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0375/2380] tcg: Introduce helpers for integer min/max These operations are re-invented by several targets so far. Several supported hosts have insns for these, so place the expanders out-of-line for a future introduction of tcg opcodes. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180508151437.4232-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- tcg/tcg-op.c | 40 ++++++++++++++++++++++++++++++++++++++++ tcg/tcg-op.h | 16 ++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 34b96d68f3..5b82c3be8d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1033,6 +1033,26 @@ void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) } } +void tcg_gen_smin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b); +} + +void tcg_gen_umin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, a, b); +} + +void tcg_gen_smax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, b, a); +} + +void tcg_gen_umax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a); +} + /* 64-bit ops */ #if TCG_TARGET_REG_BITS == 32 @@ -2438,6 +2458,26 @@ void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) tcg_temp_free_i64(t2); } +void tcg_gen_smin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, a, b); +} + +void tcg_gen_umin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, a, b); +} + +void tcg_gen_smax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, b, a); +} + +void tcg_gen_umax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a); +} + /* Size changing operations. */ void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg) diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 5d2c91a1b6..0451e2752e 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -324,6 +324,10 @@ void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg); void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg); void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg); void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_smin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_umax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); static inline void tcg_gen_discard_i32(TCGv_i32 arg) { @@ -517,6 +521,10 @@ void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg); void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg); void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg); void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_smin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_umax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); #if TCG_TARGET_REG_BITS == 64 static inline void tcg_gen_discard_i64(TCGv_i64 arg) @@ -1025,6 +1033,10 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_mulu2_tl tcg_gen_mulu2_i64 #define tcg_gen_muls2_tl tcg_gen_muls2_i64 #define tcg_gen_mulsu2_tl tcg_gen_mulsu2_i64 +#define tcg_gen_smin_tl tcg_gen_smin_i64 +#define tcg_gen_umin_tl tcg_gen_umin_i64 +#define tcg_gen_smax_tl tcg_gen_smax_i64 +#define tcg_gen_umax_tl tcg_gen_umax_i64 #define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i64 #define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i64 #define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i64 @@ -1123,6 +1135,10 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_mulu2_tl tcg_gen_mulu2_i32 #define tcg_gen_muls2_tl tcg_gen_muls2_i32 #define tcg_gen_mulsu2_tl tcg_gen_mulsu2_i32 +#define tcg_gen_smin_tl tcg_gen_smin_i32 +#define tcg_gen_umin_tl tcg_gen_umin_i32 +#define tcg_gen_smax_tl tcg_gen_smax_i32 +#define tcg_gen_umax_tl tcg_gen_umax_i32 #define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i32 #define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i32 #define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i32 From ecb8ab8d71aab770555a6972428b711400a27248 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0376/2380] target/arm: Use new min/max expanders The generic expanders replace nearly identical code in the translator. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180508151437.4232-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 46 ++++++++++++-------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 6d49f30b4a..60d104cc8a 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -6021,15 +6021,18 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) tcg_gen_add_i64(tcg_res, tcg_res, tcg_elt); break; case 0x0a: /* SMAXV / UMAXV */ - tcg_gen_movcond_i64(is_u ? TCG_COND_GEU : TCG_COND_GE, - tcg_res, - tcg_res, tcg_elt, tcg_res, tcg_elt); + if (is_u) { + tcg_gen_umax_i64(tcg_res, tcg_res, tcg_elt); + } else { + tcg_gen_smax_i64(tcg_res, tcg_res, tcg_elt); + } break; case 0x1a: /* SMINV / UMINV */ - tcg_gen_movcond_i64(is_u ? TCG_COND_LEU : TCG_COND_LE, - tcg_res, - tcg_res, tcg_elt, tcg_res, tcg_elt); - break; + if (is_u) { + tcg_gen_umin_i64(tcg_res, tcg_res, tcg_elt); + } else { + tcg_gen_smin_i64(tcg_res, tcg_res, tcg_elt); + } break; default: g_assert_not_reached(); @@ -9927,27 +9930,6 @@ static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) } } -/* Helper functions for 32 bit comparisons */ -static void gen_max_s32(TCGv_i32 res, TCGv_i32 op1, TCGv_i32 op2) -{ - tcg_gen_movcond_i32(TCG_COND_GE, res, op1, op2, op1, op2); -} - -static void gen_max_u32(TCGv_i32 res, TCGv_i32 op1, TCGv_i32 op2) -{ - tcg_gen_movcond_i32(TCG_COND_GEU, res, op1, op2, op1, op2); -} - -static void gen_min_s32(TCGv_i32 res, TCGv_i32 op1, TCGv_i32 op2) -{ - tcg_gen_movcond_i32(TCG_COND_LE, res, op1, op2, op1, op2); -} - -static void gen_min_u32(TCGv_i32 res, TCGv_i32 op1, TCGv_i32 op2) -{ - tcg_gen_movcond_i32(TCG_COND_LEU, res, op1, op2, op1, op2); -} - /* Pairwise op subgroup of C3.6.16. * * This is called directly or via the handle_3same_float for float pairwise @@ -10047,7 +10029,7 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, static NeonGenTwoOpFn * const fns[3][2] = { { gen_helper_neon_pmax_s8, gen_helper_neon_pmax_u8 }, { gen_helper_neon_pmax_s16, gen_helper_neon_pmax_u16 }, - { gen_max_s32, gen_max_u32 }, + { tcg_gen_smax_i32, tcg_gen_umax_i32 }, }; genfn = fns[size][u]; break; @@ -10057,7 +10039,7 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, static NeonGenTwoOpFn * const fns[3][2] = { { gen_helper_neon_pmin_s8, gen_helper_neon_pmin_u8 }, { gen_helper_neon_pmin_s16, gen_helper_neon_pmin_u16 }, - { gen_min_s32, gen_min_u32 }, + { tcg_gen_smin_i32, tcg_gen_umin_i32 }, }; genfn = fns[size][u]; break; @@ -10512,7 +10494,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) static NeonGenTwoOpFn * const fns[3][2] = { { gen_helper_neon_max_s8, gen_helper_neon_max_u8 }, { gen_helper_neon_max_s16, gen_helper_neon_max_u16 }, - { gen_max_s32, gen_max_u32 }, + { tcg_gen_smax_i32, tcg_gen_umax_i32 }, }; genfn = fns[size][u]; break; @@ -10523,7 +10505,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) static NeonGenTwoOpFn * const fns[3][2] = { { gen_helper_neon_min_s8, gen_helper_neon_min_u8 }, { gen_helper_neon_min_s16, gen_helper_neon_min_u16 }, - { gen_min_s32, gen_min_u32 }, + { tcg_gen_smin_i32, tcg_gen_umin_i32 }, }; genfn = fns[size][u]; break; From 426afc3bd94a2c06b06652010d0802d84809b53b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0377/2380] target/xtensa: Use new min/max expanders The generic expanders replace nearly identical code in the translator. Acked-by: Max Filippov Signed-off-by: Richard Henderson Message-id: 20180508151437.4232-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/xtensa/translate.c | 50 ++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 4f6d03059f..bad5cdb009 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -1527,10 +1527,8 @@ static void translate_clamps(DisasContext *dc, const uint32_t arg[], TCGv_i32 tmp1 = tcg_const_i32(-1u << arg[2]); TCGv_i32 tmp2 = tcg_const_i32((1 << arg[2]) - 1); - tcg_gen_movcond_i32(TCG_COND_GT, tmp1, - cpu_R[arg[1]], tmp1, cpu_R[arg[1]], tmp1); - tcg_gen_movcond_i32(TCG_COND_LT, cpu_R[arg[0]], - tmp1, tmp2, tmp1, tmp2); + tcg_gen_smax_i32(tmp1, tmp1, cpu_R[arg[1]]); + tcg_gen_smin_i32(cpu_R[arg[0]], tmp1, tmp2); tcg_temp_free(tmp1); tcg_temp_free(tmp2); } @@ -1855,13 +1853,35 @@ static void translate_memw(DisasContext *dc, const uint32_t arg[], tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); } -static void translate_minmax(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) +static void translate_smin(DisasContext *dc, const uint32_t arg[], + const uint32_t par[]) { if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_movcond_i32(par[0], cpu_R[arg[0]], - cpu_R[arg[1]], cpu_R[arg[2]], - cpu_R[arg[1]], cpu_R[arg[2]]); + tcg_gen_smin_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); + } +} + +static void translate_umin(DisasContext *dc, const uint32_t arg[], + const uint32_t par[]) +{ + if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { + tcg_gen_umin_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); + } +} + +static void translate_smax(DisasContext *dc, const uint32_t arg[], + const uint32_t par[]) +{ + if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { + tcg_gen_smax_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); + } +} + +static void translate_umax(DisasContext *dc, const uint32_t arg[], + const uint32_t par[]) +{ + if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { + tcg_gen_umax_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); } } @@ -2984,23 +3004,19 @@ static const XtensaOpcodeOps core_ops[] = { .par = (const uint32_t[]){TCG_COND_NE}, }, { .name = "max", - .translate = translate_minmax, - .par = (const uint32_t[]){TCG_COND_GE}, + .translate = translate_smax, }, { .name = "maxu", - .translate = translate_minmax, - .par = (const uint32_t[]){TCG_COND_GEU}, + .translate = translate_umax, }, { .name = "memw", .translate = translate_memw, }, { .name = "min", - .translate = translate_minmax, - .par = (const uint32_t[]){TCG_COND_LT}, + .translate = translate_smin, }, { .name = "minu", - .translate = translate_minmax, - .par = (const uint32_t[]){TCG_COND_LTU}, + .translate = translate_umin, }, { .name = "mov", .translate = translate_mov, From 5507c2bf35aa6b4705939349184e71afd5e058b2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0378/2380] tcg: Introduce atomic helpers for integer min/max Given that this atomic operation will be used by both risc-v and aarch64, let's not duplicate code across the two targets. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180508151437.4232-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- accel/tcg/atomic_template.h | 71 +++++++++++++++++++++++++++++++++++++ accel/tcg/tcg-runtime.h | 8 +++++ tcg/tcg-op.c | 8 +++++ tcg/tcg-op.h | 34 ++++++++++++++++++ tcg/tcg.h | 8 +++++ 5 files changed, 129 insertions(+) diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index e022df4571..2489dd3ec1 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -25,18 +25,22 @@ #elif DATA_SIZE == 8 # define SUFFIX q # define DATA_TYPE uint64_t +# define SDATA_TYPE int64_t # define BSWAP bswap64 #elif DATA_SIZE == 4 # define SUFFIX l # define DATA_TYPE uint32_t +# define SDATA_TYPE int32_t # define BSWAP bswap32 #elif DATA_SIZE == 2 # define SUFFIX w # define DATA_TYPE uint16_t +# define SDATA_TYPE int16_t # define BSWAP bswap16 #elif DATA_SIZE == 1 # define SUFFIX b # define DATA_TYPE uint8_t +# define SDATA_TYPE int8_t # define BSWAP #else # error unsupported data size @@ -118,6 +122,39 @@ GEN_ATOMIC_HELPER(or_fetch) GEN_ATOMIC_HELPER(xor_fetch) #undef GEN_ATOMIC_HELPER + +/* These helpers are, as a whole, full barriers. Within the helper, + * the leading barrier is explicit and the trailing barrier is within + * cmpxchg primitive. + */ +#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ + ABI_TYPE xval EXTRA_ARGS) \ +{ \ + ATOMIC_MMU_DECLS; \ + XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ + XDATA_TYPE cmp, old, new, val = xval; \ + smp_mb(); \ + cmp = atomic_read__nocheck(haddr); \ + do { \ + old = cmp; new = FN(old, val); \ + cmp = atomic_cmpxchg__nocheck(haddr, old, new); \ + } while (cmp != old); \ + ATOMIC_MMU_CLEANUP; \ + return RET; \ +} + +GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old) + +GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) + +#undef GEN_ATOMIC_HELPER_FN #endif /* DATA SIZE >= 16 */ #undef END @@ -233,6 +270,39 @@ ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr, ldo = ldn; } } + +/* These helpers are, as a whole, full barriers. Within the helper, + * the leading barrier is explicit and the trailing barrier is within + * cmpxchg primitive. + */ +#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ + ABI_TYPE xval EXTRA_ARGS) \ +{ \ + ATOMIC_MMU_DECLS; \ + XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ + XDATA_TYPE ldo, ldn, old, new, val = xval; \ + smp_mb(); \ + ldn = atomic_read__nocheck(haddr); \ + do { \ + ldo = ldn; old = BSWAP(ldo); new = FN(old, val); \ + ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \ + } while (ldo != ldn); \ + ATOMIC_MMU_CLEANUP; \ + return RET; \ +} + +GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old) + +GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) + +#undef GEN_ATOMIC_HELPER_FN #endif /* DATA_SIZE >= 16 */ #undef END @@ -241,5 +311,6 @@ ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr, #undef BSWAP #undef ABI_TYPE #undef DATA_TYPE +#undef SDATA_TYPE #undef SUFFIX #undef DATA_SIZE diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index 2536959a18..1bd39d136d 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -125,11 +125,19 @@ GEN_ATOMIC_HELPERS(fetch_add) GEN_ATOMIC_HELPERS(fetch_and) GEN_ATOMIC_HELPERS(fetch_or) GEN_ATOMIC_HELPERS(fetch_xor) +GEN_ATOMIC_HELPERS(fetch_smin) +GEN_ATOMIC_HELPERS(fetch_umin) +GEN_ATOMIC_HELPERS(fetch_smax) +GEN_ATOMIC_HELPERS(fetch_umax) GEN_ATOMIC_HELPERS(add_fetch) GEN_ATOMIC_HELPERS(and_fetch) GEN_ATOMIC_HELPERS(or_fetch) GEN_ATOMIC_HELPERS(xor_fetch) +GEN_ATOMIC_HELPERS(smin_fetch) +GEN_ATOMIC_HELPERS(umin_fetch) +GEN_ATOMIC_HELPERS(smax_fetch) +GEN_ATOMIC_HELPERS(umax_fetch) GEN_ATOMIC_HELPERS(xchg) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 5b82c3be8d..6a914654f5 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -3051,11 +3051,19 @@ GEN_ATOMIC_HELPER(fetch_add, add, 0) GEN_ATOMIC_HELPER(fetch_and, and, 0) GEN_ATOMIC_HELPER(fetch_or, or, 0) GEN_ATOMIC_HELPER(fetch_xor, xor, 0) +GEN_ATOMIC_HELPER(fetch_smin, smin, 0) +GEN_ATOMIC_HELPER(fetch_umin, umin, 0) +GEN_ATOMIC_HELPER(fetch_smax, smax, 0) +GEN_ATOMIC_HELPER(fetch_umax, umax, 0) GEN_ATOMIC_HELPER(add_fetch, add, 1) GEN_ATOMIC_HELPER(and_fetch, and, 1) GEN_ATOMIC_HELPER(or_fetch, or, 1) GEN_ATOMIC_HELPER(xor_fetch, xor, 1) +GEN_ATOMIC_HELPER(smin_fetch, smin, 1) +GEN_ATOMIC_HELPER(umin_fetch, umin, 1) +GEN_ATOMIC_HELPER(smax_fetch, smax, 1) +GEN_ATOMIC_HELPER(umax_fetch, umax, 1) static void tcg_gen_mov2_i32(TCGv_i32 r, TCGv_i32 a, TCGv_i32 b) { diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 0451e2752e..04eb3e9e17 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -898,6 +898,7 @@ void tcg_gen_atomic_cmpxchg_i64(TCGv_i64, TCGv, TCGv_i64, TCGv_i64, void tcg_gen_atomic_xchg_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_xchg_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); + void tcg_gen_atomic_fetch_add_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_fetch_add_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); void tcg_gen_atomic_fetch_and_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); @@ -906,6 +907,15 @@ void tcg_gen_atomic_fetch_or_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_fetch_or_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); void tcg_gen_atomic_fetch_xor_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_fetch_xor_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_smin_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_smin_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_umin_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_umin_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_smax_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_smax_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_umax_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_umax_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); + void tcg_gen_atomic_add_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_add_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); void tcg_gen_atomic_and_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); @@ -914,6 +924,14 @@ void tcg_gen_atomic_or_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_or_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); void tcg_gen_atomic_xor_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_xor_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_smin_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_smin_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_umin_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_umin_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_smax_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_smax_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_umax_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_umax_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); void tcg_gen_mov_vec(TCGv_vec, TCGv_vec); void tcg_gen_dup_i32_vec(unsigned vece, TCGv_vec, TCGv_i32); @@ -1043,10 +1061,18 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_atomic_fetch_and_tl tcg_gen_atomic_fetch_and_i64 #define tcg_gen_atomic_fetch_or_tl tcg_gen_atomic_fetch_or_i64 #define tcg_gen_atomic_fetch_xor_tl tcg_gen_atomic_fetch_xor_i64 +#define tcg_gen_atomic_fetch_smin_tl tcg_gen_atomic_fetch_smin_i64 +#define tcg_gen_atomic_fetch_umin_tl tcg_gen_atomic_fetch_umin_i64 +#define tcg_gen_atomic_fetch_smax_tl tcg_gen_atomic_fetch_smax_i64 +#define tcg_gen_atomic_fetch_umax_tl tcg_gen_atomic_fetch_umax_i64 #define tcg_gen_atomic_add_fetch_tl tcg_gen_atomic_add_fetch_i64 #define tcg_gen_atomic_and_fetch_tl tcg_gen_atomic_and_fetch_i64 #define tcg_gen_atomic_or_fetch_tl tcg_gen_atomic_or_fetch_i64 #define tcg_gen_atomic_xor_fetch_tl tcg_gen_atomic_xor_fetch_i64 +#define tcg_gen_atomic_smin_fetch_tl tcg_gen_atomic_smin_fetch_i64 +#define tcg_gen_atomic_umin_fetch_tl tcg_gen_atomic_umin_fetch_i64 +#define tcg_gen_atomic_smax_fetch_tl tcg_gen_atomic_smax_fetch_i64 +#define tcg_gen_atomic_umax_fetch_tl tcg_gen_atomic_umax_fetch_i64 #define tcg_gen_dup_tl_vec tcg_gen_dup_i64_vec #else #define tcg_gen_movi_tl tcg_gen_movi_i32 @@ -1145,10 +1171,18 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_atomic_fetch_and_tl tcg_gen_atomic_fetch_and_i32 #define tcg_gen_atomic_fetch_or_tl tcg_gen_atomic_fetch_or_i32 #define tcg_gen_atomic_fetch_xor_tl tcg_gen_atomic_fetch_xor_i32 +#define tcg_gen_atomic_fetch_smin_tl tcg_gen_atomic_fetch_smin_i32 +#define tcg_gen_atomic_fetch_umin_tl tcg_gen_atomic_fetch_umin_i32 +#define tcg_gen_atomic_fetch_smax_tl tcg_gen_atomic_fetch_smax_i32 +#define tcg_gen_atomic_fetch_umax_tl tcg_gen_atomic_fetch_umax_i32 #define tcg_gen_atomic_add_fetch_tl tcg_gen_atomic_add_fetch_i32 #define tcg_gen_atomic_and_fetch_tl tcg_gen_atomic_and_fetch_i32 #define tcg_gen_atomic_or_fetch_tl tcg_gen_atomic_or_fetch_i32 #define tcg_gen_atomic_xor_fetch_tl tcg_gen_atomic_xor_fetch_i32 +#define tcg_gen_atomic_smin_fetch_tl tcg_gen_atomic_smin_fetch_i32 +#define tcg_gen_atomic_umin_fetch_tl tcg_gen_atomic_umin_fetch_i32 +#define tcg_gen_atomic_smax_fetch_tl tcg_gen_atomic_smax_fetch_i32 +#define tcg_gen_atomic_umax_fetch_tl tcg_gen_atomic_umax_fetch_i32 #define tcg_gen_dup_tl_vec tcg_gen_dup_i32_vec #endif diff --git a/tcg/tcg.h b/tcg/tcg.h index 75fbad128b..1ca985479b 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -1415,12 +1415,20 @@ GEN_ATOMIC_HELPER_ALL(fetch_sub) GEN_ATOMIC_HELPER_ALL(fetch_and) GEN_ATOMIC_HELPER_ALL(fetch_or) GEN_ATOMIC_HELPER_ALL(fetch_xor) +GEN_ATOMIC_HELPER_ALL(fetch_smin) +GEN_ATOMIC_HELPER_ALL(fetch_umin) +GEN_ATOMIC_HELPER_ALL(fetch_smax) +GEN_ATOMIC_HELPER_ALL(fetch_umax) GEN_ATOMIC_HELPER_ALL(add_fetch) GEN_ATOMIC_HELPER_ALL(sub_fetch) GEN_ATOMIC_HELPER_ALL(and_fetch) GEN_ATOMIC_HELPER_ALL(or_fetch) GEN_ATOMIC_HELPER_ALL(xor_fetch) +GEN_ATOMIC_HELPER_ALL(smin_fetch) +GEN_ATOMIC_HELPER_ALL(umin_fetch) +GEN_ATOMIC_HELPER_ALL(smax_fetch) +GEN_ATOMIC_HELPER_ALL(umax_fetch) GEN_ATOMIC_HELPER_ALL(xchg) From 58edf9eef9d0e99dc051367c5a446a62223ec6e4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0379/2380] tcg: Use GEN_ATOMIC_HELPER_FN for opposite endian atomic add Suggested-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180508151437.4232-6-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- accel/tcg/atomic_template.h | 49 ++++++------------------------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 2489dd3ec1..3f41ef2782 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -229,48 +229,6 @@ GEN_ATOMIC_HELPER(xor_fetch) #undef GEN_ATOMIC_HELPER -/* Note that for addition, we need to use a separate cmpxchg loop instead - of bswaps for the reverse-host-endian helpers. */ -ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr, - ABI_TYPE val EXTRA_ARGS) -{ - ATOMIC_MMU_DECLS; - DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - DATA_TYPE ldo, ldn, ret, sto; - - ldo = atomic_read__nocheck(haddr); - while (1) { - ret = BSWAP(ldo); - sto = BSWAP(ret + val); - ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); - if (ldn == ldo) { - ATOMIC_MMU_CLEANUP; - return ret; - } - ldo = ldn; - } -} - -ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr, - ABI_TYPE val EXTRA_ARGS) -{ - ATOMIC_MMU_DECLS; - DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - DATA_TYPE ldo, ldn, ret, sto; - - ldo = atomic_read__nocheck(haddr); - while (1) { - ret = BSWAP(ldo) + val; - sto = BSWAP(ret); - ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); - if (ldn == ldo) { - ATOMIC_MMU_CLEANUP; - return ret; - } - ldo = ldn; - } -} - /* These helpers are, as a whole, full barriers. Within the helper, * the leading barrier is explicit and the trailing barrier is within * cmpxchg primitive. @@ -302,6 +260,13 @@ GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new) GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) +/* Note that for addition, we need to use a separate cmpxchg loop instead + of bswaps for the reverse-host-endian helpers. */ +#define ADD(X, Y) (X + Y) +GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) +#undef ADD + #undef GEN_ATOMIC_HELPER_FN #endif /* DATA_SIZE >= 16 */ From 3e281df1e1d17773cc212fa8cd7794957cee1a7d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0380/2380] target/riscv: Use new atomic min/max expanders Reviewed-by: Michael Clark Signed-off-by: Richard Henderson Message-id: 20180508151437.4232-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/riscv/translate.c | 72 +++++++++++----------------------------- 1 file changed, 20 insertions(+), 52 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index c0e6a044d3..0d19ecc733 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -724,7 +724,6 @@ static void gen_atomic(DisasContext *ctx, uint32_t opc, TCGv src1, src2, dat; TCGLabel *l1, *l2; TCGMemOp mop; - TCGCond cond; bool aq, rl; /* Extract the size of the atomic operation. */ @@ -822,60 +821,29 @@ static void gen_atomic(DisasContext *ctx, uint32_t opc, tcg_gen_atomic_fetch_or_tl(src2, src1, src2, ctx->mem_idx, mop); gen_set_gpr(rd, src2); break; - case OPC_RISC_AMOMIN: - cond = TCG_COND_LT; - goto do_minmax; - case OPC_RISC_AMOMAX: - cond = TCG_COND_GT; - goto do_minmax; - case OPC_RISC_AMOMINU: - cond = TCG_COND_LTU; - goto do_minmax; - case OPC_RISC_AMOMAXU: - cond = TCG_COND_GTU; - goto do_minmax; - do_minmax: - /* Handle the RL barrier. The AQ barrier is handled along the - parallel path by the SC atomic cmpxchg. On the serial path, - of course, barriers do not matter. */ - if (rl) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - if (tb_cflags(ctx->tb) & CF_PARALLEL) { - l1 = gen_new_label(); - gen_set_label(l1); - } else { - l1 = NULL; - } - gen_get_gpr(src1, rs1); gen_get_gpr(src2, rs2); - if ((mop & MO_SSIZE) == MO_SL) { - /* Sign-extend the register comparison input. */ - tcg_gen_ext32s_tl(src2, src2); - } - dat = tcg_temp_local_new(); - tcg_gen_qemu_ld_tl(dat, src1, ctx->mem_idx, mop); - tcg_gen_movcond_tl(cond, src2, dat, src2, dat, src2); - - if (tb_cflags(ctx->tb) & CF_PARALLEL) { - /* Parallel context. Make this operation atomic by verifying - that the memory didn't change while we computed the result. */ - tcg_gen_atomic_cmpxchg_tl(src2, src1, dat, src2, ctx->mem_idx, mop); - - /* If the cmpxchg failed, retry. */ - /* ??? There is an assumption here that this will eventually - succeed, such that we don't live-lock. This is not unlike - a similar loop that the compiler would generate for e.g. - __atomic_fetch_and_xor, so don't worry about it. */ - tcg_gen_brcond_tl(TCG_COND_NE, dat, src2, l1); - } else { - /* Serial context. Directly store the result. */ - tcg_gen_qemu_st_tl(src2, src1, ctx->mem_idx, mop); - } - gen_set_gpr(rd, dat); - tcg_temp_free(dat); + tcg_gen_atomic_fetch_smin_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + case OPC_RISC_AMOMAX: + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_fetch_smax_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + case OPC_RISC_AMOMINU: + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_fetch_umin_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + case OPC_RISC_AMOMAXU: + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_fetch_umax_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); break; default: From 68412d2ecedbab5a43b0d346cddb27e00d724aff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0381/2380] target/arm: Introduce ARM_FEATURE_V8_ATOMICS and initial decode The insns in the ARMv8.1-Atomics are added to the existing load/store exclusive and load/store reg opcode spaces. Rearrange the top-level decoders for these to accomodate. The Atomics insns themselves still generate Unallocated. Signed-off-by: Richard Henderson Message-id: 20180508151437.4232-8-richard.henderson@linaro.org [PMM: Drop the ARM_FEATURE_V8_1 feature flag] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- linux-user/elfload.c | 1 + target/arm/cpu.h | 1 + target/arm/translate-a64.c | 182 +++++++++++++++++++++++++++---------- 3 files changed, 138 insertions(+), 46 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 36d52194bc..13bc78d0c8 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -581,6 +581,7 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE(ARM_FEATURE_V8_SHA512, ARM_HWCAP_A64_SHA512); GET_FEATURE(ARM_FEATURE_V8_FP16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); + GET_FEATURE(ARM_FEATURE_V8_ATOMICS, ARM_HWCAP_A64_ATOMICS); GET_FEATURE(ARM_FEATURE_V8_RDM, ARM_HWCAP_A64_ASIMDRDM); GET_FEATURE(ARM_FEATURE_V8_FCMA, ARM_HWCAP_A64_FCMA); #undef GET_FEATURE diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 44e6b77151..3b086be570 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1449,6 +1449,7 @@ enum arm_features { ARM_FEATURE_V8_SHA3, /* implements SHA3 part of v8 Crypto Extensions */ ARM_FEATURE_V8_SM3, /* implements SM3 part of v8 Crypto Extensions */ ARM_FEATURE_V8_SM4, /* implements SM4 part of v8 Crypto Extensions */ + ARM_FEATURE_V8_ATOMICS, /* ARMv8.1-Atomics feature */ ARM_FEATURE_V8_RDM, /* implements v8.1 simd round multiply */ ARM_FEATURE_V8_FP16, /* implements v8.2 half-precision float */ ARM_FEATURE_V8_FCMA, /* has complex number part of v8.3 extensions. */ diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 60d104cc8a..bb8a176f9a 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -2147,62 +2147,98 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) int rt = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); int rt2 = extract32(insn, 10, 5); - int is_lasr = extract32(insn, 15, 1); int rs = extract32(insn, 16, 5); - int is_pair = extract32(insn, 21, 1); - int is_store = !extract32(insn, 22, 1); - int is_excl = !extract32(insn, 23, 1); + int is_lasr = extract32(insn, 15, 1); + int o2_L_o1_o0 = extract32(insn, 21, 3) * 2 | is_lasr; int size = extract32(insn, 30, 2); TCGv_i64 tcg_addr; - if ((!is_excl && !is_pair && !is_lasr) || - (!is_excl && is_pair) || - (is_pair && size < 2)) { - unallocated_encoding(s); + switch (o2_L_o1_o0) { + case 0x0: /* STXR */ + case 0x1: /* STLXR */ + if (rn == 31) { + gen_check_sp_alignment(s); + } + if (is_lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + tcg_addr = read_cpu_reg_sp(s, rn, 1); + gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, false); return; - } - if (rn == 31) { - gen_check_sp_alignment(s); - } - tcg_addr = read_cpu_reg_sp(s, rn, 1); - - /* Note that since TCG is single threaded load-acquire/store-release - * semantics require no extra if (is_lasr) { ... } handling. - */ - - if (is_excl) { - if (!is_store) { - s->is_ldex = true; - gen_load_exclusive(s, rt, rt2, tcg_addr, size, is_pair); - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - } - } else { - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, is_pair); + case 0x4: /* LDXR */ + case 0x5: /* LDAXR */ + if (rn == 31) { + gen_check_sp_alignment(s); } - } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - bool iss_sf = disas_ldst_compute_iss_sf(size, false, 0); + tcg_addr = read_cpu_reg_sp(s, rn, 1); + s->is_ldex = true; + gen_load_exclusive(s, rt, rt2, tcg_addr, size, false); + if (is_lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + return; + case 0x9: /* STLR */ /* Generate ISS for non-exclusive accesses including LASR. */ - if (is_store) { + if (rn == 31) { + gen_check_sp_alignment(s); + } + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + tcg_addr = read_cpu_reg_sp(s, rn, 1); + do_gpr_st(s, cpu_reg(s, rt), tcg_addr, size, true, rt, + disas_ldst_compute_iss_sf(size, false, 0), is_lasr); + return; + + case 0xd: /* LDAR */ + /* Generate ISS for non-exclusive accesses including LASR. */ + if (rn == 31) { + gen_check_sp_alignment(s); + } + tcg_addr = read_cpu_reg_sp(s, rn, 1); + do_gpr_ld(s, cpu_reg(s, rt), tcg_addr, size, false, false, true, rt, + disas_ldst_compute_iss_sf(size, false, 0), is_lasr); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + return; + + case 0x2: case 0x3: /* CASP / STXP */ + if (size & 2) { /* STXP / STLXP */ + if (rn == 31) { + gen_check_sp_alignment(s); + } if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } - do_gpr_st(s, tcg_rt, tcg_addr, size, - true, rt, iss_sf, is_lasr); - } else { - do_gpr_ld(s, tcg_rt, tcg_addr, size, false, false, - true, rt, iss_sf, is_lasr); + tcg_addr = read_cpu_reg_sp(s, rn, 1); + gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, true); + return; + } + /* CASP / CASPL */ + break; + + case 0x6: case 0x7: /* CASP / LDXP */ + if (size & 2) { /* LDXP / LDAXP */ + if (rn == 31) { + gen_check_sp_alignment(s); + } + tcg_addr = read_cpu_reg_sp(s, rn, 1); + s->is_ldex = true; + gen_load_exclusive(s, rt, rt2, tcg_addr, size, true); if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); } + return; } + /* CASPA / CASPAL */ + break; + + case 0xa: /* CAS */ + case 0xb: /* CASL */ + case 0xe: /* CASA */ + case 0xf: /* CASAL */ + break; } + unallocated_encoding(s); } /* @@ -2715,6 +2751,55 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, } } +/* Atomic memory operations + * + * 31 30 27 26 24 22 21 16 15 12 10 5 0 + * +------+-------+---+-----+-----+---+----+----+-----+-----+----+-----+ + * | size | 1 1 1 | V | 0 0 | A R | 1 | Rs | o3 | opc | 0 0 | Rn | Rt | + * +------+-------+---+-----+-----+--------+----+-----+-----+----+-----+ + * + * Rt: the result register + * Rn: base address or SP + * Rs: the source register for the operation + * V: vector flag (always 0 as of v8.3) + * A: acquire flag + * R: release flag + */ +static void disas_ldst_atomic(DisasContext *s, uint32_t insn, + int size, int rt, bool is_vector) +{ + int rs = extract32(insn, 16, 5); + int rn = extract32(insn, 5, 5); + int o3_opc = extract32(insn, 12, 4); + int feature = ARM_FEATURE_V8_ATOMICS; + + if (is_vector) { + unallocated_encoding(s); + return; + } + switch (o3_opc) { + case 000: /* LDADD */ + case 001: /* LDCLR */ + case 002: /* LDEOR */ + case 003: /* LDSET */ + case 004: /* LDSMAX */ + case 005: /* LDSMIN */ + case 006: /* LDUMAX */ + case 007: /* LDUMIN */ + case 010: /* SWP */ + default: + unallocated_encoding(s); + return; + } + if (!arm_dc_feature(s, feature)) { + unallocated_encoding(s); + return; + } + + (void)rs; + (void)rn; +} + /* Load/store register (all forms) */ static void disas_ldst_reg(DisasContext *s, uint32_t insn) { @@ -2725,23 +2810,28 @@ static void disas_ldst_reg(DisasContext *s, uint32_t insn) switch (extract32(insn, 24, 2)) { case 0: - if (extract32(insn, 21, 1) == 1 && extract32(insn, 10, 2) == 2) { - disas_ldst_reg_roffset(s, insn, opc, size, rt, is_vector); - } else { + if (extract32(insn, 21, 1) == 0) { /* Load/store register (unscaled immediate) * Load/store immediate pre/post-indexed * Load/store register unprivileged */ disas_ldst_reg_imm9(s, insn, opc, size, rt, is_vector); + return; + } + switch (extract32(insn, 10, 2)) { + case 0: + disas_ldst_atomic(s, insn, size, rt, is_vector); + return; + case 2: + disas_ldst_reg_roffset(s, insn, opc, size, rt, is_vector); + return; } break; case 1: disas_ldst_reg_unsigned_imm(s, insn, opc, size, rt, is_vector); - break; - default: - unallocated_encoding(s); - break; + return; } + unallocated_encoding(s); } /* AdvSIMD load/store multiple structures From 74608ea45434c9b07055b21885e093528c5ed98c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0382/2380] target/arm: Fill in disas_ldst_atomic This implements all of the v8.1-Atomics instructions except for compare-and-swap, which is decoded elsewhere. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180508151437.4232-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index bb8a176f9a..86989fda6c 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -84,6 +84,7 @@ typedef void NeonGenOneOpFn(TCGv_i64, TCGv_i64); typedef void CryptoTwoOpFn(TCGv_ptr, TCGv_ptr); typedef void CryptoThreeOpIntFn(TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void CryptoThreeOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); +typedef void AtomicThreeOpFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGArg, TCGMemOp); /* Note that the gvec expanders operate on offsets + sizes. */ typedef void GVecGen2Fn(unsigned, uint32_t, uint32_t, uint32_t, uint32_t); @@ -2772,6 +2773,8 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, int rn = extract32(insn, 5, 5); int o3_opc = extract32(insn, 12, 4); int feature = ARM_FEATURE_V8_ATOMICS; + TCGv_i64 tcg_rn, tcg_rs; + AtomicThreeOpFn *fn; if (is_vector) { unallocated_encoding(s); @@ -2779,14 +2782,32 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, } switch (o3_opc) { case 000: /* LDADD */ + fn = tcg_gen_atomic_fetch_add_i64; + break; case 001: /* LDCLR */ + fn = tcg_gen_atomic_fetch_and_i64; + break; case 002: /* LDEOR */ + fn = tcg_gen_atomic_fetch_xor_i64; + break; case 003: /* LDSET */ + fn = tcg_gen_atomic_fetch_or_i64; + break; case 004: /* LDSMAX */ + fn = tcg_gen_atomic_fetch_smax_i64; + break; case 005: /* LDSMIN */ + fn = tcg_gen_atomic_fetch_smin_i64; + break; case 006: /* LDUMAX */ + fn = tcg_gen_atomic_fetch_umax_i64; + break; case 007: /* LDUMIN */ + fn = tcg_gen_atomic_fetch_umin_i64; + break; case 010: /* SWP */ + fn = tcg_gen_atomic_xchg_i64; + break; default: unallocated_encoding(s); return; @@ -2796,8 +2817,21 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, return; } - (void)rs; - (void)rn; + if (rn == 31) { + gen_check_sp_alignment(s); + } + tcg_rn = cpu_reg_sp(s, rn); + tcg_rs = read_cpu_reg(s, rs, true); + + if (o3_opc == 1) { /* LDCLR */ + tcg_gen_not_i64(tcg_rs, tcg_rs); + } + + /* The tcg atomic primitives are all full barriers. Therefore we + * can ignore the Acquire and Release bits of this instruction. + */ + fn(cpu_reg(s, rt), tcg_rn, tcg_rs, get_mem_index(s), + s->be_data | size | MO_ALIGN); } /* Load/store register (all forms) */ From 44ac14b06fa33f60982923b6b8a3bf8dd2fea61d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0383/2380] target/arm: Implement CAS and CASP Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180508151437.4232-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-a64.c | 43 ++++++++++++++ target/arm/helper-a64.h | 2 + target/arm/translate-a64.c | 119 ++++++++++++++++++++++++++++++++++++- 3 files changed, 161 insertions(+), 3 deletions(-) diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index afb25ad20c..549ed3513e 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -636,6 +636,49 @@ uint64_t HELPER(paired_cmpxchg64_be_parallel)(CPUARMState *env, uint64_t addr, return do_paired_cmpxchg64_be(env, addr, new_lo, new_hi, true, GETPC()); } +/* Writes back the old data into Rs. */ +void HELPER(casp_le_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, + uint64_t new_lo, uint64_t new_hi) +{ + uintptr_t ra = GETPC(); +#ifndef CONFIG_ATOMIC128 + cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); +#else + Int128 oldv, cmpv, newv; + + cmpv = int128_make128(env->xregs[rs], env->xregs[rs + 1]); + newv = int128_make128(new_lo, new_hi); + + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra); + + env->xregs[rs] = int128_getlo(oldv); + env->xregs[rs + 1] = int128_gethi(oldv); +#endif +} + +void HELPER(casp_be_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, + uint64_t new_hi, uint64_t new_lo) +{ + uintptr_t ra = GETPC(); +#ifndef CONFIG_ATOMIC128 + cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); +#else + Int128 oldv, cmpv, newv; + + cmpv = int128_make128(env->xregs[rs + 1], env->xregs[rs]); + newv = int128_make128(new_lo, new_hi); + + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); + + env->xregs[rs + 1] = int128_getlo(oldv); + env->xregs[rs] = int128_gethi(oldv); +#endif +} + /* * AdvSIMD half-precision */ diff --git a/target/arm/helper-a64.h b/target/arm/helper-a64.h index ef4ddfe9d8..b8028ac98c 100644 --- a/target/arm/helper-a64.h +++ b/target/arm/helper-a64.h @@ -51,6 +51,8 @@ DEF_HELPER_FLAGS_4(paired_cmpxchg64_le_parallel, TCG_CALL_NO_WG, DEF_HELPER_FLAGS_4(paired_cmpxchg64_be, TCG_CALL_NO_WG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(paired_cmpxchg64_be_parallel, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_5(casp_le_parallel, void, env, i32, i64, i64, i64) +DEF_HELPER_5(casp_be_parallel, void, env, i32, i64, i64, i64) DEF_HELPER_FLAGS_3(advsimd_maxh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) DEF_HELPER_FLAGS_3(advsimd_minh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) DEF_HELPER_FLAGS_3(advsimd_maxnumh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 86989fda6c..fa60cf908f 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -2114,6 +2114,103 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_movi_i64(cpu_exclusive_addr, -1); } +static void gen_compare_and_swap(DisasContext *s, int rs, int rt, + int rn, int size) +{ + TCGv_i64 tcg_rs = cpu_reg(s, rs); + TCGv_i64 tcg_rt = cpu_reg(s, rt); + int memidx = get_mem_index(s); + TCGv_i64 addr = cpu_reg_sp(s, rn); + + if (rn == 31) { + gen_check_sp_alignment(s); + } + tcg_gen_atomic_cmpxchg_i64(tcg_rs, addr, tcg_rs, tcg_rt, memidx, + size | MO_ALIGN | s->be_data); +} + +static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, + int rn, int size) +{ + TCGv_i64 s1 = cpu_reg(s, rs); + TCGv_i64 s2 = cpu_reg(s, rs + 1); + TCGv_i64 t1 = cpu_reg(s, rt); + TCGv_i64 t2 = cpu_reg(s, rt + 1); + TCGv_i64 addr = cpu_reg_sp(s, rn); + int memidx = get_mem_index(s); + + if (rn == 31) { + gen_check_sp_alignment(s); + } + + if (size == 2) { + TCGv_i64 cmp = tcg_temp_new_i64(); + TCGv_i64 val = tcg_temp_new_i64(); + + if (s->be_data == MO_LE) { + tcg_gen_concat32_i64(val, t1, t2); + tcg_gen_concat32_i64(cmp, s1, s2); + } else { + tcg_gen_concat32_i64(val, t2, t1); + tcg_gen_concat32_i64(cmp, s2, s1); + } + + tcg_gen_atomic_cmpxchg_i64(cmp, addr, cmp, val, memidx, + MO_64 | MO_ALIGN | s->be_data); + tcg_temp_free_i64(val); + + if (s->be_data == MO_LE) { + tcg_gen_extr32_i64(s1, s2, cmp); + } else { + tcg_gen_extr32_i64(s2, s1, cmp); + } + tcg_temp_free_i64(cmp); + } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { + TCGv_i32 tcg_rs = tcg_const_i32(rs); + + if (s->be_data == MO_LE) { + gen_helper_casp_le_parallel(cpu_env, tcg_rs, addr, t1, t2); + } else { + gen_helper_casp_be_parallel(cpu_env, tcg_rs, addr, t1, t2); + } + tcg_temp_free_i32(tcg_rs); + } else { + TCGv_i64 d1 = tcg_temp_new_i64(); + TCGv_i64 d2 = tcg_temp_new_i64(); + TCGv_i64 a2 = tcg_temp_new_i64(); + TCGv_i64 c1 = tcg_temp_new_i64(); + TCGv_i64 c2 = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_const_i64(0); + + /* Load the two words, in memory order. */ + tcg_gen_qemu_ld_i64(d1, addr, memidx, + MO_64 | MO_ALIGN_16 | s->be_data); + tcg_gen_addi_i64(a2, addr, 8); + tcg_gen_qemu_ld_i64(d2, addr, memidx, MO_64 | s->be_data); + + /* Compare the two words, also in memory order. */ + tcg_gen_setcond_i64(TCG_COND_EQ, c1, d1, s1); + tcg_gen_setcond_i64(TCG_COND_EQ, c2, d2, s2); + tcg_gen_and_i64(c2, c2, c1); + + /* If compare equal, write back new data, else write back old data. */ + tcg_gen_movcond_i64(TCG_COND_NE, c1, c2, zero, t1, d1); + tcg_gen_movcond_i64(TCG_COND_NE, c2, c2, zero, t2, d2); + tcg_gen_qemu_st_i64(c1, addr, memidx, MO_64 | s->be_data); + tcg_gen_qemu_st_i64(c2, a2, memidx, MO_64 | s->be_data); + tcg_temp_free_i64(a2); + tcg_temp_free_i64(c1); + tcg_temp_free_i64(c2); + tcg_temp_free_i64(zero); + + /* Write back the data from memory to Rs. */ + tcg_gen_mov_i64(s1, d1); + tcg_gen_mov_i64(s2, d2); + tcg_temp_free_i64(d1); + tcg_temp_free_i64(d2); + } +} + /* Update the Sixty-Four bit (SF) registersize. This logic is derived * from the ARMv8 specs for LDR (Shared decode for all encodings). */ @@ -2214,10 +2311,16 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, true); return; } - /* CASP / CASPL */ + if (rt2 == 31 + && ((rt | rs) & 1) == 0 + && arm_dc_feature(s, ARM_FEATURE_V8_ATOMICS)) { + /* CASP / CASPL */ + gen_compare_and_swap_pair(s, rs, rt, rn, size | 2); + return; + } break; - case 0x6: case 0x7: /* CASP / LDXP */ + case 0x6: case 0x7: /* CASPA / LDXP */ if (size & 2) { /* LDXP / LDAXP */ if (rn == 31) { gen_check_sp_alignment(s); @@ -2230,13 +2333,23 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) } return; } - /* CASPA / CASPAL */ + if (rt2 == 31 + && ((rt | rs) & 1) == 0 + && arm_dc_feature(s, ARM_FEATURE_V8_ATOMICS)) { + /* CASPA / CASPAL */ + gen_compare_and_swap_pair(s, rs, rt, rn, size | 2); + return; + } break; case 0xa: /* CAS */ case 0xb: /* CASL */ case 0xe: /* CASA */ case 0xf: /* CASAL */ + if (rt2 == 31 && arm_dc_feature(s, ARM_FEATURE_V8_ATOMICS)) { + gen_compare_and_swap(s, rs, rt, rn, size); + return; + } break; } unallocated_encoding(s); From ec7f05fae36637d11de272da82ad1e6c233e77d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0384/2380] target/arm: Enable ARM_FEATURE_V8_ATOMICS for user-only Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180508151437.4232-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu64.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 991d764674..c50dcd4077 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -248,6 +248,7 @@ static void aarch64_max_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_SM4); set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); + set_feature(&cpu->env, ARM_FEATURE_V8_ATOMICS); set_feature(&cpu->env, ARM_FEATURE_V8_RDM); set_feature(&cpu->env, ARM_FEATURE_V8_FP16); set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); From a6117fae4576edfe7a5a5b802a742c33112c0993 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0385/2380] target/arm: Implement vector shifted SCVF/UCVF for fp16 While we have some of the scalar paths for *CVF for fp16, we failed to decode the fp16 version of these instructions. Cc: qemu-stable@nongnu.org Signed-off-by: Richard Henderson Message-id: 20180502221552.3873-2-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index fa60cf908f..f4e2afa72c 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -7405,13 +7405,26 @@ static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar, int immh, int immb, int opcode, int rn, int rd) { - bool is_double = extract32(immh, 3, 1); - int size = is_double ? MO_64 : MO_32; - int elements; + int size, elements, fracbits; int immhb = immh << 3 | immb; - int fracbits = (is_double ? 128 : 64) - immhb; - if (!extract32(immh, 2, 2)) { + if (immh & 8) { + size = MO_64; + if (!is_scalar && !is_q) { + unallocated_encoding(s); + return; + } + } else if (immh & 4) { + size = MO_32; + } else if (immh & 2) { + size = MO_16; + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + } else { + /* immh == 0 would be a failure of the decode logic */ + g_assert(immh == 1); unallocated_encoding(s); return; } @@ -7419,20 +7432,14 @@ static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar, if (is_scalar) { elements = 1; } else { - elements = is_double ? 2 : is_q ? 4 : 2; - if (is_double && !is_q) { - unallocated_encoding(s); - return; - } + elements = (8 << is_q) >> size; } + fracbits = (16 << size) - immhb; if (!fp_access_check(s)) { return; } - /* immh == 0 would be a failure of the decode logic */ - g_assert(immh); - handle_simd_intfp_conv(s, rd, rn, elements, !is_u, fracbits, size); } From d0ba8e74acd299b092786ffc30b306638d395a9e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:57 +0100 Subject: [PATCH 0386/2380] target/arm: Implement vector shifted FCVT for fp16 While we have some of the scalar paths for FCVT for fp16, we failed to decode the fp16 version of these instructions. Cc: qemu-stable@nongnu.org Signed-off-by: Richard Henderson Message-id: 20180502221552.3873-3-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 65 +++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index f4e2afa72c..317f2773b4 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -7448,19 +7448,28 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, bool is_q, bool is_u, int immh, int immb, int rn, int rd) { - bool is_double = extract32(immh, 3, 1); int immhb = immh << 3 | immb; - int fracbits = (is_double ? 128 : 64) - immhb; - int pass; + int pass, size, fracbits; TCGv_ptr tcg_fpstatus; TCGv_i32 tcg_rmode, tcg_shift; - if (!extract32(immh, 2, 2)) { - unallocated_encoding(s); - return; - } - - if (!is_scalar && !is_q && is_double) { + if (immh & 0x8) { + size = MO_64; + if (!is_scalar && !is_q) { + unallocated_encoding(s); + return; + } + } else if (immh & 0x4) { + size = MO_32; + } else if (immh & 0x2) { + size = MO_16; + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + } else { + /* Should have split out AdvSIMD modified immediate earlier. */ + assert(immh == 1); unallocated_encoding(s); return; } @@ -7472,11 +7481,12 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, assert(!(is_scalar && is_q)); tcg_rmode = tcg_const_i32(arm_rmode_to_sf(FPROUNDING_ZERO)); - tcg_fpstatus = get_fpstatus_ptr(false); + tcg_fpstatus = get_fpstatus_ptr(size == MO_16); gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + fracbits = (16 << size) - immhb; tcg_shift = tcg_const_i32(fracbits); - if (is_double) { + if (size == MO_64) { int maxpass = is_scalar ? 1 : 2; for (pass = 0; pass < maxpass; pass++) { @@ -7493,20 +7503,37 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, } clear_vec_high(s, is_q, rd); } else { - int maxpass = is_scalar ? 1 : is_q ? 4 : 2; + void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); + int maxpass = is_scalar ? 1 : ((8 << is_q) >> size); + + switch (size) { + case MO_16: + if (is_u) { + fn = gen_helper_vfp_toulh; + } else { + fn = gen_helper_vfp_toslh; + } + break; + case MO_32: + if (is_u) { + fn = gen_helper_vfp_touls; + } else { + fn = gen_helper_vfp_tosls; + } + break; + default: + g_assert_not_reached(); + } + for (pass = 0; pass < maxpass; pass++) { TCGv_i32 tcg_op = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_op, rn, pass, MO_32); - if (is_u) { - gen_helper_vfp_touls(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_tosls(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); - } + read_vec_element_i32(s, tcg_op, rn, pass, size); + fn(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); if (is_scalar) { write_fp_sreg(s, rd, tcg_op); } else { - write_vec_element_i32(s, tcg_op, rd, pass, MO_32); + write_vec_element_i32(s, tcg_op, rd, pass, size); } tcg_temp_free_i32(tcg_op); } From 88808a022c06f98d81cd3f2d105a5734c5614839 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:58 +0100 Subject: [PATCH 0387/2380] target/arm: Fix float16 to/from int16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The instruction "ucvtf v0.4h, v04h, #2", with input 0x8000u, overflows the intermediate float16 to infinity before we have a chance to scale the output. Use float64 as the intermediate type so that no input argument (uint32_t in this case) can overflow or round before scaling. Given the declared argument, the signed int32_t function has the same problem. When converting from float16 to integer, using u/int32_t instead of u/int16_t means that the bounding is incorrect. Cc: qemu-stable@nongnu.org Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson Message-id: 20180502221552.3873-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 53 ++++++++++++++++++++++++++++++++++++-- target/arm/helper.h | 4 +-- target/arm/translate-a64.c | 4 +-- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 0fef5d4d06..817f9d81a0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11420,11 +11420,60 @@ VFP_CONV_FIX_A64(sq, s, 32, 64, int64) VFP_CONV_FIX(uh, s, 32, 32, uint16) VFP_CONV_FIX(ul, s, 32, 32, uint32) VFP_CONV_FIX_A64(uq, s, 32, 64, uint64) -VFP_CONV_FIX_A64(sl, h, 16, 32, int32) -VFP_CONV_FIX_A64(ul, h, 16, 32, uint32) + #undef VFP_CONV_FIX #undef VFP_CONV_FIX_FLOAT #undef VFP_CONV_FLOAT_FIX_ROUND +#undef VFP_CONV_FIX_A64 + +/* Conversion to/from f16 can overflow to infinity before/after scaling. + * Therefore we convert to f64 (which does not round), scale, + * and then convert f64 to f16 (which may round). + */ + +static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst) +{ + return float64_to_float16(float64_scalbn(f, -shift, fpst), true, fpst); +} + +float16 HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst) +{ + return do_postscale_fp16(int32_to_float64(x, fpst), shift, fpst); +} + +float16 HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) +{ + return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst); +} + +static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst) +{ + if (unlikely(float16_is_any_nan(f))) { + float_raise(float_flag_invalid, fpst); + return 0; + } else { + int old_exc_flags = get_float_exception_flags(fpst); + float64 ret; + + ret = float16_to_float64(f, true, fpst); + ret = float64_scalbn(ret, shift, fpst); + old_exc_flags |= get_float_exception_flags(fpst) + & float_flag_input_denormal; + set_float_exception_flags(old_exc_flags, fpst); + + return ret; + } +} + +uint32_t HELPER(vfp_toshh)(float16 x, uint32_t shift, void *fpst) +{ + return float64_to_int16(do_prescale_fp16(x, shift, fpst), fpst); +} + +uint32_t HELPER(vfp_touhh)(float16 x, uint32_t shift, void *fpst) +{ + return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst); +} /* Set the current fp rounding mode and return the old one. * The argument is a softfloat float_round_ value. diff --git a/target/arm/helper.h b/target/arm/helper.h index 34e8cc8904..1969b37f2d 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -149,8 +149,8 @@ DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_toulh, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toslh, i32, f16, i32, ptr) +DEF_HELPER_3(vfp_touhh, i32, f16, i32, ptr) +DEF_HELPER_3(vfp_toshh, i32, f16, i32, ptr) DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosqs, i64, f32, i32, ptr) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 317f2773b4..b302171545 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -7509,9 +7509,9 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, switch (size) { case MO_16: if (is_u) { - fn = gen_helper_vfp_toulh; + fn = gen_helper_vfp_touhh; } else { - fn = gen_helper_vfp_toslh; + fn = gen_helper_vfp_toshh; } break; case MO_32: From 9a9f1f59521f46e8ff4527d9a2b52f83577e2aa3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 18:10:58 +0100 Subject: [PATCH 0388/2380] target/arm: Clear SVE high bits for FMOV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use write_fp_dreg and clear_vec_high to zero the bits that need zeroing for these cases. Cc: qemu-stable@nongnu.org Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson Message-id: 20180502221552.3873-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index b302171545..b0471c842e 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -5681,31 +5681,24 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) if (itof) { TCGv_i64 tcg_rn = cpu_reg(s, rn); + TCGv_i64 tmp; switch (type) { case 0: - { /* 32 bit */ - TCGv_i64 tmp = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); tcg_gen_ext32u_i64(tmp, tcg_rn); - tcg_gen_st_i64(tmp, cpu_env, fp_reg_offset(s, rd, MO_64)); - tcg_gen_movi_i64(tmp, 0); - tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(s, rd)); + write_fp_dreg(s, rd, tmp); tcg_temp_free_i64(tmp); break; - } case 1: - { /* 64 bit */ - TCGv_i64 tmp = tcg_const_i64(0); - tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_offset(s, rd, MO_64)); - tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(s, rd)); - tcg_temp_free_i64(tmp); + write_fp_dreg(s, rd, tcg_rn); break; - } case 2: /* 64 bit to top half. */ tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_hi_offset(s, rd)); + clear_vec_high(s, true, rd); break; } } else { From 5cbc61110738accb16ff8ed1f08a32906d02790f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 May 2018 11:55:20 -0700 Subject: [PATCH 0389/2380] target/m68k: Fix build Werror with gcc 8.0.1 Fedora 28 ships with the released gcc 8. The Werror stems from the compiler finding a path through the second switch via a missing default case in which src1 is uninitialized, and not being able to prove that the missing default case is unreachable due to the first switch. Simplify the second switch to merge default with OS_LONG, which returns directly. This removes the unreachable path. Cc: Laurent Vivier Signed-off-by: Richard Henderson Reviewed-by: Laurent Vivier Message-id: 20180508185520.23757-1-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/m68k/translate.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index e407ba2db3..44a0ac4e2e 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -2297,7 +2297,7 @@ DISAS_INSN(arith_im) im = tcg_const_i32(read_im32(env, s)); break; default: - abort(); + g_assert_not_reached(); } if (with_SR) { @@ -2317,7 +2317,8 @@ DISAS_INSN(arith_im) } src1 = gen_get_sr(s); break; - case OS_LONG: + default: + /* OS_LONG; others already g_assert_not_reached. */ disas_undef(env, s, insn); return; } From ba6a4fd95de1e0a85bfbe43330448f16486e2181 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 11 Apr 2018 13:50:36 +0200 Subject: [PATCH 0390/2380] i386/kvm: add support for Hyper-V reenlightenment MSRs KVM recently gained support for Hyper-V Reenlightenment MSRs which are required to make KVM-on-Hyper-V enable TSC page clocksource to its guests when INVTSC is not passed to it (and it is not passed by default in Qemu as it effectively blocks migration). Signed-off-by: Vitaly Kuznetsov Message-Id: <20180411115036.31832-2-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 +++- target/i386/cpu.h | 4 ++++ target/i386/hyperv-proto.h | 9 ++++++++- target/i386/kvm.c | 39 +++++++++++++++++++++++++++++++++++++- target/i386/machine.c | 24 +++++++++++++++++++++++ 5 files changed, 77 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index a20fe26573..b0a1c629a3 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -416,7 +416,8 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL /* hv_vpindex_access */, NULL /* hv_msr_reset_access */, NULL /* hv_msr_stats_access */, NULL /* hv_reftsc_access */, NULL /* hv_msr_idle_access */, NULL /* hv_msr_frequency_access */, - NULL, NULL, NULL, NULL, + NULL /* hv_msr_debug_access */, NULL /* hv_msr_reenlightenment_access */, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -4770,6 +4771,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("hv-synic", X86CPU, hyperv_synic, false), DEFINE_PROP_BOOL("hv-stimer", X86CPU, hyperv_stimer, false), DEFINE_PROP_BOOL("hv-frequencies", X86CPU, hyperv_frequencies, false), + DEFINE_PROP_BOOL("hv-reenlightenment", X86CPU, hyperv_reenlightenment, false), DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true), DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false), DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 1b219fafc4..b58b779bff 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1174,6 +1174,9 @@ typedef struct CPUX86State { uint64_t msr_hv_synic_sint[HV_SINT_COUNT]; uint64_t msr_hv_stimer_config[HV_STIMER_COUNT]; uint64_t msr_hv_stimer_count[HV_STIMER_COUNT]; + uint64_t msr_hv_reenlightenment_control; + uint64_t msr_hv_tsc_emulation_control; + uint64_t msr_hv_tsc_emulation_status; uint64_t msr_rtit_ctrl; uint64_t msr_rtit_status; @@ -1297,6 +1300,7 @@ struct X86CPU { bool hyperv_synic; bool hyperv_stimer; bool hyperv_frequencies; + bool hyperv_reenlightenment; bool check_cpuid; bool enforce_cpuid; bool expose_kvm; diff --git a/target/i386/hyperv-proto.h b/target/i386/hyperv-proto.h index cb4d7f2b7a..93352ebd2a 100644 --- a/target/i386/hyperv-proto.h +++ b/target/i386/hyperv-proto.h @@ -35,7 +35,7 @@ #define HV_RESET_AVAILABLE (1u << 7) #define HV_REFERENCE_TSC_AVAILABLE (1u << 9) #define HV_ACCESS_FREQUENCY_MSRS (1u << 11) - +#define HV_ACCESS_REENLIGHTENMENTS_CONTROL (1u << 13) /* * HV_CPUID_FEATURES.EDX bits @@ -129,6 +129,13 @@ #define HV_X64_MSR_CRASH_CTL 0x40000105 #define HV_CRASH_CTL_NOTIFY (1ull << 63) +/* + * Reenlightenment notification MSRs + */ +#define HV_X64_MSR_REENLIGHTENMENT_CONTROL 0x40000106 +#define HV_X64_MSR_TSC_EMULATION_CONTROL 0x40000107 +#define HV_X64_MSR_TSC_EMULATION_STATUS 0x40000108 + /* * Hypercall status code */ diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 6c49954e68..d6666a4b19 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -90,6 +90,7 @@ static bool has_msr_hv_runtime; static bool has_msr_hv_synic; static bool has_msr_hv_stimer; static bool has_msr_hv_frequencies; +static bool has_msr_hv_reenlightenment; static bool has_msr_xss; static bool has_msr_spec_ctrl; static bool has_msr_smi_count; @@ -583,7 +584,8 @@ static bool hyperv_enabled(X86CPU *cpu) cpu->hyperv_vpindex || cpu->hyperv_runtime || cpu->hyperv_synic || - cpu->hyperv_stimer); + cpu->hyperv_stimer || + cpu->hyperv_reenlightenment); } static int kvm_arch_set_tsc_khz(CPUState *cs) @@ -669,6 +671,16 @@ static int hyperv_handle_properties(CPUState *cs) } env->features[FEAT_HYPERV_EDX] |= HV_GUEST_CRASH_MSR_AVAILABLE; } + if (cpu->hyperv_reenlightenment) { + if (!has_msr_hv_reenlightenment) { + fprintf(stderr, + "Hyper-V Reenlightenment MSRs " + "(requested by 'hv-reenlightenment' cpu flag) " + "are not supported by kernel\n"); + return -ENOSYS; + } + env->features[FEAT_HYPERV_EAX] |= HV_ACCESS_REENLIGHTENMENTS_CONTROL; + } env->features[FEAT_HYPERV_EDX] |= HV_CPU_DYNAMIC_PARTITIONING_AVAILABLE; if (cpu->hyperv_reset) { if (!has_msr_hv_reset) { @@ -1215,6 +1227,9 @@ static int kvm_get_supported_msrs(KVMState *s) case HV_X64_MSR_TSC_FREQUENCY: has_msr_hv_frequencies = true; break; + case HV_X64_MSR_REENLIGHTENMENT_CONTROL: + has_msr_hv_reenlightenment = true; + break; case MSR_IA32_SPEC_CTRL: has_msr_spec_ctrl = true; break; @@ -1778,6 +1793,14 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, HV_X64_MSR_REFERENCE_TSC, env->msr_hv_tsc); } + if (cpu->hyperv_reenlightenment) { + kvm_msr_entry_add(cpu, HV_X64_MSR_REENLIGHTENMENT_CONTROL, + env->msr_hv_reenlightenment_control); + kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_CONTROL, + env->msr_hv_tsc_emulation_control); + kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_STATUS, + env->msr_hv_tsc_emulation_status); + } } if (cpu->hyperv_vapic) { kvm_msr_entry_add(cpu, HV_X64_MSR_APIC_ASSIST_PAGE, @@ -2140,6 +2163,11 @@ static int kvm_get_msrs(X86CPU *cpu) if (cpu->hyperv_time) { kvm_msr_entry_add(cpu, HV_X64_MSR_REFERENCE_TSC, 0); } + if (cpu->hyperv_reenlightenment) { + kvm_msr_entry_add(cpu, HV_X64_MSR_REENLIGHTENMENT_CONTROL, 0); + kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_CONTROL, 0); + kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_STATUS, 0); + } if (has_msr_hv_crash) { int j; @@ -2397,6 +2425,15 @@ static int kvm_get_msrs(X86CPU *cpu) env->msr_hv_stimer_count[(index - HV_X64_MSR_STIMER0_COUNT)/2] = msrs[i].data; break; + case HV_X64_MSR_REENLIGHTENMENT_CONTROL: + env->msr_hv_reenlightenment_control = msrs[i].data; + break; + case HV_X64_MSR_TSC_EMULATION_CONTROL: + env->msr_hv_tsc_emulation_control = msrs[i].data; + break; + case HV_X64_MSR_TSC_EMULATION_STATUS: + env->msr_hv_tsc_emulation_status = msrs[i].data; + break; case MSR_MTRRdefType: env->mtrr_deftype = msrs[i].data; break; diff --git a/target/i386/machine.c b/target/i386/machine.c index bd2d82e91b..fd99c0bbb4 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -713,6 +713,29 @@ static const VMStateDescription vmstate_msr_hyperv_stimer = { } }; +static bool hyperv_reenlightenment_enable_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->msr_hv_reenlightenment_control != 0 || + env->msr_hv_tsc_emulation_control != 0 || + env->msr_hv_tsc_emulation_status != 0; +} + +static const VMStateDescription vmstate_msr_hyperv_reenlightenment = { + .name = "cpu/msr_hyperv_reenlightenment", + .version_id = 1, + .minimum_version_id = 1, + .needed = hyperv_reenlightenment_enable_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.msr_hv_reenlightenment_control, X86CPU), + VMSTATE_UINT64(env.msr_hv_tsc_emulation_control, X86CPU), + VMSTATE_UINT64(env.msr_hv_tsc_emulation_status, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + static bool avx512_needed(void *opaque) { X86CPU *cpu = opaque; @@ -1005,6 +1028,7 @@ VMStateDescription vmstate_x86_cpu = { &vmstate_msr_hyperv_runtime, &vmstate_msr_hyperv_synic, &vmstate_msr_hyperv_stimer, + &vmstate_msr_hyperv_reenlightenment, &vmstate_avx512, &vmstate_xss, &vmstate_tsc_khz, From 8a99e9a30ca8c93082d9032627c5d522ff66e664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 15 Apr 2018 20:05:19 -0300 Subject: [PATCH 0391/2380] configure: Really use local libfdt if the system one is too old MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU requires libfdt version >= 1.4.2. If the host has an older libfdt installed, the configure script will use a (git cloned) local version. Example with Debian 8: $ dpkg-query --showformat='${Version}\n' --show libfdt-dev 1.4.0+dfsg-1 $ ./configure [...] fdt support yes # from git submodule 'dtc' If this case occurs, the linker will have 2 different libfdt available in the library search path. The default behavior is to search the system path first, then the local path. Even if the configure script noticed the libfdt is too old and clone a more recent locally, when linking the system library is selected first, and the link process eventually fails: LINK mips64el-softmmu/qemu-system-mips64el ../hw/core/loader-fit.o: In function `load_fit': /root/src/github.com/philmd/qemu/hw/core/loader-fit.c:278: undefined reference to `fdt_first_subnode' /root/src/github.com/philmd/qemu/hw/core/loader-fit.c:286: undefined reference to `fdt_next_subnode' /root/src/github.com/philmd/qemu/hw/core/loader-fit.c:277: undefined reference to `fdt_first_subnode' collect2: error: ld returned 1 exit status Makefile:201: recipe for target 'qemu-system-mips64el' failed make[1]: *** [qemu-system-mips64el] Error 1 QEMU already uses a kludge to enforce local CFLAGS before system ones for libpixman and libfdt, add a similar kludge for the LDFLAGS to enforce using the local libfdt. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180415230522.24404-2-f4bug@amsat.org> Signed-off-by: Paolo Bonzini Reviewed-by: Thomas Huth --- configure | 6 +++++- rules.mak | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 83a6080bf8..e25e0d9494 100755 --- a/configure +++ b/configure @@ -3804,7 +3804,8 @@ EOF symlink "$source_path/dtc/scripts" "dtc/scripts" fi fdt_cflags="-I\$(SRC_PATH)/dtc/libfdt" - fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs" + fdt_ldflags="-L\$(BUILD_DIR)/dtc/libfdt" + fdt_libs="$fdt_libs" elif test "$fdt" = "yes" ; then # Not a git build & no libfdt found, prompt for system install error_exit "DTC (libfdt) version >= 1.4.2 not present." \ @@ -5746,6 +5747,7 @@ echo_version() { # prepend pixman and ftd flags after all config tests are done QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS" +QEMU_LDFLAGS="$fdt_ldflags $QEMU_LDFLAGS" libs_softmmu="$pixman_libs $libs_softmmu" echo "Install prefix $prefix" @@ -5776,6 +5778,7 @@ echo "ARFLAGS $ARFLAGS" echo "CFLAGS $CFLAGS" echo "QEMU_CFLAGS $QEMU_CFLAGS" echo "LDFLAGS $LDFLAGS" +echo "QEMU_LDFLAGS $QEMU_LDFLAGS" echo "make $make" echo "install $install" echo "python $python" @@ -6710,6 +6713,7 @@ else fi echo "LDFLAGS=$LDFLAGS" >> $config_host_mak echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak +echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak echo "LD_REL_FLAGS=$LD_REL_FLAGS" >> $config_host_mak echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak echo "LIBS+=$LIBS" >> $config_host_mak diff --git a/rules.mak b/rules.mak index 93a07027b0..04c7f74d07 100644 --- a/rules.mak +++ b/rules.mak @@ -73,7 +73,7 @@ expand-objs = $(strip $(sort $(filter %.o,$1)) \ # must link with the C++ compiler, not the plain C compiler. LINKPROG = $(or $(CXX),$(CC)) -LINK = $(call quiet-command, $(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \ +LINK = $(call quiet-command, $(LINKPROG) $(QEMU_LDFLAGS) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \ $(call process-archive-undefs, $1) \ $(version-obj-y) $(call extract-libs,$1) $(LIBS),"LINK","$(TARGET_DIR)$@") From e3971d612abd8a11b15e95add672fc993f65db2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 15 Apr 2018 20:05:20 -0300 Subject: [PATCH 0392/2380] configure: Display if libfdt is from system or git MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The configure script outputs "yes" regardless which libfdt is used: ./configure [...] fdt support yes Sometimes you can have both system and local git version available, change the configure script to display which library got selected: debian8$ dpkg-query --showformat='${Version}\n' --show libfdt-dev 1.4.0+dfsg-1 debian8$ ./configure [...] fdt support git Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180415230522.24404-3-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- configure | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/configure b/configure index e25e0d9494..49cdf12449 100755 --- a/configure +++ b/configure @@ -3789,15 +3789,14 @@ int main(void) { fdt_first_subnode(0, 0); return 0; } EOF if compile_prog "" "$fdt_libs" ; then # system DTC is good - use it - fdt=yes + fdt=system else # have GIT checkout, so activate dtc submodule if test -e "${source_path}/.git" ; then git_submodules="${git_submodules} dtc" fi if test -d "${source_path}/dtc/libfdt" || test -e "${source_path}/.git" ; then - fdt=yes - dtc_internal="yes" + fdt=git mkdir -p dtc if [ "$pwd_is_source_path" != "y" ] ; then symlink "$source_path/dtc/Makefile" "dtc/Makefile" @@ -6338,7 +6337,7 @@ fi if test "$preadv" = "yes" ; then echo "CONFIG_PREADV=y" >> $config_host_mak fi -if test "$fdt" = "yes" ; then +if test "$fdt" != "no" ; then echo "CONFIG_FDT=y" >> $config_host_mak fi if test "$membarrier" = "yes" ; then @@ -7140,7 +7139,7 @@ echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak done # for target in $targets -if [ "$dtc_internal" = "yes" ]; then +if [ "$fdt" = "git" ]; then echo "config-host.h: subdir-dtc" >> $config_host_mak fi if [ "$capstone" = "git" -o "$capstone" = "internal" ]; then From 03d92e26532cba48eb511633c41ec81555fb40ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 15 Apr 2018 20:05:21 -0300 Subject: [PATCH 0393/2380] shippable: Remove Debian 8 libfdt kludge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This kludge was added in a825ca06137, but a cleaner and more generic fix is now available (see ##COMMIT_CONFIGURE_LIBFDT_LDFLAGS_SHA##). Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180415230522.24404-4-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- .shippable.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.shippable.yml b/.shippable.yml index 60f2ce9218..f74a3de3ff 100644 --- a/.shippable.yml +++ b/.shippable.yml @@ -35,13 +35,5 @@ build: options: "-e HOME=/root" ci: - unset CC - # some targets require newer up to date packages, for example TARGET_LIST matching - # aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu) - # see the configure script: - # error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:" - # " (1) Preferred: Install the DTC (libfdt) devel package" - # " (2) Fetch the DTC submodule, using:" - # " git submodule update --init dtc" - - dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) From 814e1110d51f287f5616ffc04b788f6477519560 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 15 Apr 2018 20:05:22 -0300 Subject: [PATCH 0394/2380] build: Silence dtc directory creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align with other mkdir calls. Signed-off-by: Jan Kiszka Reviewed-by: Philippe Mathieu-Daudé Message-Id: <0dd4c8f5-d60e-e564-652f-cd0101f6ee68@web.de> Message-Id: <20180415230522.24404-5-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d71dd5bea4..35554b5bef 100644 --- a/Makefile +++ b/Makefile @@ -485,7 +485,7 @@ subdir-dtc: .git-submodule-status dtc/libfdt dtc/tests $(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,) dtc/%: .git-submodule-status - mkdir -p $@ + @mkdir -p $@ # Overriding CFLAGS causes us to lose defines added in the sub-makefile. # Not overriding CFLAGS leads to mis-matches between compilation modes. From 3ff333effa319df6178f138d9cf32e3937419790 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 27 Apr 2018 14:05:15 +0200 Subject: [PATCH 0395/2380] pc-dimm: fix error messages if no slots were defined If no slots were defined we try to allocate an empty bitmap, which fails. Signed-off-by: David Hildenbrand Reviewed-by: Marcel Apfelbaum Reviewed-by: Thomas Huth Message-Id: <20180427120515.24067-1-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/mem/pc-dimm.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 0119c68e01..12da89d562 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -118,9 +118,16 @@ static int pc_dimm_slot2bitmap(Object *obj, void *opaque) int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp) { - unsigned long *bitmap = bitmap_new(max_slots); + unsigned long *bitmap; int slot = 0; + if (max_slots <= 0) { + error_setg(errp, "no slots where allocated, please specify " + "the 'slots' option"); + return slot; + } + + bitmap = bitmap_new(max_slots); object_child_foreach(qdev_get_machine(), pc_dimm_slot2bitmap, bitmap); /* check if requested slot is not occupied */ From 5b27a92dcc5afb11d38c7fe0a637d5d64d31108a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 May 2018 11:52:55 +0200 Subject: [PATCH 0396/2380] rename included C files to foo.inc.c, remove osdep.h osdep.h is only needed for files that are compiled directly. Remove it from included C source files, and rename them to *.inc.c so that scripts/clean-includes knows to skip them. Cc: Eric Blake Cc: Michael S. Tsirkin Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini --- target/cris/translate.c | 2 +- .../{translate_v10.c => translate_v10.inc.c} | 0 target/mips/translate.c | 2 +- ...{translate_init.c => translate_init.inc.c} | 0 target/ppc/int_helper.c | 2 +- .../ppc/{mfrom_table.c => mfrom_table.inc.c} | 0 target/ppc/translate.c | 2 +- ...{translate_init.c => translate_init.inc.c} | 1 - ui/vnc-enc-zrle.c | 22 +++++++++---------- ...enc-zrle-template.c => vnc-enc-zrle.inc.c} | 0 10 files changed, 15 insertions(+), 16 deletions(-) rename target/cris/{translate_v10.c => translate_v10.inc.c} (100%) rename target/mips/{translate_init.c => translate_init.inc.c} (100%) rename target/ppc/{mfrom_table.c => mfrom_table.inc.c} (100%) rename target/ppc/{translate_init.c => translate_init.inc.c} (99%) rename ui/{vnc-enc-zrle-template.c => vnc-enc-zrle.inc.c} (100%) diff --git a/target/cris/translate.c b/target/cris/translate.c index f51a731db9..ce1ee7a45f 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -3047,7 +3047,7 @@ static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc) return insn_len; } -#include "translate_v10.c" +#include "translate_v10.inc.c" /* * Delay slots on QEMU/CRIS. diff --git a/target/cris/translate_v10.c b/target/cris/translate_v10.inc.c similarity index 100% rename from target/cris/translate_v10.c rename to target/cris/translate_v10.inc.c diff --git a/target/mips/translate.c b/target/mips/translate.c index d05ee67e63..26f5404bae 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -20499,7 +20499,7 @@ void mips_tcg_init(void) "fcr31"); } -#include "translate_init.c" +#include "translate_init.inc.c" void cpu_mips_realize_env(CPUMIPSState *env) { diff --git a/target/mips/translate_init.c b/target/mips/translate_init.inc.c similarity index 100% rename from target/mips/translate_init.c rename to target/mips/translate_init.inc.c diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 35bdf09773..1607a7a42b 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -379,7 +379,7 @@ target_ulong helper_divso(CPUPPCState *env, target_ulong arg1, target_ulong helper_602_mfrom(target_ulong arg) { if (likely(arg < 602)) { -#include "mfrom_table.c" +#include "mfrom_table.inc.c" return mfrom_ROM_table[arg]; } else { return 0; diff --git a/target/ppc/mfrom_table.c b/target/ppc/mfrom_table.inc.c similarity index 100% rename from target/ppc/mfrom_table.c rename to target/ppc/mfrom_table.inc.c diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 2a4140f420..257badf149 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -6991,7 +6991,7 @@ GEN_HANDLER2_E(trechkpt, "trechkpt", 0x1F, 0x0E, 0x1F, 0x03FFF800, \ }; #include "helper_regs.h" -#include "translate_init.c" +#include "translate_init.inc.c" /*****************************************************************************/ /* Misc PowerPC helpers */ diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.inc.c similarity index 99% rename from target/ppc/translate_init.c rename to target/ppc/translate_init.inc.c index a72be6d121..a0b3f184b2 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.inc.c @@ -18,7 +18,6 @@ * License along with this library; if not, see . */ -#include "qemu/osdep.h" #include "disas/bfd.h" #include "exec/gdbstub.h" #include "kvm_ppc.h" diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c index fd63d4f688..7493a84723 100644 --- a/ui/vnc-enc-zrle.c +++ b/ui/vnc-enc-zrle.c @@ -199,56 +199,56 @@ static void zrle_write_u8(VncState *vs, uint8_t value) #define ZRLE_BPP 8 #define ZYWRLE_ENDIAN ENDIAN_NO -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZRLE_BPP #define ZRLE_BPP 15 #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_LITTLE -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_BIG -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZRLE_BPP #define ZRLE_BPP 16 #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_LITTLE -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_BIG -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZRLE_BPP #define ZRLE_BPP 32 #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_LITTLE -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_BIG -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #define ZRLE_COMPACT_PIXEL 24a #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_LITTLE -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_BIG -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZRLE_COMPACT_PIXEL #define ZRLE_COMPACT_PIXEL 24b #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_LITTLE -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_BIG -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZRLE_COMPACT_PIXEL #undef ZRLE_BPP diff --git a/ui/vnc-enc-zrle-template.c b/ui/vnc-enc-zrle.inc.c similarity index 100% rename from ui/vnc-enc-zrle-template.c rename to ui/vnc-enc-zrle.inc.c From dd577a26ff03b6829721b1ffbbf9e7c411b72378 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Fri, 27 Apr 2018 17:23:11 +0100 Subject: [PATCH 0397/2380] block/file-posix: implement bdrv_co_invalidate_cache() on Linux On Linux posix_fadvise(POSIX_FADV_DONTNEED) invalidates pages*. Use this to drop page cache on the destination host during shared storage migration. This way the destination host will read the latest copy of the data and will not use stale data from the page cache. The flow is as follows: 1. Source host writes out all dirty pages and inactivates drives. 2. QEMU_VM_EOF is sent on migration stream. 3. Destination host invalidates caches before accessing drives. This patch enables live migration even with -drive cache.direct=off. * Terms and conditions may apply, please see patch for details. Signed-off-by: Stefan Hajnoczi Reviewed-by: Fam Zheng Message-id: 20180427162312.18583-2-stefanha@redhat.com Signed-off-by: Stefan Hajnoczi --- block/file-posix.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/block/file-posix.c b/block/file-posix.c index 3794c0007a..3707ea2d1c 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2236,6 +2236,49 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, return ret | BDRV_BLOCK_OFFSET_VALID; } +static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs, + Error **errp) +{ + BDRVRawState *s = bs->opaque; + int ret; + + ret = fd_open(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "The file descriptor is not open"); + return; + } + + if (s->open_flags & O_DIRECT) { + return; /* No host kernel page cache */ + } + +#if defined(__linux__) + /* This sets the scene for the next syscall... */ + ret = bdrv_co_flush(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "flush failed"); + return; + } + + /* Linux does not invalidate pages that are dirty, locked, or mmapped by a + * process. These limitations are okay because we just fsynced the file, + * we don't use mmap, and the file should not be in use by other processes. + */ + ret = posix_fadvise(s->fd, 0, 0, POSIX_FADV_DONTNEED); + if (ret != 0) { /* the return value is a positive errno */ + error_setg_errno(errp, ret, "fadvise failed"); + return; + } +#else /* __linux__ */ + /* Do nothing. Live migration to a remote host with cache.direct=off is + * unsupported on other host operating systems. Cache consistency issues + * may occur but no error is reported here, partly because that's the + * historical behavior and partly because it's hard to differentiate valid + * configurations that should not cause errors. + */ +#endif /* !__linux__ */ +} + static coroutine_fn BlockAIOCB *raw_aio_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, BlockCompletionFunc *cb, void *opaque) @@ -2328,6 +2371,7 @@ BlockDriver bdrv_file = { .bdrv_co_create_opts = raw_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_co_block_status = raw_co_block_status, + .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, .bdrv_co_preadv = raw_co_preadv, @@ -2805,6 +2849,7 @@ static BlockDriver bdrv_host_device = { .bdrv_reopen_abort = raw_reopen_abort, .bdrv_co_create_opts = hdev_co_create_opts, .create_opts = &raw_create_opts, + .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes, .bdrv_co_preadv = raw_co_preadv, @@ -2927,6 +2972,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_reopen_abort = raw_reopen_abort, .bdrv_co_create_opts = hdev_co_create_opts, .create_opts = &raw_create_opts, + .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_preadv = raw_co_preadv, From 31be8a2a97ecba7d31a82932286489cac318e9e9 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Fri, 27 Apr 2018 17:23:12 +0100 Subject: [PATCH 0398/2380] block/file-posix: add x-check-page-cache=on|off option mincore(2) checks whether pages are resident. Use it to verify that page cache has been dropped. You can trigger a verification failure by mmapping the image file from another process that loads a byte from a page, forcing it to become resident. bdrv_co_invalidate_cache() will fail while that process is alive. Signed-off-by: Stefan Hajnoczi Reviewed-by: Fam Zheng Message-id: 20180427162312.18583-3-stefanha@redhat.com Signed-off-by: Stefan Hajnoczi --- block/file-posix.c | 100 ++++++++++++++++++++++++++++++++++++++++++- qapi/block-core.json | 7 ++- 2 files changed, 104 insertions(+), 3 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 3707ea2d1c..5a602cfe37 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -161,6 +161,7 @@ typedef struct BDRVRawState { bool page_cache_inconsistent:1; bool has_fallocate; bool needs_alignment; + bool check_cache_dropped; PRManager *pr_mgr; } BDRVRawState; @@ -168,6 +169,7 @@ typedef struct BDRVRawState { typedef struct BDRVRawReopenState { int fd; int open_flags; + bool check_cache_dropped; } BDRVRawReopenState; static int fd_open(BlockDriverState *bs); @@ -415,6 +417,11 @@ static QemuOptsList raw_runtime_opts = { .type = QEMU_OPT_STRING, .help = "id of persistent reservation manager object (default: none)", }, + { + .name = "x-check-cache-dropped", + .type = QEMU_OPT_BOOL, + .help = "check that page cache was dropped on live migration (default: off)" + }, { /* end of list */ } }, }; @@ -500,6 +507,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } } + s->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", + false); + s->open_flags = open_flags; raw_parse_flags(bdrv_flags, &s->open_flags); @@ -777,6 +787,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, { BDRVRawState *s; BDRVRawReopenState *rs; + QemuOpts *opts; int ret = 0; Error *local_err = NULL; @@ -787,6 +798,19 @@ static int raw_reopen_prepare(BDRVReopenState *state, state->opaque = g_new0(BDRVRawReopenState, 1); rs = state->opaque; + rs->fd = -1; + + /* Handle options changes */ + opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, state->options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + + rs->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", + s->check_cache_dropped); if (s->type == FTYPE_CD) { rs->open_flags |= O_NONBLOCK; @@ -794,8 +818,6 @@ static int raw_reopen_prepare(BDRVReopenState *state, raw_parse_flags(state->flags, &rs->open_flags); - rs->fd = -1; - int fcntl_flags = O_APPEND | O_NONBLOCK; #ifdef O_NOATIME fcntl_flags |= O_NOATIME; @@ -850,6 +872,8 @@ static int raw_reopen_prepare(BDRVReopenState *state, } } +out: + qemu_opts_del(opts); return ret; } @@ -858,6 +882,7 @@ static void raw_reopen_commit(BDRVReopenState *state) BDRVRawReopenState *rs = state->opaque; BDRVRawState *s = state->bs->opaque; + s->check_cache_dropped = rs->check_cache_dropped; s->open_flags = rs->open_flags; qemu_close(s->fd); @@ -2236,6 +2261,73 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, return ret | BDRV_BLOCK_OFFSET_VALID; } +#if defined(__linux__) +/* Verify that the file is not in the page cache */ +static void check_cache_dropped(BlockDriverState *bs, Error **errp) +{ + const size_t window_size = 128 * 1024 * 1024; + BDRVRawState *s = bs->opaque; + void *window = NULL; + size_t length = 0; + unsigned char *vec; + size_t page_size; + off_t offset; + off_t end; + + /* mincore(2) page status information requires 1 byte per page */ + page_size = sysconf(_SC_PAGESIZE); + vec = g_malloc(DIV_ROUND_UP(window_size, page_size)); + + end = raw_getlength(bs); + + for (offset = 0; offset < end; offset += window_size) { + void *new_window; + size_t new_length; + size_t vec_end; + size_t i; + int ret; + + /* Unmap previous window if size has changed */ + new_length = MIN(end - offset, window_size); + if (new_length != length) { + munmap(window, length); + window = NULL; + length = 0; + } + + new_window = mmap(window, new_length, PROT_NONE, MAP_PRIVATE, + s->fd, offset); + if (new_window == MAP_FAILED) { + error_setg_errno(errp, errno, "mmap failed"); + break; + } + + window = new_window; + length = new_length; + + ret = mincore(window, length, vec); + if (ret < 0) { + error_setg_errno(errp, errno, "mincore failed"); + break; + } + + vec_end = DIV_ROUND_UP(length, page_size); + for (i = 0; i < vec_end; i++) { + if (vec[i] & 0x1) { + error_setg(errp, "page cache still in use!"); + break; + } + } + } + + if (window) { + munmap(window, length); + } + + g_free(vec); +} +#endif /* __linux__ */ + static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs, Error **errp) { @@ -2269,6 +2361,10 @@ static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs, error_setg_errno(errp, ret, "fadvise failed"); return; } + + if (s->check_cache_dropped) { + check_cache_dropped(bs, errp); + } #else /* __linux__ */ /* Do nothing. Live migration to a remote host with cache.direct=off is * unsupported on other host operating systems. Cache consistency issues diff --git a/qapi/block-core.json b/qapi/block-core.json index c50517bff3..21c3470234 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2530,6 +2530,10 @@ # @locking: whether to enable file locking. If set to 'auto', only enable # when Open File Descriptor (OFD) locking API is available # (default: auto, since 2.10) +# @x-check-cache-dropped: whether to check that page cache was dropped on live +# migration. May cause noticeable delays if the image +# file is large, do not use in production. +# (default: off) (since: 2.13) # # Since: 2.9 ## @@ -2537,7 +2541,8 @@ 'data': { 'filename': 'str', '*pr-manager': 'str', '*locking': 'OnOffAuto', - '*aio': 'BlockdevAioOptions' } } + '*aio': 'BlockdevAioOptions', + '*x-check-cache-dropped': 'bool' } } ## # @BlockdevOptionsNull: From cbf358a667eaac4d305b43a47effd483021f38d7 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 10 May 2018 01:11:18 +0200 Subject: [PATCH 0399/2380] linux-user: define correct fcntl() values for sparc Signed-off-by: Laurent Vivier Reviewed-by: Max Filippov Message-Id: <20180509231123.20864-2-laurent@vivier.eu> --- linux-user/syscall_defs.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 361bb83a29..ec3f561685 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2441,6 +2441,15 @@ struct target_statfs64 { #define TARGET_F_SETLKW 7 #define TARGET_F_GETOWN 11 /* for sockets. */ #define TARGET_F_SETOWN 12 /* for sockets. */ +#elif defined(TARGET_SPARC) +#define TARGET_F_RDLCK 1 +#define TARGET_F_WRLCK 2 +#define TARGET_F_UNLCK 3 +#define TARGET_F_GETOWN 5 /* for sockets. */ +#define TARGET_F_SETOWN 6 /* for sockets. */ +#define TARGET_F_GETLK 7 +#define TARGET_F_SETLK 8 +#define TARGET_F_SETLKW 9 #else #define TARGET_F_GETLK 5 #define TARGET_F_SETLK 6 From d3c6e8e98c00a801620ed7de7642c3bb957e15c6 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 10 May 2018 01:11:19 +0200 Subject: [PATCH 0400/2380] linux-user: fix flock/flock64 padding include/uapi/asm-generic/fcntl.h insert a padding macro at the end of the structures flock and flock64. This macro is defined to "short __unused;" on sparc, and "long pad[4]" on mips. Signed-off-by: Laurent Vivier Reviewed-by: Max Filippov Message-Id: <20180509231123.20864-3-laurent@vivier.eu> --- linux-user/syscall_defs.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index ec3f561685..e4cd87cc00 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2643,6 +2643,17 @@ struct target_statfs64 { #define TARGET_O_SYNC (TARGET___O_SYNC | TARGET_O_DSYNC) #endif +#if defined(TARGET_SPARC) +#define TARGET_ARCH_FLOCK_PAD abi_short __unused; +#define TARGET_ARCH_FLOCK64_PAD abi_short __unused; +#elif defined(TARGET_MIPS) +#define TARGET_ARCH_FLOCK_PAD abi_long pad[4]; +#define TARGET_ARCH_FLOCK64_PAD +#else +#define TARGET_ARCH_FLOCK_PAD +#define TARGET_ARCH_FLOCK64_PAD +#endif + struct target_flock { short l_type; short l_whence; @@ -2652,9 +2663,7 @@ struct target_flock { abi_long l_sysid; #endif int l_pid; -#if defined(TARGET_MIPS) - abi_long pad[4]; -#endif + TARGET_ARCH_FLOCK_PAD }; struct target_flock64 { @@ -2663,6 +2672,7 @@ struct target_flock64 { abi_llong l_start; abi_llong l_len; abi_int l_pid; + TARGET_ARCH_FLOCK64_PAD }; struct target_f_owner_ex { From 534cdbf56c2a72952ffde9a258160f5586cea886 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 10 May 2018 01:11:20 +0200 Subject: [PATCH 0401/2380] linux-user: update sparc/syscall_nr.h to linux header 4.16 And kill sys_aplib, add sys_sync_file_range: on sparc, since linux 2.6.17, aplib syscall has been replaced by sync_file_range syscall. (289eee6fa78e ["SPARC]: Wire up sys_sync_file_range() into syscall tables.") The syscall has been removed in linux v2.5.71 (6196166fad "[SPARC64]: Kill sys_aplib.") Signed-off-by: Laurent Vivier Reviewed-by: Max Filippov Message-Id: <20180509231123.20864-4-laurent@vivier.eu> --- linux-user/sparc/syscall_nr.h | 32 +++++++++++++++++++++++++++++++- linux-user/sparc64/syscall_nr.h | 20 ++++++++++++-------- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/linux-user/sparc/syscall_nr.h b/linux-user/sparc/syscall_nr.h index e713c9d5f4..2d77e19bec 100644 --- a/linux-user/sparc/syscall_nr.h +++ b/linux-user/sparc/syscall_nr.h @@ -22,6 +22,7 @@ #define TARGET_NR_capset 22 /* Linux Specific */ #define TARGET_NR_setuid 23 /* Implemented via setreuid in SunOS */ #define TARGET_NR_getuid 24 /* Common */ +#define TARGET_NR_vmsplice 25 #define TARGET_NR_ptrace 26 /* Common */ #define TARGET_NR_alarm 27 /* Implemented via setitimer in SunOS */ #define TARGET_NR_sigaltstack 28 /* Common */ @@ -135,6 +136,7 @@ #define TARGET_NR_rmdir 137 /* Common */ #define TARGET_NR_utimes 138 /* SunOS Specific */ #define TARGET_NR_stat64 139 /* Linux sparc32 Specific */ +#define TARGET_NR_sendfile64 140 #define TARGET_NR_getpeername 141 /* Common */ #define TARGET_NR_futex 142 /* gethostid under SunOS */ #define TARGET_NR_gettid 143 /* ENOSYS under SunOS */ @@ -145,29 +147,51 @@ #define TARGET_NR_pciconfig_read 148 /* ENOSYS under SunOS */ #define TARGET_NR_pciconfig_write 149 /* ENOSYS under SunOS */ #define TARGET_NR_getsockname 150 /* Common */ +#define TARGET_NR_inotify_init 151 +#define TARGET_NR_inotify_add_watch 152 #define TARGET_NR_poll 153 /* Common */ #define TARGET_NR_getdents64 154 /* Linux specific */ #define TARGET_NR_fcntl64 155 /* Linux sparc32 Specific */ +#define TARGET_NR_inotify_rm_watch 156 #define TARGET_NR_statfs 157 /* Common */ #define TARGET_NR_fstatfs 158 /* Common */ #define TARGET_NR_umount 159 /* Common */ +#define TARGET_NR_sched_set_affinity 160 +#define TARGET_NR_sched_get_affinity 161 #define TARGET_NR_getdomainname 162 /* SunOS Specific */ #define TARGET_NR_setdomainname 163 /* Common */ #define TARGET_NR_quotactl 165 /* Common */ #define TARGET_NR_set_tid_address 166 /* Linux specific, exportfs under SunOS */ #define TARGET_NR_mount 167 /* Common */ #define TARGET_NR_ustat 168 /* Common */ +#define TARGET_NR_setxattr 169 +#define TARGET_NR_lsetxattr 170 +#define TARGET_NR_fsetxattr 171 +#define TARGET_NR_getxattr 172 +#define TARGET_NR_lgetxattr 173 #define TARGET_NR_getdents 174 /* Common */ #define TARGET_NR_setsid 175 /* Common */ #define TARGET_NR_fchdir 176 /* Common */ +#define TARGET_NR_fgetxattr 177 +#define TARGET_NR_listxattr 178 +#define TARGET_NR_llistxattr 179 +#define TARGET_NR_flistxattr 180 +#define TARGET_NR_removexattr 181 +#define TARGET_NR_lremovexattr 182 #define TARGET_NR_sigpending 183 /* Common */ #define TARGET_NR_query_module 184 /* Linux Specific */ #define TARGET_NR_setpgid 185 /* Common */ +#define TARGET_NR_fremovexattr 186 #define TARGET_NR_tkill 187 /* SunOS: fpathconf */ #define TARGET_NR_exit_group 188 /* Linux specific, sysconf undef SunOS */ #define TARGET_NR_uname 189 /* Linux Specific */ #define TARGET_NR_init_module 190 /* Linux Specific */ #define TARGET_NR_personality 191 /* Linux Specific */ +#define TARGET_NR_remap_file_pages 192 +#define TARGET_NR_epoll_create 193 +#define TARGET_NR_epoll_ctl 194 +#define TARGET_NR_epoll_wait 195 +#define TARGET_NR_ioprio_set 196 #define TARGET_NR_getppid 197 /* Linux Specific */ #define TARGET_NR_sigaction 198 /* Linux Specific */ #define TARGET_NR_sgetmask 199 /* Linux Specific */ @@ -189,6 +213,7 @@ #define TARGET_NR_ipc 215 /* Linux Specific */ #define TARGET_NR_sigreturn 216 /* Linux Specific */ #define TARGET_NR_clone 217 /* Linux Specific */ +#define TARGET_NR_ioprio_get 218 #define TARGET_NR_adjtimex 219 /* Linux Specific */ #define TARGET_NR_sigprocmask 220 /* Linux Specific */ #define TARGET_NR_create_module 221 /* Linux Specific */ @@ -202,6 +227,7 @@ #define TARGET_NR_setfsgid 229 /* Linux Specific */ #define TARGET_NR__newselect 230 /* Linux Specific */ #define TARGET_NR_time 231 /* Linux Specific */ +#define TARGET_NR_splice 232 #define TARGET_NR_stime 233 /* Linux Specific */ #define TARGET_NR_statfs64 234 /* Linux Specific */ #define TARGET_NR_fstatfs64 235 /* Linux Specific */ @@ -224,7 +250,7 @@ #define TARGET_NR_getsid 252 #define TARGET_NR_fdatasync 253 #define TARGET_NR_nfsservctl 254 -#define TARGET_NR_aplib 255 +#define TARGET_NR_sync_file_range 255 #define TARGET_NR_clock_settime 256 #define TARGET_NR_clock_gettime 257 #define TARGET_NR_clock_getres 258 @@ -326,3 +352,7 @@ #define TARGET_NR_listen 354 #define TARGET_NR_setsockopt 355 #define TARGET_NR_mlock2 356 +#define TARGET_NR_copy_file_range 357 +#define TARGET_NR_preadv2 358 +#define TARGET_NR_pwritev2 359 +#define TARGET_NR_statx 360 diff --git a/linux-user/sparc64/syscall_nr.h b/linux-user/sparc64/syscall_nr.h index 2b49ead267..9391645598 100644 --- a/linux-user/sparc64/syscall_nr.h +++ b/linux-user/sparc64/syscall_nr.h @@ -23,7 +23,7 @@ #define TARGET_NR_capset 22 /* Linux Specific */ #define TARGET_NR_setuid 23 /* Implemented via setreuid in SunOS */ #define TARGET_NR_getuid 24 /* Common */ -/* #define TARGET_NR_time alias 25 ENOSYS under SunOS */ +#define TARGET_NR_vmsplice 25 #define TARGET_NR_ptrace 26 /* Common */ #define TARGET_NR_alarm 27 /* Implemented via setitimer in SunOS */ #define TARGET_NR_sigaltstack 28 /* Common */ @@ -149,8 +149,8 @@ #define TARGET_NR_pciconfig_read 148 /* ENOSYS under SunOS */ #define TARGET_NR_pciconfig_write 149 /* ENOSYS under SunOS */ #define TARGET_NR_getsockname 150 /* Common */ -/* #define TARGET_NR_getmsg 151 SunOS Specific */ -/* #define TARGET_NR_putmsg 152 SunOS Specific */ +#define TARGET_NR_inotify_init 151 +#define TARGET_NR_inotify_add_watch 152 #define TARGET_NR_poll 153 /* Common */ #define TARGET_NR_getdents64 154 /* Linux specific */ #define TARGET_NR_fcntl64 155 /* Linux sparc32 Specific */ @@ -194,7 +194,7 @@ #define TARGET_NR_epoll_create 193 /* Linux Specific */ #define TARGET_NR_epoll_ctl 194 /* Linux Specific */ #define TARGET_NR_epoll_wait 195 /* Linux Specific */ -/* #define TARGET_NR_ulimit 196 Linux Specific */ +#define TARGET_NR_ioprio_set 196 #define TARGET_NR_getppid 197 /* Linux Specific */ #define TARGET_NR_sigaction 198 /* Linux Specific */ #define TARGET_NR_sgetmask 199 /* Linux Specific */ @@ -216,7 +216,7 @@ #define TARGET_NR_ipc 215 /* Linux Specific */ #define TARGET_NR_sigreturn 216 /* Linux Specific */ #define TARGET_NR_clone 217 /* Linux Specific */ -/* #define TARGET_NR_modify_ldt 218 Linux Specific - i386 specific, unused */ +#define TARGET_NR_ioprio_get 218 #define TARGET_NR_adjtimex 219 /* Linux Specific */ #define TARGET_NR_sigprocmask 220 /* Linux Specific */ #define TARGET_NR_create_module 221 /* Linux Specific */ @@ -230,7 +230,7 @@ #define TARGET_NR_setfsgid 229 /* Linux Specific */ #define TARGET_NR__newselect 230 /* Linux Specific */ #define TARGET_NR_time 231 /* Linux sparc32 */ -/* #define TARGET_NR_oldstat 232 Linux Specific */ +#define TARGET_NR_splice 232 #define TARGET_NR_stime 233 /* Linux Specific */ #define TARGET_NR_statfs64 234 /* Linux Specific */ #define TARGET_NR_fstatfs64 235 /* Linux Specific */ @@ -253,7 +253,7 @@ #define TARGET_NR_getsid 252 #define TARGET_NR_fdatasync 253 #define TARGET_NR_nfsservctl 254 -#define TARGET_NR_aplib 255 +#define TARGET_NR_sync_file_range 255 #define TARGET_NR_clock_settime 256 #define TARGET_NR_clock_gettime 257 #define TARGET_NR_clock_getres 258 @@ -310,7 +310,7 @@ #define TARGET_NR_epoll_pwait 309 #define TARGET_NR_utimensat 310 #define TARGET_NR_signalfd 311 -#define TARGET_NR_timerfd 312 +#define TARGET_NR_timerfd_create 312 #define TARGET_NR_eventfd 313 #define TARGET_NR_fallocate 314 #define TARGET_NR_timerfd_settime 315 @@ -355,3 +355,7 @@ #define TARGET_NR_listen 354 #define TARGET_NR_setsockopt 355 #define TARGET_NR_mlock2 356 +#define TARGET_NR_copy_file_range 357 +#define TARGET_NR_preadv2 358 +#define TARGET_NR_pwritev2 359 +#define TARGET_NR_statx 360 From ae68ad9fee4e51eae26e34b43a9e6999bd1c6819 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 10 May 2018 01:11:21 +0200 Subject: [PATCH 0402/2380] linux-user: fix conversion of flock/flock64 l_type field As l_type values (F_RDLCK, F_WRLCK, F_UNLCK, F_EXLCK, F_SHLCK) are not bitmasks, we can't use target_to_host_bitmask() and host_to_target_bitmask() to convert them. Introduce target_to_host_flock() and host_to_target_flock() to convert values between host and target. Signed-off-by: Laurent Vivier Reviewed-by: Max Filippov Message-Id: <20180509231123.20864-5-laurent@vivier.eu> --- linux-user/syscall.c | 66 ++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e4825747f9..af8603f1b7 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6546,28 +6546,50 @@ static int target_to_host_fcntl_cmd(int cmd) return -TARGET_EINVAL; } -#define TRANSTBL_CONVERT(a) { -1, TARGET_##a, -1, a } -static const bitmask_transtbl flock_tbl[] = { - TRANSTBL_CONVERT(F_RDLCK), - TRANSTBL_CONVERT(F_WRLCK), - TRANSTBL_CONVERT(F_UNLCK), - TRANSTBL_CONVERT(F_EXLCK), - TRANSTBL_CONVERT(F_SHLCK), - { 0, 0, 0, 0 } -}; +#define FLOCK_TRANSTBL \ + switch (type) { \ + TRANSTBL_CONVERT(F_RDLCK); \ + TRANSTBL_CONVERT(F_WRLCK); \ + TRANSTBL_CONVERT(F_UNLCK); \ + TRANSTBL_CONVERT(F_EXLCK); \ + TRANSTBL_CONVERT(F_SHLCK); \ + } + +static int target_to_host_flock(int type) +{ +#define TRANSTBL_CONVERT(a) case TARGET_##a: return a + FLOCK_TRANSTBL +#undef TRANSTBL_CONVERT + return -TARGET_EINVAL; +} + +static int host_to_target_flock(int type) +{ +#define TRANSTBL_CONVERT(a) case a: return TARGET_##a + FLOCK_TRANSTBL +#undef TRANSTBL_CONVERT + /* if we don't know how to convert the value coming + * from the host we copy to the target field as-is + */ + return type; +} static inline abi_long copy_from_user_flock(struct flock64 *fl, abi_ulong target_flock_addr) { struct target_flock *target_fl; - short l_type; + int l_type; if (!lock_user_struct(VERIFY_READ, target_fl, target_flock_addr, 1)) { return -TARGET_EFAULT; } __get_user(l_type, &target_fl->l_type); - fl->l_type = target_to_host_bitmask(l_type, flock_tbl); + l_type = target_to_host_flock(l_type); + if (l_type < 0) { + return l_type; + } + fl->l_type = l_type; __get_user(fl->l_whence, &target_fl->l_whence); __get_user(fl->l_start, &target_fl->l_start); __get_user(fl->l_len, &target_fl->l_len); @@ -6586,7 +6608,7 @@ static inline abi_long copy_to_user_flock(abi_ulong target_flock_addr, return -TARGET_EFAULT; } - l_type = host_to_target_bitmask(fl->l_type, flock_tbl); + l_type = host_to_target_flock(fl->l_type); __put_user(l_type, &target_fl->l_type); __put_user(fl->l_whence, &target_fl->l_whence); __put_user(fl->l_start, &target_fl->l_start); @@ -6604,14 +6626,18 @@ static inline abi_long copy_from_user_oabi_flock64(struct flock64 *fl, abi_ulong target_flock_addr) { struct target_oabi_flock64 *target_fl; - short l_type; + int l_type; if (!lock_user_struct(VERIFY_READ, target_fl, target_flock_addr, 1)) { return -TARGET_EFAULT; } __get_user(l_type, &target_fl->l_type); - fl->l_type = target_to_host_bitmask(l_type, flock_tbl); + l_type = target_to_host_flock(l_type); + if (l_type < 0) { + return l_type; + } + fl->l_type = l_type; __get_user(fl->l_whence, &target_fl->l_whence); __get_user(fl->l_start, &target_fl->l_start); __get_user(fl->l_len, &target_fl->l_len); @@ -6630,7 +6656,7 @@ static inline abi_long copy_to_user_oabi_flock64(abi_ulong target_flock_addr, return -TARGET_EFAULT; } - l_type = host_to_target_bitmask(fl->l_type, flock_tbl); + l_type = host_to_target_flock(fl->l_type); __put_user(l_type, &target_fl->l_type); __put_user(fl->l_whence, &target_fl->l_whence); __put_user(fl->l_start, &target_fl->l_start); @@ -6645,14 +6671,18 @@ static inline abi_long copy_from_user_flock64(struct flock64 *fl, abi_ulong target_flock_addr) { struct target_flock64 *target_fl; - short l_type; + int l_type; if (!lock_user_struct(VERIFY_READ, target_fl, target_flock_addr, 1)) { return -TARGET_EFAULT; } __get_user(l_type, &target_fl->l_type); - fl->l_type = target_to_host_bitmask(l_type, flock_tbl); + l_type = target_to_host_flock(l_type); + if (l_type < 0) { + return l_type; + } + fl->l_type = l_type; __get_user(fl->l_whence, &target_fl->l_whence); __get_user(fl->l_start, &target_fl->l_start); __get_user(fl->l_len, &target_fl->l_len); @@ -6671,7 +6701,7 @@ static inline abi_long copy_to_user_flock64(abi_ulong target_flock_addr, return -TARGET_EFAULT; } - l_type = host_to_target_bitmask(fl->l_type, flock_tbl); + l_type = host_to_target_flock(fl->l_type); __put_user(l_type, &target_fl->l_type); __put_user(fl->l_whence, &target_fl->l_whence); __put_user(fl->l_start, &target_fl->l_start); From 0562384910bb304b6c811ecac0ad95e699a4c368 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 10 May 2018 01:11:22 +0200 Subject: [PATCH 0403/2380] linux-user: add sparc/sparc64 specific errno Copied from linux/arch/sparc/include/uapi/asm/errno.h Signed-off-by: Laurent Vivier Reviewed-by: Max Filippov Message-Id: <20180509231123.20864-6-laurent@vivier.eu> --- linux-user/sparc/target_errno.h | 207 ++++++++++++++++++++++++++++ linux-user/sparc/target_syscall.h | 2 + linux-user/sparc64/target_syscall.h | 3 +- 3 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 linux-user/sparc/target_errno.h diff --git a/linux-user/sparc/target_errno.h b/linux-user/sparc/target_errno.h new file mode 100644 index 0000000000..9b846899cd --- /dev/null +++ b/linux-user/sparc/target_errno.h @@ -0,0 +1,207 @@ +#ifndef SPARC_TARGET_ERRNO_H +#define SPARC_TARGET_ERRNO_H + +/* Target errno definitions taken from asm-sparc/errno.h */ +#undef TARGET_EWOULDBLOCK +#define TARGET_EWOULDBLOCK TARGET_EAGAIN /* Operation would block */ +#undef TARGET_EINPROGRESS +#define TARGET_EINPROGRESS 36 /* Operation now in progress */ +#undef TARGET_EALREADY +#define TARGET_EALREADY 37 /* Operation already in progress */ +#undef TARGET_ENOTSOCK +#define TARGET_ENOTSOCK 38 /* Socket operation on non-socket */ +#undef TARGET_EDESTADDRREQ +#define TARGET_EDESTADDRREQ 39 /* Destination address required */ +#undef TARGET_EMSGSIZE +#define TARGET_EMSGSIZE 40 /* Message too long */ +#undef TARGET_EPROTOTYPE +#define TARGET_EPROTOTYPE 41 /* Protocol wrong type for socket */ +#undef TARGET_ENOPROTOOPT +#define TARGET_ENOPROTOOPT 42 /* Protocol not available */ +#undef TARGET_EPROTONOSUPPORT +#define TARGET_EPROTONOSUPPORT 43 /* Protocol not supported */ +#undef TARGET_ESOCKTNOSUPPORT +#define TARGET_ESOCKTNOSUPPORT 44 /* Socket type not supported */ +#undef TARGET_EOPNOTSUPP +#define TARGET_EOPNOTSUPP 45 /* Op not supported on transport endpoint */ +#undef TARGET_EPFNOSUPPORT +#define TARGET_EPFNOSUPPORT 46 /* Protocol family not supported */ +#undef TARGET_EAFNOSUPPORT +#define TARGET_EAFNOSUPPORT 47 /* Address family not supported by protocol */ +#undef TARGET_EADDRINUSE +#define TARGET_EADDRINUSE 48 /* Address already in use */ +#undef TARGET_EADDRNOTAVAIL +#define TARGET_EADDRNOTAVAIL 49 /* Cannot assign requested address */ +#undef TARGET_ENETDOWN +#define TARGET_ENETDOWN 50 /* Network is down */ +#undef TARGET_ENETUNREACH +#define TARGET_ENETUNREACH 51 /* Network is unreachable */ +#undef TARGET_ENETRESET +#define TARGET_ENETRESET 52 /* Net dropped connection because of reset */ +#undef TARGET_ECONNABORTED +#define TARGET_ECONNABORTED 53 /* Software caused connection abort */ +#undef TARGET_ECONNRESET +#define TARGET_ECONNRESET 54 /* Connection reset by peer */ +#undef TARGET_ENOBUFS +#define TARGET_ENOBUFS 55 /* No buffer space available */ +#undef TARGET_EISCONN +#define TARGET_EISCONN 56 /* Transport endpoint is already connected */ +#undef TARGET_ENOTCONN +#define TARGET_ENOTCONN 57 /* Transport endpoint is not connected */ +#undef TARGET_ESHUTDOWN +#define TARGET_ESHUTDOWN 58 /* No send after transport endpoint shutdown*/ +#undef TARGET_ETOOMANYREFS +#define TARGET_ETOOMANYREFS 59 /* Too many references: cannot splice */ +#undef TARGET_ETIMEDOUT +#define TARGET_ETIMEDOUT 60 /* Connection timed out */ +#undef TARGET_ECONNREFUSED +#define TARGET_ECONNREFUSED 61 /* Connection refused */ +#undef TARGET_ELOOP +#define TARGET_ELOOP 62 /* Too many symbolic links encountered */ +#undef TARGET_ENAMETOOLONG +#define TARGET_ENAMETOOLONG 63 /* File name too long */ +#undef TARGET_EHOSTDOWN +#define TARGET_EHOSTDOWN 64 /* Host is down */ +#undef TARGET_EHOSTUNREACH +#define TARGET_EHOSTUNREACH 65 /* No route to host */ +#undef TARGET_ENOTEMPTY +#define TARGET_ENOTEMPTY 66 /* Directory not empty */ +#undef TARGET_EPROCLIM +#define TARGET_EPROCLIM 67 /* SUNOS: Too many processes */ +#undef TARGET_EUSERS +#define TARGET_EUSERS 68 /* Too many users */ +#undef TARGET_EDQUOT +#define TARGET_EDQUOT 69 /* Quota exceeded */ +#undef TARGET_ESTALE +#define TARGET_ESTALE 70 /* Stale file handle */ +#undef TARGET_EREMOTE +#define TARGET_EREMOTE 71 /* Object is remote */ +#undef TARGET_ENOSTR +#define TARGET_ENOSTR 72 /* Device not a stream */ +#undef TARGET_ETIME +#define TARGET_ETIME 73 /* Timer expired */ +#undef TARGET_ENOSR +#define TARGET_ENOSR 74 /* Out of streams resources */ +#undef TARGET_ENOMSG +#define TARGET_ENOMSG 75 /* No message of desired type */ +#undef TARGET_EBADMSG +#define TARGET_EBADMSG 76 /* Not a data message */ +#undef TARGET_EIDRM +#define TARGET_EIDRM 77 /* Identifier removed */ +#undef TARGET_EDEADLK +#define TARGET_EDEADLK 78 /* Resource deadlock would occur */ +#undef TARGET_ENOLCK +#define TARGET_ENOLCK 79 /* No record locks available */ +#undef TARGET_ENONET +#define TARGET_ENONET 80 /* Machine is not on the network */ +#undef TARGET_ERREMOTE +#define TARGET_ERREMOTE 81 /* SunOS: Too many lvls of remote in path */ +#undef TARGET_ENOLINK +#define TARGET_ENOLINK 82 /* Link has been severed */ +#undef TARGET_EADV +#define TARGET_EADV 83 /* Advertise error */ +#undef TARGET_ESRMNT +#define TARGET_ESRMNT 84 /* Srmount error */ +#undef TARGET_ECOMM +#define TARGET_ECOMM 85 /* Communication error on send */ +#undef TARGET_EPROTO +#define TARGET_EPROTO 86 /* Protocol error */ +#undef TARGET_EMULTIHOP +#define TARGET_EMULTIHOP 87 /* Multihop attempted */ +#undef TARGET_EDOTDOT +#define TARGET_EDOTDOT 88 /* RFS specific error */ +#undef TARGET_EREMCHG +#define TARGET_EREMCHG 89 /* Remote address changed */ +#undef TARGET_ENOSYS +#define TARGET_ENOSYS 90 /* Function not implemented */ +#undef TARGET_ESTRPIPE +#define TARGET_ESTRPIPE 91 /* Streams pipe error */ +#undef TARGET_EOVERFLOW +#define TARGET_EOVERFLOW 92 /* Value too large for defined data type */ +#undef TARGET_EBADFD +#define TARGET_EBADFD 93 /* File descriptor in bad state */ +#undef TARGET_ECHRNG +#define TARGET_ECHRNG 94 /* Channel number out of range */ +#undef TARGET_EL2NSYNC +#define TARGET_EL2NSYNC 95 /* Level 2 not synchronized */ +#undef TARGET_EL3HLT +#define TARGET_EL3HLT 96 /* Level 3 halted */ +#undef TARGET_EL3RST +#define TARGET_EL3RST 97 /* Level 3 reset */ +#undef TARGET_ELNRNG +#define TARGET_ELNRNG 98 /* Link number out of range */ +#undef TARGET_EUNATCH +#define TARGET_EUNATCH 99 /* Protocol driver not attached */ +#undef TARGET_ENOCSI +#define TARGET_ENOCSI 100 /* No CSI structure available */ +#undef TARGET_EL2HLT +#define TARGET_EL2HLT 101 /* Level 2 halted */ +#undef TARGET_EBADE +#define TARGET_EBADE 102 /* Invalid exchange */ +#undef TARGET_EBADR +#define TARGET_EBADR 103 /* Invalid request descriptor */ +#undef TARGET_EXFULL +#define TARGET_EXFULL 104 /* Exchange full */ +#undef TARGET_ENOANO +#define TARGET_ENOANO 105 /* No anode */ +#undef TARGET_EBADRQC +#define TARGET_EBADRQC 106 /* Invalid request code */ +#undef TARGET_EBADSLT +#define TARGET_EBADSLT 107 /* Invalid slot */ +#undef TARGET_EDEADLOCK +#define TARGET_EDEADLOCK 108 /* File locking deadlock error */ +#undef TARGET_EBFONT +#define TARGET_EBFONT 109 /* Bad font file format */ +#undef TARGET_ELIBEXEC +#define TARGET_ELIBEXEC 110 /* Cannot exec a shared library directly */ +#undef TARGET_ENODATA +#define TARGET_ENODATA 111 /* No data available */ +#undef TARGET_ELIBBAD +#define TARGET_ELIBBAD 112 /* Accessing a corrupted shared library */ +#undef TARGET_ENOPKG +#define TARGET_ENOPKG 113 /* Package not installed */ +#undef TARGET_ELIBACC +#define TARGET_ELIBACC 114 /* Can not access a needed shared library */ +#undef TARGET_ENOTUNIQ +#define TARGET_ENOTUNIQ 115 /* Name not unique on network */ +#undef TARGET_ERESTART +#define TARGET_ERESTART 116 /* Interrupted syscall should be restarted */ +#undef TARGET_EUCLEAN +#define TARGET_EUCLEAN 117 /* Structure needs cleaning */ +#undef TARGET_ENOTNAM +#define TARGET_ENOTNAM 118 /* Not a XENIX named type file */ +#undef TARGET_ENAVAIL +#define TARGET_ENAVAIL 119 /* No XENIX semaphores available */ +#undef TARGET_EISNAM +#define TARGET_EISNAM 120 /* Is a named type file */ +#undef TARGET_EREMOTEIO +#define TARGET_EREMOTEIO 121 /* Remote I/O error */ +#undef TARGET_EILSEQ +#define TARGET_EILSEQ 122 /* Illegal byte sequence */ +#undef TARGET_ELIBMAX +#define TARGET_ELIBMAX 123 /* Atmpt to link in too many shared libs */ +#undef TARGET_ELIBSCN +#define TARGET_ELIBSCN 124 /* .lib section in a.out corrupted */ +#undef TARGET_ENOMEDIUM +#define TARGET_ENOMEDIUM 125 /* No medium found */ +#undef TARGET_EMEDIUMTYPE +#define TARGET_EMEDIUMTYPE 126 /* Wrong medium type */ +#undef TARGET_ECANCELED +#define TARGET_ECANCELED 127 /* Operation Cancelled */ +#undef TARGET_ENOKEY +#define TARGET_ENOKEY 128 /* Required key not available */ +#undef TARGET_EKEYEXPIRED +#define TARGET_EKEYEXPIRED 129 /* Key has expired */ +#undef TARGET_EKEYREVOKED +#define TARGET_EKEYREVOKED 130 /* Key has been revoked */ +#undef TARGET_EKEYREJECTED +#define TARGET_EKEYREJECTED 131 /* Key was rejected by service */ +#undef TARGET_EOWNERDEAD +#define TARGET_EOWNERDEAD 132 /* Owner died */ +#undef TARGET_ENOTRECOVERABLE +#define TARGET_ENOTRECOVERABLE 133 /* State not recoverable */ +#undef TARGET_ERFKILL +#define TARGET_ERFKILL 134 /* Operation not possible due to RF-kill */ +#undef TARGET_EHWPOISON +#define TARGET_EHWPOISON 135 /* Memory page has hardware error */ +#endif diff --git a/linux-user/sparc/target_syscall.h b/linux-user/sparc/target_syscall.h index 5f09abfe89..3725875fcf 100644 --- a/linux-user/sparc/target_syscall.h +++ b/linux-user/sparc/target_syscall.h @@ -1,6 +1,8 @@ #ifndef SPARC_TARGET_SYSCALL_H #define SPARC_TARGET_SYSCALL_H +#include "target_errno.h" + struct target_pt_regs { abi_ulong psr; abi_ulong pc; diff --git a/linux-user/sparc64/target_syscall.h b/linux-user/sparc64/target_syscall.h index 2cbbaaed1b..34f49df4a1 100644 --- a/linux-user/sparc64/target_syscall.h +++ b/linux-user/sparc64/target_syscall.h @@ -1,6 +1,8 @@ #ifndef SPARC64_TARGET_SYSCALL_H #define SPARC64_TARGET_SYSCALL_H +#include "../sparc/target_errno.h" + struct target_pt_regs { abi_ulong u_regs[16]; abi_ulong tstate; @@ -29,5 +31,4 @@ static inline abi_ulong target_shmlba(CPUSPARCState *env) { return MAX(TARGET_PAGE_SIZE, 16 * 1024); } - #endif /* SPARC64_TARGET_SYSCALL_H */ From 9a93c152fcdb4ab2cd85094487b33578fd693915 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 10 May 2018 01:11:23 +0200 Subject: [PATCH 0404/2380] linux-user: fix UNAME_MACHINE for sparc/sparc64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "sun4" is not recognized by config.guess. linux defines sparc and sparc64 in arch/sparc/Makefile. Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Max Filippov Message-Id: <20180509231123.20864-7-laurent@vivier.eu> --- linux-user/sparc/target_syscall.h | 2 +- linux-user/sparc64/target_syscall.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/linux-user/sparc/target_syscall.h b/linux-user/sparc/target_syscall.h index 3725875fcf..b9160a771b 100644 --- a/linux-user/sparc/target_syscall.h +++ b/linux-user/sparc/target_syscall.h @@ -11,7 +11,7 @@ struct target_pt_regs { abi_ulong u_regs[16]; }; -#define UNAME_MACHINE "sun4" +#define UNAME_MACHINE "sparc" #define UNAME_MINIMUM_RELEASE "2.6.32" /* SPARC kernels don't define this in their Kconfig, but they have the diff --git a/linux-user/sparc64/target_syscall.h b/linux-user/sparc64/target_syscall.h index 34f49df4a1..3073a23e03 100644 --- a/linux-user/sparc64/target_syscall.h +++ b/linux-user/sparc64/target_syscall.h @@ -12,7 +12,7 @@ struct target_pt_regs { abi_ulong fprs; }; -#define UNAME_MACHINE "sun4u" +#define UNAME_MACHINE "sparc64" #define UNAME_MINIMUM_RELEASE "2.6.32" /* SPARC kernels don't define this in their Kconfig, but they have the From 9d94619189d899b2b40e9dfaa0ede64c8b81e5ab Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 30 Apr 2018 09:26:45 +0200 Subject: [PATCH 0405/2380] net: Fix memory leak in net_param_nic() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The early exits in case of errors leak the memory allocated for nd_id. Fix it by using a "goto out" to the cleanup at the end of the function instead. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- net/net.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/net.c b/net/net.c index 29f83983e5..65457b7976 100644 --- a/net/net.c +++ b/net/net.c @@ -1502,11 +1502,12 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp) g_free(mac); if (ret) { error_setg(errp, "invalid syntax for ethernet address"); - return -1; + goto out; } if (is_multicast_ether_addr(ni->macaddr.a)) { error_setg(errp, "NIC cannot have multicast MAC address"); - return -1; + ret = -1; + goto out; } } qemu_macaddr_default_if_unset(&ni->macaddr); @@ -1518,6 +1519,7 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp) nb_nics++; } +out: g_free(nd_id); return ret; } From af1a5c3eb41521b4f090ad6125cd981b72b99ab9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 30 Apr 2018 20:02:23 +0200 Subject: [PATCH 0406/2380] net: Remove the deprecated "vlan" parameter It's been marked as deprecated since QEMU v2.9.0, so that should have been enough time for everybody to either just drop unnecessary "vlan=0" parameters, to switch to the modern -device + -netdev syntax for connecting guest NICs with host network backends, or to switch to the "hubport" netdev in case hubs are really wanted instead. Buglink: https://bugs.launchpad.net/qemu/+bug/658904 Signed-off-by: Thomas Huth Reviewed-by: Stefan Hajnoczi Signed-off-by: Jason Wang --- docs/qdev-device-use.txt | 3 -- hw/core/qdev-properties-system.c | 80 -------------------------------- include/hw/qdev-properties.h | 3 -- include/net/net.h | 1 - net/net.c | 12 ++--- qapi/net.json | 15 +++--- qemu-doc.texi | 9 ---- qemu-options.hx | 29 +++++------- 8 files changed, 22 insertions(+), 130 deletions(-) diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt index 8f188d1d0b..98229b3405 100644 --- a/docs/qdev-device-use.txt +++ b/docs/qdev-device-use.txt @@ -277,9 +277,6 @@ devices and ne2k_isa are. Some PCI devices aren't available with -net nic, e.g. i82558a. -To connect to a VLAN instead of an ordinary host part, replace -netdev=NET-ID by vlan=VLAN. - === Graphics Devices === Host and guest part of graphics devices have always been separate. diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 1d3ba722fa..8b22fb51c9 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -321,86 +321,6 @@ const PropertyInfo qdev_prop_netdev = { .set = set_netdev, }; -/* --- vlan --- */ - -static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) -{ - NetClientState **ptr = qdev_get_prop_ptr(dev, prop); - - if (*ptr) { - int id; - if (!net_hub_id_for_client(*ptr, &id)) { - return snprintf(dest, len, "%d", id); - } - } - - return snprintf(dest, len, ""); -} - -static void get_vlan(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - NetClientState **ptr = qdev_get_prop_ptr(dev, prop); - int32_t id = -1; - - if (*ptr) { - int hub_id; - if (!net_hub_id_for_client(*ptr, &hub_id)) { - id = hub_id; - } - } - - visit_type_int32(v, name, &id, errp); -} - -static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); - NetClientState **ptr = &peers_ptr->ncs[0]; - Error *local_err = NULL; - int32_t id; - NetClientState *hubport; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_int32(v, name, &id, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (id == -1) { - *ptr = NULL; - return; - } - if (*ptr) { - error_set_from_qdev_prop_error(errp, -EINVAL, dev, prop, name); - return; - } - - hubport = net_hub_port_find(id); - if (!hubport) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name, prop->info->name); - return; - } - *ptr = hubport; -} - -const PropertyInfo qdev_prop_vlan = { - .name = "int32", - .description = "Integer VLAN id to connect to", - .print = print_vlan, - .get = get_vlan, - .set = set_vlan, -}; void qdev_prop_set_drive(DeviceState *dev, const char *name, BlockBackend *value, Error **errp) diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index b2ad8e9faa..4f60cc88f3 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -29,7 +29,6 @@ extern const PropertyInfo qdev_prop_bios_chs_trans; extern const PropertyInfo qdev_prop_fdc_drive_type; extern const PropertyInfo qdev_prop_drive; extern const PropertyInfo qdev_prop_netdev; -extern const PropertyInfo qdev_prop_vlan; extern const PropertyInfo qdev_prop_pci_devfn; extern const PropertyInfo qdev_prop_blocksize; extern const PropertyInfo qdev_prop_pci_host_devaddr; @@ -195,8 +194,6 @@ extern const PropertyInfo qdev_prop_off_auto_pcibar; DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*) #define DEFINE_PROP_NETDEV(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NICPeers) -#define DEFINE_PROP_VLAN(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NICPeers) #define DEFINE_PROP_DRIVE(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockBackend *) #define DEFINE_PROP_MACADDR(_n, _s, _f) \ diff --git a/include/net/net.h b/include/net/net.h index 1f7341e459..1425960f76 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -37,7 +37,6 @@ typedef struct NICConf { #define DEFINE_NIC_PROPERTIES(_state, _conf) \ DEFINE_PROP_MACADDR("mac", _state, _conf.macaddr), \ - DEFINE_PROP_VLAN("vlan", _state, _conf.peers), \ DEFINE_PROP_NETDEV("netdev", _state, _conf.peers) diff --git a/net/net.c b/net/net.c index 65457b7976..efb9eaf779 100644 --- a/net/net.c +++ b/net/net.c @@ -965,7 +965,6 @@ static int net_client_init1(const void *object, bool is_netdev, Error **errp) const Netdev *netdev; const char *name; NetClientState *peer = NULL; - static bool vlan_warned; if (is_netdev) { netdev = object; @@ -1036,15 +1035,10 @@ static int net_client_init1(const void *object, bool is_netdev, Error **errp) return -1; } - /* Do not add to a vlan if it's a nic with a netdev= parameter. */ + /* Do not add to a hub if it's a nic with a netdev= parameter. */ if (netdev->type != NET_CLIENT_DRIVER_NIC || !opts->u.nic.has_netdev) { - peer = net_hub_add_port(net->has_vlan ? net->vlan : 0, NULL, NULL); - } - - if (net->has_vlan && !vlan_warned) { - error_report("'vlan' is deprecated. Please use 'netdev' instead."); - vlan_warned = true; + peer = net_hub_add_port(0, NULL, NULL); } } @@ -1365,7 +1359,7 @@ void qmp_set_link(const char *name, bool up, Error **errp) * If the peer is a HUBPORT or a backend, we do not change the * link status. * - * This behavior is compatible with qemu vlans where there could be + * This behavior is compatible with qemu hubs where there could be * multiple clients that can still communicate with each other in * disconnected mode. For now maintain this compatibility. */ diff --git a/qapi/net.json b/qapi/net.json index 9117c56972..b4fe4b660b 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -209,7 +209,7 @@ ## # @NetdevTapOptions: # -# Connect the host TAP network interface name to the VLAN. +# Used to configure a host TAP network interface backend. # # @ifname: interface name # @@ -267,8 +267,8 @@ ## # @NetdevSocketOptions: # -# Connect the VLAN to a remote VLAN in another QEMU virtual machine using a TCP -# socket connection. +# Socket netdevs are used to establish a network connection to another +# QEMU virtual machine via a TCP socket. # # @fd: file descriptor of an already opened socket # @@ -296,7 +296,7 @@ ## # @NetdevL2TPv3Options: # -# Connect the VLAN to Ethernet over L2TPv3 Static tunnel +# Configure an Ethernet over L2TPv3 tunnel. # # @src: source address # @@ -352,7 +352,7 @@ ## # @NetdevVdeOptions: # -# Connect the VLAN to a vde switch running on the host. +# Connect to a vde switch running on the host. # # @sock: socket path # @@ -490,8 +490,6 @@ # # Captures the configuration of a network device; legacy. # -# @vlan: vlan number -# # @id: identifier for monitor commands # # @name: identifier for monitor commands, ignored if @id is present @@ -499,10 +497,11 @@ # @opts: device type specific properties (legacy) # # Since: 1.2 +# +# 'vlan' - removed with 2.12 ## { 'struct': 'NetLegacy', 'data': { - '*vlan': 'int32', '*id': 'str', '*name': 'str', 'opts': 'NetLegacyOptions' } } diff --git a/qemu-doc.texi b/qemu-doc.texi index 0ed0f19e6b..9216848af7 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2855,15 +2855,6 @@ with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' (for embedded NICs). The new syntax allows different settings to be provided per NIC. -@subsection -net vlan (since 2.9.0) - -The ``-net vlan=NN'' argument was mostly used to attach separate -network backends to different virtual NICs. This is the default -behavior for ``-netdev'' and ``-nic''. You can connect multiple -``-netdev'' and ``-nic'' devices to the same network using the -"hubport" network backend, created with ``-netdev hubport,hubid=NN,...'' -and ``-nic hubport,hubid=NN''. - @subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) The drive geometry arguments are replaced by the the geometry arguments diff --git a/qemu-options.hx b/qemu-options.hx index c611766390..3588b04024 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2003,7 +2003,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " configure a vhost-user network, backed by a chardev 'dev'\n" #endif "-netdev hubport,id=str,hubid=n[,netdev=nd]\n" - " configure a hub port on QEMU VLAN 'n'\n", QEMU_ARCH_ALL) + " configure a hub port on the hub with ID 'n'\n", QEMU_ARCH_ALL) DEF("nic", HAS_ARG, QEMU_OPTION_nic, "--nic [tap|bridge|" #ifdef CONFIG_SLIRP @@ -2028,10 +2028,9 @@ DEF("nic", HAS_ARG, QEMU_OPTION_nic, " provided a 'user' network connection)\n", QEMU_ARCH_ALL) DEF("net", HAS_ARG, QEMU_OPTION_net, - "-net nic[,vlan=n][,netdev=nd][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n" + "-net nic[,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n" " configure or create an on-board (or machine default) NIC and\n" - " connect it either to VLAN 'n' or the netdev 'nd' (for pluggable\n" - " NICs please use '-device devtype,netdev=nd' instead)\n" + " connect it to hub 0 (please use -nic unless you need a hub)\n" "-net [" #ifdef CONFIG_SLIRP "user|" @@ -2044,7 +2043,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, #ifdef CONFIG_NETMAP "netmap|" #endif - "socket][,vlan=n][,option][,option][,...]\n" + "socket][,option][,option][,...]\n" " old way to initialize a host network interface\n" " (use the -netdev option if possible instead)\n", QEMU_ARCH_ALL) STEXI @@ -2462,17 +2461,14 @@ qemu -m 512 -object memory-backend-file,id=mem,size=512M,mem-path=/hugetlbfs,sha Create a hub port on the emulated hub with ID @var{hubid}. The hubport netdev lets you connect a NIC to a QEMU emulated hub instead of a -single netdev. @code{-net} and @code{-device} with the parameter @option{vlan} -(deprecated), or @code{-nic hubport} can also be used to connect a -network device or a NIC to a hub. Alternatively, you can also connect the -hubport to another netdev with ID @var{nd} by using the @option{netdev=@var{nd}} -option. +single netdev. Alternatively, you can also connect the hubport to another +netdev with ID @var{nd} by using the @option{netdev=@var{nd}} option. -@item -net nic[,vlan=@var{n}][,netdev=@var{nd}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}] +@item -net nic[,netdev=@var{nd}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}] @findex -net Legacy option to configure or create an on-board (or machine default) Network -Interface Card(NIC) and connect it either to the emulated hub port ("vlan") -with number @var{n} (@var{n} = 0 is the default), or to the netdev @var{nd}. +Interface Card(NIC) and connect it either to the emulated hub with ID 0 (i.e. +the default hub), or to the netdev @var{nd}. The NIC is an e1000 by default on the PC target. Optionally, the MAC address can be changed to @var{mac}, the device address set to @var{addr} (PCI cards only), and a @var{name} can be assigned for use in monitor commands. @@ -2482,11 +2478,10 @@ that the card should have; this option currently only affects virtio cards; set NIC is created. QEMU can emulate several different models of network card. Use @code{-net nic,model=help} for a list of available devices for your target. -@item -net user|tap|bridge|socket|l2tpv3|vde[,...][,vlan=@var{n}][,name=@var{name}] +@item -net user|tap|bridge|socket|l2tpv3|vde[,...][,name=@var{name}] Configure a host network backend (with the options corresponding to the same -@option{-netdev} option) and connect it to the emulated hub ("vlan") with the -number @var{n} (default is number 0). Use @var{name} to specify the name of the -hub port. +@option{-netdev} option) and connect it to the emulated hub 0 (the default +hub). Use @var{name} to specify the name of the hub port. ETEXI STEXI From 442da403ead80525761898ab0d8036a9cd3c6829 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 30 Apr 2018 20:02:24 +0200 Subject: [PATCH 0407/2380] net: Get rid of 'vlan' terminology and use 'hub' instead in the source files 'vlan' is very confusing since it does not mean something like IEEE 802.1Q, but rather emulated hubs, so let's switch to that terminology instead. Buglink: https://bugs.launchpad.net/qemu/+bug/658904 Reviewed-by: Stefan Hajnoczi Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- net/hub.c | 7 +++---- net/slirp.c | 8 ++++---- net/tap.c | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/net/hub.c b/net/hub.c index 5e84a9ad93..78b671ed95 100644 --- a/net/hub.c +++ b/net/hub.c @@ -23,8 +23,7 @@ /* * A hub broadcasts incoming packets to all its ports except the source port. - * Hubs can be used to provide independent network segments, also confusingly - * named the QEMU 'vlan' feature. + * Hubs can be used to provide independent emulated network segments. */ typedef struct NetHub NetHub; @@ -345,10 +344,10 @@ void net_hub_check_clients(void) } } if (has_host_dev && !has_nic) { - warn_report("vlan %d with no nics", hub->id); + warn_report("hub %d with no nics", hub->id); } if (has_nic && !has_host_dev) { - warn_report("vlan %d is not connected to host network", hub->id); + warn_report("hub %d is not connected to host network", hub->id); } } } diff --git a/net/slirp.c b/net/slirp.c index 8991816bbf..692252445a 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -415,7 +415,7 @@ static SlirpState *slirp_lookup(Monitor *mon, const char *hub_id, if (hub_id) { nc = net_hub_find_client_by_name(strtol(hub_id, NULL, 0), name); if (!nc) { - monitor_printf(mon, "unrecognized (vlan-id, stackname) pair\n"); + monitor_printf(mon, "unrecognized (hub-id, stackname) pair\n"); return NULL; } } else { @@ -870,9 +870,9 @@ void hmp_info_usernet(Monitor *mon, const QDict *qdict) QTAILQ_FOREACH(s, &slirp_stacks, entry) { int id; - bool got_vlan_id = net_hub_id_for_client(&s->nc, &id) == 0; - monitor_printf(mon, "VLAN %d (%s):\n", - got_vlan_id ? id : -1, + bool got_hub_id = net_hub_id_for_client(&s->nc, &id) == 0; + monitor_printf(mon, "Hub %d (%s):\n", + got_hub_id ? id : -1, s->nc.name); slirp_connection_info(s->slirp, mon); } diff --git a/net/tap.c b/net/tap.c index 2b3a36f9b5..de05f20e28 100644 --- a/net/tap.c +++ b/net/tap.c @@ -766,10 +766,10 @@ int net_init_tap(const Netdev *netdev, const char *name, queues = tap->has_queues ? tap->queues : 1; vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL; - /* QEMU vlans does not support multiqueue tap, in this case peer is set. + /* QEMU hubs do not support multiqueue tap, in this case peer is set. * For -netdev, peer is always NULL. */ if (peer && (tap->has_queues || tap->has_fds || tap->has_vhostfds)) { - error_setg(errp, "Multiqueue tap cannot be used with QEMU vlans"); + error_setg(errp, "Multiqueue tap cannot be used with hubs"); return -1; } From 0e0266c2e475b82b39a757c875fa03e64272fbe7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 30 Apr 2018 20:02:25 +0200 Subject: [PATCH 0408/2380] net: Get rid of 'vlan' terminology and use 'hub' instead in the doc files 'vlan' is very confusing since it does not mean something like IEEE 802.1Q, but rather emulated hubs, so let's switch to that terminology instead. While we're at it, move the subsection about hub a little bit downward in the documentation (it's not as important anymore as it was before the invention of the -netdev parameter), and extend it a little bit. Buglink: https://bugs.launchpad.net/qemu/+bug/658904 Reviewed-by: Stefan Hajnoczi Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- qemu-doc.texi | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/qemu-doc.texi b/qemu-doc.texi index 9216848af7..e2acac8512 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -714,20 +714,12 @@ state is not saved or restored properly (in particular USB). @node pcsys_network @section Network emulation -QEMU can simulate several network cards (PCI or ISA cards on the PC -target) and can connect them to an arbitrary number of Virtual Local -Area Networks (VLANs). Host TAP devices can be connected to any QEMU -VLAN. VLAN can be connected between separate instances of QEMU to -simulate large networks. For simpler usage, a non privileged user mode -network stack can replace the TAP device to have a basic network -connection. - -@subsection VLANs - -QEMU simulates several VLANs. A VLAN can be symbolised as a virtual -connection between several network devices. These devices can be for -example QEMU virtual Ethernet cards or virtual Host ethernet devices -(TAP devices). +QEMU can simulate several network cards (e.g. PCI or ISA cards on the PC +target) and can connect them to a network backend on the host or an emulated +hub. The various host network backends can either be used to connect the NIC of +the guest to a real network (e.g. by using a TAP devices or the non-privileged +user mode network stack), or to other guest instances running in another QEMU +process (e.g. by using the socket host network backend). @subsection Using TAP network interfaces @@ -763,7 +755,7 @@ network). The virtual network configuration is the following: @example - QEMU VLAN <------> Firewall/DHCP server <-----> Internet + guest (10.0.2.15) <------> Firewall/DHCP server <-----> Internet | (10.0.2.2) | ----> DNS server (10.0.2.3) @@ -798,11 +790,23 @@ When using the @option{'-netdev user,hostfwd=...'} option, TCP or UDP connections can be redirected from the host to the guest. It allows for example to redirect X11, telnet or SSH connections. -@subsection Connecting VLANs between QEMU instances +@subsection Hubs -Using the @option{-net socket} option, it is possible to make VLANs -that span several QEMU instances. See @ref{sec_invocation} to have a -basic example. +QEMU can simulate several hubs. A hub can be thought of as a virtual connection +between several network devices. These devices can be for example QEMU virtual +ethernet cards or virtual Host ethernet devices (TAP devices). You can connect +guest NICs or host network backends to such a hub using the @option{-netdev +hubport} or @option{-nic hubport} options. The legacy @option{-net} option +also connects the given device to the emulated hub with ID 0 (i.e. the default +hub) unless you specify a netdev with @option{-net nic,netdev=xxx} here. + +@subsection Connecting emulated networks between QEMU instances + +Using the @option{-netdev socket} (or @option{-nic socket} or +@option{-net socket}) option, it is possible to create emulated +networks that span several QEMU instances. +See the description of the @option{-netdev socket} option in the +@ref{sec_invocation,,Invocation chapter} to have a basic example. @node pcsys_other_devs @section Other Devices From f606e4d6258fa82c3f6c1cc762ebe483db5f5db6 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 10 May 2018 22:59:49 +0200 Subject: [PATCH 0409/2380] linux-user: correctly align types in thunking code This is a follow up of patch: commit c2e3dee6e03527baf8698698cce76b1a3174969a Author: Laurent Vivier Date: Sun Feb 13 23:37:34 2011 +0100 linux-user: Define target alignment size In my case m68k aligns "int" on 2 not 4. You can check this with the following program: int main(void) { struct rtentry rt; printf("rt_pad1 %ld %zd\n", offsetof(struct rtentry, rt_pad1), sizeof(rt.rt_pad1)); printf("rt_dst %ld %zd\n", offsetof(struct rtentry, rt_dst), sizeof(rt.rt_dst)); printf("rt_gateway %ld %zd\n", offsetof(struct rtentry, rt_gateway), sizeof(rt.rt_gateway)); printf("rt_genmask %ld %zd\n", offsetof(struct rtentry, rt_genmask), sizeof(rt.rt_genmask)); printf("rt_flags %ld %zd\n", offsetof(struct rtentry, rt_flags), sizeof(rt.rt_flags)); printf("rt_pad2 %ld %zd\n", offsetof(struct rtentry, rt_pad2), sizeof(rt.rt_pad2)); printf("rt_pad3 %ld %zd\n", offsetof(struct rtentry, rt_pad3), sizeof(rt.rt_pad3)); printf("rt_pad4 %ld %zd\n", offsetof(struct rtentry, rt_pad4), sizeof(rt.rt_pad4)); printf("rt_metric %ld %zd\n", offsetof(struct rtentry, rt_metric), sizeof(rt.rt_metric)); printf("rt_dev %ld %zd\n", offsetof(struct rtentry, rt_dev), sizeof(rt.rt_dev)); printf("rt_mtu %ld %zd\n", offsetof(struct rtentry, rt_mtu), sizeof(rt.rt_mtu)); printf("rt_window %ld %zd\n", offsetof(struct rtentry, rt_window), sizeof(rt.rt_window)); printf("rt_irtt %ld %zd\n", offsetof(struct rtentry, rt_irtt), sizeof(rt.rt_irtt)); } And result is : i386 rt_pad1 0 4 rt_dst 4 16 rt_gateway 20 16 rt_genmask 36 16 rt_flags 52 2 rt_pad2 54 2 rt_pad3 56 4 rt_pad4 62 2 rt_metric 64 2 rt_dev 68 4 rt_mtu 72 4 rt_window 76 4 rt_irtt 80 2 m68k rt_pad1 0 4 rt_dst 4 16 rt_gateway 20 16 rt_genmask 36 16 rt_flags 52 2 rt_pad2 54 2 rt_pad3 56 4 rt_pad4 62 2 rt_metric 64 2 rt_dev 66 4 rt_mtu 70 4 rt_window 74 4 rt_irtt 78 2 This affects the "route" command : WITHOUT this patch: $ sudo route add -net default gw 10.0.3.1 window 1024 irtt 2 eth0 $ netstat -nr Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 0.0.0.0 10.0.3.1 0.0.0.0 UG 0 67108866 32768 eth0 10.0.3.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 WITH this patch: $ sudo route add -net default gw 10.0.3.1 window 1024 irtt 2 eth0 $ netstat -nr Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 0.0.0.0 10.0.3.1 0.0.0.0 UG 0 1024 2 eth0 10.0.3.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Message-Id: <20180510205949.26455-1-laurent@vivier.eu> --- include/exec/user/thunk.h | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h index f19ef4b230..8f55b233b3 100644 --- a/include/exec/user/thunk.h +++ b/include/exec/user/thunk.h @@ -149,20 +149,32 @@ static inline int thunk_type_align(const argtype *type_ptr, int is_host) case TYPE_CHAR: return 1; case TYPE_SHORT: - return 2; + if (is_host) { + return __alignof__(short); + } else { + return ABI_SHORT_ALIGNMENT; + } case TYPE_INT: - return 4; + if (is_host) { + return __alignof__(int); + } else { + return ABI_INT_ALIGNMENT; + } case TYPE_LONGLONG: case TYPE_ULONGLONG: - return 8; + if (is_host) { + return __alignof__(long long); + } else { + return ABI_LLONG_ALIGNMENT; + } case TYPE_LONG: case TYPE_ULONG: case TYPE_PTRVOID: case TYPE_PTR: if (is_host) { - return sizeof(void *); + return __alignof__(long); } else { - return TARGET_ABI_BITS / 8; + return ABI_LONG_ALIGNMENT; } break; case TYPE_OLDDEVT: From 6e9c893ecd00afd5344c35d0d0ded50eaa0938f6 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 4 May 2018 14:53:16 +0200 Subject: [PATCH 0410/2380] s390x/css: disabled subchannels cannot be status pending The 3270 code will try to post an attention interrupt when the 3270 emulator (e.g. x3270) attaches. If the guest has not yet enabled the subchannel for the 3270 device, we will present a spurious cc 1 (status pending) when it uses msch on it later on, e.g. when trying to enable the subchannel. To fix this, just don't do anything in css_conditional_io_interrupt() if the subchannel is not enabled. The 3270 code will work fine with that, and the other user of this function (virtio-ccw) never attempts to post an interrupt for a disabled device to begin with. CC: qemu-stable@nongnu.org Reported-by: Thomas Huth Tested-by: Thomas Huth Acked-by: Christian Borntraeger Acked-by: Halil Pasic Reviewed-by: David Hildenbrand Signed-off-by: Cornelia Huck --- hw/s390x/css.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 301bf1772f..56c3fa8c89 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -616,6 +616,14 @@ void css_inject_io_interrupt(SubchDev *sch) void css_conditional_io_interrupt(SubchDev *sch) { + /* + * If the subchannel is not enabled, it is not made status pending + * (see PoP p. 16-17, "Status Control"). + */ + if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA)) { + return; + } + /* * If the subchannel is not currently status pending, make it pending * with alert status. From a6e4385dea94850d7b06b0542e7960c1063fdabd Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 8 May 2018 12:17:52 +0200 Subject: [PATCH 0411/2380] pc-bios/s390-ccw: struct tpi_info must be declared as aligned(4) I've run into a compilation error today with the current version of GCC 8: In file included from s390-ccw.h:49, from main.c:12: cio.h:128:1: error: alignment 1 of 'struct tpi_info' is less than 4 [-Werror=packed-not-aligned] } __attribute__ ((packed)); ^ cc1: all warnings being treated as errors Since the struct tpi_info contains an element ("struct subchannel_id schid") which is marked as aligned(4), we've got to mark the struct tpi_info as aligned(4), too. CC: qemu-stable@nongnu.org Signed-off-by: Thomas Huth Message-Id: <1525774672-11913-1-git-send-email-thuth@redhat.com> Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- pc-bios/s390-ccw/cio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h index 55eaeee4b6..1a0795f645 100644 --- a/pc-bios/s390-ccw/cio.h +++ b/pc-bios/s390-ccw/cio.h @@ -125,7 +125,7 @@ struct tpi_info { __u32 reserved3 : 12; __u32 int_type : 3; __u32 reserved4 : 12; -} __attribute__ ((packed)); +} __attribute__ ((packed, aligned(4))); /* channel command word (type 1) */ struct ccw1 { From 0c53057adb04d254bc09511880670c92ab185fc6 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 7 May 2018 15:05:42 +0200 Subject: [PATCH 0412/2380] virtio-ccw: common reset handler All the different virtio ccw devices use the same reset handler, so let's move setting it into the base virtio ccw device class. CC: qemu-stable@nongnu.org Reviewed-by: Thomas Huth Reviewed-by: David Hildenbrand Reviewed-by: Halil Pasic Signed-off-by: Cornelia Huck --- hw/s390x/virtio-ccw.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index e51fbefd23..40a33302a7 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1345,7 +1345,6 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_net_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_net_properties; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } @@ -1373,7 +1372,6 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_blk_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_blk_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -1401,7 +1399,6 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_serial_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_serial_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -1429,7 +1426,6 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_balloon_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_balloon_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } @@ -1457,7 +1453,6 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_scsi_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_scsi_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -1484,7 +1479,6 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) k->realize = vhost_ccw_scsi_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = vhost_ccw_scsi_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -1521,7 +1515,6 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_rng_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_rng_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } @@ -1559,7 +1552,6 @@ static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_crypto_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_crypto_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } @@ -1597,7 +1589,6 @@ static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_gpu_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_gpu_properties; dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); @@ -1626,7 +1617,6 @@ static void virtio_ccw_input_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_input_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_input_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -1730,6 +1720,7 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) dc->realize = virtio_ccw_busdev_realize; dc->unrealize = virtio_ccw_busdev_unrealize; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; + dc->reset = virtio_ccw_reset; } static const TypeInfo virtio_ccw_device_info = { @@ -1806,7 +1797,6 @@ static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) k->unrealize = virtio_ccw_unrealize; k->realize = virtio_ccw_9p_realize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_9p_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -1856,7 +1846,6 @@ static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) k->unrealize = virtio_ccw_unrealize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->props = vhost_vsock_ccw_properties; - dc->reset = virtio_ccw_reset; } static void vhost_vsock_ccw_instance_init(Object *obj) From 838fb84f83c84f00d15b1bede5e080b495644458 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 7 May 2018 15:27:57 +0200 Subject: [PATCH 0413/2380] s390x/ccw: make sure all ccw devices are properly reset Thomas reported that the subchannel for a 3270 device that ended up in a broken state (status pending even though not enabled) did not get out of that state even after a reboot (which involves a subsytem reset). The reason for this is that the 3270 device did not define a reset handler. Let's fix this by introducing a base reset handler (set up for all ccw devices) that resets the subchannel and have virtio-ccw call its virtio-specific reset procedure in addition to that. CC: qemu-stable@nongnu.org Reported-by: Thomas Huth Suggested-by: Christian Borntraeger Reviewed-by: Thomas Huth Tested-by: Thomas Huth Acked-by: Christian Borntraeger Reviewed-by: Halil Pasic Signed-off-by: Cornelia Huck --- hw/s390x/ccw-device.c | 8 ++++++++ hw/s390x/virtio-ccw.c | 9 ++++++--- hw/s390x/virtio-ccw.h | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index f9bfa154d6..7cd73df4aa 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -40,6 +40,13 @@ static Property ccw_device_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void ccw_device_reset(DeviceState *d) +{ + CcwDevice *ccw_dev = CCW_DEVICE(d); + + css_reset_sch(ccw_dev->sch); +} + static void ccw_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -48,6 +55,7 @@ static void ccw_device_class_init(ObjectClass *klass, void *data) k->realize = ccw_device_realize; k->refill_ids = ccw_device_refill_ids; dc->props = ccw_device_properties; + dc->reset = ccw_device_reset; } const VMStateDescription vmstate_ccw_dev = { diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 40a33302a7..22df33b509 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1058,10 +1058,12 @@ static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - CcwDevice *ccw_dev = CCW_DEVICE(d); + VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); virtio_ccw_reset_virtio(dev, vdev); - css_reset_sch(ccw_dev->sch); + if (vdc->parent_reset) { + vdc->parent_reset(d); + } } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) @@ -1715,12 +1717,13 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CCWDeviceClass *k = CCW_DEVICE_CLASS(dc); + VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_CLASS(klass); k->unplug = virtio_ccw_busdev_unplug; dc->realize = virtio_ccw_busdev_realize; dc->unrealize = virtio_ccw_busdev_unrealize; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; - dc->reset = virtio_ccw_reset; + device_class_set_parent_reset(dc, virtio_ccw_reset, &vdc->parent_reset); } static const TypeInfo virtio_ccw_device_info = { diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 2fc513001e..3453aa1f98 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -77,6 +77,7 @@ typedef struct VirtIOCCWDeviceClass { CCWDeviceClass parent_class; void (*realize)(VirtioCcwDevice *dev, Error **errp); void (*unrealize)(VirtioCcwDevice *dev, Error **errp); + void (*parent_reset)(DeviceState *dev); } VirtIOCCWDeviceClass; /* Performance improves when virtqueue kick processing is decoupled from the From a30fb811cbe940020a498d2cdac9326cac38b4d9 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 24 Apr 2018 12:18:59 +0200 Subject: [PATCH 0414/2380] s390x: refactor reset/reipl handling Calling pause_all_vcpus()/resume_all_vcpus() from a VCPU thread might not be the best idea. As pause_all_vcpus() temporarily drops the qemu mutex, two parallel calls to pause_all_vcpus() can be active at a time, resulting in a deadlock. (either by two VCPUs or by the main thread and a VCPU) Let's handle it via the main loop instead, as suggested by Paolo. If we would have two parallel reset requests by two different VCPUs at the same time, the last one would win. We use the existing ipl device to handle it. The nice side effect is that we can get rid of reipl_requested. This change implies that all reset handling now goes via the common path, so "no-reboot" handling is now active for all kinds of reboots. Let's execute any CPU initialization code on the target CPU using run_on_cpu. Signed-off-by: David Hildenbrand Message-Id: <20180424101859.10239-1-david@redhat.com> Acked-by: Thomas Huth Signed-off-by: Cornelia Huck --- hw/s390x/ipl.c | 43 ++++++++++++++++++--- hw/s390x/ipl.h | 16 +++++++- hw/s390x/s390-virtio-ccw.c | 51 +++++++++++++++++++++---- include/hw/s390x/s390-virtio-ccw.h | 2 - target/s390x/cpu.h | 26 +++++++++++++ target/s390x/diag.c | 61 ++---------------------------- target/s390x/internal.h | 6 --- target/s390x/kvm.c | 2 +- 8 files changed, 127 insertions(+), 80 deletions(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 150f6c0582..04245b5258 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -26,6 +26,7 @@ #include "qemu/config-file.h" #include "qemu/cutils.h" #include "qemu/option.h" +#include "exec/exec-all.h" #define KERN_IMAGE_START 0x010000UL #define KERN_PARM_AREA 0x010480UL @@ -488,12 +489,20 @@ IplParameterBlock *s390_ipl_get_iplb(void) return &ipl->iplb; } -void s390_reipl_request(void) +void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type) { S390IPLState *ipl = get_ipl_device(); - ipl->reipl_requested = true; - if (ipl->iplb_valid && + if (reset_type == S390_RESET_EXTERNAL || reset_type == S390_RESET_REIPL) { + /* use CPU 0 for full resets */ + ipl->reset_cpu_index = 0; + } else { + ipl->reset_cpu_index = cs->cpu_index; + } + ipl->reset_type = reset_type; + + if (reset_type == S390_RESET_REIPL && + ipl->iplb_valid && !ipl->netboot && ipl->iplb.pbt == S390_IPL_TYPE_CCW && is_virtio_scsi_device(&ipl->iplb)) { @@ -510,6 +519,31 @@ void s390_reipl_request(void) } } qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + /* as this is triggered by a CPU, make sure to exit the loop */ + if (tcg_enabled()) { + cpu_loop_exit(cs); + } +} + +void s390_ipl_get_reset_request(CPUState **cs, enum s390_reset *reset_type) +{ + S390IPLState *ipl = get_ipl_device(); + + *cs = qemu_get_cpu(ipl->reset_cpu_index); + if (!*cs) { + /* use any CPU */ + *cs = first_cpu; + } + *reset_type = ipl->reset_type; +} + +void s390_ipl_clear_reset_request(void) +{ + S390IPLState *ipl = get_ipl_device(); + + ipl->reset_type = S390_RESET_EXTERNAL; + /* use CPU 0 for full resets */ + ipl->reset_cpu_index = 0; } static void s390_ipl_prepare_qipl(S390CPU *cpu) @@ -556,11 +590,10 @@ static void s390_ipl_reset(DeviceState *dev) { S390IPLState *ipl = S390_IPL(dev); - if (!ipl->reipl_requested) { + if (ipl->reset_type != S390_RESET_REIPL) { ipl->iplb_valid = false; memset(&ipl->iplb, 0, sizeof(IplParameterBlock)); } - ipl->reipl_requested = false; } static void s390_ipl_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 0570d0ad75..4e87b89418 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -87,7 +87,17 @@ int s390_ipl_set_loadparm(uint8_t *loadparm); void s390_ipl_update_diag308(IplParameterBlock *iplb); void s390_ipl_prepare_cpu(S390CPU *cpu); IplParameterBlock *s390_ipl_get_iplb(void); -void s390_reipl_request(void); + +enum s390_reset { + /* default is a reset not triggered by a CPU e.g. issued by QMP */ + S390_RESET_EXTERNAL = 0, + S390_RESET_REIPL, + S390_RESET_MODIFIED_CLEAR, + S390_RESET_LOAD_NORMAL, +}; +void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type); +void s390_ipl_get_reset_request(CPUState **cs, enum s390_reset *reset_type); +void s390_ipl_clear_reset_request(void); #define QIPL_ADDRESS 0xcc @@ -129,9 +139,11 @@ struct S390IPLState { bool enforce_bios; IplParameterBlock iplb; bool iplb_valid; - bool reipl_requested; bool netboot; QemuIplParameters qipl; + /* reset related properties don't have to be migrated or reset */ + enum s390_reset reset_type; + int reset_cpu_index; /*< public >*/ char *kernel; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 5796e24bd8..e548d341a0 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -93,7 +93,7 @@ static const char *const reset_dev_types[] = { "diag288", }; -void subsystem_reset(void) +static void subsystem_reset(void) { DeviceState *dev; int i; @@ -381,17 +381,54 @@ static void s390_cpu_plug(HotplugHandler *hotplug_dev, } } +static inline void s390_do_cpu_ipl(CPUState *cs, run_on_cpu_data arg) +{ + S390CPU *cpu = S390_CPU(cs); + + s390_ipl_prepare_cpu(cpu); + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); +} + static void s390_machine_reset(void) { - S390CPU *ipl_cpu = S390_CPU(qemu_get_cpu(0)); + enum s390_reset reset_type; + CPUState *cs, *t; + /* get the reset parameters, reset them once done */ + s390_ipl_get_reset_request(&cs, &reset_type); + + /* all CPUs are paused and synchronized at this point */ s390_cmma_reset(); - qemu_devices_reset(); - s390_crypto_reset(); - /* all cpus are stopped - configure and start the ipl cpu only */ - s390_ipl_prepare_cpu(ipl_cpu); - s390_cpu_set_state(S390_CPU_STATE_OPERATING, ipl_cpu); + switch (reset_type) { + case S390_RESET_EXTERNAL: + case S390_RESET_REIPL: + qemu_devices_reset(); + s390_crypto_reset(); + + /* configure and start the ipl CPU only */ + run_on_cpu(cs, s390_do_cpu_ipl, RUN_ON_CPU_NULL); + break; + case S390_RESET_MODIFIED_CLEAR: + CPU_FOREACH(t) { + run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL); + } + subsystem_reset(); + s390_crypto_reset(); + run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL); + break; + case S390_RESET_LOAD_NORMAL: + CPU_FOREACH(t) { + run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL); + } + subsystem_reset(); + run_on_cpu(cs, s390_do_cpu_initial_reset, RUN_ON_CPU_NULL); + run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL); + break; + default: + g_assert_not_reached(); + } + s390_ipl_clear_reset_request(); } static void s390_machine_device_plug(HotplugHandler *hotplug_dev, diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index ac896e31ea..ab88d49d10 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -53,6 +53,4 @@ bool cpu_model_allowed(void); */ bool css_migration_enabled(void); -void subsystem_reset(void); - #endif diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 3ee40f08b7..6629a533f3 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -686,6 +686,32 @@ static inline uint64_t s390_build_validity_mcic(void) return mcic; } +static inline void s390_do_cpu_full_reset(CPUState *cs, run_on_cpu_data arg) +{ + cpu_reset(cs); +} + +static inline void s390_do_cpu_reset(CPUState *cs, run_on_cpu_data arg) +{ + S390CPUClass *scc = S390_CPU_GET_CLASS(cs); + + scc->cpu_reset(cs); +} + +static inline void s390_do_cpu_initial_reset(CPUState *cs, run_on_cpu_data arg) +{ + S390CPUClass *scc = S390_CPU_GET_CLASS(cs); + + scc->initial_cpu_reset(cs); +} + +static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg) +{ + S390CPUClass *scc = S390_CPU_GET_CLASS(cs); + + scc->load_normal(cs); +} + /* cpu.c */ int s390_get_clock(uint8_t *tod_high, uint64_t *tod_low); diff --git a/target/s390x/diag.c b/target/s390x/diag.c index a755837ad5..ac2c40f363 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -22,51 +22,6 @@ #include "hw/s390x/ipl.h" #include "hw/s390x/s390-virtio-ccw.h" -static int modified_clear_reset(S390CPU *cpu) -{ - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); - CPUState *t; - - pause_all_vcpus(); - cpu_synchronize_all_states(); - CPU_FOREACH(t) { - run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL); - } - s390_cmma_reset(); - subsystem_reset(); - s390_crypto_reset(); - scc->load_normal(CPU(cpu)); - cpu_synchronize_all_post_reset(); - resume_all_vcpus(); - return 0; -} - -static inline void s390_do_cpu_reset(CPUState *cs, run_on_cpu_data arg) -{ - S390CPUClass *scc = S390_CPU_GET_CLASS(cs); - - scc->cpu_reset(cs); -} - -static int load_normal_reset(S390CPU *cpu) -{ - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); - CPUState *t; - - pause_all_vcpus(); - cpu_synchronize_all_states(); - CPU_FOREACH(t) { - run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL); - } - s390_cmma_reset(); - subsystem_reset(); - scc->initial_cpu_reset(CPU(cpu)); - scc->load_normal(CPU(cpu)); - cpu_synchronize_all_post_reset(); - resume_all_vcpus(); - return 0; -} - int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) { uint64_t func = env->regs[r1]; @@ -101,6 +56,7 @@ int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) { + CPUState *cs = CPU(s390_env_get_cpu(env)); uint64_t addr = env->regs[r1]; uint64_t subcode = env->regs[r3]; IplParameterBlock *iplb; @@ -117,22 +73,13 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) switch (subcode) { case 0: - modified_clear_reset(s390_env_get_cpu(env)); - if (tcg_enabled()) { - cpu_loop_exit(CPU(s390_env_get_cpu(env))); - } + s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR); break; case 1: - load_normal_reset(s390_env_get_cpu(env)); - if (tcg_enabled()) { - cpu_loop_exit(CPU(s390_env_get_cpu(env))); - } + s390_ipl_reset_request(cs, S390_RESET_LOAD_NORMAL); break; case 3: - s390_reipl_request(); - if (tcg_enabled()) { - cpu_loop_exit(CPU(s390_env_get_cpu(env))); - } + s390_ipl_reset_request(cs, S390_RESET_REIPL); break; case 5: if ((r1 & 1) || (addr & 0x0fffULL)) { diff --git a/target/s390x/internal.h b/target/s390x/internal.h index d911e84958..e392a02d12 100644 --- a/target/s390x/internal.h +++ b/target/s390x/internal.h @@ -273,12 +273,6 @@ static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb, /* Base/displacement are at the same locations. */ #define decode_basedisp_rs decode_basedisp_s -static inline void s390_do_cpu_full_reset(CPUState *cs, run_on_cpu_data arg) -{ - cpu_reset(cs); -} - - /* arch_dump.c */ int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index 12b90cf5c5..58e4380ae3 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -1767,7 +1767,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ret = handle_intercept(cpu); break; case KVM_EXIT_S390_RESET: - s390_reipl_request(); + s390_ipl_reset_request(cs, S390_RESET_REIPL); break; case KVM_EXIT_S390_TSCH: ret = handle_tsch(cpu); From b0dad618baad1efb014c88f4507492a79f2eaf1c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 21:59:43 -0700 Subject: [PATCH 0415/2380] target/s390x: Fix brace Werror with clang 6.0.0 The warning is target/s390x/misc_helper.c:209:21: error: suggest braces around initialization of subobject [-Werror,-Wmissing-braces] SysIB sysib = { 0 }; ^ {} While the original code is correct, and technically exactly correct as per ISO C89, both GCC and Clang support plain empty set of braces as an extension. Cc: Alexander Graf Cc: David Hildenbrand Cc: Cornelia Huck Signed-off-by: Richard Henderson Message-Id: <20180512045950.12386-5-richard.henderson@linaro.org> Reviewed-by: David Hildenbrand Signed-off-by: Cornelia Huck --- target/s390x/misc_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index e0b23c1fd1..1f834f35ef 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -206,7 +206,7 @@ uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint64_t r0, uint64_t r1) const MachineState *ms = MACHINE(qdev_get_machine()); uint16_t total_cpus = 0, conf_cpus = 0, reserved_cpus = 0; S390CPU *cpu = s390_env_get_cpu(env); - SysIB sysib = { 0 }; + SysIB sysib = { }; int i, cc = 0; if ((r0 & STSI_R0_FC_MASK) > STSI_R0_FC_LEVEL_3) { From 5a73e7f313da0e4657bcac61b533ced71b0d0224 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 8 May 2018 22:39:37 +0200 Subject: [PATCH 0416/2380] m68k: fix floatx80_mod() (Coverity CID1390568) Update the variable checked by the loop condition (expDiff). Backport the update from Previous. Fixes: 591596b77a ("target/m68k: add fmod/frem") Signed-off-by: Laurent Vivier Reviewed-by: Thomas Huth Message-Id: <20180508203937.16796-1-laurent@vivier.eu> --- target/m68k/softfloat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c index e41b07d042..d093997219 100644 --- a/target/m68k/softfloat.c +++ b/target/m68k/softfloat.c @@ -103,6 +103,7 @@ floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status) mul64To128(bSig, qTemp, &term0, &term1); sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1); shortShift128Left(aSig0, aSig1, 62, &aSig0, &aSig1); + expDiff -= 62; } expDiff += 64; if (0 < expDiff) { From 4e2d30079c0e771d2c6a607001a4165f2cb51d82 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Feb 2015 20:40:38 -0800 Subject: [PATCH 0417/2380] target-openrisc: Write back result before FPE exception The architecture manual is unclear about this, but the or1ksim does writeback before the exception. This requires splitting the helpers in half, with the exception raised by the second. Acked-by: Stafford Horne Reviewed-by: Bastian Koppelmann Signed-off-by: Richard Henderson --- target/openrisc/fpu_helper.c | 250 +++++++---------------------------- target/openrisc/helper.h | 25 ++-- target/openrisc/translate.c | 101 +++++++++----- 3 files changed, 125 insertions(+), 251 deletions(-) diff --git a/target/openrisc/fpu_helper.c b/target/openrisc/fpu_helper.c index 977a1e8e55..265ce13337 100644 --- a/target/openrisc/fpu_helper.c +++ b/target/openrisc/fpu_helper.c @@ -24,121 +24,70 @@ #include "exception.h" #include "fpu/softfloat.h" -static inline uint32_t ieee_ex_to_openrisc(OpenRISCCPU *cpu, int fexcp) +static int ieee_ex_to_openrisc(int fexcp) { int ret = 0; - if (fexcp) { - if (fexcp & float_flag_invalid) { - cpu->env.fpcsr |= FPCSR_IVF; - ret = 1; - } - if (fexcp & float_flag_overflow) { - cpu->env.fpcsr |= FPCSR_OVF; - ret = 1; - } - if (fexcp & float_flag_underflow) { - cpu->env.fpcsr |= FPCSR_UNF; - ret = 1; - } - if (fexcp & float_flag_divbyzero) { - cpu->env.fpcsr |= FPCSR_DZF; - ret = 1; - } - if (fexcp & float_flag_inexact) { - cpu->env.fpcsr |= FPCSR_IXF; - ret = 1; - } + if (fexcp & float_flag_invalid) { + ret |= FPCSR_IVF; + } + if (fexcp & float_flag_overflow) { + ret |= FPCSR_OVF; + } + if (fexcp & float_flag_underflow) { + ret |= FPCSR_UNF; + } + if (fexcp & float_flag_divbyzero) { + ret |= FPCSR_DZF; + } + if (fexcp & float_flag_inexact) { + ret |= FPCSR_IXF; } - return ret; } -static inline void update_fpcsr(OpenRISCCPU *cpu) +void HELPER(update_fpcsr)(CPUOpenRISCState *env) { - int tmp = ieee_ex_to_openrisc(cpu, - get_float_exception_flags(&cpu->env.fp_status)); + int tmp = get_float_exception_flags(&env->fp_status); - SET_FP_CAUSE(cpu->env.fpcsr, tmp); - if ((GET_FP_ENABLE(cpu->env.fpcsr) & tmp) && - (cpu->env.fpcsr & FPCSR_FPEE)) { - helper_exception(&cpu->env, EXCP_FPE); - } else { - UPDATE_FP_FLAGS(cpu->env.fpcsr, tmp); + if (tmp) { + set_float_exception_flags(0, &env->fp_status); + tmp = ieee_ex_to_openrisc(tmp); + if (tmp) { + env->fpcsr |= tmp; + if (env->fpcsr & FPCSR_FPEE) { + helper_exception(env, EXCP_FPE); + } + } } } uint64_t HELPER(itofd)(CPUOpenRISCState *env, uint64_t val) { - uint64_t itofd; - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - - set_float_exception_flags(0, &cpu->env.fp_status); - itofd = int32_to_float64(val, &cpu->env.fp_status); - update_fpcsr(cpu); - - return itofd; + return int32_to_float64(val, &env->fp_status); } uint32_t HELPER(itofs)(CPUOpenRISCState *env, uint32_t val) { - uint32_t itofs; - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - - set_float_exception_flags(0, &cpu->env.fp_status); - itofs = int32_to_float32(val, &cpu->env.fp_status); - update_fpcsr(cpu); - - return itofs; + return int32_to_float32(val, &env->fp_status); } uint64_t HELPER(ftoid)(CPUOpenRISCState *env, uint64_t val) { - uint64_t ftoid; - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - - set_float_exception_flags(0, &cpu->env.fp_status); - ftoid = float32_to_int64(val, &cpu->env.fp_status); - update_fpcsr(cpu); - - return ftoid; + return float32_to_int64(val, &env->fp_status); } uint32_t HELPER(ftois)(CPUOpenRISCState *env, uint32_t val) { - uint32_t ftois; - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - - set_float_exception_flags(0, &cpu->env.fp_status); - ftois = float32_to_int32(val, &cpu->env.fp_status); - update_fpcsr(cpu); - - return ftois; + return float32_to_int32(val, &env->fp_status); } -#define FLOAT_OP(name, p) void helper_float_##_##p(void) - #define FLOAT_CALC(name) \ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ -{ \ - uint64_t result; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - result = float64_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return result; \ -} \ - \ +{ return float64_ ## name(fdt0, fdt1, &env->fp_status); } \ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ -{ \ - uint32_t result; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - result = float32_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return result; \ -} \ +{ return float32_ ## name(fdt0, fdt1, &env->fp_status); } FLOAT_CALC(add) FLOAT_CALC(sub) @@ -151,132 +100,29 @@ FLOAT_CALC(rem) uint64_t helper_float_madd_d(CPUOpenRISCState *env, uint64_t a, uint64_t b, uint64_t c) { - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - uint64_t result; - set_float_exception_flags(0, &cpu->env.fp_status); - /* Note that or1ksim doesn't use merged operation. */ - result = float64_mul(b, c, &cpu->env.fp_status); - result = float64_add(result, a, &cpu->env.fp_status); - update_fpcsr(cpu); - return result; + /* Note that or1ksim doesn't use fused operation. */ + b = float64_mul(b, c, &env->fp_status); + return float64_add(a, b, &env->fp_status); } uint32_t helper_float_madd_s(CPUOpenRISCState *env, uint32_t a, uint32_t b, uint32_t c) { - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - uint32_t result; - set_float_exception_flags(0, &cpu->env.fp_status); - /* Note that or1ksim doesn't use merged operation. */ - result = float32_mul(b, c, &cpu->env.fp_status); - result = float32_add(result, a, &cpu->env.fp_status); - update_fpcsr(cpu); - return result; + /* Note that or1ksim doesn't use fused operation. */ + b = float32_mul(b, c, &env->fp_status); + return float32_add(a, b, &env->fp_status); } -#define FLOAT_CMP(name) \ -uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ - uint64_t fdt0, uint64_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = float64_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ - uint32_t fdt0, uint32_t fdt1)\ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = float32_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} +#define FLOAT_CMP(name, impl) \ +target_ulong helper_float_ ## name ## _d(CPUOpenRISCState *env, \ + uint64_t fdt0, uint64_t fdt1) \ +{ return float64_ ## impl(fdt0, fdt1, &env->fp_status); } \ +target_ulong helper_float_ ## name ## _s(CPUOpenRISCState *env, \ + uint32_t fdt0, uint32_t fdt1) \ +{ return float32_ ## impl(fdt0, fdt1, &env->fp_status); } -FLOAT_CMP(le) -FLOAT_CMP(eq) -FLOAT_CMP(lt) +FLOAT_CMP(le, le) +FLOAT_CMP(lt, lt) +FLOAT_CMP(eq, eq_quiet) #undef FLOAT_CMP - - -#define FLOAT_CMPNE(name) \ -uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ - uint64_t fdt0, uint64_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float64_eq_quiet(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ - uint32_t fdt0, uint32_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float32_eq_quiet(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} - -FLOAT_CMPNE(ne) -#undef FLOAT_CMPNE - -#define FLOAT_CMPGT(name) \ -uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ - uint64_t fdt0, uint64_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float64_le(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ - uint32_t fdt0, uint32_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float32_le(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} -FLOAT_CMPGT(gt) -#undef FLOAT_CMPGT - -#define FLOAT_CMPGE(name) \ -uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ - uint64_t fdt0, uint64_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float64_lt(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ - uint32_t fdt0, uint32_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float32_lt(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} - -FLOAT_CMPGE(ge) -#undef FLOAT_CMPGE diff --git a/target/openrisc/helper.h b/target/openrisc/helper.h index 4fd1a6bb8e..e37dabc77a 100644 --- a/target/openrisc/helper.h +++ b/target/openrisc/helper.h @@ -24,17 +24,19 @@ DEF_HELPER_FLAGS_1(ove_ov, TCG_CALL_NO_WG, void, env) DEF_HELPER_FLAGS_1(ove_cyov, TCG_CALL_NO_WG, void, env) /* float */ -DEF_HELPER_FLAGS_2(itofd, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(itofs, TCG_CALL_NO_WG, i32, env, i32) -DEF_HELPER_FLAGS_2(ftoid, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(ftois, TCG_CALL_NO_WG, i32, env, i32) +DEF_HELPER_FLAGS_1(update_fpcsr, TCG_CALL_NO_WG, void, env) -DEF_HELPER_FLAGS_4(float_madd_s, TCG_CALL_NO_WG, i32, env, i32, i32, i32) -DEF_HELPER_FLAGS_4(float_madd_d, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_2(itofd, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(itofs, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(ftoid, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(ftois, TCG_CALL_NO_RWG, i32, env, i32) + +DEF_HELPER_FLAGS_4(float_madd_s, TCG_CALL_NO_RWG, i32, env, i32, i32, i32) +DEF_HELPER_FLAGS_4(float_madd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) #define FOP_CALC(op) \ -DEF_HELPER_FLAGS_3(float_ ## op ## _s, TCG_CALL_NO_WG, i32, env, i32, i32) \ -DEF_HELPER_FLAGS_3(float_ ## op ## _d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(float_ ## op ## _s, TCG_CALL_NO_RWG, i32, env, i32, i32) \ +DEF_HELPER_FLAGS_3(float_ ## op ## _d, TCG_CALL_NO_RWG, i64, env, i64, i64) FOP_CALC(add) FOP_CALC(sub) FOP_CALC(mul) @@ -43,14 +45,11 @@ FOP_CALC(rem) #undef FOP_CALC #define FOP_CMP(op) \ -DEF_HELPER_FLAGS_3(float_ ## op ## _s, TCG_CALL_NO_WG, i32, env, i32, i32) \ -DEF_HELPER_FLAGS_3(float_ ## op ## _d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(float_ ## op ## _s, TCG_CALL_NO_RWG, tl, env, i32, i32) \ +DEF_HELPER_FLAGS_3(float_ ## op ## _d, TCG_CALL_NO_RWG, tl, env, i64, i64) FOP_CMP(eq) FOP_CMP(lt) FOP_CMP(le) -FOP_CMP(ne) -FOP_CMP(gt) -FOP_CMP(ge) #undef FOP_CMP /* interrupt */ diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 7cf29cd5b0..586c85df5d 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1293,180 +1293,209 @@ static void dec_float(DisasContext *dc, uint32_t insn) rd = extract32(insn, 21, 5); switch (op0) { - case 0x00: /* lf.add.s */ + case 0x00: /* lf.add.s */ LOG_DIS("lf.add.s r%d, r%d, r%d\n", rd, ra, rb); check_r0_write(rd); gen_helper_float_add_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x01: /* lf.sub.s */ + case 0x01: /* lf.sub.s */ LOG_DIS("lf.sub.s r%d, r%d, r%d\n", rd, ra, rb); check_r0_write(rd); gen_helper_float_sub_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; case 0x02: /* lf.mul.s */ LOG_DIS("lf.mul.s r%d, r%d, r%d\n", rd, ra, rb); check_r0_write(rd); gen_helper_float_mul_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x03: /* lf.div.s */ + case 0x03: /* lf.div.s */ LOG_DIS("lf.div.s r%d, r%d, r%d\n", rd, ra, rb); check_r0_write(rd); gen_helper_float_div_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x04: /* lf.itof.s */ + case 0x04: /* lf.itof.s */ LOG_DIS("lf.itof r%d, r%d\n", rd, ra); check_r0_write(rd); gen_helper_itofs(cpu_R[rd], cpu_env, cpu_R[ra]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x05: /* lf.ftoi.s */ + case 0x05: /* lf.ftoi.s */ LOG_DIS("lf.ftoi r%d, r%d\n", rd, ra); check_r0_write(rd); gen_helper_ftois(cpu_R[rd], cpu_env, cpu_R[ra]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x06: /* lf.rem.s */ + case 0x06: /* lf.rem.s */ LOG_DIS("lf.rem.s r%d, r%d, r%d\n", rd, ra, rb); check_r0_write(rd); gen_helper_float_rem_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x07: /* lf.madd.s */ + case 0x07: /* lf.madd.s */ LOG_DIS("lf.madd.s r%d, r%d, r%d\n", rd, ra, rb); check_r0_write(rd); gen_helper_float_madd_s(cpu_R[rd], cpu_env, cpu_R[rd], cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x08: /* lf.sfeq.s */ + case 0x08: /* lf.sfeq.s */ LOG_DIS("lf.sfeq.s r%d, r%d\n", ra, rb); gen_helper_float_eq_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x09: /* lf.sfne.s */ + case 0x09: /* lf.sfne.s */ LOG_DIS("lf.sfne.s r%d, r%d\n", ra, rb); - gen_helper_float_ne_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_float_eq_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1); + gen_helper_update_fpcsr(cpu_env); break; - case 0x0a: /* lf.sfgt.s */ + case 0x0a: /* lf.sfgt.s */ LOG_DIS("lf.sfgt.s r%d, r%d\n", ra, rb); - gen_helper_float_gt_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_float_lt_s(cpu_sr_f, cpu_env, cpu_R[rb], cpu_R[ra]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x0b: /* lf.sfge.s */ + case 0x0b: /* lf.sfge.s */ LOG_DIS("lf.sfge.s r%d, r%d\n", ra, rb); - gen_helper_float_ge_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_float_le_s(cpu_sr_f, cpu_env, cpu_R[rb], cpu_R[ra]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x0c: /* lf.sflt.s */ + case 0x0c: /* lf.sflt.s */ LOG_DIS("lf.sflt.s r%d, r%d\n", ra, rb); gen_helper_float_lt_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x0d: /* lf.sfle.s */ + case 0x0d: /* lf.sfle.s */ LOG_DIS("lf.sfle.s r%d, r%d\n", ra, rb); gen_helper_float_le_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; -/* not used yet, open it when we need or64. */ -/*#ifdef TARGET_OPENRISC64 - case 0x10: lf.add.d +#ifdef TARGET_OPENRISC64 + case 0x10: /* lf.add.d */ LOG_DIS("lf.add.d r%d, r%d, r%d\n", rd, ra, rb); check_of64s(dc); check_r0_write(rd); gen_helper_float_add_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x11: lf.sub.d + case 0x11: /* lf.sub.d */ LOG_DIS("lf.sub.d r%d, r%d, r%d\n", rd, ra, rb); check_of64s(dc); check_r0_write(rd); gen_helper_float_sub_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x12: lf.mul.d + case 0x12: /* lf.mul.d */ LOG_DIS("lf.mul.d r%d, r%d, r%d\n", rd, ra, rb); check_of64s(dc); check_r0_write(rd); gen_helper_float_mul_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x13: lf.div.d + case 0x13: /* lf.div.d */ LOG_DIS("lf.div.d r%d, r%d, r%d\n", rd, ra, rb); check_of64s(dc); check_r0_write(rd); gen_helper_float_div_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x14: lf.itof.d + case 0x14: /* lf.itof.d */ LOG_DIS("lf.itof r%d, r%d\n", rd, ra); check_of64s(dc); check_r0_write(rd); gen_helper_itofd(cpu_R[rd], cpu_env, cpu_R[ra]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x15: lf.ftoi.d + case 0x15: /* lf.ftoi.d */ LOG_DIS("lf.ftoi r%d, r%d\n", rd, ra); check_of64s(dc); check_r0_write(rd); gen_helper_ftoid(cpu_R[rd], cpu_env, cpu_R[ra]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x16: lf.rem.d + case 0x16: /* lf.rem.d */ LOG_DIS("lf.rem.d r%d, r%d, r%d\n", rd, ra, rb); check_of64s(dc); check_r0_write(rd); gen_helper_float_rem_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x17: lf.madd.d + case 0x17: /* lf.madd.d */ LOG_DIS("lf.madd.d r%d, r%d, r%d\n", rd, ra, rb); check_of64s(dc); check_r0_write(rd); gen_helper_float_madd_d(cpu_R[rd], cpu_env, cpu_R[rd], cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x18: lf.sfeq.d + case 0x18: /* lf.sfeq.d */ LOG_DIS("lf.sfeq.d r%d, r%d\n", ra, rb); check_of64s(dc); gen_helper_float_eq_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x1a: lf.sfgt.d + case 0x1a: /* lf.sfgt.d */ LOG_DIS("lf.sfgt.d r%d, r%d\n", ra, rb); check_of64s(dc); - gen_helper_float_gt_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_float_lt_d(cpu_sr_f, cpu_env, cpu_R[rb], cpu_R[ra]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x1b: lf.sfge.d + case 0x1b: /* lf.sfge.d */ LOG_DIS("lf.sfge.d r%d, r%d\n", ra, rb); check_of64s(dc); - gen_helper_float_ge_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_float_le_d(cpu_sr_f, cpu_env, cpu_R[rb], cpu_R[ra]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x19: lf.sfne.d + case 0x19: /* lf.sfne.d */ LOG_DIS("lf.sfne.d r%d, r%d\n", ra, rb); check_of64s(dc); - gen_helper_float_ne_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_float_eq_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1); + gen_helper_update_fpcsr(cpu_env); break; - case 0x1c: lf.sflt.d + case 0x1c: /* lf.sflt.d */ LOG_DIS("lf.sflt.d r%d, r%d\n", ra, rb); check_of64s(dc); gen_helper_float_lt_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; - case 0x1d: lf.sfle.d + case 0x1d: /* lf.sfle.d */ LOG_DIS("lf.sfle.d r%d, r%d\n", ra, rb); check_of64s(dc); gen_helper_float_le_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); + gen_helper_update_fpcsr(cpu_env); break; -#endif*/ +#endif default: gen_illegal_exception(dc); From 7de9729f08043399325e37b26637493313b4543d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Feb 2018 12:28:12 -0800 Subject: [PATCH 0418/2380] target/openrisc: Start conversion to decodetree.py Begin with the 0x08 major opcode, the system instructions. Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/Makefile.objs | 9 ++++ target/openrisc/insns.decode | 28 ++++++++++++ target/openrisc/translate.c | 84 +++++++++++++++++------------------ 3 files changed, 78 insertions(+), 43 deletions(-) create mode 100644 target/openrisc/insns.decode diff --git a/target/openrisc/Makefile.objs b/target/openrisc/Makefile.objs index 918b1c6e9c..1b98a911ea 100644 --- a/target/openrisc/Makefile.objs +++ b/target/openrisc/Makefile.objs @@ -3,3 +3,12 @@ obj-y += cpu.o exception.o interrupt.o mmu.o translate.o obj-y += exception_helper.o fpu_helper.o \ interrupt_helper.o mmu_helper.o sys_helper.o obj-y += gdbstub.o + +DECODETREE = $(SRC_PATH)/scripts/decodetree.py + +target/openrisc/decode.inc.c: \ + $(SRC_PATH)/target/openrisc/insns.decode $(DECODETREE) + $(call quiet-command,\ + $(PYTHON) $(DECODETREE) -o $@ $<, "GEN", $(TARGET_DIR)$@) + +target/openrisc/translate.o: target/openrisc/decode.inc.c diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode new file mode 100644 index 0000000000..47d31afc5b --- /dev/null +++ b/target/openrisc/insns.decode @@ -0,0 +1,28 @@ +# +# OpenRISC instruction decode definitions. +# +# Copyright (c) 2018 Richard Henderson +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + +#### +# System Instructions +#### + +l_sys 001000 0000000000 k:16 +l_trap 001000 0100000000 k:16 +l_msync 001000 1000000000 00000000 00000000 +l_psync 001000 1010000000 00000000 00000000 +l_csync 001000 1100000000 00000000 00000000 diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 586c85df5d..a4b67f94c5 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -31,6 +31,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#include "exec/gen-icount.h" #include "trace-tcg.h" #include "exec/log.h" @@ -51,6 +52,9 @@ typedef struct DisasContext { uint32_t delayed_branch; } DisasContext; +/* Include the auto-generated decoder. */ +#include "decode.inc.c" + static TCGv cpu_sr; static TCGv cpu_R[32]; static TCGv cpu_R0; @@ -65,7 +69,6 @@ static TCGv cpu_lock_value; static TCGv_i32 fpcsr; static TCGv_i64 cpu_mac; /* MACHI:MACLO */ static TCGv_i32 cpu_dflag; -#include "exec/gen-icount.h" void openrisc_translate_init(void) { @@ -1241,46 +1244,41 @@ static void dec_compi(DisasContext *dc, uint32_t insn) } } -static void dec_sys(DisasContext *dc, uint32_t insn) +static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn) { - uint32_t op0; - uint32_t K16; + LOG_DIS("l.sys %d\n", a->k); + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + gen_exception(dc, EXCP_SYSCALL); + dc->base.is_jmp = DISAS_NORETURN; + return true; +} - op0 = extract32(insn, 16, 10); - K16 = extract32(insn, 0, 16); +static bool trans_l_trap(DisasContext *dc, arg_l_trap *a, uint32_t insn) +{ + LOG_DIS("l.trap %d\n", a->k); + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + gen_exception(dc, EXCP_TRAP); + dc->base.is_jmp = DISAS_NORETURN; + return true; +} - switch (op0) { - case 0x000: /* l.sys */ - LOG_DIS("l.sys %d\n", K16); - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - gen_exception(dc, EXCP_SYSCALL); - dc->base.is_jmp = DISAS_NORETURN; - break; +static bool trans_l_msync(DisasContext *dc, arg_l_msync *a, uint32_t insn) +{ + LOG_DIS("l.msync\n"); + tcg_gen_mb(TCG_MO_ALL); + return true; +} - case 0x100: /* l.trap */ - LOG_DIS("l.trap %d\n", K16); - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - gen_exception(dc, EXCP_TRAP); - dc->base.is_jmp = DISAS_NORETURN; - break; +static bool trans_l_psync(DisasContext *dc, arg_l_psync *a, uint32_t insn) +{ + LOG_DIS("l.psync\n"); + return true; +} - case 0x300: /* l.csync */ - LOG_DIS("l.csync\n"); - break; - - case 0x200: /* l.msync */ - LOG_DIS("l.msync\n"); - tcg_gen_mb(TCG_MO_ALL); - break; - - case 0x270: /* l.psync */ - LOG_DIS("l.psync\n"); - break; - - default: - gen_illegal_exception(dc); - break; - } +static bool trans_l_csync(DisasContext *dc, arg_l_csync *a, uint32_t insn) +{ + LOG_DIS("l.csync\n"); + return true; } static void dec_float(DisasContext *dc, uint32_t insn) @@ -1506,19 +1504,19 @@ static void dec_float(DisasContext *dc, uint32_t insn) static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) { uint32_t op0; - uint32_t insn; - insn = cpu_ldl_code(&cpu->env, dc->base.pc_next); - op0 = extract32(insn, 26, 6); + uint32_t insn = cpu_ldl_code(&cpu->env, dc->base.pc_next); + /* Transition to the auto-generated decoder. */ + if (decode(dc, insn)) { + return; + } + + op0 = extract32(insn, 26, 6); switch (op0) { case 0x06: dec_M(dc, insn); break; - case 0x08: - dec_sys(dc, insn); - break; - case 0x2e: dec_logic(dc, insn); break; From 136e13ae65c654da39f54e300a23fc833a4980d4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Feb 2018 12:52:43 -0800 Subject: [PATCH 0419/2380] target/openrisc: Convert branch insns Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/insns.decode | 12 +++ target/openrisc/translate.c | 150 +++++++++++++++++------------------ 2 files changed, 84 insertions(+), 78 deletions(-) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index 47d31afc5b..8d35011eab 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -26,3 +26,15 @@ l_trap 001000 0100000000 k:16 l_msync 001000 1000000000 00000000 00000000 l_psync 001000 1010000000 00000000 00000000 l_csync 001000 1100000000 00000000 00000000 + +#### +# Branch Instructions +#### + +l_j 000000 n:s26 +l_jal 000001 n:s26 +l_bnf 000011 n:s26 +l_bf 000100 n:s26 + +l_jr 010001 ---------- b:5 ----------- +l_jalr 010010 ---------- b:5 ----------- diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index a4b67f94c5..3a02a1d688 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -193,52 +193,6 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) } } -static void gen_jump(DisasContext *dc, int32_t n26, uint32_t reg, uint32_t op0) -{ - target_ulong tmp_pc = dc->base.pc_next + n26 * 4; - - switch (op0) { - case 0x00: /* l.j */ - tcg_gen_movi_tl(jmp_pc, tmp_pc); - break; - case 0x01: /* l.jal */ - tcg_gen_movi_tl(cpu_R[9], dc->base.pc_next + 8); - /* Optimize jal being used to load the PC for PIC. */ - if (tmp_pc == dc->base.pc_next + 8) { - return; - } - tcg_gen_movi_tl(jmp_pc, tmp_pc); - break; - case 0x03: /* l.bnf */ - case 0x04: /* l.bf */ - { - TCGv t_next = tcg_const_tl(dc->base.pc_next + 8); - TCGv t_true = tcg_const_tl(tmp_pc); - TCGv t_zero = tcg_const_tl(0); - - tcg_gen_movcond_tl(op0 == 0x03 ? TCG_COND_EQ : TCG_COND_NE, - jmp_pc, cpu_sr_f, t_zero, t_true, t_next); - - tcg_temp_free(t_next); - tcg_temp_free(t_true); - tcg_temp_free(t_zero); - } - break; - case 0x11: /* l.jr */ - tcg_gen_mov_tl(jmp_pc, cpu_R[reg]); - break; - case 0x12: /* l.jalr */ - tcg_gen_movi_tl(cpu_R[9], (dc->base.pc_next + 8)); - tcg_gen_mov_tl(jmp_pc, cpu_R[reg]); - break; - default: - gen_illegal_exception(dc); - break; - } - - dc->delayed_branch = 2; -} - static void gen_ove_cy(DisasContext *dc) { if (dc->tb_flags & SR_OVE) { @@ -713,12 +667,83 @@ static void dec_calc(DisasContext *dc, uint32_t insn) gen_illegal_exception(dc); } +static bool trans_l_j(DisasContext *dc, arg_l_j *a, uint32_t insn) +{ + target_ulong tmp_pc = dc->base.pc_next + a->n * 4; + + LOG_DIS("l.j %d\n", a->n); + tcg_gen_movi_tl(jmp_pc, tmp_pc); + dc->delayed_branch = 2; + return true; +} + +static bool trans_l_jal(DisasContext *dc, arg_l_jal *a, uint32_t insn) +{ + target_ulong tmp_pc = dc->base.pc_next + a->n * 4; + target_ulong ret_pc = dc->base.pc_next + 8; + + LOG_DIS("l.jal %d\n", a->n); + tcg_gen_movi_tl(cpu_R[9], ret_pc); + /* Optimize jal being used to load the PC for PIC. */ + if (tmp_pc != ret_pc) { + tcg_gen_movi_tl(jmp_pc, tmp_pc); + dc->delayed_branch = 2; + } + return true; +} + +static void do_bf(DisasContext *dc, arg_l_bf *a, TCGCond cond) +{ + target_ulong tmp_pc = dc->base.pc_next + a->n * 4; + TCGv t_next = tcg_const_tl(dc->base.pc_next + 8); + TCGv t_true = tcg_const_tl(tmp_pc); + TCGv t_zero = tcg_const_tl(0); + + tcg_gen_movcond_tl(cond, jmp_pc, cpu_sr_f, t_zero, t_true, t_next); + + tcg_temp_free(t_next); + tcg_temp_free(t_true); + tcg_temp_free(t_zero); + dc->delayed_branch = 2; +} + +static bool trans_l_bf(DisasContext *dc, arg_l_bf *a, uint32_t insn) +{ + LOG_DIS("l.bf %d\n", a->n); + do_bf(dc, a, TCG_COND_NE); + return true; +} + +static bool trans_l_bnf(DisasContext *dc, arg_l_bf *a, uint32_t insn) +{ + LOG_DIS("l.bnf %d\n", a->n); + do_bf(dc, a, TCG_COND_EQ); + return true; +} + +static bool trans_l_jr(DisasContext *dc, arg_l_jr *a, uint32_t insn) +{ + LOG_DIS("l.jr r%d\n", a->b); + tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]); + dc->delayed_branch = 2; + return true; +} + +static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a, uint32_t insn) +{ + LOG_DIS("l.jalr r%d\n", a->b); + tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]); + tcg_gen_movi_tl(cpu_R[9], dc->base.pc_next + 8); + dc->delayed_branch = 2; + return true; +} + static void dec_misc(DisasContext *dc, uint32_t insn) { uint32_t op0, op1; uint32_t ra, rb, rd; uint32_t L6, K5, K16, K5_11; - int32_t I16, I5_11, N26; + int32_t I16, I5_11; TCGMemOp mop; TCGv t0; @@ -731,31 +756,10 @@ static void dec_misc(DisasContext *dc, uint32_t insn) K5 = extract32(insn, 0, 5); K16 = extract32(insn, 0, 16); I16 = (int16_t)K16; - N26 = sextract32(insn, 0, 26); K5_11 = (extract32(insn, 21, 5) << 11) | extract32(insn, 0, 11); I5_11 = (int16_t)K5_11; switch (op0) { - case 0x00: /* l.j */ - LOG_DIS("l.j %d\n", N26); - gen_jump(dc, N26, 0, op0); - break; - - case 0x01: /* l.jal */ - LOG_DIS("l.jal %d\n", N26); - gen_jump(dc, N26, 0, op0); - break; - - case 0x03: /* l.bnf */ - LOG_DIS("l.bnf %d\n", N26); - gen_jump(dc, N26, 0, op0); - break; - - case 0x04: /* l.bf */ - LOG_DIS("l.bf %d\n", N26); - gen_jump(dc, N26, 0, op0); - break; - case 0x05: switch (op1) { case 0x01: /* l.nop */ @@ -768,16 +772,6 @@ static void dec_misc(DisasContext *dc, uint32_t insn) } break; - case 0x11: /* l.jr */ - LOG_DIS("l.jr r%d\n", rb); - gen_jump(dc, 0, rb, op0); - break; - - case 0x12: /* l.jalr */ - LOG_DIS("l.jalr r%d\n", rb); - gen_jump(dc, 0, rb, op0); - break; - case 0x13: /* l.maci */ LOG_DIS("l.maci r%d, %d\n", ra, I16); t0 = tcg_const_tl(I16); From d80bff19f9c6dbde11e8c246e245dff77ae8ac94 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 08:21:12 -0800 Subject: [PATCH 0420/2380] target/openrisc: Convert memory insns Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/insns.decode | 24 +++ target/openrisc/translate.c | 275 +++++++++++++++++------------------ 2 files changed, 160 insertions(+), 139 deletions(-) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index 8d35011eab..ba5356abe1 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -38,3 +38,27 @@ l_bf 000100 n:s26 l_jr 010001 ---------- b:5 ----------- l_jalr 010010 ---------- b:5 ----------- + +#### +# Memory Instructions +#### + +&load d a i +@load ...... d:5 a:5 i:s16 &load + +%store_i 21:s5 0:11 +&store a b i +@store ...... ..... a:5 b:5 ........... &store i=%store_i + +l_lwa 011011 ..... ..... ........ ........ @load +l_lwz 100001 ..... ..... ........ ........ @load +l_lws 100010 ..... ..... ........ ........ @load +l_lbz 100011 ..... ..... ........ ........ @load +l_lbs 100100 ..... ..... ........ ........ @load +l_lhz 100101 ..... ..... ........ ........ @load +l_lhs 100110 ..... ..... ........ ........ @load + +l_swa 110011 ..... ..... ........ ........ @store +l_sw 110101 ..... ..... ........ ........ @store +l_sb 110110 ..... ..... ........ ........ @store +l_sh 110111 ..... ..... ........ ........ @store diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 3a02a1d688..4f9388a4c2 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -455,51 +455,6 @@ static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb) gen_ove_cy(dc); } -static void gen_lwa(DisasContext *dc, TCGv rd, TCGv ra, int32_t ofs) -{ - TCGv ea = tcg_temp_new(); - - tcg_gen_addi_tl(ea, ra, ofs); - tcg_gen_qemu_ld_tl(rd, ea, dc->mem_idx, MO_TEUL); - tcg_gen_mov_tl(cpu_lock_addr, ea); - tcg_gen_mov_tl(cpu_lock_value, rd); - tcg_temp_free(ea); -} - -static void gen_swa(DisasContext *dc, int b, TCGv ra, int32_t ofs) -{ - TCGv ea, val; - TCGLabel *lab_fail, *lab_done; - - ea = tcg_temp_new(); - tcg_gen_addi_tl(ea, ra, ofs); - - /* For TB_FLAGS_R0_0, the branch below invalidates the temporary assigned - to cpu_R[0]. Since l.swa is quite often immediately followed by a - branch, don't bother reallocating; finish the TB using the "real" R0. - This also takes care of RB input across the branch. */ - cpu_R[0] = cpu_R0; - - lab_fail = gen_new_label(); - lab_done = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, ea, cpu_lock_addr, lab_fail); - tcg_temp_free(ea); - - val = tcg_temp_new(); - tcg_gen_atomic_cmpxchg_tl(val, cpu_lock_addr, cpu_lock_value, - cpu_R[b], dc->mem_idx, MO_TEUL); - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, val, cpu_lock_value); - tcg_temp_free(val); - - tcg_gen_br(lab_done); - - gen_set_label(lab_fail); - tcg_gen_movi_tl(cpu_sr_f, 0); - - gen_set_label(lab_done); - tcg_gen_movi_tl(cpu_lock_addr, -1); -} - static void dec_calc(DisasContext *dc, uint32_t insn) { uint32_t op0, op1, op2; @@ -738,13 +693,147 @@ static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a, uint32_t insn) return true; } +static bool trans_l_lwa(DisasContext *dc, arg_load *a, uint32_t insn) +{ + TCGv ea; + + LOG_DIS("l.lwa r%d, r%d, %d\n", a->d, a->a, a->i); + + check_r0_write(a->d); + ea = tcg_temp_new(); + tcg_gen_addi_tl(ea, cpu_R[a->a], a->i); + tcg_gen_qemu_ld_tl(cpu_R[a->d], ea, dc->mem_idx, MO_TEUL); + tcg_gen_mov_tl(cpu_lock_addr, ea); + tcg_gen_mov_tl(cpu_lock_value, cpu_R[a->d]); + tcg_temp_free(ea); + return true; +} + +static void do_load(DisasContext *dc, arg_load *a, TCGMemOp mop) +{ + TCGv ea; + + check_r0_write(a->d); + ea = tcg_temp_new(); + tcg_gen_addi_tl(ea, cpu_R[a->a], a->i); + tcg_gen_qemu_ld_tl(cpu_R[a->d], ea, dc->mem_idx, mop); + tcg_temp_free(ea); +} + +static bool trans_l_lwz(DisasContext *dc, arg_load *a, uint32_t insn) +{ + LOG_DIS("l.lwz r%d, r%d, %d\n", a->d, a->a, a->i); + do_load(dc, a, MO_TEUL); + return true; +} + +static bool trans_l_lws(DisasContext *dc, arg_load *a, uint32_t insn) +{ + LOG_DIS("l.lws r%d, r%d, %d\n", a->d, a->a, a->i); + do_load(dc, a, MO_TESL); + return true; +} + +static bool trans_l_lbz(DisasContext *dc, arg_load *a, uint32_t insn) +{ + LOG_DIS("l.lbz r%d, r%d, %d\n", a->d, a->a, a->i); + do_load(dc, a, MO_UB); + return true; +} + +static bool trans_l_lbs(DisasContext *dc, arg_load *a, uint32_t insn) +{ + LOG_DIS("l.lbs r%d, r%d, %d\n", a->d, a->a, a->i); + do_load(dc, a, MO_SB); + return true; +} + +static bool trans_l_lhz(DisasContext *dc, arg_load *a, uint32_t insn) +{ + LOG_DIS("l.lhz r%d, r%d, %d\n", a->d, a->a, a->i); + do_load(dc, a, MO_TEUW); + return true; +} + +static bool trans_l_lhs(DisasContext *dc, arg_load *a, uint32_t insn) +{ + LOG_DIS("l.lhs r%d, r%d, %d\n", a->d, a->a, a->i); + do_load(dc, a, MO_TESW); + return true; +} + +static bool trans_l_swa(DisasContext *dc, arg_store *a, uint32_t insn) +{ + TCGv ea, val; + TCGLabel *lab_fail, *lab_done; + + LOG_DIS("l.swa r%d, r%d, %d\n", a->a, a->b, a->i); + + ea = tcg_temp_new(); + tcg_gen_addi_tl(ea, cpu_R[a->a], a->i); + + /* For TB_FLAGS_R0_0, the branch below invalidates the temporary assigned + to cpu_R[0]. Since l.swa is quite often immediately followed by a + branch, don't bother reallocating; finish the TB using the "real" R0. + This also takes care of RB input across the branch. */ + cpu_R[0] = cpu_R0; + + lab_fail = gen_new_label(); + lab_done = gen_new_label(); + tcg_gen_brcond_tl(TCG_COND_NE, ea, cpu_lock_addr, lab_fail); + tcg_temp_free(ea); + + val = tcg_temp_new(); + tcg_gen_atomic_cmpxchg_tl(val, cpu_lock_addr, cpu_lock_value, + cpu_R[a->b], dc->mem_idx, MO_TEUL); + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, val, cpu_lock_value); + tcg_temp_free(val); + + tcg_gen_br(lab_done); + + gen_set_label(lab_fail); + tcg_gen_movi_tl(cpu_sr_f, 0); + + gen_set_label(lab_done); + tcg_gen_movi_tl(cpu_lock_addr, -1); + return true; +} + +static void do_store(DisasContext *dc, arg_store *a, TCGMemOp mop) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[a->a], a->i); + tcg_gen_qemu_st_tl(cpu_R[a->b], t0, dc->mem_idx, mop); + tcg_temp_free(t0); +} + +static bool trans_l_sw(DisasContext *dc, arg_store *a, uint32_t insn) +{ + LOG_DIS("l.sw r%d, r%d, %d\n", a->a, a->b, a->i); + do_store(dc, a, MO_TEUL); + return true; +} + +static bool trans_l_sb(DisasContext *dc, arg_store *a, uint32_t insn) +{ + LOG_DIS("l.sb r%d, r%d, %d\n", a->a, a->b, a->i); + do_store(dc, a, MO_UB); + return true; +} + +static bool trans_l_sh(DisasContext *dc, arg_store *a, uint32_t insn) +{ + LOG_DIS("l.sh r%d, r%d, %d\n", a->a, a->b, a->i); + do_store(dc, a, MO_TEUW); + return true; +} + static void dec_misc(DisasContext *dc, uint32_t insn) { uint32_t op0, op1; uint32_t ra, rb, rd; uint32_t L6, K5, K16, K5_11; - int32_t I16, I5_11; - TCGMemOp mop; + int32_t I16; TCGv t0; op0 = extract32(insn, 26, 6); @@ -757,7 +846,6 @@ static void dec_misc(DisasContext *dc, uint32_t insn) K16 = extract32(insn, 0, 16); I16 = (int16_t)K16; K5_11 = (extract32(insn, 21, 5) << 11) | extract32(insn, 0, 11); - I5_11 = (int16_t)K5_11; switch (op0) { case 0x05: @@ -795,12 +883,6 @@ static void dec_misc(DisasContext *dc, uint32_t insn) } break; - case 0x1b: /* l.lwa */ - LOG_DIS("l.lwa r%d, r%d, %d\n", rd, ra, I16); - check_r0_write(rd); - gen_lwa(dc, cpu_R[rd], cpu_R[ra], I16); - break; - case 0x1c: /* l.cust1 */ LOG_DIS("l.cust1\n"); break; @@ -833,53 +915,6 @@ static void dec_misc(DisasContext *dc, uint32_t insn) LOG_DIS("l.cust8\n"); break; -/* not used yet, open it when we need or64. */ -/*#ifdef TARGET_OPENRISC64 - case 0x20: l.ld - LOG_DIS("l.ld r%d, r%d, %d\n", rd, ra, I16); - check_ob64s(dc); - mop = MO_TEQ; - goto do_load; -#endif*/ - - case 0x21: /* l.lwz */ - LOG_DIS("l.lwz r%d, r%d, %d\n", rd, ra, I16); - mop = MO_TEUL; - goto do_load; - - case 0x22: /* l.lws */ - LOG_DIS("l.lws r%d, r%d, %d\n", rd, ra, I16); - mop = MO_TESL; - goto do_load; - - case 0x23: /* l.lbz */ - LOG_DIS("l.lbz r%d, r%d, %d\n", rd, ra, I16); - mop = MO_UB; - goto do_load; - - case 0x24: /* l.lbs */ - LOG_DIS("l.lbs r%d, r%d, %d\n", rd, ra, I16); - mop = MO_SB; - goto do_load; - - case 0x25: /* l.lhz */ - LOG_DIS("l.lhz r%d, r%d, %d\n", rd, ra, I16); - mop = MO_TEUW; - goto do_load; - - case 0x26: /* l.lhs */ - LOG_DIS("l.lhs r%d, r%d, %d\n", rd, ra, I16); - mop = MO_TESW; - goto do_load; - - do_load: - check_r0_write(rd); - t0 = tcg_temp_new(); - tcg_gen_addi_tl(t0, cpu_R[ra], I16); - tcg_gen_qemu_ld_tl(cpu_R[rd], t0, dc->mem_idx, mop); - tcg_temp_free(t0); - break; - case 0x27: /* l.addi */ LOG_DIS("l.addi r%d, r%d, %d\n", rd, ra, I16); check_r0_write(rd); @@ -957,44 +992,6 @@ static void dec_misc(DisasContext *dc, uint32_t insn) } break; - case 0x33: /* l.swa */ - LOG_DIS("l.swa r%d, r%d, %d\n", ra, rb, I5_11); - gen_swa(dc, rb, cpu_R[ra], I5_11); - break; - -/* not used yet, open it when we need or64. */ -/*#ifdef TARGET_OPENRISC64 - case 0x34: l.sd - LOG_DIS("l.sd r%d, r%d, %d\n", ra, rb, I5_11); - check_ob64s(dc); - mop = MO_TEQ; - goto do_store; -#endif*/ - - case 0x35: /* l.sw */ - LOG_DIS("l.sw r%d, r%d, %d\n", ra, rb, I5_11); - mop = MO_TEUL; - goto do_store; - - case 0x36: /* l.sb */ - LOG_DIS("l.sb r%d, r%d, %d\n", ra, rb, I5_11); - mop = MO_UB; - goto do_store; - - case 0x37: /* l.sh */ - LOG_DIS("l.sh r%d, r%d, %d\n", ra, rb, I5_11); - mop = MO_TEUW; - goto do_store; - - do_store: - { - TCGv t0 = tcg_temp_new(); - tcg_gen_addi_tl(t0, cpu_R[ra], I5_11); - tcg_gen_qemu_st_tl(cpu_R[rb], t0, dc->mem_idx, mop); - tcg_temp_free(t0); - } - break; - default: gen_illegal_exception(dc); break; From 8816f70b9372f3731d4c0810ab9bdd63b2eafd3b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 09:15:24 -0800 Subject: [PATCH 0421/2380] target/openrisc: Convert remainder of dec_misc insns Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/insns.decode | 35 ++++- target/openrisc/translate.c | 279 +++++++++++++++-------------------- 2 files changed, 151 insertions(+), 163 deletions(-) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index ba5356abe1..247a2e14f2 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -27,6 +27,8 @@ l_msync 001000 1000000000 00000000 00000000 l_psync 001000 1010000000 00000000 00000000 l_csync 001000 1100000000 00000000 00000000 +l_rfe 001001 ----- ----- -------- -------- + #### # Branch Instructions #### @@ -58,7 +60,32 @@ l_lbs 100100 ..... ..... ........ ........ @load l_lhz 100101 ..... ..... ........ ........ @load l_lhs 100110 ..... ..... ........ ........ @load -l_swa 110011 ..... ..... ........ ........ @store -l_sw 110101 ..... ..... ........ ........ @store -l_sb 110110 ..... ..... ........ ........ @store -l_sh 110111 ..... ..... ........ ........ @store +l_swa 110011 ..... ..... ..... ........... @store +l_sw 110101 ..... ..... ..... ........... @store +l_sb 110110 ..... ..... ..... ........... @store +l_sh 110111 ..... ..... ..... ........... @store + +#### +# Immediate Operand Instructions +#### + +%mtspr_k 21:5 0:11 + +&rri d a i +&rrk d a k +@rri ...... d:5 a:5 i:s16 &rri +@rrk ...... d:5 a:5 k:16 &rrk + +l_nop 000101 01--- ----- k:16 + +l_addi 100111 ..... ..... ........ ........ @rri +l_addic 101000 ..... ..... ........ ........ @rri +l_andi 101001 ..... ..... ........ ........ @rrk +l_ori 101010 ..... ..... ........ ........ @rrk +l_xori 101011 ..... ..... ........ ........ @rri +l_muli 101100 ..... ..... ........ ........ @rri + +l_mfspr 101101 ..... ..... ........ ........ @rrk +l_mtspr 110000 ..... a:5 b:5 ........... k=%mtspr_k + +l_maci 010011 ----- a:5 i:s16 diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 4f9388a4c2..e3dcaaf7a8 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -828,174 +828,118 @@ static bool trans_l_sh(DisasContext *dc, arg_store *a, uint32_t insn) return true; } -static void dec_misc(DisasContext *dc, uint32_t insn) +static bool trans_l_nop(DisasContext *dc, arg_l_nop *a, uint32_t insn) +{ + LOG_DIS("l.nop %d\n", a->k); + return true; +} + +static bool trans_l_addi(DisasContext *dc, arg_rri *a, uint32_t insn) { - uint32_t op0, op1; - uint32_t ra, rb, rd; - uint32_t L6, K5, K16, K5_11; - int32_t I16; TCGv t0; - op0 = extract32(insn, 26, 6); - op1 = extract32(insn, 24, 2); - ra = extract32(insn, 16, 5); - rb = extract32(insn, 11, 5); - rd = extract32(insn, 21, 5); - L6 = extract32(insn, 5, 6); - K5 = extract32(insn, 0, 5); - K16 = extract32(insn, 0, 16); - I16 = (int16_t)K16; - K5_11 = (extract32(insn, 21, 5) << 11) | extract32(insn, 0, 11); + LOG_DIS("l.addi r%d, r%d, %d\n", a->d, a->a, a->i); + check_r0_write(a->d); + t0 = tcg_const_tl(a->i); + gen_add(dc, cpu_R[a->d], cpu_R[a->a], t0); + tcg_temp_free(t0); + return true; +} - switch (op0) { - case 0x05: - switch (op1) { - case 0x01: /* l.nop */ - LOG_DIS("l.nop %d\n", I16); - break; +static bool trans_l_addic(DisasContext *dc, arg_rri *a, uint32_t insn) +{ + TCGv t0; - default: - gen_illegal_exception(dc); - break; - } - break; + LOG_DIS("l.addic r%d, r%d, %d\n", a->d, a->a, a->i); + check_r0_write(a->d); + t0 = tcg_const_tl(a->i); + gen_addc(dc, cpu_R[a->d], cpu_R[a->a], t0); + tcg_temp_free(t0); + return true; +} - case 0x13: /* l.maci */ - LOG_DIS("l.maci r%d, %d\n", ra, I16); - t0 = tcg_const_tl(I16); - gen_mac(dc, cpu_R[ra], t0); - tcg_temp_free(t0); - break; +static bool trans_l_muli(DisasContext *dc, arg_rri *a, uint32_t insn) +{ + TCGv t0; - case 0x09: /* l.rfe */ - LOG_DIS("l.rfe\n"); - { -#if defined(CONFIG_USER_ONLY) - return; + LOG_DIS("l.muli r%d, r%d, %d\n", a->d, a->a, a->i); + check_r0_write(a->d); + t0 = tcg_const_tl(a->i); + gen_mul(dc, cpu_R[a->d], cpu_R[a->a], t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_l_maci(DisasContext *dc, arg_l_maci *a, uint32_t insn) +{ + TCGv t0; + + LOG_DIS("l.maci r%d, %d\n", a->a, a->i); + t0 = tcg_const_tl(a->i); + gen_mac(dc, cpu_R[a->a], t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_l_andi(DisasContext *dc, arg_rrk *a, uint32_t insn) +{ + LOG_DIS("l.andi r%d, r%d, %d\n", a->d, a->a, a->k); + check_r0_write(a->d); + tcg_gen_andi_tl(cpu_R[a->d], cpu_R[a->a], a->k); + return true; +} + +static bool trans_l_ori(DisasContext *dc, arg_rrk *a, uint32_t insn) +{ + LOG_DIS("l.ori r%d, r%d, %d\n", a->d, a->a, a->k); + check_r0_write(a->d); + tcg_gen_ori_tl(cpu_R[a->d], cpu_R[a->a], a->k); + return true; +} + +static bool trans_l_xori(DisasContext *dc, arg_rri *a, uint32_t insn) +{ + LOG_DIS("l.xori r%d, r%d, %d\n", a->d, a->a, a->i); + check_r0_write(a->d); + tcg_gen_xori_tl(cpu_R[a->d], cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a, uint32_t insn) +{ + LOG_DIS("l.mfspr r%d, r%d, %d\n", a->d, a->a, a->k); + check_r0_write(a->d); + +#ifdef CONFIG_USER_ONLY + gen_illegal_exception(dc); #else - if (dc->mem_idx == MMU_USER_IDX) { - gen_illegal_exception(dc); - return; - } - gen_helper_rfe(cpu_env); - dc->base.is_jmp = DISAS_UPDATE; -#endif - } - break; - - case 0x1c: /* l.cust1 */ - LOG_DIS("l.cust1\n"); - break; - - case 0x1d: /* l.cust2 */ - LOG_DIS("l.cust2\n"); - break; - - case 0x1e: /* l.cust3 */ - LOG_DIS("l.cust3\n"); - break; - - case 0x1f: /* l.cust4 */ - LOG_DIS("l.cust4\n"); - break; - - case 0x3c: /* l.cust5 */ - LOG_DIS("l.cust5 r%d, r%d, r%d, %d, %d\n", rd, ra, rb, L6, K5); - break; - - case 0x3d: /* l.cust6 */ - LOG_DIS("l.cust6\n"); - break; - - case 0x3e: /* l.cust7 */ - LOG_DIS("l.cust7\n"); - break; - - case 0x3f: /* l.cust8 */ - LOG_DIS("l.cust8\n"); - break; - - case 0x27: /* l.addi */ - LOG_DIS("l.addi r%d, r%d, %d\n", rd, ra, I16); - check_r0_write(rd); - t0 = tcg_const_tl(I16); - gen_add(dc, cpu_R[rd], cpu_R[ra], t0); - tcg_temp_free(t0); - break; - - case 0x28: /* l.addic */ - LOG_DIS("l.addic r%d, r%d, %d\n", rd, ra, I16); - check_r0_write(rd); - t0 = tcg_const_tl(I16); - gen_addc(dc, cpu_R[rd], cpu_R[ra], t0); - tcg_temp_free(t0); - break; - - case 0x29: /* l.andi */ - LOG_DIS("l.andi r%d, r%d, %d\n", rd, ra, K16); - check_r0_write(rd); - tcg_gen_andi_tl(cpu_R[rd], cpu_R[ra], K16); - break; - - case 0x2a: /* l.ori */ - LOG_DIS("l.ori r%d, r%d, %d\n", rd, ra, K16); - check_r0_write(rd); - tcg_gen_ori_tl(cpu_R[rd], cpu_R[ra], K16); - break; - - case 0x2b: /* l.xori */ - LOG_DIS("l.xori r%d, r%d, %d\n", rd, ra, I16); - check_r0_write(rd); - tcg_gen_xori_tl(cpu_R[rd], cpu_R[ra], I16); - break; - - case 0x2c: /* l.muli */ - LOG_DIS("l.muli r%d, r%d, %d\n", rd, ra, I16); - check_r0_write(rd); - t0 = tcg_const_tl(I16); - gen_mul(dc, cpu_R[rd], cpu_R[ra], t0); - tcg_temp_free(t0); - break; - - case 0x2d: /* l.mfspr */ - LOG_DIS("l.mfspr r%d, r%d, %d\n", rd, ra, K16); - check_r0_write(rd); - { -#if defined(CONFIG_USER_ONLY) - return; -#else - TCGv_i32 ti = tcg_const_i32(K16); - if (dc->mem_idx == MMU_USER_IDX) { - gen_illegal_exception(dc); - return; - } - gen_helper_mfspr(cpu_R[rd], cpu_env, cpu_R[rd], cpu_R[ra], ti); - tcg_temp_free_i32(ti); -#endif - } - break; - - case 0x30: /* l.mtspr */ - LOG_DIS("l.mtspr r%d, r%d, %d\n", ra, rb, K5_11); - { -#if defined(CONFIG_USER_ONLY) - return; -#else - TCGv_i32 im = tcg_const_i32(K5_11); - if (dc->mem_idx == MMU_USER_IDX) { - gen_illegal_exception(dc); - return; - } - gen_helper_mtspr(cpu_env, cpu_R[ra], cpu_R[rb], im); - tcg_temp_free_i32(im); -#endif - } - break; - - default: + if (dc->mem_idx == MMU_USER_IDX) { gen_illegal_exception(dc); - break; + } else { + TCGv_i32 ti = tcg_const_i32(a->k); + gen_helper_mfspr(cpu_R[a->d], cpu_env, cpu_R[a->d], cpu_R[a->a], ti); + tcg_temp_free_i32(ti); } +#endif + return true; +} + +static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) +{ + LOG_DIS("l.mtspr r%d, r%d, %d\n", a->a, a->b, a->k); + +#ifdef CONFIG_USER_ONLY + gen_illegal_exception(dc); +#else + if (dc->mem_idx == MMU_USER_IDX) { + gen_illegal_exception(dc); + } else { + TCGv_i32 ti = tcg_const_i32(a->k); + gen_helper_mtspr(cpu_env, cpu_R[a->a], cpu_R[a->b], ti); + tcg_temp_free_i32(ti); + } +#endif + return true; } static void dec_mac(DisasContext *dc, uint32_t insn) @@ -1272,6 +1216,23 @@ static bool trans_l_csync(DisasContext *dc, arg_l_csync *a, uint32_t insn) return true; } +static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a, uint32_t insn) +{ + LOG_DIS("l.rfe\n"); + +#ifdef CONFIG_USER_ONLY + gen_illegal_exception(dc); +#else + if (dc->mem_idx == MMU_USER_IDX) { + gen_illegal_exception(dc); + } else { + gen_helper_rfe(cpu_env); + dc->base.is_jmp = DISAS_UPDATE; + } +#endif + return true; +} + static void dec_float(DisasContext *dc, uint32_t insn) { uint32_t op0; @@ -1533,7 +1494,7 @@ static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) break; default: - dec_misc(dc, insn); + gen_illegal_exception(dc); break; } } From 6ad216abfd40a781f202b1f7c61a5fdc4d6710bc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 10:04:14 -0800 Subject: [PATCH 0422/2380] target/openrisc: Convert dec_calc Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/insns.decode | 76 ++++++--- target/openrisc/translate.c | 322 +++++++++++++++++++---------------- 2 files changed, 229 insertions(+), 169 deletions(-) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index 247a2e14f2..20f035f488 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -17,6 +17,10 @@ # License along with this library; if not, see . # +&dab d a b +&da d a +&ab a b + #### # System Instructions #### @@ -27,7 +31,7 @@ l_msync 001000 1000000000 00000000 00000000 l_psync 001000 1010000000 00000000 00000000 l_csync 001000 1100000000 00000000 00000000 -l_rfe 001001 ----- ----- -------- -------- +l_rfe 001001 ----- ----- -------- -------- #### # Branch Instructions @@ -60,32 +64,64 @@ l_lbs 100100 ..... ..... ........ ........ @load l_lhz 100101 ..... ..... ........ ........ @load l_lhs 100110 ..... ..... ........ ........ @load -l_swa 110011 ..... ..... ..... ........... @store -l_sw 110101 ..... ..... ..... ........... @store -l_sb 110110 ..... ..... ..... ........... @store -l_sh 110111 ..... ..... ..... ........... @store +l_swa 110011 ..... ..... ..... ........... @store +l_sw 110101 ..... ..... ..... ........... @store +l_sb 110110 ..... ..... ..... ........... @store +l_sh 110111 ..... ..... ..... ........... @store #### # Immediate Operand Instructions #### -%mtspr_k 21:5 0:11 +%mtspr_k 21:5 0:11 -&rri d a i -&rrk d a k -@rri ...... d:5 a:5 i:s16 &rri -@rrk ...... d:5 a:5 k:16 &rrk +&rri d a i +&rrk d a k +@rri ...... d:5 a:5 i:s16 &rri +@rrk ...... d:5 a:5 k:16 &rrk -l_nop 000101 01--- ----- k:16 +l_nop 000101 01--- ----- k:16 -l_addi 100111 ..... ..... ........ ........ @rri -l_addic 101000 ..... ..... ........ ........ @rri -l_andi 101001 ..... ..... ........ ........ @rrk -l_ori 101010 ..... ..... ........ ........ @rrk -l_xori 101011 ..... ..... ........ ........ @rri -l_muli 101100 ..... ..... ........ ........ @rri +l_addi 100111 ..... ..... ........ ........ @rri +l_addic 101000 ..... ..... ........ ........ @rri +l_andi 101001 ..... ..... ........ ........ @rrk +l_ori 101010 ..... ..... ........ ........ @rrk +l_xori 101011 ..... ..... ........ ........ @rri +l_muli 101100 ..... ..... ........ ........ @rri -l_mfspr 101101 ..... ..... ........ ........ @rrk -l_mtspr 110000 ..... a:5 b:5 ........... k=%mtspr_k +l_mfspr 101101 ..... ..... ........ ........ @rrk +l_mtspr 110000 ..... a:5 b:5 ........... k=%mtspr_k -l_maci 010011 ----- a:5 i:s16 +l_maci 010011 ----- a:5 i:s16 + +#### +# Arithmetic Instructions +#### + +l_exths 111000 d:5 a:5 ----- - 0000 -- 1100 +l_extbs 111000 d:5 a:5 ----- - 0001 -- 1100 +l_exthz 111000 d:5 a:5 ----- - 0010 -- 1100 +l_extbz 111000 d:5 a:5 ----- - 0011 -- 1100 + +l_add 111000 d:5 a:5 b:5 - 00 ---- 0000 +l_addc 111000 d:5 a:5 b:5 - 00 ---- 0001 +l_sub 111000 d:5 a:5 b:5 - 00 ---- 0010 +l_and 111000 d:5 a:5 b:5 - 00 ---- 0011 +l_or 111000 d:5 a:5 b:5 - 00 ---- 0100 +l_xor 111000 d:5 a:5 b:5 - 00 ---- 0101 +l_cmov 111000 d:5 a:5 b:5 - 00 ---- 1110 +l_ff1 111000 d:5 a:5 ----- - 00 ---- 1111 +l_fl1 111000 d:5 a:5 ----- - 01 ---- 1111 + +l_sll 111000 d:5 a:5 b:5 - 0000 -- 1000 +l_srl 111000 d:5 a:5 b:5 - 0001 -- 1000 +l_sra 111000 d:5 a:5 b:5 - 0010 -- 1000 +l_ror 111000 d:5 a:5 b:5 - 0011 -- 1000 + +l_mul 111000 d:5 a:5 b:5 - 11 ---- 0110 +l_mulu 111000 d:5 a:5 b:5 - 11 ---- 1011 +l_div 111000 d:5 a:5 b:5 - 11 ---- 1001 +l_divu 111000 d:5 a:5 b:5 - 11 ---- 1010 + +l_muld 111000 ----- a:5 b:5 - 11 ---- 0111 +l_muldu 111000 ----- a:5 b:5 - 11 ---- 1100 diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index e3dcaaf7a8..755f43535e 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -455,171 +455,199 @@ static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb) gen_ove_cy(dc); } -static void dec_calc(DisasContext *dc, uint32_t insn) +static bool trans_l_add(DisasContext *dc, arg_dab *a, uint32_t insn) { - uint32_t op0, op1, op2; - uint32_t ra, rb, rd; - op0 = extract32(insn, 0, 4); - op1 = extract32(insn, 8, 2); - op2 = extract32(insn, 6, 2); - ra = extract32(insn, 16, 5); - rb = extract32(insn, 11, 5); - rd = extract32(insn, 21, 5); + LOG_DIS("l.add r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + gen_add(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - switch (op1) { - case 0: - switch (op0) { - case 0x0: /* l.add */ - LOG_DIS("l.add r%d, r%d, r%d\n", rd, ra, rb); - gen_add(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; +static bool trans_l_addc(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.addc r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + gen_addc(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x1: /* l.addc */ - LOG_DIS("l.addc r%d, r%d, r%d\n", rd, ra, rb); - gen_addc(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; +static bool trans_l_sub(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.sub r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + gen_sub(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x2: /* l.sub */ - LOG_DIS("l.sub r%d, r%d, r%d\n", rd, ra, rb); - gen_sub(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; +static bool trans_l_and(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.and r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + tcg_gen_and_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x3: /* l.and */ - LOG_DIS("l.and r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_and_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; +static bool trans_l_or(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.or r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + tcg_gen_or_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x4: /* l.or */ - LOG_DIS("l.or r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_or_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; +static bool trans_l_xor(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.xor r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + tcg_gen_xor_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x5: /* l.xor */ - LOG_DIS("l.xor r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_xor_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; +static bool trans_l_sll(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.sll r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + tcg_gen_shl_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x8: - switch (op2) { - case 0: /* l.sll */ - LOG_DIS("l.sll r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_shl_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - case 1: /* l.srl */ - LOG_DIS("l.srl r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_shr_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - case 2: /* l.sra */ - LOG_DIS("l.sra r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_sar_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - case 3: /* l.ror */ - LOG_DIS("l.ror r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_rotr_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - } - break; +static bool trans_l_srl(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.srl r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + tcg_gen_shr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0xc: - switch (op2) { - case 0: /* l.exths */ - LOG_DIS("l.exths r%d, r%d\n", rd, ra); - tcg_gen_ext16s_tl(cpu_R[rd], cpu_R[ra]); - return; - case 1: /* l.extbs */ - LOG_DIS("l.extbs r%d, r%d\n", rd, ra); - tcg_gen_ext8s_tl(cpu_R[rd], cpu_R[ra]); - return; - case 2: /* l.exthz */ - LOG_DIS("l.exthz r%d, r%d\n", rd, ra); - tcg_gen_ext16u_tl(cpu_R[rd], cpu_R[ra]); - return; - case 3: /* l.extbz */ - LOG_DIS("l.extbz r%d, r%d\n", rd, ra); - tcg_gen_ext8u_tl(cpu_R[rd], cpu_R[ra]); - return; - } - break; +static bool trans_l_sra(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.sra r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + tcg_gen_sar_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0xd: - switch (op2) { - case 0: /* l.extws */ - LOG_DIS("l.extws r%d, r%d\n", rd, ra); - tcg_gen_ext32s_tl(cpu_R[rd], cpu_R[ra]); - return; - case 1: /* l.extwz */ - LOG_DIS("l.extwz r%d, r%d\n", rd, ra); - tcg_gen_ext32u_tl(cpu_R[rd], cpu_R[ra]); - return; - } - break; +static bool trans_l_ror(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.ror r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + tcg_gen_rotr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0xe: /* l.cmov */ - LOG_DIS("l.cmov r%d, r%d, r%d\n", rd, ra, rb); - { - TCGv zero = tcg_const_tl(0); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_R[rd], cpu_sr_f, zero, - cpu_R[ra], cpu_R[rb]); - tcg_temp_free(zero); - } - return; +static bool trans_l_exths(DisasContext *dc, arg_da *a, uint32_t insn) +{ + LOG_DIS("l.exths r%d, r%d\n", a->d, a->a); + check_r0_write(a->d); + tcg_gen_ext16s_tl(cpu_R[a->d], cpu_R[a->a]); + return true; +} - case 0xf: /* l.ff1 */ - LOG_DIS("l.ff1 r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_ctzi_tl(cpu_R[rd], cpu_R[ra], -1); - tcg_gen_addi_tl(cpu_R[rd], cpu_R[rd], 1); - return; - } - break; +static bool trans_l_extbs(DisasContext *dc, arg_da *a, uint32_t insn) +{ + LOG_DIS("l.extbs r%d, r%d\n", a->d, a->a); + check_r0_write(a->d); + tcg_gen_ext8s_tl(cpu_R[a->d], cpu_R[a->a]); + return true; +} - case 1: - switch (op0) { - case 0xf: /* l.fl1 */ - LOG_DIS("l.fl1 r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_clzi_tl(cpu_R[rd], cpu_R[ra], TARGET_LONG_BITS); - tcg_gen_subfi_tl(cpu_R[rd], TARGET_LONG_BITS, cpu_R[rd]); - return; - } - break; +static bool trans_l_exthz(DisasContext *dc, arg_da *a, uint32_t insn) +{ + LOG_DIS("l.exthz r%d, r%d\n", a->d, a->a); + check_r0_write(a->d); + tcg_gen_ext16u_tl(cpu_R[a->d], cpu_R[a->a]); + return true; +} - case 2: - break; +static bool trans_l_extbz(DisasContext *dc, arg_da *a, uint32_t insn) +{ + LOG_DIS("l.extbz r%d, r%d\n", a->d, a->a); + check_r0_write(a->d); + tcg_gen_ext8u_tl(cpu_R[a->d], cpu_R[a->a]); + return true; +} - case 3: - switch (op0) { - case 0x6: /* l.mul */ - LOG_DIS("l.mul r%d, r%d, r%d\n", rd, ra, rb); - gen_mul(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; +static bool trans_l_cmov(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + TCGv zero; + LOG_DIS("l.cmov r%d, r%d, r%d\n", a->d, a->a, a->b); - case 0x7: /* l.muld */ - LOG_DIS("l.muld r%d, r%d\n", ra, rb); - gen_muld(dc, cpu_R[ra], cpu_R[rb]); - break; + check_r0_write(a->d); + zero = tcg_const_tl(0); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_R[a->d], cpu_sr_f, zero, + cpu_R[a->a], cpu_R[a->b]); + tcg_temp_free(zero); + return true; +} - case 0x9: /* l.div */ - LOG_DIS("l.div r%d, r%d, r%d\n", rd, ra, rb); - gen_div(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; +static bool trans_l_ff1(DisasContext *dc, arg_da *a, uint32_t insn) +{ + LOG_DIS("l.ff1 r%d, r%d\n", a->d, a->a); - case 0xa: /* l.divu */ - LOG_DIS("l.divu r%d, r%d, r%d\n", rd, ra, rb); - gen_divu(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; + check_r0_write(a->d); + tcg_gen_ctzi_tl(cpu_R[a->d], cpu_R[a->a], -1); + tcg_gen_addi_tl(cpu_R[a->d], cpu_R[a->d], 1); + return true; +} - case 0xb: /* l.mulu */ - LOG_DIS("l.mulu r%d, r%d, r%d\n", rd, ra, rb); - gen_mulu(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; +static bool trans_l_fl1(DisasContext *dc, arg_da *a, uint32_t insn) +{ + LOG_DIS("l.fl1 r%d, r%d\n", a->d, a->a); - case 0xc: /* l.muldu */ - LOG_DIS("l.muldu r%d, r%d\n", ra, rb); - gen_muldu(dc, cpu_R[ra], cpu_R[rb]); - return; - } - break; - } - gen_illegal_exception(dc); + check_r0_write(a->d); + tcg_gen_clzi_tl(cpu_R[a->d], cpu_R[a->a], TARGET_LONG_BITS); + tcg_gen_subfi_tl(cpu_R[a->d], TARGET_LONG_BITS, cpu_R[a->d]); + return true; +} + +static bool trans_l_mul(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.mul r%d, r%d, r%d\n", a->d, a->a, a->b); + + check_r0_write(a->d); + gen_mul(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_mulu(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.mulu r%d, r%d, r%d\n", a->d, a->a, a->b); + + check_r0_write(a->d); + gen_mulu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_div(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.div r%d, r%d, r%d\n", a->d, a->a, a->b); + + check_r0_write(a->d); + gen_div(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_divu(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("l.divu r%d, r%d, r%d\n", a->d, a->a, a->b); + + check_r0_write(a->d); + gen_divu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_muld(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("l.muld r%d, r%d\n", a->a, a->b); + gen_muld(dc, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_muldu(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("l.muldu r%d, r%d\n", a->a, a->b); + gen_muldu(dc, cpu_R[a->a], cpu_R[a->b]); + return true; } static bool trans_l_j(DisasContext *dc, arg_l_j *a, uint32_t insn) @@ -1485,10 +1513,6 @@ static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) dec_float(dc, insn); break; - case 0x38: - dec_calc(dc, insn); - break; - case 0x39: dec_comp(dc, insn); break; From 99d863d6d669b10ed5a4879f48938bb21a78216a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 10:11:02 -0800 Subject: [PATCH 0423/2380] target/openrisc: Convert dec_mac Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/insns.decode | 5 ++++ target/openrisc/translate.c | 55 +++++++++++++++--------------------- 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index 20f035f488..7240c6fb77 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -125,3 +125,8 @@ l_divu 111000 d:5 a:5 b:5 - 11 ---- 1010 l_muld 111000 ----- a:5 b:5 - 11 ---- 0111 l_muldu 111000 ----- a:5 b:5 - 11 ---- 1100 + +l_mac 110001 ----- a:5 b:5 ------- 0001 +l_macu 110001 ----- a:5 b:5 ------- 0011 +l_msb 110001 ----- a:5 b:5 ------- 0010 +l_msbu 110001 ----- a:5 b:5 ------- 0100 diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 755f43535e..48e26c4349 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -970,39 +970,32 @@ static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) return true; } -static void dec_mac(DisasContext *dc, uint32_t insn) +static bool trans_l_mac(DisasContext *dc, arg_ab *a, uint32_t insn) { - uint32_t op0; - uint32_t ra, rb; - op0 = extract32(insn, 0, 4); - ra = extract32(insn, 16, 5); - rb = extract32(insn, 11, 5); + LOG_DIS("l.mac r%d, r%d\n", a->a, a->b); + gen_mac(dc, cpu_R[a->a], cpu_R[a->b]); + return true; +} - switch (op0) { - case 0x0001: /* l.mac */ - LOG_DIS("l.mac r%d, r%d\n", ra, rb); - gen_mac(dc, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_msb(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("l.msb r%d, r%d\n", a->a, a->b); + gen_msb(dc, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x0002: /* l.msb */ - LOG_DIS("l.msb r%d, r%d\n", ra, rb); - gen_msb(dc, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_macu(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("l.mac r%d, r%d\n", a->a, a->b); + gen_macu(dc, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x0003: /* l.macu */ - LOG_DIS("l.macu r%d, r%d\n", ra, rb); - gen_macu(dc, cpu_R[ra], cpu_R[rb]); - break; - - case 0x0004: /* l.msbu */ - LOG_DIS("l.msbu r%d, r%d\n", ra, rb); - gen_msbu(dc, cpu_R[ra], cpu_R[rb]); - break; - - default: - gen_illegal_exception(dc); - break; - } +static bool trans_l_msbu(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("l.msb r%d, r%d\n", a->a, a->b); + gen_msbu(dc, cpu_R[a->a], cpu_R[a->b]); + return true; } static void dec_logic(DisasContext *dc, uint32_t insn) @@ -1505,10 +1498,6 @@ static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) dec_compi(dc, insn); break; - case 0x31: - dec_mac(dc, insn); - break; - case 0x32: dec_float(dc, insn); break; From e20c2592bc6165bffeb968684e01bc26a6181a84 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 10:20:06 -0800 Subject: [PATCH 0424/2380] target/openrisc: Convert dec_logic Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/insns.decode | 6 ++++ target/openrisc/translate.c | 62 +++++++++++++++--------------------- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index 7240c6fb77..fb8ba5812a 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -20,6 +20,7 @@ &dab d a b &da d a &ab a b +&dal d a l #### # System Instructions @@ -130,3 +131,8 @@ l_mac 110001 ----- a:5 b:5 ------- 0001 l_macu 110001 ----- a:5 b:5 ------- 0011 l_msb 110001 ----- a:5 b:5 ------- 0010 l_msbu 110001 ----- a:5 b:5 ------- 0100 + +l_slli 101110 d:5 a:5 -------- 00 l:6 +l_srli 101110 d:5 a:5 -------- 01 l:6 +l_srai 101110 d:5 a:5 -------- 10 l:6 +l_rori 101110 d:5 a:5 -------- 11 l:6 diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 48e26c4349..b5ff7577bd 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -998,42 +998,36 @@ static bool trans_l_msbu(DisasContext *dc, arg_ab *a, uint32_t insn) return true; } -static void dec_logic(DisasContext *dc, uint32_t insn) +static bool trans_l_slli(DisasContext *dc, arg_dal *a, uint32_t insn) { - uint32_t op0; - uint32_t rd, ra, L6, S6; - op0 = extract32(insn, 6, 2); - rd = extract32(insn, 21, 5); - ra = extract32(insn, 16, 5); - L6 = extract32(insn, 0, 6); - S6 = L6 & (TARGET_LONG_BITS - 1); + LOG_DIS("l.slli r%d, r%d, %d\n", a->d, a->a, a->l); + check_r0_write(a->d); + tcg_gen_shli_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); + return true; +} - check_r0_write(rd); - switch (op0) { - case 0x00: /* l.slli */ - LOG_DIS("l.slli r%d, r%d, %d\n", rd, ra, L6); - tcg_gen_shli_tl(cpu_R[rd], cpu_R[ra], S6); - break; +static bool trans_l_srli(DisasContext *dc, arg_dal *a, uint32_t insn) +{ + LOG_DIS("l.srli r%d, r%d, %d\n", a->d, a->a, a->l); + check_r0_write(a->d); + tcg_gen_shri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); + return true; +} - case 0x01: /* l.srli */ - LOG_DIS("l.srli r%d, r%d, %d\n", rd, ra, L6); - tcg_gen_shri_tl(cpu_R[rd], cpu_R[ra], S6); - break; +static bool trans_l_srai(DisasContext *dc, arg_dal *a, uint32_t insn) +{ + LOG_DIS("l.srai r%d, r%d, %d\n", a->d, a->a, a->l); + check_r0_write(a->d); + tcg_gen_sari_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); + return true; +} - case 0x02: /* l.srai */ - LOG_DIS("l.srai r%d, r%d, %d\n", rd, ra, L6); - tcg_gen_sari_tl(cpu_R[rd], cpu_R[ra], S6); - break; - - case 0x03: /* l.rori */ - LOG_DIS("l.rori r%d, r%d, %d\n", rd, ra, L6); - tcg_gen_rotri_tl(cpu_R[rd], cpu_R[ra], S6); - break; - - default: - gen_illegal_exception(dc); - break; - } +static bool trans_l_rori(DisasContext *dc, arg_dal *a, uint32_t insn) +{ + LOG_DIS("l.rori r%d, r%d, %d\n", a->d, a->a, a->l); + check_r0_write(a->d); + tcg_gen_rotri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); + return true; } static void dec_M(DisasContext *dc, uint32_t insn) @@ -1490,10 +1484,6 @@ static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) dec_M(dc, insn); break; - case 0x2e: - dec_logic(dc, insn); - break; - case 0x2f: dec_compi(dc, insn); break; From e720a5713dcfb7b2ea1d28c108353e7a12fea84d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 10:28:01 -0800 Subject: [PATCH 0425/2380] target/openrisc: Convert dec_M Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/insns.decode | 3 +++ target/openrisc/translate.c | 41 ++++++++++++------------------------ 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index fb8ba5812a..84f71c13b3 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -95,6 +95,9 @@ l_mtspr 110000 ..... a:5 b:5 ........... k=%mtspr_k l_maci 010011 ----- a:5 i:s16 +l_movhi 000110 d:5 ----0 k:16 +l_macrc 000110 d:5 ----1 00000000 00000000 + #### # Arithmetic Instructions #### diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index b5ff7577bd..bdd4626c02 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1030,32 +1030,21 @@ static bool trans_l_rori(DisasContext *dc, arg_dal *a, uint32_t insn) return true; } -static void dec_M(DisasContext *dc, uint32_t insn) +static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a, uint32_t insn) { - uint32_t op0; - uint32_t rd; - uint32_t K16; - op0 = extract32(insn, 16, 1); - rd = extract32(insn, 21, 5); - K16 = extract32(insn, 0, 16); + LOG_DIS("l.movhi r%d, %d\n", a->d, a->k); + check_r0_write(a->d); + tcg_gen_movi_tl(cpu_R[a->d], a->k << 16); + return true; +} - check_r0_write(rd); - switch (op0) { - case 0x0: /* l.movhi */ - LOG_DIS("l.movhi r%d, %d\n", rd, K16); - tcg_gen_movi_tl(cpu_R[rd], (K16 << 16)); - break; - - case 0x1: /* l.macrc */ - LOG_DIS("l.macrc r%d\n", rd); - tcg_gen_trunc_i64_tl(cpu_R[rd], cpu_mac); - tcg_gen_movi_i64(cpu_mac, 0); - break; - - default: - gen_illegal_exception(dc); - break; - } +static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn) +{ + LOG_DIS("l.macrc r%d\n", a->d); + check_r0_write(a->d); + tcg_gen_trunc_i64_tl(cpu_R[a->d], cpu_mac); + tcg_gen_movi_i64(cpu_mac, 0); + return true; } static void dec_comp(DisasContext *dc, uint32_t insn) @@ -1480,10 +1469,6 @@ static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) op0 = extract32(insn, 26, 6); switch (op0) { - case 0x06: - dec_M(dc, insn); - break; - case 0x2f: dec_compi(dc, insn); break; From fbb3e29aace97a3c51c7ed434dfa799e7ba7f652 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 10:41:47 -0800 Subject: [PATCH 0426/2380] target/openrisc: Convert dec_comp Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/insns.decode | 15 +++++ target/openrisc/translate.c | 120 +++++++++++++++++------------------ 2 files changed, 73 insertions(+), 62 deletions(-) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index 84f71c13b3..29d28ff5be 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -139,3 +139,18 @@ l_slli 101110 d:5 a:5 -------- 00 l:6 l_srli 101110 d:5 a:5 -------- 01 l:6 l_srai 101110 d:5 a:5 -------- 10 l:6 l_rori 101110 d:5 a:5 -------- 11 l:6 + +#### +# Compare Instructions +#### + +l_sfeq 111001 00000 a:5 b:5 ----------- +l_sfne 111001 00001 a:5 b:5 ----------- +l_sfgtu 111001 00010 a:5 b:5 ----------- +l_sfgeu 111001 00011 a:5 b:5 ----------- +l_sfltu 111001 00100 a:5 b:5 ----------- +l_sfleu 111001 00101 a:5 b:5 ----------- +l_sfgts 111001 01010 a:5 b:5 ----------- +l_sfges 111001 01011 a:5 b:5 ----------- +l_sflts 111001 01100 a:5 b:5 ----------- +l_sfles 111001 01101 a:5 b:5 ----------- diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index bdd4626c02..8743b5e96e 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1047,74 +1047,74 @@ static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn) return true; } -static void dec_comp(DisasContext *dc, uint32_t insn) +static bool trans_l_sfeq(DisasContext *dc, arg_ab *a, TCGCond cond) { - uint32_t op0; - uint32_t ra, rb; + LOG_DIS("l.sfeq r%d, r%d\n", a->a, a->b); + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} - op0 = extract32(insn, 21, 5); - ra = extract32(insn, 16, 5); - rb = extract32(insn, 11, 5); +static bool trans_l_sfne(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + LOG_DIS("l.sfne r%d, r%d\n", a->a, a->b); + tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} - /* unsigned integers */ - tcg_gen_ext32u_tl(cpu_R[ra], cpu_R[ra]); - tcg_gen_ext32u_tl(cpu_R[rb], cpu_R[rb]); +static bool trans_l_sfgtu(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + LOG_DIS("l.sfgtu r%d, r%d\n", a->a, a->b); + tcg_gen_setcond_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} - switch (op0) { - case 0x0: /* l.sfeq */ - LOG_DIS("l.sfeq r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_sfgeu(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + LOG_DIS("l.sfgeu r%d, r%d\n", a->a, a->b); + tcg_gen_setcond_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x1: /* l.sfne */ - LOG_DIS("l.sfne r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_sfltu(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + LOG_DIS("l.sfltu r%d, r%d\n", a->a, a->b); + tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x2: /* l.sfgtu */ - LOG_DIS("l.sfgtu r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_sfleu(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + LOG_DIS("l.sfleu r%d, r%d\n", a->a, a->b); + tcg_gen_setcond_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x3: /* l.sfgeu */ - LOG_DIS("l.sfgeu r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_sfgts(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + LOG_DIS("l.sfgts r%d, r%d\n", a->a, a->b); + tcg_gen_setcond_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x4: /* l.sfltu */ - LOG_DIS("l.sfltu r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_sfges(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + LOG_DIS("l.sfges r%d, r%d\n", a->a, a->b); + tcg_gen_setcond_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x5: /* l.sfleu */ - LOG_DIS("l.sfleu r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_sflts(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + LOG_DIS("l.sflts r%d, r%d\n", a->a, a->b); + tcg_gen_setcond_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0xa: /* l.sfgts */ - LOG_DIS("l.sfgts r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_GT, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0xb: /* l.sfges */ - LOG_DIS("l.sfges r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_GE, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0xc: /* l.sflts */ - LOG_DIS("l.sflts r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_LT, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0xd: /* l.sfles */ - LOG_DIS("l.sfles r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_LE, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - default: - gen_illegal_exception(dc); - break; - } +static bool trans_l_sfles(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + LOG_DIS("l.sfles r%d, r%d\n", a->a, a->b); + tcg_gen_setcond_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; } static void dec_compi(DisasContext *dc, uint32_t insn) @@ -1477,10 +1477,6 @@ static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) dec_float(dc, insn); break; - case 0x39: - dec_comp(dc, insn); - break; - default: gen_illegal_exception(dc); break; From 032de4fc38b9d1bac933c3be7f95b16c60db5cea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 10:49:54 -0800 Subject: [PATCH 0427/2380] target/openrisc: Convert dec_compi Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/insns.decode | 12 ++++ target/openrisc/translate.c | 116 +++++++++++++++++------------------ 2 files changed, 70 insertions(+), 58 deletions(-) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index 29d28ff5be..4ec0e2de3a 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -21,6 +21,7 @@ &da d a &ab a b &dal d a l +&ai a i #### # System Instructions @@ -154,3 +155,14 @@ l_sfgts 111001 01010 a:5 b:5 ----------- l_sfges 111001 01011 a:5 b:5 ----------- l_sflts 111001 01100 a:5 b:5 ----------- l_sfles 111001 01101 a:5 b:5 ----------- + +l_sfeqi 101111 00000 a:5 i:s16 +l_sfnei 101111 00001 a:5 i:s16 +l_sfgtui 101111 00010 a:5 i:s16 +l_sfgeui 101111 00011 a:5 i:s16 +l_sfltui 101111 00100 a:5 i:s16 +l_sfleui 101111 00101 a:5 i:s16 +l_sfgtsi 101111 01010 a:5 i:s16 +l_sfgesi 101111 01011 a:5 i:s16 +l_sfltsi 101111 01100 a:5 i:s16 +l_sflesi 101111 01101 a:5 i:s16 diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 8743b5e96e..c4256518cb 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1117,70 +1117,74 @@ static bool trans_l_sfles(DisasContext *dc, arg_ab *a, TCGCond cond) return true; } -static void dec_compi(DisasContext *dc, uint32_t insn) +static bool trans_l_sfeqi(DisasContext *dc, arg_ai *a, TCGCond cond) { - uint32_t op0, ra; - int32_t I16; + LOG_DIS("l.sfeqi r%d, %d\n", a->a, a->i); + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} - op0 = extract32(insn, 21, 5); - ra = extract32(insn, 16, 5); - I16 = sextract32(insn, 0, 16); +static bool trans_l_sfnei(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + LOG_DIS("l.sfnei r%d, %d\n", a->a, a->i); + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} - switch (op0) { - case 0x0: /* l.sfeqi */ - LOG_DIS("l.sfeqi r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[ra], I16); - break; +static bool trans_l_sfgtui(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + LOG_DIS("l.sfgtui r%d, %d\n", a->a, a->i); + tcg_gen_setcondi_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} - case 0x1: /* l.sfnei */ - LOG_DIS("l.sfnei r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_f, cpu_R[ra], I16); - break; +static bool trans_l_sfgeui(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + LOG_DIS("l.sfgeui r%d, %d\n", a->a, a->i); + tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} - case 0x2: /* l.sfgtui */ - LOG_DIS("l.sfgtui r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[ra], I16); - break; +static bool trans_l_sfltui(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + LOG_DIS("l.sfltui r%d, %d\n", a->a, a->i); + tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} - case 0x3: /* l.sfgeui */ - LOG_DIS("l.sfgeui r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[ra], I16); - break; +static bool trans_l_sfleui(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + LOG_DIS("l.sfleui r%d, %d\n", a->a, a->i); + tcg_gen_setcondi_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} - case 0x4: /* l.sfltui */ - LOG_DIS("l.sfltui r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[ra], I16); - break; +static bool trans_l_sfgtsi(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + LOG_DIS("l.sfgtsi r%d, %d\n", a->a, a->i); + tcg_gen_setcondi_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} - case 0x5: /* l.sfleui */ - LOG_DIS("l.sfleui r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[ra], I16); - break; +static bool trans_l_sfgesi(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + LOG_DIS("l.sfgesi r%d, %d\n", a->a, a->i); + tcg_gen_setcondi_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} - case 0xa: /* l.sfgtsi */ - LOG_DIS("l.sfgtsi r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_GT, cpu_sr_f, cpu_R[ra], I16); - break; +static bool trans_l_sfltsi(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + LOG_DIS("l.sfltsi r%d, %d\n", a->a, a->i); + tcg_gen_setcondi_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} - case 0xb: /* l.sfgesi */ - LOG_DIS("l.sfgesi r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_GE, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0xc: /* l.sfltsi */ - LOG_DIS("l.sfltsi r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_LT, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0xd: /* l.sflesi */ - LOG_DIS("l.sflesi r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_LE, cpu_sr_f, cpu_R[ra], I16); - break; - - default: - gen_illegal_exception(dc); - break; - } +static bool trans_l_sflesi(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + LOG_DIS("l.sflesi r%d, %d\n", a->a, a->i); + tcg_gen_setcondi_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], a->i); + return true; } static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn) @@ -1469,10 +1473,6 @@ static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) op0 = extract32(insn, 26, 6); switch (op0) { - case 0x2f: - dec_compi(dc, insn); - break; - case 0x32: dec_float(dc, insn); break; From 6fd204a2e934ae013d0b96458ab3d4163ccbea27 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 11:14:44 -0800 Subject: [PATCH 0428/2380] target/openrisc: Convert dec_float Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/insns.decode | 21 +++ target/openrisc/translate.c | 356 +++++++++++++---------------------- 2 files changed, 148 insertions(+), 229 deletions(-) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode index 4ec0e2de3a..dad68c8422 100644 --- a/target/openrisc/insns.decode +++ b/target/openrisc/insns.decode @@ -166,3 +166,24 @@ l_sfgtsi 101111 01010 a:5 i:s16 l_sfgesi 101111 01011 a:5 i:s16 l_sfltsi 101111 01100 a:5 i:s16 l_sflesi 101111 01101 a:5 i:s16 + +#### +# FP Instructions +#### + +lf_add_s 110010 d:5 a:5 b:5 --- 00000000 +lf_sub_s 110010 d:5 a:5 b:5 --- 00000001 +lf_mul_s 110010 d:5 a:5 b:5 --- 00000010 +lf_div_s 110010 d:5 a:5 b:5 --- 00000011 +lf_rem_s 110010 d:5 a:5 b:5 --- 00000110 +lf_madd_s 110010 d:5 a:5 b:5 --- 00000111 + +lf_itof_s 110010 d:5 a:5 00000 --- 00000100 +lf_ftoi_s 110010 d:5 a:5 00000 --- 00000101 + +lf_sfeq_s 110010 ----- a:5 b:5 --- 00001000 +lf_sfne_s 110010 ----- a:5 b:5 --- 00001001 +lf_sfgt_s 110010 ----- a:5 b:5 --- 00001010 +lf_sfge_s 110010 ----- a:5 b:5 --- 00001011 +lf_sflt_s 110010 ----- a:5 b:5 --- 00001100 +lf_sfle_s 110010 ----- a:5 b:5 --- 00001101 diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index c4256518cb..1f87ad6b2e 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1241,245 +1241,143 @@ static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a, uint32_t insn) return true; } -static void dec_float(DisasContext *dc, uint32_t insn) +static void do_fp2(DisasContext *dc, arg_da *a, + void (*fn)(TCGv, TCGv_env, TCGv)) { - uint32_t op0; - uint32_t ra, rb, rd; - op0 = extract32(insn, 0, 8); - ra = extract32(insn, 16, 5); - rb = extract32(insn, 11, 5); - rd = extract32(insn, 21, 5); + check_r0_write(a->d); + fn(cpu_R[a->d], cpu_env, cpu_R[a->a]); + gen_helper_update_fpcsr(cpu_env); +} - switch (op0) { - case 0x00: /* lf.add.s */ - LOG_DIS("lf.add.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_add_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; +static void do_fp3(DisasContext *dc, arg_dab *a, + void (*fn)(TCGv, TCGv_env, TCGv, TCGv)) +{ + check_r0_write(a->d); + fn(cpu_R[a->d], cpu_env, cpu_R[a->a], cpu_R[a->b]); + gen_helper_update_fpcsr(cpu_env); +} - case 0x01: /* lf.sub.s */ - LOG_DIS("lf.sub.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_sub_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x02: /* lf.mul.s */ - LOG_DIS("lf.mul.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_mul_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x03: /* lf.div.s */ - LOG_DIS("lf.div.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_div_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x04: /* lf.itof.s */ - LOG_DIS("lf.itof r%d, r%d\n", rd, ra); - check_r0_write(rd); - gen_helper_itofs(cpu_R[rd], cpu_env, cpu_R[ra]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x05: /* lf.ftoi.s */ - LOG_DIS("lf.ftoi r%d, r%d\n", rd, ra); - check_r0_write(rd); - gen_helper_ftois(cpu_R[rd], cpu_env, cpu_R[ra]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x06: /* lf.rem.s */ - LOG_DIS("lf.rem.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_rem_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x07: /* lf.madd.s */ - LOG_DIS("lf.madd.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_madd_s(cpu_R[rd], cpu_env, cpu_R[rd], - cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x08: /* lf.sfeq.s */ - LOG_DIS("lf.sfeq.s r%d, r%d\n", ra, rb); - gen_helper_float_eq_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x09: /* lf.sfne.s */ - LOG_DIS("lf.sfne.s r%d, r%d\n", ra, rb); - gen_helper_float_eq_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x0a: /* lf.sfgt.s */ - LOG_DIS("lf.sfgt.s r%d, r%d\n", ra, rb); - gen_helper_float_lt_s(cpu_sr_f, cpu_env, cpu_R[rb], cpu_R[ra]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x0b: /* lf.sfge.s */ - LOG_DIS("lf.sfge.s r%d, r%d\n", ra, rb); - gen_helper_float_le_s(cpu_sr_f, cpu_env, cpu_R[rb], cpu_R[ra]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x0c: /* lf.sflt.s */ - LOG_DIS("lf.sflt.s r%d, r%d\n", ra, rb); - gen_helper_float_lt_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x0d: /* lf.sfle.s */ - LOG_DIS("lf.sfle.s r%d, r%d\n", ra, rb); - gen_helper_float_le_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - -#ifdef TARGET_OPENRISC64 - case 0x10: /* lf.add.d */ - LOG_DIS("lf.add.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_add_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x11: /* lf.sub.d */ - LOG_DIS("lf.sub.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_sub_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x12: /* lf.mul.d */ - LOG_DIS("lf.mul.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_mul_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x13: /* lf.div.d */ - LOG_DIS("lf.div.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_div_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x14: /* lf.itof.d */ - LOG_DIS("lf.itof r%d, r%d\n", rd, ra); - check_of64s(dc); - check_r0_write(rd); - gen_helper_itofd(cpu_R[rd], cpu_env, cpu_R[ra]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x15: /* lf.ftoi.d */ - LOG_DIS("lf.ftoi r%d, r%d\n", rd, ra); - check_of64s(dc); - check_r0_write(rd); - gen_helper_ftoid(cpu_R[rd], cpu_env, cpu_R[ra]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x16: /* lf.rem.d */ - LOG_DIS("lf.rem.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_rem_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x17: /* lf.madd.d */ - LOG_DIS("lf.madd.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_madd_d(cpu_R[rd], cpu_env, cpu_R[rd], - cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x18: /* lf.sfeq.d */ - LOG_DIS("lf.sfeq.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_eq_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x1a: /* lf.sfgt.d */ - LOG_DIS("lf.sfgt.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_lt_d(cpu_sr_f, cpu_env, cpu_R[rb], cpu_R[ra]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x1b: /* lf.sfge.d */ - LOG_DIS("lf.sfge.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_le_d(cpu_sr_f, cpu_env, cpu_R[rb], cpu_R[ra]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x19: /* lf.sfne.d */ - LOG_DIS("lf.sfne.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_eq_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x1c: /* lf.sflt.d */ - LOG_DIS("lf.sflt.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_lt_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; - - case 0x1d: /* lf.sfle.d */ - LOG_DIS("lf.sfle.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_le_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - gen_helper_update_fpcsr(cpu_env); - break; -#endif - - default: - gen_illegal_exception(dc); - break; +static void do_fpcmp(DisasContext *dc, arg_ab *a, + void (*fn)(TCGv, TCGv_env, TCGv, TCGv), + bool inv, bool swap) +{ + if (swap) { + fn(cpu_sr_f, cpu_env, cpu_R[a->b], cpu_R[a->a]); + } else { + fn(cpu_sr_f, cpu_env, cpu_R[a->a], cpu_R[a->b]); } + if (inv) { + tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1); + } + gen_helper_update_fpcsr(cpu_env); +} + +static bool trans_lf_add_s(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("lf.add.s r%d, r%d, r%d\n", a->d, a->a, a->b); + do_fp3(dc, a, gen_helper_float_add_s); + return true; +} + +static bool trans_lf_sub_s(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("lf.sub.s r%d, r%d, r%d\n", a->d, a->a, a->b); + do_fp3(dc, a, gen_helper_float_sub_s); + return true; +} + +static bool trans_lf_mul_s(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("lf.mul.s r%d, r%d, r%d\n", a->d, a->a, a->b); + do_fp3(dc, a, gen_helper_float_mul_s); + return true; +} + +static bool trans_lf_div_s(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("lf.div.s r%d, r%d, r%d\n", a->d, a->a, a->b); + do_fp3(dc, a, gen_helper_float_div_s); + return true; +} + +static bool trans_lf_rem_s(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("lf.rem.s r%d, r%d, r%d\n", a->d, a->a, a->b); + do_fp3(dc, a, gen_helper_float_rem_s); + return true; +} + +static bool trans_lf_itof_s(DisasContext *dc, arg_da *a, uint32_t insn) +{ + LOG_DIS("lf.itof.s r%d, r%d\n", a->d, a->a); + do_fp2(dc, a, gen_helper_itofs); + return true; +} + +static bool trans_lf_ftoi_s(DisasContext *dc, arg_da *a, uint32_t insn) +{ + LOG_DIS("lf.ftoi.s r%d, r%d\n", a->d, a->a); + do_fp2(dc, a, gen_helper_ftois); + return true; +} + +static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + LOG_DIS("lf.madd.s r%d, r%d, r%d\n", a->d, a->a, a->b); + check_r0_write(a->d); + gen_helper_float_madd_s(cpu_R[a->d], cpu_env, cpu_R[a->d], + cpu_R[a->a], cpu_R[a->b]); + gen_helper_update_fpcsr(cpu_env); + return true; +} + +static bool trans_lf_sfeq_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("lf.sfeq.s r%d, r%d\n", a->a, a->b); + do_fpcmp(dc, a, gen_helper_float_eq_s, false, false); + return true; +} + +static bool trans_lf_sfne_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("lf.sfne.s r%d, r%d\n", a->a, a->b); + do_fpcmp(dc, a, gen_helper_float_eq_s, true, false); + return true; +} + +static bool trans_lf_sfgt_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("lf.sfgt.s r%d, r%d\n", a->a, a->b); + do_fpcmp(dc, a, gen_helper_float_lt_s, false, true); + return true; +} + +static bool trans_lf_sfge_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("lf.sfge.s r%d, r%d\n", a->a, a->b); + do_fpcmp(dc, a, gen_helper_float_le_s, false, true); + return true; +} + +static bool trans_lf_sflt_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("lf.sflt.s r%d, r%d\n", a->a, a->b); + do_fpcmp(dc, a, gen_helper_float_lt_s, false, false); + return true; +} + +static bool trans_lf_sfle_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + LOG_DIS("lf.sfle.s r%d, r%d\n", a->a, a->b); + do_fpcmp(dc, a, gen_helper_float_le_s, false, false); + return true; } static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) { - uint32_t op0; uint32_t insn = cpu_ldl_code(&cpu->env, dc->base.pc_next); - - /* Transition to the auto-generated decoder. */ - if (decode(dc, insn)) { - return; - } - - op0 = extract32(insn, 26, 6); - switch (op0) { - case 0x32: - dec_float(dc, insn); - break; - - default: + if (!decode(dc, insn)) { gen_illegal_exception(dc); - break; } } From c7b6f54bf8d6d0e847230998d09495c73eeac9aa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 11:18:07 -0800 Subject: [PATCH 0429/2380] target/openrisc: Merge disas_openrisc_insn Acked-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/translate.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 1f87ad6b2e..e7c96ca990 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1373,14 +1373,6 @@ static bool trans_lf_sfle_s(DisasContext *dc, arg_ab *a, uint32_t insn) return true; } -static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) -{ - uint32_t insn = cpu_ldl_code(&cpu->env, dc->base.pc_next); - if (!decode(dc, insn)) { - gen_illegal_exception(dc); - } -} - static void openrisc_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) { DisasContext *dc = container_of(dcb, DisasContext, base); @@ -1435,8 +1427,11 @@ static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); OpenRISCCPU *cpu = OPENRISC_CPU(cs); + uint32_t insn = cpu_ldl_code(&cpu->env, dc->base.pc_next); - disas_openrisc_insn(dc, cpu); + if (!decode(dc, insn)) { + gen_illegal_exception(dc); + } dc->base.pc_next += 4; /* delay slot */ From f88e5c5744c7969ba0ccb7f9a3b85cdcac3aacf1 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 May 2018 07:45:01 +0200 Subject: [PATCH 0430/2380] sdl2: move opts assignment into loop So the opts pointer is set for all sdl2_consoles. Fixes: 844fd50dbbcfc9e401895274bf4fb8da8e8d3f64 Signed-off-by: Gerd Hoffmann --- ui/sdl2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/sdl2.c b/ui/sdl2.c index da037248c2..a0f9b16bc1 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -804,7 +804,6 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) return; } sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs); - sdl2_console->opts = o; for (i = 0; i < sdl2_num_outputs; i++) { QemuConsole *con = qemu_console_lookup_by_index(i); assert(con != NULL); @@ -812,6 +811,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) sdl2_console[i].hidden = true; } sdl2_console[i].idx = i; + sdl2_console[i].opts = o; #ifdef CONFIG_OPENGL sdl2_console[i].opengl = display_opengl; sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops; From 1d18774579759a8b5046119266626e9fdf9f7c0b Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 11 May 2018 01:07:38 +0200 Subject: [PATCH 0431/2380] gtk: make it possible to hide the menu bar Saves some space and disables the F10 button as side-effect. Fixes: https://bugs.launchpad.net/qemu/+bug/1726910 Signed-off-by: Peter Wu Message-Id: <20180510230739.28459-1-peter@lekensteyn.nl> Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/ui/gtk.c b/ui/gtk.c index bb3214cffb..9e5390f2c2 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -145,6 +145,7 @@ #define GDK_KEY_2 GDK_2 #define GDK_KEY_f GDK_f #define GDK_KEY_g GDK_g +#define GDK_KEY_m GDK_m #define GDK_KEY_q GDK_q #define GDK_KEY_plus GDK_plus #define GDK_KEY_equal GDK_equal @@ -208,6 +209,7 @@ struct GtkDisplayState { GtkWidget *show_tabs_item; GtkWidget *untabify_item; + GtkWidget *show_menubar_item; GtkWidget *vbox; GtkWidget *notebook; @@ -1387,6 +1389,30 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) } } +static void gd_menu_show_menubar(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); + + if (s->full_screen) { + return; + } + + if (gtk_check_menu_item_get_active( + GTK_CHECK_MENU_ITEM(s->show_menubar_item))) { + gtk_widget_show(s->menu_bar); + } else { + gtk_widget_hide(s->menu_bar); + } + gd_update_windowsize(vc); +} + +static void gd_accel_show_menubar(void *opaque) +{ + GtkDisplayState *s = opaque; + gtk_menu_item_activate(GTK_MENU_ITEM(s->show_menubar_item)); +} + static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; @@ -1403,7 +1429,10 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) } else { gtk_window_unfullscreen(GTK_WINDOW(s->window)); gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); - gtk_widget_show(s->menu_bar); + if (gtk_check_menu_item_get_active( + GTK_CHECK_MENU_ITEM(s->show_menubar_item))) { + gtk_widget_show(s->menu_bar); + } s->full_screen = FALSE; if (vc->type == GD_VC_GFX) { vc->gfx.scale_x = 1.0; @@ -2036,6 +2065,8 @@ static void gd_connect_signals(GtkDisplayState *s) G_CALLBACK(gd_menu_show_tabs), s); g_signal_connect(s->untabify_item, "activate", G_CALLBACK(gd_menu_untabify), s); + g_signal_connect(s->show_menubar_item, "activate", + G_CALLBACK(gd_menu_show_menubar), s); g_signal_connect(s->window, "delete-event", G_CALLBACK(gd_window_close), s); @@ -2272,6 +2303,19 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s) s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab")); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item); + s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic( + _("Show Menubar")); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item), + TRUE); + gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0, + g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL)); +#if GTK_CHECK_VERSION(3, 8, 0) + gtk_accel_label_set_accel( + GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))), + GDK_KEY_m, HOTKEY_MODIFIERS); +#endif + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item); + return view_menu; } From 4f4cb8282df82d43ba6b3c9045a3ac6fc4c4ef09 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Mon, 7 May 2018 15:42:37 +0200 Subject: [PATCH 0432/2380] ui/gtk: Only try to initialize EGL/X11 if GtkGlArea failed The commit referenced below changed the logic by causing the gtk-egl backend to be initialized regardless of whether GtkGlArea initialization succeeded. This causes eglInitialize to crash in Wayland systems without XWayland. This patch restores the previous logic. Fixes: 4c70280592f5 ("ui/gtk: use GtkGlArea on wayland only") Signed-off-by: Tomeu Vizoso Message-id: 20180507134237.14996-1-tomeu.vizoso@collabora.com Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/gtk.c b/ui/gtk.c index 9e5390f2c2..568c9563ce 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -2484,7 +2484,7 @@ static void early_gtk_display_init(DisplayOptions *opts) if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { gtk_use_gl_area = true; gtk_gl_area_init(); - } + } else #endif { gtk_egl_init(); From f31f9c1080d8907c95f1501c6abab038eceb5490 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 7 May 2018 12:22:54 +0200 Subject: [PATCH 0433/2380] vnc: add magic cookie to VncState Set magic cookie on initialization. Clear on cleanup. Sprinkle a bunch of assert()s checking the cookie, to verify the pointer is valid. Signed-off-by: Gerd Hoffmann Message-id: 20180507102254.12107-1-kraxel@redhat.com --- ui/vnc-jobs.c | 4 ++++ ui/vnc.c | 10 +++++++++- ui/vnc.h | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 868dddef4b..b0b15d42a8 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -82,6 +82,7 @@ VncJob *vnc_job_new(VncState *vs) { VncJob *job = g_new0(VncJob, 1); + assert(vs->magic == VNC_MAGIC); job->vs = vs; vnc_lock_queue(queue); QLIST_INIT(&job->rectangles); @@ -214,6 +215,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) /* Here job can only be NULL if queue->exit is true */ job = QTAILQ_FIRST(&queue->jobs); vnc_unlock_queue(queue); + assert(job->vs->magic == VNC_MAGIC); if (queue->exit) { return -1; @@ -236,6 +238,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) /* Make a local copy of vs and switch output buffers */ vnc_async_encoding_start(job->vs, &vs); + vs.magic = VNC_MAGIC; /* Start sending rectangles */ n_rectangles = 0; @@ -289,6 +292,7 @@ disconnected: vnc_unlock_queue(queue); qemu_cond_broadcast(&queue->cond); g_free(job); + vs.magic = 0; return 0; } diff --git a/ui/vnc.c b/ui/vnc.c index 5526e54f48..359693238b 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1138,6 +1138,7 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd) { VncState *vs = opaque; + assert(vs->magic == VNC_MAGIC); switch (cmd) { case AUD_CNOTIFY_DISABLE: vnc_lock_output(vs); @@ -1167,6 +1168,7 @@ static void audio_capture(void *opaque, void *buf, int size) { VncState *vs = opaque; + assert(vs->magic == VNC_MAGIC); vnc_lock_output(vs); if (vs->output.offset < vs->throttle_output_offset) { vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); @@ -1275,6 +1277,7 @@ void vnc_disconnect_finish(VncState *vs) vs->ioc = NULL; object_unref(OBJECT(vs->sioc)); vs->sioc = NULL; + vs->magic = 0; g_free(vs); } @@ -1414,7 +1417,7 @@ static void vnc_client_write_locked(VncState *vs) static void vnc_client_write(VncState *vs) { - + assert(vs->magic == VNC_MAGIC); vnc_lock_output(vs); if (vs->output.offset) { vnc_client_write_locked(vs); @@ -1487,6 +1490,7 @@ static void vnc_jobs_bh(void *opaque) { VncState *vs = opaque; + assert(vs->magic == VNC_MAGIC); vnc_jobs_consume_buffer(vs); } @@ -1537,6 +1541,8 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, GIOCondition condition, void *opaque) { VncState *vs = opaque; + + assert(vs->magic == VNC_MAGIC); if (condition & G_IO_IN) { if (vnc_client_read(vs) < 0) { /* vs is free()ed here */ @@ -1568,6 +1574,7 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, void vnc_write(VncState *vs, const void *data, size_t len) { + assert(vs->magic == VNC_MAGIC); if (vs->disconnecting) { return; } @@ -3064,6 +3071,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, int i; trace_vnc_client_connect(vs, sioc); + vs->magic = VNC_MAGIC; vs->sioc = sioc; object_ref(OBJECT(vs->sioc)); vs->ioc = QIO_CHANNEL(sioc); diff --git a/ui/vnc.h b/ui/vnc.h index 7b29def77d..762632929b 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -255,8 +255,11 @@ typedef enum { VNC_STATE_UPDATE_FORCE, } VncStateUpdate; +#define VNC_MAGIC ((uint64_t)0x05b3f069b3d204bb) + struct VncState { + uint64_t magic; QIOChannelSocket *sioc; /* The underlying socket */ QIOChannel *ioc; /* The channel currently used for I/O */ guint ioc_tag; From 776d1344bd0566f412d5bc063e753a6b98530bcf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 7 May 2018 11:55:36 +0200 Subject: [PATCH 0434/2380] ui: add qapi parser for -display Add parse_display_qapi() function which parses the -display command line using a qapi visitor for DisplayOptions. Wire up as default catch in parse_display(). Improves the error message for unknown display types. Also enables json as -display argument, i.e. -display "{ 'type': 'gtk' }" Signed-off-by: Gerd Hoffmann Reviewed-by: Eric Blake Message-id: 20180507095539.19584-2-kraxel@redhat.com --- vl.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/vl.c b/vl.c index b9f6b42779..c56f7f8046 100644 --- a/vl.c +++ b/vl.c @@ -120,12 +120,14 @@ int main(int argc, char **argv) #include "ui/qemu-spice.h" #include "qapi/string-input-visitor.h" #include "qapi/opts-visitor.h" +#include "qapi/clone-visitor.h" #include "qom/object_interfaces.h" #include "exec/semihost.h" #include "crypto/init.h" #include "sysemu/replay.h" #include "qapi/qapi-events-run-state.h" #include "qapi/qapi-visit-block-core.h" +#include "qapi/qapi-visit-ui.h" #include "qapi/qapi-commands-block-core.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-run-state.h" @@ -2088,6 +2090,25 @@ static void select_vgahw(const char *p) } } +static void parse_display_qapi(const char *optarg) +{ + Error *err = NULL; + DisplayOptions *opts; + Visitor *v; + + v = qobject_input_visitor_new_str(optarg, "type", &err); + if (!v) { + error_report_err(err); + exit(1); + } + + visit_type_DisplayOptions(v, NULL, &opts, &error_fatal); + QAPI_CLONE_MEMBERS(DisplayOptions, &dpy, opts); + + qapi_free_DisplayOptions(opts); + visit_free(v); +} + static void parse_display(const char *p) { const char *opts; @@ -2203,8 +2224,7 @@ static void parse_display(const char *p) } else if (strstart(p, "none", &opts)) { dpy.type = DISPLAY_TYPE_NONE; } else { - error_report("unknown display type"); - exit(1); + parse_display_qapi(p); } } From 2c9498c3e44cd5574df3baaebfb9d5a095252205 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 7 May 2018 11:55:37 +0200 Subject: [PATCH 0435/2380] ui: switch trivial displays to qapi parser Drop the option-less display types (egl-headless, curses, none) from parse_display(), so they'll be handled by parse_display_qapi(). Signed-off-by: Gerd Hoffmann Reviewed-by: Eric Blake Message-id: 20180507095539.19584-3-kraxel@redhat.com --- vl.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/vl.c b/vl.c index c56f7f8046..6b8f946d58 100644 --- a/vl.c +++ b/vl.c @@ -2185,10 +2185,6 @@ static void parse_display(const char *p) error_report("VNC requires a display argument vnc="); exit(1); } - } else if (strstart(p, "egl-headless", &opts)) { - dpy.type = DISPLAY_TYPE_EGL_HEADLESS; - } else if (strstart(p, "curses", &opts)) { - dpy.type = DISPLAY_TYPE_CURSES; } else if (strstart(p, "gtk", &opts)) { dpy.type = DISPLAY_TYPE_GTK; while (*opts) { @@ -2221,8 +2217,6 @@ static void parse_display(const char *p) } opts = nextopt; } - } else if (strstart(p, "none", &opts)) { - dpy.type = DISPLAY_TYPE_NONE; } else { parse_display_qapi(p); } From 002b2902f39fd9c2b01881e1ca07db80266dc5bf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 7 May 2018 11:55:38 +0200 Subject: [PATCH 0436/2380] ui: switch gtk display to qapi parser Drop the gtk option parser from parse_display(), so parse_display_qapi() will handle it instead. With this change the parser will accept gl=core and gl=es too, gtk must catch the unsupported gles variant now. Signed-off-by: Gerd Hoffmann Message-id: 20180507095539.19584-4-kraxel@redhat.com --- ui/gtk.c | 6 +++++- vl.c | 32 -------------------------------- 2 files changed, 5 insertions(+), 33 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 568c9563ce..ef6ca7179e 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -2478,7 +2478,11 @@ static void early_gtk_display_init(DisplayOptions *opts) } assert(opts->type == DISPLAY_TYPE_GTK); - if (opts->has_gl && opts->gl) { + if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) { + if (opts->gl == DISPLAYGL_MODE_ES) { + error_report("gtk: opengl es not supported"); + return; + } #if defined(CONFIG_OPENGL) #if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND) if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { diff --git a/vl.c b/vl.c index 6b8f946d58..5f1734d851 100644 --- a/vl.c +++ b/vl.c @@ -2185,38 +2185,6 @@ static void parse_display(const char *p) error_report("VNC requires a display argument vnc="); exit(1); } - } else if (strstart(p, "gtk", &opts)) { - dpy.type = DISPLAY_TYPE_GTK; - while (*opts) { - const char *nextopt; - - if (strstart(opts, ",grab_on_hover=", &nextopt)) { - opts = nextopt; - dpy.u.gtk.has_grab_on_hover = true; - if (strstart(opts, "on", &nextopt)) { - dpy.u.gtk.grab_on_hover = true; - } else if (strstart(opts, "off", &nextopt)) { - dpy.u.gtk.grab_on_hover = false; - } else { - goto invalid_gtk_args; - } - } else if (strstart(opts, ",gl=", &nextopt)) { - opts = nextopt; - dpy.has_gl = true; - if (strstart(opts, "on", &nextopt)) { - dpy.gl = DISPLAYGL_MODE_ON; - } else if (strstart(opts, "off", &nextopt)) { - dpy.gl = DISPLAYGL_MODE_OFF; - } else { - goto invalid_gtk_args; - } - } else { - invalid_gtk_args: - error_report("invalid GTK option string"); - exit(1); - } - opts = nextopt; - } } else { parse_display_qapi(p); } From 7a61f4385991a7b47d71c8d657dea1e34a792baf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 7 May 2018 11:55:39 +0200 Subject: [PATCH 0437/2380] ui: document non-qapi parser cases. Add comments to the cases not (yet) switched over to parse_display_qapi(). Signed-off-by: Gerd Hoffmann Message-id: 20180507095539.19584-5-kraxel@redhat.com --- vl.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/vl.c b/vl.c index 5f1734d851..3b39bbd7a8 100644 --- a/vl.c +++ b/vl.c @@ -2114,6 +2114,16 @@ static void parse_display(const char *p) const char *opts; if (strstart(p, "sdl", &opts)) { + /* + * sdl DisplayType needs hand-crafted parser instead of + * parse_display_qapi() due to some options not in + * DisplayOptions, specifically: + * - frame + * Already deprecated. + * - ctrl_grab + alt_grab + * Not clear yet what happens to them long-term. Should + * replaced by something better or deprecated and dropped. + */ dpy.type = DISPLAY_TYPE_SDL; while (*opts) { const char *nextopt; @@ -2179,6 +2189,10 @@ static void parse_display(const char *p) opts = nextopt; } } else if (strstart(p, "vnc", &opts)) { + /* + * vnc isn't a (local) DisplayType but a protocol for remote + * display access. + */ if (*opts == '=') { vnc_parse(opts + 1, &error_fatal); } else { From cd6cd8fa0da7839b11ca7e5d7d45b73810394395 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 7 May 2018 11:54:24 +0200 Subject: [PATCH 0438/2380] console: use linked list for QemuConsoles Signed-off-by: Gerd Hoffmann Message-id: 20180507095424.16220-1-kraxel@redhat.com --- ui/console.c | 105 ++++++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/ui/console.c b/ui/console.c index b02510cdca..945f05d728 100644 --- a/ui/console.c +++ b/ui/console.c @@ -165,6 +165,8 @@ struct QemuConsole { QEMUFIFO out_fifo; uint8_t out_fifo_buf[16]; QEMUTimer *kbd_timer; + + QTAILQ_ENTRY(QemuConsole) next; }; struct DisplayState { @@ -180,8 +182,8 @@ struct DisplayState { static DisplayState *display_state; static QemuConsole *active_console; -static QemuConsole **consoles; -static int nb_consoles = 0; +static QTAILQ_HEAD(consoles_head, QemuConsole) consoles = + QTAILQ_HEAD_INITIALIZER(consoles); static bool cursor_visible_phase; static QEMUTimer *cursor_timer; @@ -197,7 +199,7 @@ static void gui_update(void *opaque) uint64_t dcl_interval; DisplayState *ds = opaque; DisplayChangeListener *dcl; - int i; + QemuConsole *con; ds->refreshing = true; dpy_refresh(ds); @@ -212,9 +214,9 @@ static void gui_update(void *opaque) } if (ds->update_interval != interval) { ds->update_interval = interval; - for (i = 0; i < nb_consoles; i++) { - if (consoles[i]->hw_ops->update_interval) { - consoles[i]->hw_ops->update_interval(consoles[i]->hw, interval); + QTAILQ_FOREACH(con, &consoles, next) { + if (con->hw_ops->update_interval) { + con->hw_ops->update_interval(con->hw, interval); } } trace_console_refresh(interval); @@ -1292,10 +1294,13 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, s->ds = ds; s->console_type = console_type; - consoles = g_realloc(consoles, sizeof(*consoles) * (nb_consoles+1)); - if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) { - s->index = nb_consoles; - consoles[nb_consoles++] = s; + if (QTAILQ_EMPTY(&consoles)) { + s->index = 0; + QTAILQ_INSERT_TAIL(&consoles, s, next); + } else if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) { + QemuConsole *last = QTAILQ_LAST(&consoles, consoles_head); + s->index = last->index + 1; + QTAILQ_INSERT_TAIL(&consoles, s, next); } else { /* * HACK: Put graphical consoles before text consoles. @@ -1303,15 +1308,24 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, * Only do that for coldplugged devices. After initial device * initialization we will not renumber the consoles any more. */ - for (i = nb_consoles; i > 0; i--) { - if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE) - break; - consoles[i] = consoles[i - 1]; - consoles[i]->index = i; + QemuConsole *c = QTAILQ_FIRST(&consoles); + + while (QTAILQ_NEXT(c, next) != NULL && + c->console_type == GRAPHIC_CONSOLE) { + c = QTAILQ_NEXT(c, next); + } + if (c->console_type == GRAPHIC_CONSOLE) { + /* have no text consoles */ + s->index = c->index + 1; + QTAILQ_INSERT_AFTER(&consoles, c, s, next); + } else { + s->index = c->index; + QTAILQ_INSERT_BEFORE(c, s, next); + /* renumber text consoles */ + for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) { + c->index = i; + } } - s->index = i; - consoles[i] = s; - nb_consoles++; } return s; } @@ -1861,21 +1875,21 @@ static DisplayState *get_alloc_displaystate(void) DisplayState *init_displaystate(void) { gchar *name; - int i; + QemuConsole *con; get_alloc_displaystate(); - for (i = 0; i < nb_consoles; i++) { - if (consoles[i]->console_type != GRAPHIC_CONSOLE && - consoles[i]->ds == NULL) { - text_console_do_init(consoles[i]->chr, display_state); + QTAILQ_FOREACH(con, &consoles, next) { + if (con->console_type != GRAPHIC_CONSOLE && + con->ds == NULL) { + text_console_do_init(con->chr, display_state); } /* Hook up into the qom tree here (not in new_console()), once * all QemuConsoles are created and the order / numbering * doesn't change any more */ - name = g_strdup_printf("console[%d]", i); + name = g_strdup_printf("console[%d]", con->index); object_property_add_child(container_get(object_get_root(), "/backend"), - name, OBJECT(consoles[i]), &error_abort); + name, OBJECT(con), &error_abort); g_free(name); } @@ -1957,33 +1971,34 @@ void graphic_console_close(QemuConsole *con) QemuConsole *qemu_console_lookup_by_index(unsigned int index) { - if (index >= nb_consoles) { - return NULL; + QemuConsole *con; + + QTAILQ_FOREACH(con, &consoles, next) { + if (con->index == index) { + return con; + } } - return consoles[index]; + return NULL; } QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) { + QemuConsole *con; Object *obj; uint32_t h; - int i; - for (i = 0; i < nb_consoles; i++) { - if (!consoles[i]) { - continue; - } - obj = object_property_get_link(OBJECT(consoles[i]), + QTAILQ_FOREACH(con, &consoles, next) { + obj = object_property_get_link(OBJECT(con), "device", &error_abort); if (DEVICE(obj) != dev) { continue; } - h = object_property_get_uint(OBJECT(consoles[i]), + h = object_property_get_uint(OBJECT(con), "head", &error_abort); if (h != head) { continue; } - return consoles[i]; + return con; } return NULL; } @@ -2013,22 +2028,19 @@ QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, QemuConsole *qemu_console_lookup_unused(void) { + QemuConsole *con; Object *obj; - int i; - for (i = 0; i < nb_consoles; i++) { - if (!consoles[i]) { + QTAILQ_FOREACH(con, &consoles, next) { + if (con->hw_ops != &unused_ops) { continue; } - if (consoles[i]->hw_ops != &unused_ops) { - continue; - } - obj = object_property_get_link(OBJECT(consoles[i]), + obj = object_property_get_link(OBJECT(con), "device", &error_abort); if (obj != NULL) { continue; } - return consoles[i]; + return con; } return NULL; } @@ -2130,12 +2142,11 @@ static void text_console_update_cursor_timer(void) static void text_console_update_cursor(void *opaque) { QemuConsole *s; - int i, count = 0; + int count = 0; cursor_visible_phase = !cursor_visible_phase; - for (i = 0; i < nb_consoles; i++) { - s = consoles[i]; + QTAILQ_FOREACH(s, &consoles, next) { if (qemu_console_is_graphic(s) || !qemu_console_is_visible(s)) { continue; From 677b490501823c1cb6946d8bb9e7907c12cd0c71 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 11 May 2018 01:07:39 +0200 Subject: [PATCH 0439/2380] gtk: disable the F10 menubar key The F10 key is used in various applications, disable it unconditionally (do not limit it to grab mode). Note that this property is deprecated and might be removed in the future (GTK+ commit b082fb598d). Fixes: https://bugs.launchpad.net/qemu/+bug/1726910 Signed-off-by: Peter Wu Message-id: 20180510230739.28459-2-peter@lekensteyn.nl Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/gtk.c b/ui/gtk.c index ef6ca7179e..dbce970dc4 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -2321,6 +2321,8 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s) static void gd_create_menus(GtkDisplayState *s) { + GtkSettings *settings; + s->accel_group = gtk_accel_group_new(); s->machine_menu = gd_create_menu_machine(s); s->view_menu = gd_create_menu_view(s); @@ -2336,6 +2338,10 @@ static void gd_create_menus(GtkDisplayState *s) g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group); gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group); + + /* Disable the default "F10" menu shortcut. */ + settings = gtk_widget_get_settings(s->window); + g_object_set(G_OBJECT(settings), "gtk-menu-bar-accel", "", NULL); } From 143c04c7e0639e53086519592ead15d2556bfbf2 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Mon, 7 May 2018 23:01:46 +1000 Subject: [PATCH 0440/2380] ps2: Clear the PS/2 queue and obey disable This allows guest's to correctly reinitialize and identify the mouse should the guest decide to re-scan or reset during mouse input events. When the guest sends the "Identify" command, due to the PC's hardware architecutre it is impossible to reliably determine the response from the command amongst other streaming data, such as mouse or keyboard events. Standard practice is for the guest to disable the device and then issue the identify command, so this must be obeyed. Signed-off-by: Geoffrey McRae Message-Id: <20180507150303.7486B381924@moya.office.hostfission.com> Signed-off-by: Gerd Hoffmann --- hw/input/ps2.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 06f5d2ac4a..4abc8cecdd 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -232,6 +232,11 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, uint16_t keycode = 0; int mod; + /* do not process events while disabled to prevent stream corruption */ + if (!s->scan_enabled) { + return; + } + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); assert(evt->type == INPUT_EVENT_KIND_KEY); qcode = qemu_input_key_value_to_qcode(key->key); @@ -673,6 +678,11 @@ static void ps2_mouse_sync(DeviceState *dev) { PS2MouseState *s = (PS2MouseState *)dev; + /* do not sync while disabled to prevent stream corruption */ + if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) { + return; + } + if (s->mouse_buttons) { qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); } @@ -776,6 +786,7 @@ void ps2_write_mouse(void *opaque, int val) s->mouse_resolution = 2; s->mouse_status = 0; s->mouse_type = 0; + ps2_reset_queue(&s->common); ps2_queue(&s->common, AUX_ACK); ps2_queue(&s->common, 0xaa); ps2_queue(&s->common, s->mouse_type); From 7abe7eb29494b4e4a11ec99ae5623083409a2f1e Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Mon, 7 May 2018 23:13:12 +1000 Subject: [PATCH 0441/2380] ps2: Fix mouse stream corruption due to lost data This fixes an issue by adding bounds checking to multi-byte packets where the PS/2 mouse data stream may become corrupted due to data being discarded when the PS/2 ringbuffer is full. Interrupts for Multi-byte responses are postponed until the final byte has been queued. These changes fix a bug where windows guests drop the mouse device entirely requring the guest to be restarted. Signed-off-by: Geoffrey McRae Message-Id: <20180507150310.2FEA0381924@moya.office.hostfission.com> [ kraxel: codestyle fixes ] Signed-off-by: Gerd Hoffmann --- hw/input/ps2.c | 124 +++++++++++++++++++++++++++++++---------- include/hw/input/ps2.h | 5 ++ 2 files changed, 100 insertions(+), 29 deletions(-) diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 4abc8cecdd..eeec6180d0 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -188,16 +188,64 @@ static void ps2_reset_queue(PS2State *s) q->count = 0; } -void ps2_queue(PS2State *s, int b) +void ps2_queue_noirq(PS2State *s, int b) { PS2Queue *q = &s->queue; - if (q->count >= PS2_QUEUE_SIZE - 1) + if (q->count == PS2_QUEUE_SIZE) { return; + } + q->data[q->wptr] = b; if (++q->wptr == PS2_QUEUE_SIZE) q->wptr = 0; q->count++; +} + +void ps2_raise_irq(PS2State *s) +{ + s->update_irq(s->update_arg, 1); +} + +void ps2_queue(PS2State *s, int b) +{ + ps2_queue_noirq(s, b); + s->update_irq(s->update_arg, 1); +} + +void ps2_queue_2(PS2State *s, int b1, int b2) +{ + if (PS2_QUEUE_SIZE - s->queue.count < 2) { + return; + } + + ps2_queue_noirq(s, b1); + ps2_queue_noirq(s, b2); + s->update_irq(s->update_arg, 1); +} + +void ps2_queue_3(PS2State *s, int b1, int b2, int b3) +{ + if (PS2_QUEUE_SIZE - s->queue.count < 3) { + return; + } + + ps2_queue_noirq(s, b1); + ps2_queue_noirq(s, b2); + ps2_queue_noirq(s, b3); + s->update_irq(s->update_arg, 1); +} + +void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4) +{ + if (PS2_QUEUE_SIZE - s->queue.count < 4) { + return; + } + + ps2_queue_noirq(s, b1); + ps2_queue_noirq(s, b2); + ps2_queue_noirq(s, b3); + ps2_queue_noirq(s, b4); s->update_irq(s->update_arg, 1); } @@ -501,13 +549,17 @@ void ps2_write_keyboard(void *opaque, int val) ps2_queue(&s->common, KBD_REPLY_RESEND); break; case KBD_CMD_GET_ID: - ps2_queue(&s->common, KBD_REPLY_ACK); /* We emulate a MF2 AT keyboard here */ - ps2_queue(&s->common, KBD_REPLY_ID); if (s->translate) - ps2_queue(&s->common, 0x41); + ps2_queue_3(&s->common, + KBD_REPLY_ACK, + KBD_REPLY_ID, + 0x41); else - ps2_queue(&s->common, 0x83); + ps2_queue_3(&s->common, + KBD_REPLY_ACK, + KBD_REPLY_ID, + 0x83); break; case KBD_CMD_ECHO: ps2_queue(&s->common, KBD_CMD_ECHO); @@ -534,8 +586,9 @@ void ps2_write_keyboard(void *opaque, int val) break; case KBD_CMD_RESET: ps2_reset_keyboard(s); - ps2_queue(&s->common, KBD_REPLY_ACK); - ps2_queue(&s->common, KBD_REPLY_POR); + ps2_queue_2(&s->common, + KBD_REPLY_ACK, + KBD_REPLY_POR); break; default: ps2_queue(&s->common, KBD_REPLY_RESEND); @@ -544,8 +597,10 @@ void ps2_write_keyboard(void *opaque, int val) break; case KBD_CMD_SCANCODE: if (val == 0) { - ps2_queue(&s->common, KBD_REPLY_ACK); - ps2_put_keycode(s, s->scancode_set); + if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) { + ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_put_keycode(s, s->scancode_set); + } } else if (val >= 1 && val <= 3) { s->scancode_set = val; ps2_queue(&s->common, KBD_REPLY_ACK); @@ -577,11 +632,16 @@ void ps2_keyboard_set_translation(void *opaque, int mode) s->translate = mode; } -static void ps2_mouse_send_packet(PS2MouseState *s) +static int ps2_mouse_send_packet(PS2MouseState *s) { + const int needed = 3 + (s->mouse_type - 2); unsigned int b; int dx1, dy1, dz1; + if (PS2_QUEUE_SIZE - s->common.queue.count < needed) { + return 0; + } + dx1 = s->mouse_dx; dy1 = s->mouse_dy; dz1 = s->mouse_dz; @@ -595,9 +655,9 @@ static void ps2_mouse_send_packet(PS2MouseState *s) else if (dy1 < -127) dy1 = -127; b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07); - ps2_queue(&s->common, b); - ps2_queue(&s->common, dx1 & 0xff); - ps2_queue(&s->common, dy1 & 0xff); + ps2_queue_noirq(&s->common, b); + ps2_queue_noirq(&s->common, dx1 & 0xff); + ps2_queue_noirq(&s->common, dy1 & 0xff); /* extra byte for IMPS/2 or IMEX */ switch(s->mouse_type) { default: @@ -607,7 +667,7 @@ static void ps2_mouse_send_packet(PS2MouseState *s) dz1 = 127; else if (dz1 < -127) dz1 = -127; - ps2_queue(&s->common, dz1 & 0xff); + ps2_queue_noirq(&s->common, dz1 & 0xff); break; case 4: if (dz1 > 7) @@ -615,15 +675,19 @@ static void ps2_mouse_send_packet(PS2MouseState *s) else if (dz1 < -7) dz1 = -7; b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); - ps2_queue(&s->common, b); + ps2_queue_noirq(&s->common, b); break; } + ps2_raise_irq(&s->common); + trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b); /* update deltas */ s->mouse_dx -= dx1; s->mouse_dy -= dy1; s->mouse_dz -= dz1; + + return 1; } static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, @@ -687,10 +751,9 @@ static void ps2_mouse_sync(DeviceState *dev) qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); } if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) { - while (s->common.queue.count < PS2_QUEUE_SIZE - 4) { - /* if not remote, send event. Multiple events are sent if - too big deltas */ - ps2_mouse_send_packet(s); + /* if not remote, send event. Multiple events are sent if + too big deltas */ + while (ps2_mouse_send_packet(s)) { if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0) break; } @@ -749,8 +812,9 @@ void ps2_write_mouse(void *opaque, int val) ps2_queue(&s->common, AUX_ACK); break; case AUX_GET_TYPE: - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, s->mouse_type); + ps2_queue_2(&s->common, + AUX_ACK, + s->mouse_type); break; case AUX_SET_RES: case AUX_SET_SAMPLE: @@ -758,10 +822,11 @@ void ps2_write_mouse(void *opaque, int val) ps2_queue(&s->common, AUX_ACK); break; case AUX_GET_SCALE: - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, s->mouse_status); - ps2_queue(&s->common, s->mouse_resolution); - ps2_queue(&s->common, s->mouse_sample_rate); + ps2_queue_4(&s->common, + AUX_ACK, + s->mouse_status, + s->mouse_resolution, + s->mouse_sample_rate); break; case AUX_POLL: ps2_queue(&s->common, AUX_ACK); @@ -787,9 +852,10 @@ void ps2_write_mouse(void *opaque, int val) s->mouse_status = 0; s->mouse_type = 0; ps2_reset_queue(&s->common); - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, 0xaa); - ps2_queue(&s->common, s->mouse_type); + ps2_queue_3(&s->common, + AUX_ACK, + 0xaa, + s->mouse_type); break; default: break; diff --git a/include/hw/input/ps2.h b/include/hw/input/ps2.h index 94709b8502..213aa16aa3 100644 --- a/include/hw/input/ps2.h +++ b/include/hw/input/ps2.h @@ -37,7 +37,12 @@ void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg); void ps2_write_mouse(void *, int val); void ps2_write_keyboard(void *, int val); uint32_t ps2_read_data(PS2State *s); +void ps2_queue_noirq(PS2State *s, int b); +void ps2_raise_irq(PS2State *s); void ps2_queue(PS2State *s, int b); +void ps2_queue_2(PS2State *s, int b1, int b2); +void ps2_queue_3(PS2State *s, int b1, int b2, int b3); +void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4); void ps2_keyboard_set_translation(void *opaque, int mode); void ps2_mouse_fake_event(void *opaque); From a5a5f5e2e437db6c19164b734f838a7bf9e0c5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 15 May 2018 14:58:42 +0100 Subject: [PATCH 0442/2380] fpu/softfloat: int_to_float ensure r fully initialised MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported by Coverity (CID1390635). We ensure this for uint_to_float later on so we might as well mirror that. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- fpu/softfloat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 8401b37bd4..b39c0c6fbb 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1525,7 +1525,7 @@ FLOAT_TO_UINT(64, 64) static FloatParts int_to_float(int64_t a, float_status *status) { - FloatParts r; + FloatParts r = {}; if (a == 0) { r.cls = float_class_zero; r.sign = false; From 333583757c5e910b040bef793974773635ce1918 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 May 2018 14:58:42 +0100 Subject: [PATCH 0443/2380] fpu/softfloat: Don't set Invalid for float-to-int(MAXINT) In float-to-integer conversion, if the floating point input converts exactly to the largest or smallest integer that fits in to the result type, this is not an overflow. In this situation we were producing the correct result value, but were incorrectly setting the Invalid flag. For example for Arm A64, "FCVTAS w0, d0" on an input of 0x41dfffffffc00000 should produce 0x7fffffff and set no flags. Fix the boundary case to take the right half of the if() statements. This fixes a regression from 2.11 introduced by the softfloat refactoring. Cc: qemu-stable@nongnu.org Fixes: ab52f973a50 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180510140141.12120-1-peter.maydell@linaro.org --- fpu/softfloat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index b39c0c6fbb..bc0f52fa54 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1368,14 +1368,14 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode, r = UINT64_MAX; } if (p.sign) { - if (r < -(uint64_t) min) { + if (r <= -(uint64_t) min) { return -r; } else { s->float_exception_flags = orig_flags | float_flag_invalid; return min; } } else { - if (r < max) { + if (r <= max) { return r; } else { s->float_exception_flags = orig_flags | float_flag_invalid; From bcc531f0364796104df4443d17f99b5fb494eca2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 May 2018 14:58:42 +0100 Subject: [PATCH 0444/2380] target/arm: Fix fp_status_f16 tininess before rounding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit d81ce0ef2c4f105 we added an extra float_status field fp_status_fp16 for Arm, but forgot to initialize it correctly by setting it to float_tininess_before_rounding. This currently will only cause problems for the new V8_FP16 feature, since the float-to-float conversion code doesn't use it yet. The effect would be that we failed to set the Underflow IEEE exception flag in all the cases where we should. Add the missing initialization. Fixes: d81ce0ef2c4f105 Cc: qemu-stable@nongnu.org Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Peter Maydell Message-id: 20180512004311.9299-16-richard.henderson@linaro.org --- target/arm/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index d175c5e94f..7939c6b8ae 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -324,6 +324,8 @@ static void arm_cpu_reset(CPUState *s) &env->vfp.fp_status); set_float_detect_tininess(float_tininess_before_rounding, &env->vfp.standard_fp_status); + set_float_detect_tininess(float_tininess_before_rounding, + &env->vfp.fp_status_f16); #ifndef CONFIG_USER_ONLY if (kvm_enabled()) { kvm_arm_reset_vcpu(cpu); From 68130236e30a1ec64363f4915349feee181bfbc1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0445/2380] target/arm: Implement FMOV (general) for fp16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding the fp16 moves to/from general registers. Cc: qemu-stable@nongnu.org Signed-off-by: Richard Henderson Tested-by: Alex Bennée Message-id: 20180512003217.9105-2-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 4d1b220cc6..5b8cf75e9f 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -5700,6 +5700,15 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_hi_offset(s, rd)); clear_vec_high(s, true, rd); break; + case 3: + /* 16 bit */ + tmp = tcg_temp_new_i64(); + tcg_gen_ext16u_i64(tmp, tcg_rn); + write_fp_dreg(s, rd, tmp); + tcg_temp_free_i64(tmp); + break; + default: + g_assert_not_reached(); } } else { TCGv_i64 tcg_rd = cpu_reg(s, rd); @@ -5717,6 +5726,12 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) /* 64 bits from top half */ tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_hi_offset(s, rn)); break; + case 3: + /* 16 bit */ + tcg_gen_ld16u_i64(tcg_rd, cpu_env, fp_reg_offset(s, rn, MO_16)); + break; + default: + g_assert_not_reached(); } } } @@ -5756,6 +5771,12 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) case 0xa: /* 64 bit */ case 0xd: /* 64 bit to top half of quad */ break; + case 0x6: /* 16-bit float, 32-bit int */ + case 0xe: /* 16-bit float, 64-bit int */ + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ default: /* all other sf/type/rmode combinations are invalid */ unallocated_encoding(s); From 8c738d430796edeae5e13d6daf0895c02c62bd54 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0446/2380] target/arm: Early exit after unallocated_encoding in disas_fp_int_conv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No sense in emitting code after the exception. Signed-off-by: Richard Henderson Tested-by: Alex Bennée Message-id: 20180512003217.9105-3-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 5b8cf75e9f..11d8c07943 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -5780,7 +5780,7 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) default: /* all other sf/type/rmode combinations are invalid */ unallocated_encoding(s); - break; + return; } if (!fp_access_check(s)) { From 564a0632504fad840491aa9a59453f4e64a316c4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0447/2380] target/arm: Implement FCVT (scalar, integer) for fp16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: qemu-stable@nongnu.org Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson Tested-by: Alex Bennée Message-id: 20180512003217.9105-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 38 ++++++++++++++- target/arm/helper.h | 6 +++ target/arm/translate-a64.c | 96 +++++++++++++++++++++++++++++++------- 3 files changed, 122 insertions(+), 18 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 817f9d81a0..c6fd7f9479 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11427,8 +11427,12 @@ VFP_CONV_FIX_A64(uq, s, 32, 64, uint64) #undef VFP_CONV_FIX_A64 /* Conversion to/from f16 can overflow to infinity before/after scaling. - * Therefore we convert to f64 (which does not round), scale, - * and then convert f64 to f16 (which may round). + * Therefore we convert to f64, scale, and then convert f64 to f16; or + * vice versa for conversion to integer. + * + * For 16- and 32-bit integers, the conversion to f64 never rounds. + * For 64-bit integers, any integer that would cause rounding will also + * overflow to f16 infinity, so there is no double rounding problem. */ static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst) @@ -11446,6 +11450,16 @@ float16 HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst); } +float16 HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst) +{ + return do_postscale_fp16(int64_to_float64(x, fpst), shift, fpst); +} + +float16 HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst) +{ + return do_postscale_fp16(uint64_to_float64(x, fpst), shift, fpst); +} + static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst) { if (unlikely(float16_is_any_nan(f))) { @@ -11475,6 +11489,26 @@ uint32_t HELPER(vfp_touhh)(float16 x, uint32_t shift, void *fpst) return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst); } +uint32_t HELPER(vfp_toslh)(float16 x, uint32_t shift, void *fpst) +{ + return float64_to_int32(do_prescale_fp16(x, shift, fpst), fpst); +} + +uint32_t HELPER(vfp_toulh)(float16 x, uint32_t shift, void *fpst) +{ + return float64_to_uint32(do_prescale_fp16(x, shift, fpst), fpst); +} + +uint64_t HELPER(vfp_tosqh)(float16 x, uint32_t shift, void *fpst) +{ + return float64_to_int64(do_prescale_fp16(x, shift, fpst), fpst); +} + +uint64_t HELPER(vfp_touqh)(float16 x, uint32_t shift, void *fpst) +{ + return float64_to_uint64(do_prescale_fp16(x, shift, fpst), fpst); +} + /* Set the current fp rounding mode and return the old one. * The argument is a softfloat float_round_ value. */ diff --git a/target/arm/helper.h b/target/arm/helper.h index 1969b37f2d..ce89968b2d 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -151,6 +151,10 @@ DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_touhh, i32, f16, i32, ptr) DEF_HELPER_3(vfp_toshh, i32, f16, i32, ptr) +DEF_HELPER_3(vfp_toulh, i32, f16, i32, ptr) +DEF_HELPER_3(vfp_toslh, i32, f16, i32, ptr) +DEF_HELPER_3(vfp_touqh, i64, f16, i32, ptr) +DEF_HELPER_3(vfp_tosqh, i64, f16, i32, ptr) DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosqs, i64, f32, i32, ptr) @@ -177,6 +181,8 @@ DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr) DEF_HELPER_3(vfp_uqtod, f64, i64, i32, ptr) DEF_HELPER_3(vfp_sltoh, f16, i32, i32, ptr) DEF_HELPER_3(vfp_ultoh, f16, i32, i32, ptr) +DEF_HELPER_3(vfp_sqtoh, f16, i64, i32, ptr) +DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, ptr) DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, ptr) DEF_HELPER_FLAGS_2(set_neon_rmode, TCG_CALL_NO_RWG, i32, i32, env) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 11d8c07943..93fb15d185 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -5511,11 +5511,11 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, bool itof, int rmode, int scale, int sf, int type) { bool is_signed = !(opcode & 1); - bool is_double = type; TCGv_ptr tcg_fpstatus; - TCGv_i32 tcg_shift; + TCGv_i32 tcg_shift, tcg_single; + TCGv_i64 tcg_double; - tcg_fpstatus = get_fpstatus_ptr(false); + tcg_fpstatus = get_fpstatus_ptr(type == 3); tcg_shift = tcg_const_i32(64 - scale); @@ -5533,8 +5533,9 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_int = tcg_extend; } - if (is_double) { - TCGv_i64 tcg_double = tcg_temp_new_i64(); + switch (type) { + case 1: /* float64 */ + tcg_double = tcg_temp_new_i64(); if (is_signed) { gen_helper_vfp_sqtod(tcg_double, tcg_int, tcg_shift, tcg_fpstatus); @@ -5544,8 +5545,10 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, } write_fp_dreg(s, rd, tcg_double); tcg_temp_free_i64(tcg_double); - } else { - TCGv_i32 tcg_single = tcg_temp_new_i32(); + break; + + case 0: /* float32 */ + tcg_single = tcg_temp_new_i32(); if (is_signed) { gen_helper_vfp_sqtos(tcg_single, tcg_int, tcg_shift, tcg_fpstatus); @@ -5555,6 +5558,23 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, } write_fp_sreg(s, rd, tcg_single); tcg_temp_free_i32(tcg_single); + break; + + case 3: /* float16 */ + tcg_single = tcg_temp_new_i32(); + if (is_signed) { + gen_helper_vfp_sqtoh(tcg_single, tcg_int, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_uqtoh(tcg_single, tcg_int, + tcg_shift, tcg_fpstatus); + } + write_fp_sreg(s, rd, tcg_single); + tcg_temp_free_i32(tcg_single); + break; + + default: + g_assert_not_reached(); } } else { TCGv_i64 tcg_int = cpu_reg(s, rd); @@ -5571,8 +5591,9 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); - if (is_double) { - TCGv_i64 tcg_double = read_fp_dreg(s, rn); + switch (type) { + case 1: /* float64 */ + tcg_double = read_fp_dreg(s, rn); if (is_signed) { if (!sf) { gen_helper_vfp_tosld(tcg_int, tcg_double, @@ -5590,9 +5611,14 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_shift, tcg_fpstatus); } } + if (!sf) { + tcg_gen_ext32u_i64(tcg_int, tcg_int); + } tcg_temp_free_i64(tcg_double); - } else { - TCGv_i32 tcg_single = read_fp_sreg(s, rn); + break; + + case 0: /* float32 */ + tcg_single = read_fp_sreg(s, rn); if (sf) { if (is_signed) { gen_helper_vfp_tosqs(tcg_int, tcg_single, @@ -5614,14 +5640,39 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_temp_free_i32(tcg_dest); } tcg_temp_free_i32(tcg_single); + break; + + case 3: /* float16 */ + tcg_single = read_fp_sreg(s, rn); + if (sf) { + if (is_signed) { + gen_helper_vfp_tosqh(tcg_int, tcg_single, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_touqh(tcg_int, tcg_single, + tcg_shift, tcg_fpstatus); + } + } else { + TCGv_i32 tcg_dest = tcg_temp_new_i32(); + if (is_signed) { + gen_helper_vfp_toslh(tcg_dest, tcg_single, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_toulh(tcg_dest, tcg_single, + tcg_shift, tcg_fpstatus); + } + tcg_gen_extu_i32_i64(tcg_int, tcg_dest); + tcg_temp_free_i32(tcg_dest); + } + tcg_temp_free_i32(tcg_single); + break; + + default: + g_assert_not_reached(); } gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); tcg_temp_free_i32(tcg_rmode); - - if (!sf) { - tcg_gen_ext32u_i64(tcg_int, tcg_int); - } } tcg_temp_free_ptr(tcg_fpstatus); @@ -5791,7 +5842,20 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) /* actual FP conversions */ bool itof = extract32(opcode, 1, 1); - if (type > 1 || (rmode != 0 && opcode > 1)) { + if (rmode != 0 && opcode > 1) { + unallocated_encoding(s); + return; + } + switch (type) { + case 0: /* float32 */ + case 1: /* float64 */ + break; + case 3: /* float16 */ + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } From 2752728016bef06e7c9cfb961019272859beeca4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0448/2380] target/arm: Implement FCVT (scalar, fixed-point) for fp16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: qemu-stable@nongnu.org Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson Tested-by: Alex Bennée Message-id: 20180512003217.9105-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 93fb15d185..d0ed125442 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -5697,8 +5697,21 @@ static void disas_fp_fixed_conv(DisasContext *s, uint32_t insn) bool sf = extract32(insn, 31, 1); bool itof; - if (sbit || (type > 1) - || (!sf && scale < 32)) { + if (sbit || (!sf && scale < 32)) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: /* float32 */ + case 1: /* float64 */ + break; + case 3: /* float16 */ + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } From 3d99d931266eaeaf7e83703a53f32232cd6faad7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0449/2380] target/arm: Introduce and use read_fp_hreg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Tested-by: Alex Bennée Message-id: 20180512003217.9105-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index d0ed125442..78f12daaf6 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -615,6 +615,14 @@ static TCGv_i32 read_fp_sreg(DisasContext *s, int reg) return v; } +static TCGv_i32 read_fp_hreg(DisasContext *s, int reg) +{ + TCGv_i32 v = tcg_temp_new_i32(); + + tcg_gen_ld16u_i32(v, cpu_env, fp_reg_offset(s, reg, MO_16)); + return v; +} + /* Clear the bits above an N-bit vector, for N = (is_q ? 128 : 64). * If SVE is not enabled, then there are only 128 bits in the vector. */ @@ -4881,11 +4889,9 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) { TCGv_ptr fpst = NULL; - TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_op = read_fp_hreg(s, rn); TCGv_i32 tcg_res = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_op, rn, 0, MO_16); - switch (opcode) { case 0x0: /* FMOV */ tcg_gen_mov_i32(tcg_res, tcg_op); @@ -7784,13 +7790,10 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) tcg_temp_free_i64(tcg_op2); tcg_temp_free_i64(tcg_res); } else { - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); + TCGv_i32 tcg_op1 = read_fp_hreg(s, rn); + TCGv_i32 tcg_op2 = read_fp_hreg(s, rm); TCGv_i64 tcg_res = tcg_temp_new_i64(); - read_vec_element_i32(s, tcg_op1, rn, 0, MO_16); - read_vec_element_i32(s, tcg_op2, rm, 0, MO_16); - gen_helper_neon_mull_s16(tcg_res, tcg_op1, tcg_op2); gen_helper_neon_addl_saturate_s32(tcg_res, cpu_env, tcg_res, tcg_res); @@ -8331,13 +8334,10 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, fpst = get_fpstatus_ptr(true); - tcg_op1 = tcg_temp_new_i32(); - tcg_op2 = tcg_temp_new_i32(); + tcg_op1 = read_fp_hreg(s, rn); + tcg_op2 = read_fp_hreg(s, rm); tcg_res = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_op1, rn, 0, MO_16); - read_vec_element_i32(s, tcg_op2, rm, 0, MO_16); - switch (fpopcode) { case 0x03: /* FMULX */ gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst); @@ -12235,11 +12235,9 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) } if (is_scalar) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_op = read_fp_hreg(s, rn); TCGv_i32 tcg_res = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_op, rn, 0, MO_16); - switch (fpop) { case 0x1a: /* FCVTNS */ case 0x1b: /* FCVTMS */ From b8f5171cf01420a9f0ee895c5591e9b9914f391a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0450/2380] target/arm: Implement FP data-processing (2 source) for fp16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We missed all of the scalar fp16 binary operations. Cc: qemu-stable@nongnu.org Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson Tested-by: Alex Bennée Message-id: 20180512003217.9105-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 65 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 78f12daaf6..66607668ce 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -5299,6 +5299,61 @@ static void handle_fp_2src_double(DisasContext *s, int opcode, tcg_temp_free_i64(tcg_res); } +/* Floating-point data-processing (2 source) - half precision */ +static void handle_fp_2src_half(DisasContext *s, int opcode, + int rd, int rn, int rm) +{ + TCGv_i32 tcg_op1; + TCGv_i32 tcg_op2; + TCGv_i32 tcg_res; + TCGv_ptr fpst; + + tcg_res = tcg_temp_new_i32(); + fpst = get_fpstatus_ptr(true); + tcg_op1 = read_fp_hreg(s, rn); + tcg_op2 = read_fp_hreg(s, rm); + + switch (opcode) { + case 0x0: /* FMUL */ + gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x1: /* FDIV */ + gen_helper_advsimd_divh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x2: /* FADD */ + gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x3: /* FSUB */ + gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x4: /* FMAX */ + gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x5: /* FMIN */ + gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x6: /* FMAXNM */ + gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x7: /* FMINNM */ + gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x8: /* FNMUL */ + gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); + tcg_gen_xori_i32(tcg_res, tcg_res, 0x8000); + break; + default: + g_assert_not_reached(); + } + + write_fp_sreg(s, rd, tcg_res); + + tcg_temp_free_ptr(fpst); + tcg_temp_free_i32(tcg_op1); + tcg_temp_free_i32(tcg_op2); + tcg_temp_free_i32(tcg_res); +} + /* Floating point data-processing (2 source) * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 * +---+---+---+-----------+------+---+------+--------+-----+------+------+ @@ -5331,6 +5386,16 @@ static void disas_fp_2src(DisasContext *s, uint32_t insn) } handle_fp_2src_double(s, opcode, rd, rn, rm); break; + case 3: + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_fp_2src_half(s, opcode, rd, rn, rm); + break; default: unallocated_encoding(s); } From 95f9864fde6078e2d2c036a07cc4fe44f199be96 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0451/2380] target/arm: Implement FP data-processing (3 source) for fp16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We missed all of the scalar fp16 fma operations. Cc: qemu-stable@nongnu.org Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson Tested-by: Alex Bennée Message-id: 20180512003217.9105-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 66607668ce..a79c09eda2 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -5477,6 +5477,44 @@ static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1, tcg_temp_free_i64(tcg_res); } +/* Floating-point data-processing (3 source) - half precision */ +static void handle_fp_3src_half(DisasContext *s, bool o0, bool o1, + int rd, int rn, int rm, int ra) +{ + TCGv_i32 tcg_op1, tcg_op2, tcg_op3; + TCGv_i32 tcg_res = tcg_temp_new_i32(); + TCGv_ptr fpst = get_fpstatus_ptr(true); + + tcg_op1 = read_fp_hreg(s, rn); + tcg_op2 = read_fp_hreg(s, rm); + tcg_op3 = read_fp_hreg(s, ra); + + /* These are fused multiply-add, and must be done as one + * floating point operation with no rounding between the + * multiplication and addition steps. + * NB that doing the negations here as separate steps is + * correct : an input NaN should come out with its sign bit + * flipped if it is a negated-input. + */ + if (o1 == true) { + tcg_gen_xori_i32(tcg_op3, tcg_op3, 0x8000); + } + + if (o0 != o1) { + tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000); + } + + gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); + + write_fp_sreg(s, rd, tcg_res); + + tcg_temp_free_ptr(fpst); + tcg_temp_free_i32(tcg_op1); + tcg_temp_free_i32(tcg_op2); + tcg_temp_free_i32(tcg_op3); + tcg_temp_free_i32(tcg_res); +} + /* Floating point data-processing (3 source) * 31 30 29 28 24 23 22 21 20 16 15 14 10 9 5 4 0 * +---+---+---+-----------+------+----+------+----+------+------+------+ @@ -5506,6 +5544,16 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn) } handle_fp_3src_double(s, o0, o1, rd, rn, rm, ra); break; + case 3: + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_fp_3src_half(s, o0, o1, rd, rn, rm, ra); + break; default: unallocated_encoding(s); } From 7a1929256ea1a03df12625e75ed571c60dca5bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0452/2380] target/arm: Implement FCMP for fp16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These where missed out from the rest of the half-precision work. Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Alex Bennée Tested-by: Alex Bennée Signed-off-by: Richard Henderson Message-id: 20180512003217.9105-9-richard.henderson@linaro.org [rth: Diagnose lack of FP16 before fp_access_check] Signed-off-by: Richard Henderson Signed-off-by: Peter Maydell --- target/arm/helper-a64.c | 10 +++++ target/arm/helper-a64.h | 2 + target/arm/translate-a64.c | 88 ++++++++++++++++++++++++++++++-------- 3 files changed, 83 insertions(+), 17 deletions(-) diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 549ed3513e..4f8034c513 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -85,6 +85,16 @@ static inline uint32_t float_rel_to_flags(int res) return flags; } +uint64_t HELPER(vfp_cmph_a64)(float16 x, float16 y, void *fp_status) +{ + return float_rel_to_flags(float16_compare_quiet(x, y, fp_status)); +} + +uint64_t HELPER(vfp_cmpeh_a64)(float16 x, float16 y, void *fp_status) +{ + return float_rel_to_flags(float16_compare(x, y, fp_status)); +} + uint64_t HELPER(vfp_cmps_a64)(float32 x, float32 y, void *fp_status) { return float_rel_to_flags(float32_compare_quiet(x, y, fp_status)); diff --git a/target/arm/helper-a64.h b/target/arm/helper-a64.h index b8028ac98c..9d3a907049 100644 --- a/target/arm/helper-a64.h +++ b/target/arm/helper-a64.h @@ -19,6 +19,8 @@ DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64) DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, ptr) +DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, ptr) DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr) DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, ptr) DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, ptr) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index a79c09eda2..c078a54fa5 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -4712,14 +4712,14 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) } } -static void handle_fp_compare(DisasContext *s, bool is_double, +static void handle_fp_compare(DisasContext *s, int size, unsigned int rn, unsigned int rm, bool cmp_with_zero, bool signal_all_nans) { TCGv_i64 tcg_flags = tcg_temp_new_i64(); - TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_ptr fpst = get_fpstatus_ptr(size == MO_16); - if (is_double) { + if (size == MO_64) { TCGv_i64 tcg_vn, tcg_vm; tcg_vn = read_fp_dreg(s, rn); @@ -4736,19 +4736,35 @@ static void handle_fp_compare(DisasContext *s, bool is_double, tcg_temp_free_i64(tcg_vn); tcg_temp_free_i64(tcg_vm); } else { - TCGv_i32 tcg_vn, tcg_vm; + TCGv_i32 tcg_vn = tcg_temp_new_i32(); + TCGv_i32 tcg_vm = tcg_temp_new_i32(); - tcg_vn = read_fp_sreg(s, rn); + read_vec_element_i32(s, tcg_vn, rn, 0, size); if (cmp_with_zero) { - tcg_vm = tcg_const_i32(0); + tcg_gen_movi_i32(tcg_vm, 0); } else { - tcg_vm = read_fp_sreg(s, rm); + read_vec_element_i32(s, tcg_vm, rm, 0, size); } - if (signal_all_nans) { - gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } else { - gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + + switch (size) { + case MO_32: + if (signal_all_nans) { + gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } else { + gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } + break; + case MO_16: + if (signal_all_nans) { + gen_helper_vfp_cmpeh_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } else { + gen_helper_vfp_cmph_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } + break; + default: + g_assert_not_reached(); } + tcg_temp_free_i32(tcg_vn); tcg_temp_free_i32(tcg_vm); } @@ -4769,16 +4785,35 @@ static void handle_fp_compare(DisasContext *s, bool is_double, static void disas_fp_compare(DisasContext *s, uint32_t insn) { unsigned int mos, type, rm, op, rn, opc, op2r; + int size; mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); /* 0 = single, 1 = double */ + type = extract32(insn, 22, 2); rm = extract32(insn, 16, 5); op = extract32(insn, 14, 2); rn = extract32(insn, 5, 5); opc = extract32(insn, 3, 2); op2r = extract32(insn, 0, 3); - if (mos || op || op2r || type > 1) { + if (mos || op || op2r) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: + size = MO_32; + break; + case 1: + size = MO_64; + break; + case 3: + size = MO_16; + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } @@ -4787,7 +4822,7 @@ static void disas_fp_compare(DisasContext *s, uint32_t insn) return; } - handle_fp_compare(s, type, rn, rm, opc & 1, opc & 2); + handle_fp_compare(s, size, rn, rm, opc & 1, opc & 2); } /* Floating point conditional compare @@ -4801,16 +4836,35 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) unsigned int mos, type, rm, cond, rn, op, nzcv; TCGv_i64 tcg_flags; TCGLabel *label_continue = NULL; + int size; mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); /* 0 = single, 1 = double */ + type = extract32(insn, 22, 2); rm = extract32(insn, 16, 5); cond = extract32(insn, 12, 4); rn = extract32(insn, 5, 5); op = extract32(insn, 4, 1); nzcv = extract32(insn, 0, 4); - if (mos || type > 1) { + if (mos) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: + size = MO_32; + break; + case 1: + size = MO_64; + break; + case 3: + size = MO_16; + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } @@ -4831,7 +4885,7 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) gen_set_label(label_match); } - handle_fp_compare(s, type, rn, rm, false, op); + handle_fp_compare(s, size, rn, rm, false, op); if (cond < 0x0e) { gen_set_label(label_continue); From ace97feef3613194900d4eb9ffc6819b840fbaeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0453/2380] target/arm: Implement FCSEL for fp16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These were missed out from the rest of the half-precision work. Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Alex Bennée Tested-by: Alex Bennée Signed-off-by: Richard Henderson Message-id: 20180512003217.9105-10-richard.henderson@linaro.org [rth: Fix erroneous check vs type] Signed-off-by: Richard Henderson Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index c078a54fa5..9dacb583ae 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -4903,15 +4903,34 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) unsigned int mos, type, rm, cond, rn, rd; TCGv_i64 t_true, t_false, t_zero; DisasCompare64 c; + TCGMemOp sz; mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); /* 0 = single, 1 = double */ + type = extract32(insn, 22, 2); rm = extract32(insn, 16, 5); cond = extract32(insn, 12, 4); rn = extract32(insn, 5, 5); rd = extract32(insn, 0, 5); - if (mos || type > 1) { + if (mos) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: + sz = MO_32; + break; + case 1: + sz = MO_64; + break; + case 3: + sz = MO_16; + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } @@ -4920,11 +4939,11 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) return; } - /* Zero extend sreg inputs to 64 bits now. */ + /* Zero extend sreg & hreg inputs to 64 bits now. */ t_true = tcg_temp_new_i64(); t_false = tcg_temp_new_i64(); - read_vec_element(s, t_true, rn, 0, type ? MO_64 : MO_32); - read_vec_element(s, t_false, rm, 0, type ? MO_64 : MO_32); + read_vec_element(s, t_true, rn, 0, sz); + read_vec_element(s, t_false, rm, 0, sz); a64_test_cc(&c, cond); t_zero = tcg_const_i64(0); @@ -4933,7 +4952,7 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) tcg_temp_free_i64(t_false); a64_free_cc(&c); - /* Note that sregs write back zeros to the high bits, + /* Note that sregs & hregs write back zeros to the high bits, and we've already done the zero-extension. */ write_fp_dreg(s, rd, t_true); tcg_temp_free_i64(t_true); From 6ba28ddb9be37bdb67e3e38007a53ccbdcd010df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0454/2380] target/arm: Implement FMOV (immediate) for fp16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All the hard work is already done by vfp_expand_imm, we just need to make sure we pick up the correct size. Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Alex Bennée Tested-by: Alex Bennée Signed-off-by: Richard Henderson Message-id: 20180512003217.9105-11-richard.henderson@linaro.org [rth: Merge unallocated_encoding check with TCGMemOp conversion.] Signed-off-by: Richard Henderson Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 9dacb583ae..35997969b4 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -5674,11 +5674,25 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn) { int rd = extract32(insn, 0, 5); int imm8 = extract32(insn, 13, 8); - int is_double = extract32(insn, 22, 2); + int type = extract32(insn, 22, 2); uint64_t imm; TCGv_i64 tcg_res; + TCGMemOp sz; - if (is_double > 1) { + switch (type) { + case 0: + sz = MO_32; + break; + case 1: + sz = MO_64; + break; + case 3: + sz = MO_16; + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } @@ -5687,7 +5701,7 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn) return; } - imm = vfp_expand_imm(MO_32 + is_double, imm8); + imm = vfp_expand_imm(sz, imm8); tcg_res = tcg_const_i64(imm); write_fp_dreg(s, rd, tcg_res); From 905edee9101c54cda5b72286b7f7607cf1c3c4d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 15 May 2018 14:58:43 +0100 Subject: [PATCH 0455/2380] target/arm: Fix sqrt_f16 exception raising MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are meant to explicitly pass fpst, not cpu_env. Cc: qemu-stable@nongnu.org Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Tested-by: Alex Bennée Message-id: 20180512003217.9105-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 35997969b4..a0b0c43d12 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -4976,7 +4976,8 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); break; case 0x3: /* FSQRT */ - gen_helper_sqrt_f16(tcg_res, tcg_op, cpu_env); + fpst = get_fpstatus_ptr(true); + gen_helper_sqrt_f16(tcg_res, tcg_op, fpst); break; case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ From f6fb1f9b319feac09119848d206b07640ddd39e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 May 2018 14:58:44 +0100 Subject: [PATCH 0456/2380] sdcard: Correct CRC16 offset in sd_function_switch() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the Physical Layer Simplified Spec. "4.3.10.4 Switch Function Status": The block length is predefined to 512 bits and "4.10.2 SD Status": The SD Status contains status bits that are related to the SD Memory Card proprietary features and may be used for future application-specific usage. The size of the SD Status is one data block of 512 bit. The content of this register is transmitted to the Host over the DAT bus along with a 16-bit CRC. Thus the 16-bit CRC goes at offset 64. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180509060104.4458-3-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sd/sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 235e0518d6..7af19fa06c 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -787,7 +787,7 @@ static void sd_function_switch(SDState *sd, uint32_t arg) sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4); } memset(&sd->data[17], 0, 47); - stw_be_p(sd->data + 65, sd_crc16(sd->data, 64)); + stw_be_p(sd->data + 64, sd_crc16(sd->data, 64)); } static inline bool sd_wp_addr(SDState *sd, uint64_t addr) From ae7651804748c6b479d5ae09aeac4edb9c44f76e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 May 2018 14:58:44 +0100 Subject: [PATCH 0457/2380] tcg: Optionally log FPU state in TCG -d cpu logging Usually the logging of the CPU state produced by -d cpu is sufficient to diagnose problems, but sometimes you want to see the state of the floating point registers as well. We don't want to enable that by default as it adds a lot of extra data to the log; instead, allow it to be optionally enabled via -d fpu. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180510130024.31678-1-peter.maydell@linaro.org --- accel/tcg/cpu-exec.c | 9 ++++++--- include/qemu/log.h | 1 + util/log.c | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 81153e7a13..0b154cc678 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -156,11 +156,14 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) if (qemu_loglevel_mask(CPU_LOG_TB_CPU) && qemu_log_in_addr_range(itb->pc)) { qemu_log_lock(); + int flags = 0; + if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { + flags |= CPU_DUMP_FPU; + } #if defined(TARGET_I386) - log_cpu_state(cpu, CPU_DUMP_CCOP); -#else - log_cpu_state(cpu, 0); + flags |= CPU_DUMP_CCOP; #endif + log_cpu_state(cpu, flags); qemu_log_unlock(); } #endif /* DEBUG_DISAS */ diff --git a/include/qemu/log.h b/include/qemu/log.h index ff92a8b86a..b097a6cae1 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -44,6 +44,7 @@ static inline bool qemu_log_separate(void) #define CPU_LOG_PAGE (1 << 14) /* LOG_TRACE (1 << 15) is defined in log-for-trace.h */ #define CPU_LOG_TB_OP_IND (1 << 16) +#define CPU_LOG_TB_FPU (1 << 17) /* Lock output for a series of related logs. Since this is not needed * for a single qemu_log / qemu_log_mask / qemu_log_mask_and_addr, we diff --git a/util/log.c b/util/log.c index 96f30dd21a..c0dbbd4700 100644 --- a/util/log.c +++ b/util/log.c @@ -256,6 +256,8 @@ const QEMULogItem qemu_log_items[] = { "show trace before each executed TB (lots of logs)" }, { CPU_LOG_TB_CPU, "cpu", "show CPU registers before entering a TB (lots of logs)" }, + { CPU_LOG_TB_FPU, "fpu", + "include FPU registers in the 'cpu' logging" }, { CPU_LOG_MMU, "mmu", "log MMU-related activities" }, { CPU_LOG_PCALL, "pcall", From 7803696d8557e02441a2781a19d4008b2f50925c Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 27 Mar 2018 10:08:46 -0300 Subject: [PATCH 0458/2380] block-backend: simplify blk_get_aio_context blk_get_aio_context verifies if BlockDriverState bs is not NULL, return bdrv_get_aio_context(bs) if true or qemu_get_aio_context() otherwise. However, bdrv_get_aio_context from block.c already does this verification itself, also returning qemu_get_aio_context() if bs is NULL: AioContext *bdrv_get_aio_context(BlockDriverState *bs) { return bs ? bs->aio_context : qemu_get_aio_context(); } This patch simplifies blk_get_aio_context to simply call bdrv_get_aio_context instead of replicating the same logic. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Darren Kenny Signed-off-by: Kevin Wolf --- block/block-backend.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 681b240b12..89f47b00ea 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1865,13 +1865,7 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason) AioContext *blk_get_aio_context(BlockBackend *blk) { - BlockDriverState *bs = blk_bs(blk); - - if (bs) { - return bdrv_get_aio_context(bs); - } else { - return qemu_get_aio_context(); - } + return bdrv_get_aio_context(blk_bs(blk)); } static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) From e31f6864a6d6fa072a5d02e7679d0db6d0c22311 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 24 Apr 2018 14:25:01 -0500 Subject: [PATCH 0459/2380] block: Support byte-based aio callbacks We are gradually moving away from sector-based interfaces, towards byte-based. Add new sector-based aio callbacks for read and write, to match the fact that bdrv_aio_pdiscard is already byte-based. Ideally, drivers should be converted to use coroutine callbacks rather than aio; but that is not quite as trivial (and if we were to do that conversion, the null-aio driver would disappear), so for the short term, converting the signature but keeping things with aio is easier. However, we CAN declare that a driver that uses the byte-based aio interfaces now defaults to byte-based operations, and must explicitly provide a refresh_limits override to stick with larger alignments (making the alignment issues more obvious directly in the drivers touched in the next few patches). Once all drivers are converted, the sector-based aio callbacks will be removed; in the meantime, a FIXME comment is added due to a slight inefficiency that will be touched up as part of that later cleanup. Simplify some instances of 'bs->drv' into 'drv' while touching this, since the local variable already exists to reduce typing. Signed-off-by: Eric Blake Signed-off-by: Kevin Wolf --- block/io.c | 38 +++++++++++++++++++++++++++++--------- include/block/block_int.h | 6 ++++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/block/io.c b/block/io.c index bd9a19a9c4..407bc25df4 100644 --- a/block/io.c +++ b/block/io.c @@ -92,7 +92,8 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) } /* Default alignment based on whether driver has byte interface */ - bs->bl.request_alignment = drv->bdrv_co_preadv ? 1 : 512; + bs->bl.request_alignment = (drv->bdrv_co_preadv || + drv->bdrv_aio_preadv) ? 1 : 512; /* Take some limits from the children as a default */ if (bs->file) { @@ -924,12 +925,15 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, return drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags); } + /* FIXME - no need to calculate these if .bdrv_aio_preadv exists */ sector_num = offset >> BDRV_SECTOR_BITS; nb_sectors = bytes >> BDRV_SECTOR_BITS; - assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); - assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); - assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); + if (!drv->bdrv_aio_preadv) { + assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); + } if (drv->bdrv_co_readv) { return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); @@ -939,8 +943,13 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, .coroutine = qemu_coroutine_self(), }; - acb = bs->drv->bdrv_aio_readv(bs, sector_num, qiov, nb_sectors, + if (drv->bdrv_aio_preadv) { + acb = drv->bdrv_aio_preadv(bs, offset, bytes, qiov, flags, + bdrv_co_io_em_complete, &co); + } else { + acb = drv->bdrv_aio_readv(bs, sector_num, qiov, nb_sectors, bdrv_co_io_em_complete, &co); + } if (acb == NULL) { return -EIO; } else { @@ -972,12 +981,15 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, goto emulate_flags; } + /* FIXME - no need to calculate these if .bdrv_aio_pwritev exists */ sector_num = offset >> BDRV_SECTOR_BITS; nb_sectors = bytes >> BDRV_SECTOR_BITS; - assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); - assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); - assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); + if (!drv->bdrv_aio_pwritev) { + assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); + } if (drv->bdrv_co_writev_flags) { ret = drv->bdrv_co_writev_flags(bs, sector_num, nb_sectors, qiov, @@ -992,8 +1004,16 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, .coroutine = qemu_coroutine_self(), }; - acb = bs->drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors, + if (drv->bdrv_aio_pwritev) { + acb = drv->bdrv_aio_pwritev(bs, offset, bytes, qiov, + flags & bs->supported_write_flags, + bdrv_co_io_em_complete, &co); + flags &= ~bs->supported_write_flags; + } else { + assert(!bs->supported_write_flags); + acb = drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors, bdrv_co_io_em_complete, &co); + } if (acb == NULL) { ret = -EIO; } else { diff --git a/include/block/block_int.h b/include/block/block_int.h index c4dd1d4bb8..e772e3502b 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -144,9 +144,15 @@ struct BlockDriver { BlockAIOCB *(*bdrv_aio_readv)(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockCompletionFunc *cb, void *opaque); + BlockAIOCB *(*bdrv_aio_preadv)(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, void *opaque); BlockAIOCB *(*bdrv_aio_writev)(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockCompletionFunc *cb, void *opaque); + BlockAIOCB *(*bdrv_aio_pwritev)(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, void *opaque); BlockAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *(*bdrv_aio_pdiscard)(BlockDriverState *bs, From de7056a3f95e85aa6bba30ad4b37a8dbc2b072ab Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 24 Apr 2018 14:25:02 -0500 Subject: [PATCH 0460/2380] file-win32: Switch to byte-based callbacks We are gradually moving away from sector-based interfaces, towards byte-based. Make the change for the last few sector-based callbacks in the file-win32 driver. Note that the driver was already using byte-based calls for performing actual I/O, so this just gets rid of a round trip of scaling; however, as I don't know if Windows is tolerant of non-sector AIO operations, I went with the conservative approach of modifying .bdrv_refresh_limits to override the block layer defaults back to the pre-patch value of 512. Signed-off-by: Eric Blake Signed-off-by: Kevin Wolf --- block/file-win32.c | 47 +++++++++++++++++++++++++---------------- block/win32-aio.c | 5 ++--- include/block/raw-aio.h | 2 +- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/block/file-win32.c b/block/file-win32.c index 2e2f746bb1..3c67db4336 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -251,7 +251,11 @@ static void raw_probe_alignment(BlockDriverState *bs, Error **errp) &dg.Geometry.BytesPerSector, &freeClusters, &totalClusters); bs->bl.request_alignment = dg.Geometry.BytesPerSector; + return; } + + /* XXX Does Windows support AIO on less than 512-byte alignment? */ + bs->bl.request_alignment = 512; } static void raw_parse_flags(int flags, bool use_aio, int *access_flags, @@ -410,32 +414,32 @@ fail: return ret; } -static BlockAIOCB *raw_aio_readv(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockCompletionFunc *cb, void *opaque) +static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, void *opaque) { BDRVRawState *s = bs->opaque; if (s->aio) { - return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, - nb_sectors, cb, opaque, QEMU_AIO_READ); + return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, + cb, opaque, QEMU_AIO_READ); } else { - return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, - nb_sectors << BDRV_SECTOR_BITS, + return paio_submit(bs, s->hfile, offset, qiov, bytes, cb, opaque, QEMU_AIO_READ); } } -static BlockAIOCB *raw_aio_writev(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockCompletionFunc *cb, void *opaque) +static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, void *opaque) { BDRVRawState *s = bs->opaque; if (s->aio) { - return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, - nb_sectors, cb, opaque, QEMU_AIO_WRITE); + return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, + cb, opaque, QEMU_AIO_WRITE); } else { - return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, - nb_sectors << BDRV_SECTOR_BITS, + return paio_submit(bs, s->hfile, offset, qiov, bytes, cb, opaque, QEMU_AIO_WRITE); } } @@ -632,8 +636,8 @@ BlockDriver bdrv_file = { .bdrv_co_create_opts = raw_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_aio_readv = raw_aio_readv, - .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_preadv = raw_aio_preadv, + .bdrv_aio_pwritev = raw_aio_pwritev, .bdrv_aio_flush = raw_aio_flush, .bdrv_truncate = raw_truncate, @@ -708,6 +712,12 @@ static void hdev_parse_filename(const char *filename, QDict *options, bdrv_parse_filename_strip_prefix(filename, "host_device:", options); } +static void hdev_refresh_limits(BlockDriverState *bs, Error **errp) +{ + /* XXX Does Windows support AIO on less than 512-byte alignment? */ + bs->bl.request_alignment = 512; +} + static int hdev_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -793,9 +803,10 @@ static BlockDriver bdrv_host_device = { .bdrv_probe_device = hdev_probe_device, .bdrv_file_open = hdev_open, .bdrv_close = raw_close, + .bdrv_refresh_limits = hdev_refresh_limits, - .bdrv_aio_readv = raw_aio_readv, - .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_preadv = raw_aio_preadv, + .bdrv_aio_pwritev = raw_aio_pwritev, .bdrv_aio_flush = raw_aio_flush, .bdrv_detach_aio_context = raw_detach_aio_context, diff --git a/block/win32-aio.c b/block/win32-aio.c index 3be8f458fa..9cd355d42f 100644 --- a/block/win32-aio.c +++ b/block/win32-aio.c @@ -112,15 +112,14 @@ static const AIOCBInfo win32_aiocb_info = { BlockAIOCB *win32_aio_submit(BlockDriverState *bs, QEMUWin32AIOState *aio, HANDLE hfile, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, BlockCompletionFunc *cb, void *opaque, int type) { struct QEMUWin32AIOCB *waiocb; - uint64_t offset = sector_num * 512; DWORD rc; waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque); - waiocb->nbytes = nb_sectors * 512; + waiocb->nbytes = bytes; waiocb->qiov = qiov; waiocb->is_read = (type == QEMU_AIO_READ); diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index a4cdbbf1b7..9e47b8a629 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -57,7 +57,7 @@ void win32_aio_cleanup(QEMUWin32AIOState *aio); int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile); BlockAIOCB *win32_aio_submit(BlockDriverState *bs, QEMUWin32AIOState *aio, HANDLE hfile, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, BlockCompletionFunc *cb, void *opaque, int type); void win32_aio_detach_aio_context(QEMUWin32AIOState *aio, AioContext *old_context); From b3241e9274ca1aafb94ad8683305bb383e11c766 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 24 Apr 2018 14:25:03 -0500 Subject: [PATCH 0461/2380] null: Switch to byte-based read/write We are gradually moving away from sector-based interfaces, towards byte-based. Make the change for the last few sector-based callbacks in the null-co and null-aio drivers. Note that since the null driver does nothing on writes, it trivially supports the BDRV_REQ_FUA flag (all writes have already landed to the same bit-bucket without needing an extra flush call). Also, since the null driver does just as well with byte-based requests, we can now avoid cycles wasted on read-modify-write by taking advantage of the block layer now defaulting the alignment to 1 instead of 512. Signed-off-by: Eric Blake Signed-off-by: Kevin Wolf --- block/null.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/block/null.c b/block/null.c index 3944550f67..5d610fdfba 100644 --- a/block/null.c +++ b/block/null.c @@ -93,6 +93,7 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags, } s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false); qemu_opts_del(opts); + bs->supported_write_flags = BDRV_REQ_FUA; return ret; } @@ -116,22 +117,22 @@ static coroutine_fn int null_co_common(BlockDriverState *bs) return 0; } -static coroutine_fn int null_co_readv(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov) +static coroutine_fn int null_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) { BDRVNullState *s = bs->opaque; if (s->read_zeroes) { - qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE); + qemu_iovec_memset(qiov, 0, 0, bytes); } return null_co_common(bs); } -static coroutine_fn int null_co_writev(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov) +static coroutine_fn int null_co_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) { return null_co_common(bs); } @@ -186,26 +187,26 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs, return &acb->common; } -static BlockAIOCB *null_aio_readv(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, - int nb_sectors, - BlockCompletionFunc *cb, - void *opaque) +static BlockAIOCB *null_aio_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, + void *opaque) { BDRVNullState *s = bs->opaque; if (s->read_zeroes) { - qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE); + qemu_iovec_memset(qiov, 0, 0, bytes); } return null_aio_common(bs, cb, opaque); } -static BlockAIOCB *null_aio_writev(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, - int nb_sectors, - BlockCompletionFunc *cb, - void *opaque) +static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, + void *opaque) { return null_aio_common(bs, cb, opaque); } @@ -265,8 +266,8 @@ static BlockDriver bdrv_null_co = { .bdrv_close = null_close, .bdrv_getlength = null_getlength, - .bdrv_co_readv = null_co_readv, - .bdrv_co_writev = null_co_writev, + .bdrv_co_preadv = null_co_preadv, + .bdrv_co_pwritev = null_co_pwritev, .bdrv_co_flush_to_disk = null_co_flush, .bdrv_reopen_prepare = null_reopen_prepare, @@ -285,8 +286,8 @@ static BlockDriver bdrv_null_aio = { .bdrv_close = null_close, .bdrv_getlength = null_getlength, - .bdrv_aio_readv = null_aio_readv, - .bdrv_aio_writev = null_aio_writev, + .bdrv_aio_preadv = null_aio_preadv, + .bdrv_aio_pwritev = null_aio_pwritev, .bdrv_aio_flush = null_aio_flush, .bdrv_reopen_prepare = null_reopen_prepare, From e8e16d4baffc2d5734c1c54c28867860dd5a0074 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 24 Apr 2018 14:25:04 -0500 Subject: [PATCH 0462/2380] rbd: Switch to byte-based callbacks We are gradually moving away from sector-based interfaces, towards byte-based. Make the change for the last few sector-based callbacks in the rbd driver. Note that the driver was already using byte-based calls for performing actual I/O, so this just gets rid of a round trip of scaling; however, as I don't know if RBD is tolerant of non-sector AIO operations, I went with the conservate approach of adding .bdrv_refresh_limits to override the block layer defaults back to the pre-patch value of 512. Signed-off-by: Eric Blake Signed-off-by: Kevin Wolf --- block/rbd.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index a14b42fcde..a16431e267 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -231,6 +231,13 @@ done: } +static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp) +{ + /* XXX Does RBD support AIO on less than 512-byte alignment? */ + bs->bl.request_alignment = 512; +} + + static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, Error **errp) { @@ -899,27 +906,23 @@ failed: return NULL; } -static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs, - int64_t sector_num, - QEMUIOVector *qiov, - int nb_sectors, - BlockCompletionFunc *cb, - void *opaque) -{ - return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov, - (int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque, - RBD_AIO_READ); -} - -static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs, - int64_t sector_num, - QEMUIOVector *qiov, - int nb_sectors, +static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, BlockCompletionFunc *cb, void *opaque) { - return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov, - (int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque, + return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque, + RBD_AIO_READ); +} + +static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, + void *opaque) +{ + return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque, RBD_AIO_WRITE); } @@ -1158,6 +1161,7 @@ static BlockDriver bdrv_rbd = { .format_name = "rbd", .instance_size = sizeof(BDRVRBDState), .bdrv_parse_filename = qemu_rbd_parse_filename, + .bdrv_refresh_limits = qemu_rbd_refresh_limits, .bdrv_file_open = qemu_rbd_open, .bdrv_close = qemu_rbd_close, .bdrv_reopen_prepare = qemu_rbd_reopen_prepare, @@ -1170,8 +1174,8 @@ static BlockDriver bdrv_rbd = { .bdrv_truncate = qemu_rbd_truncate, .protocol_name = "rbd", - .bdrv_aio_readv = qemu_rbd_aio_readv, - .bdrv_aio_writev = qemu_rbd_aio_writev, + .bdrv_aio_preadv = qemu_rbd_aio_preadv, + .bdrv_aio_pwritev = qemu_rbd_aio_pwritev, #ifdef LIBRBD_SUPPORTS_AIO_FLUSH .bdrv_aio_flush = qemu_rbd_aio_flush, From 918889b291dfb8f55d537e01ecbb082d27c2737a Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 24 Apr 2018 14:25:05 -0500 Subject: [PATCH 0463/2380] vxhs: Switch to byte-based callbacks We are gradually moving away from sector-based interfaces, towards byte-based. Make the change for the last few sector-based callbacks in the vxhs driver. Note that the driver was already using byte-based calls for performing actual I/O, so this just gets rid of a round trip of scaling; however, as I don't know if VxHS is tolerant of non-sector AIO operations, I went with the conservative approach of adding .bdrv_refresh_limits to override the block layer defaults back to the pre-patch value of 512. Signed-off-by: Eric Blake Signed-off-by: Kevin Wolf --- block/vxhs.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/block/vxhs.c b/block/vxhs.c index 55ae1a666e..339e23218d 100644 --- a/block/vxhs.c +++ b/block/vxhs.c @@ -216,6 +216,12 @@ static void vxhs_parse_filename(const char *filename, QDict *options, } } +static void vxhs_refresh_limits(BlockDriverState *bs, Error **errp) +{ + /* XXX Does VXHS support AIO on less than 512-byte alignment? */ + bs->bl.request_alignment = 512; +} + static int vxhs_init_and_ref(void) { if (vxhs_ref++ == 0) { @@ -424,21 +430,17 @@ static const AIOCBInfo vxhs_aiocb_info = { * and is passed to QNIO. When QNIO completes the work, * it will be passed back through the callback. */ -static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num, - QEMUIOVector *qiov, int nb_sectors, +static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset, + QEMUIOVector *qiov, uint64_t size, BlockCompletionFunc *cb, void *opaque, VDISKAIOCmd iodir) { VXHSAIOCB *acb = NULL; BDRVVXHSState *s = bs->opaque; - size_t size; - uint64_t offset; int iio_flags = 0; int ret = 0; void *dev_handle = s->vdisk_hostinfo.dev_handle; - offset = sector_num * BDRV_SECTOR_SIZE; - size = nb_sectors * BDRV_SECTOR_SIZE; acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque); /* @@ -451,11 +453,11 @@ static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num, switch (iodir) { case VDISK_AIO_WRITE: ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov, - offset, (uint64_t)size, iio_flags); + offset, size, iio_flags); break; case VDISK_AIO_READ: ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov, - offset, (uint64_t)size, iio_flags); + offset, size, iio_flags); break; default: trace_vxhs_aio_rw_invalid(iodir); @@ -474,22 +476,20 @@ errout: return NULL; } -static BlockAIOCB *vxhs_aio_readv(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, - int nb_sectors, +static BlockAIOCB *vxhs_aio_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, BlockCompletionFunc *cb, void *opaque) { - return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, cb, - opaque, VDISK_AIO_READ); + return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_READ); } -static BlockAIOCB *vxhs_aio_writev(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, - int nb_sectors, - BlockCompletionFunc *cb, void *opaque) +static BlockAIOCB *vxhs_aio_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, void *opaque) { - return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, - cb, opaque, VDISK_AIO_WRITE); + return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_WRITE); } static void vxhs_close(BlockDriverState *bs) @@ -561,10 +561,11 @@ static BlockDriver bdrv_vxhs = { .instance_size = sizeof(BDRVVXHSState), .bdrv_file_open = vxhs_open, .bdrv_parse_filename = vxhs_parse_filename, + .bdrv_refresh_limits = vxhs_refresh_limits, .bdrv_close = vxhs_close, .bdrv_getlength = vxhs_getlength, - .bdrv_aio_readv = vxhs_aio_readv, - .bdrv_aio_writev = vxhs_aio_writev, + .bdrv_aio_preadv = vxhs_aio_preadv, + .bdrv_aio_pwritev = vxhs_aio_pwritev, }; static void bdrv_vxhs_init(void) From edfab6a08b0dc240ac3a665adf4dc40db9b9ec7f Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 24 Apr 2018 14:25:06 -0500 Subject: [PATCH 0464/2380] block: Drop last of the sector-based aio callbacks We are gradually moving away from sector-based interfaces, towards byte-based. Now that all drivers with aio callbacks are using the byte-based interfaces, we can remove the sector-based versions. Signed-off-by: Eric Blake Signed-off-by: Kevin Wolf --- block/io.c | 84 +++++++++++++++++---------------------- include/block/block_int.h | 6 --- 2 files changed, 36 insertions(+), 54 deletions(-) diff --git a/block/io.c b/block/io.c index 407bc25df4..6b110b207a 100644 --- a/block/io.c +++ b/block/io.c @@ -925,31 +925,14 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, return drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags); } - /* FIXME - no need to calculate these if .bdrv_aio_preadv exists */ - sector_num = offset >> BDRV_SECTOR_BITS; - nb_sectors = bytes >> BDRV_SECTOR_BITS; - - if (!drv->bdrv_aio_preadv) { - assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); - assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); - assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); - } - - if (drv->bdrv_co_readv) { - return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); - } else { + if (drv->bdrv_aio_preadv) { BlockAIOCB *acb; CoroutineIOCompletion co = { .coroutine = qemu_coroutine_self(), }; - if (drv->bdrv_aio_preadv) { - acb = drv->bdrv_aio_preadv(bs, offset, bytes, qiov, flags, - bdrv_co_io_em_complete, &co); - } else { - acb = drv->bdrv_aio_readv(bs, sector_num, qiov, nb_sectors, - bdrv_co_io_em_complete, &co); - } + acb = drv->bdrv_aio_preadv(bs, offset, bytes, qiov, flags, + bdrv_co_io_em_complete, &co); if (acb == NULL) { return -EIO; } else { @@ -957,6 +940,16 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, return co.ret; } } + + sector_num = offset >> BDRV_SECTOR_BITS; + nb_sectors = bytes >> BDRV_SECTOR_BITS; + + assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); + assert(drv->bdrv_co_readv); + + return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); } static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, @@ -981,45 +974,40 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, goto emulate_flags; } - /* FIXME - no need to calculate these if .bdrv_aio_pwritev exists */ - sector_num = offset >> BDRV_SECTOR_BITS; - nb_sectors = bytes >> BDRV_SECTOR_BITS; - - if (!drv->bdrv_aio_pwritev) { - assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); - assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); - assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); - } - - if (drv->bdrv_co_writev_flags) { - ret = drv->bdrv_co_writev_flags(bs, sector_num, nb_sectors, qiov, - flags & bs->supported_write_flags); - flags &= ~bs->supported_write_flags; - } else if (drv->bdrv_co_writev) { - assert(!bs->supported_write_flags); - ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov); - } else { + if (drv->bdrv_aio_pwritev) { BlockAIOCB *acb; CoroutineIOCompletion co = { .coroutine = qemu_coroutine_self(), }; - if (drv->bdrv_aio_pwritev) { - acb = drv->bdrv_aio_pwritev(bs, offset, bytes, qiov, - flags & bs->supported_write_flags, - bdrv_co_io_em_complete, &co); - flags &= ~bs->supported_write_flags; - } else { - assert(!bs->supported_write_flags); - acb = drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors, - bdrv_co_io_em_complete, &co); - } + acb = drv->bdrv_aio_pwritev(bs, offset, bytes, qiov, + flags & bs->supported_write_flags, + bdrv_co_io_em_complete, &co); + flags &= ~bs->supported_write_flags; if (acb == NULL) { ret = -EIO; } else { qemu_coroutine_yield(); ret = co.ret; } + goto emulate_flags; + } + + sector_num = offset >> BDRV_SECTOR_BITS; + nb_sectors = bytes >> BDRV_SECTOR_BITS; + + assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); + + if (drv->bdrv_co_writev_flags) { + ret = drv->bdrv_co_writev_flags(bs, sector_num, nb_sectors, qiov, + flags & bs->supported_write_flags); + flags &= ~bs->supported_write_flags; + } else { + assert(drv->bdrv_co_writev); + assert(!bs->supported_write_flags); + ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov); } emulate_flags: diff --git a/include/block/block_int.h b/include/block/block_int.h index e772e3502b..0bba7ed024 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -141,15 +141,9 @@ struct BlockDriver { void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options); /* aio */ - BlockAIOCB *(*bdrv_aio_readv)(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockCompletionFunc *cb, void *opaque); BlockAIOCB *(*bdrv_aio_preadv)(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags, BlockCompletionFunc *cb, void *opaque); - BlockAIOCB *(*bdrv_aio_writev)(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockCompletionFunc *cb, void *opaque); BlockAIOCB *(*bdrv_aio_pwritev)(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags, BlockCompletionFunc *cb, void *opaque); From e18a58b4e3aa355a9b6eef1f39a980a4f18f1294 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 24 Apr 2018 17:01:57 -0500 Subject: [PATCH 0465/2380] block: Merge .bdrv_co_writev{,_flags} in drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have too many driver callback interfaces; simplify the mess somewhat by merging the flags parameter of .bdrv_co_writev_flags() into .bdrv_co_writev(). Note that as long as a driver doesn't set .supported_write_flags, the flags argument will be 0 and behavior is identical. Also note that the public function bdrv_co_writev() still lacks a flags argument; so the driver signature is thus intentionally slightly different. But that's not the end of the world, nor the first time that the driver interface differs slightly from the public interface. Ideally, we should be rewriting all of these drivers to use modern byte-based interfaces. But that's a more invasive patch to write and audit, compared to the simplification done here. Signed-off-by: Eric Blake Reviewed-by: Daniel P. Berrangé Signed-off-by: Kevin Wolf --- block/gluster.c | 4 +++- block/io.c | 13 ++++--------- block/iscsi.c | 8 ++++---- block/parallels.c | 4 +++- block/qcow.c | 6 ++++-- block/qed.c | 3 ++- block/replication.c | 4 +++- block/sheepdog.c | 4 +++- block/ssh.c | 4 +++- block/vhdx.c | 4 +++- include/block/block_int.h | 2 -- 11 files changed, 32 insertions(+), 24 deletions(-) diff --git a/block/gluster.c b/block/gluster.c index 55be566f6d..9900b6420c 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -1194,8 +1194,10 @@ static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs, static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov) + QEMUIOVector *qiov, + int flags) { + assert(!flags); return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); } diff --git a/block/io.c b/block/io.c index 6b110b207a..4fad5ac2fe 100644 --- a/block/io.c +++ b/block/io.c @@ -1000,15 +1000,10 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); - if (drv->bdrv_co_writev_flags) { - ret = drv->bdrv_co_writev_flags(bs, sector_num, nb_sectors, qiov, - flags & bs->supported_write_flags); - flags &= ~bs->supported_write_flags; - } else { - assert(drv->bdrv_co_writev); - assert(!bs->supported_write_flags); - ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov); - } + assert(drv->bdrv_co_writev); + ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov, + flags & bs->supported_write_flags); + flags &= ~bs->supported_write_flags; emulate_flags: if (ret == 0 && (flags & BDRV_REQ_FUA)) { diff --git a/block/iscsi.c b/block/iscsi.c index d19ae0e398..3fd7203916 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -556,8 +556,8 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun, } static int coroutine_fn -iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - QEMUIOVector *iov, int flags) +iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *iov, int flags) { IscsiLun *iscsilun = bs->opaque; struct IscsiTask iTask; @@ -2220,7 +2220,7 @@ static BlockDriver bdrv_iscsi = { .bdrv_co_pdiscard = iscsi_co_pdiscard, .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, .bdrv_co_readv = iscsi_co_readv, - .bdrv_co_writev_flags = iscsi_co_writev_flags, + .bdrv_co_writev = iscsi_co_writev, .bdrv_co_flush_to_disk = iscsi_co_flush, #ifdef __linux__ @@ -2255,7 +2255,7 @@ static BlockDriver bdrv_iser = { .bdrv_co_pdiscard = iscsi_co_pdiscard, .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, .bdrv_co_readv = iscsi_co_readv, - .bdrv_co_writev_flags = iscsi_co_writev_flags, + .bdrv_co_writev = iscsi_co_writev, .bdrv_co_flush_to_disk = iscsi_co_flush, #ifdef __linux__ diff --git a/block/parallels.c b/block/parallels.c index 045810d00f..6e9c37f44e 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -311,13 +311,15 @@ static int coroutine_fn parallels_co_block_status(BlockDriverState *bs, } static coroutine_fn int parallels_co_writev(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) + int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov, int flags) { BDRVParallelsState *s = bs->opaque; uint64_t bytes_done = 0; QEMUIOVector hd_qiov; int ret = 0; + assert(!flags); qemu_iovec_init(&hd_qiov, qiov->niov); while (nb_sectors > 0) { diff --git a/block/qcow.c b/block/qcow.c index 4b2f7db74c..3ba2ca25ea 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -720,7 +720,8 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, } static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) + int nb_sectors, QEMUIOVector *qiov, + int flags) { BDRVQcowState *s = bs->opaque; int index_in_cluster; @@ -731,6 +732,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, uint8_t *buf; void *orig_buf; + assert(!flags); s->cluster_cache_offset = -1; /* disable compressed cache */ /* We must always copy the iov when encrypting, so we @@ -1110,7 +1112,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, if (ret != Z_STREAM_END || out_len >= s->cluster_size) { /* could not compress: write normal cluster */ ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS, - bytes >> BDRV_SECTOR_BITS, qiov); + bytes >> BDRV_SECTOR_BITS, qiov, 0); if (ret < 0) { goto fail; } diff --git a/block/qed.c b/block/qed.c index 1db8eaf241..65cfe92393 100644 --- a/block/qed.c +++ b/block/qed.c @@ -1437,8 +1437,9 @@ static int coroutine_fn bdrv_qed_co_readv(BlockDriverState *bs, static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov) + QEMUIOVector *qiov, int flags) { + assert(!flags); return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE); } diff --git a/block/replication.c b/block/replication.c index 6c0c7186d9..48148b884a 100644 --- a/block/replication.c +++ b/block/replication.c @@ -260,7 +260,8 @@ out: static coroutine_fn int replication_co_writev(BlockDriverState *bs, int64_t sector_num, int remaining_sectors, - QEMUIOVector *qiov) + QEMUIOVector *qiov, + int flags) { BDRVReplicationState *s = bs->opaque; QEMUIOVector hd_qiov; @@ -271,6 +272,7 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, int ret; int64_t n; + assert(!flags); ret = replication_get_io_status(s); if (ret < 0) { goto out; diff --git a/block/sheepdog.c b/block/sheepdog.c index fed2a04797..4237132419 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -2614,13 +2614,15 @@ static void sd_aio_complete(SheepdogAIOCB *acb) } static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) + int nb_sectors, QEMUIOVector *qiov, + int flags) { SheepdogAIOCB acb; int ret; int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE; BDRVSheepdogState *s = bs->opaque; + assert(!flags); if (offset > s->inode.vdi_size) { ret = sd_truncate(bs, offset, PREALLOC_MODE_OFF, NULL); if (ret < 0) { diff --git a/block/ssh.c b/block/ssh.c index 412a1bfc17..4c4fa3ccfc 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -1164,11 +1164,13 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, static coroutine_fn int ssh_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) + int nb_sectors, QEMUIOVector *qiov, + int flags) { BDRVSSHState *s = bs->opaque; int ret; + assert(!flags); qemu_co_mutex_lock(&s->lock); ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE, nb_sectors * BDRV_SECTOR_SIZE, qiov); diff --git a/block/vhdx.c b/block/vhdx.c index c3a4220a35..0b1e21c750 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1226,7 +1226,8 @@ int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s) } static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) + int nb_sectors, QEMUIOVector *qiov, + int flags) { int ret = -ENOTSUP; BDRVVHDXState *s = bs->opaque; @@ -1242,6 +1243,7 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, uint64_t bat_prior_offset = 0; bool bat_update = false; + assert(!flags); qemu_iovec_init(&hd_qiov, qiov->niov); qemu_co_mutex_lock(&s->lock); diff --git a/include/block/block_int.h b/include/block/block_int.h index 0bba7ed024..e3d6219f4e 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -174,8 +174,6 @@ struct BlockDriver { int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags); int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); - int coroutine_fn (*bdrv_co_writev_flags)(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags); /** * @offset: position in bytes to write at From 83592184d8692c818e083c3ce43f63ca2533fa85 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Fri, 9 Mar 2018 16:11:07 +0200 Subject: [PATCH 0466/2380] hmp: Allow using a qdev id in block_set_io_throttle The QMP version of this command can take a qdev ID since 7a9877a02635, but the HMP version is still using the deprecated block device name so there's no way to refer to a block device added like this: -blockdev node-name=disk0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 -device virtio-blk-pci,id=virtio-blk-pci0,drive=disk0 This patch works around this problem by using the specified name as a qdev ID if the block device name is not found. Signed-off-by: Alberto Garcia Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- hmp-commands.hx | 3 ++- hmp.c | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 35d862a5d2..227f7eee88 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1647,7 +1647,8 @@ ETEXI STEXI @item block_set_io_throttle @var{device} @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} @findex block_set_io_throttle -Change I/O throttle limits for a block drive to @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} +Change I/O throttle limits for a block drive to @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr}. +@var{device} can be a block device name, a qdev ID or a QOM path. ETEXI { diff --git a/hmp.c b/hmp.c index 898e25f3e1..bdb340605c 100644 --- a/hmp.c +++ b/hmp.c @@ -1789,9 +1789,8 @@ void hmp_change(Monitor *mon, const QDict *qdict) void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) { Error *err = NULL; + char *device = (char *) qdict_get_str(qdict, "device"); BlockIOThrottle throttle = { - .has_device = true, - .device = (char *) qdict_get_str(qdict, "device"), .bps = qdict_get_int(qdict, "bps"), .bps_rd = qdict_get_int(qdict, "bps_rd"), .bps_wr = qdict_get_int(qdict, "bps_wr"), @@ -1800,6 +1799,17 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) .iops_wr = qdict_get_int(qdict, "iops_wr"), }; + /* qmp_block_set_io_throttle has separate parameters for the + * (deprecated) block device name and the qdev ID but the HMP + * version has only one, so we must decide which one to pass. */ + if (blk_by_name(device)) { + throttle.has_device = true; + throttle.device = device; + } else { + throttle.has_id = true; + throttle.id = device; + } + qmp_block_set_io_throttle(&throttle, &err); hmp_handle_error(mon, &err); } From ab9ba614556ac5b0f8d96b99e0dba19f1e28d6c2 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 8 May 2018 19:36:59 -0400 Subject: [PATCH 0467/2380] blockjob: expose error string via query When we've reached the concluded state, we need to expose the error state if applicable. Add the new field. This should be sufficient for determining if a job completed successfully or not after concluding; if we want to discriminate based on how it failed more mechanically, we can always add an explicit return code enumeration later. I didn't bother to make it only show up if we are in the concluded state; I don't think it's necessary. Cc: qemu-stable@nongnu.org Signed-off-by: John Snow Reviewed-by: Eric Blake Reviewed-by: Alberto Garcia Signed-off-by: Kevin Wolf --- blockjob.c | 2 ++ qapi/block-core.json | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/blockjob.c b/blockjob.c index dfffad921a..0b9b336d19 100644 --- a/blockjob.c +++ b/blockjob.c @@ -831,6 +831,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info->status = job->status; info->auto_finalize = job->auto_finalize; info->auto_dismiss = job->auto_dismiss; + info->has_error = job->ret != 0; + info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL; return info; } diff --git a/qapi/block-core.json b/qapi/block-core.json index 21c3470234..17ffd44cce 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1172,6 +1172,9 @@ # @auto-dismiss: Job will dismiss itself when CONCLUDED, moving to the NULL # state and disappearing from the query list. (since 2.12) # +# @error: Error information if the job did not complete successfully. +# Not set if the job completed successfully. (since 2.12.1) +# # Since: 1.1 ## { 'struct': 'BlockJobInfo', @@ -1179,7 +1182,8 @@ 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', 'status': 'BlockJobStatus', - 'auto-finalize': 'bool', 'auto-dismiss': 'bool' } } + 'auto-finalize': 'bool', 'auto-dismiss': 'bool', + '*error': 'str' } } ## # @query-block-jobs: From 37aa19b63c46d933f1e4ea944cfccee54e2caf4a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 May 2018 11:55:30 +0200 Subject: [PATCH 0468/2380] blockjob: Fix assertion in block_job_finalize() Every job gets a non-NULL job->txn on creation, but it doesn't necessarily keep it until it is decommissioned: Finalising a job removes it from its transaction. Therefore, calling 'blockdev-job-finalize' a second time on an already concluded job causes an assertion failure. Remove job->txn from the assertion in block_job_finalize() to fix this. block_job_do_finalize() still has the same assertion, but if a job is already removed from its transaction, block_job_apply_verb() will already error out before we run into that assertion. Cc: qemu-stable@nongnu.org Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Max Reitz Reviewed-by: John Snow --- blockjob.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockjob.c b/blockjob.c index 0b9b336d19..0ebc5ab178 100644 --- a/blockjob.c +++ b/blockjob.c @@ -702,7 +702,7 @@ void block_job_complete(BlockJob *job, Error **errp) void block_job_finalize(BlockJob *job, Error **errp) { - assert(job && job->id && job->txn); + assert(job && job->id); if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) { return; } From 05df8a6a2b4e36e8d69de2130e616d5ac28e8837 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 18 Jan 2018 18:08:22 +0100 Subject: [PATCH 0469/2380] blockjob: Wrappers for progress counter access Block job drivers are not expected to mess with the internals of the BlockJob object, so provide wrapper functions for one of the cases where they still do it: Updating the progress counter. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 22 +++++++++++++--------- block/commit.c | 16 ++++++++-------- block/mirror.c | 11 +++++------ block/stream.c | 14 ++++++++------ blockjob.c | 10 ++++++++++ include/block/blockjob.h | 19 +++++++++++++++++++ 6 files changed, 63 insertions(+), 29 deletions(-) diff --git a/block/backup.c b/block/backup.c index 453cd62c24..5d95805472 100644 --- a/block/backup.c +++ b/block/backup.c @@ -39,6 +39,7 @@ typedef struct BackupBlockJob { BlockdevOnError on_source_error; BlockdevOnError on_target_error; CoRwlock flush_rwlock; + uint64_t len; uint64_t bytes_read; int64_t cluster_size; bool compress; @@ -118,7 +119,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, trace_backup_do_cow_process(job, start); - n = MIN(job->cluster_size, job->common.len - start); + n = MIN(job->cluster_size, job->len - start); if (!bounce_buffer) { bounce_buffer = blk_blockalign(blk, job->cluster_size); @@ -159,7 +160,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, * offset field is an opaque progress value, it is not a disk offset. */ job->bytes_read += n; - job->common.offset += n; + block_job_progress_update(&job->common, n); } out: @@ -261,7 +262,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) return; } - len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size); + len = DIV_ROUND_UP(backup_job->len, backup_job->cluster_size); hbitmap_set(backup_job->copy_bitmap, 0, len); } @@ -420,8 +421,9 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); } - job->common.offset = job->common.len - - hbitmap_count(job->copy_bitmap) * job->cluster_size; + /* TODO block_job_progress_set_remaining() would make more sense */ + block_job_progress_update(&job->common, + job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size); bdrv_dirty_iter_free(dbi); } @@ -437,7 +439,9 @@ static void coroutine_fn backup_run(void *opaque) QLIST_INIT(&job->inflight_reqs); qemu_co_rwlock_init(&job->flush_rwlock); - nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size); + nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size); + block_job_progress_set_remaining(&job->common, job->len); + job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { backup_incremental_init_copy_bitmap(job); @@ -461,7 +465,7 @@ static void coroutine_fn backup_run(void *opaque) ret = backup_run_incremental(job); } else { /* Both FULL and TOP SYNC_MODE's require copying.. */ - for (offset = 0; offset < job->common.len; + for (offset = 0; offset < job->len; offset += job->cluster_size) { bool error_is_read; int alloced = 0; @@ -620,7 +624,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, goto error; } - /* job->common.len is fixed, so we can't allow resize */ + /* job->len is fixed, so we can't allow resize */ job = block_job_create(job_id, &backup_job_driver, txn, bs, BLK_PERM_CONSISTENT_READ, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | @@ -676,7 +680,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, /* Required permissions are already taken with target's blk_new() */ block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, &error_abort); - job->common.len = len; + job->len = len; return &job->common; diff --git a/block/commit.c b/block/commit.c index 1432baeef4..50b191c980 100644 --- a/block/commit.c +++ b/block/commit.c @@ -146,21 +146,21 @@ static void coroutine_fn commit_run(void *opaque) int64_t n = 0; /* bytes */ void *buf = NULL; int bytes_written = 0; - int64_t base_len; + int64_t len, base_len; - ret = s->common.len = blk_getlength(s->top); - - if (s->common.len < 0) { + ret = len = blk_getlength(s->top); + if (len < 0) { goto out; } + block_job_progress_set_remaining(&s->common, len); ret = base_len = blk_getlength(s->base); if (base_len < 0) { goto out; } - if (base_len < s->common.len) { - ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL); + if (base_len < len) { + ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL); if (ret) { goto out; } @@ -168,7 +168,7 @@ static void coroutine_fn commit_run(void *opaque) buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); - for (offset = 0; offset < s->common.len; offset += n) { + for (offset = 0; offset < len; offset += n) { bool copy; /* Note that even when no rate limit is applied we need to yield @@ -198,7 +198,7 @@ static void coroutine_fn commit_run(void *opaque) } } /* Publish progress */ - s->common.offset += n; + block_job_progress_update(&s->common, n); if (copy && s->common.speed) { delay_ns = ratelimit_calculate_delay(&s->limit, n); diff --git a/block/mirror.c b/block/mirror.c index 99da9c0858..56a7ce2f55 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -121,7 +121,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret) bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); } if (!s->initial_zeroing_ongoing) { - s->common.offset += op->bytes; + block_job_progress_update(&s->common, op->bytes); } } qemu_iovec_destroy(&op->qiov); @@ -792,11 +792,10 @@ static void coroutine_fn mirror_run(void *opaque) block_job_pause_point(&s->common); cnt = bdrv_get_dirty_count(s->dirty_bitmap); - /* s->common.offset contains the number of bytes already processed so - * far, cnt is the number of dirty bytes remaining and - * s->bytes_in_flight is the number of bytes currently being - * processed; together those are the current total operation length */ - s->common.len = s->common.offset + s->bytes_in_flight + cnt; + /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is + * the number of bytes currently being processed; together those are + * the current remaining operation length */ + block_job_progress_set_remaining(&s->common, s->bytes_in_flight + cnt); /* Note that even when no rate limit is applied we need to yield * periodically with no pending I/O so that bdrv_drain_all() returns. diff --git a/block/stream.c b/block/stream.c index 1a85708fcf..8369852bda 100644 --- a/block/stream.c +++ b/block/stream.c @@ -107,6 +107,7 @@ static void coroutine_fn stream_run(void *opaque) BlockBackend *blk = s->common.blk; BlockDriverState *bs = blk_bs(blk); BlockDriverState *base = s->base; + int64_t len; int64_t offset = 0; uint64_t delay_ns = 0; int error = 0; @@ -118,11 +119,12 @@ static void coroutine_fn stream_run(void *opaque) goto out; } - s->common.len = bdrv_getlength(bs); - if (s->common.len < 0) { - ret = s->common.len; + len = bdrv_getlength(bs); + if (len < 0) { + ret = len; goto out; } + block_job_progress_set_remaining(&s->common, len); buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); @@ -135,7 +137,7 @@ static void coroutine_fn stream_run(void *opaque) bdrv_enable_copy_on_read(bs); } - for ( ; offset < s->common.len; offset += n) { + for ( ; offset < len; offset += n) { bool copy; /* Note that even when no rate limit is applied we need to yield @@ -159,7 +161,7 @@ static void coroutine_fn stream_run(void *opaque) /* Finish early if end of backing file has been reached */ if (ret == 0 && n == 0) { - n = s->common.len - offset; + n = len - offset; } copy = (ret == 1); @@ -185,7 +187,7 @@ static void coroutine_fn stream_run(void *opaque) ret = 0; /* Publish progress */ - s->common.offset += n; + block_job_progress_update(&s->common, n); if (copy && s->common.speed) { delay_ns = ratelimit_calculate_delay(&s->limit, n); } else { diff --git a/blockjob.c b/blockjob.c index 0ebc5ab178..ebc26a5245 100644 --- a/blockjob.c +++ b/blockjob.c @@ -810,6 +810,16 @@ int block_job_complete_sync(BlockJob *job, Error **errp) return block_job_finish_sync(job, &block_job_complete, errp); } +void block_job_progress_update(BlockJob *job, uint64_t done) +{ + job->offset += done; +} + +void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining) +{ + job->len = job->offset + remaining; +} + BlockJobInfo *block_job_query(BlockJob *job, Error **errp) { BlockJobInfo *info; diff --git a/include/block/blockjob.h b/include/block/blockjob.h index fc645dac68..a2cc52233b 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -277,6 +277,25 @@ void block_job_finalize(BlockJob *job, Error **errp); */ void block_job_dismiss(BlockJob **job, Error **errp); +/** + * block_job_progress_update: + * @job: The job that has made progress + * @done: How much progress the job made + * + * Updates the progress counter of the job. + */ +void block_job_progress_update(BlockJob *job, uint64_t done); + +/** + * block_job_progress_set_remaining: + * @job: The job whose expected progress end value is set + * @remaining: Expected end value of the progress counter of the job + * + * Sets the expected end value of the progress counter of a job so that a + * completion percentage can be calculated when the progress is updated. + */ +void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); + /** * block_job_query: * @job: The job to get information about. From f05fee508f538ca262d2ab19bcd8772196efe848 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 18 Jan 2018 20:20:24 +0100 Subject: [PATCH 0470/2380] blockjob: Move RateLimit to BlockJob Every block job has a RateLimit, and they all do the exact same thing with it, so it should be common infrastructure. Move the struct field for a start. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 5 ++--- block/commit.c | 5 ++--- block/mirror.c | 6 +++--- block/stream.c | 5 ++--- include/block/blockjob.h | 4 ++++ 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/block/backup.c b/block/backup.c index 5d95805472..7585c4391e 100644 --- a/block/backup.c +++ b/block/backup.c @@ -35,7 +35,6 @@ typedef struct BackupBlockJob { /* bitmap for sync=incremental */ BdrvDirtyBitmap *sync_bitmap; MirrorSyncMode sync_mode; - RateLimit limit; BlockdevOnError on_source_error; BlockdevOnError on_target_error; CoRwlock flush_rwlock; @@ -199,7 +198,7 @@ static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) error_setg(errp, QERR_INVALID_PARAMETER, "speed"); return; } - ratelimit_set_speed(&s->limit, speed, SLICE_TIME); + ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); } static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) @@ -346,7 +345,7 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) * (without, VM does not reboot) */ if (job->common.speed) { - uint64_t delay_ns = ratelimit_calculate_delay(&job->limit, + uint64_t delay_ns = ratelimit_calculate_delay(&job->common.limit, job->bytes_read); job->bytes_read = 0; block_job_sleep_ns(&job->common, delay_ns); diff --git a/block/commit.c b/block/commit.c index 50b191c980..beec5d0ad6 100644 --- a/block/commit.c +++ b/block/commit.c @@ -35,7 +35,6 @@ enum { typedef struct CommitBlockJob { BlockJob common; - RateLimit limit; BlockDriverState *commit_top_bs; BlockBackend *top; BlockBackend *base; @@ -201,7 +200,7 @@ static void coroutine_fn commit_run(void *opaque) block_job_progress_update(&s->common, n); if (copy && s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->limit, n); + delay_ns = ratelimit_calculate_delay(&s->common.limit, n); } else { delay_ns = 0; } @@ -225,7 +224,7 @@ static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) error_setg(errp, QERR_INVALID_PARAMETER, "speed"); return; } - ratelimit_set_speed(&s->limit, speed, SLICE_TIME); + ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); } static const BlockJobDriver commit_job_driver = { diff --git a/block/mirror.c b/block/mirror.c index 56a7ce2f55..702c139368 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -36,7 +36,6 @@ typedef struct MirrorBuffer { typedef struct MirrorBlockJob { BlockJob common; - RateLimit limit; BlockBackend *target; BlockDriverState *mirror_top_bs; BlockDriverState *source; @@ -450,7 +449,8 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) offset += io_bytes; nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity); if (s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->limit, io_bytes_acct); + delay_ns = ratelimit_calculate_delay(&s->common.limit, + io_bytes_acct); } } return delay_ns; @@ -916,7 +916,7 @@ static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp) error_setg(errp, QERR_INVALID_PARAMETER, "speed"); return; } - ratelimit_set_speed(&s->limit, speed, SLICE_TIME); + ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); } static void mirror_complete(BlockJob *job, Error **errp) diff --git a/block/stream.c b/block/stream.c index 8369852bda..a1d4768c2e 100644 --- a/block/stream.c +++ b/block/stream.c @@ -33,7 +33,6 @@ enum { typedef struct StreamBlockJob { BlockJob common; - RateLimit limit; BlockDriverState *base; BlockdevOnError on_error; char *backing_file_str; @@ -189,7 +188,7 @@ static void coroutine_fn stream_run(void *opaque) /* Publish progress */ block_job_progress_update(&s->common, n); if (copy && s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->limit, n); + delay_ns = ratelimit_calculate_delay(&s->common.limit, n); } else { delay_ns = 0; } @@ -219,7 +218,7 @@ static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) error_setg(errp, QERR_INVALID_PARAMETER, "speed"); return; } - ratelimit_set_speed(&s->limit, speed, SLICE_TIME); + ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); } static const BlockJobDriver stream_job_driver = { diff --git a/include/block/blockjob.h b/include/block/blockjob.h index a2cc52233b..22bf418209 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -27,6 +27,7 @@ #define BLOCKJOB_H #include "block/block.h" +#include "qemu/ratelimit.h" typedef struct BlockJobDriver BlockJobDriver; typedef struct BlockJobTxn BlockJobTxn; @@ -118,6 +119,9 @@ typedef struct BlockJob { /** Speed that was set with @block_job_set_speed. */ int64_t speed; + /** Rate limiting data structure for implementing @speed. */ + RateLimit limit; + /** The completion function that will be called when the job completes. */ BlockCompletionFunc *cb; From 18bb69287ea522ab696e1bea818b93e5eaa85745 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 18 Jan 2018 20:25:40 +0100 Subject: [PATCH 0471/2380] blockjob: Implement block_job_set_speed() centrally All block job drivers support .set_speed and all of them duplicate the same code to implement it. Move that code to blockjob.c and remove the now useless callback. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 13 ------------- block/commit.c | 14 -------------- block/mirror.c | 26 ++++++-------------------- block/stream.c | 14 -------------- blockjob.c | 12 ++++-------- include/block/blockjob.h | 2 ++ include/block/blockjob_int.h | 3 --- 7 files changed, 12 insertions(+), 72 deletions(-) diff --git a/block/backup.c b/block/backup.c index 7585c4391e..8468fd9f94 100644 --- a/block/backup.c +++ b/block/backup.c @@ -27,7 +27,6 @@ #include "qemu/error-report.h" #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) -#define SLICE_TIME 100000000ULL /* ns */ typedef struct BackupBlockJob { BlockJob common; @@ -190,17 +189,6 @@ static int coroutine_fn backup_before_write_notify( return backup_do_cow(job, req->offset, req->bytes, NULL, true); } -static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) -{ - BackupBlockJob *s = container_of(job, BackupBlockJob, common); - - if (speed < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, "speed"); - return; - } - ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); -} - static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) { BdrvDirtyBitmap *bm; @@ -540,7 +528,6 @@ static const BlockJobDriver backup_job_driver = { .instance_size = sizeof(BackupBlockJob), .job_type = BLOCK_JOB_TYPE_BACKUP, .start = backup_run, - .set_speed = backup_set_speed, .commit = backup_commit, .abort = backup_abort, .clean = backup_clean, diff --git a/block/commit.c b/block/commit.c index beec5d0ad6..46cbeaec3e 100644 --- a/block/commit.c +++ b/block/commit.c @@ -31,8 +31,6 @@ enum { COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ }; -#define SLICE_TIME 100000000ULL /* ns */ - typedef struct CommitBlockJob { BlockJob common; BlockDriverState *commit_top_bs; @@ -216,21 +214,9 @@ out: block_job_defer_to_main_loop(&s->common, commit_complete, data); } -static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) -{ - CommitBlockJob *s = container_of(job, CommitBlockJob, common); - - if (speed < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, "speed"); - return; - } - ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); -} - static const BlockJobDriver commit_job_driver = { .instance_size = sizeof(CommitBlockJob), .job_type = BLOCK_JOB_TYPE_COMMIT, - .set_speed = commit_set_speed, .start = commit_run, }; diff --git a/block/mirror.c b/block/mirror.c index 702c139368..6955d68804 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -22,7 +22,6 @@ #include "qemu/ratelimit.h" #include "qemu/bitmap.h" -#define SLICE_TIME 100000000ULL /* ns */ #define MAX_IN_FLIGHT 16 #define MAX_IO_BYTES (1 << 20) /* 1 Mb */ #define DEFAULT_MIRROR_BUF_SIZE (MAX_IN_FLIGHT * MAX_IO_BYTES) @@ -596,7 +595,7 @@ static void mirror_throttle(MirrorBlockJob *s) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - if (now - s->last_pause_ns > SLICE_TIME) { + if (now - s->last_pause_ns > BLOCK_JOB_SLICE_TIME) { s->last_pause_ns = now; block_job_sleep_ns(&s->common, 0); } else { @@ -799,11 +798,10 @@ static void coroutine_fn mirror_run(void *opaque) /* Note that even when no rate limit is applied we need to yield * periodically with no pending I/O so that bdrv_drain_all() returns. - * We do so every SLICE_TIME nanoseconds, or when there is an error, - * or when the source is clean, whichever comes first. - */ + * We do so every BLKOCK_JOB_SLICE_TIME nanoseconds, or when there is + * an error, or when the source is clean, whichever comes first. */ delta = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->last_pause_ns; - if (delta < SLICE_TIME && + if (delta < BLOCK_JOB_SLICE_TIME && s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) { if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || (cnt == 0 && s->in_flight > 0)) { @@ -869,7 +867,8 @@ static void coroutine_fn mirror_run(void *opaque) ret = 0; if (s->synced && !should_complete) { - delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); + delay_ns = (s->in_flight == 0 && + cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0); } trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); block_job_sleep_ns(&s->common, delay_ns); @@ -908,17 +907,6 @@ immediate_exit: block_job_defer_to_main_loop(&s->common, mirror_exit, data); } -static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp) -{ - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); - - if (speed < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, "speed"); - return; - } - ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); -} - static void mirror_complete(BlockJob *job, Error **errp) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); @@ -1003,7 +991,6 @@ static void mirror_drain(BlockJob *job) static const BlockJobDriver mirror_job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = BLOCK_JOB_TYPE_MIRROR, - .set_speed = mirror_set_speed, .start = mirror_run, .complete = mirror_complete, .pause = mirror_pause, @@ -1014,7 +1001,6 @@ static const BlockJobDriver mirror_job_driver = { static const BlockJobDriver commit_active_job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = BLOCK_JOB_TYPE_COMMIT, - .set_speed = mirror_set_speed, .start = mirror_run, .complete = mirror_complete, .pause = mirror_pause, diff --git a/block/stream.c b/block/stream.c index a1d4768c2e..797d7c4f21 100644 --- a/block/stream.c +++ b/block/stream.c @@ -29,8 +29,6 @@ enum { STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */ }; -#define SLICE_TIME 100000000ULL /* ns */ - typedef struct StreamBlockJob { BlockJob common; BlockDriverState *base; @@ -210,21 +208,9 @@ out: block_job_defer_to_main_loop(&s->common, stream_complete, data); } -static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) -{ - StreamBlockJob *s = container_of(job, StreamBlockJob, common); - - if (speed < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, "speed"); - return; - } - ratelimit_set_speed(&s->common.limit, speed, SLICE_TIME); -} - static const BlockJobDriver stream_job_driver = { .instance_size = sizeof(StreamBlockJob), .job_type = BLOCK_JOB_TYPE_STREAM, - .set_speed = stream_set_speed, .start = stream_run, }; diff --git a/blockjob.c b/blockjob.c index ebc26a5245..2f4b768338 100644 --- a/blockjob.c +++ b/blockjob.c @@ -659,22 +659,18 @@ static bool block_job_timer_pending(BlockJob *job) void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { - Error *local_err = NULL; int64_t old_speed = job->speed; - if (!job->driver->set_speed) { - error_setg(errp, QERR_UNSUPPORTED); - return; - } if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) { return; } - job->driver->set_speed(job, speed, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (speed < 0) { + error_setg(errp, QERR_INVALID_PARAMETER, "speed"); return; } + ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME); + job->speed = speed; if (speed && speed <= old_speed) { return; diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 22bf418209..82f52f4b14 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -29,6 +29,8 @@ #include "block/block.h" #include "qemu/ratelimit.h" +#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ + typedef struct BlockJobDriver BlockJobDriver; typedef struct BlockJobTxn BlockJobTxn; diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index d5a515de9b..ad510d5a0c 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -41,9 +41,6 @@ struct BlockJobDriver { /** String describing the operation, part of query-block-jobs QMP API */ BlockJobType job_type; - /** Optional callback for job types that support setting a speed limit */ - void (*set_speed)(BlockJob *job, int64_t speed, Error **errp); - /** Mandatory: Entrypoint for the Coroutine. */ CoroutineEntry *start; From dee81d5111ff0e24ac63ab0dbbd19e84c2f87904 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 18 Jan 2018 21:19:38 +0100 Subject: [PATCH 0472/2380] blockjob: Introduce block_job_ratelimit_get_delay() This gets us rid of more direct accesses to BlockJob fields from the job drivers. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 18 +++++++----------- block/commit.c | 4 ++-- block/mirror.c | 5 +---- block/stream.c | 4 ++-- blockjob.c | 9 +++++++++ include/block/blockjob_int.h | 8 ++++++++ 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/block/backup.c b/block/backup.c index 8468fd9f94..cfdb6ecdf5 100644 --- a/block/backup.c +++ b/block/backup.c @@ -325,21 +325,17 @@ static void backup_complete(BlockJob *job, void *opaque) static bool coroutine_fn yield_and_check(BackupBlockJob *job) { + uint64_t delay_ns; + if (block_job_is_cancelled(&job->common)) { return true; } - /* we need to yield so that bdrv_drain_all() returns. - * (without, VM does not reboot) - */ - if (job->common.speed) { - uint64_t delay_ns = ratelimit_calculate_delay(&job->common.limit, - job->bytes_read); - job->bytes_read = 0; - block_job_sleep_ns(&job->common, delay_ns); - } else { - block_job_sleep_ns(&job->common, 0); - } + /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can + * return. Without a yield, the VM would not reboot. */ + delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); + job->bytes_read = 0; + block_job_sleep_ns(&job->common, delay_ns); if (block_job_is_cancelled(&job->common)) { return true; diff --git a/block/commit.c b/block/commit.c index 46cbeaec3e..ba5df6aa0a 100644 --- a/block/commit.c +++ b/block/commit.c @@ -197,8 +197,8 @@ static void coroutine_fn commit_run(void *opaque) /* Publish progress */ block_job_progress_update(&s->common, n); - if (copy && s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->common.limit, n); + if (copy) { + delay_ns = block_job_ratelimit_get_delay(&s->common, n); } else { delay_ns = 0; } diff --git a/block/mirror.c b/block/mirror.c index 6955d68804..6aa38db114 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -447,10 +447,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) assert(io_bytes); offset += io_bytes; nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity); - if (s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->common.limit, - io_bytes_acct); - } + delay_ns = block_job_ratelimit_get_delay(&s->common, io_bytes_acct); } return delay_ns; } diff --git a/block/stream.c b/block/stream.c index 797d7c4f21..df9660d2fc 100644 --- a/block/stream.c +++ b/block/stream.c @@ -185,8 +185,8 @@ static void coroutine_fn stream_run(void *opaque) /* Publish progress */ block_job_progress_update(&s->common, n); - if (copy && s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->common.limit, n); + if (copy) { + delay_ns = block_job_ratelimit_get_delay(&s->common, n); } else { delay_ns = 0; } diff --git a/blockjob.c b/blockjob.c index 2f4b768338..04416f11cd 100644 --- a/blockjob.c +++ b/blockjob.c @@ -680,6 +680,15 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) block_job_enter_cond(job, block_job_timer_pending); } +int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) +{ + if (!job->speed) { + return 0; + } + + return ratelimit_calculate_delay(&job->limit, n); +} + void block_job_complete(BlockJob *job, Error **errp) { /* Should not be reachable via external interface for internal jobs */ diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index ad510d5a0c..62ec964d09 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -165,6 +165,14 @@ void block_job_sleep_ns(BlockJob *job, int64_t ns); */ void block_job_yield(BlockJob *job); +/** + * block_job_ratelimit_get_delay: + * + * Calculate and return delay for the next request in ns. See the documentation + * of ratelimit_calculate_delay() for details. + */ +int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); + /** * block_job_early_fail: * @bs: The block device. From bd21935b50d100d8da8c05cd3c2009f0f3432cb4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 19 Jan 2018 15:54:40 +0100 Subject: [PATCH 0473/2380] blockjob: Add block_job_driver() The backup block job directly accesses the driver field in BlockJob. Add a wrapper for getting it. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 8 +++++--- blockjob.c | 5 +++++ include/block/blockjob.h | 7 +++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/block/backup.c b/block/backup.c index cfdb6ecdf5..e14d99560d 100644 --- a/block/backup.c +++ b/block/backup.c @@ -47,6 +47,8 @@ typedef struct BackupBlockJob { HBitmap *copy_bitmap; } BackupBlockJob; +static const BlockJobDriver backup_job_driver; + /* See if in-flight requests overlap and wait for them to complete */ static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, int64_t start, @@ -241,7 +243,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); int64_t len; - assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); + assert(block_job_driver(job) == &backup_job_driver); if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) { error_setg(errp, "The backup job only supports block checkpoint in" @@ -259,7 +261,7 @@ void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset, BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); int64_t start, end; - assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); + assert(block_job_driver(job) == &backup_job_driver); start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); @@ -272,7 +274,7 @@ void backup_cow_request_begin(CowRequest *req, BlockJob *job, BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); int64_t start, end; - assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); + assert(block_job_driver(job) == &backup_job_driver); start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); diff --git a/blockjob.c b/blockjob.c index 04416f11cd..36c5fdeb2f 100644 --- a/blockjob.c +++ b/blockjob.c @@ -359,6 +359,11 @@ static bool block_job_started(BlockJob *job) return job->co; } +const BlockJobDriver *block_job_driver(BlockJob *job) +{ + return job->driver; +} + /** * All jobs must allow a pause point before entering their job proper. This * ensures that jobs can be paused prior to being started, then resumed later. diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 82f52f4b14..0f56f723de 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -452,4 +452,11 @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job); */ bool block_job_is_internal(BlockJob *job); +/** + * block_job_driver: + * + * Returns the driver associated with a block job. + */ +const BlockJobDriver *block_job_driver(BlockJob *job); + #endif From 6cba5377f54d7ea859a29c1877785e7101794683 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 6 Apr 2018 18:41:08 +0200 Subject: [PATCH 0474/2380] iotests: Split 214 off of 122 Commit abd3622cc03cf41ed542126a540385f30a4c0175 added a case to 122 regarding how the qcow2 driver handles an incorrect compressed data length value. This does not really fit into 122, as that file is supposed to contain qemu-img convert test cases, which this case is not. So this patch splits it off into its own file; maybe we will even get more qcow2-only compression tests in the future. Also, that test case does not work with refcount_bits=1, so mark that option as unsupported. Signed-off-by: Max Reitz Message-id: 20180406164108.26118-1-mreitz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Alberto Garcia Signed-off-by: Max Reitz --- tests/qemu-iotests/122 | 47 ------------------ tests/qemu-iotests/122.out | 33 ------------- tests/qemu-iotests/214 | 97 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/214.out | 35 ++++++++++++++ tests/qemu-iotests/group | 1 + 5 files changed, 133 insertions(+), 80 deletions(-) create mode 100755 tests/qemu-iotests/214 create mode 100644 tests/qemu-iotests/214.out diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122 index 6cf4fcb866..45b359c2ba 100755 --- a/tests/qemu-iotests/122 +++ b/tests/qemu-iotests/122 @@ -129,53 +129,6 @@ $QEMU_IO -c "read -P 0x44 1023k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _fil $QEMU_IO -c "read -P 0 1024k 1022k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir -echo -echo "=== Corrupted size field in compressed cluster descriptor ===" -echo -# Create an empty image and fill half of it with compressed data. -# The L2 entries of the two compressed clusters are located at -# 0x800000 and 0x800008, their original values are 0x4008000000a00000 -# and 0x4008000000a00802 (5 sectors for compressed data each). -_make_test_img 8M -o cluster_size=2M -$QEMU_IO -c "write -c -P 0x11 0 2M" -c "write -c -P 0x11 2M 2M" "$TEST_IMG" \ - 2>&1 | _filter_qemu_io | _filter_testdir - -# Reduce size of compressed data to 4 sectors: this corrupts the image. -poke_file "$TEST_IMG" $((0x800000)) "\x40\x06" -$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir - -# 'qemu-img check' however doesn't see anything wrong because it -# doesn't try to decompress the data and the refcounts are consistent. -# TODO: update qemu-img so this can be detected. -_check_test_img - -# Increase size of compressed data to the maximum (8192 sectors). -# This makes QEMU read more data (8192 sectors instead of 5, host -# addresses [0xa00000, 0xdfffff]), but the decompression algorithm -# stops once we have enough to restore the uncompressed cluster, so -# the rest of the data is ignored. -poke_file "$TEST_IMG" $((0x800000)) "\x7f\xfe" -# Do it also for the second compressed cluster (L2 entry at 0x800008). -# In this case the compressed data would span 3 host clusters -# (host addresses: [0xa00802, 0xe00801]) -poke_file "$TEST_IMG" $((0x800008)) "\x7f\xfe" - -# Here the image is too small so we're asking QEMU to read beyond the -# end of the image. -$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir -# But if we grow the image we won't be reading beyond its end anymore. -$QEMU_IO -c "write -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir -$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir - -# The refcount data is however wrong because due to the increased size -# of the compressed data it now reaches the following host clusters. -# This can be repaired by qemu-img check by increasing the refcount of -# those clusters. -# TODO: update qemu-img to correct the compressed cluster size instead. -_check_test_img -r all -$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir -$QEMU_IO -c "read -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir - echo echo "=== Full allocation with -S 0 ===" echo diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out index a6b7fe007e..47d8656db8 100644 --- a/tests/qemu-iotests/122.out +++ b/tests/qemu-iotests/122.out @@ -99,39 +99,6 @@ read 1024/1024 bytes at offset 1047552 read 1046528/1046528 bytes at offset 1048576 1022 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -=== Corrupted size field in compressed cluster descriptor === - -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8388608 -wrote 2097152/2097152 bytes at offset 0 -2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 2097152/2097152 bytes at offset 2097152 -2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read failed: Input/output error -No errors were found on the image. -read 4194304/4194304 bytes at offset 0 -4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 4194304/4194304 bytes at offset 4194304 -4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4194304/4194304 bytes at offset 0 -4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -ERROR cluster 6 refcount=1 reference=3 -ERROR cluster 7 refcount=1 reference=2 -Repairing cluster 6 refcount=1 reference=3 -Repairing cluster 7 refcount=1 reference=2 -Repairing OFLAG_COPIED data cluster: l2_entry=8000000000c00000 refcount=3 -Repairing OFLAG_COPIED data cluster: l2_entry=8000000000e00000 refcount=2 -The following inconsistencies were found and repaired: - - 0 leaked clusters - 4 corruptions - -Double checking the fixed image now... -No errors were found on the image. -read 4194304/4194304 bytes at offset 0 -4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4194304/4194304 bytes at offset 4194304 -4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - === Full allocation with -S 0 === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 diff --git a/tests/qemu-iotests/214 b/tests/qemu-iotests/214 new file mode 100755 index 0000000000..c46ca2a6dd --- /dev/null +++ b/tests/qemu-iotests/214 @@ -0,0 +1,97 @@ +#!/bin/bash +# +# Test qcow2 image compression +# +# Copyright (C) 2018 Igalia, S.L. +# Author: Alberto Garcia +# +# 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 . +# + +seq=$(basename "$0") +echo "QA output created by $seq" + +here=$PWD +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +# Repairing the corrupted image requires qemu-img check to store a +# refcount up to 3, which requires at least two refcount bits. +_unsupported_imgopts 'refcount_bits=1[^0-9]' + + +echo +echo "=== Corrupted size field in compressed cluster descriptor ===" +echo +# Create an empty image and fill half of it with compressed data. +# The L2 entries of the two compressed clusters are located at +# 0x800000 and 0x800008, their original values are 0x4008000000a00000 +# and 0x4008000000a00802 (5 sectors for compressed data each). +_make_test_img 8M -o cluster_size=2M +$QEMU_IO -c "write -c -P 0x11 0 2M" -c "write -c -P 0x11 2M 2M" "$TEST_IMG" \ + 2>&1 | _filter_qemu_io | _filter_testdir + +# Reduce size of compressed data to 4 sectors: this corrupts the image. +poke_file "$TEST_IMG" $((0x800000)) "\x40\x06" +$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +# 'qemu-img check' however doesn't see anything wrong because it +# doesn't try to decompress the data and the refcounts are consistent. +# TODO: update qemu-img so this can be detected. +_check_test_img + +# Increase size of compressed data to the maximum (8192 sectors). +# This makes QEMU read more data (8192 sectors instead of 5, host +# addresses [0xa00000, 0xdfffff]), but the decompression algorithm +# stops once we have enough to restore the uncompressed cluster, so +# the rest of the data is ignored. +poke_file "$TEST_IMG" $((0x800000)) "\x7f\xfe" +# Do it also for the second compressed cluster (L2 entry at 0x800008). +# In this case the compressed data would span 3 host clusters +# (host addresses: [0xa00802, 0xe00801]) +poke_file "$TEST_IMG" $((0x800008)) "\x7f\xfe" + +# Here the image is too small so we're asking QEMU to read beyond the +# end of the image. +$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +# But if we grow the image we won't be reading beyond its end anymore. +$QEMU_IO -c "write -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +# The refcount data is however wrong because due to the increased size +# of the compressed data it now reaches the following host clusters. +# This can be repaired by qemu-img check by increasing the refcount of +# those clusters. +# TODO: update qemu-img to correct the compressed cluster size instead. +_check_test_img -r all +$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +$QEMU_IO -c "read -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/214.out b/tests/qemu-iotests/214.out new file mode 100644 index 0000000000..0fcd8dc051 --- /dev/null +++ b/tests/qemu-iotests/214.out @@ -0,0 +1,35 @@ +QA output created by 214 + +=== Corrupted size field in compressed cluster descriptor === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8388608 +wrote 2097152/2097152 bytes at offset 0 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 2097152 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read failed: Input/output error +No errors were found on the image. +read 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 4194304/4194304 bytes at offset 4194304 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +ERROR cluster 6 refcount=1 reference=3 +ERROR cluster 7 refcount=1 reference=2 +Repairing cluster 6 refcount=1 reference=3 +Repairing cluster 7 refcount=1 reference=2 +Repairing OFLAG_COPIED data cluster: l2_entry=8000000000c00000 refcount=3 +Repairing OFLAG_COPIED data cluster: l2_entry=8000000000e00000 refcount=2 +The following inconsistencies were found and repaired: + + 0 leaked clusters + 4 corruptions + +Double checking the fixed image now... +No errors were found on the image. +read 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4194304/4194304 bytes at offset 4194304 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 5daef24020..aed024af05 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -212,4 +212,5 @@ 211 rw auto quick 212 rw auto quick 213 rw auto quick +214 rw auto 218 rw auto quick From 74c44a59348f7fac96c32621e37ee636546f26f8 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Tue, 10 Apr 2018 18:05:03 +0200 Subject: [PATCH 0475/2380] Fix error message about compressed clusters with OFLAG_COPIED Compressed clusters are not supposed to have the COPIED bit set. "qemu-img check" detects that and prints an error message reporting the number of the affected host cluster. This doesn't make much sense because compressed clusters are not aligned to host clusters, so it would be better to report the offset instead. Plus, the calculation is wrong and it uses the raw L2 entry as if it was simply an offset. This patch fixes the error message and reports the offset of the compressed cluster. Signed-off-by: Alberto Garcia Message-id: 0f687957feb72e80c740403191a47e607c2463fe.1523376013.git.berto@igalia.com Signed-off-by: Max Reitz --- block/qcow2-refcount.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 6b8b63514a..2dc23005b7 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1577,9 +1577,9 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, case QCOW2_CLUSTER_COMPRESSED: /* Compressed clusters don't have QCOW_OFLAG_COPIED */ if (l2_entry & QCOW_OFLAG_COPIED) { - fprintf(stderr, "ERROR: cluster %" PRId64 ": " + fprintf(stderr, "ERROR: coffset=0x%" PRIx64 ": " "copied flag must never be set for compressed " - "clusters\n", l2_entry >> s->cluster_bits); + "clusters\n", l2_entry & s->cluster_offset_mask); l2_entry &= ~QCOW_OFLAG_COPIED; res->corruptions++; } From 3c7d14b201ee4eeec2ca259b5a071a4599aa8847 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Tue, 10 Apr 2018 18:05:04 +0200 Subject: [PATCH 0476/2380] specs/qcow2: Clarify that compressed clusters have the COPIED bit reset Compressed clusters are not supposed to have the COPIED bit set, but this is not made explicit in the specs, so let's document it. Signed-off-by: Alberto Garcia Message-id: 74552e1d6e858d3159cb0c0e188e80bc9248e337.1523376013.git.berto@igalia.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- docs/interop/qcow2.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt index feb711fb6a..8e1547ded2 100644 --- a/docs/interop/qcow2.txt +++ b/docs/interop/qcow2.txt @@ -400,10 +400,10 @@ L2 table entry: 62: 0 for standard clusters 1 for compressed clusters - 63: 0 for a cluster that is unused or requires COW, 1 if its - refcount is exactly one. This information is only accurate - in L2 tables that are reachable from the active L1 - table. + 63: 0 for clusters that are unused, compressed or require COW. + 1 for standard clusters whose refcount is exactly one. + This information is only accurate in L2 tables + that are reachable from the active L1 table. Standard Cluster Descriptor: From 52253998ec3e523c9e20ae81e2a6431d8ff733ba Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Tue, 17 Apr 2018 15:37:04 +0300 Subject: [PATCH 0477/2380] qcow2: Give the refcount cache the minimum possible size by default The L2 and refcount caches have default sizes that can be overridden using the l2-cache-size and refcount-cache-size (an additional parameter named cache-size sets the combined size of both caches). Unless forced by one of the aforementioned parameters, QEMU will set the unspecified sizes so that the L2 cache is 4 times larger than the refcount cache. This is based on the premise that the refcount metadata needs to be only a fourth of the L2 metadata to cover the same amount of disk space. This is incorrect for two reasons: a) The amount of disk covered by an L2 table depends solely on the cluster size, but in the case of a refcount block it depends on the cluster size *and* the width of each refcount entry. The 4/1 ratio is only valid with 16-bit entries (the default). b) When we talk about disk space and L2 tables we are talking about guest space (L2 tables map guest clusters to host clusters), whereas refcount blocks are used for host clusters (including L1/L2 tables and the refcount blocks themselves). On a fully populated (and uncompressed) qcow2 file, image size > virtual size so there are more refcount entries than L2 entries. Problem (a) could be fixed by adjusting the algorithm to take into account the refcount entry width. Problem (b) could be fixed by increasing a bit the refcount cache size to account for the clusters used for qcow2 metadata. However this patch takes a completely different approach and instead of keeping a ratio between both cache sizes it assigns as much as possible to the L2 cache and the remainder to the refcount cache. The reason is that L2 tables are used for every single I/O request from the guest and the effect of increasing the cache is significant and clearly measurable. Refcount blocks are however only used for cluster allocation and internal snapshots and in practice are accessed sequentially in most cases, so the effect of increasing the cache is negligible (even when doing random writes from the guest). So, make the refcount cache as small as possible unless the user explicitly asks for a larger one. Signed-off-by: Alberto Garcia Reviewed-by: Eric Blake Reviewed-by: Max Reitz Message-id: 9695182c2eb11b77cb319689a1ebaa4e7c9d6591.1523968389.git.berto@igalia.com Signed-off-by: Max Reitz --- block/qcow2.c | 31 +++++++++++++++++++------------ block/qcow2.h | 4 ---- tests/qemu-iotests/137.out | 2 +- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 2f36e632f9..6d532470a8 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -802,23 +802,30 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, } else if (refcount_cache_size_set) { *l2_cache_size = combined_cache_size - *refcount_cache_size; } else { - *refcount_cache_size = combined_cache_size - / (DEFAULT_L2_REFCOUNT_SIZE_RATIO + 1); - *l2_cache_size = combined_cache_size - *refcount_cache_size; + uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; + uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); + uint64_t min_refcount_cache = + (uint64_t) MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; + + /* Assign as much memory as possible to the L2 cache, and + * use the remainder for the refcount cache */ + if (combined_cache_size >= max_l2_cache + min_refcount_cache) { + *l2_cache_size = max_l2_cache; + *refcount_cache_size = combined_cache_size - *l2_cache_size; + } else { + *refcount_cache_size = + MIN(combined_cache_size, min_refcount_cache); + *l2_cache_size = combined_cache_size - *refcount_cache_size; + } } } else { - if (!l2_cache_size_set && !refcount_cache_size_set) { + if (!l2_cache_size_set) { *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) { - *l2_cache_size = *refcount_cache_size - * DEFAULT_L2_REFCOUNT_SIZE_RATIO; - } else if (!refcount_cache_size_set) { - *refcount_cache_size = *l2_cache_size - / DEFAULT_L2_REFCOUNT_SIZE_RATIO; + } + if (!refcount_cache_size_set) { + *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; } } diff --git a/block/qcow2.h b/block/qcow2.h index adf5c3950f..01b5250415 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -77,10 +77,6 @@ #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 - * clusters */ -#define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4 - #define DEFAULT_CLUSTER_SIZE 65536 diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out index e28e1eadba..96724a6c33 100644 --- a/tests/qemu-iotests/137.out +++ b/tests/qemu-iotests/137.out @@ -22,7 +22,7 @@ refcount-cache-size may not exceed cache-size L2 cache size too big L2 cache entry size must be a power of two between 512 and the cluster size (65536) L2 cache entry size must be a power of two between 512 and the cluster size (65536) -L2 cache size too big +Refcount cache size too big Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all') Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all From 603790ef3aec6a19b1c095188a1d2171934a27de Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Tue, 17 Apr 2018 15:37:05 +0300 Subject: [PATCH 0478/2380] docs: Document the new default sizes of the qcow2 caches We have just reduced the refcount cache size to the minimum unless the user explicitly requests a larger one, so we have to update the documentation to reflect this change. Signed-off-by: Alberto Garcia Message-id: c5f0bde23558dd9d33b21fffc76ac9953cc19c56.1523968389.git.berto@igalia.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- docs/qcow2-cache.txt | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/qcow2-cache.txt b/docs/qcow2-cache.txt index 170191a242..8a09a5cc5f 100644 --- a/docs/qcow2-cache.txt +++ b/docs/qcow2-cache.txt @@ -116,31 +116,30 @@ There are three options available, and all of them take bytes: "refcount-cache-size": maximum size of the refcount block cache "cache-size": maximum size of both caches combined -There are two things that need to be taken into account: +There are a few things that need to be taken into account: - Both caches must have a size that is a multiple of the cluster size (or the cache entry size: see "Using smaller cache sizes" below). - - If you only set one of the options above, QEMU will automatically - adjust the others so that the L2 cache is 4 times bigger than the - refcount cache. + - The default L2 cache size is 8 clusters or 1MB (whichever is more), + and the minimum is 2 clusters (or 2 cache entries, see below). -This means that these options are equivalent: + - The default (and minimum) refcount cache size is 4 clusters. - -drive file=hd.qcow2,l2-cache-size=2097152 - -drive file=hd.qcow2,refcount-cache-size=524288 - -drive file=hd.qcow2,cache-size=2621440 + - If only "cache-size" is specified then QEMU will assign as much + memory as possible to the L2 cache before increasing the refcount + cache size. -The reason for this 1/4 ratio is to ensure that both caches cover the -same amount of disk space. Note however that this is only valid with -the default value of refcount_bits (16). If you are using a different -value you might want to calculate both cache sizes yourself since QEMU -will always use the same 1/4 ratio. +Unlike L2 tables, refcount blocks are not used during normal I/O but +only during allocations and internal snapshots. In most cases they are +accessed sequentially (even during random guest I/O) so increasing the +refcount cache size won't have any measurable effect in performance +(this can change if you are using internal snapshots, so you may want +to think about increasing the cache size if you use them heavily). -It's also worth mentioning that there's no strict need for both caches -to cover the same amount of disk space. The refcount cache is used -much less often than the L2 cache, so it's perfectly reasonable to -keep it small. +Before QEMU 2.12 the refcount cache had a default size of 1/4 of the +L2 cache size. This resulted in unnecessarily large caches, so now the +refcount cache is as small as possible unless overridden by the user. Using smaller cache entries From 81c6ddf49a76a663cea16c07a07d51b67c853209 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 6 Apr 2018 17:17:30 +0200 Subject: [PATCH 0479/2380] iotests: Add failure matching to common.qemu Currently, common.qemu only allows to match for results indicating success. The only way to fail is by provoking a timeout. However, sometimes we do have a defined failure output and can match for that, which saves us from having to wait for the timeout in case of failure. Because failure can sometimes just result in a _notrun in the test, it is actually important to care about being able to fail quickly. Also, sometimes we simply do not get any specific output in case of success. The only way to handle this currently would be to define an error message as the string to look for, which means that actual success results in a timeout. This is really bad because it unnecessarily slows down a succeeding test. Therefore, this patch adds a new parameter $success_or_failure to _timed_wait_for and _send_qemu_cmd. Setting this to a non-empty string makes both commands expect two match parameters: If the first matches, the function succeeds. If the second matches, the function fails. Signed-off-by: Max Reitz Message-id: 20180406151731.4285-2-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/common.qemu | 58 ++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu index 85f66b852c..f285484951 100644 --- a/tests/qemu-iotests/common.qemu +++ b/tests/qemu-iotests/common.qemu @@ -52,11 +52,29 @@ _in_fd=4 # response is not echoed out. # If $mismatch_only is set, only non-matching responses will # be echoed. +# +# If $success_or_failure is set, the meaning of the arguments is +# changed as follows: +# $2: A string to search for in the response; if found, this indicates +# success and ${QEMU_STATUS[$1]} is set to 0. +# $3: A string to search for in the response; if found, this indicates +# failure and the test is either aborted (if $qemu_error_no_exit +# is not set) or ${QEMU_STATUS[$1]} is set to -1 (otherwise). function _timed_wait_for() { local h=${1} shift + if [ -z "${success_or_failure}" ]; then + success_match=${*} + failure_match= + else + success_match=${1} + failure_match=${2} + fi + + timeout=yes + QEMU_STATUS[$h]=0 while IFS= read -t ${QEMU_COMM_TIMEOUT} resp <&${QEMU_OUT[$h]} do @@ -64,10 +82,18 @@ function _timed_wait_for() echo "${resp}" | _filter_testdir | _filter_qemu \ | _filter_qemu_io | _filter_qmp | _filter_hmp fi - grep -q "${*}" < <(echo "${resp}") + if [ -n "${failure_match}" ]; then + grep -q "${failure_match}" < <(echo "${resp}") + if [ $? -eq 0 ]; then + timeout= + break + fi + fi + grep -q "${success_match}" < <(echo "${resp}") if [ $? -eq 0 ]; then return - elif [ -z "${silent}" ] && [ -n "${mismatch_only}" ]; then + fi + if [ -z "${silent}" ] && [ -n "${mismatch_only}" ]; then echo "${resp}" | _filter_testdir | _filter_qemu \ | _filter_qemu_io | _filter_qmp | _filter_hmp fi @@ -75,8 +101,12 @@ function _timed_wait_for() done QEMU_STATUS[$h]=-1 if [ -z "${qemu_error_no_exit}" ]; then - echo "Timeout waiting for ${*} on handle ${h}" - exit 1 # Timeout means the test failed + if [ -n "${timeout}" ]; then + echo "Timeout waiting for ${success_match} on handle ${h}" + else + echo "Wrong response matching ${failure_match} on handle ${h}" + fi + exit 1 # Timeout or wrong match mean the test failed fi } @@ -96,6 +126,11 @@ function _timed_wait_for() # If $qemu_error_no_exit is set, then even if the expected response # is not seen, we will not exit. $QEMU_STATUS[$1] will be set it -1 in # that case. +# +# If $success_or_failure is set, then the last two strings are the +# strings the response will be scanned for. The first of the two +# indicates success, the latter indicates failure. Failure is handled +# like a timeout. function _send_qemu_cmd() { local h=${1} @@ -109,14 +144,23 @@ function _send_qemu_cmd() use_error="no" fi # This array element extraction is done to accommodate pathnames with spaces - cmd=${@: 1:${#@}-1} - shift $(($# - 1)) + if [ -z "${success_or_failure}" ]; then + cmd=${@: 1:${#@}-1} + shift $(($# - 1)) + else + cmd=${@: 1:${#@}-2} + shift $(($# - 2)) + fi while [ ${count} -gt 0 ] do echo "${cmd}" >&${QEMU_IN[${h}]} if [ -n "${1}" ]; then - qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" + if [ -z "${success_or_failure}" ]; then + qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" + else + qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" "${2}" + fi if [ ${QEMU_STATUS[$h]} -eq 0 ]; then return fi From b05a2225d2e87a04697509219d00ced7c46ed34d Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 6 Apr 2018 17:17:31 +0200 Subject: [PATCH 0480/2380] iotests: Skip 181 and 201 without userfaultfd userfaultfd support depends on the host kernel, so it may not be available. If so, 181 and 201 should be skipped. Signed-off-by: Max Reitz Message-id: 20180406151731.4285-3-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/181 | 13 +++++++++++++ tests/qemu-iotests/201 | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181 index 5e767c6195..e02979378d 100755 --- a/tests/qemu-iotests/181 +++ b/tests/qemu-iotests/181 @@ -96,6 +96,19 @@ echo # Enable postcopy-ram capability both on source and destination silent=yes _send_qemu_cmd $dest 'migrate_set_capability postcopy-ram on' "(qemu)" + +qemu_error_no_exit=yes success_or_failure=yes \ + _send_qemu_cmd $dest '' "(qemu)" "Postcopy is not supported" +if [ ${QEMU_STATUS[$dest]} -lt 0 ]; then + _send_qemu_cmd $dest '' "(qemu)" + + _send_qemu_cmd $src 'quit' "" + _send_qemu_cmd $dest 'quit' "" + wait=1 _cleanup_qemu + + _notrun 'Postcopy is not supported' +fi + _send_qemu_cmd $src 'migrate_set_speed 4k' "(qemu)" _send_qemu_cmd $src 'migrate_set_capability postcopy-ram on' "(qemu)" _send_qemu_cmd $src "migrate -d unix:${MIG_SOCKET}" "(qemu)" diff --git a/tests/qemu-iotests/201 b/tests/qemu-iotests/201 index 11f640f5df..c1a1e00077 100755 --- a/tests/qemu-iotests/201 +++ b/tests/qemu-iotests/201 @@ -82,6 +82,19 @@ echo silent=yes _send_qemu_cmd $dest 'migrate_set_capability postcopy-ram on' "(qemu)" + +qemu_error_no_exit=yes success_or_failure=yes \ + _send_qemu_cmd $dest '' "(qemu)" "Postcopy is not supported" +if [ ${QEMU_STATUS[$dest]} -lt 0 ]; then + _send_qemu_cmd $dest '' "(qemu)" + + _send_qemu_cmd $src 'quit' "" + _send_qemu_cmd $dest 'quit' "" + wait=1 _cleanup_qemu + + _notrun 'Postcopy is not supported' +fi + _send_qemu_cmd $src 'migrate_set_capability postcopy-ram on' "(qemu)" _send_qemu_cmd $src "migrate -d unix:${MIG_SOCKET}" "(qemu)" From 6c6f24fd84895d03baa898bbc4324dd4ccc97071 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 21 Apr 2018 15:29:21 +0200 Subject: [PATCH 0481/2380] block: Add COR filter driver This adds a simple copy-on-read filter driver. It relies on the already existing COR functionality in the central block layer code, which may be moved here once we no longer need it there. Signed-off-by: Max Reitz Message-id: 20180421132929.21610-2-mreitz@redhat.com Reviewed-by: Alberto Garcia Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- block/Makefile.objs | 2 +- block/copy-on-read.c | 171 +++++++++++++++++++++++++++++++++++++++++++ qapi/block-core.json | 5 +- 3 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 block/copy-on-read.c diff --git a/block/Makefile.objs b/block/Makefile.objs index d644bac60a..899bfb5e2c 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -26,7 +26,7 @@ block-obj-y += accounting.o dirty-bitmap.o block-obj-y += write-threshold.o block-obj-y += backup.o block-obj-$(CONFIG_REPLICATION) += replication.o -block-obj-y += throttle.o +block-obj-y += throttle.o copy-on-read.o block-obj-y += crypto.o diff --git a/block/copy-on-read.c b/block/copy-on-read.c new file mode 100644 index 0000000000..823ec751c4 --- /dev/null +++ b/block/copy-on-read.c @@ -0,0 +1,171 @@ +/* + * Copy-on-read filter block driver + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Author: + * Max Reitz + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "block/block_int.h" + + +static int cor_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, + errp); + if (!bs->file) { + return -EINVAL; + } + + bs->supported_write_flags = BDRV_REQ_FUA & + bs->file->bs->supported_write_flags; + + bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & + bs->file->bs->supported_zero_flags; + + return 0; +} + + +static void cor_close(BlockDriverState *bs) +{ +} + + +#define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ + | BLK_PERM_WRITE \ + | BLK_PERM_RESIZE) +#define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH) + +static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole *role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + if (c == NULL) { + *nperm = (perm & PERM_PASSTHROUGH) | BLK_PERM_WRITE_UNCHANGED; + *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED; + return; + } + + *nperm = (perm & PERM_PASSTHROUGH) | + (c->perm & PERM_UNCHANGED); + *nshared = (shared & PERM_PASSTHROUGH) | + (c->shared_perm & PERM_UNCHANGED); +} + + +static int64_t cor_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file->bs); +} + + +static int cor_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) +{ + return bdrv_truncate(bs->file, offset, prealloc, errp); +} + + +static int coroutine_fn cor_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + return bdrv_co_preadv(bs->file, offset, bytes, qiov, + flags | BDRV_REQ_COPY_ON_READ); +} + + +static int coroutine_fn cor_co_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + + return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); +} + + +static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, + BdrvRequestFlags flags) +{ + return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); +} + + +static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, + int64_t offset, int bytes) +{ + return bdrv_co_pdiscard(bs->file->bs, offset, bytes); +} + + +static void cor_eject(BlockDriverState *bs, bool eject_flag) +{ + bdrv_eject(bs->file->bs, eject_flag); +} + + +static void cor_lock_medium(BlockDriverState *bs, bool locked) +{ + bdrv_lock_medium(bs->file->bs, locked); +} + + +static bool cor_recurse_is_first_non_filter(BlockDriverState *bs, + BlockDriverState *candidate) +{ + return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); +} + + +BlockDriver bdrv_copy_on_read = { + .format_name = "copy-on-read", + + .bdrv_open = cor_open, + .bdrv_close = cor_close, + .bdrv_child_perm = cor_child_perm, + + .bdrv_getlength = cor_getlength, + .bdrv_truncate = cor_truncate, + + .bdrv_co_preadv = cor_co_preadv, + .bdrv_co_pwritev = cor_co_pwritev, + .bdrv_co_pwrite_zeroes = cor_co_pwrite_zeroes, + .bdrv_co_pdiscard = cor_co_pdiscard, + + .bdrv_eject = cor_eject, + .bdrv_lock_medium = cor_lock_medium, + + .bdrv_co_block_status = bdrv_co_block_status_from_file, + + .bdrv_recurse_is_first_non_filter = cor_recurse_is_first_non_filter, + + .has_variable_length = true, + .is_filter = true, +}; + +static void bdrv_copy_on_read_init(void) +{ + bdrv_register(&bdrv_copy_on_read); +} + +block_init(bdrv_copy_on_read_init); diff --git a/qapi/block-core.json b/qapi/block-core.json index 17ffd44cce..55728cb823 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2510,11 +2510,12 @@ # @vxhs: Since 2.10 # @throttle: Since 2.11 # @nvme: Since 2.12 +# @copy-on-read: Since 2.13 # # Since: 2.9 ## { 'enum': 'BlockdevDriver', - 'data': [ 'blkdebug', 'blkverify', 'bochs', 'cloop', + 'data': [ 'blkdebug', 'blkverify', 'bochs', 'cloop', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', 'qcow2', 'qed', @@ -3531,6 +3532,7 @@ 'blkverify': 'BlockdevOptionsBlkverify', 'bochs': 'BlockdevOptionsGenericFormat', 'cloop': 'BlockdevOptionsGenericFormat', + 'copy-on-read':'BlockdevOptionsGenericFormat', 'dmg': 'BlockdevOptionsGenericFormat', 'file': 'BlockdevOptionsFile', 'ftp': 'BlockdevOptionsCurlFtp', @@ -4058,6 +4060,7 @@ 'blkverify': 'BlockdevCreateNotSupported', 'bochs': 'BlockdevCreateNotSupported', 'cloop': 'BlockdevCreateNotSupported', + 'copy-on-read': 'BlockdevCreateNotSupported', 'dmg': 'BlockdevCreateNotSupported', 'file': 'BlockdevCreateOptionsFile', 'ftp': 'BlockdevCreateNotSupported', From 24b7c538fea86b598e2a335f4805a0ab50a30e98 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 21 Apr 2018 15:29:22 +0200 Subject: [PATCH 0482/2380] block: BLK_PERM_WRITE includes ..._UNCHANGED Currently we never actually check whether the WRITE_UNCHANGED permission has been taken for unchanging writes. But the one check that is commented out checks both WRITE and WRITE_UNCHANGED; and considering that WRITE_UNCHANGED is already documented as being weaker than WRITE, we should probably explicitly document WRITE to include WRITE_UNCHANGED. Signed-off-by: Max Reitz Reviewed-by: Stefan Hajnoczi Reviewed-by: Alberto Garcia Message-id: 20180421132929.21610-3-mreitz@redhat.com Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- include/block/block.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/block/block.h b/include/block/block.h index cdec3639a3..397b5e8d44 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -205,6 +205,9 @@ enum { * This permission (which is weaker than BLK_PERM_WRITE) is both enough and * required for writes to the block node when the caller promises that * the visible disk content doesn't change. + * + * As the BLK_PERM_WRITE permission is strictly stronger, either is + * sufficient to perform an unchanging write. */ BLK_PERM_WRITE_UNCHANGED = 0x04, From c6035964f8316b504060618d05b5dd434f18595b Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 21 Apr 2018 15:29:23 +0200 Subject: [PATCH 0483/2380] block: Add BDRV_REQ_WRITE_UNCHANGED flag This flag signifies that a write request will not change the visible disk content. With this flag set, it is sufficient to have the BLK_PERM_WRITE_UNCHANGED permission instead of BLK_PERM_WRITE. Signed-off-by: Max Reitz Reviewed-by: Stefan Hajnoczi Reviewed-by: Alberto Garcia Message-id: 20180421132929.21610-4-mreitz@redhat.com Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- block/io.c | 6 +++++- include/block/block.h | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/block/io.c b/block/io.c index 4fad5ac2fe..9e8449e795 100644 --- a/block/io.c +++ b/block/io.c @@ -1504,7 +1504,11 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, assert(!waited || !req->serialising); assert(req->overlap_offset <= offset); assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); - assert(child->perm & BLK_PERM_WRITE); + if (flags & BDRV_REQ_WRITE_UNCHANGED) { + assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); + } else { + assert(child->perm & BLK_PERM_WRITE); + } assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); diff --git a/include/block/block.h b/include/block/block.h index 397b5e8d44..3894edda9d 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -54,8 +54,12 @@ typedef enum { BDRV_REQ_FUA = 0x10, BDRV_REQ_WRITE_COMPRESSED = 0x20, + /* Signifies that this write request will not change the visible disk + * content. */ + BDRV_REQ_WRITE_UNCHANGED = 0x40, + /* Mask of valid flags */ - BDRV_REQ_MASK = 0x3f, + BDRV_REQ_MASK = 0x7f, } BdrvRequestFlags; typedef struct BlockSizes { From 7adcf59fecf3c8ce9330430187350b53f9e50cf7 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 21 Apr 2018 15:29:24 +0200 Subject: [PATCH 0484/2380] block: Set BDRV_REQ_WRITE_UNCHANGED for COR writes Signed-off-by: Max Reitz Reviewed-by: Stefan Hajnoczi Reviewed-by: Alberto Garcia Message-id: 20180421132929.21610-5-mreitz@redhat.com Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- block/io.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/block/io.c b/block/io.c index 9e8449e795..ca96b487eb 100644 --- a/block/io.c +++ b/block/io.c @@ -1118,13 +1118,15 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, /* FIXME: Should we (perhaps conditionally) be setting * BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy * that still correctly reads as zero? */ - ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, pnum, 0); + ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, pnum, + BDRV_REQ_WRITE_UNCHANGED); } else { /* This does not change the data on the disk, it is not * necessary to flush even in cache=writethrough mode. */ ret = bdrv_driver_pwritev(bs, cluster_offset, pnum, - &local_qiov, 0); + &local_qiov, + BDRV_REQ_WRITE_UNCHANGED); } if (ret < 0) { From 1b1a920b713af6af795d49d0e3d2a8a65020bf82 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 21 Apr 2018 15:29:25 +0200 Subject: [PATCH 0485/2380] block/quorum: Support BDRV_REQ_WRITE_UNCHANGED We just need to forward it to quorum's children (except in case of a rewrite because of corruption), but for that we first have to support flags in child requests at all. Signed-off-by: Max Reitz Reviewed-by: Stefan Hajnoczi Reviewed-by: Alberto Garcia Message-id: 20180421132929.21610-6-mreitz@redhat.com Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- block/quorum.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/block/quorum.c b/block/quorum.c index a5051da56e..e448d7e384 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -115,6 +115,7 @@ struct QuorumAIOCB { /* Request metadata */ uint64_t offset; uint64_t bytes; + int flags; QEMUIOVector *qiov; /* calling IOV */ @@ -157,7 +158,8 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b) static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, QEMUIOVector *qiov, uint64_t offset, - uint64_t bytes) + uint64_t bytes, + int flags) { BDRVQuorumState *s = bs->opaque; QuorumAIOCB *acb = g_new(QuorumAIOCB, 1); @@ -168,6 +170,7 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, .bs = bs, .offset = offset, .bytes = bytes, + .flags = flags, .qiov = qiov, .votes.compare = quorum_sha256_compare, .votes.vote_list = QLIST_HEAD_INITIALIZER(acb.votes.vote_list), @@ -271,9 +274,11 @@ static void quorum_rewrite_entry(void *opaque) BDRVQuorumState *s = acb->bs->opaque; /* Ignore any errors, it's just a correction attempt for already - * corrupted data. */ + * corrupted data. + * Mask out BDRV_REQ_WRITE_UNCHANGED because this overwrites the + * area with different data from the other children. */ bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes, - acb->qiov, 0); + acb->qiov, acb->flags & ~BDRV_REQ_WRITE_UNCHANGED); /* Wake up the caller after the last rewrite */ acb->rewrite_count--; @@ -673,7 +678,7 @@ static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { BDRVQuorumState *s = bs->opaque; - QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes); + QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); int ret; acb->is_read = true; @@ -699,7 +704,7 @@ static void write_quorum_entry(void *opaque) sacb->bs = s->children[i]->bs; sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes, - acb->qiov, 0); + acb->qiov, acb->flags); if (sacb->ret == 0) { acb->success_count++; } else { @@ -719,7 +724,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { BDRVQuorumState *s = bs->opaque; - QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes); + QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); int i, ret; for (i = 0; i < s->num_children; i++) { @@ -961,6 +966,8 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, } s->next_child_index = s->num_children; + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; + g_free(opened); goto exit; From 228345bf5db8bc97d1c64f062e138d389065d1ab Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 21 Apr 2018 15:29:26 +0200 Subject: [PATCH 0486/2380] block: Support BDRV_REQ_WRITE_UNCHANGED in filters Update the rest of the filter drivers to support BDRV_REQ_WRITE_UNCHANGED. They already forward write request flags to their children, so we just have to announce support for it. This patch does not cover the replication driver because that currently does not support flags at all, and because it just grabs the WRITE permission for its children when it can, so we should be fine just submitting the incoming WRITE_UNCHANGED requests as normal writes. It also does not cover format drivers for similar reasons. They all use bdrv_format_default_perms() as their .bdrv_child_perm() implementation so they just always grab the WRITE permission for their file children whenever possible. In addition, it often would be difficult to ascertain whether incoming unchanging writes end up as unchanging writes in their files. So we just leave them as normal potentially changing writes. Signed-off-by: Max Reitz Reviewed-by: Stefan Hajnoczi Reviewed-by: Alberto Garcia Message-id: 20180421132929.21610-7-mreitz@redhat.com Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- block/blkdebug.c | 9 +++++---- block/blkreplay.c | 3 +++ block/blkverify.c | 3 +++ block/copy-on-read.c | 10 ++++++---- block/mirror.c | 2 ++ block/raw-format.c | 9 +++++---- block/throttle.c | 6 ++++-- 7 files changed, 28 insertions(+), 14 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index 053372c22e..526af2a808 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -398,10 +398,11 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, goto out; } - bs->supported_write_flags = BDRV_REQ_FUA & - bs->file->bs->supported_write_flags; - bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & - bs->file->bs->supported_zero_flags; + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | + (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & + bs->file->bs->supported_zero_flags); ret = -EINVAL; /* Set alignment overrides */ diff --git a/block/blkreplay.c b/block/blkreplay.c index fe5a9b4a98..b016dbeee7 100755 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -35,6 +35,9 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; + ret = 0; fail: return ret; diff --git a/block/blkverify.c b/block/blkverify.c index 754cc9e857..da97ee5927 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -141,6 +141,9 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; + ret = 0; fail: qemu_opts_del(opts); diff --git a/block/copy-on-read.c b/block/copy-on-read.c index 823ec751c4..6a97208888 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -33,11 +33,13 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - bs->supported_write_flags = BDRV_REQ_FUA & - bs->file->bs->supported_write_flags; + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | + (BDRV_REQ_FUA & + bs->file->bs->supported_write_flags); - bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & - bs->file->bs->supported_zero_flags; + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & + bs->file->bs->supported_zero_flags); return 0; } diff --git a/block/mirror.c b/block/mirror.c index 6aa38db114..a4197bb975 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1134,6 +1134,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, mirror_top_bs->implicit = true; } mirror_top_bs->total_sectors = bs->total_sectors; + mirror_top_bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; + mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs)); /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep diff --git a/block/raw-format.c b/block/raw-format.c index a378547c99..fe33693a2d 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -415,10 +415,11 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, } bs->sg = bs->file->bs->sg; - bs->supported_write_flags = BDRV_REQ_FUA & - bs->file->bs->supported_write_flags; - bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & - bs->file->bs->supported_zero_flags; + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | + (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & + bs->file->bs->supported_zero_flags); if (bs->probed && !bdrv_is_read_only(bs)) { fprintf(stderr, diff --git a/block/throttle.c b/block/throttle.c index 95ed06acd8..e298827f95 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -81,8 +81,10 @@ static int throttle_open(BlockDriverState *bs, QDict *options, if (!bs->file) { return -EINVAL; } - bs->supported_write_flags = bs->file->bs->supported_write_flags; - bs->supported_zero_flags = bs->file->bs->supported_zero_flags; + bs->supported_write_flags = bs->file->bs->supported_write_flags | + BDRV_REQ_WRITE_UNCHANGED; + bs->supported_zero_flags = bs->file->bs->supported_zero_flags | + BDRV_REQ_WRITE_UNCHANGED; return throttle_configure_tgm(bs, tgm, options, errp); } From 5fdc0b73eb68d107944cfa65185fb155b511e496 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 21 Apr 2018 15:29:27 +0200 Subject: [PATCH 0487/2380] iotests: Clean up wrap image in 197 Signed-off-by: Max Reitz Reviewed-by: Stefan Hajnoczi Reviewed-by: Alberto Garcia Message-id: 20180421132929.21610-8-mreitz@redhat.com Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- tests/qemu-iotests/197 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 index 5e869fe2b7..3ae4975eec 100755 --- a/tests/qemu-iotests/197 +++ b/tests/qemu-iotests/197 @@ -44,6 +44,7 @@ esac _cleanup() { _cleanup_test_img + rm -f "$TEST_WRAP" rm -f "$BLKDBG_CONF" } trap "_cleanup; exit \$status" 0 1 2 3 15 From a62cbac4ce2db79c14ff299e98ee556b57467c19 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 21 Apr 2018 15:29:28 +0200 Subject: [PATCH 0488/2380] iotests: Copy 197 for COR filter driver iotest 197 tests copy-on-read using the (now old) copy-on-read flag. Copy it to 215 and modify it to use the COR filter driver instead. Signed-off-by: Max Reitz Message-id: 20180421132929.21610-9-mreitz@redhat.com Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- tests/qemu-iotests/215 | 120 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/215.out | 26 ++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 147 insertions(+) create mode 100755 tests/qemu-iotests/215 create mode 100644 tests/qemu-iotests/215.out diff --git a/tests/qemu-iotests/215 b/tests/qemu-iotests/215 new file mode 100755 index 0000000000..2e616ed659 --- /dev/null +++ b/tests/qemu-iotests/215 @@ -0,0 +1,120 @@ +#!/bin/bash +# +# Test case for copy-on-read into qcow2, using the COR filter driver +# +# Copyright (C) 2018 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 . +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +TEST_WRAP="$TEST_DIR/t.wrap.qcow2" +BLKDBG_CONF="$TEST_DIR/blkdebug.conf" + +# Sanity check: our use of blkdebug fails if $TEST_DIR contains spaces +# or other problems +case "$TEST_DIR" in + *[^-_a-zA-Z0-9/]*) + _notrun "Suspicious TEST_DIR='$TEST_DIR', cowardly refusing to run" ;; +esac + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_WRAP" + rm -f "$BLKDBG_CONF" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# Test is supported for any backing file; but we force qcow2 for our wrapper. +_supported_fmt generic +_supported_proto generic +_supported_os Linux +# LUKS support may be possible, but it complicates things. +_unsupported_fmt luks + +echo +echo '=== Copy-on-read ===' +echo + +# Prep the images +# VPC rounds image sizes to a specific geometry, force a specific size. +if [ "$IMGFMT" = "vpc" ]; then + IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size") +fi +_make_test_img 4G +$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io +IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \ + _make_test_img -F "$IMGFMT" -b "$TEST_IMG" | _filter_img_create +$QEMU_IO -f qcow2 -c "write -z -u 1M 64k" "$TEST_WRAP" | _filter_qemu_io + +# Ensure that a read of two clusters, but where one is already allocated, +# does not re-write the allocated cluster +cat > "$BLKDBG_CONF" <&1 | _filter_qemu_io) +case $output in + *allocate*) + _notrun "Insufficent memory to run test" ;; + *) printf '%s\n' "$output" ;; +esac +$QEMU_IO \ + -c "open -o driver=copy-on-read,file.driver=qcow2 $TEST_WRAP" \ + -c "read -P 0 $((3*1024*1024*1024 + 1024)) 1k" \ + | _filter_qemu_io + +# Copy-on-read is incompatible with read-only +$QEMU_IO \ + -c "open -r -o driver=copy-on-read,file.driver=qcow2 $TEST_WRAP" \ + 2>&1 | _filter_testdir + +# Break the backing chain, and show that images are identical, and that +# we properly copied over explicit zeros. +$QEMU_IMG rebase -u -b "" -f qcow2 "$TEST_WRAP" +$QEMU_IO -f qcow2 -c map "$TEST_WRAP" +_check_test_img +$QEMU_IMG compare -f $IMGFMT -F qcow2 "$TEST_IMG" "$TEST_WRAP" + +# success, all done +echo '*** done' +status=0 diff --git a/tests/qemu-iotests/215.out b/tests/qemu-iotests/215.out new file mode 100644 index 0000000000..70b0f5fb19 --- /dev/null +++ b/tests/qemu-iotests/215.out @@ -0,0 +1,26 @@ +QA output created by 215 + +=== Copy-on-read === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 +wrote 1024/1024 bytes at offset 3221225472 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.wrap.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +wrote 65536/65536 bytes at offset 1048576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1048576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 0/0 bytes at offset 0 +0 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2147483136/2147483136 bytes at offset 1024 +2 GiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 3221226496 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +can't open device TEST_DIR/t.wrap.qcow2: Block node is read-only +2 GiB (0x80010000) bytes allocated at offset 0 bytes (0x0) +1023.938 MiB (0x3fff0000) bytes not allocated at offset 2 GiB (0x80010000) +64 KiB (0x10000) bytes allocated at offset 3 GiB (0xc0000000) +1023.938 MiB (0x3fff0000) bytes not allocated at offset 3 GiB (0xc0010000) +No errors were found on the image. +Images are identical. +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index aed024af05..b59bcea640 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -213,4 +213,5 @@ 212 rw auto quick 213 rw auto quick 214 rw auto +215 rw auto quick 218 rw auto quick From 3e7a95feb9b5d66cff7fee38b3c423135ed245f6 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 21 Apr 2018 15:29:29 +0200 Subject: [PATCH 0489/2380] iotests: Add test for COR across nodes COR across nodes (that is, you have some filter node between the actually COR target and the node that performs the COR) cannot reliably work together with the permission system when there is no explicit COR node that can request the WRITE_UNCHANGED permission for its child. This is because COR (currently) sneaks its requests by the usual permission checks, so it can work without a WRITE* permission; but if there is a filter node in between, that will re-issue the request, which then passes through the usual check -- and if nobody has requested a WRITE_UNCHANGED permission, that check will fail. There is no real direct fix apart from hoping that there is someone who has requested that permission; in case of just the qemu-io HMP command (and no guest device), however, that is not the case. The real real fix is to implement the copy-on-read flag through an implicitly added COR node. Such a node can request the necessary permissions as shown in this test. Signed-off-by: Max Reitz Message-id: 20180421132929.21610-10-mreitz@redhat.com Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- tests/qemu-iotests/216 | 115 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/216.out | 28 +++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 144 insertions(+) create mode 100755 tests/qemu-iotests/216 create mode 100644 tests/qemu-iotests/216.out diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216 new file mode 100755 index 0000000000..ca9b47a7fd --- /dev/null +++ b/tests/qemu-iotests/216 @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# +# Copy-on-read tests using a COR filter node +# +# Copyright (C) 2018 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 . +# +# Creator/Owner: Max Reitz + +import iotests +from iotests import log, qemu_img_pipe, qemu_io, filter_qemu_io + +# Need backing file support +iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk']) +iotests.verify_platform(['linux']) + +log('') +log('=== Copy-on-read across nodes ===') +log('') + +# The old copy-on-read mechanism without a filter node cannot request +# WRITE_UNCHANGED permissions for its child. Therefore it just tries +# to sneak its write by the usual permission system and holds its +# fingers crossed. However, that sneaking does not work so well when +# there is a filter node in the way: That will receive the write +# request and re-issue a new one to its child, which this time is a +# proper write request that will make the permission system cough -- +# unless there is someone at the top (like a guest device) that has +# requested write permissions. +# +# A COR filter node, however, can request the proper permissions for +# its child and therefore is not hit by this issue. + +with iotests.FilePath('base.img') as base_img_path, \ + iotests.FilePath('top.img') as top_img_path, \ + iotests.VM() as vm: + + log('--- Setting up images ---') + log('') + + qemu_img_pipe('create', '-f', iotests.imgfmt, base_img_path, '64M') + + log(filter_qemu_io(qemu_io(base_img_path, '-c', 'write -P 1 0M 1M'))) + + qemu_img_pipe('create', '-f', iotests.imgfmt, '-b', base_img_path, + top_img_path) + + log(filter_qemu_io(qemu_io(top_img_path, '-c', 'write -P 2 1M 1M'))) + + log('') + log('--- Doing COR ---') + log('') + + # Compare with e.g. the following: + # vm.add_drive_raw('if=none,node-name=node0,copy-on-read=on,driver=raw,' \ + # 'file.driver=%s,file.file.filename=%s' % + # (iotests.imgfmt, top_img_path)) + # (Remove the blockdev-add instead.) + # ((Not tested here because it hits an assertion in the permission + # system.)) + + vm.launch() + + log(vm.qmp('blockdev-add', + node_name='node0', + driver='copy-on-read', + file={ + 'driver': 'raw', + 'file': { + 'driver': 'copy-on-read', + 'file': { + 'driver': 'raw', + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': top_img_path + }, + 'backing': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': base_img_path + } + } + } + } + } + })) + + # Trigger COR + log(vm.qmp('human-monitor-command', + command_line='qemu-io node0 "read 0 64M"')) + + vm.shutdown() + + log('') + log('--- Checking COR result ---') + log('') + + log(filter_qemu_io(qemu_io(base_img_path, '-c', 'discard 0 64M'))) + log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 1 0M 1M'))) + log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 2 1M 1M'))) diff --git a/tests/qemu-iotests/216.out b/tests/qemu-iotests/216.out new file mode 100644 index 0000000000..d3fc590d29 --- /dev/null +++ b/tests/qemu-iotests/216.out @@ -0,0 +1,28 @@ + +=== Copy-on-read across nodes === + +--- Setting up images --- + +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +wrote 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + +--- Doing COR --- + +{u'return': {}} +{u'return': u''} + +--- Checking COR result --- + +discard 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index b59bcea640..cc8cd8cc8e 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -214,4 +214,5 @@ 213 rw auto quick 214 rw auto 215 rw auto quick +216 rw auto quick 218 rw auto quick From 5279b30392da7a3248b320c75f20c61e3a95863c Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 21 Apr 2018 18:39:57 +0200 Subject: [PATCH 0490/2380] qemu-img: Check post-truncation size Some block drivers (iscsi and file-posix when dealing with device files) do not actually support truncation, even though they provide a .bdrv_truncate() method and will happily return success when providing a new size that does not exceed the current size. This is because these drivers expect the user to resize the image outside of qemu and then provide qemu with that information through the block_resize command (compare cb1b83e740384b4e0d950f3d7c81c02b8ce86c2e). Of course, anyone using qemu-img resize will find that behavior useless. So we should check the actual size of the image after the supposedly successful truncation took place, emit an error if nothing changed and emit a warning if the target size was not met. Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1523065 Signed-off-by: Max Reitz Message-id: 20180421163957.29872-1-mreitz@redhat.com Reviewed-by: Stefan Hajnoczi Signed-off-by: Max Reitz --- qemu-img.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index ea62d2d61e..62b29e7feb 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3381,7 +3381,7 @@ static int img_resize(int argc, char **argv) Error *err = NULL; int c, ret, relative; const char *filename, *fmt, *size; - int64_t n, total_size, current_size; + int64_t n, total_size, current_size, new_size; bool quiet = false; BlockBackend *blk = NULL; PreallocMode prealloc = PREALLOC_MODE_OFF; @@ -3557,11 +3557,42 @@ static int img_resize(int argc, char **argv) } ret = blk_truncate(blk, total_size, prealloc, &err); - if (!ret) { - qprintf(quiet, "Image resized.\n"); - } else { + if (ret < 0) { error_report_err(err); + goto out; } + + new_size = blk_getlength(blk); + if (new_size < 0) { + error_report("Failed to verify truncated image length: %s", + strerror(-new_size)); + ret = -1; + goto out; + } + + /* Some block drivers implement a truncation method, but only so + * the user can cause qemu to refresh the image's size from disk. + * The idea is that the user resizes the image outside of qemu and + * then invokes block_resize to inform qemu about it. + * (This includes iscsi and file-posix for device files.) + * Of course, that is not the behavior someone invoking + * qemu-img resize would find useful, so we catch that behavior + * here and tell the user. */ + if (new_size != total_size && new_size == current_size) { + error_report("Image was not resized; resizing may not be supported " + "for this image"); + ret = -1; + goto out; + } + + if (new_size != total_size) { + warn_report("Image should have been resized to %" PRIi64 + " bytes, but was resized to %" PRIi64 " bytes", + total_size, new_size); + } + + qprintf(quiet, "Image resized.\n"); + out: blk_unref(blk); if (ret) { From c1e3489dfaa01d215e37b1412759b856c33d44ed Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 2 May 2018 16:03:59 +0200 Subject: [PATCH 0491/2380] block: Document BDRV_REQ_WRITE_UNCHANGED support Add BDRV_REQ_WRITE_UNCHANGED to the list of flags honored during pwrite and pwrite_zeroes, and also add a note on when you absolutely need to support it. Signed-off-by: Max Reitz Message-id: 20180502140359.18222-1-mreitz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- include/block/block_int.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/include/block/block_int.h b/include/block/block_int.h index e3d6219f4e..76b589da57 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -656,10 +656,24 @@ struct BlockDriverState { /* I/O Limits */ BlockLimits bl; - /* Flags honored during pwrite (so far: BDRV_REQ_FUA) */ + /* Flags honored during pwrite (so far: BDRV_REQ_FUA, + * BDRV_REQ_WRITE_UNCHANGED). + * If a driver does not support BDRV_REQ_WRITE_UNCHANGED, those + * writes will be issued as normal writes without the flag set. + * This is important to note for drivers that do not explicitly + * request a WRITE permission for their children and instead take + * the same permissions as their parent did (this is commonly what + * block filters do). Such drivers have to be aware that the + * parent may have taken a WRITE_UNCHANGED permission only and is + * issuing such requests. Drivers either must make sure that + * these requests do not result in plain WRITE accesses (usually + * by supporting BDRV_REQ_WRITE_UNCHANGED, and then forwarding + * every incoming write request as-is, including potentially that + * flag), or they have to explicitly take the WRITE permission for + * their children. */ unsigned int supported_write_flags; /* Flags honored during pwrite_zeroes (so far: BDRV_REQ_FUA, - * BDRV_REQ_MAY_UNMAP) */ + * BDRV_REQ_MAY_UNMAP, BDRV_REQ_WRITE_UNCHANGED) */ unsigned int supported_zero_flags; /* the following member gives a name to every node on the bs graph. */ From 2a01c01f9ecb43af4c0a85fe6adc429ffc9c31b5 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 2 May 2018 22:20:49 +0200 Subject: [PATCH 0492/2380] qemu-io: Use purely string blockdev options Currently, qemu-io only uses string-valued blockdev options (as all are converted directly from QemuOpts) -- with one exception: -U adds the force-share option as a boolean. This in itself is already a bit questionable, but a real issue is that it also assumes the value already existing in the options QDict would be a boolean, which is wrong. That has the following effect: $ ./qemu-io -r -U --image-opts \ driver=file,filename=/dev/null,force-share=off [1] 15200 segmentation fault (core dumped) ./qemu-io -r -U --image-opts driver=file,filename=/dev/null,force-share=off Since @opts is converted from QemuOpts, the value must be a string, and we have to compare it as such. Consequently, it makes sense to also set it as a string instead of a boolean. Cc: qemu-stable@nongnu.org Signed-off-by: Max Reitz Message-id: 20180502202051.15493-2-mreitz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- qemu-io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qemu-io.c b/qemu-io.c index 72fee0d8b7..73c638ff8b 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -95,12 +95,12 @@ static int openfile(char *name, int flags, bool writethrough, bool force_share, opts = qdict_new(); } if (qdict_haskey(opts, BDRV_OPT_FORCE_SHARE) - && !qdict_get_bool(opts, BDRV_OPT_FORCE_SHARE)) { + && strcmp(qdict_get_str(opts, BDRV_OPT_FORCE_SHARE), "on")) { error_report("-U conflicts with image options"); qobject_unref(opts); return 1; } - qdict_put_bool(opts, BDRV_OPT_FORCE_SHARE, true); + qdict_put_str(opts, BDRV_OPT_FORCE_SHARE, "on"); } qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err); if (!qemuio_blk) { From 4615f87832d2fcb7a544bedeece2741bf8c21f94 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 2 May 2018 22:20:50 +0200 Subject: [PATCH 0493/2380] qemu-img: Use only string options in img_open_opts img_open_opts() takes a QemuOpts and converts them to a QDict, so all values therein are strings. Then it may try to call qdict_get_bool(), however, which will fail with a segmentation fault every time: $ ./qemu-img info -U --image-opts \ driver=file,filename=/dev/null,force-share=off [1] 27869 segmentation fault (core dumped) ./qemu-img info -U --image-opts driver=file,filename=/dev/null,force-share=off Fix this by using qdict_get_str() and comparing the value as a string. Also, when adding a force-share value to the QDict, add it as a string so it fits the rest of the dict. Cc: qemu-stable@nongnu.org Signed-off-by: Max Reitz Message-id: 20180502202051.15493-3-mreitz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- qemu-img.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 62b29e7feb..60e45ec103 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -277,12 +277,12 @@ static BlockBackend *img_open_opts(const char *optstr, options = qemu_opts_to_qdict(opts, NULL); if (force_share) { if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE) - && !qdict_get_bool(options, BDRV_OPT_FORCE_SHARE)) { + && strcmp(qdict_get_str(options, BDRV_OPT_FORCE_SHARE), "on")) { error_report("--force-share/-U conflicts with image options"); qobject_unref(options); return NULL; } - qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); + qdict_put_str(options, BDRV_OPT_FORCE_SHARE, "on"); } blk = blk_new_open(NULL, NULL, options, flags, &local_err); if (!blk) { From 4e7d73c5fbd97e55ffe5af02f24d1f7dbe3bbf20 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 2 May 2018 22:20:51 +0200 Subject: [PATCH 0494/2380] iotests: Add test for -U/force-share conflicts Signed-off-by: Max Reitz Message-id: 20180502202051.15493-4-mreitz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- tests/qemu-iotests/153 | 17 +++++++++++++++++ tests/qemu-iotests/153.out | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index a0fd815483..ec508c758f 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -242,6 +242,23 @@ _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' _cleanup_qemu +echo +echo "== Detecting -U and force-share conflicts ==" + +echo +echo 'No conflict:' +$QEMU_IMG info -U --image-opts driver=null-co,force-share=on +echo +echo 'Conflict:' +$QEMU_IMG info -U --image-opts driver=null-co,force-share=off + +echo +echo 'No conflict:' +$QEMU_IO -c 'open -r -U -o driver=null-co,force-share=on' +echo +echo 'Conflict:' +$QEMU_IO -c 'open -r -U -o driver=null-co,force-share=off' + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index bb721cb747..2510762ba1 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -399,4 +399,20 @@ Is another process using the image? Closing the other _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 + +== Detecting -U and force-share conflicts == + +No conflict: +image: null-co:// +file format: null-co +virtual size: 1.0G (1073741824 bytes) +disk size: unavailable + +Conflict: +qemu-img: --force-share/-U conflicts with image options + +No conflict: + +Conflict: +-U conflicts with image options *** done From a18495159a35e9c5973d9aa0f612a97318bf684d Mon Sep 17 00:00:00 2001 From: Boqun Feng Date: Tue, 20 Mar 2018 08:08:15 +0800 Subject: [PATCH 0495/2380] i386: add KnightsMill cpu model A new cpu model called "KnightsMill" is added to model Knights Mill processors. Compared to "Skylake-Server" cpu model, the following features are added: avx512_4vnniw avx512_4fmaps avx512pf avx512er avx512_vpopcntdq and the following features are removed: pcid invpcid clflushopt avx512dq avx512bw clwb smap rtm mpx xsavec xgetbv1 hle Signed-off-by: Boqun Feng Message-Id: <20180320000821.8337-1-boqun.feng@intel.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b0a1c629a3..52fd35b6a1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1839,6 +1839,48 @@ static X86CPUDefinition builtin_x86_defs[] = { .xlevel = 0x80000008, .model_id = "Intel Xeon Processor (Skylake, IBRS)", }, + { + .name = "KnightsMill", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 133, + .stepping = 0, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SS | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | + CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | + CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | + CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | + CPUID_PSE | CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | + CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | + CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | + CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP | + CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | + CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_AVX512F | + CPUID_7_0_EBX_AVX512CD | CPUID_7_0_EBX_AVX512PF | + CPUID_7_0_EBX_AVX512ER, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_AVX512_VPOPCNTDQ, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_AVX512_4VNNIW | CPUID_7_0_EDX_AVX512_4FMAPS, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .xlevel = 0x80000008, + .model_id = "Intel Xeon Phi Processor (Knights Mill)", + }, { .name = "Opteron_G1", .level = 5, From 0da0fb062841d0dcd8ba47e4a989d2e952cdf0ff Mon Sep 17 00:00:00 2001 From: Jingqi Liu Date: Fri, 4 May 2018 11:57:33 +0800 Subject: [PATCH 0496/2380] x86/cpu: Enable CLDEMOTE(Demote Cache Line) cpu feature The CLDEMOTE instruction hints to hardware that the cache line that contains the linear address should be moved("demoted") from the cache(s) closest to the processor core to a level more distant from the processor core. This may accelerate subsequent accesses to the line by other cores in the same coherence domain, especially if the line was written by the core that demotes the line. Intel Snow Ridge has added new cpu feature, CLDEMOTE. The new cpu feature needs to be exposed to guest VM. The bit definition: CPUID.(EAX=7,ECX=0):ECX[bit 25] CLDEMOTE The release document ref below link: https://software.intel.com/sites/default/files/managed/c5/15/\ architecture-instruction-set-extensions-programming-reference.pdf Signed-off-by: Jingqi Liu Message-Id: <1525406253-54846-1-git-send-email-jingqi.liu@intel.com> Reviewed-by: Eduardo Habkost Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 2 +- target/i386/cpu.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 52fd35b6a1..4b39ab5dd4 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -494,7 +494,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "avx512bitalg", NULL, "avx512-vpopcntdq", NULL, "la57", NULL, NULL, NULL, NULL, NULL, "rdpid", NULL, - NULL, NULL, NULL, NULL, + NULL, "cldemote", NULL, NULL, NULL, NULL, NULL, NULL, }, .cpuid_eax = 7, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index b58b779bff..8fbe1537c1 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -680,6 +680,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_ECX_AVX512_VPOPCNTDQ (1U << 14) /* POPCNT for vectors of DW/QW */ #define CPUID_7_0_ECX_LA57 (1U << 16) #define CPUID_7_0_ECX_RDPID (1U << 22) +#define CPUID_7_0_ECX_CLDEMOTE (1U << 25) /* CLDEMOTE Instruction */ #define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2) /* AVX512 Neural Network Instructions */ #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */ From 7e3482f824809e1f6ffeb5bb8103ba27a7d1a52a Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 10 May 2018 15:41:41 -0500 Subject: [PATCH 0497/2380] i386: Helpers to encode cache information consistently Instead of having a collection of macros that need to be used in complex expressions to build CPUID data, define a CPUCacheInfo struct that can hold information about a given cache. Helper functions will take a CPUCacheInfo struct as input to encode CPUID leaves for a cache. This will help us ensure consistency between cache information CPUID leaves, and make the existing inconsistencies in CPUID info more visible. Signed-off-by: Eduardo Habkost Signed-off-by: Babu Moger Tested-by: Geoffrey McRae Message-Id: <20180510204148.11687-2-babu.moger@amd.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 495 ++++++++++++++++++++++++++++++++++------------ target/i386/cpu.h | 53 +++++ 2 files changed, 424 insertions(+), 124 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4b39ab5dd4..28bb93990e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -56,33 +56,240 @@ #include "disas/capstone.h" +/* Helpers for building CPUID[2] descriptors: */ -/* Cache topology CPUID constants: */ +struct CPUID2CacheDescriptorInfo { + enum CacheType type; + int level; + int size; + int line_size; + int associativity; +}; -/* CPUID Leaf 2 Descriptors */ +#define KiB 1024 +#define MiB (1024 * 1024) -#define CPUID_2_L1D_32KB_8WAY_64B 0x2c -#define CPUID_2_L1I_32KB_8WAY_64B 0x30 -#define CPUID_2_L2_2MB_8WAY_64B 0x7d -#define CPUID_2_L3_16MB_16WAY_64B 0x4d +/* + * Known CPUID 2 cache descriptors. + * From Intel SDM Volume 2A, CPUID instruction + */ +struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { + [0x06] = { .level = 1, .type = ICACHE, .size = 8 * KiB, + .associativity = 4, .line_size = 32, }, + [0x08] = { .level = 1, .type = ICACHE, .size = 16 * KiB, + .associativity = 4, .line_size = 32, }, + [0x09] = { .level = 1, .type = ICACHE, .size = 32 * KiB, + .associativity = 4, .line_size = 64, }, + [0x0A] = { .level = 1, .type = DCACHE, .size = 8 * KiB, + .associativity = 2, .line_size = 32, }, + [0x0C] = { .level = 1, .type = DCACHE, .size = 16 * KiB, + .associativity = 4, .line_size = 32, }, + [0x0D] = { .level = 1, .type = DCACHE, .size = 16 * KiB, + .associativity = 4, .line_size = 64, }, + [0x0E] = { .level = 1, .type = DCACHE, .size = 24 * KiB, + .associativity = 6, .line_size = 64, }, + [0x1D] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, + .associativity = 2, .line_size = 64, }, + [0x21] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, + .associativity = 8, .line_size = 64, }, + /* lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x22, 0x23 are not included + */ + [0x24] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 16, .line_size = 64, }, + /* lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x25, 0x20 are not included + */ + [0x2C] = { .level = 1, .type = DCACHE, .size = 32 * KiB, + .associativity = 8, .line_size = 64, }, + [0x30] = { .level = 1, .type = ICACHE, .size = 32 * KiB, + .associativity = 8, .line_size = 64, }, + [0x41] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, + .associativity = 4, .line_size = 32, }, + [0x42] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, + .associativity = 4, .line_size = 32, }, + [0x43] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 4, .line_size = 32, }, + [0x44] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 4, .line_size = 32, }, + [0x45] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 4, .line_size = 32, }, + [0x46] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, + .associativity = 4, .line_size = 64, }, + [0x47] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, + .associativity = 8, .line_size = 64, }, + [0x48] = { .level = 2, .type = UNIFIED_CACHE, .size = 3 * MiB, + .associativity = 12, .line_size = 64, }, + /* Descriptor 0x49 depends on CPU family/model, so it is not included */ + [0x4A] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, + .associativity = 12, .line_size = 64, }, + [0x4B] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, + .associativity = 16, .line_size = 64, }, + [0x4C] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB, + .associativity = 12, .line_size = 64, }, + [0x4D] = { .level = 3, .type = UNIFIED_CACHE, .size = 16 * MiB, + .associativity = 16, .line_size = 64, }, + [0x4E] = { .level = 2, .type = UNIFIED_CACHE, .size = 6 * MiB, + .associativity = 24, .line_size = 64, }, + [0x60] = { .level = 1, .type = DCACHE, .size = 16 * KiB, + .associativity = 8, .line_size = 64, }, + [0x66] = { .level = 1, .type = DCACHE, .size = 8 * KiB, + .associativity = 4, .line_size = 64, }, + [0x67] = { .level = 1, .type = DCACHE, .size = 16 * KiB, + .associativity = 4, .line_size = 64, }, + [0x68] = { .level = 1, .type = DCACHE, .size = 32 * KiB, + .associativity = 4, .line_size = 64, }, + [0x78] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 4, .line_size = 64, }, + /* lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x79, 0x7A, 0x7B, 0x7C are not included. + */ + [0x7D] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 8, .line_size = 64, }, + [0x7F] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 2, .line_size = 64, }, + [0x80] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 8, .line_size = 64, }, + [0x82] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, + .associativity = 8, .line_size = 32, }, + [0x83] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 8, .line_size = 32, }, + [0x84] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 8, .line_size = 32, }, + [0x85] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 8, .line_size = 32, }, + [0x86] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 4, .line_size = 64, }, + [0x87] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 8, .line_size = 64, }, + [0xD0] = { .level = 3, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 4, .line_size = 64, }, + [0xD1] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 4, .line_size = 64, }, + [0xD2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 4, .line_size = 64, }, + [0xD6] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 8, .line_size = 64, }, + [0xD7] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 8, .line_size = 64, }, + [0xD8] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, + .associativity = 8, .line_size = 64, }, + [0xDC] = { .level = 3, .type = UNIFIED_CACHE, .size = 1.5 * MiB, + .associativity = 12, .line_size = 64, }, + [0xDD] = { .level = 3, .type = UNIFIED_CACHE, .size = 3 * MiB, + .associativity = 12, .line_size = 64, }, + [0xDE] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, + .associativity = 12, .line_size = 64, }, + [0xE2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 16, .line_size = 64, }, + [0xE3] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, + .associativity = 16, .line_size = 64, }, + [0xE4] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, + .associativity = 16, .line_size = 64, }, + [0xEA] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB, + .associativity = 24, .line_size = 64, }, + [0xEB] = { .level = 3, .type = UNIFIED_CACHE, .size = 18 * MiB, + .associativity = 24, .line_size = 64, }, + [0xEC] = { .level = 3, .type = UNIFIED_CACHE, .size = 24 * MiB, + .associativity = 24, .line_size = 64, }, +}; +/* + * "CPUID leaf 2 does not report cache descriptor information, + * use CPUID leaf 4 to query cache parameters" + */ +#define CACHE_DESCRIPTOR_UNAVAILABLE 0xFF + +/* + * Return a CPUID 2 cache descriptor for a given cache. + * If no known descriptor is found, return CACHE_DESCRIPTOR_UNAVAILABLE + */ +static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache) +{ + int i; + + assert(cache->size > 0); + assert(cache->level > 0); + assert(cache->line_size > 0); + assert(cache->associativity > 0); + for (i = 0; i < ARRAY_SIZE(cpuid2_cache_descriptors); i++) { + struct CPUID2CacheDescriptorInfo *d = &cpuid2_cache_descriptors[i]; + if (d->level == cache->level && d->type == cache->type && + d->size == cache->size && d->line_size == cache->line_size && + d->associativity == cache->associativity) { + return i; + } + } + + return CACHE_DESCRIPTOR_UNAVAILABLE; +} /* CPUID Leaf 4 constants: */ /* EAX: */ -#define CPUID_4_TYPE_DCACHE 1 -#define CPUID_4_TYPE_ICACHE 2 -#define CPUID_4_TYPE_UNIFIED 3 +#define CACHE_TYPE_D 1 +#define CACHE_TYPE_I 2 +#define CACHE_TYPE_UNIFIED 3 -#define CPUID_4_LEVEL(l) ((l) << 5) +#define CACHE_LEVEL(l) (l << 5) -#define CPUID_4_SELF_INIT_LEVEL (1 << 8) -#define CPUID_4_FULLY_ASSOC (1 << 9) +#define CACHE_SELF_INIT_LEVEL (1 << 8) /* EDX: */ -#define CPUID_4_NO_INVD_SHARING (1 << 0) -#define CPUID_4_INCLUSIVE (1 << 1) -#define CPUID_4_COMPLEX_IDX (1 << 2) +#define CACHE_NO_INVD_SHARING (1 << 0) +#define CACHE_INCLUSIVE (1 << 1) +#define CACHE_COMPLEX_IDX (1 << 2) + +/* Encode CacheType for CPUID[4].EAX */ +#define CACHE_TYPE(t) (((t) == DCACHE) ? CACHE_TYPE_D : \ + ((t) == ICACHE) ? CACHE_TYPE_I : \ + ((t) == UNIFIED_CACHE) ? CACHE_TYPE_UNIFIED : \ + 0 /* Invalid value */) + + +/* Encode cache info for CPUID[4] */ +static void encode_cache_cpuid4(CPUCacheInfo *cache, + int num_apic_ids, int num_cores, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + assert(cache->size == cache->line_size * cache->associativity * + cache->partitions * cache->sets); + + assert(num_apic_ids > 0); + *eax = CACHE_TYPE(cache->type) | + CACHE_LEVEL(cache->level) | + (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0) | + ((num_cores - 1) << 26) | + ((num_apic_ids - 1) << 14); + + assert(cache->line_size > 0); + assert(cache->partitions > 0); + assert(cache->associativity > 0); + /* We don't implement fully-associative caches */ + assert(cache->associativity < cache->sets); + *ebx = (cache->line_size - 1) | + ((cache->partitions - 1) << 12) | + ((cache->associativity - 1) << 22); + + assert(cache->sets > 0); + *ecx = cache->sets - 1; + + *edx = (cache->no_invd_sharing ? CACHE_NO_INVD_SHARING : 0) | + (cache->inclusive ? CACHE_INCLUSIVE : 0) | + (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); +} + +/* Encode cache info for CPUID[0x80000005].ECX or CPUID[0x80000005].EDX */ +static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) +{ + assert(cache->size % 1024 == 0); + assert(cache->lines_per_tag > 0); + assert(cache->associativity > 0); + assert(cache->line_size > 0); + return ((cache->size / 1024) << 24) | (cache->associativity << 16) | + (cache->lines_per_tag << 8) | (cache->line_size); +} #define ASSOC_FULL 0xFF @@ -100,57 +307,140 @@ a == ASSOC_FULL ? 0xF : \ 0 /* invalid value */) +/* + * Encode cache info for CPUID[0x80000006].ECX and CPUID[0x80000006].EDX + * @l3 can be NULL. + */ +static void encode_cache_cpuid80000006(CPUCacheInfo *l2, + CPUCacheInfo *l3, + uint32_t *ecx, uint32_t *edx) +{ + assert(l2->size % 1024 == 0); + assert(l2->associativity > 0); + assert(l2->lines_per_tag > 0); + assert(l2->line_size > 0); + *ecx = ((l2->size / 1024) << 16) | + (AMD_ENC_ASSOC(l2->associativity) << 12) | + (l2->lines_per_tag << 8) | (l2->line_size); + + if (l3) { + assert(l3->size % (512 * 1024) == 0); + assert(l3->associativity > 0); + assert(l3->lines_per_tag > 0); + assert(l3->line_size > 0); + *edx = ((l3->size / (512 * 1024)) << 18) | + (AMD_ENC_ASSOC(l3->associativity) << 12) | + (l3->lines_per_tag << 8) | (l3->line_size); + } else { + *edx = 0; + } +} /* Definitions of the hardcoded cache entries we expose: */ /* L1 data cache: */ -#define L1D_LINE_SIZE 64 -#define L1D_ASSOCIATIVITY 8 -#define L1D_SETS 64 -#define L1D_PARTITIONS 1 -/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 32KiB */ -#define L1D_DESCRIPTOR CPUID_2_L1D_32KB_8WAY_64B +static CPUCacheInfo l1d_cache = { + .type = DCACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, +}; + /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ -#define L1D_LINES_PER_TAG 1 -#define L1D_SIZE_KB_AMD 64 -#define L1D_ASSOCIATIVITY_AMD 2 +static CPUCacheInfo l1d_cache_amd = { + .type = DCACHE, + .level = 1, + .size = 64 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 2, + .sets = 512, + .partitions = 1, + .lines_per_tag = 1, + .no_invd_sharing = true, +}; /* L1 instruction cache: */ -#define L1I_LINE_SIZE 64 -#define L1I_ASSOCIATIVITY 8 -#define L1I_SETS 64 -#define L1I_PARTITIONS 1 -/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 32KiB */ -#define L1I_DESCRIPTOR CPUID_2_L1I_32KB_8WAY_64B +static CPUCacheInfo l1i_cache = { + .type = ICACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, +}; + /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ -#define L1I_LINES_PER_TAG 1 -#define L1I_SIZE_KB_AMD 64 -#define L1I_ASSOCIATIVITY_AMD 2 +static CPUCacheInfo l1i_cache_amd = { + .type = ICACHE, + .level = 1, + .size = 64 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 2, + .sets = 512, + .partitions = 1, + .lines_per_tag = 1, + .no_invd_sharing = true, +}; /* Level 2 unified cache: */ -#define L2_LINE_SIZE 64 -#define L2_ASSOCIATIVITY 16 -#define L2_SETS 4096 -#define L2_PARTITIONS 1 -/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 4MiB */ +static CPUCacheInfo l2_cache = { + .type = UNIFIED_CACHE, + .level = 2, + .size = 4 * MiB, + .self_init = 1, + .line_size = 64, + .associativity = 16, + .sets = 4096, + .partitions = 1, + .no_invd_sharing = true, +}; + /*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ -#define L2_DESCRIPTOR CPUID_2_L2_2MB_8WAY_64B +static CPUCacheInfo l2_cache_cpuid2 = { + .type = UNIFIED_CACHE, + .level = 2, + .size = 2 * MiB, + .line_size = 64, + .associativity = 8, +}; + + /*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ -#define L2_LINES_PER_TAG 1 -#define L2_SIZE_KB_AMD 512 +static CPUCacheInfo l2_cache_amd = { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .lines_per_tag = 1, + .associativity = 16, + .sets = 512, + .partitions = 1, +}; /* Level 3 unified cache: */ -#define L3_SIZE_KB 0 /* disabled */ -#define L3_ASSOCIATIVITY 0 /* disabled */ -#define L3_LINES_PER_TAG 0 /* disabled */ -#define L3_LINE_SIZE 0 /* disabled */ -#define L3_N_LINE_SIZE 64 -#define L3_N_ASSOCIATIVITY 16 -#define L3_N_SETS 16384 -#define L3_N_PARTITIONS 1 -#define L3_N_DESCRIPTOR CPUID_2_L3_16MB_16WAY_64B -#define L3_N_LINES_PER_TAG 1 -#define L3_N_SIZE_KB_AMD 16384 +static CPUCacheInfo l3_cache = { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .sets = 16384, + .partitions = 1, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, +}; /* TLB definitions: */ @@ -3344,85 +3634,53 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (!cpu->enable_l3_cache) { *ecx = 0; } else { - *ecx = L3_N_DESCRIPTOR; + *ecx = cpuid2_cache_descriptor(&l3_cache); } - *edx = (L1D_DESCRIPTOR << 16) | \ - (L1I_DESCRIPTOR << 8) | \ - (L2_DESCRIPTOR); + *edx = (cpuid2_cache_descriptor(&l1d_cache) << 16) | + (cpuid2_cache_descriptor(&l1i_cache) << 8) | + (cpuid2_cache_descriptor(&l2_cache_cpuid2)); break; case 4: /* cache info: needed for Core compatibility */ if (cpu->cache_info_passthrough) { host_cpuid(index, count, eax, ebx, ecx, edx); + /* QEMU gives out its own APIC IDs, never pass down bits 31..26. */ *eax &= ~0xFC000000; + if ((*eax & 31) && cs->nr_cores > 1) { + *eax |= (cs->nr_cores - 1) << 26; + } } else { *eax = 0; switch (count) { case 0: /* L1 dcache info */ - *eax |= CPUID_4_TYPE_DCACHE | \ - CPUID_4_LEVEL(1) | \ - CPUID_4_SELF_INIT_LEVEL; - *ebx = (L1D_LINE_SIZE - 1) | \ - ((L1D_PARTITIONS - 1) << 12) | \ - ((L1D_ASSOCIATIVITY - 1) << 22); - *ecx = L1D_SETS - 1; - *edx = CPUID_4_NO_INVD_SHARING; + encode_cache_cpuid4(&l1d_cache, + 1, cs->nr_cores, + eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ - *eax |= CPUID_4_TYPE_ICACHE | \ - CPUID_4_LEVEL(1) | \ - CPUID_4_SELF_INIT_LEVEL; - *ebx = (L1I_LINE_SIZE - 1) | \ - ((L1I_PARTITIONS - 1) << 12) | \ - ((L1I_ASSOCIATIVITY - 1) << 22); - *ecx = L1I_SETS - 1; - *edx = CPUID_4_NO_INVD_SHARING; + encode_cache_cpuid4(&l1i_cache, + 1, cs->nr_cores, + eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ - *eax |= CPUID_4_TYPE_UNIFIED | \ - CPUID_4_LEVEL(2) | \ - CPUID_4_SELF_INIT_LEVEL; - if (cs->nr_threads > 1) { - *eax |= (cs->nr_threads - 1) << 14; - } - *ebx = (L2_LINE_SIZE - 1) | \ - ((L2_PARTITIONS - 1) << 12) | \ - ((L2_ASSOCIATIVITY - 1) << 22); - *ecx = L2_SETS - 1; - *edx = CPUID_4_NO_INVD_SHARING; + encode_cache_cpuid4(&l2_cache, + cs->nr_threads, cs->nr_cores, + eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - if (!cpu->enable_l3_cache) { - *eax = 0; - *ebx = 0; - *ecx = 0; - *edx = 0; + pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); + if (cpu->enable_l3_cache) { + encode_cache_cpuid4(&l3_cache, + (1 << pkg_offset), cs->nr_cores, + eax, ebx, ecx, edx); break; } - *eax |= CPUID_4_TYPE_UNIFIED | \ - CPUID_4_LEVEL(3) | \ - CPUID_4_SELF_INIT_LEVEL; - pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); - *eax |= ((1 << pkg_offset) - 1) << 14; - *ebx = (L3_N_LINE_SIZE - 1) | \ - ((L3_N_PARTITIONS - 1) << 12) | \ - ((L3_N_ASSOCIATIVITY - 1) << 22); - *ecx = L3_N_SETS - 1; - *edx = CPUID_4_INCLUSIVE | CPUID_4_COMPLEX_IDX; - break; + /* fall through */ default: /* end of info */ - *eax = 0; - *ebx = 0; - *ecx = 0; - *edx = 0; + *eax = *ebx = *ecx = *edx = 0; break; } } - - /* QEMU gives out its own APIC IDs, never pass down bits 31..26. */ - if ((*eax & 31) && cs->nr_cores > 1) { - *eax |= (cs->nr_cores - 1) << 26; - } break; case 5: /* mwait info: needed for Core compatibility */ @@ -3626,10 +3884,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \ (L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES); - *ecx = (L1D_SIZE_KB_AMD << 24) | (L1D_ASSOCIATIVITY_AMD << 16) | \ - (L1D_LINES_PER_TAG << 8) | (L1D_LINE_SIZE); - *edx = (L1I_SIZE_KB_AMD << 24) | (L1I_ASSOCIATIVITY_AMD << 16) | \ - (L1I_LINES_PER_TAG << 8) | (L1I_LINE_SIZE); + *ecx = encode_cache_cpuid80000005(&l1d_cache_amd); + *edx = encode_cache_cpuid80000005(&l1i_cache_amd); break; case 0x80000006: /* cache info (L2 cache) */ @@ -3645,18 +3901,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (L2_DTLB_4K_ENTRIES << 16) | \ (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \ (L2_ITLB_4K_ENTRIES); - *ecx = (L2_SIZE_KB_AMD << 16) | \ - (AMD_ENC_ASSOC(L2_ASSOCIATIVITY) << 12) | \ - (L2_LINES_PER_TAG << 8) | (L2_LINE_SIZE); - if (!cpu->enable_l3_cache) { - *edx = ((L3_SIZE_KB / 512) << 18) | \ - (AMD_ENC_ASSOC(L3_ASSOCIATIVITY) << 12) | \ - (L3_LINES_PER_TAG << 8) | (L3_LINE_SIZE); - } else { - *edx = ((L3_N_SIZE_KB_AMD / 512) << 18) | \ - (AMD_ENC_ASSOC(L3_N_ASSOCIATIVITY) << 12) | \ - (L3_N_LINES_PER_TAG << 8) | (L3_N_LINE_SIZE); - } + encode_cache_cpuid80000006(&l2_cache_amd, + cpu->enable_l3_cache ? &l3_cache : NULL, + ecx, edx); break; case 0x80000007: *eax = 0; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8fbe1537c1..512c69dddd 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1045,6 +1045,59 @@ typedef enum TPRAccess { TPR_ACCESS_WRITE, } TPRAccess; +/* Cache information data structures: */ + +enum CacheType { + DCACHE, + ICACHE, + UNIFIED_CACHE +}; + +typedef struct CPUCacheInfo { + enum CacheType type; + uint8_t level; + /* Size in bytes */ + uint32_t size; + /* Line size, in bytes */ + uint16_t line_size; + /* + * Associativity. + * Note: representation of fully-associative caches is not implemented + */ + uint8_t associativity; + /* Physical line partitions. CPUID[0x8000001D].EBX, CPUID[4].EBX */ + uint8_t partitions; + /* Number of sets. CPUID[0x8000001D].ECX, CPUID[4].ECX */ + uint32_t sets; + /* + * Lines per tag. + * AMD-specific: CPUID[0x80000005], CPUID[0x80000006]. + * (Is this synonym to @partitions?) + */ + uint8_t lines_per_tag; + + /* Self-initializing cache */ + bool self_init; + /* + * WBINVD/INVD is not guaranteed to act upon lower level caches of + * non-originating threads sharing this cache. + * CPUID[4].EDX[bit 0], CPUID[0x8000001D].EDX[bit 0] + */ + bool no_invd_sharing; + /* + * Cache is inclusive of lower cache levels. + * CPUID[4].EDX[bit 1], CPUID[0x8000001D].EDX[bit 1]. + */ + bool inclusive; + /* + * A complex function is used to index the cache, potentially using all + * address bits. CPUID[4].EDX[bit 2]. + */ + bool complex_indexing; +} CPUCacheInfo; + + + typedef struct CPUX86State { /* standard registers */ target_ulong regs[CPU_NB_REGS]; From 6aaeb05492ef668f415324f43e7d875c0f1e90b3 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 10 May 2018 15:41:42 -0500 Subject: [PATCH 0498/2380] i386: Add cache information in X86CPUDefinition Add cache information in X86CPUDefinition and CPUX86State. Signed-off-by: Babu Moger Tested-by: Geoffrey McRae Reviewed-by: Eduardo Habkost Message-Id: <20180510204148.11687-3-babu.moger@amd.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 1 + target/i386/cpu.h | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 28bb93990e..55685ed19d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1106,6 +1106,7 @@ struct X86CPUDefinition { int stepping; FeatureWordArray features; const char *model_id; + CPUCaches *cache_info; }; static X86CPUDefinition builtin_x86_defs[] = { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 512c69dddd..ac94013c4a 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1097,6 +1097,12 @@ typedef struct CPUCacheInfo { } CPUCacheInfo; +typedef struct CPUCaches { + CPUCacheInfo l1d_cache; + CPUCacheInfo l1i_cache; + CPUCacheInfo l2_cache; + CPUCacheInfo l3_cache; +} CPUCaches; typedef struct CPUX86State { /* standard registers */ @@ -1286,6 +1292,7 @@ typedef struct CPUX86State { /* Features that were explicitly enabled/disabled */ FeatureWordArray user_features; uint32_t cpuid_model[12]; + CPUCaches *cache_info; /* MTRRs */ uint64_t mtrr_fixed[11]; From fe52acd2a054b97765963a42037f2f886545e30c Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 10 May 2018 15:41:44 -0500 Subject: [PATCH 0499/2380] i386: Initialize cache information for EPYC family processors Initialize pre-determined cache information for EPYC processors. Signed-off-by: Babu Moger Tested-by: Geoffrey McRae Message-Id: <20180510204148.11687-5-babu.moger@amd.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 55685ed19d..174a8f434b 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1109,6 +1109,56 @@ struct X86CPUDefinition { CPUCaches *cache_info; }; +static CPUCaches epyc_cache_info = { + .l1d_cache = { + .type = DCACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = 1, + .no_invd_sharing = true, + }, + .l1i_cache = { + .type = ICACHE, + .level = 1, + .size = 64 * KiB, + .line_size = 64, + .associativity = 4, + .partitions = 1, + .sets = 256, + .lines_per_tag = 1, + .self_init = 1, + .no_invd_sharing = true, + }, + .l2_cache = { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + }, + .l3_cache = { + .type = UNIFIED_CACHE, + .level = 3, + .size = 8 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 8192, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + }, +}; + static X86CPUDefinition builtin_x86_defs[] = { { .name = "qemu64", @@ -2345,6 +2395,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_6_EAX_ARAT, .xlevel = 0x8000000A, .model_id = "AMD EPYC Processor", + .cache_info = &epyc_cache_info, }, { .name = "EPYC-IBPB", @@ -2391,6 +2442,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_6_EAX_ARAT, .xlevel = 0x8000000A, .model_id = "AMD EPYC Processor (with IBPB)", + .cache_info = &epyc_cache_info, }, }; From 968ee4ad25934717b9192dfed6650a6282854e17 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Mon, 14 May 2018 11:41:50 -0500 Subject: [PATCH 0500/2380] pc: add 2.13 machine types Add pc-q35-2.13 and pc-i440fx-2.13 machine types Signed-off-by: Babu Moger Message-Id: <20180514164156.27034-2-babu.moger@amd.com> Reviewed-by: Eduardo Habkost Signed-off-by: Eduardo Habkost --- hw/i386/pc_piix.c | 15 ++++++++++++--- hw/i386/pc_q35.c | 13 +++++++++++-- include/hw/i386/pc.h | 3 +++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 729a0508aa..e36c7bbb40 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -425,21 +425,30 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_display = "std"; } -static void pc_i440fx_2_12_machine_options(MachineClass *m) +static void pc_i440fx_2_13_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; } +DEFINE_I440FX_MACHINE(v2_13, "pc-i440fx-2.13", NULL, + pc_i440fx_2_13_machine_options); + +static void pc_i440fx_2_12_machine_options(MachineClass *m) +{ + pc_i440fx_2_13_machine_options(m); + m->is_default = 0; + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); +} + DEFINE_I440FX_MACHINE(v2_12, "pc-i440fx-2.12", NULL, pc_i440fx_2_12_machine_options); static void pc_i440fx_2_11_machine_options(MachineClass *m) { pc_i440fx_2_12_machine_options(m); - m->is_default = 0; - m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_11); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 9ae916327e..2372457c6a 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -308,12 +308,22 @@ static void pc_q35_machine_options(MachineClass *m) m->max_cpus = 288; } -static void pc_q35_2_12_machine_options(MachineClass *m) +static void pc_q35_2_13_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } +DEFINE_Q35_MACHINE(v2_13, "pc-q35-2.13", NULL, + pc_q35_2_13_machine_options); + +static void pc_q35_2_12_machine_options(MachineClass *m) +{ + pc_q35_2_13_machine_options(m); + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); +} + DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL, pc_q35_2_12_machine_options); @@ -323,7 +333,6 @@ static void pc_q35_2_11_machine_options(MachineClass *m) pc_q35_2_12_machine_options(m); pcmc->default_nic_model = "e1000"; - m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_11); } diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 2a98e3ad68..d4fe073cb8 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -296,6 +296,9 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); int e820_get_num_entries(void); bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); +#define PC_COMPAT_2_12 \ + HW_COMPAT_2_12 \ + #define PC_COMPAT_2_11 \ HW_COMPAT_2_11 \ {\ From ab8f992e3e63e91be257e4e343d386dae7be4bcb Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Mon, 14 May 2018 11:41:51 -0500 Subject: [PATCH 0501/2380] i386: Add new property to control cache info The property legacy-cache will be used to control the cache information. If user passes "-cpu legacy-cache" then older information will be displayed even if the hardware supports new information. Otherwise use the statically loaded cache definitions if available. Renamed the previous cache structures to legacy_*. If there is any change in the cache information, then it needs to be initialized in builtin_x86_defs. Signed-off-by: Babu Moger Tested-by: Geoffrey McRae Message-Id: <20180514164156.27034-3-babu.moger@amd.com> Reviewed-by: Eduardo Habkost Signed-off-by: Eduardo Habkost --- include/hw/i386/pc.h | 5 +++ target/i386/cpu.c | 97 ++++++++++++++++++++++++++++++++------------ target/i386/cpu.h | 5 +++ 3 files changed, 81 insertions(+), 26 deletions(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index d4fe073cb8..a0c269fc34 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -298,6 +298,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); #define PC_COMPAT_2_12 \ HW_COMPAT_2_12 \ + {\ + .driver = TYPE_X86_CPU,\ + .property = "legacy-cache",\ + .value = "on",\ + }, #define PC_COMPAT_2_11 \ HW_COMPAT_2_11 \ diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 174a8f434b..e5e66a75d4 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -336,10 +336,14 @@ static void encode_cache_cpuid80000006(CPUCacheInfo *l2, } } -/* Definitions of the hardcoded cache entries we expose: */ +/* + * Definitions of the hardcoded cache entries we expose: + * These are legacy cache values. If there is a need to change any + * of these values please use builtin_x86_defs + */ /* L1 data cache: */ -static CPUCacheInfo l1d_cache = { +static CPUCacheInfo legacy_l1d_cache = { .type = DCACHE, .level = 1, .size = 32 * KiB, @@ -352,7 +356,7 @@ static CPUCacheInfo l1d_cache = { }; /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ -static CPUCacheInfo l1d_cache_amd = { +static CPUCacheInfo legacy_l1d_cache_amd = { .type = DCACHE, .level = 1, .size = 64 * KiB, @@ -366,7 +370,7 @@ static CPUCacheInfo l1d_cache_amd = { }; /* L1 instruction cache: */ -static CPUCacheInfo l1i_cache = { +static CPUCacheInfo legacy_l1i_cache = { .type = ICACHE, .level = 1, .size = 32 * KiB, @@ -379,7 +383,7 @@ static CPUCacheInfo l1i_cache = { }; /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ -static CPUCacheInfo l1i_cache_amd = { +static CPUCacheInfo legacy_l1i_cache_amd = { .type = ICACHE, .level = 1, .size = 64 * KiB, @@ -393,7 +397,7 @@ static CPUCacheInfo l1i_cache_amd = { }; /* Level 2 unified cache: */ -static CPUCacheInfo l2_cache = { +static CPUCacheInfo legacy_l2_cache = { .type = UNIFIED_CACHE, .level = 2, .size = 4 * MiB, @@ -406,7 +410,7 @@ static CPUCacheInfo l2_cache = { }; /*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ -static CPUCacheInfo l2_cache_cpuid2 = { +static CPUCacheInfo legacy_l2_cache_cpuid2 = { .type = UNIFIED_CACHE, .level = 2, .size = 2 * MiB, @@ -416,7 +420,7 @@ static CPUCacheInfo l2_cache_cpuid2 = { /*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ -static CPUCacheInfo l2_cache_amd = { +static CPUCacheInfo legacy_l2_cache_amd = { .type = UNIFIED_CACHE, .level = 2, .size = 512 * KiB, @@ -428,7 +432,7 @@ static CPUCacheInfo l2_cache_amd = { }; /* Level 3 unified cache: */ -static CPUCacheInfo l3_cache = { +static CPUCacheInfo legacy_l3_cache = { .type = UNIFIED_CACHE, .level = 3, .size = 16 * MiB, @@ -3338,6 +3342,10 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) env->features[w] = def->features[w]; } + /* Store Cache information from the X86CPUDefinition if available */ + env->cache_info = def->cache_info; + cpu->legacy_cache = def->cache_info ? 0 : 1; + /* Special cases not set in the X86CPUDefinition structs: */ /* TODO: in-kernel irqchip for hvf */ if (kvm_enabled()) { @@ -3687,11 +3695,21 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (!cpu->enable_l3_cache) { *ecx = 0; } else { - *ecx = cpuid2_cache_descriptor(&l3_cache); + if (env->cache_info && !cpu->legacy_cache) { + *ecx = cpuid2_cache_descriptor(&env->cache_info->l3_cache); + } else { + *ecx = cpuid2_cache_descriptor(&legacy_l3_cache); + } + } + if (env->cache_info && !cpu->legacy_cache) { + *edx = (cpuid2_cache_descriptor(&env->cache_info->l1d_cache) << 16) | + (cpuid2_cache_descriptor(&env->cache_info->l1i_cache) << 8) | + (cpuid2_cache_descriptor(&env->cache_info->l2_cache)); + } else { + *edx = (cpuid2_cache_descriptor(&legacy_l1d_cache) << 16) | + (cpuid2_cache_descriptor(&legacy_l1i_cache) << 8) | + (cpuid2_cache_descriptor(&legacy_l2_cache_cpuid2)); } - *edx = (cpuid2_cache_descriptor(&l1d_cache) << 16) | - (cpuid2_cache_descriptor(&l1i_cache) << 8) | - (cpuid2_cache_descriptor(&l2_cache_cpuid2)); break; case 4: /* cache info: needed for Core compatibility */ @@ -3704,27 +3722,35 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } else { *eax = 0; + CPUCacheInfo *l1d, *l1i, *l2, *l3; + if (env->cache_info && !cpu->legacy_cache) { + l1d = &env->cache_info->l1d_cache; + l1i = &env->cache_info->l1i_cache; + l2 = &env->cache_info->l2_cache; + l3 = &env->cache_info->l3_cache; + } else { + l1d = &legacy_l1d_cache; + l1i = &legacy_l1i_cache; + l2 = &legacy_l2_cache; + l3 = &legacy_l3_cache; + } switch (count) { case 0: /* L1 dcache info */ - encode_cache_cpuid4(&l1d_cache, - 1, cs->nr_cores, + encode_cache_cpuid4(l1d, 1, cs->nr_cores, eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ - encode_cache_cpuid4(&l1i_cache, - 1, cs->nr_cores, + encode_cache_cpuid4(l1i, 1, cs->nr_cores, eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ - encode_cache_cpuid4(&l2_cache, - cs->nr_threads, cs->nr_cores, + encode_cache_cpuid4(l2, cs->nr_threads, cs->nr_cores, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); if (cpu->enable_l3_cache) { - encode_cache_cpuid4(&l3_cache, - (1 << pkg_offset), cs->nr_cores, + encode_cache_cpuid4(l3, (1 << pkg_offset), cs->nr_cores, eax, ebx, ecx, edx); break; } @@ -3937,8 +3963,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \ (L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES); - *ecx = encode_cache_cpuid80000005(&l1d_cache_amd); - *edx = encode_cache_cpuid80000005(&l1i_cache_amd); + if (env->cache_info && !cpu->legacy_cache) { + *ecx = encode_cache_cpuid80000005(&env->cache_info->l1d_cache); + *edx = encode_cache_cpuid80000005(&env->cache_info->l1i_cache); + } else { + *ecx = encode_cache_cpuid80000005(&legacy_l1d_cache_amd); + *edx = encode_cache_cpuid80000005(&legacy_l1i_cache_amd); + } break; case 0x80000006: /* cache info (L2 cache) */ @@ -3954,9 +3985,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (L2_DTLB_4K_ENTRIES << 16) | \ (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \ (L2_ITLB_4K_ENTRIES); - encode_cache_cpuid80000006(&l2_cache_amd, - cpu->enable_l3_cache ? &l3_cache : NULL, - ecx, edx); + if (env->cache_info && !cpu->legacy_cache) { + encode_cache_cpuid80000006(&env->cache_info->l2_cache, + cpu->enable_l3_cache ? + &env->cache_info->l3_cache : NULL, + ecx, edx); + } else { + encode_cache_cpuid80000006(&legacy_l2_cache_amd, + cpu->enable_l3_cache ? + &legacy_l3_cache : NULL, + ecx, edx); + } break; case 0x80000007: *eax = 0; @@ -5135,6 +5174,12 @@ static Property x86_cpu_properties[] = { false), DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true), + /* + * lecacy_cache defaults to CPU model being chosen. This is set in + * x86_cpu_load_def based on cache_info which is initialized in + * builtin_x86_defs + */ + DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, false), /* * From "Requirements for Implementing the Microsoft diff --git a/target/i386/cpu.h b/target/i386/cpu.h index ac94013c4a..8bc54d70bf 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1399,6 +1399,11 @@ struct X86CPU { */ bool enable_l3_cache; + /* Compatibility bits for old machine types. + * If true present the old cache topology information + */ + bool legacy_cache; + /* Compatibility bits for old machine types: */ bool enable_cpuid_0xb; From 701b1876c0fc0c583e4aff300ace5d33a1b97ed6 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Sat, 28 Apr 2018 16:10:45 +0800 Subject: [PATCH 0502/2380] migration: fix saving normal page even if it's been compressed Fix the bug introduced by da3f56cb2e767016 (migration: remove ram_save_compressed_page()), It should be 'return' rather than 'res' Sorry for this stupid mistake :( Signed-off-by: Xiao Guangrong Message-Id: <20180428081045.8878-1-xiaoguangrong@tencent.com> Signed-off-by: Juan Quintela --- migration/ram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index 912810c18e..da0b567003 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1490,7 +1490,7 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, * CPU resource. */ if (block == rs->last_sent_block && save_page_use_compression(rs)) { - res = compress_page_with_multi_thread(rs, block, offset); + return compress_page_with_multi_thread(rs, block, offset); } return ram_save_page(rs, pss, last_stage); From 2884100cc68d0b2ee1ed14da84790659df347505 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 5 Jan 2018 13:26:01 +0100 Subject: [PATCH 0503/2380] tests: Add migration precopy test Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Peter Xu --- tests/migration-test.c | 44 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/tests/migration-test.c b/tests/migration-test.c index b99661b773..2b9c0ce91e 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -536,7 +536,7 @@ static void test_deprecated(void) qtest_quit(from); } -static void test_migrate(void) +static void test_postcopy(void) { char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; @@ -611,6 +611,45 @@ static void test_baddest(void) test_migrate_end(from, to, false); } +static void test_precopy_unix(void) +{ + char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + + test_migrate_start(&from, &to, uri, false); + + /* We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + /* 1 ms should make it not converge*/ + migrate_set_parameter(from, "downtime-limit", "1"); + /* 1GB/s */ + migrate_set_parameter(from, "max-bandwidth", "1000000000"); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate(from, uri); + + wait_for_migration_pass(from); + + /* 300 ms should converge */ + migrate_set_parameter(from, "downtime-limit", "300"); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + test_migrate_end(from, to, true); + g_free(uri); +} + int main(int argc, char **argv) { char template[] = "/tmp/migration-test-XXXXXX"; @@ -630,9 +669,10 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); - qtest_add_func("/migration/postcopy/unix", test_migrate); + qtest_add_func("/migration/postcopy/unix", test_postcopy); qtest_add_func("/migration/deprecated", test_deprecated); qtest_add_func("/migration/bad_dest", test_baddest); + qtest_add_func("/migration/precopy/unix", test_precopy_unix); ret = g_test_run(); From cdf338152f64616d4c1f880861fec7cf0df306ae Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 18 Jan 2018 18:44:17 +0100 Subject: [PATCH 0504/2380] tests: Migration ppc now inlines its program No need to write it to a file. Just need a proper firmware O:-) Signed-off-by: Juan Quintela Reviewed-by: Laurent Vivier Reviewed-by: Thomas Huth --- tests/migration-test.c | 41 +++++------------------------------------ 1 file changed, 5 insertions(+), 36 deletions(-) diff --git a/tests/migration-test.c b/tests/migration-test.c index 2b9c0ce91e..3a85446f95 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -19,9 +19,6 @@ #include "qemu/sockets.h" #include "chardev/char.h" #include "sysemu/sysemu.h" -#include "hw/nvram/chrp_nvram.h" - -#define MIN_NVRAM_SIZE 8192 /* from spapr_nvram.c */ const unsigned start_address = 1024 * 1024; const unsigned end_address = 100 * 1024 * 1024; @@ -92,36 +89,6 @@ static void init_bootfile_x86(const char *bootpath) fclose(bootfile); } -static void init_bootfile_ppc(const char *bootpath) -{ - FILE *bootfile; - char buf[MIN_NVRAM_SIZE]; - ChrpNvramPartHdr *header = (ChrpNvramPartHdr *)buf; - - memset(buf, 0, MIN_NVRAM_SIZE); - - /* Create a "common" partition in nvram to store boot-command property */ - - header->signature = CHRP_NVPART_SYSTEM; - memcpy(header->name, "common", 6); - chrp_nvram_finish_partition(header, MIN_NVRAM_SIZE); - - /* FW_MAX_SIZE is 4MB, but slof.bin is only 900KB, - * so let's modify memory between 1MB and 100MB - * to do like PC bootsector - */ - - sprintf(buf + 16, - "boot-command=hex .\" _\" begin %x %x do i c@ 1 + i c! 1000 +loop " - ".\" B\" 0 until", end_address, start_address); - - /* Write partition to the NVRAM file */ - - bootfile = fopen(bootpath, "wb"); - g_assert_cmpint(fwrite(buf, MIN_NVRAM_SIZE, 1, bootfile), ==, 1); - fclose(bootfile); -} - /* * Wait for some output in the serial output file, * we get an 'A' followed by an endless string of 'B's @@ -422,12 +389,14 @@ static void test_migrate_start(QTestState **from, QTestState **to, if (access("/sys/module/kvm_hv", F_OK)) { accel = "tcg"; } - init_bootfile_ppc(bootpath); cmd_src = g_strdup_printf("-machine accel=%s -m 256M" " -name source,debug-threads=on" " -serial file:%s/src_serial" - " -drive file=%s,if=pflash,format=raw", - accel, tmpfs, bootpath); + " -prom-env '" + "boot-command=hex .\" _\" begin %x %x " + "do i c@ 1 + i c! 1000 +loop .\" B\" 0 " + "until'", accel, tmpfs, end_address, + start_address); cmd_dst = g_strdup_printf("-machine accel=%s -m 256M" " -name target,debug-threads=on" " -serial file:%s/dest_serial" From 7a169d745c854bb722a978c63d1f9edb05f04cd6 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 19 Feb 2018 19:01:15 +0100 Subject: [PATCH 0505/2380] migration: Set error state in case of error Signed-off-by: Juan Quintela --- migration/ram.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index da0b567003..4d8be30676 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -448,10 +448,22 @@ struct { int count; } *multifd_send_state; -static void terminate_multifd_send_threads(Error *errp) +static void terminate_multifd_send_threads(Error *err) { int i; + if (err) { + MigrationState *s = migrate_get_current(); + migrate_set_error(s, err); + if (s->state == MIGRATION_STATUS_SETUP || + s->state == MIGRATION_STATUS_PRE_SWITCHOVER || + s->state == MIGRATION_STATUS_DEVICE || + s->state == MIGRATION_STATUS_ACTIVE) { + migrate_set_state(&s->state, s->state, + MIGRATION_STATUS_FAILED); + } + } + for (i = 0; i < multifd_send_state->count; i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; @@ -548,10 +560,20 @@ struct { int count; } *multifd_recv_state; -static void terminate_multifd_recv_threads(Error *errp) +static void terminate_multifd_recv_threads(Error *err) { int i; + if (err) { + MigrationState *s = migrate_get_current(); + migrate_set_error(s, err); + if (s->state == MIGRATION_STATUS_SETUP || + s->state == MIGRATION_STATUS_ACTIVE) { + migrate_set_state(&s->state, s->state, + MIGRATION_STATUS_FAILED); + } + } + for (i = 0; i < multifd_recv_state->count; i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; From 71bb07dbfcd465236738ee4b1cde6b80989e94bf Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 19 Feb 2018 19:01:03 +0100 Subject: [PATCH 0506/2380] migration: Introduce multifd_recv_new_channel() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Juan Quintela Reviewed-by: Daniel P. Berrangé --- migration/migration.c | 3 ++- migration/ram.c | 6 ++++++ migration/ram.h | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index 35f2781b03..4a7959c111 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -449,8 +449,9 @@ void migration_ioc_process_incoming(QIOChannel *ioc) if (!mis->from_src_file) { QEMUFile *f = qemu_fopen_channel_input(ioc); migration_fd_process_incoming(f); + return; } - /* We still only have a single channel. Nothing to do here yet */ + multifd_recv_new_channel(ioc); } /** diff --git a/migration/ram.c b/migration/ram.c index 4d8be30676..826172c342 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -36,6 +36,7 @@ #include "xbzrle.h" #include "ram.h" #include "migration.h" +#include "socket.h" #include "migration/register.h" #include "migration/misc.h" #include "qemu-file.h" @@ -654,6 +655,11 @@ int multifd_load_setup(void) return 0; } +void multifd_recv_new_channel(QIOChannel *ioc) +{ + /* nothing to do yet */ +} + /** * save_page_header: write page header to wire * diff --git a/migration/ram.h b/migration/ram.h index 5030be110a..06dbddc2a2 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -32,6 +32,7 @@ #include "qemu-common.h" #include "qapi/qapi-types-migration.h" #include "exec/cpu-common.h" +#include "io/channel.h" extern MigrationStats ram_counters; extern XBZRLECacheStats xbzrle_counters; @@ -44,6 +45,7 @@ int multifd_save_setup(void); int multifd_save_cleanup(Error **errp); int multifd_load_setup(void); int multifd_load_cleanup(Error **errp); +void multifd_recv_new_channel(QIOChannel *ioc); uint64_t ram_pagesize_summary(void); int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); From 667707078d8d365d449d1908805c09506f67825f Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 19 Feb 2018 19:01:45 +0100 Subject: [PATCH 0507/2380] migration: terminate_* can be called for other threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once there, make count field to always be accessed with atomic operations. To make blocking operations, we need to know that the thread is running, so create a bool to indicate that. Signed-off-by: Juan Quintela Reviewed-by: Daniel P. Berrangé -- Once here, s/terminate_multifd_*-threads/multifd_*_terminate_threads/ This is consistente with every other function --- migration/ram.c | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 826172c342..1aa661e6ab 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -439,6 +439,7 @@ struct MultiFDSendParams { QemuThread thread; QemuSemaphore sem; QemuMutex mutex; + bool running; bool quit; }; typedef struct MultiFDSendParams MultiFDSendParams; @@ -449,7 +450,7 @@ struct { int count; } *multifd_send_state; -static void terminate_multifd_send_threads(Error *err) +static void multifd_send_terminate_threads(Error *err) { int i; @@ -465,7 +466,7 @@ static void terminate_multifd_send_threads(Error *err) } } - for (i = 0; i < multifd_send_state->count; i++) { + for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; qemu_mutex_lock(&p->mutex); @@ -483,11 +484,13 @@ int multifd_save_cleanup(Error **errp) if (!migrate_use_multifd()) { return 0; } - terminate_multifd_send_threads(NULL); - for (i = 0; i < multifd_send_state->count; i++) { + multifd_send_terminate_threads(NULL); + for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; - qemu_thread_join(&p->thread); + if (p->running) { + qemu_thread_join(&p->thread); + } qemu_mutex_destroy(&p->mutex); qemu_sem_destroy(&p->sem); g_free(p->name); @@ -514,6 +517,10 @@ static void *multifd_send_thread(void *opaque) qemu_sem_wait(&p->sem); } + qemu_mutex_lock(&p->mutex); + p->running = false; + qemu_mutex_unlock(&p->mutex); + return NULL; } @@ -528,7 +535,7 @@ int multifd_save_setup(void) thread_count = migrate_multifd_channels(); multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); - multifd_send_state->count = 0; + atomic_set(&multifd_send_state->count, 0); for (i = 0; i < thread_count; i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; @@ -537,10 +544,11 @@ int multifd_save_setup(void) p->quit = false; p->id = i; p->name = g_strdup_printf("multifdsend_%d", i); + p->running = true; qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, QEMU_THREAD_JOINABLE); - multifd_send_state->count++; + atomic_inc(&multifd_send_state->count); } return 0; } @@ -551,6 +559,7 @@ struct MultiFDRecvParams { QemuThread thread; QemuSemaphore sem; QemuMutex mutex; + bool running; bool quit; }; typedef struct MultiFDRecvParams MultiFDRecvParams; @@ -561,7 +570,7 @@ struct { int count; } *multifd_recv_state; -static void terminate_multifd_recv_threads(Error *err) +static void multifd_recv_terminate_threads(Error *err) { int i; @@ -575,7 +584,7 @@ static void terminate_multifd_recv_threads(Error *err) } } - for (i = 0; i < multifd_recv_state->count; i++) { + for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; qemu_mutex_lock(&p->mutex); @@ -593,11 +602,13 @@ int multifd_load_cleanup(Error **errp) if (!migrate_use_multifd()) { return 0; } - terminate_multifd_recv_threads(NULL); - for (i = 0; i < multifd_recv_state->count; i++) { + multifd_recv_terminate_threads(NULL); + for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; - qemu_thread_join(&p->thread); + if (p->running) { + qemu_thread_join(&p->thread); + } qemu_mutex_destroy(&p->mutex); qemu_sem_destroy(&p->sem); g_free(p->name); @@ -625,6 +636,10 @@ static void *multifd_recv_thread(void *opaque) qemu_sem_wait(&p->sem); } + qemu_mutex_lock(&p->mutex); + p->running = false; + qemu_mutex_unlock(&p->mutex); + return NULL; } @@ -639,7 +654,7 @@ int multifd_load_setup(void) thread_count = migrate_multifd_channels(); multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state)); multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count); - multifd_recv_state->count = 0; + atomic_set(&multifd_recv_state->count, 0); for (i = 0; i < thread_count; i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; @@ -648,9 +663,10 @@ int multifd_load_setup(void) p->quit = false; p->id = i; p->name = g_strdup_printf("multifdrecv_%d", i); + p->running = true; qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, QEMU_THREAD_JOINABLE); - multifd_recv_state->count++; + atomic_inc(&multifd_recv_state->count); } return 0; } From 62c1e0ca739008aaec6d3a18456f479bed61713b Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 19 Feb 2018 18:59:02 +0100 Subject: [PATCH 0508/2380] migration: Be sure all recv channels are created MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need them before we start migration. Signed-off-by: Juan Quintela Reviewed-by: Daniel P. Berrangé --- migration/migration.c | 6 +++++- migration/ram.c | 11 +++++++++++ migration/ram.h | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index 4a7959c111..8e5b421b97 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -462,7 +462,11 @@ void migration_ioc_process_incoming(QIOChannel *ioc) */ bool migration_has_all_channels(void) { - return true; + bool all_channels; + + all_channels = multifd_recv_all_channels_created(); + + return all_channels; } /* diff --git a/migration/ram.c b/migration/ram.c index 1aa661e6ab..d5335c10b6 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -671,6 +671,17 @@ int multifd_load_setup(void) return 0; } +bool multifd_recv_all_channels_created(void) +{ + int thread_count = migrate_multifd_channels(); + + if (!migrate_use_multifd()) { + return true; + } + + return thread_count == atomic_read(&multifd_recv_state->count); +} + void multifd_recv_new_channel(QIOChannel *ioc) { /* nothing to do yet */ diff --git a/migration/ram.h b/migration/ram.h index 06dbddc2a2..3f4b7daee8 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -45,6 +45,7 @@ int multifd_save_setup(void); int multifd_save_cleanup(Error **errp); int multifd_load_setup(void); int multifd_load_cleanup(Error **errp); +bool multifd_recv_all_channels_created(void); void multifd_recv_new_channel(QIOChannel *ioc); uint64_t ram_pagesize_summary(void); From 3854956ad7ea0a4a29f434a026eeea9bae250d0d Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 28 Feb 2018 12:05:15 +0100 Subject: [PATCH 0509/2380] migration: Export functions to create send channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Juan Quintela Reviewed-by: Daniel P. Berrangé --- migration/socket.c | 28 +++++++++++++++++++++++++++- migration/socket.h | 7 +++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/migration/socket.c b/migration/socket.c index 122d8ccfbe..e09fd1aae5 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -28,6 +28,28 @@ #include "trace.h" +struct SocketOutgoingArgs { + SocketAddress *saddr; +} outgoing_args; + +void socket_send_channel_create(QIOTaskFunc f, void *data) +{ + QIOChannelSocket *sioc = qio_channel_socket_new(); + qio_channel_socket_connect_async(sioc, outgoing_args.saddr, + f, data, NULL, NULL); +} + +int socket_send_channel_destroy(QIOChannel *send) +{ + /* Remove channel */ + object_unref(OBJECT(send)); + if (outgoing_args.saddr) { + qapi_free_SocketAddress(outgoing_args.saddr); + outgoing_args.saddr = NULL; + } + return 0; +} + static SocketAddress *tcp_build_address(const char *host_port, Error **errp) { SocketAddress *saddr; @@ -95,6 +117,11 @@ static void socket_start_outgoing_migration(MigrationState *s, struct SocketConnectData *data = g_new0(struct SocketConnectData, 1); data->s = s; + + /* in case previous migration leaked it */ + qapi_free_SocketAddress(outgoing_args.saddr); + outgoing_args.saddr = saddr; + if (saddr->type == SOCKET_ADDRESS_TYPE_INET) { data->hostname = g_strdup(saddr->u.inet.host); } @@ -106,7 +133,6 @@ static void socket_start_outgoing_migration(MigrationState *s, data, socket_connect_data_free, NULL); - qapi_free_SocketAddress(saddr); } void tcp_start_outgoing_migration(MigrationState *s, diff --git a/migration/socket.h b/migration/socket.h index 6b91e9db38..528c3b0202 100644 --- a/migration/socket.h +++ b/migration/socket.h @@ -16,6 +16,13 @@ #ifndef QEMU_MIGRATION_SOCKET_H #define QEMU_MIGRATION_SOCKET_H + +#include "io/channel.h" +#include "io/task.h" + +void socket_send_channel_create(QIOTaskFunc f, void *data); +int socket_send_channel_destroy(QIOChannel *send); + void tcp_start_incoming_migration(const char *host_port, Error **errp); void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, From 60df2d4ae59837d1a5afced71ec62a0c86c09f50 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 7 Mar 2018 07:56:15 +0100 Subject: [PATCH 0510/2380] migration: Create multifd channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In both sides. We still don't transmit anything through them. Signed-off-by: Juan Quintela Reviewed-by: Daniel P. Berrangé --- migration/ram.c | 52 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index d5335c10b6..87434d3fce 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -437,6 +437,7 @@ struct MultiFDSendParams { uint8_t id; char *name; QemuThread thread; + QIOChannel *c; QemuSemaphore sem; QemuMutex mutex; bool running; @@ -491,6 +492,8 @@ int multifd_save_cleanup(Error **errp) if (p->running) { qemu_thread_join(&p->thread); } + socket_send_channel_destroy(p->c); + p->c = NULL; qemu_mutex_destroy(&p->mutex); qemu_sem_destroy(&p->sem); g_free(p->name); @@ -524,6 +527,27 @@ static void *multifd_send_thread(void *opaque) return NULL; } +static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) +{ + MultiFDSendParams *p = opaque; + QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task)); + Error *local_err = NULL; + + if (qio_task_propagate_error(task, &local_err)) { + if (multifd_save_cleanup(&local_err) != 0) { + migrate_set_error(migrate_get_current(), local_err); + } + } else { + p->c = QIO_CHANNEL(sioc); + qio_channel_set_delay(p->c, false); + p->running = true; + qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, + QEMU_THREAD_JOINABLE); + + atomic_inc(&multifd_send_state->count); + } +} + int multifd_save_setup(void) { int thread_count; @@ -544,11 +568,7 @@ int multifd_save_setup(void) p->quit = false; p->id = i; p->name = g_strdup_printf("multifdsend_%d", i); - p->running = true; - qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, - QEMU_THREAD_JOINABLE); - - atomic_inc(&multifd_send_state->count); + socket_send_channel_create(multifd_new_send_channel_async, p); } return 0; } @@ -557,6 +577,7 @@ struct MultiFDRecvParams { uint8_t id; char *name; QemuThread thread; + QIOChannel *c; QemuSemaphore sem; QemuMutex mutex; bool running; @@ -609,6 +630,8 @@ int multifd_load_cleanup(Error **errp) if (p->running) { qemu_thread_join(&p->thread); } + object_unref(OBJECT(p->c)); + p->c = NULL; qemu_mutex_destroy(&p->mutex); qemu_sem_destroy(&p->sem); g_free(p->name); @@ -663,10 +686,6 @@ int multifd_load_setup(void) p->quit = false; p->id = i; p->name = g_strdup_printf("multifdrecv_%d", i); - p->running = true; - qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, - QEMU_THREAD_JOINABLE); - atomic_inc(&multifd_recv_state->count); } return 0; } @@ -684,7 +703,20 @@ bool multifd_recv_all_channels_created(void) void multifd_recv_new_channel(QIOChannel *ioc) { - /* nothing to do yet */ + MultiFDRecvParams *p; + /* we need to invent channels id's until we transmit */ + /* we will remove this on a later patch */ + static int i; + + p = &multifd_recv_state->params[i]; + i++; + p->c = ioc; + object_ref(OBJECT(ioc)); + + p->running = true; + qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, + QEMU_THREAD_JOINABLE); + atomic_inc(&multifd_recv_state->count); } /** From 36c2f8be2c4eb0003ac77a14910842b7ddd7337e Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 7 Mar 2018 08:40:52 +0100 Subject: [PATCH 0511/2380] migration: Delay start of migration main routines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to make sure that we have started all the multifd threads. Signed-off-by: Juan Quintela Reviewed-by: Daniel P. Berrangé --- migration/migration.c | 4 ++-- migration/migration.h | 1 + migration/ram.c | 3 +++ migration/socket.c | 4 ++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 8e5b421b97..61c4ee7850 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -430,7 +430,7 @@ static void migration_incoming_setup(QEMUFile *f) qemu_file_set_blocking(f, false); } -static void migration_incoming_process(void) +void migration_incoming_process(void) { Coroutine *co = qemu_coroutine_create(process_incoming_migration_co, NULL); qemu_coroutine_enter(co); @@ -448,7 +448,7 @@ void migration_ioc_process_incoming(QIOChannel *ioc) if (!mis->from_src_file) { QEMUFile *f = qemu_fopen_channel_input(ioc); - migration_fd_process_incoming(f); + migration_incoming_setup(f); return; } multifd_recv_new_channel(ioc); diff --git a/migration/migration.h b/migration/migration.h index 7c69598c54..26e5951d56 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -200,6 +200,7 @@ void migrate_set_state(int *state, int old_state, int new_state); void migration_fd_process_incoming(QEMUFile *f); void migration_ioc_process_incoming(QIOChannel *ioc); +void migration_incoming_process(void); bool migration_has_all_channels(void); diff --git a/migration/ram.c b/migration/ram.c index 87434d3fce..f7e8615e15 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -717,6 +717,9 @@ void multifd_recv_new_channel(QIOChannel *ioc) qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, QEMU_THREAD_JOINABLE); atomic_inc(&multifd_recv_state->count); + if (multifd_recv_state->count == migrate_multifd_channels()) { + migration_incoming_process(); + } } /** diff --git a/migration/socket.c b/migration/socket.c index e09fd1aae5..7a5eb562b8 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -170,6 +170,10 @@ static void socket_accept_incoming_migration(QIONetListener *listener, qio_net_listener_disconnect(listener); object_unref(OBJECT(listener)); + + if (!migrate_use_multifd()) { + migration_incoming_process(); + } } } From af8b7d2b09471051aba8e6ed5c468469a1ea8309 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 6 Apr 2018 19:32:12 +0200 Subject: [PATCH 0512/2380] migration: Transmit initial package through the multifd channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Juan Quintela Reviewed-by: Daniel P. Berrangé -- Be network agnostic. Add error checking for all values. --- migration/ram.c | 104 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index f7e8615e15..f46a373074 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -52,6 +52,8 @@ #include "qemu/rcu_queue.h" #include "migration/colo.h" #include "migration/block.h" +#include "sysemu/sysemu.h" +#include "qemu/uuid.h" /***********************************************************/ /* ram save/restore */ @@ -433,6 +435,16 @@ exit: /* Multiple fd's */ +#define MULTIFD_MAGIC 0x11223344U +#define MULTIFD_VERSION 1 + +typedef struct { + uint32_t magic; + uint32_t version; + unsigned char uuid[16]; /* QemuUUID */ + uint8_t id; +} __attribute__((packed)) MultiFDInit_t; + struct MultiFDSendParams { uint8_t id; char *name; @@ -445,6 +457,68 @@ struct MultiFDSendParams { }; typedef struct MultiFDSendParams MultiFDSendParams; +static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) +{ + MultiFDInit_t msg; + int ret; + + msg.magic = cpu_to_be32(MULTIFD_MAGIC); + msg.version = cpu_to_be32(MULTIFD_VERSION); + msg.id = p->id; + memcpy(msg.uuid, &qemu_uuid.data, sizeof(msg.uuid)); + + ret = qio_channel_write_all(p->c, (char *)&msg, sizeof(msg), errp); + if (ret != 0) { + return -1; + } + return 0; +} + +static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) +{ + MultiFDInit_t msg; + int ret; + + ret = qio_channel_read_all(c, (char *)&msg, sizeof(msg), errp); + if (ret != 0) { + return -1; + } + + be32_to_cpus(&msg.magic); + be32_to_cpus(&msg.version); + + if (msg.magic != MULTIFD_MAGIC) { + error_setg(errp, "multifd: received packet magic %x " + "expected %x", msg.magic, MULTIFD_MAGIC); + return -1; + } + + if (msg.version != MULTIFD_VERSION) { + error_setg(errp, "multifd: received packet version %d " + "expected %d", msg.version, MULTIFD_VERSION); + return -1; + } + + if (memcmp(msg.uuid, &qemu_uuid, sizeof(qemu_uuid))) { + char *uuid = qemu_uuid_unparse_strdup(&qemu_uuid); + char *msg_uuid = qemu_uuid_unparse_strdup((const QemuUUID *)msg.uuid); + + error_setg(errp, "multifd: received uuid '%s' and expected " + "uuid '%s' for channel %hhd", msg_uuid, uuid, msg.id); + g_free(uuid); + g_free(msg_uuid); + return -1; + } + + if (msg.id > migrate_multifd_channels()) { + error_setg(errp, "multifd: received channel version %d " + "expected %d", msg.version, MULTIFD_VERSION); + return -1; + } + + return msg.id; +} + struct { MultiFDSendParams *params; /* number of created threads */ @@ -509,6 +583,11 @@ int multifd_save_cleanup(Error **errp) static void *multifd_send_thread(void *opaque) { MultiFDSendParams *p = opaque; + Error *local_err = NULL; + + if (multifd_send_initial_packet(p, &local_err) < 0) { + goto out; + } while (true) { qemu_mutex_lock(&p->mutex); @@ -520,6 +599,11 @@ static void *multifd_send_thread(void *opaque) qemu_sem_wait(&p->sem); } +out: + if (local_err) { + multifd_send_terminate_threads(local_err); + } + qemu_mutex_lock(&p->mutex); p->running = false; qemu_mutex_unlock(&p->mutex); @@ -704,12 +788,22 @@ bool multifd_recv_all_channels_created(void) void multifd_recv_new_channel(QIOChannel *ioc) { MultiFDRecvParams *p; - /* we need to invent channels id's until we transmit */ - /* we will remove this on a later patch */ - static int i; + Error *local_err = NULL; + int id; - p = &multifd_recv_state->params[i]; - i++; + id = multifd_recv_initial_packet(ioc, &local_err); + if (id < 0) { + multifd_recv_terminate_threads(local_err); + return; + } + + p = &multifd_recv_state->params[id]; + if (p->c != NULL) { + error_setg(&local_err, "multifd: received id '%d' already setup'", + id); + multifd_recv_terminate_threads(local_err); + return; + } p->c = ioc; object_ref(OBJECT(ioc)); From 8c4598f2b1f65ca063d0d5e0dc7f621e2023a0fd Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Sat, 7 Apr 2018 13:59:07 +0200 Subject: [PATCH 0513/2380] migration: Define MultifdRecvParams sooner Once there, we don't need the struct names anywhere, just the typedefs. And now also document all fields. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/ram.c | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index f46a373074..cb14399ef9 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -445,17 +445,45 @@ typedef struct { uint8_t id; } __attribute__((packed)) MultiFDInit_t; -struct MultiFDSendParams { +typedef struct { + /* this fields are not changed once the thread is created */ + /* channel number */ uint8_t id; + /* channel thread name */ char *name; + /* channel thread id */ QemuThread thread; + /* communication channel */ QIOChannel *c; + /* sem where to wait for more work */ QemuSemaphore sem; + /* this mutex protects the following parameters */ QemuMutex mutex; + /* is this channel thread running */ bool running; + /* should this thread finish */ bool quit; -}; -typedef struct MultiFDSendParams MultiFDSendParams; +} MultiFDSendParams; + +typedef struct { + /* this fields are not changed once the thread is created */ + /* channel number */ + uint8_t id; + /* channel thread name */ + char *name; + /* channel thread id */ + QemuThread thread; + /* communication channel */ + QIOChannel *c; + /* sem where to wait for more work */ + QemuSemaphore sem; + /* this mutex protects the following parameters */ + QemuMutex mutex; + /* is this channel thread running */ + bool running; + /* should this thread finish */ + bool quit; +} MultiFDRecvParams; static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) { @@ -657,18 +685,6 @@ int multifd_save_setup(void) return 0; } -struct MultiFDRecvParams { - uint8_t id; - char *name; - QemuThread thread; - QIOChannel *c; - QemuSemaphore sem; - QemuMutex mutex; - bool running; - bool quit; -}; -typedef struct MultiFDRecvParams MultiFDRecvParams; - struct { MultiFDRecvParams *params; /* number of created threads */ From e89f5ff2c305a335dd42091ab379f1e38df6f161 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:17 +0800 Subject: [PATCH 0514/2380] migration: let incoming side use thread context The old incoming migration is running in main thread and default gcontext. With the new qio_channel_add_watch_full() we can now let it run in the thread's own gcontext (if there is one). Currently this patch does nothing alone. But when any of the incoming migration is run in another iothread (e.g., the upcoming migrate-recover command), this patch will bind the incoming logic to the iothread instead of the main thread (which may already get page faulted and hanged). RDMA is not considered for now since it's not even using the QIO watch framework at all. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-2-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/exec.c | 9 ++++----- migration/fd.c | 9 ++++----- migration/socket.c | 7 ++++--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/migration/exec.c b/migration/exec.c index 0bc5a427dd..9d0f82f1f0 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -65,9 +65,8 @@ void exec_start_incoming_migration(const char *command, Error **errp) } qio_channel_set_name(ioc, "migration-exec-incoming"); - qio_channel_add_watch(ioc, - G_IO_IN, - exec_accept_incoming_migration, - NULL, - NULL); + qio_channel_add_watch_full(ioc, G_IO_IN, + exec_accept_incoming_migration, + NULL, NULL, + g_main_context_get_thread_default()); } diff --git a/migration/fd.c b/migration/fd.c index cd06182d1e..9a380bbbc4 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -66,9 +66,8 @@ void fd_start_incoming_migration(const char *infd, Error **errp) } qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-incoming"); - qio_channel_add_watch(ioc, - G_IO_IN, - fd_accept_incoming_migration, - NULL, - NULL); + qio_channel_add_watch_full(ioc, G_IO_IN, + fd_accept_incoming_migration, + NULL, NULL, + g_main_context_get_thread_default()); } diff --git a/migration/socket.c b/migration/socket.c index 7a5eb562b8..3456eb76e9 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -190,9 +190,10 @@ static void socket_start_incoming_migration(SocketAddress *saddr, return; } - qio_net_listener_set_client_func(listener, - socket_accept_incoming_migration, - NULL, NULL); + qio_net_listener_set_client_func_full(listener, + socket_accept_incoming_migration, + NULL, NULL, + g_main_context_get_thread_default()); } void tcp_start_incoming_migration(const char *host_port, Error **errp) From a688d2c1abc791254fddd1366ecc11f8f5c6eb7a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:18 +0800 Subject: [PATCH 0515/2380] migration: new postcopy-pause state Introducing a new state "postcopy-paused", which can be used when the postcopy migration is paused. It is targeted for postcopy network failure recovery. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-3-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 2 ++ qapi/migration.json | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index 61c4ee7850..02ebd6c9d1 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -574,6 +574,7 @@ static bool migration_is_setup_or_active(int state) switch (state) { case MIGRATION_STATUS_ACTIVE: case MIGRATION_STATUS_POSTCOPY_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_PAUSED: case MIGRATION_STATUS_SETUP: case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: @@ -654,6 +655,7 @@ static void fill_source_migration_info(MigrationInfo *info) case MIGRATION_STATUS_POSTCOPY_ACTIVE: case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: + case MIGRATION_STATUS_POSTCOPY_PAUSED: /* TODO add some postcopy stats */ info->has_status = true; info->has_total_time = true; diff --git a/qapi/migration.json b/qapi/migration.json index f3974c6807..244334e9f4 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -89,6 +89,8 @@ # # @postcopy-active: like active, but now in postcopy mode. (since 2.5) # +# @postcopy-paused: during postcopy but paused. (since 2.13) +# # @completed: migration is finished. # # @failed: some error occurred during migration process. @@ -106,7 +108,8 @@ ## { 'enum': 'MigrationStatus', 'data': [ 'none', 'setup', 'cancelling', 'cancelled', - 'active', 'postcopy-active', 'completed', 'failed', 'colo', + 'active', 'postcopy-active', 'postcopy-paused', + 'completed', 'failed', 'colo', 'pre-switchover', 'device' ] } ## From b23c2ade250718fe77b51e116f95f3c34c8a4c24 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:19 +0800 Subject: [PATCH 0516/2380] migration: implement "postcopy-pause" src logic Now when network down for postcopy, the source side will not fail the migration. Instead we convert the status into this new paused state, and we will try to wait for a rescue in the future. If a recovery is detected, migration_thread() will reset its local variables to prepare for that. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-4-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 99 +++++++++++++++++++++++++++++++++++++++--- migration/migration.h | 3 ++ migration/trace-events | 1 + 3 files changed, 97 insertions(+), 6 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 02ebd6c9d1..8392cf467d 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2245,6 +2245,80 @@ bool migrate_colo_enabled(void) return s->enabled_capabilities[MIGRATION_CAPABILITY_X_COLO]; } +typedef enum MigThrError { + /* No error detected */ + MIG_THR_ERR_NONE = 0, + /* Detected error, but resumed successfully */ + MIG_THR_ERR_RECOVERED = 1, + /* Detected fatal error, need to exit */ + MIG_THR_ERR_FATAL = 2, +} MigThrError; + +/* + * We don't return until we are in a safe state to continue current + * postcopy migration. Returns MIG_THR_ERR_RECOVERED if recovered, or + * MIG_THR_ERR_FATAL if unrecovery failure happened. + */ +static MigThrError postcopy_pause(MigrationState *s) +{ + assert(s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); + migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, + MIGRATION_STATUS_POSTCOPY_PAUSED); + + /* Current channel is possibly broken. Release it. */ + assert(s->to_dst_file); + qemu_file_shutdown(s->to_dst_file); + qemu_fclose(s->to_dst_file); + s->to_dst_file = NULL; + + error_report("Detected IO failure for postcopy. " + "Migration paused."); + + /* + * We wait until things fixed up. Then someone will setup the + * status back for us. + */ + while (s->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + qemu_sem_wait(&s->postcopy_pause_sem); + } + + trace_postcopy_pause_continued(); + + return MIG_THR_ERR_RECOVERED; +} + +static MigThrError migration_detect_error(MigrationState *s) +{ + int ret; + + /* Try to detect any file errors */ + ret = qemu_file_get_error(s->to_dst_file); + + if (!ret) { + /* Everything is fine */ + return MIG_THR_ERR_NONE; + } + + if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) { + /* + * For postcopy, we allow the network to be down for a + * while. After that, it can be continued by a + * recovery phase. + */ + return postcopy_pause(s); + } else { + /* + * For precopy (or postcopy with error outside IO), we fail + * with no time. + */ + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + trace_migration_thread_file_err(); + + /* Time to stop the migration, now. */ + return MIG_THR_ERR_FATAL; + } +} + static void migration_calculate_complete(MigrationState *s) { uint64_t bytes = qemu_ftell(s->to_dst_file); @@ -2401,6 +2475,7 @@ static void *migration_thread(void *opaque) { MigrationState *s = opaque; int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST); + MigThrError thr_error; rcu_register_thread(); @@ -2450,13 +2525,22 @@ static void *migration_thread(void *opaque) } } - if (qemu_file_get_error(s->to_dst_file)) { - if (migration_is_setup_or_active(s->state)) { - migrate_set_state(&s->state, s->state, - MIGRATION_STATUS_FAILED); - } - trace_migration_thread_file_err(); + /* + * Try to detect any kind of failures, and see whether we + * should stop the migration now. + */ + thr_error = migration_detect_error(s); + if (thr_error == MIG_THR_ERR_FATAL) { + /* Stop migration */ break; + } else if (thr_error == MIG_THR_ERR_RECOVERED) { + /* + * Just recovered from a e.g. network failure, reset all + * the local variables. This is important to avoid + * breaking transferred_bytes and bandwidth calculation + */ + s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + s->iteration_initial_bytes = 0; } current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); @@ -2614,6 +2698,7 @@ static void migration_instance_finalize(Object *obj) g_free(params->tls_hostname); g_free(params->tls_creds); qemu_sem_destroy(&ms->pause_sem); + qemu_sem_destroy(&ms->postcopy_pause_sem); error_free(ms->error); } @@ -2643,6 +2728,8 @@ static void migration_instance_init(Object *obj) params->has_x_multifd_channels = true; params->has_x_multifd_page_count = true; params->has_xbzrle_cache_size = true; + + qemu_sem_init(&ms->postcopy_pause_sem, 0); } /* diff --git a/migration/migration.h b/migration/migration.h index 26e5951d56..60283c39b2 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -194,6 +194,9 @@ struct MigrationState bool send_configuration; /* Whether we send section footer during migration */ bool send_section_footer; + + /* Needed by postcopy-pause state */ + QemuSemaphore postcopy_pause_sem; }; void migrate_set_state(int *state, int old_state, int new_state); diff --git a/migration/trace-events b/migration/trace-events index d6be74b7a7..409b4b8be3 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -99,6 +99,7 @@ migration_thread_setup_complete(void) "" open_return_path_on_source(void) "" open_return_path_on_source_continue(void) "" postcopy_start(void) "" +postcopy_pause_continued(void) "" postcopy_start_set_run(void) "" source_return_path_thread_bad_end(void) "" source_return_path_thread_end(void) "" From b411b844fb2e038c6ba92fd50fed3213a27c6445 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:20 +0800 Subject: [PATCH 0517/2380] migration: allow dst vm pause on postcopy When there is IO error on the incoming channel (e.g., network down), instead of bailing out immediately, we allow the dst vm to switch to the new POSTCOPY_PAUSE state. Currently it is still simple - it waits the new semaphore, until someone poke it for another attempt. One note is that here on ram loading thread we cannot detect the POSTCOPY_ACTIVE state, but we need to detect the more specific POSTCOPY_INCOMING_RUNNING state, to make sure we have already loaded all the device states. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-5-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 1 + migration/migration.h | 3 ++ migration/savevm.c | 63 ++++++++++++++++++++++++++++++++++++++++-- migration/trace-events | 2 ++ 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 8392cf467d..4a7c02fd3c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -159,6 +159,7 @@ MigrationIncomingState *migration_incoming_get_current(void) sizeof(struct PostCopyFD)); qemu_mutex_init(&mis_current.rp_mutex); qemu_event_init(&mis_current.main_thread_load_event, false); + qemu_sem_init(&mis_current.postcopy_pause_sem_dst, 0); init_dirty_bitmap_incoming_migration(); diff --git a/migration/migration.h b/migration/migration.h index 60283c39b2..0ccdcb8ffe 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -73,6 +73,9 @@ struct MigrationIncomingState { * live migration, to calculate vCPU block time * */ struct PostcopyBlocktimeContext *blocktime_ctx; + + /* notify PAUSED postcopy incoming migrations to try to continue */ + QemuSemaphore postcopy_pause_sem_dst; }; MigrationIncomingState *migration_incoming_get_current(void); diff --git a/migration/savevm.c b/migration/savevm.c index e2be02afe4..8ad99b1eaa 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1564,8 +1564,8 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis, */ static void *postcopy_ram_listen_thread(void *opaque) { - QEMUFile *f = opaque; MigrationIncomingState *mis = migration_incoming_get_current(); + QEMUFile *f = mis->from_src_file; int load_res; migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, @@ -1579,6 +1579,14 @@ static void *postcopy_ram_listen_thread(void *opaque) */ qemu_file_set_blocking(f, true); load_res = qemu_loadvm_state_main(f, mis); + + /* + * This is tricky, but, mis->from_src_file can change after it + * returns, when postcopy recovery happened. In the future, we may + * want a wrapper for the QEMUFile handle. + */ + f = mis->from_src_file; + /* And non-blocking again so we don't block in any cleanup */ qemu_file_set_blocking(f, false); @@ -1668,7 +1676,7 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) /* Start up the listening thread and wait for it to signal ready */ qemu_sem_init(&mis->listen_thread_sem, 0); qemu_thread_create(&mis->listen_thread, "postcopy/listen", - postcopy_ram_listen_thread, mis->from_src_file, + postcopy_ram_listen_thread, NULL, QEMU_THREAD_DETACHED); qemu_sem_wait(&mis->listen_thread_sem); qemu_sem_destroy(&mis->listen_thread_sem); @@ -2055,11 +2063,44 @@ void qemu_loadvm_state_cleanup(void) } } +/* Return true if we should continue the migration, or false. */ +static bool postcopy_pause_incoming(MigrationIncomingState *mis) +{ + trace_postcopy_pause_incoming(); + + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, + MIGRATION_STATUS_POSTCOPY_PAUSED); + + assert(mis->from_src_file); + qemu_file_shutdown(mis->from_src_file); + qemu_fclose(mis->from_src_file); + mis->from_src_file = NULL; + + assert(mis->to_src_file); + qemu_file_shutdown(mis->to_src_file); + qemu_mutex_lock(&mis->rp_mutex); + qemu_fclose(mis->to_src_file); + mis->to_src_file = NULL; + qemu_mutex_unlock(&mis->rp_mutex); + + error_report("Detected IO failure for postcopy. " + "Migration paused."); + + while (mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + qemu_sem_wait(&mis->postcopy_pause_sem_dst); + } + + trace_postcopy_pause_incoming_continued(); + + return true; +} + static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) { uint8_t section_type; int ret = 0; +retry: while (true) { section_type = qemu_get_byte(f); @@ -2104,6 +2145,24 @@ static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) out: if (ret < 0) { qemu_file_set_error(f, ret); + + /* + * Detect whether it is: + * + * 1. postcopy running (after receiving all device data, which + * must be in POSTCOPY_INCOMING_RUNNING state. Note that + * POSTCOPY_INCOMING_LISTENING is still not enough, it's + * still receiving device states). + * 2. network failure (-EIO) + * + * If so, we try to wait for a recovery. + */ + if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING && + ret == -EIO && postcopy_pause_incoming(mis)) { + /* Reset f to point to the newly created channel */ + f = mis->from_src_file; + goto retry; + } } return ret; } diff --git a/migration/trace-events b/migration/trace-events index 409b4b8be3..e23ec019be 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -100,6 +100,8 @@ open_return_path_on_source(void) "" open_return_path_on_source_continue(void) "" postcopy_start(void) "" postcopy_pause_continued(void) "" +postcopy_pause_incoming(void) "" +postcopy_pause_incoming_continued(void) "" postcopy_start_set_run(void) "" source_return_path_thread_bad_end(void) "" source_return_path_thread_end(void) "" From 14b1742eaaad1148c5b421eea0ae7e74d823e630 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:21 +0800 Subject: [PATCH 0518/2380] migration: allow src return path to pause Let the thread pause for network issues. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-6-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 35 +++++++++++++++++++++++++++++++++-- migration/migration.h | 1 + migration/trace-events | 2 ++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 4a7c02fd3c..f9c58d2511 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1779,6 +1779,18 @@ static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, } } +/* Return true to retry, false to quit */ +static bool postcopy_pause_return_path_thread(MigrationState *s) +{ + trace_postcopy_pause_return_path(); + + qemu_sem_wait(&s->postcopy_pause_rp_sem); + + trace_postcopy_pause_return_path_continued(); + + return true; +} + /* * Handles messages sent on the return path towards the source VM * @@ -1795,6 +1807,8 @@ static void *source_return_path_thread(void *opaque) int res; trace_source_return_path_thread_entry(); + +retry: while (!ms->rp_state.error && !qemu_file_get_error(rp) && migration_is_setup_or_active(ms->state)) { trace_source_return_path_thread_loop_top(); @@ -1886,13 +1900,28 @@ static void *source_return_path_thread(void *opaque) break; } } - if (qemu_file_get_error(rp)) { + +out: + res = qemu_file_get_error(rp); + if (res) { + if (res == -EIO) { + /* + * Maybe there is something we can do: it looks like a + * network down issue, and we pause for a recovery. + */ + if (postcopy_pause_return_path_thread(ms)) { + /* Reload rp, reset the rest */ + rp = ms->rp_state.from_dst_file; + ms->rp_state.error = false; + goto retry; + } + } + trace_source_return_path_thread_bad_end(); mark_source_rp_bad(ms); } trace_source_return_path_thread_end(); -out: ms->rp_state.from_dst_file = NULL; qemu_fclose(rp); return NULL; @@ -2700,6 +2729,7 @@ static void migration_instance_finalize(Object *obj) g_free(params->tls_creds); qemu_sem_destroy(&ms->pause_sem); qemu_sem_destroy(&ms->postcopy_pause_sem); + qemu_sem_destroy(&ms->postcopy_pause_rp_sem); error_free(ms->error); } @@ -2731,6 +2761,7 @@ static void migration_instance_init(Object *obj) params->has_xbzrle_cache_size = true; qemu_sem_init(&ms->postcopy_pause_sem, 0); + qemu_sem_init(&ms->postcopy_pause_rp_sem, 0); } /* diff --git a/migration/migration.h b/migration/migration.h index 0ccdcb8ffe..32fd50e9be 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -200,6 +200,7 @@ struct MigrationState /* Needed by postcopy-pause state */ QemuSemaphore postcopy_pause_sem; + QemuSemaphore postcopy_pause_rp_sem; }; void migrate_set_state(int *state, int old_state, int new_state); diff --git a/migration/trace-events b/migration/trace-events index e23ec019be..cd971bf9fe 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -99,6 +99,8 @@ migration_thread_setup_complete(void) "" open_return_path_on_source(void) "" open_return_path_on_source_continue(void) "" postcopy_start(void) "" +postcopy_pause_return_path(void) "" +postcopy_pause_return_path_continued(void) "" postcopy_pause_continued(void) "" postcopy_pause_incoming(void) "" postcopy_pause_incoming_continued(void) "" From 3a7804c3065ac79ff6e609e75ff2765d79f64f85 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:22 +0800 Subject: [PATCH 0519/2380] migration: allow fault thread to pause Allows the fault thread to stop handling page faults temporarily. When network failure happened (and if we expect a recovery afterwards), we should not allow the fault thread to continue sending things to source, instead, it should halt for a while until the connection is rebuilt. When the dest main thread noticed the failure, it kicks the fault thread to switch to pause state. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-7-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 1 + migration/migration.h | 1 + migration/postcopy-ram.c | 54 +++++++++++++++++++++++++++++++++++++--- migration/savevm.c | 3 +++ migration/trace-events | 2 ++ 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index f9c58d2511..3d3b0a5b4a 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -160,6 +160,7 @@ MigrationIncomingState *migration_incoming_get_current(void) qemu_mutex_init(&mis_current.rp_mutex); qemu_event_init(&mis_current.main_thread_load_event, false); qemu_sem_init(&mis_current.postcopy_pause_sem_dst, 0); + qemu_sem_init(&mis_current.postcopy_pause_sem_fault, 0); init_dirty_bitmap_incoming_migration(); diff --git a/migration/migration.h b/migration/migration.h index 32fd50e9be..4ea5949104 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -76,6 +76,7 @@ struct MigrationIncomingState { /* notify PAUSED postcopy incoming migrations to try to continue */ QemuSemaphore postcopy_pause_sem_dst; + QemuSemaphore postcopy_pause_sem_fault; }; MigrationIncomingState *migration_incoming_get_current(void); diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 8ceeaa2a93..658b750a8e 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -830,6 +830,17 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) affected_cpu); } +static bool postcopy_pause_fault_thread(MigrationIncomingState *mis) +{ + trace_postcopy_pause_fault_thread(); + + qemu_sem_wait(&mis->postcopy_pause_sem_fault); + + trace_postcopy_pause_fault_thread_continued(); + + return true; +} + /* * Handle faults detected by the USERFAULT markings */ @@ -880,6 +891,22 @@ static void *postcopy_ram_fault_thread(void *opaque) break; } + if (!mis->to_src_file) { + /* + * Possibly someone tells us that the return path is + * broken already using the event. We should hold until + * the channel is rebuilt. + */ + if (postcopy_pause_fault_thread(mis)) { + mis->last_rb = NULL; + /* Continue to read the userfaultfd */ + } else { + error_report("%s: paused but don't allow to continue", + __func__); + break; + } + } + if (pfd[1].revents) { uint64_t tmp64 = 0; @@ -942,18 +969,37 @@ static void *postcopy_ram_fault_thread(void *opaque) (uintptr_t)(msg.arg.pagefault.address), msg.arg.pagefault.feat.ptid, rb); +retry: /* * Send the request to the source - we want to request one * of our host page sizes (which is >= TPS) */ if (rb != mis->last_rb) { mis->last_rb = rb; - migrate_send_rp_req_pages(mis, qemu_ram_get_idstr(rb), - rb_offset, qemu_ram_pagesize(rb)); + ret = migrate_send_rp_req_pages(mis, + qemu_ram_get_idstr(rb), + rb_offset, + qemu_ram_pagesize(rb)); } else { /* Save some space */ - migrate_send_rp_req_pages(mis, NULL, - rb_offset, qemu_ram_pagesize(rb)); + ret = migrate_send_rp_req_pages(mis, + NULL, + rb_offset, + qemu_ram_pagesize(rb)); + } + + if (ret) { + /* May be network failure, try to wait for recovery */ + if (ret == -EIO && postcopy_pause_fault_thread(mis)) { + /* We got reconnected somehow, try to continue */ + mis->last_rb = NULL; + goto retry; + } else { + /* This is a unavoidable fault */ + error_report("%s: migrate_send_rp_req_pages() get %d", + __func__, ret); + break; + } } } diff --git a/migration/savevm.c b/migration/savevm.c index 8ad99b1eaa..6ee69d8283 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2083,6 +2083,9 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) mis->to_src_file = NULL; qemu_mutex_unlock(&mis->rp_mutex); + /* Notify the fault thread for the invalidated file handle */ + postcopy_fault_thread_notify(mis); + error_report("Detected IO failure for postcopy. " "Migration paused."); diff --git a/migration/trace-events b/migration/trace-events index cd971bf9fe..7f836499d1 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -101,6 +101,8 @@ open_return_path_on_source_continue(void) "" postcopy_start(void) "" postcopy_pause_return_path(void) "" postcopy_pause_return_path_continued(void) "" +postcopy_pause_fault_thread(void) "" +postcopy_pause_fault_thread_continued(void) "" postcopy_pause_continued(void) "" postcopy_pause_incoming(void) "" postcopy_pause_incoming_continued(void) "" From 7a4da28b26382bbfc01847dcb7ac01de99f0a1f0 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:23 +0800 Subject: [PATCH 0520/2380] qmp: hmp: add migrate "resume" option It will be used when we want to resume one paused migration. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-8-peterx@redhat.com> Signed-off-by: Juan Quintela --- s/2.12/2.13/ --- hmp-commands.hx | 7 ++++--- hmp.c | 4 +++- migration/migration.c | 2 +- qapi/migration.json | 5 ++++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 227f7eee88..a7051ee391 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -897,13 +897,14 @@ ETEXI { .name = "migrate", - .args_type = "detach:-d,blk:-b,inc:-i,uri:s", - .params = "[-d] [-b] [-i] uri", + .args_type = "detach:-d,blk:-b,inc:-i,resume:-r,uri:s", + .params = "[-d] [-b] [-i] [-r] uri", .help = "migrate to URI (using -d to not wait for completion)" "\n\t\t\t -b for migration without shared storage with" " full copy of disk\n\t\t\t -i for migration without " "shared storage with incremental copy of disk " - "(base image shared between src and destination)", + "(base image shared between src and destination)" + "\n\t\t\t -r to resume a paused migration", .cmd = hmp_migrate, }, diff --git a/hmp.c b/hmp.c index bdb340605c..a7aa878788 100644 --- a/hmp.c +++ b/hmp.c @@ -1929,10 +1929,12 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) bool detach = qdict_get_try_bool(qdict, "detach", false); bool blk = qdict_get_try_bool(qdict, "blk", false); bool inc = qdict_get_try_bool(qdict, "inc", false); + bool resume = qdict_get_try_bool(qdict, "resume", false); const char *uri = qdict_get_str(qdict, "uri"); Error *err = NULL; - qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, &err); + qmp_migrate(uri, !!blk, blk, !!inc, inc, + false, false, true, resume, &err); if (err) { hmp_handle_error(mon, &err); return; diff --git a/migration/migration.c b/migration/migration.c index 3d3b0a5b4a..19f07fb64f 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1413,7 +1413,7 @@ bool migration_is_blocked(Error **errp) void qmp_migrate(const char *uri, bool has_blk, bool blk, bool has_inc, bool inc, bool has_detach, bool detach, - Error **errp) + bool has_resume, bool resume, Error **errp) { Error *local_err = NULL; MigrationState *s = migrate_get_current(); diff --git a/qapi/migration.json b/qapi/migration.json index 244334e9f4..b8ca60ac43 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1031,6 +1031,8 @@ # @detach: this argument exists only for compatibility reasons and # is ignored by QEMU # +# @resume: resume one paused migration, default "off". (since 2.13) +# # Returns: nothing on success # # Since: 0.14.0 @@ -1052,7 +1054,8 @@ # ## { 'command': 'migrate', - 'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', '*detach': 'bool' } } + 'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', + '*detach': 'bool', '*resume': 'bool' } } ## # @migrate-incoming: From d3e35b8f6248eab2c3d412198a487ae49706b214 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:24 +0800 Subject: [PATCH 0521/2380] migration: rebuild channel on source This patch detects the "resume" flag of migration command, rebuild the channels only if the flag is set. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-9-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 131 +++++++++++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 41 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 19f07fb64f..038fae93ab 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1411,6 +1411,61 @@ bool migration_is_blocked(Error **errp) return false; } +/* Returns true if continue to migrate, or false if error detected */ +static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, + bool resume, Error **errp) +{ + Error *local_err = NULL; + + if (resume) { + if (s->state != MIGRATION_STATUS_POSTCOPY_PAUSED) { + error_setg(errp, "Cannot resume if there is no " + "paused migration"); + return false; + } + /* This is a resume, skip init status */ + return true; + } + + if (migration_is_setup_or_active(s->state) || + s->state == MIGRATION_STATUS_CANCELLING || + s->state == MIGRATION_STATUS_COLO) { + error_setg(errp, QERR_MIGRATION_ACTIVE); + return false; + } + + if (runstate_check(RUN_STATE_INMIGRATE)) { + error_setg(errp, "Guest is waiting for an incoming migration"); + return false; + } + + if (migration_is_blocked(errp)) { + return false; + } + + if (blk || blk_inc) { + if (migrate_use_block() || migrate_use_block_incremental()) { + error_setg(errp, "Command options are incompatible with " + "current migration capabilities"); + return false; + } + migrate_set_block_enabled(true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return false; + } + s->must_remove_block_options = true; + } + + if (blk_inc) { + migrate_set_block_incremental(s, true); + } + + migrate_init(s); + + return true; +} + void qmp_migrate(const char *uri, bool has_blk, bool blk, bool has_inc, bool inc, bool has_detach, bool detach, bool has_resume, bool resume, Error **errp) @@ -1419,40 +1474,11 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, MigrationState *s = migrate_get_current(); const char *p; - if (migration_is_setup_or_active(s->state) || - s->state == MIGRATION_STATUS_CANCELLING || - s->state == MIGRATION_STATUS_COLO) { - error_setg(errp, QERR_MIGRATION_ACTIVE); + if (!migrate_prepare(s, has_blk && blk, has_inc && inc, + has_resume && resume, errp)) { + /* Error detected, put into errp */ return; } - if (runstate_check(RUN_STATE_INMIGRATE)) { - error_setg(errp, "Guest is waiting for an incoming migration"); - return; - } - - if (migration_is_blocked(errp)) { - return; - } - - if ((has_blk && blk) || (has_inc && inc)) { - if (migrate_use_block() || migrate_use_block_incremental()) { - error_setg(errp, "Command options are incompatible with " - "current migration capabilities"); - return; - } - migrate_set_block_enabled(true, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - s->must_remove_block_options = true; - } - - if (has_inc && inc) { - migrate_set_block_incremental(s, true); - } - - migrate_init(s); if (strstart(uri, "tcp:", &p)) { tcp_start_outgoing_migration(s, p, &local_err); @@ -1928,7 +1954,8 @@ out: return NULL; } -static int open_return_path_on_source(MigrationState *ms) +static int open_return_path_on_source(MigrationState *ms, + bool create_thread) { ms->rp_state.from_dst_file = qemu_file_get_return_path(ms->to_dst_file); @@ -1937,6 +1964,12 @@ static int open_return_path_on_source(MigrationState *ms) } trace_open_return_path_on_source(); + + if (!create_thread) { + /* We're done */ + return 0; + } + qemu_thread_create(&ms->rp_state.rp_thread, "return path", source_return_path_thread, ms, QEMU_THREAD_JOINABLE); @@ -2593,6 +2626,9 @@ static void *migration_thread(void *opaque) void migrate_fd_connect(MigrationState *s, Error *error_in) { + int64_t rate_limit; + bool resume = s->state == MIGRATION_STATUS_POSTCOPY_PAUSED; + s->expected_downtime = s->parameters.downtime_limit; s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s); if (error_in) { @@ -2601,12 +2637,21 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) return; } - qemu_file_set_blocking(s->to_dst_file, true); - qemu_file_set_rate_limit(s->to_dst_file, - s->parameters.max_bandwidth / XFER_LIMIT_RATIO); + if (resume) { + /* This is a resumed migration */ + rate_limit = INT64_MAX; + } else { + /* This is a fresh new migration */ + rate_limit = s->parameters.max_bandwidth / XFER_LIMIT_RATIO; + s->expected_downtime = s->parameters.downtime_limit; + s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s); - /* Notify before starting migration thread */ - notifier_list_notify(&migration_state_notifiers, s); + /* Notify before starting migration thread */ + notifier_list_notify(&migration_state_notifiers, s); + } + + qemu_file_set_rate_limit(s->to_dst_file, rate_limit); + qemu_file_set_blocking(s->to_dst_file, true); /* * Open the return path. For postcopy, it is used exclusively. For @@ -2614,15 +2659,19 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) * QEMU uses the return path. */ if (migrate_postcopy_ram() || migrate_use_return_path()) { - if (open_return_path_on_source(s)) { + if (open_return_path_on_source(s, !resume)) { error_report("Unable to open return-path for postcopy"); - migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, - MIGRATION_STATUS_FAILED); + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); migrate_fd_cleanup(s); return; } } + if (resume) { + /* TODO: do the resume logic */ + return; + } + if (multifd_save_setup() != 0) { migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_FAILED); From 135b87b4f08029a4ec5c31f9c4473b519da7e2f7 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:25 +0800 Subject: [PATCH 0522/2380] migration: new state "postcopy-recover" Introducing new migration state "postcopy-recover". If a migration procedure is paused and the connection is rebuilt afterward successfully, we'll switch the source VM state from "postcopy-paused" to the new state "postcopy-recover", then we'll do the resume logic in the migration thread (along with the return path thread). This patch only do the state switch on source side. Another following up patch will handle the state switching on destination side using the same status bit. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-10-peterx@redhat.com> Signed-off-by: Juan Quintela --- s/2.11/2.13/ --- migration/migration.c | 78 ++++++++++++++++++++++++++++++++----------- qapi/migration.json | 4 ++- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 038fae93ab..4ab637a1fe 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -577,6 +577,7 @@ static bool migration_is_setup_or_active(int state) case MIGRATION_STATUS_ACTIVE: case MIGRATION_STATUS_POSTCOPY_ACTIVE: case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER: case MIGRATION_STATUS_SETUP: case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: @@ -658,6 +659,7 @@ static void fill_source_migration_info(MigrationInfo *info) case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER: /* TODO add some postcopy stats */ info->has_status = true; info->has_total_time = true; @@ -2318,6 +2320,13 @@ typedef enum MigThrError { MIG_THR_ERR_FATAL = 2, } MigThrError; +/* Return zero if success, or <0 for error */ +static int postcopy_do_resume(MigrationState *s) +{ + /* TODO: do the resume logic */ + return 0; +} + /* * We don't return until we are in a safe state to continue current * postcopy migration. Returns MIG_THR_ERR_RECOVERED if recovered, or @@ -2326,29 +2335,55 @@ typedef enum MigThrError { static MigThrError postcopy_pause(MigrationState *s) { assert(s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); - migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, - MIGRATION_STATUS_POSTCOPY_PAUSED); - /* Current channel is possibly broken. Release it. */ - assert(s->to_dst_file); - qemu_file_shutdown(s->to_dst_file); - qemu_fclose(s->to_dst_file); - s->to_dst_file = NULL; + while (true) { + migrate_set_state(&s->state, s->state, + MIGRATION_STATUS_POSTCOPY_PAUSED); - error_report("Detected IO failure for postcopy. " - "Migration paused."); + /* Current channel is possibly broken. Release it. */ + assert(s->to_dst_file); + qemu_file_shutdown(s->to_dst_file); + qemu_fclose(s->to_dst_file); + s->to_dst_file = NULL; - /* - * We wait until things fixed up. Then someone will setup the - * status back for us. - */ - while (s->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { - qemu_sem_wait(&s->postcopy_pause_sem); + error_report("Detected IO failure for postcopy. " + "Migration paused."); + + /* + * We wait until things fixed up. Then someone will setup the + * status back for us. + */ + while (s->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + qemu_sem_wait(&s->postcopy_pause_sem); + } + + if (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { + /* Woken up by a recover procedure. Give it a shot */ + + /* + * Firstly, let's wake up the return path now, with a new + * return path channel. + */ + qemu_sem_post(&s->postcopy_pause_rp_sem); + + /* Do the resume logic */ + if (postcopy_do_resume(s) == 0) { + /* Let's continue! */ + trace_postcopy_pause_continued(); + return MIG_THR_ERR_RECOVERED; + } else { + /* + * Something wrong happened during the recovery, let's + * pause again. Pause is always better than throwing + * data away. + */ + continue; + } + } else { + /* This is not right... Time to quit. */ + return MIG_THR_ERR_FATAL; + } } - - trace_postcopy_pause_continued(); - - return MIG_THR_ERR_RECOVERED; } static MigThrError migration_detect_error(MigrationState *s) @@ -2668,7 +2703,10 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) } if (resume) { - /* TODO: do the resume logic */ + /* Wakeup the main migration thread to do the recovery */ + migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_PAUSED, + MIGRATION_STATUS_POSTCOPY_RECOVER); + qemu_sem_post(&s->postcopy_pause_sem); return; } diff --git a/qapi/migration.json b/qapi/migration.json index b8ca60ac43..4e8e61ceef 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -91,6 +91,8 @@ # # @postcopy-paused: during postcopy but paused. (since 2.13) # +# @postcopy-recover: trying to recover from a paused postcopy. (since 2.13) +# # @completed: migration is finished. # # @failed: some error occurred during migration process. @@ -109,7 +111,7 @@ { 'enum': 'MigrationStatus', 'data': [ 'none', 'setup', 'cancelling', 'cancelled', 'active', 'postcopy-active', 'postcopy-paused', - 'completed', 'failed', 'colo', + 'postcopy-recover', 'completed', 'failed', 'colo', 'pre-switchover', 'device' ] } ## From d96c9e8d784a7eea95f7d09e3a06a66e0b6558e5 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:26 +0800 Subject: [PATCH 0523/2380] migration: wakeup dst ram-load-thread for recover On the destination side, we cannot wake up all the threads when we got reconnected. The first thing to do is to wake up the main load thread, so that we can continue to receive valid messages from source again and reply when needed. At this point, we switch the destination VM state from postcopy-paused back to postcopy-recover. Now we are finally ready to do the resume logic. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-11-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 4ab637a1fe..ec3bc9ae20 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -440,8 +440,34 @@ void migration_incoming_process(void) void migration_fd_process_incoming(QEMUFile *f) { - migration_incoming_setup(f); - migration_incoming_process(); + MigrationIncomingState *mis = migration_incoming_get_current(); + + if (mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + /* Resumed from a paused postcopy migration */ + + mis->from_src_file = f; + /* Postcopy has standalone thread to do vm load */ + qemu_file_set_blocking(f, true); + + /* Re-configure the return path */ + mis->to_src_file = qemu_file_get_return_path(f); + + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_PAUSED, + MIGRATION_STATUS_POSTCOPY_RECOVER); + + /* + * Here, we only wake up the main loading thread (while the + * fault thread will still be waiting), so that we can receive + * commands from source now, and answer it if needed. The + * fault thread will be woken up afterwards until we are sure + * that source is ready to reply to page requests. + */ + qemu_sem_post(&mis->postcopy_pause_sem_dst); + } else { + /* New incoming migration */ + migration_incoming_setup(f); + migration_incoming_process(); + } } void migration_ioc_process_incoming(QIOChannel *ioc) From f25d42253ca137f79541f655dd915377ad596e28 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:27 +0800 Subject: [PATCH 0524/2380] migration: new cmd MIG_CMD_RECV_BITMAP Add a new vm command MIG_CMD_RECV_BITMAP to request received bitmap for one ramblock. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-12-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/savevm.c | 61 ++++++++++++++++++++++++++++++++++++++++++ migration/savevm.h | 1 + migration/trace-events | 2 ++ 3 files changed, 64 insertions(+) diff --git a/migration/savevm.c b/migration/savevm.c index 6ee69d8283..9f4a95d411 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -81,6 +81,7 @@ enum qemu_vm_cmd { were previously sent during precopy but are dirty. */ MIG_CMD_PACKAGED, /* Send a wrapped stream within this stream */ + MIG_CMD_RECV_BITMAP, /* Request for recved bitmap on dst */ MIG_CMD_MAX }; @@ -98,6 +99,7 @@ static struct mig_cmd_args { [MIG_CMD_POSTCOPY_RAM_DISCARD] = { .len = -1, .name = "POSTCOPY_RAM_DISCARD" }, [MIG_CMD_PACKAGED] = { .len = 4, .name = "PACKAGED" }, + [MIG_CMD_RECV_BITMAP] = { .len = -1, .name = "RECV_BITMAP" }, [MIG_CMD_MAX] = { .len = -1, .name = "MAX" }, }; @@ -956,6 +958,19 @@ void qemu_savevm_send_postcopy_run(QEMUFile *f) qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_RUN, 0, NULL); } +void qemu_savevm_send_recv_bitmap(QEMUFile *f, char *block_name) +{ + size_t len; + char buf[256]; + + trace_savevm_send_recv_bitmap(block_name); + + buf[0] = len = strlen(block_name); + memcpy(buf + 1, block_name, len); + + qemu_savevm_command_send(f, MIG_CMD_RECV_BITMAP, len + 1, (uint8_t *)buf); +} + bool qemu_savevm_state_blocked(Error **errp) { SaveStateEntry *se; @@ -1801,6 +1816,49 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) return ret; } +/* + * Handle request that source requests for recved_bitmap on + * destination. Payload format: + * + * len (1 byte) + ramblock_name (<255 bytes) + */ +static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis, + uint16_t len) +{ + QEMUFile *file = mis->from_src_file; + RAMBlock *rb; + char block_name[256]; + size_t cnt; + + cnt = qemu_get_counted_string(file, block_name); + if (!cnt) { + error_report("%s: failed to read block name", __func__); + return -EINVAL; + } + + /* Validate before using the data */ + if (qemu_file_get_error(file)) { + return qemu_file_get_error(file); + } + + if (len != cnt + 1) { + error_report("%s: invalid payload length (%d)", __func__, len); + return -EINVAL; + } + + rb = qemu_ram_block_by_name(block_name); + if (!rb) { + error_report("%s: block '%s' not found", __func__, block_name); + return -EINVAL; + } + + /* TODO: send the bitmap back to source */ + + trace_loadvm_handle_recv_bitmap(block_name); + + return 0; +} + /* * Process an incoming 'QEMU_VM_COMMAND' * 0 just a normal return @@ -1874,6 +1932,9 @@ static int loadvm_process_command(QEMUFile *f) case MIG_CMD_POSTCOPY_RAM_DISCARD: return loadvm_postcopy_ram_handle_discard(mis, len); + + case MIG_CMD_RECV_BITMAP: + return loadvm_handle_recv_bitmap(mis, len); } return 0; diff --git a/migration/savevm.h b/migration/savevm.h index cf4f0d37ca..b8cee00d41 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -47,6 +47,7 @@ int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len); void qemu_savevm_send_postcopy_advise(QEMUFile *f); void qemu_savevm_send_postcopy_listen(QEMUFile *f); void qemu_savevm_send_postcopy_run(QEMUFile *f); +void qemu_savevm_send_recv_bitmap(QEMUFile *f, char *block_name); void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name, uint16_t len, diff --git a/migration/trace-events b/migration/trace-events index 7f836499d1..5bee6d525a 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -12,6 +12,7 @@ loadvm_state_cleanup(void) "" loadvm_handle_cmd_packaged(unsigned int length) "%u" loadvm_handle_cmd_packaged_main(int ret) "%d" loadvm_handle_cmd_packaged_received(int ret) "%d" +loadvm_handle_recv_bitmap(char *s) "%s" loadvm_postcopy_handle_advise(void) "" loadvm_postcopy_handle_listen(void) "" loadvm_postcopy_handle_run(void) "" @@ -34,6 +35,7 @@ savevm_send_open_return_path(void) "" savevm_send_ping(uint32_t val) "0x%x" savevm_send_postcopy_listen(void) "" savevm_send_postcopy_run(void) "" +savevm_send_recv_bitmap(char *name) "%s" savevm_state_setup(void) "" savevm_state_header(void) "" savevm_state_iterate(void) "" From a335debb35bb30ade46e0e62c0b2fbb3882c8448 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:28 +0800 Subject: [PATCH 0525/2380] migration: new message MIG_RP_MSG_RECV_BITMAP Introducing new return path message MIG_RP_MSG_RECV_BITMAP to send received bitmap of ramblock back to source. This is the reply message of MIG_CMD_RECV_BITMAP, it contains not only the header (including the ramblock name), and it was appended with the whole ramblock received bitmap on the destination side. When the source receives such a reply message (MIG_RP_MSG_RECV_BITMAP), it parses it, convert it to the dirty bitmap by inverting the bits. One thing to mention is that, when we send the recv bitmap, we are doing these things in extra: - converting the bitmap to little endian, to support when hosts are using different endianess on src/dst. - do proper alignment for 8 bytes, to support when hosts are using different word size (32/64 bits) on src/dst. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-13-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 68 +++++++++++++++++++ migration/migration.h | 2 + migration/ram.c | 144 +++++++++++++++++++++++++++++++++++++++++ migration/ram.h | 3 + migration/savevm.c | 2 +- migration/trace-events | 3 + 6 files changed, 221 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index ec3bc9ae20..7c5e20b3f6 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -95,6 +95,7 @@ enum mig_rp_message_type { MIG_RP_MSG_REQ_PAGES_ID, /* data (start: be64, len: be32, id: string) */ MIG_RP_MSG_REQ_PAGES, /* data (start: be64, len: be32) */ + MIG_RP_MSG_RECV_BITMAP, /* send recved_bitmap back to source */ MIG_RP_MSG_MAX }; @@ -524,6 +525,45 @@ void migrate_send_rp_pong(MigrationIncomingState *mis, migrate_send_rp_message(mis, MIG_RP_MSG_PONG, sizeof(buf), &buf); } +void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis, + char *block_name) +{ + char buf[512]; + int len; + int64_t res; + + /* + * First, we send the header part. It contains only the len of + * idstr, and the idstr itself. + */ + len = strlen(block_name); + buf[0] = len; + memcpy(buf + 1, block_name, len); + + if (mis->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { + error_report("%s: MSG_RP_RECV_BITMAP only used for recovery", + __func__); + return; + } + + migrate_send_rp_message(mis, MIG_RP_MSG_RECV_BITMAP, len + 1, buf); + + /* + * Next, we dump the received bitmap to the stream. + * + * TODO: currently we are safe since we are the only one that is + * using the to_src_file handle (fault thread is still paused), + * and it's ok even not taking the mutex. However the best way is + * to take the lock before sending the message header, and release + * the lock after sending the bitmap. + */ + qemu_mutex_lock(&mis->rp_mutex); + res = ramblock_recv_bitmap_send(mis->to_src_file, block_name); + qemu_mutex_unlock(&mis->rp_mutex); + + trace_migrate_send_rp_recv_bitmap(block_name, res); +} + MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) { MigrationCapabilityStatusList *head = NULL; @@ -1802,6 +1842,7 @@ static struct rp_cmd_args { [MIG_RP_MSG_PONG] = { .len = 4, .name = "PONG" }, [MIG_RP_MSG_REQ_PAGES] = { .len = 12, .name = "REQ_PAGES" }, [MIG_RP_MSG_REQ_PAGES_ID] = { .len = -1, .name = "REQ_PAGES_ID" }, + [MIG_RP_MSG_RECV_BITMAP] = { .len = -1, .name = "RECV_BITMAP" }, [MIG_RP_MSG_MAX] = { .len = -1, .name = "MAX" }, }; @@ -1846,6 +1887,19 @@ static bool postcopy_pause_return_path_thread(MigrationState *s) return true; } +static int migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name) +{ + RAMBlock *block = qemu_ram_block_by_name(block_name); + + if (!block) { + error_report("%s: invalid block name '%s'", __func__, block_name); + return -EINVAL; + } + + /* Fetch the received bitmap and refresh the dirty bitmap */ + return ram_dirty_bitmap_reload(s, block); +} + /* * Handles messages sent on the return path towards the source VM * @@ -1951,6 +2005,20 @@ retry: migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len); break; + case MIG_RP_MSG_RECV_BITMAP: + if (header_len < 1) { + error_report("%s: missing block name", __func__); + mark_source_rp_bad(ms); + goto out; + } + /* Format: len (1B) + idstr (<255B). This ends the idstr. */ + buf[buf[0] + 1] = '\0'; + if (migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1))) { + mark_source_rp_bad(ms); + goto out; + } + break; + default: break; } diff --git a/migration/migration.h b/migration/migration.h index 4ea5949104..2321ea37b3 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -260,6 +260,8 @@ void migrate_send_rp_pong(MigrationIncomingState *mis, uint32_t value); int migrate_send_rp_req_pages(MigrationIncomingState *mis, const char* rbname, ram_addr_t start, size_t len); +void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis, + char *block_name); void dirty_bitmap_mig_before_vm_start(void); void init_dirty_bitmap_incoming_migration(void); diff --git a/migration/ram.c b/migration/ram.c index cb14399ef9..5542843adc 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -190,6 +190,70 @@ void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, nr); } +#define RAMBLOCK_RECV_BITMAP_ENDING (0x0123456789abcdefULL) + +/* + * Format: bitmap_size (8 bytes) + whole_bitmap (N bytes). + * + * Returns >0 if success with sent bytes, or <0 if error. + */ +int64_t ramblock_recv_bitmap_send(QEMUFile *file, + const char *block_name) +{ + RAMBlock *block = qemu_ram_block_by_name(block_name); + unsigned long *le_bitmap, nbits; + uint64_t size; + + if (!block) { + error_report("%s: invalid block name: %s", __func__, block_name); + return -1; + } + + nbits = block->used_length >> TARGET_PAGE_BITS; + + /* + * Make sure the tmp bitmap buffer is big enough, e.g., on 32bit + * machines we may need 4 more bytes for padding (see below + * comment). So extend it a bit before hand. + */ + le_bitmap = bitmap_new(nbits + BITS_PER_LONG); + + /* + * Always use little endian when sending the bitmap. This is + * required that when source and destination VMs are not using the + * same endianess. (Note: big endian won't work.) + */ + bitmap_to_le(le_bitmap, block->receivedmap, nbits); + + /* Size of the bitmap, in bytes */ + size = nbits / 8; + + /* + * size is always aligned to 8 bytes for 64bit machines, but it + * may not be true for 32bit machines. We need this padding to + * make sure the migration can survive even between 32bit and + * 64bit machines. + */ + size = ROUND_UP(size, 8); + + qemu_put_be64(file, size); + qemu_put_buffer(file, (const uint8_t *)le_bitmap, size); + /* + * Mark as an end, in case the middle part is screwed up due to + * some "misterious" reason. + */ + qemu_put_be64(file, RAMBLOCK_RECV_BITMAP_ENDING); + qemu_fflush(file); + + free(le_bitmap); + + if (qemu_file_get_error(file)) { + return qemu_file_get_error(file); + } + + return size + sizeof(size); +} + /* * An outstanding page request, on the source, having been received * and queued @@ -3300,6 +3364,86 @@ static bool ram_has_postcopy(void *opaque) return migrate_postcopy_ram(); } +/* + * Read the received bitmap, revert it as the initial dirty bitmap. + * This is only used when the postcopy migration is paused but wants + * to resume from a middle point. + */ +int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) +{ + int ret = -EINVAL; + QEMUFile *file = s->rp_state.from_dst_file; + unsigned long *le_bitmap, nbits = block->used_length >> TARGET_PAGE_BITS; + uint64_t local_size = nbits / 8; + uint64_t size, end_mark; + + trace_ram_dirty_bitmap_reload_begin(block->idstr); + + if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { + error_report("%s: incorrect state %s", __func__, + MigrationStatus_str(s->state)); + return -EINVAL; + } + + /* + * Note: see comments in ramblock_recv_bitmap_send() on why we + * need the endianess convertion, and the paddings. + */ + local_size = ROUND_UP(local_size, 8); + + /* Add paddings */ + le_bitmap = bitmap_new(nbits + BITS_PER_LONG); + + size = qemu_get_be64(file); + + /* The size of the bitmap should match with our ramblock */ + if (size != local_size) { + error_report("%s: ramblock '%s' bitmap size mismatch " + "(0x%"PRIx64" != 0x%"PRIx64")", __func__, + block->idstr, size, local_size); + ret = -EINVAL; + goto out; + } + + size = qemu_get_buffer(file, (uint8_t *)le_bitmap, local_size); + end_mark = qemu_get_be64(file); + + ret = qemu_file_get_error(file); + if (ret || size != local_size) { + error_report("%s: read bitmap failed for ramblock '%s': %d" + " (size 0x%"PRIx64", got: 0x%"PRIx64")", + __func__, block->idstr, ret, local_size, size); + ret = -EIO; + goto out; + } + + if (end_mark != RAMBLOCK_RECV_BITMAP_ENDING) { + error_report("%s: ramblock '%s' end mark incorrect: 0x%"PRIu64, + __func__, block->idstr, end_mark); + ret = -EINVAL; + goto out; + } + + /* + * Endianess convertion. We are during postcopy (though paused). + * The dirty bitmap won't change. We can directly modify it. + */ + bitmap_from_le(block->bmap, le_bitmap, nbits); + + /* + * What we received is "received bitmap". Revert it as the initial + * dirty bitmap for this ramblock. + */ + bitmap_complement(block->bmap, block->bmap, nbits); + + trace_ram_dirty_bitmap_reload_complete(block->idstr); + + ret = 0; +out: + free(le_bitmap); + return ret; +} + static SaveVMHandlers savevm_ram_handlers = { .save_setup = ram_save_setup, .save_live_iterate = ram_save_iterate, diff --git a/migration/ram.h b/migration/ram.h index 3f4b7daee8..d386f4d641 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -66,5 +66,8 @@ int ramblock_recv_bitmap_test(RAMBlock *rb, void *host_addr); bool ramblock_recv_bitmap_test_byte_offset(RAMBlock *rb, uint64_t byte_offset); void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr); void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr); +int64_t ramblock_recv_bitmap_send(QEMUFile *file, + const char *block_name); +int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb); #endif diff --git a/migration/savevm.c b/migration/savevm.c index 9f4a95d411..7176b350d5 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1852,7 +1852,7 @@ static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis, return -EINVAL; } - /* TODO: send the bitmap back to source */ + migrate_send_rp_recv_bitmap(mis, block_name); trace_loadvm_handle_recv_bitmap(block_name); diff --git a/migration/trace-events b/migration/trace-events index 5bee6d525a..72e57089f3 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -79,6 +79,8 @@ ram_load_postcopy_loop(uint64_t addr, int flags) "@%" PRIx64 " %x" ram_postcopy_send_discard_bitmap(void) "" ram_save_page(const char *rbname, uint64_t offset, void *host) "%s: offset: 0x%" PRIx64 " host: %p" ram_save_queue_pages(const char *rbname, size_t start, size_t len) "%s: start: 0x%zx len: 0x%zx" +ram_dirty_bitmap_reload_begin(char *str) "%s" +ram_dirty_bitmap_reload_complete(char *str) "%s" # migration/migration.c await_return_path_close_on_source_close(void) "" @@ -90,6 +92,7 @@ migrate_fd_cancel(void) "" migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx" migrate_pending(uint64_t size, uint64_t max, uint64_t pre, uint64_t compat, uint64_t post) "pending size %" PRIu64 " max %" PRIu64 " (pre = %" PRIu64 " compat=%" PRIu64 " post=%" PRIu64 ")" migrate_send_rp_message(int msg_type, uint16_t len) "%d: len %d" +migrate_send_rp_recv_bitmap(char *name, int64_t size) "block '%s' size 0x%"PRIi64 migration_completion_file_err(void) "" migration_completion_postcopy_end(void) "" migration_completion_postcopy_end_after_complete(void) "" From 3f5875eca5da5106d5eec121325a05d2277022c3 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:29 +0800 Subject: [PATCH 0526/2380] migration: new cmd MIG_CMD_POSTCOPY_RESUME Introducing this new command to be sent when the source VM is ready to resume the paused migration. What the destination does here is basically release the fault thread to continue service page faults. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-14-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/savevm.c | 35 +++++++++++++++++++++++++++++++++++ migration/savevm.h | 1 + migration/trace-events | 2 ++ 3 files changed, 38 insertions(+) diff --git a/migration/savevm.c b/migration/savevm.c index 7176b350d5..a7e793eef7 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -80,6 +80,7 @@ enum qemu_vm_cmd { MIG_CMD_POSTCOPY_RAM_DISCARD, /* A list of pages to discard that were previously sent during precopy but are dirty. */ + MIG_CMD_POSTCOPY_RESUME, /* resume postcopy on dest */ MIG_CMD_PACKAGED, /* Send a wrapped stream within this stream */ MIG_CMD_RECV_BITMAP, /* Request for recved bitmap on dst */ MIG_CMD_MAX @@ -98,6 +99,7 @@ static struct mig_cmd_args { [MIG_CMD_POSTCOPY_RUN] = { .len = 0, .name = "POSTCOPY_RUN" }, [MIG_CMD_POSTCOPY_RAM_DISCARD] = { .len = -1, .name = "POSTCOPY_RAM_DISCARD" }, + [MIG_CMD_POSTCOPY_RESUME] = { .len = 0, .name = "POSTCOPY_RESUME" }, [MIG_CMD_PACKAGED] = { .len = 4, .name = "PACKAGED" }, [MIG_CMD_RECV_BITMAP] = { .len = -1, .name = "RECV_BITMAP" }, [MIG_CMD_MAX] = { .len = -1, .name = "MAX" }, @@ -958,6 +960,12 @@ void qemu_savevm_send_postcopy_run(QEMUFile *f) qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_RUN, 0, NULL); } +void qemu_savevm_send_postcopy_resume(QEMUFile *f) +{ + trace_savevm_send_postcopy_resume(); + qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_RESUME, 0, NULL); +} + void qemu_savevm_send_recv_bitmap(QEMUFile *f, char *block_name) { size_t len; @@ -1768,6 +1776,30 @@ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) return LOADVM_QUIT; } +static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) +{ + if (mis->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { + error_report("%s: illegal resume received", __func__); + /* Don't fail the load, only for this. */ + return 0; + } + + /* + * This means source VM is ready to resume the postcopy migration. + * It's time to switch state and release the fault thread to + * continue service page faults. + */ + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_RECOVER, + MIGRATION_STATUS_POSTCOPY_ACTIVE); + qemu_sem_post(&mis->postcopy_pause_sem_fault); + + trace_loadvm_postcopy_handle_resume(); + + /* TODO: Tell source that "we are ready" */ + + return 0; +} + /** * Immediately following this command is a blob of data containing an embedded * chunk of migration stream; read it and load it. @@ -1933,6 +1965,9 @@ static int loadvm_process_command(QEMUFile *f) case MIG_CMD_POSTCOPY_RAM_DISCARD: return loadvm_postcopy_ram_handle_discard(mis, len); + case MIG_CMD_POSTCOPY_RESUME: + return loadvm_postcopy_handle_resume(mis); + case MIG_CMD_RECV_BITMAP: return loadvm_handle_recv_bitmap(mis, len); } diff --git a/migration/savevm.h b/migration/savevm.h index b8cee00d41..b24ff073ad 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -47,6 +47,7 @@ int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len); void qemu_savevm_send_postcopy_advise(QEMUFile *f); void qemu_savevm_send_postcopy_listen(QEMUFile *f); void qemu_savevm_send_postcopy_run(QEMUFile *f); +void qemu_savevm_send_postcopy_resume(QEMUFile *f); void qemu_savevm_send_recv_bitmap(QEMUFile *f, char *block_name); void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name, diff --git a/migration/trace-events b/migration/trace-events index 72e57089f3..29cc10f299 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -18,6 +18,7 @@ loadvm_postcopy_handle_listen(void) "" loadvm_postcopy_handle_run(void) "" loadvm_postcopy_handle_run_cpu_sync(void) "" loadvm_postcopy_handle_run_vmstart(void) "" +loadvm_postcopy_handle_resume(void) "" loadvm_postcopy_ram_handle_discard(void) "" loadvm_postcopy_ram_handle_discard_end(void) "" loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %ud" @@ -35,6 +36,7 @@ savevm_send_open_return_path(void) "" savevm_send_ping(uint32_t val) "0x%x" savevm_send_postcopy_listen(void) "" savevm_send_postcopy_run(void) "" +savevm_send_postcopy_resume(void) "" savevm_send_recv_bitmap(char *name) "%s" savevm_state_setup(void) "" savevm_state_header(void) "" From 13955b89ce54a85a9e88c64a196db61aa8bf0f34 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:30 +0800 Subject: [PATCH 0527/2380] migration: new message MIG_RP_MSG_RESUME_ACK Creating new message to reply for MIG_CMD_POSTCOPY_RESUME. One uint32_t is used as payload to let the source know whether destination is ready to continue the migration. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-15-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 37 +++++++++++++++++++++++++++++++++++++ migration/migration.h | 3 +++ migration/savevm.c | 3 ++- migration/trace-events | 1 + 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index 7c5e20b3f6..4f2c6d22d1 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -96,6 +96,7 @@ enum mig_rp_message_type { MIG_RP_MSG_REQ_PAGES_ID, /* data (start: be64, len: be32, id: string) */ MIG_RP_MSG_REQ_PAGES, /* data (start: be64, len: be32) */ MIG_RP_MSG_RECV_BITMAP, /* send recved_bitmap back to source */ + MIG_RP_MSG_RESUME_ACK, /* tell source that we are ready to resume */ MIG_RP_MSG_MAX }; @@ -564,6 +565,14 @@ void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis, trace_migrate_send_rp_recv_bitmap(block_name, res); } +void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value) +{ + uint32_t buf; + + buf = cpu_to_be32(value); + migrate_send_rp_message(mis, MIG_RP_MSG_RESUME_ACK, sizeof(buf), &buf); +} + MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) { MigrationCapabilityStatusList *head = NULL; @@ -1843,6 +1852,7 @@ static struct rp_cmd_args { [MIG_RP_MSG_REQ_PAGES] = { .len = 12, .name = "REQ_PAGES" }, [MIG_RP_MSG_REQ_PAGES_ID] = { .len = -1, .name = "REQ_PAGES_ID" }, [MIG_RP_MSG_RECV_BITMAP] = { .len = -1, .name = "RECV_BITMAP" }, + [MIG_RP_MSG_RESUME_ACK] = { .len = 4, .name = "RESUME_ACK" }, [MIG_RP_MSG_MAX] = { .len = -1, .name = "MAX" }, }; @@ -1900,6 +1910,25 @@ static int migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name) return ram_dirty_bitmap_reload(s, block); } +static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value) +{ + trace_source_return_path_thread_resume_ack(value); + + if (value != MIGRATION_RESUME_ACK_VALUE) { + error_report("%s: illegal resume_ack value %"PRIu32, + __func__, value); + return -1; + } + + /* Now both sides are active. */ + migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_RECOVER, + MIGRATION_STATUS_POSTCOPY_ACTIVE); + + /* TODO: notify send thread that time to continue send pages */ + + return 0; +} + /* * Handles messages sent on the return path towards the source VM * @@ -2019,6 +2048,14 @@ retry: } break; + case MIG_RP_MSG_RESUME_ACK: + tmp32 = ldl_be_p(buf); + if (migrate_handle_rp_resume_ack(ms, tmp32)) { + mark_source_rp_bad(ms); + goto out; + } + break; + default: break; } diff --git a/migration/migration.h b/migration/migration.h index 2321ea37b3..556964d9d9 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -24,6 +24,8 @@ struct PostcopyBlocktimeContext; +#define MIGRATION_RESUME_ACK_VALUE (1) + /* State for the incoming migration */ struct MigrationIncomingState { QEMUFile *from_src_file; @@ -262,6 +264,7 @@ int migrate_send_rp_req_pages(MigrationIncomingState *mis, const char* rbname, ram_addr_t start, size_t len); void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis, char *block_name); +void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value); void dirty_bitmap_mig_before_vm_start(void); void init_dirty_bitmap_incoming_migration(void); diff --git a/migration/savevm.c b/migration/savevm.c index a7e793eef7..6a2d77cbf3 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1795,7 +1795,8 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) trace_loadvm_postcopy_handle_resume(); - /* TODO: Tell source that "we are ready" */ + /* Tell source that "we are ready" */ + migrate_send_rp_resume_ack(mis, MIGRATION_RESUME_ACK_VALUE); return 0; } diff --git a/migration/trace-events b/migration/trace-events index 29cc10f299..40a7217829 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -120,6 +120,7 @@ source_return_path_thread_entry(void) "" source_return_path_thread_loop_top(void) "" source_return_path_thread_pong(uint32_t val) "0x%x" source_return_path_thread_shut(uint32_t val) "0x%x" +source_return_path_thread_resume_ack(uint32_t v) "%"PRIu32 migrate_global_state_post_load(const char *state) "loaded state: %s" migrate_global_state_pre_save(const char *state) "saved state: %s" migration_thread_low_pending(uint64_t pending) "%" PRIu64 From d1b8eadbc43739992eed75912f6a065b9f299221 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:31 +0800 Subject: [PATCH 0528/2380] migration: introduce SaveVMHandlers.resume_prepare This is hook function to be called when a postcopy migration wants to resume from a failure. For each module, it should provide its own recovery logic before we switch to the postcopy-active state. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-16-peterx@redhat.com> Signed-off-by: Juan Quintela --- include/migration/register.h | 2 ++ migration/migration.c | 20 +++++++++++++++++++- migration/savevm.c | 25 +++++++++++++++++++++++++ migration/savevm.h | 1 + migration/trace-events | 1 + 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/include/migration/register.h b/include/migration/register.h index f6f12f9b1a..d287f4c317 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -64,6 +64,8 @@ typedef struct SaveVMHandlers { LoadStateHandler *load_state; int (*load_setup)(QEMUFile *f, void *opaque); int (*load_cleanup)(void *opaque); + /* Called when postcopy migration wants to resume from failure */ + int (*resume_prepare)(MigrationState *s, void *opaque); } SaveVMHandlers; int register_savevm_live(DeviceState *dev, diff --git a/migration/migration.c b/migration/migration.c index 4f2c6d22d1..b0217c4823 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2454,7 +2454,25 @@ typedef enum MigThrError { /* Return zero if success, or <0 for error */ static int postcopy_do_resume(MigrationState *s) { - /* TODO: do the resume logic */ + int ret; + + /* + * Call all the resume_prepare() hooks, so that modules can be + * ready for the migration resume. + */ + ret = qemu_savevm_state_resume_prepare(s); + if (ret) { + error_report("%s: resume_prepare() failure detected: %d", + __func__, ret); + return ret; + } + + /* + * TODO: handshake with dest using MIG_CMD_RESUME, + * MIG_RP_MSG_RESUME_ACK, then switch source state to + * "postcopy-active" + */ + return 0; } diff --git a/migration/savevm.c b/migration/savevm.c index 6a2d77cbf3..8e7653badc 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1031,6 +1031,31 @@ void qemu_savevm_state_setup(QEMUFile *f) } } +int qemu_savevm_state_resume_prepare(MigrationState *s) +{ + SaveStateEntry *se; + int ret; + + trace_savevm_state_resume_prepare(); + + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (!se->ops || !se->ops->resume_prepare) { + continue; + } + if (se->ops && se->ops->is_active) { + if (!se->ops->is_active(se->opaque)) { + continue; + } + } + ret = se->ops->resume_prepare(s, se->opaque); + if (ret < 0) { + return ret; + } + } + + return 0; +} + /* * this function has three return values: * negative: there was one error, and we have -errno. diff --git a/migration/savevm.h b/migration/savevm.h index b24ff073ad..a5e65b8ae3 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -31,6 +31,7 @@ bool qemu_savevm_state_blocked(Error **errp); void qemu_savevm_state_setup(QEMUFile *f); +int qemu_savevm_state_resume_prepare(MigrationState *s); void qemu_savevm_state_header(QEMUFile *f); int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy); void qemu_savevm_state_cleanup(void); diff --git a/migration/trace-events b/migration/trace-events index 40a7217829..be36fbccfe 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -39,6 +39,7 @@ savevm_send_postcopy_run(void) "" savevm_send_postcopy_resume(void) "" savevm_send_recv_bitmap(char *name) "%s" savevm_state_setup(void) "" +savevm_state_resume_prepare(void) "" savevm_state_header(void) "" savevm_state_iterate(void) "" savevm_state_cleanup(void) "" From edd090c72825b483df4de6a525d430d7635a5d8e Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:32 +0800 Subject: [PATCH 0529/2380] migration: synchronize dirty bitmap for resume This patch implements the first part of core RAM resume logic for postcopy. ram_resume_prepare() is provided for the work. When the migration is interrupted by network failure, the dirty bitmap on the source side will be meaningless, because even the dirty bit is cleared, it is still possible that the sent page was lost along the way to destination. Here instead of continue the migration with the old dirty bitmap on source, we ask the destination side to send back its received bitmap, then invert it to be our initial dirty bitmap. The source side send thread will issue the MIG_CMD_RECV_BITMAP requests, once per ramblock, to ask for the received bitmap. On destination side, MIG_RP_MSG_RECV_BITMAP will be issued, along with the requested bitmap. Data will be received on the return-path thread of source, and the main migration thread will be notified when all the ramblock bitmaps are synchronized. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-17-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 2 ++ migration/migration.h | 1 + migration/ram.c | 47 ++++++++++++++++++++++++++++++++++++++++++ migration/trace-events | 4 ++++ 4 files changed, 54 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index b0217c4823..19ef8b05b1 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2967,6 +2967,7 @@ static void migration_instance_finalize(Object *obj) qemu_sem_destroy(&ms->pause_sem); qemu_sem_destroy(&ms->postcopy_pause_sem); qemu_sem_destroy(&ms->postcopy_pause_rp_sem); + qemu_sem_destroy(&ms->rp_state.rp_sem); error_free(ms->error); } @@ -2999,6 +3000,7 @@ static void migration_instance_init(Object *obj) qemu_sem_init(&ms->postcopy_pause_sem, 0); qemu_sem_init(&ms->postcopy_pause_rp_sem, 0); + qemu_sem_init(&ms->rp_state.rp_sem, 0); } /* diff --git a/migration/migration.h b/migration/migration.h index 556964d9d9..b4438ccb65 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -135,6 +135,7 @@ struct MigrationState QEMUFile *from_dst_file; QemuThread rp_thread; bool error; + QemuSemaphore rp_sem; } rp_state; double mbps; diff --git a/migration/ram.c b/migration/ram.c index 5542843adc..b16eabcfb9 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -54,6 +54,7 @@ #include "migration/block.h" #include "sysemu/sysemu.h" #include "qemu/uuid.h" +#include "savevm.h" /***********************************************************/ /* ram save/restore */ @@ -3364,6 +3365,38 @@ static bool ram_has_postcopy(void *opaque) return migrate_postcopy_ram(); } +/* Sync all the dirty bitmap with destination VM. */ +static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) +{ + RAMBlock *block; + QEMUFile *file = s->to_dst_file; + int ramblock_count = 0; + + trace_ram_dirty_bitmap_sync_start(); + + RAMBLOCK_FOREACH(block) { + qemu_savevm_send_recv_bitmap(file, block->idstr); + trace_ram_dirty_bitmap_request(block->idstr); + ramblock_count++; + } + + trace_ram_dirty_bitmap_sync_wait(); + + /* Wait until all the ramblocks' dirty bitmap synced */ + while (ramblock_count--) { + qemu_sem_wait(&s->rp_state.rp_sem); + } + + trace_ram_dirty_bitmap_sync_complete(); + + return 0; +} + +static void ram_dirty_bitmap_reload_notify(MigrationState *s) +{ + qemu_sem_post(&s->rp_state.rp_sem); +} + /* * Read the received bitmap, revert it as the initial dirty bitmap. * This is only used when the postcopy migration is paused but wants @@ -3438,12 +3471,25 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) trace_ram_dirty_bitmap_reload_complete(block->idstr); + /* + * We succeeded to sync bitmap for current ramblock. If this is + * the last one to sync, we need to notify the main send thread. + */ + ram_dirty_bitmap_reload_notify(s); + ret = 0; out: free(le_bitmap); return ret; } +static int ram_resume_prepare(MigrationState *s, void *opaque) +{ + RAMState *rs = *(RAMState **)opaque; + + return ram_dirty_bitmap_sync_all(s, rs); +} + static SaveVMHandlers savevm_ram_handlers = { .save_setup = ram_save_setup, .save_live_iterate = ram_save_iterate, @@ -3455,6 +3501,7 @@ static SaveVMHandlers savevm_ram_handlers = { .save_cleanup = ram_save_cleanup, .load_setup = ram_load_setup, .load_cleanup = ram_load_cleanup, + .resume_prepare = ram_resume_prepare, }; void ram_mig_init(void) diff --git a/migration/trace-events b/migration/trace-events index be36fbccfe..53243e17ec 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -82,8 +82,12 @@ ram_load_postcopy_loop(uint64_t addr, int flags) "@%" PRIx64 " %x" ram_postcopy_send_discard_bitmap(void) "" ram_save_page(const char *rbname, uint64_t offset, void *host) "%s: offset: 0x%" PRIx64 " host: %p" ram_save_queue_pages(const char *rbname, size_t start, size_t len) "%s: start: 0x%zx len: 0x%zx" +ram_dirty_bitmap_request(char *str) "%s" ram_dirty_bitmap_reload_begin(char *str) "%s" ram_dirty_bitmap_reload_complete(char *str) "%s" +ram_dirty_bitmap_sync_start(void) "" +ram_dirty_bitmap_sync_wait(void) "" +ram_dirty_bitmap_sync_complete(void) "" # migration/migration.c await_return_path_close_on_source_close(void) "" From 08614f34977a18fdc06c56f4e12b6cf47c06da57 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:33 +0800 Subject: [PATCH 0530/2380] migration: setup ramstate for resume After we updated the dirty bitmaps of ramblocks, we also need to update the critical fields in RAMState to make sure it is ready for a resume. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-18-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/ram.c | 45 +++++++++++++++++++++++++++++++++++++++++- migration/trace-events | 1 + 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index b16eabcfb9..5bcbf7a9f9 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2491,6 +2491,41 @@ static int ram_init_all(RAMState **rsp) return 0; } +static void ram_state_resume_prepare(RAMState *rs, QEMUFile *out) +{ + RAMBlock *block; + uint64_t pages = 0; + + /* + * Postcopy is not using xbzrle/compression, so no need for that. + * Also, since source are already halted, we don't need to care + * about dirty page logging as well. + */ + + RAMBLOCK_FOREACH(block) { + pages += bitmap_count_one(block->bmap, + block->used_length >> TARGET_PAGE_BITS); + } + + /* This may not be aligned with current bitmaps. Recalculate. */ + rs->migration_dirty_pages = pages; + + rs->last_seen_block = NULL; + rs->last_sent_block = NULL; + rs->last_page = 0; + rs->last_version = ram_list.version; + /* + * Disable the bulk stage, otherwise we'll resend the whole RAM no + * matter what we have sent. + */ + rs->ram_bulk_stage = false; + + /* Update RAMState cache of output QEMUFile */ + rs->f = out; + + trace_ram_state_resume_prepare(pages); +} + /* * Each of ram_save_setup, ram_save_iterate and ram_save_complete has * long-running RCU critical section. When rcu-reclaims in the code @@ -3486,8 +3521,16 @@ out: static int ram_resume_prepare(MigrationState *s, void *opaque) { RAMState *rs = *(RAMState **)opaque; + int ret; - return ram_dirty_bitmap_sync_all(s, rs); + ret = ram_dirty_bitmap_sync_all(s, rs); + if (ret) { + return ret; + } + + ram_state_resume_prepare(rs, s->to_dst_file); + + return 0; } static SaveVMHandlers savevm_ram_handlers = { diff --git a/migration/trace-events b/migration/trace-events index 53243e17ec..3c798ddd11 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -88,6 +88,7 @@ ram_dirty_bitmap_reload_complete(char *str) "%s" ram_dirty_bitmap_sync_start(void) "" ram_dirty_bitmap_sync_wait(void) "" ram_dirty_bitmap_sync_complete(void) "" +ram_state_resume_prepare(uint64_t v) "%" PRId64 # migration/migration.c await_return_path_close_on_source_close(void) "" From 9419069695a534b1c8051e8ba8f3b4b3d21e4520 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:34 +0800 Subject: [PATCH 0531/2380] migration: final handshake for the resume Finish the last step to do the final handshake for the recovery. First source sends one MIG_CMD_RESUME to dst, telling that source is ready to resume. Then, dest replies with MIG_RP_MSG_RESUME_ACK to source, telling that dest is ready to resume (after switch to postcopy-active state). When source received the RESUME_ACK, it switches its state to postcopy-active, and finally the recovery is completed. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-19-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 19ef8b05b1..240960d951 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1924,7 +1924,8 @@ static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value) migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_RECOVER, MIGRATION_STATUS_POSTCOPY_ACTIVE); - /* TODO: notify send thread that time to continue send pages */ + /* Notify send thread that time to continue send pages */ + qemu_sem_post(&s->rp_state.rp_sem); return 0; } @@ -2451,6 +2452,21 @@ typedef enum MigThrError { MIG_THR_ERR_FATAL = 2, } MigThrError; +static int postcopy_resume_handshake(MigrationState *s) +{ + qemu_savevm_send_postcopy_resume(s->to_dst_file); + + while (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { + qemu_sem_wait(&s->rp_state.rp_sem); + } + + if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + return 0; + } + + return -1; +} + /* Return zero if success, or <0 for error */ static int postcopy_do_resume(MigrationState *s) { @@ -2468,10 +2484,14 @@ static int postcopy_do_resume(MigrationState *s) } /* - * TODO: handshake with dest using MIG_CMD_RESUME, - * MIG_RP_MSG_RESUME_ACK, then switch source state to - * "postcopy-active" + * Last handshake with destination on the resume (destination will + * switch to postcopy-active afterwards) */ + ret = postcopy_resume_handshake(s); + if (ret) { + error_report("%s: handshake failed: %d", __func__, ret); + return ret; + } return 0; } From e1b1b1bc367a1997373c10a58db4adb428daf54e Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:35 +0800 Subject: [PATCH 0532/2380] migration: init dst in migration_object_init too Though we may not need it, now we init both the src/dst migration objects in migration_object_init() so that even incoming migration object would be thread safe (it was not). Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-20-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/migration.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 240960d951..c1832b0263 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -106,6 +106,7 @@ enum mig_rp_message_type { dynamic creation of migration */ static MigrationState *current_migration; +static MigrationIncomingState *current_incoming; static bool migration_object_check(MigrationState *ms, Error **errp); static int migration_maybe_pause(MigrationState *s, @@ -121,6 +122,22 @@ void migration_object_init(void) assert(!current_migration); current_migration = MIGRATION_OBJ(object_new(TYPE_MIGRATION)); + /* + * Init the migrate incoming object as well no matter whether + * we'll use it or not. + */ + assert(!current_incoming); + current_incoming = g_new0(MigrationIncomingState, 1); + current_incoming->state = MIGRATION_STATUS_NONE; + current_incoming->postcopy_remote_fds = + g_array_new(FALSE, TRUE, sizeof(struct PostCopyFD)); + qemu_mutex_init(¤t_incoming->rp_mutex); + qemu_event_init(¤t_incoming->main_thread_load_event, false); + qemu_sem_init(¤t_incoming->postcopy_pause_sem_dst, 0); + qemu_sem_init(¤t_incoming->postcopy_pause_sem_fault, 0); + + init_dirty_bitmap_incoming_migration(); + if (!migration_object_check(current_migration, &err)) { error_report_err(err); exit(1); @@ -151,24 +168,8 @@ MigrationState *migrate_get_current(void) MigrationIncomingState *migration_incoming_get_current(void) { - static bool once; - static MigrationIncomingState mis_current; - - if (!once) { - mis_current.state = MIGRATION_STATUS_NONE; - memset(&mis_current, 0, sizeof(MigrationIncomingState)); - mis_current.postcopy_remote_fds = g_array_new(FALSE, TRUE, - sizeof(struct PostCopyFD)); - qemu_mutex_init(&mis_current.rp_mutex); - qemu_event_init(&mis_current.main_thread_load_event, false); - qemu_sem_init(&mis_current.postcopy_pause_sem_dst, 0); - qemu_sem_init(&mis_current.postcopy_pause_sem_fault, 0); - - init_dirty_bitmap_incoming_migration(); - - once = true; - } - return &mis_current; + assert(current_incoming); + return current_incoming; } void migration_incoming_state_destroy(void) From 02affd41b1157c511ea7fd0cc0bc61a07d9696b1 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:36 +0800 Subject: [PATCH 0533/2380] qmp/migration: new command migrate-recover The first allow-oob=true command. It's used on destination side when the postcopy migration is paused and ready for a recovery. After execution, a new migration channel will be established for postcopy to continue. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-21-peterx@redhat.com> Signed-off-by: Juan Quintela --- s/2.12/2.13/ --- migration/migration.c | 24 ++++++++++++++++++++++++ migration/migration.h | 1 + migration/savevm.c | 3 +++ qapi/migration.json | 20 ++++++++++++++++++++ 4 files changed, 48 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index c1832b0263..1beb5e07fb 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1475,6 +1475,30 @@ void qmp_migrate_incoming(const char *uri, Error **errp) once = false; } +void qmp_migrate_recover(const char *uri, Error **errp) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + + if (mis->state != MIGRATION_STATUS_POSTCOPY_PAUSED) { + error_setg(errp, "Migrate recover can only be run " + "when postcopy is paused."); + return; + } + + if (atomic_cmpxchg(&mis->postcopy_recover_triggered, + false, true) == true) { + error_setg(errp, "Migrate recovery is triggered already"); + return; + } + + /* + * Note that this call will never start a real migration; it will + * only re-setup the migration stream and poke existing migration + * to continue using that newly established channel. + */ + qemu_start_incoming_migration(uri, errp); +} + bool migration_is_blocked(Error **errp) { if (qemu_savevm_state_blocked(errp)) { diff --git a/migration/migration.h b/migration/migration.h index b4438ccb65..f83f1064b5 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -77,6 +77,7 @@ struct MigrationIncomingState { struct PostcopyBlocktimeContext *blocktime_ctx; /* notify PAUSED postcopy incoming migrations to try to continue */ + bool postcopy_recover_triggered; QemuSemaphore postcopy_pause_sem_dst; QemuSemaphore postcopy_pause_sem_fault; }; diff --git a/migration/savevm.c b/migration/savevm.c index 8e7653badc..4251125831 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2190,6 +2190,9 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) { trace_postcopy_pause_incoming(); + /* Clear the triggered bit to allow one recovery */ + mis->postcopy_recover_triggered = false; + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, MIGRATION_STATUS_POSTCOPY_PAUSED); diff --git a/qapi/migration.json b/qapi/migration.json index 4e8e61ceef..da351d7421 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1191,3 +1191,23 @@ # Since: 2.9 ## { 'command': 'xen-colo-do-checkpoint' } + +## +# @migrate-recover: +# +# Provide a recovery migration stream URI. +# +# @uri: the URI to be used for the recovery of migration stream. +# +# Returns: nothing. +# +# Example: +# +# -> { "execute": "migrate-recover", +# "arguments": { "uri": "tcp:192.168.1.200:12345" } } +# <- { "return": {} } +# +# Since: 2.13 +## +{ 'command': 'migrate-recover', 'data': { 'uri': 'str' }, + 'allow-oob': true } From 3b563c4be01f42d2a4e641d50736bd5ac8e3b15f Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:37 +0800 Subject: [PATCH 0534/2380] hmp/migration: add migrate_recover command Sister command to migrate-recover in QMP. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-22-peterx@redhat.com> Signed-off-by: Juan Quintela --- hmp-commands.hx | 13 +++++++++++++ hmp.c | 10 ++++++++++ hmp.h | 1 + 3 files changed, 24 insertions(+) diff --git a/hmp-commands.hx b/hmp-commands.hx index a7051ee391..2fa387d341 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -957,7 +957,20 @@ STEXI @findex migrate_incoming Continue an incoming migration using the @var{uri} (that has the same syntax as the -incoming option). +ETEXI + { + .name = "migrate_recover", + .args_type = "uri:s", + .params = "uri", + .help = "Continue a paused incoming postcopy migration", + .cmd = hmp_migrate_recover, + }, + +STEXI +@item migrate_recover @var{uri} +@findex migrate_recover +Continue a paused incoming postcopy migration using the @var{uri}. ETEXI { diff --git a/hmp.c b/hmp.c index a7aa878788..2a36c1cf8d 100644 --- a/hmp.c +++ b/hmp.c @@ -1517,6 +1517,16 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } +void hmp_migrate_recover(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *uri = qdict_get_str(qdict, "uri"); + + qmp_migrate_recover(uri, &err); + + hmp_handle_error(mon, &err); +} + /* Kept for backwards compatibility */ void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict) { diff --git a/hmp.h b/hmp.h index 4e2ec375b0..b6b56c8161 100644 --- a/hmp.h +++ b/hmp.h @@ -68,6 +68,7 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict); void hmp_migrate_cancel(Monitor *mon, const QDict *qdict); void hmp_migrate_continue(Monitor *mon, const QDict *qdict); void hmp_migrate_incoming(Monitor *mon, const QDict *qdict); +void hmp_migrate_recover(Monitor *mon, const QDict *qdict); void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict); void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict); void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict); From 62df066ffffdbd41b42ca91130611e40fbc53f0b Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:38 +0800 Subject: [PATCH 0535/2380] migration: introduce lock for to_dst_file Let's introduce a lock for that QEMUFile since we are going to operate on it in multiple threads. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-23-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/channel.c | 3 ++- migration/migration.c | 22 +++++++++++++++++++--- migration/migration.h | 6 ++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/migration/channel.c b/migration/channel.c index c5eaf0fa0e..716192bf75 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -74,8 +74,9 @@ void migration_channel_connect(MigrationState *s, } else { QEMUFile *f = qemu_fopen_channel_output(ioc); + qemu_mutex_lock(&s->qemu_file_lock); s->to_dst_file = f; - + qemu_mutex_unlock(&s->qemu_file_lock); } } migrate_fd_connect(s, error); diff --git a/migration/migration.c b/migration/migration.c index 1beb5e07fb..3deded90e5 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1234,6 +1234,7 @@ static void migrate_fd_cleanup(void *opaque) if (s->to_dst_file) { Error *local_err = NULL; + QEMUFile *tmp; trace_migrate_fd_cleanup(); qemu_mutex_unlock_iothread(); @@ -1246,8 +1247,15 @@ static void migrate_fd_cleanup(void *opaque) if (multifd_save_cleanup(&local_err) != 0) { error_report_err(local_err); } - qemu_fclose(s->to_dst_file); + qemu_mutex_lock(&s->qemu_file_lock); + tmp = s->to_dst_file; s->to_dst_file = NULL; + qemu_mutex_unlock(&s->qemu_file_lock); + /* + * Close the file handle without the lock to make sure the + * critical section won't block for long. + */ + qemu_fclose(tmp); } assert((s->state != MIGRATION_STATUS_ACTIVE) && @@ -2531,14 +2539,20 @@ static MigThrError postcopy_pause(MigrationState *s) assert(s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); while (true) { + QEMUFile *file; + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_POSTCOPY_PAUSED); /* Current channel is possibly broken. Release it. */ assert(s->to_dst_file); - qemu_file_shutdown(s->to_dst_file); - qemu_fclose(s->to_dst_file); + qemu_mutex_lock(&s->qemu_file_lock); + file = s->to_dst_file; s->to_dst_file = NULL; + qemu_mutex_unlock(&s->qemu_file_lock); + + qemu_file_shutdown(file); + qemu_fclose(file); error_report("Detected IO failure for postcopy. " "Migration paused."); @@ -3007,6 +3021,7 @@ static void migration_instance_finalize(Object *obj) MigrationParameters *params = &ms->parameters; qemu_mutex_destroy(&ms->error_mutex); + qemu_mutex_destroy(&ms->qemu_file_lock); g_free(params->tls_hostname); g_free(params->tls_creds); qemu_sem_destroy(&ms->pause_sem); @@ -3046,6 +3061,7 @@ static void migration_instance_init(Object *obj) qemu_sem_init(&ms->postcopy_pause_sem, 0); qemu_sem_init(&ms->postcopy_pause_rp_sem, 0); qemu_sem_init(&ms->rp_state.rp_sem, 0); + qemu_mutex_init(&ms->qemu_file_lock); } /* diff --git a/migration/migration.h b/migration/migration.h index f83f1064b5..8f0c82159b 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -114,6 +114,12 @@ struct MigrationState QemuThread thread; QEMUBH *cleanup_bh; QEMUFile *to_dst_file; + /* + * Protects to_dst_file pointer. We need to make sure we won't + * yield or hang during the critical section, since this lock will + * be used in OOB command handler. + */ + QemuMutex qemu_file_lock; /* bytes already send at the beggining of current interation */ uint64_t iteration_initial_bytes; From bfbf89c2b524670edbf71e12fc5bc1b34d925211 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:39 +0800 Subject: [PATCH 0536/2380] migration/qmp: add command migrate-pause It pauses an ongoing migration. Currently it only supports postcopy. Note that this command will work on either side of the migration. Basically when we trigger this on one side, it'll interrupt the other side as well since the other side will get notified on the disconnect event. However, it's still possible that the other side is not notified, for example, when the network is totally broken, or due to some firewall configuration changes. In that case, we will also need to run the same command on the other side so both sides will go into the paused state. Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-24-peterx@redhat.com> Signed-off-by: Juan Quintela --- s/2.12/2.13/ --- migration/migration.c | 29 +++++++++++++++++++++++++++++ qapi/migration.json | 16 ++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index 3deded90e5..05aec2c905 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1507,6 +1507,35 @@ void qmp_migrate_recover(const char *uri, Error **errp) qemu_start_incoming_migration(uri, errp); } +void qmp_migrate_pause(Error **errp) +{ + MigrationState *ms = migrate_get_current(); + MigrationIncomingState *mis = migration_incoming_get_current(); + int ret; + + if (ms->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + /* Source side, during postcopy */ + qemu_mutex_lock(&ms->qemu_file_lock); + ret = qemu_file_shutdown(ms->to_dst_file); + qemu_mutex_unlock(&ms->qemu_file_lock); + if (ret) { + error_setg(errp, "Failed to pause source migration"); + } + return; + } + + if (mis->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + ret = qemu_file_shutdown(mis->from_src_file); + if (ret) { + error_setg(errp, "Failed to pause destination migration"); + } + return; + } + + error_setg(errp, "migrate-pause is currently only supported " + "during postcopy-active state"); +} + bool migration_is_blocked(Error **errp) { if (qemu_savevm_state_blocked(errp)) { diff --git a/qapi/migration.json b/qapi/migration.json index da351d7421..23ba85ed3a 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1211,3 +1211,19 @@ ## { 'command': 'migrate-recover', 'data': { 'uri': 'str' }, 'allow-oob': true } + +## +# @migrate-pause: +# +# Pause a migration. Currently it only supports postcopy. +# +# Returns: nothing. +# +# Example: +# +# -> { "execute": "migrate-pause" } +# <- { "return": {} } +# +# Since: 2.13 +## +{ 'command': 'migrate-pause', 'allow-oob': true } From d37297dc66202c33f9cafbc48ccae629e7d6dc31 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 2 May 2018 18:47:40 +0800 Subject: [PATCH 0537/2380] migration/hmp: add migrate_pause command Wrapper for QMP command "migrate-pause". Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180502104740.12123-25-peterx@redhat.com> Signed-off-by: Juan Quintela --- hmp-commands.hx | 14 ++++++++++++++ hmp.c | 9 +++++++++ hmp.h | 1 + 3 files changed, 24 insertions(+) diff --git a/hmp-commands.hx b/hmp-commands.hx index 2fa387d341..0734fea931 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -971,6 +971,20 @@ STEXI @item migrate_recover @var{uri} @findex migrate_recover Continue a paused incoming postcopy migration using the @var{uri}. +ETEXI + + { + .name = "migrate_pause", + .args_type = "", + .params = "", + .help = "Pause an ongoing migration (postcopy-only)", + .cmd = hmp_migrate_pause, + }, + +STEXI +@item migrate_pause +@findex migrate_pause +Pause an ongoing migration. Currently it only supports postcopy. ETEXI { diff --git a/hmp.c b/hmp.c index 2a36c1cf8d..ef93f4878b 100644 --- a/hmp.c +++ b/hmp.c @@ -1527,6 +1527,15 @@ void hmp_migrate_recover(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } +void hmp_migrate_pause(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_migrate_pause(&err); + + hmp_handle_error(mon, &err); +} + /* Kept for backwards compatibility */ void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict) { diff --git a/hmp.h b/hmp.h index b6b56c8161..20f27439d3 100644 --- a/hmp.h +++ b/hmp.h @@ -69,6 +69,7 @@ void hmp_migrate_cancel(Monitor *mon, const QDict *qdict); void hmp_migrate_continue(Monitor *mon, const QDict *qdict); void hmp_migrate_incoming(Monitor *mon, const QDict *qdict); void hmp_migrate_recover(Monitor *mon, const QDict *qdict); +void hmp_migrate_pause(Monitor *mon, const QDict *qdict); void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict); void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict); void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict); From edd7080692cb43147352d736cce0fb728787f2b4 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 3 May 2018 20:10:59 +0100 Subject: [PATCH 0538/2380] migration: update docs Update the migration docs: Among other changes: * Added a general list of advice for device authors * Reordered the section on conditional state (subsections etc) into the order we prefer. * Add a note about firmware Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Peter Xu Reviewed-by: Balamuruhan S Reviewed-by: Juan Quintela Message-Id: <20180503191059.19576-1-dgilbert@redhat.com> Signed-off-by: Juan Quintela --- docs/devel/migration.rst | 516 ++++++++++++++++++++++++++++----------- 1 file changed, 368 insertions(+), 148 deletions(-) diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index 9342a8af06..40f136f6be 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -28,11 +28,11 @@ the guest to be stopped. Typically the time that the guest is unresponsive during live migration is the low hundred of milliseconds (notice that this depends on a lot of things). -Types of migration -================== +Transports +========== -Now that we have talked about live migration, there are several ways -to do migration: +The migration stream is normally just a byte stream that can be passed +over any transport. - tcp migration: do the migration using tcp sockets - unix migration: do the migration using unix sockets @@ -40,16 +40,16 @@ to do migration: - fd migration: do the migration using an file descriptor that is passed to QEMU. QEMU doesn't care how this file descriptor is opened. -All these four migration protocols use the same infrastructure to +In addition, support is included for migration using RDMA, which +transports the page data using ``RDMA``, where the hardware takes care of +transporting the pages, and the load on the CPU is much lower. While the +internals of RDMA migration are a bit different, this isn't really visible +outside the RAM migration code. + +All these migration protocols use the same infrastructure to save/restore state devices. This infrastructure is shared with the savevm/loadvm functionality. -State Live Migration -==================== - -This is used for RAM and block devices. It is not yet ported to vmstate. - - Common infrastructure ===================== @@ -57,60 +57,75 @@ The files, sockets or fd's that carry the migration stream are abstracted by the ``QEMUFile`` type (see `migration/qemu-file.h`). In most cases this is connected to a subtype of ``QIOChannel`` (see `io/`). + Saving the state of one device ============================== -The state of a device is saved using intermediate buffers. There are -some helper functions to assist this saving. +For most devices, the state is saved in a single call to the migration +infrastructure; these are *non-iterative* devices. The data for these +devices is sent at the end of precopy migration, when the CPUs are paused. +There are also *iterative* devices, which contain a very large amount of +data (e.g. RAM or large tables). See the iterative device section below. -There is a new concept that we have to explain here: device state -version. When we migrate a device, we save/load the state as a series -of fields. Some times, due to bugs or new functionality, we need to -change the state to store more/different information. We use the -version to identify each time that we do a change. Each version is -associated with a series of fields saved. The `save_state` always saves -the state as the newer version. But `load_state` sometimes is able to -load state from an older version. +General advice for device developers +------------------------------------ -Legacy way ----------- +- The migration state saved should reflect the device being modelled rather + than the way your implementation works. That way if you change the implementation + later the migration stream will stay compatible. That model may include + internal state that's not directly visible in a register. -This way is going to disappear as soon as all current users are ported to VMSTATE. +- When saving a migration stream the device code may walk and check + the state of the device. These checks might fail in various ways (e.g. + discovering internal state is corrupt or that the guest has done something bad). + Consider carefully before asserting/aborting at this point, since the + normal response from users is that *migration broke their VM* since it had + apparently been running fine until then. In these error cases, the device + should log a message indicating the cause of error, and should consider + putting the device into an error state, allowing the rest of the VM to + continue execution. -Each device has to register two functions, one to save the state and -another to load the state back. +- The migration might happen at an inconvenient point, + e.g. right in the middle of the guest reprogramming the device, during + guest reboot or shutdown or while the device is waiting for external IO. + It's strongly preferred that migrations do not fail in this situation, + since in the cloud environment migrations might happen automatically to + VMs that the administrator doesn't directly control. -.. code:: c +- If you do need to fail a migration, ensure that sufficient information + is logged to identify what went wrong. - int register_savevm(DeviceState *dev, - const char *idstr, - int instance_id, - int version_id, - SaveStateHandler *save_state, - LoadStateHandler *load_state, - void *opaque); +- The destination should treat an incoming migration stream as hostile + (which we do to varying degrees in the existing code). Check that offsets + into buffers and the like can't cause overruns. Fail the incoming migration + in the case of a corrupted stream like this. - typedef void SaveStateHandler(QEMUFile *f, void *opaque); - typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); +- Take care with internal device state or behaviour that might become + migration version dependent. For example, the order of PCI capabilities + is required to stay constant across migration. Another example would + be that a special case handled by subsections (see below) might become + much more common if a default behaviour is changed. -The important functions for the device state format are the `save_state` -and `load_state`. Notice that `load_state` receives a version_id -parameter to know what state format is receiving. `save_state` doesn't -have a version_id parameter because it always uses the latest version. +- The state of the source should not be changed or destroyed by the + outgoing migration. Migrations timing out or being failed by + higher levels of management, or failures of the destination host are + not unusual, and in that case the VM is restarted on the source. + Note that the management layer can validly revert the migration + even though the QEMU level of migration has succeeded as long as it + does it before starting execution on the destination. + +- Buses and devices should be able to explicitly specify addresses when + instantiated, and management tools should use those. For example, + when hot adding USB devices it's important to specify the ports + and addresses, since implicit ordering based on the command line order + may be different on the destination. This can result in the + device state being loaded into the wrong device. VMState ------- -The legacy way of saving/loading state of the device had the problem -that we have to maintain two functions in sync. If we did one change -in one of them and not in the other, we would get a failed migration. - -VMState changed the way that state is saved/loaded. Instead of using -a function to save the state and another to load it, it was changed to -a declarative way of what the state consisted of. Now VMState is able -to interpret that definition to be able to load/save the state. As -the state is declared only once, it can't go out of sync in the -save/load functions. +Most device data can be described using the ``VMSTATE`` macros (mostly defined +in ``include/migration/vmstate.h``). An example (from hw/input/pckbd.c) @@ -137,103 +152,99 @@ We registered this with: vmstate_register(NULL, 0, &vmstate_kbd, s); -Note: talk about how vmstate <-> qdev interact, and what the instance ids mean. +For devices that are `qdev` based, we can register the device in the class +init function: -You can search for ``VMSTATE_*`` macros for lots of types used in QEMU in -include/hw/hw.h. +.. code:: c -More about versions -------------------- + dc->vmsd = &vmstate_kbd_isa; -Version numbers are intended for major incompatible changes to the -migration of a device, and using them breaks backwards-migration -compatibility; in general most changes can be made by adding Subsections -(see below) or _TEST macros (see below) which won't break compatibility. +The VMState macros take care of ensuring that the device data section +is formatted portably (normally big endian) and make some compile time checks +against the types of the fields in the structures. -You can see that there are several version fields: +VMState macros can include other VMStateDescriptions to store substructures +(see ``VMSTATE_STRUCT_``), arrays (``VMSTATE_ARRAY_``) and variable length +arrays (``VMSTATE_VARRAY_``). Various other macros exist for special +cases. -- `version_id`: the maximum version_id supported by VMState for that device. -- `minimum_version_id`: the minimum version_id that VMState is able to understand - for that device. -- `minimum_version_id_old`: For devices that were not able to port to vmstate, we can - assign a function that knows how to read this old state. This field is - ignored if there is no `load_state_old` handler. +Note that the format on the wire is still very raw; i.e. a VMSTATE_UINT32 +ends up with a 4 byte bigendian representation on the wire; in the future +it might be possible to use a more structured format. -So, VMState is able to read versions from minimum_version_id to -version_id. And the function ``load_state_old()`` (if present) is able to -load state from minimum_version_id_old to minimum_version_id. This -function is deprecated and will be removed when no more users are left. +Legacy way +---------- -Saving state will always create a section with the 'version_id' value -and thus can't be loaded by any older QEMU. +This way is going to disappear as soon as all current users are ported to VMSTATE; +although converting existing code can be tricky, and thus 'soon' is relative. -Massaging functions -------------------- +Each device has to register two functions, one to save the state and +another to load the state back. -Sometimes, it is not enough to be able to save the state directly -from one structure, we need to fill the correct values there. One -example is when we are using kvm. Before saving the cpu state, we -need to ask kvm to copy to QEMU the state that it is using. And the -opposite when we are loading the state, we need a way to tell kvm to -load the state for the cpu that we have just loaded from the QEMUFile. +.. code:: c -The functions to do that are inside a vmstate definition, and are called: + int register_savevm_live(DeviceState *dev, + const char *idstr, + int instance_id, + int version_id, + SaveVMHandlers *ops, + void *opaque); -- ``int (*pre_load)(void *opaque);`` +Two functions in the ``ops`` structure are the `save_state` +and `load_state` functions. Notice that `load_state` receives a version_id +parameter to know what state format is receiving. `save_state` doesn't +have a version_id parameter because it always uses the latest version. - This function is called before we load the state of one device. +Note that because the VMState macros still save the data in a raw +format, in many cases it's possible to replace legacy code +with a carefully constructed VMState description that matches the +byte layout of the existing code. -- ``int (*post_load)(void *opaque, int version_id);`` +Changing migration data structures +---------------------------------- - This function is called after we load the state of one device. - -- ``int (*pre_save)(void *opaque);`` - - This function is called before we save the state of one device. - -Example: You can look at hpet.c, that uses the three function to -massage the state that is transferred. - -If you use memory API functions that update memory layout outside -initialization (i.e., in response to a guest action), this is a strong -indication that you need to call these functions in a `post_load` callback. -Examples of such memory API functions are: - - - memory_region_add_subregion() - - memory_region_del_subregion() - - memory_region_set_readonly() - - memory_region_set_enabled() - - memory_region_set_address() - - memory_region_set_alias_offset() +When we migrate a device, we save/load the state as a series +of fields. Sometimes, due to bugs or new functionality, we need to +change the state to store more/different information. Changing the migration +state saved for a device can break migration compatibility unless +care is taken to use the appropriate techniques. In general QEMU tries +to maintain forward migration compatibility (i.e. migrating from +QEMU n->n+1) and there are users who benefit from backward compatibility +as well. Subsections ----------- -The use of version_id allows to be able to migrate from older versions -to newer versions of a device. But not the other way around. This -makes very complicated to fix bugs in stable branches. If we need to -add anything to the state to fix a bug, we have to disable migration -to older versions that don't have that bug-fix (i.e. a new field). +The most common structure change is adding new data, e.g. when adding +a newer form of device, or adding that state that you previously +forgot to migrate. This is best solved using a subsection. -But sometimes, that bug-fix is only needed sometimes, not always. For -instance, if the device is in the middle of a DMA operation, it is -using a specific functionality, .... - -It is impossible to create a way to make migration from any version to -any other version to work. But we can do better than only allowing -migration from older versions to newer ones. For that fields that are -only needed sometimes, we add the idea of subsections. A subsection -is "like" a device vmstate, but with a particularity, it has a Boolean -function that tells if that values are needed to be sent or not. If -this functions returns false, the subsection is not sent. +A subsection is "like" a device vmstate, but with a particularity, it +has a Boolean function that tells if that values are needed to be sent +or not. If this functions returns false, the subsection is not sent. +Subsections have a unique name, that is looked for on the receiving +side. On the receiving side, if we found a subsection for a device that we don't understand, we just fail the migration. If we understand all -the subsections, then we load the state with success. +the subsections, then we load the state with success. There's no check +that a subsection is loaded, so a newer QEMU that knows about a subsection +can (with care) load a stream from an older QEMU that didn't send +the subsection. + +If the new data is only needed in a rare case, then the subsection +can be made conditional on that case and the migration will still +succeed to older QEMUs in most cases. This is OK for data that's +critical, but in some use cases it's preferred that the migration +should succeed even with the data missing. To support this the +subsection can be connected to a device property and from there +to a versioned machine type. One important note is that the post_load() function is called "after" loading all subsections, because a newer subsection could change same -value that it uses. +value that it uses. A flag, and the combination of pre_load and post_load +can be used to detect whether a subsection was loaded, and to +fall back on default behaviour when the subsection isn't present. Example: @@ -288,9 +299,13 @@ save/send this state when we are in the middle of a pio operation not enabled, the values on that fields are garbage and don't need to be sent. +Connecting subsections to properties +------------------------------------ + Using a condition function that checks a 'property' to determine whether -to send a subsection allows backwards migration compatibility when -new subsections are added. +to send a subsection allows backward migration compatibility when +new subsections are added, especially when combined with versioned +machine types. For example: @@ -305,21 +320,7 @@ For example: Now that subsection will not be generated when using an older machine type and the migration stream will be accepted by older -QEMU versions. pre-load functions can be used to initialise state -on the newer version so that they default to suitable values -when loading streams created by older QEMU versions that do not -generate the subsection. - -In some cases subsections are added for data that had been accidentally -omitted by earlier versions; if the missing data causes the migration -process to succeed but the guest to behave badly then it may be better -to send the subsection and cause the migration to explicitly fail -with the unknown subsection error. If the bad behaviour only happens -with certain data values, making the subsection conditional on -the data value (rather than the machine type) allows migrations to succeed -in most cases. In general the preference is to tie the subsection to -the machine type, and allow reliable migrations, unless the behaviour -from omission of the subsection is really bad. +QEMU versions. Not sending existing elements ----------------------------- @@ -328,9 +329,13 @@ Sometimes members of the VMState are no longer needed: - removing them will break migration compatibility - - making them version dependent and bumping the version will break backwards migration compatibility. + - making them version dependent and bumping the version will break backward migration + compatibility. -The best way is to: +Adding a dummy field into the migration stream is normally the best way to preserve +compatibility. + +If the field really does need to be removed then: a) Add a new property/compatibility/function in the same way for subsections above. b) replace the VMSTATE macro with the _TEST version of the macro, e.g.: @@ -342,18 +347,208 @@ The best way is to: ``VMSTATE_UINT32_TEST(foo, barstruct, pre_version_baz)`` Sometime in the future when we no longer care about the ancient versions these can be killed off. + Note that for backward compatibility it's important to fill in the structure with + data that the destination will understand. + +Any difference in the predicates on the source and destination will end up +with different fields being enabled and data being loaded into the wrong +fields; for this reason conditional fields like this are very fragile. + +Versions +-------- + +Version numbers are intended for major incompatible changes to the +migration of a device, and using them breaks backward-migration +compatibility; in general most changes can be made by adding Subsections +(see above) or _TEST macros (see above) which won't break compatibility. + +Each version is associated with a series of fields saved. The `save_state` always saves +the state as the newer version. But `load_state` sometimes is able to +load state from an older version. + +You can see that there are several version fields: + +- `version_id`: the maximum version_id supported by VMState for that device. +- `minimum_version_id`: the minimum version_id that VMState is able to understand + for that device. +- `minimum_version_id_old`: For devices that were not able to port to vmstate, we can + assign a function that knows how to read this old state. This field is + ignored if there is no `load_state_old` handler. + +VMState is able to read versions from minimum_version_id to +version_id. And the function ``load_state_old()`` (if present) is able to +load state from minimum_version_id_old to minimum_version_id. This +function is deprecated and will be removed when no more users are left. + +There are *_V* forms of many ``VMSTATE_`` macros to load fields for version dependent fields, +e.g. + +.. code:: c + + VMSTATE_UINT16_V(ip_id, Slirp, 2), + +only loads that field for versions 2 and newer. + +Saving state will always create a section with the 'version_id' value +and thus can't be loaded by any older QEMU. + +Massaging functions +------------------- + +Sometimes, it is not enough to be able to save the state directly +from one structure, we need to fill the correct values there. One +example is when we are using kvm. Before saving the cpu state, we +need to ask kvm to copy to QEMU the state that it is using. And the +opposite when we are loading the state, we need a way to tell kvm to +load the state for the cpu that we have just loaded from the QEMUFile. + +The functions to do that are inside a vmstate definition, and are called: + +- ``int (*pre_load)(void *opaque);`` + + This function is called before we load the state of one device. + +- ``int (*post_load)(void *opaque, int version_id);`` + + This function is called after we load the state of one device. + +- ``int (*pre_save)(void *opaque);`` + + This function is called before we save the state of one device. + +Example: You can look at hpet.c, that uses the three function to +massage the state that is transferred. + +The ``VMSTATE_WITH_TMP`` macro may be useful when the migration +data doesn't match the stored device data well; it allows an +intermediate temporary structure to be populated with migration +data and then transferred to the main structure. + +If you use memory API functions that update memory layout outside +initialization (i.e., in response to a guest action), this is a strong +indication that you need to call these functions in a `post_load` callback. +Examples of such memory API functions are: + + - memory_region_add_subregion() + - memory_region_del_subregion() + - memory_region_set_readonly() + - memory_region_set_enabled() + - memory_region_set_address() + - memory_region_set_alias_offset() + +Iterative device migration +-------------------------- + +Some devices, such as RAM, Block storage or certain platform devices, +have large amounts of data that would mean that the CPUs would be +paused for too long if they were sent in one section. For these +devices an *iterative* approach is taken. + +The iterative devices generally don't use VMState macros +(although it may be possible in some cases) and instead use +qemu_put_*/qemu_get_* macros to read/write data to the stream. Specialist +versions exist for high bandwidth IO. + + +An iterative device must provide: + + - A ``save_setup`` function that initialises the data structures and + transmits a first section containing information on the device. In the + case of RAM this transmits a list of RAMBlocks and sizes. + + - A ``load_setup`` function that initialises the data structures on the + destination. + + - A ``save_live_pending`` function that is called repeatedly and must + indicate how much more data the iterative data must save. The core + migration code will use this to determine when to pause the CPUs + and complete the migration. + + - A ``save_live_iterate`` function (called after ``save_live_pending`` + when there is significant data still to be sent). It should send + a chunk of data until the point that stream bandwidth limits tell it + to stop. Each call generates one section. + + - A ``save_live_complete_precopy`` function that must transmit the + last section for the device containing any remaining data. + + - A ``load_state`` function used to load sections generated by + any of the save functions that generate sections. + + - ``cleanup`` functions for both save and load that are called + at the end of migration. + +Note that the contents of the sections for iterative migration tend +to be open-coded by the devices; care should be taken in parsing +the results and structuring the stream to make them easy to validate. + +Device ordering +--------------- + +There are cases in which the ordering of device loading matters; for +example in some systems where a device may assert an interrupt during loading, +if the interrupt controller is loaded later then it might lose the state. + +Some ordering is implicitly provided by the order in which the machine +definition creates devices, however this is somewhat fragile. + +The ``MigrationPriority`` enum provides a means of explicitly enforcing +ordering. Numerically higher priorities are loaded earlier. +The priority is set by setting the ``priority`` field of the top level +``VMStateDescription`` for the device. + +Stream structure +================ + +The stream tries to be word and endian agnostic, allowing migration between hosts +of different characteristics running the same VM. + + - Header + + - Magic + - Version + - VM configuration section + + - Machine type + - Target page bits + - List of sections + Each section contains a device, or one iteration of a device save. + + - section type + - section id + - ID string (First section of each device) + - instance id (First section of each device) + - version id (First section of each device) + - + - Footer mark + - EOF mark + - VM Description structure + Consisting of a JSON description of the contents for analysis only + +The ``device data`` in each section consists of the data produced +by the code described above. For non-iterative devices they have a single +section; iterative devices have an initial and last section and a set +of parts in between. +Note that there is very little checking by the common code of the integrity +of the ``device data`` contents, that's up to the devices themselves. +The ``footer mark`` provides a little bit of protection for the case where +the receiving side reads more or less data than expected. + +The ``ID string`` is normally unique, having been formed from a bus name +and device address, PCI devices and storage devices hung off PCI controllers +fit this pattern well. Some devices are fixed single instances (e.g. "pc-ram"). +Others (especially either older devices or system devices which for +some reason don't have a bus concept) make use of the ``instance id`` +for otherwise identically named devices. Return path ----------- -In most migration scenarios there is only a single data path that runs -from the source VM to the destination, typically along a single fd (although -possibly with another fd or similar for some fast way of throwing pages across). +Only a unidirectional stream is required for normal migration, however a +``return path`` can be created when bidirectional communication is desired. +This is primarily used by postcopy, but is also used to return a success +flag to the source at the end of migration. -However, some uses need two way communication; in particular the Postcopy -destination needs to be able to request pages on demand from the source. - -For these scenarios there is a 'return path' from the destination to the source; ``qemu_file_get_return_path(QEMUFile* fwdpath)`` gives the QEMUFile* for the return path. @@ -632,3 +827,28 @@ Retro-fitting postcopy to existing clients is possible: identified and the implication understood; for example if the guest memory access is made while holding a lock then all other threads waiting for that lock will also be blocked. + +Firmware +======== + +Migration migrates the copies of RAM and ROM, and thus when running +on the destination it includes the firmware from the source. Even after +resetting a VM, the old firmware is used. Only once QEMU has been restarted +is the new firmware in use. + +- Changes in firmware size can cause changes in the required RAMBlock size + to hold the firmware and thus migration can fail. In practice it's best + to pad firmware images to convenient powers of 2 with plenty of space + for growth. + +- Care should be taken with device emulation code so that newer + emulation code can work with older firmware to allow forward migration. + +- Care should be taken with newer firmware so that backward migration + to older systems with older device emulation code will work. + +In some cases it may be best to tie specific firmware versions to specific +versioned machine types to cut down on the combinations that will need +support. This is also useful when newer versions of firmware outgrow +the padding. + From 71cd73061c014d04bc6b54936e675347ebc8d964 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Sun, 6 May 2018 22:54:58 +0800 Subject: [PATCH 0539/2380] migration: update index field when delete or qsort RDMALocalBlock rdma_delete_block function deletes RDMALocalBlock base on index field, but not update the index field. So when next time invoke rdma_delete_block, it will not work correctly. If start and cancel migration repeatedly, some RDMALocalBlock not invoke ibv_dereg_mr to decrease kernel mm_struct vmpin. When vmpin is large than max locked memory limitation, ibv_reg_mr will failed, and migration can not start successfully again. Signed-off-by: Lidong Chen Reviewed-by: Dr. David Alan Gilbert Message-Id: <1525618499-1560-1-git-send-email-lidongchen@tencent.com> Signed-off-by: Juan Quintela Signed-off-by: Lidong Chen --- migration/rdma.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/migration/rdma.c b/migration/rdma.c index da474fc19f..7d233b0820 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -708,6 +708,9 @@ static int rdma_delete_block(RDMAContext *rdma, RDMALocalBlock *block) memcpy(local->block + block->index, old + (block->index + 1), sizeof(RDMALocalBlock) * (local->nb_blocks - (block->index + 1))); + for (x = block->index; x < local->nb_blocks - 1; x++) { + local->block[x].index--; + } } } else { assert(block == local->block); @@ -3246,6 +3249,10 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) qsort(rdma->local_ram_blocks.block, rdma->local_ram_blocks.nb_blocks, sizeof(RDMALocalBlock), dest_ram_sort_func); + for (i = 0; i < local->nb_blocks; i++) { + local->block[i].index = i; + } + if (rdma->pin_all) { ret = qemu_rdma_reg_whole_ram_blocks(rdma); if (ret) { From 5e50cae409a0f43332efe594a67827cb4ee66c28 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 27 Apr 2018 12:15:02 +0100 Subject: [PATCH 0540/2380] migration: Textual fixups for blocktime Blank lines and comments as suggested by Eric. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Reviewed-by: Eric Blake Message-Id: <20180427111502.9822-1-dgilbert@redhat.com> Signed-off-by: Juan Quintela --- qapi/migration.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qapi/migration.json b/qapi/migration.json index 23ba85ed3a..3ec418dabf 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -162,11 +162,13 @@ # error strings. (Since 2.7) # # @postcopy-blocktime: total time when all vCPU were blocked during postcopy -# live migration (Since 2.13) +# live migration. This is only present when the postcopy-blocktime +# migration capability is enabled. (Since 2.13) # -# @postcopy-vcpu-blocktime: list of the postcopy blocktime per vCPU (Since 2.13) +# @postcopy-vcpu-blocktime: list of the postcopy blocktime per vCPU. This is +# only present when the postcopy-blocktime migration capability +# is enabled. (Since 2.13) # - # # Since: 0.14.0 ## @@ -368,7 +370,6 @@ # # @x-multifd: Use more than one fd for migration (since 2.11) # -# # @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps. # (since 2.12) # From 8b7bf2badac25c0a52aff1b181ad75fdb304dd0c Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Mon, 30 Apr 2018 19:59:43 +0100 Subject: [PATCH 0541/2380] Migration+TLS: Fix crash due to double cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During a TLS connect we see: migration_channel_connect calls migration_tls_channel_connect (calls after TLS setup) migration_channel_connect My previous error handling fix made migration_channel_connect call migrate_fd_connect in all cases; unfortunately the above means it gets called twice and crashes doing double cleanup. Fixes: 688a3dcba98 Reported-by: Peter Krempa Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Daniel P. Berrangé Message-Id: <20180430185943.35714-1-dgilbert@redhat.com> Signed-off-by: Juan Quintela --- migration/channel.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/migration/channel.c b/migration/channel.c index 716192bf75..33e0e9b82f 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -71,6 +71,15 @@ void migration_channel_connect(MigrationState *s, !object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_TLS)) { migration_tls_channel_connect(s, ioc, hostname, &error); + + if (!error) { + /* tls_channel_connect will call back to this + * function after the TLS handshake, + * so we mustn't call migrate_fd_connect until then + */ + + return; + } } else { QEMUFile *f = qemu_fopen_channel_output(ioc); From ddf2d98a94c8a98a661a217fb629cfd15f4dcec7 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 8 May 2018 14:54:35 +0100 Subject: [PATCH 0542/2380] qemu-iotests: reduce chance of races in 185 Commit 8565c3ab537e78f3e69977ec2c609dc9417a806e ("qemu-iotests: fix 185") identified a race condition in a sub-test. Similar issues also affect the other sub-tests. If disk I/O completes quickly, it races with the QMP 'quit' command. This causes spurious test failures because QMP events are emitted in an unpredictable order. This test relies on QEMU internals and there is no QMP API for getting deterministic behavior needed to make this test 100% reliable. At the same time, the test is useful and it would be a shame to remove it. Add sleep 0.5 to reduce the chance of races. This is not a real fix but appears to reduce spurious failures in practice. Cc: Vladimir Sementsov-Ogievskiy Signed-off-by: Stefan Hajnoczi Reviewed-by: Eric Blake Message-id: 20180508135436.30140-2-stefanha@redhat.com Reviewed-by: Jeff Cody Signed-off-by: Jeff Cody --- tests/qemu-iotests/185 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index 298d88d04e..975404c99d 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -118,6 +118,9 @@ _send_qemu_cmd $h \ 'speed': 65536 } }" \ "return" +# If we don't sleep here 'quit' command races with disk I/O +sleep 0.5 + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" wait=1 _cleanup_qemu @@ -137,6 +140,9 @@ _send_qemu_cmd $h \ 'speed': 65536 } }" \ "return" +# If we don't sleep here 'quit' command races with disk I/O +sleep 0.5 + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" wait=1 _cleanup_qemu @@ -183,6 +189,9 @@ _send_qemu_cmd $h \ 'speed': 65536 } }" \ "return" +# If we don't sleep here 'quit' command races with disk I/O +sleep 0.5 + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" wait=1 _cleanup_qemu @@ -201,6 +210,9 @@ _send_qemu_cmd $h \ 'speed': 65536 } }" \ "return" +# If we don't sleep here 'quit' command races with disk I/O +sleep 0.5 + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" wait=1 _cleanup_qemu From 4c7e813ce964e230bb55cf4afc862ccb091ca3a3 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 8 May 2018 14:54:36 +0100 Subject: [PATCH 0543/2380] blockjob: do not cancel timer in resume Currently the timer is cancelled and the block job is entered by block_job_resume(). This behavior causes drain to run extra blockjob iterations when the job was sleeping due to the ratelimit. This patch leaves the job asleep when block_job_resume() is called. Jobs can still be forcibly woken up using block_job_enter(), which is used to cancel jobs. After this patch drain no longer runs extra blockjob iterations. This is the expected behavior that qemu-iotests 185 used to rely on. We temporarily changed the 185 test output to make it pass for the QEMU 2.12 release but now it's time to address this issue. Cc: QingFeng Hao Signed-off-by: Stefan Hajnoczi Reviewed-by: Eric Blake Reviewed-by: QingFeng Hao Message-id: 20180508135436.30140-3-stefanha@redhat.com Reviewed-by: Jeff Cody Signed-off-by: Jeff Cody --- blockjob.c | 22 +++++++++++++++------- tests/qemu-iotests/185 | 5 +---- tests/qemu-iotests/185.out | 12 +++++------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/blockjob.c b/blockjob.c index 36c5fdeb2f..112672a68b 100644 --- a/blockjob.c +++ b/blockjob.c @@ -209,6 +209,18 @@ static void block_job_txn_del_job(BlockJob *job) } } +/* Assumes the block_job_mutex is held */ +static bool block_job_timer_pending(BlockJob *job) +{ + return timer_pending(&job->sleep_timer); +} + +/* Assumes the block_job_mutex is held */ +static bool block_job_timer_not_pending(BlockJob *job) +{ + return !block_job_timer_pending(job); +} + static void block_job_pause(BlockJob *job) { job->pause_count++; @@ -221,7 +233,9 @@ static void block_job_resume(BlockJob *job) if (job->pause_count) { return; } - block_job_enter(job); + + /* kick only if no timer is pending */ + block_job_enter_cond(job, block_job_timer_not_pending); } void block_job_ref(BlockJob *job) @@ -656,12 +670,6 @@ static void block_job_completed_txn_success(BlockJob *job) } } -/* Assumes the block_job_mutex is held */ -static bool block_job_timer_pending(BlockJob *job) -{ - return timer_pending(&job->sleep_timer); -} - void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { int64_t old_speed = job->speed; diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index 975404c99d..9a2d317414 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -101,14 +101,11 @@ echo # command to be received (after receiving the command, the rest runs # synchronously, so jobs can arbitrarily continue or complete). # -# Jobs present while QEMU is terminating iterate once more due to -# bdrv_drain_all(). -# # The buffer size for commit and streaming is 512k (waiting for 8 seconds after # the first request), for active commit and mirror it's large enough to cover # the full 4M, and for backup it's the qcow2 cluster size, which we know is # 64k. As all of these are at least as large as the speed, we are sure that the -# offset advances exactly twice before qemu exits. +# offset advances exactly once before qemu exits. _send_qemu_cmd $h \ "{ 'execute': 'block-commit', diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 992162f418..57eaf8d699 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -20,7 +20,7 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 1048576, "speed": 65536, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "commit"}} === Start active commit job and exit qemu === @@ -28,8 +28,7 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} === Start mirror job and exit qemu === @@ -38,8 +37,7 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} === Start backup job and exit qemu === @@ -48,7 +46,7 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 131072, "speed": 65536, "type": "backup"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 65536, "speed": 65536, "type": "backup"}} === Start streaming job and exit qemu === @@ -56,6 +54,6 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 1048576, "speed": 65536, "type": "stream"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}} No errors were found on the image. *** done From 54b7af4369a37afbd82573d0dcfb27febdb6dd24 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 16 May 2018 18:10:34 +0200 Subject: [PATCH 0544/2380] nfs: Fix error path in nfs_options_qdict_to_qapi() Don't throw away local_err, but propagate it to errp. Signed-off-by: Kevin Wolf Message-id: 20180516161034.27440-1-kwolf@redhat.com Reviewed-by: Eric Blake Reviewed-by: Jeff Cody Signed-off-by: Jeff Cody --- block/nfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/nfs.c b/block/nfs.c index 66fddf12d4..4ee2ad59d9 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -570,6 +570,7 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, qobject_unref(crumpled); if (local_err) { + error_propagate(errp, local_err); return NULL; } From c82be42cc803b36fd7aed5dceec68312c7056fd5 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 16 May 2018 18:08:16 +0200 Subject: [PATCH 0545/2380] nfs: Remove processed options from QDict Commit c22a03454 QAPIfied option parsing in the NFS block driver, but forgot to remove all the options we processed. Therefore, we get an error in bdrv_open_inherit(), which thinks the remaining options are invalid. Trying to open an NFS image will result in an error like this: Block protocol 'nfs' doesn't support the option 'server.host' Remove all options from the QDict to make the NFS driver work again. Cc: qemu-stable@nongnu.org Signed-off-by: Kevin Wolf Message-id: 20180516160816.26259-1-kwolf@redhat.com Reviewed-by: Eric Blake Reviewed-by: Jeff Cody Signed-off-by: Jeff Cody --- block/nfs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/block/nfs.c b/block/nfs.c index 4ee2ad59d9..3349b67a76 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -557,6 +557,7 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, BlockdevOptionsNfs *opts = NULL; QObject *crumpled = NULL; Visitor *v; + const QDictEntry *e; Error *local_err = NULL; crumpled = qdict_crumple(options, errp); @@ -574,6 +575,12 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, return NULL; } + /* Remove the processed options from the QDict (the visitor processes + * _all_ options in the QDict) */ + while ((e = qdict_first(options))) { + qdict_del(options, e->key); + } + return opts; } From 6603d50648901e8b9e6d66ec1142accf0b1df1e6 Mon Sep 17 00:00:00 2001 From: Petr Tesarik Date: Fri, 11 May 2018 09:10:52 +0200 Subject: [PATCH 0546/2380] fpu/softfloat: Fix conversion from uint64 to float128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The significand is passed to normalizeRoundAndPackFloat128() as high first, low second. The current code passes the integer first, so the result is incorrectly shifted left by 64 bits. This bug affects the emulation of s390x instruction CXLGBR (convert from logical 64-bit binary-integer operand to extended BFP result). Cc: qemu-stable@nongnu.org Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Petr Tesarik Message-Id: <20180511071052.1443-1-ptesarik@suse.com> Signed-off-by: Richard Henderson --- fpu/softfloat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index bc0f52fa54..d07419324a 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -3147,7 +3147,7 @@ float128 uint64_to_float128(uint64_t a, float_status *status) if (a == 0) { return float128_zero; } - return normalizeRoundAndPackFloat128(0, 0x406E, a, 0, status); + return normalizeRoundAndPackFloat128(0, 0x406E, 0, a, status); } From bca52234d1c04e0665f67708bcdef6d805d60adb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 11:24:13 -0700 Subject: [PATCH 0547/2380] fpu/softfloat: Merge NO_SIGNALING_NANS definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the ifdef inside the relevant functions instead of duplicating the function declarations. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 100 +++++++++++++++---------------------- 1 file changed, 40 insertions(+), 60 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 27834af0de..58b05718c8 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -233,17 +233,6 @@ typedef struct { uint64_t high, low; } commonNaNT; -#ifdef NO_SIGNALING_NANS -int float16_is_quiet_nan(float16 a_, float_status *status) -{ - return float16_is_any_nan(a_); -} - -int float16_is_signaling_nan(float16 a_, float_status *status) -{ - return 0; -} -#else /*---------------------------------------------------------------------------- | Returns 1 if the half-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -251,12 +240,16 @@ int float16_is_signaling_nan(float16 a_, float_status *status) int float16_is_quiet_nan(float16 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return float16_is_any_nan(a_); +#else uint16_t a = float16_val(a_); if (status->snan_bit_is_one) { return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); } else { return ((a & ~0x8000) >= 0x7C80); } +#endif } /*---------------------------------------------------------------------------- @@ -266,14 +259,17 @@ int float16_is_quiet_nan(float16 a_, float_status *status) int float16_is_signaling_nan(float16 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return 0; +#else uint16_t a = float16_val(a_); if (status->snan_bit_is_one) { return ((a & ~0x8000) >= 0x7C80); } else { return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); } -} #endif +} /*---------------------------------------------------------------------------- | Returns a quiet NaN if the half-precision floating point value `a' is a @@ -333,17 +329,6 @@ static float16 commonNaNToFloat16(commonNaNT a, float_status *status) } } -#ifdef NO_SIGNALING_NANS -int float32_is_quiet_nan(float32 a_, float_status *status) -{ - return float32_is_any_nan(a_); -} - -int float32_is_signaling_nan(float32 a_, float_status *status) -{ - return 0; -} -#else /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -351,12 +336,16 @@ int float32_is_signaling_nan(float32 a_, float_status *status) int float32_is_quiet_nan(float32 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return float32_is_any_nan(a_); +#else uint32_t a = float32_val(a_); if (status->snan_bit_is_one) { return (((a >> 22) & 0x1FF) == 0x1FE) && (a & 0x003FFFFF); } else { return ((uint32_t)(a << 1) >= 0xFF800000); } +#endif } /*---------------------------------------------------------------------------- @@ -366,14 +355,17 @@ int float32_is_quiet_nan(float32 a_, float_status *status) int float32_is_signaling_nan(float32 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return 0; +#else uint32_t a = float32_val(a_); if (status->snan_bit_is_one) { return ((uint32_t)(a << 1) >= 0xFF800000); } else { return (((a >> 22) & 0x1FF) == 0x1FE) && (a & 0x003FFFFF); } -} #endif +} /*---------------------------------------------------------------------------- | Returns a quiet NaN if the single-precision floating point value `a' is a @@ -744,17 +736,6 @@ static float32 propagateFloat32NaN(float32 a, float32 b, float_status *status) } } -#ifdef NO_SIGNALING_NANS -int float64_is_quiet_nan(float64 a_, float_status *status) -{ - return float64_is_any_nan(a_); -} - -int float64_is_signaling_nan(float64 a_, float_status *status) -{ - return 0; -} -#else /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -762,6 +743,9 @@ int float64_is_signaling_nan(float64 a_, float_status *status) int float64_is_quiet_nan(float64 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return float64_is_any_nan(a_); +#else uint64_t a = float64_val(a_); if (status->snan_bit_is_one) { return (((a >> 51) & 0xFFF) == 0xFFE) @@ -769,6 +753,7 @@ int float64_is_quiet_nan(float64 a_, float_status *status) } else { return ((a << 1) >= 0xFFF0000000000000ULL); } +#endif } /*---------------------------------------------------------------------------- @@ -778,6 +763,9 @@ int float64_is_quiet_nan(float64 a_, float_status *status) int float64_is_signaling_nan(float64 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return 0; +#else uint64_t a = float64_val(a_); if (status->snan_bit_is_one) { return ((a << 1) >= 0xFFF0000000000000ULL); @@ -785,8 +773,8 @@ int float64_is_signaling_nan(float64 a_, float_status *status) return (((a >> 51) & 0xFFF) == 0xFFE) && (a & LIT64(0x0007FFFFFFFFFFFF)); } -} #endif +} /*---------------------------------------------------------------------------- | Returns a quiet NaN if the double-precision floating point value `a' is a @@ -899,17 +887,6 @@ static float64 propagateFloat64NaN(float64 a, float64 b, float_status *status) } } -#ifdef NO_SIGNALING_NANS -int floatx80_is_quiet_nan(floatx80 a_, float_status *status) -{ - return floatx80_is_any_nan(a_); -} - -int floatx80_is_signaling_nan(floatx80 a_, float_status *status) -{ - return 0; -} -#else /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a | quiet NaN; otherwise returns 0. This slightly differs from the same @@ -918,6 +895,9 @@ int floatx80_is_signaling_nan(floatx80 a_, float_status *status) int floatx80_is_quiet_nan(floatx80 a, float_status *status) { +#ifdef NO_SIGNALING_NANS + return floatx80_is_any_nan(a); +#else if (status->snan_bit_is_one) { uint64_t aLow; @@ -929,6 +909,7 @@ int floatx80_is_quiet_nan(floatx80 a, float_status *status) return ((a.high & 0x7FFF) == 0x7FFF) && (LIT64(0x8000000000000000) <= ((uint64_t)(a.low << 1))); } +#endif } /*---------------------------------------------------------------------------- @@ -939,6 +920,9 @@ int floatx80_is_quiet_nan(floatx80 a, float_status *status) int floatx80_is_signaling_nan(floatx80 a, float_status *status) { +#ifdef NO_SIGNALING_NANS + return 0; +#else if (status->snan_bit_is_one) { return ((a.high & 0x7FFF) == 0x7FFF) && ((a.low << 1) >= 0x8000000000000000ULL); @@ -950,8 +934,8 @@ int floatx80_is_signaling_nan(floatx80 a, float_status *status) && (uint64_t)(aLow << 1) && (a.low == aLow); } -} #endif +} /*---------------------------------------------------------------------------- | Returns a quiet NaN if the extended double-precision floating point value @@ -1060,17 +1044,6 @@ floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) } } -#ifdef NO_SIGNALING_NANS -int float128_is_quiet_nan(float128 a_, float_status *status) -{ - return float128_is_any_nan(a_); -} - -int float128_is_signaling_nan(float128 a_, float_status *status) -{ - return 0; -} -#else /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -1078,6 +1051,9 @@ int float128_is_signaling_nan(float128 a_, float_status *status) int float128_is_quiet_nan(float128 a, float_status *status) { +#ifdef NO_SIGNALING_NANS + return float128_is_any_nan(a); +#else if (status->snan_bit_is_one) { return (((a.high >> 47) & 0xFFFF) == 0xFFFE) && (a.low || (a.high & 0x00007FFFFFFFFFFFULL)); @@ -1085,6 +1061,7 @@ int float128_is_quiet_nan(float128 a, float_status *status) return ((a.high << 1) >= 0xFFFF000000000000ULL) && (a.low || (a.high & 0x0000FFFFFFFFFFFFULL)); } +#endif } /*---------------------------------------------------------------------------- @@ -1094,6 +1071,9 @@ int float128_is_quiet_nan(float128 a, float_status *status) int float128_is_signaling_nan(float128 a, float_status *status) { +#ifdef NO_SIGNALING_NANS + return 0; +#else if (status->snan_bit_is_one) { return ((a.high << 1) >= 0xFFFF000000000000ULL) && (a.low || (a.high & 0x0000FFFFFFFFFFFFULL)); @@ -1101,8 +1081,8 @@ int float128_is_signaling_nan(float128 a, float_status *status) return (((a.high >> 47) & 0xFFFF) == 0xFFFE) && (a.low || (a.high & LIT64(0x00007FFFFFFFFFFF))); } -} #endif +} /*---------------------------------------------------------------------------- | Returns a quiet NaN if the quadruple-precision floating point value `a' is From d619bb98fdcda24f9ee3b7a53a4d555228dbca52 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 11:39:48 -0700 Subject: [PATCH 0548/2380] fpu/softfloat: Split floatXX_silence_nan from floatXX_maybe_silence_nan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new function assumes that the input is an SNaN and does not double-check. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 174 +++++++++++++++++++++++++------------ include/fpu/softfloat.h | 5 ++ 2 files changed, 123 insertions(+), 56 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 58b05718c8..4fc9ea4ac0 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -271,22 +271,35 @@ int float16_is_signaling_nan(float16 a_, float_status *status) #endif } +/*---------------------------------------------------------------------------- +| Returns a quiet NaN from a signalling NaN for the half-precision +| floating point value `a'. +*----------------------------------------------------------------------------*/ + +float16 float16_silence_nan(float16 a, float_status *status) +{ +#ifdef NO_SIGNALING_NANS + g_assert_not_reached(); +#else + if (status->snan_bit_is_one) { + return float16_default_nan(status); + } else { + return a | (1 << 9); + } +#endif +} + /*---------------------------------------------------------------------------- | Returns a quiet NaN if the half-precision floating point value `a' is a | signaling NaN; otherwise returns `a'. *----------------------------------------------------------------------------*/ -float16 float16_maybe_silence_nan(float16 a_, float_status *status) + +float16 float16_maybe_silence_nan(float16 a, float_status *status) { - if (float16_is_signaling_nan(a_, status)) { - if (status->snan_bit_is_one) { - return float16_default_nan(status); - } else { - uint16_t a = float16_val(a_); - a |= (1 << 9); - return make_float16(a); - } + if (float16_is_signaling_nan(a, status)) { + return float16_silence_nan(a, status); } - return a_; + return a; } /*---------------------------------------------------------------------------- @@ -367,30 +380,40 @@ int float32_is_signaling_nan(float32 a_, float_status *status) #endif } +/*---------------------------------------------------------------------------- +| Returns a quiet NaN from a signalling NaN for the single-precision +| floating point value `a'. +*----------------------------------------------------------------------------*/ + +float32 float32_silence_nan(float32 a, float_status *status) +{ +#ifdef NO_SIGNALING_NANS + g_assert_not_reached(); +#else + if (status->snan_bit_is_one) { +# ifdef TARGET_HPPA + a &= ~0x00400000; + a |= 0x00200000; + return a; +# else + return float32_default_nan(status); +# endif + } else { + return a | (1 << 22); + } +#endif +} /*---------------------------------------------------------------------------- | Returns a quiet NaN if the single-precision floating point value `a' is a | signaling NaN; otherwise returns `a'. *----------------------------------------------------------------------------*/ -float32 float32_maybe_silence_nan(float32 a_, float_status *status) +float32 float32_maybe_silence_nan(float32 a, float_status *status) { - if (float32_is_signaling_nan(a_, status)) { - if (status->snan_bit_is_one) { -#ifdef TARGET_HPPA - uint32_t a = float32_val(a_); - a &= ~0x00400000; - a |= 0x00200000; - return make_float32(a); -#else - return float32_default_nan(status); -#endif - } else { - uint32_t a = float32_val(a_); - a |= (1 << 22); - return make_float32(a); - } + if (float32_is_signaling_nan(a, status)) { + return float32_silence_nan(a, status); } - return a_; + return a; } /*---------------------------------------------------------------------------- @@ -776,30 +799,41 @@ int float64_is_signaling_nan(float64 a_, float_status *status) #endif } +/*---------------------------------------------------------------------------- +| Returns a quiet NaN from a signalling NaN for the double-precision +| floating point value `a'. +*----------------------------------------------------------------------------*/ + +float64 float64_silence_nan(float64 a, float_status *status) +{ +#ifdef NO_SIGNALING_NANS + g_assert_not_reached(); +#else + if (status->snan_bit_is_one) { +# ifdef TARGET_HPPA + a &= ~0x0008000000000000ULL; + a |= 0x0004000000000000ULL; + return a; +# else + return float64_default_nan(status); +# endif + } else { + return a | LIT64(0x0008000000000000); + } +#endif +} + /*---------------------------------------------------------------------------- | Returns a quiet NaN if the double-precision floating point value `a' is a | signaling NaN; otherwise returns `a'. *----------------------------------------------------------------------------*/ -float64 float64_maybe_silence_nan(float64 a_, float_status *status) +float64 float64_maybe_silence_nan(float64 a, float_status *status) { - if (float64_is_signaling_nan(a_, status)) { - if (status->snan_bit_is_one) { -#ifdef TARGET_HPPA - uint64_t a = float64_val(a_); - a &= ~0x0008000000000000ULL; - a |= 0x0004000000000000ULL; - return make_float64(a); -#else - return float64_default_nan(status); -#endif - } else { - uint64_t a = float64_val(a_); - a |= LIT64(0x0008000000000000); - return make_float64(a); - } + if (float64_is_signaling_nan(a, status)) { + return float64_silence_nan(a, status); } - return a_; + return a; } /*---------------------------------------------------------------------------- @@ -937,6 +971,25 @@ int floatx80_is_signaling_nan(floatx80 a, float_status *status) #endif } +/*---------------------------------------------------------------------------- +| Returns a quiet NaN from a signalling NaN for the extended double-precision +| floating point value `a'. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_silence_nan(floatx80 a, float_status *status) +{ +#ifdef NO_SIGNALING_NANS + g_assert_not_reached(); +#else + if (status->snan_bit_is_one) { + return floatx80_default_nan(status); + } else { + a.low |= LIT64(0xC000000000000000); + return a; + } +#endif +} + /*---------------------------------------------------------------------------- | Returns a quiet NaN if the extended double-precision floating point value | `a' is a signaling NaN; otherwise returns `a'. @@ -945,12 +998,7 @@ int floatx80_is_signaling_nan(floatx80 a, float_status *status) floatx80 floatx80_maybe_silence_nan(floatx80 a, float_status *status) { if (floatx80_is_signaling_nan(a, status)) { - if (status->snan_bit_is_one) { - a = floatx80_default_nan(status); - } else { - a.low |= LIT64(0xC000000000000000); - return a; - } + return floatx80_silence_nan(a, status); } return a; } @@ -1084,6 +1132,25 @@ int float128_is_signaling_nan(float128 a, float_status *status) #endif } +/*---------------------------------------------------------------------------- +| Returns a quiet NaN from a signalling NaN for the quadruple-precision +| floating point value `a'. +*----------------------------------------------------------------------------*/ + +float128 float128_silence_nan(float128 a, float_status *status) +{ +#ifdef NO_SIGNALING_NANS + g_assert_not_reached(); +#else + if (status->snan_bit_is_one) { + return float128_default_nan(status); + } else { + a.high |= LIT64(0x0000800000000000); + return a; + } +#endif +} + /*---------------------------------------------------------------------------- | Returns a quiet NaN if the quadruple-precision floating point value `a' is | a signaling NaN; otherwise returns `a'. @@ -1092,12 +1159,7 @@ int float128_is_signaling_nan(float128 a, float_status *status) float128 float128_maybe_silence_nan(float128 a, float_status *status) { if (float128_is_signaling_nan(a, status)) { - if (status->snan_bit_is_one) { - a = float128_default_nan(status); - } else { - a.high |= LIT64(0x0000800000000000); - return a; - } + return float128_silence_nan(a, status); } return a; } diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 36626a501b..43962dc3f5 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -257,6 +257,7 @@ int float16_compare_quiet(float16, float16, float_status *status); int float16_is_quiet_nan(float16, float_status *status); int float16_is_signaling_nan(float16, float_status *status); +float16 float16_silence_nan(float16, float_status *status); float16 float16_maybe_silence_nan(float16, float_status *status); static inline int float16_is_any_nan(float16 a) @@ -368,6 +369,7 @@ float32 float32_minnummag(float32, float32, float_status *status); float32 float32_maxnummag(float32, float32, float_status *status); int float32_is_quiet_nan(float32, float_status *status); int float32_is_signaling_nan(float32, float_status *status); +float32 float32_silence_nan(float32, float_status *status); float32 float32_maybe_silence_nan(float32, float_status *status); float32 float32_scalbn(float32, int, float_status *status); @@ -497,6 +499,7 @@ float64 float64_minnummag(float64, float64, float_status *status); float64 float64_maxnummag(float64, float64, float_status *status); int float64_is_quiet_nan(float64 a, float_status *status); int float64_is_signaling_nan(float64, float_status *status); +float64 float64_silence_nan(float64, float_status *status); float64 float64_maybe_silence_nan(float64, float_status *status); float64 float64_scalbn(float64, int, float_status *status); @@ -600,6 +603,7 @@ int floatx80_compare(floatx80, floatx80, float_status *status); int floatx80_compare_quiet(floatx80, floatx80, float_status *status); int floatx80_is_quiet_nan(floatx80, float_status *status); int floatx80_is_signaling_nan(floatx80, float_status *status); +floatx80 floatx80_silence_nan(floatx80, float_status *status); floatx80 floatx80_maybe_silence_nan(floatx80, float_status *status); floatx80 floatx80_scalbn(floatx80, int, float_status *status); @@ -811,6 +815,7 @@ int float128_compare(float128, float128, float_status *status); int float128_compare_quiet(float128, float128, float_status *status); int float128_is_quiet_nan(float128, float_status *status); int float128_is_signaling_nan(float128, float_status *status); +float128 float128_silence_nan(float128, float_status *status); float128 float128_maybe_silence_nan(float128, float_status *status); float128 float128_scalbn(float128, int, float_status *status); From 0664335a6eb65d684918dd3f1a7f0a3d5b92cbe1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 11:58:25 -0700 Subject: [PATCH 0549/2380] fpu/softfloat: Move softfloat-specialize.h below FloatParts definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to be able to specialize on the canonical representation. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index d07419324a..0d17027379 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -95,16 +95,6 @@ this code that are retained. *----------------------------------------------------------------------------*/ #include "fpu/softfloat-macros.h" -/*---------------------------------------------------------------------------- -| Functions and definitions to determine: (1) whether tininess for underflow -| is detected before or after rounding by default, (2) what (if anything) -| happens when exceptions are raised, (3) how signaling NaNs are distinguished -| from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs -| are propagated from function inputs to output. These details are target- -| specific. -*----------------------------------------------------------------------------*/ -#include "softfloat-specialize.h" - /*---------------------------------------------------------------------------- | Returns the fraction bits of the half-precision floating-point value `a'. *----------------------------------------------------------------------------*/ @@ -322,6 +312,16 @@ static inline float64 float64_pack_raw(FloatParts p) return make_float64(pack_raw(float64_params, p)); } +/*---------------------------------------------------------------------------- +| Functions and definitions to determine: (1) whether tininess for underflow +| is detected before or after rounding by default, (2) what (if anything) +| happens when exceptions are raised, (3) how signaling NaNs are distinguished +| from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs +| are propagated from function inputs to output. These details are target- +| specific. +*----------------------------------------------------------------------------*/ +#include "softfloat-specialize.h" + /* Canonicalize EXP and FRAC, setting CLS. */ static FloatParts canonicalize(FloatParts part, const FloatFmt *parm, float_status *status) From 94933df0e5c34d1a50fc950553f9c9649cae5320 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 12:14:21 -0700 Subject: [PATCH 0550/2380] fpu/softfloat: Canonicalize NaN fraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shift the NaN fraction to a canonical position, much like we do for the fraction of normal numbers. This will facilitate manipulation of NaNs within the shared code paths. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 0d17027379..607c4a78d5 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -330,10 +330,11 @@ static FloatParts canonicalize(FloatParts part, const FloatFmt *parm, if (part.frac == 0) { part.cls = float_class_inf; } else { + part.frac <<= parm->frac_shift; #ifdef NO_SIGNALING_NANS part.cls = float_class_qnan; #else - int64_t msb = part.frac << (parm->frac_shift + 2); + int64_t msb = part.frac << 2; if ((msb < 0) == status->snan_bit_is_one) { part.cls = float_class_snan; } else { @@ -480,6 +481,7 @@ static FloatParts round_canonical(FloatParts p, float_status *s, case float_class_qnan: case float_class_snan: exp = exp_max; + frac >>= parm->frac_shift; break; default: @@ -503,6 +505,7 @@ static float16 float16_round_pack_canonical(FloatParts p, float_status *s) case float_class_dnan: return float16_default_nan(s); case float_class_msnan: + p.frac >>= float16_params.frac_shift; return float16_maybe_silence_nan(float16_pack_raw(p), s); default: p = round_canonical(p, s, &float16_params); @@ -521,6 +524,7 @@ static float32 float32_round_pack_canonical(FloatParts p, float_status *s) case float_class_dnan: return float32_default_nan(s); case float_class_msnan: + p.frac >>= float32_params.frac_shift; return float32_maybe_silence_nan(float32_pack_raw(p), s); default: p = round_canonical(p, s, &float32_params); @@ -539,6 +543,7 @@ static float64 float64_round_pack_canonical(FloatParts p, float_status *s) case float_class_dnan: return float64_default_nan(s); case float_class_msnan: + p.frac >>= float64_params.frac_shift; return float64_maybe_silence_nan(float64_pack_raw(p), s); default: p = round_canonical(p, s, &float64_params); From 298b468e4389587ab2e8599dd33eff3fbc698011 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 12:45:29 -0700 Subject: [PATCH 0551/2380] fpu/softfloat: Introduce parts_is_snan_frac MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 15 +++++++++++++++ fpu/softfloat.c | 12 ++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 4fc9ea4ac0..515cb12cfa 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -86,6 +86,21 @@ this code that are retained. #define NO_SIGNALING_NANS 1 #endif +/*---------------------------------------------------------------------------- +| For the deconstructed floating-point with fraction FRAC, return true +| if the fraction represents a signalling NaN; otherwise false. +*----------------------------------------------------------------------------*/ + +static bool parts_is_snan_frac(uint64_t frac, float_status *status) +{ +#ifdef NO_SIGNALING_NANS + return false; +#else + flag msb = extract64(frac, DECOMPOSED_BINARY_POINT - 1, 1); + return msb == status->snan_bit_is_one; +#endif +} + /*---------------------------------------------------------------------------- | The pattern for a default generated half-precision NaN. *----------------------------------------------------------------------------*/ diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 607c4a78d5..19f40d6932 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -331,16 +331,8 @@ static FloatParts canonicalize(FloatParts part, const FloatFmt *parm, part.cls = float_class_inf; } else { part.frac <<= parm->frac_shift; -#ifdef NO_SIGNALING_NANS - part.cls = float_class_qnan; -#else - int64_t msb = part.frac << 2; - if ((msb < 0) == status->snan_bit_is_one) { - part.cls = float_class_snan; - } else { - part.cls = float_class_qnan; - } -#endif + part.cls = (parts_is_snan_frac(part.frac, status) + ? float_class_snan : float_class_qnan); } } else if (part.exp == 0) { if (likely(part.frac == 0)) { From f7e598e264b94d0982e647ac303108781d5eb4fa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 13:09:49 -0700 Subject: [PATCH 0552/2380] fpu/softfloat: Replace float_class_dnan with parts_default_nan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With a canonical representation of NaNs, we can return the default nan directly rather than delay the expansion until the final format is known. Note one case where we uselessly assigned to a.sign, which was overwritten/ignored later when expanding float_class_dnan. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 37 +++++++++++++++++++++++++++++++++++++ fpu/softfloat.c | 38 +++++++++++--------------------------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 515cb12cfa..0d3d81a52b 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -101,6 +101,43 @@ static bool parts_is_snan_frac(uint64_t frac, float_status *status) #endif } +/*---------------------------------------------------------------------------- +| The pattern for a default generated deconstructed floating-point NaN. +*----------------------------------------------------------------------------*/ + +static FloatParts parts_default_nan(float_status *status) +{ + bool sign = 0; + uint64_t frac; + +#if defined(TARGET_SPARC) || defined(TARGET_M68K) + frac = (1ULL << DECOMPOSED_BINARY_POINT) - 1; +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ + defined(TARGET_S390X) || defined(TARGET_RISCV) + frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); +#elif defined(TARGET_HPPA) + frac = 1ULL << (DECOMPOSED_BINARY_POINT - 2); +#else + if (status->snan_bit_is_one) { + frac = (1ULL << (DECOMPOSED_BINARY_POINT - 1)) - 1; + } else { +#if defined(TARGET_MIPS) + frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); +#else + frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); + sign = 1; +#endif + } +#endif + + return (FloatParts) { + .cls = float_class_qnan, + .sign = sign, + .exp = INT_MAX, + .frac = frac + }; +} + /*---------------------------------------------------------------------------- | The pattern for a default generated half-precision NaN. *----------------------------------------------------------------------------*/ diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 19f40d6932..51780b718f 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -188,7 +188,6 @@ typedef enum __attribute__ ((__packed__)) { float_class_inf, float_class_qnan, /* all NaNs from here */ float_class_snan, - float_class_dnan, float_class_msnan, /* maybe silenced */ } FloatClass; @@ -494,8 +493,6 @@ static FloatParts float16_unpack_canonical(float16 f, float_status *s) static float16 float16_round_pack_canonical(FloatParts p, float_status *s) { switch (p.cls) { - case float_class_dnan: - return float16_default_nan(s); case float_class_msnan: p.frac >>= float16_params.frac_shift; return float16_maybe_silence_nan(float16_pack_raw(p), s); @@ -513,8 +510,6 @@ static FloatParts float32_unpack_canonical(float32 f, float_status *s) static float32 float32_round_pack_canonical(FloatParts p, float_status *s) { switch (p.cls) { - case float_class_dnan: - return float32_default_nan(s); case float_class_msnan: p.frac >>= float32_params.frac_shift; return float32_maybe_silence_nan(float32_pack_raw(p), s); @@ -532,8 +527,6 @@ static FloatParts float64_unpack_canonical(float64 f, float_status *s) static float64 float64_round_pack_canonical(FloatParts p, float_status *s) { switch (p.cls) { - case float_class_dnan: - return float64_default_nan(s); case float_class_msnan: p.frac >>= float64_params.frac_shift; return float64_maybe_silence_nan(float64_pack_raw(p), s); @@ -566,7 +559,7 @@ static FloatParts return_nan(FloatParts a, float_status *s) /* fall through */ case float_class_qnan: if (s->default_nan_mode) { - a.cls = float_class_dnan; + return parts_default_nan(s); } break; @@ -583,7 +576,7 @@ static FloatParts pick_nan(FloatParts a, FloatParts b, float_status *s) } if (s->default_nan_mode) { - a.cls = float_class_dnan; + return parts_default_nan(s); } else { if (pickNaN(is_qnan(a.cls), is_snan(a.cls), is_qnan(b.cls), is_snan(b.cls), @@ -614,8 +607,7 @@ static FloatParts pick_nan_muladd(FloatParts a, FloatParts b, FloatParts c, /* Note that this check is after pickNaNMulAdd so that function * has an opportunity to set the Invalid flag. */ - a.cls = float_class_dnan; - return a; + which = 3; } switch (which) { @@ -628,8 +620,7 @@ static FloatParts pick_nan_muladd(FloatParts a, FloatParts b, FloatParts c, a = c; break; case 3: - a.cls = float_class_dnan; - return a; + return parts_default_nan(s); default: g_assert_not_reached(); } @@ -682,7 +673,7 @@ static FloatParts addsub_floats(FloatParts a, FloatParts b, bool subtract, if (a.cls == float_class_inf) { if (b.cls == float_class_inf) { float_raise(float_flag_invalid, s); - a.cls = float_class_dnan; + return parts_default_nan(s); } return a; } @@ -828,9 +819,7 @@ static FloatParts mul_floats(FloatParts a, FloatParts b, float_status *s) if ((a.cls == float_class_inf && b.cls == float_class_zero) || (a.cls == float_class_zero && b.cls == float_class_inf)) { s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_dnan; - a.sign = sign; - return a; + return parts_default_nan(s); } /* Multiply by 0 or Inf */ if (a.cls == float_class_inf || a.cls == float_class_zero) { @@ -908,8 +897,7 @@ static FloatParts muladd_floats(FloatParts a, FloatParts b, FloatParts c, if (inf_zero) { s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_dnan; - return a; + return parts_default_nan(s); } if (flags & float_muladd_negate_c) { @@ -933,12 +921,12 @@ static FloatParts muladd_floats(FloatParts a, FloatParts b, FloatParts c, if (c.cls == float_class_inf) { if (p_class == float_class_inf && p_sign != c.sign) { s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_dnan; + return parts_default_nan(s); } else { a.cls = float_class_inf; a.sign = c.sign ^ sign_flip; + return a; } - return a; } if (p_class == float_class_inf) { @@ -1148,8 +1136,7 @@ static FloatParts div_floats(FloatParts a, FloatParts b, float_status *s) && (a.cls == float_class_inf || a.cls == float_class_zero)) { s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_dnan; - return a; + return parts_default_nan(s); } /* Inf / x or 0 / x */ if (a.cls == float_class_inf || a.cls == float_class_zero) { @@ -1347,7 +1334,6 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode, switch (p.cls) { case float_class_snan: case float_class_qnan: - case float_class_dnan: case float_class_msnan: s->float_exception_flags = orig_flags | float_flag_invalid; return max; @@ -1439,7 +1425,6 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, switch (p.cls) { case float_class_snan: case float_class_qnan: - case float_class_dnan: case float_class_msnan: s->float_exception_flags = orig_flags | float_flag_invalid; return max; @@ -1940,8 +1925,7 @@ static FloatParts sqrt_float(FloatParts a, float_status *s, const FloatFmt *p) } if (a.sign) { s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_dnan; - return a; + return parts_default_nan(s); } if (a.cls == float_class_inf) { return a; /* sqrt(+inf) = +inf */ From 0bcfbcbea548656ff930394f296589728c2a0c5d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 13:32:53 -0700 Subject: [PATCH 0553/2380] fpu/softfloat: Replace float_class_msnan with parts_silence_nan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With a canonical representation of NaNs, we can silence an SNaN immediately rather than delay until the final format is known. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 23 ++++++++++++++++++++++ fpu/softfloat.c | 40 ++++++++++---------------------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 0d3d81a52b..571d1df378 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -138,6 +138,29 @@ static FloatParts parts_default_nan(float_status *status) }; } +/*---------------------------------------------------------------------------- +| Returns a quiet NaN from a signalling NaN for the deconstructed +| floating-point parts. +*----------------------------------------------------------------------------*/ + +static FloatParts parts_silence_nan(FloatParts a, float_status *status) +{ +#ifdef NO_SIGNALING_NANS + g_assert_not_reached(); +#elif defined(TARGET_HPPA) + a.frac &= ~(1ULL << (DECOMPOSED_BINARY_POINT - 1)); + a.frac |= 1ULL << (DECOMPOSED_BINARY_POINT - 2); +#else + if (status->snan_bit_is_one) { + return parts_default_nan(status); + } else { + a.frac |= 1ULL << (DECOMPOSED_BINARY_POINT - 1); + } +#endif + a.cls = float_class_qnan; + return a; +} + /*---------------------------------------------------------------------------- | The pattern for a default generated half-precision NaN. *----------------------------------------------------------------------------*/ diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 51780b718f..41253c6749 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -188,7 +188,6 @@ typedef enum __attribute__ ((__packed__)) { float_class_inf, float_class_qnan, /* all NaNs from here */ float_class_snan, - float_class_msnan, /* maybe silenced */ } FloatClass; /* @@ -492,14 +491,7 @@ static FloatParts float16_unpack_canonical(float16 f, float_status *s) static float16 float16_round_pack_canonical(FloatParts p, float_status *s) { - switch (p.cls) { - case float_class_msnan: - p.frac >>= float16_params.frac_shift; - return float16_maybe_silence_nan(float16_pack_raw(p), s); - default: - p = round_canonical(p, s, &float16_params); - return float16_pack_raw(p); - } + return float16_pack_raw(round_canonical(p, s, &float16_params)); } static FloatParts float32_unpack_canonical(float32 f, float_status *s) @@ -509,14 +501,7 @@ static FloatParts float32_unpack_canonical(float32 f, float_status *s) static float32 float32_round_pack_canonical(FloatParts p, float_status *s) { - switch (p.cls) { - case float_class_msnan: - p.frac >>= float32_params.frac_shift; - return float32_maybe_silence_nan(float32_pack_raw(p), s); - default: - p = round_canonical(p, s, &float32_params); - return float32_pack_raw(p); - } + return float32_pack_raw(round_canonical(p, s, &float32_params)); } static FloatParts float64_unpack_canonical(float64 f, float_status *s) @@ -526,14 +511,7 @@ static FloatParts float64_unpack_canonical(float64 f, float_status *s) static float64 float64_round_pack_canonical(FloatParts p, float_status *s) { - switch (p.cls) { - case float_class_msnan: - p.frac >>= float64_params.frac_shift; - return float64_maybe_silence_nan(float64_pack_raw(p), s); - default: - p = round_canonical(p, s, &float64_params); - return float64_pack_raw(p); - } + return float64_pack_raw(round_canonical(p, s, &float64_params)); } /* Simple helpers for checking if what NaN we have */ @@ -555,7 +533,7 @@ static FloatParts return_nan(FloatParts a, float_status *s) switch (a.cls) { case float_class_snan: s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_msnan; + a = parts_silence_nan(a, s); /* fall through */ case float_class_qnan: if (s->default_nan_mode) { @@ -584,7 +562,9 @@ static FloatParts pick_nan(FloatParts a, FloatParts b, float_status *s) (a.frac == b.frac && a.sign < b.sign))) { a = b; } - a.cls = float_class_msnan; + if (is_snan(a.cls)) { + return parts_silence_nan(a, s); + } } return a; } @@ -624,8 +604,10 @@ static FloatParts pick_nan_muladd(FloatParts a, FloatParts b, FloatParts c, default: g_assert_not_reached(); } - a.cls = float_class_msnan; + if (is_snan(a.cls)) { + return parts_silence_nan(a, s); + } return a; } @@ -1334,7 +1316,6 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode, switch (p.cls) { case float_class_snan: case float_class_qnan: - case float_class_msnan: s->float_exception_flags = orig_flags | float_flag_invalid; return max; case float_class_inf: @@ -1425,7 +1406,6 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, switch (p.cls) { case float_class_snan: case float_class_qnan: - case float_class_msnan: s->float_exception_flags = orig_flags | float_flag_invalid; return max; case float_class_inf: From 486624fcd3eaca6165ab8401d73bbae6c0fb81c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 7 May 2018 13:17:16 +0100 Subject: [PATCH 0554/2380] target/arm: convert conversion helpers to fpst/ahp_flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of passing env and leaving it up to the helper to get the right fpstatus we pass it explicitly. There was already a get_fpstatus helper for neon for the 32 bit code. We also add an get_ahp_flag() for passing the state of the alternative FP16 format flag. This leaves scope for later tracking the AHP state in translation flags. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Signed-off-by: Richard Henderson --- target/arm/helper.c | 56 +++++------------------------ target/arm/helper.h | 10 +++--- target/arm/translate-a64.c | 37 +++++++++++++++---- target/arm/translate.c | 74 +++++++++++++++++++++++++++++--------- target/arm/translate.h | 12 +++++++ 5 files changed, 112 insertions(+), 77 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index c6fd7f9479..1762042fc7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11540,64 +11540,24 @@ uint32_t HELPER(set_neon_rmode)(uint32_t rmode, CPUARMState *env) } /* Half precision conversions. */ -static float32 do_fcvt_f16_to_f32(uint32_t a, CPUARMState *env, float_status *s) +float32 HELPER(vfp_fcvt_f16_to_f32)(float16 a, void *fpstp, uint32_t ahp_mode) { - int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0; - float32 r = float16_to_float32(make_float16(a), ieee, s); - if (ieee) { - return float32_maybe_silence_nan(r, s); - } - return r; + return float16_to_float32(a, !ahp_mode, fpstp); } -static uint32_t do_fcvt_f32_to_f16(float32 a, CPUARMState *env, float_status *s) +float16 HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) { - int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0; - float16 r = float32_to_float16(a, ieee, s); - if (ieee) { - r = float16_maybe_silence_nan(r, s); - } - return float16_val(r); + return float32_to_float16(a, !ahp_mode, fpstp); } -float32 HELPER(neon_fcvt_f16_to_f32)(uint32_t a, CPUARMState *env) +float64 HELPER(vfp_fcvt_f16_to_f64)(float16 a, void *fpstp, uint32_t ahp_mode) { - return do_fcvt_f16_to_f32(a, env, &env->vfp.standard_fp_status); + return float16_to_float64(a, !ahp_mode, fpstp); } -uint32_t HELPER(neon_fcvt_f32_to_f16)(float32 a, CPUARMState *env) +float16 HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) { - return do_fcvt_f32_to_f16(a, env, &env->vfp.standard_fp_status); -} - -float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, CPUARMState *env) -{ - return do_fcvt_f16_to_f32(a, env, &env->vfp.fp_status); -} - -uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, CPUARMState *env) -{ - return do_fcvt_f32_to_f16(a, env, &env->vfp.fp_status); -} - -float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, CPUARMState *env) -{ - int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0; - float64 r = float16_to_float64(make_float16(a), ieee, &env->vfp.fp_status); - if (ieee) { - return float64_maybe_silence_nan(r, &env->vfp.fp_status); - } - return r; -} - -uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, CPUARMState *env) -{ - int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0; - float16 r = float64_to_float16(a, ieee, &env->vfp.fp_status); - if (ieee) { - r = float16_maybe_silence_nan(r, &env->vfp.fp_status); - } - return float16_val(r); + return float64_to_float16(a, !ahp_mode, fpstp); } #define float32_two make_float32(0x40000000) diff --git a/target/arm/helper.h b/target/arm/helper.h index ce89968b2d..047f3bc1ca 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -187,12 +187,10 @@ DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, ptr) DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, ptr) DEF_HELPER_FLAGS_2(set_neon_rmode, TCG_CALL_NO_RWG, i32, i32, env) -DEF_HELPER_2(vfp_fcvt_f16_to_f32, f32, i32, env) -DEF_HELPER_2(vfp_fcvt_f32_to_f16, i32, f32, env) -DEF_HELPER_2(neon_fcvt_f16_to_f32, f32, i32, env) -DEF_HELPER_2(neon_fcvt_f32_to_f16, i32, f32, env) -DEF_HELPER_FLAGS_2(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, i32, env) -DEF_HELPER_FLAGS_2(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, i32, f64, env) +DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f32, TCG_CALL_NO_RWG, f32, f16, ptr, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f32_to_f16, TCG_CALL_NO_RWG, f16, f32, ptr, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, f16, ptr, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, f16, f64, ptr, i32) DEF_HELPER_4(vfp_muladdd, f64, f64, f64, f64, ptr) DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, ptr) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index a0b0c43d12..d8284678f7 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -5147,10 +5147,15 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, } else { /* Single to half */ TCGv_i32 tcg_rd = tcg_temp_new_i32(); - gen_helper_vfp_fcvt_f32_to_f16(tcg_rd, tcg_rn, cpu_env); + TCGv_i32 ahp = get_ahp_flag(); + TCGv_ptr fpst = get_fpstatus_ptr(false); + + gen_helper_vfp_fcvt_f32_to_f16(tcg_rd, tcg_rn, fpst, ahp); /* write_fp_sreg is OK here because top half of tcg_rd is zero */ write_fp_sreg(s, rd, tcg_rd); tcg_temp_free_i32(tcg_rd); + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); } tcg_temp_free_i32(tcg_rn); break; @@ -5163,9 +5168,13 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, /* Double to single */ gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, cpu_env); } else { + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); /* Double to half */ - gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, cpu_env); + gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, fpst, ahp); /* write_fp_sreg is OK here because top half of tcg_rd is zero */ + tcg_temp_free_ptr(fpst); + tcg_temp_free_i32(ahp); } write_fp_sreg(s, rd, tcg_rd); tcg_temp_free_i32(tcg_rd); @@ -5175,17 +5184,21 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, case 0x3: { TCGv_i32 tcg_rn = read_fp_sreg(s, rn); + TCGv_ptr tcg_fpst = get_fpstatus_ptr(false); + TCGv_i32 tcg_ahp = get_ahp_flag(); tcg_gen_ext16u_i32(tcg_rn, tcg_rn); if (dtype == 0) { /* Half to single */ TCGv_i32 tcg_rd = tcg_temp_new_i32(); - gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, cpu_env); + gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); write_fp_sreg(s, rd, tcg_rd); + tcg_temp_free_ptr(tcg_fpst); + tcg_temp_free_i32(tcg_ahp); tcg_temp_free_i32(tcg_rd); } else { /* Half to double */ TCGv_i64 tcg_rd = tcg_temp_new_i64(); - gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, cpu_env); + gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); write_fp_dreg(s, rd, tcg_rd); tcg_temp_free_i64(tcg_rd); } @@ -9053,12 +9066,17 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, } else { TCGv_i32 tcg_lo = tcg_temp_new_i32(); TCGv_i32 tcg_hi = tcg_temp_new_i32(); + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); + tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, tcg_op); - gen_helper_vfp_fcvt_f32_to_f16(tcg_lo, tcg_lo, cpu_env); - gen_helper_vfp_fcvt_f32_to_f16(tcg_hi, tcg_hi, cpu_env); + gen_helper_vfp_fcvt_f32_to_f16(tcg_lo, tcg_lo, fpst, ahp); + gen_helper_vfp_fcvt_f32_to_f16(tcg_hi, tcg_hi, fpst, ahp); tcg_gen_deposit_i32(tcg_res[pass], tcg_lo, tcg_hi, 16, 16); tcg_temp_free_i32(tcg_lo); tcg_temp_free_i32(tcg_hi); + tcg_temp_free_ptr(fpst); + tcg_temp_free_i32(ahp); } break; case 0x56: /* FCVTXN, FCVTXN2 */ @@ -11532,18 +11550,23 @@ static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, /* 16 -> 32 bit fp conversion */ int srcelt = is_q ? 4 : 0; TCGv_i32 tcg_res[4]; + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); for (pass = 0; pass < 4; pass++) { tcg_res[pass] = tcg_temp_new_i32(); read_vec_element_i32(s, tcg_res[pass], rn, srcelt + pass, MO_16); gen_helper_vfp_fcvt_f16_to_f32(tcg_res[pass], tcg_res[pass], - cpu_env); + fpst, ahp); } for (pass = 0; pass < 4; pass++) { write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_32); tcg_temp_free_i32(tcg_res[pass]); } + + tcg_temp_free_ptr(fpst); + tcg_temp_free_i32(ahp); } } diff --git a/target/arm/translate.c b/target/arm/translate.c index 731cf327a1..46a9725bfd 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -3824,38 +3824,56 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) gen_vfp_sqrt(dp); break; case 4: /* vcvtb.f32.f16, vcvtb.f64.f16 */ + { + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp_mode = get_ahp_flag(); tmp = gen_vfp_mrs(); tcg_gen_ext16u_i32(tmp, tmp); if (dp) { gen_helper_vfp_fcvt_f16_to_f64(cpu_F0d, tmp, - cpu_env); + fpst, ahp_mode); } else { gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp, - cpu_env); + fpst, ahp_mode); } + tcg_temp_free_i32(ahp_mode); + tcg_temp_free_ptr(fpst); tcg_temp_free_i32(tmp); break; + } case 5: /* vcvtt.f32.f16, vcvtt.f64.f16 */ + { + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); tmp = gen_vfp_mrs(); tcg_gen_shri_i32(tmp, tmp, 16); if (dp) { gen_helper_vfp_fcvt_f16_to_f64(cpu_F0d, tmp, - cpu_env); + fpst, ahp); } else { gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp, - cpu_env); + fpst, ahp); } tcg_temp_free_i32(tmp); + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); break; + } case 6: /* vcvtb.f16.f32, vcvtb.f16.f64 */ + { + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); tmp = tcg_temp_new_i32(); + if (dp) { gen_helper_vfp_fcvt_f64_to_f16(tmp, cpu_F0d, - cpu_env); + fpst, ahp); } else { gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, - cpu_env); + fpst, ahp); } + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); gen_mov_F0_vreg(0, rd); tmp2 = gen_vfp_mrs(); tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000); @@ -3863,15 +3881,21 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp2); gen_vfp_msr(tmp); break; + } case 7: /* vcvtt.f16.f32, vcvtt.f16.f64 */ + { + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); tmp = tcg_temp_new_i32(); if (dp) { gen_helper_vfp_fcvt_f64_to_f16(tmp, cpu_F0d, - cpu_env); + fpst, ahp); } else { gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, - cpu_env); + fpst, ahp); } + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); tcg_gen_shli_i32(tmp, tmp, 16); gen_mov_F0_vreg(0, rd); tmp2 = gen_vfp_mrs(); @@ -3880,6 +3904,7 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp2); gen_vfp_msr(tmp); break; + } case 8: /* cmp */ gen_vfp_cmp(dp); break; @@ -7222,53 +7247,70 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) } break; case NEON_2RM_VCVT_F16_F32: + { + TCGv_ptr fpst; + TCGv_i32 ahp; + if (!arm_dc_feature(s, ARM_FEATURE_VFP_FP16) || q || (rm & 1)) { return 1; } tmp = tcg_temp_new_i32(); tmp2 = tcg_temp_new_i32(); + fpst = get_fpstatus_ptr(true); + ahp = get_ahp_flag(); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 0)); - gen_helper_neon_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env); + gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, fpst, ahp); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 1)); - gen_helper_neon_fcvt_f32_to_f16(tmp2, cpu_F0s, cpu_env); + gen_helper_vfp_fcvt_f32_to_f16(tmp2, cpu_F0s, fpst, ahp); tcg_gen_shli_i32(tmp2, tmp2, 16); tcg_gen_or_i32(tmp2, tmp2, tmp); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 2)); - gen_helper_neon_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env); + gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, fpst, ahp); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 3)); neon_store_reg(rd, 0, tmp2); tmp2 = tcg_temp_new_i32(); - gen_helper_neon_fcvt_f32_to_f16(tmp2, cpu_F0s, cpu_env); + gen_helper_vfp_fcvt_f32_to_f16(tmp2, cpu_F0s, fpst, ahp); tcg_gen_shli_i32(tmp2, tmp2, 16); tcg_gen_or_i32(tmp2, tmp2, tmp); neon_store_reg(rd, 1, tmp2); tcg_temp_free_i32(tmp); + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); break; + } case NEON_2RM_VCVT_F32_F16: + { + TCGv_ptr fpst; + TCGv_i32 ahp; if (!arm_dc_feature(s, ARM_FEATURE_VFP_FP16) || q || (rd & 1)) { return 1; } + fpst = get_fpstatus_ptr(true); + ahp = get_ahp_flag(); tmp3 = tcg_temp_new_i32(); tmp = neon_load_reg(rm, 0); tmp2 = neon_load_reg(rm, 1); tcg_gen_ext16u_i32(tmp3, tmp); - gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, fpst, ahp); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 0)); tcg_gen_shri_i32(tmp3, tmp, 16); - gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, fpst, ahp); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 1)); tcg_temp_free_i32(tmp); tcg_gen_ext16u_i32(tmp3, tmp2); - gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, fpst, ahp); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 2)); tcg_gen_shri_i32(tmp3, tmp2, 16); - gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, fpst, ahp); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 3)); tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp3); + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); break; + } case NEON_2RM_AESE: case NEON_2RM_AESMC: if (!arm_dc_feature(s, ARM_FEATURE_V8_AES) || ((rm | rd) & 1)) { diff --git a/target/arm/translate.h b/target/arm/translate.h index 37a1bba056..45f04244be 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -177,4 +177,16 @@ void arm_free_cc(DisasCompare *cmp); void arm_jump_cc(DisasCompare *cmp, TCGLabel *label); void arm_gen_test_cc(int cc, TCGLabel *label); +/* Return state of Alternate Half-precision flag, caller frees result */ +static inline TCGv_i32 get_ahp_flag(void) +{ + TCGv_i32 ret = tcg_temp_new_i32(); + + tcg_gen_ld_i32(ret, cpu_env, + offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPSCR])); + tcg_gen_extract_i32(ret, ret, 26, 1); + + return ret; +} + #endif /* TARGET_ARM_TRANSLATE_H */ From 0acb9e7cb341cd767e39ec0875c8706eb2f1c359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 7 May 2018 13:57:39 +0100 Subject: [PATCH 0555/2380] target/arm: squash FZ16 behaviour for conversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ARM ARM specifies FZ16 is suppressed for conversions. Rather than pushing this logic into the softfloat code we can simply save the FZ state and temporarily disable it for the softfloat call. Reviewed-by: Peter Maydell Signed-off-by: Alex Bennée Signed-off-by: Richard Henderson --- target/arm/helper.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 1762042fc7..238a3ceba8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11542,22 +11542,54 @@ uint32_t HELPER(set_neon_rmode)(uint32_t rmode, CPUARMState *env) /* Half precision conversions. */ float32 HELPER(vfp_fcvt_f16_to_f32)(float16 a, void *fpstp, uint32_t ahp_mode) { - return float16_to_float32(a, !ahp_mode, fpstp); + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing input denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_inputs_to_zero(fpst); + set_flush_inputs_to_zero(false, fpst); + float32 r = float16_to_float32(a, !ahp_mode, fpst); + set_flush_inputs_to_zero(save, fpst); + return r; } float16 HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) { - return float32_to_float16(a, !ahp_mode, fpstp); + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing output denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_to_zero(fpst); + set_flush_to_zero(false, fpst); + float16 r = float32_to_float16(a, !ahp_mode, fpst); + set_flush_to_zero(save, fpst); + return r; } float64 HELPER(vfp_fcvt_f16_to_f64)(float16 a, void *fpstp, uint32_t ahp_mode) { - return float16_to_float64(a, !ahp_mode, fpstp); + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing input denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_inputs_to_zero(fpst); + set_flush_inputs_to_zero(false, fpst); + float64 r = float16_to_float64(a, !ahp_mode, fpst); + set_flush_inputs_to_zero(save, fpst); + return r; } float16 HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) { - return float64_to_float16(a, !ahp_mode, fpstp); + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing output denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_to_zero(fpst); + set_flush_to_zero(false, fpst); + float16 r = float64_to_float16(a, !ahp_mode, fpst); + set_flush_to_zero(save, fpst); + return r; } #define float32_two make_float32(0x40000000) From ca3a3d5a3141d44aa717dc11e4d33a834a85e1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 2 May 2018 15:58:31 +0100 Subject: [PATCH 0556/2380] fpu/softfloat: Partial support for ARM Alternative half-precision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For float16 ARM supports an alternative half-precision format which sacrifices the ability to represent NaN/Inf in return for a higher dynamic range. The new FloatFmt flag, arm_althp, is then used to modify the behaviour of canonicalize and round_canonical with respect to representation and exception raising. Usage of this new flag waits until we re-factor float-to-float conversions. Reviewed-by: Peter Maydell Signed-off-by: Alex Bennée Signed-off-by: Richard Henderson --- fpu/softfloat.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 41253c6749..64e1ad4f98 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -220,8 +220,10 @@ typedef struct { * frac_shift: shift to normalise the fraction with DECOMPOSED_BINARY_POINT * The following are computed based the size of fraction * frac_lsb: least significant bit of fraction - * fram_lsbm1: the bit bellow the least significant bit (for rounding) + * frac_lsbm1: the bit below the least significant bit (for rounding) * round_mask/roundeven_mask: masks used for rounding + * The following optional modifiers are available: + * arm_althp: handle ARM Alternative Half Precision */ typedef struct { int exp_size; @@ -233,6 +235,7 @@ typedef struct { uint64_t frac_lsbm1; uint64_t round_mask; uint64_t roundeven_mask; + bool arm_althp; } FloatFmt; /* Expand fields based on the size of exponent and fraction */ @@ -324,7 +327,7 @@ static inline float64 float64_pack_raw(FloatParts p) static FloatParts canonicalize(FloatParts part, const FloatFmt *parm, float_status *status) { - if (part.exp == parm->exp_max) { + if (part.exp == parm->exp_max && !parm->arm_althp) { if (part.frac == 0) { part.cls = float_class_inf; } else { @@ -413,7 +416,15 @@ static FloatParts round_canonical(FloatParts p, float_status *s, } frac >>= frac_shift; - if (unlikely(exp >= exp_max)) { + if (parm->arm_althp) { + /* ARM Alt HP eschews Inf and NaN for a wider exponent. */ + if (unlikely(exp > exp_max)) { + /* Overflow. Return the maximum normal. */ + flags = float_flag_invalid; + exp = exp_max; + frac = -1; + } + } else if (unlikely(exp >= exp_max)) { flags |= float_flag_overflow | float_flag_inexact; if (overflow_norm) { exp = exp_max - 1; @@ -464,12 +475,14 @@ static FloatParts round_canonical(FloatParts p, float_status *s, case float_class_inf: do_inf: + assert(!parm->arm_althp); exp = exp_max; frac = 0; break; case float_class_qnan: case float_class_snan: + assert(!parm->arm_althp); exp = exp_max; frac >>= parm->frac_shift; break; From 6fed16b265a4fcc810895bbca4d67e1ae7a89f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 16 Mar 2018 16:45:02 +0000 Subject: [PATCH 0557/2380] fpu/softfloat: re-factor float to float conversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to delete a lot of additional boilerplate code which is no longer needed. Reviewed-by: Peter Maydell Signed-off-by: Alex Bennée Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 40 --- fpu/softfloat.c | 488 +++++++++---------------------------- include/fpu/softfloat.h | 8 +- 3 files changed, 122 insertions(+), 414 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 571d1df378..995a0132c6 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -377,46 +377,6 @@ float16 float16_maybe_silence_nan(float16 a, float_status *status) return a; } -/*---------------------------------------------------------------------------- -| Returns the result of converting the half-precision floating-point NaN -| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid -| exception is raised. -*----------------------------------------------------------------------------*/ - -static commonNaNT float16ToCommonNaN(float16 a, float_status *status) -{ - commonNaNT z; - - if (float16_is_signaling_nan(a, status)) { - float_raise(float_flag_invalid, status); - } - z.sign = float16_val(a) >> 15; - z.low = 0; - z.high = ((uint64_t) float16_val(a)) << 54; - return z; -} - -/*---------------------------------------------------------------------------- -| Returns the result of converting the canonical NaN `a' to the half- -| precision floating-point format. -*----------------------------------------------------------------------------*/ - -static float16 commonNaNToFloat16(commonNaNT a, float_status *status) -{ - uint16_t mantissa = a.high >> 54; - - if (status->default_nan_mode) { - return float16_default_nan(status); - } - - if (mantissa) { - return make_float16(((((uint16_t) a.sign) << 15) - | (0x1F << 10) | mantissa)); - } else { - return float16_default_nan(status); - } -} - /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 64e1ad4f98..55e6701f26 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -113,15 +113,6 @@ static inline int extractFloat16Exp(float16 a) return (float16_val(a) >> 10) & 0x1f; } -/*---------------------------------------------------------------------------- -| Returns the sign bit of the single-precision floating-point value `a'. -*----------------------------------------------------------------------------*/ - -static inline flag extractFloat16Sign(float16 a) -{ - return float16_val(a)>>15; -} - /*---------------------------------------------------------------------------- | Returns the fraction bits of the single-precision floating-point value `a'. *----------------------------------------------------------------------------*/ @@ -254,6 +245,11 @@ static const FloatFmt float16_params = { FLOAT_PARAMS(5, 10) }; +static const FloatFmt float16_params_ahp = { + FLOAT_PARAMS(5, 10), + .arm_althp = true +}; + static const FloatFmt float32_params = { FLOAT_PARAMS(8, 23) }; @@ -497,14 +493,27 @@ static FloatParts round_canonical(FloatParts p, float_status *s, return p; } +/* Explicit FloatFmt version */ +static FloatParts float16a_unpack_canonical(float16 f, float_status *s, + const FloatFmt *params) +{ + return canonicalize(float16_unpack_raw(f), params, s); +} + static FloatParts float16_unpack_canonical(float16 f, float_status *s) { - return canonicalize(float16_unpack_raw(f), &float16_params, s); + return float16a_unpack_canonical(f, s, &float16_params); +} + +static float16 float16a_round_pack_canonical(FloatParts p, float_status *s, + const FloatFmt *params) +{ + return float16_pack_raw(round_canonical(p, s, params)); } static float16 float16_round_pack_canonical(FloatParts p, float_status *s) { - return float16_pack_raw(round_canonical(p, s, &float16_params)); + return float16a_round_pack_canonical(p, s, &float16_params); } static FloatParts float32_unpack_canonical(float32 f, float_status *s) @@ -1181,6 +1190,104 @@ float64 float64_div(float64 a, float64 b, float_status *status) return float64_round_pack_canonical(pr, status); } +/* + * Float to Float conversions + * + * Returns the result of converting one float format to another. The + * conversion is performed according to the IEC/IEEE Standard for + * Binary Floating-Point Arithmetic. + * + * The float_to_float helper only needs to take care of raising + * invalid exceptions and handling the conversion on NaNs. + */ + +static FloatParts float_to_float(FloatParts a, const FloatFmt *dstf, + float_status *s) +{ + if (dstf->arm_althp) { + switch (a.cls) { + case float_class_qnan: + case float_class_snan: + /* There is no NaN in the destination format. Raise Invalid + * and return a zero with the sign of the input NaN. + */ + s->float_exception_flags |= float_flag_invalid; + a.cls = float_class_zero; + a.frac = 0; + a.exp = 0; + break; + + case float_class_inf: + /* There is no Inf in the destination format. Raise Invalid + * and return the maximum normal with the correct sign. + */ + s->float_exception_flags |= float_flag_invalid; + a.cls = float_class_normal; + a.exp = dstf->exp_max; + a.frac = ((1ull << dstf->frac_size) - 1) << dstf->frac_shift; + break; + + default: + break; + } + } else if (is_nan(a.cls)) { + if (is_snan(a.cls)) { + s->float_exception_flags |= float_flag_invalid; + a = parts_silence_nan(a, s); + } + if (s->default_nan_mode) { + return parts_default_nan(s); + } + } + return a; +} + +float32 float16_to_float32(float16 a, bool ieee, float_status *s) +{ + const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; + FloatParts p = float16a_unpack_canonical(a, s, fmt16); + FloatParts pr = float_to_float(p, &float32_params, s); + return float32_round_pack_canonical(pr, s); +} + +float64 float16_to_float64(float16 a, bool ieee, float_status *s) +{ + const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; + FloatParts p = float16a_unpack_canonical(a, s, fmt16); + FloatParts pr = float_to_float(p, &float64_params, s); + return float64_round_pack_canonical(pr, s); +} + +float16 float32_to_float16(float32 a, bool ieee, float_status *s) +{ + const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; + FloatParts p = float32_unpack_canonical(a, s); + FloatParts pr = float_to_float(p, fmt16, s); + return float16a_round_pack_canonical(pr, s, fmt16); +} + +float64 float32_to_float64(float32 a, float_status *s) +{ + FloatParts p = float32_unpack_canonical(a, s); + FloatParts pr = float_to_float(p, &float64_params, s); + return float64_round_pack_canonical(pr, s); +} + +float16 float64_to_float16(float64 a, bool ieee, float_status *s) +{ + const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; + FloatParts p = float64_unpack_canonical(a, s); + FloatParts pr = float_to_float(p, fmt16, s); + return float16a_round_pack_canonical(pr, s, fmt16); +} + +float32 float64_to_float32(float64 a, float_status *s) +{ + FloatParts p = float64_unpack_canonical(a, s); + FloatParts pr = float_to_float(p, &float32_params, s); + return float32_round_pack_canonical(pr, s); +} + /* * Rounds the floating-point value `a' to an integer, and returns the * result as a floating-point value. The operation is performed @@ -3124,41 +3231,6 @@ float128 uint64_to_float128(uint64_t a, float_status *status) return normalizeRoundAndPackFloat128(0, 0x406E, 0, a, status); } - - - -/*---------------------------------------------------------------------------- -| Returns the result of converting the single-precision floating-point value -| `a' to the double-precision floating-point format. The conversion is -| performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. -*----------------------------------------------------------------------------*/ - -float64 float32_to_float64(float32 a, float_status *status) -{ - flag aSign; - int aExp; - uint32_t aSig; - a = float32_squash_input_denormal(a, status); - - aSig = extractFloat32Frac( a ); - aExp = extractFloat32Exp( a ); - aSign = extractFloat32Sign( a ); - if ( aExp == 0xFF ) { - if (aSig) { - return commonNaNToFloat64(float32ToCommonNaN(a, status), status); - } - return packFloat64( aSign, 0x7FF, 0 ); - } - if ( aExp == 0 ) { - if ( aSig == 0 ) return packFloat64( aSign, 0, 0 ); - normalizeFloat32Subnormal( aSig, &aExp, &aSig ); - --aExp; - } - return packFloat64( aSign, aExp + 0x380, ( (uint64_t) aSig )<<29 ); - -} - /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point value | `a' to the extended double-precision floating-point format. The conversion @@ -3677,173 +3749,6 @@ int float32_unordered_quiet(float32 a, float32 b, float_status *status) return 0; } - -/*---------------------------------------------------------------------------- -| Returns the result of converting the double-precision floating-point value -| `a' to the single-precision floating-point format. The conversion is -| performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. -*----------------------------------------------------------------------------*/ - -float32 float64_to_float32(float64 a, float_status *status) -{ - flag aSign; - int aExp; - uint64_t aSig; - uint32_t zSig; - a = float64_squash_input_denormal(a, status); - - aSig = extractFloat64Frac( a ); - aExp = extractFloat64Exp( a ); - aSign = extractFloat64Sign( a ); - if ( aExp == 0x7FF ) { - if (aSig) { - return commonNaNToFloat32(float64ToCommonNaN(a, status), status); - } - return packFloat32( aSign, 0xFF, 0 ); - } - shift64RightJamming( aSig, 22, &aSig ); - zSig = aSig; - if ( aExp || zSig ) { - zSig |= 0x40000000; - aExp -= 0x381; - } - return roundAndPackFloat32(aSign, aExp, zSig, status); - -} - - -/*---------------------------------------------------------------------------- -| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a -| half-precision floating-point value, returning the result. After being -| shifted into the proper positions, the three fields are simply added -| together to form the result. This means that any integer portion of `zSig' -| will be added into the exponent. Since a properly normalized significand -| will have an integer portion equal to 1, the `zExp' input should be 1 less -| than the desired result exponent whenever `zSig' is a complete, normalized -| significand. -*----------------------------------------------------------------------------*/ -static float16 packFloat16(flag zSign, int zExp, uint16_t zSig) -{ - return make_float16( - (((uint32_t)zSign) << 15) + (((uint32_t)zExp) << 10) + zSig); -} - -/*---------------------------------------------------------------------------- -| Takes an abstract floating-point value having sign `zSign', exponent `zExp', -| and significand `zSig', and returns the proper half-precision floating- -| point value corresponding to the abstract input. Ordinarily, the abstract -| value is simply rounded and packed into the half-precision format, with -| the inexact exception raised if the abstract input cannot be represented -| exactly. However, if the abstract value is too large, the overflow and -| inexact exceptions are raised and an infinity or maximal finite value is -| returned. If the abstract value is too small, the input value is rounded to -| a subnormal number, and the underflow and inexact exceptions are raised if -| the abstract input cannot be represented exactly as a subnormal half- -| precision floating-point number. -| The `ieee' flag indicates whether to use IEEE standard half precision, or -| ARM-style "alternative representation", which omits the NaN and Inf -| encodings in order to raise the maximum representable exponent by one. -| The input significand `zSig' has its binary point between bits 22 -| and 23, which is 13 bits to the left of the usual location. This shifted -| significand must be normalized or smaller. If `zSig' is not normalized, -| `zExp' must be 0; in that case, the result returned is a subnormal number, -| and it must not require rounding. In the usual case that `zSig' is -| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent. -| Note the slightly odd position of the binary point in zSig compared with the -| other roundAndPackFloat functions. This should probably be fixed if we -| need to implement more float16 routines than just conversion. -| The handling of underflow and overflow follows the IEC/IEEE Standard for -| Binary Floating-Point Arithmetic. -*----------------------------------------------------------------------------*/ - -static float16 roundAndPackFloat16(flag zSign, int zExp, - uint32_t zSig, flag ieee, - float_status *status) -{ - int maxexp = ieee ? 29 : 30; - uint32_t mask; - uint32_t increment; - bool rounding_bumps_exp; - bool is_tiny = false; - - /* Calculate the mask of bits of the mantissa which are not - * representable in half-precision and will be lost. - */ - if (zExp < 1) { - /* Will be denormal in halfprec */ - mask = 0x00ffffff; - if (zExp >= -11) { - mask >>= 11 + zExp; - } - } else { - /* Normal number in halfprec */ - mask = 0x00001fff; - } - - switch (status->float_rounding_mode) { - case float_round_nearest_even: - increment = (mask + 1) >> 1; - if ((zSig & mask) == increment) { - increment = zSig & (increment << 1); - } - break; - case float_round_ties_away: - increment = (mask + 1) >> 1; - break; - case float_round_up: - increment = zSign ? 0 : mask; - break; - case float_round_down: - increment = zSign ? mask : 0; - break; - default: /* round_to_zero */ - increment = 0; - break; - } - - rounding_bumps_exp = (zSig + increment >= 0x01000000); - - if (zExp > maxexp || (zExp == maxexp && rounding_bumps_exp)) { - if (ieee) { - float_raise(float_flag_overflow | float_flag_inexact, status); - return packFloat16(zSign, 0x1f, 0); - } else { - float_raise(float_flag_invalid, status); - return packFloat16(zSign, 0x1f, 0x3ff); - } - } - - if (zExp < 0) { - /* Note that flush-to-zero does not affect half-precision results */ - is_tiny = - (status->float_detect_tininess == float_tininess_before_rounding) - || (zExp < -1) - || (!rounding_bumps_exp); - } - if (zSig & mask) { - float_raise(float_flag_inexact, status); - if (is_tiny) { - float_raise(float_flag_underflow, status); - } - } - - zSig += increment; - if (rounding_bumps_exp) { - zSig >>= 1; - zExp++; - } - - if (zExp < -10) { - return packFloat16(zSign, 0, 0); - } - if (zExp < 0) { - zSig >>= -zExp; - zExp = 0; - } - return packFloat16(zSign, zExp, zSig >> 13); -} - /*---------------------------------------------------------------------------- | If `a' is denormal and we are in flush-to-zero mode then set the | input-denormal exception and return zero. Otherwise just return the value. @@ -3859,163 +3764,6 @@ float16 float16_squash_input_denormal(float16 a, float_status *status) return a; } -static void normalizeFloat16Subnormal(uint32_t aSig, int *zExpPtr, - uint32_t *zSigPtr) -{ - int8_t shiftCount = countLeadingZeros32(aSig) - 21; - *zSigPtr = aSig << shiftCount; - *zExpPtr = 1 - shiftCount; -} - -/* Half precision floats come in two formats: standard IEEE and "ARM" format. - The latter gains extra exponent range by omitting the NaN/Inf encodings. */ - -float32 float16_to_float32(float16 a, flag ieee, float_status *status) -{ - flag aSign; - int aExp; - uint32_t aSig; - - aSign = extractFloat16Sign(a); - aExp = extractFloat16Exp(a); - aSig = extractFloat16Frac(a); - - if (aExp == 0x1f && ieee) { - if (aSig) { - return commonNaNToFloat32(float16ToCommonNaN(a, status), status); - } - return packFloat32(aSign, 0xff, 0); - } - if (aExp == 0) { - if (aSig == 0) { - return packFloat32(aSign, 0, 0); - } - - normalizeFloat16Subnormal(aSig, &aExp, &aSig); - aExp--; - } - return packFloat32( aSign, aExp + 0x70, aSig << 13); -} - -float16 float32_to_float16(float32 a, flag ieee, float_status *status) -{ - flag aSign; - int aExp; - uint32_t aSig; - - a = float32_squash_input_denormal(a, status); - - aSig = extractFloat32Frac( a ); - aExp = extractFloat32Exp( a ); - aSign = extractFloat32Sign( a ); - if ( aExp == 0xFF ) { - if (aSig) { - /* Input is a NaN */ - if (!ieee) { - float_raise(float_flag_invalid, status); - return packFloat16(aSign, 0, 0); - } - return commonNaNToFloat16( - float32ToCommonNaN(a, status), status); - } - /* Infinity */ - if (!ieee) { - float_raise(float_flag_invalid, status); - return packFloat16(aSign, 0x1f, 0x3ff); - } - return packFloat16(aSign, 0x1f, 0); - } - if (aExp == 0 && aSig == 0) { - return packFloat16(aSign, 0, 0); - } - /* Decimal point between bits 22 and 23. Note that we add the 1 bit - * even if the input is denormal; however this is harmless because - * the largest possible single-precision denormal is still smaller - * than the smallest representable half-precision denormal, and so we - * will end up ignoring aSig and returning via the "always return zero" - * codepath. - */ - aSig |= 0x00800000; - aExp -= 0x71; - - return roundAndPackFloat16(aSign, aExp, aSig, ieee, status); -} - -float64 float16_to_float64(float16 a, flag ieee, float_status *status) -{ - flag aSign; - int aExp; - uint32_t aSig; - - aSign = extractFloat16Sign(a); - aExp = extractFloat16Exp(a); - aSig = extractFloat16Frac(a); - - if (aExp == 0x1f && ieee) { - if (aSig) { - return commonNaNToFloat64( - float16ToCommonNaN(a, status), status); - } - return packFloat64(aSign, 0x7ff, 0); - } - if (aExp == 0) { - if (aSig == 0) { - return packFloat64(aSign, 0, 0); - } - - normalizeFloat16Subnormal(aSig, &aExp, &aSig); - aExp--; - } - return packFloat64(aSign, aExp + 0x3f0, ((uint64_t)aSig) << 42); -} - -float16 float64_to_float16(float64 a, flag ieee, float_status *status) -{ - flag aSign; - int aExp; - uint64_t aSig; - uint32_t zSig; - - a = float64_squash_input_denormal(a, status); - - aSig = extractFloat64Frac(a); - aExp = extractFloat64Exp(a); - aSign = extractFloat64Sign(a); - if (aExp == 0x7FF) { - if (aSig) { - /* Input is a NaN */ - if (!ieee) { - float_raise(float_flag_invalid, status); - return packFloat16(aSign, 0, 0); - } - return commonNaNToFloat16( - float64ToCommonNaN(a, status), status); - } - /* Infinity */ - if (!ieee) { - float_raise(float_flag_invalid, status); - return packFloat16(aSign, 0x1f, 0x3ff); - } - return packFloat16(aSign, 0x1f, 0); - } - shift64RightJamming(aSig, 29, &aSig); - zSig = aSig; - if (aExp == 0 && zSig == 0) { - return packFloat16(aSign, 0, 0); - } - /* Decimal point between bits 22 and 23. Note that we add the 1 bit - * even if the input is denormal; however this is harmless because - * the largest possible single-precision denormal is still smaller - * than the smallest representable half-precision denormal, and so we - * will end up ignoring aSig and returning via the "always return zero" - * codepath. - */ - zSig |= 0x00800000; - aExp -= 0x3F1; - - return roundAndPackFloat16(aSign, aExp, zSig, ieee, status); -} - /*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point value | `a' to the extended double-precision floating-point format. The conversion diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 43962dc3f5..a6860e858d 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -211,10 +211,10 @@ float128 uint64_to_float128(uint64_t, float_status *status); /*---------------------------------------------------------------------------- | Software half-precision conversion routines. *----------------------------------------------------------------------------*/ -float16 float32_to_float16(float32, flag, float_status *status); -float32 float16_to_float32(float16, flag, float_status *status); -float16 float64_to_float16(float64 a, flag ieee, float_status *status); -float64 float16_to_float64(float16 a, flag ieee, float_status *status); +float16 float32_to_float16(float32, bool ieee, float_status *status); +float32 float16_to_float32(float16, bool ieee, float_status *status); +float16 float64_to_float16(float64 a, bool ieee, float_status *status); +float64 float16_to_float64(float16 a, bool ieee, float_status *status); int16_t float16_to_int16(float16, float_status *status); uint16_t float16_to_uint16(float16 a, float_status *status); int16_t float16_to_int16_round_to_zero(float16, float_status *status); From d7ecc062c4e264f716ed239df931f52adb340508 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 13:43:13 -0700 Subject: [PATCH 0558/2380] target/arm: Use floatX_silence_nan when we have already checked for SNaN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/arm/helper-a64.c | 6 +++--- target/arm/helper.c | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 4f8034c513..6f0eb83661 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -376,7 +376,7 @@ float16 HELPER(frecpx_f16)(float16 a, void *fpstp) float16 nan = a; if (float16_is_signaling_nan(a, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float16_maybe_silence_nan(a, fpst); + nan = float16_silence_nan(a, fpst); } if (fpst->default_nan_mode) { nan = float16_default_nan(fpst); @@ -405,7 +405,7 @@ float32 HELPER(frecpx_f32)(float32 a, void *fpstp) float32 nan = a; if (float32_is_signaling_nan(a, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float32_maybe_silence_nan(a, fpst); + nan = float32_silence_nan(a, fpst); } if (fpst->default_nan_mode) { nan = float32_default_nan(fpst); @@ -434,7 +434,7 @@ float64 HELPER(frecpx_f64)(float64 a, void *fpstp) float64 nan = a; if (float64_is_signaling_nan(a, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float64_maybe_silence_nan(a, fpst); + nan = float64_silence_nan(a, fpst); } if (fpst->default_nan_mode) { nan = float64_default_nan(fpst); diff --git a/target/arm/helper.c b/target/arm/helper.c index 238a3ceba8..e05c7230d4 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11731,7 +11731,7 @@ float16 HELPER(recpe_f16)(float16 input, void *fpstp) float16 nan = f16; if (float16_is_signaling_nan(f16, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float16_maybe_silence_nan(f16, fpst); + nan = float16_silence_nan(f16, fpst); } if (fpst->default_nan_mode) { nan = float16_default_nan(fpst); @@ -11779,7 +11779,7 @@ float32 HELPER(recpe_f32)(float32 input, void *fpstp) float32 nan = f32; if (float32_is_signaling_nan(f32, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float32_maybe_silence_nan(f32, fpst); + nan = float32_silence_nan(f32, fpst); } if (fpst->default_nan_mode) { nan = float32_default_nan(fpst); @@ -11827,7 +11827,7 @@ float64 HELPER(recpe_f64)(float64 input, void *fpstp) float64 nan = f64; if (float64_is_signaling_nan(f64, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float64_maybe_silence_nan(f64, fpst); + nan = float64_silence_nan(f64, fpst); } if (fpst->default_nan_mode) { nan = float64_default_nan(fpst); @@ -11926,7 +11926,7 @@ float16 HELPER(rsqrte_f16)(float16 input, void *fpstp) float16 nan = f16; if (float16_is_signaling_nan(f16, s)) { float_raise(float_flag_invalid, s); - nan = float16_maybe_silence_nan(f16, s); + nan = float16_silence_nan(f16, s); } if (s->default_nan_mode) { nan = float16_default_nan(s); @@ -11970,7 +11970,7 @@ float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) float32 nan = f32; if (float32_is_signaling_nan(f32, s)) { float_raise(float_flag_invalid, s); - nan = float32_maybe_silence_nan(f32, s); + nan = float32_silence_nan(f32, s); } if (s->default_nan_mode) { nan = float32_default_nan(s); @@ -12013,7 +12013,7 @@ float64 HELPER(rsqrte_f64)(float64 input, void *fpstp) float64 nan = f64; if (float64_is_signaling_nan(f64, s)) { float_raise(float_flag_invalid, s); - nan = float64_maybe_silence_nan(f64, s); + nan = float64_silence_nan(f64, s); } if (s->default_nan_mode) { nan = float64_default_nan(s); From a9d173dc603af74102c24c1c92d479ba580bbf07 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 13:45:21 -0700 Subject: [PATCH 0559/2380] target/arm: Remove floatX_maybe_silence_nan from conversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is now handled properly by the generic softfloat code. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/arm/helper-a64.c | 1 - target/arm/helper.c | 12 ++---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 6f0eb83661..f92bdea732 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -466,7 +466,6 @@ float32 HELPER(fcvtx_f64_to_f32)(float64 a, CPUARMState *env) set_float_rounding_mode(float_round_to_zero, &tstat); set_float_exception_flags(0, &tstat); r = float64_to_float32(a, &tstat); - r = float32_maybe_silence_nan(r, &tstat); exflags = get_float_exception_flags(&tstat); if (exflags & float_flag_inexact) { r = make_float32(float32_val(r) | 1); diff --git a/target/arm/helper.c b/target/arm/helper.c index e05c7230d4..db8bbe52a6 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11348,20 +11348,12 @@ FLOAT_CONVS(ui, d, 64, u) /* floating point conversion */ float64 VFP_HELPER(fcvtd, s)(float32 x, CPUARMState *env) { - float64 r = float32_to_float64(x, &env->vfp.fp_status); - /* ARM requires that S<->D conversion of any kind of NaN generates - * a quiet NaN by forcing the most significant frac bit to 1. - */ - return float64_maybe_silence_nan(r, &env->vfp.fp_status); + return float32_to_float64(x, &env->vfp.fp_status); } float32 VFP_HELPER(fcvts, d)(float64 x, CPUARMState *env) { - float32 r = float64_to_float32(x, &env->vfp.fp_status); - /* ARM requires that S<->D conversion of any kind of NaN generates - * a quiet NaN by forcing the most significant frac bit to 1. - */ - return float32_maybe_silence_nan(r, &env->vfp.fp_status); + return float64_to_float32(x, &env->vfp.fp_status); } /* VFP3 fixed point conversion. */ From e1cf9adf5b5defdc077e9029b9e776201e6a42a7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 13:46:40 -0700 Subject: [PATCH 0560/2380] target/hppa: Remove floatX_maybe_silence_nan from conversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is now handled properly by the generic softfloat code. Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/hppa/op_helper.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index a3af62daf7..912e8d5be4 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -341,7 +341,6 @@ float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) { float64 ret = float32_to_float64(arg, &env->fp_status); - ret = float64_maybe_silence_nan(ret, &env->fp_status); update_fr0_op(env, GETPC()); return ret; } @@ -349,7 +348,6 @@ float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) { float32 ret = float64_to_float32(arg, &env->fp_status); - ret = float32_maybe_silence_nan(ret, &env->fp_status); update_fr0_op(env, GETPC()); return ret; } From 1c0c951f717e66b4be45611c0d6661a2dff4241c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 13:49:00 -0700 Subject: [PATCH 0561/2380] target/m68k: Use floatX_silence_nan when we have already checked for SNaN Reviewed-by: Peter Maydell Reviewed-by: Laurent Vivier Signed-off-by: Richard Henderson --- target/m68k/softfloat.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c index d093997219..b45a5e8690 100644 --- a/target/m68k/softfloat.c +++ b/target/m68k/softfloat.c @@ -31,13 +31,14 @@ static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status) { if (floatx80_is_signaling_nan(a, status)) { float_raise(float_flag_invalid, status); + a = floatx80_silence_nan(a, status); } if (status->default_nan_mode) { return floatx80_default_nan(status); } - return floatx80_maybe_silence_nan(a, status); + return a; } /*---------------------------------------------------------------------------- From 4accd4a89f776b0d2a34d1edf74c785549c7f3b9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 13:50:41 -0700 Subject: [PATCH 0562/2380] target/mips: Remove floatX_maybe_silence_nan from conversions This is now handled properly by the generic softfloat code. Cc: Aurelien Jarno Cc: Yongbok Kim Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/mips/msa_helper.c | 4 ---- target/mips/op_helper.c | 2 -- 2 files changed, 6 deletions(-) diff --git a/target/mips/msa_helper.c b/target/mips/msa_helper.c index 8fb7a369ca..c74e3cdc65 100644 --- a/target/mips/msa_helper.c +++ b/target/mips/msa_helper.c @@ -1615,7 +1615,6 @@ static inline float16 float16_from_float32(int32_t a, flag ieee, float16 f_val; f_val = float32_to_float16((float32)a, ieee, status); - f_val = float16_maybe_silence_nan(f_val, status); return a < 0 ? (f_val | (1 << 15)) : f_val; } @@ -1625,7 +1624,6 @@ static inline float32 float32_from_float64(int64_t a, float_status *status) float32 f_val; f_val = float64_to_float32((float64)a, status); - f_val = float32_maybe_silence_nan(f_val, status); return a < 0 ? (f_val | (1 << 31)) : f_val; } @@ -1636,7 +1634,6 @@ static inline float32 float32_from_float16(int16_t a, flag ieee, float32 f_val; f_val = float16_to_float32((float16)a, ieee, status); - f_val = float32_maybe_silence_nan(f_val, status); return a < 0 ? (f_val | (1 << 31)) : f_val; } @@ -1646,7 +1643,6 @@ static inline float64 float64_from_float32(int32_t a, float_status *status) float64 f_val; f_val = float32_to_float64((float64)a, status); - f_val = float64_maybe_silence_nan(f_val, status); return a < 0 ? (f_val | (1ULL << 63)) : f_val; } diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c index 798cdad030..9025f42366 100644 --- a/target/mips/op_helper.c +++ b/target/mips/op_helper.c @@ -2700,7 +2700,6 @@ uint64_t helper_float_cvtd_s(CPUMIPSState *env, uint32_t fst0) uint64_t fdt2; fdt2 = float32_to_float64(fst0, &env->active_fpu.fp_status); - fdt2 = float64_maybe_silence_nan(fdt2, &env->active_fpu.fp_status); update_fcr31(env, GETPC()); return fdt2; } @@ -2790,7 +2789,6 @@ uint32_t helper_float_cvts_d(CPUMIPSState *env, uint64_t fdt0) uint32_t fst2; fst2 = float64_to_float32(fdt0, &env->active_fpu.fp_status); - fst2 = float32_maybe_silence_nan(fst2, &env->active_fpu.fp_status); update_fcr31(env, GETPC()); return fst2; } From cab3211261b3dad5de3b15fadcca483af5e2b113 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 13:52:48 -0700 Subject: [PATCH 0563/2380] target/riscv: Remove floatX_maybe_silence_nan from conversions This is now handled properly by the generic softfloat code. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Reviewed-by: Michael Clark Signed-off-by: Richard Henderson --- target/riscv/fpu_helper.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index abbadead5c..fdb87d8d82 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -279,14 +279,12 @@ uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1) { - rs1 = float64_to_float32(rs1, &env->fp_status); - return float32_maybe_silence_nan(rs1, &env->fp_status); + return float64_to_float32(rs1, &env->fp_status); } uint64_t helper_fcvt_d_s(CPURISCVState *env, uint64_t rs1) { - rs1 = float32_to_float64(rs1, &env->fp_status); - return float64_maybe_silence_nan(rs1, &env->fp_status); + return float32_to_float64(rs1, &env->fp_status); } uint64_t helper_fsqrt_d(CPURISCVState *env, uint64_t frs1) From d0cfecb50df777d9d97118325d663682598a5a83 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 13:55:15 -0700 Subject: [PATCH 0564/2380] target/s390x: Remove floatX_maybe_silence_nan from conversions This is now handled properly by the generic softfloat code. Cc: Alexander Graf Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- target/s390x/fpu_helper.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/s390x/fpu_helper.c b/target/s390x/fpu_helper.c index 43f8bf1c94..5c5b451b3b 100644 --- a/target/s390x/fpu_helper.c +++ b/target/s390x/fpu_helper.c @@ -269,7 +269,7 @@ uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2) { float64 ret = float32_to_float64(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return float64_maybe_silence_nan(ret, &env->fpu_status); + return ret; } /* convert 128-bit float to 64-bit float */ @@ -277,7 +277,7 @@ uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al) { float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status); handle_exceptions(env, GETPC()); - return float64_maybe_silence_nan(ret, &env->fpu_status); + return ret; } /* convert 64-bit float to 128-bit float */ @@ -285,7 +285,7 @@ uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2) { float128 ret = float64_to_float128(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return RET128(float128_maybe_silence_nan(ret, &env->fpu_status)); + return RET128(ret); } /* convert 32-bit float to 128-bit float */ @@ -293,7 +293,7 @@ uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2) { float128 ret = float32_to_float128(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return RET128(float128_maybe_silence_nan(ret, &env->fpu_status)); + return RET128(ret); } /* convert 64-bit float to 32-bit float */ @@ -301,7 +301,7 @@ uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2) { float32 ret = float64_to_float32(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return float32_maybe_silence_nan(ret, &env->fpu_status); + return ret; } /* convert 128-bit float to 32-bit float */ @@ -309,7 +309,7 @@ uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al) { float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status); handle_exceptions(env, GETPC()); - return float32_maybe_silence_nan(ret, &env->fpu_status); + return ret; } /* 32-bit FP compare */ From 4885312f47c0b3607e36d0568db3d717a79e51a3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 14:11:15 -0700 Subject: [PATCH 0565/2380] fpu/softfloat: Use float*_silence_nan in propagateFloat*NaN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have already checked the arguments for SNaN; we don't need to do it again. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 44 +++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 995a0132c6..4fa068a5dc 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -498,7 +498,7 @@ static float32 commonNaNToFloat32(commonNaNT a, float_status *status) | The routine is passed various bits of information about the | two NaNs and should return 0 to select NaN a and 1 for NaN b. | Note that signalling NaNs are always squashed to quiet NaNs -| by the caller, by calling floatXX_maybe_silence_nan() before +| by the caller, by calling floatXX_silence_nan() before | returning them. | | aIsLargerSignificand is only valid if both a and b are NaNs @@ -536,7 +536,7 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, { /* According to MIPS specifications, if one of the two operands is * a sNaN, a new qNaN has to be generated. This is done in - * floatXX_maybe_silence_nan(). For qNaN inputs the specifications + * floatXX_silence_nan(). For qNaN inputs the specifications * says: "When possible, this QNaN result is one of the operand QNaN * values." In practice it seems that most implementations choose * the first operand if both operands are qNaN. In short this gives @@ -788,9 +788,15 @@ static float32 propagateFloat32NaN(float32 a, float32 b, float_status *status) if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, aIsLargerSignificand)) { - return float32_maybe_silence_nan(b, status); + if (bIsSignalingNaN) { + return float32_silence_nan(b, status); + } + return b; } else { - return float32_maybe_silence_nan(a, status); + if (aIsSignalingNaN) { + return float32_silence_nan(a, status); + } + return a; } } @@ -950,9 +956,15 @@ static float64 propagateFloat64NaN(float64 a, float64 b, float_status *status) if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, aIsLargerSignificand)) { - return float64_maybe_silence_nan(b, status); + if (bIsSignalingNaN) { + return float64_silence_nan(b, status); + } + return b; } else { - return float64_maybe_silence_nan(a, status); + if (aIsSignalingNaN) { + return float64_silence_nan(a, status); + } + return a; } } @@ -1121,9 +1133,15 @@ floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, aIsLargerSignificand)) { - return floatx80_maybe_silence_nan(b, status); + if (bIsSignalingNaN) { + return floatx80_silence_nan(b, status); + } + return b; } else { - return floatx80_maybe_silence_nan(a, status); + if (aIsSignalingNaN) { + return floatx80_silence_nan(a, status); + } + return a; } } @@ -1270,8 +1288,14 @@ static float128 propagateFloat128NaN(float128 a, float128 b, if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, aIsLargerSignificand)) { - return float128_maybe_silence_nan(b, status); + if (bIsSignalingNaN) { + return float128_silence_nan(b, status); + } + return b; } else { - return float128_maybe_silence_nan(a, status); + if (aIsSignalingNaN) { + return float128_silence_nan(a, status); + } + return a; } } From 5240a30dcc6ca85dc9352f351e2cc326402288ed Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 14:15:53 -0700 Subject: [PATCH 0566/2380] fpu/softfloat: Remove floatX_maybe_silence_nan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions are now unused. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 63 -------------------------------------- include/fpu/softfloat.h | 5 --- 2 files changed, 68 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 4fa068a5dc..d7033b7757 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -364,19 +364,6 @@ float16 float16_silence_nan(float16 a, float_status *status) #endif } -/*---------------------------------------------------------------------------- -| Returns a quiet NaN if the half-precision floating point value `a' is a -| signaling NaN; otherwise returns `a'. -*----------------------------------------------------------------------------*/ - -float16 float16_maybe_silence_nan(float16 a, float_status *status) -{ - if (float16_is_signaling_nan(a, status)) { - return float16_silence_nan(a, status); - } - return a; -} - /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -438,18 +425,6 @@ float32 float32_silence_nan(float32 a, float_status *status) } #endif } -/*---------------------------------------------------------------------------- -| Returns a quiet NaN if the single-precision floating point value `a' is a -| signaling NaN; otherwise returns `a'. -*----------------------------------------------------------------------------*/ - -float32 float32_maybe_silence_nan(float32 a, float_status *status) -{ - if (float32_is_signaling_nan(a, status)) { - return float32_silence_nan(a, status); - } - return a; -} /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point NaN @@ -864,18 +839,6 @@ float64 float64_silence_nan(float64 a, float_status *status) #endif } -/*---------------------------------------------------------------------------- -| Returns a quiet NaN if the double-precision floating point value `a' is a -| signaling NaN; otherwise returns `a'. -*----------------------------------------------------------------------------*/ - -float64 float64_maybe_silence_nan(float64 a, float_status *status) -{ - if (float64_is_signaling_nan(a, status)) { - return float64_silence_nan(a, status); - } - return a; -} /*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point NaN @@ -1037,19 +1000,6 @@ floatx80 floatx80_silence_nan(floatx80 a, float_status *status) #endif } -/*---------------------------------------------------------------------------- -| Returns a quiet NaN if the extended double-precision floating point value -| `a' is a signaling NaN; otherwise returns `a'. -*----------------------------------------------------------------------------*/ - -floatx80 floatx80_maybe_silence_nan(floatx80 a, float_status *status) -{ - if (floatx80_is_signaling_nan(a, status)) { - return floatx80_silence_nan(a, status); - } - return a; -} - /*---------------------------------------------------------------------------- | Returns the result of converting the extended double-precision floating- | point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the @@ -1204,19 +1154,6 @@ float128 float128_silence_nan(float128 a, float_status *status) #endif } -/*---------------------------------------------------------------------------- -| Returns a quiet NaN if the quadruple-precision floating point value `a' is -| a signaling NaN; otherwise returns `a'. -*----------------------------------------------------------------------------*/ - -float128 float128_maybe_silence_nan(float128 a, float_status *status) -{ - if (float128_is_signaling_nan(a, status)) { - return float128_silence_nan(a, status); - } - return a; -} - /*---------------------------------------------------------------------------- | Returns the result of converting the quadruple-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index a6860e858d..69f4dbc4db 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -258,7 +258,6 @@ int float16_compare_quiet(float16, float16, float_status *status); int float16_is_quiet_nan(float16, float_status *status); int float16_is_signaling_nan(float16, float_status *status); float16 float16_silence_nan(float16, float_status *status); -float16 float16_maybe_silence_nan(float16, float_status *status); static inline int float16_is_any_nan(float16 a) { @@ -370,7 +369,6 @@ float32 float32_maxnummag(float32, float32, float_status *status); int float32_is_quiet_nan(float32, float_status *status); int float32_is_signaling_nan(float32, float_status *status); float32 float32_silence_nan(float32, float_status *status); -float32 float32_maybe_silence_nan(float32, float_status *status); float32 float32_scalbn(float32, int, float_status *status); static inline float32 float32_abs(float32 a) @@ -500,7 +498,6 @@ float64 float64_maxnummag(float64, float64, float_status *status); int float64_is_quiet_nan(float64 a, float_status *status); int float64_is_signaling_nan(float64, float_status *status); float64 float64_silence_nan(float64, float_status *status); -float64 float64_maybe_silence_nan(float64, float_status *status); float64 float64_scalbn(float64, int, float_status *status); static inline float64 float64_abs(float64 a) @@ -604,7 +601,6 @@ int floatx80_compare_quiet(floatx80, floatx80, float_status *status); int floatx80_is_quiet_nan(floatx80, float_status *status); int floatx80_is_signaling_nan(floatx80, float_status *status); floatx80 floatx80_silence_nan(floatx80, float_status *status); -floatx80 floatx80_maybe_silence_nan(floatx80, float_status *status); floatx80 floatx80_scalbn(floatx80, int, float_status *status); static inline floatx80 floatx80_abs(floatx80 a) @@ -816,7 +812,6 @@ int float128_compare_quiet(float128, float128, float_status *status); int float128_is_quiet_nan(float128, float_status *status); int float128_is_signaling_nan(float128, float_status *status); float128 float128_silence_nan(float128, float_status *status); -float128 float128_maybe_silence_nan(float128, float_status *status); float128 float128_scalbn(float128, int, float_status *status); static inline float128 float128_abs(float128 a) From 03385dfdaaa2dc31bbd07d13244a6b037bfab4cc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 14:48:17 -0700 Subject: [PATCH 0567/2380] fpu/softfloat: Specialize on snan_bit_is_one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only MIPS requires snan_bit_is_one to be variable. While we are specializing softfloat behaviour, allow other targets to eliminate this runtime check. Cc: Aurelien Jarno Cc: Yongbok Kim Cc: David Gibson Cc: Alexander Graf Cc: Guan Xuetao Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 68 ++++++++++++++++++++++------------- include/fpu/softfloat-types.h | 1 + target/hppa/cpu.c | 1 - target/ppc/fpu_helper.c | 1 - target/sh4/cpu.c | 1 - target/unicore32/cpu.c | 2 -- 6 files changed, 44 insertions(+), 30 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index d7033b7757..d1e06da75b 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -79,13 +79,31 @@ this code that are retained. * version 2 or later. See the COPYING file in the top-level directory. */ -#if defined(TARGET_XTENSA) /* Define for architectures which deviate from IEEE in not supporting * signaling NaNs (so all NaNs are treated as quiet). */ +#if defined(TARGET_XTENSA) #define NO_SIGNALING_NANS 1 #endif +/* Define how the architecture discriminates signaling NaNs. + * This done with the most significant bit of the fraction. + * In IEEE 754-1985 this was implementation defined, but in IEEE 754-2008 + * the msb must be zero. MIPS is (so far) unique in supporting both the + * 2008 revision and backward compatibility with their original choice. + * Thus for MIPS we must make the choice at runtime. + */ +static inline flag snan_bit_is_one(float_status *status) +{ +#if defined(TARGET_MIPS) + return status->snan_bit_is_one; +#elif defined(TARGET_HPPA) || defined(TARGET_UNICORE32) || defined(TARGET_SH4) + return 1; +#else + return 0; +#endif +} + /*---------------------------------------------------------------------------- | For the deconstructed floating-point with fraction FRAC, return true | if the fraction represents a signalling NaN; otherwise false. @@ -97,7 +115,7 @@ static bool parts_is_snan_frac(uint64_t frac, float_status *status) return false; #else flag msb = extract64(frac, DECOMPOSED_BINARY_POINT - 1, 1); - return msb == status->snan_bit_is_one; + return msb == snan_bit_is_one(status); #endif } @@ -118,7 +136,7 @@ static FloatParts parts_default_nan(float_status *status) #elif defined(TARGET_HPPA) frac = 1ULL << (DECOMPOSED_BINARY_POINT - 2); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { frac = (1ULL << (DECOMPOSED_BINARY_POINT - 1)) - 1; } else { #if defined(TARGET_MIPS) @@ -151,7 +169,7 @@ static FloatParts parts_silence_nan(FloatParts a, float_status *status) a.frac &= ~(1ULL << (DECOMPOSED_BINARY_POINT - 1)); a.frac |= 1ULL << (DECOMPOSED_BINARY_POINT - 2); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return parts_default_nan(status); } else { a.frac |= 1ULL << (DECOMPOSED_BINARY_POINT - 1); @@ -169,7 +187,7 @@ float16 float16_default_nan(float_status *status) #if defined(TARGET_ARM) return const_float16(0x7E00); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return const_float16(0x7DFF); } else { #if defined(TARGET_MIPS) @@ -195,7 +213,7 @@ float32 float32_default_nan(float_status *status) #elif defined(TARGET_HPPA) return const_float32(0x7FA00000); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return const_float32(0x7FBFFFFF); } else { #if defined(TARGET_MIPS) @@ -220,7 +238,7 @@ float64 float64_default_nan(float_status *status) #elif defined(TARGET_HPPA) return const_float64(LIT64(0x7FF4000000000000)); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return const_float64(LIT64(0x7FF7FFFFFFFFFFFF)); } else { #if defined(TARGET_MIPS) @@ -242,7 +260,7 @@ floatx80 floatx80_default_nan(float_status *status) r.low = LIT64(0xFFFFFFFFFFFFFFFF); r.high = 0x7FFF; #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { r.low = LIT64(0xBFFFFFFFFFFFFFFF); r.high = 0x7FFF; } else { @@ -274,7 +292,7 @@ float128 float128_default_nan(float_status *status) { float128 r; - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { r.low = LIT64(0xFFFFFFFFFFFFFFFF); r.high = LIT64(0x7FFF7FFFFFFFFFFF); } else { @@ -319,7 +337,7 @@ int float16_is_quiet_nan(float16 a_, float_status *status) return float16_is_any_nan(a_); #else uint16_t a = float16_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); } else { return ((a & ~0x8000) >= 0x7C80); @@ -338,7 +356,7 @@ int float16_is_signaling_nan(float16 a_, float_status *status) return 0; #else uint16_t a = float16_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return ((a & ~0x8000) >= 0x7C80); } else { return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); @@ -356,7 +374,7 @@ float16 float16_silence_nan(float16 a, float_status *status) #ifdef NO_SIGNALING_NANS g_assert_not_reached(); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return float16_default_nan(status); } else { return a | (1 << 9); @@ -375,7 +393,7 @@ int float32_is_quiet_nan(float32 a_, float_status *status) return float32_is_any_nan(a_); #else uint32_t a = float32_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return (((a >> 22) & 0x1FF) == 0x1FE) && (a & 0x003FFFFF); } else { return ((uint32_t)(a << 1) >= 0xFF800000); @@ -394,7 +412,7 @@ int float32_is_signaling_nan(float32 a_, float_status *status) return 0; #else uint32_t a = float32_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return ((uint32_t)(a << 1) >= 0xFF800000); } else { return (((a >> 22) & 0x1FF) == 0x1FE) && (a & 0x003FFFFF); @@ -412,7 +430,7 @@ float32 float32_silence_nan(float32 a, float_status *status) #ifdef NO_SIGNALING_NANS g_assert_not_reached(); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { # ifdef TARGET_HPPA a &= ~0x00400000; a |= 0x00200000; @@ -651,7 +669,7 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, return 3; } - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { /* Prefer sNaN over qNaN, in the a, b, c order. */ if (aIsSNaN) { return 0; @@ -786,7 +804,7 @@ int float64_is_quiet_nan(float64 a_, float_status *status) return float64_is_any_nan(a_); #else uint64_t a = float64_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return (((a >> 51) & 0xFFF) == 0xFFE) && (a & 0x0007FFFFFFFFFFFFULL); } else { @@ -806,7 +824,7 @@ int float64_is_signaling_nan(float64 a_, float_status *status) return 0; #else uint64_t a = float64_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return ((a << 1) >= 0xFFF0000000000000ULL); } else { return (((a >> 51) & 0xFFF) == 0xFFE) @@ -825,7 +843,7 @@ float64 float64_silence_nan(float64 a, float_status *status) #ifdef NO_SIGNALING_NANS g_assert_not_reached(); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { # ifdef TARGET_HPPA a &= ~0x0008000000000000ULL; a |= 0x0004000000000000ULL; @@ -942,7 +960,7 @@ int floatx80_is_quiet_nan(floatx80 a, float_status *status) #ifdef NO_SIGNALING_NANS return floatx80_is_any_nan(a); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { uint64_t aLow; aLow = a.low & ~0x4000000000000000ULL; @@ -967,7 +985,7 @@ int floatx80_is_signaling_nan(floatx80 a, float_status *status) #ifdef NO_SIGNALING_NANS return 0; #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return ((a.high & 0x7FFF) == 0x7FFF) && ((a.low << 1) >= 0x8000000000000000ULL); } else { @@ -991,7 +1009,7 @@ floatx80 floatx80_silence_nan(floatx80 a, float_status *status) #ifdef NO_SIGNALING_NANS g_assert_not_reached(); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return floatx80_default_nan(status); } else { a.low |= LIT64(0xC000000000000000); @@ -1105,7 +1123,7 @@ int float128_is_quiet_nan(float128 a, float_status *status) #ifdef NO_SIGNALING_NANS return float128_is_any_nan(a); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return (((a.high >> 47) & 0xFFFF) == 0xFFFE) && (a.low || (a.high & 0x00007FFFFFFFFFFFULL)); } else { @@ -1125,7 +1143,7 @@ int float128_is_signaling_nan(float128 a, float_status *status) #ifdef NO_SIGNALING_NANS return 0; #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return ((a.high << 1) >= 0xFFFF000000000000ULL) && (a.low || (a.high & 0x0000FFFFFFFFFFFFULL)); } else { @@ -1145,7 +1163,7 @@ float128 float128_silence_nan(float128 a, float_status *status) #ifdef NO_SIGNALING_NANS g_assert_not_reached(); #else - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return float128_default_nan(status); } else { a.high |= LIT64(0x0000800000000000); diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 4e378cb612..2aae6a89b1 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -173,6 +173,7 @@ typedef struct float_status { /* should denormalised inputs go to zero and set the input_denormal flag? */ flag flush_inputs_to_zero; flag default_nan_mode; + /* not always used -- see snan_bit_is_one() in softfloat-specialize.h */ flag snan_bit_is_one; } float_status; diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index c261b6b090..00bf444620 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -141,7 +141,6 @@ static void hppa_cpu_initfn(Object *obj) cs->env_ptr = env; cs->exception_index = -1; cpu_hppa_loaded_fr0(env); - set_snan_bit_is_one(true, &env->fp_status); cpu_hppa_put_psw(env, PSW_W); } diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 9ae418a577..d31a933cbb 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -3382,7 +3382,6 @@ void helper_xssqrtqp(CPUPPCState *env, uint32_t opcode) xt.f128 = xb.f128; } else if (float128_is_neg(xb.f128) && !float128_is_zero(xb.f128)) { float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); - set_snan_bit_is_one(0, &env->fp_status); xt.f128 = float128_default_nan(&env->fp_status); } } diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 541ffc2d97..b9f393b7c7 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -71,7 +71,6 @@ static void superh_cpu_reset(CPUState *s) set_flush_to_zero(1, &env->fp_status); #endif set_default_nan_mode(1, &env->fp_status); - set_snan_bit_is_one(1, &env->fp_status); } static void superh_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) diff --git a/target/unicore32/cpu.c b/target/unicore32/cpu.c index 29d160a88d..68f978d80b 100644 --- a/target/unicore32/cpu.c +++ b/target/unicore32/cpu.c @@ -70,7 +70,6 @@ static void unicore_ii_cpu_initfn(Object *obj) set_feature(env, UC32_HWCAP_CMOV); set_feature(env, UC32_HWCAP_UCF64); - set_snan_bit_is_one(1, &env->ucf64.fp_status); } static void uc32_any_cpu_initfn(Object *obj) @@ -83,7 +82,6 @@ static void uc32_any_cpu_initfn(Object *obj) set_feature(env, UC32_HWCAP_CMOV); set_feature(env, UC32_HWCAP_UCF64); - set_snan_bit_is_one(1, &env->ucf64.fp_status); } static void uc32_cpu_realizefn(DeviceState *dev, Error **errp) From 247d1f2190c5530fd18fe92a145d0a1985fca4e4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 10:36:25 -0700 Subject: [PATCH 0568/2380] fpu/softfloat: Make is_nan et al available to softfloat-specialize.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will need these helpers within softfloat-specialize.h, so move the definitions above the include. After specialization, they will not always be used so mark them to avoid the Werror. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 55e6701f26..ea252e0c84 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -181,6 +181,22 @@ typedef enum __attribute__ ((__packed__)) { float_class_snan, } FloatClass; +/* Simple helpers for checking if, or what kind of, NaN we have */ +static inline __attribute__((unused)) bool is_nan(FloatClass c) +{ + return unlikely(c >= float_class_qnan); +} + +static inline __attribute__((unused)) bool is_snan(FloatClass c) +{ + return c == float_class_snan; +} + +static inline __attribute__((unused)) bool is_qnan(FloatClass c) +{ + return c == float_class_qnan; +} + /* * Structure holding all of the decomposed parts of a float. The * exponent is unbiased and the fraction is normalized. All @@ -536,20 +552,6 @@ static float64 float64_round_pack_canonical(FloatParts p, float_status *s) return float64_pack_raw(round_canonical(p, s, &float64_params)); } -/* Simple helpers for checking if what NaN we have */ -static bool is_nan(FloatClass c) -{ - return unlikely(c >= float_class_qnan); -} -static bool is_snan(FloatClass c) -{ - return c == float_class_snan; -} -static bool is_qnan(FloatClass c) -{ - return c == float_class_qnan; -} - static FloatParts return_nan(FloatParts a, float_status *s) { switch (a.cls) { From 4f251cfd52c7945ebd6ab0d86518b1a9aa51b10c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 15:21:31 -0700 Subject: [PATCH 0569/2380] fpu/softfloat: Pass FloatClass to pickNaN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For each operand, pass a single enumeration instead of a pair of booleans. The commit also merges multiple different ifdef-selected implementations of pickNaN into a single function whose body is ifdef-selected. Tested-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 168 ++++++++++++++++++------------------- fpu/softfloat.c | 3 +- 2 files changed, 82 insertions(+), 89 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index d1e06da75b..2695183188 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -501,10 +501,10 @@ static float32 commonNaNToFloat32(commonNaNT a, float_status *status) | tie-break rule. *----------------------------------------------------------------------------*/ -#if defined(TARGET_ARM) -static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, +static int pickNaN(FloatClass a_cls, FloatClass b_cls, flag aIsLargerSignificand) { +#if defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_HPPA) /* ARM mandated NaN propagation rules (see FPProcessNaNs()), take * the first of: * 1. A if it is signaling @@ -513,20 +513,6 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, * 4. B (quiet) * A signaling NaN is always quietened before returning it. */ - if (aIsSNaN) { - return 0; - } else if (bIsSNaN) { - return 1; - } else if (aIsQNaN) { - return 0; - } else { - return 1; - } -} -#elif defined(TARGET_MIPS) || defined(TARGET_HPPA) -static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag aIsLargerSignificand) -{ /* According to MIPS specifications, if one of the two operands is * a sNaN, a new qNaN has to be generated. This is done in * floatXX_silence_nan(). For qNaN inputs the specifications @@ -540,35 +526,21 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, * 4. B (quiet) * A signaling NaN is always silenced before returning it. */ - if (aIsSNaN) { + if (is_snan(a_cls)) { return 0; - } else if (bIsSNaN) { + } else if (is_snan(b_cls)) { return 1; - } else if (aIsQNaN) { + } else if (is_qnan(a_cls)) { return 0; } else { return 1; } -} -#elif defined(TARGET_PPC) || defined(TARGET_XTENSA) -static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag aIsLargerSignificand) -{ +#elif defined(TARGET_PPC) || defined(TARGET_XTENSA) || defined(TARGET_M68K) /* PowerPC propagation rules: * 1. A if it sNaN or qNaN * 2. B if it sNaN or qNaN * A signaling NaN is always silenced before returning it. */ - if (aIsSNaN || aIsQNaN) { - return 0; - } else { - return 1; - } -} -#elif defined(TARGET_M68K) -static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag aIsLargerSignificand) -{ /* M68000 FAMILY PROGRAMMER'S REFERENCE MANUAL * 3.4 FLOATING-POINT INSTRUCTION DETAILS * If either operand, but not both operands, of an operation is a @@ -583,16 +555,12 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, * a nonsignaling NaN. The operation then continues as described in the * preceding paragraph for nonsignaling NaNs. */ - if (aIsQNaN || aIsSNaN) { /* a is the destination operand */ - return 0; /* return the destination operand */ + if (is_nan(a_cls)) { + return 0; } else { - return 1; /* return b */ + return 1; } -} #else -static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag aIsLargerSignificand) -{ /* This implements x87 NaN propagation rules: * SNaN + QNaN => return the QNaN * two SNaNs => return the one with the larger significand, silenced @@ -603,13 +571,13 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, * If we get down to comparing significands and they are the same, * return the NaN with the positive sign bit (if any). */ - if (aIsSNaN) { - if (bIsSNaN) { + if (is_snan(a_cls)) { + if (is_snan(b_cls)) { return aIsLargerSignificand ? 0 : 1; } - return bIsQNaN ? 1 : 0; - } else if (aIsQNaN) { - if (bIsSNaN || !bIsQNaN) { + return is_qnan(b_cls) ? 1 : 0; + } else if (is_qnan(a_cls)) { + if (is_snan(b_cls) || !is_qnan(b_cls)) { return 0; } else { return aIsLargerSignificand ? 0 : 1; @@ -617,8 +585,8 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, } else { return 1; } -} #endif +} /*---------------------------------------------------------------------------- | Select which NaN to propagate for a three-input operation. @@ -752,18 +720,26 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, static float32 propagateFloat32NaN(float32 a, float32 b, float_status *status) { - flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; uint32_t av, bv; + FloatClass a_cls, b_cls; + + /* This is not complete, but is good enough for pickNaN. */ + a_cls = (!float32_is_any_nan(a) + ? float_class_normal + : float32_is_signaling_nan(a, status) + ? float_class_snan + : float_class_qnan); + b_cls = (!float32_is_any_nan(b) + ? float_class_normal + : float32_is_signaling_nan(b, status) + ? float_class_snan + : float_class_qnan); - aIsQuietNaN = float32_is_quiet_nan(a, status); - aIsSignalingNaN = float32_is_signaling_nan(a, status); - bIsQuietNaN = float32_is_quiet_nan(b, status); - bIsSignalingNaN = float32_is_signaling_nan(b, status); av = float32_val(a); bv = float32_val(b); - if (aIsSignalingNaN | bIsSignalingNaN) { + if (is_snan(a_cls) || is_snan(b_cls)) { float_raise(float_flag_invalid, status); } @@ -779,14 +755,13 @@ static float32 propagateFloat32NaN(float32 a, float32 b, float_status *status) aIsLargerSignificand = (av < bv) ? 1 : 0; } - if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, - aIsLargerSignificand)) { - if (bIsSignalingNaN) { + if (pickNaN(a_cls, b_cls, aIsLargerSignificand)) { + if (is_snan(b_cls)) { return float32_silence_nan(b, status); } return b; } else { - if (aIsSignalingNaN) { + if (is_snan(a_cls)) { return float32_silence_nan(a, status); } return a; @@ -908,18 +883,26 @@ static float64 commonNaNToFloat64(commonNaNT a, float_status *status) static float64 propagateFloat64NaN(float64 a, float64 b, float_status *status) { - flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; uint64_t av, bv; + FloatClass a_cls, b_cls; + + /* This is not complete, but is good enough for pickNaN. */ + a_cls = (!float64_is_any_nan(a) + ? float_class_normal + : float64_is_signaling_nan(a, status) + ? float_class_snan + : float_class_qnan); + b_cls = (!float64_is_any_nan(b) + ? float_class_normal + : float64_is_signaling_nan(b, status) + ? float_class_snan + : float_class_qnan); - aIsQuietNaN = float64_is_quiet_nan(a, status); - aIsSignalingNaN = float64_is_signaling_nan(a, status); - bIsQuietNaN = float64_is_quiet_nan(b, status); - bIsSignalingNaN = float64_is_signaling_nan(b, status); av = float64_val(a); bv = float64_val(b); - if (aIsSignalingNaN | bIsSignalingNaN) { + if (is_snan(a_cls) || is_snan(b_cls)) { float_raise(float_flag_invalid, status); } @@ -935,14 +918,13 @@ static float64 propagateFloat64NaN(float64 a, float64 b, float_status *status) aIsLargerSignificand = (av < bv) ? 1 : 0; } - if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, - aIsLargerSignificand)) { - if (bIsSignalingNaN) { + if (pickNaN(a_cls, b_cls, aIsLargerSignificand)) { + if (is_snan(b_cls)) { return float64_silence_nan(b, status); } return b; } else { - if (aIsSignalingNaN) { + if (is_snan(a_cls)) { return float64_silence_nan(a, status); } return a; @@ -1075,15 +1057,22 @@ static floatx80 commonNaNToFloatx80(commonNaNT a, float_status *status) floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) { - flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; + FloatClass a_cls, b_cls; - aIsQuietNaN = floatx80_is_quiet_nan(a, status); - aIsSignalingNaN = floatx80_is_signaling_nan(a, status); - bIsQuietNaN = floatx80_is_quiet_nan(b, status); - bIsSignalingNaN = floatx80_is_signaling_nan(b, status); + /* This is not complete, but is good enough for pickNaN. */ + a_cls = (!floatx80_is_any_nan(a) + ? float_class_normal + : floatx80_is_signaling_nan(a, status) + ? float_class_snan + : float_class_qnan); + b_cls = (!floatx80_is_any_nan(b) + ? float_class_normal + : floatx80_is_signaling_nan(b, status) + ? float_class_snan + : float_class_qnan); - if (aIsSignalingNaN | bIsSignalingNaN) { + if (is_snan(a_cls) || is_snan(b_cls)) { float_raise(float_flag_invalid, status); } @@ -1099,14 +1088,13 @@ floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) aIsLargerSignificand = (a.high < b.high) ? 1 : 0; } - if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, - aIsLargerSignificand)) { - if (bIsSignalingNaN) { + if (pickNaN(a_cls, b_cls, aIsLargerSignificand)) { + if (is_snan(b_cls)) { return floatx80_silence_nan(b, status); } return b; } else { - if (aIsSignalingNaN) { + if (is_snan(a_cls)) { return floatx80_silence_nan(a, status); } return a; @@ -1217,15 +1205,22 @@ static float128 commonNaNToFloat128(commonNaNT a, float_status *status) static float128 propagateFloat128NaN(float128 a, float128 b, float_status *status) { - flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; + FloatClass a_cls, b_cls; - aIsQuietNaN = float128_is_quiet_nan(a, status); - aIsSignalingNaN = float128_is_signaling_nan(a, status); - bIsQuietNaN = float128_is_quiet_nan(b, status); - bIsSignalingNaN = float128_is_signaling_nan(b, status); + /* This is not complete, but is good enough for pickNaN. */ + a_cls = (!float128_is_any_nan(a) + ? float_class_normal + : float128_is_signaling_nan(a, status) + ? float_class_snan + : float_class_qnan); + b_cls = (!float128_is_any_nan(b) + ? float_class_normal + : float128_is_signaling_nan(b, status) + ? float_class_snan + : float_class_qnan); - if (aIsSignalingNaN | bIsSignalingNaN) { + if (is_snan(a_cls) || is_snan(b_cls)) { float_raise(float_flag_invalid, status); } @@ -1241,14 +1236,13 @@ static float128 propagateFloat128NaN(float128 a, float128 b, aIsLargerSignificand = (a.high < b.high) ? 1 : 0; } - if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, - aIsLargerSignificand)) { - if (bIsSignalingNaN) { + if (pickNaN(a_cls, b_cls, aIsLargerSignificand)) { + if (is_snan(b_cls)) { return float128_silence_nan(b, status); } return b; } else { - if (aIsSignalingNaN) { + if (is_snan(a_cls)) { return float128_silence_nan(a, status); } return a; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index ea252e0c84..55954385ff 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -580,8 +580,7 @@ static FloatParts pick_nan(FloatParts a, FloatParts b, float_status *s) if (s->default_nan_mode) { return parts_default_nan(s); } else { - if (pickNaN(is_qnan(a.cls), is_snan(a.cls), - is_qnan(b.cls), is_snan(b.cls), + if (pickNaN(a.cls, b.cls, a.frac > b.frac || (a.frac == b.frac && a.sign < b.sign))) { a = b; From 3bd2dec1a1e8fadb49e3ff2e2633f79e01a25c41 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 15:38:08 -0700 Subject: [PATCH 0570/2380] fpu/softfloat: Pass FloatClass to pickNaNMulAdd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For each operand, pass a single enumeration instead of a pair of booleans. The commit also merges multiple different ifdef-selected implementations of pickNaNMulAdd into a single function whose body is ifdef-selected. Tested-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 70 +++++++++++++++----------------------- fpu/softfloat.c | 5 +-- 2 files changed, 28 insertions(+), 47 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 2695183188..0399dfe011 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -594,15 +594,14 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls, | information. | Return values : 0 : a; 1 : b; 2 : c; 3 : default-NaN *----------------------------------------------------------------------------*/ -#if defined(TARGET_ARM) -static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero, - float_status *status) +static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, + bool infzero, float_status *status) { +#if defined(TARGET_ARM) /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns * the default NaN */ - if (infzero && cIsQNaN) { + if (infzero && is_qnan(c_cls)) { float_raise(float_flag_invalid, status); return 3; } @@ -610,25 +609,20 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, /* This looks different from the ARM ARM pseudocode, because the ARM ARM * puts the operands to a fused mac operation (a*b)+c in the order c,a,b. */ - if (cIsSNaN) { + if (is_snan(c_cls)) { return 2; - } else if (aIsSNaN) { + } else if (is_snan(a_cls)) { return 0; - } else if (bIsSNaN) { + } else if (is_snan(b_cls)) { return 1; - } else if (cIsQNaN) { + } else if (is_qnan(c_cls)) { return 2; - } else if (aIsQNaN) { + } else if (is_qnan(a_cls)) { return 0; } else { return 1; } -} #elif defined(TARGET_MIPS) -static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero, - float_status *status) -{ /* For MIPS, the (inf,zero,qnan) case sets InvalidOp and returns * the default NaN */ @@ -639,41 +633,36 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, if (snan_bit_is_one(status)) { /* Prefer sNaN over qNaN, in the a, b, c order. */ - if (aIsSNaN) { + if (is_snan(a_cls)) { return 0; - } else if (bIsSNaN) { + } else if (is_snan(b_cls)) { return 1; - } else if (cIsSNaN) { + } else if (is_snan(c_cls)) { return 2; - } else if (aIsQNaN) { + } else if (is_qnan(a_cls)) { return 0; - } else if (bIsQNaN) { + } else if (is_qnan(b_cls)) { return 1; } else { return 2; } } else { /* Prefer sNaN over qNaN, in the c, a, b order. */ - if (cIsSNaN) { + if (is_snan(c_cls)) { return 2; - } else if (aIsSNaN) { + } else if (is_snan(a_cls)) { return 0; - } else if (bIsSNaN) { + } else if (is_snan(b_cls)) { return 1; - } else if (cIsQNaN) { + } else if (is_qnan(c_cls)) { return 2; - } else if (aIsQNaN) { + } else if (is_qnan(a_cls)) { return 0; } else { return 1; } } -} #elif defined(TARGET_PPC) -static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero, - float_status *status) -{ /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer * to return an input NaN if we have one (ie c) rather than generating * a default NaN @@ -686,31 +675,26 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, /* If fRA is a NaN return it; otherwise if fRB is a NaN return it; * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB */ - if (aIsSNaN || aIsQNaN) { + if (is_nan(a_cls)) { return 0; - } else if (cIsSNaN || cIsQNaN) { + } else if (is_nan(c_cls)) { return 2; } else { return 1; } -} #else -/* A default implementation: prefer a to b to c. - * This is unlikely to actually match any real implementation. - */ -static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero, - float_status *status) -{ - if (aIsSNaN || aIsQNaN) { + /* A default implementation: prefer a to b to c. + * This is unlikely to actually match any real implementation. + */ + if (is_nan(a_cls)) { return 0; - } else if (bIsSNaN || bIsQNaN) { + } else if (is_nan(b_cls)) { return 1; } else { return 2; } -} #endif +} /*---------------------------------------------------------------------------- | Takes two single-precision floating-point values `a' and `b', one of which diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 55954385ff..8e97602ace 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -601,10 +601,7 @@ static FloatParts pick_nan_muladd(FloatParts a, FloatParts b, FloatParts c, s->float_exception_flags |= float_flag_invalid; } - which = pickNaNMulAdd(is_qnan(a.cls), is_snan(a.cls), - is_qnan(b.cls), is_snan(b.cls), - is_qnan(c.cls), is_snan(c.cls), - inf_zero, s); + which = pickNaNMulAdd(a.cls, b.cls, c.cls, inf_zero, s); if (s->default_nan_mode) { /* Note that this check is after pickNaNMulAdd so that function From 0218a16e540ad416683e19dfbd52f75092507b27 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 May 2018 13:56:44 -0700 Subject: [PATCH 0571/2380] fpu/softfloat: Define floatN_default_nan in terms of parts_default_nan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Isolate the target-specific choice to 2 functions instead of 6. The code in float16_default_nan was only correct for ARM, MIPS, and X86. Though float16 support is rare among our targets. The code in float128_default_nan was arguably wrong for Sparc. While QEMU supports the Sparc 128-bit insns, no real cpu enables it. The code in floatx80_default_nan tried to be over-general. There are only two targets that support this format: x86 and m68k. Thus there is no point in inventing a value for snan_bit_is_one. Move routines that no longer have ifdefs out of softfloat-specialize.h. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 105 +++---------------------------------- fpu/softfloat.c | 41 +++++++++++++++ 2 files changed, 47 insertions(+), 99 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 0399dfe011..9d562ed504 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -179,94 +179,22 @@ static FloatParts parts_silence_nan(FloatParts a, float_status *status) return a; } -/*---------------------------------------------------------------------------- -| The pattern for a default generated half-precision NaN. -*----------------------------------------------------------------------------*/ -float16 float16_default_nan(float_status *status) -{ -#if defined(TARGET_ARM) - return const_float16(0x7E00); -#else - if (snan_bit_is_one(status)) { - return const_float16(0x7DFF); - } else { -#if defined(TARGET_MIPS) - return const_float16(0x7E00); -#else - return const_float16(0xFE00); -#endif - } -#endif -} - -/*---------------------------------------------------------------------------- -| The pattern for a default generated single-precision NaN. -*----------------------------------------------------------------------------*/ -float32 float32_default_nan(float_status *status) -{ -#if defined(TARGET_SPARC) || defined(TARGET_M68K) - return const_float32(0x7FFFFFFF); -#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_XTENSA) || defined(TARGET_S390X) || \ - defined(TARGET_TRICORE) || defined(TARGET_RISCV) - return const_float32(0x7FC00000); -#elif defined(TARGET_HPPA) - return const_float32(0x7FA00000); -#else - if (snan_bit_is_one(status)) { - return const_float32(0x7FBFFFFF); - } else { -#if defined(TARGET_MIPS) - return const_float32(0x7FC00000); -#else - return const_float32(0xFFC00000); -#endif - } -#endif -} - -/*---------------------------------------------------------------------------- -| The pattern for a default generated double-precision NaN. -*----------------------------------------------------------------------------*/ -float64 float64_default_nan(float_status *status) -{ -#if defined(TARGET_SPARC) || defined(TARGET_M68K) - return const_float64(LIT64(0x7FFFFFFFFFFFFFFF)); -#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_S390X) || defined(TARGET_RISCV) - return const_float64(LIT64(0x7FF8000000000000)); -#elif defined(TARGET_HPPA) - return const_float64(LIT64(0x7FF4000000000000)); -#else - if (snan_bit_is_one(status)) { - return const_float64(LIT64(0x7FF7FFFFFFFFFFFF)); - } else { -#if defined(TARGET_MIPS) - return const_float64(LIT64(0x7FF8000000000000)); -#else - return const_float64(LIT64(0xFFF8000000000000)); -#endif - } -#endif -} - /*---------------------------------------------------------------------------- | The pattern for a default generated extended double-precision NaN. *----------------------------------------------------------------------------*/ floatx80 floatx80_default_nan(float_status *status) { floatx80 r; + + /* None of the targets that have snan_bit_is_one use floatx80. */ + assert(!snan_bit_is_one(status)); #if defined(TARGET_M68K) r.low = LIT64(0xFFFFFFFFFFFFFFFF); r.high = 0x7FFF; #else - if (snan_bit_is_one(status)) { - r.low = LIT64(0xBFFFFFFFFFFFFFFF); - r.high = 0x7FFF; - } else { - r.low = LIT64(0xC000000000000000); - r.high = 0xFFFF; - } + /* X86 */ + r.low = LIT64(0xC000000000000000); + r.high = 0xFFFF; #endif return r; } @@ -285,27 +213,6 @@ floatx80 floatx80_default_nan(float_status *status) const floatx80 floatx80_infinity = make_floatx80_init(floatx80_infinity_high, floatx80_infinity_low); -/*---------------------------------------------------------------------------- -| The pattern for a default generated quadruple-precision NaN. -*----------------------------------------------------------------------------*/ -float128 float128_default_nan(float_status *status) -{ - float128 r; - - if (snan_bit_is_one(status)) { - r.low = LIT64(0xFFFFFFFFFFFFFFFF); - r.high = LIT64(0x7FFF7FFFFFFFFFFF); - } else { - r.low = LIT64(0x0000000000000000); -#if defined(TARGET_S390X) || defined(TARGET_PPC) || defined(TARGET_RISCV) - r.high = LIT64(0x7FFF800000000000); -#else - r.high = LIT64(0xFFFF800000000000); -#endif - } - return r; -} - /*---------------------------------------------------------------------------- | Raises the exceptions specified by `flags'. Floating-point traps can be | defined here if desired. It is currently not possible for such a trap diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 8e97602ace..c8b33e35f4 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -2092,6 +2092,47 @@ float64 __attribute__((flatten)) float64_sqrt(float64 a, float_status *status) return float64_round_pack_canonical(pr, status); } +/*---------------------------------------------------------------------------- +| The pattern for a default generated NaN. +*----------------------------------------------------------------------------*/ + +float16 float16_default_nan(float_status *status) +{ + FloatParts p = parts_default_nan(status); + p.frac >>= float16_params.frac_shift; + return float16_pack_raw(p); +} + +float32 float32_default_nan(float_status *status) +{ + FloatParts p = parts_default_nan(status); + p.frac >>= float32_params.frac_shift; + return float32_pack_raw(p); +} + +float64 float64_default_nan(float_status *status) +{ + FloatParts p = parts_default_nan(status); + p.frac >>= float64_params.frac_shift; + return float64_pack_raw(p); +} + +float128 float128_default_nan(float_status *status) +{ + FloatParts p = parts_default_nan(status); + float128 r; + + /* Extrapolate from the choices made by parts_default_nan to fill + * in the quad-floating format. If the low bit is set, assume we + * want to set all non-snan bits. + */ + r.low = -(p.frac & 1); + r.high = p.frac >> (DECOMPOSED_BINARY_POINT - 48); + r.high |= LIT64(0x7FFF000000000000); + r.high |= (uint64_t)p.sign << 63; + + return r; +} /*---------------------------------------------------------------------------- | Takes a 64-bit fixed-point value `absZ' with binary point between bits 6 From 8fb3d90203f328d1bebcf7f20934027bfc4e7f3f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 May 2018 13:12:14 -0700 Subject: [PATCH 0572/2380] fpu/softfloat: Clean up parts_default_nan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the number of ifdefs. Correct the result for OpenRISC and TriCore (although TriCore fixed in target-specific code). Tested-by: Alex Bennée Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 9d562ed504..ec4fb6ba8b 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -129,22 +129,29 @@ static FloatParts parts_default_nan(float_status *status) uint64_t frac; #if defined(TARGET_SPARC) || defined(TARGET_M68K) + /* !snan_bit_is_one, set all bits */ frac = (1ULL << DECOMPOSED_BINARY_POINT) - 1; -#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_S390X) || defined(TARGET_RISCV) +#elif defined(TARGET_I386) || defined(TARGET_X86_64) \ + || defined(TARGET_MICROBLAZE) + /* !snan_bit_is_one, set sign and msb */ frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); + sign = 1; #elif defined(TARGET_HPPA) + /* snan_bit_is_one, set msb-1. */ frac = 1ULL << (DECOMPOSED_BINARY_POINT - 2); #else + /* This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V, + * S390, SH4, TriCore, and Xtensa. I cannot find documentation + * for Unicore32; the choice from the original commit is unchanged. + * Our other supported targets, CRIS, LM32, Moxie, Nios2, and Tile, + * do not have floating-point. + */ if (snan_bit_is_one(status)) { + /* set all bits other than msb */ frac = (1ULL << (DECOMPOSED_BINARY_POINT - 1)) - 1; } else { -#if defined(TARGET_MIPS) + /* set msb */ frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); -#else - frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); - sign = 1; -#endif } #endif From 377ed92679a2a5f838bc0a095112ea5020720fff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 May 2018 14:26:38 -0700 Subject: [PATCH 0573/2380] fpu/softfloat: Define floatN_silence_nan in terms of parts_silence_nan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Isolate the target-specific choice to 3 functions instead of 6. The code in floatx80_default_nan tried to be over-general. There are only two targets that support this format: x86 and m68k. Thus there is no point in inventing a mechanism for snan_bit_is_one. Move routines that no longer have ifdefs out of softfloat-specialize.h. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 81 ++------------------------------------ fpu/softfloat.c | 31 +++++++++++++++ 2 files changed, 35 insertions(+), 77 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index ec4fb6ba8b..16c0bcb6fa 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -278,24 +278,6 @@ int float16_is_signaling_nan(float16 a_, float_status *status) #endif } -/*---------------------------------------------------------------------------- -| Returns a quiet NaN from a signalling NaN for the half-precision -| floating point value `a'. -*----------------------------------------------------------------------------*/ - -float16 float16_silence_nan(float16 a, float_status *status) -{ -#ifdef NO_SIGNALING_NANS - g_assert_not_reached(); -#else - if (snan_bit_is_one(status)) { - return float16_default_nan(status); - } else { - return a | (1 << 9); - } -#endif -} - /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -334,30 +316,6 @@ int float32_is_signaling_nan(float32 a_, float_status *status) #endif } -/*---------------------------------------------------------------------------- -| Returns a quiet NaN from a signalling NaN for the single-precision -| floating point value `a'. -*----------------------------------------------------------------------------*/ - -float32 float32_silence_nan(float32 a, float_status *status) -{ -#ifdef NO_SIGNALING_NANS - g_assert_not_reached(); -#else - if (snan_bit_is_one(status)) { -# ifdef TARGET_HPPA - a &= ~0x00400000; - a |= 0x00200000; - return a; -# else - return float32_default_nan(status); -# endif - } else { - return a | (1 << 22); - } -#endif -} - /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid @@ -706,31 +664,6 @@ int float64_is_signaling_nan(float64 a_, float_status *status) #endif } -/*---------------------------------------------------------------------------- -| Returns a quiet NaN from a signalling NaN for the double-precision -| floating point value `a'. -*----------------------------------------------------------------------------*/ - -float64 float64_silence_nan(float64 a, float_status *status) -{ -#ifdef NO_SIGNALING_NANS - g_assert_not_reached(); -#else - if (snan_bit_is_one(status)) { -# ifdef TARGET_HPPA - a &= ~0x0008000000000000ULL; - a |= 0x0004000000000000ULL; - return a; -# else - return float64_default_nan(status); -# endif - } else { - return a | LIT64(0x0008000000000000); - } -#endif -} - - /*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid @@ -886,16 +819,10 @@ int floatx80_is_signaling_nan(floatx80 a, float_status *status) floatx80 floatx80_silence_nan(floatx80 a, float_status *status) { -#ifdef NO_SIGNALING_NANS - g_assert_not_reached(); -#else - if (snan_bit_is_one(status)) { - return floatx80_default_nan(status); - } else { - a.low |= LIT64(0xC000000000000000); - return a; - } -#endif + /* None of the targets that have snan_bit_is_one use floatx80. */ + assert(!snan_bit_is_one(status)); + a.low |= LIT64(0xC000000000000000); + return a; } /*---------------------------------------------------------------------------- diff --git a/fpu/softfloat.c b/fpu/softfloat.c index c8b33e35f4..8cd2400081 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -2134,6 +2134,37 @@ float128 float128_default_nan(float_status *status) return r; } +/*---------------------------------------------------------------------------- +| Returns a quiet NaN from a signalling NaN for the floating point value `a'. +*----------------------------------------------------------------------------*/ + +float16 float16_silence_nan(float16 a, float_status *status) +{ + FloatParts p = float16_unpack_raw(a); + p.frac <<= float16_params.frac_shift; + p = parts_silence_nan(p, status); + p.frac >>= float16_params.frac_shift; + return float16_pack_raw(p); +} + +float32 float32_silence_nan(float32 a, float_status *status) +{ + FloatParts p = float32_unpack_raw(a); + p.frac <<= float32_params.frac_shift; + p = parts_silence_nan(p, status); + p.frac >>= float32_params.frac_shift; + return float32_pack_raw(p); +} + +float64 float64_silence_nan(float64 a, float_status *status) +{ + FloatParts p = float64_unpack_raw(a); + p.frac <<= float64_params.frac_shift; + p = parts_silence_nan(p, status); + p.frac >>= float64_params.frac_shift; + return float64_pack_raw(p); +} + /*---------------------------------------------------------------------------- | Takes a 64-bit fixed-point value `absZ' with binary point between bits 6 | and 7, and returns the properly rounded 32-bit integer corresponding to the From 08d9864fa4e0c616e076ca8b225d39a7ecb189af Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Thu, 17 May 2018 17:00:11 +0200 Subject: [PATCH 0574/2380] console: Avoid segfault in screendump After f771c5440e04626f1 it is possible to select device and head which to take screendump from. And even though we check if provided head number falls within range, it may still happen that the console has no surface yet leading to SIGSEGV: qemu.git $ ./x86_64-softmmu/qemu-system-x86_64 \ -qmp stdio \ -device virtio-vga,id=video0,max_outputs=4 {"execute":"qmp_capabilities"} {"execute":"screendump", "arguments":{"filename":"/tmp/screen.ppm", "device":"video0", "head":1}} Segmentation fault #0 0x00005628249dda88 in ppm_save (filename=0x56282826cbc0 "/tmp/screen.ppm", ds=0x0, errp=0x7fff52a6fae0) at ui/console.c:304 #1 0x00005628249ddd9b in qmp_screendump (filename=0x56282826cbc0 "/tmp/screen.ppm", has_device=true, device=0x5628276902d0 "video0", has_head=true, head=1, errp=0x7fff52a6fae0) at ui/console.c:375 #2 0x00005628247740df in qmp_marshal_screendump (args=0x562828265e00, ret=0x7fff52a6fb68, errp=0x7fff52a6fb60) at qapi/qapi-commands-ui.c:110 Here, @ds from frame #0 (or @surface from frame #1) is dereferenced at the very beginning of ppm_save(). And because it's NULL crash happens. Signed-off-by: Michal Privoznik Reviewed-by: Thomas Huth Message-id: cb05bb1909daa6ba62145c0194aafa05a14ed3d1.1526569138.git.mprivozn@redhat.com Signed-off-by: Gerd Hoffmann --- ui/console.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/console.c b/ui/console.c index 945f05d728..ef1247f872 100644 --- a/ui/console.c +++ b/ui/console.c @@ -372,6 +372,11 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, graphic_hw_update(con); surface = qemu_console_surface(con); + if (!surface) { + error_setg(errp, "no surface"); + return; + } + ppm_save(filename, surface, errp); } From 68898bc82bcb0e697ed03c2405321033ba7feaf7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 17 May 2018 14:39:42 +0200 Subject: [PATCH 0575/2380] ui: add x_keymap.o to modules x_keymap.o is common to the SDL and GTK+ modules, and it causes the QEMU binary to link to the X11 libraries. Add it separately to the modules to keep the main QEMU binary smaller. Signed-off-by: Paolo Bonzini Message-id: 1526560782-18732-1-git-send-email-pbonzini@redhat.com [ kraxel: fix lm32 target build (milkymist-tmu2) ] Signed-off-by: Gerd Hoffmann --- hw/display/Makefile.objs | 2 ++ ui/Makefile.objs | 11 +++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 3c7c75b94d..11321e466b 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -20,6 +20,8 @@ common-obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o common-obj-$(CONFIG_ZAURUS) += tc6393xb.o common-obj-$(CONFIG_MILKYMIST_TMU2) += milkymist-tmu2.o +milkymist-tmu2.o-cflags := $(X11_CFLAGS) +milkymist-tmu2.o-libs := $(X11_LIBS) obj-$(CONFIG_OMAP) += omap_dss.o obj-$(CONFIG_OMAP) += omap_lcdc.o diff --git a/ui/Makefile.objs b/ui/Makefile.objs index cc784346cb..00f6976c30 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -15,10 +15,6 @@ common-obj-$(CONFIG_COCOA) += cocoa.o common-obj-$(CONFIG_VNC) += $(vnc-obj-y) common-obj-$(call lnot,$(CONFIG_VNC)) += vnc-stubs.o -common-obj-$(CONFIG_X11) += x_keymap.o -x_keymap.o-cflags := $(X11_CFLAGS) -x_keymap.o-libs := $(X11_LIBS) - # ui-sdl module common-obj-$(CONFIG_SDL) += sdl.mo ifeq ($(CONFIG_SDLABI),1.2) @@ -46,6 +42,13 @@ gtk.mo-objs += gtk-gl-area.o endif endif +ifeq ($(CONFIG_X11),y) +sdl.mo-objs += x_keymap.o +gtk.mo-objs += x_keymap.o +x_keymap.o-cflags := $(X11_CFLAGS) +x_keymap.o-libs := $(X11_LIBS) +endif + common-obj-$(CONFIG_CURSES) += curses.mo curses.mo-objs := curses.o curses.mo-cflags := $(CURSES_CFLAGS) From e8dcb8ae5121965ac8c89e6b277ac127e9d08452 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 May 2018 19:58:14 +0100 Subject: [PATCH 0576/2380] sdl: Move use of surface pointer below check for whether it is NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 2ab858c6c38ee1 we added a use of the 'surf' variable in sdl2_2d_update() that was unfortunately placed above the early-exit-if-NULL check. Move it to where it ought to be. Fixes: Coverity CID 1390598 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180515185814.1374-1-peter.maydell@linaro.org Signed-off-by: Gerd Hoffmann --- ui/sdl2-2d.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/sdl2-2d.c b/ui/sdl2-2d.c index 1f34817bae..85484407be 100644 --- a/ui/sdl2-2d.c +++ b/ui/sdl2-2d.c @@ -36,9 +36,7 @@ void sdl2_2d_update(DisplayChangeListener *dcl, struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); DisplaySurface *surf = qemu_console_surface(dcl->con); SDL_Rect rect; - size_t surface_data_offset = surface_bytes_per_pixel(surf) * x + - surface_stride(surf) * y; - + size_t surface_data_offset; assert(!scon->opengl); if (!surf) { @@ -48,6 +46,8 @@ void sdl2_2d_update(DisplayChangeListener *dcl, return; } + surface_data_offset = surface_bytes_per_pixel(surf) * x + + surface_stride(surf) * y; rect.x = x; rect.y = y; rect.w = w; From e58d64a16abc2304c4dcb644411eb9580bf63b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 15 May 2018 17:30:39 +0200 Subject: [PATCH 0577/2380] ccid-card-passthru: fix regression in realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since cc847bfd16d894fd8c1a2ce25f31772f6cdbbc74, CCID card-passthru fails to intialize, because it changed a debug line to an error, probably by mistake. Change it back to a DPRINTF debug. (solves Boxes creating VM with smartcard passthru failing to start) Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180515153039.27514-1-marcandre.lureau@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/ccid-card-passthru.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index 7684db0cb3..25fb19b0d7 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -345,7 +345,7 @@ static void passthru_realize(CCIDCardState *base, Error **errp) card->vscard_in_pos = 0; card->vscard_in_hdr = 0; if (qemu_chr_fe_backend_connected(&card->cs)) { - error_setg(errp, "ccid-card-passthru: initing chardev"); + DPRINTF(card, D_INFO, "ccid-card-passthru: initing chardev"); qemu_chr_fe_set_handlers(&card->cs, ccid_card_vscard_can_read, ccid_card_vscard_read, From 8030dca376fa1bc4d8a6be7628196578f8783ab3 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Wed, 16 May 2018 13:55:44 +0200 Subject: [PATCH 0578/2380] hw/usb/dev-smartcard-reader: Handle 64 B USB packets The current code was not correctly handling 64 B (Max USB 1.1 payload size) packets and therefore preventing some of the messages from smart card to pass through to the guest. If the smart card in host responded with 34 B of data in APDU layer, the CCID headers added up to 64 B. The packet was send, but not correctly committed per USB specification (8.5.3.2 Variable-length Data Stage): > When all of the data structure is returned to the host, the function > should indicate that the Data stage is ended by returning a packet > that is shorter than the MaxPacketSize for the pipe. If the data > structure is an exact multiple of wMaxPacketSize for the pipe, the > function will return a zero-length packet to indicate the end of the > Data stage. This lead the guest applications to timeout while waiting for the rest of data (the emulation layer is answering with NAK until the timeout). This patch is checking the current maximum packet size and if the payload of this size is detected, the message buffer is not yet released. With the next call, the empty buffer is sent and the message buffer is finally released. Signed-off-by: Jakub Jelen Message-id: 20180516115544.3897-2-jjelen@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/dev-smartcard-reader.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index cabb564788..f7451923f4 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1064,7 +1064,8 @@ err: return; } -static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) +static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p, + unsigned int max_packet_size) { int len = 0; @@ -1072,10 +1073,13 @@ static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) if (s->current_bulk_in != NULL) { len = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, p->iov.size); - usb_packet_copy(p, s->current_bulk_in->data + - s->current_bulk_in->pos, len); + if (len) { + usb_packet_copy(p, s->current_bulk_in->data + + s->current_bulk_in->pos, len); + } s->current_bulk_in->pos += len; - if (s->current_bulk_in->pos == s->current_bulk_in->len) { + if (s->current_bulk_in->pos == s->current_bulk_in->len + && len != max_packet_size) { ccid_bulk_in_release(s); } } else { @@ -1107,7 +1111,7 @@ static void ccid_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: switch (p->ep->nr) { case CCID_BULK_IN_EP: - ccid_bulk_in_copy_to_guest(s, p); + ccid_bulk_in_copy_to_guest(s, p, dev->ep_ctl.max_packet_size); break; case CCID_INT_IN_EP: if (s->notify_slot_change) { From 9d5e546af0a8500c0bde312c8dea6037428c655d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 May 2018 11:43:58 -0300 Subject: [PATCH 0579/2380] docker: Fix trivial typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180508144358.13530-1-f4bug@amsat.org> Reviewed-by: Laurent Vivier Signed-off-by: Fam Zheng --- tests/docker/Makefile.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index de87341528..ef1a3e62eb 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -46,7 +46,7 @@ docker-image-%: $(DOCKER_FILES_DIR)/%.docker docker-image-debian-powerpc-cross: EXTRA_FILES:=$(SRC_PATH)/tests/docker/dockerfiles/debian-apt-fake.sh -# Enforce dependancies for composite images +# Enforce dependencies for composite images docker-image-debian: docker-image-debian9 docker-image-debian8-mxe: docker-image-debian8 docker-image-debian-amd64: docker-image-debian9 From cd0a6d2b2c3d5cbf82a868d8c6c80961bcda6db5 Mon Sep 17 00:00:00 2001 From: Jie Wang Date: Thu, 17 May 2018 08:42:43 +0800 Subject: [PATCH 0580/2380] iothread: fix epollfd leak in the process of delIOThread When we call addIOThread, the epollfd created in aio_context_setup, but not close it in the process of delIOThread, so the epollfd will leak. Reorder the code in aio_epoll_disable and reuse it. Signed-off-by: Jie Wang Message-Id: <1526517763-11108-1-git-send-email-wangjie88@huawei.com> Reviewed-by: Fam Zheng Reviewed-by: Peter Xu [Mention change to aio_epoll_disable in commit message. - Fam] Signed-off-by: Fam Zheng --- include/block/aio.h | 8 ++++++++ util/aio-posix.c | 13 ++++++++++--- util/aio-win32.c | 4 ++++ util/async.c | 1 + 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/block/aio.h b/include/block/aio.h index a1d6b9e249..ae6f354e6c 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -554,6 +554,14 @@ static inline bool in_aio_context_home_thread(AioContext *ctx) */ void aio_context_setup(AioContext *ctx); +/** + * aio_context_destroy: + * @ctx: the aio context + * + * Destroy the aio context. + */ +void aio_context_destroy(AioContext *ctx); + /** * aio_context_set_poll_params: * @ctx: the aio context diff --git a/util/aio-posix.c b/util/aio-posix.c index d8f0cb4af8..118bf5784b 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -45,11 +45,11 @@ struct AioHandler static void aio_epoll_disable(AioContext *ctx) { - ctx->epoll_available = false; - if (!ctx->epoll_enabled) { + ctx->epoll_enabled = false; + if (!ctx->epoll_available) { return; } - ctx->epoll_enabled = false; + ctx->epoll_available = false; close(ctx->epollfd); } @@ -713,6 +713,13 @@ void aio_context_setup(AioContext *ctx) #endif } +void aio_context_destroy(AioContext *ctx) +{ +#ifdef CONFIG_EPOLL_CREATE1 + aio_epoll_disable(ctx); +#endif +} + void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, int64_t grow, int64_t shrink, Error **errp) { diff --git a/util/aio-win32.c b/util/aio-win32.c index a67b00c6ad..e676a8d9b2 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -407,6 +407,10 @@ void aio_context_setup(AioContext *ctx) { } +void aio_context_destroy(AioContext *ctx) +{ +} + void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, int64_t grow, int64_t shrink, Error **errp) { diff --git a/util/async.c b/util/async.c index 4dd9d95a9e..03f62787f2 100644 --- a/util/async.c +++ b/util/async.c @@ -298,6 +298,7 @@ aio_ctx_finalize(GSource *source) qemu_rec_mutex_destroy(&ctx->lock); qemu_lockcnt_destroy(&ctx->list_lock); timerlistgroup_deinit(&ctx->tlg); + aio_context_destroy(ctx); } static GSourceFuncs aio_source_funcs = { From 1f16378718fa87d63f70d0797f4546a88d8e3dd7 Mon Sep 17 00:00:00 2001 From: Abdallah Bouassida Date: Fri, 18 May 2018 17:48:07 +0100 Subject: [PATCH 0581/2380] target/arm: Add "ARM_CP_NO_GDB" as a new bit field for ARMCPRegInfo type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a preparation for the coming feature of creating dynamically an XML description for the ARM sysregs. A register has ARM_CP_NO_GDB enabled will not be shown in the dynamic XML. This bit is enabled automatically when creating CP_ANY wildcard aliases. This bit could be enabled manually for any register we want to remove from the dynamic XML description. Signed-off-by: Abdallah Bouassida Reviewed-by: Peter Maydell Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 1524153386-3550-2-git-send-email-abdallah.bouassida@lauterbach.com Signed-off-by: Peter Maydell --- target/arm/cpu.h | 3 ++- target/arm/helper.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 3b086be570..c78ccabded 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1821,10 +1821,11 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) #define ARM_LAST_SPECIAL ARM_CP_DC_ZVA #define ARM_CP_FPU 0x1000 #define ARM_CP_SVE 0x2000 +#define ARM_CP_NO_GDB 0x4000 /* Used only as a terminator for ARMCPRegInfo lists */ #define ARM_CP_SENTINEL 0xffff /* Mask of only the flag bits in a type field */ -#define ARM_CP_FLAG_MASK 0x30ff +#define ARM_CP_FLAG_MASK 0x70ff /* Valid values for ARMCPRegInfo state field, indicating which of * the AArch32 and AArch64 execution states this register is visible in. diff --git a/target/arm/helper.c b/target/arm/helper.c index db8bbe52a6..118422b92c 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5678,7 +5678,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, if (((r->crm == CP_ANY) && crm != 0) || ((r->opc1 == CP_ANY) && opc1 != 0) || ((r->opc2 == CP_ANY) && opc2 != 0)) { - r2->type |= ARM_CP_ALIAS; + r2->type |= ARM_CP_ALIAS | ARM_CP_NO_GDB; } /* Check that raw accesses are either forbidden or handled. Note that From 9c513e786d85cc58b8ba56a482566f759e0835b6 Mon Sep 17 00:00:00 2001 From: Abdallah Bouassida Date: Fri, 18 May 2018 17:48:07 +0100 Subject: [PATCH 0582/2380] target/arm: Add "_S" suffix to the secure version of a sysreg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a preparation for the coming feature of creating dynamically an XML description for the ARM sysregs. Add "_S" suffix to the secure version of sysregs that have both S and NS views Replace (S) and (NS) by _S and _NS for the register that are manually defined, so all the registers follow the same convention. Signed-off-by: Abdallah Bouassida Reviewed-by: Peter Maydell Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 1524153386-3550-3-git-send-email-abdallah.bouassida@lauterbach.com Signed-off-by: Peter Maydell --- target/arm/helper.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 118422b92c..369292c8b0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -690,12 +690,12 @@ static const ARMCPRegInfo cp_reginfo[] = { * the secure register to be properly reset and migrated. There is also no * v8 EL1 version of the register so the non-secure instance stands alone. */ - { .name = "FCSEIDR(NS)", + { .name = "FCSEIDR", .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 0, .access = PL1_RW, .secure = ARM_CP_SECSTATE_NS, .fieldoffset = offsetof(CPUARMState, cp15.fcseidr_ns), .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, - { .name = "FCSEIDR(S)", + { .name = "FCSEIDR_S", .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 0, .access = PL1_RW, .secure = ARM_CP_SECSTATE_S, .fieldoffset = offsetof(CPUARMState, cp15.fcseidr_s), @@ -711,7 +711,7 @@ static const ARMCPRegInfo cp_reginfo[] = { .access = PL1_RW, .secure = ARM_CP_SECSTATE_NS, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, - { .name = "CONTEXTIDR(S)", .state = ARM_CP_STATE_AA32, + { .name = "CONTEXTIDR_S", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, .access = PL1_RW, .secure = ARM_CP_SECSTATE_S, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_s), @@ -1981,7 +1981,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { cp15.c14_timer[GTIMER_PHYS].ctl), .writefn = gt_phys_ctl_write, .raw_writefn = raw_write, }, - { .name = "CNTP_CTL(S)", + { .name = "CNTP_CTL_S", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, .secure = ARM_CP_SECSTATE_S, .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL1_RW | PL0_R, @@ -2020,7 +2020,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .accessfn = gt_ptimer_access, .readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write, }, - { .name = "CNTP_TVAL(S)", + { .name = "CNTP_TVAL_S", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, .secure = ARM_CP_SECSTATE_S, .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, @@ -2074,7 +2074,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .accessfn = gt_ptimer_access, .writefn = gt_phys_cval_write, .raw_writefn = raw_write, }, - { .name = "CNTP_CVAL(S)", .cp = 15, .crm = 14, .opc1 = 2, + { .name = "CNTP_CVAL_S", .cp = 15, .crm = 14, .opc1 = 2, .secure = ARM_CP_SECSTATE_S, .access = PL1_RW | PL0_R, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, @@ -5577,7 +5577,8 @@ CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, void *opaque, int state, int secstate, - int crm, int opc1, int opc2) + int crm, int opc1, int opc2, + const char *name) { /* Private utility function for define_one_arm_cp_reg_with_opaque(): * add a single reginfo struct to the hash table. @@ -5587,6 +5588,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; int ns = (secstate & ARM_CP_SECSTATE_NS) ? 1 : 0; + r2->name = g_strdup(name); /* Reset the secure state to the specific incoming state. This is * necessary as the register may have been defined with both states. */ @@ -5818,19 +5820,24 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, /* Under AArch32 CP registers can be common * (same for secure and non-secure world) or banked. */ + char *name; + switch (r->secure) { case ARM_CP_SECSTATE_S: case ARM_CP_SECSTATE_NS: add_cpreg_to_hashtable(cpu, r, opaque, state, - r->secure, crm, opc1, opc2); + r->secure, crm, opc1, opc2, + r->name); break; default: + name = g_strdup_printf("%s_S", r->name); add_cpreg_to_hashtable(cpu, r, opaque, state, ARM_CP_SECSTATE_S, - crm, opc1, opc2); + crm, opc1, opc2, name); + g_free(name); add_cpreg_to_hashtable(cpu, r, opaque, state, ARM_CP_SECSTATE_NS, - crm, opc1, opc2); + crm, opc1, opc2, r->name); break; } } else { @@ -5838,7 +5845,7 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, * of AArch32 */ add_cpreg_to_hashtable(cpu, r, opaque, state, ARM_CP_SECSTATE_NS, - crm, opc1, opc2); + crm, opc1, opc2, r->name); } } } From 200bf5b7ffea635079cc05fdfb363372b9544ce7 Mon Sep 17 00:00:00 2001 From: Abdallah Bouassida Date: Fri, 18 May 2018 17:48:07 +0100 Subject: [PATCH 0583/2380] target/arm: Add the XML dynamic generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generate an XML description for the cp-regs. Register these regs with the gdb_register_coprocessor(). Add arm_gdb_get_sysreg() to use it as a callback to read those regs. Add a dummy arm_gdb_set_sysreg(). Signed-off-by: Abdallah Bouassida Tested-by: Alex Bennée Message-id: 1524153386-3550-4-git-send-email-abdallah.bouassida@lauterbach.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- gdbstub.c | 10 ++++++ include/qom/cpu.h | 5 ++- target/arm/cpu.c | 1 + target/arm/cpu.h | 26 +++++++++++++++ target/arm/gdbstub.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ target/arm/helper.c | 26 +++++++++++++++ 6 files changed, 143 insertions(+), 1 deletion(-) diff --git a/gdbstub.c b/gdbstub.c index 3c3807358c..9682e16ef7 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -675,6 +675,16 @@ static const char *get_feature_xml(const char *p, const char **newp, } return target_xml; } + if (cc->gdb_get_dynamic_xml) { + CPUState *cpu = first_cpu; + char *xmlname = g_strndup(p, len); + const char *xml = cc->gdb_get_dynamic_xml(cpu, xmlname); + + g_free(xmlname); + if (xml) { + return xml; + } + } for (i = 0; ; i++) { name = xml_builtin[i][0]; if (!name || (strncmp(name, p, len) == 0 && strlen(name) == len)) diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 14e45c4282..9d3afc6c75 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -132,6 +132,9 @@ struct TranslationBlock; * before the insn which triggers a watchpoint rather than after it. * @gdb_arch_name: Optional callback that returns the architecture name known * to GDB. The caller must free the returned string with g_free. + * @gdb_get_dynamic_xml: Callback to return dynamically generated XML for the + * gdb stub. Returns a pointer to the XML contents for the specified XML file + * or NULL if the CPU doesn't have a dynamically generated content for it. * @cpu_exec_enter: Callback for cpu_exec preparation. * @cpu_exec_exit: Callback for cpu_exec cleanup. * @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec. @@ -198,7 +201,7 @@ typedef struct CPUClass { const struct VMStateDescription *vmsd; const char *gdb_core_xml_file; gchar * (*gdb_arch_name)(CPUState *cpu); - + const char * (*gdb_get_dynamic_xml)(CPUState *cpu, const char *xmlname); void (*cpu_exec_enter)(CPUState *cpu); void (*cpu_exec_exit)(CPUState *cpu); bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 7939c6b8ae..5d60893a07 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1908,6 +1908,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_num_core_regs = 26; cc->gdb_core_xml_file = "arm-core.xml"; cc->gdb_arch_name = arm_gdb_arch_name; + cc->gdb_get_dynamic_xml = arm_gdb_get_dynamic_xml; cc->gdb_stop_before_watchpoint = true; cc->debug_excp_handler = arm_debug_excp_handler; cc->debug_check_watchpoint = arm_debug_check_watchpoint; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c78ccabded..01281f5c56 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -133,6 +133,19 @@ enum { s<2n+1> maps to the most significant half of d */ +/** + * DynamicGDBXMLInfo: + * @desc: Contains the XML descriptions. + * @num_cpregs: Number of the Coprocessor registers seen by GDB. + * @cpregs_keys: Array that contains the corresponding Key of + * a given cpreg with the same order of the cpreg in the XML description. + */ +typedef struct DynamicGDBXMLInfo { + char *desc; + int num_cpregs; + uint32_t *cpregs_keys; +} DynamicGDBXMLInfo; + /* CPU state for each instance of a generic timer (in cp15 c14) */ typedef struct ARMGenericTimer { uint64_t cval; /* Timer CompareValue register */ @@ -687,6 +700,8 @@ struct ARMCPU { uint64_t *cpreg_vmstate_values; int32_t cpreg_vmstate_array_len; + DynamicGDBXMLInfo dyn_xml; + /* Timers used by the generic (architected) timer */ QEMUTimer *gt_timer[NUM_GTIMERS]; /* GPIO outputs for generic timer */ @@ -868,6 +883,17 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +/* Dynamically generates for gdb stub an XML description of the sysregs from + * the cp_regs hashtable. Returns the registered sysregs number. + */ +int arm_gen_dynamic_xml(CPUState *cpu); + +/* Returns the dynamically generated XML for the gdb stub. + * Returns a pointer to the XML contents for the specified XML file or NULL + * if the XML name doesn't match the predefined one. + */ +const char *arm_gdb_get_dynamic_xml(CPUState *cpu, const char *xmlname); + int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 04c1208d03..e80cfb47c7 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -22,6 +22,11 @@ #include "cpu.h" #include "exec/gdbstub.h" +typedef struct RegisterSysregXmlParam { + CPUState *cs; + GString *s; +} RegisterSysregXmlParam; + /* Old gdb always expect FPA registers. Newer (xml-aware) gdb only expect whatever the target description contains. Due to a historical mishap the FPA registers appear in between core integer regs and the CPSR. @@ -101,3 +106,74 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) /* Unknown register. */ return 0; } + +static void arm_gen_one_xml_reg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, + ARMCPRegInfo *ri, uint32_t ri_key, + int bitsize) +{ + g_string_append_printf(s, "name); + g_string_append_printf(s, " bitsize=\"%d\"", bitsize); + g_string_append_printf(s, " group=\"cp_regs\"/>"); + dyn_xml->num_cpregs++; + dyn_xml->cpregs_keys[dyn_xml->num_cpregs - 1] = ri_key; +} + +static void arm_register_sysreg_for_xml(gpointer key, gpointer value, + gpointer p) +{ + uint32_t ri_key = *(uint32_t *)key; + ARMCPRegInfo *ri = value; + RegisterSysregXmlParam *param = (RegisterSysregXmlParam *)p; + GString *s = param->s; + ARMCPU *cpu = ARM_CPU(param->cs); + CPUARMState *env = &cpu->env; + DynamicGDBXMLInfo *dyn_xml = &cpu->dyn_xml; + + if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_NO_GDB))) { + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + if (ri->state == ARM_CP_STATE_AA64) { + arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 64); + } + } else { + if (ri->state == ARM_CP_STATE_AA32) { + if (!arm_feature(env, ARM_FEATURE_EL3) && + (ri->secure & ARM_CP_SECSTATE_S)) { + return; + } + if (ri->type & ARM_CP_64BIT) { + arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 64); + } else { + arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 32); + } + } + } + } +} + +int arm_gen_dynamic_xml(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + GString *s = g_string_new(NULL); + RegisterSysregXmlParam param = {cs, s}; + + cpu->dyn_xml.num_cpregs = 0; + cpu->dyn_xml.cpregs_keys = g_malloc(sizeof(uint32_t *) * + g_hash_table_size(cpu->cp_regs)); + g_string_printf(s, ""); + g_string_append_printf(s, ""); + g_string_append_printf(s, ""); + g_hash_table_foreach(cpu->cp_regs, arm_register_sysreg_for_xml, ¶m); + g_string_append_printf(s, ""); + cpu->dyn_xml.desc = g_string_free(s, false); + return cpu->dyn_xml.num_cpregs; +} + +const char *arm_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname) +{ + ARMCPU *cpu = ARM_CPU(cs); + + if (strcmp(xmlname, "system-registers.xml") == 0) { + return cpu->dyn_xml.desc; + } + return NULL; +} diff --git a/target/arm/helper.c b/target/arm/helper.c index 369292c8b0..c0f739972e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -215,6 +215,29 @@ static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, } } +static int arm_gdb_get_sysreg(CPUARMState *env, uint8_t *buf, int reg) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + const ARMCPRegInfo *ri; + uint32_t key; + + key = cpu->dyn_xml.cpregs_keys[reg]; + ri = get_arm_cp_reginfo(cpu->cp_regs, key); + if (ri) { + if (cpreg_field_is_64bit(ri)) { + return gdb_get_reg64(buf, (uint64_t)read_raw_cp_reg(env, ri)); + } else { + return gdb_get_reg32(buf, (uint32_t)read_raw_cp_reg(env, ri)); + } + } + return 0; +} + +static int arm_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) +{ + return 0; +} + static bool raw_accessors_invalid(const ARMCPRegInfo *ri) { /* Return true if the regdef would cause an assertion if you called @@ -5488,6 +5511,9 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, 19, "arm-vfp.xml", 0); } + gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg, + arm_gen_dynamic_xml(cs), + "system-registers.xml", 0); } /* Sort alphabetically by type name, except for "any". */ From 22cd0945b8429f818a2d90f06f02bb526bfb319d Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 18 May 2018 17:48:07 +0100 Subject: [PATCH 0584/2380] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA Add a model of the generic DMA found on Xilinx ZynqMP. Signed-off-by: Francisco Iglesias Signed-off-by: Edgar E. Iglesias Reviewed-by: Edgar E. Iglesias Message-id: 20180503214201.29082-2-frasse.iglesias@gmail.com Signed-off-by: Peter Maydell --- hw/dma/Makefile.objs | 1 + hw/dma/xlnx-zdma.c | 832 +++++++++++++++++++++++++++++++++++++ include/hw/dma/xlnx-zdma.h | 84 ++++ 3 files changed, 917 insertions(+) create mode 100644 hw/dma/xlnx-zdma.c create mode 100644 include/hw/dma/xlnx-zdma.h diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs index c2afecbf73..79affecc39 100644 --- a/hw/dma/Makefile.objs +++ b/hw/dma/Makefile.objs @@ -10,6 +10,7 @@ common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o common-obj-$(CONFIG_STP2000) += sparc32_dma.o obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dpdma.o obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dpdma.o +common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zdma.o obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c new file mode 100644 index 0000000000..14d86c254b --- /dev/null +++ b/hw/dma/xlnx-zdma.c @@ -0,0 +1,832 @@ +/* + * QEMU model of the ZynqMP generic DMA + * + * Copyright (c) 2014 Xilinx Inc. + * Copyright (c) 2018 FEIMTECH AB + * + * Written by Edgar E. Iglesias , + * Francisco Iglesias + * + * 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 "qemu/osdep.h" +#include "hw/dma/xlnx-zdma.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qapi/error.h" + +#ifndef XLNX_ZDMA_ERR_DEBUG +#define XLNX_ZDMA_ERR_DEBUG 0 +#endif + +REG32(ZDMA_ERR_CTRL, 0x0) + FIELD(ZDMA_ERR_CTRL, APB_ERR_RES, 0, 1) +REG32(ZDMA_CH_ISR, 0x100) + FIELD(ZDMA_CH_ISR, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_ISR, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_ISR, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_ISR, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_ISR, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_ISR, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_ISR, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_ISR, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_ISR, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_ISR, INV_APB, 0, 1) +REG32(ZDMA_CH_IMR, 0x104) + FIELD(ZDMA_CH_IMR, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_IMR, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_IMR, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_IMR, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_IMR, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_IMR, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_IMR, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_IMR, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_IMR, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_IMR, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_IMR, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_IMR, INV_APB, 0, 1) +REG32(ZDMA_CH_IEN, 0x108) + FIELD(ZDMA_CH_IEN, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_IEN, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_IEN, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_IEN, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_IEN, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_IEN, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_IEN, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_IEN, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_IEN, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_IEN, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_IEN, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_IEN, INV_APB, 0, 1) +REG32(ZDMA_CH_IDS, 0x10c) + FIELD(ZDMA_CH_IDS, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_IDS, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_IDS, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_IDS, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_IDS, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_IDS, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_IDS, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_IDS, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_IDS, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_IDS, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_IDS, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_IDS, INV_APB, 0, 1) +REG32(ZDMA_CH_CTRL0, 0x110) + FIELD(ZDMA_CH_CTRL0, OVR_FETCH, 7, 1) + FIELD(ZDMA_CH_CTRL0, POINT_TYPE, 6, 1) + FIELD(ZDMA_CH_CTRL0, MODE, 4, 2) + FIELD(ZDMA_CH_CTRL0, RATE_CTRL, 3, 1) + FIELD(ZDMA_CH_CTRL0, CONT_ADDR, 2, 1) + FIELD(ZDMA_CH_CTRL0, CONT, 1, 1) +REG32(ZDMA_CH_CTRL1, 0x114) + FIELD(ZDMA_CH_CTRL1, DST_ISSUE, 5, 5) + FIELD(ZDMA_CH_CTRL1, SRC_ISSUE, 0, 5) +REG32(ZDMA_CH_FCI, 0x118) + FIELD(ZDMA_CH_FCI, PROG_CELL_CNT, 2, 2) + FIELD(ZDMA_CH_FCI, SIDE, 1, 1) + FIELD(ZDMA_CH_FCI, EN, 0, 1) +REG32(ZDMA_CH_STATUS, 0x11c) + FIELD(ZDMA_CH_STATUS, STATE, 0, 2) +REG32(ZDMA_CH_DATA_ATTR, 0x120) + FIELD(ZDMA_CH_DATA_ATTR, ARBURST, 26, 2) + FIELD(ZDMA_CH_DATA_ATTR, ARCACHE, 22, 4) + FIELD(ZDMA_CH_DATA_ATTR, ARQOS, 18, 4) + FIELD(ZDMA_CH_DATA_ATTR, ARLEN, 14, 4) + FIELD(ZDMA_CH_DATA_ATTR, AWBURST, 12, 2) + FIELD(ZDMA_CH_DATA_ATTR, AWCACHE, 8, 4) + FIELD(ZDMA_CH_DATA_ATTR, AWQOS, 4, 4) + FIELD(ZDMA_CH_DATA_ATTR, AWLEN, 0, 4) +REG32(ZDMA_CH_DSCR_ATTR, 0x124) + FIELD(ZDMA_CH_DSCR_ATTR, AXCOHRNT, 8, 1) + FIELD(ZDMA_CH_DSCR_ATTR, AXCACHE, 4, 4) + FIELD(ZDMA_CH_DSCR_ATTR, AXQOS, 0, 4) +REG32(ZDMA_CH_SRC_DSCR_WORD0, 0x128) +REG32(ZDMA_CH_SRC_DSCR_WORD1, 0x12c) + FIELD(ZDMA_CH_SRC_DSCR_WORD1, MSB, 0, 17) +REG32(ZDMA_CH_SRC_DSCR_WORD2, 0x130) + FIELD(ZDMA_CH_SRC_DSCR_WORD2, SIZE, 0, 30) +REG32(ZDMA_CH_SRC_DSCR_WORD3, 0x134) + FIELD(ZDMA_CH_SRC_DSCR_WORD3, CMD, 3, 2) + FIELD(ZDMA_CH_SRC_DSCR_WORD3, INTR, 2, 1) + FIELD(ZDMA_CH_SRC_DSCR_WORD3, TYPE, 1, 1) + FIELD(ZDMA_CH_SRC_DSCR_WORD3, COHRNT, 0, 1) +REG32(ZDMA_CH_DST_DSCR_WORD0, 0x138) +REG32(ZDMA_CH_DST_DSCR_WORD1, 0x13c) + FIELD(ZDMA_CH_DST_DSCR_WORD1, MSB, 0, 17) +REG32(ZDMA_CH_DST_DSCR_WORD2, 0x140) + FIELD(ZDMA_CH_DST_DSCR_WORD2, SIZE, 0, 30) +REG32(ZDMA_CH_DST_DSCR_WORD3, 0x144) + FIELD(ZDMA_CH_DST_DSCR_WORD3, INTR, 2, 1) + FIELD(ZDMA_CH_DST_DSCR_WORD3, TYPE, 1, 1) + FIELD(ZDMA_CH_DST_DSCR_WORD3, COHRNT, 0, 1) +REG32(ZDMA_CH_WR_ONLY_WORD0, 0x148) +REG32(ZDMA_CH_WR_ONLY_WORD1, 0x14c) +REG32(ZDMA_CH_WR_ONLY_WORD2, 0x150) +REG32(ZDMA_CH_WR_ONLY_WORD3, 0x154) +REG32(ZDMA_CH_SRC_START_LSB, 0x158) +REG32(ZDMA_CH_SRC_START_MSB, 0x15c) + FIELD(ZDMA_CH_SRC_START_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_DST_START_LSB, 0x160) +REG32(ZDMA_CH_DST_START_MSB, 0x164) + FIELD(ZDMA_CH_DST_START_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_RATE_CTRL, 0x18c) + FIELD(ZDMA_CH_RATE_CTRL, CNT, 0, 12) +REG32(ZDMA_CH_SRC_CUR_PYLD_LSB, 0x168) +REG32(ZDMA_CH_SRC_CUR_PYLD_MSB, 0x16c) + FIELD(ZDMA_CH_SRC_CUR_PYLD_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_DST_CUR_PYLD_LSB, 0x170) +REG32(ZDMA_CH_DST_CUR_PYLD_MSB, 0x174) + FIELD(ZDMA_CH_DST_CUR_PYLD_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_SRC_CUR_DSCR_LSB, 0x178) +REG32(ZDMA_CH_SRC_CUR_DSCR_MSB, 0x17c) + FIELD(ZDMA_CH_SRC_CUR_DSCR_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_DST_CUR_DSCR_LSB, 0x180) +REG32(ZDMA_CH_DST_CUR_DSCR_MSB, 0x184) + FIELD(ZDMA_CH_DST_CUR_DSCR_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_TOTAL_BYTE, 0x188) +REG32(ZDMA_CH_RATE_CNTL, 0x18c) + FIELD(ZDMA_CH_RATE_CNTL, CNT, 0, 12) +REG32(ZDMA_CH_IRQ_SRC_ACCT, 0x190) + FIELD(ZDMA_CH_IRQ_SRC_ACCT, CNT, 0, 8) +REG32(ZDMA_CH_IRQ_DST_ACCT, 0x194) + FIELD(ZDMA_CH_IRQ_DST_ACCT, CNT, 0, 8) +REG32(ZDMA_CH_DBG0, 0x198) + FIELD(ZDMA_CH_DBG0, CMN_BUF_FREE, 0, 9) +REG32(ZDMA_CH_DBG1, 0x19c) + FIELD(ZDMA_CH_DBG1, CMN_BUF_OCC, 0, 9) +REG32(ZDMA_CH_CTRL2, 0x200) + FIELD(ZDMA_CH_CTRL2, EN, 0, 1) + +enum { + PT_REG = 0, + PT_MEM = 1, +}; + +enum { + CMD_HALT = 1, + CMD_STOP = 2, +}; + +enum { + RW_MODE_RW = 0, + RW_MODE_WO = 1, + RW_MODE_RO = 2, +}; + +enum { + DTYPE_LINEAR = 0, + DTYPE_LINKED = 1, +}; + +enum { + AXI_BURST_FIXED = 0, + AXI_BURST_INCR = 1, +}; + +static void zdma_ch_imr_update_irq(XlnxZDMA *s) +{ + bool pending; + + pending = s->regs[R_ZDMA_CH_ISR] & ~s->regs[R_ZDMA_CH_IMR]; + + qemu_set_irq(s->irq_zdma_ch_imr, pending); +} + +static void zdma_ch_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + zdma_ch_imr_update_irq(s); +} + +static uint64_t zdma_ch_ien_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + uint32_t val = val64; + + s->regs[R_ZDMA_CH_IMR] &= ~val; + zdma_ch_imr_update_irq(s); + return 0; +} + +static uint64_t zdma_ch_ids_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + uint32_t val = val64; + + s->regs[R_ZDMA_CH_IMR] |= val; + zdma_ch_imr_update_irq(s); + return 0; +} + +static void zdma_set_state(XlnxZDMA *s, XlnxZDMAState state) +{ + s->state = state; + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, state); + + /* Signal error if we have an error condition. */ + if (s->error) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, 3); + } +} + +static void zdma_src_done(XlnxZDMA *s) +{ + unsigned int cnt; + cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT); + cnt++; + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT, cnt); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, SRC_DSCR_DONE, true); + + /* Did we overflow? */ + if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT)) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, true); + } + zdma_ch_imr_update_irq(s); +} + +static void zdma_dst_done(XlnxZDMA *s) +{ + unsigned int cnt; + cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT); + cnt++; + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT, cnt); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DST_DSCR_DONE, true); + + /* Did we overflow? */ + if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT)) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, true); + } + zdma_ch_imr_update_irq(s); +} + +static uint64_t zdma_get_regaddr64(XlnxZDMA *s, unsigned int basereg) +{ + uint64_t addr; + + addr = s->regs[basereg + 1]; + addr <<= 32; + addr |= s->regs[basereg]; + + return addr; +} + +static void zdma_put_regaddr64(XlnxZDMA *s, unsigned int basereg, uint64_t addr) +{ + s->regs[basereg] = addr; + s->regs[basereg + 1] = addr >> 32; +} + +static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf) +{ + /* ZDMA descriptors must be aligned to their own size. */ + if (addr % sizeof(XlnxZDMADescr)) { + qemu_log_mask(LOG_GUEST_ERROR, + "zdma: unaligned descriptor at %" PRIx64, + addr); + memset(buf, 0xdeadbeef, sizeof(XlnxZDMADescr)); + s->error = true; + return false; + } + + address_space_rw(s->dma_as, addr, s->attr, + buf, sizeof(XlnxZDMADescr), false); + return true; +} + +static void zdma_load_src_descriptor(XlnxZDMA *s) +{ + uint64_t src_addr; + unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); + + if (ptype == PT_REG) { + memcpy(&s->dsc_src, &s->regs[R_ZDMA_CH_SRC_DSCR_WORD0], + sizeof(s->dsc_src)); + return; + } + + src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB); + + if (!zdma_load_descriptor(s, src_addr, &s->dsc_src)) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_SRC_DSCR, true); + } +} + +static void zdma_load_dst_descriptor(XlnxZDMA *s) +{ + uint64_t dst_addr; + unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); + + if (ptype == PT_REG) { + memcpy(&s->dsc_dst, &s->regs[R_ZDMA_CH_DST_DSCR_WORD0], + sizeof(s->dsc_dst)); + return; + } + + dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB); + + if (!zdma_load_descriptor(s, dst_addr, &s->dsc_dst)) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_DST_DSCR, true); + } +} + +static uint64_t zdma_update_descr_addr(XlnxZDMA *s, bool type, + unsigned int basereg) +{ + uint64_t addr, next; + + if (type == DTYPE_LINEAR) { + next = zdma_get_regaddr64(s, basereg); + next += sizeof(s->dsc_dst); + zdma_put_regaddr64(s, basereg, next); + } else { + addr = zdma_get_regaddr64(s, basereg); + addr += sizeof(s->dsc_dst); + address_space_rw(s->dma_as, addr, s->attr, (void *) &next, 8, false); + zdma_put_regaddr64(s, basereg, next); + } + return next; +} + +static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len) +{ + uint32_t dst_size, dlen; + bool dst_intr, dst_type; + unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); + unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE); + unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR, + AWBURST); + + /* FIXED burst types are only supported in simple dma mode. */ + if (ptype != PT_REG) { + burst_type = AXI_BURST_INCR; + } + + while (len) { + dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2, + SIZE); + dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3, + TYPE); + if (dst_size == 0 && ptype == PT_MEM) { + uint64_t next; + next = zdma_update_descr_addr(s, dst_type, + R_ZDMA_CH_DST_CUR_DSCR_LSB); + zdma_load_descriptor(s, next, &s->dsc_dst); + dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2, + SIZE); + dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3, + TYPE); + } + + /* Match what hardware does by ignoring the dst_size and only using + * the src size for Simple register mode. */ + if (ptype == PT_REG && rw_mode != RW_MODE_WO) { + dst_size = len; + } + + dst_intr = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3, + INTR); + + dlen = len > dst_size ? dst_size : len; + if (burst_type == AXI_BURST_FIXED) { + if (dlen > (s->cfg.bus_width / 8)) { + dlen = s->cfg.bus_width / 8; + } + } + + address_space_rw(s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen, + true); + if (burst_type == AXI_BURST_INCR) { + s->dsc_dst.addr += dlen; + } + dst_size -= dlen; + buf += dlen; + len -= dlen; + + if (dst_size == 0 && dst_intr) { + zdma_dst_done(s); + } + + /* Write back to buffered descriptor. */ + s->dsc_dst.words[2] = FIELD_DP32(s->dsc_dst.words[2], + ZDMA_CH_DST_DSCR_WORD2, + SIZE, + dst_size); + } +} + +static void zdma_process_descr(XlnxZDMA *s) +{ + uint64_t src_addr; + uint32_t src_size, len; + unsigned int src_cmd; + bool src_intr, src_type; + unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); + unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE); + unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR, + ARBURST); + + src_addr = s->dsc_src.addr; + src_size = FIELD_EX32(s->dsc_src.words[2], ZDMA_CH_SRC_DSCR_WORD2, SIZE); + src_cmd = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, CMD); + src_type = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, TYPE); + src_intr = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, INTR); + + /* FIXED burst types and non-rw modes are only supported in + * simple dma mode. + */ + if (ptype != PT_REG) { + if (rw_mode != RW_MODE_RW) { + qemu_log_mask(LOG_GUEST_ERROR, + "zDMA: rw-mode=%d but not simple DMA mode.\n", + rw_mode); + } + if (burst_type != AXI_BURST_INCR) { + qemu_log_mask(LOG_GUEST_ERROR, + "zDMA: burst_type=%d but not simple DMA mode.\n", + burst_type); + } + burst_type = AXI_BURST_INCR; + rw_mode = RW_MODE_RW; + } + + if (rw_mode == RW_MODE_WO) { + /* In Simple DMA Write-Only, we need to push DST size bytes + * regardless of what SRC size is set to. */ + src_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2, + SIZE); + memcpy(s->buf, &s->regs[R_ZDMA_CH_WR_ONLY_WORD0], s->cfg.bus_width / 8); + } + + while (src_size) { + len = src_size > ARRAY_SIZE(s->buf) ? ARRAY_SIZE(s->buf) : src_size; + if (burst_type == AXI_BURST_FIXED) { + if (len > (s->cfg.bus_width / 8)) { + len = s->cfg.bus_width / 8; + } + } + + if (rw_mode == RW_MODE_WO) { + if (len > s->cfg.bus_width / 8) { + len = s->cfg.bus_width / 8; + } + } else { + address_space_rw(s->dma_as, src_addr, s->attr, s->buf, len, + false); + if (burst_type == AXI_BURST_INCR) { + src_addr += len; + } + } + + if (rw_mode != RW_MODE_RO) { + zdma_write_dst(s, s->buf, len); + } + + s->regs[R_ZDMA_CH_TOTAL_BYTE] += len; + src_size -= len; + } + + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_DONE, true); + + if (src_intr) { + zdma_src_done(s); + } + + /* Load next descriptor. */ + if (ptype == PT_REG || src_cmd == CMD_STOP) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_CTRL2, EN, 0); + zdma_set_state(s, DISABLED); + return; + } + + if (src_cmd == CMD_HALT) { + zdma_set_state(s, PAUSED); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_PAUSE, 1); + zdma_ch_imr_update_irq(s); + return; + } + + zdma_update_descr_addr(s, src_type, R_ZDMA_CH_SRC_CUR_DSCR_LSB); +} + +static void zdma_run(XlnxZDMA *s) +{ + while (s->state == ENABLED && !s->error) { + zdma_load_src_descriptor(s); + + if (s->error) { + zdma_set_state(s, DISABLED); + } else { + zdma_process_descr(s); + } + } + + zdma_ch_imr_update_irq(s); +} + +static void zdma_update_descr_addr_from_start(XlnxZDMA *s) +{ + uint64_t src_addr, dst_addr; + + src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_START_LSB); + zdma_put_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB, src_addr); + dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_START_LSB); + zdma_put_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB, dst_addr); + zdma_load_dst_descriptor(s); +} + +static void zdma_ch_ctrlx_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + + if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL2, EN)) { + s->error = false; + + if (s->state == PAUSED && + ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) { + if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT_ADDR) == 1) { + zdma_update_descr_addr_from_start(s); + } else { + bool src_type = FIELD_EX32(s->dsc_src.words[3], + ZDMA_CH_SRC_DSCR_WORD3, TYPE); + zdma_update_descr_addr(s, src_type, + R_ZDMA_CH_SRC_CUR_DSCR_LSB); + } + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_CTRL0, CONT, false); + zdma_set_state(s, ENABLED); + } else if (s->state == DISABLED) { + zdma_update_descr_addr_from_start(s); + zdma_set_state(s, ENABLED); + } + } else { + /* Leave Paused state? */ + if (s->state == PAUSED && + ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) { + zdma_set_state(s, DISABLED); + } + } + + zdma_run(s); +} + +static RegisterAccessInfo zdma_regs_info[] = { + { .name = "ZDMA_ERR_CTRL", .addr = A_ZDMA_ERR_CTRL, + .rsvd = 0xfffffffe, + },{ .name = "ZDMA_CH_ISR", .addr = A_ZDMA_CH_ISR, + .rsvd = 0xfffff000, + .w1c = 0xfff, + .post_write = zdma_ch_isr_postw, + },{ .name = "ZDMA_CH_IMR", .addr = A_ZDMA_CH_IMR, + .reset = 0xfff, + .rsvd = 0xfffff000, + .ro = 0xfff, + },{ .name = "ZDMA_CH_IEN", .addr = A_ZDMA_CH_IEN, + .rsvd = 0xfffff000, + .pre_write = zdma_ch_ien_prew, + },{ .name = "ZDMA_CH_IDS", .addr = A_ZDMA_CH_IDS, + .rsvd = 0xfffff000, + .pre_write = zdma_ch_ids_prew, + },{ .name = "ZDMA_CH_CTRL0", .addr = A_ZDMA_CH_CTRL0, + .reset = 0x80, + .rsvd = 0xffffff01, + .post_write = zdma_ch_ctrlx_postw, + },{ .name = "ZDMA_CH_CTRL1", .addr = A_ZDMA_CH_CTRL1, + .reset = 0x3ff, + .rsvd = 0xfffffc00, + },{ .name = "ZDMA_CH_FCI", .addr = A_ZDMA_CH_FCI, + .rsvd = 0xffffffc0, + },{ .name = "ZDMA_CH_STATUS", .addr = A_ZDMA_CH_STATUS, + .rsvd = 0xfffffffc, + .ro = 0x3, + },{ .name = "ZDMA_CH_DATA_ATTR", .addr = A_ZDMA_CH_DATA_ATTR, + .reset = 0x483d20f, + .rsvd = 0xf0000000, + },{ .name = "ZDMA_CH_DSCR_ATTR", .addr = A_ZDMA_CH_DSCR_ATTR, + .rsvd = 0xfffffe00, + },{ .name = "ZDMA_CH_SRC_DSCR_WORD0", .addr = A_ZDMA_CH_SRC_DSCR_WORD0, + },{ .name = "ZDMA_CH_SRC_DSCR_WORD1", .addr = A_ZDMA_CH_SRC_DSCR_WORD1, + .rsvd = 0xfffe0000, + },{ .name = "ZDMA_CH_SRC_DSCR_WORD2", .addr = A_ZDMA_CH_SRC_DSCR_WORD2, + .rsvd = 0xc0000000, + },{ .name = "ZDMA_CH_SRC_DSCR_WORD3", .addr = A_ZDMA_CH_SRC_DSCR_WORD3, + .rsvd = 0xffffffe0, + },{ .name = "ZDMA_CH_DST_DSCR_WORD0", .addr = A_ZDMA_CH_DST_DSCR_WORD0, + },{ .name = "ZDMA_CH_DST_DSCR_WORD1", .addr = A_ZDMA_CH_DST_DSCR_WORD1, + .rsvd = 0xfffe0000, + },{ .name = "ZDMA_CH_DST_DSCR_WORD2", .addr = A_ZDMA_CH_DST_DSCR_WORD2, + .rsvd = 0xc0000000, + },{ .name = "ZDMA_CH_DST_DSCR_WORD3", .addr = A_ZDMA_CH_DST_DSCR_WORD3, + .rsvd = 0xfffffffa, + },{ .name = "ZDMA_CH_WR_ONLY_WORD0", .addr = A_ZDMA_CH_WR_ONLY_WORD0, + },{ .name = "ZDMA_CH_WR_ONLY_WORD1", .addr = A_ZDMA_CH_WR_ONLY_WORD1, + },{ .name = "ZDMA_CH_WR_ONLY_WORD2", .addr = A_ZDMA_CH_WR_ONLY_WORD2, + },{ .name = "ZDMA_CH_WR_ONLY_WORD3", .addr = A_ZDMA_CH_WR_ONLY_WORD3, + },{ .name = "ZDMA_CH_SRC_START_LSB", .addr = A_ZDMA_CH_SRC_START_LSB, + },{ .name = "ZDMA_CH_SRC_START_MSB", .addr = A_ZDMA_CH_SRC_START_MSB, + .rsvd = 0xfffe0000, + },{ .name = "ZDMA_CH_DST_START_LSB", .addr = A_ZDMA_CH_DST_START_LSB, + },{ .name = "ZDMA_CH_DST_START_MSB", .addr = A_ZDMA_CH_DST_START_MSB, + .rsvd = 0xfffe0000, + },{ .name = "ZDMA_CH_SRC_CUR_PYLD_LSB", .addr = A_ZDMA_CH_SRC_CUR_PYLD_LSB, + .ro = 0xffffffff, + },{ .name = "ZDMA_CH_SRC_CUR_PYLD_MSB", .addr = A_ZDMA_CH_SRC_CUR_PYLD_MSB, + .rsvd = 0xfffe0000, + .ro = 0x1ffff, + },{ .name = "ZDMA_CH_DST_CUR_PYLD_LSB", .addr = A_ZDMA_CH_DST_CUR_PYLD_LSB, + .ro = 0xffffffff, + },{ .name = "ZDMA_CH_DST_CUR_PYLD_MSB", .addr = A_ZDMA_CH_DST_CUR_PYLD_MSB, + .rsvd = 0xfffe0000, + .ro = 0x1ffff, + },{ .name = "ZDMA_CH_SRC_CUR_DSCR_LSB", .addr = A_ZDMA_CH_SRC_CUR_DSCR_LSB, + .ro = 0xffffffff, + },{ .name = "ZDMA_CH_SRC_CUR_DSCR_MSB", .addr = A_ZDMA_CH_SRC_CUR_DSCR_MSB, + .rsvd = 0xfffe0000, + .ro = 0x1ffff, + },{ .name = "ZDMA_CH_DST_CUR_DSCR_LSB", .addr = A_ZDMA_CH_DST_CUR_DSCR_LSB, + .ro = 0xffffffff, + },{ .name = "ZDMA_CH_DST_CUR_DSCR_MSB", .addr = A_ZDMA_CH_DST_CUR_DSCR_MSB, + .rsvd = 0xfffe0000, + .ro = 0x1ffff, + },{ .name = "ZDMA_CH_TOTAL_BYTE", .addr = A_ZDMA_CH_TOTAL_BYTE, + .w1c = 0xffffffff, + },{ .name = "ZDMA_CH_RATE_CNTL", .addr = A_ZDMA_CH_RATE_CNTL, + .rsvd = 0xfffff000, + },{ .name = "ZDMA_CH_IRQ_SRC_ACCT", .addr = A_ZDMA_CH_IRQ_SRC_ACCT, + .rsvd = 0xffffff00, + .ro = 0xff, + .cor = 0xff, + },{ .name = "ZDMA_CH_IRQ_DST_ACCT", .addr = A_ZDMA_CH_IRQ_DST_ACCT, + .rsvd = 0xffffff00, + .ro = 0xff, + .cor = 0xff, + },{ .name = "ZDMA_CH_DBG0", .addr = A_ZDMA_CH_DBG0, + .rsvd = 0xfffffe00, + .ro = 0x1ff, + },{ .name = "ZDMA_CH_DBG1", .addr = A_ZDMA_CH_DBG1, + .rsvd = 0xfffffe00, + .ro = 0x1ff, + },{ .name = "ZDMA_CH_CTRL2", .addr = A_ZDMA_CH_CTRL2, + .rsvd = 0xfffffffe, + .post_write = zdma_ch_ctrlx_postw, + } +}; + +static void zdma_reset(DeviceState *dev) +{ + XlnxZDMA *s = XLNX_ZDMA(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } + + zdma_ch_imr_update_irq(s); +} + +static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size) +{ + XlnxZDMA *s = XLNX_ZDMA(opaque); + RegisterInfo *r = &s->regs_info[addr / 4]; + + if (!r->data) { + qemu_log("%s: Decode error: read from %" HWADDR_PRIx "\n", + object_get_canonical_path(OBJECT(s)), + addr); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); + zdma_ch_imr_update_irq(s); + return 0; + } + return register_read(r, ~0, NULL, false); +} + +static void zdma_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + XlnxZDMA *s = XLNX_ZDMA(opaque); + RegisterInfo *r = &s->regs_info[addr / 4]; + + if (!r->data) { + qemu_log("%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64 "\n", + object_get_canonical_path(OBJECT(s)), + addr, value); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); + zdma_ch_imr_update_irq(s); + return; + } + register_write(r, value, ~0, NULL, false); +} + +static const MemoryRegionOps zdma_ops = { + .read = zdma_read, + .write = zdma_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void zdma_realize(DeviceState *dev, Error **errp) +{ + XlnxZDMA *s = XLNX_ZDMA(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(zdma_regs_info); ++i) { + RegisterInfo *r = &s->regs_info[zdma_regs_info[i].addr / 4]; + + *r = (RegisterInfo) { + .data = (uint8_t *)&s->regs[ + zdma_regs_info[i].addr / 4], + .data_size = sizeof(uint32_t), + .access = &zdma_regs_info[i], + .opaque = s, + }; + } + + if (s->dma_mr) { + s->dma_as = g_malloc0(sizeof(AddressSpace)); + address_space_init(s->dma_as, s->dma_mr, NULL); + } else { + s->dma_as = &address_space_memory; + } + s->attr = MEMTXATTRS_UNSPECIFIED; +} + +static void zdma_init(Object *obj) +{ + XlnxZDMA *s = XLNX_ZDMA(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &zdma_ops, s, + TYPE_XLNX_ZDMA, ZDMA_R_MAX * 4); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq_zdma_ch_imr); + + object_property_add_link(obj, "dma", TYPE_MEMORY_REGION, + (Object **)&s->dma_mr, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); +} + +static const VMStateDescription vmstate_zdma = { + .name = TYPE_XLNX_ZDMA, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxZDMA, ZDMA_R_MAX), + VMSTATE_UINT32(state, XlnxZDMA), + VMSTATE_UINT32_ARRAY(dsc_src.words, XlnxZDMA, 4), + VMSTATE_UINT32_ARRAY(dsc_dst.words, XlnxZDMA, 4), + VMSTATE_END_OF_LIST(), + } +}; + +static Property zdma_props[] = { + DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64), + DEFINE_PROP_END_OF_LIST(), +}; + +static void zdma_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = zdma_reset; + dc->realize = zdma_realize; + dc->props = zdma_props; + dc->vmsd = &vmstate_zdma; +} + +static const TypeInfo zdma_info = { + .name = TYPE_XLNX_ZDMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxZDMA), + .class_init = zdma_class_init, + .instance_init = zdma_init, +}; + +static void zdma_register_types(void) +{ + type_register_static(&zdma_info); +} + +type_init(zdma_register_types) diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h new file mode 100644 index 0000000000..0b240b4c3c --- /dev/null +++ b/include/hw/dma/xlnx-zdma.h @@ -0,0 +1,84 @@ +/* + * QEMU model of the ZynqMP generic DMA + * + * Copyright (c) 2014 Xilinx Inc. + * Copyright (c) 2018 FEIMTECH AB + * + * Written by Edgar E. Iglesias , + * Francisco Iglesias + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef XLNX_ZDMA_H +#define XLNX_ZDMA_H + +#include "hw/sysbus.h" +#include "hw/register.h" +#include "sysemu/dma.h" + +#define ZDMA_R_MAX (0x204 / 4) + +typedef enum { + DISABLED = 0, + ENABLED = 1, + PAUSED = 2, +} XlnxZDMAState; + +typedef union { + struct { + uint64_t addr; + uint32_t size; + uint32_t attr; + }; + uint32_t words[4]; +} XlnxZDMADescr; + +typedef struct XlnxZDMA { + SysBusDevice parent_obj; + MemoryRegion iomem; + MemTxAttrs attr; + MemoryRegion *dma_mr; + AddressSpace *dma_as; + qemu_irq irq_zdma_ch_imr; + + struct { + uint32_t bus_width; + } cfg; + + XlnxZDMAState state; + bool error; + + XlnxZDMADescr dsc_src; + XlnxZDMADescr dsc_dst; + + uint32_t regs[ZDMA_R_MAX]; + RegisterInfo regs_info[ZDMA_R_MAX]; + + /* We don't model the common bufs. Must be at least 16 bytes + to model write only mode. */ + uint8_t buf[2048]; +} XlnxZDMA; + +#define TYPE_XLNX_ZDMA "xlnx.zdma" + +#define XLNX_ZDMA(obj) \ + OBJECT_CHECK(XlnxZDMA, (obj), TYPE_XLNX_ZDMA) + +#endif /* XLNX_ZDMA_H */ From 04965bca4e659a78a73fb0de2bc394fc04282a70 Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 18 May 2018 17:48:07 +0100 Subject: [PATCH 0585/2380] xlnx-zynqmp: Connect the ZynqMP GDMA and ADMA The ZynqMP contains two instances of a generic DMA, the GDMA, located in the FPD (full power domain), and the ADMA, located in LPD (low power domain). This patch adds these two DMAs to the ZynqMP board. Signed-off-by: Francisco Iglesias Reviewed-by: Alistair Francis Reviewed-by: Edgar E. Iglesias Message-id: 20180503214201.29082-3-frasse.iglesias@gmail.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zynqmp.c | 53 ++++++++++++++++++++++++++++++++++++ include/hw/arm/xlnx-zynqmp.h | 5 ++++ 2 files changed, 58 insertions(+) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 505253e0d2..2045b9d71e 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -90,6 +90,24 @@ static const int spi_intr[XLNX_ZYNQMP_NUM_SPIS] = { 19, 20, }; +static const uint64_t gdma_ch_addr[XLNX_ZYNQMP_NUM_GDMA_CH] = { + 0xFD500000, 0xFD510000, 0xFD520000, 0xFD530000, + 0xFD540000, 0xFD550000, 0xFD560000, 0xFD570000 +}; + +static const int gdma_ch_intr[XLNX_ZYNQMP_NUM_GDMA_CH] = { + 124, 125, 126, 127, 128, 129, 130, 131 +}; + +static const uint64_t adma_ch_addr[XLNX_ZYNQMP_NUM_ADMA_CH] = { + 0xFFA80000, 0xFFA90000, 0xFFAA0000, 0xFFAB0000, + 0xFFAC0000, 0xFFAD0000, 0xFFAE0000, 0xFFAF0000 +}; + +static const int adma_ch_intr[XLNX_ZYNQMP_NUM_ADMA_CH] = { + 77, 78, 79, 80, 81, 82, 83, 84 +}; + typedef struct XlnxZynqMPGICRegion { int region_index; uint32_t address; @@ -197,6 +215,16 @@ static void xlnx_zynqmp_init(Object *obj) object_initialize(&s->rtc, sizeof(s->rtc), TYPE_XLNX_ZYNQMP_RTC); qdev_set_parent_bus(DEVICE(&s->rtc), sysbus_get_default()); + + for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { + object_initialize(&s->gdma[i], sizeof(s->gdma[i]), TYPE_XLNX_ZDMA); + qdev_set_parent_bus(DEVICE(&s->gdma[i]), sysbus_get_default()); + } + + for (i = 0; i < XLNX_ZYNQMP_NUM_ADMA_CH; i++) { + object_initialize(&s->adma[i], sizeof(s->adma[i]), TYPE_XLNX_ZDMA); + qdev_set_parent_bus(DEVICE(&s->adma[i]), sysbus_get_default()); + } } static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) @@ -492,6 +520,31 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, RTC_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, gic_spi[RTC_IRQ]); + + for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { + object_property_set_uint(OBJECT(&s->gdma[i]), 128, "bus-width", &err); + object_property_set_bool(OBJECT(&s->gdma[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gdma[i]), 0, gdma_ch_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gdma[i]), 0, + gic_spi[gdma_ch_intr[i]]); + } + + for (i = 0; i < XLNX_ZYNQMP_NUM_ADMA_CH; i++) { + object_property_set_bool(OBJECT(&s->adma[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->adma[i]), 0, adma_ch_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->adma[i]), 0, + gic_spi[adma_ch_intr[i]]); + } } static Property xlnx_zynqmp_props[] = { diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 3b613e364d..82b6ec2486 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -27,6 +27,7 @@ #include "hw/sd/sdhci.h" #include "hw/ssi/xilinx_spips.h" #include "hw/dma/xlnx_dpdma.h" +#include "hw/dma/xlnx-zdma.h" #include "hw/display/xlnx_dp.h" #include "hw/intc/xlnx-zynqmp-ipi.h" #include "hw/timer/xlnx-zynqmp-rtc.h" @@ -41,6 +42,8 @@ #define XLNX_ZYNQMP_NUM_UARTS 2 #define XLNX_ZYNQMP_NUM_SDHCI 2 #define XLNX_ZYNQMP_NUM_SPIS 2 +#define XLNX_ZYNQMP_NUM_GDMA_CH 8 +#define XLNX_ZYNQMP_NUM_ADMA_CH 8 #define XLNX_ZYNQMP_NUM_QSPI_BUS 2 #define XLNX_ZYNQMP_NUM_QSPI_BUS_CS 2 @@ -94,6 +97,8 @@ typedef struct XlnxZynqMPState { XlnxDPDMAState dpdma; XlnxZynqMPIPI ipi; XlnxZynqMPRTC rtc; + XlnxZDMA gdma[XLNX_ZYNQMP_NUM_GDMA_CH]; + XlnxZDMA adma[XLNX_ZYNQMP_NUM_ADMA_CH]; char *boot_cpu; ARMCPU *boot_cpu_ptr; From 24af32e049684827286e24114a066e09d0dcdaaf Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 18 May 2018 17:48:07 +0100 Subject: [PATCH 0586/2380] hw/arm/smmuv3: Fix Coverity issue in smmuv3_record_event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity complains about use of uninitialized Evt struct. The EVT_SET_TYPE and similar setters use deposit32() on fields in the struct, so they read the uninitialized existing values. In cases where we don't set all the fields in the event struct we'll end up leaking random uninitialized data from QEMU's stack into the guest. Initializing the struct with "Evt evt = {};" ought to satisfy Coverity and fix the data leak. Signed-off-by: Eric Auger Reported-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 1526493784-25328-2-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmuv3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index b3026dea20..42dc521c13 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -143,7 +143,7 @@ static MemTxResult smmuv3_write_eventq(SMMUv3State *s, Evt *evt) void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info) { - Evt evt; + Evt evt = {}; MemTxResult r; if (!smmuv3_eventq_enabled(s)) { From 118eee6ceeaa2b6dd007115ec65ae486e4dee4ed Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 18 May 2018 17:48:07 +0100 Subject: [PATCH 0587/2380] hw/arm/smmu-common: Fix coverity issue in get_block_pte_address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity points out that this can overflow if n > 31, because it's only doing 32-bit arithmetic. Let's use 1ULL instead of 1. Also the formulae used to compute n can be replaced by the level_shift() macro. Reported-by: Peter Maydell Signed-off-by: Eric Auger Reviewed-by: Philippe Mathieu-Daudé Message-id: 1526493784-25328-3-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 01c7be82b6..3c5f7245b5 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -83,9 +83,9 @@ static inline hwaddr get_table_pte_address(uint64_t pte, int granule_sz) static inline hwaddr get_block_pte_address(uint64_t pte, int level, int granule_sz, uint64_t *bsz) { - int n = (granule_sz - 3) * (4 - level) + 3; + int n = level_shift(level, granule_sz); - *bsz = 1 << n; + *bsz = 1ULL << n; return PTE_ADDRESS(pte, n); } From 8c71baedb8055beaa681823206ee3a74f9f8649a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:07 +0100 Subject: [PATCH 0588/2380] target/arm: Introduce translate-a64.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move some stuff that will be common to both translate-a64.c and translate-sve.c. Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 112 +++++------------------------------ target/arm/translate-a64.h | 118 +++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 97 deletions(-) create mode 100644 target/arm/translate-a64.h diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index d8284678f7..74ef756ad5 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -36,13 +36,13 @@ #include "exec/log.h" #include "trace-tcg.h" +#include "translate-a64.h" static TCGv_i64 cpu_X[32]; static TCGv_i64 cpu_pc; /* Load/store exclusive handling */ static TCGv_i64 cpu_exclusive_high; -static TCGv_i64 cpu_reg(DisasContext *s, int reg); static const char *regnames[] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", @@ -86,13 +86,6 @@ typedef void CryptoThreeOpIntFn(TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void CryptoThreeOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); typedef void AtomicThreeOpFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGArg, TCGMemOp); -/* Note that the gvec expanders operate on offsets + sizes. */ -typedef void GVecGen2Fn(unsigned, uint32_t, uint32_t, uint32_t, uint32_t); -typedef void GVecGen2iFn(unsigned, uint32_t, uint32_t, int64_t, - uint32_t, uint32_t); -typedef void GVecGen3Fn(unsigned, uint32_t, uint32_t, - uint32_t, uint32_t, uint32_t); - /* initialize TCG globals. */ void a64_translate_init(void) { @@ -405,22 +398,13 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) } } -static void unallocated_encoding(DisasContext *s) +void unallocated_encoding(DisasContext *s) { /* Unallocated and reserved encodings are uncategorized */ gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), default_exception_el(s)); } -#define unsupported_encoding(s, insn) \ - do { \ - qemu_log_mask(LOG_UNIMP, \ - "%s:%d: unsupported instruction encoding 0x%08x " \ - "at pc=%016" PRIx64 "\n", \ - __FILE__, __LINE__, insn, s->pc - 4); \ - unallocated_encoding(s); \ - } while (0) - static void init_tmp_a64_array(DisasContext *s) { #ifdef CONFIG_DEBUG_TCG @@ -438,13 +422,13 @@ static void free_tmp_a64(DisasContext *s) init_tmp_a64_array(s); } -static TCGv_i64 new_tmp_a64(DisasContext *s) +TCGv_i64 new_tmp_a64(DisasContext *s) { assert(s->tmp_a64_count < TMP_A64_MAX); return s->tmp_a64[s->tmp_a64_count++] = tcg_temp_new_i64(); } -static TCGv_i64 new_tmp_a64_zero(DisasContext *s) +TCGv_i64 new_tmp_a64_zero(DisasContext *s) { TCGv_i64 t = new_tmp_a64(s); tcg_gen_movi_i64(t, 0); @@ -466,7 +450,7 @@ static TCGv_i64 new_tmp_a64_zero(DisasContext *s) * to cpu_X[31] and ZR accesses to a temporary which can be discarded. * This is the point of the _sp forms. */ -static TCGv_i64 cpu_reg(DisasContext *s, int reg) +TCGv_i64 cpu_reg(DisasContext *s, int reg) { if (reg == 31) { return new_tmp_a64_zero(s); @@ -476,7 +460,7 @@ static TCGv_i64 cpu_reg(DisasContext *s, int reg) } /* register access for when 31 == SP */ -static TCGv_i64 cpu_reg_sp(DisasContext *s, int reg) +TCGv_i64 cpu_reg_sp(DisasContext *s, int reg) { return cpu_X[reg]; } @@ -485,7 +469,7 @@ static TCGv_i64 cpu_reg_sp(DisasContext *s, int reg) * representing the register contents. This TCGv is an auto-freed * temporary so it need not be explicitly freed, and may be modified. */ -static TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) +TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) { TCGv_i64 v = new_tmp_a64(s); if (reg != 31) { @@ -500,7 +484,7 @@ static TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) return v; } -static TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) +TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) { TCGv_i64 v = new_tmp_a64(s); if (sf) { @@ -511,72 +495,6 @@ static TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) return v; } -/* We should have at some point before trying to access an FP register - * done the necessary access check, so assert that - * (a) we did the check and - * (b) we didn't then just plough ahead anyway if it failed. - * Print the instruction pattern in the abort message so we can figure - * out what we need to fix if a user encounters this problem in the wild. - */ -static inline void assert_fp_access_checked(DisasContext *s) -{ -#ifdef CONFIG_DEBUG_TCG - if (unlikely(!s->fp_access_checked || s->fp_excp_el)) { - fprintf(stderr, "target-arm: FP access check missing for " - "instruction 0x%08x\n", s->insn); - abort(); - } -#endif -} - -/* Return the offset into CPUARMState of an element of specified - * size, 'element' places in from the least significant end of - * the FP/vector register Qn. - */ -static inline int vec_reg_offset(DisasContext *s, int regno, - int element, TCGMemOp size) -{ - int offs = 0; -#ifdef HOST_WORDS_BIGENDIAN - /* This is complicated slightly because vfp.zregs[n].d[0] is - * still the low half and vfp.zregs[n].d[1] the high half - * of the 128 bit vector, even on big endian systems. - * Calculate the offset assuming a fully bigendian 128 bits, - * then XOR to account for the order of the two 64 bit halves. - */ - offs += (16 - ((element + 1) * (1 << size))); - offs ^= 8; -#else - offs += element * (1 << size); -#endif - offs += offsetof(CPUARMState, vfp.zregs[regno]); - assert_fp_access_checked(s); - return offs; -} - -/* Return the offset info CPUARMState of the "whole" vector register Qn. */ -static inline int vec_full_reg_offset(DisasContext *s, int regno) -{ - assert_fp_access_checked(s); - return offsetof(CPUARMState, vfp.zregs[regno]); -} - -/* Return a newly allocated pointer to the vector register. */ -static TCGv_ptr vec_full_reg_ptr(DisasContext *s, int regno) -{ - TCGv_ptr ret = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ret, cpu_env, vec_full_reg_offset(s, regno)); - return ret; -} - -/* Return the byte size of the "whole" vector register, VL / 8. */ -static inline int vec_full_reg_size(DisasContext *s) -{ - /* FIXME SVE: We should put the composite ZCR_EL* value into tb->flags. - In the meantime this is just the AdvSIMD length of 128. */ - return 128 / 8; -} - /* Return the offset into CPUARMState of a slice (from * the least significant end) of FP register Qn (ie * Dn, Sn, Hn or Bn). @@ -641,7 +559,7 @@ static void clear_vec_high(DisasContext *s, bool is_q, int rd) } } -static void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v) +void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v) { unsigned ofs = fp_reg_offset(s, reg, MO_64); @@ -658,7 +576,7 @@ static void write_fp_sreg(DisasContext *s, int reg, TCGv_i32 v) tcg_temp_free_i64(tmp); } -static TCGv_ptr get_fpstatus_ptr(bool is_f16) +TCGv_ptr get_fpstatus_ptr(bool is_f16) { TCGv_ptr statusptr = tcg_temp_new_ptr(); int offset; @@ -1246,14 +1164,14 @@ static inline bool fp_access_check(DisasContext *s) /* Check that SVE access is enabled. If it is, return true. * If not, emit code to generate an appropriate exception and return false. */ -static inline bool sve_access_check(DisasContext *s) +bool sve_access_check(DisasContext *s) { if (s->sve_excp_el) { gen_exception_insn(s, 4, EXCP_UDEF, syn_sve_access_trap(), s->sve_excp_el); return false; } - return true; + return fp_access_check(s); } /* @@ -3419,8 +3337,8 @@ static inline uint64_t bitmask64(unsigned int length) * value (ie should cause a guest UNDEF exception), and true if they are * valid, in which case the decoded bit pattern is written to result. */ -static bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, - unsigned int imms, unsigned int immr) +bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, + unsigned int imms, unsigned int immr) { uint64_t mask; unsigned e, levels, s, r; @@ -5650,7 +5568,7 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn) * the range 01....1xx to 10....0xx, and the most significant 4 bits of * the mantissa; see VFPExpandImm() in the v8 ARM ARM. */ -static uint64_t vfp_expand_imm(int size, uint8_t imm8) +uint64_t vfp_expand_imm(int size, uint8_t imm8) { uint64_t imm; diff --git a/target/arm/translate-a64.h b/target/arm/translate-a64.h new file mode 100644 index 0000000000..dd9c09f89b --- /dev/null +++ b/target/arm/translate-a64.h @@ -0,0 +1,118 @@ +/* + * AArch64 translation, common definitions. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef TARGET_ARM_TRANSLATE_A64_H +#define TARGET_ARM_TRANSLATE_A64_H + +void unallocated_encoding(DisasContext *s); + +#define unsupported_encoding(s, insn) \ + do { \ + qemu_log_mask(LOG_UNIMP, \ + "%s:%d: unsupported instruction encoding 0x%08x " \ + "at pc=%016" PRIx64 "\n", \ + __FILE__, __LINE__, insn, s->pc - 4); \ + unallocated_encoding(s); \ + } while (0) + +TCGv_i64 new_tmp_a64(DisasContext *s); +TCGv_i64 new_tmp_a64_zero(DisasContext *s); +TCGv_i64 cpu_reg(DisasContext *s, int reg); +TCGv_i64 cpu_reg_sp(DisasContext *s, int reg); +TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf); +TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf); +void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v); +TCGv_ptr get_fpstatus_ptr(bool); +bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, + unsigned int imms, unsigned int immr); +uint64_t vfp_expand_imm(int size, uint8_t imm8); +bool sve_access_check(DisasContext *s); + +/* We should have at some point before trying to access an FP register + * done the necessary access check, so assert that + * (a) we did the check and + * (b) we didn't then just plough ahead anyway if it failed. + * Print the instruction pattern in the abort message so we can figure + * out what we need to fix if a user encounters this problem in the wild. + */ +static inline void assert_fp_access_checked(DisasContext *s) +{ +#ifdef CONFIG_DEBUG_TCG + if (unlikely(!s->fp_access_checked || s->fp_excp_el)) { + fprintf(stderr, "target-arm: FP access check missing for " + "instruction 0x%08x\n", s->insn); + abort(); + } +#endif +} + +/* Return the offset into CPUARMState of an element of specified + * size, 'element' places in from the least significant end of + * the FP/vector register Qn. + */ +static inline int vec_reg_offset(DisasContext *s, int regno, + int element, TCGMemOp size) +{ + int offs = 0; +#ifdef HOST_WORDS_BIGENDIAN + /* This is complicated slightly because vfp.zregs[n].d[0] is + * still the low half and vfp.zregs[n].d[1] the high half + * of the 128 bit vector, even on big endian systems. + * Calculate the offset assuming a fully bigendian 128 bits, + * then XOR to account for the order of the two 64 bit halves. + */ + offs += (16 - ((element + 1) * (1 << size))); + offs ^= 8; +#else + offs += element * (1 << size); +#endif + offs += offsetof(CPUARMState, vfp.zregs[regno]); + assert_fp_access_checked(s); + return offs; +} + +/* Return the offset info CPUARMState of the "whole" vector register Qn. */ +static inline int vec_full_reg_offset(DisasContext *s, int regno) +{ + assert_fp_access_checked(s); + return offsetof(CPUARMState, vfp.zregs[regno]); +} + +/* Return a newly allocated pointer to the vector register. */ +static inline TCGv_ptr vec_full_reg_ptr(DisasContext *s, int regno) +{ + TCGv_ptr ret = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(ret, cpu_env, vec_full_reg_offset(s, regno)); + return ret; +} + +/* Return the byte size of the "whole" vector register, VL / 8. */ +static inline int vec_full_reg_size(DisasContext *s) +{ + return s->sve_len; +} + +bool disas_sve(DisasContext *, uint32_t); + +/* Note that the gvec expanders operate on offsets + sizes. */ +typedef void GVecGen2Fn(unsigned, uint32_t, uint32_t, uint32_t, uint32_t); +typedef void GVecGen2iFn(unsigned, uint32_t, uint32_t, int64_t, + uint32_t, uint32_t); +typedef void GVecGen3Fn(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t); + +#endif /* TARGET_ARM_TRANSLATE_A64_H */ From 38388f7ee3adc04a7e7246c04352451c4f8d00fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0589/2380] target/arm: Add SVE decode skeleton Including only 4, as-yet unimplemented, instruction patterns so that the whole thing compiles. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- .gitignore | 1 + target/arm/Makefile.objs | 10 ++++++ target/arm/sve.decode | 45 +++++++++++++++++++++++++++ target/arm/translate-a64.c | 7 ++++- target/arm/translate-sve.c | 63 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 target/arm/sve.decode create mode 100644 target/arm/translate-sve.c diff --git a/.gitignore b/.gitignore index 4055e12ee8..81e1f2fb0f 100644 --- a/.gitignore +++ b/.gitignore @@ -206,3 +206,4 @@ trace-dtrace-root.h trace-dtrace-root.dtrace trace-ust-all.h trace-ust-all.c +/target/arm/decode-sve.inc.c diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs index 1297bead5f..a6f733eaa8 100644 --- a/target/arm/Makefile.objs +++ b/target/arm/Makefile.objs @@ -10,3 +10,13 @@ obj-y += gdbstub.o obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o obj-y += crypto_helper.o obj-$(CONFIG_SOFTMMU) += arm-powerctl.o + +DECODETREE = $(SRC_PATH)/scripts/decodetree.py + +target/arm/decode-sve.inc.c: $(SRC_PATH)/target/arm/sve.decode $(DECODETREE) + $(call quiet-command,\ + $(PYTHON) $(DECODETREE) --decode disas_sve -o $@ $<,\ + "GEN", $(TARGET_DIR)$@) + +target/arm/translate-sve.o: target/arm/decode-sve.inc.c +obj-$(TARGET_AARCH64) += translate-sve.o diff --git a/target/arm/sve.decode b/target/arm/sve.decode new file mode 100644 index 0000000000..48dac9f71f --- /dev/null +++ b/target/arm/sve.decode @@ -0,0 +1,45 @@ +# AArch64 SVE instruction descriptions +# +# Copyright (c) 2017 Linaro, Ltd +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . + +# +# This file is processed by scripts/decodetree.py +# + +########################################################################### +# Named attribute sets. These are used to make nice(er) names +# when creating helpers common to those for the individual +# instruction patterns. + +&rrr_esz rd rn rm esz + +########################################################################### +# Named instruction formats. These are generally used to +# reduce the amount of duplication between instruction patterns. + +# Three operand with unused vector element size +@rd_rn_rm_e0 ........ ... rm:5 ... ... rn:5 rd:5 &rrr_esz esz=0 + +########################################################################### +# Instruction patterns. Grouped according to the SVE encodingindex.xhtml. + +### SVE Logical - Unpredicated Group + +# SVE bitwise logical operations (unpredicated) +AND_zzz 00000100 00 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 +ORR_zzz 00000100 01 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 +EOR_zzz 00000100 10 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 +BIC_zzz 00000100 11 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 74ef756ad5..b32332ce2c 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -13676,9 +13676,14 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) s->fp_access_checked = false; switch (extract32(insn, 25, 4)) { - case 0x0: case 0x1: case 0x2: case 0x3: /* UNALLOCATED */ + case 0x0: case 0x1: case 0x3: /* UNALLOCATED */ unallocated_encoding(s); break; + case 0x2: + if (!arm_dc_feature(s, ARM_FEATURE_SVE) || !disas_sve(s, insn)) { + unallocated_encoding(s); + } + break; case 0x8: case 0x9: /* Data processing - immediate */ disas_data_proc_imm(s, insn); break; diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c new file mode 100644 index 0000000000..d323bd0b67 --- /dev/null +++ b/target/arm/translate-sve.c @@ -0,0 +1,63 @@ +/* + * AArch64 SVE translation + * + * Copyright (c) 2018 Linaro, Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "tcg-op.h" +#include "tcg-op-gvec.h" +#include "qemu/log.h" +#include "arm_ldst.h" +#include "translate.h" +#include "internals.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/log.h" +#include "trace-tcg.h" +#include "translate-a64.h" + +/* + * Include the generated decoder. + */ + +#include "decode-sve.inc.c" + +/* + * Implement all of the translator functions referenced by the decoder. + */ + +static bool trans_AND_zzz(DisasContext *s, arg_AND_zzz *a, uint32_t insn) +{ + return false; +} + +static bool trans_ORR_zzz(DisasContext *s, arg_ORR_zzz *a, uint32_t insn) +{ + return false; +} + +static bool trans_EOR_zzz(DisasContext *s, arg_EOR_zzz *a, uint32_t insn) +{ + return false; +} + +static bool trans_BIC_zzz(DisasContext *s, arg_BIC_zzz *a, uint32_t insn) +{ + return false; +} From 39eea56172e668cc4cca611ed9166779df54ac63 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0590/2380] target/arm: Implement SVE Bitwise Logical - Unpredicated Group These were the instructions that were stubbed out when introducing the decode skeleton. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-sve.c | 55 ++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index d323bd0b67..67d6db313e 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -42,22 +42,61 @@ * Implement all of the translator functions referenced by the decoder. */ -static bool trans_AND_zzz(DisasContext *s, arg_AND_zzz *a, uint32_t insn) +/* Invoke a vector expander on two Zregs. */ +static bool do_vector2_z(DisasContext *s, GVecGen2Fn *gvec_fn, + int esz, int rd, int rn) { - return false; + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + gvec_fn(esz, vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), vsz, vsz); + } + return true; } -static bool trans_ORR_zzz(DisasContext *s, arg_ORR_zzz *a, uint32_t insn) +/* Invoke a vector expander on three Zregs. */ +static bool do_vector3_z(DisasContext *s, GVecGen3Fn *gvec_fn, + int esz, int rd, int rn, int rm) { - return false; + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + gvec_fn(esz, vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), vsz, vsz); + } + return true; } -static bool trans_EOR_zzz(DisasContext *s, arg_EOR_zzz *a, uint32_t insn) +/* Invoke a vector move on two Zregs. */ +static bool do_mov_z(DisasContext *s, int rd, int rn) { - return false; + return do_vector2_z(s, tcg_gen_gvec_mov, 0, rd, rn); } -static bool trans_BIC_zzz(DisasContext *s, arg_BIC_zzz *a, uint32_t insn) +/* + *** SVE Logical - Unpredicated Group + */ + +static bool trans_AND_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) { - return false; + return do_vector3_z(s, tcg_gen_gvec_and, 0, a->rd, a->rn, a->rm); +} + +static bool trans_ORR_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + if (a->rn == a->rm) { /* MOV */ + return do_mov_z(s, a->rd, a->rn); + } else { + return do_vector3_z(s, tcg_gen_gvec_or, 0, a->rd, a->rn, a->rm); + } +} + +static bool trans_EOR_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_xor, 0, a->rd, a->rn, a->rm); +} + +static bool trans_BIC_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_andc, 0, a->rd, a->rn, a->rm); } From d1822297f63b68c1fd8c5282b753d00c95701dd8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0591/2380] target/arm: Implement SVE load vector/predicate Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 20 ++++++ target/arm/translate-sve.c | 127 +++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 48dac9f71f..a2c4450e7c 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -19,11 +19,17 @@ # This file is processed by scripts/decodetree.py # +########################################################################### +# Named fields. These are primarily for disjoint fields. + +%imm9_16_10 16:s6 10:3 + ########################################################################### # Named attribute sets. These are used to make nice(er) names # when creating helpers common to those for the individual # instruction patterns. +&rri rd rn imm &rrr_esz rd rn rm esz ########################################################################### @@ -33,6 +39,12 @@ # Three operand with unused vector element size @rd_rn_rm_e0 ........ ... rm:5 ... ... rn:5 rd:5 &rrr_esz esz=0 +# Basic Load/Store with 9-bit immediate offset +@pd_rn_i9 ........ ........ ...... rn:5 . rd:4 \ + &rri imm=%imm9_16_10 +@rd_rn_i9 ........ ........ ...... rn:5 rd:5 \ + &rri imm=%imm9_16_10 + ########################################################################### # Instruction patterns. Grouped according to the SVE encodingindex.xhtml. @@ -43,3 +55,11 @@ AND_zzz 00000100 00 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 ORR_zzz 00000100 01 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 EOR_zzz 00000100 10 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 BIC_zzz 00000100 11 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 + +### SVE Memory - 32-bit Gather and Unsized Contiguous Group + +# SVE load predicate register +LDR_pri 10000101 10 ...... 000 ... ..... 0 .... @pd_rn_i9 + +# SVE load vector register +LDR_zri 10000101 10 ...... 010 ... ..... ..... @rd_rn_i9 diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 67d6db313e..5ec18a6aac 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -42,6 +42,20 @@ * Implement all of the translator functions referenced by the decoder. */ +/* Return the offset info CPUARMState of the predicate vector register Pn. + * Note for this purpose, FFR is P16. + */ +static inline int pred_full_reg_offset(DisasContext *s, int regno) +{ + return offsetof(CPUARMState, vfp.pregs[regno]); +} + +/* Return the byte size of the whole predicate register, VL / 64. */ +static inline int pred_full_reg_size(DisasContext *s) +{ + return s->sve_len >> 3; +} + /* Invoke a vector expander on two Zregs. */ static bool do_vector2_z(DisasContext *s, GVecGen2Fn *gvec_fn, int esz, int rd, int rn) @@ -100,3 +114,116 @@ static bool trans_BIC_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) { return do_vector3_z(s, tcg_gen_gvec_andc, 0, a->rd, a->rn, a->rm); } + +/* + *** SVE Memory - 32-bit Gather and Unsized Contiguous Group + */ + +/* Subroutine loading a vector register at VOFS of LEN bytes. + * The load should begin at the address Rn + IMM. + */ + +static void do_ldr(DisasContext *s, uint32_t vofs, uint32_t len, + int rn, int imm) +{ + uint32_t len_align = QEMU_ALIGN_DOWN(len, 8); + uint32_t len_remain = len % 8; + uint32_t nparts = len / 8 + ctpop8(len_remain); + int midx = get_mem_index(s); + TCGv_i64 addr, t0, t1; + + addr = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + + /* Note that unpredicated load/store of vector/predicate registers + * are defined as a stream of bytes, which equates to little-endian + * operations on larger quantities. There is no nice way to force + * a little-endian load for aarch64_be-linux-user out of line. + * + * Attempt to keep code expansion to a minimum by limiting the + * amount of unrolling done. + */ + if (nparts <= 4) { + int i; + + for (i = 0; i < len_align; i += 8) { + tcg_gen_addi_i64(addr, cpu_reg_sp(s, rn), imm + i); + tcg_gen_qemu_ld_i64(t0, addr, midx, MO_LEQ); + tcg_gen_st_i64(t0, cpu_env, vofs + i); + } + } else { + TCGLabel *loop = gen_new_label(); + TCGv_ptr tp, i = tcg_const_local_ptr(0); + + gen_set_label(loop); + + /* Minimize the number of local temps that must be re-read from + * the stack each iteration. Instead, re-compute values other + * than the loop counter. + */ + tp = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(tp, i, imm); + tcg_gen_extu_ptr_i64(addr, tp); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, rn)); + + tcg_gen_qemu_ld_i64(t0, addr, midx, MO_LEQ); + + tcg_gen_add_ptr(tp, cpu_env, i); + tcg_gen_addi_ptr(i, i, 8); + tcg_gen_st_i64(t0, tp, vofs); + tcg_temp_free_ptr(tp); + + tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); + tcg_temp_free_ptr(i); + } + + /* Predicate register loads can be any multiple of 2. + * Note that we still store the entire 64-bit unit into cpu_env. + */ + if (len_remain) { + tcg_gen_addi_i64(addr, cpu_reg_sp(s, rn), imm + len_align); + + switch (len_remain) { + case 2: + case 4: + case 8: + tcg_gen_qemu_ld_i64(t0, addr, midx, MO_LE | ctz32(len_remain)); + break; + + case 6: + t1 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(t0, addr, midx, MO_LEUL); + tcg_gen_addi_i64(addr, addr, 4); + tcg_gen_qemu_ld_i64(t1, addr, midx, MO_LEUW); + tcg_gen_deposit_i64(t0, t0, t1, 32, 32); + tcg_temp_free_i64(t1); + break; + + default: + g_assert_not_reached(); + } + tcg_gen_st_i64(t0, cpu_env, vofs + len_align); + } + tcg_temp_free_i64(addr); + tcg_temp_free_i64(t0); +} + +static bool trans_LDR_zri(DisasContext *s, arg_rri *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int size = vec_full_reg_size(s); + int off = vec_full_reg_offset(s, a->rd); + do_ldr(s, off, size, a->rn, a->imm * size); + } + return true; +} + +static bool trans_LDR_pri(DisasContext *s, arg_rri *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int size = pred_full_reg_size(s); + int off = pred_full_reg_offset(s, a->rd); + do_ldr(s, off, size, a->rn, a->imm * size); + } + return true; +} From 9e18d7a67fa6be84d14fc72deaf00bb6b28d8b7e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0592/2380] target/arm: Implement SVE predicate test Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/Makefile.objs | 2 +- target/arm/helper-sve.h | 21 ++++++++++ target/arm/helper.h | 1 + target/arm/sve.decode | 5 +++ target/arm/sve_helper.c | 78 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 65 +++++++++++++++++++++++++++++++ 6 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 target/arm/helper-sve.h create mode 100644 target/arm/sve_helper.c diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs index a6f733eaa8..11c7baf8a3 100644 --- a/target/arm/Makefile.objs +++ b/target/arm/Makefile.objs @@ -19,4 +19,4 @@ target/arm/decode-sve.inc.c: $(SRC_PATH)/target/arm/sve.decode $(DECODETREE) "GEN", $(TARGET_DIR)$@) target/arm/translate-sve.o: target/arm/decode-sve.inc.c -obj-$(TARGET_AARCH64) += translate-sve.o +obj-$(TARGET_AARCH64) += translate-sve.o sve_helper.o diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h new file mode 100644 index 0000000000..b6e91539ae --- /dev/null +++ b/target/arm/helper-sve.h @@ -0,0 +1,21 @@ +/* + * AArch64 SVE specific helper definitions + * + * Copyright (c) 2018 Linaro, Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +DEF_HELPER_FLAGS_2(sve_predtest1, TCG_CALL_NO_WG, i32, i64, i64) +DEF_HELPER_FLAGS_3(sve_predtest, TCG_CALL_NO_WG, i32, ptr, ptr, i32) diff --git a/target/arm/helper.h b/target/arm/helper.h index 047f3bc1ca..0c6a144458 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -603,4 +603,5 @@ DEF_HELPER_FLAGS_5(gvec_fcmlad, TCG_CALL_NO_RWG, #ifdef TARGET_AARCH64 #include "helper-a64.h" +#include "helper-sve.h" #endif diff --git a/target/arm/sve.decode b/target/arm/sve.decode index a2c4450e7c..a44ca2f551 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -56,6 +56,11 @@ ORR_zzz 00000100 01 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 EOR_zzz 00000100 10 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 BIC_zzz 00000100 11 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 +### SVE Predicate Misc Group + +# SVE predicate test +PTEST 00100101 01 010000 11 pg:4 0 rn:4 0 0000 + ### SVE Memory - 32-bit Gather and Unsized Contiguous Group # SVE load predicate register diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c new file mode 100644 index 0000000000..1ebb67e1df --- /dev/null +++ b/target/arm/sve_helper.c @@ -0,0 +1,78 @@ +/* + * ARM SVE Operations + * + * Copyright (c) 2018 Linaro, Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-proto.h" +#include "tcg/tcg-gvec-desc.h" + + +/* Return a value for NZCV as per the ARM PredTest pseudofunction. + * + * The return value has bit 31 set if N is set, bit 1 set if Z is clear, + * and bit 0 set if C is set. Compare the definitions of these variables + * within CPUARMState. + */ + +/* For no G bits set, NZCV = C. */ +#define PREDTEST_INIT 1 + +/* This is an iterative function, called for each Pd and Pg word + * moving forward. + */ +static uint32_t iter_predtest_fwd(uint64_t d, uint64_t g, uint32_t flags) +{ + if (likely(g)) { + /* Compute N from first D & G. + Use bit 2 to signal first G bit seen. */ + if (!(flags & 4)) { + flags |= ((d & (g & -g)) != 0) << 31; + flags |= 4; + } + + /* Accumulate Z from each D & G. */ + flags |= ((d & g) != 0) << 1; + + /* Compute C from last !(D & G). Replace previous. */ + flags = deposit32(flags, 0, 1, (d & pow2floor(g)) == 0); + } + return flags; +} + +/* The same for a single word predicate. */ +uint32_t HELPER(sve_predtest1)(uint64_t d, uint64_t g) +{ + return iter_predtest_fwd(d, g, PREDTEST_INIT); +} + +/* The same for a multi-word predicate. */ +uint32_t HELPER(sve_predtest)(void *vd, void *vg, uint32_t words) +{ + uint32_t flags = PREDTEST_INIT; + uint64_t *d = vd, *g = vg; + uintptr_t i = 0; + + do { + flags = iter_predtest_fwd(d[i], g[i], flags); + } while (++i < words); + + return flags; +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 5ec18a6aac..c3f1b0bfa6 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -87,6 +87,43 @@ static bool do_mov_z(DisasContext *s, int rd, int rn) return do_vector2_z(s, tcg_gen_gvec_mov, 0, rd, rn); } +/* Set the cpu flags as per a return from an SVE helper. */ +static void do_pred_flags(TCGv_i32 t) +{ + tcg_gen_mov_i32(cpu_NF, t); + tcg_gen_andi_i32(cpu_ZF, t, 2); + tcg_gen_andi_i32(cpu_CF, t, 1); + tcg_gen_movi_i32(cpu_VF, 0); +} + +/* Subroutines computing the ARM PredTest psuedofunction. */ +static void do_predtest1(TCGv_i64 d, TCGv_i64 g) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + gen_helper_sve_predtest1(t, d, g); + do_pred_flags(t); + tcg_temp_free_i32(t); +} + +static void do_predtest(DisasContext *s, int dofs, int gofs, int words) +{ + TCGv_ptr dptr = tcg_temp_new_ptr(); + TCGv_ptr gptr = tcg_temp_new_ptr(); + TCGv_i32 t; + + tcg_gen_addi_ptr(dptr, cpu_env, dofs); + tcg_gen_addi_ptr(gptr, cpu_env, gofs); + t = tcg_const_i32(words); + + gen_helper_sve_predtest(t, dptr, gptr, t); + tcg_temp_free_ptr(dptr); + tcg_temp_free_ptr(gptr); + + do_pred_flags(t); + tcg_temp_free_i32(t); +} + /* *** SVE Logical - Unpredicated Group */ @@ -115,6 +152,34 @@ static bool trans_BIC_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) return do_vector3_z(s, tcg_gen_gvec_andc, 0, a->rd, a->rn, a->rm); } +/* + *** SVE Predicate Misc Group + */ + +static bool trans_PTEST(DisasContext *s, arg_PTEST *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int nofs = pred_full_reg_offset(s, a->rn); + int gofs = pred_full_reg_offset(s, a->pg); + int words = DIV_ROUND_UP(pred_full_reg_size(s), 8); + + if (words == 1) { + TCGv_i64 pn = tcg_temp_new_i64(); + TCGv_i64 pg = tcg_temp_new_i64(); + + tcg_gen_ld_i64(pn, cpu_env, nofs); + tcg_gen_ld_i64(pg, cpu_env, gofs); + do_predtest1(pn, pg); + + tcg_temp_free_i64(pn); + tcg_temp_free_i64(pg); + } else { + do_predtest(s, nofs, gofs, words); + } + } + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From 516e246a1a292f6c6f6aad5451799accbb08acd9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0593/2380] target/arm: Implement SVE Predicate Logical Operations Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 4 +- target/arm/helper-sve.h | 10 + target/arm/sve.decode | 16 ++ target/arm/sve_helper.c | 39 ++++ target/arm/translate-sve.c | 361 +++++++++++++++++++++++++++++++++++++ 5 files changed, 429 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 01281f5c56..df21e143cc 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -541,6 +541,8 @@ typedef struct CPUARMState { #ifdef TARGET_AARCH64 /* Store FFR as pregs[16] to make it easier to treat as any other. */ ARMPredicateReg pregs[17]; + /* Scratch space for aa64 sve predicate temporary. */ + ARMPredicateReg preg_tmp; #endif uint32_t xregs[16]; @@ -548,7 +550,7 @@ typedef struct CPUARMState { int vec_len; int vec_stride; - /* scratch space when Tn are not sufficient. */ + /* Scratch space for aa32 neon expansion. */ uint32_t scratch[8]; /* There are a number of distinct float control structures: diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index b6e91539ae..57adc4d912 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -19,3 +19,13 @@ DEF_HELPER_FLAGS_2(sve_predtest1, TCG_CALL_NO_WG, i32, i64, i64) DEF_HELPER_FLAGS_3(sve_predtest, TCG_CALL_NO_WG, i32, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_orr_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_orn_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_nor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_nand_pppp, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index a44ca2f551..f695dda3b1 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -31,6 +31,7 @@ &rri rd rn imm &rrr_esz rd rn rm esz +&rprr_s rd pg rn rm s ########################################################################### # Named instruction formats. These are generally used to @@ -39,6 +40,9 @@ # Three operand with unused vector element size @rd_rn_rm_e0 ........ ... rm:5 ... ... rn:5 rd:5 &rrr_esz esz=0 +# Three predicate operand, with governing predicate, flag setting +@pd_pg_pn_pm_s ........ . s:1 .. rm:4 .. pg:4 . rn:4 . rd:4 &rprr_s + # Basic Load/Store with 9-bit immediate offset @pd_rn_i9 ........ ........ ...... rn:5 . rd:4 \ &rri imm=%imm9_16_10 @@ -56,6 +60,18 @@ ORR_zzz 00000100 01 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 EOR_zzz 00000100 10 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 BIC_zzz 00000100 11 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 +### SVE Predicate Logical Operations Group + +# SVE predicate logical operations +AND_pppp 00100101 0. 00 .... 01 .... 0 .... 0 .... @pd_pg_pn_pm_s +BIC_pppp 00100101 0. 00 .... 01 .... 0 .... 1 .... @pd_pg_pn_pm_s +EOR_pppp 00100101 0. 00 .... 01 .... 1 .... 0 .... @pd_pg_pn_pm_s +SEL_pppp 00100101 0. 00 .... 01 .... 1 .... 1 .... @pd_pg_pn_pm_s +ORR_pppp 00100101 1. 00 .... 01 .... 0 .... 0 .... @pd_pg_pn_pm_s +ORN_pppp 00100101 1. 00 .... 01 .... 0 .... 1 .... @pd_pg_pn_pm_s +NOR_pppp 00100101 1. 00 .... 01 .... 1 .... 0 .... @pd_pg_pn_pm_s +NAND_pppp 00100101 1. 00 .... 01 .... 1 .... 1 .... @pd_pg_pn_pm_s + ### SVE Predicate Misc Group # SVE predicate test diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 1ebb67e1df..2eda6f2ef1 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -76,3 +76,42 @@ uint32_t HELPER(sve_predtest)(void *vd, void *vg, uint32_t words) return flags; } + +#define LOGICAL_PPPP(NAME, FUNC) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + uintptr_t opr_sz = simd_oprsz(desc); \ + uint64_t *d = vd, *n = vn, *m = vm, *g = vg; \ + uintptr_t i; \ + for (i = 0; i < opr_sz / 8; ++i) { \ + d[i] = FUNC(n[i], m[i], g[i]); \ + } \ +} + +#define DO_AND(N, M, G) (((N) & (M)) & (G)) +#define DO_BIC(N, M, G) (((N) & ~(M)) & (G)) +#define DO_EOR(N, M, G) (((N) ^ (M)) & (G)) +#define DO_ORR(N, M, G) (((N) | (M)) & (G)) +#define DO_ORN(N, M, G) (((N) | ~(M)) & (G)) +#define DO_NOR(N, M, G) (~((N) | (M)) & (G)) +#define DO_NAND(N, M, G) (~((N) & (M)) & (G)) +#define DO_SEL(N, M, G) (((N) & (G)) | ((M) & ~(G))) + +LOGICAL_PPPP(sve_and_pppp, DO_AND) +LOGICAL_PPPP(sve_bic_pppp, DO_BIC) +LOGICAL_PPPP(sve_eor_pppp, DO_EOR) +LOGICAL_PPPP(sve_sel_pppp, DO_SEL) +LOGICAL_PPPP(sve_orr_pppp, DO_ORR) +LOGICAL_PPPP(sve_orn_pppp, DO_ORN) +LOGICAL_PPPP(sve_nor_pppp, DO_NOR) +LOGICAL_PPPP(sve_nand_pppp, DO_NAND) + +#undef DO_AND +#undef DO_BIC +#undef DO_EOR +#undef DO_ORR +#undef DO_ORN +#undef DO_NOR +#undef DO_NAND +#undef DO_SEL +#undef LOGICAL_PPPP diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index c3f1b0bfa6..67fb3091ac 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -56,6 +56,28 @@ static inline int pred_full_reg_size(DisasContext *s) return s->sve_len >> 3; } +/* Round up the size of a register to a size allowed by + * the tcg vector infrastructure. Any operation which uses this + * size may assume that the bits above pred_full_reg_size are zero, + * and must leave them the same way. + * + * Note that this is not needed for the vector registers as they + * are always properly sized for tcg vectors. + */ +static int size_for_gvec(int size) +{ + if (size <= 8) { + return 8; + } else { + return QEMU_ALIGN_UP(size, 16); + } +} + +static int pred_gvec_reg_size(DisasContext *s) +{ + return size_for_gvec(pred_full_reg_size(s)); +} + /* Invoke a vector expander on two Zregs. */ static bool do_vector2_z(DisasContext *s, GVecGen2Fn *gvec_fn, int esz, int rd, int rn) @@ -87,6 +109,52 @@ static bool do_mov_z(DisasContext *s, int rd, int rn) return do_vector2_z(s, tcg_gen_gvec_mov, 0, rd, rn); } +/* Invoke a vector expander on two Pregs. */ +static bool do_vector2_p(DisasContext *s, GVecGen2Fn *gvec_fn, + int esz, int rd, int rn) +{ + if (sve_access_check(s)) { + unsigned psz = pred_gvec_reg_size(s); + gvec_fn(esz, pred_full_reg_offset(s, rd), + pred_full_reg_offset(s, rn), psz, psz); + } + return true; +} + +/* Invoke a vector expander on three Pregs. */ +static bool do_vector3_p(DisasContext *s, GVecGen3Fn *gvec_fn, + int esz, int rd, int rn, int rm) +{ + if (sve_access_check(s)) { + unsigned psz = pred_gvec_reg_size(s); + gvec_fn(esz, pred_full_reg_offset(s, rd), + pred_full_reg_offset(s, rn), + pred_full_reg_offset(s, rm), psz, psz); + } + return true; +} + +/* Invoke a vector operation on four Pregs. */ +static bool do_vecop4_p(DisasContext *s, const GVecGen4 *gvec_op, + int rd, int rn, int rm, int rg) +{ + if (sve_access_check(s)) { + unsigned psz = pred_gvec_reg_size(s); + tcg_gen_gvec_4(pred_full_reg_offset(s, rd), + pred_full_reg_offset(s, rn), + pred_full_reg_offset(s, rm), + pred_full_reg_offset(s, rg), + psz, psz, gvec_op); + } + return true; +} + +/* Invoke a vector move on two Pregs. */ +static bool do_mov_p(DisasContext *s, int rd, int rn) +{ + return do_vector2_p(s, tcg_gen_gvec_mov, 0, rd, rn); +} + /* Set the cpu flags as per a return from an SVE helper. */ static void do_pred_flags(TCGv_i32 t) { @@ -152,6 +220,299 @@ static bool trans_BIC_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) return do_vector3_z(s, tcg_gen_gvec_andc, 0, a->rd, a->rn, a->rm); } +/* + *** SVE Predicate Logical Operations Group + */ + +static bool do_pppp_flags(DisasContext *s, arg_rprr_s *a, + const GVecGen4 *gvec_op) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned psz = pred_gvec_reg_size(s); + int dofs = pred_full_reg_offset(s, a->rd); + int nofs = pred_full_reg_offset(s, a->rn); + int mofs = pred_full_reg_offset(s, a->rm); + int gofs = pred_full_reg_offset(s, a->pg); + + if (psz == 8) { + /* Do the operation and the flags generation in temps. */ + TCGv_i64 pd = tcg_temp_new_i64(); + TCGv_i64 pn = tcg_temp_new_i64(); + TCGv_i64 pm = tcg_temp_new_i64(); + TCGv_i64 pg = tcg_temp_new_i64(); + + tcg_gen_ld_i64(pn, cpu_env, nofs); + tcg_gen_ld_i64(pm, cpu_env, mofs); + tcg_gen_ld_i64(pg, cpu_env, gofs); + + gvec_op->fni8(pd, pn, pm, pg); + tcg_gen_st_i64(pd, cpu_env, dofs); + + do_predtest1(pd, pg); + + tcg_temp_free_i64(pd); + tcg_temp_free_i64(pn); + tcg_temp_free_i64(pm); + tcg_temp_free_i64(pg); + } else { + /* The operation and flags generation is large. The computation + * of the flags depends on the original contents of the guarding + * predicate. If the destination overwrites the guarding predicate, + * then the easiest way to get this right is to save a copy. + */ + int tofs = gofs; + if (a->rd == a->pg) { + tofs = offsetof(CPUARMState, vfp.preg_tmp); + tcg_gen_gvec_mov(0, tofs, gofs, psz, psz); + } + + tcg_gen_gvec_4(dofs, nofs, mofs, gofs, psz, psz, gvec_op); + do_predtest(s, dofs, tofs, psz / 8); + } + return true; +} + +static void gen_and_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_and_i64(pd, pn, pm); + tcg_gen_and_i64(pd, pd, pg); +} + +static void gen_and_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_and_vec(vece, pd, pn, pm); + tcg_gen_and_vec(vece, pd, pd, pg); +} + +static bool trans_AND_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_and_pg_i64, + .fniv = gen_and_pg_vec, + .fno = gen_helper_sve_and_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else if (a->rn == a->rm) { + if (a->pg == a->rn) { + return do_mov_p(s, a->rd, a->rn); + } else { + return do_vector3_p(s, tcg_gen_gvec_and, 0, a->rd, a->rn, a->pg); + } + } else if (a->pg == a->rn || a->pg == a->rm) { + return do_vector3_p(s, tcg_gen_gvec_and, 0, a->rd, a->rn, a->rm); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_bic_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_andc_i64(pd, pn, pm); + tcg_gen_and_i64(pd, pd, pg); +} + +static void gen_bic_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_andc_vec(vece, pd, pn, pm); + tcg_gen_and_vec(vece, pd, pd, pg); +} + +static bool trans_BIC_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_bic_pg_i64, + .fniv = gen_bic_pg_vec, + .fno = gen_helper_sve_bic_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else if (a->pg == a->rn) { + return do_vector3_p(s, tcg_gen_gvec_andc, 0, a->rd, a->rn, a->rm); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_eor_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_xor_i64(pd, pn, pm); + tcg_gen_and_i64(pd, pd, pg); +} + +static void gen_eor_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_xor_vec(vece, pd, pn, pm); + tcg_gen_and_vec(vece, pd, pd, pg); +} + +static bool trans_EOR_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_eor_pg_i64, + .fniv = gen_eor_pg_vec, + .fno = gen_helper_sve_eor_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_sel_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_and_i64(pn, pn, pg); + tcg_gen_andc_i64(pm, pm, pg); + tcg_gen_or_i64(pd, pn, pm); +} + +static void gen_sel_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_and_vec(vece, pn, pn, pg); + tcg_gen_andc_vec(vece, pm, pm, pg); + tcg_gen_or_vec(vece, pd, pn, pm); +} + +static bool trans_SEL_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_sel_pg_i64, + .fniv = gen_sel_pg_vec, + .fno = gen_helper_sve_sel_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return false; + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_orr_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_or_i64(pd, pn, pm); + tcg_gen_and_i64(pd, pd, pg); +} + +static void gen_orr_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_or_vec(vece, pd, pn, pm); + tcg_gen_and_vec(vece, pd, pd, pg); +} + +static bool trans_ORR_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_orr_pg_i64, + .fniv = gen_orr_pg_vec, + .fno = gen_helper_sve_orr_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else if (a->pg == a->rn && a->rn == a->rm) { + return do_mov_p(s, a->rd, a->rn); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_orn_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_orc_i64(pd, pn, pm); + tcg_gen_and_i64(pd, pd, pg); +} + +static void gen_orn_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_orc_vec(vece, pd, pn, pm); + tcg_gen_and_vec(vece, pd, pd, pg); +} + +static bool trans_ORN_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_orn_pg_i64, + .fniv = gen_orn_pg_vec, + .fno = gen_helper_sve_orn_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_nor_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_or_i64(pd, pn, pm); + tcg_gen_andc_i64(pd, pg, pd); +} + +static void gen_nor_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_or_vec(vece, pd, pn, pm); + tcg_gen_andc_vec(vece, pd, pg, pd); +} + +static bool trans_NOR_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_nor_pg_i64, + .fniv = gen_nor_pg_vec, + .fno = gen_helper_sve_nor_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_nand_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_and_i64(pd, pn, pm); + tcg_gen_andc_i64(pd, pg, pd); +} + +static void gen_nand_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_and_vec(vece, pd, pn, pm); + tcg_gen_andc_vec(vece, pd, pg, pd); +} + +static bool trans_NAND_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_nand_pg_i64, + .fniv = gen_nand_pg_vec, + .fno = gen_helper_sve_nand_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + /* *** SVE Predicate Misc Group */ From 028e2a7b876631eff165cac59eb43bdb2dcc213b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0594/2380] target/arm: Implement SVE Predicate Misc Group Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 4 + target/arm/helper-sve.h | 3 + target/arm/sve.decode | 31 ++++++ target/arm/sve_helper.c | 84 +++++++++++++++ target/arm/translate-sve.c | 209 +++++++++++++++++++++++++++++++++++++ 5 files changed, 331 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index df21e143cc..8488273c5b 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -540,6 +540,7 @@ typedef struct CPUARMState { #ifdef TARGET_AARCH64 /* Store FFR as pregs[16] to make it easier to treat as any other. */ +#define FFR_PRED_NUM 16 ARMPredicateReg pregs[17]; /* Scratch space for aa64 sve predicate temporary. */ ARMPredicateReg preg_tmp; @@ -2975,4 +2976,7 @@ static inline uint64_t *aa64_vfp_qreg(CPUARMState *env, unsigned regno) return &env->vfp.zregs[regno].d[0]; } +/* Shared between translate-sve.c and sve_helper.c. */ +extern const uint64_t pred_esz_masks[4]; + #endif diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 57adc4d912..0c04afff8c 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -20,6 +20,9 @@ DEF_HELPER_FLAGS_2(sve_predtest1, TCG_CALL_NO_WG, i32, i64, i64) DEF_HELPER_FLAGS_3(sve_predtest, TCG_CALL_NO_WG, i32, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_pfirst, TCG_CALL_NO_WG, i32, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_pnext, TCG_CALL_NO_WG, i32, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index f695dda3b1..a390abb537 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -29,6 +29,7 @@ # when creating helpers common to those for the individual # instruction patterns. +&rr_esz rd rn esz &rri rd rn imm &rrr_esz rd rn rm esz &rprr_s rd pg rn rm s @@ -37,6 +38,12 @@ # Named instruction formats. These are generally used to # reduce the amount of duplication between instruction patterns. +# Two operand with unused vector element size +@pd_pn_e0 ........ ........ ....... rn:4 . rd:4 &rr_esz esz=0 + +# Two operand +@pd_pn ........ esz:2 .. .... ....... rn:4 . rd:4 &rr_esz + # Three operand with unused vector element size @rd_rn_rm_e0 ........ ... rm:5 ... ... rn:5 rd:5 &rrr_esz esz=0 @@ -77,6 +84,30 @@ NAND_pppp 00100101 1. 00 .... 01 .... 1 .... 1 .... @pd_pg_pn_pm_s # SVE predicate test PTEST 00100101 01 010000 11 pg:4 0 rn:4 0 0000 +# SVE predicate initialize +PTRUE 00100101 esz:2 01100 s:1 111000 pat:5 0 rd:4 + +# SVE initialize FFR +SETFFR 00100101 0010 1100 1001 0000 0000 0000 + +# SVE zero predicate register +PFALSE 00100101 0001 1000 1110 0100 0000 rd:4 + +# SVE predicate read from FFR (predicated) +RDFFR_p 00100101 0 s:1 0110001111000 pg:4 0 rd:4 + +# SVE predicate read from FFR (unpredicated) +RDFFR 00100101 0001 1001 1111 0000 0000 rd:4 + +# SVE FFR write from predicate (WRFFR) +WRFFR 00100101 0010 1000 1001 000 rn:4 00000 + +# SVE predicate first active +PFIRST 00100101 01 011 000 11000 00 .... 0 .... @pd_pn_e0 + +# SVE predicate next active +PNEXT 00100101 .. 011 001 11000 10 .... 0 .... @pd_pn + ### SVE Memory - 32-bit Gather and Unsized Contiguous Group # SVE load predicate register diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 2eda6f2ef1..cc164edfe8 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -115,3 +115,87 @@ LOGICAL_PPPP(sve_nand_pppp, DO_NAND) #undef DO_NAND #undef DO_SEL #undef LOGICAL_PPPP + +/* Similar to the ARM LastActiveElement pseudocode function, except the + result is multiplied by the element size. This includes the not found + indication; e.g. not found for esz=3 is -8. */ +static intptr_t last_active_element(uint64_t *g, intptr_t words, intptr_t esz) +{ + uint64_t mask = pred_esz_masks[esz]; + intptr_t i = words; + + do { + uint64_t this_g = g[--i] & mask; + if (this_g) { + return i * 64 + (63 - clz64(this_g)); + } + } while (i > 0); + return (intptr_t)-1 << esz; +} + +uint32_t HELPER(sve_pfirst)(void *vd, void *vg, uint32_t words) +{ + uint32_t flags = PREDTEST_INIT; + uint64_t *d = vd, *g = vg; + intptr_t i = 0; + + do { + uint64_t this_d = d[i]; + uint64_t this_g = g[i]; + + if (this_g) { + if (!(flags & 4)) { + /* Set in D the first bit of G. */ + this_d |= this_g & -this_g; + d[i] = this_d; + } + flags = iter_predtest_fwd(this_d, this_g, flags); + } + } while (++i < words); + + return flags; +} + +uint32_t HELPER(sve_pnext)(void *vd, void *vg, uint32_t pred_desc) +{ + intptr_t words = extract32(pred_desc, 0, SIMD_OPRSZ_BITS); + intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + uint32_t flags = PREDTEST_INIT; + uint64_t *d = vd, *g = vg, esz_mask; + intptr_t i, next; + + next = last_active_element(vd, words, esz) + (1 << esz); + esz_mask = pred_esz_masks[esz]; + + /* Similar to the pseudocode for pnext, but scaled by ESZ + so that we find the correct bit. */ + if (next < words * 64) { + uint64_t mask = -1; + + if (next & 63) { + mask = ~((1ull << (next & 63)) - 1); + next &= -64; + } + do { + uint64_t this_g = g[next / 64] & esz_mask & mask; + if (this_g != 0) { + next = (next & -64) + ctz64(this_g); + break; + } + next += 64; + mask = -1; + } while (next < words * 64); + } + + i = 0; + do { + uint64_t this_d = 0; + if (i == next / 64) { + this_d = 1ull << (next & 63); + } + d[i] = this_d; + flags = iter_predtest_fwd(this_d, g[i] & esz_mask, flags); + } while (++i < words); + + return flags; +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 67fb3091ac..4bb40da119 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -22,6 +22,7 @@ #include "exec/exec-all.h" #include "tcg-op.h" #include "tcg-op-gvec.h" +#include "tcg-gvec-desc.h" #include "qemu/log.h" #include "arm_ldst.h" #include "translate.h" @@ -192,6 +193,12 @@ static void do_predtest(DisasContext *s, int dofs, int gofs, int words) tcg_temp_free_i32(t); } +/* For each element size, the bits within a predicate word that are active. */ +const uint64_t pred_esz_masks[4] = { + 0xffffffffffffffffull, 0x5555555555555555ull, + 0x1111111111111111ull, 0x0101010101010101ull +}; + /* *** SVE Logical - Unpredicated Group */ @@ -541,6 +548,208 @@ static bool trans_PTEST(DisasContext *s, arg_PTEST *a, uint32_t insn) return true; } +/* See the ARM pseudocode DecodePredCount. */ +static unsigned decode_pred_count(unsigned fullsz, int pattern, int esz) +{ + unsigned elements = fullsz >> esz; + unsigned bound; + + switch (pattern) { + case 0x0: /* POW2 */ + return pow2floor(elements); + case 0x1: /* VL1 */ + case 0x2: /* VL2 */ + case 0x3: /* VL3 */ + case 0x4: /* VL4 */ + case 0x5: /* VL5 */ + case 0x6: /* VL6 */ + case 0x7: /* VL7 */ + case 0x8: /* VL8 */ + bound = pattern; + break; + case 0x9: /* VL16 */ + case 0xa: /* VL32 */ + case 0xb: /* VL64 */ + case 0xc: /* VL128 */ + case 0xd: /* VL256 */ + bound = 16 << (pattern - 9); + break; + case 0x1d: /* MUL4 */ + return elements - elements % 4; + case 0x1e: /* MUL3 */ + return elements - elements % 3; + case 0x1f: /* ALL */ + return elements; + default: /* #uimm5 */ + return 0; + } + return elements >= bound ? bound : 0; +} + +/* This handles all of the predicate initialization instructions, + * PTRUE, PFALSE, SETFFR. For PFALSE, we will have set PAT == 32 + * so that decode_pred_count returns 0. For SETFFR, we will have + * set RD == 16 == FFR. + */ +static bool do_predset(DisasContext *s, int esz, int rd, int pat, bool setflag) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned fullsz = vec_full_reg_size(s); + unsigned ofs = pred_full_reg_offset(s, rd); + unsigned numelem, setsz, i; + uint64_t word, lastword; + TCGv_i64 t; + + numelem = decode_pred_count(fullsz, pat, esz); + + /* Determine what we must store into each bit, and how many. */ + if (numelem == 0) { + lastword = word = 0; + setsz = fullsz; + } else { + setsz = numelem << esz; + lastword = word = pred_esz_masks[esz]; + if (setsz % 64) { + lastword &= ~(-1ull << (setsz % 64)); + } + } + + t = tcg_temp_new_i64(); + if (fullsz <= 64) { + tcg_gen_movi_i64(t, lastword); + tcg_gen_st_i64(t, cpu_env, ofs); + goto done; + } + + if (word == lastword) { + unsigned maxsz = size_for_gvec(fullsz / 8); + unsigned oprsz = size_for_gvec(setsz / 8); + + if (oprsz * 8 == setsz) { + tcg_gen_gvec_dup64i(ofs, oprsz, maxsz, word); + goto done; + } + if (oprsz * 8 == setsz + 8) { + tcg_gen_gvec_dup64i(ofs, oprsz, maxsz, word); + tcg_gen_movi_i64(t, 0); + tcg_gen_st_i64(t, cpu_env, ofs + oprsz - 8); + goto done; + } + } + + setsz /= 8; + fullsz /= 8; + + tcg_gen_movi_i64(t, word); + for (i = 0; i < setsz; i += 8) { + tcg_gen_st_i64(t, cpu_env, ofs + i); + } + if (lastword != word) { + tcg_gen_movi_i64(t, lastword); + tcg_gen_st_i64(t, cpu_env, ofs + i); + i += 8; + } + if (i < fullsz) { + tcg_gen_movi_i64(t, 0); + for (; i < fullsz; i += 8) { + tcg_gen_st_i64(t, cpu_env, ofs + i); + } + } + + done: + tcg_temp_free_i64(t); + + /* PTRUES */ + if (setflag) { + tcg_gen_movi_i32(cpu_NF, -(word != 0)); + tcg_gen_movi_i32(cpu_CF, word == 0); + tcg_gen_movi_i32(cpu_VF, 0); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); + } + return true; +} + +static bool trans_PTRUE(DisasContext *s, arg_PTRUE *a, uint32_t insn) +{ + return do_predset(s, a->esz, a->rd, a->pat, a->s); +} + +static bool trans_SETFFR(DisasContext *s, arg_SETFFR *a, uint32_t insn) +{ + /* Note pat == 31 is #all, to set all elements. */ + return do_predset(s, 0, FFR_PRED_NUM, 31, false); +} + +static bool trans_PFALSE(DisasContext *s, arg_PFALSE *a, uint32_t insn) +{ + /* Note pat == 32 is #unimp, to set no elements. */ + return do_predset(s, 0, a->rd, 32, false); +} + +static bool trans_RDFFR_p(DisasContext *s, arg_RDFFR_p *a, uint32_t insn) +{ + /* The path through do_pppp_flags is complicated enough to want to avoid + * duplication. Frob the arguments into the form of a predicated AND. + */ + arg_rprr_s alt_a = { + .rd = a->rd, .pg = a->pg, .s = a->s, + .rn = FFR_PRED_NUM, .rm = FFR_PRED_NUM, + }; + return trans_AND_pppp(s, &alt_a, insn); +} + +static bool trans_RDFFR(DisasContext *s, arg_RDFFR *a, uint32_t insn) +{ + return do_mov_p(s, a->rd, FFR_PRED_NUM); +} + +static bool trans_WRFFR(DisasContext *s, arg_WRFFR *a, uint32_t insn) +{ + return do_mov_p(s, FFR_PRED_NUM, a->rn); +} + +static bool do_pfirst_pnext(DisasContext *s, arg_rr_esz *a, + void (*gen_fn)(TCGv_i32, TCGv_ptr, + TCGv_ptr, TCGv_i32)) +{ + if (!sve_access_check(s)) { + return true; + } + + TCGv_ptr t_pd = tcg_temp_new_ptr(); + TCGv_ptr t_pg = tcg_temp_new_ptr(); + TCGv_i32 t; + unsigned desc; + + desc = DIV_ROUND_UP(pred_full_reg_size(s), 8); + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); + + tcg_gen_addi_ptr(t_pd, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->rn)); + t = tcg_const_i32(desc); + + gen_fn(t, t_pd, t_pg, t); + tcg_temp_free_ptr(t_pd); + tcg_temp_free_ptr(t_pg); + + do_pred_flags(t); + tcg_temp_free_i32(t); + return true; +} + +static bool trans_PFIRST(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + return do_pfirst_pnext(s, a, gen_helper_sve_pfirst); +} + +static bool trans_PNEXT(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + return do_pfirst_pnext(s, a, gen_helper_sve_pnext); +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From f97cfd596ed9bd38644323cb61d19b85ac703c81 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0595/2380] target/arm: Implement SVE Integer Binary Arithmetic - Predicated Group Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 145 +++++++++++++++++++++++++++ target/arm/sve.decode | 42 ++++++++ target/arm/sve_helper.c | 194 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 68 +++++++++++++ 4 files changed, 449 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 0c04afff8c..5b82ba1501 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -23,6 +23,151 @@ DEF_HELPER_FLAGS_3(sve_predtest, TCG_CALL_NO_WG, i32, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_pfirst, TCG_CALL_NO_WG, i32, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_pnext, TCG_CALL_NO_WG, i32, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_and_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_and_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_and_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_and_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_eor_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_eor_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_eor_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_eor_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_orr_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_orr_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_orr_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_orr_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_bic_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_bic_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_bic_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_bic_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_add_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_add_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_add_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_add_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_sub_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sub_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sub_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sub_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_smax_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smax_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smax_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smax_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_umax_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umax_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umax_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umax_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_smin_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smin_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smin_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smin_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_umin_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umin_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umin_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umin_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_sabd_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sabd_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sabd_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sabd_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_uabd_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_uabd_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_uabd_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_uabd_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_mul_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_mul_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_mul_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_mul_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_smulh_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smulh_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smulh_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smulh_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_umulh_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umulh_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umulh_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umulh_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_sdiv_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sdiv_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_udiv_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_udiv_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index a390abb537..c444357c82 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -24,6 +24,10 @@ %imm9_16_10 16:s6 10:3 +# Either a copy of rd (at bit 0), or a different source +# as propagated via the MOVPRFX instruction. +%reg_movprfx 0:5 + ########################################################################### # Named attribute sets. These are used to make nice(er) names # when creating helpers common to those for the individual @@ -33,6 +37,7 @@ &rri rd rn imm &rrr_esz rd rn rm esz &rprr_s rd pg rn rm s +&rprr_esz rd pg rn rm esz ########################################################################### # Named instruction formats. These are generally used to @@ -50,6 +55,12 @@ # Three predicate operand, with governing predicate, flag setting @pd_pg_pn_pm_s ........ . s:1 .. rm:4 .. pg:4 . rn:4 . rd:4 &rprr_s +# Two register operand, with governing predicate, vector element size +@rdn_pg_rm ........ esz:2 ... ... ... pg:3 rm:5 rd:5 \ + &rprr_esz rn=%reg_movprfx +@rdm_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 \ + &rprr_esz rm=%reg_movprfx + # Basic Load/Store with 9-bit immediate offset @pd_rn_i9 ........ ........ ...... rn:5 . rd:4 \ &rri imm=%imm9_16_10 @@ -59,6 +70,37 @@ ########################################################################### # Instruction patterns. Grouped according to the SVE encodingindex.xhtml. +### SVE Integer Arithmetic - Binary Predicated Group + +# SVE bitwise logical vector operations (predicated) +ORR_zpzz 00000100 .. 011 000 000 ... ..... ..... @rdn_pg_rm +EOR_zpzz 00000100 .. 011 001 000 ... ..... ..... @rdn_pg_rm +AND_zpzz 00000100 .. 011 010 000 ... ..... ..... @rdn_pg_rm +BIC_zpzz 00000100 .. 011 011 000 ... ..... ..... @rdn_pg_rm + +# SVE integer add/subtract vectors (predicated) +ADD_zpzz 00000100 .. 000 000 000 ... ..... ..... @rdn_pg_rm +SUB_zpzz 00000100 .. 000 001 000 ... ..... ..... @rdn_pg_rm +SUB_zpzz 00000100 .. 000 011 000 ... ..... ..... @rdm_pg_rn # SUBR + +# SVE integer min/max/difference (predicated) +SMAX_zpzz 00000100 .. 001 000 000 ... ..... ..... @rdn_pg_rm +UMAX_zpzz 00000100 .. 001 001 000 ... ..... ..... @rdn_pg_rm +SMIN_zpzz 00000100 .. 001 010 000 ... ..... ..... @rdn_pg_rm +UMIN_zpzz 00000100 .. 001 011 000 ... ..... ..... @rdn_pg_rm +SABD_zpzz 00000100 .. 001 100 000 ... ..... ..... @rdn_pg_rm +UABD_zpzz 00000100 .. 001 101 000 ... ..... ..... @rdn_pg_rm + +# SVE integer multiply/divide (predicated) +MUL_zpzz 00000100 .. 010 000 000 ... ..... ..... @rdn_pg_rm +SMULH_zpzz 00000100 .. 010 010 000 ... ..... ..... @rdn_pg_rm +UMULH_zpzz 00000100 .. 010 011 000 ... ..... ..... @rdn_pg_rm +# Note that divide requires size >= 2; below 2 is unallocated. +SDIV_zpzz 00000100 .. 010 100 000 ... ..... ..... @rdn_pg_rm +UDIV_zpzz 00000100 .. 010 101 000 ... ..... ..... @rdn_pg_rm +SDIV_zpzz 00000100 .. 010 110 000 ... ..... ..... @rdm_pg_rn # SDIVR +UDIV_zpzz 00000100 .. 010 111 000 ... ..... ..... @rdm_pg_rn # UDIVR + ### SVE Logical - Unpredicated Group # SVE bitwise logical operations (unpredicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index cc164edfe8..b8c8a06122 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -25,6 +25,22 @@ #include "tcg/tcg-gvec-desc.h" +/* Note that vector data is stored in host-endian 64-bit chunks, + so addressing units smaller than that needs a host-endian fixup. */ +#ifdef HOST_WORDS_BIGENDIAN +#define H1(x) ((x) ^ 7) +#define H1_2(x) ((x) ^ 6) +#define H1_4(x) ((x) ^ 4) +#define H2(x) ((x) ^ 3) +#define H4(x) ((x) ^ 1) +#else +#define H1(x) (x) +#define H1_2(x) (x) +#define H1_4(x) (x) +#define H2(x) (x) +#define H4(x) (x) +#endif + /* Return a value for NZCV as per the ARM PredTest pseudofunction. * * The return value has bit 31 set if N is set, bit 1 set if Z is clear, @@ -116,6 +132,184 @@ LOGICAL_PPPP(sve_nand_pppp, DO_NAND) #undef DO_SEL #undef LOGICAL_PPPP +/* Fully general three-operand expander, controlled by a predicate. + * This is complicated by the host-endian storage of the register file. + */ +/* ??? I don't expect the compiler could ever vectorize this itself. + * With some tables we can convert bit masks to byte masks, and with + * extra care wrt byte/word ordering we could use gcc generic vectors + * and do 16 bytes at a time. + */ +#define DO_ZPZZ(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + TYPE mm = *(TYPE *)(vm + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, mm); \ + } \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +/* Similarly, specialized for 64-bit operands. */ +#define DO_ZPZZ_D(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + TYPE *d = vd, *n = vn, *m = vm; \ + uint8_t *pg = vg; \ + for (i = 0; i < opr_sz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + TYPE nn = n[i], mm = m[i]; \ + d[i] = OP(nn, mm); \ + } \ + } \ +} + +#define DO_AND(N, M) (N & M) +#define DO_EOR(N, M) (N ^ M) +#define DO_ORR(N, M) (N | M) +#define DO_BIC(N, M) (N & ~M) +#define DO_ADD(N, M) (N + M) +#define DO_SUB(N, M) (N - M) +#define DO_MAX(N, M) ((N) >= (M) ? (N) : (M)) +#define DO_MIN(N, M) ((N) >= (M) ? (M) : (N)) +#define DO_ABD(N, M) ((N) >= (M) ? (N) - (M) : (M) - (N)) +#define DO_MUL(N, M) (N * M) +#define DO_DIV(N, M) (M ? N / M : 0) + +DO_ZPZZ(sve_and_zpzz_b, uint8_t, H1, DO_AND) +DO_ZPZZ(sve_and_zpzz_h, uint16_t, H1_2, DO_AND) +DO_ZPZZ(sve_and_zpzz_s, uint32_t, H1_4, DO_AND) +DO_ZPZZ_D(sve_and_zpzz_d, uint64_t, DO_AND) + +DO_ZPZZ(sve_orr_zpzz_b, uint8_t, H1, DO_ORR) +DO_ZPZZ(sve_orr_zpzz_h, uint16_t, H1_2, DO_ORR) +DO_ZPZZ(sve_orr_zpzz_s, uint32_t, H1_4, DO_ORR) +DO_ZPZZ_D(sve_orr_zpzz_d, uint64_t, DO_ORR) + +DO_ZPZZ(sve_eor_zpzz_b, uint8_t, H1, DO_EOR) +DO_ZPZZ(sve_eor_zpzz_h, uint16_t, H1_2, DO_EOR) +DO_ZPZZ(sve_eor_zpzz_s, uint32_t, H1_4, DO_EOR) +DO_ZPZZ_D(sve_eor_zpzz_d, uint64_t, DO_EOR) + +DO_ZPZZ(sve_bic_zpzz_b, uint8_t, H1, DO_BIC) +DO_ZPZZ(sve_bic_zpzz_h, uint16_t, H1_2, DO_BIC) +DO_ZPZZ(sve_bic_zpzz_s, uint32_t, H1_4, DO_BIC) +DO_ZPZZ_D(sve_bic_zpzz_d, uint64_t, DO_BIC) + +DO_ZPZZ(sve_add_zpzz_b, uint8_t, H1, DO_ADD) +DO_ZPZZ(sve_add_zpzz_h, uint16_t, H1_2, DO_ADD) +DO_ZPZZ(sve_add_zpzz_s, uint32_t, H1_4, DO_ADD) +DO_ZPZZ_D(sve_add_zpzz_d, uint64_t, DO_ADD) + +DO_ZPZZ(sve_sub_zpzz_b, uint8_t, H1, DO_SUB) +DO_ZPZZ(sve_sub_zpzz_h, uint16_t, H1_2, DO_SUB) +DO_ZPZZ(sve_sub_zpzz_s, uint32_t, H1_4, DO_SUB) +DO_ZPZZ_D(sve_sub_zpzz_d, uint64_t, DO_SUB) + +DO_ZPZZ(sve_smax_zpzz_b, int8_t, H1, DO_MAX) +DO_ZPZZ(sve_smax_zpzz_h, int16_t, H1_2, DO_MAX) +DO_ZPZZ(sve_smax_zpzz_s, int32_t, H1_4, DO_MAX) +DO_ZPZZ_D(sve_smax_zpzz_d, int64_t, DO_MAX) + +DO_ZPZZ(sve_umax_zpzz_b, uint8_t, H1, DO_MAX) +DO_ZPZZ(sve_umax_zpzz_h, uint16_t, H1_2, DO_MAX) +DO_ZPZZ(sve_umax_zpzz_s, uint32_t, H1_4, DO_MAX) +DO_ZPZZ_D(sve_umax_zpzz_d, uint64_t, DO_MAX) + +DO_ZPZZ(sve_smin_zpzz_b, int8_t, H1, DO_MIN) +DO_ZPZZ(sve_smin_zpzz_h, int16_t, H1_2, DO_MIN) +DO_ZPZZ(sve_smin_zpzz_s, int32_t, H1_4, DO_MIN) +DO_ZPZZ_D(sve_smin_zpzz_d, int64_t, DO_MIN) + +DO_ZPZZ(sve_umin_zpzz_b, uint8_t, H1, DO_MIN) +DO_ZPZZ(sve_umin_zpzz_h, uint16_t, H1_2, DO_MIN) +DO_ZPZZ(sve_umin_zpzz_s, uint32_t, H1_4, DO_MIN) +DO_ZPZZ_D(sve_umin_zpzz_d, uint64_t, DO_MIN) + +DO_ZPZZ(sve_sabd_zpzz_b, int8_t, H1, DO_ABD) +DO_ZPZZ(sve_sabd_zpzz_h, int16_t, H1_2, DO_ABD) +DO_ZPZZ(sve_sabd_zpzz_s, int32_t, H1_4, DO_ABD) +DO_ZPZZ_D(sve_sabd_zpzz_d, int64_t, DO_ABD) + +DO_ZPZZ(sve_uabd_zpzz_b, uint8_t, H1, DO_ABD) +DO_ZPZZ(sve_uabd_zpzz_h, uint16_t, H1_2, DO_ABD) +DO_ZPZZ(sve_uabd_zpzz_s, uint32_t, H1_4, DO_ABD) +DO_ZPZZ_D(sve_uabd_zpzz_d, uint64_t, DO_ABD) + +/* Because the computation type is at least twice as large as required, + these work for both signed and unsigned source types. */ +static inline uint8_t do_mulh_b(int32_t n, int32_t m) +{ + return (n * m) >> 8; +} + +static inline uint16_t do_mulh_h(int32_t n, int32_t m) +{ + return (n * m) >> 16; +} + +static inline uint32_t do_mulh_s(int64_t n, int64_t m) +{ + return (n * m) >> 32; +} + +static inline uint64_t do_smulh_d(uint64_t n, uint64_t m) +{ + uint64_t lo, hi; + muls64(&lo, &hi, n, m); + return hi; +} + +static inline uint64_t do_umulh_d(uint64_t n, uint64_t m) +{ + uint64_t lo, hi; + mulu64(&lo, &hi, n, m); + return hi; +} + +DO_ZPZZ(sve_mul_zpzz_b, uint8_t, H1, DO_MUL) +DO_ZPZZ(sve_mul_zpzz_h, uint16_t, H1_2, DO_MUL) +DO_ZPZZ(sve_mul_zpzz_s, uint32_t, H1_4, DO_MUL) +DO_ZPZZ_D(sve_mul_zpzz_d, uint64_t, DO_MUL) + +DO_ZPZZ(sve_smulh_zpzz_b, int8_t, H1, do_mulh_b) +DO_ZPZZ(sve_smulh_zpzz_h, int16_t, H1_2, do_mulh_h) +DO_ZPZZ(sve_smulh_zpzz_s, int32_t, H1_4, do_mulh_s) +DO_ZPZZ_D(sve_smulh_zpzz_d, uint64_t, do_smulh_d) + +DO_ZPZZ(sve_umulh_zpzz_b, uint8_t, H1, do_mulh_b) +DO_ZPZZ(sve_umulh_zpzz_h, uint16_t, H1_2, do_mulh_h) +DO_ZPZZ(sve_umulh_zpzz_s, uint32_t, H1_4, do_mulh_s) +DO_ZPZZ_D(sve_umulh_zpzz_d, uint64_t, do_umulh_d) + +DO_ZPZZ(sve_sdiv_zpzz_s, int32_t, H1_4, DO_DIV) +DO_ZPZZ_D(sve_sdiv_zpzz_d, int64_t, DO_DIV) + +DO_ZPZZ(sve_udiv_zpzz_s, uint32_t, H1_4, DO_DIV) +DO_ZPZZ_D(sve_udiv_zpzz_d, uint64_t, DO_DIV) + +#undef DO_ZPZZ +#undef DO_ZPZZ_D +#undef DO_AND +#undef DO_ORR +#undef DO_EOR +#undef DO_BIC +#undef DO_ADD +#undef DO_SUB +#undef DO_MAX +#undef DO_MIN +#undef DO_ABD +#undef DO_MUL +#undef DO_DIV + /* Similar to the ARM LastActiveElement pseudocode function, except the result is multiplied by the element size. This includes the not found indication; e.g. not found for esz=3 is -8. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 4bb40da119..bba04ea0af 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -227,6 +227,74 @@ static bool trans_BIC_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) return do_vector3_z(s, tcg_gen_gvec_andc, 0, a->rd, a->rn, a->rm); } +/* + *** SVE Integer Arithmetic - Binary Predicated Group + */ + +static bool do_zpzz_ool(DisasContext *s, arg_rprr_esz *a, gen_helper_gvec_4 *fn) +{ + unsigned vsz = vec_full_reg_size(s); + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + tcg_gen_gvec_4_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + vsz, vsz, 0, fn); + } + return true; +} + +#define DO_ZPZZ(NAME, name) \ +static bool trans_##NAME##_zpzz(DisasContext *s, arg_rprr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_4 * const fns[4] = { \ + gen_helper_sve_##name##_zpzz_b, gen_helper_sve_##name##_zpzz_h, \ + gen_helper_sve_##name##_zpzz_s, gen_helper_sve_##name##_zpzz_d, \ + }; \ + return do_zpzz_ool(s, a, fns[a->esz]); \ +} + +DO_ZPZZ(AND, and) +DO_ZPZZ(EOR, eor) +DO_ZPZZ(ORR, orr) +DO_ZPZZ(BIC, bic) + +DO_ZPZZ(ADD, add) +DO_ZPZZ(SUB, sub) + +DO_ZPZZ(SMAX, smax) +DO_ZPZZ(UMAX, umax) +DO_ZPZZ(SMIN, smin) +DO_ZPZZ(UMIN, umin) +DO_ZPZZ(SABD, sabd) +DO_ZPZZ(UABD, uabd) + +DO_ZPZZ(MUL, mul) +DO_ZPZZ(SMULH, smulh) +DO_ZPZZ(UMULH, umulh) + +static bool trans_SDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_4 * const fns[4] = { + NULL, NULL, gen_helper_sve_sdiv_zpzz_s, gen_helper_sve_sdiv_zpzz_d + }; + return do_zpzz_ool(s, a, fns[a->esz]); +} + +static bool trans_UDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_4 * const fns[4] = { + NULL, NULL, gen_helper_sve_udiv_zpzz_s, gen_helper_sve_udiv_zpzz_d + }; + return do_zpzz_ool(s, a, fns[a->esz]); +} + +#undef DO_ZPZZ + /* *** SVE Predicate Logical Operations Group */ From 047cec971d2791b206677b954227ea92ff7ee3db Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0596/2380] target/arm: Implement SVE Integer Reduction Group Excepting MOVPRFX, which isn't a reduction. Presumably it is placed within the group because of its encoding. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 44 ++++++++++++++++++ target/arm/sve.decode | 22 +++++++++ target/arm/sve_helper.c | 91 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 68 ++++++++++++++++++++++++++++ 4 files changed, 225 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 5b82ba1501..6b6bbeb272 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -168,6 +168,50 @@ DEF_HELPER_FLAGS_5(sve_udiv_zpzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_udiv_zpzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_orv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_orv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_orv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_orv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_eorv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_eorv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_eorv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_eorv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_andv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_andv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_andv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_andv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_saddv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_saddv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_saddv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_uaddv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uaddv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uaddv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uaddv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_smaxv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_smaxv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_smaxv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_smaxv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_umaxv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_umaxv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_umaxv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_umaxv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_sminv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_sminv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_sminv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_sminv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_uminv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uminv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uminv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uminv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index c444357c82..ba27ff0979 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -36,6 +36,7 @@ &rr_esz rd rn esz &rri rd rn imm &rrr_esz rd rn rm esz +&rpr_esz rd pg rn esz &rprr_s rd pg rn rm s &rprr_esz rd pg rn rm esz @@ -61,6 +62,9 @@ @rdm_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 \ &rprr_esz rm=%reg_movprfx +# One register operand, with governing predicate, vector element size +@rd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 &rpr_esz + # Basic Load/Store with 9-bit immediate offset @pd_rn_i9 ........ ........ ...... rn:5 . rd:4 \ &rri imm=%imm9_16_10 @@ -101,6 +105,24 @@ UDIV_zpzz 00000100 .. 010 101 000 ... ..... ..... @rdn_pg_rm SDIV_zpzz 00000100 .. 010 110 000 ... ..... ..... @rdm_pg_rn # SDIVR UDIV_zpzz 00000100 .. 010 111 000 ... ..... ..... @rdm_pg_rn # UDIVR +### SVE Integer Reduction Group + +# SVE bitwise logical reduction (predicated) +ORV 00000100 .. 011 000 001 ... ..... ..... @rd_pg_rn +EORV 00000100 .. 011 001 001 ... ..... ..... @rd_pg_rn +ANDV 00000100 .. 011 010 001 ... ..... ..... @rd_pg_rn + +# SVE integer add reduction (predicated) +# Note that saddv requires size != 3. +UADDV 00000100 .. 000 001 001 ... ..... ..... @rd_pg_rn +SADDV 00000100 .. 000 000 001 ... ..... ..... @rd_pg_rn + +# SVE integer min/max reduction (predicated) +SMAXV 00000100 .. 001 000 001 ... ..... ..... @rd_pg_rn +UMAXV 00000100 .. 001 001 001 ... ..... ..... @rd_pg_rn +SMINV 00000100 .. 001 010 001 ... ..... ..... @rd_pg_rn +UMINV 00000100 .. 001 011 001 ... ..... ..... @rd_pg_rn + ### SVE Logical - Unpredicated Group # SVE bitwise logical operations (unpredicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index b8c8a06122..c1719e407a 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -298,6 +298,97 @@ DO_ZPZZ_D(sve_udiv_zpzz_d, uint64_t, DO_DIV) #undef DO_ZPZZ #undef DO_ZPZZ_D + +/* Two-operand reduction expander, controlled by a predicate. + * The difference between TYPERED and TYPERET has to do with + * sign-extension. E.g. for SMAX, TYPERED must be signed, + * but TYPERET must be unsigned so that e.g. a 32-bit value + * is not sign-extended to the ABI uint64_t return type. + */ +/* ??? If we were to vectorize this by hand the reduction ordering + * would change. For integer operands, this is perfectly fine. + */ +#define DO_VPZ(NAME, TYPEELT, TYPERED, TYPERET, H, INIT, OP) \ +uint64_t HELPER(NAME)(void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + TYPERED ret = INIT; \ + for (i = 0; i < opr_sz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPEELT nn = *(TYPEELT *)(vn + H(i)); \ + ret = OP(ret, nn); \ + } \ + i += sizeof(TYPEELT), pg >>= sizeof(TYPEELT); \ + } while (i & 15); \ + } \ + return (TYPERET)ret; \ +} + +#define DO_VPZ_D(NAME, TYPEE, TYPER, INIT, OP) \ +uint64_t HELPER(NAME)(void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + TYPEE *n = vn; \ + uint8_t *pg = vg; \ + TYPER ret = INIT; \ + for (i = 0; i < opr_sz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + TYPEE nn = n[i]; \ + ret = OP(ret, nn); \ + } \ + } \ + return ret; \ +} + +DO_VPZ(sve_orv_b, uint8_t, uint8_t, uint8_t, H1, 0, DO_ORR) +DO_VPZ(sve_orv_h, uint16_t, uint16_t, uint16_t, H1_2, 0, DO_ORR) +DO_VPZ(sve_orv_s, uint32_t, uint32_t, uint32_t, H1_4, 0, DO_ORR) +DO_VPZ_D(sve_orv_d, uint64_t, uint64_t, 0, DO_ORR) + +DO_VPZ(sve_eorv_b, uint8_t, uint8_t, uint8_t, H1, 0, DO_EOR) +DO_VPZ(sve_eorv_h, uint16_t, uint16_t, uint16_t, H1_2, 0, DO_EOR) +DO_VPZ(sve_eorv_s, uint32_t, uint32_t, uint32_t, H1_4, 0, DO_EOR) +DO_VPZ_D(sve_eorv_d, uint64_t, uint64_t, 0, DO_EOR) + +DO_VPZ(sve_andv_b, uint8_t, uint8_t, uint8_t, H1, -1, DO_AND) +DO_VPZ(sve_andv_h, uint16_t, uint16_t, uint16_t, H1_2, -1, DO_AND) +DO_VPZ(sve_andv_s, uint32_t, uint32_t, uint32_t, H1_4, -1, DO_AND) +DO_VPZ_D(sve_andv_d, uint64_t, uint64_t, -1, DO_AND) + +DO_VPZ(sve_saddv_b, int8_t, uint64_t, uint64_t, H1, 0, DO_ADD) +DO_VPZ(sve_saddv_h, int16_t, uint64_t, uint64_t, H1_2, 0, DO_ADD) +DO_VPZ(sve_saddv_s, int32_t, uint64_t, uint64_t, H1_4, 0, DO_ADD) + +DO_VPZ(sve_uaddv_b, uint8_t, uint64_t, uint64_t, H1, 0, DO_ADD) +DO_VPZ(sve_uaddv_h, uint16_t, uint64_t, uint64_t, H1_2, 0, DO_ADD) +DO_VPZ(sve_uaddv_s, uint32_t, uint64_t, uint64_t, H1_4, 0, DO_ADD) +DO_VPZ_D(sve_uaddv_d, uint64_t, uint64_t, 0, DO_ADD) + +DO_VPZ(sve_smaxv_b, int8_t, int8_t, uint8_t, H1, INT8_MIN, DO_MAX) +DO_VPZ(sve_smaxv_h, int16_t, int16_t, uint16_t, H1_2, INT16_MIN, DO_MAX) +DO_VPZ(sve_smaxv_s, int32_t, int32_t, uint32_t, H1_4, INT32_MIN, DO_MAX) +DO_VPZ_D(sve_smaxv_d, int64_t, int64_t, INT64_MIN, DO_MAX) + +DO_VPZ(sve_umaxv_b, uint8_t, uint8_t, uint8_t, H1, 0, DO_MAX) +DO_VPZ(sve_umaxv_h, uint16_t, uint16_t, uint16_t, H1_2, 0, DO_MAX) +DO_VPZ(sve_umaxv_s, uint32_t, uint32_t, uint32_t, H1_4, 0, DO_MAX) +DO_VPZ_D(sve_umaxv_d, uint64_t, uint64_t, 0, DO_MAX) + +DO_VPZ(sve_sminv_b, int8_t, int8_t, uint8_t, H1, INT8_MAX, DO_MIN) +DO_VPZ(sve_sminv_h, int16_t, int16_t, uint16_t, H1_2, INT16_MAX, DO_MIN) +DO_VPZ(sve_sminv_s, int32_t, int32_t, uint32_t, H1_4, INT32_MAX, DO_MIN) +DO_VPZ_D(sve_sminv_d, int64_t, int64_t, INT64_MAX, DO_MIN) + +DO_VPZ(sve_uminv_b, uint8_t, uint8_t, uint8_t, H1, -1, DO_MIN) +DO_VPZ(sve_uminv_h, uint16_t, uint16_t, uint16_t, H1_2, -1, DO_MIN) +DO_VPZ(sve_uminv_s, uint32_t, uint32_t, uint32_t, H1_4, -1, DO_MIN) +DO_VPZ_D(sve_uminv_d, uint64_t, uint64_t, -1, DO_MIN) + +#undef DO_VPZ +#undef DO_VPZ_D + #undef DO_AND #undef DO_ORR #undef DO_EOR diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index bba04ea0af..66ef01712d 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -295,6 +295,74 @@ static bool trans_UDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) #undef DO_ZPZZ +/* + *** SVE Integer Reduction Group + */ + +typedef void gen_helper_gvec_reduc(TCGv_i64, TCGv_ptr, TCGv_ptr, TCGv_i32); +static bool do_vpz_ool(DisasContext *s, arg_rpr_esz *a, + gen_helper_gvec_reduc *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_zn, t_pg; + TCGv_i32 desc; + TCGv_i64 temp; + + if (fn == NULL) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + temp = tcg_temp_new_i64(); + t_zn = tcg_temp_new_ptr(); + t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->pg)); + fn(temp, t_zn, t_pg, desc); + tcg_temp_free_ptr(t_zn); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(desc); + + write_fp_dreg(s, a->rd, temp); + tcg_temp_free_i64(temp); + return true; +} + +#define DO_VPZ(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_reduc * const fns[4] = { \ + gen_helper_sve_##name##_b, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d, \ + }; \ + return do_vpz_ool(s, a, fns[a->esz]); \ +} + +DO_VPZ(ORV, orv) +DO_VPZ(ANDV, andv) +DO_VPZ(EORV, eorv) + +DO_VPZ(UADDV, uaddv) +DO_VPZ(SMAXV, smaxv) +DO_VPZ(UMAXV, umaxv) +DO_VPZ(SMINV, sminv) +DO_VPZ(UMINV, uminv) + +static bool trans_SADDV(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_reduc * const fns[4] = { + gen_helper_sve_saddv_b, gen_helper_sve_saddv_h, + gen_helper_sve_saddv_s, NULL + }; + return do_vpz_ool(s, a, fns[a->esz]); +} + +#undef DO_VPZ + /* *** SVE Predicate Logical Operations Group */ From ccd841c3d71db6943f8b6d3d56bd2abb548ba40c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0597/2380] target/arm: Implement SVE bitwise shift by immediate (predicated) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 25 ++++ target/arm/sve.decode | 26 ++++ target/arm/sve_helper.c | 264 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 130 ++++++++++++++++++ 4 files changed, 445 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 6b6bbeb272..b3c89579af 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -212,6 +212,31 @@ DEF_HELPER_FLAGS_3(sve_uminv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_uminv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_uminv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_clr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_clr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_clr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_clr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_asr_zpzi_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asr_zpzi_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asr_zpzi_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asr_zpzi_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_lsr_zpzi_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsr_zpzi_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsr_zpzi_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsr_zpzi_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_lsl_zpzi_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsl_zpzi_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsl_zpzi_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsl_zpzi_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_asrd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asrd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asrd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asrd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index ba27ff0979..a1791c1d7b 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -22,8 +22,16 @@ ########################################################################### # Named fields. These are primarily for disjoint fields. +%imm6_22_5 22:1 5:5 %imm9_16_10 16:s6 10:3 +# A combination of tsz:imm3 -- extract esize. +%tszimm_esz 22:2 5:5 !function=tszimm_esz +# A combination of tsz:imm3 -- extract (2 * esize) - (tsz:imm3) +%tszimm_shr 22:2 5:5 !function=tszimm_shr +# A combination of tsz:imm3 -- extract (tsz:imm3) - esize +%tszimm_shl 22:2 5:5 !function=tszimm_shl + # Either a copy of rd (at bit 0), or a different source # as propagated via the MOVPRFX instruction. %reg_movprfx 0:5 @@ -39,6 +47,7 @@ &rpr_esz rd pg rn esz &rprr_s rd pg rn rm s &rprr_esz rd pg rn rm esz +&rpri_esz rd pg rn imm esz ########################################################################### # Named instruction formats. These are generally used to @@ -65,6 +74,11 @@ # One register operand, with governing predicate, vector element size @rd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 &rpr_esz +# Two register operand, one immediate operand, with predicate, +# element size encoded as TSZHL. User must fill in imm. +@rdn_pg_tszimm ........ .. ... ... ... pg:3 ..... rd:5 \ + &rpri_esz rn=%reg_movprfx esz=%tszimm_esz + # Basic Load/Store with 9-bit immediate offset @pd_rn_i9 ........ ........ ...... rn:5 . rd:4 \ &rri imm=%imm9_16_10 @@ -123,6 +137,18 @@ UMAXV 00000100 .. 001 001 001 ... ..... ..... @rd_pg_rn SMINV 00000100 .. 001 010 001 ... ..... ..... @rd_pg_rn UMINV 00000100 .. 001 011 001 ... ..... ..... @rd_pg_rn +### SVE Shift by Immediate - Predicated Group + +# SVE bitwise shift by immediate (predicated) +ASR_zpzi 00000100 .. 000 000 100 ... .. ... ..... \ + @rdn_pg_tszimm imm=%tszimm_shr +LSR_zpzi 00000100 .. 000 001 100 ... .. ... ..... \ + @rdn_pg_tszimm imm=%tszimm_shr +LSL_zpzi 00000100 .. 000 011 100 ... .. ... ..... \ + @rdn_pg_tszimm imm=%tszimm_shl +ASRD 00000100 .. 000 100 100 ... .. ... ..... \ + @rdn_pg_tszimm imm=%tszimm_shr + ### SVE Logical - Unpredicated Group # SVE bitwise logical operations (unpredicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index c1719e407a..b6b9a08965 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -93,6 +93,150 @@ uint32_t HELPER(sve_predtest)(void *vd, void *vg, uint32_t words) return flags; } +/* Expand active predicate bits to bytes, for byte elements. + * for (i = 0; i < 256; ++i) { + * unsigned long m = 0; + * for (j = 0; j < 8; j++) { + * if ((i >> j) & 1) { + * m |= 0xfful << (j << 3); + * } + * } + * printf("0x%016lx,\n", m); + * } + */ +static inline uint64_t expand_pred_b(uint8_t byte) +{ + static const uint64_t word[256] = { + 0x0000000000000000, 0x00000000000000ff, 0x000000000000ff00, + 0x000000000000ffff, 0x0000000000ff0000, 0x0000000000ff00ff, + 0x0000000000ffff00, 0x0000000000ffffff, 0x00000000ff000000, + 0x00000000ff0000ff, 0x00000000ff00ff00, 0x00000000ff00ffff, + 0x00000000ffff0000, 0x00000000ffff00ff, 0x00000000ffffff00, + 0x00000000ffffffff, 0x000000ff00000000, 0x000000ff000000ff, + 0x000000ff0000ff00, 0x000000ff0000ffff, 0x000000ff00ff0000, + 0x000000ff00ff00ff, 0x000000ff00ffff00, 0x000000ff00ffffff, + 0x000000ffff000000, 0x000000ffff0000ff, 0x000000ffff00ff00, + 0x000000ffff00ffff, 0x000000ffffff0000, 0x000000ffffff00ff, + 0x000000ffffffff00, 0x000000ffffffffff, 0x0000ff0000000000, + 0x0000ff00000000ff, 0x0000ff000000ff00, 0x0000ff000000ffff, + 0x0000ff0000ff0000, 0x0000ff0000ff00ff, 0x0000ff0000ffff00, + 0x0000ff0000ffffff, 0x0000ff00ff000000, 0x0000ff00ff0000ff, + 0x0000ff00ff00ff00, 0x0000ff00ff00ffff, 0x0000ff00ffff0000, + 0x0000ff00ffff00ff, 0x0000ff00ffffff00, 0x0000ff00ffffffff, + 0x0000ffff00000000, 0x0000ffff000000ff, 0x0000ffff0000ff00, + 0x0000ffff0000ffff, 0x0000ffff00ff0000, 0x0000ffff00ff00ff, + 0x0000ffff00ffff00, 0x0000ffff00ffffff, 0x0000ffffff000000, + 0x0000ffffff0000ff, 0x0000ffffff00ff00, 0x0000ffffff00ffff, + 0x0000ffffffff0000, 0x0000ffffffff00ff, 0x0000ffffffffff00, + 0x0000ffffffffffff, 0x00ff000000000000, 0x00ff0000000000ff, + 0x00ff00000000ff00, 0x00ff00000000ffff, 0x00ff000000ff0000, + 0x00ff000000ff00ff, 0x00ff000000ffff00, 0x00ff000000ffffff, + 0x00ff0000ff000000, 0x00ff0000ff0000ff, 0x00ff0000ff00ff00, + 0x00ff0000ff00ffff, 0x00ff0000ffff0000, 0x00ff0000ffff00ff, + 0x00ff0000ffffff00, 0x00ff0000ffffffff, 0x00ff00ff00000000, + 0x00ff00ff000000ff, 0x00ff00ff0000ff00, 0x00ff00ff0000ffff, + 0x00ff00ff00ff0000, 0x00ff00ff00ff00ff, 0x00ff00ff00ffff00, + 0x00ff00ff00ffffff, 0x00ff00ffff000000, 0x00ff00ffff0000ff, + 0x00ff00ffff00ff00, 0x00ff00ffff00ffff, 0x00ff00ffffff0000, + 0x00ff00ffffff00ff, 0x00ff00ffffffff00, 0x00ff00ffffffffff, + 0x00ffff0000000000, 0x00ffff00000000ff, 0x00ffff000000ff00, + 0x00ffff000000ffff, 0x00ffff0000ff0000, 0x00ffff0000ff00ff, + 0x00ffff0000ffff00, 0x00ffff0000ffffff, 0x00ffff00ff000000, + 0x00ffff00ff0000ff, 0x00ffff00ff00ff00, 0x00ffff00ff00ffff, + 0x00ffff00ffff0000, 0x00ffff00ffff00ff, 0x00ffff00ffffff00, + 0x00ffff00ffffffff, 0x00ffffff00000000, 0x00ffffff000000ff, + 0x00ffffff0000ff00, 0x00ffffff0000ffff, 0x00ffffff00ff0000, + 0x00ffffff00ff00ff, 0x00ffffff00ffff00, 0x00ffffff00ffffff, + 0x00ffffffff000000, 0x00ffffffff0000ff, 0x00ffffffff00ff00, + 0x00ffffffff00ffff, 0x00ffffffffff0000, 0x00ffffffffff00ff, + 0x00ffffffffffff00, 0x00ffffffffffffff, 0xff00000000000000, + 0xff000000000000ff, 0xff0000000000ff00, 0xff0000000000ffff, + 0xff00000000ff0000, 0xff00000000ff00ff, 0xff00000000ffff00, + 0xff00000000ffffff, 0xff000000ff000000, 0xff000000ff0000ff, + 0xff000000ff00ff00, 0xff000000ff00ffff, 0xff000000ffff0000, + 0xff000000ffff00ff, 0xff000000ffffff00, 0xff000000ffffffff, + 0xff0000ff00000000, 0xff0000ff000000ff, 0xff0000ff0000ff00, + 0xff0000ff0000ffff, 0xff0000ff00ff0000, 0xff0000ff00ff00ff, + 0xff0000ff00ffff00, 0xff0000ff00ffffff, 0xff0000ffff000000, + 0xff0000ffff0000ff, 0xff0000ffff00ff00, 0xff0000ffff00ffff, + 0xff0000ffffff0000, 0xff0000ffffff00ff, 0xff0000ffffffff00, + 0xff0000ffffffffff, 0xff00ff0000000000, 0xff00ff00000000ff, + 0xff00ff000000ff00, 0xff00ff000000ffff, 0xff00ff0000ff0000, + 0xff00ff0000ff00ff, 0xff00ff0000ffff00, 0xff00ff0000ffffff, + 0xff00ff00ff000000, 0xff00ff00ff0000ff, 0xff00ff00ff00ff00, + 0xff00ff00ff00ffff, 0xff00ff00ffff0000, 0xff00ff00ffff00ff, + 0xff00ff00ffffff00, 0xff00ff00ffffffff, 0xff00ffff00000000, + 0xff00ffff000000ff, 0xff00ffff0000ff00, 0xff00ffff0000ffff, + 0xff00ffff00ff0000, 0xff00ffff00ff00ff, 0xff00ffff00ffff00, + 0xff00ffff00ffffff, 0xff00ffffff000000, 0xff00ffffff0000ff, + 0xff00ffffff00ff00, 0xff00ffffff00ffff, 0xff00ffffffff0000, + 0xff00ffffffff00ff, 0xff00ffffffffff00, 0xff00ffffffffffff, + 0xffff000000000000, 0xffff0000000000ff, 0xffff00000000ff00, + 0xffff00000000ffff, 0xffff000000ff0000, 0xffff000000ff00ff, + 0xffff000000ffff00, 0xffff000000ffffff, 0xffff0000ff000000, + 0xffff0000ff0000ff, 0xffff0000ff00ff00, 0xffff0000ff00ffff, + 0xffff0000ffff0000, 0xffff0000ffff00ff, 0xffff0000ffffff00, + 0xffff0000ffffffff, 0xffff00ff00000000, 0xffff00ff000000ff, + 0xffff00ff0000ff00, 0xffff00ff0000ffff, 0xffff00ff00ff0000, + 0xffff00ff00ff00ff, 0xffff00ff00ffff00, 0xffff00ff00ffffff, + 0xffff00ffff000000, 0xffff00ffff0000ff, 0xffff00ffff00ff00, + 0xffff00ffff00ffff, 0xffff00ffffff0000, 0xffff00ffffff00ff, + 0xffff00ffffffff00, 0xffff00ffffffffff, 0xffffff0000000000, + 0xffffff00000000ff, 0xffffff000000ff00, 0xffffff000000ffff, + 0xffffff0000ff0000, 0xffffff0000ff00ff, 0xffffff0000ffff00, + 0xffffff0000ffffff, 0xffffff00ff000000, 0xffffff00ff0000ff, + 0xffffff00ff00ff00, 0xffffff00ff00ffff, 0xffffff00ffff0000, + 0xffffff00ffff00ff, 0xffffff00ffffff00, 0xffffff00ffffffff, + 0xffffffff00000000, 0xffffffff000000ff, 0xffffffff0000ff00, + 0xffffffff0000ffff, 0xffffffff00ff0000, 0xffffffff00ff00ff, + 0xffffffff00ffff00, 0xffffffff00ffffff, 0xffffffffff000000, + 0xffffffffff0000ff, 0xffffffffff00ff00, 0xffffffffff00ffff, + 0xffffffffffff0000, 0xffffffffffff00ff, 0xffffffffffffff00, + 0xffffffffffffffff, + }; + return word[byte]; +} + +/* Similarly for half-word elements. + * for (i = 0; i < 256; ++i) { + * unsigned long m = 0; + * if (i & 0xaa) { + * continue; + * } + * for (j = 0; j < 8; j += 2) { + * if ((i >> j) & 1) { + * m |= 0xfffful << (j << 3); + * } + * } + * printf("[0x%x] = 0x%016lx,\n", i, m); + * } + */ +static inline uint64_t expand_pred_h(uint8_t byte) +{ + static const uint64_t word[] = { + [0x01] = 0x000000000000ffff, [0x04] = 0x00000000ffff0000, + [0x05] = 0x00000000ffffffff, [0x10] = 0x0000ffff00000000, + [0x11] = 0x0000ffff0000ffff, [0x14] = 0x0000ffffffff0000, + [0x15] = 0x0000ffffffffffff, [0x40] = 0xffff000000000000, + [0x41] = 0xffff00000000ffff, [0x44] = 0xffff0000ffff0000, + [0x45] = 0xffff0000ffffffff, [0x50] = 0xffffffff00000000, + [0x51] = 0xffffffff0000ffff, [0x54] = 0xffffffffffff0000, + [0x55] = 0xffffffffffffffff, + }; + return word[byte & 0x55]; +} + +/* Similarly for single word elements. */ +static inline uint64_t expand_pred_s(uint8_t byte) +{ + static const uint64_t word[] = { + [0x01] = 0x00000000ffffffffull, + [0x10] = 0xffffffff00000000ull, + [0x11] = 0xffffffffffffffffull, + }; + return word[byte & 0x11]; +} + #define LOGICAL_PPPP(NAME, FUNC) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ { \ @@ -484,3 +628,123 @@ uint32_t HELPER(sve_pnext)(void *vd, void *vg, uint32_t pred_desc) return flags; } + +/* Store zero into every active element of Zd. We will use this for two + * and three-operand predicated instructions for which logic dictates a + * zero result. In particular, logical shift by element size, which is + * otherwise undefined on the host. + * + * For element sizes smaller than uint64_t, we use tables to expand + * the N bits of the controlling predicate to a byte mask, and clear + * those bytes. + */ +void HELPER(sve_clr_b)(void *vd, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] &= ~expand_pred_b(pg[H1(i)]); + } +} + +void HELPER(sve_clr_h)(void *vd, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] &= ~expand_pred_h(pg[H1(i)]); + } +} + +void HELPER(sve_clr_s)(void *vd, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] &= ~expand_pred_s(pg[H1(i)]); + } +} + +void HELPER(sve_clr_d)(void *vd, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + if (pg[H1(i)] & 1) { + d[i] = 0; + } + } +} + +/* Three-operand expander, immediate operand, controlled by a predicate. + */ +#define DO_ZPZI(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + TYPE imm = simd_data(desc); \ + for (i = 0; i < opr_sz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, imm); \ + } \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +/* Similarly, specialized for 64-bit operands. */ +#define DO_ZPZI_D(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + TYPE *d = vd, *n = vn; \ + TYPE imm = simd_data(desc); \ + uint8_t *pg = vg; \ + for (i = 0; i < opr_sz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + TYPE nn = n[i]; \ + d[i] = OP(nn, imm); \ + } \ + } \ +} + +#define DO_SHR(N, M) (N >> M) +#define DO_SHL(N, M) (N << M) + +/* Arithmetic shift right for division. This rounds negative numbers + toward zero as per signed division. Therefore before shifting, + when N is negative, add 2**M-1. */ +#define DO_ASRD(N, M) ((N + (N < 0 ? ((__typeof(N))1 << M) - 1 : 0)) >> M) + +DO_ZPZI(sve_asr_zpzi_b, int8_t, H1, DO_SHR) +DO_ZPZI(sve_asr_zpzi_h, int16_t, H1_2, DO_SHR) +DO_ZPZI(sve_asr_zpzi_s, int32_t, H1_4, DO_SHR) +DO_ZPZI_D(sve_asr_zpzi_d, int64_t, DO_SHR) + +DO_ZPZI(sve_lsr_zpzi_b, uint8_t, H1, DO_SHR) +DO_ZPZI(sve_lsr_zpzi_h, uint16_t, H1_2, DO_SHR) +DO_ZPZI(sve_lsr_zpzi_s, uint32_t, H1_4, DO_SHR) +DO_ZPZI_D(sve_lsr_zpzi_d, uint64_t, DO_SHR) + +DO_ZPZI(sve_lsl_zpzi_b, uint8_t, H1, DO_SHL) +DO_ZPZI(sve_lsl_zpzi_h, uint16_t, H1_2, DO_SHL) +DO_ZPZI(sve_lsl_zpzi_s, uint32_t, H1_4, DO_SHL) +DO_ZPZI_D(sve_lsl_zpzi_d, uint64_t, DO_SHL) + +DO_ZPZI(sve_asrd_b, int8_t, H1, DO_ASRD) +DO_ZPZI(sve_asrd_h, int16_t, H1_2, DO_ASRD) +DO_ZPZI(sve_asrd_s, int32_t, H1_4, DO_ASRD) +DO_ZPZI_D(sve_asrd_d, int64_t, DO_ASRD) + +#undef DO_SHR +#undef DO_SHL +#undef DO_ASRD +#undef DO_ZPZI +#undef DO_ZPZI_D diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 66ef01712d..7607a90a4a 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -33,6 +33,30 @@ #include "trace-tcg.h" #include "translate-a64.h" +/* + * Helpers for extracting complex instruction fields. + */ + +/* See e.g. ASR (immediate, predicated). + * Returns -1 for unallocated encoding; diagnose later. + */ +static int tszimm_esz(int x) +{ + x >>= 3; /* discard imm3 */ + return 31 - clz32(x); +} + +static int tszimm_shr(int x) +{ + return (16 << tszimm_esz(x)) - x; +} + +/* See e.g. LSL (immediate, predicated). */ +static int tszimm_shl(int x) +{ + return x - (8 << tszimm_esz(x)); +} + /* * Include the generated decoder. */ @@ -363,6 +387,112 @@ static bool trans_SADDV(DisasContext *s, arg_rpr_esz *a, uint32_t insn) #undef DO_VPZ +/* + *** SVE Shift by Immediate - Predicated Group + */ + +/* Store zero into every active element of Zd. We will use this for two + * and three-operand predicated instructions for which logic dictates a + * zero result. + */ +static bool do_clr_zp(DisasContext *s, int rd, int pg, int esz) +{ + static gen_helper_gvec_2 * const fns[4] = { + gen_helper_sve_clr_b, gen_helper_sve_clr_h, + gen_helper_sve_clr_s, gen_helper_sve_clr_d, + }; + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, rd), + pred_full_reg_offset(s, pg), + vsz, vsz, 0, fns[esz]); + } + return true; +} + +static bool do_zpzi_ool(DisasContext *s, arg_rpri_esz *a, + gen_helper_gvec_3 *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + pred_full_reg_offset(s, a->pg), + vsz, vsz, a->imm, fn); + } + return true; +} + +static bool trans_ASR_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_asr_zpzi_b, gen_helper_sve_asr_zpzi_h, + gen_helper_sve_asr_zpzi_s, gen_helper_sve_asr_zpzi_d, + }; + if (a->esz < 0) { + /* Invalid tsz encoding -- see tszimm_esz. */ + return false; + } + /* Shift by element size is architecturally valid. For + arithmetic right-shift, it's the same as by one less. */ + a->imm = MIN(a->imm, (8 << a->esz) - 1); + return do_zpzi_ool(s, a, fns[a->esz]); +} + +static bool trans_LSR_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_lsr_zpzi_b, gen_helper_sve_lsr_zpzi_h, + gen_helper_sve_lsr_zpzi_s, gen_helper_sve_lsr_zpzi_d, + }; + if (a->esz < 0) { + return false; + } + /* Shift by element size is architecturally valid. + For logical shifts, it is a zeroing operation. */ + if (a->imm >= (8 << a->esz)) { + return do_clr_zp(s, a->rd, a->pg, a->esz); + } else { + return do_zpzi_ool(s, a, fns[a->esz]); + } +} + +static bool trans_LSL_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_lsl_zpzi_b, gen_helper_sve_lsl_zpzi_h, + gen_helper_sve_lsl_zpzi_s, gen_helper_sve_lsl_zpzi_d, + }; + if (a->esz < 0) { + return false; + } + /* Shift by element size is architecturally valid. + For logical shifts, it is a zeroing operation. */ + if (a->imm >= (8 << a->esz)) { + return do_clr_zp(s, a->rd, a->pg, a->esz); + } else { + return do_zpzi_ool(s, a, fns[a->esz]); + } +} + +static bool trans_ASRD(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_asrd_b, gen_helper_sve_asrd_h, + gen_helper_sve_asrd_s, gen_helper_sve_asrd_d, + }; + if (a->esz < 0) { + return false; + } + /* Shift by element size is architecturally valid. For arithmetic + right shift for division, it is a zeroing operation. */ + if (a->imm >= (8 << a->esz)) { + return do_clr_zp(s, a->rd, a->pg, a->esz); + } else { + return do_zpzi_ool(s, a, fns[a->esz]); + } +} + /* *** SVE Predicate Logical Operations Group */ From 27721dbb7ae5e2a52f06588cf38854e4cbc613c0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0598/2380] target/arm: Implement SVE bitwise shift by vector (predicated) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 27 +++++++++++++++++++++++++++ target/arm/sve.decode | 8 ++++++++ target/arm/sve_helper.c | 25 +++++++++++++++++++++++++ target/arm/translate-sve.c | 4 ++++ 4 files changed, 64 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index b3c89579af..0cc02ee59e 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -168,6 +168,33 @@ DEF_HELPER_FLAGS_5(sve_udiv_zpzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_udiv_zpzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_lsr_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsr_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsr_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsr_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_lsl_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsl_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsl_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsl_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(sve_orv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_orv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_orv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index a1791c1d7b..8267963b6b 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -149,6 +149,14 @@ LSL_zpzi 00000100 .. 000 011 100 ... .. ... ..... \ ASRD 00000100 .. 000 100 100 ... .. ... ..... \ @rdn_pg_tszimm imm=%tszimm_shr +# SVE bitwise shift by vector (predicated) +ASR_zpzz 00000100 .. 010 000 100 ... ..... ..... @rdn_pg_rm +LSR_zpzz 00000100 .. 010 001 100 ... ..... ..... @rdn_pg_rm +LSL_zpzz 00000100 .. 010 011 100 ... ..... ..... @rdn_pg_rm +ASR_zpzz 00000100 .. 010 100 100 ... ..... ..... @rdm_pg_rn # ASRR +LSR_zpzz 00000100 .. 010 101 100 ... ..... ..... @rdm_pg_rn # LSRR +LSL_zpzz 00000100 .. 010 111 100 ... ..... ..... @rdm_pg_rn # LSLR + ### SVE Logical - Unpredicated Group # SVE bitwise logical operations (unpredicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index b6b9a08965..ece3a81ad3 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -440,6 +440,28 @@ DO_ZPZZ_D(sve_sdiv_zpzz_d, int64_t, DO_DIV) DO_ZPZZ(sve_udiv_zpzz_s, uint32_t, H1_4, DO_DIV) DO_ZPZZ_D(sve_udiv_zpzz_d, uint64_t, DO_DIV) +/* Note that all bits of the shift are significant + and not modulo the element size. */ +#define DO_ASR(N, M) (N >> MIN(M, sizeof(N) * 8 - 1)) +#define DO_LSR(N, M) (M < sizeof(N) * 8 ? N >> M : 0) +#define DO_LSL(N, M) (M < sizeof(N) * 8 ? N << M : 0) + +DO_ZPZZ(sve_asr_zpzz_b, int8_t, H1, DO_ASR) +DO_ZPZZ(sve_lsr_zpzz_b, uint8_t, H1_2, DO_LSR) +DO_ZPZZ(sve_lsl_zpzz_b, uint8_t, H1_4, DO_LSL) + +DO_ZPZZ(sve_asr_zpzz_h, int16_t, H1, DO_ASR) +DO_ZPZZ(sve_lsr_zpzz_h, uint16_t, H1_2, DO_LSR) +DO_ZPZZ(sve_lsl_zpzz_h, uint16_t, H1_4, DO_LSL) + +DO_ZPZZ(sve_asr_zpzz_s, int32_t, H1, DO_ASR) +DO_ZPZZ(sve_lsr_zpzz_s, uint32_t, H1_2, DO_LSR) +DO_ZPZZ(sve_lsl_zpzz_s, uint32_t, H1_4, DO_LSL) + +DO_ZPZZ_D(sve_asr_zpzz_d, int64_t, DO_ASR) +DO_ZPZZ_D(sve_lsr_zpzz_d, uint64_t, DO_LSR) +DO_ZPZZ_D(sve_lsl_zpzz_d, uint64_t, DO_LSL) + #undef DO_ZPZZ #undef DO_ZPZZ_D @@ -544,6 +566,9 @@ DO_VPZ_D(sve_uminv_d, uint64_t, uint64_t, -1, DO_MIN) #undef DO_ABD #undef DO_MUL #undef DO_DIV +#undef DO_ASR +#undef DO_LSR +#undef DO_LSL /* Similar to the ARM LastActiveElement pseudocode function, except the result is multiplied by the element size. This includes the not found diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 7607a90a4a..f0400e35d9 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -301,6 +301,10 @@ DO_ZPZZ(MUL, mul) DO_ZPZZ(SMULH, smulh) DO_ZPZZ(UMULH, umulh) +DO_ZPZZ(ASR, asr) +DO_ZPZZ(LSR, lsr) +DO_ZPZZ(LSL, lsl) + static bool trans_SDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) { static gen_helper_gvec_4 * const fns[4] = { From fe7f8dfb2dd27d4ae4fe48ecc7350c254702a0b7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0599/2380] target/arm: Implement SVE bitwise shift by wide elements (predicated) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 21 +++++++++++++++++++++ target/arm/sve.decode | 6 ++++++ target/arm/sve_helper.c | 35 +++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 24 ++++++++++++++++++++++++ 4 files changed, 86 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 0cc02ee59e..d516580134 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -195,6 +195,27 @@ DEF_HELPER_FLAGS_5(sve_lsl_zpzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_lsl_zpzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzw_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzw_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzw_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_lsr_zpzw_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsr_zpzw_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsr_zpzw_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_lsl_zpzw_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsl_zpzw_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsl_zpzw_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(sve_orv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_orv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_orv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 8267963b6b..1de289e55d 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -157,6 +157,12 @@ ASR_zpzz 00000100 .. 010 100 100 ... ..... ..... @rdm_pg_rn # ASRR LSR_zpzz 00000100 .. 010 101 100 ... ..... ..... @rdm_pg_rn # LSRR LSL_zpzz 00000100 .. 010 111 100 ... ..... ..... @rdm_pg_rn # LSLR +# SVE bitwise shift by wide elements (predicated) +# Note these require size != 3. +ASR_zpzw 00000100 .. 011 000 100 ... ..... ..... @rdn_pg_rm +LSR_zpzw 00000100 .. 011 001 100 ... ..... ..... @rdn_pg_rm +LSL_zpzw 00000100 .. 011 011 100 ... ..... ..... @rdn_pg_rm + ### SVE Logical - Unpredicated Group # SVE bitwise logical operations (unpredicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index ece3a81ad3..a5d12603e5 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -465,6 +465,41 @@ DO_ZPZZ_D(sve_lsl_zpzz_d, uint64_t, DO_LSL) #undef DO_ZPZZ #undef DO_ZPZZ_D +/* Three-operand expander, controlled by a predicate, in which the + * third operand is "wide". That is, for D = N op M, the same 64-bit + * value of M is used with all of the narrower values of N. + */ +#define DO_ZPZW(NAME, TYPE, TYPEW, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; ) { \ + uint8_t pg = *(uint8_t *)(vg + H1(i >> 3)); \ + TYPEW mm = *(TYPEW *)(vm + i); \ + do { \ + if (pg & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, mm); \ + } \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 7); \ + } \ +} + +DO_ZPZW(sve_asr_zpzw_b, int8_t, uint64_t, H1, DO_ASR) +DO_ZPZW(sve_lsr_zpzw_b, uint8_t, uint64_t, H1, DO_LSR) +DO_ZPZW(sve_lsl_zpzw_b, uint8_t, uint64_t, H1, DO_LSL) + +DO_ZPZW(sve_asr_zpzw_h, int16_t, uint64_t, H1_2, DO_ASR) +DO_ZPZW(sve_lsr_zpzw_h, uint16_t, uint64_t, H1_2, DO_LSR) +DO_ZPZW(sve_lsl_zpzw_h, uint16_t, uint64_t, H1_2, DO_LSL) + +DO_ZPZW(sve_asr_zpzw_s, int32_t, uint64_t, H1_4, DO_ASR) +DO_ZPZW(sve_lsr_zpzw_s, uint32_t, uint64_t, H1_4, DO_LSR) +DO_ZPZW(sve_lsl_zpzw_s, uint32_t, uint64_t, H1_4, DO_LSL) + +#undef DO_ZPZW + /* Two-operand reduction expander, controlled by a predicate. * The difference between TYPERED and TYPERET has to do with * sign-extension. E.g. for SMAX, TYPERED must be signed, diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index f0400e35d9..438df6359e 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -497,6 +497,30 @@ static bool trans_ASRD(DisasContext *s, arg_rpri_esz *a, uint32_t insn) } } +/* + *** SVE Bitwise Shift - Predicated Group + */ + +#define DO_ZPZW(NAME, name) \ +static bool trans_##NAME##_zpzw(DisasContext *s, arg_rprr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_4 * const fns[3] = { \ + gen_helper_sve_##name##_zpzw_b, gen_helper_sve_##name##_zpzw_h, \ + gen_helper_sve_##name##_zpzw_s, \ + }; \ + if (a->esz < 0 || a->esz >= 3) { \ + return false; \ + } \ + return do_zpzz_ool(s, a, fns[a->esz]); \ +} + +DO_ZPZW(ASR, asr) +DO_ZPZW(LSR, lsr) +DO_ZPZW(LSL, lsl) + +#undef DO_ZPZW + /* *** SVE Predicate Logical Operations Group */ From afac6d0467c1327ad2e30a3c35347fcf5a773742 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0600/2380] target/arm: Implement SVE Integer Arithmetic - Unary Predicated Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 60 ++++++++++++++++++ target/arm/sve.decode | 23 +++++++ target/arm/sve_helper.c | 127 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 113 +++++++++++++++++++++++++++++++++ 4 files changed, 323 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index d516580134..11644125d1 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -285,6 +285,66 @@ DEF_HELPER_FLAGS_4(sve_asrd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_asrd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_asrd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cls_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cls_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cls_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_clz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_clz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_clz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_clz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cnt_zpz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnt_zpz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnt_zpz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnt_zpz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cnot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnot_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnot_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fabs_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fabs_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fabs_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fneg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fneg_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fneg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_not_zpz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_not_zpz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_not_zpz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_not_zpz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_sxtb_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_sxtb_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_sxtb_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_uxtb_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uxtb_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uxtb_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_sxth_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_sxth_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_uxth_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uxth_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_sxtw_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uxtw_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_abs_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_abs_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_abs_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_abs_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_neg_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_neg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_neg_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_neg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 1de289e55d..0ddc1e96be 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -163,6 +163,29 @@ ASR_zpzw 00000100 .. 011 000 100 ... ..... ..... @rdn_pg_rm LSR_zpzw 00000100 .. 011 001 100 ... ..... ..... @rdn_pg_rm LSL_zpzw 00000100 .. 011 011 100 ... ..... ..... @rdn_pg_rm +### SVE Integer Arithmetic - Unary Predicated Group + +# SVE unary bit operations (predicated) +# Note esz != 0 for FABS and FNEG. +CLS 00000100 .. 011 000 101 ... ..... ..... @rd_pg_rn +CLZ 00000100 .. 011 001 101 ... ..... ..... @rd_pg_rn +CNT_zpz 00000100 .. 011 010 101 ... ..... ..... @rd_pg_rn +CNOT 00000100 .. 011 011 101 ... ..... ..... @rd_pg_rn +NOT_zpz 00000100 .. 011 110 101 ... ..... ..... @rd_pg_rn +FABS 00000100 .. 011 100 101 ... ..... ..... @rd_pg_rn +FNEG 00000100 .. 011 101 101 ... ..... ..... @rd_pg_rn + +# SVE integer unary operations (predicated) +# Note esz > original size for extensions. +ABS 00000100 .. 010 110 101 ... ..... ..... @rd_pg_rn +NEG 00000100 .. 010 111 101 ... ..... ..... @rd_pg_rn +SXTB 00000100 .. 010 000 101 ... ..... ..... @rd_pg_rn +UXTB 00000100 .. 010 001 101 ... ..... ..... @rd_pg_rn +SXTH 00000100 .. 010 010 101 ... ..... ..... @rd_pg_rn +UXTH 00000100 .. 010 011 101 ... ..... ..... @rd_pg_rn +SXTW 00000100 .. 010 100 101 ... ..... ..... @rd_pg_rn +UXTW 00000100 .. 010 101 101 ... ..... ..... @rd_pg_rn + ### SVE Logical - Unpredicated Group # SVE bitwise logical operations (unpredicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index a5d12603e5..236d21e771 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -500,6 +500,133 @@ DO_ZPZW(sve_lsl_zpzw_s, uint32_t, uint64_t, H1_4, DO_LSL) #undef DO_ZPZW +/* Fully general two-operand expander, controlled by a predicate. + */ +#define DO_ZPZ(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn); \ + } \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +/* Similarly, specialized for 64-bit operands. */ +#define DO_ZPZ_D(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + TYPE *d = vd, *n = vn; \ + uint8_t *pg = vg; \ + for (i = 0; i < opr_sz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + TYPE nn = n[i]; \ + d[i] = OP(nn); \ + } \ + } \ +} + +#define DO_CLS_B(N) (clrsb32(N) - 24) +#define DO_CLS_H(N) (clrsb32(N) - 16) + +DO_ZPZ(sve_cls_b, int8_t, H1, DO_CLS_B) +DO_ZPZ(sve_cls_h, int16_t, H1_2, DO_CLS_H) +DO_ZPZ(sve_cls_s, int32_t, H1_4, clrsb32) +DO_ZPZ_D(sve_cls_d, int64_t, clrsb64) + +#define DO_CLZ_B(N) (clz32(N) - 24) +#define DO_CLZ_H(N) (clz32(N) - 16) + +DO_ZPZ(sve_clz_b, uint8_t, H1, DO_CLZ_B) +DO_ZPZ(sve_clz_h, uint16_t, H1_2, DO_CLZ_H) +DO_ZPZ(sve_clz_s, uint32_t, H1_4, clz32) +DO_ZPZ_D(sve_clz_d, uint64_t, clz64) + +DO_ZPZ(sve_cnt_zpz_b, uint8_t, H1, ctpop8) +DO_ZPZ(sve_cnt_zpz_h, uint16_t, H1_2, ctpop16) +DO_ZPZ(sve_cnt_zpz_s, uint32_t, H1_4, ctpop32) +DO_ZPZ_D(sve_cnt_zpz_d, uint64_t, ctpop64) + +#define DO_CNOT(N) (N == 0) + +DO_ZPZ(sve_cnot_b, uint8_t, H1, DO_CNOT) +DO_ZPZ(sve_cnot_h, uint16_t, H1_2, DO_CNOT) +DO_ZPZ(sve_cnot_s, uint32_t, H1_4, DO_CNOT) +DO_ZPZ_D(sve_cnot_d, uint64_t, DO_CNOT) + +#define DO_FABS(N) (N & ((__typeof(N))-1 >> 1)) + +DO_ZPZ(sve_fabs_h, uint16_t, H1_2, DO_FABS) +DO_ZPZ(sve_fabs_s, uint32_t, H1_4, DO_FABS) +DO_ZPZ_D(sve_fabs_d, uint64_t, DO_FABS) + +#define DO_FNEG(N) (N ^ ~((__typeof(N))-1 >> 1)) + +DO_ZPZ(sve_fneg_h, uint16_t, H1_2, DO_FNEG) +DO_ZPZ(sve_fneg_s, uint32_t, H1_4, DO_FNEG) +DO_ZPZ_D(sve_fneg_d, uint64_t, DO_FNEG) + +#define DO_NOT(N) (~N) + +DO_ZPZ(sve_not_zpz_b, uint8_t, H1, DO_NOT) +DO_ZPZ(sve_not_zpz_h, uint16_t, H1_2, DO_NOT) +DO_ZPZ(sve_not_zpz_s, uint32_t, H1_4, DO_NOT) +DO_ZPZ_D(sve_not_zpz_d, uint64_t, DO_NOT) + +#define DO_SXTB(N) ((int8_t)N) +#define DO_SXTH(N) ((int16_t)N) +#define DO_SXTS(N) ((int32_t)N) +#define DO_UXTB(N) ((uint8_t)N) +#define DO_UXTH(N) ((uint16_t)N) +#define DO_UXTS(N) ((uint32_t)N) + +DO_ZPZ(sve_sxtb_h, uint16_t, H1_2, DO_SXTB) +DO_ZPZ(sve_sxtb_s, uint32_t, H1_4, DO_SXTB) +DO_ZPZ(sve_sxth_s, uint32_t, H1_4, DO_SXTH) +DO_ZPZ_D(sve_sxtb_d, uint64_t, DO_SXTB) +DO_ZPZ_D(sve_sxth_d, uint64_t, DO_SXTH) +DO_ZPZ_D(sve_sxtw_d, uint64_t, DO_SXTS) + +DO_ZPZ(sve_uxtb_h, uint16_t, H1_2, DO_UXTB) +DO_ZPZ(sve_uxtb_s, uint32_t, H1_4, DO_UXTB) +DO_ZPZ(sve_uxth_s, uint32_t, H1_4, DO_UXTH) +DO_ZPZ_D(sve_uxtb_d, uint64_t, DO_UXTB) +DO_ZPZ_D(sve_uxth_d, uint64_t, DO_UXTH) +DO_ZPZ_D(sve_uxtw_d, uint64_t, DO_UXTS) + +#define DO_ABS(N) (N < 0 ? -N : N) + +DO_ZPZ(sve_abs_b, int8_t, H1, DO_ABS) +DO_ZPZ(sve_abs_h, int16_t, H1_2, DO_ABS) +DO_ZPZ(sve_abs_s, int32_t, H1_4, DO_ABS) +DO_ZPZ_D(sve_abs_d, int64_t, DO_ABS) + +#define DO_NEG(N) (-N) + +DO_ZPZ(sve_neg_b, uint8_t, H1, DO_NEG) +DO_ZPZ(sve_neg_h, uint16_t, H1_2, DO_NEG) +DO_ZPZ(sve_neg_s, uint32_t, H1_4, DO_NEG) +DO_ZPZ_D(sve_neg_d, uint64_t, DO_NEG) + +#undef DO_CLS_B +#undef DO_CLS_H +#undef DO_CLZ_B +#undef DO_CLZ_H +#undef DO_CNOT +#undef DO_FABS +#undef DO_FNEG +#undef DO_ABS +#undef DO_NEG +#undef DO_ZPZ +#undef DO_ZPZ_D + /* Two-operand reduction expander, controlled by a predicate. * The difference between TYPERED and TYPERET has to do with * sign-extension. E.g. for SMAX, TYPERED must be signed, diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 438df6359e..52f1b4dbf5 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -323,6 +323,119 @@ static bool trans_UDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) #undef DO_ZPZZ +/* + *** SVE Integer Arithmetic - Unary Predicated Group + */ + +static bool do_zpz_ool(DisasContext *s, arg_rpr_esz *a, gen_helper_gvec_3 *fn) +{ + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + pred_full_reg_offset(s, a->pg), + vsz, vsz, 0, fn); + } + return true; +} + +#define DO_ZPZ(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_3 * const fns[4] = { \ + gen_helper_sve_##name##_b, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d, \ + }; \ + return do_zpz_ool(s, a, fns[a->esz]); \ +} + +DO_ZPZ(CLS, cls) +DO_ZPZ(CLZ, clz) +DO_ZPZ(CNT_zpz, cnt_zpz) +DO_ZPZ(CNOT, cnot) +DO_ZPZ(NOT_zpz, not_zpz) +DO_ZPZ(ABS, abs) +DO_ZPZ(NEG, neg) + +static bool trans_FABS(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_fabs_h, + gen_helper_sve_fabs_s, + gen_helper_sve_fabs_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_FNEG(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_fneg_h, + gen_helper_sve_fneg_s, + gen_helper_sve_fneg_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_SXTB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_sxtb_h, + gen_helper_sve_sxtb_s, + gen_helper_sve_sxtb_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_UXTB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_uxtb_h, + gen_helper_sve_uxtb_s, + gen_helper_sve_uxtb_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_SXTH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, NULL, + gen_helper_sve_sxth_s, + gen_helper_sve_sxth_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_UXTH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, NULL, + gen_helper_sve_uxth_s, + gen_helper_sve_uxth_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_SXTW(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ool(s, a, a->esz == 3 ? gen_helper_sve_sxtw_d : NULL); +} + +static bool trans_UXTW(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ool(s, a, a->esz == 3 ? gen_helper_sve_uxtw_d : NULL); +} + +#undef DO_ZPZ + /* *** SVE Integer Reduction Group */ From 96a36e4a44bbf296ac212ed68ebf4e48d3dfb1f0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:08 +0100 Subject: [PATCH 0601/2380] target/arm: Implement SVE Integer Multiply-Add Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 18 ++++++++++++ target/arm/sve.decode | 17 ++++++++++++ target/arm/sve_helper.c | 57 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 34 +++++++++++++++++++++++ 4 files changed, 126 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 11644125d1..b31d497f31 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -345,6 +345,24 @@ DEF_HELPER_FLAGS_4(sve_neg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_neg_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_neg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mla_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mla_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mla_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mla_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_mls_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mls_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mls_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mls_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 0ddc1e96be..5e4335b2ae 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -47,6 +47,7 @@ &rpr_esz rd pg rn esz &rprr_s rd pg rn rm s &rprr_esz rd pg rn rm esz +&rprrr_esz rd pg rn rm ra esz &rpri_esz rd pg rn imm esz ########################################################################### @@ -71,6 +72,12 @@ @rdm_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 \ &rprr_esz rm=%reg_movprfx +# Three register operand, with governing predicate, vector element size +@rda_pg_rn_rm ........ esz:2 . rm:5 ... pg:3 rn:5 rd:5 \ + &rprrr_esz ra=%reg_movprfx +@rdn_pg_ra_rm ........ esz:2 . rm:5 ... pg:3 ra:5 rd:5 \ + &rprrr_esz rn=%reg_movprfx + # One register operand, with governing predicate, vector element size @rd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 &rpr_esz @@ -186,6 +193,16 @@ UXTH 00000100 .. 010 011 101 ... ..... ..... @rd_pg_rn SXTW 00000100 .. 010 100 101 ... ..... ..... @rd_pg_rn UXTW 00000100 .. 010 101 101 ... ..... ..... @rd_pg_rn +### SVE Integer Multiply-Add Group + +# SVE integer multiply-add writing addend (predicated) +MLA 00000100 .. 0 ..... 010 ... ..... ..... @rda_pg_rn_rm +MLS 00000100 .. 0 ..... 011 ... ..... ..... @rda_pg_rn_rm + +# SVE integer multiply-add writing multiplicand (predicated) +MLA 00000100 .. 0 ..... 110 ... ..... ..... @rdn_pg_ra_rm # MAD +MLS 00000100 .. 0 ..... 111 ... ..... ..... @rdn_pg_ra_rm # MSB + ### SVE Logical - Unpredicated Group # SVE bitwise logical operations (unpredicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 236d21e771..56a4eb71d5 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -935,3 +935,60 @@ DO_ZPZI_D(sve_asrd_d, int64_t, DO_ASRD) #undef DO_ASRD #undef DO_ZPZI #undef DO_ZPZI_D + +/* Fully general four-operand expander, controlled by a predicate. + */ +#define DO_ZPZZZ(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *va, void *vn, void *vm, \ + void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + TYPE mm = *(TYPE *)(vm + H(i)); \ + TYPE aa = *(TYPE *)(va + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(aa, nn, mm); \ + } \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +/* Similarly, specialized for 64-bit operands. */ +#define DO_ZPZZZ_D(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *va, void *vn, void *vm, \ + void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + TYPE *d = vd, *a = va, *n = vn, *m = vm; \ + uint8_t *pg = vg; \ + for (i = 0; i < opr_sz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + TYPE aa = a[i], nn = n[i], mm = m[i]; \ + d[i] = OP(aa, nn, mm); \ + } \ + } \ +} + +#define DO_MLA(A, N, M) (A + N * M) +#define DO_MLS(A, N, M) (A - N * M) + +DO_ZPZZZ(sve_mla_b, uint8_t, H1, DO_MLA) +DO_ZPZZZ(sve_mls_b, uint8_t, H1, DO_MLS) + +DO_ZPZZZ(sve_mla_h, uint16_t, H1_2, DO_MLA) +DO_ZPZZZ(sve_mls_h, uint16_t, H1_2, DO_MLS) + +DO_ZPZZZ(sve_mla_s, uint32_t, H1_4, DO_MLA) +DO_ZPZZZ(sve_mls_s, uint32_t, H1_4, DO_MLS) + +DO_ZPZZZ_D(sve_mla_d, uint64_t, DO_MLA) +DO_ZPZZZ_D(sve_mls_d, uint64_t, DO_MLS) + +#undef DO_MLA +#undef DO_MLS +#undef DO_ZPZZZ +#undef DO_ZPZZZ_D diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 52f1b4dbf5..f14bb2196a 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -634,6 +634,40 @@ DO_ZPZW(LSL, lsl) #undef DO_ZPZW +/* + *** SVE Integer Multiply-Add Group + */ + +static bool do_zpzzz_ool(DisasContext *s, arg_rprrr_esz *a, + gen_helper_gvec_5 *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_5_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->ra), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + vsz, vsz, 0, fn); + } + return true; +} + +#define DO_ZPZZZ(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rprrr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_5 * const fns[4] = { \ + gen_helper_sve_##name##_b, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d, \ + }; \ + return do_zpzzz_ool(s, a, fns[a->esz]); \ +} + +DO_ZPZZZ(MLA, mla) +DO_ZPZZZ(MLS, mls) + +#undef DO_ZPZZZ + /* *** SVE Predicate Logical Operations Group */ From fea98f9c3077e4666f6d4933030b5891fbd6bb12 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0602/2380] target/arm: Implement SVE Integer Arithmetic - Unpredicated Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 13 +++++++++++++ target/arm/translate-sve.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 5e4335b2ae..58d59c7b77 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -66,6 +66,9 @@ # Three predicate operand, with governing predicate, flag setting @pd_pg_pn_pm_s ........ . s:1 .. rm:4 .. pg:4 . rn:4 . rd:4 &rprr_s +# Three operand, vector element size +@rd_rn_rm ........ esz:2 . rm:5 ... ... rn:5 rd:5 &rrr_esz + # Two register operand, with governing predicate, vector element size @rdn_pg_rm ........ esz:2 ... ... ... pg:3 rm:5 rd:5 \ &rprr_esz rn=%reg_movprfx @@ -203,6 +206,16 @@ MLS 00000100 .. 0 ..... 011 ... ..... ..... @rda_pg_rn_rm MLA 00000100 .. 0 ..... 110 ... ..... ..... @rdn_pg_ra_rm # MAD MLS 00000100 .. 0 ..... 111 ... ..... ..... @rdn_pg_ra_rm # MSB +### SVE Integer Arithmetic - Unpredicated Group + +# SVE integer add/subtract vectors (unpredicated) +ADD_zzz 00000100 .. 1 ..... 000 000 ..... ..... @rd_rn_rm +SUB_zzz 00000100 .. 1 ..... 000 001 ..... ..... @rd_rn_rm +SQADD_zzz 00000100 .. 1 ..... 000 100 ..... ..... @rd_rn_rm +UQADD_zzz 00000100 .. 1 ..... 000 101 ..... ..... @rd_rn_rm +SQSUB_zzz 00000100 .. 1 ..... 000 110 ..... ..... @rd_rn_rm +UQSUB_zzz 00000100 .. 1 ..... 000 111 ..... ..... @rd_rn_rm + ### SVE Logical - Unpredicated Group # SVE bitwise logical operations (unpredicated) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index f14bb2196a..d9c4118d46 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -251,6 +251,40 @@ static bool trans_BIC_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) return do_vector3_z(s, tcg_gen_gvec_andc, 0, a->rd, a->rn, a->rm); } +/* + *** SVE Integer Arithmetic - Unpredicated Group + */ + +static bool trans_ADD_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_add, a->esz, a->rd, a->rn, a->rm); +} + +static bool trans_SUB_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_sub, a->esz, a->rd, a->rn, a->rm); +} + +static bool trans_SQADD_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_ssadd, a->esz, a->rd, a->rn, a->rm); +} + +static bool trans_SQSUB_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_sssub, a->esz, a->rd, a->rn, a->rm); +} + +static bool trans_UQADD_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_usadd, a->esz, a->rd, a->rn, a->rm); +} + +static bool trans_UQSUB_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_ussub, a->esz, a->rd, a->rn, a->rm); +} + /* *** SVE Integer Arithmetic - Binary Predicated Group */ From 9a56c9c3a955b77fe436beef7ac03c76a65fa32d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0603/2380] target/arm: Implement SVE Index Generation Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 5 +++ target/arm/sve.decode | 14 +++++++ target/arm/sve_helper.c | 40 +++++++++++++++++++ target/arm/translate-sve.c | 79 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index b31d497f31..2a2dbe98dd 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -363,6 +363,11 @@ DEF_HELPER_FLAGS_6(sve_mls_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_mls_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_index_b, TCG_CALL_NO_RWG, void, ptr, i32, i32, i32) +DEF_HELPER_FLAGS_4(sve_index_h, TCG_CALL_NO_RWG, void, ptr, i32, i32, i32) +DEF_HELPER_FLAGS_4(sve_index_s, TCG_CALL_NO_RWG, void, ptr, i32, i32, i32) +DEF_HELPER_FLAGS_4(sve_index_d, TCG_CALL_NO_RWG, void, ptr, i64, i64, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 58d59c7b77..4f9f64f5ab 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -224,6 +224,20 @@ ORR_zzz 00000100 01 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 EOR_zzz 00000100 10 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 BIC_zzz 00000100 11 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 +### SVE Index Generation Group + +# SVE index generation (immediate start, immediate increment) +INDEX_ii 00000100 esz:2 1 imm2:s5 010000 imm1:s5 rd:5 + +# SVE index generation (immediate start, register increment) +INDEX_ir 00000100 esz:2 1 rm:5 010010 imm:s5 rd:5 + +# SVE index generation (register start, immediate increment) +INDEX_ri 00000100 esz:2 1 imm:s5 010001 rn:5 rd:5 + +# SVE index generation (register start, register increment) +INDEX_rr 00000100 .. 1 ..... 010011 ..... ..... @rd_rn_rm + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 56a4eb71d5..385bb8b314 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -992,3 +992,43 @@ DO_ZPZZZ_D(sve_mls_d, uint64_t, DO_MLS) #undef DO_MLS #undef DO_ZPZZZ #undef DO_ZPZZZ_D + +void HELPER(sve_index_b)(void *vd, uint32_t start, + uint32_t incr, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint8_t *d = vd; + for (i = 0; i < opr_sz; i += 1) { + d[H1(i)] = start + i * incr; + } +} + +void HELPER(sve_index_h)(void *vd, uint32_t start, + uint32_t incr, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 2; + uint16_t *d = vd; + for (i = 0; i < opr_sz; i += 1) { + d[H2(i)] = start + i * incr; + } +} + +void HELPER(sve_index_s)(void *vd, uint32_t start, + uint32_t incr, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 4; + uint32_t *d = vd; + for (i = 0; i < opr_sz; i += 1) { + d[H4(i)] = start + i * incr; + } +} + +void HELPER(sve_index_d)(void *vd, uint64_t start, + uint64_t incr, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + for (i = 0; i < opr_sz; i += 1) { + d[i] = start + i * incr; + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index d9c4118d46..e3a8e9506e 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -702,6 +702,85 @@ DO_ZPZZZ(MLS, mls) #undef DO_ZPZZZ +/* + *** SVE Index Generation Group + */ + +static void do_index(DisasContext *s, int esz, int rd, + TCGv_i64 start, TCGv_i64 incr) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + TCGv_ptr t_zd = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, rd)); + if (esz == 3) { + gen_helper_sve_index_d(t_zd, start, incr, desc); + } else { + typedef void index_fn(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32); + static index_fn * const fns[3] = { + gen_helper_sve_index_b, + gen_helper_sve_index_h, + gen_helper_sve_index_s, + }; + TCGv_i32 s32 = tcg_temp_new_i32(); + TCGv_i32 i32 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(s32, start); + tcg_gen_extrl_i64_i32(i32, incr); + fns[esz](t_zd, s32, i32, desc); + + tcg_temp_free_i32(s32); + tcg_temp_free_i32(i32); + } + tcg_temp_free_ptr(t_zd); + tcg_temp_free_i32(desc); +} + +static bool trans_INDEX_ii(DisasContext *s, arg_INDEX_ii *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 start = tcg_const_i64(a->imm1); + TCGv_i64 incr = tcg_const_i64(a->imm2); + do_index(s, a->esz, a->rd, start, incr); + tcg_temp_free_i64(start); + tcg_temp_free_i64(incr); + } + return true; +} + +static bool trans_INDEX_ir(DisasContext *s, arg_INDEX_ir *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 start = tcg_const_i64(a->imm); + TCGv_i64 incr = cpu_reg(s, a->rm); + do_index(s, a->esz, a->rd, start, incr); + tcg_temp_free_i64(start); + } + return true; +} + +static bool trans_INDEX_ri(DisasContext *s, arg_INDEX_ri *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 start = cpu_reg(s, a->rn); + TCGv_i64 incr = tcg_const_i64(a->imm); + do_index(s, a->esz, a->rd, start, incr); + tcg_temp_free_i64(incr); + } + return true; +} + +static bool trans_INDEX_rr(DisasContext *s, arg_INDEX_rr *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 start = cpu_reg(s, a->rn); + TCGv_i64 incr = cpu_reg(s, a->rm); + do_index(s, a->esz, a->rd, start, incr); + } + return true; +} + /* *** SVE Predicate Logical Operations Group */ From 96f922ccccb08c7725c1276ac87697f5dff69edf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0604/2380] target/arm: Implement SVE Stack Allocation Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 12 ++++++++++++ target/arm/translate-sve.c | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 4f9f64f5ab..9d5c061165 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -84,6 +84,9 @@ # One register operand, with governing predicate, vector element size @rd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 &rpr_esz +# Two register operands with a 6-bit signed immediate. +@rd_rn_i6 ........ ... rn:5 ..... imm:s6 rd:5 &rri + # Two register operand, one immediate operand, with predicate, # element size encoded as TSZHL. User must fill in imm. @rdn_pg_tszimm ........ .. ... ... ... pg:3 ..... rd:5 \ @@ -238,6 +241,15 @@ INDEX_ri 00000100 esz:2 1 imm:s5 010001 rn:5 rd:5 # SVE index generation (register start, register increment) INDEX_rr 00000100 .. 1 ..... 010011 ..... ..... @rd_rn_rm +### SVE Stack Allocation Group + +# SVE stack frame adjustment +ADDVL 00000100 001 ..... 01010 ...... ..... @rd_rn_i6 +ADDPL 00000100 011 ..... 01010 ...... ..... @rd_rn_i6 + +# SVE stack frame size +RDVL 00000100 101 11111 01010 imm:s6 rd:5 + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index e3a8e9506e..f95efa3c72 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -781,6 +781,33 @@ static bool trans_INDEX_rr(DisasContext *s, arg_INDEX_rr *a, uint32_t insn) return true; } +/* + *** SVE Stack Allocation Group + */ + +static bool trans_ADDVL(DisasContext *s, arg_ADDVL *a, uint32_t insn) +{ + TCGv_i64 rd = cpu_reg_sp(s, a->rd); + TCGv_i64 rn = cpu_reg_sp(s, a->rn); + tcg_gen_addi_i64(rd, rn, a->imm * vec_full_reg_size(s)); + return true; +} + +static bool trans_ADDPL(DisasContext *s, arg_ADDPL *a, uint32_t insn) +{ + TCGv_i64 rd = cpu_reg_sp(s, a->rd); + TCGv_i64 rn = cpu_reg_sp(s, a->rn); + tcg_gen_addi_i64(rd, rn, a->imm * pred_full_reg_size(s)); + return true; +} + +static bool trans_RDVL(DisasContext *s, arg_RDVL *a, uint32_t insn) +{ + TCGv_i64 reg = cpu_reg(s, a->rd); + tcg_gen_movi_i64(reg, a->imm * vec_full_reg_size(s)); + return true; +} + /* *** SVE Predicate Logical Operations Group */ From d9d78dccc86eed10ccf1c8e1ac236e41ec330b06 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0605/2380] target/arm: Implement SVE Bitwise Shift - Unpredicated Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 12 ++++++ target/arm/sve.decode | 26 ++++++++++++ target/arm/sve_helper.c | 30 ++++++++++++++ target/arm/translate-sve.c | 85 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 2a2dbe98dd..00e3cd48bb 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -368,6 +368,18 @@ DEF_HELPER_FLAGS_4(sve_index_h, TCG_CALL_NO_RWG, void, ptr, i32, i32, i32) DEF_HELPER_FLAGS_4(sve_index_s, TCG_CALL_NO_RWG, void, ptr, i32, i32, i32) DEF_HELPER_FLAGS_4(sve_index_d, TCG_CALL_NO_RWG, void, ptr, i64, i64, i32) +DEF_HELPER_FLAGS_4(sve_asr_zzw_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asr_zzw_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asr_zzw_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_lsr_zzw_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsr_zzw_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsr_zzw_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_lsl_zzw_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsl_zzw_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsl_zzw_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 9d5c061165..b24f6b2f1b 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -32,6 +32,11 @@ # A combination of tsz:imm3 -- extract (tsz:imm3) - esize %tszimm_shl 22:2 5:5 !function=tszimm_shl +# Similarly for the tszh/tszl pair at 22/16 for zzi +%tszimm16_esz 22:2 16:5 !function=tszimm_esz +%tszimm16_shr 22:2 16:5 !function=tszimm_shr +%tszimm16_shl 22:2 16:5 !function=tszimm_shl + # Either a copy of rd (at bit 0), or a different source # as propagated via the MOVPRFX instruction. %reg_movprfx 0:5 @@ -43,6 +48,7 @@ &rr_esz rd rn esz &rri rd rn imm +&rri_esz rd rn imm esz &rrr_esz rd rn rm esz &rpr_esz rd pg rn esz &rprr_s rd pg rn rm s @@ -92,6 +98,10 @@ @rdn_pg_tszimm ........ .. ... ... ... pg:3 ..... rd:5 \ &rpri_esz rn=%reg_movprfx esz=%tszimm_esz +# Similarly without predicate. +@rd_rn_tszimm ........ .. ... ... ...... rn:5 rd:5 \ + &rri_esz esz=%tszimm16_esz + # Basic Load/Store with 9-bit immediate offset @pd_rn_i9 ........ ........ ...... rn:5 . rd:4 \ &rri imm=%imm9_16_10 @@ -250,6 +260,22 @@ ADDPL 00000100 011 ..... 01010 ...... ..... @rd_rn_i6 # SVE stack frame size RDVL 00000100 101 11111 01010 imm:s6 rd:5 +### SVE Bitwise Shift - Unpredicated Group + +# SVE bitwise shift by immediate (unpredicated) +ASR_zzi 00000100 .. 1 ..... 1001 00 ..... ..... \ + @rd_rn_tszimm imm=%tszimm16_shr +LSR_zzi 00000100 .. 1 ..... 1001 01 ..... ..... \ + @rd_rn_tszimm imm=%tszimm16_shr +LSL_zzi 00000100 .. 1 ..... 1001 11 ..... ..... \ + @rd_rn_tszimm imm=%tszimm16_shl + +# SVE bitwise shift by wide elements (unpredicated) +# Note esz != 3 +ASR_zzw 00000100 .. 1 ..... 1000 00 ..... ..... @rd_rn_rm +LSR_zzw 00000100 .. 1 ..... 1000 01 ..... ..... @rd_rn_rm +LSL_zzw 00000100 .. 1 ..... 1000 11 ..... ..... @rd_rn_rm + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 385bb8b314..f43640c1eb 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -615,6 +615,36 @@ DO_ZPZ(sve_neg_h, uint16_t, H1_2, DO_NEG) DO_ZPZ(sve_neg_s, uint32_t, H1_4, DO_NEG) DO_ZPZ_D(sve_neg_d, uint64_t, DO_NEG) +/* Three-operand expander, unpredicated, in which the third operand is "wide". + */ +#define DO_ZZW(NAME, TYPE, TYPEW, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; ) { \ + TYPEW mm = *(TYPEW *)(vm + i); \ + do { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, mm); \ + i += sizeof(TYPE); \ + } while (i & 7); \ + } \ +} + +DO_ZZW(sve_asr_zzw_b, int8_t, uint64_t, H1, DO_ASR) +DO_ZZW(sve_lsr_zzw_b, uint8_t, uint64_t, H1, DO_LSR) +DO_ZZW(sve_lsl_zzw_b, uint8_t, uint64_t, H1, DO_LSL) + +DO_ZZW(sve_asr_zzw_h, int16_t, uint64_t, H1_2, DO_ASR) +DO_ZZW(sve_lsr_zzw_h, uint16_t, uint64_t, H1_2, DO_LSR) +DO_ZZW(sve_lsl_zzw_h, uint16_t, uint64_t, H1_2, DO_LSL) + +DO_ZZW(sve_asr_zzw_s, int32_t, uint64_t, H1_4, DO_ASR) +DO_ZZW(sve_lsr_zzw_s, uint32_t, uint64_t, H1_4, DO_LSR) +DO_ZZW(sve_lsl_zzw_s, uint32_t, uint64_t, H1_4, DO_LSL) + +#undef DO_ZZW + #undef DO_CLS_B #undef DO_CLS_H #undef DO_CLZ_B diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index f95efa3c72..2c2218bc31 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -134,6 +134,13 @@ static bool do_mov_z(DisasContext *s, int rd, int rn) return do_vector2_z(s, tcg_gen_gvec_mov, 0, rd, rn); } +/* Initialize a Zreg with replications of a 64-bit immediate. */ +static void do_dupi_z(DisasContext *s, int rd, uint64_t word) +{ + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_dup64i(vec_full_reg_offset(s, rd), vsz, vsz, word); +} + /* Invoke a vector expander on two Pregs. */ static bool do_vector2_p(DisasContext *s, GVecGen2Fn *gvec_fn, int esz, int rd, int rn) @@ -668,6 +675,84 @@ DO_ZPZW(LSL, lsl) #undef DO_ZPZW +/* + *** SVE Bitwise Shift - Unpredicated Group + */ + +static bool do_shift_imm(DisasContext *s, arg_rri_esz *a, bool asr, + void (*gvec_fn)(unsigned, uint32_t, uint32_t, + int64_t, uint32_t, uint32_t)) +{ + if (a->esz < 0) { + /* Invalid tsz encoding -- see tszimm_esz. */ + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + /* Shift by element size is architecturally valid. For + arithmetic right-shift, it's the same as by one less. + Otherwise it is a zeroing operation. */ + if (a->imm >= 8 << a->esz) { + if (asr) { + a->imm = (8 << a->esz) - 1; + } else { + do_dupi_z(s, a->rd, 0); + return true; + } + } + gvec_fn(a->esz, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), a->imm, vsz, vsz); + } + return true; +} + +static bool trans_ASR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_shift_imm(s, a, true, tcg_gen_gvec_sari); +} + +static bool trans_LSR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_shift_imm(s, a, false, tcg_gen_gvec_shri); +} + +static bool trans_LSL_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_shift_imm(s, a, false, tcg_gen_gvec_shli); +} + +static bool do_zzw_ool(DisasContext *s, arg_rrr_esz *a, gen_helper_gvec_3 *fn) +{ + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, 0, fn); + } + return true; +} + +#define DO_ZZW(NAME, name) \ +static bool trans_##NAME##_zzw(DisasContext *s, arg_rrr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_3 * const fns[4] = { \ + gen_helper_sve_##name##_zzw_b, gen_helper_sve_##name##_zzw_h, \ + gen_helper_sve_##name##_zzw_s, NULL \ + }; \ + return do_zzw_ool(s, a, fns[a->esz]); \ +} + +DO_ZZW(ASR, asr) +DO_ZZW(LSR, lsr) +DO_ZZW(LSL, lsl) + +#undef DO_ZZW + /* *** SVE Integer Multiply-Add Group */ From 4b242d9c1b6beaf5c81d84e956243b614a4a1d84 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0606/2380] target/arm: Implement SVE Compute Vector Address Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-20-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 5 +++++ target/arm/sve.decode | 12 ++++++++++++ target/arm/sve_helper.c | 40 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 36 ++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 00e3cd48bb..5280d375f9 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -380,6 +380,11 @@ DEF_HELPER_FLAGS_4(sve_lsl_zzw_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_lsl_zzw_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_lsl_zzw_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_adr_p32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_adr_p64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_adr_s32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_adr_u32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index b24f6b2f1b..691876de4e 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -48,6 +48,7 @@ &rr_esz rd rn esz &rri rd rn imm +&rrri rd rn rm imm &rri_esz rd rn imm esz &rrr_esz rd rn rm esz &rpr_esz rd pg rn esz @@ -75,6 +76,9 @@ # Three operand, vector element size @rd_rn_rm ........ esz:2 . rm:5 ... ... rn:5 rd:5 &rrr_esz +# Three operand with "memory" size, aka immediate left shift +@rd_rn_msz_rm ........ ... rm:5 .... imm:2 rn:5 rd:5 &rrri + # Two register operand, with governing predicate, vector element size @rdn_pg_rm ........ esz:2 ... ... ... pg:3 rm:5 rd:5 \ &rprr_esz rn=%reg_movprfx @@ -276,6 +280,14 @@ ASR_zzw 00000100 .. 1 ..... 1000 00 ..... ..... @rd_rn_rm LSR_zzw 00000100 .. 1 ..... 1000 01 ..... ..... @rd_rn_rm LSL_zzw 00000100 .. 1 ..... 1000 11 ..... ..... @rd_rn_rm +### SVE Compute Vector Address Group + +# SVE vector address generation +ADR_s32 00000100 00 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm +ADR_u32 00000100 01 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm +ADR_p32 00000100 10 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm +ADR_p64 00000100 11 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index f43640c1eb..7fa8394aec 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -1062,3 +1062,43 @@ void HELPER(sve_index_d)(void *vd, uint64_t start, d[i] = start + i * incr; } } + +void HELPER(sve_adr_p32)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 4; + uint32_t sh = simd_data(desc); + uint32_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] + (m[i] << sh); + } +} + +void HELPER(sve_adr_p64)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t sh = simd_data(desc); + uint64_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] + (m[i] << sh); + } +} + +void HELPER(sve_adr_s32)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t sh = simd_data(desc); + uint64_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] + ((uint64_t)(int32_t)m[i] << sh); + } +} + +void HELPER(sve_adr_u32)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t sh = simd_data(desc); + uint64_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] + ((uint64_t)(uint32_t)m[i] << sh); + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 2c2218bc31..8924848463 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -893,6 +893,42 @@ static bool trans_RDVL(DisasContext *s, arg_RDVL *a, uint32_t insn) return true; } +/* + *** SVE Compute Vector Address Group + */ + +static bool do_adr(DisasContext *s, arg_rrri *a, gen_helper_gvec_3 *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, a->imm, fn); + } + return true; +} + +static bool trans_ADR_p32(DisasContext *s, arg_rrri *a, uint32_t insn) +{ + return do_adr(s, a, gen_helper_sve_adr_p32); +} + +static bool trans_ADR_p64(DisasContext *s, arg_rrri *a, uint32_t insn) +{ + return do_adr(s, a, gen_helper_sve_adr_p64); +} + +static bool trans_ADR_s32(DisasContext *s, arg_rrri *a, uint32_t insn) +{ + return do_adr(s, a, gen_helper_sve_adr_s32); +} + +static bool trans_ADR_u32(DisasContext *s, arg_rrri *a, uint32_t insn) +{ + return do_adr(s, a, gen_helper_sve_adr_u32); +} + /* *** SVE Predicate Logical Operations Group */ From 0762cd428fd7b471207f5cb5b4bd4bd8f141dbe0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0607/2380] target/arm: Implement SVE floating-point exponential accelerator Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-21-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 4 ++ target/arm/sve.decode | 7 +++ target/arm/sve_helper.c | 90 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 24 ++++++++++ 4 files changed, 125 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 5280d375f9..e2925ff8ec 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -385,6 +385,10 @@ DEF_HELPER_FLAGS_4(sve_adr_p64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_adr_s32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_adr_u32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fexpa_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fexpa_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fexpa_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 691876de4e..cd53b95831 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -66,6 +66,7 @@ # Two operand @pd_pn ........ esz:2 .. .... ....... rn:4 . rd:4 &rr_esz +@rd_rn ........ esz:2 ...... ...... rn:5 rd:5 &rr_esz # Three operand with unused vector element size @rd_rn_rm_e0 ........ ... rm:5 ... ... rn:5 rd:5 &rrr_esz esz=0 @@ -288,6 +289,12 @@ ADR_u32 00000100 01 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm ADR_p32 00000100 10 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm ADR_p64 00000100 11 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm +### SVE Integer Misc - Unpredicated Group + +# SVE floating-point exponential accelerator +# Note esz != 0 +FEXPA 00000100 .. 1 00000 101110 ..... ..... @rd_rn + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 7fa8394aec..6ffb126821 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -1102,3 +1102,93 @@ void HELPER(sve_adr_u32)(void *vd, void *vn, void *vm, uint32_t desc) d[i] = n[i] + ((uint64_t)(uint32_t)m[i] << sh); } } + +void HELPER(sve_fexpa_h)(void *vd, void *vn, uint32_t desc) +{ + /* These constants are cut-and-paste directly from the ARM pseudocode. */ + static const uint16_t coeff[] = { + 0x0000, 0x0016, 0x002d, 0x0045, 0x005d, 0x0075, 0x008e, 0x00a8, + 0x00c2, 0x00dc, 0x00f8, 0x0114, 0x0130, 0x014d, 0x016b, 0x0189, + 0x01a8, 0x01c8, 0x01e8, 0x0209, 0x022b, 0x024e, 0x0271, 0x0295, + 0x02ba, 0x02e0, 0x0306, 0x032e, 0x0356, 0x037f, 0x03a9, 0x03d4, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / 2; + uint16_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz; i++) { + uint16_t nn = n[i]; + intptr_t idx = extract32(nn, 0, 5); + uint16_t exp = extract32(nn, 5, 5); + d[i] = coeff[idx] | (exp << 10); + } +} + +void HELPER(sve_fexpa_s)(void *vd, void *vn, uint32_t desc) +{ + /* These constants are cut-and-paste directly from the ARM pseudocode. */ + static const uint32_t coeff[] = { + 0x000000, 0x0164d2, 0x02cd87, 0x043a29, + 0x05aac3, 0x071f62, 0x08980f, 0x0a14d5, + 0x0b95c2, 0x0d1adf, 0x0ea43a, 0x1031dc, + 0x11c3d3, 0x135a2b, 0x14f4f0, 0x16942d, + 0x1837f0, 0x19e046, 0x1b8d3a, 0x1d3eda, + 0x1ef532, 0x20b051, 0x227043, 0x243516, + 0x25fed7, 0x27cd94, 0x29a15b, 0x2b7a3a, + 0x2d583f, 0x2f3b79, 0x3123f6, 0x3311c4, + 0x3504f3, 0x36fd92, 0x38fbaf, 0x3aff5b, + 0x3d08a4, 0x3f179a, 0x412c4d, 0x4346cd, + 0x45672a, 0x478d75, 0x49b9be, 0x4bec15, + 0x4e248c, 0x506334, 0x52a81e, 0x54f35b, + 0x5744fd, 0x599d16, 0x5bfbb8, 0x5e60f5, + 0x60ccdf, 0x633f89, 0x65b907, 0x68396a, + 0x6ac0c7, 0x6d4f30, 0x6fe4ba, 0x728177, + 0x75257d, 0x77d0df, 0x7a83b3, 0x7d3e0c, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / 4; + uint32_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz; i++) { + uint32_t nn = n[i]; + intptr_t idx = extract32(nn, 0, 6); + uint32_t exp = extract32(nn, 6, 8); + d[i] = coeff[idx] | (exp << 23); + } +} + +void HELPER(sve_fexpa_d)(void *vd, void *vn, uint32_t desc) +{ + /* These constants are cut-and-paste directly from the ARM pseudocode. */ + static const uint64_t coeff[] = { + 0x0000000000000ull, 0x02C9A3E778061ull, 0x059B0D3158574ull, + 0x0874518759BC8ull, 0x0B5586CF9890Full, 0x0E3EC32D3D1A2ull, + 0x11301D0125B51ull, 0x1429AAEA92DE0ull, 0x172B83C7D517Bull, + 0x1A35BEB6FCB75ull, 0x1D4873168B9AAull, 0x2063B88628CD6ull, + 0x2387A6E756238ull, 0x26B4565E27CDDull, 0x29E9DF51FDEE1ull, + 0x2D285A6E4030Bull, 0x306FE0A31B715ull, 0x33C08B26416FFull, + 0x371A7373AA9CBull, 0x3A7DB34E59FF7ull, 0x3DEA64C123422ull, + 0x4160A21F72E2Aull, 0x44E086061892Dull, 0x486A2B5C13CD0ull, + 0x4BFDAD5362A27ull, 0x4F9B2769D2CA7ull, 0x5342B569D4F82ull, + 0x56F4736B527DAull, 0x5AB07DD485429ull, 0x5E76F15AD2148ull, + 0x6247EB03A5585ull, 0x6623882552225ull, 0x6A09E667F3BCDull, + 0x6DFB23C651A2Full, 0x71F75E8EC5F74ull, 0x75FEB564267C9ull, + 0x7A11473EB0187ull, 0x7E2F336CF4E62ull, 0x82589994CCE13ull, + 0x868D99B4492EDull, 0x8ACE5422AA0DBull, 0x8F1AE99157736ull, + 0x93737B0CDC5E5ull, 0x97D829FDE4E50ull, 0x9C49182A3F090ull, + 0xA0C667B5DE565ull, 0xA5503B23E255Dull, 0xA9E6B5579FDBFull, + 0xAE89F995AD3ADull, 0xB33A2B84F15FBull, 0xB7F76F2FB5E47ull, + 0xBCC1E904BC1D2ull, 0xC199BDD85529Cull, 0xC67F12E57D14Bull, + 0xCB720DCEF9069ull, 0xD072D4A07897Cull, 0xD5818DCFBA487ull, + 0xDA9E603DB3285ull, 0xDFC97337B9B5Full, 0xE502EE78B3FF6ull, + 0xEA4AFA2A490DAull, 0xEFA1BEE615A27ull, 0xF50765B6E4540ull, + 0xFA7C1819E90D8ull, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz; i++) { + uint64_t nn = n[i]; + intptr_t idx = extract32(nn, 0, 6); + uint64_t exp = extract32(nn, 6, 11); + d[i] = coeff[idx] | (exp << 52); + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 8924848463..54d774b5e0 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -929,6 +929,30 @@ static bool trans_ADR_u32(DisasContext *s, arg_rrri *a, uint32_t insn) return do_adr(s, a, gen_helper_sve_adr_u32); } +/* + *** SVE Integer Misc - Unpredicated Group + */ + +static bool trans_FEXPA(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_2 * const fns[4] = { + NULL, + gen_helper_sve_fexpa_h, + gen_helper_sve_fexpa_s, + gen_helper_sve_fexpa_d, + }; + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vsz, vsz, 0, fns[a->esz]); + } + return true; +} + /* *** SVE Predicate Logical Operations Group */ From a1f233f25fd502f9a5b40c14df1b4dbdda463487 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0608/2380] target/arm: Implement SVE floating-point trig select coefficient Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-22-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 4 ++++ target/arm/sve.decode | 4 ++++ target/arm/sve_helper.c | 43 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 21 +++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index e2925ff8ec..4f1bd5a62f 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -389,6 +389,10 @@ DEF_HELPER_FLAGS_3(sve_fexpa_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_fexpa_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_fexpa_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ftssel_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ftssel_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ftssel_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index cd53b95831..224dfdd1e9 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -295,6 +295,10 @@ ADR_p64 00000100 11 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm # Note esz != 0 FEXPA 00000100 .. 1 00000 101110 ..... ..... @rd_rn +# SVE floating-point trig select coefficient +# Note esz != 0 +FTSSEL 00000100 .. 1 ..... 101100 ..... ..... @rd_rn_rm + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 6ffb126821..85a0639e3a 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -23,6 +23,7 @@ #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" +#include "fpu/softfloat.h" /* Note that vector data is stored in host-endian 64-bit chunks, @@ -1192,3 +1193,45 @@ void HELPER(sve_fexpa_d)(void *vd, void *vn, uint32_t desc) d[i] = coeff[idx] | (exp << 52); } } + +void HELPER(sve_ftssel_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 2; + uint16_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + uint16_t nn = n[i]; + uint16_t mm = m[i]; + if (mm & 1) { + nn = float16_one; + } + d[i] = nn ^ (mm & 2) << 14; + } +} + +void HELPER(sve_ftssel_s)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 4; + uint32_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + uint32_t nn = n[i]; + uint32_t mm = m[i]; + if (mm & 1) { + nn = float32_one; + } + d[i] = nn ^ (mm & 2) << 30; + } +} + +void HELPER(sve_ftssel_d)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i]; + uint64_t mm = m[i]; + if (mm & 1) { + nn = float64_one; + } + d[i] = nn ^ (mm & 2) << 62; + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 54d774b5e0..ea8d2c4112 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -953,6 +953,27 @@ static bool trans_FEXPA(DisasContext *s, arg_rr_esz *a, uint32_t insn) return true; } +static bool trans_FTSSEL(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_ftssel_h, + gen_helper_sve_ftssel_s, + gen_helper_sve_ftssel_d, + }; + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, 0, fns[a->esz]); + } + return true; +} + /* *** SVE Predicate Logical Operations Group */ From 24e82e68341e73ec0f65534c78c13fd03395b188 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0609/2380] target/arm: Implement SVE Element Count Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-23-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 11 ++ target/arm/sve.decode | 31 +++- target/arm/sve_helper.c | 136 ++++++++++++++++++ target/arm/translate-sve.c | 288 +++++++++++++++++++++++++++++++++++++ 4 files changed, 465 insertions(+), 1 deletion(-) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 4f1bd5a62f..2831e1643b 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -393,6 +393,17 @@ DEF_HELPER_FLAGS_4(sve_ftssel_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_ftssel_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_ftssel_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_sqaddi_b, TCG_CALL_NO_RWG, void, ptr, ptr, s32, i32) +DEF_HELPER_FLAGS_4(sve_sqaddi_h, TCG_CALL_NO_RWG, void, ptr, ptr, s32, i32) +DEF_HELPER_FLAGS_4(sve_sqaddi_s, TCG_CALL_NO_RWG, void, ptr, ptr, s64, i32) +DEF_HELPER_FLAGS_4(sve_sqaddi_d, TCG_CALL_NO_RWG, void, ptr, ptr, s64, i32) + +DEF_HELPER_FLAGS_4(sve_uqaddi_b, TCG_CALL_NO_RWG, void, ptr, ptr, s32, i32) +DEF_HELPER_FLAGS_4(sve_uqaddi_h, TCG_CALL_NO_RWG, void, ptr, ptr, s32, i32) +DEF_HELPER_FLAGS_4(sve_uqaddi_s, TCG_CALL_NO_RWG, void, ptr, ptr, s64, i32) +DEF_HELPER_FLAGS_4(sve_uqaddi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_uqsubi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 224dfdd1e9..b6890d0410 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -22,6 +22,7 @@ ########################################################################### # Named fields. These are primarily for disjoint fields. +%imm4_16_p1 16:4 !function=plus1 %imm6_22_5 22:1 5:5 %imm9_16_10 16:s6 10:3 @@ -56,6 +57,9 @@ &rprr_esz rd pg rn rm esz &rprrr_esz rd pg rn rm ra esz &rpri_esz rd pg rn imm esz +&ptrue rd esz pat s +&incdec_cnt rd pat esz imm d u +&incdec2_cnt rd rn pat esz imm d u ########################################################################### # Named instruction formats. These are generally used to @@ -113,6 +117,13 @@ @rd_rn_i9 ........ ........ ...... rn:5 rd:5 \ &rri imm=%imm9_16_10 +# One register, pattern, and uint4+1. +# User must fill in U and D. +@incdec_cnt ........ esz:2 .. .... ...... pat:5 rd:5 \ + &incdec_cnt imm=%imm4_16_p1 +@incdec2_cnt ........ esz:2 .. .... ...... pat:5 rd:5 \ + &incdec2_cnt imm=%imm4_16_p1 rn=%reg_movprfx + ########################################################################### # Instruction patterns. Grouped according to the SVE encodingindex.xhtml. @@ -299,7 +310,25 @@ FEXPA 00000100 .. 1 00000 101110 ..... ..... @rd_rn # Note esz != 0 FTSSEL 00000100 .. 1 ..... 101100 ..... ..... @rd_rn_rm -### SVE Predicate Logical Operations Group +### SVE Element Count Group + +# SVE element count +CNT_r 00000100 .. 10 .... 1110 0 0 ..... ..... @incdec_cnt d=0 u=1 + +# SVE inc/dec register by element count +INCDEC_r 00000100 .. 11 .... 1110 0 d:1 ..... ..... @incdec_cnt u=1 + +# SVE saturating inc/dec register by element count +SINCDEC_r_32 00000100 .. 10 .... 1111 d:1 u:1 ..... ..... @incdec_cnt +SINCDEC_r_64 00000100 .. 11 .... 1111 d:1 u:1 ..... ..... @incdec_cnt + +# SVE inc/dec vector by element count +# Note this requires esz != 0. +INCDEC_v 00000100 .. 1 1 .... 1100 0 d:1 ..... ..... @incdec2_cnt u=1 + +# SVE saturating inc/dec vector by element count +# Note these require esz != 0. +SINCDEC_v 00000100 .. 1 0 .... 1100 d:1 u:1 ..... ..... @incdec2_cnt # SVE predicate logical operations AND_pppp 00100101 0. 00 .... 01 .... 0 .... 0 .... @pd_pg_pn_pm_s diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 85a0639e3a..979aa5c409 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -1235,3 +1235,139 @@ void HELPER(sve_ftssel_d)(void *vd, void *vn, void *vm, uint32_t desc) d[i] = nn ^ (mm & 2) << 62; } } + +/* + * Signed saturating addition with scalar operand. + */ + +void HELPER(sve_sqaddi_b)(void *d, void *a, int32_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(int8_t)) { + int r = *(int8_t *)(a + i) + b; + if (r > INT8_MAX) { + r = INT8_MAX; + } else if (r < INT8_MIN) { + r = INT8_MIN; + } + *(int8_t *)(d + i) = r; + } +} + +void HELPER(sve_sqaddi_h)(void *d, void *a, int32_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(int16_t)) { + int r = *(int16_t *)(a + i) + b; + if (r > INT16_MAX) { + r = INT16_MAX; + } else if (r < INT16_MIN) { + r = INT16_MIN; + } + *(int16_t *)(d + i) = r; + } +} + +void HELPER(sve_sqaddi_s)(void *d, void *a, int64_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(int32_t)) { + int64_t r = *(int32_t *)(a + i) + b; + if (r > INT32_MAX) { + r = INT32_MAX; + } else if (r < INT32_MIN) { + r = INT32_MIN; + } + *(int32_t *)(d + i) = r; + } +} + +void HELPER(sve_sqaddi_d)(void *d, void *a, int64_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(int64_t)) { + int64_t ai = *(int64_t *)(a + i); + int64_t r = ai + b; + if (((r ^ ai) & ~(ai ^ b)) < 0) { + /* Signed overflow. */ + r = (r < 0 ? INT64_MAX : INT64_MIN); + } + *(int64_t *)(d + i) = r; + } +} + +/* + * Unsigned saturating addition with scalar operand. + */ + +void HELPER(sve_uqaddi_b)(void *d, void *a, int32_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + int r = *(uint8_t *)(a + i) + b; + if (r > UINT8_MAX) { + r = UINT8_MAX; + } else if (r < 0) { + r = 0; + } + *(uint8_t *)(d + i) = r; + } +} + +void HELPER(sve_uqaddi_h)(void *d, void *a, int32_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + int r = *(uint16_t *)(a + i) + b; + if (r > UINT16_MAX) { + r = UINT16_MAX; + } else if (r < 0) { + r = 0; + } + *(uint16_t *)(d + i) = r; + } +} + +void HELPER(sve_uqaddi_s)(void *d, void *a, int64_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + int64_t r = *(uint32_t *)(a + i) + b; + if (r > UINT32_MAX) { + r = UINT32_MAX; + } else if (r < 0) { + r = 0; + } + *(uint32_t *)(d + i) = r; + } +} + +void HELPER(sve_uqaddi_d)(void *d, void *a, uint64_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + uint64_t r = *(uint64_t *)(a + i) + b; + if (r < b) { + r = UINT64_MAX; + } + *(uint64_t *)(d + i) = r; + } +} + +void HELPER(sve_uqsubi_d)(void *d, void *a, uint64_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + uint64_t ai = *(uint64_t *)(a + i); + *(uint64_t *)(d + i) = (ai < b ? 0 : ai - b); + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index ea8d2c4112..2a0bf6b47c 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -57,6 +57,11 @@ static int tszimm_shl(int x) return x - (8 << tszimm_esz(x)); } +static inline int plus1(int x) +{ + return x + 1; +} + /* * Include the generated decoder. */ @@ -1497,6 +1502,289 @@ static bool trans_PNEXT(DisasContext *s, arg_rr_esz *a, uint32_t insn) return do_pfirst_pnext(s, a, gen_helper_sve_pnext); } +/* + *** SVE Element Count Group + */ + +/* Perform an inline saturating addition of a 32-bit value within + * a 64-bit register. The second operand is known to be positive, + * which halves the comparisions we must perform to bound the result. + */ +static void do_sat_addsub_32(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) +{ + int64_t ibound; + TCGv_i64 bound; + TCGCond cond; + + /* Use normal 64-bit arithmetic to detect 32-bit overflow. */ + if (u) { + tcg_gen_ext32u_i64(reg, reg); + } else { + tcg_gen_ext32s_i64(reg, reg); + } + if (d) { + tcg_gen_sub_i64(reg, reg, val); + ibound = (u ? 0 : INT32_MIN); + cond = TCG_COND_LT; + } else { + tcg_gen_add_i64(reg, reg, val); + ibound = (u ? UINT32_MAX : INT32_MAX); + cond = TCG_COND_GT; + } + bound = tcg_const_i64(ibound); + tcg_gen_movcond_i64(cond, reg, reg, bound, bound, reg); + tcg_temp_free_i64(bound); +} + +/* Similarly with 64-bit values. */ +static void do_sat_addsub_64(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2; + + if (u) { + if (d) { + tcg_gen_sub_i64(t0, reg, val); + tcg_gen_movi_i64(t1, 0); + tcg_gen_movcond_i64(TCG_COND_LTU, reg, reg, val, t1, t0); + } else { + tcg_gen_add_i64(t0, reg, val); + tcg_gen_movi_i64(t1, -1); + tcg_gen_movcond_i64(TCG_COND_LTU, reg, t0, reg, t1, t0); + } + } else { + if (d) { + /* Detect signed overflow for subtraction. */ + tcg_gen_xor_i64(t0, reg, val); + tcg_gen_sub_i64(t1, reg, val); + tcg_gen_xor_i64(reg, reg, t0); + tcg_gen_and_i64(t0, t0, reg); + + /* Bound the result. */ + tcg_gen_movi_i64(reg, INT64_MIN); + t2 = tcg_const_i64(0); + tcg_gen_movcond_i64(TCG_COND_LT, reg, t0, t2, reg, t1); + } else { + /* Detect signed overflow for addition. */ + tcg_gen_xor_i64(t0, reg, val); + tcg_gen_add_i64(reg, reg, val); + tcg_gen_xor_i64(t1, reg, val); + tcg_gen_andc_i64(t0, t1, t0); + + /* Bound the result. */ + tcg_gen_movi_i64(t1, INT64_MAX); + t2 = tcg_const_i64(0); + tcg_gen_movcond_i64(TCG_COND_LT, reg, t0, t2, t1, reg); + } + tcg_temp_free_i64(t2); + } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +/* Similarly with a vector and a scalar operand. */ +static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, + TCGv_i64 val, bool u, bool d) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr dptr, nptr; + TCGv_i32 t32, desc; + TCGv_i64 t64; + + dptr = tcg_temp_new_ptr(); + nptr = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(dptr, cpu_env, vec_full_reg_offset(s, rd)); + tcg_gen_addi_ptr(nptr, cpu_env, vec_full_reg_offset(s, rn)); + desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + + switch (esz) { + case MO_8: + t32 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t32, val); + if (d) { + tcg_gen_neg_i32(t32, t32); + } + if (u) { + gen_helper_sve_uqaddi_b(dptr, nptr, t32, desc); + } else { + gen_helper_sve_sqaddi_b(dptr, nptr, t32, desc); + } + tcg_temp_free_i32(t32); + break; + + case MO_16: + t32 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t32, val); + if (d) { + tcg_gen_neg_i32(t32, t32); + } + if (u) { + gen_helper_sve_uqaddi_h(dptr, nptr, t32, desc); + } else { + gen_helper_sve_sqaddi_h(dptr, nptr, t32, desc); + } + tcg_temp_free_i32(t32); + break; + + case MO_32: + t64 = tcg_temp_new_i64(); + if (d) { + tcg_gen_neg_i64(t64, val); + } else { + tcg_gen_mov_i64(t64, val); + } + if (u) { + gen_helper_sve_uqaddi_s(dptr, nptr, t64, desc); + } else { + gen_helper_sve_sqaddi_s(dptr, nptr, t64, desc); + } + tcg_temp_free_i64(t64); + break; + + case MO_64: + if (u) { + if (d) { + gen_helper_sve_uqsubi_d(dptr, nptr, val, desc); + } else { + gen_helper_sve_uqaddi_d(dptr, nptr, val, desc); + } + } else if (d) { + t64 = tcg_temp_new_i64(); + tcg_gen_neg_i64(t64, val); + gen_helper_sve_sqaddi_d(dptr, nptr, t64, desc); + tcg_temp_free_i64(t64); + } else { + gen_helper_sve_sqaddi_d(dptr, nptr, val, desc); + } + break; + + default: + g_assert_not_reached(); + } + + tcg_temp_free_ptr(dptr); + tcg_temp_free_ptr(nptr); + tcg_temp_free_i32(desc); +} + +static bool trans_CNT_r(DisasContext *s, arg_CNT_r *a, uint32_t insn) +{ + if (sve_access_check(s)) { + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + tcg_gen_movi_i64(cpu_reg(s, a->rd), numelem * a->imm); + } + return true; +} + +static bool trans_INCDEC_r(DisasContext *s, arg_incdec_cnt *a, uint32_t insn) +{ + if (sve_access_check(s)) { + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + int inc = numelem * a->imm * (a->d ? -1 : 1); + TCGv_i64 reg = cpu_reg(s, a->rd); + + tcg_gen_addi_i64(reg, reg, inc); + } + return true; +} + +static bool trans_SINCDEC_r_32(DisasContext *s, arg_incdec_cnt *a, + uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + int inc = numelem * a->imm; + TCGv_i64 reg = cpu_reg(s, a->rd); + + /* Use normal 64-bit arithmetic to detect 32-bit overflow. */ + if (inc == 0) { + if (a->u) { + tcg_gen_ext32u_i64(reg, reg); + } else { + tcg_gen_ext32s_i64(reg, reg); + } + } else { + TCGv_i64 t = tcg_const_i64(inc); + do_sat_addsub_32(reg, t, a->u, a->d); + tcg_temp_free_i64(t); + } + return true; +} + +static bool trans_SINCDEC_r_64(DisasContext *s, arg_incdec_cnt *a, + uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + int inc = numelem * a->imm; + TCGv_i64 reg = cpu_reg(s, a->rd); + + if (inc != 0) { + TCGv_i64 t = tcg_const_i64(inc); + do_sat_addsub_64(reg, t, a->u, a->d); + tcg_temp_free_i64(t); + } + return true; +} + +static bool trans_INCDEC_v(DisasContext *s, arg_incdec2_cnt *a, uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + int inc = numelem * a->imm; + + if (inc != 0) { + if (sve_access_check(s)) { + TCGv_i64 t = tcg_const_i64(a->d ? -inc : inc); + tcg_gen_gvec_adds(a->esz, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + t, fullsz, fullsz); + tcg_temp_free_i64(t); + } + } else { + do_mov_z(s, a->rd, a->rn); + } + return true; +} + +static bool trans_SINCDEC_v(DisasContext *s, arg_incdec2_cnt *a, + uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + int inc = numelem * a->imm; + + if (inc != 0) { + if (sve_access_check(s)) { + TCGv_i64 t = tcg_const_i64(inc); + do_sat_addsub_vec(s, a->esz, a->rd, a->rn, t, a->u, a->d); + tcg_temp_free_i64(t); + } + } else { + do_mov_z(s, a->rd, a->rn); + } + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From e1fa1164f397bbd381439ed32d97d9b4b4d7eb43 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0610/2380] target/arm: Implement SVE Bitwise Immediate Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-24-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 17 +++++++++++++ target/arm/translate-sve.c | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index b6890d0410..a3277a0d21 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -49,6 +49,7 @@ &rr_esz rd rn esz &rri rd rn imm +&rr_dbm rd rn dbm &rrri rd rn rm imm &rri_esz rd rn imm esz &rrr_esz rd rn rm esz @@ -111,6 +112,10 @@ @rd_rn_tszimm ........ .. ... ... ...... rn:5 rd:5 \ &rri_esz esz=%tszimm16_esz +# Two register operand, one encoded bitmask. +@rdn_dbm ........ .. .... dbm:13 rd:5 \ + &rr_dbm rn=%reg_movprfx + # Basic Load/Store with 9-bit immediate offset @pd_rn_i9 ........ ........ ...... rn:5 . rd:4 \ &rri imm=%imm9_16_10 @@ -330,6 +335,18 @@ INCDEC_v 00000100 .. 1 1 .... 1100 0 d:1 ..... ..... @incdec2_cnt u=1 # Note these require esz != 0. SINCDEC_v 00000100 .. 1 0 .... 1100 d:1 u:1 ..... ..... @incdec2_cnt +### SVE Bitwise Immediate Group + +# SVE bitwise logical with immediate (unpredicated) +ORR_zzi 00000101 00 0000 ............. ..... @rdn_dbm +EOR_zzi 00000101 01 0000 ............. ..... @rdn_dbm +AND_zzi 00000101 10 0000 ............. ..... @rdn_dbm + +# SVE broadcast bitmask immediate +DUPM 00000101 11 0000 dbm:13 rd:5 + +### SVE Predicate Logical Operations Group + # SVE predicate logical operations AND_pppp 00100101 0. 00 .... 01 .... 0 .... 0 .... @pd_pg_pn_pm_s BIC_pppp 00100101 0. 00 .... 01 .... 0 .... 1 .... @pd_pg_pn_pm_s diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 2a0bf6b47c..9d7c18c48d 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -1785,6 +1785,55 @@ static bool trans_SINCDEC_v(DisasContext *s, arg_incdec2_cnt *a, return true; } +/* + *** SVE Bitwise Immediate Group + */ + +static bool do_zz_dbm(DisasContext *s, arg_rr_dbm *a, GVecGen2iFn *gvec_fn) +{ + uint64_t imm; + if (!logic_imm_decode_wmask(&imm, extract32(a->dbm, 12, 1), + extract32(a->dbm, 0, 6), + extract32(a->dbm, 6, 6))) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + gvec_fn(MO_64, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), imm, vsz, vsz); + } + return true; +} + +static bool trans_AND_zzi(DisasContext *s, arg_rr_dbm *a, uint32_t insn) +{ + return do_zz_dbm(s, a, tcg_gen_gvec_andi); +} + +static bool trans_ORR_zzi(DisasContext *s, arg_rr_dbm *a, uint32_t insn) +{ + return do_zz_dbm(s, a, tcg_gen_gvec_ori); +} + +static bool trans_EOR_zzi(DisasContext *s, arg_rr_dbm *a, uint32_t insn) +{ + return do_zz_dbm(s, a, tcg_gen_gvec_xori); +} + +static bool trans_DUPM(DisasContext *s, arg_DUPM *a, uint32_t insn) +{ + uint64_t imm; + if (!logic_imm_decode_wmask(&imm, extract32(a->dbm, 12, 1), + extract32(a->dbm, 0, 6), + extract32(a->dbm, 6, 6))) { + return false; + } + if (sve_access_check(s)) { + do_dupi_z(s, a->rd, imm); + } + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From f25a2361539626721dbccce14c077cad03b2e72c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0611/2380] target/arm: Implement SVE Integer Wide Immediate - Predicated Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-25-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 10 ++++ target/arm/sve.decode | 19 ++++++- target/arm/sve_helper.c | 108 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 88 ++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 1 deletion(-) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 2831e1643b..79493ab647 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -404,6 +404,16 @@ DEF_HELPER_FLAGS_4(sve_uqaddi_s, TCG_CALL_NO_RWG, void, ptr, ptr, s64, i32) DEF_HELPER_FLAGS_4(sve_uqaddi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_uqsubi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_5(sve_cpy_m_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_5(sve_cpy_m_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_5(sve_cpy_m_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_5(sve_cpy_m_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_cpy_z_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_cpy_z_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_cpy_z_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_cpy_z_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index a3277a0d21..4ee7c78cda 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -22,7 +22,7 @@ ########################################################################### # Named fields. These are primarily for disjoint fields. -%imm4_16_p1 16:4 !function=plus1 +%imm4_16_p1 16:4 !function=plus1 %imm6_22_5 22:1 5:5 %imm9_16_10 16:s6 10:3 @@ -38,6 +38,9 @@ %tszimm16_shr 22:2 16:5 !function=tszimm_shr %tszimm16_shl 22:2 16:5 !function=tszimm_shl +# Signed 8-bit immediate, optionally shifted left by 8. +%sh8_i8s 5:9 !function=expand_imm_sh8s + # Either a copy of rd (at bit 0), or a different source # as propagated via the MOVPRFX instruction. %reg_movprfx 0:5 @@ -112,6 +115,11 @@ @rd_rn_tszimm ........ .. ... ... ...... rn:5 rd:5 \ &rri_esz esz=%tszimm16_esz +# Two register operand, one immediate operand, with 4-bit predicate. +# User must fill in imm. +@rdn_pg4 ........ esz:2 .. pg:4 ... ........ rd:5 \ + &rpri_esz rn=%reg_movprfx + # Two register operand, one encoded bitmask. @rdn_dbm ........ .. .... dbm:13 rd:5 \ &rr_dbm rn=%reg_movprfx @@ -345,6 +353,15 @@ AND_zzi 00000101 10 0000 ............. ..... @rdn_dbm # SVE broadcast bitmask immediate DUPM 00000101 11 0000 dbm:13 rd:5 +### SVE Integer Wide Immediate - Predicated Group + +# SVE copy floating-point immediate (predicated) +FCPY 00000101 .. 01 .... 110 imm:8 ..... @rdn_pg4 + +# SVE copy integer immediate (predicated) +CPY_m_i 00000101 .. 01 .... 01 . ........ ..... @rdn_pg4 imm=%sh8_i8s +CPY_z_i 00000101 .. 01 .... 00 . ........ ..... @rdn_pg4 imm=%sh8_i8s + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 979aa5c409..8c7ea989b1 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -1371,3 +1371,111 @@ void HELPER(sve_uqsubi_d)(void *d, void *a, uint64_t b, uint32_t desc) *(uint64_t *)(d + i) = (ai < b ? 0 : ai - b); } } + +/* Two operand predicated copy immediate with merge. All valid immediates + * can fit within 17 signed bits in the simd_data field. + */ +void HELPER(sve_cpy_m_b)(void *vd, void *vn, void *vg, + uint64_t mm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + mm = dup_const(MO_8, mm); + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i]; + uint64_t pp = expand_pred_b(pg[H1(i)]); + d[i] = (mm & pp) | (nn & ~pp); + } +} + +void HELPER(sve_cpy_m_h)(void *vd, void *vn, void *vg, + uint64_t mm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + mm = dup_const(MO_16, mm); + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i]; + uint64_t pp = expand_pred_h(pg[H1(i)]); + d[i] = (mm & pp) | (nn & ~pp); + } +} + +void HELPER(sve_cpy_m_s)(void *vd, void *vn, void *vg, + uint64_t mm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + mm = dup_const(MO_32, mm); + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i]; + uint64_t pp = expand_pred_s(pg[H1(i)]); + d[i] = (mm & pp) | (nn & ~pp); + } +} + +void HELPER(sve_cpy_m_d)(void *vd, void *vn, void *vg, + uint64_t mm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i]; + d[i] = (pg[H1(i)] & 1 ? mm : nn); + } +} + +void HELPER(sve_cpy_z_b)(void *vd, void *vg, uint64_t val, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + + val = dup_const(MO_8, val); + for (i = 0; i < opr_sz; i += 1) { + d[i] = val & expand_pred_b(pg[H1(i)]); + } +} + +void HELPER(sve_cpy_z_h)(void *vd, void *vg, uint64_t val, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + + val = dup_const(MO_16, val); + for (i = 0; i < opr_sz; i += 1) { + d[i] = val & expand_pred_h(pg[H1(i)]); + } +} + +void HELPER(sve_cpy_z_s)(void *vd, void *vg, uint64_t val, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + + val = dup_const(MO_32, val); + for (i = 0; i < opr_sz; i += 1) { + d[i] = val & expand_pred_s(pg[H1(i)]); + } +} + +void HELPER(sve_cpy_z_d)(void *vd, void *vg, uint64_t val, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + d[i] = (pg[H1(i)] & 1 ? val : 0); + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 9d7c18c48d..9bdd61ff84 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -62,6 +62,12 @@ static inline int plus1(int x) return x + 1; } +/* The SH bit is in bit 8. Extract the low 8 and shift. */ +static inline int expand_imm_sh8s(int x) +{ + return (int8_t)x << (x & 0x100 ? 8 : 0); +} + /* * Include the generated decoder. */ @@ -1834,6 +1840,88 @@ static bool trans_DUPM(DisasContext *s, arg_DUPM *a, uint32_t insn) return true; } +/* + *** SVE Integer Wide Immediate - Predicated Group + */ + +/* Implement all merging copies. This is used for CPY (immediate), + * FCPY, CPY (scalar), CPY (SIMD&FP scalar). + */ +static void do_cpy_m(DisasContext *s, int esz, int rd, int rn, int pg, + TCGv_i64 val) +{ + typedef void gen_cpy(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_i32); + static gen_cpy * const fns[4] = { + gen_helper_sve_cpy_m_b, gen_helper_sve_cpy_m_h, + gen_helper_sve_cpy_m_s, gen_helper_sve_cpy_m_d, + }; + unsigned vsz = vec_full_reg_size(s); + TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + TCGv_ptr t_zd = tcg_temp_new_ptr(); + TCGv_ptr t_zn = tcg_temp_new_ptr(); + TCGv_ptr t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, rd)); + tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, rn)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + + fns[esz](t_zd, t_zn, t_pg, val, desc); + + tcg_temp_free_ptr(t_zd); + tcg_temp_free_ptr(t_zn); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(desc); +} + +static bool trans_FCPY(DisasContext *s, arg_FCPY *a, uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + /* Decode the VFP immediate. */ + uint64_t imm = vfp_expand_imm(a->esz, a->imm); + TCGv_i64 t_imm = tcg_const_i64(imm); + do_cpy_m(s, a->esz, a->rd, a->rn, a->pg, t_imm); + tcg_temp_free_i64(t_imm); + } + return true; +} + +static bool trans_CPY_m_i(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +{ + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + TCGv_i64 t_imm = tcg_const_i64(a->imm); + do_cpy_m(s, a->esz, a->rd, a->rn, a->pg, t_imm); + tcg_temp_free_i64(t_imm); + } + return true; +} + +static bool trans_CPY_z_i(DisasContext *s, arg_CPY_z_i *a, uint32_t insn) +{ + static gen_helper_gvec_2i * const fns[4] = { + gen_helper_sve_cpy_z_b, gen_helper_sve_cpy_z_h, + gen_helper_sve_cpy_z_s, gen_helper_sve_cpy_z_d, + }; + + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_i64 t_imm = tcg_const_i64(a->imm); + tcg_gen_gvec_2i_ool(vec_full_reg_offset(s, a->rd), + pred_full_reg_offset(s, a->pg), + t_imm, vsz, vsz, 0, fns[a->esz]); + tcg_temp_free_i64(t_imm); + } + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From b94f8f60bd841c5b737185cd38263e26822f77ab Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 May 2018 17:48:09 +0100 Subject: [PATCH 0612/2380] target/arm: Implement SVE Permute - Extract Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180516223007.10256-26-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 2 + target/arm/sve.decode | 7 ++++ target/arm/sve_helper.c | 81 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 34 ++++++++++++++++ 4 files changed, 124 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 79493ab647..94f4356ce9 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -414,6 +414,8 @@ DEF_HELPER_FLAGS_4(sve_cpy_z_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_cpy_z_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_cpy_z_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_ext, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 4ee7c78cda..4761d1921e 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -24,6 +24,7 @@ %imm4_16_p1 16:4 !function=plus1 %imm6_22_5 22:1 5:5 +%imm8_16_10 16:5 10:3 %imm9_16_10 16:s6 10:3 # A combination of tsz:imm3 -- extract esize. @@ -362,6 +363,12 @@ FCPY 00000101 .. 01 .... 110 imm:8 ..... @rdn_pg4 CPY_m_i 00000101 .. 01 .... 01 . ........ ..... @rdn_pg4 imm=%sh8_i8s CPY_z_i 00000101 .. 01 .... 00 . ........ ..... @rdn_pg4 imm=%sh8_i8s +### SVE Permute - Extract Group + +# SVE extract vector (immediate offset) +EXT 00000101 001 ..... 000 ... rm:5 rd:5 \ + &rrri rn=%reg_movprfx imm=%imm8_16_10 + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 8c7ea989b1..b825e44cb5 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -1479,3 +1479,84 @@ void HELPER(sve_cpy_z_d)(void *vd, void *vg, uint64_t val, uint32_t desc) d[i] = (pg[H1(i)] & 1 ? val : 0); } } + +/* Big-endian hosts need to frob the byte indicies. If the copy + * happens to be 8-byte aligned, then no frobbing necessary. + */ +static void swap_memmove(void *vd, void *vs, size_t n) +{ + uintptr_t d = (uintptr_t)vd; + uintptr_t s = (uintptr_t)vs; + uintptr_t o = (d | s | n) & 7; + size_t i; + +#ifndef HOST_WORDS_BIGENDIAN + o = 0; +#endif + switch (o) { + case 0: + memmove(vd, vs, n); + break; + + case 4: + if (d < s || d >= s + n) { + for (i = 0; i < n; i += 4) { + *(uint32_t *)H1_4(d + i) = *(uint32_t *)H1_4(s + i); + } + } else { + for (i = n; i > 0; ) { + i -= 4; + *(uint32_t *)H1_4(d + i) = *(uint32_t *)H1_4(s + i); + } + } + break; + + case 2: + case 6: + if (d < s || d >= s + n) { + for (i = 0; i < n; i += 2) { + *(uint16_t *)H1_2(d + i) = *(uint16_t *)H1_2(s + i); + } + } else { + for (i = n; i > 0; ) { + i -= 2; + *(uint16_t *)H1_2(d + i) = *(uint16_t *)H1_2(s + i); + } + } + break; + + default: + if (d < s || d >= s + n) { + for (i = 0; i < n; i++) { + *(uint8_t *)H1(d + i) = *(uint8_t *)H1(s + i); + } + } else { + for (i = n; i > 0; ) { + i -= 1; + *(uint8_t *)H1(d + i) = *(uint8_t *)H1(s + i); + } + } + break; + } +} + +void HELPER(sve_ext)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t opr_sz = simd_oprsz(desc); + size_t n_ofs = simd_data(desc); + size_t n_siz = opr_sz - n_ofs; + + if (vd != vm) { + swap_memmove(vd, vn + n_ofs, n_siz); + swap_memmove(vd + n_siz, vm, n_ofs); + } else if (vd != vn) { + swap_memmove(vd + n_siz, vd, n_ofs); + swap_memmove(vd, vn + n_ofs, n_siz); + } else { + /* vd == vn == vm. Need temp space. */ + ARMVectorReg tmp; + swap_memmove(&tmp, vm, n_ofs); + swap_memmove(vd, vd + n_ofs, n_siz); + memcpy(vd + n_siz, &tmp, n_ofs); + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 9bdd61ff84..c48d4b530a 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -1922,6 +1922,40 @@ static bool trans_CPY_z_i(DisasContext *s, arg_CPY_z_i *a, uint32_t insn) return true; } +/* + *** SVE Permute Extract Group + */ + +static bool trans_EXT(DisasContext *s, arg_EXT *a, uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = vec_full_reg_size(s); + unsigned n_ofs = a->imm >= vsz ? 0 : a->imm; + unsigned n_siz = vsz - n_ofs; + unsigned d = vec_full_reg_offset(s, a->rd); + unsigned n = vec_full_reg_offset(s, a->rn); + unsigned m = vec_full_reg_offset(s, a->rm); + + /* Use host vector move insns if we have appropriate sizes + * and no unfortunate overlap. + */ + if (m != d + && n_ofs == size_for_gvec(n_ofs) + && n_siz == size_for_gvec(n_siz) + && (d != n || n_siz <= n_ofs)) { + tcg_gen_gvec_mov(0, d, n + n_ofs, n_siz, n_siz); + if (n_ofs != 0) { + tcg_gen_gvec_mov(0, d + n_siz, m, n_ofs, n_ofs); + } + } else { + tcg_gen_gvec_3_ool(d, n, m, vsz, vsz, n_ofs, gen_helper_sve_ext); + } + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From 1dd6af7395670f3dcd05ddb64ce6c374a81ae701 Mon Sep 17 00:00:00 2001 From: Igor Druzhinin Date: Tue, 13 Mar 2018 23:14:54 +0000 Subject: [PATCH 0613/2380] xen-pvdevice: Introduce a simplistic xen-pvdevice save state This should help to avoid problems with accessing the device after migration/resume without PV drivers by migrating its PCI configuration space state. Without an explicitly defined state record it resets every time a VM migrates which confuses the OS and makes every access to xen-pvdevice MMIO region to fail. PV tools enable some logic to save and restore PCI configuration state from within the VM every time it migrates which basically hides the issue. Older systems will acquire the new record when migrated which should not change their state for worse. Signed-off-by: Igor Druzhinin Reviewed-by: Paul Durrant Acked-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- hw/i386/xen/xen_pvdevice.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index f748823658..a146f1883a 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -71,6 +71,16 @@ static const MemoryRegionOps xen_pv_mmio_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static const VMStateDescription vmstate_xen_pvdevice = { + .name = "xen-pvdevice", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, XenPVDevice), + VMSTATE_END_OF_LIST() + } +}; + static void xen_pv_realize(PCIDevice *pci_dev, Error **errp) { XenPVDevice *d = XEN_PV_DEVICE(pci_dev); @@ -120,6 +130,7 @@ static void xen_pv_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SYSTEM_OTHER; dc->desc = "Xen PV Device"; dc->props = xen_pv_props; + dc->vmsd = &vmstate_xen_pvdevice; } static const TypeInfo xen_pv_type_info = { From 84201604c77808d6b2fa5139ded83d1e739a3f8c Mon Sep 17 00:00:00 2001 From: Igor Druzhinin Date: Tue, 17 Apr 2018 15:54:15 +0100 Subject: [PATCH 0614/2380] xen/pt: use address_space_memory object for memory region hooks Commit 99605175c (xen-pt: Fix PCI devices re-attach failed) introduced a subtle bug. As soon as the guest switches off Bus Mastering on the device it immediately causes all the BARs be unmapped due to the DMA address space of the device being changed. This is undesired behavior because the guest may try to communicate with the device after that which triggers the following errors in the logs: [00:05.0] xen_pt_bar_read: Error: Should not read BAR through QEMU. @0x0000000000000200 [00:05.0] xen_pt_bar_write: Error: Should not write BAR through QEMU. @0x0000000000000200 The issue that the original patch tried to workaround (uneven number of region_add/del calls on device attach/detach) was fixed in d25836cafd (memory: do explicit cleanup when remove listeners). Signed-off-by: Igor Druzhinin Reported-by: Ross Lagerwall Acked-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- hw/xen/xen_pt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 9b7a960de1..e5a6eff44f 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -907,7 +907,7 @@ out: } } - memory_listener_register(&s->memory_listener, &s->dev.bus_master_as); + memory_listener_register(&s->memory_listener, &address_space_memory); memory_listener_register(&s->io_listener, &address_space_io); s->listener_set = true; XEN_PT_LOG(d, From 70c292afdffe9176a3255cc32d723ddd673dab52 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Fri, 4 May 2018 10:17:56 +0100 Subject: [PATCH 0615/2380] configure: Add explanation for --enable-xen-pci-passthrough Signed-off-by: Anthony PERARD Reviewed-by: Markus Armbruster Signed-off-by: Stefano Stabellini --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 59f91ab3f9..a8498ab393 100755 --- a/configure +++ b/configure @@ -1588,7 +1588,7 @@ disabled with --disable-FEATURE, default is enabled if available: virtfs VirtFS mpath Multipath persistent reservation passthrough xen xen backend driver support - xen-pci-passthrough + xen-pci-passthrough PCI passthrough support for Xen brlapi BrlAPI (Braile) curl curl connectivity membarrier membarrier system call (for Linux 4.14+ or Windows) From dc9e46a29aad8ba5d2fb0871268baa9c7edc0f95 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Mon, 14 May 2018 10:57:46 +0100 Subject: [PATCH 0616/2380] xen_pt: Present the size of 64 bit BARs correctly The full size of the BAR is stored in the lower PCIIORegion.size. The upper PCIIORegion.size is 0. Calculate the size of the upper half correctly from the lower half otherwise the size read by the guest will be incorrect. Signed-off-by: Ross Lagerwall Acked-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- hw/xen/xen_pt_config_init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index a3ce33e78b..aee31c62bb 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -504,6 +504,8 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, bar_ro_mask = XEN_PT_BAR_IO_RO_MASK | (r_size - 1); break; case XEN_PT_BAR_FLAG_UPPER: + assert(index > 0); + r_size = d->io_regions[index - 1].size >> 32; bar_emu_mask = XEN_PT_BAR_ALLF; bar_ro_mask = r_size ? r_size - 1 : 0; break; From 71cec1ed2282a5c172ee09000ef11cf9460eff79 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Tue, 15 May 2018 17:40:51 +0100 Subject: [PATCH 0617/2380] xen-hvm: create separate function for ioreq server initialization The code is sufficiently substantial that it improves code readability to put it in a new function called by xen_hvm_init() rather than having it inline. Signed-off-by: Paul Durrant Reviewed-by: Anthony Perard Signed-off-by: Stefano Stabellini --- hw/i386/xen/xen-hvm.c | 76 ++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index caa563be3d..6ffa3c22cc 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -95,7 +95,8 @@ typedef struct XenIOState { CPUState **cpu_by_vcpu_id; /* the evtchn port for polling the notification, */ evtchn_port_t *ioreq_local_port; - /* evtchn local port for buffered io */ + /* evtchn remote and local ports for buffered io */ + evtchn_port_t bufioreq_remote_port; evtchn_port_t bufioreq_local_port; /* the evtchn fd for polling */ xenevtchn_handle *xce_handle; @@ -1236,12 +1237,52 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); } +static int xen_map_ioreq_server(XenIOState *state) +{ + xen_pfn_t ioreq_pfn; + xen_pfn_t bufioreq_pfn; + evtchn_port_t bufioreq_evtchn; + int rc; + + rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, + &ioreq_pfn, &bufioreq_pfn, + &bufioreq_evtchn); + if (rc < 0) { + error_report("failed to get ioreq server info: error %d handle=%p", + errno, xen_xc); + return rc; + } + + DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); + DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); + + state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &ioreq_pfn, NULL); + if (state->shared_page == NULL) { + error_report("map shared IO page returned error %d handle=%p", + errno, xen_xc); + return -1; + } + + state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &bufioreq_pfn, NULL); + if (state->buffered_io_page == NULL) { + error_report("map buffered IO page returned error %d", errno); + return -1; + } + + state->bufioreq_remote_port = bufioreq_evtchn; + + return 0; +} + void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) { int i, rc; xen_pfn_t ioreq_pfn; - xen_pfn_t bufioreq_pfn; - evtchn_port_t bufioreq_evtchn; XenIOState *state; state = g_malloc0(sizeof (XenIOState)); @@ -1269,25 +1310,8 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) state->wakeup.notify = xen_wakeup_notifier; qemu_register_wakeup_notifier(&state->wakeup); - rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, - &ioreq_pfn, &bufioreq_pfn, - &bufioreq_evtchn); + rc = xen_map_ioreq_server(state); if (rc < 0) { - error_report("failed to get ioreq server info: error %d handle=%p", - errno, xen_xc); - goto err; - } - - DPRINTF("shared page at pfn %lx\n", ioreq_pfn); - DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); - DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); - - state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ|PROT_WRITE, - 1, &ioreq_pfn, NULL); - if (state->shared_page == NULL) { - error_report("map shared IO page returned error %d handle=%p", - errno, xen_xc); goto err; } @@ -1308,14 +1332,6 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } - state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ|PROT_WRITE, - 1, &bufioreq_pfn, NULL); - if (state->buffered_io_page == NULL) { - error_report("map buffered IO page returned error %d", errno); - goto err; - } - /* Note: cpus is empty at this point in init */ state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *)); @@ -1340,7 +1356,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) } rc = xenevtchn_bind_interdomain(state->xce_handle, xen_domid, - bufioreq_evtchn); + state->bufioreq_remote_port); if (rc == -1) { error_report("buffered evtchn bind error %d", errno); goto err; From a68d82b8ad73ff3eea6356b000e1c68dc7acea51 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 20:01:46 -0700 Subject: [PATCH 0618/2380] target/alpha: Honor CPU_DUMP_FPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/alpha/helper.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/target/alpha/helper.c b/target/alpha/helper.c index 8a6a948572..57e2c212b3 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -442,20 +442,19 @@ void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, " PC " TARGET_FMT_lx " PS %02x\n", env->pc, extract32(env->flags, ENV_FLAG_PS_SHIFT, 8)); for (i = 0; i < 31; i++) { - cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i, - linux_reg_names[i], cpu_alpha_load_gr(env, i)); - if ((i % 3) == 2) - cpu_fprintf(f, "\n"); + cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx "%c", i, + linux_reg_names[i], cpu_alpha_load_gr(env, i), + (i % 3) == 2 ? '\n' : ' '); } cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n", env->lock_addr, env->lock_value); - for (i = 0; i < 31; i++) { - cpu_fprintf(f, "FIR%02d " TARGET_FMT_lx " ", i, - *((uint64_t *)(&env->fir[i]))); - if ((i % 3) == 2) - cpu_fprintf(f, "\n"); + if (flags & CPU_DUMP_FPU) { + for (i = 0; i < 31; i++) { + cpu_fprintf(f, "FIR%02d %016" PRIx64 "%c", i, env->fir[i], + (i % 3) == 2 ? '\n' : ' '); + } } cpu_fprintf(f, "\n"); } From 1cc5af6902cd646d91a7d590b365c87ba8909a72 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 20:14:30 -0700 Subject: [PATCH 0619/2380] target/mips: Honor CPU_DUMP_FPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Aurelien Jarno Cc: Yongbok Kim Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/mips/translate.c b/target/mips/translate.c index f1c1fdd35c..e88f983ae7 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -20446,8 +20446,9 @@ void mips_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, env->CP0_Config2, env->CP0_Config3); cpu_fprintf(f, " Config4 0x%08x Config5 0x%08x\n", env->CP0_Config4, env->CP0_Config5); - if (env->hflags & MIPS_HFLAG_FPU) + if ((flags & CPU_DUMP_FPU) && (env->hflags & MIPS_HFLAG_FPU)) { fpu_dump_state(env, f, cpu_fprintf, flags); + } } void mips_tcg_init(void) From 685f1ce236d93177532f5ec42b130a50809bf171 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 20:26:59 -0700 Subject: [PATCH 0620/2380] target/ppc: Honor CPU_DUMP_FPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Alexander Graf Cc: David Gibson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/ppc/translate.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index d5e5f953da..e30d99fcbc 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7048,14 +7048,20 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, } cpu_fprintf(f, " ] RES " TARGET_FMT_lx "\n", env->reserve_addr); - for (i = 0; i < 32; i++) { - if ((i & (RFPL - 1)) == 0) - cpu_fprintf(f, "FPR%02d", i); - cpu_fprintf(f, " %016" PRIx64, *((uint64_t *)&env->fpr[i])); - if ((i & (RFPL - 1)) == (RFPL - 1)) - cpu_fprintf(f, "\n"); + + if (flags & CPU_DUMP_FPU) { + for (i = 0; i < 32; i++) { + if ((i & (RFPL - 1)) == 0) { + cpu_fprintf(f, "FPR%02d", i); + } + cpu_fprintf(f, " %016" PRIx64, *((uint64_t *)&env->fpr[i])); + if ((i & (RFPL - 1)) == (RFPL - 1)) { + cpu_fprintf(f, "\n"); + } + } + cpu_fprintf(f, "FPSCR " TARGET_FMT_lx "\n", env->fpscr); } - cpu_fprintf(f, "FPSCR " TARGET_FMT_lx "\n", env->fpscr); + #if !defined(CONFIG_USER_ONLY) cpu_fprintf(f, " SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx " PVR " TARGET_FMT_lx " VRSAVE " TARGET_FMT_lx "\n", From 86ea188012c26a724bfefe0fb9a838ce808993cc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 20:31:33 -0700 Subject: [PATCH 0621/2380] target/riscv: Honor CPU_DUMP_FPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Reviewed-by: Michael Clark Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/riscv/cpu.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 4e5a56d4e3..d630e8fd6c 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -219,11 +219,13 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, cpu_fprintf(f, "\n"); } } - for (i = 0; i < 32; i++) { - cpu_fprintf(f, " %s %016" PRIx64, - riscv_fpr_regnames[i], env->fpr[i]); - if ((i & 3) == 3) { - cpu_fprintf(f, "\n"); + if (flags & CPU_DUMP_FPU) { + for (i = 0; i < 32; i++) { + cpu_fprintf(f, " %s %016" PRIx64, + riscv_fpr_regnames[i], env->fpr[i]); + if ((i & 3) == 3) { + cpu_fprintf(f, "\n"); + } } } } From af6e5ea28f0b59097a1215e8d063acbb71361b37 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 20:38:23 -0700 Subject: [PATCH 0622/2380] target/s390x: Honor CPU_DUMP_FPU Also do not dump both "fpu" and "vector" registers as the former overlaps the latter. Cc: Alexander Graf Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- target/s390x/helper.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/target/s390x/helper.c b/target/s390x/helper.c index e8548f340a..fd5791f134 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -327,21 +327,22 @@ void s390_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, } } - for (i = 0; i < 16; i++) { - cpu_fprintf(f, "F%02d=%016" PRIx64, i, get_freg(env, i)->ll); - if ((i % 4) == 3) { - cpu_fprintf(f, "\n"); + if (flags & CPU_DUMP_FPU) { + if (s390_has_feat(S390_FEAT_VECTOR)) { + for (i = 0; i < 32; i++) { + cpu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64 "%c", + i, env->vregs[i][0].ll, env->vregs[i][1].ll, + i % 2 ? '\n' : ' '); + } } else { - cpu_fprintf(f, " "); + for (i = 0; i < 16; i++) { + cpu_fprintf(f, "F%02d=%016" PRIx64 "%c", + i, get_freg(env, i)->ll, + (i % 4) == 3 ? '\n' : ' '); + } } } - for (i = 0; i < 32; i++) { - cpu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64, i, - env->vregs[i][0].ll, env->vregs[i][1].ll); - cpu_fprintf(f, (i % 2) ? "\n" : " "); - } - #ifndef CONFIG_USER_ONLY for (i = 0; i < 16; i++) { cpu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]); From d13c394c754cd7d3c86a8968885ad6394d8d69ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 20:42:10 -0700 Subject: [PATCH 0623/2380] target/sparc: Honor CPU_DUMP_FPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Mark Cave-Ayland Cc: Artyom Tarasenko Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/cpu.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index ff6ed91f9a..0f090ece54 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -647,15 +647,18 @@ void sparc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, } } - for (i = 0; i < TARGET_DPREGS; i++) { - if ((i & 3) == 0) { - cpu_fprintf(f, "%%f%02d: ", i * 2); - } - cpu_fprintf(f, " %016" PRIx64, env->fpr[i].ll); - if ((i & 3) == 3) { - cpu_fprintf(f, "\n"); + if (flags & CPU_DUMP_FPU) { + for (i = 0; i < TARGET_DPREGS; i++) { + if ((i & 3) == 0) { + cpu_fprintf(f, "%%f%02d: ", i * 2); + } + cpu_fprintf(f, " %016" PRIx64, env->fpr[i].ll); + if ((i & 3) == 3) { + cpu_fprintf(f, "\n"); + } } } + #ifdef TARGET_SPARC64 cpu_fprintf(f, "pstate: %08x ccr: %02x (icc: ", env->pstate, (unsigned)cpu_get_ccr(env)); From a651e033c6cae17d0d05902158d35ca6bf25eb5d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 20:44:33 -0700 Subject: [PATCH 0624/2380] target/unicore32: Honor CPU_DUMP_FPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Guan Xuetao Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/unicore32/translate.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/unicore32/translate.c b/target/unicore32/translate.c index abe2ea8592..3cae111955 100644 --- a/target/unicore32/translate.c +++ b/target/unicore32/translate.c @@ -2101,7 +2101,9 @@ void uc32_cpu_dump_state(CPUState *cs, FILE *f, psr & (1 << 28) ? 'V' : '-', cpu_mode_names[psr & 0xf]); - cpu_dump_state_ucf64(env, f, cpu_fprintf, flags); + if (flags & CPU_DUMP_FPU) { + cpu_dump_state_ucf64(env, f, cpu_fprintf, flags); + } } void restore_state_to_opc(CPUUniCore32State *env, TranslationBlock *tb, From f29c0b170fa9e0568f2d02e764e18b00cad3a27f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 May 2018 20:46:23 -0700 Subject: [PATCH 0625/2380] target/xtensa: Honor CPU_DUMP_FPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Max Filippov Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/xtensa/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index ae0feb0254..53f6f5db8f 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -1243,7 +1243,8 @@ void xtensa_cpu_dump_state(CPUState *cs, FILE *f, } } - if (xtensa_option_enabled(env->config, XTENSA_OPTION_FP_COPROCESSOR)) { + if ((flags & CPU_DUMP_FPU) && + xtensa_option_enabled(env->config, XTENSA_OPTION_FP_COPROCESSOR)) { cpu_fprintf(f, "\n"); for (i = 0; i < 16; ++i) { From 1d349821551c2da4dfefe36c6ac17319f33ebbd5 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Mon, 5 Mar 2018 17:13:30 -0500 Subject: [PATCH 0626/2380] tcg: fix s/compliment/complement/ typos Signed-off-by: Emilio G. Cota Signed-off-by: Michael Tokarev --- target/i386/translate.c | 2 +- target/m68k/translate.c | 2 +- tcg/README | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/translate.c b/target/i386/translate.c index b0f69838f2..7c21814676 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -113,7 +113,7 @@ typedef struct DisasContext { int rex_x, rex_b; #endif int vex_l; /* vex vector length */ - int vex_v; /* vex vvvv register, without 1's compliment. */ + int vex_v; /* vex vvvv register, without 1's complement. */ int ss32; /* 32 bit stack segment */ CCOp cc_op; /* current CC operation */ bool cc_op_dirty; diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 44a0ac4e2e..8959e4d0f0 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -4002,7 +4002,7 @@ DISAS_INSN(bfext_reg) TCGv shift; /* In general, we're going to rotate the field so that it's at the - top of the word and then right-shift by the compliment of the + top of the word and then right-shift by the complement of the width to extend the field. */ if (ext & 0x20) { /* Variable width. */ diff --git a/tcg/README b/tcg/README index a5237a9edb..d22ee084b8 100644 --- a/tcg/README +++ b/tcg/README @@ -561,7 +561,7 @@ E.g. VECL=1 -> 64 << 1 -> v128, and VECE=2 -> 1 << 2 -> i32. * orc_vec v0, v1, v2 * not_vec v0, v1 - Similarly, logical operations with and without compliment. + Similarly, logical operations with and without complement. Note that VECE is unused. * shli_vec v0, v1, i2 From 787bbc306e2770c73f9fd9d3a209d26af191e352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 6 Mar 2018 13:44:02 +0000 Subject: [PATCH 0627/2380] misc, ide: remove use of HWADDR_PRIx in trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The trace events all use a uint64_t data type, so should be using the corresponding PRIx64 format, not HWADDR_PRIx which is intended for use with the 'hwaddr' type. Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Michael Tokarev --- hw/ide/trace-events | 4 ++-- hw/misc/trace-events | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 0c39cabe72..5c0e59ec42 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -108,8 +108,8 @@ ahci_dma_prepare_buf_fail(void *s, int port) "ahci(%p)[%d]: sglist population fa ahci_dma_rw_buf(void *s, int port, int l) "ahci(%p)[%d] len=0x%x" ahci_cmd_done(void *s, int port) "ahci(%p)[%d]: cmd done" ahci_reset(void *s) "ahci(%p): HBA reset" -allwinner_ahci_mem_read(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): read a=%p addr=0x%"HWADDR_PRIx" val=0x%"PRIx64", size=%d" -allwinner_ahci_mem_write(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): write a=%p addr=0x%"HWADDR_PRIx" val=0x%"PRIx64", size=%d" +allwinner_ahci_mem_read(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): read a=%p addr=0x%"PRIx64" val=0x%"PRIx64", size=%d" +allwinner_ahci_mem_write(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): write a=%p addr=0x%"PRIx64" val=0x%"PRIx64", size=%d" # Warning: Verbose handle_reg_h2d_fis_dump(void *s, int port, const char *fis) "ahci(%p)[%d]: %s" diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 562d9ed005..ec5a9f0da1 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -69,13 +69,13 @@ mps2_fpgaio_reset(void) "MPS2 FPGAIO: reset" mps2_fpgaio_leds(char led1, char led0) "MPS2 FPGAIO LEDs: %c%c" # hw/misc/msf2-sysreg.c -msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" HWADDR_PRIx " data 0x%" PRIx32 " prev 0x%" PRIx32 -msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" HWADDR_PRIx " data 0x%08" PRIx32 +msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" PRIx64 " data 0x%" PRIx32 " prev 0x%" PRIx32 +msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" PRIx64 " data 0x%08" PRIx32 msf2_sysreg_write_pll_status(void) "Invalid write to read only PLL status register" #hw/misc/imx7_gpr.c -imx7_gpr_read(uint64_t offset) "addr 0x%08" HWADDR_PRIx -imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" HWADDR_PRIx "value 0x%08" HWADDR_PRIx +imx7_gpr_read(uint64_t offset) "addr 0x%08" PRIx64 +imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx64 # hw/misc/mos6522.c mos6522_set_counter(int index, unsigned int val) "T%d.counter=%d" From 3ad9319f5cf560567ae75c2d94614e979409d580 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 1 Feb 2018 20:35:45 +1100 Subject: [PATCH 0628/2380] slirp/debug: Print IP addresses in human readable form Signed-off-by: Alexey Kardashevskiy Signed-off-by: Michael Tokarev --- slirp/arp_table.c | 4 ++-- slirp/socket.c | 8 ++++---- slirp/udp.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/slirp/arp_table.c b/slirp/arp_table.c index 3547043555..bac608f97f 100644 --- a/slirp/arp_table.c +++ b/slirp/arp_table.c @@ -33,7 +33,7 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]) int i; DEBUG_CALL("arp_table_add"); - DEBUG_ARG("ip = 0x%x", ip_addr); + DEBUG_ARG("ip = %s", inet_ntoa(*(struct in_addr *)&ip_addr)); DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", ethaddr[0], ethaddr[1], ethaddr[2], ethaddr[3], ethaddr[4], ethaddr[5])); @@ -67,7 +67,7 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr, int i; DEBUG_CALL("arp_table_search"); - DEBUG_ARG("ip = 0x%x", ip_addr); + DEBUG_ARG("ip = %s", inet_ntoa(*(struct in_addr *)&ip_addr)); /* If broadcast address */ if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) { diff --git a/slirp/socket.c b/slirp/socket.c index cb7b5b608d..61347d1a0c 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -701,10 +701,10 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, memset(&addr, 0, addrlen); DEBUG_CALL("tcp_listen"); - DEBUG_ARG("haddr = %x", haddr); - DEBUG_ARG("hport = %d", hport); - DEBUG_ARG("laddr = %x", laddr); - DEBUG_ARG("lport = %d", lport); + DEBUG_ARG("haddr = %s", inet_ntoa(*(struct in_addr *)&haddr)); + DEBUG_ARG("hport = %d", ntohs(hport)); + DEBUG_ARG("laddr = %s", inet_ntoa(*(struct in_addr *)&laddr)); + DEBUG_ARG("lport = %d", ntohs(lport)); DEBUG_ARG("flags = %x", flags); so = socreate(slirp); diff --git a/slirp/udp.c b/slirp/udp.c index 227d779022..e5bf065bf2 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -241,8 +241,8 @@ int udp_output(struct socket *so, struct mbuf *m, DEBUG_CALL("udp_output"); DEBUG_ARG("so = %p", so); DEBUG_ARG("m = %p", m); - DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr); - DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr); + DEBUG_ARG("saddr = %s", inet_ntoa(saddr->sin_addr)); + DEBUG_ARG("daddr = %s", inet_ntoa(daddr->sin_addr)); /* * Adjust for header From 0b816e986dd5b39ff5dfacd6c74e012b3b4c09e1 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sun, 20 May 2018 08:28:33 +0300 Subject: [PATCH 0629/2380] qemu-option-trace: -trace enable= is a pattern, not a file Signed-off-by: Michael Tokarev --- qemu-option-trace.texi | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qemu-option-trace.texi b/qemu-option-trace.texi index 4166d5cdc2..7d1b7f05c5 100644 --- a/qemu-option-trace.texi +++ b/qemu-option-trace.texi @@ -2,9 +2,8 @@ Specify tracing options. @table @option @item [enable=]@var{pattern} -Immediately enable events matching @var{pattern}. -The file must contain one event name (as listed in the @file{trace-events-all} -file) per line; globbing patterns are accepted too. This option is only +Immediately enable events matching @var{pattern} +(either event name or a globbing pattern). This option is only available if QEMU has been compiled with the @var{simple}, @var{log} or @var{ftrace} tracing backend. To specify multiple events or patterns, specify the @option{-trace} option multiple times. From f7c922ed3d8e3cb54febbdc594ce9f4400e0d290 Mon Sep 17 00:00:00 2001 From: Murilo Opsfelder Araujo Date: Tue, 15 May 2018 10:49:50 -0300 Subject: [PATCH 0630/2380] HACKING: document preference for g_new instead of g_malloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch documents the preference for g_new instead of g_malloc. The reasons were adapted from commit b45c03f585ea9bb1af76c73e82195418c294919d. Discussion in QEMU's mailing list: http://lists.nongnu.org/archive/html/qemu-devel/2018-05/msg03238.html Cc: qemu-devel@nongnu.org Cc: David Hildenbrand Cc: Eduardo Habkost Cc: Markus Armbruster Cc: Paolo Bonzini Signed-off-by: Murilo Opsfelder Araujo Reviewed-by: Eric Blake Reviewed-by: Alex Bennée Reviewed-by: David Hildenbrand Signed-off-by: Michael Tokarev --- HACKING | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/HACKING b/HACKING index 4125c97d8d..0fc3e0fc04 100644 --- a/HACKING +++ b/HACKING @@ -118,6 +118,15 @@ Please note that g_malloc will exit on allocation failure, so there is no need to test for failure (as you would have to with malloc). Calling g_malloc with a zero size is valid and will return NULL. +Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following +reasons: + + a. It catches multiplication overflowing size_t; + b. It returns T * instead of void *, letting compiler catch more type + errors. + +Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though. + Memory allocated by qemu_memalign or qemu_blockalign must be freed with qemu_vfree, since breaking this will cause problems on Win32. From 65f389c0e7f6660e1acbd93e2ad23ce7172324bf Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 3 May 2018 18:56:44 -0400 Subject: [PATCH 0631/2380] qemu-img-commands.hx: argument ordering fixups The TEXI and string versions are actually identical, except for markup. We can probably automate this... but make the ordering the same until then. Signed-off-by: John Snow Reviewed-by: Jeff Cody Signed-off-by: Michael Tokarev --- qemu-img-cmds.hx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 2fe31893cf..8bcefcafe9 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -23,13 +23,13 @@ STEXI ETEXI DEF("check", img_check, - "check [-q] [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename") + "check [--object objectdef] [--image-opts] [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename") STEXI @item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{filename} ETEXI DEF("commit", img_commit, - "commit [-q] [--object objectdef] [--image-opts] [-f fmt] [-t cache] [-b base] [-d] [-p] filename") + "commit [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-b base] [-d] [-p] filename") STEXI @item commit [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} ETEXI @@ -47,7 +47,7 @@ STEXI ETEXI DEF("create", img_create, - "create [-q] [--object objectdef] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-o options] filename [size]") + "create [--object objectdef] [-q] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-o options] filename [size]") STEXI @item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}] ETEXI From 83e6da02b6bd3401fd6e5ea57915b84144465740 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 3 May 2018 18:56:45 -0400 Subject: [PATCH 0632/2380] qemu-img.texi: fix command ordering This should match the summary ordering, which is alphabetical. Signed-off-by: John Snow Reviewed-by: Jeff Cody Signed-off-by: Michael Tokarev --- qemu-img.texi | 58 ++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/qemu-img.texi b/qemu-img.texi index 8a26400adb..adf5176902 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -193,6 +193,12 @@ sets the number of input blocks to skip Command description: @table @option + +@item amend [-p] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} + +Amends the image format specific @var{options} for the image file +@var{filename}. Not all file formats support this operation. + @item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] @var{filename} Run a simple sequential I/O benchmark on the specified image. If @code{-w} is @@ -253,30 +259,6 @@ If @code{-r} is specified, exit codes representing the image state refer to the state after (the attempt at) repairing it. That is, a successful @code{-r all} will yield the exit code 0, independently of the image state before. -@item create [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}] - -Create the new disk image @var{filename} of size @var{size} and format -@var{fmt}. Depending on the file format, you can add one or more @var{options} -that enable additional features of this format. - -If the option @var{backing_file} is specified, then the image will record -only the differences from @var{backing_file}. No size needs to be specified in -this case. @var{backing_file} will never be modified unless you use the -@code{commit} monitor command (or qemu-img commit). - -If a relative path name is given, the backing file is looked up relative to -the directory containing @var{filename}. - -Note that a given backing file will be opened to check that it is valid. Use -the @code{-u} option to enable unsafe backing file mode, which means that the -image will be created even if the associated backing file cannot be opened. A -matching backing file must be created or additional options be used to make the -backing file specification valid when you want to use an image created this -way. - -The size can also be specified using the @var{size} option with @code{-o}, -it doesn't need to be specified separately in this case. - @item commit [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} Commit the changes recorded in @var{filename} in its base image or backing file. @@ -381,6 +363,30 @@ creating compressed images. @var{num_coroutines} specifies how many coroutines work in parallel during the convert process (defaults to 8). +@item create [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}] + +Create the new disk image @var{filename} of size @var{size} and format +@var{fmt}. Depending on the file format, you can add one or more @var{options} +that enable additional features of this format. + +If the option @var{backing_file} is specified, then the image will record +only the differences from @var{backing_file}. No size needs to be specified in +this case. @var{backing_file} will never be modified unless you use the +@code{commit} monitor command (or qemu-img commit). + +If a relative path name is given, the backing file is looked up relative to +the directory containing @var{filename}. + +Note that a given backing file will be opened to check that it is valid. Use +the @code{-u} option to enable unsafe backing file mode, which means that the +image will be created even if the associated backing file cannot be opened. A +matching backing file must be created or additional options be used to make the +backing file specification valid when you want to use an image created this +way. + +The size can also be specified using the @var{size} option with @code{-o}, +it doesn't need to be specified separately in this case. + @item dd [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output} Dd copies from @var{input} file to @var{output} file converting it from @@ -585,10 +591,6 @@ how the additional image area should be allocated on the host. See the format description in the @code{NOTES} section which values are allowed. Using this option may result in slightly more data being allocated than necessary. -@item amend [-p] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} - -Amends the image format specific @var{options} for the image file -@var{filename}. Not all file formats support this operation. @end table @c man end From 183861456d5e01649df9591a535486c10d72e060 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 3 May 2018 18:56:46 -0400 Subject: [PATCH 0633/2380] qemu-img: remove references to GEN_DOCS Nothing seemingly uses this. (jcody: commit 77bd1119ba even mentions that it appears unused) Signed-off-by: John Snow Reviewed-by: Jeff Cody Signed-off-by: Michael Tokarev --- qemu-img.c | 2 -- qemu-options-wrapper.h | 1 - 2 files changed, 3 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 60e45ec103..2b5a5706b6 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -123,7 +123,6 @@ static void QEMU_NORETURN help(void) " " arg_string "\n" #include "qemu-img-cmds.h" #undef DEF -#undef GEN_DOCS "\n" "Command parameters:\n" " 'filename' is a disk image filename\n" @@ -4716,7 +4715,6 @@ static const img_cmd_t img_cmds[] = { { option, callback }, #include "qemu-img-cmds.h" #undef DEF -#undef GEN_DOCS { NULL, NULL, }, }; diff --git a/qemu-options-wrapper.h b/qemu-options-wrapper.h index 13bfea0294..6f548e3922 100644 --- a/qemu-options-wrapper.h +++ b/qemu-options-wrapper.h @@ -34,7 +34,6 @@ #undef DEF #undef DEFHEADING #undef ARCHHEADING -#undef GEN_DOCS #undef QEMU_OPTIONS_GENERATE_ENUM #undef QEMU_OPTIONS_GENERATE_HELP From 9775fcdb11ab425cc3537ebe95dcf818bedfc954 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 3 May 2018 18:56:47 -0400 Subject: [PATCH 0634/2380] qemu-img: Make documentation between .texi and .hx consistent These are also different and out of order for whatever reason. I'd like to automate this in the future, but for now let's put on the band-aid. In the case of resize, there were options missing from all three docstrings; the new string is based on the code. Signed-off-by: John Snow Reviewed-by: Jeff Cody Signed-off-by: Michael Tokarev --- qemu-img-cmds.hx | 4 ++-- qemu-img.texi | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 8bcefcafe9..84deb858af 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -89,9 +89,9 @@ STEXI ETEXI DEF("resize", img_resize, - "resize [--object objectdef] [--image-opts] [-q] [--shrink] filename [+ | -]size") + "resize [--object objectdef] [--image-opts] [-f fmt] [--preallocation=prealloc] [-q] [--shrink] filename [+ | -]size") STEXI -@item resize [--object @var{objectdef}] [--image-opts] [-q] [--shrink] @var{filename} [+ | -]@var{size} +@item resize [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--preallocation=@var{prealloc}] [-q] [--shrink] @var{filename} [+ | -]@var{size} ETEXI STEXI diff --git a/qemu-img.texi b/qemu-img.texi index adf5176902..2be8206a05 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -194,12 +194,12 @@ Command description: @table @option -@item amend [-p] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} +@item amend [--object @var{objectdef}] [--image-opts] [-p] [-p] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} Amends the image format specific @var{options} for the image file @var{filename}. Not all file formats support this operation. -@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] @var{filename} +@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] [-U] @var{filename} Run a simple sequential I/O benchmark on the specified image. If @code{-w} is specified, a write test is performed, otherwise a read test is performed. @@ -223,7 +223,7 @@ specified as well. For write tests, by default a buffer filled with zeros is written. This can be overridden with a pattern byte specified by @var{pattern}. -@item check [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] @var{filename} +@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{filename} Perform a consistency check on the disk image @var{filename}. The command can output in the format @var{ofmt} which is either @code{human} or @code{json}. @@ -259,7 +259,7 @@ If @code{-r} is specified, exit codes representing the image state refer to the state after (the attempt at) repairing it. That is, a successful @code{-r all} will yield the exit code 0, independently of the image state before. -@item commit [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} +@item commit [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} Commit the changes recorded in @var{filename} in its base image or backing file. If the backing file is smaller than the snapshot, then the backing file will be @@ -281,7 +281,7 @@ all images between @var{base} and the top image will be invalid and may return garbage data when read. For this reason, @code{-b} implies @code{-d} (so that the top image stays valid). -@item compare [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-s] [-q] @var{filename1} @var{filename2} +@item compare [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] [-U] @var{filename1} @var{filename2} Check if two images have the same content. You can compare images with different format or settings. @@ -322,7 +322,7 @@ Error on reading data @end table -@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-m @var{num_coroutines}] [-W] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} Convert the disk image @var{filename} or a snapshot @var{snapshot_param}(@var{snapshot_id_or_name} is deprecated) to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} @@ -363,7 +363,7 @@ creating compressed images. @var{num_coroutines} specifies how many coroutines work in parallel during the convert process (defaults to 8). -@item create [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}] +@item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}] Create the new disk image @var{filename} of size @var{size} and format @var{fmt}. Depending on the file format, you can add one or more @var{options} @@ -387,7 +387,7 @@ way. The size can also be specified using the @var{size} option with @code{-o}, it doesn't need to be specified separately in this case. -@item dd [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output} +@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output} Dd copies from @var{input} file to @var{output} file converting it from @var{fmt} format to @var{output_fmt} format. @@ -398,7 +398,7 @@ dd will stop reading input after reading @var{blocks} input blocks. The size syntax is similar to dd(1)'s size syntax. -@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename} +@item info [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] [-U] @var{filename} Give information about the disk image @var{filename}. Use it in particular to know the size reserved on disk which can be different @@ -506,11 +506,11 @@ been written to all sectors. This is the maximum size that the image file can occupy with the exception of internal snapshots, dirty bitmaps, vmstate data, and other advanced image format features. -@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot} ] @var{filename} +@item snapshot [--object @var{objectdef}] [--image-opts] [-U] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename} List, apply, create or delete snapshots in image @var{filename}. -@item rebase [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} +@item rebase [--object @var{objectdef}] [--image-opts] [-U] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} Changes the backing file of an image. Only the formats @code{qcow2} and @code{qed} support changing the backing file. @@ -570,7 +570,7 @@ qemu-img rebase -b base.img diff.qcow2 At this point, @code{modified.img} can be discarded, since @code{base.img + diff.qcow2} contains the same information. -@item resize [--shrink] [--preallocation=@var{prealloc}] @var{filename} [+ | -]@var{size} +@item resize [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--preallocation=@var{prealloc}] [-q] [--shrink] @var{filename} [+ | -]@var{size} Change the disk image as if it had been created with @var{size}. From 4885b0caf607579a0f99c221fa39ca97d9a6ddf4 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 3 May 2018 18:56:48 -0400 Subject: [PATCH 0635/2380] qemu-img-cmds.hx: add passive-aggressive note I'm kidding. It's very easy to forget there are per-command sections in the texi, and insane that we don't autogenerate those, too. Until then, leave a little post-it note in this .hx file until I find a way to delete it. Signed-off-by: John Snow Reviewed-by: Jeff Cody Signed-off-by: Michael Tokarev --- qemu-img-cmds.hx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 84deb858af..3d2f7b26eb 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -6,6 +6,9 @@ HXCOMM DEF(command, callback, arg_string) is used to construct HXCOMM command structures and help message. HXCOMM HXCOMM can be used for comments, discarded from both texi and C +HXCOMM When amending the TEXI sections, please remember to copy the usage +HXCOMM over to the per-command sections in qemu-img.texi. + STEXI @table @option ETEXI From 6b1875476313ce320a6a1d1827e85511365a469c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 May 2018 11:49:48 -0300 Subject: [PATCH 0636/2380] hw/ide/ahci: Keep ALLWINNER_AHCI() macro internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ALLWINNER_AHCI() macro is only used in ahci-allwinner.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: John Snow Signed-off-by: Michael Tokarev --- hw/ide/ahci-allwinner.c | 3 +++ hw/ide/ahci_internal.h | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/ide/ahci-allwinner.c b/hw/ide/ahci-allwinner.c index 5397483fd8..2fd95078ba 100644 --- a/hw/ide/ahci-allwinner.c +++ b/hw/ide/ahci-allwinner.c @@ -24,6 +24,9 @@ #include "trace.h" +#define ALLWINNER_AHCI(obj) \ + OBJECT_CHECK(AllwinnerAHCIState, (obj), TYPE_ALLWINNER_AHCI) + #define ALLWINNER_AHCI_BISTAFR ((0xa0 - ALLWINNER_AHCI_MMIO_OFF) / 4) #define ALLWINNER_AHCI_BISTCR ((0xa4 - ALLWINNER_AHCI_MMIO_OFF) / 4) #define ALLWINNER_AHCI_BISTFCTR ((0xa8 - ALLWINNER_AHCI_MMIO_OFF) / 4) diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index 8c755d4ca1..1a25d6c039 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -375,7 +375,4 @@ void ahci_reset(AHCIState *s); #define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI) -#define ALLWINNER_AHCI(obj) OBJECT_CHECK(AllwinnerAHCIState, (obj), \ - TYPE_ALLWINNER_AHCI) - #endif /* HW_IDE_AHCI_H */ From 7647d5c6b5e3b3f36a6e0441c81ae3fe797eb233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 8 May 2018 11:29:35 -0400 Subject: [PATCH 0637/2380] tests: fix tpm-crb tpm-tis tests race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to close the TPM data socket on the emulator end, qemu will close it after a SHUTDOWN. This avoids a race between close() and read() in the TPM data thread. Reported-by: Peter Maydell Signed-off-by: Marc-André Lureau Signed-off-by: Stefan Berger Reviewed-by: Daniel P. Berrangé Signed-off-by: Michael Tokarev --- tests/tpm-emu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tpm-emu.c b/tests/tpm-emu.c index 4dada76834..8c2bd53cad 100644 --- a/tests/tpm-emu.c +++ b/tests/tpm-emu.c @@ -125,7 +125,7 @@ void *tpm_emu_ctrl_thread(void *data) case CMD_SHUTDOWN: { ptm_res res = 0; qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); - qio_channel_close(s->tpm_ioc, &error_abort); + /* the tpm data thread is expected to finish now */ g_thread_join(s->emu_tpm_thread); break; } From f23c81073a4f9aa41a3687161f6ca3d66501a280 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 30 Apr 2018 09:32:19 +0200 Subject: [PATCH 0638/2380] trivial: Do not include pci.h if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to include pci.h in these files. Signed-off-by: Thomas Huth Reviewed-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Michael Tokarev --- include/hw/ppc/ppc4xx.h | 2 -- include/hw/virtio/virtio-balloon.h | 1 - include/hw/virtio/virtio-gpu.h | 1 - 3 files changed, 4 deletions(-) diff --git a/include/hw/ppc/ppc4xx.h b/include/hw/ppc/ppc4xx.h index cb0bb55cec..3a2a04c8ce 100644 --- a/include/hw/ppc/ppc4xx.h +++ b/include/hw/ppc/ppc4xx.h @@ -25,8 +25,6 @@ #ifndef PPC4XX_H #define PPC4XX_H -#include "hw/pci/pci.h" - /* PowerPC 4xx core initialization */ PowerPCCPU *ppc4xx_init(const char *cpu_model, clk_setup_t *cpu_clk, clk_setup_t *tb_clk, diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index 1ea13bd6a4..e0df3528c8 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -17,7 +17,6 @@ #include "standard-headers/linux/virtio_balloon.h" #include "hw/virtio/virtio.h" -#include "hw/pci/pci.h" #define TYPE_VIRTIO_BALLOON "virtio-balloon-device" #define VIRTIO_BALLOON(obj) \ diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 22ac3c2d0e..79bb3fb3dd 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -18,7 +18,6 @@ #include "ui/qemu-pixman.h" #include "ui/console.h" #include "hw/virtio/virtio.h" -#include "hw/pci/pci.h" #include "qemu/log.h" #include "standard-headers/linux/virtio_gpu.h" From 4a4ff4c58fd750cde01c8b15d30d038cefc90a42 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 23 Mar 2018 15:32:02 +0100 Subject: [PATCH 0639/2380] Remove unnecessary variables for function return value Re-run Coccinelle script scripts/coccinelle/return_directly.cocci Signed-off-by: Laurent Vivier ppc part Acked-by: David Gibson Signed-off-by: Michael Tokarev --- accel/tcg/translate-all.c | 5 +- block/quorum.c | 6 +- hw/arm/exynos4210.c | 6 +- hw/block/vhost-user-blk.c | 5 +- hw/hppa/dino.c | 5 +- hw/misc/mos6522.c | 8 +-- hw/net/ftgmac100.c | 5 +- hw/ppc/pnv_lpc.c | 16 ++---- io/net-listener.c | 6 +- target/i386/hax-darwin.c | 10 +--- target/mips/dsp_helper.c | 15 +---- .../xtensa/core-dc232b/xtensa-modules.inc.c | 56 +++++-------------- .../xtensa/core-dc233c/xtensa-modules.inc.c | 56 +++++-------------- target/xtensa/core-de212/xtensa-modules.inc.c | 48 ++++------------ target/xtensa/core-fsf/xtensa-modules.inc.c | 32 +++-------- .../xtensa-modules.inc.c | 24 ++------ target/xtensa/translate.c | 7 +-- tests/m48t59-test.c | 6 +- tests/test-thread-pool.c | 6 +- util/uri.c | 5 +- 20 files changed, 79 insertions(+), 248 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index f409d42d54..732c919629 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -644,11 +644,8 @@ static inline void *alloc_code_gen_buffer(void) static inline void *alloc_code_gen_buffer(void) { size_t size = tcg_ctx->code_gen_buffer_size; - void *buf; - - buf = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, + return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - return buf; } #else static inline void *alloc_code_gen_buffer(void) diff --git a/block/quorum.c b/block/quorum.c index e448d7e384..b6476c405a 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -613,7 +613,7 @@ static void read_quorum_children_entry(void *opaque) static int read_quorum_children(QuorumAIOCB *acb) { BDRVQuorumState *s = acb->bs->opaque; - int i, ret; + int i; acb->children_read = s->num_children; for (i = 0; i < s->num_children; i++) { @@ -648,9 +648,7 @@ static int read_quorum_children(QuorumAIOCB *acb) qemu_coroutine_yield(); } - ret = acb->vote_ret; - - return ret; + return acb->vote_ret; } static int read_fifo_child(QuorumAIOCB *acb) diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 06f9d1ffa4..b7463a71ec 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -156,12 +156,8 @@ void exynos4210_write_secondary(ARMCPU *cpu, static uint64_t exynos4210_calc_affinity(int cpu) { - uint64_t mp_affinity; - /* Exynos4210 has 0x9 as cluster ID */ - mp_affinity = (0x9 << ARM_AFF1_SHIFT) | cpu; - - return mp_affinity; + return (0x9 << ARM_AFF1_SHIFT) | cpu; } Exynos4210State *exynos4210_init(MemoryRegion *system_mem) diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 262baca432..975eae6211 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -196,7 +196,6 @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, Error **errp) { VHostUserBlk *s = VHOST_USER_BLK(vdev); - uint64_t get_features; /* Turn on pre-defined features */ virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX); @@ -215,9 +214,7 @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, virtio_add_feature(&features, VIRTIO_BLK_F_MQ); } - get_features = vhost_get_features(&s->dev, user_feature_bits, features); - - return get_features; + return vhost_get_features(&s->dev, user_feature_bits, features); } static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c index 15aefde09c..c5dcf3104d 100644 --- a/hw/hppa/dino.c +++ b/hw/hppa/dino.c @@ -403,13 +403,10 @@ static void dino_set_irq(void *opaque, int irq, int level) static int dino_pci_map_irq(PCIDevice *d, int irq_num) { int slot = d->devfn >> 3; - int local_irq; assert(irq_num >= 0 && irq_num <= 3); - local_irq = slot & 0x03; - - return local_irq; + return slot & 0x03; } static void dino_set_timer_irq(void *opaque, int irq, int level) diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 8ad9fc831e..6163cea6ab 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -176,12 +176,8 @@ static void mos6522_set_sr_int(MOS6522State *s) static uint64_t mos6522_get_counter_value(MOS6522State *s, MOS6522Timer *ti) { - uint64_t d; - - d = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ti->load_time, - ti->frequency, NANOSECONDS_PER_SECOND); - - return d; + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ti->load_time, + ti->frequency, NANOSECONDS_PER_SECOND); } static uint64_t mos6522_get_load_time(MOS6522State *s, MOS6522Timer *ti) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 704f452067..3300e8ef4a 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -511,7 +511,6 @@ static uint32_t ftgmac100_rxpoll(FTGMAC100State *s) uint32_t cnt = 1024 * FTGMAC100_APTC_RXPOLL_CNT(s->aptcr); uint32_t speed = (s->maccr & FTGMAC100_MACCR_FAST_MODE) ? 1 : 0; - uint32_t period; if (s->aptcr & FTGMAC100_APTC_RXPOLL_TIME_SEL) { cnt <<= 4; @@ -521,9 +520,7 @@ static uint32_t ftgmac100_rxpoll(FTGMAC100State *s) speed = 2; } - period = cnt / div[speed]; - - return period; + return cnt / div[speed]; } static void ftgmac100_reset(DeviceState *d) diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index c42b4a8f6c..2317d1e62c 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -125,25 +125,17 @@ static int pnv_lpc_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, int sz) { - bool success; - /* XXX Handle access size limits and FW read caching here */ - success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, - data, sz, false); - - return success; + return !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, false); } static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, int sz) { - bool success; - /* XXX Handle access size limits here */ - success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, - data, sz, true); - - return success; + return !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, true); } #define ECCB_CTL_READ PPC_BIT(15) diff --git a/io/net-listener.c b/io/net-listener.c index 555e8acaa4..3317aa6e5f 100644 --- a/io/net-listener.c +++ b/io/net-listener.c @@ -25,11 +25,7 @@ QIONetListener *qio_net_listener_new(void) { - QIONetListener *ret; - - ret = QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER)); - - return ret; + return QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER)); } void qio_net_listener_set_name(QIONetListener *listener, diff --git a/target/i386/hax-darwin.c b/target/i386/hax-darwin.c index acdde476a0..a5426a6dac 100644 --- a/target/i386/hax-darwin.c +++ b/target/i386/hax-darwin.c @@ -257,10 +257,7 @@ int hax_host_setup_vcpu_channel(struct hax_vcpu_state *vcpu) int hax_vcpu_run(struct hax_vcpu_state *vcpu) { - int ret; - - ret = ioctl(vcpu->fd, HAX_VCPU_IOCTL_RUN, NULL); - return ret; + return ioctl(vcpu->fd, HAX_VCPU_IOCTL_RUN, NULL); } int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set) @@ -315,13 +312,12 @@ int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state, int set) int hax_inject_interrupt(CPUArchState *env, int vector) { - int ret, fd; + int fd; fd = hax_vcpu_get_fd(env); if (fd <= 0) { return -1; } - ret = ioctl(fd, HAX_VCPU_IOCTL_INTERRUPT, &vector); - return ret; + return ioctl(fd, HAX_VCPU_IOCTL_INTERRUPT, &vector); } diff --git a/target/mips/dsp_helper.c b/target/mips/dsp_helper.c index f152fea34a..739b69dd45 100644 --- a/target/mips/dsp_helper.c +++ b/target/mips/dsp_helper.c @@ -3274,14 +3274,11 @@ target_ulong helper_dextr_l(target_ulong ac, target_ulong shift, CPUMIPSState *env) { uint64_t temp[3]; - target_ulong result; shift = shift & 0x3F; mipsdsp_rndrashift_acc(temp, ac, shift, env); - result = (temp[1] << 63) | (temp[0] >> 1); - - return result; + return (temp[1] << 63) | (temp[0] >> 1); } target_ulong helper_dextr_r_l(target_ulong ac, target_ulong shift, @@ -3289,7 +3286,6 @@ target_ulong helper_dextr_r_l(target_ulong ac, target_ulong shift, { uint64_t temp[3]; uint32_t temp128; - target_ulong result; shift = shift & 0x3F; mipsdsp_rndrashift_acc(temp, ac, shift, env); @@ -3309,9 +3305,7 @@ target_ulong helper_dextr_r_l(target_ulong ac, target_ulong shift, set_DSPControl_overflow_flag(1, 23, env); } - result = (temp[1] << 63) | (temp[0] >> 1); - - return result; + return (temp[1] << 63) | (temp[0] >> 1); } target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, @@ -3319,7 +3313,6 @@ target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, { uint64_t temp[3]; uint32_t temp128; - target_ulong result; shift = shift & 0x3F; mipsdsp_rndrashift_acc(temp, ac, shift, env); @@ -3345,9 +3338,7 @@ target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, } set_DSPControl_overflow_flag(1, 23, env); } - result = (temp[1] << 63) | (temp[0] >> 1); - - return result; + return (temp[1] << 63) | (temp[0] >> 1); } #endif diff --git a/target/xtensa/core-dc232b/xtensa-modules.inc.c b/target/xtensa/core-dc232b/xtensa-modules.inc.c index d322c3f52a..164df3b1a4 100644 --- a/target/xtensa/core-dc232b/xtensa-modules.inc.c +++ b/target/xtensa/core-dc232b/xtensa-modules.inc.c @@ -1736,9 +1736,7 @@ Operand_arr_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_arr_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1750,9 +1748,7 @@ Operand_ars_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1764,9 +1760,7 @@ Operand_art_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_art_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1778,9 +1772,7 @@ Operand_ar0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar0_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1792,9 +1784,7 @@ Operand_ar4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar4_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1806,9 +1796,7 @@ Operand_ar8_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar8_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1820,9 +1808,7 @@ Operand_ar12_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar12_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1834,9 +1820,7 @@ Operand_ars_entry_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_entry_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -2406,9 +2390,7 @@ Operand_mx_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mx_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2436,9 +2418,7 @@ Operand_mw_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mw_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2450,9 +2430,7 @@ Operand_mr0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr0_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2464,9 +2442,7 @@ Operand_mr1_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr1_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2478,9 +2454,7 @@ Operand_mr2_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr2_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2492,9 +2466,7 @@ Operand_mr3_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr3_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int diff --git a/target/xtensa/core-dc233c/xtensa-modules.inc.c b/target/xtensa/core-dc233c/xtensa-modules.inc.c index 7c20f82349..0f32f0804a 100644 --- a/target/xtensa/core-dc233c/xtensa-modules.inc.c +++ b/target/xtensa/core-dc233c/xtensa-modules.inc.c @@ -1817,9 +1817,7 @@ Operand_arr_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_arr_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1831,9 +1829,7 @@ Operand_ars_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1845,9 +1841,7 @@ Operand_art_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_art_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1859,9 +1853,7 @@ Operand_ar0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar0_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1873,9 +1865,7 @@ Operand_ar4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar4_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1887,9 +1877,7 @@ Operand_ar8_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar8_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1901,9 +1889,7 @@ Operand_ar12_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar12_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1915,9 +1901,7 @@ Operand_ars_entry_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_entry_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -2487,9 +2471,7 @@ Operand_mx_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mx_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2517,9 +2499,7 @@ Operand_mw_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mw_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2531,9 +2511,7 @@ Operand_mr0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr0_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2545,9 +2523,7 @@ Operand_mr1_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr1_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2559,9 +2535,7 @@ Operand_mr2_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr2_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2573,9 +2547,7 @@ Operand_mr3_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr3_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int diff --git a/target/xtensa/core-de212/xtensa-modules.inc.c b/target/xtensa/core-de212/xtensa-modules.inc.c index ef7674de3a..480c68d3c6 100644 --- a/target/xtensa/core-de212/xtensa-modules.inc.c +++ b/target/xtensa/core-de212/xtensa-modules.inc.c @@ -1798,9 +1798,7 @@ OperandSem_opnd_sem_AR_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1812,9 +1810,7 @@ OperandSem_opnd_sem_AR_0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_0_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1826,9 +1822,7 @@ OperandSem_opnd_sem_AR_1_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_1_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1840,9 +1834,7 @@ OperandSem_opnd_sem_AR_2_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_2_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1854,9 +1846,7 @@ OperandSem_opnd_sem_AR_3_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_3_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1868,9 +1858,7 @@ OperandSem_opnd_sem_AR_4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_4_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -2464,9 +2452,7 @@ OperandSem_opnd_sem_MR_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int @@ -2478,9 +2464,7 @@ OperandSem_opnd_sem_MR_1_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_1_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int @@ -2492,9 +2476,7 @@ OperandSem_opnd_sem_MR_2_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_2_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int @@ -2506,9 +2488,7 @@ OperandSem_opnd_sem_MR_3_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_3_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int @@ -2520,9 +2500,7 @@ OperandSem_opnd_sem_MR_4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_4_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int @@ -2534,9 +2512,7 @@ OperandSem_opnd_sem_MR_5_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_5_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int diff --git a/target/xtensa/core-fsf/xtensa-modules.inc.c b/target/xtensa/core-fsf/xtensa-modules.inc.c index f7de2dec15..c32683ff77 100644 --- a/target/xtensa/core-fsf/xtensa-modules.inc.c +++ b/target/xtensa/core-fsf/xtensa-modules.inc.c @@ -1379,9 +1379,7 @@ Operand_arr_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_arr_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1393,9 +1391,7 @@ Operand_ars_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1407,9 +1403,7 @@ Operand_art_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_art_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1421,9 +1415,7 @@ Operand_ar0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar0_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3f) != 0; - return error; + return (*valp & ~0x3f) != 0; } static int @@ -1435,9 +1427,7 @@ Operand_ar4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar4_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3f) != 0; - return error; + return (*valp & ~0x3f) != 0; } static int @@ -1449,9 +1439,7 @@ Operand_ar8_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar8_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3f) != 0; - return error; + return (*valp & ~0x3f) != 0; } static int @@ -1463,9 +1451,7 @@ Operand_ar12_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar12_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3f) != 0; - return error; + return (*valp & ~0x3f) != 0; } static int @@ -1477,9 +1463,7 @@ Operand_ars_entry_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_entry_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3f) != 0; - return error; + return (*valp & ~0x3f) != 0; } static int diff --git a/target/xtensa/core-sample_controller/xtensa-modules.inc.c b/target/xtensa/core-sample_controller/xtensa-modules.inc.c index fba41b99ae..7e87d216bd 100644 --- a/target/xtensa/core-sample_controller/xtensa-modules.inc.c +++ b/target/xtensa/core-sample_controller/xtensa-modules.inc.c @@ -1570,9 +1570,7 @@ OperandSem_opnd_sem_AR_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1584,9 +1582,7 @@ OperandSem_opnd_sem_AR_0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_0_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1598,9 +1594,7 @@ OperandSem_opnd_sem_AR_1_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_1_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1612,9 +1606,7 @@ OperandSem_opnd_sem_AR_2_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_2_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1626,9 +1618,7 @@ OperandSem_opnd_sem_AR_3_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_3_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1640,9 +1630,7 @@ OperandSem_opnd_sem_AR_4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_4_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index ae0feb0254..df0e7ba63d 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -1271,11 +1271,8 @@ XtensaOpcodeOps * xtensa_find_opcode_ops(const XtensaOpcodeTranslators *t, const char *name) { - XtensaOpcodeOps *ops; - - ops = bsearch(name, t->opcode, t->num_opcodes, - sizeof(XtensaOpcodeOps), compare_opcode_ops); - return ops; + return bsearch(name, t->opcode, t->num_opcodes, + sizeof(XtensaOpcodeOps), compare_opcode_ops); } static void translate_abs(DisasContext *dc, const uint32_t arg[], diff --git a/tests/m48t59-test.c b/tests/m48t59-test.c index 26af7d6e8e..5b695971c7 100644 --- a/tests/m48t59-test.c +++ b/tests/m48t59-test.c @@ -256,8 +256,6 @@ static void base_setup(void) int main(int argc, char **argv) { - int ret; - base_setup(); g_test_init(&argc, &argv, NULL); @@ -267,7 +265,5 @@ int main(int argc, char **argv) qtest_add_func("/rtc/bcd-check-time", bcd_check_time); } qtest_add_func("/rtc/fuzz-registers", fuzz_registers); - ret = g_test_run(); - - return ret; + return g_test_run(); } diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c index 91b4ec5524..9cdccb3a47 100644 --- a/tests/test-thread-pool.c +++ b/tests/test-thread-pool.c @@ -224,8 +224,6 @@ static void test_cancel_async(void) int main(int argc, char **argv) { - int ret; - qemu_init_main_loop(&error_abort); ctx = qemu_get_current_aio_context(); pool = aio_get_thread_pool(ctx); @@ -238,7 +236,5 @@ int main(int argc, char **argv) g_test_add_func("/thread-pool/cancel", test_cancel); g_test_add_func("/thread-pool/cancel-async", test_cancel_async); - ret = g_test_run(); - - return ret; + return g_test_run(); } diff --git a/util/uri.c b/util/uri.c index 93ecefdaaf..8624a7ac23 100644 --- a/util/uri.c +++ b/util/uri.c @@ -1065,10 +1065,7 @@ URI *uri_parse_raw(const char *str, int raw) */ URI *uri_new(void) { - URI *ret; - - ret = g_new0(URI, 1); - return ret; + return g_new0(URI, 1); } /** From 54be4c42b2796ca6054cd1539d3ad4486447c789 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 2 Feb 2018 09:15:31 +0100 Subject: [PATCH 0640/2380] hw/timer/mt48t59: Fix bit-rotten NVRAM_PRINTF format strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling with NVRAM_PRINTF enabled, gcc currently bails out with: CC hw/timer/m48t59.o CC hw/timer/m48t59-isa.o hw/timer/m48t59.c: In function ‘NVRAM_writeb’: hw/timer/m48t59.c:460:5: error: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘hwaddr’ [-Werror=format=] NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); ^ hw/timer/m48t59.c:460:5: error: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘uint64_t’ [-Werror=format=] hw/timer/m48t59.c: In function ‘NVRAM_readb’: hw/timer/m48t59.c:492:5: error: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘hwaddr’ [-Werror=format=] NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval); Fix it by using the correct format strings and while we're at it, also change the definition of NVRAM_PRINTF so that this can not bit-rot so easily again. Signed-off-by: Thomas Huth Reviewed-by: Mark Cave-Ayland Signed-off-by: Michael Tokarev --- hw/timer/m48t59-internal.h | 9 +++------ hw/timer/m48t59.c | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/hw/timer/m48t59-internal.h b/hw/timer/m48t59-internal.h index 32ae957805..d0f0caf3c7 100644 --- a/hw/timer/m48t59-internal.h +++ b/hw/timer/m48t59-internal.h @@ -25,13 +25,10 @@ #ifndef HW_M48T59_INTERNAL_H #define HW_M48T59_INTERNAL_H 1 -//#define DEBUG_NVRAM +#define M48T59_DEBUG 0 -#if defined(DEBUG_NVRAM) -#define NVRAM_PRINTF(fmt, ...) do { printf(fmt , ## __VA_ARGS__); } while (0) -#else -#define NVRAM_PRINTF(fmt, ...) do { } while (0) -#endif +#define NVRAM_PRINTF(fmt, ...) do { \ + if (M48T59_DEBUG) { printf(fmt , ## __VA_ARGS__); } } while (0) /* * The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c index 742c576443..f2991762ab 100644 --- a/hw/timer/m48t59.c +++ b/hw/timer/m48t59.c @@ -456,7 +456,7 @@ static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val, { M48t59State *NVRAM = opaque; - NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); + NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" => 0x%"PRIx64"\n", __func__, addr, val); switch (addr) { case 0: NVRAM->addr &= ~0x00FF; @@ -488,7 +488,7 @@ static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size) retval = -1; break; } - NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval); + NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" <= 0x%08x\n", __func__, addr, retval); return retval; } From aec0d0e11812c602866c9fa044847f1e7b1e7dff Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Mon, 14 May 2018 15:45:45 +0200 Subject: [PATCH 0641/2380] qemu-options: Allow -no-user-config again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After 1217d6ca2bf28c0febe1bd7d5b3fa912bbf6af2a we error out explicitly if an unknown -option was passed on the command line. However, we are doing two pass command line option parsing. In the first pass we just look for -no-user-config or -nodefconfig being present which determines whether we load user config or not. Then in the second pass we finally parse everything else throwing an error if an unsupported -option was found. Problem is that in the second pass -no-user-config and -nodefconfig are not handled explicitly which makes us throw the unsupported option error. Signed-off-by: Michal Privoznik Reviewed-by: Thomas Huth Reviewed-by: Marc-André Lureau Signed-off-by: Michael Tokarev --- vl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vl.c b/vl.c index 3b39bbd7a8..d5836c65ae 100644 --- a/vl.c +++ b/vl.c @@ -4011,6 +4011,10 @@ int main(int argc, char **argv, char **envp) exit(1); } break; + case QEMU_OPTION_nodefconfig: + case QEMU_OPTION_nouserconfig: + /* Nothing to be parsed here. Especially, do not error out below. */ + break; default: if (os_parse_cmd_args(popt->index, optarg)) { error_report("Option not supported in this build"); From 201376cb9e500f64209444cb95f566679c0f54a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 May 2018 12:10:32 -0300 Subject: [PATCH 0642/2380] typedefs: Remove PcGuestInfo from qemu/typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is long gone since e4e8ba04c2007 ... Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Signed-off-by: Michael Tokarev --- include/qemu/typedefs.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index a46b0b347b..325c72de33 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -62,7 +62,6 @@ typedef struct NetClientState NetClientState; typedef struct NetFilterState NetFilterState; typedef struct NICInfo NICInfo; typedef struct NumaNodeMem NumaNodeMem; -typedef struct PcGuestInfo PcGuestInfo; typedef struct PCIBridge PCIBridge; typedef struct PCIBus PCIBus; typedef struct PCIDevice PCIDevice; From d29eb678bcfbb2fbf4b79423797253ee02f5c6cf Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Tue, 15 May 2018 08:31:28 +0200 Subject: [PATCH 0643/2380] replace functions which are only available in glib-2.24 Currently the minimal supported version of glib is 2.22. Since testing is done with a glib that claims to be 2.22, but in fact has APIs from newer version of glib, this bug was not caught during submit of the patch referenced below. Replace g_realloc_n, which is available only since 2.24, with g_renew. Fixes commit 418026ca43 ("util: Introduce vfio helpers") Signed-off-by: Olaf Hering Reviewed-by: Thomas Huth Reviewed-by: Cornelia Huck Signed-off-by: Michael Tokarev CC: qemu-stable@nongnu.org --- util/vfio-helpers.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/util/vfio-helpers.c b/util/vfio-helpers.c index 006674c916..1d9272efa4 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -522,8 +522,7 @@ static IOVAMapping *qemu_vfio_add_mapping(QEMUVFIOState *s, assert(index >= 0); s->nr_mappings++; - s->mappings = g_realloc_n(s->mappings, sizeof(s->mappings[0]), - s->nr_mappings); + s->mappings = g_renew(IOVAMapping, s->mappings, s->nr_mappings); insert = &s->mappings[index]; shift = s->nr_mappings - index - 1; if (shift) { @@ -577,8 +576,7 @@ static void qemu_vfio_undo_mapping(QEMUVFIOState *s, IOVAMapping *mapping, memmove(mapping, &s->mappings[index + 1], sizeof(s->mappings[0]) * (s->nr_mappings - index - 1)); s->nr_mappings--; - s->mappings = g_realloc_n(s->mappings, sizeof(s->mappings[0]), - s->nr_mappings); + s->mappings = g_renew(IOVAMapping, s->mappings, s->nr_mappings); } /* Check if the mapping list is (ascending) ordered. */ From f5bdd781316d2ba140323cb98392e44cac54017d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 May 2018 18:30:43 +0100 Subject: [PATCH 0644/2380] gdbstub: Use qemu_set_cloexec() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the utility routine qemu_set_cloexec() rather than manually calling fcntl(). This lets us drop the #ifndef _WIN32 guards and also means Coverity doesn't complain that we're ignoring the fcntl error return (CID 1005665, CID 1005667). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Signed-off-by: Michael Tokarev --- gdbstub.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 9682e16ef7..b99980d2e2 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1828,9 +1828,7 @@ static void gdb_accept(void) perror("accept"); return; } else if (fd >= 0) { -#ifndef _WIN32 - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif + qemu_set_cloexec(fd); break; } } @@ -1857,9 +1855,7 @@ static int gdbserver_open(int port) perror("socket"); return -1; } -#ifndef _WIN32 - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif + qemu_set_cloexec(fd); socket_set_fast_reuse(fd); From 2f652224f76c115f6c991766b7acac1e22580954 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 May 2018 18:30:44 +0100 Subject: [PATCH 0645/2380] gdbstub: Handle errors in gdb_accept() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In gdb_accept(), we both fail to check all errors (notably that from socket_set_nodelay(), as Coverity notes in CID 1005666), and fail to return an error status back to our caller. Correct both of these things, so that errors in accept() result in our stopping with a useful error message rather than ignoring it. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Signed-off-by: Michael Tokarev --- gdbstub.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index b99980d2e2..e4ece2f5bc 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1814,7 +1814,7 @@ void gdb_signalled(CPUArchState *env, int sig) put_packet(s, buf); } -static void gdb_accept(void) +static bool gdb_accept(void) { GDBState *s; struct sockaddr_in sockaddr; @@ -1826,7 +1826,7 @@ static void gdb_accept(void) fd = accept(gdbserver_fd, (struct sockaddr *)&sockaddr, &len); if (fd < 0 && errno != EINTR) { perror("accept"); - return; + return false; } else if (fd >= 0) { qemu_set_cloexec(fd); break; @@ -1834,7 +1834,10 @@ static void gdb_accept(void) } /* set short latency */ - socket_set_nodelay(fd); + if (socket_set_nodelay(fd)) { + perror("setsockopt"); + return false; + } s = g_malloc0(sizeof(GDBState)); s->c_cpu = first_cpu; @@ -1843,6 +1846,7 @@ static void gdb_accept(void) gdb_has_xml = false; gdbserver_state = s; + return true; } static int gdbserver_open(int port) @@ -1883,7 +1887,11 @@ int gdbserver_start(int port) if (gdbserver_fd < 0) return -1; /* accept connections */ - gdb_accept(); + if (!gdb_accept()) { + close(gdbserver_fd); + gdbserver_fd = -1; + return -1; + } return 0; } From 4088b5536436090207dcf6d15e47908f74b2d8f2 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 15 May 2018 18:26:20 +0200 Subject: [PATCH 0646/2380] qapi/net.json: Fix the version number of the "vlan" removal "vlan" will be dropped in 2.13, not in 2.12. And while we're at it, use the better wording "dropped in" instead of "removed with" (also for the "dump" removal). Reported-by: Stefan Hajnoczi Reported-by: Eric Blake Signed-off-by: Thomas Huth Reviewed-by: Eric Blake Signed-off-by: Michael Tokarev --- qapi/net.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qapi/net.json b/qapi/net.json index b4fe4b660b..b8adf1f03f 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -450,7 +450,7 @@ # # Since: 2.7 # -# 'dump' - removed with 2.12 +# 'dump': dropped in 2.12 ## { 'enum': 'NetClientDriver', 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde', @@ -498,7 +498,7 @@ # # Since: 1.2 # -# 'vlan' - removed with 2.12 +# 'vlan': dropped in 2.13 ## { 'struct': 'NetLegacy', 'data': { From 9a232487aab8a7640ff8853d7d8d7c27106b44f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 13 Apr 2018 18:45:45 +0200 Subject: [PATCH 0647/2380] acpi: fix a comment about aml_call0() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Michael Tokarev --- hw/acpi/aml-build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 3fa557cea1..1e43cd736d 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -627,7 +627,7 @@ Aml *aml_notify(Aml *arg1, Aml *arg2) return var; } -/* helper to call method with 1 argument */ +/* helper to call method without argument */ Aml *aml_call0(const char *method) { Aml *var = aml_alloc(); From 81e9cbd0ca1131012b058df6804b1f626a6b730c Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 9 Jan 2018 18:01:13 +0100 Subject: [PATCH 0648/2380] lm32: take BQL before writing IP/IM register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writing to these registers may raise an interrupt request. Actually, this prevents the milkymist board from starting. Cc: qemu-stable@nongnu.org Signed-off-by: Michael Walle Tested-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée --- target/lm32/op_helper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/lm32/op_helper.c b/target/lm32/op_helper.c index 577f8306e3..234d55e056 100644 --- a/target/lm32/op_helper.c +++ b/target/lm32/op_helper.c @@ -102,12 +102,16 @@ void HELPER(wcsr_dc)(CPULM32State *env, uint32_t dc) void HELPER(wcsr_im)(CPULM32State *env, uint32_t im) { + qemu_mutex_lock_iothread(); lm32_pic_set_im(env->pic_state, im); + qemu_mutex_unlock_iothread(); } void HELPER(wcsr_ip)(CPULM32State *env, uint32_t im) { + qemu_mutex_lock_iothread(); lm32_pic_set_ip(env->pic_state, im); + qemu_mutex_unlock_iothread(); } void HELPER(wcsr_jtx)(CPULM32State *env, uint32_t jtx) From d19d1f965904a533998739698020ff4ee8a103da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 21 May 2018 22:54:22 +0100 Subject: [PATCH 0649/2380] i386: define the 'ssbd' CPUID feature bit (CVE-2018-3639) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New microcode introduces the "Speculative Store Bypass Disable" CPUID feature bit. This needs to be exposed to guest OS to allow them to protect against CVE-2018-3639. Signed-off-by: Daniel P. Berrangé Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Konrad Rzeszutek Wilk Message-Id: <20180521215424.13520-2-berrange@redhat.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 2 +- target/i386/cpu.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index e5e66a75d4..a1185b17d1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -805,7 +805,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "spec-ctrl", NULL, - NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "ssbd", }, .cpuid_eax = 7, .cpuid_needs_ecx = true, .cpuid_ecx = 0, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8bc54d70bf..f0b68905de 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -685,6 +685,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2) /* AVX512 Neural Network Instructions */ #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */ #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */ +#define CPUID_7_0_EDX_SPEC_CTRL_SSBD (1U << 31) /* Speculative Store Bypass Disable */ #define KVM_HINTS_DEDICATED (1U << 0) From cfeea0c021db6234c154dbc723730e81553924ff Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Mon, 21 May 2018 22:54:24 +0100 Subject: [PATCH 0650/2380] i386: Define the Virt SSBD MSR and handling of it (CVE-2018-3639) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Some AMD processors only support a non-architectural means of enabling speculative store bypass disable (SSBD). To allow a simplified view of this to a guest, an architectural definition has been created through a new CPUID bit, 0x80000008_EBX[25], and a new MSR, 0xc001011f. With this, a hypervisor can virtualize the existence of this definition and provide an architectural method for using SSBD to a guest. Add the new CPUID feature, the new MSR and update the existing SSBD support to use this MSR when present." (from x86/speculation: Add virtualized speculative store bypass disable support in Linux). Signed-off-by: Konrad Rzeszutek Wilk Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé Message-Id: <20180521215424.13520-4-berrange@redhat.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.h | 2 ++ target/i386/kvm.c | 16 ++++++++++++++-- target/i386/machine.c | 20 ++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index f0b68905de..8ac13f6c2c 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -351,6 +351,7 @@ typedef enum X86Seg { #define MSR_IA32_FEATURE_CONTROL 0x0000003a #define MSR_TSC_ADJUST 0x0000003b #define MSR_IA32_SPEC_CTRL 0x48 +#define MSR_VIRT_SSBD 0xc001011f #define MSR_IA32_TSCDEADLINE 0x6e0 #define FEATURE_CONTROL_LOCKED (1<<0) @@ -1210,6 +1211,7 @@ typedef struct CPUX86State { uint32_t pkru; uint64_t spec_ctrl; + uint64_t virt_ssbd; /* End of state preserved by INIT (dummy marker). */ struct {} end_init_save; diff --git a/target/i386/kvm.c b/target/i386/kvm.c index d6666a4b19..0c656a91a4 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -93,6 +93,7 @@ static bool has_msr_hv_frequencies; static bool has_msr_hv_reenlightenment; static bool has_msr_xss; static bool has_msr_spec_ctrl; +static bool has_msr_virt_ssbd; static bool has_msr_smi_count; static uint32_t has_architectural_pmu_version; @@ -1233,6 +1234,9 @@ static int kvm_get_supported_msrs(KVMState *s) case MSR_IA32_SPEC_CTRL: has_msr_spec_ctrl = true; break; + case MSR_VIRT_SSBD: + has_msr_virt_ssbd = true; + break; } } } @@ -1721,6 +1725,10 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_spec_ctrl) { kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, env->spec_ctrl); } + if (has_msr_virt_ssbd) { + kvm_msr_entry_add(cpu, MSR_VIRT_SSBD, env->virt_ssbd); + } + #ifdef TARGET_X86_64 if (lm_capable_kernel) { kvm_msr_entry_add(cpu, MSR_CSTAR, env->cstar); @@ -2100,8 +2108,9 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_spec_ctrl) { kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, 0); } - - + if (has_msr_virt_ssbd) { + kvm_msr_entry_add(cpu, MSR_VIRT_SSBD, 0); + } if (!env->tsc_valid) { kvm_msr_entry_add(cpu, MSR_IA32_TSC, 0); env->tsc_valid = !runstate_is_running(); @@ -2481,6 +2490,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_IA32_SPEC_CTRL: env->spec_ctrl = msrs[i].data; break; + case MSR_VIRT_SSBD: + env->virt_ssbd = msrs[i].data; + break; case MSR_IA32_RTIT_CTL: env->msr_rtit_ctrl = msrs[i].data; break; diff --git a/target/i386/machine.c b/target/i386/machine.c index fd99c0bbb4..4d98d367c1 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -916,6 +916,25 @@ static const VMStateDescription vmstate_msr_intel_pt = { } }; +static bool virt_ssbd_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->virt_ssbd != 0; +} + +static const VMStateDescription vmstate_msr_virt_ssbd = { + .name = "cpu/virt_ssbd", + .version_id = 1, + .minimum_version_id = 1, + .needed = virt_ssbd_needed, + .fields = (VMStateField[]){ + VMSTATE_UINT64(env.virt_ssbd, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + VMStateDescription vmstate_x86_cpu = { .name = "cpu", .version_id = 12, @@ -1039,6 +1058,7 @@ VMStateDescription vmstate_x86_cpu = { &vmstate_spec_ctrl, &vmstate_mcg_ext_ctl, &vmstate_msr_intel_pt, + &vmstate_msr_virt_ssbd, NULL } }; From 403503b162ffc33fb64cfefdf7b880acf41772cd Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Mon, 21 May 2018 22:54:23 +0100 Subject: [PATCH 0651/2380] i386: define the AMD 'virt-ssbd' CPUID feature bit (CVE-2018-3639) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AMD Zen expose the Intel equivalant to Speculative Store Bypass Disable via the 0x80000008_EBX[25] CPUID feature bit. This needs to be exposed to guest OS to allow them to protect against CVE-2018-3639. Signed-off-by: Konrad Rzeszutek Wilk Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé Message-Id: <20180521215424.13520-3-berrange@redhat.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index a1185b17d1..d95310ffd4 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -836,7 +836,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "ibpb", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + NULL, "virt-ssbd", NULL, NULL, NULL, NULL, NULL, NULL, }, .cpuid_eax = 0x80000008, From 8d0f08e95b2d5f8ff6dea4027247bb0e8cc19cdf Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Tue, 22 May 2018 11:42:50 -0700 Subject: [PATCH 0652/2380] checkpatch: generalize xen handle matching in the list of types All the xen stable APIs define handle types of the form: xen_handle and some define additional handle types of the form: xen__handle Examples of these are xenforeignmemory_handle and xenforeignmemory_resource_handle. Both of these types will be misparsed by checkpatch if they appear as the first token in a line since, as types defined by an external library, they do not conform to the QEMU CODING_STYLE, which suggests CamelCase. A previous patch (5ac067a24a8) added xendevicemodel_handle to the list of types. This patch changes that to xen\w+_handle such that it will match all Xen stable API handles of the forms detailed above. Signed-off-by: Paul Durrant Reviewed-by: Eric Blake Acked-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- scripts/checkpatch.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index cb1b652388..e3d8c2cdfc 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -271,7 +271,7 @@ our @typeList = ( qr{hwaddr}, # external libraries qr{xml${Ident}}, - qr{xendevicemodel_handle}, + qr{xen\w+_handle}, # Glib definitions qr{gchar}, qr{gshort}, From 5c0d914a9ba9e0641554d127b62859ac0e954b9e Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Thu, 17 May 2018 16:35:50 +0100 Subject: [PATCH 0653/2380] xen: add a meaningful declaration of grant_copy_segment into xen_common.h Currently the xen_disk source has to carry #ifdef exclusions to compile against Xen older then 4.8. This is a bit messy so this patch lifts the definition of struct xengnttab_grant_copy_segment and adds it into the pre-4.8 compat area in xen_common.h, which allows xen_disk to be cleaned up. Signed-off-by: Paul Durrant Acked-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- hw/block/xen_disk.c | 18 ------------------ include/hw/xen/xen_common.h | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index f74fcd42d1..78bfb41e7f 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -496,8 +496,6 @@ static int ioreq_map(struct ioreq *ioreq) return 0; } -#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 - static void ioreq_free_copy_buffers(struct ioreq *ioreq) { int i; @@ -579,22 +577,6 @@ static int ioreq_grant_copy(struct ioreq *ioreq) return rc; } -#else -static void ioreq_free_copy_buffers(struct ioreq *ioreq) -{ - abort(); -} - -static int ioreq_init_copy_buffers(struct ioreq *ioreq) -{ - abort(); -} - -static int ioreq_grant_copy(struct ioreq *ioreq) -{ - abort(); -} -#endif static int ioreq_runio_qemu_aio(struct ioreq *ioreq); diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 5f1402b494..bbf207dcef 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -667,8 +667,21 @@ static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref, #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40800 +struct xengnttab_grant_copy_segment { + union xengnttab_copy_ptr { + void *virt; + struct { + uint32_t ref; + uint16_t offset; + uint16_t domid; + } foreign; + } source, dest; + uint16_t len; + uint16_t flags; + int16_t status; +}; -typedef void *xengnttab_grant_copy_segment_t; +typedef struct xengnttab_grant_copy_segment xengnttab_grant_copy_segment_t; static inline int xengnttab_grant_copy(xengnttab_handle *xgt, uint32_t count, xengnttab_grant_copy_segment_t *segs) From 9838824affced9e4db01a369dd28c14f694fa9b9 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Thu, 17 May 2018 16:35:51 +0100 Subject: [PATCH 0654/2380] xen_backend: add grant table helpers This patch adds grant table helper functions to the xen_backend code to localize error reporting and use of xen_domid. The patch also defers the call to xengnttab_open() until just before the initialise method in XenDevOps is invoked. This method is responsible for mapping the shared ring. No prior method requires access to the grant table. Signed-off-by: Paul Durrant Acked-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- hw/xen/xen_backend.c | 123 +++++++++++++++++++++++++++++++---- include/hw/xen/xen_backend.h | 33 ++++++++++ 2 files changed, 144 insertions(+), 12 deletions(-) diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index 7445b506ac..50412d6a09 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -106,6 +106,103 @@ int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) return 0; } +void xen_be_set_max_grant_refs(struct XenDevice *xendev, + unsigned int nr_refs) +{ + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (xengnttab_set_max_grants(xendev->gnttabdev, nr_refs)) { + xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", + strerror(errno)); + } +} + +void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs, + unsigned int nr_refs, int prot) +{ + void *ptr; + + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + ptr = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_refs, + xen_domid, refs, prot); + if (!ptr) { + xen_pv_printf(xendev, 0, + "xengnttab_map_domain_grant_refs failed: %s\n", + strerror(errno)); + } + + return ptr; +} + +void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, + unsigned int nr_refs) +{ + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (xengnttab_unmap(xendev->gnttabdev, ptr, nr_refs)) { + xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", + strerror(errno)); + } +} + +int xen_be_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, + XenGrantCopySegment segs[], + unsigned int nr_segs) +{ + xengnttab_grant_copy_segment_t *xengnttab_segs; + unsigned int i; + int rc; + + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; + + if (to_domain) { + xengnttab_seg->flags = GNTCOPY_dest_gref; + xengnttab_seg->dest.foreign.domid = xen_domid; + xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; + xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; + xengnttab_seg->source.virt = seg->source.virt; + } else { + xengnttab_seg->flags = GNTCOPY_source_gref; + xengnttab_seg->source.foreign.domid = xen_domid; + xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; + xengnttab_seg->source.foreign.offset = + seg->source.foreign.offset; + xengnttab_seg->dest.virt = seg->dest.virt; + } + + xengnttab_seg->len = seg->len; + } + + rc = xengnttab_grant_copy(xendev->gnttabdev, nr_segs, xengnttab_segs); + + if (rc) { + xen_pv_printf(xendev, 0, "xengnttab_copy failed: %s\n", + strerror(errno)); + } + + for (i = 0; i < nr_segs; i++) { + xengnttab_grant_copy_segment_t *xengnttab_seg = + &xengnttab_segs[i]; + + if (xengnttab_seg->status != GNTST_okay) { + xen_pv_printf(xendev, 0, "segment[%u] status: %d\n", i, + xengnttab_seg->status); + rc = -1; + } + } + + g_free(xengnttab_segs); + return rc; +} + /* * get xen backend device, allocate a new one if it doesn't exist. */ @@ -149,18 +246,6 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, } qemu_set_cloexec(xenevtchn_fd(xendev->evtchndev)); - if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { - xendev->gnttabdev = xengnttab_open(NULL, 0); - if (xendev->gnttabdev == NULL) { - xen_pv_printf(NULL, 0, "can't open gnttab device\n"); - xenevtchn_close(xendev->evtchndev); - qdev_unplug(DEVICE(xendev), NULL); - return NULL; - } - } else { - xendev->gnttabdev = NULL; - } - xen_pv_insert_xendev(xendev); if (xendev->ops->alloc) { @@ -322,6 +407,16 @@ static int xen_be_try_initialise(struct XenDevice *xendev) } } + if (xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xengnttab_open(NULL, 0); + if (xendev->gnttabdev == NULL) { + xen_pv_printf(NULL, 0, "can't open gnttab device\n"); + return -1; + } + } else { + xendev->gnttabdev = NULL; + } + if (xendev->ops->initialise) { rc = xendev->ops->initialise(xendev); } @@ -369,6 +464,10 @@ static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) xendev->ops->disconnect) { xendev->ops->disconnect(xendev); } + if (xendev->gnttabdev) { + xengnttab_close(xendev->gnttabdev); + xendev->gnttabdev = NULL; + } if (xendev->be_state != state) { xen_be_set_state(xendev, state); } diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen_backend.h index 3a27692407..29bf1c3bc3 100644 --- a/include/hw/xen/xen_backend.h +++ b/include/hw/xen/xen_backend.h @@ -42,6 +42,39 @@ void xen_be_register_common(void); int xen_be_register(const char *type, struct XenDevOps *ops); int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); int xen_be_bind_evtchn(struct XenDevice *xendev); +void xen_be_set_max_grant_refs(struct XenDevice *xendev, + unsigned int nr_refs); +void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs, + unsigned int nr_refs, int prot); +void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, + unsigned int nr_refs); + +typedef struct XenGrantCopySegment { + union { + void *virt; + struct { + uint32_t ref; + off_t offset; + } foreign; + } source, dest; + size_t len; +} XenGrantCopySegment; + +int xen_be_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, XenGrantCopySegment segs[], + unsigned int nr_segs); + +static inline void *xen_be_map_grant_ref(struct XenDevice *xendev, + uint32_t ref, int prot) +{ + return xen_be_map_grant_refs(xendev, &ref, 1, prot); +} + +static inline void xen_be_unmap_grant_ref(struct XenDevice *xendev, + void *ptr) +{ + return xen_be_unmap_grant_refs(xendev, ptr, 1); +} /* actual backend drivers */ extern struct XenDevOps xen_console_ops; /* xen_console.c */ From 5ee1d999138f17ad54657f1dba6408f6aefc3cc2 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Thu, 17 May 2018 16:35:52 +0100 Subject: [PATCH 0655/2380] xen_disk: remove open-coded use of libxengnttab Now that helpers are present in xen_backend, this patch removes open-coded calls to libxengnttab from the xen_disk code. This patch also fixes one whitspace error in the assignment of the XenDevOps initialise method. Signed-off-by: Paul Durrant Acked-by: Anthony Perard Signed-off-by: Stefano Stabellini --- hw/block/xen_disk.c | 122 ++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 90 deletions(-) diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 78bfb41e7f..d3be45a308 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -68,7 +68,6 @@ struct ioreq { uint8_t mapped; /* grant mapping */ - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; int prot; void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; @@ -142,7 +141,6 @@ static void ioreq_reset(struct ioreq *ioreq) ioreq->presync = 0; ioreq->mapped = 0; - memset(ioreq->domids, 0, sizeof(ioreq->domids)); memset(ioreq->refs, 0, sizeof(ioreq->refs)); ioreq->prot = 0; memset(ioreq->page, 0, sizeof(ioreq->page)); @@ -168,16 +166,12 @@ static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) static void destroy_grant(gpointer pgnt) { PersistentGrant *grant = pgnt; - xengnttab_handle *gnt = grant->blkdev->xendev.gnttabdev; + struct XenBlkDev *blkdev = grant->blkdev; + struct XenDevice *xendev = &blkdev->xendev; - if (xengnttab_unmap(gnt, grant->page, 1) != 0) { - xen_pv_printf(&grant->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } + xen_be_unmap_grant_ref(xendev, grant->page); grant->blkdev->persistent_gnt_count--; - xen_pv_printf(&grant->blkdev->xendev, 3, - "unmapped grant %p\n", grant->page); + xen_pv_printf(xendev, 3, "unmapped grant %p\n", grant->page); g_free(grant); } @@ -185,15 +179,10 @@ static void remove_persistent_region(gpointer data, gpointer dev) { PersistentRegion *region = data; struct XenBlkDev *blkdev = dev; - xengnttab_handle *gnt = blkdev->xendev.gnttabdev; + struct XenDevice *xendev = &blkdev->xendev; - if (xengnttab_unmap(gnt, region->addr, region->num) != 0) { - xen_pv_printf(&blkdev->xendev, 0, - "xengnttab_unmap region %p failed: %s\n", - region->addr, strerror(errno)); - } - xen_pv_printf(&blkdev->xendev, 3, - "unmapped grant region %p with %d pages\n", + xen_be_unmap_grant_refs(xendev, region->addr, region->num); + xen_pv_printf(xendev, 3, "unmapped grant region %p with %d pages\n", region->addr, region->num); g_free(region); } @@ -304,7 +293,6 @@ static int ioreq_parse(struct ioreq *ioreq) goto err; } - ioreq->domids[i] = blkdev->xendev.dom; ioreq->refs[i] = ioreq->req.seg[i].gref; mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; @@ -324,7 +312,8 @@ err: static void ioreq_unmap(struct ioreq *ioreq) { - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; + struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; int i; if (ioreq->num_unmap == 0 || ioreq->mapped == 0) { @@ -334,11 +323,7 @@ static void ioreq_unmap(struct ioreq *ioreq) if (!ioreq->pages) { return; } - if (xengnttab_unmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } + xen_be_unmap_grant_refs(xendev, ioreq->pages, ioreq->num_unmap); ioreq->blkdev->cnt_map -= ioreq->num_unmap; ioreq->pages = NULL; } else { @@ -346,11 +331,7 @@ static void ioreq_unmap(struct ioreq *ioreq) if (!ioreq->page[i]) { continue; } - if (xengnttab_unmap(gnt, ioreq->page[i], 1) != 0) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } + xen_be_unmap_grant_ref(xendev, ioreq->page[i]); ioreq->blkdev->cnt_map--; ioreq->page[i] = NULL; } @@ -360,14 +341,14 @@ static void ioreq_unmap(struct ioreq *ioreq) static int ioreq_map(struct ioreq *ioreq) { - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; int i, j, new_maps = 0; PersistentGrant *grant; PersistentRegion *region; - /* domids and refs variables will contain the information necessary + /* refs variable will contain the information necessary * to map the grants that are needed to fulfill this request. * * After mapping the needed grants, the page array will contain the @@ -392,7 +373,6 @@ static int ioreq_map(struct ioreq *ioreq) /* Add the grant to the list of grants that * should be mapped */ - domids[new_maps] = ioreq->domids[i]; refs[new_maps] = ioreq->refs[i]; page[i] = NULL; new_maps++; @@ -405,14 +385,13 @@ static int ioreq_map(struct ioreq *ioreq) } else { /* All grants in the request should be mapped */ memcpy(refs, ioreq->refs, sizeof(refs)); - memcpy(domids, ioreq->domids, sizeof(domids)); memset(page, 0, sizeof(page)); new_maps = ioreq->v.niov; } if (batch_maps && new_maps) { - ioreq->pages = xengnttab_map_grant_refs - (gnt, new_maps, domids, refs, ioreq->prot); + ioreq->pages = xen_be_map_grant_refs(xendev, refs, new_maps, + ioreq->prot); if (ioreq->pages == NULL) { xen_pv_printf(&ioreq->blkdev->xendev, 0, "can't map %d grant refs (%s, %d maps)\n", @@ -427,8 +406,8 @@ static int ioreq_map(struct ioreq *ioreq) ioreq->blkdev->cnt_map += new_maps; } else if (new_maps) { for (i = 0; i < new_maps; i++) { - ioreq->page[i] = xengnttab_map_grant_ref - (gnt, domids[i], refs[i], ioreq->prot); + ioreq->page[i] = xen_be_map_grant_ref(xendev, refs[i], + ioreq->prot); if (ioreq->page[i] == NULL) { xen_pv_printf(&ioreq->blkdev->xendev, 0, "can't map grant ref %d (%s, %d maps)\n", @@ -527,10 +506,12 @@ static int ioreq_init_copy_buffers(struct ioreq *ioreq) static int ioreq_grant_copy(struct ioreq *ioreq) { - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - xengnttab_grant_copy_segment_t segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; + XenGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; int i, count, rc; int64_t file_blk = ioreq->blkdev->file_blk; + bool to_domain = (ioreq->req.operation == BLKIF_OP_READ); if (ioreq->v.niov == 0) { return 0; @@ -539,16 +520,12 @@ static int ioreq_grant_copy(struct ioreq *ioreq) count = ioreq->v.niov; for (i = 0; i < count; i++) { - if (ioreq->req.operation == BLKIF_OP_READ) { - segs[i].flags = GNTCOPY_dest_gref; + if (to_domain) { segs[i].dest.foreign.ref = ioreq->refs[i]; - segs[i].dest.foreign.domid = ioreq->domids[i]; segs[i].dest.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; segs[i].source.virt = ioreq->v.iov[i].iov_base; } else { - segs[i].flags = GNTCOPY_source_gref; segs[i].source.foreign.ref = ioreq->refs[i]; - segs[i].source.foreign.domid = ioreq->domids[i]; segs[i].source.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; segs[i].dest.virt = ioreq->v.iov[i].iov_base; } @@ -556,7 +533,7 @@ static int ioreq_grant_copy(struct ioreq *ioreq) - ioreq->req.seg[i].first_sect + 1) * file_blk; } - rc = xengnttab_grant_copy(gnt, count, segs); + rc = xen_be_copy_grant_refs(xendev, to_domain, segs, count); if (rc) { xen_pv_printf(&ioreq->blkdev->xendev, 0, @@ -565,16 +542,6 @@ static int ioreq_grant_copy(struct ioreq *ioreq) return -1; } - for (i = 0; i < count; i++) { - if (segs[i].status != GNTST_okay) { - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "failed to copy data %d for gref %d, domid %d\n", - segs[i].status, ioreq->refs[i], ioreq->domids[i]); - ioreq->aio_errors++; - rc = -1; - } - } - return rc; } @@ -1067,7 +1034,6 @@ static int blk_connect(struct XenDevice *xendev) int order, ring_ref; unsigned int ring_size, max_grants; unsigned int i; - uint32_t *domids; trace_xen_disk_connect(xendev->name); @@ -1229,31 +1195,11 @@ static int blk_connect(struct XenDevice *xendev) /* Add on the number needed for the ring pages */ max_grants += blkdev->nr_ring_ref; - blkdev->xendev.gnttabdev = xengnttab_open(NULL, 0); - if (blkdev->xendev.gnttabdev == NULL) { - xen_pv_printf(xendev, 0, "xengnttab_open failed: %s\n", - strerror(errno)); - return -1; - } - if (xengnttab_set_max_grants(blkdev->xendev.gnttabdev, max_grants)) { - xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - return -1; - } - - domids = g_new0(uint32_t, blkdev->nr_ring_ref); - for (i = 0; i < blkdev->nr_ring_ref; i++) { - domids[i] = blkdev->xendev.dom; - } - - blkdev->sring = xengnttab_map_grant_refs(blkdev->xendev.gnttabdev, - blkdev->nr_ring_ref, - domids, - blkdev->ring_ref, - PROT_READ | PROT_WRITE); - - g_free(domids); + xen_be_set_max_grant_refs(xendev, max_grants); + blkdev->sring = xen_be_map_grant_refs(xendev, blkdev->ring_ref, + blkdev->nr_ring_ref, + PROT_READ | PROT_WRITE); if (!blkdev->sring) { return -1; } @@ -1326,8 +1272,8 @@ static void blk_disconnect(struct XenDevice *xendev) aio_context_release(blkdev->ctx); if (blkdev->sring) { - xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, - blkdev->nr_ring_ref); + xen_be_unmap_grant_refs(xendev, blkdev->sring, + blkdev->nr_ring_ref); blkdev->cnt_map--; blkdev->sring = NULL; } @@ -1351,11 +1297,6 @@ static void blk_disconnect(struct XenDevice *xendev) } blkdev->feature_persistent = false; } - - if (blkdev->xendev.gnttabdev) { - xengnttab_close(blkdev->xendev.gnttabdev); - blkdev->xendev.gnttabdev = NULL; - } } static int blk_free(struct XenDevice *xendev) @@ -1392,10 +1333,11 @@ static void blk_event(struct XenDevice *xendev) } struct XenDevOps xen_blkdev_ops = { + .flags = DEVOPS_FLAG_NEED_GNTDEV, .size = sizeof(struct XenBlkDev), .alloc = blk_alloc, .init = blk_init, - .initialise = blk_connect, + .initialise = blk_connect, .disconnect = blk_disconnect, .event = blk_event, .free = blk_free, From 58560f2ae71b99d3032432a4a8457bb2cb21bce1 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Thu, 17 May 2018 16:35:53 +0100 Subject: [PATCH 0656/2380] xen: remove other open-coded use of libxengnttab Now that helpers are available in xen_backend, use them throughout all Xen PV backends. Signed-off-by: Paul Durrant Acked-by: Anthony Perard Signed-off-by: Stefano Stabellini --- hw/9pfs/xen-9p-backend.c | 32 +++++++++++++++----------------- hw/char/xen_console.c | 9 ++++----- hw/net/xen_nic.c | 33 ++++++++++++++------------------- hw/usb/xen-usb.c | 37 +++++++++++++++++-------------------- 4 files changed, 50 insertions(+), 61 deletions(-) diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 95e50c4dfc..6026780f95 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -331,14 +331,14 @@ static int xen_9pfs_free(struct XenDevice *xendev) for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].data != NULL) { - xengnttab_unmap(xen_9pdev->xendev.gnttabdev, - xen_9pdev->rings[i].data, - (1 << xen_9pdev->rings[i].ring_order)); + xen_be_unmap_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].data, + (1 << xen_9pdev->rings[i].ring_order)); } if (xen_9pdev->rings[i].intf != NULL) { - xengnttab_unmap(xen_9pdev->xendev.gnttabdev, - xen_9pdev->rings[i].intf, - 1); + xen_be_unmap_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].intf, + 1); } if (xen_9pdev->rings[i].bh != NULL) { qemu_bh_delete(xen_9pdev->rings[i].bh); @@ -390,11 +390,10 @@ static int xen_9pfs_connect(struct XenDevice *xendev) } g_free(str); - xen_9pdev->rings[i].intf = xengnttab_map_grant_ref( - xen_9pdev->xendev.gnttabdev, - xen_9pdev->xendev.dom, - xen_9pdev->rings[i].ref, - PROT_READ | PROT_WRITE); + xen_9pdev->rings[i].intf = + xen_be_map_grant_ref(&xen_9pdev->xendev, + xen_9pdev->rings[i].ref, + PROT_READ | PROT_WRITE); if (!xen_9pdev->rings[i].intf) { goto out; } @@ -403,12 +402,11 @@ static int xen_9pfs_connect(struct XenDevice *xendev) goto out; } xen_9pdev->rings[i].ring_order = ring_order; - xen_9pdev->rings[i].data = xengnttab_map_domain_grant_refs( - xen_9pdev->xendev.gnttabdev, - (1 << ring_order), - xen_9pdev->xendev.dom, - xen_9pdev->rings[i].intf->ref, - PROT_READ | PROT_WRITE); + xen_9pdev->rings[i].data = + xen_be_map_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].intf->ref, + (1 << ring_order), + PROT_READ | PROT_WRITE); if (!xen_9pdev->rings[i].data) { goto out; } diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index bdfaa40ed3..8b4b4bf523 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -233,12 +233,11 @@ static int con_initialise(struct XenDevice *xendev) if (!xendev->dev) { xen_pfn_t mfn = con->ring_ref; con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom, - PROT_READ|PROT_WRITE, + PROT_READ | PROT_WRITE, 1, &mfn, NULL); } else { - con->sring = xengnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom, - con->ring_ref, - PROT_READ|PROT_WRITE); + con->sring = xen_be_map_grant_ref(xendev, con->ring_ref, + PROT_READ | PROT_WRITE); } if (!con->sring) return -1; @@ -267,7 +266,7 @@ static void con_disconnect(struct XenDevice *xendev) if (!xendev->dev) { xenforeignmemory_unmap(xen_fmem, con->sring, 1); } else { - xengnttab_unmap(xendev->gnttabdev, con->sring, 1); + xen_be_unmap_grant_ref(xendev, con->sring); } con->sring = NULL; } diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 20c43a61b3..46a8dbfc90 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -160,9 +160,8 @@ static void net_tx_packets(struct XenNetDev *netdev) (txreq.flags & NETTXF_more_data) ? " more_data" : "", (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - txreq.gref, PROT_READ); + page = xen_be_map_grant_ref(&netdev->xendev, txreq.gref, + PROT_READ); if (page == NULL) { xen_pv_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", @@ -183,7 +182,7 @@ static void net_tx_packets(struct XenNetDev *netdev) qemu_send_packet(qemu_get_queue(netdev->nic), page + txreq.offset, txreq.size); } - xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); + xen_be_unmap_grant_ref(&netdev->xendev, page); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); } if (!netdev->tx_work) { @@ -254,9 +253,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); netdev->rx_ring.req_cons = ++rc; - page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - rxreq.gref, PROT_WRITE); + page = xen_be_map_grant_ref(&netdev->xendev, rxreq.gref, PROT_WRITE); if (page == NULL) { xen_pv_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", @@ -265,7 +262,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size return -1; } memcpy(page + NET_IP_ALIGN, buf, size); - xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); + xen_be_unmap_grant_ref(&netdev->xendev, page); net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); return size; @@ -338,19 +335,17 @@ static int net_connect(struct XenDevice *xendev) return -1; } - netdev->txs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->tx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->txs = xen_be_map_grant_ref(&netdev->xendev, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); if (!netdev->txs) { return -1; } - netdev->rxs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->rx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->rxs = xen_be_map_grant_ref(&netdev->xendev, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); if (!netdev->rxs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs); netdev->txs = NULL; return -1; } @@ -375,11 +370,11 @@ static void net_disconnect(struct XenDevice *xendev) xen_pv_unbind_evtchn(&netdev->xendev); if (netdev->txs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs); netdev->txs = NULL; } if (netdev->rxs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->rxs); netdev->rxs = NULL; } } diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index b3a90c0e68..5b2e21ed18 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -173,8 +173,9 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < usbback_req->nr_buffer_segs; i++) { ref[i] = usbback_req->req.seg[i].gref; } - usbback_req->buffer = xengnttab_map_domain_grant_refs(xendev->gnttabdev, - usbback_req->nr_buffer_segs, xendev->dom, ref, prot); + usbback_req->buffer = + xen_be_map_grant_refs(xendev, ref, usbback_req->nr_buffer_segs, + prot); if (!usbback_req->buffer) { return -ENOMEM; @@ -206,8 +207,9 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < usbback_req->nr_extra_segs; i++) { ref[i] = usbback_req->req.seg[i + usbback_req->req.nr_buffer_segs].gref; } - usbback_req->isoc_buffer = xengnttab_map_domain_grant_refs( - xendev->gnttabdev, usbback_req->nr_extra_segs, xendev->dom, ref, prot); + usbback_req->isoc_buffer = + xen_be_map_grant_refs(xendev, ref, usbback_req->nr_extra_segs, + prot); if (!usbback_req->isoc_buffer) { return -ENOMEM; @@ -291,14 +293,14 @@ static void usbback_do_response(struct usbback_req *usbback_req, int32_t status, } if (usbback_req->buffer) { - xengnttab_unmap(xendev->gnttabdev, usbback_req->buffer, - usbback_req->nr_buffer_segs); + xen_be_unmap_grant_refs(xendev, usbback_req->buffer, + usbback_req->nr_buffer_segs); usbback_req->buffer = NULL; } if (usbback_req->isoc_buffer) { - xengnttab_unmap(xendev->gnttabdev, usbback_req->isoc_buffer, - usbback_req->nr_extra_segs); + xen_be_unmap_grant_refs(xendev, usbback_req->isoc_buffer, + usbback_req->nr_extra_segs); usbback_req->isoc_buffer = NULL; } @@ -834,11 +836,11 @@ static void usbback_disconnect(struct XenDevice *xendev) xen_pv_unbind_evtchn(xendev); if (usbif->urb_sring) { - xengnttab_unmap(xendev->gnttabdev, usbif->urb_sring, 1); + xen_be_unmap_grant_ref(xendev, usbif->urb_sring); usbif->urb_sring = NULL; } if (usbif->conn_sring) { - xengnttab_unmap(xendev->gnttabdev, usbif->conn_sring, 1); + xen_be_unmap_grant_ref(xendev, usbif->conn_sring); usbif->conn_sring = NULL; } @@ -877,12 +879,10 @@ static int usbback_connect(struct XenDevice *xendev) return -1; } - usbif->urb_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom, - urb_ring_ref, - PROT_READ | PROT_WRITE); - usbif->conn_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom, - conn_ring_ref, - PROT_READ | PROT_WRITE); + usbif->urb_sring = xen_be_map_grant_ref(xendev, urb_ring_ref, + PROT_READ | PROT_WRITE); + usbif->conn_sring = xen_be_map_grant_ref(xendev, conn_ring_ref, + PROT_READ | PROT_WRITE); if (!usbif->urb_sring || !usbif->conn_sring) { xen_pv_printf(xendev, 0, "error mapping rings\n"); usbback_disconnect(xendev); @@ -1024,10 +1024,7 @@ static void usbback_alloc(struct XenDevice *xendev) /* max_grants: for each request and for the rings (request and connect). */ max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2; - if (xengnttab_set_max_grants(xendev->gnttabdev, max_grants) < 0) { - xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - } + xen_be_set_max_grant_refs(xendev, max_grants); } static int usbback_free(struct XenDevice *xendev) From 3fe12b8403111021e6eaae3b44d01a34922c176a Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Thu, 17 May 2018 16:35:54 +0100 Subject: [PATCH 0657/2380] xen_backend: add an emulation of grant copy Not all Xen environments support the xengnttab_grant_copy() operation. E.g. where the OS is FreeBSD or Xen is older than 4.8.0. This patch introduces an emulation of that operation using xengnttab_map_domain_grant_refs() and memcpy() for those environments. Signed-off-by: Paul Durrant Acked-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- hw/xen/xen_backend.c | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index 50412d6a09..3c3fc2c6e6 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -146,6 +146,55 @@ void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, } } +static int compat_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, + XenGrantCopySegment segs[], + unsigned int nr_segs) +{ + uint32_t *refs = g_new(uint32_t, nr_segs); + int prot = to_domain ? PROT_WRITE : PROT_READ; + void *pages; + unsigned int i; + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + + refs[i] = to_domain ? + seg->dest.foreign.ref : seg->source.foreign.ref; + } + + pages = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_segs, + xen_domid, refs, prot); + if (!pages) { + xen_pv_printf(xendev, 0, + "xengnttab_map_domain_grant_refs failed: %s\n", + strerror(errno)); + g_free(refs); + return -1; + } + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + void *page = pages + (i * XC_PAGE_SIZE); + + if (to_domain) { + memcpy(page + seg->dest.foreign.offset, seg->source.virt, + seg->len); + } else { + memcpy(seg->dest.virt, page + seg->source.foreign.offset, + seg->len); + } + } + + if (xengnttab_unmap(xendev->gnttabdev, pages, nr_segs)) { + xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", + strerror(errno)); + } + + g_free(refs); + return 0; +} + int xen_be_copy_grant_refs(struct XenDevice *xendev, bool to_domain, XenGrantCopySegment segs[], @@ -157,6 +206,10 @@ int xen_be_copy_grant_refs(struct XenDevice *xendev, assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + if (!xen_feature_grant_copy) { + return compat_copy_grant_refs(xendev, to_domain, segs, nr_segs); + } + xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); for (i = 0; i < nr_segs; i++) { From 06454c24adf54114a56524444b4e3e915e1879bc Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Thu, 17 May 2018 16:35:55 +0100 Subject: [PATCH 0658/2380] xen_disk: remove use of grant map/unmap Now that the (native or emulated) xen_be_copy_grant_refs() helper is always available, the xen_disk code can be significantly simplified by removing direct use of grant map and unmap operations. Signed-off-by: Paul Durrant Acked-by: Anthony Perard Signed-off-by: Stefano Stabellini --- hw/block/xen_disk.c | 352 ++++---------------------------------------- 1 file changed, 25 insertions(+), 327 deletions(-) diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index d3be45a308..28be8b6d49 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -36,27 +36,9 @@ /* ------------------------------------------------------------- */ -static int batch_maps = 0; - -/* ------------------------------------------------------------- */ - #define BLOCK_SIZE 512 #define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) -struct PersistentGrant { - void *page; - struct XenBlkDev *blkdev; -}; - -typedef struct PersistentGrant PersistentGrant; - -struct PersistentRegion { - void *addr; - int num; -}; - -typedef struct PersistentRegion PersistentRegion; - struct ioreq { blkif_request_t req; int16_t status; @@ -65,14 +47,11 @@ struct ioreq { off_t start; QEMUIOVector v; int presync; - uint8_t mapped; /* grant mapping */ uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int prot; void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; void *pages; - int num_unmap; /* aio status */ int aio_inflight; @@ -103,7 +82,6 @@ struct XenBlkDev { int protocol; blkif_back_rings_t rings; int more_work; - int cnt_map; /* request lists */ QLIST_HEAD(inflight_head, ioreq) inflight; @@ -114,13 +92,7 @@ struct XenBlkDev { int requests_finished; unsigned int max_requests; - /* Persistent grants extension */ gboolean feature_discard; - gboolean feature_persistent; - GTree *persistent_gnts; - GSList *persistent_regions; - unsigned int persistent_gnt_count; - unsigned int max_grants; /* qemu block driver */ DriveInfo *dinfo; @@ -139,10 +111,8 @@ static void ioreq_reset(struct ioreq *ioreq) ioreq->status = 0; ioreq->start = 0; ioreq->presync = 0; - ioreq->mapped = 0; memset(ioreq->refs, 0, sizeof(ioreq->refs)); - ioreq->prot = 0; memset(ioreq->page, 0, sizeof(ioreq->page)); ioreq->pages = NULL; @@ -156,37 +126,6 @@ static void ioreq_reset(struct ioreq *ioreq) qemu_iovec_reset(&ioreq->v); } -static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) -{ - uint ua = GPOINTER_TO_UINT(a); - uint ub = GPOINTER_TO_UINT(b); - return (ua > ub) - (ua < ub); -} - -static void destroy_grant(gpointer pgnt) -{ - PersistentGrant *grant = pgnt; - struct XenBlkDev *blkdev = grant->blkdev; - struct XenDevice *xendev = &blkdev->xendev; - - xen_be_unmap_grant_ref(xendev, grant->page); - grant->blkdev->persistent_gnt_count--; - xen_pv_printf(xendev, 3, "unmapped grant %p\n", grant->page); - g_free(grant); -} - -static void remove_persistent_region(gpointer data, gpointer dev) -{ - PersistentRegion *region = data; - struct XenBlkDev *blkdev = dev; - struct XenDevice *xendev = &blkdev->xendev; - - xen_be_unmap_grant_refs(xendev, region->addr, region->num); - xen_pv_printf(xendev, 3, "unmapped grant region %p with %d pages\n", - region->addr, region->num); - g_free(region); -} - static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) { struct ioreq *ioreq = NULL; @@ -254,7 +193,6 @@ static int ioreq_parse(struct ioreq *ioreq) ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); switch (ioreq->req.operation) { case BLKIF_OP_READ: - ioreq->prot = PROT_WRITE; /* to memory */ break; case BLKIF_OP_FLUSH_DISKCACHE: ioreq->presync = 1; @@ -263,7 +201,6 @@ static int ioreq_parse(struct ioreq *ioreq) } /* fall through */ case BLKIF_OP_WRITE: - ioreq->prot = PROT_READ; /* from memory */ break; case BLKIF_OP_DISCARD: return 0; @@ -310,171 +247,6 @@ err: return -1; } -static void ioreq_unmap(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - struct XenDevice *xendev = &blkdev->xendev; - int i; - - if (ioreq->num_unmap == 0 || ioreq->mapped == 0) { - return; - } - if (batch_maps) { - if (!ioreq->pages) { - return; - } - xen_be_unmap_grant_refs(xendev, ioreq->pages, ioreq->num_unmap); - ioreq->blkdev->cnt_map -= ioreq->num_unmap; - ioreq->pages = NULL; - } else { - for (i = 0; i < ioreq->num_unmap; i++) { - if (!ioreq->page[i]) { - continue; - } - xen_be_unmap_grant_ref(xendev, ioreq->page[i]); - ioreq->blkdev->cnt_map--; - ioreq->page[i] = NULL; - } - } - ioreq->mapped = 0; -} - -static int ioreq_map(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - struct XenDevice *xendev = &blkdev->xendev; - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int i, j, new_maps = 0; - PersistentGrant *grant; - PersistentRegion *region; - /* refs variable will contain the information necessary - * to map the grants that are needed to fulfill this request. - * - * After mapping the needed grants, the page array will contain the - * memory address of each granted page in the order specified in ioreq - * (disregarding if it's a persistent grant or not). - */ - - if (ioreq->v.niov == 0 || ioreq->mapped == 1) { - return 0; - } - if (ioreq->blkdev->feature_persistent) { - for (i = 0; i < ioreq->v.niov; i++) { - grant = g_tree_lookup(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(ioreq->refs[i])); - - if (grant != NULL) { - page[i] = grant->page; - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "using persistent-grant %" PRIu32 "\n", - ioreq->refs[i]); - } else { - /* Add the grant to the list of grants that - * should be mapped - */ - refs[new_maps] = ioreq->refs[i]; - page[i] = NULL; - new_maps++; - } - } - /* Set the protection to RW, since grants may be reused later - * with a different protection than the one needed for this request - */ - ioreq->prot = PROT_WRITE | PROT_READ; - } else { - /* All grants in the request should be mapped */ - memcpy(refs, ioreq->refs, sizeof(refs)); - memset(page, 0, sizeof(page)); - new_maps = ioreq->v.niov; - } - - if (batch_maps && new_maps) { - ioreq->pages = xen_be_map_grant_refs(xendev, refs, new_maps, - ioreq->prot); - if (ioreq->pages == NULL) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "can't map %d grant refs (%s, %d maps)\n", - new_maps, strerror(errno), ioreq->blkdev->cnt_map); - return -1; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->pages + (j++) * XC_PAGE_SIZE; - } - } - ioreq->blkdev->cnt_map += new_maps; - } else if (new_maps) { - for (i = 0; i < new_maps; i++) { - ioreq->page[i] = xen_be_map_grant_ref(xendev, refs[i], - ioreq->prot); - if (ioreq->page[i] == NULL) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "can't map grant ref %d (%s, %d maps)\n", - refs[i], strerror(errno), ioreq->blkdev->cnt_map); - ioreq->mapped = 1; - ioreq_unmap(ioreq); - return -1; - } - ioreq->blkdev->cnt_map++; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->page[j++]; - } - } - } - if (ioreq->blkdev->feature_persistent && new_maps != 0 && - (!batch_maps || (ioreq->blkdev->persistent_gnt_count + new_maps <= - ioreq->blkdev->max_grants))) { - /* - * If we are using persistent grants and batch mappings only - * add the new maps to the list of persistent grants if the whole - * area can be persistently mapped. - */ - if (batch_maps) { - region = g_malloc0(sizeof(*region)); - region->addr = ioreq->pages; - region->num = new_maps; - ioreq->blkdev->persistent_regions = g_slist_append( - ioreq->blkdev->persistent_regions, - region); - } - while ((ioreq->blkdev->persistent_gnt_count < ioreq->blkdev->max_grants) - && new_maps) { - /* Go through the list of newly mapped grants and add as many - * as possible to the list of persistently mapped grants. - * - * Since we start at the end of ioreq->page(s), we only need - * to decrease new_maps to prevent this granted pages from - * being unmapped in ioreq_unmap. - */ - grant = g_malloc0(sizeof(*grant)); - new_maps--; - if (batch_maps) { - grant->page = ioreq->pages + (new_maps) * XC_PAGE_SIZE; - } else { - grant->page = ioreq->page[new_maps]; - } - grant->blkdev = ioreq->blkdev; - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "adding grant %" PRIu32 " page: %p\n", - refs[new_maps], grant->page); - g_tree_insert(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(refs[new_maps]), - grant); - ioreq->blkdev->persistent_gnt_count++; - } - assert(!batch_maps || new_maps == 0); - } - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->v.iov[i].iov_base += (uintptr_t)page[i]; - } - ioreq->mapped = 1; - ioreq->num_unmap = new_maps; - return 0; -} - static void ioreq_free_copy_buffers(struct ioreq *ioreq) { int i; @@ -570,32 +342,28 @@ static void qemu_aio_complete(void *opaque, int ret) goto done; } - if (xen_feature_grant_copy) { - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - /* in case of failure ioreq->aio_errors is increased */ - if (ret == 0) { - ioreq_grant_copy(ioreq); - } - ioreq_free_copy_buffers(ioreq); - break; - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - ioreq_free_copy_buffers(ioreq); - break; - default: + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + /* in case of failure ioreq->aio_errors is increased */ + if (ret == 0) { + ioreq_grant_copy(ioreq); + } + ioreq_free_copy_buffers(ioreq); + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_FLUSH_DISKCACHE: + if (!ioreq->req.nr_segments) { break; } + ioreq_free_copy_buffers(ioreq); + break; + default: + break; } ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; - if (!xen_feature_grant_copy) { - ioreq_unmap(ioreq); - } ioreq_finish(ioreq); + switch (ioreq->req.operation) { case BLKIF_OP_WRITE: case BLKIF_OP_FLUSH_DISKCACHE: @@ -655,18 +423,13 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - if (xen_feature_grant_copy) { - ioreq_init_copy_buffers(ioreq); - if (ioreq->req.nr_segments && (ioreq->req.operation == BLKIF_OP_WRITE || - ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && - ioreq_grant_copy(ioreq)) { - ioreq_free_copy_buffers(ioreq); - goto err; - } - } else { - if (ioreq->req.nr_segments && ioreq_map(ioreq)) { - goto err; - } + ioreq_init_copy_buffers(ioreq); + if (ioreq->req.nr_segments && + (ioreq->req.operation == BLKIF_OP_WRITE || + ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && + ioreq_grant_copy(ioreq)) { + ioreq_free_copy_buffers(ioreq); + goto err; } ioreq->aio_inflight++; @@ -707,9 +470,6 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) } default: /* unknown operation (shouldn't happen -- parse catches this) */ - if (!xen_feature_grant_copy) { - ioreq_unmap(ioreq); - } goto err; } @@ -895,10 +655,6 @@ static void blk_alloc(struct XenDevice *xendev) blkdev->ctx = iothread_get_aio_context(blkdev->iothread); blkdev->bh = aio_bh_new(blkdev->ctx, blk_bh, blkdev); - - if (xen_mode != XEN_EMULATE) { - batch_maps = 1; - } } static void blk_parse_discard(struct XenBlkDev *blkdev) @@ -981,15 +737,10 @@ static int blk_init(struct XenDevice *xendev) blkdev->file_blk = BLOCK_SIZE; - xen_pv_printf(&blkdev->xendev, 3, "grant copy operation %s\n", - xen_feature_grant_copy ? "enabled" : "disabled"); - /* fill info * blk_connect supplies sector-size and sectors */ xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); - xenstore_write_be_int(&blkdev->xendev, "feature-persistent", - !xen_feature_grant_copy); xenstore_write_be_int(&blkdev->xendev, "info", info); xenstore_write_be_int(&blkdev->xendev, "max-ring-page-order", @@ -1016,19 +767,10 @@ out_error: return -1; } -/* - * We need to account for the grant allocations requiring contiguous - * chunks; the worst case number would be - * max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1, - * but in order to keep things simple just use - * 2 * max_req * max_seg. - */ -#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg)) - static int blk_connect(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int pers, index, qflags; + int index, qflags; bool readonly = true; bool writethrough = true; int order, ring_ref; @@ -1150,11 +892,6 @@ static int blk_connect(struct XenDevice *xendev) &blkdev->xendev.remote_port) == -1) { return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, "feature-persistent", &pers)) { - blkdev->feature_persistent = FALSE; - } else { - blkdev->feature_persistent = !!pers; - } if (!blkdev->xendev.protocol) { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; @@ -1189,11 +926,8 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - /* Calculate the maximum number of grants needed by ioreqs */ - max_grants = MAX_GRANTS(blkdev->max_requests, - BLKIF_MAX_SEGMENTS_PER_REQUEST); /* Add on the number needed for the ring pages */ - max_grants += blkdev->nr_ring_ref; + max_grants = blkdev->nr_ring_ref; xen_be_set_max_grant_refs(xendev, max_grants); @@ -1204,8 +938,6 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - blkdev->cnt_map++; - switch (blkdev->protocol) { case BLKIF_PROTOCOL_NATIVE: { @@ -1229,19 +961,6 @@ static int blk_connect(struct XenDevice *xendev) } } - if (blkdev->feature_persistent) { - /* Init persistent grants */ - blkdev->max_grants = blkdev->max_requests * - BLKIF_MAX_SEGMENTS_PER_REQUEST; - blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp, - NULL, NULL, - batch_maps ? - (GDestroyNotify)g_free : - (GDestroyNotify)destroy_grant); - blkdev->persistent_regions = NULL; - blkdev->persistent_gnt_count = 0; - } - blk_set_aio_context(blkdev->blk, blkdev->ctx); xen_be_bind_evtchn(&blkdev->xendev); @@ -1274,29 +993,8 @@ static void blk_disconnect(struct XenDevice *xendev) if (blkdev->sring) { xen_be_unmap_grant_refs(xendev, blkdev->sring, blkdev->nr_ring_ref); - blkdev->cnt_map--; blkdev->sring = NULL; } - - /* - * Unmap persistent grants before switching to the closed state - * so the frontend can free them. - * - * In the !batch_maps case g_tree_destroy will take care of unmapping - * the grant, but in the batch_maps case we need to iterate over every - * region in persistent_regions and unmap it. - */ - if (blkdev->feature_persistent) { - g_tree_destroy(blkdev->persistent_gnts); - assert(batch_maps || blkdev->persistent_gnt_count == 0); - if (batch_maps) { - blkdev->persistent_gnt_count = 0; - g_slist_foreach(blkdev->persistent_regions, - (GFunc)remove_persistent_region, blkdev); - g_slist_free(blkdev->persistent_regions); - } - blkdev->feature_persistent = false; - } } static int blk_free(struct XenDevice *xendev) From a68bf540f67feacbbd867196b3ce420e51d92a8c Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Thu, 17 May 2018 16:35:56 +0100 Subject: [PATCH 0659/2380] xen_backend: make the xen_feature_grant_copy flag private There is no longer any use of this flag outside of the xen_backend code. Signed-off-by: Paul Durrant Acked-by: Anthony Perard Signed-off-by: Stefano Stabellini --- hw/xen/xen_backend.c | 2 +- include/hw/xen/xen_backend.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index 3c3fc2c6e6..9a8e8771ec 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -44,9 +44,9 @@ BusState *xen_sysbus; /* public */ struct xs_handle *xenstore = NULL; const char *xen_protocol; -bool xen_feature_grant_copy; /* private */ +static bool xen_feature_grant_copy; static int debug; int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen_backend.h index 29bf1c3bc3..9c17fdd85d 100644 --- a/include/hw/xen/xen_backend.h +++ b/include/hw/xen/xen_backend.h @@ -16,7 +16,6 @@ /* variables */ extern struct xs_handle *xenstore; extern const char *xen_protocol; -extern bool xen_feature_grant_copy; extern DeviceState *xen_sysdev; extern BusState *xen_sysbus; From 5ebf96265244e720d56367703a752f05010b7d55 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Thu, 17 May 2018 16:35:57 +0100 Subject: [PATCH 0660/2380] xen_disk: use a single entry iovec Since xen_disk now always copies data to and from a guest there is no need to maintain a vector entry corresponding to every page of a request. This means there is less per-request state to maintain so the ioreq structure can shrink significantly. Signed-off-by: Paul Durrant Acked-by: Anthony Perard Signed-off-by: Stefano Stabellini --- hw/block/xen_disk.c | 76 +++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 55 deletions(-) diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 28be8b6d49..28651c59fa 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -46,13 +46,10 @@ struct ioreq { /* parsed request */ off_t start; QEMUIOVector v; + void *buf; + size_t size; int presync; - /* grant mapping */ - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *pages; - /* aio status */ int aio_inflight; int aio_errors; @@ -110,12 +107,10 @@ static void ioreq_reset(struct ioreq *ioreq) memset(&ioreq->req, 0, sizeof(ioreq->req)); ioreq->status = 0; ioreq->start = 0; + ioreq->buf = NULL; + ioreq->size = 0; ioreq->presync = 0; - memset(ioreq->refs, 0, sizeof(ioreq->refs)); - memset(ioreq->page, 0, sizeof(ioreq->page)); - ioreq->pages = NULL; - ioreq->aio_inflight = 0; ioreq->aio_errors = 0; @@ -138,7 +133,7 @@ static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) ioreq = g_malloc0(sizeof(*ioreq)); ioreq->blkdev = blkdev; blkdev->requests_total++; - qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST); + qemu_iovec_init(&ioreq->v, 1); } else { /* get one from freelist */ ioreq = QLIST_FIRST(&blkdev->freelist); @@ -183,7 +178,6 @@ static void ioreq_release(struct ioreq *ioreq, bool finish) static int ioreq_parse(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - uintptr_t mem; size_t len; int i; @@ -230,13 +224,10 @@ static int ioreq_parse(struct ioreq *ioreq) goto err; } - ioreq->refs[i] = ioreq->req.seg[i].gref; - - mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; - qemu_iovec_add(&ioreq->v, (void*)mem, len); + ioreq->size += len; } - if (ioreq->start + ioreq->v.size > blkdev->file_size) { + if (ioreq->start + ioreq->size > blkdev->file_size) { xen_pv_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); goto err; } @@ -247,35 +238,6 @@ err: return -1; } -static void ioreq_free_copy_buffers(struct ioreq *ioreq) -{ - int i; - - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->page[i] = NULL; - } - - qemu_vfree(ioreq->pages); -} - -static int ioreq_init_copy_buffers(struct ioreq *ioreq) -{ - int i; - - if (ioreq->v.niov == 0) { - return 0; - } - - ioreq->pages = qemu_memalign(XC_PAGE_SIZE, ioreq->v.niov * XC_PAGE_SIZE); - - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->page[i] = ioreq->pages + i * XC_PAGE_SIZE; - ioreq->v.iov[i].iov_base = ioreq->page[i]; - } - - return 0; -} - static int ioreq_grant_copy(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; @@ -284,25 +246,27 @@ static int ioreq_grant_copy(struct ioreq *ioreq) int i, count, rc; int64_t file_blk = ioreq->blkdev->file_blk; bool to_domain = (ioreq->req.operation == BLKIF_OP_READ); + void *virt = ioreq->buf; - if (ioreq->v.niov == 0) { + if (ioreq->req.nr_segments == 0) { return 0; } - count = ioreq->v.niov; + count = ioreq->req.nr_segments; for (i = 0; i < count; i++) { if (to_domain) { - segs[i].dest.foreign.ref = ioreq->refs[i]; + segs[i].dest.foreign.ref = ioreq->req.seg[i].gref; segs[i].dest.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; - segs[i].source.virt = ioreq->v.iov[i].iov_base; + segs[i].source.virt = virt; } else { - segs[i].source.foreign.ref = ioreq->refs[i]; + segs[i].source.foreign.ref = ioreq->req.seg[i].gref; segs[i].source.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; - segs[i].dest.virt = ioreq->v.iov[i].iov_base; + segs[i].dest.virt = virt; } segs[i].len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * file_blk; + virt += segs[i].len; } rc = xen_be_copy_grant_refs(xendev, to_domain, segs, count); @@ -348,14 +312,14 @@ static void qemu_aio_complete(void *opaque, int ret) if (ret == 0) { ioreq_grant_copy(ioreq); } - ioreq_free_copy_buffers(ioreq); + qemu_vfree(ioreq->buf); break; case BLKIF_OP_WRITE: case BLKIF_OP_FLUSH_DISKCACHE: if (!ioreq->req.nr_segments) { break; } - ioreq_free_copy_buffers(ioreq); + qemu_vfree(ioreq->buf); break; default: break; @@ -423,12 +387,12 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - ioreq_init_copy_buffers(ioreq); + ioreq->buf = qemu_memalign(XC_PAGE_SIZE, ioreq->size); if (ioreq->req.nr_segments && (ioreq->req.operation == BLKIF_OP_WRITE || ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && ioreq_grant_copy(ioreq)) { - ioreq_free_copy_buffers(ioreq); + qemu_vfree(ioreq->buf); goto err; } @@ -440,6 +404,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) switch (ioreq->req.operation) { case BLKIF_OP_READ: + qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size); block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, ioreq->v.size, BLOCK_ACCT_READ); ioreq->aio_inflight++; @@ -452,6 +417,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) break; } + qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size); block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, ioreq->v.size, ioreq->req.operation == BLKIF_OP_WRITE ? From 443c3c9cf1a18afcc705745d18101a4ea4de5b26 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Thu, 17 May 2018 16:35:58 +0100 Subject: [PATCH 0661/2380] xen_disk: be consistent with use of xendev and blkdev->xendev Certain functions in xen_disk are called with a pointer to xendev (struct XenDevice *). They then use container_of() to acces the surrounding blkdev (struct XenBlkDev) but then in various places use &blkdev->xendev when use of the original xendev pointer is shorter to express and clearly equivalent. This patch is a purely cosmetic patch which makes sure there is a xendev pointer on stack for any function where the pointer is need on multiple occasions modified those functions to use it consistently. Signed-off-by: Paul Durrant Acked-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- hw/block/xen_disk.c | 90 +++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 28651c59fa..9fbc0cdb87 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -178,10 +178,11 @@ static void ioreq_release(struct ioreq *ioreq, bool finish) static int ioreq_parse(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; size_t len; int i; - xen_pv_printf(&blkdev->xendev, 3, + xen_pv_printf(xendev, 3, "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", ioreq->req.operation, ioreq->req.nr_segments, ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); @@ -199,28 +200,28 @@ static int ioreq_parse(struct ioreq *ioreq) case BLKIF_OP_DISCARD: return 0; default: - xen_pv_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + xen_pv_printf(xendev, 0, "error: unknown operation (%d)\n", ioreq->req.operation); goto err; }; if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') { - xen_pv_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + xen_pv_printf(xendev, 0, "error: write req for ro device\n"); goto err; } ioreq->start = ioreq->req.sector_number * blkdev->file_blk; for (i = 0; i < ioreq->req.nr_segments; i++) { if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - xen_pv_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + xen_pv_printf(xendev, 0, "error: nr_segments too big\n"); goto err; } if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { - xen_pv_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + xen_pv_printf(xendev, 0, "error: first > last sector\n"); goto err; } if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { - xen_pv_printf(&blkdev->xendev, 0, "error: page crossing\n"); + xen_pv_printf(xendev, 0, "error: page crossing\n"); goto err; } @@ -228,7 +229,7 @@ static int ioreq_parse(struct ioreq *ioreq) ioreq->size += len; } if (ioreq->start + ioreq->size > blkdev->file_size) { - xen_pv_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + xen_pv_printf(xendev, 0, "error: access beyond end of file\n"); goto err; } return 0; @@ -244,7 +245,7 @@ static int ioreq_grant_copy(struct ioreq *ioreq) struct XenDevice *xendev = &blkdev->xendev; XenGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; int i, count, rc; - int64_t file_blk = ioreq->blkdev->file_blk; + int64_t file_blk = blkdev->file_blk; bool to_domain = (ioreq->req.operation == BLKIF_OP_READ); void *virt = ioreq->buf; @@ -272,7 +273,7 @@ static int ioreq_grant_copy(struct ioreq *ioreq) rc = xen_be_copy_grant_refs(xendev, to_domain, segs, count); if (rc) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, + xen_pv_printf(xendev, 0, "failed to copy data %d\n", rc); ioreq->aio_errors++; return -1; @@ -287,11 +288,12 @@ static void qemu_aio_complete(void *opaque, int ret) { struct ioreq *ioreq = opaque; struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; aio_context_acquire(blkdev->ctx); if (ret != 0) { - xen_pv_printf(&blkdev->xendev, 0, "%s I/O error\n", + xen_pv_printf(xendev, 0, "%s I/O error\n", ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); ioreq->aio_errors++; } @@ -625,16 +627,17 @@ static void blk_alloc(struct XenDevice *xendev) static void blk_parse_discard(struct XenBlkDev *blkdev) { + struct XenDevice *xendev = &blkdev->xendev; int enable; blkdev->feature_discard = true; - if (xenstore_read_be_int(&blkdev->xendev, "discard-enable", &enable) == 0) { + if (xenstore_read_be_int(xendev, "discard-enable", &enable) == 0) { blkdev->feature_discard = !!enable; } if (blkdev->feature_discard) { - xenstore_write_be_int(&blkdev->xendev, "feature-discard", 1); + xenstore_write_be_int(xendev, "feature-discard", 1); } } @@ -649,7 +652,7 @@ static int blk_init(struct XenDevice *xendev) /* read xenstore entries */ if (blkdev->params == NULL) { char *h = NULL; - blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + blkdev->params = xenstore_read_be_str(xendev, "params"); if (blkdev->params != NULL) { h = strchr(blkdev->params, ':'); } @@ -669,18 +672,18 @@ static int blk_init(struct XenDevice *xendev) blkdev->fileproto = "vpc"; } if (blkdev->mode == NULL) { - blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + blkdev->mode = xenstore_read_be_str(xendev, "mode"); } if (blkdev->type == NULL) { - blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + blkdev->type = xenstore_read_be_str(xendev, "type"); } if (blkdev->dev == NULL) { - blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + blkdev->dev = xenstore_read_be_str(xendev, "dev"); } if (blkdev->devtype == NULL) { - blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + blkdev->devtype = xenstore_read_be_str(xendev, "device-type"); } - directiosafe = xenstore_read_be_str(&blkdev->xendev, "direct-io-safe"); + directiosafe = xenstore_read_be_str(xendev, "direct-io-safe"); blkdev->directiosafe = (directiosafe && atoi(directiosafe)); /* do we have all we need? */ @@ -706,10 +709,10 @@ static int blk_init(struct XenDevice *xendev) /* fill info * blk_connect supplies sector-size and sectors */ - xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); - xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(xendev, "feature-flush-cache", 1); + xenstore_write_be_int(xendev, "info", info); - xenstore_write_be_int(&blkdev->xendev, "max-ring-page-order", + xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_PAGE_ORDER); blk_parse_discard(blkdev); @@ -761,7 +764,7 @@ static int blk_connect(struct XenDevice *xendev) } /* init qemu block driver */ - index = (blkdev->xendev.dev - 202 * 256) / 16; + index = (xendev->dev - 202 * 256) / 16; blkdev->dinfo = drive_get(IF_XEN, 0, index); if (!blkdev->dinfo) { Error *local_err = NULL; @@ -773,11 +776,11 @@ static int blk_connect(struct XenDevice *xendev) } /* setup via xenbus -> create new block driver instance */ - xen_pv_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); + xen_pv_printf(xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->blk = blk_new_open(blkdev->filename, NULL, options, qflags, &local_err); if (!blkdev->blk) { - xen_pv_printf(&blkdev->xendev, 0, "error: %s\n", + xen_pv_printf(xendev, 0, "error: %s\n", error_get_pretty(local_err)); error_free(local_err); return -1; @@ -785,11 +788,11 @@ static int blk_connect(struct XenDevice *xendev) blk_set_enable_write_cache(blkdev->blk, !writethrough); } else { /* setup via qemu cmdline -> already setup for us */ - xen_pv_printf(&blkdev->xendev, 2, + xen_pv_printf(xendev, 2, "get configured bdrv (cmdline setup)\n"); blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo); if (blk_is_read_only(blkdev->blk) && !readonly) { - xen_pv_printf(&blkdev->xendev, 0, "Unexpected read-only drive"); + xen_pv_printf(xendev, 0, "Unexpected read-only drive"); blkdev->blk = NULL; return -1; } @@ -802,7 +805,7 @@ static int blk_connect(struct XenDevice *xendev) if (blkdev->file_size < 0) { BlockDriverState *bs = blk_bs(blkdev->blk); const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL; - xen_pv_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n", + xen_pv_printf(xendev, 1, "blk_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), drv_name ?: "-"); blkdev->file_size = 0; @@ -814,15 +817,15 @@ static int blk_connect(struct XenDevice *xendev) blkdev->file_size, blkdev->file_size >> 20); /* Fill in number of sector size and number of sectors */ - xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); - xenstore_write_be_int64(&blkdev->xendev, "sectors", + xenstore_write_be_int(xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int64(xendev, "sectors", blkdev->file_size / blkdev->file_blk); - if (xenstore_read_fe_int(&blkdev->xendev, "ring-page-order", + if (xenstore_read_fe_int(xendev, "ring-page-order", &order) == -1) { blkdev->nr_ring_ref = 1; - if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", + if (xenstore_read_fe_int(xendev, "ring-ref", &ring_ref) == -1) { return -1; } @@ -839,7 +842,7 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, key, + if (xenstore_read_fe_int(xendev, key, &ring_ref) == -1) { g_free(key); return -1; @@ -854,18 +857,18 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", - &blkdev->xendev.remote_port) == -1) { + if (xenstore_read_fe_int(xendev, "event-channel", + &xendev->remote_port) == -1) { return -1; } - if (!blkdev->xendev.protocol) { + if (!xendev->protocol) { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_32; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_64; } else { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; @@ -896,7 +899,6 @@ static int blk_connect(struct XenDevice *xendev) max_grants = blkdev->nr_ring_ref; xen_be_set_max_grant_refs(xendev, max_grants); - blkdev->sring = xen_be_map_grant_refs(xendev, blkdev->ring_ref, blkdev->nr_ring_ref, PROT_READ | PROT_WRITE); @@ -929,12 +931,12 @@ static int blk_connect(struct XenDevice *xendev) blk_set_aio_context(blkdev->blk, blkdev->ctx); - xen_be_bind_evtchn(&blkdev->xendev); + xen_be_bind_evtchn(xendev); - xen_pv_printf(&blkdev->xendev, 1, "ok: proto %s, nr-ring-ref %u, " + xen_pv_printf(xendev, 1, "ok: proto %s, nr-ring-ref %u, " "remote port %d, local port %d\n", - blkdev->xendev.protocol, blkdev->nr_ring_ref, - blkdev->xendev.remote_port, blkdev->xendev.local_port); + xendev->protocol, blkdev->nr_ring_ref, + xendev->remote_port, xendev->local_port); return 0; } @@ -952,7 +954,7 @@ static void blk_disconnect(struct XenDevice *xendev) blk_unref(blkdev->blk); blkdev->blk = NULL; } - xen_pv_unbind_evtchn(&blkdev->xendev); + xen_pv_unbind_evtchn(xendev); aio_context_release(blkdev->ctx); From dda53ee93af90547912f0ea99ca85deba49fd364 Mon Sep 17 00:00:00 2001 From: Zihan Yang Date: Wed, 25 Apr 2018 17:52:23 +0800 Subject: [PATCH 0662/2380] hw/pci-host/q35: Replace hardcoded value with macro During smram region initialization some addresses are hardcoded, replace them with macro to be more clear to readers. Previous patch forgets about one value and exceeds the line limit of 90 characters. The v2 breaks a few long lines Signed-off-by: Zihan Yang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-host/q35.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index a36a1195e4..02f9576588 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -535,13 +535,15 @@ static void mch_realize(PCIDevice *d, Error **errp) /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", - mch->pci_address_space, 0xa0000, 0x20000); - memory_region_add_subregion_overlap(mch->system_memory, 0xa0000, + mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); + memory_region_add_subregion_overlap(mch->system_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, &mch->smram_region, 1); memory_region_set_enabled(&mch->smram_region, true); memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_add_subregion_overlap(mch->system_memory, 0xfeda0000, &mch->open_high_smram, 1); memory_region_set_enabled(&mch->open_high_smram, false); @@ -550,11 +552,14 @@ static void mch_realize(PCIDevice *d, Error **errp) memory_region_init(&mch->smram, OBJECT(mch), "smram", 1ull << 32); memory_region_set_enabled(&mch->smram, true); memory_region_init_alias(&mch->low_smram, OBJECT(mch), "smram-low", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_set_enabled(&mch->low_smram, true); - memory_region_add_subregion(&mch->smram, 0xa0000, &mch->low_smram); + memory_region_add_subregion(&mch->smram, MCH_HOST_BRIDGE_SMRAM_C_BASE, + &mch->low_smram); memory_region_init_alias(&mch->high_smram, OBJECT(mch), "smram-high", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_set_enabled(&mch->high_smram, true); memory_region_add_subregion(&mch->smram, 0xfeda0000, &mch->high_smram); From 50a6fa8f939a3448967592e5136afcc063f9a25d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 24 Apr 2018 08:38:57 +0200 Subject: [PATCH 0663/2380] allocate pci id for mdpy mdpy is a sample pci device for vfio-mdev. Not (yet) merged upstream, patch available here: https://www.kraxel.org/cgit/linux/commit/?h=vfio-sample-display&id=6fd86cff3d7df38ab89625b16fdd6434b1c18749 Cc: Alex Williamson Signed-off-by: Gerd Hoffmann Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/specs/pci-ids.txt | 1 + include/hw/pci/pci.h | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt index bb99a0257e..4d53e5c7d9 100644 --- a/docs/specs/pci-ids.txt +++ b/docs/specs/pci-ids.txt @@ -62,6 +62,7 @@ PCI devices (other than virtio): 1b36:000a PCI-PCI bridge (multiseat) 1b36:000b PCIe Expander Bridge (-device pxb-pcie) 1b36:000d PCI xhci usb host adapter +1b36:000f mdpy (mdev sample device), linux/samples/vfio-mdev/mdpy.c All these devices are documented in docs/specs. diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index a9c3ee5aa2..990d6fcbde 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -101,6 +101,7 @@ extern bool pci_available; #define PCI_DEVICE_ID_REDHAT_PCIE_RP 0x000c #define PCI_DEVICE_ID_REDHAT_XHCI 0x000d #define PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE 0x000e +#define PCI_DEVICE_ID_REDHAT_MDPY 0x000f #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 From b7b126442977dcfa5641e38b084bd13a0b366cff Mon Sep 17 00:00:00 2001 From: Jonathan Helman Date: Mon, 19 Mar 2018 15:28:49 -0700 Subject: [PATCH 0664/2380] virtio-balloon: add hugetlb page allocation counts qemu should read and report hugetlb page allocation counts exported in the following kernel patch: commit 4c3ca37c4a4394978fd0f005625f6064ed2b9a64 Author: Jonathan Helman Date: Mon Mar 19 11:00:35 2018 -0700 virtio_balloon: export hugetlb page allocation counts Export the number of successful and failed hugetlb page allocations via the virtio balloon driver. These 2 counts come directly from the vm_events HTLB_BUDDY_PGALLOC and HTLB_BUDDY_PGALLOC_FAIL. Signed-off-by: Jonathan Helman Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Jason Wang --- docs/virtio-balloon-stats.txt | 2 ++ hw/virtio/virtio-balloon.c | 2 ++ include/standard-headers/linux/virtio_balloon.h | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/virtio-balloon-stats.txt b/docs/virtio-balloon-stats.txt index 7a66d25da5..9985e1dffc 100644 --- a/docs/virtio-balloon-stats.txt +++ b/docs/virtio-balloon-stats.txt @@ -34,6 +34,8 @@ which will return a dictionary containing: - stat-total-memory - stat-available-memory - stat-disk-caches + - stat-htlb-pgalloc + - stat-htlb-pgfail o A key named last-update, which contains the last stats update timestamp in seconds. Since this timestamp is generated by the host, diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index f456cea2e7..1f7a87f094 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -52,6 +52,8 @@ static const char *balloon_stat_names[] = { [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory", [VIRTIO_BALLOON_S_AVAIL] = "stat-available-memory", [VIRTIO_BALLOON_S_CACHES] = "stat-disk-caches", + [VIRTIO_BALLOON_S_HTLB_PGALLOC] = "stat-htlb-pgalloc", + [VIRTIO_BALLOON_S_HTLB_PGFAIL] = "stat-htlb-pgfail", [VIRTIO_BALLOON_S_NR] = NULL }; diff --git a/include/standard-headers/linux/virtio_balloon.h b/include/standard-headers/linux/virtio_balloon.h index 7b0a41b8fc..e446805ae9 100644 --- a/include/standard-headers/linux/virtio_balloon.h +++ b/include/standard-headers/linux/virtio_balloon.h @@ -53,7 +53,9 @@ struct virtio_balloon_config { #define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ #define VIRTIO_BALLOON_S_AVAIL 6 /* Available memory as in /proc */ #define VIRTIO_BALLOON_S_CACHES 7 /* Disk caches */ -#define VIRTIO_BALLOON_S_NR 8 +#define VIRTIO_BALLOON_S_HTLB_PGALLOC 8 /* Hugetlb page allocations */ +#define VIRTIO_BALLOON_S_HTLB_PGFAIL 9 /* Hugetlb page allocation failures */ +#define VIRTIO_BALLOON_S_NR 10 /* * Memory statistics structure. From ffcbbe722fcb3c162318cba1b94a115498e25acd Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 27 Apr 2018 17:07:24 +0800 Subject: [PATCH 0665/2380] vhost: add trace for IOTLB miss Add some trace points for IOTLB translation for vhost. After vhost-user is setup, the only IO path that QEMU will participate should be the IOMMU translation, so it'll be good we can track this with explicit timestamps when needed to see how long time we take to do the translation, and whether there's anything stuck inside. It might be useful for triaging vhost-user problems. Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/trace-events | 1 + hw/virtio/vhost.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 1422ff03ab..07bcbe9e85 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -6,6 +6,7 @@ vhost_region_add_section(const char *name, uint64_t gpa, uint64_t size, uint64_t vhost_region_add_section_merge(const char *name, uint64_t new_size, uint64_t gpa, uint64_t owr) "%s: size: 0x%"PRIx64 " gpa: 0x%"PRIx64 " owr: 0x%"PRIx64 vhost_region_add_section_aligned(const char *name, uint64_t gpa, uint64_t size, uint64_t host) "%s: 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64 vhost_section(const char *name, int r) "%s:%d" +vhost_iotlb_miss(void *dev, int step) "%p step %d" # hw/virtio/vhost-user.c vhost_user_postcopy_end_entry(void) "" diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 9d5850a7d7..b08290036d 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -894,12 +894,15 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) rcu_read_lock(); + trace_vhost_iotlb_miss(dev, 1); + iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as, iova, write); if (iotlb.target_as != NULL) { ret = vhost_memory_region_lookup(dev, iotlb.translated_addr, &uaddr, &len); if (ret) { + trace_vhost_iotlb_miss(dev, 3); error_report("Fail to lookup the translated address " "%"PRIx64, iotlb.translated_addr); goto out; @@ -911,10 +914,14 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) ret = vhost_backend_update_device_iotlb(dev, iova, uaddr, len, iotlb.perm); if (ret) { + trace_vhost_iotlb_miss(dev, 4); error_report("Fail to update device iotlb"); goto out; } } + + trace_vhost_iotlb_miss(dev, 2); + out: rcu_read_unlock(); From ec7b10800c3c91afe6777ea13068ccf280a741ce Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 17 Apr 2018 21:42:21 +0300 Subject: [PATCH 0666/2380] update-linux-headers.sh: drop kvm_para.h hacks It turns out (as will be clear from follow-up patches) we do not really need any kvm para macros host side for now, except on x86, and there we need it unconditionally whether we run on kvm or we don't. Import the x86 asm/kvm_para.h into standard-headers, follow-up patches remove a bunch of code using this. Signed-off-by: Michael S. Tsirkin --- scripts/update-linux-headers.sh | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index b65c03f0ae..4fa08d38c8 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -43,6 +43,7 @@ cp_portable() { -e 'limits' \ -e 'linux/kernel' \ -e 'linux/sysinfo' \ + -e 'asm-generic/kvm_para' \ > /dev/null then echo "Unexpected #include in input file $f". @@ -98,13 +99,9 @@ for arch in $ARCHLIST; do rm -rf "$output/linux-headers/asm-$arch" mkdir -p "$output/linux-headers/asm-$arch" - for header in kvm.h kvm_para.h unistd.h; do + for header in kvm.h unistd.h; do cp "$tmpdir/include/asm/$header" "$output/linux-headers/asm-$arch" done - if [ $arch = powerpc ]; then - cp "$tmpdir/include/asm/epapr_hcalls.h" "$output/linux-headers/asm-powerpc/" - fi - rm -rf "$output/include/standard-headers/asm-$arch" mkdir -p "$output/include/standard-headers/asm-$arch" if [ $arch = s390 ]; then @@ -121,20 +118,17 @@ for arch in $ARCHLIST; do cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/" cp "$tmpdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" + cp_portable "$tmpdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch" fi done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" -for header in kvm.h kvm_para.h vfio.h vfio_ccw.h vhost.h \ +for header in kvm.h vfio.h vfio_ccw.h vhost.h \ psci.h psp-sev.h userfaultfd.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done -rm -rf "$output/linux-headers/asm-generic" -mkdir -p "$output/linux-headers/asm-generic" -for header in kvm_para.h; do - cp "$tmpdir/include/asm-generic/$header" "$output/linux-headers/asm-generic" -done + if [ -L "$linux/source" ]; then cp "$linux/source/COPYING" "$output/linux-headers" else From d49bd359d186a83fbb66a23dafd2d6664b79aa6a Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 17 Apr 2018 21:45:56 +0300 Subject: [PATCH 0667/2380] include/standard-headers: add asm-x86/kvm_para.h Import asm-x86/kvm_para.h from linux where it can be easily used on Linux and non-Linux platforms. Signed-off-by: Michael S. Tsirkin --- include/standard-headers/asm-x86/kvm_para.h | 121 ++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 include/standard-headers/asm-x86/kvm_para.h diff --git a/include/standard-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h new file mode 100644 index 0000000000..53a85ae3ed --- /dev/null +++ b/include/standard-headers/asm-x86/kvm_para.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_X86_KVM_PARA_H +#define _ASM_X86_KVM_PARA_H + +#include "standard-headers/linux/types.h" + +/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It + * should be used to determine that a VM is running under KVM. + */ +#define KVM_CPUID_SIGNATURE 0x40000000 + +/* This CPUID returns two feature bitmaps in eax, edx. Before enabling + * a particular paravirtualization, the appropriate feature bit should + * be checked in eax. The performance hint feature bit should be checked + * in edx. + */ +#define KVM_CPUID_FEATURES 0x40000001 +#define KVM_FEATURE_CLOCKSOURCE 0 +#define KVM_FEATURE_NOP_IO_DELAY 1 +#define KVM_FEATURE_MMU_OP 2 +/* This indicates that the new set of kvmclock msrs + * are available. The use of 0x11 and 0x12 is deprecated + */ +#define KVM_FEATURE_CLOCKSOURCE2 3 +#define KVM_FEATURE_ASYNC_PF 4 +#define KVM_FEATURE_STEAL_TIME 5 +#define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_PV_UNHALT 7 +#define KVM_FEATURE_PV_TLB_FLUSH 9 +#define KVM_FEATURE_ASYNC_PF_VMEXIT 10 + +#define KVM_HINTS_DEDICATED 0 + +/* The last 8 bits are used to indicate how to interpret the flags field + * in pvclock structure. If no bits are set, all flags are ignored. + */ +#define KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24 + +#define MSR_KVM_WALL_CLOCK 0x11 +#define MSR_KVM_SYSTEM_TIME 0x12 + +#define KVM_MSR_ENABLED 1 +/* Custom MSRs falls in the range 0x4b564d00-0x4b564dff */ +#define MSR_KVM_WALL_CLOCK_NEW 0x4b564d00 +#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01 +#define MSR_KVM_ASYNC_PF_EN 0x4b564d02 +#define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_PV_EOI_EN 0x4b564d04 + +struct kvm_steal_time { + uint64_t steal; + uint32_t version; + uint32_t flags; + uint8_t preempted; + uint8_t uint8_t_pad[3]; + uint32_t pad[11]; +}; + +#define KVM_VCPU_PREEMPTED (1 << 0) +#define KVM_VCPU_FLUSH_TLB (1 << 1) + +#define KVM_CLOCK_PAIRING_WALLCLOCK 0 +struct kvm_clock_pairing { + int64_t sec; + int64_t nsec; + uint64_t tsc; + uint32_t flags; + uint32_t pad[9]; +}; + +#define KVM_STEAL_ALIGNMENT_BITS 5 +#define KVM_STEAL_VALID_BITS ((-1ULL << (KVM_STEAL_ALIGNMENT_BITS + 1))) +#define KVM_STEAL_RESERVED_MASK (((1 << KVM_STEAL_ALIGNMENT_BITS) - 1 ) << 1) + +#define KVM_MAX_MMU_OP_BATCH 32 + +#define KVM_ASYNC_PF_ENABLED (1 << 0) +#define KVM_ASYNC_PF_SEND_ALWAYS (1 << 1) +#define KVM_ASYNC_PF_DELIVERY_AS_PF_VMEXIT (1 << 2) + +/* Operations for KVM_HC_MMU_OP */ +#define KVM_MMU_OP_WRITE_PTE 1 +#define KVM_MMU_OP_FLUSH_TLB 2 +#define KVM_MMU_OP_RELEASE_PT 3 + +/* Payload for KVM_HC_MMU_OP */ +struct kvm_mmu_op_header { + uint32_t op; + uint32_t pad; +}; + +struct kvm_mmu_op_write_pte { + struct kvm_mmu_op_header header; + uint64_t pte_phys; + uint64_t pte_val; +}; + +struct kvm_mmu_op_flush_tlb { + struct kvm_mmu_op_header header; +}; + +struct kvm_mmu_op_release_pt { + struct kvm_mmu_op_header header; + uint64_t pt_phys; +}; + +#define KVM_PV_REASON_PAGE_NOT_PRESENT 1 +#define KVM_PV_REASON_PAGE_READY 2 + +struct kvm_vcpu_pv_apf_data { + uint32_t reason; + uint8_t pad[60]; + uint32_t enabled; +}; + +#define KVM_PV_EOI_BIT 0 +#define KVM_PV_EOI_MASK (0x1 << KVM_PV_EOI_BIT) +#define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK +#define KVM_PV_EOI_DISABLED 0x0 + +#endif /* _ASM_X86_KVM_PARA_H */ From 1814eab67398029a927847dfd29198a26aeaf7d8 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 17 Apr 2018 21:47:50 +0300 Subject: [PATCH 0668/2380] x86/cpu: use standard-headers/asm-x86.kvm_para.h Switch to the header we imported from Linux, this allows us to drop a hack in kvm_i386.h. More code will be dropped in the next patch. Signed-off-by: Michael S. Tsirkin --- hw/i386/kvm/clock.c | 2 +- include/sysemu/kvm.h | 1 - target/i386/cpu.c | 4 +--- target/i386/cpu.h | 2 -- target/i386/kvm.c | 4 ++-- target/i386/kvm_i386.h | 6 ------ 6 files changed, 4 insertions(+), 15 deletions(-) diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 7dac319403..0bf1c60a06 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include -#include +#include "standard-headers/asm-x86/kvm_para.h" #define TYPE_KVM_CLOCK "kvmclock" #define KVM_CLOCK(obj) OBJECT_CHECK(KVMClockState, (obj), TYPE_KVM_CLOCK) diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 23669c4d5a..0b64b8e067 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -22,7 +22,6 @@ #ifdef NEED_CPU_H # ifdef CONFIG_KVM # include -# include # define CONFIG_KVM_IS_POSSIBLE # endif #else diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d95310ffd4..94260412e2 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -40,9 +40,7 @@ #include "qom/qom-qobject.h" #include "sysemu/arch_init.h" -#if defined(CONFIG_KVM) -#include -#endif +#include "standard-headers/asm-x86/kvm_para.h" #include "sysemu/sysemu.h" #include "hw/qdev-properties.h" diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8ac13f6c2c..664504610e 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -688,8 +688,6 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */ #define CPUID_7_0_EDX_SPEC_CTRL_SSBD (1U << 31) /* Speculative Store Bypass Disable */ -#define KVM_HINTS_DEDICATED (1U << 0) - #define CPUID_8000_0008_EBX_IBPB (1U << 12) /* Indirect Branch Prediction Barrier */ #define CPUID_XSAVE_XSAVEOPT (1U << 0) diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 0c656a91a4..6511329d11 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -18,7 +18,7 @@ #include #include -#include +#include "standard-headers/asm-x86/kvm_para.h" #include "qemu-common.h" #include "cpu.h" @@ -387,7 +387,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, ret &= ~(1U << KVM_FEATURE_PV_UNHALT); } } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) { - ret |= KVM_HINTS_DEDICATED; + ret |= 1U << KVM_HINTS_DEDICATED; found = 1; } diff --git a/target/i386/kvm_i386.h b/target/i386/kvm_i386.h index 1de9876cd9..e5df24cad1 100644 --- a/target/i386/kvm_i386.h +++ b/target/i386/kvm_i386.h @@ -30,12 +30,6 @@ #define kvm_pic_in_kernel() 0 #define kvm_ioapic_in_kernel() 0 -/* These constants must never be used at runtime if kvm_enabled() is false. - * They exist so we don't need #ifdefs around KVM-specific code that already - * checks kvm_enabled() properly. - */ -#define KVM_CPUID_FEATURES 0 - #endif /* CONFIG_KVM */ bool kvm_allows_irq0_override(void); From fe7f9b8e6e095bc2842180fe72dd45158678eeb7 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 17 Apr 2018 21:51:25 +0300 Subject: [PATCH 0669/2380] linux-headers: drop kvm_para.h Unused now and can be removed. Signed-off-by: Michael S. Tsirkin --- linux-headers/asm-arm/kvm_para.h | 2 - linux-headers/asm-arm64/kvm_para.h | 1 - linux-headers/asm-generic/kvm_para.h | 4 - linux-headers/asm-mips/kvm_para.h | 5 - linux-headers/asm-powerpc/epapr_hcalls.h | 99 ------------------- linux-headers/asm-powerpc/kvm_para.h | 98 ------------------- linux-headers/asm-s390/kvm_para.h | 8 -- linux-headers/asm-x86/kvm_para.h | 118 ----------------------- linux-headers/linux/kvm_para.h | 35 ------- 9 files changed, 370 deletions(-) delete mode 100644 linux-headers/asm-arm/kvm_para.h delete mode 100644 linux-headers/asm-arm64/kvm_para.h delete mode 100644 linux-headers/asm-generic/kvm_para.h delete mode 100644 linux-headers/asm-mips/kvm_para.h delete mode 100644 linux-headers/asm-powerpc/epapr_hcalls.h delete mode 100644 linux-headers/asm-powerpc/kvm_para.h delete mode 100644 linux-headers/asm-s390/kvm_para.h delete mode 100644 linux-headers/asm-x86/kvm_para.h delete mode 100644 linux-headers/linux/kvm_para.h diff --git a/linux-headers/asm-arm/kvm_para.h b/linux-headers/asm-arm/kvm_para.h deleted file mode 100644 index baacc4996d..0000000000 --- a/linux-headers/asm-arm/kvm_para.h +++ /dev/null @@ -1,2 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#include diff --git a/linux-headers/asm-arm64/kvm_para.h b/linux-headers/asm-arm64/kvm_para.h deleted file mode 100644 index 14fab8f0b9..0000000000 --- a/linux-headers/asm-arm64/kvm_para.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/linux-headers/asm-generic/kvm_para.h b/linux-headers/asm-generic/kvm_para.h deleted file mode 100644 index 486f0af73c..0000000000 --- a/linux-headers/asm-generic/kvm_para.h +++ /dev/null @@ -1,4 +0,0 @@ -/* - * There isn't anything here, but the file must not be empty or patch - * will delete it. - */ diff --git a/linux-headers/asm-mips/kvm_para.h b/linux-headers/asm-mips/kvm_para.h deleted file mode 100644 index dbb2464f3b..0000000000 --- a/linux-headers/asm-mips/kvm_para.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef _ASM_MIPS_KVM_PARA_H -#define _ASM_MIPS_KVM_PARA_H - - -#endif /* _ASM_MIPS_KVM_PARA_H */ diff --git a/linux-headers/asm-powerpc/epapr_hcalls.h b/linux-headers/asm-powerpc/epapr_hcalls.h deleted file mode 100644 index 6cca559993..0000000000 --- a/linux-headers/asm-powerpc/epapr_hcalls.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */ -/* - * ePAPR hcall interface - * - * Copyright 2008-2011 Freescale Semiconductor, Inc. - * - * Author: Timur Tabi - * - * This file is provided under a dual BSD/GPL license. When using or - * redistributing this file, you may do so under either license. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _ASM_POWERPC_EPAPR_HCALLS_H -#define _ASM_POWERPC_EPAPR_HCALLS_H - -#define EV_BYTE_CHANNEL_SEND 1 -#define EV_BYTE_CHANNEL_RECEIVE 2 -#define EV_BYTE_CHANNEL_POLL 3 -#define EV_INT_SET_CONFIG 4 -#define EV_INT_GET_CONFIG 5 -#define EV_INT_SET_MASK 6 -#define EV_INT_GET_MASK 7 -#define EV_INT_IACK 9 -#define EV_INT_EOI 10 -#define EV_INT_SEND_IPI 11 -#define EV_INT_SET_TASK_PRIORITY 12 -#define EV_INT_GET_TASK_PRIORITY 13 -#define EV_DOORBELL_SEND 14 -#define EV_MSGSND 15 -#define EV_IDLE 16 - -/* vendor ID: epapr */ -#define EV_LOCAL_VENDOR_ID 0 /* for private use */ -#define EV_EPAPR_VENDOR_ID 1 -#define EV_FSL_VENDOR_ID 2 /* Freescale Semiconductor */ -#define EV_IBM_VENDOR_ID 3 /* IBM */ -#define EV_GHS_VENDOR_ID 4 /* Green Hills Software */ -#define EV_ENEA_VENDOR_ID 5 /* Enea */ -#define EV_WR_VENDOR_ID 6 /* Wind River Systems */ -#define EV_AMCC_VENDOR_ID 7 /* Applied Micro Circuits */ -#define EV_KVM_VENDOR_ID 42 /* KVM */ - -/* The max number of bytes that a byte channel can send or receive per call */ -#define EV_BYTE_CHANNEL_MAX_BYTES 16 - - -#define _EV_HCALL_TOKEN(id, num) (((id) << 16) | (num)) -#define EV_HCALL_TOKEN(hcall_num) _EV_HCALL_TOKEN(EV_EPAPR_VENDOR_ID, hcall_num) - -/* epapr return codes */ -#define EV_SUCCESS 0 -#define EV_EPERM 1 /* Operation not permitted */ -#define EV_ENOENT 2 /* Entry Not Found */ -#define EV_EIO 3 /* I/O error occurred */ -#define EV_EAGAIN 4 /* The operation had insufficient - * resources to complete and should be - * retried - */ -#define EV_ENOMEM 5 /* There was insufficient memory to - * complete the operation */ -#define EV_EFAULT 6 /* Bad guest address */ -#define EV_ENODEV 7 /* No such device */ -#define EV_EINVAL 8 /* An argument supplied to the hcall - was out of range or invalid */ -#define EV_INTERNAL 9 /* An internal error occurred */ -#define EV_CONFIG 10 /* A configuration error was detected */ -#define EV_INVALID_STATE 11 /* The object is in an invalid state */ -#define EV_UNIMPLEMENTED 12 /* Unimplemented hypercall */ -#define EV_BUFFER_OVERFLOW 13 /* Caller-supplied buffer too small */ - -#endif /* _ASM_POWERPC_EPAPR_HCALLS_H */ diff --git a/linux-headers/asm-powerpc/kvm_para.h b/linux-headers/asm-powerpc/kvm_para.h deleted file mode 100644 index 9beb49cc10..0000000000 --- a/linux-headers/asm-powerpc/kvm_para.h +++ /dev/null @@ -1,98 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * 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, write to the Free Software - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -#ifndef __POWERPC_KVM_PARA_H__ -#define __POWERPC_KVM_PARA_H__ - -#include - -/* - * Additions to this struct must only occur at the end, and should be - * accompanied by a KVM_MAGIC_FEAT flag to advertise that they are present - * (albeit not necessarily relevant to the current target hardware platform). - * - * Struct fields are always 32 or 64 bit aligned, depending on them being 32 - * or 64 bit wide respectively. - * - * See Documentation/virtual/kvm/ppc-pv.txt - */ -struct kvm_vcpu_arch_shared { - __u64 scratch1; - __u64 scratch2; - __u64 scratch3; - __u64 critical; /* Guest may not get interrupts if == r1 */ - __u64 sprg0; - __u64 sprg1; - __u64 sprg2; - __u64 sprg3; - __u64 srr0; - __u64 srr1; - __u64 dar; /* dear on BookE */ - __u64 msr; - __u32 dsisr; - __u32 int_pending; /* Tells the guest if we have an interrupt */ - __u32 sr[16]; - __u32 mas0; - __u32 mas1; - __u64 mas7_3; - __u64 mas2; - __u32 mas4; - __u32 mas6; - __u32 esr; - __u32 pir; - - /* - * SPRG4-7 are user-readable, so we can only keep these consistent - * between the shared area and the real registers when there's an - * intervening exit to KVM. This also applies to SPRG3 on some - * chips. - * - * This suffices for access by guest userspace, since in PR-mode - * KVM, an exit must occur when changing the guest's MSR[PR]. - * If the guest kernel writes to SPRG3-7 via the shared area, it - * must also use the shared area for reading while in kernel space. - */ - __u64 sprg4; - __u64 sprg5; - __u64 sprg6; - __u64 sprg7; -}; - -#define KVM_SC_MAGIC_R0 0x4b564d21 /* "KVM!" */ - -#define KVM_HCALL_TOKEN(num) _EV_HCALL_TOKEN(EV_KVM_VENDOR_ID, num) - -#include - -#define KVM_FEATURE_MAGIC_PAGE 1 - -/* Magic page flags from host to guest */ - -#define KVM_MAGIC_FEAT_SR (1 << 0) - -/* MASn, ESR, PIR, and high SPRGs */ -#define KVM_MAGIC_FEAT_MAS0_TO_SPRG7 (1 << 1) - -/* Magic page flags from guest to host */ - -#define MAGIC_PAGE_FLAG_NOT_MAPPED_NX (1 << 0) - - -#endif /* __POWERPC_KVM_PARA_H__ */ diff --git a/linux-headers/asm-s390/kvm_para.h b/linux-headers/asm-s390/kvm_para.h deleted file mode 100644 index b9ab584adf..0000000000 --- a/linux-headers/asm-s390/kvm_para.h +++ /dev/null @@ -1,8 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * User API definitions for paravirtual devices on s390 - * - * Copyright IBM Corp. 2008 - * - * Author(s): Christian Borntraeger - */ diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h deleted file mode 100644 index 4c58184395..0000000000 --- a/linux-headers/asm-x86/kvm_para.h +++ /dev/null @@ -1,118 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#ifndef _ASM_X86_KVM_PARA_H -#define _ASM_X86_KVM_PARA_H - -#include -#include - -/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It - * should be used to determine that a VM is running under KVM. - */ -#define KVM_CPUID_SIGNATURE 0x40000000 - -/* This CPUID returns a feature bitmap in eax. Before enabling a particular - * paravirtualization, the appropriate feature bit should be checked. - */ -#define KVM_CPUID_FEATURES 0x40000001 -#define KVM_FEATURE_CLOCKSOURCE 0 -#define KVM_FEATURE_NOP_IO_DELAY 1 -#define KVM_FEATURE_MMU_OP 2 -/* This indicates that the new set of kvmclock msrs - * are available. The use of 0x11 and 0x12 is deprecated - */ -#define KVM_FEATURE_CLOCKSOURCE2 3 -#define KVM_FEATURE_ASYNC_PF 4 -#define KVM_FEATURE_STEAL_TIME 5 -#define KVM_FEATURE_PV_EOI 6 -#define KVM_FEATURE_PV_UNHALT 7 -#define KVM_FEATURE_PV_TLB_FLUSH 9 -#define KVM_FEATURE_ASYNC_PF_VMEXIT 10 - -/* The last 8 bits are used to indicate how to interpret the flags field - * in pvclock structure. If no bits are set, all flags are ignored. - */ -#define KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24 - -#define MSR_KVM_WALL_CLOCK 0x11 -#define MSR_KVM_SYSTEM_TIME 0x12 - -#define KVM_MSR_ENABLED 1 -/* Custom MSRs falls in the range 0x4b564d00-0x4b564dff */ -#define MSR_KVM_WALL_CLOCK_NEW 0x4b564d00 -#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01 -#define MSR_KVM_ASYNC_PF_EN 0x4b564d02 -#define MSR_KVM_STEAL_TIME 0x4b564d03 -#define MSR_KVM_PV_EOI_EN 0x4b564d04 - -struct kvm_steal_time { - __u64 steal; - __u32 version; - __u32 flags; - __u8 preempted; - __u8 u8_pad[3]; - __u32 pad[11]; -}; - -#define KVM_VCPU_PREEMPTED (1 << 0) -#define KVM_VCPU_FLUSH_TLB (1 << 1) - -#define KVM_CLOCK_PAIRING_WALLCLOCK 0 -struct kvm_clock_pairing { - __s64 sec; - __s64 nsec; - __u64 tsc; - __u32 flags; - __u32 pad[9]; -}; - -#define KVM_STEAL_ALIGNMENT_BITS 5 -#define KVM_STEAL_VALID_BITS ((-1ULL << (KVM_STEAL_ALIGNMENT_BITS + 1))) -#define KVM_STEAL_RESERVED_MASK (((1 << KVM_STEAL_ALIGNMENT_BITS) - 1 ) << 1) - -#define KVM_MAX_MMU_OP_BATCH 32 - -#define KVM_ASYNC_PF_ENABLED (1 << 0) -#define KVM_ASYNC_PF_SEND_ALWAYS (1 << 1) -#define KVM_ASYNC_PF_DELIVERY_AS_PF_VMEXIT (1 << 2) - -/* Operations for KVM_HC_MMU_OP */ -#define KVM_MMU_OP_WRITE_PTE 1 -#define KVM_MMU_OP_FLUSH_TLB 2 -#define KVM_MMU_OP_RELEASE_PT 3 - -/* Payload for KVM_HC_MMU_OP */ -struct kvm_mmu_op_header { - __u32 op; - __u32 pad; -}; - -struct kvm_mmu_op_write_pte { - struct kvm_mmu_op_header header; - __u64 pte_phys; - __u64 pte_val; -}; - -struct kvm_mmu_op_flush_tlb { - struct kvm_mmu_op_header header; -}; - -struct kvm_mmu_op_release_pt { - struct kvm_mmu_op_header header; - __u64 pt_phys; -}; - -#define KVM_PV_REASON_PAGE_NOT_PRESENT 1 -#define KVM_PV_REASON_PAGE_READY 2 - -struct kvm_vcpu_pv_apf_data { - __u32 reason; - __u8 pad[60]; - __u32 enabled; -}; - -#define KVM_PV_EOI_BIT 0 -#define KVM_PV_EOI_MASK (0x1 << KVM_PV_EOI_BIT) -#define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK -#define KVM_PV_EOI_DISABLED 0x0 - -#endif /* _ASM_X86_KVM_PARA_H */ diff --git a/linux-headers/linux/kvm_para.h b/linux-headers/linux/kvm_para.h deleted file mode 100644 index 8bcd0aa853..0000000000 --- a/linux-headers/linux/kvm_para.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#ifndef __LINUX_KVM_PARA_H -#define __LINUX_KVM_PARA_H - -/* - * This header file provides a method for making a hypercall to the host - * Architectures should define: - * - kvm_hypercall0, kvm_hypercall1... - * - kvm_arch_para_features - * - kvm_para_available - */ - -/* Return values for hypercalls */ -#define KVM_ENOSYS 1000 -#define KVM_EFAULT EFAULT -#define KVM_E2BIG E2BIG -#define KVM_EPERM EPERM -#define KVM_EOPNOTSUPP 95 - -#define KVM_HC_VAPIC_POLL_IRQ 1 -#define KVM_HC_MMU_OP 2 -#define KVM_HC_FEATURES 3 -#define KVM_HC_PPC_MAP_MAGIC_PAGE 4 -#define KVM_HC_KICK_CPU 5 -#define KVM_HC_MIPS_GET_CLOCK_FREQ 6 -#define KVM_HC_MIPS_EXIT_VM 7 -#define KVM_HC_MIPS_CONSOLE_OUTPUT 8 -#define KVM_HC_CLOCK_PAIRING 9 - -/* - * hypercalls use architecture specific - */ -#include - -#endif /* __LINUX_KVM_PARA_H */ From 9882d3ef3c43fc89a0e1d43d7b52720092bef29b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 20 Mar 2018 23:01:04 +0200 Subject: [PATCH 0670/2380] update-linux-headers.sh: unistd.h, kvm consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework the update script slightly, add the unistd.h header and its dependencies on all architectures. This also removes the IA64 and MIPS from a KVM blacklist: Linux dropped IA64, and there was never a reason to exclude MIPS from kvm specifically - it was excluded due to dependency of its unistd.h on sgidefs.h, which we also import. Cc: Dr. David Alan Gilbert Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé --- scripts/update-linux-headers.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 4fa08d38c8..947dec2852 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -84,11 +84,6 @@ for arch in $ARCHLIST; do continue fi - # Blacklist architectures which have KVM headers but are actually dead - if [ "$arch" = "ia64" -o "$arch" = "mips" ]; then - continue - fi - if [ "$arch" = x86 ]; then arch_var=SRCARCH else @@ -99,9 +94,14 @@ for arch in $ARCHLIST; do rm -rf "$output/linux-headers/asm-$arch" mkdir -p "$output/linux-headers/asm-$arch" - for header in kvm.h unistd.h; do + for header in kvm.h unistd.h bitsperlong.h; do cp "$tmpdir/include/asm/$header" "$output/linux-headers/asm-$arch" done + + if [ $arch = mips ]; then + cp "$tmpdir/include/asm/sgidefs.h" "$output/linux-headers/asm-mips/" + fi + rm -rf "$output/include/standard-headers/asm-$arch" mkdir -p "$output/include/standard-headers/asm-$arch" if [ $arch = s390 ]; then @@ -129,6 +129,12 @@ for header in kvm.h vfio.h vfio_ccw.h vhost.h \ cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done +rm -rf "$output/linux-headers/asm-generic" +mkdir -p "$output/linux-headers/asm-generic" +for header in unistd.h bitsperlong.h; do + cp "$tmpdir/include/asm-generic/$header" "$output/linux-headers/asm-generic" +done + if [ -L "$linux/source" ]; then cp "$linux/source/COPYING" "$output/linux-headers" else From 43370036476707479a03a90e7e1c526dffbaad4b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 20 Mar 2018 23:01:05 +0200 Subject: [PATCH 0671/2380] linux-headers: add unistd.h on all arches This adds unistd.h on ARM64 and MIPS and their dependencies. Updated to Linux 4.17-rc2. Signed-off-by: Michael S. Tsirkin --- linux-headers/asm-arm/bitsperlong.h | 1 + linux-headers/asm-arm64/bitsperlong.h | 24 + linux-headers/asm-generic/bitsperlong.h | 16 + linux-headers/asm-generic/unistd.h | 781 ++++++++++++++++++++++++ linux-headers/asm-mips/bitsperlong.h | 9 + linux-headers/asm-mips/sgidefs.h | 45 ++ linux-headers/asm-mips/unistd.h | 44 +- linux-headers/asm-powerpc/bitsperlong.h | 13 + linux-headers/asm-s390/bitsperlong.h | 14 + linux-headers/asm-s390/unistd_32.h | 1 + linux-headers/asm-s390/unistd_64.h | 1 + linux-headers/asm-x86/bitsperlong.h | 14 + 12 files changed, 957 insertions(+), 6 deletions(-) create mode 100644 linux-headers/asm-arm/bitsperlong.h create mode 100644 linux-headers/asm-arm64/bitsperlong.h create mode 100644 linux-headers/asm-generic/bitsperlong.h create mode 100644 linux-headers/asm-generic/unistd.h create mode 100644 linux-headers/asm-mips/bitsperlong.h create mode 100644 linux-headers/asm-mips/sgidefs.h create mode 100644 linux-headers/asm-powerpc/bitsperlong.h create mode 100644 linux-headers/asm-s390/bitsperlong.h create mode 100644 linux-headers/asm-x86/bitsperlong.h diff --git a/linux-headers/asm-arm/bitsperlong.h b/linux-headers/asm-arm/bitsperlong.h new file mode 100644 index 0000000000..6dc0bb0c13 --- /dev/null +++ b/linux-headers/asm-arm/bitsperlong.h @@ -0,0 +1 @@ +#include diff --git a/linux-headers/asm-arm64/bitsperlong.h b/linux-headers/asm-arm64/bitsperlong.h new file mode 100644 index 0000000000..485d60bee2 --- /dev/null +++ b/linux-headers/asm-arm64/bitsperlong.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2012 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ +#ifndef __ASM_BITSPERLONG_H +#define __ASM_BITSPERLONG_H + +#define __BITS_PER_LONG 64 + +#include + +#endif /* __ASM_BITSPERLONG_H */ diff --git a/linux-headers/asm-generic/bitsperlong.h b/linux-headers/asm-generic/bitsperlong.h new file mode 100644 index 0000000000..0aac245b6b --- /dev/null +++ b/linux-headers/asm-generic/bitsperlong.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_GENERIC_BITS_PER_LONG +#define __ASM_GENERIC_BITS_PER_LONG + +/* + * There seems to be no way of detecting this automatically from user + * space, so 64 bit architectures should override this in their + * bitsperlong.h. In particular, an architecture that supports + * both 32 and 64 bit user space must not rely on CONFIG_64BIT + * to decide it, but rather check a compiler provided macro. + */ +#ifndef __BITS_PER_LONG +#define __BITS_PER_LONG 32 +#endif + +#endif /* __ASM_GENERIC_BITS_PER_LONG */ diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h new file mode 100644 index 0000000000..8bcb186c6f --- /dev/null +++ b/linux-headers/asm-generic/unistd.h @@ -0,0 +1,781 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#include + +/* + * This file contains the system call numbers, based on the + * layout of the x86-64 architecture, which embeds the + * pointer to the syscall in the table. + * + * As a basic principle, no duplication of functionality + * should be added, e.g. we don't use lseek when llseek + * is present. New architectures should use this file + * and implement the less feature-full calls in user space. + */ + +#ifndef __SYSCALL +#define __SYSCALL(x, y) +#endif + +#if __BITS_PER_LONG == 32 || defined(__SYSCALL_COMPAT) +#define __SC_3264(_nr, _32, _64) __SYSCALL(_nr, _32) +#else +#define __SC_3264(_nr, _32, _64) __SYSCALL(_nr, _64) +#endif + +#ifdef __SYSCALL_COMPAT +#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _comp) +#define __SC_COMP_3264(_nr, _32, _64, _comp) __SYSCALL(_nr, _comp) +#else +#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _sys) +#define __SC_COMP_3264(_nr, _32, _64, _comp) __SC_3264(_nr, _32, _64) +#endif + +#define __NR_io_setup 0 +__SC_COMP(__NR_io_setup, sys_io_setup, compat_sys_io_setup) +#define __NR_io_destroy 1 +__SYSCALL(__NR_io_destroy, sys_io_destroy) +#define __NR_io_submit 2 +__SC_COMP(__NR_io_submit, sys_io_submit, compat_sys_io_submit) +#define __NR_io_cancel 3 +__SYSCALL(__NR_io_cancel, sys_io_cancel) +#define __NR_io_getevents 4 +__SC_COMP(__NR_io_getevents, sys_io_getevents, compat_sys_io_getevents) + +/* fs/xattr.c */ +#define __NR_setxattr 5 +__SYSCALL(__NR_setxattr, sys_setxattr) +#define __NR_lsetxattr 6 +__SYSCALL(__NR_lsetxattr, sys_lsetxattr) +#define __NR_fsetxattr 7 +__SYSCALL(__NR_fsetxattr, sys_fsetxattr) +#define __NR_getxattr 8 +__SYSCALL(__NR_getxattr, sys_getxattr) +#define __NR_lgetxattr 9 +__SYSCALL(__NR_lgetxattr, sys_lgetxattr) +#define __NR_fgetxattr 10 +__SYSCALL(__NR_fgetxattr, sys_fgetxattr) +#define __NR_listxattr 11 +__SYSCALL(__NR_listxattr, sys_listxattr) +#define __NR_llistxattr 12 +__SYSCALL(__NR_llistxattr, sys_llistxattr) +#define __NR_flistxattr 13 +__SYSCALL(__NR_flistxattr, sys_flistxattr) +#define __NR_removexattr 14 +__SYSCALL(__NR_removexattr, sys_removexattr) +#define __NR_lremovexattr 15 +__SYSCALL(__NR_lremovexattr, sys_lremovexattr) +#define __NR_fremovexattr 16 +__SYSCALL(__NR_fremovexattr, sys_fremovexattr) + +/* fs/dcache.c */ +#define __NR_getcwd 17 +__SYSCALL(__NR_getcwd, sys_getcwd) + +/* fs/cookies.c */ +#define __NR_lookup_dcookie 18 +__SC_COMP(__NR_lookup_dcookie, sys_lookup_dcookie, compat_sys_lookup_dcookie) + +/* fs/eventfd.c */ +#define __NR_eventfd2 19 +__SYSCALL(__NR_eventfd2, sys_eventfd2) + +/* fs/eventpoll.c */ +#define __NR_epoll_create1 20 +__SYSCALL(__NR_epoll_create1, sys_epoll_create1) +#define __NR_epoll_ctl 21 +__SYSCALL(__NR_epoll_ctl, sys_epoll_ctl) +#define __NR_epoll_pwait 22 +__SC_COMP(__NR_epoll_pwait, sys_epoll_pwait, compat_sys_epoll_pwait) + +/* fs/fcntl.c */ +#define __NR_dup 23 +__SYSCALL(__NR_dup, sys_dup) +#define __NR_dup3 24 +__SYSCALL(__NR_dup3, sys_dup3) +#define __NR3264_fcntl 25 +__SC_COMP_3264(__NR3264_fcntl, sys_fcntl64, sys_fcntl, compat_sys_fcntl64) + +/* fs/inotify_user.c */ +#define __NR_inotify_init1 26 +__SYSCALL(__NR_inotify_init1, sys_inotify_init1) +#define __NR_inotify_add_watch 27 +__SYSCALL(__NR_inotify_add_watch, sys_inotify_add_watch) +#define __NR_inotify_rm_watch 28 +__SYSCALL(__NR_inotify_rm_watch, sys_inotify_rm_watch) + +/* fs/ioctl.c */ +#define __NR_ioctl 29 +__SC_COMP(__NR_ioctl, sys_ioctl, compat_sys_ioctl) + +/* fs/ioprio.c */ +#define __NR_ioprio_set 30 +__SYSCALL(__NR_ioprio_set, sys_ioprio_set) +#define __NR_ioprio_get 31 +__SYSCALL(__NR_ioprio_get, sys_ioprio_get) + +/* fs/locks.c */ +#define __NR_flock 32 +__SYSCALL(__NR_flock, sys_flock) + +/* fs/namei.c */ +#define __NR_mknodat 33 +__SYSCALL(__NR_mknodat, sys_mknodat) +#define __NR_mkdirat 34 +__SYSCALL(__NR_mkdirat, sys_mkdirat) +#define __NR_unlinkat 35 +__SYSCALL(__NR_unlinkat, sys_unlinkat) +#define __NR_symlinkat 36 +__SYSCALL(__NR_symlinkat, sys_symlinkat) +#define __NR_linkat 37 +__SYSCALL(__NR_linkat, sys_linkat) +#ifdef __ARCH_WANT_RENAMEAT +/* renameat is superseded with flags by renameat2 */ +#define __NR_renameat 38 +__SYSCALL(__NR_renameat, sys_renameat) +#endif /* __ARCH_WANT_RENAMEAT */ + +/* fs/namespace.c */ +#define __NR_umount2 39 +__SYSCALL(__NR_umount2, sys_umount) +#define __NR_mount 40 +__SC_COMP(__NR_mount, sys_mount, compat_sys_mount) +#define __NR_pivot_root 41 +__SYSCALL(__NR_pivot_root, sys_pivot_root) + +/* fs/nfsctl.c */ +#define __NR_nfsservctl 42 +__SYSCALL(__NR_nfsservctl, sys_ni_syscall) + +/* fs/open.c */ +#define __NR3264_statfs 43 +__SC_COMP_3264(__NR3264_statfs, sys_statfs64, sys_statfs, \ + compat_sys_statfs64) +#define __NR3264_fstatfs 44 +__SC_COMP_3264(__NR3264_fstatfs, sys_fstatfs64, sys_fstatfs, \ + compat_sys_fstatfs64) +#define __NR3264_truncate 45 +__SC_COMP_3264(__NR3264_truncate, sys_truncate64, sys_truncate, \ + compat_sys_truncate64) +#define __NR3264_ftruncate 46 +__SC_COMP_3264(__NR3264_ftruncate, sys_ftruncate64, sys_ftruncate, \ + compat_sys_ftruncate64) + +#define __NR_fallocate 47 +__SC_COMP(__NR_fallocate, sys_fallocate, compat_sys_fallocate) +#define __NR_faccessat 48 +__SYSCALL(__NR_faccessat, sys_faccessat) +#define __NR_chdir 49 +__SYSCALL(__NR_chdir, sys_chdir) +#define __NR_fchdir 50 +__SYSCALL(__NR_fchdir, sys_fchdir) +#define __NR_chroot 51 +__SYSCALL(__NR_chroot, sys_chroot) +#define __NR_fchmod 52 +__SYSCALL(__NR_fchmod, sys_fchmod) +#define __NR_fchmodat 53 +__SYSCALL(__NR_fchmodat, sys_fchmodat) +#define __NR_fchownat 54 +__SYSCALL(__NR_fchownat, sys_fchownat) +#define __NR_fchown 55 +__SYSCALL(__NR_fchown, sys_fchown) +#define __NR_openat 56 +__SC_COMP(__NR_openat, sys_openat, compat_sys_openat) +#define __NR_close 57 +__SYSCALL(__NR_close, sys_close) +#define __NR_vhangup 58 +__SYSCALL(__NR_vhangup, sys_vhangup) + +/* fs/pipe.c */ +#define __NR_pipe2 59 +__SYSCALL(__NR_pipe2, sys_pipe2) + +/* fs/quota.c */ +#define __NR_quotactl 60 +__SYSCALL(__NR_quotactl, sys_quotactl) + +/* fs/readdir.c */ +#define __NR_getdents64 61 +__SYSCALL(__NR_getdents64, sys_getdents64) + +/* fs/read_write.c */ +#define __NR3264_lseek 62 +__SC_3264(__NR3264_lseek, sys_llseek, sys_lseek) +#define __NR_read 63 +__SYSCALL(__NR_read, sys_read) +#define __NR_write 64 +__SYSCALL(__NR_write, sys_write) +#define __NR_readv 65 +__SC_COMP(__NR_readv, sys_readv, compat_sys_readv) +#define __NR_writev 66 +__SC_COMP(__NR_writev, sys_writev, compat_sys_writev) +#define __NR_pread64 67 +__SC_COMP(__NR_pread64, sys_pread64, compat_sys_pread64) +#define __NR_pwrite64 68 +__SC_COMP(__NR_pwrite64, sys_pwrite64, compat_sys_pwrite64) +#define __NR_preadv 69 +__SC_COMP(__NR_preadv, sys_preadv, compat_sys_preadv) +#define __NR_pwritev 70 +__SC_COMP(__NR_pwritev, sys_pwritev, compat_sys_pwritev) + +/* fs/sendfile.c */ +#define __NR3264_sendfile 71 +__SYSCALL(__NR3264_sendfile, sys_sendfile64) + +/* fs/select.c */ +#define __NR_pselect6 72 +__SC_COMP(__NR_pselect6, sys_pselect6, compat_sys_pselect6) +#define __NR_ppoll 73 +__SC_COMP(__NR_ppoll, sys_ppoll, compat_sys_ppoll) + +/* fs/signalfd.c */ +#define __NR_signalfd4 74 +__SC_COMP(__NR_signalfd4, sys_signalfd4, compat_sys_signalfd4) + +/* fs/splice.c */ +#define __NR_vmsplice 75 +__SC_COMP(__NR_vmsplice, sys_vmsplice, compat_sys_vmsplice) +#define __NR_splice 76 +__SYSCALL(__NR_splice, sys_splice) +#define __NR_tee 77 +__SYSCALL(__NR_tee, sys_tee) + +/* fs/stat.c */ +#define __NR_readlinkat 78 +__SYSCALL(__NR_readlinkat, sys_readlinkat) +#define __NR3264_fstatat 79 +__SC_3264(__NR3264_fstatat, sys_fstatat64, sys_newfstatat) +#define __NR3264_fstat 80 +__SC_3264(__NR3264_fstat, sys_fstat64, sys_newfstat) + +/* fs/sync.c */ +#define __NR_sync 81 +__SYSCALL(__NR_sync, sys_sync) +#define __NR_fsync 82 +__SYSCALL(__NR_fsync, sys_fsync) +#define __NR_fdatasync 83 +__SYSCALL(__NR_fdatasync, sys_fdatasync) +#ifdef __ARCH_WANT_SYNC_FILE_RANGE2 +#define __NR_sync_file_range2 84 +__SC_COMP(__NR_sync_file_range2, sys_sync_file_range2, \ + compat_sys_sync_file_range2) +#else +#define __NR_sync_file_range 84 +__SC_COMP(__NR_sync_file_range, sys_sync_file_range, \ + compat_sys_sync_file_range) +#endif + +/* fs/timerfd.c */ +#define __NR_timerfd_create 85 +__SYSCALL(__NR_timerfd_create, sys_timerfd_create) +#define __NR_timerfd_settime 86 +__SC_COMP(__NR_timerfd_settime, sys_timerfd_settime, \ + compat_sys_timerfd_settime) +#define __NR_timerfd_gettime 87 +__SC_COMP(__NR_timerfd_gettime, sys_timerfd_gettime, \ + compat_sys_timerfd_gettime) + +/* fs/utimes.c */ +#define __NR_utimensat 88 +__SC_COMP(__NR_utimensat, sys_utimensat, compat_sys_utimensat) + +/* kernel/acct.c */ +#define __NR_acct 89 +__SYSCALL(__NR_acct, sys_acct) + +/* kernel/capability.c */ +#define __NR_capget 90 +__SYSCALL(__NR_capget, sys_capget) +#define __NR_capset 91 +__SYSCALL(__NR_capset, sys_capset) + +/* kernel/exec_domain.c */ +#define __NR_personality 92 +__SYSCALL(__NR_personality, sys_personality) + +/* kernel/exit.c */ +#define __NR_exit 93 +__SYSCALL(__NR_exit, sys_exit) +#define __NR_exit_group 94 +__SYSCALL(__NR_exit_group, sys_exit_group) +#define __NR_waitid 95 +__SC_COMP(__NR_waitid, sys_waitid, compat_sys_waitid) + +/* kernel/fork.c */ +#define __NR_set_tid_address 96 +__SYSCALL(__NR_set_tid_address, sys_set_tid_address) +#define __NR_unshare 97 +__SYSCALL(__NR_unshare, sys_unshare) + +/* kernel/futex.c */ +#define __NR_futex 98 +__SC_COMP(__NR_futex, sys_futex, compat_sys_futex) +#define __NR_set_robust_list 99 +__SC_COMP(__NR_set_robust_list, sys_set_robust_list, \ + compat_sys_set_robust_list) +#define __NR_get_robust_list 100 +__SC_COMP(__NR_get_robust_list, sys_get_robust_list, \ + compat_sys_get_robust_list) + +/* kernel/hrtimer.c */ +#define __NR_nanosleep 101 +__SC_COMP(__NR_nanosleep, sys_nanosleep, compat_sys_nanosleep) + +/* kernel/itimer.c */ +#define __NR_getitimer 102 +__SC_COMP(__NR_getitimer, sys_getitimer, compat_sys_getitimer) +#define __NR_setitimer 103 +__SC_COMP(__NR_setitimer, sys_setitimer, compat_sys_setitimer) + +/* kernel/kexec.c */ +#define __NR_kexec_load 104 +__SC_COMP(__NR_kexec_load, sys_kexec_load, compat_sys_kexec_load) + +/* kernel/module.c */ +#define __NR_init_module 105 +__SYSCALL(__NR_init_module, sys_init_module) +#define __NR_delete_module 106 +__SYSCALL(__NR_delete_module, sys_delete_module) + +/* kernel/posix-timers.c */ +#define __NR_timer_create 107 +__SC_COMP(__NR_timer_create, sys_timer_create, compat_sys_timer_create) +#define __NR_timer_gettime 108 +__SC_COMP(__NR_timer_gettime, sys_timer_gettime, compat_sys_timer_gettime) +#define __NR_timer_getoverrun 109 +__SYSCALL(__NR_timer_getoverrun, sys_timer_getoverrun) +#define __NR_timer_settime 110 +__SC_COMP(__NR_timer_settime, sys_timer_settime, compat_sys_timer_settime) +#define __NR_timer_delete 111 +__SYSCALL(__NR_timer_delete, sys_timer_delete) +#define __NR_clock_settime 112 +__SC_COMP(__NR_clock_settime, sys_clock_settime, compat_sys_clock_settime) +#define __NR_clock_gettime 113 +__SC_COMP(__NR_clock_gettime, sys_clock_gettime, compat_sys_clock_gettime) +#define __NR_clock_getres 114 +__SC_COMP(__NR_clock_getres, sys_clock_getres, compat_sys_clock_getres) +#define __NR_clock_nanosleep 115 +__SC_COMP(__NR_clock_nanosleep, sys_clock_nanosleep, \ + compat_sys_clock_nanosleep) + +/* kernel/printk.c */ +#define __NR_syslog 116 +__SYSCALL(__NR_syslog, sys_syslog) + +/* kernel/ptrace.c */ +#define __NR_ptrace 117 +__SYSCALL(__NR_ptrace, sys_ptrace) + +/* kernel/sched/core.c */ +#define __NR_sched_setparam 118 +__SYSCALL(__NR_sched_setparam, sys_sched_setparam) +#define __NR_sched_setscheduler 119 +__SYSCALL(__NR_sched_setscheduler, sys_sched_setscheduler) +#define __NR_sched_getscheduler 120 +__SYSCALL(__NR_sched_getscheduler, sys_sched_getscheduler) +#define __NR_sched_getparam 121 +__SYSCALL(__NR_sched_getparam, sys_sched_getparam) +#define __NR_sched_setaffinity 122 +__SC_COMP(__NR_sched_setaffinity, sys_sched_setaffinity, \ + compat_sys_sched_setaffinity) +#define __NR_sched_getaffinity 123 +__SC_COMP(__NR_sched_getaffinity, sys_sched_getaffinity, \ + compat_sys_sched_getaffinity) +#define __NR_sched_yield 124 +__SYSCALL(__NR_sched_yield, sys_sched_yield) +#define __NR_sched_get_priority_max 125 +__SYSCALL(__NR_sched_get_priority_max, sys_sched_get_priority_max) +#define __NR_sched_get_priority_min 126 +__SYSCALL(__NR_sched_get_priority_min, sys_sched_get_priority_min) +#define __NR_sched_rr_get_interval 127 +__SC_COMP(__NR_sched_rr_get_interval, sys_sched_rr_get_interval, \ + compat_sys_sched_rr_get_interval) + +/* kernel/signal.c */ +#define __NR_restart_syscall 128 +__SYSCALL(__NR_restart_syscall, sys_restart_syscall) +#define __NR_kill 129 +__SYSCALL(__NR_kill, sys_kill) +#define __NR_tkill 130 +__SYSCALL(__NR_tkill, sys_tkill) +#define __NR_tgkill 131 +__SYSCALL(__NR_tgkill, sys_tgkill) +#define __NR_sigaltstack 132 +__SC_COMP(__NR_sigaltstack, sys_sigaltstack, compat_sys_sigaltstack) +#define __NR_rt_sigsuspend 133 +__SC_COMP(__NR_rt_sigsuspend, sys_rt_sigsuspend, compat_sys_rt_sigsuspend) +#define __NR_rt_sigaction 134 +__SC_COMP(__NR_rt_sigaction, sys_rt_sigaction, compat_sys_rt_sigaction) +#define __NR_rt_sigprocmask 135 +__SC_COMP(__NR_rt_sigprocmask, sys_rt_sigprocmask, compat_sys_rt_sigprocmask) +#define __NR_rt_sigpending 136 +__SC_COMP(__NR_rt_sigpending, sys_rt_sigpending, compat_sys_rt_sigpending) +#define __NR_rt_sigtimedwait 137 +__SC_COMP(__NR_rt_sigtimedwait, sys_rt_sigtimedwait, \ + compat_sys_rt_sigtimedwait) +#define __NR_rt_sigqueueinfo 138 +__SC_COMP(__NR_rt_sigqueueinfo, sys_rt_sigqueueinfo, \ + compat_sys_rt_sigqueueinfo) +#define __NR_rt_sigreturn 139 +__SC_COMP(__NR_rt_sigreturn, sys_rt_sigreturn, compat_sys_rt_sigreturn) + +/* kernel/sys.c */ +#define __NR_setpriority 140 +__SYSCALL(__NR_setpriority, sys_setpriority) +#define __NR_getpriority 141 +__SYSCALL(__NR_getpriority, sys_getpriority) +#define __NR_reboot 142 +__SYSCALL(__NR_reboot, sys_reboot) +#define __NR_setregid 143 +__SYSCALL(__NR_setregid, sys_setregid) +#define __NR_setgid 144 +__SYSCALL(__NR_setgid, sys_setgid) +#define __NR_setreuid 145 +__SYSCALL(__NR_setreuid, sys_setreuid) +#define __NR_setuid 146 +__SYSCALL(__NR_setuid, sys_setuid) +#define __NR_setresuid 147 +__SYSCALL(__NR_setresuid, sys_setresuid) +#define __NR_getresuid 148 +__SYSCALL(__NR_getresuid, sys_getresuid) +#define __NR_setresgid 149 +__SYSCALL(__NR_setresgid, sys_setresgid) +#define __NR_getresgid 150 +__SYSCALL(__NR_getresgid, sys_getresgid) +#define __NR_setfsuid 151 +__SYSCALL(__NR_setfsuid, sys_setfsuid) +#define __NR_setfsgid 152 +__SYSCALL(__NR_setfsgid, sys_setfsgid) +#define __NR_times 153 +__SC_COMP(__NR_times, sys_times, compat_sys_times) +#define __NR_setpgid 154 +__SYSCALL(__NR_setpgid, sys_setpgid) +#define __NR_getpgid 155 +__SYSCALL(__NR_getpgid, sys_getpgid) +#define __NR_getsid 156 +__SYSCALL(__NR_getsid, sys_getsid) +#define __NR_setsid 157 +__SYSCALL(__NR_setsid, sys_setsid) +#define __NR_getgroups 158 +__SYSCALL(__NR_getgroups, sys_getgroups) +#define __NR_setgroups 159 +__SYSCALL(__NR_setgroups, sys_setgroups) +#define __NR_uname 160 +__SYSCALL(__NR_uname, sys_newuname) +#define __NR_sethostname 161 +__SYSCALL(__NR_sethostname, sys_sethostname) +#define __NR_setdomainname 162 +__SYSCALL(__NR_setdomainname, sys_setdomainname) +#define __NR_getrlimit 163 +__SC_COMP(__NR_getrlimit, sys_getrlimit, compat_sys_getrlimit) +#define __NR_setrlimit 164 +__SC_COMP(__NR_setrlimit, sys_setrlimit, compat_sys_setrlimit) +#define __NR_getrusage 165 +__SC_COMP(__NR_getrusage, sys_getrusage, compat_sys_getrusage) +#define __NR_umask 166 +__SYSCALL(__NR_umask, sys_umask) +#define __NR_prctl 167 +__SYSCALL(__NR_prctl, sys_prctl) +#define __NR_getcpu 168 +__SYSCALL(__NR_getcpu, sys_getcpu) + +/* kernel/time.c */ +#define __NR_gettimeofday 169 +__SC_COMP(__NR_gettimeofday, sys_gettimeofday, compat_sys_gettimeofday) +#define __NR_settimeofday 170 +__SC_COMP(__NR_settimeofday, sys_settimeofday, compat_sys_settimeofday) +#define __NR_adjtimex 171 +__SC_COMP(__NR_adjtimex, sys_adjtimex, compat_sys_adjtimex) + +/* kernel/timer.c */ +#define __NR_getpid 172 +__SYSCALL(__NR_getpid, sys_getpid) +#define __NR_getppid 173 +__SYSCALL(__NR_getppid, sys_getppid) +#define __NR_getuid 174 +__SYSCALL(__NR_getuid, sys_getuid) +#define __NR_geteuid 175 +__SYSCALL(__NR_geteuid, sys_geteuid) +#define __NR_getgid 176 +__SYSCALL(__NR_getgid, sys_getgid) +#define __NR_getegid 177 +__SYSCALL(__NR_getegid, sys_getegid) +#define __NR_gettid 178 +__SYSCALL(__NR_gettid, sys_gettid) +#define __NR_sysinfo 179 +__SC_COMP(__NR_sysinfo, sys_sysinfo, compat_sys_sysinfo) + +/* ipc/mqueue.c */ +#define __NR_mq_open 180 +__SC_COMP(__NR_mq_open, sys_mq_open, compat_sys_mq_open) +#define __NR_mq_unlink 181 +__SYSCALL(__NR_mq_unlink, sys_mq_unlink) +#define __NR_mq_timedsend 182 +__SC_COMP(__NR_mq_timedsend, sys_mq_timedsend, compat_sys_mq_timedsend) +#define __NR_mq_timedreceive 183 +__SC_COMP(__NR_mq_timedreceive, sys_mq_timedreceive, \ + compat_sys_mq_timedreceive) +#define __NR_mq_notify 184 +__SC_COMP(__NR_mq_notify, sys_mq_notify, compat_sys_mq_notify) +#define __NR_mq_getsetattr 185 +__SC_COMP(__NR_mq_getsetattr, sys_mq_getsetattr, compat_sys_mq_getsetattr) + +/* ipc/msg.c */ +#define __NR_msgget 186 +__SYSCALL(__NR_msgget, sys_msgget) +#define __NR_msgctl 187 +__SC_COMP(__NR_msgctl, sys_msgctl, compat_sys_msgctl) +#define __NR_msgrcv 188 +__SC_COMP(__NR_msgrcv, sys_msgrcv, compat_sys_msgrcv) +#define __NR_msgsnd 189 +__SC_COMP(__NR_msgsnd, sys_msgsnd, compat_sys_msgsnd) + +/* ipc/sem.c */ +#define __NR_semget 190 +__SYSCALL(__NR_semget, sys_semget) +#define __NR_semctl 191 +__SC_COMP(__NR_semctl, sys_semctl, compat_sys_semctl) +#define __NR_semtimedop 192 +__SC_COMP(__NR_semtimedop, sys_semtimedop, compat_sys_semtimedop) +#define __NR_semop 193 +__SYSCALL(__NR_semop, sys_semop) + +/* ipc/shm.c */ +#define __NR_shmget 194 +__SYSCALL(__NR_shmget, sys_shmget) +#define __NR_shmctl 195 +__SC_COMP(__NR_shmctl, sys_shmctl, compat_sys_shmctl) +#define __NR_shmat 196 +__SC_COMP(__NR_shmat, sys_shmat, compat_sys_shmat) +#define __NR_shmdt 197 +__SYSCALL(__NR_shmdt, sys_shmdt) + +/* net/socket.c */ +#define __NR_socket 198 +__SYSCALL(__NR_socket, sys_socket) +#define __NR_socketpair 199 +__SYSCALL(__NR_socketpair, sys_socketpair) +#define __NR_bind 200 +__SYSCALL(__NR_bind, sys_bind) +#define __NR_listen 201 +__SYSCALL(__NR_listen, sys_listen) +#define __NR_accept 202 +__SYSCALL(__NR_accept, sys_accept) +#define __NR_connect 203 +__SYSCALL(__NR_connect, sys_connect) +#define __NR_getsockname 204 +__SYSCALL(__NR_getsockname, sys_getsockname) +#define __NR_getpeername 205 +__SYSCALL(__NR_getpeername, sys_getpeername) +#define __NR_sendto 206 +__SYSCALL(__NR_sendto, sys_sendto) +#define __NR_recvfrom 207 +__SC_COMP(__NR_recvfrom, sys_recvfrom, compat_sys_recvfrom) +#define __NR_setsockopt 208 +__SC_COMP(__NR_setsockopt, sys_setsockopt, compat_sys_setsockopt) +#define __NR_getsockopt 209 +__SC_COMP(__NR_getsockopt, sys_getsockopt, compat_sys_getsockopt) +#define __NR_shutdown 210 +__SYSCALL(__NR_shutdown, sys_shutdown) +#define __NR_sendmsg 211 +__SC_COMP(__NR_sendmsg, sys_sendmsg, compat_sys_sendmsg) +#define __NR_recvmsg 212 +__SC_COMP(__NR_recvmsg, sys_recvmsg, compat_sys_recvmsg) + +/* mm/filemap.c */ +#define __NR_readahead 213 +__SC_COMP(__NR_readahead, sys_readahead, compat_sys_readahead) + +/* mm/nommu.c, also with MMU */ +#define __NR_brk 214 +__SYSCALL(__NR_brk, sys_brk) +#define __NR_munmap 215 +__SYSCALL(__NR_munmap, sys_munmap) +#define __NR_mremap 216 +__SYSCALL(__NR_mremap, sys_mremap) + +/* security/keys/keyctl.c */ +#define __NR_add_key 217 +__SYSCALL(__NR_add_key, sys_add_key) +#define __NR_request_key 218 +__SYSCALL(__NR_request_key, sys_request_key) +#define __NR_keyctl 219 +__SC_COMP(__NR_keyctl, sys_keyctl, compat_sys_keyctl) + +/* arch/example/kernel/sys_example.c */ +#define __NR_clone 220 +__SYSCALL(__NR_clone, sys_clone) +#define __NR_execve 221 +__SC_COMP(__NR_execve, sys_execve, compat_sys_execve) + +#define __NR3264_mmap 222 +__SC_3264(__NR3264_mmap, sys_mmap2, sys_mmap) +/* mm/fadvise.c */ +#define __NR3264_fadvise64 223 +__SC_COMP(__NR3264_fadvise64, sys_fadvise64_64, compat_sys_fadvise64_64) + +/* mm/, CONFIG_MMU only */ +#ifndef __ARCH_NOMMU +#define __NR_swapon 224 +__SYSCALL(__NR_swapon, sys_swapon) +#define __NR_swapoff 225 +__SYSCALL(__NR_swapoff, sys_swapoff) +#define __NR_mprotect 226 +__SYSCALL(__NR_mprotect, sys_mprotect) +#define __NR_msync 227 +__SYSCALL(__NR_msync, sys_msync) +#define __NR_mlock 228 +__SYSCALL(__NR_mlock, sys_mlock) +#define __NR_munlock 229 +__SYSCALL(__NR_munlock, sys_munlock) +#define __NR_mlockall 230 +__SYSCALL(__NR_mlockall, sys_mlockall) +#define __NR_munlockall 231 +__SYSCALL(__NR_munlockall, sys_munlockall) +#define __NR_mincore 232 +__SYSCALL(__NR_mincore, sys_mincore) +#define __NR_madvise 233 +__SYSCALL(__NR_madvise, sys_madvise) +#define __NR_remap_file_pages 234 +__SYSCALL(__NR_remap_file_pages, sys_remap_file_pages) +#define __NR_mbind 235 +__SC_COMP(__NR_mbind, sys_mbind, compat_sys_mbind) +#define __NR_get_mempolicy 236 +__SC_COMP(__NR_get_mempolicy, sys_get_mempolicy, compat_sys_get_mempolicy) +#define __NR_set_mempolicy 237 +__SC_COMP(__NR_set_mempolicy, sys_set_mempolicy, compat_sys_set_mempolicy) +#define __NR_migrate_pages 238 +__SC_COMP(__NR_migrate_pages, sys_migrate_pages, compat_sys_migrate_pages) +#define __NR_move_pages 239 +__SC_COMP(__NR_move_pages, sys_move_pages, compat_sys_move_pages) +#endif + +#define __NR_rt_tgsigqueueinfo 240 +__SC_COMP(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo, \ + compat_sys_rt_tgsigqueueinfo) +#define __NR_perf_event_open 241 +__SYSCALL(__NR_perf_event_open, sys_perf_event_open) +#define __NR_accept4 242 +__SYSCALL(__NR_accept4, sys_accept4) +#define __NR_recvmmsg 243 +__SC_COMP(__NR_recvmmsg, sys_recvmmsg, compat_sys_recvmmsg) + +/* + * Architectures may provide up to 16 syscalls of their own + * starting with this value. + */ +#define __NR_arch_specific_syscall 244 + +#define __NR_wait4 260 +__SC_COMP(__NR_wait4, sys_wait4, compat_sys_wait4) +#define __NR_prlimit64 261 +__SYSCALL(__NR_prlimit64, sys_prlimit64) +#define __NR_fanotify_init 262 +__SYSCALL(__NR_fanotify_init, sys_fanotify_init) +#define __NR_fanotify_mark 263 +__SYSCALL(__NR_fanotify_mark, sys_fanotify_mark) +#define __NR_name_to_handle_at 264 +__SYSCALL(__NR_name_to_handle_at, sys_name_to_handle_at) +#define __NR_open_by_handle_at 265 +__SC_COMP(__NR_open_by_handle_at, sys_open_by_handle_at, \ + compat_sys_open_by_handle_at) +#define __NR_clock_adjtime 266 +__SC_COMP(__NR_clock_adjtime, sys_clock_adjtime, compat_sys_clock_adjtime) +#define __NR_syncfs 267 +__SYSCALL(__NR_syncfs, sys_syncfs) +#define __NR_setns 268 +__SYSCALL(__NR_setns, sys_setns) +#define __NR_sendmmsg 269 +__SC_COMP(__NR_sendmmsg, sys_sendmmsg, compat_sys_sendmmsg) +#define __NR_process_vm_readv 270 +__SC_COMP(__NR_process_vm_readv, sys_process_vm_readv, \ + compat_sys_process_vm_readv) +#define __NR_process_vm_writev 271 +__SC_COMP(__NR_process_vm_writev, sys_process_vm_writev, \ + compat_sys_process_vm_writev) +#define __NR_kcmp 272 +__SYSCALL(__NR_kcmp, sys_kcmp) +#define __NR_finit_module 273 +__SYSCALL(__NR_finit_module, sys_finit_module) +#define __NR_sched_setattr 274 +__SYSCALL(__NR_sched_setattr, sys_sched_setattr) +#define __NR_sched_getattr 275 +__SYSCALL(__NR_sched_getattr, sys_sched_getattr) +#define __NR_renameat2 276 +__SYSCALL(__NR_renameat2, sys_renameat2) +#define __NR_seccomp 277 +__SYSCALL(__NR_seccomp, sys_seccomp) +#define __NR_getrandom 278 +__SYSCALL(__NR_getrandom, sys_getrandom) +#define __NR_memfd_create 279 +__SYSCALL(__NR_memfd_create, sys_memfd_create) +#define __NR_bpf 280 +__SYSCALL(__NR_bpf, sys_bpf) +#define __NR_execveat 281 +__SC_COMP(__NR_execveat, sys_execveat, compat_sys_execveat) +#define __NR_userfaultfd 282 +__SYSCALL(__NR_userfaultfd, sys_userfaultfd) +#define __NR_membarrier 283 +__SYSCALL(__NR_membarrier, sys_membarrier) +#define __NR_mlock2 284 +__SYSCALL(__NR_mlock2, sys_mlock2) +#define __NR_copy_file_range 285 +__SYSCALL(__NR_copy_file_range, sys_copy_file_range) +#define __NR_preadv2 286 +__SC_COMP(__NR_preadv2, sys_preadv2, compat_sys_preadv2) +#define __NR_pwritev2 287 +__SC_COMP(__NR_pwritev2, sys_pwritev2, compat_sys_pwritev2) +#define __NR_pkey_mprotect 288 +__SYSCALL(__NR_pkey_mprotect, sys_pkey_mprotect) +#define __NR_pkey_alloc 289 +__SYSCALL(__NR_pkey_alloc, sys_pkey_alloc) +#define __NR_pkey_free 290 +__SYSCALL(__NR_pkey_free, sys_pkey_free) +#define __NR_statx 291 +__SYSCALL(__NR_statx, sys_statx) + +#undef __NR_syscalls +#define __NR_syscalls 292 + +/* + * 32 bit systems traditionally used different + * syscalls for off_t and loff_t arguments, while + * 64 bit systems only need the off_t version. + * For new 32 bit platforms, there is no need to + * implement the old 32 bit off_t syscalls, so + * they take different names. + * Here we map the numbers so that both versions + * use the same syscall table layout. + */ +#if __BITS_PER_LONG == 64 && !defined(__SYSCALL_COMPAT) +#define __NR_fcntl __NR3264_fcntl +#define __NR_statfs __NR3264_statfs +#define __NR_fstatfs __NR3264_fstatfs +#define __NR_truncate __NR3264_truncate +#define __NR_ftruncate __NR3264_ftruncate +#define __NR_lseek __NR3264_lseek +#define __NR_sendfile __NR3264_sendfile +#define __NR_newfstatat __NR3264_fstatat +#define __NR_fstat __NR3264_fstat +#define __NR_mmap __NR3264_mmap +#define __NR_fadvise64 __NR3264_fadvise64 +#ifdef __NR3264_stat +#define __NR_stat __NR3264_stat +#define __NR_lstat __NR3264_lstat +#endif +#else +#define __NR_fcntl64 __NR3264_fcntl +#define __NR_statfs64 __NR3264_statfs +#define __NR_fstatfs64 __NR3264_fstatfs +#define __NR_truncate64 __NR3264_truncate +#define __NR_ftruncate64 __NR3264_ftruncate +#define __NR_llseek __NR3264_lseek +#define __NR_sendfile64 __NR3264_sendfile +#define __NR_fstatat64 __NR3264_fstatat +#define __NR_fstat64 __NR3264_fstat +#define __NR_mmap2 __NR3264_mmap +#define __NR_fadvise64_64 __NR3264_fadvise64 +#ifdef __NR3264_stat +#define __NR_stat64 __NR3264_stat +#define __NR_lstat64 __NR3264_lstat +#endif +#endif diff --git a/linux-headers/asm-mips/bitsperlong.h b/linux-headers/asm-mips/bitsperlong.h new file mode 100644 index 0000000000..7268380d8d --- /dev/null +++ b/linux-headers/asm-mips/bitsperlong.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_MIPS_BITSPERLONG_H +#define __ASM_MIPS_BITSPERLONG_H + +#define __BITS_PER_LONG _MIPS_SZLONG + +#include + +#endif /* __ASM_MIPS_BITSPERLONG_H */ diff --git a/linux-headers/asm-mips/sgidefs.h b/linux-headers/asm-mips/sgidefs.h new file mode 100644 index 0000000000..26143e3b7c --- /dev/null +++ b/linux-headers/asm-mips/sgidefs.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996, 1999, 2001 Ralf Baechle + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#ifndef __ASM_SGIDEFS_H +#define __ASM_SGIDEFS_H + +/* + * Using a Linux compiler for building Linux seems logic but not to + * everybody. + */ +#ifndef __linux__ +#error Use a Linux compiler or give up. +#endif + +/* + * Definitions for the ISA levels + * + * With the introduction of MIPS32 / MIPS64 instruction sets definitions + * MIPS ISAs are no longer subsets of each other. Therefore comparisons + * on these symbols except with == may result in unexpected results and + * are forbidden! + */ +#define _MIPS_ISA_MIPS1 1 +#define _MIPS_ISA_MIPS2 2 +#define _MIPS_ISA_MIPS3 3 +#define _MIPS_ISA_MIPS4 4 +#define _MIPS_ISA_MIPS5 5 +#define _MIPS_ISA_MIPS32 6 +#define _MIPS_ISA_MIPS64 7 + +/* + * Subprogram calling convention + */ +#define _MIPS_SIM_ABI32 1 +#define _MIPS_SIM_NABI32 2 +#define _MIPS_SIM_ABI64 3 + +#endif /* __ASM_SGIDEFS_H */ diff --git a/linux-headers/asm-mips/unistd.h b/linux-headers/asm-mips/unistd.h index 2a2020938e..9bfef7f764 100644 --- a/linux-headers/asm-mips/unistd.h +++ b/linux-headers/asm-mips/unistd.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -377,16 +378,27 @@ #define __NR_memfd_create (__NR_Linux + 354) #define __NR_bpf (__NR_Linux + 355) #define __NR_execveat (__NR_Linux + 356) +#define __NR_userfaultfd (__NR_Linux + 357) +#define __NR_membarrier (__NR_Linux + 358) +#define __NR_mlock2 (__NR_Linux + 359) +#define __NR_copy_file_range (__NR_Linux + 360) +#define __NR_preadv2 (__NR_Linux + 361) +#define __NR_pwritev2 (__NR_Linux + 362) +#define __NR_pkey_mprotect (__NR_Linux + 363) +#define __NR_pkey_alloc (__NR_Linux + 364) +#define __NR_pkey_free (__NR_Linux + 365) +#define __NR_statx (__NR_Linux + 366) + /* * Offset of the last Linux o32 flavoured syscall */ -#define __NR_Linux_syscalls 356 +#define __NR_Linux_syscalls 366 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ #define __NR_O32_Linux 4000 -#define __NR_O32_Linux_syscalls 356 +#define __NR_O32_Linux_syscalls 366 #if _MIPS_SIM == _MIPS_SIM_ABI64 @@ -711,16 +723,26 @@ #define __NR_memfd_create (__NR_Linux + 314) #define __NR_bpf (__NR_Linux + 315) #define __NR_execveat (__NR_Linux + 316) +#define __NR_userfaultfd (__NR_Linux + 317) +#define __NR_membarrier (__NR_Linux + 318) +#define __NR_mlock2 (__NR_Linux + 319) +#define __NR_copy_file_range (__NR_Linux + 320) +#define __NR_preadv2 (__NR_Linux + 321) +#define __NR_pwritev2 (__NR_Linux + 322) +#define __NR_pkey_mprotect (__NR_Linux + 323) +#define __NR_pkey_alloc (__NR_Linux + 324) +#define __NR_pkey_free (__NR_Linux + 325) +#define __NR_statx (__NR_Linux + 326) /* * Offset of the last Linux 64-bit flavoured syscall */ -#define __NR_Linux_syscalls 316 +#define __NR_Linux_syscalls 326 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ #define __NR_64_Linux 5000 -#define __NR_64_Linux_syscalls 316 +#define __NR_64_Linux_syscalls 326 #if _MIPS_SIM == _MIPS_SIM_NABI32 @@ -1049,15 +1071,25 @@ #define __NR_memfd_create (__NR_Linux + 318) #define __NR_bpf (__NR_Linux + 319) #define __NR_execveat (__NR_Linux + 320) +#define __NR_userfaultfd (__NR_Linux + 321) +#define __NR_membarrier (__NR_Linux + 322) +#define __NR_mlock2 (__NR_Linux + 323) +#define __NR_copy_file_range (__NR_Linux + 324) +#define __NR_preadv2 (__NR_Linux + 325) +#define __NR_pwritev2 (__NR_Linux + 326) +#define __NR_pkey_mprotect (__NR_Linux + 327) +#define __NR_pkey_alloc (__NR_Linux + 328) +#define __NR_pkey_free (__NR_Linux + 329) +#define __NR_statx (__NR_Linux + 330) /* * Offset of the last N32 flavoured syscall */ -#define __NR_Linux_syscalls 320 +#define __NR_Linux_syscalls 330 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ #define __NR_N32_Linux 6000 -#define __NR_N32_Linux_syscalls 320 +#define __NR_N32_Linux_syscalls 330 #endif /* _ASM_UNISTD_H */ diff --git a/linux-headers/asm-powerpc/bitsperlong.h b/linux-headers/asm-powerpc/bitsperlong.h new file mode 100644 index 0000000000..46ece3ecff --- /dev/null +++ b/linux-headers/asm-powerpc/bitsperlong.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_POWERPC_BITSPERLONG_H +#define __ASM_POWERPC_BITSPERLONG_H + +#if defined(__powerpc64__) +# define __BITS_PER_LONG 64 +#else +# define __BITS_PER_LONG 32 +#endif + +#include + +#endif /* __ASM_POWERPC_BITSPERLONG_H */ diff --git a/linux-headers/asm-s390/bitsperlong.h b/linux-headers/asm-s390/bitsperlong.h new file mode 100644 index 0000000000..cceaf47b02 --- /dev/null +++ b/linux-headers/asm-s390/bitsperlong.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_S390_BITSPERLONG_H +#define __ASM_S390_BITSPERLONG_H + +#ifndef __s390x__ +#define __BITS_PER_LONG 32 +#else +#define __BITS_PER_LONG 64 +#endif + +#include + +#endif /* __ASM_S390_BITSPERLONG_H */ + diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index 1ae66a263b..d0f97cd0a4 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -360,5 +360,6 @@ #define __NR_s390_guarded_storage 378 #define __NR_statx 379 #define __NR_s390_sthyi 380 +#define __NR_kexec_file_load 381 #endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h index 8aa9d046a9..23ffb97746 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -327,5 +327,6 @@ #define __NR_s390_guarded_storage 378 #define __NR_statx 379 #define __NR_s390_sthyi 380 +#define __NR_kexec_file_load 381 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/bitsperlong.h b/linux-headers/asm-x86/bitsperlong.h new file mode 100644 index 0000000000..5d72c84588 --- /dev/null +++ b/linux-headers/asm-x86/bitsperlong.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_X86_BITSPERLONG_H +#define __ASM_X86_BITSPERLONG_H + +#if defined(__x86_64__) && !defined(__ILP32__) +# define __BITS_PER_LONG 64 +#else +# define __BITS_PER_LONG 32 +#endif + +#include + +#endif /* __ASM_X86_BITSPERLONG_H */ + From 85e99cf80bec66adf39f948db824a0216daaa486 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 20 Mar 2018 23:01:06 +0200 Subject: [PATCH 0672/2380] linux-headers: add kvm header for mips kvm header for MIPS was manually excluded from auto-updates. Update it now to 4.17-rc2. Signed-off-by: Michael S. Tsirkin --- linux-headers/asm-mips/kvm.h | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/linux-headers/asm-mips/kvm.h b/linux-headers/asm-mips/kvm.h index 6985eb59b0..edcf717c43 100644 --- a/linux-headers/asm-mips/kvm.h +++ b/linux-headers/asm-mips/kvm.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -19,6 +20,10 @@ * Some parts derived from the x86 version of this file. */ +#define __KVM_HAVE_READONLY_MEM + +#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + /* * for KVM_GET_REGS and KVM_SET_REGS * @@ -52,9 +57,14 @@ struct kvm_fpu { * Register set = 0: GP registers from kvm_regs (see definitions below). * * Register set = 1: CP0 registers. - * bits[15..8] - Must be zero. - * bits[7..3] - Register 'rd' index. - * bits[2..0] - Register 'sel' index. + * bits[15..8] - COP0 register set. + * + * COP0 register set = 0: Main CP0 registers. + * bits[7..3] - Register 'rd' index. + * bits[2..0] - Register 'sel' index. + * + * COP0 register set = 1: MAARs. + * bits[7..0] - MAAR index. * * Register set = 2: KVM specific registers (see definitions below). * @@ -112,6 +122,15 @@ struct kvm_fpu { #define KVM_REG_MIPS_PC (KVM_REG_MIPS_GP | KVM_REG_SIZE_U64 | 34) +/* + * KVM_REG_MIPS_CP0 - Coprocessor 0 registers. + */ + +#define KVM_REG_MIPS_MAAR (KVM_REG_MIPS_CP0 | (1 << 8)) +#define KVM_REG_MIPS_CP0_MAAR(n) (KVM_REG_MIPS_MAAR | \ + KVM_REG_SIZE_U64 | (n)) + + /* * KVM_REG_MIPS_KVM - KVM specific control registers. */ From 703878e2e0975123b1a89a006c0204c469333278 Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Thu, 12 Apr 2018 23:12:27 +0800 Subject: [PATCH 0673/2380] vhost-user: add Net prefix to internal state structure We are going to introduce a shared vhost user state which will be named as 'VhostUserState'. So add 'Net' prefix to the existing internal state structure in the vhost-user netdev to avoid conflict. Signed-off-by: Tiwei Bie Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-user.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/net/vhost-user.c b/net/vhost-user.c index e0f16c895b..fa28aad12d 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -20,38 +20,38 @@ #include "qemu/option.h" #include "trace.h" -typedef struct VhostUserState { +typedef struct NetVhostUserState { NetClientState nc; CharBackend chr; /* only queue index 0 */ VHostNetState *vhost_net; guint watch; uint64_t acked_features; bool started; -} VhostUserState; +} NetVhostUserState; VHostNetState *vhost_user_get_vhost_net(NetClientState *nc) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); return s->vhost_net; } uint64_t vhost_user_get_acked_features(NetClientState *nc) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); return s->acked_features; } static void vhost_user_stop(int queues, NetClientState *ncs[]) { - VhostUserState *s; + NetVhostUserState *s; int i; for (i = 0; i < queues; i++) { assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER); - s = DO_UPCAST(VhostUserState, nc, ncs[i]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[i]); if (s->vhost_net) { /* save acked features */ @@ -68,7 +68,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) { VhostNetOptions options; struct vhost_net *net = NULL; - VhostUserState *s; + NetVhostUserState *s; int max_queues; int i; @@ -77,7 +77,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) for (i = 0; i < queues; i++) { assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER); - s = DO_UPCAST(VhostUserState, nc, ncs[i]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[i]); options.net_backend = ncs[i]; options.opaque = be; @@ -123,7 +123,7 @@ static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, without GUEST_ANNOUNCE capability. */ if (size == 60) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); int r; static int display_rarp_failure = 1; char mac_addr[6]; @@ -146,7 +146,7 @@ static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, static void vhost_user_cleanup(NetClientState *nc) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); if (s->vhost_net) { vhost_net_cleanup(s->vhost_net); @@ -180,7 +180,7 @@ static bool vhost_user_has_ufo(NetClientState *nc) static NetClientInfo net_vhost_user_info = { .type = NET_CLIENT_DRIVER_VHOST_USER, - .size = sizeof(VhostUserState), + .size = sizeof(NetVhostUserState), .receive = vhost_user_receive, .cleanup = vhost_user_cleanup, .has_vnet_hdr = vhost_user_has_vnet_hdr, @@ -190,7 +190,7 @@ static NetClientInfo net_vhost_user_info = { static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond, void *opaque) { - VhostUserState *s = opaque; + NetVhostUserState *s = opaque; qemu_chr_fe_disconnect(&s->chr); @@ -203,7 +203,7 @@ static void chr_closed_bh(void *opaque) { const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; - VhostUserState *s; + NetVhostUserState *s; Error *err = NULL; int queues; @@ -212,7 +212,7 @@ static void chr_closed_bh(void *opaque) MAX_QUEUE_NUM); assert(queues < MAX_QUEUE_NUM); - s = DO_UPCAST(VhostUserState, nc, ncs[0]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[0]); qmp_set_link(name, false, &err); vhost_user_stop(queues, ncs); @@ -229,7 +229,7 @@ static void net_vhost_user_event(void *opaque, int event) { const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; - VhostUserState *s; + NetVhostUserState *s; Chardev *chr; Error *err = NULL; int queues; @@ -239,7 +239,7 @@ static void net_vhost_user_event(void *opaque, int event) MAX_QUEUE_NUM); assert(queues < MAX_QUEUE_NUM); - s = DO_UPCAST(VhostUserState, nc, ncs[0]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[0]); chr = qemu_chr_fe_get_driver(&s->chr); trace_vhost_user_event(chr->label, event); switch (event) { @@ -283,7 +283,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, { Error *err = NULL; NetClientState *nc, *nc0 = NULL; - VhostUserState *s; + NetVhostUserState *s; int i; assert(name); @@ -296,7 +296,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, nc->queue_index = i; if (!nc0) { nc0 = nc; - s = DO_UPCAST(VhostUserState, nc, nc); + s = DO_UPCAST(NetVhostUserState, nc, nc); if (!qemu_chr_fe_init(&s->chr, chr, &err)) { error_report_err(err); return -1; @@ -305,7 +305,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, } - s = DO_UPCAST(VhostUserState, nc, nc0); + s = DO_UPCAST(NetVhostUserState, nc, nc0); do { if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) { error_report_err(err); From 655ae6bb91998a01964759406cb38ef215a6ba5b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 17 May 2018 17:26:14 +0200 Subject: [PATCH 0674/2380] qemu-iotests: Fix paths for NFS Test cases were trying to use nfs:// URLs as local filenames, which made every test fail for NFS. With TEST_IMG and TEST_IMG_FILE set like for the other protocols, NFS tests can pass again. Signed-off-by: Kevin Wolf Reviewed-by: Fam Zheng --- tests/qemu-iotests/common.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 9a65a11026..cb5fa14e7f 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -147,8 +147,8 @@ else TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE" elif [ "$IMGPROTO" = "nfs" ]; then - TEST_DIR="nfs://127.0.0.1/$TEST_DIR" - TEST_IMG=$TEST_DIR/t.$IMGFMT + TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT + TEST_IMG="nfs://127.0.0.1$TEST_IMG_FILE" elif [ "$IMGPROTO" = "vxhs" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT TEST_IMG="vxhs://127.0.0.1:9999/t.$IMGFMT" From 8908b253c4ad5f8874c8d13abec169c696a5cd32 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 17 May 2018 18:33:52 +0200 Subject: [PATCH 0675/2380] qemu-iotests: Filter NFS paths NFS paths were only partially filtered in _filter_img_create, _img_info and _filter_img_info, resulting in "nfs://127.0.0.1TEST_DIR/t.IMGFMT". This adds another replacement to the sed calls that matches the test directory not as a host path, but as an NFS URL (the prefix as used for $TEST_IMG). Signed-off-by: Kevin Wolf Reviewed-by: Fam Zheng --- tests/qemu-iotests/126.out | 2 +- tests/qemu-iotests/common.filter | 6 ++++-- tests/qemu-iotests/common.rc | 8 +++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/126.out b/tests/qemu-iotests/126.out index 50d73080fa..17d03d5248 100644 --- a/tests/qemu-iotests/126.out +++ b/tests/qemu-iotests/126.out @@ -3,7 +3,7 @@ QA output created by 126 === Testing plain files === Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'file:TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864 === Testing relative backing filename resolution === diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index c5f4bcf578..f08ee55046 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -119,7 +119,8 @@ _filter_actual_image_size() # replace driver-specific options in the "Formatting..." line _filter_img_create() { - sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ + sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's#nbd:127.0.0.1:10810#TEST_DIR/t.IMGFMT#g' \ @@ -154,7 +155,8 @@ _filter_img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' - sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ + sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's#nbd://127.0.0.1:10810$#TEST_DIR/t.IMGFMT#g' \ diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index cb5fa14e7f..d054cb97fc 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -148,6 +148,7 @@ else TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE" elif [ "$IMGPROTO" = "nfs" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT + REMOTE_TEST_DIR="nfs://127.0.0.1$TEST_DIR" TEST_IMG="nfs://127.0.0.1$TEST_IMG_FILE" elif [ "$IMGPROTO" = "vxhs" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT @@ -173,6 +174,10 @@ if [ ! -d "$TEST_DIR" ]; then exit 1 fi +if [ -z "$REMOTE_TEST_DIR" ]; then + REMOTE_TEST_DIR="$TEST_DIR" +fi + if [ ! -d "$SAMPLE_IMG_DIR" ]; then echo "common.config: Error: \$SAMPLE_IMG_DIR ($SAMPLE_IMG_DIR) is not a directory" exit 1 @@ -333,7 +338,8 @@ _img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' $QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \ - sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ + sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e "/^disk size:/ D" \ From 4e24ed138b344f50a1b6ef59ad49b8a7bac15f1b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 17 May 2018 18:36:43 +0200 Subject: [PATCH 0676/2380] qemu-iotests: 086 doesn't work with NFS The reference output file only works for file. 'qemu-img convert -p' makes a lot more progress updates for NFS than for file, so disable the test for NFS. Signed-off-by: Kevin Wolf Reviewed-by: Fam Zheng --- tests/qemu-iotests/086 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086 index cd4494a660..84e3835071 100755 --- a/tests/qemu-iotests/086 +++ b/tests/qemu-iotests/086 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 raw -_supported_proto file nfs +_supported_proto file _supported_os Linux function run_qemu_img() From ae8622ec1971cd4a63db4da25ecffa9ba21f811c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 May 2018 19:17:17 +0100 Subject: [PATCH 0677/2380] sheepdog: Remove unnecessary NULL check in sd_prealloc() In commit 8b9ad56e9cbfd852a, we removed the code that could result in our getting to sd_prealloc()'s out_with_err_set label with a NULL blk pointer. That makes the NULL check in the error-handling path unnecessary, and Coverity gripes about it (CID 1390636). Delete the redundant check. Signed-off-by: Peter Maydell Signed-off-by: Kevin Wolf --- block/sheepdog.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/block/sheepdog.c b/block/sheepdog.c index 4237132419..2a5bc0a59a 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1859,9 +1859,7 @@ out: error_setg_errno(errp, -ret, "Can't pre-allocate"); } out_with_err_set: - if (blk) { - blk_unref(blk); - } + blk_unref(blk); g_free(buf); return ret; From c4d1ff2bfc3c40ce172ebc9a8860672ed97a3b4f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 23 May 2018 11:54:41 +0200 Subject: [PATCH 0678/2380] qemu-iotests: Add more tests to "migration" group grep for "migrate" turns up a few test cases which use migration, but haven't been in the "migration" group so far. Add them to the group. Signed-off-by: Kevin Wolf Reviewed-by: Juan Quintela --- tests/qemu-iotests/group | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index cc8cd8cc8e..60ba31b292 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -97,7 +97,7 @@ 088 rw auto quick 089 rw auto quick 090 rw auto quick -091 rw auto +091 rw auto migration 092 rw auto quick 093 auto 094 rw auto quick @@ -169,7 +169,7 @@ 162 auto quick 163 rw auto 165 rw auto quick -169 rw auto quick +169 rw auto quick migration 170 rw auto quick 171 rw auto quick 172 auto @@ -194,14 +194,14 @@ 192 rw auto quick 194 rw auto migration quick 195 rw auto quick -196 rw auto quick +196 rw auto quick migration 197 rw auto quick 198 rw auto -199 rw auto +199 rw auto migration 200 rw auto 201 rw auto migration 202 rw auto quick -203 rw auto +203 rw auto migration 204 rw auto quick 205 rw auto quick 206 rw auto From 169926dccfaa06f15a172331bcf0f13bd595e2e5 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 23 May 2018 11:56:07 +0200 Subject: [PATCH 0679/2380] qemu-iotests: Remove MIG_SOCKET from non-migration tests 185 and 191 define a MIG_SOCKET even though they don't do anything with migration. Remove the useless variable. Signed-off-by: Kevin Wolf Reviewed-by: Juan Quintela --- tests/qemu-iotests/185 | 2 -- tests/qemu-iotests/191 | 2 -- 2 files changed, 4 deletions(-) diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index 9a2d317414..deb42cc886 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -27,8 +27,6 @@ echo "QA output created by $seq" here=`pwd` status=1 # failure is the default! -MIG_SOCKET="${TEST_DIR}/migrate" - _cleanup() { rm -f "${TEST_IMG}.mid" diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 index dfad6555e4..77224eb151 100755 --- a/tests/qemu-iotests/191 +++ b/tests/qemu-iotests/191 @@ -27,8 +27,6 @@ echo "QA output created by $seq" here=`pwd` status=1 # failure is the default! -MIG_SOCKET="${TEST_DIR}/migrate" - _cleanup() { rm -f "${TEST_IMG}.mid" From cd44d96be90e7767c6fb8f33b90939eb58814956 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 16 May 2018 12:55:48 +0200 Subject: [PATCH 0680/2380] blockjob: Update block-job-pause/resume documentation Commit 0ec4dfb8d changed block-job_pause/resume so that they return an error if they don't do anything because the job is already paused/running. It forgot to update the documentation, so do that now. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: John Snow --- qapi/block-core.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 55728cb823..d32ec95666 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2338,8 +2338,7 @@ # # This command returns immediately after marking the active background block # operation for pausing. It is an error to call this command if no -# operation is in progress. Pausing an already paused job has no cumulative -# effect; a single block-job-resume command will resume the job. +# operation is in progress or if the job is already paused. # # The operation will pause as soon as possible. No event is emitted when # the operation is actually paused. Cancelling a paused job automatically @@ -2363,7 +2362,7 @@ # # This command returns immediately after resuming a paused background block # operation. It is an error to call this command if no operation is in -# progress. Resuming an already running job is not an error. +# progress or if the job is not paused. # # This command also clears the error status of the job. # From a81e0a825e3b89039a427bca037112f461b95fec Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 16 May 2018 13:04:34 +0200 Subject: [PATCH 0681/2380] blockjob: Improve BlockJobInfo.offset/len documentation Clarify that len is just an estimation of the end value of offset, and that offset increases monotonically while len can change arbitrarily. While touching the documentation of offset, move it directly after len to match the order of the declaration below. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: John Snow --- qapi/block-core.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index d32ec95666..0e29abf099 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1148,7 +1148,12 @@ # @device: The job identifier. Originally the device name but other # values are allowed since QEMU 2.7 # -# @len: the maximum progress value +# @len: Estimated @offset value at the completion of the job. This value can +# arbitrarily change while the job is running, in both directions. +# +# @offset: Progress made until now. The unit is arbitrary and the value can +# only meaningfully be used for the ratio of @offset to @len. The +# value is monotonically increasing. # # @busy: false if the job is known to be in a quiescent state, with # no pending I/O. Since 1.3. @@ -1156,8 +1161,6 @@ # @paused: whether the job is paused or, if @busy is true, will # pause itself as soon as possible. Since 1.3. # -# @offset: the current progress value -# # @speed: the rate limit, bytes per second # # @io-status: the status of the job (since 1.3) From 33e9e9bd62d9ae00880d9e12ad8a5b7d2c00e8a5 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 12 Apr 2018 17:29:59 +0200 Subject: [PATCH 0682/2380] job: Create Job, JobDriver and job_create() This is the first step towards creating an infrastructure for generic background jobs that aren't tied to a block device. For now, Job only stores its ID and JobDriver, the rest stays in BlockJob. The following patches will move over more parts of BlockJob to Job if they are meaningful outside the context of a block job. BlockJob.driver is now redundant, but this patch leaves it around to avoid unnecessary churn. The next patches will get rid of almost all of its uses anyway so that it can be removed later with much less churn. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- MAINTAINERS | 2 ++ Makefile.objs | 2 +- block/backup.c | 4 ++- block/commit.c | 4 ++- block/mirror.c | 10 ++++-- block/stream.c | 4 ++- blockjob.c | 46 +++++++++++++-------------- include/block/blockjob.h | 9 +++--- include/block/blockjob_int.h | 4 +-- include/qemu/job.h | 60 ++++++++++++++++++++++++++++++++++++ job.c | 48 +++++++++++++++++++++++++++++ tests/test-bdrv-drain.c | 4 ++- tests/test-blockjob-txn.c | 4 ++- tests/test-blockjob.c | 12 +++++--- 14 files changed, 169 insertions(+), 44 deletions(-) create mode 100644 include/qemu/job.h create mode 100644 job.c diff --git a/MAINTAINERS b/MAINTAINERS index e187b1f18f..9fba3307d9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1369,6 +1369,8 @@ L: qemu-block@nongnu.org S: Supported F: blockjob.c F: include/block/blockjob.h +F: job.c +F: include/block/job.h F: block/backup.c F: block/commit.c F: block/stream.c diff --git a/Makefile.objs b/Makefile.objs index c6c9b8fc21..92b73fc272 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -63,7 +63,7 @@ chardev-obj-y = chardev/ # block-obj-y is code used by both qemu system emulation and qemu-img block-obj-y += nbd/ -block-obj-y += block.o blockjob.o +block-obj-y += block.o blockjob.o job.o block-obj-y += block/ scsi/ block-obj-y += qemu-io-cmds.o block-obj-$(CONFIG_REPLICATION) += replication.o diff --git a/block/backup.c b/block/backup.c index e14d99560d..9e672bbd5e 100644 --- a/block/backup.c +++ b/block/backup.c @@ -523,7 +523,9 @@ static void coroutine_fn backup_run(void *opaque) } static const BlockJobDriver backup_job_driver = { - .instance_size = sizeof(BackupBlockJob), + .job_driver = { + .instance_size = sizeof(BackupBlockJob), + }, .job_type = BLOCK_JOB_TYPE_BACKUP, .start = backup_run, .commit = backup_commit, diff --git a/block/commit.c b/block/commit.c index ba5df6aa0a..18cbb2f9c4 100644 --- a/block/commit.c +++ b/block/commit.c @@ -215,7 +215,9 @@ out: } static const BlockJobDriver commit_job_driver = { - .instance_size = sizeof(CommitBlockJob), + .job_driver = { + .instance_size = sizeof(CommitBlockJob), + }, .job_type = BLOCK_JOB_TYPE_COMMIT, .start = commit_run, }; diff --git a/block/mirror.c b/block/mirror.c index a4197bb975..aa1d6b742e 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -913,7 +913,7 @@ static void mirror_complete(BlockJob *job, Error **errp) if (!s->synced) { error_setg(errp, "The active block job '%s' cannot be completed", - job->id); + job->job.id); return; } @@ -986,7 +986,9 @@ static void mirror_drain(BlockJob *job) } static const BlockJobDriver mirror_job_driver = { - .instance_size = sizeof(MirrorBlockJob), + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), + }, .job_type = BLOCK_JOB_TYPE_MIRROR, .start = mirror_run, .complete = mirror_complete, @@ -996,7 +998,9 @@ static const BlockJobDriver mirror_job_driver = { }; static const BlockJobDriver commit_active_job_driver = { - .instance_size = sizeof(MirrorBlockJob), + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), + }, .job_type = BLOCK_JOB_TYPE_COMMIT, .start = mirror_run, .complete = mirror_complete, diff --git a/block/stream.c b/block/stream.c index df9660d2fc..f88fc75141 100644 --- a/block/stream.c +++ b/block/stream.c @@ -209,7 +209,9 @@ out: } static const BlockJobDriver stream_job_driver = { - .instance_size = sizeof(StreamBlockJob), + .job_driver = { + .instance_size = sizeof(StreamBlockJob), + }, .job_type = BLOCK_JOB_TYPE_STREAM, .start = stream_run, }; diff --git a/blockjob.c b/blockjob.c index 112672a68b..1464856eb5 100644 --- a/blockjob.c +++ b/blockjob.c @@ -34,7 +34,6 @@ #include "qapi/qapi-events-block-core.h" #include "qapi/qmp/qerror.h" #include "qemu/coroutine.h" -#include "qemu/id.h" #include "qemu/timer.h" /* Right now, this mutex is only needed to synchronize accesses to job->busy @@ -92,7 +91,8 @@ static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp) return 0; } error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", - job->id, BlockJobStatus_str(job->status), BlockJobVerb_str(bv)); + job->job.id, BlockJobStatus_str(job->status), + BlockJobVerb_str(bv)); return -EPERM; } @@ -159,7 +159,7 @@ BlockJob *block_job_get(const char *id) BlockJob *job; QLIST_FOREACH(job, &block_jobs, job_list) { - if (job->id && !strcmp(id, job->id)) { + if (job->job.id && !strcmp(id, job->job.id)) { return job; } } @@ -261,7 +261,7 @@ void block_job_unref(BlockJob *job) block_job_detach_aio_context, job); blk_unref(job->blk); error_free(job->blocker); - g_free(job->id); + g_free(job->job.id); assert(!timer_pending(&job->sleep_timer)); g_free(job); } @@ -311,7 +311,7 @@ static char *child_job_get_parent_desc(BdrvChild *c) BlockJob *job = c->opaque; return g_strdup_printf("%s job '%s'", BlockJobType_str(job->driver->job_type), - job->id); + job->job.id); } static void child_job_drained_begin(BdrvChild *c) @@ -365,7 +365,7 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, bool block_job_is_internal(BlockJob *job) { - return (job->id == NULL); + return (job->job.id == NULL); } static bool block_job_started(BlockJob *job) @@ -705,13 +705,13 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) void block_job_complete(BlockJob *job, Error **errp) { /* Should not be reachable via external interface for internal jobs */ - assert(job->id); + assert(job->job.id); if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) { return; } if (job->pause_count || job->cancelled || !job->driver->complete) { error_setg(errp, "The active block job '%s' cannot be completed", - job->id); + job->job.id); return; } @@ -720,7 +720,7 @@ void block_job_complete(BlockJob *job, Error **errp) void block_job_finalize(BlockJob *job, Error **errp) { - assert(job && job->id); + assert(job && job->job.id); if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) { return; } @@ -731,7 +731,7 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) { BlockJob *job = *jobptr; /* similarly to _complete, this is QMP-interface only. */ - assert(job->id); + assert(job->job.id); if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) { return; } @@ -848,7 +848,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) } info = g_new0(BlockJobInfo, 1); info->type = g_strdup(BlockJobType_str(job->driver->job_type)); - info->device = g_strdup(job->id); + info->device = g_strdup(job->job.id); info->len = job->len; info->busy = atomic_read(&job->busy); info->paused = job->pause_count > 0; @@ -879,7 +879,7 @@ static void block_job_event_cancelled(BlockJob *job) } qapi_event_send_block_job_cancelled(job->driver->job_type, - job->id, + job->job.id, job->len, job->offset, job->speed, @@ -893,7 +893,7 @@ static void block_job_event_completed(BlockJob *job, const char *msg) } qapi_event_send_block_job_completed(job->driver->job_type, - job->id, + job->job.id, job->len, job->offset, job->speed, @@ -907,7 +907,7 @@ static int block_job_event_pending(BlockJob *job) block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING); if (!job->auto_finalize && !block_job_is_internal(job)) { qapi_event_send_block_job_pending(job->driver->job_type, - job->id, + job->job.id, &error_abort); } return 0; @@ -945,12 +945,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, error_setg(errp, "Cannot specify job ID for internal block job"); return NULL; } - - if (!id_wellformed(job_id)) { - error_setg(errp, "Invalid job ID '%s'", job_id); - return NULL; - } - if (block_job_get(job_id)) { error_setg(errp, "Job ID '%s' already in use", job_id); return NULL; @@ -964,9 +958,13 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return NULL; } - job = g_malloc0(driver->instance_size); + job = job_create(job_id, &driver->job_driver, errp); + if (job == NULL) { + blk_unref(blk); + return NULL; + } + job->driver = driver; - job->id = g_strdup(job_id); job->blk = blk; job->cb = cb; job->opaque = opaque; @@ -1187,7 +1185,7 @@ void block_job_event_ready(BlockJob *job) } qapi_event_send_block_job_ready(job->driver->job_type, - job->id, + job->job.id, job->len, job->offset, job->speed, &error_abort); @@ -1217,7 +1215,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, abort(); } if (!block_job_is_internal(job)) { - qapi_event_send_block_job_error(job->id, + qapi_event_send_block_job_error(job->job.id, is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE, action, &error_abort); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 0f56f723de..640e649034 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -26,6 +26,7 @@ #ifndef BLOCKJOB_H #define BLOCKJOB_H +#include "qemu/job.h" #include "block/block.h" #include "qemu/ratelimit.h" @@ -40,17 +41,15 @@ typedef struct BlockJobTxn BlockJobTxn; * Long-running operation on a BlockDriverState. */ typedef struct BlockJob { + /** Data belonging to the generic Job infrastructure */ + Job job; + /** The job type, including the job vtable. */ const BlockJobDriver *driver; /** The block device on which the job is operating. */ BlockBackend *blk; - /** - * The ID of the block job. May be NULL for internal jobs. - */ - char *id; - /** * The coroutine that executes the job. If not NULL, it is * reentered when busy is false and the job is cancelled. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 62ec964d09..e8eca44747 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -35,8 +35,8 @@ * A class type for block job driver. */ struct BlockJobDriver { - /** Derived BlockJob struct size */ - size_t instance_size; + /** Generic JobDriver callbacks and settings */ + JobDriver job_driver; /** String describing the operation, part of query-block-jobs QMP API */ BlockJobType job_type; diff --git a/include/qemu/job.h b/include/qemu/job.h new file mode 100644 index 0000000000..b4b49f19e1 --- /dev/null +++ b/include/qemu/job.h @@ -0,0 +1,60 @@ +/* + * Declarations for background jobs + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef JOB_H +#define JOB_H + +typedef struct JobDriver JobDriver; + +/** + * Long-running operation. + */ +typedef struct Job { + /** The ID of the job. May be NULL for internal jobs. */ + char *id; + + /** The type of this job. */ + const JobDriver *driver; +} Job; + +/** + * Callbacks and other information about a Job driver. + */ +struct JobDriver { + /** Derived Job struct size */ + size_t instance_size; +}; + + +/** + * Create a new long-running job and return it. + * + * @job_id: The id of the newly-created job, or %NULL for internal jobs + * @driver: The class object for the newly-created job. + * @errp: Error object. + */ +void *job_create(const char *job_id, const JobDriver *driver, Error **errp); + +#endif diff --git a/job.c b/job.c new file mode 100644 index 0000000000..87fd48468c --- /dev/null +++ b/job.c @@ -0,0 +1,48 @@ +/* + * Background jobs (long-running operations) + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * + * 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 "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "qemu/job.h" +#include "qemu/id.h" + +void *job_create(const char *job_id, const JobDriver *driver, Error **errp) +{ + Job *job; + + if (job_id) { + if (!id_wellformed(job_id)) { + error_setg(errp, "Invalid job ID '%s'", job_id); + return NULL; + } + } + + job = g_malloc0(driver->instance_size); + job->driver = driver; + job->id = g_strdup(job_id); + + return job; +} diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 7673de1062..fe9f412b39 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -520,7 +520,9 @@ static void test_job_complete(BlockJob *job, Error **errp) } BlockJobDriver test_job_driver = { - .instance_size = sizeof(TestBlockJob), + .job_driver = { + .instance_size = sizeof(TestBlockJob), + }, .start = test_job_start, .complete = test_job_complete, }; diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 5789893dda..48b12d1744 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -74,7 +74,9 @@ static void test_block_job_cb(void *opaque, int ret) } static const BlockJobDriver test_block_job_driver = { - .instance_size = sizeof(TestBlockJob), + .job_driver = { + .instance_size = sizeof(TestBlockJob), + }, .start = test_block_job_run, }; diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 8946bfd37b..b82026180a 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -17,7 +17,9 @@ #include "sysemu/block-backend.h" static const BlockJobDriver test_block_job_driver = { - .instance_size = sizeof(BlockJob), + .job_driver = { + .instance_size = sizeof(BlockJob), + }, }; static void block_job_cb(void *opaque, int ret) @@ -38,9 +40,9 @@ static BlockJob *mk_job(BlockBackend *blk, const char *id, g_assert_null(errp); g_assert_nonnull(job); if (id) { - g_assert_cmpstr(job->id, ==, id); + g_assert_cmpstr(job->job.id, ==, id); } else { - g_assert_cmpstr(job->id, ==, blk_name(blk)); + g_assert_cmpstr(job->job.id, ==, blk_name(blk)); } } else { g_assert_nonnull(errp); @@ -192,7 +194,9 @@ static void coroutine_fn cancel_job_start(void *opaque) } static const BlockJobDriver test_cancel_driver = { - .instance_size = sizeof(CancelJob), + .job_driver = { + .instance_size = sizeof(CancelJob), + }, .start = cancel_job_start, .complete = cancel_job_complete, }; From 8e4c87000fc515f8f65f7c8f18afb1e9270b11d6 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 12 Apr 2018 18:01:07 +0200 Subject: [PATCH 0683/2380] job: Rename BlockJobType into JobType QAPI types aren't externally visible, so we can rename them without causing problems. Before we add a job type to Job, rename the enum so it can be used for more than just block jobs. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 2 +- block/commit.c | 2 +- block/mirror.c | 4 ++-- block/stream.c | 2 +- blockjob.c | 6 +++--- include/block/blockjob_int.h | 2 +- qapi/block-core.json | 14 +++++++------- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/block/backup.c b/block/backup.c index 9e672bbd5e..c49ea92dca 100644 --- a/block/backup.c +++ b/block/backup.c @@ -526,7 +526,7 @@ static const BlockJobDriver backup_job_driver = { .job_driver = { .instance_size = sizeof(BackupBlockJob), }, - .job_type = BLOCK_JOB_TYPE_BACKUP, + .job_type = JOB_TYPE_BACKUP, .start = backup_run, .commit = backup_commit, .abort = backup_abort, diff --git a/block/commit.c b/block/commit.c index 18cbb2f9c4..afa2b2bacf 100644 --- a/block/commit.c +++ b/block/commit.c @@ -218,7 +218,7 @@ static const BlockJobDriver commit_job_driver = { .job_driver = { .instance_size = sizeof(CommitBlockJob), }, - .job_type = BLOCK_JOB_TYPE_COMMIT, + .job_type = JOB_TYPE_COMMIT, .start = commit_run, }; diff --git a/block/mirror.c b/block/mirror.c index aa1d6b742e..ed72656a23 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -989,7 +989,7 @@ static const BlockJobDriver mirror_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), }, - .job_type = BLOCK_JOB_TYPE_MIRROR, + .job_type = JOB_TYPE_MIRROR, .start = mirror_run, .complete = mirror_complete, .pause = mirror_pause, @@ -1001,7 +1001,7 @@ static const BlockJobDriver commit_active_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), }, - .job_type = BLOCK_JOB_TYPE_COMMIT, + .job_type = JOB_TYPE_COMMIT, .start = mirror_run, .complete = mirror_complete, .pause = mirror_pause, diff --git a/block/stream.c b/block/stream.c index f88fc75141..048bceb5d0 100644 --- a/block/stream.c +++ b/block/stream.c @@ -212,7 +212,7 @@ static const BlockJobDriver stream_job_driver = { .job_driver = { .instance_size = sizeof(StreamBlockJob), }, - .job_type = BLOCK_JOB_TYPE_STREAM, + .job_type = JOB_TYPE_STREAM, .start = stream_run, }; diff --git a/blockjob.c b/blockjob.c index 1464856eb5..2a3844721e 100644 --- a/blockjob.c +++ b/blockjob.c @@ -310,7 +310,7 @@ static char *child_job_get_parent_desc(BdrvChild *c) { BlockJob *job = c->opaque; return g_strdup_printf("%s job '%s'", - BlockJobType_str(job->driver->job_type), + JobType_str(job->driver->job_type), job->job.id); } @@ -847,7 +847,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) return NULL; } info = g_new0(BlockJobInfo, 1); - info->type = g_strdup(BlockJobType_str(job->driver->job_type)); + info->type = g_strdup(JobType_str(job->driver->job_type)); info->device = g_strdup(job->job.id); info->len = job->len; info->busy = atomic_read(&job->busy); @@ -980,7 +980,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, block_job_sleep_timer_cb, job); error_setg(&job->blocker, "block device is in use by block job: %s", - BlockJobType_str(driver->job_type)); + JobType_str(driver->job_type)); block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); bs->job = job; diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index e8eca44747..8e7e0a2f57 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -39,7 +39,7 @@ struct BlockJobDriver { JobDriver job_driver; /** String describing the operation, part of query-block-jobs QMP API */ - BlockJobType job_type; + JobType job_type; /** Mandatory: Entrypoint for the Coroutine. */ CoroutineEntry *start; diff --git a/qapi/block-core.json b/qapi/block-core.json index 0e29abf099..63c6011411 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1050,9 +1050,9 @@ 'data': ['top', 'full', 'none', 'incremental'] } ## -# @BlockJobType: +# @JobType: # -# Type of a block job. +# Type of a background job. # # @commit: block commit job type, see "block-commit" # @@ -1064,7 +1064,7 @@ # # Since: 1.7 ## -{ 'enum': 'BlockJobType', +{ 'enum': 'JobType', 'data': ['commit', 'stream', 'mirror', 'backup'] } ## @@ -4499,7 +4499,7 @@ # ## { 'event': 'BLOCK_JOB_COMPLETED', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'device': 'str', 'len' : 'int', 'offset': 'int', @@ -4535,7 +4535,7 @@ # ## { 'event': 'BLOCK_JOB_CANCELLED', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'device': 'str', 'len' : 'int', 'offset': 'int', @@ -4600,7 +4600,7 @@ # ## { 'event': 'BLOCK_JOB_READY', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'device': 'str', 'len' : 'int', 'offset': 'int', @@ -4627,7 +4627,7 @@ # ## { 'event': 'BLOCK_JOB_PENDING', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'id' : 'str' } } ## From 252291eaeafcd234a602d71cdf9415dbfc7bc867 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 12 Apr 2018 17:57:08 +0200 Subject: [PATCH 0684/2380] job: Add JobDriver.job_type This moves the job_type field from BlockJobDriver to JobDriver. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 2 +- block/commit.c | 2 +- block/mirror.c | 4 ++-- block/stream.c | 2 +- blockjob.c | 16 +++++++--------- include/block/blockjob_int.h | 3 --- include/qemu/job.h | 11 +++++++++++ job.c | 10 ++++++++++ 8 files changed, 33 insertions(+), 17 deletions(-) diff --git a/block/backup.c b/block/backup.c index c49ea92dca..baf8d432da 100644 --- a/block/backup.c +++ b/block/backup.c @@ -525,8 +525,8 @@ static void coroutine_fn backup_run(void *opaque) static const BlockJobDriver backup_job_driver = { .job_driver = { .instance_size = sizeof(BackupBlockJob), + .job_type = JOB_TYPE_BACKUP, }, - .job_type = JOB_TYPE_BACKUP, .start = backup_run, .commit = backup_commit, .abort = backup_abort, diff --git a/block/commit.c b/block/commit.c index afa2b2bacf..32d29c890e 100644 --- a/block/commit.c +++ b/block/commit.c @@ -217,8 +217,8 @@ out: static const BlockJobDriver commit_job_driver = { .job_driver = { .instance_size = sizeof(CommitBlockJob), + .job_type = JOB_TYPE_COMMIT, }, - .job_type = JOB_TYPE_COMMIT, .start = commit_run, }; diff --git a/block/mirror.c b/block/mirror.c index ed72656a23..35fcc1f0b7 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -988,8 +988,8 @@ static void mirror_drain(BlockJob *job) static const BlockJobDriver mirror_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_MIRROR, }, - .job_type = JOB_TYPE_MIRROR, .start = mirror_run, .complete = mirror_complete, .pause = mirror_pause, @@ -1000,8 +1000,8 @@ static const BlockJobDriver mirror_job_driver = { static const BlockJobDriver commit_active_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_COMMIT, }, - .job_type = JOB_TYPE_COMMIT, .start = mirror_run, .complete = mirror_complete, .pause = mirror_pause, diff --git a/block/stream.c b/block/stream.c index 048bceb5d0..cb723f190a 100644 --- a/block/stream.c +++ b/block/stream.c @@ -211,8 +211,8 @@ out: static const BlockJobDriver stream_job_driver = { .job_driver = { .instance_size = sizeof(StreamBlockJob), + .job_type = JOB_TYPE_STREAM, }, - .job_type = JOB_TYPE_STREAM, .start = stream_run, }; diff --git a/blockjob.c b/blockjob.c index 2a3844721e..ea71ec0129 100644 --- a/blockjob.c +++ b/blockjob.c @@ -309,9 +309,7 @@ static void block_job_detach_aio_context(void *opaque) static char *child_job_get_parent_desc(BdrvChild *c) { BlockJob *job = c->opaque; - return g_strdup_printf("%s job '%s'", - JobType_str(job->driver->job_type), - job->job.id); + return g_strdup_printf("%s job '%s'", job_type_str(&job->job), job->job.id); } static void child_job_drained_begin(BdrvChild *c) @@ -847,7 +845,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) return NULL; } info = g_new0(BlockJobInfo, 1); - info->type = g_strdup(JobType_str(job->driver->job_type)); + info->type = g_strdup(job_type_str(&job->job)); info->device = g_strdup(job->job.id); info->len = job->len; info->busy = atomic_read(&job->busy); @@ -878,7 +876,7 @@ static void block_job_event_cancelled(BlockJob *job) return; } - qapi_event_send_block_job_cancelled(job->driver->job_type, + qapi_event_send_block_job_cancelled(job_type(&job->job), job->job.id, job->len, job->offset, @@ -892,7 +890,7 @@ static void block_job_event_completed(BlockJob *job, const char *msg) return; } - qapi_event_send_block_job_completed(job->driver->job_type, + qapi_event_send_block_job_completed(job_type(&job->job), job->job.id, job->len, job->offset, @@ -906,7 +904,7 @@ static int block_job_event_pending(BlockJob *job) { block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING); if (!job->auto_finalize && !block_job_is_internal(job)) { - qapi_event_send_block_job_pending(job->driver->job_type, + qapi_event_send_block_job_pending(job_type(&job->job), job->job.id, &error_abort); } @@ -980,7 +978,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, block_job_sleep_timer_cb, job); error_setg(&job->blocker, "block device is in use by block job: %s", - JobType_str(driver->job_type)); + job_type_str(&job->job)); block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); bs->job = job; @@ -1184,7 +1182,7 @@ void block_job_event_ready(BlockJob *job) return; } - qapi_event_send_block_job_ready(job->driver->job_type, + qapi_event_send_block_job_ready(job_type(&job->job), job->job.id, job->len, job->offset, diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 8e7e0a2f57..1e62d6dd30 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -38,9 +38,6 @@ struct BlockJobDriver { /** Generic JobDriver callbacks and settings */ JobDriver job_driver; - /** String describing the operation, part of query-block-jobs QMP API */ - JobType job_type; - /** Mandatory: Entrypoint for the Coroutine. */ CoroutineEntry *start; diff --git a/include/qemu/job.h b/include/qemu/job.h index b4b49f19e1..279ce688fd 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -26,6 +26,8 @@ #ifndef JOB_H #define JOB_H +#include "qapi/qapi-types-block-core.h" + typedef struct JobDriver JobDriver; /** @@ -45,6 +47,9 @@ typedef struct Job { struct JobDriver { /** Derived Job struct size */ size_t instance_size; + + /** Enum describing the operation */ + JobType job_type; }; @@ -57,4 +62,10 @@ struct JobDriver { */ void *job_create(const char *job_id, const JobDriver *driver, Error **errp); +/** Returns the JobType of a given Job. */ +JobType job_type(const Job *job); + +/** Returns the enum string for the JobType of a given Job. */ +const char *job_type_str(const Job *job); + #endif diff --git a/job.c b/job.c index 87fd48468c..83724a43de 100644 --- a/job.c +++ b/job.c @@ -29,6 +29,16 @@ #include "qemu/job.h" #include "qemu/id.h" +JobType job_type(const Job *job) +{ + return job->driver->job_type; +} + +const char *job_type_str(const Job *job) +{ + return JobType_str(job_type(job)); +} + void *job_create(const char *job_id, const JobDriver *driver, Error **errp) { Job *job; From fd61a701f1de8e4c1d89b3716ba9ca749cf5c724 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 12 Apr 2018 19:06:53 +0200 Subject: [PATCH 0685/2380] job: Add job_delete() This moves freeing the Job object and its fields from block_job_unref() to job_delete(). Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- blockjob.c | 3 +-- include/qemu/job.h | 3 +++ job.c | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/blockjob.c b/blockjob.c index ea71ec0129..430a67ba63 100644 --- a/blockjob.c +++ b/blockjob.c @@ -261,9 +261,8 @@ void block_job_unref(BlockJob *job) block_job_detach_aio_context, job); blk_unref(job->blk); error_free(job->blocker); - g_free(job->job.id); assert(!timer_pending(&job->sleep_timer)); - g_free(job); + job_delete(&job->job); } } diff --git a/include/qemu/job.h b/include/qemu/job.h index 279ce688fd..43dc2e4a7d 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -62,6 +62,9 @@ struct JobDriver { */ void *job_create(const char *job_id, const JobDriver *driver, Error **errp); +/** Frees the @job object. */ +void job_delete(Job *job); + /** Returns the JobType of a given Job. */ JobType job_type(const Job *job); diff --git a/job.c b/job.c index 83724a43de..cfdd008c52 100644 --- a/job.c +++ b/job.c @@ -56,3 +56,9 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) return job; } + +void job_delete(Job *job) +{ + g_free(job->id); + g_free(job); +} From e7c1d78bbd5867804debeb7159b137fd9a6c44d3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 12 Apr 2018 17:54:37 +0200 Subject: [PATCH 0686/2380] job: Maintain a list of all jobs This moves the job list from BlockJob to Job. Now we can check for duplicate IDs in job_create(). Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- blockjob.c | 46 +++++++++++++++++++++------------------- include/block/blockjob.h | 3 --- include/qemu/job.h | 19 +++++++++++++++++ job.c | 31 +++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 25 deletions(-) diff --git a/blockjob.c b/blockjob.c index 430a67ba63..c69b2e7cf5 100644 --- a/blockjob.c +++ b/blockjob.c @@ -129,8 +129,6 @@ struct BlockJobTxn { int refcnt; }; -static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs); - /* * The block job API is composed of two categories of functions. * @@ -146,25 +144,34 @@ static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs); * blockjob_int.h. */ -BlockJob *block_job_next(BlockJob *job) +static bool is_block_job(Job *job) { - if (!job) { - return QLIST_FIRST(&block_jobs); - } - return QLIST_NEXT(job, job_list); + return job_type(job) == JOB_TYPE_BACKUP || + job_type(job) == JOB_TYPE_COMMIT || + job_type(job) == JOB_TYPE_MIRROR || + job_type(job) == JOB_TYPE_STREAM; +} + +BlockJob *block_job_next(BlockJob *bjob) +{ + Job *job = bjob ? &bjob->job : NULL; + + do { + job = job_next(job); + } while (job && !is_block_job(job)); + + return job ? container_of(job, BlockJob, job) : NULL; } BlockJob *block_job_get(const char *id) { - BlockJob *job; + Job *job = job_get(id); - QLIST_FOREACH(job, &block_jobs, job_list) { - if (job->job.id && !strcmp(id, job->job.id)) { - return job; - } + if (job && is_block_job(job)) { + return container_of(job, BlockJob, job); + } else { + return NULL; } - - return NULL; } BlockJobTxn *block_job_txn_new(void) @@ -253,7 +260,6 @@ void block_job_unref(BlockJob *job) assert(job->status == BLOCK_JOB_STATUS_NULL); assert(!job->txn); BlockDriverState *bs = blk_bs(job->blk); - QLIST_REMOVE(job, job_list); bs->job = NULL; block_job_remove_all_bdrv(job); blk_remove_aio_context_notifier(job->blk, @@ -812,7 +818,7 @@ void block_job_cancel_sync_all(void) BlockJob *job; AioContext *aio_context; - while ((job = QLIST_FIRST(&block_jobs))) { + while ((job = block_job_next(NULL))) { aio_context = blk_get_aio_context(job->blk); aio_context_acquire(aio_context); block_job_cancel_sync(job); @@ -942,10 +948,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, error_setg(errp, "Cannot specify job ID for internal block job"); return NULL; } - if (block_job_get(job_id)) { - error_setg(errp, "Job ID '%s' already in use", job_id); - return NULL; - } } blk = blk_new(perm, shared_perm); @@ -961,6 +963,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return NULL; } + assert(is_block_job(&job->job)); + job->driver = driver; job->blk = blk; job->cb = cb; @@ -983,8 +987,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); - QLIST_INSERT_HEAD(&block_jobs, job, job_list); - blk_add_aio_context_notifier(blk, block_job_attached_aio_context, block_job_detach_aio_context, job); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 640e649034..10bd9f7059 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -105,9 +105,6 @@ typedef struct BlockJob { */ bool deferred_to_main_loop; - /** Element of the list of block jobs */ - QLIST_ENTRY(BlockJob) job_list; - /** Status that is published by the query-block-jobs QMP API */ BlockDeviceIoStatus iostatus; diff --git a/include/qemu/job.h b/include/qemu/job.h index 43dc2e4a7d..bae2b0920c 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -27,6 +27,7 @@ #define JOB_H #include "qapi/qapi-types-block-core.h" +#include "qemu/queue.h" typedef struct JobDriver JobDriver; @@ -39,6 +40,9 @@ typedef struct Job { /** The type of this job. */ const JobDriver *driver; + + /** Element of the list of jobs */ + QLIST_ENTRY(Job) job_list; } Job; /** @@ -71,4 +75,19 @@ JobType job_type(const Job *job); /** Returns the enum string for the JobType of a given Job. */ const char *job_type_str(const Job *job); +/** + * Get the next element from the list of block jobs after @job, or the + * first one if @job is %NULL. + * + * Returns the requested job, or %NULL if there are no more jobs left. + */ +Job *job_next(Job *job); + +/** + * Get the job identified by @id (which must not be %NULL). + * + * Returns the requested job, or %NULL if it doesn't exist. + */ +Job *job_get(const char *id); + #endif diff --git a/job.c b/job.c index cfdd008c52..e57303c6bb 100644 --- a/job.c +++ b/job.c @@ -29,6 +29,8 @@ #include "qemu/job.h" #include "qemu/id.h" +static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); + JobType job_type(const Job *job) { return job->driver->job_type; @@ -39,6 +41,27 @@ const char *job_type_str(const Job *job) return JobType_str(job_type(job)); } +Job *job_next(Job *job) +{ + if (!job) { + return QLIST_FIRST(&jobs); + } + return QLIST_NEXT(job, job_list); +} + +Job *job_get(const char *id) +{ + Job *job; + + QLIST_FOREACH(job, &jobs, job_list) { + if (job->id && !strcmp(id, job->id)) { + return job; + } + } + + return NULL; +} + void *job_create(const char *job_id, const JobDriver *driver, Error **errp) { Job *job; @@ -48,17 +71,25 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) error_setg(errp, "Invalid job ID '%s'", job_id); return NULL; } + if (job_get(job_id)) { + error_setg(errp, "Job ID '%s' already in use", job_id); + return NULL; + } } job = g_malloc0(driver->instance_size); job->driver = driver; job->id = g_strdup(job_id); + QLIST_INSERT_HEAD(&jobs, job, job_list); + return job; } void job_delete(Job *job) { + QLIST_REMOVE(job, job_list); + g_free(job->id); g_free(job); } From a50c2ab858fe613fb805e53b4f6b970ab936706d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 13 Apr 2018 17:19:31 +0200 Subject: [PATCH 0687/2380] job: Move state transitions to Job This moves BlockJob.status and the closely related functions (block_)job_state_transition() and (block_)job_apply_verb to Job. The two QAPI enums are renamed to JobStatus and JobVerb. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow Reviewed-by: Eric Blake --- block/trace-events | 2 - blockjob.c | 102 +++++++++------------------------------ include/block/blockjob.h | 3 -- include/qemu/job.h | 13 +++++ job.c | 56 +++++++++++++++++++++ qapi/block-core.json | 16 +++--- tests/test-blockjob.c | 39 ++++++++------- trace-events | 4 ++ 8 files changed, 123 insertions(+), 112 deletions(-) diff --git a/block/trace-events b/block/trace-events index f8c50b4063..93b927908a 100644 --- a/block/trace-events +++ b/block/trace-events @@ -6,8 +6,6 @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" # blockjob.c block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" -block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" -block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" # block/block-backend.c blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" diff --git a/blockjob.c b/blockjob.c index c69b2e7cf5..0fba01edd4 100644 --- a/blockjob.c +++ b/blockjob.c @@ -41,61 +41,6 @@ * block_job_enter. */ static QemuMutex block_job_mutex; -/* BlockJob State Transition Table */ -bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = { - /* U, C, R, P, Y, S, W, D, X, E, N */ - /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, - /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, - /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, - /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, - /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, - /* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, - /* D: */ [BLOCK_JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, - /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, - /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -}; - -bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = { - /* U, C, R, P, Y, S, W, D, X, E, N */ - [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, - [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, - [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, -}; - -static void block_job_state_transition(BlockJob *job, BlockJobStatus s1) -{ - BlockJobStatus s0 = job->status; - assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX); - trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ? - "allowed" : "disallowed", - BlockJobStatus_str(s0), - BlockJobStatus_str(s1)); - assert(BlockJobSTT[s0][s1]); - job->status = s1; -} - -static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp) -{ - assert(bv >= 0 && bv <= BLOCK_JOB_VERB__MAX); - trace_block_job_apply_verb(job, BlockJobStatus_str(job->status), - BlockJobVerb_str(bv), - BlockJobVerbTable[bv][job->status] ? - "allowed" : "prohibited"); - if (BlockJobVerbTable[bv][job->status]) { - return 0; - } - error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", - job->job.id, BlockJobStatus_str(job->status), - BlockJobVerb_str(bv)); - return -EPERM; -} - static void block_job_lock(void) { qemu_mutex_lock(&block_job_mutex); @@ -257,7 +202,7 @@ static void block_job_detach_aio_context(void *opaque); void block_job_unref(BlockJob *job) { if (--job->refcnt == 0) { - assert(job->status == BLOCK_JOB_STATUS_NULL); + assert(job->job.status == JOB_STATUS_NULL); assert(!job->txn); BlockDriverState *bs = blk_bs(job->blk); bs->job = NULL; @@ -409,7 +354,7 @@ void block_job_start(BlockJob *job) job->pause_count--; job->busy = true; job->paused = false; - block_job_state_transition(job, BLOCK_JOB_STATUS_RUNNING); + job_state_transition(&job->job, JOB_STATUS_RUNNING); bdrv_coroutine_enter(blk_bs(job->blk), job->co); } @@ -421,7 +366,7 @@ static void block_job_decommission(BlockJob *job) job->paused = false; job->deferred_to_main_loop = true; block_job_txn_del_job(job); - block_job_state_transition(job, BLOCK_JOB_STATUS_NULL); + job_state_transition(&job->job, JOB_STATUS_NULL); block_job_unref(job); } @@ -432,7 +377,7 @@ static void block_job_do_dismiss(BlockJob *job) static void block_job_conclude(BlockJob *job) { - block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED); + job_state_transition(&job->job, JOB_STATUS_CONCLUDED); if (job->auto_dismiss || !block_job_started(job)) { block_job_do_dismiss(job); } @@ -444,7 +389,7 @@ static void block_job_update_rc(BlockJob *job) job->ret = -ECANCELED; } if (job->ret) { - block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING); + job_state_transition(&job->job, JOB_STATUS_ABORTING); } } @@ -652,7 +597,7 @@ static void block_job_completed_txn_success(BlockJob *job) BlockJobTxn *txn = job->txn; BlockJob *other_job; - block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING); + job_state_transition(&job->job, JOB_STATUS_WAITING); /* * Successful completion, see if there are other running jobs in this @@ -677,7 +622,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { int64_t old_speed = job->speed; - if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) { + if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) { return; } if (speed < 0) { @@ -709,7 +654,7 @@ void block_job_complete(BlockJob *job, Error **errp) { /* Should not be reachable via external interface for internal jobs */ assert(job->job.id); - if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) { + if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) { return; } if (job->pause_count || job->cancelled || !job->driver->complete) { @@ -724,7 +669,7 @@ void block_job_complete(BlockJob *job, Error **errp) void block_job_finalize(BlockJob *job, Error **errp) { assert(job && job->job.id); - if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) { + if (job_apply_verb(&job->job, JOB_VERB_FINALIZE, errp)) { return; } block_job_do_finalize(job); @@ -735,7 +680,7 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) BlockJob *job = *jobptr; /* similarly to _complete, this is QMP-interface only. */ assert(job->job.id); - if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) { + if (job_apply_verb(&job->job, JOB_VERB_DISMISS, errp)) { return; } @@ -745,7 +690,7 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) void block_job_user_pause(BlockJob *job, Error **errp) { - if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) { + if (job_apply_verb(&job->job, JOB_VERB_PAUSE, errp)) { return; } if (job->user_paused) { @@ -768,7 +713,7 @@ void block_job_user_resume(BlockJob *job, Error **errp) error_setg(errp, "Can't resume a job that was not paused"); return; } - if (block_job_apply_verb(job, BLOCK_JOB_VERB_RESUME, errp)) { + if (job_apply_verb(&job->job, JOB_VERB_RESUME, errp)) { return; } block_job_iostatus_reset(job); @@ -778,7 +723,7 @@ void block_job_user_resume(BlockJob *job, Error **errp) void block_job_cancel(BlockJob *job, bool force) { - if (job->status == BLOCK_JOB_STATUS_CONCLUDED) { + if (job->job.status == JOB_STATUS_CONCLUDED) { block_job_do_dismiss(job); return; } @@ -794,7 +739,7 @@ void block_job_cancel(BlockJob *job, bool force) void block_job_user_cancel(BlockJob *job, bool force, Error **errp) { - if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) { + if (job_apply_verb(&job->job, JOB_VERB_CANCEL, errp)) { return; } block_job_cancel(job, force); @@ -859,7 +804,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info->speed = job->speed; info->io_status = job->iostatus; info->ready = job->ready; - info->status = job->status; + info->status = job->job.status; info->auto_finalize = job->auto_finalize; info->auto_dismiss = job->auto_dismiss; info->has_error = job->ret != 0; @@ -907,7 +852,7 @@ static void block_job_event_completed(BlockJob *job, const char *msg) static int block_job_event_pending(BlockJob *job) { - block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING); + job_state_transition(&job->job, JOB_STATUS_PENDING); if (!job->auto_finalize && !block_job_is_internal(job)) { qapi_event_send_block_job_pending(job_type(&job->job), job->job.id, @@ -975,7 +920,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job->refcnt = 1; job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); - block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED); aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, QEMU_CLOCK_REALTIME, SCALE_NS, block_job_sleep_timer_cb, job); @@ -1017,7 +961,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, void block_job_early_fail(BlockJob *job) { - assert(job->status == BLOCK_JOB_STATUS_CREATED); + assert(job->job.status == JOB_STATUS_CREATED); block_job_decommission(job); } @@ -1077,14 +1021,14 @@ void coroutine_fn block_job_pause_point(BlockJob *job) } if (block_job_should_pause(job) && !block_job_is_cancelled(job)) { - BlockJobStatus status = job->status; - block_job_state_transition(job, status == BLOCK_JOB_STATUS_READY ? \ - BLOCK_JOB_STATUS_STANDBY : \ - BLOCK_JOB_STATUS_PAUSED); + JobStatus status = job->job.status; + job_state_transition(&job->job, status == JOB_STATUS_READY + ? JOB_STATUS_STANDBY + : JOB_STATUS_PAUSED); job->paused = true; block_job_do_yield(job, -1); job->paused = false; - block_job_state_transition(job, status); + job_state_transition(&job->job, status); } if (job->driver->resume) { @@ -1176,7 +1120,7 @@ void block_job_iostatus_reset(BlockJob *job) void block_job_event_ready(BlockJob *job) { - block_job_state_transition(job, BLOCK_JOB_STATUS_READY); + job_state_transition(&job->job, JOB_STATUS_READY); job->ready = true; if (block_job_is_internal(job)) { diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 10bd9f7059..01cdee6d15 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -147,9 +147,6 @@ typedef struct BlockJob { */ QEMUTimer sleep_timer; - /** Current state; See @BlockJobStatus for details. */ - BlockJobStatus status; - /** True if this job should automatically finalize itself */ bool auto_finalize; diff --git a/include/qemu/job.h b/include/qemu/job.h index bae2b0920c..0b78778b9e 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -41,6 +41,9 @@ typedef struct Job { /** The type of this job. */ const JobDriver *driver; + /** Current state; See @JobStatus for details. */ + JobStatus status; + /** Element of the list of jobs */ QLIST_ENTRY(Job) job_list; } Job; @@ -90,4 +93,14 @@ Job *job_next(Job *job); */ Job *job_get(const char *id); +/** + * Check whether the verb @verb can be applied to @job in its current state. + * Returns 0 if the verb can be applied; otherwise errp is set and -EPERM + * returned. + */ +int job_apply_verb(Job *job, JobVerb verb, Error **errp); + +/* TODO To be removed from the public interface */ +void job_state_transition(Job *job, JobStatus s1); + #endif diff --git a/job.c b/job.c index e57303c6bb..b049a322cc 100644 --- a/job.c +++ b/job.c @@ -28,9 +28,63 @@ #include "qapi/error.h" #include "qemu/job.h" #include "qemu/id.h" +#include "trace-root.h" static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); +/* Job State Transition Table */ +bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = { + /* U, C, R, P, Y, S, W, D, X, E, N */ + /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, + /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, + /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, + /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, + /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, + /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, + /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, + /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { + /* U, C, R, P, Y, S, W, D, X, E, N */ + [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, + [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, + [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, + [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, + [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, + [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, + [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, +}; + +/* TODO Make static once the whole state machine is in job.c */ +void job_state_transition(Job *job, JobStatus s1) +{ + JobStatus s0 = job->status; + assert(s1 >= 0 && s1 <= JOB_STATUS__MAX); + trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0, + JobSTT[s0][s1] ? "allowed" : "disallowed", + JobStatus_str(s0), JobStatus_str(s1)); + assert(JobSTT[s0][s1]); + job->status = s1; +} + +int job_apply_verb(Job *job, JobVerb verb, Error **errp) +{ + JobStatus s0 = job->status; + assert(verb >= 0 && verb <= JOB_VERB__MAX); + trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), + JobVerbTable[verb][s0] ? "allowed" : "prohibited"); + if (JobVerbTable[verb][s0]) { + return 0; + } + error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", + job->id, JobStatus_str(s0), JobVerb_str(verb)); + return -EPERM; +} + JobType job_type(const Job *job) { return job->driver->job_type; @@ -81,6 +135,8 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) job->driver = driver; job->id = g_strdup(job_id); + job_state_transition(job, JOB_STATUS_CREATED); + QLIST_INSERT_HEAD(&jobs, job, job_list); return job; diff --git a/qapi/block-core.json b/qapi/block-core.json index 63c6011411..bb964b4319 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1068,9 +1068,9 @@ 'data': ['commit', 'stream', 'mirror', 'backup'] } ## -# @BlockJobVerb: +# @JobVerb: # -# Represents command verbs that can be applied to a blockjob. +# Represents command verbs that can be applied to a job. # # @cancel: see @block-job-cancel # @@ -1088,14 +1088,14 @@ # # Since: 2.12 ## -{ 'enum': 'BlockJobVerb', +{ 'enum': 'JobVerb', 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', 'finalize' ] } ## -# @BlockJobStatus: +# @JobStatus: # -# Indicates the present state of a given blockjob in its lifetime. +# Indicates the present state of a given job in its lifetime. # # @undefined: Erroneous, default state. Should not ever be visible. # @@ -1134,7 +1134,7 @@ # # Since: 2.12 ## -{ 'enum': 'BlockJobStatus', +{ 'enum': 'JobStatus', 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby', 'waiting', 'pending', 'aborting', 'concluded', 'null' ] } @@ -1184,7 +1184,7 @@ 'data': {'type': 'str', 'device': 'str', 'len': 'int', 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', - 'status': 'BlockJobStatus', + 'status': 'JobStatus', 'auto-finalize': 'bool', 'auto-dismiss': 'bool', '*error': 'str' } } @@ -2416,7 +2416,7 @@ # QEMU 2.12+ job lifetime management semantics. # # This command will refuse to operate on any job that has not yet reached -# its terminal state, BLOCK_JOB_STATUS_CONCLUDED. For jobs that make use of +# its terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of the # BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need # to be used as appropriate. # diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index b82026180a..6ccd585dee 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -211,7 +211,7 @@ static CancelJob *create_common(BlockJob **pjob) job = mk_job(blk, "Steve", &test_cancel_driver, true, BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); block_job_ref(job); - assert(job->status == BLOCK_JOB_STATUS_CREATED); + assert(job->job.status == JOB_STATUS_CREATED); s = container_of(job, CancelJob, common); s->blk = blk; @@ -223,15 +223,14 @@ static void cancel_common(CancelJob *s) { BlockJob *job = &s->common; BlockBackend *blk = s->blk; - BlockJobStatus sts = job->status; + JobStatus sts = job->job.status; block_job_cancel_sync(job); - if ((sts != BLOCK_JOB_STATUS_CREATED) && - (sts != BLOCK_JOB_STATUS_CONCLUDED)) { + if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { BlockJob *dummy = job; block_job_dismiss(&dummy, &error_abort); } - assert(job->status == BLOCK_JOB_STATUS_NULL); + assert(job->job.status == JOB_STATUS_NULL); block_job_unref(job); destroy_blk(blk); } @@ -253,7 +252,7 @@ static void test_cancel_running(void) s = create_common(&job); block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + assert(job->job.status == JOB_STATUS_RUNNING); cancel_common(s); } @@ -266,11 +265,11 @@ static void test_cancel_paused(void) s = create_common(&job); block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + assert(job->job.status == JOB_STATUS_RUNNING); block_job_user_pause(job, &error_abort); block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_PAUSED); + assert(job->job.status == JOB_STATUS_PAUSED); cancel_common(s); } @@ -283,11 +282,11 @@ static void test_cancel_ready(void) s = create_common(&job); block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + assert(job->job.status == JOB_STATUS_READY); cancel_common(s); } @@ -300,15 +299,15 @@ static void test_cancel_standby(void) s = create_common(&job); block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + assert(job->job.status == JOB_STATUS_READY); block_job_user_pause(job, &error_abort); block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_STANDBY); + assert(job->job.status == JOB_STATUS_STANDBY); cancel_common(s); } @@ -321,18 +320,18 @@ static void test_cancel_pending(void) s = create_common(&job); block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + assert(job->job.status == JOB_STATUS_READY); block_job_complete(job, &error_abort); block_job_enter(job); while (!s->completed) { aio_poll(qemu_get_aio_context(), true); } - assert(job->status == BLOCK_JOB_STATUS_PENDING); + assert(job->job.status == JOB_STATUS_PENDING); cancel_common(s); } @@ -345,21 +344,21 @@ static void test_cancel_concluded(void) s = create_common(&job); block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + assert(job->job.status == JOB_STATUS_READY); block_job_complete(job, &error_abort); block_job_enter(job); while (!s->completed) { aio_poll(qemu_get_aio_context(), true); } - assert(job->status == BLOCK_JOB_STATUS_PENDING); + assert(job->job.status == JOB_STATUS_PENDING); block_job_finalize(job, &error_abort); - assert(job->status == BLOCK_JOB_STATUS_CONCLUDED); + assert(job->job.status == JOB_STATUS_CONCLUDED); cancel_common(s); } diff --git a/trace-events b/trace-events index ed71f44649..2507e1327d 100644 --- a/trace-events +++ b/trace-events @@ -104,6 +104,10 @@ gdbstub_err_invalid_rle(void) "got invalid RLE sequence" gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x" gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x" +# job.c +job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" +job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" + ### Guest events, keep at bottom From 80fa2c756b3241f24015a7503a01f7999d4a942d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 13 Apr 2018 18:50:05 +0200 Subject: [PATCH 0688/2380] job: Add reference counting This moves reference counting from BlockJob to Job. In order to keep calling the BlockJob cleanup code when the job is deleted via job_unref(), introduce a new JobDriver.free callback. Every block job must use block_job_free() for this callback, this is asserted in block_job_create(). Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 1 + block/commit.c | 1 + block/mirror.c | 2 ++ block/stream.c | 1 + blockjob.c | 48 ++++++++++++++++-------------------- include/block/blockjob.h | 21 ---------------- include/block/blockjob_int.h | 7 ++++++ include/qemu/job.h | 19 ++++++++++++-- job.c | 24 ++++++++++++++---- qemu-img.c | 4 +-- tests/test-bdrv-drain.c | 1 + tests/test-blockjob-txn.c | 1 + tests/test-blockjob.c | 6 +++-- 13 files changed, 77 insertions(+), 59 deletions(-) diff --git a/block/backup.c b/block/backup.c index baf8d432da..cfdb89d977 100644 --- a/block/backup.c +++ b/block/backup.c @@ -526,6 +526,7 @@ static const BlockJobDriver backup_job_driver = { .job_driver = { .instance_size = sizeof(BackupBlockJob), .job_type = JOB_TYPE_BACKUP, + .free = block_job_free, }, .start = backup_run, .commit = backup_commit, diff --git a/block/commit.c b/block/commit.c index 32d29c890e..925c96abe7 100644 --- a/block/commit.c +++ b/block/commit.c @@ -218,6 +218,7 @@ static const BlockJobDriver commit_job_driver = { .job_driver = { .instance_size = sizeof(CommitBlockJob), .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, }, .start = commit_run, }; diff --git a/block/mirror.c b/block/mirror.c index 35fcc1f0b7..0df4f709fd 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -989,6 +989,7 @@ static const BlockJobDriver mirror_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = JOB_TYPE_MIRROR, + .free = block_job_free, }, .start = mirror_run, .complete = mirror_complete, @@ -1001,6 +1002,7 @@ static const BlockJobDriver commit_active_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, }, .start = mirror_run, .complete = mirror_complete, diff --git a/block/stream.c b/block/stream.c index cb723f190a..7273d2213c 100644 --- a/block/stream.c +++ b/block/stream.c @@ -212,6 +212,7 @@ static const BlockJobDriver stream_job_driver = { .job_driver = { .instance_size = sizeof(StreamBlockJob), .job_type = JOB_TYPE_STREAM, + .free = block_job_free, }, .start = stream_run, }; diff --git a/blockjob.c b/blockjob.c index 0fba01edd4..0bf0a26af0 100644 --- a/blockjob.c +++ b/blockjob.c @@ -190,31 +190,25 @@ static void block_job_resume(BlockJob *job) block_job_enter_cond(job, block_job_timer_not_pending); } -void block_job_ref(BlockJob *job) -{ - ++job->refcnt; -} - static void block_job_attached_aio_context(AioContext *new_context, void *opaque); static void block_job_detach_aio_context(void *opaque); -void block_job_unref(BlockJob *job) +void block_job_free(Job *job) { - if (--job->refcnt == 0) { - assert(job->job.status == JOB_STATUS_NULL); - assert(!job->txn); - BlockDriverState *bs = blk_bs(job->blk); - bs->job = NULL; - block_job_remove_all_bdrv(job); - blk_remove_aio_context_notifier(job->blk, - block_job_attached_aio_context, - block_job_detach_aio_context, job); - blk_unref(job->blk); - error_free(job->blocker); - assert(!timer_pending(&job->sleep_timer)); - job_delete(&job->job); - } + BlockJob *bjob = container_of(job, BlockJob, job); + BlockDriverState *bs = blk_bs(bjob->blk); + + assert(!bjob->txn); + + bs->job = NULL; + block_job_remove_all_bdrv(bjob); + blk_remove_aio_context_notifier(bjob->blk, + block_job_attached_aio_context, + block_job_detach_aio_context, bjob); + blk_unref(bjob->blk); + error_free(bjob->blocker); + assert(!timer_pending(&bjob->sleep_timer)); } static void block_job_attached_aio_context(AioContext *new_context, @@ -245,7 +239,7 @@ static void block_job_detach_aio_context(void *opaque) BlockJob *job = opaque; /* In case the job terminates during aio_poll()... */ - block_job_ref(job); + job_ref(&job->job); block_job_pause(job); @@ -253,7 +247,7 @@ static void block_job_detach_aio_context(void *opaque) block_job_drain(job); } - block_job_unref(job); + job_unref(&job->job); } static char *child_job_get_parent_desc(BdrvChild *c) @@ -367,7 +361,7 @@ static void block_job_decommission(BlockJob *job) job->deferred_to_main_loop = true; block_job_txn_del_job(job); job_state_transition(&job->job, JOB_STATUS_NULL); - block_job_unref(job); + job_unref(&job->job); } static void block_job_do_dismiss(BlockJob *job) @@ -506,14 +500,14 @@ static int block_job_finish_sync(BlockJob *job, assert(blk_bs(job->blk)->job == job); - block_job_ref(job); + job_ref(&job->job); if (finish) { finish(job, &local_err); } if (local_err) { error_propagate(errp, local_err); - block_job_unref(job); + job_unref(&job->job); return -EBUSY; } /* block_job_drain calls block_job_enter, and it should be enough to @@ -526,7 +520,7 @@ static int block_job_finish_sync(BlockJob *job, aio_poll(qemu_get_aio_context(), true); } ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret; - block_job_unref(job); + job_unref(&job->job); return ret; } @@ -909,6 +903,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, } assert(is_block_job(&job->job)); + assert(job->job.driver->free == &block_job_free); job->driver = driver; job->blk = blk; @@ -917,7 +912,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job->busy = false; job->paused = true; job->pause_count = 1; - job->refcnt = 1; job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 01cdee6d15..087e7820f5 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -132,9 +132,6 @@ typedef struct BlockJob { /** The opaque value that is passed to the completion function. */ void *opaque; - /** Reference count of the block job */ - int refcnt; - /** True when job has reported completion by calling block_job_completed. */ bool completed; @@ -399,24 +396,6 @@ void block_job_iostatus_reset(BlockJob *job); */ BlockJobTxn *block_job_txn_new(void); -/** - * block_job_ref: - * - * Add a reference to BlockJob refcnt, it will be decreased with - * block_job_unref, and then be freed if it comes to be the last - * reference. - */ -void block_job_ref(BlockJob *job); - -/** - * block_job_unref: - * - * Release a reference that was previously acquired with block_job_ref - * or block_job_create. If it's the last reference to the object, it will be - * freed. - */ -void block_job_unref(BlockJob *job); - /** * block_job_txn_unref: * diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 1e62d6dd30..6f0fe3c48d 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -143,6 +143,13 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, uint64_t shared_perm, int64_t speed, int flags, BlockCompletionFunc *cb, void *opaque, Error **errp); +/** + * block_job_free: + * Callback to be used for JobDriver.free in all block jobs. Frees block job + * specific resources in @job. + */ +void block_job_free(Job *job); + /** * block_job_sleep_ns: * @job: The job that calls the function. diff --git a/include/qemu/job.h b/include/qemu/job.h index 0b78778b9e..0751e2afab 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -41,6 +41,9 @@ typedef struct Job { /** The type of this job. */ const JobDriver *driver; + /** Reference count of the block job */ + int refcnt; + /** Current state; See @JobStatus for details. */ JobStatus status; @@ -57,6 +60,9 @@ struct JobDriver { /** Enum describing the operation */ JobType job_type; + + /** Called when the job is freed */ + void (*free)(Job *job); }; @@ -69,8 +75,17 @@ struct JobDriver { */ void *job_create(const char *job_id, const JobDriver *driver, Error **errp); -/** Frees the @job object. */ -void job_delete(Job *job); +/** + * Add a reference to Job refcnt, it will be decreased with job_unref, and then + * be freed if it comes to be the last reference. + */ +void job_ref(Job *job); + +/** + * Release a reference that was previously acquired with job_ref() or + * job_create(). If it's the last reference to the object, it will be freed. + */ +void job_unref(Job *job); /** Returns the JobType of a given Job. */ JobType job_type(const Job *job); diff --git a/job.c b/job.c index b049a322cc..926f1de9a2 100644 --- a/job.c +++ b/job.c @@ -134,6 +134,7 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) job = g_malloc0(driver->instance_size); job->driver = driver; job->id = g_strdup(job_id); + job->refcnt = 1; job_state_transition(job, JOB_STATUS_CREATED); @@ -142,10 +143,23 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) return job; } -void job_delete(Job *job) +void job_ref(Job *job) { - QLIST_REMOVE(job, job_list); - - g_free(job->id); - g_free(job); + ++job->refcnt; +} + +void job_unref(Job *job) +{ + if (--job->refcnt == 0) { + assert(job->status == JOB_STATUS_NULL); + + if (job->driver->free) { + job->driver->free(job); + } + + QLIST_REMOVE(job, job_list); + + g_free(job->id); + g_free(job); + } } diff --git a/qemu-img.c b/qemu-img.c index 2b5a5706b6..911456ba40 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -861,7 +861,7 @@ static void run_block_job(BlockJob *job, Error **errp) int ret = 0; aio_context_acquire(aio_context); - block_job_ref(job); + job_ref(&job->job); do { aio_poll(aio_context, true); qemu_progress_print(job->len ? @@ -873,7 +873,7 @@ static void run_block_job(BlockJob *job, Error **errp) } else { ret = job->ret; } - block_job_unref(job); + job_unref(&job->job); aio_context_release(aio_context); /* publish completion progress only when success */ diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index fe9f412b39..f9e37d479c 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -522,6 +522,7 @@ static void test_job_complete(BlockJob *job, Error **errp) BlockJobDriver test_job_driver = { .job_driver = { .instance_size = sizeof(TestBlockJob), + .free = block_job_free, }, .start = test_job_start, .complete = test_job_complete, diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 48b12d1744..b49b28ca27 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -76,6 +76,7 @@ static void test_block_job_cb(void *opaque, int ret) static const BlockJobDriver test_block_job_driver = { .job_driver = { .instance_size = sizeof(TestBlockJob), + .free = block_job_free, }, .start = test_block_job_run, }; diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 6ccd585dee..e24fc3f140 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -19,6 +19,7 @@ static const BlockJobDriver test_block_job_driver = { .job_driver = { .instance_size = sizeof(BlockJob), + .free = block_job_free, }, }; @@ -196,6 +197,7 @@ static void coroutine_fn cancel_job_start(void *opaque) static const BlockJobDriver test_cancel_driver = { .job_driver = { .instance_size = sizeof(CancelJob), + .free = block_job_free, }, .start = cancel_job_start, .complete = cancel_job_complete, @@ -210,7 +212,7 @@ static CancelJob *create_common(BlockJob **pjob) blk = create_blk(NULL); job = mk_job(blk, "Steve", &test_cancel_driver, true, BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); - block_job_ref(job); + job_ref(&job->job); assert(job->job.status == JOB_STATUS_CREATED); s = container_of(job, CancelJob, common); s->blk = blk; @@ -231,7 +233,7 @@ static void cancel_common(CancelJob *s) block_job_dismiss(&dummy, &error_abort); } assert(job->job.status == JOB_STATUS_NULL); - block_job_unref(job); + job_unref(&job->job); destroy_blk(blk); } From daa7f2f9467bc5624f04f28d4b01b88f08c6589c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 17 Apr 2018 12:56:07 +0200 Subject: [PATCH 0689/2380] job: Move cancelled to Job We cannot yet move the whole logic around job cancelling to Job because it depends on quite a few other things that are still only in BlockJob, but we can move the cancelled field at least. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 6 +++--- block/commit.c | 4 ++-- block/mirror.c | 20 ++++++++++---------- block/stream.c | 4 ++-- blockjob.c | 28 +++++++++++++--------------- include/block/blockjob.h | 8 -------- include/block/blockjob_int.h | 8 -------- include/qemu/job.h | 11 +++++++++++ job.c | 5 +++++ tests/test-blockjob-txn.c | 6 +++--- tests/test-blockjob.c | 2 +- 11 files changed, 50 insertions(+), 52 deletions(-) diff --git a/block/backup.c b/block/backup.c index cfdb89d977..ef0aa0e24e 100644 --- a/block/backup.c +++ b/block/backup.c @@ -329,7 +329,7 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) { uint64_t delay_ns; - if (block_job_is_cancelled(&job->common)) { + if (job_is_cancelled(&job->common.job)) { return true; } @@ -339,7 +339,7 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) job->bytes_read = 0; block_job_sleep_ns(&job->common, delay_ns); - if (block_job_is_cancelled(&job->common)) { + if (job_is_cancelled(&job->common.job)) { return true; } @@ -441,7 +441,7 @@ static void coroutine_fn backup_run(void *opaque) if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { /* All bits are set in copy_bitmap to allow any cluster to be copied. * This does not actually require them to be copied. */ - while (!block_job_is_cancelled(&job->common)) { + while (!job_is_cancelled(&job->common.job)) { /* Yield until the job is cancelled. We just let our before_write * notify callback service CoW requests. */ block_job_yield(&job->common); diff --git a/block/commit.c b/block/commit.c index 925c96abe7..85baea8f92 100644 --- a/block/commit.c +++ b/block/commit.c @@ -90,7 +90,7 @@ static void commit_complete(BlockJob *job, void *opaque) * the normal backing chain can be restored. */ blk_unref(s->base); - if (!block_job_is_cancelled(&s->common) && ret == 0) { + if (!job_is_cancelled(&s->common.job) && ret == 0) { /* success */ ret = bdrv_drop_intermediate(s->commit_top_bs, base, s->backing_file_str); @@ -172,7 +172,7 @@ static void coroutine_fn commit_run(void *opaque) * with no pending I/O here so that bdrv_drain_all() returns. */ block_job_sleep_ns(&s->common, delay_ns); - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { break; } /* Copy if allocated above the base */ diff --git a/block/mirror.c b/block/mirror.c index 0df4f709fd..424072ed00 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -622,7 +622,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) mirror_throttle(s); - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { s->initial_zeroing_ongoing = false; return 0; } @@ -650,7 +650,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) mirror_throttle(s); - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { return 0; } @@ -695,7 +695,7 @@ static void coroutine_fn mirror_run(void *opaque) checking for a NULL string */ int ret = 0; - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { goto immediate_exit; } @@ -729,10 +729,10 @@ static void coroutine_fn mirror_run(void *opaque) /* Report BLOCK_JOB_READY and wait for complete. */ block_job_event_ready(&s->common); s->synced = true; - while (!block_job_is_cancelled(&s->common) && !s->should_complete) { + while (!job_is_cancelled(&s->common.job) && !s->should_complete) { block_job_yield(&s->common); } - s->common.cancelled = false; + s->common.job.cancelled = false; goto immediate_exit; } @@ -768,7 +768,7 @@ static void coroutine_fn mirror_run(void *opaque) s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); if (!s->is_none_mode) { ret = mirror_dirty_init(s); - if (ret < 0 || block_job_is_cancelled(&s->common)) { + if (ret < 0 || job_is_cancelled(&s->common.job)) { goto immediate_exit; } } @@ -828,7 +828,7 @@ static void coroutine_fn mirror_run(void *opaque) } should_complete = s->should_complete || - block_job_is_cancelled(&s->common); + job_is_cancelled(&s->common.job); cnt = bdrv_get_dirty_count(s->dirty_bitmap); } @@ -856,7 +856,7 @@ static void coroutine_fn mirror_run(void *opaque) * completion. */ assert(QLIST_EMPTY(&bs->tracked_requests)); - s->common.cancelled = false; + s->common.job.cancelled = false; need_drain = false; break; } @@ -869,7 +869,7 @@ static void coroutine_fn mirror_run(void *opaque) } trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); block_job_sleep_ns(&s->common, delay_ns); - if (block_job_is_cancelled(&s->common) && + if (job_is_cancelled(&s->common.job) && (!s->synced || s->common.force)) { break; @@ -884,7 +884,7 @@ immediate_exit: * the target is a copy of the source. */ assert(ret < 0 || ((s->common.force || !s->synced) && - block_job_is_cancelled(&s->common))); + job_is_cancelled(&s->common.job))); assert(need_drain); mirror_wait_for_all_io(s); } diff --git a/block/stream.c b/block/stream.c index 7273d2213c..22c71ae100 100644 --- a/block/stream.c +++ b/block/stream.c @@ -66,7 +66,7 @@ static void stream_complete(BlockJob *job, void *opaque) BlockDriverState *base = s->base; Error *local_err = NULL; - if (!block_job_is_cancelled(&s->common) && bs->backing && + if (!job_is_cancelled(&s->common.job) && bs->backing && data->ret == 0) { const char *base_id = NULL, *base_fmt = NULL; if (base) { @@ -141,7 +141,7 @@ static void coroutine_fn stream_run(void *opaque) * with no pending I/O here so that bdrv_drain_all() returns. */ block_job_sleep_ns(&s->common, delay_ns); - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { break; } diff --git a/blockjob.c b/blockjob.c index 0bf0a26af0..f4f9956678 100644 --- a/blockjob.c +++ b/blockjob.c @@ -379,7 +379,7 @@ static void block_job_conclude(BlockJob *job) static void block_job_update_rc(BlockJob *job) { - if (!job->ret && block_job_is_cancelled(job)) { + if (!job->ret && job_is_cancelled(&job->job)) { job->ret = -ECANCELED; } if (job->ret) { @@ -438,7 +438,7 @@ static int block_job_finalize_single(BlockJob *job) /* Emit events only if we actually started */ if (block_job_started(job)) { - if (block_job_is_cancelled(job)) { + if (job_is_cancelled(&job->job)) { block_job_event_cancelled(job); } else { const char *msg = NULL; @@ -464,7 +464,7 @@ static void block_job_cancel_async(BlockJob *job, bool force) job->user_paused = false; job->pause_count--; } - job->cancelled = true; + job->job.cancelled = true; /* To prevent 'force == false' overriding a previous 'force == true' */ job->force |= force; } @@ -519,7 +519,8 @@ static int block_job_finish_sync(BlockJob *job, while (!job->completed) { aio_poll(qemu_get_aio_context(), true); } - ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret; + ret = (job_is_cancelled(&job->job) && job->ret == 0) + ? -ECANCELED : job->ret; job_unref(&job->job); return ret; } @@ -557,7 +558,7 @@ static void block_job_completed_txn_abort(BlockJob *job) other_job = QLIST_FIRST(&txn->jobs); ctx = blk_get_aio_context(other_job->blk); if (!other_job->completed) { - assert(other_job->cancelled); + assert(job_is_cancelled(&other_job->job)); block_job_finish_sync(other_job, NULL, NULL); } block_job_finalize_single(other_job); @@ -651,7 +652,9 @@ void block_job_complete(BlockJob *job, Error **errp) if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) { return; } - if (job->pause_count || job->cancelled || !job->driver->complete) { + if (job->pause_count || job_is_cancelled(&job->job) || + !job->driver->complete) + { error_setg(errp, "The active block job '%s' cannot be completed", job->job.id); return; @@ -1006,7 +1009,7 @@ void coroutine_fn block_job_pause_point(BlockJob *job) if (!block_job_should_pause(job)) { return; } - if (block_job_is_cancelled(job)) { + if (job_is_cancelled(&job->job)) { return; } @@ -1014,7 +1017,7 @@ void coroutine_fn block_job_pause_point(BlockJob *job) job->driver->pause(job); } - if (block_job_should_pause(job) && !block_job_is_cancelled(job)) { + if (block_job_should_pause(job) && !job_is_cancelled(&job->job)) { JobStatus status = job->job.status; job_state_transition(&job->job, status == JOB_STATUS_READY ? JOB_STATUS_STANDBY @@ -1066,17 +1069,12 @@ void block_job_enter(BlockJob *job) block_job_enter_cond(job, NULL); } -bool block_job_is_cancelled(BlockJob *job) -{ - return job->cancelled; -} - void block_job_sleep_ns(BlockJob *job, int64_t ns) { assert(job->busy); /* Check cancellation *before* setting busy = false, too! */ - if (block_job_is_cancelled(job)) { + if (job_is_cancelled(&job->job)) { return; } @@ -1092,7 +1090,7 @@ void block_job_yield(BlockJob *job) assert(job->busy); /* Check cancellation *before* setting busy = false, too! */ - if (block_job_is_cancelled(job)) { + if (job_is_cancelled(&job->job)) { return; } diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 087e7820f5..1e708f468a 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -56,14 +56,6 @@ typedef struct BlockJob { */ Coroutine *co; - /** - * Set to true if the job should cancel itself. The flag must - * always be tested just before toggling the busy flag from false - * to true. After a job has been cancelled, it should only yield - * if #aio_poll will ("sooner or later") reenter the coroutine. - */ - bool cancelled; - /** * Set to true if the job should abort immediately without waiting * for data to be in sync. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 6f0fe3c48d..d64f30e6b0 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -195,14 +195,6 @@ void block_job_early_fail(BlockJob *job); */ void block_job_completed(BlockJob *job, int ret); -/** - * block_job_is_cancelled: - * @job: The job being queried. - * - * Returns whether the job is scheduled for cancellation. - */ -bool block_job_is_cancelled(BlockJob *job); - /** * block_job_pause_point: * @job: The job that is ready to pause. diff --git a/include/qemu/job.h b/include/qemu/job.h index 0751e2afab..5dfbec5d69 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -47,6 +47,14 @@ typedef struct Job { /** Current state; See @JobStatus for details. */ JobStatus status; + /** + * Set to true if the job should cancel itself. The flag must + * always be tested just before toggling the busy flag from false + * to true. After a job has been cancelled, it should only yield + * if #aio_poll will ("sooner or later") reenter the coroutine. + */ + bool cancelled; + /** Element of the list of jobs */ QLIST_ENTRY(Job) job_list; } Job; @@ -93,6 +101,9 @@ JobType job_type(const Job *job); /** Returns the enum string for the JobType of a given Job. */ const char *job_type_str(const Job *job); +/** Returns whether the job is scheduled for cancellation. */ +bool job_is_cancelled(Job *job); + /** * Get the next element from the list of block jobs after @job, or the * first one if @job is %NULL. diff --git a/job.c b/job.c index 926f1de9a2..1abca6a2fc 100644 --- a/job.c +++ b/job.c @@ -95,6 +95,11 @@ const char *job_type_str(const Job *job) return JobType_str(job_type(job)); } +bool job_is_cancelled(Job *job) +{ + return job->cancelled; +} + Job *job_next(Job *job) { if (!job) { diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index b49b28ca27..26b4bbb230 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -29,7 +29,7 @@ static void test_block_job_complete(BlockJob *job, void *opaque) BlockDriverState *bs = blk_bs(job->blk); int rc = (intptr_t)opaque; - if (block_job_is_cancelled(job)) { + if (job_is_cancelled(&job->job)) { rc = -ECANCELED; } @@ -49,7 +49,7 @@ static void coroutine_fn test_block_job_run(void *opaque) block_job_yield(job); } - if (block_job_is_cancelled(job)) { + if (job_is_cancelled(&job->job)) { break; } } @@ -66,7 +66,7 @@ typedef struct { static void test_block_job_cb(void *opaque, int ret) { TestBlockJobCBData *data = opaque; - if (!ret && block_job_is_cancelled(&data->job->common)) { + if (!ret && job_is_cancelled(&data->job->common.job)) { ret = -ECANCELED; } *data->result = ret; diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index e24fc3f140..fa31481537 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -179,7 +179,7 @@ static void coroutine_fn cancel_job_start(void *opaque) CancelJob *s = opaque; while (!s->should_complete) { - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { goto defer; } From 08be6fe26f6c76d900fc987f58d322b94bc4e248 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 17 Apr 2018 13:49:33 +0200 Subject: [PATCH 0690/2380] job: Add Job.aio_context When block jobs need an AioContext, they just take it from their main block node. Generic jobs don't have a main block node, so we need to assign them an AioContext explicitly. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- blockjob.c | 5 ++++- include/qemu/job.h | 7 ++++++- job.c | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/blockjob.c b/blockjob.c index f4f9956678..0a0b1c41dd 100644 --- a/blockjob.c +++ b/blockjob.c @@ -216,6 +216,7 @@ static void block_job_attached_aio_context(AioContext *new_context, { BlockJob *job = opaque; + job->job.aio_context = new_context; if (job->driver->attached_aio_context) { job->driver->attached_aio_context(job, new_context); } @@ -247,6 +248,7 @@ static void block_job_detach_aio_context(void *opaque) block_job_drain(job); } + job->job.aio_context = NULL; job_unref(&job->job); } @@ -899,7 +901,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return NULL; } - job = job_create(job_id, &driver->job_driver, errp); + job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk), + errp); if (job == NULL) { blk_unref(blk); return NULL; diff --git a/include/qemu/job.h b/include/qemu/job.h index 5dfbec5d69..01e083f97a 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -47,6 +47,9 @@ typedef struct Job { /** Current state; See @JobStatus for details. */ JobStatus status; + /** AioContext to run the job coroutine in */ + AioContext *aio_context; + /** * Set to true if the job should cancel itself. The flag must * always be tested just before toggling the busy flag from false @@ -79,9 +82,11 @@ struct JobDriver { * * @job_id: The id of the newly-created job, or %NULL for internal jobs * @driver: The class object for the newly-created job. + * @ctx: The AioContext to run the job coroutine in. * @errp: Error object. */ -void *job_create(const char *job_id, const JobDriver *driver, Error **errp); +void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, + Error **errp); /** * Add a reference to Job refcnt, it will be decreased with job_unref, and then diff --git a/job.c b/job.c index 1abca6a2fc..01074d0fae 100644 --- a/job.c +++ b/job.c @@ -121,7 +121,8 @@ Job *job_get(const char *id) return NULL; } -void *job_create(const char *job_id, const JobDriver *driver, Error **errp) +void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, + Error **errp) { Job *job; @@ -140,6 +141,7 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp) job->driver = driver; job->id = g_strdup(job_id); job->refcnt = 1; + job->aio_context = ctx; job_state_transition(job, JOB_STATUS_CREATED); From 1908a5590c7d214b1b6886bc19b81076fb65cec9 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 17 Apr 2018 16:41:17 +0200 Subject: [PATCH 0691/2380] job: Move defer_to_main_loop to Job Move the defer_to_main_loop functionality from BlockJob to Job. The code can be simplified because we can use job->aio_context in job_defer_to_main_loop_bh() now, instead of having to access the BlockDriverState. Probably taking the data->aio_context lock in addition was already unnecessary in the old code because we didn't actually make use of anything protected by the old AioContext except getting the new AioContext, in case it changed between scheduling the BH and running it. But it's certainly unnecessary now that the BDS isn't accessed at all any more. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 7 +++-- block/commit.c | 11 +++---- block/mirror.c | 15 +++++----- block/stream.c | 14 ++++----- blockjob.c | 57 ++++-------------------------------- include/block/blockjob.h | 5 ---- include/block/blockjob_int.h | 19 ------------ include/qemu/job.h | 20 +++++++++++++ job.c | 32 ++++++++++++++++++++ tests/test-bdrv-drain.c | 7 +++-- tests/test-blockjob-txn.c | 13 ++++---- tests/test-blockjob.c | 7 +++-- 12 files changed, 97 insertions(+), 110 deletions(-) diff --git a/block/backup.c b/block/backup.c index ef0aa0e24e..22dd368c90 100644 --- a/block/backup.c +++ b/block/backup.c @@ -317,11 +317,12 @@ typedef struct { int ret; } BackupCompleteData; -static void backup_complete(BlockJob *job, void *opaque) +static void backup_complete(Job *job, void *opaque) { + BlockJob *bjob = container_of(job, BlockJob, job); BackupCompleteData *data = opaque; - block_job_completed(job, data->ret); + block_job_completed(bjob, data->ret); g_free(data); } @@ -519,7 +520,7 @@ static void coroutine_fn backup_run(void *opaque) data = g_malloc(sizeof(*data)); data->ret = ret; - block_job_defer_to_main_loop(&job->common, backup_complete, data); + job_defer_to_main_loop(&job->common.job, backup_complete, data); } static const BlockJobDriver backup_job_driver = { diff --git a/block/commit.c b/block/commit.c index 85baea8f92..d326766e4d 100644 --- a/block/commit.c +++ b/block/commit.c @@ -72,9 +72,10 @@ typedef struct { int ret; } CommitCompleteData; -static void commit_complete(BlockJob *job, void *opaque) +static void commit_complete(Job *job, void *opaque) { - CommitBlockJob *s = container_of(job, CommitBlockJob, common); + CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); + BlockJob *bjob = &s->common; CommitCompleteData *data = opaque; BlockDriverState *top = blk_bs(s->top); BlockDriverState *base = blk_bs(s->base); @@ -90,7 +91,7 @@ static void commit_complete(BlockJob *job, void *opaque) * the normal backing chain can be restored. */ blk_unref(s->base); - if (!job_is_cancelled(&s->common.job) && ret == 0) { + if (!job_is_cancelled(job) && ret == 0) { /* success */ ret = bdrv_drop_intermediate(s->commit_top_bs, base, s->backing_file_str); @@ -114,7 +115,7 @@ static void commit_complete(BlockJob *job, void *opaque) * block_job_finish_sync()), block_job_completed() won't free it and * therefore the blockers on the intermediate nodes remain. This would * cause bdrv_set_backing_hd() to fail. */ - block_job_remove_all_bdrv(job); + block_job_remove_all_bdrv(bjob); block_job_completed(&s->common, ret); g_free(data); @@ -211,7 +212,7 @@ out: data = g_malloc(sizeof(*data)); data->ret = ret; - block_job_defer_to_main_loop(&s->common, commit_complete, data); + job_defer_to_main_loop(&s->common.job, commit_complete, data); } static const BlockJobDriver commit_job_driver = { diff --git a/block/mirror.c b/block/mirror.c index 424072ed00..90d4ac9cb6 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -484,9 +484,10 @@ typedef struct { int ret; } MirrorExitData; -static void mirror_exit(BlockJob *job, void *opaque) +static void mirror_exit(Job *job, void *opaque) { - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); + BlockJob *bjob = &s->common; MirrorExitData *data = opaque; AioContext *replace_aio_context = NULL; BlockDriverState *src = s->source; @@ -568,7 +569,7 @@ static void mirror_exit(BlockJob *job, void *opaque) * the blockers on the intermediate nodes so that the resulting state is * valid. Also give up permissions on mirror_top_bs->backing, which might * block the removal. */ - block_job_remove_all_bdrv(job); + block_job_remove_all_bdrv(bjob); bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, &error_abort); bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort); @@ -576,9 +577,9 @@ static void mirror_exit(BlockJob *job, void *opaque) /* We just changed the BDS the job BB refers to (with either or both of the * bdrv_replace_node() calls), so switch the BB back so the cleanup does * the right thing. We don't need any permissions any more now. */ - blk_remove_bs(job->blk); - blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); - blk_insert_bs(job->blk, mirror_top_bs, &error_abort); + blk_remove_bs(bjob->blk); + blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); + blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); block_job_completed(&s->common, data->ret); @@ -901,7 +902,7 @@ immediate_exit: if (need_drain) { bdrv_drained_begin(bs); } - block_job_defer_to_main_loop(&s->common, mirror_exit, data); + job_defer_to_main_loop(&s->common.job, mirror_exit, data); } static void mirror_complete(BlockJob *job, Error **errp) diff --git a/block/stream.c b/block/stream.c index 22c71ae100..0bba81678c 100644 --- a/block/stream.c +++ b/block/stream.c @@ -58,16 +58,16 @@ typedef struct { int ret; } StreamCompleteData; -static void stream_complete(BlockJob *job, void *opaque) +static void stream_complete(Job *job, void *opaque) { - StreamBlockJob *s = container_of(job, StreamBlockJob, common); + StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); + BlockJob *bjob = &s->common; StreamCompleteData *data = opaque; - BlockDriverState *bs = blk_bs(job->blk); + BlockDriverState *bs = blk_bs(bjob->blk); BlockDriverState *base = s->base; Error *local_err = NULL; - if (!job_is_cancelled(&s->common.job) && bs->backing && - data->ret == 0) { + if (!job_is_cancelled(job) && bs->backing && data->ret == 0) { const char *base_id = NULL, *base_fmt = NULL; if (base) { base_id = s->backing_file_str; @@ -88,7 +88,7 @@ out: /* Reopen the image back in read-only mode if necessary */ if (s->bs_flags != bdrv_get_flags(bs)) { /* Give up write permissions before making it read-only */ - blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); + blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); bdrv_reopen(bs, s->bs_flags, NULL); } @@ -205,7 +205,7 @@ out: /* Modify backing chain and close BDSes in main loop */ data = g_malloc(sizeof(*data)); data->ret = ret; - block_job_defer_to_main_loop(&s->common, stream_complete, data); + job_defer_to_main_loop(&s->common.job, stream_complete, data); } static const BlockJobDriver stream_job_driver = { diff --git a/blockjob.c b/blockjob.c index 0a0b1c41dd..3ede511da0 100644 --- a/blockjob.c +++ b/blockjob.c @@ -360,7 +360,7 @@ static void block_job_decommission(BlockJob *job) job->completed = true; job->busy = false; job->paused = false; - job->deferred_to_main_loop = true; + job->job.deferred_to_main_loop = true; block_job_txn_del_job(job); job_state_transition(&job->job, JOB_STATUS_NULL); job_unref(&job->job); @@ -515,7 +515,7 @@ static int block_job_finish_sync(BlockJob *job, /* block_job_drain calls block_job_enter, and it should be enough to * induce progress until the job completes or moves to the main thread. */ - while (!job->deferred_to_main_loop && !job->completed) { + while (!job->job.deferred_to_main_loop && !job->completed) { block_job_drain(job); } while (!job->completed) { @@ -729,7 +729,7 @@ void block_job_cancel(BlockJob *job, bool force) block_job_cancel_async(job, force); if (!block_job_started(job)) { block_job_completed(job, -ECANCELED); - } else if (job->deferred_to_main_loop) { + } else if (job->job.deferred_to_main_loop) { block_job_completed_txn_abort(job); } else { block_job_enter(job); @@ -1045,7 +1045,7 @@ static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)) if (!block_job_started(job)) { return; } - if (job->deferred_to_main_loop) { + if (job->job.deferred_to_main_loop) { return; } @@ -1060,7 +1060,7 @@ static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)) return; } - assert(!job->deferred_to_main_loop); + assert(!job->job.deferred_to_main_loop); timer_del(&job->sleep_timer); job->busy = true; block_job_unlock(); @@ -1166,50 +1166,3 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, } return action; } - -typedef struct { - BlockJob *job; - AioContext *aio_context; - BlockJobDeferToMainLoopFn *fn; - void *opaque; -} BlockJobDeferToMainLoopData; - -static void block_job_defer_to_main_loop_bh(void *opaque) -{ - BlockJobDeferToMainLoopData *data = opaque; - AioContext *aio_context; - - /* Prevent race with block_job_defer_to_main_loop() */ - aio_context_acquire(data->aio_context); - - /* Fetch BDS AioContext again, in case it has changed */ - aio_context = blk_get_aio_context(data->job->blk); - if (aio_context != data->aio_context) { - aio_context_acquire(aio_context); - } - - data->fn(data->job, data->opaque); - - if (aio_context != data->aio_context) { - aio_context_release(aio_context); - } - - aio_context_release(data->aio_context); - - g_free(data); -} - -void block_job_defer_to_main_loop(BlockJob *job, - BlockJobDeferToMainLoopFn *fn, - void *opaque) -{ - BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data)); - data->job = job; - data->aio_context = blk_get_aio_context(job->blk); - data->fn = fn; - data->opaque = opaque; - job->deferred_to_main_loop = true; - - aio_bh_schedule_oneshot(qemu_get_aio_context(), - block_job_defer_to_main_loop_bh, data); -} diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 1e708f468a..2a9e865e31 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -92,11 +92,6 @@ typedef struct BlockJob { */ bool ready; - /** - * Set to true when the job has deferred work to the main loop. - */ - bool deferred_to_main_loop; - /** Status that is published by the query-block-jobs QMP API */ BlockDeviceIoStatus iostatus; diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index d64f30e6b0..0c2f8de381 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -233,23 +233,4 @@ void block_job_event_ready(BlockJob *job); BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, int is_read, int error); -typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque); - -/** - * block_job_defer_to_main_loop: - * @job: The job - * @fn: The function to run in the main loop - * @opaque: The opaque value that is passed to @fn - * - * This function must be called by the main job coroutine just before it - * returns. @fn is executed in the main loop with the BlockDriverState - * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and - * anything that uses bdrv_drain_all() in the main loop. - * - * The @job AioContext is held while @fn executes. - */ -void block_job_defer_to_main_loop(BlockJob *job, - BlockJobDeferToMainLoopFn *fn, - void *opaque); - #endif diff --git a/include/qemu/job.h b/include/qemu/job.h index 01e083f97a..933e0ab328 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -58,6 +58,9 @@ typedef struct Job { */ bool cancelled; + /** Set to true when the job has deferred work to the main loop. */ + bool deferred_to_main_loop; + /** Element of the list of jobs */ QLIST_ENTRY(Job) job_list; } Job; @@ -131,6 +134,23 @@ Job *job_get(const char *id); */ int job_apply_verb(Job *job, JobVerb verb, Error **errp); +typedef void JobDeferToMainLoopFn(Job *job, void *opaque); + +/** + * @job: The job + * @fn: The function to run in the main loop + * @opaque: The opaque value that is passed to @fn + * + * This function must be called by the main job coroutine just before it + * returns. @fn is executed in the main loop with the job AioContext acquired. + * + * Block jobs must call bdrv_unref(), bdrv_close(), and anything that uses + * bdrv_drain_all() in the main loop. + * + * The @job AioContext is held while @fn executes. + */ +void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); + /* TODO To be removed from the public interface */ void job_state_transition(Job *job, JobStatus s1); diff --git a/job.c b/job.c index 01074d0fae..c5a37fb8ef 100644 --- a/job.c +++ b/job.c @@ -28,6 +28,7 @@ #include "qapi/error.h" #include "qemu/job.h" #include "qemu/id.h" +#include "qemu/main-loop.h" #include "trace-root.h" static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); @@ -170,3 +171,34 @@ void job_unref(Job *job) g_free(job); } } + +typedef struct { + Job *job; + JobDeferToMainLoopFn *fn; + void *opaque; +} JobDeferToMainLoopData; + +static void job_defer_to_main_loop_bh(void *opaque) +{ + JobDeferToMainLoopData *data = opaque; + Job *job = data->job; + AioContext *aio_context = job->aio_context; + + aio_context_acquire(aio_context); + data->fn(data->job, data->opaque); + aio_context_release(aio_context); + + g_free(data); +} + +void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque) +{ + JobDeferToMainLoopData *data = g_malloc(sizeof(*data)); + data->job = job; + data->fn = fn; + data->opaque = opaque; + job->deferred_to_main_loop = true; + + aio_bh_schedule_oneshot(qemu_get_aio_context(), + job_defer_to_main_loop_bh, data); +} diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index f9e37d479c..4f8cba8377 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -496,9 +496,10 @@ typedef struct TestBlockJob { bool should_complete; } TestBlockJob; -static void test_job_completed(BlockJob *job, void *opaque) +static void test_job_completed(Job *job, void *opaque) { - block_job_completed(job, 0); + BlockJob *bjob = container_of(job, BlockJob, job); + block_job_completed(bjob, 0); } static void coroutine_fn test_job_start(void *opaque) @@ -510,7 +511,7 @@ static void coroutine_fn test_job_start(void *opaque) block_job_sleep_ns(&s->common, 100000); } - block_job_defer_to_main_loop(&s->common, test_job_completed, NULL); + job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); } static void test_job_complete(BlockJob *job, Error **errp) diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 26b4bbb230..c03f9662d8 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -24,16 +24,17 @@ typedef struct { int *result; } TestBlockJob; -static void test_block_job_complete(BlockJob *job, void *opaque) +static void test_block_job_complete(Job *job, void *opaque) { - BlockDriverState *bs = blk_bs(job->blk); + BlockJob *bjob = container_of(job, BlockJob, job); + BlockDriverState *bs = blk_bs(bjob->blk); int rc = (intptr_t)opaque; - if (job_is_cancelled(&job->job)) { + if (job_is_cancelled(job)) { rc = -ECANCELED; } - block_job_completed(job, rc); + block_job_completed(bjob, rc); bdrv_unref(bs); } @@ -54,8 +55,8 @@ static void coroutine_fn test_block_job_run(void *opaque) } } - block_job_defer_to_main_loop(job, test_block_job_complete, - (void *)(intptr_t)s->rc); + job_defer_to_main_loop(&job->job, test_block_job_complete, + (void *)(intptr_t)s->rc); } typedef struct { diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index fa31481537..5f43bd72a4 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -161,11 +161,12 @@ typedef struct CancelJob { bool completed; } CancelJob; -static void cancel_job_completed(BlockJob *job, void *opaque) +static void cancel_job_completed(Job *job, void *opaque) { + BlockJob *bjob = container_of(job, BlockJob, job); CancelJob *s = opaque; s->completed = true; - block_job_completed(job, 0); + block_job_completed(bjob, 0); } static void cancel_job_complete(BlockJob *job, Error **errp) @@ -191,7 +192,7 @@ static void coroutine_fn cancel_job_start(void *opaque) } defer: - block_job_defer_to_main_loop(&s->common, cancel_job_completed, s); + job_defer_to_main_loop(&s->common.job, cancel_job_completed, s); } static const BlockJobDriver test_cancel_driver = { From da01ff7f38f52791f93fc3ca59afcfbb220f15af Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 13 Apr 2018 17:31:02 +0200 Subject: [PATCH 0692/2380] job: Move coroutine and related code to Job This commit moves some core functions for dealing with the job coroutine from BlockJob to Job. This includes primarily entering the coroutine (both for the first and reentering) and yielding explicitly and at pause points. Signed-off-by: Kevin Wolf Reviewed-by: John Snow --- block/backup.c | 2 +- block/commit.c | 4 +- block/mirror.c | 22 ++-- block/replication.c | 2 +- block/stream.c | 4 +- blockdev.c | 8 +- blockjob.c | 219 +++++++---------------------------- include/block/blockjob.h | 40 ------- include/block/blockjob_int.h | 26 ----- include/qemu/job.h | 76 ++++++++++++ job.c | 137 ++++++++++++++++++++++ tests/test-bdrv-drain.c | 38 +++--- tests/test-blockjob-txn.c | 12 +- tests/test-blockjob.c | 14 +-- 14 files changed, 305 insertions(+), 299 deletions(-) diff --git a/block/backup.c b/block/backup.c index 22dd368c90..7d9aad9749 100644 --- a/block/backup.c +++ b/block/backup.c @@ -528,8 +528,8 @@ static const BlockJobDriver backup_job_driver = { .instance_size = sizeof(BackupBlockJob), .job_type = JOB_TYPE_BACKUP, .free = block_job_free, + .start = backup_run, }, - .start = backup_run, .commit = backup_commit, .abort = backup_abort, .clean = backup_clean, diff --git a/block/commit.c b/block/commit.c index d326766e4d..2fbc31077a 100644 --- a/block/commit.c +++ b/block/commit.c @@ -220,8 +220,8 @@ static const BlockJobDriver commit_job_driver = { .instance_size = sizeof(CommitBlockJob), .job_type = JOB_TYPE_COMMIT, .free = block_job_free, + .start = commit_run, }, - .start = commit_run, }; static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, @@ -371,7 +371,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, s->on_error = on_error; trace_commit_start(bs, base, top, s); - block_job_start(&s->common); + job_start(&s->common.job); return; fail: diff --git a/block/mirror.c b/block/mirror.c index 90d4ac9cb6..95fc8072b0 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -126,7 +126,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret) g_free(op); if (s->waiting_for_io) { - qemu_coroutine_enter(s->common.co); + qemu_coroutine_enter(s->common.job.co); } } @@ -345,7 +345,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) mirror_wait_for_io(s); } - block_job_pause_point(&s->common); + job_pause_point(&s->common.job); /* Find the number of consective dirty chunks following the first dirty * one, and wait for in flight requests in them. */ @@ -597,7 +597,7 @@ static void mirror_throttle(MirrorBlockJob *s) s->last_pause_ns = now; block_job_sleep_ns(&s->common, 0); } else { - block_job_pause_point(&s->common); + job_pause_point(&s->common.job); } } @@ -786,7 +786,7 @@ static void coroutine_fn mirror_run(void *opaque) goto immediate_exit; } - block_job_pause_point(&s->common); + job_pause_point(&s->common.job); cnt = bdrv_get_dirty_count(s->dirty_bitmap); /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is @@ -957,9 +957,9 @@ static void mirror_complete(BlockJob *job, Error **errp) block_job_enter(&s->common); } -static void mirror_pause(BlockJob *job) +static void mirror_pause(Job *job) { - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); mirror_wait_for_all_io(s); } @@ -991,10 +991,10 @@ static const BlockJobDriver mirror_job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = JOB_TYPE_MIRROR, .free = block_job_free, + .start = mirror_run, + .pause = mirror_pause, }, - .start = mirror_run, .complete = mirror_complete, - .pause = mirror_pause, .attached_aio_context = mirror_attached_aio_context, .drain = mirror_drain, }; @@ -1004,10 +1004,10 @@ static const BlockJobDriver commit_active_job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = JOB_TYPE_COMMIT, .free = block_job_free, + .start = mirror_run, + .pause = mirror_pause, }, - .start = mirror_run, .complete = mirror_complete, - .pause = mirror_pause, .attached_aio_context = mirror_attached_aio_context, .drain = mirror_drain, }; @@ -1244,7 +1244,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, } trace_mirror_start(bs, s, opaque); - block_job_start(&s->common); + job_start(&s->common.job); return; fail: diff --git a/block/replication.c b/block/replication.c index 48148b884a..9ed6e0fb04 100644 --- a/block/replication.c +++ b/block/replication.c @@ -576,7 +576,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, aio_context_release(aio_context); return; } - block_job_start(job); + job_start(&job->job); break; default: aio_context_release(aio_context); diff --git a/block/stream.c b/block/stream.c index 0bba81678c..6d8b7b6eee 100644 --- a/block/stream.c +++ b/block/stream.c @@ -213,8 +213,8 @@ static const BlockJobDriver stream_job_driver = { .instance_size = sizeof(StreamBlockJob), .job_type = JOB_TYPE_STREAM, .free = block_job_free, + .start = stream_run, }, - .start = stream_run, }; void stream_start(const char *job_id, BlockDriverState *bs, @@ -262,7 +262,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, s->on_error = on_error; trace_stream_start(bs, base, s); - block_job_start(&s->common); + job_start(&s->common.job); return; fail: diff --git a/blockdev.c b/blockdev.c index 3808b1fc00..c551fdf39a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1910,7 +1910,7 @@ static void drive_backup_commit(BlkActionState *common) aio_context_acquire(aio_context); assert(state->job); - block_job_start(state->job); + job_start(&state->job->job); aio_context_release(aio_context); } @@ -2008,7 +2008,7 @@ static void blockdev_backup_commit(BlkActionState *common) aio_context_acquire(aio_context); assert(state->job); - block_job_start(state->job); + job_start(&state->job->job); aio_context_release(aio_context); } @@ -3425,7 +3425,7 @@ void qmp_drive_backup(DriveBackup *arg, Error **errp) BlockJob *job; job = do_drive_backup(arg, NULL, errp); if (job) { - block_job_start(job); + job_start(&job->job); } } @@ -3513,7 +3513,7 @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp) BlockJob *job; job = do_blockdev_backup(arg, NULL, errp); if (job) { - block_job_start(job); + job_start(&job->job); } } diff --git a/blockjob.c b/blockjob.c index 3ede511da0..313b1ff7ce 100644 --- a/blockjob.c +++ b/blockjob.c @@ -36,30 +36,9 @@ #include "qemu/coroutine.h" #include "qemu/timer.h" -/* Right now, this mutex is only needed to synchronize accesses to job->busy - * and job->sleep_timer, such as concurrent calls to block_job_do_yield and - * block_job_enter. */ -static QemuMutex block_job_mutex; - -static void block_job_lock(void) -{ - qemu_mutex_lock(&block_job_mutex); -} - -static void block_job_unlock(void) -{ - qemu_mutex_unlock(&block_job_mutex); -} - -static void __attribute__((__constructor__)) block_job_init(void) -{ - qemu_mutex_init(&block_job_mutex); -} - static void block_job_event_cancelled(BlockJob *job); static void block_job_event_completed(BlockJob *job, const char *msg); static int block_job_event_pending(BlockJob *job); -static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)); /* Transactional group of block jobs */ struct BlockJobTxn { @@ -161,33 +140,27 @@ static void block_job_txn_del_job(BlockJob *job) } } -/* Assumes the block_job_mutex is held */ -static bool block_job_timer_pending(BlockJob *job) +/* Assumes the job_mutex is held */ +static bool job_timer_not_pending(Job *job) { - return timer_pending(&job->sleep_timer); -} - -/* Assumes the block_job_mutex is held */ -static bool block_job_timer_not_pending(BlockJob *job) -{ - return !block_job_timer_pending(job); + return !timer_pending(&job->sleep_timer); } static void block_job_pause(BlockJob *job) { - job->pause_count++; + job->job.pause_count++; } static void block_job_resume(BlockJob *job) { - assert(job->pause_count > 0); - job->pause_count--; - if (job->pause_count) { + assert(job->job.pause_count > 0); + job->job.pause_count--; + if (job->job.pause_count) { return; } /* kick only if no timer is pending */ - block_job_enter_cond(job, block_job_timer_not_pending); + job_enter_cond(&job->job, job_timer_not_pending); } static void block_job_attached_aio_context(AioContext *new_context, @@ -208,7 +181,7 @@ void block_job_free(Job *job) block_job_detach_aio_context, bjob); blk_unref(bjob->blk); error_free(bjob->blocker); - assert(!timer_pending(&bjob->sleep_timer)); + assert(!timer_pending(&bjob->job.sleep_timer)); } static void block_job_attached_aio_context(AioContext *new_context, @@ -226,7 +199,7 @@ static void block_job_attached_aio_context(AioContext *new_context, static void block_job_drain(BlockJob *job) { - /* If job is !job->busy this kicks it into the next pause point. */ + /* If job is !job->job.busy this kicks it into the next pause point. */ block_job_enter(job); blk_drain(job->blk); @@ -244,7 +217,7 @@ static void block_job_detach_aio_context(void *opaque) block_job_pause(job); - while (!job->paused && !job->completed) { + while (!job->job.paused && !job->completed) { block_job_drain(job); } @@ -312,29 +285,11 @@ bool block_job_is_internal(BlockJob *job) return (job->job.id == NULL); } -static bool block_job_started(BlockJob *job) -{ - return job->co; -} - const BlockJobDriver *block_job_driver(BlockJob *job) { return job->driver; } -/** - * All jobs must allow a pause point before entering their job proper. This - * ensures that jobs can be paused prior to being started, then resumed later. - */ -static void coroutine_fn block_job_co_entry(void *opaque) -{ - BlockJob *job = opaque; - - assert(job && job->driver && job->driver->start); - block_job_pause_point(job); - job->driver->start(job); -} - static void block_job_sleep_timer_cb(void *opaque) { BlockJob *job = opaque; @@ -342,24 +297,12 @@ static void block_job_sleep_timer_cb(void *opaque) block_job_enter(job); } -void block_job_start(BlockJob *job) -{ - assert(job && !block_job_started(job) && job->paused && - job->driver && job->driver->start); - job->co = qemu_coroutine_create(block_job_co_entry, job); - job->pause_count--; - job->busy = true; - job->paused = false; - job_state_transition(&job->job, JOB_STATUS_RUNNING); - bdrv_coroutine_enter(blk_bs(job->blk), job->co); -} - static void block_job_decommission(BlockJob *job) { assert(job); job->completed = true; - job->busy = false; - job->paused = false; + job->job.busy = false; + job->job.paused = false; job->job.deferred_to_main_loop = true; block_job_txn_del_job(job); job_state_transition(&job->job, JOB_STATUS_NULL); @@ -374,7 +317,7 @@ static void block_job_do_dismiss(BlockJob *job) static void block_job_conclude(BlockJob *job) { job_state_transition(&job->job, JOB_STATUS_CONCLUDED); - if (job->auto_dismiss || !block_job_started(job)) { + if (job->auto_dismiss || !job_started(&job->job)) { block_job_do_dismiss(job); } } @@ -439,7 +382,7 @@ static int block_job_finalize_single(BlockJob *job) } /* Emit events only if we actually started */ - if (block_job_started(job)) { + if (job_started(&job->job)) { if (job_is_cancelled(&job->job)) { block_job_event_cancelled(job); } else { @@ -464,7 +407,7 @@ static void block_job_cancel_async(BlockJob *job, bool force) if (job->user_paused) { /* Do not call block_job_enter here, the caller will handle it. */ job->user_paused = false; - job->pause_count--; + job->job.pause_count--; } job->job.cancelled = true; /* To prevent 'force == false' overriding a previous 'force == true' */ @@ -615,6 +558,12 @@ static void block_job_completed_txn_success(BlockJob *job) } } +/* Assumes the job_mutex is held */ +static bool job_timer_pending(Job *job) +{ + return timer_pending(&job->sleep_timer); +} + void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { int64_t old_speed = job->speed; @@ -635,7 +584,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) } /* kick only if a timer is pending */ - block_job_enter_cond(job, block_job_timer_pending); + job_enter_cond(&job->job, job_timer_pending); } int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) @@ -654,7 +603,7 @@ void block_job_complete(BlockJob *job, Error **errp) if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) { return; } - if (job->pause_count || job_is_cancelled(&job->job) || + if (job->job.pause_count || job_is_cancelled(&job->job) || !job->driver->complete) { error_setg(errp, "The active block job '%s' cannot be completed", @@ -708,7 +657,7 @@ bool block_job_user_paused(BlockJob *job) void block_job_user_resume(BlockJob *job, Error **errp) { assert(job); - if (!job->user_paused || job->pause_count <= 0) { + if (!job->user_paused || job->job.pause_count <= 0) { error_setg(errp, "Can't resume a job that was not paused"); return; } @@ -727,7 +676,7 @@ void block_job_cancel(BlockJob *job, bool force) return; } block_job_cancel_async(job, force); - if (!block_job_started(job)) { + if (!job_started(&job->job)) { block_job_completed(job, -ECANCELED); } else if (job->job.deferred_to_main_loop) { block_job_completed_txn_abort(job); @@ -797,8 +746,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info->type = g_strdup(job_type_str(&job->job)); info->device = g_strdup(job->job.id); info->len = job->len; - info->busy = atomic_read(&job->busy); - info->paused = job->pause_count > 0; + info->busy = atomic_read(&job->job.busy); + info->paused = job->job.pause_count > 0; info->offset = job->offset; info->speed = job->speed; info->io_status = job->iostatus; @@ -915,12 +864,9 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job->blk = blk; job->cb = cb; job->opaque = opaque; - job->busy = false; - job->paused = true; - job->pause_count = 1; job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); - aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, + aio_timer_init(qemu_get_aio_context(), &job->job.sleep_timer, QEMU_CLOCK_REALTIME, SCALE_NS, block_job_sleep_timer_cb, job); @@ -980,128 +926,41 @@ void block_job_completed(BlockJob *job, int ret) } } -static bool block_job_should_pause(BlockJob *job) -{ - return job->pause_count > 0; -} - -/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. - * Reentering the job coroutine with block_job_enter() before the timer has - * expired is allowed and cancels the timer. - * - * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be - * called explicitly. */ -static void block_job_do_yield(BlockJob *job, uint64_t ns) -{ - block_job_lock(); - if (ns != -1) { - timer_mod(&job->sleep_timer, ns); - } - job->busy = false; - block_job_unlock(); - qemu_coroutine_yield(); - - /* Set by block_job_enter before re-entering the coroutine. */ - assert(job->busy); -} - -void coroutine_fn block_job_pause_point(BlockJob *job) -{ - assert(job && block_job_started(job)); - - if (!block_job_should_pause(job)) { - return; - } - if (job_is_cancelled(&job->job)) { - return; - } - - if (job->driver->pause) { - job->driver->pause(job); - } - - if (block_job_should_pause(job) && !job_is_cancelled(&job->job)) { - JobStatus status = job->job.status; - job_state_transition(&job->job, status == JOB_STATUS_READY - ? JOB_STATUS_STANDBY - : JOB_STATUS_PAUSED); - job->paused = true; - block_job_do_yield(job, -1); - job->paused = false; - job_state_transition(&job->job, status); - } - - if (job->driver->resume) { - job->driver->resume(job); - } -} - -/* - * Conditionally enter a block_job pending a call to fn() while - * under the block_job_lock critical section. - */ -static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)) -{ - if (!block_job_started(job)) { - return; - } - if (job->job.deferred_to_main_loop) { - return; - } - - block_job_lock(); - if (job->busy) { - block_job_unlock(); - return; - } - - if (fn && !fn(job)) { - block_job_unlock(); - return; - } - - assert(!job->job.deferred_to_main_loop); - timer_del(&job->sleep_timer); - job->busy = true; - block_job_unlock(); - aio_co_wake(job->co); -} - void block_job_enter(BlockJob *job) { - block_job_enter_cond(job, NULL); + job_enter_cond(&job->job, NULL); } void block_job_sleep_ns(BlockJob *job, int64_t ns) { - assert(job->busy); + assert(job->job.busy); /* Check cancellation *before* setting busy = false, too! */ if (job_is_cancelled(&job->job)) { return; } - if (!block_job_should_pause(job)) { - block_job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); + if (!job_should_pause(&job->job)) { + job_do_yield(&job->job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); } - block_job_pause_point(job); + job_pause_point(&job->job); } void block_job_yield(BlockJob *job) { - assert(job->busy); + assert(job->job.busy); /* Check cancellation *before* setting busy = false, too! */ if (job_is_cancelled(&job->job)) { return; } - if (!block_job_should_pause(job)) { - block_job_do_yield(job, -1); + if (!job_should_pause(&job->job)) { + job_do_yield(&job->job, -1); } - block_job_pause_point(job); + job_pause_point(&job->job); } void block_job_iostatus_reset(BlockJob *job) @@ -1109,7 +968,7 @@ void block_job_iostatus_reset(BlockJob *job) if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { return; } - assert(job->user_paused && job->pause_count > 0); + assert(job->user_paused && job->job.pause_count > 0); job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; } diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 2a9e865e31..b60d919fbf 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -50,43 +50,18 @@ typedef struct BlockJob { /** The block device on which the job is operating. */ BlockBackend *blk; - /** - * The coroutine that executes the job. If not NULL, it is - * reentered when busy is false and the job is cancelled. - */ - Coroutine *co; - /** * Set to true if the job should abort immediately without waiting * for data to be in sync. */ bool force; - /** - * Counter for pause request. If non-zero, the block job is either paused, - * or if busy == true will pause itself as soon as possible. - */ - int pause_count; - /** * Set to true if the job is paused by user. Can be unpaused with the * block-job-resume QMP command. */ bool user_paused; - /** - * Set to false by the job while the coroutine has yielded and may be - * re-entered by block_job_enter(). There may still be I/O or event loop - * activity pending. Accessed under block_job_mutex (in blockjob.c). - */ - bool busy; - - /** - * Set to true by the job while it is in a quiescent state, where - * no I/O or event loop activity is pending. - */ - bool paused; - /** * Set to true when the job is ready to be completed. */ @@ -125,12 +100,6 @@ typedef struct BlockJob { /** ret code passed to block_job_completed. */ int ret; - /** - * Timer that is used by @block_job_sleep_ns. Accessed under - * block_job_mutex (in blockjob.c). - */ - QEMUTimer sleep_timer; - /** True if this job should automatically finalize itself */ bool auto_finalize; @@ -207,15 +176,6 @@ void block_job_remove_all_bdrv(BlockJob *job); */ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); -/** - * block_job_start: - * @job: A job that has not yet been started. - * - * Begins execution of a block job. - * Takes ownership of one reference to the job object. - */ -void block_job_start(BlockJob *job); - /** * block_job_cancel: * @job: The job to be canceled. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 0c2f8de381..0a614a89b8 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -38,9 +38,6 @@ struct BlockJobDriver { /** Generic JobDriver callbacks and settings */ JobDriver job_driver; - /** Mandatory: Entrypoint for the Coroutine. */ - CoroutineEntry *start; - /** * Optional callback for job types whose completion must be triggered * manually. @@ -85,20 +82,6 @@ struct BlockJobDriver { */ void (*clean)(BlockJob *job); - /** - * If the callback is not NULL, it will be invoked when the job transitions - * into the paused state. Paused jobs must not perform any asynchronous - * I/O or event loop activity. This callback is used to quiesce jobs. - */ - void coroutine_fn (*pause)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked when the job transitions - * out of the paused state. Any asynchronous I/O or event loop activity - * should be restarted from this callback. - */ - void coroutine_fn (*resume)(BlockJob *job); - /* * If the callback is not NULL, it will be invoked before the job is * resumed in a new AioContext. This is the place to move any resources @@ -195,15 +178,6 @@ void block_job_early_fail(BlockJob *job); */ void block_job_completed(BlockJob *job, int ret); -/** - * block_job_pause_point: - * @job: The job that is ready to pause. - * - * Pause now if block_job_pause() has been called. Block jobs that perform - * lots of I/O must call this between requests so that the job can be paused. - */ -void coroutine_fn block_job_pause_point(BlockJob *job); - /** * block_job_enter: * @job: The job to enter. diff --git a/include/qemu/job.h b/include/qemu/job.h index 933e0ab328..9dcff12283 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -28,6 +28,7 @@ #include "qapi/qapi-types-block-core.h" #include "qemu/queue.h" +#include "qemu/coroutine.h" typedef struct JobDriver JobDriver; @@ -50,6 +51,37 @@ typedef struct Job { /** AioContext to run the job coroutine in */ AioContext *aio_context; + /** + * The coroutine that executes the job. If not NULL, it is reentered when + * busy is false and the job is cancelled. + */ + Coroutine *co; + + /** + * Timer that is used by @block_job_sleep_ns. Accessed under job_mutex (in + * job.c). + */ + QEMUTimer sleep_timer; + + /** + * Counter for pause request. If non-zero, the block job is either paused, + * or if busy == true will pause itself as soon as possible. + */ + int pause_count; + + /** + * Set to false by the job while the coroutine has yielded and may be + * re-entered by block_job_enter(). There may still be I/O or event loop + * activity pending. Accessed under block_job_mutex (in blockjob.c). + */ + bool busy; + + /** + * Set to true by the job while it is in a quiescent state, where + * no I/O or event loop activity is pending. + */ + bool paused; + /** * Set to true if the job should cancel itself. The flag must * always be tested just before toggling the busy flag from false @@ -75,6 +107,23 @@ struct JobDriver { /** Enum describing the operation */ JobType job_type; + /** Mandatory: Entrypoint for the Coroutine. */ + CoroutineEntry *start; + + /** + * If the callback is not NULL, it will be invoked when the job transitions + * into the paused state. Paused jobs must not perform any asynchronous + * I/O or event loop activity. This callback is used to quiesce jobs. + */ + void coroutine_fn (*pause)(Job *job); + + /** + * If the callback is not NULL, it will be invoked when the job transitions + * out of the paused state. Any asynchronous I/O or event loop activity + * should be restarted from this callback. + */ + void coroutine_fn (*resume)(Job *job); + /** Called when the job is freed */ void (*free)(Job *job); }; @@ -103,6 +152,30 @@ void job_ref(Job *job); */ void job_unref(Job *job); +/** + * Conditionally enter the job coroutine if the job is ready to run, not + * already busy and fn() returns true. fn() is called while under the job_lock + * critical section. + */ +void job_enter_cond(Job *job, bool(*fn)(Job *job)); + +/** + * @job: A job that has not yet been started. + * + * Begins execution of a job. + * Takes ownership of one reference to the job object. + */ +void job_start(Job *job); + +/** + * @job: The job that is ready to pause. + * + * Pause now if job_pause() has been called. Jobs that perform lots of I/O + * must call this between requests so that the job can be paused. + */ +void coroutine_fn job_pause_point(Job *job); + + /** Returns the JobType of a given Job. */ JobType job_type(const Job *job); @@ -153,5 +226,8 @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); /* TODO To be removed from the public interface */ void job_state_transition(Job *job, JobStatus s1); +void coroutine_fn job_do_yield(Job *job, uint64_t ns); +bool job_should_pause(Job *job); +bool job_started(Job *job); #endif diff --git a/job.c b/job.c index c5a37fb8ef..78497fd6f5 100644 --- a/job.c +++ b/job.c @@ -60,6 +60,26 @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, }; +/* Right now, this mutex is only needed to synchronize accesses to job->busy + * and job->sleep_timer, such as concurrent calls to job_do_yield and + * job_enter. */ +static QemuMutex job_mutex; + +static void job_lock(void) +{ + qemu_mutex_lock(&job_mutex); +} + +static void job_unlock(void) +{ + qemu_mutex_unlock(&job_mutex); +} + +static void __attribute__((__constructor__)) job_init(void) +{ + qemu_mutex_init(&job_mutex); +} + /* TODO Make static once the whole state machine is in job.c */ void job_state_transition(Job *job, JobStatus s1) { @@ -101,6 +121,16 @@ bool job_is_cancelled(Job *job) return job->cancelled; } +bool job_started(Job *job) +{ + return job->co; +} + +bool job_should_pause(Job *job) +{ + return job->pause_count > 0; +} + Job *job_next(Job *job) { if (!job) { @@ -143,6 +173,9 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, job->id = g_strdup(job_id); job->refcnt = 1; job->aio_context = ctx; + job->busy = false; + job->paused = true; + job->pause_count = 1; job_state_transition(job, JOB_STATUS_CREATED); @@ -172,6 +205,110 @@ void job_unref(Job *job) } } +void job_enter_cond(Job *job, bool(*fn)(Job *job)) +{ + if (!job_started(job)) { + return; + } + if (job->deferred_to_main_loop) { + return; + } + + job_lock(); + if (job->busy) { + job_unlock(); + return; + } + + if (fn && !fn(job)) { + job_unlock(); + return; + } + + assert(!job->deferred_to_main_loop); + timer_del(&job->sleep_timer); + job->busy = true; + job_unlock(); + aio_co_wake(job->co); +} + +/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. + * Reentering the job coroutine with block_job_enter() before the timer has + * expired is allowed and cancels the timer. + * + * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be + * called explicitly. */ +void coroutine_fn job_do_yield(Job *job, uint64_t ns) +{ + job_lock(); + if (ns != -1) { + timer_mod(&job->sleep_timer, ns); + } + job->busy = false; + job_unlock(); + qemu_coroutine_yield(); + + /* Set by job_enter_cond() before re-entering the coroutine. */ + assert(job->busy); +} + +void coroutine_fn job_pause_point(Job *job) +{ + assert(job && job_started(job)); + + if (!job_should_pause(job)) { + return; + } + if (job_is_cancelled(job)) { + return; + } + + if (job->driver->pause) { + job->driver->pause(job); + } + + if (job_should_pause(job) && !job_is_cancelled(job)) { + JobStatus status = job->status; + job_state_transition(job, status == JOB_STATUS_READY + ? JOB_STATUS_STANDBY + : JOB_STATUS_PAUSED); + job->paused = true; + job_do_yield(job, -1); + job->paused = false; + job_state_transition(job, status); + } + + if (job->driver->resume) { + job->driver->resume(job); + } +} + +/** + * All jobs must allow a pause point before entering their job proper. This + * ensures that jobs can be paused prior to being started, then resumed later. + */ +static void coroutine_fn job_co_entry(void *opaque) +{ + Job *job = opaque; + + assert(job && job->driver && job->driver->start); + job_pause_point(job); + job->driver->start(job); +} + + +void job_start(Job *job) +{ + assert(job && !job_started(job) && job->paused && + job->driver && job->driver->start); + job->co = qemu_coroutine_create(job_co_entry, job); + job->pause_count--; + job->busy = true; + job->paused = false; + job_state_transition(job, JOB_STATUS_RUNNING); + aio_co_enter(job->aio_context, job->co); +} + typedef struct { Job *job; JobDeferToMainLoopFn *fn; diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 4f8cba8377..c9f2f9b183 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -524,8 +524,8 @@ BlockJobDriver test_job_driver = { .job_driver = { .instance_size = sizeof(TestBlockJob), .free = block_job_free, + .start = test_job_start, }, - .start = test_job_start, .complete = test_job_complete, }; @@ -549,47 +549,47 @@ static void test_blockjob_common(enum drain_type drain_type) job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); - block_job_start(job); + job_start(&job->job); - g_assert_cmpint(job->pause_count, ==, 0); - g_assert_false(job->paused); - g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ do_drain_begin(drain_type, src); if (drain_type == BDRV_DRAIN_ALL) { /* bdrv_drain_all() drains both src and target */ - g_assert_cmpint(job->pause_count, ==, 2); + g_assert_cmpint(job->job.pause_count, ==, 2); } else { - g_assert_cmpint(job->pause_count, ==, 1); + g_assert_cmpint(job->job.pause_count, ==, 1); } /* XXX We don't wait until the job is actually paused. Is this okay? */ - /* g_assert_true(job->paused); */ - g_assert_false(job->busy); /* The job is paused */ + /* g_assert_true(job->job.paused); */ + g_assert_false(job->job.busy); /* The job is paused */ do_drain_end(drain_type, src); - g_assert_cmpint(job->pause_count, ==, 0); - g_assert_false(job->paused); - g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ do_drain_begin(drain_type, target); if (drain_type == BDRV_DRAIN_ALL) { /* bdrv_drain_all() drains both src and target */ - g_assert_cmpint(job->pause_count, ==, 2); + g_assert_cmpint(job->job.pause_count, ==, 2); } else { - g_assert_cmpint(job->pause_count, ==, 1); + g_assert_cmpint(job->job.pause_count, ==, 1); } /* XXX We don't wait until the job is actually paused. Is this okay? */ - /* g_assert_true(job->paused); */ - g_assert_false(job->busy); /* The job is paused */ + /* g_assert_true(job->job.paused); */ + g_assert_false(job->job.busy); /* The job is paused */ do_drain_end(drain_type, target); - g_assert_cmpint(job->pause_count, ==, 0); - g_assert_false(job->paused); - g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ ret = block_job_complete_sync(job, &error_abort); g_assert_cmpint(ret, ==, 0); diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index c03f9662d8..323e154a00 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -78,8 +78,8 @@ static const BlockJobDriver test_block_job_driver = { .job_driver = { .instance_size = sizeof(TestBlockJob), .free = block_job_free, + .start = test_block_job_run, }, - .start = test_block_job_run, }; /* Create a block job that completes with a given return code after a given @@ -125,7 +125,7 @@ static void test_single_job(int expected) txn = block_job_txn_new(); job = test_block_job_start(1, true, expected, &result, txn); - block_job_start(job); + job_start(&job->job); if (expected == -ECANCELED) { block_job_cancel(job, false); @@ -165,8 +165,8 @@ static void test_pair_jobs(int expected1, int expected2) txn = block_job_txn_new(); job1 = test_block_job_start(1, true, expected1, &result1, txn); job2 = test_block_job_start(2, true, expected2, &result2, txn); - block_job_start(job1); - block_job_start(job2); + job_start(&job1->job); + job_start(&job2->job); /* Release our reference now to trigger as many nice * use-after-free bugs as possible. @@ -227,8 +227,8 @@ static void test_pair_jobs_fail_cancel_race(void) txn = block_job_txn_new(); job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); job2 = test_block_job_start(2, false, 0, &result2, txn); - block_job_start(job1); - block_job_start(job2); + job_start(&job1->job); + job_start(&job2->job); block_job_cancel(job1, false); diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 5f43bd72a4..1d18325feb 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -199,8 +199,8 @@ static const BlockJobDriver test_cancel_driver = { .job_driver = { .instance_size = sizeof(CancelJob), .free = block_job_free, + .start = cancel_job_start, }, - .start = cancel_job_start, .complete = cancel_job_complete, }; @@ -254,7 +254,7 @@ static void test_cancel_running(void) s = create_common(&job); - block_job_start(job); + job_start(&job->job); assert(job->job.status == JOB_STATUS_RUNNING); cancel_common(s); @@ -267,7 +267,7 @@ static void test_cancel_paused(void) s = create_common(&job); - block_job_start(job); + job_start(&job->job); assert(job->job.status == JOB_STATUS_RUNNING); block_job_user_pause(job, &error_abort); @@ -284,7 +284,7 @@ static void test_cancel_ready(void) s = create_common(&job); - block_job_start(job); + job_start(&job->job); assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; @@ -301,7 +301,7 @@ static void test_cancel_standby(void) s = create_common(&job); - block_job_start(job); + job_start(&job->job); assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; @@ -322,7 +322,7 @@ static void test_cancel_pending(void) s = create_common(&job); - block_job_start(job); + job_start(&job->job); assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; @@ -346,7 +346,7 @@ static void test_cancel_concluded(void) s = create_common(&job); - block_job_start(job); + job_start(&job->job); assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; From 5d43e86e11f488fda7956b13160e0c0105a84845 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 18 Apr 2018 16:32:20 +0200 Subject: [PATCH 0693/2380] job: Add job_sleep_ns() There is nothing block layer specific about block_job_sleep_ns(), so move the function to Job. Signed-off-by: Kevin Wolf Reviewed-by: John Snow Reviewed-by: Max Reitz --- block/backup.c | 2 +- block/commit.c | 2 +- block/mirror.c | 4 ++-- block/stream.c | 2 +- blockjob.c | 27 --------------------------- include/block/blockjob_int.h | 11 ----------- include/qemu/job.h | 19 ++++++++++++++++++- job.c | 32 ++++++++++++++++++++++++++++++++ tests/test-bdrv-drain.c | 8 ++++---- tests/test-blockjob-txn.c | 2 +- tests/test-blockjob.c | 2 +- 11 files changed, 61 insertions(+), 50 deletions(-) diff --git a/block/backup.c b/block/backup.c index 7d9aad9749..f3a4f7c898 100644 --- a/block/backup.c +++ b/block/backup.c @@ -338,7 +338,7 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) * return. Without a yield, the VM would not reboot. */ delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); job->bytes_read = 0; - block_job_sleep_ns(&job->common, delay_ns); + job_sleep_ns(&job->common.job, delay_ns); if (job_is_cancelled(&job->common.job)) { return true; diff --git a/block/commit.c b/block/commit.c index 2fbc31077a..1c6cb6c298 100644 --- a/block/commit.c +++ b/block/commit.c @@ -172,7 +172,7 @@ static void coroutine_fn commit_run(void *opaque) /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. */ - block_job_sleep_ns(&s->common, delay_ns); + job_sleep_ns(&s->common.job, delay_ns); if (job_is_cancelled(&s->common.job)) { break; } diff --git a/block/mirror.c b/block/mirror.c index 95fc8072b0..5d8f75c677 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -595,7 +595,7 @@ static void mirror_throttle(MirrorBlockJob *s) if (now - s->last_pause_ns > BLOCK_JOB_SLICE_TIME) { s->last_pause_ns = now; - block_job_sleep_ns(&s->common, 0); + job_sleep_ns(&s->common.job, 0); } else { job_pause_point(&s->common.job); } @@ -869,7 +869,7 @@ static void coroutine_fn mirror_run(void *opaque) cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0); } trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); - block_job_sleep_ns(&s->common, delay_ns); + job_sleep_ns(&s->common.job, delay_ns); if (job_is_cancelled(&s->common.job) && (!s->synced || s->common.force)) { diff --git a/block/stream.c b/block/stream.c index 6d8b7b6eee..1faab02086 100644 --- a/block/stream.c +++ b/block/stream.c @@ -140,7 +140,7 @@ static void coroutine_fn stream_run(void *opaque) /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. */ - block_job_sleep_ns(&s->common, delay_ns); + job_sleep_ns(&s->common.job, delay_ns); if (job_is_cancelled(&s->common.job)) { break; } diff --git a/blockjob.c b/blockjob.c index 313b1ff7ce..4dc360c794 100644 --- a/blockjob.c +++ b/blockjob.c @@ -181,7 +181,6 @@ void block_job_free(Job *job) block_job_detach_aio_context, bjob); blk_unref(bjob->blk); error_free(bjob->blocker); - assert(!timer_pending(&bjob->job.sleep_timer)); } static void block_job_attached_aio_context(AioContext *new_context, @@ -290,13 +289,6 @@ const BlockJobDriver *block_job_driver(BlockJob *job) return job->driver; } -static void block_job_sleep_timer_cb(void *opaque) -{ - BlockJob *job = opaque; - - block_job_enter(job); -} - static void block_job_decommission(BlockJob *job) { assert(job); @@ -866,9 +858,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job->opaque = opaque; job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); - aio_timer_init(qemu_get_aio_context(), &job->job.sleep_timer, - QEMU_CLOCK_REALTIME, SCALE_NS, - block_job_sleep_timer_cb, job); error_setg(&job->blocker, "block device is in use by block job: %s", job_type_str(&job->job)); @@ -931,22 +920,6 @@ void block_job_enter(BlockJob *job) job_enter_cond(&job->job, NULL); } -void block_job_sleep_ns(BlockJob *job, int64_t ns) -{ - assert(job->job.busy); - - /* Check cancellation *before* setting busy = false, too! */ - if (job_is_cancelled(&job->job)) { - return; - } - - if (!job_should_pause(&job->job)) { - job_do_yield(&job->job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); - } - - job_pause_point(&job->job); -} - void block_job_yield(BlockJob *job) { assert(job->job.busy); diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 0a614a89b8..8937f5b163 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -133,17 +133,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, */ void block_job_free(Job *job); -/** - * block_job_sleep_ns: - * @job: The job that calls the function. - * @ns: How many nanoseconds to stop for. - * - * Put the job to sleep (assuming that it wasn't canceled) for @ns - * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately - * interrupt the wait. - */ -void block_job_sleep_ns(BlockJob *job, int64_t ns); - /** * block_job_yield: * @job: The job that calls the function. diff --git a/include/qemu/job.h b/include/qemu/job.h index 9dcff12283..509408f747 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -58,7 +58,7 @@ typedef struct Job { Coroutine *co; /** - * Timer that is used by @block_job_sleep_ns. Accessed under job_mutex (in + * Timer that is used by @job_sleep_ns. Accessed under job_mutex (in * job.c). */ QEMUTimer sleep_timer; @@ -167,6 +167,13 @@ void job_enter_cond(Job *job, bool(*fn)(Job *job)); */ void job_start(Job *job); +/** + * @job: The job to enter. + * + * Continue the specified job by entering the coroutine. + */ +void job_enter(Job *job); + /** * @job: The job that is ready to pause. * @@ -175,6 +182,16 @@ void job_start(Job *job); */ void coroutine_fn job_pause_point(Job *job); +/** + * @job: The job that calls the function. + * @ns: How many nanoseconds to stop for. + * + * Put the job to sleep (assuming that it wasn't canceled) for @ns + * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately + * interrupt the wait. + */ +void coroutine_fn job_sleep_ns(Job *job, int64_t ns); + /** Returns the JobType of a given Job. */ JobType job_type(const Job *job); diff --git a/job.c b/job.c index 78497fd6f5..1b8cba15ff 100644 --- a/job.c +++ b/job.c @@ -152,6 +152,13 @@ Job *job_get(const char *id) return NULL; } +static void job_sleep_timer_cb(void *opaque) +{ + Job *job = opaque; + + job_enter(job); +} + void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, Error **errp) { @@ -178,6 +185,9 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, job->pause_count = 1; job_state_transition(job, JOB_STATUS_CREATED); + aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, + QEMU_CLOCK_REALTIME, SCALE_NS, + job_sleep_timer_cb, job); QLIST_INSERT_HEAD(&jobs, job, job_list); @@ -193,6 +203,7 @@ void job_unref(Job *job) { if (--job->refcnt == 0) { assert(job->status == JOB_STATUS_NULL); + assert(!timer_pending(&job->sleep_timer)); if (job->driver->free) { job->driver->free(job); @@ -232,6 +243,11 @@ void job_enter_cond(Job *job, bool(*fn)(Job *job)) aio_co_wake(job->co); } +void job_enter(Job *job) +{ + job_enter_cond(job, NULL); +} + /* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. * Reentering the job coroutine with block_job_enter() before the timer has * expired is allowed and cancels the timer. @@ -283,6 +299,22 @@ void coroutine_fn job_pause_point(Job *job) } } +void coroutine_fn job_sleep_ns(Job *job, int64_t ns) +{ + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (job_is_cancelled(job)) { + return; + } + + if (!job_should_pause(job)) { + job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); + } + + job_pause_point(job); +} + /** * All jobs must allow a pause point before entering their job proper. This * ensures that jobs can be paused prior to being started, then resumed later. diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index c9f2f9b183..50232f5eaf 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -508,7 +508,7 @@ static void coroutine_fn test_job_start(void *opaque) block_job_event_ready(&s->common); while (!s->should_complete) { - block_job_sleep_ns(&s->common, 100000); + job_sleep_ns(&s->common.job, 100000); } job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); @@ -553,7 +553,7 @@ static void test_blockjob_common(enum drain_type drain_type) g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ + g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ do_drain_begin(drain_type, src); @@ -571,7 +571,7 @@ static void test_blockjob_common(enum drain_type drain_type) g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ + g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ do_drain_begin(drain_type, target); @@ -589,7 +589,7 @@ static void test_blockjob_common(enum drain_type drain_type) g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */ + g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ ret = block_job_complete_sync(job, &error_abort); g_assert_cmpint(ret, ==, 0); diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 323e154a00..0e6162bc71 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -45,7 +45,7 @@ static void coroutine_fn test_block_job_run(void *opaque) while (s->iterations--) { if (s->use_timer) { - block_job_sleep_ns(job, 0); + job_sleep_ns(&job->job, 0); } else { block_job_yield(job); } diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 1d18325feb..b329bd5274 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -188,7 +188,7 @@ static void coroutine_fn cancel_job_start(void *opaque) block_job_event_ready(&s->common); } - block_job_sleep_ns(&s->common, 100000); + job_sleep_ns(&s->common.job, 100000); } defer: From b15de82867975e0b4acf644b5ee36d84904b6612 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 18 Apr 2018 17:10:26 +0200 Subject: [PATCH 0694/2380] job: Move pause/resume functions to Job While we already moved the state related to job pausing to Job, the functions to do were still BlockJob only. This commit moves them over to Job. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- block/backup.c | 1 + block/commit.c | 1 + block/mirror.c | 2 + block/stream.c | 1 + blockdev.c | 6 +-- blockjob.c | 81 +++++++----------------------------- include/block/blockjob.h | 32 -------------- include/block/blockjob_int.h | 7 ++++ include/qemu/job.h | 37 ++++++++++++++++ job.c | 59 ++++++++++++++++++++++++++ tests/test-bdrv-drain.c | 1 + tests/test-blockjob-txn.c | 1 + tests/test-blockjob.c | 6 ++- 13 files changed, 133 insertions(+), 102 deletions(-) diff --git a/block/backup.c b/block/backup.c index f3a4f7c898..4d011d5a5c 100644 --- a/block/backup.c +++ b/block/backup.c @@ -528,6 +528,7 @@ static const BlockJobDriver backup_job_driver = { .instance_size = sizeof(BackupBlockJob), .job_type = JOB_TYPE_BACKUP, .free = block_job_free, + .user_resume = block_job_user_resume, .start = backup_run, }, .commit = backup_commit, diff --git a/block/commit.c b/block/commit.c index 1c6cb6c298..c4a98e5804 100644 --- a/block/commit.c +++ b/block/commit.c @@ -220,6 +220,7 @@ static const BlockJobDriver commit_job_driver = { .instance_size = sizeof(CommitBlockJob), .job_type = JOB_TYPE_COMMIT, .free = block_job_free, + .user_resume = block_job_user_resume, .start = commit_run, }, }; diff --git a/block/mirror.c b/block/mirror.c index 5d8f75c677..9a7226f38c 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -991,6 +991,7 @@ static const BlockJobDriver mirror_job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = JOB_TYPE_MIRROR, .free = block_job_free, + .user_resume = block_job_user_resume, .start = mirror_run, .pause = mirror_pause, }, @@ -1004,6 +1005,7 @@ static const BlockJobDriver commit_active_job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = JOB_TYPE_COMMIT, .free = block_job_free, + .user_resume = block_job_user_resume, .start = mirror_run, .pause = mirror_pause, }, diff --git a/block/stream.c b/block/stream.c index 1faab02086..e81b488a22 100644 --- a/block/stream.c +++ b/block/stream.c @@ -214,6 +214,7 @@ static const BlockJobDriver stream_job_driver = { .job_type = JOB_TYPE_STREAM, .free = block_job_free, .start = stream_run, + .user_resume = block_job_user_resume, }, }; diff --git a/blockdev.c b/blockdev.c index c551fdf39a..3533c0dd6a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3844,7 +3844,7 @@ void qmp_block_job_cancel(const char *device, force = false; } - if (block_job_user_paused(job) && !force) { + if (job_user_paused(&job->job) && !force) { error_setg(errp, "The block job for device '%s' is currently paused", device); goto out; @@ -3866,7 +3866,7 @@ void qmp_block_job_pause(const char *device, Error **errp) } trace_qmp_block_job_pause(job); - block_job_user_pause(job, errp); + job_user_pause(&job->job, errp); aio_context_release(aio_context); } @@ -3880,7 +3880,7 @@ void qmp_block_job_resume(const char *device, Error **errp) } trace_qmp_block_job_resume(job); - block_job_user_resume(job, errp); + job_user_resume(&job->job, errp); aio_context_release(aio_context); } diff --git a/blockjob.c b/blockjob.c index 4dc360c794..6334a54f50 100644 --- a/blockjob.c +++ b/blockjob.c @@ -140,29 +140,6 @@ static void block_job_txn_del_job(BlockJob *job) } } -/* Assumes the job_mutex is held */ -static bool job_timer_not_pending(Job *job) -{ - return !timer_pending(&job->sleep_timer); -} - -static void block_job_pause(BlockJob *job) -{ - job->job.pause_count++; -} - -static void block_job_resume(BlockJob *job) -{ - assert(job->job.pause_count > 0); - job->job.pause_count--; - if (job->job.pause_count) { - return; - } - - /* kick only if no timer is pending */ - job_enter_cond(&job->job, job_timer_not_pending); -} - static void block_job_attached_aio_context(AioContext *new_context, void *opaque); static void block_job_detach_aio_context(void *opaque); @@ -193,7 +170,7 @@ static void block_job_attached_aio_context(AioContext *new_context, job->driver->attached_aio_context(job, new_context); } - block_job_resume(job); + job_resume(&job->job); } static void block_job_drain(BlockJob *job) @@ -214,7 +191,7 @@ static void block_job_detach_aio_context(void *opaque) /* In case the job terminates during aio_poll()... */ job_ref(&job->job); - block_job_pause(job); + job_pause(&job->job); while (!job->job.paused && !job->completed) { block_job_drain(job); @@ -233,13 +210,13 @@ static char *child_job_get_parent_desc(BdrvChild *c) static void child_job_drained_begin(BdrvChild *c) { BlockJob *job = c->opaque; - block_job_pause(job); + job_pause(&job->job); } static void child_job_drained_end(BdrvChild *c) { BlockJob *job = c->opaque; - block_job_resume(job); + job_resume(&job->job); } static const BdrvChildRole child_job = { @@ -396,9 +373,9 @@ static void block_job_cancel_async(BlockJob *job, bool force) if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) { block_job_iostatus_reset(job); } - if (job->user_paused) { + if (job->job.user_paused) { /* Do not call block_job_enter here, the caller will handle it. */ - job->user_paused = false; + job->job.user_paused = false; job->job.pause_count--; } job->job.cancelled = true; @@ -628,39 +605,6 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) *jobptr = NULL; } -void block_job_user_pause(BlockJob *job, Error **errp) -{ - if (job_apply_verb(&job->job, JOB_VERB_PAUSE, errp)) { - return; - } - if (job->user_paused) { - error_setg(errp, "Job is already paused"); - return; - } - job->user_paused = true; - block_job_pause(job); -} - -bool block_job_user_paused(BlockJob *job) -{ - return job->user_paused; -} - -void block_job_user_resume(BlockJob *job, Error **errp) -{ - assert(job); - if (!job->user_paused || job->job.pause_count <= 0) { - error_setg(errp, "Can't resume a job that was not paused"); - return; - } - if (job_apply_verb(&job->job, JOB_VERB_RESUME, errp)) { - return; - } - block_job_iostatus_reset(job); - job->user_paused = false; - block_job_resume(job); -} - void block_job_cancel(BlockJob *job, bool force) { if (job->job.status == JOB_STATUS_CONCLUDED) { @@ -851,6 +795,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, assert(is_block_job(&job->job)); assert(job->job.driver->free == &block_job_free); + assert(job->job.driver->user_resume == &block_job_user_resume); job->driver = driver; job->blk = blk; @@ -941,10 +886,16 @@ void block_job_iostatus_reset(BlockJob *job) if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { return; } - assert(job->user_paused && job->job.pause_count > 0); + assert(job->job.user_paused && job->job.pause_count > 0); job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; } +void block_job_user_resume(Job *job) +{ + BlockJob *bjob = container_of(job, BlockJob, job); + block_job_iostatus_reset(bjob); +} + void block_job_event_ready(BlockJob *job) { job_state_transition(&job->job, JOB_STATUS_READY); @@ -991,9 +942,9 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, action, &error_abort); } if (action == BLOCK_ERROR_ACTION_STOP) { - block_job_pause(job); + job_pause(&job->job); /* make the pause user visible, which will be resumed from QMP. */ - job->user_paused = true; + job->job.user_paused = true; block_job_iostatus_set_err(job, error); } return action; diff --git a/include/block/blockjob.h b/include/block/blockjob.h index b60d919fbf..556a8f6375 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -56,12 +56,6 @@ typedef struct BlockJob { */ bool force; - /** - * Set to true if the job is paused by user. Can be unpaused with the - * block-job-resume QMP command. - */ - bool user_paused; - /** * Set to true when the job is ready to be completed. */ @@ -247,32 +241,6 @@ void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); */ BlockJobInfo *block_job_query(BlockJob *job, Error **errp); -/** - * block_job_user_pause: - * @job: The job to be paused. - * - * Asynchronously pause the specified job. - * Do not allow a resume until a matching call to block_job_user_resume. - */ -void block_job_user_pause(BlockJob *job, Error **errp); - -/** - * block_job_paused: - * @job: The job to query. - * - * Returns true if the job is user-paused. - */ -bool block_job_user_paused(BlockJob *job); - -/** - * block_job_user_resume: - * @job: The job to be resumed. - * - * Resume the specified job. - * Must be paired with a preceding block_job_user_pause. - */ -void block_job_user_resume(BlockJob *job, Error **errp); - /** * block_job_user_cancel: * @job: The job to be cancelled. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 8937f5b163..7e705ae0e9 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -133,6 +133,13 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, */ void block_job_free(Job *job); +/** + * block_job_user_resume: + * Callback to be used for JobDriver.user_resume in all block jobs. Resets the + * iostatus when the user resumes @job. + */ +void block_job_user_resume(Job *job); + /** * block_job_yield: * @job: The job that calls the function. diff --git a/include/qemu/job.h b/include/qemu/job.h index 509408f747..bc6398568f 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -82,6 +82,12 @@ typedef struct Job { */ bool paused; + /** + * Set to true if the job is paused by user. Can be unpaused with the + * block-job-resume QMP command. + */ + bool user_paused; + /** * Set to true if the job should cancel itself. The flag must * always be tested just before toggling the busy flag from false @@ -124,6 +130,12 @@ struct JobDriver { */ void coroutine_fn (*resume)(Job *job); + /** + * Called when the job is resumed by the user (i.e. user_paused becomes + * false). .user_resume is called before .resume. + */ + void (*user_resume)(Job *job); + /** Called when the job is freed */ void (*free)(Job *job); }; @@ -202,6 +214,31 @@ const char *job_type_str(const Job *job); /** Returns whether the job is scheduled for cancellation. */ bool job_is_cancelled(Job *job); +/** + * Request @job to pause at the next pause point. Must be paired with + * job_resume(). If the job is supposed to be resumed by user action, call + * job_user_pause() instead. + */ +void job_pause(Job *job); + +/** Resumes a @job paused with job_pause. */ +void job_resume(Job *job); + +/** + * Asynchronously pause the specified @job. + * Do not allow a resume until a matching call to job_user_resume. + */ +void job_user_pause(Job *job, Error **errp); + +/** Returns true if the job is user-paused. */ +bool job_user_paused(Job *job); + +/** + * Resume the specified @job. + * Must be paired with a preceding job_user_pause. + */ +void job_user_resume(Job *job, Error **errp); + /** * Get the next element from the list of block jobs after @job, or the * first one if @job is %NULL. diff --git a/job.c b/job.c index 1b8cba15ff..fd10b1d267 100644 --- a/job.c +++ b/job.c @@ -341,6 +341,65 @@ void job_start(Job *job) aio_co_enter(job->aio_context, job->co); } +/* Assumes the block_job_mutex is held */ +static bool job_timer_not_pending(Job *job) +{ + return !timer_pending(&job->sleep_timer); +} + +void job_pause(Job *job) +{ + job->pause_count++; +} + +void job_resume(Job *job) +{ + assert(job->pause_count > 0); + job->pause_count--; + if (job->pause_count) { + return; + } + + /* kick only if no timer is pending */ + job_enter_cond(job, job_timer_not_pending); +} + +void job_user_pause(Job *job, Error **errp) +{ + if (job_apply_verb(job, JOB_VERB_PAUSE, errp)) { + return; + } + if (job->user_paused) { + error_setg(errp, "Job is already paused"); + return; + } + job->user_paused = true; + job_pause(job); +} + +bool job_user_paused(Job *job) +{ + return job->user_paused; +} + +void job_user_resume(Job *job, Error **errp) +{ + assert(job); + if (!job->user_paused || job->pause_count <= 0) { + error_setg(errp, "Can't resume a job that was not paused"); + return; + } + if (job_apply_verb(job, JOB_VERB_RESUME, errp)) { + return; + } + if (job->driver->user_resume) { + job->driver->user_resume(job); + } + job->user_paused = false; + job_resume(job); +} + + typedef struct { Job *job; JobDeferToMainLoopFn *fn; diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 50232f5eaf..c993512f66 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -524,6 +524,7 @@ BlockJobDriver test_job_driver = { .job_driver = { .instance_size = sizeof(TestBlockJob), .free = block_job_free, + .user_resume = block_job_user_resume, .start = test_job_start, }, .complete = test_job_complete, diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 0e6162bc71..93d1ff0859 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -78,6 +78,7 @@ static const BlockJobDriver test_block_job_driver = { .job_driver = { .instance_size = sizeof(TestBlockJob), .free = block_job_free, + .user_resume = block_job_user_resume, .start = test_block_job_run, }, }; diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index b329bd5274..ceb59600ed 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -20,6 +20,7 @@ static const BlockJobDriver test_block_job_driver = { .job_driver = { .instance_size = sizeof(BlockJob), .free = block_job_free, + .user_resume = block_job_user_resume, }, }; @@ -199,6 +200,7 @@ static const BlockJobDriver test_cancel_driver = { .job_driver = { .instance_size = sizeof(CancelJob), .free = block_job_free, + .user_resume = block_job_user_resume, .start = cancel_job_start, }, .complete = cancel_job_complete, @@ -270,7 +272,7 @@ static void test_cancel_paused(void) job_start(&job->job); assert(job->job.status == JOB_STATUS_RUNNING); - block_job_user_pause(job, &error_abort); + job_user_pause(&job->job, &error_abort); block_job_enter(job); assert(job->job.status == JOB_STATUS_PAUSED); @@ -308,7 +310,7 @@ static void test_cancel_standby(void) block_job_enter(job); assert(job->job.status == JOB_STATUS_READY); - block_job_user_pause(job, &error_abort); + job_user_pause(&job->job, &error_abort); block_job_enter(job); assert(job->job.status == JOB_STATUS_STANDBY); From dbe5e6c1f73b41282624b78a2375a5c3ee59e905 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 19 Apr 2018 13:04:01 +0200 Subject: [PATCH 0695/2380] job: Replace BlockJob.completed with job_is_completed() Since we introduced an explicit status to block job, BlockJob.completed is redundant because it can be derived from the status. Remove the field from BlockJob and add a function to derive it from the status at the Job level. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- blockjob.c | 16 +++++++--------- include/block/blockjob.h | 3 --- include/qemu/job.h | 3 +++ job.c | 22 ++++++++++++++++++++++ qemu-img.c | 4 ++-- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/blockjob.c b/blockjob.c index 6334a54f50..a1d1f488e9 100644 --- a/blockjob.c +++ b/blockjob.c @@ -193,7 +193,7 @@ static void block_job_detach_aio_context(void *opaque) job_pause(&job->job); - while (!job->job.paused && !job->completed) { + while (!job->job.paused && !job_is_completed(&job->job)) { block_job_drain(job); } @@ -269,7 +269,6 @@ const BlockJobDriver *block_job_driver(BlockJob *job) static void block_job_decommission(BlockJob *job) { assert(job); - job->completed = true; job->job.busy = false; job->job.paused = false; job->job.deferred_to_main_loop = true; @@ -334,7 +333,7 @@ static void block_job_clean(BlockJob *job) static int block_job_finalize_single(BlockJob *job) { - assert(job->completed); + assert(job_is_completed(&job->job)); /* Ensure abort is called for late-transactional failures */ block_job_update_rc(job); @@ -427,10 +426,10 @@ static int block_job_finish_sync(BlockJob *job, /* block_job_drain calls block_job_enter, and it should be enough to * induce progress until the job completes or moves to the main thread. */ - while (!job->job.deferred_to_main_loop && !job->completed) { + while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) { block_job_drain(job); } - while (!job->completed) { + while (!job_is_completed(&job->job)) { aio_poll(qemu_get_aio_context(), true); } ret = (job_is_cancelled(&job->job) && job->ret == 0) @@ -471,7 +470,7 @@ static void block_job_completed_txn_abort(BlockJob *job) while (!QLIST_EMPTY(&txn->jobs)) { other_job = QLIST_FIRST(&txn->jobs); ctx = blk_get_aio_context(other_job->blk); - if (!other_job->completed) { + if (!job_is_completed(&other_job->job)) { assert(job_is_cancelled(&other_job->job)); block_job_finish_sync(other_job, NULL, NULL); } @@ -513,7 +512,7 @@ static void block_job_completed_txn_success(BlockJob *job) * txn. */ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - if (!other_job->completed) { + if (!job_is_completed(&other_job->job)) { return; } assert(other_job->ret == 0); @@ -847,9 +846,8 @@ void block_job_early_fail(BlockJob *job) void block_job_completed(BlockJob *job, int ret) { - assert(job && job->txn && !job->completed); + assert(job && job->txn && !job_is_completed(&job->job)); assert(blk_bs(job->blk)->job == job); - job->completed = true; job->ret = ret; block_job_update_rc(job); trace_block_job_completed(job, ret, job->ret); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 556a8f6375..3e94e18850 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -88,9 +88,6 @@ typedef struct BlockJob { /** The opaque value that is passed to the completion function. */ void *opaque; - /** True when job has reported completion by calling block_job_completed. */ - bool completed; - /** ret code passed to block_job_completed. */ int ret; diff --git a/include/qemu/job.h b/include/qemu/job.h index bc6398568f..858f3beea4 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -214,6 +214,9 @@ const char *job_type_str(const Job *job); /** Returns whether the job is scheduled for cancellation. */ bool job_is_cancelled(Job *job); +/** Returns whether the job is in a completed state. */ +bool job_is_completed(Job *job); + /** * Request @job to pause at the next pause point. Must be paired with * job_resume(). If the job is supposed to be resumed by user action, call diff --git a/job.c b/job.c index fd10b1d267..aaacfcc93f 100644 --- a/job.c +++ b/job.c @@ -121,6 +121,28 @@ bool job_is_cancelled(Job *job) return job->cancelled; } +bool job_is_completed(Job *job) +{ + switch (job->status) { + case JOB_STATUS_UNDEFINED: + case JOB_STATUS_CREATED: + case JOB_STATUS_RUNNING: + case JOB_STATUS_PAUSED: + case JOB_STATUS_READY: + case JOB_STATUS_STANDBY: + return false; + case JOB_STATUS_WAITING: + case JOB_STATUS_PENDING: + case JOB_STATUS_ABORTING: + case JOB_STATUS_CONCLUDED: + case JOB_STATUS_NULL: + return true; + default: + g_assert_not_reached(); + } + return false; +} + bool job_started(Job *job) { return job->co; diff --git a/qemu-img.c b/qemu-img.c index 911456ba40..f3fe903c38 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -866,9 +866,9 @@ static void run_block_job(BlockJob *job, Error **errp) aio_poll(aio_context, true); qemu_progress_print(job->len ? ((float)job->offset / job->len * 100.f) : 0.0f, 0); - } while (!job->ready && !job->completed); + } while (!job->ready && !job_is_completed(&job->job)); - if (!job->completed) { + if (!job_is_completed(&job->job)) { ret = block_job_complete_sync(job, errp); } else { ret = job->ret; From bb02b65c7d57e4f2136f39bfba95cc68d89eb216 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 19 Apr 2018 17:54:56 +0200 Subject: [PATCH 0696/2380] job: Move BlockJobCreateFlags to Job This renames the BlockJobCreateFlags constants, moves a few JOB_INTERNAL checks to job_create() and the auto_{finalize,dismiss} fields from BlockJob to Job. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/commit.c | 2 +- block/mirror.c | 2 +- block/replication.c | 4 ++-- block/stream.c | 2 +- blockdev.c | 14 +++++++------- blockjob.c | 27 +++++++-------------------- include/block/blockjob.h | 17 ----------------- include/block/blockjob_int.h | 3 +-- include/qemu/job.h | 20 +++++++++++++++++++- job.c | 11 ++++++++++- qemu-img.c | 2 +- tests/test-blockjob-txn.c | 2 +- tests/test-blockjob.c | 4 ++-- 13 files changed, 53 insertions(+), 57 deletions(-) diff --git a/block/commit.c b/block/commit.c index c4a98e5804..7a6ae59d42 100644 --- a/block/commit.c +++ b/block/commit.c @@ -282,7 +282,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, } s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, - speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); + speed, JOB_DEFAULT, NULL, NULL, errp); if (!s) { return; } diff --git a/block/mirror.c b/block/mirror.c index 9a7226f38c..5091e72554 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1284,7 +1284,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, } is_none_mode = mode == MIRROR_SYNC_MODE_NONE; base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; - mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces, + mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces, speed, granularity, buf_size, backing_mode, on_source_error, on_target_error, unmap, NULL, NULL, &mirror_job_driver, is_none_mode, base, false, diff --git a/block/replication.c b/block/replication.c index 9ed6e0fb04..ff2e2028af 100644 --- a/block/replication.c +++ b/block/replication.c @@ -568,7 +568,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs, 0, MIRROR_SYNC_MODE_NONE, NULL, false, BLOCKDEV_ON_ERROR_REPORT, - BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL, + BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, backup_job_completed, bs, NULL, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -693,7 +693,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) s->stage = BLOCK_REPLICATION_FAILOVER; commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs, - BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, + JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, NULL, replication_done, bs, true, errp); break; default: diff --git a/block/stream.c b/block/stream.c index e81b488a22..eee02538ed 100644 --- a/block/stream.c +++ b/block/stream.c @@ -242,7 +242,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, BLK_PERM_GRAPH_MOD, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE, - speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); + speed, JOB_DEFAULT, NULL, NULL, errp); if (!s) { goto fail; } diff --git a/blockdev.c b/blockdev.c index 3533c0dd6a..278b92ce03 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3244,7 +3244,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, goto out; } commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, - BLOCK_JOB_DEFAULT, speed, on_error, + JOB_DEFAULT, speed, on_error, filter_node_name, NULL, NULL, false, &local_err); } else { BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); @@ -3275,7 +3275,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, AioContext *aio_context; QDict *options = NULL; Error *local_err = NULL; - int flags, job_flags = BLOCK_JOB_DEFAULT; + int flags, job_flags = JOB_DEFAULT; int64_t size; bool set_backing_hd = false; @@ -3398,10 +3398,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, } } if (!backup->auto_finalize) { - job_flags |= BLOCK_JOB_MANUAL_FINALIZE; + job_flags |= JOB_MANUAL_FINALIZE; } if (!backup->auto_dismiss) { - job_flags |= BLOCK_JOB_MANUAL_DISMISS; + job_flags |= JOB_MANUAL_DISMISS; } job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, @@ -3442,7 +3442,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, Error *local_err = NULL; AioContext *aio_context; BlockJob *job = NULL; - int job_flags = BLOCK_JOB_DEFAULT; + int job_flags = JOB_DEFAULT; if (!backup->has_speed) { backup->speed = 0; @@ -3491,10 +3491,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, } } if (!backup->auto_finalize) { - job_flags |= BLOCK_JOB_MANUAL_FINALIZE; + job_flags |= JOB_MANUAL_FINALIZE; } if (!backup->auto_dismiss) { - job_flags |= BLOCK_JOB_MANUAL_DISMISS; + job_flags |= JOB_MANUAL_DISMISS; } job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, backup->sync, NULL, backup->compress, diff --git a/blockjob.c b/blockjob.c index a1d1f488e9..d9d8ff7f5c 100644 --- a/blockjob.c +++ b/blockjob.c @@ -285,7 +285,7 @@ static void block_job_do_dismiss(BlockJob *job) static void block_job_conclude(BlockJob *job) { job_state_transition(&job->job, JOB_STATUS_CONCLUDED); - if (job->auto_dismiss || !job_started(&job->job)) { + if (job->job.auto_dismiss || !job_started(&job->job)) { block_job_do_dismiss(job); } } @@ -483,7 +483,7 @@ static void block_job_completed_txn_abort(BlockJob *job) static int block_job_needs_finalize(BlockJob *job) { - return !job->auto_finalize; + return !job->job.auto_finalize; } static void block_job_do_finalize(BlockJob *job) @@ -688,8 +688,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info->io_status = job->iostatus; info->ready = job->ready; info->status = job->job.status; - info->auto_finalize = job->auto_finalize; - info->auto_dismiss = job->auto_dismiss; + info->auto_finalize = job->job.auto_finalize; + info->auto_dismiss = job->job.auto_dismiss; info->has_error = job->ret != 0; info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL; return info; @@ -736,7 +736,7 @@ static void block_job_event_completed(BlockJob *job, const char *msg) static int block_job_event_pending(BlockJob *job) { job_state_transition(&job->job, JOB_STATUS_PENDING); - if (!job->auto_finalize && !block_job_is_internal(job)) { + if (!job->job.auto_finalize && !block_job_is_internal(job)) { qapi_event_send_block_job_pending(job_type(&job->job), job->job.id, &error_abort); @@ -763,19 +763,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return NULL; } - if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) { + if (job_id == NULL && !(flags & JOB_INTERNAL)) { job_id = bdrv_get_device_name(bs); - if (!*job_id) { - error_setg(errp, "An explicit job ID is required for this node"); - return NULL; - } - } - - if (job_id) { - if (flags & BLOCK_JOB_INTERNAL) { - error_setg(errp, "Cannot specify job ID for internal block job"); - return NULL; - } } blk = blk_new(perm, shared_perm); @@ -786,7 +775,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, } job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk), - errp); + flags, errp); if (job == NULL) { blk_unref(blk); return NULL; @@ -800,8 +789,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job->blk = blk; job->cb = cb; job->opaque = opaque; - job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); - job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); error_setg(&job->blocker, "block device is in use by block job: %s", job_type_str(&job->job)); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 3e94e18850..f9aaaaa835 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -91,27 +91,10 @@ typedef struct BlockJob { /** ret code passed to block_job_completed. */ int ret; - /** True if this job should automatically finalize itself */ - bool auto_finalize; - - /** True if this job should automatically dismiss itself */ - bool auto_dismiss; - BlockJobTxn *txn; QLIST_ENTRY(BlockJob) txn_list; } BlockJob; -typedef enum BlockJobCreateFlags { - /* Default behavior */ - BLOCK_JOB_DEFAULT = 0x00, - /* BlockJob is not QMP-created and should not send QMP events */ - BLOCK_JOB_INTERNAL = 0x01, - /* BlockJob requires manual finalize step */ - BLOCK_JOB_MANUAL_FINALIZE = 0x02, - /* BlockJob requires manual dismiss step */ - BLOCK_JOB_MANUAL_DISMISS = 0x04, -} BlockJobCreateFlags; - /** * block_job_next: * @job: A block job, or %NULL. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 7e705ae0e9..88639f7efc 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -106,8 +106,7 @@ struct BlockJobDriver { * @bs: The block * @perm, @shared_perm: Permissions to request for @bs * @speed: The maximum speed, in bytes per second, or 0 for unlimited. - * @flags: Creation flags for the Block Job. - * See @BlockJobCreateFlags + * @flags: Creation flags for the Block Job. See @JobCreateFlags. * @cb: Completion function for the job. * @opaque: Opaque pointer value passed to @cb. * @errp: Error object. diff --git a/include/qemu/job.h b/include/qemu/job.h index 858f3beea4..9783e4049b 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -99,6 +99,12 @@ typedef struct Job { /** Set to true when the job has deferred work to the main loop. */ bool deferred_to_main_loop; + /** True if this job should automatically finalize itself */ + bool auto_finalize; + + /** True if this job should automatically dismiss itself */ + bool auto_dismiss; + /** Element of the list of jobs */ QLIST_ENTRY(Job) job_list; } Job; @@ -140,6 +146,17 @@ struct JobDriver { void (*free)(Job *job); }; +typedef enum JobCreateFlags { + /* Default behavior */ + JOB_DEFAULT = 0x00, + /* Job is not QMP-created and should not send QMP events */ + JOB_INTERNAL = 0x01, + /* Job requires manual finalize step */ + JOB_MANUAL_FINALIZE = 0x02, + /* Job requires manual dismiss step */ + JOB_MANUAL_DISMISS = 0x04, +} JobCreateFlags; + /** * Create a new long-running job and return it. @@ -147,10 +164,11 @@ struct JobDriver { * @job_id: The id of the newly-created job, or %NULL for internal jobs * @driver: The class object for the newly-created job. * @ctx: The AioContext to run the job coroutine in. + * @flags: Creation flags for the job. See @JobCreateFlags. * @errp: Error object. */ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, - Error **errp); + int flags, Error **errp); /** * Add a reference to Job refcnt, it will be decreased with job_unref, and then diff --git a/job.c b/job.c index aaacfcc93f..dd46170418 100644 --- a/job.c +++ b/job.c @@ -182,11 +182,15 @@ static void job_sleep_timer_cb(void *opaque) } void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, - Error **errp) + int flags, Error **errp) { Job *job; if (job_id) { + if (flags & JOB_INTERNAL) { + error_setg(errp, "Cannot specify job ID for internal job"); + return NULL; + } if (!id_wellformed(job_id)) { error_setg(errp, "Invalid job ID '%s'", job_id); return NULL; @@ -195,6 +199,9 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, error_setg(errp, "Job ID '%s' already in use", job_id); return NULL; } + } else if (!(flags & JOB_INTERNAL)) { + error_setg(errp, "An explicit job ID is required"); + return NULL; } job = g_malloc0(driver->instance_size); @@ -205,6 +212,8 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, job->busy = false; job->paused = true; job->pause_count = 1; + job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); + job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); job_state_transition(job, JOB_STATUS_CREATED); aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, diff --git a/qemu-img.c b/qemu-img.c index f3fe903c38..2ab04b285a 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1014,7 +1014,7 @@ static int img_commit(int argc, char **argv) aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0, + commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0, BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, &cbi, false, &local_err); aio_context_release(aio_context); diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 93d1ff0859..60e9fa2298 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -107,7 +107,7 @@ static BlockJob *test_block_job_start(unsigned int iterations, snprintf(job_id, sizeof(job_id), "job%u", counter++); s = block_job_create(job_id, &test_block_job_driver, txn, bs, - 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, + 0, BLK_PERM_ALL, 0, JOB_DEFAULT, test_block_job_cb, data, &error_abort); s->iterations = iterations; s->use_timer = use_timer; diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index ceb59600ed..8bb0aa8f85 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -59,7 +59,7 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id, bool should_succeed) { return mk_job(blk, id, &test_block_job_driver, - should_succeed, BLOCK_JOB_DEFAULT); + should_succeed, JOB_DEFAULT); } /* This creates a BlockBackend (optionally with a name) with a @@ -214,7 +214,7 @@ static CancelJob *create_common(BlockJob **pjob) blk = create_blk(NULL); job = mk_job(blk, "Steve", &test_cancel_driver, true, - BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); + JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); job_ref(&job->job); assert(job->job.status == JOB_STATUS_CREATED); s = container_of(job, CancelJob, common); From 5d4f376998bc6b01402b90634385b082b2eb5c5b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 23 Apr 2018 17:09:42 +0200 Subject: [PATCH 0697/2380] blockjob: Split block_job_event_pending() block_job_event_pending() doesn't only send a QMP event, but it also transitions to the PENDING state. Split the function so that we get one part only sending the event (like other block_job_event_* functions) and another part that does the state transition. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- blockjob.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/blockjob.c b/blockjob.c index d9d8ff7f5c..b4334fb1bf 100644 --- a/blockjob.c +++ b/blockjob.c @@ -38,7 +38,7 @@ static void block_job_event_cancelled(BlockJob *job); static void block_job_event_completed(BlockJob *job, const char *msg); -static int block_job_event_pending(BlockJob *job); +static void block_job_event_pending(BlockJob *job); /* Transactional group of block jobs */ struct BlockJobTxn { @@ -500,6 +500,15 @@ static void block_job_do_finalize(BlockJob *job) } } +static int block_job_transition_to_pending(BlockJob *job) +{ + job_state_transition(&job->job, JOB_STATUS_PENDING); + if (!job->job.auto_finalize) { + block_job_event_pending(job); + } + return 0; +} + static void block_job_completed_txn_success(BlockJob *job) { BlockJobTxn *txn = job->txn; @@ -518,7 +527,7 @@ static void block_job_completed_txn_success(BlockJob *job) assert(other_job->ret == 0); } - block_job_txn_apply(txn, block_job_event_pending, false); + block_job_txn_apply(txn, block_job_transition_to_pending, false); /* If no jobs need manual finalization, automatically do so */ if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) { @@ -733,15 +742,15 @@ static void block_job_event_completed(BlockJob *job, const char *msg) &error_abort); } -static int block_job_event_pending(BlockJob *job) +static void block_job_event_pending(BlockJob *job) { - job_state_transition(&job->job, JOB_STATUS_PENDING); - if (!job->job.auto_finalize && !block_job_is_internal(job)) { - qapi_event_send_block_job_pending(job_type(&job->job), - job->job.id, - &error_abort); + if (block_job_is_internal(job)) { + return; } - return 0; + + qapi_event_send_block_job_pending(job_type(&job->job), + job->job.id, + &error_abort); } /* From 139a9f020d49e9f863e0d46fd3d0b440dfb3b9d7 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 23 Apr 2018 18:04:57 +0200 Subject: [PATCH 0698/2380] job: Add job_event_*() Go through the Job layer in order to send QMP events. For the moment, these functions only call a notifier in the BlockJob layer that sends the existing commands. This uses notifiers rather than JobDriver callbacks because internal users of jobs won't receive QMP events, but might still be interested in getting notified for the events. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- blockjob.c | 41 ++++++++++++++++++++++++++-------------- include/block/blockjob.h | 9 +++++++++ include/qemu/job.h | 18 ++++++++++++++++++ job.c | 19 +++++++++++++++++++ 4 files changed, 73 insertions(+), 14 deletions(-) diff --git a/blockjob.c b/blockjob.c index b4334fb1bf..05d7921b3f 100644 --- a/blockjob.c +++ b/blockjob.c @@ -36,10 +36,6 @@ #include "qemu/coroutine.h" #include "qemu/timer.h" -static void block_job_event_cancelled(BlockJob *job); -static void block_job_event_completed(BlockJob *job, const char *msg); -static void block_job_event_pending(BlockJob *job); - /* Transactional group of block jobs */ struct BlockJobTxn { @@ -352,13 +348,9 @@ static int block_job_finalize_single(BlockJob *job) /* Emit events only if we actually started */ if (job_started(&job->job)) { if (job_is_cancelled(&job->job)) { - block_job_event_cancelled(job); + job_event_cancelled(&job->job); } else { - const char *msg = NULL; - if (job->ret < 0) { - msg = strerror(-job->ret); - } - block_job_event_completed(job, msg); + job_event_completed(&job->job); } } @@ -504,7 +496,7 @@ static int block_job_transition_to_pending(BlockJob *job) { job_state_transition(&job->job, JOB_STATUS_PENDING); if (!job->job.auto_finalize) { - block_job_event_pending(job); + job_event_pending(&job->job); } return 0; } @@ -712,8 +704,10 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) } } -static void block_job_event_cancelled(BlockJob *job) +static void block_job_event_cancelled(Notifier *n, void *opaque) { + BlockJob *job = opaque; + if (block_job_is_internal(job)) { return; } @@ -726,12 +720,19 @@ static void block_job_event_cancelled(BlockJob *job) &error_abort); } -static void block_job_event_completed(BlockJob *job, const char *msg) +static void block_job_event_completed(Notifier *n, void *opaque) { + BlockJob *job = opaque; + const char *msg = NULL; + if (block_job_is_internal(job)) { return; } + if (job->ret < 0) { + msg = strerror(-job->ret); + } + qapi_event_send_block_job_completed(job_type(&job->job), job->job.id, job->len, @@ -742,8 +743,10 @@ static void block_job_event_completed(BlockJob *job, const char *msg) &error_abort); } -static void block_job_event_pending(BlockJob *job) +static void block_job_event_pending(Notifier *n, void *opaque) { + BlockJob *job = opaque; + if (block_job_is_internal(job)) { return; } @@ -799,6 +802,16 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job->cb = cb; job->opaque = opaque; + job->finalize_cancelled_notifier.notify = block_job_event_cancelled; + job->finalize_completed_notifier.notify = block_job_event_completed; + job->pending_notifier.notify = block_job_event_pending; + + notifier_list_add(&job->job.on_finalize_cancelled, + &job->finalize_cancelled_notifier); + notifier_list_add(&job->job.on_finalize_completed, + &job->finalize_completed_notifier); + notifier_list_add(&job->job.on_pending, &job->pending_notifier); + error_setg(&job->blocker, "block device is in use by block job: %s", job_type_str(&job->job)); block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index f9aaaaa835..aef06295f6 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -82,6 +82,15 @@ typedef struct BlockJob { /** Block other operations when block job is running */ Error *blocker; + /** Called when a cancelled job is finalised. */ + Notifier finalize_cancelled_notifier; + + /** Called when a successfully completed job is finalised. */ + Notifier finalize_completed_notifier; + + /** Called when the job transitions to PENDING */ + Notifier pending_notifier; + /** BlockDriverStates that are involved in this block job */ GSList *nodes; diff --git a/include/qemu/job.h b/include/qemu/job.h index 9783e4049b..14d93778f3 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -105,6 +105,15 @@ typedef struct Job { /** True if this job should automatically dismiss itself */ bool auto_dismiss; + /** Notifiers called when a cancelled job is finalised */ + NotifierList on_finalize_cancelled; + + /** Notifiers called when a successfully completed job is finalised */ + NotifierList on_finalize_completed; + + /** Notifiers called when the job transitions to PENDING */ + NotifierList on_pending; + /** Element of the list of jobs */ QLIST_ENTRY(Job) job_list; } Job; @@ -182,6 +191,15 @@ void job_ref(Job *job); */ void job_unref(Job *job); +/** To be called when a cancelled job is finalised. */ +void job_event_cancelled(Job *job); + +/** To be called when a successfully completed job is finalised. */ +void job_event_completed(Job *job); + +/** To be called when the job transitions to PENDING */ +void job_event_pending(Job *job); + /** * Conditionally enter the job coroutine if the job is ready to run, not * already busy and fn() returns true. fn() is called while under the job_lock diff --git a/job.c b/job.c index dd46170418..817c3b4426 100644 --- a/job.c +++ b/job.c @@ -215,6 +215,10 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); + notifier_list_init(&job->on_finalize_cancelled); + notifier_list_init(&job->on_finalize_completed); + notifier_list_init(&job->on_pending); + job_state_transition(job, JOB_STATUS_CREATED); aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, QEMU_CLOCK_REALTIME, SCALE_NS, @@ -247,6 +251,21 @@ void job_unref(Job *job) } } +void job_event_cancelled(Job *job) +{ + notifier_list_notify(&job->on_finalize_cancelled, job); +} + +void job_event_completed(Job *job) +{ + notifier_list_notify(&job->on_finalize_completed, job); +} + +void job_event_pending(Job *job) +{ + notifier_list_notify(&job->on_pending, job); +} + void job_enter_cond(Job *job, bool(*fn)(Job *job)) { if (!job_started(job)) { From 4ad351819b974d724e926fd23cdd66bec3c9768e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 19 Apr 2018 17:30:16 +0200 Subject: [PATCH 0699/2380] job: Move single job finalisation to Job This moves the finalisation of a single job from BlockJob to Job. Some part of this code depends on job transactions, and job transactions call this code, we introduce some temporary calls from Job functions to BlockJob ones. This will be fixed once transactions move to Job, too. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/backup.c | 22 +++--- block/commit.c | 2 +- block/mirror.c | 2 +- blockjob.c | 142 ++++++----------------------------- include/block/blockjob.h | 9 --- include/block/blockjob_int.h | 36 --------- include/qemu/job.h | 53 ++++++++++++- job.c | 100 +++++++++++++++++++++++- qemu-img.c | 2 +- tests/test-blockjob.c | 10 +-- 10 files changed, 194 insertions(+), 184 deletions(-) diff --git a/block/backup.c b/block/backup.c index 4d011d5a5c..bd31282924 100644 --- a/block/backup.c +++ b/block/backup.c @@ -207,25 +207,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) } } -static void backup_commit(BlockJob *job) +static void backup_commit(Job *job) { - BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); if (s->sync_bitmap) { backup_cleanup_sync_bitmap(s, 0); } } -static void backup_abort(BlockJob *job) +static void backup_abort(Job *job) { - BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); if (s->sync_bitmap) { backup_cleanup_sync_bitmap(s, -1); } } -static void backup_clean(BlockJob *job) +static void backup_clean(Job *job) { - BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); assert(s->target); blk_unref(s->target); s->target = NULL; @@ -530,10 +530,10 @@ static const BlockJobDriver backup_job_driver = { .free = block_job_free, .user_resume = block_job_user_resume, .start = backup_run, + .commit = backup_commit, + .abort = backup_abort, + .clean = backup_clean, }, - .commit = backup_commit, - .abort = backup_abort, - .clean = backup_clean, .attached_aio_context = backup_attached_aio_context, .drain = backup_drain, }; @@ -678,8 +678,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); } if (job) { - backup_clean(&job->common); - block_job_early_fail(&job->common); + backup_clean(&job->common.job); + job_early_fail(&job->common.job); } return NULL; diff --git a/block/commit.c b/block/commit.c index 7a6ae59d42..e53b2d7d55 100644 --- a/block/commit.c +++ b/block/commit.c @@ -385,7 +385,7 @@ fail: if (commit_top_bs) { bdrv_replace_node(commit_top_bs, top, &error_abort); } - block_job_early_fail(&s->common); + job_early_fail(&s->common.job); } diff --git a/block/mirror.c b/block/mirror.c index 5091e72554..e9a90ea730 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1257,7 +1257,7 @@ fail: g_free(s->replaces); blk_unref(s->target); - block_job_early_fail(&s->common); + job_early_fail(&s->common.job); } bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, diff --git a/blockjob.c b/blockjob.c index 05d7921b3f..34c57da304 100644 --- a/blockjob.c +++ b/blockjob.c @@ -127,7 +127,7 @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job) block_job_txn_ref(txn); } -static void block_job_txn_del_job(BlockJob *job) +void block_job_txn_del_job(BlockJob *job) { if (job->txn) { QLIST_REMOVE(job, txn_list); @@ -262,101 +262,12 @@ const BlockJobDriver *block_job_driver(BlockJob *job) return job->driver; } -static void block_job_decommission(BlockJob *job) -{ - assert(job); - job->job.busy = false; - job->job.paused = false; - job->job.deferred_to_main_loop = true; - block_job_txn_del_job(job); - job_state_transition(&job->job, JOB_STATUS_NULL); - job_unref(&job->job); -} - -static void block_job_do_dismiss(BlockJob *job) -{ - block_job_decommission(job); -} - -static void block_job_conclude(BlockJob *job) -{ - job_state_transition(&job->job, JOB_STATUS_CONCLUDED); - if (job->job.auto_dismiss || !job_started(&job->job)) { - block_job_do_dismiss(job); - } -} - -static void block_job_update_rc(BlockJob *job) -{ - if (!job->ret && job_is_cancelled(&job->job)) { - job->ret = -ECANCELED; - } - if (job->ret) { - job_state_transition(&job->job, JOB_STATUS_ABORTING); - } -} - static int block_job_prepare(BlockJob *job) { - if (job->ret == 0 && job->driver->prepare) { - job->ret = job->driver->prepare(job); + if (job->job.ret == 0 && job->driver->prepare) { + job->job.ret = job->driver->prepare(job); } - return job->ret; -} - -static void block_job_commit(BlockJob *job) -{ - assert(!job->ret); - if (job->driver->commit) { - job->driver->commit(job); - } -} - -static void block_job_abort(BlockJob *job) -{ - assert(job->ret); - if (job->driver->abort) { - job->driver->abort(job); - } -} - -static void block_job_clean(BlockJob *job) -{ - if (job->driver->clean) { - job->driver->clean(job); - } -} - -static int block_job_finalize_single(BlockJob *job) -{ - assert(job_is_completed(&job->job)); - - /* Ensure abort is called for late-transactional failures */ - block_job_update_rc(job); - - if (!job->ret) { - block_job_commit(job); - } else { - block_job_abort(job); - } - block_job_clean(job); - - if (job->cb) { - job->cb(job->opaque, job->ret); - } - - /* Emit events only if we actually started */ - if (job_started(&job->job)) { - if (job_is_cancelled(&job->job)) { - job_event_cancelled(&job->job); - } else { - job_event_completed(&job->job); - } - } - - block_job_txn_del_job(job); - block_job_conclude(job); - return 0; + return job->job.ret; } static void block_job_cancel_async(BlockJob *job, bool force) @@ -424,8 +335,8 @@ static int block_job_finish_sync(BlockJob *job, while (!job_is_completed(&job->job)) { aio_poll(qemu_get_aio_context(), true); } - ret = (job_is_cancelled(&job->job) && job->ret == 0) - ? -ECANCELED : job->ret; + ret = (job_is_cancelled(&job->job) && job->job.ret == 0) + ? -ECANCELED : job->job.ret; job_unref(&job->job); return ret; } @@ -466,7 +377,7 @@ static void block_job_completed_txn_abort(BlockJob *job) assert(job_is_cancelled(&other_job->job)); block_job_finish_sync(other_job, NULL, NULL); } - block_job_finalize_single(other_job); + job_finalize_single(&other_job->job); aio_context_release(ctx); } @@ -478,6 +389,11 @@ static int block_job_needs_finalize(BlockJob *job) return !job->job.auto_finalize; } +static int block_job_finalize_single(BlockJob *job) +{ + return job_finalize_single(&job->job); +} + static void block_job_do_finalize(BlockJob *job) { int rc; @@ -516,7 +432,7 @@ static void block_job_completed_txn_success(BlockJob *job) if (!job_is_completed(&other_job->job)) { return; } - assert(other_job->ret == 0); + assert(other_job->job.ret == 0); } block_job_txn_apply(txn, block_job_transition_to_pending, false); @@ -601,14 +517,14 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) return; } - block_job_do_dismiss(job); + job_do_dismiss(&job->job); *jobptr = NULL; } void block_job_cancel(BlockJob *job, bool force) { if (job->job.status == JOB_STATUS_CONCLUDED) { - block_job_do_dismiss(job); + job_do_dismiss(&job->job); return; } block_job_cancel_async(job, force); @@ -691,8 +607,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info->status = job->job.status; info->auto_finalize = job->job.auto_finalize; info->auto_dismiss = job->job.auto_dismiss; - info->has_error = job->ret != 0; - info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL; + info->has_error = job->job.ret != 0; + info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL; return info; } @@ -729,8 +645,8 @@ static void block_job_event_completed(Notifier *n, void *opaque) return; } - if (job->ret < 0) { - msg = strerror(-job->ret); + if (job->job.ret < 0) { + msg = strerror(-job->job.ret); } qapi_event_send_block_job_completed(job_type(&job->job), @@ -787,7 +703,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, } job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk), - flags, errp); + flags, cb, opaque, errp); if (job == NULL) { blk_unref(blk); return NULL; @@ -799,8 +715,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job->driver = driver; job->blk = blk; - job->cb = cb; - job->opaque = opaque; job->finalize_cancelled_notifier.notify = block_job_event_cancelled; job->finalize_completed_notifier.notify = block_job_event_completed; @@ -828,7 +742,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, block_job_set_speed(job, speed, &local_err); if (local_err) { - block_job_early_fail(job); + job_early_fail(&job->job); error_propagate(errp, local_err); return NULL; } @@ -847,20 +761,14 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return job; } -void block_job_early_fail(BlockJob *job) -{ - assert(job->job.status == JOB_STATUS_CREATED); - block_job_decommission(job); -} - void block_job_completed(BlockJob *job, int ret) { assert(job && job->txn && !job_is_completed(&job->job)); assert(blk_bs(job->blk)->job == job); - job->ret = ret; - block_job_update_rc(job); - trace_block_job_completed(job, ret, job->ret); - if (job->ret) { + job->job.ret = ret; + job_update_rc(&job->job); + trace_block_job_completed(job, ret, job->job.ret); + if (job->job.ret) { block_job_completed_txn_abort(job); } else { block_job_completed_txn_success(job); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index aef06295f6..3f405d1fa7 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -76,9 +76,6 @@ typedef struct BlockJob { /** Rate limiting data structure for implementing @speed. */ RateLimit limit; - /** The completion function that will be called when the job completes. */ - BlockCompletionFunc *cb; - /** Block other operations when block job is running */ Error *blocker; @@ -94,12 +91,6 @@ typedef struct BlockJob { /** BlockDriverStates that are involved in this block job */ GSList *nodes; - /** The opaque value that is passed to the completion function. */ - void *opaque; - - /** ret code passed to block_job_completed. */ - int ret; - BlockJobTxn *txn; QLIST_ENTRY(BlockJob) txn_list; } BlockJob; diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 88639f7efc..bf2b762808 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -54,34 +54,6 @@ struct BlockJobDriver { */ int (*prepare)(BlockJob *job); - /** - * If the callback is not NULL, it will be invoked when all the jobs - * belonging to the same transaction complete; or upon this job's - * completion if it is not in a transaction. Skipped if NULL. - * - * All jobs will complete with a call to either .commit() or .abort() but - * never both. - */ - void (*commit)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked when any job in the - * same transaction fails; or upon this job's failure (due to error or - * cancellation) if it is not in a transaction. Skipped if NULL. - * - * All jobs will complete with a call to either .commit() or .abort() but - * never both. - */ - void (*abort)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked after a call to either - * .commit() or .abort(). Regardless of which callback is invoked after - * completion, .clean() will always be called, even if the job does not - * belong to a transaction group. - */ - void (*clean)(BlockJob *job); - /* * If the callback is not NULL, it will be invoked before the job is * resumed in a new AioContext. This is the place to move any resources @@ -155,14 +127,6 @@ void block_job_yield(BlockJob *job); */ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); -/** - * block_job_early_fail: - * @bs: The block device. - * - * The block job could not be started, free it. - */ -void block_job_early_fail(BlockJob *job); - /** * block_job_completed: * @job: The job being completed. diff --git a/include/qemu/job.h b/include/qemu/job.h index 14d93778f3..3e817beee9 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -29,6 +29,7 @@ #include "qapi/qapi-types-block-core.h" #include "qemu/queue.h" #include "qemu/coroutine.h" +#include "block/aio.h" typedef struct JobDriver JobDriver; @@ -105,6 +106,15 @@ typedef struct Job { /** True if this job should automatically dismiss itself */ bool auto_dismiss; + /** ret code passed to block_job_completed. */ + int ret; + + /** The completion function that will be called when the job completes. */ + BlockCompletionFunc *cb; + + /** The opaque value that is passed to the completion function. */ + void *opaque; + /** Notifiers called when a cancelled job is finalised */ NotifierList on_finalize_cancelled; @@ -151,6 +161,35 @@ struct JobDriver { */ void (*user_resume)(Job *job); + /** + * If the callback is not NULL, it will be invoked when all the jobs + * belonging to the same transaction complete; or upon this job's + * completion if it is not in a transaction. Skipped if NULL. + * + * All jobs will complete with a call to either .commit() or .abort() but + * never both. + */ + void (*commit)(Job *job); + + /** + * If the callback is not NULL, it will be invoked when any job in the + * same transaction fails; or upon this job's failure (due to error or + * cancellation) if it is not in a transaction. Skipped if NULL. + * + * All jobs will complete with a call to either .commit() or .abort() but + * never both. + */ + void (*abort)(Job *job); + + /** + * If the callback is not NULL, it will be invoked after a call to either + * .commit() or .abort(). Regardless of which callback is invoked after + * completion, .clean() will always be called, even if the job does not + * belong to a transaction group. + */ + void (*clean)(Job *job); + + /** Called when the job is freed */ void (*free)(Job *job); }; @@ -174,10 +213,12 @@ typedef enum JobCreateFlags { * @driver: The class object for the newly-created job. * @ctx: The AioContext to run the job coroutine in. * @flags: Creation flags for the job. See @JobCreateFlags. + * @cb: Completion function for the job. + * @opaque: Opaque pointer value passed to @cb. * @errp: Error object. */ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, - int flags, Error **errp); + int flags, BlockCompletionFunc *cb, void *opaque, Error **errp); /** * Add a reference to Job refcnt, it will be decreased with job_unref, and then @@ -300,6 +341,10 @@ Job *job_get(const char *id); */ int job_apply_verb(Job *job, JobVerb verb, Error **errp); +/** The @job could not be started, free it. */ +void job_early_fail(Job *job); + + typedef void JobDeferToMainLoopFn(Job *job, void *opaque); /** @@ -322,5 +367,11 @@ void job_state_transition(Job *job, JobStatus s1); void coroutine_fn job_do_yield(Job *job, uint64_t ns); bool job_should_pause(Job *job); bool job_started(Job *job); +void job_do_dismiss(Job *job); +int job_finalize_single(Job *job); +void job_update_rc(Job *job); + +typedef struct BlockJob BlockJob; +void block_job_txn_del_job(BlockJob *job); #endif diff --git a/job.c b/job.c index 817c3b4426..64b64daf68 100644 --- a/job.c +++ b/job.c @@ -85,7 +85,7 @@ void job_state_transition(Job *job, JobStatus s1) { JobStatus s0 = job->status; assert(s1 >= 0 && s1 <= JOB_STATUS__MAX); - trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0, + trace_job_state_transition(job, job->ret, JobSTT[s0][s1] ? "allowed" : "disallowed", JobStatus_str(s0), JobStatus_str(s1)); assert(JobSTT[s0][s1]); @@ -182,7 +182,7 @@ static void job_sleep_timer_cb(void *opaque) } void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, - int flags, Error **errp) + int flags, BlockCompletionFunc *cb, void *opaque, Error **errp) { Job *job; @@ -214,6 +214,8 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, job->pause_count = 1; job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); + job->cb = cb; + job->opaque = opaque; notifier_list_init(&job->on_finalize_cancelled); notifier_list_init(&job->on_finalize_completed); @@ -449,6 +451,100 @@ void job_user_resume(Job *job, Error **errp) job_resume(job); } +void job_do_dismiss(Job *job) +{ + assert(job); + job->busy = false; + job->paused = false; + job->deferred_to_main_loop = true; + + /* TODO Don't assume it's a BlockJob */ + block_job_txn_del_job((BlockJob*) job); + + job_state_transition(job, JOB_STATUS_NULL); + job_unref(job); +} + +void job_early_fail(Job *job) +{ + assert(job->status == JOB_STATUS_CREATED); + job_do_dismiss(job); +} + +static void job_conclude(Job *job) +{ + job_state_transition(job, JOB_STATUS_CONCLUDED); + if (job->auto_dismiss || !job_started(job)) { + job_do_dismiss(job); + } +} + +void job_update_rc(Job *job) +{ + if (!job->ret && job_is_cancelled(job)) { + job->ret = -ECANCELED; + } + if (job->ret) { + job_state_transition(job, JOB_STATUS_ABORTING); + } +} + +static void job_commit(Job *job) +{ + assert(!job->ret); + if (job->driver->commit) { + job->driver->commit(job); + } +} + +static void job_abort(Job *job) +{ + assert(job->ret); + if (job->driver->abort) { + job->driver->abort(job); + } +} + +static void job_clean(Job *job) +{ + if (job->driver->clean) { + job->driver->clean(job); + } +} + +int job_finalize_single(Job *job) +{ + assert(job_is_completed(job)); + + /* Ensure abort is called for late-transactional failures */ + job_update_rc(job); + + if (!job->ret) { + job_commit(job); + } else { + job_abort(job); + } + job_clean(job); + + if (job->cb) { + job->cb(job->opaque, job->ret); + } + + /* Emit events only if we actually started */ + if (job_started(job)) { + if (job_is_cancelled(job)) { + job_event_cancelled(job); + } else { + job_event_completed(job); + } + } + + /* TODO Don't assume it's a BlockJob */ + block_job_txn_del_job((BlockJob*) job); + job_conclude(job); + return 0; +} + typedef struct { Job *job; diff --git a/qemu-img.c b/qemu-img.c index 2ab04b285a..7419ec7a1a 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -871,7 +871,7 @@ static void run_block_job(BlockJob *job, Error **errp) if (!job_is_completed(&job->job)) { ret = block_job_complete_sync(job, errp); } else { - ret = job->ret; + ret = job->job.ret; } job_unref(&job->job); aio_context_release(aio_context); diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 8bb0aa8f85..1fe6803fe0 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -128,11 +128,11 @@ static void test_job_ids(void) job[1] = do_test_id(blk[1], "id0", false); /* But once job[0] finishes we can reuse its ID */ - block_job_early_fail(job[0]); + job_early_fail(&job[0]->job); job[1] = do_test_id(blk[1], "id0", true); /* No job ID specified, defaults to the backend name ('drive1') */ - block_job_early_fail(job[1]); + job_early_fail(&job[1]->job); job[1] = do_test_id(blk[1], NULL, true); /* Duplicate job ID */ @@ -145,9 +145,9 @@ static void test_job_ids(void) /* This one is valid */ job[2] = do_test_id(blk[2], "id_2", true); - block_job_early_fail(job[0]); - block_job_early_fail(job[1]); - block_job_early_fail(job[2]); + job_early_fail(&job[0]->job); + job_early_fail(&job[1]->job); + job_early_fail(&job[2]->job); destroy_blk(blk[0]); destroy_blk(blk[1]); From 004e95df98266da33e08c9f1731aca71b6d6d7c4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 20 Apr 2018 14:56:08 +0200 Subject: [PATCH 0700/2380] job: Convert block_job_cancel_async() to Job block_job_cancel_async() did two things that were still block job specific: * Setting job->force. This field makes sense on the Job level, so we can just move it. While at it, rename it to job->force_cancel to make its purpose more obvious. * Resetting the I/O status. This can't be moved because generic Jobs don't have an I/O status. What the function really implements is a user resume, except without entering the coroutine. Consequently, it makes sense to call the .user_resume driver callback here which already resets the I/O status. The old block_job_cancel_async() has two separate if statements that check job->iostatus != BLOCK_DEVICE_IO_STATUS_OK and job->user_paused. However, the former condition always implies the latter (as is asserted in block_job_iostatus_reset()), so changing the explicit call of block_job_iostatus_reset() on the former condition with the .user_resume callback on the latter condition is equivalent and doesn't need to access any BlockJob specific state. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/mirror.c | 4 ++-- blockjob.c | 25 +++++++++++++------------ include/block/blockjob.h | 6 ------ include/qemu/job.h | 6 ++++++ 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index e9a90ea730..c3951d1ca2 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -871,7 +871,7 @@ static void coroutine_fn mirror_run(void *opaque) trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); job_sleep_ns(&s->common.job, delay_ns); if (job_is_cancelled(&s->common.job) && - (!s->synced || s->common.force)) + (!s->synced || s->common.job.force_cancel)) { break; } @@ -884,7 +884,7 @@ immediate_exit: * or it was cancelled prematurely so that we do not guarantee that * the target is a copy of the source. */ - assert(ret < 0 || ((s->common.force || !s->synced) && + assert(ret < 0 || ((s->common.job.force_cancel || !s->synced) && job_is_cancelled(&s->common.job))); assert(need_drain); mirror_wait_for_all_io(s); diff --git a/blockjob.c b/blockjob.c index 34c57da304..4cac3670ad 100644 --- a/blockjob.c +++ b/blockjob.c @@ -270,19 +270,20 @@ static int block_job_prepare(BlockJob *job) return job->job.ret; } -static void block_job_cancel_async(BlockJob *job, bool force) +static void job_cancel_async(Job *job, bool force) { - if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) { - block_job_iostatus_reset(job); + if (job->user_paused) { + /* Do not call job_enter here, the caller will handle it. */ + job->user_paused = false; + if (job->driver->user_resume) { + job->driver->user_resume(job); + } + assert(job->pause_count > 0); + job->pause_count--; } - if (job->job.user_paused) { - /* Do not call block_job_enter here, the caller will handle it. */ - job->job.user_paused = false; - job->job.pause_count--; - } - job->job.cancelled = true; + job->cancelled = true; /* To prevent 'force == false' overriding a previous 'force == true' */ - job->force |= force; + job->force_cancel |= force; } static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock) @@ -367,7 +368,7 @@ static void block_job_completed_txn_abort(BlockJob *job) * on the caller, so leave it. */ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { if (other_job != job) { - block_job_cancel_async(other_job, false); + job_cancel_async(&other_job->job, false); } } while (!QLIST_EMPTY(&txn->jobs)) { @@ -527,7 +528,7 @@ void block_job_cancel(BlockJob *job, bool force) job_do_dismiss(&job->job); return; } - block_job_cancel_async(job, force); + job_cancel_async(&job->job, force); if (!job_started(&job->job)) { block_job_completed(job, -ECANCELED); } else if (job->job.deferred_to_main_loop) { diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 3f405d1fa7..d975efea20 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -50,12 +50,6 @@ typedef struct BlockJob { /** The block device on which the job is operating. */ BlockBackend *blk; - /** - * Set to true if the job should abort immediately without waiting - * for data to be in sync. - */ - bool force; - /** * Set to true when the job is ready to be completed. */ diff --git a/include/qemu/job.h b/include/qemu/job.h index 3e817beee9..2648c74281 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -97,6 +97,12 @@ typedef struct Job { */ bool cancelled; + /** + * Set to true if the job should abort immediately without waiting + * for data to be in sync. + */ + bool force_cancel; + /** Set to true when the job has deferred work to the main loop. */ bool deferred_to_main_loop; From b69f777dd9ba992fdd35828a90eefcd88c0ec332 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 20 Apr 2018 17:00:29 +0200 Subject: [PATCH 0701/2380] job: Add job_drain() block_job_drain() contains a blk_drain() call which cannot be moved to Job, so add a new JobDriver callback JobDriver.drain which has a common implementation for all BlockJobs. In addition to this we keep the existing BlockJobDriver.drain callback that is called by the common drain implementation for all block jobs. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/backup.c | 1 + block/commit.c | 1 + block/mirror.c | 2 ++ block/stream.c | 1 + blockjob.c | 20 ++++++++++---------- include/block/blockjob_int.h | 12 ++++++++++++ include/qemu/job.h | 13 +++++++++++++ job.c | 11 +++++++++++ tests/test-bdrv-drain.c | 1 + tests/test-blockjob-txn.c | 1 + tests/test-blockjob.c | 2 ++ 11 files changed, 55 insertions(+), 10 deletions(-) diff --git a/block/backup.c b/block/backup.c index bd31282924..ca7d990b21 100644 --- a/block/backup.c +++ b/block/backup.c @@ -529,6 +529,7 @@ static const BlockJobDriver backup_job_driver = { .job_type = JOB_TYPE_BACKUP, .free = block_job_free, .user_resume = block_job_user_resume, + .drain = block_job_drain, .start = backup_run, .commit = backup_commit, .abort = backup_abort, diff --git a/block/commit.c b/block/commit.c index e53b2d7d55..02a8af9127 100644 --- a/block/commit.c +++ b/block/commit.c @@ -221,6 +221,7 @@ static const BlockJobDriver commit_job_driver = { .job_type = JOB_TYPE_COMMIT, .free = block_job_free, .user_resume = block_job_user_resume, + .drain = block_job_drain, .start = commit_run, }, }; diff --git a/block/mirror.c b/block/mirror.c index c3951d1ca2..a579bd8cd1 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -992,6 +992,7 @@ static const BlockJobDriver mirror_job_driver = { .job_type = JOB_TYPE_MIRROR, .free = block_job_free, .user_resume = block_job_user_resume, + .drain = block_job_drain, .start = mirror_run, .pause = mirror_pause, }, @@ -1006,6 +1007,7 @@ static const BlockJobDriver commit_active_job_driver = { .job_type = JOB_TYPE_COMMIT, .free = block_job_free, .user_resume = block_job_user_resume, + .drain = block_job_drain, .start = mirror_run, .pause = mirror_pause, }, diff --git a/block/stream.c b/block/stream.c index eee02538ed..b996278ab3 100644 --- a/block/stream.c +++ b/block/stream.c @@ -215,6 +215,7 @@ static const BlockJobDriver stream_job_driver = { .free = block_job_free, .start = stream_run, .user_resume = block_job_user_resume, + .drain = block_job_drain, }, }; diff --git a/blockjob.c b/blockjob.c index 4cac3670ad..63e166927a 100644 --- a/blockjob.c +++ b/blockjob.c @@ -169,14 +169,13 @@ static void block_job_attached_aio_context(AioContext *new_context, job_resume(&job->job); } -static void block_job_drain(BlockJob *job) +void block_job_drain(Job *job) { - /* If job is !job->job.busy this kicks it into the next pause point. */ - block_job_enter(job); + BlockJob *bjob = container_of(job, BlockJob, job); - blk_drain(job->blk); - if (job->driver->drain) { - job->driver->drain(job); + blk_drain(bjob->blk); + if (bjob->driver->drain) { + bjob->driver->drain(bjob); } } @@ -190,7 +189,7 @@ static void block_job_detach_aio_context(void *opaque) job_pause(&job->job); while (!job->job.paused && !job_is_completed(&job->job)) { - block_job_drain(job); + job_drain(&job->job); } job->job.aio_context = NULL; @@ -327,11 +326,11 @@ static int block_job_finish_sync(BlockJob *job, job_unref(&job->job); return -EBUSY; } - /* block_job_drain calls block_job_enter, and it should be enough to - * induce progress until the job completes or moves to the main thread. + /* job_drain calls job_enter, and it should be enough to induce progress + * until the job completes or moves to the main thread. */ while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) { - block_job_drain(job); + job_drain(&job->job); } while (!job_is_completed(&job->job)) { aio_poll(qemu_get_aio_context(), true); @@ -713,6 +712,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, assert(is_block_job(&job->job)); assert(job->job.driver->free == &block_job_free); assert(job->job.driver->user_resume == &block_job_user_resume); + assert(job->job.driver->drain == &block_job_drain); job->driver = driver; job->blk = blk; diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index bf2b762808..38fe22d7e0 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -65,6 +65,10 @@ struct BlockJobDriver { * If the callback is not NULL, it will be invoked when the job has to be * synchronously cancelled or completed; it should drain BlockDriverStates * as required to ensure progress. + * + * Block jobs must use the default implementation for job_driver.drain, + * which will in turn call this callback after doing generic block job + * stuff. */ void (*drain)(BlockJob *job); }; @@ -111,6 +115,14 @@ void block_job_free(Job *job); */ void block_job_user_resume(Job *job); +/** + * block_job_drain: + * Callback to be used for JobDriver.drain in all block jobs. Drains the main + * block node associated with the block jobs and calls BlockJobDriver.drain for + * job-specific actions. + */ +void block_job_drain(Job *job); + /** * block_job_yield: * @job: The job that calls the function. diff --git a/include/qemu/job.h b/include/qemu/job.h index 2648c74281..aebc1959e6 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -167,6 +167,13 @@ struct JobDriver { */ void (*user_resume)(Job *job); + /* + * If the callback is not NULL, it will be invoked when the job has to be + * synchronously cancelled or completed; it should drain any activities + * as required to ensure progress. + */ + void (*drain)(Job *job); + /** * If the callback is not NULL, it will be invoked when all the jobs * belonging to the same transaction complete; or upon this job's @@ -325,6 +332,12 @@ bool job_user_paused(Job *job); */ void job_user_resume(Job *job, Error **errp); +/* + * Drain any activities as required to ensure progress. This can be called in a + * loop to synchronously complete a job. + */ +void job_drain(Job *job); + /** * Get the next element from the list of block jobs after @job, or the * first one if @job is %NULL. diff --git a/job.c b/job.c index 64b64daf68..3772a35fcf 100644 --- a/job.c +++ b/job.c @@ -367,6 +367,17 @@ void coroutine_fn job_sleep_ns(Job *job, int64_t ns) job_pause_point(job); } +void job_drain(Job *job) +{ + /* If job is !busy this kicks it into the next pause point. */ + job_enter(job); + + if (job->driver->drain) { + job->driver->drain(job); + } +} + + /** * All jobs must allow a pause point before entering their job proper. This * ensures that jobs can be paused prior to being started, then resumed later. diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index c993512f66..58ea5663f0 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -525,6 +525,7 @@ BlockJobDriver test_job_driver = { .instance_size = sizeof(TestBlockJob), .free = block_job_free, .user_resume = block_job_user_resume, + .drain = block_job_drain, .start = test_job_start, }, .complete = test_job_complete, diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 60e9fa2298..1572f8d96f 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -79,6 +79,7 @@ static const BlockJobDriver test_block_job_driver = { .instance_size = sizeof(TestBlockJob), .free = block_job_free, .user_resume = block_job_user_resume, + .drain = block_job_drain, .start = test_block_job_run, }, }; diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 1fe6803fe0..592a13613d 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -21,6 +21,7 @@ static const BlockJobDriver test_block_job_driver = { .instance_size = sizeof(BlockJob), .free = block_job_free, .user_resume = block_job_user_resume, + .drain = block_job_drain, }, }; @@ -201,6 +202,7 @@ static const BlockJobDriver test_cancel_driver = { .instance_size = sizeof(CancelJob), .free = block_job_free, .user_resume = block_job_user_resume, + .drain = block_job_drain, .start = cancel_job_start, }, .complete = cancel_job_complete, From 3453d97243c72988c89a0105fa9546890eae7bd4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 23 Apr 2018 12:24:16 +0200 Subject: [PATCH 0702/2380] job: Move .complete callback to Job This moves the .complete callback that tells a READY job to complete from BlockJobDriver to JobDriver. The wrapper function job_complete() doesn't require anything block job specific any more and can be moved to Job. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/mirror.c | 10 +++++----- blockdev.c | 2 +- blockjob.c | 23 +++++------------------ include/block/blockjob.h | 10 ---------- include/block/blockjob_int.h | 6 ------ include/qemu/job.h | 8 ++++++++ job.c | 16 ++++++++++++++++ tests/test-bdrv-drain.c | 6 +++--- tests/test-blockjob.c | 10 +++++----- 9 files changed, 43 insertions(+), 48 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index a579bd8cd1..656237af5c 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -905,16 +905,16 @@ immediate_exit: job_defer_to_main_loop(&s->common.job, mirror_exit, data); } -static void mirror_complete(BlockJob *job, Error **errp) +static void mirror_complete(Job *job, Error **errp) { - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); BlockDriverState *target; target = blk_bs(s->target); if (!s->synced) { error_setg(errp, "The active block job '%s' cannot be completed", - job->job.id); + job->id); return; } @@ -995,8 +995,8 @@ static const BlockJobDriver mirror_job_driver = { .drain = block_job_drain, .start = mirror_run, .pause = mirror_pause, + .complete = mirror_complete, }, - .complete = mirror_complete, .attached_aio_context = mirror_attached_aio_context, .drain = mirror_drain, }; @@ -1010,8 +1010,8 @@ static const BlockJobDriver commit_active_job_driver = { .drain = block_job_drain, .start = mirror_run, .pause = mirror_pause, + .complete = mirror_complete, }, - .complete = mirror_complete, .attached_aio_context = mirror_attached_aio_context, .drain = mirror_drain, }; diff --git a/blockdev.c b/blockdev.c index 278b92ce03..0967f6ab66 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3894,7 +3894,7 @@ void qmp_block_job_complete(const char *device, Error **errp) } trace_qmp_block_job_complete(job); - block_job_complete(job, errp); + job_complete(&job->job, errp); aio_context_release(aio_context); } diff --git a/blockjob.c b/blockjob.c index 63e166927a..0ca7672941 100644 --- a/blockjob.c +++ b/blockjob.c @@ -481,24 +481,6 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) return ratelimit_calculate_delay(&job->limit, n); } -void block_job_complete(BlockJob *job, Error **errp) -{ - /* Should not be reachable via external interface for internal jobs */ - assert(job->job.id); - if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) { - return; - } - if (job->job.pause_count || job_is_cancelled(&job->job) || - !job->driver->complete) - { - error_setg(errp, "The active block job '%s' cannot be completed", - job->job.id); - return; - } - - job->driver->complete(job, errp); -} - void block_job_finalize(BlockJob *job, Error **errp) { assert(job && job->job.id); @@ -571,6 +553,11 @@ void block_job_cancel_sync_all(void) } } +static void block_job_complete(BlockJob *job, Error **errp) +{ + job_complete(&job->job, errp); +} + int block_job_complete_sync(BlockJob *job, Error **errp) { return block_job_finish_sync(job, &block_job_complete, errp); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index d975efea20..85ce18a381 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -153,16 +153,6 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); */ void block_job_cancel(BlockJob *job, bool force); -/** - * block_job_complete: - * @job: The job to be completed. - * @errp: Error object. - * - * Asynchronously complete the specified job. - */ -void block_job_complete(BlockJob *job, Error **errp); - - /** * block_job_finalize: * @job: The job to fully commit and finish. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 38fe22d7e0..b8ca7bb0c9 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -38,12 +38,6 @@ struct BlockJobDriver { /** Generic JobDriver callbacks and settings */ JobDriver job_driver; - /** - * Optional callback for job types whose completion must be triggered - * manually. - */ - void (*complete)(BlockJob *job, Error **errp); - /** * If the callback is not NULL, prepare will be invoked when all the jobs * belonging to the same transaction complete; or upon this job's completion diff --git a/include/qemu/job.h b/include/qemu/job.h index aebc1959e6..8f7f71a9b1 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -167,6 +167,12 @@ struct JobDriver { */ void (*user_resume)(Job *job); + /** + * Optional callback for job types whose completion must be triggered + * manually. + */ + void (*complete)(Job *job, Error **errp); + /* * If the callback is not NULL, it will be invoked when the job has to be * synchronously cancelled or completed; it should drain any activities @@ -363,6 +369,8 @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp); /** The @job could not be started, free it. */ void job_early_fail(Job *job); +/** Asynchronously complete the specified @job. */ +void job_complete(Job *job, Error **errp);; typedef void JobDeferToMainLoopFn(Job *job, void *opaque); diff --git a/job.c b/job.c index 3772a35fcf..8ceac0b01e 100644 --- a/job.c +++ b/job.c @@ -556,6 +556,22 @@ int job_finalize_single(Job *job) return 0; } +void job_complete(Job *job, Error **errp) +{ + /* Should not be reachable via external interface for internal jobs */ + assert(job->id); + if (job_apply_verb(job, JOB_VERB_COMPLETE, errp)) { + return; + } + if (job->pause_count || job_is_cancelled(job) || !job->driver->complete) { + error_setg(errp, "The active block job '%s' cannot be completed", + job->id); + return; + } + + job->driver->complete(job, errp); +} + typedef struct { Job *job; diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 58ea5663f0..b428aaca68 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -514,9 +514,9 @@ static void coroutine_fn test_job_start(void *opaque) job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); } -static void test_job_complete(BlockJob *job, Error **errp) +static void test_job_complete(Job *job, Error **errp) { - TestBlockJob *s = container_of(job, TestBlockJob, common); + TestBlockJob *s = container_of(job, TestBlockJob, common.job); s->should_complete = true; } @@ -527,8 +527,8 @@ BlockJobDriver test_job_driver = { .user_resume = block_job_user_resume, .drain = block_job_drain, .start = test_job_start, + .complete = test_job_complete, }, - .complete = test_job_complete, }; static void test_blockjob_common(enum drain_type drain_type) diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 592a13613d..e44c608327 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -171,9 +171,9 @@ static void cancel_job_completed(Job *job, void *opaque) block_job_completed(bjob, 0); } -static void cancel_job_complete(BlockJob *job, Error **errp) +static void cancel_job_complete(Job *job, Error **errp) { - CancelJob *s = container_of(job, CancelJob, common); + CancelJob *s = container_of(job, CancelJob, common.job); s->should_complete = true; } @@ -204,8 +204,8 @@ static const BlockJobDriver test_cancel_driver = { .user_resume = block_job_user_resume, .drain = block_job_drain, .start = cancel_job_start, + .complete = cancel_job_complete, }, - .complete = cancel_job_complete, }; static CancelJob *create_common(BlockJob **pjob) @@ -333,7 +333,7 @@ static void test_cancel_pending(void) block_job_enter(job); assert(job->job.status == JOB_STATUS_READY); - block_job_complete(job, &error_abort); + job_complete(&job->job, &error_abort); block_job_enter(job); while (!s->completed) { aio_poll(qemu_get_aio_context(), true); @@ -357,7 +357,7 @@ static void test_cancel_concluded(void) block_job_enter(job); assert(job->job.status == JOB_STATUS_READY); - block_job_complete(job, &error_abort); + job_complete(&job->job, &error_abort); block_job_enter(job); while (!s->completed) { aio_poll(qemu_get_aio_context(), true); From 6a74c075aca731e7e945201a4ae2336b8e328433 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 20 Apr 2018 15:33:57 +0200 Subject: [PATCH 0703/2380] job: Move job_finish_sync() to Job block_job_finish_sync() doesn't contain anything block job specific any more, so it can be moved to Job. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/commit.c | 6 ++--- blockjob.c | 55 ++++++++-------------------------------------- include/qemu/job.h | 9 ++++++++ job.c | 28 +++++++++++++++++++++++ 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/block/commit.c b/block/commit.c index 02a8af9127..40d97a35a5 100644 --- a/block/commit.c +++ b/block/commit.c @@ -112,9 +112,9 @@ static void commit_complete(Job *job, void *opaque) blk_unref(s->top); /* If there is more than one reference to the job (e.g. if called from - * block_job_finish_sync()), block_job_completed() won't free it and - * therefore the blockers on the intermediate nodes remain. This would - * cause bdrv_set_backing_hd() to fail. */ + * job_finish_sync()), block_job_completed() won't free it and therefore + * the blockers on the intermediate nodes remain. This would cause + * bdrv_set_backing_hd() to fail. */ block_job_remove_all_bdrv(bjob); block_job_completed(&s->common, ret); diff --git a/blockjob.c b/blockjob.c index 0ca7672941..1ed3e9c88d 100644 --- a/blockjob.c +++ b/blockjob.c @@ -307,40 +307,6 @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock) return rc; } -static int block_job_finish_sync(BlockJob *job, - void (*finish)(BlockJob *, Error **errp), - Error **errp) -{ - Error *local_err = NULL; - int ret; - - assert(blk_bs(job->blk)->job == job); - - job_ref(&job->job); - - if (finish) { - finish(job, &local_err); - } - if (local_err) { - error_propagate(errp, local_err); - job_unref(&job->job); - return -EBUSY; - } - /* job_drain calls job_enter, and it should be enough to induce progress - * until the job completes or moves to the main thread. - */ - while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) { - job_drain(&job->job); - } - while (!job_is_completed(&job->job)) { - aio_poll(qemu_get_aio_context(), true); - } - ret = (job_is_cancelled(&job->job) && job->job.ret == 0) - ? -ECANCELED : job->job.ret; - job_unref(&job->job); - return ret; -} - static void block_job_completed_txn_abort(BlockJob *job) { AioContext *ctx; @@ -375,7 +341,7 @@ static void block_job_completed_txn_abort(BlockJob *job) ctx = blk_get_aio_context(other_job->blk); if (!job_is_completed(&other_job->job)) { assert(job_is_cancelled(&other_job->job)); - block_job_finish_sync(other_job, NULL, NULL); + job_finish_sync(&other_job->job, NULL, NULL); } job_finalize_single(&other_job->job); aio_context_release(ctx); @@ -528,16 +494,18 @@ void block_job_user_cancel(BlockJob *job, bool force, Error **errp) } /* A wrapper around block_job_cancel() taking an Error ** parameter so it may be - * used with block_job_finish_sync() without the need for (rather nasty) - * function pointer casts there. */ -static void block_job_cancel_err(BlockJob *job, Error **errp) + * used with job_finish_sync() without the need for (rather nasty) function + * pointer casts there. */ +static void block_job_cancel_err(Job *job, Error **errp) { - block_job_cancel(job, false); + BlockJob *bjob = container_of(job, BlockJob, job); + assert(is_block_job(job)); + block_job_cancel(bjob, false); } int block_job_cancel_sync(BlockJob *job) { - return block_job_finish_sync(job, &block_job_cancel_err, NULL); + return job_finish_sync(&job->job, &block_job_cancel_err, NULL); } void block_job_cancel_sync_all(void) @@ -553,14 +521,9 @@ void block_job_cancel_sync_all(void) } } -static void block_job_complete(BlockJob *job, Error **errp) -{ - job_complete(&job->job, errp); -} - int block_job_complete_sync(BlockJob *job, Error **errp) { - return block_job_finish_sync(job, &block_job_complete, errp); + return job_finish_sync(&job->job, job_complete, errp); } void block_job_progress_update(BlockJob *job, uint64_t done) diff --git a/include/qemu/job.h b/include/qemu/job.h index 8f7f71a9b1..17e2ceca87 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -389,6 +389,15 @@ typedef void JobDeferToMainLoopFn(Job *job, void *opaque); */ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); +/** + * Synchronously finishes the given @job. If @finish is given, it is called to + * trigger completion or cancellation of the job. + * + * Returns 0 if the job is successfully completed, -ECANCELED if the job was + * cancelled before completing, and -errno in other error cases. + */ +int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp); + /* TODO To be removed from the public interface */ void job_state_transition(Job *job, JobStatus s1); void coroutine_fn job_do_yield(Job *job, uint64_t ns); diff --git a/job.c b/job.c index 8ceac0b01e..aa74b4c03b 100644 --- a/job.c +++ b/job.c @@ -603,3 +603,31 @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque) aio_bh_schedule_oneshot(qemu_get_aio_context(), job_defer_to_main_loop_bh, data); } + +int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) +{ + Error *local_err = NULL; + int ret; + + job_ref(job); + + if (finish) { + finish(job, &local_err); + } + if (local_err) { + error_propagate(errp, local_err); + job_unref(job); + return -EBUSY; + } + /* job_drain calls job_enter, and it should be enough to induce progress + * until the job completes or moves to the main thread. */ + while (!job->deferred_to_main_loop && !job_is_completed(job)) { + job_drain(job); + } + while (!job_is_completed(job)) { + aio_poll(qemu_get_aio_context(), true); + } + ret = (job_is_cancelled(job) && job->ret == 0) ? -ECANCELED : job->ret; + job_unref(job); + return ret; +} From 62c9e4162a7bc26a1389e50d17d3b2637028bbc3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 19 Apr 2018 16:09:52 +0200 Subject: [PATCH 0704/2380] job: Switch transactions to JobTxn This doesn't actually move any transaction code to Job yet, but it renames the type for transactions from BlockJobTxn to JobTxn and makes them contain Jobs rather than BlockJobs Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/backup.c | 2 +- blockdev.c | 14 ++++----- blockjob.c | 60 +++++++++++++++++++----------------- include/block/block_int.h | 2 +- include/block/blockjob.h | 11 +++---- include/block/blockjob_int.h | 2 +- include/qemu/job.h | 3 ++ tests/test-blockjob-txn.c | 8 ++--- 8 files changed, 54 insertions(+), 48 deletions(-) diff --git a/block/backup.c b/block/backup.c index ca7d990b21..6172f90c90 100644 --- a/block/backup.c +++ b/block/backup.c @@ -547,7 +547,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, - BlockJobTxn *txn, Error **errp) + JobTxn *txn, Error **errp) { int64_t len; BlockDriverInfo bdi; diff --git a/blockdev.c b/blockdev.c index 0967f6ab66..817c3848c0 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1446,7 +1446,7 @@ typedef struct BlkActionOps { struct BlkActionState { TransactionAction *action; const BlkActionOps *ops; - BlockJobTxn *block_job_txn; + JobTxn *block_job_txn; TransactionProperties *txn_props; QSIMPLEQ_ENTRY(BlkActionState) entry; }; @@ -1864,7 +1864,7 @@ typedef struct DriveBackupState { BlockJob *job; } DriveBackupState; -static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, +static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, Error **errp); static void drive_backup_prepare(BlkActionState *common, Error **errp) @@ -1954,7 +1954,7 @@ typedef struct BlockdevBackupState { BlockJob *job; } BlockdevBackupState; -static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, +static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, Error **errp); static void blockdev_backup_prepare(BlkActionState *common, Error **errp) @@ -2243,7 +2243,7 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp) { TransactionActionList *dev_entry = dev_list; - BlockJobTxn *block_job_txn = NULL; + JobTxn *block_job_txn = NULL; BlkActionState *state, *next; Error *local_err = NULL; @@ -2251,7 +2251,7 @@ void qmp_transaction(TransactionActionList *dev_list, QSIMPLEQ_INIT(&snap_bdrv_states); /* Does this transaction get canceled as a group on failure? - * If not, we don't really need to make a BlockJobTxn. + * If not, we don't really need to make a JobTxn. */ props = get_transaction_properties(props); if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { @@ -3264,7 +3264,7 @@ out: aio_context_release(aio_context); } -static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, +static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, Error **errp) { BlockDriverState *bs; @@ -3434,7 +3434,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) return bdrv_named_nodes_list(errp); } -BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, +BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, Error **errp) { BlockDriverState *bs; diff --git a/blockjob.c b/blockjob.c index 1ed3e9c88d..bd35c4fa7a 100644 --- a/blockjob.c +++ b/blockjob.c @@ -37,13 +37,13 @@ #include "qemu/timer.h" /* Transactional group of block jobs */ -struct BlockJobTxn { +struct JobTxn { /* Is this txn being cancelled? */ bool aborting; /* List of jobs */ - QLIST_HEAD(, BlockJob) jobs; + QLIST_HEAD(, Job) jobs; /* Reference count */ int refcnt; @@ -94,27 +94,27 @@ BlockJob *block_job_get(const char *id) } } -BlockJobTxn *block_job_txn_new(void) +JobTxn *block_job_txn_new(void) { - BlockJobTxn *txn = g_new0(BlockJobTxn, 1); + JobTxn *txn = g_new0(JobTxn, 1); QLIST_INIT(&txn->jobs); txn->refcnt = 1; return txn; } -static void block_job_txn_ref(BlockJobTxn *txn) +static void block_job_txn_ref(JobTxn *txn) { txn->refcnt++; } -void block_job_txn_unref(BlockJobTxn *txn) +void block_job_txn_unref(JobTxn *txn) { if (txn && --txn->refcnt == 0) { g_free(txn); } } -void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job) +void block_job_txn_add_job(JobTxn *txn, BlockJob *job) { if (!txn) { return; @@ -123,14 +123,14 @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job) assert(!job->txn); job->txn = txn; - QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); + QLIST_INSERT_HEAD(&txn->jobs, &job->job, txn_list); block_job_txn_ref(txn); } void block_job_txn_del_job(BlockJob *job) { if (job->txn) { - QLIST_REMOVE(job, txn_list); + QLIST_REMOVE(&job->job, txn_list); block_job_txn_unref(job->txn); job->txn = NULL; } @@ -285,18 +285,22 @@ static void job_cancel_async(Job *job, bool force) job->force_cancel |= force; } -static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock) +static int block_job_txn_apply(JobTxn *txn, int fn(BlockJob *), bool lock) { AioContext *ctx; - BlockJob *job, *next; + Job *job, *next; + BlockJob *bjob; int rc = 0; QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { + assert(is_block_job(job)); + bjob = container_of(job, BlockJob, job); + if (lock) { - ctx = blk_get_aio_context(job->blk); + ctx = job->aio_context; aio_context_acquire(ctx); } - rc = fn(job); + rc = fn(bjob); if (lock) { aio_context_release(ctx); } @@ -310,8 +314,8 @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock) static void block_job_completed_txn_abort(BlockJob *job) { AioContext *ctx; - BlockJobTxn *txn = job->txn; - BlockJob *other_job; + JobTxn *txn = job->txn; + Job *other_job; if (txn->aborting) { /* @@ -324,7 +328,7 @@ static void block_job_completed_txn_abort(BlockJob *job) /* We are the first failed job. Cancel other jobs. */ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - ctx = blk_get_aio_context(other_job->blk); + ctx = other_job->aio_context; aio_context_acquire(ctx); } @@ -332,18 +336,18 @@ static void block_job_completed_txn_abort(BlockJob *job) * them; this job, however, may or may not be cancelled, depending * on the caller, so leave it. */ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - if (other_job != job) { - job_cancel_async(&other_job->job, false); + if (other_job != &job->job) { + job_cancel_async(other_job, false); } } while (!QLIST_EMPTY(&txn->jobs)) { other_job = QLIST_FIRST(&txn->jobs); - ctx = blk_get_aio_context(other_job->blk); - if (!job_is_completed(&other_job->job)) { - assert(job_is_cancelled(&other_job->job)); - job_finish_sync(&other_job->job, NULL, NULL); + ctx = other_job->aio_context; + if (!job_is_completed(other_job)) { + assert(job_is_cancelled(other_job)); + job_finish_sync(other_job, NULL, NULL); } - job_finalize_single(&other_job->job); + job_finalize_single(other_job); aio_context_release(ctx); } @@ -385,8 +389,8 @@ static int block_job_transition_to_pending(BlockJob *job) static void block_job_completed_txn_success(BlockJob *job) { - BlockJobTxn *txn = job->txn; - BlockJob *other_job; + JobTxn *txn = job->txn; + Job *other_job; job_state_transition(&job->job, JOB_STATUS_WAITING); @@ -395,10 +399,10 @@ static void block_job_completed_txn_success(BlockJob *job) * txn. */ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - if (!job_is_completed(&other_job->job)) { + if (!job_is_completed(other_job)) { return; } - assert(other_job->job.ret == 0); + assert(other_job->ret == 0); } block_job_txn_apply(txn, block_job_transition_to_pending, false); @@ -628,7 +632,7 @@ static void block_job_event_pending(Notifier *n, void *opaque) */ void *block_job_create(const char *job_id, const BlockJobDriver *driver, - BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm, + JobTxn *txn, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, int64_t speed, int flags, BlockCompletionFunc *cb, void *opaque, Error **errp) { diff --git a/include/block/block_int.h b/include/block/block_int.h index 76b589da57..6c0927bce3 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -1029,7 +1029,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, - BlockJobTxn *txn, Error **errp); + JobTxn *txn, Error **errp); void hmp_drive_add_node(Monitor *mon, const char *optstr); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 85ce18a381..44df025bd0 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -33,7 +33,7 @@ #define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ typedef struct BlockJobDriver BlockJobDriver; -typedef struct BlockJobTxn BlockJobTxn; +typedef struct JobTxn JobTxn; /** * BlockJob: @@ -85,8 +85,7 @@ typedef struct BlockJob { /** BlockDriverStates that are involved in this block job */ GSList *nodes; - BlockJobTxn *txn; - QLIST_ENTRY(BlockJob) txn_list; + JobTxn *txn; } BlockJob; /** @@ -273,7 +272,7 @@ void block_job_iostatus_reset(BlockJob *job); * group. Jobs wait for each other before completing. Cancelling one job * cancels all jobs in the transaction. */ -BlockJobTxn *block_job_txn_new(void); +JobTxn *block_job_txn_new(void); /** * block_job_txn_unref: @@ -282,7 +281,7 @@ BlockJobTxn *block_job_txn_new(void); * or block_job_txn_new. If it's the last reference to the object, it will be * freed. */ -void block_job_txn_unref(BlockJobTxn *txn); +void block_job_txn_unref(JobTxn *txn); /** * block_job_txn_add_job: @@ -293,7 +292,7 @@ void block_job_txn_unref(BlockJobTxn *txn); * The caller must call either block_job_txn_unref() or block_job_completed() * to release the reference that is automatically grabbed here. */ -void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job); +void block_job_txn_add_job(JobTxn *txn, BlockJob *job); /** * block_job_is_internal: diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index b8ca7bb0c9..ce66a9b51c 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -91,7 +91,7 @@ struct BlockJobDriver { * called from a wrapper that is specific to the job type. */ void *block_job_create(const char *job_id, const BlockJobDriver *driver, - BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm, + JobTxn *txn, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, int64_t speed, int flags, BlockCompletionFunc *cb, void *opaque, Error **errp); diff --git a/include/qemu/job.h b/include/qemu/job.h index 17e2ceca87..d4aa7fa981 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -132,6 +132,9 @@ typedef struct Job { /** Element of the list of jobs */ QLIST_ENTRY(Job) job_list; + + /** Element of the list of jobs in a job transaction */ + QLIST_ENTRY(Job) txn_list; } Job; /** diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 1572f8d96f..ec5d592b68 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -93,7 +93,7 @@ static const BlockJobDriver test_block_job_driver = { */ static BlockJob *test_block_job_start(unsigned int iterations, bool use_timer, - int rc, int *result, BlockJobTxn *txn) + int rc, int *result, JobTxn *txn) { BlockDriverState *bs; TestBlockJob *s; @@ -122,7 +122,7 @@ static BlockJob *test_block_job_start(unsigned int iterations, static void test_single_job(int expected) { BlockJob *job; - BlockJobTxn *txn; + JobTxn *txn; int result = -EINPROGRESS; txn = block_job_txn_new(); @@ -160,7 +160,7 @@ static void test_pair_jobs(int expected1, int expected2) { BlockJob *job1; BlockJob *job2; - BlockJobTxn *txn; + JobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; @@ -222,7 +222,7 @@ static void test_pair_jobs_fail_cancel_race(void) { BlockJob *job1; BlockJob *job2; - BlockJobTxn *txn; + JobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; From 7eaa8fb57da96301f4a8ce176ad503f80efc7cc0 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 23 Apr 2018 16:06:26 +0200 Subject: [PATCH 0705/2380] job: Move transactions to Job This moves the logic that implements job transactions from BlockJob to Job. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- blockdev.c | 6 +- blockjob.c | 238 +---------------------------------- include/block/blockjob.h | 54 -------- include/block/blockjob_int.h | 10 -- include/qemu/job.h | 71 +++++++++-- job.c | 234 ++++++++++++++++++++++++++++++++-- tests/test-blockjob-txn.c | 12 +- tests/test-blockjob.c | 2 +- 8 files changed, 303 insertions(+), 324 deletions(-) diff --git a/blockdev.c b/blockdev.c index 817c3848c0..87a23bed6f 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2255,7 +2255,7 @@ void qmp_transaction(TransactionActionList *dev_list, */ props = get_transaction_properties(props); if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { - block_job_txn = block_job_txn_new(); + block_job_txn = job_txn_new(); } /* drain all i/o before any operations */ @@ -2314,7 +2314,7 @@ exit: if (!has_props) { qapi_free_TransactionProperties(props); } - block_job_txn_unref(block_job_txn); + job_txn_unref(block_job_txn); } void qmp_eject(bool has_device, const char *device, @@ -3908,7 +3908,7 @@ void qmp_block_job_finalize(const char *id, Error **errp) } trace_qmp_block_job_finalize(job); - block_job_finalize(job, errp); + job_finalize(&job->job, errp); aio_context_release(aio_context); } diff --git a/blockjob.c b/blockjob.c index bd35c4fa7a..14b21c86b7 100644 --- a/blockjob.c +++ b/blockjob.c @@ -36,19 +36,6 @@ #include "qemu/coroutine.h" #include "qemu/timer.h" -/* Transactional group of block jobs */ -struct JobTxn { - - /* Is this txn being cancelled? */ - bool aborting; - - /* List of jobs */ - QLIST_HEAD(, Job) jobs; - - /* Reference count */ - int refcnt; -}; - /* * The block job API is composed of two categories of functions. * @@ -94,48 +81,6 @@ BlockJob *block_job_get(const char *id) } } -JobTxn *block_job_txn_new(void) -{ - JobTxn *txn = g_new0(JobTxn, 1); - QLIST_INIT(&txn->jobs); - txn->refcnt = 1; - return txn; -} - -static void block_job_txn_ref(JobTxn *txn) -{ - txn->refcnt++; -} - -void block_job_txn_unref(JobTxn *txn) -{ - if (txn && --txn->refcnt == 0) { - g_free(txn); - } -} - -void block_job_txn_add_job(JobTxn *txn, BlockJob *job) -{ - if (!txn) { - return; - } - - assert(!job->txn); - job->txn = txn; - - QLIST_INSERT_HEAD(&txn->jobs, &job->job, txn_list); - block_job_txn_ref(txn); -} - -void block_job_txn_del_job(BlockJob *job) -{ - if (job->txn) { - QLIST_REMOVE(&job->job, txn_list); - block_job_txn_unref(job->txn); - job->txn = NULL; - } -} - static void block_job_attached_aio_context(AioContext *new_context, void *opaque); static void block_job_detach_aio_context(void *opaque); @@ -145,8 +90,6 @@ void block_job_free(Job *job) BlockJob *bjob = container_of(job, BlockJob, job); BlockDriverState *bs = blk_bs(bjob->blk); - assert(!bjob->txn); - bs->job = NULL; block_job_remove_all_bdrv(bjob); blk_remove_aio_context_notifier(bjob->blk, @@ -261,158 +204,6 @@ const BlockJobDriver *block_job_driver(BlockJob *job) return job->driver; } -static int block_job_prepare(BlockJob *job) -{ - if (job->job.ret == 0 && job->driver->prepare) { - job->job.ret = job->driver->prepare(job); - } - return job->job.ret; -} - -static void job_cancel_async(Job *job, bool force) -{ - if (job->user_paused) { - /* Do not call job_enter here, the caller will handle it. */ - job->user_paused = false; - if (job->driver->user_resume) { - job->driver->user_resume(job); - } - assert(job->pause_count > 0); - job->pause_count--; - } - job->cancelled = true; - /* To prevent 'force == false' overriding a previous 'force == true' */ - job->force_cancel |= force; -} - -static int block_job_txn_apply(JobTxn *txn, int fn(BlockJob *), bool lock) -{ - AioContext *ctx; - Job *job, *next; - BlockJob *bjob; - int rc = 0; - - QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { - assert(is_block_job(job)); - bjob = container_of(job, BlockJob, job); - - if (lock) { - ctx = job->aio_context; - aio_context_acquire(ctx); - } - rc = fn(bjob); - if (lock) { - aio_context_release(ctx); - } - if (rc) { - break; - } - } - return rc; -} - -static void block_job_completed_txn_abort(BlockJob *job) -{ - AioContext *ctx; - JobTxn *txn = job->txn; - Job *other_job; - - if (txn->aborting) { - /* - * We are cancelled by another job, which will handle everything. - */ - return; - } - txn->aborting = true; - block_job_txn_ref(txn); - - /* We are the first failed job. Cancel other jobs. */ - QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - ctx = other_job->aio_context; - aio_context_acquire(ctx); - } - - /* Other jobs are effectively cancelled by us, set the status for - * them; this job, however, may or may not be cancelled, depending - * on the caller, so leave it. */ - QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - if (other_job != &job->job) { - job_cancel_async(other_job, false); - } - } - while (!QLIST_EMPTY(&txn->jobs)) { - other_job = QLIST_FIRST(&txn->jobs); - ctx = other_job->aio_context; - if (!job_is_completed(other_job)) { - assert(job_is_cancelled(other_job)); - job_finish_sync(other_job, NULL, NULL); - } - job_finalize_single(other_job); - aio_context_release(ctx); - } - - block_job_txn_unref(txn); -} - -static int block_job_needs_finalize(BlockJob *job) -{ - return !job->job.auto_finalize; -} - -static int block_job_finalize_single(BlockJob *job) -{ - return job_finalize_single(&job->job); -} - -static void block_job_do_finalize(BlockJob *job) -{ - int rc; - assert(job && job->txn); - - /* prepare the transaction to complete */ - rc = block_job_txn_apply(job->txn, block_job_prepare, true); - if (rc) { - block_job_completed_txn_abort(job); - } else { - block_job_txn_apply(job->txn, block_job_finalize_single, true); - } -} - -static int block_job_transition_to_pending(BlockJob *job) -{ - job_state_transition(&job->job, JOB_STATUS_PENDING); - if (!job->job.auto_finalize) { - job_event_pending(&job->job); - } - return 0; -} - -static void block_job_completed_txn_success(BlockJob *job) -{ - JobTxn *txn = job->txn; - Job *other_job; - - job_state_transition(&job->job, JOB_STATUS_WAITING); - - /* - * Successful completion, see if there are other running jobs in this - * txn. - */ - QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - if (!job_is_completed(other_job)) { - return; - } - assert(other_job->ret == 0); - } - - block_job_txn_apply(txn, block_job_transition_to_pending, false); - - /* If no jobs need manual finalization, automatically do so */ - if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) { - block_job_do_finalize(job); - } -} - /* Assumes the job_mutex is held */ static bool job_timer_pending(Job *job) { @@ -451,15 +242,6 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) return ratelimit_calculate_delay(&job->limit, n); } -void block_job_finalize(BlockJob *job, Error **errp) -{ - assert(job && job->job.id); - if (job_apply_verb(&job->job, JOB_VERB_FINALIZE, errp)) { - return; - } - block_job_do_finalize(job); -} - void block_job_dismiss(BlockJob **jobptr, Error **errp) { BlockJob *job = *jobptr; @@ -483,7 +265,7 @@ void block_job_cancel(BlockJob *job, bool force) if (!job_started(&job->job)) { block_job_completed(job, -ECANCELED); } else if (job->job.deferred_to_main_loop) { - block_job_completed_txn_abort(job); + job_completed_txn_abort(&job->job); } else { block_job_enter(job); } @@ -656,7 +438,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return NULL; } - job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk), + job = job_create(job_id, &driver->job_driver, txn, blk_get_aio_context(blk), flags, cb, opaque, errp); if (job == NULL) { blk_unref(blk); @@ -703,30 +485,20 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, } } - /* Single jobs are modeled as single-job transactions for sake of - * consolidating the job management logic */ - if (!txn) { - txn = block_job_txn_new(); - block_job_txn_add_job(txn, job); - block_job_txn_unref(txn); - } else { - block_job_txn_add_job(txn, job); - } - return job; } void block_job_completed(BlockJob *job, int ret) { - assert(job && job->txn && !job_is_completed(&job->job)); + assert(job && job->job.txn && !job_is_completed(&job->job)); assert(blk_bs(job->blk)->job == job); job->job.ret = ret; job_update_rc(&job->job); trace_block_job_completed(job, ret, job->job.ret); if (job->job.ret) { - block_job_completed_txn_abort(job); + job_completed_txn_abort(&job->job); } else { - block_job_completed_txn_success(job); + job_completed_txn_success(&job->job); } } diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 44df025bd0..09e6bb42bf 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -33,7 +33,6 @@ #define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ typedef struct BlockJobDriver BlockJobDriver; -typedef struct JobTxn JobTxn; /** * BlockJob: @@ -84,8 +83,6 @@ typedef struct BlockJob { /** BlockDriverStates that are involved in this block job */ GSList *nodes; - - JobTxn *txn; } BlockJob; /** @@ -152,22 +149,6 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); */ void block_job_cancel(BlockJob *job, bool force); -/** - * block_job_finalize: - * @job: The job to fully commit and finish. - * @errp: Error object. - * - * For jobs that have finished their work and are pending - * awaiting explicit acknowledgement to commit their work, - * This will commit that work. - * - * FIXME: Make the below statement universally true: - * For jobs that support the manual workflow mode, all graph - * changes that occur as a result will occur after this command - * and before a successful reply. - */ -void block_job_finalize(BlockJob *job, Error **errp); - /** * block_job_dismiss: * @job: The job to be dismissed. @@ -259,41 +240,6 @@ int block_job_complete_sync(BlockJob *job, Error **errp); */ void block_job_iostatus_reset(BlockJob *job); -/** - * block_job_txn_new: - * - * Allocate and return a new block job transaction. Jobs can be added to the - * transaction using block_job_txn_add_job(). - * - * The transaction is automatically freed when the last job completes or is - * cancelled. - * - * All jobs in the transaction either complete successfully or fail/cancel as a - * group. Jobs wait for each other before completing. Cancelling one job - * cancels all jobs in the transaction. - */ -JobTxn *block_job_txn_new(void); - -/** - * block_job_txn_unref: - * - * Release a reference that was previously acquired with block_job_txn_add_job - * or block_job_txn_new. If it's the last reference to the object, it will be - * freed. - */ -void block_job_txn_unref(JobTxn *txn); - -/** - * block_job_txn_add_job: - * @txn: The transaction (may be NULL) - * @job: Job to add to the transaction - * - * Add @job to the transaction. The @job must not already be in a transaction. - * The caller must call either block_job_txn_unref() or block_job_completed() - * to release the reference that is automatically grabbed here. - */ -void block_job_txn_add_job(JobTxn *txn, BlockJob *job); - /** * block_job_is_internal: * @job: The job to determine if it is user-visible or not. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index ce66a9b51c..29a28020ac 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -38,16 +38,6 @@ struct BlockJobDriver { /** Generic JobDriver callbacks and settings */ JobDriver job_driver; - /** - * If the callback is not NULL, prepare will be invoked when all the jobs - * belonging to the same transaction complete; or upon this job's completion - * if it is not in a transaction. - * - * This callback will not be invoked if the job has already failed. - * If it fails, abort and then clean will be called. - */ - int (*prepare)(BlockJob *job); - /* * If the callback is not NULL, it will be invoked before the job is * resumed in a new AioContext. This is the place to move any resources diff --git a/include/qemu/job.h b/include/qemu/job.h index d4aa7fa981..39d74abdec 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -32,6 +32,8 @@ #include "block/aio.h" typedef struct JobDriver JobDriver; +typedef struct JobTxn JobTxn; + /** * Long-running operation. @@ -133,6 +135,9 @@ typedef struct Job { /** Element of the list of jobs */ QLIST_ENTRY(Job) job_list; + /** Transaction this job is part of */ + JobTxn *txn; + /** Element of the list of jobs in a job transaction */ QLIST_ENTRY(Job) txn_list; } Job; @@ -183,6 +188,16 @@ struct JobDriver { */ void (*drain)(Job *job); + /** + * If the callback is not NULL, prepare will be invoked when all the jobs + * belonging to the same transaction complete; or upon this job's completion + * if it is not in a transaction. + * + * This callback will not be invoked if the job has already failed. + * If it fails, abort and then clean will be called. + */ + int (*prepare)(Job *job); + /** * If the callback is not NULL, it will be invoked when all the jobs * belonging to the same transaction complete; or upon this job's @@ -227,20 +242,52 @@ typedef enum JobCreateFlags { JOB_MANUAL_DISMISS = 0x04, } JobCreateFlags; +/** + * Allocate and return a new job transaction. Jobs can be added to the + * transaction using job_txn_add_job(). + * + * The transaction is automatically freed when the last job completes or is + * cancelled. + * + * All jobs in the transaction either complete successfully or fail/cancel as a + * group. Jobs wait for each other before completing. Cancelling one job + * cancels all jobs in the transaction. + */ +JobTxn *job_txn_new(void); + +/** + * Release a reference that was previously acquired with job_txn_add_job or + * job_txn_new. If it's the last reference to the object, it will be freed. + */ +void job_txn_unref(JobTxn *txn); + +/** + * @txn: The transaction (may be NULL) + * @job: Job to add to the transaction + * + * Add @job to the transaction. The @job must not already be in a transaction. + * The caller must call either job_txn_unref() or block_job_completed() to + * release the reference that is automatically grabbed here. + * + * If @txn is NULL, the function does nothing. + */ +void job_txn_add_job(JobTxn *txn, Job *job); /** * Create a new long-running job and return it. * * @job_id: The id of the newly-created job, or %NULL for internal jobs * @driver: The class object for the newly-created job. + * @txn: The transaction this job belongs to, if any. %NULL otherwise. * @ctx: The AioContext to run the job coroutine in. * @flags: Creation flags for the job. See @JobCreateFlags. * @cb: Completion function for the job. * @opaque: Opaque pointer value passed to @cb. * @errp: Error object. */ -void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, - int flags, BlockCompletionFunc *cb, void *opaque, Error **errp); +void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, + AioContext *ctx, int flags, BlockCompletionFunc *cb, + void *opaque, Error **errp); /** * Add a reference to Job refcnt, it will be decreased with job_unref, and then @@ -260,9 +307,6 @@ void job_event_cancelled(Job *job); /** To be called when a successfully completed job is finalised. */ void job_event_completed(Job *job); -/** To be called when the job transitions to PENDING */ -void job_event_pending(Job *job); - /** * Conditionally enter the job coroutine if the job is ready to run, not * already busy and fn() returns true. fn() is called while under the job_lock @@ -375,6 +419,16 @@ void job_early_fail(Job *job); /** Asynchronously complete the specified @job. */ void job_complete(Job *job, Error **errp);; +/** + * For a @job that has finished its work and is pending awaiting explicit + * acknowledgement to commit its work, this will commit that work. + * + * FIXME: Make the below statement universally true: + * For jobs that support the manual workflow mode, all graph changes that occur + * as a result will occur after this command and before a successful reply. + */ +void job_finalize(Job *job, Error **errp); + typedef void JobDeferToMainLoopFn(Job *job, void *opaque); /** @@ -407,10 +461,9 @@ void coroutine_fn job_do_yield(Job *job, uint64_t ns); bool job_should_pause(Job *job); bool job_started(Job *job); void job_do_dismiss(Job *job); -int job_finalize_single(Job *job); void job_update_rc(Job *job); - -typedef struct BlockJob BlockJob; -void block_job_txn_del_job(BlockJob *job); +void job_cancel_async(Job *job, bool force); +void job_completed_txn_abort(Job *job); +void job_completed_txn_success(Job *job); #endif diff --git a/job.c b/job.c index aa74b4c03b..4f6fd73cd7 100644 --- a/job.c +++ b/job.c @@ -60,6 +60,19 @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, }; +/* Transactional group of jobs */ +struct JobTxn { + + /* Is this txn being cancelled? */ + bool aborting; + + /* List of jobs */ + QLIST_HEAD(, Job) jobs; + + /* Reference count */ + int refcnt; +}; + /* Right now, this mutex is only needed to synchronize accesses to job->busy * and job->sleep_timer, such as concurrent calls to job_do_yield and * job_enter. */ @@ -80,6 +93,71 @@ static void __attribute__((__constructor__)) job_init(void) qemu_mutex_init(&job_mutex); } +JobTxn *job_txn_new(void) +{ + JobTxn *txn = g_new0(JobTxn, 1); + QLIST_INIT(&txn->jobs); + txn->refcnt = 1; + return txn; +} + +static void job_txn_ref(JobTxn *txn) +{ + txn->refcnt++; +} + +void job_txn_unref(JobTxn *txn) +{ + if (txn && --txn->refcnt == 0) { + g_free(txn); + } +} + +void job_txn_add_job(JobTxn *txn, Job *job) +{ + if (!txn) { + return; + } + + assert(!job->txn); + job->txn = txn; + + QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); + job_txn_ref(txn); +} + +static void job_txn_del_job(Job *job) +{ + if (job->txn) { + QLIST_REMOVE(job, txn_list); + job_txn_unref(job->txn); + job->txn = NULL; + } +} + +static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) +{ + AioContext *ctx; + Job *job, *next; + int rc = 0; + + QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { + if (lock) { + ctx = job->aio_context; + aio_context_acquire(ctx); + } + rc = fn(job); + if (lock) { + aio_context_release(ctx); + } + if (rc) { + break; + } + } + return rc; +} + + /* TODO Make static once the whole state machine is in job.c */ void job_state_transition(Job *job, JobStatus s1) { @@ -181,8 +259,9 @@ static void job_sleep_timer_cb(void *opaque) job_enter(job); } -void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, - int flags, BlockCompletionFunc *cb, void *opaque, Error **errp) +void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, + AioContext *ctx, int flags, BlockCompletionFunc *cb, + void *opaque, Error **errp) { Job *job; @@ -228,6 +307,16 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx, QLIST_INSERT_HEAD(&jobs, job, job_list); + /* Single jobs are modeled as single-job transactions for sake of + * consolidating the job management logic */ + if (!txn) { + txn = job_txn_new(); + job_txn_add_job(txn, job); + job_txn_unref(txn); + } else { + job_txn_add_job(txn, job); + } + return job; } @@ -241,6 +330,7 @@ void job_unref(Job *job) if (--job->refcnt == 0) { assert(job->status == JOB_STATUS_NULL); assert(!timer_pending(&job->sleep_timer)); + assert(!job->txn); if (job->driver->free) { job->driver->free(job); @@ -263,7 +353,7 @@ void job_event_completed(Job *job) notifier_list_notify(&job->on_finalize_completed, job); } -void job_event_pending(Job *job) +static void job_event_pending(Job *job) { notifier_list_notify(&job->on_pending, job); } @@ -469,8 +559,7 @@ void job_do_dismiss(Job *job) job->paused = false; job->deferred_to_main_loop = true; - /* TODO Don't assume it's a BlockJob */ - block_job_txn_del_job((BlockJob*) job); + job_txn_del_job(job); job_state_transition(job, JOB_STATUS_NULL); job_unref(job); @@ -523,7 +612,7 @@ static void job_clean(Job *job) } } -int job_finalize_single(Job *job) +static int job_finalize_single(Job *job) { assert(job_is_completed(job)); @@ -550,12 +639,141 @@ int job_finalize_single(Job *job) } } - /* TODO Don't assume it's a BlockJob */ - block_job_txn_del_job((BlockJob*) job); + job_txn_del_job(job); job_conclude(job); return 0; } +void job_cancel_async(Job *job, bool force) +{ + if (job->user_paused) { + /* Do not call job_enter here, the caller will handle it. */ + job->user_paused = false; + if (job->driver->user_resume) { + job->driver->user_resume(job); + } + assert(job->pause_count > 0); + job->pause_count--; + } + job->cancelled = true; + /* To prevent 'force == false' overriding a previous 'force == true' */ + job->force_cancel |= force; +} + +void job_completed_txn_abort(Job *job) +{ + AioContext *ctx; + JobTxn *txn = job->txn; + Job *other_job; + + if (txn->aborting) { + /* + * We are cancelled by another job, which will handle everything. + */ + return; + } + txn->aborting = true; + job_txn_ref(txn); + + /* We are the first failed job. Cancel other jobs. */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + ctx = other_job->aio_context; + aio_context_acquire(ctx); + } + + /* Other jobs are effectively cancelled by us, set the status for + * them; this job, however, may or may not be cancelled, depending + * on the caller, so leave it. */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + if (other_job != job) { + job_cancel_async(other_job, false); + } + } + while (!QLIST_EMPTY(&txn->jobs)) { + other_job = QLIST_FIRST(&txn->jobs); + ctx = other_job->aio_context; + if (!job_is_completed(other_job)) { + assert(job_is_cancelled(other_job)); + job_finish_sync(other_job, NULL, NULL); + } + job_finalize_single(other_job); + aio_context_release(ctx); + } + + job_txn_unref(txn); +} + +static int job_prepare(Job *job) +{ + if (job->ret == 0 && job->driver->prepare) { + job->ret = job->driver->prepare(job); + } + return job->ret; +} + +static int job_needs_finalize(Job *job) +{ + return !job->auto_finalize; +} + +static void job_do_finalize(Job *job) +{ + int rc; + assert(job && job->txn); + + /* prepare the transaction to complete */ + rc = job_txn_apply(job->txn, job_prepare, true); + if (rc) { + job_completed_txn_abort(job); + } else { + job_txn_apply(job->txn, job_finalize_single, true); + } +} + +void job_finalize(Job *job, Error **errp) +{ + assert(job && job->id); + if (job_apply_verb(job, JOB_VERB_FINALIZE, errp)) { + return; + } + job_do_finalize(job); +} + +static int job_transition_to_pending(Job *job) +{ + job_state_transition(job, JOB_STATUS_PENDING); + if (!job->auto_finalize) { + job_event_pending(job); + } + return 0; +} + +void job_completed_txn_success(Job *job) +{ + JobTxn *txn = job->txn; + Job *other_job; + + job_state_transition(job, JOB_STATUS_WAITING); + + /* + * Successful completion, see if there are other running jobs in this + * txn. + */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + if (!job_is_completed(other_job)) { + return; + } + assert(other_job->ret == 0); + } + + job_txn_apply(txn, job_transition_to_pending, false); + + /* If no jobs need manual finalization, automatically do so */ + if (job_txn_apply(txn, job_needs_finalize, false) == 0) { + job_do_finalize(job); + } +} + void job_complete(Job *job, Error **errp) { /* Should not be reachable via external interface for internal jobs */ diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index ec5d592b68..6ee31d59ad 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -125,7 +125,7 @@ static void test_single_job(int expected) JobTxn *txn; int result = -EINPROGRESS; - txn = block_job_txn_new(); + txn = job_txn_new(); job = test_block_job_start(1, true, expected, &result, txn); job_start(&job->job); @@ -138,7 +138,7 @@ static void test_single_job(int expected) } g_assert_cmpint(result, ==, expected); - block_job_txn_unref(txn); + job_txn_unref(txn); } static void test_single_job_success(void) @@ -164,7 +164,7 @@ static void test_pair_jobs(int expected1, int expected2) int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; - txn = block_job_txn_new(); + txn = job_txn_new(); job1 = test_block_job_start(1, true, expected1, &result1, txn); job2 = test_block_job_start(2, true, expected2, &result2, txn); job_start(&job1->job); @@ -173,7 +173,7 @@ static void test_pair_jobs(int expected1, int expected2) /* Release our reference now to trigger as many nice * use-after-free bugs as possible. */ - block_job_txn_unref(txn); + job_txn_unref(txn); if (expected1 == -ECANCELED) { block_job_cancel(job1, false); @@ -226,7 +226,7 @@ static void test_pair_jobs_fail_cancel_race(void) int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; - txn = block_job_txn_new(); + txn = job_txn_new(); job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); job2 = test_block_job_start(2, false, 0, &result2, txn); job_start(&job1->job); @@ -247,7 +247,7 @@ static void test_pair_jobs_fail_cancel_race(void) g_assert_cmpint(result1, ==, -ECANCELED); g_assert_cmpint(result2, ==, -ECANCELED); - block_job_txn_unref(txn); + job_txn_unref(txn); } int main(int argc, char **argv) diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index e44c608327..1e052c2e9c 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -364,7 +364,7 @@ static void test_cancel_concluded(void) } assert(job->job.status == JOB_STATUS_PENDING); - block_job_finalize(job, &error_abort); + job_finalize(&job->job, &error_abort); assert(job->job.status == JOB_STATUS_CONCLUDED); cancel_common(s); From 3d70ff53b6bf90d9eec6f97024ec9895f6799d9e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 24 Apr 2018 16:13:52 +0200 Subject: [PATCH 0706/2380] job: Move completion and cancellation to Job This moves the top-level job completion and cancellation functions from BlockJob to Job. Signed-off-by: Kevin Wolf --- block.c | 4 +- block/backup.c | 3 +- block/commit.c | 6 +-- block/mirror.c | 6 +-- block/replication.c | 4 +- block/stream.c | 2 +- block/trace-events | 3 -- blockdev.c | 8 ++-- blockjob.c | 76 -------------------------------- include/block/blockjob.h | 55 ----------------------- include/block/blockjob_int.h | 18 -------- include/qemu/job.h | 68 ++++++++++++++++++++++++----- job.c | 84 ++++++++++++++++++++++++++++++++---- qemu-img.c | 2 +- tests/test-bdrv-drain.c | 5 +-- tests/test-blockjob-txn.c | 14 +++--- tests/test-blockjob.c | 21 +++++---- trace-events | 1 + 18 files changed, 171 insertions(+), 209 deletions(-) diff --git a/block.c b/block.c index 676e57f562..d50b08c29b 100644 --- a/block.c +++ b/block.c @@ -3362,7 +3362,9 @@ static void bdrv_close(BlockDriverState *bs) void bdrv_close_all(void) { - block_job_cancel_sync_all(); + /* TODO We do want to cancel all jobs instead of just block jobs on + * shutdown, but bdrv_close_all() isn't the right place any more. */ + job_cancel_sync_all(); nbd_export_close_all(); /* Drop references from requests still in flight, such as canceled block diff --git a/block/backup.c b/block/backup.c index 6172f90c90..b13f91d8a7 100644 --- a/block/backup.c +++ b/block/backup.c @@ -319,10 +319,9 @@ typedef struct { static void backup_complete(Job *job, void *opaque) { - BlockJob *bjob = container_of(job, BlockJob, job); BackupCompleteData *data = opaque; - block_job_completed(bjob, data->ret); + job_completed(job, data->ret); g_free(data); } diff --git a/block/commit.c b/block/commit.c index 40d97a35a5..b0a847e678 100644 --- a/block/commit.c +++ b/block/commit.c @@ -112,12 +112,12 @@ static void commit_complete(Job *job, void *opaque) blk_unref(s->top); /* If there is more than one reference to the job (e.g. if called from - * job_finish_sync()), block_job_completed() won't free it and therefore - * the blockers on the intermediate nodes remain. This would cause + * job_finish_sync()), job_completed() won't free it and therefore the + * blockers on the intermediate nodes remain. This would cause * bdrv_set_backing_hd() to fail. */ block_job_remove_all_bdrv(bjob); - block_job_completed(&s->common, ret); + job_completed(job, ret); g_free(data); /* If bdrv_drop_intermediate() didn't already do that, remove the commit diff --git a/block/mirror.c b/block/mirror.c index 656237af5c..c63cf7cbc9 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -498,7 +498,7 @@ static void mirror_exit(Job *job, void *opaque) bdrv_release_dirty_bitmap(src, s->dirty_bitmap); /* Make sure that the source BDS doesn't go away before we called - * block_job_completed(). */ + * job_completed(). */ bdrv_ref(src); bdrv_ref(mirror_top_bs); bdrv_ref(target_bs); @@ -581,7 +581,7 @@ static void mirror_exit(Job *job, void *opaque) blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); - block_job_completed(&s->common, data->ret); + job_completed(job, data->ret); g_free(data); bdrv_drained_end(src); @@ -954,7 +954,7 @@ static void mirror_complete(Job *job, Error **errp) } s->should_complete = true; - block_job_enter(&s->common); + job_enter(job); } static void mirror_pause(Job *job) diff --git a/block/replication.c b/block/replication.c index ff2e2028af..826db7b304 100644 --- a/block/replication.c +++ b/block/replication.c @@ -145,7 +145,7 @@ static void replication_close(BlockDriverState *bs) replication_stop(s->rs, false, NULL); } if (s->stage == BLOCK_REPLICATION_FAILOVER) { - block_job_cancel_sync(s->active_disk->bs->job); + job_cancel_sync(&s->active_disk->bs->job->job); } if (s->mode == REPLICATION_MODE_SECONDARY) { @@ -681,7 +681,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) * disk, secondary disk in backup_job_completed(). */ if (s->secondary_disk->bs->job) { - block_job_cancel_sync(s->secondary_disk->bs->job); + job_cancel_sync(&s->secondary_disk->bs->job->job); } if (!failover) { diff --git a/block/stream.c b/block/stream.c index b996278ab3..8546c412cd 100644 --- a/block/stream.c +++ b/block/stream.c @@ -93,7 +93,7 @@ out: } g_free(s->backing_file_str); - block_job_completed(&s->common, data->ret); + job_completed(job, data->ret); g_free(data); } diff --git a/block/trace-events b/block/trace-events index 93b927908a..2d59b53fd3 100644 --- a/block/trace-events +++ b/block/trace-events @@ -4,9 +4,6 @@ bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\"" bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" -# blockjob.c -block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" - # block/block-backend.c blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" diff --git a/blockdev.c b/blockdev.c index 87a23bed6f..31319a6d5a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -150,7 +150,7 @@ void blockdev_mark_auto_del(BlockBackend *blk) aio_context_acquire(aio_context); if (bs->job) { - block_job_cancel(bs->job, false); + job_cancel(&bs->job->job, false); } aio_context_release(aio_context); @@ -1925,7 +1925,7 @@ static void drive_backup_abort(BlkActionState *common) aio_context = bdrv_get_aio_context(state->bs); aio_context_acquire(aio_context); - block_job_cancel_sync(state->job); + job_cancel_sync(&state->job->job); aio_context_release(aio_context); } @@ -2023,7 +2023,7 @@ static void blockdev_backup_abort(BlkActionState *common) aio_context = bdrv_get_aio_context(state->bs); aio_context_acquire(aio_context); - block_job_cancel_sync(state->job); + job_cancel_sync(&state->job->job); aio_context_release(aio_context); } @@ -3851,7 +3851,7 @@ void qmp_block_job_cancel(const char *device, } trace_qmp_block_job_cancel(job); - block_job_user_cancel(job, force, errp); + job_user_cancel(&job->job, force, errp); out: aio_context_release(aio_context); } diff --git a/blockjob.c b/blockjob.c index 14b21c86b7..438baa1778 100644 --- a/blockjob.c +++ b/blockjob.c @@ -255,63 +255,6 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) *jobptr = NULL; } -void block_job_cancel(BlockJob *job, bool force) -{ - if (job->job.status == JOB_STATUS_CONCLUDED) { - job_do_dismiss(&job->job); - return; - } - job_cancel_async(&job->job, force); - if (!job_started(&job->job)) { - block_job_completed(job, -ECANCELED); - } else if (job->job.deferred_to_main_loop) { - job_completed_txn_abort(&job->job); - } else { - block_job_enter(job); - } -} - -void block_job_user_cancel(BlockJob *job, bool force, Error **errp) -{ - if (job_apply_verb(&job->job, JOB_VERB_CANCEL, errp)) { - return; - } - block_job_cancel(job, force); -} - -/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be - * used with job_finish_sync() without the need for (rather nasty) function - * pointer casts there. */ -static void block_job_cancel_err(Job *job, Error **errp) -{ - BlockJob *bjob = container_of(job, BlockJob, job); - assert(is_block_job(job)); - block_job_cancel(bjob, false); -} - -int block_job_cancel_sync(BlockJob *job) -{ - return job_finish_sync(&job->job, &block_job_cancel_err, NULL); -} - -void block_job_cancel_sync_all(void) -{ - BlockJob *job; - AioContext *aio_context; - - while ((job = block_job_next(NULL))) { - aio_context = blk_get_aio_context(job->blk); - aio_context_acquire(aio_context); - block_job_cancel_sync(job); - aio_context_release(aio_context); - } -} - -int block_job_complete_sync(BlockJob *job, Error **errp) -{ - return job_finish_sync(&job->job, job_complete, errp); -} - void block_job_progress_update(BlockJob *job, uint64_t done) { job->offset += done; @@ -488,25 +431,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return job; } -void block_job_completed(BlockJob *job, int ret) -{ - assert(job && job->job.txn && !job_is_completed(&job->job)); - assert(blk_bs(job->blk)->job == job); - job->job.ret = ret; - job_update_rc(&job->job); - trace_block_job_completed(job, ret, job->job.ret); - if (job->job.ret) { - job_completed_txn_abort(&job->job); - } else { - job_completed_txn_success(&job->job); - } -} - -void block_job_enter(BlockJob *job) -{ - job_enter_cond(&job->job, NULL); -} - void block_job_yield(BlockJob *job) { assert(job->job.busy); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 09e6bb42bf..e9ed7b8b78 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -140,15 +140,6 @@ void block_job_remove_all_bdrv(BlockJob *job); */ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); -/** - * block_job_cancel: - * @job: The job to be canceled. - * @force: Quit a job without waiting for data to be in sync. - * - * Asynchronously cancel the specified job. - */ -void block_job_cancel(BlockJob *job, bool force); - /** * block_job_dismiss: * @job: The job to be dismissed. @@ -185,52 +176,6 @@ void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); */ BlockJobInfo *block_job_query(BlockJob *job, Error **errp); -/** - * block_job_user_cancel: - * @job: The job to be cancelled. - * @force: Quit a job without waiting for data to be in sync. - * - * Cancels the specified job, but may refuse to do so if the - * operation isn't currently meaningful. - */ -void block_job_user_cancel(BlockJob *job, bool force, Error **errp); - -/** - * block_job_cancel_sync: - * @job: The job to be canceled. - * - * Synchronously cancel the job. The completion callback is called - * before the function returns. The job may actually complete - * instead of canceling itself; the circumstances under which this - * happens depend on the kind of job that is active. - * - * Returns the return value from the job if the job actually completed - * during the call, or -ECANCELED if it was canceled. - */ -int block_job_cancel_sync(BlockJob *job); - -/** - * block_job_cancel_sync_all: - * - * Synchronously cancels all jobs using block_job_cancel_sync(). - */ -void block_job_cancel_sync_all(void); - -/** - * block_job_complete_sync: - * @job: The job to be completed. - * @errp: Error object which may be set by block_job_complete(); this is not - * necessarily set on every error, the job return value has to be - * checked as well. - * - * Synchronously complete the job. The completion callback is called before the - * function returns, unless it is NULL (which is permissible when using this - * function). - * - * Returns the return value from the job. - */ -int block_job_complete_sync(BlockJob *job, Error **errp); - /** * block_job_iostatus_reset: * @job: The job whose I/O status should be reset. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 29a28020ac..7df07b20cf 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -123,24 +123,6 @@ void block_job_yield(BlockJob *job); */ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); -/** - * block_job_completed: - * @job: The job being completed. - * @ret: The status code. - * - * Call the completion function that was registered at creation time, and - * free @job. - */ -void block_job_completed(BlockJob *job, int ret); - -/** - * block_job_enter: - * @job: The job to enter. - * - * Continue the specified job by entering the coroutine. - */ -void block_job_enter(BlockJob *job); - /** * block_job_event_ready: * @job: The job which is now ready to be completed. diff --git a/include/qemu/job.h b/include/qemu/job.h index 39d74abdec..bbe1b0cd1a 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -74,8 +74,8 @@ typedef struct Job { /** * Set to false by the job while the coroutine has yielded and may be - * re-entered by block_job_enter(). There may still be I/O or event loop - * activity pending. Accessed under block_job_mutex (in blockjob.c). + * re-entered by job_enter(). There may still be I/O or event loop activity + * pending. Accessed under block_job_mutex (in blockjob.c). */ bool busy; @@ -114,7 +114,7 @@ typedef struct Job { /** True if this job should automatically dismiss itself */ bool auto_dismiss; - /** ret code passed to block_job_completed. */ + /** ret code passed to job_completed. */ int ret; /** The completion function that will be called when the job completes. */ @@ -266,8 +266,8 @@ void job_txn_unref(JobTxn *txn); * @job: Job to add to the transaction * * Add @job to the transaction. The @job must not already be in a transaction. - * The caller must call either job_txn_unref() or block_job_completed() to - * release the reference that is automatically grabbed here. + * The caller must call either job_txn_unref() or job_completed() to release + * the reference that is automatically grabbed here. * * If @txn is NULL, the function does nothing. */ @@ -416,8 +416,59 @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp); /** The @job could not be started, free it. */ void job_early_fail(Job *job); +/** + * @job: The job being completed. + * @ret: The status code. + * + * Marks @job as completed. If @ret is non-zero, the job transaction it is part + * of is aborted. If @ret is zero, the job moves into the WAITING state. If it + * is the last job to complete in its transaction, all jobs in the transaction + * move from WAITING to PENDING. + */ +void job_completed(Job *job, int ret); + /** Asynchronously complete the specified @job. */ -void job_complete(Job *job, Error **errp);; +void job_complete(Job *job, Error **errp); + +/** + * Asynchronously cancel the specified @job. If @force is true, the job should + * be cancelled immediately without waiting for a consistent state. + */ +void job_cancel(Job *job, bool force); + +/** + * Cancels the specified job like job_cancel(), but may refuse to do so if the + * operation isn't meaningful in the current state of the job. + */ +void job_user_cancel(Job *job, bool force, Error **errp); + +/** + * Synchronously cancel the @job. The completion callback is called + * before the function returns. The job may actually complete + * instead of canceling itself; the circumstances under which this + * happens depend on the kind of job that is active. + * + * Returns the return value from the job if the job actually completed + * during the call, or -ECANCELED if it was canceled. + */ +int job_cancel_sync(Job *job); + +/** Synchronously cancels all jobs using job_cancel_sync(). */ +void job_cancel_sync_all(void); + +/** + * @job: The job to be completed. + * @errp: Error object which may be set by job_complete(); this is not + * necessarily set on every error, the job return value has to be + * checked as well. + * + * Synchronously complete the job. The completion callback is called before the + * function returns, unless it is NULL (which is permissible when using this + * function). + * + * Returns the return value from the job. + */ +int job_complete_sync(Job *job, Error **errp); /** * For a @job that has finished its work and is pending awaiting explicit @@ -459,11 +510,6 @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) void job_state_transition(Job *job, JobStatus s1); void coroutine_fn job_do_yield(Job *job, uint64_t ns); bool job_should_pause(Job *job); -bool job_started(Job *job); void job_do_dismiss(Job *job); -void job_update_rc(Job *job); -void job_cancel_async(Job *job, bool force); -void job_completed_txn_abort(Job *job); -void job_completed_txn_success(Job *job); #endif diff --git a/job.c b/job.c index 4f6fd73cd7..2e453f60bc 100644 --- a/job.c +++ b/job.c @@ -221,7 +221,7 @@ bool job_is_completed(Job *job) return false; } -bool job_started(Job *job) +static bool job_started(Job *job) { return job->co; } @@ -391,10 +391,10 @@ void job_enter(Job *job) } /* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. - * Reentering the job coroutine with block_job_enter() before the timer has - * expired is allowed and cancels the timer. + * Reentering the job coroutine with job_enter() before the timer has expired + * is allowed and cancels the timer. * - * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be + * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be * called explicitly. */ void coroutine_fn job_do_yield(Job *job, uint64_t ns) { @@ -579,7 +579,7 @@ static void job_conclude(Job *job) } } -void job_update_rc(Job *job) +static void job_update_rc(Job *job) { if (!job->ret && job_is_cancelled(job)) { job->ret = -ECANCELED; @@ -644,7 +644,7 @@ static int job_finalize_single(Job *job) return 0; } -void job_cancel_async(Job *job, bool force) +static void job_cancel_async(Job *job, bool force) { if (job->user_paused) { /* Do not call job_enter here, the caller will handle it. */ @@ -660,7 +660,7 @@ void job_cancel_async(Job *job, bool force) job->force_cancel |= force; } -void job_completed_txn_abort(Job *job) +static void job_completed_txn_abort(Job *job) { AioContext *ctx; JobTxn *txn = job->txn; @@ -748,7 +748,7 @@ static int job_transition_to_pending(Job *job) return 0; } -void job_completed_txn_success(Job *job) +static void job_completed_txn_success(Job *job) { JobTxn *txn = job->txn; Job *other_job; @@ -774,6 +774,74 @@ void job_completed_txn_success(Job *job) } } +void job_completed(Job *job, int ret) +{ + assert(job && job->txn && !job_is_completed(job)); + job->ret = ret; + job_update_rc(job); + trace_job_completed(job, ret, job->ret); + if (job->ret) { + job_completed_txn_abort(job); + } else { + job_completed_txn_success(job); + } +} + +void job_cancel(Job *job, bool force) +{ + if (job->status == JOB_STATUS_CONCLUDED) { + job_do_dismiss(job); + return; + } + job_cancel_async(job, force); + if (!job_started(job)) { + job_completed(job, -ECANCELED); + } else if (job->deferred_to_main_loop) { + job_completed_txn_abort(job); + } else { + job_enter(job); + } +} + +void job_user_cancel(Job *job, bool force, Error **errp) +{ + if (job_apply_verb(job, JOB_VERB_CANCEL, errp)) { + return; + } + job_cancel(job, force); +} + +/* A wrapper around job_cancel() taking an Error ** parameter so it may be + * used with job_finish_sync() without the need for (rather nasty) function + * pointer casts there. */ +static void job_cancel_err(Job *job, Error **errp) +{ + job_cancel(job, false); +} + +int job_cancel_sync(Job *job) +{ + return job_finish_sync(job, &job_cancel_err, NULL); +} + +void job_cancel_sync_all(void) +{ + Job *job; + AioContext *aio_context; + + while ((job = job_next(NULL))) { + aio_context = job->aio_context; + aio_context_acquire(aio_context); + job_cancel_sync(job); + aio_context_release(aio_context); + } +} + +int job_complete_sync(Job *job, Error **errp) +{ + return job_finish_sync(job, job_complete, errp); +} + void job_complete(Job *job, Error **errp) { /* Should not be reachable via external interface for internal jobs */ diff --git a/qemu-img.c b/qemu-img.c index 7419ec7a1a..efa7b755d4 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -869,7 +869,7 @@ static void run_block_job(BlockJob *job, Error **errp) } while (!job->ready && !job_is_completed(&job->job)); if (!job_is_completed(&job->job)) { - ret = block_job_complete_sync(job, errp); + ret = job_complete_sync(&job->job, errp); } else { ret = job->job.ret; } diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index b428aaca68..3600ffd3fb 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -498,8 +498,7 @@ typedef struct TestBlockJob { static void test_job_completed(Job *job, void *opaque) { - BlockJob *bjob = container_of(job, BlockJob, job); - block_job_completed(bjob, 0); + job_completed(job, 0); } static void coroutine_fn test_job_start(void *opaque) @@ -593,7 +592,7 @@ static void test_blockjob_common(enum drain_type drain_type) g_assert_false(job->job.paused); g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ - ret = block_job_complete_sync(job, &error_abort); + ret = job_complete_sync(&job->job, &error_abort); g_assert_cmpint(ret, ==, 0); blk_unref(blk_src); diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 6ee31d59ad..34ee179574 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -34,7 +34,7 @@ static void test_block_job_complete(Job *job, void *opaque) rc = -ECANCELED; } - block_job_completed(bjob, rc); + job_completed(job, rc); bdrv_unref(bs); } @@ -130,7 +130,7 @@ static void test_single_job(int expected) job_start(&job->job); if (expected == -ECANCELED) { - block_job_cancel(job, false); + job_cancel(&job->job, false); } while (result == -EINPROGRESS) { @@ -176,10 +176,10 @@ static void test_pair_jobs(int expected1, int expected2) job_txn_unref(txn); if (expected1 == -ECANCELED) { - block_job_cancel(job1, false); + job_cancel(&job1->job, false); } if (expected2 == -ECANCELED) { - block_job_cancel(job2, false); + job_cancel(&job2->job, false); } while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { @@ -232,13 +232,13 @@ static void test_pair_jobs_fail_cancel_race(void) job_start(&job1->job); job_start(&job2->job); - block_job_cancel(job1, false); + job_cancel(&job1->job, false); /* Now make job2 finish before the main loop kicks jobs. This simulates * the race between a pending kick and another job completing. */ - block_job_enter(job2); - block_job_enter(job2); + job_enter(&job2->job); + job_enter(&job2->job); while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { aio_poll(qemu_get_aio_context(), true); diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 1e052c2e9c..46a78739aa 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -165,10 +165,9 @@ typedef struct CancelJob { static void cancel_job_completed(Job *job, void *opaque) { - BlockJob *bjob = container_of(job, BlockJob, job); CancelJob *s = opaque; s->completed = true; - block_job_completed(bjob, 0); + job_completed(job, 0); } static void cancel_job_complete(Job *job, Error **errp) @@ -232,7 +231,7 @@ static void cancel_common(CancelJob *s) BlockBackend *blk = s->blk; JobStatus sts = job->job.status; - block_job_cancel_sync(job); + job_cancel_sync(&job->job); if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { BlockJob *dummy = job; block_job_dismiss(&dummy, &error_abort); @@ -275,7 +274,7 @@ static void test_cancel_paused(void) assert(job->job.status == JOB_STATUS_RUNNING); job_user_pause(&job->job, &error_abort); - block_job_enter(job); + job_enter(&job->job); assert(job->job.status == JOB_STATUS_PAUSED); cancel_common(s); @@ -292,7 +291,7 @@ static void test_cancel_ready(void) assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); + job_enter(&job->job); assert(job->job.status == JOB_STATUS_READY); cancel_common(s); @@ -309,11 +308,11 @@ static void test_cancel_standby(void) assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); + job_enter(&job->job); assert(job->job.status == JOB_STATUS_READY); job_user_pause(&job->job, &error_abort); - block_job_enter(job); + job_enter(&job->job); assert(job->job.status == JOB_STATUS_STANDBY); cancel_common(s); @@ -330,11 +329,11 @@ static void test_cancel_pending(void) assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); + job_enter(&job->job); assert(job->job.status == JOB_STATUS_READY); job_complete(&job->job, &error_abort); - block_job_enter(job); + job_enter(&job->job); while (!s->completed) { aio_poll(qemu_get_aio_context(), true); } @@ -354,11 +353,11 @@ static void test_cancel_concluded(void) assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); + job_enter(&job->job); assert(job->job.status == JOB_STATUS_READY); job_complete(&job->job, &error_abort); - block_job_enter(job); + job_enter(&job->job); while (!s->completed) { aio_poll(qemu_get_aio_context(), true); } diff --git a/trace-events b/trace-events index 2507e1327d..ef7579a285 100644 --- a/trace-events +++ b/trace-events @@ -107,6 +107,7 @@ gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packe # job.c job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" +job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" ### Guest events, keep at bottom From b3b5299d58bce4366c647af40374e6b063f371eb Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 16 May 2018 13:46:37 +0200 Subject: [PATCH 0707/2380] block: Cancel job in bdrv_close_all() callers Now that we cancel all jobs and not only block jobs on shutdown, doing that in bdrv_close_all() isn't really appropriate any more. Move the job_cancel_sync_all() call to the callers, and only assert that there are no job running in bdrv_close_all(). Signed-off-by: Kevin Wolf --- block.c | 4 +--- qemu-nbd.c | 8 +++++++- vl.c | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index d50b08c29b..501b64c819 100644 --- a/block.c +++ b/block.c @@ -3362,9 +3362,7 @@ static void bdrv_close(BlockDriverState *bs) void bdrv_close_all(void) { - /* TODO We do want to cancel all jobs instead of just block jobs on - * shutdown, but bdrv_close_all() isn't the right place any more. */ - job_cancel_sync_all(); + assert(job_next(NULL) == NULL); nbd_export_close_all(); /* Drop references from requests still in flight, such as canceled block diff --git a/qemu-nbd.c b/qemu-nbd.c index 0af0560ad1..51b9d38c72 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -482,6 +482,12 @@ static const char *socket_activation_validate_opts(const char *device, return NULL; } +static void qemu_nbd_shutdown(void) +{ + job_cancel_sync_all(); + bdrv_close_all(); +} + int main(int argc, char **argv) { BlockBackend *blk; @@ -928,7 +934,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } bdrv_init(); - atexit(bdrv_close_all); + atexit(qemu_nbd_shutdown); srcpath = argv[optind]; if (imageOpts) { diff --git a/vl.c b/vl.c index d5836c65ae..038d7f8042 100644 --- a/vl.c +++ b/vl.c @@ -4683,6 +4683,7 @@ int main(int argc, char **argv, char **envp) /* No more vcpu or device emulation activity beyond this point */ vm_shutdown(); + job_cancel_sync_all(); bdrv_close_all(); res_free(); From 198c49cc8d81e8eb0df3749d395599895c3a3a76 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 24 Apr 2018 16:55:04 +0200 Subject: [PATCH 0708/2380] job: Add job_yield() This moves block_job_yield() to the Job layer. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/backup.c | 2 +- block/mirror.c | 2 +- blockjob.c | 16 ---------------- include/block/blockjob_int.h | 8 -------- include/qemu/job.h | 9 +++++++-- job.c | 20 ++++++++++++++++++-- tests/test-blockjob-txn.c | 2 +- 7 files changed, 28 insertions(+), 31 deletions(-) diff --git a/block/backup.c b/block/backup.c index b13f91d8a7..6f4f3df229 100644 --- a/block/backup.c +++ b/block/backup.c @@ -444,7 +444,7 @@ static void coroutine_fn backup_run(void *opaque) while (!job_is_cancelled(&job->common.job)) { /* Yield until the job is cancelled. We just let our before_write * notify callback service CoW requests. */ - block_job_yield(&job->common); + job_yield(&job->common.job); } } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { ret = backup_run_incremental(job); diff --git a/block/mirror.c b/block/mirror.c index c63cf7cbc9..687f955c22 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -731,7 +731,7 @@ static void coroutine_fn mirror_run(void *opaque) block_job_event_ready(&s->common); s->synced = true; while (!job_is_cancelled(&s->common.job) && !s->should_complete) { - block_job_yield(&s->common); + job_yield(&s->common.job); } s->common.job.cancelled = false; goto immediate_exit; diff --git a/blockjob.c b/blockjob.c index 438baa1778..f146fe0cbd 100644 --- a/blockjob.c +++ b/blockjob.c @@ -431,22 +431,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return job; } -void block_job_yield(BlockJob *job) -{ - assert(job->job.busy); - - /* Check cancellation *before* setting busy = false, too! */ - if (job_is_cancelled(&job->job)) { - return; - } - - if (!job_should_pause(&job->job)) { - job_do_yield(&job->job, -1); - } - - job_pause_point(&job->job); -} - void block_job_iostatus_reset(BlockJob *job) { if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 7df07b20cf..806ac64d87 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -107,14 +107,6 @@ void block_job_user_resume(Job *job); */ void block_job_drain(Job *job); -/** - * block_job_yield: - * @job: The job that calls the function. - * - * Yield the block job coroutine. - */ -void block_job_yield(BlockJob *job); - /** * block_job_ratelimit_get_delay: * diff --git a/include/qemu/job.h b/include/qemu/job.h index bbe1b0cd1a..94900ec008 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -337,6 +337,13 @@ void job_enter(Job *job); */ void coroutine_fn job_pause_point(Job *job); +/** + * @job: The job that calls the function. + * + * Yield the job coroutine. + */ +void job_yield(Job *job); + /** * @job: The job that calls the function. * @ns: How many nanoseconds to stop for. @@ -508,8 +515,6 @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) /* TODO To be removed from the public interface */ void job_state_transition(Job *job, JobStatus s1); -void coroutine_fn job_do_yield(Job *job, uint64_t ns); -bool job_should_pause(Job *job); void job_do_dismiss(Job *job); #endif diff --git a/job.c b/job.c index 2e453f60bc..eede6802ae 100644 --- a/job.c +++ b/job.c @@ -226,7 +226,7 @@ static bool job_started(Job *job) return job->co; } -bool job_should_pause(Job *job) +static bool job_should_pause(Job *job) { return job->pause_count > 0; } @@ -396,7 +396,7 @@ void job_enter(Job *job) * * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be * called explicitly. */ -void coroutine_fn job_do_yield(Job *job, uint64_t ns) +static void coroutine_fn job_do_yield(Job *job, uint64_t ns) { job_lock(); if (ns != -1) { @@ -441,6 +441,22 @@ void coroutine_fn job_pause_point(Job *job) } } +void job_yield(Job *job) +{ + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (job_is_cancelled(job)) { + return; + } + + if (!job_should_pause(job)) { + job_do_yield(job, -1); + } + + job_pause_point(job); +} + void coroutine_fn job_sleep_ns(Job *job, int64_t ns) { assert(job->busy); diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 34ee179574..fce836639a 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -47,7 +47,7 @@ static void coroutine_fn test_block_job_run(void *opaque) if (s->use_timer) { job_sleep_ns(&job->job, 0); } else { - block_job_yield(job); + job_yield(&job->job); } if (job_is_cancelled(&job->job)) { From 5f9a6a08e8f65e01746d2485fc65a3a78e74865f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 24 Apr 2018 17:10:12 +0200 Subject: [PATCH 0709/2380] job: Add job_dismiss() This moves block_job_dismiss() to the Job layer. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- blockdev.c | 10 ++++++---- blockjob.c | 13 ------------- include/block/blockjob.h | 9 --------- include/qemu/job.h | 7 ++++++- job.c | 15 ++++++++++++++- tests/test-blockjob.c | 4 ++-- 6 files changed, 28 insertions(+), 30 deletions(-) diff --git a/blockdev.c b/blockdev.c index 31319a6d5a..8de95be8f4 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3915,14 +3915,16 @@ void qmp_block_job_finalize(const char *id, Error **errp) void qmp_block_job_dismiss(const char *id, Error **errp) { AioContext *aio_context; - BlockJob *job = find_block_job(id, &aio_context, errp); + BlockJob *bjob = find_block_job(id, &aio_context, errp); + Job *job; - if (!job) { + if (!bjob) { return; } - trace_qmp_block_job_dismiss(job); - block_job_dismiss(&job, errp); + trace_qmp_block_job_dismiss(bjob); + job = &bjob->job; + job_dismiss(&job, errp); aio_context_release(aio_context); } diff --git a/blockjob.c b/blockjob.c index f146fe0cbd..3ca009bea5 100644 --- a/blockjob.c +++ b/blockjob.c @@ -242,19 +242,6 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) return ratelimit_calculate_delay(&job->limit, n); } -void block_job_dismiss(BlockJob **jobptr, Error **errp) -{ - BlockJob *job = *jobptr; - /* similarly to _complete, this is QMP-interface only. */ - assert(job->job.id); - if (job_apply_verb(&job->job, JOB_VERB_DISMISS, errp)) { - return; - } - - job_do_dismiss(&job->job); - *jobptr = NULL; -} - void block_job_progress_update(BlockJob *job, uint64_t done) { job->offset += done; diff --git a/include/block/blockjob.h b/include/block/blockjob.h index e9ed7b8b78..5a81afc6c3 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -140,15 +140,6 @@ void block_job_remove_all_bdrv(BlockJob *job); */ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); -/** - * block_job_dismiss: - * @job: The job to be dismissed. - * @errp: Error object. - * - * Remove a concluded job from the query list. - */ -void block_job_dismiss(BlockJob **job, Error **errp); - /** * block_job_progress_update: * @job: The job that has made progress diff --git a/include/qemu/job.h b/include/qemu/job.h index 94900ec008..1e8050c6fb 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -487,6 +487,12 @@ int job_complete_sync(Job *job, Error **errp); */ void job_finalize(Job *job, Error **errp); +/** + * Remove the concluded @job from the query list and resets the passed pointer + * to %NULL. Returns an error if the job is not actually concluded. + */ +void job_dismiss(Job **job, Error **errp); + typedef void JobDeferToMainLoopFn(Job *job, void *opaque); /** @@ -515,6 +521,5 @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) /* TODO To be removed from the public interface */ void job_state_transition(Job *job, JobStatus s1); -void job_do_dismiss(Job *job); #endif diff --git a/job.c b/job.c index eede6802ae..7cd3602782 100644 --- a/job.c +++ b/job.c @@ -568,7 +568,7 @@ void job_user_resume(Job *job, Error **errp) job_resume(job); } -void job_do_dismiss(Job *job) +static void job_do_dismiss(Job *job) { assert(job); job->busy = false; @@ -581,6 +581,19 @@ void job_do_dismiss(Job *job) job_unref(job); } +void job_dismiss(Job **jobptr, Error **errp) +{ + Job *job = *jobptr; + /* similarly to _complete, this is QMP-interface only. */ + assert(job->id); + if (job_apply_verb(job, JOB_VERB_DISMISS, errp)) { + return; + } + + job_do_dismiss(job); + *jobptr = NULL; +} + void job_early_fail(Job *job) { assert(job->status == JOB_STATUS_CREATED); diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 46a78739aa..7131cabb16 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -233,8 +233,8 @@ static void cancel_common(CancelJob *s) job_cancel_sync(&job->job); if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { - BlockJob *dummy = job; - block_job_dismiss(&dummy, &error_abort); + Job *dummy = &job->job; + job_dismiss(&dummy, &error_abort); } assert(job->job.status == JOB_STATUS_NULL); job_unref(&job->job); From df956ae2014340bf7de0190edb1d09be55d9eadf Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 25 Apr 2018 15:09:58 +0200 Subject: [PATCH 0710/2380] job: Add job_is_ready() Instead of having a 'bool ready' in BlockJob, add a function that derives its value from the job status. At the same time, this fixes the behaviour to match what the QAPI documentation promises for query-block-job: 'true if the job may be completed'. When the ready flag was introduced in commit ef6dbf1e46e, the flag never had to be reset to match the description because after being ready, the jobs would immediately complete and disappear. Job transactions and manual job finalisation were introduced only later. With these changes, jobs may stay around even after having completed (and they are not ready to be completed a second time), however their patches forgot to reset the ready flag. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- blockjob.c | 3 +-- include/block/blockjob.h | 5 ----- include/qemu/job.h | 3 +++ job.c | 22 ++++++++++++++++++++++ qemu-img.c | 2 +- tests/test-blockjob.c | 2 +- 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/blockjob.c b/blockjob.c index 3ca009bea5..38f18e9ba3 100644 --- a/blockjob.c +++ b/blockjob.c @@ -269,7 +269,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info->offset = job->offset; info->speed = job->speed; info->io_status = job->iostatus; - info->ready = job->ready; + info->ready = job_is_ready(&job->job), info->status = job->job.status; info->auto_finalize = job->job.auto_finalize; info->auto_dismiss = job->job.auto_dismiss; @@ -436,7 +436,6 @@ void block_job_user_resume(Job *job) void block_job_event_ready(BlockJob *job) { job_state_transition(&job->job, JOB_STATUS_READY); - job->ready = true; if (block_job_is_internal(job)) { return; diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 5a81afc6c3..8e1e1ee0de 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -49,11 +49,6 @@ typedef struct BlockJob { /** The block device on which the job is operating. */ BlockBackend *blk; - /** - * Set to true when the job is ready to be completed. - */ - bool ready; - /** Status that is published by the query-block-jobs QMP API */ BlockDeviceIoStatus iostatus; diff --git a/include/qemu/job.h b/include/qemu/job.h index 1e8050c6fb..487f9d9a32 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -367,6 +367,9 @@ bool job_is_cancelled(Job *job); /** Returns whether the job is in a completed state. */ bool job_is_completed(Job *job); +/** Returns whether the job is ready to be completed. */ +bool job_is_ready(Job *job); + /** * Request @job to pause at the next pause point. Must be paired with * job_resume(). If the job is supposed to be resumed by user action, call diff --git a/job.c b/job.c index 7cd3602782..aa4c746c8a 100644 --- a/job.c +++ b/job.c @@ -199,6 +199,28 @@ bool job_is_cancelled(Job *job) return job->cancelled; } +bool job_is_ready(Job *job) +{ + switch (job->status) { + case JOB_STATUS_UNDEFINED: + case JOB_STATUS_CREATED: + case JOB_STATUS_RUNNING: + case JOB_STATUS_PAUSED: + case JOB_STATUS_WAITING: + case JOB_STATUS_PENDING: + case JOB_STATUS_ABORTING: + case JOB_STATUS_CONCLUDED: + case JOB_STATUS_NULL: + return false; + case JOB_STATUS_READY: + case JOB_STATUS_STANDBY: + return true; + default: + g_assert_not_reached(); + } + return false; +} + bool job_is_completed(Job *job) { switch (job->status) { diff --git a/qemu-img.c b/qemu-img.c index efa7b755d4..700e4e187b 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -866,7 +866,7 @@ static void run_block_job(BlockJob *job, Error **errp) aio_poll(aio_context, true); qemu_progress_print(job->len ? ((float)job->offset / job->len * 100.f) : 0.0f, 0); - } while (!job->ready && !job_is_completed(&job->job)); + } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); if (!job_is_completed(&job->job)) { ret = job_complete_sync(&job->job, errp); diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 7131cabb16..8180d03a5f 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -185,7 +185,7 @@ static void coroutine_fn cancel_job_start(void *opaque) goto defer; } - if (!s->common.ready && s->should_converge) { + if (!job_is_ready(&s->common.job) && s->should_converge) { block_job_event_ready(&s->common); } From 2e1795b58131427719c7cd11f8b9b6984b3f24f8 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 25 Apr 2018 14:56:09 +0200 Subject: [PATCH 0711/2380] job: Add job_transition_to_ready() The transition to the READY state was still performed in the BlockJob layer, in the same function that sent the BLOCK_JOB_READY QMP event. This patch brings the state transition to the Job layer and implements the QMP event using a notifier called from the Job layer, like we already do for other events related to state transitions. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/mirror.c | 6 +++--- blockjob.c | 33 ++++++++++++++++++--------------- include/block/blockjob.h | 3 +++ include/block/blockjob_int.h | 8 -------- include/qemu/job.h | 9 ++++++--- job.c | 16 +++++++++++++--- tests/test-bdrv-drain.c | 2 +- tests/test-blockjob.c | 2 +- 8 files changed, 45 insertions(+), 34 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 687f955c22..bdc1b5b7b9 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -727,8 +727,8 @@ static void coroutine_fn mirror_run(void *opaque) } if (s->bdev_length == 0) { - /* Report BLOCK_JOB_READY and wait for complete. */ - block_job_event_ready(&s->common); + /* Transition to the READY state and wait for complete. */ + job_transition_to_ready(&s->common.job); s->synced = true; while (!job_is_cancelled(&s->common.job) && !s->should_complete) { job_yield(&s->common.job); @@ -824,7 +824,7 @@ static void coroutine_fn mirror_run(void *opaque) * report completion. This way, block-job-cancel will leave * the target in a consistent state. */ - block_job_event_ready(&s->common); + job_transition_to_ready(&s->common.job); s->synced = true; } diff --git a/blockjob.c b/blockjob.c index 38f18e9ba3..da11b3b763 100644 --- a/blockjob.c +++ b/blockjob.c @@ -338,6 +338,22 @@ static void block_job_event_pending(Notifier *n, void *opaque) &error_abort); } +static void block_job_event_ready(Notifier *n, void *opaque) +{ + BlockJob *job = opaque; + + if (block_job_is_internal(job)) { + return; + } + + qapi_event_send_block_job_ready(job_type(&job->job), + job->job.id, + job->len, + job->offset, + job->speed, &error_abort); +} + + /* * API for block job drivers and the block layer. These functions are * declared in blockjob_int.h. @@ -386,12 +402,14 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job->finalize_cancelled_notifier.notify = block_job_event_cancelled; job->finalize_completed_notifier.notify = block_job_event_completed; job->pending_notifier.notify = block_job_event_pending; + job->ready_notifier.notify = block_job_event_ready; notifier_list_add(&job->job.on_finalize_cancelled, &job->finalize_cancelled_notifier); notifier_list_add(&job->job.on_finalize_completed, &job->finalize_completed_notifier); notifier_list_add(&job->job.on_pending, &job->pending_notifier); + notifier_list_add(&job->job.on_ready, &job->ready_notifier); error_setg(&job->blocker, "block device is in use by block job: %s", job_type_str(&job->job)); @@ -433,21 +451,6 @@ void block_job_user_resume(Job *job) block_job_iostatus_reset(bjob); } -void block_job_event_ready(BlockJob *job) -{ - job_state_transition(&job->job, JOB_STATUS_READY); - - if (block_job_is_internal(job)) { - return; - } - - qapi_event_send_block_job_ready(job_type(&job->job), - job->job.id, - job->len, - job->offset, - job->speed, &error_abort); -} - BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, int is_read, int error) { diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 8e1e1ee0de..4fca45f6a1 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -76,6 +76,9 @@ typedef struct BlockJob { /** Called when the job transitions to PENDING */ Notifier pending_notifier; + /** Called when the job transitions to READY */ + Notifier ready_notifier; + /** BlockDriverStates that are involved in this block job */ GSList *nodes; } BlockJob; diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 806ac64d87..5cd50c6639 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -115,14 +115,6 @@ void block_job_drain(Job *job); */ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); -/** - * block_job_event_ready: - * @job: The job which is now ready to be completed. - * - * Send a BLOCK_JOB_READY event for the specified job. - */ -void block_job_event_ready(BlockJob *job); - /** * block_job_error_action: * @job: The job to signal an error for. diff --git a/include/qemu/job.h b/include/qemu/job.h index 487f9d9a32..bfc2bc5611 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -132,6 +132,9 @@ typedef struct Job { /** Notifiers called when the job transitions to PENDING */ NotifierList on_pending; + /** Notifiers called when the job transitions to READY */ + NotifierList on_ready; + /** Element of the list of jobs */ QLIST_ENTRY(Job) job_list; @@ -426,6 +429,9 @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp); /** The @job could not be started, free it. */ void job_early_fail(Job *job); +/** Moves the @job from RUNNING to READY */ +void job_transition_to_ready(Job *job); + /** * @job: The job being completed. * @ret: The status code. @@ -522,7 +528,4 @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); */ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp); -/* TODO To be removed from the public interface */ -void job_state_transition(Job *job, JobStatus s1); - #endif diff --git a/job.c b/job.c index aa4c746c8a..b5bd51b0ad 100644 --- a/job.c +++ b/job.c @@ -157,9 +157,7 @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) return rc; } - -/* TODO Make static once the whole state machine is in job.c */ -void job_state_transition(Job *job, JobStatus s1) +static void job_state_transition(Job *job, JobStatus s1) { JobStatus s0 = job->status; assert(s1 >= 0 && s1 <= JOB_STATUS__MAX); @@ -321,6 +319,7 @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, notifier_list_init(&job->on_finalize_cancelled); notifier_list_init(&job->on_finalize_completed); notifier_list_init(&job->on_pending); + notifier_list_init(&job->on_ready); job_state_transition(job, JOB_STATUS_CREATED); aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, @@ -380,6 +379,11 @@ static void job_event_pending(Job *job) notifier_list_notify(&job->on_pending, job); } +static void job_event_ready(Job *job) +{ + notifier_list_notify(&job->on_ready, job); +} + void job_enter_cond(Job *job, bool(*fn)(Job *job)) { if (!job_started(job)) { @@ -799,6 +803,12 @@ static int job_transition_to_pending(Job *job) return 0; } +void job_transition_to_ready(Job *job) +{ + job_state_transition(job, JOB_STATUS_READY); + job_event_ready(job); +} + static void job_completed_txn_success(Job *job) { JobTxn *txn = job->txn; diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 3600ffd3fb..2cba63b881 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -505,7 +505,7 @@ static void coroutine_fn test_job_start(void *opaque) { TestBlockJob *s = opaque; - block_job_event_ready(&s->common); + job_transition_to_ready(&s->common.job); while (!s->should_complete) { job_sleep_ns(&s->common.job, 100000); } diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 8180d03a5f..e408d52351 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -186,7 +186,7 @@ static void coroutine_fn cancel_job_start(void *opaque) } if (!job_is_ready(&s->common.job) && s->should_converge) { - block_job_event_ready(&s->common); + job_transition_to_ready(&s->common.job); } job_sleep_ns(&s->common.job, 100000); From 30a5c887bf4a7e00d0e0ecbb08509e8ba2902620 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 4 May 2018 12:17:20 +0200 Subject: [PATCH 0712/2380] job: Move progress fields to Job BlockJob has fields .offset and .len, which are actually misnomers today because they are no longer tied to block device sizes, but just progress counters. As such they make a lot of sense in generic Jobs. This patch moves the fields to Job and renames them to .progress_current and .progress_total to describe their function better. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/backup.c | 8 ++++---- block/commit.c | 4 ++-- block/mirror.c | 4 ++-- block/stream.c | 4 ++-- blockjob.c | 26 ++++++++------------------ include/block/blockjob.h | 25 ------------------------- include/qemu/job.h | 28 ++++++++++++++++++++++++++++ job.c | 10 ++++++++++ qemu-img.c | 8 ++++++-- 9 files changed, 62 insertions(+), 55 deletions(-) diff --git a/block/backup.c b/block/backup.c index 6f4f3df229..4e228e959b 100644 --- a/block/backup.c +++ b/block/backup.c @@ -160,7 +160,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, * offset field is an opaque progress value, it is not a disk offset. */ job->bytes_read += n; - block_job_progress_update(&job->common, n); + job_progress_update(&job->common.job, n); } out: @@ -406,8 +406,8 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); } - /* TODO block_job_progress_set_remaining() would make more sense */ - block_job_progress_update(&job->common, + /* TODO job_progress_set_remaining() would make more sense */ + job_progress_update(&job->common.job, job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size); bdrv_dirty_iter_free(dbi); @@ -425,7 +425,7 @@ static void coroutine_fn backup_run(void *opaque) qemu_co_rwlock_init(&job->flush_rwlock); nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size); - block_job_progress_set_remaining(&job->common, job->len); + job_progress_set_remaining(&job->common.job, job->len); job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { diff --git a/block/commit.c b/block/commit.c index b0a847e678..620666161b 100644 --- a/block/commit.c +++ b/block/commit.c @@ -150,7 +150,7 @@ static void coroutine_fn commit_run(void *opaque) if (len < 0) { goto out; } - block_job_progress_set_remaining(&s->common, len); + job_progress_set_remaining(&s->common.job, len); ret = base_len = blk_getlength(s->base); if (base_len < 0) { @@ -196,7 +196,7 @@ static void coroutine_fn commit_run(void *opaque) } } /* Publish progress */ - block_job_progress_update(&s->common, n); + job_progress_update(&s->common.job, n); if (copy) { delay_ns = block_job_ratelimit_get_delay(&s->common, n); diff --git a/block/mirror.c b/block/mirror.c index bdc1b5b7b9..dcb66ec3be 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -119,7 +119,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret) bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); } if (!s->initial_zeroing_ongoing) { - block_job_progress_update(&s->common, op->bytes); + job_progress_update(&s->common.job, op->bytes); } } qemu_iovec_destroy(&op->qiov); @@ -792,7 +792,7 @@ static void coroutine_fn mirror_run(void *opaque) /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is * the number of bytes currently being processed; together those are * the current remaining operation length */ - block_job_progress_set_remaining(&s->common, s->bytes_in_flight + cnt); + job_progress_set_remaining(&s->common.job, s->bytes_in_flight + cnt); /* Note that even when no rate limit is applied we need to yield * periodically with no pending I/O so that bdrv_drain_all() returns. diff --git a/block/stream.c b/block/stream.c index 8546c412cd..a5d6e0cf8a 100644 --- a/block/stream.c +++ b/block/stream.c @@ -121,7 +121,7 @@ static void coroutine_fn stream_run(void *opaque) ret = len; goto out; } - block_job_progress_set_remaining(&s->common, len); + job_progress_set_remaining(&s->common.job, len); buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); @@ -184,7 +184,7 @@ static void coroutine_fn stream_run(void *opaque) ret = 0; /* Publish progress */ - block_job_progress_update(&s->common, n); + job_progress_update(&s->common.job, n); if (copy) { delay_ns = block_job_ratelimit_get_delay(&s->common, n); } else { diff --git a/blockjob.c b/blockjob.c index da11b3b763..5c8ff6f846 100644 --- a/blockjob.c +++ b/blockjob.c @@ -242,16 +242,6 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) return ratelimit_calculate_delay(&job->limit, n); } -void block_job_progress_update(BlockJob *job, uint64_t done) -{ - job->offset += done; -} - -void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining) -{ - job->len = job->offset + remaining; -} - BlockJobInfo *block_job_query(BlockJob *job, Error **errp) { BlockJobInfo *info; @@ -263,10 +253,10 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info = g_new0(BlockJobInfo, 1); info->type = g_strdup(job_type_str(&job->job)); info->device = g_strdup(job->job.id); - info->len = job->len; info->busy = atomic_read(&job->job.busy); info->paused = job->job.pause_count > 0; - info->offset = job->offset; + info->offset = job->job.progress_current; + info->len = job->job.progress_total; info->speed = job->speed; info->io_status = job->iostatus; info->ready = job_is_ready(&job->job), @@ -296,8 +286,8 @@ static void block_job_event_cancelled(Notifier *n, void *opaque) qapi_event_send_block_job_cancelled(job_type(&job->job), job->job.id, - job->len, - job->offset, + job->job.progress_total, + job->job.progress_current, job->speed, &error_abort); } @@ -317,8 +307,8 @@ static void block_job_event_completed(Notifier *n, void *opaque) qapi_event_send_block_job_completed(job_type(&job->job), job->job.id, - job->len, - job->offset, + job->job.progress_total, + job->job.progress_current, job->speed, !!msg, msg, @@ -348,8 +338,8 @@ static void block_job_event_ready(Notifier *n, void *opaque) qapi_event_send_block_job_ready(job_type(&job->job), job->job.id, - job->len, - job->offset, + job->job.progress_total, + job->job.progress_current, job->speed, &error_abort); } diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 4fca45f6a1..3021d11126 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -52,12 +52,6 @@ typedef struct BlockJob { /** Status that is published by the query-block-jobs QMP API */ BlockDeviceIoStatus iostatus; - /** Offset that is published by the query-block-jobs QMP API */ - int64_t offset; - - /** Length that is published by the query-block-jobs QMP API */ - int64_t len; - /** Speed that was set with @block_job_set_speed. */ int64_t speed; @@ -138,25 +132,6 @@ void block_job_remove_all_bdrv(BlockJob *job); */ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); -/** - * block_job_progress_update: - * @job: The job that has made progress - * @done: How much progress the job made - * - * Updates the progress counter of the job. - */ -void block_job_progress_update(BlockJob *job, uint64_t done); - -/** - * block_job_progress_set_remaining: - * @job: The job whose expected progress end value is set - * @remaining: Expected end value of the progress counter of the job - * - * Sets the expected end value of the progress counter of a job so that a - * completion percentage can be calculated when the progress is updated. - */ -void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); - /** * block_job_query: * @job: The job to get information about. diff --git a/include/qemu/job.h b/include/qemu/job.h index bfc2bc5611..92d1d249fe 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -114,6 +114,16 @@ typedef struct Job { /** True if this job should automatically dismiss itself */ bool auto_dismiss; + /** + * Current progress. The unit is arbitrary as long as the ratio between + * progress_current and progress_total represents the estimated percentage + * of work already done. + */ + int64_t progress_current; + + /** Estimated progress_current value at the completion of the job */ + int64_t progress_total; + /** ret code passed to job_completed. */ int ret; @@ -304,6 +314,24 @@ void job_ref(Job *job); */ void job_unref(Job *job); +/** + * @job: The job that has made progress + * @done: How much progress the job made since the last call + * + * Updates the progress counter of the job. + */ +void job_progress_update(Job *job, uint64_t done); + +/** + * @job: The job whose expected progress end value is set + * @remaining: Missing progress (on top of the current progress counter value) + * until the new expected end value is reached + * + * Sets the expected end value of the progress counter of a job so that a + * completion percentage can be calculated when the progress is updated. + */ +void job_progress_set_remaining(Job *job, uint64_t remaining); + /** To be called when a cancelled job is finalised. */ void job_event_cancelled(Job *job); diff --git a/job.c b/job.c index b5bd51b0ad..2046d2f9d3 100644 --- a/job.c +++ b/job.c @@ -364,6 +364,16 @@ void job_unref(Job *job) } } +void job_progress_update(Job *job, uint64_t done) +{ + job->progress_current += done; +} + +void job_progress_set_remaining(Job *job, uint64_t remaining) +{ + job->progress_total = job->progress_current + remaining; +} + void job_event_cancelled(Job *job) { notifier_list_notify(&job->on_finalize_cancelled, job); diff --git a/qemu-img.c b/qemu-img.c index 700e4e187b..976b437da0 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -863,9 +863,13 @@ static void run_block_job(BlockJob *job, Error **errp) aio_context_acquire(aio_context); job_ref(&job->job); do { + float progress = 0.0f; aio_poll(aio_context, true); - qemu_progress_print(job->len ? - ((float)job->offset / job->len * 100.f) : 0.0f, 0); + if (job->job.progress_total) { + progress = (float)job->job.progress_current / + job->job.progress_total * 100.f; + } + qemu_progress_print(progress, 0); } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); if (!job_is_completed(&job->job)) { From bf42508f24ee1368267b421e145fa90315b77936 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 16 May 2018 16:03:10 +0200 Subject: [PATCH 0713/2380] job: Introduce qapi/job.json This adds a separate schema file for all job-related definitions that aren't tied to the block layer. For a start, move the enums JobType, JobStatus and JobVerb. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake --- MAINTAINERS | 1 + Makefile | 9 +++++ Makefile.objs | 4 ++ qapi/block-core.json | 90 +---------------------------------------- qapi/job.json | 94 +++++++++++++++++++++++++++++++++++++++++++ qapi/qapi-schema.json | 1 + 6 files changed, 110 insertions(+), 89 deletions(-) create mode 100644 qapi/job.json diff --git a/MAINTAINERS b/MAINTAINERS index 9fba3307d9..d49c16cdd7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1375,6 +1375,7 @@ F: block/backup.c F: block/commit.c F: block/stream.c F: block/mirror.c +F: qapi/job.json T: git git://github.com/codyprime/qemu-kvm-jtc.git block Block QAPI, monitor, command line diff --git a/Makefile b/Makefile index 35554b5bef..6d588d1f71 100644 --- a/Makefile +++ b/Makefile @@ -98,6 +98,7 @@ GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c +GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c @@ -116,6 +117,7 @@ GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c +GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c @@ -133,6 +135,7 @@ GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c +GENERATED_FILES += qapi/qapi-commands-job.h qapi/qapi-commands-job.c GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c @@ -150,6 +153,7 @@ GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c +GENERATED_FILES += qapi/qapi-events-job.h qapi/qapi-events-job.c GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c @@ -582,6 +586,7 @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ $(SRC_PATH)/qapi/char.json \ $(SRC_PATH)/qapi/crypto.json \ $(SRC_PATH)/qapi/introspect.json \ + $(SRC_PATH)/qapi/job.json \ $(SRC_PATH)/qapi/migration.json \ $(SRC_PATH)/qapi/misc.json \ $(SRC_PATH)/qapi/net.json \ @@ -601,6 +606,7 @@ qapi/qapi-types-char.c qapi/qapi-types-char.h \ qapi/qapi-types-common.c qapi/qapi-types-common.h \ qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ +qapi/qapi-types-job.c qapi/qapi-types-job.h \ qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ qapi/qapi-types-misc.c qapi/qapi-types-misc.h \ qapi/qapi-types-net.c qapi/qapi-types-net.h \ @@ -619,6 +625,7 @@ qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ +qapi/qapi-visit-job.c qapi/qapi-visit-job.h \ qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \ qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ @@ -636,6 +643,7 @@ qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ +qapi/qapi-commands-job.c qapi/qapi-commands-job.h \ qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \ qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ @@ -653,6 +661,7 @@ qapi/qapi-events-char.c qapi/qapi-events-char.h \ qapi/qapi-events-common.c qapi/qapi-events-common.h \ qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ +qapi/qapi-events-job.c qapi/qapi-events-job.h \ qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ qapi/qapi-events-misc.c qapi/qapi-events-misc.h \ qapi/qapi-events-net.c qapi/qapi-events-net.h \ diff --git a/Makefile.objs b/Makefile.objs index 92b73fc272..3df8d58e49 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -10,6 +10,7 @@ util-obj-y += qapi/qapi-types-char.o util-obj-y += qapi/qapi-types-common.o util-obj-y += qapi/qapi-types-crypto.o util-obj-y += qapi/qapi-types-introspect.o +util-obj-y += qapi/qapi-types-job.o util-obj-y += qapi/qapi-types-migration.o util-obj-y += qapi/qapi-types-misc.o util-obj-y += qapi/qapi-types-net.o @@ -28,6 +29,7 @@ util-obj-y += qapi/qapi-visit-char.o util-obj-y += qapi/qapi-visit-common.o util-obj-y += qapi/qapi-visit-crypto.o util-obj-y += qapi/qapi-visit-introspect.o +util-obj-y += qapi/qapi-visit-job.o util-obj-y += qapi/qapi-visit-migration.o util-obj-y += qapi/qapi-visit-misc.o util-obj-y += qapi/qapi-visit-net.o @@ -45,6 +47,7 @@ util-obj-y += qapi/qapi-events-char.o util-obj-y += qapi/qapi-events-common.o util-obj-y += qapi/qapi-events-crypto.o util-obj-y += qapi/qapi-events-introspect.o +util-obj-y += qapi/qapi-events-job.o util-obj-y += qapi/qapi-events-migration.o util-obj-y += qapi/qapi-events-misc.o util-obj-y += qapi/qapi-events-net.o @@ -140,6 +143,7 @@ common-obj-y += qapi/qapi-commands-char.o common-obj-y += qapi/qapi-commands-common.o common-obj-y += qapi/qapi-commands-crypto.o common-obj-y += qapi/qapi-commands-introspect.o +common-obj-y += qapi/qapi-commands-job.o common-obj-y += qapi/qapi-commands-migration.o common-obj-y += qapi/qapi-commands-misc.o common-obj-y += qapi/qapi-commands-net.o diff --git a/qapi/block-core.json b/qapi/block-core.json index bb964b4319..7dfa77a05c 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -6,6 +6,7 @@ { 'include': 'common.json' } { 'include': 'crypto.json' } +{ 'include': 'job.json' } { 'include': 'sockets.json' } ## @@ -1049,95 +1050,6 @@ { 'enum': 'MirrorSyncMode', 'data': ['top', 'full', 'none', 'incremental'] } -## -# @JobType: -# -# Type of a background job. -# -# @commit: block commit job type, see "block-commit" -# -# @stream: block stream job type, see "block-stream" -# -# @mirror: drive mirror job type, see "drive-mirror" -# -# @backup: drive backup job type, see "drive-backup" -# -# Since: 1.7 -## -{ 'enum': 'JobType', - 'data': ['commit', 'stream', 'mirror', 'backup'] } - -## -# @JobVerb: -# -# Represents command verbs that can be applied to a job. -# -# @cancel: see @block-job-cancel -# -# @pause: see @block-job-pause -# -# @resume: see @block-job-resume -# -# @set-speed: see @block-job-set-speed -# -# @complete: see @block-job-complete -# -# @dismiss: see @block-job-dismiss -# -# @finalize: see @block-job-finalize -# -# Since: 2.12 -## -{ 'enum': 'JobVerb', - 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', - 'finalize' ] } - -## -# @JobStatus: -# -# Indicates the present state of a given job in its lifetime. -# -# @undefined: Erroneous, default state. Should not ever be visible. -# -# @created: The job has been created, but not yet started. -# -# @running: The job is currently running. -# -# @paused: The job is running, but paused. The pause may be requested by -# either the QMP user or by internal processes. -# -# @ready: The job is running, but is ready for the user to signal completion. -# This is used for long-running jobs like mirror that are designed to -# run indefinitely. -# -# @standby: The job is ready, but paused. This is nearly identical to @paused. -# The job may return to @ready or otherwise be canceled. -# -# @waiting: The job is waiting for other jobs in the transaction to converge -# to the waiting state. This status will likely not be visible for -# the last job in a transaction. -# -# @pending: The job has finished its work, but has finalization steps that it -# needs to make prior to completing. These changes may require -# manual intervention by the management process if manual was set -# to true. These changes may still fail. -# -# @aborting: The job is in the process of being aborted, and will finish with -# an error. The job will afterwards report that it is @concluded. -# This status may not be visible to the management process. -# -# @concluded: The job has finished all work. If manual was set to true, the job -# will remain in the query list until it is dismissed. -# -# @null: The job is in the process of being dismantled. This state should not -# ever be visible externally. -# -# Since: 2.12 -## -{ 'enum': 'JobStatus', - 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby', - 'waiting', 'pending', 'aborting', 'concluded', 'null' ] } - ## # @BlockJobInfo: # diff --git a/qapi/job.json b/qapi/job.json new file mode 100644 index 0000000000..a472c0cb29 --- /dev/null +++ b/qapi/job.json @@ -0,0 +1,94 @@ +# -*- Mode: Python -*- + +## +# == Background jobs +## + +## +# @JobType: +# +# Type of a background job. +# +# @commit: block commit job type, see "block-commit" +# +# @stream: block stream job type, see "block-stream" +# +# @mirror: drive mirror job type, see "drive-mirror" +# +# @backup: drive backup job type, see "drive-backup" +# +# Since: 1.7 +## +{ 'enum': 'JobType', + 'data': ['commit', 'stream', 'mirror', 'backup'] } + +## +# @JobStatus: +# +# Indicates the present state of a given job in its lifetime. +# +# @undefined: Erroneous, default state. Should not ever be visible. +# +# @created: The job has been created, but not yet started. +# +# @running: The job is currently running. +# +# @paused: The job is running, but paused. The pause may be requested by +# either the QMP user or by internal processes. +# +# @ready: The job is running, but is ready for the user to signal completion. +# This is used for long-running jobs like mirror that are designed to +# run indefinitely. +# +# @standby: The job is ready, but paused. This is nearly identical to @paused. +# The job may return to @ready or otherwise be canceled. +# +# @waiting: The job is waiting for other jobs in the transaction to converge +# to the waiting state. This status will likely not be visible for +# the last job in a transaction. +# +# @pending: The job has finished its work, but has finalization steps that it +# needs to make prior to completing. These changes may require +# manual intervention by the management process if manual was set +# to true. These changes may still fail. +# +# @aborting: The job is in the process of being aborted, and will finish with +# an error. The job will afterwards report that it is @concluded. +# This status may not be visible to the management process. +# +# @concluded: The job has finished all work. If manual was set to true, the job +# will remain in the query list until it is dismissed. +# +# @null: The job is in the process of being dismantled. This state should not +# ever be visible externally. +# +# Since: 2.12 +## +{ 'enum': 'JobStatus', + 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby', + 'waiting', 'pending', 'aborting', 'concluded', 'null' ] } + +## +# @JobVerb: +# +# Represents command verbs that can be applied to a job. +# +# @cancel: see @block-job-cancel +# +# @pause: see @block-job-pause +# +# @resume: see @block-job-resume +# +# @set-speed: see @block-job-set-speed +# +# @complete: see @block-job-complete +# +# @dismiss: see @block-job-dismiss +# +# @finalize: see @block-job-finalize +# +# Since: 2.12 +## +{ 'enum': 'JobVerb', + 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', + 'finalize' ] } diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 25bce78352..65b6dc2f6f 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -84,6 +84,7 @@ { 'include': 'crypto.json' } { 'include': 'block.json' } { 'include': 'char.json' } +{ 'include': 'job.json' } { 'include': 'net.json' } { 'include': 'rocker.json' } { 'include': 'tpm.json' } From 1dac83f1a10c4c66858075970e199f4e4a8d8b71 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 30 Apr 2018 19:09:46 +0200 Subject: [PATCH 0714/2380] job: Add JOB_STATUS_CHANGE QMP event This adds a QMP event that is emitted whenever a job transitions from one status to another. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake --- job.c | 10 ++ qapi/job.json | 14 +++ tests/qemu-iotests/030 | 17 +++- tests/qemu-iotests/040 | 2 + tests/qemu-iotests/041 | 17 +++- tests/qemu-iotests/094.out | 7 ++ tests/qemu-iotests/095 | 2 +- tests/qemu-iotests/095.out | 6 ++ tests/qemu-iotests/109 | 2 +- tests/qemu-iotests/109.out | 178 +++++++++++++++++++++++++++++----- tests/qemu-iotests/124 | 8 ++ tests/qemu-iotests/127.out | 7 ++ tests/qemu-iotests/141 | 13 ++- tests/qemu-iotests/141.out | 29 ++++++ tests/qemu-iotests/144 | 2 +- tests/qemu-iotests/144.out | 7 ++ tests/qemu-iotests/156 | 2 +- tests/qemu-iotests/156.out | 7 ++ tests/qemu-iotests/185 | 12 ++- tests/qemu-iotests/185.out | 10 ++ tests/qemu-iotests/191 | 4 +- tests/qemu-iotests/191.out | 132 +++++++++++++++++++++++++ tests/qemu-iotests/iotests.py | 5 + 23 files changed, 449 insertions(+), 44 deletions(-) diff --git a/job.c b/job.c index 2046d2f9d3..599a1042cf 100644 --- a/job.c +++ b/job.c @@ -30,6 +30,7 @@ #include "qemu/id.h" #include "qemu/main-loop.h" #include "trace-root.h" +#include "qapi/qapi-events-job.h" static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); @@ -157,6 +158,11 @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) return rc; } +static bool job_is_internal(Job *job) +{ + return (job->id == NULL); +} + static void job_state_transition(Job *job, JobStatus s1) { JobStatus s0 = job->status; @@ -166,6 +172,10 @@ static void job_state_transition(Job *job, JobStatus s1) JobStatus_str(s0), JobStatus_str(s1)); assert(JobSTT[s0][s1]); job->status = s1; + + if (!job_is_internal(job) && s1 != s0) { + qapi_event_send_job_status_change(job->id, job->status, &error_abort); + } } int job_apply_verb(Job *job, JobVerb verb, Error **errp) diff --git a/qapi/job.json b/qapi/job.json index a472c0cb29..9fbdd0ccd9 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -92,3 +92,17 @@ { 'enum': 'JobVerb', 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', 'finalize' ] } + +## +# @JOB_STATUS_CHANGE: +# +# Emitted when a job transitions to a different status. +# +# @id: The job identifier +# @status: The new job status +# +# Since: 2.13 +## +{ 'event': 'JOB_STATUS_CHANGE', + 'data': { 'id': 'str', + 'status': 'JobStatus' } } diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 640a6dfd10..1dbc2ddc49 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -304,8 +304,7 @@ class TestParallelOps(iotests.QMPTestCase): result = self.vm.qmp('block-stream', device='node5', base=self.imgs[3], job_id='stream-node6') self.assert_qmp(result, 'error/class', 'GenericError') - event = self.vm.get_qmp_event(wait=True) - self.assertEqual(event['event'], 'BLOCK_JOB_READY') + event = self.vm.event_wait(name='BLOCK_JOB_READY') self.assert_qmp(event, 'data/device', 'commit-drive0') self.assert_qmp(event, 'data/type', 'commit') self.assert_qmp_absent(event, 'data/error') @@ -565,6 +564,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -596,6 +597,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.image_len) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -637,6 +640,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.image_len) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -663,6 +668,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -722,6 +729,8 @@ class TestENOSPC(TestErrors): self.assert_qmp(event, 'data/offset', self.image_len) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -751,7 +760,9 @@ class TestStreamStop(iotests.QMPTestCase): time.sleep(0.1) events = self.vm.get_qmp_events(wait=False) - self.assertEqual(events, [], 'unexpected QMP event: %s' % events) + for e in events: + self.assert_qmp(e, 'event', 'JOB_STATUS_CHANGE') + self.assert_qmp(e, 'data/id', 'drive0') self.cancel_and_wait(resume=True) diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 90b5b4f2ad..1beb5e6dab 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -162,6 +162,8 @@ class TestSingleDrive(ImageCommitTestCase): elif event['event'] == 'BLOCK_JOB_CANCELLED': self.assert_qmp(event, 'data/device', 'drive0') cancelled = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') else: self.fail("Unexpected event %s" % (event['event'])) diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index a860a31e9a..e94587950c 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -445,6 +445,8 @@ new_state = "1" self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/error', 'Input/output error') completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -457,6 +459,10 @@ new_state = "1" self.assert_qmp(result, 'return', {}) event = self.vm.get_qmp_event(wait=True) + while event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') + event = self.vm.get_qmp_event(wait=True) + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') @@ -478,6 +484,10 @@ new_state = "1" self.assert_qmp(result, 'return', {}) event = self.vm.get_qmp_event(wait=True) + while event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') + event = self.vm.get_qmp_event(wait=True) + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') @@ -608,7 +618,7 @@ new_state = "1" on_target_error='ignore') self.assert_qmp(result, 'return', {}) - event = self.vm.get_qmp_event(wait=True) + event = self.vm.event_wait(name='BLOCK_JOB_ERROR') self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'write') @@ -784,7 +794,12 @@ class TestGranularity(iotests.QMPTestCase): sync='full', target=target_img, mode='absolute-paths', granularity=8192) self.assert_qmp(result, 'return', {}) + event = self.vm.get_qmp_event(wait=60.0) + while event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') + event = self.vm.get_qmp_event(wait=60.0) + # Failures will manifest as COMPLETED/ERROR. self.assert_qmp(event, 'event', 'BLOCK_JOB_READY') self.complete_and_wait(drive='drive0', wait_ready=False) diff --git a/tests/qemu-iotests/094.out b/tests/qemu-iotests/094.out index f52baffe70..665b630b08 100644 --- a/tests/qemu-iotests/094.out +++ b/tests/qemu-iotests/094.out @@ -2,10 +2,17 @@ QA output created by 094 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} *** done diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095 index 030adb22e1..72ecc22e1b 100755 --- a/tests/qemu-iotests/095 +++ b/tests/qemu-iotests/095 @@ -72,7 +72,7 @@ _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" _send_qemu_cmd $h "{ 'execute': 'block-commit', 'arguments': { 'device': 'test', - 'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED" + 'top': '"${TEST_IMG}.snp1"' } }" '"status": "null"' _cleanup_qemu diff --git a/tests/qemu-iotests/095.out b/tests/qemu-iotests/095.out index 73875cab40..8c093dfff3 100644 --- a/tests/qemu-iotests/095.out +++ b/tests/qemu-iotests/095.out @@ -11,8 +11,14 @@ virtual size: 5.0M (5242880 bytes) === Running QEMU Live Commit Test === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "test"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "test"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 104857600, "offset": 104857600, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "test"}} === Base image info after commit and resize === image: TEST_DIR/t.IMGFMT.base diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 index d70b574d88..acbd079136 100755 --- a/tests/qemu-iotests/109 +++ b/tests/qemu-iotests/109 @@ -64,7 +64,7 @@ function run_qemu() _send_qemu_cmd $QEMU_HANDLE '' "$qmp_event" if test "$qmp_event" = BLOCK_JOB_ERROR; then - _send_qemu_cmd $QEMU_HANDLE '' "BLOCK_JOB_COMPLETED" + _send_qemu_cmd $QEMU_HANDLE '' '"status": "null"' fi _send_qemu_cmd $QEMU_HANDLE '{"execute":"query-block-jobs"}' "return" _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index 8a9b93672b..ad0ee6fb48 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -6,23 +6,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -32,23 +44,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 512, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -58,23 +82,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 262144, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -84,23 +120,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -110,23 +158,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -136,23 +196,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -161,23 +233,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -186,23 +270,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -211,23 +307,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -236,23 +344,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -261,23 +381,37 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. *** done diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index 8e76e62f93..3ea4ac53f5 100755 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 @@ -151,10 +151,17 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): return self.wait_qmp_backup(kwargs['device'], error) + def ignore_job_status_change_events(self): + while True: + e = self.vm.event_wait(name="JOB_STATUS_CHANGE") + if e['data']['status'] == 'null': + break + def wait_qmp_backup(self, device, error='Input/output error'): event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED", match={'data': {'device': device}}) self.assertNotEqual(event, None) + self.ignore_job_status_change_events() try: failure = self.dictpath(event, 'data/error') @@ -172,6 +179,7 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED', match={'data': {'device': device}}) self.assertNotEqual(event, None) + self.ignore_job_status_change_events() def create_anchor_backup(self, drive=None): diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out index 543d075005..83b522d4c2 100644 --- a/tests/qemu-iotests/127.out +++ b/tests/qemu-iotests/127.out @@ -5,10 +5,17 @@ Formatting 'TEST_DIR/t.IMGFMT.overlay1', fmt=IMGFMT size=65536 backing_file=TEST wrote 42/42 bytes at offset 0 42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "mirror"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} *** done diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index 2f9d7b9bc2..4246d387a1 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -107,7 +107,7 @@ test_blockjob \ 'format': '$IMGFMT', 'sync': 'none'}}" \ 'return' \ - 'BLOCK_JOB_CANCELLED' + '"status": "null"' echo echo '=== Testing drive-mirror ===' @@ -124,7 +124,7 @@ test_blockjob \ 'format': '$IMGFMT', 'sync': 'none'}}" \ 'BLOCK_JOB_READY' \ - 'BLOCK_JOB_COMPLETED' + '"status": "null"' echo echo '=== Testing active block-commit ===' @@ -138,7 +138,7 @@ test_blockjob \ "{'execute': 'block-commit', 'arguments': {'job-id': 'job0', 'device': 'drv0'}}" \ 'BLOCK_JOB_READY' \ - 'BLOCK_JOB_COMPLETED' + '"status": "null"' echo echo '=== Testing non-active block-commit ===' @@ -157,7 +157,7 @@ test_blockjob \ 'top': '$TEST_DIR/m.$IMGFMT', 'speed': 1}}" \ 'return' \ - 'BLOCK_JOB_CANCELLED' + '"status": "null"' echo echo '=== Testing block-stream ===' @@ -170,8 +170,7 @@ echo $QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io # With some data to stream (and @speed set to 1), block-stream will not complete -# until we send the block-job-cancel command. Therefore, no event other than -# BLOCK_JOB_CANCELLED will be emitted. +# until we send the block-job-cancel command. test_blockjob \ "{'execute': 'block-stream', @@ -179,7 +178,7 @@ test_blockjob \ 'device': 'drv0', 'speed': 1}}" \ 'return' \ - 'BLOCK_JOB_CANCELLED' + '"status": "null"' _cleanup_qemu diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out index 82e763b68d..f252c86875 100644 --- a/tests/qemu-iotests/141.out +++ b/tests/qemu-iotests/141.out @@ -8,31 +8,50 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m. {"return": {}} Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing drive-mirror === {"return": {}} Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing active block-commit === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing non-active block-commit === @@ -40,10 +59,15 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t. wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing block-stream === @@ -51,9 +75,14 @@ wrote 1048576/1048576 bytes at offset 0 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} *** done diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144 index 00de3c33cf..4b915718cd 100755 --- a/tests/qemu-iotests/144 +++ b/tests/qemu-iotests/144 @@ -93,7 +93,7 @@ _send_qemu_cmd $h "{ 'execute': 'block-job-complete', 'arguments': { 'device': 'virtio0' } - }" "COMPLETED" + }" '"status": "null"' echo echo === Performing Live Snapshot 2 === diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out index 014b2817ee..55299201e4 100644 --- a/tests/qemu-iotests/144.out +++ b/tests/qemu-iotests/144.out @@ -12,10 +12,17 @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/ === Performing block-commit on active layer === +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "virtio0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} {"return": {}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "virtio0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "virtio0"}} === Performing Live Snapshot 2 === diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 index e75dc4d743..0a9a09802e 100755 --- a/tests/qemu-iotests/156 +++ b/tests/qemu-iotests/156 @@ -119,7 +119,7 @@ _send_qemu_cmd $QEMU_HANDLE \ _send_qemu_cmd $QEMU_HANDLE \ '' \ - 'BLOCK_JOB_COMPLETED' + '"status": "null"' # Remove the source images rm -f "$TEST_IMG{,.backing,.overlay}" diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out index f96a564c1d..34c057b626 100644 --- a/tests/qemu-iotests/156.out +++ b/tests/qemu-iotests/156.out @@ -12,13 +12,20 @@ wrote 131072/131072 bytes at offset 131072 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "source"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "source"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "source"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "source", "len": 131072, "offset": 131072, "speed": 0, "type": "mirror"}} wrote 65536/65536 bytes at offset 196608 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "source"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "source"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "source", "len": 196608, "offset": 196608, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "source"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "source"}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index deb42cc886..7dcfdeac60 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -116,8 +116,10 @@ _send_qemu_cmd $h \ # If we don't sleep here 'quit' command races with disk I/O sleep 0.5 +# Ignore the JOB_STATUS_CHANGE events while shutting down the VM. Depending on +# the timing, jobs may or may not transition through a paused state. _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start active commit job and exit qemu === @@ -139,7 +141,7 @@ _send_qemu_cmd $h \ sleep 0.5 _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start mirror job and exit qemu === @@ -164,7 +166,7 @@ _send_qemu_cmd $h \ sleep 0.5 _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start backup job and exit qemu === @@ -188,7 +190,7 @@ _send_qemu_cmd $h \ sleep 0.5 _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start streaming job and exit qemu === @@ -209,7 +211,7 @@ _send_qemu_cmd $h \ sleep 0.5 _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' _check_test_img diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 57eaf8d699..4e0ca0dffa 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -17,6 +17,8 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q === Start commit job and exit qemu === +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} @@ -25,6 +27,8 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q === Start active commit job and exit qemu === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} @@ -34,6 +38,8 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q {"return": {}} Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} @@ -43,6 +49,8 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l {"return": {}} Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} @@ -51,6 +59,8 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l === Start streaming job and exit qemu === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 index 77224eb151..d6860e72f7 100755 --- a/tests/qemu-iotests/191 +++ b/tests/qemu-iotests/191 @@ -81,7 +81,7 @@ _send_qemu_cmd $h \ 'device': 'top', 'base':'$TEST_IMG.base', 'top': '$TEST_IMG.mid' } }" \ - "BLOCK_JOB_COMPLETED" + '"status": "null"' _send_qemu_cmd $h "" "^}" echo @@ -129,7 +129,7 @@ _send_qemu_cmd $h \ 'device': 'top', 'base':'$TEST_IMG.base', 'top': '$TEST_IMG.mid' } }" \ - "BLOCK_JOB_COMPLETED" + '"status": "null"' _send_qemu_cmd $h "" "^}" echo diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out index 190c5f049a..31a0c7d4c4 100644 --- a/tests/qemu-iotests/191.out +++ b/tests/qemu-iotests/191.out @@ -15,10 +15,54 @@ wrote 65536/65536 bytes at offset 1048576 === Perform commit job === +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "created", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "running", + "id": "commit0" + } +} { "return": { } } +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "waiting", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "pending", + "id": "commit0" + } +} { "timestamp": { "seconds": TIMESTAMP, @@ -33,6 +77,28 @@ wrote 65536/65536 bytes at offset 1048576 "type": "commit" } } +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "concluded", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "null", + "id": "commit0" + } +} === Check that both top and top2 point to base now === @@ -355,10 +421,54 @@ wrote 65536/65536 bytes at offset 1048576 === Perform commit job === +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "created", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "running", + "id": "commit0" + } +} { "return": { } } +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "waiting", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "pending", + "id": "commit0" + } +} { "timestamp": { "seconds": TIMESTAMP, @@ -373,6 +483,28 @@ wrote 65536/65536 bytes at offset 1048576 "type": "commit" } } +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "concluded", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "null", + "id": "commit0" + } +} === Check that both top and top2 point to base now === diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index b25d48a91b..ed08e06e1f 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -464,6 +464,9 @@ class QMPTestCase(unittest.TestCase): self.assert_qmp(event, 'data/device', drive) result = event cancelled = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', drive) + self.assert_no_active_block_jobs() return result @@ -479,6 +482,8 @@ class QMPTestCase(unittest.TestCase): self.assert_qmp(event, 'data/offset', event['data']['len']) self.assert_no_active_block_jobs() return event + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', drive) def wait_ready(self, drive='drive0'): '''Wait until a block job BLOCK_JOB_READY event''' From 1a90bc8128ee7d16ce4abb131961e37084d75b16 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 3 May 2018 19:01:14 +0200 Subject: [PATCH 0715/2380] job: Add lifecycle QMP commands This adds QMP commands that control the transition between states of the job lifecycle. Signed-off-by: Kevin Wolf --- MAINTAINERS | 1 + Makefile.objs | 1 + job-qmp.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++ qapi/job.json | 99 +++++++++++++++++++++++++++++++++++++ trace-events | 9 ++++ 5 files changed, 244 insertions(+) create mode 100644 job-qmp.c diff --git a/MAINTAINERS b/MAINTAINERS index d49c16cdd7..4a0bc1e71b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1370,6 +1370,7 @@ S: Supported F: blockjob.c F: include/block/blockjob.h F: job.c +F: job-qmp.c F: include/block/job.h F: block/backup.c F: block/commit.c diff --git a/Makefile.objs b/Makefile.objs index 3df8d58e49..c6c3554203 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -97,6 +97,7 @@ io-obj-y = io/ ifeq ($(CONFIG_SOFTMMU),y) common-obj-y = blockdev.o blockdev-nbd.o block/ common-obj-y += bootdevice.o iothread.o +common-obj-y += job-qmp.o common-obj-y += net/ common-obj-y += qdev-monitor.o device-hotplug.o common-obj-$(CONFIG_WIN32) += os-win32.o diff --git a/job-qmp.c b/job-qmp.c new file mode 100644 index 0000000000..b2e18cfd9c --- /dev/null +++ b/job-qmp.c @@ -0,0 +1,134 @@ +/* + * QMP interface for background jobs + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * + * 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 "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/job.h" +#include "qapi/qapi-commands-job.h" +#include "qapi/error.h" +#include "trace-root.h" + +/* Get a job using its ID and acquire its AioContext */ +static Job *find_job(const char *id, AioContext **aio_context, Error **errp) +{ + Job *job; + + *aio_context = NULL; + + job = job_get(id); + if (!job) { + error_setg(errp, "Job not found"); + return NULL; + } + + *aio_context = job->aio_context; + aio_context_acquire(*aio_context); + + return job; +} + +void qmp_job_cancel(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_cancel(job); + job_user_cancel(job, true, errp); + aio_context_release(aio_context); +} + +void qmp_job_pause(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_pause(job); + job_user_pause(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_resume(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_resume(job); + job_user_resume(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_complete(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_complete(job); + job_complete(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_finalize(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_finalize(job); + job_finalize(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_dismiss(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_dismiss(job); + job_dismiss(&job, errp); + aio_context_release(aio_context); +} diff --git a/qapi/job.json b/qapi/job.json index 9fbdd0ccd9..b84dc6c820 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -106,3 +106,102 @@ { 'event': 'JOB_STATUS_CHANGE', 'data': { 'id': 'str', 'status': 'JobStatus' } } + +## +# @job-pause: +# +# Pause an active job. +# +# This command returns immediately after marking the active job for pausing. +# Pausing an already paused job is an error. +# +# The job will pause as soon as possible, which means transitioning into the +# PAUSED state if it was RUNNING, or into STANDBY if it was READY. The +# corresponding JOB_STATUS_CHANGE event will be emitted. +# +# Cancelling a paused job automatically resumes it. +# +# @id: The job identifier. +# +# Since: 2.13 +## +{ 'command': 'job-pause', 'data': { 'id': 'str' } } + +## +# @job-resume: +# +# Resume a paused job. +# +# This command returns immediately after resuming a paused job. Resuming an +# already running job is an error. +# +# @id : The job identifier. +# +# Since: 2.13 +## +{ 'command': 'job-resume', 'data': { 'id': 'str' } } + +## +# @job-cancel: +# +# Instruct an active background job to cancel at the next opportunity. +# This command returns immediately after marking the active job for +# cancellation. +# +# The job will cancel as soon as possible and then emit a JOB_STATUS_CHANGE +# event. Usually, the status will change to ABORTING, but it is possible that +# a job successfully completes (e.g. because it was almost done and there was +# no opportunity to cancel earlier than completing the job) and transitions to +# PENDING instead. +# +# @id: The job identifier. +# +# Since: 2.13 +## +{ 'command': 'job-cancel', 'data': { 'id': 'str' } } + + +## +# @job-complete: +# +# Manually trigger completion of an active job in the READY state. +# +# @id: The job identifier. +# +# Since: 2.13 +## +{ 'command': 'job-complete', 'data': { 'id': 'str' } } + +## +# @job-dismiss: +# +# Deletes a job that is in the CONCLUDED state. This command only needs to be +# run explicitly for jobs that don't have automatic dismiss enabled. +# +# This command will refuse to operate on any job that has not yet reached its +# terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of JOB_READY +# event, job-cancel or job-complete will still need to be used as appropriate. +# +# @id: The job identifier. +# +# Since: 2.13 +## +{ 'command': 'job-dismiss', 'data': { 'id': 'str' } } + +## +# @job-finalize: +# +# Instructs all jobs in a transaction (or a single job if it is not part of any +# transaction) to finalize any graph changes and do any necessary cleanup. This +# command requires that all involved jobs are in the PENDING state. +# +# For jobs in a transaction, instructing one job to finalize will force +# ALL jobs in the transaction to finalize, so it is only necessary to instruct +# a single member job to finalize. +# +# @id: The identifier of any job in the transaction, or of a job that is not +# part of any transaction. +# +# Since: 2.13 +## +{ 'command': 'job-finalize', 'data': { 'id': 'str' } } diff --git a/trace-events b/trace-events index ef7579a285..c445f54773 100644 --- a/trace-events +++ b/trace-events @@ -109,6 +109,15 @@ job_state_transition(void *job, int ret, const char *legal, const char *s0, con job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" +# job-qmp.c +qmp_job_cancel(void *job) "job %p" +qmp_job_pause(void *job) "job %p" +qmp_job_resume(void *job) "job %p" +qmp_job_complete(void *job) "job %p" +qmp_job_finalize(void *job) "job %p" +qmp_job_dismiss(void *job) "job %p" + + ### Guest events, keep at bottom From 456273b02474780537e2bb52a72213f63bb5227a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 4 May 2018 16:25:43 +0200 Subject: [PATCH 0716/2380] job: Add query-jobs QMP command This adds a minimal query-jobs implementation that shouldn't pose many design questions. It can later be extended to expose more information, and especially job-specific information. Signed-off-by: Kevin Wolf --- include/qemu/job.h | 3 +++ job-qmp.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ job.c | 2 +- qapi/job.json | 46 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) diff --git a/include/qemu/job.h b/include/qemu/job.h index 92d1d249fe..8c8badf75e 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -392,6 +392,9 @@ JobType job_type(const Job *job); /** Returns the enum string for the JobType of a given Job. */ const char *job_type_str(const Job *job); +/** Returns true if the job should not be visible to the management layer. */ +bool job_is_internal(Job *job); + /** Returns whether the job is scheduled for cancellation. */ bool job_is_cancelled(Job *job); diff --git a/job-qmp.c b/job-qmp.c index b2e18cfd9c..7f38f63336 100644 --- a/job-qmp.c +++ b/job-qmp.c @@ -132,3 +132,57 @@ void qmp_job_dismiss(const char *id, Error **errp) job_dismiss(&job, errp); aio_context_release(aio_context); } + +static JobInfo *job_query_single(Job *job, Error **errp) +{ + JobInfo *info; + const char *errmsg = NULL; + + assert(!job_is_internal(job)); + + if (job->ret < 0) { + errmsg = strerror(-job->ret); + } + + info = g_new(JobInfo, 1); + *info = (JobInfo) { + .id = g_strdup(job->id), + .type = job_type(job), + .status = job->status, + .current_progress = job->progress_current, + .total_progress = job->progress_total, + .has_error = !!errmsg, + .error = g_strdup(errmsg), + }; + + return info; +} + +JobInfoList *qmp_query_jobs(Error **errp) +{ + JobInfoList *head = NULL, **p_next = &head; + Job *job; + + for (job = job_next(NULL); job; job = job_next(job)) { + JobInfoList *elem; + AioContext *aio_context; + + if (job_is_internal(job)) { + continue; + } + elem = g_new0(JobInfoList, 1); + aio_context = job->aio_context; + aio_context_acquire(aio_context); + elem->value = job_query_single(job, errp); + aio_context_release(aio_context); + if (!elem->value) { + g_free(elem); + qapi_free_JobInfoList(head); + return NULL; + } + *p_next = elem; + p_next = &elem->next; + } + + return head; +} diff --git a/job.c b/job.c index 599a1042cf..f026661b0f 100644 --- a/job.c +++ b/job.c @@ -158,7 +158,7 @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) return rc; } -static bool job_is_internal(Job *job) +bool job_is_internal(Job *job) { return (job->id == NULL); } diff --git a/qapi/job.json b/qapi/job.json index b84dc6c820..970124de76 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -205,3 +205,49 @@ # Since: 2.13 ## { 'command': 'job-finalize', 'data': { 'id': 'str' } } + +## +# @JobInfo: +# +# Information about a job. +# +# @id: The job identifier +# +# @type: The kind of job that is being performed +# +# @status: Current job state/status +# +# @current-progress: Progress made until now. The unit is arbitrary and the +# value can only meaningfully be used for the ratio of +# @current-progress to @total-progress. The value is +# monotonically increasing. +# +# @total-progress: Estimated @current-progress value at the completion of +# the job. This value can arbitrarily change while the +# job is running, in both directions. +# +# @error: If this field is present, the job failed; if it is +# still missing in the CONCLUDED state, this indicates +# successful completion. +# +# The value is a human-readable error message to describe +# the reason for the job failure. It should not be parsed +# by applications. +# +# Since: 2.13 +## +{ 'struct': 'JobInfo', + 'data': { 'id': 'str', 'type': 'JobType', 'status': 'JobStatus', + 'current-progress': 'int', 'total-progress': 'int', + '*error': 'str' } } + +## +# @query-jobs: +# +# Return information about jobs. +# +# Returns: a list with a @JobInfo for each active job +# +# Since: 2.13 +## +{ 'command': 'query-jobs', 'returns': ['JobInfo'] } From 9f6bb4c004a6458227b9eec6aff3f79afe159699 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 14 May 2018 14:51:21 +0200 Subject: [PATCH 0717/2380] blockjob: Remove BlockJob.driver BlockJob.driver is redundant with Job.driver and only used in very few places any more. Remove it. Signed-off-by: Kevin Wolf --- blockjob.c | 17 ++++++++++------- include/block/blockjob.h | 3 --- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/blockjob.c b/blockjob.c index 5c8ff6f846..0306533a2e 100644 --- a/blockjob.c +++ b/blockjob.c @@ -103,10 +103,12 @@ static void block_job_attached_aio_context(AioContext *new_context, void *opaque) { BlockJob *job = opaque; + const JobDriver *drv = job->job.driver; + BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver); job->job.aio_context = new_context; - if (job->driver->attached_aio_context) { - job->driver->attached_aio_context(job, new_context); + if (bjdrv->attached_aio_context) { + bjdrv->attached_aio_context(job, new_context); } job_resume(&job->job); @@ -115,10 +117,12 @@ static void block_job_attached_aio_context(AioContext *new_context, void block_job_drain(Job *job) { BlockJob *bjob = container_of(job, BlockJob, job); + const JobDriver *drv = job->driver; + BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver); blk_drain(bjob->blk); - if (bjob->driver->drain) { - bjob->driver->drain(bjob); + if (bjdrv->drain) { + bjdrv->drain(bjob); } } @@ -201,7 +205,7 @@ bool block_job_is_internal(BlockJob *job) const BlockJobDriver *block_job_driver(BlockJob *job) { - return job->driver; + return container_of(job->job.driver, BlockJobDriver, job_driver); } /* Assumes the job_mutex is held */ @@ -386,8 +390,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, assert(job->job.driver->user_resume == &block_job_user_resume); assert(job->job.driver->drain == &block_job_drain); - job->driver = driver; - job->blk = blk; + job->blk = blk; job->finalize_cancelled_notifier.notify = block_job_event_cancelled; job->finalize_completed_notifier.notify = block_job_event_completed; diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 3021d11126..32c00b7dc0 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -43,9 +43,6 @@ typedef struct BlockJob { /** Data belonging to the generic Job infrastructure */ Job job; - /** The job type, including the job vtable. */ - const BlockJobDriver *driver; - /** The block device on which the job is operating. */ BlockBackend *blk; From 62a9428812c0f4aacbf2f7fdf449fa4f4ab3775c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 May 2018 18:10:16 +0200 Subject: [PATCH 0718/2380] iotests: Move qmp_to_opts() to VM qmp_to_opts() used to be a method of QMPTestCase, but recently we started to add more Python test cases that don't make use of QMPTestCase. In order to make the method usable there, move it to VM. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- tests/qemu-iotests/041 | 6 ++--- tests/qemu-iotests/155 | 2 +- tests/qemu-iotests/iotests.py | 45 ++++++++++++++++++----------------- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index e94587950c..c20ac7da87 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -1030,9 +1030,9 @@ class TestOrphanedSource(iotests.QMPTestCase): 'read-only': 'on' } self.vm = iotests.VM() - self.vm.add_blockdev(self.qmp_to_opts(blk0)) - self.vm.add_blockdev(self.qmp_to_opts(blk1)) - self.vm.add_blockdev(self.qmp_to_opts(blk2)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk0)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk1)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk2)) self.vm.launch() def tearDown(self): diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index 42dae04c83..63a5b5e2c0 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -63,7 +63,7 @@ class BaseClass(iotests.QMPTestCase): 'driver': iotests.imgfmt, 'file': {'driver': 'file', 'filename': source_img}} - self.vm.add_blockdev(self.qmp_to_opts(blockdev)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev)) self.vm.add_device('virtio-blk,id=qdev0,drive=source') self.vm.launch() diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index ed08e06e1f..28159d837a 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -363,6 +363,27 @@ class VM(qtest.QEMUQtestMachine): return self.qmp('human-monitor-command', command_line='qemu-io %s "%s"' % (drive, cmd)) + def flatten_qmp_object(self, obj, output=None, basestr=''): + if output is None: + output = dict() + if isinstance(obj, list): + for i in range(len(obj)): + self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') + elif isinstance(obj, dict): + for key in obj: + self.flatten_qmp_object(obj[key], output, basestr + key + '.') + else: + output[basestr[:-1]] = obj # Strip trailing '.' + return output + + def qmp_to_opts(self, obj): + obj = self.flatten_qmp_object(obj) + output_list = list() + for key in obj: + output_list += [key + '=' + obj[key]] + return ','.join(output_list) + + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') @@ -390,26 +411,6 @@ class QMPTestCase(unittest.TestCase): self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d))) return d - def flatten_qmp_object(self, obj, output=None, basestr=''): - if output is None: - output = dict() - if isinstance(obj, list): - for i in range(len(obj)): - self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') - elif isinstance(obj, dict): - for key in obj: - self.flatten_qmp_object(obj[key], output, basestr + key + '.') - else: - output[basestr[:-1]] = obj # Strip trailing '.' - return output - - def qmp_to_opts(self, obj): - obj = self.flatten_qmp_object(obj) - output_list = list() - for key in obj: - output_list += [key + '=' + obj[key]] - return ','.join(output_list) - def assert_qmp_absent(self, d, path): try: result = self.dictpath(d, path) @@ -444,8 +445,8 @@ class QMPTestCase(unittest.TestCase): '''Asserts that the given filename is a json: filename and that its content is equal to the given reference object''' self.assertEqual(json_filename[:5], 'json:') - self.assertEqual(self.flatten_qmp_object(json.loads(json_filename[5:])), - self.flatten_qmp_object(reference)) + self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])), + self.vm.flatten_qmp_object(reference)) def cancel_and_wait(self, drive='drive0', force=False, resume=False): '''Cancel a block job and wait for it to finish, returning the event''' From bdebdc712b06ba82e103d617c335830682cde242 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 May 2018 14:04:58 +0200 Subject: [PATCH 0719/2380] qemu-iotests: Test job-* with block jobs This adds a test case that tests the new job-* QMP commands with mirror and backup block jobs. Signed-off-by: Kevin Wolf --- tests/qemu-iotests/219 | 209 ++++++++++++++++++++++++ tests/qemu-iotests/219.out | 327 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 537 insertions(+) create mode 100755 tests/qemu-iotests/219 create mode 100644 tests/qemu-iotests/219.out diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219 new file mode 100755 index 0000000000..898a26eef0 --- /dev/null +++ b/tests/qemu-iotests/219 @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 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 . +# +# Creator/Owner: Kevin Wolf +# +# Check using the job-* QMP commands with block jobs + +import iotests + +iotests.verify_image_format(supported_fmts=['qcow2']) + +def pause_wait(vm, job_id): + with iotests.Timeout(3, "Timeout waiting for job to pause"): + while True: + result = vm.qmp('query-jobs') + for job in result['return']: + if job['id'] == job_id and job['status'] in ['paused', 'standby']: + return job + +# Test that block-job-pause/resume and job-pause/resume can be mixed +def test_pause_resume(vm): + for pause_cmd, pause_arg in [('block-job-pause', 'device'), + ('job-pause', 'id')]: + for resume_cmd, resume_arg in [('block-job-resume', 'device'), + ('job-resume', 'id')]: + iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd)) + + iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'})) + pause_wait(vm, 'job0') + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(vm.qmp('query-jobs')) + + iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'})) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(vm.qmp('query-jobs')) + +def test_job_lifecycle(vm, job, job_args, has_ready=False): + iotests.log('') + iotests.log('') + iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' % + (job, + job_args.get('auto-finalize', True), + job_args.get('auto-dismiss', True))) + iotests.log(vm.qmp(job, job_id='job0', **job_args)) + + # Depending on the storage, the first request may or may not have completed + # yet, so filter out the progress. Later query-job calls don't need the + # filtering because the progress is made deterministic by the block job + # speed + result = vm.qmp('query-jobs') + for j in result['return']: + del j['current-progress'] + iotests.log(result) + + # undefined -> created -> running + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + + # RUNNING state: + # pause/resume should work, complete/finalize/dismiss should error out + iotests.log('') + iotests.log('Pause/resume in RUNNING') + test_pause_resume(vm) + + iotests.log(vm.qmp('job-complete', id='job0')) + iotests.log(vm.qmp('job-finalize', id='job0')) + iotests.log(vm.qmp('job-dismiss', id='job0')) + + iotests.log(vm.qmp('block-job-complete', device='job0')) + iotests.log(vm.qmp('block-job-finalize', id='job0')) + iotests.log(vm.qmp('block-job-dismiss', id='job0')) + + # Let the job complete (or transition to READY if it supports that) + iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0)) + if has_ready: + iotests.log('') + iotests.log('Waiting for READY state...') + vm.event_wait('BLOCK_JOB_READY') + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(vm.qmp('query-jobs')) + + # READY state: + # pause/resume/complete should work, finalize/dismiss should error out + iotests.log('') + iotests.log('Pause/resume in READY') + test_pause_resume(vm) + + iotests.log(vm.qmp('job-finalize', id='job0')) + iotests.log(vm.qmp('job-dismiss', id='job0')) + + iotests.log(vm.qmp('block-job-finalize', id='job0')) + iotests.log(vm.qmp('block-job-dismiss', id='job0')) + + # Transition to WAITING + iotests.log(vm.qmp('job-complete', id='job0')) + + # Move to WAITING and PENDING state + iotests.log('') + iotests.log('Waiting for PENDING state...') + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + + if not job_args.get('auto-finalize', True): + # PENDING state: + # finalize should work, pause/complete/dismiss should error out + iotests.log(vm.qmp('query-jobs')) + + iotests.log(vm.qmp('job-pause', id='job0')) + iotests.log(vm.qmp('job-complete', id='job0')) + iotests.log(vm.qmp('job-dismiss', id='job0')) + + iotests.log(vm.qmp('block-job-pause', device='job0')) + iotests.log(vm.qmp('block-job-complete', device='job0')) + iotests.log(vm.qmp('block-job-dismiss', id='job0')) + + # Transition to CONCLUDED + iotests.log(vm.qmp('job-finalize', id='job0')) + + + # Move to CONCLUDED state + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + + if not job_args.get('auto-dismiss', True): + # CONCLUDED state: + # dismiss should work, pause/complete/finalize should error out + iotests.log(vm.qmp('query-jobs')) + + iotests.log(vm.qmp('job-pause', id='job0')) + iotests.log(vm.qmp('job-complete', id='job0')) + iotests.log(vm.qmp('job-finalize', id='job0')) + + iotests.log(vm.qmp('block-job-pause', device='job0')) + iotests.log(vm.qmp('block-job-complete', device='job0')) + iotests.log(vm.qmp('block-job-finalize', id='job0')) + + # Transition to NULL + iotests.log(vm.qmp('job-dismiss', id='job0')) + + # Move to NULL state + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(vm.qmp('query-jobs')) + + +with iotests.FilePath('disk.img') as disk_path, \ + iotests.FilePath('copy.img') as copy_path, \ + iotests.VM() as vm: + + img_size = '4M' + iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size) + iotests.qemu_io('-c', 'write 0 %s' % (img_size), + '-f', iotests.imgfmt, disk_path) + + iotests.log('Launching VM...') + vm.add_blockdev(vm.qmp_to_opts({ + 'driver': iotests.imgfmt, + 'node-name': 'drive0-node', + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + })) + vm.launch() + + # In order to keep things deterministic (especially progress in query-job, + # but related to this also automatic state transitions like job + # completion), but still get pause points often enough to avoid making this + # test very slow, it's important to have the right ratio between speed and + # buf_size. + # + # For backup, buf_size is hard-coded to the source image cluster size (64k), + # so we'll pick the same for mirror. The slice time, i.e. the granularity + # of the rate limiting is 100ms. With a speed of 256k per second, we can + # get four pause points per second. This gives us 250ms per iteration, + # which should be enough to stay deterministic. + + test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={ + 'device': 'drive0-node', + 'target': copy_path, + 'sync': 'full', + 'speed': 262144, + 'buf_size': 65536, + }) + + for auto_finalize in [True, False]: + for auto_dismiss in [True, False]: + test_job_lifecycle(vm, 'drive-backup', job_args={ + 'device': 'drive0-node', + 'target': copy_path, + 'sync': 'full', + 'speed': 262144, + 'auto-finalize': auto_finalize, + 'auto-dismiss': auto_dismiss, + }) + + vm.shutdown() diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out new file mode 100644 index 0000000000..346801b655 --- /dev/null +++ b/tests/qemu-iotests/219.out @@ -0,0 +1,327 @@ +Launching VM... + + +Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True) +{u'return': {}} +{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for READY state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} + +Pause/resume in READY +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True) +{u'return': {}} +{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False) +{u'return': {}} +{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True) +{u'return': {}} +{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False) +{u'return': {}} +{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 60ba31b292..93f93d71ba 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -216,3 +216,4 @@ 215 rw auto quick 216 rw auto quick 218 rw auto quick +219 rw auto From 1f3a4519b1c107b5db2434b30638353978366b4d Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Thu, 12 Apr 2018 23:12:29 +0800 Subject: [PATCH 0720/2380] vhost-user: support receiving file descriptors in slave_read Signed-off-by: Tiwei Bie Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-user.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 38da8692bb..85d8fd270b 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -852,14 +852,44 @@ static void slave_read(void *opaque) VhostUserHeader hdr = { 0, }; VhostUserPayload payload = { 0, }; int size, ret = 0; + struct iovec iov; + struct msghdr msgh; + int fd = -1; + char control[CMSG_SPACE(sizeof(fd))]; + struct cmsghdr *cmsg; + size_t fdsize; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = control; + msgh.msg_controllen = sizeof(control); /* Read header */ - size = read(u->slave_fd, &hdr, VHOST_USER_HDR_SIZE); + iov.iov_base = &hdr; + iov.iov_len = VHOST_USER_HDR_SIZE; + + size = recvmsg(u->slave_fd, &msgh, 0); if (size != VHOST_USER_HDR_SIZE) { error_report("Failed to read from slave."); goto err; } + if (msgh.msg_flags & MSG_CTRUNC) { + error_report("Truncated message."); + goto err; + } + + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fdsize = cmsg->cmsg_len - CMSG_LEN(0); + memcpy(&fd, CMSG_DATA(cmsg), fdsize); + break; + } + } + if (hdr.size > VHOST_USER_PAYLOAD_SIZE) { error_report("Failed to read msg header." " Size %d exceeds the maximum %zu.", hdr.size, @@ -883,9 +913,15 @@ static void slave_read(void *opaque) break; default: error_report("Received unexpected msg type."); + if (fd != -1) { + close(fd); + } ret = -EINVAL; } + /* Message handlers need to make sure that fd will be consumed. */ + fd = -1; + /* * REPLY_ACK feature handling. Other reply types has to be managed * directly in their request handlers. @@ -918,6 +954,9 @@ err: qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); close(u->slave_fd); u->slave_fd = -1; + if (fd != -1) { + close(fd); + } return; } From 6f80e6170ede13605817e5c0ca73db0de7bdf261 Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Thu, 12 Apr 2018 23:12:30 +0800 Subject: [PATCH 0721/2380] virtio: support setting memory region based host notifier This patch introduces the support for setting memory region based host notifiers for virtio device. This is helpful when using a hardware accelerator for a virtio device, because hardware heavily depends on the notification, this will allow the guest driver in the VM to notify the hardware directly. Signed-off-by: Tiwei Bie Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 22 ++++++++++++++++++++++ hw/virtio/virtio.c | 13 +++++++++++++ include/hw/virtio/virtio-bus.h | 2 ++ include/hw/virtio/virtio.h | 2 ++ 4 files changed, 39 insertions(+) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 1e8ab7bbc5..5eb0c323ca 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1037,6 +1037,27 @@ assign_error: return r; } +static int virtio_pci_set_host_notifier_mr(DeviceState *d, int n, + MemoryRegion *mr, bool assign) +{ + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + int offset; + + if (n >= VIRTIO_QUEUE_MAX || !virtio_pci_modern(proxy) || + virtio_pci_queue_mem_mult(proxy) != memory_region_size(mr)) { + return -1; + } + + if (assign) { + offset = virtio_pci_queue_mem_mult(proxy) * n; + memory_region_add_subregion_overlap(&proxy->notify.mr, offset, mr, 1); + } else { + memory_region_del_subregion(&proxy->notify.mr, mr); + } + + return 0; +} + static void virtio_pci_vmstate_change(DeviceState *d, bool running) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); @@ -2652,6 +2673,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) k->has_extra_state = virtio_pci_has_extra_state; k->query_guest_notifiers = virtio_pci_query_guest_notifiers; k->set_guest_notifiers = virtio_pci_set_guest_notifiers; + k->set_host_notifier_mr = virtio_pci_set_host_notifier_mr; k->vmstate_change = virtio_pci_vmstate_change; k->pre_plugged = virtio_pci_pre_plugged; k->device_plugged = virtio_pci_device_plugged; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 006d3d1148..1debb0147b 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2454,6 +2454,19 @@ EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) return &vq->host_notifier; } +int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n, + MemoryRegion *mr, bool assign) +{ + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + + if (k->set_host_notifier_mr) { + return k->set_host_notifier_mr(qbus->parent, n, mr, assign); + } + + return -1; +} + void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name) { g_free(vdev->bus_name); diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index ced3d2d2b0..7fec9dc929 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -52,6 +52,8 @@ typedef struct VirtioBusClass { bool (*has_extra_state)(DeviceState *d); bool (*query_guest_notifiers)(DeviceState *d); int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assign); + int (*set_host_notifier_mr)(DeviceState *d, int n, + MemoryRegion *mr, bool assign); void (*vmstate_change)(DeviceState *d, bool running); /* * Expose the features the transport layer supports before diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 098bdaaea3..9c1fa07d6d 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -239,6 +239,8 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align); void virtio_queue_notify(VirtIODevice *vdev, int n); uint16_t virtio_queue_vector(VirtIODevice *vdev, int n); void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector); +int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n, + MemoryRegion *mr, bool assign); int virtio_set_status(VirtIODevice *vdev, uint8_t val); void virtio_reset(void *opaque); void virtio_update_irq(VirtIODevice *vdev); From 9952e807fd016c95f50372536f1ac65a601be6e4 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 2 May 2018 11:55:52 +0100 Subject: [PATCH 0722/2380] vhost-user+postcopy: Use qemu_set_nonblock Use qemu_set_nonblock rather than a simple fcntl; cleaner and I have no reason to change other flags. Reported-by: Peter Maydell Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Eric Blake Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 85d8fd270b..41cbd8a455 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1115,7 +1115,7 @@ static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) error_setg(errp, "%s: Failed to get ufd", __func__); return -1; } - fcntl(ufd, F_SETFL, O_NONBLOCK); + qemu_set_nonblock(ufd); /* register ufd with userfault thread */ u->postcopy_fd.fd = ufd; From e3638151da2a314dda3ed862cf707b8b66aca4cb Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 4 May 2018 10:53:46 +0100 Subject: [PATCH 0723/2380] libvhost-user: Send messages with no data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The response to a VHOST_USER_POSTCOPY_ADVISE contains a fd but doesn't actually contain any data. FIx vu_message_write so that it doesn't do a 0-byte write() call, since this was ending up with rc=0 that was confusing the error handling code. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Marc-André Lureau Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- contrib/libvhost-user/libvhost-user.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c index beeed0c43f..54e643d871 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -323,13 +323,15 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) rc = sendmsg(conn_fd, &msg, 0); } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); - do { - if (vmsg->data) { - rc = write(conn_fd, vmsg->data, vmsg->size); - } else { - rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size); - } - } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (vmsg->size) { + do { + if (vmsg->data) { + rc = write(conn_fd, vmsg->data, vmsg->size); + } else { + rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size); + } + } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + } if (rc <= 0) { vu_panic(dev, "Error while writing: %s", strerror(errno)); From ebf2a499a5c43d5eaee2b70ab5ae655af49d935c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 21:59:40 -0700 Subject: [PATCH 0724/2380] hw/virtio: Fix brace Werror with clang 6.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The warning is hw/virtio/vhost-user.c:1319:26: error: suggest braces around initialization of subobject [-Werror,-Wmissing-braces] VhostUserMsg msg = { 0 }; ^ {} While the original code is correct, and technically exactly correct as per ISO C89, both GCC and Clang support plain empty set of braces as an extension. Cc: Michael S. Tsirkin Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eric Blake Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 41cbd8a455..ca554d4ff1 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1355,7 +1355,7 @@ static bool vhost_user_requires_shm_log(struct vhost_dev *dev) static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) { - VhostUserMsg msg = { 0 }; + VhostUserMsg msg = { }; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); From 7d405b2ffbcbee4c622deb6c0ab4f5649de7fc2b Mon Sep 17 00:00:00 2001 From: Changpeng Liu Date: Sat, 19 May 2018 06:20:46 +0800 Subject: [PATCH 0725/2380] contrib/vhost-user-blk: enable protocol feature for vhost-user-blk This patch reports the protocol feature that is only advertised by QEMU if the device implements the config ops. Signed-off-by: Changpeng Liu Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- contrib/vhost-user-blk/vhost-user-blk.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index 67dac8155a..a6a132a492 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -311,6 +311,12 @@ vub_get_features(VuDev *dev) 1ull << VHOST_USER_F_PROTOCOL_FEATURES; } +static uint64_t +vub_get_protocol_features(VuDev *dev) +{ + return 1ull << VHOST_USER_PROTOCOL_F_CONFIG; +} + static int vub_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len) { @@ -373,6 +379,7 @@ vub_set_config(VuDev *vu_dev, const uint8_t *data, static const VuDevIface vub_iface = { .get_features = vub_get_features, .queue_set_started = vub_queue_set_started, + .get_protocol_features = vub_get_protocol_features, .get_config = vub_get_config, .set_config = vub_set_config, }; From 1a97a478e69534f70ebbed1f27c315dbdc23f94f Mon Sep 17 00:00:00 2001 From: Ross Zwisler Date: Mon, 21 May 2018 10:32:00 -0600 Subject: [PATCH 0726/2380] nvdimm: fix typo in label-size definition Signed-off-by: Ross Zwisler Fixes: commit da6789c27c2e ("nvdimm: add a macro for property "label-size"") Reviewed-by: Stefan Hajnoczi Reviewed-by: Igor Mammedov Cc: Haozhong Zhang Cc: Michael S. Tsirkin Cc: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/nvdimm.c | 2 +- include/hw/mem/nvdimm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index acb656b672..4087aca25e 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -89,7 +89,7 @@ static void nvdimm_set_unarmed(Object *obj, bool value, Error **errp) static void nvdimm_init(Object *obj) { - object_property_add(obj, NVDIMM_LABLE_SIZE_PROP, "int", + object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "int", nvdimm_get_label_size, nvdimm_set_label_size, NULL, NULL, NULL); object_property_add_bool(obj, NVDIMM_UNARMED_PROP, diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index 7fd87c4e1c..74c60332e1 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -48,7 +48,7 @@ #define NVDIMM_GET_CLASS(obj) OBJECT_GET_CLASS(NVDIMMClass, (obj), \ TYPE_NVDIMM) -#define NVDIMM_LABLE_SIZE_PROP "label-size" +#define NVDIMM_LABEL_SIZE_PROP "label-size" #define NVDIMM_UNARMED_PROP "unarmed" struct NVDIMMDevice { From 36d2d52bdb45f5b753a61fdaf0fe7891f1f5b61d Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 18 May 2018 15:25:09 +0800 Subject: [PATCH 0727/2380] intel-iommu: send PSI always even if across PDEs SECURITY IMPLICATION: without this patch, any guest with both assigned device and a vIOMMU might encounter stale IO page mappings even if guest has already unmapped the page, which may lead to guest memory corruption. The stale mappings will only be limited to the guest's own memory range, so it should not affect the host memory or other guests on the host. During IOVA page table walking, there is a special case when the PSI covers one whole PDE (Page Directory Entry, which contains 512 Page Table Entries) or more. In the past, we skip that entry and we don't notify the IOMMU notifiers. This is not correct. We should send UNMAP notification to registered UNMAP notifiers in this case. For UNMAP only notifiers, this might cause IOTLBs cached in the devices even if they were already invalid. For MAP/UNMAP notifiers like vfio-pci, this will cause stale page mappings. This special case doesn't trigger often, but it is very easy to be triggered by nested device assignments, since in that case we'll possibly map the whole L2 guest RAM region into the device's IOVA address space (several GBs at least), which is far bigger than normal kernel driver usages of the device (tens of MBs normally). Without this patch applied to L1 QEMU, nested device assignment to L2 guests will dump some errors like: qemu-system-x86_64: VFIO_MAP_DMA: -17 qemu-system-x86_64: vfio_dma_map(0x557305420c30, 0xad000, 0x1000, 0x7f89a920d000) = -17 (File exists) CC: QEMU Stable Acked-by: Jason Wang [peterx: rewrite the commit message] Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index fb31de9416..b359efd6f9 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -722,6 +722,15 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); +static int vtd_page_walk_one(IOMMUTLBEntry *entry, int level, + vtd_page_walk_hook hook_fn, void *private) +{ + assert(hook_fn); + trace_vtd_page_walk_one(level, entry->iova, entry->translated_addr, + entry->addr_mask, entry->perm); + return hook_fn(entry, private); +} + /** * vtd_page_walk_level - walk over specific level for IOVA range * @@ -781,28 +790,37 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, */ entry_valid = read_cur | write_cur; + entry.target_as = &address_space_memory; + entry.iova = iova & subpage_mask; + entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); + entry.addr_mask = ~subpage_mask; + if (vtd_is_last_slpte(slpte, level)) { - entry.target_as = &address_space_memory; - entry.iova = iova & subpage_mask; /* NOTE: this is only meaningful if entry_valid == true */ entry.translated_addr = vtd_get_slpte_addr(slpte, aw); - entry.addr_mask = ~subpage_mask; - entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); if (!entry_valid && !notify_unmap) { trace_vtd_page_walk_skip_perm(iova, iova_next); goto next; } - trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr, - entry.addr_mask, entry.perm); - if (hook_fn) { - ret = hook_fn(&entry, private); - if (ret < 0) { - return ret; - } + ret = vtd_page_walk_one(&entry, level, hook_fn, private); + if (ret < 0) { + return ret; } } else { if (!entry_valid) { - trace_vtd_page_walk_skip_perm(iova, iova_next); + if (notify_unmap) { + /* + * The whole entry is invalid; unmap it all. + * Translated address is meaningless, zero it. + */ + entry.translated_addr = 0x0; + ret = vtd_page_walk_one(&entry, level, hook_fn, private); + if (ret < 0) { + return ret; + } + } else { + trace_vtd_page_walk_skip_perm(iova, iova_next); + } goto next; } ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, aw), iova, From b4a4ba0d68f50f218ee3957b6638dbee32a5eeef Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 18 May 2018 15:25:10 +0800 Subject: [PATCH 0728/2380] intel-iommu: remove IntelIOMMUNotifierNode That is not really necessary. Removing that node struct and put the list entry directly into VTDAddressSpace. It simplfies the code a lot. Since at it, rename the old notifiers_list into vtd_as_with_notifiers. CC: QEMU Stable Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 41 ++++++++++------------------------- include/hw/i386/intel_iommu.h | 9 ++------ 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index b359efd6f9..3df90457f8 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1248,10 +1248,10 @@ static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s) static void vtd_iommu_replay_all(IntelIOMMUState *s) { - IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; - QLIST_FOREACH(node, &s->notifiers_list, next) { - memory_region_iommu_replay_all(&node->vtd_as->iommu); + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { + memory_region_iommu_replay_all(&vtd_as->iommu); } } @@ -1372,7 +1372,6 @@ static void vtd_iotlb_global_invalidate(IntelIOMMUState *s) static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) { - IntelIOMMUNotifierNode *node; VTDContextEntry ce; VTDAddressSpace *vtd_as; @@ -1381,8 +1380,7 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, &domain_id); - QLIST_FOREACH(node, &s->notifiers_list, next) { - vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce) && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { @@ -1402,12 +1400,11 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, uint16_t domain_id, hwaddr addr, uint8_t am) { - IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; VTDContextEntry ce; int ret; - QLIST_FOREACH(node, &(s->notifiers_list), next) { - VTDAddressSpace *vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce); if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { @@ -2344,8 +2341,6 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; - IntelIOMMUNotifierNode *node = NULL; - IntelIOMMUNotifierNode *next_node = NULL; if (!s->caching_mode && new & IOMMU_NOTIFIER_MAP) { error_report("We need to set caching-mode=1 for intel-iommu to enable " @@ -2354,21 +2349,9 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, } if (old == IOMMU_NOTIFIER_NONE) { - node = g_malloc0(sizeof(*node)); - node->vtd_as = vtd_as; - QLIST_INSERT_HEAD(&s->notifiers_list, node, next); - return; - } - - /* update notifier node with new flags */ - QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) { - if (node->vtd_as == vtd_as) { - if (new == IOMMU_NOTIFIER_NONE) { - QLIST_REMOVE(node, next); - g_free(node); - } - return; - } + QLIST_INSERT_HEAD(&s->vtd_as_with_notifiers, vtd_as, next); + } else if (new == IOMMU_NOTIFIER_NONE) { + QLIST_REMOVE(vtd_as, next); } } @@ -2838,12 +2821,10 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) static void vtd_address_space_unmap_all(IntelIOMMUState *s) { - IntelIOMMUNotifierNode *node; VTDAddressSpace *vtd_as; IOMMUNotifier *n; - QLIST_FOREACH(node, &s->notifiers_list, next) { - vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) { vtd_address_space_unmap(vtd_as, n); } @@ -3088,7 +3069,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) return; } - QLIST_INIT(&s->notifiers_list); + QLIST_INIT(&s->vtd_as_with_notifiers); memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, "intel_iommu", DMAR_REG_SIZE); diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 45ec8919b6..032e33bcb2 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -67,7 +67,6 @@ typedef union VTD_IR_TableEntry VTD_IR_TableEntry; typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress; typedef struct VTDIrq VTDIrq; typedef struct VTD_MSIMessage VTD_MSIMessage; -typedef struct IntelIOMMUNotifierNode IntelIOMMUNotifierNode; /* Context-Entry */ struct VTDContextEntry { @@ -93,6 +92,7 @@ struct VTDAddressSpace { MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */ IntelIOMMUState *iommu_state; VTDContextCacheEntry context_cache_entry; + QLIST_ENTRY(VTDAddressSpace) next; }; struct VTDBus { @@ -253,11 +253,6 @@ struct VTD_MSIMessage { /* When IR is enabled, all MSI/MSI-X data bits should be zero */ #define VTD_IR_MSI_DATA (0) -struct IntelIOMMUNotifierNode { - VTDAddressSpace *vtd_as; - QLIST_ENTRY(IntelIOMMUNotifierNode) next; -}; - /* The iommu (DMAR) device state struct */ struct IntelIOMMUState { X86IOMMUState x86_iommu; @@ -295,7 +290,7 @@ struct IntelIOMMUState { GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */ VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */ /* list of registered notifiers */ - QLIST_HEAD(, IntelIOMMUNotifierNode) notifiers_list; + QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers; /* interrupt remapping */ bool intr_enabled; /* Whether guest enabled IR */ From 1d9efa73e12ddf361ea997c2d532cc4afa6674d1 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 18 May 2018 15:25:11 +0800 Subject: [PATCH 0729/2380] intel-iommu: add iommu lock SECURITY IMPLICATION: this patch fixes a potential race when multiple threads access the IOMMU IOTLB cache. Add a per-iommu big lock to protect IOMMU status. Currently the only thing to be protected is the IOTLB/context cache, since that can be accessed even without BQL, e.g., in IO dataplane. Note that we don't need to protect device page tables since that's fully controlled by the guest kernel. However there is still possibility that malicious drivers will program the device to not obey the rule. In that case QEMU can't really do anything useful, instead the guest itself will be responsible for all uncertainties. CC: QEMU Stable Reported-by: Fam Zheng Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 56 +++++++++++++++++++++++++++++------ include/hw/i386/intel_iommu.h | 6 ++++ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 3df90457f8..8d4069de9f 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -128,6 +128,16 @@ static uint64_t vtd_set_clear_mask_quad(IntelIOMMUState *s, hwaddr addr, return new_val; } +static inline void vtd_iommu_lock(IntelIOMMUState *s) +{ + qemu_mutex_lock(&s->iommu_lock); +} + +static inline void vtd_iommu_unlock(IntelIOMMUState *s) +{ + qemu_mutex_unlock(&s->iommu_lock); +} + /* GHashTable functions */ static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2) { @@ -172,9 +182,9 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, } /* Reset all the gen of VTDAddressSpace to zero and set the gen of - * IntelIOMMUState to 1. + * IntelIOMMUState to 1. Must be called with IOMMU lock held. */ -static void vtd_reset_context_cache(IntelIOMMUState *s) +static void vtd_reset_context_cache_locked(IntelIOMMUState *s) { VTDAddressSpace *vtd_as; VTDBus *vtd_bus; @@ -197,12 +207,20 @@ static void vtd_reset_context_cache(IntelIOMMUState *s) s->context_cache_gen = 1; } -static void vtd_reset_iotlb(IntelIOMMUState *s) +/* Must be called with IOMMU lock held. */ +static void vtd_reset_iotlb_locked(IntelIOMMUState *s) { assert(s->iotlb); g_hash_table_remove_all(s->iotlb); } +static void vtd_reset_iotlb(IntelIOMMUState *s) +{ + vtd_iommu_lock(s); + vtd_reset_iotlb_locked(s); + vtd_iommu_unlock(s); +} + static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id, uint32_t level) { @@ -215,6 +233,7 @@ static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level) return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; } +/* Must be called with IOMMU lock held */ static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, hwaddr addr) { @@ -235,6 +254,7 @@ out: return entry; } +/* Must be with IOMMU lock held */ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, uint16_t domain_id, hwaddr addr, uint64_t slpte, uint8_t access_flags, uint32_t level) @@ -246,7 +266,7 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id); if (g_hash_table_size(s->iotlb) >= VTD_IOTLB_MAX_SIZE) { trace_vtd_iotlb_reset("iotlb exceeds size limit"); - vtd_reset_iotlb(s); + vtd_reset_iotlb_locked(s); } entry->gfn = gfn; @@ -1106,7 +1126,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, IntelIOMMUState *s = vtd_as->iommu_state; VTDContextEntry ce; uint8_t bus_num = pci_bus_num(bus); - VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry; + VTDContextCacheEntry *cc_entry; uint64_t slpte, page_mask; uint32_t level; uint16_t source_id = vtd_make_source_id(bus_num, devfn); @@ -1123,6 +1143,10 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, */ assert(!vtd_is_interrupt_addr(addr)); + vtd_iommu_lock(s); + + cc_entry = &vtd_as->context_cache_entry; + /* Try to fetch slpte form IOTLB */ iotlb_entry = vtd_lookup_iotlb(s, source_id, addr); if (iotlb_entry) { @@ -1182,7 +1206,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, * IOMMU region can be swapped back. */ vtd_pt_enable_fast_path(s, source_id); - + vtd_iommu_unlock(s); return true; } @@ -1203,6 +1227,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte, access_flags, level); out: + vtd_iommu_unlock(s); entry->iova = addr & page_mask; entry->translated_addr = vtd_get_slpte_addr(slpte, s->aw_bits) & page_mask; entry->addr_mask = ~page_mask; @@ -1210,6 +1235,7 @@ out: return true; error: + vtd_iommu_unlock(s); entry->iova = 0; entry->translated_addr = 0; entry->addr_mask = 0; @@ -1258,10 +1284,13 @@ static void vtd_iommu_replay_all(IntelIOMMUState *s) static void vtd_context_global_invalidate(IntelIOMMUState *s) { trace_vtd_inv_desc_cc_global(); + /* Protects context cache */ + vtd_iommu_lock(s); s->context_cache_gen++; if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) { - vtd_reset_context_cache(s); + vtd_reset_context_cache_locked(s); } + vtd_iommu_unlock(s); vtd_switch_address_space_all(s); /* * From VT-d spec 6.5.2.1, a global context entry invalidation @@ -1313,7 +1342,9 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, if (vtd_as && ((devfn_it & mask) == (devfn & mask))) { trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it), VTD_PCI_FUNC(devfn_it)); + vtd_iommu_lock(s); vtd_as->context_cache_entry.context_cache_gen = 0; + vtd_iommu_unlock(s); /* * Do switch address space when needed, in case if the * device passthrough bit is switched. @@ -1377,8 +1408,10 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) trace_vtd_inv_desc_iotlb_domain(domain_id); + vtd_iommu_lock(s); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, &domain_id); + vtd_iommu_unlock(s); QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), @@ -1426,7 +1459,9 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, info.domain_id = domain_id; info.addr = addr; info.mask = ~((1 << am) - 1); + vtd_iommu_lock(s); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info); + vtd_iommu_unlock(s); vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am); } @@ -2929,8 +2964,10 @@ static void vtd_init(IntelIOMMUState *s) s->cap |= VTD_CAP_CM; } - vtd_reset_context_cache(s); - vtd_reset_iotlb(s); + vtd_iommu_lock(s); + vtd_reset_context_cache_locked(s); + vtd_reset_iotlb_locked(s); + vtd_iommu_unlock(s); /* Define registers with default values and bit semantics */ vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0); @@ -3070,6 +3107,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) } QLIST_INIT(&s->vtd_as_with_notifiers); + qemu_mutex_init(&s->iommu_lock); memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, "intel_iommu", DMAR_REG_SIZE); diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 032e33bcb2..016e74bedb 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -300,6 +300,12 @@ struct IntelIOMMUState { OnOffAuto intr_eim; /* Toggle for EIM cabability */ bool buggy_eim; /* Force buggy EIM unless eim=off */ uint8_t aw_bits; /* Host/IOVA address width (in bits) */ + + /* + * Protects IOMMU states in general. Currently it protects the + * per-IOMMU IOTLB cache, and context entry cache in VTDAddressSpace. + */ + QemuMutex iommu_lock; }; /* Find the VTD Address space associated with the given bus pointer, From 4f8a62a933a79094e44bc1b16b63bb23e62d67b4 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 18 May 2018 15:25:12 +0800 Subject: [PATCH 0730/2380] intel-iommu: only do page walk for MAP notifiers For UNMAP-only IOMMU notifiers, we don't need to walk the page tables. Fasten that procedure by skipping the page table walk. That should boost performance for UNMAP-only notifiers like vhost. CC: QEMU Stable Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 44 +++++++++++++++++++++++++++++++---- include/hw/i386/intel_iommu.h | 2 ++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 8d4069de9f..38ccc741f9 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -138,6 +138,12 @@ static inline void vtd_iommu_unlock(IntelIOMMUState *s) qemu_mutex_unlock(&s->iommu_lock); } +/* Whether the address space needs to notify new mappings */ +static inline gboolean vtd_as_has_map_notifier(VTDAddressSpace *as) +{ + return as->notifier_flags & IOMMU_NOTIFIER_MAP; +} + /* GHashTable functions */ static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2) { @@ -1436,14 +1442,36 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, VTDAddressSpace *vtd_as; VTDContextEntry ce; int ret; + hwaddr size = (1 << am) * VTD_PAGE_SIZE; QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce); if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { - vtd_page_walk(&ce, addr, addr + (1 << am) * VTD_PAGE_SIZE, - vtd_page_invalidate_notify_hook, - (void *)&vtd_as->iommu, true, s->aw_bits); + if (vtd_as_has_map_notifier(vtd_as)) { + /* + * As long as we have MAP notifications registered in + * any of our IOMMU notifiers, we need to sync the + * shadow page table. + */ + vtd_page_walk(&ce, addr, addr + size, + vtd_page_invalidate_notify_hook, + (void *)&vtd_as->iommu, true, s->aw_bits); + } else { + /* + * For UNMAP-only notifiers, we don't need to walk the + * page tables. We just deliver the PSI down to + * invalidate caches. + */ + IOMMUTLBEntry entry = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = 0, + .addr_mask = size - 1, + .perm = IOMMU_NONE, + }; + memory_region_notify_iommu(&vtd_as->iommu, entry); + } } } } @@ -2383,6 +2411,9 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, exit(1); } + /* Update per-address-space notifier flags */ + vtd_as->notifier_flags = new; + if (old == IOMMU_NOTIFIER_NONE) { QLIST_INSERT_HEAD(&s->vtd_as_with_notifiers, vtd_as, next); } else if (new == IOMMU_NOTIFIER_NONE) { @@ -2891,8 +2922,11 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) PCI_FUNC(vtd_as->devfn), VTD_CONTEXT_ENTRY_DID(ce.hi), ce.hi, ce.lo); - vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false, - s->aw_bits); + if (vtd_as_has_map_notifier(vtd_as)) { + /* This is required only for MAP typed notifiers */ + vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false, + s->aw_bits); + } } else { trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), PCI_FUNC(vtd_as->devfn)); diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 016e74bedb..156f35e919 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -93,6 +93,8 @@ struct VTDAddressSpace { IntelIOMMUState *iommu_state; VTDContextCacheEntry context_cache_entry; QLIST_ENTRY(VTDAddressSpace) next; + /* Superset of notifier flags that this address space has */ + IOMMUNotifierFlag notifier_flags; }; struct VTDBus { From fe215b0cbb8c1f4b4af0a64aa5c02042080dd537 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 18 May 2018 15:25:13 +0800 Subject: [PATCH 0731/2380] intel-iommu: introduce vtd_page_walk_info During the recursive page walking of IOVA page tables, some stack variables are constant variables and never changed during the whole page walking procedure. Isolate them into a struct so that we don't need to pass those contants down the stack every time and multiple times. CC: QEMU Stable Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 84 ++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 38ccc741f9..e247269659 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -748,9 +748,27 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); +/** + * Constant information used during page walking + * + * @hook_fn: hook func to be called when detected page + * @private: private data to be passed into hook func + * @notify_unmap: whether we should notify invalid entries + * @aw: maximum address width + */ +typedef struct { + vtd_page_walk_hook hook_fn; + void *private; + bool notify_unmap; + uint8_t aw; +} vtd_page_walk_info; + static int vtd_page_walk_one(IOMMUTLBEntry *entry, int level, - vtd_page_walk_hook hook_fn, void *private) + vtd_page_walk_info *info) { + vtd_page_walk_hook hook_fn = info->hook_fn; + void *private = info->private; + assert(hook_fn); trace_vtd_page_walk_one(level, entry->iova, entry->translated_addr, entry->addr_mask, entry->perm); @@ -763,17 +781,13 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, int level, * @addr: base GPA addr to start the walk * @start: IOVA range start address * @end: IOVA range end address (start <= addr < end) - * @hook_fn: hook func to be called when detected page - * @private: private data to be passed into hook func * @read: whether parent level has read permission * @write: whether parent level has write permission - * @notify_unmap: whether we should notify invalid entries - * @aw: maximum address width + * @info: constant information for the page walk */ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, - uint64_t end, vtd_page_walk_hook hook_fn, - void *private, uint32_t level, bool read, - bool write, bool notify_unmap, uint8_t aw) + uint64_t end, uint32_t level, bool read, + bool write, vtd_page_walk_info *info) { bool read_cur, write_cur, entry_valid; uint32_t offset; @@ -823,24 +837,24 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, if (vtd_is_last_slpte(slpte, level)) { /* NOTE: this is only meaningful if entry_valid == true */ - entry.translated_addr = vtd_get_slpte_addr(slpte, aw); - if (!entry_valid && !notify_unmap) { + entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); + if (!entry_valid && !info->notify_unmap) { trace_vtd_page_walk_skip_perm(iova, iova_next); goto next; } - ret = vtd_page_walk_one(&entry, level, hook_fn, private); + ret = vtd_page_walk_one(&entry, level, info); if (ret < 0) { return ret; } } else { if (!entry_valid) { - if (notify_unmap) { + if (info->notify_unmap) { /* * The whole entry is invalid; unmap it all. * Translated address is meaningless, zero it. */ entry.translated_addr = 0x0; - ret = vtd_page_walk_one(&entry, level, hook_fn, private); + ret = vtd_page_walk_one(&entry, level, info); if (ret < 0) { return ret; } @@ -849,10 +863,9 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, } goto next; } - ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, aw), iova, - MIN(iova_next, end), hook_fn, private, - level - 1, read_cur, write_cur, - notify_unmap, aw); + ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw), + iova, MIN(iova_next, end), level - 1, + read_cur, write_cur, info); if (ret < 0) { return ret; } @@ -871,28 +884,24 @@ next: * @ce: context entry to walk upon * @start: IOVA address to start the walk * @end: IOVA range end address (start <= addr < end) - * @hook_fn: the hook that to be called for each detected area - * @private: private data for the hook function - * @aw: maximum address width + * @info: page walking information struct */ static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end, - vtd_page_walk_hook hook_fn, void *private, - bool notify_unmap, uint8_t aw) + vtd_page_walk_info *info) { dma_addr_t addr = vtd_ce_get_slpt_base(ce); uint32_t level = vtd_ce_get_level(ce); - if (!vtd_iova_range_check(start, ce, aw)) { + if (!vtd_iova_range_check(start, ce, info->aw)) { return -VTD_FR_ADDR_BEYOND_MGAW; } - if (!vtd_iova_range_check(end, ce, aw)) { + if (!vtd_iova_range_check(end, ce, info->aw)) { /* Fix end so that it reaches the maximum */ - end = vtd_iova_limit(ce, aw); + end = vtd_iova_limit(ce, info->aw); } - return vtd_page_walk_level(addr, start, end, hook_fn, private, - level, true, true, notify_unmap, aw); + return vtd_page_walk_level(addr, start, end, level, true, true, info); } /* Map a device to its corresponding domain (context-entry) */ @@ -1449,14 +1458,19 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, vtd_as->devfn, &ce); if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { if (vtd_as_has_map_notifier(vtd_as)) { + vtd_page_walk_info info = { + .hook_fn = vtd_page_invalidate_notify_hook, + .private = (void *)&vtd_as->iommu, + .notify_unmap = true, + .aw = s->aw_bits, + }; + /* * As long as we have MAP notifications registered in * any of our IOMMU notifiers, we need to sync the * shadow page table. */ - vtd_page_walk(&ce, addr, addr + size, - vtd_page_invalidate_notify_hook, - (void *)&vtd_as->iommu, true, s->aw_bits); + vtd_page_walk(&ce, addr, addr + size, &info); } else { /* * For UNMAP-only notifiers, we don't need to walk the @@ -2924,8 +2938,14 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) ce.hi, ce.lo); if (vtd_as_has_map_notifier(vtd_as)) { /* This is required only for MAP typed notifiers */ - vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false, - s->aw_bits); + vtd_page_walk_info info = { + .hook_fn = vtd_replay_hook, + .private = (void *)n, + .notify_unmap = false, + .aw = s->aw_bits, + }; + + vtd_page_walk(&ce, 0, ~0ULL, &info); } } else { trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), From 2f764fa87d2a81812b313dd6d998e10126292653 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 18 May 2018 15:25:14 +0800 Subject: [PATCH 0732/2380] intel-iommu: pass in address space when page walk We pass in the VTDAddressSpace too. It'll be used in the follow up patches. CC: QEMU Stable Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index e247269659..a882894f49 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -754,9 +754,11 @@ typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); * @hook_fn: hook func to be called when detected page * @private: private data to be passed into hook func * @notify_unmap: whether we should notify invalid entries + * @as: VT-d address space of the device * @aw: maximum address width */ typedef struct { + VTDAddressSpace *as; vtd_page_walk_hook hook_fn; void *private; bool notify_unmap; @@ -1463,6 +1465,7 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, .private = (void *)&vtd_as->iommu, .notify_unmap = true, .aw = s->aw_bits, + .as = vtd_as, }; /* @@ -2943,6 +2946,7 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) .private = (void *)n, .notify_unmap = false, .aw = s->aw_bits, + .as = vtd_as, }; vtd_page_walk(&ce, 0, ~0ULL, &info); From d118c06ebbee2d23ddf873cae4a809311aa61310 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 18 May 2018 15:25:15 +0800 Subject: [PATCH 0733/2380] intel-iommu: trace domain id during page walk This patch only modifies the trace points. Previously we were tracing page walk levels. They are redundant since we have page mask (size) already. Now we trace something much more useful which is the domain ID of the page walking. That can be very useful when we trace more than one devices on the same system, so that we can know which map is for which domain. CC: QEMU Stable Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 16 ++++++++++------ hw/i386/trace-events | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index a882894f49..61bb3d31e7 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -756,6 +756,7 @@ typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); * @notify_unmap: whether we should notify invalid entries * @as: VT-d address space of the device * @aw: maximum address width + * @domain: domain ID of the page walk */ typedef struct { VTDAddressSpace *as; @@ -763,17 +764,18 @@ typedef struct { void *private; bool notify_unmap; uint8_t aw; + uint16_t domain_id; } vtd_page_walk_info; -static int vtd_page_walk_one(IOMMUTLBEntry *entry, int level, - vtd_page_walk_info *info) +static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) { vtd_page_walk_hook hook_fn = info->hook_fn; void *private = info->private; assert(hook_fn); - trace_vtd_page_walk_one(level, entry->iova, entry->translated_addr, - entry->addr_mask, entry->perm); + trace_vtd_page_walk_one(info->domain_id, entry->iova, + entry->translated_addr, entry->addr_mask, + entry->perm); return hook_fn(entry, private); } @@ -844,7 +846,7 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, trace_vtd_page_walk_skip_perm(iova, iova_next); goto next; } - ret = vtd_page_walk_one(&entry, level, info); + ret = vtd_page_walk_one(&entry, info); if (ret < 0) { return ret; } @@ -856,7 +858,7 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, * Translated address is meaningless, zero it. */ entry.translated_addr = 0x0; - ret = vtd_page_walk_one(&entry, level, info); + ret = vtd_page_walk_one(&entry, info); if (ret < 0) { return ret; } @@ -1466,6 +1468,7 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, .notify_unmap = true, .aw = s->aw_bits, .as = vtd_as, + .domain_id = domain_id, }; /* @@ -2947,6 +2950,7 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) .notify_unmap = false, .aw = s->aw_bits, .as = vtd_as, + .domain_id = VTD_CONTEXT_ENTRY_DID(ce.hi), }; vtd_page_walk(&ce, 0, ~0ULL, &info); diff --git a/hw/i386/trace-events b/hw/i386/trace-events index 22d44648af..ca23ba9fad 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -39,7 +39,7 @@ vtd_fault_disabled(void) "Fault processing disabled for context entry" vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 -vtd_page_walk_one(uint32_t level, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "detected page level 0x%"PRIx32" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIu16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read" vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty" vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" From eecf5eedbdc0fc04f39abcf3afeedfbf21b25ca4 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 18 May 2018 15:25:16 +0800 Subject: [PATCH 0734/2380] util: implement simple iova tree Introduce a simplest iova tree implementation based on GTree. CC: QEMU Stable Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 6 ++ include/qemu/iova-tree.h | 134 +++++++++++++++++++++++++++++++++++++++ util/Makefile.objs | 1 + util/iova-tree.c | 114 +++++++++++++++++++++++++++++++++ 4 files changed, 255 insertions(+) create mode 100644 include/qemu/iova-tree.h create mode 100644 util/iova-tree.c diff --git a/MAINTAINERS b/MAINTAINERS index e187b1f18f..f07fcee72e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1783,6 +1783,12 @@ F: include/sysemu/replay.h F: docs/replay.txt F: stubs/replay.c +IOVA Tree +M: Peter Xu +S: Maintained +F: include/qemu/iova-tree.h +F: util/iova-tree.c + Usermode Emulation ------------------ Overall diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h new file mode 100644 index 0000000000..b061932097 --- /dev/null +++ b/include/qemu/iova-tree.h @@ -0,0 +1,134 @@ +/* + * An very simplified iova tree implementation based on GTree. + * + * Copyright 2018 Red Hat, Inc. + * + * Authors: + * Peter Xu + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + */ +#ifndef IOVA_TREE_H +#define IOVA_TREE_H + +/* + * Currently the iova tree will only allow to keep ranges + * information, and no extra user data is allowed for each element. A + * benefit is that we can merge adjacent ranges internally within the + * tree. It can save a lot of memory when the ranges are splitted but + * mostly continuous. + * + * Note that current implementation does not provide any thread + * protections. Callers of the iova tree should be responsible + * for the thread safety issue. + */ + +#include "qemu/osdep.h" +#include "exec/memory.h" +#include "exec/hwaddr.h" + +#define IOVA_OK (0) +#define IOVA_ERR_INVALID (-1) /* Invalid parameters */ +#define IOVA_ERR_OVERLAP (-2) /* IOVA range overlapped */ + +typedef struct IOVATree IOVATree; +typedef struct DMAMap { + hwaddr iova; + hwaddr translated_addr; + hwaddr size; /* Inclusive */ + IOMMUAccessFlags perm; +} QEMU_PACKED DMAMap; +typedef gboolean (*iova_tree_iterator)(DMAMap *map); + +/** + * iova_tree_new: + * + * Create a new iova tree. + * + * Returns: the tree pointer when succeeded, or NULL if error. + */ +IOVATree *iova_tree_new(void); + +/** + * iova_tree_insert: + * + * @tree: the iova tree to insert + * @map: the mapping to insert + * + * Insert an iova range to the tree. If there is overlapped + * ranges, IOVA_ERR_OVERLAP will be returned. + * + * Return: 0 if succeeded, or <0 if error. + */ +int iova_tree_insert(IOVATree *tree, DMAMap *map); + +/** + * iova_tree_remove: + * + * @tree: the iova tree to remove range from + * @map: the map range to remove + * + * Remove mappings from the tree that are covered by the map range + * provided. The range does not need to be exactly what has inserted, + * all the mappings that are included in the provided range will be + * removed from the tree. Here map->translated_addr is meaningless. + * + * Return: 0 if succeeded, or <0 if error. + */ +int iova_tree_remove(IOVATree *tree, DMAMap *map); + +/** + * iova_tree_find: + * + * @tree: the iova tree to search from + * @map: the mapping to search + * + * Search for a mapping in the iova tree that overlaps with the + * mapping range specified. Only the first found mapping will be + * returned. + * + * Return: DMAMap pointer if found, or NULL if not found. Note that + * the returned DMAMap pointer is maintained internally. User should + * only read the content but never modify or free the content. Also, + * user is responsible to make sure the pointer is valid (say, no + * concurrent deletion in progress). + */ +DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map); + +/** + * iova_tree_find_address: + * + * @tree: the iova tree to search from + * @iova: the iova address to find + * + * Similar to iova_tree_find(), but it tries to find mapping with + * range iova=iova & size=0. + * + * Return: same as iova_tree_find(). + */ +DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova); + +/** + * iova_tree_foreach: + * + * @tree: the iova tree to iterate on + * @iterator: the interator for the mappings, return true to stop + * + * Iterate over the iova tree. + * + * Return: 1 if found any overlap, 0 if not, <0 if error. + */ +void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator); + +/** + * iova_tree_destroy: + * + * @tree: the iova tree to destroy + * + * Destroy an existing iova tree. + * + * Return: None. + */ +void iova_tree_destroy(IOVATree *tree); + +#endif diff --git a/util/Makefile.objs b/util/Makefile.objs index 728c3541db..e1c3fed4dc 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -47,4 +47,5 @@ util-obj-y += qht.o util-obj-y += range.o util-obj-y += stats64.o util-obj-y += systemd.o +util-obj-y += iova-tree.o util-obj-$(CONFIG_LINUX) += vfio-helpers.o diff --git a/util/iova-tree.c b/util/iova-tree.c new file mode 100644 index 0000000000..2d9cebfc89 --- /dev/null +++ b/util/iova-tree.c @@ -0,0 +1,114 @@ +/* + * IOVA tree implementation based on GTree. + * + * Copyright 2018 Red Hat, Inc. + * + * Authors: + * Peter Xu + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + */ + +#include +#include "qemu/iova-tree.h" + +struct IOVATree { + GTree *tree; +}; + +static int iova_tree_compare(gconstpointer a, gconstpointer b, gpointer data) +{ + const DMAMap *m1 = a, *m2 = b; + + if (m1->iova > m2->iova + m2->size) { + return 1; + } + + if (m1->iova + m1->size < m2->iova) { + return -1; + } + + /* Overlapped */ + return 0; +} + +IOVATree *iova_tree_new(void) +{ + IOVATree *iova_tree = g_new0(IOVATree, 1); + + /* We don't have values actually, no need to free */ + iova_tree->tree = g_tree_new_full(iova_tree_compare, NULL, g_free, NULL); + + return iova_tree; +} + +DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map) +{ + return g_tree_lookup(tree->tree, map); +} + +DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova) +{ + DMAMap map = { .iova = iova, .size = 0 }; + + return iova_tree_find(tree, &map); +} + +static inline void iova_tree_insert_internal(GTree *gtree, DMAMap *range) +{ + /* Key and value are sharing the same range data */ + g_tree_insert(gtree, range, range); +} + +int iova_tree_insert(IOVATree *tree, DMAMap *map) +{ + DMAMap *new; + + if (map->iova + map->size < map->iova || map->perm == IOMMU_NONE) { + return IOVA_ERR_INVALID; + } + + /* We don't allow to insert range that overlaps with existings */ + if (iova_tree_find(tree, map)) { + return IOVA_ERR_OVERLAP; + } + + new = g_new0(DMAMap, 1); + memcpy(new, map, sizeof(*new)); + iova_tree_insert_internal(tree->tree, new); + + return IOVA_OK; +} + +static gboolean iova_tree_traverse(gpointer key, gpointer value, + gpointer data) +{ + iova_tree_iterator iterator = data; + DMAMap *map = key; + + g_assert(key == value); + + return iterator(map); +} + +void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator) +{ + g_tree_foreach(tree->tree, iova_tree_traverse, iterator); +} + +int iova_tree_remove(IOVATree *tree, DMAMap *map) +{ + DMAMap *overlap; + + while ((overlap = iova_tree_find(tree, map))) { + g_tree_remove(tree->tree, overlap); + } + + return IOVA_OK; +} + +void iova_tree_destroy(IOVATree *tree) +{ + g_tree_destroy(tree->tree); + g_free(tree); +} From 63b88968f139b6a77f2f81e6f1eedf70c0170a85 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 18 May 2018 15:25:17 +0800 Subject: [PATCH 0735/2380] intel-iommu: rework the page walk logic This patch fixes a potential small window that the DMA page table might be incomplete or invalid when the guest sends domain/context invalidations to a device. This can cause random DMA errors for assigned devices. This is a major change to the VT-d shadow page walking logic. It includes but is not limited to: - For each VTDAddressSpace, now we maintain what IOVA ranges we have mapped and what we have not. With that information, now we only send MAP or UNMAP when necessary. Say, we don't send MAP notifies if we know we have already mapped the range, meanwhile we don't send UNMAP notifies if we know we never mapped the range at all. - Introduce vtd_sync_shadow_page_table[_range] APIs so that we can call in any places to resync the shadow page table for a device. - When we receive domain/context invalidation, we should not really run the replay logic, instead we use the new sync shadow page table API to resync the whole shadow page table without unmapping the whole region. After this change, we'll only do the page walk once for each domain invalidations (before this, it can be multiple, depending on number of notifiers per address space). While at it, the page walking logic is also refactored to be simpler. CC: QEMU Stable Reported-by: Jintack Lim Tested-by: Jintack Lim Signed-off-by: Peter Xu Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 213 +++++++++++++++++++++++++--------- hw/i386/trace-events | 3 +- include/hw/i386/intel_iommu.h | 2 + 3 files changed, 159 insertions(+), 59 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 61bb3d31e7..b5a09b7908 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -769,10 +769,77 @@ typedef struct { static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) { + VTDAddressSpace *as = info->as; vtd_page_walk_hook hook_fn = info->hook_fn; void *private = info->private; + DMAMap target = { + .iova = entry->iova, + .size = entry->addr_mask, + .translated_addr = entry->translated_addr, + .perm = entry->perm, + }; + DMAMap *mapped = iova_tree_find(as->iova_tree, &target); + + if (entry->perm == IOMMU_NONE && !info->notify_unmap) { + trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); + return 0; + } assert(hook_fn); + + /* Update local IOVA mapped ranges */ + if (entry->perm) { + if (mapped) { + /* If it's exactly the same translation, skip */ + if (!memcmp(mapped, &target, sizeof(target))) { + trace_vtd_page_walk_one_skip_map(entry->iova, entry->addr_mask, + entry->translated_addr); + return 0; + } else { + /* + * Translation changed. Normally this should not + * happen, but it can happen when with buggy guest + * OSes. Note that there will be a small window that + * we don't have map at all. But that's the best + * effort we can do. The ideal way to emulate this is + * atomically modify the PTE to follow what has + * changed, but we can't. One example is that vfio + * driver only has VFIO_IOMMU_[UN]MAP_DMA but no + * interface to modify a mapping (meanwhile it seems + * meaningless to even provide one). Anyway, let's + * mark this as a TODO in case one day we'll have + * a better solution. + */ + IOMMUAccessFlags cache_perm = entry->perm; + int ret; + + /* Emulate an UNMAP */ + entry->perm = IOMMU_NONE; + trace_vtd_page_walk_one(info->domain_id, + entry->iova, + entry->translated_addr, + entry->addr_mask, + entry->perm); + ret = hook_fn(entry, private); + if (ret) { + return ret; + } + /* Drop any existing mapping */ + iova_tree_remove(as->iova_tree, &target); + /* Recover the correct permission */ + entry->perm = cache_perm; + } + } + iova_tree_insert(as->iova_tree, &target); + } else { + if (!mapped) { + /* Skip since we didn't map this range at all */ + trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); + return 0; + } + iova_tree_remove(as->iova_tree, &target); + } + trace_vtd_page_walk_one(info->domain_id, entry->iova, entry->translated_addr, entry->addr_mask, entry->perm); @@ -834,45 +901,34 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, */ entry_valid = read_cur | write_cur; - entry.target_as = &address_space_memory; - entry.iova = iova & subpage_mask; - entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); - entry.addr_mask = ~subpage_mask; - - if (vtd_is_last_slpte(slpte, level)) { - /* NOTE: this is only meaningful if entry_valid == true */ - entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); - if (!entry_valid && !info->notify_unmap) { - trace_vtd_page_walk_skip_perm(iova, iova_next); - goto next; - } - ret = vtd_page_walk_one(&entry, info); - if (ret < 0) { - return ret; - } - } else { - if (!entry_valid) { - if (info->notify_unmap) { - /* - * The whole entry is invalid; unmap it all. - * Translated address is meaningless, zero it. - */ - entry.translated_addr = 0x0; - ret = vtd_page_walk_one(&entry, info); - if (ret < 0) { - return ret; - } - } else { - trace_vtd_page_walk_skip_perm(iova, iova_next); - } - goto next; - } + if (!vtd_is_last_slpte(slpte, level) && entry_valid) { + /* + * This is a valid PDE (or even bigger than PDE). We need + * to walk one further level. + */ ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw), iova, MIN(iova_next, end), level - 1, read_cur, write_cur, info); - if (ret < 0) { - return ret; - } + } else { + /* + * This means we are either: + * + * (1) the real page entry (either 4K page, or huge page) + * (2) the whole range is invalid + * + * In either case, we send an IOTLB notification down. + */ + entry.target_as = &address_space_memory; + entry.iova = iova & subpage_mask; + entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); + entry.addr_mask = ~subpage_mask; + /* NOTE: this is only meaningful if entry_valid == true */ + entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); + ret = vtd_page_walk_one(&entry, info); + } + + if (ret < 0) { + return ret; } next: @@ -964,6 +1020,58 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, return 0; } +static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry, + void *private) +{ + memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); + return 0; +} + +/* If context entry is NULL, we'll try to fetch it on our own. */ +static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, + VTDContextEntry *ce, + hwaddr addr, hwaddr size) +{ + IntelIOMMUState *s = vtd_as->iommu_state; + vtd_page_walk_info info = { + .hook_fn = vtd_sync_shadow_page_hook, + .private = (void *)&vtd_as->iommu, + .notify_unmap = true, + .aw = s->aw_bits, + .as = vtd_as, + }; + VTDContextEntry ce_cache; + int ret; + + if (ce) { + /* If the caller provided context entry, use it */ + ce_cache = *ce; + } else { + /* If the caller didn't provide ce, try to fetch */ + ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce_cache); + if (ret) { + /* + * This should not really happen, but in case it happens, + * we just skip the sync for this time. After all we even + * don't have the root table pointer! + */ + trace_vtd_err("Detected invalid context entry when " + "trying to sync shadow page table"); + return 0; + } + } + + info.domain_id = VTD_CONTEXT_ENTRY_DID(ce_cache.hi); + + return vtd_page_walk(&ce_cache, addr, addr + size, &info); +} + +static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) +{ + return vtd_sync_shadow_page_table_range(vtd_as, NULL, 0, UINT64_MAX); +} + /* * Fetch translation type for specific device. Returns <0 if error * happens, otherwise return the shifted type to check against @@ -1296,7 +1404,7 @@ static void vtd_iommu_replay_all(IntelIOMMUState *s) VTDAddressSpace *vtd_as; QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { - memory_region_iommu_replay_all(&vtd_as->iommu); + vtd_sync_shadow_page_table(vtd_as); } } @@ -1371,14 +1479,13 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, vtd_switch_address_space(vtd_as); /* * So a device is moving out of (or moving into) a - * domain, a replay() suites here to notify all the - * IOMMU_NOTIFIER_MAP registers about this change. + * domain, resync the shadow page table. * This won't bring bad even if we have no such * notifier registered - the IOMMU notification * framework will skip MAP notifications if that * happened. */ - memory_region_iommu_replay_all(&vtd_as->iommu); + vtd_sync_shadow_page_table(vtd_as); } } } @@ -1436,18 +1543,11 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce) && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { - memory_region_iommu_replay_all(&vtd_as->iommu); + vtd_sync_shadow_page_table(vtd_as); } } } -static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry, - void *private) -{ - memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); - return 0; -} - static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, uint16_t domain_id, hwaddr addr, uint8_t am) @@ -1462,21 +1562,12 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, vtd_as->devfn, &ce); if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { if (vtd_as_has_map_notifier(vtd_as)) { - vtd_page_walk_info info = { - .hook_fn = vtd_page_invalidate_notify_hook, - .private = (void *)&vtd_as->iommu, - .notify_unmap = true, - .aw = s->aw_bits, - .as = vtd_as, - .domain_id = domain_id, - }; - /* * As long as we have MAP notifications registered in * any of our IOMMU notifiers, we need to sync the * shadow page table. */ - vtd_page_walk(&ce, addr, addr + size, &info); + vtd_sync_shadow_page_table_range(vtd_as, &ce, addr, size); } else { /* * For UNMAP-only notifiers, we don't need to walk the @@ -2806,6 +2897,7 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) vtd_dev_as->devfn = (uint8_t)devfn; vtd_dev_as->iommu_state = s; vtd_dev_as->context_cache_entry.context_cache_gen = 0; + vtd_dev_as->iova_tree = iova_tree_new(); /* * Memory region relationships looks like (Address range shows @@ -2858,6 +2950,7 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) hwaddr start = n->start; hwaddr end = n->end; IntelIOMMUState *s = as->iommu_state; + DMAMap map; /* * Note: all the codes in this function has a assumption that IOVA @@ -2902,6 +2995,10 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) VTD_PCI_FUNC(as->devfn), entry.iova, size); + map.iova = entry.iova; + map.size = entry.addr_mask; + iova_tree_remove(as->iova_tree, &map); + memory_region_notify_one(n, &entry); } diff --git a/hw/i386/trace-events b/hw/i386/trace-events index ca23ba9fad..e14d06ec83 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -40,8 +40,9 @@ vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint6 vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIu16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_one_skip_map(uint64_t iova, uint64_t mask, uint64_t translated) "iova 0x%"PRIx64" mask 0x%"PRIx64" translated 0x%"PRIx64 +vtd_page_walk_one_skip_unmap(uint64_t iova, uint64_t mask) "iova 0x%"PRIx64" mask 0x%"PRIx64 vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read" -vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty" vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64 diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 156f35e919..fbfedcb1c0 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -27,6 +27,7 @@ #include "hw/i386/ioapic.h" #include "hw/pci/msi.h" #include "hw/sysbus.h" +#include "qemu/iova-tree.h" #define TYPE_INTEL_IOMMU_DEVICE "intel-iommu" #define INTEL_IOMMU_DEVICE(obj) \ @@ -95,6 +96,7 @@ struct VTDAddressSpace { QLIST_ENTRY(VTDAddressSpace) next; /* Superset of notifier flags that this address space has */ IOMMUNotifierFlag notifier_flags; + IOVATree *iova_tree; /* Traces mapped IOVA ranges */ }; struct VTDBus { From a89fe6c329799e47aaa1663650f076b28808e186 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 14 May 2018 12:31:17 +0200 Subject: [PATCH 0736/2380] vga: catch depth 0 depth == 0 is used to indicate 256 color modes. Our region calculation goes wrong in that case. So detect that and just take the safe code path we already have for the wraparound case. While being at it also catch depth == 15 (where our region size calculation goes wrong too). And make the comment more verbose, explaining what is going on here. Without this windows guest install might trigger an assert due to trying to check dirty bitmap outside the snapshot region. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1575541 Signed-off-by: Gerd Hoffmann Message-id: 20180514103117.21059-1-kraxel@redhat.com --- hw/display/vga.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/hw/display/vga.c b/hw/display/vga.c index 72181330b8..a7794f6d1f 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1480,13 +1480,28 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->get_resolution(s, &width, &height); disp_width = width; + depth = s->get_bpp(s); region_start = (s->start_addr * 4); region_end = region_start + (ram_addr_t)s->line_offset * height; - region_end += width * s->get_bpp(s) / 8; /* scanline length */ + region_end += width * depth / 8; /* scanline length */ region_end -= s->line_offset; - if (region_end > s->vbe_size) { - /* wraps around (can happen with cirrus vbe modes) */ + if (region_end > s->vbe_size || depth == 0 || depth == 15) { + /* + * We land here on: + * - wraps around (can happen with cirrus vbe modes) + * - depth == 0 (256 color palette video mode) + * - depth == 15 + * + * Take the safe and slow route: + * - create a dirty bitmap snapshot for all vga memory. + * - force shadowing (so all vga memory access goes + * through vga_read_*() helpers). + * + * Given this affects only vga features which are pretty much + * unused by modern guests there should be no performance + * impact. + */ region_start = 0; region_end = s->vbe_size; force_shadow = true; @@ -1520,8 +1535,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } } - depth = s->get_bpp(s); - /* * Check whether we can share the surface with the backend * or whether we need a shadow surface. We share native From a3ee49f07511ff487f55a15b295fb9e89453ca13 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:53 +0200 Subject: [PATCH 0737/2380] vga: move bochs vbe defines to header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a new header file, move the bochs vbe dispi interface defines to it, so they can be used outside vga code. Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-id: 20180522165058.15404-2-kraxel@redhat.com --- hw/display/vga-pci.c | 13 ------- hw/display/vga_int.h | 35 +---------------- include/hw/display/bochs-vbe.h | 69 ++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 46 deletions(-) create mode 100644 include/hw/display/bochs-vbe.h diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index f312930664..fb3e4cd400 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -31,19 +31,6 @@ #include "qemu/timer.h" #include "hw/loader.h" -#define PCI_VGA_IOPORT_OFFSET 0x400 -#define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) -#define PCI_VGA_BOCHS_OFFSET 0x500 -#define PCI_VGA_BOCHS_SIZE (0x0b * 2) -#define PCI_VGA_QEXT_OFFSET 0x600 -#define PCI_VGA_QEXT_SIZE (2 * 4) -#define PCI_VGA_MMIO_SIZE 0x1000 - -#define PCI_VGA_QEXT_REG_SIZE (0 * 4) -#define PCI_VGA_QEXT_REG_BYTEORDER (1 * 4) -#define PCI_VGA_QEXT_LITTLE_ENDIAN 0x1e1e1e1e -#define PCI_VGA_QEXT_BIG_ENDIAN 0xbebebebe - enum vga_pci_flags { PCI_VGA_FLAG_ENABLE_MMIO = 1, PCI_VGA_FLAG_ENABLE_QEXT = 2, diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index fe23b81442..313cff84fc 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -29,42 +29,11 @@ #include "exec/memory.h" #include "ui/console.h" +#include "hw/display/bochs-vbe.h" + #define ST01_V_RETRACE 0x08 #define ST01_DISP_ENABLE 0x01 -#define VBE_DISPI_MAX_XRES 16000 -#define VBE_DISPI_MAX_YRES 12000 -#define VBE_DISPI_MAX_BPP 32 - -#define VBE_DISPI_INDEX_ID 0x0 -#define VBE_DISPI_INDEX_XRES 0x1 -#define VBE_DISPI_INDEX_YRES 0x2 -#define VBE_DISPI_INDEX_BPP 0x3 -#define VBE_DISPI_INDEX_ENABLE 0x4 -#define VBE_DISPI_INDEX_BANK 0x5 -#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 -#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 -#define VBE_DISPI_INDEX_X_OFFSET 0x8 -#define VBE_DISPI_INDEX_Y_OFFSET 0x9 -#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ -#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ - -#define VBE_DISPI_ID0 0xB0C0 -#define VBE_DISPI_ID1 0xB0C1 -#define VBE_DISPI_ID2 0xB0C2 -#define VBE_DISPI_ID3 0xB0C3 -#define VBE_DISPI_ID4 0xB0C4 -#define VBE_DISPI_ID5 0xB0C5 - -#define VBE_DISPI_DISABLED 0x00 -#define VBE_DISPI_ENABLED 0x01 -#define VBE_DISPI_GETCAPS 0x02 -#define VBE_DISPI_8BIT_DAC 0x20 -#define VBE_DISPI_LFB_ENABLED 0x40 -#define VBE_DISPI_NOCLEARMEM 0x80 - -#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 - #define CH_ATTR_SIZE (160 * 100) #define VGA_MAX_HEIGHT 2048 diff --git a/include/hw/display/bochs-vbe.h b/include/hw/display/bochs-vbe.h new file mode 100644 index 0000000000..bc2f046eee --- /dev/null +++ b/include/hw/display/bochs-vbe.h @@ -0,0 +1,69 @@ +#ifndef HW_DISPLAY_BOCHS_VBE_H +#define HW_DISPLAY_BOCHS_VBE_H + +/* + * bochs vesa bios extension interface + */ + +#define VBE_DISPI_MAX_XRES 16000 +#define VBE_DISPI_MAX_YRES 12000 +#define VBE_DISPI_MAX_BPP 32 + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ + +/* VBE_DISPI_INDEX_ID */ +#define VBE_DISPI_ID0 0xB0C0 +#define VBE_DISPI_ID1 0xB0C1 +#define VBE_DISPI_ID2 0xB0C2 +#define VBE_DISPI_ID3 0xB0C3 +#define VBE_DISPI_ID4 0xB0C4 +#define VBE_DISPI_ID5 0xB0C5 + +/* VBE_DISPI_INDEX_ENABLE */ +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_GETCAPS 0x02 +#define VBE_DISPI_8BIT_DAC 0x20 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +/* only used by isa-vga, pci vga devices use a memory bar */ +#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + + +/* + * qemu extension: mmio bar (region 2) + */ + +#define PCI_VGA_MMIO_SIZE 0x1000 + +/* vga register region */ +#define PCI_VGA_IOPORT_OFFSET 0x400 +#define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) + +/* bochs vbe register region */ +#define PCI_VGA_BOCHS_OFFSET 0x500 +#define PCI_VGA_BOCHS_SIZE (0x0b * 2) + +/* qemu extension register region */ +#define PCI_VGA_QEXT_OFFSET 0x600 +#define PCI_VGA_QEXT_SIZE (2 * 4) + +/* qemu extension registers */ +#define PCI_VGA_QEXT_REG_SIZE (0 * 4) +#define PCI_VGA_QEXT_REG_BYTEORDER (1 * 4) +#define PCI_VGA_QEXT_LITTLE_ENDIAN 0x1e1e1e1e +#define PCI_VGA_QEXT_BIG_ENDIAN 0xbebebebe + +#endif /* HW_DISPLAY_BOCHS_VBE_H */ From 83ff909f93d35368832230c1a4e70c84793a695c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:54 +0200 Subject: [PATCH 0738/2380] vga-pci: use PCI_VGA_MMIO_SIZE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180522165058.15404-3-kraxel@redhat.com --- hw/display/vga-pci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index fb3e4cd400..700ac58c69 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -232,7 +232,8 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) /* mmio bar for vga register access */ if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) { - memory_region_init(&d->mmio, NULL, "vga.mmio", 4096); + memory_region_init(&d->mmio, NULL, "vga.mmio", + PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; @@ -267,7 +268,8 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); /* mmio bar */ - memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", 4096); + memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", + PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; From 765c94290863eef1fc4a67819d452cc13b7854a1 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:55 +0200 Subject: [PATCH 0739/2380] hw/display: add new bochs-display device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After writing up the virtual mdev device emulating a display supporting the bochs vbe dispi interface (mbochs.ko) and seeing how simple it actually is I've figured that would be useful for qemu too. So, here it is, -device bochs-display. It is basically -device VGA without legacy vga emulation. PCI bar 0 is the framebuffer, PCI bar 2 is mmio with the registers. The vga registers are simply not there though, neither in the legacy ioport location nor in the mmio bar. Consequently it is PCI class DISPLAY_OTHER not DISPLAY_VGA. So there is no text mode emulation, no weird video modes (planar, 256color palette), no memory window at 0xa0000. Just a linear framebuffer in the pci memory bar. And the amount of code to emulate this (and therefore the attack surface) is an order of magnitude smaller when compared to vga emulation. Compatibility wise it works with OVMF (latest git master). The bochs-drm.ko linux kernel module can handle it just fine too. So UEFI guests should not see any functional difference to VGA. Signed-off-by: Gerd Hoffmann Tested-by: Marc-André Lureau Reviewed-by: Marc-André Lureau Message-id: 20180522165058.15404-4-kraxel@redhat.com --- hw/display/Makefile.objs | 1 + hw/display/bochs-display.c | 325 +++++++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 hw/display/bochs-display.c diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 11321e466b..d907b381ae 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -9,6 +9,7 @@ common-obj-$(CONFIG_SSD0323) += ssd0323.o common-obj-$(CONFIG_XEN) += xenfb.o common-obj-$(CONFIG_VGA_PCI) += vga-pci.o +common-obj-$(CONFIG_VGA_PCI) += bochs-display.o common-obj-$(CONFIG_VGA_ISA) += vga-isa.o common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c new file mode 100644 index 0000000000..a523e81d1d --- /dev/null +++ b/hw/display/bochs-display.c @@ -0,0 +1,325 @@ +/* + * QEMU PCI bochs display adapter. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/display/bochs-vbe.h" + +#include "qapi/error.h" + +#include "ui/console.h" +#include "ui/qemu-pixman.h" + +typedef struct BochsDisplayMode { + pixman_format_code_t format; + uint32_t bytepp; + uint32_t width; + uint32_t height; + uint32_t stride; + uint64_t offset; + uint64_t size; +} BochsDisplayMode; + +typedef struct BochsDisplayState { + /* parent */ + PCIDevice pci; + + /* device elements */ + QemuConsole *con; + MemoryRegion vram; + MemoryRegion mmio; + MemoryRegion vbe; + MemoryRegion qext; + + /* device config */ + uint64_t vgamem; + + /* device registers */ + uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; + bool big_endian_fb; + + /* device state */ + BochsDisplayMode mode; +} BochsDisplayState; + +#define TYPE_BOCHS_DISPLAY "bochs-display" +#define BOCHS_DISPLAY(obj) OBJECT_CHECK(BochsDisplayState, (obj), \ + TYPE_BOCHS_DISPLAY) + +static const VMStateDescription vmstate_bochs_display = { + .name = "bochs-display", + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pci, BochsDisplayState), + VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB), + VMSTATE_BOOL(big_endian_fb, BochsDisplayState), + VMSTATE_END_OF_LIST() + } +}; + +static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr, + unsigned size) +{ + BochsDisplayState *s = ptr; + unsigned int index = addr >> 1; + + switch (index) { + case VBE_DISPI_INDEX_ID: + return VBE_DISPI_ID5; + case VBE_DISPI_INDEX_VIDEO_MEMORY_64K: + return s->vgamem / (64 * 1024); + } + + if (index >= ARRAY_SIZE(s->vbe_regs)) { + return -1; + } + return s->vbe_regs[index]; +} + +static void bochs_display_vbe_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ + BochsDisplayState *s = ptr; + unsigned int index = addr >> 1; + + if (index >= ARRAY_SIZE(s->vbe_regs)) { + return; + } + s->vbe_regs[index] = val; +} + +static const MemoryRegionOps bochs_display_vbe_ops = { + .read = bochs_display_vbe_read, + .write = bochs_display_vbe_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 2, + .impl.max_access_size = 2, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t bochs_display_qext_read(void *ptr, hwaddr addr, + unsigned size) +{ + BochsDisplayState *s = ptr; + + switch (addr) { + case PCI_VGA_QEXT_REG_SIZE: + return PCI_VGA_QEXT_SIZE; + case PCI_VGA_QEXT_REG_BYTEORDER: + return s->big_endian_fb ? + PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN; + default: + return 0; + } +} + +static void bochs_display_qext_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ + BochsDisplayState *s = ptr; + + switch (addr) { + case PCI_VGA_QEXT_REG_BYTEORDER: + if (val == PCI_VGA_QEXT_BIG_ENDIAN) { + s->big_endian_fb = true; + } + if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) { + s->big_endian_fb = false; + } + break; + } +} + +static const MemoryRegionOps bochs_display_qext_ops = { + .read = bochs_display_qext_read, + .write = bochs_display_qext_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int bochs_display_get_mode(BochsDisplayState *s, + BochsDisplayMode *mode) +{ + uint16_t *vbe = s->vbe_regs; + uint32_t virt_width; + + if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { + return -1; + } + + memset(mode, 0, sizeof(*mode)); + switch (vbe[VBE_DISPI_INDEX_BPP]) { + case 16: + /* best effort: support native endianess only */ + mode->format = PIXMAN_r5g6b5; + mode->bytepp = 2; + case 32: + mode->format = s->big_endian_fb + ? PIXMAN_BE_x8r8g8b8 + : PIXMAN_LE_x8r8g8b8; + mode->bytepp = 4; + break; + default: + return -1; + } + + mode->width = vbe[VBE_DISPI_INDEX_XRES]; + mode->height = vbe[VBE_DISPI_INDEX_YRES]; + virt_width = vbe[VBE_DISPI_INDEX_VIRT_WIDTH]; + if (virt_width < mode->width) { + virt_width = mode->width; + } + mode->stride = virt_width * mode->bytepp; + mode->size = (uint64_t)mode->stride * mode->height; + mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp + + (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride); + + if (mode->width < 64 || mode->height < 64) { + return -1; + } + if (mode->offset + mode->size > s->vgamem) { + return -1; + } + return 0; +} + +static void bochs_display_update(void *opaque) +{ + BochsDisplayState *s = opaque; + BochsDisplayMode mode; + DisplaySurface *ds; + uint8_t *ptr; + int ret; + + ret = bochs_display_get_mode(s, &mode); + if (ret < 0) { + /* no (valid) video mode */ + return; + } + + if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) { + /* video mode switch */ + s->mode = mode; + ptr = memory_region_get_ram_ptr(&s->vram); + ds = qemu_create_displaysurface_from(mode.width, + mode.height, + mode.format, + mode.stride, + ptr + mode.offset); + dpy_gfx_replace_surface(s->con, ds); + } + + dpy_gfx_update_full(s->con); +} + +static const GraphicHwOps bochs_display_gfx_ops = { + .gfx_update = bochs_display_update, +}; + +static void bochs_display_realize(PCIDevice *dev, Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(dev); + Object *obj = OBJECT(dev); + + s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s); + + if (s->vgamem < (4 * 1024 * 1024)) { + error_setg(errp, "bochs-display: video memory too small"); + } + if (s->vgamem > (256 * 1024 * 1024)) { + error_setg(errp, "bochs-display: video memory too big"); + } + s->vgamem = pow2ceil(s->vgamem); + + memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem, + &error_fatal); + memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s, + "bochs dispi interface", PCI_VGA_BOCHS_SIZE); + memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s, + "qemu extended regs", PCI_VGA_QEXT_SIZE); + + memory_region_init(&s->mmio, obj, "bochs-display-mmio", + PCI_VGA_MMIO_SIZE); + memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe); + memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext); + + pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2); + pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); + pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); +} + +static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(obj); + + return s->big_endian_fb; +} + +static void bochs_display_set_big_endian_fb(Object *obj, bool value, + Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(obj); + + s->big_endian_fb = value; +} + +static void bochs_display_init(Object *obj) +{ + /* Expose framebuffer byteorder via QOM */ + object_property_add_bool(obj, "big-endian-framebuffer", + bochs_display_get_big_endian_fb, + bochs_display_set_big_endian_fb, + NULL); +} + +static void bochs_display_exit(PCIDevice *dev) +{ + BochsDisplayState *s = BOCHS_DISPLAY(dev); + + graphic_console_close(s->con); +} + +static Property bochs_display_properties[] = { + DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * 1024 * 1024), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bochs_display_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->class_id = PCI_CLASS_DISPLAY_OTHER; + k->vendor_id = PCI_VENDOR_ID_QEMU; + k->device_id = PCI_DEVICE_ID_QEMU_VGA; + + k->realize = bochs_display_realize; + k->exit = bochs_display_exit; + dc->vmsd = &vmstate_bochs_display; + dc->props = bochs_display_properties; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); +} + +static const TypeInfo bochs_display_type_info = { + .name = TYPE_BOCHS_DISPLAY, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(BochsDisplayState), + .instance_init = bochs_display_init, + .class_init = bochs_display_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void bochs_display_register_types(void) +{ + type_register_static(&bochs_display_type_info); +} + +type_init(bochs_display_register_types) From 33ebad54056f0e54b9db1d675d34eed097479c69 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:56 +0200 Subject: [PATCH 0740/2380] bochs-display: add dirty tracking support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Message-id: 20180522165058.15404-5-kraxel@redhat.com --- hw/display/bochs-display.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index a523e81d1d..9607df1c34 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -191,10 +191,13 @@ static int bochs_display_get_mode(BochsDisplayState *s, static void bochs_display_update(void *opaque) { BochsDisplayState *s = opaque; + DirtyBitmapSnapshot *snap = NULL; + bool full_update = false; BochsDisplayMode mode; DisplaySurface *ds; uint8_t *ptr; - int ret; + bool dirty; + int y, ys, ret; ret = bochs_display_get_mode(s, &mode); if (ret < 0) { @@ -212,9 +215,34 @@ static void bochs_display_update(void *opaque) mode.stride, ptr + mode.offset); dpy_gfx_replace_surface(s->con, ds); + full_update = true; } - dpy_gfx_update_full(s->con); + if (full_update) { + dpy_gfx_update_full(s->con); + } else { + snap = memory_region_snapshot_and_clear_dirty(&s->vram, + mode.offset, mode.size, + DIRTY_MEMORY_VGA); + ys = -1; + for (y = 0; y < mode.height; y++) { + dirty = memory_region_snapshot_get_dirty(&s->vram, snap, + mode.offset + mode.stride * y, + mode.stride); + if (dirty && ys < 0) { + ys = y; + } + if (!dirty && ys >= 0) { + dpy_gfx_update(s->con, 0, ys, + mode.width, y - ys); + ys = -1; + } + } + if (ys >= 0) { + dpy_gfx_update(s->con, 0, ys, + mode.width, y - ys); + } + } } static const GraphicHwOps bochs_display_gfx_ops = { @@ -251,6 +279,8 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp) pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2); pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); + + memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); } static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp) From f2581064a68f37d3406b90f9a0929801d97b2758 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:57 +0200 Subject: [PATCH 0741/2380] bochs-display: add pcie support Signed-off-by: Gerd Hoffmann Message-id: 20180522165058.15404-6-kraxel@redhat.com --- hw/display/bochs-display.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 9607df1c34..c33524b558 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -253,6 +253,7 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp) { BochsDisplayState *s = BOCHS_DISPLAY(dev); Object *obj = OBJECT(dev); + int ret; s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s); @@ -280,6 +281,12 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp) pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); + if (pci_bus_is_express(pci_get_bus(dev))) { + dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + ret = pcie_endpoint_cap_init(dev, 0x80); + assert(ret > 0); + } + memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); } @@ -342,6 +349,7 @@ static const TypeInfo bochs_display_type_info = { .instance_init = bochs_display_init, .class_init = bochs_display_class_init, .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, From dbb2e4726c76dbffb39efe6623bf75d2ec1db8bc Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:58 +0200 Subject: [PATCH 0742/2380] MAINTAINERS: add vga entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add entries for standard vga, virtio-gpu and cirrus. Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180522165058.15404-7-kraxel@redhat.com --- MAINTAINERS | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e187b1f18f..2af3ad5172 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1314,6 +1314,27 @@ S: Maintained F: include/hw/misc/unimp.h F: hw/misc/unimp.c +Standard VGA +M: Gerd Hoffmann +S: Maintained +F: hw/display/vga* +F: hw/display/bochs-display.c +F: include/hw/display/vga.h +F: include/hw/display/bochs-vbe.h + +virtio-gpu +M: Gerd Hoffmann +S: Maintained +F: hw/display/virtio-gpu* +F: hw/display/virtio-vga.c +F: include/hw/virtio/virtio-gpu.h + +Cirrus VGA +M: Gerd Hoffmann +S: Odd Fixes +W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/ +F: hw/display/cirrus* + Subsystems ---------- Audio From 38ab74e7ca6961f019d771c9500df2ab340693fa Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 11 Oct 2017 10:36:53 -0400 Subject: [PATCH 0743/2380] tpm: extend TPM emulator with state migration support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the TPM emulator backend device with state migration support. The external TPM emulator 'swtpm' provides a protocol over its control channel to retrieve its state blobs. We implement functions for getting and setting the different state blobs. In case the setting of the state blobs fails, we return a negative errno code to fail the start of the VM. Since we have an external TPM emulator, we need to make sure that we do not migrate the state for as long as it is busy processing a request. We need to wait for notification that the request has completed processing. Signed-off-by: Stefan Berger Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Marc-André Lureau --- hw/tpm/tpm_emulator.c | 323 ++++++++++++++++++++++++++++++++++++++++-- hw/tpm/trace-events | 8 +- 2 files changed, 319 insertions(+), 12 deletions(-) diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c index 6418ef0831..10bc20dbec 100644 --- a/hw/tpm/tpm_emulator.c +++ b/hw/tpm/tpm_emulator.c @@ -4,7 +4,7 @@ * Copyright (c) 2017 Intel Corporation * Author: Amarnath Valluri * - * Copyright (c) 2010 - 2013 IBM Corporation + * Copyright (c) 2010 - 2013, 2018 IBM Corporation * Authors: * Stefan Berger * @@ -49,6 +49,19 @@ #define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) /* data structures */ + +/* blobs from the TPM; part of VM state when migrating */ +typedef struct TPMBlobBuffers { + uint32_t permanent_flags; + TPMSizedBuffer permanent; + + uint32_t volatil_flags; + TPMSizedBuffer volatil; + + uint32_t savestate_flags; + TPMSizedBuffer savestate; +} TPMBlobBuffers; + typedef struct TPMEmulator { TPMBackend parent; @@ -64,6 +77,8 @@ typedef struct TPMEmulator { unsigned int established_flag:1; unsigned int established_flag_cached:1; + + TPMBlobBuffers state_blobs; } TPMEmulator; @@ -293,7 +308,8 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb, return 0; } -static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) +static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize, + bool is_resume) { TPMEmulator *tpm_emu = TPM_EMULATOR(tb); ptm_init init = { @@ -301,12 +317,17 @@ static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) }; ptm_res res; + trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize); + if (buffersize != 0 && tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) { goto err_exit; } - trace_tpm_emulator_startup_tpm(); + if (is_resume) { + init.u.req.init_flags |= cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATILE); + } + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), sizeof(init)) < 0) { error_report("tpm-emulator: could not send INIT: %s", @@ -325,6 +346,11 @@ err_exit: return -1; } +static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) +{ + return tpm_emulator_startup_tpm_resume(tb, buffersize, false); +} + static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) { TPMEmulator *tpm_emu = TPM_EMULATOR(tb); @@ -423,16 +449,21 @@ static size_t tpm_emulator_get_buffer_size(TPMBackend *tb) static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) { Error *err = NULL; + ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | + PTM_CAP_STOP; - error_setg(&tpm_emu->migration_blocker, - "Migration disabled: TPM emulator not yet migratable"); - migrate_add_blocker(tpm_emu->migration_blocker, &err); - if (err) { - error_report_err(err); - error_free(tpm_emu->migration_blocker); - tpm_emu->migration_blocker = NULL; + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { + error_setg(&tpm_emu->migration_blocker, + "Migration disabled: TPM emulator does not support " + "migration"); + migrate_add_blocker(tpm_emu->migration_blocker, &err); + if (err) { + error_report_err(err); + error_free(tpm_emu->migration_blocker); + tpm_emu->migration_blocker = NULL; - return -1; + return -1; + } } return 0; @@ -570,6 +601,267 @@ static const QemuOptDesc tpm_emulator_cmdline_opts[] = { { /* end of list */ }, }; +/* + * Transfer a TPM state blob from the TPM into a provided buffer. + * + * @tpm_emu: TPMEmulator + * @type: the type of blob to transfer + * @tsb: the TPMSizeBuffer to fill with the blob + * @flags: the flags to return to the caller + */ +static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu, + uint8_t type, + TPMSizedBuffer *tsb, + uint32_t *flags) +{ + ptm_getstate pgs; + ptm_res res; + ssize_t n; + uint32_t totlength, length; + + tpm_sized_buffer_reset(tsb); + + pgs.u.req.state_flags = cpu_to_be32(PTM_STATE_FLAG_DECRYPTED); + pgs.u.req.type = cpu_to_be32(type); + pgs.u.req.offset = 0; + + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB, + &pgs, sizeof(pgs.u.req), + offsetof(ptm_getstate, u.resp.data)) < 0) { + error_report("tpm-emulator: could not get state blob type %d : %s", + type, strerror(errno)); + return -1; + } + + res = be32_to_cpu(pgs.u.resp.tpm_result); + if (res != 0 && (res & 0x800) == 0) { + error_report("tpm-emulator: Getting the stateblob (type %d) failed " + "with a TPM error 0x%x", type, res); + return -1; + } + + totlength = be32_to_cpu(pgs.u.resp.totlength); + length = be32_to_cpu(pgs.u.resp.length); + if (totlength != length) { + error_report("tpm-emulator: Expecting to read %u bytes " + "but would get %u", totlength, length); + return -1; + } + + *flags = be32_to_cpu(pgs.u.resp.state_flags); + + if (totlength > 0) { + tsb->buffer = g_try_malloc(totlength); + if (!tsb->buffer) { + error_report("tpm-emulator: Out of memory allocating %u bytes", + totlength); + return -1; + } + + n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, tsb->buffer, totlength); + if (n != totlength) { + error_report("tpm-emulator: Could not read stateblob (type %d); " + "expected %u bytes, got %zd", + type, totlength, n); + return -1; + } + } + tsb->size = totlength; + + trace_tpm_emulator_get_state_blob(type, tsb->size, *flags); + + return 0; +} + +static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu) +{ + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; + + if (tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, + &state_blobs->permanent, + &state_blobs->permanent_flags) < 0 || + tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, + &state_blobs->volatil, + &state_blobs->volatil_flags) < 0 || + tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, + &state_blobs->savestate, + &state_blobs->savestate_flags) < 0) { + goto err_exit; + } + + return 0; + + err_exit: + tpm_sized_buffer_reset(&state_blobs->volatil); + tpm_sized_buffer_reset(&state_blobs->permanent); + tpm_sized_buffer_reset(&state_blobs->savestate); + + return -1; +} + +/* + * Transfer a TPM state blob to the TPM emulator. + * + * @tpm_emu: TPMEmulator + * @type: the type of TPM state blob to transfer + * @tsb: TPMSizedBuffer containing the TPM state blob + * @flags: Flags describing the (encryption) state of the TPM state blob + */ +static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, + uint32_t type, + TPMSizedBuffer *tsb, + uint32_t flags) +{ + ssize_t n; + ptm_setstate pss; + ptm_res tpm_result; + + if (tsb->size == 0) { + return 0; + } + + pss = (ptm_setstate) { + .u.req.state_flags = cpu_to_be32(flags), + .u.req.type = cpu_to_be32(type), + .u.req.length = cpu_to_be32(tsb->size), + }; + + /* write the header only */ + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, + offsetof(ptm_setstate, u.req.data), 0) < 0) { + error_report("tpm-emulator: could not set state blob type %d : %s", + type, strerror(errno)); + return -1; + } + + /* now the body */ + n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size); + if (n != tsb->size) { + error_report("tpm-emulator: Writing the stateblob (type %d) " + "failed; could not write %u bytes, but only %zd", + type, tsb->size, n); + return -1; + } + + /* now get the result */ + n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, + (uint8_t *)&pss, sizeof(pss.u.resp)); + if (n != sizeof(pss.u.resp)) { + error_report("tpm-emulator: Reading response from writing stateblob " + "(type %d) failed; expected %zu bytes, got %zd", type, + sizeof(pss.u.resp), n); + return -1; + } + + tpm_result = be32_to_cpu(pss.u.resp.tpm_result); + if (tpm_result != 0) { + error_report("tpm-emulator: Setting the stateblob (type %d) failed " + "with a TPM error 0x%x", type, tpm_result); + return -1; + } + + trace_tpm_emulator_set_state_blob(type, tsb->size, flags); + + return 0; +} + +/* + * Set all the TPM state blobs. + * + * Returns a negative errno code in case of error. + */ +static int tpm_emulator_set_state_blobs(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; + + trace_tpm_emulator_set_state_blobs(); + + if (tpm_emulator_stop_tpm(tb) < 0) { + trace_tpm_emulator_set_state_blobs_error("Could not stop TPM"); + return -EIO; + } + + if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, + &state_blobs->permanent, + state_blobs->permanent_flags) < 0 || + tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, + &state_blobs->volatil, + state_blobs->volatil_flags) < 0 || + tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, + &state_blobs->savestate, + state_blobs->savestate_flags) < 0) { + return -EIO; + } + + trace_tpm_emulator_set_state_blobs_done(); + + return 0; +} + +static int tpm_emulator_pre_save(void *opaque) +{ + TPMBackend *tb = opaque; + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + + trace_tpm_emulator_pre_save(); + + tpm_backend_finish_sync(tb); + + /* get the state blobs from the TPM */ + return tpm_emulator_get_state_blobs(tpm_emu); +} + +/* + * Load the TPM state blobs into the TPM. + * + * Returns negative errno codes in case of error. + */ +static int tpm_emulator_post_load(void *opaque, int version_id) +{ + TPMBackend *tb = opaque; + int ret; + + ret = tpm_emulator_set_state_blobs(tb); + if (ret < 0) { + return ret; + } + + if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) { + return -EIO; + } + + return 0; +} + +static const VMStateDescription vmstate_tpm_emulator = { + .name = "tpm-emulator", + .version_id = 0, + .pre_save = tpm_emulator_pre_save, + .post_load = tpm_emulator_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer, + TPMEmulator, 0, 0, + state_blobs.permanent.size), + + VMSTATE_UINT32(state_blobs.volatil_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.volatil.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.volatil.buffer, + TPMEmulator, 0, 0, + state_blobs.volatil.size), + + VMSTATE_UINT32(state_blobs.savestate_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.savestate.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.savestate.buffer, + TPMEmulator, 0, 0, + state_blobs.savestate.size), + + VMSTATE_END_OF_LIST() + } +}; + static void tpm_emulator_inst_init(Object *obj) { TPMEmulator *tpm_emu = TPM_EMULATOR(obj); @@ -579,6 +871,8 @@ static void tpm_emulator_inst_init(Object *obj) tpm_emu->options = g_new0(TPMEmulatorOptions, 1); tpm_emu->cur_locty_number = ~0; qemu_mutex_init(&tpm_emu->mutex); + + vmstate_register(NULL, -1, &vmstate_tpm_emulator, obj); } /* @@ -600,6 +894,7 @@ static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) static void tpm_emulator_inst_finalize(Object *obj) { TPMEmulator *tpm_emu = TPM_EMULATOR(obj); + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; tpm_emulator_shutdown(tpm_emu); @@ -614,7 +909,13 @@ static void tpm_emulator_inst_finalize(Object *obj) error_free(tpm_emu->migration_blocker); } + tpm_sized_buffer_reset(&state_blobs->volatil); + tpm_sized_buffer_reset(&state_blobs->permanent); + tpm_sized_buffer_reset(&state_blobs->savestate); + qemu_mutex_destroy(&tpm_emu->mutex); + + vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); } static void tpm_emulator_class_init(ObjectClass *klass, void *data) diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events index 9a65384088..c5bfbf1d4b 100644 --- a/hw/tpm/trace-events +++ b/hw/tpm/trace-events @@ -20,13 +20,19 @@ tpm_emulator_set_locality(uint8_t locty) "setting locality to %d" tpm_emulator_handle_request(void) "processing TPM command" tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64 tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u" -tpm_emulator_startup_tpm(void) "startup" +tpm_emulator_startup_tpm_resume(bool is_resume, size_t buffersize) "is_resume: %d, buffer size: %zu" tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d" tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD" tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2" tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2" tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified" tpm_emulator_handle_device_opts_startup_error(void) "Startup error" +tpm_emulator_get_state_blob(uint8_t type, uint32_t size, uint32_t flags) "got state blob type %d, %u bytes, flags 0x%08x" +tpm_emulator_set_state_blob(uint8_t type, uint32_t size, uint32_t flags) "set state blob type %d, %u bytes, flags 0x%08x" +tpm_emulator_set_state_blobs(void) "setting state blobs" +tpm_emulator_set_state_blobs_error(const char *msg) "error while setting state blobs: %s" +tpm_emulator_set_state_blobs_done(void) "Done setting state blobs" +tpm_emulator_pre_save(void) "" tpm_emulator_inst_init(void) "" # hw/tpm/tpm_tis.c From 9ec08c485e3f97d26dd47868582c1ae1b959c182 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 11 Oct 2017 10:36:53 -0400 Subject: [PATCH 0744/2380] tpm: extend TPM TIS with state migration support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the TPM TIS interface with state migration support. We need to synchronize with the backend thread to make sure that a command being processed by the external TPM emulator has completed and its response been received. Signed-off-by: Stefan Berger Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Marc-André Lureau --- hw/tpm/tpm_tis.c | 52 +++++++++++++++++++++++++++++++++++++++++++-- hw/tpm/trace-events | 1 + 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index 2ac7e74307..12f5c9a759 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -894,9 +894,57 @@ static void tpm_tis_reset(DeviceState *dev) tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size); } +/* persistent state handling */ + +static int tpm_tis_pre_save(void *opaque) +{ + TPMState *s = opaque; + uint8_t locty = s->active_locty; + + trace_tpm_tis_pre_save(locty, s->rw_offset); + + if (DEBUG_TIS) { + tpm_tis_dump_state(opaque, 0); + } + + /* + * Synchronize with backend completion. + */ + tpm_backend_finish_sync(s->be_driver); + + return 0; +} + +static const VMStateDescription vmstate_locty = { + .name = "tpm-tis/locty", + .version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(state, TPMLocality), + VMSTATE_UINT32(inte, TPMLocality), + VMSTATE_UINT32(ints, TPMLocality), + VMSTATE_UINT8(access, TPMLocality), + VMSTATE_UINT32(sts, TPMLocality), + VMSTATE_UINT32(iface_id, TPMLocality), + VMSTATE_END_OF_LIST(), + } +}; + static const VMStateDescription vmstate_tpm_tis = { - .name = "tpm", - .unmigratable = 1, + .name = "tpm-tis", + .version_id = 0, + .pre_save = tpm_tis_pre_save, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(buffer, TPMState), + VMSTATE_UINT16(rw_offset, TPMState), + VMSTATE_UINT8(active_locty, TPMState), + VMSTATE_UINT8(aborting_locty, TPMState), + VMSTATE_UINT8(next_locty, TPMState), + + VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0, + vmstate_locty, TPMLocality), + + VMSTATE_END_OF_LIST() + } }; static Property tpm_tis_properties[] = { diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events index c5bfbf1d4b..25bee0cecf 100644 --- a/hw/tpm/trace-events +++ b/hw/tpm/trace-events @@ -50,3 +50,4 @@ tpm_tis_mmio_write_locty_seized(uint8_t locty, uint8_t active) "Locality %d seiz tpm_tis_mmio_write_init_abort(void) "Initiating abort" tpm_tis_mmio_write_lowering_irq(void) "Lowering IRQ" tpm_tis_mmio_write_data2send(uint32_t value, unsigned size) "Data to send to TPM: 0x%08x (size=%d)" +tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u" From 9d1f0985a77429c6d527077a44b28d43f5906112 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 5 Mar 2018 17:10:01 -0500 Subject: [PATCH 0745/2380] docs: tpm: add VM save/restore example and troubleshooting guide Extend the docs related to TPM with specs related to VM save and restore and a troubleshooting guide for TPM migration. Signed-off-by: Stefan Berger Reviewed-by: Dr. David Alan Gilbert --- docs/specs/tpm.txt | 106 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt index d1d71571e9..c230c4c93e 100644 --- a/docs/specs/tpm.txt +++ b/docs/specs/tpm.txt @@ -200,3 +200,109 @@ crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 PCR-00: 35 4E 3B CE 23 9F 38 59 ... ... PCR-23: 00 00 00 00 00 00 00 00 ... + + +=== Migration with the TPM emulator === + +The TPM emulator supports the following types of virtual machine migration: + +- VM save / restore (migration into a file) +- Network migration +- Snapshotting (migration into storage like QoW2 or QED) + +The following command sequences can be used to test VM save / restore. + + +In a 1st terminal start an instance of a swtpm using the following command: + +mkdir /tmp/mytpm1 +swtpm socket --tpmstate dir=/tmp/mytpm1 \ + --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ + --log level=20 --tpm2 + +In a 2nd terminal start the VM: + +qemu-system-x86_64 -display sdl -enable-kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -monitor stdio \ + test.img + +Verify that the attached TPM is working as expected using applications inside +the VM. + +To store the state of the VM use the following command in the QEMU monitor in +the 2nd terminal: + +(qemu) migrate "exec:cat > testvm.bin" +(qemu) quit + +At this point a file called 'testvm.bin' should exists and the swtpm and QEMU +processes should have ended. + +To test 'VM restore' you have to start the swtpm with the same parameters +as before. If previously a TPM 2 [--tpm2] was saved, --tpm2 must now be +passed again on the command line. + +In the 1st terminal restart the swtpm with the same command line as before: + +swtpm socket --tpmstate dir=/tmp/mytpm1 \ + --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ + --log level=20 --tpm2 + +In the 2nd terminal restore the state of the VM using the additonal +'-incoming' option. + +qemu-system-x86_64 -display sdl -enable-kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -incoming "exec:cat < testvm.bin" \ + test.img + + +Troubleshooting migration: + +There are several reasons why migration may fail. In case of problems, +please ensure that the command lines adhere to the following rules and, +if possible, that identical versions of QEMU and swtpm are used at all +times. + +VM save and restore: + - QEMU command line parameters should be identical apart from the + '-incoming' option on VM restore + - swtpm command line parameters should be identical + +VM migration to 'localhost': + - QEMU command line parameters should be identical apart from the + '-incoming' option on the destination side + - swtpm command line parameters should point to two different + directories on the source and destination swtpm (--tpmstate dir=...) + (especially if different versions of libtpms were to be used on the + same machine). + +VM migration across the network: + - QEMU command line parameters should be identical apart from the + '-incoming' option on the destination side + - swtpm command line parameters should be identical + +VM Snapshotting: + - QEMU command line parameters should be identical + - swtpm command line parameters should be identical + + +Besides that, migration failure reasons on the swtpm level may include +the following: + + - the versions of the swtpm on the source and destination sides are + incompatible + - downgrading of TPM state may not be supported + - the source and destination libtpms were compiled with different + compile-time options and the destination side refuses to accept the + state + - different migration keys are used on the source and destination side + and the destination side cannot decrypt the migrated state + (swtpm ... --migration-key ... ) From 988a27754bbbc45698f7acb54352e5a1ae699514 Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Thu, 24 May 2018 18:33:31 +0800 Subject: [PATCH 0746/2380] vhost: allow backends to filter memory sections This patch introduces a vhost op for vhost backends to allow them to filter the memory sections that they can handle. Signed-off-by: Tiwei Bie Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-user.c | 11 +++++++++++ hw/virtio/vhost.c | 9 +++++++-- include/hw/virtio/vhost-backend.h | 4 ++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index ca554d4ff1..da0756effe 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1620,6 +1620,16 @@ vhost_user_crypto_close_session(struct vhost_dev *dev, uint64_t session_id) return 0; } +static bool vhost_user_mem_section_filter(struct vhost_dev *dev, + MemoryRegionSection *section) +{ + bool result; + + result = memory_region_get_fd(section->mr) >= 0; + + return result; +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, .vhost_backend_init = vhost_user_init, @@ -1650,4 +1660,5 @@ const VhostOps user_ops = { .vhost_set_config = vhost_user_set_config, .vhost_crypto_create_session = vhost_user_crypto_create_session, .vhost_crypto_close_session = vhost_user_crypto_close_session, + .vhost_backend_mem_section_filter = vhost_user_mem_section_filter, }; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index b08290036d..624ade9682 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -386,7 +386,7 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, return r; } -static bool vhost_section(MemoryRegionSection *section) +static bool vhost_section(struct vhost_dev *dev, MemoryRegionSection *section) { bool result; bool log_dirty = memory_region_get_dirty_log_mask(section->mr) & @@ -399,6 +399,11 @@ static bool vhost_section(MemoryRegionSection *section) */ result &= !log_dirty; + if (result && dev->vhost_ops->vhost_backend_mem_section_filter) { + result &= + dev->vhost_ops->vhost_backend_mem_section_filter(dev, section); + } + trace_vhost_section(section->mr->name, result); return result; } @@ -632,7 +637,7 @@ static void vhost_region_addnop(MemoryListener *listener, struct vhost_dev *dev = container_of(listener, struct vhost_dev, memory_listener); - if (!vhost_section(section)) { + if (!vhost_section(dev, section)) { return; } vhost_region_add_section(dev, section); diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 5dac61f9ea..81283ec50f 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -101,6 +101,9 @@ typedef int (*vhost_crypto_create_session_op)(struct vhost_dev *dev, typedef int (*vhost_crypto_close_session_op)(struct vhost_dev *dev, uint64_t session_id); +typedef bool (*vhost_backend_mem_section_filter_op)(struct vhost_dev *dev, + MemoryRegionSection *section); + typedef struct VhostOps { VhostBackendType backend_type; vhost_backend_init vhost_backend_init; @@ -138,6 +141,7 @@ typedef struct VhostOps { vhost_set_config_op vhost_set_config; vhost_crypto_create_session_op vhost_crypto_create_session; vhost_crypto_close_session_op vhost_crypto_close_session; + vhost_backend_mem_section_filter_op vhost_backend_mem_section_filter; } VhostOps; extern const VhostOps user_ops; From 5f57fbeaaf7c4cd33152d7f2e449caab4d4209d9 Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Thu, 24 May 2018 18:33:32 +0800 Subject: [PATCH 0747/2380] vhost-user: allow slave to send fds via slave channel Introduce VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD protocol feature to allow slave to send at most 8 descriptors in each message to master via ancillary data using the slave channel. Suggested-by: Michael S. Tsirkin Signed-off-by: Tiwei Bie Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/interop/vhost-user.txt | 5 +++++ hw/virtio/vhost-user.c | 27 +++++++++++++++++---------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt index 534caab18a..682a683eb4 100644 --- a/docs/interop/vhost-user.txt +++ b/docs/interop/vhost-user.txt @@ -367,6 +367,10 @@ The fd is provided via VHOST_USER_SET_SLAVE_REQ_FD ancillary data. A slave may then send VHOST_USER_SLAVE_* messages to the master using this fd communication channel. +If VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD protocol feature is negotiated, +slave can send file descriptors (at most 8 descriptors in each message) +to master via ancillary data using this fd communication channel. + Protocol features ----------------- @@ -380,6 +384,7 @@ Protocol features #define VHOST_USER_PROTOCOL_F_CRYPTO_SESSION 7 #define VHOST_USER_PROTOCOL_F_PAGEFAULT 8 #define VHOST_USER_PROTOCOL_F_CONFIG 9 +#define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10 Master message types -------------------- diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index da0756effe..75a3faef2a 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -30,6 +30,7 @@ #define VHOST_MEMORY_MAX_NREGIONS 8 #define VHOST_USER_F_PROTOCOL_FEATURES 30 +#define VHOST_USER_SLAVE_MAX_FDS 8 /* * Maximum size of virtio device config space @@ -47,6 +48,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7, VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, VHOST_USER_PROTOCOL_F_CONFIG = 9, + VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, VHOST_USER_PROTOCOL_F_MAX }; @@ -854,10 +856,10 @@ static void slave_read(void *opaque) int size, ret = 0; struct iovec iov; struct msghdr msgh; - int fd = -1; + int fd[VHOST_USER_SLAVE_MAX_FDS]; char control[CMSG_SPACE(sizeof(fd))]; struct cmsghdr *cmsg; - size_t fdsize; + int i, fdsize = 0; memset(&msgh, 0, sizeof(msgh)); msgh.msg_iov = &iov; @@ -865,6 +867,8 @@ static void slave_read(void *opaque) msgh.msg_control = control; msgh.msg_controllen = sizeof(control); + memset(fd, -1, sizeof(fd)); + /* Read header */ iov.iov_base = &hdr; iov.iov_len = VHOST_USER_HDR_SIZE; @@ -885,7 +889,7 @@ static void slave_read(void *opaque) if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { fdsize = cmsg->cmsg_len - CMSG_LEN(0); - memcpy(&fd, CMSG_DATA(cmsg), fdsize); + memcpy(fd, CMSG_DATA(cmsg), fdsize); break; } } @@ -913,14 +917,15 @@ static void slave_read(void *opaque) break; default: error_report("Received unexpected msg type."); - if (fd != -1) { - close(fd); - } ret = -EINVAL; } - /* Message handlers need to make sure that fd will be consumed. */ - fd = -1; + /* Close the remaining file descriptors. */ + for (i = 0; i < fdsize; i++) { + if (fd[i] != -1) { + close(fd[i]); + } + } /* * REPLY_ACK feature handling. Other reply types has to be managed @@ -954,8 +959,10 @@ err: qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); close(u->slave_fd); u->slave_fd = -1; - if (fd != -1) { - close(fd); + for (i = 0; i < fdsize; i++) { + if (fd[i] != -1) { + close(fd[i]); + } } return; } From 4d0cf552d3a9585f380e8abdc313e4d416a56aa0 Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Thu, 24 May 2018 18:33:33 +0800 Subject: [PATCH 0748/2380] vhost-user: introduce shared vhost-user state When multi queue is enabled e.g. for a virtio-net device, each queue pair will have a vhost_dev, and the only thing shared between vhost devs currently is the chardev. This patch introduces a vhost-user state structure which will be shared by all vhost devs of the same virtio device. Signed-off-by: Tiwei Bie Signed-off-by: Michael S. Tsirkin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- backends/cryptodev-vhost-user.c | 20 ++++++++++++- hw/block/vhost-user-blk.c | 22 ++++++++++++++- hw/scsi/vhost-user-scsi.c | 20 ++++++++++++- hw/virtio/Makefile.objs | 2 +- hw/virtio/vhost-stub.c | 10 +++++++ hw/virtio/vhost-user.c | 31 ++++++++++++++------ include/hw/virtio/vhost-user-blk.h | 2 ++ include/hw/virtio/vhost-user-scsi.h | 2 ++ include/hw/virtio/vhost-user.h | 20 +++++++++++++ net/vhost-user.c | 44 +++++++++++++++++++++++------ 10 files changed, 152 insertions(+), 21 deletions(-) create mode 100644 include/hw/virtio/vhost-user.h diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index 862d4f2580..d52daccfcd 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" +#include "hw/virtio/vhost-user.h" #include "standard-headers/linux/virtio_crypto.h" #include "sysemu/cryptodev-vhost.h" #include "chardev/char-fe.h" @@ -46,6 +47,7 @@ typedef struct CryptoDevBackendVhostUser { CryptoDevBackend parent_obj; + VhostUserState *vhost_user; CharBackend chr; char *chr_name; bool opened; @@ -102,7 +104,7 @@ cryptodev_vhost_user_start(int queues, continue; } - options.opaque = &s->chr; + options.opaque = s->vhost_user; options.backend_type = VHOST_BACKEND_TYPE_USER; options.cc = b->conf.peers.ccs[i]; s->vhost_crypto[i] = cryptodev_vhost_init(&options); @@ -185,6 +187,7 @@ static void cryptodev_vhost_user_init( size_t i; Error *local_err = NULL; Chardev *chr; + VhostUserState *user; CryptoDevBackendClient *cc; CryptoDevBackendVhostUser *s = CRYPTODEV_BACKEND_VHOST_USER(backend); @@ -215,6 +218,15 @@ static void cryptodev_vhost_user_init( } } + user = vhost_user_init(); + if (!user) { + error_setg(errp, "Failed to init vhost_user"); + return; + } + + user->chr = &s->chr; + s->vhost_user = user; + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, cryptodev_vhost_user_event, NULL, s, NULL, true); @@ -299,6 +311,12 @@ static void cryptodev_vhost_user_cleanup( backend->conf.peers.ccs[i] = NULL; } } + + if (s->vhost_user) { + vhost_user_cleanup(s->vhost_user); + g_free(s->vhost_user); + s->vhost_user = NULL; + } } static void cryptodev_vhost_user_set_chardev(Object *obj, diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 975eae6211..7c3fa8bb1c 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -226,6 +226,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserBlk *s = VHOST_USER_BLK(vdev); + VhostUserState *user; int i, ret; if (!s->chardev.chr) { @@ -243,6 +244,15 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) return; } + user = vhost_user_init(); + if (!user) { + error_setg(errp, "vhost-user-blk: failed to init vhost_user"); + return; + } + + user->chr = &s->chardev; + s->vhost_user = user; + virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config)); @@ -258,7 +268,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) vhost_dev_set_config_notifier(&s->dev, &blk_ops); - ret = vhost_dev_init(&s->dev, &s->chardev, VHOST_BACKEND_TYPE_USER, 0); + ret = vhost_dev_init(&s->dev, s->vhost_user, VHOST_BACKEND_TYPE_USER, 0); if (ret < 0) { error_setg(errp, "vhost-user-blk: vhost initialization failed: %s", strerror(-ret)); @@ -283,6 +293,10 @@ vhost_err: virtio_err: g_free(s->dev.vqs); virtio_cleanup(vdev); + + vhost_user_cleanup(user); + g_free(user); + s->vhost_user = NULL; } static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) @@ -294,6 +308,12 @@ static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) vhost_dev_cleanup(&s->dev); g_free(s->dev.vqs); virtio_cleanup(vdev); + + if (s->vhost_user) { + vhost_user_cleanup(s->vhost_user); + g_free(s->vhost_user); + s->vhost_user = NULL; + } } static void vhost_user_blk_instance_init(Object *obj) diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 9389ed48e0..9355cfdf07 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -69,6 +69,7 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); VHostUserSCSI *s = VHOST_USER_SCSI(dev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + VhostUserState *user; Error *err = NULL; int ret; @@ -85,19 +86,30 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) return; } + user = vhost_user_init(); + if (!user) { + error_setg(errp, "vhost-user-scsi: failed to init vhost_user"); + return; + } + user->chr = &vs->conf.chardev; + vsc->dev.nvqs = 2 + vs->conf.num_queues; vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs); vsc->dev.vq_index = 0; vsc->dev.backend_features = 0; - ret = vhost_dev_init(&vsc->dev, (void *)&vs->conf.chardev, + ret = vhost_dev_init(&vsc->dev, user, VHOST_BACKEND_TYPE_USER, 0); if (ret < 0) { error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s", strerror(-ret)); + vhost_user_cleanup(user); + g_free(user); return; } + s->vhost_user = user; + /* Channel and lun both are 0 for bootable vhost-user-scsi disk */ vsc->channel = 0; vsc->lun = 0; @@ -117,6 +129,12 @@ static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp) g_free(vsc->dev.vqs); virtio_scsi_common_unrealize(dev, errp); + + if (s->vhost_user) { + vhost_user_cleanup(s->vhost_user); + g_free(s->vhost_user); + s->vhost_user = NULL; + } } static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev, diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index 765d363c1f..030969e28c 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -11,5 +11,5 @@ obj-y += virtio-crypto.o obj-$(CONFIG_VIRTIO_PCI) += virtio-crypto-pci.o endif -common-obj-$(call lnot,$(CONFIG_LINUX)) += vhost-stub.o +common-obj-$(call lnot,$(call land,$(CONFIG_VIRTIO),$(CONFIG_LINUX))) += vhost-stub.o common-obj-$(CONFIG_ALL) += vhost-stub.o diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c index 2d76cdebdc..049089b5e2 100644 --- a/hw/virtio/vhost-stub.c +++ b/hw/virtio/vhost-stub.c @@ -1,7 +1,17 @@ #include "qemu/osdep.h" #include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" bool vhost_has_free_slot(void) { return true; } + +VhostUserState *vhost_user_init(void) +{ + return NULL; +} + +void vhost_user_cleanup(VhostUserState *user) +{ +} diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 75a3faef2a..70b9610c87 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio-net.h" #include "chardev/char-fe.h" @@ -175,7 +176,8 @@ static VhostUserMsg m __attribute__ ((unused)); struct vhost_user { struct vhost_dev *dev; - CharBackend *chr; + /* Shared between vhost devs of the same virtio device */ + VhostUserState *user; int slave_fd; NotifierWithReturn postcopy_notifier; struct PostCopyFD postcopy_fd; @@ -201,7 +203,7 @@ static bool ioeventfd_enabled(void) static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) { struct vhost_user *u = dev->opaque; - CharBackend *chr = u->chr; + CharBackend *chr = u->user->chr; uint8_t *p = (uint8_t *) msg; int r, size = VHOST_USER_HDR_SIZE; @@ -287,7 +289,7 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, int *fds, int fd_num) { struct vhost_user *u = dev->opaque; - CharBackend *chr = u->chr; + CharBackend *chr = u->user->chr; int ret, size = VHOST_USER_HDR_SIZE + msg->hdr.size; /* @@ -1090,7 +1092,7 @@ static int vhost_user_postcopy_waker(struct PostCopyFD *pcfd, RAMBlock *rb, static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) { struct vhost_user *u = dev->opaque; - CharBackend *chr = u->chr; + CharBackend *chr = u->user->chr; int ufd; VhostUserMsg msg = { .hdr.request = VHOST_USER_POSTCOPY_ADVISE, @@ -1228,7 +1230,7 @@ static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier, return 0; } -static int vhost_user_init(struct vhost_dev *dev, void *opaque) +static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque) { uint64_t features, protocol_features; struct vhost_user *u; @@ -1237,7 +1239,7 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); u = g_new0(struct vhost_user, 1); - u->chr = opaque; + u->user = opaque; u->slave_fd = -1; u->dev = dev; dev->opaque = u; @@ -1313,7 +1315,7 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) return 0; } -static int vhost_user_cleanup(struct vhost_dev *dev) +static int vhost_user_backend_cleanup(struct vhost_dev *dev) { struct vhost_user *u; @@ -1637,10 +1639,21 @@ static bool vhost_user_mem_section_filter(struct vhost_dev *dev, return result; } +VhostUserState *vhost_user_init(void) +{ + VhostUserState *user = g_new0(struct VhostUserState, 1); + + return user; +} + +void vhost_user_cleanup(VhostUserState *user) +{ +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, - .vhost_backend_init = vhost_user_init, - .vhost_backend_cleanup = vhost_user_cleanup, + .vhost_backend_init = vhost_user_backend_init, + .vhost_backend_cleanup = vhost_user_backend_cleanup, .vhost_backend_memslots_limit = vhost_user_memslots_limit, .vhost_set_log_base = vhost_user_set_log_base, .vhost_set_mem_table = vhost_user_set_mem_table, diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h index 5804cc904a..f1258ae545 100644 --- a/include/hw/virtio/vhost-user-blk.h +++ b/include/hw/virtio/vhost-user-blk.h @@ -21,6 +21,7 @@ #include "hw/block/block.h" #include "chardev/char-fe.h" #include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" #define TYPE_VHOST_USER_BLK "vhost-user-blk" #define VHOST_USER_BLK(obj) \ @@ -36,6 +37,7 @@ typedef struct VHostUserBlk { uint32_t config_wce; uint32_t config_ro; struct vhost_dev dev; + VhostUserState *vhost_user; } VHostUserBlk; #endif diff --git a/include/hw/virtio/vhost-user-scsi.h b/include/hw/virtio/vhost-user-scsi.h index 01861f78d0..3ec34ae867 100644 --- a/include/hw/virtio/vhost-user-scsi.h +++ b/include/hw/virtio/vhost-user-scsi.h @@ -21,6 +21,7 @@ #include "hw/qdev.h" #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" #include "hw/virtio/vhost-scsi-common.h" #define TYPE_VHOST_USER_SCSI "vhost-user-scsi" @@ -30,6 +31,7 @@ typedef struct VHostUserSCSI { VHostSCSICommon parent_obj; uint64_t host_features; + VhostUserState *vhost_user; } VHostUserSCSI; #endif /* VHOST_USER_SCSI_H */ diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h new file mode 100644 index 0000000000..eb8bc0d90d --- /dev/null +++ b/include/hw/virtio/vhost-user.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2017-2018 Intel Corporation + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_VIRTIO_VHOST_USER_H +#define HW_VIRTIO_VHOST_USER_H + +#include "chardev/char-fe.h" + +typedef struct VhostUserState { + CharBackend *chr; +} VhostUserState; + +VhostUserState *vhost_user_init(void); +void vhost_user_cleanup(VhostUserState *user); + +#endif diff --git a/net/vhost-user.c b/net/vhost-user.c index fa28aad12d..608b837175 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -12,6 +12,7 @@ #include "clients.h" #include "net/vhost_net.h" #include "net/vhost-user.h" +#include "hw/virtio/vhost-user.h" #include "chardev/char-fe.h" #include "qapi/error.h" #include "qapi/qapi-commands-net.h" @@ -23,6 +24,7 @@ typedef struct NetVhostUserState { NetClientState nc; CharBackend chr; /* only queue index 0 */ + VhostUserState *vhost_user; VHostNetState *vhost_net; guint watch; uint64_t acked_features; @@ -64,7 +66,8 @@ static void vhost_user_stop(int queues, NetClientState *ncs[]) } } -static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) +static int vhost_user_start(int queues, NetClientState *ncs[], + VhostUserState *be) { VhostNetOptions options; struct vhost_net *net = NULL; @@ -144,7 +147,7 @@ static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, return size; } -static void vhost_user_cleanup(NetClientState *nc) +static void net_vhost_user_cleanup(NetClientState *nc) { NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); @@ -159,6 +162,11 @@ static void vhost_user_cleanup(NetClientState *nc) s->watch = 0; } qemu_chr_fe_deinit(&s->chr, true); + if (s->vhost_user) { + vhost_user_cleanup(s->vhost_user); + g_free(s->vhost_user); + s->vhost_user = NULL; + } } qemu_purge_queued_packets(nc); @@ -182,7 +190,7 @@ static NetClientInfo net_vhost_user_info = { .type = NET_CLIENT_DRIVER_VHOST_USER, .size = sizeof(NetVhostUserState), .receive = vhost_user_receive, - .cleanup = vhost_user_cleanup, + .cleanup = net_vhost_user_cleanup, .has_vnet_hdr = vhost_user_has_vnet_hdr, .has_ufo = vhost_user_has_ufo, }; @@ -244,7 +252,7 @@ static void net_vhost_user_event(void *opaque, int event) trace_vhost_user_event(chr->label, event); switch (event) { case CHR_EVENT_OPENED: - if (vhost_user_start(queues, ncs, &s->chr) < 0) { + if (vhost_user_start(queues, ncs, s->vhost_user) < 0) { qemu_chr_fe_disconnect(&s->chr); return; } @@ -283,12 +291,19 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, { Error *err = NULL; NetClientState *nc, *nc0 = NULL; - NetVhostUserState *s; + VhostUserState *user = NULL; + NetVhostUserState *s = NULL; int i; assert(name); assert(queues > 0); + user = vhost_user_init(); + if (!user) { + error_report("failed to init vhost_user"); + goto err; + } + for (i = 0; i < queues; i++) { nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s", @@ -299,17 +314,19 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, s = DO_UPCAST(NetVhostUserState, nc, nc); if (!qemu_chr_fe_init(&s->chr, chr, &err)) { error_report_err(err); - return -1; + goto err; } + user->chr = &s->chr; } - + s = DO_UPCAST(NetVhostUserState, nc, nc); + s->vhost_user = user; } s = DO_UPCAST(NetVhostUserState, nc, nc0); do { if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) { error_report_err(err); - return -1; + goto err; } qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, NULL, nc0->name, NULL, @@ -319,6 +336,17 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, assert(s->vhost_net); return 0; + +err: + if (user) { + vhost_user_cleanup(user); + g_free(user); + if (s) { + s->vhost_user = NULL; + } + } + + return -1; } static Chardev *net_vhost_claim_chardev( From 44866521bd6ea8e5152a87664dea1eee90c9438b Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Thu, 24 May 2018 18:33:34 +0800 Subject: [PATCH 0749/2380] vhost-user: support registering external host notifiers This patch introduces VHOST_USER_PROTOCOL_F_HOST_NOTIFIER. With this feature negotiated, vhost-user backend can register memory region based host notifiers. And it will allow the guest driver in the VM to notify the hardware accelerator at the vhost-user backend directly. Signed-off-by: Tiwei Bie Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/interop/vhost-user.txt | 33 ++++++++++ hw/virtio/vhost-user.c | 113 +++++++++++++++++++++++++++++++++ include/hw/virtio/vhost-user.h | 8 +++ 3 files changed, 154 insertions(+) diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt index 682a683eb4..d51fd58242 100644 --- a/docs/interop/vhost-user.txt +++ b/docs/interop/vhost-user.txt @@ -132,6 +132,16 @@ Depending on the request type, payload can be: Payload: Size bytes array holding the contents of the virtio device's configuration space + * Vring area description + ----------------------- + | u64 | size | offset | + ----------------------- + + u64: a 64-bit integer contains vring index and flags + Size: a 64-bit size of this area + Offset: a 64-bit offset of this area from the start of the + supplied file descriptor + In QEMU the vhost-user message is implemented with the following struct: typedef struct VhostUserMsg { @@ -146,6 +156,7 @@ typedef struct VhostUserMsg { VhostUserLog log; struct vhost_iotlb_msg iotlb; VhostUserConfig config; + VhostUserVringArea area; }; } QEMU_PACKED VhostUserMsg; @@ -385,6 +396,7 @@ Protocol features #define VHOST_USER_PROTOCOL_F_PAGEFAULT 8 #define VHOST_USER_PROTOCOL_F_CONFIG 9 #define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10 +#define VHOST_USER_PROTOCOL_F_HOST_NOTIFIER 11 Master message types -------------------- @@ -782,6 +794,27 @@ Slave message types the VHOST_USER_NEED_REPLY flag, master must respond with zero when operation is successfully completed, or non-zero otherwise. + * VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG + + Id: 3 + Equivalent ioctl: N/A + Slave payload: vring area description + Master payload: N/A + + Sets host notifier for a specified queue. The queue index is contained + in the u64 field of the vring area description. The host notifier is + described by the file descriptor (typically it's a VFIO device fd) which + is passed as ancillary data and the size (which is mmap size and should + be the same as host page size) and offset (which is mmap offset) carried + in the vring area description. QEMU can mmap the file descriptor based + on the size and offset to get a memory range. Registering a host notifier + means mapping this memory range to the VM as the specified queue's notify + MMIO region. Slave sends this request to tell QEMU to de-register the + existing notifier if any and register the new notifier if the request is + sent with a file descriptor. + This request should be sent only when VHOST_USER_PROTOCOL_F_HOST_NOTIFIER + protocol feature has been successfully negotiated. + VHOST_USER_PROTOCOL_F_REPLY_ACK: ------------------------------- The original vhost-user specification only demands replies for certain diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 70b9610c87..b041343632 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -13,6 +13,7 @@ #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user.h" #include "hw/virtio/vhost-backend.h" +#include "hw/virtio/virtio.h" #include "hw/virtio/virtio-net.h" #include "chardev/char-fe.h" #include "sysemu/kvm.h" @@ -50,6 +51,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, VHOST_USER_PROTOCOL_F_CONFIG = 9, VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, + VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11, VHOST_USER_PROTOCOL_F_MAX }; @@ -94,6 +96,7 @@ typedef enum VhostUserSlaveRequest { VHOST_USER_SLAVE_NONE = 0, VHOST_USER_SLAVE_IOTLB_MSG = 1, VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2, + VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3, VHOST_USER_SLAVE_MAX } VhostUserSlaveRequest; @@ -138,6 +141,12 @@ static VhostUserConfig c __attribute__ ((unused)); + sizeof(c.size) \ + sizeof(c.flags)) +typedef struct VhostUserVringArea { + uint64_t u64; + uint64_t size; + uint64_t offset; +} VhostUserVringArea; + typedef struct { VhostUserRequest request; @@ -159,6 +168,7 @@ typedef union { struct vhost_iotlb_msg iotlb; VhostUserConfig config; VhostUserCryptoSession session; + VhostUserVringArea area; } VhostUserPayload; typedef struct VhostUserMsg { @@ -640,9 +650,37 @@ static int vhost_user_set_vring_num(struct vhost_dev *dev, return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring); } +static void vhost_user_host_notifier_restore(struct vhost_dev *dev, + int queue_idx) +{ + struct vhost_user *u = dev->opaque; + VhostUserHostNotifier *n = &u->user->notifier[queue_idx]; + VirtIODevice *vdev = dev->vdev; + + if (n->addr && !n->set) { + virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true); + n->set = true; + } +} + +static void vhost_user_host_notifier_remove(struct vhost_dev *dev, + int queue_idx) +{ + struct vhost_user *u = dev->opaque; + VhostUserHostNotifier *n = &u->user->notifier[queue_idx]; + VirtIODevice *vdev = dev->vdev; + + if (n->addr && n->set) { + virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false); + n->set = false; + } +} + static int vhost_user_set_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { + vhost_user_host_notifier_restore(dev, ring->index); + return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring); } @@ -676,6 +714,8 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev, .hdr.size = sizeof(msg.payload.state), }; + vhost_user_host_notifier_remove(dev, ring->index); + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { return -1; } @@ -849,6 +889,66 @@ static int vhost_user_slave_handle_config_change(struct vhost_dev *dev) return ret; } +static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, + VhostUserVringArea *area, + int fd) +{ + int queue_idx = area->u64 & VHOST_USER_VRING_IDX_MASK; + size_t page_size = qemu_real_host_page_size; + struct vhost_user *u = dev->opaque; + VhostUserState *user = u->user; + VirtIODevice *vdev = dev->vdev; + VhostUserHostNotifier *n; + void *addr; + char *name; + + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_HOST_NOTIFIER) || + vdev == NULL || queue_idx >= virtio_get_num_queues(vdev)) { + return -1; + } + + n = &user->notifier[queue_idx]; + + if (n->addr) { + virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false); + object_unparent(OBJECT(&n->mr)); + munmap(n->addr, page_size); + n->addr = NULL; + } + + if (area->u64 & VHOST_USER_VRING_NOFD_MASK) { + return 0; + } + + /* Sanity check. */ + if (area->size != page_size) { + return -1; + } + + addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, area->offset); + if (addr == MAP_FAILED) { + return -1; + } + + name = g_strdup_printf("vhost-user/host-notifier@%p mmaps[%d]", + user, queue_idx); + memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name, + page_size, addr); + g_free(name); + + if (virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true)) { + munmap(addr, page_size); + return -1; + } + + n->addr = addr; + n->set = true; + + return 0; +} + static void slave_read(void *opaque) { struct vhost_dev *dev = opaque; @@ -917,6 +1017,10 @@ static void slave_read(void *opaque) case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG : ret = vhost_user_slave_handle_config_change(dev); break; + case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG: + ret = vhost_user_slave_handle_vring_host_notifier(dev, &payload.area, + fd[0]); + break; default: error_report("Received unexpected msg type."); ret = -EINVAL; @@ -1648,6 +1752,15 @@ VhostUserState *vhost_user_init(void) void vhost_user_cleanup(VhostUserState *user) { + int i; + + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + if (user->notifier[i].addr) { + object_unparent(OBJECT(&user->notifier[i].mr)); + munmap(user->notifier[i].addr, qemu_real_host_page_size); + user->notifier[i].addr = NULL; + } + } } const VhostOps user_ops = { diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h index eb8bc0d90d..fd660393a0 100644 --- a/include/hw/virtio/vhost-user.h +++ b/include/hw/virtio/vhost-user.h @@ -9,9 +9,17 @@ #define HW_VIRTIO_VHOST_USER_H #include "chardev/char-fe.h" +#include "hw/virtio/virtio.h" + +typedef struct VhostUserHostNotifier { + MemoryRegion mr; + void *addr; + bool set; +} VhostUserHostNotifier; typedef struct VhostUserState { CharBackend *chr; + VhostUserHostNotifier notifier[VIRTIO_QUEUE_MAX]; } VhostUserState; VhostUserState *vhost_user_init(void); From d84599f56c820d8c1ac9928a76500dcdfbbf194d Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Thu, 24 May 2018 18:33:35 +0800 Subject: [PATCH 0750/2380] libvhost-user: support host notifier This patch introduces the host notifier support in libvhost-user. A new API is added to support setting host notifier for each queue. Signed-off-by: Tiwei Bie Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- contrib/libvhost-user/libvhost-user.c | 81 ++++++++++++++++++++++++--- contrib/libvhost-user/libvhost-user.h | 32 +++++++++++ 2 files changed, 105 insertions(+), 8 deletions(-) diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c index 54e643d871..a6b46cdc03 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -314,11 +314,6 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) msg.msg_controllen = 0; } - /* Set the version in the flags when sending the reply */ - vmsg->flags &= ~VHOST_USER_VERSION_MASK; - vmsg->flags |= VHOST_USER_VERSION; - vmsg->flags |= VHOST_USER_REPLY_MASK; - do { rc = sendmsg(conn_fd, &msg, 0); } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); @@ -341,6 +336,39 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) return true; } +static bool +vu_send_reply(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) +{ + /* Set the version in the flags when sending the reply */ + vmsg->flags &= ~VHOST_USER_VERSION_MASK; + vmsg->flags |= VHOST_USER_VERSION; + vmsg->flags |= VHOST_USER_REPLY_MASK; + + return vu_message_write(dev, conn_fd, vmsg); +} + +static bool +vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg) +{ + VhostUserMsg msg_reply; + + if ((vmsg->flags & VHOST_USER_NEED_REPLY_MASK) == 0) { + return true; + } + + if (!vu_message_read(dev, dev->slave_fd, &msg_reply)) { + return false; + } + + if (msg_reply.request != vmsg->request) { + DPRINT("Received unexpected msg type. Expected %d received %d", + vmsg->request, msg_reply.request); + return false; + } + + return msg_reply.payload.u64 == 0; +} + /* Kick the log_call_fd if required. */ static void vu_log_kick(VuDev *dev) @@ -536,7 +564,7 @@ vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg) /* Send the message back to qemu with the addresses filled in */ vmsg->fd_num = 0; - if (!vu_message_write(dev, dev->sock, vmsg)) { + if (!vu_send_reply(dev, dev->sock, vmsg)) { vu_panic(dev, "failed to respond to set-mem-table for postcopy"); return false; } @@ -916,6 +944,41 @@ void vu_set_queue_handler(VuDev *dev, VuVirtq *vq, } } +bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, + int size, int offset) +{ + int qidx = vq - dev->vq; + int fd_num = 0; + VhostUserMsg vmsg = { + .request = VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG, + .flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK, + .size = sizeof(vmsg.payload.area), + .payload.area = { + .u64 = qidx & VHOST_USER_VRING_IDX_MASK, + .size = size, + .offset = offset, + }, + }; + + if (fd == -1) { + vmsg.payload.area.u64 |= VHOST_USER_VRING_NOFD_MASK; + } else { + vmsg.fds[fd_num++] = fd; + } + + vmsg.fd_num = fd_num; + + if ((dev->protocol_features & VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD) == 0) { + return false; + } + + if (!vu_message_write(dev, dev->slave_fd, &vmsg)) { + return false; + } + + return vu_process_message_reply(dev, &vmsg); +} + static bool vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg) { @@ -968,7 +1031,9 @@ static bool vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) { uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD | - 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ; + 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ | + 1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER | + 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD; if (have_userfault()) { features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT; @@ -1252,7 +1317,7 @@ vu_dispatch(VuDev *dev) goto end; } - if (!vu_message_write(dev, dev->sock, &vmsg)) { + if (!vu_send_reply(dev, dev->sock, &vmsg)) { goto end; } diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index b27075ea3b..4aa55b4d2d 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -51,6 +51,8 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7, VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, VHOST_USER_PROTOCOL_F_CONFIG = 9, + VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, + VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11, VHOST_USER_PROTOCOL_F_MAX }; @@ -92,6 +94,14 @@ typedef enum VhostUserRequest { VHOST_USER_MAX } VhostUserRequest; +typedef enum VhostUserSlaveRequest { + VHOST_USER_SLAVE_NONE = 0, + VHOST_USER_SLAVE_IOTLB_MSG = 1, + VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2, + VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3, + VHOST_USER_SLAVE_MAX +} VhostUserSlaveRequest; + typedef struct VhostUserMemoryRegion { uint64_t guest_phys_addr; uint64_t memory_size; @@ -122,6 +132,12 @@ static VhostUserConfig c __attribute__ ((unused)); + sizeof(c.size) \ + sizeof(c.flags)) +typedef struct VhostUserVringArea { + uint64_t u64; + uint64_t size; + uint64_t offset; +} VhostUserVringArea; + #if defined(_WIN32) # define VU_PACKED __attribute__((gcc_struct, packed)) #else @@ -133,6 +149,7 @@ typedef struct VhostUserMsg { #define VHOST_USER_VERSION_MASK (0x3) #define VHOST_USER_REPLY_MASK (0x1 << 2) +#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) uint32_t flags; uint32_t size; /* the following payload size */ @@ -145,6 +162,7 @@ typedef struct VhostUserMsg { VhostUserMemory memory; VhostUserLog log; VhostUserConfig config; + VhostUserVringArea area; } payload; int fds[VHOST_MEMORY_MAX_NREGIONS]; @@ -368,6 +386,20 @@ VuVirtq *vu_get_queue(VuDev *dev, int qidx); void vu_set_queue_handler(VuDev *dev, VuVirtq *vq, vu_queue_handler_cb handler); +/** + * vu_set_queue_host_notifier: + * @dev: a VuDev context + * @vq: a VuVirtq queue + * @fd: a file descriptor + * @size: host page size + * @offset: notifier offset in @fd file + * + * Set queue's host notifier. This function may be called several + * times for the same queue. If called with -1 @fd, the notifier + * is removed. + */ +bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, + int size, int offset); /** * vu_queue_set_notification: From e3af2928f8270498097af163bb023b633c0d8d1c Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Thu, 24 May 2018 18:33:36 +0800 Subject: [PATCH 0751/2380] vhost-user-bridge: support host notifier This patch introduces the host notifier support in vhost-user-bridge. A new option (-H) is added to use the host notifier. This is mainly used to test the host notifier implementation in vhost user. Signed-off-by: Tiwei Bie Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/vhost-user-bridge.c | 98 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c index e0605a529e..0884294141 100644 --- a/tests/vhost-user-bridge.c +++ b/tests/vhost-user-bridge.c @@ -29,6 +29,7 @@ #define _FILE_OFFSET_BITS 64 +#include "qemu/atomic.h" #include "qemu/osdep.h" #include "qemu/iov.h" #include "standard-headers/linux/virtio_net.h" @@ -65,6 +66,11 @@ typedef struct VubrDev { int sock; int ready; int quit; + struct { + int fd; + void *addr; + pthread_t thread; + } notifier; } VubrDev; static void @@ -445,14 +451,22 @@ static uint64_t vubr_get_features(VuDev *dev) { return 1ULL << VIRTIO_NET_F_GUEST_ANNOUNCE | - 1ULL << VIRTIO_NET_F_MRG_RXBUF; + 1ULL << VIRTIO_NET_F_MRG_RXBUF | + 1ULL << VIRTIO_F_VERSION_1; } static void vubr_queue_set_started(VuDev *dev, int qidx, bool started) { + VubrDev *vubr = container_of(dev, VubrDev, vudev); VuVirtq *vq = vu_get_queue(dev, qidx); + if (started && vubr->notifier.fd >= 0) { + vu_set_queue_host_notifier(dev, vq, vubr->notifier.fd, + getpagesize(), + qidx * getpagesize()); + } + if (qidx % 2 == 1) { vu_set_queue_handler(dev, vq, started ? vubr_handle_tx : NULL); } @@ -522,6 +536,8 @@ vubr_new(const char *path, bool client) vubr_die("socket"); } + dev->notifier.fd = -1; + un.sun_family = AF_UNIX; strcpy(un.sun_path, path); len = sizeof(un.sun_family) + strlen(path); @@ -559,6 +575,73 @@ vubr_new(const char *path, bool client) return dev; } +static void *notifier_thread(void *arg) +{ + VuDev *dev = (VuDev *)arg; + VubrDev *vubr = container_of(dev, VubrDev, vudev); + int pagesize = getpagesize(); + int qidx; + + while (true) { + for (qidx = 0; qidx < VHOST_MAX_NR_VIRTQUEUE; qidx++) { + uint16_t *n = vubr->notifier.addr + pagesize * qidx; + + if (*n == qidx) { + *n = 0xffff; + /* We won't miss notifications if we reset + * the memory first. */ + smp_mb(); + + DPRINT("Got a notification for queue%d via host notifier.\n", + qidx); + + if (qidx % 2 == 1) { + vubr_handle_tx(dev, qidx); + } + } + usleep(1000); + } + } + + return NULL; +} + +static void +vubr_host_notifier_setup(VubrDev *dev) +{ + char template[] = "/tmp/vubr-XXXXXX"; + pthread_t thread; + size_t length; + void *addr; + int fd; + + length = getpagesize() * VHOST_MAX_NR_VIRTQUEUE; + + fd = mkstemp(template); + if (fd < 0) { + vubr_die("mkstemp()"); + } + + if (posix_fallocate(fd, 0, length) != 0) { + vubr_die("posix_fallocate()"); + } + + addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + vubr_die("mmap()"); + } + + memset(addr, 0xff, length); + + if (pthread_create(&thread, NULL, notifier_thread, &dev->vudev) != 0) { + vubr_die("pthread_create()"); + } + + dev->notifier.fd = fd; + dev->notifier.addr = addr; + dev->notifier.thread = thread; +} + static void vubr_set_host(struct sockaddr_in *saddr, const char *host) { @@ -673,8 +756,9 @@ main(int argc, char *argv[]) VubrDev *dev; int opt; bool client = false; + bool host_notifier = false; - while ((opt = getopt(argc, argv, "l:r:u:c")) != -1) { + while ((opt = getopt(argc, argv, "l:r:u:cH")) != -1) { switch (opt) { case 'l': @@ -693,6 +777,9 @@ main(int argc, char *argv[]) case 'c': client = true; break; + case 'H': + host_notifier = true; + break; default: goto out; } @@ -708,6 +795,10 @@ main(int argc, char *argv[]) return 1; } + if (host_notifier) { + vubr_host_notifier_setup(dev); + } + vubr_backend_udp_setup(dev, lhost, lport, rhost, rport); vubr_run(dev); @@ -717,7 +808,7 @@ main(int argc, char *argv[]) out: fprintf(stderr, "Usage: %s ", argv[0]); - fprintf(stderr, "[-c] [-u ud_socket_path] [-l lhost:lport] [-r rhost:rport]\n"); + fprintf(stderr, "[-c] [-H] [-u ud_socket_path] [-l lhost:lport] [-r rhost:rport]\n"); fprintf(stderr, "\t-u path to unix doman socket. default: %s\n", DEFAULT_UD_SOCKET); fprintf(stderr, "\t-l local host and port. default: %s:%s\n", @@ -725,6 +816,7 @@ out: fprintf(stderr, "\t-r remote host and port. default: %s:%s\n", DEFAULT_RHOST, DEFAULT_RPORT); fprintf(stderr, "\t-c client mode\n"); + fprintf(stderr, "\t-H use host notifier\n"); return 1; } From 28012e190e6897cfc2a98364240909d08e90ffc0 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 24 May 2018 21:09:52 +0300 Subject: [PATCH 0752/2380] osdep: add wait.h compat macros Man page for WCOREDUMP says: WCOREDUMP(wstatus) returns true if the child produced a core dump. This macro should be employed only if WIFSIGNALED returned true. This macro is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS). Therefore, enclose its use inside #ifdef WCOREDUMP ... #endif. Let's do exactly this. Signed-off-by: Michael S. Tsirkin --- include/qemu/osdep.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 41658060a7..afc28e5903 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -107,6 +107,16 @@ extern int daemon(int, int); #include "glib-compat.h" #include "qemu/typedefs.h" +/* + * According to waitpid man page: + * WCOREDUMP + * This macro is not specified in POSIX.1-2001 and is not + * available on some UNIX implementations (e.g., AIX, SunOS). + * Therefore, enclose its use inside #ifdef WCOREDUMP ... #endif. + */ +#ifndef WCOREDUMP +#define WCOREDUMP(status) 0 +#endif /* * We have a lot of unaudited code that may fail in strange ways, or * even be a security risk during migration, if you disable assertions From 75578d6fcedc55b85b44dfc83f506b74c32b7395 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 May 2018 18:46:16 +0100 Subject: [PATCH 0753/2380] linux-user: Assert on bad type in thunk_type_align() and thunk_type_size() In thunk_type_align() and thunk_type_size() we currently return -1 if the value at the type_ptr isn't one of the TYPE_* values we understand. However, this should never happen, and if it does then the calling code will go confusingly wrong because none of the callsites try to handle an error return. Switch to an assertion instead, so that if this does somehow happen we'll have a nice clear backtrace of what happened rather than a weird crash or misbehaviour. This also silences various Coverity complaints about not handling the negative return value (CID 1005735, 1005736, 1005738, 1390582). Signed-off-by: Peter Maydell Reviewed-by: Laurent Vivier Message-Id: <20180514174616.19601-1-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- include/exec/user/thunk.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h index 8f55b233b3..8d3af5a3be 100644 --- a/include/exec/user/thunk.h +++ b/include/exec/user/thunk.h @@ -135,7 +135,7 @@ static inline int thunk_type_size(const argtype *type_ptr, int is_host) se = struct_entries + type_ptr[1]; return se->size[is_host]; default: - return -1; + g_assert_not_reached(); } } @@ -185,7 +185,7 @@ static inline int thunk_type_align(const argtype *type_ptr, int is_host) se = struct_entries + type_ptr[1]; return se->align[is_host]; default: - return -1; + g_assert_not_reached(); } } From c1e703f55814fe0fb95b6f0e5a1eff571455d08f Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 May 2018 00:12:13 +0200 Subject: [PATCH 0754/2380] linux-user: update netlink emulation Update enums with entries from linux 4.17 Translate entries that generate logs with iproute2 4.9.0 and host kernel 4.15: # ip address show Unknown host QEMU_IFLA type: 43 Unknown host QEMU_IFLA type: 43 Unknown host QEMU_IFLA type: 43 Unknown QEMU_IFLA_BR type 41 Unknown QEMU_IFLA_BR type 42 Unknown QEMU_IFLA_BR type 43 Unknown QEMU_IFLA_BR type 44 Unknown host QEMU_IFLA type: 43 Unknown QEMU_IFLA_BR type 41 Unknown QEMU_IFLA_BR type 42 Unknown QEMU_IFLA_BR type 43 Unknown QEMU_IFLA_BR type 44 Unknown host QEMU_IFLA type: 43 Signed-off-by: Laurent Vivier Message-Id: <20180516221213.11111-1-laurent@vivier.eu> --- linux-user/syscall.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index af8603f1b7..dd77f86ea2 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -405,6 +405,8 @@ enum { QEMU_IFLA_BR_PAD, QEMU_IFLA_BR_VLAN_STATS_ENABLED, QEMU_IFLA_BR_MCAST_STATS_ENABLED, + QEMU_IFLA_BR_MCAST_IGMP_VERSION, + QEMU_IFLA_BR_MCAST_MLD_VERSION, QEMU___IFLA_BR_MAX, }; @@ -453,6 +455,12 @@ enum { QEMU_IFLA_GSO_MAX_SIZE, QEMU_IFLA_PAD, QEMU_IFLA_XDP, + QEMU_IFLA_EVENT, + QEMU_IFLA_NEW_NETNSID, + QEMU_IFLA_IF_NETNSID, + QEMU_IFLA_CARRIER_UP_COUNT, + QEMU_IFLA_CARRIER_DOWN_COUNT, + QEMU_IFLA_NEW_IFINDEX, QEMU___IFLA_MAX }; @@ -484,6 +492,12 @@ enum { QEMU_IFLA_BRPORT_FLUSH, QEMU_IFLA_BRPORT_MULTICAST_ROUTER, QEMU_IFLA_BRPORT_PAD, + QEMU_IFLA_BRPORT_MCAST_FLOOD, + QEMU_IFLA_BRPORT_MCAST_TO_UCAST, + QEMU_IFLA_BRPORT_VLAN_TUNNEL, + QEMU_IFLA_BRPORT_BCAST_FLOOD, + QEMU_IFLA_BRPORT_GROUP_FWD_MASK, + QEMU_IFLA_BRPORT_NEIGH_SUPPRESS, QEMU___IFLA_BRPORT_MAX }; @@ -516,6 +530,15 @@ enum { QEMU___IFLA_INET6_MAX }; +enum { + QEMU_IFLA_XDP_UNSPEC, + QEMU_IFLA_XDP_FD, + QEMU_IFLA_XDP_ATTACHED, + QEMU_IFLA_XDP_FLAGS, + QEMU_IFLA_XDP_PROG_ID, + QEMU___IFLA_XDP_MAX, +}; + typedef abi_long (*TargetFdDataFunc)(void *, size_t); typedef abi_long (*TargetFdAddrFunc)(void *, abi_ulong, socklen_t); typedef struct TargetFdTrans { @@ -2182,6 +2205,10 @@ static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BR_NF_CALL_IPTABLES: case QEMU_IFLA_BR_NF_CALL_IP6TABLES: case QEMU_IFLA_BR_NF_CALL_ARPTABLES: + case QEMU_IFLA_BR_VLAN_STATS_ENABLED: + case QEMU_IFLA_BR_MCAST_STATS_ENABLED: + case QEMU_IFLA_BR_MCAST_IGMP_VERSION: + case QEMU_IFLA_BR_MCAST_MLD_VERSION: break; /* uint16_t */ case QEMU_IFLA_BR_PRIORITY: @@ -2253,6 +2280,11 @@ static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: case QEMU_IFLA_BRPORT_CONFIG_PENDING: case QEMU_IFLA_BRPORT_MULTICAST_ROUTER: + case QEMU_IFLA_BRPORT_MCAST_FLOOD: + case QEMU_IFLA_BRPORT_MCAST_TO_UCAST: + case QEMU_IFLA_BRPORT_VLAN_TUNNEL: + case QEMU_IFLA_BRPORT_BCAST_FLOOD: + case QEMU_IFLA_BRPORT_NEIGH_SUPPRESS: break; /* uint16_t */ case QEMU_IFLA_BRPORT_PRIORITY: @@ -2260,6 +2292,7 @@ static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BRPORT_DESIGNATED_COST: case QEMU_IFLA_BRPORT_ID: case QEMU_IFLA_BRPORT_NO: + case QEMU_IFLA_BRPORT_GROUP_FWD_MASK: u16 = NLA_DATA(nlattr); *u16 = tswap16(*u16); break; @@ -2434,6 +2467,27 @@ static abi_long host_to_target_data_spec_nlattr(struct nlattr *nlattr, return 0; } +static abi_long host_to_target_data_xdp_nlattr(struct nlattr *nlattr, + void *context) +{ + uint32_t *u32; + + switch (nlattr->nla_type) { + /* uint8_t */ + case QEMU_IFLA_XDP_ATTACHED: + break; + /* uint32_t */ + case QEMU_IFLA_XDP_PROG_ID: + u32 = NLA_DATA(nlattr); + *u32 = tswap32(*u32); + break; + default: + gemu_log("Unknown host XDP type: %d\n", nlattr->nla_type); + break; + } + return 0; +} + static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) { uint32_t *u32; @@ -2559,6 +2613,10 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, NULL, host_to_target_data_spec_nlattr); + case QEMU_IFLA_XDP: + return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, + NULL, + host_to_target_data_xdp_nlattr); default: gemu_log("Unknown host QEMU_IFLA type: %d\n", rtattr->rta_type); break; From 2b5249b85cd9daaca3ea0a9ecab81c3e41cf9f55 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 17 May 2018 13:51:17 +0200 Subject: [PATCH 0755/2380] linux-user: update comments to point to tcg_exec_init() cpu_init() was replaced by cpu_create() since 2.12 but comments weren't updated. So update stale comments to point that page sizes arei actually initialized by tcg_exec_init(). Also move another qemu_host_page_size related comment before tcg_exec_init() where it belongs. Signed-off-by: Igor Mammedov Reviewed-by: Laurent Vivier Message-Id: <1526557877-293151-1-git-send-email-imammedo@redhat.com> Signed-off-by: Laurent Vivier --- bsd-user/main.c | 7 ++++--- linux-user/main.c | 5 ++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 283dc6fd25..da3b833975 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -898,9 +898,10 @@ int main(int argc, char **argv) cpu_model = "any"; #endif } + + /* init tcg before creating CPUs and to get qemu_host_page_size */ tcg_exec_init(0); - /* NOTE: we need to init the CPU at this stage to get - qemu_host_page_size */ + cpu_type = parse_cpu_model(cpu_model); cpu = cpu_create(cpu_type); env = cpu->env_ptr; @@ -917,7 +918,7 @@ int main(int argc, char **argv) envlist_free(envlist); /* - * Now that page sizes are configured in cpu_init() we can do + * Now that page sizes are configured in tcg_exec_init() we can do * proper page alignment for guest_base. */ guest_base = HOST_PAGE_ALIGN(guest_base); diff --git a/linux-user/main.c b/linux-user/main.c index 32347545c9..78d6d3e7eb 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -671,9 +671,8 @@ int main(int argc, char **argv, char **envp) } cpu_type = parse_cpu_model(cpu_model); + /* init tcg before creating CPUs and to get qemu_host_page_size */ tcg_exec_init(0); - /* NOTE: we need to init the CPU at this stage to get - qemu_host_page_size */ cpu = cpu_create(cpu_type); env = cpu->env_ptr; @@ -693,7 +692,7 @@ int main(int argc, char **argv, char **envp) envlist_free(envlist); /* - * Now that page sizes are configured in cpu_init() we can do + * Now that page sizes are configured in tcg_exec_init() we can do * proper page alignment for guest_base. */ guest_base = HOST_PAGE_ALIGN(guest_base); From 309786cfd8f701182eee845fd98e30fd5addd046 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 May 2018 19:47:15 +0100 Subject: [PATCH 0756/2380] linux-user: Fix payload size logic in host_to_target_cmsg() Coverity points out that there's a missing break in the switch in host_to_target_cmsg() where we update tgt_len for cmsg_level/cmsg_type combinations which require a different length for host and target (CID 1385425). To avoid duplicating the default case (target length same as host) in both switches, set that before the switch so that only the cases which want to override it need any code. This fixes a bug where we would have used the wrong length for SOL_SOCKET/SO_TIMESTAMP messages where the target and host have differently sized 'struct timeval' (ie one is 32 bit and the other is 64 bit). Signed-off-by: Peter Maydell Reviewed-by: Laurent Vivier Message-Id: <20180518184715.29833-1-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index dd77f86ea2..d02c16bbc6 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1848,6 +1848,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, /* Payload types which need a different size of payload on * the target must adjust tgt_len here. */ + tgt_len = len; switch (cmsg->cmsg_level) { case SOL_SOCKET: switch (cmsg->cmsg_type) { @@ -1857,8 +1858,8 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, default: break; } + break; default: - tgt_len = len; break; } From 5de33b105d148446a9a7415707358cc8ca465cdf Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 19 May 2018 11:29:49 +0200 Subject: [PATCH 0757/2380] linux-user: move mips socket.h definitions to mips/sockbits.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180519092956.15134-2-laurent@vivier.eu> --- linux-user/mips/sockbits.h | 110 +++++++++++++++++++++++++++++++++++ linux-user/mips64/sockbits.h | 1 + linux-user/socket.h | 106 +-------------------------------- 3 files changed, 113 insertions(+), 104 deletions(-) create mode 100644 linux-user/mips/sockbits.h create mode 100644 linux-user/mips64/sockbits.h diff --git a/linux-user/mips/sockbits.h b/linux-user/mips/sockbits.h new file mode 100644 index 0000000000..3fe5ac88e7 --- /dev/null +++ b/linux-user/mips/sockbits.h @@ -0,0 +1,110 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef MIPS_SOCKBITS_H +#define MIPS_SOCKBITS_H +/* MIPS special values for constants */ + +/* + * For setsockopt(2) + * + * This defines are ABI conformant as far as Linux supports these ... + */ +#define TARGET_SOL_SOCKET 0xffff + +#define TARGET_SO_DEBUG 0x0001 /* Record debugging information. */ +#define TARGET_SO_REUSEADDR 0x0004 /* Allow reuse of local addresses. */ +#define TARGET_SO_KEEPALIVE 0x0008 /* Keep connections alive and send + SIGPIPE when they die. */ +#define TARGET_SO_DONTROUTE 0x0010 /* Don't do local routing. */ +#define TARGET_SO_BROADCAST 0x0020 /* Allow transmission of + broadcast messages. */ +#define TARGET_SO_LINGER 0x0080 /* Block on close of a reliable + * socket to transmit pending data. + */ +#define TARGET_SO_OOBINLINE 0x0100 /* Receive out-of-band data in-band. + */ +#if 0 +/* To add: Allow local address and port reuse. */ +#define TARGET_SO_REUSEPORT 0x0200 +#endif + +#define TARGET_SO_TYPE 0x1008 /* Compatible name for SO_STYLE. */ +#define TARGET_SO_STYLE SO_TYPE /* Synonym */ +#define TARGET_SO_ERROR 0x1007 /* get error status and clear */ +#define TARGET_SO_SNDBUF 0x1001 /* Send buffer size. */ +#define TARGET_SO_RCVBUF 0x1002 /* Receive buffer. */ +#define TARGET_SO_SNDLOWAT 0x1003 /* send low-water mark */ +#define TARGET_SO_RCVLOWAT 0x1004 /* receive low-water mark */ +#define TARGET_SO_SNDTIMEO 0x1005 /* send timeout */ +#define TARGET_SO_RCVTIMEO 0x1006 /* receive timeout */ +#define TARGET_SO_ACCEPTCONN 0x1009 + +/* linux-specific, might as well be the same as on i386 */ +#define TARGET_SO_NO_CHECK 11 +#define TARGET_SO_PRIORITY 12 +#define TARGET_SO_BSDCOMPAT 14 + +#define TARGET_SO_PASSCRED 17 +#define TARGET_SO_PEERCRED 18 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 22 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define TARGET_SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define TARGET_SO_ATTACH_FILTER 26 +#define TARGET_SO_DETACH_FILTER 27 + +#define TARGET_SO_PEERNAME 28 +#define TARGET_SO_TIMESTAMP 29 +#define SCM_TIMESTAMP SO_TIMESTAMP + +#define TARGET_SO_PEERSEC 30 +#define TARGET_SO_SNDBUFFORCE 31 +#define TARGET_SO_RCVBUFFORCE 33 +#define TARGET_SO_PASSSEC 34 + +/** sock_type - Socket types + * + * Please notice that for binary compat reasons MIPS has to + * override the enum sock_type in include/linux/net.h, so + * we define ARCH_HAS_SOCKET_TYPES here. + * + * @SOCK_DGRAM - datagram (conn.less) socket + * @SOCK_STREAM - stream (connection) socket + * @SOCK_RAW - raw socket + * @SOCK_RDM - reliably-delivered message + * @SOCK_SEQPACKET - sequential packet socket + * @SOCK_DCCP - Datagram Congestion Control Protocol socket + * @SOCK_PACKET - linux specific way of getting packets at the dev level. + * For writing rarp and other similar things on the user + * level. + * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. + * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. + */ + +#define ARCH_HAS_SOCKET_TYPES 1 + +enum sock_type { + TARGET_SOCK_DGRAM = 1, + TARGET_SOCK_STREAM = 2, + TARGET_SOCK_RAW = 3, + TARGET_SOCK_RDM = 4, + TARGET_SOCK_SEQPACKET = 5, + TARGET_SOCK_DCCP = 6, + TARGET_SOCK_PACKET = 10, + TARGET_SOCK_CLOEXEC = 02000000, + TARGET_SOCK_NONBLOCK = 0200, +}; + +#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) +#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ +#endif diff --git a/linux-user/mips64/sockbits.h b/linux-user/mips64/sockbits.h new file mode 100644 index 0000000000..e6b6d31ac9 --- /dev/null +++ b/linux-user/mips64/sockbits.h @@ -0,0 +1 @@ +#include "../mips/sockbits.h" diff --git a/linux-user/socket.h b/linux-user/socket.h index 7051cd2cf4..9c57da7a61 100644 --- a/linux-user/socket.h +++ b/linux-user/socket.h @@ -1,106 +1,6 @@ -#if defined(TARGET_MIPS) - /* MIPS special values for constants */ - - /* - * For setsockopt(2) - * - * This defines are ABI conformant as far as Linux supports these ... - */ - #define TARGET_SOL_SOCKET 0xffff - - #define TARGET_SO_DEBUG 0x0001 /* Record debugging information. */ - #define TARGET_SO_REUSEADDR 0x0004 /* Allow reuse of local addresses. */ - #define TARGET_SO_KEEPALIVE 0x0008 /* Keep connections alive and send - SIGPIPE when they die. */ - #define TARGET_SO_DONTROUTE 0x0010 /* Don't do local routing. */ - #define TARGET_SO_BROADCAST 0x0020 /* Allow transmission of - broadcast messages. */ - #define TARGET_SO_LINGER 0x0080 /* Block on close of a reliable - * socket to transmit pending data. - */ - #define TARGET_SO_OOBINLINE 0x0100 /* Receive out-of-band data in-band. - */ - #if 0 - /* To add: Allow local address and port reuse. */ - #define TARGET_SO_REUSEPORT 0x0200 - #endif - - #define TARGET_SO_TYPE 0x1008 /* Compatible name for SO_STYLE. */ - #define TARGET_SO_STYLE SO_TYPE /* Synonym */ - #define TARGET_SO_ERROR 0x1007 /* get error status and clear */ - #define TARGET_SO_SNDBUF 0x1001 /* Send buffer size. */ - #define TARGET_SO_RCVBUF 0x1002 /* Receive buffer. */ - #define TARGET_SO_SNDLOWAT 0x1003 /* send low-water mark */ - #define TARGET_SO_RCVLOWAT 0x1004 /* receive low-water mark */ - #define TARGET_SO_SNDTIMEO 0x1005 /* send timeout */ - #define TARGET_SO_RCVTIMEO 0x1006 /* receive timeout */ - #define TARGET_SO_ACCEPTCONN 0x1009 - - /* linux-specific, might as well be the same as on i386 */ - #define TARGET_SO_NO_CHECK 11 - #define TARGET_SO_PRIORITY 12 - #define TARGET_SO_BSDCOMPAT 14 - - #define TARGET_SO_PASSCRED 17 - #define TARGET_SO_PEERCRED 18 - - /* Security levels - as per NRL IPv6 - don't actually do anything */ - #define TARGET_SO_SECURITY_AUTHENTICATION 22 - #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 - #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 - - #define TARGET_SO_BINDTODEVICE 25 - - /* Socket filtering */ - #define TARGET_SO_ATTACH_FILTER 26 - #define TARGET_SO_DETACH_FILTER 27 - - #define TARGET_SO_PEERNAME 28 - #define TARGET_SO_TIMESTAMP 29 - #define SCM_TIMESTAMP SO_TIMESTAMP - - #define TARGET_SO_PEERSEC 30 - #define TARGET_SO_SNDBUFFORCE 31 - #define TARGET_SO_RCVBUFFORCE 33 - #define TARGET_SO_PASSSEC 34 - - /** sock_type - Socket types - * - * Please notice that for binary compat reasons MIPS has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - - #define ARCH_HAS_SOCKET_TYPES 1 - - enum sock_type { - TARGET_SOCK_DGRAM = 1, - TARGET_SOCK_STREAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 02000000, - TARGET_SOCK_NONBLOCK = 0200, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - +#if defined(TARGET_MIPS) || defined(TARGET_HPPA) +#include "sockbits.h" #elif defined(TARGET_ALPHA) /* For setsockopt(2) */ @@ -205,8 +105,6 @@ #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ -#elif defined(TARGET_HPPA) -#include #else #if defined(TARGET_SPARC) From f24cbd398e412bac3bde0ad4dfea984f7e67884d Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 19 May 2018 11:29:50 +0200 Subject: [PATCH 0758/2380] linux-user: move alpha socket.h definitions to alpha/sockbits.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180519092956.15134-3-laurent@vivier.eu> --- linux-user/alpha/sockbits.h | 113 ++++++++++++++++++++++++++++++++++++ linux-user/socket.h | 106 +-------------------------------- 2 files changed, 114 insertions(+), 105 deletions(-) create mode 100644 linux-user/alpha/sockbits.h diff --git a/linux-user/alpha/sockbits.h b/linux-user/alpha/sockbits.h new file mode 100644 index 0000000000..4db3e52b67 --- /dev/null +++ b/linux-user/alpha/sockbits.h @@ -0,0 +1,113 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef ALPHA_SOCKBITS_H +#define ALPHA_SOCKBITS_H + +/* For setsockopt(2) */ +#define TARGET_SOL_SOCKET 0xffff + +#define TARGET_SO_DEBUG 0x0001 +#define TARGET_SO_REUSEADDR 0x0004 +#define TARGET_SO_KEEPALIVE 0x0008 +#define TARGET_SO_DONTROUTE 0x0010 +#define TARGET_SO_BROADCAST 0x0020 +#define TARGET_SO_LINGER 0x0080 +#define TARGET_SO_OOBINLINE 0x0100 +/* To add :#define TARGET_SO_REUSEPORT 0x0200 */ + +#define TARGET_SO_TYPE 0x1008 +#define TARGET_SO_ERROR 0x1007 +#define TARGET_SO_SNDBUF 0x1001 +#define TARGET_SO_RCVBUF 0x1002 +#define TARGET_SO_SNDBUFFORCE 0x100a +#define TARGET_SO_RCVBUFFORCE 0x100b +#define TARGET_SO_RCVLOWAT 0x1010 +#define TARGET_SO_SNDLOWAT 0x1011 +#define TARGET_SO_RCVTIMEO 0x1012 +#define TARGET_SO_SNDTIMEO 0x1013 +#define TARGET_SO_ACCEPTCONN 0x1014 +#define TARGET_SO_PROTOCOL 0x1028 +#define TARGET_SO_DOMAIN 0x1029 + +/* linux-specific, might as well be the same as on i386 */ +#define TARGET_SO_NO_CHECK 11 +#define TARGET_SO_PRIORITY 12 +#define TARGET_SO_BSDCOMPAT 14 + +#define TARGET_SO_PASSCRED 17 +#define TARGET_SO_PEERCRED 18 +#define TARGET_SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define TARGET_SO_ATTACH_FILTER 26 +#define TARGET_SO_DETACH_FILTER 27 + +#define TARGET_SO_PEERNAME 28 +#define TARGET_SO_TIMESTAMP 29 +#define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP + +#define TARGET_SO_PEERSEC 30 +#define TARGET_SO_PASSSEC 34 +#define TARGET_SO_TIMESTAMPNS 35 +#define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 19 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 20 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 21 + +#define TARGET_SO_MARK 36 + +#define TARGET_SO_TIMESTAMPING 37 +#define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING + +#define TARGET_SO_RXQ_OVFL 40 + +#define TARGET_SO_WIFI_STATUS 41 +#define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS +#define TARGET_SO_PEEK_OFF 42 + +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define TARGET_SO_NOFCS 43 + +/** sock_type - Socket types + * + * Please notice that for binary compat reasons ALPHA has to + * override the enum sock_type in include/linux/net.h, so + * we define ARCH_HAS_SOCKET_TYPES here. + * + * @SOCK_DGRAM - datagram (conn.less) socket + * @SOCK_STREAM - stream (connection) socket + * @SOCK_RAW - raw socket + * @SOCK_RDM - reliably-delivered message + * @SOCK_SEQPACKET - sequential packet socket + * @SOCK_DCCP - Datagram Congestion Control Protocol socket + * @SOCK_PACKET - linux specific way of getting packets at the dev level. + * For writing rarp and other similar things on the user + * level. + * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. + * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. + */ + +#define ARCH_HAS_SOCKET_TYPES 1 + +enum sock_type { + TARGET_SOCK_STREAM = 1, + TARGET_SOCK_DGRAM = 2, + TARGET_SOCK_RAW = 3, + TARGET_SOCK_RDM = 4, + TARGET_SOCK_SEQPACKET = 5, + TARGET_SOCK_DCCP = 6, + TARGET_SOCK_PACKET = 10, + TARGET_SOCK_CLOEXEC = 010000000, + TARGET_SOCK_NONBLOCK = 010000000000, +}; + +#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) +#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ +#endif diff --git a/linux-user/socket.h b/linux-user/socket.h index 9c57da7a61..1328906205 100644 --- a/linux-user/socket.h +++ b/linux-user/socket.h @@ -1,110 +1,6 @@ -#if defined(TARGET_MIPS) || defined(TARGET_HPPA) +#if defined(TARGET_MIPS) || defined(TARGET_HPPA) || defined(TARGET_ALPHA) #include "sockbits.h" -#elif defined(TARGET_ALPHA) - - /* For setsockopt(2) */ - #define TARGET_SOL_SOCKET 0xffff - - #define TARGET_SO_DEBUG 0x0001 - #define TARGET_SO_REUSEADDR 0x0004 - #define TARGET_SO_KEEPALIVE 0x0008 - #define TARGET_SO_DONTROUTE 0x0010 - #define TARGET_SO_BROADCAST 0x0020 - #define TARGET_SO_LINGER 0x0080 - #define TARGET_SO_OOBINLINE 0x0100 - /* To add :#define TARGET_SO_REUSEPORT 0x0200 */ - - #define TARGET_SO_TYPE 0x1008 - #define TARGET_SO_ERROR 0x1007 - #define TARGET_SO_SNDBUF 0x1001 - #define TARGET_SO_RCVBUF 0x1002 - #define TARGET_SO_SNDBUFFORCE 0x100a - #define TARGET_SO_RCVBUFFORCE 0x100b - #define TARGET_SO_RCVLOWAT 0x1010 - #define TARGET_SO_SNDLOWAT 0x1011 - #define TARGET_SO_RCVTIMEO 0x1012 - #define TARGET_SO_SNDTIMEO 0x1013 - #define TARGET_SO_ACCEPTCONN 0x1014 - #define TARGET_SO_PROTOCOL 0x1028 - #define TARGET_SO_DOMAIN 0x1029 - - /* linux-specific, might as well be the same as on i386 */ - #define TARGET_SO_NO_CHECK 11 - #define TARGET_SO_PRIORITY 12 - #define TARGET_SO_BSDCOMPAT 14 - - #define TARGET_SO_PASSCRED 17 - #define TARGET_SO_PEERCRED 18 - #define TARGET_SO_BINDTODEVICE 25 - - /* Socket filtering */ - #define TARGET_SO_ATTACH_FILTER 26 - #define TARGET_SO_DETACH_FILTER 27 - - #define TARGET_SO_PEERNAME 28 - #define TARGET_SO_TIMESTAMP 29 - #define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP - - #define TARGET_SO_PEERSEC 30 - #define TARGET_SO_PASSSEC 34 - #define TARGET_SO_TIMESTAMPNS 35 - #define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS - - /* Security levels - as per NRL IPv6 - don't actually do anything */ - #define TARGET_SO_SECURITY_AUTHENTICATION 19 - #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 20 - #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 21 - - #define TARGET_SO_MARK 36 - - #define TARGET_SO_TIMESTAMPING 37 - #define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING - - #define TARGET_SO_RXQ_OVFL 40 - - #define TARGET_SO_WIFI_STATUS 41 - #define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS - #define TARGET_SO_PEEK_OFF 42 - - /* Instruct lower device to use last 4-bytes of skb data as FCS */ - #define TARGET_SO_NOFCS 43 - - /** sock_type - Socket types - * - * Please notice that for binary compat reasons ALPHA has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - - #define ARCH_HAS_SOCKET_TYPES 1 - - enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 010000000, - TARGET_SOCK_NONBLOCK = 010000000000, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ #else #if defined(TARGET_SPARC) From da84fdaaf8c36e1f92e3780717bfd6df35199292 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 19 May 2018 11:29:51 +0200 Subject: [PATCH 0759/2380] linux-user: move sparc/sparc64 socket.h definitions to sparc/sockbits.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code change. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180519092956.15134-4-laurent@vivier.eu> --- linux-user/socket.h | 44 +--------------- linux-user/sparc/sockbits.h | 94 +++++++++++++++++++++++++++++++++++ linux-user/sparc64/sockbits.h | 1 + 3 files changed, 97 insertions(+), 42 deletions(-) create mode 100644 linux-user/sparc/sockbits.h create mode 100644 linux-user/sparc64/sockbits.h diff --git a/linux-user/socket.h b/linux-user/socket.h index 1328906205..8eb62ea3f2 100644 --- a/linux-user/socket.h +++ b/linux-user/socket.h @@ -1,49 +1,9 @@ -#if defined(TARGET_MIPS) || defined(TARGET_HPPA) || defined(TARGET_ALPHA) +#if defined(TARGET_MIPS) || defined(TARGET_HPPA) || defined(TARGET_ALPHA) || \ + defined(TARGET_SPARC) #include "sockbits.h" -#else - -#if defined(TARGET_SPARC) - /** sock_type - Socket types - * - * Please notice that for binary compat reasons SPARC has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - - #define ARCH_HAS_SOCKET_TYPES 1 - - enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 020000000, - TARGET_SOCK_NONBLOCK = 040000, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - - #define TARGET_SO_PASSSEC 31 #else #define TARGET_SO_PASSSEC 34 -#endif /* For setsockopt(2) */ #define TARGET_SOL_SOCKET 1 diff --git a/linux-user/sparc/sockbits.h b/linux-user/sparc/sockbits.h new file mode 100644 index 0000000000..385061c8b0 --- /dev/null +++ b/linux-user/sparc/sockbits.h @@ -0,0 +1,94 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef SPARC_SOCKBITS_H +#define SPARC_SOCKBITS_H + +/** sock_type - Socket types + * + * Please notice that for binary compat reasons SPARC has to + * override the enum sock_type in include/linux/net.h, so + * we define ARCH_HAS_SOCKET_TYPES here. + * + * @SOCK_DGRAM - datagram (conn.less) socket + * @SOCK_STREAM - stream (connection) socket + * @SOCK_RAW - raw socket + * @SOCK_RDM - reliably-delivered message + * @SOCK_SEQPACKET - sequential packet socket + * @SOCK_DCCP - Datagram Congestion Control Protocol socket + * @SOCK_PACKET - linux specific way of getting packets at the dev level. + * For writing rarp and other similar things on the user + * level. + * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. + * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. + */ + +#define ARCH_HAS_SOCKET_TYPES 1 + +enum sock_type { + TARGET_SOCK_STREAM = 1, + TARGET_SOCK_DGRAM = 2, + TARGET_SOCK_RAW = 3, + TARGET_SOCK_RDM = 4, + TARGET_SOCK_SEQPACKET = 5, + TARGET_SOCK_DCCP = 6, + TARGET_SOCK_PACKET = 10, + TARGET_SOCK_CLOEXEC = 020000000, + TARGET_SOCK_NONBLOCK = 040000, +}; + +#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) +#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ + +#define TARGET_SO_PASSSEC 31 + +/* For setsockopt(2) */ +#define TARGET_SOL_SOCKET 1 + +#define TARGET_SO_DEBUG 1 +#define TARGET_SO_REUSEADDR 2 +#define TARGET_SO_TYPE 3 +#define TARGET_SO_ERROR 4 +#define TARGET_SO_DONTROUTE 5 +#define TARGET_SO_BROADCAST 6 +#define TARGET_SO_SNDBUF 7 +#define TARGET_SO_RCVBUF 8 +#define TARGET_SO_SNDBUFFORCE 32 +#define TARGET_SO_RCVBUFFORCE 33 +#define TARGET_SO_KEEPALIVE 9 +#define TARGET_SO_OOBINLINE 10 +#define TARGET_SO_NO_CHECK 11 +#define TARGET_SO_PRIORITY 12 +#define TARGET_SO_LINGER 13 +#define TARGET_SO_BSDCOMPAT 14 +/* To add :#define TARGET_SO_REUSEPORT 15 */ +#define TARGET_SO_PASSCRED 16 +#define TARGET_SO_PEERCRED 17 +#define TARGET_SO_RCVLOWAT 18 +#define TARGET_SO_SNDLOWAT 19 +#define TARGET_SO_RCVTIMEO 20 +#define TARGET_SO_SNDTIMEO 21 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 22 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define TARGET_SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define TARGET_SO_ATTACH_FILTER 26 +#define TARGET_SO_DETACH_FILTER 27 + +#define TARGET_SO_PEERNAME 28 +#define TARGET_SO_TIMESTAMP 29 +#define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP + +#define TARGET_SO_ACCEPTCONN 30 + +#define TARGET_SO_PEERSEC 31 +#endif diff --git a/linux-user/sparc64/sockbits.h b/linux-user/sparc64/sockbits.h new file mode 100644 index 0000000000..658899e4d3 --- /dev/null +++ b/linux-user/sparc64/sockbits.h @@ -0,0 +1 @@ +#include "../sparc/sockbits.h" From 500fa6076034729de8d4fd45b3d8a6f9d08d7585 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 19 May 2018 11:29:52 +0200 Subject: [PATCH 0760/2380] linux-user: move socket.h generic definitions to generic/sockbits.h and include the file from architectures without specific definitions Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Message-Id: <20180519092956.15134-5-laurent@vivier.eu> --- linux-user/aarch64/sockbits.h | 1 + linux-user/arm/sockbits.h | 1 + linux-user/cris/sockbits.h | 1 + linux-user/generic/sockbits.h | 67 ++++++++++++++++++++++++++++++++ linux-user/i386/sockbits.h | 1 + linux-user/m68k/sockbits.h | 1 + linux-user/microblaze/sockbits.h | 1 + linux-user/nios2/sockbits.h | 1 + linux-user/openrisc/sockbits.h | 1 + linux-user/ppc/sockbits.h | 1 + linux-user/riscv/sockbits.h | 1 + linux-user/s390x/sockbits.h | 1 + linux-user/sh4/sockbits.h | 1 + linux-user/socket.h | 61 ----------------------------- linux-user/tilegx/sockbits.h | 1 + linux-user/x86_64/sockbits.h | 1 + linux-user/xtensa/sockbits.h | 1 + 17 files changed, 82 insertions(+), 61 deletions(-) create mode 100644 linux-user/aarch64/sockbits.h create mode 100644 linux-user/arm/sockbits.h create mode 100644 linux-user/cris/sockbits.h create mode 100644 linux-user/generic/sockbits.h create mode 100644 linux-user/i386/sockbits.h create mode 100644 linux-user/m68k/sockbits.h create mode 100644 linux-user/microblaze/sockbits.h create mode 100644 linux-user/nios2/sockbits.h create mode 100644 linux-user/openrisc/sockbits.h create mode 100644 linux-user/ppc/sockbits.h create mode 100644 linux-user/riscv/sockbits.h create mode 100644 linux-user/s390x/sockbits.h create mode 100644 linux-user/sh4/sockbits.h create mode 100644 linux-user/tilegx/sockbits.h create mode 100644 linux-user/x86_64/sockbits.h create mode 100644 linux-user/xtensa/sockbits.h diff --git a/linux-user/aarch64/sockbits.h b/linux-user/aarch64/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/aarch64/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/arm/sockbits.h b/linux-user/arm/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/arm/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/cris/sockbits.h b/linux-user/cris/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/cris/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/generic/sockbits.h b/linux-user/generic/sockbits.h new file mode 100644 index 0000000000..093faf0a48 --- /dev/null +++ b/linux-user/generic/sockbits.h @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef GENERIC_SOCKBITS_H +#define GENERIC_SOCKBITS_H + +#define TARGET_SO_PASSSEC 34 + +/* For setsockopt(2) */ +#define TARGET_SOL_SOCKET 1 + +#define TARGET_SO_DEBUG 1 +#define TARGET_SO_REUSEADDR 2 +#define TARGET_SO_TYPE 3 +#define TARGET_SO_ERROR 4 +#define TARGET_SO_DONTROUTE 5 +#define TARGET_SO_BROADCAST 6 +#define TARGET_SO_SNDBUF 7 +#define TARGET_SO_RCVBUF 8 +#define TARGET_SO_SNDBUFFORCE 32 +#define TARGET_SO_RCVBUFFORCE 33 +#define TARGET_SO_KEEPALIVE 9 +#define TARGET_SO_OOBINLINE 10 +#define TARGET_SO_NO_CHECK 11 +#define TARGET_SO_PRIORITY 12 +#define TARGET_SO_LINGER 13 +#define TARGET_SO_BSDCOMPAT 14 +/* To add :#define TARGET_SO_REUSEPORT 15 */ +#if defined(TARGET_PPC) +#define TARGET_SO_RCVLOWAT 16 +#define TARGET_SO_SNDLOWAT 17 +#define TARGET_SO_RCVTIMEO 18 +#define TARGET_SO_SNDTIMEO 19 +#define TARGET_SO_PASSCRED 20 +#define TARGET_SO_PEERCRED 21 +#else +#define TARGET_SO_PASSCRED 16 +#define TARGET_SO_PEERCRED 17 +#define TARGET_SO_RCVLOWAT 18 +#define TARGET_SO_SNDLOWAT 19 +#define TARGET_SO_RCVTIMEO 20 +#define TARGET_SO_SNDTIMEO 21 +#endif + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 22 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define TARGET_SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define TARGET_SO_ATTACH_FILTER 26 +#define TARGET_SO_DETACH_FILTER 27 + +#define TARGET_SO_PEERNAME 28 +#define TARGET_SO_TIMESTAMP 29 +#define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP + +#define TARGET_SO_ACCEPTCONN 30 + +#define TARGET_SO_PEERSEC 31 +#endif diff --git a/linux-user/i386/sockbits.h b/linux-user/i386/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/i386/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/m68k/sockbits.h b/linux-user/m68k/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/m68k/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/microblaze/sockbits.h b/linux-user/microblaze/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/microblaze/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/nios2/sockbits.h b/linux-user/nios2/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/nios2/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/openrisc/sockbits.h b/linux-user/openrisc/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/openrisc/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/ppc/sockbits.h b/linux-user/ppc/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/ppc/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/riscv/sockbits.h b/linux-user/riscv/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/riscv/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/s390x/sockbits.h b/linux-user/s390x/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/s390x/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/sh4/sockbits.h b/linux-user/sh4/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/sh4/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/socket.h b/linux-user/socket.h index 8eb62ea3f2..135f438bdf 100644 --- a/linux-user/socket.h +++ b/linux-user/socket.h @@ -1,66 +1,5 @@ -#if defined(TARGET_MIPS) || defined(TARGET_HPPA) || defined(TARGET_ALPHA) || \ - defined(TARGET_SPARC) #include "sockbits.h" -#else - #define TARGET_SO_PASSSEC 34 - - /* For setsockopt(2) */ - #define TARGET_SOL_SOCKET 1 - - #define TARGET_SO_DEBUG 1 - #define TARGET_SO_REUSEADDR 2 - #define TARGET_SO_TYPE 3 - #define TARGET_SO_ERROR 4 - #define TARGET_SO_DONTROUTE 5 - #define TARGET_SO_BROADCAST 6 - #define TARGET_SO_SNDBUF 7 - #define TARGET_SO_RCVBUF 8 - #define TARGET_SO_SNDBUFFORCE 32 - #define TARGET_SO_RCVBUFFORCE 33 - #define TARGET_SO_KEEPALIVE 9 - #define TARGET_SO_OOBINLINE 10 - #define TARGET_SO_NO_CHECK 11 - #define TARGET_SO_PRIORITY 12 - #define TARGET_SO_LINGER 13 - #define TARGET_SO_BSDCOMPAT 14 - /* To add :#define TARGET_SO_REUSEPORT 15 */ -#if defined(TARGET_PPC) - #define TARGET_SO_RCVLOWAT 16 - #define TARGET_SO_SNDLOWAT 17 - #define TARGET_SO_RCVTIMEO 18 - #define TARGET_SO_SNDTIMEO 19 - #define TARGET_SO_PASSCRED 20 - #define TARGET_SO_PEERCRED 21 -#else - #define TARGET_SO_PASSCRED 16 - #define TARGET_SO_PEERCRED 17 - #define TARGET_SO_RCVLOWAT 18 - #define TARGET_SO_SNDLOWAT 19 - #define TARGET_SO_RCVTIMEO 20 - #define TARGET_SO_SNDTIMEO 21 -#endif - - /* Security levels - as per NRL IPv6 - don't actually do anything */ - #define TARGET_SO_SECURITY_AUTHENTICATION 22 - #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 - #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 - - #define TARGET_SO_BINDTODEVICE 25 - - /* Socket filtering */ - #define TARGET_SO_ATTACH_FILTER 26 - #define TARGET_SO_DETACH_FILTER 27 - - #define TARGET_SO_PEERNAME 28 - #define TARGET_SO_TIMESTAMP 29 - #define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP - - #define TARGET_SO_ACCEPTCONN 30 - - #define TARGET_SO_PEERSEC 31 - -#endif #ifndef ARCH_HAS_SOCKET_TYPES /** sock_type - Socket types - default values diff --git a/linux-user/tilegx/sockbits.h b/linux-user/tilegx/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/tilegx/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/x86_64/sockbits.h b/linux-user/x86_64/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/x86_64/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/xtensa/sockbits.h b/linux-user/xtensa/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/xtensa/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" From 9e979d64e8f38f84a8bf2908fd3b4752ae4ef249 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 19 May 2018 11:29:53 +0200 Subject: [PATCH 0761/2380] linux-user: move ppc socket.h definitions to ppc/sockbits.h Change conditional #ifdef part by #undef of the symbols redefined for PPC relative to generic/socket.h Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Message-Id: <20180519092956.15134-6-laurent@vivier.eu> --- linux-user/generic/sockbits.h | 9 --------- linux-user/ppc/sockbits.h | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/linux-user/generic/sockbits.h b/linux-user/generic/sockbits.h index 093faf0a48..99505f1f85 100644 --- a/linux-user/generic/sockbits.h +++ b/linux-user/generic/sockbits.h @@ -30,21 +30,12 @@ #define TARGET_SO_LINGER 13 #define TARGET_SO_BSDCOMPAT 14 /* To add :#define TARGET_SO_REUSEPORT 15 */ -#if defined(TARGET_PPC) -#define TARGET_SO_RCVLOWAT 16 -#define TARGET_SO_SNDLOWAT 17 -#define TARGET_SO_RCVTIMEO 18 -#define TARGET_SO_SNDTIMEO 19 -#define TARGET_SO_PASSCRED 20 -#define TARGET_SO_PEERCRED 21 -#else #define TARGET_SO_PASSCRED 16 #define TARGET_SO_PEERCRED 17 #define TARGET_SO_RCVLOWAT 18 #define TARGET_SO_SNDLOWAT 19 #define TARGET_SO_RCVTIMEO 20 #define TARGET_SO_SNDTIMEO 21 -#endif /* Security levels - as per NRL IPv6 - don't actually do anything */ #define TARGET_SO_SECURITY_AUTHENTICATION 22 diff --git a/linux-user/ppc/sockbits.h b/linux-user/ppc/sockbits.h index 0e4c8f012d..ee453347a3 100644 --- a/linux-user/ppc/sockbits.h +++ b/linux-user/ppc/sockbits.h @@ -1 +1,26 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef PPC_SOCKBITS_H +#define PPC_SOCKBITS_H + #include "../generic/sockbits.h" + +#undef TARGET_SO_RCVLOWAT +#define TARGET_SO_RCVLOWAT 16 +#undef TARGET_SO_SNDLOWAT +#define TARGET_SO_SNDLOWAT 17 +#undef TARGET_SO_RCVTIMEO +#define TARGET_SO_RCVTIMEO 18 +#undef TARGET_SO_SNDTIMEO +#define TARGET_SO_SNDTIMEO 19 +#undef TARGET_SO_PASSCRED +#define TARGET_SO_PASSCRED 20 +#undef TARGET_SO_PEERCRED +#define TARGET_SO_PEERCRED 21 + +#endif From 8f553bf77c0f915624b9e48fc2eacc4968494af0 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 19 May 2018 11:29:54 +0200 Subject: [PATCH 0762/2380] linux-user: update ARCH_HAS_SOCKET_TYPES use to be like in the kernel and rename it TARGET_ARCH_HAS_SOCKET_TYPES Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Message-Id: <20180519092956.15134-7-laurent@vivier.eu> --- linux-user/alpha/sockbits.h | 36 ++------------------- linux-user/hppa/sockbits.h | 33 ++------------------ linux-user/mips/sockbits.h | 9 ++++-- linux-user/socket.h | 62 +++++++++++++++++++------------------ linux-user/sparc/sockbits.h | 36 --------------------- 5 files changed, 44 insertions(+), 132 deletions(-) diff --git a/linux-user/alpha/sockbits.h b/linux-user/alpha/sockbits.h index 4db3e52b67..f5397dd875 100644 --- a/linux-user/alpha/sockbits.h +++ b/linux-user/alpha/sockbits.h @@ -75,39 +75,9 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define TARGET_SO_NOFCS 43 -/** sock_type - Socket types - * - * Please notice that for binary compat reasons ALPHA has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. +/* TARGET_O_NONBLOCK clashes with the bits used for socket types. Therefore we + * have to define SOCK_NONBLOCK to a different value here. */ +#define TARGET_SOCK_NONBLOCK 0x40000000 -#define ARCH_HAS_SOCKET_TYPES 1 - -enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 010000000, - TARGET_SOCK_NONBLOCK = 010000000000, -}; - -#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) -#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ #endif diff --git a/linux-user/hppa/sockbits.h b/linux-user/hppa/sockbits.h index 5044619e16..2641aea859 100644 --- a/linux-user/hppa/sockbits.h +++ b/linux-user/hppa/sockbits.h @@ -64,34 +64,7 @@ #define TARGET_SO_CNX_ADVICE 0x402E -/** sock_type - Socket types - default values - * - * - * @SOCK_STREAM - stream (connection) socket - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. +/* TARGET_O_NONBLOCK clashes with the bits used for socket types. Therefore we + * have to define SOCK_NONBLOCK to a different value here. */ -enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 010000000, - TARGET_SOCK_NONBLOCK = 0x40000000, -}; - -#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) -#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - -#define ARCH_HAS_SOCKET_TYPES 1 +#define TARGET_SOCK_NONBLOCK 0x40000000 diff --git a/linux-user/mips/sockbits.h b/linux-user/mips/sockbits.h index 3fe5ac88e7..370d13ed86 100644 --- a/linux-user/mips/sockbits.h +++ b/linux-user/mips/sockbits.h @@ -91,7 +91,7 @@ * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. */ -#define ARCH_HAS_SOCKET_TYPES 1 +#define TARGET_ARCH_HAS_SOCKET_TYPES 1 enum sock_type { TARGET_SOCK_DGRAM = 1, @@ -101,10 +101,13 @@ enum sock_type { TARGET_SOCK_SEQPACKET = 5, TARGET_SOCK_DCCP = 6, TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 02000000, - TARGET_SOCK_NONBLOCK = 0200, }; #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ + +/* Flags for socket, socketpair, paccept */ +#define TARGET_SOCK_CLOEXEC TARGET_O_CLOEXEC +#define TARGET_SOCK_NONBLOCK TARGET_O_NONBLOCK + #endif diff --git a/linux-user/socket.h b/linux-user/socket.h index 135f438bdf..4c0b5c2dfa 100644 --- a/linux-user/socket.h +++ b/linux-user/socket.h @@ -1,35 +1,37 @@ - #include "sockbits.h" -#ifndef ARCH_HAS_SOCKET_TYPES - /** sock_type - Socket types - default values - * - * - * @SOCK_STREAM - stream (connection) socket - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 02000000, - TARGET_SOCK_NONBLOCK = 04000, - }; +#ifndef TARGET_ARCH_HAS_SOCKET_TYPES +/** sock_type - Socket types - default values + * + * + * @SOCK_STREAM - stream (connection) socket + * @SOCK_DGRAM - datagram (conn.less) socket + * @SOCK_RAW - raw socket + * @SOCK_RDM - reliably-delivered message + * @SOCK_SEQPACKET - sequential packet socket + * @SOCK_DCCP - Datagram Congestion Control Protocol socket + * @SOCK_PACKET - linux specific way of getting packets at the dev level. + * For writing rarp and other similar things on the user + * level. + * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. + * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. + */ +enum sock_type { + TARGET_SOCK_STREAM = 1, + TARGET_SOCK_DGRAM = 2, + TARGET_SOCK_RAW = 3, + TARGET_SOCK_RDM = 4, + TARGET_SOCK_SEQPACKET = 5, + TARGET_SOCK_DCCP = 6, + TARGET_SOCK_PACKET = 10, +}; - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ +#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) +#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ +/* Flags for socket, socketpair, accept4 */ +#define TARGET_SOCK_CLOEXEC TARGET_O_CLOEXEC +#ifndef TARGET_SOCK_NONBLOCK +#define TARGET_SOCK_NONBLOCK TARGET_O_NONBLOCK #endif +#endif /* TARGET_ARCH_HAS_SOCKET_TYPES */ diff --git a/linux-user/sparc/sockbits.h b/linux-user/sparc/sockbits.h index 385061c8b0..6434b07033 100644 --- a/linux-user/sparc/sockbits.h +++ b/linux-user/sparc/sockbits.h @@ -8,42 +8,6 @@ #ifndef SPARC_SOCKBITS_H #define SPARC_SOCKBITS_H -/** sock_type - Socket types - * - * Please notice that for binary compat reasons SPARC has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - -#define ARCH_HAS_SOCKET_TYPES 1 - -enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 020000000, - TARGET_SOCK_NONBLOCK = 040000, -}; - -#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) -#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - #define TARGET_SO_PASSSEC 31 /* For setsockopt(2) */ From 30a1b125884f53bb33b7b7276ff63b1dd8682669 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 19 May 2018 11:29:55 +0200 Subject: [PATCH 0763/2380] linux-user: copy sparc/sockbits.h definitions from linux Values defined for sparc are not correct. Copy the content of "arch/sparc/include/uapi/asm/socket.h" to fix them. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Message-Id: <20180519092956.15134-8-laurent@vivier.eu> --- linux-user/sparc/sockbits.h | 129 +++++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 38 deletions(-) diff --git a/linux-user/sparc/sockbits.h b/linux-user/sparc/sockbits.h index 6434b07033..0a822e3e1f 100644 --- a/linux-user/sparc/sockbits.h +++ b/linux-user/sparc/sockbits.h @@ -8,51 +8,104 @@ #ifndef SPARC_SOCKBITS_H #define SPARC_SOCKBITS_H -#define TARGET_SO_PASSSEC 31 - /* For setsockopt(2) */ -#define TARGET_SOL_SOCKET 1 +#define TARGET_SOL_SOCKET 0xffff -#define TARGET_SO_DEBUG 1 -#define TARGET_SO_REUSEADDR 2 -#define TARGET_SO_TYPE 3 -#define TARGET_SO_ERROR 4 -#define TARGET_SO_DONTROUTE 5 -#define TARGET_SO_BROADCAST 6 -#define TARGET_SO_SNDBUF 7 -#define TARGET_SO_RCVBUF 8 -#define TARGET_SO_SNDBUFFORCE 32 -#define TARGET_SO_RCVBUFFORCE 33 -#define TARGET_SO_KEEPALIVE 9 -#define TARGET_SO_OOBINLINE 10 -#define TARGET_SO_NO_CHECK 11 -#define TARGET_SO_PRIORITY 12 -#define TARGET_SO_LINGER 13 -#define TARGET_SO_BSDCOMPAT 14 -/* To add :#define TARGET_SO_REUSEPORT 15 */ -#define TARGET_SO_PASSCRED 16 -#define TARGET_SO_PEERCRED 17 -#define TARGET_SO_RCVLOWAT 18 -#define TARGET_SO_SNDLOWAT 19 -#define TARGET_SO_RCVTIMEO 20 -#define TARGET_SO_SNDTIMEO 21 +#define TARGET_SO_DEBUG 0x0001 +#define TARGET_SO_PASSCRED 0x0002 +#define TARGET_SO_REUSEADDR 0x0004 +#define TARGET_SO_KEEPALIVE 0x0008 +#define TARGET_SO_DONTROUTE 0x0010 +#define TARGET_SO_BROADCAST 0x0020 +#define TARGET_SO_PEERCRED 0x0040 +#define TARGET_SO_LINGER 0x0080 +#define TARGET_SO_OOBINLINE 0x0100 +#define TARGET_SO_REUSEPORT 0x0200 +#define TARGET_SO_BSDCOMPAT 0x0400 +#define TARGET_SO_RCVLOWAT 0x0800 +#define TARGET_SO_SNDLOWAT 0x1000 +#define TARGET_SO_RCVTIMEO 0x2000 +#define TARGET_SO_SNDTIMEO 0x4000 +#define TARGET_SO_ACCEPTCONN 0x8000 -/* Security levels - as per NRL IPv6 - don't actually do anything */ -#define TARGET_SO_SECURITY_AUTHENTICATION 22 -#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 -#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 +#define TARGET_SO_SNDBUF 0x1001 +#define TARGET_SO_RCVBUF 0x1002 +#define TARGET_SO_SNDBUFFORCE 0x100a +#define TARGET_SO_RCVBUFFORCE 0x100b +#define TARGET_SO_ERROR 0x1007 +#define TARGET_SO_TYPE 0x1008 +#define TARGET_SO_PROTOCOL 0x1028 +#define TARGET_SO_DOMAIN 0x1029 -#define TARGET_SO_BINDTODEVICE 25 +/* Linux specific, keep the same. */ +#define TARGET_SO_NO_CHECK 0x000b +#define TARGET_SO_PRIORITY 0x000c -/* Socket filtering */ -#define TARGET_SO_ATTACH_FILTER 26 -#define TARGET_SO_DETACH_FILTER 27 +#define TARGET_SO_BINDTODEVICE 0x000d -#define TARGET_SO_PEERNAME 28 -#define TARGET_SO_TIMESTAMP 29 +#define TARGET_SO_ATTACH_FILTER 0x001a +#define TARGET_SO_DETACH_FILTER 0x001b +#define TARGET_SO_GET_FILTER TARGET_SO_ATTACH_FILTER + +#define TARGET_SO_PEERNAME 0x001c +#define TARGET_SO_TIMESTAMP 0x001d #define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP -#define TARGET_SO_ACCEPTCONN 30 +#define TARGET_SO_PEERSEC 0x001e +#define TARGET_SO_PASSSEC 0x001f +#define TARGET_SO_TIMESTAMPNS 0x0021 +#define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS -#define TARGET_SO_PEERSEC 31 +#define TARGET_SO_MARK 0x0022 + +#define TARGET_SO_TIMESTAMPING 0x0023 +#define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING + +#define TARGET_SO_RXQ_OVFL 0x0024 + +#define TARGET_SO_WIFI_STATUS 0x0025 +#define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS +#define TARGET_SO_PEEK_OFF 0x0026 + +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define TARGET_SO_NOFCS 0x0027 + +#define TARGET_SO_LOCK_FILTER 0x0028 + +#define TARGET_SO_SELECT_ERR_QUEUE 0x0029 + +#define TARGET_SO_BUSY_POLL 0x0030 + +#define TARGET_SO_MAX_PACING_RATE 0x0031 + +#define TARGET_SO_BPF_EXTENSIONS 0x0032 + +#define TARGET_SO_INCOMING_CPU 0x0033 + +#define TARGET_SO_ATTACH_BPF 0x0034 +#define TARGET_SO_DETACH_BPF TARGET_SO_DETACH_FILTER + +#define TARGET_SO_ATTACH_REUSEPORT_CBPF 0x0035 +#define TARGET_SO_ATTACH_REUSEPORT_EBPF 0x0036 + +#define TARGET_SO_CNX_ADVICE 0x0037 + +#define TARGET_SCM_TIMESTAMPING_OPT_STATS 0x0038 + +#define TARGET_SO_MEMINFO 0x0039 + +#define TARGET_SO_INCOMING_NAPI_ID 0x003a + +#define TARGET_SO_COOKIE 0x003b + +#define TARGET_SCM_TIMESTAMPING_PKTINFO 0x003c + +#define TARGET_SO_PEERGROUPS 0x003d + +#define TARGET_SO_ZEROCOPY 0x003e + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 0x5001 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 0x5004 #endif From b0a7413dd53cbb75227152374689c3bde39c89db Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 19 May 2018 11:29:56 +0200 Subject: [PATCH 0764/2380] linux-user: define TARGET_SO_REUSEPORT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180519092956.15134-9-laurent@vivier.eu> --- linux-user/alpha/sockbits.h | 2 +- linux-user/generic/sockbits.h | 2 +- linux-user/mips/sockbits.h | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/linux-user/alpha/sockbits.h b/linux-user/alpha/sockbits.h index f5397dd875..d54dc98c09 100644 --- a/linux-user/alpha/sockbits.h +++ b/linux-user/alpha/sockbits.h @@ -18,7 +18,7 @@ #define TARGET_SO_BROADCAST 0x0020 #define TARGET_SO_LINGER 0x0080 #define TARGET_SO_OOBINLINE 0x0100 -/* To add :#define TARGET_SO_REUSEPORT 0x0200 */ +#define TARGET_SO_REUSEPORT 0x0200 #define TARGET_SO_TYPE 0x1008 #define TARGET_SO_ERROR 0x1007 diff --git a/linux-user/generic/sockbits.h b/linux-user/generic/sockbits.h index 99505f1f85..e44733c601 100644 --- a/linux-user/generic/sockbits.h +++ b/linux-user/generic/sockbits.h @@ -29,7 +29,7 @@ #define TARGET_SO_PRIORITY 12 #define TARGET_SO_LINGER 13 #define TARGET_SO_BSDCOMPAT 14 -/* To add :#define TARGET_SO_REUSEPORT 15 */ +#define TARGET_SO_REUSEPORT 15 #define TARGET_SO_PASSCRED 16 #define TARGET_SO_PEERCRED 17 #define TARGET_SO_RCVLOWAT 18 diff --git a/linux-user/mips/sockbits.h b/linux-user/mips/sockbits.h index 370d13ed86..0f022cd598 100644 --- a/linux-user/mips/sockbits.h +++ b/linux-user/mips/sockbits.h @@ -28,10 +28,7 @@ */ #define TARGET_SO_OOBINLINE 0x0100 /* Receive out-of-band data in-band. */ -#if 0 -/* To add: Allow local address and port reuse. */ #define TARGET_SO_REUSEPORT 0x0200 -#endif #define TARGET_SO_TYPE 0x1008 /* Compatible name for SO_STYLE. */ #define TARGET_SO_STYLE SO_TYPE /* Synonym */ From 4f71086665360eb15cb0cc3392bd5063f26ee934 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 May 2018 19:19:58 +0100 Subject: [PATCH 0765/2380] gdbstub: Clarify what gdb_handlesig() is doing gdb_handlesig()'s behaviour is not entirely obvious at first glance. Add a doc comment for it, and also add a comment explaining why it's ok for gdb_do_syscallv() to ignore gdb_handlesig()'s return value. (Coverity complains about this: CID 1390850.) Signed-off-by: Peter Maydell Message-Id: <20180515181958.25837-1-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- gdbstub.c | 6 ++++++ include/exec/gdbstub.h | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index e4ece2f5bc..6081e719c5 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1558,6 +1558,12 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) *p = 0; #ifdef CONFIG_USER_ONLY put_packet(s, s->syscall_buf); + /* Return control to gdb for it to process the syscall request. + * Since the protocol requires that gdb hands control back to us + * using a "here are the results" F packet, we don't need to check + * gdb_handlesig's return value (which is the signal to deliver if + * execution was resumed via a continue packet). + */ gdb_handlesig(s->c_cpu, 0); #else /* In this case wait to send the syscall packet until notification that diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 2e8a4b83b9..08363969c1 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -48,6 +48,21 @@ int use_gdb_syscalls(void); void gdb_set_stop_cpu(CPUState *cpu); void gdb_exit(CPUArchState *, int); #ifdef CONFIG_USER_ONLY +/** + * gdb_handlesig: yield control to gdb + * @cpu: CPU + * @sig: if non-zero, the signal number which caused us to stop + * + * This function yields control to gdb, when a user-mode-only target + * needs to stop execution. If @sig is non-zero, then we will send a + * stop packet to tell gdb that we have stopped because of this signal. + * + * This function will block (handling protocol requests from gdb) + * until gdb tells us to continue target execution. When it does + * return, the return value is a signal to deliver to the target, + * or 0 if no signal should be delivered, ie the signal that caused + * us to stop should be ignored. + */ int gdb_handlesig(CPUState *, int); void gdb_signalled(CPUArchState *, int); void gdbserver_fork(CPUState *); From 0efc91423a77ba6dc4a2f6d96a1ae1f5d3d958a8 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 23 May 2018 11:14:11 +0200 Subject: [PATCH 0766/2380] migration: fix exec/fd migrations Commit: commit 36c2f8be2c4eb0003ac77a14910842b7ddd7337e Author: Juan Quintela Date: Wed Mar 7 08:40:52 2018 +0100 migration: Delay start of migration main routines Missed tcp and fd transports. This fix its. Reported-by: Kevin Wolf Signed-off-by: Juan Quintela Tested-by: Kevin Wolf Message-Id: <20180523091411.1073-1-quintela@redhat.com> Signed-off-by: Juan Quintela --- migration/exec.c | 4 ++++ migration/fd.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/migration/exec.c b/migration/exec.c index 9d0f82f1f0..0bbeb63c97 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "channel.h" #include "exec.h" +#include "migration.h" #include "io/channel-command.h" #include "trace.h" @@ -48,6 +49,9 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc, { migration_channel_process_incoming(ioc); object_unref(OBJECT(ioc)); + if (!migrate_use_multifd()) { + migration_incoming_process(); + } return G_SOURCE_REMOVE; } diff --git a/migration/fd.c b/migration/fd.c index 9a380bbbc4..fee34ffdc0 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "channel.h" #include "fd.h" +#include "migration.h" #include "monitor/monitor.h" #include "io/channel-util.h" #include "trace.h" @@ -48,6 +49,9 @@ static gboolean fd_accept_incoming_migration(QIOChannel *ioc, { migration_channel_process_incoming(ioc); object_unref(OBJECT(ioc)); + if (!migrate_use_multifd()) { + migration_incoming_process(); + } return G_SOURCE_REMOVE; } From bf269906f5b225a04825b2bce4364bfe1d509999 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 25 May 2018 09:50:42 +0800 Subject: [PATCH 0767/2380] migration: use g_free for ram load bitmap Buffers allocated with bitmap_new() should be freed with g_free(). Both reported by Coverity: *** CID 1391300: API usage errors (ALLOC_FREE_MISMATCH) /migration/ram.c: 3517 in ram_dirty_bitmap_reload() 3511 * the last one to sync, we need to notify the main send thread. 3512 */ 3513 ram_dirty_bitmap_reload_notify(s); 3514 3515 ret = 0; 3516 out: >>> CID 1391300: API usage errors (ALLOC_FREE_MISMATCH) >>> Calling "free" frees "le_bitmap" using "free" but it should have been freed using "g_free". 3517 free(le_bitmap); 3518 return ret; 3519 } 3520 3521 static int ram_resume_prepare(MigrationState *s, void *opaque) 3522 { *** CID 1391292: API usage errors (ALLOC_FREE_MISMATCH) /migration/ram.c: 249 in ramblock_recv_bitmap_send() 243 * Mark as an end, in case the middle part is screwed up due to 244 * some "misterious" reason. 245 */ 246 qemu_put_be64(file, RAMBLOCK_RECV_BITMAP_ENDING); 247 qemu_fflush(file); 248 >>> CID 1391292: API usage errors (ALLOC_FREE_MISMATCH) >>> Calling "free" frees "le_bitmap" using "free" but it should have been freed using "g_free". 249 free(le_bitmap); 250 251 if (qemu_file_get_error(file)) { 252 return qemu_file_get_error(file); 253 } 254 Signed-off-by: Peter Xu Reviewed-by: Juan Quintela Message-Id: <20180525015042.31778-1-peterx@redhat.com> Signed-off-by: Juan Quintela --- migration/ram.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 5bcbf7a9f9..c53e8369a3 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -246,7 +246,7 @@ int64_t ramblock_recv_bitmap_send(QEMUFile *file, qemu_put_be64(file, RAMBLOCK_RECV_BITMAP_ENDING); qemu_fflush(file); - free(le_bitmap); + g_free(le_bitmap); if (qemu_file_get_error(file)) { return qemu_file_get_error(file); @@ -3514,7 +3514,7 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) ret = 0; out: - free(le_bitmap); + g_free(le_bitmap); return ret; } From fa0d421f39b353ed2de901bb798f81ce9824c2f3 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 24 May 2018 17:24:35 +0300 Subject: [PATCH 0768/2380] libqtest: fail if child coredumps Right now tests report OK status if QEMU crashes during cleanup. Let's catch that case and fail the test. Signed-off-by: Michael S. Tsirkin Acked-by: Thomas Huth --- tests/libqtest.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/libqtest.c b/tests/libqtest.c index 43fb97e035..f869854849 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -103,8 +103,15 @@ static int socket_accept(int sock) static void kill_qemu(QTestState *s) { if (s->qemu_pid != -1) { + int wstatus = 0; + pid_t pid; + kill(s->qemu_pid, SIGTERM); - waitpid(s->qemu_pid, NULL, 0); + pid = waitpid(s->qemu_pid, &wstatus, 0); + + if (pid == s->qemu_pid && WIFSIGNALED(wstatus)) { + assert(!WCOREDUMP(wstatus)); + } } } From b21373d0713d060e42dcec8262197a91b71dfd4b Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 7 Mar 2018 14:45:06 -0500 Subject: [PATCH 0769/2380] test: Add test cases that use the external swtpm with CRB interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test program for testing the CRB with the external swtpm. The 1st test case extends a PCR and reads back the value and compares it against an expected return packet. The 2nd test case repeats the 1st test case and then migrates the external swtpm's state along with the VM state to a destination QEMU and swtpm and checks that the PCR has the expected value now. The test cases require 'swtpm' to be installed on the system and in the PATH and 'swtpm' must support the --tpm2 option. If this is not the case, the test will be skipped. Signed-off-by: Stefan Berger Reviewed-by: Marc-André Lureau --- tests/Makefile.include | 3 + tests/tpm-crb-swtpm-test.c | 247 +++++++++++++++++++++++++++++++++++++ tests/tpm-util.c | 186 ++++++++++++++++++++++++++++ tests/tpm-util.h | 36 ++++++ 4 files changed, 472 insertions(+) create mode 100644 tests/tpm-crb-swtpm-test.c create mode 100644 tests/tpm-util.c create mode 100644 tests/tpm-util.h diff --git a/tests/Makefile.include b/tests/Makefile.include index 3b9a5e31a2..b499ba1813 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -297,6 +297,7 @@ check-qtest-i386-$(CONFIG_VHOST_USER_NET_TEST_i386) += tests/vhost-user-test$(EX ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),) check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF) endif +check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-swtpm-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-test$(EXESUF) check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) @@ -721,6 +722,8 @@ tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \ tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y) +tests/tpm-crb-swtpm-test$(EXESUF): tests/tpm-crb-swtpm-test.o tests/tpm-emu.o \ + tests/tpm-util.o $(test-io-obj-y) tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y) tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y) tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ diff --git a/tests/tpm-crb-swtpm-test.c b/tests/tpm-crb-swtpm-test.c new file mode 100644 index 0000000000..c2bde0cbaa --- /dev/null +++ b/tests/tpm-crb-swtpm-test.c @@ -0,0 +1,247 @@ +/* + * QTest testcase for TPM CRB talking to external swtpm and swtpm migration + * + * Copyright (c) 2018 IBM Corporation + * with parts borrowed from migration-test.c that is: + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "hw/acpi/tpm.h" +#include "io/channel-socket.h" +#include "libqtest.h" +#include "tpm-util.h" +#include "sysemu/tpm.h" +#include "qapi/qmp/qdict.h" + +typedef struct TestState { + char *src_tpm_path; + char *dst_tpm_path; + char *uri; +} TestState; + +bool got_stop; + +static void migrate(QTestState *who, const char *uri) +{ + QDict *rsp; + gchar *cmd; + + cmd = g_strdup_printf("{ 'execute': 'migrate'," + "'arguments': { 'uri': '%s' } }", + uri); + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +/* + * Events can get in the way of responses we are actually waiting for. + */ +static QDict *wait_command(QTestState *who, const char *command) +{ + const char *event_string; + QDict *response; + + response = qtest_qmp(who, command); + + while (qdict_haskey(response, "event")) { + /* OK, it was an event */ + event_string = qdict_get_str(response, "event"); + if (!strcmp(event_string, "STOP")) { + got_stop = true; + } + qobject_unref(response); + response = qtest_qmp_receive(who); + } + return response; +} + +static void wait_for_migration_complete(QTestState *who) +{ + while (true) { + QDict *rsp, *rsp_return; + bool completed; + const char *status; + + rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); + rsp_return = qdict_get_qdict(rsp, "return"); + status = qdict_get_str(rsp_return, "status"); + completed = strcmp(status, "completed") == 0; + g_assert_cmpstr(status, !=, "failed"); + qobject_unref(rsp); + if (completed) { + return; + } + usleep(1000); + } +} + +static void migration_start_qemu(QTestState **src_qemu, QTestState **dst_qemu, + SocketAddress *src_tpm_addr, + SocketAddress *dst_tpm_addr, + const char *miguri) +{ + char *src_qemu_args, *dst_qemu_args; + + src_qemu_args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-crb,tpmdev=dev ", + src_tpm_addr->u.q_unix.path); + + *src_qemu = qtest_init(src_qemu_args); + + dst_qemu_args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-crb,tpmdev=dev " + "-incoming %s", + dst_tpm_addr->u.q_unix.path, + miguri); + + *dst_qemu = qtest_init(dst_qemu_args); + + free(src_qemu_args); + free(dst_qemu_args); +} + +static void tpm_crb_swtpm_test(const void *data) +{ + char *args = NULL; + QTestState *s; + SocketAddress *addr = NULL; + gboolean succ; + GPid swtpm_pid; + GError *error = NULL; + const TestState *ts = data; + + succ = tpm_util_swtpm_start(ts->src_tpm_path, &swtpm_pid, &addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + return; + } + + args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-crb,tpmdev=dev", + addr->u.q_unix.path); + + s = qtest_start(args); + g_free(args); + + tpm_util_startup(s, tpm_util_crb_transfer); + tpm_util_pcrextend(s, tpm_util_crb_transfer); + + unsigned char tpm_pcrread_resp[] = + "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" + "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" + "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" + "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; + tpm_util_pcrread(s, tpm_util_crb_transfer, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + qtest_end(); + tpm_util_swtpm_kill(swtpm_pid); + + if (addr) { + g_unlink(addr->u.q_unix.path); + qapi_free_SocketAddress(addr); + } +} + +static void tpm_crb_swtpm_migration_test(const void *data) +{ + const TestState *ts = data; + gboolean succ; + GPid src_tpm_pid, dst_tpm_pid; + SocketAddress *src_tpm_addr = NULL, *dst_tpm_addr = NULL; + GError *error = NULL; + QTestState *src_qemu, *dst_qemu; + + succ = tpm_util_swtpm_start(ts->src_tpm_path, &src_tpm_pid, + &src_tpm_addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + return; + } + + succ = tpm_util_swtpm_start(ts->dst_tpm_path, &dst_tpm_pid, + &dst_tpm_addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + goto err_src_tpm_kill; + } + + migration_start_qemu(&src_qemu, &dst_qemu, src_tpm_addr, dst_tpm_addr, + ts->uri); + + tpm_util_startup(src_qemu, tpm_util_crb_transfer); + tpm_util_pcrextend(src_qemu, tpm_util_crb_transfer); + + unsigned char tpm_pcrread_resp[] = + "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" + "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" + "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" + "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; + tpm_util_pcrread(src_qemu, tpm_util_crb_transfer, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + migrate(src_qemu, ts->uri); + wait_for_migration_complete(src_qemu); + + tpm_util_pcrread(dst_qemu, tpm_util_crb_transfer, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + qtest_quit(dst_qemu); + qtest_quit(src_qemu); + + tpm_util_swtpm_kill(dst_tpm_pid); + if (dst_tpm_addr) { + g_unlink(dst_tpm_addr->u.q_unix.path); + qapi_free_SocketAddress(dst_tpm_addr); + } + +err_src_tpm_kill: + tpm_util_swtpm_kill(src_tpm_pid); + if (src_tpm_addr) { + g_unlink(src_tpm_addr->u.q_unix.path); + qapi_free_SocketAddress(src_tpm_addr); + } +} + +int main(int argc, char **argv) +{ + int ret; + TestState ts = { 0 }; + + ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); + ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); + ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/tpm/crb-swtpm/test", &ts, tpm_crb_swtpm_test); + qtest_add_data_func("/tpm/crb-swtpm-migration/test", &ts, + tpm_crb_swtpm_migration_test); + ret = g_test_run(); + + g_rmdir(ts.dst_tpm_path); + g_free(ts.dst_tpm_path); + g_rmdir(ts.src_tpm_path); + g_free(ts.src_tpm_path); + g_free(ts.uri); + + return ret; +} diff --git a/tests/tpm-util.c b/tests/tpm-util.c new file mode 100644 index 0000000000..c9b3947c1c --- /dev/null +++ b/tests/tpm-util.c @@ -0,0 +1,186 @@ +/* + * QTest TPM utilities + * + * Copyright (c) 2018 IBM Corporation + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Stefan Berger + * Marc-André Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "hw/acpi/tpm.h" +#include "libqtest.h" +#include "tpm-util.h" + +void tpm_util_crb_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size) +{ + uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); + uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); + + qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); + + qtest_memwrite(s, caddr, req, req_size); + + uint32_t sts, start = 1; + uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); + while (true) { + start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); + if ((start & 1) == 0) { + break; + } + if (g_get_monotonic_time() >= end_time) { + break; + } + }; + start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); + g_assert_cmpint(start & 1, ==, 0); + sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); + g_assert_cmpint(sts & 1, ==, 0); + + qtest_memread(s, raddr, rsp, rsp_size); +} + +void tpm_util_startup(QTestState *s, tx_func *tx) +{ + unsigned char buffer[1024]; + unsigned char tpm_startup[] = + "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; + unsigned char tpm_startup_resp[] = + "\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00"; + + tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, sizeof(tpm_startup_resp), + tpm_startup_resp, sizeof(tpm_startup_resp)); +} + +void tpm_util_pcrextend(QTestState *s, tx_func *tx) +{ + unsigned char buffer[1024]; + unsigned char tpm_pcrextend[] = + "\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00" + "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + "\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00"; + + unsigned char tpm_pcrextend_resp[] = + "\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x01\x00\x00"; + + tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp), + tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp)); +} + +void tpm_util_pcrread(QTestState *s, tx_func *tx, + const unsigned char *exp_resp, size_t exp_resp_size) +{ + unsigned char buffer[1024]; + unsigned char tpm_pcrread[] = + "\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b" + "\x03\x00\x04\x00"; + + tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size); +} + +static gboolean tpm_util_swtpm_has_tpm2(void) +{ + gint mystdout; + gboolean succ; + unsigned i; + char buffer[10240]; + ssize_t n; + gchar *swtpm_argv[] = { + g_strdup("swtpm"), g_strdup("socket"), g_strdup("--help"), NULL + }; + + succ = g_spawn_async_with_pipes(NULL, swtpm_argv, NULL, + G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, + NULL, &mystdout, NULL, NULL); + if (!succ) { + goto cleanup; + } + + n = read(mystdout, buffer, sizeof(buffer) - 1); + if (n < 0) { + goto cleanup; + } + buffer[n] = 0; + if (!strstr(buffer, "--tpm2")) { + succ = false; + } + + cleanup: + for (i = 0; swtpm_argv[i]; i++) { + g_free(swtpm_argv[i]); + } + + return succ; +} + +gboolean tpm_util_swtpm_start(const char *path, GPid *pid, + SocketAddress **addr, GError **error) +{ + char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path); + char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock", + path); + gchar *swtpm_argv[] = { + g_strdup("swtpm"), g_strdup("socket"), + g_strdup("--tpmstate"), swtpm_argv_tpmstate, + g_strdup("--ctrl"), swtpm_argv_ctrl, + g_strdup("--tpm2"), + NULL + }; + gboolean succ; + unsigned i; + + succ = tpm_util_swtpm_has_tpm2(); + if (!succ) { + goto cleanup; + } + + *addr = g_new0(SocketAddress, 1); + (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX; + (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL); + + succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, pid, error); + +cleanup: + for (i = 0; swtpm_argv[i]; i++) { + g_free(swtpm_argv[i]); + } + + return succ; +} + +void tpm_util_swtpm_kill(GPid pid) +{ + int n; + + if (!pid) { + return; + } + + g_spawn_close_pid(pid); + + n = kill(pid, 0); + if (n < 0) { + return; + } + + kill(pid, SIGKILL); +} diff --git a/tests/tpm-util.h b/tests/tpm-util.h new file mode 100644 index 0000000000..d155d99aea --- /dev/null +++ b/tests/tpm-util.h @@ -0,0 +1,36 @@ +/* + * QTest TPM utilities + * + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TESTS_TPM_UTIL_H +#define TESTS_TPM_UTIL_H + +#include "qemu/osdep.h" +#include "io/channel-socket.h" + +typedef void (tx_func)(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); + +void tpm_util_crb_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); + +void tpm_util_startup(QTestState *s, tx_func *tx); +void tpm_util_pcrextend(QTestState *s, tx_func *tx); +void tpm_util_pcrread(QTestState *s, tx_func *tx, + const unsigned char *exp_resp, size_t exp_resp_size); + +gboolean tpm_util_swtpm_start(const char *path, GPid *pid, + SocketAddress **addr, GError **error); +void tpm_util_swtpm_kill(GPid pid); + +#endif /* TESTS_TPM_UTIL_H */ From 8534063a38583f1e0458daf076154131a83fa0e0 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Wed, 4 Apr 2018 13:47:33 +0200 Subject: [PATCH 0770/2380] target-microblaze: dec_load: Use bool instead of unsigned int Use bool instead of unsigned int to represent flags. No functional change. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 0872dc9ded..a8a5eaebec 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -897,14 +897,15 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) static void dec_load(DisasContext *dc) { TCGv t, v, *addr; - unsigned int size, rev = 0, ex = 0; + unsigned int size; + bool rev = false, ex = false; TCGMemOp mop; mop = dc->opcode & 3; size = 1 << mop; if (!dc->type_b) { - rev = (dc->ir >> 9) & 1; - ex = (dc->ir >> 10) & 1; + rev = extract32(dc->ir, 9, 1); + ex = extract32(dc->ir, 10, 1); } mop |= MO_TE; if (rev) { From b51b3d43deeb9880b383603806daedf125ae9cce Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Wed, 4 Apr 2018 13:55:48 +0200 Subject: [PATCH 0771/2380] target-microblaze: dec_store: Use bool instead of unsigned int Use bool instead of unsigned int to represent flags. Also, use extract32 instead of open coding the bit extract. No functional change. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index a8a5eaebec..413e683aec 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1027,14 +1027,15 @@ static void dec_store(DisasContext *dc) { TCGv t, *addr, swx_addr; TCGLabel *swx_skip = NULL; - unsigned int size, rev = 0, ex = 0; + unsigned int size; + bool rev = false, ex = false; TCGMemOp mop; mop = dc->opcode & 3; size = 1 << mop; if (!dc->type_b) { - rev = (dc->ir >> 9) & 1; - ex = (dc->ir >> 10) & 1; + rev = extract32(dc->ir, 9, 1); + ex = extract32(dc->ir, 10, 1); } mop |= MO_TE; if (rev) { From 0e9033c8c53c3839871c59ef431bcfdc9359d0c1 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Wed, 4 Apr 2018 16:34:21 +0200 Subject: [PATCH 0772/2380] target-microblaze: compute_ldst_addr: Use bool instead of int Use bool instead of int to represent flags. No functional change. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 413e683aec..46595e6336 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -845,13 +845,13 @@ static void dec_imm(DisasContext *dc) static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) { - unsigned int extimm = dc->tb_flags & IMM_FLAG; - /* Should be set to one if r1 is used by loadstores. */ - int stackprot = 0; + bool extimm = dc->tb_flags & IMM_FLAG; + /* Should be set to true if r1 is used by loadstores. */ + bool stackprot = false; /* All load/stores use ra. */ if (dc->ra == 1 && dc->cpu->cfg.stackprot) { - stackprot = 1; + stackprot = true; } /* Treat the common cases first. */ @@ -864,7 +864,7 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) } if (dc->rb == 1 && dc->cpu->cfg.stackprot) { - stackprot = 1; + stackprot = true; } *t = tcg_temp_new(); From 4c8ac10737ae38256d59406801591d9dca2bd69b Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Mon, 16 Apr 2018 22:23:05 +0200 Subject: [PATCH 0773/2380] target-microblaze: Fallback to our latest CPU version Today, when running QEMU in linux-user or with boards that don't select a specific CPU version, we treat it as an invalid version and log a message. Instead, if no specific version was selected, fallback to our latest CPU version. Reviewed-by: Alistair Francis Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 4dc1404800..06476f6efc 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -72,6 +72,9 @@ static const struct { {NULL, 0}, }; +/* If no specific version gets selected, default to the following. */ +#define DEFAULT_CPU_VERSION "10.0" + static void mb_cpu_set_pc(CPUState *cs, vaddr value) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); @@ -141,6 +144,7 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; uint8_t version_code = 0; + const char *version; int i = 0; Error *local_err = NULL; @@ -162,8 +166,9 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) | PVR2_FPU_EXC_MASK \ | 0; - for (i = 0; mb_cpu_lookup[i].name && cpu->cfg.version; i++) { - if (strcmp(mb_cpu_lookup[i].name, cpu->cfg.version) == 0) { + version = cpu->cfg.version ? cpu->cfg.version : DEFAULT_CPU_VERSION; + for (i = 0; mb_cpu_lookup[i].name && version; i++) { + if (strcmp(mb_cpu_lookup[i].name, version) == 0) { version_code = mb_cpu_lookup[i].version_id; break; } From 5c594ef3c77c7226e7b2080430b9b74d26d5bd7e Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 21:55:21 +0200 Subject: [PATCH 0774/2380] target-microblaze: Correct special register array sizes Correct special register array sizes. Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.h | 4 ++-- target/microblaze/translate.c | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 5be71bc320..994496515f 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -242,8 +242,8 @@ struct CPUMBState { uint32_t bimm; uint32_t imm; - uint32_t regs[33]; - uint32_t sregs[24]; + uint32_t regs[32]; + uint32_t sregs[14]; float_status fp_status; /* Stack protectors. Yes, it's a hw feature. */ uint32_t slr, shr; diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 46595e6336..9614f15d58 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -54,7 +54,7 @@ static TCGv env_debug; static TCGv cpu_R[32]; -static TCGv cpu_SR[18]; +static TCGv cpu_SR[14]; static TCGv env_imm; static TCGv env_btaken; static TCGv env_btarget; @@ -106,8 +106,7 @@ static const char *regnames[] = static const char *special_regnames[] = { "rpc", "rmsr", "sr2", "sr3", "sr4", "sr5", "sr6", "sr7", - "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15", - "sr16", "sr17", "sr18" + "sr8", "sr9", "sr10", "sr11", "sr12", "sr13" }; static inline void t_sync_flags(DisasContext *dc) From c56911a424c5279a5113c4e1d508733a60c12dbc Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 14 Apr 2018 22:15:19 +0200 Subject: [PATCH 0775/2380] target-microblaze: Correct the PVR array size Correct the PVR array size, there are 13 PVR registers. Reviewed-by: Alistair Francis Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 994496515f..2304c24b7d 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -277,7 +277,7 @@ struct CPUMBState { /* These fields are preserved on reset. */ struct { - uint32_t regs[16]; + uint32_t regs[13]; } pvr; }; From cfeea807e5af996979b2c13ab3b6eb447e1796bb Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Thu, 5 Apr 2018 19:09:30 +0200 Subject: [PATCH 0776/2380] target-microblaze: Tighten up TCGv_i32 vs TCGv type usage Tighten up TCGv_i32 vs TCGv type usage. Avoid using TCGv when TCGv_i32 should be used. This is in preparation for adding 64bit addressing support. No functional change. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/helper.c | 2 +- target/microblaze/translate.c | 577 +++++++++++++++++----------------- 2 files changed, 293 insertions(+), 286 deletions(-) diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index fac6ee9263..387d4aca5a 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -69,7 +69,7 @@ int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, /* Translate if the MMU is available and enabled. */ if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) { - target_ulong vaddr, paddr; + uint32_t vaddr, paddr; struct microblaze_mmu_lookup lu; hit = mmu_translate(&env->mmu, &lu, address, rw, mmu_idx); diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 9614f15d58..2e9a286af6 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -52,22 +52,22 @@ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ #define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ -static TCGv env_debug; -static TCGv cpu_R[32]; -static TCGv cpu_SR[14]; -static TCGv env_imm; -static TCGv env_btaken; -static TCGv env_btarget; -static TCGv env_iflags; -static TCGv env_res_addr; -static TCGv env_res_val; +static TCGv_i32 env_debug; +static TCGv_i32 cpu_R[32]; +static TCGv_i32 cpu_SR[14]; +static TCGv_i32 env_imm; +static TCGv_i32 env_btaken; +static TCGv_i32 env_btarget; +static TCGv_i32 env_iflags; +static TCGv_i32 env_res_addr; +static TCGv_i32 env_res_val; #include "exec/gen-icount.h" /* This is the state at translation time. */ typedef struct DisasContext { MicroBlazeCPU *cpu; - target_ulong pc; + uint32_t pc; /* Decoder. */ int type_b; @@ -113,7 +113,7 @@ static inline void t_sync_flags(DisasContext *dc) { /* Synch the tb dependent flags between translator and runtime. */ if (dc->tb_flags != dc->synced_flags) { - tcg_gen_movi_tl(env_iflags, dc->tb_flags); + tcg_gen_movi_i32(env_iflags, dc->tb_flags); dc->synced_flags = dc->tb_flags; } } @@ -123,7 +123,7 @@ static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) TCGv_i32 tmp = tcg_const_i32(index); t_sync_flags(dc); - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); dc->is_jmp = DISAS_UPDATE; @@ -142,41 +142,41 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) { if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); - tcg_gen_movi_tl(cpu_SR[SR_PC], dest); + tcg_gen_movi_i32(cpu_SR[SR_PC], dest); tcg_gen_exit_tb((uintptr_t)dc->tb + n); } else { - tcg_gen_movi_tl(cpu_SR[SR_PC], dest); + tcg_gen_movi_i32(cpu_SR[SR_PC], dest); tcg_gen_exit_tb(0); } } -static void read_carry(DisasContext *dc, TCGv d) +static void read_carry(DisasContext *dc, TCGv_i32 d) { - tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31); + tcg_gen_shri_i32(d, cpu_SR[SR_MSR], 31); } /* * write_carry sets the carry bits in MSR based on bit 0 of v. * v[31:1] are ignored. */ -static void write_carry(DisasContext *dc, TCGv v) +static void write_carry(DisasContext *dc, TCGv_i32 v) { - TCGv t0 = tcg_temp_new(); - tcg_gen_shli_tl(t0, v, 31); - tcg_gen_sari_tl(t0, t0, 31); - tcg_gen_andi_tl(t0, t0, (MSR_C | MSR_CC)); - tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_shli_i32(t0, v, 31); + tcg_gen_sari_i32(t0, t0, 31); + tcg_gen_andi_i32(t0, t0, (MSR_C | MSR_CC)); + tcg_gen_andi_i32(cpu_SR[SR_MSR], cpu_SR[SR_MSR], ~(MSR_C | MSR_CC)); - tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0); - tcg_temp_free(t0); + tcg_gen_or_i32(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0); + tcg_temp_free_i32(t0); } static void write_carryi(DisasContext *dc, bool carry) { - TCGv t0 = tcg_temp_new(); - tcg_gen_movi_tl(t0, carry); + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_movi_i32(t0, carry); write_carry(dc, t0); - tcg_temp_free(t0); + tcg_temp_free_i32(t0); } /* True if ALU operand b is a small immediate that may deserve @@ -187,13 +187,13 @@ static inline int dec_alu_op_b_is_small_imm(DisasContext *dc) return dc->type_b && !(dc->tb_flags & IMM_FLAG); } -static inline TCGv *dec_alu_op_b(DisasContext *dc) +static inline TCGv_i32 *dec_alu_op_b(DisasContext *dc) { if (dc->type_b) { if (dc->tb_flags & IMM_FLAG) - tcg_gen_ori_tl(env_imm, env_imm, dc->imm); + tcg_gen_ori_i32(env_imm, env_imm, dc->imm); else - tcg_gen_movi_tl(env_imm, (int32_t)((int16_t)dc->imm)); + tcg_gen_movi_i32(env_imm, (int32_t)((int16_t)dc->imm)); return &env_imm; } else return &cpu_R[dc->rb]; @@ -202,7 +202,7 @@ static inline TCGv *dec_alu_op_b(DisasContext *dc) static void dec_add(DisasContext *dc) { unsigned int k, c; - TCGv cf; + TCGv_i32 cf; k = dc->opcode & 4; c = dc->opcode & 2; @@ -216,15 +216,15 @@ static void dec_add(DisasContext *dc) /* k - keep carry, no need to update MSR. */ /* If rd == r0, it's a nop. */ if (dc->rd) { - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); if (c) { /* c - Add carry into the result. */ - cf = tcg_temp_new(); + cf = tcg_temp_new_i32(); read_carry(dc, cf); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); - tcg_temp_free(cf); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_temp_free_i32(cf); } } return; @@ -232,31 +232,31 @@ static void dec_add(DisasContext *dc) /* From now on, we can assume k is zero. So we need to update MSR. */ /* Extract carry. */ - cf = tcg_temp_new(); + cf = tcg_temp_new_i32(); if (c) { read_carry(dc, cf); } else { - tcg_gen_movi_tl(cf, 0); + tcg_gen_movi_i32(cf, 0); } if (dc->rd) { - TCGv ncf = tcg_temp_new(); + TCGv_i32 ncf = tcg_temp_new_i32(); gen_helper_carry(ncf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); write_carry(dc, ncf); - tcg_temp_free(ncf); + tcg_temp_free_i32(ncf); } else { gen_helper_carry(cf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); write_carry(dc, cf); } - tcg_temp_free(cf); + tcg_temp_free_i32(cf); } static void dec_sub(DisasContext *dc) { unsigned int u, cmp, k, c; - TCGv cf, na; + TCGv_i32 cf, na; u = dc->imm & 2; k = dc->opcode & 4; @@ -282,15 +282,15 @@ static void dec_sub(DisasContext *dc) /* k - keep carry, no need to update MSR. */ /* If rd == r0, it's a nop. */ if (dc->rd) { - tcg_gen_sub_tl(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); + tcg_gen_sub_i32(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); if (c) { /* c - Add carry into the result. */ - cf = tcg_temp_new(); + cf = tcg_temp_new_i32(); read_carry(dc, cf); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); - tcg_temp_free(cf); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_temp_free_i32(cf); } } return; @@ -298,30 +298,30 @@ static void dec_sub(DisasContext *dc) /* From now on, we can assume k is zero. So we need to update MSR. */ /* Extract carry. And complement a into na. */ - cf = tcg_temp_new(); - na = tcg_temp_new(); + cf = tcg_temp_new_i32(); + na = tcg_temp_new_i32(); if (c) { read_carry(dc, cf); } else { - tcg_gen_movi_tl(cf, 1); + tcg_gen_movi_i32(cf, 1); } /* d = b + ~a + c. carry defaults to 1. */ - tcg_gen_not_tl(na, cpu_R[dc->ra]); + tcg_gen_not_i32(na, cpu_R[dc->ra]); if (dc->rd) { - TCGv ncf = tcg_temp_new(); + TCGv_i32 ncf = tcg_temp_new_i32(); gen_helper_carry(ncf, na, *(dec_alu_op_b(dc)), cf); - tcg_gen_add_tl(cpu_R[dc->rd], na, *(dec_alu_op_b(dc))); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_gen_add_i32(cpu_R[dc->rd], na, *(dec_alu_op_b(dc))); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); write_carry(dc, ncf); - tcg_temp_free(ncf); + tcg_temp_free_i32(ncf); } else { gen_helper_carry(cf, na, *(dec_alu_op_b(dc)), cf); write_carry(dc, cf); } - tcg_temp_free(cf); - tcg_temp_free(na); + tcg_temp_free_i32(cf); + tcg_temp_free_i32(na); } static void dec_pattern(DisasContext *dc) @@ -331,7 +331,7 @@ static void dec_pattern(DisasContext *dc) if ((dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) && !dc->cpu->cfg.use_pcmp_instr) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); } @@ -346,14 +346,14 @@ static void dec_pattern(DisasContext *dc) case 2: LOG_DIS("pcmpeq r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); if (dc->rd) { - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_R[dc->rd], + tcg_gen_setcond_i32(TCG_COND_EQ, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); } break; case 3: LOG_DIS("pcmpne r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); if (dc->rd) { - tcg_gen_setcond_tl(TCG_COND_NE, cpu_R[dc->rd], + tcg_gen_setcond_i32(TCG_COND_NE, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); } break; @@ -380,9 +380,9 @@ static void dec_and(DisasContext *dc) return; if (not) { - tcg_gen_andc_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_andc_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } else - tcg_gen_and_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_and_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } static void dec_or(DisasContext *dc) @@ -394,7 +394,7 @@ static void dec_or(DisasContext *dc) LOG_DIS("or r%d r%d r%d imm=%x\n", dc->rd, dc->ra, dc->rb, dc->imm); if (dc->rd) - tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_or_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } static void dec_xor(DisasContext *dc) @@ -406,31 +406,31 @@ static void dec_xor(DisasContext *dc) LOG_DIS("xor r%d\n", dc->rd); if (dc->rd) - tcg_gen_xor_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_xor_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } -static inline void msr_read(DisasContext *dc, TCGv d) +static inline void msr_read(DisasContext *dc, TCGv_i32 d) { - tcg_gen_mov_tl(d, cpu_SR[SR_MSR]); + tcg_gen_mov_i32(d, cpu_SR[SR_MSR]); } -static inline void msr_write(DisasContext *dc, TCGv v) +static inline void msr_write(DisasContext *dc, TCGv_i32 v) { - TCGv t; + TCGv_i32 t; - t = tcg_temp_new(); + t = tcg_temp_new_i32(); dc->cpustate_changed = 1; /* PVR bit is not writable. */ - tcg_gen_andi_tl(t, v, ~MSR_PVR); - tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); - tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t); + tcg_gen_andi_i32(t, v, ~MSR_PVR); + tcg_gen_andi_i32(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); + tcg_gen_or_i32(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t); tcg_temp_free(t); } static void dec_msr(DisasContext *dc) { CPUState *cs = CPU(dc->cpu); - TCGv t0, t1; + TCGv_i32 t0, t1; unsigned int sr, to, rn; int mem_index = cpu_mmu_index(&dc->cpu->env, false); @@ -454,7 +454,7 @@ static void dec_msr(DisasContext *dc) if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX && (dc->imm != 4 && dc->imm != 0)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -462,20 +462,20 @@ static void dec_msr(DisasContext *dc) if (dc->rd) msr_read(dc, cpu_R[dc->rd]); - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); msr_read(dc, t0); - tcg_gen_mov_tl(t1, *(dec_alu_op_b(dc))); + tcg_gen_mov_i32(t1, *(dec_alu_op_b(dc))); if (clr) { - tcg_gen_not_tl(t1, t1); - tcg_gen_and_tl(t0, t0, t1); + tcg_gen_not_i32(t1, t1); + tcg_gen_and_i32(t0, t0, t1); } else - tcg_gen_or_tl(t0, t0, t1); + tcg_gen_or_i32(t0, t0, t1); msr_write(dc, t0); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc + 4); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc + 4); dc->is_jmp = DISAS_UPDATE; return; } @@ -483,7 +483,7 @@ static void dec_msr(DisasContext *dc) if (to) { if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -495,9 +495,9 @@ static void dec_msr(DisasContext *dc) sr &= 7; LOG_DIS("m%ss sr%d r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); if (to) - gen_helper_mmu_write(cpu_env, tcg_const_tl(sr), cpu_R[dc->ra]); + gen_helper_mmu_write(cpu_env, tcg_const_i32(sr), cpu_R[dc->ra]); else - gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tcg_const_tl(sr)); + gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tcg_const_i32(sr)); return; } #endif @@ -511,19 +511,21 @@ static void dec_msr(DisasContext *dc) msr_write(dc, cpu_R[dc->ra]); break; case 0x3: - tcg_gen_mov_tl(cpu_SR[SR_EAR], cpu_R[dc->ra]); + tcg_gen_mov_i32(cpu_SR[SR_EAR], cpu_R[dc->ra]); break; case 0x5: - tcg_gen_mov_tl(cpu_SR[SR_ESR], cpu_R[dc->ra]); + tcg_gen_mov_i32(cpu_SR[SR_ESR], cpu_R[dc->ra]); break; case 0x7: - tcg_gen_andi_tl(cpu_SR[SR_FSR], cpu_R[dc->ra], 31); + tcg_gen_andi_i32(cpu_SR[SR_FSR], cpu_R[dc->ra], 31); break; case 0x800: - tcg_gen_st_tl(cpu_R[dc->ra], cpu_env, offsetof(CPUMBState, slr)); + tcg_gen_st_i32(cpu_R[dc->ra], + cpu_env, offsetof(CPUMBState, slr)); break; case 0x802: - tcg_gen_st_tl(cpu_R[dc->ra], cpu_env, offsetof(CPUMBState, shr)); + tcg_gen_st_i32(cpu_R[dc->ra], + cpu_env, offsetof(CPUMBState, shr)); break; default: cpu_abort(CPU(dc->cpu), "unknown mts reg %x\n", sr); @@ -534,28 +536,30 @@ static void dec_msr(DisasContext *dc) switch (sr) { case 0: - tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc); + tcg_gen_movi_i32(cpu_R[dc->rd], dc->pc); break; case 1: msr_read(dc, cpu_R[dc->rd]); break; case 0x3: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_EAR]); + tcg_gen_mov_i32(cpu_R[dc->rd], cpu_SR[SR_EAR]); break; case 0x5: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_ESR]); + tcg_gen_mov_i32(cpu_R[dc->rd], cpu_SR[SR_ESR]); break; case 0x7: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_FSR]); + tcg_gen_mov_i32(cpu_R[dc->rd], cpu_SR[SR_FSR]); break; case 0xb: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_BTR]); + tcg_gen_mov_i32(cpu_R[dc->rd], cpu_SR[SR_BTR]); break; case 0x800: - tcg_gen_ld_tl(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, slr)); + tcg_gen_ld_i32(cpu_R[dc->rd], + cpu_env, offsetof(CPUMBState, slr)); break; case 0x802: - tcg_gen_ld_tl(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, shr)); + tcg_gen_ld_i32(cpu_R[dc->rd], + cpu_env, offsetof(CPUMBState, shr)); break; case 0x2000: case 0x2001: @@ -571,7 +575,7 @@ static void dec_msr(DisasContext *dc) case 0x200b: case 0x200c: rn = sr & 0xf; - tcg_gen_ld_tl(cpu_R[dc->rd], + tcg_gen_ld_i32(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, pvr.regs[rn])); break; default: @@ -581,20 +585,20 @@ static void dec_msr(DisasContext *dc) } if (dc->rd == 0) { - tcg_gen_movi_tl(cpu_R[0], 0); + tcg_gen_movi_i32(cpu_R[0], 0); } } /* Multiplier unit. */ static void dec_mul(DisasContext *dc) { - TCGv tmp; + TCGv_i32 tmp; unsigned int subcode; if ((dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) && !dc->cpu->cfg.use_hw_mul) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -603,7 +607,7 @@ static void dec_mul(DisasContext *dc) if (dc->type_b) { LOG_DIS("muli r%d r%d %x\n", dc->rd, dc->ra, dc->imm); - tcg_gen_mul_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_mul_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); return; } @@ -612,29 +616,31 @@ static void dec_mul(DisasContext *dc) /* nop??? */ } - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i32(); switch (subcode) { case 0: LOG_DIS("mul r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mul_tl(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_mul_i32(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); break; case 1: LOG_DIS("mulh r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_muls2_tl(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_muls2_i32(tmp, cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); break; case 2: LOG_DIS("mulhsu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mulsu2_tl(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_mulsu2_i32(tmp, cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); break; case 3: LOG_DIS("mulhu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mulu2_tl(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_mulu2_i32(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); break; default: cpu_abort(CPU(dc->cpu), "unknown MUL insn %x\n", subcode); break; } - tcg_temp_free(tmp); + tcg_temp_free_i32(tmp); } /* Div unit. */ @@ -647,7 +653,7 @@ static void dec_div(DisasContext *dc) if ((dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) && !dc->cpu->cfg.use_div) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); } @@ -658,19 +664,19 @@ static void dec_div(DisasContext *dc) gen_helper_divs(cpu_R[dc->rd], cpu_env, *(dec_alu_op_b(dc)), cpu_R[dc->ra]); if (!dc->rd) - tcg_gen_movi_tl(cpu_R[dc->rd], 0); + tcg_gen_movi_i32(cpu_R[dc->rd], 0); } static void dec_barrel(DisasContext *dc) { - TCGv t0; + TCGv_i32 t0; unsigned int imm_w, imm_s; bool s, t, e = false, i = false; if ((dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) && !dc->cpu->cfg.use_barrel) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -709,28 +715,28 @@ static void dec_barrel(DisasContext *dc) imm_s, width); } } else { - t0 = tcg_temp_new(); + t0 = tcg_temp_new_i32(); - tcg_gen_mov_tl(t0, *(dec_alu_op_b(dc))); - tcg_gen_andi_tl(t0, t0, 31); + tcg_gen_mov_i32(t0, *(dec_alu_op_b(dc))); + tcg_gen_andi_i32(t0, t0, 31); if (s) { - tcg_gen_shl_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + tcg_gen_shl_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); } else { if (t) { - tcg_gen_sar_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + tcg_gen_sar_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); } else { - tcg_gen_shr_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + tcg_gen_shr_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); } } - tcg_temp_free(t0); + tcg_temp_free_i32(t0); } } static void dec_bit(DisasContext *dc) { CPUState *cs = CPU(dc->cpu); - TCGv t0; + TCGv_i32 t0; unsigned int op; int mem_index = cpu_mmu_index(&dc->cpu->env, false); @@ -738,16 +744,16 @@ static void dec_bit(DisasContext *dc) switch (op) { case 0x21: /* src. */ - t0 = tcg_temp_new(); + t0 = tcg_temp_new_i32(); LOG_DIS("src r%d r%d\n", dc->rd, dc->ra); - tcg_gen_andi_tl(t0, cpu_SR[SR_MSR], MSR_CC); + tcg_gen_andi_i32(t0, cpu_SR[SR_MSR], MSR_CC); write_carry(dc, cpu_R[dc->ra]); if (dc->rd) { - tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); - tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->rd], t0); + tcg_gen_shri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); + tcg_gen_or_i32(cpu_R[dc->rd], cpu_R[dc->rd], t0); } - tcg_temp_free(t0); + tcg_temp_free_i32(t0); break; case 0x1: @@ -759,9 +765,9 @@ static void dec_bit(DisasContext *dc) write_carry(dc, cpu_R[dc->ra]); if (dc->rd) { if (op == 0x41) - tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); + tcg_gen_shri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); else - tcg_gen_sari_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); + tcg_gen_sari_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); } break; case 0x60: @@ -780,7 +786,7 @@ static void dec_bit(DisasContext *dc) LOG_DIS("wdc r%d\n", dc->ra); if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -790,7 +796,7 @@ static void dec_bit(DisasContext *dc) LOG_DIS("wic r%d\n", dc->ra); if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -799,7 +805,7 @@ static void dec_bit(DisasContext *dc) if ((dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) && !dc->cpu->cfg.use_pcmp_instr) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); } if (dc->cpu->cfg.use_pcmp_instr) { @@ -827,22 +833,22 @@ static inline void sync_jmpstate(DisasContext *dc) { if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { if (dc->jmp == JMP_DIRECT) { - tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_movi_i32(env_btaken, 1); } dc->jmp = JMP_INDIRECT; - tcg_gen_movi_tl(env_btarget, dc->jmp_pc); + tcg_gen_movi_i32(env_btarget, dc->jmp_pc); } } static void dec_imm(DisasContext *dc) { LOG_DIS("imm %x\n", dc->imm << 16); - tcg_gen_movi_tl(env_imm, (dc->imm << 16)); + tcg_gen_movi_i32(env_imm, (dc->imm << 16)); dc->tb_flags |= IMM_FLAG; dc->clear_imm = 0; } -static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) +static inline TCGv_i32 *compute_ldst_addr(DisasContext *dc, TCGv_i32 *t) { bool extimm = dc->tb_flags & IMM_FLAG; /* Should be set to true if r1 is used by loadstores. */ @@ -866,8 +872,8 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) stackprot = true; } - *t = tcg_temp_new(); - tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]); + *t = tcg_temp_new_i32(); + tcg_gen_add_i32(*t, cpu_R[dc->ra], cpu_R[dc->rb]); if (stackprot) { gen_helper_stackprot(cpu_env, *t); @@ -879,12 +885,12 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) if (dc->imm == 0) { return &cpu_R[dc->ra]; } - *t = tcg_temp_new(); - tcg_gen_movi_tl(*t, (int32_t)((int16_t)dc->imm)); - tcg_gen_add_tl(*t, cpu_R[dc->ra], *t); + *t = tcg_temp_new_i32(); + tcg_gen_movi_i32(*t, (int32_t)((int16_t)dc->imm)); + tcg_gen_add_i32(*t, cpu_R[dc->ra], *t); } else { - *t = tcg_temp_new(); - tcg_gen_add_tl(*t, cpu_R[dc->ra], *(dec_alu_op_b(dc))); + *t = tcg_temp_new_i32(); + tcg_gen_add_i32(*t, cpu_R[dc->ra], *(dec_alu_op_b(dc))); } if (stackprot) { @@ -895,7 +901,7 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) static void dec_load(DisasContext *dc) { - TCGv t, v, *addr; + TCGv_i32 t, v, *addr; unsigned int size; bool rev = false, ex = false; TCGMemOp mop; @@ -913,7 +919,7 @@ static void dec_load(DisasContext *dc) if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -939,20 +945,20 @@ static void dec_load(DisasContext *dc) 01 -> 10 10 -> 10 11 -> 00 */ - TCGv low = tcg_temp_new(); + TCGv_i32 low = tcg_temp_new_i32(); /* Force addr into the temp. */ if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_mov_tl(t, *addr); + t = tcg_temp_new_i32(); + tcg_gen_mov_i32(t, *addr); addr = &t; } - tcg_gen_andi_tl(low, t, 3); - tcg_gen_sub_tl(low, tcg_const_tl(3), low); - tcg_gen_andi_tl(t, t, ~3); - tcg_gen_or_tl(t, t, low); - tcg_temp_free(low); + tcg_gen_andi_i32(low, t, 3); + tcg_gen_sub_i32(low, tcg_const_i32(3), low); + tcg_gen_andi_i32(t, t, ~3); + tcg_gen_or_i32(t, t, low); + tcg_temp_free_i32(low); break; } @@ -961,11 +967,11 @@ static void dec_load(DisasContext *dc) 10 -> 00. */ /* Force addr into the temp. */ if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_xori_tl(t, *addr, 2); + t = tcg_temp_new_i32(); + tcg_gen_xori_i32(t, *addr, 2); addr = &t; } else { - tcg_gen_xori_tl(t, t, 2); + tcg_gen_xori_i32(t, t, 2); } break; default: @@ -978,11 +984,11 @@ static void dec_load(DisasContext *dc) if (ex) { /* Force addr into the temp. */ if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_mov_tl(t, *addr); + t = tcg_temp_new_i32(); + tcg_gen_mov_i32(t, *addr); addr = &t; } - tcg_gen_andi_tl(t, t, ~3); + tcg_gen_andi_i32(t, t, ~3); } /* If we get a fault on a dslot, the jmpstate better be in sync. */ @@ -995,23 +1001,23 @@ static void dec_load(DisasContext *dc) * into v. If the load succeeds, we verify alignment of the * address and if that succeeds we write into the destination reg. */ - v = tcg_temp_new(); - tcg_gen_qemu_ld_tl(v, *addr, cpu_mmu_index(&dc->cpu->env, false), mop); + v = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(v, *addr, cpu_mmu_index(&dc->cpu->env, false), mop); if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); - gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd), - tcg_const_tl(0), tcg_const_tl(size - 1)); + tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc); + gen_helper_memalign(cpu_env, *addr, tcg_const_i32(dc->rd), + tcg_const_i32(0), tcg_const_i32(size - 1)); } if (ex) { - tcg_gen_mov_tl(env_res_addr, *addr); - tcg_gen_mov_tl(env_res_val, v); + tcg_gen_mov_i32(env_res_addr, *addr); + tcg_gen_mov_i32(env_res_val, v); } if (dc->rd) { - tcg_gen_mov_tl(cpu_R[dc->rd], v); + tcg_gen_mov_i32(cpu_R[dc->rd], v); } - tcg_temp_free(v); + tcg_temp_free_i32(v); if (ex) { /* lwx */ /* no support for AXI exclusive so always clear C */ @@ -1019,12 +1025,12 @@ static void dec_load(DisasContext *dc) } if (addr == &t) - tcg_temp_free(t); + tcg_temp_free_i32(t); } static void dec_store(DisasContext *dc) { - TCGv t, *addr, swx_addr; + TCGv_i32 t, *addr, swx_addr; TCGLabel *swx_skip = NULL; unsigned int size; bool rev = false, ex = false; @@ -1043,7 +1049,7 @@ static void dec_store(DisasContext *dc) if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -1055,31 +1061,31 @@ static void dec_store(DisasContext *dc) sync_jmpstate(dc); addr = compute_ldst_addr(dc, &t); - swx_addr = tcg_temp_local_new(); + swx_addr = tcg_temp_local_new_i32(); if (ex) { /* swx */ - TCGv tval; + TCGv_i32 tval; /* Force addr into the swx_addr. */ - tcg_gen_mov_tl(swx_addr, *addr); + tcg_gen_mov_i32(swx_addr, *addr); addr = &swx_addr; /* swx does not throw unaligned access errors, so force alignment */ - tcg_gen_andi_tl(swx_addr, swx_addr, ~3); + tcg_gen_andi_i32(swx_addr, swx_addr, ~3); write_carryi(dc, 1); swx_skip = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, env_res_addr, swx_addr, swx_skip); + tcg_gen_brcond_i32(TCG_COND_NE, env_res_addr, swx_addr, swx_skip); /* Compare the value loaded at lwx with current contents of the reserved location. FIXME: This only works for system emulation where we can expect this compare and the following write to be atomic. For user emulation we need to add atomicity between threads. */ - tval = tcg_temp_new(); - tcg_gen_qemu_ld_tl(tval, swx_addr, cpu_mmu_index(&dc->cpu->env, false), + tval = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(tval, swx_addr, cpu_mmu_index(&dc->cpu->env, false), MO_TEUL); - tcg_gen_brcond_tl(TCG_COND_NE, env_res_val, tval, swx_skip); + tcg_gen_brcond_i32(TCG_COND_NE, env_res_val, tval, swx_skip); write_carryi(dc, 0); - tcg_temp_free(tval); + tcg_temp_free_i32(tval); } if (rev && size != 4) { @@ -1091,20 +1097,20 @@ static void dec_store(DisasContext *dc) 01 -> 10 10 -> 10 11 -> 00 */ - TCGv low = tcg_temp_new(); + TCGv_i32 low = tcg_temp_new_i32(); /* Force addr into the temp. */ if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_mov_tl(t, *addr); + t = tcg_temp_new_i32(); + tcg_gen_mov_i32(t, *addr); addr = &t; } - tcg_gen_andi_tl(low, t, 3); - tcg_gen_sub_tl(low, tcg_const_tl(3), low); - tcg_gen_andi_tl(t, t, ~3); - tcg_gen_or_tl(t, t, low); - tcg_temp_free(low); + tcg_gen_andi_i32(low, t, 3); + tcg_gen_sub_i32(low, tcg_const_i32(3), low); + tcg_gen_andi_i32(t, t, ~3); + tcg_gen_or_i32(t, t, low); + tcg_temp_free_i32(low); break; } @@ -1113,11 +1119,11 @@ static void dec_store(DisasContext *dc) 10 -> 00. */ /* Force addr into the temp. */ if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_xori_tl(t, *addr, 2); + t = tcg_temp_new_i32(); + tcg_gen_xori_i32(t, *addr, 2); addr = &t; } else { - tcg_gen_xori_tl(t, t, 2); + tcg_gen_xori_i32(t, t, 2); } break; default: @@ -1125,51 +1131,52 @@ static void dec_store(DisasContext *dc) break; } } - tcg_gen_qemu_st_tl(cpu_R[dc->rd], *addr, cpu_mmu_index(&dc->cpu->env, false), mop); + tcg_gen_qemu_st_i32(cpu_R[dc->rd], *addr, + cpu_mmu_index(&dc->cpu->env, false), mop); /* Verify alignment if needed. */ if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc); /* FIXME: if the alignment is wrong, we should restore the value * in memory. One possible way to achieve this is to probe * the MMU prior to the memaccess, thay way we could put * the alignment checks in between the probe and the mem * access. */ - gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd), - tcg_const_tl(1), tcg_const_tl(size - 1)); + gen_helper_memalign(cpu_env, *addr, tcg_const_i32(dc->rd), + tcg_const_i32(1), tcg_const_i32(size - 1)); } if (ex) { gen_set_label(swx_skip); } - tcg_temp_free(swx_addr); + tcg_temp_free_i32(swx_addr); if (addr == &t) - tcg_temp_free(t); + tcg_temp_free_i32(t); } static inline void eval_cc(DisasContext *dc, unsigned int cc, - TCGv d, TCGv a, TCGv b) + TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { switch (cc) { case CC_EQ: - tcg_gen_setcond_tl(TCG_COND_EQ, d, a, b); + tcg_gen_setcond_i32(TCG_COND_EQ, d, a, b); break; case CC_NE: - tcg_gen_setcond_tl(TCG_COND_NE, d, a, b); + tcg_gen_setcond_i32(TCG_COND_NE, d, a, b); break; case CC_LT: - tcg_gen_setcond_tl(TCG_COND_LT, d, a, b); + tcg_gen_setcond_i32(TCG_COND_LT, d, a, b); break; case CC_LE: - tcg_gen_setcond_tl(TCG_COND_LE, d, a, b); + tcg_gen_setcond_i32(TCG_COND_LE, d, a, b); break; case CC_GE: - tcg_gen_setcond_tl(TCG_COND_GE, d, a, b); + tcg_gen_setcond_i32(TCG_COND_GE, d, a, b); break; case CC_GT: - tcg_gen_setcond_tl(TCG_COND_GT, d, a, b); + tcg_gen_setcond_i32(TCG_COND_GT, d, a, b); break; default: cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); @@ -1177,13 +1184,13 @@ static inline void eval_cc(DisasContext *dc, unsigned int cc, } } -static void eval_cond_jmp(DisasContext *dc, TCGv pc_true, TCGv pc_false) +static void eval_cond_jmp(DisasContext *dc, TCGv_i32 pc_true, TCGv_i32 pc_false) { TCGLabel *l1 = gen_new_label(); /* Conditional jmp. */ - tcg_gen_mov_tl(cpu_SR[SR_PC], pc_false); - tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, l1); - tcg_gen_mov_tl(cpu_SR[SR_PC], pc_true); + tcg_gen_mov_i32(cpu_SR[SR_PC], pc_false); + tcg_gen_brcondi_i32(TCG_COND_EQ, env_btaken, 0, l1); + tcg_gen_mov_i32(cpu_SR[SR_PC], pc_true); gen_set_label(l1); } @@ -1200,22 +1207,22 @@ static void dec_bcc(DisasContext *dc) if (dslot) { dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; - tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + tcg_gen_st_i32(tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)), cpu_env, offsetof(CPUMBState, bimm)); } if (dec_alu_op_b_is_small_imm(dc)) { int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */ - tcg_gen_movi_tl(env_btarget, dc->pc + offset); + tcg_gen_movi_i32(env_btarget, dc->pc + offset); dc->jmp = JMP_DIRECT_CC; dc->jmp_pc = dc->pc + offset; } else { dc->jmp = JMP_INDIRECT; - tcg_gen_movi_tl(env_btarget, dc->pc); - tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_movi_i32(env_btarget, dc->pc); + tcg_gen_add_i32(env_btarget, env_btarget, *(dec_alu_op_b(dc))); } - eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0)); + eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_i32(0)); } static void dec_br(DisasContext *dc) @@ -1241,7 +1248,7 @@ static void dec_br(DisasContext *dc) tcg_gen_st_i32(tmp_1, cpu_env, -offsetof(MicroBlazeCPU, env) +offsetof(CPUState, halted)); - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc + 4); + tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc + 4); gen_helper_raise_exception(cpu_env, tmp_hlt); tcg_temp_free_i32(tmp_hlt); tcg_temp_free_i32(tmp_1); @@ -1262,22 +1269,22 @@ static void dec_br(DisasContext *dc) if (dslot) { dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; - tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + tcg_gen_st_i32(tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)), cpu_env, offsetof(CPUMBState, bimm)); } if (link && dc->rd) - tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc); + tcg_gen_movi_i32(cpu_R[dc->rd], dc->pc); dc->jmp = JMP_INDIRECT; if (abs) { - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_mov_tl(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_movi_i32(env_btaken, 1); + tcg_gen_mov_i32(env_btarget, *(dec_alu_op_b(dc))); if (link && !dslot) { if (!(dc->tb_flags & IMM_FLAG) && (dc->imm == 8 || dc->imm == 0x18)) t_gen_raise_exception(dc, EXCP_BREAK); if (dc->imm == 0) { if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -1290,63 +1297,63 @@ static void dec_br(DisasContext *dc) dc->jmp = JMP_DIRECT; dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); } else { - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_movi_tl(env_btarget, dc->pc); - tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_movi_i32(env_btaken, 1); + tcg_gen_movi_i32(env_btarget, dc->pc); + tcg_gen_add_i32(env_btarget, env_btarget, *(dec_alu_op_b(dc))); } } } static inline void do_rti(DisasContext *dc) { - TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - tcg_gen_shri_tl(t0, cpu_SR[SR_MSR], 1); - tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_IE); - tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_shri_i32(t0, cpu_SR[SR_MSR], 1); + tcg_gen_ori_i32(t1, cpu_SR[SR_MSR], MSR_IE); + tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); - tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_tl(t1, t1, t0); + tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(t1, t1, t0); msr_write(dc, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); dc->tb_flags &= ~DRTI_FLAG; } static inline void do_rtb(DisasContext *dc) { - TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - tcg_gen_andi_tl(t1, cpu_SR[SR_MSR], ~MSR_BIP); - tcg_gen_shri_tl(t0, t1, 1); - tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_andi_i32(t1, cpu_SR[SR_MSR], ~MSR_BIP); + tcg_gen_shri_i32(t0, t1, 1); + tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); - tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_tl(t1, t1, t0); + tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(t1, t1, t0); msr_write(dc, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); dc->tb_flags &= ~DRTB_FLAG; } static inline void do_rte(DisasContext *dc) { - TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); - tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_EE); - tcg_gen_andi_tl(t1, t1, ~MSR_EIP); - tcg_gen_shri_tl(t0, t1, 1); - tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + tcg_gen_ori_i32(t1, cpu_SR[SR_MSR], MSR_EE); + tcg_gen_andi_i32(t1, t1, ~MSR_EIP); + tcg_gen_shri_i32(t0, t1, 1); + tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); - tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_tl(t1, t1, t0); + tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(t1, t1, t0); msr_write(dc, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); dc->tb_flags &= ~DRTE_FLAG; } @@ -1361,14 +1368,14 @@ static void dec_rts(DisasContext *dc) dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; - tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + tcg_gen_st_i32(tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)), cpu_env, offsetof(CPUMBState, bimm)); if (i_bit) { LOG_DIS("rtid ir=%x\n", dc->ir); if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); t_gen_raise_exception(dc, EXCP_HW_EXCP); } dc->tb_flags |= DRTI_FLAG; @@ -1376,7 +1383,7 @@ static void dec_rts(DisasContext *dc) LOG_DIS("rtbd ir=%x\n", dc->ir); if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); t_gen_raise_exception(dc, EXCP_HW_EXCP); } dc->tb_flags |= DRTB_FLAG; @@ -1384,7 +1391,7 @@ static void dec_rts(DisasContext *dc) LOG_DIS("rted ir=%x\n", dc->ir); if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); t_gen_raise_exception(dc, EXCP_HW_EXCP); } dc->tb_flags |= DRTE_FLAG; @@ -1392,14 +1399,14 @@ static void dec_rts(DisasContext *dc) LOG_DIS("rts ir=%x\n", dc->ir); dc->jmp = JMP_INDIRECT; - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_add_tl(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_movi_i32(env_btaken, 1); + tcg_gen_add_i32(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc))); } static int dec_check_fpuv2(DisasContext *dc) { if ((dc->cpu->cfg.use_fpu != 2) && (dc->tb_flags & MSR_EE_FLAG)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_FPU); t_gen_raise_exception(dc, EXCP_HW_EXCP); } return (dc->cpu->cfg.use_fpu == 2) ? 0 : PVR2_USE_FPU2_MASK; @@ -1412,7 +1419,7 @@ static void dec_fpu(DisasContext *dc) if ((dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) && !dc->cpu->cfg.use_fpu) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -1514,7 +1521,7 @@ static void dec_null(DisasContext *dc) { if ((dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -1533,29 +1540,29 @@ static void dec_stream(DisasContext *dc) dc->type_b ? "" : "d", dc->imm); if ((dc->tb_flags & MSR_EE_FLAG) && (mem_index == MMU_USER_IDX)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } - t_id = tcg_temp_new(); + t_id = tcg_temp_new_i32(); if (dc->type_b) { - tcg_gen_movi_tl(t_id, dc->imm & 0xf); + tcg_gen_movi_i32(t_id, dc->imm & 0xf); ctrl = dc->imm >> 10; } else { - tcg_gen_andi_tl(t_id, cpu_R[dc->rb], 0xf); + tcg_gen_andi_i32(t_id, cpu_R[dc->rb], 0xf); ctrl = dc->imm >> 5; } - t_ctrl = tcg_const_tl(ctrl); + t_ctrl = tcg_const_i32(ctrl); if (dc->rd == 0) { gen_helper_put(t_id, t_ctrl, cpu_R[dc->ra]); } else { gen_helper_get(cpu_R[dc->rd], t_id, t_ctrl); } - tcg_temp_free(t_id); - tcg_temp_free(t_ctrl); + tcg_temp_free_i32(t_id); + tcg_temp_free_i32(t_ctrl); } static struct decoder_info { @@ -1599,7 +1606,7 @@ static inline void decode(DisasContext *dc, uint32_t ir) if ((dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) && (dc->cpu->env.pvr.regs[2] & PVR2_OPCODE_0x0_ILL_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } @@ -1637,7 +1644,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) struct DisasContext ctx; struct DisasContext *dc = &ctx; uint32_t page_start, org_flags; - target_ulong npc; + uint32_t npc; int num_insns; int max_insns; @@ -1680,7 +1687,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) #if SIM_COMPAT if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc); gen_helper_debug(); } #endif @@ -1722,7 +1729,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) dc->tb_flags &= ~D_FLAG; /* If it is a direct jump, try direct chaining. */ if (dc->jmp == JMP_INDIRECT) { - eval_cond_jmp(dc, env_btarget, tcg_const_tl(dc->pc)); + eval_cond_jmp(dc, env_btarget, tcg_const_i32(dc->pc)); dc->is_jmp = DISAS_JUMP; } else if (dc->jmp == JMP_DIRECT) { t_sync_flags(dc); @@ -1732,7 +1739,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) TCGLabel *l1 = gen_new_label(); t_sync_flags(dc); /* Conditional jmp. */ - tcg_gen_brcondi_tl(TCG_COND_NE, env_btaken, 0, l1); + tcg_gen_brcondi_i32(TCG_COND_NE, env_btaken, 0, l1); gen_goto_tb(dc, 1, dc->pc); gen_set_label(l1); gen_goto_tb(dc, 0, dc->jmp_pc); @@ -1755,7 +1762,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { if (dc->tb_flags & D_FLAG) { dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + tcg_gen_movi_i32(cpu_SR[SR_PC], npc); sync_jmpstate(dc); } else npc = dc->jmp_pc; @@ -1767,7 +1774,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (dc->is_jmp == DISAS_NEXT && (dc->cpustate_changed || org_flags != dc->tb_flags)) { dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + tcg_gen_movi_i32(cpu_SR[SR_PC], npc); } t_sync_flags(dc); @@ -1775,7 +1782,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); if (dc->is_jmp != DISAS_JUMP) { - tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + tcg_gen_movi_i32(cpu_SR[SR_PC], npc); } gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); @@ -1849,34 +1856,34 @@ void mb_tcg_init(void) { int i; - env_debug = tcg_global_mem_new(cpu_env, + env_debug = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, debug), "debug0"); - env_iflags = tcg_global_mem_new(cpu_env, + env_iflags = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, iflags), "iflags"); - env_imm = tcg_global_mem_new(cpu_env, + env_imm = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, imm), "imm"); - env_btarget = tcg_global_mem_new(cpu_env, + env_btarget = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, btarget), "btarget"); - env_btaken = tcg_global_mem_new(cpu_env, + env_btaken = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, btaken), "btaken"); - env_res_addr = tcg_global_mem_new(cpu_env, + env_res_addr = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, res_addr), "res_addr"); - env_res_val = tcg_global_mem_new(cpu_env, + env_res_val = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, res_val), "res_val"); for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, + cpu_R[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, regs[i]), regnames[i]); } for (i = 0; i < ARRAY_SIZE(cpu_SR); i++) { - cpu_SR[i] = tcg_global_mem_new(cpu_env, + cpu_SR[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, sregs[i]), special_regnames[i]); } From a17f7c05f07aa3a825f7dc1ba22d70df16098d3c Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 09:46:28 +0200 Subject: [PATCH 0777/2380] target-microblaze: Remove USE_MMU PVR checks We already have a CPU property to control if a core has an MMU or not. Remove USE_MMU PVR checks in favor of looking at the property. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/helper.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index 387d4aca5a..a9f4ca93e3 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -54,21 +54,11 @@ int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; unsigned int hit; - unsigned int mmu_available; int r = 1; int prot; - mmu_available = 0; - if (cpu->cfg.use_mmu) { - mmu_available = 1; - if ((cpu->cfg.pvr == C_PVR_FULL) && - (env->pvr.regs[11] & PVR11_USE_MMU) != PVR11_USE_MMU) { - mmu_available = 0; - } - } - /* Translate if the MMU is available and enabled. */ - if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) { + if (cpu->cfg.use_mmu && (env->sregs[SR_MSR] & MSR_VM)) { uint32_t vaddr, paddr; struct microblaze_mmu_lookup lu; From 9e50a927b4e2e724849f6ec19fa55fe40e13d174 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 16:04:13 +0200 Subject: [PATCH 0778/2380] target-microblaze: Conditionalize setting of PVR11_USE_MMU Conditionalize setting of PVR11_USE_MMU on the use_mmu CPU property, otherwise we may incorrectly advertise an MMU via PVR when the core in fact has none. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 06476f6efc..a6f1ce6549 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -201,7 +201,8 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) PVR5_DCACHE_WRITEBACK_MASK : 0; env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family. */ - env->pvr.regs[11] = PVR11_USE_MMU | (16 << 17); + env->pvr.regs[11] = (cpu->cfg.use_mmu ? PVR11_USE_MMU : 0) | + 16 << 17; mcc->parent_realize(dev, errp); } From a2de5ca451354155a0d6a9b098cbdd44eabb2da5 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 22:16:57 +0200 Subject: [PATCH 0779/2380] target-microblaze: Bypass MMU with MMU_NOMMU_IDX Bypass MMU translation when mmu-index MMU_NOMMU_IDX is used. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index a9f4ca93e3..261dcc74c7 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -58,7 +58,8 @@ int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, int prot; /* Translate if the MMU is available and enabled. */ - if (cpu->cfg.use_mmu && (env->sregs[SR_MSR] & MSR_VM)) { + if (cpu->cfg.use_mmu && (env->sregs[SR_MSR] & MSR_VM) + && mmu_idx != MMU_NOMMU_IDX) { uint32_t vaddr, paddr; struct microblaze_mmu_lookup lu; From 0dc4af5c1a0e8d3f73b176f8fd3159e77a4c2492 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 16:12:56 +0200 Subject: [PATCH 0780/2380] target-microblaze: Make compute_ldst_addr always use a temp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make compute_ldst_addr always use a temp. This simplifies the code a bit in preparation for adding support for 64bit addresses. No functional change. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 111 ++++++++++++---------------------- 1 file changed, 37 insertions(+), 74 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 2e9a286af6..2a4546ec3d 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -848,7 +848,7 @@ static void dec_imm(DisasContext *dc) dc->clear_imm = 0; } -static inline TCGv_i32 *compute_ldst_addr(DisasContext *dc, TCGv_i32 *t) +static inline void compute_ldst_addr(DisasContext *dc, TCGv_i32 *t) { bool extimm = dc->tb_flags & IMM_FLAG; /* Should be set to true if r1 is used by loadstores. */ @@ -861,47 +861,47 @@ static inline TCGv_i32 *compute_ldst_addr(DisasContext *dc, TCGv_i32 *t) /* Treat the common cases first. */ if (!dc->type_b) { - /* If any of the regs is r0, return a ptr to the other. */ + /* If any of the regs is r0, set t to the value of the other reg. */ if (dc->ra == 0) { - return &cpu_R[dc->rb]; + tcg_gen_mov_i32(*t, cpu_R[dc->rb]); + return; } else if (dc->rb == 0) { - return &cpu_R[dc->ra]; + tcg_gen_mov_i32(*t, cpu_R[dc->ra]); + return; } if (dc->rb == 1 && dc->cpu->cfg.stackprot) { stackprot = true; } - *t = tcg_temp_new_i32(); tcg_gen_add_i32(*t, cpu_R[dc->ra], cpu_R[dc->rb]); if (stackprot) { gen_helper_stackprot(cpu_env, *t); } - return t; + return; } /* Immediate. */ if (!extimm) { if (dc->imm == 0) { - return &cpu_R[dc->ra]; + tcg_gen_mov_i32(*t, cpu_R[dc->ra]); + return; } - *t = tcg_temp_new_i32(); tcg_gen_movi_i32(*t, (int32_t)((int16_t)dc->imm)); tcg_gen_add_i32(*t, cpu_R[dc->ra], *t); } else { - *t = tcg_temp_new_i32(); tcg_gen_add_i32(*t, cpu_R[dc->ra], *(dec_alu_op_b(dc))); } if (stackprot) { gen_helper_stackprot(cpu_env, *t); } - return t; + return; } static void dec_load(DisasContext *dc) { - TCGv_i32 t, v, *addr; + TCGv_i32 v, addr; unsigned int size; bool rev = false, ex = false; TCGMemOp mop; @@ -928,7 +928,8 @@ static void dec_load(DisasContext *dc) ex ? "x" : ""); t_sync_flags(dc); - addr = compute_ldst_addr(dc, &t); + addr = tcg_temp_new_i32(); + compute_ldst_addr(dc, &addr); /* * When doing reverse accesses we need to do two things. @@ -947,17 +948,10 @@ static void dec_load(DisasContext *dc) 11 -> 00 */ TCGv_i32 low = tcg_temp_new_i32(); - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new_i32(); - tcg_gen_mov_i32(t, *addr); - addr = &t; - } - - tcg_gen_andi_i32(low, t, 3); + tcg_gen_andi_i32(low, addr, 3); tcg_gen_sub_i32(low, tcg_const_i32(3), low); - tcg_gen_andi_i32(t, t, ~3); - tcg_gen_or_i32(t, t, low); + tcg_gen_andi_i32(addr, addr, ~3); + tcg_gen_or_i32(addr, addr, low); tcg_temp_free_i32(low); break; } @@ -965,14 +959,7 @@ static void dec_load(DisasContext *dc) case 2: /* 00 -> 10 10 -> 00. */ - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new_i32(); - tcg_gen_xori_i32(t, *addr, 2); - addr = &t; - } else { - tcg_gen_xori_i32(t, t, 2); - } + tcg_gen_xori_i32(addr, addr, 2); break; default: cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); @@ -982,13 +969,7 @@ static void dec_load(DisasContext *dc) /* lwx does not throw unaligned access errors, so force alignment */ if (ex) { - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new_i32(); - tcg_gen_mov_i32(t, *addr); - addr = &t; - } - tcg_gen_andi_i32(t, t, ~3); + tcg_gen_andi_i32(addr, addr, ~3); } /* If we get a fault on a dslot, the jmpstate better be in sync. */ @@ -1002,16 +983,16 @@ static void dec_load(DisasContext *dc) * address and if that succeeds we write into the destination reg. */ v = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(v, *addr, cpu_mmu_index(&dc->cpu->env, false), mop); + tcg_gen_qemu_ld_i32(v, addr, cpu_mmu_index(&dc->cpu->env, false), mop); if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc); - gen_helper_memalign(cpu_env, *addr, tcg_const_i32(dc->rd), + gen_helper_memalign(cpu_env, addr, tcg_const_i32(dc->rd), tcg_const_i32(0), tcg_const_i32(size - 1)); } if (ex) { - tcg_gen_mov_i32(env_res_addr, *addr); + tcg_gen_mov_i32(env_res_addr, addr); tcg_gen_mov_i32(env_res_val, v); } if (dc->rd) { @@ -1024,13 +1005,12 @@ static void dec_load(DisasContext *dc) write_carryi(dc, 0); } - if (addr == &t) - tcg_temp_free_i32(t); + tcg_temp_free_i32(addr); } static void dec_store(DisasContext *dc) { - TCGv_i32 t, *addr, swx_addr; + TCGv_i32 addr; TCGLabel *swx_skip = NULL; unsigned int size; bool rev = false, ex = false; @@ -1059,21 +1039,19 @@ static void dec_store(DisasContext *dc) t_sync_flags(dc); /* If we get a fault on a dslot, the jmpstate better be in sync. */ sync_jmpstate(dc); - addr = compute_ldst_addr(dc, &t); + /* SWX needs a temp_local. */ + addr = ex ? tcg_temp_local_new_i32() : tcg_temp_new_i32(); + compute_ldst_addr(dc, &addr); - swx_addr = tcg_temp_local_new_i32(); if (ex) { /* swx */ TCGv_i32 tval; - /* Force addr into the swx_addr. */ - tcg_gen_mov_i32(swx_addr, *addr); - addr = &swx_addr; /* swx does not throw unaligned access errors, so force alignment */ - tcg_gen_andi_i32(swx_addr, swx_addr, ~3); + tcg_gen_andi_i32(addr, addr, ~3); write_carryi(dc, 1); swx_skip = gen_new_label(); - tcg_gen_brcond_i32(TCG_COND_NE, env_res_addr, swx_addr, swx_skip); + tcg_gen_brcond_i32(TCG_COND_NE, env_res_addr, addr, swx_skip); /* Compare the value loaded at lwx with current contents of the reserved location. @@ -1081,8 +1059,8 @@ static void dec_store(DisasContext *dc) this compare and the following write to be atomic. For user emulation we need to add atomicity between threads. */ tval = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(tval, swx_addr, cpu_mmu_index(&dc->cpu->env, false), - MO_TEUL); + tcg_gen_qemu_ld_i32(tval, addr, cpu_mmu_index(&dc->cpu->env, false), + MO_TEUL); tcg_gen_brcond_i32(TCG_COND_NE, env_res_val, tval, swx_skip); write_carryi(dc, 0); tcg_temp_free_i32(tval); @@ -1099,17 +1077,10 @@ static void dec_store(DisasContext *dc) 11 -> 00 */ TCGv_i32 low = tcg_temp_new_i32(); - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new_i32(); - tcg_gen_mov_i32(t, *addr); - addr = &t; - } - - tcg_gen_andi_i32(low, t, 3); + tcg_gen_andi_i32(low, addr, 3); tcg_gen_sub_i32(low, tcg_const_i32(3), low); - tcg_gen_andi_i32(t, t, ~3); - tcg_gen_or_i32(t, t, low); + tcg_gen_andi_i32(addr, addr, ~3); + tcg_gen_or_i32(addr, addr, low); tcg_temp_free_i32(low); break; } @@ -1118,20 +1089,14 @@ static void dec_store(DisasContext *dc) /* 00 -> 10 10 -> 00. */ /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new_i32(); - tcg_gen_xori_i32(t, *addr, 2); - addr = &t; - } else { - tcg_gen_xori_i32(t, t, 2); - } + tcg_gen_xori_i32(addr, addr, 2); break; default: cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); break; } } - tcg_gen_qemu_st_i32(cpu_R[dc->rd], *addr, + tcg_gen_qemu_st_i32(cpu_R[dc->rd], addr, cpu_mmu_index(&dc->cpu->env, false), mop); /* Verify alignment if needed. */ @@ -1143,17 +1108,15 @@ static void dec_store(DisasContext *dc) * the alignment checks in between the probe and the mem * access. */ - gen_helper_memalign(cpu_env, *addr, tcg_const_i32(dc->rd), + gen_helper_memalign(cpu_env, addr, tcg_const_i32(dc->rd), tcg_const_i32(1), tcg_const_i32(size - 1)); } if (ex) { gen_set_label(swx_skip); } - tcg_temp_free_i32(swx_addr); - if (addr == &t) - tcg_temp_free_i32(t); + tcg_temp_free_i32(addr); } static inline void eval_cc(DisasContext *dc, unsigned int cc, From 0a87e691b3924d5e3964dd1b77eb88b000dd4126 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 20:02:07 +0200 Subject: [PATCH 0781/2380] target-microblaze: Remove pointer indirection for ld/st addresses Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 2a4546ec3d..ee17334959 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -848,7 +848,7 @@ static void dec_imm(DisasContext *dc) dc->clear_imm = 0; } -static inline void compute_ldst_addr(DisasContext *dc, TCGv_i32 *t) +static inline void compute_ldst_addr(DisasContext *dc, TCGv_i32 t) { bool extimm = dc->tb_flags & IMM_FLAG; /* Should be set to true if r1 is used by loadstores. */ @@ -863,10 +863,10 @@ static inline void compute_ldst_addr(DisasContext *dc, TCGv_i32 *t) if (!dc->type_b) { /* If any of the regs is r0, set t to the value of the other reg. */ if (dc->ra == 0) { - tcg_gen_mov_i32(*t, cpu_R[dc->rb]); + tcg_gen_mov_i32(t, cpu_R[dc->rb]); return; } else if (dc->rb == 0) { - tcg_gen_mov_i32(*t, cpu_R[dc->ra]); + tcg_gen_mov_i32(t, cpu_R[dc->ra]); return; } @@ -874,27 +874,27 @@ static inline void compute_ldst_addr(DisasContext *dc, TCGv_i32 *t) stackprot = true; } - tcg_gen_add_i32(*t, cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_add_i32(t, cpu_R[dc->ra], cpu_R[dc->rb]); if (stackprot) { - gen_helper_stackprot(cpu_env, *t); + gen_helper_stackprot(cpu_env, t); } return; } /* Immediate. */ if (!extimm) { if (dc->imm == 0) { - tcg_gen_mov_i32(*t, cpu_R[dc->ra]); + tcg_gen_mov_i32(t, cpu_R[dc->ra]); return; } - tcg_gen_movi_i32(*t, (int32_t)((int16_t)dc->imm)); - tcg_gen_add_i32(*t, cpu_R[dc->ra], *t); + tcg_gen_movi_i32(t, (int32_t)((int16_t)dc->imm)); + tcg_gen_add_i32(t, cpu_R[dc->ra], t); } else { - tcg_gen_add_i32(*t, cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_add_i32(t, cpu_R[dc->ra], *(dec_alu_op_b(dc))); } if (stackprot) { - gen_helper_stackprot(cpu_env, *t); + gen_helper_stackprot(cpu_env, t); } return; } @@ -929,7 +929,7 @@ static void dec_load(DisasContext *dc) t_sync_flags(dc); addr = tcg_temp_new_i32(); - compute_ldst_addr(dc, &addr); + compute_ldst_addr(dc, addr); /* * When doing reverse accesses we need to do two things. @@ -1041,7 +1041,7 @@ static void dec_store(DisasContext *dc) sync_jmpstate(dc); /* SWX needs a temp_local. */ addr = ex ? tcg_temp_local_new_i32() : tcg_temp_new_i32(); - compute_ldst_addr(dc, &addr); + compute_ldst_addr(dc, addr); if (ex) { /* swx */ TCGv_i32 tval; From 403322ea6c383b3337fe3c52d9ed84958f94bcd1 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 20:20:25 +0200 Subject: [PATCH 0782/2380] target-microblaze: Use TCGv for load/store addresses Use TCGv for load/store addresses, allowing for future computation of 64-bit load/store address. No functional change. Acked-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.h | 2 +- target/microblaze/helper.h | 4 +- target/microblaze/op_helper.c | 11 +++-- target/microblaze/translate.c | 78 +++++++++++++++++++---------------- 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 2304c24b7d..1593496997 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -250,7 +250,7 @@ struct CPUMBState { /* lwx/swx reserved address */ #define RES_ADDR_NONE 0xffffffff /* Use 0xffffffff to indicate no reservation */ - uint32_t res_addr; + target_ulong res_addr; uint32_t res_val; /* Internal flags. */ diff --git a/target/microblaze/helper.h b/target/microblaze/helper.h index 71a6c0858d..ce70353936 100644 --- a/target/microblaze/helper.h +++ b/target/microblaze/helper.h @@ -29,8 +29,8 @@ DEF_HELPER_2(mmu_read, i32, env, i32) DEF_HELPER_3(mmu_write, void, env, i32, i32) #endif -DEF_HELPER_5(memalign, void, env, i32, i32, i32, i32) -DEF_HELPER_2(stackprot, void, env, i32) +DEF_HELPER_5(memalign, void, env, tl, i32, i32, i32) +DEF_HELPER_2(stackprot, void, env, tl) DEF_HELPER_2(get, i32, i32, i32) DEF_HELPER_3(put, void, i32, i32, i32) diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index 1b4fe796e7..f5e851e38d 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -439,12 +439,14 @@ uint32_t helper_pcmpbf(uint32_t a, uint32_t b) return 0; } -void helper_memalign(CPUMBState *env, uint32_t addr, uint32_t dr, uint32_t wr, +void helper_memalign(CPUMBState *env, target_ulong addr, + uint32_t dr, uint32_t wr, uint32_t mask) { if (addr & mask) { qemu_log_mask(CPU_LOG_INT, - "unaligned access addr=%x mask=%x, wr=%d dr=r%d\n", + "unaligned access addr=" TARGET_FMT_lx + " mask=%x, wr=%d dr=r%d\n", addr, mask, wr, dr); env->sregs[SR_EAR] = addr; env->sregs[SR_ESR] = ESR_EC_UNALIGNED_DATA | (wr << 10) \ @@ -459,10 +461,11 @@ void helper_memalign(CPUMBState *env, uint32_t addr, uint32_t dr, uint32_t wr, } } -void helper_stackprot(CPUMBState *env, uint32_t addr) +void helper_stackprot(CPUMBState *env, target_ulong addr) { if (addr < env->slr || addr > env->shr) { - qemu_log_mask(CPU_LOG_INT, "Stack protector violation at %x %x %x\n", + qemu_log_mask(CPU_LOG_INT, "Stack protector violation at " + TARGET_FMT_lx " %x %x\n", addr, env->slr, env->shr); env->sregs[SR_EAR] = addr; env->sregs[SR_ESR] = ESR_EC_STACKPROT; diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index ee17334959..b36092b6fe 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -59,7 +59,7 @@ static TCGv_i32 env_imm; static TCGv_i32 env_btaken; static TCGv_i32 env_btarget; static TCGv_i32 env_iflags; -static TCGv_i32 env_res_addr; +static TCGv env_res_addr; static TCGv_i32 env_res_val; #include "exec/gen-icount.h" @@ -848,11 +848,12 @@ static void dec_imm(DisasContext *dc) dc->clear_imm = 0; } -static inline void compute_ldst_addr(DisasContext *dc, TCGv_i32 t) +static inline void compute_ldst_addr(DisasContext *dc, TCGv t) { bool extimm = dc->tb_flags & IMM_FLAG; /* Should be set to true if r1 is used by loadstores. */ bool stackprot = false; + TCGv_i32 t32; /* All load/stores use ra. */ if (dc->ra == 1 && dc->cpu->cfg.stackprot) { @@ -863,10 +864,10 @@ static inline void compute_ldst_addr(DisasContext *dc, TCGv_i32 t) if (!dc->type_b) { /* If any of the regs is r0, set t to the value of the other reg. */ if (dc->ra == 0) { - tcg_gen_mov_i32(t, cpu_R[dc->rb]); + tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); return; } else if (dc->rb == 0) { - tcg_gen_mov_i32(t, cpu_R[dc->ra]); + tcg_gen_extu_i32_tl(t, cpu_R[dc->ra]); return; } @@ -874,7 +875,10 @@ static inline void compute_ldst_addr(DisasContext *dc, TCGv_i32 t) stackprot = true; } - tcg_gen_add_i32(t, cpu_R[dc->ra], cpu_R[dc->rb]); + t32 = tcg_temp_new_i32(); + tcg_gen_add_i32(t32, cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_extu_i32_tl(t, t32); + tcg_temp_free_i32(t32); if (stackprot) { gen_helper_stackprot(cpu_env, t); @@ -882,16 +886,19 @@ static inline void compute_ldst_addr(DisasContext *dc, TCGv_i32 t) return; } /* Immediate. */ + t32 = tcg_temp_new_i32(); if (!extimm) { if (dc->imm == 0) { - tcg_gen_mov_i32(t, cpu_R[dc->ra]); - return; + tcg_gen_mov_i32(t32, cpu_R[dc->ra]); + } else { + tcg_gen_movi_i32(t32, (int32_t)((int16_t)dc->imm)); + tcg_gen_add_i32(t32, cpu_R[dc->ra], t32); } - tcg_gen_movi_i32(t, (int32_t)((int16_t)dc->imm)); - tcg_gen_add_i32(t, cpu_R[dc->ra], t); } else { - tcg_gen_add_i32(t, cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_add_i32(t32, cpu_R[dc->ra], *(dec_alu_op_b(dc))); } + tcg_gen_extu_i32_tl(t, t32); + tcg_temp_free_i32(t32); if (stackprot) { gen_helper_stackprot(cpu_env, t); @@ -901,7 +908,8 @@ static inline void compute_ldst_addr(DisasContext *dc, TCGv_i32 t) static void dec_load(DisasContext *dc) { - TCGv_i32 v, addr; + TCGv_i32 v; + TCGv addr; unsigned int size; bool rev = false, ex = false; TCGMemOp mop; @@ -928,7 +936,7 @@ static void dec_load(DisasContext *dc) ex ? "x" : ""); t_sync_flags(dc); - addr = tcg_temp_new_i32(); + addr = tcg_temp_new(); compute_ldst_addr(dc, addr); /* @@ -946,20 +954,20 @@ static void dec_load(DisasContext *dc) 01 -> 10 10 -> 10 11 -> 00 */ - TCGv_i32 low = tcg_temp_new_i32(); + TCGv low = tcg_temp_new(); - tcg_gen_andi_i32(low, addr, 3); - tcg_gen_sub_i32(low, tcg_const_i32(3), low); - tcg_gen_andi_i32(addr, addr, ~3); - tcg_gen_or_i32(addr, addr, low); - tcg_temp_free_i32(low); + tcg_gen_andi_tl(low, addr, 3); + tcg_gen_sub_tl(low, tcg_const_tl(3), low); + tcg_gen_andi_tl(addr, addr, ~3); + tcg_gen_or_tl(addr, addr, low); + tcg_temp_free(low); break; } case 2: /* 00 -> 10 10 -> 00. */ - tcg_gen_xori_i32(addr, addr, 2); + tcg_gen_xori_tl(addr, addr, 2); break; default: cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); @@ -969,7 +977,7 @@ static void dec_load(DisasContext *dc) /* lwx does not throw unaligned access errors, so force alignment */ if (ex) { - tcg_gen_andi_i32(addr, addr, ~3); + tcg_gen_andi_tl(addr, addr, ~3); } /* If we get a fault on a dslot, the jmpstate better be in sync. */ @@ -992,7 +1000,7 @@ static void dec_load(DisasContext *dc) } if (ex) { - tcg_gen_mov_i32(env_res_addr, addr); + tcg_gen_mov_tl(env_res_addr, addr); tcg_gen_mov_i32(env_res_val, v); } if (dc->rd) { @@ -1005,12 +1013,12 @@ static void dec_load(DisasContext *dc) write_carryi(dc, 0); } - tcg_temp_free_i32(addr); + tcg_temp_free(addr); } static void dec_store(DisasContext *dc) { - TCGv_i32 addr; + TCGv addr; TCGLabel *swx_skip = NULL; unsigned int size; bool rev = false, ex = false; @@ -1040,18 +1048,18 @@ static void dec_store(DisasContext *dc) /* If we get a fault on a dslot, the jmpstate better be in sync. */ sync_jmpstate(dc); /* SWX needs a temp_local. */ - addr = ex ? tcg_temp_local_new_i32() : tcg_temp_new_i32(); + addr = ex ? tcg_temp_local_new() : tcg_temp_new(); compute_ldst_addr(dc, addr); if (ex) { /* swx */ TCGv_i32 tval; /* swx does not throw unaligned access errors, so force alignment */ - tcg_gen_andi_i32(addr, addr, ~3); + tcg_gen_andi_tl(addr, addr, ~3); write_carryi(dc, 1); swx_skip = gen_new_label(); - tcg_gen_brcond_i32(TCG_COND_NE, env_res_addr, addr, swx_skip); + tcg_gen_brcond_tl(TCG_COND_NE, env_res_addr, addr, swx_skip); /* Compare the value loaded at lwx with current contents of the reserved location. @@ -1075,13 +1083,13 @@ static void dec_store(DisasContext *dc) 01 -> 10 10 -> 10 11 -> 00 */ - TCGv_i32 low = tcg_temp_new_i32(); + TCGv low = tcg_temp_new(); - tcg_gen_andi_i32(low, addr, 3); - tcg_gen_sub_i32(low, tcg_const_i32(3), low); - tcg_gen_andi_i32(addr, addr, ~3); - tcg_gen_or_i32(addr, addr, low); - tcg_temp_free_i32(low); + tcg_gen_andi_tl(low, addr, 3); + tcg_gen_sub_tl(low, tcg_const_tl(3), low); + tcg_gen_andi_tl(addr, addr, ~3); + tcg_gen_or_tl(addr, addr, low); + tcg_temp_free(low); break; } @@ -1089,7 +1097,7 @@ static void dec_store(DisasContext *dc) /* 00 -> 10 10 -> 00. */ /* Force addr into the temp. */ - tcg_gen_xori_i32(addr, addr, 2); + tcg_gen_xori_tl(addr, addr, 2); break; default: cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); @@ -1116,7 +1124,7 @@ static void dec_store(DisasContext *dc) gen_set_label(swx_skip); } - tcg_temp_free_i32(addr); + tcg_temp_free(addr); } static inline void eval_cc(DisasContext *dc, unsigned int cc, @@ -1834,7 +1842,7 @@ void mb_tcg_init(void) env_btaken = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, btaken), "btaken"); - env_res_addr = tcg_global_mem_new_i32(cpu_env, + env_res_addr = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, res_addr), "res_addr"); env_res_val = tcg_global_mem_new_i32(cpu_env, From 0031eef23ac0850cb04f6932ba8c42d2d6e712f2 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 14 Apr 2018 14:03:06 +0200 Subject: [PATCH 0783/2380] target-microblaze: Name special registers we support Name special registers we support. Reviewed-by: Alistair Francis Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index b36092b6fe..381f25348c 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -105,8 +105,8 @@ static const char *regnames[] = static const char *special_regnames[] = { - "rpc", "rmsr", "sr2", "sr3", "sr4", "sr5", "sr6", "sr7", - "sr8", "sr9", "sr10", "sr11", "sr12", "sr13" + "rpc", "rmsr", "sr2", "rear", "sr4", "resr", "sr6", "rfsr", + "sr8", "sr9", "sr10", "rbtr", "sr12", "redr" }; static inline void t_sync_flags(DisasContext *dc) From bdfc1e886939f06b94f4fee3105a41dbd20a97db Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 23:02:41 +0200 Subject: [PATCH 0784/2380] target-microblaze: Break out trap_userspace() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Break out trap_userspace() to avoid open coding it everywhere. For privileged insns, we now always stop translation of the current insn for cores without exceptions. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 76 +++++++++++++---------------------- 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 381f25348c..ffdb36cf94 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -179,6 +179,22 @@ static void write_carryi(DisasContext *dc, bool carry) tcg_temp_free_i32(t0); } +/* + * Returns true if the insn is illegal in userspace. + * If exceptions are enabled, an exception is raised. + */ +static bool trap_userspace(DisasContext *dc, bool cond) +{ + int mem_index = cpu_mmu_index(&dc->cpu->env, false); + bool cond_user = cond && mem_index == MMU_USER_IDX; + + if (cond_user && (dc->tb_flags & MSR_EE_FLAG)) { + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + t_gen_raise_exception(dc, EXCP_HW_EXCP); + } + return cond_user; +} + /* True if ALU operand b is a small immediate that may deserve faster treatment. */ static inline int dec_alu_op_b_is_small_imm(DisasContext *dc) @@ -432,7 +448,6 @@ static void dec_msr(DisasContext *dc) CPUState *cs = CPU(dc->cpu); TCGv_i32 t0, t1; unsigned int sr, to, rn; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); sr = dc->imm & ((1 << 14) - 1); to = dc->imm & (1 << 14); @@ -452,10 +467,7 @@ static void dec_msr(DisasContext *dc) return; } - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX && (dc->imm != 4 && dc->imm != 0)) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_userspace(dc, dc->imm != 4 && dc->imm != 0)) { return; } @@ -480,13 +492,8 @@ static void dec_msr(DisasContext *dc) return; } - if (to) { - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + if (trap_userspace(dc, to)) { + return; } #if !defined(CONFIG_USER_ONLY) @@ -738,7 +745,6 @@ static void dec_bit(DisasContext *dc) CPUState *cs = CPU(dc->cpu); TCGv_i32 t0; unsigned int op; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); op = dc->ir & ((1 << 9) - 1); switch (op) { @@ -784,22 +790,12 @@ static void dec_bit(DisasContext *dc) case 0x76: /* wdc. */ LOG_DIS("wdc r%d\n", dc->ra); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + trap_userspace(dc, true); break; case 0x68: /* wic. */ LOG_DIS("wic r%d\n", dc->ra); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + trap_userspace(dc, true); break; case 0xe0: if ((dc->tb_flags & MSR_EE_FLAG) @@ -1199,7 +1195,6 @@ static void dec_bcc(DisasContext *dc) static void dec_br(DisasContext *dc) { unsigned int dslot, link, abs, mbar; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); dslot = dc->ir & (1 << 20); abs = dc->ir & (1 << 19); @@ -1254,9 +1249,7 @@ static void dec_br(DisasContext *dc) if (!(dc->tb_flags & IMM_FLAG) && (dc->imm == 8 || dc->imm == 0x18)) t_gen_raise_exception(dc, EXCP_BREAK); if (dc->imm == 0) { - if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_userspace(dc, true)) { return; } @@ -1331,12 +1324,15 @@ static inline void do_rte(DisasContext *dc) static void dec_rts(DisasContext *dc) { unsigned int b_bit, i_bit, e_bit; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); i_bit = dc->ir & (1 << 21); b_bit = dc->ir & (1 << 22); e_bit = dc->ir & (1 << 23); + if (trap_userspace(dc, i_bit || b_bit || e_bit)) { + return; + } + dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; tcg_gen_st_i32(tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)), @@ -1344,27 +1340,12 @@ static void dec_rts(DisasContext *dc) if (i_bit) { LOG_DIS("rtid ir=%x\n", dc->ir); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } dc->tb_flags |= DRTI_FLAG; } else if (b_bit) { LOG_DIS("rtbd ir=%x\n", dc->ir); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } dc->tb_flags |= DRTB_FLAG; } else if (e_bit) { LOG_DIS("rted ir=%x\n", dc->ir); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } dc->tb_flags |= DRTE_FLAG; } else LOG_DIS("rts ir=%x\n", dc->ir); @@ -1503,16 +1484,13 @@ static void dec_null(DisasContext *dc) /* Insns connected to FSL or AXI stream attached devices. */ static void dec_stream(DisasContext *dc) { - int mem_index = cpu_mmu_index(&dc->cpu->env, false); TCGv_i32 t_id, t_ctrl; int ctrl; LOG_DIS("%s%s imm=%x\n", dc->rd ? "get" : "put", dc->type_b ? "" : "d", dc->imm); - if ((dc->tb_flags & MSR_EE_FLAG) && (mem_index == MMU_USER_IDX)) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_userspace(dc, true)) { return; } From 9ba8cd452bb48ad63e2878f8ebef4cfb18f46812 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 14 Apr 2018 18:41:24 +0200 Subject: [PATCH 0785/2380] target-microblaze: Break out trap_illegal() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Break out trap_illegal() to handle illegal operation traps. We now generally stop translation of the current insn if it's not valid. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 75 +++++++++++++---------------------- 1 file changed, 27 insertions(+), 48 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index ffdb36cf94..d63226db8f 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -179,6 +179,20 @@ static void write_carryi(DisasContext *dc, bool carry) tcg_temp_free_i32(t0); } +/* + * Returns true if the insn an illegal operation. + * If exceptions are enabled, an exception is raised. + */ +static bool trap_illegal(DisasContext *dc, bool cond) +{ + if (cond && (dc->tb_flags & MSR_EE_FLAG) + && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { + tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + t_gen_raise_exception(dc, EXCP_HW_EXCP); + } + return cond; +} + /* * Returns true if the insn is illegal in userspace. * If exceptions are enabled, an exception is raised. @@ -344,11 +358,8 @@ static void dec_pattern(DisasContext *dc) { unsigned int mode; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_pcmp_instr) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_pcmp_instr)) { + return; } mode = dc->opcode & 3; @@ -602,11 +613,7 @@ static void dec_mul(DisasContext *dc) TCGv_i32 tmp; unsigned int subcode; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_hw_mul) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_hw_mul)) { return; } @@ -658,10 +665,8 @@ static void dec_div(DisasContext *dc) u = dc->imm & 2; LOG_DIS("div\n"); - if ((dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_div) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_div)) { + return; } if (u) @@ -680,11 +685,7 @@ static void dec_barrel(DisasContext *dc) unsigned int imm_w, imm_s; bool s, t, e = false, i = false; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_barrel) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_barrel)) { return; } @@ -798,11 +799,8 @@ static void dec_bit(DisasContext *dc) trap_userspace(dc, true); break; case 0xe0: - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_pcmp_instr) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_pcmp_instr)) { + return; } if (dc->cpu->cfg.use_pcmp_instr) { tcg_gen_clzi_i32(cpu_R[dc->rd], cpu_R[dc->ra], 32); @@ -921,10 +919,7 @@ static void dec_load(DisasContext *dc) mop ^= MO_BSWAP; } - if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, size > 4)) { return; } @@ -1031,10 +1026,7 @@ static void dec_store(DisasContext *dc) mop ^= MO_BSWAP; } - if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, size > 4)) { return; } @@ -1368,11 +1360,7 @@ static void dec_fpu(DisasContext *dc) { unsigned int fpu_insn; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_fpu) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_fpu)) { return; } @@ -1471,10 +1459,7 @@ static void dec_fpu(DisasContext *dc) static void dec_null(DisasContext *dc) { - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, true)) { return; } qemu_log_mask(LOG_GUEST_ERROR, "unknown insn pc=%x opc=%x\n", dc->pc, dc->opcode); @@ -1552,13 +1537,7 @@ static inline void decode(DisasContext *dc, uint32_t ir) if (dc->ir) dc->nr_nops = 0; else { - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && (dc->cpu->env.pvr.regs[2] & PVR2_OPCODE_0x0_ILL_MASK)) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + trap_illegal(dc, dc->cpu->env.pvr.regs[2] & PVR2_OPCODE_0x0_ILL_MASK); LOG_DIS("nr_nops=%d\t", dc->nr_nops); dc->nr_nops++; From 2023e9a3bcb514a5fd41644c63825109c286e4ef Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 14 Apr 2018 15:17:52 +0200 Subject: [PATCH 0786/2380] target-microblaze: dec_msr: Use bool and extract32 Use bool and extract32 to represent the to, clr and clrset flags. No functional change. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index d63226db8f..e322c82c06 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -458,17 +458,20 @@ static void dec_msr(DisasContext *dc) { CPUState *cs = CPU(dc->cpu); TCGv_i32 t0, t1; - unsigned int sr, to, rn; + unsigned int sr, rn; + bool to, clrset; - sr = dc->imm & ((1 << 14) - 1); - to = dc->imm & (1 << 14); + sr = extract32(dc->imm, 0, 14); + to = extract32(dc->imm, 14, 1); + clrset = extract32(dc->imm, 15, 1) == 0; dc->type_b = 1; - if (to) + if (to) { dc->cpustate_changed = 1; + } /* msrclr and msrset. */ - if (!(dc->imm & (1 << 15))) { - unsigned int clr = dc->ir & (1 << 16); + if (clrset) { + bool clr = extract32(dc->ir, 16, 1); LOG_DIS("msr%s r%d imm=%x\n", clr ? "clr" : "set", dc->rd, dc->imm); From 351527b712f74749c23fdaf47be227903f0c2592 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 14 Apr 2018 15:46:20 +0200 Subject: [PATCH 0787/2380] target-microblaze: dec_msr: Reuse more code when reg-decoding Reuse more code when decoding register numbers. No functional changes. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 38 +++++++++-------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index e322c82c06..6a270fbece 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -531,11 +531,9 @@ static void dec_msr(DisasContext *dc) case 1: msr_write(dc, cpu_R[dc->ra]); break; - case 0x3: - tcg_gen_mov_i32(cpu_SR[SR_EAR], cpu_R[dc->ra]); - break; - case 0x5: - tcg_gen_mov_i32(cpu_SR[SR_ESR], cpu_R[dc->ra]); + case SR_EAR: + case SR_ESR: + tcg_gen_mov_i32(cpu_SR[sr], cpu_R[dc->ra]); break; case 0x7: tcg_gen_andi_i32(cpu_SR[SR_FSR], cpu_R[dc->ra], 31); @@ -562,17 +560,11 @@ static void dec_msr(DisasContext *dc) case 1: msr_read(dc, cpu_R[dc->rd]); break; - case 0x3: - tcg_gen_mov_i32(cpu_R[dc->rd], cpu_SR[SR_EAR]); - break; - case 0x5: - tcg_gen_mov_i32(cpu_R[dc->rd], cpu_SR[SR_ESR]); - break; - case 0x7: - tcg_gen_mov_i32(cpu_R[dc->rd], cpu_SR[SR_FSR]); - break; - case 0xb: - tcg_gen_mov_i32(cpu_R[dc->rd], cpu_SR[SR_BTR]); + case SR_EAR: + case SR_ESR: + case SR_FSR: + case SR_BTR: + tcg_gen_mov_i32(cpu_R[dc->rd], cpu_SR[sr]); break; case 0x800: tcg_gen_ld_i32(cpu_R[dc->rd], @@ -582,19 +574,7 @@ static void dec_msr(DisasContext *dc) tcg_gen_ld_i32(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, shr)); break; - case 0x2000: - case 0x2001: - case 0x2002: - case 0x2003: - case 0x2004: - case 0x2005: - case 0x2006: - case 0x2007: - case 0x2008: - case 0x2009: - case 0x200a: - case 0x200b: - case 0x200c: + case 0x2000 ... 0x200c: rn = sr & 0xf; tcg_gen_ld_i32(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, pvr.regs[rn])); From ab6dd3808d52b96347a4595f9da77c46df1a5e1d Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 14 Apr 2018 17:42:24 +0200 Subject: [PATCH 0788/2380] target-microblaze: dec_msr: Fix MTS to FSR Fix moves to FSR. Not only bit 31 is accessible. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 6a270fbece..6f2cafa88a 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -533,11 +533,9 @@ static void dec_msr(DisasContext *dc) break; case SR_EAR: case SR_ESR: + case SR_FSR: tcg_gen_mov_i32(cpu_SR[sr], cpu_R[dc->ra]); break; - case 0x7: - tcg_gen_andi_i32(cpu_SR[SR_FSR], cpu_R[dc->ra], 31); - break; case 0x800: tcg_gen_st_i32(cpu_R[dc->ra], cpu_env, offsetof(CPUMBState, slr)); From 0a22f8cf3ec1716865d635688fbfb31402c0ba7a Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 14 Apr 2018 17:59:29 +0200 Subject: [PATCH 0789/2380] target-microblaze: Make special registers 64-bit Extend special registers to 64-bits. This is in preparation for MFSE/MTSE, moves to and from extended special registers. Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- linux-user/microblaze/cpu_loop.c | 4 +- target/microblaze/cpu.h | 2 +- target/microblaze/helper.c | 15 +++-- target/microblaze/mmu.c | 3 +- target/microblaze/op_helper.c | 9 +-- target/microblaze/translate.c | 99 +++++++++++++++++--------------- 6 files changed, 72 insertions(+), 60 deletions(-) diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 5ffb83dea2..5af12d5b21 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -105,8 +105,8 @@ void cpu_loop(CPUMBState *env) queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; default: - printf ("Unhandled hw-exception: 0x%x\n", - env->sregs[SR_ESR] & ESR_EC_MASK); + printf("Unhandled hw-exception: 0x%" PRIx64 "\n", + env->sregs[SR_ESR] & ESR_EC_MASK); cpu_dump_state(cs, stderr, fprintf, 0); exit(EXIT_FAILURE); break; diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 1593496997..215f42b384 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -243,7 +243,7 @@ struct CPUMBState { uint32_t imm; uint32_t regs[32]; - uint32_t sregs[14]; + uint64_t sregs[14]; float_status fp_status; /* Stack protectors. Yes, it's a hw feature. */ uint32_t slr, shr; diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index 261dcc74c7..985bdae8d1 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -143,7 +143,8 @@ void mb_cpu_do_interrupt(CPUState *cs) env->sregs[SR_MSR] |= MSR_EIP; qemu_log_mask(CPU_LOG_INT, - "hw exception at pc=%x ear=%x esr=%x iflags=%x\n", + "hw exception at pc=%" PRIx64 " ear=%" PRIx64 " " + "esr=%" PRIx64 " iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_EAR], env->sregs[SR_ESR], env->iflags); log_cpu_state_mask(CPU_LOG_INT, cs, 0); @@ -166,7 +167,8 @@ void mb_cpu_do_interrupt(CPUState *cs) /* was the branch immprefixed?. */ if (env->bimm) { qemu_log_mask(CPU_LOG_INT, - "bimm exception at pc=%x iflags=%x\n", + "bimm exception at pc=%" PRIx64 " " + "iflags=%x\n", env->sregs[SR_PC], env->iflags); env->regs[17] -= 4; log_cpu_state_mask(CPU_LOG_INT, cs, 0); @@ -184,7 +186,8 @@ void mb_cpu_do_interrupt(CPUState *cs) env->sregs[SR_MSR] |= MSR_EIP; qemu_log_mask(CPU_LOG_INT, - "exception at pc=%x ear=%x iflags=%x\n", + "exception at pc=%" PRIx64 " ear=%" PRIx64 " " + "iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags); log_cpu_state_mask(CPU_LOG_INT, cs, 0); env->iflags &= ~(IMM_FLAG | D_FLAG); @@ -221,7 +224,8 @@ void mb_cpu_do_interrupt(CPUState *cs) } #endif qemu_log_mask(CPU_LOG_INT, - "interrupt at pc=%x msr=%x %x iflags=%x\n", + "interrupt at pc=%" PRIx64 " msr=%" PRIx64 " %x " + "iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \ @@ -239,7 +243,8 @@ void mb_cpu_do_interrupt(CPUState *cs) assert(!(env->iflags & D_FLAG)); t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; qemu_log_mask(CPU_LOG_INT, - "break at pc=%x msr=%x %x iflags=%x\n", + "break at pc=%" PRIx64 " msr=%" PRIx64 " %x " + "iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); log_cpu_state_mask(CPU_LOG_INT, cs, 0); env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 9d5e6aa8a5..0019ebd18f 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -240,7 +240,8 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) i = env->mmu.regs[MMU_R_TLBX] & 0xff; if (rn == MMU_R_TLBHI) { if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0)) - qemu_log_mask(LOG_GUEST_ERROR, "invalidating index %x at pc=%x\n", + qemu_log_mask(LOG_GUEST_ERROR, + "invalidating index %x at pc=%" PRIx64 "\n", i, env->sregs[SR_PC]); env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff; mmu_flush_idx(env, i); diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index f5e851e38d..4dc3aff84b 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -94,16 +94,17 @@ void helper_debug(CPUMBState *env) { int i; - qemu_log("PC=%8.8x\n", env->sregs[SR_PC]); - qemu_log("rmsr=%x resr=%x rear=%x debug[%x] imm=%x iflags=%x\n", + qemu_log("PC=%" PRIx64 "\n", env->sregs[SR_PC]); + qemu_log("rmsr=%" PRIx64 " resr=%" PRIx64 " rear=%" PRIx64 " " + "debug[%x] imm=%x iflags=%x\n", env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], env->debug, env->imm, env->iflags); qemu_log("btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", env->btaken, env->btarget, (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", - (env->sregs[SR_MSR] & MSR_EIP), - (env->sregs[SR_MSR] & MSR_IE)); + (bool)(env->sregs[SR_MSR] & MSR_EIP), + (bool)(env->sregs[SR_MSR] & MSR_IE)); for (i = 0; i < 32; i++) { qemu_log("r%2.2d=%8.8x ", i, env->regs[i]); if ((i + 1) % 4 == 0) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 6f2cafa88a..7db4bdcf09 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -54,7 +54,7 @@ static TCGv_i32 env_debug; static TCGv_i32 cpu_R[32]; -static TCGv_i32 cpu_SR[14]; +static TCGv_i64 cpu_SR[14]; static TCGv_i32 env_imm; static TCGv_i32 env_btaken; static TCGv_i32 env_btarget; @@ -123,7 +123,7 @@ static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) TCGv_i32 tmp = tcg_const_i32(index); t_sync_flags(dc); - tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); dc->is_jmp = DISAS_UPDATE; @@ -142,17 +142,18 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) { if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); - tcg_gen_movi_i32(cpu_SR[SR_PC], dest); + tcg_gen_movi_i64(cpu_SR[SR_PC], dest); tcg_gen_exit_tb((uintptr_t)dc->tb + n); } else { - tcg_gen_movi_i32(cpu_SR[SR_PC], dest); + tcg_gen_movi_i64(cpu_SR[SR_PC], dest); tcg_gen_exit_tb(0); } } static void read_carry(DisasContext *dc, TCGv_i32 d) { - tcg_gen_shri_i32(d, cpu_SR[SR_MSR], 31); + tcg_gen_extrl_i64_i32(d, cpu_SR[SR_MSR]); + tcg_gen_shri_i32(d, d, 31); } /* @@ -161,14 +162,12 @@ static void read_carry(DisasContext *dc, TCGv_i32 d) */ static void write_carry(DisasContext *dc, TCGv_i32 v) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_shli_i32(t0, v, 31); - tcg_gen_sari_i32(t0, t0, 31); - tcg_gen_andi_i32(t0, t0, (MSR_C | MSR_CC)); - tcg_gen_andi_i32(cpu_SR[SR_MSR], cpu_SR[SR_MSR], - ~(MSR_C | MSR_CC)); - tcg_gen_or_i32(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0); - tcg_temp_free_i32(t0); + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(t0, v); + /* Deposit bit 0 into MSR_C and the alias MSR_CC. */ + tcg_gen_deposit_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0, 2, 1); + tcg_gen_deposit_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0, 31, 1); + tcg_temp_free_i64(t0); } static void write_carryi(DisasContext *dc, bool carry) @@ -187,7 +186,7 @@ static bool trap_illegal(DisasContext *dc, bool cond) { if (cond && (dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); } return cond; @@ -203,7 +202,7 @@ static bool trap_userspace(DisasContext *dc, bool cond) bool cond_user = cond && mem_index == MMU_USER_IDX; if (cond_user && (dc->tb_flags & MSR_EE_FLAG)) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); t_gen_raise_exception(dc, EXCP_HW_EXCP); } return cond_user; @@ -438,20 +437,21 @@ static void dec_xor(DisasContext *dc) static inline void msr_read(DisasContext *dc, TCGv_i32 d) { - tcg_gen_mov_i32(d, cpu_SR[SR_MSR]); + tcg_gen_extrl_i64_i32(d, cpu_SR[SR_MSR]); } static inline void msr_write(DisasContext *dc, TCGv_i32 v) { - TCGv_i32 t; + TCGv_i64 t; - t = tcg_temp_new_i32(); + t = tcg_temp_new_i64(); dc->cpustate_changed = 1; /* PVR bit is not writable. */ - tcg_gen_andi_i32(t, v, ~MSR_PVR); - tcg_gen_andi_i32(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); - tcg_gen_or_i32(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t); - tcg_temp_free(t); + tcg_gen_extu_i32_i64(t, v); + tcg_gen_andi_i64(t, t, ~MSR_PVR); + tcg_gen_andi_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); + tcg_gen_or_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t); + tcg_temp_free_i64(t); } static void dec_msr(DisasContext *dc) @@ -501,7 +501,7 @@ static void dec_msr(DisasContext *dc) msr_write(dc, t0); tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); - tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc + 4); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc + 4); dc->is_jmp = DISAS_UPDATE; return; } @@ -534,7 +534,7 @@ static void dec_msr(DisasContext *dc) case SR_EAR: case SR_ESR: case SR_FSR: - tcg_gen_mov_i32(cpu_SR[sr], cpu_R[dc->ra]); + tcg_gen_extu_i32_i64(cpu_SR[sr], cpu_R[dc->ra]); break; case 0x800: tcg_gen_st_i32(cpu_R[dc->ra], @@ -562,7 +562,7 @@ static void dec_msr(DisasContext *dc) case SR_ESR: case SR_FSR: case SR_BTR: - tcg_gen_mov_i32(cpu_R[dc->rd], cpu_SR[sr]); + tcg_gen_extrl_i64_i32(cpu_R[dc->rd], cpu_SR[sr]); break; case 0x800: tcg_gen_ld_i32(cpu_R[dc->rd], @@ -735,7 +735,8 @@ static void dec_bit(DisasContext *dc) t0 = tcg_temp_new_i32(); LOG_DIS("src r%d r%d\n", dc->rd, dc->ra); - tcg_gen_andi_i32(t0, cpu_SR[SR_MSR], MSR_CC); + tcg_gen_extrl_i64_i32(t0, cpu_SR[SR_MSR]); + tcg_gen_andi_i32(t0, t0, MSR_CC); write_carry(dc, cpu_R[dc->ra]); if (dc->rd) { tcg_gen_shri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); @@ -966,7 +967,7 @@ static void dec_load(DisasContext *dc) tcg_gen_qemu_ld_i32(v, addr, cpu_mmu_index(&dc->cpu->env, false), mop); if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { - tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); gen_helper_memalign(cpu_env, addr, tcg_const_i32(dc->rd), tcg_const_i32(0), tcg_const_i32(size - 1)); } @@ -1078,7 +1079,7 @@ static void dec_store(DisasContext *dc) /* Verify alignment if needed. */ if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { - tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); /* FIXME: if the alignment is wrong, we should restore the value * in memory. One possible way to achieve this is to probe * the MMU prior to the memaccess, thay way we could put @@ -1124,13 +1125,13 @@ static inline void eval_cc(DisasContext *dc, unsigned int cc, } } -static void eval_cond_jmp(DisasContext *dc, TCGv_i32 pc_true, TCGv_i32 pc_false) +static void eval_cond_jmp(DisasContext *dc, TCGv_i32 pc_true, TCGv_i64 pc_false) { TCGLabel *l1 = gen_new_label(); /* Conditional jmp. */ - tcg_gen_mov_i32(cpu_SR[SR_PC], pc_false); + tcg_gen_mov_i64(cpu_SR[SR_PC], pc_false); tcg_gen_brcondi_i32(TCG_COND_EQ, env_btaken, 0, l1); - tcg_gen_mov_i32(cpu_SR[SR_PC], pc_true); + tcg_gen_extu_i32_i64(cpu_SR[SR_PC], pc_true); gen_set_label(l1); } @@ -1187,7 +1188,7 @@ static void dec_br(DisasContext *dc) tcg_gen_st_i32(tmp_1, cpu_env, -offsetof(MicroBlazeCPU, env) +offsetof(CPUState, halted)); - tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc + 4); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc + 4); gen_helper_raise_exception(cpu_env, tmp_hlt); tcg_temp_free_i32(tmp_hlt); tcg_temp_free_i32(tmp_1); @@ -1246,8 +1247,9 @@ static inline void do_rti(DisasContext *dc) TCGv_i32 t0, t1; t0 = tcg_temp_new_i32(); t1 = tcg_temp_new_i32(); - tcg_gen_shri_i32(t0, cpu_SR[SR_MSR], 1); - tcg_gen_ori_i32(t1, cpu_SR[SR_MSR], MSR_IE); + tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); + tcg_gen_shri_i32(t0, t1, 1); + tcg_gen_ori_i32(t1, t1, MSR_IE); tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); @@ -1263,7 +1265,8 @@ static inline void do_rtb(DisasContext *dc) TCGv_i32 t0, t1; t0 = tcg_temp_new_i32(); t1 = tcg_temp_new_i32(); - tcg_gen_andi_i32(t1, cpu_SR[SR_MSR], ~MSR_BIP); + tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); + tcg_gen_andi_i32(t1, t1, ~MSR_BIP); tcg_gen_shri_i32(t0, t1, 1); tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); @@ -1281,7 +1284,8 @@ static inline void do_rte(DisasContext *dc) t0 = tcg_temp_new_i32(); t1 = tcg_temp_new_i32(); - tcg_gen_ori_i32(t1, cpu_SR[SR_MSR], MSR_EE); + tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); + tcg_gen_ori_i32(t1, t1, MSR_EE); tcg_gen_andi_i32(t1, t1, ~MSR_EIP); tcg_gen_shri_i32(t0, t1, 1); tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); @@ -1331,7 +1335,7 @@ static void dec_rts(DisasContext *dc) static int dec_check_fpuv2(DisasContext *dc) { if ((dc->cpu->cfg.use_fpu != 2) && (dc->tb_flags & MSR_EE_FLAG)) { - tcg_gen_movi_i32(cpu_SR[SR_ESR], ESR_EC_FPU); + tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_FPU); t_gen_raise_exception(dc, EXCP_HW_EXCP); } return (dc->cpu->cfg.use_fpu == 2) ? 0 : PVR2_USE_FPU2_MASK; @@ -1596,7 +1600,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) #if SIM_COMPAT if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { - tcg_gen_movi_i32(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); gen_helper_debug(); } #endif @@ -1638,7 +1642,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) dc->tb_flags &= ~D_FLAG; /* If it is a direct jump, try direct chaining. */ if (dc->jmp == JMP_INDIRECT) { - eval_cond_jmp(dc, env_btarget, tcg_const_i32(dc->pc)); + eval_cond_jmp(dc, env_btarget, tcg_const_i64(dc->pc)); dc->is_jmp = DISAS_JUMP; } else if (dc->jmp == JMP_DIRECT) { t_sync_flags(dc); @@ -1671,7 +1675,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { if (dc->tb_flags & D_FLAG) { dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_i32(cpu_SR[SR_PC], npc); + tcg_gen_movi_i64(cpu_SR[SR_PC], npc); sync_jmpstate(dc); } else npc = dc->jmp_pc; @@ -1683,7 +1687,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (dc->is_jmp == DISAS_NEXT && (dc->cpustate_changed || org_flags != dc->tb_flags)) { dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_i32(cpu_SR[SR_PC], npc); + tcg_gen_movi_i64(cpu_SR[SR_PC], npc); } t_sync_flags(dc); @@ -1691,7 +1695,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); if (dc->is_jmp != DISAS_JUMP) { - tcg_gen_movi_i32(cpu_SR[SR_PC], npc); + tcg_gen_movi_i64(cpu_SR[SR_PC], npc); } gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); @@ -1741,17 +1745,18 @@ void mb_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, if (!env || !f) return; - cpu_fprintf(f, "IN: PC=%x %s\n", + cpu_fprintf(f, "IN: PC=%" PRIx64 " %s\n", env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC])); - cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug=%x imm=%x iflags=%x fsr=%x\n", + cpu_fprintf(f, "rmsr=%" PRIx64 " resr=%" PRIx64 " rear=%" PRIx64 " " + "debug=%x imm=%x iflags=%x fsr=%" PRIx64 "\n", env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], env->debug, env->imm, env->iflags, env->sregs[SR_FSR]); cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", env->btaken, env->btarget, (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", - (env->sregs[SR_MSR] & MSR_EIP), - (env->sregs[SR_MSR] & MSR_IE)); + (bool)(env->sregs[SR_MSR] & MSR_EIP), + (bool)(env->sregs[SR_MSR] & MSR_IE)); for (i = 0; i < 32; i++) { cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); @@ -1792,7 +1797,7 @@ void mb_tcg_init(void) regnames[i]); } for (i = 0; i < ARRAY_SIZE(cpu_SR); i++) { - cpu_SR[i] = tcg_global_mem_new_i32(cpu_env, + cpu_SR[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUMBState, sregs[i]), special_regnames[i]); } From be73ef6423fbe1f06c912a67d6d066d257c11e18 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 18:10:00 +0200 Subject: [PATCH 0790/2380] target-microblaze: Setup for 64bit addressing Setup MicroBlaze builds for 64bit addressing. No functional change since the translator does not yet emit 64bit addresses. Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- configure | 1 + target/microblaze/cpu.h | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/configure b/configure index a8498ab393..a6a4616c3e 100755 --- a/configure +++ b/configure @@ -6844,6 +6844,7 @@ case "$target_name" in microblaze|microblazeel) TARGET_ARCH=microblaze bflt="yes" + echo "TARGET_ABI32=y" >> $config_target_mak ;; mips|mipsel) TARGET_ARCH=mips diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 215f42b384..b631b7dc4c 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -23,7 +23,7 @@ #include "qemu-common.h" #include "cpu-qom.h" -#define TARGET_LONG_BITS 32 +#define TARGET_LONG_BITS 64 #define CPUArchState struct CPUMBState @@ -340,8 +340,8 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, /* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ #define TARGET_PAGE_BITS 12 -#define TARGET_PHYS_ADDR_SPACE_BITS 32 -#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 64 +#define TARGET_VIRT_ADDR_SPACE_BITS 64 #define CPU_RESOLVING_TYPE TYPE_MICROBLAZE_CPU From d248e1beac9a640c033cc7d3c3d494576a74bbc0 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 13 Apr 2018 22:04:37 +0200 Subject: [PATCH 0791/2380] target-microblaze: Add Extended Addressing Add support for Extended Addressing. Load/stores with EA enabled concatenate two 32bit registers to form an extended address. We don't allow users to enable address sizes larger than 32 bits quite yet though. Once the MMU support is in, we'll turn it on. Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.c | 18 +++++++++++- target/microblaze/cpu.h | 2 ++ target/microblaze/translate.c | 55 +++++++++++++++++++++++++++-------- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index a6f1ce6549..6ee15ac800 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -154,6 +154,13 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) return; } + if (cpu->cfg.addr_size != 32) { + error_setg(errp, "addr-size %d is out of range. " + "Only 32bit is supported.", + cpu->cfg.addr_size); + return; + } + qemu_init_vcpu(cs); env->pvr.regs[0] = PVR0_USE_EXC_MASK \ @@ -200,7 +207,8 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) env->pvr.regs[5] |= cpu->cfg.dcache_writeback ? PVR5_DCACHE_WRITEBACK_MASK : 0; - env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family. */ + env->pvr.regs[10] = 0x0c000000 | /* Default to spartan 3a dsp family. */ + (cpu->cfg.addr_size - 32) << PVR10_ASIZE_SHIFT; env->pvr.regs[11] = (cpu->cfg.use_mmu ? PVR11_USE_MMU : 0) | 16 << 17; @@ -232,6 +240,14 @@ static Property mb_properties[] = { DEFINE_PROP_UINT32("base-vectors", MicroBlazeCPU, cfg.base_vectors, 0), DEFINE_PROP_BOOL("use-stack-protection", MicroBlazeCPU, cfg.stackprot, false), + /* + * This is the C_ADDR_SIZE synth-time configuration option of the + * MicroBlaze cores. Supported values range between 32 and 64. + * + * When set to > 32, 32bit MicroBlaze can emit load/stores + * with extended addressing. + */ + DEFINE_PROP_UINT8("addr-size", MicroBlazeCPU, cfg.addr_size, 32), /* If use-fpu > 0 - FPU is enabled * If use-fpu = 2 - Floating point conversion and square root instructions * are enabled diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index b631b7dc4c..e62c456ccf 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -203,6 +203,7 @@ typedef struct CPUMBState CPUMBState; /* Target family PVR mask */ #define PVR10_TARGET_FAMILY_MASK 0xFF000000 +#define PVR10_ASIZE_SHIFT 18 /* MMU descrtiption */ #define PVR11_USE_MMU 0xC0000000 @@ -297,6 +298,7 @@ struct MicroBlazeCPU { struct { bool stackprot; uint32_t base_vectors; + uint8_t addr_size; uint8_t use_fpu; uint8_t use_hw_mul; bool use_barrel; diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 7db4bdcf09..504db88890 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -824,7 +824,7 @@ static void dec_imm(DisasContext *dc) dc->clear_imm = 0; } -static inline void compute_ldst_addr(DisasContext *dc, TCGv t) +static inline void compute_ldst_addr(DisasContext *dc, bool ea, TCGv t) { bool extimm = dc->tb_flags & IMM_FLAG; /* Should be set to true if r1 is used by loadstores. */ @@ -838,6 +838,22 @@ static inline void compute_ldst_addr(DisasContext *dc, TCGv t) /* Treat the common cases first. */ if (!dc->type_b) { + if (ea) { + int addr_size = dc->cpu->cfg.addr_size; + + if (addr_size == 32) { + tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); + return; + } + + tcg_gen_concat_i32_i64(t, cpu_R[dc->rb], cpu_R[dc->ra]); + if (addr_size < 64) { + /* Mask off out of range bits. */ + tcg_gen_andi_i64(t, t, MAKE_64BIT_MASK(0, addr_size)); + } + return; + } + /* If any of the regs is r0, set t to the value of the other reg. */ if (dc->ra == 0) { tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); @@ -887,12 +903,14 @@ static void dec_load(DisasContext *dc) TCGv_i32 v; TCGv addr; unsigned int size; - bool rev = false, ex = false; + bool rev = false, ex = false, ea = false; + int mem_index = cpu_mmu_index(&dc->cpu->env, false); TCGMemOp mop; mop = dc->opcode & 3; size = 1 << mop; if (!dc->type_b) { + ea = extract32(dc->ir, 7, 1); rev = extract32(dc->ir, 9, 1); ex = extract32(dc->ir, 10, 1); } @@ -905,12 +923,19 @@ static void dec_load(DisasContext *dc) return; } - LOG_DIS("l%d%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", - ex ? "x" : ""); + if (trap_userspace(dc, ea)) { + return; + } + + LOG_DIS("l%d%s%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", + ex ? "x" : "", + ea ? "ea" : ""); t_sync_flags(dc); addr = tcg_temp_new(); - compute_ldst_addr(dc, addr); + compute_ldst_addr(dc, ea, addr); + /* Extended addressing bypasses the MMU. */ + mem_index = ea ? MMU_NOMMU_IDX : mem_index; /* * When doing reverse accesses we need to do two things. @@ -964,7 +989,7 @@ static void dec_load(DisasContext *dc) * address and if that succeeds we write into the destination reg. */ v = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(v, addr, cpu_mmu_index(&dc->cpu->env, false), mop); + tcg_gen_qemu_ld_i32(v, addr, mem_index, mop); if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); @@ -994,12 +1019,14 @@ static void dec_store(DisasContext *dc) TCGv addr; TCGLabel *swx_skip = NULL; unsigned int size; - bool rev = false, ex = false; + bool rev = false, ex = false, ea = false; + int mem_index = cpu_mmu_index(&dc->cpu->env, false); TCGMemOp mop; mop = dc->opcode & 3; size = 1 << mop; if (!dc->type_b) { + ea = extract32(dc->ir, 7, 1); rev = extract32(dc->ir, 9, 1); ex = extract32(dc->ir, 10, 1); } @@ -1012,14 +1039,19 @@ static void dec_store(DisasContext *dc) return; } - LOG_DIS("s%d%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", - ex ? "x" : ""); + trap_userspace(dc, ea); + + LOG_DIS("s%d%s%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", + ex ? "x" : "", + ea ? "ea" : ""); t_sync_flags(dc); /* If we get a fault on a dslot, the jmpstate better be in sync. */ sync_jmpstate(dc); /* SWX needs a temp_local. */ addr = ex ? tcg_temp_local_new() : tcg_temp_new(); - compute_ldst_addr(dc, addr); + compute_ldst_addr(dc, ea, addr); + /* Extended addressing bypasses the MMU. */ + mem_index = ea ? MMU_NOMMU_IDX : mem_index; if (ex) { /* swx */ TCGv_i32 tval; @@ -1074,8 +1106,7 @@ static void dec_store(DisasContext *dc) break; } } - tcg_gen_qemu_st_i32(cpu_R[dc->rd], addr, - cpu_mmu_index(&dc->cpu->env, false), mop); + tcg_gen_qemu_st_i32(cpu_R[dc->rd], addr, mem_index, mop); /* Verify alignment if needed. */ if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { From a1b48e3a3aa19d6e03b6c39cae4e915f5cceb028 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 14 Apr 2018 23:44:51 +0200 Subject: [PATCH 0792/2380] target-microblaze: Implement MFSE EAR Implement MFSE EAR to enable access to the upper part of EAR. Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 504db88890..7475003847 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -459,7 +459,7 @@ static void dec_msr(DisasContext *dc) CPUState *cs = CPU(dc->cpu); TCGv_i32 t0, t1; unsigned int sr, rn; - bool to, clrset; + bool to, clrset, extended; sr = extract32(dc->imm, 0, 14); to = extract32(dc->imm, 14, 1); @@ -467,6 +467,9 @@ static void dec_msr(DisasContext *dc) dc->type_b = 1; if (to) { dc->cpustate_changed = 1; + extended = extract32(dc->imm, 24, 1); + } else { + extended = extract32(dc->imm, 19, 1); } /* msrclr and msrset. */ @@ -559,6 +562,10 @@ static void dec_msr(DisasContext *dc) msr_read(dc, cpu_R[dc->rd]); break; case SR_EAR: + if (extended) { + tcg_gen_extrh_i64_i32(cpu_R[dc->rd], cpu_SR[sr]); + break; + } case SR_ESR: case SR_FSR: case SR_BTR: From a2207b593b6d7e164e6d4a587aec4cd885a8e855 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sun, 15 Apr 2018 23:18:49 +0200 Subject: [PATCH 0793/2380] target-microblaze: mmu: Add R_TBLX_MISS macros Add a R_TBLX_MISS MASK and SHIFT macros. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/mmu.c | 5 +++-- target/microblaze/mmu.h | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 0019ebd18f..f4a4c339c9 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -292,8 +292,9 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) v & TLB_EPN_MASK, 0, cpu_mmu_index(env, false)); if (hit) { env->mmu.regs[MMU_R_TLBX] = lu.idx; - } else - env->mmu.regs[MMU_R_TLBX] |= 0x80000000; + } else { + env->mmu.regs[MMU_R_TLBX] |= R_TBLX_MISS_MASK; + } break; } default: diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h index 3b7a9983d5..113539c6e9 100644 --- a/target/microblaze/mmu.h +++ b/target/microblaze/mmu.h @@ -54,6 +54,10 @@ #define TLB_M 0x00000002 /* Memory is coherent */ #define TLB_G 0x00000001 /* Memory is guarded from prefetch */ +/* TLBX */ +#define R_TBLX_MISS_SHIFT 31 +#define R_TBLX_MISS_MASK (1U << R_TBLX_MISS_SHIFT) + #define TLB_ENTRIES 64 struct microblaze_mmu From 96716533afa039b4698360af221a8c399367f91d Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sun, 15 Apr 2018 23:25:58 +0200 Subject: [PATCH 0794/2380] target-microblaze: mmu: Remove unused register state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add explicit handling for MMU_R_TLBX and log accesses to invalid MMU registers. We can now remove the state for all regs but PID, ZPR and TLBX (0 - 2). Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias --- target/microblaze/mmu.c | 7 +++++-- target/microblaze/mmu.h | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index f4a4c339c9..231803ceea 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -211,11 +211,14 @@ uint32_t mmu_read(CPUMBState *env, uint32_t rn) } r = env->mmu.regs[rn]; break; + case MMU_R_TLBX: + r = env->mmu.regs[rn]; + break; case MMU_R_TLBSX: qemu_log_mask(LOG_GUEST_ERROR, "TLBSX is write-only.\n"); break; default: - r = env->mmu.regs[rn]; + qemu_log_mask(LOG_GUEST_ERROR, "Invalid MMU register %d.\n", rn); break; } D(qemu_log("%s rn=%d=%x\n", __func__, rn, r)); @@ -298,7 +301,7 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) break; } default: - env->mmu.regs[rn] = v; + qemu_log_mask(LOG_GUEST_ERROR, "Invalid MMU register %d.\n", rn); break; } } diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h index 113539c6e9..624becfded 100644 --- a/target/microblaze/mmu.h +++ b/target/microblaze/mmu.h @@ -67,7 +67,7 @@ struct microblaze_mmu /* We keep a separate ram for the tids to avoid the 48 bit tag width. */ uint8_t tids[TLB_ENTRIES]; /* Control flops. */ - uint32_t regs[8]; + uint32_t regs[3]; int c_mmu; int c_mmu_tlb_access; From d2f004c3cdcb786303057683252112c3ff7b337e Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Mon, 16 Apr 2018 21:03:01 +0200 Subject: [PATCH 0795/2380] target-microblaze: mmu: Prepare for 64-bit addresses Prepare for 64-bit addresses. This makes no functional difference as the upper parts of the 64-bit addresses are not yet reachable. Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/mmu.c | 14 +++++++------- target/microblaze/mmu.h | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 231803ceea..a379968618 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -81,16 +81,16 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, { unsigned int i, hit = 0; unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel; - unsigned int tlb_size; - uint32_t tlb_tag, tlb_rpn, mask, t0; + uint64_t tlb_tag, tlb_rpn, mask; + uint32_t tlb_size, t0; lu->err = ERR_MISS; for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { - uint32_t t, d; + uint64_t t, d; /* Lookup and decode. */ t = mmu->rams[RAM_TAG][i]; - D(qemu_log("TLB %d valid=%d\n", i, t & TLB_VALID)); + D(qemu_log("TLB %d valid=%" PRId64 "\n", i, t & TLB_VALID)); if (t & TLB_VALID) { tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); if (tlb_size < TARGET_PAGE_SIZE) { @@ -98,10 +98,10 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, abort(); } - mask = ~(tlb_size - 1); + mask = ~((uint64_t)tlb_size - 1); tlb_tag = t & TLB_EPN_MASK; if ((vaddr & mask) != (tlb_tag & mask)) { - D(qemu_log("TLB %d vaddr=%x != tag=%x\n", + D(qemu_log("TLB %d vaddr=%" PRIx64 " != tag=%" PRIx64 "\n", i, vaddr & mask, tlb_tag & mask)); continue; } @@ -173,7 +173,7 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, } } done: - D(qemu_log("MMU vaddr=%x rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", + D(qemu_log("MMU vaddr=%" PRIx64 " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", vaddr, rw, tlb_wr, tlb_ex, hit)); return hit; } diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h index 624becfded..1714caf82e 100644 --- a/target/microblaze/mmu.h +++ b/target/microblaze/mmu.h @@ -28,7 +28,7 @@ #define RAM_TAG 0 /* Tag portion */ -#define TLB_EPN_MASK 0xFFFFFC00 /* Effective Page Number */ +#define TLB_EPN_MASK MAKE_64BIT_MASK(10, 64 - 10) #define TLB_PAGESZ_MASK 0x00000380 #define TLB_PAGESZ(x) (((x) & 0x7) << 7) #define PAGESZ_1K 0 @@ -42,7 +42,7 @@ #define TLB_VALID 0x00000040 /* Entry is valid */ /* Data portion */ -#define TLB_RPN_MASK 0xFFFFFC00 /* Real Page Number */ +#define TLB_RPN_MASK MAKE_64BIT_MASK(10, 64 - 10) #define TLB_PERM_MASK 0x00000300 #define TLB_EX 0x00000200 /* Instruction execution allowed */ #define TLB_WR 0x00000100 /* Writes permitted */ @@ -63,7 +63,7 @@ struct microblaze_mmu { /* Data and tag brams. */ - uint32_t rams[2][TLB_ENTRIES]; + uint64_t rams[2][TLB_ENTRIES]; /* We keep a separate ram for the tids to avoid the 48 bit tag width. */ uint8_t tids[TLB_ENTRIES]; /* Control flops. */ From 3924a9aa02fa00a256ddcfe2d6a08bc410ddcaaf Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Mon, 16 Apr 2018 19:37:16 +0200 Subject: [PATCH 0796/2380] target-microblaze: mmu: Add a configurable output address mask Add a configurable output address mask, used to mimic the configurable physical address bit width. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.c | 1 + target/microblaze/mmu.c | 1 + target/microblaze/mmu.h | 1 + 3 files changed, 3 insertions(+) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 6ee15ac800..71fc8d09fe 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -128,6 +128,7 @@ static void mb_cpu_reset(CPUState *s) env->mmu.c_mmu = 3; env->mmu.c_mmu_tlb_access = 3; env->mmu.c_mmu_zones = 16; + env->mmu.c_addr_mask = MAKE_64BIT_MASK(0, cpu->cfg.addr_size); #endif } diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index a379968618..166c79908c 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -164,6 +164,7 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, tlb_rpn = d & TLB_RPN_MASK; lu->vaddr = tlb_tag; + lu->paddr = tlb_rpn & mmu->c_addr_mask; lu->paddr = tlb_rpn; lu->size = tlb_size; lu->err = ERR_HIT; diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h index 1714caf82e..9fbdf38f36 100644 --- a/target/microblaze/mmu.h +++ b/target/microblaze/mmu.h @@ -72,6 +72,7 @@ struct microblaze_mmu int c_mmu; int c_mmu_tlb_access; int c_mmu_zones; + uint64_t c_addr_mask; /* Mask to apply to physical addresses. */ }; struct microblaze_mmu_lookup From 05a9a6519c9127b5fb0b13481ecc0e72331c8a38 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Wed, 16 May 2018 00:04:54 +0200 Subject: [PATCH 0797/2380] target-microblaze: dec_msr: Plug a temp leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plug a temp leak. Reported-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 7475003847..756d901eba 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -516,12 +516,17 @@ static void dec_msr(DisasContext *dc) #if !defined(CONFIG_USER_ONLY) /* Catch read/writes to the mmu block. */ if ((sr & ~0xff) == 0x1000) { + TCGv_i32 tmp_sr; + sr &= 7; + tmp_sr = tcg_const_i32(sr); LOG_DIS("m%ss sr%d r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); - if (to) - gen_helper_mmu_write(cpu_env, tcg_const_i32(sr), cpu_R[dc->ra]); - else - gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tcg_const_i32(sr)); + if (to) { + gen_helper_mmu_write(cpu_env, tmp_sr, cpu_R[dc->ra]); + } else { + gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tmp_sr); + } + tcg_temp_free_i32(tmp_sr); return; } #endif From f0f7e7f7b284f536389a3c5b67de681055325317 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Mon, 16 Apr 2018 21:25:01 +0200 Subject: [PATCH 0798/2380] target-microblaze: Add support for extended access to TLBLO Add support for extended access to TLBLO's upper 32 bits. Reviewed-by: Alistair Francis Signed-off-by: Edgar E. Iglesias --- target/microblaze/helper.h | 4 ++-- target/microblaze/mmu.c | 18 ++++++++++++++---- target/microblaze/mmu.h | 4 ++-- target/microblaze/op_helper.c | 8 ++++---- target/microblaze/translate.c | 19 +++++++++++++------ 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/target/microblaze/helper.h b/target/microblaze/helper.h index ce70353936..2f8bdea22b 100644 --- a/target/microblaze/helper.h +++ b/target/microblaze/helper.h @@ -25,8 +25,8 @@ DEF_HELPER_3(fcmp_ge, i32, env, i32, i32) DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_NO_RWG_SE, i32, i32, i32) #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_2(mmu_read, i32, env, i32) -DEF_HELPER_3(mmu_write, void, env, i32, i32) +DEF_HELPER_3(mmu_read, i32, env, i32, i32) +DEF_HELPER_4(mmu_write, void, env, i32, i32, i32) #endif DEF_HELPER_5(memalign, void, env, tl, i32, i32, i32) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 166c79908c..9ecffb2c9c 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -180,7 +180,7 @@ done: } /* Writes/reads to the MMU's special regs end up here. */ -uint32_t mmu_read(CPUMBState *env, uint32_t rn) +uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) { unsigned int i; uint32_t r = 0; @@ -189,6 +189,10 @@ uint32_t mmu_read(CPUMBState *env, uint32_t rn) qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); return 0; } + if (ext && rn != MMU_R_TLBLO) { + qemu_log_mask(LOG_GUEST_ERROR, "Extended access only to TLBLO.\n"); + return 0; + } switch (rn) { /* Reads to HI/LO trig reads from the mmu rams. */ @@ -200,7 +204,7 @@ uint32_t mmu_read(CPUMBState *env, uint32_t rn) } i = env->mmu.regs[MMU_R_TLBX] & 0xff; - r = env->mmu.rams[rn & 1][i]; + r = extract64(env->mmu.rams[rn & 1][i], ext * 32, 32); if (rn == MMU_R_TLBHI) env->mmu.regs[MMU_R_PID] = env->mmu.tids[i]; break; @@ -226,9 +230,10 @@ uint32_t mmu_read(CPUMBState *env, uint32_t rn) return r; } -void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) +void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) { MicroBlazeCPU *cpu = mb_env_get_cpu(env); + uint64_t tmp64; unsigned int i; D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn])); @@ -236,6 +241,10 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); return; } + if (ext && rn != MMU_R_TLBLO) { + qemu_log_mask(LOG_GUEST_ERROR, "Extended access only to TLBLO.\n"); + return; + } switch (rn) { /* Writes to HI/LO trig writes to the mmu rams. */ @@ -250,7 +259,8 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff; mmu_flush_idx(env, i); } - env->mmu.rams[rn & 1][i] = v; + tmp64 = env->mmu.rams[rn & 1][i]; + env->mmu.rams[rn & 1][i] = deposit64(tmp64, ext * 32, 32, v); D(qemu_log("%s ram[%d][%d]=%x\n", __func__, rn & 1, i, v)); break; diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h index 9fbdf38f36..a4272b6356 100644 --- a/target/microblaze/mmu.h +++ b/target/microblaze/mmu.h @@ -90,6 +90,6 @@ struct microblaze_mmu_lookup unsigned int mmu_translate(struct microblaze_mmu *mmu, struct microblaze_mmu_lookup *lu, target_ulong vaddr, int rw, int mmu_idx); -uint32_t mmu_read(CPUMBState *env, uint32_t rn); -void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v); +uint32_t mmu_read(CPUMBState *env, bool ea, uint32_t rn); +void mmu_write(CPUMBState *env, bool ea, uint32_t rn, uint32_t v); void mmu_init(struct microblaze_mmu *mmu); diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index 4dc3aff84b..ddc1f71d62 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -476,14 +476,14 @@ void helper_stackprot(CPUMBState *env, target_ulong addr) #if !defined(CONFIG_USER_ONLY) /* Writes/reads to the MMU's special regs end up here. */ -uint32_t helper_mmu_read(CPUMBState *env, uint32_t rn) +uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn) { - return mmu_read(env, rn); + return mmu_read(env, ext, rn); } -void helper_mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) +void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v) { - mmu_write(env, rn, v); + mmu_write(env, ext, rn, v); } void mb_cpu_unassigned_access(CPUState *cs, hwaddr addr, diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 756d901eba..bb6b5176c1 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -459,7 +459,7 @@ static void dec_msr(DisasContext *dc) CPUState *cs = CPU(dc->cpu); TCGv_i32 t0, t1; unsigned int sr, rn; - bool to, clrset, extended; + bool to, clrset, extended = false; sr = extract32(dc->imm, 0, 14); to = extract32(dc->imm, 14, 1); @@ -467,9 +467,14 @@ static void dec_msr(DisasContext *dc) dc->type_b = 1; if (to) { dc->cpustate_changed = 1; - extended = extract32(dc->imm, 24, 1); - } else { - extended = extract32(dc->imm, 19, 1); + } + + /* Extended MSRs are only available if addr_size > 32. */ + if (dc->cpu->cfg.addr_size > 32) { + /* The E-bit is encoded differently for To/From MSR. */ + static const unsigned int e_bit[] = { 19, 24 }; + + extended = extract32(dc->imm, e_bit[to], 1); } /* msrclr and msrset. */ @@ -516,17 +521,19 @@ static void dec_msr(DisasContext *dc) #if !defined(CONFIG_USER_ONLY) /* Catch read/writes to the mmu block. */ if ((sr & ~0xff) == 0x1000) { + TCGv_i32 tmp_ext = tcg_const_i32(extended); TCGv_i32 tmp_sr; sr &= 7; tmp_sr = tcg_const_i32(sr); LOG_DIS("m%ss sr%d r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); if (to) { - gen_helper_mmu_write(cpu_env, tmp_sr, cpu_R[dc->ra]); + gen_helper_mmu_write(cpu_env, tmp_ext, tmp_sr, cpu_R[dc->ra]); } else { - gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tmp_sr); + gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tmp_ext, tmp_sr); } tcg_temp_free_i32(tmp_sr); + tcg_temp_free_i32(tmp_ext); return; } #endif From 25ec2fdd7bb4957f15fa6153bf446d43dd7acb3e Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 17 Apr 2018 18:17:40 +0200 Subject: [PATCH 0799/2380] target-microblaze: Allow address sizes between 32 and 64 bits Allow address sizes between 32 and 64 bits. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 71fc8d09fe..9b546a2c18 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -155,9 +155,8 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) return; } - if (cpu->cfg.addr_size != 32) { - error_setg(errp, "addr-size %d is out of range. " - "Only 32bit is supported.", + if (cpu->cfg.addr_size < 32 || cpu->cfg.addr_size > 64) { + error_setg(errp, "addr-size %d is out of range (32 - 64)", cpu->cfg.addr_size); return; } From f7a66e3a8602b0498d340e5de959fbcf738a19b6 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 5 May 2018 14:27:23 +0200 Subject: [PATCH 0800/2380] target-microblaze: Simplify address computation using tcg_gen_addi_i32() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify address computation using tcg_gen_addi_i32(). tcg_gen_addi_i32() already optimizes the case when the immediate is zero. No functional change. Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index bb6b5176c1..0d8ef77513 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -899,12 +899,7 @@ static inline void compute_ldst_addr(DisasContext *dc, bool ea, TCGv t) /* Immediate. */ t32 = tcg_temp_new_i32(); if (!extimm) { - if (dc->imm == 0) { - tcg_gen_mov_i32(t32, cpu_R[dc->ra]); - } else { - tcg_gen_movi_i32(t32, (int32_t)((int16_t)dc->imm)); - tcg_gen_add_i32(t32, cpu_R[dc->ra], t32); - } + tcg_gen_addi_i32(t32, cpu_R[dc->ra], (int16_t)dc->imm); } else { tcg_gen_add_i32(t32, cpu_R[dc->ra], *(dec_alu_op_b(dc))); } From 75c9ddce5d069c27b91e75127f969f32842cb664 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sat, 5 May 2018 15:55:25 +0200 Subject: [PATCH 0801/2380] target-microblaze: mmu: Cleanup debug log messages Cleanup debug log messages: * Avoid long 80+ character lines. * Remove D() macro and use qemu_log_mask. * Remove logs that are not very useful Suggested-by: Alistair Francis Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Signed-off-by: Edgar E. Iglesias --- target/microblaze/mmu.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 9ecffb2c9c..f4ceaea520 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -22,8 +22,6 @@ #include "cpu.h" #include "exec/exec-all.h" -#define D(x) - static unsigned int tlb_decode_size(unsigned int f) { static const unsigned int sizes[] = { @@ -90,25 +88,20 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, /* Lookup and decode. */ t = mmu->rams[RAM_TAG][i]; - D(qemu_log("TLB %d valid=%" PRId64 "\n", i, t & TLB_VALID)); if (t & TLB_VALID) { tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); if (tlb_size < TARGET_PAGE_SIZE) { - qemu_log("%d pages not supported\n", tlb_size); + qemu_log_mask(LOG_UNIMP, "%d pages not supported\n", tlb_size); abort(); } mask = ~((uint64_t)tlb_size - 1); tlb_tag = t & TLB_EPN_MASK; if ((vaddr & mask) != (tlb_tag & mask)) { - D(qemu_log("TLB %d vaddr=%" PRIx64 " != tag=%" PRIx64 "\n", - i, vaddr & mask, tlb_tag & mask)); continue; } if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) != mmu->tids[i])) { - D(qemu_log("TLB %d pid=%x != tid=%x\n", - i, mmu->regs[MMU_R_PID], mmu->tids[i])); continue; } @@ -123,7 +116,8 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, t0 &= 0x3; if (tlb_zsel > mmu->c_mmu_zones) { - qemu_log_mask(LOG_GUEST_ERROR, "tlb zone select out of range! %d\n", tlb_zsel); + qemu_log_mask(LOG_GUEST_ERROR, + "tlb zone select out of range! %d\n", tlb_zsel); t0 = 1; /* Ignore. */ } @@ -174,8 +168,9 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, } } done: - D(qemu_log("MMU vaddr=%" PRIx64 " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", - vaddr, rw, tlb_wr, tlb_ex, hit)); + qemu_log_mask(CPU_LOG_MMU, + "MMU vaddr=%" PRIx64 " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", + vaddr, rw, tlb_wr, tlb_ex, hit); return hit; } @@ -199,7 +194,8 @@ uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) case MMU_R_TLBLO: case MMU_R_TLBHI: if (!(env->mmu.c_mmu_tlb_access & 1)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return 0; } @@ -211,7 +207,8 @@ uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) case MMU_R_PID: case MMU_R_ZPR: if (!(env->mmu.c_mmu_tlb_access & 1)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return 0; } r = env->mmu.regs[rn]; @@ -226,7 +223,7 @@ uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) qemu_log_mask(LOG_GUEST_ERROR, "Invalid MMU register %d.\n", rn); break; } - D(qemu_log("%s rn=%d=%x\n", __func__, rn, r)); + qemu_log_mask(CPU_LOG_MMU, "%s rn=%d=%x\n", __func__, rn, r); return r; } @@ -235,7 +232,8 @@ void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) MicroBlazeCPU *cpu = mb_env_get_cpu(env); uint64_t tmp64; unsigned int i; - D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn])); + qemu_log_mask(CPU_LOG_MMU, + "%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn]); if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); @@ -261,12 +259,11 @@ void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) } tmp64 = env->mmu.rams[rn & 1][i]; env->mmu.rams[rn & 1][i] = deposit64(tmp64, ext * 32, 32, v); - - D(qemu_log("%s ram[%d][%d]=%x\n", __func__, rn & 1, i, v)); break; case MMU_R_ZPR: if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return; } @@ -279,7 +276,8 @@ void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) break; case MMU_R_PID: if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return; } @@ -298,7 +296,8 @@ void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) int hit; if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return; } From d89b86e9129a7c21a453fc495fc52b062708e8dc Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 8 May 2018 17:31:50 +0200 Subject: [PATCH 0802/2380] target-microblaze: Use table based condition-codes conversion Use a table based conversion to map condition-codes between MicroBlaze ISA encoding and TCG. No functional change. Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 41 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 0d8ef77513..092e182c2f 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1145,28 +1145,27 @@ static void dec_store(DisasContext *dc) static inline void eval_cc(DisasContext *dc, unsigned int cc, TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { + static const int mb_to_tcg_cc[] = { + [CC_EQ] = TCG_COND_EQ, + [CC_NE] = TCG_COND_NE, + [CC_LT] = TCG_COND_LT, + [CC_LE] = TCG_COND_LE, + [CC_GE] = TCG_COND_GE, + [CC_GT] = TCG_COND_GT, + }; + switch (cc) { - case CC_EQ: - tcg_gen_setcond_i32(TCG_COND_EQ, d, a, b); - break; - case CC_NE: - tcg_gen_setcond_i32(TCG_COND_NE, d, a, b); - break; - case CC_LT: - tcg_gen_setcond_i32(TCG_COND_LT, d, a, b); - break; - case CC_LE: - tcg_gen_setcond_i32(TCG_COND_LE, d, a, b); - break; - case CC_GE: - tcg_gen_setcond_i32(TCG_COND_GE, d, a, b); - break; - case CC_GT: - tcg_gen_setcond_i32(TCG_COND_GT, d, a, b); - break; - default: - cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); - break; + case CC_EQ: + case CC_NE: + case CC_LT: + case CC_LE: + case CC_GE: + case CC_GT: + tcg_gen_setcond_i32(mb_to_tcg_cc[cc], d, a, b); + break; + default: + cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); + break; } } From 9e6e1828b62094a9f44f01de3da5e9c5109d54b0 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 8 May 2018 17:34:21 +0200 Subject: [PATCH 0803/2380] target-microblaze: Remove argument b in eval_cc() Remove argument b in eval_cc() as it is always set to zero. No functional change. Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 092e182c2f..d870cb7c43 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1143,7 +1143,7 @@ static void dec_store(DisasContext *dc) } static inline void eval_cc(DisasContext *dc, unsigned int cc, - TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) + TCGv_i32 d, TCGv_i32 a) { static const int mb_to_tcg_cc[] = { [CC_EQ] = TCG_COND_EQ, @@ -1161,7 +1161,7 @@ static inline void eval_cc(DisasContext *dc, unsigned int cc, case CC_LE: case CC_GE: case CC_GT: - tcg_gen_setcond_i32(mb_to_tcg_cc[cc], d, a, b); + tcg_gen_setcondi_i32(mb_to_tcg_cc[cc], d, a, 0); break; default: cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); @@ -1207,7 +1207,7 @@ static void dec_bcc(DisasContext *dc) tcg_gen_movi_i32(env_btarget, dc->pc); tcg_gen_add_i32(env_btarget, env_btarget, *(dec_alu_op_b(dc))); } - eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_i32(0)); + eval_cc(dc, cc, env_btaken, cpu_R[dc->ra]); } static void dec_br(DisasContext *dc) From 43d318b220f52f3080293375a4d8c741b26e3563 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 8 May 2018 18:31:06 +0200 Subject: [PATCH 0804/2380] target-microblaze: Convert env_btarget to i64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert env_btarget to i64. No functional change. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.h | 2 +- target/microblaze/op_helper.c | 2 +- target/microblaze/translate.c | 36 ++++++++++++++++++++++------------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index e62c456ccf..e38580cd7f 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -239,7 +239,7 @@ typedef struct CPUMBState CPUMBState; struct CPUMBState { uint32_t debug; uint32_t btaken; - uint32_t btarget; + uint64_t btarget; uint32_t bimm; uint32_t imm; diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index ddc1f71d62..7cdbbcccae 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -99,7 +99,7 @@ void helper_debug(CPUMBState *env) "debug[%x] imm=%x iflags=%x\n", env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], env->debug, env->imm, env->iflags); - qemu_log("btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", + qemu_log("btaken=%d btarget=%" PRIx64 " mode=%s(saved=%s) eip=%d ie=%d\n", env->btaken, env->btarget, (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index d870cb7c43..591d232304 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -57,7 +57,7 @@ static TCGv_i32 cpu_R[32]; static TCGv_i64 cpu_SR[14]; static TCGv_i32 env_imm; static TCGv_i32 env_btaken; -static TCGv_i32 env_btarget; +static TCGv_i64 env_btarget; static TCGv_i32 env_iflags; static TCGv env_res_addr; static TCGv_i32 env_res_val; @@ -831,7 +831,7 @@ static inline void sync_jmpstate(DisasContext *dc) tcg_gen_movi_i32(env_btaken, 1); } dc->jmp = JMP_INDIRECT; - tcg_gen_movi_i32(env_btarget, dc->jmp_pc); + tcg_gen_movi_i64(env_btarget, dc->jmp_pc); } } @@ -1169,13 +1169,13 @@ static inline void eval_cc(DisasContext *dc, unsigned int cc, } } -static void eval_cond_jmp(DisasContext *dc, TCGv_i32 pc_true, TCGv_i64 pc_false) +static void eval_cond_jmp(DisasContext *dc, TCGv_i64 pc_true, TCGv_i64 pc_false) { TCGLabel *l1 = gen_new_label(); /* Conditional jmp. */ tcg_gen_mov_i64(cpu_SR[SR_PC], pc_false); tcg_gen_brcondi_i32(TCG_COND_EQ, env_btaken, 0, l1); - tcg_gen_extu_i32_i64(cpu_SR[SR_PC], pc_true); + tcg_gen_mov_i64(cpu_SR[SR_PC], pc_true); gen_set_label(l1); } @@ -1199,13 +1199,14 @@ static void dec_bcc(DisasContext *dc) if (dec_alu_op_b_is_small_imm(dc)) { int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */ - tcg_gen_movi_i32(env_btarget, dc->pc + offset); + tcg_gen_movi_i64(env_btarget, dc->pc + offset); dc->jmp = JMP_DIRECT_CC; dc->jmp_pc = dc->pc + offset; } else { dc->jmp = JMP_INDIRECT; - tcg_gen_movi_i32(env_btarget, dc->pc); - tcg_gen_add_i32(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_addi_i64(env_btarget, env_btarget, dc->pc); + tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); } eval_cc(dc, cc, env_btaken, cpu_R[dc->ra]); } @@ -1262,7 +1263,7 @@ static void dec_br(DisasContext *dc) dc->jmp = JMP_INDIRECT; if (abs) { tcg_gen_movi_i32(env_btaken, 1); - tcg_gen_mov_i32(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); if (link && !dslot) { if (!(dc->tb_flags & IMM_FLAG) && (dc->imm == 8 || dc->imm == 0x18)) t_gen_raise_exception(dc, EXCP_BREAK); @@ -1280,8 +1281,9 @@ static void dec_br(DisasContext *dc) dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); } else { tcg_gen_movi_i32(env_btaken, 1); - tcg_gen_movi_i32(env_btarget, dc->pc); - tcg_gen_add_i32(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_addi_i64(env_btarget, env_btarget, dc->pc); + tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); } } } @@ -1345,6 +1347,7 @@ static inline void do_rte(DisasContext *dc) static void dec_rts(DisasContext *dc) { unsigned int b_bit, i_bit, e_bit; + TCGv_i64 tmp64; i_bit = dc->ir & (1 << 21); b_bit = dc->ir & (1 << 22); @@ -1373,7 +1376,13 @@ static void dec_rts(DisasContext *dc) dc->jmp = JMP_INDIRECT; tcg_gen_movi_i32(env_btaken, 1); - tcg_gen_add_i32(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc))); + + tmp64 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_extu_i32_i64(tmp64, cpu_R[dc->ra]); + tcg_gen_add_i64(env_btarget, env_btarget, tmp64); + tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); + tcg_temp_free_i64(tmp64); } static int dec_check_fpuv2(DisasContext *dc) @@ -1795,7 +1804,8 @@ void mb_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, "debug=%x imm=%x iflags=%x fsr=%" PRIx64 "\n", env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], env->debug, env->imm, env->iflags, env->sregs[SR_FSR]); - cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", + cpu_fprintf(f, "btaken=%d btarget=%" PRIx64 " mode=%s(saved=%s) " + "eip=%d ie=%d\n", env->btaken, env->btarget, (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", @@ -1823,7 +1833,7 @@ void mb_tcg_init(void) env_imm = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, imm), "imm"); - env_btarget = tcg_global_mem_new_i32(cpu_env, + env_btarget = tcg_global_mem_new_i64(cpu_env, offsetof(CPUMBState, btarget), "btarget"); env_btaken = tcg_global_mem_new_i32(cpu_env, From e956caf2a6f1ba6e769b6e414b0a33aabae106c0 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 8 May 2018 18:42:31 +0200 Subject: [PATCH 0805/2380] target-microblaze: Use tcg_gen_movcond in eval_cond_jmp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup eval_cond_jmp to use tcg_gen_movcond_i64(). No functional change. Suggested-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 591d232304..b79600cba5 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1171,12 +1171,16 @@ static inline void eval_cc(DisasContext *dc, unsigned int cc, static void eval_cond_jmp(DisasContext *dc, TCGv_i64 pc_true, TCGv_i64 pc_false) { - TCGLabel *l1 = gen_new_label(); - /* Conditional jmp. */ - tcg_gen_mov_i64(cpu_SR[SR_PC], pc_false); - tcg_gen_brcondi_i32(TCG_COND_EQ, env_btaken, 0, l1); - tcg_gen_mov_i64(cpu_SR[SR_PC], pc_true); - gen_set_label(l1); + TCGv_i64 tmp_btaken = tcg_temp_new_i64(); + TCGv_i64 tmp_zero = tcg_const_i64(0); + + tcg_gen_extu_i32_i64(tmp_btaken, env_btaken); + tcg_gen_movcond_i64(TCG_COND_NE, cpu_SR[SR_PC], + tmp_btaken, tmp_zero, + pc_true, pc_false); + + tcg_temp_free_i64(tmp_btaken); + tcg_temp_free_i64(tmp_zero); } static void dec_bcc(DisasContext *dc) From 923ce2e6af7f8228d2ccb3ff272ea0fad855618c Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 15 May 2018 23:35:16 +0200 Subject: [PATCH 0806/2380] target-microblaze: cpu_mmu_index: Fixup indentation Fixup the indentation of cpu_mmu_index in preparation for future edits. No functional changes. Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index e38580cd7f..c77ca2d8f9 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -360,13 +360,15 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, static inline int cpu_mmu_index (CPUMBState *env, bool ifetch) { - /* Are we in nommu mode?. */ - if (!(env->sregs[SR_MSR] & MSR_VM)) - return MMU_NOMMU_IDX; + /* Are we in nommu mode?. */ + if (!(env->sregs[SR_MSR] & MSR_VM)) { + return MMU_NOMMU_IDX; + } - if (env->sregs[SR_MSR] & MSR_UM) - return MMU_USER_IDX; - return MMU_KERNEL_IDX; + if (env->sregs[SR_MSR] & MSR_UM) { + return MMU_USER_IDX; + } + return MMU_KERNEL_IDX; } int mb_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, From d10367e035eab12c77b83b5985915ff7f003de1f Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 15 May 2018 23:44:28 +0200 Subject: [PATCH 0807/2380] target-microblaze: Consolidate MMU enabled checks Consolidate MMU enabled checks to cpu_mmu_index(). No functional changes. Suggested-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Signed-off-by: Edgar E. Iglesias --- target/microblaze/cpu.h | 4 +++- target/microblaze/helper.c | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index c77ca2d8f9..3c4e0ba80a 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -360,8 +360,10 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, static inline int cpu_mmu_index (CPUMBState *env, bool ifetch) { + MicroBlazeCPU *cpu = mb_env_get_cpu(env); + /* Are we in nommu mode?. */ - if (!(env->sregs[SR_MSR] & MSR_VM)) { + if (!(env->sregs[SR_MSR] & MSR_VM) || !cpu->cfg.use_mmu) { return MMU_NOMMU_IDX; } diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index 985bdae8d1..bc753793ec 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -58,8 +58,7 @@ int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, int prot; /* Translate if the MMU is available and enabled. */ - if (cpu->cfg.use_mmu && (env->sregs[SR_MSR] & MSR_VM) - && mmu_idx != MMU_NOMMU_IDX) { + if (mmu_idx != MMU_NOMMU_IDX) { uint32_t vaddr, paddr; struct microblaze_mmu_lookup lu; @@ -270,9 +269,10 @@ hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) CPUMBState *env = &cpu->env; target_ulong vaddr, paddr = 0; struct microblaze_mmu_lookup lu; + int mmu_idx = cpu_mmu_index(env, false); unsigned int hit; - if (env->sregs[SR_MSR] & MSR_VM) { + if (mmu_idx != MMU_NOMMU_IDX) { hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); if (hit) { vaddr = addr & TARGET_PAGE_MASK; From 51f63ec7da4ed0a26168e57fea322cbbcaae6064 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 22 May 2018 11:39:56 +0100 Subject: [PATCH 0808/2380] qapi: Change "since 2.13" annotations to "since 3.0" We're going to make the next release be 3.0, not 2.13; change the annotations in our json appropriately. Changes produced with sed -i -e 's/2\.13/3.0/g' qapi/*.json Signed-off-by: Peter Maydell Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Message-id: 20180522104000.9044-2-peter.maydell@linaro.org --- qapi/block-core.json | 4 ++-- qapi/common.json | 2 +- qapi/migration.json | 16 ++++++++-------- qapi/misc.json | 4 ++-- qapi/net.json | 2 +- qapi/ui.json | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 7dfa77a05c..ad66ad6f80 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2424,7 +2424,7 @@ # @vxhs: Since 2.10 # @throttle: Since 2.11 # @nvme: Since 2.12 -# @copy-on-read: Since 2.13 +# @copy-on-read: Since 3.0 # # Since: 2.9 ## @@ -2452,7 +2452,7 @@ # @x-check-cache-dropped: whether to check that page cache was dropped on live # migration. May cause noticeable delays if the image # file is large, do not use in production. -# (default: off) (since: 2.13) +# (default: off) (since: 3.0) # # Since: 2.9 ## diff --git a/qapi/common.json b/qapi/common.json index c811d04984..c367adc4b6 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -140,7 +140,7 @@ # prefix to produce the corresponding QEMU executable name. This # is true even for "qemu-system-x86_64". # -# Since: 2.13 +# Since: 3.0 ## { 'enum' : 'SysEmuTarget', 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', diff --git a/qapi/migration.json b/qapi/migration.json index 3ec418dabf..dc9cc85545 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -89,9 +89,9 @@ # # @postcopy-active: like active, but now in postcopy mode. (since 2.5) # -# @postcopy-paused: during postcopy but paused. (since 2.13) +# @postcopy-paused: during postcopy but paused. (since 3.0) # -# @postcopy-recover: trying to recover from a paused postcopy. (since 2.13) +# @postcopy-recover: trying to recover from a paused postcopy. (since 3.0) # # @completed: migration is finished. # @@ -163,11 +163,11 @@ # # @postcopy-blocktime: total time when all vCPU were blocked during postcopy # live migration. This is only present when the postcopy-blocktime -# migration capability is enabled. (Since 2.13) +# migration capability is enabled. (Since 3.0) # # @postcopy-vcpu-blocktime: list of the postcopy blocktime per vCPU. This is # only present when the postcopy-blocktime migration capability -# is enabled. (Since 2.13) +# is enabled. (Since 3.0) # # # Since: 0.14.0 @@ -374,7 +374,7 @@ # (since 2.12) # # @postcopy-blocktime: Calculate downtime for postcopy live migration -# (since 2.13) +# (since 3.0) # # Since: 1.2 ## @@ -1034,7 +1034,7 @@ # @detach: this argument exists only for compatibility reasons and # is ignored by QEMU # -# @resume: resume one paused migration, default "off". (since 2.13) +# @resume: resume one paused migration, default "off". (since 3.0) # # Returns: nothing on success # @@ -1208,7 +1208,7 @@ # "arguments": { "uri": "tcp:192.168.1.200:12345" } } # <- { "return": {} } # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'migrate-recover', 'data': { 'uri': 'str' }, 'allow-oob': true } @@ -1225,6 +1225,6 @@ # -> { "execute": "migrate-pause" } # <- { "return": {} } # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'migrate-pause', 'allow-oob': true } diff --git a/qapi/misc.json b/qapi/misc.json index f5988cc0b5..99bcaacd62 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -558,11 +558,11 @@ # @props: properties describing to which node/socket/core/thread # virtual CPU belongs to, provided if supported by board # -# @arch: base architecture of the cpu; deprecated since 2.13.0 in favor +# @arch: base architecture of the cpu; deprecated since 3.0.0 in favor # of @target # # @target: the QEMU system emulation target, which determines which -# additional fields will be listed (since 2.13) +# additional fields will be listed (since 3.0) # # Since: 2.12 # diff --git a/qapi/net.json b/qapi/net.json index b8adf1f03f..5c1dc48890 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -498,7 +498,7 @@ # # Since: 1.2 # -# 'vlan': dropped in 2.13 +# 'vlan': dropped in 3.0 ## { 'struct': 'NetLegacy', 'data': { diff --git a/qapi/ui.json b/qapi/ui.json index 3ad7835992..fc18a05f0f 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1031,7 +1031,7 @@ # @core: Use OpenGL with Core (desktop) Context. # @es: Use OpenGL with ES (embedded systems) Context. # - # Since: 2.13 + # Since: 3.0 # ## { 'enum' : 'DisplayGLMode', From aa78a16d86453d393b0a900032b7741d3b8049fb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 22 May 2018 11:39:57 +0100 Subject: [PATCH 0809/2380] hw/i386: Rename 2.13 machine types to 3.0 Rename the 2.13 machine types to match what we're going to use as our next release number. Signed-off-by: Peter Maydell Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Reviewed-by: Eduardo Habkost Message-id: 20180522104000.9044-3-peter.maydell@linaro.org --- hw/i386/pc_piix.c | 8 ++++---- hw/i386/pc_q35.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index e36c7bbb40..b4c5b03274 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -425,19 +425,19 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_display = "std"; } -static void pc_i440fx_2_13_machine_options(MachineClass *m) +static void pc_i440fx_3_0_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; } -DEFINE_I440FX_MACHINE(v2_13, "pc-i440fx-2.13", NULL, - pc_i440fx_2_13_machine_options); +DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", NULL, + pc_i440fx_3_0_machine_options); static void pc_i440fx_2_12_machine_options(MachineClass *m) { - pc_i440fx_2_13_machine_options(m); + pc_i440fx_3_0_machine_options(m); m->is_default = 0; m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 2372457c6a..83d6d75efa 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -308,18 +308,18 @@ static void pc_q35_machine_options(MachineClass *m) m->max_cpus = 288; } -static void pc_q35_2_13_machine_options(MachineClass *m) +static void pc_q35_3_0_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } -DEFINE_Q35_MACHINE(v2_13, "pc-q35-2.13", NULL, - pc_q35_2_13_machine_options); +DEFINE_Q35_MACHINE(v3_0, "pc-q35-3.0", NULL, + pc_q35_3_0_machine_options); static void pc_q35_2_12_machine_options(MachineClass *m) { - pc_q35_2_13_machine_options(m); + pc_q35_3_0_machine_options(m); m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); } From 2c5a2eefa6ad173fd364b8bd8c3c191759462a80 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 22 May 2018 11:39:58 +0100 Subject: [PATCH 0810/2380] hw/s390x: Rename 2.13 machines to 3.0 Rename the 2.13 machines to match the number we're going to use for the next release. Signed-off-by: Peter Maydell Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Message-id: 20180522104000.9044-4-peter.maydell@linaro.org --- hw/s390x/s390-virtio-ccw.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index e548d341a0..7ae5fb38dd 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -812,23 +812,23 @@ bool css_migration_enabled(void) .value = "0",\ }, -static void ccw_machine_2_13_instance_options(MachineState *machine) +static void ccw_machine_3_0_instance_options(MachineState *machine) { } -static void ccw_machine_2_13_class_options(MachineClass *mc) +static void ccw_machine_3_0_class_options(MachineClass *mc) { } -DEFINE_CCW_MACHINE(2_13, "2.13", true); +DEFINE_CCW_MACHINE(3_0, "3.0", true); static void ccw_machine_2_12_instance_options(MachineState *machine) { - ccw_machine_2_13_instance_options(machine); + ccw_machine_3_0_instance_options(machine); } static void ccw_machine_2_12_class_options(MachineClass *mc) { - ccw_machine_2_13_class_options(mc); + ccw_machine_3_0_class_options(mc); SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_12); } DEFINE_CCW_MACHINE(2_12, "2.12", false); From d8c0c7af80d95240796d8979c0b36ae5c97a5a20 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 22 May 2018 11:39:59 +0100 Subject: [PATCH 0811/2380] ppc: Rename 2.13 machines to 3.0 Rename the 2.13 machines to match the number we're going to use for the next release. Signed-off-by: Peter Maydell Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Reviewed-by: Greg Kurz Message-id: 20180522104000.9044-5-peter.maydell@linaro.org --- hw/ppc/spapr.c | 14 +++++++------- target/ppc/cpu.h | 2 +- target/ppc/machine.c | 8 ++++---- target/ppc/translate_init.inc.c | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index ebf30dd60b..213f6f9599 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4071,18 +4071,18 @@ static const TypeInfo spapr_machine_info = { type_init(spapr_machine_register_##suffix) /* - * pseries-2.13 + * pseries-3.0 */ -static void spapr_machine_2_13_instance_options(MachineState *machine) +static void spapr_machine_3_0_instance_options(MachineState *machine) { } -static void spapr_machine_2_13_class_options(MachineClass *mc) +static void spapr_machine_3_0_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(2_13, "2.13", true); +DEFINE_SPAPR_MACHINE(3_0, "3.0", true); /* * pseries-2.12 @@ -4091,18 +4091,18 @@ DEFINE_SPAPR_MACHINE(2_13, "2.13", true); HW_COMPAT_2_12 \ { \ .driver = TYPE_POWERPC_CPU, \ - .property = "pre-2.13-migration", \ + .property = "pre-3.0-migration", \ .value = "on", \ }, static void spapr_machine_2_12_instance_options(MachineState *machine) { - spapr_machine_2_13_instance_options(machine); + spapr_machine_3_0_instance_options(machine); } static void spapr_machine_2_12_class_options(MachineClass *mc) { - spapr_machine_2_13_class_options(mc); + spapr_machine_3_0_class_options(mc); SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_12); } diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 7ccd2f460e..0247c1f04c 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1215,7 +1215,7 @@ struct PowerPCCPU { uint64_t mig_insns_flags2; uint32_t mig_nb_BATs; bool pre_2_10_migration; - bool pre_2_13_migration; + bool pre_3_0_migration; int32_t mig_slb_nr; }; diff --git a/target/ppc/machine.c b/target/ppc/machine.c index ba1b9e531f..b2745ec4e5 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -150,11 +150,11 @@ static bool cpu_pre_2_8_migration(void *opaque, int version_id) } #if defined(TARGET_PPC64) -static bool cpu_pre_2_13_migration(void *opaque, int version_id) +static bool cpu_pre_3_0_migration(void *opaque, int version_id) { PowerPCCPU *cpu = opaque; - return cpu->pre_2_13_migration; + return cpu->pre_3_0_migration; } #endif @@ -220,7 +220,7 @@ static int cpu_pre_save(void *opaque) cpu->mig_insns_flags2 = env->insns_flags2 & insns_compat_mask2; cpu->mig_nb_BATs = env->nb_BATs; } - if (cpu->pre_2_13_migration) { + if (cpu->pre_3_0_migration) { if (cpu->hash64_opts) { cpu->mig_slb_nr = cpu->hash64_opts->slb_size; } @@ -517,7 +517,7 @@ static const VMStateDescription vmstate_slb = { .needed = slb_needed, .post_load = slb_post_load, .fields = (VMStateField[]) { - VMSTATE_INT32_TEST(mig_slb_nr, PowerPCCPU, cpu_pre_2_13_migration), + VMSTATE_INT32_TEST(mig_slb_nr, PowerPCCPU, cpu_pre_3_0_migration), VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), VMSTATE_END_OF_LIST() } diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index a0b3f184b2..ab782cb32a 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -10427,7 +10427,7 @@ static Property ppc_cpu_properties[] = { DEFINE_PROP_BOOL("pre-2.8-migration", PowerPCCPU, pre_2_8_migration, false), DEFINE_PROP_BOOL("pre-2.10-migration", PowerPCCPU, pre_2_10_migration, false), - DEFINE_PROP_BOOL("pre-2.13-migration", PowerPCCPU, pre_2_13_migration, + DEFINE_PROP_BOOL("pre-3.0-migration", PowerPCCPU, pre_3_0_migration, false), DEFINE_PROP_END_OF_LIST(), }; From dcd425608240ac6a5a223df2e8ce852b8fe1a661 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 22 May 2018 11:40:00 +0100 Subject: [PATCH 0812/2380] qemu-doc.texi: Rename references to 2.13 to 3.0 Update references to 2.13 to read 3.0, since that's the number we're using for the next release. Signed-off-by: Peter Maydell Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Message-id: 20180522104000.9044-6-peter.maydell@linaro.org --- qemu-doc.texi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qemu-doc.texi b/qemu-doc.texi index 0e0e0ae72b..cac1c3b39e 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2917,7 +2917,7 @@ The @code{-localtime} option has been replaced by @code{-rtc base=localtime}. The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}. -@subsection -virtioconsole (since 2.13.0) +@subsection -virtioconsole (since 3.0.0) Option @option{-virtioconsole} has been replaced by @option{-device virtconsole}. @@ -2940,7 +2940,7 @@ from qcow2 images. The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. -@subsection query-cpus-fast "arch" output member (since 2.13.0) +@subsection query-cpus-fast "arch" output member (since 3.0.0) The ``arch'' output member of the ``query-cpus-fast'' command is replaced by the ``target'' output member. From 7af5eea9b34ffb7a9a9fc25ba71998a02b76e159 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Mon, 28 May 2018 17:01:28 +0200 Subject: [PATCH 0813/2380] qcow2: Fix Coverity warning when calculating the refcount cache size MIN_REFCOUNT_CACHE_SIZE is 4 and the cluster size is guaranteed to be at most 2MB, so the minimum refcount cache size (in bytes) is always going to fit in a 32-bit integer. Coverity doesn't know that, and since we're storing the result in a uint64_t (*refcount_cache_size) it thinks that we need the 64 bits and that we probably want to do a 64-bit multiplication to prevent the result from being truncated. This is a false positive in this case, but it's a fair warning. We could do a 64-bit multiplication to get rid of it, but since we know that a 32-bit variable is enough to store this value let's simply reuse min_refcount_cache, make it a normal int and stop doing casts. Reported-by: Peter Maydell Signed-off-by: Alberto Garcia Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/qcow2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 6d532470a8..a007dc4246 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -768,6 +768,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, BDRVQcow2State *s = bs->opaque; uint64_t combined_cache_size; bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; + int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE); l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE); @@ -804,8 +805,6 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, } else { uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); - uint64_t min_refcount_cache = - (uint64_t) MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; /* Assign as much memory as possible to the L2 cache, and * use the remainder for the refcount cache */ @@ -825,7 +824,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, * s->cluster_size); } if (!refcount_cache_size_set) { - *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; + *refcount_cache_size = min_refcount_cache; } } From b5725385d17c876a12aa176a4a436d32d34ed06d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 29 May 2018 19:15:23 +0100 Subject: [PATCH 0814/2380] ui/cocoa: Suppress NSFileHandlingPanelOKButton deprecation warning OSX 10.13 deprecates the NSFileHandlingPanelOKButton constant, and would rather you use NSModalResponseOK, which was introduced in OS 10.9. Use the recommended new constant name, with a backward compatibility define if we're building on an older OSX. Signed-off-by: Peter Maydell Reviewed-by: John Arbuckle Message-id: 20180529181523.19185-1-peter.maydell@linaro.org --- ui/cocoa.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index 110b393e4e..2991ed4f19 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -44,6 +44,9 @@ #ifndef MAC_OS_X_VERSION_10_6 #define MAC_OS_X_VERSION_10_6 1060 #endif +#ifndef MAC_OS_X_VERSION_10_9 +#define MAC_OS_X_VERSION_10_9 1090 +#endif #ifndef MAC_OS_X_VERSION_10_10 #define MAC_OS_X_VERSION_10_10 101000 #endif @@ -79,6 +82,13 @@ #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask #define NSWindowStyleMaskTitled NSTitledWindowMask #endif +/* 10.13 deprecates NSFileHandlingPanelOKButton in favour of + * NSModalResponseOK, which was introduced in 10.9. Define + * it for older versions. + */ +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 +#define NSModalResponseOK NSFileHandlingPanelOKButton +#endif //#define DEBUG @@ -1218,7 +1228,7 @@ QemuCocoaView *cocoaView; [openPanel setCanChooseFiles: YES]; [openPanel setAllowsMultipleSelection: NO]; [openPanel setAllowedFileTypes: supportedImageFileTypes]; - if([openPanel runModal] == NSFileHandlingPanelOKButton) { + if([openPanel runModal] == NSModalResponseOK) { NSString * file = [[[openPanel URLs] objectAtIndex: 0] path]; if(file == nil) { NSBeep(); From 53618dd83885cc551a3833e228cf714494602142 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 25 May 2018 14:48:16 +0200 Subject: [PATCH 0815/2380] vdi: Fix vdi_co_do_create() return value .bdrv_co_create() is supposed to return 0 on success, but vdi could return a positive value instead. Fix this. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Jeff Cody --- block/vdi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/vdi.c b/block/vdi.c index 96a22b8e83..668af0a828 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -865,6 +865,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, } } + ret = 0; exit: blk_unref(blk); bdrv_unref(bs_file); From 4a5f2779bad769184550869931937acd0707ec3b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 25 May 2018 14:48:16 +0200 Subject: [PATCH 0816/2380] vhdx: Fix vhdx_co_create() return value .bdrv_co_create() is supposed to return 0 on success, but vhdx could return a positive value instead. Fix this. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Jeff Cody --- block/vhdx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/vhdx.c b/block/vhdx.c index 0b1e21c750..b1ba121bb6 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1951,7 +1951,7 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, goto delete_and_exit; } - + ret = 0; delete_and_exit: blk_unref(blk); bdrv_unref(bs); From 1266c9b9f5fa05877b979eece5963a2bd99c3bfd Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 24 May 2018 15:26:10 +0200 Subject: [PATCH 0817/2380] job: Add error message for failing jobs So far we relied on job->ret and strerror() to produce an error message for failed jobs. Not surprisingly, this tends to result in completely useless messages. This adds a Job.error field that can contain an error string for a failing job, and a parameter to job_completed() that sets the field. As a default, if NULL is passed, we continue to use strerror(job->ret). All existing callers are changed to pass NULL. They can be improved in separate patches. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Jeff Cody --- block/backup.c | 2 +- block/commit.c | 2 +- block/mirror.c | 2 +- block/stream.c | 2 +- include/qemu/job.h | 7 ++++++- job-qmp.c | 9 ++------- job.c | 16 ++++++++++++++-- tests/test-bdrv-drain.c | 2 +- tests/test-blockjob-txn.c | 2 +- tests/test-blockjob.c | 2 +- 10 files changed, 29 insertions(+), 17 deletions(-) diff --git a/block/backup.c b/block/backup.c index 4e228e959b..5661435675 100644 --- a/block/backup.c +++ b/block/backup.c @@ -321,7 +321,7 @@ static void backup_complete(Job *job, void *opaque) { BackupCompleteData *data = opaque; - job_completed(job, data->ret); + job_completed(job, data->ret, NULL); g_free(data); } diff --git a/block/commit.c b/block/commit.c index 620666161b..e1814d9693 100644 --- a/block/commit.c +++ b/block/commit.c @@ -117,7 +117,7 @@ static void commit_complete(Job *job, void *opaque) * bdrv_set_backing_hd() to fail. */ block_job_remove_all_bdrv(bjob); - job_completed(job, ret); + job_completed(job, ret, NULL); g_free(data); /* If bdrv_drop_intermediate() didn't already do that, remove the commit diff --git a/block/mirror.c b/block/mirror.c index dcb66ec3be..435268bbbf 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -581,7 +581,7 @@ static void mirror_exit(Job *job, void *opaque) blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); - job_completed(job, data->ret); + job_completed(job, data->ret, NULL); g_free(data); bdrv_drained_end(src); diff --git a/block/stream.c b/block/stream.c index a5d6e0cf8a..9264b68a1e 100644 --- a/block/stream.c +++ b/block/stream.c @@ -93,7 +93,7 @@ out: } g_free(s->backing_file_str); - job_completed(job, data->ret); + job_completed(job, data->ret, NULL); g_free(data); } diff --git a/include/qemu/job.h b/include/qemu/job.h index 8c8badf75e..1d820530fa 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -124,6 +124,9 @@ typedef struct Job { /** Estimated progress_current value at the completion of the job */ int64_t progress_total; + /** Error string for a failed job (NULL if, and only if, job->ret == 0) */ + char *error; + /** ret code passed to job_completed. */ int ret; @@ -466,13 +469,15 @@ void job_transition_to_ready(Job *job); /** * @job: The job being completed. * @ret: The status code. + * @error: The error message for a failing job (only with @ret < 0). If @ret is + * negative, but NULL is given for @error, strerror() is used. * * Marks @job as completed. If @ret is non-zero, the job transaction it is part * of is aborted. If @ret is zero, the job moves into the WAITING state. If it * is the last job to complete in its transaction, all jobs in the transaction * move from WAITING to PENDING. */ -void job_completed(Job *job, int ret); +void job_completed(Job *job, int ret, Error *error); /** Asynchronously complete the specified @job. */ void job_complete(Job *job, Error **errp); diff --git a/job-qmp.c b/job-qmp.c index 7f38f63336..410775df61 100644 --- a/job-qmp.c +++ b/job-qmp.c @@ -136,14 +136,9 @@ void qmp_job_dismiss(const char *id, Error **errp) static JobInfo *job_query_single(Job *job, Error **errp) { JobInfo *info; - const char *errmsg = NULL; assert(!job_is_internal(job)); - if (job->ret < 0) { - errmsg = strerror(-job->ret); - } - info = g_new(JobInfo, 1); *info = (JobInfo) { .id = g_strdup(job->id), @@ -151,8 +146,8 @@ static JobInfo *job_query_single(Job *job, Error **errp) .status = job->status, .current_progress = job->progress_current, .total_progress = job->progress_total, - .has_error = !!errmsg, - .error = g_strdup(errmsg), + .has_error = !!job->error, + .error = g_strdup(job->error), }; return info; diff --git a/job.c b/job.c index f026661b0f..84e140238b 100644 --- a/job.c +++ b/job.c @@ -369,6 +369,7 @@ void job_unref(Job *job) QLIST_REMOVE(job, job_list); + g_free(job->error); g_free(job->id); g_free(job); } @@ -660,6 +661,9 @@ static void job_update_rc(Job *job) job->ret = -ECANCELED; } if (job->ret) { + if (!job->error) { + job->error = g_strdup(strerror(-job->ret)); + } job_state_transition(job, JOB_STATUS_ABORTING); } } @@ -782,6 +786,7 @@ static int job_prepare(Job *job) { if (job->ret == 0 && job->driver->prepare) { job->ret = job->driver->prepare(job); + job_update_rc(job); } return job->ret; } @@ -855,10 +860,17 @@ static void job_completed_txn_success(Job *job) } } -void job_completed(Job *job, int ret) +void job_completed(Job *job, int ret, Error *error) { assert(job && job->txn && !job_is_completed(job)); + job->ret = ret; + if (error) { + assert(job->ret < 0); + job->error = g_strdup(error_get_pretty(error)); + error_free(error); + } + job_update_rc(job); trace_job_completed(job, ret, job->ret); if (job->ret) { @@ -876,7 +888,7 @@ void job_cancel(Job *job, bool force) } job_cancel_async(job, force); if (!job_started(job)) { - job_completed(job, -ECANCELED); + job_completed(job, -ECANCELED, NULL); } else if (job->deferred_to_main_loop) { job_completed_txn_abort(job); } else { diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 2cba63b881..a11c4cfbf2 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -498,7 +498,7 @@ typedef struct TestBlockJob { static void test_job_completed(Job *job, void *opaque) { - job_completed(job, 0); + job_completed(job, 0, NULL); } static void coroutine_fn test_job_start(void *opaque) diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index fce836639a..58d9b87fb2 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -34,7 +34,7 @@ static void test_block_job_complete(Job *job, void *opaque) rc = -ECANCELED; } - job_completed(job, rc); + job_completed(job, rc, NULL); bdrv_unref(bs); } diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index e408d52351..cb42f06e61 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -167,7 +167,7 @@ static void cancel_job_completed(Job *job, void *opaque) { CancelJob *s = opaque; s->completed = true; - job_completed(job, 0); + job_completed(job, 0, NULL); } static void cancel_job_complete(Job *job, Error **errp) From e5ab4347f9f53495e31fcef5e232c7c6be4a0567 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 18 Jan 2018 14:33:04 +0100 Subject: [PATCH 0818/2380] block/create: Make x-blockdev-create a job This changes the x-blockdev-create QMP command so that it doesn't block the monitor and the main loop any more, but starts a background job that performs the image creation. The basic job as implemented here is all that is necessary to make image creation asynchronous and to provide a QMP interface that can be marked stable, but it still lacks a few features that jobs usually provide: The job will ignore pause commands and it doesn't publish more than very basic progress yet (total-progress is 1 and current-progress advances from 0 to 1 when the driver callbacks returns). These features can be added later without breaking compatibility. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Jeff Cody --- block/create.c | 67 +++++++++++++++++++++++++++------------- qapi/block-core.json | 14 ++++++--- qapi/job.json | 4 ++- tests/qemu-iotests/group | 14 +++++---- 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/block/create.c b/block/create.c index 8bd8a03719..1a263e4b13 100644 --- a/block/create.c +++ b/block/create.c @@ -24,28 +24,51 @@ #include "qemu/osdep.h" #include "block/block_int.h" +#include "qemu/job.h" #include "qapi/qapi-commands-block-core.h" +#include "qapi/qapi-visit-block-core.h" +#include "qapi/clone-visitor.h" #include "qapi/error.h" -typedef struct BlockdevCreateCo { +typedef struct BlockdevCreateJob { + Job common; BlockDriver *drv; BlockdevCreateOptions *opts; int ret; - Error **errp; -} BlockdevCreateCo; + Error *err; +} BlockdevCreateJob; -static void coroutine_fn bdrv_co_create_co_entry(void *opaque) +static void blockdev_create_complete(Job *job, void *opaque) { - BlockdevCreateCo *cco = opaque; - cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp); + BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); + + job_completed(job, s->ret, s->err); } -void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) +static void coroutine_fn blockdev_create_run(void *opaque) { + BlockdevCreateJob *s = opaque; + + job_progress_set_remaining(&s->common, 1); + s->ret = s->drv->bdrv_co_create(s->opts, &s->err); + job_progress_update(&s->common, 1); + + qapi_free_BlockdevCreateOptions(s->opts); + job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL); +} + +static const JobDriver blockdev_create_job_driver = { + .instance_size = sizeof(BlockdevCreateJob), + .job_type = JOB_TYPE_CREATE, + .start = blockdev_create_run, +}; + +void qmp_x_blockdev_create(const char *job_id, BlockdevCreateOptions *options, + Error **errp) +{ + BlockdevCreateJob *s; const char *fmt = BlockdevDriver_str(options->driver); BlockDriver *drv = bdrv_find_format(fmt); - Coroutine *co; - BlockdevCreateCo cco; /* If the driver is in the schema, we know that it exists. But it may not * be whitelisted. */ @@ -55,22 +78,24 @@ void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) return; } - /* Call callback if it exists */ + /* Error out if the driver doesn't support .bdrv_co_create */ if (!drv->bdrv_co_create) { error_setg(errp, "Driver does not support blockdev-create"); return; } - cco = (BlockdevCreateCo) { - .drv = drv, - .opts = options, - .ret = -EINPROGRESS, - .errp = errp, - }; - - co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco); - qemu_coroutine_enter(co); - while (cco.ret == -EINPROGRESS) { - aio_poll(qemu_get_aio_context(), true); + /* Create the block job */ + /* TODO Running in the main context. Block drivers need to error out or add + * locking when they use a BDS in a different AioContext. */ + s = job_create(job_id, &blockdev_create_job_driver, NULL, + qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS, + NULL, NULL, errp); + if (!s) { + return; } + + s->drv = drv, + s->opts = QAPI_CLONE(BlockdevCreateOptions, options), + + job_start(&s->common); } diff --git a/qapi/block-core.json b/qapi/block-core.json index ad66ad6f80..eb98596614 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4013,14 +4013,18 @@ ## # @x-blockdev-create: # -# Create an image format on a given node. -# TODO Replace with something asynchronous (block job?) +# Starts a job to create an image format on a given node. The job is +# automatically finalized, but a manual job-dismiss is required. # -# Since: 2.12 +# @job-id: Identifier for the newly created job. +# +# @options: Options for the image creation. +# +# Since: 3.0 ## { 'command': 'x-blockdev-create', - 'data': 'BlockdevCreateOptions', - 'boxed': true } + 'data': { 'job-id': 'str', + 'options': 'BlockdevCreateOptions' } } ## # @blockdev-open-tray: diff --git a/qapi/job.json b/qapi/job.json index 970124de76..69c1970a58 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -17,10 +17,12 @@ # # @backup: drive backup job type, see "drive-backup" # +# @create: image creation job type, see "x-blockdev-create" (since 3.0) +# # Since: 1.7 ## { 'enum': 'JobType', - 'data': ['commit', 'stream', 'mirror', 'backup'] } + 'data': ['commit', 'stream', 'mirror', 'backup', 'create'] } ## # @JobStatus: diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 93f93d71ba..22b0082db3 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -204,14 +204,16 @@ 203 rw auto migration 204 rw auto quick 205 rw auto quick -206 rw auto -207 rw auto +# TODO The following commented out tests need to be reworked to work +# with the x-blockdev-create job +#206 rw auto +#207 rw auto 208 rw auto quick 209 rw auto quick -210 rw auto -211 rw auto quick -212 rw auto quick -213 rw auto quick +#210 rw auto +#211 rw auto quick +#212 rw auto quick +#213 rw auto quick 214 rw auto 215 rw auto quick 216 rw auto quick From 5ad1dbf76a97b6f07d685585175832e990fe9a92 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 23 May 2018 17:59:46 +0200 Subject: [PATCH 0819/2380] qemu-iotests: Add VM.get_qmp_events_filtered() This adds a helper function that returns a list of QMP events that are already filtered through filter_qmp_event(). Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Jeff Cody --- tests/qemu-iotests/iotests.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 28159d837a..17aa7c88dc 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -383,6 +383,11 @@ class VM(qtest.QEMUQtestMachine): output_list += [key + '=' + obj[key]] return ','.join(output_list) + def get_qmp_events_filtered(self, wait=True): + result = [] + for ev in self.get_qmp_events(wait=wait): + result.append(filter_qmp_event(ev)) + return result index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') From e234398a8e142fd0cfe571f7efb0e6a2f34fe73d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 23 May 2018 18:17:45 +0200 Subject: [PATCH 0820/2380] qemu-iotests: Add VM.qmp_log() This adds a helper function that logs both the QMP request and the received response before returning it. Signed-off-by: Kevin Wolf Reviewed-by: Jeff Cody Reviewed-by: Max Reitz --- tests/qemu-iotests/iotests.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 17aa7c88dc..2f54823db6 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -206,6 +206,10 @@ def filter_qmp_event(event): event['timestamp']['microseconds'] = 'USECS' return event +def filter_testfiles(msg): + prefix = os.path.join(test_dir, "%s-" % (os.getpid())) + return msg.replace(prefix, 'TEST_DIR/PID-') + def log(msg, filters=[]): for flt in filters: msg = flt(msg) @@ -389,6 +393,13 @@ class VM(qtest.QEMUQtestMachine): result.append(filter_qmp_event(ev)) return result + def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs): + logmsg = "{'execute': '%s', 'arguments': %s}" % (cmd, kwargs) + log(logmsg, filters) + result = self.qmp(cmd, **kwargs) + log(str(result), filters) + return result + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') From 6b605adec4d7491488d9cfb50bc256e667d8caf1 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 24 May 2018 13:12:56 +0200 Subject: [PATCH 0821/2380] qemu-iotests: Add iotests.img_info_log() This adds a filter function to postprocess 'qemu-img info' input (similar to what _img_info does), and an img_info_log() function that calls 'qemu-img info' and logs the filtered output. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Jeff Cody --- tests/qemu-iotests/iotests.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 2f54823db6..edcd2bb701 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -109,6 +109,12 @@ def qemu_img_pipe(*args): sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) return subp.communicate()[0] +def img_info_log(filename, filter_path=None): + output = qemu_img_pipe('info', '-f', imgfmt, filename) + if not filter_path: + filter_path = filename + log(filter_img_info(output, filter_path)) + def qemu_io(*args): '''Run qemu-io and return the stdout data''' args = qemu_io_args + list(args) @@ -210,6 +216,18 @@ def filter_testfiles(msg): prefix = os.path.join(test_dir, "%s-" % (os.getpid())) return msg.replace(prefix, 'TEST_DIR/PID-') +def filter_img_info(output, filename): + lines = [] + for line in output.split('\n'): + if 'disk size' in line or 'actual-size' in line: + continue + line = line.replace(filename, 'TEST_IMG') \ + .replace(imgfmt, 'IMGFMT') + line = re.sub('iters: [0-9]+', 'iters: XXX', line) + line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line) + lines.append(line) + return '\n'.join(lines) + def log(msg, filters=[]): for flt in filters: msg = flt(msg) From fc47d8513b45b1968b0ae32d4bb2f96d3aca066a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 29 May 2018 20:52:57 +0200 Subject: [PATCH 0822/2380] qemu-iotests: Add VM.run_job() Add an iotests.py function that runs a job and only returns when it is destroyed. An error is logged when the job failed and job-finalize and job-dismiss commands are issued if necessary. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Jeff Cody --- tests/qemu-iotests/iotests.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index edcd2bb701..8b612cb891 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -418,6 +418,25 @@ class VM(qtest.QEMUQtestMachine): log(str(result), filters) return result + def run_job(self, job, auto_finalize=True, auto_dismiss=False): + while True: + for ev in self.get_qmp_events_filtered(wait=True): + if ev['event'] == 'JOB_STATUS_CHANGE': + status = ev['data']['status'] + if status == 'aborting': + result = self.qmp('query-jobs') + for j in result['return']: + if j['id'] == job: + log('Job failed: %s' % (j['error'])) + elif status == 'pending' and not auto_finalize: + self.qmp_log('job-finalize', id=job) + elif status == 'concluded' and not auto_dismiss: + self.qmp_log('job-dismiss', id=job) + elif status == 'null': + return + else: + iotests.log(ev) + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') From 5a259e868b308564de9f2bfaef0606074a4f9028 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 29 May 2018 21:44:47 +0200 Subject: [PATCH 0823/2380] qemu-iotests: iotests.py helper for non-file protocols This adds two helper functions that are useful for test cases that make use of a non-file protocol (specifically ssh). Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Jeff Cody --- tests/qemu-iotests/iotests.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 8b612cb891..bc8f404ac2 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -303,6 +303,13 @@ def file_path(*names): return paths[0] if len(paths) == 1 else paths +def remote_filename(path): + if imgproto == 'file': + return path + elif imgproto == 'ssh': + return "ssh://127.0.0.1%s" % (path) + else: + raise Exception("Protocol %s not supported" % (imgproto)) class VM(qtest.QEMUQtestMachine): '''A QEMU VM''' @@ -601,6 +608,16 @@ def verify_image_format(supported_fmts=[], unsupported_fmts=[]): if not_sup or (imgfmt in unsupported_fmts): notrun('not suitable for this image format: %s' % imgfmt) +def verify_protocol(supported=[], unsupported=[]): + assert not (supported and unsupported) + + if 'generic' in supported: + return + + not_sup = supported and (imgproto not in supported) + if not_sup or (imgproto in unsupported): + notrun('not suitable for this protocol: %s' % imgproto) + def verify_platform(supported_oses=['linux']): if True not in [sys.platform.startswith(x) for x in supported_oses]: notrun('not suitable for this OS: %s' % sys.platform) From 4de110f8fd2811171013254fd7ed2384f1de87b4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 23 May 2018 18:19:00 +0200 Subject: [PATCH 0824/2380] qemu-iotests: Rewrite 206 for blockdev-create job This rewrites the test case 206 to work with the new x-blockdev-create job rather than the old synchronous version of the command. All of the test cases stay the same as before, but in order to be able to implement proper job handling, the test case is rewritten in Python. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- tests/qemu-iotests/206 | 598 ++++++++++++++----------------------- tests/qemu-iotests/206.out | 241 +++++++++------ tests/qemu-iotests/group | 2 +- 3 files changed, 367 insertions(+), 474 deletions(-) diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 index 0a18b2b19a..b8cf2e7dca 100755 --- a/tests/qemu-iotests/206 +++ b/tests/qemu-iotests/206 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test qcow2 and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf +# # 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 @@ -18,419 +20,263 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +from iotests import imgfmt -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['qcow2']) -here=`pwd` -status=1 # failure is the default! +def blockdev_create(vm, options): + result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -_supported_fmt qcow2 -_supported_proto file -_supported_os Linux +with iotests.FilePath('t.qcow2') as disk_path, \ + iotests.FilePath('t.qcow2.base') as backing_path, \ + iotests.VM() as vm: -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} + vm.add_object('secret,id=keysec0,data=foo') -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -echo -echo "=== Successful image creation (defaults) ===" -echo + size = 128 * 1024 * 1024 -size=$((128 * 1024 * 1024)) + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) -run_qemu < Date: Wed, 23 May 2018 18:19:00 +0200 Subject: [PATCH 0825/2380] qemu-iotests: Rewrite 207 for blockdev-create job This rewrites the test case 207 to work with the new x-blockdev-create job rather than the old synchronous version of the command. Most of the test cases stay the same as before (the exception being some improved 'size' options that allow distinguishing which command created the image), but in order to be able to implement proper job handling, the test case is rewritten in Python. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- tests/qemu-iotests/207 | 396 +++++++++++++++++-------------------- tests/qemu-iotests/207.out | 101 +++++----- tests/qemu-iotests/group | 6 +- 3 files changed, 232 insertions(+), 271 deletions(-) diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 index f5c77852d1..b595c925a5 100755 --- a/tests/qemu-iotests/207 +++ b/tests/qemu-iotests/207 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test ssh image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf +# # 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 @@ -18,244 +20,198 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +import subprocess +import re -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['raw']) +iotests.verify_protocol(supported=['ssh']) -here=`pwd` -status=1 # failure is the default! +def filter_hash(msg): + return re.sub("'hash': '[0-9a-f]+'", "'hash': HASH", msg) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter +def blockdev_create(vm, options): + result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options, + filters=[iotests.filter_testfiles, filter_hash]) -_supported_fmt raw -_supported_proto ssh -_supported_os Linux + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} +with iotests.FilePath('t.img') as disk_path, \ + iotests.VM() as vm: -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + remote_path = iotests.remote_filename(disk_path) -echo -echo "=== Successful image creation (defaults) ===" -echo + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -run_qemu </dev/null | grep -v "\\^#" | ' + + 'cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1', + shell=True).rstrip() -key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | - cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1) + vm.launch() + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'md5', + 'hash': 'wrong', + } + }, + 'size': 2097152 }) + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'md5', + 'hash': md5_key, + } + }, + 'size': 8388608 }) + vm.shutdown() -run_qemu </dev/null | grep -v "\\^#" | ' + + 'cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1', + shell=True).rstrip() + vm.launch() + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'sha1', + 'hash': 'wrong', + } + }, + 'size': 2097152 }) + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'sha1', + 'hash': sha1_key, + } + }, + 'size': 4194304 }) + vm.shutdown() -key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | - cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1) + iotests.img_info_log(remote_path, filter_path=disk_path) -run_qemu < Date: Wed, 23 May 2018 18:19:00 +0200 Subject: [PATCH 0826/2380] qemu-iotests: Rewrite 210 for blockdev-create job This rewrites the test case 210 to work with the new x-blockdev-create job rather than the old synchronous version of the command. All of the test cases stay the same as before, but in order to be able to implement proper job handling, the test case is rewritten in Python. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Jeff Cody --- tests/qemu-iotests/210 | 337 ++++++++++++++-------------------- tests/qemu-iotests/210.out | 197 ++++++++++++++------ tests/qemu-iotests/group | 2 +- tests/qemu-iotests/iotests.py | 12 +- 4 files changed, 286 insertions(+), 262 deletions(-) diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 index e607c0d296..ff4fddea56 100755 --- a/tests/qemu-iotests/210 +++ b/tests/qemu-iotests/210 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test luks and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf +# # 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 @@ -18,230 +20,165 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +from iotests import imgfmt -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['luks']) +iotests.verify_protocol(supported=['file']) -here=`pwd` -status=1 # failure is the default! +def blockdev_create(vm, options): + result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -_supported_fmt luks -_supported_proto file -_supported_os Linux +with iotests.FilePath('t.luks') as disk_path, \ + iotests.VM() as vm: -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} + vm.add_object('secret,id=keysec0,data=foo') -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -echo -echo "=== Successful image creation (defaults) ===" -echo + size = 128 * 1024 * 1024 -size=$((128 * 1024 * 1024)) + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) -run_qemu -object secret,id=keysec0,data="foo" <0 size"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - -image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} +{'execute': 'block_resize', 'arguments': {'size': 9223372036854775296, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u'The requested file size is too large'}} +{'execute': 'block_resize', 'arguments': {'size': 9223372036854775808L, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} +{'execute': 'block_resize', 'arguments': {'size': 18446744073709551104L, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} +{'execute': 'block_resize', 'arguments': {'size': -9223372036854775808, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u"Parameter 'size' expects a >0 size"}} +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} file format: IMGFMT virtual size: 0 (0 bytes) -*** done +encrypted: yes +Format specific information: + ivgen alg: plain64 + hash alg: sha256 + cipher alg: aes-256 + uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + cipher mode: xts + slots: + [0]: + active: true + iters: XXX + key offset: 4096 + stripes: 4000 + [1]: + active: false + key offset: 262144 + [2]: + active: false + key offset: 520192 + [3]: + active: false + key offset: 778240 + [4]: + active: false + key offset: 1036288 + [5]: + active: false + key offset: 1294336 + [6]: + active: false + key offset: 1552384 + [7]: + active: false + key offset: 1810432 + payload offset: 2068480 + master key iters: XXX + diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 8a84bf057d..a1d04ce367 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -208,9 +208,9 @@ 207 rw auto 208 rw auto quick 209 rw auto quick +210 rw auto # TODO The following commented out tests need to be reworked to work # with the x-blockdev-create job -#210 rw auto #211 rw auto quick #212 rw auto quick #213 rw auto quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index bc8f404ac2..fdbdd8b300 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -109,8 +109,16 @@ def qemu_img_pipe(*args): sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) return subp.communicate()[0] -def img_info_log(filename, filter_path=None): - output = qemu_img_pipe('info', '-f', imgfmt, filename) +def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]): + args = [ 'info' ] + if imgopts: + args.append('--image-opts') + else: + args += [ '-f', imgfmt ] + args += extra_args + args.append(filename) + + output = qemu_img_pipe(*args) if not filter_path: filter_path = filename log(filter_img_info(output, filter_path)) From abbab72cad2eafcaf3b0f4e970add813b4264e5f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 23 May 2018 18:19:00 +0200 Subject: [PATCH 0827/2380] qemu-iotests: Rewrite 211 for blockdev-create job This rewrites the test case 211 to work with the new x-blockdev-create job rather than the old synchronous version of the command. All of the test cases stay the same as before, but in order to be able to implement proper job handling, the test case is rewritten in Python. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- tests/qemu-iotests/211 | 319 ++++++++++++++----------------------- tests/qemu-iotests/211.out | 133 +++++++++------- tests/qemu-iotests/group | 2 +- 3 files changed, 198 insertions(+), 256 deletions(-) diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 index 1edec26517..b45f886d23 100755 --- a/tests/qemu-iotests/211 +++ b/tests/qemu-iotests/211 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test VDI and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf +# # 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 @@ -18,229 +20,154 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +from iotests import imgfmt -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['vdi']) +iotests.verify_protocol(supported=['file']) -here=`pwd` -status=1 # failure is the default! +def blockdev_create(vm, options): + result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -_supported_fmt vdi -_supported_proto file -_supported_os Linux +with iotests.FilePath('t.vdi') as disk_path, \ + iotests.VM() as vm: -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + size = 128 * 1024 * 1024 -echo -echo "=== Successful image creation (defaults) ===" -echo + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) -size=$((128 * 1024 * 1024)) + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') -run_qemu < Date: Wed, 23 May 2018 18:19:00 +0200 Subject: [PATCH 0828/2380] qemu-iotests: Rewrite 212 for blockdev-create job This rewrites the test case 212 to work with the new x-blockdev-create job rather than the old synchronous version of the command. All of the test cases stay the same as before, but in order to be able to implement proper job handling, the test case is rewritten in Python. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- tests/qemu-iotests/212 | 417 +++++++++++++------------------------ tests/qemu-iotests/212.out | 187 ++++++++++------- tests/qemu-iotests/group | 2 +- 3 files changed, 260 insertions(+), 346 deletions(-) diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 index e5a1ba77ce..03cf41d133 100755 --- a/tests/qemu-iotests/212 +++ b/tests/qemu-iotests/212 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test parallels and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf +# # 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 @@ -18,309 +20,176 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +from iotests import imgfmt -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['parallels']) +iotests.verify_protocol(supported=['file']) -here=`pwd` -status=1 # failure is the default! +def blockdev_create(vm, options): + result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -_supported_fmt parallels -_supported_proto file -_supported_os Linux +with iotests.FilePath('t.parallels') as disk_path, \ + iotests.VM() as vm: -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + size = 128 * 1024 * 1024 -echo -echo "=== Successful image creation (defaults) ===" -echo + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) -size=$((128 * 1024 * 1024)) + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') -run_qemu < Date: Wed, 23 May 2018 18:19:00 +0200 Subject: [PATCH 0829/2380] qemu-iotests: Rewrite 213 for blockdev-create job This rewrites the test case 213 to work with the new x-blockdev-create job rather than the old synchronous version of the command. All of the test cases stay the same as before, but in order to be able to implement proper job handling, the test case is rewritten in Python. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- tests/qemu-iotests/213 | 450 +++++++++++++------------------------ tests/qemu-iotests/213.out | 202 ++++++++++------- tests/qemu-iotests/group | 4 +- 3 files changed, 281 insertions(+), 375 deletions(-) diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213 index 3a00a0f6d6..29d25bcee1 100755 --- a/tests/qemu-iotests/213 +++ b/tests/qemu-iotests/213 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test vhdx and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf +# # 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 @@ -18,332 +20,190 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +from iotests import imgfmt -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['vhdx']) +iotests.verify_protocol(supported=['file']) -here=`pwd` -status=1 # failure is the default! +def blockdev_create(vm, options): + result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -_supported_fmt vhdx -_supported_proto file -_supported_os Linux +with iotests.FilePath('t.vhdx') as disk_path, \ + iotests.VM() as vm: -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + size = 128 * 1024 * 1024 -echo -echo "=== Successful image creation (defaults) ===" -echo + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) -size=$((128 * 1024 * 1024)) + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') -run_qemu < Date: Fri, 25 May 2018 18:24:51 +0200 Subject: [PATCH 0830/2380] block/create: Mark blockdev-create stable We're ready to declare the blockdev-create job stable. This renames the corresponding QMP command from x-blockdev-create to blockdev-create. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Jeff Cody --- block/create.c | 4 +-- qapi/block-core.json | 4 +-- qapi/job.json | 2 +- tests/qemu-iotests/206 | 2 +- tests/qemu-iotests/206.out | 54 +++++++++++++++++++------------------- tests/qemu-iotests/207 | 2 +- tests/qemu-iotests/207.out | 18 ++++++------- tests/qemu-iotests/210 | 2 +- tests/qemu-iotests/210.out | 18 ++++++------- tests/qemu-iotests/211 | 2 +- tests/qemu-iotests/211.out | 24 ++++++++--------- tests/qemu-iotests/212 | 2 +- tests/qemu-iotests/212.out | 42 ++++++++++++++--------------- tests/qemu-iotests/213 | 2 +- tests/qemu-iotests/213.out | 44 +++++++++++++++---------------- 15 files changed, 111 insertions(+), 111 deletions(-) diff --git a/block/create.c b/block/create.c index 1a263e4b13..915cd41bcc 100644 --- a/block/create.c +++ b/block/create.c @@ -63,8 +63,8 @@ static const JobDriver blockdev_create_job_driver = { .start = blockdev_create_run, }; -void qmp_x_blockdev_create(const char *job_id, BlockdevCreateOptions *options, - Error **errp) +void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, + Error **errp) { BlockdevCreateJob *s; const char *fmt = BlockdevDriver_str(options->driver); diff --git a/qapi/block-core.json b/qapi/block-core.json index eb98596614..4b1de474a9 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4011,7 +4011,7 @@ } } ## -# @x-blockdev-create: +# @blockdev-create: # # Starts a job to create an image format on a given node. The job is # automatically finalized, but a manual job-dismiss is required. @@ -4022,7 +4022,7 @@ # # Since: 3.0 ## -{ 'command': 'x-blockdev-create', +{ 'command': 'blockdev-create', 'data': { 'job-id': 'str', 'options': 'BlockdevCreateOptions' } } diff --git a/qapi/job.json b/qapi/job.json index 69c1970a58..17d10037c4 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -17,7 +17,7 @@ # # @backup: drive backup job type, see "drive-backup" # -# @create: image creation job type, see "x-blockdev-create" (since 3.0) +# @create: image creation job type, see "blockdev-create" (since 3.0) # # Since: 1.7 ## diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 index b8cf2e7dca..128c334c7c 100755 --- a/tests/qemu-iotests/206 +++ b/tests/qemu-iotests/206 @@ -26,7 +26,7 @@ from iotests import imgfmt iotests.verify_image_format(supported_fmts=['qcow2']) def blockdev_create(vm, options): - result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) if 'return' in result: assert result['return'] == {} diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out index 34451a3fc6..789eebe57b 100644 --- a/tests/qemu-iotests/206.out +++ b/tests/qemu-iotests/206.out @@ -1,13 +1,13 @@ === Successful image creation (defaults) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} {'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'imgfile', 'size': 134217728}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'imgfile', 'size': 134217728}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -24,12 +24,12 @@ Format specific information: === Successful image creation (inline blockdev-add, explicit defaults) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': False, 'preallocation': 'off', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': False, 'preallocation': 'off', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'refcount-bits': 16, 'version': 'v3', 'preallocation': 'off', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': False, 'driver': 'qcow2', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'refcount-bits': 16, 'version': 'v3', 'preallocation': 'off', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': False, 'driver': 'qcow2', 'size': 67108864}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -46,12 +46,12 @@ Format specific information: === Successful image creation (v3 non-default options) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': True, 'preallocation': 'falloc', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': True, 'preallocation': 'falloc', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 2097152, 'refcount-bits': 1, 'version': 'v3', 'preallocation': 'metadata', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': True, 'driver': 'qcow2', 'size': 33554432}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 2097152, 'refcount-bits': 1, 'version': 'v3', 'preallocation': 'metadata', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': True, 'driver': 'qcow2', 'size': 33554432}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -68,12 +68,12 @@ Format specific information: === Successful image creation (v2 non-default options) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'backing-fmt': 'qcow2', 'driver': 'qcow2', 'version': 'v2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'backing-file': 'TEST_DIR/PID-t.qcow2.base', 'size': 33554432}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'backing-fmt': 'qcow2', 'driver': 'qcow2', 'version': 'v2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'backing-file': 'TEST_DIR/PID-t.qcow2.base', 'size': 33554432}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -90,7 +90,7 @@ Format specific information: === Successful image creation (encrypted) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'encrypt': {'key-secret': 'keysec0', 'iter-time': 10, 'cipher-mode': 'ctr', 'ivgen-hash-alg': 'md5', 'cipher-alg': 'twofish-128', 'format': 'luks', 'ivgen-alg': 'plain64', 'hash-alg': 'sha1'}, 'driver': 'qcow2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'size': 33554432}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'encrypt': {'key-secret': 'keysec0', 'iter-time': 10, 'cipher-mode': 'ctr', 'ivgen-hash-alg': 'md5', 'cipher-alg': 'twofish-128', 'format': 'luks', 'ivgen-alg': 'plain64', 'hash-alg': 'sha1'}, 'driver': 'qcow2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'size': 33554432}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -144,111 +144,111 @@ Format specific information: === Invalid BlockdevRef === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': "this doesn't exist", 'size': 33554432}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': "this doesn't exist", 'size': 33554432}}} {u'return': {}} Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} === Invalid sizes === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 1234}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 1234}}} {u'return': {}} Job failed: Image size must be a multiple of 512 bytes {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 18446744073709551104L}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 18446744073709551104L}}} {u'return': {}} Job failed: Could not resize image: Image size cannot be negative {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775808L}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775808L}}} {u'return': {}} Job failed: Could not resize image: Image size cannot be negative {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775296}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775296}}} {u'return': {}} Job failed: Could not resize image: Failed to grow the L1 table: File too large {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} === Invalid version === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'version': 'v1', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'version': 'v1', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter 'v1'"}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'lazy-refcounts': True, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'lazy-refcounts': True, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater) {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 8, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 8, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater) {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} === Invalid backing file options === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'full', 'driver': 'qcow2', 'backing-file': '/dev/null', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'full', 'driver': 'qcow2', 'backing-file': '/dev/null', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Backing file and preallocation cannot be used at the same time {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'backing-fmt': 'qcow2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'backing-fmt': 'qcow2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Backing format cannot be used without backing file {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} === Invalid cluster size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Cluster size must be a power of two between 512 and 2048k {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Cluster size must be a power of two between 512 and 2048k {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4194304, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4194304, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Cluster size must be a power of two between 512 and 2048k {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Cluster size must be a power of two between 512 and 2048k {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'qcow2', 'file': 'node0', 'size': 281474976710656}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'qcow2', 'file': 'node0', 'size': 281474976710656}}} {u'return': {}} Job failed: Could not resize image: Failed to grow the L1 table: File too large {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} === Invalid refcount width === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Refcount width must be a power of two and may not exceed 64 bits {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Refcount width must be a power of two and may not exceed 64 bits {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 7, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 7, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Refcount width must be a power of two and may not exceed 64 bits {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 index b595c925a5..444ae233ae 100755 --- a/tests/qemu-iotests/207 +++ b/tests/qemu-iotests/207 @@ -31,7 +31,7 @@ def filter_hash(msg): return re.sub("'hash': '[0-9a-f]+'", "'hash': HASH", msg) def blockdev_create(vm, options): - result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options, + result = vm.qmp_log('blockdev-create', job_id='job0', options=options, filters=[iotests.filter_testfiles, filter_hash]) if 'return' in result: diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out index 5eee17bdb9..078b7e63cb 100644 --- a/tests/qemu-iotests/207.out +++ b/tests/qemu-iotests/207.out @@ -1,6 +1,6 @@ === Successful image creation (defaults) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -16,7 +16,7 @@ virtual size: 4.0M (4194304 bytes) === Test host-key-check options === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -25,7 +25,7 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po file format: IMGFMT virtual size: 8.0M (8388608 bytes) -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'known_hosts'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'known_hosts'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -34,13 +34,13 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po file format: IMGFMT virtual size: 4.0M (4194304 bytes) -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} {u'return': {}} Job failed: remote host key does not match host_key_check 'wrong' {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -49,13 +49,13 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po file format: IMGFMT virtual size: 8.0M (8388608 bytes) -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} {u'return': {}} Job failed: remote host key does not match host_key_check 'wrong' {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -66,13 +66,13 @@ virtual size: 4.0M (4194304 bytes) === Invalid path and user === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': '/this/is/not/an/existing/path', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': '/this/is/not/an/existing/path', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} {u'return': {}} Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31) {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'user': 'invalid user', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'user': 'invalid user', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} {u'return': {}} Job failed: failed to authenticate using publickey authentication and the identities held by your ssh-agent {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 index ff4fddea56..d142841e2b 100755 --- a/tests/qemu-iotests/210 +++ b/tests/qemu-iotests/210 @@ -27,7 +27,7 @@ iotests.verify_image_format(supported_fmts=['luks']) iotests.verify_protocol(supported=['file']) def blockdev_create(vm, options): - result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) if 'return' in result: assert result['return'] == {} diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out index 0e6e5c07be..078ba544a1 100644 --- a/tests/qemu-iotests/210.out +++ b/tests/qemu-iotests/210.out @@ -1,13 +1,13 @@ === Successful image creation (defaults) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} {'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'imgfile', 'size': 134217728}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'imgfile', 'size': 134217728}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -54,12 +54,12 @@ Format specific information: === Successful image creation (with non-default options) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'hash-alg': 'sha1', 'cipher-mode': 'ctr', 'cipher-alg': 'twofish-128', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}, 'iter-time': 10, 'ivgen-alg': 'plain64', 'ivgen-hash-alg': 'md5', 'driver': 'luks', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'hash-alg': 'sha1', 'cipher-mode': 'ctr', 'cipher-alg': 'twofish-128', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}, 'iter-time': 10, 'ivgen-alg': 'plain64', 'ivgen-hash-alg': 'md5', 'driver': 'luks', 'size': 67108864}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -106,7 +106,7 @@ Format specific information: === Invalid BlockdevRef === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'luks', 'file': "this doesn't exist", 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'luks', 'file': "this doesn't exist", 'size': 67108864}}} {u'return': {}} Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} @@ -114,7 +114,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi === Zero size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'node0', 'size': 0}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'node0', 'size': 0}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -161,19 +161,19 @@ Format specific information: === Invalid sizes === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 18446744073709551104L}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 18446744073709551104L}}} {u'return': {}} Job failed: The requested file size is too large {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775808L}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775808L}}} {u'return': {}} Job failed: The requested file size is too large {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775296}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775296}}} {u'return': {}} Job failed: The requested file size is too large {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 index b45f886d23..7b7985db6c 100755 --- a/tests/qemu-iotests/211 +++ b/tests/qemu-iotests/211 @@ -27,7 +27,7 @@ iotests.verify_image_format(supported_fmts=['vdi']) iotests.verify_protocol(supported=['file']) def blockdev_create(vm, options): - result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) if 'return' in result: assert result['return'] == {} diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out index 2bf1c4a920..6feaea3978 100644 --- a/tests/qemu-iotests/211.out +++ b/tests/qemu-iotests/211.out @@ -1,13 +1,13 @@ === Successful image creation (defaults) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} {'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'imgfile', 'size': 134217728}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'imgfile', 'size': 134217728}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -21,12 +21,12 @@ cluster_size: 1048576 === Successful image creation (explicit defaults) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'off', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'off', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 67108864}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -40,12 +40,12 @@ cluster_size: 1048576 === Successful image creation (with non-default options) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'metadata', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 33554432}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'metadata', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 33554432}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -60,7 +60,7 @@ cluster_size: 1048576 === Invalid BlockdevRef === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': "this doesn't exist", 'size': 33554432}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': "this doesn't exist", 'size': 33554432}}} {u'return': {}} Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} @@ -68,7 +68,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi === Zero size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 0}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 0}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -80,7 +80,7 @@ cluster_size: 1048576 === Maximum size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203584}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203584}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -92,19 +92,19 @@ cluster_size: 1048576 === Invalid sizes === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 18446744073709551104L}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 18446744073709551104L}}} {u'return': {}} Job failed: Unsupported VDI image size (size is 0xfffffffffffffe00, max supported is 0x1fffff8000000) {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 9223372036854775808L}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 9223372036854775808L}}} {u'return': {}} Job failed: Unsupported VDI image size (size is 0x8000000000000000, max supported is 0x1fffff8000000) {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203585}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203585}}} {u'return': {}} Job failed: Unsupported VDI image size (size is 0x1fffff8000001, max supported is 0x1fffff8000000) {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 index 03cf41d133..95c8810d83 100755 --- a/tests/qemu-iotests/212 +++ b/tests/qemu-iotests/212 @@ -27,7 +27,7 @@ iotests.verify_image_format(supported_fmts=['parallels']) iotests.verify_protocol(supported=['file']) def blockdev_create(vm, options): - result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) if 'return' in result: assert result['return'] == {} diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out index 780bc30112..9150da7a2c 100644 --- a/tests/qemu-iotests/212.out +++ b/tests/qemu-iotests/212.out @@ -1,13 +1,13 @@ === Successful image creation (defaults) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} {'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'imgfile', 'size': 134217728}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'imgfile', 'size': 134217728}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -18,12 +18,12 @@ virtual size: 128M (134217728 bytes) === Successful image creation (explicit defaults) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1048576, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1048576, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 67108864}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -34,12 +34,12 @@ virtual size: 64M (67108864 bytes) === Successful image creation (with non-default options) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 33554432}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 33554432}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -50,7 +50,7 @@ virtual size: 32M (33554432 bytes) === Invalid BlockdevRef === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': "this doesn't exist", 'size': 33554432}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': "this doesn't exist", 'size': 33554432}}} {u'return': {}} Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} @@ -58,7 +58,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi === Zero size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 0}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 0}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -69,7 +69,7 @@ virtual size: 0 (0 bytes) === Maximum size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627369984}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627369984}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -80,31 +80,31 @@ virtual size: 4096T (4503599627369984 bytes) === Invalid sizes === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 1234}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 1234}}} {u'return': {}} Job failed: Image size must be a multiple of 512 bytes {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 18446744073709551104L}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 18446744073709551104L}}} {u'return': {}} Job failed: Image size is too large for this cluster size {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775808L}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775808L}}} {u'return': {}} Job failed: Image size is too large for this cluster size {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775296}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775296}}} {u'return': {}} Job failed: Image size is too large for this cluster size {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627370497}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627370497}}} {u'return': {}} Job failed: Image size is too large for this cluster size {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} @@ -112,43 +112,43 @@ Job failed: Image size is too large for this cluster size === Invalid cluster size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Cluster size must be a multiple of 512 bytes {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Cluster size must be a multiple of 512 bytes {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4294967296, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4294967296, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Cluster size is too large {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 9223372036854775808L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 9223372036854775808L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Cluster size is too large {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 18446744073709551104L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 18446744073709551104L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Cluster size is too large {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Image size is too large for this cluster size {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'parallels', 'file': 'node0', 'size': 281474976710656}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'parallels', 'file': 'node0', 'size': 281474976710656}}} {u'return': {}} Job failed: Image size is too large for this cluster size {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213 index 29d25bcee1..4054439e3c 100755 --- a/tests/qemu-iotests/213 +++ b/tests/qemu-iotests/213 @@ -27,7 +27,7 @@ iotests.verify_image_format(supported_fmts=['vhdx']) iotests.verify_protocol(supported=['file']) def blockdev_create(vm, options): - result = vm.qmp_log('x-blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) if 'return' in result: assert result['return'] == {} diff --git a/tests/qemu-iotests/213.out b/tests/qemu-iotests/213.out index f18a39a4b3..e1dcd47201 100644 --- a/tests/qemu-iotests/213.out +++ b/tests/qemu-iotests/213.out @@ -1,13 +1,13 @@ === Successful image creation (defaults) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} {'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'imgfile', 'size': 134217728}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'imgfile', 'size': 134217728}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -19,12 +19,12 @@ cluster_size: 8388608 === Successful image creation (explicit defaults) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 8388608, 'driver': 'vhdx', 'subformat': 'dynamic', 'log-size': 1048576, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': True, 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 8388608, 'driver': 'vhdx', 'subformat': 'dynamic', 'log-size': 1048576, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': True, 'size': 67108864}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -36,12 +36,12 @@ cluster_size: 8388608 === Successful image creation (with non-default options) === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 268435456, 'driver': 'vhdx', 'subformat': 'fixed', 'log-size': 8388608, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': False, 'size': 33554432}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 268435456, 'driver': 'vhdx', 'subformat': 'fixed', 'log-size': 8388608, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': False, 'size': 33554432}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -53,7 +53,7 @@ cluster_size: 268435456 === Invalid BlockdevRef === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': "this doesn't exist", 'size': 33554432}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': "this doesn't exist", 'size': 33554432}}} {u'return': {}} Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} @@ -61,7 +61,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi === Zero size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 0}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 0}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -73,7 +73,7 @@ cluster_size: 8388608 === Maximum size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177664}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177664}}} {u'return': {}} {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} @@ -85,25 +85,25 @@ cluster_size: 67108864 === Invalid sizes === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 18446744073709551104L}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 18446744073709551104L}}} {u'return': {}} Job failed: Image size too large; max of 64TB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775808L}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775808L}}} {u'return': {}} Job failed: Image size too large; max of 64TB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775296}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775296}}} {u'return': {}} Job failed: Image size too large; max of 64TB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177665}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177665}}} {u'return': {}} Job failed: Image size too large; max of 64TB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} @@ -111,31 +111,31 @@ Job failed: Image size too large; max of 64TB === Invalid block size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 1234567, 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 1234567, 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Block size must be a multiple of 1 MB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 128, 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 128, 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Block size must be a multiple of 1 MB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 3145728, 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 3145728, 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Block size must be a power of two {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 536870912, 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 536870912, 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Block size must not exceed 268435456 {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 0, 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 0, 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Block size must be a multiple of 1 MB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} @@ -143,25 +143,25 @@ Job failed: Block size must be a multiple of 1 MB === Invalid log size === -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 1234567, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 1234567, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Log size must be a multiple of 1 MB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 128, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 128, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Log size must be a multiple of 1 MB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 4294967296, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 4294967296, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Log size must be smaller than 4 GB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} {u'return': {}} -{'execute': 'x-blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 0, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 0, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} {u'return': {}} Job failed: Log size must be a multiple of 1 MB {'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} From eb7ccb3c0e14ae5b6ce3254165b91a7ed77527dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 02:40:55 -0300 Subject: [PATCH 0831/2380] trace: Sort trace-events-subdirs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having these entries sorted helps to add new ones. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180528054055.21153-1-f4bug@amsat.org Signed-off-by: Stefan Hajnoczi --- Makefile.objs | 84 +++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/Makefile.objs b/Makefile.objs index c6c3554203..2c8cb72407 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -196,66 +196,66 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/ ###################################################################### trace-events-subdirs = -trace-events-subdirs += util -trace-events-subdirs += crypto -trace-events-subdirs += io -trace-events-subdirs += migration +trace-events-subdirs += accel/kvm +trace-events-subdirs += accel/tcg +trace-events-subdirs += audio trace-events-subdirs += block trace-events-subdirs += chardev +trace-events-subdirs += crypto +trace-events-subdirs += hw/9pfs +trace-events-subdirs += hw/acpi +trace-events-subdirs += hw/alpha +trace-events-subdirs += hw/arm +trace-events-subdirs += hw/audio trace-events-subdirs += hw/block trace-events-subdirs += hw/block/dataplane trace-events-subdirs += hw/char -trace-events-subdirs += hw/intc -trace-events-subdirs += hw/net -trace-events-subdirs += hw/rdma -trace-events-subdirs += hw/rdma/vmw -trace-events-subdirs += hw/virtio -trace-events-subdirs += hw/audio -trace-events-subdirs += hw/misc -trace-events-subdirs += hw/misc/macio -trace-events-subdirs += hw/usb -trace-events-subdirs += hw/scsi -trace-events-subdirs += hw/nvram trace-events-subdirs += hw/display -trace-events-subdirs += hw/input -trace-events-subdirs += hw/timer trace-events-subdirs += hw/dma -trace-events-subdirs += hw/sparc -trace-events-subdirs += hw/sparc64 -trace-events-subdirs += hw/sd -trace-events-subdirs += hw/isa -trace-events-subdirs += hw/mem +trace-events-subdirs += hw/hppa trace-events-subdirs += hw/i386 trace-events-subdirs += hw/i386/xen -trace-events-subdirs += hw/9pfs -trace-events-subdirs += hw/ppc +trace-events-subdirs += hw/ide +trace-events-subdirs += hw/input +trace-events-subdirs += hw/intc +trace-events-subdirs += hw/isa +trace-events-subdirs += hw/mem +trace-events-subdirs += hw/misc +trace-events-subdirs += hw/misc/macio +trace-events-subdirs += hw/net +trace-events-subdirs += hw/nvram trace-events-subdirs += hw/pci trace-events-subdirs += hw/pci-host +trace-events-subdirs += hw/ppc +trace-events-subdirs += hw/rdma +trace-events-subdirs += hw/rdma/vmw trace-events-subdirs += hw/s390x -trace-events-subdirs += hw/vfio -trace-events-subdirs += hw/acpi -trace-events-subdirs += hw/arm -trace-events-subdirs += hw/alpha -trace-events-subdirs += hw/hppa -trace-events-subdirs += hw/xen -trace-events-subdirs += hw/ide +trace-events-subdirs += hw/scsi +trace-events-subdirs += hw/sd +trace-events-subdirs += hw/sparc +trace-events-subdirs += hw/sparc64 +trace-events-subdirs += hw/timer trace-events-subdirs += hw/tpm -trace-events-subdirs += ui -trace-events-subdirs += audio +trace-events-subdirs += hw/usb +trace-events-subdirs += hw/vfio +trace-events-subdirs += hw/virtio +trace-events-subdirs += hw/xen +trace-events-subdirs += io +trace-events-subdirs += linux-user +trace-events-subdirs += migration +trace-events-subdirs += nbd trace-events-subdirs += net +trace-events-subdirs += qapi +trace-events-subdirs += qom +trace-events-subdirs += scsi trace-events-subdirs += target/arm trace-events-subdirs += target/i386 trace-events-subdirs += target/mips -trace-events-subdirs += target/sparc -trace-events-subdirs += target/s390x trace-events-subdirs += target/ppc -trace-events-subdirs += qom -trace-events-subdirs += linux-user -trace-events-subdirs += qapi -trace-events-subdirs += accel/tcg -trace-events-subdirs += accel/kvm -trace-events-subdirs += nbd -trace-events-subdirs += scsi +trace-events-subdirs += target/s390x +trace-events-subdirs += target/sparc +trace-events-subdirs += ui +trace-events-subdirs += util trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) From 74f38e96b321ef8df2bf7fa1bd4f673ef06aca5b Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 16 May 2018 17:06:14 +0200 Subject: [PATCH 0832/2380] numa: clarify error message when node index is out of range in -numa dist, ... When using following CLI: -numa dist,src=128,dst=1,val=20 user gets a rather confusing error message: "Invalid node 128, max possible could be 128" Where 128 is number of nodes that QEMU supports (MAX_NODES), while src/dst is an index up to that limit, so it should be MAX_NODES - 1 in error message. Make error message to explicitly state valid range for node index to be more clear. Signed-off-by: Igor Mammedov Message-Id: <1526483174-169008-1-git-send-email-imammedo@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Eduardo Habkost --- numa.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/numa.c b/numa.c index aac22a9612..efc78b2f17 100644 --- a/numa.c +++ b/numa.c @@ -141,9 +141,8 @@ static void parse_numa_distance(NumaDistOptions *dist, Error **errp) uint8_t val = dist->val; if (src >= MAX_NODES || dst >= MAX_NODES) { - error_setg(errp, - "Invalid node %d, max possible could be %d", - MAX(src, dst), MAX_NODES); + error_setg(errp, "Parameter '%s' expects an integer between 0 and %d", + src >= MAX_NODES ? "src" : "dst", MAX_NODES - 1); return; } From 7a3099fc9c5c7789fa1613165812bbc8bd28ee52 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 4 May 2018 10:37:39 +0200 Subject: [PATCH 0833/2380] numa: postpone options post-processing till machine_run_board_init() in preparation for numa options to being handled via QMP before machine_run_board_init(), move final numa configuration checks and processing to machine_run_board_init() so it could take into account both CLI (via parse_numa_opts()) and QMP input Signed-off-by: Igor Mammedov Reviewed-by: Eduardo Habkost Message-Id: <1525423069-61903-2-git-send-email-imammedo@redhat.com> Signed-off-by: Eduardo Habkost --- hw/core/machine.c | 5 +++-- include/sysemu/numa.h | 1 + numa.c | 13 ++++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 2040177664..617e5f8d75 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -737,7 +737,7 @@ static char *cpu_slot_to_string(const CPUArchId *cpu) return g_string_free(s, false); } -static void machine_numa_finish_init(MachineState *machine) +static void machine_numa_finish_cpu_init(MachineState *machine) { int i; bool default_mapping; @@ -792,7 +792,8 @@ void machine_run_board_init(MachineState *machine) MachineClass *machine_class = MACHINE_GET_CLASS(machine); if (nb_numa_nodes) { - machine_numa_finish_init(machine); + numa_complete_configuration(machine); + machine_numa_finish_cpu_init(machine); } /* If the machine supports the valid_cpu_types check and the user diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h index d99e5474b4..21713b7e2f 100644 --- a/include/sysemu/numa.h +++ b/include/sysemu/numa.h @@ -23,6 +23,7 @@ struct NumaNodeMem { extern NodeInfo numa_info[MAX_NODES]; void parse_numa_opts(MachineState *ms); +void numa_complete_configuration(MachineState *ms); void query_numa_node_mem(NumaNodeMem node_mem[]); extern QemuOptsList qemu_numa_opts; void numa_legacy_auto_assign_ram(MachineClass *mc, NodeInfo *nodes, diff --git a/numa.c b/numa.c index efc78b2f17..ad1d7934f2 100644 --- a/numa.c +++ b/numa.c @@ -338,15 +338,11 @@ void numa_default_auto_assign_ram(MachineClass *mc, NodeInfo *nodes, nodes[i].node_mem = size - usedmem; } -void parse_numa_opts(MachineState *ms) +void numa_complete_configuration(MachineState *ms) { int i; MachineClass *mc = MACHINE_GET_CLASS(ms); - if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) { - exit(1); - } - /* * If memory hotplug is enabled (slots > 0) but without '-numa' * options explicitly on CLI, guestes will break. @@ -433,6 +429,13 @@ void parse_numa_opts(MachineState *ms) } } +void parse_numa_opts(MachineState *ms) +{ + if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) { + exit(1); + } +} + void numa_cpu_pre_plug(const CPUArchId *slot, DeviceState *dev, Error **errp) { int node_id = object_property_get_int(OBJECT(dev), "node-id", &error_abort); From 3319b4efc295b421fac442746202c9a1da193973 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 4 May 2018 10:37:40 +0200 Subject: [PATCH 0834/2380] numa: split out NumaOptions parsing into set_numa_options() it will allow to reuse set_numa_options() for parsing configuration commands received via QMP interface Signed-off-by: Igor Mammedov Message-Id: <1525423069-61903-3-git-send-email-imammedo@redhat.com> Signed-off-by: Eduardo Habkost --- include/sysemu/numa.h | 1 + numa.c | 46 +++++++++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h index 21713b7e2f..7a0ae751aa 100644 --- a/include/sysemu/numa.h +++ b/include/sysemu/numa.h @@ -22,6 +22,7 @@ struct NumaNodeMem { }; extern NodeInfo numa_info[MAX_NODES]; +int parse_numa(void *opaque, QemuOpts *opts, Error **errp); void parse_numa_opts(MachineState *ms); void numa_complete_configuration(MachineState *ms); void query_numa_node_mem(NumaNodeMem node_mem[]); diff --git a/numa.c b/numa.c index ad1d7934f2..53bd65a05d 100644 --- a/numa.c +++ b/numa.c @@ -169,28 +169,11 @@ static void parse_numa_distance(NumaDistOptions *dist, Error **errp) have_numa_distance = true; } -static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) +static +void set_numa_options(MachineState *ms, NumaOptions *object, Error **errp) { - NumaOptions *object = NULL; - MachineState *ms = opaque; Error *err = NULL; - { - Visitor *v = opts_visitor_new(opts); - visit_type_NumaOptions(v, NULL, &object, &err); - visit_free(v); - } - - if (err) { - goto end; - } - - /* Fix up legacy suffix-less format */ - if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) { - const char *mem_str = qemu_opt_get(opts, "mem"); - qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); - } - switch (object->type) { case NUMA_OPTIONS_TYPE_NODE: parse_numa_node(ms, &object->u.node, &err); @@ -223,6 +206,31 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) abort(); } +end: + error_propagate(errp, err); +} + +int parse_numa(void *opaque, QemuOpts *opts, Error **errp) +{ + NumaOptions *object = NULL; + MachineState *ms = MACHINE(opaque); + Error *err = NULL; + Visitor *v = opts_visitor_new(opts); + + visit_type_NumaOptions(v, NULL, &object, &err); + visit_free(v); + if (err) { + goto end; + } + + /* Fix up legacy suffix-less format */ + if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) { + const char *mem_str = qemu_opt_get(opts, "mem"); + qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); + } + + set_numa_options(ms, object, &err); + end: qapi_free_NumaOptions(object); if (err) { From 8a36283e120ab3633557d6732fa700366e0137c7 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 4 May 2018 10:37:41 +0200 Subject: [PATCH 0835/2380] qapi: introduce preconfig runstate New preconfig runstate will be used in follow up patches related to introducing --preconfig CLI option and is intended to replace prelaunch runstate from QEMU start up to machine_init callback. Signed-off-by: Igor Mammedov Message-Id: <1525423069-61903-4-git-send-email-imammedo@redhat.com> Reviewed-by: Eric Blake [ehabkost: Changed "since 2.13" to "since 3.0"] Signed-off-by: Eduardo Habkost --- qapi/run-state.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qapi/run-state.json b/qapi/run-state.json index 1c9fff3aef..d3c2a9113b 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -49,12 +49,15 @@ # @colo: guest is paused to save/restore VM state under colo checkpoint, # VM can not get into this state unless colo capability is enabled # for migration. (since 2.8) +# @preconfig: QEMU is paused before board specific init callback is executed. +# The state is reachable only if the --preconfig CLI option is used. +# (Since 3.0) ## { 'enum': 'RunState', 'data': [ 'debug', 'inmigrate', 'internal-error', 'io-error', 'paused', 'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm', 'running', 'save-vm', 'shutdown', 'suspended', 'watchdog', - 'guest-panicked', 'colo' ] } + 'guest-panicked', 'colo', 'preconfig' ] } ## # @StatusInfo: From 71dc578e116599ea73c8a2a4e693134702ec0e83 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 4 May 2018 10:37:42 +0200 Subject: [PATCH 0836/2380] hmp: disable monitor in preconfig state Ban it for now, if someone would need it to work early, one would have to implement checks if HMP command is valid at preconfig state. Signed-off-by: Igor Mammedov Reviewed-by: Eric Blake Message-Id: <1525423069-61903-5-git-send-email-imammedo@redhat.com> Signed-off-by: Eduardo Habkost --- monitor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/monitor.c b/monitor.c index 46814af533..9e50418afe 100644 --- a/monitor.c +++ b/monitor.c @@ -3371,6 +3371,12 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline) trace_handle_hmp_command(mon, cmdline); + if (runstate_check(RUN_STATE_PRECONFIG)) { + monitor_printf(mon, "HMP not available in preconfig state, " + "use QMP instead\n"); + return; + } + cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table); if (!cmd) { return; From d6fe3d02e9a2ce7b63a0723d0b71f3013f59d705 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 11 May 2018 18:51:43 +0200 Subject: [PATCH 0837/2380] qapi: introduce new cmd option "allow-preconfig" New option will be used to allow commands, which are prepared/need to run, during preconfig state. Other commands that should be able to run in preconfig state, should be amended to not expect machine in initialized state or deal with it. For compatibility reasons, commands that don't use new flag 'allow-preconfig' explicitly are not permitted to run in preconfig state but allowed in all other states like they used to be. Within this patch allow following commands in preconfig state: qmp_capabilities query-qmp-schema query-commands query-command-line-options query-status exit-preconfig to allow qmp connection, basic introspection and moving to the next state. PS: set-numa-node and query-hotpluggable-cpus will be enabled later in a separate patches. Signed-off-by: Igor Mammedov Message-Id: <1526057503-39287-1-git-send-email-imammedo@redhat.com> Reviewed-by: Eric Blake [ehabkost: Changed "since 2.13" to "since 3.0"] Signed-off-by: Eduardo Habkost --- docs/devel/qapi-code-gen.txt | 11 ++++++++++- include/qapi/qmp/dispatch.h | 1 + monitor.c | 5 ++--- qapi/introspect.json | 5 ++++- qapi/misc.json | 9 ++++++--- qapi/run-state.json | 3 ++- scripts/qapi/commands.py | 11 +++++++---- scripts/qapi/common.py | 18 +++++++++++------- scripts/qapi/doc.py | 4 ++-- scripts/qapi/introspect.py | 7 ++++--- tests/qapi-schema/test-qapi.py | 4 ++-- 11 files changed, 51 insertions(+), 27 deletions(-) diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index b9b6eabd08..1366228b2a 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -559,7 +559,7 @@ following example objects: Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, '*returns': TYPE-NAME, '*boxed': true, '*gen': false, '*success-response': false, - '*allow-oob': true } + '*allow-oob': true, '*allow-preconfig': true } Commands are defined by using a dictionary containing several members, where three members are most common. The 'command' member is a @@ -683,6 +683,15 @@ OOB command handlers must satisfy the following conditions: If in doubt, do not implement OOB execution support. +A command may use the optional 'allow-preconfig' key to permit its execution +at early runtime configuration stage (preconfig runstate). +If not specified then a command defaults to 'allow-preconfig': false. + +An example of declaring a command that is enabled during preconfig: + { 'command': 'qmp_capabilities', + 'data': { '*enable': [ 'QMPCapability' ] }, + 'allow-preconfig': true } + === Events === Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index ffb4652f71..b366bb48bd 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -23,6 +23,7 @@ typedef enum QmpCommandOptions QCO_NO_OPTIONS = 0x0, QCO_NO_SUCCESS_RESP = (1U << 0), QCO_ALLOW_OOB = (1U << 1), + QCO_ALLOW_PRECONFIG = (1U << 2), } QmpCommandOptions; typedef struct QmpCommand diff --git a/monitor.c b/monitor.c index 9e50418afe..922cfc041e 100644 --- a/monitor.c +++ b/monitor.c @@ -1179,8 +1179,7 @@ static void monitor_init_qmp_commands(void) qmp_init_marshal(&qmp_commands); qmp_register_command(&qmp_commands, "query-qmp-schema", - qmp_query_qmp_schema, - QCO_NO_OPTIONS); + qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG); qmp_register_command(&qmp_commands, "device_add", qmp_device_add, QCO_NO_OPTIONS); qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add, @@ -1190,7 +1189,7 @@ static void monitor_init_qmp_commands(void) QTAILQ_INIT(&qmp_cap_negotiation_commands); qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", - qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS); + qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG); } static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap) diff --git a/qapi/introspect.json b/qapi/introspect.json index c7f67b7d78..80a0a3e656 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -262,13 +262,16 @@ # @allow-oob: whether the command allows out-of-band execution. # (Since: 2.12) # +# @allow-preconfig: command can be executed in preconfig runstate, +# default: false (Since 3.0) +# # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # # Since: 2.5 ## { 'struct': 'SchemaInfoCommand', 'data': { 'arg-type': 'str', 'ret-type': 'str', - 'allow-oob': 'bool' } } + 'allow-oob': 'bool', 'allow-preconfig': 'bool' } } ## # @SchemaInfoEvent: diff --git a/qapi/misc.json b/qapi/misc.json index 99bcaacd62..ae2bb27b83 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -37,7 +37,8 @@ # ## { 'command': 'qmp_capabilities', - 'data': { '*enable': [ 'QMPCapability' ] } } + 'data': { '*enable': [ 'QMPCapability' ] }, + 'allow-preconfig': true } ## # @QMPCapability: @@ -155,7 +156,8 @@ # Note: This example has been shortened as the real response is too long. # ## -{ 'command': 'query-commands', 'returns': ['CommandInfo'] } +{ 'command': 'query-commands', 'returns': ['CommandInfo'], + 'allow-preconfig': true } ## # @LostTickPolicy: @@ -2648,7 +2650,8 @@ # ## {'command': 'query-command-line-options', 'data': { '*option': 'str' }, - 'returns': ['CommandLineOptionInfo'] } + 'returns': ['CommandLineOptionInfo'], + 'allow-preconfig': true } ## # @X86CPURegister32: diff --git a/qapi/run-state.json b/qapi/run-state.json index d3c2a9113b..332e44897b 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -94,7 +94,8 @@ # "status": "running" } } # ## -{ 'command': 'query-status', 'returns': 'StatusInfo' } +{ 'command': 'query-status', 'returns': 'StatusInfo', + 'allow-preconfig': true } ## # @SHUTDOWN: diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 0c5da3a54d..3b0867c14f 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -193,13 +193,15 @@ out: return ret -def gen_register_command(name, success_response, allow_oob): +def gen_register_command(name, success_response, allow_oob, allow_preconfig): options = [] if not success_response: options += ['QCO_NO_SUCCESS_RESP'] if allow_oob: options += ['QCO_ALLOW_OOB'] + if allow_preconfig: + options += ['QCO_ALLOW_PRECONFIG'] if not options: options = ['QCO_NO_OPTIONS'] @@ -275,8 +277,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); c_prefix=c_name(self._prefix, protect=False))) genc.add(gen_registry(self._regy, self._prefix)) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): if not gen: return self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) @@ -285,7 +287,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); self._genc.add(gen_marshal_output(ret_type)) self._genh.add(gen_marshal_decl(name)) self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) - self._regy += gen_register_command(name, success_response, allow_oob) + self._regy += gen_register_command(name, success_response, allow_oob, + allow_preconfig) def gen_commands(schema, output_dir, prefix): diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index a032cec375..e82990f0f2 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -872,7 +872,8 @@ def check_keys(expr_elem, meta, required, optional=[]): raise QAPISemError(info, "'%s' of %s '%s' should only use false value" % (key, meta, name)) - if (key == 'boxed' or key == 'allow-oob') and value is not True: + if (key == 'boxed' or key == 'allow-oob' or + key == 'allow-preconfig') and value is not True: raise QAPISemError(info, "'%s' of %s '%s' should only use true value" % (key, meta, name)) @@ -922,7 +923,7 @@ def check_exprs(exprs): meta = 'command' check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response', - 'boxed', 'allow-oob']) + 'boxed', 'allow-oob', 'allow-preconfig']) elif 'event' in expr: meta = 'event' check_keys(expr_elem, 'event', [], ['data', 'boxed']) @@ -1044,8 +1045,8 @@ class QAPISchemaVisitor(object): def visit_alternate_type(self, name, info, variants): pass - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): pass def visit_event(self, name, info, arg_type, boxed): @@ -1422,7 +1423,7 @@ class QAPISchemaAlternateType(QAPISchemaType): class QAPISchemaCommand(QAPISchemaEntity): def __init__(self, name, info, doc, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + gen, success_response, boxed, allow_oob, allow_preconfig): QAPISchemaEntity.__init__(self, name, info, doc) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) @@ -1434,6 +1435,7 @@ class QAPISchemaCommand(QAPISchemaEntity): self.success_response = success_response self.boxed = boxed self.allow_oob = allow_oob + self.allow_preconfig = allow_preconfig def check(self, schema): if self._arg_type_name: @@ -1458,7 +1460,8 @@ class QAPISchemaCommand(QAPISchemaEntity): visitor.visit_command(self.name, self.info, self.arg_type, self.ret_type, self.gen, self.success_response, - self.boxed, self.allow_oob) + self.boxed, self.allow_oob, + self.allow_preconfig) class QAPISchemaEvent(QAPISchemaEntity): @@ -1678,6 +1681,7 @@ class QAPISchema(object): success_response = expr.get('success-response', True) boxed = expr.get('boxed', False) allow_oob = expr.get('allow-oob', False) + allow_preconfig = expr.get('allow-preconfig', False) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( name, info, doc, 'arg', self._make_members(data, info)) @@ -1686,7 +1690,7 @@ class QAPISchema(object): rets = self._make_array_type(rets[0], info) self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, gen, success_response, - boxed, allow_oob)) + boxed, allow_oob, allow_preconfig)) def _def_event(self, expr, info, doc): name = expr['event'] diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 9b312b2c51..b5630844f9 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -226,8 +226,8 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): name=doc.symbol, body=texi_entity(doc, 'Members'))) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): doc = self.cur_doc if boxed: body = texi_body(doc) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index f9e67e8227..5b6c72c7b2 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -171,14 +171,15 @@ const QLitObject %(c_name)s = %(c_string)s; {'members': [{'type': self._use_type(m.type)} for m in variants.variants]}) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type self._gen_qlit(name, 'command', {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type), - 'allow-oob': allow_oob}) + 'allow-oob': allow_oob, + 'allow-preconfig': allow_preconfig}) def visit_event(self, name, info, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index c1a144ba29..89b92eddd4 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -41,8 +41,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print('alternate %s' % name) self._print_variants(variants) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): print('command %s %s -> %s' % \ (name, arg_type and arg_type.name, ret_type and ret_type.name)) print(' gen=%s success_response=%s boxed=%s oob=%s' % \ From 7b13f2c27a02499e9f8d955e0a4c68a5165e150d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 11 May 2018 19:15:59 +0200 Subject: [PATCH 0838/2380] tests: qapi-schema tests for allow-preconfig use new allow-preconfig parameter in tests and make sure that the QAPISchema can parse allow-preconfig correctly Signed-off-by: Igor Mammedov Reviewed-by: Eric Blake Message-Id: <1526058959-41425-1-git-send-email-imammedo@redhat.com> Signed-off-by: Eduardo Habkost --- tests/Makefile.include | 1 + tests/qapi-schema/allow-preconfig-test.err | 1 + tests/qapi-schema/allow-preconfig-test.exit | 1 + tests/qapi-schema/allow-preconfig-test.json | 2 ++ tests/qapi-schema/allow-preconfig-test.out | 0 tests/qapi-schema/doc-good.out | 4 ++-- tests/qapi-schema/ident-with-escape.out | 2 +- tests/qapi-schema/indented-expr.out | 4 ++-- tests/qapi-schema/qapi-schema-test.json | 4 ++-- tests/qapi-schema/qapi-schema-test.out | 22 ++++++++++----------- tests/qapi-schema/test-qapi.py | 4 ++-- tests/test-qmp-cmds.c | 2 +- 12 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 tests/qapi-schema/allow-preconfig-test.err create mode 100644 tests/qapi-schema/allow-preconfig-test.exit create mode 100644 tests/qapi-schema/allow-preconfig-test.json create mode 100644 tests/qapi-schema/allow-preconfig-test.out diff --git a/tests/Makefile.include b/tests/Makefile.include index b499ba1813..86f90c0cb0 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -525,6 +525,7 @@ qapi-schema += missing-type.json qapi-schema += nested-struct-data.json qapi-schema += non-objects.json qapi-schema += oob-test.json +qapi-schema += allow-preconfig-test.json qapi-schema += pragma-doc-required-crap.json qapi-schema += pragma-extra-junk.json qapi-schema += pragma-name-case-whitelist-crap.json diff --git a/tests/qapi-schema/allow-preconfig-test.err b/tests/qapi-schema/allow-preconfig-test.err new file mode 100644 index 0000000000..700d583306 --- /dev/null +++ b/tests/qapi-schema/allow-preconfig-test.err @@ -0,0 +1 @@ +tests/qapi-schema/allow-preconfig-test.json:2: 'allow-preconfig' of command 'allow-preconfig-test' should only use true value diff --git a/tests/qapi-schema/allow-preconfig-test.exit b/tests/qapi-schema/allow-preconfig-test.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/allow-preconfig-test.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/allow-preconfig-test.json b/tests/qapi-schema/allow-preconfig-test.json new file mode 100644 index 0000000000..d9f0e914df --- /dev/null +++ b/tests/qapi-schema/allow-preconfig-test.json @@ -0,0 +1,2 @@ +# Check against allow-preconfig illegal value +{ 'command': 'allow-preconfig-test', 'allow-preconfig': 'some-string' } diff --git a/tests/qapi-schema/allow-preconfig-test.out b/tests/qapi-schema/allow-preconfig-test.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 63058b1590..9c8a4838e1 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -28,9 +28,9 @@ object q_obj_cmd-arg member arg2: str optional=True member arg3: bool optional=False command cmd q_obj_cmd-arg -> Object - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False command cmd-boxed Object -> None - gen=True success_response=True boxed=True oob=False + gen=True success_response=True boxed=True oob=False preconfig=False doc freeform body= = Section diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out index 82213aa51d..24c976f473 100644 --- a/tests/qapi-schema/ident-with-escape.out +++ b/tests/qapi-schema/ident-with-escape.out @@ -5,4 +5,4 @@ module ident-with-escape.json object q_obj_fooA-arg member bar1: str optional=False command fooA q_obj_fooA-arg -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index 862678f8f4..bd8a48630e 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -3,6 +3,6 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE module indented-expr.json command eins None -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False command zwei None -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 06e30f452e..46c7282945 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -139,8 +139,8 @@ { 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' } { 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true } -# Smoke test on Out-Of-Band -{ 'command': 'an-oob-command', 'allow-oob': true } +# Smoke test on Out-Of-Band and allow-preconfig-test +{ 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': true } # For testing integer range flattening in opts-visitor. The following schema # corresponds to the option format: diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 467577d770..542a19c407 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -16,7 +16,7 @@ object Empty1 object Empty2 base Empty1 command user_def_cmd0 Empty2 -> Empty2 - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False enum QEnumTwo ['value1', 'value2'] prefix QENUM_TWO object UserDefOne @@ -143,31 +143,31 @@ object UserDefNativeListUnion case sizes: q_obj_sizeList-wrapper case any: q_obj_anyList-wrapper command user_def_cmd None -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_user_def_cmd1-arg member ud1a: UserDefOne optional=False command user_def_cmd1 q_obj_user_def_cmd1-arg -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_user_def_cmd2-arg member ud1a: UserDefOne optional=False member ud1b: UserDefOne optional=True command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_guest-get-time-arg member a: int optional=False member b: int optional=True command guest-get-time q_obj_guest-get-time-arg -> int - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_guest-sync-arg member arg: any optional=False command guest-sync q_obj_guest-sync-arg -> any - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False command boxed-struct UserDefZero -> None - gen=True success_response=True boxed=True oob=False + gen=True success_response=True boxed=True oob=False preconfig=False command boxed-union UserDefNativeListUnion -> None - gen=True success_response=True boxed=True oob=False -command an-oob-command None -> None - gen=True success_response=True boxed=False oob=True + gen=True success_response=True boxed=True oob=False preconfig=False +command test-flags-command None -> None + gen=True success_response=True boxed=False oob=True preconfig=True object UserDefOptions member i64: intList optional=True member u64: uint64List optional=True @@ -231,4 +231,4 @@ object q_obj___org.qemu_x-command-arg member c: __org.qemu_x-Union2 optional=False member d: __org.qemu_x-Alt optional=False command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 89b92eddd4..4512a41504 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -45,8 +45,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): success_response, boxed, allow_oob, allow_preconfig): print('command %s %s -> %s' % \ (name, arg_type and arg_type.name, ret_type and ret_type.name)) - print(' gen=%s success_response=%s boxed=%s oob=%s' % \ - (gen, success_response, boxed, allow_oob)) + print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' % \ + (gen, success_response, boxed, allow_oob, allow_preconfig)) def visit_event(self, name, info, arg_type, boxed): print('event %s %s' % (name, arg_type and arg_type.name)) diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index e0ed461f58..491b0c4a44 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -16,7 +16,7 @@ void qmp_user_def_cmd(Error **errp) { } -void qmp_an_oob_command(Error **errp) +void qmp_test_flags_command(Error **errp) { } From 047f7038f586d2150f16c6d9ba9cfd0479f0f6ac Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 11 May 2018 19:24:43 +0200 Subject: [PATCH 0839/2380] cli: add --preconfig option This option allows pausing QEMU in the new RUN_STATE_PRECONFIG state, allowing the configuration of QEMU from QMP before the machine jumps into board initialization code of machine_run_board_init() The intent is to allow management to query machine state and additionally configure it using previous query results within one QEMU instance (i.e. eliminate the need to start QEMU twice, 1st to query board specific parameters and 2nd for actual VM start using query results for additional parameters). The new option complements -S option and could be used with or without it. The difference is that -S pauses QEMU when the machine is completely initialized with all devices wired up and ready to execute guest code (QEMU needs only to unpause VCPUs to let guest execute its code), while the "preconfig" option pauses QEMU early before board specific init callback (machine_run_board_init) is executed and allows the configuration of machine parameters which will be used by board init code. When early introspection/configuration is done, command 'exit-preconfig' should be used to exit RUN_STATE_PRECONFIG and transition to the next requested state (i.e. if -S is used then QEMU will pause the second time when board/device initialization is completed or start guest execution if -S isn't provided on CLI) PS: Initially 'preconfig' is planned to be used for configuring numa topology depending on board specified possible cpus layout. Signed-off-by: Igor Mammedov Reviewed-by: Eric Blake Message-Id: <1526059483-42847-1-git-send-email-imammedo@redhat.com> [ehabkost: Changed "since 2.13" to "since 3.0"] Signed-off-by: Eduardo Habkost --- include/sysemu/sysemu.h | 1 + qapi/misc.json | 23 +++++++++++++++++++++++ qapi/qmp-dispatch.c | 8 ++++++++ qemu-options.hx | 13 +++++++++++++ qemu-tech.texi | 40 ++++++++++++++++++++++++++++++++++++++++ qmp.c | 10 ++++++++++ vl.c | 35 ++++++++++++++++++++++++++++++++++- 7 files changed, 129 insertions(+), 1 deletion(-) diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 544ab77a2b..e893f72f3b 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -66,6 +66,7 @@ typedef enum WakeupReason { QEMU_WAKEUP_REASON_OTHER, } WakeupReason; +void qemu_exit_preconfig_request(void); void qemu_system_reset_request(ShutdownCause reason); void qemu_system_suspend_request(void); void qemu_register_suspend_notifier(Notifier *notifier); diff --git a/qapi/misc.json b/qapi/misc.json index ae2bb27b83..bd6d1805ab 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -1244,6 +1244,29 @@ ## { 'command': 'cont' } +## +# @exit-preconfig: +# +# Exit from "preconfig" state +# +# This command makes QEMU exit the preconfig state and proceed with +# VM initialization using configuration data provided on the command line +# and via the QMP monitor during the preconfig state. The command is only +# available during the preconfig state (i.e. when the --preconfig command +# line option was in use). +# +# Since 3.0 +# +# Returns: nothing +# +# Example: +# +# -> { "execute": "exit-preconfig" } +# <- { "return": {} } +# +## +{ 'command': 'exit-preconfig', 'allow-preconfig': true } + ## # @system_wakeup: # diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index f9377b27fd..935f9e159c 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -18,6 +18,7 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qbool.h" +#include "sysemu/sysemu.h" QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) { @@ -101,6 +102,13 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, return NULL; } + if (runstate_check(RUN_STATE_PRECONFIG) && + !(cmd->options & QCO_ALLOW_PRECONFIG)) { + error_setg(errp, "The command '%s' isn't permitted in '%s' state", + cmd->name, RunState_str(RUN_STATE_PRECONFIG)); + return NULL; + } + if (!qdict_haskey(dict, "arguments")) { args = qdict_new(); } else { diff --git a/qemu-options.hx b/qemu-options.hx index abbfa6ae9e..2f61ea42ee 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3299,6 +3299,19 @@ STEXI Run the emulation in single step mode. ETEXI +DEF("preconfig", 0, QEMU_OPTION_preconfig, \ + "--preconfig pause QEMU before machine is initialized\n", + QEMU_ARCH_ALL) +STEXI +@item --preconfig +@findex --preconfig +Pause QEMU for interactive configuration before the machine is created, +which allows querying and configuring properties that will affect +machine initialization. Use the QMP command 'exit-preconfig' to exit +the preconfig state and move to the next state (ie. run guest if -S +isn't used or pause the second time if -S is used). +ETEXI + DEF("S", 0, QEMU_OPTION_S, \ "-S freeze CPU at startup (use 'c' to start execution)\n", QEMU_ARCH_ALL) diff --git a/qemu-tech.texi b/qemu-tech.texi index 52a56ae25e..dcecba83cb 100644 --- a/qemu-tech.texi +++ b/qemu-tech.texi @@ -5,6 +5,7 @@ * CPU emulation:: * Translator Internals:: * QEMU compared to other emulators:: +* Managed start up options:: * Bibliography:: @end menu @@ -314,6 +315,45 @@ VirtualBox [9], Xen [10] and KVM [11] are based on QEMU. QEMU-SystemC [12] uses QEMU to simulate a system where some hardware devices are developed in SystemC. +@node Managed start up options +@section Managed start up options + +In system mode emulation, it's possible to create a VM in a paused state using +the -S command line option. In this state the machine is completely initialized +according to command line options and ready to execute VM code but VCPU threads +are not executing any code. The VM state in this paused state depends on the way +QEMU was started. It could be in: +@table @asis +@item initial state (after reset/power on state) +@item with direct kernel loading, the initial state could be amended to execute +code loaded by QEMU in the VM's RAM and with incoming migration +@item with incoming migration, initial state will by amended with the migrated +machine state after migration completes. +@end table + +This paused state is typically used by users to query machine state and/or +additionally configure the machine (by hotplugging devices) in runtime before +allowing VM code to run. + +However, at the -S pause point, it's impossible to configure options that affect +initial VM creation (like: -smp/-m/-numa ...) or cold plug devices. That's +when the --preconfig command line option should be used. It allows pausing QEMU +before the initial VM creation, in a new preconfig state, where additional +queries and configuration can be performed via QMP before moving on to +the resulting configuration startup. In the preconfig state, QEMU only allows +a limited set of commands over the QMP monitor, where the commands do not +depend on an initialized machine, including but not limited to: +@table @asis +@item qmp_capabilities +@item query-qmp-schema +@item query-commands +@item query-status +@item exit-preconfig +@end table +The full list of commands is in QMP schema which could be queried with +query-qmp-schema, where commands supported at preconfig state have option +'allow-preconfig' set to true. + @node Bibliography @section Bibliography diff --git a/qmp.c b/qmp.c index 25fdc9a5b2..73e46d795f 100644 --- a/qmp.c +++ b/qmp.c @@ -161,6 +161,16 @@ SpiceInfo *qmp_query_spice(Error **errp) }; #endif +void qmp_exit_preconfig(Error **errp) +{ + if (!runstate_check(RUN_STATE_PRECONFIG)) { + error_setg(errp, "The command is permitted only in '%s' state", + RunState_str(RUN_STATE_PRECONFIG)); + return; + } + qemu_exit_preconfig_request(); +} + void qmp_cont(Error **errp) { BlockBackend *blk; diff --git a/vl.c b/vl.c index 038d7f8042..c4fe25560c 100644 --- a/vl.c +++ b/vl.c @@ -594,7 +594,7 @@ static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp) /***********************************************************/ /* QEMU state */ -static RunState current_run_state = RUN_STATE_PRELAUNCH; +static RunState current_run_state = RUN_STATE_PRECONFIG; /* We use RUN_STATE__MAX but any invalid value will do */ static RunState vmstop_requested = RUN_STATE__MAX; @@ -607,6 +607,13 @@ typedef struct { static const RunStateTransition runstate_transitions_def[] = { /* from -> to */ + { RUN_STATE_PRECONFIG, RUN_STATE_PRELAUNCH }, + /* Early switch to inmigrate state to allow -incoming CLI option work + * as it used to. TODO: delay actual switching to inmigrate state to + * the point after machine is built and remove this hack. + */ + { RUN_STATE_PRECONFIG, RUN_STATE_INMIGRATE }, + { RUN_STATE_DEBUG, RUN_STATE_RUNNING }, { RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_DEBUG, RUN_STATE_PRELAUNCH }, @@ -1630,6 +1637,7 @@ static pid_t shutdown_pid; static int powerdown_requested; static int debug_requested; static int suspend_requested; +static bool preconfig_exit_requested = true; static WakeupReason wakeup_reason; static NotifierList powerdown_notifiers = NOTIFIER_LIST_INITIALIZER(powerdown_notifiers); @@ -1714,6 +1722,11 @@ static int qemu_debug_requested(void) return r; } +void qemu_exit_preconfig_request(void) +{ + preconfig_exit_requested = true; +} + /* * Reset the VM. Issue an event unless @reason is SHUTDOWN_CAUSE_NONE. */ @@ -1887,6 +1900,13 @@ static bool main_loop_should_exit(void) RunState r; ShutdownCause request; + if (preconfig_exit_requested) { + if (runstate_check(RUN_STATE_PRECONFIG)) { + runstate_set(RUN_STATE_PRELAUNCH); + } + preconfig_exit_requested = false; + return true; + } if (qemu_debug_requested()) { vm_stop(RUN_STATE_DEBUG); } @@ -3667,6 +3687,9 @@ int main(int argc, char **argv, char **envp) exit(1); } break; + case QEMU_OPTION_preconfig: + preconfig_exit_requested = false; + break; case QEMU_OPTION_enable_kvm: olist = qemu_find_opts("machine"); qemu_opts_parse_noisily(olist, "accel=kvm", false); @@ -4031,6 +4054,12 @@ int main(int argc, char **argv, char **envp) replay_configure(icount_opts); + if (incoming && !preconfig_exit_requested) { + error_report("'preconfig' and 'incoming' options are " + "mutually exclusive"); + exit(EXIT_FAILURE); + } + machine_class = select_machine(); set_memory_options(&ram_slots, &maxram_size, machine_class); @@ -4548,6 +4577,10 @@ int main(int argc, char **argv, char **envp) } parse_numa_opts(current_machine); + /* do monitor/qmp handling at preconfig state if requested */ + main_loop(); + + /* from here on runstate is RUN_STATE_PRELAUNCH */ machine_run_board_init(current_machine); realtime_init(); From fb1e58f72ba8daf7446ac46b00b5d1853486d28b Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 17 May 2018 13:28:44 +0200 Subject: [PATCH 0840/2380] tests: extend qmp test with preconfig checks Add permission checks for commands at 'preconfig' stage. Signed-off-by: Igor Mammedov Message-Id: <1526556524-267991-1-git-send-email-imammedo@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Eduardo Habkost --- tests/qmp-test.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 88f867f8c0..2ee441cdb3 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -392,6 +392,52 @@ static void add_query_tests(QmpSchema *schema) } } +static bool qmp_rsp_is_err(QDict *rsp) +{ + QDict *error = qdict_get_qdict(rsp, "error"); + qobject_unref(rsp); + return !!error; +} + +static void test_qmp_preconfig(void) +{ + QDict *rsp, *ret; + QTestState *qs = qtest_startf("%s --preconfig", common_args); + + /* preconfig state */ + /* enabled commands, no error expected */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-commands' }"))); + + /* forbidden commands, expected error */ + g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); + + /* check that query-status returns preconfig state */ + rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); + ret = qdict_get_qdict(rsp, "return"); + g_assert(ret); + g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "preconfig"); + qobject_unref(rsp); + + /* exit preconfig state */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + /* check that query-status returns running state */ + rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); + ret = qdict_get_qdict(rsp, "return"); + g_assert(ret); + g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running"); + qobject_unref(rsp); + + /* check that exit-preconfig returns error after exiting preconfig */ + g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); + + /* enabled commands, no error expected */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); + + qtest_quit(qs); +} + int main(int argc, char *argv[]) { QmpSchema schema; @@ -403,6 +449,7 @@ int main(int argc, char *argv[]) qtest_add_func("qmp/oob", test_qmp_oob); qmp_schema_init(&schema); add_query_tests(&schema); + qtest_add_func("qmp/preconfig", test_qmp_preconfig); ret = g_test_run(); From 899eaab464fab310ecb16c8e2572d835ae1929da Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 4 May 2018 10:37:47 +0200 Subject: [PATCH 0841/2380] qmp: permit query-hotpluggable-cpus in preconfig state it will allow mgmt to query possible CPUs, which depends on used machine(version)/-smp options, without restarting QEMU and use results to configure numa mapping or adding CPUs with device_add* later. PS: *) device_add is not allowed to run at preconfig in this series but later it could be dealt with by injecting -device in preconfig state and letting existing -device handling to actually plug devices Signed-off-by: Igor Mammedov Message-Id: <1525423069-61903-10-git-send-email-imammedo@redhat.com> Reviewed-by: Eduardo Habkost Signed-off-by: Eduardo Habkost --- qapi/misc.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qapi/misc.json b/qapi/misc.json index bd6d1805ab..b155db2642 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -3285,7 +3285,8 @@ # ]} # ## -{ 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'] } +{ 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'], + 'allow-preconfig': true } ## # @GuidInfo: From f3be67812c226162f86ce92634bd913714445420 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 4 May 2018 10:37:48 +0200 Subject: [PATCH 0842/2380] qmp: add set-numa-node command Command is allowed to run only in preconfig stage and will allow to configure numa mapping for CPUs depending on possible CPUs layout (query-hotpluggable-cpus) for given machine instance. Example of configuration session: $QEMU -smp 2 --preconfig ... QMP: -> {'execute': 'query-hotpluggable-cpus' } <- {'return': [ {'props': {'core-id': 0, 'thread-id': 0, 'socket-id': 1}, ... }, {'props': {'core-id': 0, 'thread-id': 0, 'socket-id': 0}, ... } ]} -> {'execute': 'set-numa-node', 'arguments': { 'type': 'node', 'nodeid': 0 } } <- {'return': {}} -> {'execute': 'set-numa-node', 'arguments': { 'type': 'cpu', 'node-id': 0, 'core-id': 0, 'thread-id': 0, 'socket-id': 1, } } <- {'return': {}} -> {'execute': 'set-numa-node', 'arguments': { 'type': 'node', 'nodeid': 1 } } -> {'execute': 'set-numa-node', 'arguments': { 'type': 'cpu', 'node-id': 1, 'core-id': 0, 'thread-id': 0, 'socket-id': 0 } } <- {'return': {}} -> {'execute': 'query-hotpluggable-cpus' } <- {'return': [ {'props': {'core-id': 0, 'thread-id': 0, 'node-id': 0, 'socket-id': 1}, ... }, {'props': {'core-id': 0, 'thread-id': 0, 'node-id': 1, 'socket-id': 0}, ... } ]} Signed-off-by: Igor Mammedov Message-Id: <1525423069-61903-11-git-send-email-imammedo@redhat.com> Reviewed-by: Eduardo Habkost [ehabkost: Changed "since 2.13" to "since 3.0"] Signed-off-by: Eduardo Habkost --- numa.c | 11 +++++++++++ qapi/misc.json | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/numa.c b/numa.c index 53bd65a05d..33572bfa74 100644 --- a/numa.c +++ b/numa.c @@ -444,6 +444,17 @@ void parse_numa_opts(MachineState *ms) } } +void qmp_set_numa_node(NumaOptions *cmd, Error **errp) +{ + if (!runstate_check(RUN_STATE_PRECONFIG)) { + error_setg(errp, "The command is permitted only in '%s' state", + RunState_str(RUN_STATE_PRECONFIG)); + return; + } + + set_numa_options(MACHINE(qdev_get_machine()), cmd, errp); +} + void numa_cpu_pre_plug(const CPUArchId *slot, DeviceState *dev, Error **errp) { int node_id = object_property_get_int(OBJECT(dev), "node-id", &error_abort); diff --git a/qapi/misc.json b/qapi/misc.json index b155db2642..02bb295c13 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -3510,3 +3510,17 @@ ## { 'command': 'x-oob-test', 'data' : { 'lock': 'bool' }, 'allow-oob': true } + +## +# @set-numa-node: +# +# Runtime equivalent of '-numa' CLI option, available at +# preconfigure stage to configure numa mapping before initializing +# machine. +# +# Since 3.0 +## +{ 'command': 'set-numa-node', 'boxed': true, + 'data': 'NumaOptions', + 'allow-preconfig': true +} From c35665e1ee3d4344cd5156430ebc92310635f9bd Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 17 May 2018 13:30:07 +0200 Subject: [PATCH 0843/2380] tests: functional tests for QMP command set-numa-node * start QEMU with 2 unmapped cpus, * while in preconfig state * add 2 numa nodes * assign cpus to them * exit preconfig and in running state check that cpus are mapped correctly. Signed-off-by: Igor Mammedov Message-Id: <1526556607-268163-1-git-send-email-imammedo@redhat.com> Signed-off-by: Eduardo Habkost --- tests/libqtest.c | 7 ++++++ tests/libqtest.h | 9 +++++++ tests/numa-test.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ tests/qmp-test.c | 7 ------ 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/tests/libqtest.c b/tests/libqtest.c index 43fb97e035..e0ca19dbfe 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -1098,3 +1098,10 @@ void qtest_qmp_device_del(const char *id) qobject_unref(response1); qobject_unref(response2); } + +bool qmp_rsp_is_err(QDict *rsp) +{ + QDict *error = qdict_get_qdict(rsp, "error"); + qobject_unref(rsp); + return !!error; +} diff --git a/tests/libqtest.h b/tests/libqtest.h index cbe8df4473..ac52872cbe 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -972,4 +972,13 @@ void qtest_qmp_device_add(const char *driver, const char *id, const char *fmt, */ void qtest_qmp_device_del(const char *id); +/** + * qmp_rsp_is_err: + * @rsp: QMP response to check for error + * + * Test @rsp for error and discard @rsp. + * Returns 'true' if there is error in @rsp and 'false' otherwise. + */ +bool qmp_rsp_is_err(QDict *rsp); + #endif diff --git a/tests/numa-test.c b/tests/numa-test.c index 169213fc1c..b7a6ef8815 100644 --- a/tests/numa-test.c +++ b/tests/numa-test.c @@ -260,6 +260,66 @@ static void aarch64_numa_cpu(const void *data) g_free(cli); } +static void pc_dynamic_cpu_cfg(const void *data) +{ + QObject *e; + QDict *resp; + QList *cpus; + QTestState *qs; + + qs = qtest_startf("%s %s", data ? (char *)data : "", + "-nodefaults --preconfig -smp 2"); + + /* create 2 numa nodes */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 0 } }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 1 } }"))); + + /* map 2 cpus in non default reverse order + * i.e socket1->node0, socket0->node1 + */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'cpu', 'node-id': 0, 'socket-id': 1 } }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'cpu', 'node-id': 1, 'socket-id': 0 } }"))); + + /* let machine initialization to complete and run */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + /* check that CPUs are mapped as expected */ + resp = qtest_qmp(qs, "{ 'execute': 'query-hotpluggable-cpus'}"); + g_assert(qdict_haskey(resp, "return")); + cpus = qdict_get_qlist(resp, "return"); + g_assert(cpus); + while ((e = qlist_pop(cpus))) { + const QDict *cpu, *props; + int64_t socket, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "socket-id")); + socket = qdict_get_int(props, "socket-id"); + + if (socket == 0) { + g_assert_cmpint(node, ==, 1); + } else if (socket == 1) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert(false); + } + qobject_unref(e); + } + qobject_unref(resp); + + qtest_quit(qs); +} + int main(int argc, char **argv) { const char *args = NULL; @@ -278,6 +338,7 @@ int main(int argc, char **argv) if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) { qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu); + qtest_add_data_func("/numa/pc/dynamic/cpu", args, pc_dynamic_cpu_cfg); } if (!strcmp(arch, "ppc64")) { diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 2ee441cdb3..a49cbc6fde 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -392,13 +392,6 @@ static void add_query_tests(QmpSchema *schema) } } -static bool qmp_rsp_is_err(QDict *rsp) -{ - QDict *error = qdict_get_qdict(rsp, "error"); - qobject_unref(rsp); - return !!error; -} - static void test_qmp_preconfig(void) { QDict *rsp, *ret; From 0d8c41dae5b0566d0022be2af84d50ff456b3537 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:20 +0300 Subject: [PATCH 0844/2380] block: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- block/crypto.c | 2 +- block/nbd.c | 2 +- block/qcow.c | 2 +- block/qcow2-bitmap.c | 2 +- block/qcow2-cluster.c | 2 +- block/qcow2-refcount.c | 2 +- block/qcow2-snapshot.c | 2 +- block/qcow2.c | 4 ++-- block/vhdx-endian.c | 2 +- block/vhdx-log.c | 2 +- block/vhdx.c | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/block/crypto.c b/block/crypto.c index 7e7ad2d2a6..bc322b50f5 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -29,7 +29,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" #include "qemu/option.h" -#include "block/crypto.h" +#include "crypto.h" typedef struct BlockCrypto BlockCrypto; diff --git a/block/nbd.c b/block/nbd.c index 3e1693cc55..ff8333e3c1 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -27,7 +27,7 @@ */ #include "qemu/osdep.h" -#include "block/nbd-client.h" +#include "nbd-client.h" #include "qapi/error.h" #include "qemu/uri.h" #include "block/block_int.h" diff --git a/block/qcow.c b/block/qcow.c index 3ba2ca25ea..1f866af0d3 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -37,7 +37,7 @@ #include "qapi/qapi-visit-block-core.h" #include "crypto/block.h" #include "migration/blocker.h" -#include "block/crypto.h" +#include "crypto.h" /**************************************************************/ /* QEMU COW block driver with compression and encryption support */ diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 6e93ec43e1..60d5290f10 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -30,7 +30,7 @@ #include "qemu/cutils.h" #include "block/block_int.h" -#include "block/qcow2.h" +#include "qcow2.h" /* NOTICE: BME here means Bitmaps Extension and used as a namespace for * _internal_ constants. Please do not use this _internal_ abbreviation for diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 1aee726c6a..0d74584c9b 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -28,7 +28,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" -#include "block/qcow2.h" +#include "qcow2.h" #include "qemu/bswap.h" #include "trace.h" diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 2dc23005b7..403236256b 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" -#include "block/qcow2.h" +#include "qcow2.h" #include "qemu/range.h" #include "qemu/bswap.h" #include "qemu/cutils.h" diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 74293be470..bb6a5b7516 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "block/block_int.h" -#include "block/qcow2.h" +#include "qcow2.h" #include "qemu/bswap.h" #include "qemu/error-report.h" #include "qemu/cutils.h" diff --git a/block/qcow2.c b/block/qcow2.c index 6d532470a8..db13109744 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -27,7 +27,7 @@ #include "sysemu/block-backend.h" #include "qemu/module.h" #include -#include "block/qcow2.h" +#include "qcow2.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-events-block-core.h" @@ -39,7 +39,7 @@ #include "qemu/bswap.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" -#include "block/crypto.h" +#include "crypto.h" /* Differences with QCOW: diff --git a/block/vhdx-endian.c b/block/vhdx-endian.c index 429d7556bd..41fbdd2b8f 100644 --- a/block/vhdx-endian.c +++ b/block/vhdx-endian.c @@ -19,7 +19,7 @@ #include "qemu-common.h" #include "block/block_int.h" #include "qemu/bswap.h" -#include "block/vhdx.h" +#include "vhdx.h" /* * All the VHDX formats on disk are little endian - the following diff --git a/block/vhdx-log.c b/block/vhdx-log.c index 0ac4863b25..d2f1b98199 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -24,7 +24,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/bswap.h" -#include "block/vhdx.h" +#include "vhdx.h" typedef struct VHDXLogSequence { diff --git a/block/vhdx.c b/block/vhdx.c index 0b1e21c750..79c68a2061 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -23,7 +23,7 @@ #include "qemu/option.h" #include "qemu/crc32c.h" #include "qemu/bswap.h" -#include "block/vhdx.h" +#include "vhdx.h" #include "migration/blocker.h" #include "qemu/uuid.h" #include "qapi/qmp/qdict.h" From 2cfbf36ec07f7cac1aabb3b86f1c95c8a55424ba Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:51 +0100 Subject: [PATCH 0845/2380] target/arm: Honour FPCR.FZ in FRECPX The FRECPX instructions should (like most other floating point operations) honour the FPCR.FZ bit which specifies whether input denormals should be flushed to zero (or FZ16 for the half-precision version). We forgot to implement this, which doesn't affect the results (since the calculation doesn't actually care about the mantissa bits) but did mean we were failing to set the FPSR.IDC bit. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180521172712.19930-1-peter.maydell@linaro.org --- target/arm/helper-a64.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index f92bdea732..c4d2a04827 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -384,6 +384,8 @@ float16 HELPER(frecpx_f16)(float16 a, void *fpstp) return nan; } + a = float16_squash_input_denormal(a, fpst); + val16 = float16_val(a); sbit = 0x8000 & val16; exp = extract32(val16, 10, 5); @@ -413,6 +415,8 @@ float32 HELPER(frecpx_f32)(float32 a, void *fpstp) return nan; } + a = float32_squash_input_denormal(a, fpst); + val32 = float32_val(a); sbit = 0x80000000ULL & val32; exp = extract32(val32, 23, 8); @@ -442,6 +446,8 @@ float64 HELPER(frecpx_f64)(float64 a, void *fpstp) return nan; } + a = float64_squash_input_denormal(a, fpst); + val64 = float64_val(a); sbit = 0x8000000000000000ULL & val64; exp = extract64(float64_val(a), 52, 11); From 0d4a7551d9f14b8ad50adb412cbd9e88dbfa20d0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:51 +0100 Subject: [PATCH 0846/2380] MAINTAINERS: Add entries for newer MPS2 boards and devices Add entries to MAINTAINERS to cover the newer MPS2 boards and the new devices they use. Signed-off-by: Peter Maydell Message-id: 20180518153157.14899-1-peter.maydell@linaro.org --- MAINTAINERS | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index bf482fd4e9..41cd3736a9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -447,6 +447,8 @@ F: hw/timer/cmsdk-apb-timer.c F: include/hw/timer/cmsdk-apb-timer.h F: hw/char/cmsdk-apb-uart.c F: include/hw/char/cmsdk-apb-uart.h +F: hw/misc/tz-ppc.c +F: include/hw/misc/tz-ppc.h ARM cores M: Peter Maydell @@ -515,8 +517,11 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/arm/mps2.c -F: hw/misc/mps2-scc.c -F: include/hw/misc/mps2-scc.h +F: hw/arm/mps2-tz.c +F: hw/misc/mps2-*.c +F: include/hw/misc/mps2-*.h +F: hw/arm/iotkit.c +F: include/hw/arm/iotkit.h Musicpal M: Jan Kiszka From 887aae10f6150dfdc71c45d7588e8efe6c144019 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Thu, 31 May 2018 14:50:51 +0100 Subject: [PATCH 0847/2380] hw/intc/arm_gicv3: Fix APxR register dispatching There was a nasty flip in identifying which register group an access is targeting. The issue caused spuriously raised priorities of the guest when handing CPUs over in the Jailhouse hypervisor. Cc: qemu-stable@nongnu.org Signed-off-by: Jan Kiszka Message-id: 28b927d3-da58-bce4-cc13-bfec7f9b1cb9@siemens.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index cb9a3a542d..5c89be1af0 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -427,7 +427,7 @@ static uint64_t icv_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; uint64_t value = cs->ich_apr[grp][regno]; trace_gicv3_icv_ap_read(ri->crm & 1, regno, gicv3_redist_affid(cs), value); @@ -439,7 +439,7 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; trace_gicv3_icv_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); @@ -1461,7 +1461,7 @@ static uint64_t icc_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t value; int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1; + int grp = (ri->crm & 1) ? GICV3_G1 : GICV3_G0; if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { return icv_ap_read(env, ri); @@ -1483,7 +1483,7 @@ static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1; + int grp = (ri->crm & 1) ? GICV3_G1 : GICV3_G0; if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { icv_ap_write(env, ri, value); @@ -2292,7 +2292,7 @@ static uint64_t ich_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; uint64_t value; value = cs->ich_apr[grp][regno]; @@ -2305,7 +2305,7 @@ static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); From 34ffacae085914fce54590ea84bae9c6ad95e2a4 Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Thu, 31 May 2018 14:50:51 +0100 Subject: [PATCH 0848/2380] arm_gicv3_kvm: increase clroffset accordingly It forgot to increase clroffset during the loop. So it only clear the first 4 bytes. Fixes: 367b9f527becdd20ddf116e17a3c0c2bbc486920 Cc: qemu-stable@nongnu.org Signed-off-by: Shannon Zhao Reviewed-by: Eric Auger Message-id: 1527047633-12368-1-git-send-email-zhaoshenglong@huawei.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index ec371772b3..3536795501 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -243,6 +243,7 @@ static void kvm_dist_putbmp(GICv3State *s, uint32_t offset, if (clroffset != 0) { reg = 0; kvm_gicd_access(s, clroffset, ®, true); + clroffset += 4; } reg = *gic_bmp_ptr32(bmp, irq); kvm_gicd_access(s, offset, ®, true); From 6c2be133a7478e443c99757b833d0f265c48e0a6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 31 May 2018 14:50:51 +0100 Subject: [PATCH 0849/2380] tcg: Fix helper function vs host abi for float16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Depending on the host abi, float16, aka uint16_t, values are passed and returned either zero-extended in the host register or with garbage at the top of the host register. The tcg code generator has so far been assuming garbage, as that matches the x86 abi, but this is incorrect for other host abis. Further, target/arm has so far been assuming zero-extended results, so that it may store the 16-bit value into a 32-bit slot with the high 16-bits already clear. Rectify both problems by mapping "f16" in the helper definition to uint32_t instead of (a typedef for) uint16_t. This forces the host compiler to assume garbage in the upper 16 bits on input and to zero-extend the result on output. Cc: qemu-stable@nongnu.org Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Laurent Desnogues Message-id: 20180522175629.24932-1-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- include/exec/helper-head.h | 2 +- target/arm/helper-a64.c | 35 +++++++++-------- target/arm/helper.c | 80 +++++++++++++++++++------------------- 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index 15b6a68de3..276dd5afce 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -39,7 +39,7 @@ #define dh_ctype_int int #define dh_ctype_i64 uint64_t #define dh_ctype_s64 int64_t -#define dh_ctype_f16 float16 +#define dh_ctype_f16 uint32_t #define dh_ctype_f32 float32 #define dh_ctype_f64 float64 #define dh_ctype_ptr void * diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index c4d2a04827..7f6ad3000b 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -85,12 +85,12 @@ static inline uint32_t float_rel_to_flags(int res) return flags; } -uint64_t HELPER(vfp_cmph_a64)(float16 x, float16 y, void *fp_status) +uint64_t HELPER(vfp_cmph_a64)(uint32_t x, uint32_t y, void *fp_status) { return float_rel_to_flags(float16_compare_quiet(x, y, fp_status)); } -uint64_t HELPER(vfp_cmpeh_a64)(float16 x, float16 y, void *fp_status) +uint64_t HELPER(vfp_cmpeh_a64)(uint32_t x, uint32_t y, void *fp_status) { return float_rel_to_flags(float16_compare(x, y, fp_status)); } @@ -214,7 +214,7 @@ uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, void *fpstp) #define float64_three make_float64(0x4008000000000000ULL) #define float64_one_point_five make_float64(0x3FF8000000000000ULL) -float16 HELPER(recpsf_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(recpsf_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; @@ -259,7 +259,7 @@ float64 HELPER(recpsf_f64)(float64 a, float64 b, void *fpstp) return float64_muladd(a, b, float64_two, 0, fpst); } -float16 HELPER(rsqrtsf_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(rsqrtsf_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; @@ -366,7 +366,7 @@ uint64_t HELPER(neon_addlp_u16)(uint64_t a) } /* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ -float16 HELPER(frecpx_f16)(float16 a, void *fpstp) +uint32_t HELPER(frecpx_f16)(uint32_t a, void *fpstp) { float_status *fpst = fpstp; uint16_t val16, sbit; @@ -701,7 +701,7 @@ void HELPER(casp_be_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, #define ADVSIMD_HELPER(name, suffix) HELPER(glue(glue(advsimd_, name), suffix)) #define ADVSIMD_HALFOP(name) \ -float16 ADVSIMD_HELPER(name, h)(float16 a, float16 b, void *fpstp) \ +uint32_t ADVSIMD_HELPER(name, h)(uint32_t a, uint32_t b, void *fpstp) \ { \ float_status *fpst = fpstp; \ return float16_ ## name(a, b, fpst); \ @@ -761,7 +761,8 @@ ADVSIMD_HALFOP(mulx) ADVSIMD_TWOHALFOP(mulx) /* fused multiply-accumulate */ -float16 HELPER(advsimd_muladdh)(float16 a, float16 b, float16 c, void *fpstp) +uint32_t HELPER(advsimd_muladdh)(uint32_t a, uint32_t b, uint32_t c, + void *fpstp) { float_status *fpst = fpstp; return float16_muladd(a, b, c, 0, fpst); @@ -792,14 +793,14 @@ uint32_t HELPER(advsimd_muladd2h)(uint32_t two_a, uint32_t two_b, #define ADVSIMD_CMPRES(test) (test) ? 0xffff : 0 -uint32_t HELPER(advsimd_ceq_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_ceq_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; int compare = float16_compare_quiet(a, b, fpst); return ADVSIMD_CMPRES(compare == float_relation_equal); } -uint32_t HELPER(advsimd_cge_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_cge_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; int compare = float16_compare(a, b, fpst); @@ -807,14 +808,14 @@ uint32_t HELPER(advsimd_cge_f16)(float16 a, float16 b, void *fpstp) compare == float_relation_equal); } -uint32_t HELPER(advsimd_cgt_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_cgt_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; int compare = float16_compare(a, b, fpst); return ADVSIMD_CMPRES(compare == float_relation_greater); } -uint32_t HELPER(advsimd_acge_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_acge_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; float16 f0 = float16_abs(a); @@ -824,7 +825,7 @@ uint32_t HELPER(advsimd_acge_f16)(float16 a, float16 b, void *fpstp) compare == float_relation_equal); } -uint32_t HELPER(advsimd_acgt_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; float16 f0 = float16_abs(a); @@ -834,12 +835,12 @@ uint32_t HELPER(advsimd_acgt_f16)(float16 a, float16 b, void *fpstp) } /* round to integral */ -float16 HELPER(advsimd_rinth_exact)(float16 x, void *fp_status) +uint32_t HELPER(advsimd_rinth_exact)(uint32_t x, void *fp_status) { return float16_round_to_int(x, fp_status); } -float16 HELPER(advsimd_rinth)(float16 x, void *fp_status) +uint32_t HELPER(advsimd_rinth)(uint32_t x, void *fp_status) { int old_flags = get_float_exception_flags(fp_status), new_flags; float16 ret; @@ -863,7 +864,7 @@ float16 HELPER(advsimd_rinth)(float16 x, void *fp_status) * setting the mode appropriately before calling the helper. */ -uint32_t HELPER(advsimd_f16tosinth)(float16 a, void *fpstp) +uint32_t HELPER(advsimd_f16tosinth)(uint32_t a, void *fpstp) { float_status *fpst = fpstp; @@ -875,7 +876,7 @@ uint32_t HELPER(advsimd_f16tosinth)(float16 a, void *fpstp) return float16_to_int16(a, fpst); } -uint32_t HELPER(advsimd_f16touinth)(float16 a, void *fpstp) +uint32_t HELPER(advsimd_f16touinth)(uint32_t a, void *fpstp) { float_status *fpst = fpstp; @@ -891,7 +892,7 @@ uint32_t HELPER(advsimd_f16touinth)(float16 a, void *fpstp) * Square Root and Reciprocal square root */ -float16 HELPER(sqrt_f16)(float16 a, void *fpstp) +uint32_t HELPER(sqrt_f16)(uint32_t a, void *fpstp) { float_status *s = fpstp; diff --git a/target/arm/helper.c b/target/arm/helper.c index c0f739972e..a4bfac3932 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11344,35 +11344,35 @@ DO_VFP_cmp(d, float64) /* Integer to float and float to integer conversions */ -#define CONV_ITOF(name, fsz, sign) \ - float##fsz HELPER(name)(uint32_t x, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ +#define CONV_ITOF(name, ftype, fsz, sign) \ +ftype HELPER(name)(uint32_t x, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ } -#define CONV_FTOI(name, fsz, sign, round) \ -uint32_t HELPER(name)(float##fsz x, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - if (float##fsz##_is_any_nan(x)) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - return float##fsz##_to_##sign##int32##round(x, fpst); \ +#define CONV_FTOI(name, ftype, fsz, sign, round) \ +uint32_t HELPER(name)(ftype x, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + if (float##fsz##_is_any_nan(x)) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##sign##int32##round(x, fpst); \ } -#define FLOAT_CONVS(name, p, fsz, sign) \ -CONV_ITOF(vfp_##name##to##p, fsz, sign) \ -CONV_FTOI(vfp_to##name##p, fsz, sign, ) \ -CONV_FTOI(vfp_to##name##z##p, fsz, sign, _round_to_zero) +#define FLOAT_CONVS(name, p, ftype, fsz, sign) \ + CONV_ITOF(vfp_##name##to##p, ftype, fsz, sign) \ + CONV_FTOI(vfp_to##name##p, ftype, fsz, sign, ) \ + CONV_FTOI(vfp_to##name##z##p, ftype, fsz, sign, _round_to_zero) -FLOAT_CONVS(si, h, 16, ) -FLOAT_CONVS(si, s, 32, ) -FLOAT_CONVS(si, d, 64, ) -FLOAT_CONVS(ui, h, 16, u) -FLOAT_CONVS(ui, s, 32, u) -FLOAT_CONVS(ui, d, 64, u) +FLOAT_CONVS(si, h, uint32_t, 16, ) +FLOAT_CONVS(si, s, float32, 32, ) +FLOAT_CONVS(si, d, float64, 64, ) +FLOAT_CONVS(ui, h, uint32_t, 16, u) +FLOAT_CONVS(ui, s, float32, 32, u) +FLOAT_CONVS(ui, d, float64, 64, u) #undef CONV_ITOF #undef CONV_FTOI @@ -11465,22 +11465,22 @@ static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst) return float64_to_float16(float64_scalbn(f, -shift, fpst), true, fpst); } -float16 HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst) { return do_postscale_fp16(int32_to_float64(x, fpst), shift, fpst); } -float16 HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) { return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst); } -float16 HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst) { return do_postscale_fp16(int64_to_float64(x, fpst), shift, fpst); } -float16 HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst) { return do_postscale_fp16(uint64_to_float64(x, fpst), shift, fpst); } @@ -11504,32 +11504,32 @@ static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst) } } -uint32_t HELPER(vfp_toshh)(float16 x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_toshh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_int16(do_prescale_fp16(x, shift, fpst), fpst); } -uint32_t HELPER(vfp_touhh)(float16 x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_touhh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst); } -uint32_t HELPER(vfp_toslh)(float16 x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_toslh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_int32(do_prescale_fp16(x, shift, fpst), fpst); } -uint32_t HELPER(vfp_toulh)(float16 x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_toulh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_uint32(do_prescale_fp16(x, shift, fpst), fpst); } -uint64_t HELPER(vfp_tosqh)(float16 x, uint32_t shift, void *fpst) +uint64_t HELPER(vfp_tosqh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_int64(do_prescale_fp16(x, shift, fpst), fpst); } -uint64_t HELPER(vfp_touqh)(float16 x, uint32_t shift, void *fpst) +uint64_t HELPER(vfp_touqh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_uint64(do_prescale_fp16(x, shift, fpst), fpst); } @@ -11565,7 +11565,7 @@ uint32_t HELPER(set_neon_rmode)(uint32_t rmode, CPUARMState *env) } /* Half precision conversions. */ -float32 HELPER(vfp_fcvt_f16_to_f32)(float16 a, void *fpstp, uint32_t ahp_mode) +float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, void *fpstp, uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing input denormals. @@ -11578,7 +11578,7 @@ float32 HELPER(vfp_fcvt_f16_to_f32)(float16 a, void *fpstp, uint32_t ahp_mode) return r; } -float16 HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) +uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing output denormals. @@ -11591,7 +11591,7 @@ float16 HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) return r; } -float64 HELPER(vfp_fcvt_f16_to_f64)(float16 a, void *fpstp, uint32_t ahp_mode) +float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, void *fpstp, uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing input denormals. @@ -11604,7 +11604,7 @@ float64 HELPER(vfp_fcvt_f16_to_f64)(float16 a, void *fpstp, uint32_t ahp_mode) return r; } -float16 HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) +uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing output denormals. @@ -11742,7 +11742,7 @@ static bool round_to_inf(float_status *fpst, bool sign_bit) g_assert_not_reached(); } -float16 HELPER(recpe_f16)(float16 input, void *fpstp) +uint32_t HELPER(recpe_f16)(uint32_t input, void *fpstp) { float_status *fpst = fpstp; float16 f16 = float16_squash_input_denormal(input, fpst); @@ -11937,7 +11937,7 @@ static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac) return extract64(estimate, 0, 8) << 44; } -float16 HELPER(rsqrte_f16)(float16 input, void *fpstp) +uint32_t HELPER(rsqrte_f16)(uint32_t input, void *fpstp) { float_status *s = fpstp; float16 f16 = float16_squash_input_denormal(input, s); From 60b8fe49cca0cca360adcf9e58fbe5d1612df36d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 31 May 2018 14:50:51 +0100 Subject: [PATCH 0850/2380] arm: fix qemu crash on startup with -bios option When QEMU is started with following CLI -machine virt,gic-version=3,accel=kvm -cpu host -bios AAVMF_CODE.fd it crashes with abort at accel/kvm/kvm-all.c:2164: KVM_SET_DEVICE_ATTR failed: Group 6 attr 0x000000000000c665: Invalid argument Which is caused by implicit dependency of kvm_arm_gicv3_reset() on arm_gicv3_icc_reset() where the later is called by CPU reset reset callback. However commit: 3b77f6c arm/boot: split load_dtb() from arm_load_kernel() broke CPU reset callback registration in case arm_load_kernel() ... if (!info->kernel_filename || info->firmware_loaded) branch is taken, i.e. it's sufficient to provide a firmware or do not provide kernel on CLI to skip cpu reset callback registration, where before offending commit the callback has been registered unconditionally. Fix it by registering the callback right at the beginning of arm_load_kernel() unconditionally instead of doing it at the end. NOTE: we probably should eliminate that dependency anyways as well as separate arch CPU reset parts from arm_load_kernel() into CPU itself, but that refactoring that I probably would have to do anyways later for CPU hotplug to work. Reported-by: Auger Eric Signed-off-by: Igor Mammedov Reviewed-by: Eric Auger Tested-by: Eric Auger Message-id: 1527070950-208350-1-git-send-email-imammedo@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/boot.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 9496f331a8..1e481662ad 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -926,6 +926,15 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) static const ARMInsnFixup *primary_loader; AddressSpace *as = arm_boot_address_space(cpu, info); + /* 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 = first_cpu; cs; cs = CPU_NEXT(cs)) { + qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); + } + /* The board code is not supposed to set secure_board_setup unless * running its code in secure mode is actually possible, and KVM * doesn't support secure. @@ -1143,15 +1152,6 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) ARM_CPU(cs)->env.boot_info = info; } - /* 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 = first_cpu; cs; cs = CPU_NEXT(cs)) { - qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); - } - if (!info->skip_dtb_autoload && have_dtb(info)) { if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as) < 0) { exit(1); From 7e3ef27c7c1ccbac589ad0a67c40b5df57e2af71 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0851/2380] arm: fix malloc type mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpregs_keys is an uint32_t* so the allocation should use uint32_t. g_new is even better because it is type-safe. Signed-off-by: Paolo Bonzini Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/gdbstub.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index e80cfb47c7..0c64c0292e 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -157,8 +157,7 @@ int arm_gen_dynamic_xml(CPUState *cs) RegisterSysregXmlParam param = {cs, s}; cpu->dyn_xml.num_cpregs = 0; - cpu->dyn_xml.cpregs_keys = g_malloc(sizeof(uint32_t *) * - g_hash_table_size(cpu->cp_regs)); + cpu->dyn_xml.cpregs_keys = g_new(uint32_t, g_hash_table_size(cpu->cp_regs)); g_string_printf(s, ""); g_string_append_printf(s, ""); g_string_append_printf(s, ""); From a13b6d8eeca5cb786e934ad73f8b3b7e01c20121 Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0852/2380] xlnx-zdma: Correct mem leaks and memset to zero on desc unaligned errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity found that the string return by 'object_get_canonical_path' was not being freed at two locations in the model (CID 1391294 and CID 1391293) and also that a memset was being called with a value greater than the max of a byte on the second argument (CID 1391286). This patch corrects this by adding the freeing of the strings and also changing to memset to zero instead on descriptor unaligned errors. Signed-off-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180528184859.3530-1-frasse.iglesias@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/dma/xlnx-zdma.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 14d86c254b..8eea757aff 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -302,7 +302,7 @@ static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf) qemu_log_mask(LOG_GUEST_ERROR, "zdma: unaligned descriptor at %" PRIx64, addr); - memset(buf, 0xdeadbeef, sizeof(XlnxZDMADescr)); + memset(buf, 0x0, sizeof(XlnxZDMADescr)); s->error = true; return false; } @@ -707,9 +707,11 @@ static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size) RegisterInfo *r = &s->regs_info[addr / 4]; if (!r->data) { + gchar *path = object_get_canonical_path(OBJECT(s)); qemu_log("%s: Decode error: read from %" HWADDR_PRIx "\n", - object_get_canonical_path(OBJECT(s)), + path, addr); + g_free(path); ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); zdma_ch_imr_update_irq(s); return 0; @@ -724,9 +726,11 @@ static void zdma_write(void *opaque, hwaddr addr, uint64_t value, RegisterInfo *r = &s->regs_info[addr / 4]; if (!r->data) { + gchar *path = object_get_canonical_path(OBJECT(s)); qemu_log("%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64 "\n", - object_get_canonical_path(OBJECT(s)), + path, addr, value); + g_free(path); ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); zdma_ch_imr_update_irq(s); return; From 5deac39cd94e11d72b6ca8663fbf369691a33dc8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0853/2380] Correct CPACR reset value for v7 cores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit f0aff255700 we made cpacr_write() enforce that some CPACR bits are RAZ/WI and some are RAO/WI for ARMv7 cores. Unfortunately we forgot to also update the register's reset value. The effect was that (a) a guest that read CPACR on reset would not see ones in the RAO bits, and (b) if you did a migration before the guest did a write to the CPACR then the migration would fail because the destination would enforce the RAO bits and then complain that they didn't match the zero value from the source. Implement reset for the CPACR using a custom reset function that just calls cpacr_write(), to avoid having to duplicate the logic for which bits are RAO. This bug would affect migration for TCG CPUs which are ARMv7 with VFP but without one of Neon or VFPv3. Reported-by: Cédric Le Goater Signed-off-by: Peter Maydell Tested-by: Cédric Le Goater Message-id: 20180522173713.26282-1-peter.maydell@linaro.org --- target/arm/helper.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index a4bfac3932..f75aa6e9ca 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -863,6 +863,14 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.cpacr_el1 = value; } +static void cpacr_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* Call cpacr_write() so that we reset with the correct RAO bits set + * for our CPU features. + */ + cpacr_write(env, ri, 0); +} + static CPAccessResult cpacr_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -920,7 +928,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { { .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1), - .resetvalue = 0, .writefn = cpacr_write }, + .resetfn = cpacr_reset, .writefn = cpacr_write }, REGINFO_SENTINEL }; From 2ce931d01290d450a9c9b5fa00d1347535230b4c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0854/2380] memory.h: Improve IOMMU related documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add more detail to the documentation for memory_region_init_iommu() and other IOMMU-related functions and data structures. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Eric Auger Message-id: 20180521140402.23318-2-peter.maydell@linaro.org --- include/exec/memory.h | 105 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 10 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 525619a5f4..267aa5fca4 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -194,29 +194,100 @@ enum IOMMUMemoryRegionAttr { IOMMU_ATTR_SPAPR_TCE_FD }; +/** + * IOMMUMemoryRegionClass: + * + * All IOMMU implementations need to subclass TYPE_IOMMU_MEMORY_REGION + * and provide an implementation of at least the @translate method here + * to handle requests to the memory region. Other methods are optional. + * + * The IOMMU implementation must use the IOMMU notifier infrastructure + * to report whenever mappings are changed, by calling + * memory_region_notify_iommu() (or, if necessary, by calling + * memory_region_notify_one() for each registered notifier). + */ typedef struct IOMMUMemoryRegionClass { /* private */ struct DeviceClass parent_class; /* - * Return a TLB entry that contains a given address. Flag should - * be the access permission of this translation operation. We can - * set flag to IOMMU_NONE to mean that we don't need any - * read/write permission checks, like, when for region replay. + * Return a TLB entry that contains a given address. + * + * The IOMMUAccessFlags indicated via @flag are optional and may + * be specified as IOMMU_NONE to indicate that the caller needs + * the full translation information for both reads and writes. If + * the access flags are specified then the IOMMU implementation + * may use this as an optimization, to stop doing a page table + * walk as soon as it knows that the requested permissions are not + * allowed. If IOMMU_NONE is passed then the IOMMU must do the + * full page table walk and report the permissions in the returned + * IOMMUTLBEntry. (Note that this implies that an IOMMU may not + * return different mappings for reads and writes.) + * + * The returned information remains valid while the caller is + * holding the big QEMU lock or is inside an RCU critical section; + * if the caller wishes to cache the mapping beyond that it must + * register an IOMMU notifier so it can invalidate its cached + * information when the IOMMU mapping changes. + * + * @iommu: the IOMMUMemoryRegion + * @hwaddr: address to be translated within the memory region + * @flag: requested access permissions */ IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag); - /* Returns minimum supported page size */ + /* Returns minimum supported page size in bytes. + * If this method is not provided then the minimum is assumed to + * be TARGET_PAGE_SIZE. + * + * @iommu: the IOMMUMemoryRegion + */ uint64_t (*get_min_page_size)(IOMMUMemoryRegion *iommu); - /* Called when IOMMU Notifier flag changed */ + /* Called when IOMMU Notifier flag changes (ie when the set of + * events which IOMMU users are requesting notification for changes). + * Optional method -- need not be provided if the IOMMU does not + * need to know exactly which events must be notified. + * + * @iommu: the IOMMUMemoryRegion + * @old_flags: events which previously needed to be notified + * @new_flags: events which now need to be notified + */ void (*notify_flag_changed)(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old_flags, IOMMUNotifierFlag new_flags); - /* Set this up to provide customized IOMMU replay function */ + /* Called to handle memory_region_iommu_replay(). + * + * The default implementation of memory_region_iommu_replay() is to + * call the IOMMU translate method for every page in the address space + * with flag == IOMMU_NONE and then call the notifier if translate + * returns a valid mapping. If this method is implemented then it + * overrides the default behaviour, and must provide the full semantics + * of memory_region_iommu_replay(), by calling @notifier for every + * translation present in the IOMMU. + * + * Optional method -- an IOMMU only needs to provide this method + * if the default is inefficient or produces undesirable side effects. + * + * Note: this is not related to record-and-replay functionality. + */ void (*replay)(IOMMUMemoryRegion *iommu, IOMMUNotifier *notifier); - /* Get IOMMU misc attributes */ - int (*get_attr)(IOMMUMemoryRegion *iommu, enum IOMMUMemoryRegionAttr, + /* Get IOMMU misc attributes. This is an optional method that + * can be used to allow users of the IOMMU to get implementation-specific + * information. The IOMMU implements this method to handle calls + * by IOMMU users to memory_region_iommu_get_attr() by filling in + * the arbitrary data pointer for any IOMMUMemoryRegionAttr values that + * the IOMMU supports. If the method is unimplemented then + * memory_region_iommu_get_attr() will always return -EINVAL. + * + * @iommu: the IOMMUMemoryRegion + * @attr: attribute being queried + * @data: memory to fill in with the attribute data + * + * Returns 0 on success, or a negative errno; in particular + * returns -EINVAL for unrecognized or unimplemented attribute types. + */ + int (*get_attr)(IOMMUMemoryRegion *iommu, enum IOMMUMemoryRegionAttr attr, void *data); } IOMMUMemoryRegionClass; @@ -705,6 +776,14 @@ static inline void memory_region_init_reservation(MemoryRegion *mr, * An IOMMU region translates addresses and forwards accesses to a target * memory region. * + * The IOMMU implementation must define a subclass of TYPE_IOMMU_MEMORY_REGION. + * @_iommu_mr should be a pointer to enough memory for an instance of + * that subclass, @instance_size is the size of that subclass, and + * @mrtypename is its name. This function will initialize @_iommu_mr as an + * instance of the subclass, and its methods will then be called to handle + * accesses to the memory region. See the documentation of + * #IOMMUMemoryRegionClass for further details. + * * @_iommu_mr: the #IOMMUMemoryRegion to be initialized * @instance_size: the IOMMUMemoryRegion subclass instance size * @mrtypename: the type name of the #IOMMUMemoryRegion @@ -953,6 +1032,8 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, * a notifier with the minimum page granularity returned by * mr->iommu_ops->get_page_size(). * + * Note: this is not related to record-and-replay functionality. + * * @iommu_mr: the memory region to observe * @n: the notifier to which to replay iommu mappings */ @@ -962,6 +1043,8 @@ void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n); * memory_region_iommu_replay_all: replay existing IOMMU translations * to all the notifiers registered. * + * Note: this is not related to record-and-replay functionality. + * * @iommu_mr: the memory region to observe */ void memory_region_iommu_replay_all(IOMMUMemoryRegion *iommu_mr); @@ -981,7 +1064,9 @@ void memory_region_unregister_iommu_notifier(MemoryRegion *mr, * memory_region_iommu_get_attr: return an IOMMU attr if get_attr() is * defined on the IOMMU. * - * Returns 0 if succeded, error code otherwise. + * Returns 0 on success, or a negative errno otherwise. In particular, + * -EINVAL indicates that the IOMMU does not support the requested + * attribute. * * @iommu_mr: the memory region * @attr: the requested attribute From c874dc4f5e8ffae46ddaf2a0f223269f23f3a00d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0855/2380] Make tb_invalidate_phys_addr() take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to tb_invalidate_phys_addr(). Its callers either have an attrs value to hand, or don't care and can use MEMTXATTRS_UNSPECIFIED. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Message-id: 20180521140402.23318-3-peter.maydell@linaro.org --- accel/tcg/translate-all.c | 2 +- exec.c | 2 +- include/exec/exec-all.h | 5 +++-- target/xtensa/op_helper.c | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 732c919629..18ce24a94d 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1669,7 +1669,7 @@ static TranslationBlock *tb_find_pc(uintptr_t tc_ptr) } #if !defined(CONFIG_USER_ONLY) -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr) +void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) { ram_addr_t ram_addr; MemoryRegion *mr; diff --git a/exec.c b/exec.c index ffa1099547..c3a197e67b 100644 --- a/exec.c +++ b/exec.c @@ -898,7 +898,7 @@ static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) if (phys != -1) { /* Locks grabbed by tb_invalidate_phys_addr */ tb_invalidate_phys_addr(cpu->cpu_ases[asidx].as, - phys | (pc & ~TARGET_PAGE_MASK)); + phys | (pc & ~TARGET_PAGE_MASK), attrs); } } #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index bd68328ed9..4d09eaba72 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -255,7 +255,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, void tlb_set_page(CPUState *cpu, target_ulong vaddr, hwaddr paddr, int prot, int mmu_idx, target_ulong size); -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr); +void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs); void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, uintptr_t retaddr); #else @@ -303,7 +303,8 @@ static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap) { } -static inline void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr) +static inline void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs) { } #endif diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index e3bcbe10d6..8a8c763c63 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -105,7 +105,8 @@ static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) int ret = xtensa_get_physical_addr(env, false, vaddr, 2, 0, &paddr, &page_size, &access); if (ret == 0) { - tb_invalidate_phys_addr(&address_space_memory, paddr); + tb_invalidate_phys_addr(&address_space_memory, paddr, + MEMTXATTRS_UNSPECIFIED); } } From bc6b1cec84618bf977a451eac30ed1ee4c735963 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0856/2380] Make address_space_translate{, _cached}() take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to address_space_translate() and address_space_translate_cached(). Callers either have an attrs value to hand, or don't care and can use MEMTXATTRS_UNSPECIFIED. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-4-peter.maydell@linaro.org --- accel/tcg/translate-all.c | 2 +- exec.c | 14 +++++++++----- hw/vfio/common.c | 3 ++- include/exec/memory.h | 4 +++- memory_ldst.inc.c | 18 +++++++++--------- target/arm/kvm.c | 3 ++- target/riscv/helper.c | 2 +- 7 files changed, 27 insertions(+), 19 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 18ce24a94d..d48b56ca38 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1676,7 +1676,7 @@ void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) hwaddr l = 1; rcu_read_lock(); - mr = address_space_translate(as, addr, &addr, &l, false); + mr = address_space_translate(as, addr, &addr, &l, false, attrs); if (!(memory_region_is_ram(mr) || memory_region_is_romd(mr))) { rcu_read_unlock(); diff --git a/exec.c b/exec.c index c3a197e67b..d314c7cc39 100644 --- a/exec.c +++ b/exec.c @@ -3322,7 +3322,8 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as, rcu_read_lock(); while (len > 0) { l = len; - mr = address_space_translate(as, addr, &addr1, &l, true); + mr = address_space_translate(as, addr, &addr1, &l, true, + MEMTXATTRS_UNSPECIFIED); if (!(memory_region_is_ram(mr) || memory_region_is_romd(mr))) { @@ -3699,7 +3700,7 @@ void address_space_cache_destroy(MemoryRegionCache *cache) */ static inline MemoryRegion *address_space_translate_cached( MemoryRegionCache *cache, hwaddr addr, hwaddr *xlat, - hwaddr *plen, bool is_write) + hwaddr *plen, bool is_write, MemTxAttrs attrs) { MemoryRegionSection section; MemoryRegion *mr; @@ -3733,7 +3734,8 @@ address_space_read_cached_slow(MemoryRegionCache *cache, hwaddr addr, MemoryRegion *mr; l = len; - mr = address_space_translate_cached(cache, addr, &addr1, &l, false); + mr = address_space_translate_cached(cache, addr, &addr1, &l, false, + MEMTXATTRS_UNSPECIFIED); flatview_read_continue(cache->fv, addr, MEMTXATTRS_UNSPECIFIED, buf, len, addr1, l, mr); @@ -3750,7 +3752,8 @@ address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, MemoryRegion *mr; l = len; - mr = address_space_translate_cached(cache, addr, &addr1, &l, true); + mr = address_space_translate_cached(cache, addr, &addr1, &l, true, + MEMTXATTRS_UNSPECIFIED); flatview_write_continue(cache->fv, addr, MEMTXATTRS_UNSPECIFIED, buf, len, addr1, l, mr); @@ -3848,7 +3851,8 @@ bool cpu_physical_memory_is_io(hwaddr phys_addr) rcu_read_lock(); mr = address_space_translate(&address_space_memory, - phys_addr, &phys_addr, &l, false); + phys_addr, &phys_addr, &l, false, + MEMTXATTRS_UNSPECIFIED); res = !(memory_region_is_ram(mr) || memory_region_is_romd(mr)); rcu_read_unlock(); diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 07ffa0ba10..8e57265edf 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -324,7 +324,8 @@ static bool vfio_get_vaddr(IOMMUTLBEntry *iotlb, void **vaddr, */ mr = address_space_translate(&address_space_memory, iotlb->translated_addr, - &xlat, &len, writable); + &xlat, &len, writable, + MEMTXATTRS_UNSPECIFIED); if (!memory_region_is_ram(mr)) { error_report("iommu map to non memory area %"HWADDR_PRIx"", xlat); diff --git a/include/exec/memory.h b/include/exec/memory.h index 267aa5fca4..b95ceb272b 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1908,6 +1908,7 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, * #MemoryRegion. * @len: pointer to length * @is_write: indicates the transfer direction + * @attrs: memory attributes */ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, @@ -1915,7 +1916,8 @@ MemoryRegion *flatview_translate(FlatView *fv, static inline MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, hwaddr *xlat, - hwaddr *len, bool is_write) + hwaddr *len, bool is_write, + MemTxAttrs attrs) { return flatview_translate(address_space_to_flatview(as), addr, xlat, len, is_write); diff --git a/memory_ldst.inc.c b/memory_ldst.inc.c index 25d6125747..15483987fe 100644 --- a/memory_ldst.inc.c +++ b/memory_ldst.inc.c @@ -33,7 +33,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); + mr = TRANSLATE(addr, &addr1, &l, false, attrs); if (l < 4 || !IS_DIRECT(mr, false)) { release_lock |= prepare_mmio_access(mr); @@ -109,7 +109,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); + mr = TRANSLATE(addr, &addr1, &l, false, attrs); if (l < 8 || !IS_DIRECT(mr, false)) { release_lock |= prepare_mmio_access(mr); @@ -183,7 +183,7 @@ uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); + mr = TRANSLATE(addr, &addr1, &l, false, attrs); if (!IS_DIRECT(mr, false)) { release_lock |= prepare_mmio_access(mr); @@ -219,7 +219,7 @@ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); + mr = TRANSLATE(addr, &addr1, &l, false, attrs); if (l < 2 || !IS_DIRECT(mr, false)) { release_lock |= prepare_mmio_access(mr); @@ -296,7 +296,7 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); + mr = TRANSLATE(addr, &addr1, &l, true, attrs); if (l < 4 || !IS_DIRECT(mr, true)) { release_lock |= prepare_mmio_access(mr); @@ -333,7 +333,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); + mr = TRANSLATE(addr, &addr1, &l, true, attrs); if (l < 4 || !IS_DIRECT(mr, true)) { release_lock |= prepare_mmio_access(mr); @@ -405,7 +405,7 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); + mr = TRANSLATE(addr, &addr1, &l, true, attrs); if (!IS_DIRECT(mr, true)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, 1, attrs); @@ -438,7 +438,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); + mr = TRANSLATE(addr, &addr1, &l, true, attrs); if (l < 2 || !IS_DIRECT(mr, true)) { release_lock |= prepare_mmio_access(mr); @@ -511,7 +511,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); + mr = TRANSLATE(addr, &addr1, &l, true, attrs); if (l < 8 || !IS_DIRECT(mr, true)) { release_lock |= prepare_mmio_access(mr); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 5141d0adc5..98f5006323 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -664,7 +664,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, /* MSI doorbell address is translated by an IOMMU */ rcu_read_lock(); - mr = address_space_translate(as, address, &xlat, &len, true); + mr = address_space_translate(as, address, &xlat, &len, true, + MEMTXATTRS_UNSPECIFIED); if (!mr) { goto unlock; } diff --git a/target/riscv/helper.c b/target/riscv/helper.c index 95889f23b9..29e1a603dc 100644 --- a/target/riscv/helper.c +++ b/target/riscv/helper.c @@ -210,7 +210,7 @@ restart: MemoryRegion *mr; hwaddr l = sizeof(target_ulong), addr1; mr = address_space_translate(cs->as, pte_addr, - &addr1, &l, false); + &addr1, &l, false, MEMTXATTRS_UNSPECIFIED); if (memory_access_is_direct(mr, true)) { target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); From f26404fbeef33ac8798d2541839f7873bbae91fd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0857/2380] Make address_space_map() take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to address_space_map(). Its callers either have an attrs value to hand, or don't care and can use MEMTXATTRS_UNSPECIFIED. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-5-peter.maydell@linaro.org --- exec.c | 6 ++++-- include/exec/memory.h | 3 ++- include/sysemu/dma.h | 3 ++- target/ppc/mmu-hash64.c | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/exec.c b/exec.c index d314c7cc39..1dc81cfe4a 100644 --- a/exec.c +++ b/exec.c @@ -3529,7 +3529,8 @@ flatview_extend_translation(FlatView *fv, hwaddr addr, void *address_space_map(AddressSpace *as, hwaddr addr, hwaddr *plen, - bool is_write) + bool is_write, + MemTxAttrs attrs) { hwaddr len = *plen; hwaddr l, xlat; @@ -3616,7 +3617,8 @@ void *cpu_physical_memory_map(hwaddr addr, hwaddr *plen, int is_write) { - return address_space_map(&address_space_memory, addr, plen, is_write); + return address_space_map(&address_space_memory, addr, plen, is_write, + MEMTXATTRS_UNSPECIFIED); } void cpu_physical_memory_unmap(void *buffer, hwaddr len, diff --git a/include/exec/memory.h b/include/exec/memory.h index b95ceb272b..d594639d7c 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1952,9 +1952,10 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, bool is_ * @addr: address within that address space * @plen: pointer to length of buffer; updated on return * @is_write: indicates the transfer direction + * @attrs: memory attributes */ void *address_space_map(AddressSpace *as, hwaddr addr, - hwaddr *plen, bool is_write); + hwaddr *plen, bool is_write, MemTxAttrs attrs); /* address_space_unmap: Unmaps a memory region previously mapped by address_space_map() * diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h index c228c66513..0d73902634 100644 --- a/include/sysemu/dma.h +++ b/include/sysemu/dma.h @@ -132,7 +132,8 @@ static inline void *dma_memory_map(AddressSpace *as, hwaddr xlen = *len; void *p; - p = address_space_map(as, addr, &xlen, dir == DMA_DIRECTION_FROM_DEVICE); + p = address_space_map(as, addr, &xlen, dir == DMA_DIRECTION_FROM_DEVICE, + MEMTXATTRS_UNSPECIFIED); *len = xlen; return p; } diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index a1db20e3a8..aa200cba4c 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -431,7 +431,8 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, return NULL; } - hptes = address_space_map(CPU(cpu)->as, base + pte_offset, &plen, false); + hptes = address_space_map(CPU(cpu)->as, base + pte_offset, &plen, false, + MEMTXATTRS_UNSPECIFIED); if (plen < (n * HASH_PTE_SIZE_64)) { hw_error("%s: Unable to map all requested HPTEs\n", __func__); } From fddffa426894e3dae339d113c98a2979026bb6d3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0858/2380] Make address_space_access_valid() take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to address_space_access_valid(). Its callers either have an attrs value to hand, or don't care and can use MEMTXATTRS_UNSPECIFIED. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-6-peter.maydell@linaro.org --- exec.c | 3 ++- include/exec/memory.h | 4 +++- include/sysemu/dma.h | 3 ++- target/s390x/diag.c | 6 ++++-- target/s390x/excp_helper.c | 3 ++- target/s390x/mmu_helper.c | 3 ++- target/s390x/sigp.c | 3 ++- 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/exec.c b/exec.c index 1dc81cfe4a..22af4e8cb9 100644 --- a/exec.c +++ b/exec.c @@ -3480,7 +3480,8 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, } bool address_space_access_valid(AddressSpace *as, hwaddr addr, - int len, bool is_write) + int len, bool is_write, + MemTxAttrs attrs) { FlatView *fv; bool result; diff --git a/include/exec/memory.h b/include/exec/memory.h index d594639d7c..3f2671f332 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1937,8 +1937,10 @@ static inline MemoryRegion *address_space_translate(AddressSpace *as, * @addr: address within that address space * @len: length of the area to be checked * @is_write: indicates the transfer direction + * @attrs: memory attributes */ -bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, bool is_write); +bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, + bool is_write, MemTxAttrs attrs); /* address_space_map: map a physical memory region into a host virtual address * diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h index 0d73902634..5da3c4e3c5 100644 --- a/include/sysemu/dma.h +++ b/include/sysemu/dma.h @@ -77,7 +77,8 @@ static inline bool dma_memory_valid(AddressSpace *as, DMADirection dir) { return address_space_access_valid(as, addr, len, - dir == DMA_DIRECTION_FROM_DEVICE); + dir == DMA_DIRECTION_FROM_DEVICE, + MEMTXATTRS_UNSPECIFIED); } static inline int dma_memory_rw_relaxed(AddressSpace *as, dma_addr_t addr, diff --git a/target/s390x/diag.c b/target/s390x/diag.c index ac2c40f363..d1d3433aa7 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -87,7 +87,8 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) return; } if (!address_space_access_valid(&address_space_memory, addr, - sizeof(IplParameterBlock), false)) { + sizeof(IplParameterBlock), false, + MEMTXATTRS_UNSPECIFIED)) { s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); return; } @@ -116,7 +117,8 @@ out: return; } if (!address_space_access_valid(&address_space_memory, addr, - sizeof(IplParameterBlock), true)) { + sizeof(IplParameterBlock), true, + MEMTXATTRS_UNSPECIFIED)) { s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); return; } diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c index dfee221111..f0ce60cff2 100644 --- a/target/s390x/excp_helper.c +++ b/target/s390x/excp_helper.c @@ -120,7 +120,8 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, int size, /* check out of RAM access */ if (!address_space_access_valid(&address_space_memory, raddr, - TARGET_PAGE_SIZE, rw)) { + TARGET_PAGE_SIZE, rw, + MEMTXATTRS_UNSPECIFIED)) { DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, (uint64_t)raddr, (uint64_t)ram_size); trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO); diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index a25deef5dd..145b62a7ef 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -461,7 +461,8 @@ static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages, return ret; } if (!address_space_access_valid(&address_space_memory, pages[i], - TARGET_PAGE_SIZE, is_write)) { + TARGET_PAGE_SIZE, is_write, + MEMTXATTRS_UNSPECIFIED)) { trigger_access_exception(env, PGM_ADDRESSING, ILEN_AUTO, 0); return -EFAULT; } diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index aff1530c82..c1f9245797 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -280,7 +280,8 @@ static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg) cpu_synchronize_state(cs); if (!address_space_access_valid(&address_space_memory, addr, - sizeof(struct LowCore), false)) { + sizeof(struct LowCore), false, + MEMTXATTRS_UNSPECIFIED)) { set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); return; } From 53d0790dfefdf3e226dd50a581477c5721786a76 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0859/2380] Make flatview_extend_translation() take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to flatview_extend_translation(). Its callers either have an attrs value to hand, or don't care and can use MEMTXATTRS_UNSPECIFIED. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-7-peter.maydell@linaro.org --- exec.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/exec.c b/exec.c index 22af4e8cb9..718b33921b 100644 --- a/exec.c +++ b/exec.c @@ -3495,9 +3495,9 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, static hwaddr flatview_extend_translation(FlatView *fv, hwaddr addr, - hwaddr target_len, - MemoryRegion *mr, hwaddr base, hwaddr len, - bool is_write) + hwaddr target_len, + MemoryRegion *mr, hwaddr base, hwaddr len, + bool is_write, MemTxAttrs attrs) { hwaddr done = 0; hwaddr xlat; @@ -3574,7 +3574,7 @@ void *address_space_map(AddressSpace *as, memory_region_ref(mr); *plen = flatview_extend_translation(fv, addr, len, mr, xlat, - l, is_write); + l, is_write, attrs); ptr = qemu_ram_ptr_length(mr->ram_block, xlat, plen, true); rcu_read_unlock(); @@ -3659,8 +3659,13 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, mr = cache->mrs.mr; memory_region_ref(mr); if (memory_access_is_direct(mr, is_write)) { + /* We don't care about the memory attributes here as we're only + * doing this if we found actual RAM, which behaves the same + * regardless of attributes; so UNSPECIFIED is fine. + */ l = flatview_extend_translation(cache->fv, addr, len, mr, - cache->xlat, l, is_write); + cache->xlat, l, is_write, + MEMTXATTRS_UNSPECIFIED); cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true); } else { cache->ptr = NULL; From 6d7b9a6c3bd21bba95203abd1ed9a572688822ae Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0860/2380] Make memory_region_access_valid() take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to memory_region_access_valid(). Its callers either have an attrs value to hand, or don't care and can use MEMTXATTRS_UNSPECIFIED. The callsite in flatview_access_valid() is part of a recursive loop flatview_access_valid() -> memory_region_access_valid() -> subpage_accepts() -> flatview_access_valid(); we make it pass MEMTXATTRS_UNSPECIFIED for now, until the next several commits have plumbed an attrs parameter through the rest of the loop and we can add an attrs parameter to flatview_access_valid(). Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-8-peter.maydell@linaro.org --- exec.c | 4 +++- hw/s390x/s390-pci-inst.c | 3 ++- include/exec/memory-internal.h | 3 ++- memory.c | 7 ++++--- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/exec.c b/exec.c index 718b33921b..6cf97b5d28 100644 --- a/exec.c +++ b/exec.c @@ -3468,7 +3468,9 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, mr = flatview_translate(fv, addr, &xlat, &l, is_write); if (!memory_access_is_direct(mr, is_write)) { l = memory_access_size(mr, l, addr); - if (!memory_region_access_valid(mr, xlat, l, is_write)) { + /* When our callers all have attrs we'll pass them through here */ + if (!memory_region_access_valid(mr, xlat, l, is_write, + MEMTXATTRS_UNSPECIFIED)) { return false; } } diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 02a815fd31..d1a5f79678 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -762,7 +762,8 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, mr = s390_get_subregion(mr, offset, len); offset -= mr->addr; - if (!memory_region_access_valid(mr, offset, len, true)) { + if (!memory_region_access_valid(mr, offset, len, true, + MEMTXATTRS_UNSPECIFIED)) { s390_program_interrupt(env, PGM_OPERAND, 6, ra); return 0; } diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 58399b9318..56c25c0ef7 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -37,7 +37,8 @@ void flatview_unref(FlatView *view); extern const MemoryRegionOps unassigned_mem_ops; bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, - unsigned size, bool is_write); + unsigned size, bool is_write, + MemTxAttrs attrs); void flatview_add_to_dispatch(FlatView *fv, MemoryRegionSection *section); AddressSpaceDispatch *address_space_dispatch_new(FlatView *fv); diff --git a/memory.c b/memory.c index fc7f9b782b..279f7c9b4a 100644 --- a/memory.c +++ b/memory.c @@ -1347,7 +1347,8 @@ static const MemoryRegionOps ram_device_mem_ops = { bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, unsigned size, - bool is_write) + bool is_write, + MemTxAttrs attrs) { int access_size_min, access_size_max; int access_size, i; @@ -1416,7 +1417,7 @@ MemTxResult memory_region_dispatch_read(MemoryRegion *mr, { MemTxResult r; - if (!memory_region_access_valid(mr, addr, size, false)) { + if (!memory_region_access_valid(mr, addr, size, false, attrs)) { *pval = unassigned_mem_read(mr, addr, size); return MEMTX_DECODE_ERROR; } @@ -1458,7 +1459,7 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr, unsigned size, MemTxAttrs attrs) { - if (!memory_region_access_valid(mr, addr, size, true)) { + if (!memory_region_access_valid(mr, addr, size, true, attrs)) { unassigned_mem_write(mr, addr, data, size); return MEMTX_DECODE_ERROR; } From 8372d383279f772a8727ca7ee5f288cecb175c7b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0861/2380] Make MemoryRegion valid.accepts callback take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to the MemoryRegion valid.accepts callback. We'll need this for subpage_accepts(). We could take the approach we used with the read and write callbacks and add new a new _with_attrs version, but since there are so few implementations of the accepts hook we just change them all. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-9-peter.maydell@linaro.org --- exec.c | 9 ++++++--- hw/hppa/dino.c | 3 ++- hw/nvram/fw_cfg.c | 12 ++++++++---- hw/scsi/esp.c | 3 ++- hw/xen/xen_pt_msi.c | 3 ++- include/exec/memory.h | 3 ++- memory.c | 5 +++-- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/exec.c b/exec.c index 6cf97b5d28..b58eb0fedd 100644 --- a/exec.c +++ b/exec.c @@ -2539,7 +2539,8 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, } static bool notdirty_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return is_write; } @@ -2762,7 +2763,8 @@ static MemTxResult subpage_write(void *opaque, hwaddr addr, } static bool subpage_accepts(void *opaque, hwaddr addr, - unsigned len, bool is_write) + unsigned len, bool is_write, + MemTxAttrs attrs) { subpage_t *subpage = opaque; #if defined(DEBUG_SUBPAGE) @@ -2845,7 +2847,8 @@ static void readonly_mem_write(void *opaque, hwaddr addr, } static bool readonly_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return is_write; } diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c index c5dcf3104d..26f2704cd5 100644 --- a/hw/hppa/dino.c +++ b/hw/hppa/dino.c @@ -137,7 +137,8 @@ static void gsc_to_pci_forwarding(DinoState *s) } static bool dino_chip_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { switch (addr) { case DINO_IAR0: diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 2a0739d0e9..b23e7f64a8 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -420,14 +420,16 @@ static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, } static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return !is_write || ((size == 4 && (addr == 0 || addr == 4)) || (size == 8 && addr == 0)); } static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return addr == 0; } @@ -439,7 +441,8 @@ static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr, } static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return is_write && size == 2; } @@ -458,7 +461,8 @@ static void fw_cfg_comb_write(void *opaque, hwaddr addr, } static bool fw_cfg_comb_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return (size == 1) || (is_write && size == 2); } diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 64ec285826..9ed9727744 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -564,7 +564,8 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) } static bool esp_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return (size == 1) || (is_write && size == 4); } diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index 6d1e3bdeb4..cc514f9157 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -498,7 +498,8 @@ static uint64_t pci_msix_read(void *opaque, hwaddr addr, } static bool pci_msix_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return !(addr & (size - 1)); } diff --git a/include/exec/memory.h b/include/exec/memory.h index 3f2671f332..76a2dd3878 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -166,7 +166,8 @@ struct MemoryRegionOps { * as a machine check exception). */ bool (*accepts)(void *opaque, hwaddr addr, - unsigned size, bool is_write); + unsigned size, bool is_write, + MemTxAttrs attrs); } valid; /* Internal implementation constraints: */ struct { diff --git a/memory.c b/memory.c index 279f7c9b4a..10fa2ddd31 100644 --- a/memory.c +++ b/memory.c @@ -1269,7 +1269,8 @@ static void unassigned_mem_write(void *opaque, hwaddr addr, } static bool unassigned_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return false; } @@ -1374,7 +1375,7 @@ bool memory_region_access_valid(MemoryRegion *mr, access_size = MAX(MIN(size, access_size_max), access_size_min); for (i = 0; i < size; i += access_size) { if (!mr->ops->valid.accepts(mr->opaque, addr + i, access_size, - is_write)) { + is_write, attrs)) { return false; } } From eace72b7a6d01d2e8fb69611c7ca028c8832f7aa Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0862/2380] Make flatview_access_valid() take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to flatview_access_valid(). Its callers now all have an attrs value to hand, so we can correct our earlier temporary use of MEMTXATTRS_UNSPECIFIED. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-10-peter.maydell@linaro.org --- exec.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/exec.c b/exec.c index b58eb0fedd..33e0a4f153 100644 --- a/exec.c +++ b/exec.c @@ -2697,7 +2697,7 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, const uint8_t *buf, int len); static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, - bool is_write); + bool is_write, MemTxAttrs attrs); static MemTxResult subpage_read(void *opaque, hwaddr addr, uint64_t *data, unsigned len, MemTxAttrs attrs) @@ -2773,7 +2773,7 @@ static bool subpage_accepts(void *opaque, hwaddr addr, #endif return flatview_access_valid(subpage->fv, addr + subpage->base, - len, is_write); + len, is_write, attrs); } static const MemoryRegionOps subpage_ops = { @@ -3461,7 +3461,7 @@ static void cpu_notify_map_clients(void) } static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, - bool is_write) + bool is_write, MemTxAttrs attrs) { MemoryRegion *mr; hwaddr l, xlat; @@ -3471,9 +3471,7 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, mr = flatview_translate(fv, addr, &xlat, &l, is_write); if (!memory_access_is_direct(mr, is_write)) { l = memory_access_size(mr, l, addr); - /* When our callers all have attrs we'll pass them through here */ - if (!memory_region_access_valid(mr, xlat, l, is_write, - MEMTXATTRS_UNSPECIFIED)) { + if (!memory_region_access_valid(mr, xlat, l, is_write, attrs)) { return false; } } @@ -3493,7 +3491,7 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, rcu_read_lock(); fv = address_space_to_flatview(as); - result = flatview_access_valid(fv, addr, len, is_write); + result = flatview_access_valid(fv, addr, len, is_write, attrs); rcu_read_unlock(); return result; } From efa99a2ff8edf39f48777326c2debdb196f07c41 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:52 +0100 Subject: [PATCH 0863/2380] Make flatview_translate() take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to flatview_translate(); all its callers now have attrs available. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-11-peter.maydell@linaro.org --- exec.c | 17 +++++++++-------- include/exec/memory.h | 7 ++++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/exec.c b/exec.c index 33e0a4f153..d54a56fefa 100644 --- a/exec.c +++ b/exec.c @@ -618,7 +618,8 @@ iotlb_fail: /* Called from RCU critical section */ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, - hwaddr *plen, bool is_write) + hwaddr *plen, bool is_write, + MemTxAttrs attrs) { MemoryRegion *mr; MemoryRegionSection section; @@ -3152,7 +3153,7 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, } l = len; - mr = flatview_translate(fv, addr, &addr1, &l, true); + mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); } return result; @@ -3168,7 +3169,7 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, MemTxResult result = MEMTX_OK; l = len; - mr = flatview_translate(fv, addr, &addr1, &l, true); + mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); result = flatview_write_continue(fv, addr, attrs, buf, len, addr1, l, mr); @@ -3239,7 +3240,7 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, } l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false); + mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); } return result; @@ -3254,7 +3255,7 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, MemoryRegion *mr; l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false); + mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); return flatview_read_continue(fv, addr, attrs, buf, len, addr1, l, mr); } @@ -3468,7 +3469,7 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, while (len > 0) { l = len; - mr = flatview_translate(fv, addr, &xlat, &l, is_write); + mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); if (!memory_access_is_direct(mr, is_write)) { l = memory_access_size(mr, l, addr); if (!memory_region_access_valid(mr, xlat, l, is_write, attrs)) { @@ -3516,7 +3517,7 @@ flatview_extend_translation(FlatView *fv, hwaddr addr, len = target_len; this_mr = flatview_translate(fv, addr, &xlat, - &len, is_write); + &len, is_write, attrs); if (this_mr != mr || xlat != base + done) { return done; } @@ -3549,7 +3550,7 @@ void *address_space_map(AddressSpace *as, l = len; rcu_read_lock(); fv = address_space_to_flatview(as); - mr = flatview_translate(fv, addr, &xlat, &l, is_write); + mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); if (!memory_access_is_direct(mr, is_write)) { if (atomic_xchg(&bounce.in_use, true)) { diff --git a/include/exec/memory.h b/include/exec/memory.h index 76a2dd3878..28a694970a 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1913,7 +1913,8 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, */ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, - hwaddr *len, bool is_write); + hwaddr *len, bool is_write, + MemTxAttrs attrs); static inline MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, hwaddr *xlat, @@ -1921,7 +1922,7 @@ static inline MemoryRegion *address_space_translate(AddressSpace *as, MemTxAttrs attrs) { return flatview_translate(address_space_to_flatview(as), - addr, xlat, len, is_write); + addr, xlat, len, is_write, attrs); } /* address_space_access_valid: check for validity of accessing an address @@ -2030,7 +2031,7 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, rcu_read_lock(); fv = address_space_to_flatview(as); l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false); + mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); if (len == l && memory_access_is_direct(mr, false)) { ptr = qemu_map_ram_ptr(mr->ram_block, addr1); memcpy(buf, ptr, len); From 7446eb07c1da660c38bf075555f2d69b84caf481 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:53 +0100 Subject: [PATCH 0864/2380] Make address_space_get_iotlb_entry() take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to address_space_get_iotlb_entry(). Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-12-peter.maydell@linaro.org --- exec.c | 2 +- hw/virtio/vhost.c | 3 ++- include/exec/memory.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/exec.c b/exec.c index d54a56fefa..57c64c6c13 100644 --- a/exec.c +++ b/exec.c @@ -582,7 +582,7 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, /* Called from RCU critical section */ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, - bool is_write) + bool is_write, MemTxAttrs attrs) { MemoryRegionSection section; hwaddr xlat, page_mask; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index b08290036d..4565b69f83 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -897,7 +897,8 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) trace_vhost_iotlb_miss(dev, 1); iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as, - iova, write); + iova, write, + MEMTXATTRS_UNSPECIFIED); if (iotlb.target_as != NULL) { ret = vhost_memory_region_lookup(dev, iotlb.translated_addr, &uaddr, &len); diff --git a/include/exec/memory.h b/include/exec/memory.h index 28a694970a..67ea7fe1ee 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1896,7 +1896,7 @@ void address_space_cache_destroy(MemoryRegionCache *cache); * entry. Should be called from an RCU critical section. */ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, - bool is_write); + bool is_write, MemTxAttrs attrs); /* address_space_translate: translate an address range into an address space * into a MemoryRegion and an address range into that section. Should be From 49e14aa827d5e85a5e9196d9d747e3c353c57b5c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:53 +0100 Subject: [PATCH 0865/2380] Make flatview_do_translate() take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to flatview_do_translate(). Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-13-peter.maydell@linaro.org --- exec.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/exec.c b/exec.c index 57c64c6c13..e84e0d8c58 100644 --- a/exec.c +++ b/exec.c @@ -541,6 +541,7 @@ unassigned: * @is_write: whether the translation operation is for write * @is_mmio: whether this can be MMIO, set true if it can * @target_as: the address space targeted by the IOMMU + * @attrs: memory transaction attributes * * This function is called from RCU critical section */ @@ -551,7 +552,8 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, hwaddr *page_mask_out, bool is_write, bool is_mmio, - AddressSpace **target_as) + AddressSpace **target_as, + MemTxAttrs attrs) { MemoryRegionSection *section; IOMMUMemoryRegion *iommu_mr; @@ -592,7 +594,8 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, * but page mask. */ section = flatview_do_translate(address_space_to_flatview(as), addr, &xlat, - NULL, &page_mask, is_write, false, &as); + NULL, &page_mask, is_write, false, &as, + attrs); /* Illegal translation */ if (section.mr == &io_mem_unassigned) { @@ -627,7 +630,7 @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, /* This can be MMIO, so setup MMIO bit. */ section = flatview_do_translate(fv, addr, xlat, plen, NULL, - is_write, true, &as); + is_write, true, &as, attrs); mr = section.mr; if (xen_enabled() && memory_access_is_direct(mr, is_write)) { From 2f7b009c2e791e1321dd762e16ce3b0a611ac009 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:53 +0100 Subject: [PATCH 0866/2380] Make address_space_translate_iommu take a MemTxAttrs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of plumbing MemTxAttrs down to the IOMMU translate method, add MemTxAttrs as an argument to address_space_translate_iommu(). Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180521140402.23318-14-peter.maydell@linaro.org --- exec.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/exec.c b/exec.c index e84e0d8c58..c30f905598 100644 --- a/exec.c +++ b/exec.c @@ -478,6 +478,7 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x * @is_write: whether the translation operation is for write * @is_mmio: whether this can be MMIO, set true if it can * @target_as: the address space targeted by the IOMMU + * @attrs: transaction attributes * * This function is called from RCU critical section. It is the common * part of flatview_do_translate and address_space_translate_cached. @@ -488,7 +489,8 @@ static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iomm hwaddr *page_mask_out, bool is_write, bool is_mmio, - AddressSpace **target_as) + AddressSpace **target_as, + MemTxAttrs attrs) { MemoryRegionSection *section; hwaddr page_mask = (hwaddr)-1; @@ -572,7 +574,7 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, return address_space_translate_iommu(iommu_mr, xlat, plen_out, page_mask_out, is_write, is_mmio, - target_as); + target_as, attrs); } if (page_mask_out) { /* Not behind an IOMMU, use default page size. */ @@ -3734,7 +3736,7 @@ static inline MemoryRegion *address_space_translate_cached( section = address_space_translate_iommu(iommu_mr, xlat, plen, NULL, is_write, true, - &target_as); + &target_as, attrs); return section.mr; } From e0a37e266084fb26951465e08515af46807c312c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 May 2018 14:50:53 +0100 Subject: [PATCH 0867/2380] vmstate.h: Provide VMSTATE_BOOL_SUB_ARRAY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide a VMSTATE_BOOL_SUB_ARRAY to go with VMSTATE_UINT8_SUB_ARRAY and friends. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180521140402.23318-23-peter.maydell@linaro.org --- include/migration/vmstate.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index df463fd33d..59fc75e418 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -870,6 +870,9 @@ extern const VMStateInfo vmstate_info_qtailq; #define VMSTATE_BOOL_ARRAY(_f, _s, _n) \ VMSTATE_BOOL_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_BOOL_SUB_ARRAY(_f, _s, _start, _num) \ + VMSTATE_SUB_ARRAY(_f, _s, _start, _num, 0, vmstate_info_bool, bool) + #define VMSTATE_UINT16_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint16, uint16_t) From 6e3e723966197463117642d8a5fd7b4f6446e510 Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Thu, 31 May 2018 14:50:53 +0100 Subject: [PATCH 0868/2380] ARM: ACPI: Fix use-after-free due to memory realloc acpi_data_push uses g_array_set_size to resize the memory size. If there is no enough contiguous memory, the address will be changed. So previous pointer could not be used any more. It must update the pointer and use the new one. Also, previous codes wrongly use le32 conversion of iort->node_offset for subsequent computations that will result incorrect value if host is not litlle endian. So use the non-converted one instead. Signed-off-by: Shannon Zhao Reviewed-by: Eric Auger Message-id: 1527663951-14552-1-git-send-email-zhaoshenglong@huawei.com Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 92ceee9c0f..74f5744e87 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -400,7 +400,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) AcpiIortItsGroup *its; AcpiIortTable *iort; AcpiIortSmmu3 *smmu; - size_t node_size, iort_length, smmu_offset = 0; + size_t node_size, iort_node_offset, iort_length, smmu_offset = 0; AcpiIortRC *rc; iort = acpi_data_push(table_data, sizeof(*iort)); @@ -413,7 +413,12 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) iort_length = sizeof(*iort); iort->node_count = cpu_to_le32(nb_nodes); - iort->node_offset = cpu_to_le32(sizeof(*iort)); + /* + * Use a copy in case table_data->data moves during acpi_data_push + * operations. + */ + iort_node_offset = sizeof(*iort); + iort->node_offset = cpu_to_le32(iort_node_offset); /* ITS group node */ node_size = sizeof(*its) + sizeof(uint32_t); @@ -429,7 +434,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) int irq = vms->irqmap[VIRT_SMMU]; /* SMMUv3 node */ - smmu_offset = iort->node_offset + node_size; + smmu_offset = iort_node_offset + node_size; node_size = sizeof(*smmu) + sizeof(*idmap); iort_length += node_size; smmu = acpi_data_push(table_data, node_size); @@ -450,7 +455,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) idmap->id_count = cpu_to_le32(0xFFFF); idmap->output_base = 0; /* output IORT node is the ITS group node (the first node) */ - idmap->output_reference = cpu_to_le32(iort->node_offset); + idmap->output_reference = cpu_to_le32(iort_node_offset); } /* Root Complex Node */ @@ -479,9 +484,14 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) idmap->output_reference = cpu_to_le32(smmu_offset); } else { /* output IORT node is the ITS group node (the first node) */ - idmap->output_reference = cpu_to_le32(iort->node_offset); + idmap->output_reference = cpu_to_le32(iort_node_offset); } + /* + * Update the pointer address in case table_data->data moves during above + * acpi_data_push operations. + */ + iort = (AcpiIortTable *)(table_data->data + iort_start); iort->length = cpu_to_le32(iort_length); build_header(linker, table_data, (void *)(table_data->data + iort_start), From 2f15b79280cf71b7991dfd3f0312a1797630e376 Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Thu, 31 May 2018 15:16:34 +0800 Subject: [PATCH 0869/2380] KVM: GIC: Fix memory leak due to calling kvm_init_irq_routing twice kvm_irqchip_create called by kvm_init will call kvm_init_irq_routing to initialize global capability variables. If we call kvm_init_irq_routing in GIC realize function, previous allocated memory will leak. Fix this by deleting the unnecessary call. Signed-off-by: Shannon Zhao Reviewed-by: Eric Auger Message-id: 1527750994-14360-1-git-send-email-zhaoshenglong@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gic_kvm.c | 1 - hw/intc/arm_gicv3_kvm.c | 1 - 2 files changed, 2 deletions(-) diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 6f467e68a8..204369d0e2 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -572,7 +572,6 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) if (kvm_has_gsi_routing()) { /* set up irq routing */ - kvm_init_irq_routing(kvm_state); for (i = 0; i < s->num_irq - GIC_INTERNAL; ++i) { kvm_irqchip_add_irq_route(kvm_state, i, 0, i); } diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 3536795501..0279b86cd9 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -761,7 +761,6 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) if (kvm_has_gsi_routing()) { /* set up irq routing */ - kvm_init_irq_routing(kvm_state); for (i = 0; i < s->num_irq - GIC_INTERNAL; ++i) { kvm_irqchip_add_irq_route(kvm_state, i, 0, i); } From 0dd693ef1f15b6e9c4ba8b0118663e10338077cf Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Thu, 31 May 2018 11:29:37 +0800 Subject: [PATCH 0870/2380] sandbox: disable -sandbox if CONFIG_SECCOMP undefined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_SECCOMP is undefined, the option 'elevateprivileges' remains compiled. This would make libvirt set the corresponding capability and then trigger failure during guest startup. This patch moves the code regarding seccomp command line options to qemu-seccomp.c file and wraps qemu_opts_foreach finding sandbox option with CONFIG_SECCOMP. Because parse_sandbox() is moved into qemu-seccomp.c file, change seccomp_start() to static function. Signed-off-by: Yi Min Zhao Reviewed-by: Ján Tomko Tested-by: Ján Tomko Acked-by: Eduardo Otubo Message-Id: <20180531032937.1925-1-zyimin@linux.ibm.com> Signed-off-by: Paolo Bonzini --- include/sysemu/seccomp.h | 3 +- qemu-seccomp.c | 121 +++++++++++++++++++++++++++++++++++++- vl.c | 124 +++------------------------------------ 3 files changed, 130 insertions(+), 118 deletions(-) diff --git a/include/sysemu/seccomp.h b/include/sysemu/seccomp.h index 9b092aa23f..fe859894f6 100644 --- a/include/sysemu/seccomp.h +++ b/include/sysemu/seccomp.h @@ -21,5 +21,6 @@ #define QEMU_SECCOMP_SET_SPAWN (1 << 3) #define QEMU_SECCOMP_SET_RESOURCECTL (1 << 4) -int seccomp_start(uint32_t seccomp_opts); +int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp); + #endif diff --git a/qemu-seccomp.c b/qemu-seccomp.c index b770a77d33..148e4c6f24 100644 --- a/qemu-seccomp.c +++ b/qemu-seccomp.c @@ -13,6 +13,11 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu/osdep.h" +#include "qemu/config-file.h" +#include "qemu/option.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include #include #include "sysemu/seccomp.h" @@ -96,7 +101,7 @@ static const struct QemuSeccompSyscall blacklist[] = { }; -int seccomp_start(uint32_t seccomp_opts) +static int seccomp_start(uint32_t seccomp_opts) { int rc = 0; unsigned int i = 0; @@ -125,3 +130,117 @@ int seccomp_start(uint32_t seccomp_opts) seccomp_release(ctx); return rc; } + +#ifdef CONFIG_SECCOMP +int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) +{ + if (qemu_opt_get_bool(opts, "enable", false)) { + uint32_t seccomp_opts = QEMU_SECCOMP_SET_DEFAULT + | QEMU_SECCOMP_SET_OBSOLETE; + const char *value = NULL; + + value = qemu_opt_get(opts, "obsolete"); + if (value) { + if (g_str_equal(value, "allow")) { + seccomp_opts &= ~QEMU_SECCOMP_SET_OBSOLETE; + } else if (g_str_equal(value, "deny")) { + /* this is the default option, this if is here + * to provide a little bit of consistency for + * the command line */ + } else { + error_report("invalid argument for obsolete"); + return -1; + } + } + + value = qemu_opt_get(opts, "elevateprivileges"); + if (value) { + if (g_str_equal(value, "deny")) { + seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; + } else if (g_str_equal(value, "children")) { + seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; + + /* calling prctl directly because we're + * not sure if host has CAP_SYS_ADMIN set*/ + if (prctl(PR_SET_NO_NEW_PRIVS, 1)) { + error_report("failed to set no_new_privs " + "aborting"); + return -1; + } + } else if (g_str_equal(value, "allow")) { + /* default value */ + } else { + error_report("invalid argument for elevateprivileges"); + return -1; + } + } + + value = qemu_opt_get(opts, "spawn"); + if (value) { + if (g_str_equal(value, "deny")) { + seccomp_opts |= QEMU_SECCOMP_SET_SPAWN; + } else if (g_str_equal(value, "allow")) { + /* default value */ + } else { + error_report("invalid argument for spawn"); + return -1; + } + } + + value = qemu_opt_get(opts, "resourcecontrol"); + if (value) { + if (g_str_equal(value, "deny")) { + seccomp_opts |= QEMU_SECCOMP_SET_RESOURCECTL; + } else if (g_str_equal(value, "allow")) { + /* default value */ + } else { + error_report("invalid argument for resourcecontrol"); + return -1; + } + } + + if (seccomp_start(seccomp_opts) < 0) { + error_report("failed to install seccomp syscall filter " + "in the kernel"); + return -1; + } + } + + return 0; +} + +static QemuOptsList qemu_sandbox_opts = { + .name = "sandbox", + .implied_opt_name = "enable", + .head = QTAILQ_HEAD_INITIALIZER(qemu_sandbox_opts.head), + .desc = { + { + .name = "enable", + .type = QEMU_OPT_BOOL, + }, + { + .name = "obsolete", + .type = QEMU_OPT_STRING, + }, + { + .name = "elevateprivileges", + .type = QEMU_OPT_STRING, + }, + { + .name = "spawn", + .type = QEMU_OPT_STRING, + }, + { + .name = "resourcecontrol", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +static void seccomp_register(void) +{ + qemu_add_opts(&qemu_sandbox_opts); +} +opts_init(seccomp_register); +#endif diff --git a/vl.c b/vl.c index 038d7f8042..4a0e17833d 100644 --- a/vl.c +++ b/vl.c @@ -28,11 +28,7 @@ #include "qemu/cutils.h" #include "qemu/help_option.h" #include "qemu/uuid.h" - -#ifdef CONFIG_SECCOMP -#include #include "sysemu/seccomp.h" -#endif #ifdef CONFIG_SDL #if defined(__APPLE__) || defined(main) @@ -259,35 +255,6 @@ static QemuOptsList qemu_rtc_opts = { }, }; -static QemuOptsList qemu_sandbox_opts = { - .name = "sandbox", - .implied_opt_name = "enable", - .head = QTAILQ_HEAD_INITIALIZER(qemu_sandbox_opts.head), - .desc = { - { - .name = "enable", - .type = QEMU_OPT_BOOL, - }, - { - .name = "obsolete", - .type = QEMU_OPT_STRING, - }, - { - .name = "elevateprivileges", - .type = QEMU_OPT_STRING, - }, - { - .name = "spawn", - .type = QEMU_OPT_STRING, - }, - { - .name = "resourcecontrol", - .type = QEMU_OPT_STRING, - }, - { /* end of list */ } - }, -}; - static QemuOptsList qemu_option_rom_opts = { .name = "option-rom", .implied_opt_name = "romfile", @@ -1043,88 +1010,6 @@ static int bt_parse(const char *opt) return 1; } -static int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) -{ - if (qemu_opt_get_bool(opts, "enable", false)) { -#ifdef CONFIG_SECCOMP - uint32_t seccomp_opts = QEMU_SECCOMP_SET_DEFAULT - | QEMU_SECCOMP_SET_OBSOLETE; - const char *value = NULL; - - value = qemu_opt_get(opts, "obsolete"); - if (value) { - if (g_str_equal(value, "allow")) { - seccomp_opts &= ~QEMU_SECCOMP_SET_OBSOLETE; - } else if (g_str_equal(value, "deny")) { - /* this is the default option, this if is here - * to provide a little bit of consistency for - * the command line */ - } else { - error_report("invalid argument for obsolete"); - return -1; - } - } - - value = qemu_opt_get(opts, "elevateprivileges"); - if (value) { - if (g_str_equal(value, "deny")) { - seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; - } else if (g_str_equal(value, "children")) { - seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; - - /* calling prctl directly because we're - * not sure if host has CAP_SYS_ADMIN set*/ - if (prctl(PR_SET_NO_NEW_PRIVS, 1)) { - error_report("failed to set no_new_privs " - "aborting"); - return -1; - } - } else if (g_str_equal(value, "allow")) { - /* default value */ - } else { - error_report("invalid argument for elevateprivileges"); - return -1; - } - } - - value = qemu_opt_get(opts, "spawn"); - if (value) { - if (g_str_equal(value, "deny")) { - seccomp_opts |= QEMU_SECCOMP_SET_SPAWN; - } else if (g_str_equal(value, "allow")) { - /* default value */ - } else { - error_report("invalid argument for spawn"); - return -1; - } - } - - value = qemu_opt_get(opts, "resourcecontrol"); - if (value) { - if (g_str_equal(value, "deny")) { - seccomp_opts |= QEMU_SECCOMP_SET_RESOURCECTL; - } else if (g_str_equal(value, "allow")) { - /* default value */ - } else { - error_report("invalid argument for resourcecontrol"); - return -1; - } - } - - if (seccomp_start(seccomp_opts) < 0) { - error_report("failed to install seccomp syscall filter " - "in the kernel"); - return -1; - } -#else - error_report("seccomp support is disabled"); - return -1; -#endif - } - - return 0; -} - static int parse_name(void *opaque, QemuOpts *opts, Error **errp) { const char *proc_name; @@ -3059,7 +2944,6 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_mem_opts); qemu_add_opts(&qemu_smp_opts); qemu_add_opts(&qemu_boot_opts); - qemu_add_opts(&qemu_sandbox_opts); qemu_add_opts(&qemu_add_fd_opts); qemu_add_opts(&qemu_object_opts); qemu_add_opts(&qemu_tpmdev_opts); @@ -3957,11 +3841,17 @@ int main(int argc, char **argv, char **envp) qtest_log = optarg; break; case QEMU_OPTION_sandbox: +#ifdef CONFIG_SECCOMP opts = qemu_opts_parse_noisily(qemu_find_opts("sandbox"), optarg, true); if (!opts) { exit(1); } +#else + error_report("-sandbox support is not enabled " + "in this QEMU binary"); + exit(1); +#endif break; case QEMU_OPTION_add_fd: #ifndef _WIN32 @@ -4048,10 +3938,12 @@ int main(int argc, char **argv, char **envp) exit(1); } +#ifdef CONFIG_SECCOMP if (qemu_opts_foreach(qemu_find_opts("sandbox"), parse_sandbox, NULL, NULL)) { exit(1); } +#endif if (qemu_opts_foreach(qemu_find_opts("name"), parse_name, NULL, NULL)) { From d791937fa0db6e6f64fc0e60c6aaf881ce51f083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:26:59 -0300 Subject: [PATCH 0871/2380] vfio: Include "exec/address-spaces.h" directly in the source file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No declaration of "hw/vfio/vfio-common.h" directly requires to include the "exec/address-spaces.h" header. To simplify dependencies and ease the upcoming cleanup of "exec/address-spaces.h", directly include it in the source file where the declaration are used. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-2-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Acked-by: Cornelia Huck Signed-off-by: Paolo Bonzini --- hw/vfio/ccw.c | 1 + hw/vfio/platform.c | 1 + include/hw/vfio/vfio-common.h | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index e67392c5f9..76e4e8c652 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -22,6 +22,7 @@ #include "hw/vfio/vfio-common.h" #include "hw/s390x/s390-ccw.h" #include "hw/s390x/ccw-device.h" +#include "exec/address-spaces.h" #include "qemu/error-report.h" #define TYPE_VFIO_CCW "vfio-ccw" diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 5c921c27ba..57c4a0ee2b 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -24,6 +24,7 @@ #include "qemu/range.h" #include "sysemu/sysemu.h" #include "exec/memory.h" +#include "exec/address-spaces.h" #include "qemu/queue.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index d9360148e6..8264a65fa5 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -22,7 +22,6 @@ #define HW_VFIO_VFIO_COMMON_H #include "qemu-common.h" -#include "exec/address-spaces.h" #include "exec/memory.h" #include "qemu/queue.h" #include "qemu/notify.h" From df924c06430f55ed527f564adcab358c71cf3c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:00 -0300 Subject: [PATCH 0872/2380] accel: Do not include "exec/address-spaces.h" if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code change produced with: $ git grep '#include "exec/address-spaces.h"' accel | \ cut -d: -f-1 | \ xargs egrep -L "(get_system_|address_space_)" | \ xargs sed -i.bak '/#include "exec\/address-spaces.h"/d' Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-3-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 1 - 1 file changed, 1 deletion(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 0b154cc678..4ef95d8dd3 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -25,7 +25,6 @@ #include "qemu/atomic.h" #include "sysemu/qtest.h" #include "qemu/timer.h" -#include "exec/address-spaces.h" #include "qemu/rcu.h" #include "exec/tb-hash.h" #include "exec/tb-lookup.h" From 25a3173a0cd318445f274d82543d9a8133b4c384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:01 -0300 Subject: [PATCH 0873/2380] target: Do not include "exec/address-spaces.h" if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code change produced with: $ git grep '#include "exec/address-spaces.h"' target | \ cut -d: -f-1 | \ xargs egrep -L "(get_system_|address_space_)" | \ xargs sed -i.bak '/#include "exec\/address-spaces.h"/d' Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-4-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Acked-by: Cornelia Huck Signed-off-by: Paolo Bonzini --- target/i386/hvf/x86_task.c | 1 - target/s390x/kvm.c | 1 - target/s390x/mem_helper.c | 1 - target/s390x/misc_helper.c | 1 - target/sparc/mmu_helper.c | 1 - 5 files changed, 5 deletions(-) diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index 4abf3db25e..c3ead2ca73 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -26,7 +26,6 @@ #include #include -#include "exec/address-spaces.h" #include "exec/exec-all.h" #include "exec/ioport.h" #include "hw/i386/apic_internal.h" diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index 58e4380ae3..ac370da281 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -39,7 +39,6 @@ #include "hw/hw.h" #include "sysemu/device_tree.h" #include "exec/gdbstub.h" -#include "exec/address-spaces.h" #include "trace.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-bus.h" diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index a0e28bd124..e21a47fb4d 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "exec/address-spaces.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index 1f834f35ef..de1ced2082 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -26,7 +26,6 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "qemu/timer.h" -#include "exec/address-spaces.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index f8886ae039..135a9c9d9b 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "exec/exec-all.h" #include "trace.h" -#include "exec/address-spaces.h" /* Sparc MMU emulation */ From 86b246534b488f1882ac545ffc76fe896185efd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:02 -0300 Subject: [PATCH 0874/2380] memory: Do not include "exec/ioport.h" if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code change produced with: $ git grep '#include "exec/ioport.h"' memory.c | \ cut -d: -f-1 | \ xargs egrep -Li "(portio|cpu_(in|out).\()" | \ xargs sed -i.bak '/#include "exec\/ioport.h"/d' Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-5-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- memory.c | 1 - 1 file changed, 1 deletion(-) diff --git a/memory.c b/memory.c index fc7f9b782b..7ead90f8cd 100644 --- a/memory.c +++ b/memory.c @@ -19,7 +19,6 @@ #include "cpu.h" #include "exec/memory.h" #include "exec/address-spaces.h" -#include "exec/ioport.h" #include "qapi/visitor.h" #include "qemu/bitops.h" #include "qemu/error-report.h" From 383952e7c8ad8bd856af460e290f2662cf6caec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:03 -0300 Subject: [PATCH 0875/2380] target/i386: Do not include "exec/ioport.h" if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code change produced with: $ git grep '#include "exec/ioport.h"' target | \ cut -d: -f-1 | \ xargs egrep -Li "(portio|cpu_(in|out).\()" | \ xargs sed -i.bak '/#include "exec\/ioport.h"/d' Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-6-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- target/i386/hax-all.c | 1 - target/i386/hvf/hvf.c | 1 - target/i386/hvf/x86_task.c | 1 - target/i386/kvm.c | 1 - 4 files changed, 4 deletions(-) diff --git a/target/i386/hax-all.c b/target/i386/hax-all.c index cad7531406..c5856bbdc3 100644 --- a/target/i386/hax-all.c +++ b/target/i386/hax-all.c @@ -27,7 +27,6 @@ #include "cpu.h" #include "exec/address-spaces.h" #include "exec/exec-all.h" -#include "exec/ioport.h" #include "qemu-common.h" #include "hax-i386.h" diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index c36753954b..f6c872e678 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -66,7 +66,6 @@ #include "exec/address-spaces.h" #include "exec/exec-all.h" -#include "exec/ioport.h" #include "hw/i386/apic_internal.h" #include "hw/boards.h" #include "qemu/main-loop.h" diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index c3ead2ca73..7610d85802 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -27,7 +27,6 @@ #include #include "exec/exec-all.h" -#include "exec/ioport.h" #include "hw/i386/apic_internal.h" #include "hw/boards.h" #include "qemu/main-loop.h" diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 6511329d11..9d8f80f4c0 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -40,7 +40,6 @@ #include "hw/i386/intel_iommu.h" #include "hw/i386/x86-iommu.h" -#include "exec/ioport.h" #include "hw/pci/pci.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" From 3a739112daf4a47d8fd33a52a7d7a72e4c1c5e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:04 -0300 Subject: [PATCH 0876/2380] target/xtensa: Include "qemu/timer.h" to use NANOSECONDS_PER_SECOND MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since d0ce7e9cfc the dc232b structure uses the NANOSECONDS_PER_SECOND definition from "qemu/timer.h". Include it to allow further includes cleanup. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-7-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- target/xtensa/core-dc232b.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/xtensa/core-dc232b.c b/target/xtensa/core-dc232b.c index 7331eeea2f..70f33622ec 100644 --- a/target/xtensa/core-dc232b.c +++ b/target/xtensa/core-dc232b.c @@ -30,6 +30,7 @@ #include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu/host-utils.h" +#include "qemu/timer.h" #include "core-dc232b/core-isa.h" #include "overlay_tool.h" From 9b87338ff36ae1f84aec75395520c2ab232ed017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:05 -0300 Subject: [PATCH 0877/2380] target/ppc: Include "exec/exec-all.h" which provides tlb_flush() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since it inception this include uses tlb_flush() declared in "exec/exec-all.h". Include the other header to allow further includes cleanup. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-8-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- target/ppc/helper_regs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h index 84fd30c2db..5efd18049e 100644 --- a/target/ppc/helper_regs.h +++ b/target/ppc/helper_regs.h @@ -21,6 +21,7 @@ #define HELPER_REGS_H #include "qemu/main-loop.h" +#include "exec/exec-all.h" /* Swap temporary saved registers with GPRs */ static inline void hreg_swap_gpr_tgpr(CPUPPCState *env) From 0d114d0d5b9f3d10a1148b2412865e177ea5e4b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:06 -0300 Subject: [PATCH 0878/2380] target/hppa: Include "qemu/log.h" to use qemu_log() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since his inception in 61766fe9e2d, this file uses the qemu_log() API from "qemu/log.h". Include it to allow further includes cleanup. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-9-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- target/hppa/int_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 787f3d6357..561bf6eb60 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +#include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" From 04a8f72e877b9a912ffd7afa84209909577d1d96 Mon Sep 17 00:00:00 2001 From: Igor Druzhinin Date: Wed, 25 Apr 2018 14:46:47 +0100 Subject: [PATCH 0879/2380] xen/hvm: correct reporting of modified memory under physmap during migration When global_log_dirty is enabled VRAM modification tracking never worked correctly. The address that is passed to xen_hvm_modified_memory() is not the effective PFN but RAM block address which is not the same for VRAM. We need to make a translation for this address into PFN using physmap. Since there is no way to access physmap properly inside xen_hvm_modified_memory() let's make it a global structure. Signed-off-by: Igor Druzhinin Acked-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- hw/i386/xen/xen-hvm.c | 37 ++++++++++++++++++----------------- hw/i386/xen/xen-mapcache.c | 2 +- include/sysemu/xen-mapcache.h | 5 ++--- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 6ffa3c22cc..2afab6573b 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -86,6 +86,8 @@ typedef struct XenPhysmap { QLIST_ENTRY(XenPhysmap) list; } XenPhysmap; +static QLIST_HEAD(, XenPhysmap) xen_physmap; + typedef struct XenIOState { ioservid_t ioservid; shared_iopage_t *shared_page; @@ -107,7 +109,6 @@ typedef struct XenIOState { MemoryListener memory_listener; MemoryListener io_listener; DeviceListener device_listener; - QLIST_HEAD(, XenPhysmap) physmap; hwaddr free_phys_offset; const XenPhysmap *log_for_dirtybit; @@ -274,14 +275,13 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, g_free(pfn_list); } -static XenPhysmap *get_physmapping(XenIOState *state, - hwaddr start_addr, ram_addr_t size) +static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size) { XenPhysmap *physmap = NULL; start_addr &= TARGET_PAGE_MASK; - QLIST_FOREACH(physmap, &state->physmap, list) { + QLIST_FOREACH(physmap, &xen_physmap, list) { if (range_covers_byte(physmap->start_addr, physmap->size, start_addr)) { return physmap; } @@ -289,23 +289,21 @@ static XenPhysmap *get_physmapping(XenIOState *state, return NULL; } -#ifdef XEN_COMPAT_PHYSMAP -static hwaddr xen_phys_offset_to_gaddr(hwaddr start_addr, - ram_addr_t size, void *opaque) +static hwaddr xen_phys_offset_to_gaddr(hwaddr phys_offset, ram_addr_t size) { - hwaddr addr = start_addr & TARGET_PAGE_MASK; - XenIOState *xen_io_state = opaque; + hwaddr addr = phys_offset & TARGET_PAGE_MASK; XenPhysmap *physmap = NULL; - QLIST_FOREACH(physmap, &xen_io_state->physmap, list) { + QLIST_FOREACH(physmap, &xen_physmap, list) { if (range_covers_byte(physmap->phys_offset, physmap->size, addr)) { - return physmap->start_addr; + return physmap->start_addr + (phys_offset - physmap->phys_offset); } } - return start_addr; + return phys_offset; } +#ifdef XEN_COMPAT_PHYSMAP static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) { char path[80], value[17]; @@ -355,7 +353,7 @@ static int xen_add_to_physmap(XenIOState *state, hwaddr phys_offset = memory_region_get_ram_addr(mr); const char *mr_name; - if (get_physmapping(state, start_addr, size)) { + if (get_physmapping(start_addr, size)) { return 0; } if (size <= 0) { @@ -384,7 +382,7 @@ go_physmap: physmap->name = mr_name; physmap->phys_offset = phys_offset; - QLIST_INSERT_HEAD(&state->physmap, physmap, list); + QLIST_INSERT_HEAD(&xen_physmap, physmap, list); if (runstate_check(RUN_STATE_INMIGRATE)) { /* Now when we have a physmap entry we can replace a dummy mapping with @@ -428,7 +426,7 @@ static int xen_remove_from_physmap(XenIOState *state, XenPhysmap *physmap = NULL; hwaddr phys_offset = 0; - physmap = get_physmapping(state, start_addr, size); + physmap = get_physmapping(start_addr, size); if (physmap == NULL) { return -1; } @@ -597,7 +595,7 @@ static void xen_sync_dirty_bitmap(XenIOState *state, int rc, i, j; const XenPhysmap *physmap = NULL; - physmap = get_physmapping(state, start_addr, size); + physmap = get_physmapping(start_addr, size); if (physmap == NULL) { /* not handled */ return; @@ -1222,7 +1220,7 @@ static void xen_read_physmap(XenIOState *state) xen_domid, entries[i]); physmap->name = xs_read(state->xenstore, 0, path, &len); - QLIST_INSERT_HEAD(&state->physmap, physmap, list); + QLIST_INSERT_HEAD(&xen_physmap, physmap, list); } free(entries); } @@ -1374,7 +1372,6 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); state->memory_listener = xen_memory_listener; - QLIST_INIT(&state->physmap); memory_listener_register(&state->memory_listener, &address_space_memory); state->log_for_dirtybit = NULL; @@ -1390,6 +1387,8 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } xen_be_register_common(); + + QLIST_INIT(&xen_physmap); xen_read_physmap(state); /* Disable ACPI build because Xen handles it */ @@ -1461,6 +1460,8 @@ void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) int rc; ram_addr_t start_pfn, nb_pages; + start = xen_phys_offset_to_gaddr(start, length); + if (length == 0) { length = TARGET_PAGE_SIZE; } diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index efa35dc6e0..12fd932284 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -319,7 +319,7 @@ tryagain: mapcache->last_entry = NULL; #ifdef XEN_COMPAT_PHYSMAP if (!translated && mapcache->phys_offset_to_gaddr) { - phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size, mapcache->opaque); + phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size); translated = true; goto tryagain; } diff --git a/include/sysemu/xen-mapcache.h b/include/sysemu/xen-mapcache.h index bd4d49e0a4..a03e2f1878 100644 --- a/include/sysemu/xen-mapcache.h +++ b/include/sysemu/xen-mapcache.h @@ -9,9 +9,8 @@ #ifndef XEN_MAPCACHE_H #define XEN_MAPCACHE_H -typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr start_addr, - ram_addr_t size, - void *opaque); +typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, + ram_addr_t size); #ifdef CONFIG_XEN void xen_map_cache_init(phys_offset_to_gaddr_t f, From d3c49ebbe26b48615e14b8baa88a59cd33761ea6 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Tue, 15 May 2018 17:40:53 +0100 Subject: [PATCH 0880/2380] xen-hvm: try to use xenforeignmemory_map_resource() to map ioreq pages Xen 4.11 has a new API to directly map guest resources. Among the resources that can be mapped using this API are ioreq pages. This patch modifies QEMU to attempt to use the new API should it exist, falling back to the previous mechanism if it is unavailable. Signed-off-by: Paul Durrant Reviewed-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- configure | 5 +++ hw/i386/xen/trace-events | 1 + hw/i386/xen/xen-hvm.c | 70 ++++++++++++++++++++++++++++--------- include/hw/xen/xen_common.h | 16 +++++++++ 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/configure b/configure index a6a4616c3e..be6edc7b81 100755 --- a/configure +++ b/configure @@ -2231,12 +2231,17 @@ EOF #undef XC_WANT_COMPAT_DEVICEMODEL_API #define __XEN_TOOLS__ #include +#include int main(void) { xendevicemodel_handle *xd; + xenforeignmemory_handle *xfmem; xd = xendevicemodel_open(0, 0); xendevicemodel_pin_memory_cacheattr(xd, 0, 0, 0, 0); + xfmem = xenforeignmemory_open(0, 0); + xenforeignmemory_map_resource(xfmem, 0, 0, 0, 0, 0, NULL, 0, 0); + return 0; } EOF diff --git a/hw/i386/xen/trace-events b/hw/i386/xen/trace-events index 8dab7bcfe0..38616b698f 100644 --- a/hw/i386/xen/trace-events +++ b/hw/i386/xen/trace-events @@ -15,6 +15,7 @@ cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64 cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p" # xen-mapcache.c xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64 diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 2afab6573b..54f99abfea 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -1237,13 +1237,39 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) static int xen_map_ioreq_server(XenIOState *state) { + void *addr = NULL; + xenforeignmemory_resource_handle *fres; xen_pfn_t ioreq_pfn; xen_pfn_t bufioreq_pfn; evtchn_port_t bufioreq_evtchn; int rc; + /* + * Attempt to map using the resource API and fall back to normal + * foreign mapping if this is not supported. + */ + QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_bufioreq != 0); + QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_ioreq(0) != 1); + fres = xenforeignmemory_map_resource(xen_fmem, xen_domid, + XENMEM_resource_ioreq_server, + state->ioservid, 0, 2, + &addr, + PROT_READ | PROT_WRITE, 0); + if (fres != NULL) { + trace_xen_map_resource_ioreq(state->ioservid, addr); + state->buffered_io_page = addr; + state->shared_page = addr + TARGET_PAGE_SIZE; + } else if (errno != EOPNOTSUPP) { + error_report("failed to map ioreq server resources: error %d handle=%p", + errno, xen_xc); + return -1; + } + rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, - &ioreq_pfn, &bufioreq_pfn, + (state->shared_page == NULL) ? + &ioreq_pfn : NULL, + (state->buffered_io_page == NULL) ? + &bufioreq_pfn : NULL, &bufioreq_evtchn); if (rc < 0) { error_report("failed to get ioreq server info: error %d handle=%p", @@ -1251,26 +1277,36 @@ static int xen_map_ioreq_server(XenIOState *state) return rc; } - DPRINTF("shared page at pfn %lx\n", ioreq_pfn); - DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); - DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); - - state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ | PROT_WRITE, - 1, &ioreq_pfn, NULL); if (state->shared_page == NULL) { - error_report("map shared IO page returned error %d handle=%p", - errno, xen_xc); + DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + + state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &ioreq_pfn, NULL); + if (state->shared_page == NULL) { + error_report("map shared IO page returned error %d handle=%p", + errno, xen_xc); + } + } + + if (state->buffered_io_page == NULL) { + DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); + + state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &bufioreq_pfn, + NULL); + if (state->buffered_io_page == NULL) { + error_report("map buffered IO page returned error %d", errno); + return -1; + } + } + + if (state->shared_page == NULL || state->buffered_io_page == NULL) { return -1; } - state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ | PROT_WRITE, - 1, &bufioreq_pfn, NULL); - if (state->buffered_io_page == NULL) { - error_report("map buffered IO page returned error %d", errno); - return -1; - } + DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); state->bufioreq_remote_port = bufioreq_evtchn; diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index bbf207dcef..93f631e5bf 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -119,6 +119,22 @@ static inline int xendevicemodel_pin_memory_cacheattr( return xc_domain_pin_memory_cacheattr(xen_xc, domid, start, end, type); } +typedef void xenforeignmemory_resource_handle; + +#define XENMEM_resource_ioreq_server 0 + +#define XENMEM_resource_ioreq_server_frame_bufioreq 0 +#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n)) + +static inline xenforeignmemory_resource_handle *xenforeignmemory_map_resource( + xenforeignmemory_handle *fmem, domid_t domid, unsigned int type, + unsigned int id, unsigned long frame, unsigned long nr_frames, + void **paddr, int prot, int flags) +{ + errno = EOPNOTSUPP; + return NULL; +} + #endif /* CONFIG_XEN_CTRL_INTERFACE_VERSION < 41100 */ #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41000 From dfb6578d69d60e464be36dafed9741dcfd73d2cf Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Thu, 31 May 2018 11:01:13 -0700 Subject: [PATCH 0881/2380] xen-hvm: stop faking I/O to access PCI config space This patch removes the current hackery where IOREQ_TYPE_PCI_CONFIG requests are handled by faking PIO to 0xcf8 and 0xcfc and replaces it with direct calls to pci_host_config_read/write_common(). Doing so necessitates mapping BDFs to PCIDevices but maintaining a simple QLIST in xen_device_realize/unrealize() will suffice. NOTE: whilst config space accesses are currently limited to PCI_CONFIG_SPACE_SIZE, this patch paves the way to increasing the limit to PCIE_CONFIG_SPACE_SIZE when Xen gains the ability to emulate MCFG table accesses. Signed-off-by: Paul Durrant Reviewed-by: Anthony PERARD Signed-off-by: Stefano Stabellini --- hw/i386/xen/trace-events | 2 + hw/i386/xen/xen-hvm.c | 102 +++++++++++++++++++++++++++++++-------- 2 files changed, 84 insertions(+), 20 deletions(-) diff --git a/hw/i386/xen/trace-events b/hw/i386/xen/trace-events index 38616b698f..8a9077cd4e 100644 --- a/hw/i386/xen/trace-events +++ b/hw/i386/xen/trace-events @@ -16,6 +16,8 @@ cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) " cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p" +cpu_ioreq_config_read(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" +cpu_ioreq_config_write(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" # xen-mapcache.c xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64 diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 54f99abfea..935a3676c8 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -12,6 +12,7 @@ #include "cpu.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" #include "hw/i386/pc.h" #include "hw/i386/apic-msidef.h" #include "hw/xen/xen_common.h" @@ -88,6 +89,12 @@ typedef struct XenPhysmap { static QLIST_HEAD(, XenPhysmap) xen_physmap; +typedef struct XenPciDevice { + PCIDevice *pci_dev; + uint32_t sbdf; + QLIST_ENTRY(XenPciDevice) entry; +} XenPciDevice; + typedef struct XenIOState { ioservid_t ioservid; shared_iopage_t *shared_page; @@ -108,6 +115,7 @@ typedef struct XenIOState { struct xs_handle *xenstore; MemoryListener memory_listener; MemoryListener io_listener; + QLIST_HEAD(, XenPciDevice) dev_list; DeviceListener device_listener; hwaddr free_phys_offset; const XenPhysmap *log_for_dirtybit; @@ -568,6 +576,12 @@ static void xen_device_realize(DeviceListener *listener, if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { PCIDevice *pci_dev = PCI_DEVICE(dev); + XenPciDevice *xendev = g_new(XenPciDevice, 1); + + xendev->pci_dev = pci_dev; + xendev->sbdf = PCI_BUILD_BDF(pci_dev_bus_num(pci_dev), + pci_dev->devfn); + QLIST_INSERT_HEAD(&state->dev_list, xendev, entry); xen_map_pcidev(xen_domid, state->ioservid, pci_dev); } @@ -580,8 +594,17 @@ static void xen_device_unrealize(DeviceListener *listener, if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { PCIDevice *pci_dev = PCI_DEVICE(dev); + XenPciDevice *xendev, *next; xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev); + + QLIST_FOREACH_SAFE(xendev, &state->dev_list, entry, next) { + if (xendev->pci_dev == pci_dev) { + QLIST_REMOVE(xendev, entry); + g_free(xendev); + break; + } + } } } @@ -902,6 +925,62 @@ static void cpu_ioreq_move(ioreq_t *req) } } +static void cpu_ioreq_config(XenIOState *state, ioreq_t *req) +{ + uint32_t sbdf = req->addr >> 32; + uint32_t reg = req->addr; + XenPciDevice *xendev; + + if (req->size != sizeof(uint8_t) && req->size != sizeof(uint16_t) && + req->size != sizeof(uint32_t)) { + hw_error("PCI config access: bad size (%u)", req->size); + } + + if (req->count != 1) { + hw_error("PCI config access: bad count (%u)", req->count); + } + + QLIST_FOREACH(xendev, &state->dev_list, entry) { + if (xendev->sbdf != sbdf) { + continue; + } + + if (!req->data_is_ptr) { + if (req->dir == IOREQ_READ) { + req->data = pci_host_config_read_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->size); + trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, + req->size, req->data); + } else if (req->dir == IOREQ_WRITE) { + trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, + req->size, req->data); + pci_host_config_write_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->data, req->size); + } + } else { + uint32_t tmp; + + if (req->dir == IOREQ_READ) { + tmp = pci_host_config_read_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->size); + trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, + req->size, tmp); + write_phys_req_item(req->data, req, 0, &tmp); + } else if (req->dir == IOREQ_WRITE) { + read_phys_req_item(req->data, req, 0, &tmp); + trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, + req->size, tmp); + pci_host_config_write_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + tmp, req->size); + } + } + } +} + static void regs_to_cpu(vmware_regs_t *vmport_regs, ioreq_t *req) { X86CPU *cpu; @@ -974,27 +1053,9 @@ static void handle_ioreq(XenIOState *state, ioreq_t *req) case IOREQ_TYPE_INVALIDATE: xen_invalidate_map_cache(); break; - case IOREQ_TYPE_PCI_CONFIG: { - uint32_t sbdf = req->addr >> 32; - uint32_t val; - - /* Fake a write to port 0xCF8 so that - * the config space access will target the - * correct device model. - */ - val = (1u << 31) | - ((req->addr & 0x0f00) << 16) | - ((sbdf & 0xffff) << 8) | - (req->addr & 0xfc); - do_outp(0xcf8, 4, val); - - /* Now issue the config space access via - * port 0xCFC - */ - req->addr = 0xcfc | (req->addr & 0x03); - cpu_ioreq_pio(req); + case IOREQ_TYPE_PCI_CONFIG: + cpu_ioreq_config(state, req); break; - } default: hw_error("Invalid ioreq type 0x%x\n", req->type); } @@ -1415,6 +1476,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) memory_listener_register(&state->io_listener, &address_space_io); state->device_listener = xen_device_listener; + QLIST_INIT(&state->dev_list); device_listener_register(&state->device_listener); /* Initialize backend core & drivers */ From f18d137542a48150d177031fc6c54786179bf2a3 Mon Sep 17 00:00:00 2001 From: Benjamin Drung Date: Tue, 27 Feb 2018 17:06:01 +0100 Subject: [PATCH 0882/2380] slirp: Add domainname option to slirp's DHCP server This patch will allow the user to include the domainname option in replies from the built-in DHCP server. Signed-off-by: Benjamin Drung Signed-off-by: Samuel Thibault --- net/slirp.c | 12 +++++++++--- qapi/net.json | 4 ++++ qemu-options.hx | 7 +++++-- slirp/bootp.c | 8 ++++++++ slirp/libslirp.h | 2 +- slirp/slirp.c | 4 +++- slirp/slirp.h | 1 + 7 files changed, 31 insertions(+), 7 deletions(-) diff --git a/net/slirp.c b/net/slirp.c index 692252445a..005c2675e6 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -157,7 +157,8 @@ static int net_slirp_init(NetClientState *peer, const char *model, const char *bootfile, const char *vdhcp_start, const char *vnameserver, const char *vnameserver6, const char *smb_export, const char *vsmbserver, - const char **dnssearch, Error **errp) + const char **dnssearch, const char *vdomainname, + Error **errp) { /* default settings according to historic slirp */ struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ @@ -359,6 +360,11 @@ static int net_slirp_init(NetClientState *peer, const char *model, ip6_dns.s6_addr[15] |= 3; } + if (vdomainname && !*vdomainname) { + error_setg(errp, "'domainname' parameter cannot be empty"); + return -1; + } + nc = qemu_new_net_client(&net_slirp_info, peer, model, name); @@ -371,7 +377,7 @@ static int net_slirp_init(NetClientState *peer, const char *model, s->slirp = slirp_init(restricted, ipv4, net, mask, host, ipv6, ip6_prefix, vprefix6_len, ip6_host, vhostname, tftp_export, bootfile, dhcp, - dns, ip6_dns, dnssearch, s); + dns, ip6_dns, dnssearch, vdomainname, s); QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry); for (config = slirp_configs; config; config = config->next) { @@ -958,7 +964,7 @@ int net_init_slirp(const Netdev *netdev, const char *name, user->ipv6_host, user->hostname, user->tftp, user->bootfile, user->dhcpstart, user->dns, user->ipv6_dns, user->smb, - user->smbserver, dnssearch, errp); + user->smbserver, dnssearch, user->domainname, errp); while (slirp_configs) { config = slirp_configs; diff --git a/qapi/net.json b/qapi/net.json index 5c1dc48890..32681a1af7 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -160,6 +160,9 @@ # @dnssearch: list of DNS suffixes to search, passed as DHCP option # to the guest # +# @domainname: guest-visible domain name of the virtual nameserver +# (since 2.12) +# # @ipv6-prefix: IPv6 network prefix (default is fec0::) (since # 2.6). The network prefix is given in the usual # hexadecimal IPv6 address notation. @@ -197,6 +200,7 @@ '*dhcpstart': 'str', '*dns': 'str', '*dnssearch': ['String'], + '*domainname': 'str', '*ipv6-prefix': 'str', '*ipv6-prefixlen': 'int', '*ipv6-host': 'str', diff --git a/qemu-options.hx b/qemu-options.hx index 2f61ea42ee..c0d3951e9f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1906,8 +1906,8 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, "-netdev user,id=str[,ipv4[=on|off]][,net=addr[/mask]][,host=addr]\n" " [,ipv6[=on|off]][,ipv6-net=addr[/int]][,ipv6-host=addr]\n" " [,restrict=on|off][,hostname=host][,dhcpstart=addr]\n" - " [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,tftp=dir]\n" - " [,bootfile=f][,hostfwd=rule][,guestfwd=rule]" + " [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,domainname=domain]\n" + " [,tftp=dir][,bootfile=f][,hostfwd=rule][,guestfwd=rule]" #ifndef _WIN32 "[,smb=dir[,smbserver=addr]]\n" #endif @@ -2135,6 +2135,9 @@ Example: qemu-system-i386 -nic user,dnssearch=mgmt.example.org,dnssearch=example.org @end example +@item domainname=@var{domain} +Specifies the client domain name reported by the built-in DHCP server. + @item tftp=@var{dir} When using the user mode network stack, activate a built-in TFTP server. The files in @var{dir} will be exposed as the root of a TFTP server. diff --git a/slirp/bootp.c b/slirp/bootp.c index 5dd1a415b5..9e7b53ba94 100644 --- a/slirp/bootp.c +++ b/slirp/bootp.c @@ -298,6 +298,14 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) q += val; } + if (slirp->vdomainname) { + val = strlen(slirp->vdomainname); + *q++ = RFC1533_DOMAINNAME; + *q++ = val; + memcpy(q, slirp->vdomainname, val); + q += val; + } + if (slirp->vdnssearch) { size_t spaceleft = sizeof(rbp->bp_vend) - (q - rbp->bp_vend); val = slirp->vdnssearch_len; diff --git a/slirp/libslirp.h b/slirp/libslirp.h index 540b3e5903..740408a96e 100644 --- a/slirp/libslirp.h +++ b/slirp/libslirp.h @@ -16,7 +16,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, const char *tftp_path, const char *bootfile, struct in_addr vdhcp_start, struct in_addr vnameserver, struct in6_addr vnameserver6, const char **vdnssearch, - void *opaque); + const char *vdomainname, void *opaque); void slirp_cleanup(Slirp *slirp); void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout); diff --git a/slirp/slirp.c b/slirp/slirp.c index 1cb6b07004..4f29753444 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -286,7 +286,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, const char *tftp_path, const char *bootfile, struct in_addr vdhcp_start, struct in_addr vnameserver, struct in6_addr vnameserver6, const char **vdnssearch, - void *opaque) + const char *vdomainname, void *opaque) { Slirp *slirp = g_malloc0(sizeof(Slirp)); @@ -317,6 +317,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, } slirp->tftp_prefix = g_strdup(tftp_path); slirp->bootp_filename = g_strdup(bootfile); + slirp->vdomainname = g_strdup(vdomainname); slirp->vdhcp_startaddr = vdhcp_start; slirp->vnameserver_addr = vnameserver; slirp->vnameserver_addr6 = vnameserver6; @@ -349,6 +350,7 @@ void slirp_cleanup(Slirp *slirp) g_free(slirp->vdnssearch); g_free(slirp->tftp_prefix); g_free(slirp->bootp_filename); + g_free(slirp->vdomainname); g_free(slirp); } diff --git a/slirp/slirp.h b/slirp/slirp.h index 06febfc78b..10b410898a 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -193,6 +193,7 @@ struct Slirp { char *bootp_filename; size_t vdnssearch_len; uint8_t *vdnssearch; + char *vdomainname; /* tcp states */ struct socket tcb; From 058665b9fe8b00c799b3db5d9202b007fab1c2fb Mon Sep 17 00:00:00 2001 From: Andreas Gustafsson Date: Wed, 7 Mar 2018 23:26:15 +0100 Subject: [PATCH 0883/2380] slirp: disable Nagle in outgoing connections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When setting up an outgoing user mode networking TCP connection, disable the Nagle algorithm in the host-side connection. Either the guest is already doing Nagle, in which case there is no point in doing it twice, or it has chosen to disable it, in which case we should respect that choice. This change speeds up GDB remote debugging over TCP over user mode networking (with GDB runing on the guest) by multiple orders of magnitude, and has been part of the local patches applied by pkgsrc since 2012 with no reported ill effects. Signed-off-by: Andreas Gustafsson Reviewed-by: Kamil Rytarowski Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Samuel Thibault --- slirp/tcp_subr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index da0d53743f..8d0f94b75f 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -416,6 +416,8 @@ int tcp_fconnect(struct socket *so, unsigned short af) socket_set_fast_reuse(s); opt = 1; qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); + opt = 1; + qemu_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); addr = so->fhost.ss; DEBUG_CALL(" connect()ing") From 05658ecb5582d7dab9e275a33db698e44413d4a7 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Wed, 7 Mar 2018 23:29:41 +0100 Subject: [PATCH 0884/2380] slirp: disable Nagle in ingoing connections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This follows 3929766fb3e4 ('slirp: disable Nagle in outgoing connections'): for the same reasons, ingoing connections should have the Nagle algorithm disabled. Signed-off-by: Samuel Thibault Reviewed-by: Philippe Mathieu-Daudé --- slirp/socket.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slirp/socket.c b/slirp/socket.c index 61347d1a0c..6f18e157a5 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -754,6 +754,8 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, return NULL; } qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); + opt = 1; + qemu_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)); getsockname(s,(struct sockaddr *)&addr,&addrlen); so->so_ffamily = AF_INET; From 8ee2022b44e5bbc5360513957790c656824eec55 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 13 Mar 2018 15:49:44 +1100 Subject: [PATCH 0885/2380] slirp/debug: Print IP addresses in human readable form Signed-off-by: Alexey Kardashevskiy Signed-off-by: Samuel Thibault --- slirp/arp_table.c | 4 ++-- slirp/socket.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/slirp/arp_table.c b/slirp/arp_table.c index bac608f97f..f81963bb88 100644 --- a/slirp/arp_table.c +++ b/slirp/arp_table.c @@ -33,7 +33,7 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]) int i; DEBUG_CALL("arp_table_add"); - DEBUG_ARG("ip = %s", inet_ntoa(*(struct in_addr *)&ip_addr)); + DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){.s_addr = ip_addr})); DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", ethaddr[0], ethaddr[1], ethaddr[2], ethaddr[3], ethaddr[4], ethaddr[5])); @@ -67,7 +67,7 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr, int i; DEBUG_CALL("arp_table_search"); - DEBUG_ARG("ip = %s", inet_ntoa(*(struct in_addr *)&ip_addr)); + DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){.s_addr = ip_addr})); /* If broadcast address */ if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) { diff --git a/slirp/socket.c b/slirp/socket.c index 6f18e157a5..e2a71c9b04 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -701,9 +701,9 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, memset(&addr, 0, addrlen); DEBUG_CALL("tcp_listen"); - DEBUG_ARG("haddr = %s", inet_ntoa(*(struct in_addr *)&haddr)); + DEBUG_ARG("haddr = %s", inet_ntoa((struct in_addr){.s_addr = haddr})); DEBUG_ARG("hport = %d", ntohs(hport)); - DEBUG_ARG("laddr = %s", inet_ntoa(*(struct in_addr *)&laddr)); + DEBUG_ARG("laddr = %s", inet_ntoa((struct in_addr){.s_addr = laddr})); DEBUG_ARG("lport = %d", ntohs(lport)); DEBUG_ARG("flags = %x", flags); From 1fb3f7f285606c3af2b58cac52f47e9927a5ebea Mon Sep 17 00:00:00 2001 From: Nia Alarie Date: Fri, 16 Mar 2018 14:39:21 +0000 Subject: [PATCH 0886/2380] net/slirp: Convert atoi to qemu_strtoi to allow error checking Signed-off-by: Nia Alarie Signed-off-by: Samuel Thibault --- net/slirp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/slirp.c b/net/slirp.c index 005c2675e6..1e14318b4d 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -492,7 +492,9 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) goto fail_syntax; } - host_port = atoi(p); + if (qemu_strtoi(p, NULL, 10, &host_port)) { + goto fail_syntax; + } err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port); From b0060110aaaf3d2b7a0d2fc938e87848d188837c Mon Sep 17 00:00:00 2001 From: James Clarke Date: Tue, 17 Apr 2018 15:10:58 +0100 Subject: [PATCH 0887/2380] slirp: Send window updates to guest after window was closed If the receive window presented to the guest closes, slirp should send a window update once the window reopens sufficiently, rather than forcing the guest to send a window probe, which can take several seconds. Signed-off-by: James Clarke Signed-off-by: Samuel Thibault --- slirp/slirp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/slirp/slirp.c b/slirp/slirp.c index 4f29753444..5c3bd6163f 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -678,13 +678,13 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error) /* continue; */ } else { ret = sowrite(so); + if (ret > 0) { + /* Call tcp_output in case we need to send a window + * update to the guest, otherwise it will be stuck + * until it sends a window probe. */ + tcp_output(sototcpcb(so)); + } } - /* - * XXXXX If we wrote something (a lot), there - * could be a need for a window update. - * In the worst case, the remote will send - * a window probe to get things going again - */ } /* From 551f84544edf80ae474255deac12a2f936379a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 30 May 2018 08:10:33 +0200 Subject: [PATCH 0888/2380] slirp/ncsi: fix "Get Version ID" payload length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Tested-by: Joel Stanley Signed-off-by: Samuel Thibault --- slirp/ncsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slirp/ncsi.c b/slirp/ncsi.c index d12ba3e494..02d0e9def3 100644 --- a/slirp/ncsi.c +++ b/slirp/ncsi.c @@ -60,7 +60,7 @@ static const struct ncsi_rsp_handler { { NCSI_PKT_RSP_EGMF, 4, NULL }, { NCSI_PKT_RSP_DGMF, 4, NULL }, { NCSI_PKT_RSP_SNFC, 4, NULL }, - { NCSI_PKT_RSP_GVI, 36, NULL }, + { NCSI_PKT_RSP_GVI, 40, NULL }, { NCSI_PKT_RSP_GC, 32, ncsi_rsp_handler_gc }, { NCSI_PKT_RSP_GP, -1, NULL }, { NCSI_PKT_RSP_GCPS, 172, NULL }, From b2d1678fa1733144826f582d28020da39372649f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 30 May 2018 08:10:34 +0200 Subject: [PATCH 0889/2380] slirp/ncsi: add a "Get Parameters" response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Command 0x17 'Get Parameters' is used to get configuration parameter values currently in effect on the controller and it is mandatory in the NS-CI specification. Provide a minimum response to exercise the kernel. Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Tested-by: Joel Stanley Signed-off-by: Samuel Thibault --- slirp/ncsi.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/slirp/ncsi.c b/slirp/ncsi.c index 02d0e9def3..7b3fff207a 100644 --- a/slirp/ncsi.c +++ b/slirp/ncsi.c @@ -35,6 +35,20 @@ static int ncsi_rsp_handler_gls(struct ncsi_rsp_pkt_hdr *rnh) return 0; } +/* Get Parameters */ +static int ncsi_rsp_handler_gp(struct ncsi_rsp_pkt_hdr *rnh) +{ + struct ncsi_rsp_gp_pkt *rsp = (struct ncsi_rsp_gp_pkt *) rnh; + + /* no MAC address filters or VLAN filters on the channel */ + rsp->mac_cnt = 0; + rsp->mac_enable = 0; + rsp->vlan_cnt = 0; + rsp->vlan_enable = 0; + + return 0; +} + static const struct ncsi_rsp_handler { unsigned char type; int payload; @@ -62,7 +76,7 @@ static const struct ncsi_rsp_handler { { NCSI_PKT_RSP_SNFC, 4, NULL }, { NCSI_PKT_RSP_GVI, 40, NULL }, { NCSI_PKT_RSP_GC, 32, ncsi_rsp_handler_gc }, - { NCSI_PKT_RSP_GP, -1, NULL }, + { NCSI_PKT_RSP_GP, 40, ncsi_rsp_handler_gp }, { NCSI_PKT_RSP_GCPS, 172, NULL }, { NCSI_PKT_RSP_GNS, 172, NULL }, { NCSI_PKT_RSP_GNPTS, 172, NULL }, From 47335eeea8f1d14b7c6a1dd585a25a9166721168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 30 May 2018 08:10:35 +0200 Subject: [PATCH 0890/2380] slirp/ncsi: add checksum support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The checksum field of a NC-SI packet contains a value that may be included in each command and response. The verification is optional but the Linux driver does so when a non-zero value is provided. Let's extend the model to compute the checksum value and exercise a little more the Linux driver. See section "8.2.2.3 - 2's Complement Checksum Compensation" in the Network Controller Sideband Interface (NC-SI) Specification for more details. Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Tested-by: Joel Stanley Signed-off-by: Samuel Thibault --- slirp/ncsi.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/slirp/ncsi.c b/slirp/ncsi.c index 7b3fff207a..7116034afc 100644 --- a/slirp/ncsi.c +++ b/slirp/ncsi.c @@ -1,7 +1,7 @@ /* * NC-SI (Network Controller Sideband Interface) "echo" model * - * Copyright (C) 2016 IBM Corp. + * Copyright (C) 2016-2018 IBM Corp. * * This code is licensed under the GPL version 2 or later. See the * COPYING file in the top-level directory. @@ -11,6 +11,23 @@ #include "ncsi-pkt.h" +static uint32_t ncsi_calculate_checksum(uint16_t *data, int len) +{ + uint32_t checksum = 0; + int i; + + /* + * 32-bit unsigned sum of the NC-SI packet header and NC-SI packet + * payload interpreted as a series of 16-bit unsigned integer values. + */ + for (i = 0; i < len; i++) { + checksum += htons(data[i]); + } + + checksum = (~checksum + 1); + return checksum; +} + /* Get Capabilities */ static int ncsi_rsp_handler_gc(struct ncsi_rsp_pkt_hdr *rnh) { @@ -101,6 +118,9 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) (ncsi_reply + ETH_HLEN); const struct ncsi_rsp_handler *handler = NULL; int i; + int ncsi_rsp_len = sizeof(*nh); + uint32_t checksum; + uint32_t *pchecksum; memset(ncsi_reply, 0, sizeof(ncsi_reply)); @@ -130,15 +150,18 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) /* TODO: handle errors */ handler->handler(rnh); } + ncsi_rsp_len += handler->payload; } else { rnh->common.length = 0; rnh->code = htons(NCSI_PKT_RSP_C_UNAVAILABLE); rnh->reason = htons(NCSI_PKT_RSP_R_UNKNOWN); } - /* TODO: add a checksum at the end of the frame but the specs - * allows it to be zero */ + /* Add the optional checksum at the end of the frame. */ + checksum = ncsi_calculate_checksum((uint16_t *) rnh, ncsi_rsp_len); + pchecksum = (uint32_t *)((void *) rnh + ncsi_rsp_len); + *pchecksum = htonl(checksum); + ncsi_rsp_len += 4; - slirp_output(slirp->opaque, ncsi_reply, ETH_HLEN + sizeof(*nh) + - (handler ? handler->payload : 0) + 4); + slirp_output(slirp->opaque, ncsi_reply, ETH_HLEN + ncsi_rsp_len); } From 9d0fdecbad130f01b602e35e87c6d3fad5821d6e Mon Sep 17 00:00:00 2001 From: Yi Min Zhao Date: Thu, 31 May 2018 11:29:37 +0800 Subject: [PATCH 0891/2380] sandbox: disable -sandbox if CONFIG_SECCOMP undefined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_SECCOMP is undefined, the option 'elevatedprivileges' remains compiled. This would make libvirt set the corresponding capability and then trigger failure during guest startup. This patch moves the code regarding seccomp command line options to qemu-seccomp.c file and wraps qemu_opts_foreach finding sandbox option with CONFIG_SECCOMP. Because parse_sandbox() is moved into qemu-seccomp.c file, change seccomp_start() to static function. Signed-off-by: Yi Min Zhao Reviewed-by: Ján Tomko Tested-by: Ján Tomko Acked-by: Eduardo Otubo --- include/sysemu/seccomp.h | 3 +- qemu-seccomp.c | 121 +++++++++++++++++++++++++++++++++++++- vl.c | 124 +++------------------------------------ 3 files changed, 130 insertions(+), 118 deletions(-) diff --git a/include/sysemu/seccomp.h b/include/sysemu/seccomp.h index 9b092aa23f..fe859894f6 100644 --- a/include/sysemu/seccomp.h +++ b/include/sysemu/seccomp.h @@ -21,5 +21,6 @@ #define QEMU_SECCOMP_SET_SPAWN (1 << 3) #define QEMU_SECCOMP_SET_RESOURCECTL (1 << 4) -int seccomp_start(uint32_t seccomp_opts); +int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp); + #endif diff --git a/qemu-seccomp.c b/qemu-seccomp.c index b770a77d33..148e4c6f24 100644 --- a/qemu-seccomp.c +++ b/qemu-seccomp.c @@ -13,6 +13,11 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu/osdep.h" +#include "qemu/config-file.h" +#include "qemu/option.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include #include #include "sysemu/seccomp.h" @@ -96,7 +101,7 @@ static const struct QemuSeccompSyscall blacklist[] = { }; -int seccomp_start(uint32_t seccomp_opts) +static int seccomp_start(uint32_t seccomp_opts) { int rc = 0; unsigned int i = 0; @@ -125,3 +130,117 @@ int seccomp_start(uint32_t seccomp_opts) seccomp_release(ctx); return rc; } + +#ifdef CONFIG_SECCOMP +int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) +{ + if (qemu_opt_get_bool(opts, "enable", false)) { + uint32_t seccomp_opts = QEMU_SECCOMP_SET_DEFAULT + | QEMU_SECCOMP_SET_OBSOLETE; + const char *value = NULL; + + value = qemu_opt_get(opts, "obsolete"); + if (value) { + if (g_str_equal(value, "allow")) { + seccomp_opts &= ~QEMU_SECCOMP_SET_OBSOLETE; + } else if (g_str_equal(value, "deny")) { + /* this is the default option, this if is here + * to provide a little bit of consistency for + * the command line */ + } else { + error_report("invalid argument for obsolete"); + return -1; + } + } + + value = qemu_opt_get(opts, "elevateprivileges"); + if (value) { + if (g_str_equal(value, "deny")) { + seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; + } else if (g_str_equal(value, "children")) { + seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; + + /* calling prctl directly because we're + * not sure if host has CAP_SYS_ADMIN set*/ + if (prctl(PR_SET_NO_NEW_PRIVS, 1)) { + error_report("failed to set no_new_privs " + "aborting"); + return -1; + } + } else if (g_str_equal(value, "allow")) { + /* default value */ + } else { + error_report("invalid argument for elevateprivileges"); + return -1; + } + } + + value = qemu_opt_get(opts, "spawn"); + if (value) { + if (g_str_equal(value, "deny")) { + seccomp_opts |= QEMU_SECCOMP_SET_SPAWN; + } else if (g_str_equal(value, "allow")) { + /* default value */ + } else { + error_report("invalid argument for spawn"); + return -1; + } + } + + value = qemu_opt_get(opts, "resourcecontrol"); + if (value) { + if (g_str_equal(value, "deny")) { + seccomp_opts |= QEMU_SECCOMP_SET_RESOURCECTL; + } else if (g_str_equal(value, "allow")) { + /* default value */ + } else { + error_report("invalid argument for resourcecontrol"); + return -1; + } + } + + if (seccomp_start(seccomp_opts) < 0) { + error_report("failed to install seccomp syscall filter " + "in the kernel"); + return -1; + } + } + + return 0; +} + +static QemuOptsList qemu_sandbox_opts = { + .name = "sandbox", + .implied_opt_name = "enable", + .head = QTAILQ_HEAD_INITIALIZER(qemu_sandbox_opts.head), + .desc = { + { + .name = "enable", + .type = QEMU_OPT_BOOL, + }, + { + .name = "obsolete", + .type = QEMU_OPT_STRING, + }, + { + .name = "elevateprivileges", + .type = QEMU_OPT_STRING, + }, + { + .name = "spawn", + .type = QEMU_OPT_STRING, + }, + { + .name = "resourcecontrol", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +static void seccomp_register(void) +{ + qemu_add_opts(&qemu_sandbox_opts); +} +opts_init(seccomp_register); +#endif diff --git a/vl.c b/vl.c index c4fe25560c..70f090c823 100644 --- a/vl.c +++ b/vl.c @@ -28,11 +28,7 @@ #include "qemu/cutils.h" #include "qemu/help_option.h" #include "qemu/uuid.h" - -#ifdef CONFIG_SECCOMP -#include #include "sysemu/seccomp.h" -#endif #ifdef CONFIG_SDL #if defined(__APPLE__) || defined(main) @@ -259,35 +255,6 @@ static QemuOptsList qemu_rtc_opts = { }, }; -static QemuOptsList qemu_sandbox_opts = { - .name = "sandbox", - .implied_opt_name = "enable", - .head = QTAILQ_HEAD_INITIALIZER(qemu_sandbox_opts.head), - .desc = { - { - .name = "enable", - .type = QEMU_OPT_BOOL, - }, - { - .name = "obsolete", - .type = QEMU_OPT_STRING, - }, - { - .name = "elevateprivileges", - .type = QEMU_OPT_STRING, - }, - { - .name = "spawn", - .type = QEMU_OPT_STRING, - }, - { - .name = "resourcecontrol", - .type = QEMU_OPT_STRING, - }, - { /* end of list */ } - }, -}; - static QemuOptsList qemu_option_rom_opts = { .name = "option-rom", .implied_opt_name = "romfile", @@ -1050,88 +1017,6 @@ static int bt_parse(const char *opt) return 1; } -static int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) -{ - if (qemu_opt_get_bool(opts, "enable", false)) { -#ifdef CONFIG_SECCOMP - uint32_t seccomp_opts = QEMU_SECCOMP_SET_DEFAULT - | QEMU_SECCOMP_SET_OBSOLETE; - const char *value = NULL; - - value = qemu_opt_get(opts, "obsolete"); - if (value) { - if (g_str_equal(value, "allow")) { - seccomp_opts &= ~QEMU_SECCOMP_SET_OBSOLETE; - } else if (g_str_equal(value, "deny")) { - /* this is the default option, this if is here - * to provide a little bit of consistency for - * the command line */ - } else { - error_report("invalid argument for obsolete"); - return -1; - } - } - - value = qemu_opt_get(opts, "elevateprivileges"); - if (value) { - if (g_str_equal(value, "deny")) { - seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; - } else if (g_str_equal(value, "children")) { - seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; - - /* calling prctl directly because we're - * not sure if host has CAP_SYS_ADMIN set*/ - if (prctl(PR_SET_NO_NEW_PRIVS, 1)) { - error_report("failed to set no_new_privs " - "aborting"); - return -1; - } - } else if (g_str_equal(value, "allow")) { - /* default value */ - } else { - error_report("invalid argument for elevateprivileges"); - return -1; - } - } - - value = qemu_opt_get(opts, "spawn"); - if (value) { - if (g_str_equal(value, "deny")) { - seccomp_opts |= QEMU_SECCOMP_SET_SPAWN; - } else if (g_str_equal(value, "allow")) { - /* default value */ - } else { - error_report("invalid argument for spawn"); - return -1; - } - } - - value = qemu_opt_get(opts, "resourcecontrol"); - if (value) { - if (g_str_equal(value, "deny")) { - seccomp_opts |= QEMU_SECCOMP_SET_RESOURCECTL; - } else if (g_str_equal(value, "allow")) { - /* default value */ - } else { - error_report("invalid argument for resourcecontrol"); - return -1; - } - } - - if (seccomp_start(seccomp_opts) < 0) { - error_report("failed to install seccomp syscall filter " - "in the kernel"); - return -1; - } -#else - error_report("seccomp support is disabled"); - return -1; -#endif - } - - return 0; -} - static int parse_name(void *opaque, QemuOpts *opts, Error **errp) { const char *proc_name; @@ -3079,7 +2964,6 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_mem_opts); qemu_add_opts(&qemu_smp_opts); qemu_add_opts(&qemu_boot_opts); - qemu_add_opts(&qemu_sandbox_opts); qemu_add_opts(&qemu_add_fd_opts); qemu_add_opts(&qemu_object_opts); qemu_add_opts(&qemu_tpmdev_opts); @@ -3980,11 +3864,17 @@ int main(int argc, char **argv, char **envp) qtest_log = optarg; break; case QEMU_OPTION_sandbox: +#ifdef CONFIG_SECCOMP opts = qemu_opts_parse_noisily(qemu_find_opts("sandbox"), optarg, true); if (!opts) { exit(1); } +#else + error_report("-sandbox support is not enabled " + "in this QEMU binary"); + exit(1); +#endif break; case QEMU_OPTION_add_fd: #ifndef _WIN32 @@ -4077,10 +3967,12 @@ int main(int argc, char **argv, char **envp) exit(1); } +#ifdef CONFIG_SECCOMP if (qemu_opts_foreach(qemu_find_opts("sandbox"), parse_sandbox, NULL, NULL)) { exit(1); } +#endif if (qemu_opts_foreach(qemu_find_opts("name"), parse_name, NULL, NULL)) { From 05757c5d8753a600a9ef6f1f0509cd51ba3c91b4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 Jun 2018 14:15:01 +0200 Subject: [PATCH 0892/2380] bsd-user: include "exec/exec-all.h" which provides mmap_lock/unlock" Signed-off-by: Paolo Bonzini --- bsd-user/mmap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 20cd29d145..17f4cd80aa 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -21,6 +21,7 @@ #include "qemu.h" #include "qemu-common.h" #include "bsd-mman.h" +#include "exec/exec-all.h" //#define DEBUG_MMAP From 23c11b04dcc508536a7a39e723058523a94013a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:07 -0300 Subject: [PATCH 0893/2380] target: Do not include "exec/exec-all.h" if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code change produced with: $ git grep '#include "exec/exec-all.h"' | \ cut -d: -f-1 | \ xargs egrep -L "(cpu_address_space_init|cpu_loop_|tlb_|tb_|GETPC|singlestep|TranslationBlock)" | \ xargs sed -i.bak '/#include "exec\/exec-all.h"/d' Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-10-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- bsd-user/qemu.h | 1 - hw/i386/kvmvapic.c | 1 - target/arm/arm-powerctl.c | 1 - target/arm/arm_ldst.h | 1 - target/arm/crypto_helper.c | 1 - target/arm/iwmmxt_helper.c | 1 - target/arm/neon_helper.c | 1 - target/arm/psci.c | 1 - target/arm/vec_helper.c | 1 - target/cris/cpu.c | 1 - target/hppa/helper.c | 1 - target/hppa/int_helper.c | 1 - target/i386/hax-all.c | 1 - target/i386/hax-mem.c | 1 - target/i386/hax-windows.c | 1 - target/i386/hvf/hvf.c | 1 - target/i386/hvf/x86_task.c | 1 - target/i386/whpx-all.c | 1 - target/lm32/cpu.c | 1 - target/m68k/cpu.c | 1 - target/moxie/cpu.c | 1 - target/moxie/mmu.c | 1 - target/openrisc/cpu.c | 1 - target/ppc/int_helper.c | 1 - target/s390x/cpu.c | 1 - target/s390x/diag.c | 1 - target/s390x/helper.c | 1 - target/tilegx/cpu.c | 1 - target/xtensa/core-dc232b.c | 1 - target/xtensa/core-dc233c.c | 1 - target/xtensa/core-de212.c | 1 - target/xtensa/core-fsf.c | 1 - target/xtensa/core-sample_controller.c | 1 - target/xtensa/cpu.c | 1 - target/xtensa/import_core.sh | 1 - tcg/tcg-op-vec.c | 1 - 36 files changed, 36 deletions(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 19b2b8fecb..09e8aed9c7 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -19,7 +19,6 @@ #include "cpu.h" -#include "exec/exec-all.h" #include "exec/cpu_ldst.h" #undef DEBUG_REMAP diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c index fc962c5fbc..70f6f26a94 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "cpu.h" -#include "exec/exec-all.h" #include "sysemu/sysemu.h" #include "sysemu/cpus.h" #include "sysemu/hw_accel.h" diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index 25207cb850..ce55eeb682 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -15,7 +15,6 @@ #include "arm-powerctl.h" #include "qemu/log.h" #include "qemu/main-loop.h" -#include "exec/exec-all.h" #ifndef DEBUG_ARM_POWERCTL #define DEBUG_ARM_POWERCTL 0 diff --git a/target/arm/arm_ldst.h b/target/arm/arm_ldst.h index 01587b3ebb..5e0ac8bef0 100644 --- a/target/arm/arm_ldst.h +++ b/target/arm/arm_ldst.h @@ -20,7 +20,6 @@ #ifndef ARM_LDST_H #define ARM_LDST_H -#include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "qemu/bswap.h" diff --git a/target/arm/crypto_helper.c b/target/arm/crypto_helper.c index cc339ea7e0..f800266727 100644 --- a/target/arm/crypto_helper.c +++ b/target/arm/crypto_helper.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "crypto/aes.h" diff --git a/target/arm/iwmmxt_helper.c b/target/arm/iwmmxt_helper.c index 7d87e1a0a8..f6a4fc5b7f 100644 --- a/target/arm/iwmmxt_helper.c +++ b/target/arm/iwmmxt_helper.c @@ -22,7 +22,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" /* iwMMXt macros extracted from GNU gdb. */ diff --git a/target/arm/neon_helper.c b/target/arm/neon_helper.c index a1ec6537eb..c2c6491a83 100644 --- a/target/arm/neon_helper.c +++ b/target/arm/neon_helper.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/arm/psci.c b/target/arm/psci.c index eb7b88e926..a74d78802a 100644 --- a/target/arm/psci.c +++ b/target/arm/psci.c @@ -22,7 +22,6 @@ #include "sysemu/sysemu.h" #include "internals.h" #include "arm-powerctl.h" -#include "exec/exec-all.h" bool arm_is_psci_call(ARMCPU *cpu, int excp_type) { diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index ec705cfca5..25e209da31 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" diff --git a/target/cris/cpu.c b/target/cris/cpu.c index db8d0884a1..a23aba2688 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -26,7 +26,6 @@ #include "cpu.h" #include "qemu-common.h" #include "mmu.h" -#include "exec/exec-all.h" static void cris_cpu_set_pc(CPUState *cs, vaddr value) diff --git a/target/hppa/helper.c b/target/hppa/helper.c index 858ec205b6..6539061e52 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "fpu/softfloat.h" #include "exec/helper-proto.h" diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 561bf6eb60..8d5edd3a20 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -21,7 +21,6 @@ #include "qemu/main-loop.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qom/cpu.h" diff --git a/target/i386/hax-all.c b/target/i386/hax-all.c index c5856bbdc3..d2e512856b 100644 --- a/target/i386/hax-all.c +++ b/target/i386/hax-all.c @@ -26,7 +26,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/address-spaces.h" -#include "exec/exec-all.h" #include "qemu-common.h" #include "hax-i386.h" diff --git a/target/i386/hax-mem.c b/target/i386/hax-mem.c index f46e85544d..5c37e94caa 100644 --- a/target/i386/hax-mem.c +++ b/target/i386/hax-mem.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/address-spaces.h" -#include "exec/exec-all.h" #include "qemu/error-report.h" #include "target/i386/hax-i386.h" diff --git a/target/i386/hax-windows.c b/target/i386/hax-windows.c index b1ac737ae4..5729ad9b48 100644 --- a/target/i386/hax-windows.c +++ b/target/i386/hax-windows.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "hax-i386.h" /* diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index f6c872e678..df69e6d0a7 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -65,7 +65,6 @@ #include #include "exec/address-spaces.h" -#include "exec/exec-all.h" #include "hw/i386/apic_internal.h" #include "hw/boards.h" #include "qemu/main-loop.h" diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index 7610d85802..7099335e89 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -26,7 +26,6 @@ #include #include -#include "exec/exec-all.h" #include "hw/i386/apic_internal.h" #include "hw/boards.h" #include "qemu/main-loop.h" diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c index 58435178a4..bd7df10ba5 100644 --- a/target/i386/whpx-all.c +++ b/target/i386/whpx-all.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/address-spaces.h" -#include "exec/exec-all.h" #include "exec/ioport.h" #include "qemu-common.h" #include "strings.h" diff --git a/target/lm32/cpu.c b/target/lm32/cpu.c index 0003152469..b7499cb627 100644 --- a/target/lm32/cpu.c +++ b/target/lm32/cpu.c @@ -22,7 +22,6 @@ #include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" -#include "exec/exec-all.h" static void lm32_cpu_set_pc(CPUState *cs, vaddr value) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index a4ed8770aa..582e3a73b3 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -23,7 +23,6 @@ #include "cpu.h" #include "qemu-common.h" #include "migration/vmstate.h" -#include "exec/exec-all.h" #include "fpu/softfloat.h" static void m68k_cpu_set_pc(CPUState *cs, vaddr value) diff --git a/target/moxie/cpu.c b/target/moxie/cpu.c index 4170284da6..8d67eb6727 100644 --- a/target/moxie/cpu.c +++ b/target/moxie/cpu.c @@ -23,7 +23,6 @@ #include "qemu-common.h" #include "migration/vmstate.h" #include "machine.h" -#include "exec/exec-all.h" static void moxie_cpu_set_pc(CPUState *cs, vaddr value) { diff --git a/target/moxie/mmu.c b/target/moxie/mmu.c index 9203330b3b..bd90b1eebc 100644 --- a/target/moxie/mmu.c +++ b/target/moxie/mmu.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "mmu.h" -#include "exec/exec-all.h" int moxie_mmu_translate(MoxieMMUResult *res, CPUMoxieState *env, uint32_t vaddr, diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 20b115afae..a692a98ec0 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -21,7 +21,6 @@ #include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" -#include "exec/exec-all.h" static void openrisc_cpu_set_pc(CPUState *cs, vaddr value) { diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 1607a7a42b..03d37da79f 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "crypto/aes.h" diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index c2b775f4eb..c268065887 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -38,7 +38,6 @@ #include "qapi/qapi-visit-misc.h" #include "qapi/qapi-visit-run-state.h" #include "sysemu/hw_accel.h" -#include "exec/exec-all.h" #include "hw/qdev-properties.h" #ifndef CONFIG_USER_ONLY #include "hw/hw.h" diff --git a/target/s390x/diag.c b/target/s390x/diag.c index ac2c40f363..0a1fabee51 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -16,7 +16,6 @@ #include "cpu.h" #include "internal.h" #include "exec/address-spaces.h" -#include "exec/exec-all.h" #include "hw/watchdog/wdt_diag288.h" #include "sysemu/cpus.h" #include "hw/s390x/ipl.h" diff --git a/target/s390x/helper.c b/target/s390x/helper.c index fd5791f134..254631693d 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -23,7 +23,6 @@ #include "internal.h" #include "exec/gdbstub.h" #include "qemu/timer.h" -#include "exec/exec-all.h" #include "hw/s390x/ioinst.h" #include "sysemu/hw_accel.h" #ifndef CONFIG_USER_ONLY diff --git a/target/tilegx/cpu.c b/target/tilegx/cpu.c index b7451bdcf2..bfe9be59b5 100644 --- a/target/tilegx/cpu.c +++ b/target/tilegx/cpu.c @@ -24,7 +24,6 @@ #include "qemu-common.h" #include "hw/qdev-properties.h" #include "linux-user/syscall_defs.h" -#include "exec/exec-all.h" static void tilegx_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) diff --git a/target/xtensa/core-dc232b.c b/target/xtensa/core-dc232b.c index 70f33622ec..7131337840 100644 --- a/target/xtensa/core-dc232b.c +++ b/target/xtensa/core-dc232b.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu/host-utils.h" #include "qemu/timer.h" diff --git a/target/xtensa/core-dc233c.c b/target/xtensa/core-dc233c.c index 8296e6fa10..d701e3f5de 100644 --- a/target/xtensa/core-dc233c.c +++ b/target/xtensa/core-dc233c.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu-common.h" #include "qemu/host-utils.h" diff --git a/target/xtensa/core-de212.c b/target/xtensa/core-de212.c index 53775a97fa..7322179b56 100644 --- a/target/xtensa/core-de212.c +++ b/target/xtensa/core-de212.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu-common.h" #include "qemu/host-utils.h" diff --git a/target/xtensa/core-fsf.c b/target/xtensa/core-fsf.c index 01932bdc8b..e100e212b9 100644 --- a/target/xtensa/core-fsf.c +++ b/target/xtensa/core-fsf.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu-common.h" #include "qemu/host-utils.h" diff --git a/target/xtensa/core-sample_controller.c b/target/xtensa/core-sample_controller.c index c622335ca5..f433ea8d66 100644 --- a/target/xtensa/core-sample_controller.c +++ b/target/xtensa/core-sample_controller.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu-common.h" #include "qemu/host-utils.h" diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 2b5b537222..b50c840e09 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -33,7 +33,6 @@ #include "cpu.h" #include "qemu-common.h" #include "migration/vmstate.h" -#include "exec/exec-all.h" static void xtensa_cpu_set_pc(CPUState *cs, vaddr value) diff --git a/target/xtensa/import_core.sh b/target/xtensa/import_core.sh index af6c610479..039406bf28 100755 --- a/target/xtensa/import_core.sh +++ b/target/xtensa/import_core.sh @@ -39,7 +39,6 @@ tar -xf "$OVERLAY" -O binutils/xtensa-modules.c | \ cat < "${TARGET}.c" #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu-common.h" #include "qemu/host-utils.h" diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index 2ca219734d..cefba3d185 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "cpu.h" -#include "exec/exec-all.h" #include "tcg.h" #include "tcg-op.h" #include "tcg-mo.h" From 23cecc2717cec380ba5898d48969f0a5432c1043 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 30 May 2018 11:59:31 +0200 Subject: [PATCH 0894/2380] nios2: do not include exec-all.h from cpu.h exec-all.h contains TCG-specific declarations, it should only be includer from helper C files. Signed-off-by: Paolo Bonzini --- target/nios2/cpu.h | 1 - target/nios2/op_helper.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index 145796e8ce..047f3764b7 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -260,7 +260,6 @@ static inline int cpu_interrupts_enabled(CPUNios2State *env) } #include "exec/cpu-all.h" -#include "exec/exec-all.h" static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c index c853aeae02..529ec6ac0e 100644 --- a/target/nios2/op_helper.c +++ b/target/nios2/op_helper.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" +#include "exec/exec-all.h" #include "qemu/main-loop.h" #if !defined(CONFIG_USER_ONLY) From 9c5900bce58a775e13cee6668a26c4d2cd4edfb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:08 -0300 Subject: [PATCH 0895/2380] hw: Do not include "exec/ioport.h" if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code change produced with: $ git grep '#include "exec/ioport.h"' hw | \ cut -d: -f-1 | \ xargs egrep -Li "(portio|cpu_(in|out).\()" | \ xargs sed -i.bak '/#include "exec\/ioport.h"/d' Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-11-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum Signed-off-by: Paolo Bonzini --- hw/acpi/pcihp.c | 1 - hw/acpi/piix4.c | 1 - 2 files changed, 2 deletions(-) diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 91c82fdc7a..80d42e12ff 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -32,7 +32,6 @@ #include "hw/pci/pci.h" #include "hw/acpi/acpi.h" #include "sysemu/sysemu.h" -#include "exec/ioport.h" #include "exec/address-spaces.h" #include "hw/pci/pci_bus.h" #include "qapi/error.h" diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 8b703455b7..6404af5f33 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -28,7 +28,6 @@ #include "sysemu/sysemu.h" #include "qapi/error.h" #include "qemu/range.h" -#include "exec/ioport.h" #include "hw/nvram/fw_cfg.h" #include "exec/address-spaces.h" #include "hw/acpi/piix4.h" From ab728275e48c7cd23f5f4df860f77a771125e3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:09 -0300 Subject: [PATCH 0896/2380] hw: Do not include "exec/address-spaces.h" if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code change produced with: $ git grep '#include "exec/address-spaces.h"' hw include/hw | \ cut -d: -f-1 | \ xargs egrep -L "(get_system_|address_space_)" | \ xargs sed -i.bak '/#include "exec\/address-spaces.h"/d' Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-12-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- hw/arm/bcm2836.c | 1 - hw/arm/xlnx-zcu102.c | 1 - hw/block/onenand.c | 1 - hw/block/pflash_cfi01.c | 1 - hw/block/pflash_cfi02.c | 1 - hw/char/mcf_uart.c | 1 - hw/char/serial.c | 1 - hw/char/sh_serial.c | 1 - hw/core/loader-fit.c | 1 - hw/core/platform-bus.c | 1 - hw/display/sm501.c | 1 - hw/m68k/mcf5206.c | 1 - hw/m68k/mcf_intc.c | 1 - hw/misc/arm_integrator_debug.c | 1 - hw/net/mcf_fec.c | 1 - hw/net/ne2000-isa.c | 1 - hw/pci-host/versatile.c | 1 - hw/riscv/riscv_htif.c | 1 - hw/sh4/sh7750.c | 1 - hw/timer/sh_timer.c | 1 - include/hw/arm/allwinner-a10.h | 1 - include/hw/arm/bcm2835_peripherals.h | 1 - include/hw/display/bcm2835_fb.h | 1 - include/hw/dma/bcm2835_dma.h | 1 - include/hw/misc/bcm2835_mbox.h | 1 - include/hw/misc/bcm2835_property.h | 1 - include/hw/sh4/sh_intc.h | 1 - include/hw/virtio/virtio-access.h | 1 - 28 files changed, 28 deletions(-) diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 3c4b44a53e..6805a7d7c8 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -15,7 +15,6 @@ #include "hw/arm/bcm2836.h" #include "hw/arm/raspi_platform.h" #include "hw/sysbus.h" -#include "exec/address-spaces.h" /* Peripheral base address seen by the CPU */ #define BCM2836_PERI_BASE 0x3F000000 diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index b126cf148b..c70278c8c1 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -22,7 +22,6 @@ #include "hw/arm/xlnx-zynqmp.h" #include "hw/boards.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" #include "qemu/log.h" #include "sysemu/qtest.h" diff --git a/hw/block/onenand.c b/hw/block/onenand.c index ed77f859e9..ab0c7ea1b3 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -27,7 +27,6 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "exec/memory.h" -#include "exec/address-spaces.h" #include "hw/sysbus.h" #include "qemu/error-report.h" diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 2e8284001d..e4b5b3c273 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -43,7 +43,6 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "qemu/bitops.h" -#include "exec/address-spaces.h" #include "qemu/host-utils.h" #include "qemu/log.h" #include "hw/sysbus.h" diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 75d1ae1026..a8b3f7f978 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -41,7 +41,6 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/block-backend.h" -#include "exec/address-spaces.h" #include "qemu/host-utils.h" #include "hw/sysbus.h" diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index faae083e78..787f985db6 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -10,7 +10,6 @@ #include "hw/sysbus.h" #include "hw/m68k/mcf.h" #include "chardev/char-fe.h" -#include "exec/address-spaces.h" typedef struct { SysBusDevice parent_obj; diff --git a/hw/char/serial.c b/hw/char/serial.c index 2c080c9862..605b0d02f9 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -28,7 +28,6 @@ #include "chardev/char-serial.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "exec/address-spaces.h" #include "qemu/error-report.h" //#define DEBUG_SERIAL diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 835b5378a0..373a40595f 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -28,7 +28,6 @@ #include "hw/hw.h" #include "hw/sh4/sh.h" #include "chardev/char-fe.h" -#include "exec/address-spaces.h" #include "qapi/error.h" //#define DEBUG_SERIAL diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 0c4a7207f4..6387854b54 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" #include "exec/memory.h" #include "hw/loader.h" #include "hw/loader-fit.h" diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index 807cb5ccda..e473a44746 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "hw/platform-bus.h" -#include "exec/address-spaces.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" diff --git a/hw/display/sm501.c b/hw/display/sm501.c index f4bb33c279..e47be99451 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -36,7 +36,6 @@ #include "hw/pci/pci.h" #include "qemu/range.h" #include "ui/pixel_ops.h" -#include "exec/address-spaces.h" /* * Status: 2010/05/07 diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index 6ad1e4bd2d..7abd84ac47 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -14,7 +14,6 @@ #include "qemu/timer.h" #include "hw/ptimer.h" #include "sysemu/sysemu.h" -#include "exec/address-spaces.h" /* General purpose timer module. */ typedef struct { diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 8198afac1e..393ce284a2 100644 --- a/hw/m68k/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -11,7 +11,6 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/m68k/mcf.h" -#include "exec/address-spaces.h" #define TYPE_MCF_INTC "mcf-intc" #define MCF_INTC(obj) OBJECT_CHECK(mcf_intc_state, (obj), TYPE_MCF_INTC) diff --git a/hw/misc/arm_integrator_debug.c b/hw/misc/arm_integrator_debug.c index 8a5f29559d..533e6e3208 100644 --- a/hw/misc/arm_integrator_debug.c +++ b/hw/misc/arm_integrator_debug.c @@ -17,7 +17,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" -#include "exec/address-spaces.h" #include "hw/misc/arm_integrator_debug.h" #include "qemu/log.h" diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index bfa6b4bcce..0091e4ecdd 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -14,7 +14,6 @@ #include "hw/sysbus.h" /* For crc32 */ #include -#include "exec/address-spaces.h" //#define DEBUG_FEC 1 diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 70e5c1d3d4..c7fdeb0f6c 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -27,7 +27,6 @@ #include "hw/qdev.h" #include "ne2000.h" #include "sysemu/sysemu.h" -#include "exec/address-spaces.h" #include "qapi/error.h" #include "qapi/visitor.h" diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index d0b02bdc47..7b19078c80 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -12,7 +12,6 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" -#include "exec/address-spaces.h" #include "qemu/log.h" /* Old and buggy versions of QEMU used the wrong mapping from diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c index f73512941f..4f7b11dc37 100644 --- a/hw/riscv/riscv_htif.c +++ b/hw/riscv/riscv_htif.c @@ -29,7 +29,6 @@ #include "chardev/char-fe.h" #include "hw/riscv/riscv_htif.h" #include "qemu/timer.h" -#include "exec/address-spaces.h" #include "qemu/error-report.h" #define RISCV_DEBUG_HTIF 0 diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 5a7d47d31e..2dc07a904b 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -31,7 +31,6 @@ #include "hw/sh4/sh_intc.h" #include "cpu.h" #include "exec/exec-all.h" -#include "exec/address-spaces.h" #define NB_DEVICES 4 diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 9afb2d048c..5f8736cf10 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -13,7 +13,6 @@ #include "hw/sh4/sh.h" #include "qemu/timer.h" #include "qemu/main-loop.h" -#include "exec/address-spaces.h" #include "hw/ptimer.h" //#define DEBUG_TIMER diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index 6b32a99e21..efb8fc8123 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -11,7 +11,6 @@ #include "hw/ide/ahci.h" #include "sysemu/sysemu.h" -#include "exec/address-spaces.h" #define AW_A10_PIC_REG_BASE 0x01c20400 diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index 122b286de7..f5b193f670 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -12,7 +12,6 @@ #define BCM2835_PERIPHERALS_H #include "qemu-common.h" -#include "exec/address-spaces.h" #include "hw/sysbus.h" #include "hw/char/bcm2835_aux.h" #include "hw/display/bcm2835_fb.h" diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index 9a12d7afa2..ae0a3807f2 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -12,7 +12,6 @@ #define BCM2835_FB_H #include "hw/sysbus.h" -#include "exec/address-spaces.h" #include "ui/console.h" #define TYPE_BCM2835_FB "bcm2835-fb" diff --git a/include/hw/dma/bcm2835_dma.h b/include/hw/dma/bcm2835_dma.h index 75312e2e17..60138f4d31 100644 --- a/include/hw/dma/bcm2835_dma.h +++ b/include/hw/dma/bcm2835_dma.h @@ -7,7 +7,6 @@ #define BCM2835_DMA_H #include "qemu-common.h" -#include "exec/address-spaces.h" #include "hw/sysbus.h" typedef struct { diff --git a/include/hw/misc/bcm2835_mbox.h b/include/hw/misc/bcm2835_mbox.h index f4e9ff9ef6..7e8f3ce86d 100644 --- a/include/hw/misc/bcm2835_mbox.h +++ b/include/hw/misc/bcm2835_mbox.h @@ -8,7 +8,6 @@ #include "bcm2835_mbox_defs.h" #include "hw/sysbus.h" -#include "exec/address-spaces.h" #define TYPE_BCM2835_MBOX "bcm2835-mbox" #define BCM2835_MBOX(obj) \ diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h index edcab603ce..11be0dbeac 100644 --- a/include/hw/misc/bcm2835_property.h +++ b/include/hw/misc/bcm2835_property.h @@ -7,7 +7,6 @@ #define BCM2835_PROPERTY_H #include "hw/sysbus.h" -#include "exec/address-spaces.h" #include "net/net.h" #include "hw/display/bcm2835_fb.h" diff --git a/include/hw/sh4/sh_intc.h b/include/hw/sh4/sh_intc.h index 7913bc48a2..fbcee94ed7 100644 --- a/include/hw/sh4/sh_intc.h +++ b/include/hw/sh4/sh_intc.h @@ -3,7 +3,6 @@ #include "qemu-common.h" #include "hw/irq.h" -#include "exec/address-spaces.h" typedef unsigned char intc_enum; diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h index 2e92074bd1..bdf58f3119 100644 --- a/include/hw/virtio/virtio-access.h +++ b/include/hw/virtio/virtio-access.h @@ -18,7 +18,6 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" -#include "exec/address-spaces.h" #if defined(TARGET_PPC64) || defined(TARGET_ARM) #define LEGACY_VIRTIO_IS_BIENDIAN 1 From 0304f9ec9c5fddd2bd3d830d5c4dbe0d54006aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:10 -0300 Subject: [PATCH 0897/2380] hw: Do not include "sysemu/block-backend.h" if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove those unneeded includes to speed up the compilation process a little bit. (Continue 7eceff5b5a1fa cleanup) Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-13-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Acked-by: Cornelia Huck Signed-off-by: Paolo Bonzini --- hw/arm/collie.c | 1 - hw/arm/gumstix.c | 1 - hw/arm/mainstone.c | 1 - hw/arm/nseries.c | 1 - hw/arm/omap1.c | 1 - hw/arm/omap2.c | 1 - hw/arm/omap_sx1.c | 1 - hw/arm/pxa2xx.c | 1 - hw/arm/spitz.c | 1 - hw/arm/versatilepb.c | 1 - hw/arm/vexpress.c | 1 - hw/arm/virt.c | 1 - hw/arm/xilinx_zynq.c | 1 - hw/arm/z2.c | 1 - hw/block/dataplane/virtio-blk.c | 1 - hw/block/virtio-blk.c | 1 - hw/core/qdev-properties.c | 1 - hw/cris/axis_dev88.c | 1 - hw/display/tc6393xb.c | 1 - hw/ide/pci.c | 1 - hw/ide/via.c | 1 - hw/isa/isa-superio.c | 1 - hw/lm32/lm32_boards.c | 1 - hw/lm32/milkymist.c | 1 - hw/microblaze/petalogix_ml605_mmu.c | 1 - hw/microblaze/petalogix_s3adsp1800_mmu.c | 1 - hw/mips/mips_r4k.c | 1 - hw/ppc/spapr.c | 1 - hw/ppc/virtex_ml507.c | 2 -- hw/s390x/virtio-ccw.c | 1 - hw/scsi/mptsas.c | 1 - hw/sd/pl181.c | 1 - hw/sd/sdhci.c | 1 - hw/sd/ssi-sd.c | 1 - hw/sh4/r2d.c | 1 - hw/virtio/virtio-pci.c | 1 - hw/xen/xen_devconfig.c | 1 - hw/xtensa/xtfpga.c | 1 - 38 files changed, 39 deletions(-) diff --git a/hw/arm/collie.c b/hw/arm/collie.c index f8c566e2e5..48b732c176 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -16,7 +16,6 @@ #include "strongarm.h" #include "hw/arm/arm.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "cpu.h" diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c index ea2a3c532d..56cb763c4e 100644 --- a/hw/arm/gumstix.c +++ b/hw/arm/gumstix.c @@ -42,7 +42,6 @@ #include "hw/block/flash.h" #include "hw/devices.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" #include "cpu.h" diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c index 4215c025fc..0beb5c426b 100644 --- a/hw/arm/mainstone.c +++ b/hw/arm/mainstone.c @@ -21,7 +21,6 @@ #include "hw/devices.h" #include "hw/boards.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 32687afced..906b7ca22d 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -35,7 +35,6 @@ #include "hw/hw.h" #include "hw/bt.h" #include "hw/loader.h" -#include "sysemu/block-backend.h" #include "hw/sysbus.h" #include "qemu/log.h" #include "exec/address-spaces.h" diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index e54c1f8f99..854996c1ac 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -28,7 +28,6 @@ #include "hw/arm/omap.h" #include "sysemu/sysemu.h" #include "hw/arm/soc_dma.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/qtest.h" #include "qemu/range.h" diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index b8d0910a1f..cc4250b7da 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -23,7 +23,6 @@ #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/qtest.h" #include "hw/boards.h" diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index eccc19c77b..84550f0236 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -33,7 +33,6 @@ #include "hw/boards.h" #include "hw/arm/arm.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "sysemu/qtest.h" #include "exec/address-spaces.h" #include "cpu.h" diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index a2803fdee4..b67b0cefb6 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -19,7 +19,6 @@ #include "hw/i2c/i2c.h" #include "hw/ssi/ssi.h" #include "chardev/char-fe.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/qtest.h" #include "qemu/cutils.h" diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index e419e3c00e..3cc27a1e44 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -27,7 +27,6 @@ #include "hw/audio/wm8750.h" #include "audio/audio.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "cpu.h" diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index e01e3192ff..a5a06b6d40 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -19,7 +19,6 @@ #include "hw/pci/pci.h" #include "hw/i2c/i2c.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "hw/block/flash.h" #include "qemu/error-report.h" diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index f1e33c8a36..5bfe2e4348 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -35,7 +35,6 @@ #include "hw/boards.h" #include "hw/loader.h" #include "exec/address-spaces.h" -#include "sysemu/block-backend.h" #include "hw/block/flash.h" #include "sysemu/device_tree.h" #include "qemu/error-report.h" diff --git a/hw/arm/virt.c b/hw/arm/virt.c index a3a28e20e8..3aa19b2935 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -38,7 +38,6 @@ #include "hw/vfio/vfio-amd-xgbe.h" #include "hw/devices.h" #include "net/net.h" -#include "sysemu/block-backend.h" #include "sysemu/device_tree.h" #include "sysemu/numa.h" #include "sysemu/sysemu.h" diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 899a26326f..f1496d2927 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -26,7 +26,6 @@ #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "hw/loader.h" #include "hw/misc/zynq-xadc.h" #include "hw/ssi/ssi.h" diff --git a/hw/arm/z2.c b/hw/arm/z2.c index 730a5392e9..697a822f1e 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -21,7 +21,6 @@ #include "hw/boards.h" #include "sysemu/sysemu.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "ui/console.h" #include "hw/audio/wm8750.h" #include "audio/audio.h" diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 101f32cf66..d648aeb73b 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -19,7 +19,6 @@ #include "qemu/thread.h" #include "qemu/error-report.h" #include "hw/virtio/virtio-access.h" -#include "sysemu/block-backend.h" #include "hw/virtio/virtio-blk.h" #include "virtio-blk.h" #include "block/aio.h" diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index b1532e4e91..50b5c869e3 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -18,7 +18,6 @@ #include "qemu/error-report.h" #include "trace.h" #include "hw/block/block.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/virtio/virtio-blk.h" #include "dataplane/virtio-blk.h" diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 5bbc2d98b5..989778ab7f 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -5,7 +5,6 @@ #include "hw/pci/pci.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" -#include "sysemu/block-backend.h" #include "hw/block/block.h" #include "net/hub.h" #include "qapi/visitor.h" diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index 409f3d581a..56ee398ee5 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -34,7 +34,6 @@ #include "hw/loader.h" #include "elf.h" #include "boot.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" #include "sysemu/sysemu.h" diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c index 464465b7c2..8392e59493 100644 --- a/hw/display/tc6393xb.c +++ b/hw/display/tc6393xb.c @@ -18,7 +18,6 @@ #include "hw/block/flash.h" #include "ui/console.h" #include "ui/pixel_ops.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #define IRQ_TC6393_NAND 0 diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 1ab0a892d0..fe1ceeb0cd 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -26,7 +26,6 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/isa/isa.h" -#include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "qemu/error-report.h" #include "hw/ide/pci.h" diff --git a/hw/ide/via.c b/hw/ide/via.c index 117ac4d95e..238f038d72 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -27,7 +27,6 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/isa/isa.h" -#include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index 76286c81a1..9359be7008 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -13,7 +13,6 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "chardev/char.h" #include "hw/isa/superio.h" diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index 907e875d02..167058348e 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -27,7 +27,6 @@ #include "hw/devices.h" #include "hw/boards.h" #include "hw/loader.h" -#include "sysemu/block-backend.h" #include "elf.h" #include "lm32_hwsetup.h" #include "lm32.h" diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index f9688e059e..c36bbc4ae2 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -30,7 +30,6 @@ #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" -#include "sysemu/block-backend.h" #include "milkymist-hw.h" #include "lm32.h" #include "exec/address-spaces.h" diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index cf6bf3f32a..6c4a544eac 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -36,7 +36,6 @@ #include "sysemu/sysemu.h" #include "hw/devices.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "hw/char/serial.h" #include "exec/address-spaces.h" #include "hw/ssi/ssi.h" diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 1186002a76..0da3e62102 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -34,7 +34,6 @@ #include "sysemu/sysemu.h" #include "hw/devices.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "hw/char/xilinx_uartlite.h" diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index e04b49d3c5..e5cf8ed1a3 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -30,7 +30,6 @@ #include "hw/timer/mc146818rtc.h" #include "hw/input/i8042.h" #include "hw/timer/i8254.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" #include "qemu/error-report.h" diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index ebf30dd60b..2e910428f3 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -35,7 +35,6 @@ #include "elf.h" #include "net/net.h" #include "sysemu/device_tree.h" -#include "sysemu/block-backend.h" #include "sysemu/cpus.h" #include "sysemu/hw_accel.h" #include "kvm_ppc.h" diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index a80cbdd7ee..b4bb90d50b 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -44,8 +44,6 @@ #include "hw/ppc/ppc4xx.h" #include "ppc405.h" -#include "sysemu/block-backend.h" - #define EPAPR_MAGIC (0x45504150) #define FLASH_SIZE (16 * 1024 * 1024) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 22df33b509..b68798ac52 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 3f061f3f68..4176e871e1 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -26,7 +26,6 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "sysemu/dma.h" -#include "sysemu/block-backend.h" #include "hw/pci/msi.h" #include "qemu/iov.h" #include "hw/scsi/scsi.h" diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 3ba1f7dd23..1cc94dbfdf 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/sysbus.h" #include "hw/sd/sd.h" diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 63c44a4ee8..b65403947b 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -26,7 +26,6 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/dma.h" #include "qemu/timer.h" diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index ae04b6641b..96542ecd62 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/ssi/ssi.h" #include "hw/sd/sd.h" diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 6b01d6eed8..8fe8766eb9 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -40,7 +40,6 @@ #include "hw/loader.h" #include "hw/usb.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #define FLASH_BASE 0x00000000 diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 5eb0c323ca..3a01fe90f0 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -32,7 +32,6 @@ #include "hw/pci/msix.h" #include "hw/loader.h" #include "sysemu/kvm.h" -#include "sysemu/block-backend.h" #include "virtio-pci.h" #include "qemu/range.h" #include "hw/virtio/virtio-bus.h" diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c index fac9d3fcdc..aebc19bd71 100644 --- a/hw/xen/xen_devconfig.c +++ b/hw/xen/xen_devconfig.c @@ -1,7 +1,6 @@ #include "qemu/osdep.h" #include "hw/xen/xen_backend.h" #include "qemu/option.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" /* ------------------------------------------------------------- */ diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 63734c70ec..5dc13034f9 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -38,7 +38,6 @@ #include "net/net.h" #include "hw/sysbus.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "chardev/char.h" #include "sysemu/device_tree.h" #include "qemu/error-report.h" From 7dbaea42f101c7e47bf6cf3aed77ff903dfa2023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:11 -0300 Subject: [PATCH 0898/2380] hw: Do not include "sysemu/blockdev.h" if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header "hw/boards.h" already includes "sysemu/blockdev.h". Code change produced with: $ git grep '#include "sysemu/blockdev.h"' hw | \ cut -d: -f-1 | \ xargs fgrep -l '#include "hw/boards.h"' | \ xargs sed -i.bak '/#include "sysemu\/blockdev.h"/d' Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-14-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- hw/arm/aspeed.c | 1 - hw/arm/omap1.c | 1 - hw/arm/omap2.c | 1 - hw/mips/mips_malta.c | 1 - hw/ppc/ppc405_boards.c | 1 - hw/ppc/sam460ex.c | 1 - 6 files changed, 6 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index aecb3c1e75..a7110a712f 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -19,7 +19,6 @@ #include "hw/boards.h" #include "qemu/log.h" #include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #include "hw/loader.h" #include "qemu/error-report.h" diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 854996c1ac..9af04728e3 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -28,7 +28,6 @@ #include "hw/arm/omap.h" #include "sysemu/sysemu.h" #include "hw/arm/soc_dma.h" -#include "sysemu/blockdev.h" #include "sysemu/qtest.h" #include "qemu/range.h" #include "hw/sysbus.h" diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index cc4250b7da..3c7d1364a9 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -23,7 +23,6 @@ #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" -#include "sysemu/blockdev.h" #include "sysemu/qtest.h" #include "hw/boards.h" #include "hw/hw.h" diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index af70ecffc0..494f84e290 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -46,7 +46,6 @@ #include "elf.h" #include "hw/timer/mc146818rtc.h" #include "hw/timer/i8254.h" -#include "sysemu/blockdev.h" #include "exec/address-spaces.h" #include "hw/sysbus.h" /* SysBusDevice */ #include "qemu/host-utils.h" diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 0b658931ee..d301067d3b 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -37,7 +37,6 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "hw/loader.h" -#include "sysemu/blockdev.h" #include "exec/address-spaces.h" #define BIOS_FILENAME "ppc405_rom.bin" diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index a48e6e6fce..3dd23de71f 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -17,7 +17,6 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/blockdev.h" #include "hw/boards.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" From 6dd046a3c4ecf9864653c00c83aba05d0539a372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:12 -0300 Subject: [PATCH 0899/2380] hw: Do not include "sysemu/blockdev.h" if it is not necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove those unneeded includes to speed up the compilation process a little bit. Code change produced with: $ git grep '#include "sysemu/blockdev.h"' | \ cut -d: -f-1 | \ xargs egrep -L "(BlockInterfaceType|DriveInfo|drive_get|blk_legacy_dinfo|blockdev_mark_auto_del)" | \ xargs sed -i.bak '/#include "sysemu\/blockdev.h"/d' Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-15-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- hw/block/m25p80.c | 1 - hw/block/onenand.c | 1 - hw/i386/xen/xen-mapcache.c | 1 - hw/s390x/virtio-ccw.c | 1 - hw/scsi/scsi-generic.c | 1 - hw/sd/sdhci.c | 1 - hw/usb/dev-storage.c | 1 - monitor.c | 1 - 8 files changed, 8 deletions(-) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index b49c8e9caa..a5ccffb4aa 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -24,7 +24,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #include "hw/ssi/ssi.h" #include "qemu/bitops.h" #include "qemu/log.h" diff --git a/hw/block/onenand.c b/hw/block/onenand.c index ab0c7ea1b3..0cb8d7fa13 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -25,7 +25,6 @@ #include "hw/block/flash.h" #include "hw/irq.h" #include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #include "exec/memory.h" #include "hw/sysbus.h" #include "qemu/error-report.h" diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index efa35dc6e0..541b7693b3 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -14,7 +14,6 @@ #include #include "hw/xen/xen_backend.h" -#include "sysemu/blockdev.h" #include "qemu/bitmap.h" #include diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index b68798ac52..0a9bec484b 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "net/net.h" diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 381f04e339..03bce8ff39 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -17,7 +17,6 @@ #include "qemu/error-report.h" #include "hw/scsi/scsi.h" #include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #ifdef __linux__ diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index b65403947b..3017e5a95a 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -26,7 +26,6 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/blockdev.h" #include "sysemu/dma.h" #include "qemu/timer.h" #include "qemu/bitops.h" diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index b56c75a73a..d02acda945 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -20,7 +20,6 @@ #include "monitor/monitor.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #include "qapi/visitor.h" #include "qemu/cutils.h" diff --git a/monitor.c b/monitor.c index 46814af533..d75cb20815 100644 --- a/monitor.c +++ b/monitor.c @@ -44,7 +44,6 @@ #include "qemu/readline.h" #include "ui/console.h" #include "ui/input.h" -#include "sysemu/blockdev.h" #include "sysemu/block-backend.h" #include "audio/audio.h" #include "disas/disas.h" From 6b39bad02e6a503eeea1be0631779e9ad4e1513c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:13 -0300 Subject: [PATCH 0900/2380] hw/block/nvme: Include "qemu/cutils.h" directly in the source file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20180528232719.4721-16-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- hw/block/nvme.c | 1 + hw/block/nvme.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 85d2406400..811084b6a7 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -35,6 +35,7 @@ #include "sysemu/block-backend.h" #include "qemu/log.h" +#include "qemu/cutils.h" #include "trace.h" #include "nvme.h" diff --git a/hw/block/nvme.h b/hw/block/nvme.h index 8f3981121d..cabcf20c32 100644 --- a/hw/block/nvme.h +++ b/hw/block/nvme.h @@ -1,6 +1,5 @@ #ifndef HW_NVME_H #define HW_NVME_H -#include "qemu/cutils.h" #include "block/nvme.h" typedef struct NvmeAsyncEvent { From 921e1a2ab3ffd3b670e227078f564aaf846d154c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:14 -0300 Subject: [PATCH 0901/2380] hw/misc/mips_itu: Cleanup includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-17-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- hw/misc/mips_itu.c | 5 +---- include/hw/misc/mips_itu.h | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index c84a48bbb7..ccc4c7d98a 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -18,13 +18,10 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qapi/error.h" #include "cpu.h" -#include "qemu/log.h" #include "exec/exec-all.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" #include "hw/misc/mips_itu.h" #define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8) diff --git a/include/hw/misc/mips_itu.h b/include/hw/misc/mips_itu.h index b3a4532036..030eb4ac62 100644 --- a/include/hw/misc/mips_itu.h +++ b/include/hw/misc/mips_itu.h @@ -20,6 +20,8 @@ #ifndef MIPS_ITU_H #define MIPS_ITU_H +#include "hw/sysbus.h" + #define TYPE_MIPS_ITU "mips-itu" #define MIPS_ITU(obj) OBJECT_CHECK(MIPSITUState, (obj), TYPE_MIPS_ITU) From b18aad5c99f63a00105d0b94aa5ca408ac3ce89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:15 -0300 Subject: [PATCH 0902/2380] hw/misc/sga: Use the correct ISA include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SGA BIOS loader is an ISA device, it does not require the PCI header. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-18-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum Signed-off-by: Paolo Bonzini --- hw/misc/sga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/sga.c b/hw/misc/sga.c index 97fd63f176..4a22a52a60 100644 --- a/hw/misc/sga.c +++ b/hw/misc/sga.c @@ -25,7 +25,7 @@ * */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/isa/isa.h" #include "hw/loader.h" #include "sysemu/sysemu.h" From 6984e4a93237b1e251d6d68eb8c29f67d4cb3443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:16 -0300 Subject: [PATCH 0903/2380] hw/hppa: Remove unused include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-19-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- hw/hppa/hppa_sys.h | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/hppa/hppa_sys.h b/hw/hppa/hppa_sys.h index a182d1f34e..3f6c145120 100644 --- a/hw/hppa/hppa_sys.h +++ b/hw/hppa/hppa_sys.h @@ -3,7 +3,6 @@ #ifndef HW_HPPA_SYS_H #define HW_HPPA_SYS_H -#include "target/hppa/cpu-qom.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" #include "hw/ide.h" From 0f2cc50cfaa94bbd20e28ca2073e37cc0d47a534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:17 -0300 Subject: [PATCH 0904/2380] hw/i386/pc: Remove unused include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-20-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d768930d02..8b0803cb83 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -64,7 +64,6 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/cpu_hotplug.h" #include "hw/boards.h" -#include "hw/pci/pci_host.h" #include "acpi-build.h" #include "hw/mem/pc-dimm.h" #include "qapi/error.h" From ca473f23207eaf0ec7fab73ce642f914d73315dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:18 -0300 Subject: [PATCH 0905/2380] hw/ide: Remove unused include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to include pci.h in this file. (Continue f23c81073a cleanup). Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-21-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- hw/ide/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 866c659498..cc9ca28c33 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" -#include "hw/pci/pci.h" #include "hw/isa/isa.h" #include "qemu/error-report.h" #include "qemu/timer.h" From b969ea6bcddbac3c1956f5b8d0cc49dbd17f59a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 20:27:19 -0300 Subject: [PATCH 0906/2380] hw: Clean "hw/devices.h" includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180528232719.4721-22-f4bug@amsat.org> Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- include/hw/devices.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/include/hw/devices.h b/include/hw/devices.h index 861ddea8af..0e27feb0c2 100644 --- a/include/hw/devices.h +++ b/include/hw/devices.h @@ -1,13 +1,10 @@ #ifndef QEMU_DEVICES_H #define QEMU_DEVICES_H -#include "hw/irq.h" - -/* ??? Not all users of this file can include cpu-common.h. */ -struct MemoryRegion; - /* Devices that have nowhere better to go. */ +#include "hw/hw.h" + /* smc91c111.c */ void smc91c111_init(NICInfo *, uint32_t, qemu_irq); From 24ed11723230e43f44e9a1aef72e7c71714288d1 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Wed, 30 May 2018 17:11:29 +1000 Subject: [PATCH 0907/2380] qom: Document qom/device-list-properties implementation specific The recently introduced qom-list-properties QMP command raised a question what properties it (and its cousin - device-list-properties) can possibly print - only those defined by DeviceClass::props or dynamically created in TypeInfo::instance_init() so properties created elsewhere won't show up and this behaviour might confuse the user. For example, PIIX4 does that from piix4_pm_realize() via piix4_pm_add_propeties(): object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_ENABLE_CMD, &acpi_enable_cmd, NULL); This adds a note to the command descriptions about the limitation. Reviewed-by: Markus Armbruster Acked-by: Paolo Bonzini Signed-off-by: Alexey Kardashevskiy Message-Id: <20180530071129.9013-1-aik@ozlabs.ru> Signed-off-by: Paolo Bonzini --- qapi/misc.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qapi/misc.json b/qapi/misc.json index f5988cc0b5..efc3d23057 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -1529,6 +1529,10 @@ # # Returns: a list of ObjectPropertyInfo describing a devices properties # +# Note: objects can create properties at runtime, for example to describe +# links between different devices and/or objects. These properties +# are not included in the output of this command. +# # Since: 1.2 ## { 'command': 'device-list-properties', @@ -1542,6 +1546,10 @@ # # @typename: the type name of an object # +# Note: objects can create properties at runtime, for example to describe +# links between different devices and/or objects. These properties +# are not included in the output of this command. +# # Returns: a list of ObjectPropertyInfo describing object properties # # Since: 2.12 From e40077fd2ccbf4ffa8d07f186110fac240a45bf9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 30 May 2018 18:16:04 +0200 Subject: [PATCH 0908/2380] qom: support orphan objects in object_get_canonical_path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly a rewrite, in order to keep the loop simple. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- qom/object.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/qom/object.c b/qom/object.c index 0fc972030e..cb7a8cd589 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1669,25 +1669,29 @@ gchar *object_get_canonical_path(Object *obj) Object *root = object_get_root(); char *newpath, *path = NULL; - while (obj != root) { - char *component = object_get_canonical_path_component(obj); - - if (path) { - newpath = g_strdup_printf("%s/%s", component, path); - g_free(component); - g_free(path); - path = newpath; - } else { - path = component; - } - - obj = obj->parent; + if (obj == root) { + return g_strdup("/"); } - newpath = g_strdup_printf("/%s", path ? path : ""); - g_free(path); + do { + char *component = object_get_canonical_path_component(obj); - return newpath; + if (!component) { + /* A canonical path must be complete, so discard what was + * collected so far. + */ + g_free(path); + return NULL; + } + + newpath = g_strdup_printf("/%s%s", component, path ? path : ""); + g_free(path); + g_free(component); + path = newpath; + obj = obj->parent; + } while (obj != root); + + return path; } Object *object_resolve_path_component(Object *parent, const gchar *part) From 45641dba38f6f44c3ea44c2d1c37b31619276ce3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 15 May 2018 15:25:31 +0200 Subject: [PATCH 0909/2380] virtio: free MemoryRegionCache when initialization fails --- hw/virtio/virtio.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 1debb0147b..d4e4d98b59 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -123,11 +123,22 @@ static void virtio_free_region_cache(VRingMemoryRegionCaches *caches) g_free(caches); } +static void virtio_virtqueue_reset_region_cache(struct VirtQueue *vq) +{ + VRingMemoryRegionCaches *caches; + + caches = atomic_read(&vq->vring.caches); + atomic_rcu_set(&vq->vring.caches, NULL); + if (caches) { + call_rcu(caches, virtio_free_region_cache, rcu); + } +} + static void virtio_init_region_cache(VirtIODevice *vdev, int n) { VirtQueue *vq = &vdev->vq[n]; VRingMemoryRegionCaches *old = vq->vring.caches; - VRingMemoryRegionCaches *new; + VRingMemoryRegionCaches *new = NULL; hwaddr addr, size; int event_size; int64_t len; @@ -136,7 +147,7 @@ static void virtio_init_region_cache(VirtIODevice *vdev, int n) addr = vq->vring.desc; if (!addr) { - return; + goto out_no_cache; } new = g_new0(VRingMemoryRegionCaches, 1); size = virtio_queue_get_desc_size(vdev, n); @@ -170,11 +181,14 @@ static void virtio_init_region_cache(VirtIODevice *vdev, int n) return; err_avail: - address_space_cache_destroy(&new->used); + address_space_cache_destroy(&new->avail); err_used: - address_space_cache_destroy(&new->desc); + address_space_cache_destroy(&new->used); err_desc: + address_space_cache_destroy(&new->desc); +out_no_cache: g_free(new); + virtio_virtqueue_reset_region_cache(vq); } /* virt queue functions */ @@ -1168,17 +1182,6 @@ static enum virtio_device_endian virtio_current_cpu_endian(void) } } -static void virtio_virtqueue_reset_region_cache(struct VirtQueue *vq) -{ - VRingMemoryRegionCaches *caches; - - caches = atomic_read(&vq->vring.caches); - atomic_rcu_set(&vq->vring.caches, NULL); - if (caches) { - call_rcu(caches, virtio_free_region_cache, rcu); - } -} - void virtio_reset(void *opaque) { VirtIODevice *vdev = opaque; From 0330002cb5176fcbc376478ac36f5f512ed53b9f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 May 2018 14:48:35 +0100 Subject: [PATCH 0910/2380] memory.h: Fix typo in documentation comment Signed-off-by: Peter Maydell Message-Id: <20180515134835.3409-1-peter.maydell@linaro.org> Signed-off-by: Paolo Bonzini --- include/exec/memory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 525619a5f4..4fa1227f13 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -679,7 +679,7 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, * memory_region_init_reservation: Initialize a memory region that reserves * I/O space. * - * A reservation region primariy serves debugging purposes. It claims I/O + * A reservation region primarily serves debugging purposes. It claims I/O * space that is not supposed to be handled by QEMU itself. Any access via * the memory API will cause an abort(). * This function is deprecated. Use memory_region_init_io() with NULL From 257a7430e7a6bc5a9e4c17d884f4a092529501c7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 15 May 2018 16:35:16 +0200 Subject: [PATCH 0911/2380] memory: get rid of memory_region_init_reservation The function has been deprecated for 2.5 years, and there are just a handful of users. Convert them to memory_region_init_io with NULL callbacks, and while at it pass the right device as the owner. Signed-off-by: Paolo Bonzini --- docs/devel/memory.txt | 5 ++--- hw/i386/kvm/i8254.c | 2 +- hw/i386/kvm/i8259.c | 4 ++-- hw/i386/kvm/ioapic.c | 2 +- include/exec/memory.h | 23 ----------------------- 5 files changed, 6 insertions(+), 30 deletions(-) diff --git a/docs/devel/memory.txt b/docs/devel/memory.txt index 8ed810f8b9..c1dee1252c 100644 --- a/docs/devel/memory.txt +++ b/docs/devel/memory.txt @@ -77,9 +77,8 @@ MemoryRegion): - reservation region: a reservation region is primarily for debugging. It claims I/O space that is not supposed to be handled by QEMU itself. The typical use is to track parts of the address space which will be - handled by the host kernel when KVM is enabled. - You initialize these with memory_region_init_reservation(), or by - passing a NULL callback parameter to memory_region_init_io(). + handled by the host kernel when KVM is enabled. You initialize these + by passing a NULL callback parameter to memory_region_init_io(). It is valid to add subregions to a region which is not a pure container (that is, to an MMIO, RAM or ROM region). This means that the region diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 13f20f47d9..d4d4a859f0 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -293,7 +293,7 @@ static void kvm_pit_realizefn(DeviceState *dev, Error **errp) return; } - memory_region_init_reservation(&pit->ioports, NULL, "kvm-pit", 4); + memory_region_init_io(&pit->ioports, OBJECT(dev), NULL, NULL, "kvm-pit", 4); qdev_init_gpio_in(dev, kvm_pit_irq_control, 1); diff --git a/hw/i386/kvm/i8259.c b/hw/i386/kvm/i8259.c index 05394cdb7b..83b6bfec77 100644 --- a/hw/i386/kvm/i8259.c +++ b/hw/i386/kvm/i8259.c @@ -121,8 +121,8 @@ static void kvm_pic_realize(DeviceState *dev, Error **errp) PICCommonState *s = PIC_COMMON(dev); KVMPICClass *kpc = KVM_PIC_GET_CLASS(dev); - memory_region_init_reservation(&s->base_io, NULL, "kvm-pic", 2); - memory_region_init_reservation(&s->elcr_io, NULL, "kvm-elcr", 1); + memory_region_init_io(&s->base_io, OBJECT(dev), NULL, NULL, "kvm-pic", 2); + memory_region_init_io(&s->elcr_io, OBJECT(dev), NULL, NULL, "kvm-elcr", 1); kpc->parent_realize(dev, errp); } diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 98ca480792..646f6245ee 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -142,7 +142,7 @@ static void kvm_ioapic_realize(DeviceState *dev, Error **errp) { IOAPICCommonState *s = IOAPIC_COMMON(dev); - memory_region_init_reservation(&s->io_memory, NULL, "kvm-ioapic", 0x1000); + memory_region_init_io(&s->io_memory, OBJECT(dev), NULL, NULL, "kvm-ioapic", 0x1000); /* * KVM ioapic only supports 0x11 now. This will only be used when * we want to dump ioapic version. diff --git a/include/exec/memory.h b/include/exec/memory.h index 4fa1227f13..a116d936fc 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -675,29 +675,6 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, uint64_t size, Error **errp); -/** - * memory_region_init_reservation: Initialize a memory region that reserves - * I/O space. - * - * A reservation region primarily serves debugging purposes. It claims I/O - * space that is not supposed to be handled by QEMU itself. Any access via - * the memory API will cause an abort(). - * This function is deprecated. Use memory_region_init_io() with NULL - * callbacks instead. - * - * @mr: the #MemoryRegion to be initialized - * @owner: the object that tracks the region's reference count - * @name: used for debugging; not visible to the user or ABI - * @size: size of the region. - */ -static inline void memory_region_init_reservation(MemoryRegion *mr, - Object *owner, - const char *name, - uint64_t size) -{ - memory_region_init_io(mr, owner, NULL, mr, name, size); -} - /** * memory_region_init_iommu: Initialize a memory region of a custom type * that translates addresses From 12d3a03830818f8fb347fbf724f3627dd064a29f Mon Sep 17 00:00:00 2001 From: Jay Zhou Date: Tue, 15 May 2018 19:35:08 +0800 Subject: [PATCH 0912/2380] memory: delete struct AddressSpaceOps Since struct AddressSpaceOps isn't used anywhere else, so just delete it. Signed-off-by: Jay Zhou Message-Id: <1526384108-49348-1-git-send-email-jianjay.zhou@huawei.com> Signed-off-by: Paolo Bonzini --- memory.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/memory.c b/memory.c index 7ead90f8cd..e4f484b73e 100644 --- a/memory.c +++ b/memory.c @@ -219,8 +219,6 @@ struct FlatRange { bool readonly; }; -typedef struct AddressSpaceOps AddressSpaceOps; - #define FOR_EACH_FLAT_RANGE(var, view) \ for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var) From d4c8fcd91abba584f2788dfe5fba5245cea380f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 May 2018 12:24:59 -0300 Subject: [PATCH 0913/2380] hw/isa/superio: Fix inconsistent use of Chardev->be MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4c3119a6e3e and cd9526ab7c0 introduced an incorrect and inconsistent use of Chardev->be. Also, this CharBackend member is private and is not supposed to be accessible. Fix it by removing the inconsistent check. Cc: qemu-stable@nongnu.org Reported-by: Marc-André Lureau Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180515152500.19460-2-f4bug@amsat.org> Signed-off-by: Paolo Bonzini Reviewed-by: Marc-André Lureau --- hw/isa/isa-superio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index 9359be7008..8bc2f69eaa 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -42,7 +42,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) if (!k->parallel.is_enabled || k->parallel.is_enabled(sio, i)) { /* FIXME use a qdev chardev prop instead of parallel_hds[] */ chr = parallel_hds[i]; - if (chr == NULL || chr->be) { + if (chr == NULL) { name = g_strdup_printf("discarding-parallel%d", i); chr = qemu_chr_new(name, "null"); } else { @@ -82,7 +82,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) if (!k->serial.is_enabled || k->serial.is_enabled(sio, i)) { /* FIXME use a qdev chardev prop instead of serial_hd() */ chr = serial_hd(i); - if (chr == NULL || chr->be) { + if (chr == NULL) { name = g_strdup_printf("discarding-serial%d", i); chr = qemu_chr_new(name, "null"); } else { From eeaa6715050ed3f9cbedd322220f31570a503217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 15 May 2018 12:25:00 -0300 Subject: [PATCH 0914/2380] mux: fix ctrl-a b again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit fb5e19d2e1472e96d72d5e4d89c20033f8ab345c originally fixed the regression, but was inadvertently broken again in merge commit 2d6752d38d8acda. Fixes: https://bugs.launchpad.net/qemu/+bug/1654137 Cc: qemu-stable@nongnu.org Signed-off-by: Marc-André Lureau Reviewed-by: Peter Maydell Message-Id: <20180515152500.19460-3-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- chardev/char-mux.c | 1 + tests/test-char.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/chardev/char-mux.c b/chardev/char-mux.c index 1b925c8dec..6055e76293 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -304,6 +304,7 @@ void mux_set_focus(Chardev *chr, int focus) } d->focus = focus; + chr->be = d->backends[focus]; mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); } diff --git a/tests/test-char.c b/tests/test-char.c index 1880d36783..5905d31441 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -214,6 +214,10 @@ static void char_mux_test(void) g_assert_cmpint(h2.last_event, ==, -1); /* switch focus */ + qemu_chr_be_write(base, (void *)"\1b", 2); + g_assert_cmpint(h1.last_event, ==, 42); + g_assert_cmpint(h2.last_event, ==, CHR_EVENT_BREAK); + qemu_chr_be_write(base, (void *)"\1c", 2); g_assert_cmpint(h1.last_event, ==, CHR_EVENT_MUX_IN); g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); @@ -227,6 +231,10 @@ static void char_mux_test(void) g_assert_cmpstr(h1.read_buf, ==, "hello"); h1.read_count = 0; + qemu_chr_be_write(base, (void *)"\1b", 2); + g_assert_cmpint(h1.last_event, ==, CHR_EVENT_BREAK); + g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); + /* remove first handler */ qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, NULL, true); From 4f938cbd90e7cf3004a6877ce579a11a87a4aa33 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 May 2018 18:27:29 +0100 Subject: [PATCH 0915/2380] memfd: Avoid Coverity warning about integer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity complains about qemu_memfd_create() (CID 1385858) because we calculate a bit position htsize which could be up to 63, but then use it in "1 << htsize" which is a 32-bit integer calculation and could push the 1 off the top of the value. Silence the complaint bu using "1ULL"; this isn't a bug in practice since a hugetlbsize of 4GB is not very plausible. Signed-off-by: Peter Maydell Message-Id: <20180515172729.24564-1-peter.maydell@linaro.org> Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- util/memfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/memfd.c b/util/memfd.c index b3ecbac19e..d248a53c3c 100644 --- a/util/memfd.c +++ b/util/memfd.c @@ -66,7 +66,7 @@ int qemu_memfd_create(const char *name, size_t size, bool hugetlb, { int htsize = hugetlbsize ? ctz64(hugetlbsize) : 0; - if (htsize && 1 << htsize != hugetlbsize) { + if (htsize && 1ULL << htsize != hugetlbsize) { error_setg(errp, "Hugepage size must be a power of 2"); return -1; } From 8347c18506c3f8619527d19134cb4aac071dc54a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 May 2018 19:27:00 +0100 Subject: [PATCH 0916/2380] exec.c: Initialize sa_flags passed to sigaction() Coverity points out that in the user-only version of cpu_abort() we call sigaction() with a partially initialized struct sigaction (CID 1005351). Correct the omission. Signed-off-by: Peter Maydell Message-Id: <20180515182700.31736-1-peter.maydell@linaro.org> Signed-off-by: Paolo Bonzini --- exec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/exec.c b/exec.c index ffa1099547..bd8833fc9d 100644 --- a/exec.c +++ b/exec.c @@ -1124,6 +1124,7 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) struct sigaction act; sigfillset(&act.sa_mask); act.sa_handler = SIG_DFL; + act.sa_flags = 0; sigaction(SIGABRT, &act, NULL); } #endif From 327fccb288976f95808efa968082fc9d4a9ced84 Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Tue, 15 May 2018 20:35:21 +0300 Subject: [PATCH 0917/2380] WHPX: dynamically load WHP libraries We're currently linking against import libraries of the WHP DLLs. By dynamically loading the libraries, we ensure that QEMU will work on previous Windows versions, where the WHP DLLs will be missing (assuming that WHP is not requested). Also, we're simplifying the build process, as we no longer require the import libraries. Signed-off-by: Alessandro Pilotti Signed-off-by: Justin Terry (VM) Signed-off-by: Lucian Petrut Message-Id: <1526405722-10887-2-git-send-email-lpetrut@cloudbasesolutions.com> Signed-off-by: Paolo Bonzini --- configure | 15 +-- target/i386/whpx-all.c | 212 ++++++++++++++++++++++++++++------------- 2 files changed, 145 insertions(+), 82 deletions(-) diff --git a/configure b/configure index a8498ab393..99b4a287dd 100755 --- a/configure +++ b/configure @@ -2524,20 +2524,7 @@ fi ########################################## # Windows Hypervisor Platform accelerator (WHPX) check if test "$whpx" != "no" ; then - cat > $TMPC << EOF -#include -#include -#include -int main(void) { - WHV_CAPABILITY whpx_cap; - UINT32 writtenSize; - WHvGetCapability(WHvCapabilityCodeFeatures, &whpx_cap, sizeof(whpx_cap), - &writtenSize); - return 0; -} -EOF - if compile_prog "" "-lWinHvPlatform -lWinHvEmulation" ; then - libs_softmmu="$libs_softmmu -lWinHvPlatform -lWinHvEmulation" + if check_include "WinHvPlatform.h" && check_include "WinHvEmulation.h"; then whpx="yes" else if test "$whpx" = "yes"; then diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c index bd7df10ba5..32695d47f9 100644 --- a/target/i386/whpx-all.c +++ b/target/i386/whpx-all.c @@ -24,6 +24,7 @@ #include "qemu/queue.h" #include "qapi/error.h" #include "migration/blocker.h" +#include "whp-dispatch.h" #include #include @@ -159,8 +160,11 @@ struct whpx_vcpu { }; static bool whpx_allowed; +static bool whp_dispatch_initialized; +static HMODULE hWinHvPlatform, hWinHvEmulation; struct whpx_state whpx_global; +struct WHPDispatch whp_dispatch; /* @@ -354,10 +358,11 @@ static void whpx_set_registers(CPUState *cpu) assert(idx == RTL_NUMBER_OF(whpx_register_names)); - hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, - whpx_register_names, - RTL_NUMBER_OF(whpx_register_names), - &vcxt.values[0]); + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + whpx_register_names, + RTL_NUMBER_OF(whpx_register_names), + &vcxt.values[0]); if (FAILED(hr)) { error_report("WHPX: Failed to set virtual processor context, hr=%08lx", @@ -381,10 +386,11 @@ static void whpx_get_registers(CPUState *cpu) assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); - hr = WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, - whpx_register_names, - RTL_NUMBER_OF(whpx_register_names), - &vcxt.values[0]); + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + whpx_register_names, + RTL_NUMBER_OF(whpx_register_names), + &vcxt.values[0]); if (FAILED(hr)) { error_report("WHPX: Failed to get virtual processor context, hr=%08lx", hr); @@ -544,9 +550,10 @@ static HRESULT CALLBACK whpx_emu_getreg_callback( struct whpx_state *whpx = &whpx_global; CPUState *cpu = (CPUState *)ctx; - hr = WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, - RegisterNames, RegisterCount, - RegisterValues); + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + RegisterNames, RegisterCount, + RegisterValues); if (FAILED(hr)) { error_report("WHPX: Failed to get virtual processor registers," " hr=%08lx", hr); @@ -565,9 +572,10 @@ static HRESULT CALLBACK whpx_emu_setreg_callback( struct whpx_state *whpx = &whpx_global; CPUState *cpu = (CPUState *)ctx; - hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, - RegisterNames, RegisterCount, - RegisterValues); + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + RegisterNames, RegisterCount, + RegisterValues); if (FAILED(hr)) { error_report("WHPX: Failed to set virtual processor registers," " hr=%08lx", hr); @@ -594,8 +602,8 @@ static HRESULT CALLBACK whpx_emu_translate_callback( CPUState *cpu = (CPUState *)ctx; WHV_TRANSLATE_GVA_RESULT res; - hr = WHvTranslateGva(whpx->partition, cpu->cpu_index, - Gva, TranslateFlags, &res, Gpa); + hr = whp_dispatch.WHvTranslateGva(whpx->partition, cpu->cpu_index, + Gva, TranslateFlags, &res, Gpa); if (FAILED(hr)) { error_report("WHPX: Failed to translate GVA, hr=%08lx", hr); } else { @@ -620,16 +628,18 @@ static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx) struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); WHV_EMULATOR_STATUS emu_status; - hr = WHvEmulatorTryMmioEmulation(vcpu->emulator, cpu, - &vcpu->exit_ctx.VpContext, ctx, - &emu_status); + hr = whp_dispatch.WHvEmulatorTryMmioEmulation( + vcpu->emulator, cpu, + &vcpu->exit_ctx.VpContext, ctx, + &emu_status); if (FAILED(hr)) { error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr); return -1; } if (!emu_status.EmulationSuccessful) { - error_report("WHPX: Failed to emulate MMIO access"); + error_report("WHPX: Failed to emulate MMIO access with" + " EmulatorReturnStatus: %u", emu_status.AsUINT32); return -1; } @@ -643,16 +653,18 @@ static int whpx_handle_portio(CPUState *cpu, struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); WHV_EMULATOR_STATUS emu_status; - hr = WHvEmulatorTryIoEmulation(vcpu->emulator, cpu, - &vcpu->exit_ctx.VpContext, ctx, - &emu_status); + hr = whp_dispatch.WHvEmulatorTryIoEmulation( + vcpu->emulator, cpu, + &vcpu->exit_ctx.VpContext, ctx, + &emu_status); if (FAILED(hr)) { error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr); return -1; } if (!emu_status.EmulationSuccessful) { - error_report("WHPX: Failed to emulate PortMMIO access"); + error_report("WHPX: Failed to emulate PortIO access with" + " EmulatorReturnStatus: %u", emu_status.AsUINT32); return -1; } @@ -767,8 +779,9 @@ static void whpx_vcpu_pre_run(CPUState *cpu) qemu_mutex_unlock_iothread(); if (reg_count) { - hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, - reg_names, reg_count, reg_values); + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + reg_names, reg_count, reg_values); if (FAILED(hr)) { error_report("WHPX: Failed to set interrupt state registers," " hr=%08lx", hr); @@ -876,8 +889,9 @@ static int whpx_vcpu_run(CPUState *cpu) whpx_vcpu_kick(cpu); } - hr = WHvRunVirtualProcessor(whpx->partition, cpu->cpu_index, - &vcpu->exit_ctx, sizeof(vcpu->exit_ctx)); + hr = whp_dispatch.WHvRunVirtualProcessor( + whpx->partition, cpu->cpu_index, + &vcpu->exit_ctx, sizeof(vcpu->exit_ctx)); if (FAILED(hr)) { error_report("WHPX: Failed to exec a virtual processor," @@ -948,11 +962,11 @@ static int whpx_vcpu_run(CPUState *cpu) reg_values[3].Reg64 = rdx; reg_values[4].Reg64 = rbx; - hr = WHvSetVirtualProcessorRegisters(whpx->partition, - cpu->cpu_index, - reg_names, - reg_count, - reg_values); + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + reg_names, + reg_count, + reg_values); if (FAILED(hr)) { error_report("WHPX: Failed to set CpuidAccess state registers," @@ -1064,8 +1078,8 @@ int whpx_init_vcpu(CPUState *cpu) (void)migrate_add_blocker(whpx_migration_blocker, &local_error); if (local_error) { error_report_err(local_error); - error_free(whpx_migration_blocker); migrate_del_blocker(whpx_migration_blocker); + error_free(whpx_migration_blocker); return -EINVAL; } } @@ -1077,7 +1091,9 @@ int whpx_init_vcpu(CPUState *cpu) return -ENOMEM; } - hr = WHvEmulatorCreateEmulator(&whpx_emu_callbacks, &vcpu->emulator); + hr = whp_dispatch.WHvEmulatorCreateEmulator( + &whpx_emu_callbacks, + &vcpu->emulator); if (FAILED(hr)) { error_report("WHPX: Failed to setup instruction completion support," " hr=%08lx", hr); @@ -1085,11 +1101,12 @@ int whpx_init_vcpu(CPUState *cpu) return -EINVAL; } - hr = WHvCreateVirtualProcessor(whpx->partition, cpu->cpu_index, 0); + hr = whp_dispatch.WHvCreateVirtualProcessor( + whpx->partition, cpu->cpu_index, 0); if (FAILED(hr)) { error_report("WHPX: Failed to create a virtual processor," " hr=%08lx", hr); - WHvEmulatorDestroyEmulator(vcpu->emulator); + whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator); g_free(vcpu); return -EINVAL; } @@ -1130,8 +1147,8 @@ void whpx_destroy_vcpu(CPUState *cpu) struct whpx_state *whpx = &whpx_global; struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); - WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index); - WHvEmulatorDestroyEmulator(vcpu->emulator); + whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index); + whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator); g_free(cpu->hax_vcpu); return; } @@ -1139,7 +1156,8 @@ void whpx_destroy_vcpu(CPUState *cpu) void whpx_vcpu_kick(CPUState *cpu) { struct whpx_state *whpx = &whpx_global; - WHvCancelRunVirtualProcessor(whpx->partition, cpu->cpu_index, 0); + whp_dispatch.WHvCancelRunVirtualProcessor( + whpx->partition, cpu->cpu_index, 0); } /* @@ -1165,17 +1183,17 @@ static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size, */ if (add) { - hr = WHvMapGpaRange(whpx->partition, - host_va, - start_pa, - size, - (WHvMapGpaRangeFlagRead | - WHvMapGpaRangeFlagExecute | - (rom ? 0 : WHvMapGpaRangeFlagWrite))); + hr = whp_dispatch.WHvMapGpaRange(whpx->partition, + host_va, + start_pa, + size, + (WHvMapGpaRangeFlagRead | + WHvMapGpaRangeFlagExecute | + (rom ? 0 : WHvMapGpaRangeFlagWrite))); } else { - hr = WHvUnmapGpaRange(whpx->partition, - start_pa, - size); + hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition, + start_pa, + size); } if (FAILED(hr)) { @@ -1289,18 +1307,24 @@ static int whpx_accel_init(MachineState *ms) whpx = &whpx_global; + if (!init_whp_dispatch()) { + ret = -ENOSYS; + goto error; + } + memset(whpx, 0, sizeof(struct whpx_state)); whpx->mem_quota = ms->ram_size; - hr = WHvGetCapability(WHvCapabilityCodeHypervisorPresent, &whpx_cap, - sizeof(whpx_cap), &whpx_cap_size); + hr = whp_dispatch.WHvGetCapability( + WHvCapabilityCodeHypervisorPresent, &whpx_cap, + sizeof(whpx_cap), &whpx_cap_size); if (FAILED(hr) || !whpx_cap.HypervisorPresent) { error_report("WHPX: No accelerator found, hr=%08lx", hr); ret = -ENOSPC; goto error; } - hr = WHvCreatePartition(&whpx->partition); + hr = whp_dispatch.WHvCreatePartition(&whpx->partition); if (FAILED(hr)) { error_report("WHPX: Failed to create partition, hr=%08lx", hr); ret = -EINVAL; @@ -1309,10 +1333,11 @@ static int whpx_accel_init(MachineState *ms) memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY)); prop.ProcessorCount = smp_cpus; - hr = WHvSetPartitionProperty(whpx->partition, - WHvPartitionPropertyCodeProcessorCount, - &prop, - sizeof(WHV_PARTITION_PROPERTY)); + hr = whp_dispatch.WHvSetPartitionProperty( + whpx->partition, + WHvPartitionPropertyCodeProcessorCount, + &prop, + sizeof(WHV_PARTITION_PROPERTY)); if (FAILED(hr)) { error_report("WHPX: Failed to set partition core count to %d," @@ -1323,10 +1348,11 @@ static int whpx_accel_init(MachineState *ms) memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY)); prop.ExtendedVmExits.X64CpuidExit = 1; - hr = WHvSetPartitionProperty(whpx->partition, - WHvPartitionPropertyCodeExtendedVmExits, - &prop, - sizeof(WHV_PARTITION_PROPERTY)); + hr = whp_dispatch.WHvSetPartitionProperty( + whpx->partition, + WHvPartitionPropertyCodeExtendedVmExits, + &prop, + sizeof(WHV_PARTITION_PROPERTY)); if (FAILED(hr)) { error_report("WHPX: Failed to enable partition extended X64CpuidExit" @@ -1336,11 +1362,11 @@ static int whpx_accel_init(MachineState *ms) } UINT32 cpuidExitList[] = {1}; - hr = WHvSetPartitionProperty(whpx->partition, - WHvPartitionPropertyCodeCpuidExitList, - cpuidExitList, - RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32)); - + hr = whp_dispatch.WHvSetPartitionProperty( + whpx->partition, + WHvPartitionPropertyCodeCpuidExitList, + cpuidExitList, + RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32)); if (FAILED(hr)) { error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx", hr); @@ -1348,7 +1374,7 @@ static int whpx_accel_init(MachineState *ms) goto error; } - hr = WHvSetupPartition(whpx->partition); + hr = whp_dispatch.WHvSetupPartition(whpx->partition); if (FAILED(hr)) { error_report("WHPX: Failed to setup partition, hr=%08lx", hr); ret = -EINVAL; @@ -1365,7 +1391,7 @@ static int whpx_accel_init(MachineState *ms) error: if (NULL != whpx->partition) { - WHvDeletePartition(whpx->partition); + whp_dispatch.WHvDeletePartition(whpx->partition); whpx->partition = NULL; } @@ -1397,4 +1423,54 @@ static void whpx_type_init(void) type_register_static(&whpx_accel_type); } +bool init_whp_dispatch(void) +{ + const char *lib_name; + HMODULE hLib; + + if (whp_dispatch_initialized) { + return true; + } + + #define WHP_LOAD_FIELD(return_type, function_name, signature) \ + whp_dispatch.function_name = \ + (function_name ## _t)GetProcAddress(hLib, #function_name); \ + if (!whp_dispatch.function_name) { \ + error_report("Could not load function %s from library %s.", \ + #function_name, lib_name); \ + goto error; \ + } \ + + lib_name = "WinHvPlatform.dll"; + hWinHvPlatform = LoadLibrary(lib_name); + if (!hWinHvPlatform) { + error_report("Could not load library %s.", lib_name); + goto error; + } + hLib = hWinHvPlatform; + LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD) + + lib_name = "WinHvEmulation.dll"; + hWinHvEmulation = LoadLibrary(lib_name); + if (!hWinHvEmulation) { + error_report("Could not load library %s.", lib_name); + goto error; + } + hLib = hWinHvEmulation; + LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD) + + whp_dispatch_initialized = true; + return true; + + error: + + if (hWinHvPlatform) { + FreeLibrary(hWinHvPlatform); + } + if (hWinHvEmulation) { + FreeLibrary(hWinHvEmulation); + } + return false; +} + type_init(whpx_type_init); From c3942bf2bdcc80fcf38379cf26db5d02b07f41cf Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Tue, 15 May 2018 20:35:22 +0300 Subject: [PATCH 0918/2380] WHPX: fix some compiler warnings This patch fixes a few compiler warnings, especially in case of x86 targets, where the number of registers was not properly handled and could cause an overflow. Signed-off-by: Alessandro Pilotti Signed-off-by: Justin Terry (VM) Signed-off-by: Lucian Petrut Message-Id: <1526405722-10887-3-git-send-email-lpetrut@cloudbasesolutions.com> Signed-off-by: Paolo Bonzini --- target/i386/whpx-all.c | 49 ++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c index 32695d47f9..6b42096698 100644 --- a/target/i386/whpx-all.c +++ b/target/i386/whpx-all.c @@ -223,24 +223,31 @@ static void whpx_set_registers(CPUState *cpu) struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); X86CPU *x86_cpu = X86_CPU(cpu); - struct whpx_register_set vcxt = {0}; + struct whpx_register_set vcxt; HRESULT hr; - int idx = 0; + int idx; + int idx_next; int i; int v86, r86; assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + memset(&vcxt, 0, sizeof(struct whpx_register_set)); + v86 = (env->eflags & VM_MASK); r86 = !(env->cr[0] & CR0_PE_MASK); vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state); vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state); + idx = 0; + /* Indexes for first 16 registers match between HV and QEMU definitions */ - for (idx = 0; idx < CPU_NB_REGS64; idx += 1) { - vcxt.values[idx].Reg64 = env->regs[idx]; + idx_next = 16; + for (idx = 0; idx < CPU_NB_REGS; idx += 1) { + vcxt.values[idx].Reg64 = (uint64_t)env->regs[idx]; } + idx = idx_next; /* Same goes for RIP and RFLAGS */ assert(whpx_register_names[idx] == WHvX64RegisterRip); @@ -287,10 +294,12 @@ static void whpx_set_registers(CPUState *cpu) /* 16 XMM registers */ assert(whpx_register_names[idx] == WHvX64RegisterXmm0); - for (i = 0; i < 16; i += 1, idx += 1) { + idx_next = idx + 16; + for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) { vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0); vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1); } + idx = idx_next; /* 8 FP registers */ assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0); @@ -381,7 +390,8 @@ static void whpx_get_registers(CPUState *cpu) struct whpx_register_set vcxt; uint64_t tpr, apic_base; HRESULT hr; - int idx = 0; + int idx; + int idx_next; int i; assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); @@ -396,10 +406,14 @@ static void whpx_get_registers(CPUState *cpu) hr); } + idx = 0; + /* Indexes for first 16 registers match between HV and QEMU definitions */ - for (idx = 0; idx < CPU_NB_REGS64; idx += 1) { + idx_next = 16; + for (idx = 0; idx < CPU_NB_REGS; idx += 1) { env->regs[idx] = vcxt.values[idx].Reg64; } + idx = idx_next; /* Same goes for RIP and RFLAGS */ assert(whpx_register_names[idx] == WHvX64RegisterRip); @@ -446,10 +460,12 @@ static void whpx_get_registers(CPUState *cpu) /* 16 XMM registers */ assert(whpx_register_names[idx] == WHvX64RegisterXmm0); - for (i = 0; i < 16; i += 1, idx += 1) { + idx_next = idx + 16; + for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) { env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64; env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64; } + idx = idx_next; /* 8 FP registers */ assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0); @@ -698,11 +714,14 @@ static void whpx_vcpu_pre_run(CPUState *cpu) X86CPU *x86_cpu = X86_CPU(cpu); int irq; uint8_t tpr; - WHV_X64_PENDING_INTERRUPTION_REGISTER new_int = {0}; + WHV_X64_PENDING_INTERRUPTION_REGISTER new_int; UINT32 reg_count = 0; - WHV_REGISTER_VALUE reg_values[3] = {0}; + WHV_REGISTER_VALUE reg_values[3]; WHV_REGISTER_NAME reg_names[3]; + memset(&new_int, 0, sizeof(new_int)); + memset(reg_values, 0, sizeof(reg_values)); + qemu_mutex_lock_iothread(); /* Inject NMI */ @@ -925,11 +944,13 @@ static int whpx_vcpu_run(CPUState *cpu) break; case WHvRunVpExitReasonX64Cpuid: { - WHV_REGISTER_VALUE reg_values[5] = {0}; + WHV_REGISTER_VALUE reg_values[5]; WHV_REGISTER_NAME reg_names[5]; UINT32 reg_count = 5; UINT64 rip, rax, rcx, rdx, rbx; + memset(reg_values, 0, sizeof(reg_values)); + rip = vcpu->exit_ctx.VpContext.Rip + vcpu->exit_ctx.VpContext.InstructionLength; switch (vcpu->exit_ctx.CpuidAccess.Rax) { @@ -1200,7 +1221,7 @@ static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size, error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes," " Host:%p, hr=%08lx", (add ? "MAP" : "UNMAP"), name, - (void *)start_pa, (void *)size, host_va, hr); + (void *)(uintptr_t)start_pa, (void *)size, host_va, hr); } } @@ -1231,8 +1252,8 @@ static void whpx_process_section(MemoryRegionSection *section, int add) host_va = (uintptr_t)memory_region_get_ram_ptr(mr) + section->offset_within_region + delta; - whpx_update_mapping(start_pa, size, (void *)host_va, add, - memory_region_is_rom(mr), mr->name); + whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add, + memory_region_is_rom(mr), mr->name); } static void whpx_region_add(MemoryListener *listener, From d7e19545215e529d7bbc4ef555c123b95cbe35a1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 22 May 2018 13:58:18 +0200 Subject: [PATCH 0919/2380] qemu-options: Mark the non-functional -clock option as deprecated The function is only ignored since QEMU version 1.7.0. Let's mark it as deprecated, so that we can finally completely remove it soon. Signed-off-by: Thomas Huth Message-Id: <1526990298-17924-1-git-send-email-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- qemu-doc.texi | 5 +++++ vl.c | 1 + 2 files changed, 6 insertions(+) diff --git a/qemu-doc.texi b/qemu-doc.texi index 0e0e0ae72b..057a48ec1f 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2922,6 +2922,11 @@ The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}. Option @option{-virtioconsole} has been replaced by @option{-device virtconsole}. +@subsection -clock (since 3.0.0) + +The @code{-clock} option is ignored since QEMU version 1.7.0. There is no +replacement since it is not needed anymore. + @section qemu-img command line arguments @subsection convert -s (since 2.0.0) diff --git a/vl.c b/vl.c index 4a0e17833d..c70ce25de7 100644 --- a/vl.c +++ b/vl.c @@ -3713,6 +3713,7 @@ int main(int argc, char **argv, char **envp) /* Clock options no longer exist. Keep this option for * backward compatibility. */ + warn_report("This option is ignored and will be removed soon"); break; case QEMU_OPTION_startdate: warn_report("This option is deprecated, use '-rtc base=' instead."); From 8f971cf0c9265fa5d9f06a195f119237e403c0ce Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 22 May 2018 21:20:03 +0200 Subject: [PATCH 0920/2380] tcg: remove softfloat from --disable-tcg builds Even though the presence of softfloat does not cause --disable-tcg builds to fail, it is the single largest .o file in them. Remove it, since TCG is the only client. Acked-by: Richard Henderson Signed-off-by: Paolo Bonzini --- Makefile.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.target b/Makefile.target index d0ec77a307..dad2cf8778 100644 --- a/Makefile.target +++ b/Makefile.target @@ -97,7 +97,7 @@ obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o -obj-y += fpu/softfloat.o +obj-$(CONFIG_TCG) += fpu/softfloat.o obj-y += target/$(TARGET_BASE_ARCH)/ obj-y += disas.o obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o From 2dc6660bd8db0bfab0be91135cb0f6d83940d190 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Wed, 25 Apr 2018 10:27:30 -0500 Subject: [PATCH 0921/2380] vmstate: Add a VSTRUCT type The VMS_STRUCT has no way to specify which version of a structure to use. Add a type and a new field to allow the specific version of a structure to be used. Signed-off-by: Corey Minyard Message-Id: <1524670052-28373-2-git-send-email-minyard@acm.org> Signed-off-by: Paolo Bonzini --- include/migration/vmstate.h | 45 +++++++++++++++++++++++++++++++++++++ migration/vmstate.c | 25 +++++++++++++++++---- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index df463fd33d..efa0a1150e 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -143,6 +143,11 @@ enum VMStateFlags { * to determine the number of entries in the array. Only valid in * combination with one of VMS_VARRAY*. */ VMS_MULTIPLY_ELEMENTS = 0x4000, + + /* A structure field that is like VMS_STRUCT, but uses + * VMStateField.struct_version_id to tell which version of the + * structure we are referencing to use. */ + VMS_VSTRUCT = 0x8000, }; typedef enum { @@ -167,6 +172,7 @@ struct VMStateField { enum VMStateFlags flags; const VMStateDescription *vmsd; int version_id; + int struct_version_id; bool (*field_exists)(void *opaque, int version_id); }; @@ -248,6 +254,25 @@ extern const VMStateInfo vmstate_info_qtailq; vmstate_offset_array(_state, _field, uint8_t, \ sizeof(typeof_field(_state, _field))) +/* In the macros below, if there is a _version, that means the macro's + * field will be processed only if the version being received is >= + * the _version specified. In general, if you add a new field, you + * would increment the structure's version and put that version + * number into the new field so it would only be processed with the + * new version. + * + * In particular, for VMSTATE_STRUCT() and friends the _version does + * *NOT* pick the version of the sub-structure. It works just as + * specified above. The version of the top-level structure received + * is passed down to all sub-structures. This means that the + * sub-structures must have version that are compatible with all the + * structures that use them. + * + * If you want to specify the version of the sub-structure, use + * VMSTATE_VSTRUCT(), which allows the specific sub-structure version + * to be directly specified. + */ + #define VMSTATE_SINGLE_TEST(_field, _state, _test, _version, _info, _type) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -395,6 +420,17 @@ extern const VMStateInfo vmstate_info_qtailq; .offset = offsetof(_state, _field), \ } +#define VMSTATE_VSTRUCT_TEST(_field, _state, _test, _version, _vmsd, _type, _struct_version) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .struct_version_id = (_struct_version), \ + .field_exists = (_test), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_VSTRUCT, \ + .offset = vmstate_offset_value(_state, _field, _type), \ +} + #define VMSTATE_STRUCT_TEST(_field, _state, _test, _version, _vmsd, _type) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -712,6 +748,13 @@ extern const VMStateInfo vmstate_info_qtailq; #define VMSTATE_SINGLE(_field, _state, _version, _info, _type) \ VMSTATE_SINGLE_TEST(_field, _state, NULL, _version, _info, _type) +#define VMSTATE_VSTRUCT(_field, _state, _vmsd, _type, _struct_version)\ + VMSTATE_VSTRUCT_TEST(_field, _state, NULL, 0, _vmsd, _type, _struct_version) + +#define VMSTATE_VSTRUCT_V(_field, _state, _version, _vmsd, _type, _struct_version) \ + VMSTATE_VSTRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type, \ + _struct_version) + #define VMSTATE_STRUCT(_field, _state, _version, _vmsd, _type) \ VMSTATE_STRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type) @@ -1000,6 +1043,8 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id); int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, QJSON *vmdesc); +int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque, QJSON *vmdesc, int version_id); bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque); diff --git a/migration/vmstate.c b/migration/vmstate.c index 0b3282c9df..f0b07f4ea1 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -136,6 +136,9 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, } else if (field->flags & VMS_STRUCT) { ret = vmstate_load_state(f, field->vmsd, curr_elem, field->vmsd->version_id); + } else if (field->flags & VMS_VSTRUCT) { + ret = vmstate_load_state(f, field->vmsd, curr_elem, + field->struct_version_id); } else { ret = field->info->get(f, curr_elem, size, field); } @@ -209,6 +212,8 @@ static const char *vmfield_get_type_name(VMStateField *field) if (field->flags & VMS_STRUCT) { type = "struct"; + } else if (field->flags & VMS_VSTRUCT) { + type = "vstruct"; } else if (field->info->name) { type = field->info->name; } @@ -309,7 +314,13 @@ bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque) int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, QJSON *vmdesc) + void *opaque, QJSON *vmdesc_id) +{ + return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id); +} + +int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque, QJSON *vmdesc, int version_id) { int ret = 0; VMStateField *field = vmsd->fields; @@ -327,13 +338,15 @@ int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, if (vmdesc) { json_prop_str(vmdesc, "vmsd_name", vmsd->name); - json_prop_int(vmdesc, "version", vmsd->version_id); + json_prop_int(vmdesc, "version", version_id); json_start_array(vmdesc, "fields"); } while (field->name) { - if (!field->field_exists || - field->field_exists(opaque, vmsd->version_id)) { + if ((field->field_exists && + field->field_exists(opaque, version_id)) || + (!field->field_exists && + field->version_id <= version_id)) { void *first_elem = opaque + field->offset; int i, n_elems = vmstate_n_elems(opaque, field); int size = vmstate_size(opaque, field); @@ -363,6 +376,10 @@ int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, } else if (field->flags & VMS_STRUCT) { ret = vmstate_save_state(f, field->vmsd, curr_elem, vmdesc_loop); + } else if (field->flags & VMS_VSTRUCT) { + ret = vmstate_save_state_v(f, field->vmsd, curr_elem, + vmdesc_loop, + field->struct_version_id); } else { ret = field->info->put(f, curr_elem, size, field, vmdesc_loop); From 7e57b82ec3f1a87f623e4baef9ef1ba2b157935c Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Wed, 25 Apr 2018 10:27:31 -0500 Subject: [PATCH 0922/2380] ipmi: Use proper struct reference for KCS vmstate The vmstate for isa_ipmi_kcs was referencing into the kcs structure, instead create a kcs structure separate and use that. There were also some issues in the state transfer. The inlen field was not being transferred, so if a transaction was in process during the transfer it would be messed up. And the use_irq field was transferred, but that should come from the configuration. To fix this, the new VMS_VSTRUCT macros are used so the exact version of the structure can be specified, depending on what version was being received. So an upgrade should work for KCS. Signed-off-by: Corey Minyard Cc: Dr. David Alan Gilbert Message-Id: <1524670052-28373-3-git-send-email-minyard@acm.org> Signed-off-by: Paolo Bonzini --- hw/ipmi/isa_ipmi_kcs.c | 81 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 15 deletions(-) diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index 689587b65d..a79431554a 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/ipmi/ipmi.h" @@ -422,24 +423,69 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); } -const VMStateDescription vmstate_ISAIPMIKCSDevice = { +static int ipmi_kcs_vmstate_post_load(void *opaque, int version) +{ + IPMIKCS *ik = opaque; + + /* Make sure all the values are sane. */ + if (ik->outpos >= MAX_IPMI_MSG_SIZE || ik->outlen >= MAX_IPMI_MSG_SIZE || + ik->outpos >= ik->outlen) { + qemu_log_mask(LOG_GUEST_ERROR, + "ipmi:kcs: vmstate transfer received bad out values: %d %d\n", + ik->outpos, ik->outlen); + ik->outpos = 0; + ik->outlen = 0; + } + + if (ik->inlen >= MAX_IPMI_MSG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "ipmi:kcs: vmstate transfer received bad in value: %d\n", + ik->inlen); + ik->inlen = 0; + } + + return 0; +} + +static bool vmstate_kcs_before_version2(void *opaque, int version) +{ + return version <= 1; +} + +static const VMStateDescription vmstate_IPMIKCS = { + .name = TYPE_IPMI_INTERFACE_PREFIX "kcs", + .version_id = 2, + .minimum_version_id = 1, + .post_load = ipmi_kcs_vmstate_post_load, + .fields = (VMStateField[]) { + VMSTATE_BOOL(obf_irq_set, IPMIKCS), + VMSTATE_BOOL(atn_irq_set, IPMIKCS), + VMSTATE_UNUSED_TEST(vmstate_kcs_before_version2, 1), /* Was use_irq */ + VMSTATE_BOOL(irqs_enabled, IPMIKCS), + VMSTATE_UINT32(outpos, IPMIKCS), + VMSTATE_UINT32_V(outlen, IPMIKCS, 2), + VMSTATE_UINT8_ARRAY(outmsg, IPMIKCS, MAX_IPMI_MSG_SIZE), + VMSTATE_UINT32_V(inlen, IPMIKCS, 2), + VMSTATE_UINT8_ARRAY(inmsg, IPMIKCS, MAX_IPMI_MSG_SIZE), + VMSTATE_BOOL(write_end, IPMIKCS), + VMSTATE_UINT8(status_reg, IPMIKCS), + VMSTATE_UINT8(data_out_reg, IPMIKCS), + VMSTATE_INT16(data_in_reg, IPMIKCS), + VMSTATE_INT16(cmd_reg, IPMIKCS), + VMSTATE_UINT8(waiting_rsp, IPMIKCS), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_ISAIPMIKCSDevice = { .name = TYPE_IPMI_INTERFACE, - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_BOOL(kcs.obf_irq_set, ISAIPMIKCSDevice), - VMSTATE_BOOL(kcs.atn_irq_set, ISAIPMIKCSDevice), - VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice), - VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice), - VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice), - VMSTATE_UINT8_ARRAY(kcs.outmsg, ISAIPMIKCSDevice, MAX_IPMI_MSG_SIZE), - VMSTATE_UINT8_ARRAY(kcs.inmsg, ISAIPMIKCSDevice, MAX_IPMI_MSG_SIZE), - VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice), - VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice), - VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice), - VMSTATE_INT16(kcs.data_in_reg, ISAIPMIKCSDevice), - VMSTATE_INT16(kcs.cmd_reg, ISAIPMIKCSDevice), - VMSTATE_UINT8(kcs.waiting_rsp, ISAIPMIKCSDevice), + VMSTATE_VSTRUCT_TEST(kcs, ISAIPMIKCSDevice, vmstate_kcs_before_version2, + 0, vmstate_IPMIKCS, IPMIKCS, 1), + VMSTATE_VSTRUCT_V(kcs, ISAIPMIKCSDevice, 2, vmstate_IPMIKCS, + IPMIKCS, 2), VMSTATE_END_OF_LIST() } }; @@ -450,6 +496,11 @@ static void isa_ipmi_kcs_init(Object *obj) ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc); + /* + * Version 1 had an incorrect name, it clashed with the BT + * IPMI device, so receive it, but transmit a different + * version. + */ vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik); } From 3a0adfc9bfcf217017bfc49d00c9a9b845e7118d Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Wed, 9 May 2018 17:26:08 +0200 Subject: [PATCH 0923/2380] docs/interop: add "firmware.json" Add a schema that describes the different uses and properties of virtual machine firmware. Each firmware executable installed on a host system should come with at least one JSON file that conforms to this schema. Each file informs the management applications about - the firmware's properties and one possible use case / feature set, - configuration bits that are required to run the firmware binary. In addition, define rules for management apps for picking the highest priority firmware JSON file when multiple such files match the search criteria. Cc: "Daniel P. Berrange" Cc: David Gibson Cc: Eric Blake Cc: Gerd Hoffmann Cc: Kashyap Chamarthy Cc: Markus Armbruster Cc: Paolo Bonzini Cc: Thomas Huth Reviewed-by: Gerd Hoffmann Signed-off-by: Laszlo Ersek Message-Id: <20180509152608.9343-1-lersek@redhat.com> Signed-off-by: Paolo Bonzini --- docs/interop/firmware.json | 540 +++++++++++++++++++++++++++++++++++++ 1 file changed, 540 insertions(+) create mode 100644 docs/interop/firmware.json diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json new file mode 100644 index 0000000000..28f9bc1591 --- /dev/null +++ b/docs/interop/firmware.json @@ -0,0 +1,540 @@ +# -*- Mode: Python -*- +# +# Copyright (C) 2018 Red Hat, Inc. +# +# Authors: +# Daniel P. Berrange +# Laszlo Ersek +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +## +# = Firmware +## + +{ 'include' : 'common.json' } +{ 'include' : 'block-core.json' } + +## +# @FirmwareOSInterface: +# +# Lists the firmware-OS interface types provided by various firmware +# that is commonly used with QEMU virtual machines. +# +# @bios: Traditional x86 BIOS interface. For example, firmware built +# from the SeaBIOS project usually provides this interface. +# +# @openfirmware: The interface is defined by the (historical) IEEE +# 1275-1994 standard. Examples for firmware projects that +# provide this interface are: OpenBIOS, OpenHackWare, +# SLOF. +# +# @uboot: Firmware interface defined by the U-Boot project. +# +# @uefi: Firmware interface defined by the UEFI specification. For +# example, firmware built from the edk2 (EFI Development Kit II) +# project usually provides this interface. +# +# Since: 3.0 +## +{ 'enum' : 'FirmwareOSInterface', + 'data' : [ 'bios', 'openfirmware', 'uboot', 'uefi' ] } + +## +# @FirmwareDevice: +# +# Defines the device types that firmware can be mapped into. +# +# @flash: The firmware executable and its accompanying NVRAM file are to +# be mapped into a pflash chip each. +# +# @kernel: The firmware is to be loaded like a Linux kernel. This is +# similar to @memory but may imply additional processing that +# is specific to the target architecture and machine type. +# +# @memory: The firmware is to be mapped into memory. +# +# Since: 3.0 +## +{ 'enum' : 'FirmwareDevice', + 'data' : [ 'flash', 'kernel', 'memory' ] } + +## +# @FirmwareTarget: +# +# Defines the machine types that firmware may execute on. +# +# @architecture: Determines the emulation target (the QEMU system +# emulator) that can execute the firmware. +# +# @machines: Lists the machine types (known by the emulator that is +# specified through @architecture) that can execute the +# firmware. Elements of @machines are supposed to be concrete +# machine types, not aliases. Glob patterns are understood, +# which is especially useful for versioned machine types. +# (For example, the glob pattern "pc-i440fx-*" matches +# "pc-i440fx-2.12".) On the QEMU command line, "-machine +# type=..." specifies the requested machine type (but that +# option does not accept glob patterns). +# +# Since: 3.0 +## +{ 'struct' : 'FirmwareTarget', + 'data' : { 'architecture' : 'SysEmuTarget', + 'machines' : [ 'str' ] } } + +## +# @FirmwareFeature: +# +# Defines the features that firmware may support, and the platform +# requirements that firmware may present. +# +# @acpi-s3: The firmware supports S3 sleep (suspend to RAM), as defined +# in the ACPI specification. On the "pc-i440fx-*" machine +# types of the @i386 and @x86_64 emulation targets, S3 can be +# enabled with "-global PIIX4_PM.disable_s3=0" and disabled +# with "-global PIIX4_PM.disable_s3=1". On the "pc-q35-*" +# machine types of the @i386 and @x86_64 emulation targets, S3 +# can be enabled with "-global ICH9-LPC.disable_s3=0" and +# disabled with "-global ICH9-LPC.disable_s3=1". +# +# @acpi-s4: The firmware supports S4 hibernation (suspend to disk), as +# defined in the ACPI specification. On the "pc-i440fx-*" +# machine types of the @i386 and @x86_64 emulation targets, S4 +# can be enabled with "-global PIIX4_PM.disable_s4=0" and +# disabled with "-global PIIX4_PM.disable_s4=1". On the +# "pc-q35-*" machine types of the @i386 and @x86_64 emulation +# targets, S4 can be enabled with "-global +# ICH9-LPC.disable_s4=0" and disabled with "-global +# ICH9-LPC.disable_s4=1". +# +# @amd-sev: The firmware supports running under AMD Secure Encrypted +# Virtualization, as specified in the AMD64 Architecture +# Programmer's Manual. QEMU command line options related to +# this feature are documented in +# "docs/amd-memory-encryption.txt". +# +# @enrolled-keys: The variable store (NVRAM) template associated with +# the firmware binary has the UEFI Secure Boot +# operational mode turned on, with certificates +# enrolled. +# +# @requires-smm: The firmware requires the platform to emulate SMM +# (System Management Mode), as defined in the AMD64 +# Architecture Programmer's Manual, and in the Intel(R)64 +# and IA-32 Architectures Software Developer's Manual. On +# the "pc-q35-*" machine types of the @i386 and @x86_64 +# emulation targets, SMM emulation can be enabled with +# "-machine smm=on". (On the "pc-q35-*" machine types of +# the @i386 emulation target, @requires-smm presents +# further CPU requirements; one combination known to work +# is "-cpu coreduo,-nx".) If the firmware is marked as +# both @secure-boot and @requires-smm, then write +# accesses to the pflash chip (NVRAM) that holds the UEFI +# variable store must be restricted to code that executes +# in SMM, using the additional option "-global +# driver=cfi.pflash01,property=secure,value=on". +# Furthermore, a large guest-physical address space +# (comprising guest RAM, memory hotplug range, and 64-bit +# PCI MMIO aperture), and/or a high VCPU count, may +# present high SMRAM requirements from the firmware. On +# the "pc-q35-*" machine types of the @i386 and @x86_64 +# emulation targets, the SMRAM size may be increased +# above the default 16MB with the "-global +# mch.extended-tseg-mbytes=uint16" option. As a rule of +# thumb, the default 16MB size suffices for 1TB of +# guest-phys address space and a few tens of VCPUs; for +# every further TB of guest-phys address space, add 8MB +# of SMRAM. 48MB should suffice for 4TB of guest-phys +# address space and 2-3 hundred VCPUs. +# +# @secure-boot: The firmware implements the software interfaces for UEFI +# Secure Boot, as defined in the UEFI specification. Note +# that without @requires-smm, guest code running with +# kernel privileges can undermine the security of Secure +# Boot. +# +# @verbose-dynamic: When firmware log capture is enabled, the firmware +# logs a large amount of debug messages, which may +# impact boot performance. With log capture disabled, +# there is no boot performance impact. On the +# "pc-i440fx-*" and "pc-q35-*" machine types of the +# @i386 and @x86_64 emulation targets, firmware log +# capture can be enabled with the QEMU command line +# options "-chardev file,id=fwdebug,path=LOGFILEPATH +# -device isa-debugcon,iobase=0x402,chardev=fwdebug". +# @verbose-dynamic is mutually exclusive with +# @verbose-static. +# +# @verbose-static: The firmware unconditionally produces a large amount +# of debug messages, which may impact boot performance. +# This feature may typically be carried by certain UEFI +# firmware for the "virt-*" machine types of the @arm +# and @aarch64 emulation targets, where the debug +# messages are written to the first (always present) +# PL011 UART. @verbose-static is mutually exclusive +# with @verbose-dynamic. +# +# Since: 3.0 +## +{ 'enum' : 'FirmwareFeature', + 'data' : [ 'acpi-s3', 'acpi-s4', 'amd-sev', 'enrolled-keys', + 'requires-smm', 'secure-boot', 'verbose-dynamic', + 'verbose-static' ] } + +## +# @FirmwareFlashFile: +# +# Defines common properties that are necessary for loading a firmware +# file into a pflash chip. The corresponding QEMU command line option is +# "-drive file=@filename,format=@format". Note however that the +# option-argument shown here is incomplete; it is completed under +# @FirmwareMappingFlash. +# +# @filename: Specifies the filename on the host filesystem where the +# firmware file can be found. +# +# @format: Specifies the block format of the file pointed-to by +# @filename, such as @raw or @qcow2. +# +# Since: 3.0 +## +{ 'struct' : 'FirmwareFlashFile', + 'data' : { 'filename' : 'str', + 'format' : 'BlockdevDriver' } } + +## +# @FirmwareMappingFlash: +# +# Describes loading and mapping properties for the firmware executable +# and its accompanying NVRAM file, when @FirmwareDevice is @flash. +# +# @executable: Identifies the firmware executable. The firmware +# executable may be shared by multiple virtual machine +# definitions. The corresponding QEMU command line option +# is "-drive +# if=pflash,unit=0,readonly=on,file=@executable.@filename,format=@executable.@format". +# +# @nvram-template: Identifies the NVRAM template compatible with +# @executable. Management software instantiates an +# individual copy -- a specific NVRAM file -- from +# @nvram-template.@filename for each new virtual +# machine definition created. @nvram-template.@filename +# itself is never mapped into virtual machines, only +# individual copies of it are. An NVRAM file is +# typically used for persistently storing the +# non-volatile UEFI variables of a virtual machine +# definition. The corresponding QEMU command line +# option is "-drive +# if=pflash,unit=1,readonly=off,file=FILENAME_OF_PRIVATE_NVRAM_FILE,format=@nvram-template.@format". +# +# Since: 3.0 +## +{ 'struct' : 'FirmwareMappingFlash', + 'data' : { 'executable' : 'FirmwareFlashFile', + 'nvram-template' : 'FirmwareFlashFile' } } + +## +# @FirmwareMappingKernel: +# +# Describes loading and mapping properties for the firmware executable, +# when @FirmwareDevice is @kernel. +# +# @filename: Identifies the firmware executable. The firmware executable +# may be shared by multiple virtual machine definitions. The +# corresponding QEMU command line option is "-kernel +# @filename". +# +# Since: 3.0 +## +{ 'struct' : 'FirmwareMappingKernel', + 'data' : { 'filename' : 'str' } } + +## +# @FirmwareMappingMemory: +# +# Describes loading and mapping properties for the firmware executable, +# when @FirmwareDevice is @memory. +# +# @filename: Identifies the firmware executable. The firmware executable +# may be shared by multiple virtual machine definitions. The +# corresponding QEMU command line option is "-bios +# @filename". +# +# Since: 3.0 +## +{ 'struct' : 'FirmwareMappingMemory', + 'data' : { 'filename' : 'str' } } + +## +# @FirmwareMapping: +# +# Provides a discriminated structure for firmware to describe its +# loading / mapping properties. +# +# @device: Selects the device type that the firmware must be mapped +# into. +# +# Since: 3.0 +## +{ 'union' : 'FirmwareMapping', + 'base' : { 'device' : 'FirmwareDevice' }, + 'discriminator' : 'device', + 'data' : { 'flash' : 'FirmwareMappingFlash', + 'kernel' : 'FirmwareMappingKernel', + 'memory' : 'FirmwareMappingMemory' } } + +## +# @Firmware: +# +# Describes a firmware (or a firmware use case) to management software. +# +# It is possible for multiple @Firmware elements to match the search +# criteria of management software. Applications thus need rules to pick +# one of the many matches, and users need the ability to override distro +# defaults. +# +# It is recommended to create firmware JSON files (each containing a +# single @Firmware root element) with a double-digit prefix, for example +# "50-ovmf.json", "50-seabios-256k.json", etc, so they can be sorted in +# predictable order. The firmware JSON files should be searched for in +# three directories: +# +# - /usr/share/qemu/firmware -- populated by distro-provided firmware +# packages (XDG_DATA_DIRS covers +# /usr/share by default), +# +# - /etc/qemu/firmware -- exclusively for sysadmins' local additions, +# +# - $XDG_CONFIG_HOME/qemu/firmware -- exclusively for per-user local +# additions (XDG_CONFIG_HOME +# defaults to $HOME/.config). +# +# Top-down, the list of directories goes from general to specific. +# +# Management software should build a list of files from all three +# locations, then sort the list by filename (i.e., last pathname +# component). Management software should choose the first JSON file on +# the sorted list that matches the search criteria. If a more specific +# directory has a file with same name as a less specific directory, then +# the file in the more specific directory takes effect. If the more +# specific file is zero length, it hides the less specific one. +# +# For example, if a distro ships +# +# - /usr/share/qemu/firmware/50-ovmf.json +# +# - /usr/share/qemu/firmware/50-seabios-256k.json +# +# then the sysadmin can prevent the default OVMF being used at all with +# +# $ touch /etc/qemu/firmware/50-ovmf.json +# +# The sysadmin can replace/alter the distro default OVMF with +# +# $ vim /etc/qemu/firmware/50-ovmf.json +# +# or they can provide a parallel OVMF with higher priority +# +# $ vim /etc/qemu/firmware/10-ovmf.json +# +# or they can provide a parallel OVMF with lower priority +# +# $ vim /etc/qemu/firmware/99-ovmf.json +# +# @description: Provides a human-readable description of the firmware. +# Management software may or may not display @description. +# +# @interface-types: Lists the types of interfaces that the firmware can +# expose to the guest OS. This is a non-empty, ordered +# list; entries near the beginning of @interface-types +# are considered more native to the firmware, and/or +# to have a higher quality implementation in the +# firmware, than entries near the end of +# @interface-types. +# +# @mapping: Describes the loading / mapping properties of the firmware. +# +# @targets: Collects the target architectures (QEMU system emulators) +# and their machine types that may execute the firmware. +# +# @features: Lists the features that the firmware supports, and the +# platform requirements it presents. +# +# @tags: A list of auxiliary strings associated with the firmware for +# which @description is not appropriate, due to the latter's +# possible exposure to the end-user. @tags serves development and +# debugging purposes only, and management software shall +# explicitly ignore it. +# +# Since: 3.0 +# +# Examples: +# +# { +# "description": "SeaBIOS", +# "interface-types": [ +# "bios" +# ], +# "mapping": { +# "device": "memory", +# "filename": "/usr/share/seabios/bios-256k.bin" +# }, +# "targets": [ +# { +# "architecture": "i386", +# "machines": [ +# "pc-i440fx-*", +# "pc-q35-*" +# ] +# }, +# { +# "architecture": "x86_64", +# "machines": [ +# "pc-i440fx-*", +# "pc-q35-*" +# ] +# } +# ], +# "features": [ +# "acpi-s3", +# "acpi-s4" +# ], +# "tags": [ +# "CONFIG_BOOTSPLASH=n", +# "CONFIG_ROM_SIZE=256", +# "CONFIG_USE_SMM=n" +# ] +# } +# +# { +# "description": "OVMF with SB+SMM, empty varstore", +# "interface-types": [ +# "uefi" +# ], +# "mapping": { +# "device": "flash", +# "executable": { +# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", +# "format": "raw" +# }, +# "nvram-template": { +# "filename": "/usr/share/OVMF/OVMF_VARS.fd", +# "format": "raw" +# } +# }, +# "targets": [ +# { +# "architecture": "x86_64", +# "machines": [ +# "pc-q35-*" +# ] +# } +# ], +# "features": [ +# "acpi-s3", +# "amd-sev", +# "requires-smm", +# "secure-boot", +# "verbose-dynamic" +# ], +# "tags": [ +# "-a IA32", +# "-a X64", +# "-p OvmfPkg/OvmfPkgIa32X64.dsc", +# "-t GCC48", +# "-b DEBUG", +# "-D SMM_REQUIRE", +# "-D SECURE_BOOT_ENABLE", +# "-D FD_SIZE_4MB" +# ] +# } +# +# { +# "description": "OVMF with SB+SMM, SB enabled, MS certs enrolled", +# "interface-types": [ +# "uefi" +# ], +# "mapping": { +# "device": "flash", +# "executable": { +# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", +# "format": "raw" +# }, +# "nvram-template": { +# "filename": "/usr/share/OVMF/OVMF_VARS.secboot.fd", +# "format": "raw" +# } +# }, +# "targets": [ +# { +# "architecture": "x86_64", +# "machines": [ +# "pc-q35-*" +# ] +# } +# ], +# "features": [ +# "acpi-s3", +# "amd-sev", +# "enrolled-keys", +# "requires-smm", +# "secure-boot", +# "verbose-dynamic" +# ], +# "tags": [ +# "-a IA32", +# "-a X64", +# "-p OvmfPkg/OvmfPkgIa32X64.dsc", +# "-t GCC48", +# "-b DEBUG", +# "-D SMM_REQUIRE", +# "-D SECURE_BOOT_ENABLE", +# "-D FD_SIZE_4MB" +# ] +# } +# +# { +# "description": "UEFI firmware for ARM64 virtual machines", +# "interface-types": [ +# "uefi" +# ], +# "mapping": { +# "device": "flash", +# "executable": { +# "filename": "/usr/share/AAVMF/AAVMF_CODE.fd", +# "format": "raw" +# }, +# "nvram-template": { +# "filename": "/usr/share/AAVMF/AAVMF_VARS.fd", +# "format": "raw" +# } +# }, +# "targets": [ +# { +# "architecture": "aarch64", +# "machines": [ +# "virt-*" +# ] +# } +# ], +# "features": [ +# +# ], +# "tags": [ +# "-a AARCH64", +# "-p ArmVirtPkg/ArmVirtQemu.dsc", +# "-t GCC48", +# "-b DEBUG", +# "-D DEBUG_PRINT_ERROR_LEVEL=0x80000000" +# ] +# } +## +{ 'struct' : 'Firmware', + 'data' : { 'description' : 'str', + 'interface-types' : [ 'FirmwareOSInterface' ], + 'mapping' : 'FirmwareMapping', + 'targets' : [ 'FirmwareTarget' ], + 'features' : [ 'FirmwareFeature' ], + 'tags' : [ 'str' ] } } From ead75d84cb076d51357ca0a48a2bbaf75e5466f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 May 2018 19:34:58 -0300 Subject: [PATCH 0924/2380] gdbstub: Prevent fd leakage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 2f652224f7, we now check if socket_set_nodelay() errored, but forgot to close the socket before reporting an error. Fixes: Coverity CID 1391290 (RESOURCE_LEAK) Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180524223458.5651-1-f4bug@amsat.org> Reviewed-by: Thomas Huth Reviewed-by: Peter Maydell Signed-off-by: Paolo Bonzini --- gdbstub.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gdbstub.c b/gdbstub.c index 6081e719c5..d6ab95006c 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1842,6 +1842,7 @@ static bool gdb_accept(void) /* set short latency */ if (socket_set_nodelay(fd)) { perror("setsockopt"); + close(fd); return false; } From a8bff79e9f27dfe0f1427bc824cebe45400d6c19 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 25 May 2018 14:27:50 +0100 Subject: [PATCH 0925/2380] virtio-gpu-3d: Define VIRTIO_GPU_CAPSET_VIRGL2 elsewhere Commit 5643cc94ac1c ("virtio-gpu-3d: add support for second capability set (v4)") updated virtio_gpu.h with a define that does not yet(?) exist upstream resulting in build breakage every time Linux headers are updated via the standard update script. Conditionally define this within QEMU code instead to avoid future breakage. Cc: Dave Airlie Cc: Gerd Hoffmann Fixes: 5643cc94ac1c ("virtio-gpu-3d: add support for second capability set (v4)") Signed-off-by: Alex Williamson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Message-Id: <20180525132755.21839-2-peter.maydell@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- include/hw/virtio/virtio-gpu.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 79bb3fb3dd..d6ba61f2f1 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -21,6 +21,12 @@ #include "qemu/log.h" #include "standard-headers/linux/virtio_gpu.h" + +/* Not yet(?) defined in standard-headers, remove when possible */ +#ifndef VIRTIO_GPU_CAPSET_VIRGL2 +#define VIRTIO_GPU_CAPSET_VIRGL2 2 +#endif + #define TYPE_VIRTIO_GPU "virtio-gpu-device" #define VIRTIO_GPU(obj) \ OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU) From c5022c31c2b7be863a2f58ae5af52717e017825b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 May 2018 14:27:51 +0100 Subject: [PATCH 0926/2380] scripts/update-linux-headers: Handle __aligned_u64 We'll currently replace any 'u64' with a 'uint64_t' including when it's embedded in an '__aligned_u64', creating a '__aligned_uint64_t' which doesn't exist. We need to instead expand out the kernel's definition of __aligned_u64: #define __aligned_u64 __u64 __attribute__((aligned(8))) before we convert the __u64 to uint64_t. Signed-off-by: Peter Maydell Message-Id: <20180525132755.21839-3-peter.maydell@linaro.org> Reviewed-by: Michael S. Tsirkin Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- scripts/update-linux-headers.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 947dec2852..1fe54f8ab1 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -51,7 +51,8 @@ cp_portable() { fi header=$(basename "$f"); - sed -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ + sed -e 's/__aligned_u64/__u64 __attribute__((aligned(8)))/g' \ + -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ -e 's/u\([0-9][0-9]*\)/uint\1_t/g' \ -e 's/__s\([0-9][0-9]*\)/int\1_t/g' \ -e 's/__le\([0-9][0-9]*\)/uint\1_t/g' \ From f5bba4ca92b1fb9b6ff063895103a31d66ceac06 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 May 2018 14:27:52 +0100 Subject: [PATCH 0927/2380] scripts/update-linux-headers: Handle kernel license no longer being one file The kernel has changed its license documentation, so instead of COPYING being a stand-alone file that defines the license, it refers to various other files under LICENSES/. This means we need to copy not just COPYING but also these other files to our copy of the kernel headers. Signed-off-by: Peter Maydell Message-Id: <20180525132755.21839-4-peter.maydell@linaro.org> Reviewed-by: Michael S. Tsirkin Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- scripts/update-linux-headers.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 1fe54f8ab1..feb75390aa 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -142,6 +142,20 @@ else cp "$linux/COPYING" "$output/linux-headers" fi +# Recent kernel sources split the copyright/license info into multiple +# files, which we need to copy. This set of licenses is the set that +# are referred to by SPDX lines in the headers we currently copy. +# We don't copy the Documentation/process/license-rules.rst which +# is also referred to by COPYING, since it's explanatory rather than license. +if [ -d "$linux/LICENSES" ]; then + mkdir -p "$output/linux-headers/LICENSES/preferred" \ + "$output/linux-headers/LICENSES/exceptions" + for l in preferred/GPL-2.0 preferred/BSD-2-Clause preferred/BSD-3-Clause \ + exceptions/Linux-syscall-note; do + cp "$linux/LICENSES/$l" "$output/linux-headers/LICENSES/$l" + done +fi + cat <$output/linux-headers/linux/virtio_config.h #include "standard-headers/linux/virtio_config.h" EOF From 2af1acadc22e86b9fff7b2cd73ea9c779ba1a065 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 May 2018 14:27:53 +0100 Subject: [PATCH 0928/2380] target/i386/kvm.c: Handle renaming of KVM_HINTS_DEDICATED In kernel header commit 633711e8287, the define KVM_HINTS_DEDICATED was renamed to KVM_HINTS_REALTIME. Work around this compatibility break by (a) using the new constant name, and (b) defining it if the headers don't. Part (b) can be removed once we've updated our copy of the kernel headers to a version that defines KVM_HINTS_REALTIME. Signed-off-by: Peter Maydell Message-Id: <20180525132755.21839-5-peter.maydell@linaro.org> Reviewed-by: Michael S. Tsirkin Acked-by: Paolo Bonzini Signed-off-by: Paolo Bonzini --- target/i386/kvm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 9d8f80f4c0..8eae654991 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -47,6 +47,11 @@ #include "exec/memattrs.h" #include "trace.h" +/* Work around this kernel header constant changing its name */ +#ifndef KVM_HINTS_REALTIME +#define KVM_HINTS_REALTIME KVM_HINTS_DEDICATED +#endif + //#define DEBUG_KVM #ifdef DEBUG_KVM @@ -386,7 +391,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, ret &= ~(1U << KVM_FEATURE_PV_UNHALT); } } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) { - ret |= 1U << KVM_HINTS_DEDICATED; + ret |= 1U << KVM_HINTS_REALTIME; found = 1; } From 65a6d8dd3f322e91efa320f2811bec83dde36b6b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 May 2018 14:27:54 +0100 Subject: [PATCH 0929/2380] Update Linux headers to 4.17-rc6 Update our copy of the Linux headers to upstream 4.17-rc6 (kernel commit 771c577c23bac90597c68). Signed-off-by: Peter Maydell Message-Id: <20180525132755.21839-6-peter.maydell@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- include/standard-headers/asm-x86/hyperv.h | 1 - include/standard-headers/asm-x86/kvm_para.h | 2 +- include/standard-headers/linux/ethtool.h | 36 +- include/standard-headers/linux/input.h | 4 +- include/standard-headers/linux/pci_regs.h | 7 +- .../standard-headers/linux/virtio_balloon.h | 15 + include/standard-headers/linux/virtio_gpu.h | 1 - .../standard-headers/rdma/vmw_pvrdma-abi.h | 49 +-- linux-headers/COPYING | 358 +----------------- .../LICENSES/exceptions/Linux-syscall-note | 25 ++ linux-headers/LICENSES/preferred/BSD-2-Clause | 32 ++ linux-headers/LICENSES/preferred/BSD-3-Clause | 36 ++ linux-headers/LICENSES/preferred/GPL-2.0 | 353 +++++++++++++++++ linux-headers/asm-arm/kvm.h | 15 + linux-headers/asm-arm64/kvm.h | 6 + linux-headers/asm-x86/hyperv.h | 1 - linux-headers/asm-x86/kvm.h | 19 +- linux-headers/linux/kvm.h | 30 +- linux-headers/linux/vfio.h | 27 ++ 19 files changed, 628 insertions(+), 389 deletions(-) delete mode 100644 include/standard-headers/asm-x86/hyperv.h create mode 100644 linux-headers/LICENSES/exceptions/Linux-syscall-note create mode 100644 linux-headers/LICENSES/preferred/BSD-2-Clause create mode 100644 linux-headers/LICENSES/preferred/BSD-3-Clause create mode 100644 linux-headers/LICENSES/preferred/GPL-2.0 delete mode 100644 linux-headers/asm-x86/hyperv.h diff --git a/include/standard-headers/asm-x86/hyperv.h b/include/standard-headers/asm-x86/hyperv.h deleted file mode 100644 index ce87d0c344..0000000000 --- a/include/standard-headers/asm-x86/hyperv.h +++ /dev/null @@ -1 +0,0 @@ - /* this is a temporary placeholder until kvm_para.h stops including it */ diff --git a/include/standard-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h index 53a85ae3ed..1617c84b0d 100644 --- a/include/standard-headers/asm-x86/kvm_para.h +++ b/include/standard-headers/asm-x86/kvm_para.h @@ -29,7 +29,7 @@ #define KVM_FEATURE_PV_TLB_FLUSH 9 #define KVM_FEATURE_ASYNC_PF_VMEXIT 10 -#define KVM_HINTS_DEDICATED 0 +#define KVM_HINTS_REALTIME 0 /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 94aacb7adf..eb10c075e4 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -217,10 +217,14 @@ struct ethtool_value { uint32_t data; }; +#define PFC_STORM_PREVENTION_AUTO 0xffff +#define PFC_STORM_PREVENTION_DISABLE 0 + enum tunable_id { ETHTOOL_ID_UNSPEC, ETHTOOL_RX_COPYBREAK, ETHTOOL_TX_COPYBREAK, + ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */ /* * Add your fresh new tubale attribute above and remember to update * tunable_strings[] in net/core/ethtool.c @@ -914,12 +918,15 @@ static inline uint64_t ethtool_get_flow_spec_ring_vf(uint64_t ring_cookie) * @flow_type: Type of flow to be affected, e.g. %TCP_V4_FLOW * @data: Command-dependent value * @fs: Flow classification rule + * @rss_context: RSS context to be affected * @rule_cnt: Number of rules to be affected * @rule_locs: Array of used rule locations * * For %ETHTOOL_GRXFH and %ETHTOOL_SRXFH, @data is a bitmask indicating * the fields included in the flow hash, e.g. %RXH_IP_SRC. The following - * structure fields must not be used. + * structure fields must not be used, except that if @flow_type includes + * the %FLOW_RSS flag, then @rss_context determines which RSS context to + * act on. * * For %ETHTOOL_GRXRINGS, @data is set to the number of RX rings/queues * on return. @@ -931,7 +938,9 @@ static inline uint64_t ethtool_get_flow_spec_ring_vf(uint64_t ring_cookie) * set in @data then special location values should not be used. * * For %ETHTOOL_GRXCLSRULE, @fs.@location specifies the location of an - * existing rule on entry and @fs contains the rule on return. + * existing rule on entry and @fs contains the rule on return; if + * @fs.@flow_type includes the %FLOW_RSS flag, then @rss_context is + * filled with the RSS context ID associated with the rule. * * For %ETHTOOL_GRXCLSRLALL, @rule_cnt specifies the array size of the * user buffer for @rule_locs on entry. On return, @data is the size @@ -942,7 +951,11 @@ static inline uint64_t ethtool_get_flow_spec_ring_vf(uint64_t ring_cookie) * For %ETHTOOL_SRXCLSRLINS, @fs specifies the rule to add or update. * @fs.@location either specifies the location to use or is a special * location value with %RX_CLS_LOC_SPECIAL flag set. On return, - * @fs.@location is the actual rule location. + * @fs.@location is the actual rule location. If @fs.@flow_type + * includes the %FLOW_RSS flag, @rss_context is the RSS context ID to + * use for flow spreading traffic which matches this rule. The value + * from the rxfh indirection table will be added to @fs.@ring_cookie + * to choose which ring to deliver to. * * For %ETHTOOL_SRXCLSRLDEL, @fs.@location specifies the location of an * existing rule on entry. @@ -963,7 +976,10 @@ struct ethtool_rxnfc { uint32_t flow_type; uint64_t data; struct ethtool_rx_flow_spec fs; - uint32_t rule_cnt; + union { + uint32_t rule_cnt; + uint32_t rss_context; + }; uint32_t rule_locs[0]; }; @@ -990,7 +1006,11 @@ struct ethtool_rxfh_indir { /** * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key. * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH - * @rss_context: RSS context identifier. + * @rss_context: RSS context identifier. Context 0 is the default for normal + * traffic; other contexts can be referenced as the destination for RX flow + * classification rules. %ETH_RXFH_CONTEXT_ALLOC is used with command + * %ETHTOOL_SRSSH to allocate a new RSS context; on return this field will + * contain the ID of the newly allocated context. * @indir_size: On entry, the array size of the user buffer for the * indirection table, which may be zero, or (for %ETHTOOL_SRSSH), * %ETH_RXFH_INDIR_NO_CHANGE. On return from %ETHTOOL_GRSSH, @@ -1009,7 +1029,8 @@ struct ethtool_rxfh_indir { * size should be returned. For %ETHTOOL_SRSSH, an @indir_size of * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested * and a @indir_size of zero means the indir table should be reset to default - * values. An hfunc of zero means that hash function setting is not requested. + * values (if @rss_context == 0) or that the RSS context should be deleted. + * An hfunc of zero means that hash function setting is not requested. */ struct ethtool_rxfh { uint32_t cmd; @@ -1021,6 +1042,7 @@ struct ethtool_rxfh { uint32_t rsvd32; uint32_t rss_config[0]; }; +#define ETH_RXFH_CONTEXT_ALLOC 0xffffffff #define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff /** @@ -1635,6 +1657,8 @@ static inline int ethtool_validate_duplex(uint8_t duplex) /* Flag to enable additional fields in struct ethtool_rx_flow_spec */ #define FLOW_EXT 0x80000000 #define FLOW_MAC_EXT 0x40000000 +/* Flag to enable RSS spreading of traffic matching rule (nfc only) */ +#define FLOW_RSS 0x20000000 /* L3-L4 network traffic flow hash options */ #define RXH_L2DA (1 << 1) diff --git a/include/standard-headers/linux/input.h b/include/standard-headers/linux/input.h index 939b62775c..6d6128c081 100644 --- a/include/standard-headers/linux/input.h +++ b/include/standard-headers/linux/input.h @@ -28,8 +28,8 @@ struct input_event { #define input_event_sec time.tv_sec #define input_event_usec time.tv_usec #else - __kernel_ulong_t __sec; - __kernel_ulong_t __usec; + unsigned long __sec; + unsigned long __usec; #define input_event_sec __sec #define input_event_usec __usec #endif diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 0c79eac5e9..103ba797a8 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -520,6 +520,7 @@ #define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ +#define PCI_EXP_LNKCAP_SLS_16_0GB 0x00000004 /* LNKCAP2 SLS Vector bit 3 */ #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ @@ -547,6 +548,7 @@ #define PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */ #define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */ #define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */ +#define PCI_EXP_LNKSTA_CLS_16_0GB 0x0004 /* Current Link Speed 16.0GT/s */ #define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */ #define PCI_EXP_LNKSTA_NLW_X1 0x0010 /* Current Link Width x1 */ #define PCI_EXP_LNKSTA_NLW_X2 0x0020 /* Current Link Width x2 */ @@ -648,8 +650,9 @@ #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints without link end here */ #define PCI_EXP_LNKCAP2 44 /* Link Capabilities 2 */ #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ -#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5.0GT/s */ -#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8.0GT/s */ +#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */ +#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */ +#define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ diff --git a/include/standard-headers/linux/virtio_balloon.h b/include/standard-headers/linux/virtio_balloon.h index e446805ae9..4dbb7dc6c0 100644 --- a/include/standard-headers/linux/virtio_balloon.h +++ b/include/standard-headers/linux/virtio_balloon.h @@ -57,6 +57,21 @@ struct virtio_balloon_config { #define VIRTIO_BALLOON_S_HTLB_PGFAIL 9 /* Hugetlb page allocation failures */ #define VIRTIO_BALLOON_S_NR 10 +#define VIRTIO_BALLOON_S_NAMES_WITH_PREFIX(VIRTIO_BALLOON_S_NAMES_prefix) { \ + VIRTIO_BALLOON_S_NAMES_prefix "swap-in", \ + VIRTIO_BALLOON_S_NAMES_prefix "swap-out", \ + VIRTIO_BALLOON_S_NAMES_prefix "major-faults", \ + VIRTIO_BALLOON_S_NAMES_prefix "minor-faults", \ + VIRTIO_BALLOON_S_NAMES_prefix "free-memory", \ + VIRTIO_BALLOON_S_NAMES_prefix "total-memory", \ + VIRTIO_BALLOON_S_NAMES_prefix "available-memory", \ + VIRTIO_BALLOON_S_NAMES_prefix "disk-caches", \ + VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-allocations", \ + VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-failures" \ +} + +#define VIRTIO_BALLOON_S_NAMES VIRTIO_BALLOON_S_NAMES_WITH_PREFIX("") + /* * Memory statistics structure. * Driver fills an array of these structures and passes to device. diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard-headers/linux/virtio_gpu.h index 52a830dcf8..c1c8f0751d 100644 --- a/include/standard-headers/linux/virtio_gpu.h +++ b/include/standard-headers/linux/virtio_gpu.h @@ -260,7 +260,6 @@ struct virtio_gpu_cmd_submit { }; #define VIRTIO_GPU_CAPSET_VIRGL 1 -#define VIRTIO_GPU_CAPSET_VIRGL2 2 /* VIRTIO_GPU_CMD_GET_CAPSET_INFO */ struct virtio_gpu_get_capset_info { diff --git a/include/standard-headers/rdma/vmw_pvrdma-abi.h b/include/standard-headers/rdma/vmw_pvrdma-abi.h index 07a820d337..6c2bc46116 100644 --- a/include/standard-headers/rdma/vmw_pvrdma-abi.h +++ b/include/standard-headers/rdma/vmw_pvrdma-abi.h @@ -143,7 +143,7 @@ struct pvrdma_alloc_pd_resp { }; struct pvrdma_create_cq { - uint64_t buf_addr; + uint64_t __attribute__((aligned(8))) buf_addr; uint32_t buf_size; uint32_t reserved; }; @@ -154,13 +154,13 @@ struct pvrdma_create_cq_resp { }; struct pvrdma_resize_cq { - uint64_t buf_addr; + uint64_t __attribute__((aligned(8))) buf_addr; uint32_t buf_size; uint32_t reserved; }; struct pvrdma_create_srq { - uint64_t buf_addr; + uint64_t __attribute__((aligned(8))) buf_addr; uint32_t buf_size; uint32_t reserved; }; @@ -171,25 +171,25 @@ struct pvrdma_create_srq_resp { }; struct pvrdma_create_qp { - uint64_t rbuf_addr; - uint64_t sbuf_addr; + uint64_t __attribute__((aligned(8))) rbuf_addr; + uint64_t __attribute__((aligned(8))) sbuf_addr; uint32_t rbuf_size; uint32_t sbuf_size; - uint64_t qp_addr; + uint64_t __attribute__((aligned(8))) qp_addr; }; /* PVRDMA masked atomic compare and swap */ struct pvrdma_ex_cmp_swap { - uint64_t swap_val; - uint64_t compare_val; - uint64_t swap_mask; - uint64_t compare_mask; + uint64_t __attribute__((aligned(8))) swap_val; + uint64_t __attribute__((aligned(8))) compare_val; + uint64_t __attribute__((aligned(8))) swap_mask; + uint64_t __attribute__((aligned(8))) compare_mask; }; /* PVRDMA masked atomic fetch and add */ struct pvrdma_ex_fetch_add { - uint64_t add_val; - uint64_t field_boundary; + uint64_t __attribute__((aligned(8))) add_val; + uint64_t __attribute__((aligned(8))) field_boundary; }; /* PVRDMA address vector. */ @@ -207,14 +207,14 @@ struct pvrdma_av { /* PVRDMA scatter/gather entry */ struct pvrdma_sge { - uint64_t addr; + uint64_t __attribute__((aligned(8))) addr; uint32_t length; uint32_t lkey; }; /* PVRDMA receive queue work request */ struct pvrdma_rq_wqe_hdr { - uint64_t wr_id; /* wr id */ + uint64_t __attribute__((aligned(8))) wr_id; /* wr id */ uint32_t num_sge; /* size of s/g array */ uint32_t total_len; /* reserved */ }; @@ -222,7 +222,7 @@ struct pvrdma_rq_wqe_hdr { /* PVRDMA send queue work request */ struct pvrdma_sq_wqe_hdr { - uint64_t wr_id; /* wr id */ + uint64_t __attribute__((aligned(8))) wr_id; /* wr id */ uint32_t num_sge; /* size of s/g array */ uint32_t total_len; /* reserved */ uint32_t opcode; /* operation type */ @@ -234,19 +234,19 @@ struct pvrdma_sq_wqe_hdr { uint32_t reserved; union { struct { - uint64_t remote_addr; + uint64_t __attribute__((aligned(8))) remote_addr; uint32_t rkey; uint8_t reserved[4]; } rdma; struct { - uint64_t remote_addr; - uint64_t compare_add; - uint64_t swap; + uint64_t __attribute__((aligned(8))) remote_addr; + uint64_t __attribute__((aligned(8))) compare_add; + uint64_t __attribute__((aligned(8))) swap; uint32_t rkey; uint32_t reserved; } atomic; struct { - uint64_t remote_addr; + uint64_t __attribute__((aligned(8))) remote_addr; uint32_t log_arg_sz; uint32_t rkey; union { @@ -255,13 +255,14 @@ struct pvrdma_sq_wqe_hdr { } wr_data; } masked_atomics; struct { - uint64_t iova_start; - uint64_t pl_pdir_dma; + uint64_t __attribute__((aligned(8))) iova_start; + uint64_t __attribute__((aligned(8))) pl_pdir_dma; uint32_t page_shift; uint32_t page_list_len; uint32_t length; uint32_t access_flags; uint32_t rkey; + uint32_t reserved; } fast_reg; struct { uint32_t remote_qpn; @@ -274,8 +275,8 @@ struct pvrdma_sq_wqe_hdr { /* Completion queue element. */ struct pvrdma_cqe { - uint64_t wr_id; - uint64_t qp; + uint64_t __attribute__((aligned(8))) wr_id; + uint64_t __attribute__((aligned(8))) qp; uint32_t opcode; uint32_t status; uint32_t byte_len; diff --git a/linux-headers/COPYING b/linux-headers/COPYING index ca442d313d..da4cb28feb 100644 --- a/linux-headers/COPYING +++ b/linux-headers/COPYING @@ -1,356 +1,18 @@ +The Linux Kernel is provided under: - NOTE! This copyright does *not* cover user programs that use kernel - services by normal system calls - this is merely considered normal use - of the kernel, and does *not* fall under the heading of "derived work". - Also note that the GPL below is copyrighted by the Free Software - Foundation, but the instance of code that it refers to (the Linux - kernel) is copyrighted by me and others who actually wrote it. + SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note - Also note that the only valid version of the GPL as far as the kernel - is concerned is _this_ particular version of the license (ie v2, not - v2.2 or v3.x or whatever), unless explicitly otherwise stated. +Being under the terms of the GNU General Public License version 2 only, +according with: - Linus Torvalds + LICENSES/preferred/GPL-2.0 ----------------------------------------- +With an explicit syscall exception, as stated at: - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + LICENSES/exceptions/Linux-syscall-note - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +In addition, other licenses may also apply. Please see: - Preamble + Documentation/process/license-rules.rst - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. +for more details. diff --git a/linux-headers/LICENSES/exceptions/Linux-syscall-note b/linux-headers/LICENSES/exceptions/Linux-syscall-note new file mode 100644 index 0000000000..6b60b61be4 --- /dev/null +++ b/linux-headers/LICENSES/exceptions/Linux-syscall-note @@ -0,0 +1,25 @@ +SPDX-Exception-Identifier: Linux-syscall-note +SPDX-URL: https://spdx.org/licenses/Linux-syscall-note.html +SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-1.0+, LGPL-2.0, LGPL-2.0+, LGPL-2.1, LGPL-2.1+ +Usage-Guide: + This exception is used together with one of the above SPDX-Licenses + to mark user space API (uapi) header files so they can be included + into non GPL compliant user space application code. + To use this exception add it with the keyword WITH to one of the + identifiers in the SPDX-Licenses tag: + SPDX-License-Identifier: WITH Linux-syscall-note +License-Text: + + NOTE! This copyright does *not* cover user programs that use kernel + services by normal system calls - this is merely considered normal use + of the kernel, and does *not* fall under the heading of "derived work". + Also note that the GPL below is copyrighted by the Free Software + Foundation, but the instance of code that it refers to (the Linux + kernel) is copyrighted by me and others who actually wrote it. + + Also note that the only valid version of the GPL as far as the kernel + is concerned is _this_ particular version of the license (ie v2, not + v2.2 or v3.x or whatever), unless explicitly otherwise stated. + + Linus Torvalds + diff --git a/linux-headers/LICENSES/preferred/BSD-2-Clause b/linux-headers/LICENSES/preferred/BSD-2-Clause new file mode 100644 index 0000000000..da366e2ce5 --- /dev/null +++ b/linux-headers/LICENSES/preferred/BSD-2-Clause @@ -0,0 +1,32 @@ +Valid-License-Identifier: BSD-2-Clause +SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html +Usage-Guide: + To use the BSD 2-clause "Simplified" License put the following SPDX + tag/value pair into a comment according to the placement guidelines in + the licensing rules documentation: + SPDX-License-Identifier: BSD-2-Clause +License-Text: + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux-headers/LICENSES/preferred/BSD-3-Clause b/linux-headers/LICENSES/preferred/BSD-3-Clause new file mode 100644 index 0000000000..34c7f057c8 --- /dev/null +++ b/linux-headers/LICENSES/preferred/BSD-3-Clause @@ -0,0 +1,36 @@ +Valid-License-Identifier: BSD-3-Clause +SPDX-URL: https://spdx.org/licenses/BSD-3-Clause.html +Usage-Guide: + To use the BSD 3-clause "New" or "Revised" License put the following SPDX + tag/value pair into a comment according to the placement guidelines in + the licensing rules documentation: + SPDX-License-Identifier: BSD-3-Clause +License-Text: + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux-headers/LICENSES/preferred/GPL-2.0 b/linux-headers/LICENSES/preferred/GPL-2.0 new file mode 100644 index 0000000000..b8db91d3a1 --- /dev/null +++ b/linux-headers/LICENSES/preferred/GPL-2.0 @@ -0,0 +1,353 @@ +Valid-License-Identifier: GPL-2.0 +Valid-License-Identifier: GPL-2.0+ +SPDX-URL: https://spdx.org/licenses/GPL-2.0.html +Usage-Guide: + To use this license in source code, put one of the following SPDX + tag/value pairs into a comment according to the placement + guidelines in the licensing rules documentation. + For 'GNU General Public License (GPL) version 2 only' use: + SPDX-License-Identifier: GPL-2.0 + For 'GNU General Public License (GPL) version 2 or any later version' use: + SPDX-License-Identifier: GPL-2.0+ +License-Text: + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index 4392955081..670b43c9e9 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -135,6 +135,15 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_CRM_SHIFT 7 #define KVM_REG_ARM_32_CRN_MASK 0x0000000000007800 #define KVM_REG_ARM_32_CRN_SHIFT 11 +/* + * For KVM currently all guest registers are nonsecure, but we reserve a bit + * in the encoding to distinguish secure from nonsecure for AArch32 system + * registers that are banked by security. This is 1 for the secure banked + * register, and 0 for the nonsecure banked register or if the register is + * not banked by security. + */ +#define KVM_REG_ARM_SECURE_MASK 0x0000000010000000 +#define KVM_REG_ARM_SECURE_SHIFT 28 #define ARM_CP15_REG_SHIFT_MASK(x,n) \ (((x) << KVM_REG_ARM_ ## n ## _SHIFT) & KVM_REG_ARM_ ## n ## _MASK) @@ -186,6 +195,12 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_VFP_FPINST 0x1009 #define KVM_REG_ARM_VFP_FPINST2 0x100A +/* KVM-as-firmware specific pseudo-registers */ +#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM_FW | ((r) & 0xffff)) +#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0) + /* Device Control API: ARM VGIC */ #define KVM_DEV_ARM_VGIC_GRP_ADDR 0 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 4e80651efe..17315aba6a 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -206,6 +206,12 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_TIMER_CNT ARM64_SYS_REG(3, 3, 14, 3, 2) #define KVM_REG_ARM_TIMER_CVAL ARM64_SYS_REG(3, 3, 14, 0, 2) +/* KVM-as-firmware specific pseudo-registers */ +#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM_FW | ((r) & 0xffff)) +#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0) + /* Device Control API: ARM VGIC */ #define KVM_DEV_ARM_VGIC_GRP_ADDR 0 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 diff --git a/linux-headers/asm-x86/hyperv.h b/linux-headers/asm-x86/hyperv.h deleted file mode 100644 index 01af4d8593..0000000000 --- a/linux-headers/asm-x86/hyperv.h +++ /dev/null @@ -1 +0,0 @@ -#include "standard-headers/asm-x86/hyperv.h" diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index f3a960488e..c535c2fdea 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -354,8 +354,25 @@ struct kvm_xcrs { __u64 padding[16]; }; -/* definition of registers in kvm_run */ +#define KVM_SYNC_X86_REGS (1UL << 0) +#define KVM_SYNC_X86_SREGS (1UL << 1) +#define KVM_SYNC_X86_EVENTS (1UL << 2) + +#define KVM_SYNC_X86_VALID_FIELDS \ + (KVM_SYNC_X86_REGS| \ + KVM_SYNC_X86_SREGS| \ + KVM_SYNC_X86_EVENTS) + +/* kvm_sync_regs struct included by kvm_run struct */ struct kvm_sync_regs { + /* Members of this structure are potentially malicious. + * Care must be taken by code reading, esp. interpreting, + * data fields from them inside KVM to prevent TOCTOU and + * double-fetch types of vulnerabilities. + */ + struct kvm_regs regs; + struct kvm_sregs sregs; + struct kvm_vcpu_events events; }; #define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index a167be89d1..cdb148e959 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -396,6 +396,10 @@ struct kvm_run { char padding[256]; }; + /* 2048 is the size of the char array used to bound/pad the size + * of the union that holds sync regs. + */ + #define SYNC_REGS_SIZE_BYTES 2048 /* * shared registers between kvm and userspace. * kvm_valid_regs specifies the register classes set by the host @@ -407,7 +411,7 @@ struct kvm_run { __u64 kvm_dirty_regs; union { struct kvm_sync_regs regs; - char padding[2048]; + char padding[SYNC_REGS_SIZE_BYTES]; } s; }; @@ -672,6 +676,13 @@ struct kvm_ioeventfd { __u8 pad[36]; }; +#define KVM_X86_DISABLE_EXITS_MWAIT (1 << 0) +#define KVM_X86_DISABLE_EXITS_HTL (1 << 1) +#define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2) +#define KVM_X86_DISABLE_VALID_EXITS (KVM_X86_DISABLE_EXITS_MWAIT | \ + KVM_X86_DISABLE_EXITS_HTL | \ + KVM_X86_DISABLE_EXITS_PAUSE) + /* for KVM_ENABLE_CAP */ struct kvm_enable_cap { /* in */ @@ -925,7 +936,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_GS 140 #define KVM_CAP_S390_AIS 141 #define KVM_CAP_SPAPR_TCE_VFIO 142 -#define KVM_CAP_X86_GUEST_MWAIT 143 +#define KVM_CAP_X86_DISABLE_EXITS 143 #define KVM_CAP_ARM_USER_IRQ 144 #define KVM_CAP_S390_CMMA_MIGRATION 145 #define KVM_CAP_PPC_FWNMI 146 @@ -936,6 +947,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_PPC_GET_CPU_CHAR 151 #define KVM_CAP_S390_BPB 152 #define KVM_CAP_GET_MSR_FEATURES 153 +#define KVM_CAP_HYPERV_EVENTFD 154 #ifdef KVM_CAP_IRQ_ROUTING @@ -1375,6 +1387,10 @@ struct kvm_enc_region { #define KVM_MEMORY_ENCRYPT_REG_REGION _IOR(KVMIO, 0xbb, struct kvm_enc_region) #define KVM_MEMORY_ENCRYPT_UNREG_REGION _IOR(KVMIO, 0xbc, struct kvm_enc_region) +/* Available with KVM_CAP_HYPERV_EVENTFD */ +#define KVM_HYPERV_EVENTFD _IOW(KVMIO, 0xbd, struct kvm_hyperv_eventfd) + + /* Secure Encrypted Virtualization command */ enum sev_cmd_id { /* Guest initialization commands */ @@ -1515,4 +1531,14 @@ struct kvm_assigned_msix_entry { #define KVM_ARM_DEV_EL1_PTIMER (1 << 1) #define KVM_ARM_DEV_PMU (1 << 2) +struct kvm_hyperv_eventfd { + __u32 conn_id; + __s32 fd; + __u32 flags; + __u32 padding[3]; +}; + +#define KVM_HYPERV_CONN_ID_MASK 0x00ffffff +#define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0) + #endif /* __LINUX_KVM_H */ diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index 3a0a305c8c..3615a269d3 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -575,6 +575,33 @@ struct vfio_device_gfx_plane_info { #define VFIO_DEVICE_GET_GFX_DMABUF _IO(VFIO_TYPE, VFIO_BASE + 15) +/** + * VFIO_DEVICE_IOEVENTFD - _IOW(VFIO_TYPE, VFIO_BASE + 16, + * struct vfio_device_ioeventfd) + * + * Perform a write to the device at the specified device fd offset, with + * the specified data and width when the provided eventfd is triggered. + * vfio bus drivers may not support this for all regions, for all widths, + * or at all. vfio-pci currently only enables support for BAR regions, + * excluding the MSI-X vector table. + * + * Return: 0 on success, -errno on failure. + */ +struct vfio_device_ioeventfd { + __u32 argsz; + __u32 flags; +#define VFIO_DEVICE_IOEVENTFD_8 (1 << 0) /* 1-byte write */ +#define VFIO_DEVICE_IOEVENTFD_16 (1 << 1) /* 2-byte write */ +#define VFIO_DEVICE_IOEVENTFD_32 (1 << 2) /* 4-byte write */ +#define VFIO_DEVICE_IOEVENTFD_64 (1 << 3) /* 8-byte write */ +#define VFIO_DEVICE_IOEVENTFD_SIZE_MASK (0xf) + __u64 offset; /* device fd offset of write */ + __u64 data; /* data to be written */ + __s32 fd; /* -1 for de-assignment */ +}; + +#define VFIO_DEVICE_IOEVENTFD _IO(VFIO_TYPE, VFIO_BASE + 16) + /* -------- API for Type1 VFIO IOMMU -------- */ /** From 244e2ad0dcddad68c3c4842eba7675fda79fa511 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 May 2018 14:27:55 +0100 Subject: [PATCH 0930/2380] target/i386/kvm.c: Remove compatibility shim for KVM_HINTS_REALTIME Now we've updated our copy of the kernel headers we can remove the compatibility shim that handled KVM_HINTS_REALTIME not being defined. Signed-off-by: Peter Maydell Message-Id: <20180525132755.21839-7-peter.maydell@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- target/i386/kvm.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 8eae654991..44f70733e7 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -47,11 +47,6 @@ #include "exec/memattrs.h" #include "trace.h" -/* Work around this kernel header constant changing its name */ -#ifndef KVM_HINTS_REALTIME -#define KVM_HINTS_REALTIME KVM_HINTS_DEDICATED -#endif - //#define DEBUG_KVM #ifdef DEBUG_KVM From 19473e51cc1b019b1987261e1af8bc8b4a858f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 16:45:06 +0200 Subject: [PATCH 0931/2380] hw/i2c/smbus: Use DeviceClass::realize instead of SMBusDeviceClass::init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SMBusDeviceClass::init is no more used, remove it. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180419212727.26095-2-f4bug@amsat.org> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-Id: <20180528144509.15812-2-armbru@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i2c/smbus.c | 9 --------- hw/i2c/smbus_eeprom.c | 5 ++--- include/hw/i2c/smbus.h | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c index 2d1b79a689..587ce1ab7f 100644 --- a/hw/i2c/smbus.c +++ b/hw/i2c/smbus.c @@ -202,14 +202,6 @@ static int smbus_i2c_send(I2CSlave *s, uint8_t data) return 0; } -static int smbus_device_init(I2CSlave *i2c) -{ - SMBusDevice *dev = SMBUS_DEVICE(i2c); - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - - return sc->init(dev); -} - /* Master device commands. */ int smbus_quick_command(I2CBus *bus, uint8_t addr, int read) { @@ -350,7 +342,6 @@ static void smbus_device_class_init(ObjectClass *klass, void *data) { I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - sc->init = smbus_device_init; sc->event = smbus_i2c_event; sc->recv = smbus_i2c_recv; sc->send = smbus_i2c_send; diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index b13ec0fe7a..125c887d1f 100644 --- a/hw/i2c/smbus_eeprom.c +++ b/hw/i2c/smbus_eeprom.c @@ -97,12 +97,11 @@ static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n) return eeprom_receive_byte(dev); } -static int smbus_eeprom_initfn(SMBusDevice *dev) +static void smbus_eeprom_realize(DeviceState *dev, Error **errp) { SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev; eeprom->offset = 0; - return 0; } static Property smbus_eeprom_properties[] = { @@ -115,7 +114,7 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass); - sc->init = smbus_eeprom_initfn; + dc->realize = smbus_eeprom_realize; sc->quick_cmd = eeprom_quick_cmd; sc->send_byte = eeprom_send_byte; sc->receive_byte = eeprom_receive_byte; diff --git a/include/hw/i2c/smbus.h b/include/hw/i2c/smbus.h index 544bbc1957..cfe3fa69f3 100644 --- a/include/hw/i2c/smbus.h +++ b/include/hw/i2c/smbus.h @@ -38,7 +38,6 @@ typedef struct SMBusDeviceClass { I2CSlaveClass parent_class; - int (*init)(SMBusDevice *dev); void (*quick_cmd)(SMBusDevice *dev, uint8_t read); void (*send_byte)(SMBusDevice *dev, uint8_t val); uint8_t (*receive_byte)(SMBusDevice *dev); From c8c9e1039434b907ee982f3be04f81576bd1f588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 16:45:07 +0200 Subject: [PATCH 0932/2380] hw/i2c: Use DeviceClass::realize instead of I2CSlaveClass::init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I2CSlaveClass::init is no more used, remove it. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180419212727.26095-3-f4bug@amsat.org> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-Id: <20180528144509.15812-3-armbru@redhat.com> Signed-off-by: Paolo Bonzini --- hw/audio/wm8750.c | 8 +++----- hw/display/ssd0303.c | 9 ++++----- hw/gpio/max7310.c | 9 ++++----- hw/i2c/core.c | 13 ------------- hw/input/lm832x.c | 9 ++++----- hw/misc/tmp105.c | 7 +++---- hw/misc/tmp421.c | 8 +++----- hw/nvram/eeprom_at24c.c | 24 +++++++++++------------- hw/timer/twl92230.c | 11 ++++------- include/hw/i2c/i2c.h | 3 --- 10 files changed, 36 insertions(+), 65 deletions(-) diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index 416a78e869..f4aa838f62 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -617,14 +617,12 @@ static const VMStateDescription vmstate_wm8750 = { } }; -static int wm8750_init(I2CSlave *i2c) +static void wm8750_realize(DeviceState *dev, Error **errp) { - WM8750State *s = WM8750(i2c); + WM8750State *s = WM8750(dev); AUD_register_card(CODEC, &s->card); wm8750_reset(I2C_SLAVE(s)); - - return 0; } #if 0 @@ -707,7 +705,7 @@ static void wm8750_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - sc->init = wm8750_init; + dc->realize = wm8750_realize; sc->event = wm8750_event; sc->recv = wm8750_rx; sc->send = wm8750_tx; diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index 68a80b9d64..eb90ba26be 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -297,13 +297,12 @@ static const GraphicHwOps ssd0303_ops = { .gfx_update = ssd0303_update_display, }; -static int ssd0303_init(I2CSlave *i2c) +static void ssd0303_realize(DeviceState *dev, Error **errp) { - ssd0303_state *s = SSD0303(i2c); + ssd0303_state *s = SSD0303(dev); - s->con = graphic_console_init(DEVICE(i2c), 0, &ssd0303_ops, s); + s->con = graphic_console_init(dev, 0, &ssd0303_ops, s); qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY); - return 0; } static void ssd0303_class_init(ObjectClass *klass, void *data) @@ -311,7 +310,7 @@ static void ssd0303_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = ssd0303_init; + dc->realize = ssd0303_realize; k->event = ssd0303_event; k->recv = ssd0303_recv; k->send = ssd0303_send; diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c index 4c203ef5c6..a560e3afd2 100644 --- a/hw/gpio/max7310.c +++ b/hw/gpio/max7310.c @@ -182,14 +182,13 @@ static void max7310_gpio_set(void *opaque, int line, int level) /* MAX7310 is SMBus-compatible (can be used with only SMBus protocols), * but also accepts sequences that are not SMBus so return an I2C device. */ -static int max7310_init(I2CSlave *i2c) +static void max7310_realize(DeviceState *dev, Error **errp) { - MAX7310State *s = MAX7310(i2c); + I2CSlave *i2c = I2C_SLAVE(dev); + MAX7310State *s = MAX7310(dev); qdev_init_gpio_in(&i2c->qdev, max7310_gpio_set, 8); qdev_init_gpio_out(&i2c->qdev, s->handler, 8); - - return 0; } static void max7310_class_init(ObjectClass *klass, void *data) @@ -197,7 +196,7 @@ static void max7310_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = max7310_init; + dc->realize = max7310_realize; k->event = max7310_event; k->recv = max7310_rx; k->send = max7310_tx; diff --git a/hw/i2c/core.c b/hw/i2c/core.c index cfccefca3d..ab72d5bf2b 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -258,18 +258,6 @@ const VMStateDescription vmstate_i2c_slave = { } }; -static int i2c_slave_qdev_init(DeviceState *dev) -{ - I2CSlave *s = I2C_SLAVE(dev); - I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s); - - if (sc->init) { - return sc->init(s); - } - - return 0; -} - DeviceState *i2c_create_slave(I2CBus *bus, const char *name, uint8_t addr) { DeviceState *dev; @@ -283,7 +271,6 @@ DeviceState *i2c_create_slave(I2CBus *bus, const char *name, uint8_t addr) static void i2c_slave_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); - k->init = i2c_slave_qdev_init; set_bit(DEVICE_CATEGORY_MISC, k->categories); k->bus_type = TYPE_I2C_BUS; k->props = i2c_props; diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c index d39953126b..74da30d9ca 100644 --- a/hw/input/lm832x.c +++ b/hw/input/lm832x.c @@ -464,20 +464,19 @@ static const VMStateDescription vmstate_lm_kbd = { }; -static int lm8323_init(I2CSlave *i2c) +static void lm8323_realize(DeviceState *dev, Error **errp) { - LM823KbdState *s = LM8323(i2c); + LM823KbdState *s = LM8323(dev); s->model = 0x8323; s->pwm.tm[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm0_tick, s); s->pwm.tm[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm1_tick, s); s->pwm.tm[2] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm2_tick, s); - qdev_init_gpio_out(DEVICE(i2c), &s->nirq, 1); + qdev_init_gpio_out(dev, &s->nirq, 1); lm_kbd_reset(s); qemu_register_reset((void *) lm_kbd_reset, s); - return 0; } void lm832x_key_event(DeviceState *dev, int key, int state) @@ -505,7 +504,7 @@ static void lm8323_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = lm8323_init; + dc->realize = lm8323_realize; k->event = lm_i2c_event; k->recv = lm_i2c_rx; k->send = lm_i2c_tx; diff --git a/hw/misc/tmp105.c b/hw/misc/tmp105.c index 9e22d64e36..0918f3a6ea 100644 --- a/hw/misc/tmp105.c +++ b/hw/misc/tmp105.c @@ -229,15 +229,14 @@ static void tmp105_reset(I2CSlave *i2c) tmp105_interrupt_update(s); } -static int tmp105_init(I2CSlave *i2c) +static void tmp105_realize(DeviceState *dev, Error **errp) { + I2CSlave *i2c = I2C_SLAVE(dev); TMP105State *s = TMP105(i2c); qdev_init_gpio_out(&i2c->qdev, &s->pin, 1); tmp105_reset(&s->i2c); - - return 0; } static void tmp105_initfn(Object *obj) @@ -252,7 +251,7 @@ static void tmp105_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = tmp105_init; + dc->realize = tmp105_realize; k->event = tmp105_event; k->recv = tmp105_rx; k->send = tmp105_tx; diff --git a/hw/misc/tmp421.c b/hw/misc/tmp421.c index 4a505abbce..c234044305 100644 --- a/hw/misc/tmp421.c +++ b/hw/misc/tmp421.c @@ -335,13 +335,11 @@ static void tmp421_reset(I2CSlave *i2c) s->status = 0; } -static int tmp421_init(I2CSlave *i2c) +static void tmp421_realize(DeviceState *dev, Error **errp) { - TMP421State *s = TMP421(i2c); + TMP421State *s = TMP421(dev); tmp421_reset(&s->i2c); - - return 0; } static void tmp421_initfn(Object *obj) @@ -366,7 +364,7 @@ static void tmp421_class_init(ObjectClass *klass, void *data) I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); TMP421Class *sc = TMP421_CLASS(klass); - k->init = tmp421_init; + dc->realize = tmp421_realize; k->event = tmp421_event; k->recv = tmp421_rx; k->send = tmp421_tx; diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 22183f5360..27cd01e615 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -116,31 +116,29 @@ int at24c_eeprom_send(I2CSlave *s, uint8_t data) return 0; } -static -int at24c_eeprom_init(I2CSlave *i2c) +static void at24c_eeprom_realize(DeviceState *dev, Error **errp) { - EEPROMState *ee = AT24C_EE(i2c); - - ee->mem = g_malloc0(ee->rsize); + EEPROMState *ee = AT24C_EE(dev); if (ee->blk) { int64_t len = blk_getlength(ee->blk); if (len != ee->rsize) { - ERR(TYPE_AT24C_EE " : Backing file size %lu != %u\n", - (unsigned long)len, (unsigned)ee->rsize); - exit(1); + error_setg(errp, "%s: Backing file size %" PRId64 " != %u", + TYPE_AT24C_EE, len, ee->rsize); + return; } if (blk_set_perm(ee->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, BLK_PERM_ALL, &error_fatal) < 0) { - ERR(TYPE_AT24C_EE - " : Backing file incorrect permission\n"); - exit(1); + error_setg(errp, "%s: Backing file incorrect permission", + TYPE_AT24C_EE); + return; } } - return 0; + + ee->mem = g_malloc0(ee->rsize); } static @@ -178,7 +176,7 @@ void at24c_eeprom_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = &at24c_eeprom_init; + dc->realize = &at24c_eeprom_realize; k->event = &at24c_eeprom_event; k->recv = &at24c_eeprom_recv; k->send = &at24c_eeprom_send; diff --git a/hw/timer/twl92230.c b/hw/timer/twl92230.c index ef116c636c..3b43b46199 100644 --- a/hw/timer/twl92230.c +++ b/hw/timer/twl92230.c @@ -853,10 +853,9 @@ static const VMStateDescription vmstate_menelaus = { } }; -static int twl92230_init(I2CSlave *i2c) +static void twl92230_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(i2c); - MenelausState *s = TWL92230(i2c); + MenelausState *s = TWL92230(dev); s->rtc.hz_tm = timer_new_ms(rtc_clock, menelaus_rtc_hz, s); /* Three output pins plus one interrupt pin. */ @@ -865,9 +864,7 @@ static int twl92230_init(I2CSlave *i2c) /* Three input pins plus one power-button pin. */ qdev_init_gpio_in(dev, menelaus_gpio_set, 4); - menelaus_reset(i2c); - - return 0; + menelaus_reset(I2C_SLAVE(dev)); } static void twl92230_class_init(ObjectClass *klass, void *data) @@ -875,7 +872,7 @@ static void twl92230_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - sc->init = twl92230_init; + dc->realize = twl92230_realize; sc->event = menelaus_event; sc->recv = menelaus_rx; sc->send = menelaus_tx; diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index d727379b48..5dc166158b 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -28,9 +28,6 @@ typedef struct I2CSlave I2CSlave; typedef struct I2CSlaveClass { DeviceClass parent_class; - /* Callbacks provided by the device. */ - int (*init)(I2CSlave *dev); - /* Master to slave. Returns non-zero for a NAK, 0 for success. */ int (*send)(I2CSlave *s, uint8_t data); From dbfe00130e4b0cd34d06d693a060655b4f7e95b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 16:45:08 +0200 Subject: [PATCH 0933/2380] qdev: Simplify the SysBusDeviceClass::init path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using SysBusDeviceClass::realize -> DeviceClass::realize -> DeviceClass::init -> sysbus_device_init -> SysBusDeviceClass::init Simplify the path by directly calling SysBusDeviceClass::init in SysBusDeviceClass::realize: SysBusDeviceClass::realize -> SysBusDeviceClass::init Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180419212727.26095-4-f4bug@amsat.org> Reviewed-by: Markus Armbruster [Removal of DeviceClass::init() moved into next patch, sysbus_realize() tweaked for clarity] Signed-off-by: Markus Armbruster Message-Id: <20180528144509.15812-4-armbru@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/sysbus.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 5d0887f499..ecfb0cfc0e 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "monitor/monitor.h" #include "exec/address-spaces.h" @@ -200,15 +201,18 @@ void sysbus_init_ioports(SysBusDevice *dev, uint32_t ioport, uint32_t size) } } -static int sysbus_device_init(DeviceState *dev) +/* TODO remove once all sysbus devices have been converted to realize */ +static void sysbus_realize(DeviceState *dev, Error **errp) { SysBusDevice *sd = SYS_BUS_DEVICE(dev); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(sd); if (!sbc->init) { - return 0; + return; + } + if (sbc->init(sd) < 0) { + error_setg(errp, "Device initialization failed"); } - return sbc->init(sd); } DeviceState *sysbus_create_varargs(const char *name, @@ -324,7 +328,7 @@ MemoryRegion *sysbus_address_space(SysBusDevice *dev) static void sysbus_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); - k->init = sysbus_device_init; + k->realize = sysbus_realize; k->bus_type = TYPE_SYSTEM_BUS; /* * device_add plugs devices into a suitable bus. For "real" buses, From ff46d9d4d6dda7716c534a300087f447c285ad2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 28 May 2018 16:45:09 +0200 Subject: [PATCH 0934/2380] qdev: Remove DeviceClass::init() and ::exit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since no devices use it, we can safely remove it. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180419212727.26095-5-f4bug@amsat.org> Reviewed-by: Markus Armbruster [Removal of DeviceClass::init() moved from previous patch, missing documentation updates supplied] Signed-off-by: Markus Armbruster Message-Id: <20180528144509.15812-5-armbru@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/qdev.c | 28 ---------------------------- include/hw/qdev-core.h | 20 ++++---------------- 2 files changed, 4 insertions(+), 44 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index f6f92473b8..ffec461791 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -208,32 +208,6 @@ void device_listener_unregister(DeviceListener *listener) QTAILQ_REMOVE(&device_listeners, listener, link); } -static void device_realize(DeviceState *dev, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dc->init) { - int rc = dc->init(dev); - if (rc < 0) { - error_setg(errp, "Device initialization failed."); - return; - } - } -} - -static void device_unrealize(DeviceState *dev, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dc->exit) { - int rc = dc->exit(dev); - if (rc < 0) { - error_setg(errp, "Device exit failed."); - return; - } - } -} - void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version) { @@ -1065,8 +1039,6 @@ static void device_class_init(ObjectClass *class, void *data) DeviceClass *dc = DEVICE_CLASS(class); class->unparent = device_unparent; - dc->realize = device_realize; - dc->unrealize = device_unrealize; /* by default all devices were considered as hotpluggable, * so with intent to check it in generic qdev_unplug() / diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 9453588160..f1fd0f8736 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -29,8 +29,6 @@ typedef enum DeviceCategory { DEVICE_CATEGORY_MAX } DeviceCategory; -typedef int (*qdev_initfn)(DeviceState *dev); -typedef int (*qdev_event)(DeviceState *dev); typedef void (*DeviceRealize)(DeviceState *dev, Error **errp); typedef void (*DeviceUnrealize)(DeviceState *dev, Error **errp); typedef void (*DeviceReset)(DeviceState *dev); @@ -43,13 +41,9 @@ struct VMStateDescription; * DeviceClass: * @props: Properties accessing state fields. * @realize: Callback function invoked when the #DeviceState:realized - * property is changed to %true. The default invokes @init if not %NULL. + * property is changed to %true. * @unrealize: Callback function invoked when the #DeviceState:realized * property is changed to %false. - * @init: Callback function invoked when the #DeviceState::realized property - * is changed to %true. Deprecated, new types inheriting directly from - * TYPE_DEVICE should use @realize instead, new leaf types should consult - * their respective parent type. * @hotpluggable: indicates if #DeviceClass is hotpluggable, available * as readonly "hotpluggable" property of #DeviceState instance * @@ -73,19 +67,15 @@ struct VMStateDescription; * object_initialize() in their own #TypeInfo.instance_init and forward the * realization events appropriately. * - * The @init callback is considered private to a particular bus implementation - * (immediate abstract child types of TYPE_DEVICE). Derived leaf types set an - * "init" callback on their parent class instead. - * * Any type may override the @realize and/or @unrealize callbacks but needs * to call the parent type's implementation if keeping their functionality * is desired. Refer to QOM documentation for further discussion and examples. * * * - * If a type derived directly from TYPE_DEVICE implements @realize, it does - * not need to implement @init and therefore does not need to store and call - * #DeviceClass' default @realize callback. + * Since TYPE_DEVICE doesn't implement @realize and @unrealize, types + * derived directly from it need not call their parent's @realize and + * @unrealize. * For other types consult the documentation and implementation of the * respective parent types. * @@ -124,8 +114,6 @@ typedef struct DeviceClass { const struct VMStateDescription *vmsd; /* Private to qdev / bus. */ - qdev_initfn init; /* TODO remove, once users are converted to realize */ - qdev_event exit; /* TODO remove, once users are converted to unrealize */ const char *bus_type; } DeviceClass; From 12fb0ac0575df83cec72ec5ede293561ee634cc9 Mon Sep 17 00:00:00 2001 From: Patryk Olszewski Date: Wed, 23 May 2018 21:50:41 +0200 Subject: [PATCH 0935/2380] char: Remove unwanted crlf conversion This patch fixes a bug in serial that made it almost impossible for guest to communicate with devices through host's serial. OPOST flag in c_oflag enables output processing letting other flags in c_oflag take effect. Usually in c_oflag ONLCR flag is also set, which causes crlf to be sent in place of lf. This breaks binary transmissions. Unsetting OPOST flag turns off any output processing which fixes the bug. Bug reports related: https://bugs.launchpad.net/qemu/+bug/1772086 https://bugs.launchpad.net/qemu/+bug/1407813 https://bugs.launchpad.net/qemu/+bug/1715296 also https://lists.nongnu.org/archive/html/qemu-devel/2006-06/msg00196.html Signed-off-by: Patryk Olszewski Message-Id: <1527105041-21013-1-git-send-email-patryk@fala.ehost.pl> Reviewed-by: Markus Armbruster Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- chardev/char-serial.c | 2 +- chardev/char-stdio.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chardev/char-serial.c b/chardev/char-serial.c index feb52e559d..ae548d28da 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -139,7 +139,7 @@ static void tty_serial_init(int fd, int speed, tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - tty.c_oflag |= OPOST; + tty.c_oflag &= ~OPOST; tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB); switch (data_bits) { diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index 96375f2ab8..d83e60e787 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -59,7 +59,7 @@ static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo) if (!echo) { tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - tty.c_oflag |= OPOST; + tty.c_oflag &= ~OPOST; tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); tty.c_cflag &= ~(CSIZE | PARENB); tty.c_cflag |= CS8; From 73bb753d24a702b37913ce4b5ddb6dca40dab067 Mon Sep 17 00:00:00 2001 From: Tristan Burgess Date: Mon, 28 May 2018 23:04:45 -0400 Subject: [PATCH 0936/2380] memory: Make operations using MemoryRegionIoeventfd struct pass by pointer. This changes the functions memory_region_ioeventfd_equal, memory_region_ioeventfd_before, and their callers, to pass the MemoryRegionIoeventfd struct via pointer, instead of directly passing the struct. This saves on stack space and is considered safe practice. Signed-off-by: Tristan Burgess Message-Id: <20180529030445.177867-1-tburgessdev@gmail.com> Fixes: Launchpad bug 1720969 Signed-off-by: Paolo Bonzini --- memory.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/memory.c b/memory.c index e4f484b73e..af88b12bab 100644 --- a/memory.c +++ b/memory.c @@ -172,38 +172,38 @@ struct MemoryRegionIoeventfd { EventNotifier *e; }; -static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd a, - MemoryRegionIoeventfd b) +static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd *a, + MemoryRegionIoeventfd *b) { - if (int128_lt(a.addr.start, b.addr.start)) { + if (int128_lt(a->addr.start, b->addr.start)) { return true; - } else if (int128_gt(a.addr.start, b.addr.start)) { + } else if (int128_gt(a->addr.start, b->addr.start)) { return false; - } else if (int128_lt(a.addr.size, b.addr.size)) { + } else if (int128_lt(a->addr.size, b->addr.size)) { return true; - } else if (int128_gt(a.addr.size, b.addr.size)) { + } else if (int128_gt(a->addr.size, b->addr.size)) { return false; - } else if (a.match_data < b.match_data) { + } else if (a->match_data < b->match_data) { return true; - } else if (a.match_data > b.match_data) { + } else if (a->match_data > b->match_data) { return false; - } else if (a.match_data) { - if (a.data < b.data) { + } else if (a->match_data) { + if (a->data < b->data) { return true; - } else if (a.data > b.data) { + } else if (a->data > b->data) { return false; } } - if (a.e < b.e) { + if (a->e < b->e) { return true; - } else if (a.e > b.e) { + } else if (a->e > b->e) { return false; } return false; } -static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd a, - MemoryRegionIoeventfd b) +static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd *a, + MemoryRegionIoeventfd *b) { return !memory_region_ioeventfd_before(a, b) && !memory_region_ioeventfd_before(b, a); @@ -788,8 +788,8 @@ static void address_space_add_del_ioeventfds(AddressSpace *as, while (iold < fds_old_nb || inew < fds_new_nb) { if (iold < fds_old_nb && (inew == fds_new_nb - || memory_region_ioeventfd_before(fds_old[iold], - fds_new[inew]))) { + || memory_region_ioeventfd_before(&fds_old[iold], + &fds_new[inew]))) { fd = &fds_old[iold]; section = (MemoryRegionSection) { .fv = address_space_to_flatview(as), @@ -801,8 +801,8 @@ static void address_space_add_del_ioeventfds(AddressSpace *as, ++iold; } else if (inew < fds_new_nb && (iold == fds_old_nb - || memory_region_ioeventfd_before(fds_new[inew], - fds_old[iold]))) { + || memory_region_ioeventfd_before(&fds_new[inew], + &fds_old[iold]))) { fd = &fds_new[inew]; section = (MemoryRegionSection) { .fv = address_space_to_flatview(as), @@ -1440,7 +1440,7 @@ static bool memory_region_dispatch_write_eventfds(MemoryRegion *mr, ioeventfd.match_data = mr->ioeventfds[i].match_data; ioeventfd.e = mr->ioeventfds[i].e; - if (memory_region_ioeventfd_equal(ioeventfd, mr->ioeventfds[i])) { + if (memory_region_ioeventfd_equal(&ioeventfd, &mr->ioeventfds[i])) { event_notifier_set(ioeventfd.e); return true; } @@ -2210,7 +2210,7 @@ void memory_region_add_eventfd(MemoryRegion *mr, } memory_region_transaction_begin(); for (i = 0; i < mr->ioeventfd_nb; ++i) { - if (memory_region_ioeventfd_before(mrfd, mr->ioeventfds[i])) { + if (memory_region_ioeventfd_before(&mrfd, &mr->ioeventfds[i])) { break; } } @@ -2245,7 +2245,7 @@ void memory_region_del_eventfd(MemoryRegion *mr, } memory_region_transaction_begin(); for (i = 0; i < mr->ioeventfd_nb; ++i) { - if (memory_region_ioeventfd_equal(mrfd, mr->ioeventfds[i])) { + if (memory_region_ioeventfd_equal(&mrfd, &mr->ioeventfds[i])) { break; } } From 890b126e6ad305cc60599985c997a34a8ad9ff71 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 22 May 2018 21:44:55 +0200 Subject: [PATCH 0937/2380] hw: allow compiling out SCSI Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini --- default-configs/mips-softmmu-common.mak | 1 + default-configs/pci.mak | 1 + default-configs/riscv32-softmmu.mak | 1 + default-configs/riscv64-softmmu.mak | 1 + default-configs/s390x-softmmu.mak | 1 + default-configs/sparc-softmmu.mak | 1 + default-configs/usb.mak | 1 + hw/Makefile.objs | 2 +- 8 files changed, 8 insertions(+), 1 deletion(-) diff --git a/default-configs/mips-softmmu-common.mak b/default-configs/mips-softmmu-common.mak index e31f046b3b..fae2347ee7 100644 --- a/default-configs/mips-softmmu-common.mak +++ b/default-configs/mips-softmmu-common.mak @@ -4,6 +4,7 @@ include pci.mak include sound.mak include usb.mak CONFIG_ESP=y +CONFIG_SCSI=y CONFIG_VGA_ISA=y CONFIG_VGA_ISA_MM=y CONFIG_VGA_CIRRUS=y diff --git a/default-configs/pci.mak b/default-configs/pci.mak index 35e7596949..163dd814c7 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -15,6 +15,7 @@ CONFIG_PCNET_COMMON=y CONFIG_AC97=y CONFIG_HDA=y CONFIG_ES1370=y +CONFIG_SCSI=y CONFIG_LSI_SCSI_PCI=y CONFIG_VMW_PVSCSI_SCSI_PCI=y CONFIG_MEGASAS_SCSI_PCI=y diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak index f9e742120c..9159a4a8af 100644 --- a/default-configs/riscv32-softmmu.mak +++ b/default-configs/riscv32-softmmu.mak @@ -2,3 +2,4 @@ CONFIG_SERIAL=y CONFIG_VIRTIO=y +CONFIG_SCSI=y diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak index f9e742120c..9159a4a8af 100644 --- a/default-configs/riscv64-softmmu.mak +++ b/default-configs/riscv64-softmmu.mak @@ -2,3 +2,4 @@ CONFIG_SERIAL=y CONFIG_VIRTIO=y +CONFIG_SCSI=y diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index 2f4bfe73b4..729033b25a 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -2,6 +2,7 @@ CONFIG_PCI=y CONFIG_VIRTIO_PCI=$(CONFIG_PCI) CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) +CONFIG_SCSI=y CONFIG_VIRTIO=y CONFIG_SCLPCONSOLE=y CONFIG_TERMINAL3270=y diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak index 004b0f4e77..12f97eeb20 100644 --- a/default-configs/sparc-softmmu.mak +++ b/default-configs/sparc-softmmu.mak @@ -2,6 +2,7 @@ CONFIG_ISA_BUS=y CONFIG_ECC=y +CONFIG_SCSI=y CONFIG_ESP=y CONFIG_ESCC=y CONFIG_M48T59=y diff --git a/default-configs/usb.mak b/default-configs/usb.mak index f4b85684f0..e42cfeabbe 100644 --- a/default-configs/usb.mak +++ b/default-configs/usb.mak @@ -3,6 +3,7 @@ CONFIG_USB_TABLET_WACOM=y CONFIG_USB_STORAGE_BOT=y CONFIG_USB_STORAGE_UAS=y CONFIG_USB_STORAGE_MTP=y +CONFIG_SCSI=y CONFIG_USB_SMARTCARD=y CONFIG_USB_AUDIO=y CONFIG_USB_SERIAL=y diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 6a0ffe0afd..a19c1417ed 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -23,7 +23,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += nvram/ devices-dirs-$(CONFIG_SOFTMMU) += pci/ devices-dirs-$(CONFIG_PCI) += pci-bridge/ pci-host/ devices-dirs-$(CONFIG_SOFTMMU) += pcmcia/ -devices-dirs-$(CONFIG_SOFTMMU) += scsi/ +devices-dirs-$(CONFIG_SCSI) += scsi/ devices-dirs-$(CONFIG_SOFTMMU) += sd/ devices-dirs-$(CONFIG_SOFTMMU) += ssi/ devices-dirs-$(CONFIG_SOFTMMU) += timer/ From b5dfdb082fc350f3e68dfa61dc988d97cad28cfe Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 22 May 2018 21:48:22 +0200 Subject: [PATCH 0938/2380] hw: make virtio devices configurable via default-configs/ This is only half of the work, because the proxy devices (virtio-*-pci, virtio-*-ccw, etc.) are still included unconditionally. It is still a move in the right direction. Based-on: <20180522194943.24871-1-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- default-configs/arm-softmmu.mak | 1 + default-configs/hppa-softmmu.mak | 2 -- default-configs/pci.mak | 4 +--- default-configs/riscv32-softmmu.mak | 4 ++-- default-configs/riscv64-softmmu.mak | 4 ++-- default-configs/s390x-softmmu.mak | 5 +---- default-configs/virtio.mak | 14 ++++++++++++++ hw/9pfs/Makefile.objs | 6 ++++-- hw/block/Makefile.objs | 6 ++---- hw/char/Makefile.objs | 2 +- hw/display/Makefile.objs | 4 ++-- hw/input/Makefile.objs | 6 +++--- hw/net/Makefile.objs | 2 +- hw/scsi/Makefile.objs | 2 +- hw/virtio/Makefile.objs | 16 +++++++++------- 15 files changed, 44 insertions(+), 34 deletions(-) create mode 100644 default-configs/virtio.mak diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index dd29e741c2..8ba2558b36 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -41,6 +41,7 @@ CONFIG_USB=y CONFIG_USB_MUSB=y CONFIG_USB_EHCI_SYSBUS=y CONFIG_PLATFORM_BUS=y +CONFIG_VIRTIO_MMIO=y CONFIG_ARM11MPCORE=y CONFIG_A9MPCORE=y diff --git a/default-configs/hppa-softmmu.mak b/default-configs/hppa-softmmu.mak index 013e5f046f..4badc0521e 100644 --- a/default-configs/hppa-softmmu.mak +++ b/default-configs/hppa-softmmu.mak @@ -4,8 +4,6 @@ CONFIG_SERIAL=y CONFIG_SERIAL_ISA=y CONFIG_ISA_BUS=y CONFIG_I8259=y -CONFIG_VIRTIO_PCI=$(CONFIG_PCI) -CONFIG_VIRTIO=y CONFIG_E1000_PCI=y CONFIG_IDE_ISA=y CONFIG_IDE_CMD646=y diff --git a/default-configs/pci.mak b/default-configs/pci.mak index 163dd814c7..de53d20ac6 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -2,7 +2,7 @@ CONFIG_PCI=y # For now, CONFIG_IDE_CORE requires ISA, so we enable it here CONFIG_ISA_BUS=y CONFIG_VIRTIO_PCI=y -CONFIG_VIRTIO=y +include virtio.mak CONFIG_USB_UHCI=y CONFIG_USB_OHCI=y CONFIG_USB_EHCI=y @@ -46,5 +46,3 @@ CONFIG_VGA=y CONFIG_VGA_PCI=y CONFIG_IVSHMEM_DEVICE=$(CONFIG_IVSHMEM) CONFIG_ROCKER=y -CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) -CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak index 9159a4a8af..20e670d99c 100644 --- a/default-configs/riscv32-softmmu.mak +++ b/default-configs/riscv32-softmmu.mak @@ -1,5 +1,5 @@ # Default configuration for riscv-softmmu CONFIG_SERIAL=y -CONFIG_VIRTIO=y -CONFIG_SCSI=y +CONFIG_VIRTIO_MMIO=y +include virtio.mak diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak index 9159a4a8af..20e670d99c 100644 --- a/default-configs/riscv64-softmmu.mak +++ b/default-configs/riscv64-softmmu.mak @@ -1,5 +1,5 @@ # Default configuration for riscv-softmmu CONFIG_SERIAL=y -CONFIG_VIRTIO=y -CONFIG_SCSI=y +CONFIG_VIRTIO_MMIO=y +include virtio.mak diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index 729033b25a..d6b67d50f0 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -1,9 +1,6 @@ CONFIG_PCI=y CONFIG_VIRTIO_PCI=$(CONFIG_PCI) -CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) -CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) -CONFIG_SCSI=y -CONFIG_VIRTIO=y +include virtio.mak CONFIG_SCLPCONSOLE=y CONFIG_TERMINAL3270=y CONFIG_S390_FLIC=y diff --git a/default-configs/virtio.mak b/default-configs/virtio.mak new file mode 100644 index 0000000000..1304849018 --- /dev/null +++ b/default-configs/virtio.mak @@ -0,0 +1,14 @@ +CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) +CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) +CONFIG_VIRTIO=y +CONFIG_VIRTIO_9P=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_BLK=y +CONFIG_VIRTIO_CRYPTO=y +CONFIG_VIRTIO_GPU=y +CONFIG_VIRTIO_INPUT=y +CONFIG_VIRTIO_NET=y +CONFIG_VIRTIO_RNG=y +CONFIG_SCSI=y +CONFIG_VIRTIO_SCSI=y +CONFIG_VIRTIO_SERIAL=y diff --git a/hw/9pfs/Makefile.objs b/hw/9pfs/Makefile.objs index fd90b62900..e3fa673665 100644 --- a/hw/9pfs/Makefile.objs +++ b/hw/9pfs/Makefile.objs @@ -1,3 +1,4 @@ +ifeq ($(call lor,$(CONFIG_VIRTIO_9P),$(CONFIG_XEN)),y) common-obj-y = 9p.o 9p-util.o common-obj-y += 9p-local.o 9p-xattr.o common-obj-y += 9p-xattr-user.o 9p-posix-acl.o @@ -5,6 +6,7 @@ common-obj-y += coth.o cofs.o codir.o cofile.o common-obj-y += coxattr.o 9p-synth.o common-obj-$(CONFIG_OPEN_BY_HANDLE) += 9p-handle.o common-obj-y += 9p-proxy.o -common-obj-$(CONFIG_XEN) += xen-9p-backend.o +endif -obj-$(CONFIG_VIRTIO) += virtio-9p-device.o +common-obj-$(CONFIG_XEN) += xen-9p-backend.o +obj-$(CONFIG_VIRTIO_9P) += virtio-9p-device.o diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs index 4c19a583c8..53ce5751ae 100644 --- a/hw/block/Makefile.objs +++ b/hw/block/Makefile.objs @@ -11,8 +11,6 @@ common-obj-$(CONFIG_NVME_PCI) += nvme.o obj-$(CONFIG_SH4) += tc58128.o -obj-$(CONFIG_VIRTIO) += virtio-blk.o -obj-$(CONFIG_VIRTIO) += dataplane/ -ifeq ($(CONFIG_VIRTIO),y) +obj-$(CONFIG_VIRTIO_BLK) += virtio-blk.o +obj-$(CONFIG_VIRTIO_BLK) += dataplane/ obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk.o -endif diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 1b979100b7..b570531291 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -6,7 +6,7 @@ common-obj-$(CONFIG_PL011) += pl011.o common-obj-$(CONFIG_SERIAL) += serial.o common-obj-$(CONFIG_SERIAL_ISA) += serial-isa.o common-obj-$(CONFIG_SERIAL_PCI) += serial-pci.o -common-obj-$(CONFIG_VIRTIO) += virtio-console.o +common-obj-$(CONFIG_VIRTIO_SERIAL) += virtio-console.o common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o common-obj-$(CONFIG_XEN) += xen_console.o common-obj-$(CONFIG_CADENCE) += cadence_uart.o diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index d907b381ae..b5d97ab26d 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -36,8 +36,8 @@ obj-$(CONFIG_VGA) += vga.o common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o -obj-$(CONFIG_VIRTIO) += virtio-gpu.o virtio-gpu-3d.o -obj-$(CONFIG_VIRTIO_PCI) += virtio-gpu-pci.o +obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu.o virtio-gpu-3d.o +obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) += virtio-gpu-pci.o obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o virtio-gpu.o-cflags := $(VIRGL_CFLAGS) virtio-gpu.o-libs += $(VIRGL_LIBS) diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs index 77e53e6883..c8b00f71ec 100644 --- a/hw/input/Makefile.objs +++ b/hw/input/Makefile.objs @@ -7,10 +7,10 @@ common-obj-y += ps2.o common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o common-obj-$(CONFIG_TSC2005) += tsc2005.o -common-obj-$(CONFIG_VIRTIO) += virtio-input.o -common-obj-$(CONFIG_VIRTIO) += virtio-input-hid.o +common-obj-$(CONFIG_VIRTIO_INPUT) += virtio-input.o +common-obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-hid.o ifeq ($(CONFIG_LINUX),y) -common-obj-$(CONFIG_VIRTIO) += virtio-input-host.o +common-obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-host.o endif obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index ab22968641..fa461d4463 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -36,7 +36,7 @@ obj-$(CONFIG_MILKYMIST) += milkymist-minimac2.o obj-$(CONFIG_PSERIES) += spapr_llan.o obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o -obj-$(CONFIG_VIRTIO) += virtio-net.o +obj-$(CONFIG_VIRTIO_NET) += virtio-net.o obj-y += vhost_net.o obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs index b188f7242b..718b4c2a68 100644 --- a/hw/scsi/Makefile.objs +++ b/hw/scsi/Makefile.objs @@ -8,7 +8,7 @@ common-obj-$(CONFIG_ESP) += esp.o common-obj-$(CONFIG_ESP_PCI) += esp-pci.o obj-$(CONFIG_PSERIES) += spapr_vscsi.o -ifeq ($(CONFIG_VIRTIO),y) +ifeq ($(CONFIG_VIRTIO_SCSI),y) obj-y += virtio-scsi.o virtio-scsi-dataplane.o obj-$(CONFIG_VHOST_SCSI) += vhost-scsi-common.o vhost-scsi.o obj-$(CONFIG_VHOST_USER_SCSI) += vhost-scsi-common.o vhost-user-scsi.o diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index 765d363c1f..1b2799cfd8 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -1,15 +1,17 @@ ifeq ($(CONFIG_VIRTIO),y) -common-obj-y += virtio-rng.o -common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-y += virtio-bus.o -common-obj-y += virtio-mmio.o +obj-y += virtio.o + +common-obj-$(CONFIG_VIRTIO_RNG) += virtio-rng.o +common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o +common-obj-$(CONFIG_VIRTIO_MMIO) += virtio-mmio.o +obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon.o +obj-$(CONFIG_VIRTIO_CRYPTO) += virtio-crypto.o +obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-pci.o -obj-y += virtio.o virtio-balloon.o obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o -obj-y += virtio-crypto.o -obj-$(CONFIG_VIRTIO_PCI) += virtio-crypto-pci.o endif -common-obj-$(call lnot,$(CONFIG_LINUX)) += vhost-stub.o +common-obj-$(call lnot,$(call land,$(CONFIG_VIRTIO),$(CONFIG_LINUX))) += vhost-stub.o common-obj-$(CONFIG_ALL) += vhost-stub.o From fcc6767836efe1b160289905dce7228d594c123c Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 17:26:39 +0800 Subject: [PATCH 0939/2380] block: Introduce API for copy offloading Introduce the bdrv_co_copy_range() API for copy offloading. Block drivers implementing this API support efficient copy operations that avoid reading each block from the source device and writing it to the destination devices. Examples of copy offload primitives are SCSI EXTENDED COPY and Linux copy_file_range(2). Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Message-id: 20180601092648.24614-2-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- block/io.c | 97 +++++++++++++++++++++++++++++++++++++++ include/block/block.h | 32 +++++++++++++ include/block/block_int.h | 38 +++++++++++++++ 3 files changed, 167 insertions(+) diff --git a/block/io.c b/block/io.c index ca96b487eb..b7beaeeb9f 100644 --- a/block/io.c +++ b/block/io.c @@ -2835,3 +2835,100 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host) bdrv_unregister_buf(child->bs, host); } } + +static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags flags, + bool recurse_src) +{ + int ret; + + if (!src || !dst || !src->bs || !dst->bs) { + return -ENOMEDIUM; + } + ret = bdrv_check_byte_request(src->bs, src_offset, bytes); + if (ret) { + return ret; + } + + ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes); + if (ret) { + return ret; + } + if (flags & BDRV_REQ_ZERO_WRITE) { + return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags); + } + + if (!src->bs->drv->bdrv_co_copy_range_from + || !dst->bs->drv->bdrv_co_copy_range_to + || src->bs->encrypted || dst->bs->encrypted) { + return -ENOTSUP; + } + if (recurse_src) { + return src->bs->drv->bdrv_co_copy_range_from(src->bs, + src, src_offset, + dst, dst_offset, + bytes, flags); + } else { + return dst->bs->drv->bdrv_co_copy_range_to(dst->bs, + src, src_offset, + dst, dst_offset, + bytes, flags); + } +} + +/* Copy range from @src to @dst. + * + * See the comment of bdrv_co_copy_range for the parameter and return value + * semantics. */ +int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, + bytes, flags, true); +} + +/* Copy range from @src to @dst. + * + * See the comment of bdrv_co_copy_range for the parameter and return value + * semantics. */ +int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, + bytes, flags, false); +} + +int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + BdrvTrackedRequest src_req, dst_req; + BlockDriverState *src_bs = src->bs; + BlockDriverState *dst_bs = dst->bs; + int ret; + + bdrv_inc_in_flight(src_bs); + bdrv_inc_in_flight(dst_bs); + tracked_request_begin(&src_req, src_bs, src_offset, + bytes, BDRV_TRACKED_READ); + tracked_request_begin(&dst_req, dst_bs, dst_offset, + bytes, BDRV_TRACKED_WRITE); + + wait_serialising_requests(&src_req); + wait_serialising_requests(&dst_req); + ret = bdrv_co_copy_range_from(src, src_offset, + dst, dst_offset, + bytes, flags); + + tracked_request_end(&src_req); + tracked_request_end(&dst_req); + bdrv_dec_in_flight(src_bs); + bdrv_dec_in_flight(dst_bs); + return ret; +} diff --git a/include/block/block.h b/include/block/block.h index 3894edda9d..6cc6c7e699 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -611,4 +611,36 @@ bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, */ void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size); void bdrv_unregister_buf(BlockDriverState *bs, void *host); + +/** + * + * bdrv_co_copy_range: + * + * Do offloaded copy between two children. If the operation is not implemented + * by the driver, or if the backend storage doesn't support it, a negative + * error code will be returned. + * + * Note: block layer doesn't emulate or fallback to a bounce buffer approach + * because usually the caller shouldn't attempt offloaded copy any more (e.g. + * calling copy_file_range(2)) after the first error, thus it should fall back + * to a read+write path in the caller level. + * + * @src: Source child to copy data from + * @src_offset: offset in @src image to read data + * @dst: Destination child to copy data to + * @dst_offset: offset in @dst image to write data + * @bytes: number of bytes to copy + * @flags: request flags. Must be one of: + * 0 - actually read data from src; + * BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero + * write on @dst as if bdrv_co_pwrite_zeroes is + * called. Used to simplify caller code, or + * during BlockDriver.bdrv_co_copy_range_from() + * recursion. + * + * Returns: 0 if succeeded; negative error code if failed. + **/ +int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags); #endif diff --git a/include/block/block_int.h b/include/block/block_int.h index 6c0927bce3..888b7f7bff 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -204,6 +204,37 @@ struct BlockDriver { int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs, int64_t offset, int bytes); + /* Map [offset, offset + nbytes) range onto a child of @bs to copy from, + * and invoke bdrv_co_copy_range_from(child, ...), or invoke + * bdrv_co_copy_range_to() if @bs is the leaf child to copy data from. + * + * See the comment of bdrv_co_copy_range for the parameter and return value + * semantics. + */ + int coroutine_fn (*bdrv_co_copy_range_from)(BlockDriverState *bs, + BdrvChild *src, + uint64_t offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags flags); + + /* Map [offset, offset + nbytes) range onto a child of bs to copy data to, + * and invoke bdrv_co_copy_range_to(child, src, ...), or perform the copy + * operation if @bs is the leaf and @src has the same BlockDriver. Return + * -ENOTSUP if @bs is the leaf but @src has a different BlockDriver. + * + * See the comment of bdrv_co_copy_range for the parameter and return value + * semantics. + */ + int coroutine_fn (*bdrv_co_copy_range_to)(BlockDriverState *bs, + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags flags); + /* * Building block for bdrv_block_status[_above] and * bdrv_is_allocated[_above]. The driver should answer only @@ -1102,4 +1133,11 @@ void bdrv_dec_in_flight(BlockDriverState *bs); void blockdev_close_all_bdrv_states(void); +int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags); +int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags); + #endif /* BLOCK_INT_H */ From 384455385248762e74a080978f18f0c8f74757fe Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 17:26:40 +0800 Subject: [PATCH 0940/2380] raw: Check byte range uniformly We don't verify the request range against s->size in the I/O callbacks except for raw_co_pwritev. This is inconsistent (especially for raw_co_pwrite_zeroes and raw_co_pdiscard), so fix them, in the meanwhile make the helper reusable by the coming new callbacks. Note that in most cases the block layer already verifies the request byte range against our reported image length, before invoking the driver callbacks. The exception is during image creating, after blk_set_allow_write_beyond_eof(blk, true) is called. But in that case, the requests are not directly from the user or guest. So there is no visible behavior change in adding the check code. The int64_t -> uint64_t inconsistency, as shown by the type casting, is pre-existing due to the interface. Reviewed-by: Stefan Hajnoczi Reviewed-by: Eric Blake Signed-off-by: Fam Zheng Message-id: 20180601092648.24614-3-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- block/raw-format.c | 64 ++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/block/raw-format.c b/block/raw-format.c index fe33693a2d..b69a0674b3 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -167,16 +167,37 @@ static void raw_reopen_abort(BDRVReopenState *state) state->opaque = NULL; } +/* Check and adjust the offset, against 'offset' and 'size' options. */ +static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset, + uint64_t bytes, bool is_write) +{ + BDRVRawState *s = bs->opaque; + + if (s->has_size && (*offset > s->size || bytes > (s->size - *offset))) { + /* There's not enough space for the write, or the read request is + * out-of-range. Don't read/write anything to prevent leaking out of + * the size specified in options. */ + return is_write ? -ENOSPC : -EINVAL;; + } + + if (*offset > INT64_MAX - s->offset) { + return -EINVAL; + } + *offset += s->offset; + + return 0; +} + static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { - BDRVRawState *s = bs->opaque; + int ret; - if (offset > UINT64_MAX - s->offset) { - return -EINVAL; + ret = raw_adjust_offset(bs, &offset, bytes, false); + if (ret) { + return ret; } - offset += s->offset; BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); @@ -186,23 +207,11 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { - BDRVRawState *s = bs->opaque; void *buf = NULL; BlockDriver *drv; QEMUIOVector local_qiov; int ret; - if (s->has_size && (offset > s->size || bytes > (s->size - offset))) { - /* There's not enough space for the data. Don't write anything and just - * fail to prevent leaking out of the size specified in options. */ - return -ENOSPC; - } - - if (offset > UINT64_MAX - s->offset) { - ret = -EINVAL; - goto fail; - } - if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) { /* Handling partial writes would be a pain - so we just * require that guests have 512-byte request alignment if @@ -237,7 +246,10 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, qiov = &local_qiov; } - offset += s->offset; + ret = raw_adjust_offset(bs, &offset, bytes, true); + if (ret) { + goto fail; + } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); @@ -267,22 +279,24 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { - BDRVRawState *s = bs->opaque; - if (offset > UINT64_MAX - s->offset) { - return -EINVAL; + int ret; + + ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); + if (ret) { + return ret; } - offset += s->offset; return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); } static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { - BDRVRawState *s = bs->opaque; - if (offset > UINT64_MAX - s->offset) { - return -EINVAL; + int ret; + + ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); + if (ret) { + return ret; } - offset += s->offset; return bdrv_co_pdiscard(bs->file->bs, offset, bytes); } From 72d219e2f916adeec9845473d239571a267f3314 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 17:26:41 +0800 Subject: [PATCH 0941/2380] raw: Implement copy offloading Just pass down to ->file. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Message-id: 20180601092648.24614-4-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- block/raw-format.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/block/raw-format.c b/block/raw-format.c index b69a0674b3..f2e468df6f 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -497,6 +497,36 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) return bdrv_probe_geometry(bs->file->bs, geo); } +static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + int ret; + + ret = raw_adjust_offset(bs, &src_offset, bytes, false); + if (ret) { + return ret; + } + return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset, + bytes, flags); +} + +static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + int ret; + + ret = raw_adjust_offset(bs, &dst_offset, bytes, true); + if (ret) { + return ret; + } + return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes, + flags); +} + BlockDriver bdrv_raw = { .format_name = "raw", .instance_size = sizeof(BDRVRawState), @@ -513,6 +543,8 @@ BlockDriver bdrv_raw = { .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, .bdrv_co_pdiscard = &raw_co_pdiscard, .bdrv_co_block_status = &raw_co_block_status, + .bdrv_co_copy_range_from = &raw_co_copy_range_from, + .bdrv_co_copy_range_to = &raw_co_copy_range_to, .bdrv_truncate = &raw_truncate, .bdrv_getlength = &raw_getlength, .has_variable_length = true, From fd9fcd37a8645efe322956d94f76e90135522a16 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 17:26:42 +0800 Subject: [PATCH 0942/2380] qcow2: Implement copy offloading The two callbacks are implemented quite similarly to the read/write functions: bdrv_co_copy_range_from maps for read and calls into bs->file or bs->backing depending on the allocation status; bdrv_co_copy_range_to maps for write and calls into bs->file. Reviewed-by: Stefan Hajnoczi Signed-off-by: Fam Zheng Message-id: 20180601092648.24614-5-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- block/qcow2.c | 229 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 199 insertions(+), 30 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index a007dc4246..79e5ed7fbb 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1761,6 +1761,39 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, return status; } +static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, + QCowL2Meta **pl2meta, + bool link_l2) +{ + int ret = 0; + QCowL2Meta *l2meta = *pl2meta; + + while (l2meta != NULL) { + QCowL2Meta *next; + + if (!ret && link_l2) { + ret = qcow2_alloc_cluster_link_l2(bs, l2meta); + if (ret) { + goto out; + } + } + + /* Take the request off the list of running requests */ + if (l2meta->nb_clusters != 0) { + QLIST_REMOVE(l2meta, next_in_flight); + } + + qemu_co_queue_restart_all(&l2meta->dependent_requests); + + next = l2meta->next; + g_free(l2meta); + l2meta = next; + } +out: + *pl2meta = l2meta; + return ret; +} + static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) @@ -2047,24 +2080,9 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, } } - while (l2meta != NULL) { - QCowL2Meta *next; - - ret = qcow2_alloc_cluster_link_l2(bs, l2meta); - if (ret < 0) { - goto fail; - } - - /* Take the request off the list of running requests */ - if (l2meta->nb_clusters != 0) { - QLIST_REMOVE(l2meta, next_in_flight); - } - - qemu_co_queue_restart_all(&l2meta->dependent_requests); - - next = l2meta->next; - g_free(l2meta); - l2meta = next; + ret = qcow2_handle_l2meta(bs, &l2meta, true); + if (ret) { + goto fail; } bytes -= cur_bytes; @@ -2075,18 +2093,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, ret = 0; fail: - while (l2meta != NULL) { - QCowL2Meta *next; - - if (l2meta->nb_clusters != 0) { - QLIST_REMOVE(l2meta, next_in_flight); - } - qemu_co_queue_restart_all(&l2meta->dependent_requests); - - next = l2meta->next; - g_free(l2meta); - l2meta = next; - } + qcow2_handle_l2meta(bs, &l2meta, false); qemu_co_mutex_unlock(&s->lock); @@ -3273,6 +3280,166 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs, return ret; } +static int coroutine_fn +qcow2_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + BDRVQcow2State *s = bs->opaque; + int ret; + unsigned int cur_bytes; /* number of bytes in current iteration */ + BdrvChild *child = NULL; + BdrvRequestFlags cur_flags; + + assert(!bs->encrypted); + qemu_co_mutex_lock(&s->lock); + + while (bytes != 0) { + uint64_t copy_offset = 0; + /* prepare next request */ + cur_bytes = MIN(bytes, INT_MAX); + cur_flags = flags; + + ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset); + if (ret < 0) { + goto out; + } + + switch (ret) { + case QCOW2_CLUSTER_UNALLOCATED: + if (bs->backing && bs->backing->bs) { + int64_t backing_length = bdrv_getlength(bs->backing->bs); + if (src_offset >= backing_length) { + cur_flags |= BDRV_REQ_ZERO_WRITE; + } else { + child = bs->backing; + cur_bytes = MIN(cur_bytes, backing_length - src_offset); + copy_offset = src_offset; + } + } else { + cur_flags |= BDRV_REQ_ZERO_WRITE; + } + break; + + case QCOW2_CLUSTER_ZERO_PLAIN: + case QCOW2_CLUSTER_ZERO_ALLOC: + cur_flags |= BDRV_REQ_ZERO_WRITE; + break; + + case QCOW2_CLUSTER_COMPRESSED: + ret = -ENOTSUP; + goto out; + break; + + case QCOW2_CLUSTER_NORMAL: + child = bs->file; + copy_offset += offset_into_cluster(s, src_offset); + if ((copy_offset & 511) != 0) { + ret = -EIO; + goto out; + } + break; + + default: + abort(); + } + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_copy_range_from(child, + copy_offset, + dst, dst_offset, + cur_bytes, cur_flags); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto out; + } + + bytes -= cur_bytes; + src_offset += cur_bytes; + dst_offset += cur_bytes; + } + ret = 0; + +out: + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static int coroutine_fn +qcow2_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + BDRVQcow2State *s = bs->opaque; + int offset_in_cluster; + int ret; + unsigned int cur_bytes; /* number of sectors in current iteration */ + uint64_t cluster_offset; + uint8_t *cluster_data = NULL; + QCowL2Meta *l2meta = NULL; + + assert(!bs->encrypted); + s->cluster_cache_offset = -1; /* disable compressed cache */ + + qemu_co_mutex_lock(&s->lock); + + while (bytes != 0) { + + l2meta = NULL; + + offset_in_cluster = offset_into_cluster(s, dst_offset); + cur_bytes = MIN(bytes, INT_MAX); + + /* TODO: + * If src->bs == dst->bs, we could simply copy by incrementing + * the refcnt, without copying user data. + * Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */ + ret = qcow2_alloc_cluster_offset(bs, dst_offset, &cur_bytes, + &cluster_offset, &l2meta); + if (ret < 0) { + goto fail; + } + + assert((cluster_offset & 511) == 0); + + ret = qcow2_pre_write_overlap_check(bs, 0, + cluster_offset + offset_in_cluster, cur_bytes); + if (ret < 0) { + goto fail; + } + + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_copy_range_to(src, src_offset, + bs->file, + cluster_offset + offset_in_cluster, + cur_bytes, flags); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto fail; + } + + ret = qcow2_handle_l2meta(bs, &l2meta, true); + if (ret) { + goto fail; + } + + bytes -= cur_bytes; + dst_offset += cur_bytes; + } + ret = 0; + +fail: + qcow2_handle_l2meta(bs, &l2meta, false); + + qemu_co_mutex_unlock(&s->lock); + + qemu_vfree(cluster_data); + trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); + + return ret; +} + static int qcow2_truncate(BlockDriverState *bs, int64_t offset, PreallocMode prealloc, Error **errp) { @@ -4521,6 +4688,8 @@ BlockDriver bdrv_qcow2 = { .bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes, .bdrv_co_pdiscard = qcow2_co_pdiscard, + .bdrv_co_copy_range_from = qcow2_co_copy_range_from, + .bdrv_co_copy_range_to = qcow2_co_copy_range_to, .bdrv_truncate = qcow2_truncate, .bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed, .bdrv_make_empty = qcow2_make_empty, From 1efad060d7e131dd52ecd1e038a6ddd37a3940c8 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 17:26:43 +0800 Subject: [PATCH 0943/2380] file-posix: Implement bdrv_co_copy_range With copy_file_range(2), we can implement the bdrv_co_copy_range semantics. Signed-off-by: Fam Zheng Message-id: 20180601092648.24614-6-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- block/file-posix.c | 98 +++++++++++++++++++++++++++++++++++++++-- configure | 17 +++++++ include/block/raw-aio.h | 10 ++++- 3 files changed, 120 insertions(+), 5 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 5a602cfe37..513d371bb1 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -59,6 +59,7 @@ #ifdef __linux__ #include #include +#include #include #include #include @@ -187,6 +188,8 @@ typedef struct RawPosixAIOData { #define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */ off_t aio_offset; int aio_type; + int aio_fd2; + off_t aio_offset2; } RawPosixAIOData; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) @@ -1446,6 +1449,49 @@ static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) return -ENOTSUP; } +#ifndef HAVE_COPY_FILE_RANGE +static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, + off_t *out_off, size_t len, unsigned int flags) +{ +#ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, in_fd, in_off, out_fd, + out_off, len, flags); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif + +static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb) +{ + uint64_t bytes = aiocb->aio_nbytes; + off_t in_off = aiocb->aio_offset; + off_t out_off = aiocb->aio_offset2; + + while (bytes) { + ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, + aiocb->aio_fd2, &out_off, + bytes, 0); + if (ret == -EINTR) { + continue; + } + if (ret < 0) { + if (errno == ENOSYS) { + return -ENOTSUP; + } else { + return -errno; + } + } + if (!ret) { + /* No progress (e.g. when beyond EOF), fall back to buffer I/O. */ + return -ENOTSUP; + } + bytes -= ret; + } + return 0; +} + static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) { int ret = -EOPNOTSUPP; @@ -1526,6 +1572,9 @@ static int aio_worker(void *arg) case QEMU_AIO_WRITE_ZEROES: ret = handle_aiocb_write_zeroes(aiocb); break; + case QEMU_AIO_COPY_RANGE: + ret = handle_aiocb_copy_range(aiocb); + break; default: fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); ret = -EINVAL; @@ -1536,9 +1585,10 @@ static int aio_worker(void *arg) return ret; } -static int paio_submit_co(BlockDriverState *bs, int fd, - int64_t offset, QEMUIOVector *qiov, - int bytes, int type) +static int paio_submit_co_full(BlockDriverState *bs, int fd, + int64_t offset, int fd2, int64_t offset2, + QEMUIOVector *qiov, + int bytes, int type) { RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); ThreadPool *pool; @@ -1546,6 +1596,8 @@ static int paio_submit_co(BlockDriverState *bs, int fd, acb->bs = bs; acb->aio_type = type; acb->aio_fildes = fd; + acb->aio_fd2 = fd2; + acb->aio_offset2 = offset2; acb->aio_nbytes = bytes; acb->aio_offset = offset; @@ -1561,6 +1613,13 @@ static int paio_submit_co(BlockDriverState *bs, int fd, return thread_pool_submit_co(pool, aio_worker, acb); } +static inline int paio_submit_co(BlockDriverState *bs, int fd, + int64_t offset, QEMUIOVector *qiov, + int bytes, int type) +{ + return paio_submit_co_full(bs, fd, offset, -1, 0, qiov, bytes, type); +} + static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd, int64_t offset, QEMUIOVector *qiov, int bytes, BlockCompletionFunc *cb, void *opaque, int type) @@ -2451,6 +2510,35 @@ static void raw_abort_perm_update(BlockDriverState *bs) raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); } +static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags); +} + +static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + BDRVRawState *s = bs->opaque; + BDRVRawState *src_s; + + assert(dst->bs == bs); + if (src->bs->drv->bdrv_co_copy_range_to != raw_co_copy_range_to) { + return -ENOTSUP; + } + + src_s = src->bs->opaque; + if (fd_open(bs) < 0 || fd_open(bs) < 0) { + return -EIO; + } + return paio_submit_co_full(bs, src_s->fd, src_offset, s->fd, dst_offset, + NULL, bytes, QEMU_AIO_COPY_RANGE); +} + BlockDriver bdrv_file = { .format_name = "file", .protocol_name = "file", @@ -2474,6 +2562,8 @@ BlockDriver bdrv_file = { .bdrv_co_pwritev = raw_co_pwritev, .bdrv_aio_flush = raw_aio_flush, .bdrv_aio_pdiscard = raw_aio_pdiscard, + .bdrv_co_copy_range_from = raw_co_copy_range_from, + .bdrv_co_copy_range_to = raw_co_copy_range_to, .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, @@ -2952,6 +3042,8 @@ static BlockDriver bdrv_host_device = { .bdrv_co_pwritev = raw_co_pwritev, .bdrv_aio_flush = raw_aio_flush, .bdrv_aio_pdiscard = hdev_aio_pdiscard, + .bdrv_co_copy_range_from = raw_co_copy_range_from, + .bdrv_co_copy_range_to = raw_co_copy_range_to, .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, diff --git a/configure b/configure index be6edc7b81..8c6ea9ba7f 100755 --- a/configure +++ b/configure @@ -5183,6 +5183,20 @@ if test "$fortify_source" != "no"; then fi fi +############################################### +# Check if copy_file_range is provided by glibc +have_copy_file_range=no +cat > $TMPC << EOF +#include +int main(void) { + copy_file_range(0, NULL, 0, NULL, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + have_copy_file_range=yes +fi + ########################################## # check if struct fsxattr is available via linux/fs.h @@ -6286,6 +6300,9 @@ fi if test "$have_fsxattr" = "yes" ; then echo "HAVE_FSXATTR=y" >> $config_host_mak fi +if test "$have_copy_file_range" = "yes" ; then + echo "HAVE_COPY_FILE_RANGE=y" >> $config_host_mak +fi if test "$vte" = "yes" ; then echo "CONFIG_VTE=y" >> $config_host_mak echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index 9e47b8a629..0e717fd475 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -25,9 +25,15 @@ #define QEMU_AIO_FLUSH 0x0008 #define QEMU_AIO_DISCARD 0x0010 #define QEMU_AIO_WRITE_ZEROES 0x0020 +#define QEMU_AIO_COPY_RANGE 0x0040 #define QEMU_AIO_TYPE_MASK \ - (QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \ - QEMU_AIO_DISCARD|QEMU_AIO_WRITE_ZEROES) + (QEMU_AIO_READ | \ + QEMU_AIO_WRITE | \ + QEMU_AIO_IOCTL | \ + QEMU_AIO_FLUSH | \ + QEMU_AIO_DISCARD | \ + QEMU_AIO_WRITE_ZEROES | \ + QEMU_AIO_COPY_RANGE) /* AIO flags */ #define QEMU_AIO_MISALIGNED 0x1000 From cc9743c236cce8a35449e3ef67140287b68bb705 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 17:26:44 +0800 Subject: [PATCH 0944/2380] iscsi: Query and save device designator when opening The device designator data returned in INQUIRY command will be useful to fill in source/target fields during copy offloading. Do this when connecting to the target and save the data for later use. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Message-id: 20180601092648.24614-7-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- block/iscsi.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/block/iscsi.c b/block/iscsi.c index 3fd7203916..6d0035d4b9 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -68,6 +68,7 @@ typedef struct IscsiLun { QemuMutex mutex; struct scsi_inquiry_logical_block_provisioning lbp; struct scsi_inquiry_block_limits bl; + struct scsi_inquiry_device_designator *dd; unsigned char *zeroblock; /* The allocmap tracks which clusters (pages) on the iSCSI target are * allocated and which are not. In case a target returns zeros for @@ -1740,6 +1741,30 @@ static QemuOptsList runtime_opts = { }, }; +static void iscsi_save_designator(IscsiLun *lun, + struct scsi_inquiry_device_identification *inq_di) +{ + struct scsi_inquiry_device_designator *desig, *copy = NULL; + + for (desig = inq_di->designators; desig; desig = desig->next) { + if (desig->association || + desig->designator_type > SCSI_DESIGNATOR_TYPE_NAA) { + continue; + } + /* NAA works better than T10 vendor ID based designator. */ + if (!copy || copy->designator_type < desig->designator_type) { + copy = desig; + } + } + if (copy) { + lun->dd = g_new(struct scsi_inquiry_device_designator, 1); + *lun->dd = *copy; + lun->dd->next = NULL; + lun->dd->designator = g_malloc(copy->designator_length); + memcpy(lun->dd->designator, copy->designator, copy->designator_length); + } +} + static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -1922,6 +1947,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, struct scsi_task *inq_task; struct scsi_inquiry_logical_block_provisioning *inq_lbp; struct scsi_inquiry_block_limits *inq_bl; + struct scsi_inquiry_device_identification *inq_di; switch (inq_vpd->pages[i]) { case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, @@ -1947,6 +1973,17 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, sizeof(struct scsi_inquiry_block_limits)); scsi_free_scsi_task(inq_task); break; + case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: + inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, + SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, + (void **) &inq_di, errp); + if (inq_task == NULL) { + ret = -EINVAL; + goto out; + } + iscsi_save_designator(iscsilun, inq_di); + scsi_free_scsi_task(inq_task); + break; default: break; } @@ -2003,6 +2040,10 @@ static void iscsi_close(BlockDriverState *bs) iscsi_logout_sync(iscsi); } iscsi_destroy_context(iscsi); + if (iscsilun->dd) { + g_free(iscsilun->dd->designator); + g_free(iscsilun->dd); + } g_free(iscsilun->zeroblock); iscsi_allocmap_free(iscsilun); qemu_mutex_destroy(&iscsilun->mutex); From 66e75c03b2247bda6dcaa883b700291bc0f7f0ef Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 17:26:45 +0800 Subject: [PATCH 0945/2380] iscsi: Create and use iscsi_co_wait_for_task This loop is repeated a growing number times. Make a helper. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Reviewed-by: Eric Blake Message-id: 20180601092648.24614-8-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- block/iscsi.c | 54 ++++++++++++++++----------------------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/block/iscsi.c b/block/iscsi.c index 6d0035d4b9..6a365cb07b 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -556,6 +556,17 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun, offset / iscsilun->cluster_size) == size); } +static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask, + IscsiLun *iscsilun) +{ + while (!iTask->complete) { + iscsi_set_events(iscsilun); + qemu_mutex_unlock(&iscsilun->mutex); + qemu_coroutine_yield(); + qemu_mutex_lock(&iscsilun->mutex); + } +} + static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *iov, int flags) @@ -617,12 +628,7 @@ retry: scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); #endif - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.task != NULL) { scsi_free_scsi_task(iTask.task); @@ -693,13 +699,7 @@ retry: ret = -ENOMEM; goto out_unlock; } - - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.do_retry) { if (iTask.task != NULL) { @@ -863,13 +863,8 @@ retry: #if LIBISCSI_API_VERSION < (20160603) scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); #endif - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.task != NULL) { scsi_free_scsi_task(iTask.task); iTask.task = NULL; @@ -906,12 +901,7 @@ retry: return -ENOMEM; } - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.task != NULL) { scsi_free_scsi_task(iTask.task); @@ -1143,12 +1133,7 @@ retry: goto out_unlock; } - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.task != NULL) { scsi_free_scsi_task(iTask.task); @@ -1244,12 +1229,7 @@ retry: return -ENOMEM; } - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.status == SCSI_STATUS_CHECK_CONDITION && iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && From 604dfaaa3270081da689991afe83d94d3e8231df Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 17:26:46 +0800 Subject: [PATCH 0946/2380] iscsi: Implement copy offloading Issue EXTENDED COPY (LID1) command to implement the copy_range API. The parameter data construction code is modified from libiscsi's iscsi-dd.c. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Message-id: 20180601092648.24614-9-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- block/iscsi.c | 219 +++++++++++++++++++++++++++++++++++++++ include/scsi/constants.h | 4 + 2 files changed, 223 insertions(+) diff --git a/block/iscsi.c b/block/iscsi.c index 6a365cb07b..c2fbd8a8aa 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2205,6 +2205,221 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs, iscsi_allocmap_invalidate(iscsilun); } +static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags flags) +{ + return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags); +} + +static struct scsi_task *iscsi_xcopy_task(int param_len) +{ + struct scsi_task *task; + + task = g_new0(struct scsi_task, 1); + + task->cdb[0] = EXTENDED_COPY; + task->cdb[10] = (param_len >> 24) & 0xFF; + task->cdb[11] = (param_len >> 16) & 0xFF; + task->cdb[12] = (param_len >> 8) & 0xFF; + task->cdb[13] = param_len & 0xFF; + task->cdb_size = 16; + task->xfer_dir = SCSI_XFER_WRITE; + task->expxferlen = param_len; + + return task; +} + +static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun) +{ + struct scsi_inquiry_device_designator *dd = lun->dd; + + memset(desc, 0, 32); + desc[0] = 0xE4; /* IDENT_DESCR_TGT_DESCR */ + desc[4] = dd->code_set; + desc[5] = (dd->designator_type & 0xF) + | ((dd->association & 3) << 4); + desc[7] = dd->designator_length; + memcpy(desc + 8, dd->designator, dd->designator_length); + + desc[28] = 0; + desc[29] = (lun->block_size >> 16) & 0xFF; + desc[30] = (lun->block_size >> 8) & 0xFF; + desc[31] = lun->block_size & 0xFF; +} + +static void iscsi_xcopy_desc_hdr(uint8_t *hdr, int dc, int cat, int src_index, + int dst_index) +{ + hdr[0] = 0x02; /* BLK_TO_BLK_SEG_DESCR */ + hdr[1] = ((dc << 1) | cat) & 0xFF; + hdr[2] = (XCOPY_BLK2BLK_SEG_DESC_SIZE >> 8) & 0xFF; + /* don't account for the first 4 bytes in descriptor header*/ + hdr[3] = (XCOPY_BLK2BLK_SEG_DESC_SIZE - 4 /* SEG_DESC_SRC_INDEX_OFFSET */) & 0xFF; + hdr[4] = (src_index >> 8) & 0xFF; + hdr[5] = src_index & 0xFF; + hdr[6] = (dst_index >> 8) & 0xFF; + hdr[7] = dst_index & 0xFF; +} + +static void iscsi_xcopy_populate_desc(uint8_t *desc, int dc, int cat, + int src_index, int dst_index, int num_blks, + uint64_t src_lba, uint64_t dst_lba) +{ + iscsi_xcopy_desc_hdr(desc, dc, cat, src_index, dst_index); + + /* The caller should verify the request size */ + assert(num_blks < 65536); + desc[10] = (num_blks >> 8) & 0xFF; + desc[11] = num_blks & 0xFF; + desc[12] = (src_lba >> 56) & 0xFF; + desc[13] = (src_lba >> 48) & 0xFF; + desc[14] = (src_lba >> 40) & 0xFF; + desc[15] = (src_lba >> 32) & 0xFF; + desc[16] = (src_lba >> 24) & 0xFF; + desc[17] = (src_lba >> 16) & 0xFF; + desc[18] = (src_lba >> 8) & 0xFF; + desc[19] = src_lba & 0xFF; + desc[20] = (dst_lba >> 56) & 0xFF; + desc[21] = (dst_lba >> 48) & 0xFF; + desc[22] = (dst_lba >> 40) & 0xFF; + desc[23] = (dst_lba >> 32) & 0xFF; + desc[24] = (dst_lba >> 24) & 0xFF; + desc[25] = (dst_lba >> 16) & 0xFF; + desc[26] = (dst_lba >> 8) & 0xFF; + desc[27] = dst_lba & 0xFF; +} + +static void iscsi_xcopy_populate_header(unsigned char *buf, int list_id, int str, + int list_id_usage, int prio, + int tgt_desc_len, + int seg_desc_len, int inline_data_len) +{ + buf[0] = list_id; + buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7); + buf[2] = (tgt_desc_len >> 8) & 0xFF; + buf[3] = tgt_desc_len & 0xFF; + buf[8] = (seg_desc_len >> 24) & 0xFF; + buf[9] = (seg_desc_len >> 16) & 0xFF; + buf[10] = (seg_desc_len >> 8) & 0xFF; + buf[11] = seg_desc_len & 0xFF; + buf[12] = (inline_data_len >> 24) & 0xFF; + buf[13] = (inline_data_len >> 16) & 0xFF; + buf[14] = (inline_data_len >> 8) & 0xFF; + buf[15] = inline_data_len & 0xFF; +} + +static void iscsi_xcopy_data(struct iscsi_data *data, + IscsiLun *src, int64_t src_lba, + IscsiLun *dst, int64_t dst_lba, + uint16_t num_blocks) +{ + uint8_t *buf; + const int src_offset = XCOPY_DESC_OFFSET; + const int dst_offset = XCOPY_DESC_OFFSET + IDENT_DESCR_TGT_DESCR_SIZE; + const int seg_offset = dst_offset + IDENT_DESCR_TGT_DESCR_SIZE; + + data->size = XCOPY_DESC_OFFSET + + IDENT_DESCR_TGT_DESCR_SIZE * 2 + + XCOPY_BLK2BLK_SEG_DESC_SIZE; + data->data = g_malloc0(data->size); + buf = data->data; + + /* Initialise the parameter list header */ + iscsi_xcopy_populate_header(buf, 1, 0, 2 /* LIST_ID_USAGE_DISCARD */, + 0, 2 * IDENT_DESCR_TGT_DESCR_SIZE, + XCOPY_BLK2BLK_SEG_DESC_SIZE, + 0); + + /* Initialise CSCD list with one src + one dst descriptor */ + iscsi_populate_target_desc(&buf[src_offset], src); + iscsi_populate_target_desc(&buf[dst_offset], dst); + + /* Initialise one segment descriptor */ + iscsi_xcopy_populate_desc(&buf[seg_offset], 0, 0, 0, 1, num_blocks, + src_lba, dst_lba); +} + +static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags flags) +{ + IscsiLun *dst_lun = dst->bs->opaque; + IscsiLun *src_lun; + struct IscsiTask iscsi_task; + struct iscsi_data data; + int r = 0; + int block_size; + + if (src->bs->drv->bdrv_co_copy_range_to != iscsi_co_copy_range_to) { + return -ENOTSUP; + } + src_lun = src->bs->opaque; + + if (!src_lun->dd || !dst_lun->dd) { + return -ENOTSUP; + } + if (!is_byte_request_lun_aligned(dst_offset, bytes, dst_lun)) { + return -ENOTSUP; + } + if (!is_byte_request_lun_aligned(src_offset, bytes, src_lun)) { + return -ENOTSUP; + } + if (dst_lun->block_size != src_lun->block_size || + !dst_lun->block_size) { + return -ENOTSUP; + } + + block_size = dst_lun->block_size; + if (bytes / block_size > 65535) { + return -ENOTSUP; + } + + iscsi_xcopy_data(&data, + src_lun, src_offset / block_size, + dst_lun, dst_offset / block_size, + bytes / block_size); + + iscsi_co_init_iscsitask(dst_lun, &iscsi_task); + + qemu_mutex_lock(&dst_lun->mutex); + iscsi_task.task = iscsi_xcopy_task(data.size); +retry: + if (iscsi_scsi_command_async(dst_lun->iscsi, dst_lun->lun, + iscsi_task.task, iscsi_co_generic_cb, + &data, + &iscsi_task) != 0) { + r = -EIO; + goto out_unlock; + } + + iscsi_co_wait_for_task(&iscsi_task, dst_lun); + + if (iscsi_task.do_retry) { + iscsi_task.complete = 0; + goto retry; + } + + if (iscsi_task.status != SCSI_STATUS_GOOD) { + r = iscsi_task.err_code; + goto out_unlock; + } + +out_unlock: + g_free(iscsi_task.task); + qemu_mutex_unlock(&dst_lun->mutex); + g_free(iscsi_task.err_str); + return r; +} + static QemuOptsList iscsi_create_opts = { .name = "iscsi-create-opts", .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), @@ -2239,6 +2454,8 @@ static BlockDriver bdrv_iscsi = { .bdrv_co_block_status = iscsi_co_block_status, .bdrv_co_pdiscard = iscsi_co_pdiscard, + .bdrv_co_copy_range_from = iscsi_co_copy_range_from, + .bdrv_co_copy_range_to = iscsi_co_copy_range_to, .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, .bdrv_co_readv = iscsi_co_readv, .bdrv_co_writev = iscsi_co_writev, @@ -2274,6 +2491,8 @@ static BlockDriver bdrv_iser = { .bdrv_co_block_status = iscsi_co_block_status, .bdrv_co_pdiscard = iscsi_co_pdiscard, + .bdrv_co_copy_range_from = iscsi_co_copy_range_from, + .bdrv_co_copy_range_to = iscsi_co_copy_range_to, .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, .bdrv_co_readv = iscsi_co_readv, .bdrv_co_writev = iscsi_co_writev, diff --git a/include/scsi/constants.h b/include/scsi/constants.h index a141dd71f8..083a8e887a 100644 --- a/include/scsi/constants.h +++ b/include/scsi/constants.h @@ -311,4 +311,8 @@ #define MMC_PROFILE_HDDVD_RW_DL 0x005A #define MMC_PROFILE_INVALID 0xFFFF +#define XCOPY_DESC_OFFSET 16 +#define IDENT_DESCR_TGT_DESCR_SIZE 32 +#define XCOPY_BLK2BLK_SEG_DESC_SIZE 28 + #endif From b5679fa49c9a70efa7bf01f6efad1a65e2349a0b Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 17:26:47 +0800 Subject: [PATCH 0947/2380] block-backend: Add blk_co_copy_range It's a BlockBackend wrapper of the BDS interface. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Message-id: 20180601092648.24614-10-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- block/block-backend.c | 18 ++++++++++++++++++ include/sysemu/block-backend.h | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/block/block-backend.c b/block/block-backend.c index 89f47b00ea..d55c328736 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -2211,3 +2211,21 @@ void blk_unregister_buf(BlockBackend *blk, void *host) { bdrv_unregister_buf(blk_bs(blk), host); } + +int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, + BlockBackend *blk_out, int64_t off_out, + int bytes, BdrvRequestFlags flags) +{ + int r; + r = blk_check_byte_request(blk_in, off_in, bytes); + if (r) { + return r; + } + r = blk_check_byte_request(blk_out, off_out, bytes); + if (r) { + return r; + } + return bdrv_co_copy_range(blk_in->root, off_in, + blk_out->root, off_out, + bytes, flags); +} diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 92ab624fac..8d03d493c2 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -232,4 +232,8 @@ void blk_set_force_allow_inactivate(BlockBackend *blk); void blk_register_buf(BlockBackend *blk, void *host, size_t size); void blk_unregister_buf(BlockBackend *blk, void *host); +int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, + BlockBackend *blk_out, int64_t off_out, + int bytes, BdrvRequestFlags flags); + #endif From ee5306d0923377439776e8a30b9fd2de34b5cbfb Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 17:26:48 +0800 Subject: [PATCH 0948/2380] qemu-img: Convert with copy offloading The new blk_co_copy_range interface offers a more efficient way in the case of network based storage. Make use of it to allow faster convert operation. Since copy offloading cannot do zero detection ('-S') and compression (-c), only try it when these options are not used. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Message-id: 20180601092648.24614-11-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- qemu-img.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 976b437da0..75f1610aa0 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1547,6 +1547,7 @@ typedef struct ImgConvertState { bool compressed; bool target_has_backing; bool wr_in_order; + bool copy_range; int min_sparse; size_t cluster_sectors; size_t buf_sectors; @@ -1740,6 +1741,37 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, return 0; } +static int coroutine_fn convert_co_copy_range(ImgConvertState *s, int64_t sector_num, + int nb_sectors) +{ + int n, ret; + + while (nb_sectors > 0) { + BlockBackend *blk; + int src_cur; + int64_t bs_sectors, src_cur_offset; + int64_t offset; + + convert_select_part(s, sector_num, &src_cur, &src_cur_offset); + offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS; + blk = s->src[src_cur]; + bs_sectors = s->src_sectors[src_cur]; + + n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset)); + + ret = blk_co_copy_range(blk, offset, s->target, + sector_num << BDRV_SECTOR_BITS, + n << BDRV_SECTOR_BITS, 0); + if (ret < 0) { + return ret; + } + + sector_num += n; + nb_sectors -= n; + } + return 0; +} + static void coroutine_fn convert_co_do_copy(void *opaque) { ImgConvertState *s = opaque; @@ -1762,6 +1794,7 @@ static void coroutine_fn convert_co_do_copy(void *opaque) int n; int64_t sector_num; enum ImgConvertBlockStatus status; + bool copy_range; qemu_co_mutex_lock(&s->lock); if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) { @@ -1791,7 +1824,9 @@ static void coroutine_fn convert_co_do_copy(void *opaque) s->allocated_sectors, 0); } - if (status == BLK_DATA) { +retry: + copy_range = s->copy_range && s->status == BLK_DATA; + if (status == BLK_DATA && !copy_range) { ret = convert_co_read(s, sector_num, n, buf); if (ret < 0) { error_report("error while reading sector %" PRId64 @@ -1813,7 +1848,15 @@ static void coroutine_fn convert_co_do_copy(void *opaque) } if (s->ret == -EINPROGRESS) { - ret = convert_co_write(s, sector_num, n, buf, status); + if (copy_range) { + ret = convert_co_copy_range(s, sector_num, n); + if (ret) { + s->copy_range = false; + goto retry; + } + } else { + ret = convert_co_write(s, sector_num, n, buf, status); + } if (ret < 0) { error_report("error while writing sector %" PRId64 ": %s", sector_num, strerror(-ret)); @@ -1936,6 +1979,7 @@ static int img_convert(int argc, char **argv) ImgConvertState s = (ImgConvertState) { /* Need at least 4k of zeros for sparse detection */ .min_sparse = 8, + .copy_range = true, .buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE, .wr_in_order = true, .num_coroutines = 8, @@ -1976,6 +2020,7 @@ static int img_convert(int argc, char **argv) break; case 'c': s.compressed = true; + s.copy_range = false; break; case 'o': if (!is_valid_option_list(optarg)) { @@ -2017,6 +2062,7 @@ static int img_convert(int argc, char **argv) } s.min_sparse = sval / BDRV_SECTOR_SIZE; + s.copy_range = false; break; } case 'p': From 7421f73966f065c3aa0bb8898b271f984a4417cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 9 May 2018 10:28:34 +0100 Subject: [PATCH 0949/2380] .travis.yml: disable linux-user build for gcov MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the default testing doesn't exercise the linux-user builds so there is no point spending time building them. We may want to enable a separate gcov build once linux-user testing is re-enabled although it's likely to report very low coverage. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c1e99237b2..aa83e9aed7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -74,7 +74,7 @@ matrix: - env: CONFIG="" compiler: clang # gprof/gcov are GCC features - - env: CONFIG="--enable-gprof --enable-gcov --disable-pie" + - env: CONFIG="--enable-gprof --enable-gcov --disable-pie --disable-linux-user" compiler: gcc # We manually include builds which we disable "make check" for - env: CONFIG="--enable-debug --enable-tcg-interpreter" From 1a06f5c910785f1110a74e6575c5f71248b7fab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 15 Jan 2018 11:34:52 -0300 Subject: [PATCH 0950/2380] docker: sort images list displayed by 'make docker' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit we can now directly see different version sort consecutively. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée --- tests/docker/Makefile.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index ef1a3e62eb..50cd51a54e 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -4,7 +4,7 @@ DOCKER_SUFFIX := .docker DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles -DOCKER_IMAGES := $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))) +DOCKER_IMAGES := $(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker)))) DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES)) # Use a global constant ccache directory to speed up repetitive builds DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache From bcaf457786c205d90ca99993048a289017685e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 15 Jan 2018 11:34:53 -0300 Subject: [PATCH 0951/2380] docker: do not display deprecated images in 'make docker' help MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the 'debian' base image is deprecated since 3e11974988d8 Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée --- tests/docker/Makefile.include | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 50cd51a54e..31f21a43f5 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -4,7 +4,8 @@ DOCKER_SUFFIX := .docker DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles -DOCKER_IMAGES := $(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker)))) +DOCKER_DEPRECATED_IMAGES := debian +DOCKER_IMAGES := $(filter-out $(DOCKER_DEPRECATED_IMAGES),$(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))))) DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES)) # Use a global constant ccache directory to speed up repetitive builds DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache @@ -63,7 +64,7 @@ docker-image-debian-win64-cross: docker-image-debian8-mxe docker-image-travis: NOUSER=1 # Expand all the pre-requistes for each docker image and test combination -$(foreach i,$(DOCKER_IMAGES), \ +$(foreach i,$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES), \ $(foreach t,$(DOCKER_TESTS) $(DOCKER_TOOLS), \ $(eval .PHONY: docker-$t@$i) \ $(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \ From 67659ab1eb65771cf8a8a4a6841991a98669e95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 29 May 2018 15:25:27 +0100 Subject: [PATCH 0952/2380] docker: update Travis docker image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is still poorly documented by Travis but according to: https://docs.travis-ci.com/user/common-build-problems/#Running-a-Container-Based-Docker-Image-Locally their reference images are now hosted on Docker Hub. So we update the FROM line to refer to the new default image. We also need a few additional tweaks: - re-enable deb-src lines for our build-dep install - add explicit PATH definition for tools - force the build USER to be Travis - add clang to FEATURES for our test-clang machinery Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/travis.docker | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/docker/dockerfiles/travis.docker b/tests/docker/dockerfiles/travis.docker index 605b6e429b..c5ad39b533 100644 --- a/tests/docker/dockerfiles/travis.docker +++ b/tests/docker/dockerfiles/travis.docker @@ -1,8 +1,13 @@ -FROM quay.io/travisci/travis-ruby +FROM travisci/ci-garnet:packer-1512502276-986baf0 ENV DEBIAN_FRONTEND noninteractive ENV LANG en_US.UTF-8 ENV LC_ALL en_US.UTF-8 +RUN cat /etc/apt/sources.list | sed "s/# deb-src/deb-src/" >> /etc/apt/sources.list RUN apt-get update RUN apt-get -y build-dep qemu RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools -ENV FEATURES pyyaml +# Travis tools require PhantomJS / Neo4j / Maven accessible +# in their PATH (QEMU build won't access them). +ENV PATH /usr/local/phantomjs/bin:/usr/local/phantomjs:/usr/local/neo4j-3.2.7/bin:/usr/local/maven-3.5.2/bin:/usr/local/cmake-3.9.2/bin:/usr/local/clang-5.0.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV FEATURES clang pyyaml +USER travis From ae6d692d88fe5e855fb2d0eee38255bb84d558e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 29 May 2018 18:14:04 +0100 Subject: [PATCH 0953/2380] .travis.yml: rationalise clang testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As Travis includes Clang 5.0 in its own build environment there is no point manually building with older Clangs. We still need to test with the two pythons though so we leave them as minimal system only builds. We also split the clang build into two as it often exceeds the 40 minute build time limit. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- .travis.yml | 69 ++++++----------------------------------------------- 1 file changed, 7 insertions(+), 62 deletions(-) diff --git a/.travis.yml b/.travis.yml index aa83e9aed7..f91db3ab1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,8 +70,10 @@ script: - make ${MAKEFLAGS} && ${TEST_CMD} matrix: include: - # Test with CLang for compile portability - - env: CONFIG="" + # Test with Clang for compile portability (Travis uses clang-5.0) + - env: CONFIG="--disable-system" + compiler: clang + - env: CONFIG="--disable-user" compiler: clang # gprof/gcov are GCC features - env: CONFIG="--enable-gprof --enable-gcov --disable-pie --disable-linux-user" @@ -95,70 +97,13 @@ matrix: - env: CONFIG="" os: osx compiler: clang - # Plain Trusty System Build - - env: CONFIG="--disable-linux-user" - sudo: required - addons: - dist: trusty - compiler: gcc - before_install: - - sudo apt-get update -qq - - sudo apt-get build-dep -qq qemu - - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ - - git submodule update --init --recursive - # Plain Trusty Linux User Build - - env: CONFIG="--disable-system" - sudo: required - addons: - dist: trusty - compiler: gcc - before_install: - - sudo apt-get update -qq - - sudo apt-get build-dep -qq qemu - - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ - - git submodule update --init --recursive - # Trusty System build with latest stable clang & python 3.0 - - sudo: required - addons: - dist: trusty - language: generic - compiler: none + # Python builds + - env: CONFIG="--target-list=x86_64-softmmu" python: - "3.0" - env: - - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 - - CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" - before_install: - - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - - - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' - - sudo apt-get update -qq - - sudo apt-get install -qq -y clang-3.9 - - sudo apt-get build-dep -qq qemu - - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ - - git submodule update --init --recursive - before_script: - - ./configure ${CONFIG} || cat config.log - # Trusty Linux User build with latest stable clang & python 3.6 - - sudo: required - addons: - dist: trusty - language: generic - compiler: none + - env: CONFIG="--target-list=x86_64-softmmu" python: - "3.6" - env: - - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 - - CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" - before_install: - - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - - - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' - - sudo apt-get update -qq - - sudo apt-get install -qq -y clang-3.9 - - sudo apt-get build-dep -qq qemu - - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ - - git submodule update --init --recursive - before_script: - - ./configure ${CONFIG} || cat config.log # Using newer GCC with sanitizers - addons: apt: From 044722d507bc846501da8d43b5f372a49d6b513f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 30 May 2018 09:04:04 +0100 Subject: [PATCH 0954/2380] .travis.yml: make current setup explicit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some commentary and make the selection of Container based Trusty build explicit. We will need to add VM builds later when using docker. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index f91db3ab1a..7197247a34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,8 @@ +# The current Travis default is a container based 14.04 Trust on EC2 +# Additional builds with specific requirements for a full VM need to +# be added as additional matrix: entries later on sudo: false +dist: trusty language: c python: - "2.6" From ac07ffc65472ec3b1255f427051481fc9050a2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 30 May 2018 09:09:31 +0100 Subject: [PATCH 0955/2380] .travis.yml: update GCC sanitizer build to GCC 7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC has moved on and so should we. We also enable apt update to ensure we get the latest build from the toolchain PPA. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- .travis.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7197247a34..814be151f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -111,13 +111,14 @@ matrix: # Using newer GCC with sanitizers - addons: apt: + update: true sources: # PPAs for newer toolchains - ubuntu-toolchain-r-test packages: # Extra toolchains - - gcc-5 - - g++-5 + - gcc-7 + - g++-7 # Build dependencies - libaio-dev - libattr1-dev @@ -146,8 +147,8 @@ matrix: language: generic compiler: none env: - - COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 - - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user" + - COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7 + - CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user" - TEST_CMD="" before_script: - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log From c7950fb3f3c710a70ac1411b894bed854628a5e1 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Thu, 31 May 2018 21:24:19 +0200 Subject: [PATCH 0956/2380] slirp: Fix spurious error report when sending directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move check to where it actually is useful, and reduce scope of 'len' variable along the way. Signed-off-by: Samuel Thibault Reviewed-by: Philippe Mathieu-Daudé --- slirp/socket.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/slirp/socket.c b/slirp/socket.c index e2a71c9b04..08fe98907d 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -340,7 +340,7 @@ sosendoob(struct socket *so) struct sbuf *sb = &so->so_rcv; char buff[2048]; /* XXX Shouldn't be sending more oob data than this */ - int n, len; + int n; DEBUG_CALL("sosendoob"); DEBUG_ARG("so = %p", so); @@ -359,7 +359,7 @@ sosendoob(struct socket *so) * send it all */ uint32_t urgc = so->so_urgc; - len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; + int len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; if (len > urgc) { len = urgc; } @@ -374,13 +374,13 @@ sosendoob(struct socket *so) len += n; } n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */ +#ifdef DEBUG + if (n != len) { + DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n")); + } +#endif } -#ifdef DEBUG - if (n != len) { - DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n")); - } -#endif if (n < 0) { return n; } From 21891a5a3011608845b5d7f1f9cce60cdc2bcc62 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 30 May 2018 20:42:38 +0100 Subject: [PATCH 0957/2380] main-loop: drop spin_counter Commit d759c951f3287fad04210a52f2dc93f94cf58c7f ("replay: push replay_mutex_lock up the call tree") removed the !timeout lock optimization in the main loop. The idea of the optimization was to avoid ping-pongs between threads by keeping the Big QEMU Lock held across non-blocking (!timeout) main loop iterations. A warning is printed when the main loop spins without releasing BQL for long periods of time. These warnings were supposed to aid debugging but in practice they just alarm users. They are considered noise because the cause of spinning is not shown and is hard to find. Now that the lock optimization has been removed, there is no danger of hogging the BQL. Drop the spin counter and the infamous warning. Signed-off-by: Stefan Hajnoczi Reviewed-by: Jeff Cody --- tests/qemu-iotests/common.filter | 1 - util/main-loop.c | 25 ------------------------- 2 files changed, 26 deletions(-) diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index f08ee55046..2031e353a5 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -77,7 +77,6 @@ _filter_qemu() { sed -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \ -e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#' \ - -e '/main-loop: WARNING: I\/O thread spun for [0-9]\+ iterations/d' \ -e $'s#\r##' # QEMU monitor uses \r\n line endings } diff --git a/util/main-loop.c b/util/main-loop.c index 992f9b0f34..affe0403c5 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -222,36 +222,11 @@ static int os_host_main_loop_wait(int64_t timeout) { GMainContext *context = g_main_context_default(); int ret; - static int spin_counter; g_main_context_acquire(context); glib_pollfds_fill(&timeout); - /* If the I/O thread is very busy or we are incorrectly busy waiting in - * the I/O thread, this can lead to starvation of the BQL such that the - * VCPU threads never run. To make sure we can detect the later case, - * print a message to the screen. If we run into this condition, create - * a fake timeout in order to give the VCPU threads a chance to run. - */ - if (!timeout && (spin_counter > MAX_MAIN_LOOP_SPIN)) { - static bool notified; - - if (!notified && !qtest_enabled() && !qtest_driver()) { - warn_report("I/O thread spun for %d iterations", - MAX_MAIN_LOOP_SPIN); - notified = true; - } - - timeout = SCALE_MS; - } - - - if (timeout) { - spin_counter = 0; - } else { - spin_counter++; - } qemu_mutex_unlock_iothread(); replay_mutex_unlock(); From 986bc8ded9a5459e72951cc91b53cf2b52eb735f Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:23 +0300 Subject: [PATCH 0958/2380] crypto: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Acked-by: Daniel P. Berrangé --- crypto/block-luks.c | 2 +- crypto/block-luks.h | 2 +- crypto/block-qcow.c | 2 +- crypto/block-qcow.h | 2 +- crypto/block.c | 6 +++--- crypto/cipher.c | 6 +++--- crypto/ivgen-essiv.c | 2 +- crypto/ivgen-essiv.h | 2 +- crypto/ivgen-plain.c | 2 +- crypto/ivgen-plain.h | 2 +- crypto/ivgen-plain64.c | 2 +- crypto/ivgen-plain64.h | 2 +- crypto/ivgen.c | 8 ++++---- crypto/tlscreds.c | 2 +- crypto/tlscredsanon.c | 2 +- crypto/tlscredsx509.c | 2 +- 16 files changed, 23 insertions(+), 23 deletions(-) diff --git a/crypto/block-luks.c b/crypto/block-luks.c index d418ac30b8..5738124773 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -22,7 +22,7 @@ #include "qapi/error.h" #include "qemu/bswap.h" -#include "crypto/block-luks.h" +#include "block-luks.h" #include "crypto/hash.h" #include "crypto/afsplit.h" diff --git a/crypto/block-luks.h b/crypto/block-luks.h index b2d8a35c1b..befd8b2c56 100644 --- a/crypto/block-luks.h +++ b/crypto/block-luks.h @@ -21,7 +21,7 @@ #ifndef QCRYPTO_BLOCK_LUKS_H #define QCRYPTO_BLOCK_LUKS_H -#include "crypto/blockpriv.h" +#include "blockpriv.h" extern const QCryptoBlockDriver qcrypto_block_driver_luks; diff --git a/crypto/block-qcow.c b/crypto/block-qcow.c index 8817d6aaa7..4284e05167 100644 --- a/crypto/block-qcow.c +++ b/crypto/block-qcow.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "crypto/block-qcow.h" +#include "block-qcow.h" #include "crypto/secret.h" #define QCRYPTO_BLOCK_QCOW_SECTOR_SIZE 512 diff --git a/crypto/block-qcow.h b/crypto/block-qcow.h index 3e2c0a851a..6988fb210b 100644 --- a/crypto/block-qcow.h +++ b/crypto/block-qcow.h @@ -21,7 +21,7 @@ #ifndef QCRYPTO_BLOCK_QCOW_H #define QCRYPTO_BLOCK_QCOW_H -#include "crypto/blockpriv.h" +#include "blockpriv.h" extern const QCryptoBlockDriver qcrypto_block_driver_qcow; diff --git a/crypto/block.c b/crypto/block.c index f206d5eea8..e59d1140fe 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -20,9 +20,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "crypto/blockpriv.h" -#include "crypto/block-qcow.h" -#include "crypto/block-luks.h" +#include "blockpriv.h" +#include "block-qcow.h" +#include "block-luks.h" static const QCryptoBlockDriver *qcrypto_block_drivers[] = { [Q_CRYPTO_BLOCK_FORMAT_QCOW] = &qcrypto_block_driver_qcow, diff --git a/crypto/cipher.c b/crypto/cipher.c index bcbfb3d5b8..b3af57961b 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -150,11 +150,11 @@ qcrypto_cipher_munge_des_rfb_key(const uint8_t *key, #endif /* CONFIG_GCRYPT || CONFIG_NETTLE */ #ifdef CONFIG_GCRYPT -#include "crypto/cipher-gcrypt.c" +#include "cipher-gcrypt.c" #elif defined CONFIG_NETTLE -#include "crypto/cipher-nettle.c" +#include "cipher-nettle.c" #else -#include "crypto/cipher-builtin.c" +#include "cipher-builtin.c" #endif QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, diff --git a/crypto/ivgen-essiv.c b/crypto/ivgen-essiv.c index aeaa8fcd5b..43e258c6f7 100644 --- a/crypto/ivgen-essiv.c +++ b/crypto/ivgen-essiv.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" -#include "crypto/ivgen-essiv.h" +#include "ivgen-essiv.h" typedef struct QCryptoIVGenESSIV QCryptoIVGenESSIV; struct QCryptoIVGenESSIV { diff --git a/crypto/ivgen-essiv.h b/crypto/ivgen-essiv.h index 4a00af849a..f34dbab57b 100644 --- a/crypto/ivgen-essiv.h +++ b/crypto/ivgen-essiv.h @@ -18,7 +18,7 @@ * */ -#include "crypto/ivgenpriv.h" +#include "ivgenpriv.h" #ifndef QCRYPTO_IVGEN_ESSIV_H__ #define QCRYPTO_IVGEN_ESSIV_H__ diff --git a/crypto/ivgen-plain.c b/crypto/ivgen-plain.c index bf2fb7aac4..06f4145fe5 100644 --- a/crypto/ivgen-plain.c +++ b/crypto/ivgen-plain.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" -#include "crypto/ivgen-plain.h" +#include "ivgen-plain.h" static int qcrypto_ivgen_plain_init(QCryptoIVGen *ivgen, const uint8_t *key, size_t nkey, diff --git a/crypto/ivgen-plain.h b/crypto/ivgen-plain.h index 0fe8835c3e..16e1ae5b27 100644 --- a/crypto/ivgen-plain.h +++ b/crypto/ivgen-plain.h @@ -18,7 +18,7 @@ * */ -#include "crypto/ivgenpriv.h" +#include "ivgenpriv.h" #ifndef QCRYPTO_IVGEN_PLAIN_H__ #define QCRYPTO_IVGEN_PLAIN_H__ diff --git a/crypto/ivgen-plain64.c b/crypto/ivgen-plain64.c index e4679a1e6e..fbb7724b20 100644 --- a/crypto/ivgen-plain64.c +++ b/crypto/ivgen-plain64.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" -#include "crypto/ivgen-plain.h" +#include "ivgen-plain.h" static int qcrypto_ivgen_plain_init(QCryptoIVGen *ivgen, const uint8_t *key, size_t nkey, diff --git a/crypto/ivgen-plain64.h b/crypto/ivgen-plain64.h index c4104459b5..f8611bd705 100644 --- a/crypto/ivgen-plain64.h +++ b/crypto/ivgen-plain64.h @@ -18,7 +18,7 @@ * */ -#include "crypto/ivgenpriv.h" +#include "ivgenpriv.h" #ifndef QCRYPTO_IVGEN_PLAIN64_H__ #define QCRYPTO_IVGEN_PLAIN64_H__ diff --git a/crypto/ivgen.c b/crypto/ivgen.c index f66435112b..6a2b3ad01e 100644 --- a/crypto/ivgen.c +++ b/crypto/ivgen.c @@ -21,10 +21,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "crypto/ivgenpriv.h" -#include "crypto/ivgen-plain.h" -#include "crypto/ivgen-plain64.h" -#include "crypto/ivgen-essiv.h" +#include "ivgenpriv.h" +#include "ivgen-plain.h" +#include "ivgen-plain64.h" +#include "ivgen-essiv.h" QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg, diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c index 3cd41035bb..02255a6f3c 100644 --- a/crypto/tlscreds.c +++ b/crypto/tlscreds.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "crypto/tlscredspriv.h" +#include "tlscredspriv.h" #include "trace.h" #define DH_BITS 2048 diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c index 1464220080..7ad66d1e7d 100644 --- a/crypto/tlscredsanon.c +++ b/crypto/tlscredsanon.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "crypto/tlscredsanon.h" -#include "crypto/tlscredspriv.h" +#include "tlscredspriv.h" #include "qapi/error.h" #include "qom/object_interfaces.h" #include "trace.h" diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index 50eb54f6bb..98ee0424e5 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "crypto/tlscredsx509.h" -#include "crypto/tlscredspriv.h" +#include "tlscredspriv.h" #include "crypto/secret.h" #include "qapi/error.h" #include "qom/object_interfaces.h" From 070e9a1e837e1a2ba5c6450eb8ce2e18b29c4af8 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:25 +0300 Subject: [PATCH 0959/2380] hppa: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/hppa/hppa_sys.h | 2 +- hw/hppa/machine.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/hppa/hppa_sys.h b/hw/hppa/hppa_sys.h index a182d1f34e..f98cc8daad 100644 --- a/hw/hppa/hppa_sys.h +++ b/hw/hppa/hppa_sys.h @@ -10,7 +10,7 @@ #include "hw/i386/pc.h" #include "hw/irq.h" -#include "hw/hppa/hppa_hardware.h" +#include "hppa_hardware.h" PCIBus *dino_init(MemoryRegion *, qemu_irq *, qemu_irq *); diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index a1d6b0ebfb..aba269bb85 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -16,7 +16,7 @@ #include "hw/ide.h" #include "hw/timer/i8254.h" #include "hw/char/serial.h" -#include "hw/hppa/hppa_sys.h" +#include "hppa_sys.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "qemu/log.h" From 83ee768d6247bf121b5b8f4b7580fbbc1281d473 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:27 +0300 Subject: [PATCH 0960/2380] migration: drop an unused include In the vmstate.h file, we just need a struct name. Use a forward declaration instead of an include, then adjust the one affected .c file to include the file that is no longer implicit from the header. Signed-off-by: Michael S. Tsirkin Reviewed-by: Eric Blake --- include/migration/vmstate.h | 2 +- migration/savevm.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index df463fd33d..5877caed90 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -27,7 +27,7 @@ #ifndef QEMU_VMSTATE_H #define QEMU_VMSTATE_H -#include "migration/qjson.h" +typedef struct QJSON QJSON; typedef struct VMStateInfo VMStateInfo; typedef struct VMStateDescription VMStateDescription; diff --git a/migration/savevm.c b/migration/savevm.c index 4251125831..da724c52f2 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -55,6 +55,7 @@ #include "io/channel-buffer.h" #include "io/channel-file.h" #include "sysemu/replay.h" +#include "qjson.h" #ifndef ETH_P_RARP #define ETH_P_RARP 0x8035 From a322714248b9e8dffe6a2bb379ffd5d59b394bb7 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:28 +0300 Subject: [PATCH 0961/2380] trace: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- trace/control.h | 2 +- trace/qmp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/trace/control.h b/trace/control.h index 1903e22975..eb65c8e320 100644 --- a/trace/control.h +++ b/trace/control.h @@ -267,6 +267,6 @@ char *trace_opt_parse(const char *optarg); uint32_t trace_get_vcpu_event_count(void); -#include "trace/control-internal.h" +#include "control-internal.h" #endif /* TRACE__CONTROL_H */ diff --git a/trace/qmp.c b/trace/qmp.c index 756086c79f..ea99b00956 100644 --- a/trace/qmp.c +++ b/trace/qmp.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-commands-trace.h" -#include "trace/control.h" +#include "control.h" static CPUState *get_cpu(bool has_vcpu, int vcpu, Error **errp) From e921d6eff9b7fc0260a69bd286b2b134890ef03a Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:31 +0300 Subject: [PATCH 0962/2380] display: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/display/bcm2835_fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 7eab927652..3355f4c131 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/display/bcm2835_fb.h" -#include "hw/display/framebuffer.h" +#include "framebuffer.h" #include "ui/pixel_ops.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "qemu/log.h" From 9314b859bb1065ef9ee1ad2a863aa30f2aa93178 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:31 +0300 Subject: [PATCH 0963/2380] ide: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/ide/ahci-allwinner.c | 2 +- hw/ide/ahci.c | 2 +- hw/ide/ich.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/ide/ahci-allwinner.c b/hw/ide/ahci-allwinner.c index 2fd95078ba..f98e6cb3d4 100644 --- a/hw/ide/ahci-allwinner.c +++ b/hw/ide/ahci-allwinner.c @@ -20,7 +20,7 @@ #include "qemu/error-report.h" #include "sysemu/dma.h" #include "hw/ide/internal.h" -#include "hw/ide/ahci_internal.h" +#include "ahci_internal.h" #include "trace.h" diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index e22d7be05f..24dbad5125 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -31,7 +31,7 @@ #include "sysemu/dma.h" #include "hw/ide/internal.h" #include "hw/ide/pci.h" -#include "hw/ide/ahci_internal.h" +#include "ahci_internal.h" #include "trace.h" diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 134478ebb2..51c935a0da 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -67,7 +67,7 @@ #include "hw/isa/isa.h" #include "sysemu/dma.h" #include "hw/ide/pci.h" -#include "hw/ide/ahci_internal.h" +#include "ahci_internal.h" #define ICH9_MSI_CAP_OFFSET 0x80 #define ICH9_SATA_CAP_OFFSET 0xA8 From 455e17a1b85fbc3d8c7208b5bc9a742d8508b324 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:32 +0300 Subject: [PATCH 0964/2380] ioapic: fix up includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit include files shouldn't have the "include/" part, that is implied. Also, drop an unused include. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/intc/ioapic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 36139a4db6..c45f073271 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -28,9 +28,8 @@ #include "hw/i386/apic.h" #include "hw/i386/ioapic.h" #include "hw/i386/ioapic_internal.h" -#include "include/hw/pci/msi.h" +#include "hw/pci/msi.h" #include "sysemu/kvm.h" -#include "target/i386/cpu.h" #include "hw/i386/apic-msidef.h" #include "hw/i386/x86-iommu.h" #include "trace.h" From e703a5a1e043ad78f9370b1f91825625da4383ed Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:33 +0300 Subject: [PATCH 0965/2380] e1000e: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/net/e1000e.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index 16a9417a85..cda8d48333 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -41,7 +41,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" -#include "hw/net/e1000_regs.h" +#include "e1000_regs.h" #include "e1000x_common.h" #include "e1000e_core.h" From 4147a0358e214551a71c108b4474a5c58835abbb Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:34 +0300 Subject: [PATCH 0966/2380] rocker: drop an unused include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't use net/clients.h, drop that include. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/net/rocker/rocker_fp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c index 27b17c890f..4aa7da79b8 100644 --- a/hw/net/rocker/rocker_fp.c +++ b/hw/net/rocker/rocker_fp.c @@ -15,7 +15,6 @@ */ #include "qemu/osdep.h" -#include "net/clients.h" #include "qapi/qapi-types-rocker.h" #include "rocker.h" #include "rocker_hw.h" From 72a56a1f797c67015bff20d4b77fbf4f6d5b9440 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:35 +0300 Subject: [PATCH 0967/2380] ppc: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Acked-by: David Gibson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/ppc/ppc440_uc.c | 2 +- hw/ppc/sam460ex.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index e312fdba70..123f4ac09d 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -20,7 +20,7 @@ #include "hw/ppc/ppc.h" #include "hw/pci/pci.h" #include "sysemu/block-backend.h" -#include "hw/ppc/ppc440.h" +#include "ppc440.h" /*****************************************************************************/ /* L2 Cache as SRAM */ diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index a48e6e6fce..cb2ab1d1e4 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -27,8 +27,8 @@ #include "elf.h" #include "exec/address-spaces.h" #include "exec/memory.h" -#include "hw/ppc/ppc440.h" -#include "hw/ppc/ppc405.h" +#include "ppc440.h" +#include "ppc405.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" From dbff516dc96c9e4c3ca1ac1826377af88c7b7f1d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:37 +0300 Subject: [PATCH 0968/2380] vhost-scsi: drop an unused include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No reason for vhost-scsi to pull in migration headers directly. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/scsi/vhost-scsi-common.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index 77e9897244..e2a5828af1 100644 --- a/hw/scsi/vhost-scsi-common.c +++ b/hw/scsi/vhost-scsi-common.c @@ -17,7 +17,6 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "migration/migration.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-scsi-common.h" #include "hw/virtio/virtio-scsi.h" From 222ee196abe316ad21be8629b8be8e60bc3e8c4b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:46 +0300 Subject: [PATCH 0969/2380] sd: fix up include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit include files shouldn't have the "include/" part, that is implied. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/sd/milkymist-memcard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c index 5570c1e9a0..fe1cccca76 100644 --- a/hw/sd/milkymist-memcard.c +++ b/hw/sd/milkymist-memcard.c @@ -27,7 +27,7 @@ #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "trace.h" -#include "include/qapi/error.h" +#include "qapi/error.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/sd/sd.h" From 463581a827ea8fd540ddf1d4260a7610d17f1b2e Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:48 +0300 Subject: [PATCH 0970/2380] usb: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/usb/desc-msos.c | 2 +- hw/usb/desc.c | 2 +- hw/usb/dev-audio.c | 2 +- hw/usb/dev-bluetooth.c | 2 +- hw/usb/dev-hid.c | 2 +- hw/usb/dev-hub.c | 2 +- hw/usb/dev-mtp.c | 2 +- hw/usb/dev-network.c | 2 +- hw/usb/dev-serial.c | 2 +- hw/usb/dev-smartcard-reader.c | 2 +- hw/usb/dev-storage.c | 2 +- hw/usb/dev-uas.c | 2 +- hw/usb/dev-wacom.c | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/hw/usb/desc-msos.c b/hw/usb/desc-msos.c index 3652919815..3a5ad7c8d0 100644 --- a/hw/usb/desc-msos.c +++ b/hw/usb/desc-msos.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" /* * Microsoft OS Descriptors diff --git a/hw/usb/desc.c b/hw/usb/desc.c index 85c15addc5..8b6eaea407 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -1,7 +1,7 @@ #include "qemu/osdep.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "trace.h" /* ------------------------------------------------------------------ */ diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index 343345235c..ee43e4914d 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -32,7 +32,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "hw/hw.h" #include "audio/audio.h" diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c index 0bbceaea0b..eac7365b0a 100644 --- a/hw/usb/dev-bluetooth.c +++ b/hw/usb/dev-bluetooth.c @@ -22,7 +22,7 @@ #include "qemu-common.h" #include "qemu/error-report.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "sysemu/bt.h" #include "hw/bt.h" diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index c40019df96..62d18290dc 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -26,7 +26,7 @@ #include "hw/hw.h" #include "ui/console.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "qapi/error.h" #include "qemu/timer.h" #include "hw/input/hid.h" diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 752e30c305..5d9743ef93 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -26,7 +26,7 @@ #include "qemu-common.h" #include "trace.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "qemu/error-report.h" #define NUM_PORTS 8 diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 3d59fe4944..560c61c7c1 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -24,7 +24,7 @@ #include "qemu/iov.h" #include "trace.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" /* ----------------------------------------------------------------------- */ diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index aea7edcf31..385e090336 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -27,7 +27,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "net/net.h" #include "qemu/error-report.h" #include "qemu/queue.h" diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 2829dda391..98d1ca3c91 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -14,7 +14,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "chardev/char-serial.h" #include "chardev/char-fe.h" diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index f7451923f4..2131e33d27 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -39,7 +39,7 @@ #include "qemu-common.h" #include "qemu/error-report.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "ccid.h" diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index b56c75a73a..f2f632aeb0 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -14,7 +14,7 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "hw/scsi/scsi.h" #include "ui/console.h" #include "monitor/monitor.h" diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index c218b53f09..aaf5a88095 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -17,7 +17,7 @@ #include "qemu/error-report.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c index bf70013059..ac0bc83b52 100644 --- a/hw/usb/dev-wacom.c +++ b/hw/usb/dev-wacom.c @@ -29,7 +29,7 @@ #include "hw/hw.h" #include "ui/console.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" /* Interface requests */ #define WACOM_GET_REPORT 0x2101 From 53d37d36caa046d7d93f365e56dcf664f47ccf31 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:51 +0300 Subject: [PATCH 0971/2380] migration: use local path for local headers When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Juan Quintela --- migration/block-dirty-bitmap.c | 2 +- migration/page_cache.c | 2 +- migration/ram.c | 4 ++-- migration/vmstate.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 8819aabe3a..eeccaff34b 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -66,7 +66,7 @@ #include "qemu/error-report.h" #include "migration/misc.h" #include "migration/migration.h" -#include "migration/qemu-file.h" +#include "qemu-file.h" #include "migration/vmstate.h" #include "migration/register.h" #include "qemu/hbitmap.h" diff --git a/migration/page_cache.c b/migration/page_cache.c index 96268c3aea..acc252b100 100644 --- a/migration/page_cache.c +++ b/migration/page_cache.c @@ -18,7 +18,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "qemu/host-utils.h" -#include "migration/page_cache.h" +#include "page_cache.h" #ifdef DEBUG_CACHE #define DPRINTF(fmt, ...) \ diff --git a/migration/ram.c b/migration/ram.c index 5bcbf7a9f9..e7d6cf6492 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -41,7 +41,7 @@ #include "migration/misc.h" #include "qemu-file.h" #include "postcopy-ram.h" -#include "migration/page_cache.h" +#include "page_cache.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-events-migration.h" @@ -51,7 +51,7 @@ #include "exec/target_page.h" #include "qemu/rcu_queue.h" #include "migration/colo.h" -#include "migration/block.h" +#include "block.h" #include "sysemu/sysemu.h" #include "qemu/uuid.h" #include "savevm.h" diff --git a/migration/vmstate.c b/migration/vmstate.c index 0b3282c9df..0a096360e2 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -14,7 +14,7 @@ #include "qemu-common.h" #include "migration.h" #include "migration/vmstate.h" -#include "migration/savevm.h" +#include "savevm.h" #include "qemu-file.h" #include "qemu/bitops.h" #include "qemu/error-report.h" From f27f01db0391990098620cd39473edf03e170183 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:56 +0300 Subject: [PATCH 0972/2380] colo: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Reviewed-by: Zhang Chen Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- net/colo-compare.c | 2 +- net/colo.c | 2 +- net/filter-rewriter.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/colo-compare.c b/net/colo-compare.c index 23b2d2c4cc..c3a2be4c90 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -25,7 +25,7 @@ #include "net/queue.h" #include "chardev/char-fe.h" #include "qemu/sockets.h" -#include "net/colo.h" +#include "colo.h" #include "sysemu/iothread.h" #define TYPE_COLO_COMPARE "colo-compare" diff --git a/net/colo.c b/net/colo.c index 842626502e..6dda4ed66e 100644 --- a/net/colo.c +++ b/net/colo.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "trace.h" -#include "net/colo.h" +#include "colo.h" uint32_t connection_key_hash(const void *opaque) { diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index 62dad2d773..f584e4eba4 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "trace.h" -#include "net/colo.h" +#include "colo.h" #include "net/filter.h" #include "net/net.h" #include "qemu-common.h" From dc03272da6d234a02b8caf938242d26584489e27 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:50:57 +0300 Subject: [PATCH 0973/2380] qga: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé --- qga/channel-posix.c | 2 +- qga/channel-win32.c | 4 ++-- qga/commands-posix.c | 2 +- qga/commands-win32.c | 4 ++-- qga/commands.c | 2 +- qga/guest-agent-command-state.c | 2 +- qga/main.c | 4 ++-- qga/vss-win32.c | 6 +++--- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/qga/channel-posix.c b/qga/channel-posix.c index b812bf4d51..5a925a9818 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -2,7 +2,7 @@ #include #include "qapi/error.h" #include "qemu/sockets.h" -#include "qga/channel.h" +#include "channel.h" #ifdef CONFIG_SOLARIS #include diff --git a/qga/channel-win32.c b/qga/channel-win32.c index 7e6dc4d26f..b3597a8a0f 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" #include #include -#include "qga/guest-agent-core.h" -#include "qga/channel.h" +#include "guest-agent-core.h" +#include "channel.h" typedef struct GAChannelReadState { guint thread_id; diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0dc219dbcf..eae817191b 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -16,7 +16,7 @@ #include #include #include -#include "qga/guest-agent-core.h" +#include "guest-agent-core.h" #include "qga-qapi-commands.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 2d48394748..70ee5379f6 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -32,8 +32,8 @@ #include #include -#include "qga/guest-agent-core.h" -#include "qga/vss-win32.h" +#include "guest-agent-core.h" +#include "vss-win32.h" #include "qga-qapi-commands.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" diff --git a/qga/commands.c b/qga/commands.c index a64b34ccab..cce3010f0f 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "qga/guest-agent-core.h" +#include "guest-agent-core.h" #include "qga-qapi-commands.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" diff --git a/qga/guest-agent-command-state.c b/qga/guest-agent-command-state.c index e609d320f0..18bcb5941d 100644 --- a/qga/guest-agent-command-state.c +++ b/qga/guest-agent-command-state.c @@ -10,7 +10,7 @@ * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" -#include "qga/guest-agent-core.h" +#include "guest-agent-core.h" struct GACommandState { GSList *groups; diff --git a/qga/main.c b/qga/main.c index 1e1cec708f..ea7540edcc 100644 --- a/qga/main.c +++ b/qga/main.c @@ -23,12 +23,12 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qstring.h" -#include "qga/guest-agent-core.h" +#include "guest-agent-core.h" #include "qemu/module.h" #include "qga-qapi-commands.h" #include "qapi/qmp/qerror.h" #include "qapi/error.h" -#include "qga/channel.h" +#include "channel.h" #include "qemu/bswap.h" #include "qemu/help_option.h" #include "qemu/sockets.h" diff --git a/qga/vss-win32.c b/qga/vss-win32.c index 0199c2a792..a541f3ae01 100644 --- a/qga/vss-win32.c +++ b/qga/vss-win32.c @@ -14,9 +14,9 @@ #include #include "qapi/error.h" #include "qemu/error-report.h" -#include "qga/guest-agent-core.h" -#include "qga/vss-win32.h" -#include "qga/vss-win32/requester.h" +#include "guest-agent-core.h" +#include "vss-win32.h" +#include "vss-win32/requester.h" #define QGA_VSS_DLL "qga-vss.dll" From 0041e9a0ac7f140256351a50b218f479981f06b2 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:51:00 +0300 Subject: [PATCH 0974/2380] ui: use local path for local headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pulling in headers that are in the same directory as the C file (as opposed to one in include/), we should use its relative path, without a directory. Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé --- ui/gtk.c | 2 +- ui/input-keymap.c | 2 +- ui/input-legacy.c | 2 +- ui/spice-input.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index dbce970dc4..903f136b8f 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -66,7 +66,7 @@ #define VC_SCALE_STEP 0.25 #ifdef GDK_WINDOWING_X11 -#include "ui/x_keymap.h" +#include "x_keymap.h" /* Gtk2 compat */ #ifndef GDK_IS_X11_DISPLAY diff --git a/ui/input-keymap.c b/ui/input-keymap.c index 3d4e66bab5..87cc301b7a 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "sysemu/sysemu.h" -#include "ui/keymaps.h" +#include "keymaps.h" #include "ui/input.h" #include "standard-headers/linux/input.h" diff --git a/ui/input-legacy.c b/ui/input-legacy.c index e5d4db1d97..549654e26a 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -26,7 +26,7 @@ #include "qapi/qapi-commands-ui.h" #include "sysemu/sysemu.h" #include "ui/console.h" -#include "ui/keymaps.h" +#include "keymaps.h" #include "ui/input.h" struct QEMUPutMouseEntry { diff --git a/ui/spice-input.c b/ui/spice-input.c index 3d41aa1831..a426c03b5e 100644 --- a/ui/spice-input.c +++ b/ui/spice-input.c @@ -23,7 +23,7 @@ #include "qemu-common.h" #include "ui/qemu-spice.h" #include "ui/console.h" -#include "ui/keymaps.h" +#include "keymaps.h" #include "ui/input.h" /* keyboard bits */ From d35ea39e8db126b5f41b3f5c8aa6edf8bc3ded7d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 May 2018 22:51:03 +0300 Subject: [PATCH 0975/2380] arch_init: sort architectures Sort alphabetically. Will help us see if anything is missing (e.g. tile is not there now). Signed-off-by: Michael S. Tsirkin Reviewed-by: Laszlo Ersek --- arch_init.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch_init.c b/arch_init.c index 9597218ced..f4f3f610c8 100644 --- a/arch_init.c +++ b/arch_init.c @@ -52,14 +52,14 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_ARM #elif defined(TARGET_CRIS) #define QEMU_ARCH QEMU_ARCH_CRIS -#elif defined(TARGET_I386) -#define QEMU_ARCH QEMU_ARCH_I386 #elif defined(TARGET_HPPA) #define QEMU_ARCH QEMU_ARCH_HPPA -#elif defined(TARGET_M68K) -#define QEMU_ARCH QEMU_ARCH_M68K +#elif defined(TARGET_I386) +#define QEMU_ARCH QEMU_ARCH_I386 #elif defined(TARGET_LM32) #define QEMU_ARCH QEMU_ARCH_LM32 +#elif defined(TARGET_M68K) +#define QEMU_ARCH QEMU_ARCH_M68K #elif defined(TARGET_MICROBLAZE) #define QEMU_ARCH QEMU_ARCH_MICROBLAZE #elif defined(TARGET_MIPS) @@ -80,12 +80,12 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_SH4 #elif defined(TARGET_SPARC) #define QEMU_ARCH QEMU_ARCH_SPARC -#elif defined(TARGET_XTENSA) -#define QEMU_ARCH QEMU_ARCH_XTENSA -#elif defined(TARGET_UNICORE32) -#define QEMU_ARCH QEMU_ARCH_UNICORE32 #elif defined(TARGET_TRICORE) #define QEMU_ARCH QEMU_ARCH_TRICORE +#elif defined(TARGET_UNICORE32) +#define QEMU_ARCH QEMU_ARCH_UNICORE32 +#elif defined(TARGET_XTENSA) +#define QEMU_ARCH QEMU_ARCH_XTENSA #endif const uint32_t arch_type = QEMU_ARCH; From 2e0c56cdde0b4bbd6ed4070626ed72d1977da07f Mon Sep 17 00:00:00 2001 From: Ross Zwisler Date: Mon, 21 May 2018 10:32:01 -0600 Subject: [PATCH 0976/2380] tests/.gitignore: add entry for generated file After a "make check" we end up with the following: $ git status On branch master Your branch is up-to-date with 'origin/master'. Untracked files: (use "git add ..." to include in what will be committed) tests/test-block-backend nothing added to commit but untracked files present (use "git add" to track) Signed-off-by: Ross Zwisler Fixes: commit ad0df3e0fdac ("block: test blk_aio_flush() with blk->root == NULL") Cc: Kevin Wolf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Eric Blake --- tests/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/.gitignore b/tests/.gitignore index fb62d2299b..2bc61a9a58 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -21,6 +21,7 @@ test-base64 test-bdrv-drain test-bitops test-bitcnt +test-block-backend test-blockjob test-blockjob-txn test-bufferiszero From 9ab3aad2813ce5d9e79c86cb65a013016b61a08f Mon Sep 17 00:00:00 2001 From: Ross Zwisler Date: Mon, 21 May 2018 10:32:02 -0600 Subject: [PATCH 0977/2380] nvdimm, acpi: support NFIT platform capabilities Add a machine command line option to allow the user to control the Platform Capabilities Structure in the virtualized NFIT. This Platform Capabilities Structure was added in ACPI 6.2 Errata A. Signed-off-by: Ross Zwisler Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/nvdimm.txt | 27 +++++++++++++++++++++++++ hw/acpi/nvdimm.c | 45 +++++++++++++++++++++++++++++++++++++---- hw/i386/pc.c | 31 ++++++++++++++++++++++++++++ include/hw/i386/pc.h | 1 + include/hw/mem/nvdimm.h | 5 +++++ 5 files changed, 105 insertions(+), 4 deletions(-) diff --git a/docs/nvdimm.txt b/docs/nvdimm.txt index e903d8bb09..8b48fb4633 100644 --- a/docs/nvdimm.txt +++ b/docs/nvdimm.txt @@ -153,3 +153,30 @@ guest NVDIMM region mapping structure. This unarmed flag indicates guest software that this vNVDIMM device contains a region that cannot accept persistent writes. In result, for example, the guest Linux NVDIMM driver, marks such vNVDIMM device as read-only. + +Platform Capabilities +--------------------- + +ACPI 6.2 Errata A added support for a new Platform Capabilities Structure +which allows the platform to communicate what features it supports related to +NVDIMM data durability. Users can provide a capabilities value to a guest via +the optional "nvdimm-cap" machine command line option: + + -machine pc,accel=kvm,nvdimm,nvdimm-cap=2 + +This "nvdimm-cap" field is an integer, and is the combined value of the +various capability bits defined in table 5-137 of the ACPI 6.2 Errata A spec. + +Here is a quick summary of the three bits that are defined as of that spec: + +Bit[0] - CPU Cache Flush to NVDIMM Durability on Power Loss Capable. +Bit[1] - Memory Controller Flush to NVDIMM Durability on Power Loss Capable. + Note: If bit 0 is set to 1 then this bit shall be set to 1 as well. +Bit[2] - Byte Addressable Persistent Memory Hardware Mirroring Capable. + +So, a "nvdimm-cap" value of 2 would mean that the platform supports Memory +Controller Flush on Power Loss, a value of 3 would mean that the platform +supports CPU Cache Flush and Memory Controller Flush on Power Loss, etc. + +For a complete list of the flags available and for more detailed descriptions, +please consult the ACPI spec. diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 59d6e4254c..87e4280c71 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -169,6 +169,21 @@ struct NvdimmNfitControlRegion { } QEMU_PACKED; typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion; +/* + * NVDIMM Platform Capabilities Structure + * + * Defined in section 5.2.25.9 of ACPI 6.2 Errata A, September 2017 + */ +struct NvdimmNfitPlatformCaps { + uint16_t type; + uint16_t length; + uint8_t highest_cap; + uint8_t reserved[3]; + uint32_t capabilities; + uint8_t reserved2[4]; +} QEMU_PACKED; +typedef struct NvdimmNfitPlatformCaps NvdimmNfitPlatformCaps; + /* * Module serial number is a unique number for each device. We use the * slot id of NVDIMM device to generate this number so that each device @@ -351,7 +366,23 @@ static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev) JEDEC Annex L Release 3. */); } -static GArray *nvdimm_build_device_structure(void) +/* + * ACPI 6.2 Errata A: 5.2.25.9 NVDIMM Platform Capabilities Structure + */ +static void +nvdimm_build_structure_caps(GArray *structures, uint32_t capabilities) +{ + NvdimmNfitPlatformCaps *nfit_caps; + + nfit_caps = acpi_data_push(structures, sizeof(*nfit_caps)); + + nfit_caps->type = cpu_to_le16(7 /* NVDIMM Platform Capabilities */); + nfit_caps->length = cpu_to_le16(sizeof(*nfit_caps)); + nfit_caps->highest_cap = 31 - clz32(capabilities); + nfit_caps->capabilities = cpu_to_le32(capabilities); +} + +static GArray *nvdimm_build_device_structure(AcpiNVDIMMState *state) { GSList *device_list = nvdimm_get_device_list(); GArray *structures = g_array_new(false, true /* clear */, 1); @@ -373,6 +404,10 @@ static GArray *nvdimm_build_device_structure(void) } g_slist_free(device_list); + if (state->capabilities) { + nvdimm_build_structure_caps(structures, state->capabilities); + } + return structures; } @@ -381,16 +416,18 @@ static void nvdimm_init_fit_buffer(NvdimmFitBuffer *fit_buf) fit_buf->fit = g_array_new(false, true /* clear */, 1); } -static void nvdimm_build_fit_buffer(NvdimmFitBuffer *fit_buf) +static void nvdimm_build_fit_buffer(AcpiNVDIMMState *state) { + NvdimmFitBuffer *fit_buf = &state->fit_buf; + g_array_free(fit_buf->fit, true); - fit_buf->fit = nvdimm_build_device_structure(); + fit_buf->fit = nvdimm_build_device_structure(state); fit_buf->dirty = true; } void nvdimm_plug(AcpiNVDIMMState *state) { - nvdimm_build_fit_buffer(&state->fit_buf); + nvdimm_build_fit_buffer(state); } static void nvdimm_build_nfit(AcpiNVDIMMState *state, GArray *table_offsets, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d768930d02..1b2684c549 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -2182,6 +2182,33 @@ static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp) pcms->acpi_nvdimm_state.is_enabled = value; } +static void pc_machine_get_nvdimm_capabilities(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + uint32_t value = pcms->acpi_nvdimm_state.capabilities; + + visit_type_uint32(v, name, &value, errp); +} + +static void pc_machine_set_nvdimm_capabilities(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + Error *error = NULL; + uint32_t value; + + visit_type_uint32(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + pcms->acpi_nvdimm_state.capabilities = value; +} + static bool pc_machine_get_smbus(Object *obj, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); @@ -2395,6 +2422,10 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) object_class_property_add_bool(oc, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm, pc_machine_set_nvdimm, &error_abort); + object_class_property_add(oc, PC_MACHINE_NVDIMM_CAP, "uint32", + pc_machine_get_nvdimm_capabilities, + pc_machine_set_nvdimm_capabilities, NULL, NULL, &error_abort); + object_class_property_add_bool(oc, PC_MACHINE_SMBUS, pc_machine_get_smbus, pc_machine_set_smbus, &error_abort); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index a0c269fc34..04d1f8c6c3 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -76,6 +76,7 @@ struct PCMachineState { #define PC_MACHINE_VMPORT "vmport" #define PC_MACHINE_SMM "smm" #define PC_MACHINE_NVDIMM "nvdimm" +#define PC_MACHINE_NVDIMM_CAP "nvdimm-cap" #define PC_MACHINE_SMBUS "smbus" #define PC_MACHINE_SATA "sata" #define PC_MACHINE_PIT "pit" diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index 74c60332e1..3c82751bab 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -134,6 +134,11 @@ struct AcpiNVDIMMState { /* the IO region used by OSPM to transfer control to QEMU. */ MemoryRegion io_mr; + + /* + * Platform capabilities, section 5.2.25.9 of ACPI 6.2 Errata A + */ + int32_t capabilities; }; typedef struct AcpiNVDIMMState AcpiNVDIMMState; From cab27afa10c241cb99df41f770b29cebd6a91e73 Mon Sep 17 00:00:00 2001 From: Ross Zwisler Date: Mon, 21 May 2018 10:32:03 -0600 Subject: [PATCH 0978/2380] ACPI testing: test NFIT platform capabilities Add testing for the newly added NFIT Platform Capabilities Structure. Signed-off-by: Ross Zwisler Suggested-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/acpi-test-data/pc/NFIT.dimmpxm | Bin 224 -> 240 bytes tests/acpi-test-data/q35/NFIT.dimmpxm | Bin 224 -> 240 bytes tests/bios-tables-test.c | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acpi-test-data/pc/NFIT.dimmpxm b/tests/acpi-test-data/pc/NFIT.dimmpxm index 2bfc6c51f31c25a052803c494c933d4948fc0106..598d331b751cd3cb2137d431c1f34bb8957a0d31 100644 GIT binary patch delta 35 lcmaFB_<@nj&&@OB0|NsCqsm0CYXa;H0t}2m9y1Vw005dH1-}3Q delta 18 Zcmeys_<)hi&&@OB0RsaAqyI#%YXCU~1+M@A diff --git a/tests/acpi-test-data/q35/NFIT.dimmpxm b/tests/acpi-test-data/q35/NFIT.dimmpxm index 2bfc6c51f31c25a052803c494c933d4948fc0106..598d331b751cd3cb2137d431c1f34bb8957a0d31 100644 GIT binary patch delta 35 lcmaFB_<@nj&&@OB0|NsCqsm0CYXa;H0t}2m9y1Vw005dH1-}3Q delta 18 Zcmeys_<)hi&&@OB0RsaAqyI#%YXCU~1+M@A diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index bf3e193ae9..256d463cb8 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -830,7 +830,7 @@ static void test_acpi_tcg_dimm_pxm(const char *machine) memset(&data, 0, sizeof(data)); data.machine = machine; data.variant = ".dimmpxm"; - test_acpi_one(" -machine nvdimm=on" + test_acpi_one(" -machine nvdimm=on,nvdimm-cap=3" " -smp 4,sockets=4" " -m 128M,slots=3,maxmem=1G" " -numa node,mem=32M,nodeid=0" From 25b1d45a1975fd8624c37b5bf42e8502ccf53460 Mon Sep 17 00:00:00 2001 From: Changpeng Liu Date: Tue, 29 May 2018 09:24:35 +0800 Subject: [PATCH 0979/2380] vhost-blk: turn on pre-defined RO feature bit Read only feature shouldn't be negotiable, because if the backend device reported Read only feature supported, QEMU host driver shouldn't change backend's RO attribute. While here, also enable the vhost-user-blk test utility to test RO feature. Signed-off-by: Changpeng Liu Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- contrib/vhost-user-blk/vhost-user-blk.c | 48 ++++++++++++++++++------- hw/block/vhost-user-blk.c | 5 +-- include/hw/virtio/vhost-user-blk.h | 1 - 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index a6a132a492..571f114a56 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -31,6 +31,7 @@ typedef struct VubDev { VugDev parent; int blk_fd; struct virtio_blk_config blkcfg; + bool enable_ro; char *blk_name; GMainLoop *loop; } VubDev; @@ -301,14 +302,27 @@ static void vub_queue_set_started(VuDev *vu_dev, int idx, bool started) static uint64_t vub_get_features(VuDev *dev) { - return 1ull << VIRTIO_BLK_F_SIZE_MAX | - 1ull << VIRTIO_BLK_F_SEG_MAX | - 1ull << VIRTIO_BLK_F_TOPOLOGY | - 1ull << VIRTIO_BLK_F_BLK_SIZE | - 1ull << VIRTIO_BLK_F_FLUSH | - 1ull << VIRTIO_BLK_F_CONFIG_WCE | - 1ull << VIRTIO_F_VERSION_1 | - 1ull << VHOST_USER_F_PROTOCOL_FEATURES; + uint64_t features; + VugDev *gdev; + VubDev *vdev_blk; + + gdev = container_of(dev, VugDev, parent); + vdev_blk = container_of(gdev, VubDev, parent); + + features = 1ull << VIRTIO_BLK_F_SIZE_MAX | + 1ull << VIRTIO_BLK_F_SEG_MAX | + 1ull << VIRTIO_BLK_F_TOPOLOGY | + 1ull << VIRTIO_BLK_F_BLK_SIZE | + 1ull << VIRTIO_BLK_F_FLUSH | + 1ull << VIRTIO_BLK_F_CONFIG_WCE | + 1ull << VIRTIO_F_VERSION_1 | + 1ull << VHOST_USER_F_PROTOCOL_FEATURES; + + if (vdev_blk->enable_ro) { + features |= 1ull << VIRTIO_BLK_F_RO; + } + + return features; } static uint64_t @@ -476,6 +490,7 @@ vub_new(char *blk_file) vub_free(vdev_blk); return NULL; } + vdev_blk->enable_ro = false; vdev_blk->blkcfg.wce = 0; vdev_blk->blk_name = blk_file; @@ -490,10 +505,11 @@ int main(int argc, char **argv) int opt; char *unix_socket = NULL; char *blk_file = NULL; + bool enable_ro = false; int lsock = -1, csock = -1; VubDev *vdev_blk = NULL; - while ((opt = getopt(argc, argv, "b:s:h")) != -1) { + while ((opt = getopt(argc, argv, "b:rs:h")) != -1) { switch (opt) { case 'b': blk_file = g_strdup(optarg); @@ -501,17 +517,20 @@ int main(int argc, char **argv) case 's': unix_socket = g_strdup(optarg); break; + case 'r': + enable_ro = true; + break; case 'h': default: - printf("Usage: %s [-b block device or file, -s UNIX domain socket]" - " | [ -h ]\n", argv[0]); + printf("Usage: %s [ -b block device or file, -s UNIX domain socket" + " | -r Enable read-only ] | [ -h ]\n", argv[0]); return 0; } } if (!unix_socket || !blk_file) { - printf("Usage: %s [-b block device or file, -s UNIX domain socket] |" - " [ -h ]\n", argv[0]); + printf("Usage: %s [ -b block device or file, -s UNIX domain socket" + " | -r Enable read-only ] | [ -h ]\n", argv[0]); return -1; } @@ -530,6 +549,9 @@ int main(int argc, char **argv) if (!vdev_blk) { goto err; } + if (enable_ro) { + vdev_blk->enable_ro = true; + } vug_init(&vdev_blk->parent, csock, vub_panic_cb, &vub_iface); diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 7c3fa8bb1c..d755223643 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -203,13 +203,11 @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH); + virtio_add_feature(&features, VIRTIO_BLK_F_RO); if (s->config_wce) { virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); } - if (s->config_ro) { - virtio_add_feature(&features, VIRTIO_BLK_F_RO); - } if (s->num_queues > 1) { virtio_add_feature(&features, VIRTIO_BLK_F_MQ); } @@ -339,7 +337,6 @@ static Property vhost_user_blk_properties[] = { DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, 1), DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128), DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true), - DEFINE_PROP_BIT("config-ro", VHostUserBlk, config_ro, 0, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h index f1258ae545..d52944aeeb 100644 --- a/include/hw/virtio/vhost-user-blk.h +++ b/include/hw/virtio/vhost-user-blk.h @@ -35,7 +35,6 @@ typedef struct VHostUserBlk { uint16_t num_queues; uint32_t queue_size; uint32_t config_wce; - uint32_t config_ro; struct vhost_dev dev; VhostUserState *vhost_user; } VHostUserBlk; From 07ea28b41830f946de3841b0ac61a3413679feb9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 May 2018 18:06:23 -0700 Subject: [PATCH 0980/2380] tcg: Pass tb and index to tcg_gen_exit_tb separately Do the cast to uintptr_t within the helper, so that the compiler can type check the pointer argument. We can also do some more sanity checking of the index argument. Reviewed-by: Laurent Vivier Signed-off-by: Richard Henderson --- include/exec/gen-icount.h | 2 +- target/alpha/translate.c | 12 ++++++------ target/arm/translate-a64.c | 6 +++--- target/arm/translate.c | 8 ++++---- target/cris/translate.c | 6 +++--- target/hppa/translate.c | 6 +++--- target/i386/translate.c | 8 ++++---- target/lm32/translate.c | 6 +++--- target/m68k/translate.c | 6 +++--- target/microblaze/translate.c | 6 +++--- target/mips/translate.c | 4 ++-- target/moxie/translate.c | 14 +++++++------- target/nios2/translate.c | 8 ++++---- target/openrisc/translate.c | 6 +++--- target/ppc/translate.c | 4 ++-- target/riscv/translate.c | 20 ++++++++++---------- target/s390x/translate.c | 10 +++++----- target/sh4/translate.c | 8 ++++---- target/sparc/translate.c | 16 ++++++++-------- target/tilegx/translate.c | 4 ++-- target/tricore/translate.c | 20 ++++++++++---------- target/unicore32/translate.c | 6 +++--- target/xtensa/translate.c | 4 ++-- tcg/tcg-op.c | 24 ++++++++++++++++++++++-- tcg/tcg-op.h | 17 +++++++++++++---- tcg/tcg.h | 7 ++++--- 26 files changed, 134 insertions(+), 104 deletions(-) diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h index 54aaa61d65..24f7991781 100644 --- a/include/exec/gen-icount.h +++ b/include/exec/gen-icount.h @@ -52,7 +52,7 @@ static inline void gen_tb_end(TranslationBlock *tb, int num_insns) } gen_set_label(tcg_ctx->exitreq_label); - tcg_gen_exit_tb((uintptr_t)tb + TB_EXIT_REQUESTED); + tcg_gen_exit_tb(tb, TB_EXIT_REQUESTED); } static inline void gen_io_start(void) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 15eca71d49..e5d62850c5 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -488,7 +488,7 @@ static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) } else if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb); + tcg_gen_exit_tb(ctx->base.tb, 0); return DISAS_NORETURN; } else { tcg_gen_movi_i64(cpu_pc, dest); @@ -507,12 +507,12 @@ static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb); + tcg_gen_exit_tb(ctx->base.tb, 0); gen_set_label(lab_true); tcg_gen_goto_tb(1); tcg_gen_movi_i64(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb + 1); + tcg_gen_exit_tb(ctx->base.tb, 1); return DISAS_NORETURN; } else { @@ -1273,7 +1273,7 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) if (!use_exit_tb(ctx)) { tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, entry); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb); + tcg_gen_exit_tb(ctx->base.tb, 0); return DISAS_NORETURN; } else { tcg_gen_movi_i64(cpu_pc, entry); @@ -3009,7 +3009,7 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) if (use_goto_tb(ctx, ctx->base.pc_next)) { tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb); + tcg_gen_exit_tb(ctx->base.tb, 0); } /* FALLTHRU */ case DISAS_PC_STALE: @@ -3025,7 +3025,7 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) if (ctx->base.singlestep_enabled) { gen_excp_1(EXCP_DEBUG, 0); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } break; default: diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index b32332ce2c..8d8a4cecb0 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -383,7 +383,7 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) if (use_goto_tb(s, n, dest)) { tcg_gen_goto_tb(n); gen_a64_set_pc_im(dest); - tcg_gen_exit_tb((intptr_t)tb + n); + tcg_gen_exit_tb(tb, n); s->base.is_jmp = DISAS_NORETURN; } else { gen_a64_set_pc_im(dest); @@ -13883,7 +13883,7 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) gen_a64_set_pc_im(dc->pc); /* fall through */ case DISAS_EXIT: - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_JUMP: tcg_gen_lookup_and_goto_ptr(); @@ -13912,7 +13912,7 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* The helper doesn't necessarily throw an exception, but we * must go back to the main loop to check for interrupts anyway. */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; } } diff --git a/target/arm/translate.c b/target/arm/translate.c index 46a9725bfd..0ff5edf2ce 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -995,7 +995,7 @@ static inline void gen_bx_excret_final_code(DisasContext *s) if (is_singlestepping(s)) { gen_singlestep_exception(s); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } gen_set_label(excret_label); /* Yes: this is an exception return. @@ -4263,7 +4263,7 @@ static void gen_goto_tb(DisasContext *s, int n, target_ulong dest) if (use_goto_tb(s, dest)) { tcg_gen_goto_tb(n); gen_set_pc_im(s, dest); - tcg_gen_exit_tb((uintptr_t)s->base.tb + n); + tcg_gen_exit_tb(s->base.tb, n); } else { gen_set_pc_im(s, dest); gen_goto_ptr(); @@ -12699,7 +12699,7 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* fall through */ default: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_NORETURN: /* nothing more to generate */ @@ -12714,7 +12714,7 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* The helper doesn't necessarily throw an exception, but we * must go back to the main loop to check for interrupts anyway. */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; } case DISAS_WFE: diff --git a/target/cris/translate.c b/target/cris/translate.c index d2f05971ab..4ae1c04daf 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -540,10 +540,10 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(env_pc, dest); - tcg_gen_exit_tb((uintptr_t)dc->tb + n); + tcg_gen_exit_tb(dc->tb, n); } else { tcg_gen_movi_tl(env_pc, dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -3276,7 +3276,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) case DISAS_UPDATE: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_SWI: case DISAS_TB_JUMP: diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 5320b217de..ce05d5619d 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -779,7 +779,7 @@ static void gen_goto_tb(DisasContext *ctx, int which, tcg_gen_goto_tb(which); tcg_gen_movi_reg(cpu_iaoq_f, f); tcg_gen_movi_reg(cpu_iaoq_b, b); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb + which); + tcg_gen_exit_tb(ctx->base.tb, which); } else { copy_iaoq_entry(cpu_iaoq_f, f, cpu_iaoq_b); copy_iaoq_entry(cpu_iaoq_b, b, ctx->iaoq_n_var); @@ -2303,7 +2303,7 @@ static DisasJumpType trans_rfi(DisasContext *ctx, uint32_t insn, if (ctx->base.singlestep_enabled) { gen_excp_1(EXCP_DEBUG); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } /* Exit the TB to recognize new interrupts. */ @@ -4844,7 +4844,7 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) if (ctx->base.singlestep_enabled) { gen_excp_1(EXCP_DEBUG); } else if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); } diff --git a/target/i386/translate.c b/target/i386/translate.c index 7c21814676..697a918c11 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -2193,7 +2193,7 @@ static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) /* jump to same page: we can use a direct jump */ tcg_gen_goto_tb(tb_num); gen_jmp_im(eip); - tcg_gen_exit_tb((uintptr_t)s->base.tb + tb_num); + tcg_gen_exit_tb(s->base.tb, tb_num); s->base.is_jmp = DISAS_NORETURN; } else { /* jump to another page */ @@ -2572,13 +2572,13 @@ do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) gen_helper_debug(cpu_env); } else if (recheck_tf) { gen_helper_rechecking_single_step(cpu_env); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } else if (s->tf) { gen_helper_single_step(cpu_env); } else if (jr) { tcg_gen_lookup_and_goto_ptr(); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } s->base.is_jmp = DISAS_NORETURN; } @@ -7402,7 +7402,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_jmp_im(pc_start - s->cs_base); gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), tcg_const_i32(s->pc - pc_start)); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); s->base.is_jmp = DISAS_NORETURN; break; diff --git a/target/lm32/translate.c b/target/lm32/translate.c index fdd206a860..b32feb7564 100644 --- a/target/lm32/translate.c +++ b/target/lm32/translate.c @@ -159,13 +159,13 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)dc->tb + n); + tcg_gen_exit_tb(dc->tb, n); } else { tcg_gen_movi_tl(cpu_pc, dest); if (dc->singlestep_enabled) { t_gen_raise_exception(dc, EXCP_DEBUG); } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -1137,7 +1137,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) case DISAS_UPDATE: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: /* nothing more to generate */ diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 8959e4d0f0..37d6ffd853 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -1491,10 +1491,10 @@ static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest) } else if (use_goto_tb(s, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(QREG_PC, dest); - tcg_gen_exit_tb((uintptr_t)s->tb + n); + tcg_gen_exit_tb(s->tb, n); } else { gen_jmp_im(s, dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } s->is_jmp = DISAS_TB_JUMP; } @@ -6147,7 +6147,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) case DISAS_UPDATE: update_cc_op(dc); /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: /* nothing more to generate */ diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index b79600cba5..6c64946398 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -143,10 +143,10 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i64(cpu_SR[SR_PC], dest); - tcg_gen_exit_tb((uintptr_t)dc->tb + n); + tcg_gen_exit_tb(dc->tb, n); } else { tcg_gen_movi_i64(cpu_SR[SR_PC], dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -1766,7 +1766,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) case DISAS_UPDATE: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: /* nothing more to generate */ diff --git a/target/mips/translate.c b/target/mips/translate.c index e88f983ae7..e57d71e485 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -4291,7 +4291,7 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); gen_save_pc(dest); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb + n); + tcg_gen_exit_tb(ctx->base.tb, n); } else { gen_save_pc(dest); if (ctx->base.singlestep_enabled) { @@ -20344,7 +20344,7 @@ static void mips_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) gen_goto_tb(ctx, 0, ctx->base.pc_next); break; case DISAS_EXIT: - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_NORETURN: break; diff --git a/target/moxie/translate.c b/target/moxie/translate.c index 28b405f0e4..29da02bc05 100644 --- a/target/moxie/translate.c +++ b/target/moxie/translate.c @@ -132,13 +132,13 @@ static inline void gen_goto_tb(CPUMoxieState *env, DisasContext *ctx, if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->tb + n); + tcg_gen_exit_tb(ctx->tb, n); } else { tcg_gen_movi_i32(cpu_pc, dest); if (ctx->singlestep_enabled) { gen_helper_debug(cpu_env); } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -328,7 +328,7 @@ static int decode_opc(MoxieCPU *cpu, DisasContext *ctx) tcg_temp_free_i32(t1); /* Jump... */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; } @@ -472,14 +472,14 @@ static int decode_opc(MoxieCPU *cpu, DisasContext *ctx) tcg_gen_mov_i32(cpu_pc, REG(fnreg)); tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; } break; case 0x1a: /* jmpa */ { tcg_gen_movi_i32(cpu_pc, cpu_ldl_code(env, ctx->pc+2)); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; length = 6; } @@ -584,7 +584,7 @@ static int decode_opc(MoxieCPU *cpu, DisasContext *ctx) { int reg = (opcode >> 4) & 0xf; tcg_gen_mov_i32(cpu_pc, REG(reg)); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; } break; @@ -878,7 +878,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) gen_goto_tb(env, &ctx, 0, ctx.pc); break; case BS_EXCP: - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case BS_BRANCH: default: diff --git a/target/nios2/translate.c b/target/nios2/translate.c index cb8624e8d2..7fa03ed05a 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -171,10 +171,10 @@ static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); - tcg_gen_exit_tb((uintptr_t)tb + n); + tcg_gen_exit_tb(tb, n); } else { tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -880,14 +880,14 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) case DISAS_NEXT: /* Save the current PC back into the CPU register */ tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; default: case DISAS_JUMP: case DISAS_UPDATE: /* The jump will already have updated the PC register */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index e7c96ca990..d69f8d0422 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -183,13 +183,13 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) if (use_goto_tb(dc, dest)) { tcg_gen_movi_tl(cpu_pc, dest); tcg_gen_goto_tb(n); - tcg_gen_exit_tb((uintptr_t)dc->base.tb + n); + tcg_gen_exit_tb(dc->base.tb, n); } else { tcg_gen_movi_tl(cpu_pc, dest); if (dc->base.singlestep_enabled) { gen_exception(dc, EXCP_DEBUG); } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -1473,7 +1473,7 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) case DISAS_UPDATE: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; default: g_assert_not_reached(); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index e30d99fcbc..b28e8b91d3 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3422,7 +3422,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_nip, dest & ~3); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb + n); + tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_tl(cpu_nip, dest & ~3); if (unlikely(ctx->singlestep_enabled)) { @@ -7410,7 +7410,7 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) gen_debug_exception(ctx); } /* Generate the return instruction */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index ee2bbc55b0..0b6be74f2d 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -129,13 +129,13 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) /* chaining is only allowed when the jump is to the same page */ tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb + n); + tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_tl(cpu_pc, dest); if (ctx->base.singlestep_enabled) { gen_exception_debug(); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } } @@ -548,7 +548,7 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, if (rd != 0) { tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn); } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); if (misaligned) { gen_set_label(misaligned); @@ -1303,12 +1303,12 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, case 0x0: /* ECALL */ /* always generates U-level ECALL, fixed in do_interrupt handler */ generate_exception(ctx, RISCV_EXCP_U_ECALL); - tcg_gen_exit_tb(0); /* no chaining */ + tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; break; case 0x1: /* EBREAK */ generate_exception(ctx, RISCV_EXCP_BREAKPOINT); - tcg_gen_exit_tb(0); /* no chaining */ + tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; break; #ifndef CONFIG_USER_ONLY @@ -1318,7 +1318,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, case 0x102: /* SRET */ if (riscv_has_ext(env, RVS)) { gen_helper_sret(cpu_pc, cpu_env, cpu_pc); - tcg_gen_exit_tb(0); /* no chaining */ + tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; } else { gen_exception_illegal(ctx); @@ -1329,7 +1329,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, break; case 0x302: /* MRET */ gen_helper_mret(cpu_pc, cpu_env, cpu_pc); - tcg_gen_exit_tb(0); /* no chaining */ + tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; break; case 0x7b2: /* DRET */ @@ -1378,7 +1378,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_set_gpr(rd, dest); /* end tb since we may be changing priv modes, to get mmu_index right */ tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - tcg_gen_exit_tb(0); /* no chaining */ + tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; break; } @@ -1771,7 +1771,7 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) /* FENCE_I is a no-op in QEMU, * however we need to end the translation block */ tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->base.is_jmp = DISAS_NORETURN; } else { /* FENCE is a full memory barrier. */ @@ -1872,7 +1872,7 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) if (ctx->base.singlestep_enabled) { gen_exception_debug(); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } break; case DISAS_NORETURN: diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 82309faa11..fdfec7feba 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -1159,7 +1159,7 @@ static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) per_breaking_event(s); tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb((uintptr_t)s->base.tb); + tcg_gen_exit_tb(s->base.tb, 0); return DISAS_GOTO_TB; } else { tcg_gen_movi_i64(psw_addr, dest); @@ -1220,14 +1220,14 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, /* Branch not taken. */ tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, s->pc_tmp); - tcg_gen_exit_tb((uintptr_t)s->base.tb + 0); + tcg_gen_exit_tb(s->base.tb, 0); /* Branch taken. */ gen_set_label(lab); per_breaking_event(s); tcg_gen_goto_tb(1); tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb((uintptr_t)s->base.tb + 1); + tcg_gen_exit_tb(s->base.tb, 1); ret = DISAS_GOTO_TB; } else { @@ -1250,7 +1250,7 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, update_cc_op(s); tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, s->pc_tmp); - tcg_gen_exit_tb((uintptr_t)s->base.tb + 0); + tcg_gen_exit_tb(s->base.tb, 0); gen_set_label(lab); if (is_imm) { @@ -6240,7 +6240,7 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) gen_exception(EXCP_DEBUG); } else if (use_exit_tb(dc) || dc->base.is_jmp == DISAS_PC_STALE_NOCHAIN) { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); } diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 58bdfeb4fb..c716b74a0f 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -242,13 +242,13 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb + n); + tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_i32(cpu_pc, dest); if (ctx->base.singlestep_enabled) { gen_helper_debug(cpu_env); } else if (use_exit_tb(ctx)) { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); } @@ -266,7 +266,7 @@ static void gen_jump(DisasContext * ctx) if (ctx->base.singlestep_enabled) { gen_helper_debug(cpu_env); } else if (use_exit_tb(ctx)) { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); } @@ -2343,7 +2343,7 @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) if (ctx->base.singlestep_enabled) { gen_helper_debug(cpu_env); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } break; case DISAS_NEXT: diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 40b2eaad39..f3d430c1b2 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -360,12 +360,12 @@ static inline void gen_goto_tb(DisasContext *s, int tb_num, tcg_gen_goto_tb(tb_num); tcg_gen_movi_tl(cpu_pc, pc); tcg_gen_movi_tl(cpu_npc, npc); - tcg_gen_exit_tb((uintptr_t)s->base.tb + tb_num); + tcg_gen_exit_tb(s->base.tb, tb_num); } else { /* jump to another page: currently not optimized */ tcg_gen_movi_tl(cpu_pc, pc); tcg_gen_movi_tl(cpu_npc, npc); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -4329,7 +4329,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) /* End TB to notice changed ASI. */ save_state(dc); gen_op_next_insn(); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); dc->base.is_jmp = DISAS_NORETURN; break; case 0x6: /* V9 wrfprs */ @@ -4338,7 +4338,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) dc->fprs_dirty = 0; save_state(dc); gen_op_next_insn(); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); dc->base.is_jmp = DISAS_NORETURN; break; case 0xf: /* V9 sir, nop if user */ @@ -4466,7 +4466,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) dc->cc_op = CC_OP_FLAGS; save_state(dc); gen_op_next_insn(); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); dc->base.is_jmp = DISAS_NORETURN; #endif } @@ -4622,7 +4622,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) hpstate)); save_state(dc); gen_op_next_insn(); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); dc->base.is_jmp = DISAS_NORETURN; break; case 1: // htstate @@ -5793,7 +5793,7 @@ static bool sparc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, save_state(dc); } gen_helper_debug(cpu_env); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); dc->base.is_jmp = DISAS_NORETURN; /* update pc_next so that the current instruction is included in tb->size */ dc->base.pc_next += 4; @@ -5832,7 +5832,7 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) tcg_gen_movi_tl(cpu_pc, dc->pc); } save_npc(dc); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } } diff --git a/target/tilegx/translate.c b/target/tilegx/translate.c index 6c53c5e767..f201150fc7 100644 --- a/target/tilegx/translate.c +++ b/target/tilegx/translate.c @@ -2362,7 +2362,7 @@ static void translate_one_bundle(DisasContext *dc, uint64_t bundle) tcg_temp_free_i64(next); } tcg_temp_free_i64(dc->jmp.dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); dc->exit_tb = true; } else if (dc->atomic_excp != TILEGX_EXCP_NONE) { gen_exception(dc, dc->atomic_excp); @@ -2419,7 +2419,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) || tcg_op_buf_full()) { /* Ending the TB due to TB size or page boundary. Set PC. */ tcg_gen_movi_tl(cpu_pc, dc->pc); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; } } diff --git a/target/tricore/translate.c b/target/tricore/translate.c index aef0d9cf06..b5ab40d4a2 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -3253,13 +3253,13 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); gen_save_pc(dest); - tcg_gen_exit_tb((uintptr_t)ctx->tb + n); + tcg_gen_exit_tb(ctx->tb, n); } else { gen_save_pc(dest); if (ctx->singlestep_enabled) { /* raise exception debug */ } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -3327,7 +3327,7 @@ static void gen_fret(DisasContext *ctx) tcg_gen_qemu_ld_tl(cpu_gpr_a[11], cpu_gpr_a[10], ctx->mem_idx, MO_LESL); tcg_gen_addi_tl(cpu_gpr_a[10], cpu_gpr_a[10], 4); tcg_gen_mov_tl(cpu_PC, temp); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; tcg_temp_free(temp); @@ -3431,12 +3431,12 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, /* SR-format jumps */ case OPC1_16_SR_JI: tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], 0xfffffffe); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case OPC2_32_SYS_RET: case OPC2_16_SR_RET: gen_helper_ret(cpu_env); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; /* B-format */ case OPC1_32_B_CALLA: @@ -3939,7 +3939,7 @@ static void decode_sr_system(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_16_SR_RFE: gen_helper_rfe(cpu_env); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; break; case OPC2_16_SR_DEBUG: @@ -6578,7 +6578,7 @@ static void decode_rr_idirect(CPUTriCoreState *env, DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; } @@ -8398,7 +8398,7 @@ static void decode_sys_interrupts(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_SYS_RFE: gen_helper_rfe(cpu_env); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; break; case OPC2_32_SYS_RFM: @@ -8411,7 +8411,7 @@ static void decode_sys_interrupts(CPUTriCoreState *env, DisasContext *ctx) tcg_gen_brcondi_tl(TCG_COND_NE, tmp, 1, l1); gen_helper_rfm(cpu_env); gen_set_label(l1); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; tcg_temp_free(tmp); } else { @@ -8845,7 +8845,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (num_insns >= max_insns || tcg_op_buf_full()) { gen_save_pc(ctx.next_pc); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; } ctx.pc = ctx.next_pc; diff --git a/target/unicore32/translate.c b/target/unicore32/translate.c index 3cae111955..002569ff3b 100644 --- a/target/unicore32/translate.c +++ b/target/unicore32/translate.c @@ -1106,10 +1106,10 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint32_t dest) if (use_goto_tb(s, dest)) { tcg_gen_goto_tb(n); gen_set_pc_im(dest); - tcg_gen_exit_tb((uintptr_t)s->tb + n); + tcg_gen_exit_tb(s->tb, n); } else { gen_set_pc_im(dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -2002,7 +2002,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) case DISAS_JUMP: case DISAS_UPDATE: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: /* nothing more to generate */ diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 720bc592e1..89db23852b 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -377,9 +377,9 @@ static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot) } else { if (slot >= 0) { tcg_gen_goto_tb(slot); - tcg_gen_exit_tb((uintptr_t)dc->tb + slot); + tcg_gen_exit_tb(dc->tb, slot); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } dc->is_jmp = DISAS_UPDATE; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 6a914654f5..daa416a143 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2574,10 +2574,30 @@ void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg) /* QEMU specific operations. */ +void tcg_gen_exit_tb(TranslationBlock *tb, unsigned idx) +{ + uintptr_t val = (uintptr_t)tb + idx; + + if (tb == NULL) { + tcg_debug_assert(idx == 0); + } else if (idx <= TB_EXIT_IDXMAX) { +#ifdef CONFIG_DEBUG_TCG + /* This is an exit following a goto_tb. Verify that we have + seen this numbered exit before, via tcg_gen_goto_tb. */ + tcg_debug_assert(tcg_ctx->goto_tb_issue_mask & (1 << idx)); +#endif + } else { + /* This is an exit via the exitreq label. */ + tcg_debug_assert(idx == TB_EXIT_REQUESTED); + } + + tcg_gen_op1i(INDEX_op_exit_tb, val); +} + void tcg_gen_goto_tb(unsigned idx) { /* We only support two chained exits. */ - tcg_debug_assert(idx <= 1); + tcg_debug_assert(idx <= TB_EXIT_IDXMAX); #ifdef CONFIG_DEBUG_TCG /* Verify that we havn't seen this numbered exit before. */ tcg_debug_assert((tcg_ctx->goto_tb_issue_mask & (1 << idx)) == 0); @@ -2594,7 +2614,7 @@ void tcg_gen_lookup_and_goto_ptr(void) tcg_gen_op1i(INDEX_op_goto_ptr, tcgv_ptr_arg(ptr)); tcg_temp_free_ptr(ptr); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 04eb3e9e17..7513c1eb7c 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -782,10 +782,19 @@ static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, # error "Unhandled number of operands to insn_start" #endif -static inline void tcg_gen_exit_tb(uintptr_t val) -{ - tcg_gen_op1i(INDEX_op_exit_tb, val); -} +/** + * tcg_gen_exit_tb() - output exit_tb TCG operation + * @tb: The TranslationBlock from which we are exiting + * @idx: Direct jump slot index, or exit request + * + * See tcg/README for more info about this TCG operation. + * See also tcg.h and the block comment above TB_EXIT_MASK. + * + * For a normal exit from the TB, back to the main loop, @tb should + * be NULL and @idx should be 0. Otherwise, @tb should be valid and + * @idx should be one of the TB_EXIT_ values. + */ +void tcg_gen_exit_tb(TranslationBlock *tb, unsigned idx); /** * tcg_gen_goto_tb() - output goto_tb TCG operation diff --git a/tcg/tcg.h b/tcg/tcg.h index 08f8bbfe54..509f4d65d2 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -1239,9 +1239,10 @@ static inline unsigned get_mmuidx(TCGMemOpIdx oi) * to this default (which just calls the prologue.code emitted by * tcg_target_qemu_prologue()). */ -#define TB_EXIT_MASK 3 -#define TB_EXIT_IDX0 0 -#define TB_EXIT_IDX1 1 +#define TB_EXIT_MASK 3 +#define TB_EXIT_IDX0 0 +#define TB_EXIT_IDX1 1 +#define TB_EXIT_IDXMAX 1 #define TB_EXIT_REQUESTED 3 #ifdef HAVE_TCG_QEMU_TB_EXEC From 871f95c6171d5301d14dbc73997aa8fbd8e9e7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 24 Jul 2017 15:27:47 -0300 Subject: [PATCH 0981/2380] syscall: replace strcpy() by g_strlcpy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit linux-user/syscall.c:9860:17: warning: Call to function 'strcpy' is insecure as it does not provide bounding of the memory buffer. Replace unbounded copy functions with analogous functions that support length arguments such as 'strlcpy'. CWE-119 strcpy (buf->machine, cpu_to_uname_machine(cpu_env)); ^~~~~~ Reported-by: Clang Static Analyzer Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20170724182751.18261-32-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d02c16bbc6..7b9ac3b408 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -10156,7 +10156,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret)) { /* Overwrite the native machine name with whatever is being emulated. */ - strcpy (buf->machine, cpu_to_uname_machine(cpu_env)); + g_strlcpy(buf->machine, cpu_to_uname_machine(cpu_env), + sizeof(buf->machine)); /* Allow the user to override the reported release. */ if (qemu_uname_release && *qemu_uname_release) { g_strlcpy(buf->release, qemu_uname_release, From b8e13ba94e39aae79be5724b2a382091e4c91c83 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 28 May 2018 21:48:12 +0200 Subject: [PATCH 0982/2380] linux-user: SPARC "rd %tick" can be used by user application MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit we have the same problem decribed in 7d6b1daedd ("linux-user, ppc: mftbl can be used by user application") for ppc in the case of sparc. When we use an application trying to resolve a name, it hangs in 0x00000000ff5dd40c: rd %tick, %o5 0x00000000ff5dd410: srlx %o5, 0x20, %o4 0x00000000ff5dd414: btst %o5, %g4 0x00000000ff5dd418: be %icc, 0xff5dd40c because %tick is staying at 0. As QEMU_CLOCK_VIRTUAL is not available in linux-user mode, simply use cpu_get_host_ticks() instead. Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180528194812.31216-1-laurent@vivier.eu> --- target/sparc/helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/sparc/helper.c b/target/sparc/helper.c index 1d854890b4..46232788c8 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -67,7 +67,9 @@ uint64_t helper_tick_get_count(CPUSPARCState *env, void *opaque, int mem_idx) return cpu_tick_get_count(timer); #else - return 0; + /* In user-mode, QEMU_CLOCK_VIRTUAL doesn't exist. + Just pass through the host cpu clock ticks. */ + return cpu_get_host_ticks(); #endif } From 5de7706e2c713e7fe2cf6ea039b042f1e7c53fa0 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:41:53 +0200 Subject: [PATCH 0983/2380] linux-user: move generic fcntl definitions to generic/fcntl.h add a per target target_fcntl.h and include the generic one from them No code change. Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-2-laurent@vivier.eu> --- linux-user/aarch64/target_fcntl.h | 11 ++ linux-user/alpha/target_fcntl.h | 11 ++ linux-user/arm/target_fcntl.h | 11 ++ linux-user/cris/target_fcntl.h | 11 ++ linux-user/generic/fcntl.h | 151 +++++++++++++++++++++++++++ linux-user/hppa/target_fcntl.h | 11 ++ linux-user/i386/target_fcntl.h | 11 ++ linux-user/m68k/target_fcntl.h | 11 ++ linux-user/microblaze/target_fcntl.h | 11 ++ linux-user/mips/target_fcntl.h | 11 ++ linux-user/mips64/target_fcntl.h | 1 + linux-user/nios2/target_fcntl.h | 11 ++ linux-user/openrisc/target_fcntl.h | 11 ++ linux-user/ppc/target_fcntl.h | 11 ++ linux-user/riscv/target_fcntl.h | 11 ++ linux-user/s390x/target_fcntl.h | 11 ++ linux-user/sh4/target_fcntl.h | 11 ++ linux-user/sparc/target_fcntl.h | 11 ++ linux-user/sparc64/target_fcntl.h | 1 + linux-user/syscall_defs.h | 129 +---------------------- linux-user/tilegx/target_fcntl.h | 11 ++ linux-user/x86_64/target_fcntl.h | 11 ++ linux-user/xtensa/target_fcntl.h | 11 ++ 23 files changed, 363 insertions(+), 128 deletions(-) create mode 100644 linux-user/aarch64/target_fcntl.h create mode 100644 linux-user/alpha/target_fcntl.h create mode 100644 linux-user/arm/target_fcntl.h create mode 100644 linux-user/cris/target_fcntl.h create mode 100644 linux-user/generic/fcntl.h create mode 100644 linux-user/hppa/target_fcntl.h create mode 100644 linux-user/i386/target_fcntl.h create mode 100644 linux-user/m68k/target_fcntl.h create mode 100644 linux-user/microblaze/target_fcntl.h create mode 100644 linux-user/mips/target_fcntl.h create mode 100644 linux-user/mips64/target_fcntl.h create mode 100644 linux-user/nios2/target_fcntl.h create mode 100644 linux-user/openrisc/target_fcntl.h create mode 100644 linux-user/ppc/target_fcntl.h create mode 100644 linux-user/riscv/target_fcntl.h create mode 100644 linux-user/s390x/target_fcntl.h create mode 100644 linux-user/sh4/target_fcntl.h create mode 100644 linux-user/sparc/target_fcntl.h create mode 100644 linux-user/sparc64/target_fcntl.h create mode 100644 linux-user/tilegx/target_fcntl.h create mode 100644 linux-user/x86_64/target_fcntl.h create mode 100644 linux-user/xtensa/target_fcntl.h diff --git a/linux-user/aarch64/target_fcntl.h b/linux-user/aarch64/target_fcntl.h new file mode 100644 index 0000000000..59be406280 --- /dev/null +++ b/linux-user/aarch64/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef AARCH64_TARGET_FCNTL_H +#define AARCH64_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/alpha/target_fcntl.h b/linux-user/alpha/target_fcntl.h new file mode 100644 index 0000000000..bb603ff28c --- /dev/null +++ b/linux-user/alpha/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef ALPHA_TARGET_FCNTL_H +#define ALPHA_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/arm/target_fcntl.h b/linux-user/arm/target_fcntl.h new file mode 100644 index 0000000000..ca819df519 --- /dev/null +++ b/linux-user/arm/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef ARM_TARGET_FCNTL_H +#define ARM_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/cris/target_fcntl.h b/linux-user/cris/target_fcntl.h new file mode 100644 index 0000000000..df0aceea34 --- /dev/null +++ b/linux-user/cris/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef CRIS_TARGET_FCNTL_H +#define CRIS_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/generic/fcntl.h b/linux-user/generic/fcntl.h new file mode 100644 index 0000000000..a775a491e9 --- /dev/null +++ b/linux-user/generic/fcntl.h @@ -0,0 +1,151 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef GENERIC_FCNTL_H +#define GENERIC_FCNTL_H + +/* values follow. */ +#define TARGET_O_ACCMODE 0003 +#define TARGET_O_RDONLY 00 +#define TARGET_O_WRONLY 01 +#define TARGET_O_RDWR 02 +#ifndef TARGET_O_CREAT +#define TARGET_O_CREAT 0100 /* not fcntl */ +#endif +#ifndef TARGET_O_EXCL +#define TARGET_O_EXCL 0200 /* not fcntl */ +#endif +#ifndef TARGET_O_NOCTTY +#define TARGET_O_NOCTTY 0400 /* not fcntl */ +#endif +#ifndef TARGET_O_TRUNC +#define TARGET_O_TRUNC 01000 /* not fcntl */ +#endif +#ifndef TARGET_O_APPEND +#define TARGET_O_APPEND 02000 +#endif +#ifndef TARGET_O_NONBLOCK +#define TARGET_O_NONBLOCK 04000 +#endif +#ifndef TARGET_O_DSYNC +#define TARGET_O_DSYNC 010000 +#endif +#ifndef TARGET_FASYNC +#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */ +#endif +#ifndef TARGET_O_DIRECT +#define TARGET_O_DIRECT 040000 /* direct disk access hint */ +#endif +#ifndef TARGET_O_LARGEFILE +#define TARGET_O_LARGEFILE 0100000 +#endif +#ifndef TARGET_O_DIRECTORY +#define TARGET_O_DIRECTORY 0200000 /* must be a directory */ +#endif +#ifndef TARGET_O_NOFOLLOW +#define TARGET_O_NOFOLLOW 0400000 /* don't follow links */ +#endif +#ifndef TARGET_O_NOATIME +#define TARGET_O_NOATIME 01000000 +#endif +#ifndef TARGET_O_CLOEXEC +#define TARGET_O_CLOEXEC 02000000 +#endif +#ifndef TARGET___O_SYNC +#define TARGET___O_SYNC 04000000 +#endif +#ifndef TARGET_O_PATH +#define TARGET_O_PATH 010000000 +#endif +#ifndef TARGET___O_TMPFILE +#define TARGET___O_TMPFILE 020000000 +#endif +#ifndef TARGET_O_TMPFILE +#define TARGET_O_TMPFILE (TARGET___O_TMPFILE | TARGET_O_DIRECTORY) +#endif +#ifndef TARGET_O_NDELAY +#define TARGET_O_NDELAY TARGET_O_NONBLOCK +#endif +#ifndef TARGET_O_SYNC +#define TARGET_O_SYNC (TARGET___O_SYNC | TARGET_O_DSYNC) +#endif + +#define TARGET_F_DUPFD 0 /* dup */ +#define TARGET_F_GETFD 1 /* get close_on_exec */ +#define TARGET_F_SETFD 2 /* set/clear close_on_exec */ +#define TARGET_F_GETFL 3 /* get file->f_flags */ +#define TARGET_F_SETFL 4 /* set file->f_flags */ +#ifndef TARGET_F_GETLK +#define TARGET_F_GETLK 5 +#define TARGET_F_SETLK 6 +#define TARGET_F_SETLKW 7 +#endif +#ifndef TARGET_F_SETOWN +#define TARGET_F_SETOWN 8 /* for sockets. */ +#define TARGET_F_GETOWN 9 /* for sockets. */ +#endif +#ifndef TARGET_F_SETSIG +#define TARGET_F_SETSIG 10 /* for sockets. */ +#define TARGET_F_GETSIG 11 /* for sockets. */ +#endif + +#ifndef TARGET_F_GETLK64 +#define TARGET_F_GETLK64 12 /* using 'struct flock64' */ +#define TARGET_F_SETLK64 13 +#define TARGET_F_SETLKW64 14 +#endif + +#ifndef TARGET_F_SETOWN_EX +#define TARGET_F_SETOWN_EX 15 +#define TARGET_F_GETOWN_EX 16 +#endif + +struct target_f_owner_ex { + int type; /* Owner type of ID. */ + int pid; /* ID of owner. */ +}; + +#ifndef TARGET_F_RDLCK +#define TARGET_F_RDLCK 0 +#define TARGET_F_WRLCK 1 +#define TARGET_F_UNLCK 2 +#endif + +#ifndef TARGET_F_EXLCK +#define TARGET_F_EXLCK 4 +#define TARGET_F_SHLCK 8 +#endif + +#ifndef TARGET_ARCH_FLOCK_PAD +#define TARGET_ARCH_FLOCK_PAD +#endif + +struct target_flock { + short l_type; + short l_whence; + abi_long l_start; + abi_long l_len; +#if defined(TARGET_MIPS) + abi_long l_sysid; +#endif + int l_pid; + TARGET_ARCH_FLOCK_PAD +}; + +#ifndef TARGET_ARCH_FLOCK64_PAD +#define TARGET_ARCH_FLOCK64_PAD +#endif + +struct target_flock64 { + abi_short l_type; + abi_short l_whence; + abi_llong l_start; + abi_llong l_len; + abi_int l_pid; + TARGET_ARCH_FLOCK64_PAD +}; +#endif diff --git a/linux-user/hppa/target_fcntl.h b/linux-user/hppa/target_fcntl.h new file mode 100644 index 0000000000..aa282a5ce8 --- /dev/null +++ b/linux-user/hppa/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef HPPA_TARGET_FCNTL_H +#define HPPA_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/i386/target_fcntl.h b/linux-user/i386/target_fcntl.h new file mode 100644 index 0000000000..4819743dae --- /dev/null +++ b/linux-user/i386/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef I386_TARGET_FCNTL_H +#define I386_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/m68k/target_fcntl.h b/linux-user/m68k/target_fcntl.h new file mode 100644 index 0000000000..4328c60d22 --- /dev/null +++ b/linux-user/m68k/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef M68K_TARGET_FCNTL_H +#define M68K_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/microblaze/target_fcntl.h b/linux-user/microblaze/target_fcntl.h new file mode 100644 index 0000000000..45402275ff --- /dev/null +++ b/linux-user/microblaze/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef MICROBLAZE_TARGET_FCNTL_H +#define MICROBLAZE_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/mips/target_fcntl.h b/linux-user/mips/target_fcntl.h new file mode 100644 index 0000000000..5404245068 --- /dev/null +++ b/linux-user/mips/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef MIPS_TARGET_FCNTL_H +#define MIPS_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/mips64/target_fcntl.h b/linux-user/mips64/target_fcntl.h new file mode 100644 index 0000000000..a511bc0e6c --- /dev/null +++ b/linux-user/mips64/target_fcntl.h @@ -0,0 +1 @@ +#include "../mips/target_fcntl.h" diff --git a/linux-user/nios2/target_fcntl.h b/linux-user/nios2/target_fcntl.h new file mode 100644 index 0000000000..714583215d --- /dev/null +++ b/linux-user/nios2/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef NIOS2_TARGET_FCNTL_H +#define NIOS2_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/openrisc/target_fcntl.h b/linux-user/openrisc/target_fcntl.h new file mode 100644 index 0000000000..ea31bf8b70 --- /dev/null +++ b/linux-user/openrisc/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef OPENRISC_TARGET_FCNTL_H +#define OPENRISC_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/ppc/target_fcntl.h b/linux-user/ppc/target_fcntl.h new file mode 100644 index 0000000000..627d547289 --- /dev/null +++ b/linux-user/ppc/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef PPC_TARGET_FCNTL_H +#define PPC_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/riscv/target_fcntl.h b/linux-user/riscv/target_fcntl.h new file mode 100644 index 0000000000..9c3d0fbe2b --- /dev/null +++ b/linux-user/riscv/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef RISCV_TARGET_FCNTL_H +#define RISCV_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/s390x/target_fcntl.h b/linux-user/s390x/target_fcntl.h new file mode 100644 index 0000000000..36dc50fba0 --- /dev/null +++ b/linux-user/s390x/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef S390X_TARGET_FCNTL_H +#define S390X_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/sh4/target_fcntl.h b/linux-user/sh4/target_fcntl.h new file mode 100644 index 0000000000..2622d95539 --- /dev/null +++ b/linux-user/sh4/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef SH4_TARGET_FCNTL_H +#define SH4_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/sparc/target_fcntl.h b/linux-user/sparc/target_fcntl.h new file mode 100644 index 0000000000..35a753153b --- /dev/null +++ b/linux-user/sparc/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef SPARC_TARGET_FCNTL_H +#define SPARC_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/sparc64/target_fcntl.h b/linux-user/sparc64/target_fcntl.h new file mode 100644 index 0000000000..053c774257 --- /dev/null +++ b/linux-user/sparc64/target_fcntl.h @@ -0,0 +1 @@ +#include "../sparc/target_fcntl.h" diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index e4cd87cc00..9969f21ba1 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2407,13 +2407,6 @@ struct target_statfs64 { }; #endif - -#define TARGET_F_DUPFD 0 /* dup */ -#define TARGET_F_GETFD 1 /* get close_on_exec */ -#define TARGET_F_SETFD 2 /* set/clear close_on_exec */ -#define TARGET_F_GETFL 3 /* get file->f_flags */ -#define TARGET_F_SETFL 4 /* set file->f_flags */ - #if defined(TARGET_ALPHA) #define TARGET_F_GETLK 7 #define TARGET_F_SETLK 8 @@ -2450,34 +2443,11 @@ struct target_statfs64 { #define TARGET_F_GETLK 7 #define TARGET_F_SETLK 8 #define TARGET_F_SETLKW 9 -#else -#define TARGET_F_GETLK 5 -#define TARGET_F_SETLK 6 -#define TARGET_F_SETLKW 7 -#define TARGET_F_SETOWN 8 /* for sockets. */ -#define TARGET_F_GETOWN 9 /* for sockets. */ #endif -#define TARGET_F_SETOWN_EX 15 -#define TARGET_F_GETOWN_EX 16 - -#ifndef TARGET_F_RDLCK -#define TARGET_F_RDLCK 0 -#define TARGET_F_WRLCK 1 -#define TARGET_F_UNLCK 2 -#endif - -#ifndef TARGET_F_EXLCK -#define TARGET_F_EXLCK 4 -#define TARGET_F_SHLCK 8 -#endif - #if defined(TARGET_HPPA) #define TARGET_F_SETSIG 13 /* for sockets. */ #define TARGET_F_GETSIG 14 /* for sockets. */ -#else -#define TARGET_F_SETSIG 10 /* for sockets. */ -#define TARGET_F_GETSIG 11 /* for sockets. */ #endif #if defined(TARGET_MIPS) @@ -2488,10 +2458,6 @@ struct target_statfs64 { #define TARGET_F_GETLK64 8 /* using 'struct flock64' */ #define TARGET_F_SETLK64 9 #define TARGET_F_SETLKW64 10 -#else -#define TARGET_F_GETLK64 12 /* using 'struct flock64' */ -#define TARGET_F_SETLK64 13 -#define TARGET_F_SETLKW64 14 #endif #define TARGET_F_LINUX_SPECIFIC_BASE 1024 @@ -2577,108 +2543,15 @@ struct target_statfs64 { #define TARGET___O_TMPFILE 0x2000000 #endif -/* values follow. */ -#define TARGET_O_ACCMODE 0003 -#define TARGET_O_RDONLY 00 -#define TARGET_O_WRONLY 01 -#define TARGET_O_RDWR 02 -#ifndef TARGET_O_CREAT -#define TARGET_O_CREAT 0100 /* not fcntl */ -#endif -#ifndef TARGET_O_EXCL -#define TARGET_O_EXCL 0200 /* not fcntl */ -#endif -#ifndef TARGET_O_NOCTTY -#define TARGET_O_NOCTTY 0400 /* not fcntl */ -#endif -#ifndef TARGET_O_TRUNC -#define TARGET_O_TRUNC 01000 /* not fcntl */ -#endif -#ifndef TARGET_O_APPEND -#define TARGET_O_APPEND 02000 -#endif -#ifndef TARGET_O_NONBLOCK -#define TARGET_O_NONBLOCK 04000 -#endif -#ifndef TARGET_O_DSYNC -#define TARGET_O_DSYNC 010000 -#endif -#ifndef TARGET_FASYNC -#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */ -#endif -#ifndef TARGET_O_DIRECT -#define TARGET_O_DIRECT 040000 /* direct disk access hint */ -#endif -#ifndef TARGET_O_LARGEFILE -#define TARGET_O_LARGEFILE 0100000 -#endif -#ifndef TARGET_O_DIRECTORY -#define TARGET_O_DIRECTORY 0200000 /* must be a directory */ -#endif -#ifndef TARGET_O_NOFOLLOW -#define TARGET_O_NOFOLLOW 0400000 /* don't follow links */ -#endif -#ifndef TARGET_O_NOATIME -#define TARGET_O_NOATIME 01000000 -#endif -#ifndef TARGET_O_CLOEXEC -#define TARGET_O_CLOEXEC 02000000 -#endif -#ifndef TARGET___O_SYNC -#define TARGET___O_SYNC 04000000 -#endif -#ifndef TARGET_O_PATH -#define TARGET_O_PATH 010000000 -#endif -#ifndef TARGET___O_TMPFILE -#define TARGET___O_TMPFILE 020000000 -#endif -#ifndef TARGET_O_TMPFILE -#define TARGET_O_TMPFILE (TARGET___O_TMPFILE | TARGET_O_DIRECTORY) -#endif -#ifndef TARGET_O_NDELAY -#define TARGET_O_NDELAY TARGET_O_NONBLOCK -#endif -#ifndef TARGET_O_SYNC -#define TARGET_O_SYNC (TARGET___O_SYNC | TARGET_O_DSYNC) -#endif - #if defined(TARGET_SPARC) #define TARGET_ARCH_FLOCK_PAD abi_short __unused; #define TARGET_ARCH_FLOCK64_PAD abi_short __unused; #elif defined(TARGET_MIPS) #define TARGET_ARCH_FLOCK_PAD abi_long pad[4]; #define TARGET_ARCH_FLOCK64_PAD -#else -#define TARGET_ARCH_FLOCK_PAD -#define TARGET_ARCH_FLOCK64_PAD #endif -struct target_flock { - short l_type; - short l_whence; - abi_long l_start; - abi_long l_len; -#if defined(TARGET_MIPS) - abi_long l_sysid; -#endif - int l_pid; - TARGET_ARCH_FLOCK_PAD -}; - -struct target_flock64 { - abi_short l_type; - abi_short l_whence; - abi_llong l_start; - abi_llong l_len; - abi_int l_pid; - TARGET_ARCH_FLOCK64_PAD -}; - -struct target_f_owner_ex { - int type; /* Owner type of ID. */ - int pid; /* ID of owner. */ -}; +#include "target_fcntl.h" /* soundcard defines */ /* XXX: convert them all to arch independent entries */ diff --git a/linux-user/tilegx/target_fcntl.h b/linux-user/tilegx/target_fcntl.h new file mode 100644 index 0000000000..5ed7438459 --- /dev/null +++ b/linux-user/tilegx/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef TILEGX_TARGET_FCNTL_H +#define TILEGX_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/x86_64/target_fcntl.h b/linux-user/x86_64/target_fcntl.h new file mode 100644 index 0000000000..3c7238e56b --- /dev/null +++ b/linux-user/x86_64/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef X86_64_TARGET_FCNTL_H +#define X86_64_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/xtensa/target_fcntl.h b/linux-user/xtensa/target_fcntl.h new file mode 100644 index 0000000000..dc1ca7eaa5 --- /dev/null +++ b/linux-user/xtensa/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef XTENSA_TARGET_FCNTL_H +#define XTENSA_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif From 40460c16eed61a4206e7033d8a98506c4cf3df5d Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:41:54 +0200 Subject: [PATCH 0984/2380] linux-user: move alpha fcntl definitions to alpha/target_fcntl.h No code change. Signed-off-by: Laurent Vivier Acked-by: Richard Henderson Message-Id: <20180529194207.31503-3-laurent@vivier.eu> --- linux-user/alpha/target_fcntl.h | 29 +++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 32 ++------------------------------ 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/linux-user/alpha/target_fcntl.h b/linux-user/alpha/target_fcntl.h index bb603ff28c..2617e73472 100644 --- a/linux-user/alpha/target_fcntl.h +++ b/linux-user/alpha/target_fcntl.h @@ -7,5 +7,34 @@ #ifndef ALPHA_TARGET_FCNTL_H #define ALPHA_TARGET_FCNTL_H + +#define TARGET_O_NONBLOCK 04 +#define TARGET_O_APPEND 010 +#define TARGET_O_CREAT 01000 /* not fcntl */ +#define TARGET_O_TRUNC 02000 /* not fcntl */ +#define TARGET_O_EXCL 04000 /* not fcntl */ +#define TARGET_O_NOCTTY 010000 /* not fcntl */ +#define TARGET_O_DSYNC 040000 +#define TARGET_O_LARGEFILE 0 /* not necessary, always 64-bit */ +#define TARGET_O_DIRECTORY 0100000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0200000 /* don't follow links */ +#define TARGET_O_DIRECT 02000000 /* direct disk access hint */ +#define TARGET_O_NOATIME 04000000 +#define TARGET_O_CLOEXEC 010000000 +#define TARGET___O_SYNC 020000000 +#define TARGET_O_PATH 040000000 + +#define TARGET_F_GETLK 7 +#define TARGET_F_SETLK 8 +#define TARGET_F_SETLKW 9 +#define TARGET_F_SETOWN 5 /* for sockets. */ +#define TARGET_F_GETOWN 6 /* for sockets. */ + +#define TARGET_F_RDLCK 1 +#define TARGET_F_WRLCK 2 +#define TARGET_F_UNLCK 8 +#define TARGET_F_EXLCK 16 +#define TARGET_F_SHLCK 32 + #include "../generic/fcntl.h" #endif diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 9969f21ba1..d8318ebd27 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2407,19 +2407,7 @@ struct target_statfs64 { }; #endif -#if defined(TARGET_ALPHA) -#define TARGET_F_GETLK 7 -#define TARGET_F_SETLK 8 -#define TARGET_F_SETLKW 9 -#define TARGET_F_SETOWN 5 /* for sockets. */ -#define TARGET_F_GETOWN 6 /* for sockets. */ - -#define TARGET_F_RDLCK 1 -#define TARGET_F_WRLCK 2 -#define TARGET_F_UNLCK 8 -#define TARGET_F_EXLCK 16 -#define TARGET_F_SHLCK 32 -#elif defined(TARGET_MIPS) +#if defined(TARGET_MIPS) #define TARGET_F_GETLK 14 #define TARGET_F_SETLK 6 #define TARGET_F_SETLKW 7 @@ -2468,23 +2456,7 @@ struct target_statfs64 { #define TARGET_F_GETPIPE_SZ (TARGET_F_LINUX_SPECIFIC_BASE + 8) #define TARGET_F_NOTIFY (TARGET_F_LINUX_SPECIFIC_BASE+2) -#if defined(TARGET_ALPHA) -#define TARGET_O_NONBLOCK 04 -#define TARGET_O_APPEND 010 -#define TARGET_O_CREAT 01000 /* not fcntl */ -#define TARGET_O_TRUNC 02000 /* not fcntl */ -#define TARGET_O_EXCL 04000 /* not fcntl */ -#define TARGET_O_NOCTTY 010000 /* not fcntl */ -#define TARGET_O_DSYNC 040000 -#define TARGET_O_LARGEFILE 0 /* not necessary, always 64-bit */ -#define TARGET_O_DIRECTORY 0100000 /* must be a directory */ -#define TARGET_O_NOFOLLOW 0200000 /* don't follow links */ -#define TARGET_O_DIRECT 02000000 /* direct disk access hint */ -#define TARGET_O_NOATIME 04000000 -#define TARGET_O_CLOEXEC 010000000 -#define TARGET___O_SYNC 020000000 -#define TARGET_O_PATH 040000000 -#elif defined(TARGET_HPPA) +#if defined(TARGET_HPPA) #define TARGET_O_NONBLOCK 000200004 /* HPUX has separate NDELAY & NONBLOCK */ #define TARGET_O_APPEND 000000010 #define TARGET_O_CREAT 000000400 /* not fcntl */ From 8b08c98e4704df3bee99da3989dce4be2c36f873 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:41:55 +0200 Subject: [PATCH 0985/2380] linux-user: move hppa fcntl definitions to hppa/target_fcntl.h No code change. Signed-off-by: Laurent Vivier Acked-by: Richard Henderson Message-Id: <20180529194207.31503-4-laurent@vivier.eu> --- linux-user/hppa/target_fcntl.h | 31 +++++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 34 +--------------------------------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/linux-user/hppa/target_fcntl.h b/linux-user/hppa/target_fcntl.h index aa282a5ce8..bd966a59b8 100644 --- a/linux-user/hppa/target_fcntl.h +++ b/linux-user/hppa/target_fcntl.h @@ -7,5 +7,36 @@ #ifndef HPPA_TARGET_FCNTL_H #define HPPA_TARGET_FCNTL_H + +#define TARGET_O_NONBLOCK 000200004 /* HPUX has separate NDELAY & NONBLOCK */ +#define TARGET_O_APPEND 000000010 +#define TARGET_O_CREAT 000000400 /* not fcntl */ +#define TARGET_O_EXCL 000002000 /* not fcntl */ +#define TARGET_O_NOCTTY 000400000 /* not fcntl */ +#define TARGET_O_DSYNC 001000000 +#define TARGET_O_LARGEFILE 000004000 +#define TARGET_O_DIRECTORY 000010000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 000000200 /* don't follow links */ +#define TARGET_O_NOATIME 004000000 +#define TARGET_O_CLOEXEC 010000000 +#define TARGET___O_SYNC 000100000 +#define TARGET_O_PATH 020000000 + +#define TARGET_F_RDLCK 1 +#define TARGET_F_WRLCK 2 +#define TARGET_F_UNLCK 3 + +#define TARGET_F_GETLK64 8 /* using 'struct flock64' */ +#define TARGET_F_SETLK64 9 +#define TARGET_F_SETLKW64 10 + +#define TARGET_F_GETLK 5 +#define TARGET_F_SETLK 6 +#define TARGET_F_SETLKW 7 +#define TARGET_F_GETOWN 11 /* for sockets. */ +#define TARGET_F_SETOWN 12 /* for sockets. */ +#define TARGET_F_SETSIG 13 /* for sockets. */ +#define TARGET_F_GETSIG 14 /* for sockets. */ + #include "../generic/fcntl.h" #endif diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index d8318ebd27..46d2353254 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2413,15 +2413,6 @@ struct target_statfs64 { #define TARGET_F_SETLKW 7 #define TARGET_F_SETOWN 24 /* for sockets. */ #define TARGET_F_GETOWN 23 /* for sockets. */ -#elif defined(TARGET_HPPA) -#define TARGET_F_RDLCK 1 -#define TARGET_F_WRLCK 2 -#define TARGET_F_UNLCK 3 -#define TARGET_F_GETLK 5 -#define TARGET_F_SETLK 6 -#define TARGET_F_SETLKW 7 -#define TARGET_F_GETOWN 11 /* for sockets. */ -#define TARGET_F_SETOWN 12 /* for sockets. */ #elif defined(TARGET_SPARC) #define TARGET_F_RDLCK 1 #define TARGET_F_WRLCK 2 @@ -2433,19 +2424,10 @@ struct target_statfs64 { #define TARGET_F_SETLKW 9 #endif -#if defined(TARGET_HPPA) -#define TARGET_F_SETSIG 13 /* for sockets. */ -#define TARGET_F_GETSIG 14 /* for sockets. */ -#endif - #if defined(TARGET_MIPS) #define TARGET_F_GETLK64 33 /* using 'struct flock64' */ #define TARGET_F_SETLK64 34 #define TARGET_F_SETLKW64 35 -#elif defined(TARGET_HPPA) -#define TARGET_F_GETLK64 8 /* using 'struct flock64' */ -#define TARGET_F_SETLK64 9 -#define TARGET_F_SETLKW64 10 #endif #define TARGET_F_LINUX_SPECIFIC_BASE 1024 @@ -2456,21 +2438,7 @@ struct target_statfs64 { #define TARGET_F_GETPIPE_SZ (TARGET_F_LINUX_SPECIFIC_BASE + 8) #define TARGET_F_NOTIFY (TARGET_F_LINUX_SPECIFIC_BASE+2) -#if defined(TARGET_HPPA) -#define TARGET_O_NONBLOCK 000200004 /* HPUX has separate NDELAY & NONBLOCK */ -#define TARGET_O_APPEND 000000010 -#define TARGET_O_CREAT 000000400 /* not fcntl */ -#define TARGET_O_EXCL 000002000 /* not fcntl */ -#define TARGET_O_NOCTTY 000400000 /* not fcntl */ -#define TARGET_O_DSYNC 001000000 -#define TARGET_O_LARGEFILE 000004000 -#define TARGET_O_DIRECTORY 000010000 /* must be a directory */ -#define TARGET_O_NOFOLLOW 000000200 /* don't follow links */ -#define TARGET_O_NOATIME 004000000 -#define TARGET_O_CLOEXEC 010000000 -#define TARGET___O_SYNC 000100000 -#define TARGET_O_PATH 020000000 -#elif defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_AARCH64) +#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_AARCH64) #define TARGET_O_DIRECTORY 040000 /* must be a directory */ #define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ #define TARGET_O_DIRECT 0200000 /* direct disk access hint */ From 050a1ba69a61f0d4ef79a6b252ffe449b7ca37e7 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:41:56 +0200 Subject: [PATCH 0986/2380] linux-user: move arm/aarch64/m68k fcntl definitions to [arm|aarch64|m68k]/target_fcntl.h No code change. Signed-off-by: Laurent Vivier Acked-by: Richard Henderson Message-Id: <20180529194207.31503-5-laurent@vivier.eu> --- linux-user/aarch64/target_fcntl.h | 5 +++++ linux-user/arm/target_fcntl.h | 6 ++++++ linux-user/m68k/target_fcntl.h | 6 ++++++ linux-user/syscall_defs.h | 7 +------ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/linux-user/aarch64/target_fcntl.h b/linux-user/aarch64/target_fcntl.h index 59be406280..efdf6e5f05 100644 --- a/linux-user/aarch64/target_fcntl.h +++ b/linux-user/aarch64/target_fcntl.h @@ -7,5 +7,10 @@ #ifndef AARCH64_TARGET_FCNTL_H #define AARCH64_TARGET_FCNTL_H + +#define TARGET_O_DIRECTORY 040000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ +#define TARGET_O_DIRECT 0200000 /* direct disk access hint */ + #include "../generic/fcntl.h" #endif diff --git a/linux-user/arm/target_fcntl.h b/linux-user/arm/target_fcntl.h index ca819df519..c8ff6b2505 100644 --- a/linux-user/arm/target_fcntl.h +++ b/linux-user/arm/target_fcntl.h @@ -7,5 +7,11 @@ #ifndef ARM_TARGET_FCNTL_H #define ARM_TARGET_FCNTL_H + +#define TARGET_O_DIRECTORY 040000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ +#define TARGET_O_DIRECT 0200000 /* direct disk access hint */ +#define TARGET_O_LARGEFILE 0400000 + #include "../generic/fcntl.h" #endif diff --git a/linux-user/m68k/target_fcntl.h b/linux-user/m68k/target_fcntl.h index 4328c60d22..068bc3243e 100644 --- a/linux-user/m68k/target_fcntl.h +++ b/linux-user/m68k/target_fcntl.h @@ -7,5 +7,11 @@ #ifndef M68K_TARGET_FCNTL_H #define M68K_TARGET_FCNTL_H + +#define TARGET_O_DIRECTORY 040000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ +#define TARGET_O_DIRECT 0200000 /* direct disk access hint */ +#define TARGET_O_LARGEFILE 0400000 + #include "../generic/fcntl.h" #endif diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 46d2353254..3d13cdd654 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2438,12 +2438,7 @@ struct target_statfs64 { #define TARGET_F_GETPIPE_SZ (TARGET_F_LINUX_SPECIFIC_BASE + 8) #define TARGET_F_NOTIFY (TARGET_F_LINUX_SPECIFIC_BASE+2) -#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_AARCH64) -#define TARGET_O_DIRECTORY 040000 /* must be a directory */ -#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ -#define TARGET_O_DIRECT 0200000 /* direct disk access hint */ -#define TARGET_O_LARGEFILE 0400000 -#elif defined(TARGET_MIPS) +#if defined(TARGET_MIPS) #define TARGET_O_APPEND 0x0008 #define TARGET_O_DSYNC 0x0010 #define TARGET_O_NONBLOCK 0x0080 From b9acdef7861b63b17335112133995db3667aa9de Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:41:57 +0200 Subject: [PATCH 0987/2380] linux-user: move mips/mips64 fcntl definitions to mips/target_fcntl.h No code change. Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-6-laurent@vivier.eu> --- linux-user/mips/target_fcntl.h | 27 +++++++++++++++++++++++++++ linux-user/syscall_defs.h | 31 ++----------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/linux-user/mips/target_fcntl.h b/linux-user/mips/target_fcntl.h index 5404245068..000527cc95 100644 --- a/linux-user/mips/target_fcntl.h +++ b/linux-user/mips/target_fcntl.h @@ -7,5 +7,32 @@ #ifndef MIPS_TARGET_FCNTL_H #define MIPS_TARGET_FCNTL_H + +#define TARGET_O_APPEND 0x0008 +#define TARGET_O_DSYNC 0x0010 +#define TARGET_O_NONBLOCK 0x0080 +#define TARGET_O_CREAT 0x0100 /* not fcntl */ +#define TARGET_O_TRUNC 0x0200 /* not fcntl */ +#define TARGET_O_EXCL 0x0400 /* not fcntl */ +#define TARGET_O_NOCTTY 0x0800 /* not fcntl */ +#define TARGET_FASYNC 0x1000 /* fcntl, for BSD compatibility */ +#define TARGET_O_LARGEFILE 0x2000 /* allow large file opens */ +#define TARGET___O_SYNC 0x4000 +#define TARGET_O_DIRECT 0x8000 /* direct disk access hint */ + +#define TARGET_F_GETLK 14 +#define TARGET_F_SETLK 6 +#define TARGET_F_SETLKW 7 + +#define TARGET_F_SETOWN 24 /* for sockets. */ +#define TARGET_F_GETOWN 23 /* for sockets. */ + +#define TARGET_ARCH_FLOCK_PAD abi_long pad[4]; +#define TARGET_ARCH_FLOCK64_PAD + +#define TARGET_F_GETLK64 33 /* using 'struct flock64' */ +#define TARGET_F_SETLK64 34 +#define TARGET_F_SETLKW64 35 + #include "../generic/fcntl.h" #endif diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 3d13cdd654..44a590d2c9 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2407,13 +2407,7 @@ struct target_statfs64 { }; #endif -#if defined(TARGET_MIPS) -#define TARGET_F_GETLK 14 -#define TARGET_F_SETLK 6 -#define TARGET_F_SETLKW 7 -#define TARGET_F_SETOWN 24 /* for sockets. */ -#define TARGET_F_GETOWN 23 /* for sockets. */ -#elif defined(TARGET_SPARC) +#if defined(TARGET_SPARC) #define TARGET_F_RDLCK 1 #define TARGET_F_WRLCK 2 #define TARGET_F_UNLCK 3 @@ -2424,12 +2418,6 @@ struct target_statfs64 { #define TARGET_F_SETLKW 9 #endif -#if defined(TARGET_MIPS) -#define TARGET_F_GETLK64 33 /* using 'struct flock64' */ -#define TARGET_F_SETLK64 34 -#define TARGET_F_SETLKW64 35 -#endif - #define TARGET_F_LINUX_SPECIFIC_BASE 1024 #define TARGET_F_SETLEASE (TARGET_F_LINUX_SPECIFIC_BASE + 0) #define TARGET_F_GETLEASE (TARGET_F_LINUX_SPECIFIC_BASE + 1) @@ -2438,19 +2426,7 @@ struct target_statfs64 { #define TARGET_F_GETPIPE_SZ (TARGET_F_LINUX_SPECIFIC_BASE + 8) #define TARGET_F_NOTIFY (TARGET_F_LINUX_SPECIFIC_BASE+2) -#if defined(TARGET_MIPS) -#define TARGET_O_APPEND 0x0008 -#define TARGET_O_DSYNC 0x0010 -#define TARGET_O_NONBLOCK 0x0080 -#define TARGET_O_CREAT 0x0100 /* not fcntl */ -#define TARGET_O_TRUNC 0x0200 /* not fcntl */ -#define TARGET_O_EXCL 0x0400 /* not fcntl */ -#define TARGET_O_NOCTTY 0x0800 /* not fcntl */ -#define TARGET_FASYNC 0x1000 /* fcntl, for BSD compatibility */ -#define TARGET_O_LARGEFILE 0x2000 /* allow large file opens */ -#define TARGET___O_SYNC 0x4000 -#define TARGET_O_DIRECT 0x8000 /* direct disk access hint */ -#elif defined (TARGET_PPC) +#if defined (TARGET_PPC) #define TARGET_O_DIRECTORY 040000 /* must be a directory */ #define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ #define TARGET_O_LARGEFILE 0200000 @@ -2481,9 +2457,6 @@ struct target_statfs64 { #if defined(TARGET_SPARC) #define TARGET_ARCH_FLOCK_PAD abi_short __unused; #define TARGET_ARCH_FLOCK64_PAD abi_short __unused; -#elif defined(TARGET_MIPS) -#define TARGET_ARCH_FLOCK_PAD abi_long pad[4]; -#define TARGET_ARCH_FLOCK64_PAD #endif #include "target_fcntl.h" From 3e6800ba3c79faa725160464a869a2a73491d573 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:41:58 +0200 Subject: [PATCH 0988/2380] linux-user: move ppc fcntl definitions to ppc/target_fcntl.h No code change. Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-7-laurent@vivier.eu> --- linux-user/ppc/target_fcntl.h | 6 ++++++ linux-user/syscall_defs.h | 7 +------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/linux-user/ppc/target_fcntl.h b/linux-user/ppc/target_fcntl.h index 627d547289..d74ab710cf 100644 --- a/linux-user/ppc/target_fcntl.h +++ b/linux-user/ppc/target_fcntl.h @@ -7,5 +7,11 @@ #ifndef PPC_TARGET_FCNTL_H #define PPC_TARGET_FCNTL_H + +#define TARGET_O_DIRECTORY 040000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ +#define TARGET_O_LARGEFILE 0200000 +#define TARGET_O_DIRECT 0400000 /* direct disk access hint */ + #include "../generic/fcntl.h" #endif diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 44a590d2c9..a434edadf8 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2426,12 +2426,7 @@ struct target_statfs64 { #define TARGET_F_GETPIPE_SZ (TARGET_F_LINUX_SPECIFIC_BASE + 8) #define TARGET_F_NOTIFY (TARGET_F_LINUX_SPECIFIC_BASE+2) -#if defined (TARGET_PPC) -#define TARGET_O_DIRECTORY 040000 /* must be a directory */ -#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ -#define TARGET_O_LARGEFILE 0200000 -#define TARGET_O_DIRECT 0400000 /* direct disk access hint */ -#elif defined (TARGET_SPARC) +#if defined (TARGET_SPARC) #define TARGET_O_APPEND 0x0008 #define TARGET_FASYNC 0x0040 /* fcntl, for BSD compatibility */ #define TARGET_O_CREAT 0x0200 /* not fcntl */ From 995d2004b739a2a8ff60f9a0dda8c53bbdcceccf Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:41:59 +0200 Subject: [PATCH 0989/2380] linux-user: move sparc/sparc64 fcntl definitions to sparc/target_fcntl.h No code change. Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-8-laurent@vivier.eu> --- linux-user/sparc/target_fcntl.h | 34 ++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 39 --------------------------------- 2 files changed, 34 insertions(+), 39 deletions(-) diff --git a/linux-user/sparc/target_fcntl.h b/linux-user/sparc/target_fcntl.h index 35a753153b..c2532989e5 100644 --- a/linux-user/sparc/target_fcntl.h +++ b/linux-user/sparc/target_fcntl.h @@ -7,5 +7,39 @@ #ifndef SPARC_TARGET_FCNTL_H #define SPARC_TARGET_FCNTL_H + +#define TARGET_O_APPEND 0x0008 +#define TARGET_FASYNC 0x0040 /* fcntl, for BSD compatibility */ +#define TARGET_O_CREAT 0x0200 /* not fcntl */ +#define TARGET_O_TRUNC 0x0400 /* not fcntl */ +#define TARGET_O_EXCL 0x0800 /* not fcntl */ +#define TARGET_O_DSYNC 0x2000 +#define TARGET_O_NONBLOCK 0x4000 +# ifdef TARGET_SPARC64 +# define TARGET_O_NDELAY 0x0004 +# else +# define TARGET_O_NDELAY (0x0004 | TARGET_O_NONBLOCK) +# endif +#define TARGET_O_NOCTTY 0x8000 /* not fcntl */ +#define TARGET_O_LARGEFILE 0x40000 +#define TARGET_O_DIRECT 0x100000 /* direct disk access hint */ +#define TARGET_O_NOATIME 0x200000 +#define TARGET_O_CLOEXEC 0x400000 +#define TARGET___O_SYNC 0x800000 +#define TARGET_O_PATH 0x1000000 +#define TARGET___O_TMPFILE 0x2000000 + +#define TARGET_F_RDLCK 1 +#define TARGET_F_WRLCK 2 +#define TARGET_F_UNLCK 3 +#define TARGET_F_GETOWN 5 /* for sockets. */ +#define TARGET_F_SETOWN 6 /* for sockets. */ +#define TARGET_F_GETLK 7 +#define TARGET_F_SETLK 8 +#define TARGET_F_SETLKW 9 + +#define TARGET_ARCH_FLOCK_PAD abi_short __unused; +#define TARGET_ARCH_FLOCK64_PAD abi_short __unused; + #include "../generic/fcntl.h" #endif diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index a434edadf8..fbf1bf995a 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2407,17 +2407,6 @@ struct target_statfs64 { }; #endif -#if defined(TARGET_SPARC) -#define TARGET_F_RDLCK 1 -#define TARGET_F_WRLCK 2 -#define TARGET_F_UNLCK 3 -#define TARGET_F_GETOWN 5 /* for sockets. */ -#define TARGET_F_SETOWN 6 /* for sockets. */ -#define TARGET_F_GETLK 7 -#define TARGET_F_SETLK 8 -#define TARGET_F_SETLKW 9 -#endif - #define TARGET_F_LINUX_SPECIFIC_BASE 1024 #define TARGET_F_SETLEASE (TARGET_F_LINUX_SPECIFIC_BASE + 0) #define TARGET_F_GETLEASE (TARGET_F_LINUX_SPECIFIC_BASE + 1) @@ -2426,34 +2415,6 @@ struct target_statfs64 { #define TARGET_F_GETPIPE_SZ (TARGET_F_LINUX_SPECIFIC_BASE + 8) #define TARGET_F_NOTIFY (TARGET_F_LINUX_SPECIFIC_BASE+2) -#if defined (TARGET_SPARC) -#define TARGET_O_APPEND 0x0008 -#define TARGET_FASYNC 0x0040 /* fcntl, for BSD compatibility */ -#define TARGET_O_CREAT 0x0200 /* not fcntl */ -#define TARGET_O_TRUNC 0x0400 /* not fcntl */ -#define TARGET_O_EXCL 0x0800 /* not fcntl */ -#define TARGET_O_DSYNC 0x2000 -#define TARGET_O_NONBLOCK 0x4000 -# ifdef TARGET_SPARC64 -# define TARGET_O_NDELAY 0x0004 -# else -# define TARGET_O_NDELAY (0x0004 | TARGET_O_NONBLOCK) -# endif -#define TARGET_O_NOCTTY 0x8000 /* not fcntl */ -#define TARGET_O_LARGEFILE 0x40000 -#define TARGET_O_DIRECT 0x100000 /* direct disk access hint */ -#define TARGET_O_NOATIME 0x200000 -#define TARGET_O_CLOEXEC 0x400000 -#define TARGET___O_SYNC 0x800000 -#define TARGET_O_PATH 0x1000000 -#define TARGET___O_TMPFILE 0x2000000 -#endif - -#if defined(TARGET_SPARC) -#define TARGET_ARCH_FLOCK_PAD abi_short __unused; -#define TARGET_ARCH_FLOCK64_PAD abi_short __unused; -#endif - #include "target_fcntl.h" /* soundcard defines */ From 9850f9f63acb44724138a2b89b07ea4f6b3d2ba0 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:42:00 +0200 Subject: [PATCH 0990/2380] linux-user: move get_sp_from_cpustate() to target_cpu.h Remove useless includes Fix HPPA include guard. Signed-off-by: Laurent Vivier Acked-by: Richard Henderson Message-Id: <20180529194207.31503-9-laurent@vivier.eu> --- linux-user/aarch64/signal.c | 1 - linux-user/aarch64/target_cpu.h | 4 ++++ linux-user/aarch64/target_signal.h | 7 ------- linux-user/alpha/signal.c | 1 - linux-user/alpha/target_cpu.h | 4 ++++ linux-user/alpha/target_signal.h | 8 -------- linux-user/arm/signal.c | 1 - linux-user/arm/target_cpu.h | 4 ++++ linux-user/arm/target_signal.h | 7 ------- linux-user/cris/signal.c | 1 - linux-user/cris/target_cpu.h | 4 ++++ linux-user/cris/target_signal.h | 7 ------- linux-user/hppa/signal.c | 1 - linux-user/hppa/target_cpu.h | 8 ++++++-- linux-user/hppa/target_signal.h | 6 ------ linux-user/i386/signal.c | 1 - linux-user/i386/target_cpu.h | 4 ++++ linux-user/i386/target_signal.h | 7 ------- linux-user/m68k/signal.c | 1 - linux-user/m68k/target_cpu.h | 4 ++++ linux-user/m68k/target_signal.h | 7 ------- linux-user/microblaze/signal.c | 1 - linux-user/microblaze/target_cpu.h | 4 ++++ linux-user/microblaze/target_signal.h | 7 ------- linux-user/mips/signal.c | 1 - linux-user/mips/target_cpu.h | 4 ++++ linux-user/mips/target_signal.h | 7 ------- linux-user/mips64/target_signal.h | 6 ------ linux-user/nios2/signal.c | 1 - linux-user/nios2/target_cpu.h | 4 ++++ linux-user/nios2/target_signal.h | 6 ------ linux-user/openrisc/signal.c | 1 - linux-user/openrisc/target_cpu.h | 4 ++++ linux-user/openrisc/target_signal.h | 6 ------ linux-user/ppc/signal.c | 1 - linux-user/ppc/target_cpu.h | 5 ++++- linux-user/ppc/target_signal.h | 7 ------- linux-user/qemu.h | 1 - linux-user/riscv/signal.c | 1 - linux-user/riscv/target_cpu.h | 4 ++++ linux-user/riscv/target_signal.h | 6 ------ linux-user/s390x/signal.c | 1 - linux-user/s390x/target_cpu.h | 4 ++++ linux-user/s390x/target_signal.h | 7 ------- linux-user/sh4/signal.c | 1 - linux-user/sh4/target_cpu.h | 4 ++++ linux-user/sh4/target_signal.h | 7 ------- linux-user/signal.c | 1 - linux-user/sparc/signal.c | 1 - linux-user/sparc/target_cpu.h | 11 +++++++++++ linux-user/sparc/target_signal.h | 14 -------------- linux-user/sparc64/target_signal.h | 14 -------------- linux-user/syscall_defs.h | 2 ++ linux-user/tilegx/signal.c | 1 - linux-user/tilegx/target_cpu.h | 4 ++++ linux-user/tilegx/target_signal.h | 6 ------ linux-user/x86_64/target_signal.h | 6 ------ linux-user/xtensa/signal.c | 1 - linux-user/xtensa/target_cpu.h | 4 ++++ linux-user/xtensa/target_signal.h | 6 ------ 60 files changed, 83 insertions(+), 177 deletions(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index f95dc61dfb..07fedfc33c 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/aarch64/target_cpu.h b/linux-user/aarch64/target_cpu.h index 777ce29f16..a021c95fa4 100644 --- a/linux-user/aarch64/target_cpu.h +++ b/linux-user/aarch64/target_cpu.h @@ -35,4 +35,8 @@ static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls) env->cp15.tpidr_el[0] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) +{ + return state->xregs[31]; +} #endif diff --git a/linux-user/aarch64/target_signal.h b/linux-user/aarch64/target_signal.h index 0b7ae25120..18599b1447 100644 --- a/linux-user/aarch64/target_signal.h +++ b/linux-user/aarch64/target_signal.h @@ -1,8 +1,6 @@ #ifndef AARCH64_TARGET_SIGNAL_H #define AARCH64_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,5 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) -{ - return state->xregs[31]; -} - #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* AARCH64_TARGET_SIGNAL_H */ diff --git a/linux-user/alpha/signal.c b/linux-user/alpha/signal.c index f24de02c6f..c5c27ce084 100644 --- a/linux-user/alpha/signal.c +++ b/linux-user/alpha/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/alpha/target_cpu.h b/linux-user/alpha/target_cpu.h index ad124da7c0..ac4d255ae7 100644 --- a/linux-user/alpha/target_cpu.h +++ b/linux-user/alpha/target_cpu.h @@ -33,4 +33,8 @@ static inline void cpu_set_tls(CPUAlphaState *env, target_ulong newtls) env->unique = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state) +{ + return state->ir[IR_SP]; +} #endif diff --git a/linux-user/alpha/target_signal.h b/linux-user/alpha/target_signal.h index 4e912e1cf9..e6f2f04911 100644 --- a/linux-user/alpha/target_signal.h +++ b/linux-user/alpha/target_signal.h @@ -1,8 +1,6 @@ #ifndef ALPHA_TARGET_SIGNAL_H #define ALPHA_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -22,12 +20,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 4096 #define TARGET_SIGSTKSZ 16384 -static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state) -{ - return state->ir[IR_SP]; -} - - /* From . */ #define TARGET_GEN_INTOVF -1 /* integer overflow */ #define TARGET_GEN_INTDIV -2 /* integer division by zero */ diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index 59b5b65ed1..b0e753801b 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/arm/target_cpu.h b/linux-user/arm/target_cpu.h index c3eb4b243d..8a3764919a 100644 --- a/linux-user/arm/target_cpu.h +++ b/linux-user/arm/target_cpu.h @@ -49,4 +49,8 @@ static inline target_ulong cpu_get_tls(CPUARMState *env) } } +static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) +{ + return state->regs[13]; +} #endif diff --git a/linux-user/arm/target_signal.h b/linux-user/arm/target_signal.h index d6a03ec87d..f80eb0a215 100644 --- a/linux-user/arm/target_signal.h +++ b/linux-user/arm/target_signal.h @@ -1,8 +1,6 @@ #ifndef ARM_TARGET_SIGNAL_H #define ARM_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,5 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) -{ - return state->regs[13]; -} - #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* ARM_TARGET_SIGNAL_H */ diff --git a/linux-user/cris/signal.c b/linux-user/cris/signal.c index 322d9db1a7..0b405247cf 100644 --- a/linux-user/cris/signal.c +++ b/linux-user/cris/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/cris/target_cpu.h b/linux-user/cris/target_cpu.h index c43aac62f9..2309343979 100644 --- a/linux-user/cris/target_cpu.h +++ b/linux-user/cris/target_cpu.h @@ -33,4 +33,8 @@ static inline void cpu_set_tls(CPUCRISState *env, target_ulong newtls) env->pregs[PR_PID] = (env->pregs[PR_PID] & 0xff) | newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUCRISState *state) +{ + return state->regs[14]; +} #endif diff --git a/linux-user/cris/target_signal.h b/linux-user/cris/target_signal.h index 74ff2f3382..bf404a85fd 100644 --- a/linux-user/cris/target_signal.h +++ b/linux-user/cris/target_signal.h @@ -1,8 +1,6 @@ #ifndef CRIS_TARGET_SIGNAL_H #define CRIS_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,5 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUCRISState *state) -{ - return state->regs[14]; -} - #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* CRIS_TARGET_SIGNAL_H */ diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c index 6e7a295aee..b6927ee673 100644 --- a/linux-user/hppa/signal.c +++ b/linux-user/hppa/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/hppa/target_cpu.h b/linux-user/hppa/target_cpu.h index 7b78bbea80..1c539bdbd6 100644 --- a/linux-user/hppa/target_cpu.h +++ b/linux-user/hppa/target_cpu.h @@ -16,8 +16,8 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#ifndef ALPHA_TARGET_CPU_H -#define ALPHA_TARGET_CPU_H +#ifndef HPPA_TARGET_CPU_H +#define HPPA_TARGET_CPU_H static inline void cpu_clone_regs(CPUHPPAState *env, target_ulong newsp) { @@ -36,4 +36,8 @@ static inline void cpu_set_tls(CPUHPPAState *env, target_ulong newtls) env->cr[27] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUHPPAState *state) +{ + return state->gr[30]; +} #endif diff --git a/linux-user/hppa/target_signal.h b/linux-user/hppa/target_signal.h index f28b4bf6e8..1beae6485a 100644 --- a/linux-user/hppa/target_signal.h +++ b/linux-user/hppa/target_signal.h @@ -1,8 +1,6 @@ #ifndef HPPA_TARGET_SIGNAL_H #define HPPA_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,8 +19,4 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUHPPAState *state) -{ - return state->gr[30]; -} #endif /* HPPA_TARGET_SIGNAL_H */ diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index e9a23a2dec..fecb4c99c3 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/i386/target_cpu.h b/linux-user/i386/target_cpu.h index 7fbcf9bb57..ece04d0966 100644 --- a/linux-user/i386/target_cpu.h +++ b/linux-user/i386/target_cpu.h @@ -45,4 +45,8 @@ static inline void cpu_set_tls(CPUX86State *env, target_ulong newtls) } #endif /* defined(TARGET_ABI32) */ +static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) +{ + return state->regs[R_ESP]; +} #endif /* I386_TARGET_CPU_H */ diff --git a/linux-user/i386/target_signal.h b/linux-user/i386/target_signal.h index 6ad4089482..8a284f4b57 100644 --- a/linux-user/i386/target_signal.h +++ b/linux-user/i386/target_signal.h @@ -1,8 +1,6 @@ #ifndef I386_TARGET_SIGNAL_H #define I386_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,5 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) -{ - return state->regs[R_ESP]; -} - #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* I386_TARGET_SIGNAL_H */ diff --git a/linux-user/m68k/signal.c b/linux-user/m68k/signal.c index 5dd8bb5f99..38bd77ec16 100644 --- a/linux-user/m68k/signal.c +++ b/linux-user/m68k/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/m68k/target_cpu.h b/linux-user/m68k/target_cpu.h index cc0bfc298e..611df065ca 100644 --- a/linux-user/m68k/target_cpu.h +++ b/linux-user/m68k/target_cpu.h @@ -37,4 +37,8 @@ static inline void cpu_set_tls(CPUM68KState *env, target_ulong newtls) ts->tp_value = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUM68KState *state) +{ + return state->aregs[7]; +} #endif diff --git a/linux-user/m68k/target_signal.h b/linux-user/m68k/target_signal.h index ff303f2b3c..0cf26b79e5 100644 --- a/linux-user/m68k/target_signal.h +++ b/linux-user/m68k/target_signal.h @@ -1,8 +1,6 @@ #ifndef M68K_TARGET_SIGNAL_H #define M68K_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,5 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUM68KState *state) -{ - return state->aregs[7]; -} - #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* M68K_TARGET_SIGNAL_H */ diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index fada0f1495..712ee522b2 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/microblaze/target_cpu.h b/linux-user/microblaze/target_cpu.h index 7dd979f960..73e139938c 100644 --- a/linux-user/microblaze/target_cpu.h +++ b/linux-user/microblaze/target_cpu.h @@ -32,4 +32,8 @@ static inline void cpu_set_tls(CPUMBState *env, target_ulong newtls) env->regs[21] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUMBState *state) +{ + return state->regs[1]; +} #endif diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h index 9fe4048292..86adcc1fc9 100644 --- a/linux-user/microblaze/target_signal.h +++ b/linux-user/microblaze/target_signal.h @@ -1,8 +1,6 @@ #ifndef MICROBLAZE_TARGET_SIGNAL_H #define MICROBLAZE_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,5 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUMBState *state) -{ - return state->regs[1]; -} - #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* MICROBLAZE_TARGET_SIGNAL_H */ diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c index ed9849c7f6..6aa303ec9c 100644 --- a/linux-user/mips/signal.c +++ b/linux-user/mips/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/mips/target_cpu.h b/linux-user/mips/target_cpu.h index 2002920312..02cf5eeff7 100644 --- a/linux-user/mips/target_cpu.h +++ b/linux-user/mips/target_cpu.h @@ -33,4 +33,8 @@ static inline void cpu_set_tls(CPUMIPSState *env, target_ulong newtls) env->active_tc.CP0_UserLocal = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) +{ + return state->active_tc.gpr[29]; +} #endif diff --git a/linux-user/mips/target_signal.h b/linux-user/mips/target_signal.h index d36f5da0a0..5f68bd7634 100644 --- a/linux-user/mips/target_signal.h +++ b/linux-user/mips/target_signal.h @@ -1,8 +1,6 @@ #ifndef MIPS_TARGET_SIGNAL_H #define MIPS_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,11 +19,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) -{ - return state->active_tc.gpr[29]; -} - #if defined(TARGET_ABI_MIPSO32) /* compare linux/arch/mips/kernel/signal.c:setup_frame() */ #define TARGET_ARCH_HAS_SETUP_FRAME diff --git a/linux-user/mips64/target_signal.h b/linux-user/mips64/target_signal.h index c074e1592f..7fe6b2f517 100644 --- a/linux-user/mips64/target_signal.h +++ b/linux-user/mips64/target_signal.h @@ -1,8 +1,6 @@ #ifndef MIPS64_TARGET_SIGNAL_H #define MIPS64_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,8 +19,4 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) -{ - return state->active_tc.gpr[29]; -} #endif /* MIPS64_TARGET_SIGNAL_H */ diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c index 9a0b36e5ad..4985dc2212 100644 --- a/linux-user/nios2/signal.c +++ b/linux-user/nios2/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/nios2/target_cpu.h b/linux-user/nios2/target_cpu.h index 20ab4790a9..14f63338fa 100644 --- a/linux-user/nios2/target_cpu.h +++ b/linux-user/nios2/target_cpu.h @@ -36,4 +36,8 @@ static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls) */ } +static inline abi_ulong get_sp_from_cpustate(CPUNios2State *state) +{ + return state->regs[R_SP]; +} #endif diff --git a/linux-user/nios2/target_signal.h b/linux-user/nios2/target_signal.h index f4db4d6d62..1f09f1e6bb 100644 --- a/linux-user/nios2/target_signal.h +++ b/linux-user/nios2/target_signal.h @@ -1,8 +1,6 @@ #ifndef TARGET_SIGNAL_H #define TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -18,8 +16,4 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUNios2State *state) -{ - return state->regs[R_SP]; -} #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c index ecf2897ccd..8be0b74001 100644 --- a/linux-user/openrisc/signal.c +++ b/linux-user/openrisc/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/openrisc/target_cpu.h b/linux-user/openrisc/target_cpu.h index 606ad6f695..d1ea4506e2 100644 --- a/linux-user/openrisc/target_cpu.h +++ b/linux-user/openrisc/target_cpu.h @@ -33,4 +33,8 @@ static inline void cpu_set_tls(CPUOpenRISCState *env, target_ulong newtls) cpu_set_gpr(env, 10, newtls); } +static inline abi_ulong get_sp_from_cpustate(CPUOpenRISCState *state) +{ + return cpu_get_gpr(state, 1); +} #endif diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h index 2a4e00b035..590383302c 100644 --- a/linux-user/openrisc/target_signal.h +++ b/linux-user/openrisc/target_signal.h @@ -1,8 +1,6 @@ #ifndef OPENRISC_TARGET_SIGNAL_H #define OPENRISC_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -18,8 +16,4 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUOpenRISCState *state) -{ - return cpu_get_gpr(state, 1); -} #endif /* OPENRISC_TARGET_SIGNAL_H */ diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index cacc9afb5a..ef4c518f11 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/ppc/target_cpu.h b/linux-user/ppc/target_cpu.h index 3aab3d185d..c4641834e7 100644 --- a/linux-user/ppc/target_cpu.h +++ b/linux-user/ppc/target_cpu.h @@ -47,5 +47,8 @@ static inline uint32_t get_ppc64_abi(struct image_info *infop) return infop->elf_flags & EF_PPC64_ABI; } - +static inline abi_ulong get_sp_from_cpustate(CPUPPCState *state) +{ + return state->gpr[1]; +} #endif diff --git a/linux-user/ppc/target_signal.h b/linux-user/ppc/target_signal.h index e3bf1d2856..6f9e67e321 100644 --- a/linux-user/ppc/target_signal.h +++ b/linux-user/ppc/target_signal.h @@ -1,8 +1,6 @@ #ifndef PPC_TARGET_SIGNAL_H #define PPC_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,11 +19,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUPPCState *state) -{ - return state->gpr[1]; -} - #if !defined(TARGET_PPC64) #define TARGET_ARCH_HAS_SETUP_FRAME #endif diff --git a/linux-user/qemu.h b/linux-user/qemu.h index c55c8e294b..6fa1e968db 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -623,7 +623,6 @@ static inline void *lock_user_string(abi_ulong guest_addr) * above, so include them last. */ #include "target_cpu.h" -#include "target_signal.h" #include "target_structs.h" #endif /* QEMU_H */ diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index ef599e319a..f598d41891 100644 --- a/linux-user/riscv/signal.c +++ b/linux-user/riscv/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/riscv/target_cpu.h b/linux-user/riscv/target_cpu.h index c5549b1120..7e090f376a 100644 --- a/linux-user/riscv/target_cpu.h +++ b/linux-user/riscv/target_cpu.h @@ -15,4 +15,8 @@ static inline void cpu_set_tls(CPURISCVState *env, target_ulong newtls) env->gpr[xTP] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state) +{ + return state->gpr[xSP]; +} #endif diff --git a/linux-user/riscv/target_signal.h b/linux-user/riscv/target_signal.h index 9dac002c0d..c7fa357008 100644 --- a/linux-user/riscv/target_signal.h +++ b/linux-user/riscv/target_signal.h @@ -1,8 +1,6 @@ #ifndef TARGET_SIGNAL_H #define TARGET_SIGNAL_H -#include "cpu.h" - typedef struct target_sigaltstack { abi_ulong ss_sp; abi_int ss_flags; @@ -15,8 +13,4 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state) -{ - return state->gpr[xSP]; -} #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index e35cbe6870..3d3cb67bbe 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/s390x/target_cpu.h b/linux-user/s390x/target_cpu.h index 87ea4d2d9b..66ef8aa8c2 100644 --- a/linux-user/s390x/target_cpu.h +++ b/linux-user/s390x/target_cpu.h @@ -36,4 +36,8 @@ static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls) env->aregs[1] = newtls & 0xffffffffULL; } +static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state) +{ + return state->regs[15]; +} #endif diff --git a/linux-user/s390x/target_signal.h b/linux-user/s390x/target_signal.h index 4e99f8fadd..8f41ccf9b2 100644 --- a/linux-user/s390x/target_signal.h +++ b/linux-user/s390x/target_signal.h @@ -1,8 +1,6 @@ #ifndef S390X_TARGET_SIGNAL_H #define S390X_TARGET_SIGNAL_H -#include "cpu.h" - typedef struct target_sigaltstack { abi_ulong ss_sp; int ss_flags; @@ -18,10 +16,5 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state) -{ - return state->regs[15]; -} - #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* S390X_TARGET_SIGNAL_H */ diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index 2a5378e16e..c6752baa7e 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/sh4/target_cpu.h b/linux-user/sh4/target_cpu.h index 9d305d2833..1a647ddb98 100644 --- a/linux-user/sh4/target_cpu.h +++ b/linux-user/sh4/target_cpu.h @@ -32,4 +32,8 @@ static inline void cpu_set_tls(CPUSH4State *env, target_ulong newtls) env->gbr = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUSH4State *state) +{ + return state->gregs[15]; +} #endif diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h index e7b18a6db4..2bdc24c48e 100644 --- a/linux-user/sh4/target_signal.h +++ b/linux-user/sh4/target_signal.h @@ -1,8 +1,6 @@ #ifndef SH4_TARGET_SIGNAL_H #define SH4_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,5 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUSH4State *state) -{ - return state->gregs[15]; -} - #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* SH4_TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 01de433e3a..be2815b45d 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -23,7 +23,6 @@ #include "qemu.h" #include "qemu-common.h" -#include "target_signal.h" #include "trace.h" #include "signal-common.h" diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 45e922f328..55e9d6f9b2 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/sparc/target_cpu.h b/linux-user/sparc/target_cpu.h index f2fe526204..1ffc0ae9f2 100644 --- a/linux-user/sparc/target_cpu.h +++ b/linux-user/sparc/target_cpu.h @@ -41,4 +41,15 @@ static inline void cpu_set_tls(CPUSPARCState *env, target_ulong newtls) env->gregs[7] = newtls; } +#ifndef UREG_I6 +#define UREG_I6 6 +#endif +#ifndef UREG_FP +#define UREG_FP UREG_I6 +#endif + +static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) +{ + return state->regwptr[UREG_FP]; +} #endif diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h index 467abea49e..bfa19bbb67 100644 --- a/linux-user/sparc/target_signal.h +++ b/linux-user/sparc/target_signal.h @@ -1,8 +1,6 @@ #ifndef SPARC_TARGET_SIGNAL_H #define SPARC_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,17 +19,5 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 4096 #define TARGET_SIGSTKSZ 16384 -#ifndef UREG_I6 -#define UREG_I6 6 -#endif -#ifndef UREG_FP -#define UREG_FP UREG_I6 -#endif - -static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) -{ - return state->regwptr[UREG_FP]; -} - #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* SPARC_TARGET_SIGNAL_H */ diff --git a/linux-user/sparc64/target_signal.h b/linux-user/sparc64/target_signal.h index 14b01d9632..1d804bfe86 100644 --- a/linux-user/sparc64/target_signal.h +++ b/linux-user/sparc64/target_signal.h @@ -1,8 +1,6 @@ #ifndef SPARC64_TARGET_SIGNAL_H #define SPARC64_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,17 +19,5 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 4096 #define TARGET_SIGSTKSZ 16384 -#ifndef UREG_I6 -#define UREG_I6 6 -#endif -#ifndef UREG_FP -#define UREG_FP UREG_I6 -#endif - -static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) -{ - return state->regwptr[UREG_FP]; -} - #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* SPARC64_TARGET_SIGNAL_H */ diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index fbf1bf995a..85e0d870d8 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -482,6 +482,8 @@ int do_sigaction(int sig, const struct target_sigaction *act, #define TARGET_SA_RESTORER 0x04000000 #endif +#include "target_signal.h" + #ifdef TARGET_SA_RESTORER #define TARGET_ARCH_HAS_SA_RESTORER 1 #endif diff --git a/linux-user/tilegx/signal.c b/linux-user/tilegx/signal.c index d0ed3de569..c5a1c7161d 100644 --- a/linux-user/tilegx/signal.c +++ b/linux-user/tilegx/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/tilegx/target_cpu.h b/linux-user/tilegx/target_cpu.h index 4878e01b03..d1aa5824f2 100644 --- a/linux-user/tilegx/target_cpu.h +++ b/linux-user/tilegx/target_cpu.h @@ -32,4 +32,8 @@ static inline void cpu_set_tls(CPUTLGState *env, target_ulong newtls) env->regs[TILEGX_R_TP] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUTLGState *state) +{ + return state->regs[TILEGX_R_SP]; +} #endif diff --git a/linux-user/tilegx/target_signal.h b/linux-user/tilegx/target_signal.h index a74fa37aac..4cb8c56adf 100644 --- a/linux-user/tilegx/target_signal.h +++ b/linux-user/tilegx/target_signal.h @@ -1,8 +1,6 @@ #ifndef TILEGX_TARGET_SIGNAL_H #define TILEGX_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -20,8 +18,4 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUTLGState *state) -{ - return state->regs[TILEGX_R_SP]; -} #endif /* TILEGX_TARGET_SIGNAL_H */ diff --git a/linux-user/x86_64/target_signal.h b/linux-user/x86_64/target_signal.h index 6b01b5acb7..be054d1e59 100644 --- a/linux-user/x86_64/target_signal.h +++ b/linux-user/x86_64/target_signal.h @@ -1,8 +1,6 @@ #ifndef X86_64_TARGET_SIGNAL_H #define X86_64_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,8 +19,4 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) -{ - return state->regs[R_ESP]; -} #endif /* X86_64_TARGET_SIGNAL_H */ diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c index 3e483efc61..8d54ef3ae3 100644 --- a/linux-user/xtensa/signal.c +++ b/linux-user/xtensa/signal.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#include "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" diff --git a/linux-user/xtensa/target_cpu.h b/linux-user/xtensa/target_cpu.h index 747d828614..e31efe3ea0 100644 --- a/linux-user/xtensa/target_cpu.h +++ b/linux-user/xtensa/target_cpu.h @@ -19,4 +19,8 @@ static inline void cpu_set_tls(CPUXtensaState *env, target_ulong newtls) env->uregs[THREADPTR] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUXtensaState *state) +{ + return state->regs[1]; +} #endif diff --git a/linux-user/xtensa/target_signal.h b/linux-user/xtensa/target_signal.h index 4376b2e538..de03c0a564 100644 --- a/linux-user/xtensa/target_signal.h +++ b/linux-user/xtensa/target_signal.h @@ -1,8 +1,6 @@ #ifndef XTENSA_TARGET_SIGNAL_H #define XTENSA_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -20,8 +18,4 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUXtensaState *state) -{ - return state->regs[1]; -} #endif From e5171a9eb9a580b874a7e69aebaf7bf2a89818c2 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:42:01 +0200 Subject: [PATCH 0991/2380] linux-user: move generic signal definitions to generic/signal.h No code change. Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-10-laurent@vivier.eu> --- linux-user/aarch64/target_signal.h | 2 + linux-user/arm/target_signal.h | 2 + linux-user/cris/target_signal.h | 2 + linux-user/generic/signal.h | 57 +++++++++++++++++++++++++++ linux-user/i386/target_signal.h | 2 + linux-user/m68k/target_signal.h | 2 + linux-user/microblaze/target_signal.h | 2 + linux-user/nios2/target_signal.h | 2 + linux-user/openrisc/target_signal.h | 2 + linux-user/ppc/target_signal.h | 2 + linux-user/riscv/target_signal.h | 2 + linux-user/s390x/target_signal.h | 2 + linux-user/sh4/target_signal.h | 2 + linux-user/syscall_defs.h | 50 ----------------------- linux-user/tilegx/target_signal.h | 2 + linux-user/x86_64/target_signal.h | 2 + linux-user/xtensa/target_signal.h | 2 + 17 files changed, 87 insertions(+), 50 deletions(-) create mode 100644 linux-user/generic/signal.h diff --git a/linux-user/aarch64/target_signal.h b/linux-user/aarch64/target_signal.h index 18599b1447..ddd73169f0 100644 --- a/linux-user/aarch64/target_signal.h +++ b/linux-user/aarch64/target_signal.h @@ -19,5 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* AARCH64_TARGET_SIGNAL_H */ diff --git a/linux-user/arm/target_signal.h b/linux-user/arm/target_signal.h index f80eb0a215..ea123c40f3 100644 --- a/linux-user/arm/target_signal.h +++ b/linux-user/arm/target_signal.h @@ -19,5 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* ARM_TARGET_SIGNAL_H */ diff --git a/linux-user/cris/target_signal.h b/linux-user/cris/target_signal.h index bf404a85fd..1cb5548f85 100644 --- a/linux-user/cris/target_signal.h +++ b/linux-user/cris/target_signal.h @@ -19,5 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* CRIS_TARGET_SIGNAL_H */ diff --git a/linux-user/generic/signal.h b/linux-user/generic/signal.h new file mode 100644 index 0000000000..e1083f8fba --- /dev/null +++ b/linux-user/generic/signal.h @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef GENERIC_SIGNAL_H +#define GENERIC_SIGNAL_H + +#define TARGET_SA_NOCLDSTOP 0x00000001 +#define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */ +#define TARGET_SA_SIGINFO 0x00000004 +#define TARGET_SA_ONSTACK 0x08000000 +#define TARGET_SA_RESTART 0x10000000 +#define TARGET_SA_NODEFER 0x40000000 +#define TARGET_SA_RESETHAND 0x80000000 +#define TARGET_SA_RESTORER 0x04000000 + +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGIOT 6 +#define TARGET_SIGBUS 7 +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGUSR1 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGUSR2 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGSTKFLT 16 +#define TARGET_SIGCHLD 17 +#define TARGET_SIGCONT 18 +#define TARGET_SIGSTOP 19 +#define TARGET_SIGTSTP 20 +#define TARGET_SIGTTIN 21 +#define TARGET_SIGTTOU 22 +#define TARGET_SIGURG 23 +#define TARGET_SIGXCPU 24 +#define TARGET_SIGXFSZ 25 +#define TARGET_SIGVTALRM 26 +#define TARGET_SIGPROF 27 +#define TARGET_SIGWINCH 28 +#define TARGET_SIGIO 29 +#define TARGET_SIGPWR 30 +#define TARGET_SIGSYS 31 +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 0 /* for blocking signals */ +#define TARGET_SIG_UNBLOCK 1 /* for unblocking signals */ +#define TARGET_SIG_SETMASK 2 /* for setting the signal mask */ +#endif diff --git a/linux-user/i386/target_signal.h b/linux-user/i386/target_signal.h index 8a284f4b57..f55e78fd33 100644 --- a/linux-user/i386/target_signal.h +++ b/linux-user/i386/target_signal.h @@ -19,5 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* I386_TARGET_SIGNAL_H */ diff --git a/linux-user/m68k/target_signal.h b/linux-user/m68k/target_signal.h index 0cf26b79e5..314e808844 100644 --- a/linux-user/m68k/target_signal.h +++ b/linux-user/m68k/target_signal.h @@ -19,5 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* M68K_TARGET_SIGNAL_H */ diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h index 86adcc1fc9..35efd5e928 100644 --- a/linux-user/microblaze/target_signal.h +++ b/linux-user/microblaze/target_signal.h @@ -19,5 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* MICROBLAZE_TARGET_SIGNAL_H */ diff --git a/linux-user/nios2/target_signal.h b/linux-user/nios2/target_signal.h index 1f09f1e6bb..7776bcdbfd 100644 --- a/linux-user/nios2/target_signal.h +++ b/linux-user/nios2/target_signal.h @@ -16,4 +16,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h index 590383302c..3ccbb974d9 100644 --- a/linux-user/openrisc/target_signal.h +++ b/linux-user/openrisc/target_signal.h @@ -16,4 +16,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #endif /* OPENRISC_TARGET_SIGNAL_H */ diff --git a/linux-user/ppc/target_signal.h b/linux-user/ppc/target_signal.h index 6f9e67e321..4453e2e7ef 100644 --- a/linux-user/ppc/target_signal.h +++ b/linux-user/ppc/target_signal.h @@ -19,6 +19,8 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #if !defined(TARGET_PPC64) #define TARGET_ARCH_HAS_SETUP_FRAME #endif diff --git a/linux-user/riscv/target_signal.h b/linux-user/riscv/target_signal.h index c7fa357008..c8b1455800 100644 --- a/linux-user/riscv/target_signal.h +++ b/linux-user/riscv/target_signal.h @@ -13,4 +13,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/s390x/target_signal.h b/linux-user/s390x/target_signal.h index 8f41ccf9b2..b58bc7c20f 100644 --- a/linux-user/s390x/target_signal.h +++ b/linux-user/s390x/target_signal.h @@ -16,5 +16,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* S390X_TARGET_SIGNAL_H */ diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h index 2bdc24c48e..434970a990 100644 --- a/linux-user/sh4/target_signal.h +++ b/linux-user/sh4/target_signal.h @@ -19,5 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #define TARGET_ARCH_HAS_SETUP_FRAME #endif /* SH4_TARGET_SIGNAL_H */ diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 85e0d870d8..44dc7831d6 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -471,15 +471,6 @@ int do_sigaction(int sig, const struct target_sigaction *act, #define TARGET_SA_NODEFER 0x00000020 #define TARGET_SA_RESTART 0x00000040 #define TARGET_SA_NOCLDWAIT 0x00000080 -#else -#define TARGET_SA_NOCLDSTOP 0x00000001 -#define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */ -#define TARGET_SA_SIGINFO 0x00000004 -#define TARGET_SA_ONSTACK 0x08000000 -#define TARGET_SA_RESTART 0x10000000 -#define TARGET_SA_NODEFER 0x40000000 -#define TARGET_SA_RESETHAND 0x80000000 -#define TARGET_SA_RESTORER 0x04000000 #endif #include "target_signal.h" @@ -650,47 +641,6 @@ int do_sigaction(int sig, const struct target_sigaction *act, #define TARGET_SIG_UNBLOCK 1 #define TARGET_SIG_SETMASK 2 -#else - -/* OpenRISC Using the general signals */ -#define TARGET_SIGHUP 1 -#define TARGET_SIGINT 2 -#define TARGET_SIGQUIT 3 -#define TARGET_SIGILL 4 -#define TARGET_SIGTRAP 5 -#define TARGET_SIGABRT 6 -#define TARGET_SIGIOT 6 -#define TARGET_SIGBUS 7 -#define TARGET_SIGFPE 8 -#define TARGET_SIGKILL 9 -#define TARGET_SIGUSR1 10 -#define TARGET_SIGSEGV 11 -#define TARGET_SIGUSR2 12 -#define TARGET_SIGPIPE 13 -#define TARGET_SIGALRM 14 -#define TARGET_SIGTERM 15 -#define TARGET_SIGSTKFLT 16 -#define TARGET_SIGCHLD 17 -#define TARGET_SIGCONT 18 -#define TARGET_SIGSTOP 19 -#define TARGET_SIGTSTP 20 -#define TARGET_SIGTTIN 21 -#define TARGET_SIGTTOU 22 -#define TARGET_SIGURG 23 -#define TARGET_SIGXCPU 24 -#define TARGET_SIGXFSZ 25 -#define TARGET_SIGVTALRM 26 -#define TARGET_SIGPROF 27 -#define TARGET_SIGWINCH 28 -#define TARGET_SIGIO 29 -#define TARGET_SIGPWR 30 -#define TARGET_SIGSYS 31 -#define TARGET_SIGRTMIN 32 - -#define TARGET_SIG_BLOCK 0 /* for blocking signals */ -#define TARGET_SIG_UNBLOCK 1 /* for unblocking signals */ -#define TARGET_SIG_SETMASK 2 /* for setting the signal mask */ - #endif #if defined(TARGET_ALPHA) diff --git a/linux-user/tilegx/target_signal.h b/linux-user/tilegx/target_signal.h index 4cb8c56adf..655be13009 100644 --- a/linux-user/tilegx/target_signal.h +++ b/linux-user/tilegx/target_signal.h @@ -18,4 +18,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #endif /* TILEGX_TARGET_SIGNAL_H */ diff --git a/linux-user/x86_64/target_signal.h b/linux-user/x86_64/target_signal.h index be054d1e59..4c4380f7b9 100644 --- a/linux-user/x86_64/target_signal.h +++ b/linux-user/x86_64/target_signal.h @@ -19,4 +19,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #endif /* X86_64_TARGET_SIGNAL_H */ diff --git a/linux-user/xtensa/target_signal.h b/linux-user/xtensa/target_signal.h index de03c0a564..c60bf656f6 100644 --- a/linux-user/xtensa/target_signal.h +++ b/linux-user/xtensa/target_signal.h @@ -18,4 +18,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +#include "../generic/signal.h" + #endif From 1bdefb5ac73a8a5f7cc4858627424ea88d6db6de Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:42:02 +0200 Subject: [PATCH 0992/2380] linux-user: move sparc signal definitions to sparc/target_signal.h Remove sparc64/target_signal.h, use sparc/target_signal.h instead. Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-11-laurent@vivier.eu> --- linux-user/sparc/target_signal.h | 48 +++++++++++++++++++++++++++ linux-user/sparc64/signal.c | 1 - linux-user/sparc64/target_signal.h | 24 +------------- linux-user/syscall_defs.h | 52 +----------------------------- 4 files changed, 50 insertions(+), 75 deletions(-) diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h index bfa19bbb67..5cc40327d2 100644 --- a/linux-user/sparc/target_signal.h +++ b/linux-user/sparc/target_signal.h @@ -1,6 +1,44 @@ #ifndef SPARC_TARGET_SIGNAL_H #define SPARC_TARGET_SIGNAL_H +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGIOT 6 +#define TARGET_SIGSTKFLT 7 /* actually EMT */ +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGBUS 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGSYS 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGURG 16 +#define TARGET_SIGSTOP 17 +#define TARGET_SIGTSTP 18 +#define TARGET_SIGCONT 19 +#define TARGET_SIGCHLD 20 +#define TARGET_SIGTTIN 21 +#define TARGET_SIGTTOU 22 +#define TARGET_SIGIO 23 +#define TARGET_SIGXCPU 24 +#define TARGET_SIGXFSZ 25 +#define TARGET_SIGVTALRM 26 +#define TARGET_SIGPROF 27 +#define TARGET_SIGWINCH 28 +#define TARGET_SIGPWR 29 +#define TARGET_SIGUSR1 30 +#define TARGET_SIGUSR2 31 +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 0x01 /* for blocking signals */ +#define TARGET_SIG_UNBLOCK 0x02 /* for unblocking signals */ +#define TARGET_SIG_SETMASK 0x04 /* for setting the signal mask */ + /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -16,6 +54,16 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_NOCLDSTOP 8u +#define TARGET_SA_NOCLDWAIT 0x100u +#define TARGET_SA_SIGINFO 0x200u +#define TARGET_SA_ONSTACK 1u +#define TARGET_SA_RESTART 2u +#define TARGET_SA_NODEFER 0x20u +#define TARGET_SA_RESETHAND 4u +#define TARGET_ARCH_HAS_SA_RESTORER 1 +#define TARGET_ARCH_HAS_KA_RESTORER 1 + #define TARGET_MINSIGSTKSZ 4096 #define TARGET_SIGSTKSZ 16384 diff --git a/linux-user/sparc64/signal.c b/linux-user/sparc64/signal.c index c263eb0f08..170ebac232 100644 --- a/linux-user/sparc64/signal.c +++ b/linux-user/sparc64/signal.c @@ -16,5 +16,4 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#define SPARC_TARGET_SIGNAL_H /* to only include sparc64/target_signal.h */ #include "../sparc/signal.c" diff --git a/linux-user/sparc64/target_signal.h b/linux-user/sparc64/target_signal.h index 1d804bfe86..6a7d57d024 100644 --- a/linux-user/sparc64/target_signal.h +++ b/linux-user/sparc64/target_signal.h @@ -1,23 +1 @@ -#ifndef SPARC64_TARGET_SIGNAL_H -#define SPARC64_TARGET_SIGNAL_H - -/* this struct defines a stack used during syscall handling */ - -typedef struct target_sigaltstack { - abi_ulong ss_sp; - abi_long ss_flags; - abi_ulong ss_size; -} target_stack_t; - - -/* - * sigaltstack controls - */ -#define TARGET_SS_ONSTACK 1 -#define TARGET_SS_DISABLE 2 - -#define TARGET_MINSIGSTKSZ 4096 -#define TARGET_SIGSTKSZ 16384 - -#define TARGET_ARCH_HAS_SETUP_FRAME -#endif /* SPARC64_TARGET_SIGNAL_H */ +#include "../sparc/target_signal.h" diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 44dc7831d6..0034156c6b 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -426,17 +426,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \ || defined(TARGET_RISCV) || defined(TARGET_XTENSA) -#if defined(TARGET_SPARC) -#define TARGET_SA_NOCLDSTOP 8u -#define TARGET_SA_NOCLDWAIT 0x100u -#define TARGET_SA_SIGINFO 0x200u -#define TARGET_SA_ONSTACK 1u -#define TARGET_SA_RESTART 2u -#define TARGET_SA_NODEFER 0x20u -#define TARGET_SA_RESETHAND 4u -#define TARGET_ARCH_HAS_SA_RESTORER 1 -#define TARGET_ARCH_HAS_KA_RESTORER 1 -#elif defined(TARGET_MIPS) +#if defined(TARGET_MIPS) #define TARGET_SA_NOCLDSTOP 0x00000001 #define TARGET_SA_NOCLDWAIT 0x00010000 #define TARGET_SA_SIGINFO 0x00000008 @@ -518,46 +508,6 @@ int do_sigaction(int sig, const struct target_sigaction *act, #define TARGET_SIG_UNBLOCK 2 #define TARGET_SIG_SETMASK 3 -#elif defined(TARGET_SPARC) - -#define TARGET_SIGHUP 1 -#define TARGET_SIGINT 2 -#define TARGET_SIGQUIT 3 -#define TARGET_SIGILL 4 -#define TARGET_SIGTRAP 5 -#define TARGET_SIGABRT 6 -#define TARGET_SIGIOT 6 -#define TARGET_SIGSTKFLT 7 /* actually EMT */ -#define TARGET_SIGFPE 8 -#define TARGET_SIGKILL 9 -#define TARGET_SIGBUS 10 -#define TARGET_SIGSEGV 11 -#define TARGET_SIGSYS 12 -#define TARGET_SIGPIPE 13 -#define TARGET_SIGALRM 14 -#define TARGET_SIGTERM 15 -#define TARGET_SIGURG 16 -#define TARGET_SIGSTOP 17 -#define TARGET_SIGTSTP 18 -#define TARGET_SIGCONT 19 -#define TARGET_SIGCHLD 20 -#define TARGET_SIGTTIN 21 -#define TARGET_SIGTTOU 22 -#define TARGET_SIGIO 23 -#define TARGET_SIGXCPU 24 -#define TARGET_SIGXFSZ 25 -#define TARGET_SIGVTALRM 26 -#define TARGET_SIGPROF 27 -#define TARGET_SIGWINCH 28 -#define TARGET_SIGPWR 29 -#define TARGET_SIGUSR1 30 -#define TARGET_SIGUSR2 31 -#define TARGET_SIGRTMIN 32 - -#define TARGET_SIG_BLOCK 0x01 /* for blocking signals */ -#define TARGET_SIG_UNBLOCK 0x02 /* for unblocking signals */ -#define TARGET_SIG_SETMASK 0x04 /* for setting the signal mask */ - #elif defined(TARGET_MIPS) #define TARGET_SIGHUP 1 /* Hangup (POSIX). */ From f70c731347406cbd2bc79ab466415e21061fef96 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:42:03 +0200 Subject: [PATCH 0993/2380] linux-user: move mips signal definitions to mips/target_signal.h No code change. Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-12-laurent@vivier.eu> --- linux-user/mips/target_signal.h | 50 +++++++++++++++++++++++++++ linux-user/mips64/target_signal.h | 49 +++++++++++++++++++++++++++ linux-user/syscall_defs.h | 56 +------------------------------ 3 files changed, 100 insertions(+), 55 deletions(-) diff --git a/linux-user/mips/target_signal.h b/linux-user/mips/target_signal.h index 5f68bd7634..66e1ad44a6 100644 --- a/linux-user/mips/target_signal.h +++ b/linux-user/mips/target_signal.h @@ -1,6 +1,47 @@ #ifndef MIPS_TARGET_SIGNAL_H #define MIPS_TARGET_SIGNAL_H +#define TARGET_SIGHUP 1 /* Hangup (POSIX). */ +#define TARGET_SIGINT 2 /* Interrupt (ANSI). */ +#define TARGET_SIGQUIT 3 /* Quit (POSIX). */ +#define TARGET_SIGILL 4 /* Illegal instruction (ANSI). */ +#define TARGET_SIGTRAP 5 /* Trace trap (POSIX). */ +#define TARGET_SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define TARGET_SIGABRT TARGET_SIGIOT /* Abort (ANSI). */ +#define TARGET_SIGEMT 7 +#define TARGET_SIGSTKFLT 7 /* XXX: incorrect */ +#define TARGET_SIGFPE 8 /* Floating-point exception (ANSI). */ +#define TARGET_SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define TARGET_SIGBUS 10 /* BUS error (4.2 BSD). */ +#define TARGET_SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define TARGET_SIGSYS 12 +#define TARGET_SIGPIPE 13 /* Broken pipe (POSIX). */ +#define TARGET_SIGALRM 14 /* Alarm clock (POSIX). */ +#define TARGET_SIGTERM 15 /* Termination (ANSI). */ +#define TARGET_SIGUSR1 16 /* User-defined signal 1 (POSIX). */ +#define TARGET_SIGUSR2 17 /* User-defined signal 2 (POSIX). */ +#define TARGET_SIGCHLD 18 /* Child status has changed (POSIX). */ +#define TARGET_SIGCLD TARGET_SIGCHLD /* Same as TARGET_SIGCHLD (System V). */ +#define TARGET_SIGPWR 19 /* Power failure restart (System V). */ +#define TARGET_SIGWINCH 20 /* Window size change (4.3 BSD, Sun). */ +#define TARGET_SIGURG 21 /* Urgent condition on socket (4.2 BSD). */ +#define TARGET_SIGIO 22 /* I/O now possible (4.2 BSD). */ +#define TARGET_SIGPOLL TARGET_SIGIO /* Pollable event occurred (System V). */ +#define TARGET_SIGSTOP 23 /* Stop, unblockable (POSIX). */ +#define TARGET_SIGTSTP 24 /* Keyboard stop (POSIX). */ +#define TARGET_SIGCONT 25 /* Continue (POSIX). */ +#define TARGET_SIGTTIN 26 /* Background read from tty (POSIX). */ +#define TARGET_SIGTTOU 27 /* Background write to tty (POSIX). */ +#define TARGET_SIGVTALRM 28 /* Virtual alarm clock (4.2 BSD). */ +#define TARGET_SIGPROF 29 /* Profiling alarm clock (4.2 BSD). */ +#define TARGET_SIGXCPU 30 /* CPU limit exceeded (4.2 BSD). */ +#define TARGET_SIGXFSZ 31 /* File size limit exceeded (4.2 BSD). */ +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 1 /* for blocking signals */ +#define TARGET_SIG_UNBLOCK 2 /* for unblocking signals */ +#define TARGET_SIG_SETMASK 3 /* for setting the signal mask */ + /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -16,6 +57,15 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_NOCLDSTOP 0x00000001 +#define TARGET_SA_NOCLDWAIT 0x00010000 +#define TARGET_SA_SIGINFO 0x00000008 +#define TARGET_SA_ONSTACK 0x08000000 +#define TARGET_SA_NODEFER 0x40000000 +#define TARGET_SA_RESTART 0x10000000 +#define TARGET_SA_RESETHAND 0x80000000 +#define TARGET_SA_RESTORER 0x04000000 /* Only for O32 */ + #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 diff --git a/linux-user/mips64/target_signal.h b/linux-user/mips64/target_signal.h index 7fe6b2f517..753e91fbd6 100644 --- a/linux-user/mips64/target_signal.h +++ b/linux-user/mips64/target_signal.h @@ -1,6 +1,47 @@ #ifndef MIPS64_TARGET_SIGNAL_H #define MIPS64_TARGET_SIGNAL_H +#define TARGET_SIGHUP 1 /* Hangup (POSIX). */ +#define TARGET_SIGINT 2 /* Interrupt (ANSI). */ +#define TARGET_SIGQUIT 3 /* Quit (POSIX). */ +#define TARGET_SIGILL 4 /* Illegal instruction (ANSI). */ +#define TARGET_SIGTRAP 5 /* Trace trap (POSIX). */ +#define TARGET_SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define TARGET_SIGABRT TARGET_SIGIOT /* Abort (ANSI). */ +#define TARGET_SIGEMT 7 +#define TARGET_SIGSTKFLT 7 /* XXX: incorrect */ +#define TARGET_SIGFPE 8 /* Floating-point exception (ANSI). */ +#define TARGET_SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define TARGET_SIGBUS 10 /* BUS error (4.2 BSD). */ +#define TARGET_SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define TARGET_SIGSYS 12 +#define TARGET_SIGPIPE 13 /* Broken pipe (POSIX). */ +#define TARGET_SIGALRM 14 /* Alarm clock (POSIX). */ +#define TARGET_SIGTERM 15 /* Termination (ANSI). */ +#define TARGET_SIGUSR1 16 /* User-defined signal 1 (POSIX). */ +#define TARGET_SIGUSR2 17 /* User-defined signal 2 (POSIX). */ +#define TARGET_SIGCHLD 18 /* Child status has changed (POSIX). */ +#define TARGET_SIGCLD TARGET_SIGCHLD /* Same as TARGET_SIGCHLD (System V). */ +#define TARGET_SIGPWR 19 /* Power failure restart (System V). */ +#define TARGET_SIGWINCH 20 /* Window size change (4.3 BSD, Sun). */ +#define TARGET_SIGURG 21 /* Urgent condition on socket (4.2 BSD). */ +#define TARGET_SIGIO 22 /* I/O now possible (4.2 BSD). */ +#define TARGET_SIGPOLL TARGET_SIGIO /* Pollable event occurred (System V). */ +#define TARGET_SIGSTOP 23 /* Stop, unblockable (POSIX). */ +#define TARGET_SIGTSTP 24 /* Keyboard stop (POSIX). */ +#define TARGET_SIGCONT 25 /* Continue (POSIX). */ +#define TARGET_SIGTTIN 26 /* Background read from tty (POSIX). */ +#define TARGET_SIGTTOU 27 /* Background write to tty (POSIX). */ +#define TARGET_SIGVTALRM 28 /* Virtual alarm clock (4.2 BSD). */ +#define TARGET_SIGPROF 29 /* Profiling alarm clock (4.2 BSD). */ +#define TARGET_SIGXCPU 30 /* CPU limit exceeded (4.2 BSD). */ +#define TARGET_SIGXFSZ 31 /* File size limit exceeded (4.2 BSD). */ +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 1 /* for blocking signals */ +#define TARGET_SIG_UNBLOCK 2 /* for unblocking signals */ +#define TARGET_SIG_SETMASK 3 /* for setting the signal mask */ + /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -16,6 +57,14 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_NOCLDSTOP 0x00000001 +#define TARGET_SA_NOCLDWAIT 0x00010000 +#define TARGET_SA_SIGINFO 0x00000008 +#define TARGET_SA_ONSTACK 0x08000000 +#define TARGET_SA_NODEFER 0x40000000 +#define TARGET_SA_RESTART 0x10000000 +#define TARGET_SA_RESETHAND 0x80000000 + #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 0034156c6b..21d3ef5559 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -426,18 +426,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \ || defined(TARGET_RISCV) || defined(TARGET_XTENSA) -#if defined(TARGET_MIPS) -#define TARGET_SA_NOCLDSTOP 0x00000001 -#define TARGET_SA_NOCLDWAIT 0x00010000 -#define TARGET_SA_SIGINFO 0x00000008 -#define TARGET_SA_ONSTACK 0x08000000 -#define TARGET_SA_NODEFER 0x40000000 -#define TARGET_SA_RESTART 0x10000000 -#define TARGET_SA_RESETHAND 0x80000000 -#if !defined(TARGET_ABI_MIPSN32) && !defined(TARGET_ABI_MIPSN64) -#define TARGET_SA_RESTORER 0x04000000 /* Only for O32 */ -#endif -#elif defined(TARGET_OPENRISC) +#if defined(TARGET_OPENRISC) #define TARGET_SA_NOCLDSTOP 0x00000001 #define TARGET_SA_NOCLDWAIT 0x00000002 #define TARGET_SA_SIGINFO 0x00000004 @@ -508,49 +497,6 @@ int do_sigaction(int sig, const struct target_sigaction *act, #define TARGET_SIG_UNBLOCK 2 #define TARGET_SIG_SETMASK 3 -#elif defined(TARGET_MIPS) - -#define TARGET_SIGHUP 1 /* Hangup (POSIX). */ -#define TARGET_SIGINT 2 /* Interrupt (ANSI). */ -#define TARGET_SIGQUIT 3 /* Quit (POSIX). */ -#define TARGET_SIGILL 4 /* Illegal instruction (ANSI). */ -#define TARGET_SIGTRAP 5 /* Trace trap (POSIX). */ -#define TARGET_SIGIOT 6 /* IOT trap (4.2 BSD). */ -#define TARGET_SIGABRT TARGET_SIGIOT /* Abort (ANSI). */ -#define TARGET_SIGEMT 7 -#define TARGET_SIGSTKFLT 7 /* XXX: incorrect */ -#define TARGET_SIGFPE 8 /* Floating-point exception (ANSI). */ -#define TARGET_SIGKILL 9 /* Kill, unblockable (POSIX). */ -#define TARGET_SIGBUS 10 /* BUS error (4.2 BSD). */ -#define TARGET_SIGSEGV 11 /* Segmentation violation (ANSI). */ -#define TARGET_SIGSYS 12 -#define TARGET_SIGPIPE 13 /* Broken pipe (POSIX). */ -#define TARGET_SIGALRM 14 /* Alarm clock (POSIX). */ -#define TARGET_SIGTERM 15 /* Termination (ANSI). */ -#define TARGET_SIGUSR1 16 /* User-defined signal 1 (POSIX). */ -#define TARGET_SIGUSR2 17 /* User-defined signal 2 (POSIX). */ -#define TARGET_SIGCHLD 18 /* Child status has changed (POSIX). */ -#define TARGET_SIGCLD TARGET_SIGCHLD /* Same as TARGET_SIGCHLD (System V). */ -#define TARGET_SIGPWR 19 /* Power failure restart (System V). */ -#define TARGET_SIGWINCH 20 /* Window size change (4.3 BSD, Sun). */ -#define TARGET_SIGURG 21 /* Urgent condition on socket (4.2 BSD). */ -#define TARGET_SIGIO 22 /* I/O now possible (4.2 BSD). */ -#define TARGET_SIGPOLL TARGET_SIGIO /* Pollable event occurred (System V). */ -#define TARGET_SIGSTOP 23 /* Stop, unblockable (POSIX). */ -#define TARGET_SIGTSTP 24 /* Keyboard stop (POSIX). */ -#define TARGET_SIGCONT 25 /* Continue (POSIX). */ -#define TARGET_SIGTTIN 26 /* Background read from tty (POSIX). */ -#define TARGET_SIGTTOU 27 /* Background write to tty (POSIX). */ -#define TARGET_SIGVTALRM 28 /* Virtual alarm clock (4.2 BSD). */ -#define TARGET_SIGPROF 29 /* Profiling alarm clock (4.2 BSD). */ -#define TARGET_SIGXCPU 30 /* CPU limit exceeded (4.2 BSD). */ -#define TARGET_SIGXFSZ 31 /* File size limit exceeded (4.2 BSD). */ -#define TARGET_SIGRTMIN 32 - -#define TARGET_SIG_BLOCK 1 /* for blocking signals */ -#define TARGET_SIG_UNBLOCK 2 /* for unblocking signals */ -#define TARGET_SIG_SETMASK 3 /* for setting the signal mask */ - #elif defined(TARGET_HPPA) #define TARGET_SIGHUP 1 From 5795083b1616545910d9508b663008beb016fd6a Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:42:04 +0200 Subject: [PATCH 0994/2380] linux-user: move openrisc signal definitions to openrisc/target_signal.h No code change. Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-13-laurent@vivier.eu> --- linux-user/openrisc/target_signal.h | 8 ++++++++ linux-user/syscall_defs.h | 10 +--------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h index 3ccbb974d9..c352a8b333 100644 --- a/linux-user/openrisc/target_signal.h +++ b/linux-user/openrisc/target_signal.h @@ -13,6 +13,14 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_NOCLDSTOP 0x00000001 +#define TARGET_SA_NOCLDWAIT 0x00000002 +#define TARGET_SA_SIGINFO 0x00000004 +#define TARGET_SA_ONSTACK 0x08000000 +#define TARGET_SA_RESTART 0x10000000 +#define TARGET_SA_NODEFER 0x40000000 +#define TARGET_SA_RESETHAND 0x80000000 + #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 21d3ef5559..360d37c9c0 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -426,15 +426,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \ || defined(TARGET_RISCV) || defined(TARGET_XTENSA) -#if defined(TARGET_OPENRISC) -#define TARGET_SA_NOCLDSTOP 0x00000001 -#define TARGET_SA_NOCLDWAIT 0x00000002 -#define TARGET_SA_SIGINFO 0x00000004 -#define TARGET_SA_ONSTACK 0x08000000 -#define TARGET_SA_RESTART 0x10000000 -#define TARGET_SA_NODEFER 0x40000000 -#define TARGET_SA_RESETHAND 0x80000000 -#elif defined(TARGET_ALPHA) +#if defined(TARGET_ALPHA) #define TARGET_SA_ONSTACK 0x00000001 #define TARGET_SA_RESTART 0x00000002 #define TARGET_SA_NOCLDSTOP 0x00000004 From 3e511153a810c779c85b2ae8058fb3d41188b6d9 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:42:05 +0200 Subject: [PATCH 0995/2380] linux-user: move alpha signal definitions to alpha/target_signal.h No code change. Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-14-laurent@vivier.eu> --- linux-user/alpha/target_signal.h | 45 ++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 51 ++------------------------------ 2 files changed, 47 insertions(+), 49 deletions(-) diff --git a/linux-user/alpha/target_signal.h b/linux-user/alpha/target_signal.h index e6f2f04911..cd63d59fde 100644 --- a/linux-user/alpha/target_signal.h +++ b/linux-user/alpha/target_signal.h @@ -1,6 +1,43 @@ #ifndef ALPHA_TARGET_SIGNAL_H #define ALPHA_TARGET_SIGNAL_H +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGSTKFLT 7 /* actually SIGEMT */ +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGBUS 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGSYS 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGURG 16 +#define TARGET_SIGSTOP 17 +#define TARGET_SIGTSTP 18 +#define TARGET_SIGCONT 19 +#define TARGET_SIGCHLD 20 +#define TARGET_SIGTTIN 21 +#define TARGET_SIGTTOU 22 +#define TARGET_SIGIO 23 +#define TARGET_SIGXCPU 24 +#define TARGET_SIGXFSZ 25 +#define TARGET_SIGVTALRM 26 +#define TARGET_SIGPROF 27 +#define TARGET_SIGWINCH 28 +#define TARGET_SIGPWR 29 /* actually SIGINFO */ +#define TARGET_SIGUSR1 30 +#define TARGET_SIGUSR2 31 +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 1 +#define TARGET_SIG_UNBLOCK 2 +#define TARGET_SIG_SETMASK 3 + /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -17,6 +54,14 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_ONSTACK 0x00000001 +#define TARGET_SA_RESTART 0x00000002 +#define TARGET_SA_NOCLDSTOP 0x00000004 +#define TARGET_SA_NODEFER 0x00000008 +#define TARGET_SA_RESETHAND 0x00000010 +#define TARGET_SA_NOCLDWAIT 0x00000020 /* not supported yet */ +#define TARGET_SA_SIGINFO 0x00000040 + #define TARGET_MINSIGSTKSZ 4096 #define TARGET_SIGSTKSZ 16384 diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 360d37c9c0..8436875005 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -426,15 +426,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \ || defined(TARGET_RISCV) || defined(TARGET_XTENSA) -#if defined(TARGET_ALPHA) -#define TARGET_SA_ONSTACK 0x00000001 -#define TARGET_SA_RESTART 0x00000002 -#define TARGET_SA_NOCLDSTOP 0x00000004 -#define TARGET_SA_NODEFER 0x00000008 -#define TARGET_SA_RESETHAND 0x00000010 -#define TARGET_SA_NOCLDWAIT 0x00000020 /* not supported yet */ -#define TARGET_SA_SIGINFO 0x00000040 -#elif defined(TARGET_HPPA) +#if defined(TARGET_HPPA) #define TARGET_SA_ONSTACK 0x00000001 #define TARGET_SA_RESETHAND 0x00000004 #define TARGET_SA_NOCLDSTOP 0x00000008 @@ -450,46 +442,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, #define TARGET_ARCH_HAS_SA_RESTORER 1 #endif -#if defined(TARGET_ALPHA) - -#define TARGET_SIGHUP 1 -#define TARGET_SIGINT 2 -#define TARGET_SIGQUIT 3 -#define TARGET_SIGILL 4 -#define TARGET_SIGTRAP 5 -#define TARGET_SIGABRT 6 -#define TARGET_SIGSTKFLT 7 /* actually SIGEMT */ -#define TARGET_SIGFPE 8 -#define TARGET_SIGKILL 9 -#define TARGET_SIGBUS 10 -#define TARGET_SIGSEGV 11 -#define TARGET_SIGSYS 12 -#define TARGET_SIGPIPE 13 -#define TARGET_SIGALRM 14 -#define TARGET_SIGTERM 15 -#define TARGET_SIGURG 16 -#define TARGET_SIGSTOP 17 -#define TARGET_SIGTSTP 18 -#define TARGET_SIGCONT 19 -#define TARGET_SIGCHLD 20 -#define TARGET_SIGTTIN 21 -#define TARGET_SIGTTOU 22 -#define TARGET_SIGIO 23 -#define TARGET_SIGXCPU 24 -#define TARGET_SIGXFSZ 25 -#define TARGET_SIGVTALRM 26 -#define TARGET_SIGPROF 27 -#define TARGET_SIGWINCH 28 -#define TARGET_SIGPWR 29 /* actually SIGINFO */ -#define TARGET_SIGUSR1 30 -#define TARGET_SIGUSR2 31 -#define TARGET_SIGRTMIN 32 - -#define TARGET_SIG_BLOCK 1 -#define TARGET_SIG_UNBLOCK 2 -#define TARGET_SIG_SETMASK 3 - -#elif defined(TARGET_HPPA) +#if defined(TARGET_HPPA) #define TARGET_SIGHUP 1 #define TARGET_SIGINT 2 From db30b1aa8cab867d9790ba3ec593fc8a1784e48f Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:42:06 +0200 Subject: [PATCH 0996/2380] linux-user: move hppa signal definitions to hppa/target_signal.h No code change. Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-15-laurent@vivier.eu> --- linux-user/hppa/target_signal.h | 46 +++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 52 --------------------------------- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/linux-user/hppa/target_signal.h b/linux-user/hppa/target_signal.h index 1beae6485a..ba159ff8d0 100644 --- a/linux-user/hppa/target_signal.h +++ b/linux-user/hppa/target_signal.h @@ -1,6 +1,44 @@ #ifndef HPPA_TARGET_SIGNAL_H #define HPPA_TARGET_SIGNAL_H +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGIOT 6 +#define TARGET_SIGSTKFLT 7 +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGBUS 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGXCPU 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGUSR1 16 +#define TARGET_SIGUSR2 17 +#define TARGET_SIGCHLD 18 +#define TARGET_SIGPWR 19 +#define TARGET_SIGVTALRM 20 +#define TARGET_SIGPROF 21 +#define TARGET_SIGIO 22 +#define TARGET_SIGPOLL TARGET_SIGIO +#define TARGET_SIGWINCH 23 +#define TARGET_SIGSTOP 24 +#define TARGET_SIGTSTP 25 +#define TARGET_SIGCONT 26 +#define TARGET_SIGTTIN 27 +#define TARGET_SIGTTOU 28 +#define TARGET_SIGURG 29 +#define TARGET_SIGXFSZ 30 +#define TARGET_SIGSYS 31 + +#define TARGET_SIG_BLOCK 0 +#define TARGET_SIG_UNBLOCK 1 +#define TARGET_SIG_SETMASK 2 + /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -16,6 +54,14 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_ONSTACK 0x00000001 +#define TARGET_SA_RESETHAND 0x00000004 +#define TARGET_SA_NOCLDSTOP 0x00000008 +#define TARGET_SA_SIGINFO 0x00000010 +#define TARGET_SA_NODEFER 0x00000020 +#define TARGET_SA_RESTART 0x00000040 +#define TARGET_SA_NOCLDWAIT 0x00000080 + #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 8436875005..e2896ae1b3 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -426,64 +426,12 @@ int do_sigaction(int sig, const struct target_sigaction *act, || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \ || defined(TARGET_RISCV) || defined(TARGET_XTENSA) -#if defined(TARGET_HPPA) -#define TARGET_SA_ONSTACK 0x00000001 -#define TARGET_SA_RESETHAND 0x00000004 -#define TARGET_SA_NOCLDSTOP 0x00000008 -#define TARGET_SA_SIGINFO 0x00000010 -#define TARGET_SA_NODEFER 0x00000020 -#define TARGET_SA_RESTART 0x00000040 -#define TARGET_SA_NOCLDWAIT 0x00000080 -#endif - #include "target_signal.h" #ifdef TARGET_SA_RESTORER #define TARGET_ARCH_HAS_SA_RESTORER 1 #endif -#if defined(TARGET_HPPA) - -#define TARGET_SIGHUP 1 -#define TARGET_SIGINT 2 -#define TARGET_SIGQUIT 3 -#define TARGET_SIGILL 4 -#define TARGET_SIGTRAP 5 -#define TARGET_SIGABRT 6 -#define TARGET_SIGIOT 6 -#define TARGET_SIGSTKFLT 7 -#define TARGET_SIGFPE 8 -#define TARGET_SIGKILL 9 -#define TARGET_SIGBUS 10 -#define TARGET_SIGSEGV 11 -#define TARGET_SIGXCPU 12 -#define TARGET_SIGPIPE 13 -#define TARGET_SIGALRM 14 -#define TARGET_SIGTERM 15 -#define TARGET_SIGUSR1 16 -#define TARGET_SIGUSR2 17 -#define TARGET_SIGCHLD 18 -#define TARGET_SIGPWR 19 -#define TARGET_SIGVTALRM 20 -#define TARGET_SIGPROF 21 -#define TARGET_SIGIO 22 -#define TARGET_SIGPOLL TARGET_SIGIO -#define TARGET_SIGWINCH 23 -#define TARGET_SIGSTOP 24 -#define TARGET_SIGTSTP 25 -#define TARGET_SIGCONT 26 -#define TARGET_SIGTTIN 27 -#define TARGET_SIGTTOU 28 -#define TARGET_SIGURG 29 -#define TARGET_SIGXFSZ 30 -#define TARGET_SIGSYS 31 - -#define TARGET_SIG_BLOCK 0 -#define TARGET_SIG_UNBLOCK 1 -#define TARGET_SIG_SETMASK 2 - -#endif - #if defined(TARGET_ALPHA) struct target_old_sigaction { abi_ulong _sa_handler; From 7ea65371ee6ba714c5b369b38dd608b128dab57c Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 May 2018 21:42:07 +0200 Subject: [PATCH 0997/2380] linux-user: remove useless #if Remove a "#if defined(XX) || defined(YY) || ..." with all available targets Signed-off-by: Laurent Vivier Message-Id: <20180529194207.31503-16-laurent@vivier.eu> --- linux-user/syscall_defs.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index e2896ae1b3..40bb60ef4c 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -418,14 +418,6 @@ struct target_sigaction; int do_sigaction(int sig, const struct target_sigaction *act, struct target_sigaction *oact); -#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \ - || defined(TARGET_PPC) || defined(TARGET_MIPS) || defined(TARGET_SH4) \ - || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \ - || defined(TARGET_MICROBLAZE) \ - || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ - || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \ - || defined(TARGET_RISCV) || defined(TARGET_XTENSA) - #include "target_signal.h" #ifdef TARGET_SA_RESTORER @@ -666,8 +658,6 @@ typedef struct target_siginfo { #define TARGET_TRAP_BRANCH (3) /* process taken branch trap */ #define TARGET_TRAP_HWBKPT (4) /* hardware breakpoint/watchpoint */ -#endif /* defined(TARGET_I386) || defined(TARGET_ARM) */ - struct target_rlimit { abi_ulong rlim_cur; abi_ulong rlim_max; From f548222c24342ca74689de7794f9006b43f86a54 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Thu, 3 May 2018 16:06:11 +0800 Subject: [PATCH 0998/2380] migration: introduce decompress-error-check QEMU 3.0 enables strict check for compression & decompression to make the migration more robust, that depends on the source to fix the internal design which triggers the unexpected error conditions To make it work for migrating old version QEMU to 2.13 QEMU, we introduce this parameter to disable the error check on the destination which is the default behavior of the machine type which is older than 2.13, alternately, the strict check can be enabled explicitly as followings: -M pc-q35-2.11 -global migration.decompress-error-check=true Signed-off-by: Xiao Guangrong Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- hw/arm/virt.c | 4 ++++ hw/i386/pc_piix.c | 1 + hw/i386/pc_q35.c | 1 + include/hw/compat.h | 7 ++++++- migration/migration.c | 4 ++++ migration/migration.h | 7 +++++++ migration/ram.c | 2 +- 7 files changed, 24 insertions(+), 2 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index a3a28e20e8..b2a67a4c00 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1693,6 +1693,9 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); +#define VIRT_COMPAT_2_12 \ + HW_COMPAT_2_12 + static void virt_2_12_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); @@ -1763,6 +1766,7 @@ static void virt_2_12_instance_init(Object *obj) static void virt_machine_2_12_options(MachineClass *mc) { + SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12); } DEFINE_VIRT_MACHINE_AS_LATEST(2, 12) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index b4c5b03274..3d81136065 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -430,6 +430,7 @@ static void pc_i440fx_3_0_machine_options(MachineClass *m) pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); } DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", NULL, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 83d6d75efa..b60cbb9266 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -312,6 +312,7 @@ static void pc_q35_3_0_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); } DEFINE_Q35_MACHINE(v3_0, "pc-q35-3.0", NULL, diff --git a/include/hw/compat.h b/include/hw/compat.h index 4681c2719a..563908b874 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -1,7 +1,12 @@ #ifndef HW_COMPAT_H #define HW_COMPAT_H -#define HW_COMPAT_2_12 +#define HW_COMPAT_2_12 \ + {\ + .driver = "migration",\ + .property = "decompress-error-check",\ + .value = "off",\ + }, #define HW_COMPAT_2_11 \ {\ diff --git a/migration/migration.c b/migration/migration.c index 05aec2c905..a5384865ff 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2971,6 +2971,8 @@ void migration_global_dump(Monitor *mon) ms->send_configuration ? "on" : "off"); monitor_printf(mon, "send-section-footer: %s\n", ms->send_section_footer ? "on" : "off"); + monitor_printf(mon, "decompress-error-check: %s\n", + ms->decompress_error_check ? "on" : "off"); } #define DEFINE_PROP_MIG_CAP(name, x) \ @@ -2984,6 +2986,8 @@ static Property migration_properties[] = { send_configuration, true), DEFINE_PROP_BOOL("send-section-footer", MigrationState, send_section_footer, true), + DEFINE_PROP_BOOL("decompress-error-check", MigrationState, + decompress_error_check, true), /* Migration parameters */ DEFINE_PROP_UINT8("x-compress-level", MigrationState, diff --git a/migration/migration.h b/migration/migration.h index 8f0c82159b..5af57d616c 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -212,6 +212,13 @@ struct MigrationState /* Needed by postcopy-pause state */ QemuSemaphore postcopy_pause_sem; QemuSemaphore postcopy_pause_rp_sem; + /* + * Whether we abort the migration if decompression errors are + * detected at the destination. It is left at false for qemu + * older than 3.0, since only newer qemu sends streams that + * do not trigger spurious decompression errors. + */ + bool decompress_error_check; }; void migrate_set_state(int *state, int old_state, int new_state); diff --git a/migration/ram.c b/migration/ram.c index c53e8369a3..090187ca04 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2881,7 +2881,7 @@ static void *do_data_decompress(void *opaque) ret = qemu_uncompress_data(¶m->stream, des, pagesize, param->compbuf, len); - if (ret < 0) { + if (ret < 0 && migrate_get_current()->decompress_error_check) { error_report("decompress data failed"); qemu_file_set_error(decomp_file, ret); } From b895de502717b83b4e5f089df617cb23530c4d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 14 May 2018 08:57:00 +0200 Subject: [PATCH 0999/2380] migration: discard non-migratable RAMBlocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the POWER9 processor, the XIVE interrupt controller can control interrupt sources using MMIO to trigger events, to EOI or to turn off the sources. Priority management and interrupt acknowledgment is also controlled by MMIO in the presenter sub-engine. These MMIO regions are exposed to guests in QEMU with a set of 'ram device' memory mappings, similarly to VFIO, and the VMAs are populated dynamically with the appropriate pages using a fault handler. But, these regions are an issue for migration. We need to discard the associated RAMBlocks from the RAM state on the source VM and let the destination VM rebuild the memory mappings on the new host in the post_load() operation just before resuming the system. To achieve this goal, the following introduces a new RAMBlock flag RAM_MIGRATABLE which is updated in the vmstate_register_ram() and vmstate_unregister_ram() routines. This flag is then used by the migration to identify RAMBlocks to discard on the source. Some checks are also performed on the destination to make sure nothing invalid was sent. This change impacts the boston, malta and jazz mips boards for which migration compatibility is broken. Signed-off-by: Cédric Le Goater Reviewed-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Juan Quintela --- exec.c | 38 ++++++++++++++++++++++++++++++++ include/exec/cpu-common.h | 4 ++++ migration/postcopy-ram.c | 12 +++++----- migration/ram.c | 46 +++++++++++++++++++++++++++++---------- migration/savevm.c | 2 ++ 5 files changed, 84 insertions(+), 18 deletions(-) diff --git a/exec.c b/exec.c index c30f905598..af90e914cf 100644 --- a/exec.c +++ b/exec.c @@ -104,6 +104,9 @@ static MemoryRegion io_mem_unassigned; * (Set during postcopy) */ #define RAM_UF_ZEROPAGE (1 << 3) + +/* RAM can be migrated */ +#define RAM_MIGRATABLE (1 << 4) #endif #ifdef TARGET_PAGE_BITS_VARY @@ -1838,6 +1841,21 @@ void qemu_ram_set_uf_zeroable(RAMBlock *rb) rb->flags |= RAM_UF_ZEROPAGE; } +bool qemu_ram_is_migratable(RAMBlock *rb) +{ + return rb->flags & RAM_MIGRATABLE; +} + +void qemu_ram_set_migratable(RAMBlock *rb) +{ + rb->flags |= RAM_MIGRATABLE; +} + +void qemu_ram_unset_migratable(RAMBlock *rb) +{ + rb->flags &= ~RAM_MIGRATABLE; +} + /* Called with iothread lock held. */ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev) { @@ -3893,6 +3911,26 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) return ret; } +int qemu_ram_foreach_migratable_block(RAMBlockIterFunc func, void *opaque) +{ + RAMBlock *block; + int ret = 0; + + rcu_read_lock(); + RAMBLOCK_FOREACH(block) { + if (!qemu_ram_is_migratable(block)) { + continue; + } + ret = func(block->idstr, block->host, block->offset, + block->used_length, opaque); + if (ret) { + break; + } + } + rcu_read_unlock(); + return ret; +} + /* * Unmap pages of memory from start to start+length such that * they a) read as 0, b) Trigger whatever fault mechanism diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 24d335f95d..0b58e262f3 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -75,6 +75,9 @@ const char *qemu_ram_get_idstr(RAMBlock *rb); bool qemu_ram_is_shared(RAMBlock *rb); bool qemu_ram_is_uf_zeroable(RAMBlock *rb); void qemu_ram_set_uf_zeroable(RAMBlock *rb); +bool qemu_ram_is_migratable(RAMBlock *rb); +void qemu_ram_set_migratable(RAMBlock *rb); +void qemu_ram_unset_migratable(RAMBlock *rb); size_t qemu_ram_pagesize(RAMBlock *block); size_t qemu_ram_pagesize_largest(void); @@ -119,6 +122,7 @@ typedef int (RAMBlockIterFunc)(const char *block_name, void *host_addr, ram_addr_t offset, ram_addr_t length, void *opaque); int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); +int qemu_ram_foreach_migratable_block(RAMBlockIterFunc func, void *opaque); int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); #endif diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 658b750a8e..48e51556a7 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -374,7 +374,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) } /* We don't support postcopy with shared RAM yet */ - if (qemu_ram_foreach_block(test_ramblock_postcopiable, NULL)) { + if (qemu_ram_foreach_migratable_block(test_ramblock_postcopiable, NULL)) { goto out; } @@ -502,7 +502,7 @@ static int cleanup_range(const char *block_name, void *host_addr, */ int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages) { - if (qemu_ram_foreach_block(init_range, NULL)) { + if (qemu_ram_foreach_migratable_block(init_range, NULL)) { return -1; } @@ -524,7 +524,7 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) return -1; } - if (qemu_ram_foreach_block(cleanup_range, mis)) { + if (qemu_ram_foreach_migratable_block(cleanup_range, mis)) { return -1; } /* Let the fault thread quit */ @@ -593,7 +593,7 @@ static int nhp_range(const char *block_name, void *host_addr, */ int postcopy_ram_prepare_discard(MigrationIncomingState *mis) { - if (qemu_ram_foreach_block(nhp_range, mis)) { + if (qemu_ram_foreach_migratable_block(nhp_range, mis)) { return -1; } @@ -604,7 +604,7 @@ int postcopy_ram_prepare_discard(MigrationIncomingState *mis) /* * Mark the given area of RAM as requiring notification to unwritten areas - * Used as a callback on qemu_ram_foreach_block. + * Used as a callback on qemu_ram_foreach_migratable_block. * host_addr: Base of area to mark * offset: Offset in the whole ram arena * length: Length of the section @@ -1099,7 +1099,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis) mis->have_fault_thread = true; /* Mark so that we get notified of accesses to unwritten areas */ - if (qemu_ram_foreach_block(ram_block_enable_notify, mis)) { + if (qemu_ram_foreach_migratable_block(ram_block_enable_notify, mis)) { return -1; } diff --git a/migration/ram.c b/migration/ram.c index 090187ca04..290b281446 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -157,11 +157,16 @@ out: return ret; } +/* Should be holding either ram_list.mutex, or the RCU lock. */ +#define RAMBLOCK_FOREACH_MIGRATABLE(block) \ + RAMBLOCK_FOREACH(block) \ + if (!qemu_ram_is_migratable(block)) {} else + static void ramblock_recv_map_init(void) { RAMBlock *rb; - RAMBLOCK_FOREACH(rb) { + RAMBLOCK_FOREACH_MIGRATABLE(rb) { assert(!rb->receivedmap); rb->receivedmap = bitmap_new(rb->max_length >> qemu_target_page_bits()); } @@ -1078,6 +1083,10 @@ unsigned long migration_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, unsigned long *bitmap = rb->bmap; unsigned long next; + if (!qemu_ram_is_migratable(rb)) { + return size; + } + if (rs->ram_bulk_stage && start > 0) { next = start + 1; } else { @@ -1123,7 +1132,7 @@ uint64_t ram_pagesize_summary(void) RAMBlock *block; uint64_t summary = 0; - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { summary |= block->page_size; } @@ -1147,7 +1156,7 @@ static void migration_bitmap_sync(RAMState *rs) qemu_mutex_lock(&rs->bitmap_mutex); rcu_read_lock(); - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { migration_bitmap_sync_range(rs, block, 0, block->used_length); } rcu_read_unlock(); @@ -1786,6 +1795,11 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, size_t pagesize_bits = qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS; + if (!qemu_ram_is_migratable(pss->block)) { + error_report("block %s should not be migrated !", pss->block->idstr); + return 0; + } + do { /* Check the pages is dirty and if it is send it */ if (!migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { @@ -1884,7 +1898,7 @@ uint64_t ram_bytes_total(void) uint64_t total = 0; rcu_read_lock(); - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { total += block->used_length; } rcu_read_unlock(); @@ -1939,7 +1953,7 @@ static void ram_save_cleanup(void *opaque) */ memory_global_dirty_log_stop(); - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { g_free(block->bmap); block->bmap = NULL; g_free(block->unsentmap); @@ -2002,7 +2016,7 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms) { struct RAMBlock *block; - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { unsigned long *bitmap = block->bmap; unsigned long range = block->used_length >> TARGET_PAGE_BITS; unsigned long run_start = find_next_zero_bit(bitmap, range, 0); @@ -2080,7 +2094,7 @@ static int postcopy_each_ram_send_discard(MigrationState *ms) struct RAMBlock *block; int ret; - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { PostcopyDiscardState *pds = postcopy_discard_send_init(ms, block->idstr); @@ -2288,7 +2302,7 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) rs->last_sent_block = NULL; rs->last_page = 0; - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { unsigned long pages = block->used_length >> TARGET_PAGE_BITS; unsigned long *bitmap = block->bmap; unsigned long *unsentmap = block->unsentmap; @@ -2447,7 +2461,7 @@ static void ram_list_init_bitmaps(void) /* Skip setting bitmap if there is no RAM */ if (ram_bytes_total()) { - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { pages = block->max_length >> TARGET_PAGE_BITS; block->bmap = bitmap_new(pages); bitmap_set(block->bmap, 0, pages); @@ -2563,7 +2577,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { 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); @@ -2807,6 +2821,11 @@ static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags) return NULL; } + if (!qemu_ram_is_migratable(block)) { + error_report("block %s should not be migrated !", id); + return NULL; + } + return block; } @@ -3049,7 +3068,7 @@ static int ram_load_cleanup(void *opaque) xbzrle_load_cleanup(); compress_threads_load_cleanup(); - RAMBLOCK_FOREACH(rb) { + RAMBLOCK_FOREACH_MIGRATABLE(rb) { g_free(rb->receivedmap); rb->receivedmap = NULL; } @@ -3311,7 +3330,10 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) length = qemu_get_be64(f); block = qemu_ram_block_by_name(id); - if (block) { + if (block && !qemu_ram_is_migratable(block)) { + error_report("block %s should not be migrated !", id); + ret = -EINVAL; + } else if (block) { if (length != block->used_length) { Error *local_err = NULL; diff --git a/migration/savevm.c b/migration/savevm.c index 4251125831..a68a9ac635 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2688,11 +2688,13 @@ void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev) { qemu_ram_set_idstr(mr->ram_block, memory_region_name(mr), dev); + qemu_ram_set_migratable(mr->ram_block); } void vmstate_unregister_ram(MemoryRegion *mr, DeviceState *dev) { qemu_ram_unset_idstr(mr->ram_block); + qemu_ram_unset_migratable(mr->ram_block); } void vmstate_register_ram_global(MemoryRegion *mr) From 0f073f44df109ea0910d67caede70dec95956ff6 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Mon, 16 Apr 2018 18:09:30 +0100 Subject: [PATCH 1000/2380] migration: Don't activate block devices if using -S Activating the block devices causes the locks to be taken on the backing file. If we're running with -S and the destination libvirt hasn't started the destination with 'cont', it's expecting the locks are still untaken. Don't activate the block devices if we're not going to autostart the VM; 'cont' already will do that anyway. This change is tied to the new migration capability 'late-block-activate' that defaults to off, keeping the old behaviour by default. bz: https://bugzilla.redhat.com/show_bug.cgi?id=1560854 Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 34 +++++++++++++++++++++++++++------- qapi/migration.json | 6 +++++- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index a5384865ff..1e99ec9b7e 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -202,6 +202,16 @@ static void migrate_generate_event(int new_state) } } +static bool migrate_late_block_activate(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[ + MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE]; +} + /* * Called on -incoming with a defer: uri. * The migration can be started later after any parameters have been @@ -311,13 +321,23 @@ static void process_incoming_migration_bh(void *opaque) Error *local_err = NULL; MigrationIncomingState *mis = opaque; - /* Make sure all file formats flush their mutable metadata. - * If we get an error here, just don't restart the VM yet. */ - bdrv_invalidate_cache_all(&local_err); - if (local_err) { - error_report_err(local_err); - local_err = NULL; - autostart = false; + /* If capability late_block_activate is set: + * Only fire up the block code now if we're going to restart the + * VM, else 'cont' will do it. + * This causes file locking to happen; so we don't want it to happen + * unless we really are starting the VM. + */ + if (!migrate_late_block_activate() || + (autostart && (!global_state_received() || + global_state_get_runstate() == RUN_STATE_RUNNING))) { + /* Make sure all file formats flush their mutable metadata. + * If we get an error here, just don't restart the VM yet. */ + bdrv_invalidate_cache_all(&local_err); + if (local_err) { + error_report_err(local_err); + local_err = NULL; + autostart = false; + } } /* diff --git a/qapi/migration.json b/qapi/migration.json index dc9cc85545..f7e10ee90f 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -376,13 +376,17 @@ # @postcopy-blocktime: Calculate downtime for postcopy live migration # (since 3.0) # +# @late-block-activate: If enabled, the destination will not activate block +# devices (and thus take locks) immediately at the end of migration. +# (since 3.0) +# # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', 'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram', 'block', 'return-path', 'pause-before-switchover', 'x-multifd', - 'dirty-bitmaps', 'postcopy-blocktime' ] } + 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate' ] } ## # @MigrationCapabilityStatus: From f38f6d4155c0c5a3e96d81183362a40e2cc09b4c Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Wed, 30 May 2018 17:43:27 +0800 Subject: [PATCH 1001/2380] migration: remove unnecessary variables len in QIOChannelRDMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because qio_channel_rdma_writev and qio_channel_rdma_readv maybe invoked by different threads concurrently, this patch removes unnecessary variables len in QIOChannelRDMA and use local variable instead. Signed-off-by: Lidong Chen Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Daniel P. Berrangé Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Signed-off-by: Lidong Chen --- migration/rdma.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/migration/rdma.c b/migration/rdma.c index 7d233b0820..60779221b1 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -400,7 +400,6 @@ struct QIOChannelRDMA { QIOChannel parent; RDMAContext *rdma; QEMUFile *file; - size_t len; bool blocking; /* XXX we don't actually honour this yet */ }; @@ -2608,6 +2607,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, int ret; ssize_t done = 0; size_t i; + size_t len = 0; CHECK_ERROR_STATE(); @@ -2627,10 +2627,10 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, while (remaining) { RDMAControlHeader head; - rioc->len = MIN(remaining, RDMA_SEND_INCREMENT); - remaining -= rioc->len; + len = MIN(remaining, RDMA_SEND_INCREMENT); + remaining -= len; - head.len = rioc->len; + head.len = len; head.type = RDMA_CONTROL_QEMU_FILE; ret = qemu_rdma_exchange_send(rdma, &head, data, NULL, NULL, NULL); @@ -2640,8 +2640,8 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, return ret; } - data += rioc->len; - done += rioc->len; + data += len; + done += len; } } @@ -2736,8 +2736,7 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, } } } - rioc->len = done; - return rioc->len; + return done; } /* From c5e76115ccb4979cec795a8ae38becd07c2fde9f Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Wed, 30 May 2018 17:43:31 +0800 Subject: [PATCH 1002/2380] migration: not wait RDMA_CM_EVENT_DISCONNECTED event after rdma_disconnect When cancel migration during RDMA precopy, the source qemu main thread hangs sometime. The backtrace is: (gdb) bt #0 0x00007f249eabd43d in write () from /lib64/libpthread.so.0 #1 0x00007f24a1ce98e4 in rdma_get_cm_event (channel=0x4675d10, event=0x7ffe2f643dd0) at src/cma.c:2189 #2 0x00000000007b6166 in qemu_rdma_cleanup (rdma=0x6784000) at migration/rdma.c:2296 #3 0x00000000007b7cae in qio_channel_rdma_close (ioc=0x3bfcc30, errp=0x0) at migration/rdma.c:2999 #4 0x00000000008db60e in qio_channel_close (ioc=0x3bfcc30, errp=0x0) at io/channel.c:273 #5 0x00000000007a8765 in channel_close (opaque=0x3bfcc30) at migration/qemu-file-channel.c:98 #6 0x00000000007a71f9 in qemu_fclose (f=0x527c000) at migration/qemu-file.c:334 #7 0x0000000000795b96 in migrate_fd_cleanup (opaque=0x3b46280) at migration/migration.c:1162 #8 0x000000000093a71b in aio_bh_call (bh=0x3db7a20) at util/async.c:90 #9 0x000000000093a7b2 in aio_bh_poll (ctx=0x3b121c0) at util/async.c:118 #10 0x000000000093f2ad in aio_dispatch (ctx=0x3b121c0) at util/aio-posix.c:436 #11 0x000000000093ab41 in aio_ctx_dispatch (source=0x3b121c0, callback=0x0, user_data=0x0) at util/async.c:261 #12 0x00007f249f73c7aa in g_main_context_dispatch () from /lib64/libglib-2.0.so.0 #13 0x000000000093dc5e in glib_pollfds_poll () at util/main-loop.c:215 #14 0x000000000093dd4e in os_host_main_loop_wait (timeout=28000000) at util/main-loop.c:263 #15 0x000000000093de05 in main_loop_wait (nonblocking=0) at util/main-loop.c:522 #16 0x00000000005bc6a5 in main_loop () at vl.c:1944 #17 0x00000000005c39b5 in main (argc=56, argv=0x7ffe2f6443f8, envp=0x3ad0030) at vl.c:4752 It does not get the RDMA_CM_EVENT_DISCONNECTED event after rdma_disconnect sometime. According to IB Spec once active side send DREQ message, it should wait for DREP message and only once it arrived it should trigger a DISCONNECT event. DREP message can be dropped due to network issues. For that case the spec defines a DREP_timeout state in the CM state machine, if the DREP is dropped we should get a timeout and a TIMEWAIT_EXIT event will be trigger. Unfortunately the current kernel CM implementation doesn't include the DREP_timeout state and in above scenario we will not get DISCONNECT or TIMEWAIT_EXIT events. So it should not invoke rdma_get_cm_event which may hang forever, and the event channel is also destroyed in qemu_rdma_cleanup. Signed-off-by: Lidong Chen Reviewed-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Juan Quintela --- migration/rdma.c | 12 ++---------- migration/trace-events | 1 - 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/migration/rdma.c b/migration/rdma.c index 60779221b1..05aee3d591 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -2267,8 +2267,7 @@ static int qemu_rdma_write(QEMUFile *f, RDMAContext *rdma, static void qemu_rdma_cleanup(RDMAContext *rdma) { - struct rdma_cm_event *cm_event; - int ret, idx; + int idx; if (rdma->cm_id && rdma->connected) { if ((rdma->error_state || @@ -2282,14 +2281,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) qemu_rdma_post_send_control(rdma, NULL, &head); } - ret = rdma_disconnect(rdma->cm_id); - if (!ret) { - trace_qemu_rdma_cleanup_waiting_for_disconnect(); - ret = rdma_get_cm_event(rdma->channel, &cm_event); - if (!ret) { - rdma_ack_cm_event(cm_event); - } - } + rdma_disconnect(rdma->cm_id); trace_qemu_rdma_cleanup_disconnect(); rdma->connected = false; } diff --git a/migration/trace-events b/migration/trace-events index 3c798ddd11..4a768eaaeb 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -146,7 +146,6 @@ qemu_rdma_accept_pin_state(bool pin) "%d" qemu_rdma_accept_pin_verbsc(void *verbs) "Verbs context after listen: %p" qemu_rdma_block_for_wrid_miss(const char *wcompstr, int wcomp, const char *gcompstr, uint64_t req) "A Wanted wrid %s (%d) but got %s (%" PRIu64 ")" qemu_rdma_cleanup_disconnect(void) "" -qemu_rdma_cleanup_waiting_for_disconnect(void) "" qemu_rdma_close(void) "" qemu_rdma_connect_pin_all_requested(void) "" qemu_rdma_connect_pin_all_outcome(bool pin) "%d" From 5609c5127dcdcb68fbc76958435a2986f095d939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 21 May 2018 11:35:04 +0100 Subject: [PATCH 1003/2380] tests/docker/Makefile.include: handle empty TARGET_LIST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the user doesn't specify a TARGET_LIST they get the current configuration but with spaces and hilarity ensues. This adds some make magic to turn the TARGET_LIST back into a comma separated list. Signed-off-by: Alex Bennée Message-Id: <20180521103504.26432-1-alex.bennee@linaro.org> Signed-off-by: Fam Zheng --- rules.mak | 3 +++ tests/docker/Makefile.include | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/rules.mak b/rules.mak index 04c7f74d07..bbb2667928 100644 --- a/rules.mak +++ b/rules.mak @@ -1,4 +1,7 @@ +# These are used when we want to do substitutions without confusing Make +NULL := +SPACE := $(NULL) # COMMA := , # Don't use implicit rules or variables diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 31f21a43f5..9d5749887a 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -140,7 +140,7 @@ docker-run: docker-qemu-src $(if $V,,--rm) \ $(if $(DEBUG),-ti,) \ $(if $(NETWORK),$(if $(subst $(NETWORK),,1),--net=$(NETWORK)),--net=none) \ - -e TARGET_LIST=$(TARGET_LIST) \ + -e TARGET_LIST=$(subst $(SPACE),$(COMMA),$(TARGET_LIST)) \ -e EXTRA_CONFIGURE_OPTS="$(EXTRA_CONFIGURE_OPTS)" \ -e V=$V -e J=$J -e DEBUG=$(DEBUG) \ -e SHOW_ENV=$(SHOW_ENV) \ From 63dba573fa65bf830ec3f78a8f3a6b0697ce1637 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 1 Jun 2018 10:35:57 +0800 Subject: [PATCH 1004/2380] docker: Update fedora image to 28 Signed-off-by: Fam Zheng Message-Id: <20180601023557.9770-1-famz@redhat.com> --- tests/docker/dockerfiles/fedora.docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index b706f42405..65d7761cf5 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,4 +1,4 @@ -FROM fedora:27 +FROM fedora:28 ENV PACKAGES \ ccache gettext git tar PyYAML sparse flex bison python3 bzip2 hostname \ gcc gcc-c++ llvm clang make perl which bc findutils glib2-devel \ From bc820db0647040be38e693e0ca14fd49d8cf51ed Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 25 May 2018 06:53:44 +0200 Subject: [PATCH 1005/2380] bochs-display: add missing break MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: CID 1391291 Signed-off-by: Gerd Hoffmann Reviewed-by: Ján Tomko Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180525045344.28347-1-kraxel@redhat.com --- hw/display/bochs-display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index c33524b558..1187d77576 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -158,6 +158,7 @@ static int bochs_display_get_mode(BochsDisplayState *s, /* best effort: support native endianess only */ mode->format = PIXMAN_r5g6b5; mode->bytepp = 2; + break; case 32: mode->format = s->big_endian_fb ? PIXMAN_BE_x8r8g8b8 From 6bc2fd57e1fc2d1957d1ff952793c53764130218 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 25 May 2018 15:13:18 +0200 Subject: [PATCH 1006/2380] vga: cleanup surface handling Just set the full_update flag if we need a new DisplaySurface. Create a new surface when the flag is set instead of having two places where qemu_create_displaysurface_from() is called. Signed-off-by: Gerd Hoffmann Message-id: 20180525131318.28437-1-kraxel@redhat.com --- hw/display/vga.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/hw/display/vga.c b/hw/display/vga.c index a7794f6d1f..ed476e4e80 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1548,12 +1548,31 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } else { share_surface = false; } + if (s->line_offset != s->last_line_offset || disp_width != s->last_width || height != s->last_height || s->last_depth != depth || s->last_byteswap != byteswap || share_surface != is_buffer_shared(surface)) { + /* display parameters changed -> need new display surface */ + s->last_scr_width = disp_width; + s->last_scr_height = height; + s->last_width = disp_width; + s->last_height = height; + s->last_line_offset = s->line_offset; + s->last_depth = depth; + s->last_byteswap = byteswap; + full_update = 1; + } + if (surface_data(surface) != s->vram_ptr + (s->start_addr * 4) + && is_buffer_shared(surface)) { + /* base address changed (page flip) -> shared display surfaces + * must be updated with the new base address */ + full_update = 1; + } + + if (full_update) { if (share_surface) { surface = qemu_create_displaysurface_from(disp_width, height, format, s->line_offset, @@ -1563,23 +1582,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) qemu_console_resize(s->con, disp_width, height); surface = qemu_console_surface(s->con); } - s->last_scr_width = disp_width; - s->last_scr_height = height; - s->last_width = disp_width; - s->last_height = height; - s->last_line_offset = s->line_offset; - s->last_depth = depth; - s->last_byteswap = byteswap; - full_update = 1; - } else if (is_buffer_shared(surface) && - (full_update || surface_data(surface) != s->vram_ptr - + (s->start_addr * 4))) { - pixman_format_code_t format = - qemu_default_pixman_format(depth, !byteswap); - surface = qemu_create_displaysurface_from(disp_width, - height, format, s->line_offset, - s->vram_ptr + (s->start_addr * 4)); - dpy_gfx_replace_surface(s->con, surface); } if (shift_control == 0) { From 03b036cc0ca3362ff1aacd70315f5e53f4234f5c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 23 May 2018 18:07:20 +0200 Subject: [PATCH 1007/2380] sheepdog: cleanup repeated expression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The expression "SD_INODE_SIZE - sizeof(inode.data_vdi_id)" already has a macro defined for the same value (though with a nicer definition using offsetof). Replace it. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Fam Zheng Reviewed-by: Jeff Cody Signed-off-by: Paolo Bonzini Message-Id: <20180523160721.14018-2-pbonzini@redhat.com> Signed-off-by: Jeff Cody --- block/sheepdog.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/block/sheepdog.c b/block/sheepdog.c index 2a5bc0a59a..cfc0e28aa5 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -2335,7 +2335,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset, } /* we don't need to update entire object */ - datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); + datalen = SD_INODE_HEADER_SIZE; s->inode.vdi_size = offset; ret = write_object(fd, s->bs, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies, @@ -2703,7 +2703,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) */ strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag)); /* we don't need to update entire object */ - datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); + datalen = SD_INODE_HEADER_SIZE; inode = g_malloc(datalen); /* refresh inode. */ @@ -2989,7 +2989,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) /* we don't need to read entire object */ ret = read_object(fd, s->bs, (char *)&inode, vid_to_vdi_oid(vid), - 0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0, + 0, SD_INODE_HEADER_SIZE, 0, s->cache_flags); if (ret) { From 68acc99f143b60ec4faa2903065b187d4d3c4bf3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 23 May 2018 18:07:21 +0200 Subject: [PATCH 1008/2380] sheepdog: remove huge BSS object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit block/sheepdog.o has a 4M static variable that is 90% of QEMU's whole .bss section. Replace it with a heap-allocated block, and make it smaller too since only the inode header is actually being used. bss size goes down from 4464280 to 269976. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Jeff Cody Signed-off-by: Paolo Bonzini Message-Id: <20180523160721.14018-3-pbonzini@redhat.com> Signed-off-by: Jeff Cody --- block/sheepdog.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/block/sheepdog.c b/block/sheepdog.c index cfc0e28aa5..7b98725af7 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -2938,13 +2938,14 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) QEMUSnapshotInfo *sn_tab = NULL; unsigned wlen, rlen; int found = 0; - static SheepdogInode inode; + SheepdogInode *inode; unsigned long *vdi_inuse; unsigned int start_nr; uint64_t hval; uint32_t vid; vdi_inuse = g_malloc(max); + inode = g_malloc(SD_INODE_HEADER_SIZE); fd = connect_to_sdog(s, &local_err); if (fd < 0) { @@ -2987,7 +2988,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) } /* we don't need to read entire object */ - ret = read_object(fd, s->bs, (char *)&inode, + ret = read_object(fd, s->bs, (char *)inode, vid_to_vdi_oid(vid), 0, SD_INODE_HEADER_SIZE, 0, s->cache_flags); @@ -2996,17 +2997,17 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) continue; } - if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) { - sn_tab[found].date_sec = inode.snap_ctime >> 32; - sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff; - sn_tab[found].vm_state_size = inode.vm_state_size; - sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec; + if (!strcmp(inode->name, s->name) && is_snapshot(inode)) { + sn_tab[found].date_sec = inode->snap_ctime >> 32; + sn_tab[found].date_nsec = inode->snap_ctime & 0xffffffff; + sn_tab[found].vm_state_size = inode->vm_state_size; + sn_tab[found].vm_clock_nsec = inode->vm_clock_nsec; snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), - "%" PRIu32, inode.snap_id); + "%" PRIu32, inode->snap_id); pstrcpy(sn_tab[found].name, - MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)), - inode.tag); + MIN(sizeof(sn_tab[found].name), sizeof(inode->tag)), + inode->tag); found++; } } @@ -3016,6 +3017,7 @@ out: *psn_tab = sn_tab; g_free(vdi_inuse); + g_free(inode); if (ret < 0) { return ret; From f88b44f9eb2fbba6540db782b5eaa7efbcb0e266 Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Tue, 5 Jun 2018 08:23:16 -0600 Subject: [PATCH 1009/2380] vfio: remove DPRINTF() definition from vfio-common.h This macro isn't used by any VFIO code. And its name is too generic. The vfio-common.h (in include/hw/vfio) can be included by other modules in QEMU. It can introduce conflicts. Signed-off-by: Tiwei Bie Reviewed-by: Michael S. Tsirkin Reviewed-by: Eric Auger Signed-off-by: Alex Williamson --- include/hw/vfio/vfio-common.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 8264a65fa5..a9036929b2 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -33,15 +33,6 @@ #define ERR_PREFIX "vfio error: %s: " #define WARN_PREFIX "vfio warning: %s: " -/*#define DEBUG_VFIO*/ -#ifdef DEBUG_VFIO -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "vfio: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - enum { VFIO_DEVICE_TYPE_PCI = 0, VFIO_DEVICE_TYPE_PLATFORM = 1, From bcf3c3d029e73d54455e1d7a51177c37d668378c Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 5 Jun 2018 08:23:16 -0600 Subject: [PATCH 1010/2380] vfio/quirks: Add common quirk alloc helper This will later be used to include list initialization. Reviewed-by: Eric Auger Reviewed-by: Peter Xu Signed-off-by: Alex Williamson --- hw/vfio/pci-quirks.c | 48 +++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index e5779a7ad3..cc3a74ed99 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -275,6 +275,15 @@ static const MemoryRegionOps vfio_ati_3c3_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static VFIOQuirk *vfio_quirk_alloc(int nr_mem) +{ + VFIOQuirk *quirk = g_new0(VFIOQuirk, 1); + quirk->mem = g_new0(MemoryRegion, nr_mem); + quirk->nr_mem = nr_mem; + + return quirk; +} + static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev) { VFIOQuirk *quirk; @@ -288,9 +297,7 @@ static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev) return; } - quirk = g_malloc0(sizeof(*quirk)); - quirk->mem = g_new0(MemoryRegion, 1); - quirk->nr_mem = 1; + quirk = vfio_quirk_alloc(1); memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, vdev, "vfio-ati-3c3-quirk", 1); @@ -323,9 +330,7 @@ static void vfio_probe_ati_bar4_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = g_malloc0(sizeof(*quirk)); - quirk->mem = g_new0(MemoryRegion, 2); - quirk->nr_mem = 2; + quirk = vfio_quirk_alloc(2); window = quirk->data = g_malloc0(sizeof(*window) + sizeof(VFIOConfigWindowMatch)); window->vdev = vdev; @@ -371,10 +376,9 @@ static void vfio_probe_ati_bar2_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = g_malloc0(sizeof(*quirk)); + quirk = vfio_quirk_alloc(1); mirror = quirk->data = g_malloc0(sizeof(*mirror)); - mirror->mem = quirk->mem = g_new0(MemoryRegion, 1); - quirk->nr_mem = 1; + mirror->mem = quirk->mem; mirror->vdev = vdev; mirror->offset = 0x4000; mirror->bar = nr; @@ -548,10 +552,8 @@ static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev) return; } - quirk = g_malloc0(sizeof(*quirk)); + quirk = vfio_quirk_alloc(2); quirk->data = data = g_malloc0(sizeof(*data)); - quirk->mem = g_new0(MemoryRegion, 2); - quirk->nr_mem = 2; data->vdev = vdev; memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_nvidia_3d4_quirk, @@ -667,9 +669,7 @@ static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = g_malloc0(sizeof(*quirk)); - quirk->mem = g_new0(MemoryRegion, 4); - quirk->nr_mem = 4; + quirk = vfio_quirk_alloc(4); bar5 = quirk->data = g_malloc0(sizeof(*bar5) + (sizeof(VFIOConfigWindowMatch) * 2)); window = &bar5->window; @@ -762,10 +762,9 @@ static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = g_malloc0(sizeof(*quirk)); + quirk = vfio_quirk_alloc(1); mirror = quirk->data = g_malloc0(sizeof(*mirror)); - mirror->mem = quirk->mem = g_new0(MemoryRegion, 1); - quirk->nr_mem = 1; + mirror->mem = quirk->mem; mirror->vdev = vdev; mirror->offset = 0x88000; mirror->bar = nr; @@ -781,10 +780,9 @@ static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr) /* The 0x1800 offset mirror only seems to get used by legacy VGA */ if (vdev->vga) { - quirk = g_malloc0(sizeof(*quirk)); + quirk = vfio_quirk_alloc(1); mirror = quirk->data = g_malloc0(sizeof(*mirror)); - mirror->mem = quirk->mem = g_new0(MemoryRegion, 1); - quirk->nr_mem = 1; + mirror->mem = quirk->mem; mirror->vdev = vdev; mirror->offset = 0x1800; mirror->bar = nr; @@ -945,9 +943,7 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = g_malloc0(sizeof(*quirk)); - quirk->mem = g_new0(MemoryRegion, 2); - quirk->nr_mem = 2; + quirk = vfio_quirk_alloc(2); quirk->data = rtl = g_malloc0(sizeof(*rtl)); rtl->vdev = vdev; @@ -1507,9 +1503,7 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) } /* Setup our quirk to munge GTT addresses to the VM allocated buffer */ - quirk = g_malloc0(sizeof(*quirk)); - quirk->mem = g_new0(MemoryRegion, 2); - quirk->nr_mem = 2; + quirk = vfio_quirk_alloc(2); igd = quirk->data = g_malloc0(sizeof(*igd)); igd->vdev = vdev; igd->index = ~0; From 469d02de993817dcf4430d08fdff92aef8352d8f Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 5 Jun 2018 08:23:17 -0600 Subject: [PATCH 1011/2380] vfio/quirks: Add quirk reset callback Quirks can be self modifying, provide a hook to allow them to cleanup on device reset if desired. Reviewed-by: Eric Auger Reviewed-by: Peter Xu Signed-off-by: Alex Williamson --- hw/vfio/pci-quirks.c | 15 +++++++++++++++ hw/vfio/pci.c | 2 ++ hw/vfio/pci.h | 2 ++ 3 files changed, 19 insertions(+) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index cc3a74ed99..f0947cbf15 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1694,6 +1694,21 @@ void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr) /* * Reset quirks */ +void vfio_quirk_reset(VFIOPCIDevice *vdev) +{ + int i; + + for (i = 0; i < PCI_ROM_SLOT; i++) { + VFIOQuirk *quirk; + VFIOBAR *bar = &vdev->bars[i]; + + QLIST_FOREACH(quirk, &bar->quirks, next) { + if (quirk->reset) { + quirk->reset(vdev, quirk); + } + } + } +} /* * AMD Radeon PCI config reset, based on Linux: diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 4947fe39a2..65446fb428 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2207,6 +2207,8 @@ static void vfio_pci_post_reset(VFIOPCIDevice *vdev) vdev->vbasedev.name, nr); } } + + vfio_quirk_reset(vdev); } static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name) diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 59ab7757a3..594a5bd005 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -29,6 +29,7 @@ typedef struct VFIOQuirk { void *data; int nr_mem; MemoryRegion *mem; + void (*reset)(struct VFIOPCIDevice *vdev, struct VFIOQuirk *quirk); } VFIOQuirk; typedef struct VFIOBAR { @@ -167,6 +168,7 @@ void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr); void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr); void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev); int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); +void vfio_quirk_reset(VFIOPCIDevice *vdev); extern const PropertyInfo qdev_prop_nv_gpudirect_clique; From c958c51d2e9923d0eb14dfec46920f69bd793cb4 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 5 Jun 2018 08:23:17 -0600 Subject: [PATCH 1012/2380] vfio/quirks: ioeventfd quirk acceleration The NVIDIA BAR0 quirks virtualize the PCI config space mirrors found in device MMIO space. Normally PCI config space is considered a slow path and further optimization is unnecessary, however NVIDIA uses a register here to enable the MSI interrupt to re-trigger. Exiting to QEMU for this MSI-ACK handling can therefore rate limit our interrupt handling. Fortunately the MSI-ACK write is easily detected since the quirk MemoryRegion otherwise has very few accesses, so simply looking for consecutive writes with the same data is sufficient, in this case 10 consecutive writes with the same data and size is arbitrarily chosen. We configure the KVM ioeventfd with data match, so there's no risk of triggering for the wrong data or size, but we do risk that pathological driver behavior might consume all of QEMU's file descriptors, so we cap ourselves to 10 ioeventfds for this purpose. In support of the above, generic ioeventfd infrastructure is added for vfio quirks. This automatically initializes an ioeventfd list per quirk, disables and frees ioeventfds on exit, and allows ioeventfds marked as dynamic to be dropped on device reset. The rationale for this latter feature is that useful ioeventfds may depend on specific driver behavior and since we necessarily place a cap on our use of ioeventfds, a machine reset is a reasonable point at which to assume a new driver and re-profile. Reviewed-by: Peter Xu Reviewed-by: Eric Auger Signed-off-by: Alex Williamson --- hw/vfio/pci-quirks.c | 166 ++++++++++++++++++++++++++++++++++++++++++- hw/vfio/pci.c | 2 + hw/vfio/pci.h | 14 ++++ hw/vfio/trace-events | 3 + 4 files changed, 183 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index f0947cbf15..f788648774 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qemu/main-loop.h" #include "qemu/range.h" #include "qapi/error.h" #include "qapi/visitor.h" @@ -202,6 +203,7 @@ typedef struct VFIOConfigMirrorQuirk { uint32_t offset; uint8_t bar; MemoryRegion *mem; + uint8_t data[]; } VFIOConfigMirrorQuirk; static uint64_t vfio_generic_quirk_mirror_read(void *opaque, @@ -278,12 +280,95 @@ static const MemoryRegionOps vfio_ati_3c3_quirk = { static VFIOQuirk *vfio_quirk_alloc(int nr_mem) { VFIOQuirk *quirk = g_new0(VFIOQuirk, 1); + QLIST_INIT(&quirk->ioeventfds); quirk->mem = g_new0(MemoryRegion, nr_mem); quirk->nr_mem = nr_mem; return quirk; } +static void vfio_ioeventfd_exit(VFIOIOEventFD *ioeventfd) +{ + QLIST_REMOVE(ioeventfd, next); + memory_region_del_eventfd(ioeventfd->mr, ioeventfd->addr, ioeventfd->size, + true, ioeventfd->data, &ioeventfd->e); + qemu_set_fd_handler(event_notifier_get_fd(&ioeventfd->e), NULL, NULL, NULL); + event_notifier_cleanup(&ioeventfd->e); + trace_vfio_ioeventfd_exit(memory_region_name(ioeventfd->mr), + (uint64_t)ioeventfd->addr, ioeventfd->size, + ioeventfd->data); + g_free(ioeventfd); +} + +static void vfio_drop_dynamic_eventfds(VFIOPCIDevice *vdev, VFIOQuirk *quirk) +{ + VFIOIOEventFD *ioeventfd, *tmp; + + QLIST_FOREACH_SAFE(ioeventfd, &quirk->ioeventfds, next, tmp) { + if (ioeventfd->dynamic) { + vfio_ioeventfd_exit(ioeventfd); + } + } +} + +static void vfio_ioeventfd_handler(void *opaque) +{ + VFIOIOEventFD *ioeventfd = opaque; + + if (event_notifier_test_and_clear(&ioeventfd->e)) { + vfio_region_write(ioeventfd->region, ioeventfd->region_addr, + ioeventfd->data, ioeventfd->size); + trace_vfio_ioeventfd_handler(memory_region_name(ioeventfd->mr), + (uint64_t)ioeventfd->addr, ioeventfd->size, + ioeventfd->data); + } +} + +static VFIOIOEventFD *vfio_ioeventfd_init(VFIOPCIDevice *vdev, + MemoryRegion *mr, hwaddr addr, + unsigned size, uint64_t data, + VFIORegion *region, + hwaddr region_addr, bool dynamic) +{ + VFIOIOEventFD *ioeventfd; + + if (vdev->no_kvm_ioeventfd) { + return NULL; + } + + ioeventfd = g_malloc0(sizeof(*ioeventfd)); + + if (event_notifier_init(&ioeventfd->e, 0)) { + g_free(ioeventfd); + return NULL; + } + + /* + * MemoryRegion and relative offset, plus additional ioeventfd setup + * parameters for configuring and later tearing down KVM ioeventfd. + */ + ioeventfd->mr = mr; + ioeventfd->addr = addr; + ioeventfd->size = size; + ioeventfd->data = data; + ioeventfd->dynamic = dynamic; + /* + * VFIORegion and relative offset for implementing the userspace + * handler. data & size fields shared for both uses. + */ + ioeventfd->region = region; + ioeventfd->region_addr = region_addr; + + qemu_set_fd_handler(event_notifier_get_fd(&ioeventfd->e), + vfio_ioeventfd_handler, NULL, ioeventfd); + memory_region_add_eventfd(ioeventfd->mr, ioeventfd->addr, ioeventfd->size, + true, ioeventfd->data, &ioeventfd->e); + trace_vfio_ioeventfd_init(memory_region_name(mr), (uint64_t)addr, + size, data); + + return ioeventfd; +} + static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev) { VFIOQuirk *quirk; @@ -719,6 +804,18 @@ static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr) trace_vfio_quirk_nvidia_bar5_probe(vdev->vbasedev.name); } +typedef struct LastDataSet { + VFIOQuirk *quirk; + hwaddr addr; + uint64_t data; + unsigned size; + int hits; + int added; +} LastDataSet; + +#define MAX_DYN_IOEVENTFD 10 +#define HITS_FOR_IOEVENTFD 10 + /* * Finally, BAR0 itself. We want to redirect any accesses to either * 0x1800 or 0x88000 through the PCI config space access functions. @@ -729,6 +826,7 @@ static void vfio_nvidia_quirk_mirror_write(void *opaque, hwaddr addr, VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; PCIDevice *pdev = &vdev->pdev; + LastDataSet *last = (LastDataSet *)&mirror->data; vfio_generic_quirk_mirror_write(opaque, addr, data, size); @@ -743,6 +841,49 @@ static void vfio_nvidia_quirk_mirror_write(void *opaque, hwaddr addr, addr + mirror->offset, data, size); trace_vfio_quirk_nvidia_bar0_msi_ack(vdev->vbasedev.name); } + + /* + * Automatically add an ioeventfd to handle any repeated write with the + * same data and size above the standard PCI config space header. This is + * primarily expected to accelerate the MSI-ACK behavior, such as noted + * above. Current hardware/drivers should trigger an ioeventfd at config + * offset 0x704 (region offset 0x88704), with data 0x0, size 4. + * + * The criteria of 10 successive hits is arbitrary but reliably adds the + * MSI-ACK region. Note that as some writes are bypassed via the ioeventfd, + * the remaining ones have a greater chance of being seen successively. + * To avoid the pathological case of burning up all of QEMU's open file + * handles, arbitrarily limit this algorithm from adding no more than 10 + * ioeventfds, print an error if we would have added an 11th, and then + * stop counting. + */ + if (!vdev->no_kvm_ioeventfd && + addr >= PCI_STD_HEADER_SIZEOF && last->added <= MAX_DYN_IOEVENTFD) { + if (addr != last->addr || data != last->data || size != last->size) { + last->addr = addr; + last->data = data; + last->size = size; + last->hits = 1; + } else if (++last->hits >= HITS_FOR_IOEVENTFD) { + if (last->added < MAX_DYN_IOEVENTFD) { + VFIOIOEventFD *ioeventfd; + ioeventfd = vfio_ioeventfd_init(vdev, mirror->mem, addr, size, + data, &vdev->bars[mirror->bar].region, + mirror->offset + addr, true); + if (ioeventfd) { + VFIOQuirk *quirk = last->quirk; + + QLIST_INSERT_HEAD(&quirk->ioeventfds, ioeventfd, next); + last->added++; + } + } else { + last->added++; + warn_report("NVIDIA ioeventfd queue full for %s, unable to " + "accelerate 0x%"HWADDR_PRIx", data 0x%"PRIx64", " + "size %u", vdev->vbasedev.name, addr, data, size); + } + } + } } static const MemoryRegionOps vfio_nvidia_mirror_quirk = { @@ -751,10 +892,21 @@ static const MemoryRegionOps vfio_nvidia_mirror_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void vfio_nvidia_bar0_quirk_reset(VFIOPCIDevice *vdev, VFIOQuirk *quirk) +{ + VFIOConfigMirrorQuirk *mirror = quirk->data; + LastDataSet *last = (LastDataSet *)&mirror->data; + + last->addr = last->data = last->size = last->hits = last->added = 0; + + vfio_drop_dynamic_eventfds(vdev, quirk); +} + static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr) { VFIOQuirk *quirk; VFIOConfigMirrorQuirk *mirror; + LastDataSet *last; if (vdev->no_geforce_quirks || !vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) || @@ -763,11 +915,14 @@ static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr) } quirk = vfio_quirk_alloc(1); - mirror = quirk->data = g_malloc0(sizeof(*mirror)); + quirk->reset = vfio_nvidia_bar0_quirk_reset; + mirror = quirk->data = g_malloc0(sizeof(*mirror) + sizeof(LastDataSet)); mirror->mem = quirk->mem; mirror->vdev = vdev; mirror->offset = 0x88000; mirror->bar = nr; + last = (LastDataSet *)&mirror->data; + last->quirk = quirk; memory_region_init_io(mirror->mem, OBJECT(vdev), &vfio_nvidia_mirror_quirk, mirror, @@ -781,11 +936,14 @@ static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr) /* The 0x1800 offset mirror only seems to get used by legacy VGA */ if (vdev->vga) { quirk = vfio_quirk_alloc(1); - mirror = quirk->data = g_malloc0(sizeof(*mirror)); + quirk->reset = vfio_nvidia_bar0_quirk_reset; + mirror = quirk->data = g_malloc0(sizeof(*mirror) + sizeof(LastDataSet)); mirror->mem = quirk->mem; mirror->vdev = vdev; mirror->offset = 0x1800; mirror->bar = nr; + last = (LastDataSet *)&mirror->data; + last->quirk = quirk; memory_region_init_io(mirror->mem, OBJECT(vdev), &vfio_nvidia_mirror_quirk, mirror, @@ -1668,6 +1826,10 @@ void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr) int i; QLIST_FOREACH(quirk, &bar->quirks, next) { + while (!QLIST_EMPTY(&quirk->ioeventfds)) { + vfio_ioeventfd_exit(QLIST_FIRST(&quirk->ioeventfds)); + } + for (i = 0; i < quirk->nr_mem; i++) { memory_region_del_subregion(bar->region.mem, &quirk->mem[i]); } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 65446fb428..ba12395511 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3175,6 +3175,8 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_BOOL("x-no-kvm-msix", VFIOPCIDevice, no_kvm_msix, false), DEFINE_PROP_BOOL("x-no-geforce-quirks", VFIOPCIDevice, no_geforce_quirks, false), + DEFINE_PROP_BOOL("x-no-kvm-ioeventfd", VFIOPCIDevice, no_kvm_ioeventfd, + false), DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, vendor_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice, device_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 594a5bd005..a4ac583fbd 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -24,9 +24,22 @@ struct VFIOPCIDevice; +typedef struct VFIOIOEventFD { + QLIST_ENTRY(VFIOIOEventFD) next; + MemoryRegion *mr; + hwaddr addr; + unsigned size; + uint64_t data; + EventNotifier e; + VFIORegion *region; + hwaddr region_addr; + bool dynamic; /* Added runtime, removed on device reset */ +} VFIOIOEventFD; + typedef struct VFIOQuirk { QLIST_ENTRY(VFIOQuirk) next; void *data; + QLIST_HEAD(, VFIOIOEventFD) ioeventfds; int nr_mem; MemoryRegion *mem; void (*reset)(struct VFIOPCIDevice *vdev, struct VFIOQuirk *quirk); @@ -149,6 +162,7 @@ typedef struct VFIOPCIDevice { bool no_kvm_msi; bool no_kvm_msix; bool no_geforce_quirks; + bool no_kvm_ioeventfd; VFIODisplay *dpy; } VFIOPCIDevice; diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 20109cb758..f8f97d1ff9 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -77,6 +77,9 @@ vfio_quirk_ati_bonaire_reset_no_smc(const char *name) "%s" vfio_quirk_ati_bonaire_reset_timeout(const char *name) "%s" vfio_quirk_ati_bonaire_reset_done(const char *name) "%s" vfio_quirk_ati_bonaire_reset(const char *name) "%s" +vfio_ioeventfd_exit(const char *name, uint64_t addr, unsigned size, uint64_t data) "%s+0x%"PRIx64"[%d]:0x%"PRIx64 +vfio_ioeventfd_handler(const char *name, uint64_t addr, unsigned size, uint64_t data) "%s+0x%"PRIx64"[%d] -> 0x%"PRIx64 +vfio_ioeventfd_init(const char *name, uint64_t addr, unsigned size, uint64_t data) "%s+0x%"PRIx64"[%d]:0x%"PRIx64 vfio_pci_igd_bar4_write(const char *name, uint32_t index, uint32_t data, uint32_t base) "%s [0x%03x] 0x%08x -> 0x%08x" vfio_pci_igd_bdsm_enabled(const char *name, int size) "%s %dMB" vfio_pci_igd_opregion_enabled(const char *name) "%s" From 2b1dbd0d7250254e9421bce721cc2ea25b4af894 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 5 Jun 2018 08:23:17 -0600 Subject: [PATCH 1013/2380] vfio/quirks: Enable ioeventfd quirks to be handled by vfio directly With vfio ioeventfd support, we can program vfio-pci to perform a specified BAR write when an eventfd is triggered. This allows the KVM ioeventfd to be wired directly to vfio-pci, entirely avoiding userspace handling for these events. On the same micro-benchmark where the ioeventfd got us to almost 90% of performance versus disabling the GeForce quirks, this gets us to within 95%. Reviewed-by: Peter Xu Reviewed-by: Eric Auger Signed-off-by: Alex Williamson --- hw/vfio/pci-quirks.c | 53 ++++++++++++++++++++++++++++++++++++++------ hw/vfio/pci.c | 2 ++ hw/vfio/pci.h | 2 ++ hw/vfio/trace-events | 2 +- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index f788648774..061259b86b 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -16,6 +16,7 @@ #include "qemu/range.h" #include "qapi/error.h" #include "qapi/visitor.h" +#include #include "hw/nvram/fw_cfg.h" #include "pci.h" #include "trace.h" @@ -287,12 +288,33 @@ static VFIOQuirk *vfio_quirk_alloc(int nr_mem) return quirk; } -static void vfio_ioeventfd_exit(VFIOIOEventFD *ioeventfd) +static void vfio_ioeventfd_exit(VFIOPCIDevice *vdev, VFIOIOEventFD *ioeventfd) { QLIST_REMOVE(ioeventfd, next); memory_region_del_eventfd(ioeventfd->mr, ioeventfd->addr, ioeventfd->size, true, ioeventfd->data, &ioeventfd->e); - qemu_set_fd_handler(event_notifier_get_fd(&ioeventfd->e), NULL, NULL, NULL); + + if (ioeventfd->vfio) { + struct vfio_device_ioeventfd vfio_ioeventfd; + + vfio_ioeventfd.argsz = sizeof(vfio_ioeventfd); + vfio_ioeventfd.flags = ioeventfd->size; + vfio_ioeventfd.data = ioeventfd->data; + vfio_ioeventfd.offset = ioeventfd->region->fd_offset + + ioeventfd->region_addr; + vfio_ioeventfd.fd = -1; + + if (ioctl(vdev->vbasedev.fd, VFIO_DEVICE_IOEVENTFD, &vfio_ioeventfd)) { + error_report("Failed to remove vfio ioeventfd for %s+0x%" + HWADDR_PRIx"[%d]:0x%"PRIx64" (%m)", + memory_region_name(ioeventfd->mr), ioeventfd->addr, + ioeventfd->size, ioeventfd->data); + } + } else { + qemu_set_fd_handler(event_notifier_get_fd(&ioeventfd->e), + NULL, NULL, NULL); + } + event_notifier_cleanup(&ioeventfd->e); trace_vfio_ioeventfd_exit(memory_region_name(ioeventfd->mr), (uint64_t)ioeventfd->addr, ioeventfd->size, @@ -306,7 +328,7 @@ static void vfio_drop_dynamic_eventfds(VFIOPCIDevice *vdev, VFIOQuirk *quirk) QLIST_FOREACH_SAFE(ioeventfd, &quirk->ioeventfds, next, tmp) { if (ioeventfd->dynamic) { - vfio_ioeventfd_exit(ioeventfd); + vfio_ioeventfd_exit(vdev, ioeventfd); } } } @@ -359,12 +381,29 @@ static VFIOIOEventFD *vfio_ioeventfd_init(VFIOPCIDevice *vdev, ioeventfd->region = region; ioeventfd->region_addr = region_addr; - qemu_set_fd_handler(event_notifier_get_fd(&ioeventfd->e), - vfio_ioeventfd_handler, NULL, ioeventfd); + if (!vdev->no_vfio_ioeventfd) { + struct vfio_device_ioeventfd vfio_ioeventfd; + + vfio_ioeventfd.argsz = sizeof(vfio_ioeventfd); + vfio_ioeventfd.flags = ioeventfd->size; + vfio_ioeventfd.data = ioeventfd->data; + vfio_ioeventfd.offset = ioeventfd->region->fd_offset + + ioeventfd->region_addr; + vfio_ioeventfd.fd = event_notifier_get_fd(&ioeventfd->e); + + ioeventfd->vfio = !ioctl(vdev->vbasedev.fd, + VFIO_DEVICE_IOEVENTFD, &vfio_ioeventfd); + } + + if (!ioeventfd->vfio) { + qemu_set_fd_handler(event_notifier_get_fd(&ioeventfd->e), + vfio_ioeventfd_handler, NULL, ioeventfd); + } + memory_region_add_eventfd(ioeventfd->mr, ioeventfd->addr, ioeventfd->size, true, ioeventfd->data, &ioeventfd->e); trace_vfio_ioeventfd_init(memory_region_name(mr), (uint64_t)addr, - size, data); + size, data, ioeventfd->vfio); return ioeventfd; } @@ -1827,7 +1866,7 @@ void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr) QLIST_FOREACH(quirk, &bar->quirks, next) { while (!QLIST_EMPTY(&quirk->ioeventfds)) { - vfio_ioeventfd_exit(QLIST_FIRST(&quirk->ioeventfds)); + vfio_ioeventfd_exit(vdev, QLIST_FIRST(&quirk->ioeventfds)); } for (i = 0; i < quirk->nr_mem; i++) { diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index ba12395511..84e27c7bb2 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3177,6 +3177,8 @@ static Property vfio_pci_dev_properties[] = { no_geforce_quirks, false), DEFINE_PROP_BOOL("x-no-kvm-ioeventfd", VFIOPCIDevice, no_kvm_ioeventfd, false), + DEFINE_PROP_BOOL("x-no-vfio-ioeventfd", VFIOPCIDevice, no_vfio_ioeventfd, + false), DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, vendor_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice, device_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index a4ac583fbd..52b065421a 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -34,6 +34,7 @@ typedef struct VFIOIOEventFD { VFIORegion *region; hwaddr region_addr; bool dynamic; /* Added runtime, removed on device reset */ + bool vfio; } VFIOIOEventFD; typedef struct VFIOQuirk { @@ -163,6 +164,7 @@ typedef struct VFIOPCIDevice { bool no_kvm_msix; bool no_geforce_quirks; bool no_kvm_ioeventfd; + bool no_vfio_ioeventfd; VFIODisplay *dpy; } VFIOPCIDevice; diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index f8f97d1ff9..d2a74952e3 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -79,7 +79,7 @@ vfio_quirk_ati_bonaire_reset_done(const char *name) "%s" vfio_quirk_ati_bonaire_reset(const char *name) "%s" vfio_ioeventfd_exit(const char *name, uint64_t addr, unsigned size, uint64_t data) "%s+0x%"PRIx64"[%d]:0x%"PRIx64 vfio_ioeventfd_handler(const char *name, uint64_t addr, unsigned size, uint64_t data) "%s+0x%"PRIx64"[%d] -> 0x%"PRIx64 -vfio_ioeventfd_init(const char *name, uint64_t addr, unsigned size, uint64_t data) "%s+0x%"PRIx64"[%d]:0x%"PRIx64 +vfio_ioeventfd_init(const char *name, uint64_t addr, unsigned size, uint64_t data, bool vfio) "%s+0x%"PRIx64"[%d]:0x%"PRIx64" vfio:%d" vfio_pci_igd_bar4_write(const char *name, uint32_t index, uint32_t data, uint32_t base) "%s [0x%03x] 0x%08x -> 0x%08x" vfio_pci_igd_bdsm_enabled(const char *name, int size) "%s %dMB" vfio_pci_igd_opregion_enabled(const char *name) "%s" From 8151a9c56d31eeeea872b8103c8b86d03c411667 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 5 Jun 2018 08:23:18 -0600 Subject: [PATCH 1014/2380] vfio/pci: Default display option to "off" Commit a9994687cb9b ("vfio/display: core & wireup") added display support to vfio-pci with the default being "auto", which breaks existing VMs when the vGPU requires GL support but had no previous requirement for a GL compatible configuration. "Off" is the safer default as we impose no new requirements to VM configurations. Fixes: a9994687cb9b ("vfio/display: core & wireup") Cc: qemu-stable@nongnu.org Cc: Gerd Hoffmann Signed-off-by: Alex Williamson --- hw/vfio/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 84e27c7bb2..18c493b49e 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3160,7 +3160,7 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host), DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev), DEFINE_PROP_ON_OFF_AUTO("display", VFIOPCIDevice, - display, ON_OFF_AUTO_AUTO), + display, ON_OFF_AUTO_OFF), DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIOPCIDevice, intx.mmap_timeout, 1100), DEFINE_PROP_BIT("x-vga", VFIOPCIDevice, features, From 15df9d3783d80f64be3149b9120b6a086bdc210a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 10 May 2018 10:45:55 +0100 Subject: [PATCH 1015/2380] docker: add "probe" command for configure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a helper function for the configure script. It replies yes, sudo or no to inform the user if non-interactive docker support is available. We trap the Exception to fail gracefully. Signed-off-by: Alex Bennée Reviewed-by: Fam Zheng --- tests/docker/docker.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 1246ba9578..f8267586eb 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -390,6 +390,24 @@ class ImagesCommand(SubCommand): def run(self, args, argv): return Docker().command("images", argv, args.quiet) + +class ProbeCommand(SubCommand): + """Probe if we can run docker automatically""" + name = "probe" + + def run(self, args, argv): + try: + docker = Docker() + if docker._command[0] == "docker": + print "yes" + elif docker._command[0] == "sudo": + print "sudo" + except Exception: + print "no" + + return + + def main(): parser = argparse.ArgumentParser(description="A Docker helper", usage="%s ..." % os.path.basename(sys.argv[0])) From 51a12b51fd3693a16eb73362713a98d467e15af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 4 Apr 2018 14:24:39 +0100 Subject: [PATCH 1016/2380] configure: add test for docker availability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This tests for a working docker installation without sudo and sets up config-host.mak accordingly. This will be useful from cross compiling things in the future. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson --- configure | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/configure b/configure index ab810d728f..db8c9d8288 100755 --- a/configure +++ b/configure @@ -456,6 +456,7 @@ jemalloc="no" replication="yes" vxhs="" libxml2="" +docker="no" supported_cpu="no" supported_os="no" @@ -5450,6 +5451,17 @@ EOF fi fi +########################################## +# Docker and cross-compiler support +# +# This is specifically for building test +# cases for foreign architectures, not +# cross-compiling QEMU itself. + +if has "docker"; then + docker=$($python $source_path/tests/docker/docker.py probe) +fi + ########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -5913,6 +5925,7 @@ echo "avx2 optimization $avx2_opt" echo "replication support $replication" echo "VxHS block device $vxhs" echo "capstone $capstone" +echo "docker $docker" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -6740,6 +6753,10 @@ if test "$gcov" = "yes" ; then echo "GCOV=$gcov_tool" >> $config_host_mak fi +if test "$docker" != "no"; then + echo "HAVE_USER_DOCKER=y" >> $config_host_mak +fi + # use included Linux headers if test "$linux" = "yes" ; then mkdir -p linux-headers From 208ecb3e1acc8d55dab49fdf721a86d513691688 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 8 Sep 2017 17:16:54 +0800 Subject: [PATCH 1017/2380] Makefile: Rename TARGET_DIRS to TARGET_LIST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be more accurate on its purpose and make code that looks for a certain target out of this variable more readable. Signed-off-by: Fam Zheng Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée --- Makefile | 20 ++++++++++---------- configure | 2 +- scripts/create_config | 2 +- tests/Makefile.include | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 6d588d1f71..023b3437ec 100644 --- a/Makefile +++ b/Makefile @@ -62,8 +62,8 @@ seems to have been used for an in-tree build. You can fix this by running \ endif endif -CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_DIRS)),y) -CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_DIRS)),y) +CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_LIST)),y) +CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_LIST)),y) CONFIG_XEN := $(CONFIG_XEN_BACKEND) CONFIG_ALL=y -include config-all-devices.mak @@ -366,8 +366,8 @@ DOCS= endif SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR) -SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) -SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS)) +SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_LIST)) +SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_LIST)) ifeq ($(SUBDIR_DEVICES_MAK),) config-all-devices.mak: @@ -470,7 +470,7 @@ config-host.h-timestamp: config-host.mak qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@") -SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) +SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_LIST)) SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) @@ -514,7 +514,7 @@ ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) romsubdir-%: $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pc-bios/$* V="$(V)" TARGET_DIR="$*/" CFLAGS="$(filter -O% -g%,$(CFLAGS))",) -ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS)) +ALL_SUBDIRS=$(TARGET_LIST) $(patsubst %,pc-bios/%, $(ROMS)) recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES) @@ -772,7 +772,7 @@ distclean: clean rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html rm -f docs/qemu-block-drivers.7 - for d in $(TARGET_DIRS); do \ + for d in $(TARGET_LIST); do \ rm -rf $$d || exit 1 ; \ done rm -Rf .sdk @@ -873,7 +873,7 @@ endif $(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \ done $(INSTALL_DATA) $(BUILD_DIR)/trace-events-all "$(DESTDIR)$(qemu_datadir)/trace-events-all" - for d in $(TARGET_DIRS); do \ + for d in $(TARGET_LIST); do \ $(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \ done @@ -1071,9 +1071,9 @@ endif @echo ' ctags/TAGS - Generate tags file for editors' @echo ' cscope - Generate cscope index' @echo '' - @$(if $(TARGET_DIRS), \ + @$(if $(TARGET_LIST), \ echo 'Architecture specific targets:'; \ - $(foreach t, $(TARGET_DIRS), \ + $(foreach t, $(TARGET_LIST), \ printf " %-30s - Build for %s\\n" $(patsubst %,subdir-%,$(t)) $(t);) \ echo '') @echo 'Cleaning targets:' diff --git a/configure b/configure index db8c9d8288..14b11130a7 100755 --- a/configure +++ b/configure @@ -6128,7 +6128,7 @@ qemu_version=$(head $source_path/VERSION) echo "VERSION=$qemu_version" >>$config_host_mak echo "PKGVERSION=$pkgversion" >>$config_host_mak echo "SRC_PATH=$source_path" >> $config_host_mak -echo "TARGET_DIRS=$target_list" >> $config_host_mak +echo "TARGET_LIST=$target_list" >> $config_host_mak if [ "$docs" = "yes" ] ; then echo "BUILD_DOCS=yes" >> $config_host_mak fi diff --git a/scripts/create_config b/scripts/create_config index d727e5e36e..58948a67a4 100755 --- a/scripts/create_config +++ b/scripts/create_config @@ -107,7 +107,7 @@ case $line in target_name=${line#*=} echo "#define TARGET_NAME \"$target_name\"" ;; - TARGET_DIRS=*) + TARGET_LIST=*) # do nothing ;; TARGET_*=y) # configuration diff --git a/tests/Makefile.include b/tests/Makefile.include index 86f90c0cb0..9854e7794b 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -858,7 +858,7 @@ endif # QTest rules -TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS))) +TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_LIST))) ifeq ($(CONFIG_POSIX),y) QTEST_TARGETS = $(TARGETS) check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y)) From 8bce19d3a6abc35c8680e22e7b7687a6846d46cd Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 8 Sep 2017 17:16:56 +0800 Subject: [PATCH 1018/2380] docker: Add fedora-i386-cross image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has some basic *-devel.i686 packages to be used with "gcc -m32" as a 32 bit cross build environment. Signed-off-by: Fam Zheng [AJB: add glibc-static] Signed-off-by: Alex Bennée Tested-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/fedora-i386-cross.docker | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/docker/dockerfiles/fedora-i386-cross.docker diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker new file mode 100644 index 0000000000..8fbef2fa53 --- /dev/null +++ b/tests/docker/dockerfiles/fedora-i386-cross.docker @@ -0,0 +1,14 @@ +FROM fedora:latest +ENV PACKAGES \ + gcc \ + glibc-static.i686 \ + glibc-devel.i686 \ + glib2-devel.i686 \ + zlib-devel.i686 \ + glib2-devel.i686 \ + nettle-devel.i686 \ + pixman-devel.i686 \ + gnutls-devel.i686 + +RUN dnf install -y $PACKAGES +RUN rpm -q $PACKAGES | sort > /packages.txt From cabd35840749dc4ddbc866a21f635d20374af743 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 16 Mar 2018 13:25:39 +0100 Subject: [PATCH 1019/2380] docker: test-mingw: use SDL2 and GTK+3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not test the deprecated API versions. debian-win32-cross and debian-win64-cross are already using SDL2 (they do not cover GTK+ at all). Signed-off-by: Paolo Bonzini Reviewed-by: Daniel P. Berrangé [AJB: fix merge conflicts] Signed-off-by: Alex Bennée --- tests/docker/dockerfiles/fedora.docker | 8 ++++---- tests/docker/test-mingw | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 65d7761cf5..7d1d008002 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -11,12 +11,12 @@ ENV PACKAGES \ numactl-devel SDL2-devel snappy-devel spice-server-devel \ systemtap-sdt-devel usbredir-devel virglrenderer-devel vte3-devel \ xen-devel \ - mingw32-pixman mingw32-glib2 mingw32-gmp mingw32-SDL mingw32-pkg-config \ - mingw32-gtk2 mingw32-gtk3 mingw32-gnutls mingw32-nettle mingw32-libtasn1 \ + mingw32-pixman mingw32-glib2 mingw32-gmp mingw32-SDL2 mingw32-pkg-config \ + mingw32-gtk3 mingw32-gnutls mingw32-nettle mingw32-libtasn1 \ mingw32-libjpeg-turbo mingw32-libpng mingw32-curl mingw32-libssh2 \ mingw32-bzip2 \ - mingw64-pixman mingw64-glib2 mingw64-gmp mingw64-SDL mingw64-pkg-config \ - mingw64-gtk2 mingw64-gtk3 mingw64-gnutls mingw64-nettle mingw64-libtasn1 \ + mingw64-pixman mingw64-glib2 mingw64-gmp mingw64-SDL2 mingw64-pkg-config \ + mingw64-gtk3 mingw64-gnutls mingw64-nettle mingw64-libtasn1 \ mingw64-libjpeg-turbo mingw64-libpng mingw64-curl mingw64-libssh2 \ mingw64-bzip2 ENV QEMU_CONFIGURE_OPTS --python=/usr/bin/python3 diff --git a/tests/docker/test-mingw b/tests/docker/test-mingw index 503a6bc6f7..7cca7e16a6 100755 --- a/tests/docker/test-mingw +++ b/tests/docker/test-mingw @@ -28,8 +28,8 @@ for prefix in x86_64-w64-mingw32- i686-w64-mingw32-; do --enable-vnc \ --enable-bzip2 \ --enable-guest-agent \ - --with-sdlabi=1.2 \ - --with-gtkabi=2.0 + --with-sdlabi=2.0 \ + --with-gtkabi=3.0 install_qemu make clean From 36dc5fedab0339afdeb3c40ff820c7e5ce334751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 May 2018 21:21:43 -0300 Subject: [PATCH 1020/2380] docker: add debian/tricore image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TriCore binutils is built from Bastian Koppelmann repository. Note: There is no TriCore compiler in this image (only assembler/linker). Signed-off-by: Philippe Mathieu-Daudé [AJB: base of Debian9, add to Makefile.include] Signed-off-by: Alex Bennée --- tests/docker/Makefile.include | 3 +++ .../dockerfiles/debian-tricore-cross.docker | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/docker/dockerfiles/debian-tricore-cross.docker diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 9d5749887a..74fd51c22c 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -63,6 +63,9 @@ docker-image-debian-win32-cross: docker-image-debian8-mxe docker-image-debian-win64-cross: docker-image-debian8-mxe docker-image-travis: NOUSER=1 +# Specialist build images, sometimes very limited tools +docker-image-tricore-cross: docker-image-debian9 + # Expand all the pre-requistes for each docker image and test combination $(foreach i,$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES), \ $(foreach t,$(DOCKER_TESTS) $(DOCKER_TOOLS), \ diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker new file mode 100644 index 0000000000..898b8dd511 --- /dev/null +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -0,0 +1,23 @@ +# +# Docker TriCore cross-compiler target +# +# This docker target builds on the debian Stretch base image. +# +# Copyright (c) 2018 Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +FROM debian:9 + +MAINTAINER Philippe Mathieu-Daudé + +RUN git clone --single-branch \ + https://github.com/bkoppelmann/tricore-binutils.git \ + /usr/src/binutils && \ + cd /usr/src/binutils && chmod +x missing && \ + CFLAGS=-w ./configure --prefix=/usr --disable-nls --target=tricore && \ + make && make install && \ + rm -rf /usr/src/binutils + +# Specify the cross prefix for this image (see tests/docker/common.rc) +ENV QEMU_CONFIGURE_OPTS --cross-prefix=tricore- From b1e4b7c6b299b8aba1cef9a5ac7221d9eef4e949 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 30 May 2018 12:12:50 -0400 Subject: [PATCH 1021/2380] test: Move reusable code from tpm-crb-swtpm-test.c to tpm-util.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move code we can reuse from tpm-crb-swtpm-test.c into tpm-util.c and prefix functions with 'tpm_util_'. Remove some unnecessary #include's. Signed-off-by: Stefan Berger Reviewed-by: Marc-André Lureau --- tests/tpm-crb-swtpm-test.c | 100 ++----------------------------------- tests/tpm-util.c | 89 +++++++++++++++++++++++++++++++++ tests/tpm-util.h | 10 ++++ 3 files changed, 104 insertions(+), 95 deletions(-) diff --git a/tests/tpm-crb-swtpm-test.c b/tests/tpm-crb-swtpm-test.c index c2bde0cbaa..2a22b032ca 100644 --- a/tests/tpm-crb-swtpm-test.c +++ b/tests/tpm-crb-swtpm-test.c @@ -15,12 +15,8 @@ #include "qemu/osdep.h" #include -#include "hw/acpi/tpm.h" -#include "io/channel-socket.h" #include "libqtest.h" #include "tpm-util.h" -#include "sysemu/tpm.h" -#include "qapi/qmp/qdict.h" typedef struct TestState { char *src_tpm_path; @@ -28,93 +24,6 @@ typedef struct TestState { char *uri; } TestState; -bool got_stop; - -static void migrate(QTestState *who, const char *uri) -{ - QDict *rsp; - gchar *cmd; - - cmd = g_strdup_printf("{ 'execute': 'migrate'," - "'arguments': { 'uri': '%s' } }", - uri); - rsp = qtest_qmp(who, cmd); - g_free(cmd); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); -} - -/* - * Events can get in the way of responses we are actually waiting for. - */ -static QDict *wait_command(QTestState *who, const char *command) -{ - const char *event_string; - QDict *response; - - response = qtest_qmp(who, command); - - while (qdict_haskey(response, "event")) { - /* OK, it was an event */ - event_string = qdict_get_str(response, "event"); - if (!strcmp(event_string, "STOP")) { - got_stop = true; - } - qobject_unref(response); - response = qtest_qmp_receive(who); - } - return response; -} - -static void wait_for_migration_complete(QTestState *who) -{ - while (true) { - QDict *rsp, *rsp_return; - bool completed; - const char *status; - - rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); - rsp_return = qdict_get_qdict(rsp, "return"); - status = qdict_get_str(rsp_return, "status"); - completed = strcmp(status, "completed") == 0; - g_assert_cmpstr(status, !=, "failed"); - qobject_unref(rsp); - if (completed) { - return; - } - usleep(1000); - } -} - -static void migration_start_qemu(QTestState **src_qemu, QTestState **dst_qemu, - SocketAddress *src_tpm_addr, - SocketAddress *dst_tpm_addr, - const char *miguri) -{ - char *src_qemu_args, *dst_qemu_args; - - src_qemu_args = g_strdup_printf( - "-chardev socket,id=chr,path=%s " - "-tpmdev emulator,id=dev,chardev=chr " - "-device tpm-crb,tpmdev=dev ", - src_tpm_addr->u.q_unix.path); - - *src_qemu = qtest_init(src_qemu_args); - - dst_qemu_args = g_strdup_printf( - "-chardev socket,id=chr,path=%s " - "-tpmdev emulator,id=dev,chardev=chr " - "-device tpm-crb,tpmdev=dev " - "-incoming %s", - dst_tpm_addr->u.q_unix.path, - miguri); - - *dst_qemu = qtest_init(dst_qemu_args); - - free(src_qemu_args); - free(dst_qemu_args); -} - static void tpm_crb_swtpm_test(const void *data) { char *args = NULL; @@ -183,8 +92,9 @@ static void tpm_crb_swtpm_migration_test(const void *data) goto err_src_tpm_kill; } - migration_start_qemu(&src_qemu, &dst_qemu, src_tpm_addr, dst_tpm_addr, - ts->uri); + tpm_util_migration_start_qemu(&src_qemu, &dst_qemu, + src_tpm_addr, dst_tpm_addr, + ts->uri); tpm_util_startup(src_qemu, tpm_util_crb_transfer); tpm_util_pcrextend(src_qemu, tpm_util_crb_transfer); @@ -197,8 +107,8 @@ static void tpm_crb_swtpm_migration_test(const void *data) tpm_util_pcrread(src_qemu, tpm_util_crb_transfer, tpm_pcrread_resp, sizeof(tpm_pcrread_resp)); - migrate(src_qemu, ts->uri); - wait_for_migration_complete(src_qemu); + tpm_util_migrate(src_qemu, ts->uri); + tpm_util_wait_for_migration_complete(src_qemu); tpm_util_pcrread(dst_qemu, tpm_util_crb_transfer, tpm_pcrread_resp, sizeof(tpm_pcrread_resp)); diff --git a/tests/tpm-util.c b/tests/tpm-util.c index c9b3947c1c..e6e3b922fa 100644 --- a/tests/tpm-util.c +++ b/tests/tpm-util.c @@ -17,6 +17,9 @@ #include "hw/acpi/tpm.h" #include "libqtest.h" #include "tpm-util.h" +#include "qapi/qmp/qdict.h" + +static bool got_stop; void tpm_util_crb_transfer(QTestState *s, const unsigned char *req, size_t req_size, @@ -184,3 +187,89 @@ void tpm_util_swtpm_kill(GPid pid) kill(pid, SIGKILL); } + +void tpm_util_migrate(QTestState *who, const char *uri) +{ + QDict *rsp; + gchar *cmd; + + cmd = g_strdup_printf("{ 'execute': 'migrate'," + "'arguments': { 'uri': '%s' } }", + uri); + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +/* + * Events can get in the way of responses we are actually waiting for. + */ +static QDict *tpm_util_wait_command(QTestState *who, const char *command) +{ + const char *event_string; + QDict *response; + + response = qtest_qmp(who, command); + + while (qdict_haskey(response, "event")) { + /* OK, it was an event */ + event_string = qdict_get_str(response, "event"); + if (!strcmp(event_string, "STOP")) { + got_stop = true; + } + qobject_unref(response); + response = qtest_qmp_receive(who); + } + return response; +} + +void tpm_util_wait_for_migration_complete(QTestState *who) +{ + while (true) { + QDict *rsp, *rsp_return; + bool completed; + const char *status; + + rsp = tpm_util_wait_command(who, "{ 'execute': 'query-migrate' }"); + rsp_return = qdict_get_qdict(rsp, "return"); + status = qdict_get_str(rsp_return, "status"); + completed = strcmp(status, "completed") == 0; + g_assert_cmpstr(status, !=, "failed"); + qobject_unref(rsp); + if (completed) { + return; + } + usleep(1000); + } +} + +void tpm_util_migration_start_qemu(QTestState **src_qemu, + QTestState **dst_qemu, + SocketAddress *src_tpm_addr, + SocketAddress *dst_tpm_addr, + const char *miguri) +{ + char *src_qemu_args, *dst_qemu_args; + + src_qemu_args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-crb,tpmdev=dev ", + src_tpm_addr->u.q_unix.path); + + *src_qemu = qtest_init(src_qemu_args); + + dst_qemu_args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-crb,tpmdev=dev " + "-incoming %s", + dst_tpm_addr->u.q_unix.path, + miguri); + + *dst_qemu = qtest_init(dst_qemu_args); + + free(src_qemu_args); + free(dst_qemu_args); +} diff --git a/tests/tpm-util.h b/tests/tpm-util.h index d155d99aea..b6253106d9 100644 --- a/tests/tpm-util.h +++ b/tests/tpm-util.h @@ -33,4 +33,14 @@ gboolean tpm_util_swtpm_start(const char *path, GPid *pid, SocketAddress **addr, GError **error); void tpm_util_swtpm_kill(GPid pid); +void tpm_util_migrate(QTestState *who, const char *uri); + +void tpm_util_migration_start_qemu(QTestState **src_qemu, + QTestState **dst_qemu, + SocketAddress *src_tpm_addr, + SocketAddress *dst_tpm_addr, + const char *miguri); + +void tpm_util_wait_for_migration_complete(QTestState *who); + #endif /* TESTS_TPM_UTIL_H */ From 2b4ccb87ec498184de50068f840eec7426d5da2a Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 30 May 2018 12:30:26 -0400 Subject: [PATCH 1022/2380] test: Move common TPM test functions to tpm-tests.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move common TPM test functions from tpm-crb-swtpm-test.c to tpm-tests.c so that for example test cases with the TPM TIS interface can use the same code. Prefix all funcions with 'tpm_test_'. Signed-off-by: Stefan Berger Reviewed-by: Marc-André Lureau --- tests/Makefile.include | 2 +- tests/tpm-crb-swtpm-test.c | 99 ++--------------------------- tests/tpm-tests.c | 124 +++++++++++++++++++++++++++++++++++++ tests/tpm-tests.h | 24 +++++++ 4 files changed, 153 insertions(+), 96 deletions(-) create mode 100644 tests/tpm-tests.c create mode 100644 tests/tpm-tests.h diff --git a/tests/Makefile.include b/tests/Makefile.include index 9854e7794b..392273317f 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -724,7 +724,7 @@ tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y) tests/tpm-crb-swtpm-test$(EXESUF): tests/tpm-crb-swtpm-test.o tests/tpm-emu.o \ - tests/tpm-util.o $(test-io-obj-y) + tests/tpm-util.o tests/tpm-tests.o $(test-io-obj-y) tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y) tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y) tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ diff --git a/tests/tpm-crb-swtpm-test.c b/tests/tpm-crb-swtpm-test.c index 2a22b032ca..4acffff568 100644 --- a/tests/tpm-crb-swtpm-test.c +++ b/tests/tpm-crb-swtpm-test.c @@ -16,7 +16,7 @@ #include #include "libqtest.h" -#include "tpm-util.h" +#include "tpm-tests.h" typedef struct TestState { char *src_tpm_path; @@ -26,108 +26,17 @@ typedef struct TestState { static void tpm_crb_swtpm_test(const void *data) { - char *args = NULL; - QTestState *s; - SocketAddress *addr = NULL; - gboolean succ; - GPid swtpm_pid; - GError *error = NULL; const TestState *ts = data; - succ = tpm_util_swtpm_start(ts->src_tpm_path, &swtpm_pid, &addr, &error); - /* succ may be false if swtpm is not available */ - if (!succ) { - return; - } - - args = g_strdup_printf( - "-chardev socket,id=chr,path=%s " - "-tpmdev emulator,id=dev,chardev=chr " - "-device tpm-crb,tpmdev=dev", - addr->u.q_unix.path); - - s = qtest_start(args); - g_free(args); - - tpm_util_startup(s, tpm_util_crb_transfer); - tpm_util_pcrextend(s, tpm_util_crb_transfer); - - unsigned char tpm_pcrread_resp[] = - "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" - "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" - "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" - "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; - tpm_util_pcrread(s, tpm_util_crb_transfer, tpm_pcrread_resp, - sizeof(tpm_pcrread_resp)); - - qtest_end(); - tpm_util_swtpm_kill(swtpm_pid); - - if (addr) { - g_unlink(addr->u.q_unix.path); - qapi_free_SocketAddress(addr); - } + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_transfer); } static void tpm_crb_swtpm_migration_test(const void *data) { const TestState *ts = data; - gboolean succ; - GPid src_tpm_pid, dst_tpm_pid; - SocketAddress *src_tpm_addr = NULL, *dst_tpm_addr = NULL; - GError *error = NULL; - QTestState *src_qemu, *dst_qemu; - succ = tpm_util_swtpm_start(ts->src_tpm_path, &src_tpm_pid, - &src_tpm_addr, &error); - /* succ may be false if swtpm is not available */ - if (!succ) { - return; - } - - succ = tpm_util_swtpm_start(ts->dst_tpm_path, &dst_tpm_pid, - &dst_tpm_addr, &error); - /* succ may be false if swtpm is not available */ - if (!succ) { - goto err_src_tpm_kill; - } - - tpm_util_migration_start_qemu(&src_qemu, &dst_qemu, - src_tpm_addr, dst_tpm_addr, - ts->uri); - - tpm_util_startup(src_qemu, tpm_util_crb_transfer); - tpm_util_pcrextend(src_qemu, tpm_util_crb_transfer); - - unsigned char tpm_pcrread_resp[] = - "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" - "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" - "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" - "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; - tpm_util_pcrread(src_qemu, tpm_util_crb_transfer, tpm_pcrread_resp, - sizeof(tpm_pcrread_resp)); - - tpm_util_migrate(src_qemu, ts->uri); - tpm_util_wait_for_migration_complete(src_qemu); - - tpm_util_pcrread(dst_qemu, tpm_util_crb_transfer, tpm_pcrread_resp, - sizeof(tpm_pcrread_resp)); - - qtest_quit(dst_qemu); - qtest_quit(src_qemu); - - tpm_util_swtpm_kill(dst_tpm_pid); - if (dst_tpm_addr) { - g_unlink(dst_tpm_addr->u.q_unix.path); - qapi_free_SocketAddress(dst_tpm_addr); - } - -err_src_tpm_kill: - tpm_util_swtpm_kill(src_tpm_pid); - if (src_tpm_addr) { - g_unlink(src_tpm_addr->u.q_unix.path); - qapi_free_SocketAddress(src_tpm_addr); - } + tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, + tpm_util_crb_transfer); } int main(int argc, char **argv) diff --git a/tests/tpm-tests.c b/tests/tpm-tests.c new file mode 100644 index 0000000000..adf2c618c8 --- /dev/null +++ b/tests/tpm-tests.c @@ -0,0 +1,124 @@ +/* + * QTest TPM commont test code + * + * Copyright (c) 2018 IBM Corporation + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Stefan Berger + * Marc-André Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "libqtest.h" +#include "tpm-tests.h" + +void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx) +{ + char *args = NULL; + QTestState *s; + SocketAddress *addr = NULL; + gboolean succ; + GPid swtpm_pid; + GError *error = NULL; + + succ = tpm_util_swtpm_start(src_tpm_path, &swtpm_pid, &addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + return; + } + + args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-crb,tpmdev=dev", + addr->u.q_unix.path); + + s = qtest_start(args); + g_free(args); + + tpm_util_startup(s, tx); + tpm_util_pcrextend(s, tx); + + unsigned char tpm_pcrread_resp[] = + "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" + "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" + "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" + "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; + tpm_util_pcrread(s, tx, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + qtest_end(); + tpm_util_swtpm_kill(swtpm_pid); + + if (addr) { + g_unlink(addr->u.q_unix.path); + qapi_free_SocketAddress(addr); + } +} + +void tpm_test_swtpm_migration_test(const char *src_tpm_path, + const char *dst_tpm_path, + const char *uri, tx_func *tx) +{ + gboolean succ; + GPid src_tpm_pid, dst_tpm_pid; + SocketAddress *src_tpm_addr = NULL, *dst_tpm_addr = NULL; + GError *error = NULL; + QTestState *src_qemu, *dst_qemu; + + succ = tpm_util_swtpm_start(src_tpm_path, &src_tpm_pid, + &src_tpm_addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + return; + } + + succ = tpm_util_swtpm_start(dst_tpm_path, &dst_tpm_pid, + &dst_tpm_addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + goto err_src_tpm_kill; + } + + tpm_util_migration_start_qemu(&src_qemu, &dst_qemu, + src_tpm_addr, dst_tpm_addr, uri); + + tpm_util_startup(src_qemu, tx); + tpm_util_pcrextend(src_qemu, tx); + + unsigned char tpm_pcrread_resp[] = + "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" + "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" + "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" + "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; + tpm_util_pcrread(src_qemu, tx, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + tpm_util_migrate(src_qemu, uri); + tpm_util_wait_for_migration_complete(src_qemu); + + tpm_util_pcrread(dst_qemu, tx, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + qtest_quit(dst_qemu); + qtest_quit(src_qemu); + + tpm_util_swtpm_kill(dst_tpm_pid); + if (dst_tpm_addr) { + g_unlink(dst_tpm_addr->u.q_unix.path); + qapi_free_SocketAddress(dst_tpm_addr); + } + +err_src_tpm_kill: + tpm_util_swtpm_kill(src_tpm_pid); + if (src_tpm_addr) { + g_unlink(src_tpm_addr->u.q_unix.path); + qapi_free_SocketAddress(src_tpm_addr); + } +} diff --git a/tests/tpm-tests.h b/tests/tpm-tests.h new file mode 100644 index 0000000000..377f184c77 --- /dev/null +++ b/tests/tpm-tests.h @@ -0,0 +1,24 @@ +/* + * QTest TPM commont test code + * + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TESTS_TPM_TESTS_H +#define TESTS_TPM_TESTS_H + +#include "tpm-util.h" + +void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx); + +void tpm_test_swtpm_migration_test(const char *src_tpm_path, + const char *dst_tpm_path, + const char *uri, tx_func *tx); + +#endif /* TESTS_TPM_TESTS_H */ From ea71a3369164685d4daeeeac9dfe85a64eeeb877 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 30 May 2018 14:31:12 -0400 Subject: [PATCH 1023/2380] test: Pass TPM interface model to functions creating command line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass the TPM interface model, such as 'tpm-crb', through to the functions that create the command line for QEMU. Signed-off-by: Stefan Berger Reviewed-by: Marc-André Lureau --- tests/tpm-crb-swtpm-test.c | 4 ++-- tests/tpm-tests.c | 13 ++++++++----- tests/tpm-tests.h | 6 ++++-- tests/tpm-util.c | 11 ++++++----- tests/tpm-util.h | 3 ++- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/tests/tpm-crb-swtpm-test.c b/tests/tpm-crb-swtpm-test.c index 4acffff568..8c0a55f3ca 100644 --- a/tests/tpm-crb-swtpm-test.c +++ b/tests/tpm-crb-swtpm-test.c @@ -28,7 +28,7 @@ static void tpm_crb_swtpm_test(const void *data) { const TestState *ts = data; - tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_transfer); + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_transfer, "tpm-crb"); } static void tpm_crb_swtpm_migration_test(const void *data) @@ -36,7 +36,7 @@ static void tpm_crb_swtpm_migration_test(const void *data) const TestState *ts = data; tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, - tpm_util_crb_transfer); + tpm_util_crb_transfer, "tpm-crb"); } int main(int argc, char **argv) diff --git a/tests/tpm-tests.c b/tests/tpm-tests.c index adf2c618c8..10c6592aac 100644 --- a/tests/tpm-tests.c +++ b/tests/tpm-tests.c @@ -18,7 +18,8 @@ #include "libqtest.h" #include "tpm-tests.h" -void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx) +void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, + const char *ifmodel) { char *args = NULL; QTestState *s; @@ -36,8 +37,8 @@ void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx) args = g_strdup_printf( "-chardev socket,id=chr,path=%s " "-tpmdev emulator,id=dev,chardev=chr " - "-device tpm-crb,tpmdev=dev", - addr->u.q_unix.path); + "-device %s,tpmdev=dev", + addr->u.q_unix.path, ifmodel); s = qtest_start(args); g_free(args); @@ -64,7 +65,8 @@ void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx) void tpm_test_swtpm_migration_test(const char *src_tpm_path, const char *dst_tpm_path, - const char *uri, tx_func *tx) + const char *uri, tx_func *tx, + const char *ifmodel) { gboolean succ; GPid src_tpm_pid, dst_tpm_pid; @@ -87,7 +89,8 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path, } tpm_util_migration_start_qemu(&src_qemu, &dst_qemu, - src_tpm_addr, dst_tpm_addr, uri); + src_tpm_addr, dst_tpm_addr, uri, + ifmodel); tpm_util_startup(src_qemu, tx); tpm_util_pcrextend(src_qemu, tx); diff --git a/tests/tpm-tests.h b/tests/tpm-tests.h index 377f184c77..b97688fe75 100644 --- a/tests/tpm-tests.h +++ b/tests/tpm-tests.h @@ -15,10 +15,12 @@ #include "tpm-util.h" -void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx); +void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, + const char *ifmodel); void tpm_test_swtpm_migration_test(const char *src_tpm_path, const char *dst_tpm_path, - const char *uri, tx_func *tx); + const char *uri, tx_func *tx, + const char *ifmodel); #endif /* TESTS_TPM_TESTS_H */ diff --git a/tests/tpm-util.c b/tests/tpm-util.c index e6e3b922fa..e1ac4d1bd5 100644 --- a/tests/tpm-util.c +++ b/tests/tpm-util.c @@ -248,25 +248,26 @@ void tpm_util_migration_start_qemu(QTestState **src_qemu, QTestState **dst_qemu, SocketAddress *src_tpm_addr, SocketAddress *dst_tpm_addr, - const char *miguri) + const char *miguri, + const char *ifmodel) { char *src_qemu_args, *dst_qemu_args; src_qemu_args = g_strdup_printf( "-chardev socket,id=chr,path=%s " "-tpmdev emulator,id=dev,chardev=chr " - "-device tpm-crb,tpmdev=dev ", - src_tpm_addr->u.q_unix.path); + "-device %s,tpmdev=dev ", + src_tpm_addr->u.q_unix.path, ifmodel); *src_qemu = qtest_init(src_qemu_args); dst_qemu_args = g_strdup_printf( "-chardev socket,id=chr,path=%s " "-tpmdev emulator,id=dev,chardev=chr " - "-device tpm-crb,tpmdev=dev " + "-device %s,tpmdev=dev " "-incoming %s", dst_tpm_addr->u.q_unix.path, - miguri); + ifmodel, miguri); *dst_qemu = qtest_init(dst_qemu_args); diff --git a/tests/tpm-util.h b/tests/tpm-util.h index b6253106d9..bb128360dd 100644 --- a/tests/tpm-util.h +++ b/tests/tpm-util.h @@ -39,7 +39,8 @@ void tpm_util_migration_start_qemu(QTestState **src_qemu, QTestState **dst_qemu, SocketAddress *src_tpm_addr, SocketAddress *dst_tpm_addr, - const char *miguri); + const char *miguri, + const char *ifmodel); void tpm_util_wait_for_migration_complete(QTestState *who); From 70663851ed4242435676c0bc288efbc1bc4ccf87 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 30 May 2018 13:44:41 -0400 Subject: [PATCH 1024/2380] test: Add swtpm migration test for the TPM TIS interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test case for testing swtpm migration with the TPM TIS interface. Signed-off-by: Stefan Berger Reviewed-by: Marc-André Lureau --- tests/Makefile.include | 3 ++ tests/tpm-tis-swtpm-test.c | 66 ++++++++++++++++++++++++++++++++++++++ tests/tpm-util.c | 48 +++++++++++++++++++++++++++ tests/tpm-util.h | 3 ++ 4 files changed, 120 insertions(+) create mode 100644 tests/tpm-tis-swtpm-test.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 392273317f..0eaa835b8a 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -299,6 +299,7 @@ check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test endif check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-swtpm-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF) +check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-swtpm-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-test$(EXESUF) check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) @@ -726,6 +727,8 @@ tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ tests/tpm-crb-swtpm-test$(EXESUF): tests/tpm-crb-swtpm-test.o tests/tpm-emu.o \ tests/tpm-util.o tests/tpm-tests.o $(test-io-obj-y) tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y) +tests/tpm-tis-swtpm-test$(EXESUF): tests/tpm-tis-swtpm-test.o tests/tpm-emu.o \ + tests/tpm-util.o tests/tpm-tests.o $(test-io-obj-y) tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y) tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ tests/io-channel-helpers.o $(test-io-obj-y) diff --git a/tests/tpm-tis-swtpm-test.c b/tests/tpm-tis-swtpm-test.c new file mode 100644 index 0000000000..7dcd1d3912 --- /dev/null +++ b/tests/tpm-tis-swtpm-test.c @@ -0,0 +1,66 @@ +/* + * QTest testcase for TPM TIS talking to external swtpm and swtpm migration + * + * Copyright (c) 2018 IBM Corporation + * with parts borrowed from migration-test.c that is: + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "libqtest.h" +#include "tpm-tests.h" + +typedef struct TestState { + char *src_tpm_path; + char *dst_tpm_path; + char *uri; +} TestState; + +static void tpm_tis_swtpm_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_tis_transfer, "tpm-tis"); +} + +static void tpm_tis_swtpm_migration_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, + tpm_util_tis_transfer, "tpm-tis"); +} + +int main(int argc, char **argv) +{ + int ret; + TestState ts = { 0 }; + + ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-tis-swtpm-test.XXXXXX", NULL); + ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-tis-swtpm-test.XXXXXX", NULL); + ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/tpm/tis-swtpm/test", &ts, tpm_tis_swtpm_test); + qtest_add_data_func("/tpm/tis-swtpm-migration/test", &ts, + tpm_tis_swtpm_migration_test); + ret = g_test_run(); + + g_rmdir(ts.dst_tpm_path); + g_free(ts.dst_tpm_path); + g_rmdir(ts.src_tpm_path); + g_free(ts.src_tpm_path); + g_free(ts.uri); + + return ret; +} diff --git a/tests/tpm-util.c b/tests/tpm-util.c index e1ac4d1bd5..672cedf905 100644 --- a/tests/tpm-util.c +++ b/tests/tpm-util.c @@ -19,6 +19,9 @@ #include "tpm-util.h" #include "qapi/qmp/qdict.h" +#define TIS_REG(LOCTY, REG) \ + (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG) + static bool got_stop; void tpm_util_crb_transfer(QTestState *s, @@ -52,6 +55,51 @@ void tpm_util_crb_transfer(QTestState *s, qtest_memread(s, raddr, rsp, rsp_size); } +void tpm_util_tis_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size) +{ + uint32_t sts; + uint16_t bcount; + size_t i; + + /* request use of locality 0 */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + qtest_writel(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY); + + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + bcount = (sts >> 8) & 0xffff; + g_assert_cmpint(bcount, >=, req_size); + + /* transmit command */ + for (i = 0; i < req_size; i++) { + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO), req[i]); + } + + /* start processing */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO); + + uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; + do { + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { + break; + } + } while (g_get_monotonic_time() < end_time); + + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + bcount = (sts >> 8) & 0xffff; + + memset(rsp, 0, rsp_size); + for (i = 0; i < bcount; i++) { + rsp[i] = qtest_readb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO)); + } + + /* relinquish use of locality 0 */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), + TPM_TIS_ACCESS_ACTIVE_LOCALITY); +} + void tpm_util_startup(QTestState *s, tx_func *tx) { unsigned char buffer[1024]; diff --git a/tests/tpm-util.h b/tests/tpm-util.h index bb128360dd..330b9657fe 100644 --- a/tests/tpm-util.h +++ b/tests/tpm-util.h @@ -23,6 +23,9 @@ typedef void (tx_func)(QTestState *s, void tpm_util_crb_transfer(QTestState *s, const unsigned char *req, size_t req_size, unsigned char *rsp, size_t rsp_size); +void tpm_util_tis_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); void tpm_util_startup(QTestState *s, tx_func *tx); void tpm_util_pcrextend(QTestState *s, tx_func *tx); From fde1f3e4a0644feb005894950427b622496769cb Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 7 Jun 2018 12:17:21 +0200 Subject: [PATCH 1025/2380] 9p: proxy: Fix size passed to `connect` The size to pass to the `connect` call is the size of the entire `struct sockaddr_un`. Passing anything shorter than this causes errors on darwin. Signed-off-by: Keno Fischer Signed-off-by: Greg Kurz --- hw/9pfs/9p-proxy.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c index e2e03292de..47a94e088d 100644 --- a/hw/9pfs/9p-proxy.c +++ b/hw/9pfs/9p-proxy.c @@ -1088,7 +1088,7 @@ static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path, static int connect_namedsocket(const char *path, Error **errp) { - int sockfd, size; + int sockfd; struct sockaddr_un helper; if (strlen(path) >= sizeof(helper.sun_path)) { @@ -1102,8 +1102,7 @@ static int connect_namedsocket(const char *path, Error **errp) } strcpy(helper.sun_path, path); helper.sun_family = AF_UNIX; - size = strlen(helper.sun_path) + sizeof(helper.sun_family); - if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) { + if (connect(sockfd, (struct sockaddr *)&helper, sizeof(helper)) < 0) { error_setg_errno(errp, errno, "failed to connect to '%s'", path); close(sockfd); return -1; From 2306271c381b75b66d76c4231bba3e7452a0d876 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 7 Jun 2018 12:17:21 +0200 Subject: [PATCH 1026/2380] 9p: local: Properly set errp in fstatfs error path In the review of 9p: Avoid warning if FS_IOC_GETVERSION is not defined Grep Kurz noted this error path was failing to set errp. Fix that. Signed-off-by: Keno Fischer [added local: to commit title, Greg Kurz] Signed-off-by: Greg Kurz --- hw/9pfs/9p-local.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index b37b1db453..7758c38509 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -1418,6 +1418,8 @@ static int local_init(FsContext *ctx, Error **errp) */ if (fstatfs(data->mountfd, &stbuf) < 0) { close_preserve_errno(data->mountfd); + error_setg_errno(errp, errno, + "failed to stat file system at '%s'", ctx->fs_root); goto err; } switch (stbuf.f_type) { From ec70b956fddf628ee2e42521c54362e80115a3c4 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 7 Jun 2018 12:17:22 +0200 Subject: [PATCH 1027/2380] 9p: Move a couple xattr functions to 9p-util These functions will need custom implementations on Darwin. Since the implementation is very similar among all of them, and 9p-util already has the _nofollow version of fgetxattrat, let's move them all there. Signed-off-by: Keno Fischer Signed-off-by: Greg Kurz --- hw/9pfs/9p-util.c | 33 +++++++++++++++++++++++++++++++++ hw/9pfs/9p-util.h | 4 ++++ hw/9pfs/9p-xattr.c | 33 --------------------------------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/hw/9pfs/9p-util.c b/hw/9pfs/9p-util.c index f709c27a1f..614b7fc34d 100644 --- a/hw/9pfs/9p-util.c +++ b/hw/9pfs/9p-util.c @@ -24,3 +24,36 @@ ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name, g_free(proc_path); return ret; } + +ssize_t flistxattrat_nofollow(int dirfd, const char *filename, + char *list, size_t size) +{ + char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); + int ret; + + ret = llistxattr(proc_path, list, size); + g_free(proc_path); + return ret; +} + +ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, + const char *name) +{ + char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); + int ret; + + ret = lremovexattr(proc_path, name); + g_free(proc_path); + return ret; +} + +int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name, + void *value, size_t size, int flags) +{ + char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); + int ret; + + ret = lsetxattr(proc_path, name, value, size, flags); + g_free(proc_path); + return ret; +} diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index dc0d2e29aa..79ed6b233e 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -60,5 +60,9 @@ ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, void *value, size_t size); int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, void *value, size_t size, int flags); +ssize_t flistxattrat_nofollow(int dirfd, const char *filename, + char *list, size_t size); +ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, + const char *name); #endif diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c index d05c1a1c1d..c696d8f846 100644 --- a/hw/9pfs/9p-xattr.c +++ b/hw/9pfs/9p-xattr.c @@ -60,17 +60,6 @@ ssize_t pt_listxattr(FsContext *ctx, const char *path, return name_size; } -static ssize_t flistxattrat_nofollow(int dirfd, const char *filename, - char *list, size_t size) -{ - char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); - int ret; - - ret = llistxattr(proc_path, list, size); - g_free(proc_path); - return ret; -} - /* * Get the list and pass to each layer to find out whether * to send the data or not @@ -196,17 +185,6 @@ ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, return local_getxattr_nofollow(ctx, path, name, value, size); } -int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name, - void *value, size_t size, int flags) -{ - char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); - int ret; - - ret = lsetxattr(proc_path, name, value, size, flags); - g_free(proc_path); - return ret; -} - ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path, const char *name, void *value, size_t size, int flags) @@ -235,17 +213,6 @@ int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value, return local_setxattr_nofollow(ctx, path, name, value, size, flags); } -static ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, - const char *name) -{ - char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); - int ret; - - ret = lremovexattr(proc_path, name); - g_free(proc_path); - return ret; -} - ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path, const char *name) { From a647502c582981c395b5d16e52a22ac7aff0fb2b Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 7 Jun 2018 12:17:22 +0200 Subject: [PATCH 1028/2380] 9p: xattr: Fix crashes due to free of uninitialized value If the size returned from llistxattr/lgetxattr is 0, we skipped the malloc call, leaving xattr.value uninitialized. However, this value is later passed to `g_free` without any further checks, causing an error. Fix that by always calling g_malloc unconditionally. If `size` is 0, it will return NULL, which is safe to pass to g_free. Signed-off-by: Keno Fischer Signed-off-by: Greg Kurz --- hw/9pfs/9p.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index d74302deeb..4386d69817 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -3256,8 +3256,8 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) xattr_fidp->fs.xattr.len = size; xattr_fidp->fid_type = P9_FID_XATTR; xattr_fidp->fs.xattr.xattrwalk_fid = true; + xattr_fidp->fs.xattr.value = g_malloc0(size); if (size) { - xattr_fidp->fs.xattr.value = g_malloc0(size); err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, xattr_fidp->fs.xattr.value, xattr_fidp->fs.xattr.len); @@ -3289,8 +3289,8 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) xattr_fidp->fs.xattr.len = size; xattr_fidp->fid_type = P9_FID_XATTR; xattr_fidp->fs.xattr.xattrwalk_fid = true; + xattr_fidp->fs.xattr.value = g_malloc0(size); if (size) { - xattr_fidp->fs.xattr.value = g_malloc0(size); err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, &name, xattr_fidp->fs.xattr.value, xattr_fidp->fs.xattr.len); From 5b7b2f9a85bcc44485da713f60e371dd66a644b1 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 7 Jun 2018 12:17:22 +0200 Subject: [PATCH 1029/2380] 9p: local: Avoid warning if FS_IOC_GETVERSION is not defined Both `stbuf` and `local_ioc_getversion` where unused when FS_IOC_GETVERSION was not defined, causing a compiler warning. Reorganize the code to avoid this warning. Signed-off-by: Keno Fischer Signed-off-by: Greg Kurz --- hw/9pfs/9p-local.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 7758c38509..5721eff1e1 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -1373,10 +1373,10 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir, return ret; } +#ifdef FS_IOC_GETVERSION static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, mode_t st_mode, uint64_t *st_gen) { -#ifdef FS_IOC_GETVERSION int err; V9fsFidOpenState fid_open; @@ -1395,32 +1395,21 @@ static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); local_close(ctx, &fid_open); return err; -#else - errno = ENOTTY; - return -1; -#endif } +#endif -static int local_init(FsContext *ctx, Error **errp) +static int local_ioc_getversion_init(FsContext *ctx, LocalData *data, Error **errp) { - struct statfs stbuf; - LocalData *data = g_malloc(sizeof(*data)); - - data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY); - if (data->mountfd == -1) { - error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); - goto err; - } - #ifdef FS_IOC_GETVERSION + struct statfs stbuf; + /* * use ioc_getversion only if the ioctl is definied */ if (fstatfs(data->mountfd, &stbuf) < 0) { - close_preserve_errno(data->mountfd); error_setg_errno(errp, errno, - "failed to stat file system at '%s'", ctx->fs_root); - goto err; + "failed to stat file system at '%s'", ctx->fs_root); + return -1; } switch (stbuf.f_type) { case EXT2_SUPER_MAGIC: @@ -1431,6 +1420,23 @@ static int local_init(FsContext *ctx, Error **errp) break; } #endif + return 0; +} + +static int local_init(FsContext *ctx, Error **errp) +{ + LocalData *data = g_malloc(sizeof(*data)); + + data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY); + if (data->mountfd == -1) { + error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); + goto err; + } + + if (local_ioc_getversion_init(ctx, data, errp) < 0) { + close(data->mountfd); + goto err; + } if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { ctx->xops = passthrough_xattr_ops; From 67e87345744ac96d6c9560827ea094264c88fbff Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 7 Jun 2018 12:17:22 +0200 Subject: [PATCH 1030/2380] 9p: Properly check/translate flags in unlinkat The 9p-local code previously relied on P9_DOTL_AT_REMOVEDIR and AT_REMOVEDIR having the same numerical value and deferred any errorchecking to the syscall itself. However, while the former assumption is true on Linux, it is not true in general. 9p-handle did this properly however. Move the translation code to the generic 9p server code and add an error if unrecognized flags are passed. Signed-off-by: Keno Fischer Signed-off-by: Greg Kurz --- hw/9pfs/9p-handle.c | 8 +------- hw/9pfs/9p.c | 13 +++++++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/hw/9pfs/9p-handle.c b/hw/9pfs/9p-handle.c index 4dc0d2bed1..f3641dbe4a 100644 --- a/hw/9pfs/9p-handle.c +++ b/hw/9pfs/9p-handle.c @@ -559,19 +559,13 @@ static int handle_unlinkat(FsContext *ctx, V9fsPath *dir, { int dirfd, ret; HandleData *data = (HandleData *) ctx->private; - int rflags; dirfd = open_by_handle(data->mountfd, dir->data, O_PATH); if (dirfd < 0) { return dirfd; } - rflags = 0; - if (flags & P9_DOTL_AT_REMOVEDIR) { - rflags |= AT_REMOVEDIR; - } - - ret = unlinkat(dirfd, name, rflags); + ret = unlinkat(dirfd, name, flags); close(dirfd); return ret; diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 4386d69817..c842ec555e 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -2522,7 +2522,7 @@ static void coroutine_fn v9fs_unlinkat(void *opaque) { int err = 0; V9fsString name; - int32_t dfid, flags; + int32_t dfid, flags, rflags = 0; size_t offset = 7; V9fsPath path; V9fsFidState *dfidp; @@ -2549,6 +2549,15 @@ static void coroutine_fn v9fs_unlinkat(void *opaque) goto out_nofid; } + if (flags & ~P9_DOTL_AT_REMOVEDIR) { + err = -EINVAL; + goto out_nofid; + } + + if (flags & P9_DOTL_AT_REMOVEDIR) { + rflags |= AT_REMOVEDIR; + } + dfidp = get_fid(pdu, dfid); if (dfidp == NULL) { err = -EINVAL; @@ -2567,7 +2576,7 @@ static void coroutine_fn v9fs_unlinkat(void *opaque) if (err < 0) { goto out_err; } - err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, flags); + err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, rflags); if (!err) { err = offset; } From aca6897fba149a2a650dcdf5a5e1ae828371f4aa Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 7 Jun 2018 12:17:22 +0200 Subject: [PATCH 1031/2380] 9p: xattr: Properly translate xattrcreate flags As with unlinkat, these flags come from the client and need to be translated to their host values. The protocol values happen to match linux, but that need not be true in general. Signed-off-by: Keno Fischer Signed-off-by: Greg Kurz --- hw/9pfs/9p.c | 17 +++++++++++++++-- hw/9pfs/9p.h | 4 ++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index c842ec555e..eef289e394 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -3327,7 +3327,7 @@ out_nofid: static void coroutine_fn v9fs_xattrcreate(void *opaque) { - int flags; + int flags, rflags = 0; int32_t fid; uint64_t size; ssize_t err = 0; @@ -3344,6 +3344,19 @@ static void coroutine_fn v9fs_xattrcreate(void *opaque) } trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags); + if (flags & ~(P9_XATTR_CREATE | P9_XATTR_REPLACE)) { + err = -EINVAL; + goto out_nofid; + } + + if (flags & P9_XATTR_CREATE) { + rflags |= XATTR_CREATE; + } + + if (flags & P9_XATTR_REPLACE) { + rflags |= XATTR_REPLACE; + } + if (size > XATTR_SIZE_MAX) { err = -E2BIG; goto out_nofid; @@ -3365,7 +3378,7 @@ static void coroutine_fn v9fs_xattrcreate(void *opaque) xattr_fidp->fs.xattr.copied_len = 0; xattr_fidp->fs.xattr.xattrwalk_fid = false; xattr_fidp->fs.xattr.len = size; - xattr_fidp->fs.xattr.flags = flags; + xattr_fidp->fs.xattr.flags = rflags; v9fs_string_init(&xattr_fidp->fs.xattr.name); v9fs_string_copy(&xattr_fidp->fs.xattr.name, &name); xattr_fidp->fs.xattr.value = g_malloc0(size); diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index bad8ee719c..8883761b2c 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -169,6 +169,10 @@ typedef struct V9fsConf char *fsdev_id; } V9fsConf; +/* 9p2000.L xattr flags (matches Linux values) */ +#define P9_XATTR_CREATE 1 +#define P9_XATTR_REPLACE 2 + typedef struct V9fsXattr { uint64_t copied_len; From 02cffe24a28dafb63b80f67c6fb0f91ef7d89ef2 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Thu, 31 May 2018 21:45:10 +0200 Subject: [PATCH 1032/2380] slirp: Add Samuel Thibault's staging tree for slirp Signed-off-by: Samuel Thibault Acked-by: Thomas Huth --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 41cd3736a9..4c73c16fee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1675,6 +1675,7 @@ S: Maintained F: slirp/ F: net/slirp.c F: include/net/slirp.h +T: git https://people.debian.org/~sthibault/qemu.git slirp T: git git://git.kiszka.org/qemu.git queues/slirp Stubs From 3835c310bd13662d5fb3f50f3dd62605dfd40cf9 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Thu, 31 May 2018 21:51:02 +0200 Subject: [PATCH 1033/2380] slirp: fix domainname version availability The change missed the 2.12 deadline. Signed-off-by: Samuel Thibault Reviewed-by: Eric Blake --- qapi/net.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/net.json b/qapi/net.json index 32681a1af7..6b7d93cb59 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -161,7 +161,7 @@ # to the guest # # @domainname: guest-visible domain name of the virtual nameserver -# (since 2.12) +# (since 3.0) # # @ipv6-prefix: IPv6 network prefix (default is fec0::) (since # 2.6). The network prefix is given in the usual From 864036e251f54c99d31df124aad7f34f01f5344c Mon Sep 17 00:00:00 2001 From: Prasad J Pandit Date: Tue, 5 Jun 2018 23:38:35 +0530 Subject: [PATCH 1034/2380] slirp: correct size computation while concatenating mbuf While reassembling incoming fragmented datagrams, 'm_cat' routine extends the 'mbuf' buffer, if it has insufficient room. It computes a wrong buffer size, which leads to overwriting adjacent heap buffer area. Correct this size computation in m_cat. Reported-by: ZDI Disclosures Signed-off-by: Prasad J Pandit Signed-off-by: Samuel Thibault --- slirp/mbuf.c | 11 +++++------ slirp/mbuf.h | 8 +++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/slirp/mbuf.c b/slirp/mbuf.c index 5ff24559fd..18cbf759a7 100644 --- a/slirp/mbuf.c +++ b/slirp/mbuf.c @@ -138,7 +138,7 @@ m_cat(struct mbuf *m, struct mbuf *n) * If there's no room, realloc */ if (M_FREEROOM(m) < n->m_len) - m_inc(m,m->m_size+MINCSIZE); + m_inc(m, m->m_len + n->m_len); memcpy(m->m_data+m->m_len, n->m_data, n->m_len); m->m_len += n->m_len; @@ -147,7 +147,7 @@ m_cat(struct mbuf *m, struct mbuf *n) } -/* make m size bytes large */ +/* make m 'size' bytes large from m_data */ void m_inc(struct mbuf *m, int size) { @@ -158,12 +158,12 @@ m_inc(struct mbuf *m, int size) if (m->m_flags & M_EXT) { datasize = m->m_data - m->m_ext; - m->m_ext = g_realloc(m->m_ext, size); + m->m_ext = g_realloc(m->m_ext, size + datasize); m->m_data = m->m_ext + datasize; } else { char *dat; datasize = m->m_data - m->m_dat; - dat = g_malloc(size); + dat = g_malloc(size + datasize); memcpy(dat, m->m_dat, m->m_size); m->m_ext = dat; @@ -171,8 +171,7 @@ m_inc(struct mbuf *m, int size) m->m_flags |= M_EXT; } - m->m_size = size; - + m->m_size = size + datasize; } diff --git a/slirp/mbuf.h b/slirp/mbuf.h index 893601ff9d..33b84485d6 100644 --- a/slirp/mbuf.h +++ b/slirp/mbuf.h @@ -33,8 +33,6 @@ #ifndef MBUF_H #define MBUF_H -#define MINCSIZE 4096 /* Amount to increase mbuf if too small */ - /* * Macros for type conversion * mtod(m,t) - convert mbuf pointer to data pointer of correct type @@ -72,11 +70,11 @@ struct mbuf { struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */ int m_flags; /* Misc flags */ - int m_size; /* Size of data */ + int m_size; /* Size of mbuf, from m_dat or m_ext */ struct socket *m_so; - caddr_t m_data; /* Location of data */ - int m_len; /* Amount of data in this mbuf */ + caddr_t m_data; /* Current location of data */ + int m_len; /* Amount of data in this mbuf, from m_data */ Slirp *slirp; bool resolution_requested; From c22098c74a09164797fae6511c5eaf68f32c4dd8 Mon Sep 17 00:00:00 2001 From: Prasad J Pandit Date: Tue, 5 Jun 2018 23:38:35 +0530 Subject: [PATCH 1035/2380] slirp: reformat m_inc routine Coding style changes to the m_inc routine and minor refactoring. Reported-by: ZDI Disclosures Signed-off-by: Prasad J Pandit Signed-off-by: Samuel Thibault --- slirp/mbuf.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/slirp/mbuf.c b/slirp/mbuf.c index 18cbf759a7..0c189e1a7b 100644 --- a/slirp/mbuf.c +++ b/slirp/mbuf.c @@ -151,27 +151,25 @@ m_cat(struct mbuf *m, struct mbuf *n) void m_inc(struct mbuf *m, int size) { - int datasize; + int datasize; - /* some compiles throw up on gotos. This one we can fake. */ - if(m->m_size>size) return; + /* some compilers throw up on gotos. This one we can fake. */ + if (m->m_size > size) { + return; + } - if (m->m_flags & M_EXT) { - datasize = m->m_data - m->m_ext; - m->m_ext = g_realloc(m->m_ext, size + datasize); - m->m_data = m->m_ext + datasize; - } else { - char *dat; - datasize = m->m_data - m->m_dat; - dat = g_malloc(size + datasize); - memcpy(dat, m->m_dat, m->m_size); + if (m->m_flags & M_EXT) { + datasize = m->m_data - m->m_ext; + m->m_ext = g_realloc(m->m_ext, size + datasize); + } else { + datasize = m->m_data - m->m_dat; + m->m_ext = g_malloc(size + datasize); + memcpy(m->m_ext, m->m_dat, m->m_size); + m->m_flags |= M_EXT; + } - m->m_ext = dat; - m->m_data = m->m_ext + datasize; - m->m_flags |= M_EXT; - } - - m->m_size = size + datasize; + m->m_data = m->m_ext + datasize; + m->m_size = size + datasize; } From ed6b018ef7667f73aa25190b04e1fe3a4a87c323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Jun 2018 18:08:18 -0300 Subject: [PATCH 1036/2380] chardev: Restore CR,LF on stdio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the 'stair-step output' on stdio. This partially reverts commit 12fb0ac05, which was correct on the mailing list but got corrupted by the maintainer :p Introduced-by: 3b876140-c035-dd39-75d0-d54c48128fac@redhat.com Reported-by: BALATON Zoltan Reviewed-by: Thomas Huth Tested-by: Laurent Desnogues Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180607210818.12727-1-f4bug@amsat.org Suggested-by: Thomas Huth Tested-by: Laurent Desnogues Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- chardev/char-stdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index d83e60e787..96375f2ab8 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -59,7 +59,7 @@ static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo) if (!echo) { tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - tty.c_oflag &= ~OPOST; + tty.c_oflag |= OPOST; tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); tty.c_cflag &= ~(CSIZE | PARENB); tty.c_cflag |= CS8; From 910e204841954b95c051b2ee49ab0f5c735ff93c Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1037/2380] arm_gicv3_kvm: kvm_dist_get/put: skip the registers banked by GICR While we skip the GIC_INTERNAL irqs, we don't change the register offset accordingly. This will overlap the GICR registers value and leave the last GIC_INTERNAL irq's registers out of update. Fix this by skipping the registers banked by GICR. Also for migration compatibility if the migration source (old version qemu) doesn't send gicd_no_migration_shift_bug = 1 to destination, then we shift the data of PPI to get the right data for SPI. Fixes: 367b9f527becdd20ddf116e17a3c0c2bbc486920 Cc: qemu-stable@nongnu.org Reviewed-by: Eric Auger Reviewed-by: Peter Maydell Signed-off-by: Shannon Zhao Message-id: 1527816987-16108-1-git-send-email-zhaoshenglong@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_common.c | 79 ++++++++++++++++++++++++++++++ hw/intc/arm_gicv3_kvm.c | 38 ++++++++++++++ include/hw/intc/arm_gicv3_common.h | 1 + 3 files changed, 118 insertions(+) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 7b54d52376..864b7c6515 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -27,6 +27,7 @@ #include "hw/intc/arm_gicv3_common.h" #include "gicv3_internal.h" #include "hw/arm/linux-boot-if.h" +#include "sysemu/kvm.h" static int gicv3_pre_save(void *opaque) { @@ -141,6 +142,79 @@ static const VMStateDescription vmstate_gicv3_cpu = { } }; +static int gicv3_gicd_no_migration_shift_bug_pre_load(void *opaque) +{ + GICv3State *cs = opaque; + + /* + * The gicd_no_migration_shift_bug flag is used for migration compatibility + * for old version QEMU which may have the GICD bmp shift bug under KVM mode. + * Strictly, what we want to know is whether the migration source is using + * KVM. Since we don't have any way to determine that, we look at whether the + * destination is using KVM; this is close enough because for the older QEMU + * versions with this bug KVM -> TCG migration didn't work anyway. If the + * source is a newer QEMU without this bug it will transmit the migration + * subsection which sets the flag to true; otherwise it will remain set to + * the value we select here. + */ + if (kvm_enabled()) { + cs->gicd_no_migration_shift_bug = false; + } + + return 0; +} + +static int gicv3_gicd_no_migration_shift_bug_post_load(void *opaque, + int version_id) +{ + GICv3State *cs = opaque; + + if (cs->gicd_no_migration_shift_bug) { + return 0; + } + + /* Older versions of QEMU had a bug in the handling of state save/restore + * to the KVM GICv3: they got the offset in the bitmap arrays wrong, + * so that instead of the data for external interrupts 32 and up + * starting at bit position 32 in the bitmap, it started at bit + * position 64. If we're receiving data from a QEMU with that bug, + * we must move the data down into the right place. + */ + memmove(cs->group, (uint8_t *)cs->group + GIC_INTERNAL / 8, + sizeof(cs->group) - GIC_INTERNAL / 8); + memmove(cs->grpmod, (uint8_t *)cs->grpmod + GIC_INTERNAL / 8, + sizeof(cs->grpmod) - GIC_INTERNAL / 8); + memmove(cs->enabled, (uint8_t *)cs->enabled + GIC_INTERNAL / 8, + sizeof(cs->enabled) - GIC_INTERNAL / 8); + memmove(cs->pending, (uint8_t *)cs->pending + GIC_INTERNAL / 8, + sizeof(cs->pending) - GIC_INTERNAL / 8); + memmove(cs->active, (uint8_t *)cs->active + GIC_INTERNAL / 8, + sizeof(cs->active) - GIC_INTERNAL / 8); + memmove(cs->edge_trigger, (uint8_t *)cs->edge_trigger + GIC_INTERNAL / 8, + sizeof(cs->edge_trigger) - GIC_INTERNAL / 8); + + /* + * While this new version QEMU doesn't have this kind of bug as we fix it, + * so it needs to set the flag to true to indicate that and it's necessary + * for next migration to work from this new version QEMU. + */ + cs->gicd_no_migration_shift_bug = true; + + return 0; +} + +const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = { + .name = "arm_gicv3/gicd_no_migration_shift_bug", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = gicv3_gicd_no_migration_shift_bug_pre_load, + .post_load = gicv3_gicd_no_migration_shift_bug_post_load, + .fields = (VMStateField[]) { + VMSTATE_BOOL(gicd_no_migration_shift_bug, GICv3State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3 = { .name = "arm_gicv3", .version_id = 1, @@ -165,6 +239,10 @@ static const VMStateDescription vmstate_gicv3 = { VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, GICv3State, num_cpu, vmstate_gicv3_cpu, GICv3CPUState), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_gicv3_gicd_no_migration_shift_bug, + NULL } }; @@ -364,6 +442,7 @@ static void arm_gicv3_common_reset(DeviceState *dev) gicv3_gicd_group_set(s, i); } } + s->gicd_no_migration_shift_bug = true; } static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 0279b86cd9..5649cac46e 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -164,6 +164,14 @@ static void kvm_dist_get_edge_trigger(GICv3State *s, uint32_t offset, uint32_t reg; int irq; + /* For the KVM GICv3, affinity routing is always enabled, and the first 2 + * GICD_ICFGR registers are always RAZ/WI. The corresponding + * functionality is replaced by GICR_ICFGR. It doesn't need to sync + * them. So it should increase the offset to skip GIC_INTERNAL irqs. + * This matches the for_each_dist_irq_reg() macro which also skips the + * first GIC_INTERNAL irqs. + */ + offset += (GIC_INTERNAL * 2) / 8; for_each_dist_irq_reg(irq, s->num_irq, 2) { kvm_gicd_access(s, offset, ®, false); reg = half_unshuffle32(reg >> 1); @@ -181,6 +189,14 @@ static void kvm_dist_put_edge_trigger(GICv3State *s, uint32_t offset, uint32_t reg; int irq; + /* For the KVM GICv3, affinity routing is always enabled, and the first 2 + * GICD_ICFGR registers are always RAZ/WI. The corresponding + * functionality is replaced by GICR_ICFGR. It doesn't need to sync + * them. So it should increase the offset to skip GIC_INTERNAL irqs. + * This matches the for_each_dist_irq_reg() macro which also skips the + * first GIC_INTERNAL irqs. + */ + offset += (GIC_INTERNAL * 2) / 8; for_each_dist_irq_reg(irq, s->num_irq, 2) { reg = *gic_bmp_ptr32(bmp, irq); if (irq % 32 != 0) { @@ -222,6 +238,15 @@ static void kvm_dist_getbmp(GICv3State *s, uint32_t offset, uint32_t *bmp) uint32_t reg; int irq; + /* For the KVM GICv3, affinity routing is always enabled, and the + * GICD_IGROUPR0/GICD_IGRPMODR0/GICD_ISENABLER0/GICD_ISPENDR0/ + * GICD_ISACTIVER0 registers are always RAZ/WI. The corresponding + * functionality is replaced by the GICR registers. It doesn't need to sync + * them. So it should increase the offset to skip GIC_INTERNAL irqs. + * This matches the for_each_dist_irq_reg() macro which also skips the + * first GIC_INTERNAL irqs. + */ + offset += (GIC_INTERNAL * 1) / 8; for_each_dist_irq_reg(irq, s->num_irq, 1) { kvm_gicd_access(s, offset, ®, false); *gic_bmp_ptr32(bmp, irq) = reg; @@ -235,6 +260,19 @@ static void kvm_dist_putbmp(GICv3State *s, uint32_t offset, uint32_t reg; int irq; + /* For the KVM GICv3, affinity routing is always enabled, and the + * GICD_IGROUPR0/GICD_IGRPMODR0/GICD_ISENABLER0/GICD_ISPENDR0/ + * GICD_ISACTIVER0 registers are always RAZ/WI. The corresponding + * functionality is replaced by the GICR registers. It doesn't need to sync + * them. So it should increase the offset and clroffset to skip GIC_INTERNAL + * irqs. This matches the for_each_dist_irq_reg() macro which also skips the + * first GIC_INTERNAL irqs. + */ + offset += (GIC_INTERNAL * 1) / 8; + if (clroffset != 0) { + clroffset += (GIC_INTERNAL * 1) / 8; + } + for_each_dist_irq_reg(irq, s->num_irq, 1) { /* If this bitmap is a set/clear register pair, first write to the * clear-reg to clear all bits before using the set-reg to write diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index bccdfe17c6..d75b49d558 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -217,6 +217,7 @@ struct GICv3State { uint32_t revision; bool security_extn; bool irq_reset_nonsecure; + bool gicd_no_migration_shift_bug; int dev_fd; /* kvm device fd if backed by kvm vgic support */ Error *migration_blocker; From 72ee64b6a7d4500d992ad2b3780c31137a747c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1038/2380] aspeed: remove ignore_memory_transaction_failures on all boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Reviewed-by: Peter Maydell Message-id: 20180530064049.27976-2-clg@kaod.org Signed-off-by: Peter Maydell --- hw/arm/aspeed.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index a7110a712f..4d7201d74a 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -278,7 +278,6 @@ static void palmetto_bmc_class_init(ObjectClass *oc, void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; - mc->ignore_memory_transaction_failures = true; } static const TypeInfo palmetto_bmc_type = { @@ -311,7 +310,6 @@ static void ast2500_evb_class_init(ObjectClass *oc, void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; - mc->ignore_memory_transaction_failures = true; } static const TypeInfo ast2500_evb_type = { @@ -336,7 +334,6 @@ static void romulus_bmc_class_init(ObjectClass *oc, void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; - mc->ignore_memory_transaction_failures = true; } static const TypeInfo romulus_bmc_type = { From 62c2c2ebde27c8cc07d7f5dce329a5fe04bb98bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1039/2380] aspeed: add support for the witherspoon-bmc board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Witherspoon boards are OpenPOWER system hosting POWER9 Processors. Add support for their BMC including a couple of I2C devices as found on real HW. Signed-off-by: Cédric Le Goater Reviewed-by: Andrew Jeffery Message-id: 20180530064049.27976-3-clg@kaod.org Signed-off-by: Peter Maydell --- hw/arm/aspeed.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 4d7201d74a..732c034cca 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -45,6 +45,7 @@ enum { PALMETTO_BMC, AST2500_EVB, ROMULUS_BMC, + WITHERSPOON_BMC, }; /* Palmetto hardware value: 0x120CE416 */ @@ -82,8 +83,12 @@ enum { SCU_AST2500_HW_STRAP_ACPI_ENABLE | \ SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER)) +/* Witherspoon hardware value: 0xF10AD216 (but use romulus definition) */ +#define WITHERSPOON_BMC_HW_STRAP1 ROMULUS_BMC_HW_STRAP1 + static void palmetto_bmc_i2c_init(AspeedBoardState *bmc); static void ast2500_evb_i2c_init(AspeedBoardState *bmc); +static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc); static const AspeedBoardConfig aspeed_boards[] = { [PALMETTO_BMC] = { @@ -109,6 +114,14 @@ static const AspeedBoardConfig aspeed_boards[] = { .spi_model = "mx66l1g45g", .num_cs = 2, }, + [WITHERSPOON_BMC] = { + .soc_name = "ast2500-a1", + .hw_strap1 = WITHERSPOON_BMC_HW_STRAP1, + .fmc_model = "mx25l25635e", + .spi_model = "mx66l1g45g", + .num_cs = 2, + .i2c_init = witherspoon_bmc_i2c_init, + }, }; #define FIRMWARE_ADDR 0x0 @@ -342,11 +355,47 @@ static const TypeInfo romulus_bmc_type = { .class_init = romulus_bmc_class_init, }; +static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 4), "tmp423", 0x4c); + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 5), "tmp423", 0x4c); + + /* The Witherspoon expects a TMP275 but a TMP105 is compatible */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 9), "tmp105", 0x4a); +} + +static void witherspoon_bmc_init(MachineState *machine) +{ + aspeed_board_init(machine, &aspeed_boards[WITHERSPOON_BMC]); +} + +static void witherspoon_bmc_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "OpenPOWER Witherspoon BMC (ARM1176)"; + mc->init = witherspoon_bmc_init; + mc->max_cpus = 1; + mc->no_sdcard = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->no_parallel = 1; +} + +static const TypeInfo witherspoon_bmc_type = { + .name = MACHINE_TYPE_NAME("witherspoon-bmc"), + .parent = TYPE_MACHINE, + .class_init = witherspoon_bmc_class_init, +}; + static void aspeed_machine_init(void) { type_register_static(&palmetto_bmc_type); type_register_static(&ast2500_evb_type); type_register_static(&romulus_bmc_type); + type_register_static(&witherspoon_bmc_type); } type_init(aspeed_machine_init) From 6c4567c730488a667eb7d33f19c9dfcae21187bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1040/2380] aspeed: add an I2C RTC device to all machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST2500 EVB does not have an RTC but we can pretend that one is plugged on the I2C bus header. The romulus and witherspoon boards expects an Epson RX8900 I2C RTC but a ds1338 is good enough for the basic features we need. Signed-off-by: Cédric Le Goater Reviewed-by: Andrew Jeffery Message-id: 20180530064049.27976-4-clg@kaod.org Signed-off-by: Peter Maydell --- hw/arm/aspeed.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 732c034cca..73b482226f 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -88,6 +88,7 @@ enum { static void palmetto_bmc_i2c_init(AspeedBoardState *bmc); static void ast2500_evb_i2c_init(AspeedBoardState *bmc); +static void romulus_bmc_i2c_init(AspeedBoardState *bmc); static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc); static const AspeedBoardConfig aspeed_boards[] = { @@ -113,6 +114,7 @@ static const AspeedBoardConfig aspeed_boards[] = { .fmc_model = "n25q256a", .spi_model = "mx66l1g45g", .num_cs = 2, + .i2c_init = romulus_bmc_i2c_init, }, [WITHERSPOON_BMC] = { .soc_name = "ast2500-a1", @@ -305,6 +307,10 @@ static void ast2500_evb_i2c_init(AspeedBoardState *bmc) /* The AST2500 EVB expects a LM75 but a TMP105 is compatible */ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "tmp105", 0x4d); + + /* The AST2500 EVB does not have an RTC. Let's pretend that one is + * plugged on the I2C bus header */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32); } static void ast2500_evb_init(MachineState *machine) @@ -331,6 +337,15 @@ static const TypeInfo ast2500_evb_type = { .class_init = ast2500_evb_class_init, }; +static void romulus_bmc_i2c_init(AspeedBoardState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + + /* The romulus board expects Epson RX8900 I2C RTC but a ds1338 is + * good enough */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32); +} + static void romulus_bmc_init(MachineState *machine) { aspeed_board_init(machine, &aspeed_boards[ROMULUS_BMC]); @@ -364,6 +379,10 @@ static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) /* The Witherspoon expects a TMP275 but a TMP105 is compatible */ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 9), "tmp105", 0x4a); + + /* The witherspoon board expects Epson RX8900 I2C RTC but a ds1338 is + * good enough */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32); } static void witherspoon_bmc_init(MachineState *machine) From e22242141331bc829ecca194f5c06f674da618ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1041/2380] smbus: add a smbus_eeprom_init_one() routine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an helper routine to add a single EEPROM on an I2C bus. It can be directly used by smbus_eeprom_init() which adds a certain number of EEPROMs on mips and x86 machines. Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180530064049.27976-5-clg@kaod.org Signed-off-by: Peter Maydell --- hw/i2c/smbus_eeprom.c | 16 +++++++++++----- include/hw/i2c/smbus.h | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index 125c887d1f..f18aa3de35 100644 --- a/hw/i2c/smbus_eeprom.c +++ b/hw/i2c/smbus_eeprom.c @@ -139,6 +139,16 @@ static void smbus_eeprom_register_types(void) type_init(smbus_eeprom_register_types) +void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf) +{ + DeviceState *dev; + + dev = qdev_create((BusState *) smbus, "smbus-eeprom"); + qdev_prop_set_uint8(dev, "address", address); + qdev_prop_set_ptr(dev, "data", eeprom_buf); + qdev_init_nofail(dev); +} + void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, const uint8_t *eeprom_spd, int eeprom_spd_size) { @@ -149,10 +159,6 @@ void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, } for (i = 0; i < nb_eeprom; i++) { - DeviceState *eeprom; - eeprom = qdev_create((BusState *)smbus, "smbus-eeprom"); - qdev_prop_set_uint8(eeprom, "address", 0x50 + i); - qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256)); - qdev_init_nofail(eeprom); + smbus_eeprom_init_one(smbus, 0x50 + i, eeprom_buf + (i * 256)); } } diff --git a/include/hw/i2c/smbus.h b/include/hw/i2c/smbus.h index cfe3fa69f3..4fdba022c1 100644 --- a/include/hw/i2c/smbus.h +++ b/include/hw/i2c/smbus.h @@ -76,6 +76,7 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data); int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, int len); +void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf); void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, const uint8_t *eeprom_spd, int size); From 3d165f12db211c0c17e937a87551d869b25a82db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1042/2380] aspeed: Add EEPROM I2C devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Aspeed boards have at least one EEPROM to hold the Vital Product Data (VPD). Signed-off-by: Cédric Le Goater Reviewed-by: Andrew Jeffery Message-id: 20180530064049.27976-6-clg@kaod.org Signed-off-by: Peter Maydell --- hw/arm/aspeed.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 73b482226f..5d0ed3391e 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -17,6 +17,7 @@ #include "hw/arm/arm.h" #include "hw/arm/aspeed_soc.h" #include "hw/boards.h" +#include "hw/i2c/smbus.h" #include "qemu/log.h" #include "sysemu/block-backend.h" #include "hw/loader.h" @@ -263,11 +264,15 @@ static void palmetto_bmc_i2c_init(AspeedBoardState *bmc) { AspeedSoCState *soc = &bmc->soc; DeviceState *dev; + uint8_t *eeprom_buf = g_malloc0(32 * 1024); /* The palmetto platform expects a ds3231 RTC but a ds1338 is * enough to provide basic RTC features. Alarms will be missing */ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 0), "ds1338", 0x68); + smbus_eeprom_init_one(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 0), 0x50, + eeprom_buf); + /* add a TMP423 temperature sensor */ dev = i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 2), "tmp423", 0x4c); @@ -304,6 +309,10 @@ static const TypeInfo palmetto_bmc_type = { static void ast2500_evb_i2c_init(AspeedBoardState *bmc) { AspeedSoCState *soc = &bmc->soc; + uint8_t *eeprom_buf = g_malloc0(8 * 1024); + + smbus_eeprom_init_one(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 3), 0x50, + eeprom_buf); /* The AST2500 EVB expects a LM75 but a TMP105 is compatible */ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "tmp105", 0x4d); @@ -373,6 +382,7 @@ static const TypeInfo romulus_bmc_type = { static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) { AspeedSoCState *soc = &bmc->soc; + uint8_t *eeprom_buf = g_malloc0(8 * 1024); i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 4), "tmp423", 0x4c); i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 5), "tmp423", 0x4c); @@ -383,6 +393,9 @@ static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) /* The witherspoon board expects Epson RX8900 I2C RTC but a ds1338 is * good enough */ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32); + + smbus_eeprom_init_one(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), 0x51, + eeprom_buf); } static void witherspoon_bmc_init(MachineState *machine) From 5141d4158cf0dbdc13e42f175d1718d40b789f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1043/2380] misc: add pca9552 LED blinker model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specs are available here : https://www.nxp.com/docs/en/application-note/AN264.pdf This is a simple model supporting the basic registers for led and GPIO mode. The device also supports two blinking rates but not the model yet. Signed-off-by: Cédric Le Goater Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180530064049.27976-7-clg@kaod.org Signed-off-by: Peter Maydell --- default-configs/arm-softmmu.mak | 1 + hw/misc/Makefile.objs | 1 + hw/misc/pca9552.c | 240 ++++++++++++++++++++++++++++++++ include/hw/misc/pca9552.h | 32 +++++ include/hw/misc/pca9552_regs.h | 32 +++++ tests/Makefile.include | 2 + tests/libqos/i2c.h | 2 + tests/pca9552-test.c | 116 +++++++++++++++ tests/tmp105-test.c | 2 - 9 files changed, 426 insertions(+), 2 deletions(-) create mode 100644 hw/misc/pca9552.c create mode 100644 include/hw/misc/pca9552.h create mode 100644 include/hw/misc/pca9552_regs.h create mode 100644 tests/pca9552-test.c diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 8ba2558b36..7cf73d2f27 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -16,6 +16,7 @@ CONFIG_TSC2005=y CONFIG_LM832X=y CONFIG_TMP105=y CONFIG_TMP421=y +CONFIG_PCA9552=y CONFIG_STELLARIS=y CONFIG_STELLARIS_INPUT=y CONFIG_STELLARIS_ENET=y diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 00e834d0f0..ecd8d61098 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -7,6 +7,7 @@ common-obj-$(CONFIG_SGA) += sga.o common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o common-obj-$(CONFIG_EDU) += edu.o +common-obj-$(CONFIG_PCA9552) += pca9552.o common-obj-y += unimp.o common-obj-$(CONFIG_FW_CFG_DMA) += vmcoreinfo.o diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c new file mode 100644 index 0000000000..9775d5274a --- /dev/null +++ b/hw/misc/pca9552.c @@ -0,0 +1,240 @@ +/* + * PCA9552 I2C LED blinker + * + * https://www.nxp.com/docs/en/application-note/AN264.pdf + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/hw.h" +#include "hw/misc/pca9552.h" +#include "hw/misc/pca9552_regs.h" + +#define PCA9552_LED_ON 0x0 +#define PCA9552_LED_OFF 0x1 +#define PCA9552_LED_PWM0 0x2 +#define PCA9552_LED_PWM1 0x3 + +static uint8_t pca9552_pin_get_config(PCA9552State *s, int pin) +{ + uint8_t reg = PCA9552_LS0 + (pin / 4); + uint8_t shift = (pin % 4) << 1; + + return extract32(s->regs[reg], shift, 2); +} + +static void pca9552_update_pin_input(PCA9552State *s) +{ + int i; + + for (i = 0; i < s->nr_leds; i++) { + uint8_t input_reg = PCA9552_INPUT0 + (i / 8); + uint8_t input_shift = (i % 8); + uint8_t config = pca9552_pin_get_config(s, i); + + switch (config) { + case PCA9552_LED_ON: + s->regs[input_reg] |= 1 << input_shift; + break; + case PCA9552_LED_OFF: + s->regs[input_reg] &= ~(1 << input_shift); + break; + case PCA9552_LED_PWM0: + case PCA9552_LED_PWM1: + /* TODO */ + default: + break; + } + } +} + +static uint8_t pca9552_read(PCA9552State *s, uint8_t reg) +{ + switch (reg) { + case PCA9552_INPUT0: + case PCA9552_INPUT1: + case PCA9552_PSC0: + case PCA9552_PWM0: + case PCA9552_PSC1: + case PCA9552_PWM1: + case PCA9552_LS0: + case PCA9552_LS1: + case PCA9552_LS2: + case PCA9552_LS3: + return s->regs[reg]; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", + __func__, reg); + return 0xFF; + } +} + +static void pca9552_write(PCA9552State *s, uint8_t reg, uint8_t data) +{ + switch (reg) { + case PCA9552_PSC0: + case PCA9552_PWM0: + case PCA9552_PSC1: + case PCA9552_PWM1: + s->regs[reg] = data; + break; + + case PCA9552_LS0: + case PCA9552_LS1: + case PCA9552_LS2: + case PCA9552_LS3: + s->regs[reg] = data; + pca9552_update_pin_input(s); + break; + + case PCA9552_INPUT0: + case PCA9552_INPUT1: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", + __func__, reg); + } +} + +/* + * When Auto-Increment is on, the register address is incremented + * after each byte is sent to or received by the device. The index + * rollovers to 0 when the maximum register address is reached. + */ +static void pca9552_autoinc(PCA9552State *s) +{ + if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) { + uint8_t reg = s->pointer & 0xf; + + reg = (reg + 1) % (s->max_reg + 1); + s->pointer = reg | PCA9552_AUTOINC; + } +} + +static int pca9552_recv(I2CSlave *i2c) +{ + PCA9552State *s = PCA9552(i2c); + uint8_t ret; + + ret = pca9552_read(s, s->pointer & 0xf); + + /* + * From the Specs: + * + * Important Note: When a Read sequence is initiated and the + * AI bit is set to Logic Level 1, the Read Sequence MUST + * start by a register different from 0. + * + * I don't know what should be done in this case, so throw an + * error. + */ + if (s->pointer == PCA9552_AUTOINC) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Autoincrement read starting with register 0\n", + __func__); + } + + pca9552_autoinc(s); + + return ret; +} + +static int pca9552_send(I2CSlave *i2c, uint8_t data) +{ + PCA9552State *s = PCA9552(i2c); + + /* First byte sent by is the register address */ + if (s->len == 0) { + s->pointer = data; + s->len++; + } else { + pca9552_write(s, s->pointer & 0xf, data); + + pca9552_autoinc(s); + } + + return 0; +} + +static int pca9552_event(I2CSlave *i2c, enum i2c_event event) +{ + PCA9552State *s = PCA9552(i2c); + + s->len = 0; + return 0; +} + +static const VMStateDescription pca9552_vmstate = { + .name = "PCA9552", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(len, PCA9552State), + VMSTATE_UINT8(pointer, PCA9552State), + VMSTATE_UINT8_ARRAY(regs, PCA9552State, PCA9552_NR_REGS), + VMSTATE_I2C_SLAVE(i2c, PCA9552State), + VMSTATE_END_OF_LIST() + } +}; + +static void pca9552_reset(DeviceState *dev) +{ + PCA9552State *s = PCA9552(dev); + + s->regs[PCA9552_PSC0] = 0xFF; + s->regs[PCA9552_PWM0] = 0x80; + s->regs[PCA9552_PSC1] = 0xFF; + s->regs[PCA9552_PWM1] = 0x80; + s->regs[PCA9552_LS0] = 0x55; /* all OFF */ + s->regs[PCA9552_LS1] = 0x55; + s->regs[PCA9552_LS2] = 0x55; + s->regs[PCA9552_LS3] = 0x55; + + pca9552_update_pin_input(s); + + s->pointer = 0xFF; + s->len = 0; +} + +static void pca9552_initfn(Object *obj) +{ + PCA9552State *s = PCA9552(obj); + + /* If support for the other PCA955X devices are implemented, these + * constant values might be part of class structure describing the + * PCA955X device + */ + s->max_reg = PCA9552_LS3; + s->nr_leds = 16; +} + +static void pca9552_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + k->event = pca9552_event; + k->recv = pca9552_recv; + k->send = pca9552_send; + dc->reset = pca9552_reset; + dc->vmsd = &pca9552_vmstate; +} + +static const TypeInfo pca9552_info = { + .name = TYPE_PCA9552, + .parent = TYPE_I2C_SLAVE, + .instance_init = pca9552_initfn, + .instance_size = sizeof(PCA9552State), + .class_init = pca9552_class_init, +}; + +static void pca9552_register_types(void) +{ + type_register_static(&pca9552_info); +} + +type_init(pca9552_register_types) diff --git a/include/hw/misc/pca9552.h b/include/hw/misc/pca9552.h new file mode 100644 index 0000000000..ebb43c63fe --- /dev/null +++ b/include/hw/misc/pca9552.h @@ -0,0 +1,32 @@ +/* + * PCA9552 I2C LED blinker + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#ifndef PCA9552_H +#define PCA9552_H + +#include "hw/i2c/i2c.h" + +#define TYPE_PCA9552 "pca9552" +#define PCA9552(obj) OBJECT_CHECK(PCA9552State, (obj), TYPE_PCA9552) + +#define PCA9552_NR_REGS 10 + +typedef struct PCA9552State { + /*< private >*/ + I2CSlave i2c; + /*< public >*/ + + uint8_t len; + uint8_t pointer; + + uint8_t regs[PCA9552_NR_REGS]; + uint8_t max_reg; + uint8_t nr_leds; +} PCA9552State; + +#endif diff --git a/include/hw/misc/pca9552_regs.h b/include/hw/misc/pca9552_regs.h new file mode 100644 index 0000000000..d8051cfbd6 --- /dev/null +++ b/include/hw/misc/pca9552_regs.h @@ -0,0 +1,32 @@ +/* + * PCA9552 I2C LED blinker registers + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#ifndef PCA9552_REGS_H +#define PCA9552_REGS_H + +/* + * Bits [0:3] are used to address a specific register. + */ +#define PCA9552_INPUT0 0 /* read only input register 0 */ +#define PCA9552_INPUT1 1 /* read only input register 1 */ +#define PCA9552_PSC0 2 /* read/write frequency prescaler 0 */ +#define PCA9552_PWM0 3 /* read/write PWM register 0 */ +#define PCA9552_PSC1 4 /* read/write frequency prescaler 1 */ +#define PCA9552_PWM1 5 /* read/write PWM register 1 */ +#define PCA9552_LS0 6 /* read/write LED0 to LED3 selector */ +#define PCA9552_LS1 7 /* read/write LED4 to LED7 selector */ +#define PCA9552_LS2 8 /* read/write LED8 to LED11 selector */ +#define PCA9552_LS3 9 /* read/write LED12 to LED15 selector */ + +/* + * Bit [4] is used to activate the Auto-Increment option of the + * register address + */ +#define PCA9552_AUTOINC (1 << 4) + +#endif diff --git a/tests/Makefile.include b/tests/Makefile.include index 0eaa835b8a..400d8890e7 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -373,6 +373,7 @@ check-qtest-sparc64-y += tests/prom-env-test$(EXESUF) check-qtest-sparc64-y += tests/boot-serial-test$(EXESUF) check-qtest-arm-y = tests/tmp105-test$(EXESUF) +check-qtest-arm-y += tests/pca9552-test$(EXESUF) check-qtest-arm-y += tests/ds1338-test$(EXESUF) check-qtest-arm-y += tests/m25p80-test$(EXESUF) gcov-files-arm-y += hw/misc/tmp105.c @@ -778,6 +779,7 @@ tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \ tests/boot-sector.o tests/acpi-utils.o $(libqos-obj-y) tests/pxe-test$(EXESUF): tests/pxe-test.o tests/boot-sector.o $(libqos-obj-y) tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) +tests/pca9552-test$(EXESUF): tests/pca9552-test.o $(libqos-omap-obj-y) tests/ds1338-test$(EXESUF): tests/ds1338-test.o $(libqos-imx-obj-y) tests/m25p80-test$(EXESUF): tests/m25p80-test.o tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) diff --git a/tests/libqos/i2c.h b/tests/libqos/i2c.h index eb40b808bd..cc01358a9f 100644 --- a/tests/libqos/i2c.h +++ b/tests/libqos/i2c.h @@ -21,6 +21,8 @@ struct I2CAdapter { QTestState *qts; }; +#define OMAP2_I2C_1_BASE 0x48070000 + void i2c_send(I2CAdapter *i2c, uint8_t addr, const uint8_t *buf, uint16_t len); void i2c_recv(I2CAdapter *i2c, uint8_t addr, diff --git a/tests/pca9552-test.c b/tests/pca9552-test.c new file mode 100644 index 0000000000..5466a67ed7 --- /dev/null +++ b/tests/pca9552-test.c @@ -0,0 +1,116 @@ +/* + * QTest testcase for the PCA9552 LED blinker + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "libqtest.h" +#include "libqos/i2c.h" +#include "hw/misc/pca9552_regs.h" + +#define PCA9552_TEST_ID "pca9552-test" +#define PCA9552_TEST_ADDR 0x60 + +static I2CAdapter *i2c; + +static uint8_t pca9552_get8(I2CAdapter *i2c, uint8_t addr, uint8_t reg) +{ + uint8_t resp[1]; + i2c_send(i2c, addr, ®, 1); + i2c_recv(i2c, addr, resp, 1); + return resp[0]; +} + +static void pca9552_set8(I2CAdapter *i2c, uint8_t addr, uint8_t reg, + uint8_t value) +{ + uint8_t cmd[2]; + uint8_t resp[1]; + + cmd[0] = reg; + cmd[1] = value; + i2c_send(i2c, addr, cmd, 2); + i2c_recv(i2c, addr, resp, 1); + g_assert_cmphex(resp[0], ==, cmd[1]); +} + +static void receive_autoinc(void) +{ + uint8_t resp; + uint8_t reg = PCA9552_LS0 | PCA9552_AUTOINC; + + i2c_send(i2c, PCA9552_TEST_ADDR, ®, 1); + + /* PCA9552_LS0 */ + i2c_recv(i2c, PCA9552_TEST_ADDR, &resp, 1); + g_assert_cmphex(resp, ==, 0x54); + + /* PCA9552_LS1 */ + i2c_recv(i2c, PCA9552_TEST_ADDR, &resp, 1); + g_assert_cmphex(resp, ==, 0x55); + + /* PCA9552_LS2 */ + i2c_recv(i2c, PCA9552_TEST_ADDR, &resp, 1); + g_assert_cmphex(resp, ==, 0x55); + + /* PCA9552_LS3 */ + i2c_recv(i2c, PCA9552_TEST_ADDR, &resp, 1); + g_assert_cmphex(resp, ==, 0x54); +} + +static void send_and_receive(void) +{ + uint8_t value; + + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_LS0); + g_assert_cmphex(value, ==, 0x55); + + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_INPUT0); + g_assert_cmphex(value, ==, 0x0); + + /* Switch on LED 0 */ + pca9552_set8(i2c, PCA9552_TEST_ADDR, PCA9552_LS0, 0x54); + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_LS0); + g_assert_cmphex(value, ==, 0x54); + + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_INPUT0); + g_assert_cmphex(value, ==, 0x01); + + /* Switch on LED 12 */ + pca9552_set8(i2c, PCA9552_TEST_ADDR, PCA9552_LS3, 0x54); + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_LS3); + g_assert_cmphex(value, ==, 0x54); + + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_INPUT1); + g_assert_cmphex(value, ==, 0x10); +} + +int main(int argc, char **argv) +{ + QTestState *s = NULL; + int ret; + + g_test_init(&argc, &argv, NULL); + + s = qtest_start("-machine n800 " + "-device pca9552,bus=i2c-bus.0,id=" PCA9552_TEST_ID + ",address=0x60"); + i2c = omap_i2c_create(s, OMAP2_I2C_1_BASE); + + qtest_add_func("/pca9552/tx-rx", send_and_receive); + qtest_add_func("/pca9552/rx-autoinc", receive_autoinc); + + ret = g_test_run(); + + if (s) { + qtest_quit(s); + } + g_free(i2c); + + return ret; +} diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c index d093cffe1e..34cae7a582 100644 --- a/tests/tmp105-test.c +++ b/tests/tmp105-test.c @@ -14,8 +14,6 @@ #include "qapi/qmp/qdict.h" #include "hw/misc/tmp105_regs.h" -#define OMAP2_I2C_1_BASE 0x48070000 - #define TMP105_TEST_ID "tmp105-test" #define TMP105_TEST_ADDR 0x49 From 8c9a61d7e0eae793df282894813074f3f43be97d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1044/2380] aspeed: add the pc9552 chips to the witherspoon machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pca9552 LED blinkers on the Witherspoon machine are used for leds but also as GPIOs to control fans and GPUs. Signed-off-by: Cédric Le Goater Reviewed-by: Andrew Jeffery Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180530064049.27976-8-clg@kaod.org Signed-off-by: Peter Maydell --- hw/arm/aspeed.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 5d0ed3391e..bb9d33848d 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -384,6 +384,8 @@ static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) AspeedSoCState *soc = &bmc->soc; uint8_t *eeprom_buf = g_malloc0(8 * 1024); + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 3), "pca9552", 0x60); + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 4), "tmp423", 0x4c); i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 5), "tmp423", 0x4c); @@ -396,6 +398,8 @@ static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) smbus_eeprom_init_one(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), 0x51, eeprom_buf); + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "pca9552", + 0x60); } static void witherspoon_bmc_init(MachineState *machine) From cd679a76a0ec8e634922454ab26b7ef8d2d114b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1045/2380] ftgmac100: compute maximum frame size depending on the protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The maximum frame size includes the CRC and depends if a VLAN tag is inserted or not. Adjust the frame size limit in the transmit handler using on the FTGMAC100State buffer size and in the receive handler use the packet protocol. Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180530061711.23673-2-clg@kaod.org Signed-off-by: Peter Maydell --- hw/net/ftgmac100.c | 23 ++++++++++++----------- include/hw/net/ftgmac100.h | 7 ++++++- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 3300e8ef4a..425ac36cff 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -207,16 +207,18 @@ typedef struct { /* * Max frame size for the receiving buffer */ -#define FTGMAC100_MAX_FRAME_SIZE 10240 +#define FTGMAC100_MAX_FRAME_SIZE 9220 /* Limits depending on the type of the frame * * 9216 for Jumbo frames (+ 4 for VLAN) * 1518 for other frames (+ 4 for VLAN) */ -static int ftgmac100_max_frame_size(FTGMAC100State *s) +static int ftgmac100_max_frame_size(FTGMAC100State *s, uint16_t proto) { - return (s->maccr & FTGMAC100_MACCR_JUMBO_LF ? 9216 : 1518) + 4; + int max = (s->maccr & FTGMAC100_MACCR_JUMBO_LF ? 9216 : 1518); + + return max + (proto == ETH_P_VLAN ? 4 : 0); } static void ftgmac100_update_irq(FTGMAC100State *s) @@ -408,7 +410,6 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, uint8_t *ptr = s->frame; uint32_t addr = tx_descriptor; uint32_t flags = 0; - int max_frame_size = ftgmac100_max_frame_size(s); while (1) { FTGMAC100Desc bd; @@ -427,11 +428,12 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, flags = bd.des1; } - len = bd.des0 & 0x3FFF; - if (frame_size + len > max_frame_size) { + len = FTGMAC100_TXDES0_TXBUF_SIZE(bd.des0); + if (frame_size + len > sizeof(s->frame)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %d bytes\n", __func__, len); - len = max_frame_size - frame_size; + s->isr |= FTGMAC100_INT_XPKT_LOST; + len = sizeof(s->frame) - frame_size; } if (dma_memory_read(&address_space_memory, bd.des3, ptr, len)) { @@ -788,7 +790,8 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, uint32_t buf_len; size_t size = len; uint32_t first = FTGMAC100_RXDES0_FRS; - int max_frame_size = ftgmac100_max_frame_size(s); + uint16_t proto = be16_to_cpu(PKT_GET_ETH_HDR(buf)->h_proto); + int max_frame_size = ftgmac100_max_frame_size(s, proto); if ((s->maccr & (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) != (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) { @@ -820,9 +823,9 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, /* Huge frames are truncated. */ if (size > max_frame_size) { - size = max_frame_size; qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %zd bytes\n", __func__, size); + size = max_frame_size; flags |= FTGMAC100_RXDES0_FTL; } @@ -940,8 +943,6 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp) object_get_typename(OBJECT(dev)), DEVICE(dev)->id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - s->frame = g_malloc(FTGMAC100_MAX_FRAME_SIZE); } static const VMStateDescription vmstate_ftgmac100 = { diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h index d9bc589fbf..94cfe05332 100644 --- a/include/hw/net/ftgmac100.h +++ b/include/hw/net/ftgmac100.h @@ -16,6 +16,11 @@ #include "hw/sysbus.h" #include "net/net.h" +/* + * Max frame size for the receiving buffer + */ +#define FTGMAC100_MAX_FRAME_SIZE 9220 + typedef struct FTGMAC100State { /*< private >*/ SysBusDevice parent_obj; @@ -26,7 +31,7 @@ typedef struct FTGMAC100State { qemu_irq irq; MemoryRegion iomem; - uint8_t *frame; + uint8_t frame[FTGMAC100_MAX_FRAME_SIZE]; uint32_t irq_state; uint32_t isr; From 8576b12df7abf29ddff0e69e1f9b6534e040cbcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1046/2380] ftgmac100: add IEEE 802.1Q VLAN support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ftgmac100 NIC supports VLAN tag insertion and the MAC engine also has a control to remove VLAN tags from received packets. The VLAN control bits and VLAN tag information are contained in the second word of the transmit and receive descriptors. The Insert VLAN bit and the VLAN Tag available bit are only valid in the first segment of the packet. Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180530061711.23673-3-clg@kaod.org Signed-off-by: Peter Maydell --- hw/net/ftgmac100.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 425ac36cff..abf80655f2 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -443,6 +443,22 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, break; } + /* Check for VLAN */ + if (bd.des0 & FTGMAC100_TXDES0_FTS && + bd.des1 & FTGMAC100_TXDES1_INS_VLANTAG && + be16_to_cpu(PKT_GET_ETH_HDR(ptr)->h_proto) != ETH_P_VLAN) { + if (frame_size + len + 4 > sizeof(s->frame)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %d bytes\n", + __func__, len); + s->isr |= FTGMAC100_INT_XPKT_LOST; + len = sizeof(s->frame) - frame_size - 4; + } + memmove(ptr + 16, ptr + 12, len - 12); + stw_be_p(ptr + 12, ETH_P_VLAN); + stw_be_p(ptr + 14, bd.des1); + len += 4; + } + ptr += len; frame_size += len; if (bd.des0 & FTGMAC100_TXDES0_LTS) { @@ -864,7 +880,20 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, buf_len += size - 4; } buf_addr = bd.des3; - dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + if (first && proto == ETH_P_VLAN && buf_len >= 18) { + bd.des1 = lduw_be_p(buf + 14) | FTGMAC100_RXDES1_VLANTAG_AVAIL; + + if (s->maccr & FTGMAC100_MACCR_RM_VLAN) { + dma_memory_write(&address_space_memory, buf_addr, buf, 12); + dma_memory_write(&address_space_memory, buf_addr + 12, buf + 16, + buf_len - 16); + } else { + dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + } + } else { + bd.des1 = 0; + dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + } buf += buf_len; if (size < 4) { dma_memory_write(&address_space_memory, buf_addr + buf_len, From 44effc1f99d0375fe5939f99bd65a5b6037f5f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1047/2380] ftgmac100: fix multicast hash routine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on the multicast hash calculation of the FTGMAC100 Linux driver. Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180530061711.23673-4-clg@kaod.org Signed-off-by: Peter Maydell --- hw/net/ftgmac100.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index abf80655f2..8a7f274dc1 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -776,8 +776,8 @@ static int ftgmac100_filter(FTGMAC100State *s, const uint8_t *buf, size_t len) return 0; } - /* TODO: this does not seem to work for ftgmac100 */ - mcast_idx = net_crc32(buf, ETH_ALEN) >> 26; + mcast_idx = net_crc32_le(buf, ETH_ALEN); + mcast_idx = (~(mcast_idx >> 2)) & 0x3f; if (!(s->math[mcast_idx / 32] & (1 << (mcast_idx % 32)))) { return 0; } From 99a00e55c7dbf2fe622e77d6f7f563b8a5fe5d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1048/2380] ftgmac100: remove check on runt messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a ethernet wire limitation not needed in emulation. It breaks U-Boot n/w stack also. Signed-off-by: Cédric Le Goater Message-id: 20180530061711.23673-5-clg@kaod.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/net/ftgmac100.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 8a7f274dc1..909c1182ee 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -822,12 +822,6 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, return size; } - if (size < 64 && !(s->maccr & FTGMAC100_MACCR_RX_RUNT)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: dropped runt frame of %zd bytes\n", - __func__, size); - return size; - } - if (!ftgmac100_filter(s, buf, size)) { return size; } From da969774af548f7b75c0ec1672787fe8cebd1257 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 8 Jun 2018 13:15:32 +0100 Subject: [PATCH 1049/2380] hw/arm: Remove the deprecated xlnx-ep108 machine It has been marked as deprecated since QEMU v2.11, so it is time to remove this now. The xlnx-zcu102 machine is very much the same and can be used as a replacement instead. Signed-off-by: Thomas Huth Reviewed-by: Alistair Francis Signed-off-by: Peter Maydell --- hw/arm/xlnx-zcu102.c | 62 ++------------------------------------------ qemu-doc.texi | 5 ---- 2 files changed, 2 insertions(+), 65 deletions(-) diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index c70278c8c1..f26fd8eb91 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -39,10 +39,6 @@ typedef struct XlnxZCU102 { #define ZCU102_MACHINE(obj) \ OBJECT_CHECK(XlnxZCU102, (obj), TYPE_ZCU102_MACHINE) -#define TYPE_EP108_MACHINE MACHINE_TYPE_NAME("xlnx-ep108") -#define EP108_MACHINE(obj) \ - OBJECT_CHECK(XlnxZCU102, (obj), TYPE_EP108_MACHINE) - static struct arm_boot_info xlnx_zcu102_binfo; static bool zcu102_get_secure(Object *obj, Error **errp) @@ -73,8 +69,9 @@ static void zcu102_set_virt(Object *obj, bool value, Error **errp) s->virt = value; } -static void xlnx_zynqmp_init(XlnxZCU102 *s, MachineState *machine) +static void xlnx_zcu102_init(MachineState *machine) { + XlnxZCU102 *s = ZCU102_MACHINE(machine); int i; uint64_t ram_size = machine->ram_size; @@ -183,60 +180,6 @@ static void xlnx_zynqmp_init(XlnxZCU102 *s, MachineState *machine) arm_load_kernel(s->soc.boot_cpu_ptr, &xlnx_zcu102_binfo); } -static void xlnx_ep108_init(MachineState *machine) -{ - XlnxZCU102 *s = EP108_MACHINE(machine); - - if (!qtest_enabled()) { - info_report("The Xilinx EP108 machine is deprecated, please use the " - "ZCU102 machine (which has the same features) instead."); - } - - xlnx_zynqmp_init(s, machine); -} - -static void xlnx_ep108_machine_instance_init(Object *obj) -{ - XlnxZCU102 *s = EP108_MACHINE(obj); - - /* EP108, we don't support setting secure or virt */ - s->secure = false; - s->virt = false; -} - -static void xlnx_ep108_machine_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Xilinx ZynqMP EP108 board (Deprecated, please use xlnx-zcu102)"; - mc->init = xlnx_ep108_init; - mc->block_default_type = IF_IDE; - mc->units_per_default_bus = 1; - mc->ignore_memory_transaction_failures = true; - mc->max_cpus = XLNX_ZYNQMP_NUM_APU_CPUS + XLNX_ZYNQMP_NUM_RPU_CPUS; - mc->default_cpus = XLNX_ZYNQMP_NUM_APU_CPUS; -} - -static const TypeInfo xlnx_ep108_machine_init_typeinfo = { - .name = MACHINE_TYPE_NAME("xlnx-ep108"), - .parent = TYPE_MACHINE, - .class_init = xlnx_ep108_machine_class_init, - .instance_init = xlnx_ep108_machine_instance_init, - .instance_size = sizeof(XlnxZCU102), -}; - -static void xlnx_ep108_machine_init_register_types(void) -{ - type_register_static(&xlnx_ep108_machine_init_typeinfo); -} - -static void xlnx_zcu102_init(MachineState *machine) -{ - XlnxZCU102 *s = ZCU102_MACHINE(machine); - - xlnx_zynqmp_init(s, machine); -} - static void xlnx_zcu102_machine_instance_init(Object *obj) { XlnxZCU102 *s = ZCU102_MACHINE(obj); @@ -289,4 +232,3 @@ static void xlnx_zcu102_machine_init_register_types(void) } type_init(xlnx_zcu102_machine_init_register_types) -type_init(xlnx_ep108_machine_init_register_types) diff --git a/qemu-doc.texi b/qemu-doc.texi index f00706b999..2effe66d6b 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2965,11 +2965,6 @@ support page sizes < 4096 any longer. @section System emulator machines -@subsection Xilinx EP108 (since 2.11.0) - -The ``xlnx-ep108'' machine has been replaced by the ``xlnx-zcu102'' machine. -The ``xlnx-zcu102'' machine has the same features and capabilites in QEMU. - @section Block device options @subsection "backing": "" (since 2.12.0) From 08bb9b347b9597252338a599421818f4a4e333cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1050/2380] hw/i2c: Add trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180606191801.6331-1-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- Makefile.objs | 1 + hw/i2c/core.c | 25 ++++++++++++++++++------- hw/i2c/trace-events | 7 +++++++ 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 hw/i2c/trace-events diff --git a/Makefile.objs b/Makefile.objs index 2c8cb72407..7a9828da28 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -213,6 +213,7 @@ trace-events-subdirs += hw/char trace-events-subdirs += hw/display trace-events-subdirs += hw/dma trace-events-subdirs += hw/hppa +trace-events-subdirs += hw/i2c trace-events-subdirs += hw/i386 trace-events-subdirs += hw/i386/xen trace-events-subdirs += hw/ide diff --git a/hw/i2c/core.c b/hw/i2c/core.c index ab72d5bf2b..b54725985a 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" +#include "trace.h" #define I2C_BROADCAST 0x00 @@ -130,14 +131,16 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) } QLIST_FOREACH(node, &bus->current_devs, next) { + I2CSlave *s = node->elt; int rv; - sc = I2C_SLAVE_GET_CLASS(node->elt); + sc = I2C_SLAVE_GET_CLASS(s); /* If the bus is already busy, assume this is a repeated start condition. */ if (sc->event) { - rv = sc->event(node->elt, recv ? I2C_START_RECV : I2C_START_SEND); + trace_i2c_event("start", s->address); + rv = sc->event(s, recv ? I2C_START_RECV : I2C_START_SEND); if (rv && !bus->broadcast) { if (bus_scanned) { /* First call, terminate the transfer. */ @@ -156,9 +159,11 @@ void i2c_end_transfer(I2CBus *bus) I2CNode *node, *next; QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { - sc = I2C_SLAVE_GET_CLASS(node->elt); + I2CSlave *s = node->elt; + sc = I2C_SLAVE_GET_CLASS(s); if (sc->event) { - sc->event(node->elt, I2C_FINISH); + trace_i2c_event("finish", s->address); + sc->event(s, I2C_FINISH); } QLIST_REMOVE(node, next); g_free(node); @@ -169,14 +174,17 @@ void i2c_end_transfer(I2CBus *bus) int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send) { I2CSlaveClass *sc; + I2CSlave *s; I2CNode *node; int ret = 0; if (send) { QLIST_FOREACH(node, &bus->current_devs, next) { - sc = I2C_SLAVE_GET_CLASS(node->elt); + s = node->elt; + sc = I2C_SLAVE_GET_CLASS(s); if (sc->send) { - ret = ret || sc->send(node->elt, *data); + trace_i2c_send(s->address, *data); + ret = ret || sc->send(s, *data); } else { ret = -1; } @@ -189,7 +197,9 @@ int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send) sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt); if (sc->recv) { - ret = sc->recv(QLIST_FIRST(&bus->current_devs)->elt); + s = QLIST_FIRST(&bus->current_devs)->elt; + ret = sc->recv(s); + trace_i2c_recv(s->address, ret); if (ret < 0) { return ret; } else { @@ -226,6 +236,7 @@ void i2c_nack(I2CBus *bus) QLIST_FOREACH(node, &bus->current_devs, next) { sc = I2C_SLAVE_GET_CLASS(node->elt); if (sc->event) { + trace_i2c_event("nack", node->elt->address); sc->event(node->elt, I2C_NACK); } } diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events new file mode 100644 index 0000000000..d339b61202 --- /dev/null +++ b/hw/i2c/trace-events @@ -0,0 +1,7 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# hw/i2c/core.c + +i2c_event(const char *event, uint8_t address) "%s(addr:0x%02x)" +i2c_send(uint8_t address, uint8_t data) "send(addr:0x%02x) data:0x%02x" +i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x" From c78d6a6466d6b76734b5ad59bf3f69d7369eff05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1051/2380] hw/sd/milkymist-memcard: Add trailing '\n' to qemu_log() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180606152128.449-2-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sd/milkymist-memcard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c index fe1cccca76..fcbccf54ea 100644 --- a/hw/sd/milkymist-memcard.c +++ b/hw/sd/milkymist-memcard.c @@ -140,7 +140,7 @@ static uint64_t memcard_read(void *opaque, hwaddr addr, r = s->response[s->response_read_ptr++]; if (s->response_read_ptr > s->response_len) { qemu_log_mask(LOG_GUEST_ERROR, "milkymist_memcard: " - "read more cmd bytes than available. Clipping."); + "read more cmd bytes than available: clipping\n"); s->response_read_ptr = 0; } } From 428d42ce23758b1bf7c7208873c1b1d7fccce2ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1052/2380] hw/digic: Add trailing '\n' to qemu_log() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180606152128.449-3-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/char/digic-uart.c | 4 ++-- hw/timer/digic-timer.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 6ebcb87a40..ccc75eaa4d 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -60,7 +60,7 @@ static uint64_t digic_uart_read(void *opaque, hwaddr addr, default: qemu_log_mask(LOG_UNIMP, "digic-uart: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); + TARGET_FMT_plx "\n", addr << 2); } return ret; @@ -98,7 +98,7 @@ static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value, default: qemu_log_mask(LOG_UNIMP, "digic-uart: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); + TARGET_FMT_plx "\n", addr << 2); } } diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c index e1fcf73c3e..4d73077207 100644 --- a/hw/timer/digic-timer.c +++ b/hw/timer/digic-timer.c @@ -73,7 +73,7 @@ static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size) default: qemu_log_mask(LOG_UNIMP, "digic-timer: read access to unknown register 0x" - TARGET_FMT_plx, offset); + TARGET_FMT_plx "\n", offset); } return ret; @@ -109,7 +109,7 @@ static void digic_timer_write(void *opaque, hwaddr offset, default: qemu_log_mask(LOG_UNIMP, "digic-timer: read access to unknown register 0x" - TARGET_FMT_plx, offset); + TARGET_FMT_plx "\n", offset); } } From f2bbb6862820dfd9fc2db4ad7ee13704a1bc2c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1053/2380] xilinx-dp: Add trailing '\n' to qemu_log() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20180606152128.449-4-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/display/xlnx_dp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 6715b9cc2b..c32ab083f8 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1074,7 +1074,9 @@ static void xlnx_dp_avbufm_write(void *opaque, hwaddr offset, uint64_t value, case AV_BUF_STC_SNAPSHOT1: case AV_BUF_HCOUNT_VCOUNT_INT0: case AV_BUF_HCOUNT_VCOUNT_INT1: - qemu_log_mask(LOG_UNIMP, "avbufm: unimplmented"); + qemu_log_mask(LOG_UNIMP, "avbufm: unimplemented register 0x%04" + PRIx64 "\n", + offset << 2); break; default: s->avbufm_registers[offset] = value; From c7e71a182d8212919060a5775a9208ed195c67ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1054/2380] ppc/pnv: Add trailing '\n' to qemu_log() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Acked-by: David Gibson Message-id: 20180606152128.449-5-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/ppc/pnv_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index cbb64ad9e7..13ad7d9e04 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -97,7 +97,7 @@ static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr, val = 0x24f000000000000ull; break; default: - qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx, + qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx "\n", addr); } @@ -107,7 +107,7 @@ static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr, static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned int width) { - qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx, + qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx "\n", addr); } From 697f4958ae9619c960117e04dfb5739914b88bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1055/2380] hw/core/register: Add trailing '\n' to qemu_log() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20180606152128.449-6-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/core/register.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/register.c b/hw/core/register.c index 0741a1af32..d2d1636250 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -96,7 +96,7 @@ void register_write(RegisterInfo *reg, uint64_t val, uint64_t we, if (test) { qemu_log_mask(LOG_UNIMP, "%s:%s writing %#" PRIx64 " to unimplemented bits:" \ - " %#" PRIx64 "", + " %#" PRIx64 "\n", prefix, reg->access->name, val, ac->unimp); } From c4c98835c6ef2778014ff583d42a38f395a7cc08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1056/2380] hw/mips/boston: Add trailing '\n' to qemu_log() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180606152128.449-7-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/mips/boston.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 5302e5c885..52cce19766 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -176,7 +176,7 @@ static uint64_t boston_platreg_read(void *opaque, hwaddr addr, uint32_t gic_freq, val; if (size != 4) { - qemu_log_mask(LOG_UNIMP, "%uB platform register read", size); + qemu_log_mask(LOG_UNIMP, "%uB platform register read\n", size); return 0; } @@ -205,7 +205,7 @@ static uint64_t boston_platreg_read(void *opaque, hwaddr addr, val |= PLAT_DDR_CFG_MHZ; return val; default: - qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" HWADDR_PRIx, + qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" HWADDR_PRIx "\n", addr & 0xffff); return 0; } @@ -215,7 +215,7 @@ static void boston_platreg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { if (size != 4) { - qemu_log_mask(LOG_UNIMP, "%uB platform register write", size); + qemu_log_mask(LOG_UNIMP, "%uB platform register write\n", size); return; } @@ -237,7 +237,7 @@ static void boston_platreg_write(void *opaque, hwaddr addr, break; default: qemu_log_mask(LOG_UNIMP, "Write platform register 0x%" HWADDR_PRIx - " = 0x%" PRIx64, addr & 0xffff, val); + " = 0x%" PRIx64 "\n", addr & 0xffff, val); break; } } From 9492e4b2de7bb25bd2c9a1697e90307cc8a29b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1057/2380] stellaris: Add trailing '\n' to qemu_log() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180606152128.449-8-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index e886f54976..502a20842c 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -203,11 +203,11 @@ static uint64_t gptm_read(void *opaque, hwaddr offset, return s->rtc; } qemu_log_mask(LOG_UNIMP, - "GPTM: read of TAR but timer read not supported"); + "GPTM: read of TAR but timer read not supported\n"); return 0; case 0x4c: /* TBR */ qemu_log_mask(LOG_UNIMP, - "GPTM: read of TBR but timer read not supported"); + "GPTM: read of TBR but timer read not supported\n"); return 0; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -836,11 +836,12 @@ static void stellaris_i2c_write(void *opaque, hwaddr offset, break; case 0x20: /* MCR */ if (value & 1) { - qemu_log_mask(LOG_UNIMP, "stellaris_i2c: Loopback not implemented"); + qemu_log_mask(LOG_UNIMP, + "stellaris_i2c: Loopback not implemented\n"); } if (value & 0x20) { qemu_log_mask(LOG_UNIMP, - "stellaris_i2c: Slave mode not implemented"); + "stellaris_i2c: Slave mode not implemented\n"); } s->mcr = value & 0x31; break; @@ -1124,7 +1125,7 @@ static void stellaris_adc_write(void *opaque, hwaddr offset, s->sspri = value; break; case 0x28: /* PSSI */ - qemu_log_mask(LOG_UNIMP, "ADC: sample initiate unimplemented"); + qemu_log_mask(LOG_UNIMP, "ADC: sample initiate unimplemented\n"); break; case 0x30: /* SAC */ s->sac = value; From 0221c8fdf2321137b34bd4dbb2557d0725739822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1058/2380] target/arm: Add trailing '\n' to qemu_log() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180606152128.449-9-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index f75aa6e9ca..1248d84e6f 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4570,7 +4570,7 @@ void hw_breakpoint_update(ARMCPU *cpu, int n) case 4: /* unlinked address mismatch (reserved if AArch64) */ case 5: /* linked address mismatch (reserved if AArch64) */ qemu_log_mask(LOG_UNIMP, - "arm: address mismatch breakpoint types not implemented"); + "arm: address mismatch breakpoint types not implemented\n"); return; case 0: /* unlinked address match */ case 1: /* linked address match */ @@ -4604,7 +4604,7 @@ void hw_breakpoint_update(ARMCPU *cpu, int n) case 8: /* unlinked VMID match (reserved if no EL2) */ case 10: /* unlinked context ID and VMID match (reserved if no EL2) */ qemu_log_mask(LOG_UNIMP, - "arm: unlinked context breakpoint types not implemented"); + "arm: unlinked context breakpoint types not implemented\n"); return; case 9: /* linked VMID match (reserved if no EL2) */ case 11: /* linked context ID and VMID match (reserved if no EL2) */ From 21528149eba03fc17f428a8e1660d8666683ca85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1059/2380] target/m68k: Add trailing '\n' to qemu_log() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-id: 20180606152128.449-10-f4bug@amsat.org Signed-off-by: Peter Maydell --- target/m68k/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 37d6ffd853..4b5dbdb51c 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -1556,7 +1556,7 @@ DISAS_INSN(undef) /* ??? This is both instructions that are as yet unimplemented for the 680x0 series, as well as those that are implemented but actually illegal for CPU32 or pre-68020. */ - qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %08x", + qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %08x\n", insn, s->insn_pc); gen_exception(s, s->insn_pc, EXCP_UNSUPPORTED); } From 0cd3f644feda1fc6c7c370c97a91df4552a69548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1060/2380] RISC-V: Add trailing '\n' to qemu_log() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180606152128.449-11-f4bug@amsat.org Signed-off-by: Peter Maydell --- target/riscv/op_helper.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 3abf52453c..aec7558e1b 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -293,7 +293,8 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, if ((val_to_write & 3) == 0) { env->stvec = val_to_write >> 2 << 2; } else { - qemu_log_mask(LOG_UNIMP, "CSR_STVEC: vectored traps not supported"); + qemu_log_mask(LOG_UNIMP, + "CSR_STVEC: vectored traps not supported\n"); } break; case CSR_SCOUNTEREN: @@ -320,7 +321,8 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, if ((val_to_write & 3) == 0) { env->mtvec = val_to_write >> 2 << 2; } else { - qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: vectored traps not supported"); + qemu_log_mask(LOG_UNIMP, + "CSR_MTVEC: vectored traps not supported\n"); } break; case CSR_MCOUNTEREN: From 8e96f594416a22f4aa6d38c0832f22ef6fc584c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1061/2380] target/xtensa: Add trailing '\n' to qemu_log() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Acked-by: Max Filippov Message-id: 20180606152128.449-12-f4bug@amsat.org Signed-off-by: Peter Maydell --- target/xtensa/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 89db23852b..a11162eebe 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -2234,7 +2234,7 @@ static void translate_rur(DisasContext *dc, const uint32_t arg[], if (uregnames[par[0]].name) { tcg_gen_mov_i32(cpu_R[arg[0]], cpu_UR[par[0]]); } else { - qemu_log_mask(LOG_UNIMP, "RUR %d not implemented, ", par[0]); + qemu_log_mask(LOG_UNIMP, "RUR %d not implemented\n", par[0]); } } } @@ -2375,7 +2375,7 @@ static void translate_slli(DisasContext *dc, const uint32_t arg[], { if (gen_window_check2(dc, arg[0], arg[1])) { if (arg[2] == 32) { - qemu_log_mask(LOG_GUEST_ERROR, "slli a%d, a%d, 32 is undefined", + qemu_log_mask(LOG_GUEST_ERROR, "slli a%d, a%d, 32 is undefined\n", arg[0], arg[1]); } tcg_gen_shli_i32(cpu_R[arg[0]], cpu_R[arg[1]], arg[2] & 0x1f); @@ -2571,7 +2571,7 @@ static void translate_wur(DisasContext *dc, const uint32_t arg[], if (uregnames[par[0]].name) { gen_wur(par[0], cpu_R[arg[0]]); } else { - qemu_log_mask(LOG_UNIMP, "WUR %d not implemented, ", par[0]); + qemu_log_mask(LOG_UNIMP, "WUR %d not implemented\n", par[0]); } } } From 9e1245795f99897d8ec4f49281938032de272536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1062/2380] sdcard: Update the Configuration Register (SCR) to Spec Version 1.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The initial implementation is based on the Specs v1.10 (see a1bb27b1e98). However the SCR is anouncing the card being v1.01. The new chapters added in version 1.10 are: 4.3.10 Switch function command Switch function command (CMD6) 1 is used to switch or expand memory card functions. [...] This is a new feature, introduced in SD physical Layer Specification Version 1.10. Therefore, cards that are compatible with earlier versions of the spec do not support it. The host shall check the "SD_SPEC" field in the SCR register to recognize what version of the spec the card complies with before using CMD6. It is mandatory for SD memory card of Ver1.10 to support CMD6. 4.3.11 High-Speed mode (25MB/sec interface speed) Though the Rev 1.01 SD memory card supports up to 12.5MB/sec interface speed, the speed of 25MB/sec is necessary to support increasing performance needs of the host and because of memory size which continues to grow. To achieve 25MB/sec interface speed, clock rate is increased to 50MHz and CLK/CMD/DAT signal timing and circuit conditions are reconsidered and changed from Physical Layer Specification Version 1.01. 4.3.12 Command system (This chapter is newly added in version 1.10) SD commands CMD34-37, CMD50, CMD57 are reserved for SD command system expansion via the switch command. [These commands] will be considered as illegal commands (as defined in revision 1.01 of the SD physical layer specification). The SWITCH_FUNCTION is implemented since the first commit, a1bb27b1e98. The 25MB/sec High-Speed mode was already updated in d7ecb867529. The current implementation does not implements CMD34-37, CMD50 and CMD57, thus these commands already return ILLEGAL. With this patch, the SCR register now matches the description of the header: * SD Memory Card emulation as defined in the "SD Memory Card Physical * layer specification, Version 1.10." Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180607180641.874-2-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sd/sd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 7af19fa06c..e1218d1fb6 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -310,8 +310,8 @@ static void sd_ocr_powerup(void *opaque) static void sd_set_scr(SDState *sd) { - sd->scr[0] = (0 << 4) /* SCR version 1.0 */ - | 0; /* Spec Versions 1.0 and 1.01 */ + sd->scr[0] = (0 << 4) /* SCR structure version 1.0 */ + | 1; /* Spec Version 1.10 */ sd->scr[1] = (2 << 4) /* SDSC Card (Security Version 1.01) */ | 0b0101; /* 1-bit or 4-bit width bus modes */ sd->scr[2] = 0x00; /* Extended Security is not supported. */ From e2fce16edba8736eed4f18c4732ac80b7edaa0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1063/2380] sdcard: Allow commands valid in SPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the "Physical Layer Simplified Specification Version 1.10" Chapter 7.3 "SPI Mode Transaction Packets" Table 57: "Commands and arguments" Signed-off-by: Philippe Mathieu-Daudé Acked-by: Alistair Francis Message-id: 20180607180641.874-3-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sd/sd.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index e1218d1fb6..80e70dd93e 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -960,8 +960,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_illegal; case 6: /* CMD6: SWITCH_FUNCTION */ - if (sd->spi) - goto bad_cmd; switch (sd->mode) { case sd_data_transfer_mode: sd_function_switch(sd, req.arg); @@ -1190,9 +1188,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Block write commands (Class 4) */ case 24: /* CMD24: WRITE_SINGLE_BLOCK */ - if (sd->spi) { - goto unimplemented_spi_cmd; - } switch (sd->state) { case sd_transfer_state: /* Writing in SPI mode not implemented. */ @@ -1217,9 +1212,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ - if (sd->spi) { - goto unimplemented_spi_cmd; - } switch (sd->state) { case sd_transfer_state: /* Writing in SPI mode not implemented. */ @@ -1259,9 +1251,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 27: /* CMD27: PROGRAM_CSD */ - if (sd->spi) { - goto unimplemented_spi_cmd; - } switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -1371,9 +1360,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Lock card commands (Class 7) */ case 42: /* CMD42: LOCK_UNLOCK */ - if (sd->spi) { - goto unimplemented_spi_cmd; - } switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; From 2f0939c2342f0dbe5d3abb264cf5e6cd6636375a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:33 +0100 Subject: [PATCH 1064/2380] sdcard: Add a 'spec_version' property, default to Spec v2.00 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of this commit, the Spec v1 is not working, and all controllers expect the cards to be conformant to Spec v2. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20180607180641.874-4-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/sd/sd.c | 23 ++++++++++++++++++++--- include/hw/sd/sd.h | 5 +++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 80e70dd93e..1ae085de69 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1,9 +1,10 @@ /* * SD Memory Card emulation as defined in the "SD Memory Card Physical - * layer specification, Version 1.10." + * layer specification, Version 2.00." * * Copyright (c) 2006 Andrzej Zaborowski * Copyright (c) 2007 CodeSourcery + * Copyright (c) 2018 Philippe Mathieu-Daudé * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -91,6 +92,7 @@ struct SDState { uint8_t sd_status[64]; /* Configurable properties */ + uint8_t spec_version; BlockBackend *blk; bool spi; @@ -310,8 +312,12 @@ static void sd_ocr_powerup(void *opaque) static void sd_set_scr(SDState *sd) { - sd->scr[0] = (0 << 4) /* SCR structure version 1.0 */ - | 1; /* Spec Version 1.10 */ + sd->scr[0] = 0 << 4; /* SCR structure version 1.0 */ + if (sd->spec_version == SD_PHY_SPECv1_10_VERS) { + sd->scr[0] |= 1; /* Spec Version 1.10 */ + } else { + sd->scr[0] |= 2; /* Spec Version 2.00 */ + } sd->scr[1] = (2 << 4) /* SDSC Card (Security Version 1.01) */ | 0b0101; /* 1-bit or 4-bit width bus modes */ sd->scr[2] = 0x00; /* Extended Security is not supported. */ @@ -2058,6 +2064,15 @@ static void sd_realize(DeviceState *dev, Error **errp) sd->proto_name = sd->spi ? "SPI" : "SD"; + switch (sd->spec_version) { + case SD_PHY_SPECv1_10_VERS + ... SD_PHY_SPECv2_00_VERS: + break; + default: + error_setg(errp, "Invalid SD card Spec version: %u", sd->spec_version); + return; + } + if (sd->blk && blk_is_read_only(sd->blk)) { error_setg(errp, "Cannot use read-only drive as SD card"); return; @@ -2074,6 +2089,8 @@ static void sd_realize(DeviceState *dev, Error **errp) } static Property sd_properties[] = { + DEFINE_PROP_UINT8("spec_version", SDState, + spec_version, SD_PHY_SPECv2_00_VERS), DEFINE_PROP_DRIVE("drive", SDState, blk), /* We do not model the chip select pin, so allow the board to select * whether card should be in SSI or MMC/SD mode. It is also up to the diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 9bdb3c9285..7c6ad3c8f1 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -54,6 +54,11 @@ #define APP_CMD (1 << 5) #define AKE_SEQ_ERROR (1 << 3) +enum SDPhySpecificationVersion { + SD_PHY_SPECv1_10_VERS = 1, + SD_PHY_SPECv2_00_VERS = 2, +}; + typedef enum { SD_VOLTAGE_0_4V = 400, /* currently not supported */ SD_VOLTAGE_1_8V = 1800, From d1b322182bfebd8fe9442c52f50e7ac3acf07edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:34 +0100 Subject: [PATCH 1065/2380] sdcard: Disable SEND_IF_COND (CMD8) for Spec v1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CMD8 is "Reserved" in Spec v1.10. Spec v2.00 introduces the SEND_IF_COND command: 6.4.1 Power Up CMD8 is newly added in the Physical Layer Specification Version 2.00 to support multiple voltage ranges and used to check whether the card supports supplied voltage. The version 2.00 or later host shall issue CMD8 and verify voltage before card initialization. The host that does not support CMD8 shall supply high voltage range. Message-Id: 201204252110.20873.paul@codesourcery.com Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180607180641.874-5-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sd/sd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 1ae085de69..3ec0f71f24 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1018,7 +1018,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 8: /* CMD8: SEND_IF_COND */ - /* Physical Layer Specification Version 2.00 command */ + if (sd->spec_version < SD_PHY_SPECv2_00_VERS) { + break; + } if (sd->state != sd_idle_state) { break; } From 2c51137538d5069f05244b414ae984cee1d4c53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:34 +0100 Subject: [PATCH 1066/2380] sdcard: Reflect when the Spec v3 is supported in the Config Register (SCR) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20180607180641.874-6-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/sd/sd.c | 7 +++++-- include/hw/sd/sd.h | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 3ec0f71f24..4e49a3827a 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -316,11 +316,14 @@ static void sd_set_scr(SDState *sd) if (sd->spec_version == SD_PHY_SPECv1_10_VERS) { sd->scr[0] |= 1; /* Spec Version 1.10 */ } else { - sd->scr[0] |= 2; /* Spec Version 2.00 */ + sd->scr[0] |= 2; /* Spec Version 2.00 or Version 3.0X */ } sd->scr[1] = (2 << 4) /* SDSC Card (Security Version 1.01) */ | 0b0101; /* 1-bit or 4-bit width bus modes */ sd->scr[2] = 0x00; /* Extended Security is not supported. */ + if (sd->spec_version >= SD_PHY_SPECv3_01_VERS) { + sd->scr[2] |= 1 << 7; /* Spec Version 3.0X */ + } sd->scr[3] = 0x00; /* reserved for manufacturer usage */ sd->scr[4] = 0x00; @@ -2068,7 +2071,7 @@ static void sd_realize(DeviceState *dev, Error **errp) switch (sd->spec_version) { case SD_PHY_SPECv1_10_VERS - ... SD_PHY_SPECv2_00_VERS: + ... SD_PHY_SPECv3_01_VERS: break; default: error_setg(errp, "Invalid SD card Spec version: %u", sd->spec_version); diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 7c6ad3c8f1..b865aafc33 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -57,6 +57,7 @@ enum SDPhySpecificationVersion { SD_PHY_SPECv1_10_VERS = 1, SD_PHY_SPECv2_00_VERS = 2, + SD_PHY_SPECv3_01_VERS = 3, }; typedef enum { From 113f31c06c6bf16451892b2459d83c9b9c5e9844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Jun 2018 13:15:34 +0100 Subject: [PATCH 1067/2380] sdcard: Disable CMD19/CMD23 for Spec v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These commands got introduced by Spec v3 (see 0c3fb03f7ec and 4481bbc79d2). Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180607180641.874-7-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sd/sd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 4e49a3827a..540bccb8d1 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1179,6 +1179,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ + if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { + break; + } if (sd->state == sd_transfer_state) { sd->state = sd_sendingdata_state; sd->data_offset = 0; @@ -1187,6 +1190,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 23: /* CMD23: SET_BLOCK_COUNT */ + if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { + break; + } switch (sd->state) { case sd_transfer_state: sd->multi_blk_cnt = req.arg; From dc5a43eda68fff32c7b0b43847332db325b094f3 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:35 -0400 Subject: [PATCH 1068/2380] ahci: trim signatures on raise/lower These functions work on the AHCI device, not the individual AHCI devices, so trim the AHCIDevice argument. Signed-off-by: John Snow Reviewed-by: Stefan Hajnoczi Reviewed-by: Jeff Cody Message-id: 20180531004323.4611-2-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 24dbad5125..66f55aecb3 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -131,7 +131,7 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) return val; } -static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) +static void ahci_irq_raise(AHCIState *s) { DeviceState *dev_state = s->container; PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), @@ -146,7 +146,7 @@ static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) } } -static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev) +static void ahci_irq_lower(AHCIState *s) { DeviceState *dev_state = s->container; PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), @@ -174,9 +174,9 @@ static void ahci_check_irq(AHCIState *s) trace_ahci_check_irq(s, old_irq, s->control_regs.irqstatus); if (s->control_regs.irqstatus && (s->control_regs.ghc & HOST_CTL_IRQ_EN)) { - ahci_irq_raise(s, NULL); + ahci_irq_raise(s); } else { - ahci_irq_lower(s, NULL); + ahci_irq_lower(s); } } From 5694c7eacce6b263ad7497cc1bb76aad746cfd4e Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:36 -0400 Subject: [PATCH 1069/2380] ahci: fix PxCI register race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://bugs.launchpad.net/qemu/+bug/1769189 AHCI presently signals completion prior to the PxCI register being cleared to indicate completion. If a guest driver attempts to issue a new command in its IRQ handler, it might be surprised to learn there is still a command pending. In the case of Windows 10's boot driver, it will actually poll the IRQ register hoping to find out when the command is done running -- which will never happen, as there isn't a command running. Fix this: clear PxCI in ahci_cmd_done and not in the asynchronous BH. Because it now runs synchronously, we don't need to check if the command is actually done by spying on the ATA registers. We know it's done. CC: qemu-stable Reported-by: François Guerraz Tested-by: Bruce Rogers Signed-off-by: John Snow Reviewed-by: Stefan Hajnoczi Reviewed-by: Jeff Cody Message-id: 20180531004323.4611-3-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 66f55aecb3..b11640ddbb 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -532,13 +532,6 @@ static void ahci_check_cmd_bh(void *opaque) qemu_bh_delete(ad->check_bh); ad->check_bh = NULL; - if ((ad->busy_slot != -1) && - !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) { - /* no longer busy */ - ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot); - ad->busy_slot = -1; - } - check_cmd(ad->hba, ad->port_no); } @@ -1425,6 +1418,12 @@ static void ahci_cmd_done(IDEDMA *dma) trace_ahci_cmd_done(ad->hba, ad->port_no); + /* no longer busy */ + if (ad->busy_slot != -1) { + ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot); + ad->busy_slot = -1; + } + /* update d2h status */ ahci_write_fis_d2h(ad); From 42af312adef8afdae11d5f83d12a404b178dbda4 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:36 -0400 Subject: [PATCH 1070/2380] ahci: don't schedule unnecessary BH The comment gives us a hint. *Maybe* we still have something to process. Well, why not check? Signed-off-by: John Snow Reviewed-by: Stefan Hajnoczi Reviewed-by: Jeff Cody Message-id: 20180531004323.4611-4-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index b11640ddbb..ac4bc1738b 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1427,8 +1427,7 @@ static void ahci_cmd_done(IDEDMA *dma) /* update d2h status */ ahci_write_fis_d2h(ad); - if (!ad->check_bh) { - /* maybe we still have something to process, check later */ + if (ad->port_regs.cmd_issue && !ad->check_bh) { ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); qemu_bh_schedule(ad->check_bh); } From 4e6e1de4e05b01292027fdd9f36a507872c33b9d Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:36 -0400 Subject: [PATCH 1071/2380] ahci: add port register enumeration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of tracking offsets, lets count the registers. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-2-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 25 +++++++++++++++++++++++++ hw/ide/ahci_internal.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index ac4bc1738b..9815a64b5a 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -46,6 +46,31 @@ static bool ahci_map_fis_address(AHCIDevice *ad); static void ahci_unmap_clb_address(AHCIDevice *ad); static void ahci_unmap_fis_address(AHCIDevice *ad); +__attribute__((__unused__)) /* TODO */ +static const char *AHCIPortReg_lookup[AHCI_PORT_REG__COUNT] = { + [AHCI_PORT_REG_LST_ADDR] = "PxCLB", + [AHCI_PORT_REG_LST_ADDR_HI] = "PxCLBU", + [AHCI_PORT_REG_FIS_ADDR] = "PxFB", + [AHCI_PORT_REG_FIS_ADDR_HI] = "PxFBU", + [AHCI_PORT_REG_IRQ_STAT] = "PxIS", + [AHCI_PORT_REG_IRQ_MASK] = "PXIE", + [AHCI_PORT_REG_CMD] = "PxCMD", + [7] = "Reserved", + [AHCI_PORT_REG_TFDATA] = "PxTFD", + [AHCI_PORT_REG_SIG] = "PxSIG", + [AHCI_PORT_REG_SCR_STAT] = "PxSSTS", + [AHCI_PORT_REG_SCR_CTL] = "PxSCTL", + [AHCI_PORT_REG_SCR_ERR] = "PxSERR", + [AHCI_PORT_REG_SCR_ACT] = "PxSACT", + [AHCI_PORT_REG_CMD_ISSUE] = "PxCI", + [AHCI_PORT_REG_SCR_NOTIF] = "PxSNTF", + [AHCI_PORT_REG_FIS_CTL] = "PxFBS", + [AHCI_PORT_REG_DEV_SLEEP] = "PxDEVSLP", + [18 ... 27] = "Reserved", + [AHCI_PORT_REG_VENDOR_1 ... + AHCI_PORT_REG_VENDOR_4] = "PxVS", +}; + static const char *AHCIPortIRQ_lookup[AHCI_PORT_IRQ__COUNT] = { [AHCI_PORT_IRQ_BIT_DHRS] = "DHRS", [AHCI_PORT_IRQ_BIT_PSS] = "PSS", diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index 1a25d6c039..eb7e1eefc0 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -74,6 +74,34 @@ #define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */ #define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */ +/* registers for each SATA port */ +enum AHCIPortReg { + AHCI_PORT_REG_LST_ADDR = 0, /* PxCLB: command list DMA addr */ + AHCI_PORT_REG_LST_ADDR_HI = 1, /* PxCLBU: command list DMA addr hi */ + AHCI_PORT_REG_FIS_ADDR = 2, /* PxFB: FIS rx buf addr */ + AHCI_PORT_REG_FIS_ADDR_HI = 3, /* PxFBU: FIX rx buf addr hi */ + AHCI_PORT_REG_IRQ_STAT = 4, /* PxIS: interrupt status */ + AHCI_PORT_REG_IRQ_MASK = 5, /* PxIE: interrupt enable/disable mask */ + AHCI_PORT_REG_CMD = 6, /* PxCMD: port command */ + /* RESERVED */ + AHCI_PORT_REG_TFDATA = 8, /* PxTFD: taskfile data */ + AHCI_PORT_REG_SIG = 9, /* PxSIG: device TF signature */ + AHCI_PORT_REG_SCR_STAT = 10, /* PxSSTS: SATA phy register: SStatus */ + AHCI_PORT_REG_SCR_CTL = 11, /* PxSCTL: SATA phy register: SControl */ + AHCI_PORT_REG_SCR_ERR = 12, /* PxSERR: SATA phy register: SError */ + AHCI_PORT_REG_SCR_ACT = 13, /* PxSACT: SATA phy register: SActive */ + AHCI_PORT_REG_CMD_ISSUE = 14, /* PxCI: command issue */ + AHCI_PORT_REG_SCR_NOTIF = 15, /* PxSNTF: SATA phy register: SNotification */ + AHCI_PORT_REG_FIS_CTL = 16, /* PxFBS: Port multiplier switching ctl */ + AHCI_PORT_REG_DEV_SLEEP = 17, /* PxDEVSLP: device sleep control */ + /* RESERVED */ + AHCI_PORT_REG_VENDOR_1 = 28, /* PxVS: Vendor Specific */ + AHCI_PORT_REG_VENDOR_2 = 29, + AHCI_PORT_REG_VENDOR_3 = 30, + AHCI_PORT_REG_VENDOR_4 = 31, + AHCI_PORT_REG__COUNT = 32 +}; + /* registers for each SATA port */ #define PORT_LST_ADDR 0x00 /* command list DMA addr */ #define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ @@ -82,6 +110,7 @@ #define PORT_IRQ_STAT 0x10 /* interrupt status */ #define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ #define PORT_CMD 0x18 /* port command */ + #define PORT_TFDATA 0x20 /* taskfile data */ #define PORT_SIG 0x24 /* device TF signature */ #define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ From 536551d75874f0ba7651a1455a295239a5cabe05 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:36 -0400 Subject: [PATCH 1072/2380] ahci: modify ahci_port_read to use register numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-3-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 9815a64b5a..fb0e5f1c12 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -93,41 +93,42 @@ static const char *AHCIPortIRQ_lookup[AHCI_PORT_IRQ__COUNT] = { [AHCI_PORT_IRQ_BIT_CPDS] = "CPDS" }; -static uint32_t ahci_port_read(AHCIState *s, int port, int offset) +static uint32_t ahci_port_read(AHCIState *s, int port, int offset) { uint32_t val; - AHCIPortRegs *pr; - pr = &s->dev[port].port_regs; + AHCIPortRegs *pr = &s->dev[port].port_regs; + enum AHCIPortReg regnum = offset / sizeof(uint32_t); + assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t))); - switch (offset) { - case PORT_LST_ADDR: + switch (regnum) { + case AHCI_PORT_REG_LST_ADDR: val = pr->lst_addr; break; - case PORT_LST_ADDR_HI: + case AHCI_PORT_REG_LST_ADDR_HI: val = pr->lst_addr_hi; break; - case PORT_FIS_ADDR: + case AHCI_PORT_REG_FIS_ADDR: val = pr->fis_addr; break; - case PORT_FIS_ADDR_HI: + case AHCI_PORT_REG_FIS_ADDR_HI: val = pr->fis_addr_hi; break; - case PORT_IRQ_STAT: + case AHCI_PORT_REG_IRQ_STAT: val = pr->irq_stat; break; - case PORT_IRQ_MASK: + case AHCI_PORT_REG_IRQ_MASK: val = pr->irq_mask; break; - case PORT_CMD: + case AHCI_PORT_REG_CMD: val = pr->cmd; break; - case PORT_TFDATA: + case AHCI_PORT_REG_TFDATA: val = pr->tfdata; break; - case PORT_SIG: + case AHCI_PORT_REG_SIG: val = pr->sig; break; - case PORT_SCR_STAT: + case AHCI_PORT_REG_SCR_STAT: if (s->dev[port].port.ifs[0].blk) { val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP | SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE; @@ -135,19 +136,18 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) val = SATA_SCR_SSTATUS_DET_NODEV; } break; - case PORT_SCR_CTL: + case AHCI_PORT_REG_SCR_CTL: val = pr->scr_ctl; break; - case PORT_SCR_ERR: + case AHCI_PORT_REG_SCR_ERR: val = pr->scr_err; break; - case PORT_SCR_ACT: + case AHCI_PORT_REG_SCR_ACT: val = pr->scr_act; break; - case PORT_CMD_ISSUE: + case AHCI_PORT_REG_CMD_ISSUE: val = pr->cmd_issue; break; - case PORT_RESERVED: default: val = 0; } From e538916366553e4ef49cd3f73e510ada8170092a Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:36 -0400 Subject: [PATCH 1073/2380] ahci: make port read traces more descriptive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A trace is added to let us watch unimplemented registers specifically, as these are more likely to cause us trouble. Otherwise, the port read traces now tell us what register is getting hit, which is nicer. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-4-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 5 +++-- hw/ide/trace-events | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index fb0e5f1c12..1107a9b118 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -46,7 +46,6 @@ static bool ahci_map_fis_address(AHCIDevice *ad); static void ahci_unmap_clb_address(AHCIDevice *ad); static void ahci_unmap_fis_address(AHCIDevice *ad); -__attribute__((__unused__)) /* TODO */ static const char *AHCIPortReg_lookup[AHCI_PORT_REG__COUNT] = { [AHCI_PORT_REG_LST_ADDR] = "PxCLB", [AHCI_PORT_REG_LST_ADDR_HI] = "PxCLBU", @@ -149,10 +148,12 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) val = pr->cmd_issue; break; default: + trace_ahci_port_read_default(s, port, AHCIPortReg_lookup[regnum], + offset); val = 0; } - trace_ahci_port_read(s, port, offset, val); + trace_ahci_port_read(s, port, AHCIPortReg_lookup[regnum], offset, val); return val; } diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 5c0e59ec42..0db18d8271 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -63,7 +63,8 @@ ide_atapi_cmd_read_dma_cb_aio(void *s, int lba, int n) "IDEState: %p; aio read: ide_atapi_cmd_packet(void *s, uint16_t limit, const char *packet) "IDEState: %p; limit=0x%x packet: %s" # hw/ide/ahci.c -ahci_port_read(void *s, int port, int offset, uint32_t ret) "ahci(%p)[%d]: port read @ 0x%x: 0x%08x" +ahci_port_read(void *s, int port, const char *reg, int offset, uint32_t ret) "ahci(%p)[%d]: port read [reg:%s] @ 0x%x: 0x%08x" +ahci_port_read_default(void *s, int port, const char *reg, int offset) "ahci(%p)[%d]: unimplemented port read [reg:%s] @ 0x%x" ahci_irq_raise(void *s) "ahci(%p): raise irq" ahci_irq_lower(void *s) "ahci(%p): lower irq" ahci_check_irq(void *s, uint32_t old, uint32_t new) "ahci(%p): check irq 0x%08x --> 0x%08x" From f1123e4b5c2b6bc8b4ebb9953b1c6ecfb4eb460d Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:36 -0400 Subject: [PATCH 1074/2380] ahci: fix spacing damage on ahci_port_write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Churn. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-5-jsnow@redhat.com [Fix patchew/checkpatch nit. --js] Signed-off-by: John Snow --- hw/ide/ahci.c | 142 +++++++++++++++++++++++++------------------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 1107a9b118..a19f46c301 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -279,85 +279,85 @@ static int ahci_cond_start_engines(AHCIDevice *ad) return 0; } -static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) +static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) { AHCIPortRegs *pr = &s->dev[port].port_regs; trace_ahci_port_write(s, port, offset, val); switch (offset) { - case PORT_LST_ADDR: - pr->lst_addr = val; - break; - case PORT_LST_ADDR_HI: - pr->lst_addr_hi = val; - break; - case PORT_FIS_ADDR: - pr->fis_addr = val; - break; - case PORT_FIS_ADDR_HI: - pr->fis_addr_hi = val; - break; - case PORT_IRQ_STAT: - pr->irq_stat &= ~val; - ahci_check_irq(s); - break; - case PORT_IRQ_MASK: - pr->irq_mask = val & 0xfdc000ff; - ahci_check_irq(s); - break; - case PORT_CMD: - /* Block any Read-only fields from being set; - * including LIST_ON and FIS_ON. - * The spec requires to set ICC bits to zero after the ICC change - * is done. We don't support ICC state changes, therefore always - * force the ICC bits to zero. - */ - pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | - (val & ~(PORT_CMD_RO_MASK|PORT_CMD_ICC_MASK)); + case PORT_LST_ADDR: + pr->lst_addr = val; + break; + case PORT_LST_ADDR_HI: + pr->lst_addr_hi = val; + break; + case PORT_FIS_ADDR: + pr->fis_addr = val; + break; + case PORT_FIS_ADDR_HI: + pr->fis_addr_hi = val; + break; + case PORT_IRQ_STAT: + pr->irq_stat &= ~val; + ahci_check_irq(s); + break; + case PORT_IRQ_MASK: + pr->irq_mask = val & 0xfdc000ff; + ahci_check_irq(s); + break; + case PORT_CMD: + /* Block any Read-only fields from being set; + * including LIST_ON and FIS_ON. + * The spec requires to set ICC bits to zero after the ICC change + * is done. We don't support ICC state changes, therefore always + * force the ICC bits to zero. + */ + pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | + (val & ~(PORT_CMD_RO_MASK | PORT_CMD_ICC_MASK)); - /* Check FIS RX and CLB engines */ - ahci_cond_start_engines(&s->dev[port]); + /* Check FIS RX and CLB engines */ + ahci_cond_start_engines(&s->dev[port]); - /* XXX usually the FIS would be pending on the bus here and - issuing deferred until the OS enables FIS receival. - Instead, we only submit it once - which works in most - cases, but is a hack. */ - if ((pr->cmd & PORT_CMD_FIS_ON) && - !s->dev[port].init_d2h_sent) { - ahci_init_d2h(&s->dev[port]); - } + /* XXX usually the FIS would be pending on the bus here and + issuing deferred until the OS enables FIS receival. + Instead, we only submit it once - which works in most + cases, but is a hack. */ + if ((pr->cmd & PORT_CMD_FIS_ON) && + !s->dev[port].init_d2h_sent) { + ahci_init_d2h(&s->dev[port]); + } - check_cmd(s, port); - break; - case PORT_TFDATA: - /* Read Only. */ - break; - case PORT_SIG: - /* Read Only */ - break; - case PORT_SCR_STAT: - /* Read Only */ - break; - case PORT_SCR_CTL: - if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && - ((val & AHCI_SCR_SCTL_DET) == 0)) { - ahci_reset_port(s, port); - } - pr->scr_ctl = val; - break; - case PORT_SCR_ERR: - pr->scr_err &= ~val; - break; - case PORT_SCR_ACT: - /* RW1 */ - pr->scr_act |= val; - break; - case PORT_CMD_ISSUE: - pr->cmd_issue |= val; - check_cmd(s, port); - break; - default: - break; + check_cmd(s, port); + break; + case PORT_TFDATA: + /* Read Only. */ + break; + case PORT_SIG: + /* Read Only */ + break; + case PORT_SCR_STAT: + /* Read Only */ + break; + case PORT_SCR_CTL: + if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && + ((val & AHCI_SCR_SCTL_DET) == 0)) { + ahci_reset_port(s, port); + } + pr->scr_ctl = val; + break; + case PORT_SCR_ERR: + pr->scr_err &= ~val; + break; + case PORT_SCR_ACT: + /* RW1 */ + pr->scr_act |= val; + break; + case PORT_CMD_ISSUE: + pr->cmd_issue |= val; + check_cmd(s, port); + break; + default: + break; } } From 59281eee97e4c2ae357f307413159e62cbc709c1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:37 -0400 Subject: [PATCH 1075/2380] ahci: combine identical clauses in port write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-6-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index a19f46c301..2aea9c3e12 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -330,11 +330,7 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) check_cmd(s, port); break; case PORT_TFDATA: - /* Read Only. */ - break; case PORT_SIG: - /* Read Only */ - break; case PORT_SCR_STAT: /* Read Only */ break; From f647f4587e2377dd6052c0752c3f9e64f1597605 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:37 -0400 Subject: [PATCH 1076/2380] ahci: modify ahci_port_write to use register numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-7-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 2aea9c3e12..4e6f3f1514 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -282,30 +282,32 @@ static int ahci_cond_start_engines(AHCIDevice *ad) static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) { AHCIPortRegs *pr = &s->dev[port].port_regs; + enum AHCIPortReg regnum = offset / sizeof(uint32_t); + assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t))); trace_ahci_port_write(s, port, offset, val); - switch (offset) { - case PORT_LST_ADDR: + switch (regnum) { + case AHCI_PORT_REG_LST_ADDR: pr->lst_addr = val; break; - case PORT_LST_ADDR_HI: + case AHCI_PORT_REG_LST_ADDR_HI: pr->lst_addr_hi = val; break; - case PORT_FIS_ADDR: + case AHCI_PORT_REG_FIS_ADDR: pr->fis_addr = val; break; - case PORT_FIS_ADDR_HI: + case AHCI_PORT_REG_FIS_ADDR_HI: pr->fis_addr_hi = val; break; - case PORT_IRQ_STAT: + case AHCI_PORT_REG_IRQ_STAT: pr->irq_stat &= ~val; ahci_check_irq(s); break; - case PORT_IRQ_MASK: + case AHCI_PORT_REG_IRQ_MASK: pr->irq_mask = val & 0xfdc000ff; ahci_check_irq(s); break; - case PORT_CMD: + case AHCI_PORT_REG_CMD: /* Block any Read-only fields from being set; * including LIST_ON and FIS_ON. * The spec requires to set ICC bits to zero after the ICC change @@ -329,26 +331,26 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) check_cmd(s, port); break; - case PORT_TFDATA: - case PORT_SIG: - case PORT_SCR_STAT: + case AHCI_PORT_REG_TFDATA: + case AHCI_PORT_REG_SIG: + case AHCI_PORT_REG_SCR_STAT: /* Read Only */ break; - case PORT_SCR_CTL: + case AHCI_PORT_REG_SCR_CTL: if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && ((val & AHCI_SCR_SCTL_DET) == 0)) { ahci_reset_port(s, port); } pr->scr_ctl = val; break; - case PORT_SCR_ERR: + case AHCI_PORT_REG_SCR_ERR: pr->scr_err &= ~val; break; - case PORT_SCR_ACT: + case AHCI_PORT_REG_SCR_ACT: /* RW1 */ pr->scr_act |= val; break; - case PORT_CMD_ISSUE: + case AHCI_PORT_REG_CMD_ISSUE: pr->cmd_issue |= val; check_cmd(s, port); break; From 06e350655c852f99d7b0295838d11b2277876ffd Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:37 -0400 Subject: [PATCH 1077/2380] ahci: make port write traces more descriptive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-8-jsnow@redhat.com [Changed format specifier. --js] Signed-off-by: John Snow --- hw/ide/ahci.c | 8 +++++++- hw/ide/trace-events | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 4e6f3f1514..a85847b165 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -27,6 +27,7 @@ #include "hw/pci/pci.h" #include "qemu/error-report.h" +#include "qemu/log.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "hw/ide/internal.h" @@ -284,8 +285,8 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) AHCIPortRegs *pr = &s->dev[port].port_regs; enum AHCIPortReg regnum = offset / sizeof(uint32_t); assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t))); + trace_ahci_port_write(s, port, AHCIPortReg_lookup[regnum], offset, val); - trace_ahci_port_write(s, port, offset, val); switch (regnum) { case AHCI_PORT_REG_LST_ADDR: pr->lst_addr = val; @@ -355,6 +356,11 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) check_cmd(s, port); break; default: + trace_ahci_port_write_unimpl(s, port, AHCIPortReg_lookup[regnum], + offset, val); + qemu_log_mask(LOG_UNIMP, "Attempted write to unimplemented register: " + "AHCI port %d register %s, offset 0x%x: 0x%"PRIx32, + port, AHCIPortReg_lookup[regnum], offset, val); break; } } diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 0db18d8271..1efbbb8114 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -69,7 +69,8 @@ ahci_irq_raise(void *s) "ahci(%p): raise irq" ahci_irq_lower(void *s) "ahci(%p): lower irq" ahci_check_irq(void *s, uint32_t old, uint32_t new) "ahci(%p): check irq 0x%08x --> 0x%08x" ahci_trigger_irq(void *s, int port, const char *name, uint32_t val, uint32_t old, uint32_t new, uint32_t effective) "ahci(%p)[%d]: trigger irq +%s (0x%08x); irqstat: 0x%08x --> 0x%08x; effective: 0x%08x" -ahci_port_write(void *s, int port, int offset, uint32_t val) "ahci(%p)[%d]: port write @ 0x%x: 0x%08x" +ahci_port_write(void *s, int port, const char *reg, int offset, uint32_t val) "ahci(%p)[%d]: port write [reg:%s] @ 0x%x: 0x%08x" +ahci_port_write_unimpl(void *s, int port, const char *reg, int offset, uint32_t val) "ahci(%p)[%d]: unimplemented port write [reg:%s] @ 0x%x: 0x%08x" ahci_mem_read_32(void *s, uint64_t addr, uint32_t val) "ahci(%p): mem read @ 0x%"PRIx64": 0x%08x" ahci_mem_read(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): read%u @ 0x%"PRIx64": 0x%016"PRIx64 ahci_mem_write(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u @ 0x%"PRIx64": 0x%016"PRIx64 From 3d74e87d09180c3a1b89b8daa7f7d143a12737fa Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:37 -0400 Subject: [PATCH 1078/2380] ahci: delete old port register address definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They're now unused. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-9-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci_internal.h | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index eb7e1eefc0..db00c9aa39 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -102,24 +102,6 @@ enum AHCIPortReg { AHCI_PORT_REG__COUNT = 32 }; -/* registers for each SATA port */ -#define PORT_LST_ADDR 0x00 /* command list DMA addr */ -#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ -#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */ -#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */ -#define PORT_IRQ_STAT 0x10 /* interrupt status */ -#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ -#define PORT_CMD 0x18 /* port command */ - -#define PORT_TFDATA 0x20 /* taskfile data */ -#define PORT_SIG 0x24 /* device TF signature */ -#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ -#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */ -#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */ -#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */ -#define PORT_CMD_ISSUE 0x38 /* command issue */ -#define PORT_RESERVED 0x3c /* reserved */ - /* Port interrupt bit descriptors */ enum AHCIPortIRQ { AHCI_PORT_IRQ_BIT_DHRS = 0, From da868a46db847123965a8b234f2a33d73fa8d26d Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:37 -0400 Subject: [PATCH 1079/2380] ahci: add host register enumeration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-10-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 15 +++++++++++++++ hw/ide/ahci_internal.h | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index a85847b165..5be43ba2d0 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -47,6 +47,21 @@ static bool ahci_map_fis_address(AHCIDevice *ad); static void ahci_unmap_clb_address(AHCIDevice *ad); static void ahci_unmap_fis_address(AHCIDevice *ad); +__attribute__((__unused__)) /* TODO */ +static const char *AHCIHostReg_lookup[AHCI_HOST_REG__COUNT] = { + [AHCI_HOST_REG_CAP] = "CAP", + [AHCI_HOST_REG_CTL] = "GHC", + [AHCI_HOST_REG_IRQ_STAT] = "IS", + [AHCI_HOST_REG_PORTS_IMPL] = "PI", + [AHCI_HOST_REG_VERSION] = "VS", + [AHCI_HOST_REG_CCC_CTL] = "CCC_CTL", + [AHCI_HOST_REG_CCC_PORTS] = "CCC_PORTS", + [AHCI_HOST_REG_EM_LOC] = "EM_LOC", + [AHCI_HOST_REG_EM_CTL] = "EM_CTL", + [AHCI_HOST_REG_CAP2] = "CAP2", + [AHCI_HOST_REG_BOHC] = "BOHC", +}; + static const char *AHCIPortReg_lookup[AHCI_PORT_REG__COUNT] = { [AHCI_PORT_REG_LST_ADDR] = "PxCLB", [AHCI_PORT_REG_LST_ADDR_HI] = "PxCLBU", diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index db00c9aa39..af366db6f3 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -61,6 +61,21 @@ #define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ #define HOST_VERSION 0x10 /* AHCI spec. version compliancy */ +enum AHCIHostReg { + AHCI_HOST_REG_CAP = 0, /* CAP: host capabilities */ + AHCI_HOST_REG_CTL = 1, /* GHC: global host control */ + AHCI_HOST_REG_IRQ_STAT = 2, /* IS: interrupt status */ + AHCI_HOST_REG_PORTS_IMPL = 3, /* PI: bitmap of implemented ports */ + AHCI_HOST_REG_VERSION = 4, /* VS: AHCI spec. version compliancy */ + AHCI_HOST_REG_CCC_CTL = 5, /* CCC_CTL: CCC Control */ + AHCI_HOST_REG_CCC_PORTS = 6, /* CCC_PORTS: CCC Ports */ + AHCI_HOST_REG_EM_LOC = 7, /* EM_LOC: Enclosure Mgmt Location */ + AHCI_HOST_REG_EM_CTL = 8, /* EM_CTL: Enclosure Mgmt Control */ + AHCI_HOST_REG_CAP2 = 9, /* CAP2: host capabilities, extended */ + AHCI_HOST_REG_BOHC = 10, /* BOHC: firmare/os handoff ctrl & status */ + AHCI_HOST_REG__COUNT = 11 +}; + /* HOST_CTL bits */ #define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ #define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */ From 96034081dd2dd5177ffdf19475712264783c7bef Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:37 -0400 Subject: [PATCH 1080/2380] ahci: fix host register max address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Yes, comment, it ought to be 0x2C. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-11-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci_internal.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index af366db6f3..f9dcf8b6e6 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -224,8 +224,7 @@ enum AHCIPortIRQ { #define SATA_SIGNATURE_CDROM 0xeb140101 #define SATA_SIGNATURE_DISK 0x00000101 -#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20 - /* Shouldn't this be 0x2c? */ +#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x2c #define AHCI_PORT_REGS_START_ADDR 0x100 #define AHCI_PORT_ADDR_OFFSET_MASK 0x7f From 215c41aa67febfab018dbc4c5d2ac2d2de26c4d2 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:38 -0400 Subject: [PATCH 1081/2380] ahci: modify ahci_mem_read_32 to work on register numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-12-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 5be43ba2d0..99cbfe6447 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -386,22 +386,27 @@ static uint64_t ahci_mem_read_32(void *opaque, hwaddr addr) uint32_t val = 0; if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { - switch (addr) { - case HOST_CAP: + enum AHCIHostReg regnum = addr / 4; + assert(regnum < AHCI_HOST_REG__COUNT); + + switch (regnum) { + case AHCI_HOST_REG_CAP: val = s->control_regs.cap; break; - case HOST_CTL: + case AHCI_HOST_REG_CTL: val = s->control_regs.ghc; break; - case HOST_IRQ_STAT: + case AHCI_HOST_REG_IRQ_STAT: val = s->control_regs.irqstatus; break; - case HOST_PORTS_IMPL: + case AHCI_HOST_REG_PORTS_IMPL: val = s->control_regs.impl; break; - case HOST_VERSION: + case AHCI_HOST_REG_VERSION: val = s->control_regs.version; break; + default: + break; } } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && (addr < (AHCI_PORT_REGS_START_ADDR + From 9da8ac3203503195a9c602328ea25c73ed1ab55d Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:38 -0400 Subject: [PATCH 1082/2380] ahci: make mem_read_32 traces more descriptive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-13-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 7 +++++-- hw/ide/trace-events | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 99cbfe6447..b31eb84aaa 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -47,7 +47,6 @@ static bool ahci_map_fis_address(AHCIDevice *ad); static void ahci_unmap_clb_address(AHCIDevice *ad); static void ahci_unmap_fis_address(AHCIDevice *ad); -__attribute__((__unused__)) /* TODO */ static const char *AHCIHostReg_lookup[AHCI_HOST_REG__COUNT] = { [AHCI_HOST_REG_CAP] = "CAP", [AHCI_HOST_REG_CTL] = "GHC", @@ -406,13 +405,17 @@ static uint64_t ahci_mem_read_32(void *opaque, hwaddr addr) val = s->control_regs.version; break; default: - break; + trace_ahci_mem_read_32_host_default(s, AHCIHostReg_lookup[regnum], + addr); } + trace_ahci_mem_read_32_host(s, AHCIHostReg_lookup[regnum], addr, val); } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && (addr < (AHCI_PORT_REGS_START_ADDR + (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7, addr & AHCI_PORT_ADDR_OFFSET_MASK); + } else { + trace_ahci_mem_read_32_default(s, addr, val); } trace_ahci_mem_read_32(s, addr, val); diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 1efbbb8114..8149a54db8 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -72,6 +72,9 @@ ahci_trigger_irq(void *s, int port, const char *name, uint32_t val, uint32_t old ahci_port_write(void *s, int port, const char *reg, int offset, uint32_t val) "ahci(%p)[%d]: port write [reg:%s] @ 0x%x: 0x%08x" ahci_port_write_unimpl(void *s, int port, const char *reg, int offset, uint32_t val) "ahci(%p)[%d]: unimplemented port write [reg:%s] @ 0x%x: 0x%08x" ahci_mem_read_32(void *s, uint64_t addr, uint32_t val) "ahci(%p): mem read @ 0x%"PRIx64": 0x%08x" +ahci_mem_read_32_default(void *s, uint64_t addr, uint32_t val) "ahci(%p): mem read @ 0x%"PRIx64": 0x%08x" +ahci_mem_read_32_host(void *s, const char *reg, uint64_t addr, uint32_t val) "ahci(%p): mem read [reg:%s] @ 0x%"PRIx64": 0x%08x" +ahci_mem_read_32_host_default(void *s, const char *reg, uint64_t addr) "ahci(%p): unimplemented mem read [reg:%s] @ 0x%"PRIx64 ahci_mem_read(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): read%u @ 0x%"PRIx64": 0x%016"PRIx64 ahci_mem_write(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u @ 0x%"PRIx64": 0x%016"PRIx64 ahci_mem_write_unknown(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u to unknown register 0x%"PRIx64": 0x%016"PRIx64 From 467378baed0c1b3bfaa11eb97d1c231d1b0ed916 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:38 -0400 Subject: [PATCH 1083/2380] ahci: fix spacing damage on ahci_mem_write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-14-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index b31eb84aaa..51c3e96c80 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -469,37 +469,36 @@ static void ahci_mem_write(void *opaque, hwaddr addr, if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { switch (addr) { - case HOST_CAP: /* R/WO, RO */ - /* FIXME handle R/WO */ - break; - case HOST_CTL: /* R/W */ - if (val & HOST_CTL_RESET) { - ahci_reset(s); - } else { - s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN; - ahci_check_irq(s); - } - break; - case HOST_IRQ_STAT: /* R/WC, RO */ - s->control_regs.irqstatus &= ~val; + case HOST_CAP: /* R/WO, RO */ + /* FIXME handle R/WO */ + break; + case HOST_CTL: /* R/W */ + if (val & HOST_CTL_RESET) { + ahci_reset(s); + } else { + s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN; ahci_check_irq(s); - break; - case HOST_PORTS_IMPL: /* R/WO, RO */ - /* FIXME handle R/WO */ - break; - case HOST_VERSION: /* RO */ - /* FIXME report write? */ - break; - default: - trace_ahci_mem_write_unknown(s, size, addr, val); + } + break; + case HOST_IRQ_STAT: /* R/WC, RO */ + s->control_regs.irqstatus &= ~val; + ahci_check_irq(s); + break; + case HOST_PORTS_IMPL: /* R/WO, RO */ + /* FIXME handle R/WO */ + break; + case HOST_VERSION: /* RO */ + /* FIXME report write? */ + break; + default: + trace_ahci_mem_write_unknown(s, size, addr, val); } } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && (addr < (AHCI_PORT_REGS_START_ADDR + - (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { + (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7, addr & AHCI_PORT_ADDR_OFFSET_MASK, val); } - } static const MemoryRegionOps ahci_mem_ops = { From d566811a10a8310da120cc962e487d491d1e91d3 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:38 -0400 Subject: [PATCH 1084/2380] ahci: adjust ahci_mem_write to work on registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Actually, this function looks pretty broken, but for now, let's finish up what this series of commits came here to do. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-15-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 51c3e96c80..e4e87351c9 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -468,11 +468,14 @@ static void ahci_mem_write(void *opaque, hwaddr addr, } if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { - switch (addr) { - case HOST_CAP: /* R/WO, RO */ + enum AHCIHostReg regnum = addr / 4; + assert(regnum < AHCI_HOST_REG__COUNT); + + switch (regnum) { + case AHCI_HOST_REG_CAP: /* R/WO, RO */ /* FIXME handle R/WO */ break; - case HOST_CTL: /* R/W */ + case AHCI_HOST_REG_CTL: /* R/W */ if (val & HOST_CTL_RESET) { ahci_reset(s); } else { @@ -480,14 +483,14 @@ static void ahci_mem_write(void *opaque, hwaddr addr, ahci_check_irq(s); } break; - case HOST_IRQ_STAT: /* R/WC, RO */ + case AHCI_HOST_REG_IRQ_STAT: /* R/WC, RO */ s->control_regs.irqstatus &= ~val; ahci_check_irq(s); break; - case HOST_PORTS_IMPL: /* R/WO, RO */ + case AHCI_HOST_REG_PORTS_IMPL: /* R/WO, RO */ /* FIXME handle R/WO */ break; - case HOST_VERSION: /* RO */ + case AHCI_HOST_REG_VERSION: /* RO */ /* FIXME report write? */ break; default: From ead019e7ddf07556ce73498380fdd2c1384657e6 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:38 -0400 Subject: [PATCH 1085/2380] ahci: delete old host register address definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-16-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci_internal.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index f9dcf8b6e6..2953243929 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -55,12 +55,6 @@ #define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ /* global controller registers */ -#define HOST_CAP 0x00 /* host capabilities */ -#define HOST_CTL 0x04 /* global host control */ -#define HOST_IRQ_STAT 0x08 /* interrupt status */ -#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ -#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */ - enum AHCIHostReg { AHCI_HOST_REG_CAP = 0, /* CAP: host capabilities */ AHCI_HOST_REG_CTL = 1, /* GHC: global host control */ From 017961262d318075fd224d16c6a0aee9890844e6 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 8 Jun 2018 13:17:38 -0400 Subject: [PATCH 1086/2380] ahci: make ahci_mem_write traces more descriptive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180531222835.16558-17-jsnow@redhat.com [Fixed format specifiers. --js] Signed-off-by: John Snow --- hw/ide/ahci.c | 15 ++++++++++++++- hw/ide/trace-events | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index e4e87351c9..571e32dd66 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -494,13 +494,26 @@ static void ahci_mem_write(void *opaque, hwaddr addr, /* FIXME report write? */ break; default: - trace_ahci_mem_write_unknown(s, size, addr, val); + qemu_log_mask(LOG_UNIMP, + "Attempted write to unimplemented register: " + "AHCI host register %s, " + "offset 0x%"PRIx64": 0x%"PRIx64, + AHCIHostReg_lookup[regnum], addr, val); + trace_ahci_mem_write_host_unimpl(s, size, + AHCIHostReg_lookup[regnum], addr); } + trace_ahci_mem_write_host(s, size, AHCIHostReg_lookup[regnum], + addr, val); } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && (addr < (AHCI_PORT_REGS_START_ADDR + (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7, addr & AHCI_PORT_ADDR_OFFSET_MASK, val); + } else { + qemu_log_mask(LOG_UNIMP, "Attempted write to unimplemented register: " + "AHCI global register at offset 0x%"PRIx64": 0x%"PRIx64, + addr, val); + trace_ahci_mem_write_unimpl(s, size, addr, val); } } diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 8149a54db8..e6bd95f52f 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -77,7 +77,9 @@ ahci_mem_read_32_host(void *s, const char *reg, uint64_t addr, uint32_t val) "ah ahci_mem_read_32_host_default(void *s, const char *reg, uint64_t addr) "ahci(%p): unimplemented mem read [reg:%s] @ 0x%"PRIx64 ahci_mem_read(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): read%u @ 0x%"PRIx64": 0x%016"PRIx64 ahci_mem_write(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u @ 0x%"PRIx64": 0x%016"PRIx64 -ahci_mem_write_unknown(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u to unknown register 0x%"PRIx64": 0x%016"PRIx64 +ahci_mem_write_host_unimpl(void *s, unsigned size, const char *reg, uint64_t addr) "ahci(%p) unimplemented write%u [reg:%s] @ 0x%"PRIx64 +ahci_mem_write_host(void *s, unsigned size, const char *reg, uint64_t addr, uint64_t val) "ahci(%p) write%u [reg:%s] @ 0x%"PRIx64": 0x%016"PRIx64 +ahci_mem_write_unimpl(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u to unknown register 0x%"PRIx64": 0x%016"PRIx64 ahci_set_signature(void *s, int port, uint8_t nsector, uint8_t sector, uint8_t lcyl, uint8_t hcyl, uint32_t sig) "ahci(%p)[%d]: set signature sector:0x%02x nsector:0x%02x lcyl:0x%02x hcyl:0x%02x (cumulatively: 0x%08x)" ahci_reset_port(void *s, int port) "ahci(%p)[%d]: reset port" ahci_unmap_fis_address_null(void *s, int port) "ahci(%p)[%d]: Attempt to unmap NULL FIS address" From b1dd6d2d270a2b72db47576635c51636f0db36e6 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 8 Jun 2018 13:17:39 -0400 Subject: [PATCH 1087/2380] tests/boot-sector: Add magic bytes to s390x boot code header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're going to use the s390x boot code for testing CD-ROM booting. But the ISO loader of the s390-ccw bios is a little bit more picky than the network loader and expects some magic bytes in the header of the file (see linux_s390_magic in pc-bios/s390-ccw/bootmap.c), so we've got to add them in our boot code here, too. Reviewed-by: Christian Borntraeger Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Reviewed-by: Hervé Poussineau Acked-By: Mark Cave-Ayland Signed-off-by: Thomas Huth Signed-off-by: John Snow --- tests/boot-sector.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/boot-sector.c b/tests/boot-sector.c index c373f0e715..7824286b9a 100644 --- a/tests/boot-sector.c +++ b/tests/boot-sector.c @@ -68,8 +68,11 @@ static uint8_t x86_boot_sector[512] = { }; /* For s390x, use a mini "kernel" with the appropriate signature */ -static const uint8_t s390x_psw[] = { - 0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00 +static const uint8_t s390x_psw_and_magic[] = { + 0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, /* Program status word */ + 0x02, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x50, /* Magic: */ + 0x02, 0x00, 0x00, 0x68, 0x60, 0x00, 0x00, 0x50, /* see linux_s390_magic */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* in the s390-ccw bios */ }; static const uint8_t s390x_code[] = { 0xa7, 0xf4, 0x00, 0x0a, /* j 0x10010 */ @@ -110,7 +113,7 @@ int boot_sector_init(char *fname) } else if (g_str_equal(arch, "s390x")) { len = 0x10000 + sizeof(s390x_code); boot_code = g_malloc0(len); - memcpy(boot_code, s390x_psw, sizeof(s390x_psw)); + memcpy(boot_code, s390x_psw_and_magic, sizeof(s390x_psw_and_magic)); memcpy(&boot_code[0x10000], s390x_code, sizeof(s390x_code)); } else { g_assert_not_reached(); From 42f455054f8301ce18936b583e8354551a4f2c17 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 8 Jun 2018 13:17:39 -0400 Subject: [PATCH 1088/2380] tests/cdrom-test: Test booting from CD-ROM ISO image file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have the code for a boot file in tests/boot-sector.c, so if the genisoimage program is available, we can easily create a bootable CD ISO image that we can use for testing whether our CD-ROM emulation and the BIOS CD-ROM boot works correctly. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Reviewed-by: Hervé Poussineau Acked-By: Mark Cave-Ayland Signed-off-by: Thomas Huth Signed-off-by: John Snow --- tests/Makefile.include | 2 + tests/cdrom-test.c | 164 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 tests/cdrom-test.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 400d8890e7..d098a104bb 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -179,6 +179,7 @@ check-qtest-generic-y = tests/qmp-test$(EXESUF) gcov-files-generic-y = monitor.c qapi/qmp-dispatch.c check-qtest-generic-y += tests/device-introspect-test$(EXESUF) gcov-files-generic-y = qdev-monitor.c qmp.c +check-qtest-generic-y += tests/cdrom-test$(EXESUF) gcov-files-ipack-y += hw/ipack/ipack.c check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) @@ -844,6 +845,7 @@ tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) tests/numa-test$(EXESUF): tests/numa-test.o tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y) +tests/cdrom-test$(EXESUF): tests/cdrom-test.o tests/boot-sector.o $(libqos-obj-y) tests/migration/stress$(EXESUF): tests/migration/stress.o $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") diff --git a/tests/cdrom-test.c b/tests/cdrom-test.c new file mode 100644 index 0000000000..5bbf322789 --- /dev/null +++ b/tests/cdrom-test.c @@ -0,0 +1,164 @@ +/* + * Various tests for emulated CD-ROM drives. + * + * Copyright (c) 2018 Red Hat Inc. + * + * Author: + * Thomas Huth + * + * This work is licensed under the terms of the GNU GPL, version 2 + * or later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "boot-sector.h" + +static char isoimage[] = "cdrom-boot-iso-XXXXXX"; + +static int exec_genisoimg(const char **args) +{ + gchar *out_err = NULL; + gint exit_status = -1; + bool success; + + success = g_spawn_sync(NULL, (gchar **)args, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL, + NULL, NULL, NULL, &out_err, &exit_status, NULL); + if (!success) { + return -ENOENT; + } + if (out_err) { + fputs(out_err, stderr); + g_free(out_err); + } + + return exit_status; +} + +static int prepare_image(const char *arch, char *isoimage) +{ + char srcdir[] = "cdrom-test-dir-XXXXXX"; + char *codefile = NULL; + int ifh, ret = -1; + const char *args[] = { + "genisoimage", "-quiet", "-l", "-no-emul-boot", + "-b", NULL, "-o", isoimage, srcdir, NULL + }; + + ifh = mkstemp(isoimage); + if (ifh < 0) { + perror("Error creating temporary iso image file"); + return -1; + } + if (!mkdtemp(srcdir)) { + perror("Error creating temporary directory"); + goto cleanup; + } + + if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") || + g_str_equal(arch, "s390x")) { + codefile = g_strdup_printf("%s/bootcode-XXXXXX", srcdir); + ret = boot_sector_init(codefile); + if (ret) { + goto cleanup; + } + } else { + /* Just create a dummy file */ + char txt[] = "empty disc"; + codefile = g_strdup_printf("%s/readme.txt", srcdir); + if (!g_file_set_contents(codefile, txt, sizeof(txt) - 1, NULL)) { + fprintf(stderr, "Failed to create '%s'\n", codefile); + goto cleanup; + } + } + + args[5] = strchr(codefile, '/') + 1; + ret = exec_genisoimg(args); + if (ret) { + fprintf(stderr, "genisoimage failed: %i\n", ret); + } + + unlink(codefile); + +cleanup: + g_free(codefile); + rmdir(srcdir); + close(ifh); + + return ret; +} + +static void test_cdboot(gconstpointer data) +{ + QTestState *qts; + + qts = qtest_startf("-accel kvm:tcg -no-shutdown %s%s", (const char *)data, + isoimage); + boot_sector_test(qts); + qtest_quit(qts); +} + +static void add_x86_tests(void) +{ + qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); + qtest_add_data_func("cdrom/boot/virtio-scsi", + "-device virtio-scsi -device scsi-cd,drive=cdr " + "-blockdev file,node-name=cdr,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/isapc", "-M isapc " + "-drive if=ide,media=cdrom,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/am53c974", + "-device am53c974 -device scsi-cd,drive=cd1 " + "-drive if=none,id=cd1,format=raw,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/dc390", + "-device dc390 -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/lsi53c895a", + "-device lsi53c895a -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/megasas", "-M q35 " + "-device megasas -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 " + "-device megasas-gen2 -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); +} + +static void add_s390x_tests(void) +{ + qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); + qtest_add_data_func("cdrom/boot/virtio-scsi", + "-device virtio-scsi -device scsi-cd,drive=cdr " + "-blockdev file,node-name=cdr,filename=", test_cdboot); +} + +int main(int argc, char **argv) +{ + int ret; + const char *arch = qtest_get_arch(); + const char *genisocheck[] = { "genisoimage", "-version", NULL }; + + g_test_init(&argc, &argv, NULL); + + if (exec_genisoimg(genisocheck)) { + /* genisoimage not available - so can't run tests */ + return 0; + } + + ret = prepare_image(arch, isoimage); + if (ret) { + return ret; + } + + if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64")) { + add_x86_tests(); + } else if (g_str_equal(arch, "s390x")) { + add_s390x_tests(); + } + + ret = g_test_run(); + + unlink(isoimage); + + return ret; +} From 49d43a5978703d515393c70c5620ace8e8daf8cd Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 8 Jun 2018 13:17:39 -0400 Subject: [PATCH 1089/2380] tests/cdrom-test: Test that -cdrom parameter is working MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1454509726719e0933c800 recently broke the "-cdrom" parameter on a couple of boards without us noticing it immediately. Thus let's add a test which checks that "-cdrom" can at least be used to start QEMU with certain machine types. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Reviewed-by: Hervé Poussineau Acked-By: Mark Cave-Ayland Signed-off-by: Thomas Huth Signed-off-by: John Snow --- tests/cdrom-test.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/cdrom-test.c b/tests/cdrom-test.c index 5bbf322789..7a1fce5dfb 100644 --- a/tests/cdrom-test.c +++ b/tests/cdrom-test.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "boot-sector.h" +#include "qapi/qmp/qdict.h" static char isoimage[] = "cdrom-boot-iso-XXXXXX"; @@ -89,6 +90,32 @@ cleanup: return ret; } +/** + * Check that at least the -cdrom parameter is basically working, i.e. we can + * see the filename of the ISO image in the output of "info block" afterwards + */ +static void test_cdrom_param(gconstpointer data) +{ + QTestState *qts; + char *resp; + + qts = qtest_startf("-M %s -cdrom %s", (const char *)data, isoimage); + resp = qtest_hmp(qts, "info block"); + g_assert(strstr(resp, isoimage) != 0); + g_free(resp); + qtest_quit(qts); +} + +static void add_cdrom_param_tests(const char **machines) +{ + while (*machines) { + char *testname = g_strdup_printf("cdrom/param/%s", *machines); + qtest_add_data_func(testname, *machines, test_cdrom_param); + g_free(testname); + machines++; + } +} + static void test_cdboot(gconstpointer data) { QTestState *qts; @@ -154,6 +181,37 @@ int main(int argc, char **argv) add_x86_tests(); } else if (g_str_equal(arch, "s390x")) { add_s390x_tests(); + } else if (g_str_equal(arch, "ppc64")) { + const char *ppcmachines[] = { + "pseries", "mac99", "g3beige", "40p", "prep", NULL + }; + add_cdrom_param_tests(ppcmachines); + } else if (g_str_equal(arch, "sparc")) { + const char *sparcmachines[] = { + "LX", "SPARCClassic", "SPARCbook", "SS-10", "SS-20", "SS-4", + "SS-5", "SS-600MP", "Voyager", "leon3_generic", NULL + }; + add_cdrom_param_tests(sparcmachines); + } else if (g_str_equal(arch, "sparc64")) { + const char *sparc64machines[] = { + "niagara", "sun4u", "sun4v", NULL + }; + add_cdrom_param_tests(sparc64machines); + } else if (!strncmp(arch, "mips64", 6)) { + const char *mips64machines[] = { + "magnum", "malta", "mips", "pica61", NULL + }; + add_cdrom_param_tests(mips64machines); + } else if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { + const char *armmachines[] = { + "realview-eb", "realview-eb-mpcore", "realview-pb-a8", + "realview-pbx-a9", "versatileab", "versatilepb", "vexpress-a15", + "vexpress-a9", "virt", NULL + }; + add_cdrom_param_tests(armmachines); + } else { + const char *nonemachine[] = { "none", NULL }; + add_cdrom_param_tests(nonemachine); } ret = g_test_run(); From edc35b3dc25a71f226ce9a0e6517a9ee26a023ff Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 8 Jun 2018 13:17:39 -0400 Subject: [PATCH 1090/2380] MAINTAINERS: Add the cdrom-test to John's section The cdrom-test checks various block types - IDE, SCSI and virtio, so it's a little bit hard to decide where this should belong to in the MAINTAINERS file. But John volunteered to take it, so let's put it into the IDE section for now. Signed-off-by: Thomas Huth Acked-by: John Snow Signed-off-by: John Snow --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4c73c16fee..a40f558694 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1003,6 +1003,7 @@ F: hw/block/cdrom.c F: hw/block/hd-geometry.c F: tests/ide-test.c F: tests/ahci-test.c +F: tests/cdrom-test.c F: tests/libqos/ahci* T: git git://github.com/jnsnow/qemu.git ide From 27e4648ce98f8172c2c2449896430e87652818d1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 6 Jun 2018 15:09:49 -0400 Subject: [PATCH 1091/2380] libqos/ahci: track sector size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's not always 512, and it does wind up mattering for PIO tranfers, because this means DRQ blocks are four times as big for ATAPI. Replace an instance of 2048 with the correct define, too. This patch by itself winds changing no behavior. fis->count is ignored for CMD_PACKET, and sect_count only gets used in non-ATAPI cases. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180606190955.20845-2-jsnow@redhat.com Signed-off-by: John Snow --- tests/libqos/ahci.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index bc201d762b..63e1f9b92d 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -90,6 +90,7 @@ struct AHCICommand { uint32_t interrupts; uint64_t xbytes; uint32_t prd_size; + uint32_t sector_size; uint64_t buffer; AHCICommandProp *props; /* Data to be transferred to the guest */ @@ -796,7 +797,7 @@ static void command_header_init(AHCICommand *cmd) static void command_table_init(AHCICommand *cmd) { RegH2DFIS *fis = &(cmd->fis); - uint16_t sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE); + uint16_t sect_count = (cmd->xbytes / cmd->sector_size); fis->fis_type = REG_H2D_FIS; fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */ @@ -819,7 +820,7 @@ static void command_table_init(AHCICommand *cmd) if (cmd->props->lba28 || cmd->props->lba48) { fis->device = ATA_DEVICE_LBA; } - fis->count = (cmd->xbytes / AHCI_SECTOR_SIZE); + fis->count = (cmd->xbytes / cmd->sector_size); } fis->icc = 0x00; fis->control = 0x00; @@ -857,6 +858,7 @@ AHCICommand *ahci_command_create(uint8_t command_name) cmd->xbytes = props->size; cmd->prd_size = 4096; cmd->buffer = 0xabad1dea; + cmd->sector_size = props->atapi ? ATAPI_SECTOR_SIZE : AHCI_SECTOR_SIZE; if (!cmd->props->ncq) { cmd->interrupts = AHCI_PX_IS_DHRS; @@ -1033,7 +1035,7 @@ void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer) static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes) { unsigned char *cbd = cmd->atapi_cmd; - uint64_t nsectors = xbytes / 2048; + uint64_t nsectors = xbytes / ATAPI_SECTOR_SIZE; uint32_t tmp; g_assert(cbd); @@ -1080,7 +1082,7 @@ void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, cmd->prd_size = prd_size; } cmd->xbytes = xbytes; - sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE); + sect_count = (cmd->xbytes / cmd->sector_size); if (cmd->props->ncq) { NCQFIS *nfis = (NCQFIS *)&(cmd->fis); From 956556e131e35f387ac482ad7b41151576fef057 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 6 Jun 2018 15:09:50 -0400 Subject: [PATCH 1092/2380] ahci: move PIO Setup FIS before transfer, fix it for ATAPI commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PIO Setup FIS is written in the PIO:Entry state, which comes before the ATA and ATAPI data transfer states. As a result, the PIO Setup FIS interrupt is now raised before DMA ends for ATAPI commands, and tests have to be adjusted. This is also hinted by the description of the command header in the AHCI specification, where the "A" bit is described as When ‘1’, indicates that a PIO setup FIS shall be sent by the device indicating a transfer for the ATAPI command. and also by the description of the ACMD (ATAPI command region): The ATAPI command must be either 12 or 16 bytes in length. The length transmitted by the HBA is determined by the PIO setup FIS that is sent by the device requesting the ATAPI command. QEMU, which conflates the "generator" and the "receiver" of the FIS into one device, always uses ATAPI_PACKET_SIZE, aka 12, for the length. Signed-off-by: Paolo Bonzini Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180606190955.20845-3-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 16 +++++----------- tests/libqos/ahci.c | 35 +++++++++++++++++++++-------------- tests/libqos/ahci.h | 3 +-- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 571e32dd66..f25bef501d 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1259,7 +1259,6 @@ static void handle_reg_h2d_fis(AHCIState *s, int port, g_free(pretty_fis); } s->dev[port].done_atapi_packet = false; - /* XXX send PIO setup FIS */ } ide_state->error = 0; @@ -1353,10 +1352,12 @@ static void ahci_start_transfer(IDEDMA *dma) int is_atapi = opts & AHCI_CMD_ATAPI; int has_sglist = 0; + /* PIO FIS gets written prior to transfer */ + ahci_write_fis_pio(ad, size); + if (is_atapi && !ad->done_atapi_packet) { /* already prepopulated iobuffer */ ad->done_atapi_packet = true; - size = 0; goto out; } @@ -1376,19 +1377,12 @@ static void ahci_start_transfer(IDEDMA *dma) } } + /* Update number of transferred bytes, destroy sglist */ + dma_buf_commit(s, size); out: /* declare that we processed everything */ s->data_ptr = s->data_end; - - /* Update number of transferred bytes, destroy sglist */ - dma_buf_commit(s, size); - s->end_transfer_func(s); - - if (!(s->status & DRQ_STAT)) { - /* done with PIO send/receive */ - ahci_write_fis_pio(ad, le32_to_cpu(ad->cur_cmd->status)); - } } static void ahci_start_dma(IDEDMA *dma, IDEState *s, diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index 63e1f9b92d..7264e085d0 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -478,10 +478,10 @@ void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot) g_free(d2h); } -void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, - uint8_t slot, size_t buffsize) +void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd) { PIOSetupFIS *pio = g_malloc0(0x20); + uint8_t port = cmd->port; /* We cannot check the Status or E_Status registers, because * the status may have again changed between the PIO Setup FIS @@ -489,15 +489,22 @@ void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x20, pio, 0x20); g_assert_cmphex(pio->fis_type, ==, 0x5f); - /* BUG: PIO Setup FIS as utilized by QEMU tries to fit the entire - * transfer size in a uint16_t field. The maximum transfer size can - * eclipse this; the field is meant to convey the size of data per - * each Data FIS, not the entire operation as a whole. For now, - * we will sanity check the broken case where applicable. */ - if (buffsize <= UINT16_MAX) { - g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, buffsize); + /* Data transferred by PIO will either be: + * (1) 12 or 16 bytes for an ATAPI command packet (QEMU always uses 12), or + * (2) Actual data from the drive. + * If we do both, (2) winds up erasing any evidence of (1). + */ + if (cmd->props->atapi && (cmd->xbytes == 0 || cmd->props->dma)) { + g_assert(le16_to_cpu(pio->tx_count) == 12 || + le16_to_cpu(pio->tx_count) == 16); + } else { + /* The AHCI test suite here does not test any PIO command that specifies + * a DRQ block larger than one sector (like 0xC4), so this should always + * be one sector or less. */ + size_t pio_len = ((cmd->xbytes % cmd->sector_size) ? + (cmd->xbytes % cmd->sector_size) : cmd->sector_size); + g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, pio_len); } - g_free(pio); } @@ -832,9 +839,9 @@ void ahci_command_enable_atapi_dma(AHCICommand *cmd) RegH2DFIS *fis = &(cmd->fis); g_assert(cmd->props->atapi); fis->feature_low |= 0x01; - cmd->interrupts &= ~AHCI_PX_IS_PSS; + /* PIO is still used to transfer the ATAPI command */ + g_assert(cmd->props->pio); cmd->props->dma = true; - cmd->props->pio = false; /* BUG: We expect the DMA Setup interrupt for DMA commands */ /* cmd->interrupts |= AHCI_PX_IS_DSS; */ } @@ -846,7 +853,7 @@ AHCICommand *ahci_command_create(uint8_t command_name) g_assert(props); cmd = g_new0(AHCICommand, 1); - g_assert(!(props->dma && props->pio)); + g_assert(!(props->dma && props->pio) || props->atapi); g_assert(!(props->lba28 && props->lba48)); g_assert(!(props->read && props->write)); g_assert(!props->size || props->data); @@ -1218,7 +1225,7 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd) ahci_port_check_d2h_sanity(ahci, port, slot); } if (cmd->props->pio) { - ahci_port_check_pio_sanity(ahci, port, slot, cmd->xbytes); + ahci_port_check_pio_sanity(ahci, cmd); } } diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index 715ca1e226..13f6d87b75 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -596,8 +596,7 @@ void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port, uint32_t intr_mask); void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot); void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot); -void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, - uint8_t slot, size_t buffsize); +void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd); void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd); /* Misc */ From bed9bcfa3275a9cfee82846a9f521c4858a9739a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Jun 2018 15:09:51 -0400 Subject: [PATCH 1093/2380] ide: push end_transfer_func out of start_transfer callback, rename callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that end_transfer_func is a tail call in ahci_start_transfer, formalize the fact that the callback (of which ahci_start_transfer is the sole implementation) takes care of the transfer too: rename it to pio_transfer and, if it is present, call the end_transfer_func as soon as it returns. Signed-off-by: Paolo Bonzini Signed-off-by: John Snow Reviewed-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180606190955.20845-4-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/ahci.c | 13 ++++++------- hw/ide/core.c | 8 +++++--- hw/ide/trace-events | 2 +- include/hw/ide/internal.h | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index f25bef501d..f7852be842 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1340,8 +1340,8 @@ out: return 0; } -/* DMA dev <-> ram */ -static void ahci_start_transfer(IDEDMA *dma) +/* Transfer PIO data between RAM and device */ +static void ahci_pio_transfer(IDEDMA *dma) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; @@ -1365,9 +1365,9 @@ static void ahci_start_transfer(IDEDMA *dma) has_sglist = 1; } - trace_ahci_start_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read", - size, is_atapi ? "atapi" : "ata", - has_sglist ? "" : "o"); + trace_ahci_pio_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read", + size, is_atapi ? "atapi" : "ata", + has_sglist ? "" : "o"); if (has_sglist && size) { if (is_write) { @@ -1382,7 +1382,6 @@ static void ahci_start_transfer(IDEDMA *dma) out: /* declare that we processed everything */ s->data_ptr = s->data_end; - s->end_transfer_func(s); } static void ahci_start_dma(IDEDMA *dma, IDEState *s, @@ -1503,7 +1502,7 @@ static const IDEDMAOps ahci_dma_ops = { .start_dma = ahci_start_dma, .restart = ahci_restart, .restart_dma = ahci_restart_dma, - .start_transfer = ahci_start_transfer, + .pio_transfer = ahci_pio_transfer, .prepare_buf = ahci_dma_prepare_buf, .commit_buf = ahci_commit_buf, .rw_buf = ahci_dma_rw_buf, diff --git a/hw/ide/core.c b/hw/ide/core.c index cc9ca28c33..1a6cb337bf 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -526,16 +526,18 @@ static void ide_clear_retry(IDEState *s) void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func) { - s->end_transfer_func = end_transfer_func; s->data_ptr = buf; s->data_end = buf + size; ide_set_retry(s); if (!(s->status & ERR_STAT)) { s->status |= DRQ_STAT; } - if (s->bus->dma->ops->start_transfer) { - s->bus->dma->ops->start_transfer(s->bus->dma); + if (!s->bus->dma->ops->pio_transfer) { + s->end_transfer_func = end_transfer_func; + return; } + s->bus->dma->ops->pio_transfer(s->bus->dma); + end_transfer_func(s); } static void ide_cmd_done(IDEState *s) diff --git a/hw/ide/trace-events b/hw/ide/trace-events index e6bd95f52f..65d6f9034d 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -108,7 +108,7 @@ handle_cmd_badport(void *s, int port) "ahci(%p)[%d]: guest accessed unused port" handle_cmd_badfis(void *s, int port) "ahci(%p)[%d]: guest provided an invalid cmd FIS" handle_cmd_badmap(void *s, int port, uint64_t len) "ahci(%p)[%d]: dma_memory_map failed, 0x%02"PRIx64" != 0x80" handle_cmd_unhandled_fis(void *s, int port, uint8_t b0, uint8_t b1, uint8_t b2) "ahci(%p)[%d]: unhandled FIS type. cmd_fis: 0x%02x-%02x-%02x" -ahci_start_transfer(void *s, int port, const char *rw, uint32_t size, const char *tgt, const char *sgl) "ahci(%p)[%d]: %sing %d bytes on %s w/%s sglist" +ahci_pio_transfer(void *s, int port, const char *rw, uint32_t size, const char *tgt, const char *sgl) "ahci(%p)[%d]: %sing %d bytes on %s w/%s sglist" ahci_start_dma(void *s, int port) "ahci(%p)[%d]: start dma" ahci_dma_prepare_buf(void *s, int port, int32_t io_buffer_size, int32_t limit) "ahci(%p)[%d]: prepare buf limit=%"PRId32" prepared=%"PRId32 ahci_dma_prepare_buf_fail(void *s, int port) "ahci(%p)[%d]: sglist population failed" diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 88212f59df..f3de6f9b73 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -444,7 +444,7 @@ struct IDEState { struct IDEDMAOps { DMAStartFunc *start_dma; - DMAVoidFunc *start_transfer; + DMAVoidFunc *pio_transfer; DMAInt32Func *prepare_buf; DMAu32Func *commit_buf; DMAIntFunc *rw_buf; From ee4cd662addd2ef51c5bc33e57f7fc6cdee830a0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Jun 2018 15:09:52 -0400 Subject: [PATCH 1094/2380] ide: call ide_cmd_done from ide_transfer_stop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code can simply be moved to the sole caller that has notify == true. Signed-off-by: Paolo Bonzini Signed-off-by: John Snow Reviewed-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180606190955.20845-5-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/core.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 1a6cb337bf..54799ea6fb 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -548,26 +548,23 @@ static void ide_cmd_done(IDEState *s) } static void ide_transfer_halt(IDEState *s, - void(*end_transfer_func)(IDEState *), - bool notify) + void(*end_transfer_func)(IDEState *)) { s->end_transfer_func = end_transfer_func; s->data_ptr = s->io_buffer; s->data_end = s->io_buffer; s->status &= ~DRQ_STAT; - if (notify) { - ide_cmd_done(s); - } } void ide_transfer_stop(IDEState *s) { - ide_transfer_halt(s, ide_transfer_stop, true); + ide_transfer_halt(s, ide_transfer_stop); + ide_cmd_done(s); } static void ide_transfer_cancel(IDEState *s) { - ide_transfer_halt(s, ide_transfer_cancel, false); + ide_transfer_halt(s, ide_transfer_cancel); } int64_t ide_get_sector(IDEState *s) From 882941a5680db4d3e247a089126be23181ae13fd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Jun 2018 15:09:53 -0400 Subject: [PATCH 1095/2380] ide: make ide_transfer_stop idempotent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is code checking s->end_transfer_func and it was not taught about ide_transfer_cancel. We can just use ide_transfer_stop because s->end_transfer_func is only ever called in the DRQ phase. ide_transfer_cancel can then be removed, since it would just be calling ide_transfer_halt. Signed-off-by: Paolo Bonzini Signed-off-by: John Snow Reviewed-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180606190955.20845-6-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/core.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 54799ea6fb..9c4864ae54 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -547,10 +547,9 @@ static void ide_cmd_done(IDEState *s) } } -static void ide_transfer_halt(IDEState *s, - void(*end_transfer_func)(IDEState *)) +static void ide_transfer_halt(IDEState *s) { - s->end_transfer_func = end_transfer_func; + s->end_transfer_func = ide_transfer_stop; s->data_ptr = s->io_buffer; s->data_end = s->io_buffer; s->status &= ~DRQ_STAT; @@ -558,15 +557,10 @@ static void ide_transfer_halt(IDEState *s, void ide_transfer_stop(IDEState *s) { - ide_transfer_halt(s, ide_transfer_stop); + ide_transfer_halt(s); ide_cmd_done(s); } -static void ide_transfer_cancel(IDEState *s) -{ - ide_transfer_halt(s, ide_transfer_cancel); -} - int64_t ide_get_sector(IDEState *s) { int64_t sector_num; @@ -1361,7 +1355,7 @@ static bool cmd_nop(IDEState *s, uint8_t cmd) static bool cmd_device_reset(IDEState *s, uint8_t cmd) { /* Halt PIO (in the DRQ phase), then DMA */ - ide_transfer_cancel(s); + ide_transfer_halt(s); ide_cancel_dma_sync(s); /* Reset any PIO commands, reset signature, etc */ From d02cea6437b420150915b03aef3691010c7d40df Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Jun 2018 15:09:54 -0400 Subject: [PATCH 1096/2380] atapi: call ide_set_irq before ide_transfer_start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ATAPI_INT_REASON_IO interrupt is raised when I/O starts, but in the AHCI case ide_set_irq was actually called at the end of a mutual recursion. Move it early, with the side effect that ide_transfer_start becomes a tail call in ide_atapi_cmd_reply_end. Signed-off-by: Paolo Bonzini Signed-off-by: John Snow Reviewed-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180606190955.20845-7-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/atapi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index c0509c8bf5..7168ff55a7 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -287,6 +287,7 @@ void ide_atapi_cmd_reply_end(IDEState *s) } else { /* a new transfer is needed */ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; + ide_set_irq(s->bus); byte_count_limit = atapi_byte_count_limit(s); trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); size = s->packet_transfer_size; @@ -304,13 +305,12 @@ void ide_atapi_cmd_reply_end(IDEState *s) if (size > (s->cd_sector_size - s->io_buffer_index)) size = (s->cd_sector_size - s->io_buffer_index); } + trace_ide_atapi_cmd_reply_end_new(s, s->status); s->packet_transfer_size -= size; s->elementary_transfer_size -= size; s->io_buffer_index += size; ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size, size, ide_atapi_cmd_reply_end); - ide_set_irq(s->bus); - trace_ide_atapi_cmd_reply_end_new(s, s->status); } } } From c173723f247c69974a83af1395020d0f01a0d334 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Jun 2018 15:09:55 -0400 Subject: [PATCH 1097/2380] ide: introduce ide_transfer_start_norecurse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the case where the end_transfer_func is also the caller of ide_transfer_start, the mutual recursion can lead to unlimited stack usage. Introduce a new version that can be used to change tail recursion into a loop, and use it in trace_ide_atapi_cmd_reply_end. Signed-off-by: Paolo Bonzini Signed-off-by: John Snow Reviewed-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180606190955.20845-8-jsnow@redhat.com Signed-off-by: John Snow --- hw/ide/atapi.c | 42 +++++++++++++++++++++------------------ hw/ide/core.c | 16 +++++++++++---- include/hw/ide/internal.h | 2 ++ 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 7168ff55a7..39e473f9c2 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -245,15 +245,11 @@ static uint16_t atapi_byte_count_limit(IDEState *s) void ide_atapi_cmd_reply_end(IDEState *s) { int byte_count_limit, size, ret; - trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, - s->elementary_transfer_size, - s->io_buffer_index); - if (s->packet_transfer_size <= 0) { - /* end of transfer */ - ide_atapi_cmd_ok(s); - ide_set_irq(s->bus); - trace_ide_atapi_cmd_reply_end_eot(s, s->status); - } else { + while (s->packet_transfer_size > 0) { + trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, + s->elementary_transfer_size, + s->io_buffer_index); + /* see if a new sector must be read */ if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { if (!s->elementary_transfer_size) { @@ -279,11 +275,6 @@ void ide_atapi_cmd_reply_end(IDEState *s) size = s->cd_sector_size - s->io_buffer_index; if (size > s->elementary_transfer_size) size = s->elementary_transfer_size; - s->packet_transfer_size -= size; - s->elementary_transfer_size -= size; - s->io_buffer_index += size; - ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size, - size, ide_atapi_cmd_reply_end); } else { /* a new transfer is needed */ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; @@ -306,13 +297,26 @@ void ide_atapi_cmd_reply_end(IDEState *s) size = (s->cd_sector_size - s->io_buffer_index); } trace_ide_atapi_cmd_reply_end_new(s, s->status); - s->packet_transfer_size -= size; - s->elementary_transfer_size -= size; - s->io_buffer_index += size; - ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size, - size, ide_atapi_cmd_reply_end); + } + s->packet_transfer_size -= size; + s->elementary_transfer_size -= size; + s->io_buffer_index += size; + + /* Some adapters process PIO data right away. In that case, we need + * to avoid mutual recursion between ide_transfer_start + * and ide_atapi_cmd_reply_end. + */ + if (!ide_transfer_start_norecurse(s, + s->io_buffer + s->io_buffer_index - size, + size, ide_atapi_cmd_reply_end)) { + return; } } + + /* end of transfer */ + trace_ide_atapi_cmd_reply_end_eot(s, s->status); + ide_atapi_cmd_ok(s); + ide_set_irq(s->bus); } /* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */ diff --git a/hw/ide/core.c b/hw/ide/core.c index 9c4864ae54..2c62efc536 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -523,8 +523,8 @@ static void ide_clear_retry(IDEState *s) } /* prepare data transfer and tell what to do after */ -void ide_transfer_start(IDEState *s, uint8_t *buf, int size, - EndTransferFunc *end_transfer_func) +bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size, + EndTransferFunc *end_transfer_func) { s->data_ptr = buf; s->data_end = buf + size; @@ -534,10 +534,18 @@ void ide_transfer_start(IDEState *s, uint8_t *buf, int size, } if (!s->bus->dma->ops->pio_transfer) { s->end_transfer_func = end_transfer_func; - return; + return false; } s->bus->dma->ops->pio_transfer(s->bus->dma); - end_transfer_func(s); + return true; +} + +void ide_transfer_start(IDEState *s, uint8_t *buf, int size, + EndTransferFunc *end_transfer_func) +{ + if (ide_transfer_start_norecurse(s, buf, size, end_transfer_func)) { + end_transfer_func(s); + } } static void ide_cmd_done(IDEState *s) diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index f3de6f9b73..594081e57f 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -623,6 +623,8 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val); void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func); +bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size, + EndTransferFunc *end_transfer_func); void ide_transfer_stop(IDEState *s); void ide_set_inactive(IDEState *s, bool more); BlockAIOCB *ide_issue_trim( From f03868bd5653265e97b253102d77d83ea85efdea Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 09:29:43 -0300 Subject: [PATCH 1098/2380] python: futurize -f libfuturize.fixes.fix_print_with_import Change all Python code to use print as a function. This is necessary for Python 3 compatibility. Done using: $ py=$( (g grep -l -E '^#!.*python';find -name '*.py' -printf '%P\n';) | \ sort -u | grep -v README.sh4) $ futurize -w -f libfuturize.fixes.fix_print_with_import $py Reviewed-by: Stefan Hajnoczi Acked-by: Fam Zheng Signed-off-by: Eduardo Habkost Message-Id: <20180608122952.2009-2-ehabkost@redhat.com> [ehabkost: fixup tests/docker/docker.py] Signed-off-by: Eduardo Habkost --- scripts/analyse-9p-simpletrace.py | 89 ++++++++++++------------ scripts/analyse-locks-simpletrace.py | 1 + scripts/analyze-migration.py | 11 +-- scripts/device-crash-test | 3 +- scripts/dump-guest-memory.py | 1 + scripts/kvm/kvm_flightrecorder | 21 +++--- scripts/kvm/vmxcap | 1 + scripts/qmp/qemu-ga-client | 1 + scripts/qmp/qmp | 17 ++--- scripts/qmp/qmp-shell | 35 +++++----- scripts/qmp/qom-get | 7 +- scripts/qmp/qom-list | 11 +-- scripts/qmp/qom-set | 5 +- scripts/qmp/qom-tree | 11 +-- scripts/replay-dump.py | 21 +++--- scripts/signrom.py | 1 + scripts/simpletrace.py | 3 +- scripts/vmstate-static-checker.py | 85 +++++++++++----------- tests/docker/docker.py | 17 ++--- tests/docker/travis.py | 15 ++-- tests/guest-debug/test-gdbstub.py | 1 + tests/image-fuzzer/runner.py | 38 +++++----- tests/migration/guestperf/engine.py | 29 ++++---- tests/migration/guestperf/plot.py | 17 ++--- tests/migration/guestperf/shell.py | 19 ++--- tests/qemu-iotests/149 | 3 +- tests/qemu-iotests/165 | 3 +- tests/qemu-iotests/iotests.py | 5 +- tests/qemu-iotests/nbd-fault-injector.py | 7 +- tests/qemu-iotests/qcow2.py | 39 ++++++----- tests/qemu-iotests/qed.py | 17 ++--- tests/vm/basevm.py | 3 +- 32 files changed, 281 insertions(+), 256 deletions(-) diff --git a/scripts/analyse-9p-simpletrace.py b/scripts/analyse-9p-simpletrace.py index 3c3dee4337..710e01adba 100755 --- a/scripts/analyse-9p-simpletrace.py +++ b/scripts/analyse-9p-simpletrace.py @@ -3,6 +3,7 @@ # Usage: ./analyse-9p-simpletrace # # Author: Harsh Prateek Bora +from __future__ import print_function import os import simpletrace @@ -79,135 +80,135 @@ symbol_9p = { class VirtFSRequestTracker(simpletrace.Analyzer): def begin(self): - print "Pretty printing 9p simpletrace log ..." + print("Pretty printing 9p simpletrace log ...") def v9fs_rerror(self, tag, id, err): - print "RERROR (tag =", tag, ", id =", symbol_9p[id], ", err = \"", os.strerror(err), "\")" + print("RERROR (tag =", tag, ", id =", symbol_9p[id], ", err = \"", os.strerror(err), "\")") def v9fs_version(self, tag, id, msize, version): - print "TVERSION (tag =", tag, ", msize =", msize, ", version =", version, ")" + print("TVERSION (tag =", tag, ", msize =", msize, ", version =", version, ")") def v9fs_version_return(self, tag, id, msize, version): - print "RVERSION (tag =", tag, ", msize =", msize, ", version =", version, ")" + print("RVERSION (tag =", tag, ", msize =", msize, ", version =", version, ")") def v9fs_attach(self, tag, id, fid, afid, uname, aname): - print "TATTACH (tag =", tag, ", fid =", fid, ", afid =", afid, ", uname =", uname, ", aname =", aname, ")" + print("TATTACH (tag =", tag, ", fid =", fid, ", afid =", afid, ", uname =", uname, ", aname =", aname, ")") def v9fs_attach_return(self, tag, id, type, version, path): - print "RATTACH (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})" + print("RATTACH (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})") def v9fs_stat(self, tag, id, fid): - print "TSTAT (tag =", tag, ", fid =", fid, ")" + print("TSTAT (tag =", tag, ", fid =", fid, ")") def v9fs_stat_return(self, tag, id, mode, atime, mtime, length): - print "RSTAT (tag =", tag, ", mode =", mode, ", atime =", atime, ", mtime =", mtime, ", length =", length, ")" + print("RSTAT (tag =", tag, ", mode =", mode, ", atime =", atime, ", mtime =", mtime, ", length =", length, ")") def v9fs_getattr(self, tag, id, fid, request_mask): - print "TGETATTR (tag =", tag, ", fid =", fid, ", request_mask =", hex(request_mask), ")" + print("TGETATTR (tag =", tag, ", fid =", fid, ", request_mask =", hex(request_mask), ")") def v9fs_getattr_return(self, tag, id, result_mask, mode, uid, gid): - print "RGETATTR (tag =", tag, ", result_mask =", hex(result_mask), ", mode =", oct(mode), ", uid =", uid, ", gid =", gid, ")" + print("RGETATTR (tag =", tag, ", result_mask =", hex(result_mask), ", mode =", oct(mode), ", uid =", uid, ", gid =", gid, ")") def v9fs_walk(self, tag, id, fid, newfid, nwnames): - print "TWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", nwnames =", nwnames, ")" + print("TWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", nwnames =", nwnames, ")") def v9fs_walk_return(self, tag, id, nwnames, qids): - print "RWALK (tag =", tag, ", nwnames =", nwnames, ", qids =", hex(qids), ")" + print("RWALK (tag =", tag, ", nwnames =", nwnames, ", qids =", hex(qids), ")") def v9fs_open(self, tag, id, fid, mode): - print "TOPEN (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ")" + print("TOPEN (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ")") def v9fs_open_return(self, tag, id, type, version, path, iounit): - print "ROPEN (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")" + print("ROPEN (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")") def v9fs_lcreate(self, tag, id, dfid, flags, mode, gid): - print "TLCREATE (tag =", tag, ", dfid =", dfid, ", flags =", oct(flags), ", mode =", oct(mode), ", gid =", gid, ")" + print("TLCREATE (tag =", tag, ", dfid =", dfid, ", flags =", oct(flags), ", mode =", oct(mode), ", gid =", gid, ")") def v9fs_lcreate_return(self, tag, id, type, version, path, iounit): - print "RLCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")" + print("RLCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")") def v9fs_fsync(self, tag, id, fid, datasync): - print "TFSYNC (tag =", tag, ", fid =", fid, ", datasync =", datasync, ")" + print("TFSYNC (tag =", tag, ", fid =", fid, ", datasync =", datasync, ")") def v9fs_clunk(self, tag, id, fid): - print "TCLUNK (tag =", tag, ", fid =", fid, ")" + print("TCLUNK (tag =", tag, ", fid =", fid, ")") def v9fs_read(self, tag, id, fid, off, max_count): - print "TREAD (tag =", tag, ", fid =", fid, ", off =", off, ", max_count =", max_count, ")" + print("TREAD (tag =", tag, ", fid =", fid, ", off =", off, ", max_count =", max_count, ")") def v9fs_read_return(self, tag, id, count, err): - print "RREAD (tag =", tag, ", count =", count, ", err =", err, ")" + print("RREAD (tag =", tag, ", count =", count, ", err =", err, ")") def v9fs_readdir(self, tag, id, fid, offset, max_count): - print "TREADDIR (tag =", tag, ", fid =", fid, ", offset =", offset, ", max_count =", max_count, ")" + print("TREADDIR (tag =", tag, ", fid =", fid, ", offset =", offset, ", max_count =", max_count, ")") def v9fs_readdir_return(self, tag, id, count, retval): - print "RREADDIR (tag =", tag, ", count =", count, ", retval =", retval, ")" + print("RREADDIR (tag =", tag, ", count =", count, ", retval =", retval, ")") def v9fs_write(self, tag, id, fid, off, count, cnt): - print "TWRITE (tag =", tag, ", fid =", fid, ", off =", off, ", count =", count, ", cnt =", cnt, ")" + print("TWRITE (tag =", tag, ", fid =", fid, ", off =", off, ", count =", count, ", cnt =", cnt, ")") def v9fs_write_return(self, tag, id, total, err): - print "RWRITE (tag =", tag, ", total =", total, ", err =", err, ")" + print("RWRITE (tag =", tag, ", total =", total, ", err =", err, ")") def v9fs_create(self, tag, id, fid, name, perm, mode): - print "TCREATE (tag =", tag, ", fid =", fid, ", perm =", oct(perm), ", name =", name, ", mode =", oct(mode), ")" + print("TCREATE (tag =", tag, ", fid =", fid, ", perm =", oct(perm), ", name =", name, ", mode =", oct(mode), ")") def v9fs_create_return(self, tag, id, type, version, path, iounit): - print "RCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")" + print("RCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")") def v9fs_symlink(self, tag, id, fid, name, symname, gid): - print "TSYMLINK (tag =", tag, ", fid =", fid, ", name =", name, ", symname =", symname, ", gid =", gid, ")" + print("TSYMLINK (tag =", tag, ", fid =", fid, ", name =", name, ", symname =", symname, ", gid =", gid, ")") def v9fs_symlink_return(self, tag, id, type, version, path): - print "RSYMLINK (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})" + print("RSYMLINK (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})") def v9fs_flush(self, tag, id, flush_tag): - print "TFLUSH (tag =", tag, ", flush_tag =", flush_tag, ")" + print("TFLUSH (tag =", tag, ", flush_tag =", flush_tag, ")") def v9fs_link(self, tag, id, dfid, oldfid, name): - print "TLINK (tag =", tag, ", dfid =", dfid, ", oldfid =", oldfid, ", name =", name, ")" + print("TLINK (tag =", tag, ", dfid =", dfid, ", oldfid =", oldfid, ", name =", name, ")") def v9fs_remove(self, tag, id, fid): - print "TREMOVE (tag =", tag, ", fid =", fid, ")" + print("TREMOVE (tag =", tag, ", fid =", fid, ")") def v9fs_wstat(self, tag, id, fid, mode, atime, mtime): - print "TWSTAT (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", atime =", atime, "mtime =", mtime, ")" + print("TWSTAT (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", atime =", atime, "mtime =", mtime, ")") def v9fs_mknod(self, tag, id, fid, mode, major, minor): - print "TMKNOD (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", major =", major, ", minor =", minor, ")" + print("TMKNOD (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", major =", major, ", minor =", minor, ")") def v9fs_lock(self, tag, id, fid, type, start, length): - print "TLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")" + print("TLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")") def v9fs_lock_return(self, tag, id, status): - print "RLOCK (tag =", tag, ", status =", status, ")" + print("RLOCK (tag =", tag, ", status =", status, ")") def v9fs_getlock(self, tag, id, fid, type, start, length): - print "TGETLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")" + print("TGETLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")") def v9fs_getlock_return(self, tag, id, type, start, length, proc_id): - print "RGETLOCK (tag =", tag, "type =", type, ", start =", start, ", length =", length, ", proc_id =", proc_id, ")" + print("RGETLOCK (tag =", tag, "type =", type, ", start =", start, ", length =", length, ", proc_id =", proc_id, ")") def v9fs_mkdir(self, tag, id, fid, name, mode, gid): - print "TMKDIR (tag =", tag, ", fid =", fid, ", name =", name, ", mode =", mode, ", gid =", gid, ")" + print("TMKDIR (tag =", tag, ", fid =", fid, ", name =", name, ", mode =", mode, ", gid =", gid, ")") def v9fs_mkdir_return(self, tag, id, type, version, path, err): - print "RMKDIR (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, err =", err, ")" + print("RMKDIR (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, err =", err, ")") def v9fs_xattrwalk(self, tag, id, fid, newfid, name): - print "TXATTRWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", xattr name =", name, ")" + print("TXATTRWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", xattr name =", name, ")") def v9fs_xattrwalk_return(self, tag, id, size): - print "RXATTRWALK (tag =", tag, ", xattrsize =", size, ")" + print("RXATTRWALK (tag =", tag, ", xattrsize =", size, ")") def v9fs_xattrcreate(self, tag, id, fid, name, size, flags): - print "TXATTRCREATE (tag =", tag, ", fid =", fid, ", name =", name, ", xattrsize =", size, ", flags =", flags, ")" + print("TXATTRCREATE (tag =", tag, ", fid =", fid, ", name =", name, ", xattrsize =", size, ", flags =", flags, ")") def v9fs_readlink(self, tag, id, fid): - print "TREADLINK (tag =", tag, ", fid =", fid, ")" + print("TREADLINK (tag =", tag, ", fid =", fid, ")") def v9fs_readlink_return(self, tag, id, target): - print "RREADLINK (tag =", tag, ", target =", target, ")" + print("RREADLINK (tag =", tag, ", target =", target, ")") simpletrace.run(VirtFSRequestTracker()) diff --git a/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py index 101e84dea5..352bc9c22d 100755 --- a/scripts/analyse-locks-simpletrace.py +++ b/scripts/analyse-locks-simpletrace.py @@ -6,6 +6,7 @@ # Author: Alex Bennée # +from __future__ import print_function import os import simpletrace import argparse diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index 88ff4adb30..5c2010c917 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, see . +from __future__ import print_function import numpy as np import json import os @@ -162,7 +163,7 @@ class RamSection(object): len = self.file.read64() self.sizeinfo[self.name] = '0x%016x' % len if self.write_memory: - print self.name + print(self.name) mkdir_p('./' + os.path.dirname(self.name)) f = open('./' + self.name, "wb") f.truncate(0) @@ -588,7 +589,7 @@ if args.extract: dump = MigrationDump(args.file) dump.read(desc_only = True) - print "desc.json" + print("desc.json") f = open("desc.json", "wb") f.truncate() f.write(jsonenc.encode(dump.vmsd_desc)) @@ -596,7 +597,7 @@ if args.extract: dump.read(write_memory = True) dict = dump.getDict() - print "state.json" + print("state.json") f = open("state.json", "wb") f.truncate() f.write(jsonenc.encode(dict)) @@ -605,10 +606,10 @@ elif args.dump == "state": dump = MigrationDump(args.file) dump.read(dump_memory = args.memory) dict = dump.getDict() - print jsonenc.encode(dict) + print(jsonenc.encode(dict)) elif args.dump == "desc": dump = MigrationDump(args.file) dump.read(desc_only = True) - print jsonenc.encode(dump.vmsd_desc) + print(jsonenc.encode(dump.vmsd_desc)) else: raise Exception("Please specify either -x, -d state or -d dump") diff --git a/scripts/device-crash-test b/scripts/device-crash-test index b3ce72069f..e6c233e9bf 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -23,6 +23,7 @@ Run QEMU with all combinations of -machine and -device types, check for crashes and unexpected errors. """ +from __future__ import print_function import sys import os @@ -554,7 +555,7 @@ def main(): tc[k] = v if len(binariesToTest(args, tc)) == 0: - print >>sys.stderr, "No QEMU binary found" + print("No QEMU binary found", file=sys.stderr) parser.print_usage(sys.stderr) return 1 diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py index 276eebf0c2..5a857cebcf 100644 --- a/scripts/dump-guest-memory.py +++ b/scripts/dump-guest-memory.py @@ -12,6 +12,7 @@ Authors: This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. """ +from __future__ import print_function import ctypes import struct diff --git a/scripts/kvm/kvm_flightrecorder b/scripts/kvm/kvm_flightrecorder index 7fb1c2d1a7..54a56745e4 100755 --- a/scripts/kvm/kvm_flightrecorder +++ b/scripts/kvm/kvm_flightrecorder @@ -32,6 +32,7 @@ # consuming CPU cycles. No disk I/O is performed since the ring buffer holds a # fixed-size in-memory trace. +from __future__ import print_function import sys import os @@ -77,8 +78,8 @@ def tail_trace(): pass def usage(): - print 'Usage: %s start [buffer_size_kb] | stop | dump | tail' % sys.argv[0] - print 'Control the KVM flight recorder tracing.' + print('Usage: %s start [buffer_size_kb] | stop | dump | tail' % sys.argv[0]) + print('Control the KVM flight recorder tracing.') sys.exit(0) def main(): @@ -87,15 +88,15 @@ def main(): cmd = sys.argv[1] if cmd == '--version': - print 'kvm_flightrecorder version 1.0' + print('kvm_flightrecorder version 1.0') sys.exit(0) if not os.path.isdir(tracing_dir): - print 'Unable to tracing debugfs directory, try:' - print 'mount -t debugfs none /sys/kernel/debug' + print('Unable to tracing debugfs directory, try:') + print('mount -t debugfs none /sys/kernel/debug') sys.exit(1) if not os.access(tracing_dir, os.W_OK): - print 'Unable to write to tracing debugfs directory, please run as root' + print('Unable to write to tracing debugfs directory, please run as root') sys.exit(1) if cmd == 'start': @@ -105,16 +106,16 @@ def main(): try: buffer_size_kb = int(sys.argv[2]) except ValueError: - print 'Invalid per-cpu trace buffer size in KB' + print('Invalid per-cpu trace buffer size in KB') sys.exit(1) write_file(trace_path('buffer_size_kb'), str(buffer_size_kb)) - print 'Per-CPU ring buffer size set to %d KB' % buffer_size_kb + print('Per-CPU ring buffer size set to %d KB' % buffer_size_kb) start_tracing() - print 'KVM flight recorder enabled' + print('KVM flight recorder enabled') elif cmd == 'stop': stop_tracing() - print 'KVM flight recorder disabled' + print('KVM flight recorder disabled') elif cmd == 'dump': dump_trace() elif cmd == 'tail': diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index d9a6db0bb7..99a8146aaa 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -10,6 +10,7 @@ # This work is licensed under the terms of the GNU GPL, version 2. See # the COPYING file in the top-level directory. +from __future__ import print_function MSR_IA32_VMX_BASIC = 0x480 MSR_IA32_VMX_PINBASED_CTLS = 0x481 MSR_IA32_VMX_PROCBASED_CTLS = 0x482 diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client index 7d2a472094..8510814683 100755 --- a/scripts/qmp/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client @@ -36,6 +36,7 @@ # See also: https://wiki.qemu.org/Features/QAPI/GuestAgent # +from __future__ import print_function import base64 import random diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp index 514b539a6b..16d3bdb6fe 100755 --- a/scripts/qmp/qmp +++ b/scripts/qmp/qmp @@ -10,6 +10,7 @@ # This work is licensed under the terms of the GNU GPLv2 or later. # See the COPYING file in the top-level directory. +from __future__ import print_function import sys, os from qmp import QEMUMonitorProtocol @@ -26,9 +27,9 @@ def print_response(rsp, prefix=[]): print_response(rsp[key], prefix + [key]) else: if len(prefix): - print '%s: %s' % ('.'.join(prefix), rsp) + print('%s: %s' % ('.'.join(prefix), rsp)) else: - print '%s' % (rsp) + print('%s' % (rsp)) def main(args): path = None @@ -53,21 +54,21 @@ def main(args): elif arg in ['help']: os.execlp('man', 'man', 'qmp') else: - print 'Unknown argument "%s"' % arg + print('Unknown argument "%s"' % arg) args = args[1:] else: break if not path: - print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH" + print("QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH") return 1 if len(args): command, args = args[0], args[1:] else: - print 'No command found' - print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"' + print('No command found') + print('Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"') return 1 if command in ['help']: @@ -93,7 +94,7 @@ def main(args): os.execvp(fullcmd, [fullcmd] + args) except OSError as exc: if exc.errno == 2: - print 'Command "%s" not found.' % (fullcmd) + print('Command "%s" not found.' % (fullcmd)) return 1 raise return 0 @@ -104,7 +105,7 @@ def main(args): arguments = {} for arg in args: if not arg.startswith('--'): - print 'Unknown argument "%s"' % arg + print('Unknown argument "%s"' % arg) return 1 arg = arg[2:] diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index be449de621..b1cc7e2271 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -65,6 +65,7 @@ # which will echo back the properly formatted JSON-compliant QMP that is being # sent to QEMU, which is useful for debugging and documentation generation. +from __future__ import print_function import qmp import json import ast @@ -153,14 +154,14 @@ class QMPShell(qmp.QEMUMonitorProtocol): # File not found. No problem. pass else: - print "Failed to read history '%s'; %s" % (self._histfile, e) + print("Failed to read history '%s'; %s" % (self._histfile, e)) atexit.register(self.__save_history) def __save_history(self): try: readline.write_history_file(self._histfile) except Exception as e: - print "Failed to save history file '%s'; %s" % (self._histfile, e) + print("Failed to save history file '%s'; %s" % (self._histfile, e)) def __parse_value(self, val): try: @@ -258,15 +259,15 @@ class QMPShell(qmp.QEMUMonitorProtocol): if self._pretty: indent = 4 jsobj = json.dumps(qmp, indent=indent) - print str(jsobj) + print(str(jsobj)) def _execute_cmd(self, cmdline): try: qmpcmd = self.__build_cmd(cmdline) except Exception as e: - print 'Error while parsing command line: %s' % e - print 'command format: ', - print '[arg-name1=arg1] ... [arg-nameN=argN]' + print('Error while parsing command line: %s' % e) + print('command format: ', end=' ') + print('[arg-name1=arg1] ... [arg-nameN=argN]') return True # For transaction mode, we may have just cached the action: if qmpcmd is None: @@ -275,7 +276,7 @@ class QMPShell(qmp.QEMUMonitorProtocol): self._print(qmpcmd) resp = self.cmd_obj(qmpcmd) if resp is None: - print 'Disconnected' + print('Disconnected') return False self._print(resp) return True @@ -285,12 +286,12 @@ class QMPShell(qmp.QEMUMonitorProtocol): self.__completer_setup() def show_banner(self, msg='Welcome to the QMP low-level shell!'): - print msg + print(msg) if not self._greeting: - print 'Connected' + print('Connected') return version = self._greeting['QMP']['version']['qemu'] - print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']) + print('Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])) def get_prompt(self): if self._transmode: @@ -306,11 +307,11 @@ class QMPShell(qmp.QEMUMonitorProtocol): try: cmdline = raw_input(prompt) except EOFError: - print + print() return False if cmdline == '': for ev in self.get_events(): - print ev + print(ev) self.clear_events() return True else: @@ -366,24 +367,24 @@ class HMPShell(QMPShell): try: idx = int(cmdline.split()[1]) if not 'return' in self.__cmd_passthrough('info version', idx): - print 'bad CPU index' + print('bad CPU index') return True self.__cpu_index = idx except ValueError: - print 'cpu command takes an integer argument' + print('cpu command takes an integer argument') return True resp = self.__cmd_passthrough(cmdline, self.__cpu_index) if resp is None: - print 'Disconnected' + print('Disconnected') return False assert 'return' in resp or 'error' in resp if 'return' in resp: # Success if len(resp['return']) > 0: - print resp['return'], + print(resp['return'], end=' ') else: # Error - print '%s: %s' % (resp['error']['class'], resp['error']['desc']) + print('%s: %s' % (resp['error']['class'], resp['error']['desc'])) return True def show_banner(self): diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get index 0172c69441..291c8bfbc2 100755 --- a/scripts/qmp/qom-get +++ b/scripts/qmp/qom-get @@ -11,6 +11,7 @@ # the COPYING file in the top-level directory. ## +from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol @@ -33,7 +34,7 @@ def usage_error(error_msg = "unspecified error"): if len(args) > 0: if args[0] == "-h": - print usage() + print(usage()) exit(0); elif args[0] == "-s": try: @@ -62,6 +63,6 @@ srv.connect() rsp = srv.command('qom-get', path=path, property=prop) if type(rsp) == dict: for i in rsp.keys(): - print '%s: %s' % (i, rsp[i]) + print('%s: %s' % (i, rsp[i])) else: - print rsp + print(rsp) diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list index 1e7cc6cb2d..cd907bb81f 100755 --- a/scripts/qmp/qom-list +++ b/scripts/qmp/qom-list @@ -11,6 +11,7 @@ # the COPYING file in the top-level directory. ## +from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol @@ -33,7 +34,7 @@ def usage_error(error_msg = "unspecified error"): if len(args) > 0: if args[0] == "-h": - print usage() + print(usage()) exit(0); elif args[0] == "-s": try: @@ -52,13 +53,13 @@ srv = QEMUMonitorProtocol(socket_path) srv.connect() if len(args) == 0: - print '/' + print('/') sys.exit(0) for item in srv.command('qom-list', path=args[0]): if item['type'].startswith('child<'): - print '%s/' % item['name'] + print('%s/' % item['name']) elif item['type'].startswith('link<'): - print '@%s/' % item['name'] + print('@%s/' % item['name']) else: - print '%s' % item['name'] + print('%s' % item['name']) diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set index 94e2778922..fbe4b3e471 100755 --- a/scripts/qmp/qom-set +++ b/scripts/qmp/qom-set @@ -11,6 +11,7 @@ # the COPYING file in the top-level directory. ## +from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol @@ -34,7 +35,7 @@ def usage_error(error_msg = "unspecified error"): if len(args) > 0: if args[0] == "-h": - print usage() + print(usage()) exit(0); elif args[0] == "-s": try: @@ -61,4 +62,4 @@ else: srv = QEMUMonitorProtocol(socket_path) srv.connect() -print srv.command('qom-set', path=path, property=prop, value=value) +print(srv.command('qom-set', path=path, property=prop, value=value)) diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree index 906fcd2640..0ffd1ff1de 100755 --- a/scripts/qmp/qom-tree +++ b/scripts/qmp/qom-tree @@ -13,6 +13,7 @@ # the COPYING file in the top-level directory. ## +from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol @@ -35,7 +36,7 @@ def usage_error(error_msg = "unspecified error"): if len(args) > 0: if args[0] == "-h": - print usage() + print(usage()) exit(0); elif args[0] == "-s": try: @@ -54,15 +55,15 @@ srv = QEMUMonitorProtocol(socket_path) srv.connect() def list_node(path): - print '%s' % path + print('%s' % path) items = srv.command('qom-list', path=path) for item in items: if not item['type'].startswith('child<'): try: - print ' %s: %s (%s)' % (item['name'], srv.command('qom-get', path=path, property=item['name']), item['type']) + print(' %s: %s (%s)' % (item['name'], srv.command('qom-get', path=path, property=item['name']), item['type'])) except: - print ' %s: (%s)' % (item['name'], item['type']) - print '' + print(' %s: (%s)' % (item['name'], item['type'])) + print('') for item in items: if item['type'].startswith('child<'): list_node((path if (path != '/') else '') + '/' + item['name']) diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py index e274086277..5ae77c8a92 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -18,6 +18,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, see . +from __future__ import print_function import argparse import struct from collections import namedtuple @@ -89,9 +90,9 @@ def call_decode(table, index, dumpfile): "Search decode table for next step" decoder = next((d for d in table if d.eid == index), None) if not decoder: - print "Could not decode index: %d" % (index) - print "Entry is: %s" % (decoder) - print "Decode Table is:\n%s" % (table) + print("Could not decode index: %d" % (index)) + print("Entry is: %s" % (decoder)) + print("Decode Table is:\n%s" % (table)) return False else: return decoder.fn(decoder.eid, decoder.name, dumpfile) @@ -103,23 +104,23 @@ def print_event(eid, name, string=None, event_count=None): event_count = replay_state.event_count if string: - print "%d:%s(%d) %s" % (event_count, name, eid, string) + print("%d:%s(%d) %s" % (event_count, name, eid, string)) else: - print "%d:%s(%d)" % (event_count, name, eid) + print("%d:%s(%d)" % (event_count, name, eid)) # Decoders for each event type def decode_unimp(eid, name, _unused_dumpfile): "Unimplimented decoder, will trigger exit" - print "%s not handled - will now stop" % (name) + print("%s not handled - will now stop" % (name)) return False # Checkpoint decoder def swallow_async_qword(eid, name, dumpfile): "Swallow a qword of data without looking at it" step_id = read_qword(dumpfile) - print " %s(%d) @ %d" % (name, eid, step_id) + print(" %s(%d) @ %d" % (name, eid, step_id)) return True async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword), @@ -139,8 +140,8 @@ def decode_async(eid, name, dumpfile): async_event_checkpoint = read_byte(dumpfile) if async_event_checkpoint != replay_state.current_checkpoint: - print " mismatch between checkpoint %d and async data %d" % ( - replay_state.current_checkpoint, async_event_checkpoint) + print(" mismatch between checkpoint %d and async data %d" % ( + replay_state.current_checkpoint, async_event_checkpoint)) return True return call_decode(async_decode_table, async_event_kind, dumpfile) @@ -283,7 +284,7 @@ def decode_file(filename): version = read_dword(dumpfile) junk = read_qword(dumpfile) - print "HEADER: version 0x%x" % (version) + print("HEADER: version 0x%x" % (version)) if version == 0xe02007: event_decode_table = v7_event_table diff --git a/scripts/signrom.py b/scripts/signrom.py index 0497a1c32e..313ee28a17 100644 --- a/scripts/signrom.py +++ b/scripts/signrom.py @@ -1,3 +1,4 @@ +from __future__ import print_function # # Option ROM signing utility # diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py index 9d45c6ba4e..2658b5cc7c 100755 --- a/scripts/simpletrace.py +++ b/scripts/simpletrace.py @@ -9,6 +9,7 @@ # # For help see docs/devel/tracing.txt +from __future__ import print_function import struct import re import inspect @@ -257,6 +258,6 @@ if __name__ == '__main__': else: fields.append('%s=0x%x' % (name, rec[i])) i += 1 - print ' '.join(fields) + print(' '.join(fields)) run(Formatter()) diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py index bcef7ee28e..60d1bf4cda 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -19,6 +19,7 @@ # You should have received a copy of the GNU General Public License along # with this program; if not, see . +from __future__ import print_function import argparse import json import sys @@ -176,10 +177,10 @@ def check_fields(src_fields, dest_fields, desc, sec): except StopIteration: if d_iter_list == []: # We were not in a substruct - print "Section \"" + sec + "\",", - print "Description " + "\"" + desc + "\":", - print "expected field \"" + s_item["field"] + "\",", - print "while dest has no further fields" + print("Section \"" + sec + "\",", end=' ') + print("Description " + "\"" + desc + "\":", end=' ') + print("expected field \"" + s_item["field"] + "\",", end=' ') + print("while dest has no further fields") bump_taint() break @@ -197,10 +198,10 @@ def check_fields(src_fields, dest_fields, desc, sec): advance_dest = True continue if unused_count < 0: - print "Section \"" + sec + "\",", - print "Description \"" + desc + "\":", - print "unused size mismatch near \"", - print s_item["field"] + "\"" + print("Section \"" + sec + "\",", end=' ') + print("Description \"" + desc + "\":", end=' ') + print("unused size mismatch near \"", end=' ') + print(s_item["field"] + "\"") bump_taint() break continue @@ -211,10 +212,10 @@ def check_fields(src_fields, dest_fields, desc, sec): advance_src = True continue if unused_count < 0: - print "Section \"" + sec + "\",", - print "Description \"" + desc + "\":", - print "unused size mismatch near \"", - print d_item["field"] + "\"" + print("Section \"" + sec + "\",", end=' ') + print("Description \"" + desc + "\":", end=' ') + print("unused size mismatch near \"", end=' ') + print(d_item["field"] + "\"") bump_taint() break continue @@ -262,10 +263,10 @@ def check_fields(src_fields, dest_fields, desc, sec): unused_count = s_item["size"] - d_item["size"] continue - print "Section \"" + sec + "\",", - print "Description \"" + desc + "\":", - print "expected field \"" + s_item["field"] + "\",", - print "got \"" + d_item["field"] + "\"; skipping rest" + print("Section \"" + sec + "\",", end=' ') + print("Description \"" + desc + "\":", end=' ') + print("expected field \"" + s_item["field"] + "\",", end=' ') + print("got \"" + d_item["field"] + "\"; skipping rest") bump_taint() break @@ -289,8 +290,8 @@ def check_subsections(src_sub, dest_sub, desc, sec): check_descriptions(s_item, d_item, sec) if not found: - print "Section \"" + sec + "\", Description \"" + desc + "\":", - print "Subsection \"" + s_item["name"] + "\" not found" + print("Section \"" + sec + "\", Description \"" + desc + "\":", end=' ') + print("Subsection \"" + s_item["name"] + "\" not found") bump_taint() @@ -299,8 +300,8 @@ def check_description_in_list(s_item, d_item, sec, desc): return if not "Description" in d_item: - print "Section \"" + sec + "\", Description \"" + desc + "\",", - print "Field \"" + s_item["field"] + "\": missing description" + print("Section \"" + sec + "\", Description \"" + desc + "\",", end=' ') + print("Field \"" + s_item["field"] + "\": missing description") bump_taint() return @@ -311,17 +312,17 @@ def check_descriptions(src_desc, dest_desc, sec): check_version(src_desc, dest_desc, sec, src_desc["name"]) if not check_fields_match(sec, src_desc["name"], dest_desc["name"]): - print "Section \"" + sec + "\":", - print "Description \"" + src_desc["name"] + "\"", - print "missing, got \"" + dest_desc["name"] + "\" instead; skipping" + print("Section \"" + sec + "\":", end=' ') + print("Description \"" + src_desc["name"] + "\"", end=' ') + print("missing, got \"" + dest_desc["name"] + "\" instead; skipping") bump_taint() return for f in src_desc: if not f in dest_desc: - print "Section \"" + sec + "\"", - print "Description \"" + src_desc["name"] + "\":", - print "Entry \"" + f + "\" missing" + print("Section \"" + sec + "\"", end=' ') + print("Description \"" + src_desc["name"] + "\":", end=' ') + print("Entry \"" + f + "\" missing") bump_taint() continue @@ -334,39 +335,39 @@ def check_descriptions(src_desc, dest_desc, sec): def check_version(s, d, sec, desc=None): if s["version_id"] > d["version_id"]: - print "Section \"" + sec + "\"", + print("Section \"" + sec + "\"", end=' ') if desc: - print "Description \"" + desc + "\":", - print "version error:", s["version_id"], ">", d["version_id"] + print("Description \"" + desc + "\":", end=' ') + print("version error:", s["version_id"], ">", d["version_id"]) bump_taint() if not "minimum_version_id" in d: return if s["version_id"] < d["minimum_version_id"]: - print "Section \"" + sec + "\"", + print("Section \"" + sec + "\"", end=' ') if desc: - print "Description \"" + desc + "\":", - print "minimum version error:", s["version_id"], "<", - print d["minimum_version_id"] + print("Description \"" + desc + "\":", end=' ') + print("minimum version error:", s["version_id"], "<", end=' ') + print(d["minimum_version_id"]) bump_taint() def check_size(s, d, sec, desc=None, field=None): if s["size"] != d["size"]: - print "Section \"" + sec + "\"", + print("Section \"" + sec + "\"", end=' ') if desc: - print "Description \"" + desc + "\"", + print("Description \"" + desc + "\"", end=' ') if field: - print "Field \"" + field + "\"", - print "size mismatch:", s["size"], ",", d["size"] + print("Field \"" + field + "\"", end=' ') + print("size mismatch:", s["size"], ",", d["size"]) bump_taint() def check_machine_type(s, d): if s["Name"] != d["Name"]: - print "Warning: checking incompatible machine types:", - print "\"" + s["Name"] + "\", \"" + d["Name"] + "\"" + print("Warning: checking incompatible machine types:", end=' ') + print("\"" + s["Name"] + "\", \"" + d["Name"] + "\"") return @@ -400,7 +401,7 @@ def main(): # doesn't exist in dest. dest_sec = get_changed_sec_name(sec) if not dest_sec in dest_data: - print "Section \"" + sec + "\" does not exist in dest" + print("Section \"" + sec + "\" does not exist in dest") bump_taint() continue @@ -415,8 +416,8 @@ def main(): for entry in s: if not entry in d: - print "Section \"" + sec + "\": Entry \"" + entry + "\"", - print "missing" + print("Section \"" + sec + "\": Entry \"" + entry + "\"", end=' ') + print("missing") bump_taint() continue diff --git a/tests/docker/docker.py b/tests/docker/docker.py index f8267586eb..306e14cf69 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -11,6 +11,7 @@ # or (at your option) any later version. See the COPYING file in # the top-level directory. +from __future__ import print_function import os import sys sys.path.append(os.path.join(os.path.dirname(__file__), @@ -87,7 +88,7 @@ def _get_so_libs(executable): so_lib = search.groups()[1] libs.append("%s/%s" % (so_path, so_lib)) except subprocess.CalledProcessError: - print "%s had no associated libraries (static build?)" % (executable) + print("%s had no associated libraries (static build?)" % (executable)) return libs @@ -161,7 +162,7 @@ class Docker(object): continue if only_known and instance_uuid not in self._instances: continue - print "Terminating", i + print("Terminating", i) if active: self._do(["kill", i]) self._do(["rm", i]) @@ -288,7 +289,7 @@ class BuildCommand(SubCommand): if "--no-cache" not in argv and \ dkr.image_matches_dockerfile(tag, dockerfile): if not args.quiet: - print "Image is up to date." + print("Image is up to date.") else: # Create a docker context directory for the build docker_dir = tempfile.mkdtemp(prefix="docker_build") @@ -300,10 +301,10 @@ class BuildCommand(SubCommand): rc = subprocess.call(os.path.realpath(docker_pre), cwd=docker_dir, stdout=stdout) if rc == 3: - print "Skip" + print("Skip") return 0 elif rc != 0: - print "%s exited with code %d" % (docker_pre, rc) + print("%s exited with code %d" % (docker_pre, rc)) return 1 # Copy any extra files into the Docker context. These can be @@ -399,11 +400,11 @@ class ProbeCommand(SubCommand): try: docker = Docker() if docker._command[0] == "docker": - print "yes" + print("yes") elif docker._command[0] == "sudo": - print "sudo" + print("sudo") except Exception: - print "no" + print("no") return diff --git a/tests/docker/travis.py b/tests/docker/travis.py index 703a7fde85..ea1ef169e6 100755 --- a/tests/docker/travis.py +++ b/tests/docker/travis.py @@ -11,6 +11,7 @@ # or (at your option) any later version. See the COPYING file in # the top-level directory. +from __future__ import print_function import sys import yaml import itertools @@ -34,14 +35,14 @@ def main(): sys.stderr.write("Usage: %s \n" % sys.argv[0]) return 1 conf = load_yaml(sys.argv[1]) - print "\n".join((": ${%s}" % var for var in conf["env"]["global"])) + print("\n".join((": ${%s}" % var for var in conf["env"]["global"]))) for config in conf_iter(conf): - print "(" - print "\n".join(config["env"]) - print "alias cc=" + config["compiler"] - print "\n".join(conf["before_script"]) - print "\n".join(conf["script"]) - print ")" + print("(") + print("\n".join(config["env"])) + print("alias cc=" + config["compiler"]) + print("\n".join(conf["before_script"])) + print("\n".join(conf["script"])) + print(")") return 0 if __name__ == "__main__": diff --git a/tests/guest-debug/test-gdbstub.py b/tests/guest-debug/test-gdbstub.py index 31ba6c943a..474d2c5c65 100644 --- a/tests/guest-debug/test-gdbstub.py +++ b/tests/guest-debug/test-gdbstub.py @@ -1,3 +1,4 @@ +from __future__ import print_function # # This script needs to be run on startup # qemu -kernel ${KERNEL} -s -S diff --git a/tests/image-fuzzer/runner.py b/tests/image-fuzzer/runner.py index 96a1c11b2f..8de656933e 100755 --- a/tests/image-fuzzer/runner.py +++ b/tests/image-fuzzer/runner.py @@ -18,6 +18,7 @@ # along with this program. If not, see . # +from __future__ import print_function import sys import os import signal @@ -36,9 +37,8 @@ except ImportError: try: import simplejson as json except ImportError: - print >>sys.stderr, \ - "Warning: Module for JSON processing is not found.\n" \ - "'--config' and '--command' options are not supported." + print("Warning: Module for JSON processing is not found.\n" \ + "'--config' and '--command' options are not supported.", file=sys.stderr) # Backing file sizes in MB MAX_BACKING_FILE_SIZE = 10 @@ -158,9 +158,8 @@ class TestEnv(object): try: os.makedirs(self.current_dir) except OSError as e: - print >>sys.stderr, \ - "Error: The working directory '%s' cannot be used. Reason: %s"\ - % (self.work_dir, e[1]) + print("Error: The working directory '%s' cannot be used. Reason: %s"\ + % (self.work_dir, e[1]), file=sys.stderr) raise TestException self.log = open(os.path.join(self.current_dir, "test.log"), "w") self.parent_log = open(run_log, "a") @@ -277,7 +276,7 @@ class TestEnv(object): if __name__ == '__main__': def usage(): - print """ + print(""" Usage: runner.py [OPTION...] TEST_DIR IMG_GENERATOR Set up test environment in TEST_DIR and run a test in it. A module for @@ -326,7 +325,7 @@ if __name__ == '__main__': If '--config' argument is specified, fields not listed in the configuration array will not be fuzzed. - """ + """) def run_test(test_id, seed, work_dir, run_log, cleanup, log_all, command, fuzz_config): @@ -357,8 +356,7 @@ if __name__ == '__main__': ['command=', 'help', 'seed=', 'config=', 'keep_passed', 'verbose', 'duration=']) except getopt.error as e: - print >>sys.stderr, \ - "Error: %s\n\nTry 'runner.py --help' for more information" % e + print("Error: %s\n\nTry 'runner.py --help' for more information" % e, file=sys.stderr) sys.exit(1) command = None @@ -375,9 +373,8 @@ if __name__ == '__main__': try: command = json.loads(arg) except (TypeError, ValueError, NameError) as e: - print >>sys.stderr, \ - "Error: JSON array of test commands cannot be loaded.\n" \ - "Reason: %s" % e + print("Error: JSON array of test commands cannot be loaded.\n" \ + "Reason: %s" % e, file=sys.stderr) sys.exit(1) elif opt in ('-k', '--keep_passed'): cleanup = False @@ -391,15 +388,13 @@ if __name__ == '__main__': try: config = json.loads(arg) except (TypeError, ValueError, NameError) as e: - print >>sys.stderr, \ - "Error: JSON array with the fuzzer configuration cannot" \ - " be loaded\nReason: %s" % e + print("Error: JSON array with the fuzzer configuration cannot" \ + " be loaded\nReason: %s" % e, file=sys.stderr) sys.exit(1) if not len(args) == 2: - print >>sys.stderr, \ - "Expected two parameters\nTry 'runner.py --help'" \ - " for more information." + print("Expected two parameters\nTry 'runner.py --help'" \ + " for more information.", file=sys.stderr) sys.exit(1) work_dir = os.path.realpath(args[0]) @@ -415,9 +410,8 @@ if __name__ == '__main__': try: image_generator = __import__(generator_name) except ImportError as e: - print >>sys.stderr, \ - "Error: The image generator '%s' cannot be imported.\n" \ - "Reason: %s" % (generator_name, e) + print("Error: The image generator '%s' cannot be imported.\n" \ + "Reason: %s" % (generator_name, e), file=sys.stderr) sys.exit(1) # Enable core dumps diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index e14d4320b2..398e3f2706 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -1,3 +1,4 @@ +from __future__ import print_function # # Migration test main engine # @@ -117,7 +118,7 @@ class Engine(object): # XXX how to get dst timings on remote host ? if self._verbose: - print "Sleeping %d seconds for initial guest workload run" % self._sleep + print("Sleeping %d seconds for initial guest workload run" % self._sleep) sleep_secs = self._sleep while sleep_secs > 1: src_qemu_time.append(self._cpu_timing(src_pid)) @@ -126,7 +127,7 @@ class Engine(object): sleep_secs -= 1 if self._verbose: - print "Starting migration" + print("Starting migration") if scenario._auto_converge: resp = src.command("migrate-set-capabilities", capabilities = [ @@ -216,7 +217,7 @@ class Engine(object): if progress._status == "completed": if self._verbose: - print "Sleeping %d seconds for final guest workload run" % self._sleep + print("Sleeping %d seconds for final guest workload run" % self._sleep) sleep_secs = self._sleep while sleep_secs > 1: time.sleep(1) @@ -227,23 +228,23 @@ class Engine(object): return [progress_history, src_qemu_time, src_vcpu_time] if self._verbose and (loop % 20) == 0: - print "Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % ( + print("Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % ( progress._ram._iterations, progress._ram._remaining_bytes / (1024 * 1024), progress._ram._total_bytes / (1024 * 1024), progress._ram._transferred_bytes / (1024 * 1024), progress._ram._transfer_rate_mbs, - ) + )) if progress._ram._iterations > scenario._max_iters: if self._verbose: - print "No completion after %d iterations over RAM" % scenario._max_iters + print("No completion after %d iterations over RAM" % scenario._max_iters) src.command("migrate_cancel") continue if time.time() > (start + scenario._max_time): if self._verbose: - print "No completion after %d seconds" % scenario._max_time + print("No completion after %d seconds" % scenario._max_time) src.command("migrate_cancel") continue @@ -251,7 +252,7 @@ class Engine(object): progress._ram._iterations >= scenario._post_copy_iters and not post_copy): if self._verbose: - print "Switching to post-copy after %d iterations" % scenario._post_copy_iters + print("Switching to post-copy after %d iterations" % scenario._post_copy_iters) resp = src.command("migrate-start-postcopy") post_copy = True @@ -259,7 +260,7 @@ class Engine(object): progress._ram._iterations >= scenario._pause_iters and not paused): if self._verbose: - print "Pausing VM after %d iterations" % scenario._pause_iters + print("Pausing VM after %d iterations" % scenario._pause_iters) resp = src.command("stop") paused = True @@ -348,7 +349,7 @@ class Engine(object): if not log: return [] if self._debug: - print log + print(log) regex = r"[^\s]+\s\((\d+)\):\sINFO:\s(\d+)ms\scopied\s\d+\sGB\sin\s(\d+)ms" matcher = re.compile(regex) @@ -407,7 +408,7 @@ class Engine(object): if uri[0:5] == "unix:": os.remove(uri[5:]) if self._verbose: - print "Finished migration" + print("Finished migration") src.shutdown() dst.shutdown() @@ -420,7 +421,7 @@ class Engine(object): self._initrd, self._transport, self._sleep) except Exception as e: if self._debug: - print "Failed: %s" % str(e) + print("Failed: %s" % str(e)) try: src.shutdown() except: @@ -431,7 +432,7 @@ class Engine(object): pass if self._debug: - print src.get_log() - print dst.get_log() + print(src.get_log()) + print(dst.get_log()) raise diff --git a/tests/migration/guestperf/plot.py b/tests/migration/guestperf/plot.py index bc42249e16..aa98912a82 100644 --- a/tests/migration/guestperf/plot.py +++ b/tests/migration/guestperf/plot.py @@ -1,3 +1,4 @@ +from __future__ import print_function # # Migration test graph plotting # @@ -588,7 +589,7 @@ class Plot(object): """ def generate_html(self, fh): - print >>fh, """ + print(""" @@ -601,19 +602,19 @@ class Plot(object):

Migration report

Chart summary

-""" % self._generate_style() - print >>fh, self._generate_chart() - print >>fh, """ +""" % self._generate_style(), file=fh) + print(self._generate_chart(), file=fh) + print("""

Report details

-""" - print >>fh, self._generate_report() - print >>fh, """ +""", file=fh) + print(self._generate_report(), file=fh) + print("""
-""" +""", file=fh) def generate(self, filename): if filename is None: diff --git a/tests/migration/guestperf/shell.py b/tests/migration/guestperf/shell.py index b272978f47..a6b8cec1e0 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration/guestperf/shell.py @@ -1,3 +1,4 @@ +from __future__ import print_function # # Migration test command line shell integration # @@ -160,13 +161,13 @@ class Shell(BaseShell): try: report = engine.run(hardware, scenario) if args.output is None: - print report.to_json() + print(report.to_json()) else: with open(args.output, "w") as fh: - print >>fh, report.to_json() + print(report.to_json(), file=fh) return 0 except Exception as e: - print >>sys.stderr, "Error: %s" % str(e) + print("Error: %s" % str(e), file=sys.stderr) if args.debug: raise return 1 @@ -199,11 +200,11 @@ class BatchShell(BaseShell): name = os.path.join(comparison._name, scenario._name) if not fnmatch.fnmatch(name, args.filter): if args.verbose: - print "Skipping %s" % name + print("Skipping %s" % name) continue if args.verbose: - print "Running %s" % name + print("Running %s" % name) dirname = os.path.join(args.output, comparison._name) filename = os.path.join(dirname, scenario._name + ".json") @@ -211,9 +212,9 @@ class BatchShell(BaseShell): os.makedirs(dirname) report = engine.run(hardware, scenario) with open(filename, "w") as fh: - print >>fh, report.to_json() + print(report.to_json(), file=fh) except Exception as e: - print >>sys.stderr, "Error: %s" % str(e) + print("Error: %s" % str(e), file=sys.stderr) if args.debug: raise @@ -246,14 +247,14 @@ class PlotShell(object): if len(args.reports) == 0: - print >>sys.stderr, "At least one report required" + print("At least one report required", file=sys.stderr) return 1 if not (args.qemu_cpu or args.vcpu_cpu or args.total_guest_cpu or args.split_guest_cpu): - print >>sys.stderr, "At least one chart type is required" + print("At least one chart type is required", file=sys.stderr) return 1 reports = [] diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149 index 223cd68ad5..d3ffa259db 100755 --- a/tests/qemu-iotests/149 +++ b/tests/qemu-iotests/149 @@ -20,6 +20,7 @@ # Exercise the QEMU 'luks' block driver to validate interoperability # with the Linux dm-crypt + cryptsetup implementation +from __future__ import print_function import subprocess import os import os.path @@ -376,7 +377,7 @@ def test_once(config, qemu_img=False): finally: iotests.log("# Delete image") delete_image(config) - print + print() # Obviously we only work with the luks image format diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 index 2936929627..88f62d3c6d 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -18,6 +18,7 @@ # along with this program. If not, see . # +from __future__ import print_function import os import re import iotests @@ -85,7 +86,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) if log: - print log + print(log) self.vm = self.mkVm() self.vm.launch() diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index fdbdd8b300..fd8f79fef9 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -1,3 +1,4 @@ +from __future__ import print_function # Common utilities and Python wrappers for qemu-iotests # # Copyright (C) 2012 IBM Corp. @@ -239,7 +240,7 @@ def filter_img_info(output, filename): def log(msg, filters=[]): for flt in filters: msg = flt(msg) - print msg + print(msg) class Timeout: def __init__(self, seconds, errmsg = "Timeout"): @@ -599,7 +600,7 @@ def notrun(reason): seq = os.path.basename(sys.argv[0]) open('%s/%s.notrun' % (output_dir, seq), 'wb').write(reason + '\n') - print '%s not run: %s' % (seq, reason) + print('%s not run: %s' % (seq, reason)) sys.exit(0) def verify_image_format(supported_fmts=[], unsupported_fmts=[]): diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py index 8a04d979aa..f9193c0fae 100755 --- a/tests/qemu-iotests/nbd-fault-injector.py +++ b/tests/qemu-iotests/nbd-fault-injector.py @@ -43,6 +43,7 @@ # This work is licensed under the terms of the GNU GPL, version 2 or later. # See the COPYING file in the top-level directory. +from __future__ import print_function import sys import socket import struct @@ -110,7 +111,7 @@ class FaultInjectionSocket(object): for rule in self.rules: if rule.match(event, io): if rule.when == 0 or bufsize is None: - print 'Closing connection on rule match %s' % rule.name + print('Closing connection on rule match %s' % rule.name) sys.exit(0) if rule.when != -1: return rule.when @@ -182,7 +183,7 @@ def handle_connection(conn, use_export): elif req.type == NBD_CMD_DISC: break else: - print 'unrecognized command type %#02x' % req.type + print('unrecognized command type %#02x' % req.type) break conn.close() @@ -242,7 +243,7 @@ def open_socket(path): sock = socket.socket(socket.AF_UNIX) sock.bind(path) sock.listen(0) - print 'Listening on %s' % path + print('Listening on %s' % path) sys.stdout.flush() # another process may be waiting, show message now return sock diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py index 9cc4cf7d08..b95a837759 100755 --- a/tests/qemu-iotests/qcow2.py +++ b/tests/qemu-iotests/qcow2.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +from __future__ import print_function import sys import struct import string @@ -129,8 +130,8 @@ class QcowHeader: def dump(self): for f in QcowHeader.fields: - print "%-25s" % f[2], f[1] % self.__dict__[f[2]] - print "" + print("%-25s" % f[2], f[1] % self.__dict__[f[2]]) + print("") def dump_extensions(self): for ex in self.extensions: @@ -141,11 +142,11 @@ class QcowHeader: else: data = "" - print "Header extension:" - print "%-25s %#x" % ("magic", ex.magic) - print "%-25s %d" % ("length", ex.length) - print "%-25s %s" % ("data", data) - print "" + print("Header extension:") + print("%-25s %#x" % ("magic", ex.magic)) + print("%-25s %d" % ("length", ex.length)) + print("%-25s %s" % ("data", data)) + print("") def cmd_dump_header(fd): @@ -157,12 +158,12 @@ def cmd_set_header(fd, name, value): try: value = int(value, 0) except: - print "'%s' is not a valid number" % value + print("'%s' is not a valid number" % value) sys.exit(1) fields = (field[2] for field in QcowHeader.fields) if not name in fields: - print "'%s' is not a known header field" % name + print("'%s' is not a known header field" % name) sys.exit(1) h = QcowHeader(fd) @@ -173,7 +174,7 @@ def cmd_add_header_ext(fd, magic, data): try: magic = int(magic, 0) except: - print "'%s' is not a valid magic number" % magic + print("'%s' is not a valid magic number" % magic) sys.exit(1) h = QcowHeader(fd) @@ -188,7 +189,7 @@ def cmd_del_header_ext(fd, magic): try: magic = int(magic, 0) except: - print "'%s' is not a valid magic number" % magic + print("'%s' is not a valid magic number" % magic) sys.exit(1) h = QcowHeader(fd) @@ -200,7 +201,7 @@ def cmd_del_header_ext(fd, magic): h.extensions.remove(ex) if not found: - print "No such header extension" + print("No such header extension") return h.update(fd) @@ -211,7 +212,7 @@ def cmd_set_feature_bit(fd, group, bit): if bit < 0 or bit >= 64: raise ValueError except: - print "'%s' is not a valid bit number in range [0, 64)" % bit + print("'%s' is not a valid bit number in range [0, 64)" % bit) sys.exit(1) h = QcowHeader(fd) @@ -222,7 +223,7 @@ def cmd_set_feature_bit(fd, group, bit): elif group == 'autoclear': h.autoclear_features |= 1 << bit else: - print "'%s' is not a valid group, try 'incompatible', 'compatible', or 'autoclear'" % group + print("'%s' is not a valid group, try 'incompatible', 'compatible', or 'autoclear'" % group) sys.exit(1) h.update(fd) @@ -248,16 +249,16 @@ def main(filename, cmd, args): else: handler(fd, *args) return - print "Unknown command '%s'" % cmd + print("Unknown command '%s'" % cmd) finally: fd.close() def usage(): - print "Usage: %s [, ...]" % sys.argv[0] - print "" - print "Supported commands:" + print("Usage: %s [, ...]" % sys.argv[0]) + print("") + print("Supported commands:") for name, handler, num_args, desc in cmds: - print " %-20s - %s" % (name, desc) + print(" %-20s - %s" % (name, desc)) if __name__ == '__main__': if len(sys.argv) < 3: diff --git a/tests/qemu-iotests/qed.py b/tests/qemu-iotests/qed.py index 748068d7fe..ea469b9c48 100755 --- a/tests/qemu-iotests/qed.py +++ b/tests/qemu-iotests/qed.py @@ -10,6 +10,7 @@ # This work is licensed under the terms of the GNU GPL, version 2 or later. # See the COPYING file in the top-level directory. +from __future__ import print_function import sys import struct import random @@ -108,12 +109,12 @@ def corrupt_table_invalidate(qed, table): def cmd_show(qed, *args): '''show [header|l1|l2 ]- Show header or l1/l2 tables''' if not args or args[0] == 'header': - print qed.header + print(qed.header) elif args[0] == 'l1': - print qed.l1_table + print(qed.l1_table) elif len(args) == 2 and args[0] == 'l2': offset = int(args[1]) - print qed.read_table(offset) + print(qed.read_table(offset)) else: err('unrecognized sub-command') @@ -146,7 +147,7 @@ def cmd_invalidate(qed, table_level): def cmd_need_check(qed, *args): '''need-check [on|off] - Test, set, or clear the QED_F_NEED_CHECK header bit''' if not args: - print bool(qed.header['features'] & QED_F_NEED_CHECK) + print(bool(qed.header['features'] & QED_F_NEED_CHECK)) return if args[0] == 'on': @@ -208,11 +209,11 @@ def cmd_copy_metadata(qed, outfile): out.close() def usage(): - print 'Usage: %s [, ...]' % sys.argv[0] - print - print 'Supported commands:' + print('Usage: %s [, ...]' % sys.argv[0]) + print() + print('Supported commands:') for cmd in sorted(x for x in globals() if x.startswith('cmd_')): - print globals()[cmd].__doc__ + print(globals()[cmd].__doc__) sys.exit(1) def main(): diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index 3a2d508c35..3643117816 100755 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -11,6 +11,7 @@ # the COPYING file in the top-level directory. # +from __future__ import print_function import os import sys import logging @@ -222,7 +223,7 @@ def main(vmcls): try: args, argv = parse_args(vmcls.name) if not argv and not args.build_qemu and not args.build_image: - print "Nothing to do?" + print("Nothing to do?") return 1 logging.basicConfig(level=(logging.DEBUG if args.debug else logging.WARN)) From 068cf7a44cd4d65c05aa877dbebced295be5ce44 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 09:29:44 -0300 Subject: [PATCH 1099/2380] python: futurize -f libfuturize.fixes.fix_absolute_import Make implicit relative imports explicit and add "from __future__ import absolute_import" at the top of each relevant module. This is necessary for Python 3 compatibility. Done using: $ py=$( (g grep -l -E '^#!.*python';find -name '*.py' -printf '%P\n';) | \ sort -u | grep -v README.sh4) $ futurize -w -f libfuturize.fixes.fix_absolute_import $py Signed-off-by: Eduardo Habkost Message-Id: <20180608122952.2009-3-ehabkost@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost --- scripts/qmp/qemu-ga-client | 3 ++- scripts/qmp/qmp | 3 ++- scripts/qmp/qmp-shell | 3 ++- scripts/qmp/qom-fuse | 3 ++- scripts/qmp/qom-get | 3 ++- scripts/qmp/qom-list | 3 ++- scripts/qmp/qom-set | 3 ++- scripts/qmp/qom-tree | 3 ++- tests/image-fuzzer/qcow2/__init__.py | 3 ++- tests/image-fuzzer/qcow2/layout.py | 3 ++- 10 files changed, 20 insertions(+), 10 deletions(-) diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client index 8510814683..6045fcd3f2 100755 --- a/scripts/qmp/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client @@ -37,10 +37,11 @@ # from __future__ import print_function +from __future__ import absolute_import import base64 import random -import qmp +from . import qmp class QemuGuestAgent(qmp.QEMUMonitorProtocol): diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp index 16d3bdb6fe..4d2be4e98a 100755 --- a/scripts/qmp/qmp +++ b/scripts/qmp/qmp @@ -11,8 +11,9 @@ # See the COPYING file in the top-level directory. from __future__ import print_function +from __future__ import absolute_import import sys, os -from qmp import QEMUMonitorProtocol +from .qmp import QEMUMonitorProtocol def print_response(rsp, prefix=[]): if type(rsp) == list: diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index b1cc7e2271..38c99d8f72 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -66,7 +66,8 @@ # sent to QEMU, which is useful for debugging and documentation generation. from __future__ import print_function -import qmp +from __future__ import absolute_import +from . import qmp import json import ast import readline diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse index 5c6754aa63..b75aa72767 100755 --- a/scripts/qmp/qom-fuse +++ b/scripts/qmp/qom-fuse @@ -11,11 +11,12 @@ # the COPYING file in the top-level directory. ## +from __future__ import absolute_import import fuse, stat from fuse import Fuse import os, posix from errno import * -from qmp import QEMUMonitorProtocol +from .qmp import QEMUMonitorProtocol fuse.fuse_python_api = (0, 2) diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get index 291c8bfbc2..6313f27e8e 100755 --- a/scripts/qmp/qom-get +++ b/scripts/qmp/qom-get @@ -12,9 +12,10 @@ ## from __future__ import print_function +from __future__ import absolute_import import sys import os -from qmp import QEMUMonitorProtocol +from .qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list index cd907bb81f..80b0a3d1be 100755 --- a/scripts/qmp/qom-list +++ b/scripts/qmp/qom-list @@ -12,9 +12,10 @@ ## from __future__ import print_function +from __future__ import absolute_import import sys import os -from qmp import QEMUMonitorProtocol +from .qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set index fbe4b3e471..cbffb65880 100755 --- a/scripts/qmp/qom-set +++ b/scripts/qmp/qom-set @@ -12,9 +12,10 @@ ## from __future__ import print_function +from __future__ import absolute_import import sys import os -from qmp import QEMUMonitorProtocol +from .qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree index 0ffd1ff1de..ad4be233e6 100755 --- a/scripts/qmp/qom-tree +++ b/scripts/qmp/qom-tree @@ -14,9 +14,10 @@ ## from __future__ import print_function +from __future__ import absolute_import import sys import os -from qmp import QEMUMonitorProtocol +from .qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None diff --git a/tests/image-fuzzer/qcow2/__init__.py b/tests/image-fuzzer/qcow2/__init__.py index e2ebe19311..09ef59821b 100644 --- a/tests/image-fuzzer/qcow2/__init__.py +++ b/tests/image-fuzzer/qcow2/__init__.py @@ -1 +1,2 @@ -from layout import create_image +from __future__ import absolute_import +from .layout import create_image diff --git a/tests/image-fuzzer/qcow2/layout.py b/tests/image-fuzzer/qcow2/layout.py index 63e801f4e8..675877da96 100644 --- a/tests/image-fuzzer/qcow2/layout.py +++ b/tests/image-fuzzer/qcow2/layout.py @@ -16,9 +16,10 @@ # along with this program. If not, see . # +from __future__ import absolute_import import random import struct -import fuzz +from . import fuzz from math import ceil from os import urandom from itertools import chain From d24d523c14f5f519e04ea49bbe066323bb795879 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 09:29:45 -0300 Subject: [PATCH 1100/2380] python: futurize -f libfuturize.fixes.fix_next_call Change obj.next() calls to next(obj). This is necessary for Python 3 compatibility. Done using: $ py=$( (g grep -l -E '^#!.*python';find -name '*.py' -printf '%P\n';) | \ sort -u | grep -v README.sh4) $ futurize -w -f libfuturize.fixes.fix_next_call $py Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost Message-Id: <20180608122952.2009-4-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/ordereddict.py | 4 ++-- scripts/vmstate-static-checker.py | 4 ++-- tests/image-fuzzer/runner.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/ordereddict.py b/scripts/ordereddict.py index 2d1d81370b..68ed340b33 100644 --- a/scripts/ordereddict.py +++ b/scripts/ordereddict.py @@ -71,9 +71,9 @@ class OrderedDict(dict, DictMixin): if not self: raise KeyError('dictionary is empty') if last: - key = reversed(self).next() + key = next(reversed(self)) else: - key = iter(self).next() + key = next(iter(self)) value = self.pop(key) return key, value diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py index 60d1bf4cda..d3467288dc 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -158,7 +158,7 @@ def check_fields(src_fields, dest_fields, desc, sec): while True: if advance_src: try: - s_item = s_iter.next() + s_item = next(s_iter) except StopIteration: if s_iter_list == []: break @@ -173,7 +173,7 @@ def check_fields(src_fields, dest_fields, desc, sec): if advance_dest: try: - d_item = d_iter.next() + d_item = next(d_iter) except StopIteration: if d_iter_list == []: # We were not in a substruct diff --git a/tests/image-fuzzer/runner.py b/tests/image-fuzzer/runner.py index 8de656933e..45e8fca63f 100755 --- a/tests/image-fuzzer/runner.py +++ b/tests/image-fuzzer/runner.py @@ -422,7 +422,7 @@ if __name__ == '__main__': test_id = count(1) while should_continue(duration, start_time): try: - run_test(str(test_id.next()), seed, work_dir, run_log, cleanup, + run_test(str(next(test_id)), seed, work_dir, run_log, cleanup, log_all, command, config) except (KeyboardInterrupt, SystemExit): sys.exit(1) From d7a4228ebb0150e4c97a19077f3fac81d843e2c3 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 09:29:46 -0300 Subject: [PATCH 1101/2380] python: futurize -f lib2to3.fixes.fix_has_key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change "dict.has_key(key)" to "key in dict" This is necessary for Python 3 compatibility. Done using: $ py=$( (g grep -l -E '^#!.*python';find -name '*.py' -printf '%P\n';) | \ sort -u | grep -v README.sh4) $ futurize -w -f lib2to3.fixes.fix_has_key $py Reviewed-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Eduardo Habkost Message-Id: <20180608122952.2009-5-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/qmp/qmp | 4 ++-- scripts/qmp/qmp-shell | 2 +- scripts/qmp/qom-fuse | 2 +- scripts/qmp/qom-get | 2 +- scripts/qmp/qom-list | 2 +- scripts/qmp/qom-set | 2 +- scripts/qmp/qom-tree | 2 +- tests/qemu-iotests/093 | 2 +- tests/qemu-iotests/096 | 4 ++-- tests/qemu-iotests/136 | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp index 4d2be4e98a..33a0d6b73a 100755 --- a/scripts/qmp/qmp +++ b/scripts/qmp/qmp @@ -36,7 +36,7 @@ def main(args): path = None # Use QMP_PATH if it's set - if os.environ.has_key('QMP_PATH'): + if 'QMP_PATH' in os.environ: path = os.environ['QMP_PATH'] while len(args): @@ -80,7 +80,7 @@ def main(args): def do_command(srv, cmd, **kwds): rsp = srv.cmd(cmd, kwds) - if rsp.has_key('error'): + if 'error' in rsp: raise Exception(rsp['error']['desc']) return rsp['return'] diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index 38c99d8f72..26418dab95 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -134,7 +134,7 @@ class QMPShell(qmp.QEMUMonitorProtocol): def _fill_completion(self): cmds = self.cmd('query-commands') - if cmds.has_key('error'): + if 'error' in cmds: return for cmd in cmds['return']: self._completer.append(cmd['name']) diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse index b75aa72767..b00cb0a0af 100755 --- a/scripts/qmp/qom-fuse +++ b/scripts/qmp/qom-fuse @@ -29,7 +29,7 @@ class QOMFS(Fuse): self.ino_count = 1 def get_ino(self, path): - if self.ino_map.has_key(path): + if path in self.ino_map: return self.ino_map[path] self.ino_map[path] = self.ino_count self.ino_count += 1 diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get index 6313f27e8e..a3f5d7660e 100755 --- a/scripts/qmp/qom-get +++ b/scripts/qmp/qom-get @@ -45,7 +45,7 @@ if len(args) > 0: args = args[2:] if not socket_path: - if os.environ.has_key('QMP_SOCKET'): + if 'QMP_SOCKET' in os.environ: socket_path = os.environ['QMP_SOCKET'] else: usage_error("no QMP socket path or address given"); diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list index 80b0a3d1be..2ba25e1792 100755 --- a/scripts/qmp/qom-list +++ b/scripts/qmp/qom-list @@ -45,7 +45,7 @@ if len(args) > 0: args = args[2:] if not socket_path: - if os.environ.has_key('QMP_SOCKET'): + if 'QMP_SOCKET' in os.environ: socket_path = os.environ['QMP_SOCKET'] else: usage_error("no QMP socket path or address given"); diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set index cbffb65880..0352668812 100755 --- a/scripts/qmp/qom-set +++ b/scripts/qmp/qom-set @@ -46,7 +46,7 @@ if len(args) > 0: args = args[2:] if not socket_path: - if os.environ.has_key('QMP_SOCKET'): + if 'QMP_SOCKET' in os.environ: socket_path = os.environ['QMP_SOCKET'] else: usage_error("no QMP socket path or address given"); diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree index ad4be233e6..32e708a13e 100755 --- a/scripts/qmp/qom-tree +++ b/scripts/qmp/qom-tree @@ -47,7 +47,7 @@ if len(args) > 0: args = args[2:] if not socket_path: - if os.environ.has_key('QMP_SOCKET'): + if 'QMP_SOCKET' in os.environ: socket_path = os.environ['QMP_SOCKET'] else: usage_error("no QMP socket path or address given"); diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 index c3404a3171..68e344f8c1 100755 --- a/tests/qemu-iotests/093 +++ b/tests/qemu-iotests/093 @@ -237,7 +237,7 @@ class ThrottleTestGroupNames(iotests.QMPTestCase): if name: self.assertEqual(info["group"], name) else: - self.assertFalse(info.has_key('group')) + self.assertFalse('group' in info) return raise Exception("No group information found for '%s'" % device) diff --git a/tests/qemu-iotests/096 b/tests/qemu-iotests/096 index aeeb3753cf..a69439602d 100755 --- a/tests/qemu-iotests/096 +++ b/tests/qemu-iotests/096 @@ -53,9 +53,9 @@ class TestLiveSnapshot(iotests.QMPTestCase): self.assertEqual(r['iops'], self.iops) self.assertEqual(r['iops_size'], self.iops_size) else: - self.assertFalse(r.has_key('group')) + self.assertFalse('group' in r) self.assertEqual(r['iops'], 0) - self.assertFalse(r.has_key('iops_size')) + self.assertFalse('iops_size' in r) def testSnapshot(self): self.checkConfig('base') diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136 index 88b97ea7c6..a154d8ef9d 100755 --- a/tests/qemu-iotests/136 +++ b/tests/qemu-iotests/136 @@ -203,7 +203,7 @@ sector = "%d" if (self.accounted_ops(read = True, write = True, flush = True) != 0): self.assertLess(0, stats['idle_time_ns']) else: - self.assertFalse(stats.has_key('idle_time_ns')) + self.assertFalse('idle_time_ns' in stats) # This test does not alter these, so they must be all 0 self.assertEqual(0, stats['rd_merged']) From 050c5d865e946c7616f183d8be950ad1aae2cee4 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 09:29:47 -0300 Subject: [PATCH 1102/2380] python: futurize -f lib2to3.fixes.fix_standarderror Rename StandardError to Exception. This is necessary for Python 3 compatibility. Done using: $ py=$( (g grep -l -E '^#!.*python';find -name '*.py' -printf '%P\n';) | \ sort -u | grep -v README.sh4) $ futurize -w -f lib2to3.fixes.fix_standarderror $py Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost Message-Id: <20180608122952.2009-6-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/qmp/qemu-ga-client | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client index 6045fcd3f2..976e69e05f 100755 --- a/scripts/qmp/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client @@ -137,7 +137,7 @@ class QemuGuestAgentClient: def fsfreeze(self, cmd): if cmd not in ['status', 'freeze', 'thaw']: - raise StandardError('Invalid command: ' + cmd) + raise Exception('Invalid command: ' + cmd) return getattr(self.qga, 'fsfreeze' + '_' + cmd)() @@ -146,7 +146,7 @@ class QemuGuestAgentClient: def suspend(self, mode): if mode not in ['disk', 'ram', 'hybrid']: - raise StandardError('Invalid mode: ' + mode) + raise Exception('Invalid mode: ' + mode) try: getattr(self.qga, 'suspend' + '_' + mode)() @@ -157,7 +157,7 @@ class QemuGuestAgentClient: def shutdown(self, mode='powerdown'): if mode not in ['powerdown', 'halt', 'reboot']: - raise StandardError('Invalid mode: ' + mode) + raise Exception('Invalid mode: ' + mode) try: self.qga.shutdown(mode=mode) From 8d2860cb91dbb3586a659c7793fb7828505c8a5a Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 09:29:48 -0300 Subject: [PATCH 1103/2380] python: futurize -f lib2to3.fixes.fix_reduce Handle the move of reduce() to functools.reduce(). This is necessary for Python 3 compatibility. Done using: $ py=$( (g grep -l -E '^#!.*python';find -name '*.py' -printf '%P\n';) | \ sort -u | grep -v README.sh4) $ futurize -w -f lib2to3.fixes.fix_reduce $py Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost Message-Id: <20180608122952.2009-7-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- tests/image-fuzzer/qcow2/fuzz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/image-fuzzer/qcow2/fuzz.py b/tests/image-fuzzer/qcow2/fuzz.py index 20eba6bc1b..abc4f0635d 100644 --- a/tests/image-fuzzer/qcow2/fuzz.py +++ b/tests/image-fuzzer/qcow2/fuzz.py @@ -17,6 +17,7 @@ # import random +from functools import reduce UINT8 = 0xff UINT16 = 0xffff From 5cdda8ce11820c52e5d51cf3e6cef54aa9ad9425 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 09:29:49 -0300 Subject: [PATCH 1104/2380] python: futurize -f lib2to3.fixes.fix_tuple_params Remove implicit tuple parameter unpacking. This is necessary for Python 3 compatibility. Done using: $ py=$( (g grep -l -E '^#!.*python';find -name '*.py' -printf '%P\n';) | \ sort -u | grep -v README.sh4) $ futurize -w -f lib2to3.fixes.fix_tuple_params $py Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost Message-Id: <20180608122952.2009-8-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/analyse-locks-simpletrace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py index 352bc9c22d..30090bdfff 100755 --- a/scripts/analyse-locks-simpletrace.py +++ b/scripts/analyse-locks-simpletrace.py @@ -78,7 +78,7 @@ if __name__ == '__main__': # Now dump the individual lock stats for key, val in sorted(analyser.mutex_records.iteritems(), - key=lambda (k,v): v["locks"]): + key=lambda k_v: k_v[1]["locks"]): print ("Lock: %#x locks: %d, locked: %d, unlocked: %d" % (key, val["locks"], val["locked"], val["unlocked"])) From cd67daaa93e259f5b5baedbe7cbd42e5c99421d3 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 09:29:50 -0300 Subject: [PATCH 1105/2380] python: futurize -f lib2to3.fixes.fix_renames Change sys.maxint to sys.maxsize. This is necessary for Python 3 compatibility. Done using: $ py=$( (g grep -l -E '^#!.*python';find -name '*.py' -printf '%P\n';) | \ sort -u | grep -v README.sh4) $ futurize -w -f lib2to3.fixes.fix_renames $py Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost Message-Id: <20180608122952.2009-9-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- tests/image-fuzzer/runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/image-fuzzer/runner.py b/tests/image-fuzzer/runner.py index 45e8fca63f..95d84f38f3 100755 --- a/tests/image-fuzzer/runner.py +++ b/tests/image-fuzzer/runner.py @@ -128,7 +128,7 @@ class TestEnv(object): if seed is not None: self.seed = seed else: - self.seed = str(random.randint(0, sys.maxint)) + self.seed = str(random.randint(0, sys.maxsize)) random.seed(self.seed) self.init_path = os.getcwd() From bd228083f7364dd2903970ec4dc0cc55bf156104 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 09:29:51 -0300 Subject: [PATCH 1106/2380] python: futurize -f lib2to3.fixes.fix_except Convert "except X, T" to "except X as T". This is necessary for Python 3 compatibility. Done using: $ py=$( (g grep -l -E '^#!.*python';find -name '*.py' -printf '%P\n';) | \ sort -u | grep -v README.sh4) $ futurize -w -f lib2to3.fixes.fix_except $py Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost Message-Id: <20180608122952.2009-10-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/simpletrace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py index 2658b5cc7c..d4a50a1e2b 100755 --- a/scripts/simpletrace.py +++ b/scripts/simpletrace.py @@ -45,7 +45,7 @@ def get_record(edict, idtoname, rechdr, fobj): rec = (name, rechdr[1], rechdr[3]) try: event = edict[name] - except KeyError, e: + except KeyError as e: import sys sys.stderr.write('%s event is logged but is not declared ' \ 'in the trace events file, try using ' \ From 4803c5cde8afa3f8b8fc1bc639ebf4f4fccddf94 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 09:29:52 -0300 Subject: [PATCH 1107/2380] python: futurize -f lib2to3.fixes.fix_numliterals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert octal literals into the new syntax. This is necessary for Python 3 compatibility. Done using: $ py=$( (g grep -l -E '^#!.*python';find -name '*.py' -printf '%P\n';) | \ sort -u | grep -v README.sh4) $ futurize -w -f lib2to3.fixes.fix_numliterals $py Reviewed-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Eduardo Habkost Message-Id: <20180608122952.2009-11-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/qmp/qom-fuse | 6 +++--- tests/qemu-iotests/118 | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse index b00cb0a0af..e524e798fc 100755 --- a/scripts/qmp/qom-fuse +++ b/scripts/qmp/qom-fuse @@ -90,7 +90,7 @@ class QOMFS(Fuse): def getattr(self, path): if self.is_link(path): - value = posix.stat_result((0755 | stat.S_IFLNK, + value = posix.stat_result((0o755 | stat.S_IFLNK, self.get_ino(path), 0, 2, @@ -101,7 +101,7 @@ class QOMFS(Fuse): 0, 0)) elif self.is_object(path): - value = posix.stat_result((0755 | stat.S_IFDIR, + value = posix.stat_result((0o755 | stat.S_IFDIR, self.get_ino(path), 0, 2, @@ -112,7 +112,7 @@ class QOMFS(Fuse): 0, 0)) elif self.is_property(path): - value = posix.stat_result((0644 | stat.S_IFREG, + value = posix.stat_result((0o644 | stat.S_IFREG, self.get_ino(path), 0, 1, diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118 index a0469b570e..ff3b2ae3e7 100755 --- a/tests/qemu-iotests/118 +++ b/tests/qemu-iotests/118 @@ -390,14 +390,14 @@ class TestChangeReadOnly(ChangeBaseClass): def tearDown(self): self.vm.shutdown() - os.chmod(old_img, 0666) - os.chmod(new_img, 0666) + os.chmod(old_img, 0o666) + os.chmod(new_img, 0o666) os.remove(old_img) os.remove(new_img) def test_ro_ro_retain(self): - os.chmod(old_img, 0444) - os.chmod(new_img, 0444) + os.chmod(old_img, 0o444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -417,7 +417,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_ro_rw_retain(self): - os.chmod(old_img, 0444) + os.chmod(old_img, 0o444) self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -437,7 +437,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_rw_ro_retain(self): - os.chmod(new_img, 0444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -459,7 +459,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) def test_ro_rw(self): - os.chmod(old_img, 0444) + os.chmod(old_img, 0o444) self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -480,7 +480,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_rw_ro(self): - os.chmod(new_img, 0444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -521,7 +521,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_make_ro_rw(self): - os.chmod(new_img, 0444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -542,7 +542,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) def test_make_rw_ro_by_retain(self): - os.chmod(old_img, 0444) + os.chmod(old_img, 0o444) self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -562,7 +562,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_make_ro_rw_by_retain(self): - os.chmod(new_img, 0444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -582,7 +582,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) def test_rw_ro_cycle(self): - os.chmod(new_img, 0444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() From a9f27ea9adc8c695197bd08f2e938ef7b4183f07 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 24 May 2018 11:43:30 -0400 Subject: [PATCH 1108/2380] i386: Clean up cache CPUID code Always initialize CPUCaches structs with cache information, even if legacy_cache=true. Use different CPUCaches struct for CPUID[2], CPUID[4], and the AMD CPUID leaves. This will simplify a lot the logic inside cpu_x86_cpuid(). Signed-off-by: Eduardo Habkost Signed-off-by: Babu Moger Message-Id: <1527176614-26271-2-git-send-email-babu.moger@amd.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 117 +++++++++++++++++++++++----------------------- target/i386/cpu.h | 14 ++++-- 2 files changed, 67 insertions(+), 64 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 94260412e2..1ea7bf4911 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1112,7 +1112,7 @@ struct X86CPUDefinition { }; static CPUCaches epyc_cache_info = { - .l1d_cache = { + .l1d_cache = &(CPUCacheInfo) { .type = DCACHE, .level = 1, .size = 32 * KiB, @@ -1124,7 +1124,7 @@ static CPUCaches epyc_cache_info = { .self_init = 1, .no_invd_sharing = true, }, - .l1i_cache = { + .l1i_cache = &(CPUCacheInfo) { .type = ICACHE, .level = 1, .size = 64 * KiB, @@ -1136,7 +1136,7 @@ static CPUCaches epyc_cache_info = { .self_init = 1, .no_invd_sharing = true, }, - .l2_cache = { + .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, .level = 2, .size = 512 * KiB, @@ -1146,7 +1146,7 @@ static CPUCaches epyc_cache_info = { .sets = 1024, .lines_per_tag = 1, }, - .l3_cache = { + .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, .level = 3, .size = 8 * MiB, @@ -3340,9 +3340,8 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) env->features[w] = def->features[w]; } - /* Store Cache information from the X86CPUDefinition if available */ - env->cache_info = def->cache_info; - cpu->legacy_cache = def->cache_info ? 0 : 1; + /* legacy-cache defaults to 'off' if CPU model provides cache info */ + cpu->legacy_cache = !def->cache_info; /* Special cases not set in the X86CPUDefinition structs: */ /* TODO: in-kernel irqchip for hvf */ @@ -3693,21 +3692,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (!cpu->enable_l3_cache) { *ecx = 0; } else { - if (env->cache_info && !cpu->legacy_cache) { - *ecx = cpuid2_cache_descriptor(&env->cache_info->l3_cache); - } else { - *ecx = cpuid2_cache_descriptor(&legacy_l3_cache); - } - } - if (env->cache_info && !cpu->legacy_cache) { - *edx = (cpuid2_cache_descriptor(&env->cache_info->l1d_cache) << 16) | - (cpuid2_cache_descriptor(&env->cache_info->l1i_cache) << 8) | - (cpuid2_cache_descriptor(&env->cache_info->l2_cache)); - } else { - *edx = (cpuid2_cache_descriptor(&legacy_l1d_cache) << 16) | - (cpuid2_cache_descriptor(&legacy_l1i_cache) << 8) | - (cpuid2_cache_descriptor(&legacy_l2_cache_cpuid2)); + *ecx = cpuid2_cache_descriptor(env->cache_info_cpuid2.l3_cache); } + *edx = (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1d_cache) << 16) | + (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1i_cache) << 8) | + (cpuid2_cache_descriptor(env->cache_info_cpuid2.l2_cache)); break; case 4: /* cache info: needed for Core compatibility */ @@ -3720,35 +3709,27 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } else { *eax = 0; - CPUCacheInfo *l1d, *l1i, *l2, *l3; - if (env->cache_info && !cpu->legacy_cache) { - l1d = &env->cache_info->l1d_cache; - l1i = &env->cache_info->l1i_cache; - l2 = &env->cache_info->l2_cache; - l3 = &env->cache_info->l3_cache; - } else { - l1d = &legacy_l1d_cache; - l1i = &legacy_l1i_cache; - l2 = &legacy_l2_cache; - l3 = &legacy_l3_cache; - } switch (count) { case 0: /* L1 dcache info */ - encode_cache_cpuid4(l1d, 1, cs->nr_cores, + encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, + 1, cs->nr_cores, eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ - encode_cache_cpuid4(l1i, 1, cs->nr_cores, + encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, + 1, cs->nr_cores, eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ - encode_cache_cpuid4(l2, cs->nr_threads, cs->nr_cores, + encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache, + cs->nr_threads, cs->nr_cores, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); if (cpu->enable_l3_cache) { - encode_cache_cpuid4(l3, (1 << pkg_offset), cs->nr_cores, + encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, + (1 << pkg_offset), cs->nr_cores, eax, ebx, ecx, edx); break; } @@ -3961,13 +3942,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \ (L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES); - if (env->cache_info && !cpu->legacy_cache) { - *ecx = encode_cache_cpuid80000005(&env->cache_info->l1d_cache); - *edx = encode_cache_cpuid80000005(&env->cache_info->l1i_cache); - } else { - *ecx = encode_cache_cpuid80000005(&legacy_l1d_cache_amd); - *edx = encode_cache_cpuid80000005(&legacy_l1i_cache_amd); - } + *ecx = encode_cache_cpuid80000005(env->cache_info_amd.l1d_cache); + *edx = encode_cache_cpuid80000005(env->cache_info_amd.l1i_cache); break; case 0x80000006: /* cache info (L2 cache) */ @@ -3983,17 +3959,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (L2_DTLB_4K_ENTRIES << 16) | \ (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \ (L2_ITLB_4K_ENTRIES); - if (env->cache_info && !cpu->legacy_cache) { - encode_cache_cpuid80000006(&env->cache_info->l2_cache, - cpu->enable_l3_cache ? - &env->cache_info->l3_cache : NULL, - ecx, edx); - } else { - encode_cache_cpuid80000006(&legacy_l2_cache_amd, - cpu->enable_l3_cache ? - &legacy_l3_cache : NULL, - ecx, edx); - } + encode_cache_cpuid80000006(env->cache_info_amd.l2_cache, + cpu->enable_l3_cache ? + env->cache_info_amd.l3_cache : NULL, + ecx, edx); break; case 0x80000007: *eax = 0; @@ -4690,6 +4659,37 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) cpu->phys_bits = 32; } } + + /* Cache information initialization */ + if (!cpu->legacy_cache) { + if (!xcc->cpu_def || !xcc->cpu_def->cache_info) { + char *name = x86_cpu_class_get_model_name(xcc); + error_setg(errp, + "CPU model '%s' doesn't support legacy-cache=off", name); + g_free(name); + return; + } + env->cache_info_cpuid2 = env->cache_info_cpuid4 = env->cache_info_amd = + *xcc->cpu_def->cache_info; + } else { + /* Build legacy cache information */ + env->cache_info_cpuid2.l1d_cache = &legacy_l1d_cache; + env->cache_info_cpuid2.l1i_cache = &legacy_l1i_cache; + env->cache_info_cpuid2.l2_cache = &legacy_l2_cache_cpuid2; + env->cache_info_cpuid2.l3_cache = &legacy_l3_cache; + + env->cache_info_cpuid4.l1d_cache = &legacy_l1d_cache; + env->cache_info_cpuid4.l1i_cache = &legacy_l1i_cache; + env->cache_info_cpuid4.l2_cache = &legacy_l2_cache; + env->cache_info_cpuid4.l3_cache = &legacy_l3_cache; + + env->cache_info_amd.l1d_cache = &legacy_l1d_cache_amd; + env->cache_info_amd.l1i_cache = &legacy_l1i_cache_amd; + env->cache_info_amd.l2_cache = &legacy_l2_cache_amd; + env->cache_info_amd.l3_cache = &legacy_l3_cache; + } + + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -5173,11 +5173,10 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true), /* - * lecacy_cache defaults to CPU model being chosen. This is set in - * x86_cpu_load_def based on cache_info which is initialized in - * builtin_x86_defs + * lecacy_cache defaults to true unless the CPU model provides its + * own cache information (see x86_cpu_load_def()). */ - DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, false), + DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true), /* * From "Requirements for Implementing the Microsoft diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 664504610e..89c82be8d2 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1098,10 +1098,10 @@ typedef struct CPUCacheInfo { typedef struct CPUCaches { - CPUCacheInfo l1d_cache; - CPUCacheInfo l1i_cache; - CPUCacheInfo l2_cache; - CPUCacheInfo l3_cache; + CPUCacheInfo *l1d_cache; + CPUCacheInfo *l1i_cache; + CPUCacheInfo *l2_cache; + CPUCacheInfo *l3_cache; } CPUCaches; typedef struct CPUX86State { @@ -1293,7 +1293,11 @@ typedef struct CPUX86State { /* Features that were explicitly enabled/disabled */ FeatureWordArray user_features; uint32_t cpuid_model[12]; - CPUCaches *cache_info; + /* Cache information for CPUID. When legacy-cache=on, the cache data + * on each CPUID leaf will be different, because we keep compatibility + * with old QEMU versions. + */ + CPUCaches cache_info_cpuid2, cache_info_cpuid4, cache_info_amd; /* MTRRs */ uint64_t mtrr_fixed[11]; From 8f4202fb1080f86958782b1fca0bf0279f67d136 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 24 May 2018 11:43:31 -0400 Subject: [PATCH 1109/2380] i386: Populate AMD Processor Cache Information for cpuid 0x8000001D Add information for cpuid 0x8000001D leaf. Populate cache topology information for different cache types (Data Cache, Instruction Cache, L2 and L3) supported by 0x8000001D leaf. Please refer to the Processor Programming Reference (PPR) for AMD Family 17h Model for more details. Signed-off-by: Babu Moger Message-Id: <1527176614-26271-3-git-send-email-babu.moger@amd.com> Reviewed-by: Eduardo Habkost Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++ target/i386/kvm.c | 29 ++++++++++-- 2 files changed, 143 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1ea7bf4911..1e69e68f25 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -334,6 +334,99 @@ static void encode_cache_cpuid80000006(CPUCacheInfo *l2, } } +/* + * Definitions used for building CPUID Leaf 0x8000001D and 0x8000001E + * Please refer to the AMD64 Architecture Programmer’s Manual Volume 3. + * Define the constants to build the cpu topology. Right now, TOPOEXT + * feature is enabled only on EPYC. So, these constants are based on + * EPYC supported configurations. We may need to handle the cases if + * these values change in future. + */ +/* Maximum core complexes in a node */ +#define MAX_CCX 2 +/* Maximum cores in a core complex */ +#define MAX_CORES_IN_CCX 4 +/* Maximum cores in a node */ +#define MAX_CORES_IN_NODE 8 +/* Maximum nodes in a socket */ +#define MAX_NODES_PER_SOCKET 4 + +/* + * Figure out the number of nodes required to build this config. + * Max cores in a node is 8 + */ +static int nodes_in_socket(int nr_cores) +{ + int nodes; + + nodes = DIV_ROUND_UP(nr_cores, MAX_CORES_IN_NODE); + + /* Hardware does not support config with 3 nodes, return 4 in that case */ + return (nodes == 3) ? 4 : nodes; +} + +/* + * Decide the number of cores in a core complex with the given nr_cores using + * following set constants MAX_CCX, MAX_CORES_IN_CCX, MAX_CORES_IN_NODE and + * MAX_NODES_PER_SOCKET. Maintain symmetry as much as possible + * L3 cache is shared across all cores in a core complex. So, this will also + * tell us how many cores are sharing the L3 cache. + */ +static int cores_in_core_complex(int nr_cores) +{ + int nodes; + + /* Check if we can fit all the cores in one core complex */ + if (nr_cores <= MAX_CORES_IN_CCX) { + return nr_cores; + } + /* Get the number of nodes required to build this config */ + nodes = nodes_in_socket(nr_cores); + + /* + * Divide the cores accros all the core complexes + * Return rounded up value + */ + return DIV_ROUND_UP(nr_cores, nodes * MAX_CCX); +} + +/* Encode cache info for CPUID[8000001D] */ +static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, CPUState *cs, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + uint32_t l3_cores; + assert(cache->size == cache->line_size * cache->associativity * + cache->partitions * cache->sets); + + *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | + (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0); + + /* L3 is shared among multiple cores */ + if (cache->level == 3) { + l3_cores = cores_in_core_complex(cs->nr_cores); + *eax |= ((l3_cores * cs->nr_threads) - 1) << 14; + } else { + *eax |= ((cs->nr_threads - 1) << 14); + } + + assert(cache->line_size > 0); + assert(cache->partitions > 0); + assert(cache->associativity > 0); + /* We don't implement fully-associative caches */ + assert(cache->associativity < cache->sets); + *ebx = (cache->line_size - 1) | + ((cache->partitions - 1) << 12) | + ((cache->associativity - 1) << 22); + + assert(cache->sets > 0); + *ecx = cache->sets - 1; + + *edx = (cache->no_invd_sharing ? CACHE_NO_INVD_SHARING : 0) | + (cache->inclusive ? CACHE_INCLUSIVE : 0) | + (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); +} + /* * Definitions of the hardcoded cache entries we expose: * These are legacy cache values. If there is a need to change any @@ -4003,6 +4096,30 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = 0; } break; + case 0x8000001D: + *eax = 0; + switch (count) { + case 0: /* L1 dcache info */ + encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, cs, + eax, ebx, ecx, edx); + break; + case 1: /* L1 icache info */ + encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, cs, + eax, ebx, ecx, edx); + break; + case 2: /* L2 cache info */ + encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, cs, + eax, ebx, ecx, edx); + break; + case 3: /* L3 cache info */ + encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, cs, + eax, ebx, ecx, edx); + break; + default: /* end of info */ + *eax = *ebx = *ecx = *edx = 0; + break; + } + break; case 0xC0000000: *eax = env->cpuid_xlevel2; *ebx = 0; diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 44f70733e7..445e0e0b11 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -979,9 +979,32 @@ int kvm_arch_init_vcpu(CPUState *cs) } c = &cpuid_data.entries[cpuid_i++]; - c->function = i; - c->flags = 0; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + switch (i) { + case 0x8000001d: + /* Query for all AMD cache information leaves */ + for (j = 0; ; j++) { + c->function = i; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + c->index = j; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + + if (c->eax == 0) { + break; + } + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "cpuid_data is full, no space for " + "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); + abort(); + } + c = &cpuid_data.entries[cpuid_i++]; + } + break; + default: + c->function = i; + c->flags = 0; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + break; + } } /* Call Centaur's CPUID instructions they are supported. */ From 7f2b55443a1c5cf17905b14c3ad38413924e4123 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 11:30:26 -0300 Subject: [PATCH 1110/2380] configure: Require Python 2.7 or newer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All of the supported build platforms documented in qemu-doc.texi should already support Python 2.7. Removing support for Python 2.6 will allow us to remove some compatibility modules we carry in the QEMU tree: * scripts/argparse.py * scripts/ordereddict.py Python 2.6 is also not receiving bug fixes upstream and is not supported by pylint, which makes it harder to keep the code compatible with both Python 2 and Python 3. Signed-off-by: Eduardo Habkost Message-Id: <20180608143026.20167-1-ehabkost@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Cleber Rosa Reviewed-by: John Snow Signed-off-by: Eduardo Habkost --- configure | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 14b11130a7..a8c4094c87 100755 --- a/configure +++ b/configure @@ -1651,8 +1651,8 @@ fi # Note that if the Python conditional here evaluates True we will exit # with status 1 which is a shell 'false' value. -if ! $python -c 'import sys; sys.exit(sys.version_info < (2,6))'; then - error_exit "Cannot use '$python', Python 2 >= 2.6 or Python 3 is required." \ +if ! $python -c 'import sys; sys.exit(sys.version_info < (2,7))'; then + error_exit "Cannot use '$python', Python 2 >= 2.7 or Python 3 is required." \ "Use --python=/path/to/python to specify a supported Python." fi From 7f5ed8c997d16e1ee24b25a9b0af80e71fb79770 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 14:52:51 -0300 Subject: [PATCH 1111/2380] python: Remove scripts/argparse.py Python 2.7 (the minimum Python version we require) already provides the argparse module on the standard library. Signed-off-by: Eduardo Habkost Message-Id: <20180608175252.25110-2-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/argparse.py | 2406 ------------------------------------------- 1 file changed, 2406 deletions(-) delete mode 100644 scripts/argparse.py diff --git a/scripts/argparse.py b/scripts/argparse.py deleted file mode 100644 index 27d1f28935..0000000000 --- a/scripts/argparse.py +++ /dev/null @@ -1,2406 +0,0 @@ -# This is a local copy of the standard library argparse module taken from PyPI. -# It is licensed under the Python Software Foundation License. This is a -# fallback for Python 2.6 which does not include this module. Python 2.7+ and -# 3+ will never load this module because built-in modules are loaded before -# anything in sys.path. -# -# If your script is not located in the same directory as this file, import it -# like this: -# -# import os -# import sys -# sys.path.append(os.path.join(os.path.dirname(__file__), ..., 'scripts')) -# import argparse - -# Author: Steven J. Bethard . -# Maintainer: Thomas Waldmann - -"""Command-line parsing library - -This module is an optparse-inspired command-line parsing library that: - - - handles both optional and positional arguments - - produces highly informative usage messages - - supports parsers that dispatch to sub-parsers - -The following is a simple usage example that sums integers from the -command-line and writes the result to a file:: - - parser = argparse.ArgumentParser( - description='sum the integers at the command line') - parser.add_argument( - 'integers', metavar='int', nargs='+', type=int, - help='an integer to be summed') - parser.add_argument( - '--log', default=sys.stdout, type=argparse.FileType('w'), - help='the file where the sum should be written') - args = parser.parse_args() - args.log.write('%s' % sum(args.integers)) - args.log.close() - -The module contains the following public classes: - - - ArgumentParser -- The main entry point for command-line parsing. As the - example above shows, the add_argument() method is used to populate - the parser with actions for optional and positional arguments. Then - the parse_args() method is invoked to convert the args at the - command-line into an object with attributes. - - - ArgumentError -- The exception raised by ArgumentParser objects when - there are errors with the parser's actions. Errors raised while - parsing the command-line are caught by ArgumentParser and emitted - as command-line messages. - - - FileType -- A factory for defining types of files to be created. As the - example above shows, instances of FileType are typically passed as - the type= argument of add_argument() calls. - - - Action -- The base class for parser actions. Typically actions are - selected by passing strings like 'store_true' or 'append_const' to - the action= argument of add_argument(). However, for greater - customization of ArgumentParser actions, subclasses of Action may - be defined and passed as the action= argument. - - - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, - ArgumentDefaultsHelpFormatter -- Formatter classes which - may be passed as the formatter_class= argument to the - ArgumentParser constructor. HelpFormatter is the default, - RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser - not to change the formatting for help text, and - ArgumentDefaultsHelpFormatter adds information about argument defaults - to the help. - -All other classes in this module are considered implementation details. -(Also note that HelpFormatter and RawDescriptionHelpFormatter are only -considered public as object names -- the API of the formatter objects is -still considered an implementation detail.) -""" - -__version__ = '1.4.0' # we use our own version number independent of the - # one in stdlib and we release this on pypi. - -__external_lib__ = True # to make sure the tests really test THIS lib, - # not the builtin one in Python stdlib - -__all__ = [ - 'ArgumentParser', - 'ArgumentError', - 'ArgumentTypeError', - 'FileType', - 'HelpFormatter', - 'ArgumentDefaultsHelpFormatter', - 'RawDescriptionHelpFormatter', - 'RawTextHelpFormatter', - 'Namespace', - 'Action', - 'ONE_OR_MORE', - 'OPTIONAL', - 'PARSER', - 'REMAINDER', - 'SUPPRESS', - 'ZERO_OR_MORE', -] - - -import copy as _copy -import os as _os -import re as _re -import sys as _sys -import textwrap as _textwrap - -from gettext import gettext as _ - -try: - set -except NameError: - # for python < 2.4 compatibility (sets module is there since 2.3): - from sets import Set as set - -try: - basestring -except NameError: - basestring = str - -try: - sorted -except NameError: - # for python < 2.4 compatibility: - def sorted(iterable, reverse=False): - result = list(iterable) - result.sort() - if reverse: - result.reverse() - return result - - -def _callable(obj): - return hasattr(obj, '__call__') or hasattr(obj, '__bases__') - - -SUPPRESS = '==SUPPRESS==' - -OPTIONAL = '?' -ZERO_OR_MORE = '*' -ONE_OR_MORE = '+' -PARSER = 'A...' -REMAINDER = '...' -_UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args' - -# ============================= -# Utility functions and classes -# ============================= - -class _AttributeHolder(object): - """Abstract base class that provides __repr__. - - The __repr__ method returns a string in the format:: - ClassName(attr=name, attr=name, ...) - The attributes are determined either by a class-level attribute, - '_kwarg_names', or by inspecting the instance __dict__. - """ - - def __repr__(self): - type_name = type(self).__name__ - arg_strings = [] - for arg in self._get_args(): - arg_strings.append(repr(arg)) - for name, value in self._get_kwargs(): - arg_strings.append('%s=%r' % (name, value)) - return '%s(%s)' % (type_name, ', '.join(arg_strings)) - - def _get_kwargs(self): - return sorted(self.__dict__.items()) - - def _get_args(self): - return [] - - -def _ensure_value(namespace, name, value): - if getattr(namespace, name, None) is None: - setattr(namespace, name, value) - return getattr(namespace, name) - - -# =============== -# Formatting Help -# =============== - -class HelpFormatter(object): - """Formatter for generating usage messages and argument help strings. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def __init__(self, - prog, - indent_increment=2, - max_help_position=24, - width=None): - - # default setting for width - if width is None: - try: - width = int(_os.environ['COLUMNS']) - except (KeyError, ValueError): - width = 80 - width -= 2 - - self._prog = prog - self._indent_increment = indent_increment - self._max_help_position = max_help_position - self._width = width - - self._current_indent = 0 - self._level = 0 - self._action_max_length = 0 - - self._root_section = self._Section(self, None) - self._current_section = self._root_section - - self._whitespace_matcher = _re.compile(r'\s+') - self._long_break_matcher = _re.compile(r'\n\n\n+') - - # =============================== - # Section and indentation methods - # =============================== - def _indent(self): - self._current_indent += self._indent_increment - self._level += 1 - - def _dedent(self): - self._current_indent -= self._indent_increment - assert self._current_indent >= 0, 'Indent decreased below 0.' - self._level -= 1 - - class _Section(object): - - def __init__(self, formatter, parent, heading=None): - self.formatter = formatter - self.parent = parent - self.heading = heading - self.items = [] - - def format_help(self): - # format the indented section - if self.parent is not None: - self.formatter._indent() - join = self.formatter._join_parts - for func, args in self.items: - func(*args) - item_help = join([func(*args) for func, args in self.items]) - if self.parent is not None: - self.formatter._dedent() - - # return nothing if the section was empty - if not item_help: - return '' - - # add the heading if the section was non-empty - if self.heading is not SUPPRESS and self.heading is not None: - current_indent = self.formatter._current_indent - heading = '%*s%s:\n' % (current_indent, '', self.heading) - else: - heading = '' - - # join the section-initial newline, the heading and the help - return join(['\n', heading, item_help, '\n']) - - def _add_item(self, func, args): - self._current_section.items.append((func, args)) - - # ======================== - # Message building methods - # ======================== - def start_section(self, heading): - self._indent() - section = self._Section(self, self._current_section, heading) - self._add_item(section.format_help, []) - self._current_section = section - - def end_section(self): - self._current_section = self._current_section.parent - self._dedent() - - def add_text(self, text): - if text is not SUPPRESS and text is not None: - self._add_item(self._format_text, [text]) - - def add_usage(self, usage, actions, groups, prefix=None): - if usage is not SUPPRESS: - args = usage, actions, groups, prefix - self._add_item(self._format_usage, args) - - def add_argument(self, action): - if action.help is not SUPPRESS: - - # find all invocations - get_invocation = self._format_action_invocation - invocations = [get_invocation(action)] - for subaction in self._iter_indented_subactions(action): - invocations.append(get_invocation(subaction)) - - # update the maximum item length - invocation_length = max([len(s) for s in invocations]) - action_length = invocation_length + self._current_indent - self._action_max_length = max(self._action_max_length, - action_length) - - # add the item to the list - self._add_item(self._format_action, [action]) - - def add_arguments(self, actions): - for action in actions: - self.add_argument(action) - - # ======================= - # Help-formatting methods - # ======================= - def format_help(self): - help = self._root_section.format_help() - if help: - help = self._long_break_matcher.sub('\n\n', help) - help = help.strip('\n') + '\n' - return help - - def _join_parts(self, part_strings): - return ''.join([part - for part in part_strings - if part and part is not SUPPRESS]) - - def _format_usage(self, usage, actions, groups, prefix): - if prefix is None: - prefix = _('usage: ') - - # if usage is specified, use that - if usage is not None: - usage = usage % dict(prog=self._prog) - - # if no optionals or positionals are available, usage is just prog - elif usage is None and not actions: - usage = '%(prog)s' % dict(prog=self._prog) - - # if optionals and positionals are available, calculate usage - elif usage is None: - prog = '%(prog)s' % dict(prog=self._prog) - - # split optionals from positionals - optionals = [] - positionals = [] - for action in actions: - if action.option_strings: - optionals.append(action) - else: - positionals.append(action) - - # build full usage string - format = self._format_actions_usage - action_usage = format(optionals + positionals, groups) - usage = ' '.join([s for s in [prog, action_usage] if s]) - - # wrap the usage parts if it's too long - text_width = self._width - self._current_indent - if len(prefix) + len(usage) > text_width: - - # break usage into wrappable parts - part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' - opt_usage = format(optionals, groups) - pos_usage = format(positionals, groups) - opt_parts = _re.findall(part_regexp, opt_usage) - pos_parts = _re.findall(part_regexp, pos_usage) - assert ' '.join(opt_parts) == opt_usage - assert ' '.join(pos_parts) == pos_usage - - # helper for wrapping lines - def get_lines(parts, indent, prefix=None): - lines = [] - line = [] - if prefix is not None: - line_len = len(prefix) - 1 - else: - line_len = len(indent) - 1 - for part in parts: - if line_len + 1 + len(part) > text_width: - lines.append(indent + ' '.join(line)) - line = [] - line_len = len(indent) - 1 - line.append(part) - line_len += len(part) + 1 - if line: - lines.append(indent + ' '.join(line)) - if prefix is not None: - lines[0] = lines[0][len(indent):] - return lines - - # if prog is short, follow it with optionals or positionals - if len(prefix) + len(prog) <= 0.75 * text_width: - indent = ' ' * (len(prefix) + len(prog) + 1) - if opt_parts: - lines = get_lines([prog] + opt_parts, indent, prefix) - lines.extend(get_lines(pos_parts, indent)) - elif pos_parts: - lines = get_lines([prog] + pos_parts, indent, prefix) - else: - lines = [prog] - - # if prog is long, put it on its own line - else: - indent = ' ' * len(prefix) - parts = opt_parts + pos_parts - lines = get_lines(parts, indent) - if len(lines) > 1: - lines = [] - lines.extend(get_lines(opt_parts, indent)) - lines.extend(get_lines(pos_parts, indent)) - lines = [prog] + lines - - # join lines into usage - usage = '\n'.join(lines) - - # prefix with 'usage:' - return '%s%s\n\n' % (prefix, usage) - - def _format_actions_usage(self, actions, groups): - # find group indices and identify actions in groups - group_actions = set() - inserts = {} - for group in groups: - try: - start = actions.index(group._group_actions[0]) - except ValueError: - continue - else: - end = start + len(group._group_actions) - if actions[start:end] == group._group_actions: - for action in group._group_actions: - group_actions.add(action) - if not group.required: - if start in inserts: - inserts[start] += ' [' - else: - inserts[start] = '[' - inserts[end] = ']' - else: - if start in inserts: - inserts[start] += ' (' - else: - inserts[start] = '(' - inserts[end] = ')' - for i in range(start + 1, end): - inserts[i] = '|' - - # collect all actions format strings - parts = [] - for i, action in enumerate(actions): - - # suppressed arguments are marked with None - # remove | separators for suppressed arguments - if action.help is SUPPRESS: - parts.append(None) - if inserts.get(i) == '|': - inserts.pop(i) - elif inserts.get(i + 1) == '|': - inserts.pop(i + 1) - - # produce all arg strings - elif not action.option_strings: - part = self._format_args(action, action.dest) - - # if it's in a group, strip the outer [] - if action in group_actions: - if part[0] == '[' and part[-1] == ']': - part = part[1:-1] - - # add the action string to the list - parts.append(part) - - # produce the first way to invoke the option in brackets - else: - option_string = action.option_strings[0] - - # if the Optional doesn't take a value, format is: - # -s or --long - if action.nargs == 0: - part = '%s' % option_string - - # if the Optional takes a value, format is: - # -s ARGS or --long ARGS - else: - default = action.dest.upper() - args_string = self._format_args(action, default) - part = '%s %s' % (option_string, args_string) - - # make it look optional if it's not required or in a group - if not action.required and action not in group_actions: - part = '[%s]' % part - - # add the action string to the list - parts.append(part) - - # insert things at the necessary indices - for i in sorted(inserts, reverse=True): - parts[i:i] = [inserts[i]] - - # join all the action items with spaces - text = ' '.join([item for item in parts if item is not None]) - - # clean up separators for mutually exclusive groups - open = r'[\[(]' - close = r'[\])]' - text = _re.sub(r'(%s) ' % open, r'\1', text) - text = _re.sub(r' (%s)' % close, r'\1', text) - text = _re.sub(r'%s *%s' % (open, close), r'', text) - text = _re.sub(r'\(([^|]*)\)', r'\1', text) - text = text.strip() - - # return the text - return text - - def _format_text(self, text): - if '%(prog)' in text: - text = text % dict(prog=self._prog) - text_width = self._width - self._current_indent - indent = ' ' * self._current_indent - return self._fill_text(text, text_width, indent) + '\n\n' - - def _format_action(self, action): - # determine the required width and the entry label - help_position = min(self._action_max_length + 2, - self._max_help_position) - help_width = self._width - help_position - action_width = help_position - self._current_indent - 2 - action_header = self._format_action_invocation(action) - - # ho nelp; start on same line and add a final newline - if not action.help: - tup = self._current_indent, '', action_header - action_header = '%*s%s\n' % tup - - # short action name; start on the same line and pad two spaces - elif len(action_header) <= action_width: - tup = self._current_indent, '', action_width, action_header - action_header = '%*s%-*s ' % tup - indent_first = 0 - - # long action name; start on the next line - else: - tup = self._current_indent, '', action_header - action_header = '%*s%s\n' % tup - indent_first = help_position - - # collect the pieces of the action help - parts = [action_header] - - # if there was help for the action, add lines of help text - if action.help: - help_text = self._expand_help(action) - help_lines = self._split_lines(help_text, help_width) - parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) - for line in help_lines[1:]: - parts.append('%*s%s\n' % (help_position, '', line)) - - # or add a newline if the description doesn't end with one - elif not action_header.endswith('\n'): - parts.append('\n') - - # if there are any sub-actions, add their help as well - for subaction in self._iter_indented_subactions(action): - parts.append(self._format_action(subaction)) - - # return a single string - return self._join_parts(parts) - - def _format_action_invocation(self, action): - if not action.option_strings: - metavar, = self._metavar_formatter(action, action.dest)(1) - return metavar - - else: - parts = [] - - # if the Optional doesn't take a value, format is: - # -s, --long - if action.nargs == 0: - parts.extend(action.option_strings) - - # if the Optional takes a value, format is: - # -s ARGS, --long ARGS - else: - default = action.dest.upper() - args_string = self._format_args(action, default) - for option_string in action.option_strings: - parts.append('%s %s' % (option_string, args_string)) - - return ', '.join(parts) - - def _metavar_formatter(self, action, default_metavar): - if action.metavar is not None: - result = action.metavar - elif action.choices is not None: - choice_strs = [str(choice) for choice in action.choices] - result = '{%s}' % ','.join(choice_strs) - else: - result = default_metavar - - def format(tuple_size): - if isinstance(result, tuple): - return result - else: - return (result, ) * tuple_size - return format - - def _format_args(self, action, default_metavar): - get_metavar = self._metavar_formatter(action, default_metavar) - if action.nargs is None: - result = '%s' % get_metavar(1) - elif action.nargs == OPTIONAL: - result = '[%s]' % get_metavar(1) - elif action.nargs == ZERO_OR_MORE: - result = '[%s [%s ...]]' % get_metavar(2) - elif action.nargs == ONE_OR_MORE: - result = '%s [%s ...]' % get_metavar(2) - elif action.nargs == REMAINDER: - result = '...' - elif action.nargs == PARSER: - result = '%s ...' % get_metavar(1) - else: - formats = ['%s' for _ in range(action.nargs)] - result = ' '.join(formats) % get_metavar(action.nargs) - return result - - def _expand_help(self, action): - params = dict(vars(action), prog=self._prog) - for name in list(params): - if params[name] is SUPPRESS: - del params[name] - for name in list(params): - if hasattr(params[name], '__name__'): - params[name] = params[name].__name__ - if params.get('choices') is not None: - choices_str = ', '.join([str(c) for c in params['choices']]) - params['choices'] = choices_str - return self._get_help_string(action) % params - - def _iter_indented_subactions(self, action): - try: - get_subactions = action._get_subactions - except AttributeError: - pass - else: - self._indent() - for subaction in get_subactions(): - yield subaction - self._dedent() - - def _split_lines(self, text, width): - text = self._whitespace_matcher.sub(' ', text).strip() - return _textwrap.wrap(text, width) - - def _fill_text(self, text, width, indent): - text = self._whitespace_matcher.sub(' ', text).strip() - return _textwrap.fill(text, width, initial_indent=indent, - subsequent_indent=indent) - - def _get_help_string(self, action): - return action.help - - -class RawDescriptionHelpFormatter(HelpFormatter): - """Help message formatter which retains any formatting in descriptions. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _fill_text(self, text, width, indent): - return ''.join([indent + line for line in text.splitlines(True)]) - - -class RawTextHelpFormatter(RawDescriptionHelpFormatter): - """Help message formatter which retains formatting of all help text. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _split_lines(self, text, width): - return text.splitlines() - - -class ArgumentDefaultsHelpFormatter(HelpFormatter): - """Help message formatter which adds default values to argument help. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _get_help_string(self, action): - help = action.help - if '%(default)' not in action.help: - if action.default is not SUPPRESS: - defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] - if action.option_strings or action.nargs in defaulting_nargs: - help += ' (default: %(default)s)' - return help - - -# ===================== -# Options and Arguments -# ===================== - -def _get_action_name(argument): - if argument is None: - return None - elif argument.option_strings: - return '/'.join(argument.option_strings) - elif argument.metavar not in (None, SUPPRESS): - return argument.metavar - elif argument.dest not in (None, SUPPRESS): - return argument.dest - else: - return None - - -class ArgumentError(Exception): - """An error from creating or using an argument (optional or positional). - - The string value of this exception is the message, augmented with - information about the argument that caused it. - """ - - def __init__(self, argument, message): - self.argument_name = _get_action_name(argument) - self.message = message - - def __str__(self): - if self.argument_name is None: - format = '%(message)s' - else: - format = 'argument %(argument_name)s: %(message)s' - return format % dict(message=self.message, - argument_name=self.argument_name) - - -class ArgumentTypeError(Exception): - """An error from trying to convert a command line string to a type.""" - pass - - -# ============== -# Action classes -# ============== - -class Action(_AttributeHolder): - """Information about how to convert command line strings to Python objects. - - Action objects are used by an ArgumentParser to represent the information - needed to parse a single argument from one or more strings from the - command line. The keyword arguments to the Action constructor are also - all attributes of Action instances. - - Keyword Arguments: - - - option_strings -- A list of command-line option strings which - should be associated with this action. - - - dest -- The name of the attribute to hold the created object(s) - - - nargs -- The number of command-line arguments that should be - consumed. By default, one argument will be consumed and a single - value will be produced. Other values include: - - N (an integer) consumes N arguments (and produces a list) - - '?' consumes zero or one arguments - - '*' consumes zero or more arguments (and produces a list) - - '+' consumes one or more arguments (and produces a list) - Note that the difference between the default and nargs=1 is that - with the default, a single value will be produced, while with - nargs=1, a list containing a single value will be produced. - - - const -- The value to be produced if the option is specified and the - option uses an action that takes no values. - - - default -- The value to be produced if the option is not specified. - - - type -- The type which the command-line arguments should be converted - to, should be one of 'string', 'int', 'float', 'complex' or a - callable object that accepts a single string argument. If None, - 'string' is assumed. - - - choices -- A container of values that should be allowed. If not None, - after a command-line argument has been converted to the appropriate - type, an exception will be raised if it is not a member of this - collection. - - - required -- True if the action must always be specified at the - command line. This is only meaningful for optional command-line - arguments. - - - help -- The help string describing the argument. - - - metavar -- The name to be used for the option's argument with the - help string. If None, the 'dest' value will be used as the name. - """ - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - self.option_strings = option_strings - self.dest = dest - self.nargs = nargs - self.const = const - self.default = default - self.type = type - self.choices = choices - self.required = required - self.help = help - self.metavar = metavar - - def _get_kwargs(self): - names = [ - 'option_strings', - 'dest', - 'nargs', - 'const', - 'default', - 'type', - 'choices', - 'help', - 'metavar', - ] - return [(name, getattr(self, name)) for name in names] - - def __call__(self, parser, namespace, values, option_string=None): - raise NotImplementedError(_('.__call__() not defined')) - - -class _StoreAction(Action): - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - if nargs == 0: - raise ValueError('nargs for store actions must be > 0; if you ' - 'have nothing to store, actions such as store ' - 'true or store const may be more appropriate') - if const is not None and nargs != OPTIONAL: - raise ValueError('nargs must be %r to supply const' % OPTIONAL) - super(_StoreAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=nargs, - const=const, - default=default, - type=type, - choices=choices, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, values) - - -class _StoreConstAction(Action): - - def __init__(self, - option_strings, - dest, - const, - default=None, - required=False, - help=None, - metavar=None): - super(_StoreConstAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - const=const, - default=default, - required=required, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, self.const) - - -class _StoreTrueAction(_StoreConstAction): - - def __init__(self, - option_strings, - dest, - default=False, - required=False, - help=None): - super(_StoreTrueAction, self).__init__( - option_strings=option_strings, - dest=dest, - const=True, - default=default, - required=required, - help=help) - - -class _StoreFalseAction(_StoreConstAction): - - def __init__(self, - option_strings, - dest, - default=True, - required=False, - help=None): - super(_StoreFalseAction, self).__init__( - option_strings=option_strings, - dest=dest, - const=False, - default=default, - required=required, - help=help) - - -class _AppendAction(Action): - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - if nargs == 0: - raise ValueError('nargs for append actions must be > 0; if arg ' - 'strings are not supplying the value to append, ' - 'the append const action may be more appropriate') - if const is not None and nargs != OPTIONAL: - raise ValueError('nargs must be %r to supply const' % OPTIONAL) - super(_AppendAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=nargs, - const=const, - default=default, - type=type, - choices=choices, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - items = _copy.copy(_ensure_value(namespace, self.dest, [])) - items.append(values) - setattr(namespace, self.dest, items) - - -class _AppendConstAction(Action): - - def __init__(self, - option_strings, - dest, - const, - default=None, - required=False, - help=None, - metavar=None): - super(_AppendConstAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - const=const, - default=default, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - items = _copy.copy(_ensure_value(namespace, self.dest, [])) - items.append(self.const) - setattr(namespace, self.dest, items) - - -class _CountAction(Action): - - def __init__(self, - option_strings, - dest, - default=None, - required=False, - help=None): - super(_CountAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - default=default, - required=required, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - new_count = _ensure_value(namespace, self.dest, 0) + 1 - setattr(namespace, self.dest, new_count) - - -class _HelpAction(Action): - - def __init__(self, - option_strings, - dest=SUPPRESS, - default=SUPPRESS, - help=None): - super(_HelpAction, self).__init__( - option_strings=option_strings, - dest=dest, - default=default, - nargs=0, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - parser.print_help() - parser.exit() - - -class _VersionAction(Action): - - def __init__(self, - option_strings, - version=None, - dest=SUPPRESS, - default=SUPPRESS, - help="show program's version number and exit"): - super(_VersionAction, self).__init__( - option_strings=option_strings, - dest=dest, - default=default, - nargs=0, - help=help) - self.version = version - - def __call__(self, parser, namespace, values, option_string=None): - version = self.version - if version is None: - version = parser.version - formatter = parser._get_formatter() - formatter.add_text(version) - parser.exit(message=formatter.format_help()) - - -class _SubParsersAction(Action): - - class _ChoicesPseudoAction(Action): - - def __init__(self, name, aliases, help): - metavar = dest = name - if aliases: - metavar += ' (%s)' % ', '.join(aliases) - sup = super(_SubParsersAction._ChoicesPseudoAction, self) - sup.__init__(option_strings=[], dest=dest, help=help, - metavar=metavar) - - def __init__(self, - option_strings, - prog, - parser_class, - dest=SUPPRESS, - help=None, - metavar=None): - - self._prog_prefix = prog - self._parser_class = parser_class - self._name_parser_map = {} - self._choices_actions = [] - - super(_SubParsersAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=PARSER, - choices=self._name_parser_map, - help=help, - metavar=metavar) - - def add_parser(self, name, **kwargs): - # set prog from the existing prefix - if kwargs.get('prog') is None: - kwargs['prog'] = '%s %s' % (self._prog_prefix, name) - - aliases = kwargs.pop('aliases', ()) - - # create a pseudo-action to hold the choice help - if 'help' in kwargs: - help = kwargs.pop('help') - choice_action = self._ChoicesPseudoAction(name, aliases, help) - self._choices_actions.append(choice_action) - - # create the parser and add it to the map - parser = self._parser_class(**kwargs) - self._name_parser_map[name] = parser - - # make parser available under aliases also - for alias in aliases: - self._name_parser_map[alias] = parser - - return parser - - def _get_subactions(self): - return self._choices_actions - - def __call__(self, parser, namespace, values, option_string=None): - parser_name = values[0] - arg_strings = values[1:] - - # set the parser name if requested - if self.dest is not SUPPRESS: - setattr(namespace, self.dest, parser_name) - - # select the parser - try: - parser = self._name_parser_map[parser_name] - except KeyError: - tup = parser_name, ', '.join(self._name_parser_map) - msg = _('unknown parser %r (choices: %s)' % tup) - raise ArgumentError(self, msg) - - # parse all the remaining options into the namespace - # store any unrecognized options on the object, so that the top - # level parser can decide what to do with them - namespace, arg_strings = parser.parse_known_args(arg_strings, namespace) - if arg_strings: - vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, []) - getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings) - - -# ============== -# Type classes -# ============== - -class FileType(object): - """Factory for creating file object types - - Instances of FileType are typically passed as type= arguments to the - ArgumentParser add_argument() method. - - Keyword Arguments: - - mode -- A string indicating how the file is to be opened. Accepts the - same values as the builtin open() function. - - bufsize -- The file's desired buffer size. Accepts the same values as - the builtin open() function. - """ - - def __init__(self, mode='r', bufsize=None): - self._mode = mode - self._bufsize = bufsize - - def __call__(self, string): - # the special argument "-" means sys.std{in,out} - if string == '-': - if 'r' in self._mode: - return _sys.stdin - elif 'w' in self._mode: - return _sys.stdout - else: - msg = _('argument "-" with mode %r' % self._mode) - raise ValueError(msg) - - try: - # all other arguments are used as file names - if self._bufsize: - return open(string, self._mode, self._bufsize) - else: - return open(string, self._mode) - except IOError: - err = _sys.exc_info()[1] - message = _("can't open '%s': %s") - raise ArgumentTypeError(message % (string, err)) - - def __repr__(self): - args = [self._mode, self._bufsize] - args_str = ', '.join([repr(arg) for arg in args if arg is not None]) - return '%s(%s)' % (type(self).__name__, args_str) - -# =========================== -# Optional and Positional Parsing -# =========================== - -class Namespace(_AttributeHolder): - """Simple object for storing attributes. - - Implements equality by attribute names and values, and provides a simple - string representation. - """ - - def __init__(self, **kwargs): - for name in kwargs: - setattr(self, name, kwargs[name]) - - __hash__ = None - - def __eq__(self, other): - return vars(self) == vars(other) - - def __ne__(self, other): - return not (self == other) - - def __contains__(self, key): - return key in self.__dict__ - - -class _ActionsContainer(object): - - def __init__(self, - description, - prefix_chars, - argument_default, - conflict_handler): - super(_ActionsContainer, self).__init__() - - self.description = description - self.argument_default = argument_default - self.prefix_chars = prefix_chars - self.conflict_handler = conflict_handler - - # set up registries - self._registries = {} - - # register actions - self.register('action', None, _StoreAction) - self.register('action', 'store', _StoreAction) - self.register('action', 'store_const', _StoreConstAction) - self.register('action', 'store_true', _StoreTrueAction) - self.register('action', 'store_false', _StoreFalseAction) - self.register('action', 'append', _AppendAction) - self.register('action', 'append_const', _AppendConstAction) - self.register('action', 'count', _CountAction) - self.register('action', 'help', _HelpAction) - self.register('action', 'version', _VersionAction) - self.register('action', 'parsers', _SubParsersAction) - - # raise an exception if the conflict handler is invalid - self._get_handler() - - # action storage - self._actions = [] - self._option_string_actions = {} - - # groups - self._action_groups = [] - self._mutually_exclusive_groups = [] - - # defaults storage - self._defaults = {} - - # determines whether an "option" looks like a negative number - self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$') - - # whether or not there are any optionals that look like negative - # numbers -- uses a list so it can be shared and edited - self._has_negative_number_optionals = [] - - # ==================== - # Registration methods - # ==================== - def register(self, registry_name, value, object): - registry = self._registries.setdefault(registry_name, {}) - registry[value] = object - - def _registry_get(self, registry_name, value, default=None): - return self._registries[registry_name].get(value, default) - - # ================================== - # Namespace default accessor methods - # ================================== - def set_defaults(self, **kwargs): - self._defaults.update(kwargs) - - # if these defaults match any existing arguments, replace - # the previous default on the object with the new one - for action in self._actions: - if action.dest in kwargs: - action.default = kwargs[action.dest] - - def get_default(self, dest): - for action in self._actions: - if action.dest == dest and action.default is not None: - return action.default - return self._defaults.get(dest, None) - - - # ======================= - # Adding argument actions - # ======================= - def add_argument(self, *args, **kwargs): - """ - add_argument(dest, ..., name=value, ...) - add_argument(option_string, option_string, ..., name=value, ...) - """ - - # if no positional args are supplied or only one is supplied and - # it doesn't look like an option string, parse a positional - # argument - chars = self.prefix_chars - if not args or len(args) == 1 and args[0][0] not in chars: - if args and 'dest' in kwargs: - raise ValueError('dest supplied twice for positional argument') - kwargs = self._get_positional_kwargs(*args, **kwargs) - - # otherwise, we're adding an optional argument - else: - kwargs = self._get_optional_kwargs(*args, **kwargs) - - # if no default was supplied, use the parser-level default - if 'default' not in kwargs: - dest = kwargs['dest'] - if dest in self._defaults: - kwargs['default'] = self._defaults[dest] - elif self.argument_default is not None: - kwargs['default'] = self.argument_default - - # create the action object, and add it to the parser - action_class = self._pop_action_class(kwargs) - if not _callable(action_class): - raise ValueError('unknown action "%s"' % action_class) - action = action_class(**kwargs) - - # raise an error if the action type is not callable - type_func = self._registry_get('type', action.type, action.type) - if not _callable(type_func): - raise ValueError('%r is not callable' % type_func) - - return self._add_action(action) - - def add_argument_group(self, *args, **kwargs): - group = _ArgumentGroup(self, *args, **kwargs) - self._action_groups.append(group) - return group - - def add_mutually_exclusive_group(self, **kwargs): - group = _MutuallyExclusiveGroup(self, **kwargs) - self._mutually_exclusive_groups.append(group) - return group - - def _add_action(self, action): - # resolve any conflicts - self._check_conflict(action) - - # add to actions list - self._actions.append(action) - action.container = self - - # index the action by any option strings it has - for option_string in action.option_strings: - self._option_string_actions[option_string] = action - - # set the flag if any option strings look like negative numbers - for option_string in action.option_strings: - if self._negative_number_matcher.match(option_string): - if not self._has_negative_number_optionals: - self._has_negative_number_optionals.append(True) - - # return the created action - return action - - def _remove_action(self, action): - self._actions.remove(action) - - def _add_container_actions(self, container): - # collect groups by titles - title_group_map = {} - for group in self._action_groups: - if group.title in title_group_map: - msg = _('cannot merge actions - two groups are named %r') - raise ValueError(msg % (group.title)) - title_group_map[group.title] = group - - # map each action to its group - group_map = {} - for group in container._action_groups: - - # if a group with the title exists, use that, otherwise - # create a new group matching the container's group - if group.title not in title_group_map: - title_group_map[group.title] = self.add_argument_group( - title=group.title, - description=group.description, - conflict_handler=group.conflict_handler) - - # map the actions to their new group - for action in group._group_actions: - group_map[action] = title_group_map[group.title] - - # add container's mutually exclusive groups - # NOTE: if add_mutually_exclusive_group ever gains title= and - # description= then this code will need to be expanded as above - for group in container._mutually_exclusive_groups: - mutex_group = self.add_mutually_exclusive_group( - required=group.required) - - # map the actions to their new mutex group - for action in group._group_actions: - group_map[action] = mutex_group - - # add all actions to this container or their group - for action in container._actions: - group_map.get(action, self)._add_action(action) - - def _get_positional_kwargs(self, dest, **kwargs): - # make sure required is not specified - if 'required' in kwargs: - msg = _("'required' is an invalid argument for positionals") - raise TypeError(msg) - - # mark positional arguments as required if at least one is - # always required - if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: - kwargs['required'] = True - if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: - kwargs['required'] = True - - # return the keyword arguments with no option strings - return dict(kwargs, dest=dest, option_strings=[]) - - def _get_optional_kwargs(self, *args, **kwargs): - # determine short and long option strings - option_strings = [] - long_option_strings = [] - for option_string in args: - # error on strings that don't start with an appropriate prefix - if not option_string[0] in self.prefix_chars: - msg = _('invalid option string %r: ' - 'must start with a character %r') - tup = option_string, self.prefix_chars - raise ValueError(msg % tup) - - # strings starting with two prefix characters are long options - option_strings.append(option_string) - if option_string[0] in self.prefix_chars: - if len(option_string) > 1: - if option_string[1] in self.prefix_chars: - long_option_strings.append(option_string) - - # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' - dest = kwargs.pop('dest', None) - if dest is None: - if long_option_strings: - dest_option_string = long_option_strings[0] - else: - dest_option_string = option_strings[0] - dest = dest_option_string.lstrip(self.prefix_chars) - if not dest: - msg = _('dest= is required for options like %r') - raise ValueError(msg % option_string) - dest = dest.replace('-', '_') - - # return the updated keyword arguments - return dict(kwargs, dest=dest, option_strings=option_strings) - - def _pop_action_class(self, kwargs, default=None): - action = kwargs.pop('action', default) - return self._registry_get('action', action, action) - - def _get_handler(self): - # determine function from conflict handler string - handler_func_name = '_handle_conflict_%s' % self.conflict_handler - try: - return getattr(self, handler_func_name) - except AttributeError: - msg = _('invalid conflict_resolution value: %r') - raise ValueError(msg % self.conflict_handler) - - def _check_conflict(self, action): - - # find all options that conflict with this option - confl_optionals = [] - for option_string in action.option_strings: - if option_string in self._option_string_actions: - confl_optional = self._option_string_actions[option_string] - confl_optionals.append((option_string, confl_optional)) - - # resolve any conflicts - if confl_optionals: - conflict_handler = self._get_handler() - conflict_handler(action, confl_optionals) - - def _handle_conflict_error(self, action, conflicting_actions): - message = _('conflicting option string(s): %s') - conflict_string = ', '.join([option_string - for option_string, action - in conflicting_actions]) - raise ArgumentError(action, message % conflict_string) - - def _handle_conflict_resolve(self, action, conflicting_actions): - - # remove all conflicting options - for option_string, action in conflicting_actions: - - # remove the conflicting option - action.option_strings.remove(option_string) - self._option_string_actions.pop(option_string, None) - - # if the option now has no option string, remove it from the - # container holding it - if not action.option_strings: - action.container._remove_action(action) - - -class _ArgumentGroup(_ActionsContainer): - - def __init__(self, container, title=None, description=None, **kwargs): - # add any missing keyword arguments by checking the container - update = kwargs.setdefault - update('conflict_handler', container.conflict_handler) - update('prefix_chars', container.prefix_chars) - update('argument_default', container.argument_default) - super_init = super(_ArgumentGroup, self).__init__ - super_init(description=description, **kwargs) - - # group attributes - self.title = title - self._group_actions = [] - - # share most attributes with the container - self._registries = container._registries - self._actions = container._actions - self._option_string_actions = container._option_string_actions - self._defaults = container._defaults - self._has_negative_number_optionals = \ - container._has_negative_number_optionals - - def _add_action(self, action): - action = super(_ArgumentGroup, self)._add_action(action) - self._group_actions.append(action) - return action - - def _remove_action(self, action): - super(_ArgumentGroup, self)._remove_action(action) - self._group_actions.remove(action) - - -class _MutuallyExclusiveGroup(_ArgumentGroup): - - def __init__(self, container, required=False): - super(_MutuallyExclusiveGroup, self).__init__(container) - self.required = required - self._container = container - - def _add_action(self, action): - if action.required: - msg = _('mutually exclusive arguments must be optional') - raise ValueError(msg) - action = self._container._add_action(action) - self._group_actions.append(action) - return action - - def _remove_action(self, action): - self._container._remove_action(action) - self._group_actions.remove(action) - - -class ArgumentParser(_AttributeHolder, _ActionsContainer): - """Object for parsing command line strings into Python objects. - - Keyword Arguments: - - prog -- The name of the program (default: sys.argv[0]) - - usage -- A usage message (default: auto-generated from arguments) - - description -- A description of what the program does - - epilog -- Text following the argument descriptions - - parents -- Parsers whose arguments should be copied into this one - - formatter_class -- HelpFormatter class for printing help messages - - prefix_chars -- Characters that prefix optional arguments - - fromfile_prefix_chars -- Characters that prefix files containing - additional arguments - - argument_default -- The default value for all arguments - - conflict_handler -- String indicating how to handle conflicts - - add_help -- Add a -h/-help option - """ - - def __init__(self, - prog=None, - usage=None, - description=None, - epilog=None, - version=None, - parents=[], - formatter_class=HelpFormatter, - prefix_chars='-', - fromfile_prefix_chars=None, - argument_default=None, - conflict_handler='error', - add_help=True): - - if version is not None: - import warnings - warnings.warn( - """The "version" argument to ArgumentParser is deprecated. """ - """Please use """ - """"add_argument(..., action='version', version="N", ...)" """ - """instead""", DeprecationWarning) - - superinit = super(ArgumentParser, self).__init__ - superinit(description=description, - prefix_chars=prefix_chars, - argument_default=argument_default, - conflict_handler=conflict_handler) - - # default setting for prog - if prog is None: - prog = _os.path.basename(_sys.argv[0]) - - self.prog = prog - self.usage = usage - self.epilog = epilog - self.version = version - self.formatter_class = formatter_class - self.fromfile_prefix_chars = fromfile_prefix_chars - self.add_help = add_help - - add_group = self.add_argument_group - self._positionals = add_group(_('positional arguments')) - self._optionals = add_group(_('optional arguments')) - self._subparsers = None - - # register types - def identity(string): - return string - self.register('type', None, identity) - - # add help and version arguments if necessary - # (using explicit default to override global argument_default) - if '-' in prefix_chars: - default_prefix = '-' - else: - default_prefix = prefix_chars[0] - if self.add_help: - self.add_argument( - default_prefix+'h', default_prefix*2+'help', - action='help', default=SUPPRESS, - help=_('show this help message and exit')) - if self.version: - self.add_argument( - default_prefix+'v', default_prefix*2+'version', - action='version', default=SUPPRESS, - version=self.version, - help=_("show program's version number and exit")) - - # add parent arguments and defaults - for parent in parents: - self._add_container_actions(parent) - try: - defaults = parent._defaults - except AttributeError: - pass - else: - self._defaults.update(defaults) - - # ======================= - # Pretty __repr__ methods - # ======================= - def _get_kwargs(self): - names = [ - 'prog', - 'usage', - 'description', - 'version', - 'formatter_class', - 'conflict_handler', - 'add_help', - ] - return [(name, getattr(self, name)) for name in names] - - # ================================== - # Optional/Positional adding methods - # ================================== - def add_subparsers(self, **kwargs): - if self._subparsers is not None: - self.error(_('cannot have multiple subparser arguments')) - - # add the parser class to the arguments if it's not present - kwargs.setdefault('parser_class', type(self)) - - if 'title' in kwargs or 'description' in kwargs: - title = _(kwargs.pop('title', 'subcommands')) - description = _(kwargs.pop('description', None)) - self._subparsers = self.add_argument_group(title, description) - else: - self._subparsers = self._positionals - - # prog defaults to the usage message of this parser, skipping - # optional arguments and with no "usage:" prefix - if kwargs.get('prog') is None: - formatter = self._get_formatter() - positionals = self._get_positional_actions() - groups = self._mutually_exclusive_groups - formatter.add_usage(self.usage, positionals, groups, '') - kwargs['prog'] = formatter.format_help().strip() - - # create the parsers action and add it to the positionals list - parsers_class = self._pop_action_class(kwargs, 'parsers') - action = parsers_class(option_strings=[], **kwargs) - self._subparsers._add_action(action) - - # return the created parsers action - return action - - def _add_action(self, action): - if action.option_strings: - self._optionals._add_action(action) - else: - self._positionals._add_action(action) - return action - - def _get_optional_actions(self): - return [action - for action in self._actions - if action.option_strings] - - def _get_positional_actions(self): - return [action - for action in self._actions - if not action.option_strings] - - # ===================================== - # Command line argument parsing methods - # ===================================== - def parse_args(self, args=None, namespace=None): - args, argv = self.parse_known_args(args, namespace) - if argv: - msg = _('unrecognized arguments: %s') - self.error(msg % ' '.join(argv)) - return args - - def parse_known_args(self, args=None, namespace=None): - # args default to the system args - if args is None: - args = _sys.argv[1:] - - # default Namespace built from parser defaults - if namespace is None: - namespace = Namespace() - - # add any action defaults that aren't present - for action in self._actions: - if action.dest is not SUPPRESS: - if not hasattr(namespace, action.dest): - if action.default is not SUPPRESS: - setattr(namespace, action.dest, action.default) - - # add any parser defaults that aren't present - for dest in self._defaults: - if not hasattr(namespace, dest): - setattr(namespace, dest, self._defaults[dest]) - - # parse the arguments and exit if there are any errors - try: - namespace, args = self._parse_known_args(args, namespace) - if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): - args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) - delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) - return namespace, args - except ArgumentError: - err = _sys.exc_info()[1] - self.error(str(err)) - - def _parse_known_args(self, arg_strings, namespace): - # replace arg strings that are file references - if self.fromfile_prefix_chars is not None: - arg_strings = self._read_args_from_files(arg_strings) - - # map all mutually exclusive arguments to the other arguments - # they can't occur with - action_conflicts = {} - for mutex_group in self._mutually_exclusive_groups: - group_actions = mutex_group._group_actions - for i, mutex_action in enumerate(mutex_group._group_actions): - conflicts = action_conflicts.setdefault(mutex_action, []) - conflicts.extend(group_actions[:i]) - conflicts.extend(group_actions[i + 1:]) - - # find all option indices, and determine the arg_string_pattern - # which has an 'O' if there is an option at an index, - # an 'A' if there is an argument, or a '-' if there is a '--' - option_string_indices = {} - arg_string_pattern_parts = [] - arg_strings_iter = iter(arg_strings) - for i, arg_string in enumerate(arg_strings_iter): - - # all args after -- are non-options - if arg_string == '--': - arg_string_pattern_parts.append('-') - for arg_string in arg_strings_iter: - arg_string_pattern_parts.append('A') - - # otherwise, add the arg to the arg strings - # and note the index if it was an option - else: - option_tuple = self._parse_optional(arg_string) - if option_tuple is None: - pattern = 'A' - else: - option_string_indices[i] = option_tuple - pattern = 'O' - arg_string_pattern_parts.append(pattern) - - # join the pieces together to form the pattern - arg_strings_pattern = ''.join(arg_string_pattern_parts) - - # converts arg strings to the appropriate and then takes the action - seen_actions = set() - seen_non_default_actions = set() - - def take_action(action, argument_strings, option_string=None): - seen_actions.add(action) - argument_values = self._get_values(action, argument_strings) - - # error if this argument is not allowed with other previously - # seen arguments, assuming that actions that use the default - # value don't really count as "present" - if argument_values is not action.default: - seen_non_default_actions.add(action) - for conflict_action in action_conflicts.get(action, []): - if conflict_action in seen_non_default_actions: - msg = _('not allowed with argument %s') - action_name = _get_action_name(conflict_action) - raise ArgumentError(action, msg % action_name) - - # take the action if we didn't receive a SUPPRESS value - # (e.g. from a default) - if argument_values is not SUPPRESS: - action(self, namespace, argument_values, option_string) - - # function to convert arg_strings into an optional action - def consume_optional(start_index): - - # get the optional identified at this index - option_tuple = option_string_indices[start_index] - action, option_string, explicit_arg = option_tuple - - # identify additional optionals in the same arg string - # (e.g. -xyz is the same as -x -y -z if no args are required) - match_argument = self._match_argument - action_tuples = [] - while True: - - # if we found no optional action, skip it - if action is None: - extras.append(arg_strings[start_index]) - return start_index + 1 - - # if there is an explicit argument, try to match the - # optional's string arguments to only this - if explicit_arg is not None: - arg_count = match_argument(action, 'A') - - # if the action is a single-dash option and takes no - # arguments, try to parse more single-dash options out - # of the tail of the option string - chars = self.prefix_chars - if arg_count == 0 and option_string[1] not in chars: - action_tuples.append((action, [], option_string)) - char = option_string[0] - option_string = char + explicit_arg[0] - new_explicit_arg = explicit_arg[1:] or None - optionals_map = self._option_string_actions - if option_string in optionals_map: - action = optionals_map[option_string] - explicit_arg = new_explicit_arg - else: - msg = _('ignored explicit argument %r') - raise ArgumentError(action, msg % explicit_arg) - - # if the action expect exactly one argument, we've - # successfully matched the option; exit the loop - elif arg_count == 1: - stop = start_index + 1 - args = [explicit_arg] - action_tuples.append((action, args, option_string)) - break - - # error if a double-dash option did not use the - # explicit argument - else: - msg = _('ignored explicit argument %r') - raise ArgumentError(action, msg % explicit_arg) - - # if there is no explicit argument, try to match the - # optional's string arguments with the following strings - # if successful, exit the loop - else: - start = start_index + 1 - selected_patterns = arg_strings_pattern[start:] - arg_count = match_argument(action, selected_patterns) - stop = start + arg_count - args = arg_strings[start:stop] - action_tuples.append((action, args, option_string)) - break - - # add the Optional to the list and return the index at which - # the Optional's string args stopped - assert action_tuples - for action, args, option_string in action_tuples: - take_action(action, args, option_string) - return stop - - # the list of Positionals left to be parsed; this is modified - # by consume_positionals() - positionals = self._get_positional_actions() - - # function to convert arg_strings into positional actions - def consume_positionals(start_index): - # match as many Positionals as possible - match_partial = self._match_arguments_partial - selected_pattern = arg_strings_pattern[start_index:] - arg_counts = match_partial(positionals, selected_pattern) - - # slice off the appropriate arg strings for each Positional - # and add the Positional and its args to the list - for action, arg_count in zip(positionals, arg_counts): - args = arg_strings[start_index: start_index + arg_count] - start_index += arg_count - take_action(action, args) - - # slice off the Positionals that we just parsed and return the - # index at which the Positionals' string args stopped - positionals[:] = positionals[len(arg_counts):] - return start_index - - # consume Positionals and Optionals alternately, until we have - # passed the last option string - extras = [] - start_index = 0 - if option_string_indices: - max_option_string_index = max(option_string_indices) - else: - max_option_string_index = -1 - while start_index <= max_option_string_index: - - # consume any Positionals preceding the next option - next_option_string_index = min([ - index - for index in option_string_indices - if index >= start_index]) - if start_index != next_option_string_index: - positionals_end_index = consume_positionals(start_index) - - # only try to parse the next optional if we didn't consume - # the option string during the positionals parsing - if positionals_end_index > start_index: - start_index = positionals_end_index - continue - else: - start_index = positionals_end_index - - # if we consumed all the positionals we could and we're not - # at the index of an option string, there were extra arguments - if start_index not in option_string_indices: - strings = arg_strings[start_index:next_option_string_index] - extras.extend(strings) - start_index = next_option_string_index - - # consume the next optional and any arguments for it - start_index = consume_optional(start_index) - - # consume any positionals following the last Optional - stop_index = consume_positionals(start_index) - - # if we didn't consume all the argument strings, there were extras - extras.extend(arg_strings[stop_index:]) - - # if we didn't use all the Positional objects, there were too few - # arg strings supplied. - if positionals: - self.error(_('too few arguments')) - - # make sure all required actions were present, and convert defaults. - for action in self._actions: - if action not in seen_actions: - if action.required: - name = _get_action_name(action) - self.error(_('argument %s is required') % name) - else: - # Convert action default now instead of doing it before - # parsing arguments to avoid calling convert functions - # twice (which may fail) if the argument was given, but - # only if it was defined already in the namespace - if (action.default is not None and - isinstance(action.default, basestring) and - hasattr(namespace, action.dest) and - action.default is getattr(namespace, action.dest)): - setattr(namespace, action.dest, - self._get_value(action, action.default)) - - # make sure all required groups had one option present - for group in self._mutually_exclusive_groups: - if group.required: - for action in group._group_actions: - if action in seen_non_default_actions: - break - - # if no actions were used, report the error - else: - names = [_get_action_name(action) - for action in group._group_actions - if action.help is not SUPPRESS] - msg = _('one of the arguments %s is required') - self.error(msg % ' '.join(names)) - - # return the updated namespace and the extra arguments - return namespace, extras - - def _read_args_from_files(self, arg_strings): - # expand arguments referencing files - new_arg_strings = [] - for arg_string in arg_strings: - - # for regular arguments, just add them back into the list - if arg_string[0] not in self.fromfile_prefix_chars: - new_arg_strings.append(arg_string) - - # replace arguments referencing files with the file content - else: - try: - args_file = open(arg_string[1:]) - try: - arg_strings = [] - for arg_line in args_file.read().splitlines(): - for arg in self.convert_arg_line_to_args(arg_line): - arg_strings.append(arg) - arg_strings = self._read_args_from_files(arg_strings) - new_arg_strings.extend(arg_strings) - finally: - args_file.close() - except IOError: - err = _sys.exc_info()[1] - self.error(str(err)) - - # return the modified argument list - return new_arg_strings - - def convert_arg_line_to_args(self, arg_line): - return [arg_line] - - def _match_argument(self, action, arg_strings_pattern): - # match the pattern for this action to the arg strings - nargs_pattern = self._get_nargs_pattern(action) - match = _re.match(nargs_pattern, arg_strings_pattern) - - # raise an exception if we weren't able to find a match - if match is None: - nargs_errors = { - None: _('expected one argument'), - OPTIONAL: _('expected at most one argument'), - ONE_OR_MORE: _('expected at least one argument'), - } - default = _('expected %s argument(s)') % action.nargs - msg = nargs_errors.get(action.nargs, default) - raise ArgumentError(action, msg) - - # return the number of arguments matched - return len(match.group(1)) - - def _match_arguments_partial(self, actions, arg_strings_pattern): - # progressively shorten the actions list by slicing off the - # final actions until we find a match - result = [] - for i in range(len(actions), 0, -1): - actions_slice = actions[:i] - pattern = ''.join([self._get_nargs_pattern(action) - for action in actions_slice]) - match = _re.match(pattern, arg_strings_pattern) - if match is not None: - result.extend([len(string) for string in match.groups()]) - break - - # return the list of arg string counts - return result - - def _parse_optional(self, arg_string): - # if it's an empty string, it was meant to be a positional - if not arg_string: - return None - - # if it doesn't start with a prefix, it was meant to be positional - if not arg_string[0] in self.prefix_chars: - return None - - # if the option string is present in the parser, return the action - if arg_string in self._option_string_actions: - action = self._option_string_actions[arg_string] - return action, arg_string, None - - # if it's just a single character, it was meant to be positional - if len(arg_string) == 1: - return None - - # if the option string before the "=" is present, return the action - if '=' in arg_string: - option_string, explicit_arg = arg_string.split('=', 1) - if option_string in self._option_string_actions: - action = self._option_string_actions[option_string] - return action, option_string, explicit_arg - - # search through all possible prefixes of the option string - # and all actions in the parser for possible interpretations - option_tuples = self._get_option_tuples(arg_string) - - # if multiple actions match, the option string was ambiguous - if len(option_tuples) > 1: - options = ', '.join([option_string - for action, option_string, explicit_arg in option_tuples]) - tup = arg_string, options - self.error(_('ambiguous option: %s could match %s') % tup) - - # if exactly one action matched, this segmentation is good, - # so return the parsed action - elif len(option_tuples) == 1: - option_tuple, = option_tuples - return option_tuple - - # if it was not found as an option, but it looks like a negative - # number, it was meant to be positional - # unless there are negative-number-like options - if self._negative_number_matcher.match(arg_string): - if not self._has_negative_number_optionals: - return None - - # if it contains a space, it was meant to be a positional - if ' ' in arg_string: - return None - - # it was meant to be an optional but there is no such option - # in this parser (though it might be a valid option in a subparser) - return None, arg_string, None - - def _get_option_tuples(self, option_string): - result = [] - - # option strings starting with two prefix characters are only - # split at the '=' - chars = self.prefix_chars - if option_string[0] in chars and option_string[1] in chars: - if '=' in option_string: - option_prefix, explicit_arg = option_string.split('=', 1) - else: - option_prefix = option_string - explicit_arg = None - for option_string in self._option_string_actions: - if option_string.startswith(option_prefix): - action = self._option_string_actions[option_string] - tup = action, option_string, explicit_arg - result.append(tup) - - # single character options can be concatenated with their arguments - # but multiple character options always have to have their argument - # separate - elif option_string[0] in chars and option_string[1] not in chars: - option_prefix = option_string - explicit_arg = None - short_option_prefix = option_string[:2] - short_explicit_arg = option_string[2:] - - for option_string in self._option_string_actions: - if option_string == short_option_prefix: - action = self._option_string_actions[option_string] - tup = action, option_string, short_explicit_arg - result.append(tup) - elif option_string.startswith(option_prefix): - action = self._option_string_actions[option_string] - tup = action, option_string, explicit_arg - result.append(tup) - - # shouldn't ever get here - else: - self.error(_('unexpected option string: %s') % option_string) - - # return the collected option tuples - return result - - def _get_nargs_pattern(self, action): - # in all examples below, we have to allow for '--' args - # which are represented as '-' in the pattern - nargs = action.nargs - - # the default (None) is assumed to be a single argument - if nargs is None: - nargs_pattern = '(-*A-*)' - - # allow zero or one arguments - elif nargs == OPTIONAL: - nargs_pattern = '(-*A?-*)' - - # allow zero or more arguments - elif nargs == ZERO_OR_MORE: - nargs_pattern = '(-*[A-]*)' - - # allow one or more arguments - elif nargs == ONE_OR_MORE: - nargs_pattern = '(-*A[A-]*)' - - # allow any number of options or arguments - elif nargs == REMAINDER: - nargs_pattern = '([-AO]*)' - - # allow one argument followed by any number of options or arguments - elif nargs == PARSER: - nargs_pattern = '(-*A[-AO]*)' - - # all others should be integers - else: - nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) - - # if this is an optional action, -- is not allowed - if action.option_strings: - nargs_pattern = nargs_pattern.replace('-*', '') - nargs_pattern = nargs_pattern.replace('-', '') - - # return the pattern - return nargs_pattern - - # ======================== - # Value conversion methods - # ======================== - def _get_values(self, action, arg_strings): - # for everything but PARSER args, strip out '--' - if action.nargs not in [PARSER, REMAINDER]: - arg_strings = [s for s in arg_strings if s != '--'] - - # optional argument produces a default when not present - if not arg_strings and action.nargs == OPTIONAL: - if action.option_strings: - value = action.const - else: - value = action.default - if isinstance(value, basestring): - value = self._get_value(action, value) - self._check_value(action, value) - - # when nargs='*' on a positional, if there were no command-line - # args, use the default if it is anything other than None - elif (not arg_strings and action.nargs == ZERO_OR_MORE and - not action.option_strings): - if action.default is not None: - value = action.default - else: - value = arg_strings - self._check_value(action, value) - - # single argument or optional argument produces a single value - elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: - arg_string, = arg_strings - value = self._get_value(action, arg_string) - self._check_value(action, value) - - # REMAINDER arguments convert all values, checking none - elif action.nargs == REMAINDER: - value = [self._get_value(action, v) for v in arg_strings] - - # PARSER arguments convert all values, but check only the first - elif action.nargs == PARSER: - value = [self._get_value(action, v) for v in arg_strings] - self._check_value(action, value[0]) - - # all other types of nargs produce a list - else: - value = [self._get_value(action, v) for v in arg_strings] - for v in value: - self._check_value(action, v) - - # return the converted value - return value - - def _get_value(self, action, arg_string): - type_func = self._registry_get('type', action.type, action.type) - if not _callable(type_func): - msg = _('%r is not callable') - raise ArgumentError(action, msg % type_func) - - # convert the value to the appropriate type - try: - result = type_func(arg_string) - - # ArgumentTypeErrors indicate errors - except ArgumentTypeError: - name = getattr(action.type, '__name__', repr(action.type)) - msg = str(_sys.exc_info()[1]) - raise ArgumentError(action, msg) - - # TypeErrors or ValueErrors also indicate errors - except (TypeError, ValueError): - name = getattr(action.type, '__name__', repr(action.type)) - msg = _('invalid %s value: %r') - raise ArgumentError(action, msg % (name, arg_string)) - - # return the converted value - return result - - def _check_value(self, action, value): - # converted value must be one of the choices (if specified) - if action.choices is not None and value not in action.choices: - tup = value, ', '.join(map(repr, action.choices)) - msg = _('invalid choice: %r (choose from %s)') % tup - raise ArgumentError(action, msg) - - # ======================= - # Help-formatting methods - # ======================= - def format_usage(self): - formatter = self._get_formatter() - formatter.add_usage(self.usage, self._actions, - self._mutually_exclusive_groups) - return formatter.format_help() - - def format_help(self): - formatter = self._get_formatter() - - # usage - formatter.add_usage(self.usage, self._actions, - self._mutually_exclusive_groups) - - # description - formatter.add_text(self.description) - - # positionals, optionals and user-defined groups - for action_group in self._action_groups: - formatter.start_section(action_group.title) - formatter.add_text(action_group.description) - formatter.add_arguments(action_group._group_actions) - formatter.end_section() - - # epilog - formatter.add_text(self.epilog) - - # determine help from format above - return formatter.format_help() - - def format_version(self): - import warnings - warnings.warn( - 'The format_version method is deprecated -- the "version" ' - 'argument to ArgumentParser is no longer supported.', - DeprecationWarning) - formatter = self._get_formatter() - formatter.add_text(self.version) - return formatter.format_help() - - def _get_formatter(self): - return self.formatter_class(prog=self.prog) - - # ===================== - # Help-printing methods - # ===================== - def print_usage(self, file=None): - if file is None: - file = _sys.stdout - self._print_message(self.format_usage(), file) - - def print_help(self, file=None): - if file is None: - file = _sys.stdout - self._print_message(self.format_help(), file) - - def print_version(self, file=None): - import warnings - warnings.warn( - 'The print_version method is deprecated -- the "version" ' - 'argument to ArgumentParser is no longer supported.', - DeprecationWarning) - self._print_message(self.format_version(), file) - - def _print_message(self, message, file=None): - if message: - if file is None: - file = _sys.stderr - file.write(message) - - # =============== - # Exiting methods - # =============== - def exit(self, status=0, message=None): - if message: - self._print_message(message, _sys.stderr) - _sys.exit(status) - - def error(self, message): - """error(message: string) - - Prints a usage message incorporating the message to stderr and - exits. - - If you override this in a subclass, it should not return -- it - should either exit or raise an exception. - """ - self.print_usage(_sys.stderr) - self.exit(2, _('%s: error: %s\n') % (self.prog, message)) From c7883412440905b41dde7e70f4af782932e80e90 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 14:52:52 -0300 Subject: [PATCH 1112/2380] python: Remove scripts/ordereddict.py Python 2.7 (the minimum Python version we require) provides collections.OrderedDict on the standard library, so we don't need to carry our own implementation. Signed-off-by: Eduardo Habkost Message-Id: <20180608175252.25110-3-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- Makefile | 1 - scripts/ordereddict.py | 128 ----------------------------------------- scripts/qapi/common.py | 5 +- tests/Makefile.include | 1 - 4 files changed, 1 insertion(+), 134 deletions(-) delete mode 100644 scripts/ordereddict.py diff --git a/Makefile b/Makefile index 023b3437ec..6c6664d9a3 100644 --- a/Makefile +++ b/Makefile @@ -567,7 +567,6 @@ $(SRC_PATH)/scripts/qapi/types.py \ $(SRC_PATH)/scripts/qapi/visit.py \ $(SRC_PATH)/scripts/qapi/common.py \ $(SRC_PATH)/scripts/qapi/doc.py \ -$(SRC_PATH)/scripts/ordereddict.py \ $(SRC_PATH)/scripts/qapi-gen.py qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ diff --git a/scripts/ordereddict.py b/scripts/ordereddict.py deleted file mode 100644 index 68ed340b33..0000000000 --- a/scripts/ordereddict.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) 2009 Raymond Hettinger -# -# 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. - -from UserDict import DictMixin - - -class OrderedDict(dict, DictMixin): - - def __init__(self, *args, **kwds): - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__end - except AttributeError: - self.clear() - self.update(*args, **kwds) - - def clear(self): - self.__end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.__map = {} # key --> [key, prev, next] - dict.clear(self) - - def __setitem__(self, key, value): - if key not in self: - end = self.__end - curr = end[1] - curr[2] = end[1] = self.__map[key] = [key, curr, end] - dict.__setitem__(self, key, value) - - def __delitem__(self, key): - dict.__delitem__(self, key) - key, prev, next = self.__map.pop(key) - prev[2] = next - next[1] = prev - - def __iter__(self): - end = self.__end - curr = end[2] - while curr is not end: - yield curr[0] - curr = curr[2] - - def __reversed__(self): - end = self.__end - curr = end[1] - while curr is not end: - yield curr[0] - curr = curr[1] - - def popitem(self, last=True): - if not self: - raise KeyError('dictionary is empty') - if last: - key = next(reversed(self)) - else: - key = next(iter(self)) - value = self.pop(key) - return key, value - - def __reduce__(self): - items = [[k, self[k]] for k in self] - tmp = self.__map, self.__end - del self.__map, self.__end - inst_dict = vars(self).copy() - self.__map, self.__end = tmp - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def keys(self): - return list(self) - - setdefault = DictMixin.setdefault - update = DictMixin.update - pop = DictMixin.pop - values = DictMixin.values - items = DictMixin.items - iterkeys = DictMixin.iterkeys - itervalues = DictMixin.itervalues - iteritems = DictMixin.iteritems - - def __repr__(self): - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - - def copy(self): - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - if isinstance(other, OrderedDict): - if len(self) != len(other): - return False - for p, q in zip(self.items(), other.items()): - if p != q: - return False - return True - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index e82990f0f2..2462fc0291 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -16,10 +16,7 @@ import errno import os import re import string -try: - from collections import OrderedDict -except: - from ordereddict import OrderedDict +from collections import OrderedDict builtin_types = { 'null': 'QTYPE_QNULL', diff --git a/tests/Makefile.include b/tests/Makefile.include index 400d8890e7..ccedacebd5 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -31,7 +31,6 @@ $(SRC_PATH)/scripts/qapi/types.py \ $(SRC_PATH)/scripts/qapi/visit.py \ $(SRC_PATH)/scripts/qapi/common.py \ $(SRC_PATH)/scripts/qapi/doc.py \ -$(SRC_PATH)/scripts/ordereddict.py \ $(SRC_PATH)/scripts/qapi-gen.py # Get the list of all supported sysemu targets From 1fb6752ecf87fb132035d3c03b0b9ca012265492 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 8 Jun 2018 16:29:40 -0300 Subject: [PATCH 1113/2380] pc: Remove PC_COMPAT_2_12 from 3.0 machine-types commit f548222c added PC_COMPAT_2_12 to the 3.0 PC machine-types. I believe this happened during manual conflict resolution when applying the patch. Cc: Xiao Guangrong Cc: Juan Quintela Fixes: f548222c24342ca74689de7794f9006b43f86a54 Signed-off-by: Eduardo Habkost Message-Id: <20180608192940.19548-1-ehabkost@redhat.com> Reviewed-by: Marcel Apfelbaum Signed-off-by: Eduardo Habkost --- hw/i386/pc_piix.c | 1 - hw/i386/pc_q35.c | 1 - 2 files changed, 2 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 3d81136065..b4c5b03274 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -430,7 +430,6 @@ static void pc_i440fx_3_0_machine_options(MachineClass *m) pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); } DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", NULL, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b60cbb9266..83d6d75efa 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -312,7 +312,6 @@ static void pc_q35_3_0_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); } DEFINE_Q35_MACHINE(v3_0, "pc-q35-3.0", NULL, From cb4add334a5a8db263c20c33c5365be3868f8967 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 22:02:42 -0700 Subject: [PATCH 1114/2380] target/m68k: Use DISAS_NORETURN for exceptions The raise_exception helper does not return. Do not generate any code following that. Signed-off-by: Richard Henderson Message-Id: <20180512050250.12774-2-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- target/m68k/translate.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 4b5dbdb51c..de1be58f65 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -291,18 +291,18 @@ static void gen_jmp(DisasContext *s, TCGv dest) s->is_jmp = DISAS_JUMP; } -static void gen_raise_exception(int nr) +static void gen_exception(DisasContext *s, uint32_t dest, int nr) { - TCGv_i32 tmp = tcg_const_i32(nr); + TCGv_i32 tmp; + update_cc_op(s); + tcg_gen_movi_i32(QREG_PC, dest); + + tmp = tcg_const_i32(nr); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); -} -static void gen_exception(DisasContext *s, uint32_t where, int nr) -{ - gen_jmp_im(s, where); - gen_raise_exception(nr); + s->is_jmp = DISAS_NORETURN; } static inline void gen_addr_fault(DisasContext *s) @@ -6106,7 +6106,6 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { gen_exception(dc, dc->pc, EXCP_DEBUG); - dc->is_jmp = DISAS_JUMP; /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that @@ -6150,6 +6149,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: + case DISAS_NORETURN: /* nothing more to generate */ break; } From 825340f5659647deb62743c3cb479ec8d78f1862 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 22:02:43 -0700 Subject: [PATCH 1115/2380] target/m68k: Replace DISAS_TB_JUMP with DISAS_NORETURN We have exited the TB after using goto_tb; there is no distinction from DISAS_NORETURN. Signed-off-by: Richard Henderson Message-Id: <20180512050250.12774-3-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- target/m68k/translate.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index de1be58f65..bfa30cde0a 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -199,7 +199,6 @@ static void do_writebacks(DisasContext *s) /* is_jmp field values */ #define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ -#define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ #define DISAS_JUMP_NEXT DISAS_TARGET_3 #if defined(CONFIG_USER_ONLY) @@ -1496,7 +1495,7 @@ static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest) gen_jmp_im(s, dest); tcg_gen_exit_tb(NULL, 0); } - s->is_jmp = DISAS_TB_JUMP; + s->is_jmp = DISAS_NORETURN; } DISAS_INSN(scc) @@ -6148,7 +6147,6 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) /* indicate that the hash table must be used to find the next TB */ tcg_gen_exit_tb(NULL, 0); break; - case DISAS_TB_JUMP: case DISAS_NORETURN: /* nothing more to generate */ break; From 707ddb5ac6f91309ff6ed6bec988ed8100846f50 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 22:02:44 -0700 Subject: [PATCH 1116/2380] target/m68k: Remove DISAS_JUMP_NEXT as unused Signed-off-by: Richard Henderson Message-Id: <20180512050250.12774-4-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- target/m68k/translate.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index bfa30cde0a..6238d9edc9 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -199,7 +199,6 @@ static void do_writebacks(DisasContext *s) /* is_jmp field values */ #define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ -#define DISAS_JUMP_NEXT DISAS_TARGET_3 #if defined(CONFIG_USER_ONLY) #define IS_USER(s) 1 From 8aaf7da9c3b1f282b5a123de3e87a2e6ca87f3b9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 22:02:45 -0700 Subject: [PATCH 1117/2380] target/m68k: Use lookup_and_goto_tb for DISAS_JUMP These are all indirect or out-of-page direct jumps. We can indirectly chain to the next TB without going back to the main loop. Signed-off-by: Richard Henderson Message-Id: <20180512050250.12774-5-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- target/m68k/translate.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 6238d9edc9..4b92a20c05 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -6139,8 +6139,11 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) update_cc_op(dc); gen_jmp_tb(dc, 0, dc->pc); break; - default: case DISAS_JUMP: + /* We updated CC_OP and PC in gen_jmp/gen_jmp_im. */ + tcg_gen_lookup_and_goto_ptr(); + break; + default: case DISAS_UPDATE: update_cc_op(dc); /* indicate that the hash table must be used to find the next TB */ From 4106f26e95c83b8759c3fe61a4d3a1fa740db0a9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 22:02:46 -0700 Subject: [PATCH 1118/2380] target/m68k: Rename DISAS_UPDATE and gen_lookup_tb The name gen_lookup_tb is at odds with tcg_gen_lookup_and_goto_tb. For these cases, we do indeed want to exit back to the main loop. Similarly, DISAS_UPDATE performs no actual update, whereas DISAS_EXIT does what it says. Signed-off-by: Richard Henderson Message-Id: <20180512050250.12774-6-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- target/m68k/translate.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 4b92a20c05..0cbd715550 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -198,7 +198,7 @@ static void do_writebacks(DisasContext *s) /* is_jmp field values */ #define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ -#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ +#define DISAS_EXIT DISAS_TARGET_1 /* cpu state was modified dynamically */ #if defined(CONFIG_USER_ONLY) #define IS_USER(s) 1 @@ -1446,11 +1446,11 @@ static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1) } /* Force a TB lookup after an instruction that changes the CPU state. */ -static void gen_lookup_tb(DisasContext *s) +static void gen_exit_tb(DisasContext *s) { update_cc_op(s); tcg_gen_movi_i32(QREG_PC, s->pc); - s->is_jmp = DISAS_UPDATE; + s->is_jmp = DISAS_EXIT; } #define SRC_EA(env, result, opsize, op_sign, addrp) do { \ @@ -4604,7 +4604,7 @@ DISAS_INSN(move_to_sr) return; } gen_move_to_sr(env, s, insn, false); - gen_lookup_tb(s); + gen_exit_tb(s); } DISAS_INSN(move_from_usp) @@ -4680,7 +4680,7 @@ DISAS_INSN(cf_movec) reg = DREG(ext, 12); } gen_helper_cf_movec_to(cpu_env, tcg_const_i32(ext & 0xfff), reg); - gen_lookup_tb(s); + gen_exit_tb(s); } DISAS_INSN(m68k_movec) @@ -4705,7 +4705,7 @@ DISAS_INSN(m68k_movec) } else { gen_helper_m68k_movec_from(reg, cpu_env, tcg_const_i32(ext & 0xfff)); } - gen_lookup_tb(s); + gen_exit_tb(s); } DISAS_INSN(intouch) @@ -5749,7 +5749,7 @@ DISAS_INSN(to_macsr) TCGv val; SRC_EA(env, val, OS_LONG, 0, NULL); gen_helper_set_macsr(cpu_env, val); - gen_lookup_tb(s); + gen_exit_tb(s); } DISAS_INSN(to_mask) @@ -6144,9 +6144,9 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) tcg_gen_lookup_and_goto_ptr(); break; default: - case DISAS_UPDATE: - update_cc_op(dc); - /* indicate that the hash table must be used to find the next TB */ + case DISAS_EXIT: + /* We updated CC_OP and PC in gen_exit_tb, but also modified + other state that may require returning to the main loop. */ tcg_gen_exit_tb(NULL, 0); break; case DISAS_NORETURN: From a575cbe01caecf22ab322a9baa5930a6d9e39ca6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 22:02:47 -0700 Subject: [PATCH 1119/2380] target/m68k: Convert to DisasContextBase Removed ctx->insn_pc in favour of ctx->base.pc_next. Yes, it is annoying, but didn't want to waste its 4 bytes. Signed-off-by: Richard Henderson Message-Id: <20180512050250.12774-7-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- target/m68k/translate.c | 137 ++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 70 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 0cbd715550..12a74b0b59 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -111,14 +111,11 @@ void m68k_tcg_init(void) /* internal defines */ typedef struct DisasContext { + DisasContextBase base; CPUM68KState *env; - target_ulong insn_pc; /* Start of the current instruction. */ target_ulong pc; - int is_jmp; CCOp cc_op; /* Current CC operation */ int cc_op_synced; - struct TranslationBlock *tb; - int singlestep_enabled; TCGv_i64 mactmp; int done_mac; int writeback_mask; @@ -203,10 +200,10 @@ static void do_writebacks(DisasContext *s) #if defined(CONFIG_USER_ONLY) #define IS_USER(s) 1 #else -#define IS_USER(s) (!(s->tb->flags & TB_FLAGS_MSR_S)) -#define SFC_INDEX(s) ((s->tb->flags & TB_FLAGS_SFC_S) ? \ +#define IS_USER(s) (!(s->base.tb->flags & TB_FLAGS_MSR_S)) +#define SFC_INDEX(s) ((s->base.tb->flags & TB_FLAGS_SFC_S) ? \ MMU_KERNEL_IDX : MMU_USER_IDX) -#define DFC_INDEX(s) ((s->tb->flags & TB_FLAGS_DFC_S) ? \ +#define DFC_INDEX(s) ((s->base.tb->flags & TB_FLAGS_DFC_S) ? \ MMU_KERNEL_IDX : MMU_USER_IDX) #endif @@ -278,7 +275,7 @@ static void gen_jmp_im(DisasContext *s, uint32_t dest) { update_cc_op(s); tcg_gen_movi_i32(QREG_PC, dest); - s->is_jmp = DISAS_JUMP; + s->base.is_jmp = DISAS_JUMP; } /* Generate a jump to the address in qreg DEST. */ @@ -286,7 +283,7 @@ static void gen_jmp(DisasContext *s, TCGv dest) { update_cc_op(s); tcg_gen_mov_i32(QREG_PC, dest); - s->is_jmp = DISAS_JUMP; + s->base.is_jmp = DISAS_JUMP; } static void gen_exception(DisasContext *s, uint32_t dest, int nr) @@ -300,12 +297,12 @@ static void gen_exception(DisasContext *s, uint32_t dest, int nr) gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); - s->is_jmp = DISAS_NORETURN; + s->base.is_jmp = DISAS_NORETURN; } static inline void gen_addr_fault(DisasContext *s) { - gen_exception(s, s->insn_pc, EXCP_ADDRESS); + gen_exception(s, s->base.pc_next, EXCP_ADDRESS); } /* Generate a load from the specified address. Narrow values are @@ -1003,7 +1000,7 @@ static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; } tcg_gen_qemu_ld32u(tmp, addr, index); @@ -1017,7 +1014,7 @@ static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, /* unimplemented data type on 68040/ColdFire * FIXME if needed for another FPU */ - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; default: g_assert_not_reached(); @@ -1057,7 +1054,7 @@ static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; } tcg_gen_ld16u_i32(tmp, fp, offsetof(FPReg, l.upper)); @@ -1071,7 +1068,7 @@ static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, /* unimplemented data type on 68040/ColdFire * FIXME if needed for another FPU */ - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; default: g_assert_not_reached(); @@ -1203,7 +1200,7 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode, break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; } tmp = tcg_const_i32(read_im32(env, s) >> 16); @@ -1217,7 +1214,7 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode, /* unimplemented data type on 68040/ColdFire * FIXME if needed for another FPU */ - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; default: g_assert_not_reached(); @@ -1450,7 +1447,7 @@ static void gen_exit_tb(DisasContext *s) { update_cc_op(s); tcg_gen_movi_i32(QREG_PC, s->pc); - s->is_jmp = DISAS_EXIT; + s->base.is_jmp = DISAS_EXIT; } #define SRC_EA(env, result, opsize, op_sign, addrp) do { \ @@ -1474,8 +1471,8 @@ static void gen_exit_tb(DisasContext *s) static inline bool use_goto_tb(DisasContext *s, uint32_t dest) { #ifndef CONFIG_USER_ONLY - return (s->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) || - (s->insn_pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); + return (s->base.pc_first & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) + || (s->base.pc_next & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); #else return true; #endif @@ -1484,17 +1481,17 @@ static inline bool use_goto_tb(DisasContext *s, uint32_t dest) /* Generate a jump to an immediate address. */ static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest) { - if (unlikely(s->singlestep_enabled)) { + if (unlikely(s->base.singlestep_enabled)) { gen_exception(s, dest, EXCP_DEBUG); } else if (use_goto_tb(s, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(QREG_PC, dest); - tcg_gen_exit_tb(s->tb, n); + tcg_gen_exit_tb(s->base.tb, n); } else { gen_jmp_im(s, dest); tcg_gen_exit_tb(NULL, 0); } - s->is_jmp = DISAS_NORETURN; + s->base.is_jmp = DISAS_NORETURN; } DISAS_INSN(scc) @@ -1541,12 +1538,12 @@ DISAS_INSN(dbcc) DISAS_INSN(undef_mac) { - gen_exception(s, s->insn_pc, EXCP_LINEA); + gen_exception(s, s->base.pc_next, EXCP_LINEA); } DISAS_INSN(undef_fpu) { - gen_exception(s, s->insn_pc, EXCP_LINEF); + gen_exception(s, s->base.pc_next, EXCP_LINEF); } DISAS_INSN(undef) @@ -1555,8 +1552,8 @@ DISAS_INSN(undef) for the 680x0 series, as well as those that are implemented but actually illegal for CPU32 or pre-68020. */ qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %08x\n", - insn, s->insn_pc); - gen_exception(s, s->insn_pc, EXCP_UNSUPPORTED); + insn, s->base.pc_next); + gen_exception(s, s->base.pc_next, EXCP_UNSUPPORTED); } DISAS_INSN(mulw) @@ -1616,7 +1613,7 @@ DISAS_INSN(divl) if (ext & 0x400) { if (!m68k_feature(s->env, M68K_FEATURE_QUAD_MULDIV)) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } @@ -2310,7 +2307,7 @@ DISAS_INSN(arith_im) break; case OS_WORD: if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } src1 = gen_get_sr(s); @@ -2479,7 +2476,7 @@ DISAS_INSN(cas2w) (REG(ext1, 6) << 3) | (REG(ext2, 0) << 6) | (REG(ext1, 0) << 9)); - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_exit_atomic(cpu_env); } else { gen_helper_cas2w(cpu_env, regs, addr1, addr2); @@ -2529,7 +2526,7 @@ DISAS_INSN(cas2l) (REG(ext1, 6) << 3) | (REG(ext2, 0) << 6) | (REG(ext1, 0) << 9)); - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_cas2l_parallel(cpu_env, regs, addr1, addr2); } else { gen_helper_cas2l(cpu_env, regs, addr1, addr2); @@ -2720,7 +2717,7 @@ DISAS_INSN(swap) DISAS_INSN(bkpt) { - gen_exception(s, s->insn_pc, EXCP_DEBUG); + gen_exception(s, s->base.pc_next, EXCP_DEBUG); } DISAS_INSN(pea) @@ -2773,7 +2770,7 @@ DISAS_INSN(pulse) DISAS_INSN(illegal) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); } /* ??? This should be atomic. */ @@ -2803,7 +2800,7 @@ DISAS_INSN(mull) if (ext & 0x400) { if (!m68k_feature(s->env, M68K_FEATURE_QUAD_MULDIV)) { - gen_exception(s, s->insn_pc, EXCP_UNSUPPORTED); + gen_exception(s, s->base.pc_next, EXCP_UNSUPPORTED); return; } @@ -2904,7 +2901,7 @@ DISAS_INSN(unlk) DISAS_INSN(reset) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4375,7 +4372,7 @@ DISAS_INSN(chk) } /* fallthru */ default: - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } SRC_EA(env, src, opsize, 1, NULL); @@ -4402,13 +4399,13 @@ DISAS_INSN(chk2) opsize = OS_LONG; break; default: - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } ext = read_im16(env, s); if ((ext & 0x0800) == 0) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } @@ -4468,7 +4465,7 @@ DISAS_INSN(move16_reg) ext = read_im16(env, s); if ((ext & (1 << 15)) == 0) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); } m68k_copy_line(AREG(ext, 12), AREG(insn, 0), index); @@ -4530,7 +4527,7 @@ DISAS_INSN(move_from_sr) TCGv sr; if (IS_USER(s) && !m68k_feature(env, M68K_FEATURE_M68000)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } sr = gen_get_sr(s); @@ -4547,7 +4544,7 @@ DISAS_INSN(moves) int extend; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4600,7 +4597,7 @@ DISAS_INSN(moves) DISAS_INSN(move_to_sr) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } gen_move_to_sr(env, s, insn, false); @@ -4610,7 +4607,7 @@ DISAS_INSN(move_to_sr) DISAS_INSN(move_from_usp) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } tcg_gen_ld_i32(AREG(insn, 0), cpu_env, @@ -4620,7 +4617,7 @@ DISAS_INSN(move_from_usp) DISAS_INSN(move_to_usp) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } tcg_gen_st_i32(AREG(insn, 0), cpu_env, @@ -4630,7 +4627,7 @@ DISAS_INSN(move_to_usp) DISAS_INSN(halt) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4642,7 +4639,7 @@ DISAS_INSN(stop) uint16_t ext; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4656,10 +4653,10 @@ DISAS_INSN(stop) DISAS_INSN(rte) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } - gen_exception(s, s->insn_pc, EXCP_RTE); + gen_exception(s, s->base.pc_next, EXCP_RTE); } DISAS_INSN(cf_movec) @@ -4668,7 +4665,7 @@ DISAS_INSN(cf_movec) TCGv reg; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4689,7 +4686,7 @@ DISAS_INSN(m68k_movec) TCGv reg; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4711,7 +4708,7 @@ DISAS_INSN(m68k_movec) DISAS_INSN(intouch) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } /* ICache fetch. Implement as no-op. */ @@ -4720,7 +4717,7 @@ DISAS_INSN(intouch) DISAS_INSN(cpushl) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } /* Cache push/invalidate. Implement as no-op. */ @@ -4729,7 +4726,7 @@ DISAS_INSN(cpushl) DISAS_INSN(cpush) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } /* Cache push/invalidate. Implement as no-op. */ @@ -4738,7 +4735,7 @@ DISAS_INSN(cpush) DISAS_INSN(cinv) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } /* Invalidate cache line. Implement as no-op. */ @@ -4750,7 +4747,7 @@ DISAS_INSN(pflush) TCGv opmode; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4764,7 +4761,7 @@ DISAS_INSN(ptest) TCGv is_read; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } is_read = tcg_const_i32((insn >> 5) & 1); @@ -4775,7 +4772,7 @@ DISAS_INSN(ptest) DISAS_INSN(wddata) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); } DISAS_INSN(wdebug) @@ -4783,7 +4780,7 @@ DISAS_INSN(wdebug) M68kCPU *cpu = m68k_env_get_cpu(env); if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } /* TODO: Implement wdebug. */ @@ -4793,7 +4790,7 @@ DISAS_INSN(wdebug) DISAS_INSN(trap) { - gen_exception(s, s->insn_pc, EXCP_TRAP0 + (insn & 0xf)); + gen_exception(s, s->base.pc_next, EXCP_TRAP0 + (insn & 0xf)); } static void gen_load_fcr(DisasContext *s, TCGv res, int reg) @@ -4860,7 +4857,7 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s, switch (mode) { case 0: /* Dn */ if (mask != M68K_FPIAR && mask != M68K_FPSR && mask != M68K_FPCR) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } if (is_write) { @@ -4871,7 +4868,7 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s, return; case 1: /* An, only with FPIAR */ if (mask != M68K_FPIAR) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } if (is_write) { @@ -5429,7 +5426,7 @@ DISAS_INSN(frestore) TCGv addr; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } if (m68k_feature(s->env, M68K_FEATURE_M68040)) { @@ -5443,7 +5440,7 @@ DISAS_INSN(frestore) DISAS_INSN(fsave) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -6075,14 +6072,14 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) /* generate intermediate code */ pc_start = tb->pc; - dc->tb = tb; + dc->base.tb = tb; dc->env = env; - dc->is_jmp = DISAS_NEXT; + dc->base.is_jmp = DISAS_NEXT; dc->pc = pc_start; dc->cc_op = CC_OP_DYNAMIC; dc->cc_op_synced = 1; - dc->singlestep_enabled = cs->singlestep_enabled; + dc->base.singlestep_enabled = cs->singlestep_enabled; dc->done_mac = 0; dc->writeback_mask = 0; num_insns = 0; @@ -6116,9 +6113,9 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) gen_io_start(); } - dc->insn_pc = dc->pc; + dc->base.pc_next = dc->pc; disas_m68k_insn(env, dc); - } while (!dc->is_jmp && !tcg_op_buf_full() && + } while (!dc->base.is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && (pc_offset) < (TARGET_PAGE_SIZE - 32) && @@ -6128,13 +6125,13 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) gen_io_end(); if (unlikely(cs->singlestep_enabled)) { /* Make sure the pc is updated, and raise a debug exception. */ - if (!dc->is_jmp) { + if (!dc->base.is_jmp) { update_cc_op(dc); tcg_gen_movi_i32(QREG_PC, dc->pc); } gen_helper_raise_exception(cpu_env, tcg_const_i32(EXCP_DEBUG)); } else { - switch(dc->is_jmp) { + switch (dc->base.is_jmp) { case DISAS_NEXT: update_cc_op(dc); gen_jmp_tb(dc, 0, dc->pc); From 11ab74b01e0a8ea4973eed89c6b90fa6e4fb9fb6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 22:02:48 -0700 Subject: [PATCH 1120/2380] target/m68k: Convert to TranslatorOps Signed-off-by: Richard Henderson Message-Id: <20180512050250.12774-8-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- target/m68k/translate.c | 176 ++++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 90 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 12a74b0b59..62a96bedcd 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -6059,113 +6059,109 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) do_release(s); } -/* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +static void m68k_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { - CPUM68KState *env = cs->env_ptr; - DisasContext dc1, *dc = &dc1; - target_ulong pc_start; - int pc_offset; - int num_insns; - int max_insns; - - /* generate intermediate code */ - pc_start = tb->pc; - - dc->base.tb = tb; + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUM68KState *env = cpu->env_ptr; dc->env = env; - dc->base.is_jmp = DISAS_NEXT; - dc->pc = pc_start; + dc->pc = dc->base.pc_first; dc->cc_op = CC_OP_DYNAMIC; dc->cc_op_synced = 1; - dc->base.singlestep_enabled = cs->singlestep_enabled; dc->done_mac = 0; dc->writeback_mask = 0; - num_insns = 0; - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; - } - init_release_array(dc); +} - gen_tb_start(tb); - do { - pc_offset = dc->pc - pc_start; - tcg_gen_insn_start(dc->pc, dc->cc_op); - num_insns++; +static void m68k_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) +{ +} - if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { - gen_exception(dc, dc->pc, EXCP_DEBUG); - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - dc->pc += 2; - break; - } +static void m68k_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + tcg_gen_insn_start(dc->base.pc_next, dc->cc_op); +} - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } +static bool m68k_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); - dc->base.pc_next = dc->pc; - disas_m68k_insn(env, dc); - } while (!dc->base.is_jmp && !tcg_op_buf_full() && - !cs->singlestep_enabled && - !singlestep && - (pc_offset) < (TARGET_PAGE_SIZE - 32) && - num_insns < max_insns); + gen_exception(dc, dc->base.pc_next, EXCP_DEBUG); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->base.pc_next += 2; - if (tb_cflags(tb) & CF_LAST_IO) - gen_io_end(); - if (unlikely(cs->singlestep_enabled)) { - /* Make sure the pc is updated, and raise a debug exception. */ - if (!dc->base.is_jmp) { - update_cc_op(dc); - tcg_gen_movi_i32(QREG_PC, dc->pc); - } + return true; +} + +static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + disas_m68k_insn(cpu->env_ptr, dc); + dc->base.pc_next = dc->pc; + + if (dc->base.is_jmp == DISAS_NEXT + && dc->pc - dc->base.pc_first >= TARGET_PAGE_SIZE - 32) { + dc->base.is_jmp = DISAS_TOO_MANY; + } +} + +static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + if (dc->base.is_jmp == DISAS_NORETURN) { + return; + } + if (dc->base.singlestep_enabled) { gen_helper_raise_exception(cpu_env, tcg_const_i32(EXCP_DEBUG)); - } else { - switch (dc->base.is_jmp) { - case DISAS_NEXT: - update_cc_op(dc); - gen_jmp_tb(dc, 0, dc->pc); - break; - case DISAS_JUMP: - /* We updated CC_OP and PC in gen_jmp/gen_jmp_im. */ - tcg_gen_lookup_and_goto_ptr(); - break; - default: - case DISAS_EXIT: - /* We updated CC_OP and PC in gen_exit_tb, but also modified - other state that may require returning to the main loop. */ - tcg_gen_exit_tb(NULL, 0); - break; - case DISAS_NORETURN: - /* nothing more to generate */ - break; - } + return; } - gen_tb_end(tb, num_insns); -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - qemu_log("----------------\n"); - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc->pc - pc_start); - qemu_log("\n"); - qemu_log_unlock(); + switch (dc->base.is_jmp) { + case DISAS_TOO_MANY: + update_cc_op(dc); + gen_jmp_tb(dc, 0, dc->pc); + break; + case DISAS_JUMP: + /* We updated CC_OP and PC in gen_jmp/gen_jmp_im. */ + tcg_gen_lookup_and_goto_ptr(); + break; + case DISAS_EXIT: + /* We updated CC_OP and PC in gen_exit_tb, but also modified + other state that may require returning to the main loop. */ + tcg_gen_exit_tb(NULL, 0); + break; + default: + g_assert_not_reached(); } -#endif - tb->size = dc->pc - pc_start; - tb->icount = num_insns; +} + +static void m68k_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); +} + +static const TranslatorOps m68k_tr_ops = { + .init_disas_context = m68k_tr_init_disas_context, + .tb_start = m68k_tr_tb_start, + .insn_start = m68k_tr_insn_start, + .breakpoint_check = m68k_tr_breakpoint_check, + .translate_insn = m68k_tr_translate_insn, + .tb_stop = m68k_tr_tb_stop, + .disas_log = m68k_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb) +{ + DisasContext dc; + translator_loop(&m68k_tr_ops, &dc.base, cpu, tb); } static double floatx80_to_double(CPUM68KState *env, uint16_t high, uint64_t low) From 4c7a0f6f34869b3dfe7091d28ff27a8dfbdd8b70 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 22:02:49 -0700 Subject: [PATCH 1121/2380] target/m68k: Improve ending TB at page boundaries Rather than limit total TB size to PAGE-32 bytes, end the TB when near the end of a page. This should provide proper semantics of SIGSEGV when executing near the end of a page. Signed-off-by: Richard Henderson Message-Id: <20180512050250.12774-9-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- target/m68k/translate.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 62a96bedcd..ff3493d8ab 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -6105,9 +6105,25 @@ static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) disas_m68k_insn(cpu->env_ptr, dc); dc->base.pc_next = dc->pc; - if (dc->base.is_jmp == DISAS_NEXT - && dc->pc - dc->base.pc_first >= TARGET_PAGE_SIZE - 32) { - dc->base.is_jmp = DISAS_TOO_MANY; + if (dc->base.is_jmp == DISAS_NEXT) { + /* Stop translation when the next insn might touch a new page. + * This ensures that prefetch aborts at the right place. + * + * We cannot determine the size of the next insn without + * completely decoding it. However, the maximum insn size + * is 32 bytes, so end if we do not have that much remaining. + * This may produce several small TBs at the end of each page, + * but they will all be linked with goto_tb. + * + * ??? ColdFire maximum is 4 bytes; MC68000's maximum is also + * smaller than MC68020's. + */ + target_ulong start_page_offset + = dc->pc - (dc->base.pc_first & TARGET_PAGE_MASK); + + if (start_page_offset >= TARGET_PAGE_SIZE - 32) { + dc->base.is_jmp = DISAS_TOO_MANY; + } } } From a56f36c1d2bccbc50a53fa8093b93d205607f1b8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 11 May 2018 22:02:50 -0700 Subject: [PATCH 1122/2380] target/m68k: Merge disas_m68k_insn into m68k_tr_translate_insn Signed-off-by: Richard Henderson Message-Id: <20180512050250.12774-10-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- target/m68k/translate.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index ff3493d8ab..ae3651b867 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -6049,16 +6049,6 @@ void register_m68k_insns (CPUM68KState *env) #undef INSN } -/* ??? Some of this implementation is not exception safe. We should always - write back the result to memory before setting the condition codes. */ -static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) -{ - uint16_t insn = read_im16(env, s); - opcode_table[insn](env, s, insn); - do_writebacks(s); - do_release(s); -} - static void m68k_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); @@ -6101,8 +6091,13 @@ static bool m68k_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUM68KState *env = cpu->env_ptr; + uint16_t insn = read_im16(env, dc); + + opcode_table[insn](env, dc, insn); + do_writebacks(dc); + do_release(dc); - disas_m68k_insn(cpu->env_ptr, dc); dc->base.pc_next = dc->pc; if (dc->base.is_jmp == DISAS_NEXT) { From 1129dd712157b42397275681e6397bf2d6bdfa0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Jun 2018 12:37:21 -0300 Subject: [PATCH 1123/2380] linux-user: Export use is_error(), use it to avoid warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: linux-user/flatload.c:740:9: warning: Loss of sign in implicit conversion if (res > (unsigned long)-4096) ^~~ Reported-by: Clang Static Analyzer Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20180604153722.24956-2-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/qemu.h | 5 +++++ linux-user/syscall.c | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 6fa1e968db..793cd4df04 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -618,6 +618,11 @@ static inline void *lock_user_string(abi_ulong guest_addr) #include +static inline int is_error(abi_long ret) +{ + return (abi_ulong)ret >= (abi_ulong)(-4096); +} + /* Include target-specific struct and function definitions; * they may need access to the target-independent structures * above, so include them last. diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 7b9ac3b408..2117fb13b4 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -906,11 +906,6 @@ static inline abi_long get_errno(abi_long ret) return ret; } -static inline int is_error(abi_long ret) -{ - return (abi_ulong)ret >= (abi_ulong)(-4096); -} - const char *target_strerror(int err) { if (err == TARGET_ERESTARTSYS) { From 71987b125ba49314b631f81a4f221e010fa9be48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Jun 2018 12:37:22 -0300 Subject: [PATCH 1124/2380] linux-user: Use is_error() to avoid warnings and make the code clearer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: linux-user/flatload.c:740:9: warning: Loss of sign in implicit conversion if (res > (unsigned long)-4096) ^~~ Reported-by: Clang Static Analyzer Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20180604153722.24956-3-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/flatload.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/linux-user/flatload.c b/linux-user/flatload.c index a35a560904..10c529910f 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -224,8 +224,9 @@ static int decompress_exec( ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos); if (ret <= 0) break; - if (ret >= (unsigned long) -4096) + if (is_error(ret)) { break; + } len -= ret; strm.next_in = buf; @@ -283,8 +284,7 @@ calc_reloc(abi_ulong r, struct lib_info *p, int curid, int internalp) "in same module (%d != %d)\n", (unsigned) r, curid, id); goto failed; - } else if ( ! p[id].loaded && - load_flat_shared_library(id, p) > (unsigned long) -4096) { + } else if (!p[id].loaded && is_error(load_flat_shared_library(id, p))) { fprintf(stderr, "BINFMT_FLAT: failed to load library %d\n", id); goto failed; } @@ -523,9 +523,10 @@ static int load_flat_file(struct linux_binprm * bprm, fpos = 0; result = bprm->file->f_op->read(bprm->file, (char *) textpos, text_len, &fpos); - if (result < (unsigned long) -4096) + if (!is_error(result)) { result = decompress_exec(bprm, text_len, (char *) datapos, data_len + (relocs * sizeof(unsigned long)), 0); + } } else #endif @@ -693,8 +694,9 @@ static int load_flat_shared_library(int id, struct lib_info *libs) res = prepare_binprm(&bprm); - if (res <= (unsigned long)-4096) + if (!is_error(res)) { res = load_flat_file(&bprm, libs, id, NULL); + } if (bprm.file) { allow_write_access(bprm.file); fput(bprm.file); @@ -737,8 +739,9 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) res = load_flat_file(bprm, libinfo, 0, &stack_len); - if (res > (unsigned long)-4096) + if (is_error(res)) { return res; + } /* Update data segment pointers for all libraries */ for (i=0; i Date: Tue, 5 Jun 2018 18:09:58 +0200 Subject: [PATCH 1125/2380] linux-user: disable qemu-bridge-helper and socket_scm_helper build linux-user targets don't need them, and if we ask to build statically linked binaries, some static libraries they need are not available. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Message-Id: <20180605160958.5434-1-laurent@vivier.eu> --- Makefile | 2 +- tests/Makefile.include | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 023b3437ec..e4bc34a1cb 100644 --- a/Makefile +++ b/Makefile @@ -351,7 +351,7 @@ $(call set-vpath, $(SRC_PATH)) LIBS+=-lz $(LIBS_TOOLS) -HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) +HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF) ifdef BUILD_DOCS DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 diff --git a/tests/Makefile.include b/tests/Makefile.include index d098a104bb..10397ed159 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -930,7 +930,7 @@ check-report.html: check-report.xml # Other tests -QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) = tests/qemu-iotests/socket_scm_helper$(EXESUF) +QEMU_IOTESTS_HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = tests/qemu-iotests/socket_scm_helper$(EXESUF) .PHONY: check-tests/qemu-iotests-quick.sh check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) $(QEMU_IOTESTS_HELPERS-y) From daf238dcd54fc80c0a83e289574ed5bb66eaa125 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 5 Jun 2018 21:47:25 +0200 Subject: [PATCH 1126/2380] qemu-binfmt-conf.sh: ignore the OS/ABI field Most of the binaries have a value of "UNIX - System V" for the OS/ABI. But cc1 has a value of "UNIX - GNU", and if we don't update the binfmt mask to ignore the OS/ABI field, gcc fails to execute it: gcc: error trying to exec '/usr/lib/gcc/m68k-linux-gnu/7/cc1': execv: Exec format error Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180605194725.8585-1-laurent@vivier.eu> --- scripts/qemu-binfmt-conf.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index 7ab7435fbd..d7eefda0b8 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -7,15 +7,15 @@ mips mipsel mipsn32 mipsn32el mips64 mips64el \ sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb microblaze microblazeel" i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00' -i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' i386_family=i386 i486_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00' -i486_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +i486_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' i486_family=i386 alpha_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90' -alpha_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +alpha_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' alpha_family=alpha arm_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00' @@ -27,11 +27,11 @@ armeb_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff armeb_family=armeb sparc_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02' -sparc_mask='\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +sparc_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' sparc_family=sparc sparc32plus_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x12' -sparc32plus_mask='\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +sparc32plus_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' sparc32plus_family=sparc ppc_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14' @@ -47,7 +47,7 @@ ppc64le_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\x ppc64le_family=ppcle m68k_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04' -m68k_mask='\xff\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +m68k_mask='\xff\xff\xff\xff\xff\xff\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' m68k_family=m68k # FIXME: We could use the other endianness on a MIPS host. @@ -77,15 +77,15 @@ mips64el_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\ mips64el_family=mips sh4_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00' -sh4_mask='\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +sh4_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' sh4_family=sh4 sh4eb_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a' -sh4eb_mask='\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +sh4eb_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' sh4eb_family=sh4 s390x_magic='\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16' -s390x_mask='\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +s390x_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' s390x_family=s390x aarch64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00' From b7bf79ccddcf9fec49da0536bfd23093a929056a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 7 Jun 2018 11:48:41 -0700 Subject: [PATCH 1127/2380] linux-user/alpha: Fix epoll syscalls These were named incorrectly, going so far as to invade strace.list. Signed-off-by: Richard Henderson Reviewed-by: Laurent Vivier Message-Id: <20180607184844.30126-2-richard.henderson@linaro.org> [lv: replace tabs by spaces] Signed-off-by: Laurent Vivier --- linux-user/alpha/syscall_nr.h | 6 +++--- linux-user/strace.list | 9 --------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/linux-user/alpha/syscall_nr.h b/linux-user/alpha/syscall_nr.h index 00e14bb6b3..fbb1ed288b 100644 --- a/linux-user/alpha/syscall_nr.h +++ b/linux-user/alpha/syscall_nr.h @@ -343,9 +343,9 @@ #define TARGET_NR_io_cancel 402 #define TARGET_NR_exit_group 405 #define TARGET_NR_lookup_dcookie 406 -#define TARGET_NR_sys_epoll_create 407 -#define TARGET_NR_sys_epoll_ctl 408 -#define TARGET_NR_sys_epoll_wait 409 +#define TARGET_NR_epoll_create 407 +#define TARGET_NR_epoll_ctl 408 +#define TARGET_NR_epoll_wait 409 #define TARGET_NR_remap_file_pages 410 #define TARGET_NR_set_tid_address 411 #define TARGET_NR_restart_syscall 412 diff --git a/linux-user/strace.list b/linux-user/strace.list index a91e33f7e5..2bc5ba04d4 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1467,15 +1467,6 @@ #ifdef TARGET_NR__sysctl { TARGET_NR__sysctl, "_sysctl" , NULL, NULL, NULL }, #endif -#ifdef TARGET_NR_sys_epoll_create -{ TARGET_NR_sys_epoll_create, "sys_epoll_create" , NULL, NULL, NULL }, -#endif -#ifdef TARGET_NR_sys_epoll_ctl -{ TARGET_NR_sys_epoll_ctl, "sys_epoll_ctl" , NULL, NULL, NULL }, -#endif -#ifdef TARGET_NR_sys_epoll_wait -{ TARGET_NR_sys_epoll_wait, "sys_epoll_wait" , NULL, NULL, NULL }, -#endif #ifdef TARGET_NR_sysfs { TARGET_NR_sysfs, "sysfs" , NULL, NULL, NULL }, #endif From 58c9d45a1d754879316258c843d247ce3c540d72 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 7 Jun 2018 11:48:42 -0700 Subject: [PATCH 1128/2380] linux-user/hppa: Fix typo in mknodat syscall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20180607184844.30126-3-richard.henderson@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/hppa/syscall_nr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/hppa/syscall_nr.h b/linux-user/hppa/syscall_nr.h index 55bdf71d50..9c1d0a195d 100644 --- a/linux-user/hppa/syscall_nr.h +++ b/linux-user/hppa/syscall_nr.h @@ -279,7 +279,7 @@ #define TARGET_NR_ppoll 274 #define TARGET_NR_openat 275 #define TARGET_NR_mkdirat 276 -#define TARGET_NR_mknotat 277 +#define TARGET_NR_mknodat 277 #define TARGET_NR_fchownat 278 #define TARGET_NR_futimesat 279 #define TARGET_NR_fstatat64 280 From 966a6356b7281cc2f82533210c23bd786734762b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 7 Jun 2018 11:48:43 -0700 Subject: [PATCH 1129/2380] linux-user/microblaze: Fix typo in accept4 syscall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20180607184844.30126-4-richard.henderson@linaro.org> [lv: replace tabs by spaces] Signed-off-by: Laurent Vivier --- linux-user/microblaze/syscall_nr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/microblaze/syscall_nr.h b/linux-user/microblaze/syscall_nr.h index 0704449bae..5d1a47a9a9 100644 --- a/linux-user/microblaze/syscall_nr.h +++ b/linux-user/microblaze/syscall_nr.h @@ -363,7 +363,7 @@ #define TARGET_NR_shutdown 359 /* new */ #define TARGET_NR_sendmsg 360 /* new */ #define TARGET_NR_recvmsg 361 /* new */ -#define TARGET_NR_accept04 362 /* new */ +#define TARGET_NR_accept4 362 /* new */ #define TARGET_NR_preadv 363 /* new */ #define TARGET_NR_pwritev 364 /* new */ #define TARGET_NR_rt_tgsigqueueinfo 365 /* new */ From dec1c928494f76ddd1484a7a4584ec18b1900a7a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 7 Jun 2018 11:48:44 -0700 Subject: [PATCH 1130/2380] linux-user/sparc64: Add inotify_rm_watch and tee syscalls Signed-off-by: Richard Henderson Reviewed-by: Laurent Vivier Message-Id: <20180607184844.30126-5-richard.henderson@linaro.org> --- linux-user/sparc64/syscall_nr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux-user/sparc64/syscall_nr.h b/linux-user/sparc64/syscall_nr.h index 9391645598..0b91b896da 100644 --- a/linux-user/sparc64/syscall_nr.h +++ b/linux-user/sparc64/syscall_nr.h @@ -154,7 +154,7 @@ #define TARGET_NR_poll 153 /* Common */ #define TARGET_NR_getdents64 154 /* Linux specific */ #define TARGET_NR_fcntl64 155 /* Linux sparc32 Specific */ -/* #define TARGET_NR_getdirentries 156 SunOS Specific */ +#define TARGET_NR_inotify_rm_watch 156 /* Linux specific */ #define TARGET_NR_statfs 157 /* Common */ #define TARGET_NR_fstatfs 158 /* Common */ #define TARGET_NR_umount 159 /* Common */ @@ -278,7 +278,7 @@ #define TARGET_NR_mq_notify 277 #define TARGET_NR_mq_getsetattr 278 #define TARGET_NR_waitid 279 -/*#define TARGET_NR_sys_setaltroot 280 available (was setaltroot) */ +#define TARGET_NR_tee 280 #define TARGET_NR_add_key 281 #define TARGET_NR_request_key 282 #define TARGET_NR_keyctl 283 From d0a96155de099d388f5e1f46277a54bdcfbb0bb2 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 23:53:34 +0200 Subject: [PATCH 1131/2380] block/file-posix: Pass FD to locking helpers raw_apply_lock_bytes() and raw_check_lock_bytes() currently take a BDRVRawState *, but they only use the lock_fd field. During image creation, we do not have a BDRVRawState, but we do have an FD; so if we want to reuse the functions there, we should modify them to receive only the FD. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Message-id: 20180509215336.31304-2-mreitz@redhat.com Signed-off-by: Max Reitz --- block/file-posix.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 513d371bb1..7583bbfbb3 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -643,7 +643,7 @@ typedef enum { * file; if @unlock == true, also unlock the unneeded bytes. * @shared_perm_lock_bits is the mask of all permissions that are NOT shared. */ -static int raw_apply_lock_bytes(BDRVRawState *s, +static int raw_apply_lock_bytes(int fd, uint64_t perm_lock_bits, uint64_t shared_perm_lock_bits, bool unlock, Error **errp) @@ -654,13 +654,13 @@ static int raw_apply_lock_bytes(BDRVRawState *s, PERM_FOREACH(i) { int off = RAW_LOCK_PERM_BASE + i; if (perm_lock_bits & (1ULL << i)) { - ret = qemu_lock_fd(s->lock_fd, off, 1, false); + ret = qemu_lock_fd(fd, off, 1, false); if (ret) { error_setg(errp, "Failed to lock byte %d", off); return ret; } } else if (unlock) { - ret = qemu_unlock_fd(s->lock_fd, off, 1); + ret = qemu_unlock_fd(fd, off, 1); if (ret) { error_setg(errp, "Failed to unlock byte %d", off); return ret; @@ -670,13 +670,13 @@ static int raw_apply_lock_bytes(BDRVRawState *s, PERM_FOREACH(i) { int off = RAW_LOCK_SHARED_BASE + i; if (shared_perm_lock_bits & (1ULL << i)) { - ret = qemu_lock_fd(s->lock_fd, off, 1, false); + ret = qemu_lock_fd(fd, off, 1, false); if (ret) { error_setg(errp, "Failed to lock byte %d", off); return ret; } } else if (unlock) { - ret = qemu_unlock_fd(s->lock_fd, off, 1); + ret = qemu_unlock_fd(fd, off, 1); if (ret) { error_setg(errp, "Failed to unlock byte %d", off); return ret; @@ -687,8 +687,7 @@ static int raw_apply_lock_bytes(BDRVRawState *s, } /* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */ -static int raw_check_lock_bytes(BDRVRawState *s, - uint64_t perm, uint64_t shared_perm, +static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, Error **errp) { int ret; @@ -698,7 +697,7 @@ static int raw_check_lock_bytes(BDRVRawState *s, int off = RAW_LOCK_SHARED_BASE + i; uint64_t p = 1ULL << i; if (perm & p) { - ret = qemu_lock_fd_test(s->lock_fd, off, 1, true); + ret = qemu_lock_fd_test(fd, off, 1, true); if (ret) { char *perm_name = bdrv_perm_names(p); error_setg(errp, @@ -715,7 +714,7 @@ static int raw_check_lock_bytes(BDRVRawState *s, int off = RAW_LOCK_PERM_BASE + i; uint64_t p = 1ULL << i; if (!(shared_perm & p)) { - ret = qemu_lock_fd_test(s->lock_fd, off, 1, true); + ret = qemu_lock_fd_test(fd, off, 1, true); if (ret) { char *perm_name = bdrv_perm_names(p); error_setg(errp, @@ -752,11 +751,11 @@ static int raw_handle_perm_lock(BlockDriverState *bs, switch (op) { case RAW_PL_PREPARE: - ret = raw_apply_lock_bytes(s, s->perm | new_perm, + ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm, ~s->shared_perm | ~new_shared, false, errp); if (!ret) { - ret = raw_check_lock_bytes(s, new_perm, new_shared, errp); + ret = raw_check_lock_bytes(s->lock_fd, new_perm, new_shared, errp); if (!ret) { return 0; } @@ -764,7 +763,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs, op = RAW_PL_ABORT; /* fall through to unlock bytes. */ case RAW_PL_ABORT: - raw_apply_lock_bytes(s, s->perm, ~s->shared_perm, true, &local_err); + raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm, + true, &local_err); if (local_err) { /* Theoretically the above call only unlocks bytes and it cannot * fail. Something weird happened, report it. @@ -773,7 +773,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs, } break; case RAW_PL_COMMIT: - raw_apply_lock_bytes(s, new_perm, ~new_shared, true, &local_err); + raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared, + true, &local_err); if (local_err) { /* Theoretically the above call only unlocks bytes and it cannot * fail. Something weird happened, report it. From b8cf1913a989b9ea6f248aaa233330047a62962e Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 23:53:35 +0200 Subject: [PATCH 1132/2380] block/file-posix: File locking during creation When creating a file, we should take the WRITE and RESIZE permissions. We do not need either for the creation itself, but we do need them for clearing and resizing it. So we can take the proper permissions by replacing O_TRUNC with an explicit truncation to 0, and by taking the appropriate file locks between those two steps. Signed-off-by: Max Reitz Message-id: 20180509215336.31304-3-mreitz@redhat.com Reviewed-by: Fam Zheng Signed-off-by: Max Reitz --- block/file-posix.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 7583bbfbb3..07bb061fe4 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2076,6 +2076,7 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) { BlockdevCreateOptionsFile *file_opts; int fd; + int perm, shared; int result = 0; /* Validate options and set default values */ @@ -2090,14 +2091,44 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) } /* Create file */ - fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, - 0644); + fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_BINARY, 0644); if (fd < 0) { result = -errno; error_setg_errno(errp, -result, "Could not create file"); goto out; } + /* Take permissions: We want to discard everything, so we need + * BLK_PERM_WRITE; and truncation to the desired size requires + * BLK_PERM_RESIZE. + * On the other hand, we cannot share the RESIZE permission + * because we promise that after this function, the file has the + * size given in the options. If someone else were to resize it + * concurrently, we could not guarantee that. + * Note that after this function, we can no longer guarantee that + * the file is not touched by a third party, so it may be resized + * then. */ + perm = BLK_PERM_WRITE | BLK_PERM_RESIZE; + shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; + + /* Step one: Take locks */ + result = raw_apply_lock_bytes(fd, perm, shared, false, errp); + if (result < 0) { + goto out_close; + } + + /* Step two: Check that nobody else has taken conflicting locks */ + result = raw_check_lock_bytes(fd, perm, shared, errp); + if (result < 0) { + goto out_close; + } + + /* Clear the file by truncating it to 0 */ + result = raw_regular_truncate(fd, 0, PREALLOC_MODE_OFF, errp); + if (result < 0) { + goto out_close; + } + if (file_opts->nocow) { #ifdef __linux__ /* Set NOCOW flag to solve performance issue on fs like btrfs. @@ -2113,6 +2144,8 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) #endif } + /* Resize and potentially preallocate the file to the desired + * final size */ result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation, errp); if (result < 0) { From f45b638f9f967cdbea4e24704bd16a858ddcde03 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 23:53:36 +0200 Subject: [PATCH 1133/2380] iotests: Add creation test to 153 This patch adds a test case to 153 which tries to overwrite an image (using qemu-img create) while it is in use. Without the original user explicitly sharing the necessary permissions (writing and truncation), this should not be allowed. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Message-id: 20180509215336.31304-4-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/153 | 18 ++++++++++++++++++ tests/qemu-iotests/153.out | 13 +++++++++++++ 2 files changed, 31 insertions(+) diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index ec508c758f..673813cd39 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -137,6 +137,24 @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do _run_cmd $QEMU_IMG dd $L if="${TEST_IMG}" of="${TEST_IMG}.convert" bs=512 count=1 _run_cmd $QEMU_IMG bench $L -c 1 "${TEST_IMG}" _run_cmd $QEMU_IMG bench $L -w -c 1 "${TEST_IMG}" + + # qemu-img create does not support -U + if [ -z "$L" ]; then + _run_cmd $QEMU_IMG create -f $IMGFMT "${TEST_IMG}" \ + -b ${TEST_IMG}.base + # Read the file format. It used to be the case that + # file-posix simply truncated the file, but the qcow2 + # driver then failed to format it because it was unable + # to acquire the necessary WRITE permission. However, the + # truncation was already wrong, and the whole process + # resulted in the file being completely empty and thus its + # format would be detected to be raw. + # So we read it here to see that creation either completed + # successfully (thus the format is qcow2) or it aborted + # before the file was changed at all (thus the format stays + # qcow2). + _img_info -U | grep 'file format' + fi done _send_qemu_cmd $h "{ 'execute': 'quit', }" "" echo diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index 2510762ba1..3492ba7a29 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -92,6 +92,11 @@ _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock Is another process using the image? +_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base +qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock +Is another process using the image? +file format: IMGFMT + == Running utility commands -U == _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 @@ -209,6 +214,11 @@ _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock Is another process using the image? +_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base +qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock +Is another process using the image? +file format: IMGFMT + == Running utility commands -U == _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 @@ -309,6 +319,9 @@ _qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2 _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 +_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base +file format: IMGFMT + == Running utility commands -U == _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 From 1f996683ad908fd41c572221a63d9fc31ce04d07 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 23:00:17 +0200 Subject: [PATCH 1134/2380] qemu-img: Amendment support implies create_opts Instead of checking whether a driver has a non-NULL create_opts we should check whether it supports image amendment in the first place. If it does, it must have create_opts. On the other hand, if it does not have create_opts (so it does not support amendment either), the error message "does not support any options" is a bit useless. Stating clearly that the driver has no amendment support whatsoever is probably better. Signed-off-by: Max Reitz Reviewed-by: John Snow Reviewed-by: Eric Blake Message-id: 20180509210023.20283-2-mreitz@redhat.com Signed-off-by: Max Reitz --- qemu-img.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 75f1610aa0..0be11b3852 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3789,13 +3789,16 @@ static int img_amend(int argc, char **argv) goto out; } - if (!bs->drv->create_opts) { - error_report("Format driver '%s' does not support any options to amend", + if (!bs->drv->bdrv_amend_options) { + error_report("Format driver '%s' does not support option amendment", fmt); ret = -1; goto out; } + /* Every driver supporting amendment must have create_opts */ + assert(bs->drv->create_opts); + create_opts = qemu_opts_append(create_opts, bs->drv->create_opts); opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); qemu_opts_do_parse(opts, options, NULL, &err); From d1402b502691142b9cebadd5cb993dc8858e9071 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 23:00:18 +0200 Subject: [PATCH 1135/2380] block: Add Error parameter to bdrv_amend_options Looking at the qcow2 code that is riddled with error_report() calls, this is really how it should have been from the start. Along the way, turn the target_version/current_version comparisons at the beginning of qcow2_downgrade() into assertions (the caller has to make sure these conditions are met), and rephrase the error message on using compat=1.1 to get refcount widths other than 16 bits. Signed-off-by: Max Reitz Message-id: 20180509210023.20283-3-mreitz@redhat.com Reviewed-by: Eric Blake Reviewed-by: John Snow Signed-off-by: Max Reitz --- block.c | 8 +++-- block/qcow2.c | 72 ++++++++++++++++++++++---------------- include/block/block.h | 3 +- include/block/block_int.h | 3 +- qemu-img.c | 4 +-- tests/qemu-iotests/060.out | 4 +-- tests/qemu-iotests/061.out | 7 ---- tests/qemu-iotests/080.out | 4 +-- tests/qemu-iotests/112.out | 5 +-- 9 files changed, 58 insertions(+), 52 deletions(-) diff --git a/block.c b/block.c index 501b64c819..9d577f65bb 100644 --- a/block.c +++ b/block.c @@ -4996,15 +4996,19 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs, } int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, void *cb_opaque) + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + Error **errp) { if (!bs->drv) { + error_setg(errp, "Node is ejected"); return -ENOMEDIUM; } if (!bs->drv->bdrv_amend_options) { + error_setg(errp, "Block driver '%s' does not support option amendment", + bs->drv->format_name); return -ENOTSUP; } - return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque); + return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp); } /* This function will be called by the bdrv_recurse_is_first_non_filter method diff --git a/block/qcow2.c b/block/qcow2.c index 549fee9b69..6b2d88759d 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -4215,22 +4215,21 @@ static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, * have to be removed. */ static int qcow2_downgrade(BlockDriverState *bs, int target_version, - BlockDriverAmendStatusCB *status_cb, void *cb_opaque) + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + Error **errp) { BDRVQcow2State *s = bs->opaque; int current_version = s->qcow_version; int ret; - if (target_version == current_version) { - return 0; - } else if (target_version > current_version) { - return -EINVAL; - } else if (target_version != 2) { - return -EINVAL; - } + /* This is qcow2_downgrade(), not qcow2_upgrade() */ + assert(target_version < current_version); + + /* There are no other versions (now) that you can downgrade to */ + assert(target_version == 2); if (s->refcount_order != 4) { - error_report("compat=0.10 requires refcount_bits=16"); + error_setg(errp, "compat=0.10 requires refcount_bits=16"); return -ENOTSUP; } @@ -4238,6 +4237,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { ret = qcow2_mark_clean(bs); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to make the image clean"); return ret; } } @@ -4247,6 +4247,8 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, * best thing to do anyway */ if (s->incompatible_features) { + error_setg(errp, "Cannot downgrade an image with incompatible features " + "%#" PRIx64 " set", s->incompatible_features); return -ENOTSUP; } @@ -4260,6 +4262,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to turn zero into data clusters"); return ret; } @@ -4267,6 +4270,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, ret = qcow2_update_header(bs); if (ret < 0) { s->qcow_version = current_version; + error_setg_errno(errp, -ret, "Failed to update the image header"); return ret; } return 0; @@ -4344,7 +4348,8 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs, static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb, - void *cb_opaque) + void *cb_opaque, + Error **errp) { BDRVQcow2State *s = bs->opaque; int old_version = s->qcow_version, new_version = old_version; @@ -4356,7 +4361,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, bool encrypt; int encformat; int refcount_bits = s->refcount_bits; - Error *local_err = NULL; int ret; QemuOptDesc *desc = opts->list->desc; Qcow2AmendHelperCBInfo helper_cb_info; @@ -4377,11 +4381,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, } else if (!strcmp(compat, "1.1")) { new_version = 3; } else { - error_report("Unknown compatibility level %s", compat); + error_setg(errp, "Unknown compatibility level %s", compat); return -EINVAL; } } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) { - error_report("Cannot change preallocation mode"); + error_setg(errp, "Cannot change preallocation mode"); return -ENOTSUP; } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); @@ -4394,7 +4398,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, !!s->crypto); if (encrypt != !!s->crypto) { - error_report("Changing the encryption flag is not supported"); + error_setg(errp, + "Changing the encryption flag is not supported"); return -ENOTSUP; } } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) { @@ -4402,17 +4407,19 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT)); if (encformat != s->crypt_method_header) { - error_report("Changing the encryption format is not supported"); + error_setg(errp, + "Changing the encryption format is not supported"); return -ENOTSUP; } } else if (g_str_has_prefix(desc->name, "encrypt.")) { - error_report("Changing the encryption parameters is not supported"); + error_setg(errp, + "Changing the encryption parameters is not supported"); return -ENOTSUP; } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, cluster_size); if (cluster_size != s->cluster_size) { - error_report("Changing the cluster size is not supported"); + error_setg(errp, "Changing the cluster size is not supported"); return -ENOTSUP; } } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { @@ -4425,8 +4432,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (refcount_bits <= 0 || refcount_bits > 64 || !is_power_of_2(refcount_bits)) { - error_report("Refcount width must be a power of two and may " - "not exceed 64 bits"); + error_setg(errp, "Refcount width must be a power of two and " + "may not exceed 64 bits"); return -EINVAL; } } else { @@ -4451,6 +4458,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, ret = qcow2_update_header(bs); if (ret < 0) { s->qcow_version = old_version; + error_setg_errno(errp, -ret, "Failed to update the image header"); return ret; } } @@ -4459,18 +4467,17 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, int refcount_order = ctz32(refcount_bits); if (new_version < 3 && refcount_bits != 16) { - error_report("Different refcount widths than 16 bits require " - "compatibility level 1.1 or above (use compat=1.1 or " - "greater)"); + error_setg(errp, "Refcount widths other than 16 bits require " + "compatibility level 1.1 or above (use compat=1.1 or " + "greater)"); return -EINVAL; } helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER; ret = qcow2_change_refcount_order(bs, refcount_order, &qcow2_amend_helper_cb, - &helper_cb_info, &local_err); + &helper_cb_info, errp); if (ret < 0) { - error_report_err(local_err); return ret; } } @@ -4480,6 +4487,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, backing_file ?: s->image_backing_file, backing_format ?: s->image_backing_format); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to change the backing file"); return ret; } } @@ -4487,14 +4495,16 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (s->use_lazy_refcounts != lazy_refcounts) { if (lazy_refcounts) { if (new_version < 3) { - error_report("Lazy refcounts only supported with compatibility " - "level 1.1 and above (use compat=1.1 or greater)"); + error_setg(errp, "Lazy refcounts only supported with " + "compatibility level 1.1 and above (use compat=1.1 " + "or greater)"); return -EINVAL; } s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; ret = qcow2_update_header(bs); if (ret < 0) { s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; + error_setg_errno(errp, -ret, "Failed to update the image header"); return ret; } s->use_lazy_refcounts = true; @@ -4502,6 +4512,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, /* make image clean first */ ret = qcow2_mark_clean(bs); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to make the image clean"); return ret; } /* now disallow lazy refcounts */ @@ -4509,6 +4520,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, ret = qcow2_update_header(bs); if (ret < 0) { s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; + error_setg_errno(errp, -ret, "Failed to update the image header"); return ret; } s->use_lazy_refcounts = false; @@ -4517,17 +4529,15 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (new_size) { BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL); - ret = blk_insert_bs(blk, bs, &local_err); + ret = blk_insert_bs(blk, bs, errp); if (ret < 0) { - error_report_err(local_err); blk_unref(blk); return ret; } - ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, &local_err); + ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, errp); blk_unref(blk); if (ret < 0) { - error_report_err(local_err); return ret; } } @@ -4536,7 +4546,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (new_version < old_version) { helper_cb_info.current_operation = QCOW2_DOWNGRADING; ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb, - &helper_cb_info); + &helper_cb_info, errp); if (ret < 0) { return ret; } diff --git a/include/block/block.h b/include/block/block.h index 6cc6c7e699..4dd4f1eab2 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -343,7 +343,8 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, int64_t total_work_size, void *opaque); int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, void *cb_opaque); + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + Error **errp); /* external snapshots */ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, diff --git a/include/block/block_int.h b/include/block/block_int.h index 888b7f7bff..327e478a73 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -353,7 +353,8 @@ struct BlockDriver { int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb, - void *cb_opaque); + void *cb_opaque, + Error **errp); void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event); diff --git a/qemu-img.c b/qemu-img.c index 0be11b3852..df3ec5b7fe 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3810,10 +3810,10 @@ static int img_amend(int argc, char **argv) /* In case the driver does not call amend_status_cb() */ qemu_progress_print(0.f, 0); - ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL); + ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, &err); qemu_progress_print(100.f, 0); if (ret < 0) { - error_report("Error while amending options: %s", strerror(-ret)); + error_report_err(err); goto out; } diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index 25d5c3938b..5f4264cff6 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -129,7 +129,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Marking image as corrupt: L2 table offset 0x42a00 unaligned (L1 index: 0); further corruption events will be suppressed -qemu-img: Error while amending options: Input/output error +qemu-img: Failed to turn zero into data clusters: Input/output error === Testing unaligned L2 entry === @@ -145,7 +145,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Marking image as corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed -qemu-img: Error while amending options: Input/output error +qemu-img: Failed to turn zero into data clusters: Input/output error === Testing unaligned reftable entry === diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index e857ef9a7d..183f7dd690 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -358,18 +358,12 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) -qemu-img: Error while amending options: Invalid argument qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) -qemu-img: Error while amending options: Invalid argument qemu-img: Unknown compatibility level 0.42 -qemu-img: Error while amending options: Invalid argument qemu-img: Invalid parameter 'foo' qemu-img: Changing the cluster size is not supported -qemu-img: Error while amending options: Operation not supported qemu-img: Changing the encryption flag is not supported -qemu-img: Error while amending options: Operation not supported qemu-img: Cannot change preallocation mode -qemu-img: Error while amending options: Operation not supported === Testing correct handling of unset value === @@ -377,7 +371,6 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Should work: Should not work: qemu-img: Changing the cluster size is not supported -qemu-img: Error while amending options: Operation not supported === Testing zero expansion on inactive clusters === diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out index 4e0f7f7b92..281c7e0d1d 100644 --- a/tests/qemu-iotests/080.out +++ b/tests/qemu-iotests/080.out @@ -65,7 +65,7 @@ wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid qemu-img: Snapshot L1 table offset invalid -qemu-img: Error while amending options: Invalid argument +qemu-img: Failed to turn zero into data clusters: Invalid argument Failed to flush the refcount block cache: Invalid argument write failed: Invalid argument qemu-img: Snapshot L1 table offset invalid @@ -88,7 +88,7 @@ wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Failed to load snapshot: Snapshot L1 table too large qemu-img: Snapshot L1 table too large -qemu-img: Error while amending options: File too large +qemu-img: Failed to turn zero into data clusters: File too large Failed to flush the refcount block cache: File too large write failed: File too large qemu-img: Snapshot L1 table too large diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out index 86f041075d..ae0318cabe 100644 --- a/tests/qemu-iotests/112.out +++ b/tests/qemu-iotests/112.out @@ -99,13 +99,11 @@ refcount bits: 64 === Amend to compat=0.10 === qemu-img: compat=0.10 requires refcount_bits=16 -qemu-img: Error while amending options: Operation not supported refcount bits: 64 No errors were found on the image. refcount bits: 16 refcount bits: 16 -qemu-img: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) -qemu-img: Error while amending options: Invalid argument +qemu-img: Refcount widths other than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) refcount bits: 16 === Amend with snapshot === @@ -113,7 +111,6 @@ refcount bits: 16 wrote 16777216/16777216 bytes at offset 0 16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Cannot decrease refcount entry width to 1 bits: Cluster at offset 0x50000 has a refcount of 2 -qemu-img: Error while amending options: Invalid argument No errors were found on the image. refcount bits: 16 No errors were found on the image. From 7f3fb00136aaec74df8132492d1f48b8e9840258 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 23:00:19 +0200 Subject: [PATCH 1136/2380] qemu-option: Pull out "Supported options" print It really is up to the caller to decide what this list of options means. Signed-off-by: Max Reitz Reviewed-by: John Snow Reviewed-by: Eric Blake Message-id: 20180509210023.20283-4-mreitz@redhat.com Signed-off-by: Max Reitz --- qemu-img.c | 1 + util/qemu-option.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c index df3ec5b7fe..52008c5647 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -260,6 +260,7 @@ static int print_block_option_help(const char *filename, const char *fmt) create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); } + printf("Supported options:\n"); qemu_opts_print_help(create_opts); qemu_opts_free(create_opts); return 0; diff --git a/util/qemu-option.c b/util/qemu-option.c index 58d1c23893..ba44a0895c 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -220,7 +220,6 @@ void qemu_opts_print_help(QemuOptsList *list) assert(list); desc = list->desc; - printf("Supported options:\n"); while (desc && desc->name) { printf("%-16s %s\n", desc->name, desc->help ? desc->help : "No description available"); From 51641351420e4f9dc6613d0dd03a5f0f214ed5b6 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 23:00:20 +0200 Subject: [PATCH 1137/2380] qemu-img: Add print_amend_option_help() The more generic print_block_option_help() function is not really suitable for qemu-img amend, for a couple of reasons: (1) We do not need to append the protocol-level options, as amendment happens only on one node and does not descend downwards to its children. (2) print_block_option_help() says those options are "supported". For option amendment, we do not really know that. So this new function explicitly says that those options are the creation options, and not all of them may be supported. (3) If the driver does not support option amendment, we should not print anything (except for an error message that amendment is not supported). Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1537956 Signed-off-by: Max Reitz Reviewed-by: John Snow Reviewed-by: Eric Blake Message-id: 20180509210023.20283-5-mreitz@redhat.com Signed-off-by: Max Reitz --- qemu-img.c | 30 ++++++++++++++++++++++++-- tests/qemu-iotests/082.out | 44 +++++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 52008c5647..07935cb232 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3658,6 +3658,32 @@ static void amend_status_cb(BlockDriverState *bs, qemu_progress_print(100.f * offset / total_work_size, 0); } +static int print_amend_option_help(const char *format) +{ + BlockDriver *drv; + + /* Find driver and parse its options */ + drv = bdrv_find_format(format); + if (!drv) { + error_report("Unknown file format '%s'", format); + return 1; + } + + if (!drv->bdrv_amend_options) { + error_report("Format driver '%s' does not support option amendment", + format); + return 1; + } + + /* Every driver supporting amendment must have create_opts */ + assert(drv->create_opts); + + printf("Creation options for '%s':\n", format); + qemu_opts_print_help(drv->create_opts); + printf("\nNote that not all of these options may be amendable.\n"); + return 0; +} + static int img_amend(int argc, char **argv) { Error *err = NULL; @@ -3757,7 +3783,7 @@ static int img_amend(int argc, char **argv) if (fmt && has_help_option(options)) { /* If a format is explicitly specified (and possibly no filename is * given), print option help here */ - ret = print_block_option_help(filename, fmt); + ret = print_amend_option_help(fmt); goto out; } @@ -3786,7 +3812,7 @@ static int img_amend(int argc, char **argv) if (has_help_option(options)) { /* If the format was auto-detected, print option help here */ - ret = print_block_option_help(filename, fmt); + ret = print_amend_option_help(fmt); goto out; } diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index 1527fbe1b7..4e52dce8d6 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -546,7 +546,7 @@ cluster_size: 65536 === amend: help for -o === Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -564,10 +564,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -585,10 +586,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -606,10 +608,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -627,10 +630,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -648,10 +652,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -669,10 +674,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -690,10 +696,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -711,7 +718,8 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 @@ -731,7 +739,7 @@ Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/ qemu-img: Invalid option list: ,, Testing: amend -f qcow2 -o help -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -750,6 +758,8 @@ preallocation Preallocation mode (allowed values: off, metadata, falloc, full lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits +Note that not all of these options may be amendable. + Testing: convert -o help Supported options: size Virtual disk size From d402b6a21a825a5c07aac9251990860723d49f5d Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 23:00:21 +0200 Subject: [PATCH 1138/2380] qemu-img: Recognize no creation support in -o help The only users of print_block_option_help() are qemu-img create and qemu-img convert for the output image, so this function is always used for image creation (it used to be used for amendment also, but that is no longer the case). So if image creation is not supported by either the format or the protocol, there is no need to print any option description, because the user cannot create an image like this anyway. This also fixes an assertion failure: $ qemu-img create -f bochs -o help Supported options: qemu-img: util/qemu-option.c:219: qemu_opts_print_help: Assertion `list' failed. [1] 24831 abort (core dumped) qemu-img create -f bochs -o help Signed-off-by: Max Reitz Reviewed-by: John Snow Reviewed-by: Eric Blake Message-id: 20180509210023.20283-6-mreitz@redhat.com Signed-off-by: Max Reitz --- qemu-img.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qemu-img.c b/qemu-img.c index 07935cb232..b1c1e484d2 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -249,6 +249,11 @@ static int print_block_option_help(const char *filename, const char *fmt) return 1; } + if (!drv->create_opts) { + error_report("Format driver '%s' does not support image creation", fmt); + return 1; + } + create_opts = qemu_opts_append(create_opts, drv->create_opts); if (filename) { proto_drv = bdrv_find_protocol(filename, true, &local_err); @@ -257,6 +262,11 @@ static int print_block_option_help(const char *filename, const char *fmt) qemu_opts_free(create_opts); return 1; } + if (!proto_drv->create_opts) { + error_report("Protocal driver '%s' does not support image creation", + proto_drv->format_name); + return 1; + } create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); } From e53995eb19b546b18d1a34cd6eaa07faedbade79 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 23:00:22 +0200 Subject: [PATCH 1139/2380] iotests: Test help option for unsupporting formats This adds test cases to 082 for qemu-img create/convert/amend "-o help" on formats that do not support creation or amendment, respectively. Signed-off-by: Max Reitz Reviewed-by: John Snow Reviewed-by: Eric Blake Message-id: 20180509210023.20283-7-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/082 | 9 +++++++++ tests/qemu-iotests/082.out | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 index d5c83d45ed..a872f771a6 100755 --- a/tests/qemu-iotests/082 +++ b/tests/qemu-iotests/082 @@ -97,6 +97,9 @@ run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_ run_qemu_img create -f $IMGFMT -o help run_qemu_img create -o help +# Try help option for a format that does not support creation +run_qemu_img create -f bochs -o help + echo echo === convert: Options specified more than once === @@ -151,6 +154,9 @@ run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST run_qemu_img convert -O $IMGFMT -o help run_qemu_img convert -o help +# Try help option for a format that does not support creation +run_qemu_img convert -O bochs -o help + echo echo === amend: Options specified more than once === @@ -201,6 +207,9 @@ run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_I run_qemu_img amend -f $IMGFMT -o help run_qemu_img convert -o help +# Try help option for a format that does not support amendment +run_qemu_img amend -f bochs -o help + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index 4e52dce8d6..60ef87c276 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -249,6 +249,9 @@ Testing: create -o help Supported options: size Virtual disk size +Testing: create -f bochs -o help +qemu-img: Format driver 'bochs' does not support image creation + === convert: Options specified more than once === Testing: create -f qcow2 TEST_DIR/t.qcow2 128M @@ -502,6 +505,9 @@ Testing: convert -o help Supported options: size Virtual disk size +Testing: convert -O bochs -o help +qemu-img: Format driver 'bochs' does not support image creation + === amend: Options specified more than once === Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2 @@ -763,4 +769,7 @@ Note that not all of these options may be amendable. Testing: convert -o help Supported options: size Virtual disk size + +Testing: amend -f bochs -o help +qemu-img: Format driver 'bochs' does not support option amendment *** done From dee6ddd8a6b7978d0bc8ef8e1f006282ce30e4fa Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 23:00:23 +0200 Subject: [PATCH 1140/2380] iotests: Rework 113 This test case has been broken since 398e6ad014df261d (roughly half a year). qemu-img amend requires its output image to be R/W, so it opens it as such; the node is then turned into an read-only node automatically which is now accompanied by a warning, however. This warning has not been part of the reference output. For one thing, this warning shows that we cannot keep the test case as it is. We would need a format that has no create_opts but that does have write support -- we do not have such a format, though. Another thing is that qemu now actually checks whether an image format supports amendment instead of whether it has create_opts (since the former always implies the latter). So we can now use any format that does not support amendment (even if it supports creation) and thus test the same code path. The reason nobody has noticed the breakage until now of course is the fact that nobody runs the iotests for nbd+bochs. There actually was never any reason to set the protocol to "nbd" but because that was technically correct; functionally it made no difference. So that is the first thing we are going to change: Make the protocol "file" instead so that people might actually notice breakage here. Secondly, now that bochs no longer works for the amend test case, we have to change the format there anyway. Set let us just bend the truth a bit, declare this test a raw test. In fact, that does not even concern the bochs test cases, other than the output now reading 'bochs' instead of 'IMGFMT'. So with this test now being a raw test, we can rework the amend test case to use raw instead. Signed-off-by: Max Reitz Reviewed-by: John Snow Message-id: 20180509210023.20283-8-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/113 | 19 +++++++++---------- tests/qemu-iotests/113.out | 7 ++++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/qemu-iotests/113 b/tests/qemu-iotests/113 index 19b68b2727..4e09810905 100755 --- a/tests/qemu-iotests/113 +++ b/tests/qemu-iotests/113 @@ -38,16 +38,17 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -# We can only test one format here because we need its sample file -_supported_fmt bochs -_supported_proto nbd +# Some of these test cases use bochs, but others do use raw, so this +# is only half a lie. +_supported_fmt raw +_supported_proto file _supported_os Linux echo echo '=== Unsupported image creation in qemu-img create ===' echo -$QEMU_IMG create -f $IMGFMT nbd://example.com 2>&1 64M | _filter_imgfmt +$QEMU_IMG create -f bochs nbd://example.com 2>&1 64M echo echo '=== Unsupported image creation in qemu-img convert ===' @@ -56,17 +57,15 @@ echo # We could use any input image format here, but this is a bochs test, so just # use the bochs image _use_sample_img empty.bochs.bz2 -$QEMU_IMG convert -f $IMGFMT -O $IMGFMT "$TEST_IMG" nbd://example.com 2>&1 \ - | _filter_imgfmt +$QEMU_IMG convert -f bochs -O bochs "$TEST_IMG" nbd://example.com echo echo '=== Unsupported format in qemu-img amend ===' echo -# The protocol does not matter here -_use_sample_img empty.bochs.bz2 -$QEMU_IMG amend -f $IMGFMT -o foo=bar "$TEST_IMG" 2>&1 | _filter_imgfmt - +TEST_IMG="$TEST_DIR/t.$IMGFMT" +_make_test_img 1M +$QEMU_IMG amend -f $IMGFMT -o size=2M "$TEST_IMG" 2>&1 | _filter_imgfmt # success, all done echo diff --git a/tests/qemu-iotests/113.out b/tests/qemu-iotests/113.out index 00bdfd6887..3557e2bbf0 100644 --- a/tests/qemu-iotests/113.out +++ b/tests/qemu-iotests/113.out @@ -2,14 +2,15 @@ QA output created by 113 === Unsupported image creation in qemu-img create === -qemu-img: nbd://example.com: Format driver 'IMGFMT' does not support image creation +qemu-img: nbd://example.com: Format driver 'bochs' does not support image creation === Unsupported image creation in qemu-img convert === -qemu-img: Format driver 'IMGFMT' does not support image creation +qemu-img: Format driver 'bochs' does not support image creation === Unsupported format in qemu-img amend === -qemu-img: Format driver 'IMGFMT' does not support any options to amend +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +qemu-img: Format driver 'IMGFMT' does not support option amendment *** done From 3cce51c919c7b4028cf6676dfcb80a45741b5117 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 22:00:58 +0200 Subject: [PATCH 1141/2380] qcow2: Repair OFLAG_COPIED when fixing leaks Repairing OFLAG_COPIED is usually safe because it is done after the refcounts have been repaired. Therefore, it we did not find anyone else referencing a data or L2 cluster, it makes no sense to not set OFLAG_COPIED -- and the other direction (clearing OFLAG_COPIED) is always safe, anyway, it may just induce leaks. Furthermore, if OFLAG_COPIED is actually consistent with a wrong (leaky) refcount, we will decrement the refcount with -r leaks, but OFLAG_COPIED will then be wrong. qemu-img check should not produce images that are more corrupted afterwards then they were before. Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1527085 Signed-off-by: Max Reitz Reviewed-by: Eric Blake Message-id: 20180509200059.31125-2-mreitz@redhat.com Signed-off-by: Max Reitz --- block/qcow2-refcount.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 403236256b..18c729aa27 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1799,6 +1799,19 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, int ret; uint64_t refcount; int i, j; + bool repair; + + if (fix & BDRV_FIX_ERRORS) { + /* Always repair */ + repair = true; + } else if (fix & BDRV_FIX_LEAKS) { + /* Repair only if that seems safe: This function is always + * called after the refcounts have been fixed, so the refcount + * is accurate if that repair was successful */ + repair = !res->check_errors && !res->corruptions && !res->leaks; + } else { + repair = false; + } for (i = 0; i < s->l1_size; i++) { uint64_t l1_entry = s->l1_table[i]; @@ -1818,10 +1831,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", - fix & BDRV_FIX_ERRORS ? "Repairing" : - "ERROR", - i, l1_entry, refcount); - if (fix & BDRV_FIX_ERRORS) { + repair ? "Repairing" : "ERROR", i, l1_entry, refcount); + if (repair) { s->l1_table[i] = refcount == 1 ? l1_entry | QCOW_OFLAG_COPIED : l1_entry & ~QCOW_OFLAG_COPIED; @@ -1862,10 +1873,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED data cluster: " "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", - fix & BDRV_FIX_ERRORS ? "Repairing" : - "ERROR", - l2_entry, refcount); - if (fix & BDRV_FIX_ERRORS) { + repair ? "Repairing" : "ERROR", l2_entry, refcount); + if (repair) { l2_table[j] = cpu_to_be64(refcount == 1 ? l2_entry | QCOW_OFLAG_COPIED : l2_entry & ~QCOW_OFLAG_COPIED); From b41ad73a3bb972eb43cf52d28669f67ea3fe1762 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 22:00:59 +0200 Subject: [PATCH 1142/2380] iotests: Repairing error during snapshot deletion This adds a test for an I/O error during snapshot deletion, and maybe more importantly, for how to repair the resulting image. If the snapshot has been deleted before the error occurs, the only negative result will be leaked clusters -- and those should be repairable with qemu-img check -r leaks. Signed-off-by: Max Reitz Reviewed-by: Eric Blake Message-id: 20180509200059.31125-3-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/217 | 90 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/217.out | 42 ++++++++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 133 insertions(+) create mode 100755 tests/qemu-iotests/217 create mode 100644 tests/qemu-iotests/217.out diff --git a/tests/qemu-iotests/217 b/tests/qemu-iotests/217 new file mode 100755 index 0000000000..d3ab5d72be --- /dev/null +++ b/tests/qemu-iotests/217 @@ -0,0 +1,90 @@ +#!/bin/bash +# +# I/O errors when working with internal qcow2 snapshots, and repairing +# the result +# +# Copyright (C) 2018 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 . + +seq=$(basename $0) +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_DIR/blkdebug.conf" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# This test is specific to qcow2 +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +# This test needs clusters with at least a refcount of 2 so that +# OFLAG_COPIED is not set. refcount_bits=1 is therefore unsupported. +_unsupported_imgopts 'refcount_bits=1[^0-9]' + +echo +echo '=== Simulating an I/O error during snapshot deletion ===' +echo + +_make_test_img 64M +$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io + +# Create the snapshot +$QEMU_IMG snapshot -c foo "$TEST_IMG" + +# Verify the snapshot is there +echo +_img_info | grep 'Snapshot list' +echo '(Snapshot filtered)' +echo + +# Try to delete the snapshot (with an error happening when freeing the +# then leaked clusters) +cat > "$TEST_DIR/blkdebug.conf" < Date: Wed, 9 May 2018 21:42:58 +0200 Subject: [PATCH 1143/2380] qemu-io: Drop command functions' return values For qemu-io, a function returns an integer with two possible values: 0 for "qemu-io may continue execution", or 1 for "qemu-io should exit". However, there is only a single command that returns 1, and that is "quit". So let's turn this case into a global variable instead so we can make better use of the return value in a later patch. Signed-off-by: Max Reitz Reviewed-by: Eric Blake Message-id: 20180509194302.21585-2-mreitz@redhat.com Signed-off-by: Max Reitz --- include/qemu-io.h | 6 +- qemu-io-cmds.c | 294 +++++++++++++++++++++------------------------- qemu-io.c | 36 +++--- 3 files changed, 157 insertions(+), 179 deletions(-) diff --git a/include/qemu-io.h b/include/qemu-io.h index 196fde0f3a..06cdfbf660 100644 --- a/include/qemu-io.h +++ b/include/qemu-io.h @@ -22,7 +22,7 @@ #define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ -typedef int (*cfunc_t)(BlockBackend *blk, int argc, char **argv); +typedef void (*cfunc_t)(BlockBackend *blk, int argc, char **argv); typedef void (*helpfunc_t)(void); typedef struct cmdinfo { @@ -41,10 +41,10 @@ typedef struct cmdinfo { extern bool qemuio_misalign; -bool qemuio_command(BlockBackend *blk, const char *cmd); +void qemuio_command(BlockBackend *blk, const char *cmd); void qemuio_add_command(const cmdinfo_t *ci); -int qemuio_command_usage(const cmdinfo_t *ci); +void qemuio_command_usage(const cmdinfo_t *ci); void qemuio_complete_command(const char *input, void (*fn)(const char *cmd, void *opaque), void *opaque); diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 9b3cd00af6..c2fbaae0fd 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -48,10 +48,9 @@ void qemuio_add_command(const cmdinfo_t *ci) qsort(cmdtab, ncmds, sizeof(*cmdtab), compare_cmdname); } -int qemuio_command_usage(const cmdinfo_t *ci) +void qemuio_command_usage(const cmdinfo_t *ci) { printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline); - return 0; } static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct) @@ -66,13 +65,13 @@ static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct) return 1; } -static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, - char **argv) +static void command(BlockBackend *blk, const cmdinfo_t *ct, int argc, + char **argv) { char *cmd = argv[0]; if (!init_check_command(blk, ct)) { - return 0; + return; } if (argc - 1 < ct->argmin || (ct->argmax != -1 && argc - 1 > ct->argmax)) { @@ -89,7 +88,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, "bad argument count %d to %s, expected between %d and %d arguments\n", argc-1, cmd, ct->argmin, ct->argmax); } - return 0; + return; } /* Request additional permissions if necessary for this command. The caller @@ -109,13 +108,13 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err); if (ret < 0) { error_report_err(local_err); - return 0; + return; } } } optind = 0; - return ct->cfunc(blk, argc, argv); + ct->cfunc(blk, argc, argv); } static const cmdinfo_t *find_command(const char *cmd) @@ -634,7 +633,7 @@ static void read_help(void) "\n"); } -static int read_f(BlockBackend *blk, int argc, char **argv); +static void read_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t read_cmd = { .name = "read", @@ -647,7 +646,7 @@ static const cmdinfo_t read_cmd = { .help = read_help, }; -static int read_f(BlockBackend *blk, int argc, char **argv) +static void read_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false, vflag = false; @@ -674,7 +673,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) pattern_count = cvtnum(optarg); if (pattern_count < 0) { print_cvtnum_err(pattern_count, optarg); - return 0; + return; } break; case 'p': @@ -684,7 +683,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) Pflag = true; pattern = parse_pattern(optarg); if (pattern < 0) { - return 0; + return; } break; case 'q': @@ -695,40 +694,43 @@ static int read_f(BlockBackend *blk, int argc, char **argv) pattern_offset = cvtnum(optarg); if (pattern_offset < 0) { print_cvtnum_err(pattern_offset, optarg); - return 0; + return; } break; case 'v': vflag = true; break; default: - return qemuio_command_usage(&read_cmd); + qemuio_command_usage(&read_cmd); + return; } } if (optind != argc - 2) { - return qemuio_command_usage(&read_cmd); + qemuio_command_usage(&read_cmd); + return; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return 0; + return; } optind++; count = cvtnum(argv[optind]); if (count < 0) { print_cvtnum_err(count, argv[optind]); - return 0; + return; } else if (count > BDRV_REQUEST_MAX_BYTES) { printf("length cannot exceed %" PRIu64 ", given %s\n", (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); - return 0; + return; } if (!Pflag && (lflag || sflag)) { - return qemuio_command_usage(&read_cmd); + qemuio_command_usage(&read_cmd); + return; } if (!lflag) { @@ -737,19 +739,19 @@ static int read_f(BlockBackend *blk, int argc, char **argv) if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) { printf("pattern verification range exceeds end of read data\n"); - return 0; + return; } if (bflag) { if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", offset); - return 0; + return; } if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { printf("%"PRId64" is not a sector-aligned value for 'count'\n", count); - return 0; + return; } } @@ -793,8 +795,6 @@ static int read_f(BlockBackend *blk, int argc, char **argv) out: qemu_io_free(buf); - - return 0; } static void readv_help(void) @@ -816,7 +816,7 @@ static void readv_help(void) "\n"); } -static int readv_f(BlockBackend *blk, int argc, char **argv); +static void readv_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t readv_cmd = { .name = "readv", @@ -828,7 +828,7 @@ static const cmdinfo_t readv_cmd = { .help = readv_help, }; -static int readv_f(BlockBackend *blk, int argc, char **argv) +static void readv_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false, vflag = false; @@ -851,7 +851,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) Pflag = true; pattern = parse_pattern(optarg); if (pattern < 0) { - return 0; + return; } break; case 'q': @@ -861,26 +861,28 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) vflag = true; break; default: - return qemuio_command_usage(&readv_cmd); + qemuio_command_usage(&readv_cmd); + return; } } if (optind > argc - 2) { - return qemuio_command_usage(&readv_cmd); + qemuio_command_usage(&readv_cmd); + return; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return 0; + return; } optind++; nr_iov = argc - optind; buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab); if (buf == NULL) { - return 0; + return; } gettimeofday(&t1, NULL); @@ -917,7 +919,6 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) out: qemu_iovec_destroy(&qiov); qemu_io_free(buf); - return 0; } static void write_help(void) @@ -943,7 +944,7 @@ static void write_help(void) "\n"); } -static int write_f(BlockBackend *blk, int argc, char **argv); +static void write_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t write_cmd = { .name = "write", @@ -957,7 +958,7 @@ static const cmdinfo_t write_cmd = { .help = write_help, }; -static int write_f(BlockBackend *blk, int argc, char **argv) +static void write_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false, bflag = false; @@ -992,7 +993,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) Pflag = true; pattern = parse_pattern(optarg); if (pattern < 0) { - return 0; + return; } break; case 'q': @@ -1005,62 +1006,64 @@ static int write_f(BlockBackend *blk, int argc, char **argv) zflag = true; break; default: - return qemuio_command_usage(&write_cmd); + qemuio_command_usage(&write_cmd); + return; } } if (optind != argc - 2) { - return qemuio_command_usage(&write_cmd); + qemuio_command_usage(&write_cmd); + return; } if (bflag && zflag) { printf("-b and -z cannot be specified at the same time\n"); - return 0; + return; } if ((flags & BDRV_REQ_FUA) && (bflag || cflag)) { printf("-f and -b or -c cannot be specified at the same time\n"); - return 0; + return; } if ((flags & BDRV_REQ_MAY_UNMAP) && !zflag) { printf("-u requires -z to be specified\n"); - return 0; + return; } if (zflag && Pflag) { printf("-z and -P cannot be specified at the same time\n"); - return 0; + return; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return 0; + return; } optind++; count = cvtnum(argv[optind]); if (count < 0) { print_cvtnum_err(count, argv[optind]); - return 0; + return; } else if (count > BDRV_REQUEST_MAX_BYTES) { printf("length cannot exceed %" PRIu64 ", given %s\n", (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); - return 0; + return; } if (bflag || cflag) { if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", offset); - return 0; + return; } if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { printf("%"PRId64" is not a sector-aligned value for 'count'\n", count); - return 0; + return; } } @@ -1097,8 +1100,6 @@ out: if (!zflag) { qemu_io_free(buf); } - - return 0; } static void @@ -1120,7 +1121,7 @@ writev_help(void) "\n"); } -static int writev_f(BlockBackend *blk, int argc, char **argv); +static void writev_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t writev_cmd = { .name = "writev", @@ -1133,7 +1134,7 @@ static const cmdinfo_t writev_cmd = { .help = writev_help, }; -static int writev_f(BlockBackend *blk, int argc, char **argv) +static void writev_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false; @@ -1161,29 +1162,31 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) case 'P': pattern = parse_pattern(optarg); if (pattern < 0) { - return 0; + return; } break; default: - return qemuio_command_usage(&writev_cmd); + qemuio_command_usage(&writev_cmd); + return; } } if (optind > argc - 2) { - return qemuio_command_usage(&writev_cmd); + qemuio_command_usage(&writev_cmd); + return; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return 0; + return; } optind++; nr_iov = argc - optind; buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern); if (buf == NULL) { - return 0; + return; } gettimeofday(&t1, NULL); @@ -1205,7 +1208,6 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) out: qemu_iovec_destroy(&qiov); qemu_io_free(buf); - return 0; } struct aio_ctx { @@ -1320,7 +1322,7 @@ static void aio_read_help(void) "\n"); } -static int aio_read_f(BlockBackend *blk, int argc, char **argv); +static void aio_read_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t aio_read_cmd = { .name = "aio_read", @@ -1332,7 +1334,7 @@ static const cmdinfo_t aio_read_cmd = { .help = aio_read_help, }; -static int aio_read_f(BlockBackend *blk, int argc, char **argv) +static void aio_read_f(BlockBackend *blk, int argc, char **argv) { int nr_iov, c; struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); @@ -1348,14 +1350,14 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) ctx->pattern = parse_pattern(optarg); if (ctx->pattern < 0) { g_free(ctx); - return 0; + return; } break; case 'i': printf("injecting invalid read request\n"); block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); g_free(ctx); - return 0; + return; case 'q': ctx->qflag = true; break; @@ -1364,20 +1366,22 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) break; default: g_free(ctx); - return qemuio_command_usage(&aio_read_cmd); + qemuio_command_usage(&aio_read_cmd); + return; } } if (optind > argc - 2) { g_free(ctx); - return qemuio_command_usage(&aio_read_cmd); + qemuio_command_usage(&aio_read_cmd); + return; } ctx->offset = cvtnum(argv[optind]); if (ctx->offset < 0) { print_cvtnum_err(ctx->offset, argv[optind]); g_free(ctx); - return 0; + return; } optind++; @@ -1386,14 +1390,13 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) if (ctx->buf == NULL) { block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); g_free(ctx); - return 0; + return; } gettimeofday(&ctx->t1, NULL); block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, BLOCK_ACCT_READ); blk_aio_preadv(blk, ctx->offset, &ctx->qiov, 0, aio_read_done, ctx); - return 0; } static void aio_write_help(void) @@ -1420,7 +1423,7 @@ static void aio_write_help(void) "\n"); } -static int aio_write_f(BlockBackend *blk, int argc, char **argv); +static void aio_write_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t aio_write_cmd = { .name = "aio_write", @@ -1433,7 +1436,7 @@ static const cmdinfo_t aio_write_cmd = { .help = aio_write_help, }; -static int aio_write_f(BlockBackend *blk, int argc, char **argv) +static void aio_write_f(BlockBackend *blk, int argc, char **argv) { int nr_iov, c; int pattern = 0xcd; @@ -1459,51 +1462,53 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) pattern = parse_pattern(optarg); if (pattern < 0) { g_free(ctx); - return 0; + return; } break; case 'i': printf("injecting invalid write request\n"); block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); g_free(ctx); - return 0; + return; case 'z': ctx->zflag = true; break; default: g_free(ctx); - return qemuio_command_usage(&aio_write_cmd); + qemuio_command_usage(&aio_write_cmd); + return; } } if (optind > argc - 2) { g_free(ctx); - return qemuio_command_usage(&aio_write_cmd); + qemuio_command_usage(&aio_write_cmd); + return; } if (ctx->zflag && optind != argc - 2) { printf("-z supports only a single length parameter\n"); g_free(ctx); - return 0; + return; } if ((flags & BDRV_REQ_MAY_UNMAP) && !ctx->zflag) { printf("-u requires -z to be specified\n"); g_free(ctx); - return 0; + return; } if (ctx->zflag && ctx->Pflag) { printf("-z and -P cannot be specified at the same time\n"); g_free(ctx); - return 0; + return; } ctx->offset = cvtnum(argv[optind]); if (ctx->offset < 0) { print_cvtnum_err(ctx->offset, argv[optind]); g_free(ctx); - return 0; + return; } optind++; @@ -1512,7 +1517,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) if (count < 0) { print_cvtnum_err(count, argv[optind]); g_free(ctx); - return 0; + return; } ctx->qiov.size = count; @@ -1525,7 +1530,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) if (ctx->buf == NULL) { block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); g_free(ctx); - return 0; + return; } gettimeofday(&ctx->t1, NULL); @@ -1535,16 +1540,14 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) blk_aio_pwritev(blk, ctx->offset, &ctx->qiov, flags, aio_write_done, ctx); } - return 0; } -static int aio_flush_f(BlockBackend *blk, int argc, char **argv) +static void aio_flush_f(BlockBackend *blk, int argc, char **argv) { BlockAcctCookie cookie; block_acct_start(blk_get_stats(blk), &cookie, 0, BLOCK_ACCT_FLUSH); blk_drain_all(); block_acct_done(blk_get_stats(blk), &cookie); - return 0; } static const cmdinfo_t aio_flush_cmd = { @@ -1553,10 +1556,9 @@ static const cmdinfo_t aio_flush_cmd = { .oneline = "completes all outstanding aio requests" }; -static int flush_f(BlockBackend *blk, int argc, char **argv) +static void flush_f(BlockBackend *blk, int argc, char **argv) { blk_flush(blk); - return 0; } static const cmdinfo_t flush_cmd = { @@ -1566,7 +1568,7 @@ static const cmdinfo_t flush_cmd = { .oneline = "flush all in-core file state to disk", }; -static int truncate_f(BlockBackend *blk, int argc, char **argv) +static void truncate_f(BlockBackend *blk, int argc, char **argv) { Error *local_err = NULL; int64_t offset; @@ -1575,16 +1577,14 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv) offset = cvtnum(argv[1]); if (offset < 0) { print_cvtnum_err(offset, argv[1]); - return 0; + return; } ret = blk_truncate(blk, offset, PREALLOC_MODE_OFF, &local_err); if (ret < 0) { error_report_err(local_err); - return 0; + return; } - - return 0; } static const cmdinfo_t truncate_cmd = { @@ -1598,7 +1598,7 @@ static const cmdinfo_t truncate_cmd = { .oneline = "truncates the current file at the given offset", }; -static int length_f(BlockBackend *blk, int argc, char **argv) +static void length_f(BlockBackend *blk, int argc, char **argv) { int64_t size; char s1[64]; @@ -1606,12 +1606,11 @@ static int length_f(BlockBackend *blk, int argc, char **argv) size = blk_getlength(blk); if (size < 0) { printf("getlength: %s\n", strerror(-size)); - return 0; + return; } cvtstr(size, s1, sizeof(s1)); printf("%s\n", s1); - return 0; } @@ -1623,7 +1622,7 @@ static const cmdinfo_t length_cmd = { }; -static int info_f(BlockBackend *blk, int argc, char **argv) +static void info_f(BlockBackend *blk, int argc, char **argv) { BlockDriverState *bs = blk_bs(blk); BlockDriverInfo bdi; @@ -1640,7 +1639,7 @@ static int info_f(BlockBackend *blk, int argc, char **argv) ret = bdrv_get_info(bs, &bdi); if (ret) { - return 0; + return; } cvtstr(bdi.cluster_size, s1, sizeof(s1)); @@ -1655,8 +1654,6 @@ static int info_f(BlockBackend *blk, int argc, char **argv) bdrv_image_info_specific_dump(fprintf, stdout, spec_info); qapi_free_ImageInfoSpecific(spec_info); } - - return 0; } @@ -1683,7 +1680,7 @@ static void discard_help(void) "\n"); } -static int discard_f(BlockBackend *blk, int argc, char **argv); +static void discard_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t discard_cmd = { .name = "discard", @@ -1697,7 +1694,7 @@ static const cmdinfo_t discard_cmd = { .help = discard_help, }; -static int discard_f(BlockBackend *blk, int argc, char **argv) +static void discard_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false; @@ -1713,30 +1710,32 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) qflag = true; break; default: - return qemuio_command_usage(&discard_cmd); + qemuio_command_usage(&discard_cmd); + return; } } if (optind != argc - 2) { - return qemuio_command_usage(&discard_cmd); + qemuio_command_usage(&discard_cmd); + return; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return 0; + return; } optind++; bytes = cvtnum(argv[optind]); if (bytes < 0) { print_cvtnum_err(bytes, argv[optind]); - return 0; + return; } else if (bytes >> BDRV_SECTOR_BITS > BDRV_REQUEST_MAX_SECTORS) { printf("length cannot exceed %"PRIu64", given %s\n", (uint64_t)BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS, argv[optind]); - return 0; + return; } gettimeofday(&t1, NULL); @@ -1745,7 +1744,7 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) if (ret < 0) { printf("discard failed: %s\n", strerror(-ret)); - goto out; + return; } /* Finally, report back -- -C gives a parsable format */ @@ -1753,12 +1752,9 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) t2 = tsub(t2, t1); print_report("discard", &t2, offset, bytes, bytes, 1, Cflag); } - -out: - return 0; } -static int alloc_f(BlockBackend *blk, int argc, char **argv) +static void alloc_f(BlockBackend *blk, int argc, char **argv) { BlockDriverState *bs = blk_bs(blk); int64_t offset, start, remaining, count; @@ -1769,14 +1765,14 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv) start = offset = cvtnum(argv[1]); if (offset < 0) { print_cvtnum_err(offset, argv[1]); - return 0; + return; } if (argc == 3) { count = cvtnum(argv[2]); if (count < 0) { print_cvtnum_err(count, argv[2]); - return 0; + return; } } else { count = BDRV_SECTOR_SIZE; @@ -1788,7 +1784,7 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv) ret = bdrv_is_allocated(bs, offset, remaining, &num); if (ret < 0) { printf("is_allocated failed: %s\n", strerror(-ret)); - return 0; + return; } offset += num; remaining -= num; @@ -1805,7 +1801,6 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv) printf("%"PRId64"/%"PRId64" bytes allocated at offset %s\n", sum_alloc, count, s1); - return 0; } static const cmdinfo_t alloc_cmd = { @@ -1851,7 +1846,7 @@ static int map_is_allocated(BlockDriverState *bs, int64_t offset, return firstret; } -static int map_f(BlockBackend *blk, int argc, char **argv) +static void map_f(BlockBackend *blk, int argc, char **argv) { int64_t offset, bytes; char s1[64], s2[64]; @@ -1863,17 +1858,17 @@ static int map_f(BlockBackend *blk, int argc, char **argv) bytes = blk_getlength(blk); if (bytes < 0) { error_report("Failed to query image length: %s", strerror(-bytes)); - return 0; + return; } while (bytes) { ret = map_is_allocated(blk_bs(blk), offset, bytes, &num); if (ret < 0) { error_report("Failed to get allocation status: %s", strerror(-ret)); - return 0; + return; } else if (!num) { error_report("Unexpected end of image"); - return 0; + return; } retstr = ret ? " allocated" : "not allocated"; @@ -1885,8 +1880,6 @@ static int map_f(BlockBackend *blk, int argc, char **argv) offset += num; bytes -= num; } - - return 0; } static const cmdinfo_t map_cmd = { @@ -1914,7 +1907,7 @@ static void reopen_help(void) "\n"); } -static int reopen_f(BlockBackend *blk, int argc, char **argv); +static void reopen_f(BlockBackend *blk, int argc, char **argv); static QemuOptsList reopen_opts = { .name = "reopen", @@ -1936,7 +1929,7 @@ static const cmdinfo_t reopen_cmd = { .help = reopen_help, }; -static int reopen_f(BlockBackend *blk, int argc, char **argv) +static void reopen_f(BlockBackend *blk, int argc, char **argv) { BlockDriverState *bs = blk_bs(blk); QemuOpts *qopts; @@ -1954,19 +1947,19 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) case 'c': if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { error_report("Invalid cache option: %s", optarg); - return 0; + return; } break; case 'o': if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) { qemu_opts_reset(&reopen_opts); - return 0; + return; } break; case 'r': if (has_rw_option) { error_report("Only one -r/-w option may be given"); - return 0; + return; } flags &= ~BDRV_O_RDWR; has_rw_option = true; @@ -1974,20 +1967,22 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) case 'w': if (has_rw_option) { error_report("Only one -r/-w option may be given"); - return 0; + return; } flags |= BDRV_O_RDWR; has_rw_option = true; break; default: qemu_opts_reset(&reopen_opts); - return qemuio_command_usage(&reopen_cmd); + qemuio_command_usage(&reopen_cmd); + return; } } if (optind != argc) { qemu_opts_reset(&reopen_opts); - return qemuio_command_usage(&reopen_cmd); + qemuio_command_usage(&reopen_cmd); + return; } if (writethrough != blk_enable_write_cache(blk) && @@ -1995,7 +1990,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) { error_report("Cannot change cache.writeback: Device attached"); qemu_opts_reset(&reopen_opts); - return 0; + return; } if (!(flags & BDRV_O_RDWR)) { @@ -2024,11 +2019,9 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) } else { blk_set_enable_write_cache(blk, !writethrough); } - - return 0; } -static int break_f(BlockBackend *blk, int argc, char **argv) +static void break_f(BlockBackend *blk, int argc, char **argv) { int ret; @@ -2036,11 +2029,9 @@ static int break_f(BlockBackend *blk, int argc, char **argv) if (ret < 0) { printf("Could not set breakpoint: %s\n", strerror(-ret)); } - - return 0; } -static int remove_break_f(BlockBackend *blk, int argc, char **argv) +static void remove_break_f(BlockBackend *blk, int argc, char **argv) { int ret; @@ -2048,8 +2039,6 @@ static int remove_break_f(BlockBackend *blk, int argc, char **argv) if (ret < 0) { printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret)); } - - return 0; } static const cmdinfo_t break_cmd = { @@ -2071,7 +2060,7 @@ static const cmdinfo_t remove_break_cmd = { .oneline = "remove a breakpoint by tag", }; -static int resume_f(BlockBackend *blk, int argc, char **argv) +static void resume_f(BlockBackend *blk, int argc, char **argv) { int ret; @@ -2079,8 +2068,6 @@ static int resume_f(BlockBackend *blk, int argc, char **argv) if (ret < 0) { printf("Could not resume request: %s\n", strerror(-ret)); } - - return 0; } static const cmdinfo_t resume_cmd = { @@ -2092,13 +2079,11 @@ static const cmdinfo_t resume_cmd = { .oneline = "resumes the request tagged as tag", }; -static int wait_break_f(BlockBackend *blk, int argc, char **argv) +static void wait_break_f(BlockBackend *blk, int argc, char **argv) { while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) { aio_poll(blk_get_aio_context(blk), true); } - - return 0; } static const cmdinfo_t wait_break_cmd = { @@ -2110,7 +2095,7 @@ static const cmdinfo_t wait_break_cmd = { .oneline = "waits for the suspension of a request", }; -static int abort_f(BlockBackend *blk, int argc, char **argv) +static void abort_f(BlockBackend *blk, int argc, char **argv) { abort(); } @@ -2136,7 +2121,7 @@ static void sigraise_help(void) "\n", SIGTERM); } -static int sigraise_f(BlockBackend *blk, int argc, char **argv); +static void sigraise_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t sigraise_cmd = { .name = "sigraise", @@ -2149,16 +2134,16 @@ static const cmdinfo_t sigraise_cmd = { .help = sigraise_help, }; -static int sigraise_f(BlockBackend *blk, int argc, char **argv) +static void sigraise_f(BlockBackend *blk, int argc, char **argv) { int64_t sig = cvtnum(argv[1]); if (sig < 0) { print_cvtnum_err(sig, argv[1]); - return 0; + return; } else if (sig > NSIG) { printf("signal argument '%s' is too large to be a valid signal\n", argv[1]); - return 0; + return; } /* Using raise() to kill this process does not necessarily flush all open @@ -2168,7 +2153,6 @@ static int sigraise_f(BlockBackend *blk, int argc, char **argv) fflush(stderr); raise(sig); - return 0; } static void sleep_cb(void *opaque) @@ -2177,7 +2161,7 @@ static void sleep_cb(void *opaque) *expired = true; } -static int sleep_f(BlockBackend *blk, int argc, char **argv) +static void sleep_f(BlockBackend *blk, int argc, char **argv) { char *endptr; long ms; @@ -2187,7 +2171,7 @@ static int sleep_f(BlockBackend *blk, int argc, char **argv) ms = strtol(argv[1], &endptr, 0); if (ms < 0 || *endptr != '\0') { printf("%s is not a valid number\n", argv[1]); - return 0; + return; } timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired); @@ -2198,8 +2182,6 @@ static int sleep_f(BlockBackend *blk, int argc, char **argv) } timer_free(timer); - - return 0; } static const cmdinfo_t sleep_cmd = { @@ -2246,23 +2228,22 @@ static void help_all(void) printf("\nUse 'help commandname' for extended help.\n"); } -static int help_f(BlockBackend *blk, int argc, char **argv) +static void help_f(BlockBackend *blk, int argc, char **argv) { const cmdinfo_t *ct; if (argc == 1) { help_all(); - return 0; + return; } ct = find_command(argv[1]); if (ct == NULL) { printf("command %s not found\n", argv[1]); - return 0; + return; } help_onecmd(argv[1], ct); - return 0; } static const cmdinfo_t help_cmd = { @@ -2276,14 +2257,13 @@ static const cmdinfo_t help_cmd = { .oneline = "help for one or all commands", }; -bool qemuio_command(BlockBackend *blk, const char *cmd) +void qemuio_command(BlockBackend *blk, const char *cmd) { AioContext *ctx; char *input; const cmdinfo_t *ct; char **v; int c; - bool done = false; input = g_strdup(cmd); v = breakline(input, &c); @@ -2292,7 +2272,7 @@ bool qemuio_command(BlockBackend *blk, const char *cmd) if (ct) { ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context(); aio_context_acquire(ctx); - done = command(blk, ct, c, v); + command(blk, ct, c, v); aio_context_release(ctx); } else { fprintf(stderr, "command \"%s\" not found\n", v[0]); @@ -2300,8 +2280,6 @@ bool qemuio_command(BlockBackend *blk, const char *cmd) } g_free(input); g_free(v); - - return done; } static void __attribute((constructor)) init_qemuio_commands(void) diff --git a/qemu-io.c b/qemu-io.c index 73c638ff8b..02a67c929a 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -37,6 +37,7 @@ static char *progname; static BlockBackend *qemuio_blk; +static bool quit_qemu_io; /* qemu-io commands passed using -c */ static int ncmdline; @@ -65,11 +66,10 @@ static int get_eof_char(void) #endif } -static int close_f(BlockBackend *blk, int argc, char **argv) +static void close_f(BlockBackend *blk, int argc, char **argv) { blk_unref(qemuio_blk); qemuio_blk = NULL; - return 0; } static const cmdinfo_t close_cmd = { @@ -136,7 +136,7 @@ static void open_help(void) "\n"); } -static int open_f(BlockBackend *blk, int argc, char **argv); +static void open_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t open_cmd = { .name = "open", @@ -160,7 +160,7 @@ static QemuOptsList empty_opts = { }, }; -static int open_f(BlockBackend *blk, int argc, char **argv) +static void open_f(BlockBackend *blk, int argc, char **argv) { int flags = BDRV_O_UNMAP; int readonly = 0; @@ -192,25 +192,25 @@ static int open_f(BlockBackend *blk, int argc, char **argv) if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { error_report("Invalid cache option: %s", optarg); qemu_opts_reset(&empty_opts); - return 0; + return; } break; case 'd': if (bdrv_parse_discard_flags(optarg, &flags) < 0) { error_report("Invalid discard option: %s", optarg); qemu_opts_reset(&empty_opts); - return 0; + return; } break; case 'o': if (imageOpts) { printf("--image-opts and 'open -o' are mutually exclusive\n"); qemu_opts_reset(&empty_opts); - return 0; + return; } if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) { qemu_opts_reset(&empty_opts); - return 0; + return; } break; case 'U': @@ -218,7 +218,8 @@ static int open_f(BlockBackend *blk, int argc, char **argv) break; default: qemu_opts_reset(&empty_opts); - return qemuio_command_usage(&open_cmd); + qemuio_command_usage(&open_cmd); + return; } } @@ -229,7 +230,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv) if (imageOpts && (optind == argc - 1)) { if (!qemu_opts_parse_noisily(&empty_opts, argv[optind], false)) { qemu_opts_reset(&empty_opts); - return 0; + return; } optind++; } @@ -246,12 +247,11 @@ static int open_f(BlockBackend *blk, int argc, char **argv) qobject_unref(opts); qemuio_command_usage(&open_cmd); } - return 0; } -static int quit_f(BlockBackend *blk, int argc, char **argv) +static void quit_f(BlockBackend *blk, int argc, char **argv) { - return 1; + quit_qemu_io = true; } static const cmdinfo_t quit_cmd = { @@ -392,18 +392,18 @@ static void prep_fetchline(void *opaque) static void command_loop(void) { - int i, done = 0, fetchable = 0, prompted = 0; + int i, fetchable = 0, prompted = 0; char *input; - for (i = 0; !done && i < ncmdline; i++) { - done = qemuio_command(qemuio_blk, cmdline[i]); + for (i = 0; !quit_qemu_io && i < ncmdline; i++) { + qemuio_command(qemuio_blk, cmdline[i]); } if (cmdline) { g_free(cmdline); return; } - while (!done) { + while (!quit_qemu_io) { if (!prompted) { printf("%s", get_prompt()); fflush(stdout); @@ -421,7 +421,7 @@ static void command_loop(void) if (input == NULL) { break; } - done = qemuio_command(qemuio_blk, input); + qemuio_command(qemuio_blk, input); g_free(input); prompted = 0; From b32d7a39af488d280ce5f02a2ed94871d696f87a Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 21:42:59 +0200 Subject: [PATCH 1144/2380] qemu-io: Let command functions return error code This is basically what everything else in the qemu code base does, so we can do it here, too. Signed-off-by: Max Reitz Reviewed-by: Eric Blake Message-id: 20180509194302.21585-3-mreitz@redhat.com Signed-off-by: Max Reitz --- include/qemu-io.h | 9 +- qemu-io-cmds.c | 346 +++++++++++++++++++++++++++------------------- qemu-io.c | 34 +++-- 3 files changed, 232 insertions(+), 157 deletions(-) diff --git a/include/qemu-io.h b/include/qemu-io.h index 06cdfbf660..7433239372 100644 --- a/include/qemu-io.h +++ b/include/qemu-io.h @@ -22,7 +22,12 @@ #define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ -typedef void (*cfunc_t)(BlockBackend *blk, int argc, char **argv); +/* Implement a qemu-io command. + * Operate on @blk using @argc/@argv as the command's arguments, and + * return 0 on success or negative errno on failure. + */ +typedef int (*cfunc_t)(BlockBackend *blk, int argc, char **argv); + typedef void (*helpfunc_t)(void); typedef struct cmdinfo { @@ -41,7 +46,7 @@ typedef struct cmdinfo { extern bool qemuio_misalign; -void qemuio_command(BlockBackend *blk, const char *cmd); +int qemuio_command(BlockBackend *blk, const char *cmd); void qemuio_add_command(const cmdinfo_t *ci); void qemuio_command_usage(const cmdinfo_t *ci); diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index c2fbaae0fd..5bf5f28178 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -65,13 +65,13 @@ static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct) return 1; } -static void command(BlockBackend *blk, const cmdinfo_t *ct, int argc, - char **argv) +static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, + char **argv) { char *cmd = argv[0]; if (!init_check_command(blk, ct)) { - return; + return -EINVAL; } if (argc - 1 < ct->argmin || (ct->argmax != -1 && argc - 1 > ct->argmax)) { @@ -88,7 +88,7 @@ static void command(BlockBackend *blk, const cmdinfo_t *ct, int argc, "bad argument count %d to %s, expected between %d and %d arguments\n", argc-1, cmd, ct->argmin, ct->argmax); } - return; + return -EINVAL; } /* Request additional permissions if necessary for this command. The caller @@ -108,13 +108,13 @@ static void command(BlockBackend *blk, const cmdinfo_t *ct, int argc, ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err); if (ret < 0) { error_report_err(local_err); - return; + return ret; } } } optind = 0; - ct->cfunc(blk, argc, argv); + return ct->cfunc(blk, argc, argv); } static const cmdinfo_t *find_command(const char *cmd) @@ -633,7 +633,7 @@ static void read_help(void) "\n"); } -static void read_f(BlockBackend *blk, int argc, char **argv); +static int read_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t read_cmd = { .name = "read", @@ -646,12 +646,12 @@ static const cmdinfo_t read_cmd = { .help = read_help, }; -static void read_f(BlockBackend *blk, int argc, char **argv) +static int read_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false, vflag = false; bool Pflag = false, sflag = false, lflag = false, bflag = false; - int c, cnt; + int c, cnt, ret; char *buf; int64_t offset; int64_t count; @@ -673,7 +673,7 @@ static void read_f(BlockBackend *blk, int argc, char **argv) pattern_count = cvtnum(optarg); if (pattern_count < 0) { print_cvtnum_err(pattern_count, optarg); - return; + return pattern_count; } break; case 'p': @@ -683,7 +683,7 @@ static void read_f(BlockBackend *blk, int argc, char **argv) Pflag = true; pattern = parse_pattern(optarg); if (pattern < 0) { - return; + return -EINVAL; } break; case 'q': @@ -694,7 +694,7 @@ static void read_f(BlockBackend *blk, int argc, char **argv) pattern_offset = cvtnum(optarg); if (pattern_offset < 0) { print_cvtnum_err(pattern_offset, optarg); - return; + return pattern_offset; } break; case 'v': @@ -702,35 +702,35 @@ static void read_f(BlockBackend *blk, int argc, char **argv) break; default: qemuio_command_usage(&read_cmd); - return; + return -EINVAL; } } if (optind != argc - 2) { qemuio_command_usage(&read_cmd); - return; + return -EINVAL; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return; + return offset; } optind++; count = cvtnum(argv[optind]); if (count < 0) { print_cvtnum_err(count, argv[optind]); - return; + return count; } else if (count > BDRV_REQUEST_MAX_BYTES) { printf("length cannot exceed %" PRIu64 ", given %s\n", (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); - return; + return -EINVAL; } if (!Pflag && (lflag || sflag)) { qemuio_command_usage(&read_cmd); - return; + return -EINVAL; } if (!lflag) { @@ -739,19 +739,19 @@ static void read_f(BlockBackend *blk, int argc, char **argv) if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) { printf("pattern verification range exceeds end of read data\n"); - return; + return -EINVAL; } if (bflag) { if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", offset); - return; + return -EINVAL; } if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { printf("%"PRId64" is not a sector-aligned value for 'count'\n", count); - return; + return -EINVAL; } } @@ -759,16 +759,19 @@ static void read_f(BlockBackend *blk, int argc, char **argv) gettimeofday(&t1, NULL); if (bflag) { - cnt = do_load_vmstate(blk, buf, offset, count, &total); + ret = do_load_vmstate(blk, buf, offset, count, &total); } else { - cnt = do_pread(blk, buf, offset, count, &total); + ret = do_pread(blk, buf, offset, count, &total); } gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("read failed: %s\n", strerror(-cnt)); + if (ret < 0) { + printf("read failed: %s\n", strerror(-ret)); goto out; } + cnt = ret; + + ret = 0; if (Pflag) { void *cmp_buf = g_malloc(pattern_count); @@ -777,6 +780,7 @@ static void read_f(BlockBackend *blk, int argc, char **argv) printf("Pattern verification failed at offset %" PRId64 ", %"PRId64" bytes\n", offset + pattern_offset, pattern_count); + ret = -EINVAL; } g_free(cmp_buf); } @@ -795,6 +799,7 @@ static void read_f(BlockBackend *blk, int argc, char **argv) out: qemu_io_free(buf); + return ret; } static void readv_help(void) @@ -816,7 +821,7 @@ static void readv_help(void) "\n"); } -static void readv_f(BlockBackend *blk, int argc, char **argv); +static int readv_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t readv_cmd = { .name = "readv", @@ -828,11 +833,11 @@ static const cmdinfo_t readv_cmd = { .help = readv_help, }; -static void readv_f(BlockBackend *blk, int argc, char **argv) +static int readv_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false, vflag = false; - int c, cnt; + int c, cnt, ret; char *buf; int64_t offset; /* Some compilers get confused and warn if this is not initialized. */ @@ -851,7 +856,7 @@ static void readv_f(BlockBackend *blk, int argc, char **argv) Pflag = true; pattern = parse_pattern(optarg); if (pattern < 0) { - return; + return -EINVAL; } break; case 'q': @@ -862,37 +867,40 @@ static void readv_f(BlockBackend *blk, int argc, char **argv) break; default: qemuio_command_usage(&readv_cmd); - return; + return -EINVAL; } } if (optind > argc - 2) { qemuio_command_usage(&readv_cmd); - return; + return -EINVAL; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return; + return offset; } optind++; nr_iov = argc - optind; buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab); if (buf == NULL) { - return; + return -EINVAL; } gettimeofday(&t1, NULL); - cnt = do_aio_readv(blk, &qiov, offset, &total); + ret = do_aio_readv(blk, &qiov, offset, &total); gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("readv failed: %s\n", strerror(-cnt)); + if (ret < 0) { + printf("readv failed: %s\n", strerror(-ret)); goto out; } + cnt = ret; + + ret = 0; if (Pflag) { void *cmp_buf = g_malloc(qiov.size); @@ -900,6 +908,7 @@ static void readv_f(BlockBackend *blk, int argc, char **argv) if (memcmp(buf, cmp_buf, qiov.size)) { printf("Pattern verification failed at offset %" PRId64 ", %zd bytes\n", offset, qiov.size); + ret = -EINVAL; } g_free(cmp_buf); } @@ -919,6 +928,7 @@ static void readv_f(BlockBackend *blk, int argc, char **argv) out: qemu_iovec_destroy(&qiov); qemu_io_free(buf); + return ret; } static void write_help(void) @@ -944,7 +954,7 @@ static void write_help(void) "\n"); } -static void write_f(BlockBackend *blk, int argc, char **argv); +static int write_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t write_cmd = { .name = "write", @@ -958,13 +968,13 @@ static const cmdinfo_t write_cmd = { .help = write_help, }; -static void write_f(BlockBackend *blk, int argc, char **argv) +static int write_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false, bflag = false; bool Pflag = false, zflag = false, cflag = false; int flags = 0; - int c, cnt; + int c, cnt, ret; char *buf = NULL; int64_t offset; int64_t count; @@ -993,7 +1003,7 @@ static void write_f(BlockBackend *blk, int argc, char **argv) Pflag = true; pattern = parse_pattern(optarg); if (pattern < 0) { - return; + return -EINVAL; } break; case 'q': @@ -1007,63 +1017,63 @@ static void write_f(BlockBackend *blk, int argc, char **argv) break; default: qemuio_command_usage(&write_cmd); - return; + return -EINVAL; } } if (optind != argc - 2) { qemuio_command_usage(&write_cmd); - return; + return -EINVAL; } if (bflag && zflag) { printf("-b and -z cannot be specified at the same time\n"); - return; + return -EINVAL; } if ((flags & BDRV_REQ_FUA) && (bflag || cflag)) { printf("-f and -b or -c cannot be specified at the same time\n"); - return; + return -EINVAL; } if ((flags & BDRV_REQ_MAY_UNMAP) && !zflag) { printf("-u requires -z to be specified\n"); - return; + return -EINVAL; } if (zflag && Pflag) { printf("-z and -P cannot be specified at the same time\n"); - return; + return -EINVAL; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return; + return offset; } optind++; count = cvtnum(argv[optind]); if (count < 0) { print_cvtnum_err(count, argv[optind]); - return; + return count; } else if (count > BDRV_REQUEST_MAX_BYTES) { printf("length cannot exceed %" PRIu64 ", given %s\n", (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); - return; + return -EINVAL; } if (bflag || cflag) { if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", offset); - return; + return -EINVAL; } if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { printf("%"PRId64" is not a sector-aligned value for 'count'\n", count); - return; + return -EINVAL; } } @@ -1073,20 +1083,23 @@ static void write_f(BlockBackend *blk, int argc, char **argv) gettimeofday(&t1, NULL); if (bflag) { - cnt = do_save_vmstate(blk, buf, offset, count, &total); + ret = do_save_vmstate(blk, buf, offset, count, &total); } else if (zflag) { - cnt = do_co_pwrite_zeroes(blk, offset, count, flags, &total); + ret = do_co_pwrite_zeroes(blk, offset, count, flags, &total); } else if (cflag) { - cnt = do_write_compressed(blk, buf, offset, count, &total); + ret = do_write_compressed(blk, buf, offset, count, &total); } else { - cnt = do_pwrite(blk, buf, offset, count, flags, &total); + ret = do_pwrite(blk, buf, offset, count, flags, &total); } gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("write failed: %s\n", strerror(-cnt)); + if (ret < 0) { + printf("write failed: %s\n", strerror(-ret)); goto out; } + cnt = ret; + + ret = 0; if (qflag) { goto out; @@ -1100,6 +1113,7 @@ out: if (!zflag) { qemu_io_free(buf); } + return ret; } static void @@ -1121,7 +1135,7 @@ writev_help(void) "\n"); } -static void writev_f(BlockBackend *blk, int argc, char **argv); +static int writev_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t writev_cmd = { .name = "writev", @@ -1134,12 +1148,12 @@ static const cmdinfo_t writev_cmd = { .help = writev_help, }; -static void writev_f(BlockBackend *blk, int argc, char **argv) +static int writev_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false; int flags = 0; - int c, cnt; + int c, cnt, ret; char *buf; int64_t offset; /* Some compilers get confused and warn if this is not initialized. */ @@ -1162,41 +1176,44 @@ static void writev_f(BlockBackend *blk, int argc, char **argv) case 'P': pattern = parse_pattern(optarg); if (pattern < 0) { - return; + return -EINVAL; } break; default: qemuio_command_usage(&writev_cmd); - return; + return -EINVAL; } } if (optind > argc - 2) { qemuio_command_usage(&writev_cmd); - return; + return -EINVAL; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return; + return offset; } optind++; nr_iov = argc - optind; buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern); if (buf == NULL) { - return; + return -EINVAL; } gettimeofday(&t1, NULL); - cnt = do_aio_writev(blk, &qiov, offset, flags, &total); + ret = do_aio_writev(blk, &qiov, offset, flags, &total); gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("writev failed: %s\n", strerror(-cnt)); + if (ret < 0) { + printf("writev failed: %s\n", strerror(-ret)); goto out; } + cnt = ret; + + ret = 0; if (qflag) { goto out; @@ -1208,6 +1225,7 @@ static void writev_f(BlockBackend *blk, int argc, char **argv) out: qemu_iovec_destroy(&qiov); qemu_io_free(buf); + return ret; } struct aio_ctx { @@ -1314,6 +1332,9 @@ static void aio_read_help(void) " standard output stream (with -v option) for subsequent inspection.\n" " The read is performed asynchronously and the aio_flush command must be\n" " used to ensure all outstanding aio requests have been completed.\n" +" Note that due to its asynchronous nature, this command will be\n" +" considered successful once the request is submitted, independently\n" +" of potential I/O errors or pattern mismatches.\n" " -C, -- report statistics in a machine parsable format\n" " -P, -- use a pattern to verify read data\n" " -i, -- treat request as invalid, for exercising stats\n" @@ -1322,7 +1343,7 @@ static void aio_read_help(void) "\n"); } -static void aio_read_f(BlockBackend *blk, int argc, char **argv); +static int aio_read_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t aio_read_cmd = { .name = "aio_read", @@ -1334,7 +1355,7 @@ static const cmdinfo_t aio_read_cmd = { .help = aio_read_help, }; -static void aio_read_f(BlockBackend *blk, int argc, char **argv) +static int aio_read_f(BlockBackend *blk, int argc, char **argv) { int nr_iov, c; struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); @@ -1350,14 +1371,14 @@ static void aio_read_f(BlockBackend *blk, int argc, char **argv) ctx->pattern = parse_pattern(optarg); if (ctx->pattern < 0) { g_free(ctx); - return; + return -EINVAL; } break; case 'i': printf("injecting invalid read request\n"); block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); g_free(ctx); - return; + return 0; case 'q': ctx->qflag = true; break; @@ -1367,21 +1388,22 @@ static void aio_read_f(BlockBackend *blk, int argc, char **argv) default: g_free(ctx); qemuio_command_usage(&aio_read_cmd); - return; + return -EINVAL; } } if (optind > argc - 2) { g_free(ctx); qemuio_command_usage(&aio_read_cmd); - return; + return -EINVAL; } ctx->offset = cvtnum(argv[optind]); if (ctx->offset < 0) { - print_cvtnum_err(ctx->offset, argv[optind]); + int ret = ctx->offset; + print_cvtnum_err(ret, argv[optind]); g_free(ctx); - return; + return ret; } optind++; @@ -1390,13 +1412,14 @@ static void aio_read_f(BlockBackend *blk, int argc, char **argv) if (ctx->buf == NULL) { block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); g_free(ctx); - return; + return -EINVAL; } gettimeofday(&ctx->t1, NULL); block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, BLOCK_ACCT_READ); blk_aio_preadv(blk, ctx->offset, &ctx->qiov, 0, aio_read_done, ctx); + return 0; } static void aio_write_help(void) @@ -1413,6 +1436,9 @@ static void aio_write_help(void) " filled with a set pattern (0xcdcdcdcd).\n" " The write is performed asynchronously and the aio_flush command must be\n" " used to ensure all outstanding aio requests have been completed.\n" +" Note that due to its asynchronous nature, this command will be\n" +" considered successful once the request is submitted, independently\n" +" of potential I/O errors or pattern mismatches.\n" " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" " -f, -- use Force Unit Access semantics\n" @@ -1423,7 +1449,7 @@ static void aio_write_help(void) "\n"); } -static void aio_write_f(BlockBackend *blk, int argc, char **argv); +static int aio_write_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t aio_write_cmd = { .name = "aio_write", @@ -1436,7 +1462,7 @@ static const cmdinfo_t aio_write_cmd = { .help = aio_write_help, }; -static void aio_write_f(BlockBackend *blk, int argc, char **argv) +static int aio_write_f(BlockBackend *blk, int argc, char **argv) { int nr_iov, c; int pattern = 0xcd; @@ -1462,53 +1488,54 @@ static void aio_write_f(BlockBackend *blk, int argc, char **argv) pattern = parse_pattern(optarg); if (pattern < 0) { g_free(ctx); - return; + return -EINVAL; } break; case 'i': printf("injecting invalid write request\n"); block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); g_free(ctx); - return; + return 0; case 'z': ctx->zflag = true; break; default: g_free(ctx); qemuio_command_usage(&aio_write_cmd); - return; + return -EINVAL; } } if (optind > argc - 2) { g_free(ctx); qemuio_command_usage(&aio_write_cmd); - return; + return -EINVAL; } if (ctx->zflag && optind != argc - 2) { printf("-z supports only a single length parameter\n"); g_free(ctx); - return; + return -EINVAL; } if ((flags & BDRV_REQ_MAY_UNMAP) && !ctx->zflag) { printf("-u requires -z to be specified\n"); g_free(ctx); - return; + return -EINVAL; } if (ctx->zflag && ctx->Pflag) { printf("-z and -P cannot be specified at the same time\n"); g_free(ctx); - return; + return -EINVAL; } ctx->offset = cvtnum(argv[optind]); if (ctx->offset < 0) { - print_cvtnum_err(ctx->offset, argv[optind]); + int ret = ctx->offset; + print_cvtnum_err(ret, argv[optind]); g_free(ctx); - return; + return ret; } optind++; @@ -1517,7 +1544,7 @@ static void aio_write_f(BlockBackend *blk, int argc, char **argv) if (count < 0) { print_cvtnum_err(count, argv[optind]); g_free(ctx); - return; + return count; } ctx->qiov.size = count; @@ -1530,7 +1557,7 @@ static void aio_write_f(BlockBackend *blk, int argc, char **argv) if (ctx->buf == NULL) { block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); g_free(ctx); - return; + return -EINVAL; } gettimeofday(&ctx->t1, NULL); @@ -1540,14 +1567,17 @@ static void aio_write_f(BlockBackend *blk, int argc, char **argv) blk_aio_pwritev(blk, ctx->offset, &ctx->qiov, flags, aio_write_done, ctx); } + + return 0; } -static void aio_flush_f(BlockBackend *blk, int argc, char **argv) +static int aio_flush_f(BlockBackend *blk, int argc, char **argv) { BlockAcctCookie cookie; block_acct_start(blk_get_stats(blk), &cookie, 0, BLOCK_ACCT_FLUSH); blk_drain_all(); block_acct_done(blk_get_stats(blk), &cookie); + return 0; } static const cmdinfo_t aio_flush_cmd = { @@ -1556,9 +1586,9 @@ static const cmdinfo_t aio_flush_cmd = { .oneline = "completes all outstanding aio requests" }; -static void flush_f(BlockBackend *blk, int argc, char **argv) +static int flush_f(BlockBackend *blk, int argc, char **argv) { - blk_flush(blk); + return blk_flush(blk); } static const cmdinfo_t flush_cmd = { @@ -1568,7 +1598,7 @@ static const cmdinfo_t flush_cmd = { .oneline = "flush all in-core file state to disk", }; -static void truncate_f(BlockBackend *blk, int argc, char **argv) +static int truncate_f(BlockBackend *blk, int argc, char **argv) { Error *local_err = NULL; int64_t offset; @@ -1577,14 +1607,16 @@ static void truncate_f(BlockBackend *blk, int argc, char **argv) offset = cvtnum(argv[1]); if (offset < 0) { print_cvtnum_err(offset, argv[1]); - return; + return offset; } ret = blk_truncate(blk, offset, PREALLOC_MODE_OFF, &local_err); if (ret < 0) { error_report_err(local_err); - return; + return ret; } + + return 0; } static const cmdinfo_t truncate_cmd = { @@ -1598,7 +1630,7 @@ static const cmdinfo_t truncate_cmd = { .oneline = "truncates the current file at the given offset", }; -static void length_f(BlockBackend *blk, int argc, char **argv) +static int length_f(BlockBackend *blk, int argc, char **argv) { int64_t size; char s1[64]; @@ -1606,11 +1638,12 @@ static void length_f(BlockBackend *blk, int argc, char **argv) size = blk_getlength(blk); if (size < 0) { printf("getlength: %s\n", strerror(-size)); - return; + return size; } cvtstr(size, s1, sizeof(s1)); printf("%s\n", s1); + return 0; } @@ -1622,7 +1655,7 @@ static const cmdinfo_t length_cmd = { }; -static void info_f(BlockBackend *blk, int argc, char **argv) +static int info_f(BlockBackend *blk, int argc, char **argv) { BlockDriverState *bs = blk_bs(blk); BlockDriverInfo bdi; @@ -1639,7 +1672,7 @@ static void info_f(BlockBackend *blk, int argc, char **argv) ret = bdrv_get_info(bs, &bdi); if (ret) { - return; + return ret; } cvtstr(bdi.cluster_size, s1, sizeof(s1)); @@ -1654,6 +1687,8 @@ static void info_f(BlockBackend *blk, int argc, char **argv) bdrv_image_info_specific_dump(fprintf, stdout, spec_info); qapi_free_ImageInfoSpecific(spec_info); } + + return 0; } @@ -1680,7 +1715,7 @@ static void discard_help(void) "\n"); } -static void discard_f(BlockBackend *blk, int argc, char **argv); +static int discard_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t discard_cmd = { .name = "discard", @@ -1694,7 +1729,7 @@ static const cmdinfo_t discard_cmd = { .help = discard_help, }; -static void discard_f(BlockBackend *blk, int argc, char **argv) +static int discard_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false; @@ -1711,31 +1746,31 @@ static void discard_f(BlockBackend *blk, int argc, char **argv) break; default: qemuio_command_usage(&discard_cmd); - return; + return -EINVAL; } } if (optind != argc - 2) { qemuio_command_usage(&discard_cmd); - return; + return -EINVAL; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return; + return offset; } optind++; bytes = cvtnum(argv[optind]); if (bytes < 0) { print_cvtnum_err(bytes, argv[optind]); - return; + return bytes; } else if (bytes >> BDRV_SECTOR_BITS > BDRV_REQUEST_MAX_SECTORS) { printf("length cannot exceed %"PRIu64", given %s\n", (uint64_t)BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS, argv[optind]); - return; + return -EINVAL; } gettimeofday(&t1, NULL); @@ -1744,7 +1779,7 @@ static void discard_f(BlockBackend *blk, int argc, char **argv) if (ret < 0) { printf("discard failed: %s\n", strerror(-ret)); - return; + return ret; } /* Finally, report back -- -C gives a parsable format */ @@ -1752,9 +1787,11 @@ static void discard_f(BlockBackend *blk, int argc, char **argv) t2 = tsub(t2, t1); print_report("discard", &t2, offset, bytes, bytes, 1, Cflag); } + + return 0; } -static void alloc_f(BlockBackend *blk, int argc, char **argv) +static int alloc_f(BlockBackend *blk, int argc, char **argv) { BlockDriverState *bs = blk_bs(blk); int64_t offset, start, remaining, count; @@ -1765,14 +1802,14 @@ static void alloc_f(BlockBackend *blk, int argc, char **argv) start = offset = cvtnum(argv[1]); if (offset < 0) { print_cvtnum_err(offset, argv[1]); - return; + return offset; } if (argc == 3) { count = cvtnum(argv[2]); if (count < 0) { print_cvtnum_err(count, argv[2]); - return; + return count; } } else { count = BDRV_SECTOR_SIZE; @@ -1784,7 +1821,7 @@ static void alloc_f(BlockBackend *blk, int argc, char **argv) ret = bdrv_is_allocated(bs, offset, remaining, &num); if (ret < 0) { printf("is_allocated failed: %s\n", strerror(-ret)); - return; + return ret; } offset += num; remaining -= num; @@ -1801,6 +1838,7 @@ static void alloc_f(BlockBackend *blk, int argc, char **argv) printf("%"PRId64"/%"PRId64" bytes allocated at offset %s\n", sum_alloc, count, s1); + return 0; } static const cmdinfo_t alloc_cmd = { @@ -1846,7 +1884,7 @@ static int map_is_allocated(BlockDriverState *bs, int64_t offset, return firstret; } -static void map_f(BlockBackend *blk, int argc, char **argv) +static int map_f(BlockBackend *blk, int argc, char **argv) { int64_t offset, bytes; char s1[64], s2[64]; @@ -1858,17 +1896,17 @@ static void map_f(BlockBackend *blk, int argc, char **argv) bytes = blk_getlength(blk); if (bytes < 0) { error_report("Failed to query image length: %s", strerror(-bytes)); - return; + return bytes; } while (bytes) { ret = map_is_allocated(blk_bs(blk), offset, bytes, &num); if (ret < 0) { error_report("Failed to get allocation status: %s", strerror(-ret)); - return; + return ret; } else if (!num) { error_report("Unexpected end of image"); - return; + return -EIO; } retstr = ret ? " allocated" : "not allocated"; @@ -1880,6 +1918,8 @@ static void map_f(BlockBackend *blk, int argc, char **argv) offset += num; bytes -= num; } + + return 0; } static const cmdinfo_t map_cmd = { @@ -1907,7 +1947,7 @@ static void reopen_help(void) "\n"); } -static void reopen_f(BlockBackend *blk, int argc, char **argv); +static int reopen_f(BlockBackend *blk, int argc, char **argv); static QemuOptsList reopen_opts = { .name = "reopen", @@ -1929,7 +1969,7 @@ static const cmdinfo_t reopen_cmd = { .help = reopen_help, }; -static void reopen_f(BlockBackend *blk, int argc, char **argv) +static int reopen_f(BlockBackend *blk, int argc, char **argv) { BlockDriverState *bs = blk_bs(blk); QemuOpts *qopts; @@ -1947,19 +1987,19 @@ static void reopen_f(BlockBackend *blk, int argc, char **argv) case 'c': if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { error_report("Invalid cache option: %s", optarg); - return; + return -EINVAL; } break; case 'o': if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) { qemu_opts_reset(&reopen_opts); - return; + return -EINVAL; } break; case 'r': if (has_rw_option) { error_report("Only one -r/-w option may be given"); - return; + return -EINVAL; } flags &= ~BDRV_O_RDWR; has_rw_option = true; @@ -1967,7 +2007,7 @@ static void reopen_f(BlockBackend *blk, int argc, char **argv) case 'w': if (has_rw_option) { error_report("Only one -r/-w option may be given"); - return; + return -EINVAL; } flags |= BDRV_O_RDWR; has_rw_option = true; @@ -1975,14 +2015,14 @@ static void reopen_f(BlockBackend *blk, int argc, char **argv) default: qemu_opts_reset(&reopen_opts); qemuio_command_usage(&reopen_cmd); - return; + return -EINVAL; } } if (optind != argc) { qemu_opts_reset(&reopen_opts); qemuio_command_usage(&reopen_cmd); - return; + return -EINVAL; } if (writethrough != blk_enable_write_cache(blk) && @@ -1990,7 +2030,7 @@ static void reopen_f(BlockBackend *blk, int argc, char **argv) { error_report("Cannot change cache.writeback: Device attached"); qemu_opts_reset(&reopen_opts); - return; + return -EBUSY; } if (!(flags & BDRV_O_RDWR)) { @@ -2016,29 +2056,37 @@ static void reopen_f(BlockBackend *blk, int argc, char **argv) if (local_err) { error_report_err(local_err); - } else { - blk_set_enable_write_cache(blk, !writethrough); + return -EINVAL; } + + blk_set_enable_write_cache(blk, !writethrough); + return 0; } -static void break_f(BlockBackend *blk, int argc, char **argv) +static int break_f(BlockBackend *blk, int argc, char **argv) { int ret; ret = bdrv_debug_breakpoint(blk_bs(blk), argv[1], argv[2]); if (ret < 0) { printf("Could not set breakpoint: %s\n", strerror(-ret)); + return ret; } + + return 0; } -static void remove_break_f(BlockBackend *blk, int argc, char **argv) +static int remove_break_f(BlockBackend *blk, int argc, char **argv) { int ret; ret = bdrv_debug_remove_breakpoint(blk_bs(blk), argv[1]); if (ret < 0) { printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret)); + return ret; } + + return 0; } static const cmdinfo_t break_cmd = { @@ -2060,14 +2108,17 @@ static const cmdinfo_t remove_break_cmd = { .oneline = "remove a breakpoint by tag", }; -static void resume_f(BlockBackend *blk, int argc, char **argv) +static int resume_f(BlockBackend *blk, int argc, char **argv) { int ret; ret = bdrv_debug_resume(blk_bs(blk), argv[1]); if (ret < 0) { printf("Could not resume request: %s\n", strerror(-ret)); + return ret; } + + return 0; } static const cmdinfo_t resume_cmd = { @@ -2079,11 +2130,12 @@ static const cmdinfo_t resume_cmd = { .oneline = "resumes the request tagged as tag", }; -static void wait_break_f(BlockBackend *blk, int argc, char **argv) +static int wait_break_f(BlockBackend *blk, int argc, char **argv) { while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) { aio_poll(blk_get_aio_context(blk), true); } + return 0; } static const cmdinfo_t wait_break_cmd = { @@ -2095,7 +2147,7 @@ static const cmdinfo_t wait_break_cmd = { .oneline = "waits for the suspension of a request", }; -static void abort_f(BlockBackend *blk, int argc, char **argv) +static int abort_f(BlockBackend *blk, int argc, char **argv) { abort(); } @@ -2121,7 +2173,7 @@ static void sigraise_help(void) "\n", SIGTERM); } -static void sigraise_f(BlockBackend *blk, int argc, char **argv); +static int sigraise_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t sigraise_cmd = { .name = "sigraise", @@ -2134,16 +2186,16 @@ static const cmdinfo_t sigraise_cmd = { .help = sigraise_help, }; -static void sigraise_f(BlockBackend *blk, int argc, char **argv) +static int sigraise_f(BlockBackend *blk, int argc, char **argv) { int64_t sig = cvtnum(argv[1]); if (sig < 0) { print_cvtnum_err(sig, argv[1]); - return; + return sig; } else if (sig > NSIG) { printf("signal argument '%s' is too large to be a valid signal\n", argv[1]); - return; + return -EINVAL; } /* Using raise() to kill this process does not necessarily flush all open @@ -2153,6 +2205,8 @@ static void sigraise_f(BlockBackend *blk, int argc, char **argv) fflush(stderr); raise(sig); + + return 0; } static void sleep_cb(void *opaque) @@ -2161,7 +2215,7 @@ static void sleep_cb(void *opaque) *expired = true; } -static void sleep_f(BlockBackend *blk, int argc, char **argv) +static int sleep_f(BlockBackend *blk, int argc, char **argv) { char *endptr; long ms; @@ -2171,7 +2225,7 @@ static void sleep_f(BlockBackend *blk, int argc, char **argv) ms = strtol(argv[1], &endptr, 0); if (ms < 0 || *endptr != '\0') { printf("%s is not a valid number\n", argv[1]); - return; + return -EINVAL; } timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired); @@ -2182,6 +2236,7 @@ static void sleep_f(BlockBackend *blk, int argc, char **argv) } timer_free(timer); + return 0; } static const cmdinfo_t sleep_cmd = { @@ -2228,22 +2283,23 @@ static void help_all(void) printf("\nUse 'help commandname' for extended help.\n"); } -static void help_f(BlockBackend *blk, int argc, char **argv) +static int help_f(BlockBackend *blk, int argc, char **argv) { const cmdinfo_t *ct; if (argc == 1) { help_all(); - return; + return 0; } ct = find_command(argv[1]); if (ct == NULL) { printf("command %s not found\n", argv[1]); - return; + return -EINVAL; } help_onecmd(argv[1], ct); + return 0; } static const cmdinfo_t help_cmd = { @@ -2257,13 +2313,14 @@ static const cmdinfo_t help_cmd = { .oneline = "help for one or all commands", }; -void qemuio_command(BlockBackend *blk, const char *cmd) +int qemuio_command(BlockBackend *blk, const char *cmd) { AioContext *ctx; char *input; const cmdinfo_t *ct; char **v; int c; + int ret = 0; input = g_strdup(cmd); v = breakline(input, &c); @@ -2272,14 +2329,17 @@ void qemuio_command(BlockBackend *blk, const char *cmd) if (ct) { ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context(); aio_context_acquire(ctx); - command(blk, ct, c, v); + ret = command(blk, ct, c, v); aio_context_release(ctx); } else { fprintf(stderr, "command \"%s\" not found\n", v[0]); + ret = -EINVAL; } } g_free(input); g_free(v); + + return ret; } static void __attribute((constructor)) init_qemuio_commands(void) diff --git a/qemu-io.c b/qemu-io.c index 02a67c929a..ec6683803f 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -66,10 +66,11 @@ static int get_eof_char(void) #endif } -static void close_f(BlockBackend *blk, int argc, char **argv) +static int close_f(BlockBackend *blk, int argc, char **argv) { blk_unref(qemuio_blk); qemuio_blk = NULL; + return 0; } static const cmdinfo_t close_cmd = { @@ -136,7 +137,7 @@ static void open_help(void) "\n"); } -static void open_f(BlockBackend *blk, int argc, char **argv); +static int open_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t open_cmd = { .name = "open", @@ -160,12 +161,13 @@ static QemuOptsList empty_opts = { }, }; -static void open_f(BlockBackend *blk, int argc, char **argv) +static int open_f(BlockBackend *blk, int argc, char **argv) { int flags = BDRV_O_UNMAP; int readonly = 0; bool writethrough = true; int c; + int ret; QemuOpts *qopts; QDict *opts; bool force_share = false; @@ -192,25 +194,25 @@ static void open_f(BlockBackend *blk, int argc, char **argv) if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { error_report("Invalid cache option: %s", optarg); qemu_opts_reset(&empty_opts); - return; + return -EINVAL; } break; case 'd': if (bdrv_parse_discard_flags(optarg, &flags) < 0) { error_report("Invalid discard option: %s", optarg); qemu_opts_reset(&empty_opts); - return; + return -EINVAL; } break; case 'o': if (imageOpts) { printf("--image-opts and 'open -o' are mutually exclusive\n"); qemu_opts_reset(&empty_opts); - return; + return -EINVAL; } if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) { qemu_opts_reset(&empty_opts); - return; + return -EINVAL; } break; case 'U': @@ -219,7 +221,7 @@ static void open_f(BlockBackend *blk, int argc, char **argv) default: qemu_opts_reset(&empty_opts); qemuio_command_usage(&open_cmd); - return; + return -EINVAL; } } @@ -230,7 +232,7 @@ static void open_f(BlockBackend *blk, int argc, char **argv) if (imageOpts && (optind == argc - 1)) { if (!qemu_opts_parse_noisily(&empty_opts, argv[optind], false)) { qemu_opts_reset(&empty_opts); - return; + return -EINVAL; } optind++; } @@ -240,18 +242,26 @@ static void open_f(BlockBackend *blk, int argc, char **argv) qemu_opts_reset(&empty_opts); if (optind == argc - 1) { - openfile(argv[optind], flags, writethrough, force_share, opts); + ret = openfile(argv[optind], flags, writethrough, force_share, opts); } else if (optind == argc) { - openfile(NULL, flags, writethrough, force_share, opts); + ret = openfile(NULL, flags, writethrough, force_share, opts); } else { qobject_unref(opts); qemuio_command_usage(&open_cmd); + return -EINVAL; } + + if (ret) { + return -EINVAL; + } + + return 0; } -static void quit_f(BlockBackend *blk, int argc, char **argv) +static int quit_f(BlockBackend *blk, int argc, char **argv) { quit_qemu_io = true; + return 0; } static const cmdinfo_t quit_cmd = { From 6b3aa8485ca8e61b168f51d465188855cf549bd2 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 21:43:00 +0200 Subject: [PATCH 1145/2380] qemu-io: Exit with error when a command failed Currently, qemu-io basically always returns success when it gets to interactive mode (so once the whole command line has been parsed; even before the commands on the command line are interpreted). That is not very useful. This patch makes qemu-io return failure when any of the executed commands failed. Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1519617 Signed-off-by: Max Reitz Reviewed-by: Eric Blake Message-id: 20180509194302.21585-4-mreitz@redhat.com Signed-off-by: Max Reitz --- qemu-io.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/qemu-io.c b/qemu-io.c index ec6683803f..13829f5e21 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -400,17 +400,21 @@ static void prep_fetchline(void *opaque) *fetchable= 1; } -static void command_loop(void) +static int command_loop(void) { int i, fetchable = 0, prompted = 0; + int ret, last_error = 0; char *input; for (i = 0; !quit_qemu_io && i < ncmdline; i++) { - qemuio_command(qemuio_blk, cmdline[i]); + ret = qemuio_command(qemuio_blk, cmdline[i]); + if (ret < 0) { + last_error = ret; + } } if (cmdline) { g_free(cmdline); - return; + return last_error; } while (!quit_qemu_io) { @@ -431,13 +435,19 @@ static void command_loop(void) if (input == NULL) { break; } - qemuio_command(qemuio_blk, input); + ret = qemuio_command(qemuio_blk, input); g_free(input); + if (ret < 0) { + last_error = ret; + } + prompted = 0; fetchable = 0; } qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); + + return last_error; } static void add_user_command(char *optarg) @@ -502,6 +512,7 @@ int main(int argc, char **argv) int c; int opt_index = 0; int flags = BDRV_O_UNMAP; + int ret; bool writethrough = true; Error *local_error = NULL; QDict *opts = NULL; @@ -663,7 +674,7 @@ int main(int argc, char **argv) } } } - command_loop(); + ret = command_loop(); /* * Make sure all outstanding requests complete before the program exits. @@ -672,5 +683,10 @@ int main(int argc, char **argv) blk_unref(qemuio_blk); g_free(readline_state); - return 0; + + if (ret < 0) { + return 1; + } else { + return 0; + } } From 745f2bf4a5b973d1a69b21db97f9e4219da60624 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 21:43:01 +0200 Subject: [PATCH 1146/2380] iotests.py: Add qemu_io_silent With qemu-io now returning a useful exit code, some tests may find it sufficient to just query that instead of logging (and filtering) the whole output. Signed-off-by: Max Reitz Reviewed-by: Eric Blake Message-id: 20180509194302.21585-5-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/iotests.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index fdbdd8b300..0b204dc220 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -133,6 +133,15 @@ def qemu_io(*args): sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) return subp.communicate()[0] +def qemu_io_silent(*args): + '''Run qemu-io and return the exit code, suppressing stdout''' + args = qemu_io_args + list(args) + exitcode = subprocess.call(args, stdout=open('/dev/null', 'w')) + if exitcode < 0: + sys.stderr.write('qemu-io received signal %i: %s\n' % + (-exitcode, ' '.join(args))) + return exitcode + class QemuIoInteractive: def __init__(self, *args): From e4ca4e981a0a389d6af5dc5d8b5fbdd1a05276a0 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 21:43:02 +0200 Subject: [PATCH 1147/2380] iotests: Let 216 make use of qemu-io's exit code As a showcase of how you can use qemu-io's exit code to determine success or failure (same for qemu-img), this test is changed to use qemu_io_silent() instead of qemu_io(), and to assert the exit code instead of logging the filtered result. One real advantage of this is that in case of an error, you get a backtrace that helps you locate the issue in the test file quickly. Signed-off-by: Max Reitz Reviewed-by: Eric Blake Message-id: 20180509194302.21585-6-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/216 | 23 ++++++++++++----------- tests/qemu-iotests/216.out | 17 ++--------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216 index ca9b47a7fd..3c0ae54b44 100755 --- a/tests/qemu-iotests/216 +++ b/tests/qemu-iotests/216 @@ -20,7 +20,7 @@ # Creator/Owner: Max Reitz import iotests -from iotests import log, qemu_img_pipe, qemu_io, filter_qemu_io +from iotests import log, qemu_img, qemu_io_silent # Need backing file support iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk']) @@ -50,14 +50,13 @@ with iotests.FilePath('base.img') as base_img_path, \ log('--- Setting up images ---') log('') - qemu_img_pipe('create', '-f', iotests.imgfmt, base_img_path, '64M') + assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 + assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0 + assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, + top_img_path) == 0 + assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0 - log(filter_qemu_io(qemu_io(base_img_path, '-c', 'write -P 1 0M 1M'))) - - qemu_img_pipe('create', '-f', iotests.imgfmt, '-b', base_img_path, - top_img_path) - - log(filter_qemu_io(qemu_io(top_img_path, '-c', 'write -P 2 1M 1M'))) + log('Done') log('') log('--- Doing COR ---') @@ -110,6 +109,8 @@ with iotests.FilePath('base.img') as base_img_path, \ log('--- Checking COR result ---') log('') - log(filter_qemu_io(qemu_io(base_img_path, '-c', 'discard 0 64M'))) - log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 1 0M 1M'))) - log(filter_qemu_io(qemu_io(top_img_path, '-c', 'read -P 2 1M 1M'))) + assert qemu_io_silent(base_img_path, '-c', 'discard 0 64M') == 0 + assert qemu_io_silent(top_img_path, '-c', 'read -P 1 0M 1M') == 0 + assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0 + + log('Done') diff --git a/tests/qemu-iotests/216.out b/tests/qemu-iotests/216.out index d3fc590d29..45ea857ee1 100644 --- a/tests/qemu-iotests/216.out +++ b/tests/qemu-iotests/216.out @@ -3,12 +3,7 @@ --- Setting up images --- -wrote 1048576/1048576 bytes at offset 0 -1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -wrote 1048576/1048576 bytes at offset 1048576 -1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - +Done --- Doing COR --- @@ -17,12 +12,4 @@ wrote 1048576/1048576 bytes at offset 1048576 --- Checking COR result --- -discard 67108864/67108864 bytes at offset 0 -64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -read 1048576/1048576 bytes at offset 0 -1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -read 1048576/1048576 bytes at offset 1048576 -1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - +Done From d16699b64671466b42079c45b89127aeea1ca565 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 20:20:01 +0200 Subject: [PATCH 1148/2380] qemu-img: Resolve relative backing paths in rebase Currently, rebase interprets a relative path for the new backing image as follows: (1) Open the new backing image with the given relative path (thus relative to qemu-img's working directory). (2) Write it directly into the overlay's backing path field (thus relative to the overlay). If the overlay is not in qemu-img's working directory, both will be different interpretations, which may either lead to an error somewhere (either rebase fails because it cannot open the new backing image, or your overlay becomes unusable because its backing path does not point to a file), or, even worse, it may result in your rebase being performed for a different backing file than what your overlay will point to after the rebase. Fix this by interpreting the target backing path as relative to the overlay, like qemu-img does everywhere else. Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1569835 Cc: qemu-stable@nongnu.org Signed-off-by: Max Reitz Message-id: 20180509182002.8044-2-mreitz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- qemu-img.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c index b1c1e484d2..ebe1b866da 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3251,6 +3251,9 @@ static int img_rebase(int argc, char **argv) } if (out_baseimg[0]) { + const char *overlay_filename; + char *out_real_path; + options = qdict_new(); if (out_basefmt) { qdict_put_str(options, "driver", out_basefmt); @@ -3259,8 +3262,26 @@ static int img_rebase(int argc, char **argv) qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); } - blk_new_backing = blk_new_open(out_baseimg, NULL, + overlay_filename = bs->exact_filename[0] ? bs->exact_filename + : bs->filename; + out_real_path = g_malloc(PATH_MAX); + + bdrv_get_full_backing_filename_from_filename(overlay_filename, + out_baseimg, + out_real_path, + PATH_MAX, + &local_err); + if (local_err) { + error_reportf_err(local_err, + "Could not resolve backing filename: "); + ret = -1; + g_free(out_real_path); + goto out; + } + + blk_new_backing = blk_new_open(out_real_path, NULL, options, src_flags, &local_err); + g_free(out_real_path); if (!blk_new_backing) { error_reportf_err(local_err, "Could not open new backing file '%s': ", From 28036a7f7044fddb79819e3c8fcb4ae5605c60e0 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 9 May 2018 20:20:02 +0200 Subject: [PATCH 1149/2380] iotests: Add test for rebasing with relative paths Signed-off-by: Max Reitz Reviewed-by: Eric Blake Message-id: 20180509182002.8044-3-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/024 | 82 ++++++++++++++++++++++++++++++++++++-- tests/qemu-iotests/024.out | 30 ++++++++++++++ 2 files changed, 109 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index e0d77ce2f5..4071ed6093 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -29,9 +29,14 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img - rm -f "$TEST_DIR/t.$IMGFMT.base_old" - rm -f "$TEST_DIR/t.$IMGFMT.base_new" + _cleanup_test_img + rm -f "$TEST_DIR/t.$IMGFMT.base_old" + rm -f "$TEST_DIR/t.$IMGFMT.base_new" + + rm -f "$TEST_DIR/subdir/t.$IMGFMT" + rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_old" + rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_new" + rmdir "$TEST_DIR/subdir" 2> /dev/null } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -123,6 +128,77 @@ io_pattern readv $((13 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00 io_pattern readv $((14 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x11 io_pattern readv $((15 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00 +echo +echo "=== Test rebase in a subdirectory of the working directory ===" +echo + +# Clean up the old images beforehand so they do not interfere with +# this test +_cleanup + +mkdir "$TEST_DIR/subdir" + +# Relative to the overlay +BASE_OLD_OREL="t.$IMGFMT.base_old" +BASE_NEW_OREL="t.$IMGFMT.base_new" + +# Relative to $TEST_DIR (which is going to be our working directory) +OVERLAY_WREL="subdir/t.$IMGFMT" + +BASE_OLD="$TEST_DIR/subdir/$BASE_OLD_OREL" +BASE_NEW="$TEST_DIR/subdir/$BASE_NEW_OREL" +OVERLAY="$TEST_DIR/$OVERLAY_WREL" + +# Test done here: +# +# Backing (old): 11 11 -- 11 +# Backing (new): -- 22 22 11 +# Overlay: -- -- -- -- +# +# Rebasing works, we have verified that above. Here, we just want to +# see that rebasing is done for the correct target backing file. + +TEST_IMG=$BASE_OLD _make_test_img 1M +TEST_IMG=$BASE_NEW _make_test_img 1M +TEST_IMG=$OVERLAY _make_test_img -b "$BASE_OLD_OREL" 1M + +echo + +$QEMU_IO "$BASE_OLD" \ + -c "write -P 0x11 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + -c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \ + | _filter_qemu_io + +$QEMU_IO "$BASE_NEW" \ + -c "write -P 0x22 $((1 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + -c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \ + | _filter_qemu_io + +echo + +pushd "$TEST_DIR" >/dev/null +$QEMU_IMG rebase -f "$IMGFMT" -b "$BASE_NEW_OREL" "$OVERLAY_WREL" +popd >/dev/null + +# Verify the backing path is correct +TEST_IMG=$OVERLAY _img_info | grep '^backing file' + +echo + +# Verify the data is correct +$QEMU_IO "$OVERLAY" \ + -c "read -P 0x11 $((0 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + -c "read -P 0x11 $((1 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + -c "read -P 0x00 $((2 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + -c "read -P 0x11 $((3 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo + +# Verify that cluster #3 is not allocated (because it is the same in +# $BASE_OLD and $BASE_NEW) +$QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map + # success, all done echo "*** done" diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out index 33cfaf5cfc..024dc786b3 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -141,4 +141,34 @@ read 65536/65536 bytes at offset 917504 === IO: pattern 0x00 read 65536/65536 bytes at offset 983040 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Test rebase in a subdirectory of the working directory === + +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=t.IMGFMT.base_old + +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 196608 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 65536 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 196608 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +backing file: t.IMGFMT.base_new (actual path: TEST_DIR/subdir/t.IMGFMT.base_new) + +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 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 196608 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Offset Length File +0 0x30000 TEST_DIR/subdir/t.IMGFMT +0x30000 0x10000 TEST_DIR/subdir/t.IMGFMT.base_new *** done From 351c8efff9ad809c822d55620df54d575d536f68 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Tue, 1 May 2018 18:57:49 +0200 Subject: [PATCH 1150/2380] qemu-img: Special post-backing convert handling Currently, qemu-img convert writes zeroes when it reads zeroes. Sometimes it does not because the target is initialized to zeroes anyway, so we do not need to overwrite (and thus potentially allocate) it. This is never the case for targets with backing files, though. But even they may have an area that is initialized to zeroes, and that is the area past the end of the backing file (if that is shorter than the overlay). So if the target format's unallocated blocks are zero and there is a gap between the target's backing file's end and the target's end, we do not have to explicitly write zeroes there. Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1527898 Signed-off-by: Max Reitz Message-id: 20180501165750.19242-2-mreitz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- qemu-img.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c index ebe1b866da..ae4acb655b 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1556,7 +1556,9 @@ typedef struct ImgConvertState { BlockBackend *target; bool has_zero_init; bool compressed; + bool unallocated_blocks_are_zero; bool target_has_backing; + int64_t target_backing_sectors; /* negative if unknown */ bool wr_in_order; bool copy_range; int min_sparse; @@ -1586,12 +1588,23 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) { int64_t src_cur_offset; int ret, n, src_cur; + bool post_backing_zero = false; convert_select_part(s, sector_num, &src_cur, &src_cur_offset); assert(s->total_sectors > sector_num); n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS); + if (s->target_backing_sectors >= 0) { + if (sector_num >= s->target_backing_sectors) { + post_backing_zero = s->unallocated_blocks_are_zero; + } else if (sector_num + n > s->target_backing_sectors) { + /* Split requests around target_backing_sectors (because + * starting from there, zeros are handled differently) */ + n = s->target_backing_sectors - sector_num; + } + } + if (s->sector_next_status <= sector_num) { int64_t count = n * BDRV_SECTOR_SIZE; @@ -1613,7 +1626,7 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); if (ret & BDRV_BLOCK_ZERO) { - s->status = BLK_ZERO; + s->status = post_backing_zero ? BLK_BACKING_FILE : BLK_ZERO; } else if (ret & BDRV_BLOCK_DATA) { s->status = BLK_DATA; } else { @@ -2379,6 +2392,16 @@ static int img_convert(int argc, char **argv) } } + if (s.target_has_backing) { + /* Errors are treated as "backing length unknown" (which means + * s.target_backing_sectors has to be negative, which it will + * be automatically). The backing file length is used only + * for optimizations, so such a case is not fatal. */ + s.target_backing_sectors = bdrv_nb_sectors(out_bs->backing->bs); + } else { + s.target_backing_sectors = -1; + } + ret = bdrv_get_info(out_bs, &bdi); if (ret < 0) { if (s.compressed) { @@ -2388,6 +2411,7 @@ static int img_convert(int argc, char **argv) } else { s.compressed = s.compressed || bdi.needs_compressed_writes; s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; + s.unallocated_blocks_are_zero = bdi.unallocated_blocks_are_zero; } ret = convert_do_copy(&s); From 0682854f899d03c78befce9f5aae4a7e37655d25 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Tue, 1 May 2018 18:57:50 +0200 Subject: [PATCH 1151/2380] iotests: Test post-backing convert target behavior This adds a test case to 122 for what happens when you convert to a target with a backing file that is shorter than the target, and the image format does not support efficient zero writes (as is the case with qcow2 v2). Signed-off-by: Max Reitz Message-id: 20180501165750.19242-3-mreitz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- tests/qemu-iotests/122 | 42 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/122.out | 18 ++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122 index 45b359c2ba..d8c8ad722d 100755 --- a/tests/qemu-iotests/122 +++ b/tests/qemu-iotests/122 @@ -76,6 +76,48 @@ $QEMU_IMG convert -O $IMGFMT -c -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig $QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir +echo +echo "=== Converting to an overlay larger than its backing file ===" +echo + +TEST_IMG="$TEST_IMG".base _make_test_img 256M +# Needs to be at least how much an L2 table covers +# (64 kB/entry * 64 kB / 8 B/entry = 512 MB) +# That way, qcow2 will yield at least two status request responses. +# With just a single response, it would always say "Allocated in the +# backing file", so the optimization qemu-img convert tries to do is +# done automatically. Once it has to be queried twice, however (and +# one of the queries is completely after the end of the backing file), +# the block layer will automatically add a ZERO flag that qemu-img +# convert used to follow up with a zero write to the target. +# We do not want such a zero write, however, because we are past the +# end of the backing file on the target as well, so we do not need to +# write anything there. +_make_test_img -b "$TEST_IMG".base 768M + +# Use compat=0.10 as the output so there is no zero cluster support +$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \ + "$TEST_IMG" "$TEST_IMG".orig +# See that nothing has been allocated past 64M +$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map + +echo + +# Just before the end of the backing file +$QEMU_IO -c 'write -P 0x11 255M 1M' "$TEST_IMG".base 2>&1 | _filter_qemu_io +# Somewhere in the second L2 table +$QEMU_IO -c 'write -P 0x22 600M 1M' "$TEST_IMG" 2>&1 | _filter_qemu_io + +$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \ + "$TEST_IMG" "$TEST_IMG".orig + +$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map +$QEMU_IO -c 'read -P 0x11 255M 1M' \ + -c 'read -P 0x22 600M 1M' \ + "$TEST_IMG".orig \ + | _filter_qemu_io + + echo echo "=== Concatenate multiple source images ===" echo diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out index 47d8656db8..6c7ee1da6c 100644 --- a/tests/qemu-iotests/122.out +++ b/tests/qemu-iotests/122.out @@ -28,6 +28,24 @@ read 3145728/3145728 bytes at offset 0 read 3145728/3145728 bytes at offset 0 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +=== Converting to an overlay larger than its backing file === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=268435456 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=805306368 backing_file=TEST_DIR/t.IMGFMT.base +Offset Length File + +wrote 1048576/1048576 bytes at offset 267386880 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1048576/1048576 bytes at offset 629145600 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0xff00000 0x100000 TEST_DIR/t.IMGFMT.base +0x25800000 0x100000 TEST_DIR/t.IMGFMT.orig +read 1048576/1048576 bytes at offset 267386880 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 629145600 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + === Concatenate multiple source images === Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=4194304 From c1bac161bb7ad27243776e90971c51cc38c2e1b6 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 1 Jun 2018 14:59:23 +0300 Subject: [PATCH 1152/2380] iotests: improve pause_job It's possible, that job was finished during waiting. In this case we will see error message "Timeout waiting for job to pause" which is not very informative. So, let's check during waiting iteration that the job exists. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-id: 20180601115923.17159-1-vsementsov@virtuozzo.com Signed-off-by: Max Reitz --- tests/qemu-iotests/iotests.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 0b204dc220..2f22fab2a7 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -590,9 +590,14 @@ class QMPTestCase(unittest.TestCase): with Timeout(1, "Timeout waiting for job to pause"): while True: result = self.vm.qmp('query-block-jobs') + found = False for job in result['return']: - if job['device'] == job_id and job['paused'] == True and job['busy'] == False: - return job + if job['device'] == job_id: + found = True + if job['paused'] == True and job['busy'] == False: + return job + break + assert found def pause_job(self, job_id='job0', wait=True): result = self.vm.qmp('block-job-pause', device=job_id) From 83f90b535a0d5e64056c087aa4022ea35c59bcd0 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 6 Jun 2018 21:06:28 +0200 Subject: [PATCH 1153/2380] iotests: Fix 219's timing 219 has two issues that may lead to sporadic failure, both of which are the result of issuing query-jobs too early after a job has been modified. This can then lead to different results based on whether the modification has taken effect already or not. First, query-jobs is issued right after the job has been created. Besides its current progress possibly being in any random state (which has already been taken care of), its total progress too is basically arbitrary, because the job may not yet have been able to determine it. This patch addresses this by just filtering the total progress, like what has been done for the current progress already. However, for more clarity, the filtering is changed to replace the values by a string 'FILTERED' instead of deleting them. Secondly, query-jobs is issued right after a job has been resumed. The job may or may not yet have had the time to actually perform any I/O, and thus its current progress may or may not have advanced. To make sure it has indeed advanced (which is what the reference output already assumes), keep querying it until it has. Signed-off-by: Max Reitz Message-id: 20180606190628.8170-1-mreitz@redhat.com Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- tests/qemu-iotests/219 | 26 ++++++++++++++++++++------ tests/qemu-iotests/219.out | 10 +++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219 index 898a26eef0..c03bbdb294 100755 --- a/tests/qemu-iotests/219 +++ b/tests/qemu-iotests/219 @@ -42,11 +42,24 @@ def test_pause_resume(vm): iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'})) pause_wait(vm, 'job0') iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) - iotests.log(vm.qmp('query-jobs')) + result = vm.qmp('query-jobs') + iotests.log(result) + + old_progress = result['return'][0]['current-progress'] + total_progress = result['return'][0]['total-progress'] iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'})) iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) - iotests.log(vm.qmp('query-jobs')) + if old_progress < total_progress: + # Wait for the job to advance + while result['return'][0]['current-progress'] == old_progress: + result = vm.qmp('query-jobs') + iotests.log(result) + else: + # Already reached the end, so the job cannot advance + # any further; therefore, the query-jobs result can be + # logged immediately + iotests.log(vm.qmp('query-jobs')) def test_job_lifecycle(vm, job, job_args, has_ready=False): iotests.log('') @@ -58,12 +71,13 @@ def test_job_lifecycle(vm, job, job_args, has_ready=False): iotests.log(vm.qmp(job, job_id='job0', **job_args)) # Depending on the storage, the first request may or may not have completed - # yet, so filter out the progress. Later query-job calls don't need the - # filtering because the progress is made deterministic by the block job - # speed + # yet (and the total progress may not have been fully determined yet), so + # filter out the progress. Later query-job calls don't need the filtering + # because the progress is made deterministic by the block job speed result = vm.qmp('query-jobs') for j in result['return']: - del j['current-progress'] + j['current-progress'] = 'FILTERED' + j['total-progress'] = 'FILTERED' iotests.log(result) # undefined -> created -> running diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out index 346801b655..6dc07bc41e 100644 --- a/tests/qemu-iotests/219.out +++ b/tests/qemu-iotests/219.out @@ -3,7 +3,7 @@ Launching VM... Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True) {u'return': {}} -{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'mirror'}]} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} @@ -93,7 +93,7 @@ Waiting for PENDING state... Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True) {u'return': {}} -{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} @@ -144,7 +144,7 @@ Waiting for PENDING state... Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False) {u'return': {}} -{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} @@ -203,7 +203,7 @@ Waiting for PENDING state... Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True) {u'return': {}} -{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} @@ -262,7 +262,7 @@ Waiting for PENDING state... Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False) {u'return': {}} -{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} {u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} From 46e8d272baa0608adcfdbd8bc1d2312bea06da40 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 6 Jun 2018 14:35:51 +0200 Subject: [PATCH 1154/2380] qemu-img: Remove deprecated -s snapshot_id_or_name option It has been marked as deprecated since QEMU v2.0 already, so it is time now to finally remove it. Signed-off-by: Thomas Huth Message-id: 1528288551-31641-1-git-send-email-thuth@redhat.com Reviewed-by: Jeff Cody Signed-off-by: Max Reitz --- qemu-doc.texi | 7 ------- qemu-img-cmds.hx | 4 ++-- qemu-img.c | 7 +------ qemu-img.texi | 7 ++----- tests/qemu-iotests/029 | 2 +- tests/qemu-iotests/080 | 4 ++-- 6 files changed, 8 insertions(+), 23 deletions(-) diff --git a/qemu-doc.texi b/qemu-doc.texi index 2effe66d6b..9aff6b4ea9 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2927,13 +2927,6 @@ Option @option{-virtioconsole} has been replaced by The @code{-clock} option is ignored since QEMU version 1.7.0. There is no replacement since it is not needed anymore. -@section qemu-img command line arguments - -@subsection convert -s (since 2.0.0) - -The ``convert -s snapshot_id_or_name'' argument is obsoleted -by the ``convert -l snapshot_param'' argument instead. - @section QEMU Machine Protocol (QMP) commands @subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 3d2f7b26eb..69758fb6e8 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -44,9 +44,9 @@ STEXI ETEXI DEF("convert", img_convert, - "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") + "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") STEXI -@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("create", img_create, diff --git a/qemu-img.c b/qemu-img.c index ae4acb655b..1dcdd47254 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -148,8 +148,6 @@ static void QEMU_NORETURN help(void) " 'snapshot_param' is param used for internal snapshot, format\n" " is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" " '[ID_OR_NAME]'\n" - " 'snapshot_id_or_name' is deprecated, use 'snapshot_param'\n" - " instead\n" " '-c' indicates that target image must be compressed (qcow format only)\n" " '-u' allows unsafe backing chains. For rebasing, it is assumed that old and\n" " new backing file match exactly. The image doesn't need a working\n" @@ -2018,7 +2016,7 @@ static int img_convert(int argc, char **argv) {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:O:B:co:s:l:S:pt:T:qnm:WU", + c = getopt_long(argc, argv, ":hf:O:B:co:l:S:pt:T:qnm:WU", long_options, NULL); if (c == -1) { break; @@ -2059,9 +2057,6 @@ static int img_convert(int argc, char **argv) g_free(old_options); } break; - case 's': - snapshot_name = optarg; - break; case 'l': if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts, diff --git a/qemu-img.texi b/qemu-img.texi index 2be8206a05..aeb1b9e66c 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -61,9 +61,6 @@ by the used format or see the format descriptions below for details. is param used for internal snapshot, format is 'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]' -@item snapshot_id_or_name -is deprecated, use snapshot_param instead - @end table @table @option @@ -322,9 +319,9 @@ Error on reading data @end table -@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} -Convert the disk image @var{filename} or a snapshot @var{snapshot_param}(@var{snapshot_id_or_name} is deprecated) +Convert the disk image @var{filename} or a snapshot @var{snapshot_param} to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} option) or use any format specific options like encryption (@code{-o} option). diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 index 30bab24dc0..5cff6875bf 100755 --- a/tests/qemu-iotests/029 +++ b/tests/qemu-iotests/029 @@ -92,7 +92,7 @@ _make_test_img 64M { $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir poke_file "$TEST_IMG" "$offset_size" "\x00\x00\x00\x00\x00\x00\x02\x00" poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\x00\x01" -{ $QEMU_IMG convert -s foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG convert -l foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir # success, all done diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 index 4dbe68e950..f0eb42f390 100755 --- a/tests/qemu-iotests/080 +++ b/tests/qemu-iotests/080 @@ -176,7 +176,7 @@ _make_test_img 64M { $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00" -{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir +{ $QEMU_IMG convert -l test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir { $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir { $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \ -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir @@ -190,7 +190,7 @@ _make_test_img 64M { $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00" -{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir +{ $QEMU_IMG convert -l test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir { $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir { $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \ -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir From 7eb24009dbf8102fc27d5459bec1cb8a932540c4 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 8 Jun 2018 13:12:25 +0300 Subject: [PATCH 1155/2380] block/qcow2-bitmap: fix free_bitmap_clusters This assert may fail, because bitmap_table is not initialized. Just drop it, as it's obvious, that bitmap_table_load sets bitmap_table parameter only when returning zero. Reported-by: Pavel Butsykin Signed-off-by: Vladimir Sementsov-Ogievskiy Message-id: 20180608101225.2575-1-vsementsov@virtuozzo.com Signed-off-by: Max Reitz --- block/qcow2-bitmap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 60d5290f10..69485aa1de 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -254,7 +254,6 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) ret = bitmap_table_load(bs, tb, &bitmap_table); if (ret < 0) { - assert(bitmap_table == NULL); return ret; } From bc33c047d1ec0b35c9cd8be62bcefae2da28654f Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Fri, 8 Jun 2018 18:15:36 +0300 Subject: [PATCH 1156/2380] throttle: Fix crash on reopen The throttle block filter can be reopened, and with this it is possible to change the throttle group that the filter belongs to. The way the code does that is the following: - On throttle_reopen_prepare(): create a new ThrottleGroupMember and attach it to the new throttle group. - On throttle_reopen_commit(): detach the old ThrottleGroupMember, delete it and replace it with the new one. The problem with this is that by replacing the ThrottleGroupMember the previous value of io_limits_disabled is lost, causing an assertion failure in throttle_co_drain_end(). This problem can be reproduced by reopening a throttle node: $QEMU -monitor stdio -object throttle-group,id=tg0,x-iops-total=1000 \ -blockdev node-name=hd0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 \ -blockdev node-name=root,driver=throttle,throttle-group=tg0,file=hd0,read-only=on (qemu) block_stream root block/throttle.c:214: throttle_co_drain_end: Assertion `tgm->io_limits_disabled' failed. Since we only want to change the throttle group on reopen there's no need to create a ThrottleGroupMember and discard the old one. It's easier if we simply detach it from its current group and attach it to the new one. Signed-off-by: Alberto Garcia Message-id: 20180608151536.7378-1-berto@igalia.com Signed-off-by: Max Reitz --- block/throttle.c | 54 +++++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/block/throttle.c b/block/throttle.c index e298827f95..f617f23a12 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -36,9 +36,12 @@ static QemuOptsList throttle_opts = { }, }; -static int throttle_configure_tgm(BlockDriverState *bs, - ThrottleGroupMember *tgm, - QDict *options, Error **errp) +/* + * If this function succeeds then the throttle group name is stored in + * @group and must be freed by the caller. + * If there's an error then @group remains unmodified. + */ +static int throttle_parse_options(QDict *options, char **group, Error **errp) { int ret; const char *group_name; @@ -63,8 +66,7 @@ static int throttle_configure_tgm(BlockDriverState *bs, goto fin; } - /* Register membership to group with name group_name */ - throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs)); + *group = g_strdup(group_name); ret = 0; fin: qemu_opts_del(opts); @@ -75,6 +77,8 @@ static int throttle_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { ThrottleGroupMember *tgm = bs->opaque; + char *group; + int ret; bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, errp); @@ -86,7 +90,14 @@ static int throttle_open(BlockDriverState *bs, QDict *options, bs->supported_zero_flags = bs->file->bs->supported_zero_flags | BDRV_REQ_WRITE_UNCHANGED; - return throttle_configure_tgm(bs, tgm, options, errp); + ret = throttle_parse_options(options, &group, errp); + if (ret == 0) { + /* Register membership to group with name group_name */ + throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs)); + g_free(group); + } + + return ret; } static void throttle_close(BlockDriverState *bs) @@ -162,35 +173,36 @@ static void throttle_attach_aio_context(BlockDriverState *bs, static int throttle_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp) { - ThrottleGroupMember *tgm; + int ret; + char *group = NULL; assert(reopen_state != NULL); assert(reopen_state->bs != NULL); - reopen_state->opaque = g_new0(ThrottleGroupMember, 1); - tgm = reopen_state->opaque; - - return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options, - errp); + ret = throttle_parse_options(reopen_state->options, &group, errp); + reopen_state->opaque = group; + return ret; } static void throttle_reopen_commit(BDRVReopenState *reopen_state) { - ThrottleGroupMember *old_tgm = reopen_state->bs->opaque; - ThrottleGroupMember *new_tgm = reopen_state->opaque; + BlockDriverState *bs = reopen_state->bs; + ThrottleGroupMember *tgm = bs->opaque; + char *group = reopen_state->opaque; - throttle_group_unregister_tgm(old_tgm); - g_free(old_tgm); - reopen_state->bs->opaque = new_tgm; + assert(group); + + if (strcmp(group, throttle_group_get_name(tgm))) { + throttle_group_unregister_tgm(tgm); + throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs)); + } + g_free(reopen_state->opaque); reopen_state->opaque = NULL; } static void throttle_reopen_abort(BDRVReopenState *reopen_state) { - ThrottleGroupMember *tgm = reopen_state->opaque; - - throttle_group_unregister_tgm(tgm); - g_free(tgm); + g_free(reopen_state->opaque); reopen_state->opaque = NULL; } From cc022140972f8b6ac3973c12ccf9dd6b1d2fd200 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 6 Jun 2018 21:37:00 +0200 Subject: [PATCH 1157/2380] block: Make bdrv_is_writable() public This is a useful function for the whole block layer, so make it public. At the same time, users outside of block.c probably do not need to make use of the reopen functionality, so rename the current function to bdrv_is_writable_after_reopen() create a new bdrv_is_writable() function that just passes NULL to it for the reopen queue. Cc: qemu-stable@nongnu.org Signed-off-by: Max Reitz Message-id: 20180606193702.7113-2-mreitz@redhat.com Reviewed-by: John Snow Reviewed-by: Jeff Cody Signed-off-by: Max Reitz --- block.c | 17 ++++++++++++++--- include/block/block.h | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/block.c b/block.c index 9d577f65bb..50887087f3 100644 --- a/block.c +++ b/block.c @@ -1620,13 +1620,24 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs) /* Returns whether the image file can be written to after the reopen queue @q * has been successfully applied, or right now if @q is NULL. */ -static bool bdrv_is_writable(BlockDriverState *bs, BlockReopenQueue *q) +static bool bdrv_is_writable_after_reopen(BlockDriverState *bs, + BlockReopenQueue *q) { int flags = bdrv_reopen_get_flags(q, bs); return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR; } +/* + * Return whether the BDS can be written to. This is not necessarily + * the same as !bdrv_is_read_only(bs), as inactivated images may not + * be written to but do not count as read-only images. + */ +bool bdrv_is_writable(BlockDriverState *bs) +{ + return bdrv_is_writable_after_reopen(bs, NULL); +} + static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, BdrvChild *c, const BdrvChildRole *role, BlockReopenQueue *reopen_queue, @@ -1664,7 +1675,7 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, /* Write permissions never work with read-only images */ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && - !bdrv_is_writable(bs, q)) + !bdrv_is_writable_after_reopen(bs, q)) { error_setg(errp, "Block node is read-only"); return -EPERM; @@ -1956,7 +1967,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, &perm, &shared); /* Format drivers may touch metadata even if the guest doesn't write */ - if (bdrv_is_writable(bs, reopen_queue)) { + if (bdrv_is_writable_after_reopen(bs, reopen_queue)) { perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; } diff --git a/include/block/block.h b/include/block/block.h index 4dd4f1eab2..e677080c4e 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -408,6 +408,7 @@ bool bdrv_is_read_only(BlockDriverState *bs); int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, bool ignore_allow_rdw, Error **errp); int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); +bool bdrv_is_writable(BlockDriverState *bs); bool bdrv_is_sg(BlockDriverState *bs); bool bdrv_is_inserted(BlockDriverState *bs); void bdrv_lock_medium(BlockDriverState *bs, bool locked); From ddf3b47ef4b5ed0bf6558d4c2c8ae130b8d8a580 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 6 Jun 2018 21:37:01 +0200 Subject: [PATCH 1158/2380] qcow2: Do not mark inactive images corrupt When signaling a corruption on a read-only image, qcow2 already makes fatal events non-fatal (i.e., they will not result in the image being closed, and the image header's corrupt flag will not be set). This is necessary because we cannot set the corrupt flag on read-only images, and it is possible because further corruption of read-only images is impossible. Inactive images are effectively read-only, too, so we should do the same for them. bdrv_is_writable() can tell us whether an image can actually be written to, so use its result instead of !bs->read_only. (Otherwise, the assert(!(bs->open_flags & BDRV_O_INACTIVE)) in bdrv_co_pwritev() will fail, crashing qemu.) Cc: qemu-stable@nongnu.org Signed-off-by: Max Reitz Message-id: 20180606193702.7113-3-mreitz@redhat.com Reviewed-by: John Snow Reviewed-by: Jeff Cody Signed-off-by: Max Reitz --- block/qcow2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/qcow2.c b/block/qcow2.c index 6b2d88759d..6fa5e1d71a 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -4569,7 +4569,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, char *message; va_list ap; - fatal = fatal && !bs->read_only; + fatal = fatal && bdrv_is_writable(bs); if (s->signaled_corruption && (!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT))) From c50abd175a88cd41c2c08339de91f6f6e4a7b162 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 6 Jun 2018 21:37:02 +0200 Subject: [PATCH 1159/2380] iotests: Add case for a corrupted inactive image Reviewed-by: John Snow Tested-by: Jeff Cody Reviewed-by: Jeff Cody Signed-off-by: Max Reitz Message-id: 20180606193702.7113-4-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/060 | 30 ++++++++++++++++++++++++++++++ tests/qemu-iotests/060.out | 14 ++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index 6c7407f499..7bdf609f3f 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -440,6 +440,36 @@ echo "{'execute': 'qmp_capabilities'} -drive if=none,node-name=drive,file="$TEST_IMG",driver=qcow2 \ | _filter_qmp | _filter_qemu_io +echo +echo "=== Testing incoming inactive corrupted image ===" +echo + +_make_test_img 64M +# Create an unaligned L1 entry, so qemu will signal a corruption when +# reading from the covered area +poke_file "$TEST_IMG" "$l1_offset" "\x00\x00\x00\x00\x2a\x2a\x2a\x2a" + +# Inactive images are effectively read-only images, so this should be a +# non-fatal corruption (which does not modify the image) +echo "{'execute': 'qmp_capabilities'} + {'execute': 'human-monitor-command', + 'arguments': {'command-line': 'qemu-io drive \"read 0 512\"'}} + {'execute': 'quit'}" \ + | $QEMU -qmp stdio -nographic -nodefaults \ + -blockdev "{'node-name': 'drive', + 'driver': 'qcow2', + 'file': { + 'driver': 'file', + 'filename': '$TEST_IMG' + }}" \ + -incoming exec:'cat /dev/null' \ + 2>&1 \ + | _filter_qmp | _filter_qemu_io + +echo +# Image should not have been marked corrupt +_img_info --format-specific | grep 'corrupt:' + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index 5f4264cff6..bff023d889 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -420,4 +420,18 @@ write failed: Input/output error {"return": ""} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +=== Testing incoming inactive corrupted image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +QMP_VERSION +{"return": {}} +qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}} +read failed: Input/output error +{"return": ""} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + corrupt: false *** done From 0f5319ea25fb0a05827d512859b7c7d23371ac5d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Tue, 5 Jun 2018 16:00:42 +0200 Subject: [PATCH 1160/2380] cli: Don't run early event loop if no --preconfig was specified After 047f7038f586d215 it is possible for event loop to run two times. First time whilst parsing command line options (the idea is to bring up monitor early so that management applications can tweak config before machine is initialized). And the second time is after everything is set up (this is the usual place). In both cases the event loop is called as main_loop_wait(nonblocking = false) which causes the event loop to block until at least one event occurred. Now, consider that somebody (i.e. libvirt) calls us with -daemonize. This operation is split in two steps. The main() calls os_daemonize() which fork()-s and then waits in read() until child notifies it via write(): /qemu.git $ ./x86_64-softmmu/qemu-system-x86_64 -S -daemonize \ -no-user-config -nodefaults -nographic main(): child: os_daemonize(): read(pipe[0]) main_loop(): main_loop_wait(false) os_setup_post(): write(pipe[1]) main_loop(): main_loop_wait(false) Here it can be clearly seen that main() does not exit until an event occurs, but at the same time nobody will touch the monitor socket until their exec("qemu-system-*") finishes. So the whole thing deadlocks. The solution is to not call main_loop_wait() unless --preconfig was specified (in which case caller knows they must connect to the socket before exec() finishes). Patch also fixes hang when -nodefaults option is used, which were causing QEMU hang in the early main_loop_wait() indefinitely by the same means (not calling main_loop_wait() unless --preconfig is present on CLI) Based on From: Michal Privoznik Subject: [PATCH] cli: Don't run early event loop if no --preconfig was specified Message-Id: Fixes: 047f7038f586d215 Signed-off-by: Igor Mammedov Message-Id: <1528207243-268226-2-git-send-email-imammedo@redhat.com> Signed-off-by: Eduardo Habkost --- vl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vl.c b/vl.c index 06031715ac..6e34fb348d 100644 --- a/vl.c +++ b/vl.c @@ -1841,7 +1841,7 @@ static void main_loop(void) #ifdef CONFIG_PROFILER int64_t ti; #endif - do { + while (!main_loop_should_exit()) { #ifdef CONFIG_PROFILER ti = profile_getclock(); #endif @@ -1849,7 +1849,7 @@ static void main_loop(void) #ifdef CONFIG_PROFILER dev_time += profile_getclock() - ti; #endif - } while (!main_loop_should_exit()); + } } static void version(void) From ab41fc4853cc0cf01ed4903ffe7c36e3768b538f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 11 Jun 2018 14:53:31 -0400 Subject: [PATCH 1161/2380] block: remove bdrv_dirty_bitmap_make_anon All this function is doing will be repeated by bdrv_do_release_matching_dirty_bitmap_locked, except resetting bm->persistent. But even that does not matter because the bitmap will be freed. Signed-off-by: Paolo Bonzini Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Stefan Hajnoczi Message-id: 20180323164254.26487-1-pbonzini@redhat.com Signed-off-by: John Snow --- block/dirty-bitmap.c | 9 --------- blockdev.c | 1 - include/block/dirty-bitmap.h | 1 - 3 files changed, 11 deletions(-) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 967159479d..ea82c06f07 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -97,15 +97,6 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) return NULL; } -/* Called with BQL taken. */ -void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) -{ - assert(!bdrv_dirty_bitmap_frozen(bitmap)); - g_free(bitmap->name); - bitmap->name = NULL; - bitmap->persistent = false; -} - /* Called with BQL taken. */ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, uint32_t granularity, diff --git a/blockdev.c b/blockdev.c index 8de95be8f4..266ecc06cc 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2881,7 +2881,6 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, } } - bdrv_dirty_bitmap_make_anon(bitmap); bdrv_release_dirty_bitmap(bs, bitmap); } diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 1ff8949b1b..5a51a78b63 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -24,7 +24,6 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap); BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name); -void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap); void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs); From b133c27f5dc59969574b0715e5837d32c99caa86 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 11 Jun 2018 14:53:31 -0400 Subject: [PATCH 1162/2380] block: simplify code around releasing bitmaps QLIST_REMOVE does not require walking the list, and once the "bitmap" argument is removed from bdrv_do_release_matching_dirty_bitmap_locked the code simplifies a lot and it is worth inlining everything in the callers of bdrv_do_release_matching_dirty_bitmap. Signed-off-by: Paolo Bonzini Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: John Snow Message-id: 20180326104037.6894-1-pbonzini@redhat.com Signed-off-by: John Snow --- block/dirty-bitmap.c | 84 +++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index ea82c06f07..a7eaa1051d 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -249,49 +249,16 @@ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) qemu_mutex_unlock(bitmap->mutex); } -/* Called within bdrv_dirty_bitmap_lock..unlock */ -static void bdrv_do_release_matching_dirty_bitmap_locked( - BlockDriverState *bs, BdrvDirtyBitmap *bitmap, - bool (*cond)(BdrvDirtyBitmap *bitmap)) +/* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */ +static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) { - BdrvDirtyBitmap *bm, *next; - - QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { - if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) { - assert(!bm->active_iterators); - assert(!bdrv_dirty_bitmap_frozen(bm)); - assert(!bm->meta); - QLIST_REMOVE(bm, list); - hbitmap_free(bm->bitmap); - g_free(bm->name); - g_free(bm); - - if (bitmap) { - return; - } - } - } - - if (bitmap) { - abort(); - } -} - -/* Called with BQL taken. */ -static void bdrv_do_release_matching_dirty_bitmap( - BlockDriverState *bs, BdrvDirtyBitmap *bitmap, - bool (*cond)(BdrvDirtyBitmap *bitmap)) -{ - bdrv_dirty_bitmaps_lock(bs); - bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, cond); - bdrv_dirty_bitmaps_unlock(bs); -} - -/* Called within bdrv_dirty_bitmap_lock..unlock */ -static void bdrv_release_dirty_bitmap_locked(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap) -{ - bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, NULL); + assert(!bitmap->active_iterators); + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + assert(!bitmap->meta); + QLIST_REMOVE(bitmap, list); + hbitmap_free(bitmap->bitmap); + g_free(bitmap->name); + g_free(bitmap); } /** @@ -344,7 +311,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, error_setg(errp, "Merging of parent and successor bitmap failed"); return NULL; } - bdrv_release_dirty_bitmap_locked(bs, successor); + bdrv_release_dirty_bitmap_locked(successor); parent->successor = NULL; return parent; @@ -382,15 +349,12 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) bdrv_dirty_bitmaps_unlock(bs); } -static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap) -{ - return !!bdrv_dirty_bitmap_name(bitmap); -} - /* Called with BQL taken. */ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) { - bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL); + bdrv_dirty_bitmaps_lock(bs); + bdrv_release_dirty_bitmap_locked(bitmap); + bdrv_dirty_bitmaps_unlock(bs); } /** @@ -401,7 +365,15 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) */ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) { - bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name); + BdrvDirtyBitmap *bm, *next; + + bdrv_dirty_bitmaps_lock(bs); + QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { + if (bdrv_dirty_bitmap_name(bm)) { + bdrv_release_dirty_bitmap_locked(bm); + } + } + bdrv_dirty_bitmaps_unlock(bs); } /** @@ -409,11 +381,19 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) * bdrv_inactivate_recurse()). * There must not be any frozen bitmaps attached. * This function does not remove persistent bitmaps from the storage. + * Called with BQL taken. */ void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs) { - bdrv_do_release_matching_dirty_bitmap(bs, NULL, - bdrv_dirty_bitmap_get_persistance); + BdrvDirtyBitmap *bm, *next; + + bdrv_dirty_bitmaps_lock(bs); + QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { + if (bdrv_dirty_bitmap_get_persistance(bm)) { + bdrv_release_dirty_bitmap_locked(bm); + } + } + bdrv_dirty_bitmaps_unlock(bs); } /** From 8b1402ce80d74dc02802f101a0f6c340462380d1 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 11 Jun 2018 14:53:31 -0400 Subject: [PATCH 1163/2380] block/dirty-bitmap: add lock to bdrv_enable/disable_dirty_bitmap Add locks and remove comments about BQL accordingly to dirty_bitmap_mutex definition in block_int.h. Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: John Snow Reviewed-by: Jeff Cody Message-id: 20180606182449.1607-2-jsnow@redhat.com Signed-off-by: John Snow --- block/dirty-bitmap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index a7eaa1051d..59b38b2671 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -413,18 +413,20 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, } } -/* Called with BQL taken. */ void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) { + bdrv_dirty_bitmap_lock(bitmap); assert(!bdrv_dirty_bitmap_frozen(bitmap)); bitmap->disabled = true; + bdrv_dirty_bitmap_unlock(bitmap); } -/* Called with BQL taken. */ void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) { + bdrv_dirty_bitmap_lock(bitmap); assert(!bdrv_dirty_bitmap_frozen(bitmap)); bitmap->disabled = false; + bdrv_dirty_bitmap_unlock(bitmap); } BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) From 5c5d2e50e5ac85234d793f0127a20ea3424a1229 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 11 Jun 2018 14:53:32 -0400 Subject: [PATCH 1164/2380] qapi: add x-block-dirty-bitmap-enable/disable Expose the ability to turn bitmaps "on" or "off". This is experimental and principally for the sake of the Libvirt Checkpoints API, and it may or may not be committed for 3.0. Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: John Snow Reviewed-by: Jeff Cody Message-id: 20180606182449.1607-3-jsnow@redhat.com Signed-off-by: John Snow --- blockdev.c | 42 ++++++++++++++++++++++++++++++++++++++++++ qapi/block-core.json | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/blockdev.c b/blockdev.c index 266ecc06cc..4a353c934a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2922,6 +2922,48 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, bdrv_clear_dirty_bitmap(bitmap, NULL); } +void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); + if (!bitmap) { + return; + } + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently frozen and cannot be enabled", + name); + return; + } + + bdrv_enable_dirty_bitmap(bitmap); +} + +void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); + if (!bitmap) { + return; + } + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently frozen and cannot be disabled", + name); + return; + } + + bdrv_disable_dirty_bitmap(bitmap); +} + BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, const char *name, Error **errp) diff --git a/qapi/block-core.json b/qapi/block-core.json index 4b1de474a9..02de674f5f 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1808,6 +1808,48 @@ { 'command': 'block-dirty-bitmap-clear', 'data': 'BlockDirtyBitmap' } +## +# @x-block-dirty-bitmap-enable: +# +# Enables a dirty bitmap so that it will begin tracking disk changes. +# +# Returns: nothing on success +# If @node is not a valid block device, DeviceNotFound +# If @name is not found, GenericError with an explanation +# +# Since: 3.0 +# +# Example: +# +# -> { "execute": "x-block-dirty-bitmap-enable", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } +# +## + { 'command': 'x-block-dirty-bitmap-enable', + 'data': 'BlockDirtyBitmap' } + +## +# @x-block-dirty-bitmap-disable: +# +# Disables a dirty bitmap so that it will stop tracking disk changes. +# +# Returns: nothing on success +# If @node is not a valid block device, DeviceNotFound +# If @name is not found, GenericError with an explanation +# +# Since: 3.0 +# +# Example: +# +# -> { "execute": "x-block-dirty-bitmap-disable", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } +# +## + { 'command': 'x-block-dirty-bitmap-disable', + 'data': 'BlockDirtyBitmap' } + ## # @BlockDirtyBitmapSha256: # From c6490447402f574bbe45f45c318e0bdd19121baf Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 11 Jun 2018 14:53:32 -0400 Subject: [PATCH 1165/2380] qmp: transaction support for x-block-dirty-bitmap-enable/disable Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: John Snow Reviewed-by: Jeff Cody Message-id: 20180606182449.1607-4-jsnow@redhat.com [Added x- prefix. --js] Signed-off-by: John Snow --- blockdev.c | 81 ++++++++++++++++++++++++++++++++++++++++++- qapi/transaction.json | 4 +++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index 4a353c934a..2ac15601d9 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2052,6 +2052,7 @@ typedef struct BlockDirtyBitmapState { BlockDriverState *bs; HBitmap *backup; bool prepared; + bool was_enabled; } BlockDirtyBitmapState; static void block_dirty_bitmap_add_prepare(BlkActionState *common, @@ -2151,6 +2152,74 @@ static void block_dirty_bitmap_clear_commit(BlkActionState *common) hbitmap_free(state->backup); } +static void block_dirty_bitmap_enable_prepare(BlkActionState *common, + Error **errp) +{ + BlockDirtyBitmap *action; + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + if (action_check_completion_mode(common, errp) < 0) { + return; + } + + action = common->action->u.x_block_dirty_bitmap_enable.data; + state->bitmap = block_dirty_bitmap_lookup(action->node, + action->name, + NULL, + errp); + if (!state->bitmap) { + return; + } + + state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); + bdrv_enable_dirty_bitmap(state->bitmap); +} + +static void block_dirty_bitmap_enable_abort(BlkActionState *common) +{ + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + if (!state->was_enabled) { + bdrv_disable_dirty_bitmap(state->bitmap); + } +} + +static void block_dirty_bitmap_disable_prepare(BlkActionState *common, + Error **errp) +{ + BlockDirtyBitmap *action; + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + if (action_check_completion_mode(common, errp) < 0) { + return; + } + + action = common->action->u.x_block_dirty_bitmap_disable.data; + state->bitmap = block_dirty_bitmap_lookup(action->node, + action->name, + NULL, + errp); + if (!state->bitmap) { + return; + } + + state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); + bdrv_disable_dirty_bitmap(state->bitmap); +} + +static void block_dirty_bitmap_disable_abort(BlkActionState *common) +{ + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + if (state->was_enabled) { + bdrv_enable_dirty_bitmap(state->bitmap); + } +} + static void abort_prepare(BlkActionState *common, Error **errp) { error_setg(errp, "Transaction aborted using Abort action"); @@ -2211,7 +2280,17 @@ static const BlkActionOps actions[] = { .prepare = block_dirty_bitmap_clear_prepare, .commit = block_dirty_bitmap_clear_commit, .abort = block_dirty_bitmap_clear_abort, - } + }, + [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_enable_prepare, + .abort = block_dirty_bitmap_enable_abort, + }, + [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_DISABLE] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_disable_prepare, + .abort = block_dirty_bitmap_disable_abort, + } }; /** diff --git a/qapi/transaction.json b/qapi/transaction.json index bd312792da..d7e4274550 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -46,6 +46,8 @@ # - @abort: since 1.6 # - @block-dirty-bitmap-add: since 2.5 # - @block-dirty-bitmap-clear: since 2.5 +# - @x-block-dirty-bitmap-enable: since 3.0 +# - @x-block-dirty-bitmap-disable: since 3.0 # - @blockdev-backup: since 2.3 # - @blockdev-snapshot: since 2.5 # - @blockdev-snapshot-internal-sync: since 1.7 @@ -59,6 +61,8 @@ 'abort': 'Abort', 'block-dirty-bitmap-add': 'BlockDirtyBitmapAdd', 'block-dirty-bitmap-clear': 'BlockDirtyBitmap', + 'x-block-dirty-bitmap-enable': 'BlockDirtyBitmap', + 'x-block-dirty-bitmap-disable': 'BlockDirtyBitmap', 'blockdev-backup': 'BlockdevBackup', 'blockdev-snapshot': 'BlockdevSnapshot', 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal', From b598e531f1123d2fb72615f1161c66093be751ea Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 11 Jun 2018 14:53:32 -0400 Subject: [PATCH 1166/2380] qapi: add x-block-dirty-bitmap-merge Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: John Snow Reviewed-by: Jeff Cody Message-id: 20180606182449.1607-5-jsnow@redhat.com Signed-off-by: John Snow --- block/dirty-bitmap.c | 18 +++++++++++++++++ blockdev.c | 30 ++++++++++++++++++++++++++++ include/block/dirty-bitmap.h | 3 ++- qapi/block-core.json | 38 ++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 59b38b2671..383d742cdb 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -728,3 +728,21 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) { return hbitmap_next_zero(bitmap->bitmap, offset); } + +void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + Error **errp) +{ + /* only bitmaps from one bds are supported */ + assert(dest->mutex == src->mutex); + + qemu_mutex_lock(dest->mutex); + + assert(bdrv_dirty_bitmap_enabled(dest)); + assert(!bdrv_dirty_bitmap_readonly(dest)); + + if (!hbitmap_merge(dest->bitmap, src->bitmap)) { + error_setg(errp, "Bitmaps are incompatible and can't be merged"); + } + + qemu_mutex_unlock(dest->mutex); +} diff --git a/blockdev.c b/blockdev.c index 2ac15601d9..dcb7a6d54e 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3043,6 +3043,36 @@ void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, bdrv_disable_dirty_bitmap(bitmap); } +void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name, + const char *src_name, Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *dst, *src; + + dst = block_dirty_bitmap_lookup(node, dst_name, &bs, errp); + if (!dst) { + return; + } + + if (bdrv_dirty_bitmap_frozen(dst)) { + error_setg(errp, "Bitmap '%s' is frozen and cannot be modified", + dst_name); + return; + } else if (bdrv_dirty_bitmap_readonly(dst)) { + error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", + dst_name); + return; + } + + src = bdrv_find_dirty_bitmap(bs, src_name); + if (!src) { + error_setg(errp, "Dirty bitmap '%s' not found", src_name); + return; + } + + bdrv_merge_dirty_bitmap(dst, src, errp); +} + BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, const char *name, Error **errp) diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 5a51a78b63..02e0cbabd2 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -69,7 +69,8 @@ void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent); void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked); - +void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + Error **errp); /* Functions that require manual locking. */ void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); diff --git a/qapi/block-core.json b/qapi/block-core.json index 02de674f5f..9d4ab93190 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1740,6 +1740,20 @@ 'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32', '*persistent': 'bool', '*autoload': 'bool' } } +## +# @BlockDirtyBitmapMerge: +# +# @node: name of device/node which the bitmap is tracking +# +# @dst_name: name of the destination dirty bitmap +# +# @src_name: name of the source dirty bitmap +# +# Since: 3.0 +## +{ 'struct': 'BlockDirtyBitmapMerge', + 'data': { 'node': 'str', 'dst_name': 'str', 'src_name': 'str' } } + ## # @block-dirty-bitmap-add: # @@ -1850,6 +1864,30 @@ { 'command': 'x-block-dirty-bitmap-disable', 'data': 'BlockDirtyBitmap' } +## +# @x-block-dirty-bitmap-merge: +# +# Merge @src_name dirty bitmap to @dst_name dirty bitmap. @src_name dirty +# bitmap is unchanged. On error, @dst_name is unchanged. +# +# Returns: nothing on success +# If @node is not a valid block device, DeviceNotFound +# If @dst_name or @src_name is not found, GenericError +# If bitmaps has different sizes or granularities, GenericError +# +# Since: 3.0 +# +# Example: +# +# -> { "execute": "x-block-dirty-bitmap-merge", +# "arguments": { "node": "drive0", "dst_name": "bitmap0", +# "src_name": "bitmap1" } } +# <- { "return": {} } +# +## + { 'command': 'x-block-dirty-bitmap-merge', + 'data': 'BlockDirtyBitmapMerge' } + ## # @BlockDirtyBitmapSha256: # From a6e2ca5f6521553681ae136578ec1cb67e1a7973 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 11 Jun 2018 14:53:32 -0400 Subject: [PATCH 1167/2380] qapi: add disabled parameter to block-dirty-bitmap-add This is needed, for example, to create a new bitmap and merge several disabled bitmaps into a new one. Without this flag we will have to put block-dirty-bitmap-add and block-dirty-bitmap-disable into one transaction. Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: John Snow Reviewed-by: Jeff Cody Message-id: 20180606182449.1607-6-jsnow@redhat.com Signed-off-by: John Snow --- blockdev.c | 10 ++++++++++ qapi/block-core.json | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index dcb7a6d54e..4862323012 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2073,6 +2073,7 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common, action->has_granularity, action->granularity, action->has_persistent, action->persistent, action->has_autoload, action->autoload, + action->has_x_disabled, action->x_disabled, &local_err); if (!local_err) { @@ -2880,6 +2881,7 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, bool has_granularity, uint32_t granularity, bool has_persistent, bool persistent, bool has_autoload, bool autoload, + bool has_disabled, bool disabled, Error **errp) { BlockDriverState *bs; @@ -2914,6 +2916,10 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, warn_report("Autoload option is deprecated and its value is ignored"); } + if (!has_disabled) { + disabled = false; + } + if (persistent && !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) { @@ -2925,6 +2931,10 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, return; } + if (disabled) { + bdrv_disable_dirty_bitmap(bitmap); + } + bdrv_dirty_bitmap_set_persistance(bitmap, persistent); } diff --git a/qapi/block-core.json b/qapi/block-core.json index 9d4ab93190..fff23fc82b 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1734,11 +1734,15 @@ # Currently, all dirty tracking bitmaps are loaded from Qcow2 on # open. # +# @x-disabled: the bitmap is created in the disabled state, which means that +# it will not track drive changes. The bitmap may be enabled with +# x-block-dirty-bitmap-enable. Default is false. (Since: 3.0) +# # Since: 2.4 ## { 'struct': 'BlockDirtyBitmapAdd', 'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32', - '*persistent': 'bool', '*autoload': 'bool' } } + '*persistent': 'bool', '*autoload': 'bool', '*x-disabled': 'bool' } } ## # @BlockDirtyBitmapMerge: From 0f3ef1c0628d15e27b1cc4bf9fbd0e5532c59c76 Mon Sep 17 00:00:00 2001 From: Ross Zwisler Date: Thu, 7 Jun 2018 16:31:09 -0600 Subject: [PATCH 1168/2380] hw/i386: Update SSDT table used by "make check" This commit: commit aa78a16d8645 ("hw/i386: Rename 2.13 machine types to 3.0") updated the name used to create the q35 machine, which in turn changed the SSDT table which is generated when we run "make check": acpi-test: Warning! SSDT mismatch. Actual [asl:/tmp/asl-QZDWJZ.dsl, aml:/tmp/aml-T8JYJZ], Expected [asl:/tmp/asl-DTWVJZ.dsl, aml:tests/acpi-test-data/q35/SSDT.dimmpxm]. Here's the only difference, aside from the checksum: < Name (MEMA, 0x07FFF000) --- > Name (MEMA, 0x07FFE000) Update the binary table that we compare against so it reflects this name change. Signed-off-by: Ross Zwisler Cc: Peter Maydell Cc: Cornelia Huck Cc: Thomas Huth Cc: Eduardo Habkost Fixes: commit aa78a16d8645 ("hw/i386: Rename 2.13 machine types to 3.0") Tested-by: Thomas Huth Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/acpi-test-data/q35/SSDT.dimmpxm | Bin 685 -> 685 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/acpi-test-data/q35/SSDT.dimmpxm b/tests/acpi-test-data/q35/SSDT.dimmpxm index 8ba0e67cb72daa81a65da4906d37a5e0f4af1fd4..2d5b721bcf9c398feb6d005761f898015042e8a4 100644 GIT binary patch delta 23 fcmZ3>x|WqIIM^j*EfWI+qr*n71x(Bz{<8xBPCEwk delta 23 fcmZ3>x|WqIIM^j*EfWI+W57nP1x(Bj{<8xBPMZev From 11c39b5cd966ddc067a1ca0c5392ec9b666c45b7 Mon Sep 17 00:00:00 2001 From: Ross Zwisler Date: Thu, 7 Jun 2018 16:31:11 -0600 Subject: [PATCH 1169/2380] nvdimm: make persistence option symbolic Replace the "nvdimm-cap" option which took numeric arguments such as "2" with a more user friendly "nvdimm-persistence" option which takes symbolic arguments "cpu" or "mem-ctrl". Signed-off-by: Ross Zwisler Suggested-by: Michael S. Tsirkin Suggested-by: Dan Williams Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/nvdimm.txt | 31 ++++++++++++------------------- hw/acpi/nvdimm.c | 4 ++-- hw/i386/pc.c | 33 ++++++++++++++++----------------- include/hw/i386/pc.h | 2 +- include/hw/mem/nvdimm.h | 3 ++- tests/bios-tables-test.c | 2 +- 6 files changed, 34 insertions(+), 41 deletions(-) diff --git a/docs/nvdimm.txt b/docs/nvdimm.txt index 8b48fb4633..24b443b655 100644 --- a/docs/nvdimm.txt +++ b/docs/nvdimm.txt @@ -154,29 +154,22 @@ guest software that this vNVDIMM device contains a region that cannot accept persistent writes. In result, for example, the guest Linux NVDIMM driver, marks such vNVDIMM device as read-only. -Platform Capabilities ---------------------- +NVDIMM Persistence +------------------ ACPI 6.2 Errata A added support for a new Platform Capabilities Structure which allows the platform to communicate what features it supports related to -NVDIMM data durability. Users can provide a capabilities value to a guest via -the optional "nvdimm-cap" machine command line option: +NVDIMM data persistence. Users can provide a persistence value to a guest via +the optional "nvdimm-persistence" machine command line option: - -machine pc,accel=kvm,nvdimm,nvdimm-cap=2 + -machine pc,accel=kvm,nvdimm,nvdimm-persistence=cpu -This "nvdimm-cap" field is an integer, and is the combined value of the -various capability bits defined in table 5-137 of the ACPI 6.2 Errata A spec. +There are currently two valid values for this option: -Here is a quick summary of the three bits that are defined as of that spec: +"mem-ctrl" - The platform supports flushing dirty data from the memory + controller to the NVDIMMs in the event of power loss. -Bit[0] - CPU Cache Flush to NVDIMM Durability on Power Loss Capable. -Bit[1] - Memory Controller Flush to NVDIMM Durability on Power Loss Capable. - Note: If bit 0 is set to 1 then this bit shall be set to 1 as well. -Bit[2] - Byte Addressable Persistent Memory Hardware Mirroring Capable. - -So, a "nvdimm-cap" value of 2 would mean that the platform supports Memory -Controller Flush on Power Loss, a value of 3 would mean that the platform -supports CPU Cache Flush and Memory Controller Flush on Power Loss, etc. - -For a complete list of the flags available and for more detailed descriptions, -please consult the ACPI spec. +"cpu" - The platform supports flushing dirty data from the CPU cache to + the NVDIMMs in the event of power loss. This implies that the + platform also supports flushing dirty data through the memory + controller on power loss. diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 87e4280c71..27eeb6609f 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -404,8 +404,8 @@ static GArray *nvdimm_build_device_structure(AcpiNVDIMMState *state) } g_slist_free(device_list); - if (state->capabilities) { - nvdimm_build_structure_caps(structures, state->capabilities); + if (state->persistence) { + nvdimm_build_structure_caps(structures, state->persistence); } return structures; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f3befe6721..5bba9dcf5a 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -2181,31 +2181,30 @@ static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp) pcms->acpi_nvdimm_state.is_enabled = value; } -static void pc_machine_get_nvdimm_capabilities(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) +static char *pc_machine_get_nvdimm_persistence(Object *obj, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - uint32_t value = pcms->acpi_nvdimm_state.capabilities; - visit_type_uint32(v, name, &value, errp); + return g_strdup(pcms->acpi_nvdimm_state.persistence_string); } -static void pc_machine_set_nvdimm_capabilities(Object *obj, Visitor *v, - const char *name, void *opaque, +static void pc_machine_set_nvdimm_persistence(Object *obj, const char *value, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - Error *error = NULL; - uint32_t value; + AcpiNVDIMMState *nvdimm_state = &pcms->acpi_nvdimm_state; - visit_type_uint32(v, name, &value, &error); - if (error) { - error_propagate(errp, error); - return; + if (strcmp(value, "cpu") == 0) + nvdimm_state->persistence = 3; + else if (strcmp(value, "mem-ctrl") == 0) + nvdimm_state->persistence = 2; + else { + error_report("-machine nvdimm-persistence=%s: unsupported option", value); + exit(EXIT_FAILURE); } - pcms->acpi_nvdimm_state.capabilities = value; + g_free(nvdimm_state->persistence_string); + nvdimm_state->persistence_string = g_strdup(value); } static bool pc_machine_get_smbus(Object *obj, Error **errp) @@ -2421,9 +2420,9 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) object_class_property_add_bool(oc, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm, pc_machine_set_nvdimm, &error_abort); - object_class_property_add(oc, PC_MACHINE_NVDIMM_CAP, "uint32", - pc_machine_get_nvdimm_capabilities, - pc_machine_set_nvdimm_capabilities, NULL, NULL, &error_abort); + object_class_property_add_str(oc, PC_MACHINE_NVDIMM_PERSIST, + pc_machine_get_nvdimm_persistence, + pc_machine_set_nvdimm_persistence, &error_abort); object_class_property_add_bool(oc, PC_MACHINE_SMBUS, pc_machine_get_smbus, pc_machine_set_smbus, &error_abort); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 04d1f8c6c3..fc8dedca12 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -76,7 +76,7 @@ struct PCMachineState { #define PC_MACHINE_VMPORT "vmport" #define PC_MACHINE_SMM "smm" #define PC_MACHINE_NVDIMM "nvdimm" -#define PC_MACHINE_NVDIMM_CAP "nvdimm-cap" +#define PC_MACHINE_NVDIMM_PERSIST "nvdimm-persistence" #define PC_MACHINE_SMBUS "smbus" #define PC_MACHINE_SATA "sata" #define PC_MACHINE_PIT "pit" diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index 3c82751bab..9340631cfc 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -138,7 +138,8 @@ struct AcpiNVDIMMState { /* * Platform capabilities, section 5.2.25.9 of ACPI 6.2 Errata A */ - int32_t capabilities; + int32_t persistence; + char *persistence_string; }; typedef struct AcpiNVDIMMState AcpiNVDIMMState; diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 256d463cb8..4e24930c4b 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -830,7 +830,7 @@ static void test_acpi_tcg_dimm_pxm(const char *machine) memset(&data, 0, sizeof(data)); data.machine = machine; data.variant = ".dimmpxm"; - test_acpi_one(" -machine nvdimm=on,nvdimm-cap=3" + test_acpi_one(" -machine nvdimm=on,nvdimm-persistence=cpu" " -smp 4,sockets=4" " -m 128M,slots=3,maxmem=1G" " -numa node,mem=32M,nodeid=0" From 681431893e7c2414825aa3f5265368adbe005dae Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 4 May 2018 10:45:50 +0200 Subject: [PATCH 1170/2380] ppc440_pcix: Fix a typo in setting a register (Coverity CID1390577) Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/ppc/ppc440_pcix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c index b1307e6477..d8af04b70f 100644 --- a/hw/ppc/ppc440_pcix.c +++ b/hw/ppc/ppc440_pcix.c @@ -257,7 +257,7 @@ static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr, break; case PCIX0_PIM2SAL: s->pim[2].sa &= 0xffffffff00000000ULL; - s->pim[2].sa = val; + s->pim[2].sa |= val; ppc440_pcix_update_pim(s, 2); break; case PCIX0_PIM2LAL: From 56e7404bc19c8abfe02c08f733ad387b58817f94 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 6 May 2018 15:20:03 +0100 Subject: [PATCH 1171/2380] macio: add trace-events to timer device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/misc/macio/macio.c | 3 +++ hw/misc/macio/trace-events | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 79621eb879..f9a40eea81 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -32,6 +32,7 @@ #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" #include "hw/intc/heathrow_pic.h" +#include "trace.h" /* Note: this code is strongly inspirated from the corresponding code * in PearPC */ @@ -246,6 +247,7 @@ static void macio_oldworld_init(Object *obj) static void timer_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { + trace_macio_timer_write(addr, size, value); } static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) @@ -266,6 +268,7 @@ static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) break; } + trace_macio_timer_read(addr, size, value); return value; } diff --git a/hw/misc/macio/trace-events b/hw/misc/macio/trace-events index 24c0a36824..d499d78c99 100644 --- a/hw/misc/macio/trace-events +++ b/hw/misc/macio/trace-events @@ -9,3 +9,7 @@ cuda_packet_receive(int len) "length %d" cuda_packet_receive_data(int i, const uint8_t data) "[%d] 0x%02x" cuda_packet_send(int len) "length %d" cuda_packet_send_data(int i, const uint8_t data) "[%d] 0x%02x" + +# hw/misc/macio/macio.c +macio_timer_write(uint64_t addr, unsigned len, uint64_t val) "write addr 0x%"PRIx64 " len %d val 0x%"PRIx64 +macio_timer_read(uint64_t addr, unsigned len, uint32_t val) "read addr 0x%"PRIx64 " len %d val 0x%"PRIx32 From 45fefe7c4d9bfc5a4a222d3d2c8244531e87130f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 6 May 2018 15:20:05 +0100 Subject: [PATCH 1172/2380] uninorth: remove token register from uninorth device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit >From observation of various OS sources it can be seen that the token register introduced in 4e46dcdbd3 "PPC: Newworld: Add uninorth token register" is not required, since the only register currently implemented is the uninorth hardware version which is read-only. Remove the token register implementation and instead return the uninorth version corresponding to the hardware. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/pci-host/uninorth.c | 11 +++++------ include/hw/pci-host/uninorth.h | 4 +++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index ba76b84dbc..a843aa7b36 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -524,19 +524,18 @@ static void unin_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { trace_unin_write(addr, value); - if (addr == 0x0) { - *(int *)opaque = value; - } } static uint64_t unin_read(void *opaque, hwaddr addr, unsigned size) { uint32_t value; - value = 0; switch (addr) { case 0: - value = *(int *)opaque; + value = UNINORTH_VERSION_10A; + break; + default: + value = 0; } trace_unin_read(addr, value); @@ -555,7 +554,7 @@ static void unin_init(Object *obj) UNINState *s = UNI_NORTH(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->mem, obj, &unin_ops, &s->token, "unin", 0x1000); + memory_region_init_io(&s->mem, obj, &unin_ops, s, "unin", 0x1000); sysbus_init_mmio(sbd, &s->mem); } diff --git a/include/hw/pci-host/uninorth.h b/include/hw/pci-host/uninorth.h index f6654bad9b..2a1cf9f284 100644 --- a/include/hw/pci-host/uninorth.h +++ b/include/hw/pci-host/uninorth.h @@ -29,6 +29,9 @@ #include "hw/ppc/openpic.h" +/* UniNorth version */ +#define UNINORTH_VERSION_10A 0x7 + #define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost" #define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost" #define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost" @@ -57,7 +60,6 @@ typedef struct UNINState { SysBusDevice parent_obj; MemoryRegion mem; - int token[1]; } UNINState; #define TYPE_UNI_NORTH "uni-north" From e20c63140a185c166e91d8c68a2aa6bb99e600c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 May 2018 14:48:19 -0300 Subject: [PATCH 1173/2380] hw/ppc/spapr_drc: Replace error_setg(&error_abort) by error_report() + abort() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use error_report() + abort() instead of error_setg(&error_abort), as suggested by the "qapi/error.h" documentation: Please don't error_setg(&error_fatal, ...), use error_report() and exit(), because that's more obvious. Likewise, don't error_setg(&error_abort, ...), use assert(). Use abort() instead of the suggested assert() because the error message already got displayed. Suggested-by: Eric Blake Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Eric Auger Signed-off-by: David Gibson --- hw/ppc/spapr_drc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 8a045d6b93..2edb7d1e9c 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -366,7 +366,8 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, break; } default: - error_setg(&error_abort, "device FDT in unexpected state: %d", tag); + error_report("device FDT in unexpected state: %d", tag); + abort(); } fdt_offset = fdt_offset_next; } while (fdt_depth != 0); From 31085338293a1203187c6ef6dba9dfce14021189 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 28 May 2018 20:11:19 +0200 Subject: [PATCH 1174/2380] target/ppc: Use proper logging function for possible guest errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fprintf() and qemu_log_separate() are frowned upon these days for printing logging information in QEMU. Accessing the wrong SPRs indicates wrong guest behaviour in most cases, and we've got a proper way to log such situations, which is the qemu_log_mask(LOG_GUEST_ERROR, ...) function. So use this function now for logging the bad SPR accesses instead. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Greg Kurz Reviewed-by: Alistair Francis Signed-off-by: David Gibson --- target/ppc/translate.c | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index b28e8b91d3..8ba8f67dc5 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3933,13 +3933,9 @@ static inline void gen_op_mfspr(DisasContext *ctx) * allowing userland application to read the PVR */ if (sprn != SPR_PVR) { - fprintf(stderr, "Trying to read privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - if (qemu_log_separate()) { - qemu_log("Trying to read privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, - ctx->base.pc_next - 4); - } + qemu_log_mask(LOG_GUEST_ERROR, "Trying to read privileged spr " + "%d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, + ctx->base.pc_next - 4); } gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } @@ -3951,12 +3947,9 @@ static inline void gen_op_mfspr(DisasContext *ctx) return; } /* Not defined */ - fprintf(stderr, "Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - if (qemu_log_separate()) { - qemu_log("Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - } + qemu_log_mask(LOG_GUEST_ERROR, + "Trying to read invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); /* The behaviour depends on MSR:PR and SPR# bit 0x10, * it can generate a priv, a hv emu or a no-op @@ -4097,12 +4090,9 @@ static void gen_mtspr(DisasContext *ctx) (*write_cb)(ctx, sprn, rS(ctx->opcode)); } else { /* Privilege exception */ - fprintf(stderr, "Trying to write privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - if (qemu_log_separate()) { - qemu_log("Trying to write privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - } + qemu_log_mask(LOG_GUEST_ERROR, "Trying to write privileged spr " + "%d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, + ctx->base.pc_next - 4); gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } else { @@ -4114,12 +4104,9 @@ static void gen_mtspr(DisasContext *ctx) } /* Not defined */ - if (qemu_log_separate()) { - qemu_log("Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - } - fprintf(stderr, "Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); + qemu_log_mask(LOG_GUEST_ERROR, + "Trying to write invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); /* The behaviour depends on MSR:PR and SPR# bit 0x10, From 875bad313580f99cc9642d2941d0dbeeadf4ef25 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 25 May 2018 22:15:23 +0100 Subject: [PATCH 1175/2380] 40p: remove pci_allow_0_address = true from 40p machine class The Linux sandalfoot zImage has an initialisation process which resets the VGA controller by setting all the BAR addresses to zero to access the VGA ioports at their legacy addresses. Unfortunately setting the framebuffer BAR to address 0 makes the framebuffer memory overlap the internal VGA memory causing accesses to fail, and so prevents the kernel from switching successfully to text mode. Since OpenHackWare configures the framebuffer BAR address outside of the legacy VGA internal memory space, remove pci_allow_0_address from the 40p machine class which causes the BAR reprogramming to zero to fail and so the VGA internal memory can be accessed correctly again. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/ppc/prep.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index a1e7219db6..dbbe749bde 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -885,7 +885,6 @@ static void ibm_40p_machine_init(MachineClass *mc) mc->desc = "IBM RS/6000 7020 (40p)", mc->init = ibm_40p_init; mc->max_cpus = 1; - mc->pci_allow_0_address = true; mc->default_ram_size = 128 * M_BYTE; mc->block_default_type = IF_SCSI; mc->default_boot_order = "c"; From 7cb00357c155240bae3b17eaa00415087b004c52 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 24 May 2018 06:39:58 +0100 Subject: [PATCH 1176/2380] prep: fix keyboard for the 40p machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 72d3d8f052 "hw/isa/superio: Add a keyboard/mouse controller (8042)" added an 8042 keyboard device to the PC87312 superio device to replace that being used by the prep machine. Unfortunately this commit didn't do the same for the 40p machine which broke the keyboard by registering two 8042 keyboard devices at the same address. Resolve this by similarly removing the 8042 keyboard from the 40p machine as done for the prep machine in commit 72d3d8f052. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Hervé Poussineau Signed-off-by: David Gibson --- hw/ppc/prep.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index dbbe749bde..5ed0bcd862 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -770,7 +770,6 @@ static void ibm_40p_init(MachineState *machine) /* add some more devices */ if (defaults_enabled()) { - isa_create_simple(isa_bus, TYPE_I8042); m48t59 = NVRAM(isa_create_simple(isa_bus, "isa-m48t59")); dev = DEVICE(isa_create(isa_bus, "cs4231a")); From 8fea70440eb0d095442de7e80d586a285cf96be5 Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Fri, 11 May 2018 16:25:07 +1000 Subject: [PATCH 1177/2380] target/ppc: Factor out the parsing in kvmppc_get_cpu_characteristics() Factor out the parsing of struct kvm_ppc_cpu_char in kvmppc_get_cpu_characteristics() into a separate function for each cap for simplicity. Signed-off-by: Suraj Jitindar Singh Signed-off-by: David Gibson --- target/ppc/kvm.c | 59 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index cbe13b18d1..2c0c34e125 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2412,6 +2412,41 @@ bool kvmppc_has_cap_mmu_hash_v3(void) return cap_mmu_hash_v3; } +static int parse_cap_ppc_safe_cache(struct kvm_ppc_cpu_char c) +{ + if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_L1D_FLUSH_PR) { + return 2; + } else if ((c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && + (c.character & c.character_mask + & (H_CPU_CHAR_L1D_FLUSH_ORI30 | H_CPU_CHAR_L1D_FLUSH_TRIG2))) { + return 1; + } + + return 0; +} + +static int parse_cap_ppc_safe_bounds_check(struct kvm_ppc_cpu_char c) +{ + if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_BNDS_CHK_SPEC_BAR) { + return 2; + } else if (c.character & c.character_mask & H_CPU_CHAR_SPEC_BAR_ORI31) { + return 1; + } + + return 0; +} + +static int parse_cap_ppc_safe_indirect_branch(struct kvm_ppc_cpu_char c) +{ + if (c.character & c.character_mask & H_CPU_CHAR_CACHE_COUNT_DIS) { + return SPAPR_CAP_FIXED_CCD; + } else if (c.character & c.character_mask & H_CPU_CHAR_BCCTRL_SERIALISED) { + return SPAPR_CAP_FIXED_IBS; + } + + return 0; +} + static void kvmppc_get_cpu_characteristics(KVMState *s) { struct kvm_ppc_cpu_char c; @@ -2430,26 +2465,10 @@ static void kvmppc_get_cpu_characteristics(KVMState *s) if (ret < 0) { return; } - /* Parse and set cap_ppc_safe_cache */ - if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_L1D_FLUSH_PR) { - cap_ppc_safe_cache = 2; - } else if ((c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && - (c.character & c.character_mask - & (H_CPU_CHAR_L1D_FLUSH_ORI30 | H_CPU_CHAR_L1D_FLUSH_TRIG2))) { - cap_ppc_safe_cache = 1; - } - /* Parse and set cap_ppc_safe_bounds_check */ - if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_BNDS_CHK_SPEC_BAR) { - cap_ppc_safe_bounds_check = 2; - } else if (c.character & c.character_mask & H_CPU_CHAR_SPEC_BAR_ORI31) { - cap_ppc_safe_bounds_check = 1; - } - /* Parse and set cap_ppc_safe_indirect_branch */ - if (c.character & c.character_mask & H_CPU_CHAR_CACHE_COUNT_DIS) { - cap_ppc_safe_indirect_branch = SPAPR_CAP_FIXED_CCD; - } else if (c.character & c.character_mask & H_CPU_CHAR_BCCTRL_SERIALISED) { - cap_ppc_safe_indirect_branch = SPAPR_CAP_FIXED_IBS; - } + + cap_ppc_safe_cache = parse_cap_ppc_safe_cache(c); + cap_ppc_safe_bounds_check = parse_cap_ppc_safe_bounds_check(c); + cap_ppc_safe_indirect_branch = parse_cap_ppc_safe_indirect_branch(c); } int kvmppc_get_cap_safe_cache(void) From 6b37554458a1e8cfe8f91ef0beac82e8de8f85bb Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 4 Jun 2018 18:15:13 +0930 Subject: [PATCH 1178/2380] target/ppc: Allow privileged access to SPR_PCR The powerpc Linux kernel[1] and skiboot firmware[2] recently gained changes that cause the Processor Compatibility Register (PCR) SPR to be cleared. These changes cause Linux to fail to boot on the Qemu powernv machine with an error: Trying to write privileged spr 338 (0x152) at 0000000030017f0c With this patch Qemu makes this register available as a hypervisor privileged register. Note that bits set in this register disable features of the processor. Currently the only register state that is supported is when the register is zeroed (enable all features). This is sufficient for guests to once again boot. [1] https://lkml.kernel.org/r/20180518013742.24095-1-mikey@neuling.org [2] https://patchwork.ozlabs.org/patch/915932/ Signed-off-by: Joel Stanley Signed-off-by: David Gibson --- target/ppc/helper.h | 1 + target/ppc/misc_helper.c | 9 +++++++++ target/ppc/translate_init.inc.c | 9 +++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 19453c6813..d751f0e219 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -17,6 +17,7 @@ DEF_HELPER_2(pminsn, void, env, i32) DEF_HELPER_1(rfid, void, env) DEF_HELPER_1(hrfid, void, env) DEF_HELPER_2(store_lpcr, void, env, tl) +DEF_HELPER_2(store_pcr, void, env, tl) #endif DEF_HELPER_1(check_tlb_flush_local, void, env) DEF_HELPER_1(check_tlb_flush_global, void, env) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 8c8cba5cc6..b884930096 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -20,6 +20,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" +#include "qemu/error-report.h" #include "helper_regs.h" @@ -98,6 +99,14 @@ void helper_store_ptcr(CPUPPCState *env, target_ulong val) tlb_flush(CPU(cpu)); } } + +void helper_store_pcr(CPUPPCState *env, target_ulong value) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + + env->spr[SPR_PCR] = value & pcc->pcr_mask; +} #endif /* defined(TARGET_PPC64) */ void helper_store_pidr(CPUPPCState *env, target_ulong val) diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index ab782cb32a..1a89017dde 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -424,6 +424,10 @@ static void spr_write_ptcr(DisasContext *ctx, int sprn, int gprn) gen_helper_store_ptcr(cpu_env, cpu_gpr[gprn]); } +static void spr_write_pcr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_pcr(cpu_env, cpu_gpr[gprn]); +} #endif #endif @@ -7957,11 +7961,12 @@ static void gen_spr_power6_common(CPUPPCState *env) #endif /* * Register PCR to report POWERPC_EXCP_PRIV_REG instead of - * POWERPC_EXCP_INVAL_SPR. + * POWERPC_EXCP_INVAL_SPR in userspace. Permit hypervisor access. */ - spr_register(env, SPR_PCR, "PCR", + spr_register_hv(env, SPR_PCR, "PCR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pcr, 0x00000000); } From efe2add7cb7f870ebd90ac4f9637161a4821200a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 5 Jun 2018 08:56:26 +0200 Subject: [PATCH 1179/2380] spapr/vio: deprecate the "irq" property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VIO devices have an "irq" property that can be used by the sPAPR IRQ allocator as an IRQ number hint. But it is not set in QEMU nor in libvirt. It brings unnecessary complexity to the underlying layers managing the IRQ number space and it is in full opposition with the new static IRQ allocator we want to introduce in sPAPR. Let's deprecate it to simplify the spapr_irq_alloc routine in the future. Signed-off-by: Cédric Le Goater Reviewed-by: Thomas Huth Reviewed-by: Greg Kurz [dwg: Check qtest_enabled() to suppress bogus warnings from make check] Signed-off-by: David Gibson --- hw/ppc/spapr_vio.c | 22 +++++++++++++++++++++- qemu-doc.texi | 12 ++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 472dd6f33a..4555c648a8 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qapi/visitor.h" #include "hw/hw.h" #include "qemu/log.h" #include "sysemu/sysemu.h" @@ -32,6 +33,7 @@ #include "sysemu/kvm.h" #include "sysemu/device_tree.h" #include "kvm_ppc.h" +#include "sysemu/qtest.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" @@ -41,8 +43,26 @@ #include +static void spapr_vio_getset_irq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Property *prop = opaque; + uint32_t *ptr = qdev_get_prop_ptr(DEVICE(obj), prop); + + if (!qtest_enabled()) { + warn_report(TYPE_VIO_SPAPR_DEVICE " '%s' property is deprecated", name); + } + visit_type_uint32(v, name, ptr, errp); +} + +static const PropertyInfo spapr_vio_irq_propinfo = { + .name = "irq", + .get = spapr_vio_getset_irq, + .set = spapr_vio_getset_irq, +}; + static Property spapr_vio_props[] = { - DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, irq, 0), \ + DEFINE_PROP("irq", VIOsPAPRDevice, irq, spapr_vio_irq_propinfo, uint32_t), DEFINE_PROP_END_OF_LIST(), }; diff --git a/qemu-doc.texi b/qemu-doc.texi index 9aff6b4ea9..cd05760cac 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2958,13 +2958,21 @@ support page sizes < 4096 any longer. @section System emulator machines -@section Block device options +@section Device options -@subsection "backing": "" (since 2.12.0) +@subsection Block device options + +@subsubsection "backing": "" (since 2.12.0) In order to prevent QEMU from automatically opening an image's backing chain, use ``"backing": null'' instead. +@subsection vio-spapr-device device options + +@subsubsection "irq": "" (since 3.0.0) + +The ``irq'' property is obsoleted. + @node Supported build platforms @appendix Supported build platforms From 0c1272cc7c72dfe0ef66be8f283cf67c74b58586 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 6 May 2018 17:29:49 +1000 Subject: [PATCH 1180/2380] osdep: powerpc64 align memory to allow 2MB radix THP page tables This allows KVM with the Book3S radix MMU mode to take advantage of THP and install larger pages in the partition scope page tables (the host translation). Signed-off-by: Nicholas Piggin Signed-off-by: David Gibson --- include/qemu/osdep.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index afc28e5903..9ed62423c0 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -367,7 +367,8 @@ void qemu_anon_ram_free(void *ptr, size_t size); #endif #if defined(__linux__) && \ - (defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)) + (defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) \ + || defined(__powerpc64__)) /* Use 2 MiB alignment so transparent hugepages can be used by KVM. Valgrind does not support alignments larger than 1 MiB, therefore we need special code which handles running on Valgrind. */ From eba45926c2dfb64000da7f83b253f935d6d77941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Jun 2018 11:59:19 -0300 Subject: [PATCH 1181/2380] MAINTAINERS: Add an entry for the MacIO device headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed while moved in 7092e84d42b. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index a40f558694..5286a1519c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -766,8 +766,9 @@ F: hw/ppc/mac_newworld.c F: hw/pci-host/uninorth.c F: hw/pci-bridge/dec.[hc] F: hw/misc/macio/ -F: include/hw/ppc/mac_dbdma.h F: hw/nvram/mac_nvram.c +F: include/hw/misc/macio/ +F: include/hw/ppc/mac_dbdma.h Old World M: Alexander Graf From 6ba1647664efc0d7295c03ad4295df403853c70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Jun 2018 11:59:20 -0300 Subject: [PATCH 1182/2380] MAINTAINERS: Add entries for the MOS6522 VIA device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduced in 51f233ec92c. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5286a1519c..8a94517e9e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -766,8 +766,10 @@ F: hw/ppc/mac_newworld.c F: hw/pci-host/uninorth.c F: hw/pci-bridge/dec.[hc] F: hw/misc/macio/ +F: hw/misc/mos6522.c F: hw/nvram/mac_nvram.c F: include/hw/misc/macio/ +F: include/hw/misc/mos6522.h F: include/hw/ppc/mac_dbdma.h Old World From c9bca798447879ee1288a469604cf824bc2a4622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Jun 2018 11:59:21 -0300 Subject: [PATCH 1183/2380] hw/misc/mos6522: Add trailing '\n' to qemu_log() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/misc/mos6522.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 6163cea6ab..49166cb8ad 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -189,12 +189,12 @@ static uint64_t mos6522_get_load_time(MOS6522State *s, MOS6522Timer *ti) static void mos6522_portA_write(MOS6522State *s) { - qemu_log_mask(LOG_UNIMP, "portA_write unimplemented"); + qemu_log_mask(LOG_UNIMP, "portA_write unimplemented\n"); } static void mos6522_portB_write(MOS6522State *s) { - qemu_log_mask(LOG_UNIMP, "portB_write unimplemented"); + qemu_log_mask(LOG_UNIMP, "portB_write unimplemented\n"); } uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) From a72fed214a9a9503764fc6f62eea17dd09cb487a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Jun 2018 17:59:53 +0100 Subject: [PATCH 1184/2380] ppc: remove obsolete pci_pmac_init() definitions from mac.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commits 7b19318bee and 8ce3f743c7 removed the pci_pmac_init() and pci_pmac_u3_init() functions but missed the header prototypes in mac.h. Remove them since they are no longer needed. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/ppc/mac.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 22a7efbed6..dac96317af 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -97,12 +97,6 @@ void macio_init(PCIDevice *dev, /* Grackle PCI */ #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" -/* UniNorth PCI */ -UNINHostState *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem); -UNINHostState *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem); - /* Mac NVRAM */ #define TYPE_MACIO_NVRAM "macio-nvram" #define MACIO_NVRAM(obj) \ From 4558fadaf550c0bf3c2284f353439a1cdbc407f1 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Jun 2018 17:59:54 +0100 Subject: [PATCH 1185/2380] ppc: remove obsolete macio_init() definition from mac.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commits b6712ea391 removed the macio_init() function but missed the header prototype in mac.h. Remove it since it is no longer needed. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/ppc/mac.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index dac96317af..89fa8bbed7 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -91,9 +91,6 @@ typedef struct MACIOIDEState { void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); void macio_ide_register_dma(MACIOIDEState *ide); -void macio_init(PCIDevice *dev, - MemoryRegion *pic_mem); - /* Grackle PCI */ #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" From 5b64db9754bde0b8e0329ce77d3647a0a2f6da56 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Jun 2018 17:59:55 +0100 Subject: [PATCH 1186/2380] ppc: add missing FW_CFG_PPC_NVRAM_FLAT definition This is used in OpenBIOS to define the memory layout of the NVRAM device. Whilst currently left at its default value, add the missing definition to ensure it is reserved. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- include/hw/ppc/ppc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index ff0ac306be..b18ef3eefb 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -100,6 +100,7 @@ enum { #define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07) #define FW_CFG_PPC_NVRAM_ADDR (FW_CFG_ARCH_LOCAL + 0x08) #define FW_CFG_PPC_BUSFREQ (FW_CFG_ARCH_LOCAL + 0x09) +#define FW_CFG_PPC_NVRAM_FLAT (FW_CFG_ARCH_LOCAL + 0x0a) #define PPC_SERIAL_MM_BAUDBASE 399193 From 72ee08cf4f2d4443d198b4e9f7a6fa2beb06dd6b Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Jun 2018 18:17:48 +0100 Subject: [PATCH 1187/2380] mos6522: fix vmstate_mos6522_timer version in vmstate_mos6522 This was accidentally introduced when extracting the 6522 VIA functionality from the CUDA device, and prevents loadvm from completing successfully. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/mos6522.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 49166cb8ad..bcb306d357 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -405,7 +405,7 @@ static const VMStateDescription vmstate_mos6522 = { VMSTATE_UINT8(ifr, MOS6522State), VMSTATE_UINT8(ier, MOS6522State), VMSTATE_UINT8(anh, MOS6522State), - VMSTATE_STRUCT_ARRAY(timers, MOS6522State, 2, 1, + VMSTATE_STRUCT_ARRAY(timers, MOS6522State, 2, 0, vmstate_mos6522_timer, MOS6522Timer), VMSTATE_END_OF_LIST() } From 2e3e5c7e92452900e2bb5143c5fb6d47c0897a34 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Jun 2018 18:17:49 +0100 Subject: [PATCH 1188/2380] cuda: embed mos6522_cuda device directly rather than using QOM object link Examining the migration stream it can be seen that the mos6522 device state is being stored separately rather than as part of the CUDA device which is incorrect (and likely to cause issues if another mos6522 device is added to the machine). Resolve this by embedding the mos6522_cuda device directly within the CUDA device rather than using a QOM object link to reference the device separately. Note that we also bump the version in vmstate_cuda to reflect this change: this isn't particularly important for the moment as the Mac machine migration isn't 100% reliable due to issues migrating the timebase under TCG. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/cuda.c | 44 +++++++++++++++--------------------- hw/misc/mos6522.c | 2 +- include/hw/misc/macio/cuda.h | 27 ++++++++++------------ include/hw/misc/mos6522.h | 2 ++ 4 files changed, 33 insertions(+), 42 deletions(-) diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index bd9b862034..8aba2e63ec 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -65,7 +65,7 @@ static void cuda_receive_packet_from_host(CUDAState *s, static uint64_t cuda_get_counter_value(MOS6522State *s, MOS6522Timer *ti) { MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); - CUDAState *cs = mcs->cuda; + CUDAState *cs = container_of(mcs, CUDAState, mos6522_cuda); /* Reverse of the tb calculation algorithm that Mac OS X uses on bootup */ uint64_t tb_diff = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), @@ -78,7 +78,7 @@ static uint64_t cuda_get_counter_value(MOS6522State *s, MOS6522Timer *ti) static uint64_t cuda_get_load_time(MOS6522State *s, MOS6522Timer *ti) { MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); - CUDAState *cs = mcs->cuda; + CUDAState *cs = container_of(mcs, CUDAState, mos6522_cuda); uint64_t load_time = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), cs->tb_frequency, NANOSECONDS_PER_SECOND); @@ -88,7 +88,7 @@ static uint64_t cuda_get_load_time(MOS6522State *s, MOS6522Timer *ti) static void cuda_set_sr_int(void *opaque) { CUDAState *s = opaque; - MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); @@ -97,7 +97,7 @@ static void cuda_set_sr_int(void *opaque) static void cuda_delay_set_sr_int(CUDAState *s) { - MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); int64_t expire; @@ -117,7 +117,7 @@ static void cuda_delay_set_sr_int(CUDAState *s) /* NOTE: TIP and TREQ are negated */ static void cuda_update(CUDAState *s) { - MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); int packet_received, len; @@ -462,7 +462,7 @@ static void cuda_receive_packet_from_host(CUDAState *s, static uint64_t mos6522_cuda_read(void *opaque, hwaddr addr, unsigned size) { CUDAState *s = opaque; - MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); addr = (addr >> 9) & 0xf; @@ -473,7 +473,7 @@ static void mos6522_cuda_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { CUDAState *s = opaque; - MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); addr = (addr >> 9) & 0xf; @@ -492,9 +492,11 @@ static const MemoryRegionOps mos6522_cuda_ops = { static const VMStateDescription vmstate_cuda = { .name = "cuda", - .version_id = 4, - .minimum_version_id = 4, + .version_id = 5, + .minimum_version_id = 5, .fields = (VMStateField[]) { + VMSTATE_STRUCT(mos6522_cuda.parent_obj, CUDAState, 0, vmstate_mos6522, + MOS6522State), VMSTATE_UINT8(last_b, CUDAState), VMSTATE_UINT8(last_acr, CUDAState), VMSTATE_INT32(data_in_size, CUDAState), @@ -530,12 +532,8 @@ static void cuda_realize(DeviceState *dev, Error **errp) DeviceState *d; struct tm tm; - d = qdev_create(NULL, TYPE_MOS6522_CUDA); - object_property_set_link(OBJECT(d), OBJECT(s), "cuda", errp); - qdev_init_nofail(d); - s->mos6522_cuda = MOS6522_CUDA(d); - /* Pass IRQ from 6522 */ + d = DEVICE(&s->mos6522_cuda); ms = MOS6522(d); sbd = SYS_BUS_DEVICE(s); sysbus_pass_irq(sbd, SYS_BUS_DEVICE(ms)); @@ -556,6 +554,10 @@ static void cuda_init(Object *obj) CUDAState *s = CUDA(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + object_initialize(&s->mos6522_cuda, sizeof(s->mos6522_cuda), + TYPE_MOS6522_CUDA); + qdev_set_parent_bus(DEVICE(&s->mos6522_cuda), sysbus_get_default()); + memory_region_init_io(&s->mem, obj, &mos6522_cuda_ops, s, "cuda", 0x2000); sysbus_init_mmio(sbd, &s->mem); @@ -590,8 +592,9 @@ static const TypeInfo cuda_type_info = { static void mos6522_cuda_portB_write(MOS6522State *s) { MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); + CUDAState *cs = container_of(mcs, CUDAState, mos6522_cuda); - cuda_update(mcs->cuda); + cuda_update(cs); } static void mos6522_cuda_realize(DeviceState *dev, Error **errp) @@ -605,16 +608,6 @@ static void mos6522_cuda_realize(DeviceState *dev, Error **errp) ms->timers[1].frequency = (SCALE_US * 6000) / 4700; } -static void mos6522_cuda_init(Object *obj) -{ - MOS6522CUDAState *s = MOS6522_CUDA(obj); - - object_property_add_link(obj, "cuda", TYPE_CUDA, - (Object **) &s->cuda, - qdev_prop_allow_set_link_before_realize, - 0, NULL); -} - static void mos6522_cuda_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -632,7 +625,6 @@ static const TypeInfo mos6522_cuda_type_info = { .name = TYPE_MOS6522_CUDA, .parent = TYPE_MOS6522, .instance_size = sizeof(MOS6522CUDAState), - .instance_init = mos6522_cuda_init, .class_init = mos6522_cuda_class_init, }; diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index bcb306d357..524a250329 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -390,7 +390,7 @@ static const VMStateDescription vmstate_mos6522_timer = { } }; -static const VMStateDescription vmstate_mos6522 = { +const VMStateDescription vmstate_mos6522 = { .name = "mos6522", .version_id = 0, .minimum_version_id = 0, diff --git a/include/hw/misc/macio/cuda.h b/include/hw/misc/macio/cuda.h index 494b709579..7dad469142 100644 --- a/include/hw/misc/macio/cuda.h +++ b/include/hw/misc/macio/cuda.h @@ -54,12 +54,21 @@ #define CUDA_TIMER_TICKLE 0x24 #define CUDA_COMBINED_FORMAT_IIC 0x25 + +/* MOS6522 CUDA */ +typedef struct MOS6522CUDAState { + /*< private >*/ + MOS6522State parent_obj; +} MOS6522CUDAState; + +#define TYPE_MOS6522_CUDA "mos6522-cuda" +#define MOS6522_CUDA(obj) OBJECT_CHECK(MOS6522CUDAState, (obj), \ + TYPE_MOS6522_CUDA) + /* Cuda */ #define TYPE_CUDA "cuda" #define CUDA(obj) OBJECT_CHECK(CUDAState, (obj), TYPE_CUDA) -typedef struct MOS6522CUDAState MOS6522CUDAState; - typedef struct CUDAState { /*< private >*/ SysBusDevice parent_obj; @@ -67,7 +76,7 @@ typedef struct CUDAState { MemoryRegion mem; ADBBusState adb_bus; - MOS6522CUDAState *mos6522_cuda; + MOS6522CUDAState mos6522_cuda; uint32_t tick_offset; uint64_t tb_frequency; @@ -92,16 +101,4 @@ typedef struct CUDAState { QEMUTimer *adb_poll_timer; } CUDAState; -/* MOS6522 CUDA */ -struct MOS6522CUDAState { - /*< private >*/ - MOS6522State parent_obj; - - CUDAState *cuda; -}; - -#define TYPE_MOS6522_CUDA "mos6522-cuda" -#define MOS6522_CUDA(obj) OBJECT_CHECK(MOS6522CUDAState, (obj), \ - TYPE_MOS6522_CUDA) - #endif /* CUDA_H */ diff --git a/include/hw/misc/mos6522.h b/include/hw/misc/mos6522.h index a53c161b00..cb0fd7db78 100644 --- a/include/hw/misc/mos6522.h +++ b/include/hw/misc/mos6522.h @@ -146,6 +146,8 @@ typedef struct MOS6522DeviceClass { #define MOS6522_DEVICE_GET_CLASS(obj) \ OBJECT_GET_CLASS(MOS6522DeviceClass, (obj), TYPE_MOS6522) +extern const VMStateDescription vmstate_mos6522; + uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size); void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); From d638fd5c9681dbb8915147365520888c64f39dac Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Jun 2018 18:17:50 +0100 Subject: [PATCH 1189/2380] mos6522: move timer frequency initialisation to mos6522_reset The 6522 VIA timer frequency cannot be set by altering registers within the device itself and hence it is a fixed property of the machine. Move the initialisation of the timer frequency to the mos6522 reset function and ensure that any subclasses always call the parent reset function so that it isn't required to store the timer frequency within vmstate_mos6522_timer itself. By moving the frequency initialisation to the device reset function then we find that the realize function for both mos6522 and mos6522_cuda becomes obsolete and can simply be removed. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/cuda.c | 6 +++--- hw/misc/mos6522.c | 13 +++---------- include/hw/misc/mos6522.h | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 8aba2e63ec..9651ed9744 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -597,12 +597,12 @@ static void mos6522_cuda_portB_write(MOS6522State *s) cuda_update(cs); } -static void mos6522_cuda_realize(DeviceState *dev, Error **errp) +static void mos6522_cuda_reset(DeviceState *dev) { MOS6522State *ms = MOS6522(dev); MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); - mdc->parent_realize(dev, errp); + mdc->parent_reset(dev); ms->timers[0].frequency = CUDA_TIMER_FREQ; ms->timers[1].frequency = (SCALE_US * 6000) / 4700; @@ -613,7 +613,7 @@ static void mos6522_cuda_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); - dc->realize = mos6522_cuda_realize; + dc->reset = mos6522_cuda_reset; mdc->portB_write = mos6522_cuda_portB_write; mdc->get_timer1_counter_value = cuda_get_counter_value; mdc->get_timer2_counter_value = cuda_get_counter_value; diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 524a250329..2f58b9707f 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -427,18 +427,12 @@ static void mos6522_reset(DeviceState *dev) /* s->ier = T1_INT | SR_INT; */ s->anh = 0; + s->timers[0].frequency = s->frequency; s->timers[0].latch = 0xffff; set_counter(s, &s->timers[0], 0xffff); - s->timers[1].latch = 0xffff; -} - -static void mos6522_realize(DeviceState *dev, Error **errp) -{ - MOS6522State *s = MOS6522(dev); - - s->timers[0].frequency = s->frequency; s->timers[1].frequency = s->frequency; + s->timers[1].latch = 0xffff; } static void mos6522_init(Object *obj) @@ -469,11 +463,10 @@ static void mos6522_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); - dc->realize = mos6522_realize; dc->reset = mos6522_reset; dc->vmsd = &vmstate_mos6522; dc->props = mos6522_properties; - mdc->parent_realize = dc->realize; + mdc->parent_reset = dc->reset; mdc->set_sr_int = mos6522_set_sr_int; mdc->portB_write = mos6522_portB_write; mdc->portA_write = mos6522_portA_write; diff --git a/include/hw/misc/mos6522.h b/include/hw/misc/mos6522.h index cb0fd7db78..f52b41920b 100644 --- a/include/hw/misc/mos6522.h +++ b/include/hw/misc/mos6522.h @@ -130,7 +130,7 @@ typedef struct MOS6522State { typedef struct MOS6522DeviceClass { DeviceClass parent_class; - DeviceRealize parent_realize; + DeviceReset parent_reset; void (*set_sr_int)(MOS6522State *dev); void (*portB_write)(MOS6522State *dev); void (*portA_write)(MOS6522State *dev); From 3431bdf5a3c3bd732f78b0433471c2b1cb373564 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 7 Jun 2018 18:17:51 +0100 Subject: [PATCH 1190/2380] mos6522: convert VMSTATE_TIMER_PTR_TEST to VMSTATE_TIMER_PTR The timers are configured in the mos6522 init function and therefore will always exist, so the function can never return false. Peter also pointed out that this is the only remaining user of VMSTATE_TIMER_PTR_TEST in the codebase, so we might as well just convert it over to VMSTATE_TIMER_PTR and remove mos6522_timer_exist() as it is no longer required. Signed-off-by: Mark Cave-Ayland Reviewed-by: Peter Maydell Signed-off-by: David Gibson --- hw/misc/mos6522.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 2f58b9707f..44eb306cf1 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -369,13 +369,6 @@ static const MemoryRegionOps mos6522_ops = { }, }; -static bool mos6522_timer_exist(void *opaque, int version_id) -{ - MOS6522Timer *s = opaque; - - return s->timer != NULL; -} - static const VMStateDescription vmstate_mos6522_timer = { .name = "mos6522_timer", .version_id = 0, @@ -385,7 +378,7 @@ static const VMStateDescription vmstate_mos6522_timer = { VMSTATE_UINT16(counter_value, MOS6522Timer), VMSTATE_INT64(load_time, MOS6522Timer), VMSTATE_INT64(next_irq_time, MOS6522Timer), - VMSTATE_TIMER_PTR_TEST(timer, MOS6522Timer, mos6522_timer_exist), + VMSTATE_TIMER_PTR(timer, MOS6522Timer), VMSTATE_END_OF_LIST() } }; From c8fd8373e42821984400382cd91b8bf4e7c14e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 6 Jun 2018 09:33:53 +0200 Subject: [PATCH 1191/2380] target/ppc: extend eieio for POWER9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POWER9 introduced a new variant of the eieio instruction using bit 6 as a hint to tell the CPU it is a store-forwarding barrier. The usage of this eieio extension was recently added in Linux 4.17 which activated the "support for a store forwarding barrier at kernel entry/exit". Unfortunately, it is not possible to insert this new eieio instruction without considerable change in ppc_tr_translate_insn(). So instead we loosen the QEMU eieio instruction mask and modify the gen_eieio() helper to test for bit6. On non-POWER9 CPUs, the bit6 is just ignored but a warning is emitted as this is not an instruction software should be using. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- target/ppc/translate.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 8ba8f67dc5..5fe1ba6555 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2967,7 +2967,28 @@ static void gen_stswx(DisasContext *ctx) /* eieio */ static void gen_eieio(DisasContext *ctx) { - tcg_gen_mb(TCG_MO_LD_ST | TCG_BAR_SC); + TCGBar bar = TCG_MO_LD_ST; + + /* + * POWER9 has a eieio instruction variant using bit 6 as a hint to + * tell the CPU it is a store-forwarding barrier. + */ + if (ctx->opcode & 0x2000000) { + /* + * ISA says that "Reserved fields in instructions are ignored + * by the processor". So ignore the bit 6 on non-POWER9 CPU but + * as this is not an instruction software should be using, + * complain to the user. + */ + if (!(ctx->insns_flags2 & PPC2_ISA300)) { + qemu_log_mask(LOG_GUEST_ERROR, "invalid eieio using bit 6 at @" + TARGET_FMT_lx "\n", ctx->base.pc_next - 4); + } else { + bar = TCG_MO_ST_LD; + } + } + + tcg_gen_mb(bar | TCG_BAR_SC); } #if !defined(CONFIG_USER_ONLY) @@ -6483,7 +6504,7 @@ GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_STRING), GEN_HANDLER(lswx, 0x1F, 0x15, 0x10, 0x00000001, PPC_STRING), GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_STRING), GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING), -GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x03FFF801, PPC_MEM_EIEIO), +GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x01FFF801, PPC_MEM_EIEIO), GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM), GEN_HANDLER_E(lbarx, 0x1F, 0x14, 0x01, 0, PPC_NONE, PPC2_ATOMIC_ISA206), GEN_HANDLER_E(lharx, 0x1F, 0x14, 0x03, 0, PPC_NONE, PPC2_ATOMIC_ISA206), From 42a907e83921ff2c4f5e6ca9fa6aa6791b43a73f Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 6 Jun 2018 15:31:48 +0200 Subject: [PATCH 1192/2380] ppc4xx_i2c: Clean up and improve error logging Make it more readable by converting register indexes to decimal (avoids lot of superfluous 0x0) and distinguish errors caused by accessing non-existent vs. unimplemented registers. No functional change. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/i2c/ppc4xx_i2c.c | 94 ++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index ab64d196be..d1936dbdca 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -31,7 +31,7 @@ #include "hw/hw.h" #include "hw/i2c/ppc4xx_i2c.h" -#define PPC4xx_I2C_MEM_SIZE 0x12 +#define PPC4xx_I2C_MEM_SIZE 18 #define IIC_CNTL_PT (1 << 0) #define IIC_CNTL_READ (1 << 1) @@ -70,7 +70,7 @@ static void ppc4xx_i2c_reset(DeviceState *s) i2c->intrmsk = 0; i2c->xfrcnt = 0; i2c->xtcntlss = 0; - i2c->directcntl = 0x0f; + i2c->directcntl = 0xf; i2c->intr = 0; } @@ -85,7 +85,7 @@ static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size) uint64_t ret; switch (addr) { - case 0x00: + case 0: ret = i2c->mdata; if (ppc4xx_i2c_is_master(i2c)) { ret = 0xff; @@ -139,58 +139,62 @@ static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size) TYPE_PPC4xx_I2C, __func__); } break; - case 0x02: + case 2: ret = i2c->sdata; break; - case 0x04: + case 4: ret = i2c->lmadr; break; - case 0x05: + case 5: ret = i2c->hmadr; break; - case 0x06: + case 6: ret = i2c->cntl; break; - case 0x07: + case 7: ret = i2c->mdcntl; break; - case 0x08: + case 8: ret = i2c->sts; break; - case 0x09: + case 9: ret = i2c->extsts; break; - case 0x0A: + case 10: ret = i2c->lsadr; break; - case 0x0B: + case 11: ret = i2c->hsadr; break; - case 0x0C: + case 12: ret = i2c->clkdiv; break; - case 0x0D: + case 13: ret = i2c->intrmsk; break; - case 0x0E: + case 14: ret = i2c->xfrcnt; break; - case 0x0F: + case 15: ret = i2c->xtcntlss; break; - case 0x10: + case 16: ret = i2c->directcntl; break; - case 0x11: + case 17: ret = i2c->intr; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" - HWADDR_PRIx "\n", TYPE_PPC4xx_I2C, __func__, addr); + if (addr < PPC4xx_I2C_MEM_SIZE) { + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address 0x%" + HWADDR_PRIx "\n", __func__, addr); + } ret = 0; break; } - return ret; } @@ -200,7 +204,7 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, PPC4xxI2CState *i2c = opaque; switch (addr) { - case 0x00: + case 0: i2c->mdata = value; if (!i2c_bus_busy(i2c->bus)) { /* assume we start a write transfer */ @@ -225,19 +229,19 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, } } break; - case 0x02: + case 2: i2c->sdata = value; break; - case 0x04: + case 4: i2c->lmadr = value; if (i2c_bus_busy(i2c->bus)) { i2c_end_transfer(i2c->bus); } break; - case 0x05: + case 5: i2c->hmadr = value; break; - case 0x06: + case 6: i2c->cntl = value; if (i2c->cntl & IIC_CNTL_PT) { if (i2c->cntl & IIC_CNTL_READ) { @@ -263,32 +267,31 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, } } break; - case 0x07: - i2c->mdcntl = value & 0xDF; + case 7: + i2c->mdcntl = value & 0xdf; break; - case 0x08: - i2c->sts &= ~(value & 0x0A); + case 8: + i2c->sts &= ~(value & 0xa); break; - case 0x09: - i2c->extsts &= ~(value & 0x8F); + case 9: + i2c->extsts &= ~(value & 0x8f); break; - case 0x0A: + case 10: i2c->lsadr = value; - /*i2c_set_slave_address(i2c->bus, i2c->lsadr);*/ break; - case 0x0B: + case 11: i2c->hsadr = value; break; - case 0x0C: + case 12: i2c->clkdiv = value; break; - case 0x0D: + case 13: i2c->intrmsk = value; break; - case 0x0E: + case 14: i2c->xfrcnt = value & 0x77; break; - case 0x0F: + case 15: if (value & IIC_XTCNTLSS_SRST) { /* Is it actually a full reset? U-Boot sets some regs before */ ppc4xx_i2c_reset(DEVICE(i2c)); @@ -296,15 +299,20 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, } i2c->xtcntlss = value; break; - case 0x10: + case 16: i2c->directcntl = value & 0x7; break; - case 0x11: + case 17: i2c->intr = value; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" - HWADDR_PRIx "\n", TYPE_PPC4xx_I2C, __func__, addr); + if (addr < PPC4xx_I2C_MEM_SIZE) { + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address 0x%" + HWADDR_PRIx "\n", __func__, addr); + } break; } } From bfda32a87bc77b9fac83f6c2789d65585115970d Mon Sep 17 00:00:00 2001 From: luporl Date: Fri, 8 Jun 2018 11:46:55 +0200 Subject: [PATCH 1193/2380] target/ppc: Allow PIR read in privileged mode According to PowerISA, the PIR register should be readable in privileged mode also, not only in hypervisor privileged mode. PowerISA 3.0 - 4.3.3 Processor Identification Register "Read access to the PIR is privileged; write access is not provided." Figure 18 in section 4.4.4 explicitly confirms that mfspr PIR is privileged and doesn't require hypervisor state. Cc: David Gibson Cc: Alexander Graf Cc: qemu-ppc@nongnu.org Signed-off-by: Leandro Lupori Reviewed-by: Jose Ricardo Ziviani Reviewed-by: Greg Kurz Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- target/ppc/translate_init.inc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index 1a89017dde..bb9296f5a3 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -7819,7 +7819,7 @@ static void gen_spr_book3s_ids(CPUPPCState *env) /* Processor identification */ spr_register_hv(env, SPR_PIR, "PIR", SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, &spr_read_generic, NULL, 0x00000000); spr_register_hv(env, SPR_HID0, "HID0", From fcc8ef17e264ef6f4afb6fa4af71b88993f49363 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 8 Jun 2018 14:48:11 +0200 Subject: [PATCH 1194/2380] spapr: no need to verify the node The node property can always be queried and the value has already been verified in pc_dimm_realize(). Acked-by: David Gibson Reviewed-by: Greg Kurz Signed-off-by: David Hildenbrand Signed-off-by: David Gibson --- hw/ppc/spapr.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 2375cbee12..f16a0b2870 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3578,14 +3578,8 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, error_setg(errp, "Memory hotplug not supported for this machine"); return; } - node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, errp); - if (*errp) { - return; - } - if (node < 0 || node >= MAX_NODES) { - error_setg(errp, "Invaild node %d", node); - return; - } + node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, + &error_abort); spapr_memory_plug(hotplug_dev, dev, node, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { From 81985f3be9679b8770f46f78d475ff53a2e8cdbb Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 8 Jun 2018 14:48:12 +0200 Subject: [PATCH 1195/2380] spapr: move lookup of the node into spapr_memory_plug() Let's clean the hotplug handler up by moving lookup of the node into the function where it is actually being used. Signed-off-by: David Hildenbrand Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index f16a0b2870..1f577b274b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3136,7 +3136,7 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, } static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, - uint32_t node, Error **errp) + Error **errp) { Error *local_err = NULL; sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev); @@ -3144,6 +3144,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr; uint64_t align, size, addr; + uint32_t node; mr = ddc->get_memory_region(dimm, &local_err); if (local_err) { @@ -3163,6 +3164,8 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out_unplug; } + node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, + &error_abort); spapr_add_lmbs(dev, addr, size, node, spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), &local_err); @@ -3572,16 +3575,11 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(ms); if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - int node; - if (!smc->dr_lmb_enabled) { error_setg(errp, "Memory hotplug not supported for this machine"); return; } - node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, - &error_abort); - - spapr_memory_plug(hotplug_dev, dev, node, errp); + spapr_memory_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_plug(hotplug_dev, dev, errp); } From 4e8a01bdb20557ee4f5bf6f86890d2b95fa2a7fb Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 8 Jun 2018 14:48:13 +0200 Subject: [PATCH 1196/2380] spapr: move memory hotplug support check into spapr_memory_pre_plug() Let's finish cleaning up the hotplug handler. This check can be performed in the pre_plug code as the very first thing. Signed-off-by: David Hildenbrand Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 1f577b274b..9b8b4068b1 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3184,12 +3184,18 @@ out: static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + const sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr; uint64_t size; char *mem_dev; + if (!smc->dr_lmb_enabled) { + error_setg(errp, "Memory hotplug not supported for this machine"); + return; + } + mr = ddc->get_memory_region(dimm, errp); if (!mr) { return; @@ -3571,14 +3577,7 @@ out: static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - MachineState *ms = MACHINE(hotplug_dev); - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(ms); - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - if (!smc->dr_lmb_enabled) { - error_setg(errp, "Memory hotplug not supported for this machine"); - return; - } spapr_memory_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_plug(hotplug_dev, dev, errp); From 88432f44aae752028103408f2f6df8c681143907 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 8 Jun 2018 14:48:14 +0200 Subject: [PATCH 1197/2380] spapr: introduce machine unplug handler We'll be handling unplug of e.g. CPUs and PCDIMMs via the general hotplug handler soon, so let's add that handler function. Acked-by: David Gibson Reviewed-by: Greg Kurz Signed-off-by: David Hildenbrand Signed-off-by: David Gibson --- hw/ppc/spapr.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 9b8b4068b1..c45f8bc75b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3584,6 +3584,11 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, } } +static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ +} + static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -3978,6 +3983,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->get_default_cpu_node_id = spapr_get_default_cpu_node_id; mc->possible_cpu_arch_ids = spapr_possible_cpu_arch_ids; hc->unplug_request = spapr_machine_device_unplug_request; + hc->unplug = spapr_machine_device_unplug; smc->dr_lmb_enabled = true; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0"); From 3ec71474cad1066865b3c2059add208794a418da Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 8 Jun 2018 14:48:15 +0200 Subject: [PATCH 1198/2380] spapr: handle pc-dimm unplug via hotplug handler chain Factor out memory unplug into separate function from spapr_lmb_release(). Then use generic hotplug_handler_unplug() to trigger memory unplug, which will call spapr_machine_device_unplug() -> spapr_memory_unplug() in the end. This way unplug operation is not buried in lmb internals and located in the same place like in other targets, following similar logic/call chain across targets. Acked-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Igor Mammedov Signed-off-by: David Hildenbrand Signed-off-by: David Gibson --- hw/ppc/spapr.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index c45f8bc75b..404d887f4e 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3299,7 +3299,8 @@ static sPAPRDIMMState *spapr_recover_pending_dimm_state(sPAPRMachineState *ms, /* Callback to be called during DRC release. */ void spapr_lmb_release(DeviceState *dev) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_hotplug_handler(dev)); + HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_ctrl); sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); /* This information will get lost if a migration occurs @@ -3317,9 +3318,17 @@ void spapr_lmb_release(DeviceState *dev) /* * Now that all the LMBs have been removed by the guest, call the - * pc-dimm unplug handler to cleanup up the pc-dimm device. + * unplug handler chain. This can never fail. */ - pc_dimm_memory_unplug(dev, MACHINE(spapr)); + hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); +} + +static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); + sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); + + pc_dimm_memory_unplug(dev, MACHINE(hotplug_dev)); object_unparent(OBJECT(dev)); spapr_pending_dimm_unplugs_remove(spapr, ds); } @@ -3587,6 +3596,9 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + spapr_memory_unplug(hotplug_dev, dev); + } } static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, From a4261be17213eea96673fac71bc89f5342422b76 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 8 Jun 2018 14:48:16 +0200 Subject: [PATCH 1199/2380] spapr: handle cpu core unplug via hotplug handler chain Factor out cpu core unplug into separate function from spapr_core_release(). Then use generic hotplug_handler_unplug() to trigger cpu core unplug, which would call spapr_machine_device_unplug() -> spapr_core_unplug() in the end. This way unplug operation is not buried in spapr internals and located in the same place like in other targets, following similar logic/call chain across targets. Acked-by: Igor Mammedov Acked-by: David Gibson Reviewed-by: Greg Kurz Signed-off-by: David Hildenbrand Signed-off-by: David Gibson --- hw/ppc/spapr.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 404d887f4e..f59999daac 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3416,7 +3416,15 @@ static void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, /* Callback to be called during DRC release. */ void spapr_core_release(DeviceState *dev) { - MachineState *ms = MACHINE(qdev_get_hotplug_handler(dev)); + HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + + /* Call the unplug handler chain. This can never fail. */ + hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); +} + +static void spapr_core_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) +{ + MachineState *ms = MACHINE(hotplug_dev); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(ms); CPUCore *cc = CPU_CORE(dev); CPUArchId *core_slot = spapr_find_cpu_slot(ms, cc->core_id, NULL); @@ -3598,6 +3606,8 @@ static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { spapr_memory_unplug(hotplug_dev, dev); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + spapr_core_unplug(hotplug_dev, dev); } } From d61c2857037e6211667a52563742af798d4c0332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 11 Jun 2018 19:12:10 +0200 Subject: [PATCH 1200/2380] ppc/pnv: fix LPC HC firmware address space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A specific MemoryRegion is required for the LPC HC Firmware address space. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/pnv_lpc.c | 4 +++- include/hw/ppc/pnv_lpc.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 2317d1e62c..402c4fefa8 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -79,6 +79,7 @@ enum { #define ISA_IO_SIZE 0x00010000 #define ISA_MEM_SIZE 0x10000000 +#define ISA_FW_SIZE 0x10000000 #define LPC_IO_OPB_ADDR 0xd0010000 #define LPC_IO_OPB_SIZE 0x00010000 #define LPC_MEM_OPB_ADDR 0xe0010000 @@ -429,6 +430,7 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) */ memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE); + memory_region_init(&lpc->isa_fw, OBJECT(dev), "isa-fw", ISA_FW_SIZE); /* Create windows from the OPB space to the ISA space */ memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io", @@ -440,7 +442,7 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR, &lpc->opb_isa_mem); memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw", - &lpc->isa_mem, 0, LPC_FW_OPB_SIZE); + &lpc->isa_fw, 0, LPC_FW_OPB_SIZE); memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR, &lpc->opb_isa_fw); diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index 023b4f0fec..53fdd5bb64 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -38,6 +38,7 @@ typedef struct PnvLpcController { /* ISA IO and Memory space */ MemoryRegion isa_io; MemoryRegion isa_mem; + MemoryRegion isa_fw; /* Windows from OPB to ISA (aliases) */ MemoryRegion opb_isa_io; From bf358b541b803d8f415807a632c0d0cde1504bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 11 Jun 2018 18:23:10 +0200 Subject: [PATCH 1201/2380] xics_kvm: use KVM helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The KVM helpers hide the low level interface used to communicate to the XICS KVM device and provide a good cleanup to the XICS KVM models. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics_kvm.c | 52 +++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 89fb20e2c5..8bdf6afe82 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -56,10 +56,6 @@ static QLIST_HEAD(, KVMEnabledICP) static void icp_get_kvm_state(ICPState *icp) { uint64_t state; - struct kvm_one_reg reg = { - .id = KVM_REG_PPC_ICP_STATE, - .addr = (uintptr_t)&state, - }; int ret; /* ICP for this CPU thread is not in use, exiting */ @@ -67,7 +63,7 @@ static void icp_get_kvm_state(ICPState *icp) return; } - ret = kvm_vcpu_ioctl(icp->cs, KVM_GET_ONE_REG, ®); + ret = kvm_get_one_reg(icp->cs, KVM_REG_PPC_ICP_STATE, &state); if (ret != 0) { error_report("Unable to retrieve KVM interrupt controller state" " for CPU %ld: %s", kvm_arch_vcpu_id(icp->cs), strerror(errno)); @@ -96,10 +92,6 @@ static void icp_synchronize_state(ICPState *icp) static int icp_set_kvm_state(ICPState *icp, int version_id) { uint64_t state; - struct kvm_one_reg reg = { - .id = KVM_REG_PPC_ICP_STATE, - .addr = (uintptr_t)&state, - }; int ret; /* ICP for this CPU thread is not in use, exiting */ @@ -111,7 +103,7 @@ static int icp_set_kvm_state(ICPState *icp, int version_id) | ((uint64_t)icp->mfrr << KVM_REG_PPC_ICP_MFRR_SHIFT) | ((uint64_t)icp->pending_priority << KVM_REG_PPC_ICP_PPRI_SHIFT); - ret = kvm_vcpu_ioctl(icp->cs, KVM_SET_ONE_REG, ®); + ret = kvm_set_one_reg(icp->cs, KVM_REG_PPC_ICP_STATE, &state); if (ret != 0) { error_report("Unable to restore KVM interrupt controller state (0x%" PRIx64 ") for CPU %ld: %s", state, kvm_arch_vcpu_id(icp->cs), @@ -185,21 +177,15 @@ static const TypeInfo icp_kvm_info = { static void ics_get_kvm_state(ICSState *ics) { uint64_t state; - struct kvm_device_attr attr = { - .flags = 0, - .group = KVM_DEV_XICS_GRP_SOURCES, - .addr = (uint64_t)(uintptr_t)&state, - }; int i; + Error *local_err = NULL; for (i = 0; i < ics->nr_irqs; i++) { ICSIRQState *irq = &ics->irqs[i]; - int ret; - attr.attr = i + ics->offset; - - ret = ioctl(kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr); - if (ret != 0) { + kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, + i + ics->offset, &state, false, &local_err); + if (local_err) { error_report("Unable to retrieve KVM interrupt controller state" " for IRQ %d: %s", i + ics->offset, strerror(errno)); exit(1); @@ -255,19 +241,13 @@ static void ics_synchronize_state(ICSState *ics) static int ics_set_kvm_state(ICSState *ics, int version_id) { uint64_t state; - struct kvm_device_attr attr = { - .flags = 0, - .group = KVM_DEV_XICS_GRP_SOURCES, - .addr = (uint64_t)(uintptr_t)&state, - }; int i; + Error *local_err = NULL; for (i = 0; i < ics->nr_irqs; i++) { ICSIRQState *irq = &ics->irqs[i]; int ret; - attr.attr = i + ics->offset; - state = irq->server; state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK) << KVM_XICS_PRIORITY_SHIFT; @@ -293,8 +273,9 @@ static int ics_set_kvm_state(ICSState *ics, int version_id) state |= KVM_XICS_QUEUED; } - ret = ioctl(kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr); - if (ret != 0) { + kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, + i + ics->offset, &state, true, &local_err); + if (local_err) { error_report("Unable to restore KVM interrupt controller state" " for IRQs %d: %s", i + ics->offset, strerror(errno)); return ret; @@ -391,10 +372,6 @@ static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr, int xics_kvm_init(sPAPRMachineState *spapr, Error **errp) { int rc; - struct kvm_create_device xics_create_device = { - .type = KVM_DEV_TYPE_XICS, - .flags = 0, - }; if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) { error_setg(errp, @@ -431,20 +408,19 @@ int xics_kvm_init(sPAPRMachineState *spapr, Error **errp) goto fail; } - /* Create the kernel ICP */ - rc = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &xics_create_device); + /* Create the KVM XICS device */ + rc = kvm_create_device(kvm_state, KVM_DEV_TYPE_XICS, false); if (rc < 0) { error_setg_errno(errp, -rc, "Error on KVM_CREATE_DEVICE for XICS"); goto fail; } - kernel_xics_fd = xics_create_device.fd; - + kernel_xics_fd = rc; kvm_kernel_irqchip = true; kvm_msi_via_irqfd_allowed = true; kvm_gsi_direct_mapping = true; - return rc; + return 0; fail: kvmppc_define_rtas_kernel_token(0, "ibm,set-xive"); From 30f79dc13f116a79ff45d37ad0f5c035012064a7 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 19 Apr 2018 16:07:40 +1000 Subject: [PATCH 1202/2380] spapr_pci: Remove unhelpful pagesize warning By default, the IOMMU model built into the spapr virtual PCI host bridge supports 4kiB and 64kiB IOMMU page sizes. However this can be overridden which may be desirable to allow larger IOMMU page sizes when running a guest with hugepage backing and passthrough devices. For that reason a warning was printed when the device wasn't configured to allow the pagesize with which guest RAM is backed. Experience has proven, however, that this message is more confusing than useful. Worse it sometimes makes little sense when the host-available page sizes don't match those available on the guest, which can happen with a POWER8 guest running on a POWER9 KVM host. Long term we do want better handling to allow large IOMMU page sizes to be used, but for now this parameter and warning don't really accomplish it. So, remove the message, pending a better solution. Signed-off-by: David Gibson --- hw/ppc/spapr_pci.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 39a14980d3..f936ce63ef 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1717,13 +1717,6 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) } /* DMA setup */ - if (((sphb->page_size_mask & qemu_getrampagesize()) == 0) - && kvm_enabled()) { - warn_report("System page size 0x%lx is not enabled in page_size_mask " - "(0x%"PRIx64"). Performance may be slow", - qemu_getrampagesize(), sphb->page_size_mask); - } - for (i = 0; i < windows_supported; ++i) { tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn[i]); if (!tcet) { From 649ae1040db656d5775f711e0dbea4c09c6c8fe9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 5 Jun 2018 15:29:15 +0200 Subject: [PATCH 1203/2380] usb: update docs xhci is rock solid meanwhile. So move it up in the docs and feature it as prefered usb host adapter, instead of the old shy version saying "you might want try ...". While being at it rework the text on ehci and companion controllers too. Signed-off-by: Gerd Hoffmann Reviewed-by: Markus Armbruster Message-id: 20180605132915.3640-1-kraxel@redhat.com --- docs/usb2.txt | 99 ++++++++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/docs/usb2.txt b/docs/usb2.txt index 09df45b5b1..e2fa2cfde0 100644 --- a/docs/usb2.txt +++ b/docs/usb2.txt @@ -1,16 +1,42 @@ -USB 2.0 Quick Start -=================== +USB Quick Start +=============== -The QEMU EHCI Adapter can be used with and without companion -controllers. See below for the companion controller mode. +XHCI controller support +----------------------- -When not running in companion controller mode there are two completely -separate USB busses: One USB 1.1 bus driven by the UHCI controller and -one USB 2.0 bus driven by the EHCI controller. Devices must be -attached to the correct controller manually. +QEMU has XHCI host adapter support. The XHCI hardware design is much +more virtualization-friendly when compared to EHCI and UHCI, thus XHCI +emulation uses less resources (especially cpu). So if your guest +supports XHCI (which should be the case for any operating system +released around 2010 or later) we recommend using it: -The '-usb' switch will make qemu create the UHCI controller as part of + qemu -device qemu-xhci + +XHCI supports USB 1.1, USB 2.0 and USB 3.0 devices, so this is the +only controller you need. With only a single USB controller (and +therefore only a single USB bus) present in the system there is no +need to use the bus= parameter when adding USB devices. + + +EHCI controller support +----------------------- + +The QEMU EHCI Adapter supports USB 2.0 devices. It can be used either +standalone or with companion controllers (UHCI, OHCI) for USB 1.1 +devices. The companion controller setup is more convenient to use +because it provides a single USB bus supporting both USB 2.0 and USB +1.1 devices. See next section for details. + +When running EHCI in standalone mode you can add UHCI or OHCI +controllers for USB 1.1 devices too. Each controller creates its own +bus though, so there are two completely separate USB buses: One USB +1.1 bus driven by the UHCI controller and one USB 2.0 bus driven by +the EHCI controller. Devices must be attached to the correct +controller manually. + +The easiest way to add a UHCI controller to a 'pc' machine is the +'-usb' switch. QEMU will create the UHCI controller as function of the PIIX3 chipset. The USB 1.1 bus will carry the name "usb-bus.0". You can use the standard -device switch to add a EHCI controller to @@ -19,9 +45,8 @@ the controller so the USB 2.0 bus gets a individual name, for example '-device usb-ehci,id=ehci". This will give you a USB 2.0 bus named "ehci.0". -I strongly recommend to also use -device to attach usb devices because -you can specify the bus they should be attached to this way. Here is -a complete example: +When adding USB devices using the -device switch you can specify the +bus they should be attached to. Here is a complete example: qemu -M pc ${otheroptions} \ -drive if=none,id=usbstick,file=/path/to/image \ @@ -30,58 +55,44 @@ a complete example: -device usb-tablet,bus=usb-bus.0 \ -device usb-storage,bus=ehci.0,drive=usbstick -This attaches a usb tablet to the UHCI adapter and a usb mass storage +This attaches a USB tablet to the UHCI adapter and a USB mass storage device to the EHCI adapter. Companion controller support ---------------------------- -Companion controller support has been added recently. The operational -model described above with two completely separate busses still works -fine. Additionally the UHCI and OHCI controllers got the ability to -attach to a usb bus created by EHCI as companion controllers. This is -done by specifying the masterbus and firstport properties. masterbus -specifies the bus name the controller should attach to. firstport -specifies the first port the controller should attach to, which is -needed as usually one ehci controller with six ports has three uhci -companion controllers with two ports each. +The UHCI and OHCI controllers can attach to a USB bus created by EHCI +as companion controllers. This is done by specifying the masterbus +and firstport properties. masterbus specifies the bus name the +controller should attach to. firstport specifies the first port the +controller should attach to, which is needed as usually one EHCI +controller with six ports has three UHCI companion controllers with +two ports each. -There is a config file in docs which will do all this for you, just -try ... +There is a config file in docs which will do all this for +you, just try ... qemu -readconfig docs/config/ich9-ehci-uhci.cfg -... then use "bus=ehci.0" to assign your usb devices to that bus. +... then use "bus=ehci.0" to assign your USB devices to that bus. - -xhci controller support ------------------------ - -There is also xhci host controller support available. It got a lot -less testing than ehci and there are a bunch of known limitations, so -ehci may work better for you. On the other hand the xhci hardware -design is much more virtualization-friendly, thus xhci emulation uses -less resources (especially cpu). If you want to give xhci a try -use this to add the host controller ... - - qemu -device nec-usb-xhci,id=xhci - -... then use "bus=xhci.0" when assigning usb devices. +Using the '-usb' switch for 'q35' machines will create a similar +USB controller configuration. More USB tips & tricks ====================== -Recently the usb pass through driver (also known as usb-host) and the -qemu usb subsystem gained a few capabilities which are available only +Recently the USB pass through driver (also known as usb-host) and the +QEMU USB subsystem gained a few capabilities which are available only via qdev properties, i,e. when using '-device'. physical port addressing ------------------------ -First you can (for all usb devices) specify the physical port where +First you can (for all USB devices) specify the physical port where the device will show up in the guest. This can be done using the "port" property. UHCI has two root ports (1,2). EHCI has four root ports (1-4), the emulated (1.1) USB hub has eight ports. @@ -94,7 +105,7 @@ Plugging a hub into UHCI port 2 works like this: -device usb-hub,bus=usb-bus.0,port=2 -Plugging a virtual usb stick into port 4 of the hub just plugged works +Plugging a virtual USB stick into port 4 of the hub just plugged works this way: -device usb-storage,bus=usb-bus.0,port=2.4,drive=... @@ -143,7 +154,7 @@ practice only a few combinations are useful: Note that USB 1.1 devices are handled by UHCI/OHCI and USB 2.0 by EHCI. That means a device plugged into the very same physical port -may show up on different busses depending on the speed. The port I'm +may show up on different buses depending on the speed. The port I'm using for testing is bus 1 + port 1 for 2.0 devices and bus 3 + port 1 for 1.1 devices. Passing through any device plugged into that port and also assign them to the correct bus can be done this way: From bf78fb1c1b61a819a47f7a1dbecf9934b9f32a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Jun 2018 12:14:19 -0300 Subject: [PATCH 1204/2380] usb: correctly handle Zero Length Packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit USB Specification Revision 2.0, §5.5.3: The Data stage of a control transfer from an endpoint to the host is complete when the endpoint does one of the following: • Has transferred exactly the amount of data specified during the Setup stage • Transfers a packet with a payload size less than wMaxPacketSize or transfers a zero-length packet" hw/usb/redirect.c:802:9: warning: Declared variable-length array (VLA) has zero size uint8_t buf[size]; ^~~~~~~~~~~ ~~~~ Reported-by: Clang Static Analyzer Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180604151421.23385-2-f4bug@amsat.org Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 65a9196c1a..58e8f7f5bd 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -795,7 +795,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_32bits_bulk_length)); - if (ep & USB_DIR_IN) { + if (ep & USB_DIR_IN || size == 0) { usbredirparser_send_bulk_packet(dev->parser, p->id, &bulk_packet, NULL, 0); } else { From 62713a2e50f653162387451034f1a2490e87be88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Jun 2018 12:14:20 -0300 Subject: [PATCH 1205/2380] usb/dev-mtp: Fix use of uninitialized values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: hw/usb/dev-mtp.c:971:5: warning: 4th function call argument is an uninitialized value trace_usb_mtp_op_get_partial_object(s->dev.addr, o->handle, o->path, c->argv[1], c->argv[2]); ^~~~~~~~~~ and: hw/usb/dev-mtp.c:981:12: warning: Assigned value is garbage or undefined offset = c->argv[1]; ^ ~~~~~~~~~~ Reported-by: Clang Static Analyzer Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180604151421.23385-3-f4bug@amsat.org Signed-off-by: Gerd Hoffmann --- hw/usb/dev-mtp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 560c61c7c1..b0ab6a7912 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -1017,12 +1017,16 @@ static MTPData *usb_mtp_get_object(MTPState *s, MTPControl *c, static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c, MTPObject *o) { - MTPData *d = usb_mtp_data_alloc(c); + MTPData *d; off_t offset; + if (c->argc <= 2) { + return NULL; + } trace_usb_mtp_op_get_partial_object(s->dev.addr, o->handle, o->path, c->argv[1], c->argv[2]); + d = usb_mtp_data_alloc(c); d->fd = open(o->path, O_RDONLY); if (d->fd == -1) { usb_mtp_data_free(d); From f3d58385a6d3d82f65db602c5506e2d3d8c82394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 31 May 2018 21:51:16 +0200 Subject: [PATCH 1206/2380] bus: do not unref the added child bus on realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the parent bus removes the child property, it takes care of removing the added reference, in object_finalize_child_property(). Signed-off-by: Marc-André Lureau Message-id: 20180531195119.22021-2-marcandre.lureau@redhat.com Signed-off-by: Gerd Hoffmann --- hw/core/bus.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/core/bus.c b/hw/core/bus.c index 4651f24486..ad0c9df335 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -102,7 +102,6 @@ static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); bus->parent->num_child_bus++; object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL); - object_unref(OBJECT(bus)); } else if (bus != sysbus_get_default()) { /* TODO: once all bus devices are qdevified, only reset handler for main_system_bus should be registered here. */ From 265b578c584b1a86c7028790deaa2f4392dd0a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 31 May 2018 21:51:17 +0200 Subject: [PATCH 1207/2380] object: fix OBJ_PROP_LINK_UNREF_ON_RELEASE ambivalence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A link property can be set during creation, with object_property_add_link() and later with object_property_set_link(). add_link() doesn't add a reference to the target object, while set_link() does. Furthemore, OBJ_PROP_LINK_UNREF_ON_RELEASE flags, set during add_link, says whether a reference must be released when the property is destroyed. This can lead to leaks if the property was later set_link(), as the added reference is never released. Instead, rename OBJ_PROP_LINK_UNREF_ON_RELEASE to OBJ_PROP_LINK_STRONG and use that has an indication on how the link handle reference management in set_link(). Signed-off-by: Marc-André Lureau Message-id: 20180531195119.22021-3-marcandre.lureau@redhat.com Signed-off-by: Gerd Hoffmann --- hw/core/qdev-properties.c | 2 +- hw/core/qdev.c | 2 +- hw/display/xlnx_dp.c | 2 +- hw/dma/xilinx_axidma.c | 4 ++-- hw/dma/xlnx-zdma.c | 2 +- hw/i386/pc.c | 2 +- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/ipmi/ipmi.c | 2 +- hw/net/xilinx_axienet.c | 4 ++-- hw/ssi/xilinx_spips.c | 2 +- include/qom/object.h | 12 +++++++++--- net/can/can_host.c | 2 +- net/colo-compare.c | 2 +- qom/object.c | 8 +++++--- target/arm/cpu.c | 4 ++-- ui/console.c | 2 +- 17 files changed, 32 insertions(+), 24 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 989778ab7f..35072dec1e 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -1308,7 +1308,7 @@ static void create_link_property(Object *obj, Property *prop, Error **errp) object_property_add_link(obj, prop->name, prop->link_type, child, qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, errp); } diff --git a/hw/core/qdev.c b/hw/core/qdev.c index ffec461791..cf0db4b6da 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -409,7 +409,7 @@ void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, object_property_add_link(OBJECT(dev), propname, TYPE_IRQ, (Object **)&pins[i], object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); g_free(propname); } diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index c32ab083f8..51301220e8 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1223,7 +1223,7 @@ static void xlnx_dp_init(Object *obj) object_property_add_link(obj, "dpdma", TYPE_XLNX_DPDMA, (Object **) &s->dpdma, xlnx_dp_set_dpdma, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); /* diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index 9b48103574..401a328e27 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -525,12 +525,12 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp) object_property_add_link(OBJECT(ds), "dma", TYPE_XILINX_AXI_DMA, (Object **)&ds->dma, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &local_err); object_property_add_link(OBJECT(cs), "dma", TYPE_XILINX_AXI_DMA, (Object **)&cs->dma, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &local_err); if (local_err) { goto xilinx_axidma_realize_fail; diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 8eea757aff..b6745f5bcf 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -787,7 +787,7 @@ static void zdma_init(Object *obj) object_property_add_link(obj, "dma", TYPE_MEMORY_REGION, (Object **)&s->dma_mr, qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f3befe6721..ea57a46f81 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -483,7 +483,7 @@ void pc_cmos_init(PCMachineState *pcms, TYPE_ISA_DEVICE, (Object **)&pcms->rtc, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + OBJ_PROP_LINK_STRONG, &error_abort); object_property_set_link(OBJECT(pcms), OBJECT(s), "rtc_state", &error_abort); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 3d81136065..d2f0d60361 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -289,7 +289,7 @@ static void pc_init1(MachineState *machine, TYPE_HOTPLUG_HANDLER, (Object **)&pcms->acpi_dev, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + OBJ_PROP_LINK_STRONG, &error_abort); object_property_set_link(OBJECT(machine), OBJECT(piix4_pm), PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b60cbb9266..5be6ef73bb 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -194,7 +194,7 @@ static void pc_q35_init(MachineState *machine) TYPE_HOTPLUG_HANDLER, (Object **)&pcms->acpi_dev, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + OBJ_PROP_LINK_STRONG, &error_abort); object_property_set_link(OBJECT(machine), OBJECT(lpc), PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index 9be281fd87..63c031703d 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -104,7 +104,7 @@ void ipmi_bmc_find_and_link(Object *obj, Object **bmc) { object_property_add_link(obj, "bmc", TYPE_IPMI_BMC, bmc, isa_ipmi_bmc_check, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); } diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index d4c2c89dc1..cc880a3d08 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -951,12 +951,12 @@ static void xilinx_enet_realize(DeviceState *dev, Error **errp) object_property_add_link(OBJECT(ds), "enet", "xlnx.axi-ethernet", (Object **) &ds->enet, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &local_err); object_property_add_link(OBJECT(cs), "enet", "xlnx.axi-ethernet", (Object **) &cs->enet, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &local_err); if (local_err) { goto xilinx_enet_realize_fail; diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 03f5faee4b..f599025956 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -1346,7 +1346,7 @@ static void xlnx_zynqmp_qspips_init(Object *obj) object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SLAVE, (Object **)&rq->dma, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, NULL); } diff --git a/include/qom/object.h b/include/qom/object.h index a0c78c76f7..f3d2308d56 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1103,6 +1103,11 @@ char *object_property_get_str(Object *obj, const char *name, * @errp: returns an error if this function fails * * Writes an object's canonical path to a property. + * + * If the link property was created with + * OBJ_PROP_LINK_STRONG bit, the old target object is + * unreferenced, and a reference is added to the new target object. + * */ void object_property_set_link(Object *obj, Object *value, const char *name, Error **errp); @@ -1394,7 +1399,7 @@ void object_property_add_child(Object *obj, const char *name, typedef enum { /* Unref the link pointer when the property is deleted */ - OBJ_PROP_LINK_UNREF_ON_RELEASE = 0x1, + OBJ_PROP_LINK_STRONG = 0x1, } ObjectPropertyLinkFlags; /** @@ -1432,8 +1437,9 @@ void object_property_allow_set_link(const Object *, const char *, * link property. The reference count for *@child is * managed by the property from after the function returns till the * property is deleted with object_property_del(). If the - * @flags OBJ_PROP_LINK_UNREF_ON_RELEASE bit is set, - * the reference count is decremented when the property is deleted. + * @flags OBJ_PROP_LINK_STRONG bit is set, + * the reference count is decremented when the property is deleted or + * modified. */ void object_property_add_link(Object *obj, const char *name, const char *type, Object **child, diff --git a/net/can/can_host.c b/net/can/can_host.c index c3d26521cd..c79347abab 100644 --- a/net/can/can_host.c +++ b/net/can/can_host.c @@ -77,7 +77,7 @@ static void can_host_instance_init(Object *obj) object_property_add_link(obj, "canbus", TYPE_CAN_BUS, (Object **)&ch->bus, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); } diff --git a/net/colo-compare.c b/net/colo-compare.c index c3a2be4c90..dd745a491b 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -980,7 +980,7 @@ static void colo_compare_init(Object *obj) object_property_add_link(obj, "iothread", TYPE_IOTHREAD, (Object **)&s->iothread, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); + OBJ_PROP_LINK_STRONG, NULL); s->vnet_hdr = false; object_property_add_bool(obj, "vnet_hdr_support", compare_get_vnet_hdr, diff --git a/qom/object.c b/qom/object.c index cb7a8cd589..e6462f289c 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1564,9 +1564,11 @@ static void object_set_link_property(Object *obj, Visitor *v, return; } - object_ref(new_target); *child = new_target; - object_unref(old_target); + if (prop->flags == OBJ_PROP_LINK_STRONG) { + object_ref(new_target); + object_unref(old_target); + } } static Object *object_resolve_link_property(Object *parent, void *opaque, const gchar *part) @@ -1581,7 +1583,7 @@ static void object_release_link_property(Object *obj, const char *name, { LinkProperty *prop = opaque; - if ((prop->flags & OBJ_PROP_LINK_UNREF_ON_RELEASE) && *prop->child) { + if ((prop->flags & OBJ_PROP_LINK_STRONG) && *prop->child) { object_unref(*prop->child); } g_free(prop); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 5d60893a07..ab047b9402 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -690,7 +690,7 @@ static void arm_cpu_post_init(Object *obj) TYPE_MEMORY_REGION, (Object **)&cpu->secure_memory, qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); #endif } @@ -718,7 +718,7 @@ static void arm_cpu_post_init(Object *obj) if (arm_feature(&cpu->env, ARM_FEATURE_M_SECURITY)) { object_property_add_link(obj, "idau", TYPE_IDAU_INTERFACE, &cpu->idau, qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); qdev_property_add_static(DEVICE(obj), &arm_cpu_initsvtor_property, &error_abort); diff --git a/ui/console.c b/ui/console.c index ef1247f872..bc58458ee8 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1287,7 +1287,7 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, object_property_add_link(obj, "device", TYPE_DEVICE, (Object **)&s->device, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); object_property_add_uint32_ptr(obj, "head", &s->head, &error_abort); From 410a096adf991ce437d4d7dabc59b6557e6d488d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 31 May 2018 21:51:18 +0200 Subject: [PATCH 1208/2380] usb-ccid: fix bus leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qbus_create_inplace() creates a new reference in realize(), it must be released in unrealize(). Signed-off-by: Marc-André Lureau Message-id: 20180531195119.22021-4-marcandre.lureau@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/dev-smartcard-reader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 2131e33d27..f7c91230d5 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1147,6 +1147,7 @@ static void ccid_unrealize(USBDevice *dev, Error **errp) USBCCIDState *s = USB_CCID_DEV(dev); ccid_bulk_in_clear(s); + object_unref(OBJECT(&s->bus)); } static void ccid_flush_pending_answers(USBCCIDState *s) From 1a3ff20e67330a15d62b00c2916e3541872103c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 31 May 2018 21:51:19 +0200 Subject: [PATCH 1209/2380] usb-hcd-xhci-test: add a test for ccid hotplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Message-id: 20180531195119.22021-5-marcandre.lureau@redhat.com Signed-off-by: Gerd Hoffmann --- tests/usb-hcd-xhci-test.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c index 9c14e3053a..5b1b681bf2 100644 --- a/tests/usb-hcd-xhci-test.c +++ b/tests/usb-hcd-xhci-test.c @@ -35,6 +35,15 @@ static void test_usb_uas_hotplug(void) qtest_qmp_device_del("uas"); } +static void test_usb_ccid_hotplug(void) +{ + qtest_qmp_device_add("usb-ccid", "ccid", NULL); + qtest_qmp_device_del("ccid"); + /* check the device can be added again */ + qtest_qmp_device_add("usb-ccid", "ccid", NULL); + qtest_qmp_device_del("ccid"); +} + int main(int argc, char **argv) { int ret; @@ -44,6 +53,7 @@ int main(int argc, char **argv) qtest_add_func("/xhci/pci/init", test_xhci_init); qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); + qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug); qtest_start("-device nec-usb-xhci,id=xhci" " -drive id=drive0,if=none,file=null-co://,format=raw"); From 3c969a6022438cf59de10d2dc3c58f4807788f98 Mon Sep 17 00:00:00 2001 From: Bandan Das Date: Fri, 18 May 2018 14:49:03 -0400 Subject: [PATCH 1210/2380] usb-mtp: Return error on suspicious TYPE_DATA packet from initiator CID 1390604 If the initiator sends a packet with TYPE_DATA set without initiating a CMD_GET_OBJECT_INFO first, then usb_mtp_get_data can trip on a null s->data_out. Signed-off-by: Bandan Das Message-Id: Signed-off-by: Gerd Hoffmann --- hw/usb/dev-mtp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index b0ab6a7912..1ded7ac9a3 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -1700,6 +1700,11 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, uint64_t dlen; uint32_t data_len = p->iov.size; + if (!d) { + usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, 0, + 0, 0, 0, 0); + return; + } if (d->first) { /* Total length of incoming data */ d->length = cpu_to_le32(container->length) - sizeof(mtp_container); From 717ea844a7640cf0c9d7713ce2f00ee091d4a01f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 4 May 2018 07:42:41 +0200 Subject: [PATCH 1211/2380] Revert "Makefile: add target to print generated files" This reverts commit 9578f8cc3e8bd71de8e3f543dc7b95644d64824e. The patch snuck in by accident without having been posted to qemu-devel. It's entirely redundant: existing target print-% already serves the purpose. Cc: Michael S. Tsirkin Signed-off-by: Markus Armbruster Message-Id: <20180504054241.6833-1-armbru@redhat.com> Reviewed-by: Eric Blake --- Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile b/Makefile index 6c6664d9a3..355b44ff37 100644 --- a/Makefile +++ b/Makefile @@ -1055,9 +1055,6 @@ endif include $(SRC_PATH)/tests/docker/Makefile.include include $(SRC_PATH)/tests/vm/Makefile.include -printgen: - @echo $(GENERATED_FILES) - .PHONY: help help: @echo 'Generic targets:' From 04a0afe52852e6b6d6f81230b6d7acd25dac88dd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 14 May 2018 16:12:18 +0200 Subject: [PATCH 1212/2380] coverity-model: replay data is considered trusted Replay data is not considered a possible attack vector; add a model that does not use getc so that "tainted data" warnings are suppressed. Signed-off-by: Paolo Bonzini Message-Id: <20180514141218.28438-1-pbonzini@redhat.com> Reviewed-by: Markus Armbruster [Whitespace tweaked] Signed-off-by: Markus Armbruster --- scripts/coverity-model.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/coverity-model.c b/scripts/coverity-model.c index c702804f41..48b112393b 100644 --- a/scripts/coverity-model.c +++ b/scripts/coverity-model.c @@ -103,6 +103,18 @@ static int get_keysym(const name2keysym_t *table, } } +/* Replay data is considered trusted. */ +uint8_t replay_get_byte(void) +{ + uint8_t byte = 0; + if (replay_file) { + uint8_t c; + byte = c; + } + return byte; +} + + /* * GLib memory allocation functions. * From 719a30776b94d3da8c613e5047414f483d40256d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 8 Jun 2018 19:02:31 +0200 Subject: [PATCH 1213/2380] Purge uses of banned g_assert_FOO() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We banned use of certain g_assert_FOO() functions outside tests, and made checkpatch.pl flag them (commit 6e9389563e5). We neglected to purge existing uses. Do that now. Signed-off-by: Markus Armbruster Message-Id: <20180608170231.27912-1-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: John Snow --- hw/ide/ahci.c | 2 +- hw/ppc/spapr_ovec.c | 12 ++++++------ hw/usb/dev-smartcard-reader.c | 2 +- qom/object.c | 10 +++++----- util/qht.c | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index f7852be842..2ec24cad9f 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -440,7 +440,7 @@ static uint64_t ahci_mem_read(void *opaque, hwaddr addr, unsigned size) if (ofst + size <= 4) { val = lo >> (ofst * 8); } else { - g_assert_cmpint(size, >, 1); + g_assert(size > 1); /* If the 64bit read is unaligned, we will produce undefined * results. AHCI does not support unaligned 64bit reads. */ diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 41df4c35ba..318bf33de4 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -113,7 +113,7 @@ void spapr_ovec_cleanup(sPAPROptionVector *ov) void spapr_ovec_set(sPAPROptionVector *ov, long bitnr) { g_assert(ov); - g_assert_cmpint(bitnr, <, OV_MAXBITS); + g_assert(bitnr < OV_MAXBITS); set_bit(bitnr, ov->bitmap); } @@ -121,7 +121,7 @@ void spapr_ovec_set(sPAPROptionVector *ov, long bitnr) void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr) { g_assert(ov); - g_assert_cmpint(bitnr, <, OV_MAXBITS); + g_assert(bitnr < OV_MAXBITS); clear_bit(bitnr, ov->bitmap); } @@ -129,7 +129,7 @@ void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr) bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr) { g_assert(ov); - g_assert_cmpint(bitnr, <, OV_MAXBITS); + g_assert(bitnr < OV_MAXBITS); return test_bit(bitnr, ov->bitmap) ? true : false; } @@ -186,7 +186,7 @@ sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) int i; g_assert(table_addr); - g_assert_cmpint(vector, >=, 1); /* vector numbering starts at 1 */ + g_assert(vector >= 1); /* vector numbering starts at 1 */ addr = vector_addr(table_addr, vector); if (!addr) { @@ -195,7 +195,7 @@ sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) } vector_len = ldub_phys(&address_space_memory, addr++) + 1; - g_assert_cmpint(vector_len, <=, OV_MAXBYTES); + g_assert(vector_len <= OV_MAXBYTES); ov = spapr_ovec_new(); for (i = 0; i < vector_len; i++) { @@ -225,7 +225,7 @@ int spapr_ovec_populate_dt(void *fdt, int fdt_offset, * encoding/sizing expected in ibm,client-architecture-support */ vec_len = (lastbit == OV_MAXBITS) ? 1 : lastbit / BITS_PER_BYTE + 1; - g_assert_cmpint(vec_len, <=, OV_MAXBYTES); + g_assert(vec_len <= OV_MAXBYTES); /* guest expects vector len encoded as vec_len - 1, since the length byte * is assumed and not included, and the first byte of the vector * is assumed as well diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index f7c91230d5..fa546fb3ce 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -786,7 +786,7 @@ static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq, DPRINTF(s, D_VERBOSE, "error %d\n", p->b.bError); } if (len) { - g_assert_nonnull(data); + assert(data); memcpy(p->abData, data, len); } ccid_reset_error_status(s); diff --git a/qom/object.c b/qom/object.c index e6462f289c..4609e34a6a 100644 --- a/qom/object.c +++ b/qom/object.c @@ -295,7 +295,7 @@ static void type_initialize(TypeImpl *ti) GSList *e; int i; - g_assert_cmpint(parent->class_size, <=, ti->class_size); + g_assert(parent->class_size <= ti->class_size); memcpy(ti->class, parent->class, parent->class_size); ti->class->interfaces = NULL; ti->class->properties = g_hash_table_new_full( @@ -372,9 +372,9 @@ static void object_initialize_with_type(void *data, size_t size, TypeImpl *type) g_assert(type != NULL); type_initialize(type); - g_assert_cmpint(type->instance_size, >=, sizeof(Object)); + g_assert(type->instance_size >= sizeof(Object)); g_assert(type->abstract == false); - g_assert_cmpint(size, >=, type->instance_size); + g_assert(size >= type->instance_size); memset(obj, 0, type->instance_size); obj->class = type->class; @@ -475,7 +475,7 @@ static void object_finalize(void *data) object_property_del_all(obj); object_deinit(obj, ti); - g_assert_cmpint(obj->ref, ==, 0); + g_assert(obj->ref == 0); if (obj->free) { obj->free(obj); } @@ -917,7 +917,7 @@ void object_unref(Object *obj) if (!obj) { return; } - g_assert_cmpint(obj->ref, >, 0); + g_assert(obj->ref > 0); /* parent always holds a reference to its children */ if (atomic_fetch_dec(&obj->ref) == 1) { diff --git a/util/qht.c b/util/qht.c index ff4d2e6974..55b0738cd1 100644 --- a/util/qht.c +++ b/util/qht.c @@ -759,7 +759,7 @@ static void qht_do_resize_reset(struct qht *ht, struct qht_map *new, bool reset) return; } - g_assert_cmpuint(new->n_buckets, !=, old->n_buckets); + g_assert(new->n_buckets != old->n_buckets); qht_map_iter__all_locked(ht, old, qht_map_copy, new); qht_map_debug__all_locked(new); From b61acdecbf19c3c5a327baec1e8e4c06d0da68b7 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 31 May 2018 16:24:35 -0500 Subject: [PATCH 1214/2380] block: Ignore generated job QAPI files Commit bf42508f introduced new generated files; make sure they don't get accidentally committed from an in-tree build. Signed-off-by: Eric Blake Reviewed-by: Jeff Cody Reviewed-by: Max Reitz Message-id: 20180531212435.165261-1-eblake@redhat.com Signed-off-by: Jeff Cody --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 81e1f2fb0f..9da3b3e626 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ /qapi/qapi-commands-common.[ch] /qapi/qapi-commands-crypto.[ch] /qapi/qapi-commands-introspect.[ch] +/qapi/qapi-commands-job.[ch] /qapi/qapi-commands-migration.[ch] /qapi/qapi-commands-misc.[ch] /qapi/qapi-commands-net.[ch] @@ -53,6 +54,7 @@ /qapi/qapi-events-common.[ch] /qapi/qapi-events-crypto.[ch] /qapi/qapi-events-introspect.[ch] +/qapi/qapi-events-job.[ch] /qapi/qapi-events-migration.[ch] /qapi/qapi-events-misc.[ch] /qapi/qapi-events-net.[ch] @@ -71,6 +73,7 @@ /qapi/qapi-types-common.[ch] /qapi/qapi-types-crypto.[ch] /qapi/qapi-types-introspect.[ch] +/qapi/qapi-types-job.[ch] /qapi/qapi-types-migration.[ch] /qapi/qapi-types-misc.[ch] /qapi/qapi-types-net.[ch] @@ -88,6 +91,7 @@ /qapi/qapi-visit-common.[ch] /qapi/qapi-visit-crypto.[ch] /qapi/qapi-visit-introspect.[ch] +/qapi/qapi-visit-job.[ch] /qapi/qapi-visit-migration.[ch] /qapi/qapi-visit-misc.[ch] /qapi/qapi-visit-net.[ch] From 656282d245b49b84d4a1a47d7b7ede482d541776 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 13 Jun 2018 19:51:56 -0400 Subject: [PATCH 1215/2380] ui: darwin: gtk: Add missing input keymap In appears the input keymap for osx was forgotten in the commit that converted the gtk frontend to keycodemapdb. Add it. Fixes: 2ec78706 ("ui: convert GTK and SDL1 frontends to keycodemapdb") CC: Daniel P. Berrange Signed-off-by: Keno Fischer Message-id: 1528933916-40670-1-git-send-email-keno@juliacomputing.com Signed-off-by: Gerd Hoffmann --- Makefile | 1 + include/ui/input.h | 3 +++ ui/input-keymap.c | 1 + 3 files changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 6c6664d9a3..bdae3133fb 100644 --- a/Makefile +++ b/Makefile @@ -322,6 +322,7 @@ KEYCODEMAP_FILES = \ ui/input-keymap-xorgkbd-to-qcode.c \ ui/input-keymap-xorgxquartz-to-qcode.c \ ui/input-keymap-xorgxwin-to-qcode.c \ + ui/input-keymap-osx-to-qcode.c \ $(NULL) GENERATED_FILES += $(KEYCODEMAP_FILES) diff --git a/include/ui/input.h b/include/ui/input.h index 16395ab8f2..34ebc67c5a 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -116,4 +116,7 @@ extern const guint16 qemu_input_map_xorgxquartz_to_qcode[]; extern const guint qemu_input_map_xorgxwin_to_qcode_len; extern const guint16 qemu_input_map_xorgxwin_to_qcode[]; +extern const guint qemu_input_map_osx_to_qcode_len; +extern const guint16 qemu_input_map_osx_to_qcode[]; + #endif /* INPUT_H */ diff --git a/ui/input-keymap.c b/ui/input-keymap.c index 87cc301b7a..db5ccff5ad 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -21,6 +21,7 @@ #include "ui/input-keymap-xorgkbd-to-qcode.c" #include "ui/input-keymap-xorgxquartz-to-qcode.c" #include "ui/input-keymap-xorgxwin-to-qcode.c" +#include "ui/input-keymap-osx-to-qcode.c" int qemu_input_linux_to_qcode(unsigned int lnx) { From 64bf97e56f3ab44d587c92c40503d646273a24df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amadeusz=20S=C5=82awi=C5=84ski?= Date: Wed, 13 Jun 2018 19:27:07 +0200 Subject: [PATCH 1216/2380] sdl2: restore window dimensions by resize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit instead of destroying and recreating window, fixes segfault caused by handle_keyup trying to access no more existing window when using Ctrl-Alt-U to restore window "un-scaled" dimensions Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7ffff7f92b80 (LWP 3711)] handle_keyup (ev=0x7fffffffd010) at ui/sdl2.c:416 416 scon->ignore_hotkeys = false; (gdb) bt #0 handle_keyup (ev=0x7fffffffd010) at ui/sdl2.c:416 #1 sdl2_poll_events (scon=0x100fee5a8) at ui/sdl2.c:608 #2 0x0000000100585bf2 in dpy_refresh (s=0x101ad3e00) at ui/console.c:1658 #3 gui_update (opaque=0x101ad3e00) at ui/console.c:205 #4 0x0000000100690f2c in timerlist_run_timers (timer_list=0x100ede130) at util/qemu-timer.c:536 #5 0x0000000100691177 in qemu_clock_run_timers (type=QEMU_CLOCK_REALTIME) at util/qemu-timer.c:547 #6 qemu_clock_run_all_timers () at util/qemu-timer.c:674 #7 0x0000000100691651 in main_loop_wait (nonblocking=) at util/main-loop.c:503 #8 0x00000001003d650f in main_loop () at vl.c:1848 #9 0x0000000100289681 in main (argc=, argv=, envp=) at vl.c:4605 Signed-off-by: Amadeusz Sławiński Message-id: 20180613172707.31530-1-amade@asmblr.net Signed-off-by: Gerd Hoffmann --- ui/sdl2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/sdl2.c b/ui/sdl2.c index a0f9b16bc1..3ae4719c32 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -371,8 +371,7 @@ static void handle_keydown(SDL_Event *ev) } break; case SDL_SCANCODE_U: - sdl2_window_destroy(scon); - sdl2_window_create(scon); + sdl2_window_resize(scon); if (!scon->opengl) { /* re-create scon->texture */ sdl2_2d_switch(&scon->dcl, scon->surface); From 6a259f8d759e0c7f999473fd0668070f5b03ed7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 12 Jun 2018 09:28:23 +0100 Subject: [PATCH 1217/2380] travis: display config.log when configure fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When configure fails in CI systems we must be able to see the contents of the config.log file to diagnose the root cause. Signed-off-by: Daniel P. Berrangé Reviewed-by: Eric Blake Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Fam Zheng [AJB: used Eric's suggested {} form] Signed-off-by: Alex Bennée --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 814be151f4..d5e6d50e6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,7 +69,7 @@ before_install: - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ - git submodule update --init --recursive before_script: - - ./configure ${CONFIG} + - ./configure ${CONFIG} || { cat config.log && exit 1; } script: - make ${MAKEFLAGS} && ${TEST_CMD} matrix: @@ -151,4 +151,4 @@ matrix: - CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user" - TEST_CMD="" before_script: - - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log + - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; } From 73d99c11d2009c2195825fa5de4fb44f867f73b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 12 Jun 2018 14:24:20 +0100 Subject: [PATCH 1218/2380] travis: reduce coverage of gprof build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This build is regularly timing out and even switching off linux-user wasn't enough. Instead explicitly choose a target list of broadly the "major" architectures. This is enough to check the gprof build machinery works without worrying about the actual coverage results. I did try various YAML constructs for specifying CONFIG with continuation but couldn't get any of them to work hence the very long line. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d5e6d50e6d..c200dde534 100644 --- a/.travis.yml +++ b/.travis.yml @@ -80,7 +80,7 @@ matrix: - env: CONFIG="--disable-user" compiler: clang # gprof/gcov are GCC features - - env: CONFIG="--enable-gprof --enable-gcov --disable-pie --disable-linux-user" + - env: CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" compiler: gcc # We manually include builds which we disable "make check" for - env: CONFIG="--enable-debug --enable-tcg-interpreter" From f8309de9b7e7de1bfdfaddf8df4130a06e7e31b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 14 Jun 2018 11:28:20 +0100 Subject: [PATCH 1219/2380] travis: reduce time taken for trace-backend testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These builds are reaching regular timeouts and probably don't need to be so widely exercised. ftrace and ust in particular are used in conjunction with whole system profiling which makes most sense with KVM setups, hence the native softmmu target. We also expand simple to cover the multiple log backends while restricting its scope to user-mode testing only. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c200dde534..fabfe9ec34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -86,13 +86,14 @@ matrix: - env: CONFIG="--enable-debug --enable-tcg-interpreter" TEST_CMD="" compiler: gcc - - env: CONFIG="--enable-trace-backends=simple" + # We don't need to exercise every backend with every front-end + - env: CONFIG="--enable-trace-backends=log,simple,syslog --disable-system" TEST_CMD="" compiler: gcc - - env: CONFIG="--enable-trace-backends=ftrace" + - env: CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu" TEST_CMD="" compiler: gcc - - env: CONFIG="--enable-trace-backends=ust" + - env: CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu" TEST_CMD="" compiler: gcc - env: CONFIG="--disable-tcg" From d542800d1edc62f63f8a29cfa6bdd1a9536ae11c Mon Sep 17 00:00:00 2001 From: Brijesh Singh Date: Fri, 6 Apr 2018 13:51:25 -0500 Subject: [PATCH 1220/2380] tap: set vhostfd passed from qemu cli to non-blocking A guest boot hangs while probing the network interface when iommu_platform=on is used. The following qemu cli hangs without this patch: # $QEMU \ -netdev tap,fd=3,id=hostnet0,vhost=on,vhostfd=4 3<>/dev/tap67 4<>/dev/host-net \ -device virtio-net-pci,netdev=hostnet0,id=net0,iommu_platform=on,disable-legacy=on \ ... Commit: c471ad0e9bd46 (vhost_net: device IOTLB support) took care of setting vhostfd to non-blocking when QEMU opens /dev/host-net but if the fd is passed from qemu cli then we need to ensure that fd is set to non-blocking. Fixes: c471ad0e9bd46 ("vhost_net: device IOTLB support") Cc: qemu-stable@nongnu.org Cc: Michael S. Tsirkin Cc: Jason Wang Signed-off-by: Brijesh Singh Signed-off-by: Jason Wang --- net/tap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/tap.c b/net/tap.c index de05f20e28..2126f4882d 100644 --- a/net/tap.c +++ b/net/tap.c @@ -40,6 +40,7 @@ #include "qemu-common.h" #include "qemu/cutils.h" #include "qemu/error-report.h" +#include "qemu/sockets.h" #include "net/tap.h" @@ -693,6 +694,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } return; } + qemu_set_nonblock(vhostfd); } else { vhostfd = open("/dev/vhost-net", O_RDWR); if (vhostfd < 0) { From 8b43f964f987d44f25df1b7c002d0c241b57bffe Mon Sep 17 00:00:00 2001 From: Lin Ma Date: Mon, 11 Jun 2018 17:23:05 +0800 Subject: [PATCH 1221/2380] net: Fix a potential segfault If user forgets to provide any backend types for '-netdev' in qemu CLI, It triggers seg fault. e.g. Expected: $ qemu -netdev id=net0 qemu-system-x86_64: Parameter 'type' is missing Actual: $ qemu -netdev id=net0 Segmentation fault (core dumped) Fixes: 547203ead4327 ("net: List available netdevs with "-netdev help") Reviewed-by: Thomas Huth Cc: qemu-stable@nongnu.org Signed-off-by: Lin Ma Signed-off-by: Jason Wang --- net/net.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/net.c b/net/net.c index efb9eaf779..2a3133990c 100644 --- a/net/net.c +++ b/net/net.c @@ -1093,7 +1093,9 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp) int ret = -1; Visitor *v = opts_visitor_new(opts); - if (is_netdev && is_help_option(qemu_opt_get(opts, "type"))) { + const char *type = qemu_opt_get(opts, "type"); + + if (is_netdev && type && is_help_option(type)) { show_netdevs(); exit(0); } else { From 2285a00c113469bb3e750ca4921cdb7baaae9e25 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 1 Apr 2018 23:17:55 +0200 Subject: [PATCH 1222/2380] e1000e: Do not auto-clear ICR bits which aren't set in EIAC The spec does not justify clearing of any E1000_ICR_OTHER_CAUSES when E1000_ICR_OTHER is set in EIAC. In fact, removing this code fixes the issue the Linux driver runs into since 4aea7a5c5e94 ("e1000e: Avoid receiver overrun interrupt bursts") and was worked around by 745d0bd3af99 ("e1000e: Remove Other from EIAC"). Signed-off-by: Jan Kiszka Signed-off-by: Jason Wang --- hw/net/e1000e_core.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index c93c4661ed..950489160a 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -2022,10 +2022,6 @@ e1000e_msix_notify_one(E1000ECore *core, uint32_t cause, uint32_t int_cfg) effective_eiac = core->mac[EIAC] & cause; - if (effective_eiac == E1000_ICR_OTHER) { - effective_eiac |= E1000_ICR_OTHER_CAUSES; - } - core->mac[ICR] &= ~effective_eiac; if (!(core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { From c67daf4a24442d1bb404a11a6a54dc45ea10f234 Mon Sep 17 00:00:00 2001 From: linzhecheng Date: Tue, 12 Jun 2018 10:24:45 +0800 Subject: [PATCH 1223/2380] vhost-user: delete net client if necessary As qemu_new_net_client create new ncs but error happens later, ncs will be left in global net_clients list and we can't use them any more, so we need to cleanup them. Cc: qemu-stable@nongnu.org Signed-off-by: linzhecheng Signed-off-by: Jason Wang --- net/vhost-user.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/vhost-user.c b/net/vhost-user.c index 608b837175..a39f9c9974 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -345,6 +345,9 @@ err: s->vhost_user = NULL; } } + if (nc0) { + qemu_del_net_client(nc0); + } return -1; } From 643fbf02e04ee2631fda82cb7e1d0f3c86e38213 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Thu, 14 Jun 2018 15:34:44 +0200 Subject: [PATCH 1224/2380] target-microblaze: mmu: Correct masking of output addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct the masking of output addresses. This fixes Coverity CID 1391441. Fixes: commit 3924a9aa02 Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reported-by: Peter Maydell Signed-off-by: Edgar E. Iglesias --- target/microblaze/mmu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index f4ceaea520..fcf86b12d5 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -159,7 +159,6 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, lu->vaddr = tlb_tag; lu->paddr = tlb_rpn & mmu->c_addr_mask; - lu->paddr = tlb_rpn; lu->size = tlb_size; lu->err = ERR_HIT; lu->idx = i; From 462c254430b49ef708c75e038d7e796764058ca1 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Thu, 14 Jun 2018 15:48:16 +0200 Subject: [PATCH 1225/2380] target-microblaze: Rework NOP/zero instruction handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the abort on a sequence of NOP/zero instructions. Always return early and avoid decoding NOP/zero instructions. This fixes Coverity CID 1391443. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias --- target/microblaze/translate.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 6c64946398..78ca265b04 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -90,7 +90,6 @@ typedef struct DisasContext { uint32_t jmp_pc; int abort_at_next_insn; - int nr_nops; struct TranslationBlock *tb; int singlestep_enabled; } DisasContext; @@ -1576,17 +1575,12 @@ static inline void decode(DisasContext *dc, uint32_t ir) dc->ir = ir; LOG_DIS("%8.8x\t", dc->ir); - if (dc->ir) - dc->nr_nops = 0; - else { + if (ir == 0) { trap_illegal(dc, dc->cpu->env.pvr.regs[2] & PVR2_OPCODE_0x0_ILL_MASK); - - LOG_DIS("nr_nops=%d\t", dc->nr_nops); - dc->nr_nops++; - if (dc->nr_nops > 4) { - cpu_abort(CPU(dc->cpu), "fetching nop sequence\n"); - } + /* Don't decode nop/zero instructions any further. */ + return; } + /* bit 2 seems to indicate insn type. */ dc->type_b = ir & (1 << 29); @@ -1633,7 +1627,6 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) dc->singlestep_enabled = cs->singlestep_enabled; dc->cpustate_changed = 0; dc->abort_at_next_insn = 0; - dc->nr_nops = 0; if (pc_start & 3) { cpu_abort(cs, "Microblaze: unaligned PC=%x\n", pc_start); From e0b371ed5e2db079051139136fd0478728b6a58f Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 11 Jun 2018 16:39:26 -0500 Subject: [PATCH 1226/2380] qemu-img: Fix assert when mapping unaligned raw file Commit a290f085 exposed a latent bug in qemu-img map introduced during the conversion of block status to be byte-based. Earlier in commit 5e344dd8, the internal interface get_block_status() switched to take byte-based parameters, but still called a sector-based block layer function; as such, rounding was added in the lone caller to obey the contract. However, commit 237d78f8 changed get_block_status() to truly be byte-based, at which point rounding to sector boundaries can result in calling bdrv_block_status() with 'bytes == 0' (a coding error) when the boundary between data and a hole falls mid-sector (true for the past-EOF implicit hole present in POSIX files). Fix things by removing the rounding that is now no longer necessary. See also https://bugzilla.redhat.com/1589738 Fixes: 237d78f8 Reported-by: Dan Kenigsberg Reported-by: Nir Soffer Reported-by: Maor Lipchuk CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake Signed-off-by: Kevin Wolf --- qemu-img.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c index 1dcdd47254..e1a506f7f6 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -2906,7 +2906,7 @@ static int img_map(int argc, char **argv) int64_t n; /* Probe up to 1 GiB at a time. */ - n = QEMU_ALIGN_DOWN(MIN(1 << 30, length - offset), BDRV_SECTOR_SIZE); + n = MIN(1 << 30, length - offset); ret = get_block_status(bs, offset, n, &next); if (ret < 0) { From c6a9d2f6f9bc0c163b3a3073126464a2446bad5f Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 11 Jun 2018 16:39:27 -0500 Subject: [PATCH 1227/2380] iotests: Add test 221 to catch qemu-img map regression Although qemu-img creates aligned files (by rounding up), it must also gracefully handle files that are not sector-aligned. Test that the bug fixed in the previous patch does not recur. It's a bit annoying that we can see the (implicit) hole past the end of the file on to the next sector boundary, so if we ever reach the point where we report a byte-accurate size rather than our current behavior of always rounding up, this test will probably need a slight modification. Signed-off-by: Eric Blake Signed-off-by: Kevin Wolf --- tests/qemu-iotests/221 | 60 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/221.out | 16 ++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 77 insertions(+) create mode 100755 tests/qemu-iotests/221 create mode 100644 tests/qemu-iotests/221.out diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221 new file mode 100755 index 0000000000..41c4e4bdf8 --- /dev/null +++ b/tests/qemu-iotests/221 @@ -0,0 +1,60 @@ +#!/bin/bash +# +# Test qemu-img vs. unaligned images +# +# Copyright (C) 2018 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 . +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +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 file +_supported_os Linux + +echo +echo "=== Check mapping of unaligned raw image ===" +echo + +_make_test_img 43009 # qemu-img create rounds size up +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +truncate --size=43009 "$TEST_IMG" # so we resize it and check again +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +$QEMU_IO -c 'w 43008 1' "$TEST_IMG" | _filter_qemu_io # writing also rounds up +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +truncate --size=43009 "$TEST_IMG" # so we resize it and check again +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out new file mode 100644 index 0000000000..a9c0190aad --- /dev/null +++ b/tests/qemu-iotests/221.out @@ -0,0 +1,16 @@ +QA output created by 221 + +=== Check mapping of unaligned raw image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=43009 +[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +wrote 1/1 bytes at offset 43008 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, +{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, +{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 0914c922d7..937a3d0a4d 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -218,3 +218,4 @@ 217 rw auto quick 218 rw auto quick 219 rw auto +221 rw auto quick From c5b09f3f2c4db32f39ecbde2cf2f78e7a657a85d Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 6 Jun 2018 19:02:56 -0400 Subject: [PATCH 1228/2380] jobs: fix stale wording During the design for manual completion, we decided not to use the "manual" property as a shorthand for both auto-dismiss and auto-finalize. Fix the wording. Signed-off-by: John Snow Reviewed-by: Jeff Cody Reviewed-by: Markus Armbruster Signed-off-by: Kevin Wolf --- qapi/job.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/qapi/job.json b/qapi/job.json index 17d10037c4..226443594b 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -50,16 +50,17 @@ # the last job in a transaction. # # @pending: The job has finished its work, but has finalization steps that it -# needs to make prior to completing. These changes may require -# manual intervention by the management process if manual was set -# to true. These changes may still fail. +# needs to make prior to completing. These changes will require +# manual intervention via @job-finalize if auto-finalize was set to +# false. These pending changes may still fail. # # @aborting: The job is in the process of being aborted, and will finish with # an error. The job will afterwards report that it is @concluded. # This status may not be visible to the management process. # -# @concluded: The job has finished all work. If manual was set to true, the job -# will remain in the query list until it is dismissed. +# @concluded: The job has finished all work. If auto-dismiss was set to false, +# the job will remain in the query list until it is dismissed via +# @job-dismiss. # # @null: The job is in the process of being dismantled. This state should not # ever be visible externally. From b8a366feb2aa2200c42fa983a9c607fb78a501db Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 6 Jun 2018 19:02:57 -0400 Subject: [PATCH 1229/2380] jobs: fix verb references in docs These point to the job versions now, not the blockjob versions which don't really exist anymore. Except set-speed, which does. It sticks out like a sore thumb. This patch doesn't fix that, but it doesn't make it any worse, either. Signed-off-by: John Snow Reviewed-by: Jeff Cody Reviewed-by: Markus Armbruster Signed-off-by: Kevin Wolf --- qapi/job.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qapi/job.json b/qapi/job.json index 226443594b..9d074eb8d2 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -76,19 +76,19 @@ # # Represents command verbs that can be applied to a job. # -# @cancel: see @block-job-cancel +# @cancel: see @job-cancel # -# @pause: see @block-job-pause +# @pause: see @job-pause # -# @resume: see @block-job-resume +# @resume: see @job-resume # # @set-speed: see @block-job-set-speed # -# @complete: see @block-job-complete +# @complete: see @job-complete # -# @dismiss: see @block-job-dismiss +# @dismiss: see @job-dismiss # -# @finalize: see @block-job-finalize +# @finalize: see @job-finalize # # Since: 2.12 ## From bb9f762ff35459e34630d731a5a91c02602150d1 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:26 +0200 Subject: [PATCH 1230/2380] rbd: Drop deprecated -drive parameter "filename" Parameter "filename" is deprecated since commit 91589d9e5ca, v2.10.0. Time to get rid of it. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/rbd.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index a16431e267..40c6e4185f 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -632,25 +632,9 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, QObject *crumpled = NULL; const QDictEntry *e; Error *local_err = NULL; - const char *filename; char *keypairs, *secretid; int r; - /* If we are given a filename, parse the filename, with precedence given to - * filename encoded options */ - filename = qdict_get_try_str(options, "filename"); - if (filename) { - warn_report("'filename' option specified. " - "This is an unsupported option, and may be deprecated " - "in the future"); - qemu_rbd_parse_filename(filename, options, &local_err); - qdict_del(options, "filename"); - if (local_err) { - error_propagate(errp, local_err); - return -EINVAL; - } - } - keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); if (keypairs) { qdict_del(options, "=keyvalue-pairs"); From deadbb8ebb5c253da9b8ed02ab51a0fadf60edc7 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:27 +0200 Subject: [PATCH 1231/2380] iscsi: Drop deprecated -drive parameter "filename" Parameter "filename" is deprecated since commit 5c3ad1a6a8f, v2.10.0. Time to get rid of it. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/iscsi.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/block/iscsi.c b/block/iscsi.c index c2fbd8a8aa..7e3ea72bd2 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1713,10 +1713,6 @@ static QemuOptsList runtime_opts = { .name = "timeout", .type = QEMU_OPT_NUMBER, }, - { - .name = "filename", - .type = QEMU_OPT_STRING, - }, { /* end of list */ } }, }; @@ -1756,27 +1752,12 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, char *initiator_name = NULL; QemuOpts *opts; Error *local_err = NULL; - const char *transport_name, *portal, *target, *filename; + const char *transport_name, *portal, *target; #if LIBISCSI_API_VERSION >= (20160603) enum iscsi_transport_type transport; #endif int i, ret = 0, timeout = 0, lun; - /* If we are given a filename, parse the filename, with precedence given to - * filename encoded options */ - filename = qdict_get_try_str(options, "filename"); - if (filename) { - warn_report("'filename' option specified. " - "This is an unsupported option, and may be deprecated " - "in the future"); - iscsi_parse_filename(filename, options, &local_err); - if (local_err) { - ret = -EINVAL; - error_propagate(errp, local_err); - goto exit; - } - } - opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { @@ -2006,7 +1987,7 @@ out: } memset(iscsilun, 0, sizeof(IscsiLun)); } -exit: + return ret; } From 609f45ea9507fc1603eaeda7f5066b99beac6721 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Thu, 14 Jun 2018 21:14:28 +0200 Subject: [PATCH 1232/2380] block: Add block-specific QDict header There are numerous QDict functions that have been introduced for and are used only by the block layer. Move their declarations into an own header file to reflect that. While qdict_extract_subqdict() is in fact used outside of the block layer (in util/qemu-config.c), it is still a function related very closely to how the block layer works with nested QDicts, namely by sometimes flattening them. Therefore, its declaration is put into this header as well and util/qemu-config.c includes it with a comment stating exactly which function it needs. Suggested-by: Markus Armbruster Signed-off-by: Max Reitz Message-Id: <20180509165530.29561-7-mreitz@redhat.com> [Copyright note tweaked, superfluous includes dropped] Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 1 + block/gluster.c | 1 + block/iscsi.c | 1 + block/nbd.c | 1 + block/nfs.c | 1 + block/parallels.c | 1 + block/qcow.c | 1 + block/qcow2.c | 1 + block/qed.c | 1 + block/quorum.c | 1 + block/rbd.c | 1 + block/sheepdog.c | 1 + block/snapshot.c | 1 + block/ssh.c | 1 + block/vhdx.c | 1 + block/vpc.c | 1 + block/vvfat.c | 1 + block/vxhs.c | 1 + blockdev.c | 1 + include/block/qdict.h | 32 ++++++++++++++++++++++++++++++++ include/qapi/qmp/qdict.h | 17 ----------------- qobject/qdict.c | 1 + tests/check-qdict.c | 1 + tests/check-qobject.c | 1 + tests/test-replication.c | 1 + util/qemu-config.c | 1 + 26 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 include/block/qdict.h diff --git a/block.c b/block.c index 50887087f3..afe30caac3 100644 --- a/block.c +++ b/block.c @@ -27,6 +27,7 @@ #include "block/block_int.h" #include "block/blockjob.h" #include "block/nbd.h" +#include "block/qdict.h" #include "qemu/error-report.h" #include "module_block.h" #include "qemu/module.h" diff --git a/block/gluster.c b/block/gluster.c index 9900b6420c..b5fe7f3e87 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include #include "block/block_int.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" diff --git a/block/iscsi.c b/block/iscsi.c index 7e3ea72bd2..9f00fb47a5 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -33,6 +33,7 @@ #include "qemu/bitops.h" #include "qemu/bitmap.h" #include "block/block_int.h" +#include "block/qdict.h" #include "scsi/constants.h" #include "qemu/iov.h" #include "qemu/option.h" diff --git a/block/nbd.c b/block/nbd.c index ff8333e3c1..d6c4c4ddbc 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -28,6 +28,7 @@ #include "qemu/osdep.h" #include "nbd-client.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qemu/uri.h" #include "block/block_int.h" diff --git a/block/nfs.c b/block/nfs.c index 3349b67a76..3170b059b3 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -29,6 +29,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "block/block_int.h" +#include "block/qdict.h" #include "trace.h" #include "qemu/iov.h" #include "qemu/option.h" diff --git a/block/parallels.c b/block/parallels.c index 6e9c37f44e..c1d9643498 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -31,6 +31,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" diff --git a/block/qcow.c b/block/qcow.c index 1f866af0d3..8c08908fd8 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" diff --git a/block/qcow2.c b/block/qcow2.c index 6fa5e1d71a..d2d955f984 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include diff --git a/block/qed.c b/block/qed.c index 65cfe92393..324a953cbc 100644 --- a/block/qed.c +++ b/block/qed.c @@ -13,6 +13,7 @@ */ #include "qemu/osdep.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qemu/timer.h" #include "qemu/bswap.h" diff --git a/block/quorum.c b/block/quorum.c index b6476c405a..9152da8c58 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -17,6 +17,7 @@ #include "qemu/cutils.h" #include "qemu/option.h" #include "block/block_int.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qapi/qapi-events-block.h" #include "qapi/qmp/qdict.h" diff --git a/block/rbd.c b/block/rbd.c index 40c6e4185f..9659c7361f 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -18,6 +18,7 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include "block/block_int.h" +#include "block/qdict.h" #include "crypto/secret.h" #include "qemu/cutils.h" #include "qapi/qmp/qstring.h" diff --git a/block/sheepdog.c b/block/sheepdog.c index 7b98725af7..2e1f0e6eca 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -24,6 +24,7 @@ #include "qemu/option.h" #include "qemu/sockets.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/bitops.h" #include "qemu/cutils.h" diff --git a/block/snapshot.c b/block/snapshot.c index 2953d96c06..f9903bc94e 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "block/snapshot.h" #include "block/block_int.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" diff --git a/block/ssh.c b/block/ssh.c index 4c4fa3ccfc..eec37dd27c 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -28,6 +28,7 @@ #include #include "block/block_int.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/option.h" diff --git a/block/vhdx.c b/block/vhdx.c index 0831c5c5f4..2e32e24514 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -18,6 +18,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" diff --git a/block/vpc.c b/block/vpc.c index 0ebfcd3cc8..41c8c980f1 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" diff --git a/block/vvfat.c b/block/vvfat.c index 662dca0114..4595f335b8 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -27,6 +27,7 @@ #include #include "qapi/error.h" #include "block/block_int.h" +#include "block/qdict.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/bswap.h" diff --git a/block/vxhs.c b/block/vxhs.c index 339e23218d..0cb0a007e9 100644 --- a/block/vxhs.c +++ b/block/vxhs.c @@ -12,6 +12,7 @@ #include #include #include "block/block_int.h" +#include "block/qdict.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" diff --git a/blockdev.c b/blockdev.c index 4862323012..c24e261e37 100644 --- a/blockdev.c +++ b/blockdev.c @@ -35,6 +35,7 @@ #include "sysemu/blockdev.h" #include "hw/block/block.h" #include "block/blockjob.h" +#include "block/qdict.h" #include "block/throttle-groups.h" #include "monitor/monitor.h" #include "qemu/error-report.h" diff --git a/include/block/qdict.h b/include/block/qdict.h new file mode 100644 index 0000000000..71c037afba --- /dev/null +++ b/include/block/qdict.h @@ -0,0 +1,32 @@ +/* + * Special QDict functions used by the block layer + * + * Copyright (c) 2013-2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#ifndef BLOCK_QDICT_H +#define BLOCK_QDICT_H + +#include "qapi/qmp/qdict.h" + +void qdict_copy_default(QDict *dst, QDict *src, const char *key); +void qdict_set_default_str(QDict *dst, const char *key, const char *val); + +void qdict_join(QDict *dest, QDict *src, bool overwrite); + +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); +QObject *qdict_crumple(const QDict *src, Error **errp); +void qdict_flatten(QDict *qdict); + +typedef struct QDictRenames { + const char *from; + const char *to; +} QDictRenames; +bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); + +#endif diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index 921a28d2d3..7f3ec10a10 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -67,23 +67,6 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key, bool qdict_get_try_bool(const QDict *qdict, const char *key, bool 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); -QObject *qdict_crumple(const QDict *src, Error **errp); - -void qdict_join(QDict *dest, QDict *src, bool overwrite); - -typedef struct QDictRenames { - const char *from; - const char *to; -} QDictRenames; -bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); #endif /* QDICT_H */ diff --git a/qobject/qdict.c b/qobject/qdict.c index 22800eeceb..0554c64553 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "block/qdict.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qbool.h" diff --git a/tests/check-qdict.c b/tests/check-qdict.c index eba5d3528e..93e2112b6d 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "block/qdict.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qnum.h" diff --git a/tests/check-qobject.c b/tests/check-qobject.c index 5cb08fcb63..16ccbde82c 100644 --- a/tests/check-qobject.c +++ b/tests/check-qobject.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "block/qdict.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" diff --git a/tests/test-replication.c b/tests/test-replication.c index 68c0d04f2a..c8165ae954 100644 --- a/tests/test-replication.c +++ b/tests/test-replication.c @@ -15,6 +15,7 @@ #include "qemu/option.h" #include "replication.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #define IMG_SIZE (64 * 1024 * 1024) diff --git a/util/qemu-config.c b/util/qemu-config.c index 14d84022dc..9d2e278e29 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "block/qdict.h" /* for qdict_extract_subqdict() */ #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qmp/qdict.h" From 0bcc8e5bd8d6fd6e5cb6462054f7cfa45b282f9a Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:29 +0200 Subject: [PATCH 1233/2380] qobject: Move block-specific qdict code to block-qdict.c Pure code motion, except for two brace placements and a comment tweaked to appease checkpatch. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- MAINTAINERS | 2 + qobject/Makefile.objs | 1 + qobject/block-qdict.c | 640 +++++++++++++++++++++++++++++++++++++ qobject/qdict.c | 629 ------------------------------------ tests/Makefile.include | 4 + tests/check-block-qdict.c | 655 ++++++++++++++++++++++++++++++++++++++ tests/check-qdict.c | 642 ------------------------------------- 7 files changed, 1302 insertions(+), 1271 deletions(-) create mode 100644 qobject/block-qdict.c create mode 100644 tests/check-block-qdict.c diff --git a/MAINTAINERS b/MAINTAINERS index 8a94517e9e..0fb5f38f9f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1369,6 +1369,8 @@ F: qemu-img* F: qemu-io* F: tests/qemu-iotests/ F: util/qemu-progress.c +F: qobject/block-qdict.c +F: test/check-block-qdict.c T: git git://repo.or.cz/qemu/kevin.git block Block I/O path diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs index 002d25873a..7b12c9cacf 100644 --- a/qobject/Makefile.objs +++ b/qobject/Makefile.objs @@ -1,2 +1,3 @@ util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o +util-obj-y += block-qdict.o diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c new file mode 100644 index 0000000000..fb92010dc5 --- /dev/null +++ b/qobject/block-qdict.c @@ -0,0 +1,640 @@ +/* + * Special QDict functions used by the block layer + * + * Copyright (c) 2013-2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "block/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qemu/cutils.h" +#include "qapi/error.h" + +/** + * 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) { + qdict_put_obj(dst, key, qobject_ref(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_str(dst, key, val); +} + +static void qdict_flatten_qdict(QDict *qdict, QDict *target, + const char *prefix); + +static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) +{ + QObject *value; + const QListEntry *entry; + char *new_key; + int i; + + /* This function is never called with prefix == NULL, i.e., it is always + * called from within qdict_flatten_q(list|dict)(). Therefore, it does not + * need to remove list entries during the iteration (the whole list will be + * deleted eventually anyway from qdict_flatten_qdict()). */ + assert(prefix); + + entry = qlist_first(qlist); + + for (i = 0; entry; entry = qlist_next(entry), i++) { + value = qlist_entry_obj(entry); + new_key = g_strdup_printf("%s.%i", prefix, i); + + if (qobject_type(value) == QTYPE_QDICT) { + qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); + } else if (qobject_type(value) == QTYPE_QLIST) { + qdict_flatten_qlist(qobject_to(QList, value), target, new_key); + } else { + /* All other types are moved to the target unchanged. */ + qdict_put_obj(target, new_key, qobject_ref(value)); + } + + g_free(new_key); + } +} + +static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) +{ + QObject *value; + const QDictEntry *entry, *next; + char *new_key; + bool delete; + + entry = qdict_first(qdict); + + while (entry != NULL) { + + next = qdict_next(qdict, entry); + value = qdict_entry_value(entry); + new_key = NULL; + delete = false; + + if (prefix) { + new_key = g_strdup_printf("%s.%s", prefix, entry->key); + } + + if (qobject_type(value) == QTYPE_QDICT) { + /* Entries of QDicts are processed recursively, the QDict object + * itself disappears. */ + qdict_flatten_qdict(qobject_to(QDict, value), target, + new_key ? new_key : entry->key); + delete = true; + } else if (qobject_type(value) == QTYPE_QLIST) { + qdict_flatten_qlist(qobject_to(QList, value), target, + new_key ? new_key : entry->key); + delete = true; + } else if (prefix) { + /* All other objects are moved to the target unchanged. */ + qdict_put_obj(target, new_key, qobject_ref(value)); + delete = true; + } + + g_free(new_key); + + if (delete) { + qdict_del(qdict, entry->key); + + /* Restart loop after modifying the iterated QDict */ + entry = qdict_first(qdict); + continue; + } + + entry = next; + } +} + +/** + * qdict_flatten(): For each nested QDict with key x, all fields with key y + * are moved to this QDict and their key is renamed to "x.y". For each nested + * QList with key x, the field at index y is moved to this QDict with the key + * "x.y" (i.e., the reverse of what qdict_array_split() does). + * This operation is applied recursively for nested QDicts and QLists. + */ +void qdict_flatten(QDict *qdict) +{ + qdict_flatten_qdict(qdict, qdict, NULL); +} + +/* extract all the src QDict entries starting by start into dst */ +void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) + +{ + const QDictEntry *entry, *next; + const char *p; + + *dst = qdict_new(); + entry = qdict_first(src); + + while (entry != NULL) { + next = qdict_next(src, entry); + if (strstart(entry->key, start, &p)) { + qdict_put_obj(*dst, p, qobject_ref(entry->value)); + qdict_del(src, entry->key); + } + entry = next; + } +} + +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)) { + if (count == INT_MAX) { + return -ERANGE; + } + count++; + } + } + + return count; +} + +/** + * qdict_array_split(): This function moves array-like elements of a QDict into + * a new QList. Every entry in the original QDict with a key "%u" or one + * prefixed "%u.", where %u designates an unsigned integer starting at 0 and + * incrementally counting up, will be moved to a new QDict at index %u in the + * output QList with the key prefix removed, if that prefix is "%u.". If the + * whole key is just "%u", the whole QObject will be moved unchanged without + * creating a new QDict. The function terminates when there is no entry in the + * QDict with a prefix directly (incrementally) following the last one; it also + * returns if there are both entries with "%u" and "%u." for the same index %u. + * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66} + * (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66}) + * => [{"a": 42, "b": 23}, {"x": 0}, 66] + * and {"4.y": 1, "o.o": 7} (remainder of the old QDict) + */ +void qdict_array_split(QDict *src, QList **dst) +{ + unsigned i; + + *dst = qlist_new(); + + for (i = 0; i < UINT_MAX; i++) { + QObject *subqobj; + bool is_subqdict; + QDict *subqdict; + char indexstr[32], prefix[32]; + size_t snprintf_ret; + + snprintf_ret = snprintf(indexstr, 32, "%u", i); + assert(snprintf_ret < 32); + + subqobj = qdict_get(src, indexstr); + + snprintf_ret = snprintf(prefix, 32, "%u.", i); + assert(snprintf_ret < 32); + + /* 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. + */ + if (!subqobj == !is_subqdict) { + break; + } + + if (is_subqdict) { + qdict_extract_subqdict(src, &subqdict, prefix); + assert(qdict_size(subqdict) > 0); + } else { + qobject_ref(subqobj); + qdict_del(src, indexstr); + } + + qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); + } +} + +/** + * qdict_split_flat_key: + * @key: the key string to split + * @prefix: non-NULL pointer to hold extracted prefix + * @suffix: non-NULL pointer to remaining suffix + * + * Given a flattened key such as 'foo.0.bar', split it into two parts + * at the first '.' separator. Allows double dot ('..') to escape the + * normal separator. + * + * e.g. + * 'foo.0.bar' -> prefix='foo' and suffix='0.bar' + * 'foo..0.bar' -> prefix='foo.0' and suffix='bar' + * + * The '..' sequence will be unescaped in the returned 'prefix' + * string. The 'suffix' string will be left in escaped format, so it + * can be fed back into the qdict_split_flat_key() key as the input + * later. + * + * The caller is responsible for freeing the string returned in @prefix + * using g_free(). + */ +static void qdict_split_flat_key(const char *key, char **prefix, + const char **suffix) +{ + const char *separator; + size_t i, j; + + /* Find first '.' separator, but if there is a pair '..' + * that acts as an escape, so skip over '..' */ + separator = NULL; + do { + if (separator) { + separator += 2; + } else { + separator = key; + } + separator = strchr(separator, '.'); + } while (separator && separator[1] == '.'); + + if (separator) { + *prefix = g_strndup(key, separator - key); + *suffix = separator + 1; + } else { + *prefix = g_strdup(key); + *suffix = NULL; + } + + /* Unescape the '..' sequence into '.' */ + for (i = 0, j = 0; (*prefix)[i] != '\0'; i++, j++) { + if ((*prefix)[i] == '.') { + assert((*prefix)[i + 1] == '.'); + i++; + } + (*prefix)[j] = (*prefix)[i]; + } + (*prefix)[j] = '\0'; +} + +/** + * qdict_is_list: + * @maybe_list: dict to check if keys represent list elements. + * + * Determine whether all keys in @maybe_list are valid list elements. + * If @maybe_list is non-zero in length and all the keys look like + * valid list indexes, this will return 1. If @maybe_list is zero + * length or all keys are non-numeric then it will return 0 to indicate + * it is a normal qdict. If there is a mix of numeric and non-numeric + * keys, or the list indexes are non-contiguous, an error is reported. + * + * Returns: 1 if a valid list, 0 if a dict, -1 on error + */ +static int qdict_is_list(QDict *maybe_list, Error **errp) +{ + const QDictEntry *ent; + ssize_t len = 0; + ssize_t max = -1; + int is_list = -1; + int64_t val; + + for (ent = qdict_first(maybe_list); ent != NULL; + ent = qdict_next(maybe_list, ent)) { + + if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { + if (is_list == -1) { + is_list = 1; + } else if (!is_list) { + error_setg(errp, + "Cannot mix list and non-list keys"); + return -1; + } + len++; + if (val > max) { + max = val; + } + } else { + if (is_list == -1) { + is_list = 0; + } else if (is_list) { + error_setg(errp, + "Cannot mix list and non-list keys"); + return -1; + } + } + } + + if (is_list == -1) { + assert(!qdict_size(maybe_list)); + is_list = 0; + } + + /* NB this isn't a perfect check - e.g. it won't catch + * a list containing '1', '+1', '01', '3', but that + * does not matter - we've still proved that the + * input is a list. It is up the caller to do a + * stricter check if desired */ + if (len != (max + 1)) { + error_setg(errp, "List indices are not contiguous, " + "saw %zd elements but %zd largest index", + len, max); + return -1; + } + + return is_list; +} + +/** + * qdict_crumple: + * @src: the original flat dictionary (only scalar values) to crumple + * + * Takes a flat dictionary whose keys use '.' separator to indicate + * nesting, and values are scalars, and crumples it into a nested + * structure. + * + * To include a literal '.' in a key name, it must be escaped as '..' + * + * For example, an input of: + * + * { 'foo.0.bar': 'one', 'foo.0.wizz': '1', + * 'foo.1.bar': 'two', 'foo.1.wizz': '2' } + * + * will result in an output of: + * + * { + * 'foo': [ + * { 'bar': 'one', 'wizz': '1' }, + * { 'bar': 'two', 'wizz': '2' } + * ], + * } + * + * The following scenarios in the input dict will result in an + * error being returned: + * + * - Any values in @src are non-scalar types + * - If keys in @src imply that a particular level is both a + * list and a dict. e.g., "foo.0.bar" and "foo.eek.bar". + * - If keys in @src imply that a particular level is a list, + * but the indices are non-contiguous. e.g. "foo.0.bar" and + * "foo.2.bar" without any "foo.1.bar" present. + * - If keys in @src represent list indexes, but are not in + * the "%zu" format. e.g. "foo.+0.bar" + * + * Returns: either a QDict or QList for the nested data structure, or NULL + * on error + */ +QObject *qdict_crumple(const QDict *src, Error **errp) +{ + const QDictEntry *ent; + QDict *two_level, *multi_level = NULL; + QObject *dst = NULL, *child; + size_t i; + char *prefix = NULL; + const char *suffix = NULL; + int is_list; + + two_level = qdict_new(); + + /* Step 1: split our totally flat dict into a two level dict */ + for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { + if (qobject_type(ent->value) == QTYPE_QDICT || + qobject_type(ent->value) == QTYPE_QLIST) { + error_setg(errp, "Value %s is not a scalar", + ent->key); + goto error; + } + + qdict_split_flat_key(ent->key, &prefix, &suffix); + + child = qdict_get(two_level, prefix); + if (suffix) { + QDict *child_dict = qobject_to(QDict, child); + if (!child_dict) { + if (child) { + error_setg(errp, "Key %s prefix is already set as a scalar", + prefix); + goto error; + } + + child_dict = qdict_new(); + qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); + } + + qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); + } else { + if (child) { + error_setg(errp, "Key %s prefix is already set as a dict", + prefix); + goto error; + } + qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); + } + + g_free(prefix); + prefix = NULL; + } + + /* Step 2: optionally process the two level dict recursively + * into a multi-level dict */ + multi_level = qdict_new(); + for (ent = qdict_first(two_level); ent != NULL; + ent = qdict_next(two_level, ent)) { + QDict *dict = qobject_to(QDict, ent->value); + if (dict) { + child = qdict_crumple(dict, errp); + if (!child) { + goto error; + } + + qdict_put_obj(multi_level, ent->key, child); + } else { + qdict_put_obj(multi_level, ent->key, qobject_ref(ent->value)); + } + } + qobject_unref(two_level); + two_level = NULL; + + /* Step 3: detect if we need to turn our dict into list */ + is_list = qdict_is_list(multi_level, errp); + if (is_list < 0) { + goto error; + } + + if (is_list) { + dst = QOBJECT(qlist_new()); + + for (i = 0; i < qdict_size(multi_level); i++) { + char *key = g_strdup_printf("%zu", i); + + child = qdict_get(multi_level, key); + g_free(key); + + if (!child) { + error_setg(errp, "Missing list index %zu", i); + goto error; + } + + qlist_append_obj(qobject_to(QList, dst), qobject_ref(child)); + } + qobject_unref(multi_level); + multi_level = NULL; + } else { + dst = QOBJECT(multi_level); + } + + return dst; + + error: + g_free(prefix); + qobject_unref(multi_level); + qobject_unref(two_level); + qobject_unref(dst); + return NULL; +} + +/** + * 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; + char *prefix = g_strdup_printf("%s%u.", subqdict, i); + + subqdict_entries = qdict_count_prefixed_entries(src, prefix); + + /* Remove ending "." */ + prefix[strlen(prefix) - 1] = 0; + subqobj = qdict_get(src, prefix); + + g_free(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. + * + * If an element from src has a key already present in dest, it will not be + * moved unless overwrite is true. + * + * If overwrite is true, the conflicting values in dest will be discarded and + * replaced by the corresponding values from src. + * + * Therefore, with overwrite being true, the src QDict will always be empty when + * this function returns. If overwrite is false, the src QDict will be empty + * iff there were no conflicts. + */ +void qdict_join(QDict *dest, QDict *src, bool overwrite) +{ + const QDictEntry *entry, *next; + + entry = qdict_first(src); + while (entry) { + next = qdict_next(src, entry); + + if (overwrite || !qdict_haskey(dest, entry->key)) { + qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); + qdict_del(src, entry->key); + } + + entry = next; + } +} + +/** + * qdict_rename_keys(): Rename keys in qdict according to the replacements + * specified in the array renames. The array must be terminated by an entry + * with from = NULL. + * + * The renames are performed individually in the order of the array, so entries + * may be renamed multiple times and may or may not conflict depending on the + * order of the renames array. + * + * Returns true for success, false in error cases. + */ +bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) +{ + QObject *qobj; + + while (renames->from) { + if (qdict_haskey(qdict, renames->from)) { + if (qdict_haskey(qdict, renames->to)) { + error_setg(errp, "'%s' and its alias '%s' can't be used at the " + "same time", renames->to, renames->from); + return false; + } + + qobj = qdict_get(qdict, renames->from); + qdict_put_obj(qdict, renames->to, qobject_ref(qobj)); + qdict_del(qdict, renames->from); + } + + renames++; + } + return true; +} diff --git a/qobject/qdict.c b/qobject/qdict.c index 0554c64553..3d8c2f7bbc 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -11,17 +11,11 @@ */ #include "qemu/osdep.h" -#include "block/qdict.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qbool.h" -#include "qapi/qmp/qlist.h" #include "qapi/qmp/qnull.h" #include "qapi/qmp/qstring.h" -#include "qapi/error.h" -#include "qemu/queue.h" -#include "qemu-common.h" -#include "qemu/cutils.h" /** * qdict_new(): Create a new QDict @@ -464,626 +458,3 @@ 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) { - qdict_put_obj(dst, key, qobject_ref(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_str(dst, key, val); -} - -static void qdict_flatten_qdict(QDict *qdict, QDict *target, - const char *prefix); - -static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) -{ - QObject *value; - const QListEntry *entry; - char *new_key; - int i; - - /* This function is never called with prefix == NULL, i.e., it is always - * called from within qdict_flatten_q(list|dict)(). Therefore, it does not - * need to remove list entries during the iteration (the whole list will be - * deleted eventually anyway from qdict_flatten_qdict()). */ - assert(prefix); - - entry = qlist_first(qlist); - - for (i = 0; entry; entry = qlist_next(entry), i++) { - value = qlist_entry_obj(entry); - new_key = g_strdup_printf("%s.%i", prefix, i); - - if (qobject_type(value) == QTYPE_QDICT) { - qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); - } else if (qobject_type(value) == QTYPE_QLIST) { - qdict_flatten_qlist(qobject_to(QList, value), target, new_key); - } else { - /* All other types are moved to the target unchanged. */ - qdict_put_obj(target, new_key, qobject_ref(value)); - } - - g_free(new_key); - } -} - -static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) -{ - QObject *value; - const QDictEntry *entry, *next; - char *new_key; - bool delete; - - entry = qdict_first(qdict); - - while (entry != NULL) { - - next = qdict_next(qdict, entry); - value = qdict_entry_value(entry); - new_key = NULL; - delete = false; - - if (prefix) { - new_key = g_strdup_printf("%s.%s", prefix, entry->key); - } - - if (qobject_type(value) == QTYPE_QDICT) { - /* Entries of QDicts are processed recursively, the QDict object - * itself disappears. */ - qdict_flatten_qdict(qobject_to(QDict, value), target, - new_key ? new_key : entry->key); - delete = true; - } else if (qobject_type(value) == QTYPE_QLIST) { - qdict_flatten_qlist(qobject_to(QList, value), target, - new_key ? new_key : entry->key); - delete = true; - } else if (prefix) { - /* All other objects are moved to the target unchanged. */ - qdict_put_obj(target, new_key, qobject_ref(value)); - delete = true; - } - - g_free(new_key); - - if (delete) { - qdict_del(qdict, entry->key); - - /* Restart loop after modifying the iterated QDict */ - entry = qdict_first(qdict); - continue; - } - - entry = next; - } -} - -/** - * qdict_flatten(): For each nested QDict with key x, all fields with key y - * are moved to this QDict and their key is renamed to "x.y". For each nested - * QList with key x, the field at index y is moved to this QDict with the key - * "x.y" (i.e., the reverse of what qdict_array_split() does). - * This operation is applied recursively for nested QDicts and QLists. - */ -void qdict_flatten(QDict *qdict) -{ - qdict_flatten_qdict(qdict, qdict, NULL); -} - -/* extract all the src QDict entries starting by start into dst */ -void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) - -{ - const QDictEntry *entry, *next; - const char *p; - - *dst = qdict_new(); - entry = qdict_first(src); - - while (entry != NULL) { - next = qdict_next(src, entry); - if (strstart(entry->key, start, &p)) { - qdict_put_obj(*dst, p, qobject_ref(entry->value)); - qdict_del(src, entry->key); - } - entry = next; - } -} - -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)) { - if (count == INT_MAX) { - return -ERANGE; - } - count++; - } - } - - return count; -} - -/** - * qdict_array_split(): This function moves array-like elements of a QDict into - * a new QList. Every entry in the original QDict with a key "%u" or one - * prefixed "%u.", where %u designates an unsigned integer starting at 0 and - * incrementally counting up, will be moved to a new QDict at index %u in the - * output QList with the key prefix removed, if that prefix is "%u.". If the - * whole key is just "%u", the whole QObject will be moved unchanged without - * creating a new QDict. The function terminates when there is no entry in the - * QDict with a prefix directly (incrementally) following the last one; it also - * returns if there are both entries with "%u" and "%u." for the same index %u. - * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66} - * (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66}) - * => [{"a": 42, "b": 23}, {"x": 0}, 66] - * and {"4.y": 1, "o.o": 7} (remainder of the old QDict) - */ -void qdict_array_split(QDict *src, QList **dst) -{ - unsigned i; - - *dst = qlist_new(); - - for (i = 0; i < UINT_MAX; i++) { - QObject *subqobj; - bool is_subqdict; - QDict *subqdict; - char indexstr[32], prefix[32]; - size_t snprintf_ret; - - snprintf_ret = snprintf(indexstr, 32, "%u", i); - assert(snprintf_ret < 32); - - subqobj = qdict_get(src, indexstr); - - snprintf_ret = snprintf(prefix, 32, "%u.", i); - assert(snprintf_ret < 32); - - /* 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. - if (!subqobj == !is_subqdict) { - break; - } - - if (is_subqdict) { - qdict_extract_subqdict(src, &subqdict, prefix); - assert(qdict_size(subqdict) > 0); - } else { - qobject_ref(subqobj); - qdict_del(src, indexstr); - } - - qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); - } -} - -/** - * qdict_split_flat_key: - * @key: the key string to split - * @prefix: non-NULL pointer to hold extracted prefix - * @suffix: non-NULL pointer to remaining suffix - * - * Given a flattened key such as 'foo.0.bar', split it into two parts - * at the first '.' separator. Allows double dot ('..') to escape the - * normal separator. - * - * e.g. - * 'foo.0.bar' -> prefix='foo' and suffix='0.bar' - * 'foo..0.bar' -> prefix='foo.0' and suffix='bar' - * - * The '..' sequence will be unescaped in the returned 'prefix' - * string. The 'suffix' string will be left in escaped format, so it - * can be fed back into the qdict_split_flat_key() key as the input - * later. - * - * The caller is responsible for freeing the string returned in @prefix - * using g_free(). - */ -static void qdict_split_flat_key(const char *key, char **prefix, - const char **suffix) -{ - const char *separator; - size_t i, j; - - /* Find first '.' separator, but if there is a pair '..' - * that acts as an escape, so skip over '..' */ - separator = NULL; - do { - if (separator) { - separator += 2; - } else { - separator = key; - } - separator = strchr(separator, '.'); - } while (separator && separator[1] == '.'); - - if (separator) { - *prefix = g_strndup(key, separator - key); - *suffix = separator + 1; - } else { - *prefix = g_strdup(key); - *suffix = NULL; - } - - /* Unescape the '..' sequence into '.' */ - for (i = 0, j = 0; (*prefix)[i] != '\0'; i++, j++) { - if ((*prefix)[i] == '.') { - assert((*prefix)[i + 1] == '.'); - i++; - } - (*prefix)[j] = (*prefix)[i]; - } - (*prefix)[j] = '\0'; -} - -/** - * qdict_is_list: - * @maybe_list: dict to check if keys represent list elements. - * - * Determine whether all keys in @maybe_list are valid list elements. - * If @maybe_list is non-zero in length and all the keys look like - * valid list indexes, this will return 1. If @maybe_list is zero - * length or all keys are non-numeric then it will return 0 to indicate - * it is a normal qdict. If there is a mix of numeric and non-numeric - * keys, or the list indexes are non-contiguous, an error is reported. - * - * Returns: 1 if a valid list, 0 if a dict, -1 on error - */ -static int qdict_is_list(QDict *maybe_list, Error **errp) -{ - const QDictEntry *ent; - ssize_t len = 0; - ssize_t max = -1; - int is_list = -1; - int64_t val; - - for (ent = qdict_first(maybe_list); ent != NULL; - ent = qdict_next(maybe_list, ent)) { - - if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { - if (is_list == -1) { - is_list = 1; - } else if (!is_list) { - error_setg(errp, - "Cannot mix list and non-list keys"); - return -1; - } - len++; - if (val > max) { - max = val; - } - } else { - if (is_list == -1) { - is_list = 0; - } else if (is_list) { - error_setg(errp, - "Cannot mix list and non-list keys"); - return -1; - } - } - } - - if (is_list == -1) { - assert(!qdict_size(maybe_list)); - is_list = 0; - } - - /* NB this isn't a perfect check - e.g. it won't catch - * a list containing '1', '+1', '01', '3', but that - * does not matter - we've still proved that the - * input is a list. It is up the caller to do a - * stricter check if desired */ - if (len != (max + 1)) { - error_setg(errp, "List indices are not contiguous, " - "saw %zd elements but %zd largest index", - len, max); - return -1; - } - - return is_list; -} - -/** - * qdict_crumple: - * @src: the original flat dictionary (only scalar values) to crumple - * - * Takes a flat dictionary whose keys use '.' separator to indicate - * nesting, and values are scalars, and crumples it into a nested - * structure. - * - * To include a literal '.' in a key name, it must be escaped as '..' - * - * For example, an input of: - * - * { 'foo.0.bar': 'one', 'foo.0.wizz': '1', - * 'foo.1.bar': 'two', 'foo.1.wizz': '2' } - * - * will result in an output of: - * - * { - * 'foo': [ - * { 'bar': 'one', 'wizz': '1' }, - * { 'bar': 'two', 'wizz': '2' } - * ], - * } - * - * The following scenarios in the input dict will result in an - * error being returned: - * - * - Any values in @src are non-scalar types - * - If keys in @src imply that a particular level is both a - * list and a dict. e.g., "foo.0.bar" and "foo.eek.bar". - * - If keys in @src imply that a particular level is a list, - * but the indices are non-contiguous. e.g. "foo.0.bar" and - * "foo.2.bar" without any "foo.1.bar" present. - * - If keys in @src represent list indexes, but are not in - * the "%zu" format. e.g. "foo.+0.bar" - * - * Returns: either a QDict or QList for the nested data structure, or NULL - * on error - */ -QObject *qdict_crumple(const QDict *src, Error **errp) -{ - const QDictEntry *ent; - QDict *two_level, *multi_level = NULL; - QObject *dst = NULL, *child; - size_t i; - char *prefix = NULL; - const char *suffix = NULL; - int is_list; - - two_level = qdict_new(); - - /* Step 1: split our totally flat dict into a two level dict */ - for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { - if (qobject_type(ent->value) == QTYPE_QDICT || - qobject_type(ent->value) == QTYPE_QLIST) { - error_setg(errp, "Value %s is not a scalar", - ent->key); - goto error; - } - - qdict_split_flat_key(ent->key, &prefix, &suffix); - - child = qdict_get(two_level, prefix); - if (suffix) { - QDict *child_dict = qobject_to(QDict, child); - if (!child_dict) { - if (child) { - error_setg(errp, "Key %s prefix is already set as a scalar", - prefix); - goto error; - } - - child_dict = qdict_new(); - qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); - } - - qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); - } else { - if (child) { - error_setg(errp, "Key %s prefix is already set as a dict", - prefix); - goto error; - } - qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); - } - - g_free(prefix); - prefix = NULL; - } - - /* Step 2: optionally process the two level dict recursively - * into a multi-level dict */ - multi_level = qdict_new(); - for (ent = qdict_first(two_level); ent != NULL; - ent = qdict_next(two_level, ent)) { - QDict *dict = qobject_to(QDict, ent->value); - if (dict) { - child = qdict_crumple(dict, errp); - if (!child) { - goto error; - } - - qdict_put_obj(multi_level, ent->key, child); - } else { - qdict_put_obj(multi_level, ent->key, qobject_ref(ent->value)); - } - } - qobject_unref(two_level); - two_level = NULL; - - /* Step 3: detect if we need to turn our dict into list */ - is_list = qdict_is_list(multi_level, errp); - if (is_list < 0) { - goto error; - } - - if (is_list) { - dst = QOBJECT(qlist_new()); - - for (i = 0; i < qdict_size(multi_level); i++) { - char *key = g_strdup_printf("%zu", i); - - child = qdict_get(multi_level, key); - g_free(key); - - if (!child) { - error_setg(errp, "Missing list index %zu", i); - goto error; - } - - qlist_append_obj(qobject_to(QList, dst), qobject_ref(child)); - } - qobject_unref(multi_level); - multi_level = NULL; - } else { - dst = QOBJECT(multi_level); - } - - return dst; - - error: - g_free(prefix); - qobject_unref(multi_level); - qobject_unref(two_level); - qobject_unref(dst); - return NULL; -} - -/** - * 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; - char *prefix = g_strdup_printf("%s%u.", subqdict, i); - - subqdict_entries = qdict_count_prefixed_entries(src, prefix); - - /* Remove ending "." */ - prefix[strlen(prefix) - 1] = 0; - subqobj = qdict_get(src, prefix); - - g_free(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. - * - * If an element from src has a key already present in dest, it will not be - * moved unless overwrite is true. - * - * If overwrite is true, the conflicting values in dest will be discarded and - * replaced by the corresponding values from src. - * - * Therefore, with overwrite being true, the src QDict will always be empty when - * this function returns. If overwrite is false, the src QDict will be empty - * iff there were no conflicts. - */ -void qdict_join(QDict *dest, QDict *src, bool overwrite) -{ - const QDictEntry *entry, *next; - - entry = qdict_first(src); - while (entry) { - next = qdict_next(src, entry); - - if (overwrite || !qdict_haskey(dest, entry->key)) { - qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); - qdict_del(src, entry->key); - } - - entry = next; - } -} - -/** - * qdict_rename_keys(): Rename keys in qdict according to the replacements - * specified in the array renames. The array must be terminated by an entry - * with from = NULL. - * - * The renames are performed individually in the order of the array, so entries - * may be renamed multiple times and may or may not conflict depending on the - * order of the renames array. - * - * Returns true for success, false in error cases. - */ -bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) -{ - QObject *qobj; - - while (renames->from) { - if (qdict_haskey(qdict, renames->from)) { - if (qdict_haskey(qdict, renames->to)) { - error_setg(errp, "'%s' and its alias '%s' can't be used at the " - "same time", renames->to, renames->from); - return false; - } - - qobj = qdict_get(qdict, renames->from); - qdict_put_obj(qdict, renames->to, qobject_ref(qobj)); - qdict_del(qdict, renames->from); - } - - renames++; - } - return true; -} diff --git a/tests/Makefile.include b/tests/Makefile.include index 607afe5bed..ca91da26cb 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -39,6 +39,8 @@ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ check-unit-y = tests/check-qdict$(EXESUF) gcov-files-check-qdict-y = qobject/qdict.c +check-unit-y = tests/check-block-qdict$(EXESUF) +gcov-files-check-block-qdict-y = qobject/block-qdict.c check-unit-y += tests/test-char$(EXESUF) gcov-files-check-qdict-y = chardev/char.c check-unit-y += tests/check-qnum$(EXESUF) @@ -584,6 +586,7 @@ GENERATED_FILES += tests/test-qapi-types.h tests/test-qapi-visit.h \ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ tests/check-qlist.o tests/check-qnull.o tests/check-qobject.o \ tests/check-qjson.o tests/check-qlit.o \ + tests/check-block-qtest.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \ tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \ tests/test-clone-visitor.o \ @@ -614,6 +617,7 @@ test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y) tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y) tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y) +tests/check-block-qdict$(EXESUF): tests/check-block-qdict.o $(test-util-obj-y) tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y) tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y) tests/check-qobject$(EXESUF): tests/check-qobject.o $(test-util-obj-y) diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c new file mode 100644 index 0000000000..5b9f4d506e --- /dev/null +++ b/tests/check-block-qdict.c @@ -0,0 +1,655 @@ +/* + * Unit-tests for Block layer QDict extras + * + * Copyright (c) 2013-2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "block/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnum.h" +#include "qapi/error.h" + +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"); + + qobject_unref(copy); + qobject_unref(dict); +} + +static void qdict_flatten_test(void) +{ + QList *list1 = qlist_new(); + QList *list2 = qlist_new(); + QDict *dict1 = qdict_new(); + QDict *dict2 = qdict_new(); + QDict *dict3 = qdict_new(); + + /* + * Test the flattening of + * + * { + * "e": [ + * 42, + * [ + * 23, + * 66, + * { + * "a": 0, + * "b": 1 + * } + * ] + * ], + * "f": { + * "c": 2, + * "d": 3, + * }, + * "g": 4 + * } + * + * to + * + * { + * "e.0": 42, + * "e.1.0": 23, + * "e.1.1": 66, + * "e.1.2.a": 0, + * "e.1.2.b": 1, + * "f.c": 2, + * "f.d": 3, + * "g": 4 + * } + */ + + qdict_put_int(dict1, "a", 0); + qdict_put_int(dict1, "b", 1); + + qlist_append_int(list1, 23); + qlist_append_int(list1, 66); + qlist_append(list1, dict1); + qlist_append_int(list2, 42); + qlist_append(list2, list1); + + qdict_put_int(dict2, "c", 2); + qdict_put_int(dict2, "d", 3); + qdict_put(dict3, "e", list2); + qdict_put(dict3, "f", dict2); + qdict_put_int(dict3, "g", 4); + + qdict_flatten(dict3); + + g_assert(qdict_get_int(dict3, "e.0") == 42); + g_assert(qdict_get_int(dict3, "e.1.0") == 23); + g_assert(qdict_get_int(dict3, "e.1.1") == 66); + g_assert(qdict_get_int(dict3, "e.1.2.a") == 0); + g_assert(qdict_get_int(dict3, "e.1.2.b") == 1); + g_assert(qdict_get_int(dict3, "f.c") == 2); + g_assert(qdict_get_int(dict3, "f.d") == 3); + g_assert(qdict_get_int(dict3, "g") == 4); + + g_assert(qdict_size(dict3) == 8); + + qobject_unref(dict3); +} + +static void qdict_array_split_test(void) +{ + QDict *test_dict = qdict_new(); + QDict *dict1, *dict2; + QNum *int1; + QList *test_list; + + /* + * Test the split of + * + * { + * "1.x": 0, + * "4.y": 1, + * "0.a": 42, + * "o.o": 7, + * "0.b": 23, + * "2": 66 + * } + * + * to + * + * [ + * { + * "a": 42, + * "b": 23 + * }, + * { + * "x": 0 + * }, + * 66 + * ] + * + * and + * + * { + * "4.y": 1, + * "o.o": 7 + * } + * + * (remaining in the old QDict) + * + * This example is given in the comment of qdict_array_split(). + */ + + qdict_put_int(test_dict, "1.x", 0); + qdict_put_int(test_dict, "4.y", 1); + qdict_put_int(test_dict, "0.a", 42); + qdict_put_int(test_dict, "o.o", 7); + qdict_put_int(test_dict, "0.b", 23); + qdict_put_int(test_dict, "2", 66); + + qdict_array_split(test_dict, &test_list); + + dict1 = qobject_to(QDict, qlist_pop(test_list)); + dict2 = qobject_to(QDict, qlist_pop(test_list)); + int1 = qobject_to(QNum, qlist_pop(test_list)); + + g_assert(dict1); + g_assert(dict2); + g_assert(int1); + g_assert(qlist_empty(test_list)); + + qobject_unref(test_list); + + g_assert(qdict_get_int(dict1, "a") == 42); + g_assert(qdict_get_int(dict1, "b") == 23); + + g_assert(qdict_size(dict1) == 2); + + qobject_unref(dict1); + + g_assert(qdict_get_int(dict2, "x") == 0); + + g_assert(qdict_size(dict2) == 1); + + qobject_unref(dict2); + + g_assert_cmpint(qnum_get_int(int1), ==, 66); + + qobject_unref(int1); + + g_assert(qdict_get_int(test_dict, "4.y") == 1); + g_assert(qdict_get_int(test_dict, "o.o") == 7); + + g_assert(qdict_size(test_dict) == 2); + + qobject_unref(test_dict); + + /* + * Test the split of + * + * { + * "0": 42, + * "1": 23, + * "1.x": 84 + * } + * + * to + * + * [ + * 42 + * ] + * + * and + * + * { + * "1": 23, + * "1.x": 84 + * } + * + * That is, test whether splitting stops if there is both an entry with key + * of "%u" and other entries with keys prefixed "%u." for the same index. + */ + + test_dict = qdict_new(); + + qdict_put_int(test_dict, "0", 42); + qdict_put_int(test_dict, "1", 23); + qdict_put_int(test_dict, "1.x", 84); + + qdict_array_split(test_dict, &test_list); + + int1 = qobject_to(QNum, qlist_pop(test_list)); + + g_assert(int1); + g_assert(qlist_empty(test_list)); + + qobject_unref(test_list); + + g_assert_cmpint(qnum_get_int(int1), ==, 42); + + qobject_unref(int1); + + g_assert(qdict_get_int(test_dict, "1") == 23); + g_assert(qdict_get_int(test_dict, "1.x") == 84); + + g_assert(qdict_size(test_dict) == 2); + + qobject_unref(test_dict); +} + +static void qdict_array_entries_test(void) +{ + QDict *dict = qdict_new(); + + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); + + qdict_put_int(dict, "bar", 0); + qdict_put_int(dict, "baz.0", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); + + qdict_put_int(dict, "foo.1", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); + qdict_put_int(dict, "foo.0", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); + qdict_put_int(dict, "foo.bar", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); + qdict_del(dict, "foo.bar"); + + qdict_put_int(dict, "foo.2.a", 0); + qdict_put_int(dict, "foo.2.b", 0); + qdict_put_int(dict, "foo.2.c", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + + qobject_unref(dict); + + dict = qdict_new(); + qdict_put_int(dict, "1", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + qdict_put_int(dict, "0", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); + qdict_put_int(dict, "bar", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + qdict_del(dict, "bar"); + + qdict_put_int(dict, "2.a", 0); + qdict_put_int(dict, "2.b", 0); + qdict_put_int(dict, "2.c", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); + + qobject_unref(dict); +} + +static void qdict_join_test(void) +{ + QDict *dict1, *dict2; + bool overwrite = false; + int i; + + dict1 = qdict_new(); + dict2 = qdict_new(); + + /* Test everything once without overwrite and once with */ + do { + /* Test empty dicts */ + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 0); + g_assert(qdict_size(dict2) == 0); + + /* First iteration: Test movement */ + /* Second iteration: Test empty source and non-empty destination */ + qdict_put_int(dict2, "foo", 42); + + for (i = 0; i < 2; i++) { + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 1); + g_assert(qdict_size(dict2) == 0); + + g_assert(qdict_get_int(dict1, "foo") == 42); + } + + /* Test non-empty source and destination without conflict */ + qdict_put_int(dict2, "bar", 23); + + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 2); + g_assert(qdict_size(dict2) == 0); + + g_assert(qdict_get_int(dict1, "foo") == 42); + g_assert(qdict_get_int(dict1, "bar") == 23); + + /* Test conflict */ + qdict_put_int(dict2, "foo", 84); + + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 2); + g_assert(qdict_size(dict2) == !overwrite); + + g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42)); + g_assert(qdict_get_int(dict1, "bar") == 23); + + if (!overwrite) { + g_assert(qdict_get_int(dict2, "foo") == 84); + } + + /* Check the references */ + g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); + g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); + + if (!overwrite) { + g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); + } + + /* Clean up */ + qdict_del(dict1, "foo"); + qdict_del(dict1, "bar"); + + if (!overwrite) { + qdict_del(dict2, "foo"); + } + } while (overwrite ^= true); + + qobject_unref(dict1); + qobject_unref(dict2); +} + +static void qdict_crumple_test_recursive(void) +{ + QDict *src, *dst, *rule, *vnc, *acl, *listen; + QList *rules; + + src = qdict_new(); + qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); + qdict_put_str(src, "vnc.listen.port", "5901"); + qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); + qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); + qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); + qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); + qdict_put_str(src, "vnc.acl.default", "deny"); + qdict_put_str(src, "vnc.acl..name", "acl0"); + qdict_put_str(src, "vnc.acl.rule..name", "acl0"); + + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); + g_assert(dst); + g_assert_cmpint(qdict_size(dst), ==, 1); + + vnc = qdict_get_qdict(dst, "vnc"); + g_assert(vnc); + g_assert_cmpint(qdict_size(vnc), ==, 3); + + listen = qdict_get_qdict(vnc, "listen"); + g_assert(listen); + g_assert_cmpint(qdict_size(listen), ==, 2); + g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); + g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); + + acl = qdict_get_qdict(vnc, "acl"); + g_assert(acl); + g_assert_cmpint(qdict_size(acl), ==, 3); + + rules = qdict_get_qlist(acl, "rules"); + g_assert(rules); + g_assert_cmpint(qlist_size(rules), ==, 2); + + rule = qobject_to(QDict, qlist_pop(rules)); + g_assert(rule); + g_assert_cmpint(qdict_size(rule), ==, 2); + g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); + g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); + qobject_unref(rule); + + rule = qobject_to(QDict, qlist_pop(rules)); + g_assert(rule); + g_assert_cmpint(qdict_size(rule), ==, 2); + g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); + g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); + qobject_unref(rule); + + /* With recursive crumpling, we should see all names unescaped */ + g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); + g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); + + qobject_unref(src); + qobject_unref(dst); +} + +static void qdict_crumple_test_empty(void) +{ + QDict *src, *dst; + + src = qdict_new(); + + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); + + g_assert_cmpint(qdict_size(dst), ==, 0); + + qobject_unref(src); + qobject_unref(dst); +} + +static int qdict_count_entries(QDict *dict) +{ + const QDictEntry *e; + int count = 0; + + for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { + count++; + } + + return count; +} + +static void qdict_rename_keys_test(void) +{ + QDict *dict = qdict_new(); + QDict *copy; + QDictRenames *renames; + Error *local_err = NULL; + + qdict_put_str(dict, "abc", "foo"); + qdict_put_str(dict, "abcdef", "bar"); + qdict_put_int(dict, "number", 42); + qdict_put_bool(dict, "flag", true); + qdict_put_null(dict, "nothing"); + + /* Empty rename list */ + renames = (QDictRenames[]) { + { NULL, "this can be anything" } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Simple rename of all entries */ + renames = (QDictRenames[]) { + { "abc", "str1" }, + { "abcdef", "str2" }, + { "number", "int" }, + { "flag", "bool" }, + { "nothing", "null" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert(!qdict_haskey(copy, "abc")); + g_assert(!qdict_haskey(copy, "abcdef")); + g_assert(!qdict_haskey(copy, "number")); + g_assert(!qdict_haskey(copy, "flag")); + g_assert(!qdict_haskey(copy, "nothing")); + + g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); + g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Renames are processed top to bottom */ + renames = (QDictRenames[]) { + { "abc", "tmp" }, + { "abcdef", "abc" }, + { "number", "abcdef" }, + { "flag", "number" }, + { "nothing", "flag" }, + { "tmp", "nothing" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); + g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); + g_assert(!qdict_haskey(copy, "tmp")); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Conflicting rename */ + renames = (QDictRenames[]) { + { "abcdef", "abc" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &local_err); + + g_assert(local_err != NULL); + error_free(local_err); + local_err = NULL; + + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Renames in an empty dict */ + renames = (QDictRenames[]) { + { "abcdef", "abc" }, + { NULL , NULL } + }; + + qobject_unref(dict); + dict = qdict_new(); + + qdict_rename_keys(dict, renames, &error_abort); + g_assert(qdict_first(dict) == NULL); + + qobject_unref(dict); +} + +static void qdict_crumple_test_bad_inputs(void) +{ + QDict *src; + Error *error = NULL; + + src = qdict_new(); + /* rule.0 can't be both a string and a dict */ + qdict_put_str(src, "rule.0", "fred"); + qdict_put_str(src, "rule.0.policy", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + qobject_unref(src); + + src = qdict_new(); + /* rule can't be both a list and a dict */ + qdict_put_str(src, "rule.0", "fred"); + qdict_put_str(src, "rule.a", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + qobject_unref(src); + + src = qdict_new(); + /* The input should be flat, ie no dicts or lists */ + qdict_put(src, "rule.a", qdict_new()); + qdict_put_str(src, "rule.b", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + qobject_unref(src); + + src = qdict_new(); + /* List indexes must not have gaps */ + qdict_put_str(src, "rule.0", "deny"); + qdict_put_str(src, "rule.3", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + qobject_unref(src); + + src = qdict_new(); + /* List indexes must be in %zu format */ + qdict_put_str(src, "rule.0", "deny"); + qdict_put_str(src, "rule.+1", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + qobject_unref(src); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/defaults", qdict_defaults_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("/public/crumple/recursive", + qdict_crumple_test_recursive); + g_test_add_func("/public/crumple/empty", + qdict_crumple_test_empty); + g_test_add_func("/public/crumple/bad_inputs", + qdict_crumple_test_bad_inputs); + + g_test_add_func("/public/rename_keys", qdict_rename_keys_test); + + return g_test_run(); +} diff --git a/tests/check-qdict.c b/tests/check-qdict.c index 93e2112b6d..86e9fe7dc4 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -11,13 +11,7 @@ */ #include "qemu/osdep.h" -#include "block/qdict.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" -#include "qapi/error.h" -#include "qemu-common.h" /* * Public Interface test-cases @@ -157,28 +151,6 @@ static void qdict_get_try_str_test(void) qobject_unref(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"); - - qobject_unref(copy); - qobject_unref(dict); -} - static void qdict_haskey_not_test(void) { QDict *tests_dict = qdict_new(); @@ -254,606 +226,6 @@ static void qdict_iterapi_test(void) qobject_unref(tests_dict); } -static void qdict_flatten_test(void) -{ - QList *list1 = qlist_new(); - QList *list2 = qlist_new(); - QDict *dict1 = qdict_new(); - QDict *dict2 = qdict_new(); - QDict *dict3 = qdict_new(); - - /* - * Test the flattening of - * - * { - * "e": [ - * 42, - * [ - * 23, - * 66, - * { - * "a": 0, - * "b": 1 - * } - * ] - * ], - * "f": { - * "c": 2, - * "d": 3, - * }, - * "g": 4 - * } - * - * to - * - * { - * "e.0": 42, - * "e.1.0": 23, - * "e.1.1": 66, - * "e.1.2.a": 0, - * "e.1.2.b": 1, - * "f.c": 2, - * "f.d": 3, - * "g": 4 - * } - */ - - qdict_put_int(dict1, "a", 0); - qdict_put_int(dict1, "b", 1); - - qlist_append_int(list1, 23); - qlist_append_int(list1, 66); - qlist_append(list1, dict1); - qlist_append_int(list2, 42); - qlist_append(list2, list1); - - qdict_put_int(dict2, "c", 2); - qdict_put_int(dict2, "d", 3); - qdict_put(dict3, "e", list2); - qdict_put(dict3, "f", dict2); - qdict_put_int(dict3, "g", 4); - - qdict_flatten(dict3); - - g_assert(qdict_get_int(dict3, "e.0") == 42); - g_assert(qdict_get_int(dict3, "e.1.0") == 23); - g_assert(qdict_get_int(dict3, "e.1.1") == 66); - g_assert(qdict_get_int(dict3, "e.1.2.a") == 0); - g_assert(qdict_get_int(dict3, "e.1.2.b") == 1); - g_assert(qdict_get_int(dict3, "f.c") == 2); - g_assert(qdict_get_int(dict3, "f.d") == 3); - g_assert(qdict_get_int(dict3, "g") == 4); - - g_assert(qdict_size(dict3) == 8); - - qobject_unref(dict3); -} - -static void qdict_array_split_test(void) -{ - QDict *test_dict = qdict_new(); - QDict *dict1, *dict2; - QNum *int1; - QList *test_list; - - /* - * Test the split of - * - * { - * "1.x": 0, - * "4.y": 1, - * "0.a": 42, - * "o.o": 7, - * "0.b": 23, - * "2": 66 - * } - * - * to - * - * [ - * { - * "a": 42, - * "b": 23 - * }, - * { - * "x": 0 - * }, - * 66 - * ] - * - * and - * - * { - * "4.y": 1, - * "o.o": 7 - * } - * - * (remaining in the old QDict) - * - * This example is given in the comment of qdict_array_split(). - */ - - qdict_put_int(test_dict, "1.x", 0); - qdict_put_int(test_dict, "4.y", 1); - qdict_put_int(test_dict, "0.a", 42); - qdict_put_int(test_dict, "o.o", 7); - qdict_put_int(test_dict, "0.b", 23); - qdict_put_int(test_dict, "2", 66); - - qdict_array_split(test_dict, &test_list); - - dict1 = qobject_to(QDict, qlist_pop(test_list)); - dict2 = qobject_to(QDict, qlist_pop(test_list)); - int1 = qobject_to(QNum, qlist_pop(test_list)); - - g_assert(dict1); - g_assert(dict2); - g_assert(int1); - g_assert(qlist_empty(test_list)); - - qobject_unref(test_list); - - g_assert(qdict_get_int(dict1, "a") == 42); - g_assert(qdict_get_int(dict1, "b") == 23); - - g_assert(qdict_size(dict1) == 2); - - qobject_unref(dict1); - - g_assert(qdict_get_int(dict2, "x") == 0); - - g_assert(qdict_size(dict2) == 1); - - qobject_unref(dict2); - - g_assert_cmpint(qnum_get_int(int1), ==, 66); - - qobject_unref(int1); - - g_assert(qdict_get_int(test_dict, "4.y") == 1); - g_assert(qdict_get_int(test_dict, "o.o") == 7); - - g_assert(qdict_size(test_dict) == 2); - - qobject_unref(test_dict); - - /* - * Test the split of - * - * { - * "0": 42, - * "1": 23, - * "1.x": 84 - * } - * - * to - * - * [ - * 42 - * ] - * - * and - * - * { - * "1": 23, - * "1.x": 84 - * } - * - * That is, test whether splitting stops if there is both an entry with key - * of "%u" and other entries with keys prefixed "%u." for the same index. - */ - - test_dict = qdict_new(); - - qdict_put_int(test_dict, "0", 42); - qdict_put_int(test_dict, "1", 23); - qdict_put_int(test_dict, "1.x", 84); - - qdict_array_split(test_dict, &test_list); - - int1 = qobject_to(QNum, qlist_pop(test_list)); - - g_assert(int1); - g_assert(qlist_empty(test_list)); - - qobject_unref(test_list); - - g_assert_cmpint(qnum_get_int(int1), ==, 42); - - qobject_unref(int1); - - g_assert(qdict_get_int(test_dict, "1") == 23); - g_assert(qdict_get_int(test_dict, "1.x") == 84); - - g_assert(qdict_size(test_dict) == 2); - - qobject_unref(test_dict); -} - -static void qdict_array_entries_test(void) -{ - QDict *dict = qdict_new(); - - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); - - qdict_put_int(dict, "bar", 0); - qdict_put_int(dict, "baz.0", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); - - qdict_put_int(dict, "foo.1", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); - qdict_put_int(dict, "foo.0", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); - qdict_put_int(dict, "foo.bar", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); - qdict_del(dict, "foo.bar"); - - qdict_put_int(dict, "foo.2.a", 0); - qdict_put_int(dict, "foo.2.b", 0); - qdict_put_int(dict, "foo.2.c", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); - - qobject_unref(dict); - - dict = qdict_new(); - qdict_put_int(dict, "1", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); - qdict_put_int(dict, "0", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); - qdict_put_int(dict, "bar", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); - qdict_del(dict, "bar"); - - qdict_put_int(dict, "2.a", 0); - qdict_put_int(dict, "2.b", 0); - qdict_put_int(dict, "2.c", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); - - qobject_unref(dict); -} - -static void qdict_join_test(void) -{ - QDict *dict1, *dict2; - bool overwrite = false; - int i; - - dict1 = qdict_new(); - dict2 = qdict_new(); - - /* Test everything once without overwrite and once with */ - do - { - /* Test empty dicts */ - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 0); - g_assert(qdict_size(dict2) == 0); - - /* First iteration: Test movement */ - /* Second iteration: Test empty source and non-empty destination */ - qdict_put_int(dict2, "foo", 42); - - for (i = 0; i < 2; i++) { - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 1); - g_assert(qdict_size(dict2) == 0); - - g_assert(qdict_get_int(dict1, "foo") == 42); - } - - /* Test non-empty source and destination without conflict */ - qdict_put_int(dict2, "bar", 23); - - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 2); - g_assert(qdict_size(dict2) == 0); - - g_assert(qdict_get_int(dict1, "foo") == 42); - g_assert(qdict_get_int(dict1, "bar") == 23); - - /* Test conflict */ - qdict_put_int(dict2, "foo", 84); - - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 2); - g_assert(qdict_size(dict2) == !overwrite); - - g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42)); - g_assert(qdict_get_int(dict1, "bar") == 23); - - if (!overwrite) { - g_assert(qdict_get_int(dict2, "foo") == 84); - } - - /* Check the references */ - g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); - g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); - - if (!overwrite) { - g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); - } - - /* Clean up */ - qdict_del(dict1, "foo"); - qdict_del(dict1, "bar"); - - if (!overwrite) { - qdict_del(dict2, "foo"); - } - } - while (overwrite ^= true); - - qobject_unref(dict1); - qobject_unref(dict2); -} - -static void qdict_crumple_test_recursive(void) -{ - QDict *src, *dst, *rule, *vnc, *acl, *listen; - QList *rules; - - src = qdict_new(); - qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); - qdict_put_str(src, "vnc.listen.port", "5901"); - qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); - qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); - qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); - qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); - qdict_put_str(src, "vnc.acl.default", "deny"); - qdict_put_str(src, "vnc.acl..name", "acl0"); - qdict_put_str(src, "vnc.acl.rule..name", "acl0"); - - dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); - g_assert(dst); - g_assert_cmpint(qdict_size(dst), ==, 1); - - vnc = qdict_get_qdict(dst, "vnc"); - g_assert(vnc); - g_assert_cmpint(qdict_size(vnc), ==, 3); - - listen = qdict_get_qdict(vnc, "listen"); - g_assert(listen); - g_assert_cmpint(qdict_size(listen), ==, 2); - g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); - g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); - - acl = qdict_get_qdict(vnc, "acl"); - g_assert(acl); - g_assert_cmpint(qdict_size(acl), ==, 3); - - rules = qdict_get_qlist(acl, "rules"); - g_assert(rules); - g_assert_cmpint(qlist_size(rules), ==, 2); - - rule = qobject_to(QDict, qlist_pop(rules)); - g_assert(rule); - g_assert_cmpint(qdict_size(rule), ==, 2); - g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); - g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); - qobject_unref(rule); - - rule = qobject_to(QDict, qlist_pop(rules)); - g_assert(rule); - g_assert_cmpint(qdict_size(rule), ==, 2); - g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); - g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); - qobject_unref(rule); - - /* With recursive crumpling, we should see all names unescaped */ - g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); - g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); - - qobject_unref(src); - qobject_unref(dst); -} - -static void qdict_crumple_test_empty(void) -{ - QDict *src, *dst; - - src = qdict_new(); - - dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); - - g_assert_cmpint(qdict_size(dst), ==, 0); - - qobject_unref(src); - qobject_unref(dst); -} - -static int qdict_count_entries(QDict *dict) -{ - const QDictEntry *e; - int count = 0; - - for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { - count++; - } - - return count; -} - -static void qdict_rename_keys_test(void) -{ - QDict *dict = qdict_new(); - QDict *copy; - QDictRenames *renames; - Error *local_err = NULL; - - qdict_put_str(dict, "abc", "foo"); - qdict_put_str(dict, "abcdef", "bar"); - qdict_put_int(dict, "number", 42); - qdict_put_bool(dict, "flag", true); - qdict_put_null(dict, "nothing"); - - /* Empty rename list */ - renames = (QDictRenames[]) { - { NULL, "this can be anything" } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &error_abort); - - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); - g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - qobject_unref(copy); - - /* Simple rename of all entries */ - renames = (QDictRenames[]) { - { "abc", "str1" }, - { "abcdef", "str2" }, - { "number", "int" }, - { "flag", "bool" }, - { "nothing", "null" }, - { NULL , NULL } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &error_abort); - - g_assert(!qdict_haskey(copy, "abc")); - g_assert(!qdict_haskey(copy, "abcdef")); - g_assert(!qdict_haskey(copy, "number")); - g_assert(!qdict_haskey(copy, "flag")); - g_assert(!qdict_haskey(copy, "nothing")); - - g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); - g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - qobject_unref(copy); - - /* Renames are processed top to bottom */ - renames = (QDictRenames[]) { - { "abc", "tmp" }, - { "abcdef", "abc" }, - { "number", "abcdef" }, - { "flag", "number" }, - { "nothing", "flag" }, - { "tmp", "nothing" }, - { NULL , NULL } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &error_abort); - - g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); - g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); - g_assert(!qdict_haskey(copy, "tmp")); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - qobject_unref(copy); - - /* Conflicting rename */ - renames = (QDictRenames[]) { - { "abcdef", "abc" }, - { NULL , NULL } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &local_err); - - g_assert(local_err != NULL); - error_free(local_err); - local_err = NULL; - - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); - g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - qobject_unref(copy); - - /* Renames in an empty dict */ - renames = (QDictRenames[]) { - { "abcdef", "abc" }, - { NULL , NULL } - }; - - qobject_unref(dict); - dict = qdict_new(); - - qdict_rename_keys(dict, renames, &error_abort); - g_assert(qdict_first(dict) == NULL); - - qobject_unref(dict); -} - -static void qdict_crumple_test_bad_inputs(void) -{ - QDict *src; - Error *error = NULL; - - src = qdict_new(); - /* rule.0 can't be both a string and a dict */ - qdict_put_str(src, "rule.0", "fred"); - qdict_put_str(src, "rule.0.policy", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - g_assert(error != NULL); - error_free(error); - error = NULL; - qobject_unref(src); - - src = qdict_new(); - /* rule can't be both a list and a dict */ - qdict_put_str(src, "rule.0", "fred"); - qdict_put_str(src, "rule.a", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - g_assert(error != NULL); - error_free(error); - error = NULL; - qobject_unref(src); - - src = qdict_new(); - /* The input should be flat, ie no dicts or lists */ - qdict_put(src, "rule.a", qdict_new()); - qdict_put_str(src, "rule.b", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - g_assert(error != NULL); - error_free(error); - error = NULL; - qobject_unref(src); - - src = qdict_new(); - /* List indexes must not have gaps */ - qdict_put_str(src, "rule.0", "deny"); - qdict_put_str(src, "rule.3", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - g_assert(error != NULL); - error_free(error); - error = NULL; - qobject_unref(src); - - src = qdict_new(); - /* List indexes must be in %zu format */ - qdict_put_str(src, "rule.0", "deny"); - qdict_put_str(src, "rule.+1", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - g_assert(error != NULL); - error_free(error); - error = NULL; - qobject_unref(src); -} - /* * Errors test-cases */ @@ -987,29 +359,15 @@ 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); g_test_add_func("/public/to_qdict", qobject_to_qdict_test); 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); g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test); - g_test_add_func("/public/crumple/recursive", - qdict_crumple_test_recursive); - g_test_add_func("/public/crumple/empty", - qdict_crumple_test_empty); - g_test_add_func("/public/crumple/bad_inputs", - qdict_crumple_test_bad_inputs); - - g_test_add_func("/public/rename_keys", qdict_rename_keys_test); - /* The Big one */ if (g_test_slow()) { g_test_add_func("/stress/test", qdict_stress_test); From e5af0da1dcbfb1a4694150f9954554fb6df88819 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:30 +0200 Subject: [PATCH 1234/2380] block: Fix -blockdev for certain non-string scalars Configuration flows through the block subsystem in a rather peculiar way. Configuration made with -drive enters it as QemuOpts. Configuration made with -blockdev / blockdev-add enters it as QAPI type BlockdevOptions. The block subsystem uses QDict, QemuOpts and QAPI types internally. The precise flow is next to impossible to explain (I tried for this commit message, but gave up after wasting several hours). What I can explain is a flaw in the BlockDriver interface that leads to this bug: $ qemu-system-x86_64 -blockdev node-name=n1,driver=nfs,server.type=inet,server.host=localhost,path=/foo/bar,user=1234 qemu-system-x86_64: -blockdev node-name=n1,driver=nfs,server.type=inet,server.host=localhost,path=/foo/bar,user=1234: Internal error: parameter user invalid QMP blockdev-add is broken the same way. Here's what happens. The block layer passes configuration represented as flat QDict (with dotted keys) to BlockDriver methods .bdrv_file_open(). The QDict's members are typed according to the QAPI schema. nfs_file_open() converts it to QAPI type BlockdevOptionsNfs, with qdict_crumple() and a qobject input visitor. This visitor comes in two flavors. The plain flavor requires scalars to be typed according to the QAPI schema. That's the case here. The keyval flavor requires string scalars. That's not the case here. nfs_file_open() uses the latter, and promptly falls apart for members @user, @group, @tcp-syn-count, @readahead-size, @page-cache-size, @debug. Switching to the plain flavor would fix -blockdev, but break -drive, because there the scalars arrive in nfs_file_open() as strings. The proper fix would be to replace the QDict by QAPI type BlockdevOptions in the BlockDriver interface. Sadly, that's beyond my reach right now. Next best would be to fix the block layer to always pass correctly typed QDicts to the BlockDriver methods. Also beyond my reach. What I can do is throw another hack onto the pile: have nfs_file_open() convert all members to string, so use of the keyval flavor actually works, by replacing qdict_crumple() by new function qdict_crumple_for_keyval_qiv(). The pattern "pass result of qdict_crumple() to qobject_input_visitor_new_keyval()" occurs several times more: * qemu_rbd_open() Same issue as nfs_file_open(), but since BlockdevOptionsRbd has only string members, its only a latent bug. Fix it anyway. * parallels_co_create_opts(), qcow_co_create_opts(), qcow2_co_create_opts(), bdrv_qed_co_create_opts(), sd_co_create_opts(), vhdx_co_create_opts(), vpc_co_create_opts() These work, because they create the QDict with qemu_opts_to_qdict_filtered(), which creates only string scalars. The function sports a TODO comment asking for better typing; that's going to be fun. Use qdict_crumple_for_keyval_qiv() to be safe. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/nfs.c | 2 +- block/parallels.c | 2 +- block/qcow.c | 2 +- block/qcow2.c | 2 +- block/qed.c | 2 +- block/rbd.c | 2 +- block/sheepdog.c | 2 +- block/vhdx.c | 2 +- block/vpc.c | 2 +- include/block/qdict.h | 1 + qobject/block-qdict.c | 57 +++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 67 insertions(+), 9 deletions(-) diff --git a/block/nfs.c b/block/nfs.c index 3170b059b3..6935b611cc 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -561,7 +561,7 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, const QDictEntry *e; Error *local_err = NULL; - crumpled = qdict_crumple(options, errp); + crumpled = qdict_crumple_for_keyval_qiv(options, errp); if (crumpled == NULL) { return NULL; } diff --git a/block/parallels.c b/block/parallels.c index c1d9643498..695899fc4b 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -653,7 +653,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "parallels"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple(qdict, errp); + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { diff --git a/block/qcow.c b/block/qcow.c index 8c08908fd8..860b058240 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -997,7 +997,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "qcow"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple(qdict, errp); + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { diff --git a/block/qcow2.c b/block/qcow2.c index d2d955f984..0a27afa2ef 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3152,7 +3152,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt qdict_put_str(qdict, "file", bs->node_name); /* Now get the QAPI type BlockdevCreateOptions */ - qobj = qdict_crumple(qdict, errp); + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { diff --git a/block/qed.c b/block/qed.c index 324a953cbc..88fa36d409 100644 --- a/block/qed.c +++ b/block/qed.c @@ -763,7 +763,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "qed"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple(qdict, errp); + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { diff --git a/block/rbd.c b/block/rbd.c index 9659c7361f..09720e97c0 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -647,7 +647,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, } /* Convert the remaining options into a QAPI object */ - crumpled = qdict_crumple(options, errp); + crumpled = qdict_crumple_for_keyval_qiv(options, errp); if (crumpled == NULL) { r = -EINVAL; goto out; diff --git a/block/sheepdog.c b/block/sheepdog.c index 2e1f0e6eca..a93f93d360 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -2217,7 +2217,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, } /* Get the QAPI object */ - crumpled = qdict_crumple(qdict, errp); + crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); if (crumpled == NULL) { ret = -EINVAL; goto fail; diff --git a/block/vhdx.c b/block/vhdx.c index 2e32e24514..78b29d9fc7 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -2005,7 +2005,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "vhdx"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple(qdict, errp); + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { diff --git a/block/vpc.c b/block/vpc.c index 41c8c980f1..16178e5a90 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -1119,7 +1119,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "vpc"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple(qdict, errp); + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { diff --git a/include/block/qdict.h b/include/block/qdict.h index 71c037afba..47d9638c37 100644 --- a/include/block/qdict.h +++ b/include/block/qdict.h @@ -21,6 +21,7 @@ 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); QObject *qdict_crumple(const QDict *src, Error **errp); +QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp); void qdict_flatten(QDict *qdict); typedef struct QDictRenames { diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index fb92010dc5..aba372c2eb 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -9,7 +9,10 @@ #include "qemu/osdep.h" #include "block/qdict.h" +#include "qapi/qmp/qbool.h" #include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" #include "qemu/cutils.h" #include "qapi/error.h" @@ -513,6 +516,60 @@ QObject *qdict_crumple(const QDict *src, Error **errp) return NULL; } +/** + * qdict_crumple_for_keyval_qiv: + * @src: the flat dictionary (only scalar values) to crumple + * @errp: location to store error + * + * Like qdict_crumple(), but additionally transforms scalar values so + * the result can be passed to qobject_input_visitor_new_keyval(). + * + * The block subsystem uses this function to prepare its flat QDict + * with possibly confused scalar types for a visit. It should not be + * used for anything else, and it should go away once the block + * subsystem has been cleaned up. + */ +QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) +{ + QDict *tmp = NULL; + char *buf; + const char *s; + const QDictEntry *ent; + QObject *dst; + + for (ent = qdict_first(src); ent; ent = qdict_next(src, ent)) { + buf = NULL; + switch (qobject_type(ent->value)) { + case QTYPE_QNULL: + case QTYPE_QSTRING: + continue; + case QTYPE_QNUM: + s = buf = qnum_to_string(qobject_to(QNum, ent->value)); + break; + case QTYPE_QDICT: + case QTYPE_QLIST: + /* @src isn't flat; qdict_crumple() will fail */ + continue; + case QTYPE_QBOOL: + s = qbool_get_bool(qobject_to(QBool, ent->value)) + ? "on" : "off"; + break; + default: + abort(); + } + + if (!tmp) { + tmp = qdict_clone_shallow(src); + } + qdict_put(tmp, ent->key, qstring_from_str(s)); + g_free(buf); + } + + dst = qdict_crumple(tmp ?: src, errp); + qobject_unref(tmp); + return 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 From 374c52467a38c2e811f6c0db4edc9ea7d5f34341 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:31 +0200 Subject: [PATCH 1235/2380] block: Fix -drive for certain non-string scalars The previous commit fixed -blockdev breakage due to misuse of the qobject input visitor's keyval flavor in bdrv_file_open(). The commit message explain why using the plain flavor would be just as wrong; it would break -drive. Turns out we break it in three places: nbd_open(), sd_open() and ssh_file_open(). They are even marked FIXME. Example breakage: $ qemu-system-x86 -drive node-name=n1,driver=nbd,server.type=inet,server.host=localhost,server.port=1234,server.numeric=off qemu-system-x86: -drive node-name=n1,driver=nbd,server.type=inet,server.host=localhost,server.port=1234,server.numeric=off: Invalid parameter type for 'numeric', expected: boolean Fix it the same way: replace qdict_crumple() by qdict_crumple_for_keyval_qiv(), and switch from plain to the keyval flavor. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/nbd.c | 12 ++---------- block/sheepdog.c | 12 ++---------- block/ssh.c | 12 ++---------- 3 files changed, 6 insertions(+), 30 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index d6c4c4ddbc..614dd9fec0 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -273,20 +273,12 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, goto done; } - crumpled_addr = qdict_crumple(addr, errp); + crumpled_addr = qdict_crumple_for_keyval_qiv(addr, errp); if (!crumpled_addr) { goto done; } - /* - * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive - * server.type=inet. .to doesn't matter, it's ignored anyway. - * That's because when @options come from -blockdev or - * blockdev_add, members are typed according to the QAPI schema, - * but when they come from -drive, they're all QString. The - * visitor expects the former. - */ - iv = qobject_input_visitor_new(crumpled_addr); + iv = qobject_input_visitor_new_keyval(crumpled_addr); visit_type_SocketAddress(iv, NULL, &saddr, &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/block/sheepdog.c b/block/sheepdog.c index a93f93d360..29e3e1eaaa 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -546,20 +546,12 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp) qdict_extract_subqdict(options, &server, "server."); - crumpled_server = qdict_crumple(server, errp); + crumpled_server = qdict_crumple_for_keyval_qiv(server, errp); if (!crumpled_server) { goto done; } - /* - * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive - * server.type=inet. .to doesn't matter, it's ignored anyway. - * That's because when @options come from -blockdev or - * blockdev_add, members are typed according to the QAPI schema, - * but when they come from -drive, they're all QString. The - * visitor expects the former. - */ - iv = qobject_input_visitor_new(crumpled_server); + iv = qobject_input_visitor_new_keyval(crumpled_server); visit_type_SocketAddress(iv, NULL, &saddr, &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/block/ssh.c b/block/ssh.c index eec37dd27c..bd85d989d5 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -623,20 +623,12 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) } /* Create the QAPI object */ - crumpled = qdict_crumple(options, errp); + crumpled = qdict_crumple_for_keyval_qiv(options, errp); if (crumpled == NULL) { goto fail; } - /* - * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive. - * .to doesn't matter, it's ignored anyway. - * That's because when @options come from -blockdev or - * blockdev_add, members are typed according to the QAPI schema, - * but when they come from -drive, they're all QString. The - * visitor expects the former. - */ - v = qobject_input_visitor_new(crumpled); + v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); visit_free(v); qobject_unref(crumpled); From 92adf9dbcd3cf9cedddae995b04a99f5c42ae08c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:32 +0200 Subject: [PATCH 1236/2380] block: Clean up a misuse of qobject_to() in .bdrv_co_create_opts() The following pattern occurs in the .bdrv_co_create_opts() methods of parallels, qcow, qcow2, qed, vhdx and vpc: qobj = qdict_crumple_for_keyval_qiv(qdict, errp); qobject_unref(qdict); qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; goto done; } v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); [...] ret = 0; done: qobject_unref(qdict); [...] return ret; If qobject_to() fails, we return failure without setting errp. That's wrong. As far as I can tell, it cannot fail here. Clean it up anyway, by removing the useless conversion. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/parallels.c | 9 ++++----- block/qcow.c | 9 ++++----- block/qcow2.c | 9 ++++----- block/qed.c | 9 ++++----- block/vhdx.c | 9 ++++----- block/vpc.c | 9 ++++----- 6 files changed, 24 insertions(+), 30 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index 695899fc4b..ceb7a15d62 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -616,7 +616,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, BlockdevCreateOptions *create_options = NULL; Error *local_err = NULL; BlockDriverState *bs = NULL; - QDict *qdict = NULL; + QDict *qdict; QObject *qobj; Visitor *v; int ret; @@ -654,14 +654,13 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, qdict_put_str(qdict, "file", bs->node_name); qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - qobject_unref(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + if (!qobj) { ret = -EINVAL; goto done; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + v = qobject_input_visitor_new_keyval(qobj); + qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/block/qcow.c b/block/qcow.c index 860b058240..2f81f081fd 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -946,7 +946,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, { BlockdevCreateOptions *create_options = NULL; BlockDriverState *bs = NULL; - QDict *qdict = NULL; + QDict *qdict; QObject *qobj; Visitor *v; const char *val; @@ -998,14 +998,13 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, qdict_put_str(qdict, "file", bs->node_name); qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - qobject_unref(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + if (!qobj) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + v = qobject_input_visitor_new_keyval(qobj); + qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/block/qcow2.c b/block/qcow2.c index 0a27afa2ef..8c338661db 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3080,7 +3080,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt Error **errp) { BlockdevCreateOptions *create_options = NULL; - QDict *qdict = NULL; + QDict *qdict; QObject *qobj; Visitor *v; BlockDriverState *bs = NULL; @@ -3153,14 +3153,13 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt /* Now get the QAPI type BlockdevCreateOptions */ qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - qobject_unref(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + if (!qobj) { ret = -EINVAL; goto finish; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + v = qobject_input_visitor_new_keyval(qobj); + qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/block/qed.c b/block/qed.c index 88fa36d409..fcec760b26 100644 --- a/block/qed.c +++ b/block/qed.c @@ -722,7 +722,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, Error **errp) { BlockdevCreateOptions *create_options = NULL; - QDict *qdict = NULL; + QDict *qdict; QObject *qobj; Visitor *v; BlockDriverState *bs = NULL; @@ -764,14 +764,13 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, qdict_put_str(qdict, "file", bs->node_name); qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - qobject_unref(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + if (!qobj) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + v = qobject_input_visitor_new_keyval(qobj); + qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/block/vhdx.c b/block/vhdx.c index 78b29d9fc7..f2aec3d2cd 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1965,7 +1965,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, Error **errp) { BlockdevCreateOptions *create_options = NULL; - QDict *qdict = NULL; + QDict *qdict; QObject *qobj; Visitor *v; BlockDriverState *bs = NULL; @@ -2006,14 +2006,13 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, qdict_put_str(qdict, "file", bs->node_name); qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - qobject_unref(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + if (!qobj) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + v = qobject_input_visitor_new_keyval(qobj); + qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/block/vpc.c b/block/vpc.c index 16178e5a90..a9bb04149d 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -1081,7 +1081,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options = NULL; - QDict *qdict = NULL; + QDict *qdict; QObject *qobj; Visitor *v; BlockDriverState *bs = NULL; @@ -1120,14 +1120,13 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, qdict_put_str(qdict, "file", bs->node_name); qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - qobject_unref(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + if (!qobj) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + v = qobject_input_visitor_new_keyval(qobj); + qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); From af91062ee1408f7f5bb58389d355d29a5040c648 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:33 +0200 Subject: [PATCH 1237/2380] block: Factor out qobject_input_visitor_new_flat_confused() Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/nbd.c | 7 ++----- block/nfs.c | 7 ++----- block/parallels.c | 7 ++----- block/qcow.c | 7 ++----- block/qcow2.c | 7 ++----- block/qed.c | 7 ++----- block/rbd.c | 7 ++----- block/sheepdog.c | 14 ++++---------- block/ssh.c | 7 ++----- block/vhdx.c | 7 ++----- block/vpc.c | 7 ++----- include/block/qdict.h | 3 ++- qobject/block-qdict.c | 28 +++++++++++++++++++++++++++- 13 files changed, 53 insertions(+), 62 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index 614dd9fec0..13db4030e6 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -263,7 +263,6 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, { SocketAddress *saddr = NULL; QDict *addr = NULL; - QObject *crumpled_addr = NULL; Visitor *iv = NULL; Error *local_err = NULL; @@ -273,12 +272,11 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, goto done; } - crumpled_addr = qdict_crumple_for_keyval_qiv(addr, errp); - if (!crumpled_addr) { + iv = qobject_input_visitor_new_flat_confused(addr, errp); + if (!iv) { goto done; } - iv = qobject_input_visitor_new_keyval(crumpled_addr); visit_type_SocketAddress(iv, NULL, &saddr, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -287,7 +285,6 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, done: qobject_unref(addr); - qobject_unref(crumpled_addr); visit_free(iv); return saddr; } diff --git a/block/nfs.c b/block/nfs.c index 6935b611cc..743ca0450e 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -556,20 +556,17 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, Error **errp) { BlockdevOptionsNfs *opts = NULL; - QObject *crumpled = NULL; Visitor *v; const QDictEntry *e; Error *local_err = NULL; - crumpled = qdict_crumple_for_keyval_qiv(options, errp); - if (crumpled == NULL) { + v = qobject_input_visitor_new_flat_confused(options, errp); + if (!v) { return NULL; } - v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); visit_free(v); - qobject_unref(crumpled); if (local_err) { error_propagate(errp, local_err); diff --git a/block/parallels.c b/block/parallels.c index ceb7a15d62..fd215e202a 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -617,7 +617,6 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, Error *local_err = NULL; BlockDriverState *bs = NULL; QDict *qdict; - QObject *qobj; Visitor *v; int ret; @@ -653,14 +652,12 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "parallels"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - if (!qobj) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto done; } - v = qobject_input_visitor_new_keyval(qobj); - qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/block/qcow.c b/block/qcow.c index 2f81f081fd..5532731b9f 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -947,7 +947,6 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, BlockdevCreateOptions *create_options = NULL; BlockDriverState *bs = NULL; QDict *qdict; - QObject *qobj; Visitor *v; const char *val; Error *local_err = NULL; @@ -997,14 +996,12 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "qcow"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - if (!qobj) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(qobj); - qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/block/qcow2.c b/block/qcow2.c index 8c338661db..945132f692 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3081,7 +3081,6 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt { BlockdevCreateOptions *create_options = NULL; QDict *qdict; - QObject *qobj; Visitor *v; BlockDriverState *bs = NULL; Error *local_err = NULL; @@ -3152,14 +3151,12 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt qdict_put_str(qdict, "file", bs->node_name); /* Now get the QAPI type BlockdevCreateOptions */ - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - if (!qobj) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto finish; } - v = qobject_input_visitor_new_keyval(qobj); - qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/block/qed.c b/block/qed.c index fcec760b26..2363814538 100644 --- a/block/qed.c +++ b/block/qed.c @@ -723,7 +723,6 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, { BlockdevCreateOptions *create_options = NULL; QDict *qdict; - QObject *qobj; Visitor *v; BlockDriverState *bs = NULL; Error *local_err = NULL; @@ -763,14 +762,12 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "qed"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - if (!qobj) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(qobj); - qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/block/rbd.c b/block/rbd.c index 09720e97c0..82346a2a5e 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -630,7 +630,6 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, BDRVRBDState *s = bs->opaque; BlockdevOptionsRbd *opts = NULL; Visitor *v; - QObject *crumpled = NULL; const QDictEntry *e; Error *local_err = NULL; char *keypairs, *secretid; @@ -647,16 +646,14 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, } /* Convert the remaining options into a QAPI object */ - crumpled = qdict_crumple_for_keyval_qiv(options, errp); - if (crumpled == NULL) { + v = qobject_input_visitor_new_flat_confused(options, errp); + if (!v) { r = -EINVAL; goto out; } - v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); visit_free(v); - qobject_unref(crumpled); if (local_err) { error_propagate(errp, local_err); diff --git a/block/sheepdog.c b/block/sheepdog.c index 29e3e1eaaa..665b1763eb 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -539,19 +539,17 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s, static SocketAddress *sd_server_config(QDict *options, Error **errp) { QDict *server = NULL; - QObject *crumpled_server = NULL; Visitor *iv = NULL; SocketAddress *saddr = NULL; Error *local_err = NULL; qdict_extract_subqdict(options, &server, "server."); - crumpled_server = qdict_crumple_for_keyval_qiv(server, errp); - if (!crumpled_server) { + iv = qobject_input_visitor_new_flat_confused(server, errp); + if (!iv) { goto done; } - iv = qobject_input_visitor_new_keyval(crumpled_server); visit_type_SocketAddress(iv, NULL, &saddr, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -560,7 +558,6 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp) done: visit_free(iv); - qobject_unref(crumpled_server); qobject_unref(server); return saddr; } @@ -2173,7 +2170,6 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, { BlockdevCreateOptions *create_options = NULL; QDict *qdict, *location_qdict; - QObject *crumpled; Visitor *v; char *redundancy; Error *local_err = NULL; @@ -2209,16 +2205,14 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, } /* Get the QAPI object */ - crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); - if (crumpled == NULL) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); - qobject_unref(crumpled); if (local_err) { error_propagate(errp, local_err); diff --git a/block/ssh.c b/block/ssh.c index bd85d989d5..da7bbf73e2 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -606,7 +606,6 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) BlockdevOptionsSsh *result = NULL; QemuOpts *opts = NULL; Error *local_err = NULL; - QObject *crumpled; const QDictEntry *e; Visitor *v; @@ -623,15 +622,13 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) } /* Create the QAPI object */ - crumpled = qdict_crumple_for_keyval_qiv(options, errp); - if (crumpled == NULL) { + v = qobject_input_visitor_new_flat_confused(options, errp); + if (!v) { goto fail; } - v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); visit_free(v); - qobject_unref(crumpled); if (local_err) { error_propagate(errp, local_err); diff --git a/block/vhdx.c b/block/vhdx.c index f2aec3d2cd..a677703a9e 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1966,7 +1966,6 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, { BlockdevCreateOptions *create_options = NULL; QDict *qdict; - QObject *qobj; Visitor *v; BlockDriverState *bs = NULL; Error *local_err = NULL; @@ -2005,14 +2004,12 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "vhdx"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - if (!qobj) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(qobj); - qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/block/vpc.c b/block/vpc.c index a9bb04149d..bf294abfa7 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -1082,7 +1082,6 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, { BlockdevCreateOptions *create_options = NULL; QDict *qdict; - QObject *qobj; Visitor *v; BlockDriverState *bs = NULL; Error *local_err = NULL; @@ -1119,14 +1118,12 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "vpc"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); - if (!qobj) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(qobj); - qobject_unref(qobj); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); diff --git a/include/block/qdict.h b/include/block/qdict.h index 47d9638c37..d8cb502d7d 100644 --- a/include/block/qdict.h +++ b/include/block/qdict.h @@ -21,7 +21,6 @@ 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); QObject *qdict_crumple(const QDict *src, Error **errp); -QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp); void qdict_flatten(QDict *qdict); typedef struct QDictRenames { @@ -30,4 +29,6 @@ typedef struct QDictRenames { } QDictRenames; bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); +Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict, + Error **errp); #endif diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index aba372c2eb..41f39abc4a 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -13,6 +13,7 @@ #include "qapi/qmp/qlist.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" +#include "qapi/qobject-input-visitor.h" #include "qemu/cutils.h" #include "qapi/error.h" @@ -529,7 +530,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) * used for anything else, and it should go away once the block * subsystem has been cleaned up. */ -QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) +static QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) { QDict *tmp = NULL; char *buf; @@ -695,3 +696,28 @@ bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) } return true; } + +/* + * Create a QObject input visitor for flat @qdict with possibly + * confused scalar types. + * + * The block subsystem uses this function to visit its flat QDict with + * possibly confused scalar types. It should not be used for anything + * else, and it should go away once the block subsystem has been + * cleaned up. + */ +Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict, + Error **errp) +{ + QObject *crumpled; + Visitor *v; + + crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); + if (!crumpled) { + return NULL; + } + + v = qobject_input_visitor_new_keyval(crumpled); + qobject_unref(crumpled); + return v; +} From f853465aacb45dbb07e4cc9815e39b55e10dc690 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:34 +0200 Subject: [PATCH 1238/2380] block: Make remaining uses of qobject input visitor more robust Remaining uses of qobject_input_visitor_new_keyval() in the block subsystem: * block_crypto_open_opts_init() Currently doesn't visit any non-string scalars, thus safe. It's called from - block_crypto_open_luks() Creates the QDict with qemu_opts_to_qdict_filtered(), which creates only string scalars, but has a TODO asking for other types. - qcow_open() - qcow2_open(), qcow2_co_invalidate_cache(), qcow2_reopen_prepare() * block_crypto_create_opts_init(), called from - block_crypto_co_create_opts_luks() Also creates the QDict with qemu_opts_to_qdict_filtered(). * vdi_co_create_opts() Also creates the QDict with qemu_opts_to_qdict_filtered(). Replace these uses by qobject_input_visitor_new_flat_confused() for robustness. This adds crumpling. Right now, that's a no-op, but if we ever extend these things in non-flat ways, crumpling will be needed. Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- block/crypto.c | 12 +++++++++--- block/vdi.c | 8 ++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/block/crypto.c b/block/crypto.c index bc322b50f5..82091c5f70 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -21,11 +21,11 @@ #include "qemu/osdep.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "crypto/block.h" #include "qapi/opts-visitor.h" #include "qapi/qapi-visit-crypto.h" -#include "qapi/qmp/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" #include "qemu/option.h" @@ -159,7 +159,10 @@ block_crypto_open_opts_init(QCryptoBlockFormat format, ret = g_new0(QCryptoBlockOpenOptions, 1); ret->format = format; - v = qobject_input_visitor_new_keyval(QOBJECT(opts)); + v = qobject_input_visitor_new_flat_confused(opts, &local_err); + if (local_err) { + goto out; + } visit_start_struct(v, NULL, NULL, 0, &local_err); if (local_err) { @@ -210,7 +213,10 @@ block_crypto_create_opts_init(QCryptoBlockFormat format, ret = g_new0(QCryptoBlockCreateOptions, 1); ret->format = format; - v = qobject_input_visitor_new_keyval(QOBJECT(opts)); + v = qobject_input_visitor_new_flat_confused(opts, &local_err); + if (local_err) { + goto out; + } visit_start_struct(v, NULL, NULL, 0, &local_err); if (local_err) { diff --git a/block/vdi.c b/block/vdi.c index 668af0a828..1d8ed67dbf 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -51,10 +51,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" @@ -934,7 +934,11 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, } /* Get the QAPI object */ - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { + ret = -EINVAL; + goto done; + } visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); From eb0e0f7d3d4a9c585421d05b19ca71df5d69fc47 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:35 +0200 Subject: [PATCH 1239/2380] block-qdict: Simplify qdict_flatten_qdict() There's no need to restart the loop. We don't elsewhere, e.g. in qdict_extract_subqdict(), qdict_join() and qemu_opts_absorb_qdict(). Simplify accordingly. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qobject/block-qdict.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index 41f39abc4a..f32df343e8 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -89,16 +89,13 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) QObject *value; const QDictEntry *entry, *next; char *new_key; - bool delete; entry = qdict_first(qdict); while (entry != NULL) { - next = qdict_next(qdict, entry); value = qdict_entry_value(entry); new_key = NULL; - delete = false; if (prefix) { new_key = g_strdup_printf("%s.%s", prefix, entry->key); @@ -109,27 +106,18 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) * itself disappears. */ qdict_flatten_qdict(qobject_to(QDict, value), target, new_key ? new_key : entry->key); - delete = true; + qdict_del(qdict, entry->key); } else if (qobject_type(value) == QTYPE_QLIST) { qdict_flatten_qlist(qobject_to(QList, value), target, new_key ? new_key : entry->key); - delete = true; + qdict_del(qdict, entry->key); } else if (prefix) { /* All other objects are moved to the target unchanged. */ qdict_put_obj(target, new_key, qobject_ref(value)); - delete = true; + qdict_del(qdict, entry->key); } g_free(new_key); - - if (delete) { - qdict_del(qdict, entry->key); - - /* Restart loop after modifying the iterated QDict */ - entry = qdict_first(qdict); - continue; - } - entry = next; } } From f1b34a248e9785e8cc0d28a1685d2cf4460bb256 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:36 +0200 Subject: [PATCH 1240/2380] block-qdict: Tweak qdict_flatten_qdict(), qdict_flatten_qlist() qdict_flatten_qdict() skips copying scalars from @qdict to @target when the two are the same. Fair enough, but it uses a non-obvious test for "same". Replace it by the obvious one. While there, improve comments. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qobject/block-qdict.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index f32df343e8..a4e1c8d08f 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -71,12 +71,15 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) value = qlist_entry_obj(entry); new_key = g_strdup_printf("%s.%i", prefix, i); + /* + * Flatten non-empty QDict and QList recursively into @target, + * copy other objects to @target + */ if (qobject_type(value) == QTYPE_QDICT) { qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); } else if (qobject_type(value) == QTYPE_QLIST) { qdict_flatten_qlist(qobject_to(QList, value), target, new_key); } else { - /* All other types are moved to the target unchanged. */ qdict_put_obj(target, new_key, qobject_ref(value)); } @@ -101,9 +104,11 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) new_key = g_strdup_printf("%s.%s", prefix, entry->key); } + /* + * Flatten non-empty QDict and QList recursively into @target, + * copy other objects to @target + */ if (qobject_type(value) == QTYPE_QDICT) { - /* Entries of QDicts are processed recursively, the QDict object - * itself disappears. */ qdict_flatten_qdict(qobject_to(QDict, value), target, new_key ? new_key : entry->key); qdict_del(qdict, entry->key); @@ -111,8 +116,7 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) qdict_flatten_qlist(qobject_to(QList, value), target, new_key ? new_key : entry->key); qdict_del(qdict, entry->key); - } else if (prefix) { - /* All other objects are moved to the target unchanged. */ + } else if (target != qdict) { qdict_put_obj(target, new_key, qobject_ref(value)); qdict_del(qdict, entry->key); } From 3692b5d76819e573dedc9004c4b2b0e3dad83530 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:37 +0200 Subject: [PATCH 1241/2380] block-qdict: Clean up qdict_crumple() a bit When you mix scalar and non-scalar keys, whether you get an "already set as scalar" or an "already set as dict" error depends on qdict iteration order. Neither message makes much sense. Replace by ""Cannot mix scalar and non-scalar keys". This is similar to the message we get for mixing list and non-list keys. I find qdict_crumple()'s first loop hard to understand. Rearrange it and add a comment. Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- qobject/block-qdict.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index a4e1c8d08f..36cf58acc8 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -403,7 +403,7 @@ static int qdict_is_list(QDict *maybe_list, Error **errp) QObject *qdict_crumple(const QDict *src, Error **errp) { const QDictEntry *ent; - QDict *two_level, *multi_level = NULL; + QDict *two_level, *multi_level = NULL, *child_dict; QObject *dst = NULL, *child; size_t i; char *prefix = NULL; @@ -422,28 +422,28 @@ QObject *qdict_crumple(const QDict *src, Error **errp) } qdict_split_flat_key(ent->key, &prefix, &suffix); - child = qdict_get(two_level, prefix); - if (suffix) { - QDict *child_dict = qobject_to(QDict, child); - if (!child_dict) { - if (child) { - error_setg(errp, "Key %s prefix is already set as a scalar", - prefix); - goto error; - } + child_dict = qobject_to(QDict, child); - child_dict = qdict_new(); - qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); - } - - qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); - } else { - if (child) { - error_setg(errp, "Key %s prefix is already set as a dict", - prefix); + if (child) { + /* + * If @child_dict, then all previous keys with this prefix + * had a suffix. If @suffix, this one has one as well, + * and we're good, else there's a clash. + */ + if (!child_dict || !suffix) { + error_setg(errp, "Cannot mix scalar and non-scalar keys"); goto error; } + } + + if (suffix) { + if (!child_dict) { + child_dict = qdict_new(); + qdict_put(two_level, prefix, child_dict); + } + qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); + } else { qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); } From c78b8cfbfd53737353dc94dfb99c57d72ce31ab5 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:38 +0200 Subject: [PATCH 1242/2380] block-qdict: Simplify qdict_is_list() some Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qobject/block-qdict.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index 36cf58acc8..e51a3d2c0f 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -317,27 +317,22 @@ static int qdict_is_list(QDict *maybe_list, Error **errp) for (ent = qdict_first(maybe_list); ent != NULL; ent = qdict_next(maybe_list, ent)) { + int is_index = !qemu_strtoi64(ent->key, NULL, 10, &val); - if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { - if (is_list == -1) { - is_list = 1; - } else if (!is_list) { - error_setg(errp, - "Cannot mix list and non-list keys"); - return -1; - } + if (is_list == -1) { + is_list = is_index; + } + + if (is_index != is_list) { + error_setg(errp, "Cannot mix list and non-list keys"); + return -1; + } + + if (is_index) { len++; if (val > max) { max = val; } - } else { - if (is_list == -1) { - is_list = 0; - } else if (is_list) { - error_setg(errp, - "Cannot mix list and non-list keys"); - return -1; - } } } From cddec036830ada5d5d45023bcfba09015b8ab394 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:39 +0200 Subject: [PATCH 1243/2380] check-block-qdict: Rename qdict_flatten()'s variables for clarity Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/check-block-qdict.c | 57 ++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c index 5b9f4d506e..29f58a2d3d 100644 --- a/tests/check-block-qdict.c +++ b/tests/check-block-qdict.c @@ -37,11 +37,11 @@ static void qdict_defaults_test(void) static void qdict_flatten_test(void) { - QList *list1 = qlist_new(); - QList *list2 = qlist_new(); - QDict *dict1 = qdict_new(); - QDict *dict2 = qdict_new(); - QDict *dict3 = qdict_new(); + QList *e_1 = qlist_new(); + QList *e = qlist_new(); + QDict *e_1_2 = qdict_new(); + QDict *f = qdict_new(); + QDict *root = qdict_new(); /* * Test the flattening of @@ -79,35 +79,36 @@ static void qdict_flatten_test(void) * } */ - qdict_put_int(dict1, "a", 0); - qdict_put_int(dict1, "b", 1); + qdict_put_int(e_1_2, "a", 0); + qdict_put_int(e_1_2, "b", 1); - qlist_append_int(list1, 23); - qlist_append_int(list1, 66); - qlist_append(list1, dict1); - qlist_append_int(list2, 42); - qlist_append(list2, list1); + qlist_append_int(e_1, 23); + qlist_append_int(e_1, 66); + qlist_append(e_1, e_1_2); + qlist_append_int(e, 42); + qlist_append(e, e_1); - qdict_put_int(dict2, "c", 2); - qdict_put_int(dict2, "d", 3); - qdict_put(dict3, "e", list2); - qdict_put(dict3, "f", dict2); - qdict_put_int(dict3, "g", 4); + qdict_put_int(f, "c", 2); + qdict_put_int(f, "d", 3); - qdict_flatten(dict3); + qdict_put(root, "e", e); + qdict_put(root, "f", f); + qdict_put_int(root, "g", 4); - g_assert(qdict_get_int(dict3, "e.0") == 42); - g_assert(qdict_get_int(dict3, "e.1.0") == 23); - g_assert(qdict_get_int(dict3, "e.1.1") == 66); - g_assert(qdict_get_int(dict3, "e.1.2.a") == 0); - g_assert(qdict_get_int(dict3, "e.1.2.b") == 1); - g_assert(qdict_get_int(dict3, "f.c") == 2); - g_assert(qdict_get_int(dict3, "f.d") == 3); - g_assert(qdict_get_int(dict3, "g") == 4); + qdict_flatten(root); - g_assert(qdict_size(dict3) == 8); + g_assert(qdict_get_int(root, "e.0") == 42); + g_assert(qdict_get_int(root, "e.1.0") == 23); + g_assert(qdict_get_int(root, "e.1.1") == 66); + g_assert(qdict_get_int(root, "e.1.2.a") == 0); + g_assert(qdict_get_int(root, "e.1.2.b") == 1); + g_assert(qdict_get_int(root, "f.c") == 2); + g_assert(qdict_get_int(root, "f.d") == 3); + g_assert(qdict_get_int(root, "g") == 4); - qobject_unref(dict3); + g_assert(qdict_size(root) == 8); + + qobject_unref(root); } static void qdict_array_split_test(void) From bef96b1549907b005ce1fa1456d2a0910d2a1aa5 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:40 +0200 Subject: [PATCH 1244/2380] check-block-qdict: Cover flattening of empty lists and dictionaries Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/check-block-qdict.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c index 29f58a2d3d..2da16f01a6 100644 --- a/tests/check-block-qdict.c +++ b/tests/check-block-qdict.c @@ -41,6 +41,8 @@ static void qdict_flatten_test(void) QList *e = qlist_new(); QDict *e_1_2 = qdict_new(); QDict *f = qdict_new(); + QList *y = qlist_new(); + QDict *z = qdict_new(); QDict *root = qdict_new(); /* @@ -62,7 +64,9 @@ static void qdict_flatten_test(void) * "c": 2, * "d": 3, * }, - * "g": 4 + * "g": 4, + * "y": [{}], + * "z": {"a": []} * } * * to @@ -77,6 +81,8 @@ static void qdict_flatten_test(void) * "f.d": 3, * "g": 4 * } + * + * Note that "y" and "z" get eaten. */ qdict_put_int(e_1_2, "a", 0); @@ -91,9 +97,15 @@ static void qdict_flatten_test(void) qdict_put_int(f, "c", 2); qdict_put_int(f, "d", 3); + qlist_append(y, qdict_new()); + + qdict_put(z, "a", qlist_new()); + qdict_put(root, "e", e); qdict_put(root, "f", f); qdict_put_int(root, "g", 4); + qdict_put(root, "y", y); + qdict_put(root, "z", z); qdict_flatten(root); From 2860b2b2cb883969c8f6464bd9f8bc88742c5c73 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:41 +0200 Subject: [PATCH 1245/2380] block: Fix -blockdev / blockdev-add for empty objects and arrays -blockdev and blockdev-add silently ignore empty objects and arrays in their argument. That's because qmp_blockdev_add() converts the argument to a flat QDict, and qdict_flatten() eats empty QDict and QList members. For instance, we ignore an empty BlockdevOptions member @cache. No real harm, as absent means the same as empty there. Thus, the flaw puts an artificial restriction on the QAPI schema: we can't have potentially empty objects and arrays within BlockdevOptions, except when they're optional and "empty" has the same meaning as "absent". Our QAPI schema satisfies this restriction (I checked), but it's a trap for the unwary, and a temptation to employ awkward workarounds for the wary. Let's get rid of it. Change qdict_flatten() and qdict_crumple() to treat empty dictionaries and lists exactly like scalars. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qobject/block-qdict.c | 54 ++++++++++++++++++++++++--------------- tests/check-block-qdict.c | 38 +++++++++++++++++++++------ 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index e51a3d2c0f..df833083a7 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -56,6 +56,8 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) { QObject *value; const QListEntry *entry; + QDict *dict_val; + QList *list_val; char *new_key; int i; @@ -69,16 +71,18 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) for (i = 0; entry; entry = qlist_next(entry), i++) { value = qlist_entry_obj(entry); + dict_val = qobject_to(QDict, value); + list_val = qobject_to(QList, value); new_key = g_strdup_printf("%s.%i", prefix, i); /* * Flatten non-empty QDict and QList recursively into @target, * copy other objects to @target */ - if (qobject_type(value) == QTYPE_QDICT) { - qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); - } else if (qobject_type(value) == QTYPE_QLIST) { - qdict_flatten_qlist(qobject_to(QList, value), target, new_key); + if (dict_val && qdict_size(dict_val)) { + qdict_flatten_qdict(dict_val, target, new_key); + } else if (list_val && !qlist_empty(list_val)) { + qdict_flatten_qlist(list_val, target, new_key); } else { qdict_put_obj(target, new_key, qobject_ref(value)); } @@ -91,6 +95,8 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) { QObject *value; const QDictEntry *entry, *next; + QDict *dict_val; + QList *list_val; char *new_key; entry = qdict_first(qdict); @@ -98,6 +104,8 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) while (entry != NULL) { next = qdict_next(qdict, entry); value = qdict_entry_value(entry); + dict_val = qobject_to(QDict, value); + list_val = qobject_to(QList, value); new_key = NULL; if (prefix) { @@ -108,12 +116,12 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) * Flatten non-empty QDict and QList recursively into @target, * copy other objects to @target */ - if (qobject_type(value) == QTYPE_QDICT) { - qdict_flatten_qdict(qobject_to(QDict, value), target, + if (dict_val && qdict_size(dict_val)) { + qdict_flatten_qdict(dict_val, target, new_key ? new_key : entry->key); qdict_del(qdict, entry->key); - } else if (qobject_type(value) == QTYPE_QLIST) { - qdict_flatten_qlist(qobject_to(QList, value), target, + } else if (list_val && !qlist_empty(list_val)) { + qdict_flatten_qlist(list_val, target, new_key ? new_key : entry->key); qdict_del(qdict, entry->key); } else if (target != qdict) { @@ -127,10 +135,11 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) } /** - * qdict_flatten(): For each nested QDict with key x, all fields with key y - * are moved to this QDict and their key is renamed to "x.y". For each nested - * QList with key x, the field at index y is moved to this QDict with the key - * "x.y" (i.e., the reverse of what qdict_array_split() does). + * qdict_flatten(): For each nested non-empty QDict with key x, all + * fields with key y are moved to this QDict and their key is renamed + * to "x.y". For each nested non-empty QList with key x, the field at + * index y is moved to this QDict with the key "x.y" (i.e., the + * reverse of what qdict_array_split() does). * This operation is applied recursively for nested QDicts and QLists. */ void qdict_flatten(QDict *qdict) @@ -361,8 +370,8 @@ static int qdict_is_list(QDict *maybe_list, Error **errp) * @src: the original flat dictionary (only scalar values) to crumple * * Takes a flat dictionary whose keys use '.' separator to indicate - * nesting, and values are scalars, and crumples it into a nested - * structure. + * nesting, and values are scalars, empty dictionaries or empty lists, + * and crumples it into a nested structure. * * To include a literal '.' in a key name, it must be escaped as '..' * @@ -399,6 +408,8 @@ QObject *qdict_crumple(const QDict *src, Error **errp) { const QDictEntry *ent; QDict *two_level, *multi_level = NULL, *child_dict; + QDict *dict_val; + QList *list_val; QObject *dst = NULL, *child; size_t i; char *prefix = NULL; @@ -409,10 +420,11 @@ QObject *qdict_crumple(const QDict *src, Error **errp) /* Step 1: split our totally flat dict into a two level dict */ for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { - if (qobject_type(ent->value) == QTYPE_QDICT || - qobject_type(ent->value) == QTYPE_QLIST) { - error_setg(errp, "Value %s is not a scalar", - ent->key); + dict_val = qobject_to(QDict, ent->value); + list_val = qobject_to(QList, ent->value); + if ((dict_val && qdict_size(dict_val)) + || (list_val && !qlist_empty(list_val))) { + error_setg(errp, "Value %s is not flat", ent->key); goto error; } @@ -451,9 +463,9 @@ QObject *qdict_crumple(const QDict *src, Error **errp) multi_level = qdict_new(); for (ent = qdict_first(two_level); ent != NULL; ent = qdict_next(two_level, ent)) { - QDict *dict = qobject_to(QDict, ent->value); - if (dict) { - child = qdict_crumple(dict, errp); + dict_val = qobject_to(QDict, ent->value); + if (dict_val && qdict_size(dict_val)) { + child = qdict_crumple(dict_val, errp); if (!child) { goto error; } diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c index 2da16f01a6..1d20fccbd4 100644 --- a/tests/check-block-qdict.c +++ b/tests/check-block-qdict.c @@ -79,10 +79,10 @@ static void qdict_flatten_test(void) * "e.1.2.b": 1, * "f.c": 2, * "f.d": 3, - * "g": 4 + * "g": 4, + * "y.0": {}, + * "z.a": [] * } - * - * Note that "y" and "z" get eaten. */ qdict_put_int(e_1_2, "a", 0); @@ -117,8 +117,10 @@ static void qdict_flatten_test(void) g_assert(qdict_get_int(root, "f.c") == 2); g_assert(qdict_get_int(root, "f.d") == 3); g_assert(qdict_get_int(root, "g") == 4); + g_assert(!qdict_size(qdict_get_qdict(root, "y.0"))); + g_assert(qlist_empty(qdict_get_qlist(root, "z.a"))); - g_assert(qdict_size(root) == 8); + g_assert(qdict_size(root) == 10); qobject_unref(root); } @@ -387,7 +389,8 @@ static void qdict_join_test(void) static void qdict_crumple_test_recursive(void) { QDict *src, *dst, *rule, *vnc, *acl, *listen; - QList *rules; + QDict *empty, *empty_dict, *empty_list_0; + QList *rules, *empty_list, *empty_dict_a; src = qdict_new(); qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); @@ -399,10 +402,12 @@ static void qdict_crumple_test_recursive(void) qdict_put_str(src, "vnc.acl.default", "deny"); qdict_put_str(src, "vnc.acl..name", "acl0"); qdict_put_str(src, "vnc.acl.rule..name", "acl0"); + qdict_put(src, "empty.dict.a", qlist_new()); + qdict_put(src, "empty.list.0", qdict_new()); dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); g_assert(dst); - g_assert_cmpint(qdict_size(dst), ==, 1); + g_assert_cmpint(qdict_size(dst), ==, 2); vnc = qdict_get_qdict(dst, "vnc"); g_assert(vnc); @@ -440,6 +445,21 @@ static void qdict_crumple_test_recursive(void) g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); + empty = qdict_get_qdict(dst, "empty"); + g_assert(empty); + g_assert_cmpint(qdict_size(empty), ==, 2); + empty_dict = qdict_get_qdict(empty, "dict"); + g_assert(empty_dict); + g_assert_cmpint(qdict_size(empty_dict), ==, 1); + empty_dict_a = qdict_get_qlist(empty_dict, "a"); + g_assert(empty_dict_a && qlist_empty(empty_dict_a)); + empty_list = qdict_get_qlist(empty, "list"); + g_assert(empty_list); + g_assert_cmpint(qlist_size(empty_list), ==, 1); + empty_list_0 = qobject_to(QDict, qlist_pop(empty_list)); + g_assert(empty_list_0); + g_assert_cmpint(qdict_size(empty_list_0), ==, 0); + qobject_unref(src); qobject_unref(dst); } @@ -587,7 +607,7 @@ static void qdict_rename_keys_test(void) static void qdict_crumple_test_bad_inputs(void) { - QDict *src; + QDict *src, *nested; Error *error = NULL; src = qdict_new(); @@ -614,7 +634,9 @@ static void qdict_crumple_test_bad_inputs(void) src = qdict_new(); /* The input should be flat, ie no dicts or lists */ - qdict_put(src, "rule.a", qdict_new()); + nested = qdict_new(); + qdict_put(nested, "x", qdict_new()); + qdict_put(src, "rule.a", nested); qdict_put_str(src, "rule.b", "allow"); g_assert(qdict_crumple(src, &error) == NULL); From a3699de4dde82bc76b33a83798a9da82c2336cce Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:42 +0200 Subject: [PATCH 1246/2380] rbd: New parameter auth-client-required Parameter auth-client-required lets you configure authentication methods. We tried to provide that in v2.9.0, but backed out due to interface design doubts (commit 464444fcc16). This commit is similar to what we backed out, but simpler: we use a list of enumeration values instead of a list of objects with a member of enumeration type. Let's review our reasons for backing out the first try, as stated in the commit message: * The implementation uses deprecated rados_conf_set() key "auth_supported". No biggie. Fixed: we use "auth-client-required". * The implementation makes -drive silently ignore invalid parameters "auth" and "auth-supported.*.X" where X isn't "auth". Fixable (in fact I'm going to fix similar bugs around parameter server), so again no biggie. That fix is commit 2836284db60. This commit doesn't bring the bugs back. * BlockdevOptionsRbd member @password-secret applies only to authentication method cephx. Should it be a variant member of RbdAuthMethod? We've had time to ponder, and we decided to stick to the way Ceph configuration works: the key configured separately, and silently ignored if the authentication method doesn't use it. * BlockdevOptionsRbd member @user could apply to both methods cephx and none, but I'm not sure it's actually used with none. If it isn't, should it be a variant member of RbdAuthMethod? Likewise. * The client offers a *set* of authentication methods, not a list. Should the methods be optional members of BlockdevOptionsRbd instead of members of list @auth-supported? The latter begs the question what multiple entries for the same method mean. Trivial question now that RbdAuthMethod contains nothing but @type, but less so when RbdAuthMethod acquires other members, such the ones discussed above. Again, we decided to stick to the way Ceph configuration works, except we make auth-client-required a list of enumeration values instead of a string containing keywords separated by delimiters. * How BlockdevOptionsRbd member @auth-supported interacts with settings from a configuration file specified with @conf is undocumented. I suspect it's untested, too. Not actually true, the documentation for @conf says "Values in the configuration file will be overridden by options specified via QAPI", and we've tested this. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/rbd.c | 42 ++++++++++++++++++++++++++++++++---------- qapi/block-core.json | 13 +++++++++++++ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index 82346a2a5e..ea0575d068 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -240,21 +240,43 @@ static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp) static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, + BlockdevOptionsRbd *opts, Error **errp) { - if (secretid == 0) { - return 0; + char *acr; + int r; + GString *accu; + RbdAuthModeList *auth; + + if (secretid) { + gchar *secret = qcrypto_secret_lookup_as_base64(secretid, + errp); + if (!secret) { + return -1; + } + + rados_conf_set(cluster, "key", secret); + g_free(secret); } - gchar *secret = qcrypto_secret_lookup_as_base64(secretid, - errp); - if (!secret) { - return -1; + if (opts->has_auth_client_required) { + accu = g_string_new(""); + for (auth = opts->auth_client_required; auth; auth = auth->next) { + if (accu->str[0]) { + g_string_append_c(accu, ';'); + } + g_string_append(accu, RbdAuthMode_str(auth->value)); + } + acr = g_string_free(accu, FALSE); + r = rados_conf_set(cluster, "auth_client_required", acr); + g_free(acr); + if (r < 0) { + error_setg_errno(errp, -r, + "Could not set 'auth_client_required'"); + return r; + } } - rados_conf_set(cluster, "key", secret); - g_free(secret); - return 0; } @@ -585,7 +607,7 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, } } - if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) { + if (qemu_rbd_set_auth(*cluster, secretid, opts, errp) < 0) { r = -EIO; goto failed_shutdown; } diff --git a/qapi/block-core.json b/qapi/block-core.json index fff23fc82b..0f68ca56f3 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3177,6 +3177,14 @@ '*timeout': 'int' } } +## +# @RbdAuthMode: +# +# Since: 3.0 +## +{ 'enum': 'RbdAuthMode', + 'data': [ 'cephx', 'none' ] } + ## # @BlockdevOptionsRbd: # @@ -3192,6 +3200,10 @@ # # @user: Ceph id name. # +# @auth-client-required: Acceptable authentication modes. +# This maps to Ceph configuration option +# "auth_client_required". (Since 3.0) +# # @server: Monitor host address and port. This maps # to the "mon_host" Ceph option. # @@ -3203,6 +3215,7 @@ '*conf': 'str', '*snapshot': 'str', '*user': 'str', + '*auth-client-required': ['RbdAuthMode'], '*server': ['InetSocketAddressBase'] } } ## From d083f954a95d37b460df0c2fbfe46ad7eb207b10 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 14 Jun 2018 21:14:43 +0200 Subject: [PATCH 1247/2380] rbd: New parameter key-secret Legacy -drive supports "password-secret" parameter that isn't available with -blockdev / blockdev-add. That's because we backed out our first try to provide it there due to interface design doubts, in commit 577d8c9a811, v2.9.0. This is the second try. It brings back the parameter, except it's named "key-secret" now. Let's review our reasons for backing out the first try, as stated in the commit message: * BlockdevOptionsRbd member @password-secret isn't actually a password, it's a key generated by Ceph. Addressed by the rename. * We're not sure where member @password-secret belongs (see the previous commit). See previous commit. * How @password-secret interacts with settings from a configuration file specified with @conf is undocumented. Not actually true, the documentation for @conf says "Values in the configuration file will be overridden by options specified via QAPI", and we've tested this. Signed-off-by: Markus Armbruster Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/rbd.c | 41 +++++++++++++++++++++++++---------------- qapi/block-core.json | 6 ++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index ea0575d068..f2c6965418 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -239,24 +239,25 @@ static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp) } -static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, - BlockdevOptionsRbd *opts, +static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts, Error **errp) { - char *acr; + char *key, *acr; int r; GString *accu; RbdAuthModeList *auth; - if (secretid) { - gchar *secret = qcrypto_secret_lookup_as_base64(secretid, - errp); - if (!secret) { - return -1; + if (opts->key_secret) { + key = qcrypto_secret_lookup_as_base64(opts->key_secret, errp); + if (!key) { + return -EIO; + } + r = rados_conf_set(cluster, "key", key); + g_free(key); + if (r < 0) { + error_setg_errno(errp, -r, "Could not set 'key'"); + return r; } - - rados_conf_set(cluster, "key", secret); - g_free(secret); } if (opts->has_auth_client_required) { @@ -367,9 +368,7 @@ static QemuOptsList runtime_opts = { }, }; -/* FIXME Deprecate and remove keypairs or make it available in QMP. - * password_secret should eventually be configurable in opts->location. Support - * for it in .bdrv_open will make it work here as well. */ +/* FIXME Deprecate and remove keypairs or make it available in QMP. */ static int qemu_rbd_do_create(BlockdevCreateOptions *options, const char *keypairs, const char *password_secret, Error **errp) @@ -575,6 +574,16 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, Error *local_err = NULL; int r; + if (secretid) { + if (opts->key_secret) { + error_setg(errp, + "Legacy 'password-secret' clashes with 'key-secret'"); + return -EINVAL; + } + opts->key_secret = g_strdup(secretid); + opts->has_key_secret = true; + } + mon_host = qemu_rbd_mon_host(opts, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -607,8 +616,8 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, } } - if (qemu_rbd_set_auth(*cluster, secretid, opts, errp) < 0) { - r = -EIO; + r = qemu_rbd_set_auth(*cluster, opts, errp); + if (r < 0) { goto failed_shutdown; } diff --git a/qapi/block-core.json b/qapi/block-core.json index 0f68ca56f3..ab629d1647 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3204,6 +3204,11 @@ # This maps to Ceph configuration option # "auth_client_required". (Since 3.0) # +# @key-secret: ID of a QCryptoSecret object providing a key +# for cephx authentication. +# This maps to Ceph configuration option +# "key". (Since 3.0) +# # @server: Monitor host address and port. This maps # to the "mon_host" Ceph option. # @@ -3216,6 +3221,7 @@ '*snapshot': 'str', '*user': 'str', '*auth-client-required': ['RbdAuthMode'], + '*key-secret': 'str', '*server': ['InetSocketAddressBase'] } } ## From a7aff6dd10b16b67e8b142d0c94c5d92c3fe88f6 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 13 Jun 2018 11:01:30 +0200 Subject: [PATCH 1248/2380] block: Remove deprecated -drive geometry options The -drive options cyls, heads, secs and trans were deprecated in QEMU 2.10. It's time to remove them. hd-geo-test tested both the old version with geometry options in -drive and the new one with -device. Therefore the code using -drive doesn't have to be replaced there, we just need to remove the -drive test cases. This in turn allows some simplification of the code. Signed-off-by: Kevin Wolf Reviewed-by: Markus Armbruster --- blockdev.c | 75 +-------------------------------------- hmp-commands.hx | 1 - hw/block/block.c | 14 -------- include/sysemu/blockdev.h | 1 - qemu-doc.texi | 5 --- qemu-options.hx | 7 +--- tests/hd-geo-test.c | 37 ++++--------------- 7 files changed, 9 insertions(+), 131 deletions(-) diff --git a/blockdev.c b/blockdev.c index c24e261e37..bc9f34810f 100644 --- a/blockdev.c +++ b/blockdev.c @@ -730,22 +730,6 @@ QemuOptsList qemu_legacy_drive_opts = { .name = "if", .type = QEMU_OPT_STRING, .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", - },{ - .name = "cyls", - .type = QEMU_OPT_NUMBER, - .help = "number of cylinders (ide disk geometry)", - },{ - .name = "heads", - .type = QEMU_OPT_NUMBER, - .help = "number of heads (ide disk geometry)", - },{ - .name = "secs", - .type = QEMU_OPT_NUMBER, - .help = "number of sectors (ide disk geometry)", - },{ - .name = "trans", - .type = QEMU_OPT_STRING, - .help = "chs translation (auto, lba, none)", },{ .name = "addr", .type = QEMU_OPT_STRING, @@ -792,7 +776,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) QemuOpts *legacy_opts; DriveMediaType media = MEDIA_DISK; BlockInterfaceType type; - int cyls, heads, secs, translation; int max_devs, bus_id, unit_id, index; const char *devaddr; const char *werror, *rerror; @@ -803,7 +786,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) Error *local_err = NULL; int i; const char *deprecated[] = { - "serial", "trans", "secs", "heads", "cyls", "addr" + "serial", "addr" }; /* Change legacy command line options into QMP ones */ @@ -932,57 +915,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) type = block_default_type; } - /* Geometry */ - cyls = qemu_opt_get_number(legacy_opts, "cyls", 0); - heads = qemu_opt_get_number(legacy_opts, "heads", 0); - secs = qemu_opt_get_number(legacy_opts, "secs", 0); - - if (cyls || heads || secs) { - if (cyls < 1) { - error_report("invalid physical cyls number"); - goto fail; - } - if (heads < 1) { - error_report("invalid physical heads number"); - goto fail; - } - if (secs < 1) { - error_report("invalid physical secs number"); - goto fail; - } - } - - translation = BIOS_ATA_TRANSLATION_AUTO; - value = qemu_opt_get(legacy_opts, "trans"); - if (value != NULL) { - if (!cyls) { - error_report("'%s' trans must be used with cyls, heads and secs", - value); - goto fail; - } - if (!strcmp(value, "none")) { - translation = BIOS_ATA_TRANSLATION_NONE; - } else if (!strcmp(value, "lba")) { - translation = BIOS_ATA_TRANSLATION_LBA; - } else if (!strcmp(value, "large")) { - translation = BIOS_ATA_TRANSLATION_LARGE; - } else if (!strcmp(value, "rechs")) { - translation = BIOS_ATA_TRANSLATION_RECHS; - } else if (!strcmp(value, "auto")) { - translation = BIOS_ATA_TRANSLATION_AUTO; - } else { - error_report("'%s' invalid translation type", value); - goto fail; - } - } - - if (media == MEDIA_CDROM) { - if (cyls || secs || heads) { - error_report("CHS can't be set with media=cdrom"); - goto fail; - } - } - /* Device address specified by bus/unit or index. * If none was specified, try to find the first free one. */ bus_id = qemu_opt_get_number(legacy_opts, "bus", 0); @@ -1105,11 +1037,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) dinfo = g_malloc0(sizeof(*dinfo)); dinfo->opts = all_opts; - dinfo->cyls = cyls; - dinfo->heads = heads; - dinfo->secs = secs; - dinfo->trans = translation; - dinfo->type = type; dinfo->bus = bus_id; dinfo->unit = unit_id; diff --git a/hmp-commands.hx b/hmp-commands.hx index 0734fea931..0de7c4c29e 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1283,7 +1283,6 @@ ETEXI .params = "[-n] [[:]:]\n" "[file=file][,if=type][,bus=n]\n" "[,unit=m][,media=d][,index=i]\n" - "[,cyls=c,heads=h,secs=s[,trans=t]]\n" "[,snapshot=on|off][,cache=on|off]\n" "[,readonly=on|off][,copy-on-read=on|off]", .help = "add drive to PCI storage controller", diff --git a/hw/block/block.c b/hw/block/block.c index b91e2b6d7e..b6c80ab0b7 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -108,20 +108,6 @@ bool blkconf_geometry(BlockConf *conf, int *ptrans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp) { - DriveInfo *dinfo; - - if (!conf->cyls && !conf->heads && !conf->secs) { - /* try to fall back to value set with legacy -drive cyls=... */ - dinfo = blk_legacy_dinfo(conf->blk); - if (dinfo) { - conf->cyls = dinfo->cyls; - conf->heads = dinfo->heads; - conf->secs = dinfo->secs; - if (ptrans) { - *ptrans = dinfo->trans; - } - } - } if (!conf->cyls && !conf->heads && !conf->secs) { hd_geometry_guess(conf->blk, &conf->cyls, &conf->heads, &conf->secs, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index ac22f2ae1f..37ea39719e 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -35,7 +35,6 @@ struct DriveInfo { int auto_del; /* see blockdev_mark_auto_del() */ bool is_default; /* Added by default_drive() ? */ int media_cd; - int cyls, heads, secs, trans; QemuOpts *opts; char *serial; QTAILQ_ENTRY(DriveInfo) next; diff --git a/qemu-doc.texi b/qemu-doc.texi index cd05760cac..ab95bffc74 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2850,11 +2850,6 @@ with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' (for embedded NICs). The new syntax allows different settings to be provided per NIC. -@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) - -The drive geometry arguments are replaced by the the geometry arguments -that can be specified with the ``-device'' parameter. - @subsection -drive serial=... (since 2.10.0) The drive serial argument is replaced by the the serial argument diff --git a/qemu-options.hx b/qemu-options.hx index c0d3951e9f..a14b9655c5 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -804,9 +804,8 @@ ETEXI DEF("drive", HAS_ARG, QEMU_OPTION_drive, "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" - " [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n" " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" - " [,serial=s][,addr=A][,rerror=ignore|stop|report]\n" + " [,snapshot=on|off][,serial=s][,addr=A][,rerror=ignore|stop|report]\n" " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" " [,readonly=on|off][,copy-on-read=on|off]\n" " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" @@ -847,10 +846,6 @@ This option defines where is connected the drive by using an index in the list of available connectors of a given interface type. @item media=@var{media} This option defines the type of the media: disk or cdrom. -@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}] -Force disk physical geometry and the optional BIOS translation (trans=none or -lba). These parameters are deprecated, use the corresponding parameters -of @code{-device} instead. @item snapshot=@var{snapshot} @var{snapshot} is "on" or "off" and controls snapshot mode for the given drive (see @option{-snapshot}). diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c index 24870b38f4..ce665f1f83 100644 --- a/tests/hd-geo-test.c +++ b/tests/hd-geo-test.c @@ -201,7 +201,7 @@ static void setup_mbr(int img_idx, MBRcontents mbr) static int setup_ide(int argc, char *argv[], int argv_sz, int ide_idx, const char *dev, int img_idx, - MBRcontents mbr, const char *opts) + MBRcontents mbr) { char *s1, *s2, *s3; @@ -216,7 +216,7 @@ static int setup_ide(int argc, char *argv[], int argv_sz, s3 = g_strdup(",media=cdrom"); } argc = append_arg(argc, argv, argv_sz, - g_strdup_printf("%s%s%s%s", s1, s2, s3, opts)); + g_strdup_printf("%s%s%s", s1, s2, s3)); g_free(s1); g_free(s2); g_free(s3); @@ -260,7 +260,7 @@ static void test_ide_mbr(bool use_device, MBRcontents mbr) for (i = 0; i < backend_last; i++) { cur_ide[i] = &hd_chst[i][mbr]; dev = use_device ? (is_hd(cur_ide[i]) ? "ide-hd" : "ide-cd") : NULL; - argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr, ""); + argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr); } args = g_strjoinv(" ", argv); qtest_start(args); @@ -327,16 +327,12 @@ static void test_ide_drive_user(const char *dev, bool trans) const CHST expected_chst = { secs / (4 * 32) , 4, 32, trans }; argc = setup_common(argv, ARGV_SIZE); - opts = g_strdup_printf("%s,%s%scyls=%d,heads=%d,secs=%d", - dev ?: "", - trans && dev ? "bios-chs-" : "", - trans ? "trans=lba," : "", + opts = g_strdup_printf("%s,%scyls=%d,heads=%d,secs=%d", + dev, trans ? "bios-chs-trans=lba," : "", expected_chst.cyls, expected_chst.heads, expected_chst.secs); cur_ide[0] = &expected_chst; - argc = setup_ide(argc, argv, ARGV_SIZE, - 0, dev ? opts : NULL, backend_small, mbr_chs, - dev ? "" : opts); + argc = setup_ide(argc, argv, ARGV_SIZE, 0, opts, backend_small, mbr_chs); g_free(opts); args = g_strjoinv(" ", argv); qtest_start(args); @@ -346,22 +342,6 @@ static void test_ide_drive_user(const char *dev, bool trans) qtest_end(); } -/* - * Test case: IDE device (if=ide) with explicit CHS - */ -static void test_ide_drive_user_chs(void) -{ - test_ide_drive_user(NULL, false); -} - -/* - * Test case: IDE device (if=ide) with explicit CHS and translation - */ -static void test_ide_drive_user_chst(void) -{ - test_ide_drive_user(NULL, true); -} - /* * Test case: IDE device (if=none) with explicit CHS */ @@ -392,8 +372,7 @@ static void test_ide_drive_cd_0(void) for (i = 0; i <= backend_empty; i++) { ide_idx = backend_empty - i; cur_ide[ide_idx] = &hd_chst[i][mbr_blank]; - argc = setup_ide(argc, argv, ARGV_SIZE, - ide_idx, NULL, i, mbr_blank, ""); + argc = setup_ide(argc, argv, ARGV_SIZE, ide_idx, NULL, i, mbr_blank); } args = g_strjoinv(" ", argv); qtest_start(args); @@ -422,8 +401,6 @@ int main(int argc, char **argv) qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); - qtest_add_func("hd-geo/ide/drive/user/chs", test_ide_drive_user_chs); - qtest_add_func("hd-geo/ide/drive/user/chst", test_ide_drive_user_chst); qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); From eae3bd1eb7c6b105d30ec06008b3bc3dfc5f45bb Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 13 Jun 2018 11:01:30 +0200 Subject: [PATCH 1249/2380] block: Remove deprecated -drive option addr The -drive option addr was deprecated in QEMU 2.10. It's time to remove it. Signed-off-by: Kevin Wolf Reviewed-by: Markus Armbruster Reviewed-by: Jeff Cody --- blockdev.c | 17 +---------------- device-hotplug.c | 4 ---- include/sysemu/blockdev.h | 1 - qemu-doc.texi | 5 ----- qemu-options.hx | 5 +---- 5 files changed, 2 insertions(+), 30 deletions(-) diff --git a/blockdev.c b/blockdev.c index bc9f34810f..2984e400c2 100644 --- a/blockdev.c +++ b/blockdev.c @@ -730,10 +730,6 @@ QemuOptsList qemu_legacy_drive_opts = { .name = "if", .type = QEMU_OPT_STRING, .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", - },{ - .name = "addr", - .type = QEMU_OPT_STRING, - .help = "pci address (virtio only)", },{ .name = "serial", .type = QEMU_OPT_STRING, @@ -777,7 +773,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) DriveMediaType media = MEDIA_DISK; BlockInterfaceType type; int max_devs, bus_id, unit_id, index; - const char *devaddr; const char *werror, *rerror; bool read_only = false; bool copy_on_read; @@ -786,7 +781,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) Error *local_err = NULL; int i; const char *deprecated[] = { - "serial", "addr" + "serial" }; /* Change legacy command line options into QMP ones */ @@ -976,12 +971,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) } /* Add virtio block device */ - devaddr = qemu_opt_get(legacy_opts, "addr"); - if (devaddr && type != IF_VIRTIO) { - error_report("addr is not supported by this bus type"); - goto fail; - } - if (type == IF_VIRTIO) { QemuOpts *devopts; devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, @@ -993,9 +982,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) } qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), &error_abort); - if (devaddr) { - qemu_opt_set(devopts, "addr", devaddr, &error_abort); - } } filename = qemu_opt_get(legacy_opts, "file"); @@ -1040,7 +1026,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) dinfo->type = type; dinfo->bus = bus_id; dinfo->unit = unit_id; - dinfo->devaddr = devaddr; dinfo->serial = g_strdup(serial); blk_set_legacy_dinfo(blk, dinfo); diff --git a/device-hotplug.c b/device-hotplug.c index 23fd6656f1..cd427e2c76 100644 --- a/device-hotplug.c +++ b/device-hotplug.c @@ -69,10 +69,6 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict) if (!dinfo) { goto err; } - if (dinfo->devaddr) { - monitor_printf(mon, "Parameter addr not supported\n"); - goto err; - } switch (dinfo->type) { case IF_NONE: diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 37ea39719e..c0ae3700ec 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -28,7 +28,6 @@ typedef enum { } BlockInterfaceType; struct DriveInfo { - const char *devaddr; BlockInterfaceType type; int bus; int unit; diff --git a/qemu-doc.texi b/qemu-doc.texi index ab95bffc74..338477725f 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2855,11 +2855,6 @@ provided per NIC. The drive serial argument is replaced by the the serial argument that can be specified with the ``-device'' parameter. -@subsection -drive addr=... (since 2.10.0) - -The drive addr argument is replaced by the the addr argument -that can be specified with the ``-device'' parameter. - @subsection -usbdevice (since 2.10.0) The ``-usbdevice DEV'' argument is now a synonym for setting diff --git a/qemu-options.hx b/qemu-options.hx index a14b9655c5..c2531e2f3c 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -805,7 +805,7 @@ ETEXI DEF("drive", HAS_ARG, QEMU_OPTION_drive, "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" - " [,snapshot=on|off][,serial=s][,addr=A][,rerror=ignore|stop|report]\n" + " [,snapshot=on|off][,serial=s][,rerror=ignore|stop|report]\n" " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" " [,readonly=on|off][,copy-on-read=on|off]\n" " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" @@ -883,9 +883,6 @@ an untrusted format header. This option specifies the serial number to assign to the device. This parameter is deprecated, use the corresponding parameter of @code{-device} instead. -@item addr=@var{addr} -Specify the controller's PCI address (if=virtio only). This parameter is -deprecated, use the corresponding parameter of @code{-device} instead. @item werror=@var{action},rerror=@var{action} Specify which @var{action} to take on write and read errors. Valid actions are: "ignore" (ignore the error and try to continue), "stop" (pause QEMU), From b0083267444a5e0f28391f6c2831a539f878d424 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 13 Jun 2018 11:01:30 +0200 Subject: [PATCH 1250/2380] block: Remove deprecated -drive option serial The -drive option serial was deprecated in QEMU 2.10. It's time to remove it. Tests need to be updated to set the serial number with -global instead of using the -drive option. Signed-off-by: Kevin Wolf Reviewed-by: Markus Armbruster Reviewed-by: Jeff Cody --- block/block-backend.c | 1 - blockdev.c | 10 ---------- hw/block/block.c | 13 ------------- hw/block/nvme.c | 1 - hw/block/virtio-blk.c | 1 - hw/ide/qdev.c | 1 - hw/scsi/scsi-disk.c | 1 - hw/usb/dev-storage.c | 1 - include/hw/block/block.h | 1 - include/sysemu/blockdev.h | 1 - qemu-doc.texi | 5 ----- qemu-options.hx | 6 +----- tests/ahci-test.c | 6 +++--- tests/ide-test.c | 8 ++++---- 14 files changed, 8 insertions(+), 48 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index d55c328736..2d1a3463e8 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -419,7 +419,6 @@ static void drive_info_del(DriveInfo *dinfo) return; } qemu_opts_del(dinfo->opts); - g_free(dinfo->serial); g_free(dinfo); } diff --git a/blockdev.c b/blockdev.c index 2984e400c2..d1ab425085 100644 --- a/blockdev.c +++ b/blockdev.c @@ -730,10 +730,6 @@ QemuOptsList qemu_legacy_drive_opts = { .name = "if", .type = QEMU_OPT_STRING, .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "disk serial number", },{ .name = "file", .type = QEMU_OPT_STRING, @@ -776,12 +772,10 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) const char *werror, *rerror; bool read_only = false; bool copy_on_read; - const char *serial; const char *filename; Error *local_err = NULL; int i; const char *deprecated[] = { - "serial" }; /* Change legacy command line options into QMP ones */ @@ -949,9 +943,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) goto fail; } - /* Serial number */ - serial = qemu_opt_get(legacy_opts, "serial"); - /* no id supplied -> create one */ if (qemu_opts_id(all_opts) == NULL) { char *new_id; @@ -1026,7 +1017,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) dinfo->type = type; dinfo->bus = bus_id; dinfo->unit = unit_id; - dinfo->serial = g_strdup(serial); blk_set_legacy_dinfo(blk, dinfo); diff --git a/hw/block/block.c b/hw/block/block.c index b6c80ab0b7..cf0eb826f1 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -15,19 +15,6 @@ #include "qapi/qapi-types-block.h" #include "qemu/error-report.h" -void blkconf_serial(BlockConf *conf, char **serial) -{ - DriveInfo *dinfo; - - if (!*serial) { - /* try to fall back to value set with legacy -drive serial=... */ - dinfo = blk_legacy_dinfo(conf->blk); - if (dinfo) { - *serial = g_strdup(dinfo->serial); - } - } -} - void blkconf_blocksizes(BlockConf *conf) { BlockBackend *blk = conf->blk; diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 811084b6a7..d5bf95b79b 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -1215,7 +1215,6 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) return; } - blkconf_serial(&n->conf, &n->serial); if (!n->serial) { error_setg(errp, "serial property not set"); return; diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 50b5c869e3..225fe44b7a 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -935,7 +935,6 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) return; } - blkconf_serial(&conf->conf, &conf->serial); if (!blkconf_apply_backend_options(&conf->conf, blk_is_read_only(conf->conf.blk), true, errp)) { diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index f395d24592..573b022e1e 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -188,7 +188,6 @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp) return; } - blkconf_serial(&dev->conf, &dev->serial); if (kind != IDE_CD) { if (!blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, errp)) { diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index ded23d36ca..aeaf611854 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2368,7 +2368,6 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) return; } - blkconf_serial(&s->qdev.conf, &s->serial); blkconf_blocksizes(&s->qdev.conf); if (s->qdev.conf.logical_block_size > diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 481694a473..47b992f403 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -606,7 +606,6 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) return; } - blkconf_serial(&s->conf, &dev->serial); blkconf_blocksizes(&s->conf); if (!blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true, errp)) { diff --git a/include/hw/block/block.h b/include/hw/block/block.h index d4f4dfffab..e9f9e2223f 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -72,7 +72,6 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) /* Configuration helpers */ -void blkconf_serial(BlockConf *conf, char **serial); bool blkconf_geometry(BlockConf *conf, int *trans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp); diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index c0ae3700ec..24954b94e0 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -35,7 +35,6 @@ struct DriveInfo { bool is_default; /* Added by default_drive() ? */ int media_cd; QemuOpts *opts; - char *serial; QTAILQ_ENTRY(DriveInfo) next; }; diff --git a/qemu-doc.texi b/qemu-doc.texi index 338477725f..282bc3dc35 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2850,11 +2850,6 @@ with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' (for embedded NICs). The new syntax allows different settings to be provided per NIC. -@subsection -drive serial=... (since 2.10.0) - -The drive serial argument is replaced by the the serial argument -that can be specified with the ``-device'' parameter. - @subsection -usbdevice (since 2.10.0) The ``-usbdevice DEV'' argument is now a synonym for setting diff --git a/qemu-options.hx b/qemu-options.hx index c2531e2f3c..d5b0c26e8e 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -805,7 +805,7 @@ ETEXI DEF("drive", HAS_ARG, QEMU_OPTION_drive, "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" - " [,snapshot=on|off][,serial=s][,rerror=ignore|stop|report]\n" + " [,snapshot=on|off][,rerror=ignore|stop|report]\n" " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" " [,readonly=on|off][,copy-on-read=on|off]\n" " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" @@ -879,10 +879,6 @@ The default mode is @option{cache=writeback}. Specify which disk @var{format} will be used rather than detecting the format. Can be used to specify format=raw to avoid interpreting an untrusted format header. -@item serial=@var{serial} -This option specifies the serial number to assign to the device. This -parameter is deprecated, use the corresponding parameter of @code{-device} -instead. @item werror=@var{action},rerror=@var{action} Specify which @var{action} to take on write and read errors. Valid actions are: "ignore" (ignore the error and try to continue), "stop" (pause QEMU), diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 1a7b761304..937ed2f910 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -180,12 +180,12 @@ static AHCIQState *ahci_boot(const char *cli, ...) s = ahci_vboot(cli, ap); va_end(ap); } else { - cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" - ",format=%s" + cli = "-drive if=none,id=drive0,file=%s,cache=writeback,format=%s" " -M q35 " "-device ide-hd,drive=drive0 " + "-global ide-hd.serial=%s " "-global ide-hd.ver=%s"; - s = ahci_boot(cli, tmp_path, "testdisk", imgfmt, "version"); + s = ahci_boot(cli, tmp_path, imgfmt, "testdisk", "version"); } return s; diff --git a/tests/ide-test.c b/tests/ide-test.c index 2384c2c3e2..f39431b1a9 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -529,8 +529,8 @@ static void test_bmdma_no_busmaster(void) static void test_bmdma_setup(void) { ide_test_start( - "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " - "-global ide-hd.ver=%s", + "-drive file=%s,if=ide,cache=writeback,format=raw " + "-global ide-hd.serial=%s -global ide-hd.ver=%s", tmp_path, "testdisk", "version"); qtest_irq_intercept_in(global_qtest, "ioapic"); } @@ -561,8 +561,8 @@ static void test_identify(void) int ret; ide_test_start( - "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " - "-global ide-hd.ver=%s", + "-drive file=%s,if=ide,cache=writeback,format=raw " + "-global ide-hd.serial=%s -global ide-hd.ver=%s", tmp_path, "testdisk", "version"); dev = get_pci_device(&bmdma_bar, &ide_bar); From 6266e900b8083945cb766b45c124fb3c42932cb3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 13 Jun 2018 11:01:30 +0200 Subject: [PATCH 1251/2380] block: Remove dead deprecation warning code We removed all options from the 'deprecated' array, so the code is dead and can be removed as well. Signed-off-by: Kevin Wolf Reviewed-by: Markus Armbruster --- blockdev.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/blockdev.c b/blockdev.c index d1ab425085..7f65cd7497 100644 --- a/blockdev.c +++ b/blockdev.c @@ -775,8 +775,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) const char *filename; Error *local_err = NULL; int i; - const char *deprecated[] = { - }; /* Change legacy command line options into QMP ones */ static const struct { @@ -853,16 +851,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) goto fail; } - /* Other deprecated options */ - if (!qtest_enabled()) { - for (i = 0; i < ARRAY_SIZE(deprecated); i++) { - if (qemu_opt_get(legacy_opts, deprecated[i]) != NULL) { - error_report("'%s' is deprecated, please use the corresponding " - "option of '-device' instead", deprecated[i]); - } - } - } - /* Media type */ value = qemu_opt_get(legacy_opts, "media"); if (value) { From ea134caa08ca86689a56665f30bebeb38c5cd1ab Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Fri, 8 Jun 2018 10:39:09 +0200 Subject: [PATCH 1252/2380] typedefs: add QJSON Since commit 83ee768d6247b, we now have two places that define the QJSON type: $ git grep 'typedef struct QJSON QJSON' include/migration/vmstate.h:typedef struct QJSON QJSON; migration/qjson.h:typedef struct QJSON QJSON; This breaks docker-test-build@centos6: In file included from /tmp/qemu-test/src/migration/savevm.c:59: /tmp/qemu-test/src/migration/qjson.h:16: error: redefinition of typedef 'QJSON' /tmp/qemu-test/src/include/migration/vmstate.h:30: note: previous declaration of 'QJSON' was here make: *** [migration/savevm.o] Error 1 This happens because CentOS 6 has an old GCC 4.4.7. Even if redefining a typedef with the same type is permitted since GCC 4.6, unless -pedantic is passed, we don't really need to do that on purpose. Let's have a single definition in instead. Signed-off-by: Greg Kurz Message-Id: <152844714981.11789.3657734445739553287.stgit@bahia.lan> Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Dr. David Alan Gilbert --- include/migration/vmstate.h | 2 -- include/qemu/typedefs.h | 1 + migration/qjson.h | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 3747110f95..42b946ce90 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -27,8 +27,6 @@ #ifndef QEMU_VMSTATE_H #define QEMU_VMSTATE_H -typedef struct QJSON QJSON; - typedef struct VMStateInfo VMStateInfo; typedef struct VMStateDescription VMStateDescription; typedef struct VMStateField VMStateField; diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 325c72de33..3ec0e13a96 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -97,6 +97,7 @@ typedef struct QEMUTimer QEMUTimer; typedef struct QEMUTimerListGroup QEMUTimerListGroup; typedef struct QBool QBool; typedef struct QDict QDict; +typedef struct QJSON QJSON; typedef struct QList QList; typedef struct QNull QNull; typedef struct QNum QNum; diff --git a/migration/qjson.h b/migration/qjson.h index 2978b5f371..41664f2d71 100644 --- a/migration/qjson.h +++ b/migration/qjson.h @@ -13,8 +13,6 @@ #ifndef QEMU_QJSON_H #define QEMU_QJSON_H -typedef struct QJSON QJSON; - QJSON *qjson_new(void); void qjson_destroy(QJSON *json); void json_prop_str(QJSON *json, const char *name, const char *str); From ff0769a4adf52299504642882efba83ca1340a80 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Tue, 5 Jun 2018 17:25:44 +0100 Subject: [PATCH 1253/2380] migration: Fixes for non-migratable RAMBlocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are still a few cases where migration code is using the macros and functions that do all RAMBlocks rather than just the migratable blocks; fix those up. Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180605162545.80778-2-dgilbert@redhat.com> Reviewed-by: Peter Xu Reviewed-by: Cédric Le Goater Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 4 ++-- migration/rdma.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index a500015a2f..a7807cea84 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2516,7 +2516,7 @@ static void ram_state_resume_prepare(RAMState *rs, QEMUFile *out) * about dirty page logging as well. */ - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { pages += bitmap_count_one(block->bmap, block->used_length >> TARGET_PAGE_BITS); } @@ -3431,7 +3431,7 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) trace_ram_dirty_bitmap_sync_start(); - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { qemu_savevm_send_recv_bitmap(file, block->idstr); trace_ram_dirty_bitmap_request(block->idstr); ramblock_count++; diff --git a/migration/rdma.c b/migration/rdma.c index 05aee3d591..8bd7159059 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -635,7 +635,7 @@ static int qemu_rdma_init_ram_blocks(RDMAContext *rdma) assert(rdma->blockmap == NULL); memset(local, 0, sizeof *local); - qemu_ram_foreach_block(qemu_rdma_init_one_block, rdma); + qemu_ram_foreach_migratable_block(qemu_rdma_init_one_block, rdma); trace_qemu_rdma_init_ram_blocks(local->nb_blocks); rdma->dest_blocks = g_new0(RDMADestBlock, rdma->local_ram_blocks.nb_blocks); From 343f632c70ab401b1343140c3b19c9fb99395323 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Tue, 5 Jun 2018 17:25:45 +0100 Subject: [PATCH 1254/2380] migration: Poison ramblock loops in migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The migration code should be using the RAMBLOCK_FOREACH_MIGRATABLE and qemu_ram_foreach_block_migratable not the all-block versions; poison them so that we can't accidentally use them. Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180605162545.80778-3-dgilbert@redhat.com> Reviewed-by: Peter Xu Reviewed-by: Cédric Le Goater Signed-off-by: Dr. David Alan Gilbert --- include/exec/ramlist.h | 4 +++- migration/migration.h | 3 +++ migration/ram.c | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/exec/ramlist.h b/include/exec/ramlist.h index 2e2ac6cb99..bc4faa1b00 100644 --- a/include/exec/ramlist.h +++ b/include/exec/ramlist.h @@ -56,8 +56,10 @@ typedef struct RAMList { extern RAMList ram_list; /* Should be holding either ram_list.mutex, or the RCU lock. */ -#define RAMBLOCK_FOREACH(block) \ +#define INTERNAL_RAMBLOCK_FOREACH(block) \ QLIST_FOREACH_RCU(block, &ram_list.blocks, next) +/* Never use the INTERNAL_ version except for defining other macros */ +#define RAMBLOCK_FOREACH(block) INTERNAL_RAMBLOCK_FOREACH(block) void qemu_mutex_lock_ramlist(void); void qemu_mutex_unlock_ramlist(void); diff --git a/migration/migration.h b/migration/migration.h index 5af57d616c..31d3ed12dc 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -284,4 +284,7 @@ void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value); void dirty_bitmap_mig_before_vm_start(void); void init_dirty_bitmap_incoming_migration(void); +#define qemu_ram_foreach_block \ + #warning "Use qemu_ram_foreach_block_migratable in migration code" + #endif diff --git a/migration/ram.c b/migration/ram.c index a7807cea84..e0d19305ee 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -159,9 +159,11 @@ out: /* Should be holding either ram_list.mutex, or the RCU lock. */ #define RAMBLOCK_FOREACH_MIGRATABLE(block) \ - RAMBLOCK_FOREACH(block) \ + INTERNAL_RAMBLOCK_FOREACH(block) \ if (!qemu_ram_is_migratable(block)) {} else +#undef RAMBLOCK_FOREACH + static void ramblock_recv_map_init(void) { RAMBlock *rb; From a36f6ff46f115672cf86d0e1e7cdb1c2fa4d304b Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 30 May 2018 14:24:24 +0300 Subject: [PATCH 1255/2380] migration/block-dirty-bitmap: fix dirty_bitmap_load dirty_bitmap_load_header return code is obtained but not handled. Fix this. Bug was introduced in b35ebdf076d697bc "migration: add postcopy migration of dirty bitmaps" with the whole function. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180530112424.204835-1-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Reviewed-by: John Snow Signed-off-by: Dr. David Alan Gilbert --- migration/block-dirty-bitmap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index eeccaff34b..3bafbbdc4c 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -672,6 +672,9 @@ static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id) do { ret = dirty_bitmap_load_header(f, &s); + if (ret < 0) { + return ret; + } if (s.flags & DIRTY_BITMAP_MIG_FLAG_START) { ret = dirty_bitmap_load_start(f, &s); From e0e7a45d7fa64563191d3723fc4a4570122e6e42 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Mon, 4 Jun 2018 17:55:11 +0800 Subject: [PATCH 1256/2380] migration: fix counting xbzrle cache_miss_rate Sync up xbzrle_cache_miss_prev only after migration iteration goes forward Signed-off-by: Xiao Guangrong Message-Id: <20180604095520.8563-4-xiaoguangrong@tencent.com> Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index e0d19305ee..d273a19699 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1200,9 +1200,9 @@ static void migration_bitmap_sync(RAMState *rs) (double)(xbzrle_counters.cache_miss - rs->xbzrle_cache_miss_prev) / (rs->iterations - rs->iterations_prev); + rs->xbzrle_cache_miss_prev = xbzrle_counters.cache_miss; } rs->iterations_prev = rs->iterations; - rs->xbzrle_cache_miss_prev = xbzrle_counters.cache_miss; } /* reset period counters */ From b734035b61c0943c11bc591549d81de3ed7c0029 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Mon, 4 Jun 2018 17:55:12 +0800 Subject: [PATCH 1257/2380] migration: introduce migration_update_rates It is used to slightly clean the code up, no logic is changed Signed-off-by: Xiao Guangrong Message-Id: <20180604095520.8563-5-xiaoguangrong@tencent.com> Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index d273a19699..77071a43ed 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1141,6 +1141,25 @@ uint64_t ram_pagesize_summary(void) return summary; } +static void migration_update_rates(RAMState *rs, int64_t end_time) +{ + uint64_t iter_count = rs->iterations - rs->iterations_prev; + + /* calculate period counters */ + ram_counters.dirty_pages_rate = rs->num_dirty_pages_period * 1000 + / (end_time - rs->time_last_bitmap_sync); + + if (!iter_count) { + return; + } + + if (migrate_use_xbzrle()) { + xbzrle_counters.cache_miss_rate = (double)(xbzrle_counters.cache_miss - + rs->xbzrle_cache_miss_prev) / iter_count; + rs->xbzrle_cache_miss_prev = xbzrle_counters.cache_miss; + } +} + static void migration_bitmap_sync(RAMState *rs) { RAMBlock *block; @@ -1170,9 +1189,6 @@ static void migration_bitmap_sync(RAMState *rs) /* more than 1 second = 1000 millisecons */ if (end_time > rs->time_last_bitmap_sync + 1000) { - /* calculate period counters */ - ram_counters.dirty_pages_rate = rs->num_dirty_pages_period * 1000 - / (end_time - rs->time_last_bitmap_sync); bytes_xfer_now = ram_counters.transferred; /* During block migration the auto-converge logic incorrectly detects @@ -1194,16 +1210,9 @@ static void migration_bitmap_sync(RAMState *rs) } } - if (migrate_use_xbzrle()) { - if (rs->iterations_prev != rs->iterations) { - xbzrle_counters.cache_miss_rate = - (double)(xbzrle_counters.cache_miss - - rs->xbzrle_cache_miss_prev) / - (rs->iterations - rs->iterations_prev); - rs->xbzrle_cache_miss_prev = xbzrle_counters.cache_miss; - } - rs->iterations_prev = rs->iterations; - } + migration_update_rates(rs, end_time); + + rs->iterations_prev = rs->iterations; /* reset period counters */ rs->time_last_bitmap_sync = end_time; From 7e555c6c58f095c9286814c3f9cd06196feba8a1 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 13 Jun 2018 11:26:40 +0100 Subject: [PATCH 1258/2380] migration/postcopy: Add max-postcopy-bandwidth parameter Limit the background transfer bandwidth during the postcopy phase to the value set on this new parameter. The default, 0, corresponds to the existing behaviour which is unlimited bandwidth. Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180613102642.23995-2-dgilbert@redhat.com> Reviewed-by: Peter Xu Signed-off-by: Dr. David Alan Gilbert --- hmp.c | 7 +++++++ migration/migration.c | 35 ++++++++++++++++++++++++++++++++++- qapi/migration.json | 19 ++++++++++++++++--- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/hmp.c b/hmp.c index ef93f4878b..f40d8279cf 100644 --- a/hmp.c +++ b/hmp.c @@ -370,6 +370,9 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %" PRIu64 "\n", MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), params->xbzrle_cache_size); + monitor_printf(mon, "%s: %" PRIu64 "\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH), + params->max_postcopy_bandwidth); } qapi_free_MigrationParameters(params); @@ -1676,6 +1679,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) } p->xbzrle_cache_size = cache_size; break; + case MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH: + p->has_max_postcopy_bandwidth = true; + visit_type_size(v, param, &p->max_postcopy_bandwidth, &err); + break; default: assert(0); } diff --git a/migration/migration.c b/migration/migration.c index 1e99ec9b7e..3a50d4c35c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -82,6 +82,11 @@ #define DEFAULT_MIGRATE_MULTIFD_CHANNELS 2 #define DEFAULT_MIGRATE_MULTIFD_PAGE_COUNT 16 +/* Background transfer rate for postcopy, 0 means unlimited, note + * that page requests can still exceed this limit. + */ +#define DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH 0 + static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); @@ -659,6 +664,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->x_multifd_page_count = s->parameters.x_multifd_page_count; params->has_xbzrle_cache_size = true; params->xbzrle_cache_size = s->parameters.xbzrle_cache_size; + params->has_max_postcopy_bandwidth = true; + params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth; return params; } @@ -1066,6 +1073,9 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_xbzrle_cache_size) { dest->xbzrle_cache_size = params->xbzrle_cache_size; } + if (params->has_max_postcopy_bandwidth) { + dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth; + } } static void migrate_params_apply(MigrateSetParameters *params, Error **errp) @@ -1138,6 +1148,9 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) s->parameters.xbzrle_cache_size = params->xbzrle_cache_size; xbzrle_cache_resize(params->xbzrle_cache_size, errp); } + if (params->has_max_postcopy_bandwidth) { + s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth; + } } void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) @@ -1887,6 +1900,16 @@ int64_t migrate_xbzrle_cache_size(void) return s->parameters.xbzrle_cache_size; } +static int64_t migrate_max_postcopy_bandwidth(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->parameters.max_postcopy_bandwidth; +} + + bool migrate_use_block(void) { MigrationState *s; @@ -2226,6 +2249,7 @@ static int postcopy_start(MigrationState *ms) QIOChannelBuffer *bioc; QEMUFile *fb; int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + int64_t bandwidth = migrate_max_postcopy_bandwidth(); bool restart_block = false; int cur_state = MIGRATION_STATUS_ACTIVE; if (!migrate_pause_before_switchover()) { @@ -2280,7 +2304,12 @@ static int postcopy_start(MigrationState *ms) * will notice we're in POSTCOPY_ACTIVE and not actually * wrap their state up here */ - qemu_file_set_rate_limit(ms->to_dst_file, INT64_MAX); + /* 0 max-postcopy-bandwidth means unlimited */ + if (!bandwidth) { + qemu_file_set_rate_limit(ms->to_dst_file, INT64_MAX); + } else { + qemu_file_set_rate_limit(ms->to_dst_file, bandwidth / XFER_LIMIT_RATIO); + } if (migrate_postcopy_ram()) { /* Ping just for debugging, helps line traces up */ qemu_savevm_send_ping(ms->to_dst_file, 2); @@ -3042,6 +3071,9 @@ static Property migration_properties[] = { DEFINE_PROP_SIZE("xbzrle-cache-size", MigrationState, parameters.xbzrle_cache_size, DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE), + DEFINE_PROP_SIZE("max-postcopy-bandwidth", MigrationState, + parameters.max_postcopy_bandwidth, + DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH), /* Migration capabilities */ DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), @@ -3110,6 +3142,7 @@ static void migration_instance_init(Object *obj) params->has_x_multifd_channels = true; params->has_x_multifd_page_count = true; params->has_xbzrle_cache_size = true; + params->has_max_postcopy_bandwidth = true; qemu_sem_init(&ms->postcopy_pause_sem, 0); qemu_sem_init(&ms->postcopy_pause_rp_sem, 0); diff --git a/qapi/migration.json b/qapi/migration.json index f7e10ee90f..1b4c1db670 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -517,6 +517,9 @@ # and a power of 2 # (Since 2.11) # +# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. +# Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # Since: 2.4 ## { 'enum': 'MigrationParameter', @@ -525,7 +528,7 @@ 'tls-creds', 'tls-hostname', 'max-bandwidth', 'downtime-limit', 'x-checkpoint-delay', 'block-incremental', 'x-multifd-channels', 'x-multifd-page-count', - 'xbzrle-cache-size' ] } + 'xbzrle-cache-size', 'max-postcopy-bandwidth' ] } ## # @MigrateSetParameters: @@ -593,6 +596,10 @@ # needs to be a multiple of the target page size # and a power of 2 # (Since 2.11) +# +# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. +# Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # Since: 2.4 ## # TODO either fuse back into MigrationParameters, or make @@ -611,7 +618,8 @@ '*block-incremental': 'bool', '*x-multifd-channels': 'int', '*x-multifd-page-count': 'int', - '*xbzrle-cache-size': 'size' } } + '*xbzrle-cache-size': 'size', + '*max-postcopy-bandwidth': 'size' } } ## # @migrate-set-parameters: @@ -694,6 +702,10 @@ # needs to be a multiple of the target page size # and a power of 2 # (Since 2.11) +# +# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. +# Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # Since: 2.4 ## { 'struct': 'MigrationParameters', @@ -710,7 +722,8 @@ '*block-incremental': 'bool' , '*x-multifd-channels': 'uint8', '*x-multifd-page-count': 'uint32', - '*xbzrle-cache-size': 'size' } } + '*xbzrle-cache-size': 'size', + '*max-postcopy-bandwidth': 'size' } } ## # @query-migrate-parameters: From ad767bed5af559ed32c6dabdcaccc3f75731e1f4 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 13 Jun 2018 11:26:41 +0100 Subject: [PATCH 1259/2380] migration: Wake rate limiting for urgent requests Rate limiting sleeps the migration thread for a while when it runs out of bandwidth; but sometimes we want to wake up to get on with something more urgent (like a postcopy request). Here we use a semaphore with a timedwait instead of a simple sleep; Incrementing the sempahore will wake it up sooner. Anything that consumes these urgent events must decrement the sempahore. Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180613102642.23995-3-dgilbert@redhat.com> Reviewed-by: Peter Xu Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 35 +++++++++++++++++++++++++++++++---- migration/migration.h | 8 ++++++++ migration/trace-events | 2 ++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 3a50d4c35c..108c3d7142 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2852,6 +2852,16 @@ static void migration_iteration_finish(MigrationState *s) qemu_mutex_unlock_iothread(); } +void migration_make_urgent_request(void) +{ + qemu_sem_post(&migrate_get_current()->rate_limit_sem); +} + +void migration_consume_urgent_request(void) +{ + qemu_sem_wait(&migrate_get_current()->rate_limit_sem); +} + /* * Master migration thread on the source VM. * It drives the migration and pumps the data down the outgoing channel. @@ -2861,6 +2871,7 @@ static void *migration_thread(void *opaque) MigrationState *s = opaque; int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST); MigThrError thr_error; + bool urgent = false; rcu_register_thread(); @@ -2901,7 +2912,7 @@ static void *migration_thread(void *opaque) s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { int64_t current_time; - if (!qemu_file_rate_limit(s->to_dst_file)) { + if (urgent || !qemu_file_rate_limit(s->to_dst_file)) { MigIterateState iter_state = migration_iteration_run(s); if (iter_state == MIG_ITERATE_SKIP) { continue; @@ -2932,10 +2943,24 @@ static void *migration_thread(void *opaque) migration_update_counters(s, current_time); + urgent = false; if (qemu_file_rate_limit(s->to_dst_file)) { - /* usleep expects microseconds */ - g_usleep((s->iteration_start_time + BUFFER_DELAY - - current_time) * 1000); + /* Wait for a delay to do rate limiting OR + * something urgent to post the semaphore. + */ + int ms = s->iteration_start_time + BUFFER_DELAY - current_time; + trace_migration_thread_ratelimit_pre(ms); + if (qemu_sem_timedwait(&s->rate_limit_sem, ms) == 0) { + /* We were worken by one or more urgent things but + * the timedwait will have consumed one of them. + * The service routine for the urgent wake will dec + * the semaphore itself for each item it consumes, + * so add this one we just eat back. + */ + qemu_sem_post(&s->rate_limit_sem); + urgent = true; + } + trace_migration_thread_ratelimit_post(urgent); } } @@ -3109,6 +3134,7 @@ static void migration_instance_finalize(Object *obj) qemu_mutex_destroy(&ms->qemu_file_lock); g_free(params->tls_hostname); g_free(params->tls_creds); + qemu_sem_destroy(&ms->rate_limit_sem); qemu_sem_destroy(&ms->pause_sem); qemu_sem_destroy(&ms->postcopy_pause_sem); qemu_sem_destroy(&ms->postcopy_pause_rp_sem); @@ -3147,6 +3173,7 @@ static void migration_instance_init(Object *obj) qemu_sem_init(&ms->postcopy_pause_sem, 0); qemu_sem_init(&ms->postcopy_pause_rp_sem, 0); qemu_sem_init(&ms->rp_state.rp_sem, 0); + qemu_sem_init(&ms->rate_limit_sem, 0); qemu_mutex_init(&ms->qemu_file_lock); } diff --git a/migration/migration.h b/migration/migration.h index 31d3ed12dc..64a7b33735 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -121,6 +121,11 @@ struct MigrationState */ QemuMutex qemu_file_lock; + /* + * Used to allow urgent requests to override rate limiting. + */ + QemuSemaphore rate_limit_sem; + /* bytes already send at the beggining of current interation */ uint64_t iteration_initial_bytes; /* time at the start of current iteration */ @@ -287,4 +292,7 @@ void init_dirty_bitmap_incoming_migration(void); #define qemu_ram_foreach_block \ #warning "Use qemu_ram_foreach_block_migratable in migration code" +void migration_make_urgent_request(void); +void migration_consume_urgent_request(void); + #endif diff --git a/migration/trace-events b/migration/trace-events index 4a768eaaeb..3f67758893 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -108,6 +108,8 @@ migration_return_path_end_before(void) "" migration_return_path_end_after(int rp_error) "%d" migration_thread_after_loop(void) "" migration_thread_file_err(void) "" +migration_thread_ratelimit_pre(int ms) "%d ms" +migration_thread_ratelimit_post(int urgent) "urgent: %d" migration_thread_setup_complete(void) "" open_return_path_on_source(void) "" open_return_path_on_source_continue(void) "" From e03a34f8f36e3b357d7a0c38e881fe183727b7ef Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 13 Jun 2018 11:26:42 +0100 Subject: [PATCH 1260/2380] migration/postcopy: Wake rate limit sleep on postcopy request Use the 'urgent request' mechanism added in the previous patch for entries added to the postcopy request queue for RAM. Ignore the rate limiting while we have requests. Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180613102642.23995-4-dgilbert@redhat.com> Reviewed-by: Peter Xu Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index 77071a43ed..225b201aff 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1547,6 +1547,7 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) memory_region_unref(block->mr); QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req); g_free(entry); + migration_consume_urgent_request(); } } qemu_mutex_unlock(&rs->src_page_req_mutex); @@ -1695,6 +1696,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) memory_region_ref(ramblock->mr); qemu_mutex_lock(&rs->src_page_req_mutex); QSIMPLEQ_INSERT_TAIL(&rs->src_page_requests, new_entry, next_req); + migration_make_urgent_request(); qemu_mutex_unlock(&rs->src_page_req_mutex); rcu_read_unlock(); @@ -2643,9 +2645,14 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); i = 0; - while ((ret = qemu_file_rate_limit(f)) == 0) { + while ((ret = qemu_file_rate_limit(f)) == 0 || + !QSIMPLEQ_EMPTY(&rs->src_page_requests)) { int pages; + if (qemu_file_get_error(f)) { + break; + } + pages = ram_find_and_save_block(rs, false); /* no more pages to sent */ if (pages == 0) { From 650af8907bd567db914b7ce3a7e9df4c323f4619 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Tue, 12 Jun 2018 14:20:09 +0530 Subject: [PATCH 1261/2380] migration: calculate expected_downtime with ram_bytes_remaining() expected_downtime value is not accurate with dirty_pages_rate * page_size, using ram_bytes_remaining() would yeild it resonable. consider to read the remaining ram just after having updated the dirty pages count later migration_bitmap_sync_range() in migration_bitmap_sync() and reuse the `remaining` field in ram_counters to hold ram_bytes_remaining() for calculating expected_downtime. Reported-by: Michael Roth Signed-off-by: Balamuruhan S Signed-off-by: Laurent Vivier Message-Id: <20180612085009.17594-2-bala24@linux.vnet.ibm.com> Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 3 +-- migration/ram.c | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 108c3d7142..e1eaa97df4 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2746,8 +2746,7 @@ static void migration_update_counters(MigrationState *s, * recalculate. 10000 is a small enough number for our purposes */ if (ram_counters.dirty_pages_rate && transferred > 10000) { - s->expected_downtime = ram_counters.dirty_pages_rate * - qemu_target_page_size() / bandwidth; + s->expected_downtime = ram_counters.remaining / bandwidth; } qemu_file_reset_rate_limit(s->to_dst_file); diff --git a/migration/ram.c b/migration/ram.c index 225b201aff..cd5f55117d 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1180,6 +1180,7 @@ static void migration_bitmap_sync(RAMState *rs) RAMBLOCK_FOREACH_MIGRATABLE(block) { migration_bitmap_sync_range(rs, block, 0, block->used_length); } + ram_counters.remaining = ram_bytes_remaining(); rcu_read_unlock(); qemu_mutex_unlock(&rs->bitmap_mutex); From 1dcf3675196a1cec616ce71b067d9498590a60a6 Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1262/2380] arm_gicv3_kvm: kvm_dist_get/put_priority: skip the registers banked by GICR_IPRIORITYR While for_each_dist_irq_reg loop starts from GIC_INTERNAL, it forgot to offset the date array and index. This will overlap the GICR registers value and leave the last GIC_INTERNAL irq's registers out of update. Fixes: 367b9f527becdd20ddf116e17a3c0c2bbc486920 Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Reviewed-by: Eric Auger Signed-off-by: Shannon Zhao Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 5649cac46e..d8d3b25403 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -135,7 +135,14 @@ static void kvm_dist_get_priority(GICv3State *s, uint32_t offset, uint8_t *bmp) uint32_t reg, *field; int irq; - field = (uint32_t *)bmp; + /* For the KVM GICv3, affinity routing is always enabled, and the first 8 + * GICD_IPRIORITYR registers are always RAZ/WI. The corresponding + * functionality is replaced by GICR_IPRIORITYR. It doesn't need to + * sync them. So it needs to skip the field of GIC_INTERNAL irqs in bmp and + * offset. + */ + field = (uint32_t *)(bmp + GIC_INTERNAL); + offset += (GIC_INTERNAL * 8) / 8; for_each_dist_irq_reg(irq, s->num_irq, 8) { kvm_gicd_access(s, offset, ®, false); *field = reg; @@ -149,7 +156,14 @@ static void kvm_dist_put_priority(GICv3State *s, uint32_t offset, uint8_t *bmp) uint32_t reg, *field; int irq; - field = (uint32_t *)bmp; + /* For the KVM GICv3, affinity routing is always enabled, and the first 8 + * GICD_IPRIORITYR registers are always RAZ/WI. The corresponding + * functionality is replaced by GICR_IPRIORITYR. It doesn't need to + * sync them. So it needs to skip the field of GIC_INTERNAL irqs in bmp and + * offset. + */ + field = (uint32_t *)(bmp + GIC_INTERNAL); + offset += (GIC_INTERNAL * 8) / 8; for_each_dist_irq_reg(irq, s->num_irq, 8) { reg = *field; kvm_gicd_access(s, offset, ®, true); From 519655e62590f8eaed1a17d159b70be24d74967e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1263/2380] hw/arm/mps2-tz: Put ethernet controller behind PPC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ethernet controller in the AN505 MPC FPGA image is behind the same AHB Peripheral Protection Controller that handles the graphics and GPIOs. (In the documentation this is clear in the block diagram but the ethernet controller was omitted from the table listing devices connected to the PPC.) The ethernet sits behind AHB PPCEXP0 interface 5. We had incorrectly claimed that this was a "gpio4", but there are only 4 GPIOs in this image. Correct the QEMU model to match the hardware. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell Message-id: 20180515171446.10834-1-peter.maydell@linaro.org --- hw/arm/mps2-tz.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 8dc8bfd4ab..c5ef95e4cc 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -74,12 +74,13 @@ typedef struct { UnimplementedDeviceState spi[5]; UnimplementedDeviceState i2c[4]; UnimplementedDeviceState i2s_audio; - UnimplementedDeviceState gpio[5]; + UnimplementedDeviceState gpio[4]; UnimplementedDeviceState dma[4]; UnimplementedDeviceState gfx; CMSDKAPBUART uart[5]; SplitIRQ sec_resp_splitter; qemu_or_irq uart_irq_orgate; + DeviceState *lan9118; } MPS2TZMachineState; #define TYPE_MPS2TZ_MACHINE "mps2tz" @@ -224,6 +225,26 @@ static MemoryRegion *make_fpgaio(MPS2TZMachineState *mms, void *opaque, return sysbus_mmio_get_region(SYS_BUS_DEVICE(fpgaio), 0); } +static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + SysBusDevice *s; + DeviceState *iotkitdev = DEVICE(&mms->iotkit); + NICInfo *nd = &nd_table[0]; + + /* In hardware this is a LAN9220; the LAN9118 is software compatible + * except that it doesn't support the checksum-offload feature. + */ + qemu_check_nic_model(nd, "lan9118"); + mms->lan9118 = qdev_create(NULL, "lan9118"); + qdev_set_nic_properties(mms->lan9118, nd); + qdev_init_nofail(mms->lan9118); + + s = SYS_BUS_DEVICE(mms->lan9118); + sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16)); + return sysbus_mmio_get_region(s, 0); +} + static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -363,7 +384,7 @@ static void mps2tz_common_init(MachineState *machine) { "gpio1", make_unimp_dev, &mms->gpio[1], 0x40101000, 0x1000 }, { "gpio2", make_unimp_dev, &mms->gpio[2], 0x40102000, 0x1000 }, { "gpio3", make_unimp_dev, &mms->gpio[3], 0x40103000, 0x1000 }, - { "gpio4", make_unimp_dev, &mms->gpio[4], 0x40104000, 0x1000 }, + { "eth", make_eth_dev, NULL, 0x42000000, 0x100000 }, }, }, { .name = "ahb_ppcexp1", @@ -447,13 +468,6 @@ static void mps2tz_common_init(MachineState *machine) "cfg_sec_resp", 0)); } - /* In hardware this is a LAN9220; the LAN9118 is software compatible - * except that it doesn't support the checksum-offload feature. - * The ethernet controller is not behind a PPC. - */ - lan9118_init(&nd_table[0], 0x42000000, - qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16)); - create_unimplemented_device("FPGA NS PC", 0x48007000, 0x1000); armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000); From d2af524a18dae38691cd13e1aa70d133e9c645b7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1264/2380] hw/sh/sh7750: Convert away from old_mmio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the sh7750 device away from using the old_mmio field of MemoryRegionOps. This device is used by the sh4 r2d board. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180601141223.26630-2-peter.maydell@linaro.org --- hw/sh4/sh7750.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 2dc07a904b..2fb6e618d9 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -450,15 +450,43 @@ static void sh7750_mem_writel(void *opaque, hwaddr addr, } } +static uint64_t sh7750_mem_readfn(void *opaque, hwaddr addr, unsigned size) +{ + switch (size) { + case 1: + return sh7750_mem_readb(opaque, addr); + case 2: + return sh7750_mem_readw(opaque, addr); + case 4: + return sh7750_mem_readl(opaque, addr); + default: + g_assert_not_reached(); + } +} + +static void sh7750_mem_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + switch (size) { + case 1: + sh7750_mem_writeb(opaque, addr, value); + break; + case 2: + sh7750_mem_writew(opaque, addr, value); + break; + case 4: + sh7750_mem_writel(opaque, addr, value); + break; + default: + g_assert_not_reached(); + } +} + static const MemoryRegionOps sh7750_mem_ops = { - .old_mmio = { - .read = {sh7750_mem_readb, - sh7750_mem_readw, - sh7750_mem_readl }, - .write = {sh7750_mem_writeb, - sh7750_mem_writew, - sh7750_mem_writel }, - }, + .read = sh7750_mem_readfn, + .write = sh7750_mem_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; From bb428791c8bf9ffed6940e15c6c255c28ea74e72 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1265/2380] hw/m68k/mcf5206: Convert away from old_mmio Convert the mcf5206 device away from using the old_mmio field of MemoryRegionOps. This device is used by the an5206 board. Signed-off-by: Peter Maydell Acked-by: Thomas Huth Message-id: 20180601141223.26630-3-peter.maydell@linaro.org --- hw/m68k/mcf5206.c | 48 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index 7abd84ac47..d7f26d6810 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -512,19 +512,43 @@ static void m5206_mbar_writel(void *opaque, hwaddr offset, m5206_mbar_write(s, offset, value, 4); } +static uint64_t m5206_mbar_readfn(void *opaque, hwaddr addr, unsigned size) +{ + switch (size) { + case 1: + return m5206_mbar_readb(opaque, addr); + case 2: + return m5206_mbar_readw(opaque, addr); + case 4: + return m5206_mbar_readl(opaque, addr); + default: + g_assert_not_reached(); + } +} + +static void m5206_mbar_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + switch (size) { + case 1: + m5206_mbar_writeb(opaque, addr, value); + break; + case 2: + m5206_mbar_writew(opaque, addr, value); + break; + case 4: + m5206_mbar_writel(opaque, addr, value); + break; + default: + g_assert_not_reached(); + } +} + static const MemoryRegionOps m5206_mbar_ops = { - .old_mmio = { - .read = { - m5206_mbar_readb, - m5206_mbar_readw, - m5206_mbar_readl, - }, - .write = { - m5206_mbar_writeb, - m5206_mbar_writew, - m5206_mbar_writel, - }, - }, + .read = m5206_mbar_readfn, + .write = m5206_mbar_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; From a4afb28dae411dae1cc6e5b0d28afd818609cb3d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1266/2380] hw/block/pflash_cfi02: Convert away from old_mmio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the pflash_cfi02 device away from using the old_mmio field of MemoryRegionOps. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Acked-by: Max Reitz Message-id: 20180601141223.26630-4-peter.maydell@linaro.org --- hw/block/pflash_cfi02.c | 97 ++++++++--------------------------------- 1 file changed, 18 insertions(+), 79 deletions(-) diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index a8b3f7f978..6c18e5e578 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -493,102 +493,41 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, pfl->cmd = 0; } - -static uint32_t pflash_readb_be(void *opaque, hwaddr addr) +static uint64_t pflash_be_readfn(void *opaque, hwaddr addr, unsigned size) { - return pflash_read(opaque, addr, 1, 1); + return pflash_read(opaque, addr, size, 1); } -static uint32_t pflash_readb_le(void *opaque, hwaddr addr) +static void pflash_be_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { - return pflash_read(opaque, addr, 1, 0); + pflash_write(opaque, addr, value, size, 1); } -static uint32_t pflash_readw_be(void *opaque, hwaddr addr) +static uint64_t pflash_le_readfn(void *opaque, hwaddr addr, unsigned size) { - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 2, 1); + return pflash_read(opaque, addr, size, 0); } -static uint32_t pflash_readw_le(void *opaque, hwaddr addr) +static void pflash_le_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 2, 0); -} - -static uint32_t pflash_readl_be(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 4, 1); -} - -static uint32_t pflash_readl_le(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 4, 0); -} - -static void pflash_writeb_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_write(opaque, addr, value, 1, 1); -} - -static void pflash_writeb_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_write(opaque, addr, value, 1, 0); -} - -static void pflash_writew_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 2, 1); -} - -static void pflash_writew_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 2, 0); -} - -static void pflash_writel_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 4, 1); -} - -static void pflash_writel_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 4, 0); + pflash_write(opaque, addr, value, size, 0); } static const MemoryRegionOps pflash_cfi02_ops_be = { - .old_mmio = { - .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, }, - .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, }, - }, + .read = pflash_be_readfn, + .write = pflash_be_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; static const MemoryRegionOps pflash_cfi02_ops_le = { - .old_mmio = { - .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, }, - .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, }, - }, + .read = pflash_le_readfn, + .write = pflash_le_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; From a821541edf2bf8e73d6a5a4d9f80fec2b4110db2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1267/2380] hw/watchdog/wdt_i6300esb: Convert away from old_mmio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the wdt_i6300esb device away from using the old_mmio field of MemoryRegionOps. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180601141223.26630-5-peter.maydell@linaro.org --- hw/watchdog/wdt_i6300esb.c | 48 ++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index e596b0804d..7b59469888 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -361,19 +361,43 @@ static void i6300esb_mem_writel(void *vp, hwaddr addr, uint32_t val) } } +static uint64_t i6300esb_mem_readfn(void *opaque, hwaddr addr, unsigned size) +{ + switch (size) { + case 1: + return i6300esb_mem_readb(opaque, addr); + case 2: + return i6300esb_mem_readw(opaque, addr); + case 4: + return i6300esb_mem_readl(opaque, addr); + default: + g_assert_not_reached(); + } +} + +static void i6300esb_mem_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + switch (size) { + case 1: + i6300esb_mem_writeb(opaque, addr, value); + break; + case 2: + i6300esb_mem_writew(opaque, addr, value); + break; + case 4: + i6300esb_mem_writel(opaque, addr, value); + break; + default: + g_assert_not_reached(); + } +} + static const MemoryRegionOps i6300esb_ops = { - .old_mmio = { - .read = { - i6300esb_mem_readb, - i6300esb_mem_readw, - i6300esb_mem_readl, - }, - .write = { - i6300esb_mem_writeb, - i6300esb_mem_writew, - i6300esb_mem_writel, - }, - }, + .read = i6300esb_mem_readfn, + .write = i6300esb_mem_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_LITTLE_ENDIAN, }; From 5876503c0fb4728ae875e30fe9da82c09ba38ddd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1268/2380] hw/input/pckbd: Convert away from old_mmio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the pckbd device away from using the old_mmio field of MemoryRegionOps. This change only affects the memory-mapped variant of the i8042, which is used by the Unicore32 'puv3' board and the MIPS Jazz boards 'magnum' and 'pica61'. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180601141223.26630-6-peter.maydell@linaro.org --- hw/input/pckbd.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index f17f18e51b..f33e3fc63d 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -434,7 +434,7 @@ static const VMStateDescription vmstate_kbd = { }; /* Memory mapped interface */ -static uint32_t kbd_mm_readb (void *opaque, hwaddr addr) +static uint64_t kbd_mm_readfn(void *opaque, hwaddr addr, unsigned size) { KBDState *s = opaque; @@ -444,7 +444,8 @@ static uint32_t kbd_mm_readb (void *opaque, hwaddr addr) return kbd_read_data(s, 0, 1) & 0xff; } -static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value) +static void kbd_mm_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { KBDState *s = opaque; @@ -454,12 +455,13 @@ static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value) kbd_write_data(s, 0, value & 0xff, 1); } + static const MemoryRegionOps i8042_mmio_ops = { + .read = kbd_mm_readfn, + .write = kbd_mm_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, - .old_mmio = { - .read = { kbd_mm_readb, kbd_mm_readb, kbd_mm_readb }, - .write = { kbd_mm_writeb, kbd_mm_writeb, kbd_mm_writeb }, - }, }; void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, From 05b4940bf1d6f6b32169d233562fafde1789cdbe Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1269/2380] hw/char/parallel: Convert away from old_mmio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the parallel device away from using the old_mmio field of MemoryRegionOps. This change only affects the memory-mapped variant, which is used by the MIPS Jazz boards 'magnum' and 'pica61'. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180601141223.26630-7-peter.maydell@linaro.org --- hw/char/parallel.c | 50 ++++++++++------------------------------------ 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 1542d62201..35748e6c1b 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -554,56 +554,28 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) } /* Memory mapped interface */ -static uint32_t parallel_mm_readb (void *opaque, hwaddr addr) +static uint64_t parallel_mm_readfn(void *opaque, hwaddr addr, unsigned size) { ParallelState *s = opaque; - return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFF; + return parallel_ioport_read_sw(s, addr >> s->it_shift) & + MAKE_64BIT_MASK(0, size * 8); } -static void parallel_mm_writeb (void *opaque, - hwaddr addr, uint32_t value) +static void parallel_mm_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { ParallelState *s = opaque; - parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFF); -} - -static uint32_t parallel_mm_readw (void *opaque, hwaddr addr) -{ - ParallelState *s = opaque; - - return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFFFF; -} - -static void parallel_mm_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - ParallelState *s = opaque; - - parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFFFF); -} - -static uint32_t parallel_mm_readl (void *opaque, hwaddr addr) -{ - ParallelState *s = opaque; - - return parallel_ioport_read_sw(s, addr >> s->it_shift); -} - -static void parallel_mm_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - ParallelState *s = opaque; - - parallel_ioport_write_sw(s, addr >> s->it_shift, value); + parallel_ioport_write_sw(s, addr >> s->it_shift, + value & MAKE_64BIT_MASK(0, size * 8)); } static const MemoryRegionOps parallel_mm_ops = { - .old_mmio = { - .read = { parallel_mm_readb, parallel_mm_readw, parallel_mm_readl }, - .write = { parallel_mm_writeb, parallel_mm_writew, parallel_mm_writel }, - }, + .read = parallel_mm_readfn, + .write = parallel_mm_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; From f04d44654d85e25f71e00d3295db03a4ea95d8d8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1270/2380] stellaris: Stop using armv7m_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stellaris board is still using the legacy armv7m_init() function, which predates conversion of the ARMv7M into a proper QOM container object. Make the board code directly create the ARMv7M object instead. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Message-id: 20180601144328.23817-2-peter.maydell@linaro.org --- hw/arm/stellaris.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 502a20842c..a8f1f6a912 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -20,6 +20,7 @@ #include "qemu/log.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" +#include "hw/arm/armv7m.h" #include "hw/char/pl011.h" #include "hw/misc/unimp.h" #include "cpu.h" @@ -1298,8 +1299,13 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) &error_fatal); memory_region_add_subregion(system_memory, 0x20000000, sram); - nvic = armv7m_init(system_memory, flash_size, NUM_IRQ_LINES, - ms->kernel_filename, ms->cpu_type); + nvic = qdev_create(NULL, TYPE_ARMV7M); + qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES); + qdev_prop_set_string(nvic, "cpu-type", ms->cpu_type); + object_property_set_link(OBJECT(nvic), OBJECT(get_system_memory()), + "memory", &error_abort); + /* This will exit with an error if the user passed us a bad cpu_type */ + qdev_init_nofail(nvic); qdev_connect_gpio_out_named(nvic, "SYSRESETREQ", 0, qemu_allocate_irq(&do_sys_reset, NULL, 0)); @@ -1431,6 +1437,8 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) create_unimplemented_device("analogue-comparator", 0x4003c000, 0x1000); create_unimplemented_device("hibernation", 0x400fc000, 0x1000); create_unimplemented_device("flash-control", 0x400fd000, 0x1000); + + armv7m_load_kernel(ARM_CPU(first_cpu), ms->kernel_filename, flash_size); } /* FIXME: Figure out how to generate these from stellaris_boards. */ From 38d81dafb316291356db9591e6752b27848b2ea4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1271/2380] hw/arm/armv7m: Remove unused armv7m_init() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the now-unused armv7m_init() function. This was a legacy from before we properly QOMified ARMv7M, and it has some flaws: * it combines work that needs to be done by an SoC object (creating and initializing the TYPE_ARMV7M object) with work that needs to be done by the board model (setting the system up to load the ELF file specified with -kernel) * TYPE_ARMV7M creation failure is fatal, but an SoC object wants to arrange to propagate the failure outward * it uses allocate-and-create via qdev_create() whereas the current preferred style for SoC objects is to do creation in-place Board and SoC models can instead do the two jobs this function was doing themselves, in the right places and with whatever their preferred style/error handling is. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Message-id: 20180601144328.23817-3-peter.maydell@linaro.org --- hw/arm/armv7m.c | 21 --------------------- include/hw/arm/arm.h | 8 ++------ 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index f123cc7d3d..a4ab7d2069 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -261,27 +261,6 @@ static void armv7m_reset(void *opaque) cpu_reset(CPU(cpu)); } -/* Init CPU and memory for a v7-M based board. - mem_size is in bytes. - Returns the ARMv7M device. */ - -DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, - const char *kernel_filename, const char *cpu_type) -{ - DeviceState *armv7m; - - armv7m = qdev_create(NULL, TYPE_ARMV7M); - qdev_prop_set_uint32(armv7m, "num-irq", num_irq); - qdev_prop_set_string(armv7m, "cpu-type", cpu_type); - object_property_set_link(OBJECT(armv7m), OBJECT(get_system_memory()), - "memory", &error_abort); - /* This will exit with an error if the user passed us a bad cpu_type */ - qdev_init_nofail(armv7m); - - armv7m_load_kernel(ARM_CPU(first_cpu), kernel_filename, mem_size); - return armv7m; -} - void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) { int image_size; diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index 70fa2287e2..ffed39252d 100644 --- a/include/hw/arm/arm.h +++ b/include/hw/arm/arm.h @@ -23,9 +23,6 @@ typedef enum { ARM_ENDIANNESS_BE32, } arm_endianness; -/* armv7m.c */ -DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, - const char *kernel_filename, const char *cpu_type); /** * armv7m_load_kernel: * @cpu: CPU @@ -33,9 +30,8 @@ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, * @mem_size: mem_size: maximum image size to load * * Load the guest image for an ARMv7M system. This must be called by - * any ARMv7M board, either directly or via armv7m_init(). (This is - * necessary to ensure that the CPU resets correctly on system reset, - * as well as for kernel loading.) + * any ARMv7M board. (This is necessary to ensure that the CPU resets + * correctly on system reset, as well as for kernel loading.) */ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size); From 95f875654ae8b433b50a2bc7858e34af957cbaa4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:13 +0100 Subject: [PATCH 1272/2380] arm: Don't crash if user tries to use a Cortex-M CPU without an NVIC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Cortex-M CPU and its NVIC are two intimately intertwined parts of the same hardware; it is not possible to use one without the other. Unfortunately a lot of our board models don't do any sanity checking on the CPU type the user asks for, so a command line like qemu-system-arm -M versatilepb -cpu cortex-m3 will create an M3 without an NVIC, and coredump immediately. In the other direction, trying a non-M-profile CPU in an M-profile board won't blow up, but doesn't do anything useful either: qemu-system-arm -M lm3s6965evb -cpu arm926 Add some checking in the NVIC and CPU realize functions that the user isn't trying to use an NVIC without an M-profile CPU or an M-profile CPU without an NVIC, so we can produce a helpful error message rather than a core dump. Fixes: https://bugs.launchpad.net/qemu/+bug/1766896 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180601160355.15393-1-peter.maydell@linaro.org --- hw/arm/armv7m.c | 7 ++++++- hw/intc/armv7m_nvic.c | 6 +++++- target/arm/cpu.c | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index a4ab7d2069..9e00d4037c 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -178,6 +178,12 @@ static void armv7m_realize(DeviceState *dev, Error **errp) return; } } + + /* Tell the CPU where the NVIC is; it will fail realize if it doesn't + * have one. + */ + s->cpu->env.nvic = &s->nvic; + object_property_set_bool(OBJECT(s->cpu), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); @@ -202,7 +208,6 @@ static void armv7m_realize(DeviceState *dev, Error **errp) sbd = SYS_BUS_DEVICE(&s->nvic); sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); - s->cpu->env.nvic = &s->nvic; memory_region_add_subregion(&s->container, 0xe000e000, sysbus_mmio_get_region(sbd, 0)); diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index c51151fa8a..661be8878a 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2183,7 +2183,11 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) int regionlen; s->cpu = ARM_CPU(qemu_get_cpu(0)); - assert(s->cpu); + + if (!s->cpu || !arm_feature(&s->cpu->env, ARM_FEATURE_M)) { + error_setg(errp, "The NVIC can only be used with a Cortex-M CPU"); + return; + } if (s->num_irq > NVIC_MAX_IRQ) { error_setg(errp, "num-irq %d exceeds NVIC maximum", s->num_irq); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ab047b9402..e1de45e904 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -767,6 +767,24 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) return; } +#ifndef CONFIG_USER_ONLY + /* The NVIC and M-profile CPU are two halves of a single piece of + * hardware; trying to use one without the other is a command line + * error and will result in segfaults if not caught here. + */ + if (arm_feature(env, ARM_FEATURE_M)) { + if (!env->nvic) { + error_setg(errp, "This board cannot be used with Cortex-M CPUs"); + return; + } + } else { + if (env->nvic) { + error_setg(errp, "This board can only be used with Cortex-M CPUs"); + return; + } + } +#endif + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); From f81804a52b5d4609f68ea367a55a2ccb4cc99f77 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1273/2380] hw/core/or-irq: Support more than 16 inputs to an OR gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the IoTKit MPC support, we need to wire together the interrupt outputs of 17 MPCs; this exceeds the current value of MAX_OR_LINES. Increase MAX_OR_LINES to 32 (which should be enough for anyone). The tricky part is retaining the migration compatibility for existing OR gates; we add a subsection which is only used for larger OR gates, and define it such that we can freely increase MAX_OR_LINES in future (or even move to a dynamically allocated levels[] array without an upper size limit) without breaking compatibility. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180604152941.20374-10-peter.maydell@linaro.org --- hw/core/or-irq.c | 39 +++++++++++++++++++++++++++++++++++++-- include/hw/or-irq.h | 5 ++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c index f9d76c4641..a86901b673 100644 --- a/hw/core/or-irq.c +++ b/hw/core/or-irq.c @@ -66,14 +66,49 @@ static void or_irq_init(Object *obj) qdev_init_gpio_out(DEVICE(obj), &s->out_irq, 1); } +/* The original version of this device had a fixed 16 entries in its + * VMState array; devices with more inputs than this need to + * migrate the extra lines via a subsection. + * The subsection migrates as much of the levels[] array as is needed + * (including repeating the first 16 elements), to avoid the awkwardness + * of splitting it in two to meet the requirements of VMSTATE_VARRAY_UINT16. + */ +#define OLD_MAX_OR_LINES 16 +#if MAX_OR_LINES < OLD_MAX_OR_LINES +#error MAX_OR_LINES must be at least 16 for migration compatibility +#endif + +static bool vmstate_extras_needed(void *opaque) +{ + qemu_or_irq *s = OR_IRQ(opaque); + + return s->num_lines >= OLD_MAX_OR_LINES; +} + +static const VMStateDescription vmstate_or_irq_extras = { + .name = "or-irq-extras", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_extras_needed, + .fields = (VMStateField[]) { + VMSTATE_VARRAY_UINT16_UNSAFE(levels, qemu_or_irq, num_lines, 0, + vmstate_info_bool, bool), + VMSTATE_END_OF_LIST(), + }, +}; + static const VMStateDescription vmstate_or_irq = { .name = TYPE_OR_IRQ, .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_BOOL_ARRAY(levels, qemu_or_irq, MAX_OR_LINES), + VMSTATE_BOOL_SUB_ARRAY(levels, qemu_or_irq, 0, OLD_MAX_OR_LINES), VMSTATE_END_OF_LIST(), - } + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_or_irq_extras, + NULL + }, }; static Property or_irq_properties[] = { diff --git a/include/hw/or-irq.h b/include/hw/or-irq.h index 3f6fc1b58a..5a31e5a188 100644 --- a/include/hw/or-irq.h +++ b/include/hw/or-irq.h @@ -31,7 +31,10 @@ #define TYPE_OR_IRQ "or-irq" -#define MAX_OR_LINES 16 +/* This can safely be increased if necessary without breaking + * migration compatibility (as long as it remains greater than 15). + */ +#define MAX_OR_LINES 32 typedef struct OrIRQState qemu_or_irq; From ace4109011b4912b24e76f152e2cf010e78819c5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1274/2380] cpu-defs.h: Document CPUIOTLBEntry 'addr' field The 'addr' field in the CPUIOTLBEntry struct has a rather non-obvious use; add a comment documenting it (reverse-engineered from what the code that sets it is doing). Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Richard Henderson Message-id: 20180611125633.32755-2-peter.maydell@linaro.org --- accel/tcg/cputlb.c | 12 ++++++++++++ include/exec/cpu-defs.h | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 05439039e9..31f7695cb2 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -664,6 +664,18 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; /* refill the tlb */ + /* + * At this point iotlb contains a physical section number in the lower + * TARGET_PAGE_BITS, and either + * + the ram_addr_t of the page base of the target RAM (if NOTDIRTY or ROM) + * + the offset within section->mr of the page base (otherwise) + * We subtract the vaddr (which is page aligned and thus won't + * disturb the low bits) to give an offset which can be added to the + * (non-page-aligned) vaddr of the eventual memory access to get + * the MemoryRegion offset for the access. Note that the vaddr we + * subtract here is that of the page base, and not the same as the + * vaddr we add back in io_readx()/io_writex()/get_page_addr_code(). + */ env->iotlb[mmu_idx][index].addr = iotlb - vaddr; env->iotlb[mmu_idx][index].attrs = attrs; diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index e43ff8346b..a171ffc1a4 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -127,6 +127,15 @@ QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); * structs into one.) */ typedef struct CPUIOTLBEntry { + /* + * @addr contains: + * - in the lower TARGET_PAGE_BITS, a physical section number + * - with the lower TARGET_PAGE_BITS masked off, an offset which + * must be added to the virtual address to obtain: + * + the ram_addr_t of the target RAM (if the physical section + * number is PHYS_SECTION_NOTDIRTY or PHYS_SECTION_ROM) + * + the offset within the target MemoryRegion (otherwise) + */ hwaddr addr; MemTxAttrs attrs; } CPUIOTLBEntry; From 2d54f19401bc54b3b56d1cc44c96e4087b604b97 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1275/2380] cputlb: Pass cpu_transaction_failed() the correct physaddr The API for cpu_transaction_failed() says that it takes the physical address for the failed transaction. However we were actually passing it the offset within the target MemoryRegion. We don't currently have any target CPU implementations of this hook that require the physical address; fix this bug so we don't get confused if we ever do add one. Suggested-by: Paolo Bonzini Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Richard Henderson Message-id: 20180611125633.32755-3-peter.maydell@linaro.org --- accel/tcg/cputlb.c | 44 +++++++++++++++++++++++++++++------------ exec.c | 5 +++-- include/exec/exec-all.h | 13 ++++++++++-- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 31f7695cb2..1768fcdc47 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -777,13 +777,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, target_ulong addr, uintptr_t retaddr, int size) { CPUState *cpu = ENV_GET_CPU(env); - hwaddr physaddr = iotlbentry->addr; - MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); + hwaddr mr_offset; + MemoryRegionSection *section; + MemoryRegion *mr; uint64_t val; bool locked = false; MemTxResult r; - physaddr = (physaddr & TARGET_PAGE_MASK) + addr; + section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); + mr = section->mr; + mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; cpu->mem_io_pc = retaddr; if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { cpu_io_recompile(cpu, retaddr); @@ -795,9 +798,13 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, qemu_mutex_lock_iothread(); locked = true; } - r = memory_region_dispatch_read(mr, physaddr, + r = memory_region_dispatch_read(mr, mr_offset, &val, size, iotlbentry->attrs); if (r != MEMTX_OK) { + hwaddr physaddr = mr_offset + + section->offset_within_address_space - + section->offset_within_region; + cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_LOAD, mmu_idx, iotlbentry->attrs, r, retaddr); } @@ -814,12 +821,15 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, uintptr_t retaddr, int size) { CPUState *cpu = ENV_GET_CPU(env); - hwaddr physaddr = iotlbentry->addr; - MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); + hwaddr mr_offset; + MemoryRegionSection *section; + MemoryRegion *mr; bool locked = false; MemTxResult r; - physaddr = (physaddr & TARGET_PAGE_MASK) + addr; + section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); + mr = section->mr; + mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { cpu_io_recompile(cpu, retaddr); } @@ -830,9 +840,13 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, qemu_mutex_lock_iothread(); locked = true; } - r = memory_region_dispatch_write(mr, physaddr, + r = memory_region_dispatch_write(mr, mr_offset, val, size, iotlbentry->attrs); if (r != MEMTX_OK) { + hwaddr physaddr = mr_offset + + section->offset_within_address_space - + section->offset_within_region; + cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_STORE, mmu_idx, iotlbentry->attrs, r, retaddr); } @@ -880,12 +894,13 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, */ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) { - int mmu_idx, index, pd; + int mmu_idx, index; void *p; MemoryRegion *mr; + MemoryRegionSection *section; CPUState *cpu = ENV_GET_CPU(env); CPUIOTLBEntry *iotlbentry; - hwaddr physaddr; + hwaddr physaddr, mr_offset; index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); mmu_idx = cpu_mmu_index(env, true); @@ -896,8 +911,8 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) } } iotlbentry = &env->iotlb[mmu_idx][index]; - pd = iotlbentry->addr & ~TARGET_PAGE_MASK; - mr = iotlb_to_region(cpu, pd, iotlbentry->attrs); + section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); + mr = section->mr; if (memory_region_is_unassigned(mr)) { qemu_mutex_lock_iothread(); if (memory_region_request_mmio_ptr(mr, addr)) { @@ -918,7 +933,10 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) * and use the MemTXResult it produced). However it is the * simplest place we have currently available for the check. */ - physaddr = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; + mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; + physaddr = mr_offset + + section->offset_within_address_space - + section->offset_within_region; cpu_transaction_failed(cpu, physaddr, addr, 0, MMU_INST_FETCH, mmu_idx, iotlbentry->attrs, MEMTX_DECODE_ERROR, 0); diff --git a/exec.c b/exec.c index f6645ede0c..9cbba6adcd 100644 --- a/exec.c +++ b/exec.c @@ -2897,14 +2897,15 @@ static const MemoryRegionOps readonly_mem_ops = { }, }; -MemoryRegion *iotlb_to_region(CPUState *cpu, hwaddr index, MemTxAttrs attrs) +MemoryRegionSection *iotlb_to_section(CPUState *cpu, + hwaddr index, MemTxAttrs attrs) { int asidx = cpu_asidx_from_attrs(cpu, attrs); CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx]; AddressSpaceDispatch *d = atomic_rcu_read(&cpuas->memory_dispatch); MemoryRegionSection *sections = d->map.sections; - return sections[index & ~TARGET_PAGE_MASK].mr; + return §ions[index & ~TARGET_PAGE_MASK]; } static void io_mem_init(void) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 4d09eaba72..aed55aaaa7 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -437,8 +437,17 @@ void tb_lock_reset(void); #if !defined(CONFIG_USER_ONLY) -struct MemoryRegion *iotlb_to_region(CPUState *cpu, - hwaddr index, MemTxAttrs attrs); +/** + * iotlb_to_section: + * @cpu: CPU performing the access + * @index: TCG CPU IOTLB entry + * + * Given a TCG CPU IOTLB entry, return the MemoryRegionSection that + * it refers to. @index will have been initially created and returned + * by memory_region_section_get_iotlb(). + */ +struct MemoryRegionSection *iotlb_to_section(CPUState *cpu, + hwaddr index, MemTxAttrs attrs); void tlb_fill(CPUState *cpu, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); From 2948f0cde3974185ad22d6721438cf85df852877 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1276/2380] CODING_STYLE: Define our preferred form for multiline comments The codebase has a bit of a mix of different multiline comment styles. State a preference for the Linux kernel style: /* * Star on the left for each line. * Leading slash-star and trailing star-slash * each go on a line of their own. */ Signed-off-by: Peter Maydell Reviewed-by: Eric Blake Reviewed-by: Cornelia Huck Reviewed-by: Markus Armbruster Reviewed-by: Alex Williamson Reviewed-by: Thomas Huth Reviewed-by: John Snow Reviewed-by: Stefan Hajnoczi Message-id: 20180611141716.3813-1-peter.maydell@linaro.org --- CODING_STYLE | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CODING_STYLE b/CODING_STYLE index 12ba58ee29..ec075dedc4 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -124,6 +124,23 @@ We use traditional C-style /* */ comments and avoid // comments. Rationale: The // form is valid in C99, so this is purely a matter of consistency of style. The checkpatch script will warn you about this. +Multiline comment blocks should have a row of stars on the left, +and the initial /* and terminating */ both on their own lines: + /* + * like + * this + */ +This is the same format required by the Linux kernel coding style. + +(Some of the existing comments in the codebase use the GNU Coding +Standards form which does not have stars on the left, or other +variations; avoid these when writing new comments, but don't worry +about converting to the preferred form unless you're editing that +comment anyway.) + +Rationale: Consistency, and ease of visually picking out a multiline +comment from the surrounding code. + 8. trace-events style 8.1 0x prefix From afa4f6653dca095f63f3fe7f2001e9334f5676c1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1277/2380] bswap: Add new stn_*_p() and ldn_*_p() memory access functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's a common pattern in QEMU where a function needs to perform a data load or store of an N byte integer in a particular endianness. At the moment this is handled by doing a switch() on the size and calling the appropriate ld*_p or st*_p function for each size. Provide a new family of functions ldn_*_p() and stn_*_p() which take the size as an argument and do the switch() themselves. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20180611171007.4165-2-peter.maydell@linaro.org --- docs/devel/loads-stores.rst | 15 +++++++++++ include/exec/cpu-all.h | 4 +++ include/qemu/bswap.h | 52 +++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index 6a990cc243..57d8c524bf 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -53,9 +53,24 @@ The ``_{endian}`` infix is omitted for target-endian accesses. The target endian accessors are only available to source files which are built per-target. +There are also functions which take the size as an argument: + +load: ``ldn{endian}_p(ptr, sz)`` + +which performs an unsigned load of ``sz`` bytes from ``ptr`` +as an ``{endian}`` order value and returns it in a uint64_t. + +store: ``stn{endian}_p(ptr, sz, val)`` + +which stores ``val`` to ``ptr`` as an ``{endian}`` order value +of size ``sz`` bytes. + + Regexes for git grep - ``\`` - ``\`` + - ``\`` + - ``\`` ``cpu_{ld,st}_*`` ~~~~~~~~~~~~~~~~~ diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index a635f532f9..7fa726b8e3 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -133,6 +133,8 @@ static inline void tswap64s(uint64_t *s) #define stq_p(p, v) stq_be_p(p, v) #define stfl_p(p, v) stfl_be_p(p, v) #define stfq_p(p, v) stfq_be_p(p, v) +#define ldn_p(p, sz) ldn_be_p(p, sz) +#define stn_p(p, sz, v) stn_be_p(p, sz, v) #else #define lduw_p(p) lduw_le_p(p) #define ldsw_p(p) ldsw_le_p(p) @@ -145,6 +147,8 @@ static inline void tswap64s(uint64_t *s) #define stq_p(p, v) stq_le_p(p, v) #define stfl_p(p, v) stfl_le_p(p, v) #define stfq_p(p, v) stfq_le_p(p, v) +#define ldn_p(p, sz) ldn_le_p(p, sz) +#define stn_p(p, sz, v) stn_le_p(p, sz, v) #endif /* MMU memory access macros */ diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 3f28f661b1..a684c1a7a2 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -290,6 +290,15 @@ typedef union { * For accessors that take a guest address rather than a * host address, see the cpu_{ld,st}_* accessors defined in * cpu_ldst.h. + * + * For cases where the size to be used is not fixed at compile time, + * there are + * stn{endian}_p(ptr, sz, val) + * which stores @val to @ptr as an @endian-order number @sz bytes in size + * and + * ldn{endian}_p(ptr, sz) + * which loads @sz bytes from @ptr as an unsigned @endian-order number + * and returns it in a uint64_t. */ static inline int ldub_p(const void *ptr) @@ -495,6 +504,49 @@ static inline unsigned long leul_to_cpu(unsigned long v) #endif } +/* Store v to p as a sz byte value in host order */ +#define DO_STN_LDN_P(END) \ + static inline void stn_## END ## _p(void *ptr, int sz, uint64_t v) \ + { \ + switch (sz) { \ + case 1: \ + stb_p(ptr, v); \ + break; \ + case 2: \ + stw_ ## END ## _p(ptr, v); \ + break; \ + case 4: \ + stl_ ## END ## _p(ptr, v); \ + break; \ + case 8: \ + stq_ ## END ## _p(ptr, v); \ + break; \ + default: \ + g_assert_not_reached(); \ + } \ + } \ + static inline uint64_t ldn_## END ## _p(const void *ptr, int sz) \ + { \ + switch (sz) { \ + case 1: \ + return ldub_p(ptr); \ + case 2: \ + return lduw_ ## END ## _p(ptr); \ + case 4: \ + return (uint32_t)ldl_ ## END ## _p(ptr); \ + case 8: \ + return ldq_ ## END ## _p(ptr); \ + default: \ + g_assert_not_reached(); \ + } \ + } + +DO_STN_LDN_P(he) +DO_STN_LDN_P(le) +DO_STN_LDN_P(be) + +#undef DO_STN_LDN_P + #undef le_bswap #undef be_bswap #undef le_bswaps From 22672c6075a16d1998e37686f02ed4bd2fb30f78 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1278/2380] exec.c: Don't accidentally sign-extend 4-byte loads in subpage_read() In subpage_read() we perform a load of the data into a local buffer which we then access using ldub_p(), lduw_p(), ldl_p() or ldq_p() depending on its size, storing the result into the uint64_t *data. Since ldl_p() returns an 'int', this means that for the 4-byte case we will sign-extend the data, whereas for 1 and 2 byte reads we zero-extend it. This ought not to matter since the caller will likely ignore values in the high bytes of the data, but add a cast so that we're consistent. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180611171007.4165-3-peter.maydell@linaro.org --- exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exec.c b/exec.c index 9cbba6adcd..90b47cde7b 100644 --- a/exec.c +++ b/exec.c @@ -2747,7 +2747,7 @@ static MemTxResult subpage_read(void *opaque, hwaddr addr, uint64_t *data, *data = lduw_p(buf); return MEMTX_OK; case 4: - *data = ldl_p(buf); + *data = (uint32_t)ldl_p(buf); return MEMTX_OK; case 8: *data = ldq_p(buf); From 6d3ede5410e05c5f6221dab1daf99164fd6bf879 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1279/2380] exec.c: Use stn_p() and ldn_p() instead of explicit switches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have stn_p() and ldn_p() we can use them in various functions in exec.c that used to have their own switch-on-size code. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20180611171007.4165-4-peter.maydell@linaro.org --- exec.c | 112 +++++---------------------------------------------------- 1 file changed, 8 insertions(+), 104 deletions(-) diff --git a/exec.c b/exec.c index 90b47cde7b..1fa2cdb874 100644 --- a/exec.c +++ b/exec.c @@ -2544,22 +2544,7 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, memory_notdirty_write_prepare(&ndi, current_cpu, current_cpu->mem_io_vaddr, ram_addr, size); - switch (size) { - case 1: - stb_p(qemu_map_ram_ptr(NULL, ram_addr), val); - break; - case 2: - stw_p(qemu_map_ram_ptr(NULL, ram_addr), val); - break; - case 4: - stl_p(qemu_map_ram_ptr(NULL, ram_addr), val); - break; - case 8: - stq_p(qemu_map_ram_ptr(NULL, ram_addr), val); - break; - default: - abort(); - } + stn_p(qemu_map_ram_ptr(NULL, ram_addr), size, val); memory_notdirty_write_complete(&ndi); } @@ -2739,22 +2724,8 @@ static MemTxResult subpage_read(void *opaque, hwaddr addr, uint64_t *data, if (res) { return res; } - switch (len) { - case 1: - *data = ldub_p(buf); - return MEMTX_OK; - case 2: - *data = lduw_p(buf); - return MEMTX_OK; - case 4: - *data = (uint32_t)ldl_p(buf); - return MEMTX_OK; - case 8: - *data = ldq_p(buf); - return MEMTX_OK; - default: - abort(); - } + *data = ldn_p(buf, len); + return MEMTX_OK; } static MemTxResult subpage_write(void *opaque, hwaddr addr, @@ -2768,22 +2739,7 @@ static MemTxResult subpage_write(void *opaque, hwaddr addr, " value %"PRIx64"\n", __func__, subpage, len, addr, value); #endif - switch (len) { - case 1: - stb_p(buf, value); - break; - case 2: - stw_p(buf, value); - break; - case 4: - stl_p(buf, value); - break; - case 8: - stq_p(buf, value); - break; - default: - abort(); - } + stn_p(buf, len, value); return flatview_write(subpage->fv, addr + subpage->base, attrs, buf, len); } @@ -3129,34 +3085,8 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, l = memory_access_size(mr, l, addr1); /* XXX: could force current_cpu to NULL to avoid potential bugs */ - switch (l) { - case 8: - /* 64 bit write access */ - val = ldq_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 8, - attrs); - break; - case 4: - /* 32 bit write access */ - val = (uint32_t)ldl_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 4, - attrs); - break; - case 2: - /* 16 bit write access */ - val = lduw_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 2, - attrs); - break; - case 1: - /* 8 bit write access */ - val = ldub_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 1, - attrs); - break; - default: - abort(); - } + val = ldn_p(buf, l); + result |= memory_region_dispatch_write(mr, addr1, val, l, attrs); } else { /* RAM case */ ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); @@ -3217,34 +3147,8 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, /* I/O case */ release_lock |= prepare_mmio_access(mr); l = memory_access_size(mr, l, addr1); - switch (l) { - case 8: - /* 64 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 8, - attrs); - stq_p(buf, val); - break; - case 4: - /* 32 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 4, - attrs); - stl_p(buf, val); - break; - case 2: - /* 16 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 2, - attrs); - stw_p(buf, val); - break; - case 1: - /* 8 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 1, - attrs); - stb_p(buf, val); - break; - default: - abort(); - } + result |= memory_region_dispatch_read(mr, addr1, &val, l, attrs); + stn_p(buf, l, val); } else { /* RAM case */ ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); From 66f2dbd783d0b6172043e3679171421b2d0bac11 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1280/2380] target/arm: Extend vec_reg_offset to larger sizes Rearrange the arithmetic so that we are agnostic about the total size of the vector and the size of the element. This will allow us to index up to the 32nd byte and with 16-byte elements. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.h | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/target/arm/translate-a64.h b/target/arm/translate-a64.h index dd9c09f89b..63d958cf50 100644 --- a/target/arm/translate-a64.h +++ b/target/arm/translate-a64.h @@ -67,18 +67,26 @@ static inline void assert_fp_access_checked(DisasContext *s) static inline int vec_reg_offset(DisasContext *s, int regno, int element, TCGMemOp size) { - int offs = 0; + int element_size = 1 << size; + int offs = element * element_size; #ifdef HOST_WORDS_BIGENDIAN /* This is complicated slightly because vfp.zregs[n].d[0] is - * still the low half and vfp.zregs[n].d[1] the high half - * of the 128 bit vector, even on big endian systems. - * Calculate the offset assuming a fully bigendian 128 bits, - * then XOR to account for the order of the two 64 bit halves. + * still the lowest and vfp.zregs[n].d[15] the highest of the + * 256 byte vector, even on big endian systems. + * + * Calculate the offset assuming fully little-endian, + * then XOR to account for the order of the 8-byte units. + * + * For 16 byte elements, the two 8 byte halves will not form a + * host int128 if the host is bigendian, since they're in the + * wrong order. However the only 16 byte operation we have is + * a move, so we can ignore this for the moment. More complicated + * operations will have to special case loading and storing from + * the zregs array. */ - offs += (16 - ((element + 1) * (1 << size))); - offs ^= 8; -#else - offs += element * (1 << size); + if (element_size < 8) { + offs ^= 8 - element_size; + } #endif offs += offsetof(CPUARMState, vfp.zregs[regno]); assert_fp_access_checked(s); From 30562ab716bcec0bf718b47b5268949856b17604 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1281/2380] target/arm: Implement SVE Permute - Unpredicated Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 23 +++++++ target/arm/sve.decode | 27 ++++++++ target/arm/sve_helper.c | 114 +++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 133 +++++++++++++++++++++++++++++++++++++ 4 files changed, 297 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 94f4356ce9..0c9aad575e 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -416,6 +416,29 @@ DEF_HELPER_FLAGS_4(sve_cpy_z_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_ext, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_insr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_insr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_insr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_insr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_3(sve_rev_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_rev_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_rev_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_rev_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_tbl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_tbl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_tbl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_tbl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_sunpk_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_sunpk_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_sunpk_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_uunpk_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uunpk_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uunpk_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 4761d1921e..7ffd7962c8 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -24,6 +24,7 @@ %imm4_16_p1 16:4 !function=plus1 %imm6_22_5 22:1 5:5 +%imm7_22_16 22:2 16:5 %imm8_16_10 16:5 10:3 %imm9_16_10 16:s6 10:3 @@ -85,6 +86,8 @@ # Three operand, vector element size @rd_rn_rm ........ esz:2 . rm:5 ... ... rn:5 rd:5 &rrr_esz +@rdn_rm ........ esz:2 ...... ...... rm:5 rd:5 \ + &rrr_esz rn=%reg_movprfx # Three operand with "memory" size, aka immediate left shift @rd_rn_msz_rm ........ ... rm:5 .... imm:2 rn:5 rd:5 &rrri @@ -369,6 +372,30 @@ CPY_z_i 00000101 .. 01 .... 00 . ........ ..... @rdn_pg4 imm=%sh8_i8s EXT 00000101 001 ..... 000 ... rm:5 rd:5 \ &rrri rn=%reg_movprfx imm=%imm8_16_10 +### SVE Permute - Unpredicated Group + +# SVE broadcast general register +DUP_s 00000101 .. 1 00000 001110 ..... ..... @rd_rn + +# SVE broadcast indexed element +DUP_x 00000101 .. 1 ..... 001000 rn:5 rd:5 \ + &rri imm=%imm7_22_16 + +# SVE insert SIMD&FP scalar register +INSR_f 00000101 .. 1 10100 001110 ..... ..... @rdn_rm + +# SVE insert general register +INSR_r 00000101 .. 1 00100 001110 ..... ..... @rdn_rm + +# SVE reverse vector elements +REV_v 00000101 .. 1 11000 001110 ..... ..... @rd_rn + +# SVE vector table lookup +TBL 00000101 .. 1 ..... 001100 ..... ..... @rd_rn_rm + +# SVE unpack vector elements +UNPK 00000101 esz:2 1100 u:1 h:1 001110 rn:5 rd:5 + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index b825e44cb5..58c0fda333 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -1560,3 +1560,117 @@ void HELPER(sve_ext)(void *vd, void *vn, void *vm, uint32_t desc) memcpy(vd + n_siz, &tmp, n_ofs); } } + +#define DO_INSR(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, uint64_t val, uint32_t desc) \ +{ \ + intptr_t opr_sz = simd_oprsz(desc); \ + swap_memmove(vd + sizeof(TYPE), vn, opr_sz - sizeof(TYPE)); \ + *(TYPE *)(vd + H(0)) = val; \ +} + +DO_INSR(sve_insr_b, uint8_t, H1) +DO_INSR(sve_insr_h, uint16_t, H1_2) +DO_INSR(sve_insr_s, uint32_t, H1_4) +DO_INSR(sve_insr_d, uint64_t, ) + +#undef DO_INSR + +void HELPER(sve_rev_b)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + for (i = 0, j = opr_sz - 8; i < opr_sz / 2; i += 8, j -= 8) { + uint64_t f = *(uint64_t *)(vn + i); + uint64_t b = *(uint64_t *)(vn + j); + *(uint64_t *)(vd + i) = bswap64(b); + *(uint64_t *)(vd + j) = bswap64(f); + } +} + +static inline uint64_t hswap64(uint64_t h) +{ + uint64_t m = 0x0000ffff0000ffffull; + h = rol64(h, 32); + return ((h & m) << 16) | ((h >> 16) & m); +} + +void HELPER(sve_rev_h)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + for (i = 0, j = opr_sz - 8; i < opr_sz / 2; i += 8, j -= 8) { + uint64_t f = *(uint64_t *)(vn + i); + uint64_t b = *(uint64_t *)(vn + j); + *(uint64_t *)(vd + i) = hswap64(b); + *(uint64_t *)(vd + j) = hswap64(f); + } +} + +void HELPER(sve_rev_s)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + for (i = 0, j = opr_sz - 8; i < opr_sz / 2; i += 8, j -= 8) { + uint64_t f = *(uint64_t *)(vn + i); + uint64_t b = *(uint64_t *)(vn + j); + *(uint64_t *)(vd + i) = rol64(b, 32); + *(uint64_t *)(vd + j) = rol64(f, 32); + } +} + +void HELPER(sve_rev_d)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + for (i = 0, j = opr_sz - 8; i < opr_sz / 2; i += 8, j -= 8) { + uint64_t f = *(uint64_t *)(vn + i); + uint64_t b = *(uint64_t *)(vn + j); + *(uint64_t *)(vd + i) = b; + *(uint64_t *)(vd + j) = f; + } +} + +#define DO_TBL(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + uintptr_t elem = opr_sz / sizeof(TYPE); \ + TYPE *d = vd, *n = vn, *m = vm; \ + ARMVectorReg tmp; \ + if (unlikely(vd == vn)) { \ + n = memcpy(&tmp, vn, opr_sz); \ + } \ + for (i = 0; i < elem; i++) { \ + TYPE j = m[H(i)]; \ + d[H(i)] = j < elem ? n[H(j)] : 0; \ + } \ +} + +DO_TBL(sve_tbl_b, uint8_t, H1) +DO_TBL(sve_tbl_h, uint16_t, H2) +DO_TBL(sve_tbl_s, uint32_t, H4) +DO_TBL(sve_tbl_d, uint64_t, ) + +#undef TBL + +#define DO_UNPK(NAME, TYPED, TYPES, HD, HS) \ +void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + TYPED *d = vd; \ + TYPES *n = vn; \ + ARMVectorReg tmp; \ + if (unlikely(vn - vd < opr_sz)) { \ + n = memcpy(&tmp, n, opr_sz / 2); \ + } \ + for (i = 0; i < opr_sz / sizeof(TYPED); i++) { \ + d[HD(i)] = n[HS(i)]; \ + } \ +} + +DO_UNPK(sve_sunpk_h, int16_t, int8_t, H2, H1) +DO_UNPK(sve_sunpk_s, int32_t, int16_t, H4, H2) +DO_UNPK(sve_sunpk_d, int64_t, int32_t, , H4) + +DO_UNPK(sve_uunpk_h, uint16_t, uint8_t, H2, H1) +DO_UNPK(sve_uunpk_s, uint32_t, uint16_t, H4, H2) +DO_UNPK(sve_uunpk_d, uint64_t, uint32_t, , H4) + +#undef DO_UNPK diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index c48d4b530a..388cce9924 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -1956,6 +1956,139 @@ static bool trans_EXT(DisasContext *s, arg_EXT *a, uint32_t insn) return true; } +/* + *** SVE Permute - Unpredicated Group + */ + +static bool trans_DUP_s(DisasContext *s, arg_DUP_s *a, uint32_t insn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_dup_i64(a->esz, vec_full_reg_offset(s, a->rd), + vsz, vsz, cpu_reg_sp(s, a->rn)); + } + return true; +} + +static bool trans_DUP_x(DisasContext *s, arg_DUP_x *a, uint32_t insn) +{ + if ((a->imm & 0x1f) == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + unsigned dofs = vec_full_reg_offset(s, a->rd); + unsigned esz, index; + + esz = ctz32(a->imm); + index = a->imm >> (esz + 1); + + if ((index << esz) < vsz) { + unsigned nofs = vec_reg_offset(s, a->rn, index, esz); + tcg_gen_gvec_dup_mem(esz, dofs, nofs, vsz, vsz); + } else { + tcg_gen_gvec_dup64i(dofs, vsz, vsz, 0); + } + } + return true; +} + +static void do_insr_i64(DisasContext *s, arg_rrr_esz *a, TCGv_i64 val) +{ + typedef void gen_insr(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_i32); + static gen_insr * const fns[4] = { + gen_helper_sve_insr_b, gen_helper_sve_insr_h, + gen_helper_sve_insr_s, gen_helper_sve_insr_d, + }; + unsigned vsz = vec_full_reg_size(s); + TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + TCGv_ptr t_zd = tcg_temp_new_ptr(); + TCGv_ptr t_zn = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, a->rn)); + + fns[a->esz](t_zd, t_zn, val, desc); + + tcg_temp_free_ptr(t_zd); + tcg_temp_free_ptr(t_zn); + tcg_temp_free_i32(desc); +} + +static bool trans_INSR_f(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_ld_i64(t, cpu_env, vec_reg_offset(s, a->rm, 0, MO_64)); + do_insr_i64(s, a, t); + tcg_temp_free_i64(t); + } + return true; +} + +static bool trans_INSR_r(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_insr_i64(s, a, cpu_reg(s, a->rm)); + } + return true; +} + +static bool trans_REV_v(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_2 * const fns[4] = { + gen_helper_sve_rev_b, gen_helper_sve_rev_h, + gen_helper_sve_rev_s, gen_helper_sve_rev_d + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vsz, vsz, 0, fns[a->esz]); + } + return true; +} + +static bool trans_TBL(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_tbl_b, gen_helper_sve_tbl_h, + gen_helper_sve_tbl_s, gen_helper_sve_tbl_d + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, 0, fns[a->esz]); + } + return true; +} + +static bool trans_UNPK(DisasContext *s, arg_UNPK *a, uint32_t insn) +{ + static gen_helper_gvec_2 * const fns[4][2] = { + { NULL, NULL }, + { gen_helper_sve_sunpk_h, gen_helper_sve_uunpk_h }, + { gen_helper_sve_sunpk_s, gen_helper_sve_uunpk_s }, + { gen_helper_sve_sunpk_d, gen_helper_sve_uunpk_d }, + }; + + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn) + + (a->h ? vsz / 2 : 0), + vsz, vsz, 0, fns[a->esz][a->u]); + } + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From d731d8cb3c74258669211f065c918353eb7b8f4a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1282/2380] target/arm: Implement SVE Permute - Predicates Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 6 + target/arm/sve.decode | 18 +++ target/arm/sve_helper.c | 290 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 120 +++++++++++++++ 4 files changed, 434 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 0c9aad575e..ff958fcebd 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -439,6 +439,12 @@ DEF_HELPER_FLAGS_3(sve_uunpk_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_uunpk_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_uunpk_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_zip_p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uzp_p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_trn_p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_rev_p, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_punpk_p, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 7ffd7962c8..26fe1608c4 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -86,6 +86,7 @@ # Three operand, vector element size @rd_rn_rm ........ esz:2 . rm:5 ... ... rn:5 rd:5 &rrr_esz +@pd_pn_pm ........ esz:2 .. rm:4 ....... rn:4 . rd:4 &rrr_esz @rdn_rm ........ esz:2 ...... ...... rm:5 rd:5 \ &rrr_esz rn=%reg_movprfx @@ -396,6 +397,23 @@ TBL 00000101 .. 1 ..... 001100 ..... ..... @rd_rn_rm # SVE unpack vector elements UNPK 00000101 esz:2 1100 u:1 h:1 001110 rn:5 rd:5 +### SVE Permute - Predicates Group + +# SVE permute predicate elements +ZIP1_p 00000101 .. 10 .... 010 000 0 .... 0 .... @pd_pn_pm +ZIP2_p 00000101 .. 10 .... 010 001 0 .... 0 .... @pd_pn_pm +UZP1_p 00000101 .. 10 .... 010 010 0 .... 0 .... @pd_pn_pm +UZP2_p 00000101 .. 10 .... 010 011 0 .... 0 .... @pd_pn_pm +TRN1_p 00000101 .. 10 .... 010 100 0 .... 0 .... @pd_pn_pm +TRN2_p 00000101 .. 10 .... 010 101 0 .... 0 .... @pd_pn_pm + +# SVE reverse predicate elements +REV_p 00000101 .. 11 0100 010 000 0 .... 0 .... @pd_pn + +# SVE unpack predicate elements +PUNPKLO 00000101 00 11 0000 010 000 0 .... 0 .... @pd_pn_e0 +PUNPKHI 00000101 00 11 0001 010 000 0 .... 0 .... @pd_pn_e0 + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 58c0fda333..f4d49d4aff 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -1674,3 +1674,293 @@ DO_UNPK(sve_uunpk_s, uint32_t, uint16_t, H4, H2) DO_UNPK(sve_uunpk_d, uint64_t, uint32_t, , H4) #undef DO_UNPK + +/* Mask of bits included in the even numbered predicates of width esz. + * We also use this for expand_bits/compress_bits, and so extend the + * same pattern out to 16-bit units. + */ +static const uint64_t even_bit_esz_masks[5] = { + 0x5555555555555555ull, + 0x3333333333333333ull, + 0x0f0f0f0f0f0f0f0full, + 0x00ff00ff00ff00ffull, + 0x0000ffff0000ffffull, +}; + +/* Zero-extend units of 2**N bits to units of 2**(N+1) bits. + * For N==0, this corresponds to the operation that in qemu/bitops.h + * we call half_shuffle64; this algorithm is from Hacker's Delight, + * section 7-2 Shuffling Bits. + */ +static uint64_t expand_bits(uint64_t x, int n) +{ + int i; + + x &= 0xffffffffu; + for (i = 4; i >= n; i--) { + int sh = 1 << i; + x = ((x << sh) | x) & even_bit_esz_masks[i]; + } + return x; +} + +/* Compress units of 2**(N+1) bits to units of 2**N bits. + * For N==0, this corresponds to the operation that in qemu/bitops.h + * we call half_unshuffle64; this algorithm is from Hacker's Delight, + * section 7-2 Shuffling Bits, where it is called an inverse half shuffle. + */ +static uint64_t compress_bits(uint64_t x, int n) +{ + int i; + + for (i = n; i <= 4; i++) { + int sh = 1 << i; + x &= even_bit_esz_masks[i]; + x = (x >> sh) | x; + } + return x & 0xffffffffu; +} + +void HELPER(sve_zip_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + int esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + intptr_t high = extract32(pred_desc, SIMD_DATA_SHIFT + 2, 1); + uint64_t *d = vd; + intptr_t i; + + if (oprsz <= 8) { + uint64_t nn = *(uint64_t *)vn; + uint64_t mm = *(uint64_t *)vm; + int half = 4 * oprsz; + + nn = extract64(nn, high * half, half); + mm = extract64(mm, high * half, half); + nn = expand_bits(nn, esz); + mm = expand_bits(mm, esz); + d[0] = nn + (mm << (1 << esz)); + } else { + ARMPredicateReg tmp_n, tmp_m; + + /* We produce output faster than we consume input. + Therefore we must be mindful of possible overlap. */ + if ((vn - vd) < (uintptr_t)oprsz) { + vn = memcpy(&tmp_n, vn, oprsz); + } + if ((vm - vd) < (uintptr_t)oprsz) { + vm = memcpy(&tmp_m, vm, oprsz); + } + if (high) { + high = oprsz >> 1; + } + + if ((high & 3) == 0) { + uint32_t *n = vn, *m = vm; + high >>= 2; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); i++) { + uint64_t nn = n[H4(high + i)]; + uint64_t mm = m[H4(high + i)]; + + nn = expand_bits(nn, esz); + mm = expand_bits(mm, esz); + d[i] = nn + (mm << (1 << esz)); + } + } else { + uint8_t *n = vn, *m = vm; + uint16_t *d16 = vd; + + for (i = 0; i < oprsz / 2; i++) { + uint16_t nn = n[H1(high + i)]; + uint16_t mm = m[H1(high + i)]; + + nn = expand_bits(nn, esz); + mm = expand_bits(mm, esz); + d16[H2(i)] = nn + (mm << (1 << esz)); + } + } + } +} + +void HELPER(sve_uzp_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + int esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + int odd = extract32(pred_desc, SIMD_DATA_SHIFT + 2, 1) << esz; + uint64_t *d = vd, *n = vn, *m = vm; + uint64_t l, h; + intptr_t i; + + if (oprsz <= 8) { + l = compress_bits(n[0] >> odd, esz); + h = compress_bits(m[0] >> odd, esz); + d[0] = extract64(l + (h << (4 * oprsz)), 0, 8 * oprsz); + } else { + ARMPredicateReg tmp_m; + intptr_t oprsz_16 = oprsz / 16; + + if ((vm - vd) < (uintptr_t)oprsz) { + m = memcpy(&tmp_m, vm, oprsz); + } + + for (i = 0; i < oprsz_16; i++) { + l = n[2 * i + 0]; + h = n[2 * i + 1]; + l = compress_bits(l >> odd, esz); + h = compress_bits(h >> odd, esz); + d[i] = l + (h << 32); + } + + /* For VL which is not a power of 2, the results from M do not + align nicely with the uint64_t for D. Put the aligned results + from M into TMP_M and then copy it into place afterward. */ + if (oprsz & 15) { + d[i] = compress_bits(n[2 * i] >> odd, esz); + + for (i = 0; i < oprsz_16; i++) { + l = m[2 * i + 0]; + h = m[2 * i + 1]; + l = compress_bits(l >> odd, esz); + h = compress_bits(h >> odd, esz); + tmp_m.p[i] = l + (h << 32); + } + tmp_m.p[i] = compress_bits(m[2 * i] >> odd, esz); + + swap_memmove(vd + oprsz / 2, &tmp_m, oprsz / 2); + } else { + for (i = 0; i < oprsz_16; i++) { + l = m[2 * i + 0]; + h = m[2 * i + 1]; + l = compress_bits(l >> odd, esz); + h = compress_bits(h >> odd, esz); + d[oprsz_16 + i] = l + (h << 32); + } + } + } +} + +void HELPER(sve_trn_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + uintptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + bool odd = extract32(pred_desc, SIMD_DATA_SHIFT + 2, 1); + uint64_t *d = vd, *n = vn, *m = vm; + uint64_t mask; + int shr, shl; + intptr_t i; + + shl = 1 << esz; + shr = 0; + mask = even_bit_esz_masks[esz]; + if (odd) { + mask <<= shl; + shr = shl; + shl = 0; + } + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); i++) { + uint64_t nn = (n[i] & mask) >> shr; + uint64_t mm = (m[i] & mask) << shl; + d[i] = nn + mm; + } +} + +/* Reverse units of 2**N bits. */ +static uint64_t reverse_bits_64(uint64_t x, int n) +{ + int i, sh; + + x = bswap64(x); + for (i = 2, sh = 4; i >= n; i--, sh >>= 1) { + uint64_t mask = even_bit_esz_masks[i]; + x = ((x & mask) << sh) | ((x >> sh) & mask); + } + return x; +} + +static uint8_t reverse_bits_8(uint8_t x, int n) +{ + static const uint8_t mask[3] = { 0x55, 0x33, 0x0f }; + int i, sh; + + for (i = 2, sh = 4; i >= n; i--, sh >>= 1) { + x = ((x & mask[i]) << sh) | ((x >> sh) & mask[i]); + } + return x; +} + +void HELPER(sve_rev_p)(void *vd, void *vn, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + int esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + intptr_t i, oprsz_2 = oprsz / 2; + + if (oprsz <= 8) { + uint64_t l = *(uint64_t *)vn; + l = reverse_bits_64(l << (64 - 8 * oprsz), esz); + *(uint64_t *)vd = l; + } else if ((oprsz & 15) == 0) { + for (i = 0; i < oprsz_2; i += 8) { + intptr_t ih = oprsz - 8 - i; + uint64_t l = reverse_bits_64(*(uint64_t *)(vn + i), esz); + uint64_t h = reverse_bits_64(*(uint64_t *)(vn + ih), esz); + *(uint64_t *)(vd + i) = h; + *(uint64_t *)(vd + ih) = l; + } + } else { + for (i = 0; i < oprsz_2; i += 1) { + intptr_t il = H1(i); + intptr_t ih = H1(oprsz - 1 - i); + uint8_t l = reverse_bits_8(*(uint8_t *)(vn + il), esz); + uint8_t h = reverse_bits_8(*(uint8_t *)(vn + ih), esz); + *(uint8_t *)(vd + il) = h; + *(uint8_t *)(vd + ih) = l; + } + } +} + +void HELPER(sve_punpk_p)(void *vd, void *vn, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t high = extract32(pred_desc, SIMD_DATA_SHIFT + 2, 1); + uint64_t *d = vd; + intptr_t i; + + if (oprsz <= 8) { + uint64_t nn = *(uint64_t *)vn; + int half = 4 * oprsz; + + nn = extract64(nn, high * half, half); + nn = expand_bits(nn, 0); + d[0] = nn; + } else { + ARMPredicateReg tmp_n; + + /* We produce output faster than we consume input. + Therefore we must be mindful of possible overlap. */ + if ((vn - vd) < (uintptr_t)oprsz) { + vn = memcpy(&tmp_n, vn, oprsz); + } + if (high) { + high = oprsz >> 1; + } + + if ((high & 3) == 0) { + uint32_t *n = vn; + high >>= 2; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); i++) { + uint64_t nn = n[H4(high + i)]; + d[i] = expand_bits(nn, 0); + } + } else { + uint16_t *d16 = vd; + uint8_t *n = vn; + + for (i = 0; i < oprsz / 2; i++) { + uint16_t nn = n[H1(high + i)]; + d16[H2(i)] = expand_bits(nn, 0); + } + } + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 388cce9924..0160d06915 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -2089,6 +2089,126 @@ static bool trans_UNPK(DisasContext *s, arg_UNPK *a, uint32_t insn) return true; } +/* + *** SVE Permute - Predicates Group + */ + +static bool do_perm_pred3(DisasContext *s, arg_rrr_esz *a, bool high_odd, + gen_helper_gvec_3 *fn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = pred_full_reg_size(s); + + /* Predicate sizes may be smaller and cannot use simd_desc. + We cannot round up, as we do elsewhere, because we need + the exact size for ZIP2 and REV. We retain the style for + the other helpers for consistency. */ + TCGv_ptr t_d = tcg_temp_new_ptr(); + TCGv_ptr t_n = tcg_temp_new_ptr(); + TCGv_ptr t_m = tcg_temp_new_ptr(); + TCGv_i32 t_desc; + int desc; + + desc = vsz - 2; + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); + desc = deposit32(desc, SIMD_DATA_SHIFT + 2, 2, high_odd); + + tcg_gen_addi_ptr(t_d, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_n, cpu_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_m, cpu_env, pred_full_reg_offset(s, a->rm)); + t_desc = tcg_const_i32(desc); + + fn(t_d, t_n, t_m, t_desc); + + tcg_temp_free_ptr(t_d); + tcg_temp_free_ptr(t_n); + tcg_temp_free_ptr(t_m); + tcg_temp_free_i32(t_desc); + return true; +} + +static bool do_perm_pred2(DisasContext *s, arg_rr_esz *a, bool high_odd, + gen_helper_gvec_2 *fn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = pred_full_reg_size(s); + TCGv_ptr t_d = tcg_temp_new_ptr(); + TCGv_ptr t_n = tcg_temp_new_ptr(); + TCGv_i32 t_desc; + int desc; + + tcg_gen_addi_ptr(t_d, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_n, cpu_env, pred_full_reg_offset(s, a->rn)); + + /* Predicate sizes may be smaller and cannot use simd_desc. + We cannot round up, as we do elsewhere, because we need + the exact size for ZIP2 and REV. We retain the style for + the other helpers for consistency. */ + + desc = vsz - 2; + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); + desc = deposit32(desc, SIMD_DATA_SHIFT + 2, 2, high_odd); + t_desc = tcg_const_i32(desc); + + fn(t_d, t_n, t_desc); + + tcg_temp_free_i32(t_desc); + tcg_temp_free_ptr(t_d); + tcg_temp_free_ptr(t_n); + return true; +} + +static bool trans_ZIP1_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 0, gen_helper_sve_zip_p); +} + +static bool trans_ZIP2_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 1, gen_helper_sve_zip_p); +} + +static bool trans_UZP1_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 0, gen_helper_sve_uzp_p); +} + +static bool trans_UZP2_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 1, gen_helper_sve_uzp_p); +} + +static bool trans_TRN1_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 0, gen_helper_sve_trn_p); +} + +static bool trans_TRN2_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 1, gen_helper_sve_trn_p); +} + +static bool trans_REV_p(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + return do_perm_pred2(s, a, 0, gen_helper_sve_rev_p); +} + +static bool trans_PUNPKLO(DisasContext *s, arg_PUNPKLO *a, uint32_t insn) +{ + return do_perm_pred2(s, a, 0, gen_helper_sve_punpk_p); +} + +static bool trans_PUNPKHI(DisasContext *s, arg_PUNPKHI *a, uint32_t insn) +{ + return do_perm_pred2(s, a, 1, gen_helper_sve_punpk_p); +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From 234b48e9c68759aea78ff5a1e49c2ba806cd1d83 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1283/2380] target/arm: Implement SVE Permute - Interleaving Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 15 ++++++++ target/arm/sve.decode | 10 +++++ target/arm/sve_helper.c | 72 ++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 75 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index ff958fcebd..bab20345c6 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -445,6 +445,21 @@ DEF_HELPER_FLAGS_4(sve_trn_p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_rev_p, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_punpk_p, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_zip_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_zip_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_zip_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_zip_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_uzp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uzp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uzp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uzp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_trn_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_trn_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_trn_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_trn_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 26fe1608c4..df2b94dc0a 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -414,6 +414,16 @@ REV_p 00000101 .. 11 0100 010 000 0 .... 0 .... @pd_pn PUNPKLO 00000101 00 11 0000 010 000 0 .... 0 .... @pd_pn_e0 PUNPKHI 00000101 00 11 0001 010 000 0 .... 0 .... @pd_pn_e0 +### SVE Permute - Interleaving Group + +# SVE permute vector elements +ZIP1_z 00000101 .. 1 ..... 011 000 ..... ..... @rd_rn_rm +ZIP2_z 00000101 .. 1 ..... 011 001 ..... ..... @rd_rn_rm +UZP1_z 00000101 .. 1 ..... 011 010 ..... ..... @rd_rn_rm +UZP2_z 00000101 .. 1 ..... 011 011 ..... ..... @rd_rn_rm +TRN1_z 00000101 .. 1 ..... 011 100 ..... ..... @rd_rn_rm +TRN2_z 00000101 .. 1 ..... 011 101 ..... ..... @rd_rn_rm + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index f4d49d4aff..f114e9ab63 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -1964,3 +1964,75 @@ void HELPER(sve_punpk_p)(void *vd, void *vn, uint32_t pred_desc) } } } + +#define DO_ZIP(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + intptr_t i, oprsz_2 = oprsz / 2; \ + ARMVectorReg tmp_n, tmp_m; \ + /* We produce output faster than we consume input. \ + Therefore we must be mindful of possible overlap. */ \ + if (unlikely((vn - vd) < (uintptr_t)oprsz)) { \ + vn = memcpy(&tmp_n, vn, oprsz_2); \ + } \ + if (unlikely((vm - vd) < (uintptr_t)oprsz)) { \ + vm = memcpy(&tmp_m, vm, oprsz_2); \ + } \ + for (i = 0; i < oprsz_2; i += sizeof(TYPE)) { \ + *(TYPE *)(vd + H(2 * i + 0)) = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(2 * i + sizeof(TYPE))) = *(TYPE *)(vm + H(i)); \ + } \ +} + +DO_ZIP(sve_zip_b, uint8_t, H1) +DO_ZIP(sve_zip_h, uint16_t, H1_2) +DO_ZIP(sve_zip_s, uint32_t, H1_4) +DO_ZIP(sve_zip_d, uint64_t, ) + +#define DO_UZP(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + intptr_t oprsz_2 = oprsz / 2; \ + intptr_t odd_ofs = simd_data(desc); \ + intptr_t i; \ + ARMVectorReg tmp_m; \ + if (unlikely((vm - vd) < (uintptr_t)oprsz)) { \ + vm = memcpy(&tmp_m, vm, oprsz); \ + } \ + for (i = 0; i < oprsz_2; i += sizeof(TYPE)) { \ + *(TYPE *)(vd + H(i)) = *(TYPE *)(vn + H(2 * i + odd_ofs)); \ + } \ + for (i = 0; i < oprsz_2; i += sizeof(TYPE)) { \ + *(TYPE *)(vd + H(oprsz_2 + i)) = *(TYPE *)(vm + H(2 * i + odd_ofs)); \ + } \ +} + +DO_UZP(sve_uzp_b, uint8_t, H1) +DO_UZP(sve_uzp_h, uint16_t, H1_2) +DO_UZP(sve_uzp_s, uint32_t, H1_4) +DO_UZP(sve_uzp_d, uint64_t, ) + +#define DO_TRN(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + intptr_t odd_ofs = simd_data(desc); \ + intptr_t i; \ + for (i = 0; i < oprsz; i += 2 * sizeof(TYPE)) { \ + TYPE ae = *(TYPE *)(vn + H(i + odd_ofs)); \ + TYPE be = *(TYPE *)(vm + H(i + odd_ofs)); \ + *(TYPE *)(vd + H(i + 0)) = ae; \ + *(TYPE *)(vd + H(i + sizeof(TYPE))) = be; \ + } \ +} + +DO_TRN(sve_trn_b, uint8_t, H1) +DO_TRN(sve_trn_h, uint16_t, H1_2) +DO_TRN(sve_trn_s, uint32_t, H1_4) +DO_TRN(sve_trn_d, uint64_t, ) + +#undef DO_ZIP +#undef DO_UZP +#undef DO_TRN diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 0160d06915..21319518d7 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -2209,6 +2209,81 @@ static bool trans_PUNPKHI(DisasContext *s, arg_PUNPKHI *a, uint32_t insn) return do_perm_pred2(s, a, 1, gen_helper_sve_punpk_p); } +/* + *** SVE Permute - Interleaving Group + */ + +static bool do_zip(DisasContext *s, arg_rrr_esz *a, bool high) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_zip_b, gen_helper_sve_zip_h, + gen_helper_sve_zip_s, gen_helper_sve_zip_d, + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + unsigned high_ofs = high ? vsz / 2 : 0; + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn) + high_ofs, + vec_full_reg_offset(s, a->rm) + high_ofs, + vsz, vsz, 0, fns[a->esz]); + } + return true; +} + +static bool do_zzz_data_ool(DisasContext *s, arg_rrr_esz *a, int data, + gen_helper_gvec_3 *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, data, fn); + } + return true; +} + +static bool trans_ZIP1_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zip(s, a, false); +} + +static bool trans_ZIP2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zip(s, a, true); +} + +static gen_helper_gvec_3 * const uzp_fns[4] = { + gen_helper_sve_uzp_b, gen_helper_sve_uzp_h, + gen_helper_sve_uzp_s, gen_helper_sve_uzp_d, +}; + +static bool trans_UZP1_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zzz_data_ool(s, a, 0, uzp_fns[a->esz]); +} + +static bool trans_UZP2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zzz_data_ool(s, a, 1 << a->esz, uzp_fns[a->esz]); +} + +static gen_helper_gvec_3 * const trn_fns[4] = { + gen_helper_sve_trn_b, gen_helper_sve_trn_h, + gen_helper_sve_trn_s, gen_helper_sve_trn_d, +}; + +static bool trans_TRN1_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zzz_data_ool(s, a, 0, trn_fns[a->esz]); +} + +static bool trans_TRN2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zzz_data_ool(s, a, 1 << a->esz, trn_fns[a->esz]); +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From 3ca879aeb3412bc2be35d01a7bedf5fada960b5d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1284/2380] target/arm: Implement SVE compress active elements Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 3 +++ target/arm/sve.decode | 6 ++++++ target/arm/sve_helper.c | 34 ++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 12 ++++++++++++ 4 files changed, 55 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index bab20345c6..d977aea00d 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -460,6 +460,9 @@ DEF_HELPER_FLAGS_4(sve_trn_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_trn_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_trn_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_compact_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_compact_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index df2b94dc0a..9da6566d32 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -424,6 +424,12 @@ UZP2_z 00000101 .. 1 ..... 011 011 ..... ..... @rd_rn_rm TRN1_z 00000101 .. 1 ..... 011 100 ..... ..... @rd_rn_rm TRN2_z 00000101 .. 1 ..... 011 101 ..... ..... @rd_rn_rm +### SVE Permute - Predicated Group + +# SVE compress active elements +# Note esz >= 2 +COMPACT 00000101 .. 100001 100 ... ..... ..... @rd_pg_rn + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index f114e9ab63..ed3c6d4ca9 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2036,3 +2036,37 @@ DO_TRN(sve_trn_d, uint64_t, ) #undef DO_ZIP #undef DO_UZP #undef DO_TRN + +void HELPER(sve_compact_s)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc) / 4; + uint32_t *d = vd, *n = vn; + uint8_t *pg = vg; + + for (i = j = 0; i < opr_sz; i++) { + if (pg[H1(i / 2)] & (i & 1 ? 0x10 : 0x01)) { + d[H4(j)] = n[H4(i)]; + j++; + } + } + for (; j < opr_sz; j++) { + d[H4(j)] = 0; + } +} + +void HELPER(sve_compact_d)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + for (i = j = 0; i < opr_sz; i++) { + if (pg[H1(i)] & 1) { + d[j] = n[i]; + j++; + } + } + for (; j < opr_sz; j++) { + d[j] = 0; + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 21319518d7..ed0f48a927 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -2284,6 +2284,18 @@ static bool trans_TRN2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) return do_zzz_data_ool(s, a, 1 << a->esz, trn_fns[a->esz]); } +/* + *** SVE Permute Vector - Predicated Group + */ + +static bool trans_COMPACT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, NULL, gen_helper_sve_compact_s, gen_helper_sve_compact_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From ef23cb726dc32375bc2fca7ac3e9f34816f6ee13 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1285/2380] target/arm: Implement SVE conditionally broadcast/extract element Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 2 + target/arm/sve.decode | 20 +++ target/arm/sve_helper.c | 12 ++ target/arm/translate-sve.c | 328 +++++++++++++++++++++++++++++++++++++ 4 files changed, 362 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index d977aea00d..a58fb4ba01 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -463,6 +463,8 @@ DEF_HELPER_FLAGS_4(sve_trn_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_compact_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_compact_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_2(sve_last_active_element, TCG_CALL_NO_RWG, s32, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 9da6566d32..1226867f69 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -430,6 +430,26 @@ TRN2_z 00000101 .. 1 ..... 011 101 ..... ..... @rd_rn_rm # Note esz >= 2 COMPACT 00000101 .. 100001 100 ... ..... ..... @rd_pg_rn +# SVE conditionally broadcast element to vector +CLASTA_z 00000101 .. 10100 0 100 ... ..... ..... @rdn_pg_rm +CLASTB_z 00000101 .. 10100 1 100 ... ..... ..... @rdn_pg_rm + +# SVE conditionally copy element to SIMD&FP scalar +CLASTA_v 00000101 .. 10101 0 100 ... ..... ..... @rd_pg_rn +CLASTB_v 00000101 .. 10101 1 100 ... ..... ..... @rd_pg_rn + +# SVE conditionally copy element to general register +CLASTA_r 00000101 .. 11000 0 101 ... ..... ..... @rd_pg_rn +CLASTB_r 00000101 .. 11000 1 101 ... ..... ..... @rd_pg_rn + +# SVE copy element to SIMD&FP scalar register +LASTA_v 00000101 .. 10001 0 100 ... ..... ..... @rd_pg_rn +LASTB_v 00000101 .. 10001 1 100 ... ..... ..... @rd_pg_rn + +# SVE copy element to general register +LASTA_r 00000101 .. 10000 0 101 ... ..... ..... @rd_pg_rn +LASTB_r 00000101 .. 10000 1 101 ... ..... ..... @rd_pg_rn + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index ed3c6d4ca9..cb7d101b57 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2070,3 +2070,15 @@ void HELPER(sve_compact_d)(void *vd, void *vn, void *vg, uint32_t desc) d[j] = 0; } } + +/* Similar to the ARM LastActiveElement pseudocode function, except the + * result is multiplied by the element size. This includes the not found + * indication; e.g. not found for esz=3 is -8. + */ +int32_t HELPER(sve_last_active_element)(void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + + return last_active_element(vg, DIV_ROUND_UP(oprsz, 8), esz); +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index ed0f48a927..feb4c09f1b 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -2296,6 +2296,334 @@ static bool trans_COMPACT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } +/* Call the helper that computes the ARM LastActiveElement pseudocode + * function, scaled by the element size. This includes the not found + * indication; e.g. not found for esz=3 is -8. + */ +static void find_last_active(DisasContext *s, TCGv_i32 ret, int esz, int pg) +{ + /* Predicate sizes may be smaller and cannot use simd_desc. We cannot + * round up, as we do elsewhere, because we need the exact size. + */ + TCGv_ptr t_p = tcg_temp_new_ptr(); + TCGv_i32 t_desc; + unsigned vsz = pred_full_reg_size(s); + unsigned desc; + + desc = vsz - 2; + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, esz); + + tcg_gen_addi_ptr(t_p, cpu_env, pred_full_reg_offset(s, pg)); + t_desc = tcg_const_i32(desc); + + gen_helper_sve_last_active_element(ret, t_p, t_desc); + + tcg_temp_free_i32(t_desc); + tcg_temp_free_ptr(t_p); +} + +/* Increment LAST to the offset of the next element in the vector, + * wrapping around to 0. + */ +static void incr_last_active(DisasContext *s, TCGv_i32 last, int esz) +{ + unsigned vsz = vec_full_reg_size(s); + + tcg_gen_addi_i32(last, last, 1 << esz); + if (is_power_of_2(vsz)) { + tcg_gen_andi_i32(last, last, vsz - 1); + } else { + TCGv_i32 max = tcg_const_i32(vsz); + TCGv_i32 zero = tcg_const_i32(0); + tcg_gen_movcond_i32(TCG_COND_GEU, last, last, max, zero, last); + tcg_temp_free_i32(max); + tcg_temp_free_i32(zero); + } +} + +/* If LAST < 0, set LAST to the offset of the last element in the vector. */ +static void wrap_last_active(DisasContext *s, TCGv_i32 last, int esz) +{ + unsigned vsz = vec_full_reg_size(s); + + if (is_power_of_2(vsz)) { + tcg_gen_andi_i32(last, last, vsz - 1); + } else { + TCGv_i32 max = tcg_const_i32(vsz - (1 << esz)); + TCGv_i32 zero = tcg_const_i32(0); + tcg_gen_movcond_i32(TCG_COND_LT, last, last, zero, max, last); + tcg_temp_free_i32(max); + tcg_temp_free_i32(zero); + } +} + +/* Load an unsigned element of ESZ from BASE+OFS. */ +static TCGv_i64 load_esz(TCGv_ptr base, int ofs, int esz) +{ + TCGv_i64 r = tcg_temp_new_i64(); + + switch (esz) { + case 0: + tcg_gen_ld8u_i64(r, base, ofs); + break; + case 1: + tcg_gen_ld16u_i64(r, base, ofs); + break; + case 2: + tcg_gen_ld32u_i64(r, base, ofs); + break; + case 3: + tcg_gen_ld_i64(r, base, ofs); + break; + default: + g_assert_not_reached(); + } + return r; +} + +/* Load an unsigned element of ESZ from RM[LAST]. */ +static TCGv_i64 load_last_active(DisasContext *s, TCGv_i32 last, + int rm, int esz) +{ + TCGv_ptr p = tcg_temp_new_ptr(); + TCGv_i64 r; + + /* Convert offset into vector into offset into ENV. + * The final adjustment for the vector register base + * is added via constant offset to the load. + */ +#ifdef HOST_WORDS_BIGENDIAN + /* Adjust for element ordering. See vec_reg_offset. */ + if (esz < 3) { + tcg_gen_xori_i32(last, last, 8 - (1 << esz)); + } +#endif + tcg_gen_ext_i32_ptr(p, last); + tcg_gen_add_ptr(p, p, cpu_env); + + r = load_esz(p, vec_full_reg_offset(s, rm), esz); + tcg_temp_free_ptr(p); + + return r; +} + +/* Compute CLAST for a Zreg. */ +static bool do_clast_vector(DisasContext *s, arg_rprr_esz *a, bool before) +{ + TCGv_i32 last; + TCGLabel *over; + TCGv_i64 ele; + unsigned vsz, esz = a->esz; + + if (!sve_access_check(s)) { + return true; + } + + last = tcg_temp_local_new_i32(); + over = gen_new_label(); + + find_last_active(s, last, esz, a->pg); + + /* There is of course no movcond for a 2048-bit vector, + * so we must branch over the actual store. + */ + tcg_gen_brcondi_i32(TCG_COND_LT, last, 0, over); + + if (!before) { + incr_last_active(s, last, esz); + } + + ele = load_last_active(s, last, a->rm, esz); + tcg_temp_free_i32(last); + + vsz = vec_full_reg_size(s); + tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd), vsz, vsz, ele); + tcg_temp_free_i64(ele); + + /* If this insn used MOVPRFX, we may need a second move. */ + if (a->rd != a->rn) { + TCGLabel *done = gen_new_label(); + tcg_gen_br(done); + + gen_set_label(over); + do_mov_z(s, a->rd, a->rn); + + gen_set_label(done); + } else { + gen_set_label(over); + } + return true; +} + +static bool trans_CLASTA_z(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + return do_clast_vector(s, a, false); +} + +static bool trans_CLASTB_z(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + return do_clast_vector(s, a, true); +} + +/* Compute CLAST for a scalar. */ +static void do_clast_scalar(DisasContext *s, int esz, int pg, int rm, + bool before, TCGv_i64 reg_val) +{ + TCGv_i32 last = tcg_temp_new_i32(); + TCGv_i64 ele, cmp, zero; + + find_last_active(s, last, esz, pg); + + /* Extend the original value of last prior to incrementing. */ + cmp = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(cmp, last); + + if (!before) { + incr_last_active(s, last, esz); + } + + /* The conceit here is that while last < 0 indicates not found, after + * adjusting for cpu_env->vfp.zregs[rm], it is still a valid address + * from which we can load garbage. We then discard the garbage with + * a conditional move. + */ + ele = load_last_active(s, last, rm, esz); + tcg_temp_free_i32(last); + + zero = tcg_const_i64(0); + tcg_gen_movcond_i64(TCG_COND_GE, reg_val, cmp, zero, ele, reg_val); + + tcg_temp_free_i64(zero); + tcg_temp_free_i64(cmp); + tcg_temp_free_i64(ele); +} + +/* Compute CLAST for a Vreg. */ +static bool do_clast_fp(DisasContext *s, arg_rpr_esz *a, bool before) +{ + if (sve_access_check(s)) { + int esz = a->esz; + int ofs = vec_reg_offset(s, a->rd, 0, esz); + TCGv_i64 reg = load_esz(cpu_env, ofs, esz); + + do_clast_scalar(s, esz, a->pg, a->rn, before, reg); + write_fp_dreg(s, a->rd, reg); + tcg_temp_free_i64(reg); + } + return true; +} + +static bool trans_CLASTA_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_clast_fp(s, a, false); +} + +static bool trans_CLASTB_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_clast_fp(s, a, true); +} + +/* Compute CLAST for a Xreg. */ +static bool do_clast_general(DisasContext *s, arg_rpr_esz *a, bool before) +{ + TCGv_i64 reg; + + if (!sve_access_check(s)) { + return true; + } + + reg = cpu_reg(s, a->rd); + switch (a->esz) { + case 0: + tcg_gen_ext8u_i64(reg, reg); + break; + case 1: + tcg_gen_ext16u_i64(reg, reg); + break; + case 2: + tcg_gen_ext32u_i64(reg, reg); + break; + case 3: + break; + default: + g_assert_not_reached(); + } + + do_clast_scalar(s, a->esz, a->pg, a->rn, before, reg); + return true; +} + +static bool trans_CLASTA_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_clast_general(s, a, false); +} + +static bool trans_CLASTB_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_clast_general(s, a, true); +} + +/* Compute LAST for a scalar. */ +static TCGv_i64 do_last_scalar(DisasContext *s, int esz, + int pg, int rm, bool before) +{ + TCGv_i32 last = tcg_temp_new_i32(); + TCGv_i64 ret; + + find_last_active(s, last, esz, pg); + if (before) { + wrap_last_active(s, last, esz); + } else { + incr_last_active(s, last, esz); + } + + ret = load_last_active(s, last, rm, esz); + tcg_temp_free_i32(last); + return ret; +} + +/* Compute LAST for a Vreg. */ +static bool do_last_fp(DisasContext *s, arg_rpr_esz *a, bool before) +{ + if (sve_access_check(s)) { + TCGv_i64 val = do_last_scalar(s, a->esz, a->pg, a->rn, before); + write_fp_dreg(s, a->rd, val); + tcg_temp_free_i64(val); + } + return true; +} + +static bool trans_LASTA_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_last_fp(s, a, false); +} + +static bool trans_LASTB_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_last_fp(s, a, true); +} + +/* Compute LAST for a Xreg. */ +static bool do_last_general(DisasContext *s, arg_rpr_esz *a, bool before) +{ + if (sve_access_check(s)) { + TCGv_i64 val = do_last_scalar(s, a->esz, a->pg, a->rn, before); + tcg_gen_mov_i64(cpu_reg(s, a->rd), val); + tcg_temp_free_i64(val); + } + return true; +} + +static bool trans_LASTA_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_last_general(s, a, false); +} + +static bool trans_LASTB_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_last_general(s, a, true); +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From 792a557847697235037fea30eaaacb9b45b4c9e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:14 +0100 Subject: [PATCH 1286/2380] target/arm: Implement SVE copy to vector (predicated) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 6 ++++++ target/arm/translate-sve.c | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 1226867f69..519139f684 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -450,6 +450,12 @@ LASTB_v 00000101 .. 10001 1 100 ... ..... ..... @rd_pg_rn LASTA_r 00000101 .. 10000 0 101 ... ..... ..... @rd_pg_rn LASTB_r 00000101 .. 10000 1 101 ... ..... ..... @rd_pg_rn +# SVE copy element from SIMD&FP scalar register +CPY_m_v 00000101 .. 100000 100 ... ..... ..... @rd_pg_rn + +# SVE copy element from general register to vector (predicated) +CPY_m_r 00000101 .. 101000 101 ... ..... ..... @rd_pg_rn + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index feb4c09f1b..eed59524a9 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -2624,6 +2624,25 @@ static bool trans_LASTB_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_last_general(s, a, true); } +static bool trans_CPY_m_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_cpy_m(s, a->esz, a->rd, a->rd, a->pg, cpu_reg_sp(s, a->rn)); + } + return true; +} + +static bool trans_CPY_m_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int ofs = vec_reg_offset(s, a->rn, 0, a->esz); + TCGv_i64 t = load_esz(cpu_env, ofs, a->esz); + do_cpy_m(s, a->esz, a->rd, a->rd, a->pg, t); + tcg_temp_free_i64(t); + } + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From dae8fb9019d2aa6ccb151a19871df40de6c98e29 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1287/2380] target/arm: Implement SVE reverse within elements Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 14 +++++++++++++ target/arm/sve.decode | 7 +++++++ target/arm/sve_helper.c | 41 +++++++++++++++++++++++++++++++------- target/arm/translate-sve.c | 38 +++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 7 deletions(-) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index a58fb4ba01..3b7c54905d 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -465,6 +465,20 @@ DEF_HELPER_FLAGS_4(sve_compact_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_2(sve_last_active_element, TCG_CALL_NO_RWG, s32, ptr, i32) +DEF_HELPER_FLAGS_4(sve_revb_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_revb_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_revb_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_revh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_revh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_revw_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_rbit_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_rbit_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_rbit_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_rbit_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 519139f684..95eb4968a9 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -456,6 +456,13 @@ CPY_m_v 00000101 .. 100000 100 ... ..... ..... @rd_pg_rn # SVE copy element from general register to vector (predicated) CPY_m_r 00000101 .. 101000 101 ... ..... ..... @rd_pg_rn +# SVE reverse within elements +# Note esz >= operation size +REVB 00000101 .. 1001 00 100 ... ..... ..... @rd_pg_rn +REVH 00000101 .. 1001 01 100 ... ..... ..... @rd_pg_rn +REVW 00000101 .. 1001 10 100 ... ..... ..... @rd_pg_rn +RBIT 00000101 .. 1001 11 100 ... ..... ..... @rd_pg_rn + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index cb7d101b57..4017b9eed1 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -238,6 +238,26 @@ static inline uint64_t expand_pred_s(uint8_t byte) return word[byte & 0x11]; } +/* Swap 16-bit words within a 32-bit word. */ +static inline uint32_t hswap32(uint32_t h) +{ + return rol32(h, 16); +} + +/* Swap 16-bit words within a 64-bit word. */ +static inline uint64_t hswap64(uint64_t h) +{ + uint64_t m = 0x0000ffff0000ffffull; + h = rol64(h, 32); + return ((h & m) << 16) | ((h >> 16) & m); +} + +/* Swap 32-bit words within a 64-bit word. */ +static inline uint64_t wswap64(uint64_t h) +{ + return rol64(h, 32); +} + #define LOGICAL_PPPP(NAME, FUNC) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ { \ @@ -616,6 +636,20 @@ DO_ZPZ(sve_neg_h, uint16_t, H1_2, DO_NEG) DO_ZPZ(sve_neg_s, uint32_t, H1_4, DO_NEG) DO_ZPZ_D(sve_neg_d, uint64_t, DO_NEG) +DO_ZPZ(sve_revb_h, uint16_t, H1_2, bswap16) +DO_ZPZ(sve_revb_s, uint32_t, H1_4, bswap32) +DO_ZPZ_D(sve_revb_d, uint64_t, bswap64) + +DO_ZPZ(sve_revh_s, uint32_t, H1_4, hswap32) +DO_ZPZ_D(sve_revh_d, uint64_t, hswap64) + +DO_ZPZ_D(sve_revw_d, uint64_t, wswap64) + +DO_ZPZ(sve_rbit_b, uint8_t, H1, revbit8) +DO_ZPZ(sve_rbit_h, uint16_t, H1_2, revbit16) +DO_ZPZ(sve_rbit_s, uint32_t, H1_4, revbit32) +DO_ZPZ_D(sve_rbit_d, uint64_t, revbit64) + /* Three-operand expander, unpredicated, in which the third operand is "wide". */ #define DO_ZZW(NAME, TYPE, TYPEW, H, OP) \ @@ -1587,13 +1621,6 @@ void HELPER(sve_rev_b)(void *vd, void *vn, uint32_t desc) } } -static inline uint64_t hswap64(uint64_t h) -{ - uint64_t m = 0x0000ffff0000ffffull; - h = rol64(h, 32); - return ((h & m) << 16) | ((h >> 16) & m); -} - void HELPER(sve_rev_h)(void *vd, void *vn, uint32_t desc) { intptr_t i, j, opr_sz = simd_oprsz(desc); diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index eed59524a9..f8d8cf1547 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -2643,6 +2643,44 @@ static bool trans_CPY_m_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return true; } +static bool trans_REVB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_revb_h, + gen_helper_sve_revb_s, + gen_helper_sve_revb_d, + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_REVH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + NULL, + gen_helper_sve_revh_s, + gen_helper_sve_revh_d, + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_REVW(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ool(s, a, a->esz == 3 ? gen_helper_sve_revw_d : NULL); +} + +static bool trans_RBIT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_rbit_b, + gen_helper_sve_rbit_h, + gen_helper_sve_rbit_s, + gen_helper_sve_rbit_d, + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From b48ff24098c72f86e187e6abb7e9ca4de40a7fb4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1288/2380] target/arm: Implement SVE vector splice (predicated) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 2 ++ target/arm/sve.decode | 3 +++ target/arm/sve_helper.c | 37 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 13 +++++++++++++ 4 files changed, 55 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 3b7c54905d..c3f8a2b502 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -479,6 +479,8 @@ DEF_HELPER_FLAGS_4(sve_rbit_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_rbit_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_rbit_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_splice, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 95eb4968a9..a9fa631252 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -463,6 +463,9 @@ REVH 00000101 .. 1001 01 100 ... ..... ..... @rd_pg_rn REVW 00000101 .. 1001 10 100 ... ..... ..... @rd_pg_rn RBIT 00000101 .. 1001 11 100 ... ..... ..... @rd_pg_rn +# SVE vector splice (predicated) +SPLICE 00000101 .. 101 100 100 ... ..... ..... @rdn_pg_rm + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 4017b9eed1..8da7baad76 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2109,3 +2109,40 @@ int32_t HELPER(sve_last_active_element)(void *vg, uint32_t pred_desc) return last_active_element(vg, DIV_ROUND_UP(oprsz, 8), esz); } + +void HELPER(sve_splice)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) +{ + intptr_t opr_sz = simd_oprsz(desc) / 8; + int esz = simd_data(desc); + uint64_t pg, first_g, last_g, len, mask = pred_esz_masks[esz]; + intptr_t i, first_i, last_i; + ARMVectorReg tmp; + + first_i = last_i = 0; + first_g = last_g = 0; + + /* Find the extent of the active elements within VG. */ + for (i = QEMU_ALIGN_UP(opr_sz, 8) - 8; i >= 0; i -= 8) { + pg = *(uint64_t *)(vg + i) & mask; + if (pg) { + if (last_g == 0) { + last_g = pg; + last_i = i; + } + first_g = pg; + first_i = i; + } + } + + len = 0; + if (first_g != 0) { + first_i = first_i * 8 + ctz64(first_g); + last_i = last_i * 8 + 63 - clz64(last_g); + len = last_i - first_i + (1 << esz); + if (vd == vm) { + vm = memcpy(&tmp, vm, opr_sz * 8); + } + swap_memmove(vd, vn + first_i, len); + } + swap_memmove(vd + len, vm, opr_sz * 8 - len); +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index f8d8cf1547..1517d82468 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -2681,6 +2681,19 @@ static bool trans_RBIT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } +static bool trans_SPLICE(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_4_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + vsz, vsz, a->esz, gen_helper_sve_splice); + } + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From d3fe4a29d754dee73cbf3cb7584db222981179ac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1289/2380] target/arm: Implement SVE Select Vectors Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 9 +++++++ target/arm/sve.decode | 6 +++++ target/arm/sve_helper.c | 55 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 2 ++ 4 files changed, 72 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index c3f8a2b502..0f57f64895 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -195,6 +195,15 @@ DEF_HELPER_FLAGS_5(sve_lsl_zpzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_lsl_zpzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_asr_zpzw_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_asr_zpzw_h, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index a9fa631252..91522d8e13 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -98,6 +98,7 @@ &rprr_esz rn=%reg_movprfx @rdm_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 \ &rprr_esz rm=%reg_movprfx +@rd_pg4_rn_rm ........ esz:2 . rm:5 .. pg:4 rn:5 rd:5 &rprr_esz # Three register operand, with governing predicate, vector element size @rda_pg_rn_rm ........ esz:2 . rm:5 ... pg:3 rn:5 rd:5 \ @@ -466,6 +467,11 @@ RBIT 00000101 .. 1001 11 100 ... ..... ..... @rd_pg_rn # SVE vector splice (predicated) SPLICE 00000101 .. 101 100 100 ... ..... ..... @rdn_pg_rm +### SVE Select Vectors Group + +# SVE select vector elements (predicated) +SEL_zpzz 00000101 .. 1 ..... 11 .... ..... ..... @rd_pg4_rn_rm + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 8da7baad76..f55fdc7dbe 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2146,3 +2146,58 @@ void HELPER(sve_splice)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) } swap_memmove(vd + len, vm, opr_sz * 8 - len); } + +void HELPER(sve_sel_zpzz_b)(void *vd, void *vn, void *vm, + void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn, *m = vm; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i], mm = m[i]; + uint64_t pp = expand_pred_b(pg[H1(i)]); + d[i] = (nn & pp) | (mm & ~pp); + } +} + +void HELPER(sve_sel_zpzz_h)(void *vd, void *vn, void *vm, + void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn, *m = vm; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i], mm = m[i]; + uint64_t pp = expand_pred_h(pg[H1(i)]); + d[i] = (nn & pp) | (mm & ~pp); + } +} + +void HELPER(sve_sel_zpzz_s)(void *vd, void *vn, void *vm, + void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn, *m = vm; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i], mm = m[i]; + uint64_t pp = expand_pred_s(pg[H1(i)]); + d[i] = (nn & pp) | (mm & ~pp); + } +} + +void HELPER(sve_sel_zpzz_d)(void *vd, void *vn, void *vm, + void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn, *m = vm; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i], mm = m[i]; + d[i] = (pg[H1(i)] & 1 ? nn : mm); + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 1517d82468..0de9118fdf 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -373,6 +373,8 @@ static bool trans_UDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) return do_zpzz_ool(s, a, fns[a->esz]); } +DO_ZPZZ(SEL, sel) + #undef DO_ZPZZ /* From 757f9cff1b63895bfd6fc8d66a6e52d7c40baa7b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1290/2380] target/arm: Implement SVE Integer Compare - Vectors Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 115 +++++++++++++++++++++++ target/arm/sve.decode | 24 +++++ target/arm/sve_helper.c | 187 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 91 ++++++++++++++++++ 4 files changed, 417 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 0f57f64895..6ffd1fbe8e 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -490,6 +490,121 @@ DEF_HELPER_FLAGS_4(sve_rbit_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_splice, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmple_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplt_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplo_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpls_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmple_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplt_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplo_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpls_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmple_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplt_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplo_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpls_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 91522d8e13..76a42193e4 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -99,6 +99,7 @@ @rdm_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 \ &rprr_esz rm=%reg_movprfx @rd_pg4_rn_rm ........ esz:2 . rm:5 .. pg:4 rn:5 rd:5 &rprr_esz +@pd_pg_rn_rm ........ esz:2 . rm:5 ... pg:3 rn:5 . rd:4 &rprr_esz # Three register operand, with governing predicate, vector element size @rda_pg_rn_rm ........ esz:2 . rm:5 ... pg:3 rn:5 rd:5 \ @@ -472,6 +473,29 @@ SPLICE 00000101 .. 101 100 100 ... ..... ..... @rdn_pg_rm # SVE select vector elements (predicated) SEL_zpzz 00000101 .. 1 ..... 11 .... ..... ..... @rd_pg4_rn_rm +### SVE Integer Compare - Vectors Group + +# SVE integer compare_vectors +CMPHS_ppzz 00100100 .. 0 ..... 000 ... ..... 0 .... @pd_pg_rn_rm +CMPHI_ppzz 00100100 .. 0 ..... 000 ... ..... 1 .... @pd_pg_rn_rm +CMPGE_ppzz 00100100 .. 0 ..... 100 ... ..... 0 .... @pd_pg_rn_rm +CMPGT_ppzz 00100100 .. 0 ..... 100 ... ..... 1 .... @pd_pg_rn_rm +CMPEQ_ppzz 00100100 .. 0 ..... 101 ... ..... 0 .... @pd_pg_rn_rm +CMPNE_ppzz 00100100 .. 0 ..... 101 ... ..... 1 .... @pd_pg_rn_rm + +# SVE integer compare with wide elements +# Note these require esz != 3. +CMPEQ_ppzw 00100100 .. 0 ..... 001 ... ..... 0 .... @pd_pg_rn_rm +CMPNE_ppzw 00100100 .. 0 ..... 001 ... ..... 1 .... @pd_pg_rn_rm +CMPGE_ppzw 00100100 .. 0 ..... 010 ... ..... 0 .... @pd_pg_rn_rm +CMPGT_ppzw 00100100 .. 0 ..... 010 ... ..... 1 .... @pd_pg_rn_rm +CMPLT_ppzw 00100100 .. 0 ..... 011 ... ..... 0 .... @pd_pg_rn_rm +CMPLE_ppzw 00100100 .. 0 ..... 011 ... ..... 1 .... @pd_pg_rn_rm +CMPHS_ppzw 00100100 .. 0 ..... 110 ... ..... 0 .... @pd_pg_rn_rm +CMPHI_ppzw 00100100 .. 0 ..... 110 ... ..... 1 .... @pd_pg_rn_rm +CMPLO_ppzw 00100100 .. 0 ..... 111 ... ..... 0 .... @pd_pg_rn_rm +CMPLS_ppzw 00100100 .. 0 ..... 111 ... ..... 1 .... @pd_pg_rn_rm + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index f55fdc7dbe..d11f591661 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -74,6 +74,28 @@ static uint32_t iter_predtest_fwd(uint64_t d, uint64_t g, uint32_t flags) return flags; } +/* This is an iterative function, called for each Pd and Pg word + * moving backward. + */ +static uint32_t iter_predtest_bwd(uint64_t d, uint64_t g, uint32_t flags) +{ + if (likely(g)) { + /* Compute C from first (i.e last) !(D & G). + Use bit 2 to signal first G bit seen. */ + if (!(flags & 4)) { + flags += 4 - 1; /* add bit 2, subtract C from PREDTEST_INIT */ + flags |= (d & pow2floor(g)) == 0; + } + + /* Accumulate Z from each D & G. */ + flags |= ((d & g) != 0) << 1; + + /* Compute N from last (i.e first) D & G. Replace previous. */ + flags = deposit32(flags, 31, 1, (d & (g & -g)) != 0); + } + return flags; +} + /* The same for a single word predicate. */ uint32_t HELPER(sve_predtest1)(uint64_t d, uint64_t g) { @@ -2201,3 +2223,168 @@ void HELPER(sve_sel_zpzz_d)(void *vd, void *vn, void *vm, d[i] = (pg[H1(i)] & 1 ? nn : mm); } } + +/* Two operand comparison controlled by a predicate. + * ??? It is very tempting to want to be able to expand this inline + * with x86 instructions, e.g. + * + * vcmpeqw zm, zn, %ymm0 + * vpmovmskb %ymm0, %eax + * and $0x5555, %eax + * and pg, %eax + * + * or even aarch64, e.g. + * + * // mask = 4000 1000 0400 0100 0040 0010 0004 0001 + * cmeq v0.8h, zn, zm + * and v0.8h, v0.8h, mask + * addv h0, v0.8h + * and v0.8b, pg + * + * However, coming up with an abstraction that allows vector inputs and + * a scalar output, and also handles the byte-ordering of sub-uint64_t + * scalar outputs, is tricky. + */ +#define DO_CMP_PPZZ(NAME, TYPE, OP, H, MASK) \ +uint32_t HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + intptr_t opr_sz = simd_oprsz(desc); \ + uint32_t flags = PREDTEST_INIT; \ + intptr_t i = opr_sz; \ + do { \ + uint64_t out = 0, pg; \ + do { \ + i -= sizeof(TYPE), out <<= sizeof(TYPE); \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + TYPE mm = *(TYPE *)(vm + H(i)); \ + out |= nn OP mm; \ + } while (i & 63); \ + pg = *(uint64_t *)(vg + (i >> 3)) & MASK; \ + out &= pg; \ + *(uint64_t *)(vd + (i >> 3)) = out; \ + flags = iter_predtest_bwd(out, pg, flags); \ + } while (i > 0); \ + return flags; \ +} + +#define DO_CMP_PPZZ_B(NAME, TYPE, OP) \ + DO_CMP_PPZZ(NAME, TYPE, OP, H1, 0xffffffffffffffffull) +#define DO_CMP_PPZZ_H(NAME, TYPE, OP) \ + DO_CMP_PPZZ(NAME, TYPE, OP, H1_2, 0x5555555555555555ull) +#define DO_CMP_PPZZ_S(NAME, TYPE, OP) \ + DO_CMP_PPZZ(NAME, TYPE, OP, H1_4, 0x1111111111111111ull) +#define DO_CMP_PPZZ_D(NAME, TYPE, OP) \ + DO_CMP_PPZZ(NAME, TYPE, OP, , 0x0101010101010101ull) + +DO_CMP_PPZZ_B(sve_cmpeq_ppzz_b, uint8_t, ==) +DO_CMP_PPZZ_H(sve_cmpeq_ppzz_h, uint16_t, ==) +DO_CMP_PPZZ_S(sve_cmpeq_ppzz_s, uint32_t, ==) +DO_CMP_PPZZ_D(sve_cmpeq_ppzz_d, uint64_t, ==) + +DO_CMP_PPZZ_B(sve_cmpne_ppzz_b, uint8_t, !=) +DO_CMP_PPZZ_H(sve_cmpne_ppzz_h, uint16_t, !=) +DO_CMP_PPZZ_S(sve_cmpne_ppzz_s, uint32_t, !=) +DO_CMP_PPZZ_D(sve_cmpne_ppzz_d, uint64_t, !=) + +DO_CMP_PPZZ_B(sve_cmpgt_ppzz_b, int8_t, >) +DO_CMP_PPZZ_H(sve_cmpgt_ppzz_h, int16_t, >) +DO_CMP_PPZZ_S(sve_cmpgt_ppzz_s, int32_t, >) +DO_CMP_PPZZ_D(sve_cmpgt_ppzz_d, int64_t, >) + +DO_CMP_PPZZ_B(sve_cmpge_ppzz_b, int8_t, >=) +DO_CMP_PPZZ_H(sve_cmpge_ppzz_h, int16_t, >=) +DO_CMP_PPZZ_S(sve_cmpge_ppzz_s, int32_t, >=) +DO_CMP_PPZZ_D(sve_cmpge_ppzz_d, int64_t, >=) + +DO_CMP_PPZZ_B(sve_cmphi_ppzz_b, uint8_t, >) +DO_CMP_PPZZ_H(sve_cmphi_ppzz_h, uint16_t, >) +DO_CMP_PPZZ_S(sve_cmphi_ppzz_s, uint32_t, >) +DO_CMP_PPZZ_D(sve_cmphi_ppzz_d, uint64_t, >) + +DO_CMP_PPZZ_B(sve_cmphs_ppzz_b, uint8_t, >=) +DO_CMP_PPZZ_H(sve_cmphs_ppzz_h, uint16_t, >=) +DO_CMP_PPZZ_S(sve_cmphs_ppzz_s, uint32_t, >=) +DO_CMP_PPZZ_D(sve_cmphs_ppzz_d, uint64_t, >=) + +#undef DO_CMP_PPZZ_B +#undef DO_CMP_PPZZ_H +#undef DO_CMP_PPZZ_S +#undef DO_CMP_PPZZ_D +#undef DO_CMP_PPZZ + +/* Similar, but the second source is "wide". */ +#define DO_CMP_PPZW(NAME, TYPE, TYPEW, OP, H, MASK) \ +uint32_t HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + intptr_t opr_sz = simd_oprsz(desc); \ + uint32_t flags = PREDTEST_INIT; \ + intptr_t i = opr_sz; \ + do { \ + uint64_t out = 0, pg; \ + do { \ + TYPEW mm = *(TYPEW *)(vm + i - 8); \ + do { \ + i -= sizeof(TYPE), out <<= sizeof(TYPE); \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + out |= nn OP mm; \ + } while (i & 7); \ + } while (i & 63); \ + pg = *(uint64_t *)(vg + (i >> 3)) & MASK; \ + out &= pg; \ + *(uint64_t *)(vd + (i >> 3)) = out; \ + flags = iter_predtest_bwd(out, pg, flags); \ + } while (i > 0); \ + return flags; \ +} + +#define DO_CMP_PPZW_B(NAME, TYPE, TYPEW, OP) \ + DO_CMP_PPZW(NAME, TYPE, TYPEW, OP, H1, 0xffffffffffffffffull) +#define DO_CMP_PPZW_H(NAME, TYPE, TYPEW, OP) \ + DO_CMP_PPZW(NAME, TYPE, TYPEW, OP, H1_2, 0x5555555555555555ull) +#define DO_CMP_PPZW_S(NAME, TYPE, TYPEW, OP) \ + DO_CMP_PPZW(NAME, TYPE, TYPEW, OP, H1_4, 0x1111111111111111ull) + +DO_CMP_PPZW_B(sve_cmpeq_ppzw_b, uint8_t, uint64_t, ==) +DO_CMP_PPZW_H(sve_cmpeq_ppzw_h, uint16_t, uint64_t, ==) +DO_CMP_PPZW_S(sve_cmpeq_ppzw_s, uint32_t, uint64_t, ==) + +DO_CMP_PPZW_B(sve_cmpne_ppzw_b, uint8_t, uint64_t, !=) +DO_CMP_PPZW_H(sve_cmpne_ppzw_h, uint16_t, uint64_t, !=) +DO_CMP_PPZW_S(sve_cmpne_ppzw_s, uint32_t, uint64_t, !=) + +DO_CMP_PPZW_B(sve_cmpgt_ppzw_b, int8_t, int64_t, >) +DO_CMP_PPZW_H(sve_cmpgt_ppzw_h, int16_t, int64_t, >) +DO_CMP_PPZW_S(sve_cmpgt_ppzw_s, int32_t, int64_t, >) + +DO_CMP_PPZW_B(sve_cmpge_ppzw_b, int8_t, int64_t, >=) +DO_CMP_PPZW_H(sve_cmpge_ppzw_h, int16_t, int64_t, >=) +DO_CMP_PPZW_S(sve_cmpge_ppzw_s, int32_t, int64_t, >=) + +DO_CMP_PPZW_B(sve_cmphi_ppzw_b, uint8_t, uint64_t, >) +DO_CMP_PPZW_H(sve_cmphi_ppzw_h, uint16_t, uint64_t, >) +DO_CMP_PPZW_S(sve_cmphi_ppzw_s, uint32_t, uint64_t, >) + +DO_CMP_PPZW_B(sve_cmphs_ppzw_b, uint8_t, uint64_t, >=) +DO_CMP_PPZW_H(sve_cmphs_ppzw_h, uint16_t, uint64_t, >=) +DO_CMP_PPZW_S(sve_cmphs_ppzw_s, uint32_t, uint64_t, >=) + +DO_CMP_PPZW_B(sve_cmplt_ppzw_b, int8_t, int64_t, <) +DO_CMP_PPZW_H(sve_cmplt_ppzw_h, int16_t, int64_t, <) +DO_CMP_PPZW_S(sve_cmplt_ppzw_s, int32_t, int64_t, <) + +DO_CMP_PPZW_B(sve_cmple_ppzw_b, int8_t, int64_t, <=) +DO_CMP_PPZW_H(sve_cmple_ppzw_h, int16_t, int64_t, <=) +DO_CMP_PPZW_S(sve_cmple_ppzw_s, int32_t, int64_t, <=) + +DO_CMP_PPZW_B(sve_cmplo_ppzw_b, uint8_t, uint64_t, <) +DO_CMP_PPZW_H(sve_cmplo_ppzw_h, uint16_t, uint64_t, <) +DO_CMP_PPZW_S(sve_cmplo_ppzw_s, uint32_t, uint64_t, <) + +DO_CMP_PPZW_B(sve_cmpls_ppzw_b, uint8_t, uint64_t, <=) +DO_CMP_PPZW_H(sve_cmpls_ppzw_h, uint16_t, uint64_t, <=) +DO_CMP_PPZW_S(sve_cmpls_ppzw_s, uint32_t, uint64_t, <=) + +#undef DO_CMP_PPZW_B +#undef DO_CMP_PPZW_H +#undef DO_CMP_PPZW_S +#undef DO_CMP_PPZW diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 0de9118fdf..1510af6ece 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -33,6 +33,10 @@ #include "trace-tcg.h" #include "translate-a64.h" + +typedef void gen_helper_gvec_flags_4(TCGv_i32, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_ptr, TCGv_i32); + /* * Helpers for extracting complex instruction fields. */ @@ -2696,6 +2700,93 @@ static bool trans_SPLICE(DisasContext *s, arg_rprr_esz *a, uint32_t insn) return true; } +/* + *** SVE Integer Compare - Vectors Group + */ + +static bool do_ppzz_flags(DisasContext *s, arg_rprr_esz *a, + gen_helper_gvec_flags_4 *gen_fn) +{ + TCGv_ptr pd, zn, zm, pg; + unsigned vsz; + TCGv_i32 t; + + if (gen_fn == NULL) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + vsz = vec_full_reg_size(s); + t = tcg_const_i32(simd_desc(vsz, vsz, 0)); + pd = tcg_temp_new_ptr(); + zn = tcg_temp_new_ptr(); + zm = tcg_temp_new_ptr(); + pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(pd, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(zn, cpu_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(zm, cpu_env, vec_full_reg_offset(s, a->rm)); + tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); + + gen_fn(t, pd, zn, zm, pg, t); + + tcg_temp_free_ptr(pd); + tcg_temp_free_ptr(zn); + tcg_temp_free_ptr(zm); + tcg_temp_free_ptr(pg); + + do_pred_flags(t); + + tcg_temp_free_i32(t); + return true; +} + +#define DO_PPZZ(NAME, name) \ +static bool trans_##NAME##_ppzz(DisasContext *s, arg_rprr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_flags_4 * const fns[4] = { \ + gen_helper_sve_##name##_ppzz_b, gen_helper_sve_##name##_ppzz_h, \ + gen_helper_sve_##name##_ppzz_s, gen_helper_sve_##name##_ppzz_d, \ + }; \ + return do_ppzz_flags(s, a, fns[a->esz]); \ +} + +DO_PPZZ(CMPEQ, cmpeq) +DO_PPZZ(CMPNE, cmpne) +DO_PPZZ(CMPGT, cmpgt) +DO_PPZZ(CMPGE, cmpge) +DO_PPZZ(CMPHI, cmphi) +DO_PPZZ(CMPHS, cmphs) + +#undef DO_PPZZ + +#define DO_PPZW(NAME, name) \ +static bool trans_##NAME##_ppzw(DisasContext *s, arg_rprr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_flags_4 * const fns[4] = { \ + gen_helper_sve_##name##_ppzw_b, gen_helper_sve_##name##_ppzw_h, \ + gen_helper_sve_##name##_ppzw_s, NULL \ + }; \ + return do_ppzz_flags(s, a, fns[a->esz]); \ +} + +DO_PPZW(CMPEQ, cmpeq) +DO_PPZW(CMPNE, cmpne) +DO_PPZW(CMPGT, cmpgt) +DO_PPZW(CMPGE, cmpge) +DO_PPZW(CMPHI, cmphi) +DO_PPZW(CMPHS, cmphs) +DO_PPZW(CMPLT, cmplt) +DO_PPZW(CMPLE, cmple) +DO_PPZW(CMPLO, cmplo) +DO_PPZW(CMPLS, cmpls) + +#undef DO_PPZW + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From 38cadeba0daf0f16cf2aeaa5b2752b26fb0676c5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1291/2380] target/arm: Implement SVE Integer Compare - Immediate Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 44 +++++++++++++++++++ target/arm/sve.decode | 23 ++++++++++ target/arm/sve_helper.c | 88 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 66 ++++++++++++++++++++++++++++ 4 files changed, 221 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 6ffd1fbe8e..ae38c0a4be 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -605,6 +605,50 @@ DEF_HELPER_FLAGS_5(sve_cmplo_ppzw_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_cmpls_ppzw_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpeq_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpne_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpgt_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpge_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplt_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmple_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphs_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphi_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplo_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpls_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cmpeq_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpne_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpgt_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpge_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplt_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmple_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphs_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphi_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplo_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpls_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cmpeq_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpne_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpgt_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpge_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplt_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmple_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphs_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphi_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplo_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpls_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cmpeq_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpne_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpgt_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpge_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplt_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmple_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphs_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphi_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplo_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpls_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 76a42193e4..9bc383b085 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -131,6 +131,11 @@ @rdn_dbm ........ .. .... dbm:13 rd:5 \ &rr_dbm rn=%reg_movprfx +# Predicate output, vector and immediate input, +# controlling predicate, element size. +@pd_pg_rn_i7 ........ esz:2 . imm:7 . pg:3 rn:5 . rd:4 &rpri_esz +@pd_pg_rn_i5 ........ esz:2 . imm:s5 ... pg:3 rn:5 . rd:4 &rpri_esz + # Basic Load/Store with 9-bit immediate offset @pd_rn_i9 ........ ........ ...... rn:5 . rd:4 \ &rri imm=%imm9_16_10 @@ -496,6 +501,24 @@ CMPHI_ppzw 00100100 .. 0 ..... 110 ... ..... 1 .... @pd_pg_rn_rm CMPLO_ppzw 00100100 .. 0 ..... 111 ... ..... 0 .... @pd_pg_rn_rm CMPLS_ppzw 00100100 .. 0 ..... 111 ... ..... 1 .... @pd_pg_rn_rm +### SVE Integer Compare - Unsigned Immediate Group + +# SVE integer compare with unsigned immediate +CMPHS_ppzi 00100100 .. 1 ....... 0 ... ..... 0 .... @pd_pg_rn_i7 +CMPHI_ppzi 00100100 .. 1 ....... 0 ... ..... 1 .... @pd_pg_rn_i7 +CMPLO_ppzi 00100100 .. 1 ....... 1 ... ..... 0 .... @pd_pg_rn_i7 +CMPLS_ppzi 00100100 .. 1 ....... 1 ... ..... 1 .... @pd_pg_rn_i7 + +### SVE Integer Compare - Signed Immediate Group + +# SVE integer compare with signed immediate +CMPGE_ppzi 00100101 .. 0 ..... 000 ... ..... 0 .... @pd_pg_rn_i5 +CMPGT_ppzi 00100101 .. 0 ..... 000 ... ..... 1 .... @pd_pg_rn_i5 +CMPLT_ppzi 00100101 .. 0 ..... 001 ... ..... 0 .... @pd_pg_rn_i5 +CMPLE_ppzi 00100101 .. 0 ..... 001 ... ..... 1 .... @pd_pg_rn_i5 +CMPEQ_ppzi 00100101 .. 0 ..... 100 ... ..... 0 .... @pd_pg_rn_i5 +CMPNE_ppzi 00100101 .. 0 ..... 100 ... ..... 1 .... @pd_pg_rn_i5 + ### SVE Predicate Logical Operations Group # SVE predicate logical operations diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index d11f591661..c1d95edfca 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2388,3 +2388,91 @@ DO_CMP_PPZW_S(sve_cmpls_ppzw_s, uint32_t, uint64_t, <=) #undef DO_CMP_PPZW_H #undef DO_CMP_PPZW_S #undef DO_CMP_PPZW + +/* Similar, but the second source is immediate. */ +#define DO_CMP_PPZI(NAME, TYPE, OP, H, MASK) \ +uint32_t HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t opr_sz = simd_oprsz(desc); \ + uint32_t flags = PREDTEST_INIT; \ + TYPE mm = simd_data(desc); \ + intptr_t i = opr_sz; \ + do { \ + uint64_t out = 0, pg; \ + do { \ + i -= sizeof(TYPE), out <<= sizeof(TYPE); \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + out |= nn OP mm; \ + } while (i & 63); \ + pg = *(uint64_t *)(vg + (i >> 3)) & MASK; \ + out &= pg; \ + *(uint64_t *)(vd + (i >> 3)) = out; \ + flags = iter_predtest_bwd(out, pg, flags); \ + } while (i > 0); \ + return flags; \ +} + +#define DO_CMP_PPZI_B(NAME, TYPE, OP) \ + DO_CMP_PPZI(NAME, TYPE, OP, H1, 0xffffffffffffffffull) +#define DO_CMP_PPZI_H(NAME, TYPE, OP) \ + DO_CMP_PPZI(NAME, TYPE, OP, H1_2, 0x5555555555555555ull) +#define DO_CMP_PPZI_S(NAME, TYPE, OP) \ + DO_CMP_PPZI(NAME, TYPE, OP, H1_4, 0x1111111111111111ull) +#define DO_CMP_PPZI_D(NAME, TYPE, OP) \ + DO_CMP_PPZI(NAME, TYPE, OP, , 0x0101010101010101ull) + +DO_CMP_PPZI_B(sve_cmpeq_ppzi_b, uint8_t, ==) +DO_CMP_PPZI_H(sve_cmpeq_ppzi_h, uint16_t, ==) +DO_CMP_PPZI_S(sve_cmpeq_ppzi_s, uint32_t, ==) +DO_CMP_PPZI_D(sve_cmpeq_ppzi_d, uint64_t, ==) + +DO_CMP_PPZI_B(sve_cmpne_ppzi_b, uint8_t, !=) +DO_CMP_PPZI_H(sve_cmpne_ppzi_h, uint16_t, !=) +DO_CMP_PPZI_S(sve_cmpne_ppzi_s, uint32_t, !=) +DO_CMP_PPZI_D(sve_cmpne_ppzi_d, uint64_t, !=) + +DO_CMP_PPZI_B(sve_cmpgt_ppzi_b, int8_t, >) +DO_CMP_PPZI_H(sve_cmpgt_ppzi_h, int16_t, >) +DO_CMP_PPZI_S(sve_cmpgt_ppzi_s, int32_t, >) +DO_CMP_PPZI_D(sve_cmpgt_ppzi_d, int64_t, >) + +DO_CMP_PPZI_B(sve_cmpge_ppzi_b, int8_t, >=) +DO_CMP_PPZI_H(sve_cmpge_ppzi_h, int16_t, >=) +DO_CMP_PPZI_S(sve_cmpge_ppzi_s, int32_t, >=) +DO_CMP_PPZI_D(sve_cmpge_ppzi_d, int64_t, >=) + +DO_CMP_PPZI_B(sve_cmphi_ppzi_b, uint8_t, >) +DO_CMP_PPZI_H(sve_cmphi_ppzi_h, uint16_t, >) +DO_CMP_PPZI_S(sve_cmphi_ppzi_s, uint32_t, >) +DO_CMP_PPZI_D(sve_cmphi_ppzi_d, uint64_t, >) + +DO_CMP_PPZI_B(sve_cmphs_ppzi_b, uint8_t, >=) +DO_CMP_PPZI_H(sve_cmphs_ppzi_h, uint16_t, >=) +DO_CMP_PPZI_S(sve_cmphs_ppzi_s, uint32_t, >=) +DO_CMP_PPZI_D(sve_cmphs_ppzi_d, uint64_t, >=) + +DO_CMP_PPZI_B(sve_cmplt_ppzi_b, int8_t, <) +DO_CMP_PPZI_H(sve_cmplt_ppzi_h, int16_t, <) +DO_CMP_PPZI_S(sve_cmplt_ppzi_s, int32_t, <) +DO_CMP_PPZI_D(sve_cmplt_ppzi_d, int64_t, <) + +DO_CMP_PPZI_B(sve_cmple_ppzi_b, int8_t, <=) +DO_CMP_PPZI_H(sve_cmple_ppzi_h, int16_t, <=) +DO_CMP_PPZI_S(sve_cmple_ppzi_s, int32_t, <=) +DO_CMP_PPZI_D(sve_cmple_ppzi_d, int64_t, <=) + +DO_CMP_PPZI_B(sve_cmplo_ppzi_b, uint8_t, <) +DO_CMP_PPZI_H(sve_cmplo_ppzi_h, uint16_t, <) +DO_CMP_PPZI_S(sve_cmplo_ppzi_s, uint32_t, <) +DO_CMP_PPZI_D(sve_cmplo_ppzi_d, uint64_t, <) + +DO_CMP_PPZI_B(sve_cmpls_ppzi_b, uint8_t, <=) +DO_CMP_PPZI_H(sve_cmpls_ppzi_h, uint16_t, <=) +DO_CMP_PPZI_S(sve_cmpls_ppzi_s, uint32_t, <=) +DO_CMP_PPZI_D(sve_cmpls_ppzi_d, uint64_t, <=) + +#undef DO_CMP_PPZI_B +#undef DO_CMP_PPZI_H +#undef DO_CMP_PPZI_S +#undef DO_CMP_PPZI_D +#undef DO_CMP_PPZI diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 1510af6ece..00481e92de 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -34,6 +34,8 @@ #include "translate-a64.h" +typedef void gen_helper_gvec_flags_3(TCGv_i32, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i32); typedef void gen_helper_gvec_flags_4(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); @@ -2787,6 +2789,70 @@ DO_PPZW(CMPLS, cmpls) #undef DO_PPZW +/* + *** SVE Integer Compare - Immediate Groups + */ + +static bool do_ppzi_flags(DisasContext *s, arg_rpri_esz *a, + gen_helper_gvec_flags_3 *gen_fn) +{ + TCGv_ptr pd, zn, pg; + unsigned vsz; + TCGv_i32 t; + + if (gen_fn == NULL) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + vsz = vec_full_reg_size(s); + t = tcg_const_i32(simd_desc(vsz, vsz, a->imm)); + pd = tcg_temp_new_ptr(); + zn = tcg_temp_new_ptr(); + pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(pd, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(zn, cpu_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); + + gen_fn(t, pd, zn, pg, t); + + tcg_temp_free_ptr(pd); + tcg_temp_free_ptr(zn); + tcg_temp_free_ptr(pg); + + do_pred_flags(t); + + tcg_temp_free_i32(t); + return true; +} + +#define DO_PPZI(NAME, name) \ +static bool trans_##NAME##_ppzi(DisasContext *s, arg_rpri_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_flags_3 * const fns[4] = { \ + gen_helper_sve_##name##_ppzi_b, gen_helper_sve_##name##_ppzi_h, \ + gen_helper_sve_##name##_ppzi_s, gen_helper_sve_##name##_ppzi_d, \ + }; \ + return do_ppzi_flags(s, a, fns[a->esz]); \ +} + +DO_PPZI(CMPEQ, cmpeq) +DO_PPZI(CMPNE, cmpne) +DO_PPZI(CMPGT, cmpgt) +DO_PPZI(CMPGE, cmpge) +DO_PPZI(CMPHI, cmphi) +DO_PPZI(CMPHS, cmphs) +DO_PPZI(CMPLT, cmplt) +DO_PPZI(CMPLE, cmple) +DO_PPZI(CMPLO, cmplo) +DO_PPZI(CMPLS, cmpls) + +#undef DO_PPZI + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From 35da316f5e847292ffbe7b6d16cd3988043dfe22 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1292/2380] target/arm: Implement SVE Partition Break Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 18 +++ target/arm/sve.decode | 19 +++ target/arm/sve_helper.c | 248 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 106 ++++++++++++++++ 4 files changed, 391 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index ae38c0a4be..f0a3ed3414 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -658,3 +658,21 @@ DEF_HELPER_FLAGS_5(sve_orn_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_nor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_nand_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_brkpa, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_brkpb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_brkpas, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_brkpbs, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_brka_z, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkb_z, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brka_m, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkb_m, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_brkas_z, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkbs_z, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkas_m, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkbs_m, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_brkn, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkns, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 9bc383b085..66e1ee6b5c 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -59,6 +59,7 @@ &rri_esz rd rn imm esz &rrr_esz rd rn rm esz &rpr_esz rd pg rn esz +&rpr_s rd pg rn s &rprr_s rd pg rn rm s &rprr_esz rd pg rn rm esz &rprrr_esz rd pg rn rm ra esz @@ -78,6 +79,9 @@ @pd_pn ........ esz:2 .. .... ....... rn:4 . rd:4 &rr_esz @rd_rn ........ esz:2 ...... ...... rn:5 rd:5 &rr_esz +# Two operand with governing predicate, flags setting +@pd_pg_pn_s ........ . s:1 ...... .. pg:4 . rn:4 . rd:4 &rpr_s + # Three operand with unused vector element size @rd_rn_rm_e0 ........ ... rm:5 ... ... rn:5 rd:5 &rrr_esz esz=0 @@ -560,6 +564,21 @@ PFIRST 00100101 01 011 000 11000 00 .... 0 .... @pd_pn_e0 # SVE predicate next active PNEXT 00100101 .. 011 001 11000 10 .... 0 .... @pd_pn +### SVE Partition Break Group + +# SVE propagate break from previous partition +BRKPA 00100101 0. 00 .... 11 .... 0 .... 0 .... @pd_pg_pn_pm_s +BRKPB 00100101 0. 00 .... 11 .... 0 .... 1 .... @pd_pg_pn_pm_s + +# SVE partition break condition +BRKA_z 00100101 0. 01000001 .... 0 .... 0 .... @pd_pg_pn_s +BRKB_z 00100101 1. 01000001 .... 0 .... 0 .... @pd_pg_pn_s +BRKA_m 00100101 0. 01000001 .... 0 .... 1 .... @pd_pg_pn_s +BRKB_m 00100101 1. 01000001 .... 0 .... 1 .... @pd_pg_pn_s + +# SVE propagate break to next partition +BRKN 00100101 0. 01100001 .... 0 .... 0 .... @pd_pg_pn_s + ### SVE Memory - 32-bit Gather and Unsized Contiguous Group # SVE load predicate register diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index c1d95edfca..b27b502d75 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2476,3 +2476,251 @@ DO_CMP_PPZI_D(sve_cmpls_ppzi_d, uint64_t, <=) #undef DO_CMP_PPZI_S #undef DO_CMP_PPZI_D #undef DO_CMP_PPZI + +/* Similar to the ARM LastActive pseudocode function. */ +static bool last_active_pred(void *vd, void *vg, intptr_t oprsz) +{ + intptr_t i; + + for (i = QEMU_ALIGN_UP(oprsz, 8) - 8; i >= 0; i -= 8) { + uint64_t pg = *(uint64_t *)(vg + i); + if (pg) { + return (pow2floor(pg) & *(uint64_t *)(vd + i)) != 0; + } + } + return 0; +} + +/* Compute a mask into RETB that is true for all G, up to and including + * (if after) or excluding (if !after) the first G & N. + * Return true if BRK found. + */ +static bool compute_brk(uint64_t *retb, uint64_t n, uint64_t g, + bool brk, bool after) +{ + uint64_t b; + + if (brk) { + b = 0; + } else if ((g & n) == 0) { + /* For all G, no N are set; break not found. */ + b = g; + } else { + /* Break somewhere in N. Locate it. */ + b = g & n; /* guard true, pred true */ + b = b & -b; /* first such */ + if (after) { + b = b | (b - 1); /* break after same */ + } else { + b = b - 1; /* break before same */ + } + brk = true; + } + + *retb = b; + return brk; +} + +/* Compute a zeroing BRK. */ +static void compute_brk_z(uint64_t *d, uint64_t *n, uint64_t *g, + intptr_t oprsz, bool after) +{ + bool brk = false; + intptr_t i; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); ++i) { + uint64_t this_b, this_g = g[i]; + + brk = compute_brk(&this_b, n[i], this_g, brk, after); + d[i] = this_b & this_g; + } +} + +/* Likewise, but also compute flags. */ +static uint32_t compute_brks_z(uint64_t *d, uint64_t *n, uint64_t *g, + intptr_t oprsz, bool after) +{ + uint32_t flags = PREDTEST_INIT; + bool brk = false; + intptr_t i; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); ++i) { + uint64_t this_b, this_d, this_g = g[i]; + + brk = compute_brk(&this_b, n[i], this_g, brk, after); + d[i] = this_d = this_b & this_g; + flags = iter_predtest_fwd(this_d, this_g, flags); + } + return flags; +} + +/* Compute a merging BRK. */ +static void compute_brk_m(uint64_t *d, uint64_t *n, uint64_t *g, + intptr_t oprsz, bool after) +{ + bool brk = false; + intptr_t i; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); ++i) { + uint64_t this_b, this_g = g[i]; + + brk = compute_brk(&this_b, n[i], this_g, brk, after); + d[i] = (this_b & this_g) | (d[i] & ~this_g); + } +} + +/* Likewise, but also compute flags. */ +static uint32_t compute_brks_m(uint64_t *d, uint64_t *n, uint64_t *g, + intptr_t oprsz, bool after) +{ + uint32_t flags = PREDTEST_INIT; + bool brk = false; + intptr_t i; + + for (i = 0; i < oprsz / 8; ++i) { + uint64_t this_b, this_d = d[i], this_g = g[i]; + + brk = compute_brk(&this_b, n[i], this_g, brk, after); + d[i] = this_d = (this_b & this_g) | (this_d & ~this_g); + flags = iter_predtest_fwd(this_d, this_g, flags); + } + return flags; +} + +static uint32_t do_zero(ARMPredicateReg *d, intptr_t oprsz) +{ + /* It is quicker to zero the whole predicate than loop on OPRSZ. + * The compiler should turn this into 4 64-bit integer stores. + */ + memset(d, 0, sizeof(ARMPredicateReg)); + return PREDTEST_INIT; +} + +void HELPER(sve_brkpa)(void *vd, void *vn, void *vm, void *vg, + uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + if (last_active_pred(vn, vg, oprsz)) { + compute_brk_z(vd, vm, vg, oprsz, true); + } else { + do_zero(vd, oprsz); + } +} + +uint32_t HELPER(sve_brkpas)(void *vd, void *vn, void *vm, void *vg, + uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + if (last_active_pred(vn, vg, oprsz)) { + return compute_brks_z(vd, vm, vg, oprsz, true); + } else { + return do_zero(vd, oprsz); + } +} + +void HELPER(sve_brkpb)(void *vd, void *vn, void *vm, void *vg, + uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + if (last_active_pred(vn, vg, oprsz)) { + compute_brk_z(vd, vm, vg, oprsz, false); + } else { + do_zero(vd, oprsz); + } +} + +uint32_t HELPER(sve_brkpbs)(void *vd, void *vn, void *vm, void *vg, + uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + if (last_active_pred(vn, vg, oprsz)) { + return compute_brks_z(vd, vm, vg, oprsz, false); + } else { + return do_zero(vd, oprsz); + } +} + +void HELPER(sve_brka_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + compute_brk_z(vd, vn, vg, oprsz, true); +} + +uint32_t HELPER(sve_brkas_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + return compute_brks_z(vd, vn, vg, oprsz, true); +} + +void HELPER(sve_brkb_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + compute_brk_z(vd, vn, vg, oprsz, false); +} + +uint32_t HELPER(sve_brkbs_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + return compute_brks_z(vd, vn, vg, oprsz, false); +} + +void HELPER(sve_brka_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + compute_brk_m(vd, vn, vg, oprsz, true); +} + +uint32_t HELPER(sve_brkas_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + return compute_brks_m(vd, vn, vg, oprsz, true); +} + +void HELPER(sve_brkb_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + compute_brk_m(vd, vn, vg, oprsz, false); +} + +uint32_t HELPER(sve_brkbs_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + return compute_brks_m(vd, vn, vg, oprsz, false); +} + +void HELPER(sve_brkn)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + + if (!last_active_pred(vn, vg, oprsz)) { + do_zero(vd, oprsz); + } +} + +/* As if PredTest(Ones(PL), D, esz). */ +static uint32_t predtest_ones(ARMPredicateReg *d, intptr_t oprsz, + uint64_t esz_mask) +{ + uint32_t flags = PREDTEST_INIT; + intptr_t i; + + for (i = 0; i < oprsz / 8; i++) { + flags = iter_predtest_fwd(d->p[i], esz_mask, flags); + } + if (oprsz & 7) { + uint64_t mask = ~(-1ULL << (8 * (oprsz & 7))); + flags = iter_predtest_fwd(d->p[i], esz_mask & mask, flags); + } + return flags; +} + +uint32_t HELPER(sve_brkns)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + + if (last_active_pred(vn, vg, oprsz)) { + return predtest_ones(vd, oprsz, -1); + } else { + return do_zero(vd, oprsz); + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 00481e92de..c381240b6f 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -2853,6 +2853,112 @@ DO_PPZI(CMPLS, cmpls) #undef DO_PPZI +/* + *** SVE Partition Break Group + */ + +static bool do_brk3(DisasContext *s, arg_rprr_s *a, + gen_helper_gvec_4 *fn, gen_helper_gvec_flags_4 *fn_s) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = pred_full_reg_size(s); + + /* Predicate sizes may be smaller and cannot use simd_desc. */ + TCGv_ptr d = tcg_temp_new_ptr(); + TCGv_ptr n = tcg_temp_new_ptr(); + TCGv_ptr m = tcg_temp_new_ptr(); + TCGv_ptr g = tcg_temp_new_ptr(); + TCGv_i32 t = tcg_const_i32(vsz - 2); + + tcg_gen_addi_ptr(d, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(m, cpu_env, pred_full_reg_offset(s, a->rm)); + tcg_gen_addi_ptr(g, cpu_env, pred_full_reg_offset(s, a->pg)); + + if (a->s) { + fn_s(t, d, n, m, g, t); + do_pred_flags(t); + } else { + fn(d, n, m, g, t); + } + tcg_temp_free_ptr(d); + tcg_temp_free_ptr(n); + tcg_temp_free_ptr(m); + tcg_temp_free_ptr(g); + tcg_temp_free_i32(t); + return true; +} + +static bool do_brk2(DisasContext *s, arg_rpr_s *a, + gen_helper_gvec_3 *fn, gen_helper_gvec_flags_3 *fn_s) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = pred_full_reg_size(s); + + /* Predicate sizes may be smaller and cannot use simd_desc. */ + TCGv_ptr d = tcg_temp_new_ptr(); + TCGv_ptr n = tcg_temp_new_ptr(); + TCGv_ptr g = tcg_temp_new_ptr(); + TCGv_i32 t = tcg_const_i32(vsz - 2); + + tcg_gen_addi_ptr(d, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(g, cpu_env, pred_full_reg_offset(s, a->pg)); + + if (a->s) { + fn_s(t, d, n, g, t); + do_pred_flags(t); + } else { + fn(d, n, g, t); + } + tcg_temp_free_ptr(d); + tcg_temp_free_ptr(n); + tcg_temp_free_ptr(g); + tcg_temp_free_i32(t); + return true; +} + +static bool trans_BRKPA(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + return do_brk3(s, a, gen_helper_sve_brkpa, gen_helper_sve_brkpas); +} + +static bool trans_BRKPB(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + return do_brk3(s, a, gen_helper_sve_brkpb, gen_helper_sve_brkpbs); +} + +static bool trans_BRKA_m(DisasContext *s, arg_rpr_s *a, uint32_t insn) +{ + return do_brk2(s, a, gen_helper_sve_brka_m, gen_helper_sve_brkas_m); +} + +static bool trans_BRKB_m(DisasContext *s, arg_rpr_s *a, uint32_t insn) +{ + return do_brk2(s, a, gen_helper_sve_brkb_m, gen_helper_sve_brkbs_m); +} + +static bool trans_BRKA_z(DisasContext *s, arg_rpr_s *a, uint32_t insn) +{ + return do_brk2(s, a, gen_helper_sve_brka_z, gen_helper_sve_brkas_z); +} + +static bool trans_BRKB_z(DisasContext *s, arg_rpr_s *a, uint32_t insn) +{ + return do_brk2(s, a, gen_helper_sve_brkb_z, gen_helper_sve_brkbs_z); +} + +static bool trans_BRKN(DisasContext *s, arg_rpr_s *a, uint32_t insn) +{ + return do_brk2(s, a, gen_helper_sve_brkn, gen_helper_sve_brkns); +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From 9ee3a611de28b8d0862fa687215b04b5aad20747 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1293/2380] target/arm: Implement SVE Predicate Count Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 2 + target/arm/sve.decode | 27 ++++++++ target/arm/sve_helper.c | 14 ++++ target/arm/translate-sve.c | 133 +++++++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index f0a3ed3414..dd4f8f754d 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -676,3 +676,5 @@ DEF_HELPER_FLAGS_4(sve_brkbs_m, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_brkn, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_brkns, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_cntp, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 66e1ee6b5c..62d51c252b 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -67,6 +67,8 @@ &ptrue rd esz pat s &incdec_cnt rd pat esz imm d u &incdec2_cnt rd rn pat esz imm d u +&incdec_pred rd pg esz d u +&incdec2_pred rd rn pg esz d u ########################################################################### # Named instruction formats. These are generally used to @@ -113,6 +115,7 @@ # One register operand, with governing predicate, vector element size @rd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 &rpr_esz +@rd_pg4_pn ........ esz:2 ... ... .. pg:4 . rn:4 rd:5 &rpr_esz # Two register operands with a 6-bit signed immediate. @rd_rn_i6 ........ ... rn:5 ..... imm:s6 rd:5 &rri @@ -153,6 +156,12 @@ @incdec2_cnt ........ esz:2 .. .... ...... pat:5 rd:5 \ &incdec2_cnt imm=%imm4_16_p1 rn=%reg_movprfx +# One register, predicate. +# User must fill in U and D. +@incdec_pred ........ esz:2 .... .. ..... .. pg:4 rd:5 &incdec_pred +@incdec2_pred ........ esz:2 .... .. ..... .. pg:4 rd:5 \ + &incdec2_pred rn=%reg_movprfx + ########################################################################### # Instruction patterns. Grouped according to the SVE encodingindex.xhtml. @@ -579,6 +588,24 @@ BRKB_m 00100101 1. 01000001 .... 0 .... 1 .... @pd_pg_pn_s # SVE propagate break to next partition BRKN 00100101 0. 01100001 .... 0 .... 0 .... @pd_pg_pn_s +### SVE Predicate Count Group + +# SVE predicate count +CNTP 00100101 .. 100 000 10 .... 0 .... ..... @rd_pg4_pn + +# SVE inc/dec register by predicate count +INCDECP_r 00100101 .. 10110 d:1 10001 00 .... ..... @incdec_pred u=1 + +# SVE inc/dec vector by predicate count +INCDECP_z 00100101 .. 10110 d:1 10000 00 .... ..... @incdec2_pred u=1 + +# SVE saturating inc/dec register by predicate count +SINCDECP_r_32 00100101 .. 1010 d:1 u:1 10001 00 .... ..... @incdec_pred +SINCDECP_r_64 00100101 .. 1010 d:1 u:1 10001 10 .... ..... @incdec_pred + +# SVE saturating inc/dec vector by predicate count +SINCDECP_z 00100101 .. 1010 d:1 u:1 10000 00 .... ..... @incdec2_pred + ### SVE Memory - 32-bit Gather and Unsized Contiguous Group # SVE load predicate register diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index b27b502d75..a4ecd653c1 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2724,3 +2724,17 @@ uint32_t HELPER(sve_brkns)(void *vd, void *vn, void *vg, uint32_t pred_desc) return do_zero(vd, oprsz); } } + +uint64_t HELPER(sve_cntp)(void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + uint64_t *n = vn, *g = vg, sum = 0, mask = pred_esz_masks[esz]; + intptr_t i; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); ++i) { + uint64_t t = n[i] & g[i] & mask; + sum += ctpop64(t); + } + return sum; +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index c381240b6f..6b0b8c55d0 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -34,6 +34,9 @@ #include "translate-a64.h" +typedef void GVecGen2sFn(unsigned, uint32_t, uint32_t, + TCGv_i64, uint32_t, uint32_t); + typedef void gen_helper_gvec_flags_3(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void gen_helper_gvec_flags_4(TCGv_i32, TCGv_ptr, TCGv_ptr, @@ -2959,6 +2962,136 @@ static bool trans_BRKN(DisasContext *s, arg_rpr_s *a, uint32_t insn) return do_brk2(s, a, gen_helper_sve_brkn, gen_helper_sve_brkns); } +/* + *** SVE Predicate Count Group + */ + +static void do_cntp(DisasContext *s, TCGv_i64 val, int esz, int pn, int pg) +{ + unsigned psz = pred_full_reg_size(s); + + if (psz <= 8) { + uint64_t psz_mask; + + tcg_gen_ld_i64(val, cpu_env, pred_full_reg_offset(s, pn)); + if (pn != pg) { + TCGv_i64 g = tcg_temp_new_i64(); + tcg_gen_ld_i64(g, cpu_env, pred_full_reg_offset(s, pg)); + tcg_gen_and_i64(val, val, g); + tcg_temp_free_i64(g); + } + + /* Reduce the pred_esz_masks value simply to reduce the + * size of the code generated here. + */ + psz_mask = MAKE_64BIT_MASK(0, psz * 8); + tcg_gen_andi_i64(val, val, pred_esz_masks[esz] & psz_mask); + + tcg_gen_ctpop_i64(val, val); + } else { + TCGv_ptr t_pn = tcg_temp_new_ptr(); + TCGv_ptr t_pg = tcg_temp_new_ptr(); + unsigned desc; + TCGv_i32 t_desc; + + desc = psz - 2; + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, esz); + + tcg_gen_addi_ptr(t_pn, cpu_env, pred_full_reg_offset(s, pn)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + t_desc = tcg_const_i32(desc); + + gen_helper_sve_cntp(val, t_pn, t_pg, t_desc); + tcg_temp_free_ptr(t_pn); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(t_desc); + } +} + +static bool trans_CNTP(DisasContext *s, arg_CNTP *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_cntp(s, cpu_reg(s, a->rd), a->esz, a->rn, a->pg); + } + return true; +} + +static bool trans_INCDECP_r(DisasContext *s, arg_incdec_pred *a, + uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 reg = cpu_reg(s, a->rd); + TCGv_i64 val = tcg_temp_new_i64(); + + do_cntp(s, val, a->esz, a->pg, a->pg); + if (a->d) { + tcg_gen_sub_i64(reg, reg, val); + } else { + tcg_gen_add_i64(reg, reg, val); + } + tcg_temp_free_i64(val); + } + return true; +} + +static bool trans_INCDECP_z(DisasContext *s, arg_incdec2_pred *a, + uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_i64 val = tcg_temp_new_i64(); + GVecGen2sFn *gvec_fn = a->d ? tcg_gen_gvec_subs : tcg_gen_gvec_adds; + + do_cntp(s, val, a->esz, a->pg, a->pg); + gvec_fn(a->esz, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), val, vsz, vsz); + } + return true; +} + +static bool trans_SINCDECP_r_32(DisasContext *s, arg_incdec_pred *a, + uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 reg = cpu_reg(s, a->rd); + TCGv_i64 val = tcg_temp_new_i64(); + + do_cntp(s, val, a->esz, a->pg, a->pg); + do_sat_addsub_32(reg, val, a->u, a->d); + } + return true; +} + +static bool trans_SINCDECP_r_64(DisasContext *s, arg_incdec_pred *a, + uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 reg = cpu_reg(s, a->rd); + TCGv_i64 val = tcg_temp_new_i64(); + + do_cntp(s, val, a->esz, a->pg, a->pg); + do_sat_addsub_64(reg, val, a->u, a->d); + } + return true; +} + +static bool trans_SINCDECP_z(DisasContext *s, arg_incdec2_pred *a, + uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + TCGv_i64 val = tcg_temp_new_i64(); + do_cntp(s, val, a->esz, a->pg, a->pg); + do_sat_addsub_vec(s, a->esz, a->rd, a->rn, val, a->u, a->d); + } + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From caf1cefc72be98497e0907d0e07f4327fc641e96 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1294/2380] target/arm: Implement SVE Integer Compare - Scalars Group Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-16-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 2 + target/arm/sve.decode | 8 +++ target/arm/sve_helper.c | 31 ++++++++++++ target/arm/translate-sve.c | 99 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index dd4f8f754d..1863106d0f 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -678,3 +678,5 @@ DEF_HELPER_FLAGS_4(sve_brkn, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_brkns, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_cntp, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_while, TCG_CALL_NO_RWG, i32, ptr, i32, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 62d51c252b..4b718060a9 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -606,6 +606,14 @@ SINCDECP_r_64 00100101 .. 1010 d:1 u:1 10001 10 .... ..... @incdec_pred # SVE saturating inc/dec vector by predicate count SINCDECP_z 00100101 .. 1010 d:1 u:1 10000 00 .... ..... @incdec2_pred +### SVE Integer Compare - Scalars Group + +# SVE conditionally terminate scalars +CTERM 00100101 1 sf:1 1 rm:5 001000 rn:5 ne:1 0000 + +# SVE integer compare scalar count and limit +WHILE 00100101 esz:2 1 rm:5 000 sf:1 u:1 1 rn:5 eq:1 rd:4 + ### SVE Memory - 32-bit Gather and Unsized Contiguous Group # SVE load predicate register diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index a4ecd653c1..8539595bd7 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2738,3 +2738,34 @@ uint64_t HELPER(sve_cntp)(void *vn, void *vg, uint32_t pred_desc) } return sum; } + +uint32_t HELPER(sve_while)(void *vd, uint32_t count, uint32_t pred_desc) +{ + uintptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + uint64_t esz_mask = pred_esz_masks[esz]; + ARMPredicateReg *d = vd; + uint32_t flags; + intptr_t i; + + /* Begin with a zero predicate register. */ + flags = do_zero(d, oprsz); + if (count == 0) { + return flags; + } + + /* Scale from predicate element count to bits. */ + count <<= esz; + /* Bound to the bits in the predicate. */ + count = MIN(count, oprsz * 8); + + /* Set all of the requested bits. */ + for (i = 0; i < count / 64; ++i) { + d->p[i] = esz_mask; + } + if (count & 63) { + d->p[i] = MAKE_64BIT_MASK(0, count & 63) & esz_mask; + } + + return predtest_ones(d, oprsz, esz_mask); +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 6b0b8c55d0..ae6a504f61 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3092,6 +3092,105 @@ static bool trans_SINCDECP_z(DisasContext *s, arg_incdec2_pred *a, return true; } +/* + *** SVE Integer Compare Scalars Group + */ + +static bool trans_CTERM(DisasContext *s, arg_CTERM *a, uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + TCGCond cond = (a->ne ? TCG_COND_NE : TCG_COND_EQ); + TCGv_i64 rn = read_cpu_reg(s, a->rn, a->sf); + TCGv_i64 rm = read_cpu_reg(s, a->rm, a->sf); + TCGv_i64 cmp = tcg_temp_new_i64(); + + tcg_gen_setcond_i64(cond, cmp, rn, rm); + tcg_gen_extrl_i64_i32(cpu_NF, cmp); + tcg_temp_free_i64(cmp); + + /* VF = !NF & !CF. */ + tcg_gen_xori_i32(cpu_VF, cpu_NF, 1); + tcg_gen_andc_i32(cpu_VF, cpu_VF, cpu_CF); + + /* Both NF and VF actually look at bit 31. */ + tcg_gen_neg_i32(cpu_NF, cpu_NF); + tcg_gen_neg_i32(cpu_VF, cpu_VF); + return true; +} + +static bool trans_WHILE(DisasContext *s, arg_WHILE *a, uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + TCGv_i64 op0 = read_cpu_reg(s, a->rn, 1); + TCGv_i64 op1 = read_cpu_reg(s, a->rm, 1); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i32 t2, t3; + TCGv_ptr ptr; + unsigned desc, vsz = vec_full_reg_size(s); + TCGCond cond; + + if (!a->sf) { + if (a->u) { + tcg_gen_ext32u_i64(op0, op0); + tcg_gen_ext32u_i64(op1, op1); + } else { + tcg_gen_ext32s_i64(op0, op0); + tcg_gen_ext32s_i64(op1, op1); + } + } + + /* For the helper, compress the different conditions into a computation + * of how many iterations for which the condition is true. + * + * This is slightly complicated by 0 <= UINT64_MAX, which is nominally + * 2**64 iterations, overflowing to 0. Of course, predicate registers + * aren't that large, so any value >= predicate size is sufficient. + */ + tcg_gen_sub_i64(t0, op1, op0); + + /* t0 = MIN(op1 - op0, vsz). */ + tcg_gen_movi_i64(t1, vsz); + tcg_gen_umin_i64(t0, t0, t1); + if (a->eq) { + /* Equality means one more iteration. */ + tcg_gen_addi_i64(t0, t0, 1); + } + + /* t0 = (condition true ? t0 : 0). */ + cond = (a->u + ? (a->eq ? TCG_COND_LEU : TCG_COND_LTU) + : (a->eq ? TCG_COND_LE : TCG_COND_LT)); + tcg_gen_movi_i64(t1, 0); + tcg_gen_movcond_i64(cond, t0, op0, op1, t0, t1); + + t2 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t2, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + + desc = (vsz / 8) - 2; + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); + t3 = tcg_const_i32(desc); + + ptr = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(ptr, cpu_env, pred_full_reg_offset(s, a->rd)); + + gen_helper_sve_while(t2, ptr, t2, t3); + do_pred_flags(t2); + + tcg_temp_free_ptr(ptr); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From ed49196125360c037d7f23c1c315a85cc234e72d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1295/2380] target/arm: Implement FDUP/DUP Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 8 ++++++++ target/arm/translate-sve.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 4b718060a9..b8bd22aff7 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -614,6 +614,14 @@ CTERM 00100101 1 sf:1 1 rm:5 001000 rn:5 ne:1 0000 # SVE integer compare scalar count and limit WHILE 00100101 esz:2 1 rm:5 000 sf:1 u:1 1 rn:5 eq:1 rd:4 +### SVE Integer Wide Immediate - Unpredicated Group + +# SVE broadcast floating-point immediate (unpredicated) +FDUP 00100101 esz:2 111 00 1110 imm:8 rd:5 + +# SVE broadcast integer immediate (unpredicated) +DUP_i 00100101 esz:2 111 00 011 . ........ rd:5 imm=%sh8_i8s + ### SVE Memory - 32-bit Gather and Unsized Contiguous Group # SVE load predicate register diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index ae6a504f61..13d5effff1 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3191,6 +3191,43 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a, uint32_t insn) return true; } +/* + *** SVE Integer Wide Immediate - Unpredicated Group + */ + +static bool trans_FDUP(DisasContext *s, arg_FDUP *a, uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + int dofs = vec_full_reg_offset(s, a->rd); + uint64_t imm; + + /* Decode the VFP immediate. */ + imm = vfp_expand_imm(a->esz, a->imm); + imm = dup_const(a->esz, imm); + + tcg_gen_gvec_dup64i(dofs, vsz, vsz, imm); + } + return true; +} + +static bool trans_DUP_i(DisasContext *s, arg_DUP_i *a, uint32_t insn) +{ + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + int dofs = vec_full_reg_offset(s, a->rd); + + tcg_gen_gvec_dup64i(dofs, vsz, vsz, dup_const(a->esz, a->imm)); + } + return true; +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From 6e6a157d683058d86a224cf128320d67b0915365 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1296/2380] target/arm: Implement SVE Integer Wide Immediate - Unpredicated Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 25 +++++++ target/arm/sve.decode | 26 +++++++ target/arm/sve_helper.c | 41 +++++++++++ target/arm/translate-sve.c | 144 +++++++++++++++++++++++++++++++++++++ 4 files changed, 236 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 1863106d0f..97bfe0f47b 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -680,3 +680,28 @@ DEF_HELPER_FLAGS_4(sve_brkns, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_cntp, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_while, TCG_CALL_NO_RWG, i32, ptr, i32, i32) + +DEF_HELPER_FLAGS_4(sve_subri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_subri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_subri_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_subri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_smaxi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smaxi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smaxi_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smaxi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_smini_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smini_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smini_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_umaxi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umaxi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umaxi_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umaxi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_umini_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umini_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umini_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index b8bd22aff7..eee8726bdf 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -42,6 +42,8 @@ # Signed 8-bit immediate, optionally shifted left by 8. %sh8_i8s 5:9 !function=expand_imm_sh8s +# Unsigned 8-bit immediate, optionally shifted left by 8. +%sh8_i8u 5:9 !function=expand_imm_sh8u # Either a copy of rd (at bit 0), or a different source # as propagated via the MOVPRFX instruction. @@ -95,6 +97,12 @@ @pd_pn_pm ........ esz:2 .. rm:4 ....... rn:4 . rd:4 &rrr_esz @rdn_rm ........ esz:2 ...... ...... rm:5 rd:5 \ &rrr_esz rn=%reg_movprfx +@rdn_sh_i8u ........ esz:2 ...... ...... ..... rd:5 \ + &rri_esz rn=%reg_movprfx imm=%sh8_i8u +@rdn_i8u ........ esz:2 ...... ... imm:8 rd:5 \ + &rri_esz rn=%reg_movprfx +@rdn_i8s ........ esz:2 ...... ... imm:s8 rd:5 \ + &rri_esz rn=%reg_movprfx # Three operand with "memory" size, aka immediate left shift @rd_rn_msz_rm ........ ... rm:5 .... imm:2 rn:5 rd:5 &rrri @@ -622,6 +630,24 @@ FDUP 00100101 esz:2 111 00 1110 imm:8 rd:5 # SVE broadcast integer immediate (unpredicated) DUP_i 00100101 esz:2 111 00 011 . ........ rd:5 imm=%sh8_i8s +# SVE integer add/subtract immediate (unpredicated) +ADD_zzi 00100101 .. 100 000 11 . ........ ..... @rdn_sh_i8u +SUB_zzi 00100101 .. 100 001 11 . ........ ..... @rdn_sh_i8u +SUBR_zzi 00100101 .. 100 011 11 . ........ ..... @rdn_sh_i8u +SQADD_zzi 00100101 .. 100 100 11 . ........ ..... @rdn_sh_i8u +UQADD_zzi 00100101 .. 100 101 11 . ........ ..... @rdn_sh_i8u +SQSUB_zzi 00100101 .. 100 110 11 . ........ ..... @rdn_sh_i8u +UQSUB_zzi 00100101 .. 100 111 11 . ........ ..... @rdn_sh_i8u + +# SVE integer min/max immediate (unpredicated) +SMAX_zzi 00100101 .. 101 000 110 ........ ..... @rdn_i8s +UMAX_zzi 00100101 .. 101 001 110 ........ ..... @rdn_i8u +SMIN_zzi 00100101 .. 101 010 110 ........ ..... @rdn_i8s +UMIN_zzi 00100101 .. 101 011 110 ........ ..... @rdn_i8u + +# SVE integer multiply immediate (unpredicated) +MUL_zzi 00100101 .. 110 000 110 ........ ..... @rdn_i8s + ### SVE Memory - 32-bit Gather and Unsized Contiguous Group # SVE load predicate register diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 8539595bd7..128bbf9b04 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -804,6 +804,46 @@ DO_VPZ_D(sve_uminv_d, uint64_t, uint64_t, -1, DO_MIN) #undef DO_VPZ #undef DO_VPZ_D +/* Two vector operand, one scalar operand, unpredicated. */ +#define DO_ZZI(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *vn, uint64_t s64, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(TYPE); \ + TYPE s = s64, *d = vd, *n = vn; \ + for (i = 0; i < opr_sz; ++i) { \ + d[i] = OP(n[i], s); \ + } \ +} + +#define DO_SUBR(X, Y) (Y - X) + +DO_ZZI(sve_subri_b, uint8_t, DO_SUBR) +DO_ZZI(sve_subri_h, uint16_t, DO_SUBR) +DO_ZZI(sve_subri_s, uint32_t, DO_SUBR) +DO_ZZI(sve_subri_d, uint64_t, DO_SUBR) + +DO_ZZI(sve_smaxi_b, int8_t, DO_MAX) +DO_ZZI(sve_smaxi_h, int16_t, DO_MAX) +DO_ZZI(sve_smaxi_s, int32_t, DO_MAX) +DO_ZZI(sve_smaxi_d, int64_t, DO_MAX) + +DO_ZZI(sve_smini_b, int8_t, DO_MIN) +DO_ZZI(sve_smini_h, int16_t, DO_MIN) +DO_ZZI(sve_smini_s, int32_t, DO_MIN) +DO_ZZI(sve_smini_d, int64_t, DO_MIN) + +DO_ZZI(sve_umaxi_b, uint8_t, DO_MAX) +DO_ZZI(sve_umaxi_h, uint16_t, DO_MAX) +DO_ZZI(sve_umaxi_s, uint32_t, DO_MAX) +DO_ZZI(sve_umaxi_d, uint64_t, DO_MAX) + +DO_ZZI(sve_umini_b, uint8_t, DO_MIN) +DO_ZZI(sve_umini_h, uint16_t, DO_MIN) +DO_ZZI(sve_umini_s, uint32_t, DO_MIN) +DO_ZZI(sve_umini_d, uint64_t, DO_MIN) + +#undef DO_ZZI + #undef DO_AND #undef DO_ORR #undef DO_EOR @@ -818,6 +858,7 @@ DO_VPZ_D(sve_uminv_d, uint64_t, uint64_t, -1, DO_MIN) #undef DO_ASR #undef DO_LSR #undef DO_LSL +#undef DO_SUBR /* Similar to the ARM LastActiveElement pseudocode function, except the result is multiplied by the element size. This includes the not found diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 13d5effff1..afd0b1638d 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -77,6 +77,11 @@ static inline int expand_imm_sh8s(int x) return (int8_t)x << (x & 0x100 ? 8 : 0); } +static inline int expand_imm_sh8u(int x) +{ + return (uint8_t)x << (x & 0x100 ? 8 : 0); +} + /* * Include the generated decoder. */ @@ -3228,6 +3233,145 @@ static bool trans_DUP_i(DisasContext *s, arg_DUP_i *a, uint32_t insn) return true; } +static bool trans_ADD_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_addi(a->esz, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), a->imm, vsz, vsz); + } + return true; +} + +static bool trans_SUB_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + a->imm = -a->imm; + return trans_ADD_zzi(s, a, insn); +} + +static bool trans_SUBR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + static const GVecGen2s op[4] = { + { .fni8 = tcg_gen_vec_sub8_i64, + .fniv = tcg_gen_sub_vec, + .fno = gen_helper_sve_subri_b, + .opc = INDEX_op_sub_vec, + .vece = MO_8, + .scalar_first = true }, + { .fni8 = tcg_gen_vec_sub16_i64, + .fniv = tcg_gen_sub_vec, + .fno = gen_helper_sve_subri_h, + .opc = INDEX_op_sub_vec, + .vece = MO_16, + .scalar_first = true }, + { .fni4 = tcg_gen_sub_i32, + .fniv = tcg_gen_sub_vec, + .fno = gen_helper_sve_subri_s, + .opc = INDEX_op_sub_vec, + .vece = MO_32, + .scalar_first = true }, + { .fni8 = tcg_gen_sub_i64, + .fniv = tcg_gen_sub_vec, + .fno = gen_helper_sve_subri_d, + .opc = INDEX_op_sub_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .vece = MO_64, + .scalar_first = true } + }; + + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_i64 c = tcg_const_i64(a->imm); + tcg_gen_gvec_2s(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vsz, vsz, c, &op[a->esz]); + tcg_temp_free_i64(c); + } + return true; +} + +static bool trans_MUL_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_muli(a->esz, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), a->imm, vsz, vsz); + } + return true; +} + +static bool do_zzi_sat(DisasContext *s, arg_rri_esz *a, uint32_t insn, + bool u, bool d) +{ + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + TCGv_i64 val = tcg_const_i64(a->imm); + do_sat_addsub_vec(s, a->esz, a->rd, a->rn, val, u, d); + tcg_temp_free_i64(val); + } + return true; +} + +static bool trans_SQADD_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_zzi_sat(s, a, insn, false, false); +} + +static bool trans_UQADD_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_zzi_sat(s, a, insn, true, false); +} + +static bool trans_SQSUB_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_zzi_sat(s, a, insn, false, true); +} + +static bool trans_UQSUB_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_zzi_sat(s, a, insn, true, true); +} + +static bool do_zzi_ool(DisasContext *s, arg_rri_esz *a, gen_helper_gvec_2i *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_i64 c = tcg_const_i64(a->imm); + + tcg_gen_gvec_2i_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + c, vsz, vsz, 0, fn); + tcg_temp_free_i64(c); + } + return true; +} + +#define DO_ZZI(NAME, name) \ +static bool trans_##NAME##_zzi(DisasContext *s, arg_rri_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_2i * const fns[4] = { \ + gen_helper_sve_##name##i_b, gen_helper_sve_##name##i_h, \ + gen_helper_sve_##name##i_s, gen_helper_sve_##name##i_d, \ + }; \ + return do_zzi_ool(s, a, fns[a->esz]); \ +} + +DO_ZZI(SMAX, smax) +DO_ZZI(UMAX, umax) +DO_ZZI(SMIN, smin) +DO_ZZI(UMIN, umin) + +#undef DO_ZZI + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From 29b80469dc51ae4064e9ef9223967882d2610523 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1297/2380] target/arm: Implement SVE Floating Point Arithmetic - Unpredicated Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180613015641.5667-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 14 ++++++++ target/arm/helper.h | 19 +++++++++++ target/arm/sve.decode | 10 ++++++ target/arm/translate-sve.c | 42 +++++++++++++++++++++++ target/arm/vec_helper.c | 69 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 97bfe0f47b..2e76084992 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -705,3 +705,17 @@ DEF_HELPER_FLAGS_4(sve_umini_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_umini_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_umini_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_umini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_5(gvec_recps_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_recps_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_recps_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_rsqrts_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/helper.h b/target/arm/helper.h index 0c6a144458..879a7229e9 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -601,6 +601,25 @@ DEF_HELPER_FLAGS_5(gvec_fcmlas_idx, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_fcmlad, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_ftsmul_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "helper-a64.h" #include "helper-sve.h" diff --git a/target/arm/sve.decode b/target/arm/sve.decode index eee8726bdf..6f436f9096 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -648,6 +648,16 @@ UMIN_zzi 00100101 .. 101 011 110 ........ ..... @rdn_i8u # SVE integer multiply immediate (unpredicated) MUL_zzi 00100101 .. 110 000 110 ........ ..... @rdn_i8s +### SVE Floating Point Arithmetic - Unpredicated Group + +# SVE floating-point arithmetic (unpredicated) +FADD_zzz 01100101 .. 0 ..... 000 000 ..... ..... @rd_rn_rm +FSUB_zzz 01100101 .. 0 ..... 000 001 ..... ..... @rd_rn_rm +FMUL_zzz 01100101 .. 0 ..... 000 010 ..... ..... @rd_rn_rm +FTSMUL 01100101 .. 0 ..... 000 011 ..... ..... @rd_rn_rm +FRECPS 01100101 .. 0 ..... 000 110 ..... ..... @rd_rn_rm +FRSQRTS 01100101 .. 0 ..... 000 111 ..... ..... @rd_rn_rm + ### SVE Memory - 32-bit Gather and Unsized Contiguous Group # SVE load predicate register diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index afd0b1638d..226c97579c 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3372,6 +3372,48 @@ DO_ZZI(UMIN, umin) #undef DO_ZZI +/* + *** SVE Floating Point Arithmetic - Unpredicated Group + */ + +static bool do_zzz_fp(DisasContext *s, arg_rrr_esz *a, + gen_helper_gvec_3_ptr *fn) +{ + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); + } + return true; +} + + +#define DO_FP3(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rrr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_3_ptr * const fns[4] = { \ + NULL, gen_helper_gvec_##name##_h, \ + gen_helper_gvec_##name##_s, gen_helper_gvec_##name##_d \ + }; \ + return do_zzz_fp(s, a, fns[a->esz]); \ +} + +DO_FP3(FADD_zzz, fadd) +DO_FP3(FSUB_zzz, fsub) +DO_FP3(FMUL_zzz, fmul) +DO_FP3(FTSMUL, ftsmul) +DO_FP3(FRECPS, recps) +DO_FP3(FRSQRTS, rsqrts) + +#undef DO_FP3 + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index 25e209da31..f504dd53c8 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -426,3 +426,72 @@ void HELPER(gvec_fcmlad)(void *vd, void *vn, void *vm, } clear_tail(d, opr_sz, simd_maxsz(desc)); } + +/* Floating-point trigonometric starting value. + * See the ARM ARM pseudocode function FPTrigSMul. + */ +static float16 float16_ftsmul(float16 op1, uint16_t op2, float_status *stat) +{ + float16 result = float16_mul(op1, op1, stat); + if (!float16_is_any_nan(result)) { + result = float16_set_sign(result, op2 & 1); + } + return result; +} + +static float32 float32_ftsmul(float32 op1, uint32_t op2, float_status *stat) +{ + float32 result = float32_mul(op1, op1, stat); + if (!float32_is_any_nan(result)) { + result = float32_set_sign(result, op2 & 1); + } + return result; +} + +static float64 float64_ftsmul(float64 op1, uint64_t op2, float_status *stat) +{ + float64 result = float64_mul(op1, op1, stat); + if (!float64_is_any_nan(result)) { + result = float64_set_sign(result, op2 & 1); + } + return result; +} + +#define DO_3OP(NAME, FUNC, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = FUNC(n[i], m[i], stat); \ + } \ +} + +DO_3OP(gvec_fadd_h, float16_add, float16) +DO_3OP(gvec_fadd_s, float32_add, float32) +DO_3OP(gvec_fadd_d, float64_add, float64) + +DO_3OP(gvec_fsub_h, float16_sub, float16) +DO_3OP(gvec_fsub_s, float32_sub, float32) +DO_3OP(gvec_fsub_d, float64_sub, float64) + +DO_3OP(gvec_fmul_h, float16_mul, float16) +DO_3OP(gvec_fmul_s, float32_mul, float32) +DO_3OP(gvec_fmul_d, float64_mul, float64) + +DO_3OP(gvec_ftsmul_h, float16_ftsmul, float16) +DO_3OP(gvec_ftsmul_s, float32_ftsmul, float32) +DO_3OP(gvec_ftsmul_d, float64_ftsmul, float64) + +#ifdef TARGET_AARCH64 + +DO_3OP(gvec_recps_h, helper_recpsf_f16, float16) +DO_3OP(gvec_recps_s, helper_recpsf_f32, float32) +DO_3OP(gvec_recps_d, helper_recpsf_f64, float64) + +DO_3OP(gvec_rsqrts_h, helper_rsqrtsf_f16, float16) +DO_3OP(gvec_rsqrts_s, helper_rsqrtsf_f32, float32) +DO_3OP(gvec_rsqrts_d, helper_rsqrtsf_f64, float64) + +#endif +#undef DO_3OP From acd9575e59da1bfc21a1feccb00c5dddd45328f7 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1298/2380] aspeed_scu: Implement RNG register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ASPEED SoCs contain a single register that returns random data when read. This models that register so that guests can use it. The random number data register has a corresponding control register, however it returns data regardless of the state of the enabled bit, so the model follows this behaviour. When the qcrypto call fails we exit as the guest uses the random number device to feed it's entropy pool, which is used for cryptographic purposes. Reviewed-by: Cédric Le Goater Signed-off-by: Joel Stanley Message-id: 20180613114836.9265-1-joel@jms.id.au Signed-off-by: Peter Maydell --- hw/misc/aspeed_scu.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 5e6d5744ee..59315010db 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -16,6 +16,7 @@ #include "qapi/visitor.h" #include "qemu/bitops.h" #include "qemu/log.h" +#include "crypto/random.h" #include "trace.h" #define TO_REG(offset) ((offset) >> 2) @@ -154,6 +155,19 @@ static const uint32_t ast2500_a1_resets[ASPEED_SCU_NR_REGS] = { [BMC_DEV_ID] = 0x00002402U }; +static uint32_t aspeed_scu_get_random(void) +{ + Error *err = NULL; + uint32_t num; + + if (qcrypto_random_bytes((uint8_t *)&num, sizeof(num), &err)) { + error_report_err(err); + exit(1); + } + + return num; +} + static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) { AspeedSCUState *s = ASPEED_SCU(opaque); @@ -167,6 +181,12 @@ static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) } switch (reg) { + case RNG_DATA: + /* On hardware, RNG_DATA works regardless of + * the state of the enable bit in RNG_CTRL + */ + s->regs[RNG_DATA] = aspeed_scu_get_random(); + break; case WAKEUP_EN: qemu_log_mask(LOG_GUEST_ERROR, "%s: Read of write-only offset 0x%" HWADDR_PRIx "\n", From 2151b044fdca74a4fe7148f302ba9d6191516744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1299/2380] m25p80: add support for two bytes WRSR for Macronix chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Macronix chips, two bytes can written to the WRSR. First byte will configure the status register and the second the configuration register. It is important to save the configuration value as it contains the dummy cycle setting when using dual or quad IO mode. Signed-off-by: Cédric Le Goater Acked-by: Alistair Francis Signed-off-by: Peter Maydell --- hw/block/m25p80.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index a5ccffb4aa..b0ed8fa418 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -698,6 +698,7 @@ static void complete_collecting_data(Flash *s) case MAN_MACRONIX: s->quad_enable = extract32(s->data[0], 6, 1); if (s->len > 1) { + s->volatile_cfg = s->data[1]; s->four_bytes_address_mode = extract32(s->data[1], 5, 1); } break; From 21f402093c5ee7363f5ba56916cd5c651b424fef Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:15 +0100 Subject: [PATCH 1300/2380] iommu: Add IOMMU index concept to IOMMU API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an IOMMU supports mappings that care about the memory transaction attributes, then it no longer has a unique address -> output mapping, but more than one. We can represent these using an IOMMU index, analogous to TCG's mmu indexes. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Message-id: 20180604152941.20374-2-peter.maydell@linaro.org --- include/exec/memory.h | 55 +++++++++++++++++++++++++++++++++++++++++++ memory.c | 23 ++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/include/exec/memory.h b/include/exec/memory.h index eb2ba06519..fa6e98ee7b 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -206,6 +206,20 @@ enum IOMMUMemoryRegionAttr { * to report whenever mappings are changed, by calling * memory_region_notify_iommu() (or, if necessary, by calling * memory_region_notify_one() for each registered notifier). + * + * Conceptually an IOMMU provides a mapping from input address + * to an output TLB entry. If the IOMMU is aware of memory transaction + * attributes and the output TLB entry depends on the transaction + * attributes, we represent this using IOMMU indexes. Each index + * selects a particular translation table that the IOMMU has: + * @attrs_to_index returns the IOMMU index for a set of transaction attributes + * @translate takes an input address and an IOMMU index + * and the mapping returned can only depend on the input address and the + * IOMMU index. + * + * Most IOMMUs don't care about the transaction attributes and support + * only a single IOMMU index. A more complex IOMMU might have one index + * for secure transactions and one for non-secure transactions. */ typedef struct IOMMUMemoryRegionClass { /* private */ @@ -290,6 +304,29 @@ typedef struct IOMMUMemoryRegionClass { */ int (*get_attr)(IOMMUMemoryRegion *iommu, enum IOMMUMemoryRegionAttr attr, void *data); + + /* Return the IOMMU index to use for a given set of transaction attributes. + * + * Optional method: if an IOMMU only supports a single IOMMU index then + * the default implementation of memory_region_iommu_attrs_to_index() + * will return 0. + * + * The indexes supported by an IOMMU must be contiguous, starting at 0. + * + * @iommu: the IOMMUMemoryRegion + * @attrs: memory transaction attributes + */ + int (*attrs_to_index)(IOMMUMemoryRegion *iommu, MemTxAttrs attrs); + + /* Return the number of IOMMU indexes this IOMMU supports. + * + * Optional method: if this method is not provided, then + * memory_region_iommu_num_indexes() will return 1, indicating that + * only a single IOMMU index is supported. + * + * @iommu: the IOMMUMemoryRegion + */ + int (*num_indexes)(IOMMUMemoryRegion *iommu); } IOMMUMemoryRegionClass; typedef struct CoalescedMemoryRange CoalescedMemoryRange; @@ -1054,6 +1091,24 @@ int memory_region_iommu_get_attr(IOMMUMemoryRegion *iommu_mr, enum IOMMUMemoryRegionAttr attr, void *data); +/** + * memory_region_iommu_attrs_to_index: return the IOMMU index to + * use for translations with the given memory transaction attributes. + * + * @iommu_mr: the memory region + * @attrs: the memory transaction attributes + */ +int memory_region_iommu_attrs_to_index(IOMMUMemoryRegion *iommu_mr, + MemTxAttrs attrs); + +/** + * memory_region_iommu_num_indexes: return the total number of IOMMU + * indexes that this IOMMU supports. + * + * @iommu_mr: the memory region + */ +int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr); + /** * memory_region_name: get a memory region's name * diff --git a/memory.c b/memory.c index 3212acc7f4..64f4a55d54 100644 --- a/memory.c +++ b/memory.c @@ -1915,6 +1915,29 @@ int memory_region_iommu_get_attr(IOMMUMemoryRegion *iommu_mr, return imrc->get_attr(iommu_mr, attr, data); } +int memory_region_iommu_attrs_to_index(IOMMUMemoryRegion *iommu_mr, + MemTxAttrs attrs) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + + if (!imrc->attrs_to_index) { + return 0; + } + + return imrc->attrs_to_index(iommu_mr, attrs); +} + +int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + + if (!imrc->num_indexes) { + return 1; + } + + return imrc->num_indexes(iommu_mr); +} + void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) { uint8_t mask = 1 << client; From cb1efcf462a2ff72926e0c5267a2e1d95490f347 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:16 +0100 Subject: [PATCH 1301/2380] iommu: Add IOMMU index argument to notifier APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for multiple IOMMU indexes to the IOMMU notifier APIs. When initializing a notifier with iommu_notifier_init(), the caller must pass the IOMMU index that it is interested in. When a change happens, the IOMMU implementation must pass memory_region_notify_iommu() the IOMMU index that has changed and that notifiers must be called for. IOMMUs which support only a single index don't need to change. Callers which only really support working with IOMMUs with a single index can use the result of passing MEMTXATTRS_UNSPECIFIED to memory_region_iommu_attrs_to_index(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Message-id: 20180604152941.20374-3-peter.maydell@linaro.org --- hw/i386/intel_iommu.c | 6 +++--- hw/ppc/spapr_iommu.c | 2 +- hw/s390x/s390-pci-inst.c | 4 ++-- hw/vfio/common.c | 6 +++++- hw/virtio/vhost.c | 7 ++++++- include/exec/memory.h | 7 ++++++- memory.c | 8 +++++++- 7 files changed, 30 insertions(+), 10 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index b5a09b7908..9c0b45963b 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1023,7 +1023,7 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry, void *private) { - memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); + memory_region_notify_iommu((IOMMUMemoryRegion *)private, 0, *entry); return 0; } @@ -1581,7 +1581,7 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, .addr_mask = size - 1, .perm = IOMMU_NONE, }; - memory_region_notify_iommu(&vtd_as->iommu, entry); + memory_region_notify_iommu(&vtd_as->iommu, 0, entry); } } } @@ -2015,7 +2015,7 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, entry.iova = addr; entry.perm = IOMMU_NONE; entry.translated_addr = 0; - memory_region_notify_iommu(&vtd_dev_as->iommu, entry); + memory_region_notify_iommu(&vtd_dev_as->iommu, 0, entry); done: return true; diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index aaa6010d5c..301708e45e 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -428,7 +428,7 @@ static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, entry.translated_addr = tce & page_mask; entry.addr_mask = ~page_mask; entry.perm = spapr_tce_iommu_access_flags(tce); - memory_region_notify_iommu(&tcet->iommu, entry); + memory_region_notify_iommu(&tcet->iommu, 0, entry); return H_SUCCESS; } diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index d1a5f79678..7b61367ee3 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -589,7 +589,7 @@ static void s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry) } notify.perm = IOMMU_NONE; - memory_region_notify_iommu(&iommu->iommu_mr, notify); + memory_region_notify_iommu(&iommu->iommu_mr, 0, notify); notify.perm = entry->perm; } @@ -601,7 +601,7 @@ static void s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry) g_hash_table_replace(iommu->iotlb, &cache->iova, cache); } - memory_region_notify_iommu(&iommu->iommu_mr, notify); + memory_region_notify_iommu(&iommu->iommu_mr, 0, notify); } int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 8e57265edf..fb396cf00a 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -507,6 +507,7 @@ static void vfio_listener_region_add(MemoryListener *listener, if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); + int iommu_idx; trace_vfio_listener_region_add_iommu(iova, end); /* @@ -523,10 +524,13 @@ static void vfio_listener_region_add(MemoryListener *listener, llend = int128_add(int128_make64(section->offset_within_region), section->size); llend = int128_sub(llend, int128_one()); + iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, + MEMTXATTRS_UNSPECIFIED); iommu_notifier_init(&giommu->n, vfio_iommu_map_notify, IOMMU_NOTIFIER_ALL, section->offset_within_region, - int128_get64(llend)); + int128_get64(llend), + iommu_idx); QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); memory_region_register_iommu_notifier(section->mr, &giommu->n); diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 96175b214d..b129cb9ddd 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -662,6 +662,8 @@ static void vhost_iommu_region_add(MemoryListener *listener, iommu_listener); struct vhost_iommu *iommu; Int128 end; + int iommu_idx; + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); if (!memory_region_is_iommu(section->mr)) { return; @@ -671,10 +673,13 @@ static void vhost_iommu_region_add(MemoryListener *listener, end = int128_add(int128_make64(section->offset_within_region), section->size); end = int128_sub(end, int128_one()); + iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, + MEMTXATTRS_UNSPECIFIED); iommu_notifier_init(&iommu->n, vhost_iommu_unmap_notify, IOMMU_NOTIFIER_UNMAP, section->offset_within_region, - int128_get64(end)); + int128_get64(end), + iommu_idx); iommu->mr = section->mr; iommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; diff --git a/include/exec/memory.h b/include/exec/memory.h index fa6e98ee7b..ec75c45296 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -98,18 +98,21 @@ struct IOMMUNotifier { /* Notify for address space range start <= addr <= end */ hwaddr start; hwaddr end; + int iommu_idx; QLIST_ENTRY(IOMMUNotifier) node; }; typedef struct IOMMUNotifier IOMMUNotifier; static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, - hwaddr start, hwaddr end) + hwaddr start, hwaddr end, + int iommu_idx) { n->notify = fn; n->notifier_flags = flags; n->start = start; n->end = end; + n->iommu_idx = iommu_idx; } /* @@ -1008,11 +1011,13 @@ uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr); * should be notified with an UNMAP followed by a MAP. * * @iommu_mr: the memory region that was changed + * @iommu_idx: the IOMMU index for the translation table which has changed * @entry: the new entry in the IOMMU translation table. The entry * replaces all old entries for the same virtual I/O address range. * Deleted entries have .@perm == 0. */ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, + int iommu_idx, IOMMUTLBEntry entry); /** diff --git a/memory.c b/memory.c index 64f4a55d54..7aa75ff02d 100644 --- a/memory.c +++ b/memory.c @@ -1799,6 +1799,9 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, iommu_mr = IOMMU_MEMORY_REGION(mr); assert(n->notifier_flags != IOMMU_NOTIFIER_NONE); assert(n->start <= n->end); + assert(n->iommu_idx >= 0 && + n->iommu_idx < memory_region_iommu_num_indexes(iommu_mr)); + QLIST_INSERT_HEAD(&iommu_mr->iommu_notify, n, node); memory_region_update_iommu_notify_flags(iommu_mr); } @@ -1891,6 +1894,7 @@ void memory_region_notify_one(IOMMUNotifier *notifier, } void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, + int iommu_idx, IOMMUTLBEntry entry) { IOMMUNotifier *iommu_notifier; @@ -1898,7 +1902,9 @@ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, assert(memory_region_is_iommu(MEMORY_REGION(iommu_mr))); IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) { - memory_region_notify_one(iommu_notifier, &entry); + if (iommu_notifier->iommu_idx == iommu_idx) { + memory_region_notify_one(iommu_notifier, &entry); + } } } From 2c91bcf273ffb95898d2ca901b699558d9e73fd1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:16 +0100 Subject: [PATCH 1302/2380] iommu: Add IOMMU index argument to translate method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an IOMMU index argument to the translate method of IOMMUs. Since all of our current IOMMU implementations support only a single IOMMU index, this has no effect on the behaviour. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Message-id: 20180604152941.20374-4-peter.maydell@linaro.org --- exec.c | 11 +++++++++-- hw/alpha/typhoon.c | 3 ++- hw/arm/smmuv3.c | 2 +- hw/dma/rc4030.c | 2 +- hw/i386/amd_iommu.c | 2 +- hw/i386/intel_iommu.c | 2 +- hw/ppc/spapr_iommu.c | 3 ++- hw/s390x/s390-pci-bus.c | 2 +- hw/sparc/sun4m_iommu.c | 3 ++- hw/sparc64/sun4u_iommu.c | 2 +- include/exec/memory.h | 3 ++- memory.c | 2 +- 12 files changed, 24 insertions(+), 13 deletions(-) diff --git a/exec.c b/exec.c index 1fa2cdb874..ecdf192994 100644 --- a/exec.c +++ b/exec.c @@ -501,8 +501,15 @@ static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iomm do { hwaddr addr = *xlat; IOMMUMemoryRegionClass *imrc = memory_region_get_iommu_class_nocheck(iommu_mr); - IOMMUTLBEntry iotlb = imrc->translate(iommu_mr, addr, is_write ? - IOMMU_WO : IOMMU_RO); + int iommu_idx = 0; + IOMMUTLBEntry iotlb; + + if (imrc->attrs_to_index) { + iommu_idx = imrc->attrs_to_index(iommu_mr, attrs); + } + + iotlb = imrc->translate(iommu_mr, addr, is_write ? + IOMMU_WO : IOMMU_RO, iommu_idx); if (!(iotlb.perm & (1 << is_write))) { goto unassigned; diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index 6a40869488..d3ed7cdbe8 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -666,7 +666,8 @@ static bool window_translate(TyphoonWindow *win, hwaddr addr, Pchip and generate a machine check interrupt. */ static IOMMUTLBEntry typhoon_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, + int iommu_idx) { TyphoonPchip *pchip = container_of(iommu, TyphoonPchip, iommu); IOMMUTLBEntry ret; diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 42dc521c13..978330900d 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -538,7 +538,7 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, } static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); SMMUv3State *s = sdev->smmu; diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index 5d4833eeca..ccd8612888 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -491,7 +491,7 @@ static const MemoryRegionOps jazzio_ops = { }; static IOMMUTLBEntry rc4030_dma_translate(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { rc4030State *s = container_of(iommu, rc4030State, dma_mr); IOMMUTLBEntry ret = { diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 63d46ff6ee..1fd669fef8 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -991,7 +991,7 @@ static inline bool amdvi_is_interrupt_addr(hwaddr addr) } static IOMMUTLBEntry amdvi_translate(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { AMDVIAddressSpace *as = container_of(iommu, AMDVIAddressSpace, iommu); AMDVIState *s = as->iommu_state; diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 9c0b45963b..0a8cd4e9cc 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2471,7 +2471,7 @@ static void vtd_mem_write(void *opaque, hwaddr addr, } static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 301708e45e..1b0880ac9e 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -112,7 +112,8 @@ static void spapr_tce_free_table(uint64_t *table, int fd, uint32_t nb_table) /* Called from RCU critical section */ static IOMMUTLBEntry spapr_tce_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, + int iommu_idx) { sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); uint64_t tce; diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 10da87458e..e3e0ebb7f6 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -484,7 +484,7 @@ uint16_t s390_guest_io_table_walk(uint64_t g_iota, hwaddr addr, } static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr); S390IOTLBEntry *entry; diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c index b677601fc6..7ca1e3fce4 100644 --- a/hw/sparc/sun4m_iommu.c +++ b/hw/sparc/sun4m_iommu.c @@ -282,7 +282,8 @@ static void iommu_bad_addr(IOMMUState *s, hwaddr addr, /* Called from RCU critical section */ static IOMMUTLBEntry sun4m_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flags) + IOMMUAccessFlags flags, + int iommu_idx) { IOMMUState *is = container_of(iommu, IOMMUState, iommu); hwaddr page, pa; diff --git a/hw/sparc64/sun4u_iommu.c b/hw/sparc64/sun4u_iommu.c index eb3aaa87e6..1ef7645ba5 100644 --- a/hw/sparc64/sun4u_iommu.c +++ b/hw/sparc64/sun4u_iommu.c @@ -73,7 +73,7 @@ /* Called from RCU critical section */ static IOMMUTLBEntry sun4u_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { IOMMUState *is = container_of(iommu, IOMMUState, iommu); hwaddr baseaddr, offset; diff --git a/include/exec/memory.h b/include/exec/memory.h index ec75c45296..050323f532 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -251,9 +251,10 @@ typedef struct IOMMUMemoryRegionClass { * @iommu: the IOMMUMemoryRegion * @hwaddr: address to be translated within the memory region * @flag: requested access permissions + * @iommu_idx: IOMMU index for the translation */ IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag); + IOMMUAccessFlags flag, int iommu_idx); /* Returns minimum supported page size in bytes. * If this method is not provided then the minimum is assumed to * be TARGET_PAGE_SIZE. diff --git a/memory.c b/memory.c index 7aa75ff02d..21aa57d24c 100644 --- a/memory.c +++ b/memory.c @@ -1832,7 +1832,7 @@ void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) granularity = memory_region_iommu_get_min_page_size(iommu_mr); for (addr = 0; addr < memory_region_size(mr); addr += granularity) { - iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE); + iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE, n->iommu_idx); if (iotlb.perm != IOMMU_NONE) { n->notify(n, &iotlb); } From 1f871c5e6b0f30644a60a81a6a7aadb3afb030ac Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jun 2018 14:57:16 +0100 Subject: [PATCH 1303/2380] exec.c: Handle IOMMUs in address_space_translate_for_iotlb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we don't support board configurations that put an IOMMU in the path of the CPU's memory transactions, and instead just assert() if the memory region fonud in address_space_translate_for_iotlb() is an IOMMUMemoryRegion. Remove this limitation by having the function handle IOMMUs. This is mostly straightforward, but we must make sure we have a notifier registered for every IOMMU that a transaction has passed through, so that we can flush the TLB appropriately when any of the IOMMUs change their mappings. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180604152941.20374-5-peter.maydell@linaro.org --- accel/tcg/cputlb.c | 3 +- exec.c | 135 +++++++++++++++++++++++++++++++++++++++- include/exec/exec-all.h | 3 +- include/qom/cpu.h | 3 + 4 files changed, 140 insertions(+), 4 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 1768fcdc47..0a721bb9c4 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -632,7 +632,8 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, } sz = size; - section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz); + section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz, + attrs, &prot); assert(sz >= TARGET_PAGE_SIZE); tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx diff --git a/exec.c b/exec.c index ecdf192994..ebadc0e302 100644 --- a/exec.c +++ b/exec.c @@ -653,18 +653,144 @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, return mr; } +typedef struct TCGIOMMUNotifier { + IOMMUNotifier n; + MemoryRegion *mr; + CPUState *cpu; + int iommu_idx; + bool active; +} TCGIOMMUNotifier; + +static void tcg_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) +{ + TCGIOMMUNotifier *notifier = container_of(n, TCGIOMMUNotifier, n); + + if (!notifier->active) { + return; + } + tlb_flush(notifier->cpu); + notifier->active = false; + /* We leave the notifier struct on the list to avoid reallocating it later. + * Generally the number of IOMMUs a CPU deals with will be small. + * In any case we can't unregister the iommu notifier from a notify + * callback. + */ +} + +static void tcg_register_iommu_notifier(CPUState *cpu, + IOMMUMemoryRegion *iommu_mr, + int iommu_idx) +{ + /* Make sure this CPU has an IOMMU notifier registered for this + * IOMMU/IOMMU index combination, so that we can flush its TLB + * when the IOMMU tells us the mappings we've cached have changed. + */ + MemoryRegion *mr = MEMORY_REGION(iommu_mr); + TCGIOMMUNotifier *notifier; + int i; + + for (i = 0; i < cpu->iommu_notifiers->len; i++) { + notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i); + if (notifier->mr == mr && notifier->iommu_idx == iommu_idx) { + break; + } + } + if (i == cpu->iommu_notifiers->len) { + /* Not found, add a new entry at the end of the array */ + cpu->iommu_notifiers = g_array_set_size(cpu->iommu_notifiers, i + 1); + notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i); + + notifier->mr = mr; + notifier->iommu_idx = iommu_idx; + notifier->cpu = cpu; + /* Rather than trying to register interest in the specific part + * of the iommu's address space that we've accessed and then + * expand it later as subsequent accesses touch more of it, we + * just register interest in the whole thing, on the assumption + * that iommu reconfiguration will be rare. + */ + iommu_notifier_init(¬ifier->n, + tcg_iommu_unmap_notify, + IOMMU_NOTIFIER_UNMAP, + 0, + HWADDR_MAX, + iommu_idx); + memory_region_register_iommu_notifier(notifier->mr, ¬ifier->n); + } + + if (!notifier->active) { + notifier->active = true; + } +} + +static void tcg_iommu_free_notifier_list(CPUState *cpu) +{ + /* Destroy the CPU's notifier list */ + int i; + TCGIOMMUNotifier *notifier; + + for (i = 0; i < cpu->iommu_notifiers->len; i++) { + notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i); + memory_region_unregister_iommu_notifier(notifier->mr, ¬ifier->n); + } + g_array_free(cpu->iommu_notifiers, true); +} + /* Called from RCU critical section */ MemoryRegionSection * address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, - hwaddr *xlat, hwaddr *plen) + hwaddr *xlat, hwaddr *plen, + MemTxAttrs attrs, int *prot) { MemoryRegionSection *section; + IOMMUMemoryRegion *iommu_mr; + IOMMUMemoryRegionClass *imrc; + IOMMUTLBEntry iotlb; + int iommu_idx; AddressSpaceDispatch *d = atomic_rcu_read(&cpu->cpu_ases[asidx].memory_dispatch); - section = address_space_translate_internal(d, addr, xlat, plen, false); + for (;;) { + section = address_space_translate_internal(d, addr, &addr, plen, false); + + iommu_mr = memory_region_get_iommu(section->mr); + if (!iommu_mr) { + break; + } + + imrc = memory_region_get_iommu_class_nocheck(iommu_mr); + + iommu_idx = imrc->attrs_to_index(iommu_mr, attrs); + tcg_register_iommu_notifier(cpu, iommu_mr, iommu_idx); + /* We need all the permissions, so pass IOMMU_NONE so the IOMMU + * doesn't short-cut its translation table walk. + */ + iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE, iommu_idx); + addr = ((iotlb.translated_addr & ~iotlb.addr_mask) + | (addr & iotlb.addr_mask)); + /* Update the caller's prot bits to remove permissions the IOMMU + * is giving us a failure response for. If we get down to no + * permissions left at all we can give up now. + */ + if (!(iotlb.perm & IOMMU_RO)) { + *prot &= ~(PAGE_READ | PAGE_EXEC); + } + if (!(iotlb.perm & IOMMU_WO)) { + *prot &= ~PAGE_WRITE; + } + + if (!*prot) { + goto translate_fail; + } + + d = flatview_to_dispatch(address_space_to_flatview(iotlb.target_as)); + } assert(!memory_region_is_iommu(section->mr)); + *xlat = addr; return section; + +translate_fail: + return &d->map.sections[PHYS_SECTION_UNASSIGNED]; } #endif @@ -823,6 +949,9 @@ void cpu_exec_unrealizefn(CPUState *cpu) if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { vmstate_unregister(NULL, &vmstate_cpu_common, cpu); } +#ifndef CONFIG_USER_ONLY + tcg_iommu_free_notifier_list(cpu); +#endif } Property cpu_common_props[] = { @@ -870,6 +999,8 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp) if (cc->vmsd != NULL) { vmstate_register(NULL, cpu->cpu_index, cc->vmsd, cpu); } + + cpu->iommu_notifiers = g_array_new(false, true, sizeof(TCGIOMMUNotifier)); #endif } diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index aed55aaaa7..8bbea787a9 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -478,7 +478,8 @@ void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr); MemoryRegionSection * address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, - hwaddr *xlat, hwaddr *plen); + hwaddr *xlat, hwaddr *plen, + MemTxAttrs attrs, int *prot); hwaddr memory_region_section_get_iotlb(CPUState *cpu, MemoryRegionSection *section, target_ulong vaddr, diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 9d3afc6c75..cce2fd6acc 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -429,6 +429,9 @@ struct CPUState { uint16_t pending_tlb_flush; int hvf_fd; + + /* track IOMMUs whose translations we've cached in the TCG TLB */ + GArray *iommu_notifiers; }; QTAILQ_HEAD(CPUTailQ, CPUState); From 14120108f87b3f9e1beacdf0a6096e464e62bb65 Mon Sep 17 00:00:00 2001 From: Julia Suvorova Date: Fri, 15 Jun 2018 14:57:16 +0100 Subject: [PATCH 1304/2380] target/arm: Allow ARMv6-M Thumb2 instructions ARMv6-M supports 6 Thumb2 instructions. This patch checks for these instructions and allows their execution. Like Thumb2 cores, ARMv6-M always interprets BL instruction as 32-bit. This patch is required for future Cortex-M0 support. Signed-off-by: Julia Suvorova Reviewed-by: Stefan Hajnoczi Message-id: 20180612204632.28780-1-jusual@mail.ru [PMM: move armv6m_insn[] and armv6m_mask[] closer to point of use, and mark 'const'. Check for M-and-not-v7 rather than M-and-6.] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate.c | 43 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index 0ff5edf2ce..f405c82fb2 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -9965,7 +9965,8 @@ static bool thumb_insn_is_16bit(DisasContext *s, uint32_t insn) * end up actually treating this as two 16-bit insns, though, * if it's half of a bl/blx pair that might span a page boundary. */ - if (arm_dc_feature(s, ARM_FEATURE_THUMB2)) { + if (arm_dc_feature(s, ARM_FEATURE_THUMB2) || + arm_dc_feature(s, ARM_FEATURE_M)) { /* Thumb2 cores (including all M profile ones) always treat * 32-bit insns as 32-bit. */ @@ -10085,10 +10086,38 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) int conds; int logic_cc; - /* The only 32 bit insn that's allowed for Thumb1 is the combined - * BL/BLX prefix and suffix. + /* + * ARMv6-M supports a limited subset of Thumb2 instructions. + * Other Thumb1 architectures allow only 32-bit + * combined BL/BLX prefix and suffix. */ - if ((insn & 0xf800e800) != 0xf000e800) { + if (arm_dc_feature(s, ARM_FEATURE_M) && + !arm_dc_feature(s, ARM_FEATURE_V7)) { + int i; + bool found = false; + const uint32_t armv6m_insn[] = {0xf3808000 /* msr */, + 0xf3b08040 /* dsb */, + 0xf3b08050 /* dmb */, + 0xf3b08060 /* isb */, + 0xf3e08000 /* mrs */, + 0xf000d000 /* bl */}; + const uint32_t armv6m_mask[] = {0xffe0d000, + 0xfff0d0f0, + 0xfff0d0f0, + 0xfff0d0f0, + 0xffe0d000, + 0xf800d000}; + + for (i = 0; i < ARRAY_SIZE(armv6m_insn); i++) { + if ((insn & armv6m_mask[i]) == armv6m_insn[i]) { + found = true; + break; + } + } + if (!found) { + goto illegal_op; + } + } else if ((insn & 0xf800e800) != 0xf000e800) { ARCH(6T2); } @@ -11009,7 +11038,11 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) } break; case 3: /* Special control operations. */ - ARCH(7); + if (!arm_dc_feature(s, ARM_FEATURE_V7) && + !(arm_dc_feature(s, ARM_FEATURE_V6) && + arm_dc_feature(s, ARM_FEATURE_M))) { + goto illegal_op; + } op = (insn >> 4) & 0xf; switch (op) { case 2: /* clrex */ From 1019242af11400252f6735ca71a35f81ac23a66d Mon Sep 17 00:00:00 2001 From: John Arbuckle Date: Mon, 4 Jun 2018 17:51:02 -0400 Subject: [PATCH 1305/2380] tcg/i386: Use byte form of xgetbv instruction The assembler in most versions of Mac OS X is pretty old and does not support the xgetbv instruction. To go around this problem, the raw encoding of the instruction is used instead. Signed-off-by: John Arbuckle Message-Id: <20180604215102.11002-1-programmingkidx@gmail.com> Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.inc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index 5357909fff..0d0ff524b7 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -3501,7 +3501,10 @@ static void tcg_target_init(TCGContext *s) sure of not hitting invalid opcode. */ if (c & bit_OSXSAVE) { unsigned xcrl, xcrh; - asm ("xgetbv" : "=a" (xcrl), "=d" (xcrh) : "c" (0)); + /* The xgetbv instruction is not available to older versions of + * the assembler, so we encode the instruction manually. + */ + asm(".byte 0x0f, 0x01, 0xd0" : "=a" (xcrl), "=d" (xcrh) : "c" (0)); if ((xcrl & 6) == 6) { have_avx1 = (c & bit_AVX) != 0; have_avx2 = (b7 & bit_AVX2) != 0; From 61b8cef1d42567d3029e0c7180cbd0f16cc4be2d Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 11 Jul 2017 18:47:38 -0400 Subject: [PATCH 1306/2380] qht: require a default comparison function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qht_lookup now uses the default cmp function. qht_lookup_custom is defined to retain the old behaviour, that is a cmp function is explicitly provided. qht_insert will gain use of the default cmp in the next patch. Note that we move qht_lookup_custom's @func to be the last argument, which makes the new qht_lookup as simple as possible. Instead of this (i.e. keeping @func 2nd): 0000000000010750 : 10750: 89 d1 mov %edx,%ecx 10752: 48 89 f2 mov %rsi,%rdx 10755: 48 8b 77 08 mov 0x8(%rdi),%rsi 10759: e9 22 ff ff ff jmpq 10680 1075e: 66 90 xchg %ax,%ax We get: 0000000000010740 : 10740: 48 8b 4f 08 mov 0x8(%rdi),%rcx 10744: e9 37 ff ff ff jmpq 10680 10749: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 4 ++-- accel/tcg/translate-all.c | 16 +++++++++++++++- include/qemu/qht.h | 25 ++++++++++++++++++++----- tests/qht-bench.c | 14 +++++++------- tests/test-qht.c | 15 ++++++++++----- util/qht.c | 14 +++++++++++--- 6 files changed, 65 insertions(+), 23 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 4ef95d8dd3..6d6c51b686 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -295,7 +295,7 @@ struct tb_desc { uint32_t trace_vcpu_dstate; }; -static bool tb_cmp(const void *p, const void *d) +static bool tb_lookup_cmp(const void *p, const void *d) { const TranslationBlock *tb = p; const struct tb_desc *desc = d; @@ -340,7 +340,7 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, phys_pc = get_page_addr_code(desc.env, pc); desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); - return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h); + return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); } void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index d48b56ca38..f39123bd5a 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -782,11 +782,25 @@ static inline void code_gen_alloc(size_t tb_size) qemu_mutex_init(&tb_ctx.tb_lock); } +static bool tb_cmp(const void *ap, const void *bp) +{ + const TranslationBlock *a = ap; + const TranslationBlock *b = bp; + + return a->pc == b->pc && + a->cs_base == b->cs_base && + a->flags == b->flags && + (tb_cflags(a) & CF_HASH_MASK) == (tb_cflags(b) & CF_HASH_MASK) && + a->trace_vcpu_dstate == b->trace_vcpu_dstate && + a->page_addr[0] == b->page_addr[0] && + a->page_addr[1] == b->page_addr[1]; +} + static void tb_htable_init(void) { unsigned int mode = QHT_MODE_AUTO_RESIZE; - qht_init(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE, mode); + qht_init(&tb_ctx.htable, tb_cmp, CODE_GEN_HTABLE_SIZE, mode); } /* Must be called before using the QEMU cpus. 'tb_size' is the size diff --git a/include/qemu/qht.h b/include/qemu/qht.h index 531aa95325..5f03a0f4cb 100644 --- a/include/qemu/qht.h +++ b/include/qemu/qht.h @@ -11,8 +11,11 @@ #include "qemu/thread.h" #include "qemu/qdist.h" +typedef bool (*qht_cmp_func_t)(const void *a, const void *b); + struct qht { struct qht_map *map; + qht_cmp_func_t cmp; QemuMutex lock; /* serializes setters of ht->map */ unsigned int mode; }; @@ -47,10 +50,12 @@ typedef void (*qht_iter_func_t)(struct qht *ht, void *p, uint32_t h, void *up); /** * qht_init - Initialize a QHT * @ht: QHT to be initialized + * @cmp: default comparison function. Cannot be NULL. * @n_elems: number of entries the hash table should be optimized for. * @mode: bitmask with OR'ed QHT_MODE_* */ -void qht_init(struct qht *ht, size_t n_elems, unsigned int mode); +void qht_init(struct qht *ht, qht_cmp_func_t cmp, size_t n_elems, + unsigned int mode); /** * qht_destroy - destroy a previously initialized QHT @@ -78,11 +83,11 @@ void qht_destroy(struct qht *ht); bool qht_insert(struct qht *ht, void *p, uint32_t hash); /** - * qht_lookup - Look up a pointer in a QHT + * qht_lookup_custom - Look up a pointer using a custom comparison function. * @ht: QHT to be looked up - * @func: function to compare existing pointers against @userp * @userp: pointer to pass to @func * @hash: hash of the pointer to be looked up + * @func: function to compare existing pointers against @userp * * Needs to be called under an RCU read-critical section. * @@ -94,8 +99,18 @@ bool qht_insert(struct qht *ht, void *p, uint32_t hash); * Returns the corresponding pointer when a match is found. * Returns NULL otherwise. */ -void *qht_lookup(struct qht *ht, qht_lookup_func_t func, const void *userp, - uint32_t hash); +void *qht_lookup_custom(struct qht *ht, const void *userp, uint32_t hash, + qht_lookup_func_t func); + +/** + * qht_lookup - Look up a pointer in a QHT + * @ht: QHT to be looked up + * @userp: pointer to pass to the comparison function + * @hash: hash of the pointer to be looked up + * + * Calls qht_lookup_custom() using @ht's default comparison function. + */ +void *qht_lookup(struct qht *ht, const void *userp, uint32_t hash); /** * qht_remove - remove a pointer from the hash table diff --git a/tests/qht-bench.c b/tests/qht-bench.c index 4cabdfd62a..c94ac25158 100644 --- a/tests/qht-bench.c +++ b/tests/qht-bench.c @@ -93,10 +93,10 @@ static void usage_complete(int argc, char *argv[]) exit(-1); } -static bool is_equal(const void *obj, const void *userp) +static bool is_equal(const void *ap, const void *bp) { - const long *a = obj; - const long *b = userp; + const long *a = ap; + const long *b = bp; return *a == *b; } @@ -150,7 +150,7 @@ static void do_rw(struct thread_info *info) p = &keys[info->r & (lookup_range - 1)]; hash = h(*p); - read = qht_lookup(&ht, is_equal, p, hash); + read = qht_lookup(&ht, p, hash); if (read) { stats->rd++; } else { @@ -162,7 +162,7 @@ static void do_rw(struct thread_info *info) if (info->write_op) { bool written = false; - if (qht_lookup(&ht, is_equal, p, hash) == NULL) { + if (qht_lookup(&ht, p, hash) == NULL) { written = qht_insert(&ht, p, hash); } if (written) { @@ -173,7 +173,7 @@ static void do_rw(struct thread_info *info) } else { bool removed = false; - if (qht_lookup(&ht, is_equal, p, hash)) { + if (qht_lookup(&ht, p, hash)) { removed = qht_remove(&ht, p, hash); } if (removed) { @@ -308,7 +308,7 @@ static void htable_init(void) } /* initialize the hash table */ - qht_init(&ht, qht_n_elems, qht_mode); + qht_init(&ht, is_equal, qht_n_elems, qht_mode); assert(init_size <= init_range); pr_params(); diff --git a/tests/test-qht.c b/tests/test-qht.c index 9b7423abb6..b069881342 100644 --- a/tests/test-qht.c +++ b/tests/test-qht.c @@ -13,10 +13,10 @@ static struct qht ht; static int32_t arr[N * 2]; -static bool is_equal(const void *obj, const void *userp) +static bool is_equal(const void *ap, const void *bp) { - const int32_t *a = obj; - const int32_t *b = userp; + const int32_t *a = ap; + const int32_t *b = bp; return *a == *b; } @@ -60,7 +60,12 @@ static void check(int a, int b, bool expected) val = i; hash = i; - p = qht_lookup(&ht, is_equal, &val, hash); + /* test both lookup variants; results should be the same */ + if (i % 2) { + p = qht_lookup(&ht, &val, hash); + } else { + p = qht_lookup_custom(&ht, &val, hash, is_equal); + } g_assert_true(!!p == expected); } rcu_read_unlock(); @@ -102,7 +107,7 @@ static void qht_do_test(unsigned int mode, size_t init_entries) /* under KVM we might fetch stats from an uninitialized qht */ check_n(0); - qht_init(&ht, 0, mode); + qht_init(&ht, is_equal, 0, mode); check_n(0); insert(0, N); diff --git a/util/qht.c b/util/qht.c index 55b0738cd1..487e9237bc 100644 --- a/util/qht.c +++ b/util/qht.c @@ -351,11 +351,14 @@ static struct qht_map *qht_map_create(size_t n_buckets) return map; } -void qht_init(struct qht *ht, size_t n_elems, unsigned int mode) +void qht_init(struct qht *ht, qht_cmp_func_t cmp, size_t n_elems, + unsigned int mode) { struct qht_map *map; size_t n_buckets = qht_elems_to_buckets(n_elems); + g_assert(cmp); + ht->cmp = cmp; ht->mode = mode; qemu_mutex_init(&ht->lock); map = qht_map_create(n_buckets); @@ -479,8 +482,8 @@ void *qht_lookup__slowpath(struct qht_bucket *b, qht_lookup_func_t func, return ret; } -void *qht_lookup(struct qht *ht, qht_lookup_func_t func, const void *userp, - uint32_t hash) +void *qht_lookup_custom(struct qht *ht, const void *userp, uint32_t hash, + qht_lookup_func_t func) { struct qht_bucket *b; struct qht_map *map; @@ -502,6 +505,11 @@ void *qht_lookup(struct qht *ht, qht_lookup_func_t func, const void *userp, return qht_lookup__slowpath(b, func, userp, hash); } +void *qht_lookup(struct qht *ht, const void *userp, uint32_t hash) +{ + return qht_lookup_custom(ht, userp, hash, ht->cmp); +} + /* call with head->lock held */ static bool qht_insert__locked(struct qht *ht, struct qht_map *map, struct qht_bucket *head, void *p, uint32_t hash, From 32359d529f30bea8124ed671b2e6a22f22540488 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 11 Jul 2017 18:48:21 -0400 Subject: [PATCH 1307/2380] qht: return existing entry when qht_insert fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The meaning of "existing" is now changed to "matches in hash and ht->cmp result". This is saner than just checking the pointer value. Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 2 +- include/qemu/qht.h | 7 +++++-- tests/qht-bench.c | 4 ++-- tests/test-qht.c | 8 +++++++- util/qht.c | 27 +++++++++++++++++---------- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index f39123bd5a..1695f8c352 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1242,7 +1242,7 @@ static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, /* add in the hash table */ h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, tb->trace_vcpu_dstate); - qht_insert(&tb_ctx.htable, tb, h); + qht_insert(&tb_ctx.htable, tb, h, NULL); #ifdef CONFIG_USER_ONLY if (DEBUG_TB_CHECK_GATE) { diff --git a/include/qemu/qht.h b/include/qemu/qht.h index 5f03a0f4cb..1fb9116fa0 100644 --- a/include/qemu/qht.h +++ b/include/qemu/qht.h @@ -70,6 +70,7 @@ void qht_destroy(struct qht *ht); * @ht: QHT to insert to * @p: pointer to be inserted * @hash: hash corresponding to @p + * @existing: address where the pointer to an existing entry can be copied to * * Attempting to insert a NULL @p is a bug. * Inserting the same pointer @p with different @hash values is a bug. @@ -78,9 +79,11 @@ void qht_destroy(struct qht *ht); * inserted into the hash table. * * Returns true on success. - * Returns false if the @p-@hash pair already exists in the hash table. + * Returns false if there is an existing entry in the table that is equivalent + * (i.e. ht->cmp matches and the hash is the same) to @p-@h. If @existing + * is !NULL, a pointer to this existing entry is copied to it. */ -bool qht_insert(struct qht *ht, void *p, uint32_t hash); +bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing); /** * qht_lookup_custom - Look up a pointer using a custom comparison function. diff --git a/tests/qht-bench.c b/tests/qht-bench.c index c94ac25158..f492b3a20a 100644 --- a/tests/qht-bench.c +++ b/tests/qht-bench.c @@ -163,7 +163,7 @@ static void do_rw(struct thread_info *info) bool written = false; if (qht_lookup(&ht, p, hash) == NULL) { - written = qht_insert(&ht, p, hash); + written = qht_insert(&ht, p, hash, NULL); } if (written) { stats->in++; @@ -322,7 +322,7 @@ static void htable_init(void) r = xorshift64star(r); p = &keys[r & (init_range - 1)]; hash = h(*p); - if (qht_insert(&ht, p, hash)) { + if (qht_insert(&ht, p, hash, NULL)) { break; } retries++; diff --git a/tests/test-qht.c b/tests/test-qht.c index b069881342..dda6a067be 100644 --- a/tests/test-qht.c +++ b/tests/test-qht.c @@ -27,11 +27,17 @@ static void insert(int a, int b) for (i = a; i < b; i++) { uint32_t hash; + void *existing; + bool inserted; arr[i] = i; hash = i; - qht_insert(&ht, &arr[i], hash); + inserted = qht_insert(&ht, &arr[i], hash, NULL); + g_assert_true(inserted); + inserted = qht_insert(&ht, &arr[i], hash, &existing); + g_assert_false(inserted); + g_assert_true(existing == &arr[i]); } } diff --git a/util/qht.c b/util/qht.c index 487e9237bc..c138777a9c 100644 --- a/util/qht.c +++ b/util/qht.c @@ -511,9 +511,9 @@ void *qht_lookup(struct qht *ht, const void *userp, uint32_t hash) } /* call with head->lock held */ -static bool qht_insert__locked(struct qht *ht, struct qht_map *map, - struct qht_bucket *head, void *p, uint32_t hash, - bool *needs_resize) +static void *qht_insert__locked(struct qht *ht, struct qht_map *map, + struct qht_bucket *head, void *p, uint32_t hash, + bool *needs_resize) { struct qht_bucket *b = head; struct qht_bucket *prev = NULL; @@ -523,8 +523,9 @@ static bool qht_insert__locked(struct qht *ht, struct qht_map *map, do { for (i = 0; i < QHT_BUCKET_ENTRIES; i++) { if (b->pointers[i]) { - if (unlikely(b->pointers[i] == p)) { - return false; + if (unlikely(b->hashes[i] == hash && + ht->cmp(b->pointers[i], p))) { + return b->pointers[i]; } } else { goto found; @@ -553,7 +554,7 @@ static bool qht_insert__locked(struct qht *ht, struct qht_map *map, atomic_set(&b->hashes[i], hash); atomic_set(&b->pointers[i], p); seqlock_write_end(&head->sequence); - return true; + return NULL; } static __attribute__((noinline)) void qht_grow_maybe(struct qht *ht) @@ -577,25 +578,31 @@ static __attribute__((noinline)) void qht_grow_maybe(struct qht *ht) qemu_mutex_unlock(&ht->lock); } -bool qht_insert(struct qht *ht, void *p, uint32_t hash) +bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing) { struct qht_bucket *b; struct qht_map *map; bool needs_resize = false; - bool ret; + void *prev; /* NULL pointers are not supported */ qht_debug_assert(p); b = qht_bucket_lock__no_stale(ht, hash, &map); - ret = qht_insert__locked(ht, map, b, p, hash, &needs_resize); + prev = qht_insert__locked(ht, map, b, p, hash, &needs_resize); qht_bucket_debug__locked(b); qemu_spin_unlock(&b->lock); if (unlikely(needs_resize) && ht->mode & QHT_MODE_AUTO_RESIZE) { qht_grow_maybe(ht); } - return ret; + if (likely(prev == NULL)) { + return true; + } + if (existing) { + *existing = prev; + } + return false; } static inline bool qht_entry_is_last(struct qht_bucket *b, int pos) From be2cdc5e352eb28b4ff631f053a261d91e6af78e Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Wed, 26 Jul 2017 16:58:05 -0400 Subject: [PATCH 1308/2380] tcg: track TBs with per-region BST's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This paves the way for enabling scalable parallel generation of TCG code. Instead of tracking TBs with a single binary search tree (BST), use a BST for each TCG region, protecting it with a lock. This is as scalable as it gets, since each TCG thread operates on a separate region. The core of this change is the introduction of struct tcg_region_tree, which contains a pointer to a GTree and an associated lock to serialize accesses to it. We then allocate an array of tcg_region_tree's, adding the appropriate padding to avoid false sharing based on qemu_dcache_linesize. Given a tc_ptr, we first find the corresponding region_tree. This is done by special-casing the first and last regions first, since they might be of size != region.size; otherwise we just divide the offset by region.stride. I was worried about this division (several dozen cycles of latency), but profiling shows that this is not a fast path. Note that region.stride is not required to be a power of two; it is only required to be a multiple of the host's page size. Note that with this design we can also provide consistent snapshots about all region trees at once; for instance, tcg_tb_foreach acquires/releases all region_tree locks before/after iterating over them. For this reason we now drop tb_lock in dump_exec_info(). As an alternative I considered implementing a concurrent BST, but this can be tricky to get right, offers no consistent snapshots of the BST, and performance and scalability-wise I don't think it could ever beat having separate GTrees, given that our workload is insert-mostly (all concurrent BST designs I've seen focus, understandably, on making lookups fast, which comes at the expense of convoluted, non-wait-free insertions/removals). Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 2 +- accel/tcg/translate-all.c | 101 +++----------------- include/exec/exec-all.h | 1 - include/exec/tb-context.h | 1 - tcg/tcg.c | 191 ++++++++++++++++++++++++++++++++++++++ tcg/tcg.h | 6 ++ 6 files changed, 213 insertions(+), 89 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 6d6c51b686..7570c59f09 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -224,7 +224,7 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, tb_lock(); tb_phys_invalidate(tb, -1); - tb_remove(tb); + tcg_tb_remove(tb); tb_unlock(); } #endif diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 1695f8c352..ef841c82cc 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -205,8 +205,6 @@ void tb_lock_reset(void) } } -static TranslationBlock *tb_find_pc(uintptr_t tc_ptr); - void cpu_gen_init(void) { tcg_context_init(&tcg_init_ctx); @@ -375,13 +373,13 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) if (check_offset < tcg_init_ctx.code_gen_buffer_size) { tb_lock(); - tb = tb_find_pc(host_pc); + tb = tcg_tb_lookup(host_pc); if (tb) { cpu_restore_state_from_tb(cpu, tb, host_pc, will_exit); if (tb->cflags & CF_NOCACHE) { /* one-shot translation, invalidate it immediately */ tb_phys_invalidate(tb, -1); - tb_remove(tb); + tcg_tb_remove(tb); } r = true; } @@ -728,48 +726,6 @@ static inline void *alloc_code_gen_buffer(void) } #endif /* USE_STATIC_CODE_GEN_BUFFER, WIN32, POSIX */ -/* compare a pointer @ptr and a tb_tc @s */ -static int ptr_cmp_tb_tc(const void *ptr, const struct tb_tc *s) -{ - if (ptr >= s->ptr + s->size) { - return 1; - } else if (ptr < s->ptr) { - return -1; - } - return 0; -} - -static gint tb_tc_cmp(gconstpointer ap, gconstpointer bp) -{ - const struct tb_tc *a = ap; - const struct tb_tc *b = bp; - - /* - * When both sizes are set, we know this isn't a lookup. - * This is the most likely case: every TB must be inserted; lookups - * are a lot less frequent. - */ - if (likely(a->size && b->size)) { - if (a->ptr > b->ptr) { - return 1; - } else if (a->ptr < b->ptr) { - return -1; - } - /* a->ptr == b->ptr should happen only on deletions */ - g_assert(a->size == b->size); - return 0; - } - /* - * All lookups have either .size field set to 0. - * From the glib sources we see that @ap is always the lookup key. However - * the docs provide no guarantee, so we just mark this case as likely. - */ - if (likely(a->size == 0)) { - return ptr_cmp_tb_tc(a->ptr, b); - } - return ptr_cmp_tb_tc(b->ptr, a); -} - static inline void code_gen_alloc(size_t tb_size) { tcg_ctx->code_gen_buffer_size = size_code_gen_buffer(tb_size); @@ -778,7 +734,6 @@ static inline void code_gen_alloc(size_t tb_size) fprintf(stderr, "Could not allocate dynamic translator buffer\n"); exit(1); } - tb_ctx.tb_tree = g_tree_new(tb_tc_cmp); qemu_mutex_init(&tb_ctx.tb_lock); } @@ -839,14 +794,6 @@ static TranslationBlock *tb_alloc(target_ulong pc) return tb; } -/* Called with tb_lock held. */ -void tb_remove(TranslationBlock *tb) -{ - assert_tb_locked(); - - g_tree_remove(tb_ctx.tb_tree, &tb->tc); -} - static inline void invalidate_page_bitmap(PageDesc *p) { #ifdef CONFIG_SOFTMMU @@ -911,10 +858,10 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) } if (DEBUG_TB_FLUSH_GATE) { - size_t nb_tbs = g_tree_nnodes(tb_ctx.tb_tree); + size_t nb_tbs = tcg_nb_tbs(); size_t host_size = 0; - g_tree_foreach(tb_ctx.tb_tree, tb_host_size_iter, &host_size); + tcg_tb_foreach(tb_host_size_iter, &host_size); printf("qemu: flush code_size=%zu nb_tbs=%zu avg_tb_size=%zu\n", tcg_code_size(), nb_tbs, nb_tbs > 0 ? host_size / nb_tbs : 0); } @@ -923,10 +870,6 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) cpu_tb_jmp_cache_clear(cpu); } - /* Increment the refcount first so that destroy acts as a reset */ - g_tree_ref(tb_ctx.tb_tree); - g_tree_destroy(tb_ctx.tb_tree); - qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE); page_flush_tb(); @@ -1406,7 +1349,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, * through the physical hash table and physical page list. */ tb_link_page(tb, phys_pc, phys_page2); - g_tree_insert(tb_ctx.tb_tree, &tb->tc, tb); + tcg_tb_insert(tb); return tb; } @@ -1510,7 +1453,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, current_tb = NULL; if (cpu->mem_io_pc) { /* now we have a real cpu fault */ - current_tb = tb_find_pc(cpu->mem_io_pc); + current_tb = tcg_tb_lookup(cpu->mem_io_pc); } } if (current_tb == tb && @@ -1627,7 +1570,7 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) tb = p->first_tb; #ifdef TARGET_HAS_PRECISE_SMC if (tb && pc != 0) { - current_tb = tb_find_pc(pc); + current_tb = tcg_tb_lookup(pc); } if (cpu != NULL) { env = cpu->env_ptr; @@ -1670,18 +1613,6 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) } #endif -/* - * Find the TB 'tb' such that - * tb->tc.ptr <= tc_ptr < tb->tc.ptr + tb->tc.size - * Return NULL if not found. - */ -static TranslationBlock *tb_find_pc(uintptr_t tc_ptr) -{ - struct tb_tc s = { .ptr = (void *)tc_ptr }; - - return g_tree_lookup(tb_ctx.tb_tree, &s); -} - #if !defined(CONFIG_USER_ONLY) void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) { @@ -1709,7 +1640,7 @@ void tb_check_watchpoint(CPUState *cpu) { TranslationBlock *tb; - tb = tb_find_pc(cpu->mem_io_pc); + tb = tcg_tb_lookup(cpu->mem_io_pc); if (tb) { /* We can use retranslation to find the PC. */ cpu_restore_state_from_tb(cpu, tb, cpu->mem_io_pc, true); @@ -1743,7 +1674,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) uint32_t n; tb_lock(); - tb = tb_find_pc(retaddr); + tb = tcg_tb_lookup(retaddr); if (!tb) { cpu_abort(cpu, "cpu_io_recompile: could not find TB for pc=%p", (void *)retaddr); @@ -1782,7 +1713,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) * cpu_exec_nocache() */ tb_phys_invalidate(tb->orig_tb, -1); } - tb_remove(tb); + tcg_tb_remove(tb); } /* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not @@ -1853,6 +1784,7 @@ static void print_qht_statistics(FILE *f, fprintf_function cpu_fprintf, } struct tb_tree_stats { + size_t nb_tbs; size_t host_size; size_t target_size; size_t max_target_size; @@ -1866,6 +1798,7 @@ static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data) const TranslationBlock *tb = value; struct tb_tree_stats *tst = data; + tst->nb_tbs++; tst->host_size += tb->tc.size; tst->target_size += tb->size; if (tb->size > tst->max_target_size) { @@ -1889,10 +1822,8 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) struct qht_stats hst; size_t nb_tbs; - tb_lock(); - - nb_tbs = g_tree_nnodes(tb_ctx.tb_tree); - g_tree_foreach(tb_ctx.tb_tree, tb_tree_stats_iter, &tst); + tcg_tb_foreach(tb_tree_stats_iter, &tst); + nb_tbs = tst.nb_tbs; /* XXX: avoid using doubles ? */ cpu_fprintf(f, "Translation buffer state:\n"); /* @@ -1927,8 +1858,6 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) cpu_fprintf(f, "TB invalidate count %d\n", tb_ctx.tb_phys_invalidate_count); cpu_fprintf(f, "TLB flush count %zu\n", tlb_flush_count()); tcg_dump_info(f, cpu_fprintf); - - tb_unlock(); } void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf) @@ -2196,7 +2125,7 @@ int page_unprotect(target_ulong address, uintptr_t pc) * set the page to PAGE_WRITE and did the TB invalidate for us. */ #ifdef TARGET_HAS_PRECISE_SMC - TranslationBlock *current_tb = tb_find_pc(pc); + TranslationBlock *current_tb = tcg_tb_lookup(pc); if (current_tb) { current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID; } diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 8bbea787a9..8d4306ac25 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -405,7 +405,6 @@ static inline uint32_t curr_cflags(void) | (use_icount ? CF_USE_ICOUNT : 0); } -void tb_remove(TranslationBlock *tb); void tb_flush(CPUState *cpu); void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, diff --git a/include/exec/tb-context.h b/include/exec/tb-context.h index 1d41202485..d8472c88fb 100644 --- a/include/exec/tb-context.h +++ b/include/exec/tb-context.h @@ -31,7 +31,6 @@ typedef struct TBContext TBContext; struct TBContext { - GTree *tb_tree; struct qht htable; /* any access to the tbs or the page table must use this lock */ QemuMutex tb_lock; diff --git a/tcg/tcg.c b/tcg/tcg.c index 6eeebe0624..62e3391020 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -135,6 +135,12 @@ static TCGContext **tcg_ctxs; static unsigned int n_tcg_ctxs; TCGv_env cpu_env = 0; +struct tcg_region_tree { + QemuMutex lock; + GTree *tree; + /* padding to avoid false sharing is computed at run-time */ +}; + /* * We divide code_gen_buffer into equally-sized "regions" that TCG threads * dynamically allocate from as demand dictates. Given appropriate region @@ -158,6 +164,13 @@ struct tcg_region_state { }; static struct tcg_region_state region; +/* + * This is an array of struct tcg_region_tree's, with padding. + * We use void * to simplify the computation of region_trees[i]; each + * struct is found every tree_size bytes. + */ +static void *region_trees; +static size_t tree_size; static TCGRegSet tcg_target_available_regs[TCG_TYPE_COUNT]; static TCGRegSet tcg_target_call_clobber_regs; @@ -295,6 +308,180 @@ TCGLabel *gen_new_label(void) #include "tcg-target.inc.c" +/* compare a pointer @ptr and a tb_tc @s */ +static int ptr_cmp_tb_tc(const void *ptr, const struct tb_tc *s) +{ + if (ptr >= s->ptr + s->size) { + return 1; + } else if (ptr < s->ptr) { + return -1; + } + return 0; +} + +static gint tb_tc_cmp(gconstpointer ap, gconstpointer bp) +{ + const struct tb_tc *a = ap; + const struct tb_tc *b = bp; + + /* + * When both sizes are set, we know this isn't a lookup. + * This is the most likely case: every TB must be inserted; lookups + * are a lot less frequent. + */ + if (likely(a->size && b->size)) { + if (a->ptr > b->ptr) { + return 1; + } else if (a->ptr < b->ptr) { + return -1; + } + /* a->ptr == b->ptr should happen only on deletions */ + g_assert(a->size == b->size); + return 0; + } + /* + * All lookups have either .size field set to 0. + * From the glib sources we see that @ap is always the lookup key. However + * the docs provide no guarantee, so we just mark this case as likely. + */ + if (likely(a->size == 0)) { + return ptr_cmp_tb_tc(a->ptr, b); + } + return ptr_cmp_tb_tc(b->ptr, a); +} + +static void tcg_region_trees_init(void) +{ + size_t i; + + tree_size = ROUND_UP(sizeof(struct tcg_region_tree), qemu_dcache_linesize); + region_trees = qemu_memalign(qemu_dcache_linesize, region.n * tree_size); + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + qemu_mutex_init(&rt->lock); + rt->tree = g_tree_new(tb_tc_cmp); + } +} + +static struct tcg_region_tree *tc_ptr_to_region_tree(void *p) +{ + size_t region_idx; + + if (p < region.start_aligned) { + region_idx = 0; + } else { + ptrdiff_t offset = p - region.start_aligned; + + if (offset > region.stride * (region.n - 1)) { + region_idx = region.n - 1; + } else { + region_idx = offset / region.stride; + } + } + return region_trees + region_idx * tree_size; +} + +void tcg_tb_insert(TranslationBlock *tb) +{ + struct tcg_region_tree *rt = tc_ptr_to_region_tree(tb->tc.ptr); + + qemu_mutex_lock(&rt->lock); + g_tree_insert(rt->tree, &tb->tc, tb); + qemu_mutex_unlock(&rt->lock); +} + +void tcg_tb_remove(TranslationBlock *tb) +{ + struct tcg_region_tree *rt = tc_ptr_to_region_tree(tb->tc.ptr); + + qemu_mutex_lock(&rt->lock); + g_tree_remove(rt->tree, &tb->tc); + qemu_mutex_unlock(&rt->lock); +} + +/* + * Find the TB 'tb' such that + * tb->tc.ptr <= tc_ptr < tb->tc.ptr + tb->tc.size + * Return NULL if not found. + */ +TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr) +{ + struct tcg_region_tree *rt = tc_ptr_to_region_tree((void *)tc_ptr); + TranslationBlock *tb; + struct tb_tc s = { .ptr = (void *)tc_ptr }; + + qemu_mutex_lock(&rt->lock); + tb = g_tree_lookup(rt->tree, &s); + qemu_mutex_unlock(&rt->lock); + return tb; +} + +static void tcg_region_tree_lock_all(void) +{ + size_t i; + + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + qemu_mutex_lock(&rt->lock); + } +} + +static void tcg_region_tree_unlock_all(void) +{ + size_t i; + + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + qemu_mutex_unlock(&rt->lock); + } +} + +void tcg_tb_foreach(GTraverseFunc func, gpointer user_data) +{ + size_t i; + + tcg_region_tree_lock_all(); + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + g_tree_foreach(rt->tree, func, user_data); + } + tcg_region_tree_unlock_all(); +} + +size_t tcg_nb_tbs(void) +{ + size_t nb_tbs = 0; + size_t i; + + tcg_region_tree_lock_all(); + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + nb_tbs += g_tree_nnodes(rt->tree); + } + tcg_region_tree_unlock_all(); + return nb_tbs; +} + +static void tcg_region_tree_reset_all(void) +{ + size_t i; + + tcg_region_tree_lock_all(); + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + /* Increment the refcount first so that destroy acts as a reset */ + g_tree_ref(rt->tree); + g_tree_destroy(rt->tree); + } + tcg_region_tree_unlock_all(); +} + static void tcg_region_bounds(size_t curr_region, void **pstart, void **pend) { void *start, *end; @@ -380,6 +567,8 @@ void tcg_region_reset_all(void) g_assert(!err); } qemu_mutex_unlock(®ion.lock); + + tcg_region_tree_reset_all(); } #ifdef CONFIG_USER_ONLY @@ -496,6 +685,8 @@ void tcg_region_init(void) g_assert(!rc); } + tcg_region_trees_init(); + /* In user-mode we support only one ctx, so do the initial allocation now */ #ifdef CONFIG_USER_ONLY { diff --git a/tcg/tcg.h b/tcg/tcg.h index 509f4d65d2..1e6df1906f 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -866,6 +866,12 @@ void tcg_region_reset_all(void); size_t tcg_code_size(void); size_t tcg_code_capacity(void); +void tcg_tb_insert(TranslationBlock *tb); +void tcg_tb_remove(TranslationBlock *tb); +TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr); +void tcg_tb_foreach(GTraverseFunc func, gpointer user_data); +size_t tcg_nb_tbs(void); + /* user-mode: Called with tb_lock held. */ static inline void *tcg_malloc(int size) { From 128ed2278c4e6ad063f101c5dda7999b43f2d8a3 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 1 Aug 2017 15:11:12 -0400 Subject: [PATCH 1309/2380] tcg: move tb_ctx.tb_phys_invalidate_count to tcg_ctx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thereby making it per-TCGContext. Once we remove tb_lock, this will avoid an atomic increment every time a TB is invalidated. Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 5 +++-- include/exec/tb-context.h | 1 - tcg/tcg.c | 14 ++++++++++++++ tcg/tcg.h | 3 +++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index ef841c82cc..a9f2bfb468 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1069,7 +1069,8 @@ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) /* suppress any remaining jumps to this TB */ tb_jmp_unlink(tb); - tb_ctx.tb_phys_invalidate_count++; + atomic_set(&tcg_ctx->tb_phys_invalidate_count, + tcg_ctx->tb_phys_invalidate_count + 1); } #ifdef CONFIG_SOFTMMU @@ -1855,7 +1856,7 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) cpu_fprintf(f, "\nStatistics:\n"); cpu_fprintf(f, "TB flush count %u\n", atomic_read(&tb_ctx.tb_flush_count)); - cpu_fprintf(f, "TB invalidate count %d\n", tb_ctx.tb_phys_invalidate_count); + cpu_fprintf(f, "TB invalidate count %zu\n", tcg_tb_phys_invalidate_count()); cpu_fprintf(f, "TLB flush count %zu\n", tlb_flush_count()); tcg_dump_info(f, cpu_fprintf); } diff --git a/include/exec/tb-context.h b/include/exec/tb-context.h index d8472c88fb..8c9b49c98e 100644 --- a/include/exec/tb-context.h +++ b/include/exec/tb-context.h @@ -37,7 +37,6 @@ struct TBContext { /* statistics */ unsigned tb_flush_count; - int tb_phys_invalidate_count; }; extern TBContext tb_ctx; diff --git a/tcg/tcg.c b/tcg/tcg.c index 62e3391020..1d1dfd7f7c 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -791,6 +791,20 @@ size_t tcg_code_capacity(void) return capacity; } +size_t tcg_tb_phys_invalidate_count(void) +{ + unsigned int n_ctxs = atomic_read(&n_tcg_ctxs); + unsigned int i; + size_t total = 0; + + for (i = 0; i < n_ctxs; i++) { + const TCGContext *s = atomic_read(&tcg_ctxs[i]); + + total += atomic_read(&s->tb_phys_invalidate_count); + } + return total; +} + /* pool based memory allocation */ void *tcg_malloc_internal(TCGContext *s, int size) { diff --git a/tcg/tcg.h b/tcg/tcg.h index 1e6df1906f..e49b289ba1 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -695,6 +695,8 @@ struct TCGContext { /* Threshold to flush the translated code buffer. */ void *code_gen_highwater; + size_t tb_phys_invalidate_count; + /* Track which vCPU triggers events */ CPUState *cpu; /* *_trans */ @@ -868,6 +870,7 @@ size_t tcg_code_capacity(void); void tcg_tb_insert(TranslationBlock *tb); void tcg_tb_remove(TranslationBlock *tb); +size_t tcg_tb_phys_invalidate_count(void); TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr); void tcg_tb_foreach(GTraverseFunc func, gpointer user_data); size_t tcg_nb_tbs(void); From 1e05197f24c49d52f339de9053bb1d17082f1be3 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Thu, 3 Aug 2017 18:37:15 -0400 Subject: [PATCH 1310/2380] translate-all: iterate over TBs in a page with PAGE_FOR_EACH_TB This commit does several things, but to avoid churn I merged them all into the same commit. To wit: - Use uintptr_t instead of TranslationBlock * for the list of TBs in a page. Just like we did in (c37e6d7e "tcg: Use uintptr_t type for jmp_list_{next|first} fields of TB"), the rationale is the same: these are tagged pointers, not pointers. So use a more appropriate type. - Only check the least significant bit of the tagged pointers. Masking with 3/~3 is unnecessary and confusing. - Introduce the TB_FOR_EACH_TAGGED macro, and use it to define PAGE_FOR_EACH_TB, which improves readability. Note that TB_FOR_EACH_TAGGED will gain another user in a subsequent patch. - Update tb_page_remove to use PAGE_FOR_EACH_TB. In case there is a bug and we attempt to remove a TB that is not in the list, instead of segfaulting (since the list is NULL-terminated) we will reach g_assert_not_reached(). Reviewed-by: Richard Henderson Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 62 ++++++++++++++++++--------------------- include/exec/exec-all.h | 2 +- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index a9f2bfb468..52e62125ed 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -103,7 +103,7 @@ typedef struct PageDesc { /* list of TBs intersecting this ram page */ - TranslationBlock *first_tb; + uintptr_t first_tb; #ifdef CONFIG_SOFTMMU /* in order to optimize self modifying code, we count the number of lookups we do to a given page to use a bitmap */ @@ -114,6 +114,15 @@ typedef struct PageDesc { #endif } PageDesc; +/* list iterators for lists of tagged pointers in TranslationBlock */ +#define TB_FOR_EACH_TAGGED(head, tb, n, field) \ + for (n = (head) & 1, tb = (TranslationBlock *)((head) & ~1); \ + tb; tb = (TranslationBlock *)tb->field[n], n = (uintptr_t)tb & 1, \ + tb = (TranslationBlock *)((uintptr_t)tb & ~1)) + +#define PAGE_FOR_EACH_TB(pagedesc, tb, n) \ + TB_FOR_EACH_TAGGED((pagedesc)->first_tb, tb, n, page_next) + /* In system mode we want L1_MAP to be based on ram offsets, while in user mode we want it to be based on virtual addresses. */ #if !defined(CONFIG_USER_ONLY) @@ -815,7 +824,7 @@ static void page_flush_tb_1(int level, void **lp) PageDesc *pd = *lp; for (i = 0; i < V_L2_SIZE; ++i) { - pd[i].first_tb = NULL; + pd[i].first_tb = (uintptr_t)NULL; invalidate_page_bitmap(pd + i); } } else { @@ -943,21 +952,21 @@ static void tb_page_check(void) #endif /* CONFIG_USER_ONLY */ -static inline void tb_page_remove(TranslationBlock **ptb, TranslationBlock *tb) +static inline void tb_page_remove(PageDesc *pd, TranslationBlock *tb) { TranslationBlock *tb1; + uintptr_t *pprev; unsigned int n1; - for (;;) { - tb1 = *ptb; - n1 = (uintptr_t)tb1 & 3; - tb1 = (TranslationBlock *)((uintptr_t)tb1 & ~3); + pprev = &pd->first_tb; + PAGE_FOR_EACH_TB(pd, tb1, n1) { if (tb1 == tb) { - *ptb = tb1->page_next[n1]; - break; + *pprev = tb1->page_next[n1]; + return; } - ptb = &tb1->page_next[n1]; + pprev = &tb1->page_next[n1]; } + g_assert_not_reached(); } /* remove the TB from a list of TBs jumping to the n-th jump target of the TB */ @@ -1045,12 +1054,12 @@ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) /* remove the TB from the page list */ if (tb->page_addr[0] != page_addr) { p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); - tb_page_remove(&p->first_tb, tb); + tb_page_remove(p, tb); invalidate_page_bitmap(p); } if (tb->page_addr[1] != -1 && tb->page_addr[1] != page_addr) { p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); - tb_page_remove(&p->first_tb, tb); + tb_page_remove(p, tb); invalidate_page_bitmap(p); } @@ -1081,10 +1090,7 @@ static void build_page_bitmap(PageDesc *p) p->code_bitmap = bitmap_new(TARGET_PAGE_SIZE); - tb = p->first_tb; - while (tb != NULL) { - n = (uintptr_t)tb & 3; - tb = (TranslationBlock *)((uintptr_t)tb & ~3); + PAGE_FOR_EACH_TB(p, tb, n) { /* NOTE: this is subtle as a TB may span two physical pages */ if (n == 0) { /* NOTE: tb_end may be after the end of the page, but @@ -1099,7 +1105,6 @@ static void build_page_bitmap(PageDesc *p) tb_end = ((tb->pc + tb->size) & ~TARGET_PAGE_MASK); } bitmap_set(p->code_bitmap, tb_start, tb_end - tb_start); - tb = tb->page_next[n]; } } #endif @@ -1122,9 +1127,9 @@ static inline void tb_alloc_page(TranslationBlock *tb, p = page_find_alloc(page_addr >> TARGET_PAGE_BITS, 1); tb->page_next[n] = p->first_tb; #ifndef CONFIG_USER_ONLY - page_already_protected = p->first_tb != NULL; + page_already_protected = p->first_tb != (uintptr_t)NULL; #endif - p->first_tb = (TranslationBlock *)((uintptr_t)tb | n); + p->first_tb = (uintptr_t)tb | n; invalidate_page_bitmap(p); #if defined(CONFIG_USER_ONLY) @@ -1401,7 +1406,7 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, int is_cpu_write_access) { - TranslationBlock *tb, *tb_next; + TranslationBlock *tb; tb_page_addr_t tb_start, tb_end; PageDesc *p; int n; @@ -1432,11 +1437,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, /* we remove all the TBs in the range [start, end[ */ /* XXX: see if in some cases it could be faster to invalidate all the code */ - tb = p->first_tb; - while (tb != NULL) { - n = (uintptr_t)tb & 3; - tb = (TranslationBlock *)((uintptr_t)tb & ~3); - tb_next = tb->page_next[n]; + PAGE_FOR_EACH_TB(p, tb, n) { /* NOTE: this is subtle as a TB may span two physical pages */ if (n == 0) { /* NOTE: tb_end may be after the end of the page, but @@ -1474,7 +1475,6 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, #endif /* TARGET_HAS_PRECISE_SMC */ tb_phys_invalidate(tb, -1); } - tb = tb_next; } #if !defined(CONFIG_USER_ONLY) /* if no code remaining, no need to continue to use slow writes */ @@ -1568,18 +1568,15 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) } tb_lock(); - tb = p->first_tb; #ifdef TARGET_HAS_PRECISE_SMC - if (tb && pc != 0) { + if (p->first_tb && pc != 0) { current_tb = tcg_tb_lookup(pc); } if (cpu != NULL) { env = cpu->env_ptr; } #endif - while (tb != NULL) { - n = (uintptr_t)tb & 3; - tb = (TranslationBlock *)((uintptr_t)tb & ~3); + PAGE_FOR_EACH_TB(p, tb, n) { #ifdef TARGET_HAS_PRECISE_SMC if (current_tb == tb && (current_tb->cflags & CF_COUNT_MASK) != 1) { @@ -1596,9 +1593,8 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) } #endif /* TARGET_HAS_PRECISE_SMC */ tb_phys_invalidate(tb, addr); - tb = tb->page_next[n]; } - p->first_tb = NULL; + p->first_tb = (uintptr_t)NULL; #ifdef TARGET_HAS_PRECISE_SMC if (current_tb_modified) { /* Force execution of one insn next time. */ diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 8d4306ac25..07653d3c92 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -360,7 +360,7 @@ struct TranslationBlock { struct TranslationBlock *orig_tb; /* first and second physical page containing code. The lower bit of the pointer tells the index in page_next[] */ - struct TranslationBlock *page_next[2]; + uintptr_t page_next[2]; tb_page_addr_t page_addr[2]; /* The following data are used to directly call another TB from From 78722ed0b826644ae240e3c0bbb6bdde02dfe7e1 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Wed, 26 Jul 2017 20:15:41 -0400 Subject: [PATCH 1311/2380] translate-all: make l1_map lockless MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Groundwork for supporting parallel TCG generation. We never remove entries from the radix tree, so we can use cmpxchg to implement lockless insertions. Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 24 ++++++++++++++---------- docs/devel/multi-thread-tcg.txt | 4 ++-- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 52e62125ed..52273e5846 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -469,20 +469,12 @@ static void page_init(void) #endif } -/* If alloc=1: - * Called with tb_lock held for system emulation. - * Called with mmap_lock held for user-mode emulation. - */ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) { PageDesc *pd; void **lp; int i; - if (alloc) { - assert_memory_lock(); - } - /* Level 1. Always allocated. */ lp = l1_map + ((index >> v_l1_shift) & (v_l1_size - 1)); @@ -491,11 +483,17 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) void **p = atomic_rcu_read(lp); if (p == NULL) { + void *existing; + if (!alloc) { return NULL; } p = g_new0(void *, V_L2_SIZE); - atomic_rcu_set(lp, p); + existing = atomic_cmpxchg(lp, NULL, p); + if (unlikely(existing)) { + g_free(p); + p = existing; + } } lp = p + ((index >> (i * V_L2_BITS)) & (V_L2_SIZE - 1)); @@ -503,11 +501,17 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) pd = atomic_rcu_read(lp); if (pd == NULL) { + void *existing; + if (!alloc) { return NULL; } pd = g_new0(PageDesc, V_L2_SIZE); - atomic_rcu_set(lp, pd); + existing = atomic_cmpxchg(lp, NULL, pd); + if (unlikely(existing)) { + g_free(pd); + pd = existing; + } } return pd + (index & (V_L2_SIZE - 1)); diff --git a/docs/devel/multi-thread-tcg.txt b/docs/devel/multi-thread-tcg.txt index a99b4564c6..faf8918b23 100644 --- a/docs/devel/multi-thread-tcg.txt +++ b/docs/devel/multi-thread-tcg.txt @@ -134,8 +134,8 @@ tb_set_jmp_target() code. Modification to the linked lists that allow searching for linked pages are done under the protect of the tb_lock(). -The global page table is protected by the tb_lock() in system-mode and -mmap_lock() in linux-user mode. +The global page table is a lockless radix tree; cmpxchg is used +to atomically insert new elements. The lookup caches are updated atomically and the lookup hash uses QHT which is designed for concurrent safe lookup. From 94da9aec2a50f0c82e6c60939275c0337f03d5fe Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Sat, 29 Jul 2017 01:19:17 -0400 Subject: [PATCH 1312/2380] translate-all: remove hole in PageDesc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Groundwork for supporting parallel TCG generation. Move the hole to the end of the struct, so that a u32 field can be added there without bloating the struct. Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 52273e5846..b9c36a3e45 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -107,8 +107,8 @@ typedef struct PageDesc { #ifdef CONFIG_SOFTMMU /* in order to optimize self modifying code, we count the number of lookups we do to a given page to use a bitmap */ - unsigned int code_write_count; unsigned long *code_bitmap; + unsigned int code_write_count; #else unsigned long flags; #endif From ae5486e273a4e368515a963a6d0076e20453eb72 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Sat, 5 Aug 2017 02:04:40 -0400 Subject: [PATCH 1313/2380] translate-all: work page-by-page in tb_invalidate_phys_range_1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that we pass a same-page range to tb_invalidate_phys_page_range, instead of always passing an end address that could be on a different page. As discussed with Peter Maydell on the list [1], tb_invalidate_phys_page_range doesn't actually do much with 'end', which explains why we have never hit a bug despite going against what the comment on top of tb_invalidate_phys_page_range requires: > * Invalidate all TBs which intersect with the target physical address range > * [start;end[. NOTE: start and end must refer to the *same* physical page. The appended honours the comment, which avoids confusion. While at it, rework the loop into a for loop, which is less error prone (e.g. "continue" won't result in an infinite loop). [1] https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg09165.html Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index b9c36a3e45..f32904d4a3 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1375,10 +1375,14 @@ TranslationBlock *tb_gen_code(CPUState *cpu, */ static void tb_invalidate_phys_range_1(tb_page_addr_t start, tb_page_addr_t end) { - while (start < end) { - tb_invalidate_phys_page_range(start, end, 0); - start &= TARGET_PAGE_MASK; - start += TARGET_PAGE_SIZE; + tb_page_addr_t next; + + for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + start < end; + start = next, next += TARGET_PAGE_SIZE) { + tb_page_addr_t bound = MIN(next, end); + + tb_invalidate_phys_page_range(start, bound, 0); } } From 45c73de594414904b0d6a7ade70fb4514d35f79c Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Sat, 5 Aug 2017 02:20:19 -0400 Subject: [PATCH 1314/2380] translate-all: move tb_invalidate_phys_page_range up in the file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This greatly simplifies next commit's diff. Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 77 ++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index f32904d4a3..83bb40fb20 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1363,44 +1363,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu, return tb; } -/* - * Invalidate all TBs which intersect with the target physical address range - * [start;end[. NOTE: start and end may refer to *different* physical pages. - * 'is_cpu_write_access' should be true if called from a real cpu write - * access: the virtual CPU will exit the current TB if code is modified inside - * this TB. - * - * Called with mmap_lock held for user-mode emulation, grabs tb_lock - * Called with tb_lock held for system-mode emulation - */ -static void tb_invalidate_phys_range_1(tb_page_addr_t start, tb_page_addr_t end) -{ - tb_page_addr_t next; - - for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - start < end; - start = next, next += TARGET_PAGE_SIZE) { - tb_page_addr_t bound = MIN(next, end); - - tb_invalidate_phys_page_range(start, bound, 0); - } -} - -#ifdef CONFIG_SOFTMMU -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) -{ - assert_tb_locked(); - tb_invalidate_phys_range_1(start, end); -} -#else -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) -{ - assert_memory_lock(); - tb_lock(); - tb_invalidate_phys_range_1(start, end); - tb_unlock(); -} -#endif /* * Invalidate all TBs which intersect with the target physical address range * [start;end[. NOTE: start and end must refer to the *same* physical page. @@ -1500,6 +1462,45 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, #endif } +/* + * Invalidate all TBs which intersect with the target physical address range + * [start;end[. NOTE: start and end may refer to *different* physical pages. + * 'is_cpu_write_access' should be true if called from a real cpu write + * access: the virtual CPU will exit the current TB if code is modified inside + * this TB. + * + * Called with mmap_lock held for user-mode emulation, grabs tb_lock + * Called with tb_lock held for system-mode emulation + */ +static void tb_invalidate_phys_range_1(tb_page_addr_t start, tb_page_addr_t end) +{ + tb_page_addr_t next; + + for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + start < end; + start = next, next += TARGET_PAGE_SIZE) { + tb_page_addr_t bound = MIN(next, end); + + tb_invalidate_phys_page_range(start, bound, 0); + } +} + +#ifdef CONFIG_SOFTMMU +void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) +{ + assert_tb_locked(); + tb_invalidate_phys_range_1(start, end); +} +#else +void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) +{ + assert_memory_lock(); + tb_lock(); + tb_invalidate_phys_range_1(start, end); + tb_unlock(); +} +#endif + #ifdef CONFIG_SOFTMMU /* len must be <= 8 and start must be a multiple of len. * Called via softmmu_template.h when code areas are written to with From 0b5c91f74f3c83a36f37740969df8c775c997e69 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Wed, 26 Jul 2017 20:22:51 -0400 Subject: [PATCH 1315/2380] translate-all: use per-page locking in !user-mode Groundwork for supporting parallel TCG generation. Instead of using a global lock (tb_lock) to protect changes to pages, use fine-grained, per-page locks in !user-mode. User-mode stays with mmap_lock. Sometimes changes need to happen atomically on more than one page (e.g. when a TB that spans across two pages is added/invalidated, or when a range of pages is invalidated). We therefore introduce struct page_collection, which helps us keep track of a set of pages that have been locked in the appropriate locking order (i.e. by ascending page index). This commit first introduces the structs and the function helpers, to then convert the calling code to use per-page locking. Note that tb_lock is not removed yet. While at it, rename tb_alloc_page to tb_page_add, which pairs with tb_page_remove. Reviewed-by: Richard Henderson Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 444 ++++++++++++++++++++++++++++++++++---- accel/tcg/translate-all.h | 3 + include/exec/exec-all.h | 3 +- 3 files changed, 409 insertions(+), 41 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 83bb40fb20..1cc7aab82c 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -112,8 +112,55 @@ typedef struct PageDesc { #else unsigned long flags; #endif +#ifndef CONFIG_USER_ONLY + QemuSpin lock; +#endif } PageDesc; +/** + * struct page_entry - page descriptor entry + * @pd: pointer to the &struct PageDesc of the page this entry represents + * @index: page index of the page + * @locked: whether the page is locked + * + * This struct helps us keep track of the locked state of a page, without + * bloating &struct PageDesc. + * + * A page lock protects accesses to all fields of &struct PageDesc. + * + * See also: &struct page_collection. + */ +struct page_entry { + PageDesc *pd; + tb_page_addr_t index; + bool locked; +}; + +/** + * struct page_collection - tracks a set of pages (i.e. &struct page_entry's) + * @tree: Binary search tree (BST) of the pages, with key == page index + * @max: Pointer to the page in @tree with the highest page index + * + * To avoid deadlock we lock pages in ascending order of page index. + * When operating on a set of pages, we need to keep track of them so that + * we can lock them in order and also unlock them later. For this we collect + * pages (i.e. &struct page_entry's) in a binary search @tree. Given that the + * @tree implementation we use does not provide an O(1) operation to obtain the + * highest-ranked element, we use @max to keep track of the inserted page + * with the highest index. This is valuable because if a page is not in + * the tree and its index is higher than @max's, then we can lock it + * without breaking the locking order rule. + * + * Note on naming: 'struct page_set' would be shorter, but we already have a few + * page_set_*() helpers, so page_collection is used instead to avoid confusion. + * + * See also: page_collection_lock(). + */ +struct page_collection { + GTree *tree; + struct page_entry *max; +}; + /* list iterators for lists of tagged pointers in TranslationBlock */ #define TB_FOR_EACH_TAGGED(head, tb, n, field) \ for (n = (head) & 1, tb = (TranslationBlock *)((head) & ~1); \ @@ -507,6 +554,15 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) return NULL; } pd = g_new0(PageDesc, V_L2_SIZE); +#ifndef CONFIG_USER_ONLY + { + int i; + + for (i = 0; i < V_L2_SIZE; i++) { + qemu_spin_init(&pd[i].lock); + } + } +#endif existing = atomic_cmpxchg(lp, NULL, pd); if (unlikely(existing)) { g_free(pd); @@ -522,6 +578,253 @@ static inline PageDesc *page_find(tb_page_addr_t index) return page_find_alloc(index, 0); } +static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, + PageDesc **ret_p2, tb_page_addr_t phys2, int alloc); + +/* In user-mode page locks aren't used; mmap_lock is enough */ +#ifdef CONFIG_USER_ONLY +static inline void page_lock(PageDesc *pd) +{ } + +static inline void page_unlock(PageDesc *pd) +{ } + +static inline void page_lock_tb(const TranslationBlock *tb) +{ } + +static inline void page_unlock_tb(const TranslationBlock *tb) +{ } + +struct page_collection * +page_collection_lock(tb_page_addr_t start, tb_page_addr_t end) +{ + return NULL; +} + +void page_collection_unlock(struct page_collection *set) +{ } +#else /* !CONFIG_USER_ONLY */ + +static inline void page_lock(PageDesc *pd) +{ + qemu_spin_lock(&pd->lock); +} + +static inline void page_unlock(PageDesc *pd) +{ + qemu_spin_unlock(&pd->lock); +} + +/* lock the page(s) of a TB in the correct acquisition order */ +static inline void page_lock_tb(const TranslationBlock *tb) +{ + page_lock_pair(NULL, tb->page_addr[0], NULL, tb->page_addr[1], 0); +} + +static inline void page_unlock_tb(const TranslationBlock *tb) +{ + page_unlock(page_find(tb->page_addr[0] >> TARGET_PAGE_BITS)); + if (unlikely(tb->page_addr[1] != -1)) { + page_unlock(page_find(tb->page_addr[1] >> TARGET_PAGE_BITS)); + } +} + +static inline struct page_entry * +page_entry_new(PageDesc *pd, tb_page_addr_t index) +{ + struct page_entry *pe = g_malloc(sizeof(*pe)); + + pe->index = index; + pe->pd = pd; + pe->locked = false; + return pe; +} + +static void page_entry_destroy(gpointer p) +{ + struct page_entry *pe = p; + + g_assert(pe->locked); + page_unlock(pe->pd); + g_free(pe); +} + +/* returns false on success */ +static bool page_entry_trylock(struct page_entry *pe) +{ + bool busy; + + busy = qemu_spin_trylock(&pe->pd->lock); + if (!busy) { + g_assert(!pe->locked); + pe->locked = true; + } + return busy; +} + +static void do_page_entry_lock(struct page_entry *pe) +{ + page_lock(pe->pd); + g_assert(!pe->locked); + pe->locked = true; +} + +static gboolean page_entry_lock(gpointer key, gpointer value, gpointer data) +{ + struct page_entry *pe = value; + + do_page_entry_lock(pe); + return FALSE; +} + +static gboolean page_entry_unlock(gpointer key, gpointer value, gpointer data) +{ + struct page_entry *pe = value; + + if (pe->locked) { + pe->locked = false; + page_unlock(pe->pd); + } + return FALSE; +} + +/* + * Trylock a page, and if successful, add the page to a collection. + * Returns true ("busy") if the page could not be locked; false otherwise. + */ +static bool page_trylock_add(struct page_collection *set, tb_page_addr_t addr) +{ + tb_page_addr_t index = addr >> TARGET_PAGE_BITS; + struct page_entry *pe; + PageDesc *pd; + + pe = g_tree_lookup(set->tree, &index); + if (pe) { + return false; + } + + pd = page_find(index); + if (pd == NULL) { + return false; + } + + pe = page_entry_new(pd, index); + g_tree_insert(set->tree, &pe->index, pe); + + /* + * If this is either (1) the first insertion or (2) a page whose index + * is higher than any other so far, just lock the page and move on. + */ + if (set->max == NULL || pe->index > set->max->index) { + set->max = pe; + do_page_entry_lock(pe); + return false; + } + /* + * Try to acquire out-of-order lock; if busy, return busy so that we acquire + * locks in order. + */ + return page_entry_trylock(pe); +} + +static gint tb_page_addr_cmp(gconstpointer ap, gconstpointer bp, gpointer udata) +{ + tb_page_addr_t a = *(const tb_page_addr_t *)ap; + tb_page_addr_t b = *(const tb_page_addr_t *)bp; + + if (a == b) { + return 0; + } else if (a < b) { + return -1; + } + return 1; +} + +/* + * Lock a range of pages ([@start,@end[) as well as the pages of all + * intersecting TBs. + * Locking order: acquire locks in ascending order of page index. + */ +struct page_collection * +page_collection_lock(tb_page_addr_t start, tb_page_addr_t end) +{ + struct page_collection *set = g_malloc(sizeof(*set)); + tb_page_addr_t index; + PageDesc *pd; + + start >>= TARGET_PAGE_BITS; + end >>= TARGET_PAGE_BITS; + g_assert(start <= end); + + set->tree = g_tree_new_full(tb_page_addr_cmp, NULL, NULL, + page_entry_destroy); + set->max = NULL; + + retry: + g_tree_foreach(set->tree, page_entry_lock, NULL); + + for (index = start; index <= end; index++) { + TranslationBlock *tb; + int n; + + pd = page_find(index); + if (pd == NULL) { + continue; + } + if (page_trylock_add(set, index << TARGET_PAGE_BITS)) { + g_tree_foreach(set->tree, page_entry_unlock, NULL); + goto retry; + } + PAGE_FOR_EACH_TB(pd, tb, n) { + if (page_trylock_add(set, tb->page_addr[0]) || + (tb->page_addr[1] != -1 && + page_trylock_add(set, tb->page_addr[1]))) { + /* drop all locks, and reacquire in order */ + g_tree_foreach(set->tree, page_entry_unlock, NULL); + goto retry; + } + } + } + return set; +} + +void page_collection_unlock(struct page_collection *set) +{ + /* entries are unlocked and freed via page_entry_destroy */ + g_tree_destroy(set->tree); + g_free(set); +} + +#endif /* !CONFIG_USER_ONLY */ + +static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, + PageDesc **ret_p2, tb_page_addr_t phys2, int alloc) +{ + PageDesc *p1, *p2; + + assert_memory_lock(); + g_assert(phys1 != -1 && phys1 != phys2); + p1 = page_find_alloc(phys1 >> TARGET_PAGE_BITS, alloc); + if (ret_p1) { + *ret_p1 = p1; + } + if (likely(phys2 == -1)) { + page_lock(p1); + return; + } + p2 = page_find_alloc(phys2 >> TARGET_PAGE_BITS, alloc); + if (ret_p2) { + *ret_p2 = p2; + } + if (phys1 < phys2) { + page_lock(p1); + page_lock(p2); + } else { + page_lock(p2); + page_lock(p1); + } +} + #if defined(CONFIG_USER_ONLY) /* Currently it is not recommended to allocate big chunks of data in user mode. It will change when a dedicated libc will be used. */ @@ -807,6 +1110,7 @@ static TranslationBlock *tb_alloc(target_ulong pc) return tb; } +/* call with @p->lock held */ static inline void invalidate_page_bitmap(PageDesc *p) { #ifdef CONFIG_SOFTMMU @@ -828,8 +1132,10 @@ static void page_flush_tb_1(int level, void **lp) PageDesc *pd = *lp; for (i = 0; i < V_L2_SIZE; ++i) { + page_lock(&pd[i]); pd[i].first_tb = (uintptr_t)NULL; invalidate_page_bitmap(pd + i); + page_unlock(&pd[i]); } } else { void **pp = *lp; @@ -956,6 +1262,7 @@ static void tb_page_check(void) #endif /* CONFIG_USER_ONLY */ +/* call with @pd->lock held */ static inline void tb_page_remove(PageDesc *pd, TranslationBlock *tb) { TranslationBlock *tb1; @@ -1032,11 +1339,8 @@ static inline void tb_jmp_unlink(TranslationBlock *tb) } } -/* invalidate one TB - * - * Called with tb_lock held. - */ -void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) +/* If @rm_from_page_list is set, call with the TB's pages' locks held */ +static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) { CPUState *cpu; PageDesc *p; @@ -1056,15 +1360,15 @@ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) } /* remove the TB from the page list */ - if (tb->page_addr[0] != page_addr) { + if (rm_from_page_list) { p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); tb_page_remove(p, tb); invalidate_page_bitmap(p); - } - if (tb->page_addr[1] != -1 && tb->page_addr[1] != page_addr) { - p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); - tb_page_remove(p, tb); - invalidate_page_bitmap(p); + if (tb->page_addr[1] != -1) { + p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); + tb_page_remove(p, tb); + invalidate_page_bitmap(p); + } } /* remove the TB from the hash list */ @@ -1086,7 +1390,28 @@ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) tcg_ctx->tb_phys_invalidate_count + 1); } +static void tb_phys_invalidate__locked(TranslationBlock *tb) +{ + do_tb_phys_invalidate(tb, true); +} + +/* invalidate one TB + * + * Called with tb_lock held. + */ +void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) +{ + if (page_addr == -1) { + page_lock_tb(tb); + do_tb_phys_invalidate(tb, true); + page_unlock_tb(tb); + } else { + do_tb_phys_invalidate(tb, false); + } +} + #ifdef CONFIG_SOFTMMU +/* call with @p->lock held */ static void build_page_bitmap(PageDesc *p) { int n, tb_start, tb_end; @@ -1116,11 +1441,11 @@ static void build_page_bitmap(PageDesc *p) /* add the tb in the target page and protect it if necessary * * Called with mmap_lock held for user-mode emulation. + * Called with @p->lock held. */ -static inline void tb_alloc_page(TranslationBlock *tb, - unsigned int n, tb_page_addr_t page_addr) +static inline void tb_page_add(PageDesc *p, TranslationBlock *tb, + unsigned int n, tb_page_addr_t page_addr) { - PageDesc *p; #ifndef CONFIG_USER_ONLY bool page_already_protected; #endif @@ -1128,7 +1453,6 @@ static inline void tb_alloc_page(TranslationBlock *tb, assert_memory_lock(); tb->page_addr[n] = page_addr; - p = page_find_alloc(page_addr >> TARGET_PAGE_BITS, 1); tb->page_next[n] = p->first_tb; #ifndef CONFIG_USER_ONLY page_already_protected = p->first_tb != (uintptr_t)NULL; @@ -1180,18 +1504,28 @@ static inline void tb_alloc_page(TranslationBlock *tb, static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, tb_page_addr_t phys_page2) { + PageDesc *p; + PageDesc *p2 = NULL; uint32_t h; assert_memory_lock(); - /* add in the page list */ - tb_alloc_page(tb, 0, phys_pc & TARGET_PAGE_MASK); - if (phys_page2 != -1) { - tb_alloc_page(tb, 1, phys_page2); + /* + * Add the TB to the page list, acquiring first the pages's locks. + */ + page_lock_pair(&p, phys_pc, &p2, phys_page2, 1); + tb_page_add(p, tb, 0, phys_pc & TARGET_PAGE_MASK); + if (p2) { + tb_page_add(p2, tb, 1, phys_page2); } else { tb->page_addr[1] = -1; } + if (p2) { + page_unlock(p2); + } + page_unlock(p); + /* add in the hash table */ h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, tb->trace_vcpu_dstate); @@ -1364,21 +1698,17 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } /* - * Invalidate all TBs which intersect with the target physical address range - * [start;end[. NOTE: start and end must refer to the *same* physical page. - * 'is_cpu_write_access' should be true if called from a real cpu write - * access: the virtual CPU will exit the current TB if code is modified inside - * this TB. - * - * Called with tb_lock/mmap_lock held for user-mode emulation - * Called with tb_lock held for system-mode emulation + * Call with all @pages locked. + * @p must be non-NULL. */ -void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, - int is_cpu_write_access) +static void +tb_invalidate_phys_page_range__locked(struct page_collection *pages, + PageDesc *p, tb_page_addr_t start, + tb_page_addr_t end, + int is_cpu_write_access) { TranslationBlock *tb; tb_page_addr_t tb_start, tb_end; - PageDesc *p; int n; #ifdef TARGET_HAS_PRECISE_SMC CPUState *cpu = current_cpu; @@ -1394,10 +1724,6 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, assert_memory_lock(); assert_tb_locked(); - p = page_find(start >> TARGET_PAGE_BITS); - if (!p) { - return; - } #if defined(TARGET_HAS_PRECISE_SMC) if (cpu != NULL) { env = cpu->env_ptr; @@ -1443,7 +1769,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, ¤t_flags); } #endif /* TARGET_HAS_PRECISE_SMC */ - tb_phys_invalidate(tb, -1); + tb_phys_invalidate__locked(tb); } } #if !defined(CONFIG_USER_ONLY) @@ -1455,6 +1781,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, #endif #ifdef TARGET_HAS_PRECISE_SMC if (current_tb_modified) { + page_collection_unlock(pages); /* Force execution of one insn next time. */ cpu->cflags_next_tb = 1 | curr_cflags(); cpu_loop_exit_noexc(cpu); @@ -1462,6 +1789,35 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, #endif } +/* + * Invalidate all TBs which intersect with the target physical address range + * [start;end[. NOTE: start and end must refer to the *same* physical page. + * 'is_cpu_write_access' should be true if called from a real cpu write + * access: the virtual CPU will exit the current TB if code is modified inside + * this TB. + * + * Called with tb_lock/mmap_lock held for user-mode emulation + * Called with tb_lock held for system-mode emulation + */ +void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, + int is_cpu_write_access) +{ + struct page_collection *pages; + PageDesc *p; + + assert_memory_lock(); + assert_tb_locked(); + + p = page_find(start >> TARGET_PAGE_BITS); + if (p == NULL) { + return; + } + pages = page_collection_lock(start, end); + tb_invalidate_phys_page_range__locked(pages, p, start, end, + is_cpu_write_access); + page_collection_unlock(pages); +} + /* * Invalidate all TBs which intersect with the target physical address range * [start;end[. NOTE: start and end may refer to *different* physical pages. @@ -1474,15 +1830,22 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, */ static void tb_invalidate_phys_range_1(tb_page_addr_t start, tb_page_addr_t end) { + struct page_collection *pages; tb_page_addr_t next; + pages = page_collection_lock(start, end); for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; start < end; start = next, next += TARGET_PAGE_SIZE) { + PageDesc *pd = page_find(start >> TARGET_PAGE_BITS); tb_page_addr_t bound = MIN(next, end); - tb_invalidate_phys_page_range(start, bound, 0); + if (pd == NULL) { + continue; + } + tb_invalidate_phys_page_range__locked(pages, pd, start, bound, 0); } + page_collection_unlock(pages); } #ifdef CONFIG_SOFTMMU @@ -1508,6 +1871,7 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) */ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) { + struct page_collection *pages; PageDesc *p; #if 0 @@ -1525,11 +1889,10 @@ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) if (!p) { return; } + + pages = page_collection_lock(start, start + len); if (!p->code_bitmap && ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD) { - /* build code bitmap. FIXME: writes should be protected by - * tb_lock, reads by tb_lock or RCU. - */ build_page_bitmap(p); } if (p->code_bitmap) { @@ -1543,8 +1906,9 @@ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) } } else { do_invalidate: - tb_invalidate_phys_page_range(start, start + len, 1); + tb_invalidate_phys_page_range__locked(pages, p, start, start + len, 1); } + page_collection_unlock(pages); } #else /* Called with mmap_lock held. If pc is not 0 then it indicates the diff --git a/accel/tcg/translate-all.h b/accel/tcg/translate-all.h index ba8e4d63c4..6d1d2588b5 100644 --- a/accel/tcg/translate-all.h +++ b/accel/tcg/translate-all.h @@ -23,6 +23,9 @@ /* translate-all.c */ +struct page_collection *page_collection_lock(tb_page_addr_t start, + tb_page_addr_t end); +void page_collection_unlock(struct page_collection *set); void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, int is_cpu_write_access); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 07653d3c92..8d92e3cea9 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -359,7 +359,8 @@ struct TranslationBlock { /* original tb when cflags has CF_NOCACHE */ struct TranslationBlock *orig_tb; /* first and second physical page containing code. The lower bit - of the pointer tells the index in page_next[] */ + of the pointer tells the index in page_next[]. + The list is protected by the TB's page('s) lock(s) */ uintptr_t page_next[2]; tb_page_addr_t page_addr[2]; From 6d9abf85d538731ccff25fc29d7fa938115b1a80 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Thu, 5 Apr 2018 19:52:53 -0400 Subject: [PATCH 1316/2380] translate-all: add page_locked assertions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is only compiled under CONFIG_DEBUG_TCG to avoid bloating the binary. In user-mode, assert_page_locked is equivalent to assert_mmap_lock. Note: There are some tb_lock assertions left that will be removed by later patches. Reviewed-by: Richard Henderson Suggested-by: Alex Bennée Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 82 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 1cc7aab82c..8b378586f4 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -583,6 +583,9 @@ static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, /* In user-mode page locks aren't used; mmap_lock is enough */ #ifdef CONFIG_USER_ONLY + +#define assert_page_locked(pd) tcg_debug_assert(have_mmap_lock()) + static inline void page_lock(PageDesc *pd) { } @@ -605,14 +608,80 @@ void page_collection_unlock(struct page_collection *set) { } #else /* !CONFIG_USER_ONLY */ +#ifdef CONFIG_DEBUG_TCG + +static __thread GHashTable *ht_pages_locked_debug; + +static void ht_pages_locked_debug_init(void) +{ + if (ht_pages_locked_debug) { + return; + } + ht_pages_locked_debug = g_hash_table_new(NULL, NULL); +} + +static bool page_is_locked(const PageDesc *pd) +{ + PageDesc *found; + + ht_pages_locked_debug_init(); + found = g_hash_table_lookup(ht_pages_locked_debug, pd); + return !!found; +} + +static void page_lock__debug(PageDesc *pd) +{ + ht_pages_locked_debug_init(); + g_assert(!page_is_locked(pd)); + g_hash_table_insert(ht_pages_locked_debug, pd, pd); +} + +static void page_unlock__debug(const PageDesc *pd) +{ + bool removed; + + ht_pages_locked_debug_init(); + g_assert(page_is_locked(pd)); + removed = g_hash_table_remove(ht_pages_locked_debug, pd); + g_assert(removed); +} + +static void +do_assert_page_locked(const PageDesc *pd, const char *file, int line) +{ + if (unlikely(!page_is_locked(pd))) { + error_report("assert_page_lock: PageDesc %p not locked @ %s:%d", + pd, file, line); + abort(); + } +} + +#define assert_page_locked(pd) do_assert_page_locked(pd, __FILE__, __LINE__) + +#else /* !CONFIG_DEBUG_TCG */ + +#define assert_page_locked(pd) + +static inline void page_lock__debug(const PageDesc *pd) +{ +} + +static inline void page_unlock__debug(const PageDesc *pd) +{ +} + +#endif /* CONFIG_DEBUG_TCG */ + static inline void page_lock(PageDesc *pd) { + page_lock__debug(pd); qemu_spin_lock(&pd->lock); } static inline void page_unlock(PageDesc *pd) { qemu_spin_unlock(&pd->lock); + page_unlock__debug(pd); } /* lock the page(s) of a TB in the correct acquisition order */ @@ -658,6 +727,7 @@ static bool page_entry_trylock(struct page_entry *pe) if (!busy) { g_assert(!pe->locked); pe->locked = true; + page_lock__debug(pe->pd); } return busy; } @@ -775,6 +845,7 @@ page_collection_lock(tb_page_addr_t start, tb_page_addr_t end) g_tree_foreach(set->tree, page_entry_unlock, NULL); goto retry; } + assert_page_locked(pd); PAGE_FOR_EACH_TB(pd, tb, n) { if (page_trylock_add(set, tb->page_addr[0]) || (tb->page_addr[1] != -1 && @@ -1113,6 +1184,7 @@ static TranslationBlock *tb_alloc(target_ulong pc) /* call with @p->lock held */ static inline void invalidate_page_bitmap(PageDesc *p) { + assert_page_locked(p); #ifdef CONFIG_SOFTMMU g_free(p->code_bitmap); p->code_bitmap = NULL; @@ -1269,6 +1341,7 @@ static inline void tb_page_remove(PageDesc *pd, TranslationBlock *tb) uintptr_t *pprev; unsigned int n1; + assert_page_locked(pd); pprev = &pd->first_tb; PAGE_FOR_EACH_TB(pd, tb1, n1) { if (tb1 == tb) { @@ -1417,6 +1490,7 @@ static void build_page_bitmap(PageDesc *p) int n, tb_start, tb_end; TranslationBlock *tb; + assert_page_locked(p); p->code_bitmap = bitmap_new(TARGET_PAGE_SIZE); PAGE_FOR_EACH_TB(p, tb, n) { @@ -1450,7 +1524,7 @@ static inline void tb_page_add(PageDesc *p, TranslationBlock *tb, bool page_already_protected; #endif - assert_memory_lock(); + assert_page_locked(p); tb->page_addr[n] = page_addr; tb->page_next[n] = p->first_tb; @@ -1721,8 +1795,7 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, uint32_t current_flags = 0; #endif /* TARGET_HAS_PRECISE_SMC */ - assert_memory_lock(); - assert_tb_locked(); + assert_page_locked(p); #if defined(TARGET_HAS_PRECISE_SMC) if (cpu != NULL) { @@ -1734,6 +1807,7 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, /* XXX: see if in some cases it could be faster to invalidate all the code */ PAGE_FOR_EACH_TB(p, tb, n) { + assert_page_locked(p); /* NOTE: this is subtle as a TB may span two physical pages */ if (n == 0) { /* NOTE: tb_end may be after the end of the page, but @@ -1891,6 +1965,7 @@ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) } pages = page_collection_lock(start, start + len); + assert_page_locked(p); if (!p->code_bitmap && ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD) { build_page_bitmap(p); @@ -1949,6 +2024,7 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) env = cpu->env_ptr; } #endif + assert_page_locked(p); PAGE_FOR_EACH_TB(p, tb, n) { #ifdef TARGET_HAS_PRECISE_SMC if (current_tb == tb && From faa9372c07d062eb01f9da72e3f6c0f32efffca7 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Thu, 22 Feb 2018 20:50:29 -0500 Subject: [PATCH 1317/2380] translate-all: introduce assert_no_pages_locked The appended adds assertions to make sure we do not longjmp with page locks held. Note that user-mode has nothing to check, since page_locks are !user-mode only. Reviewed-by: Richard Henderson Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 1 + accel/tcg/translate-all.c | 7 +++++++ include/exec/exec-all.h | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 7570c59f09..d75c35380a 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -273,6 +273,7 @@ void cpu_exec_step_atomic(CPUState *cpu) tcg_debug_assert(!have_mmap_lock()); #endif tb_lock_reset(); + assert_no_pages_locked(); } if (in_exclusive_region) { diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 8b378586f4..c75298d08a 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -658,6 +658,12 @@ do_assert_page_locked(const PageDesc *pd, const char *file, int line) #define assert_page_locked(pd) do_assert_page_locked(pd, __FILE__, __LINE__) +void assert_no_pages_locked(void) +{ + ht_pages_locked_debug_init(); + g_assert(g_hash_table_size(ht_pages_locked_debug) == 0); +} + #else /* !CONFIG_DEBUG_TCG */ #define assert_page_locked(pd) @@ -829,6 +835,7 @@ page_collection_lock(tb_page_addr_t start, tb_page_addr_t end) set->tree = g_tree_new_full(tb_page_addr_cmp, NULL, NULL, page_entry_destroy); set->max = NULL; + assert_no_pages_locked(); retry: g_tree_foreach(set->tree, page_entry_lock, NULL); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 8d92e3cea9..4f07a17052 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -435,6 +435,14 @@ void tb_lock(void); void tb_unlock(void); void tb_lock_reset(void); +#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_DEBUG_TCG) +void assert_no_pages_locked(void); +#else +static inline void assert_no_pages_locked(void) +{ +} +#endif + #if !defined(CONFIG_USER_ONLY) /** From 95590e24af11236ef334f6bc3e2b71404a790ddb Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 1 Aug 2017 15:40:16 -0400 Subject: [PATCH 1318/2380] translate-all: discard TB when tb_link_page returns an existing matching TB Use the recently-gained QHT feature of returning the matching TB if it already exists. This allows us to get rid of the lookup we perform right after acquiring tb_lock. Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 14 ++------- accel/tcg/translate-all.c | 50 +++++++++++++++++++++++++++------ docs/devel/multi-thread-tcg.txt | 3 ++ 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index d75c35380a..45f6ebc65e 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -245,10 +245,7 @@ void cpu_exec_step_atomic(CPUState *cpu) if (tb == NULL) { mmap_lock(); tb_lock(); - tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask); - if (likely(tb == NULL)) { - tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); - } + tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); tb_unlock(); mmap_unlock(); } @@ -398,14 +395,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu, tb_lock(); acquired_tb_lock = true; - /* There's a chance that our desired tb has been translated while - * taking the locks so we check again inside the lock. - */ - tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask); - if (likely(tb == NULL)) { - /* if no translated code available, then translate it now */ - tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); - } + tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); mmap_unlock(); /* We add the TB in the virtual pc hash table for the fast lookup */ diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index c75298d08a..3f977532bf 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1581,18 +1581,30 @@ static inline void tb_page_add(PageDesc *p, TranslationBlock *tb, * (-1) to indicate that only one page contains the TB. * * Called with mmap_lock held for user-mode emulation. + * + * Returns a pointer @tb, or a pointer to an existing TB that matches @tb. + * Note that in !user-mode, another thread might have already added a TB + * for the same block of guest code that @tb corresponds to. In that case, + * the caller should discard the original @tb, and use instead the returned TB. */ -static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, - tb_page_addr_t phys_page2) +static TranslationBlock * +tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, + tb_page_addr_t phys_page2) { PageDesc *p; PageDesc *p2 = NULL; + void *existing_tb = NULL; uint32_t h; assert_memory_lock(); /* * Add the TB to the page list, acquiring first the pages's locks. + * We keep the locks held until after inserting the TB in the hash table, + * so that if the insertion fails we know for sure that the TBs are still + * in the page descriptors. + * Note that inserting into the hash table first isn't an option, since + * we can only insert TBs that are fully initialized. */ page_lock_pair(&p, phys_pc, &p2, phys_page2, 1); tb_page_add(p, tb, 0, phys_pc & TARGET_PAGE_MASK); @@ -1602,21 +1614,33 @@ static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, tb->page_addr[1] = -1; } + /* add in the hash table */ + h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, + tb->trace_vcpu_dstate); + qht_insert(&tb_ctx.htable, tb, h, &existing_tb); + + /* remove TB from the page(s) if we couldn't insert it */ + if (unlikely(existing_tb)) { + tb_page_remove(p, tb); + invalidate_page_bitmap(p); + if (p2) { + tb_page_remove(p2, tb); + invalidate_page_bitmap(p2); + } + tb = existing_tb; + } + if (p2) { page_unlock(p2); } page_unlock(p); - /* add in the hash table */ - h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, - tb->trace_vcpu_dstate); - qht_insert(&tb_ctx.htable, tb, h, NULL); - #ifdef CONFIG_USER_ONLY if (DEBUG_TB_CHECK_GATE) { tb_page_check(); } #endif + return tb; } /* Called with mmap_lock held for user mode emulation. */ @@ -1625,7 +1649,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, uint32_t flags, int cflags) { CPUArchState *env = cpu->env_ptr; - TranslationBlock *tb; + TranslationBlock *tb, *existing_tb; tb_page_addr_t phys_pc, phys_page2; target_ulong virt_page2; tcg_insn_unit *gen_code_buf; @@ -1773,7 +1797,15 @@ TranslationBlock *tb_gen_code(CPUState *cpu, * memory barrier is required before tb_link_page() makes the TB visible * through the physical hash table and physical page list. */ - tb_link_page(tb, phys_pc, phys_page2); + existing_tb = tb_link_page(tb, phys_pc, phys_page2); + /* if the TB already exists, discard what we just translated */ + if (unlikely(existing_tb != tb)) { + uintptr_t orig_aligned = (uintptr_t)gen_code_buf; + + orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize); + atomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned); + return existing_tb; + } tcg_tb_insert(tb); return tb; } diff --git a/docs/devel/multi-thread-tcg.txt b/docs/devel/multi-thread-tcg.txt index faf8918b23..faf09c6069 100644 --- a/docs/devel/multi-thread-tcg.txt +++ b/docs/devel/multi-thread-tcg.txt @@ -140,6 +140,9 @@ to atomically insert new elements. The lookup caches are updated atomically and the lookup hash uses QHT which is designed for concurrent safe lookup. +Parallel code generation is supported. QHT is used at insertion time +as the synchronization point across threads, thereby ensuring that we only +keep track of a single TranslationBlock for each guest code block. Memory maps and TLBs -------------------- From 194125e3ebd553acb02aaf3797a4f0387493fe94 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Wed, 2 Aug 2017 20:34:06 -0400 Subject: [PATCH 1319/2380] translate-all: protect TB jumps with a per-destination-TB lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This applies to both user-mode and !user-mode emulation. Instead of relying on a global lock, protect the list of incoming jumps with tb->jmp_lock. This lock also protects tb->cflags, so update all tb->cflags readers outside tb->jmp_lock to use atomic reads via tb_cflags(). In order to find the destination TB (and therefore its jmp_lock) from the origin TB, we introduce tb->jmp_dest[]. I considered not using a linked list of jumps, which simplifies code and makes the struct smaller. However, it unnecessarily increases memory usage, which results in a performance decrease. See for instance these numbers booting+shutting down debian-arm: Time (s) Rel. err (%) Abs. err (s) Rel. slowdown (%) ------------------------------------------------------------------------------ before 20.88 0.74 0.154512 0. after 20.81 0.38 0.079078 -0.33524904 GTree 21.02 0.28 0.058856 0.67049808 GHashTable + xxhash 21.63 1.08 0.233604 3.5919540 Using a hash table or a binary tree to keep track of the jumps doesn't really pay off, not only due to the increased memory usage, but also because most TBs have only 0 or 1 jumps to them. The maximum number of jumps when booting debian-arm that I measured is 35, but as we can see in the histogram below a TB with that many incoming jumps is extremely rare; the average TB has 0.80 incoming jumps. n_jumps: 379208; avg jumps/tb: 0.801099 dist: [0.0,1.0)|▄█▁▁▁▁▁▁▁▁▁▁▁ ▁▁▁▁▁▁ ▁▁▁ ▁▁▁ ▁|[34.0,35.0] Reviewed-by: Richard Henderson Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 41 +++++++---- accel/tcg/translate-all.c | 120 +++++++++++++++++++------------- docs/devel/multi-thread-tcg.txt | 6 +- include/exec/exec-all.h | 35 ++++++---- 4 files changed, 125 insertions(+), 77 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 45f6ebc65e..c482008bc7 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -352,28 +352,43 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) } } -/* Called with tb_lock held. */ static inline void tb_add_jump(TranslationBlock *tb, int n, TranslationBlock *tb_next) { + uintptr_t old; + assert(n < ARRAY_SIZE(tb->jmp_list_next)); - if (tb->jmp_list_next[n]) { - /* Another thread has already done this while we were - * outside of the lock; nothing to do in this case */ - return; + qemu_spin_lock(&tb_next->jmp_lock); + + /* make sure the destination TB is valid */ + if (tb_next->cflags & CF_INVALID) { + goto out_unlock_next; } + /* Atomically claim the jump destination slot only if it was NULL */ + old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next); + if (old) { + goto out_unlock_next; + } + + /* patch the native jump address */ + tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); + + /* add in TB jmp list */ + tb->jmp_list_next[n] = tb_next->jmp_list_head; + tb_next->jmp_list_head = (uintptr_t)tb | n; + + qemu_spin_unlock(&tb_next->jmp_lock); + qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, "Linking TBs %p [" TARGET_FMT_lx "] index %d -> %p [" TARGET_FMT_lx "]\n", tb->tc.ptr, tb->pc, n, tb_next->tc.ptr, tb_next->pc); + return; - /* patch the native jump address */ - tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); - - /* add in TB jmp circular list */ - tb->jmp_list_next[n] = tb_next->jmp_list_first; - tb_next->jmp_list_first = (uintptr_t)tb | n; + out_unlock_next: + qemu_spin_unlock(&tb_next->jmp_lock); + return; } static inline TranslationBlock *tb_find(CPUState *cpu, @@ -416,9 +431,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu, tb_lock(); acquired_tb_lock = true; } - if (!(tb->cflags & CF_INVALID)) { - tb_add_jump(last_tb, tb_exit, tb); - } + tb_add_jump(last_tb, tb_exit, tb); } if (acquired_tb_lock) { tb_unlock(); diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 3f977532bf..55c9e1b196 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -170,6 +170,9 @@ struct page_collection { #define PAGE_FOR_EACH_TB(pagedesc, tb, n) \ TB_FOR_EACH_TAGGED((pagedesc)->first_tb, tb, n, page_next) +#define TB_FOR_EACH_JMP(head_tb, tb, n) \ + TB_FOR_EACH_TAGGED((head_tb)->jmp_list_head, tb, n, jmp_list_next) + /* In system mode we want L1_MAP to be based on ram offsets, while in user mode we want it to be based on virtual addresses. */ #if !defined(CONFIG_USER_ONLY) @@ -389,7 +392,7 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, return -1; found: - if (reset_icount && (tb->cflags & CF_USE_ICOUNT)) { + if (reset_icount && (tb_cflags(tb) & CF_USE_ICOUNT)) { assert(use_icount); /* Reset the cycle counter to the start of the block and shift if to the number of actually executed instructions */ @@ -432,7 +435,7 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) tb = tcg_tb_lookup(host_pc); if (tb) { cpu_restore_state_from_tb(cpu, tb, host_pc, will_exit); - if (tb->cflags & CF_NOCACHE) { + if (tb_cflags(tb) & CF_NOCACHE) { /* one-shot translation, invalidate it immediately */ tb_phys_invalidate(tb, -1); tcg_tb_remove(tb); @@ -1360,34 +1363,53 @@ static inline void tb_page_remove(PageDesc *pd, TranslationBlock *tb) g_assert_not_reached(); } -/* remove the TB from a list of TBs jumping to the n-th jump target of the TB */ -static inline void tb_remove_from_jmp_list(TranslationBlock *tb, int n) +/* remove @orig from its @n_orig-th jump list */ +static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig) { - TranslationBlock *tb1; - uintptr_t *ptb, ntb; - unsigned int n1; + uintptr_t ptr, ptr_locked; + TranslationBlock *dest; + TranslationBlock *tb; + uintptr_t *pprev; + int n; - ptb = &tb->jmp_list_next[n]; - if (*ptb) { - /* find tb(n) in circular list */ - for (;;) { - ntb = *ptb; - n1 = ntb & 3; - tb1 = (TranslationBlock *)(ntb & ~3); - if (n1 == n && tb1 == tb) { - break; - } - if (n1 == 2) { - ptb = &tb1->jmp_list_first; - } else { - ptb = &tb1->jmp_list_next[n1]; - } - } - /* now we can suppress tb(n) from the list */ - *ptb = tb->jmp_list_next[n]; - - tb->jmp_list_next[n] = (uintptr_t)NULL; + /* mark the LSB of jmp_dest[] so that no further jumps can be inserted */ + ptr = atomic_or_fetch(&orig->jmp_dest[n_orig], 1); + dest = (TranslationBlock *)(ptr & ~1); + if (dest == NULL) { + return; } + + qemu_spin_lock(&dest->jmp_lock); + /* + * While acquiring the lock, the jump might have been removed if the + * destination TB was invalidated; check again. + */ + ptr_locked = atomic_read(&orig->jmp_dest[n_orig]); + if (ptr_locked != ptr) { + qemu_spin_unlock(&dest->jmp_lock); + /* + * The only possibility is that the jump was unlinked via + * tb_jump_unlink(dest). Seeing here another destination would be a bug, + * because we set the LSB above. + */ + g_assert(ptr_locked == 1 && dest->cflags & CF_INVALID); + return; + } + /* + * We first acquired the lock, and since the destination pointer matches, + * we know for sure that @orig is in the jmp list. + */ + pprev = &dest->jmp_list_head; + TB_FOR_EACH_JMP(dest, tb, n) { + if (tb == orig && n == n_orig) { + *pprev = tb->jmp_list_next[n]; + /* no need to set orig->jmp_dest[n]; setting the LSB was enough */ + qemu_spin_unlock(&dest->jmp_lock); + return; + } + pprev = &tb->jmp_list_next[n]; + } + g_assert_not_reached(); } /* reset the jump entry 'n' of a TB so that it is not chained to @@ -1399,24 +1421,21 @@ static inline void tb_reset_jump(TranslationBlock *tb, int n) } /* remove any jumps to the TB */ -static inline void tb_jmp_unlink(TranslationBlock *tb) +static inline void tb_jmp_unlink(TranslationBlock *dest) { - TranslationBlock *tb1; - uintptr_t *ptb, ntb; - unsigned int n1; + TranslationBlock *tb; + int n; - ptb = &tb->jmp_list_first; - for (;;) { - ntb = *ptb; - n1 = ntb & 3; - tb1 = (TranslationBlock *)(ntb & ~3); - if (n1 == 2) { - break; - } - tb_reset_jump(tb1, n1); - *ptb = tb1->jmp_list_next[n1]; - tb1->jmp_list_next[n1] = (uintptr_t)NULL; + qemu_spin_lock(&dest->jmp_lock); + + TB_FOR_EACH_JMP(dest, tb, n) { + tb_reset_jump(tb, n); + atomic_and(&tb->jmp_dest[n], (uintptr_t)NULL | 1); + /* No need to clear the list entry; setting the dest ptr is enough */ } + dest->jmp_list_head = (uintptr_t)NULL; + + qemu_spin_unlock(&dest->jmp_lock); } /* If @rm_from_page_list is set, call with the TB's pages' locks held */ @@ -1429,11 +1448,14 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) assert_tb_locked(); + /* make sure no further incoming jumps will be chained to this TB */ + qemu_spin_lock(&tb->jmp_lock); atomic_set(&tb->cflags, tb->cflags | CF_INVALID); + qemu_spin_unlock(&tb->jmp_lock); /* remove the TB from the hash list */ phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); - h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, + h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb_cflags(tb) & CF_HASH_MASK, tb->trace_vcpu_dstate); if (!qht_remove(&tb_ctx.htable, tb, h)) { return; @@ -1773,10 +1795,12 @@ TranslationBlock *tb_gen_code(CPUState *cpu, CODE_GEN_ALIGN)); /* init jump list */ - assert(((uintptr_t)tb & 3) == 0); - tb->jmp_list_first = (uintptr_t)tb | 2; + qemu_spin_init(&tb->jmp_lock); + tb->jmp_list_head = (uintptr_t)NULL; tb->jmp_list_next[0] = (uintptr_t)NULL; tb->jmp_list_next[1] = (uintptr_t)NULL; + tb->jmp_dest[0] = (uintptr_t)NULL; + tb->jmp_dest[1] = (uintptr_t)NULL; /* init original jump addresses wich has been set during tcg_gen_code() */ if (tb->jmp_reset_offset[0] != TB_JMP_RESET_OFFSET_INVALID) { @@ -1868,7 +1892,7 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, } } if (current_tb == tb && - (current_tb->cflags & CF_COUNT_MASK) != 1) { + (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { /* If we are modifying the current TB, we must stop its execution. We could be more precise by checking that the modification is after the current PC, but it @@ -2067,7 +2091,7 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) PAGE_FOR_EACH_TB(p, tb, n) { #ifdef TARGET_HAS_PRECISE_SMC if (current_tb == tb && - (current_tb->cflags & CF_COUNT_MASK) != 1) { + (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { /* If we are modifying the current TB, we must stop its execution. We could be more precise by checking that the modification is after the current PC, but it @@ -2192,7 +2216,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) /* Generate a new TB executing the I/O insn. */ cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | n; - if (tb->cflags & CF_NOCACHE) { + if (tb_cflags(tb) & CF_NOCACHE) { if (tb->orig_tb) { /* Invalidate original TB if this TB was generated in * cpu_exec_nocache() */ diff --git a/docs/devel/multi-thread-tcg.txt b/docs/devel/multi-thread-tcg.txt index faf09c6069..df83445ccf 100644 --- a/docs/devel/multi-thread-tcg.txt +++ b/docs/devel/multi-thread-tcg.txt @@ -131,8 +131,10 @@ DESIGN REQUIREMENT: Safely handle invalidation of TBs The direct jump themselves are updated atomically by the TCG tb_set_jmp_target() code. Modification to the linked lists that allow -searching for linked pages are done under the protect of the -tb_lock(). +searching for linked pages are done under the protection of tb->jmp_lock, +where tb is the destination block of a jump. Each origin block keeps a +pointer to its destinations so that the appropriate lock can be acquired before +iterating over a jump list. The global page table is a lockless radix tree; cmpxchg is used to atomically insert new elements. diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 4f07a17052..3c2a0efb55 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -345,7 +345,7 @@ struct TranslationBlock { #define CF_LAST_IO 0x00008000 /* Last insn may be an IO access. */ #define CF_NOCACHE 0x00010000 /* To be freed after execution */ #define CF_USE_ICOUNT 0x00020000 -#define CF_INVALID 0x00040000 /* TB is stale. Setters need tb_lock */ +#define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */ #define CF_PARALLEL 0x00080000 /* Generate code for a parallel context */ /* cflags' mask for hashing/comparison */ #define CF_HASH_MASK \ @@ -364,6 +364,9 @@ struct TranslationBlock { uintptr_t page_next[2]; tb_page_addr_t page_addr[2]; + /* jmp_lock placed here to fill a 4-byte hole. Its documentation is below */ + QemuSpin jmp_lock; + /* The following data are used to directly call another TB from * the code of this one. This can be done either by emitting direct or * indirect native jump instructions. These jumps are reset so that the TB @@ -375,20 +378,26 @@ struct TranslationBlock { #define TB_JMP_RESET_OFFSET_INVALID 0xffff /* indicates no jump generated */ uintptr_t jmp_target_arg[2]; /* target address or offset */ - /* Each TB has an associated circular list of TBs jumping to this one. - * jmp_list_first points to the first TB jumping to this one. - * jmp_list_next is used to point to the next TB in a list. - * Since each TB can have two jumps, it can participate in two lists. - * jmp_list_first and jmp_list_next are 4-byte aligned pointers to a - * TranslationBlock structure, but the two least significant bits of - * them are used to encode which data field of the pointed TB should - * be used to traverse the list further from that TB: - * 0 => jmp_list_next[0], 1 => jmp_list_next[1], 2 => jmp_list_first. - * In other words, 0/1 tells which jump is used in the pointed TB, - * and 2 means that this is a pointer back to the target TB of this list. + /* + * Each TB has a NULL-terminated list (jmp_list_head) of incoming jumps. + * Each TB can have two outgoing jumps, and therefore can participate + * in two lists. The list entries are kept in jmp_list_next[2]. The least + * significant bit (LSB) of the pointers in these lists is used to encode + * which of the two list entries is to be used in the pointed TB. + * + * List traversals are protected by jmp_lock. The destination TB of each + * outgoing jump is kept in jmp_dest[] so that the appropriate jmp_lock + * can be acquired from any origin TB. + * + * jmp_dest[] are tagged pointers as well. The LSB is set when the TB is + * being invalidated, so that no further outgoing jumps from it can be set. + * + * jmp_lock also protects the CF_INVALID cflag; a jump must not be chained + * to a destination TB that has CF_INVALID set. */ + uintptr_t jmp_list_head; uintptr_t jmp_list_next[2]; - uintptr_t jmp_list_first; + uintptr_t jmp_dest[2]; }; extern bool parallel_cpus; From b7542f7fe8f879b7b1e74f5fbd36b5746dbb6712 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Fri, 4 Aug 2017 18:58:37 -0400 Subject: [PATCH 1320/2380] cputlb: remove tb_lock from tlb_flush functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The acquisition of tb_lock was added when the async tlb_flush was introduced in e3b9ca810 ("cputlb: introduce tlb_flush_* async work.") tb_lock was there to allow us to do memset() on the tb_jmp_cache's. However, since f3ced3c5928 ("tcg: consistently access cpu->tb_jmp_cache atomically") all accesses to tb_jmp_cache are atomic, so tb_lock is not needed here. Get rid of it. Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 0a721bb9c4..719cca2268 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -125,8 +125,6 @@ static void tlb_flush_nocheck(CPUState *cpu) atomic_set(&env->tlb_flush_count, env->tlb_flush_count + 1); tlb_debug("(count: %zu)\n", tlb_flush_count()); - tb_lock(); - memset(env->tlb_table, -1, sizeof(env->tlb_table)); memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table)); cpu_tb_jmp_cache_clear(cpu); @@ -135,8 +133,6 @@ static void tlb_flush_nocheck(CPUState *cpu) env->tlb_flush_addr = -1; env->tlb_flush_mask = 0; - tb_unlock(); - atomic_mb_set(&cpu->pending_tlb_flush, 0); } @@ -180,8 +176,6 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) assert_cpu_is_self(cpu); - tb_lock(); - tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { @@ -197,8 +191,6 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) cpu_tb_jmp_cache_clear(cpu); tlb_debug("done\n"); - - tb_unlock(); } void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) From 705ad1ff0ce264475cb4c9a3aa31ba94a04869fe Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Sat, 5 Aug 2017 01:27:30 -0400 Subject: [PATCH 1321/2380] translate-all: remove tb_lock mention from cpu_restore_state_from_tb tb_lock was needed when the function did retranslation. However, since fca8a500d519 ("tcg: Save insn data and use it in cpu_restore_state_from_tb") we don't do retranslation. Get rid of the comment. Reviewed-by: Richard Henderson Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 1 - 1 file changed, 1 deletion(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 55c9e1b196..b33cd9bc98 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -355,7 +355,6 @@ static int encode_search(TranslationBlock *tb, uint8_t *block) } /* The cpu state corresponding to 'searched_pc' is restored. - * Called with tb_lock held. * When reset_icount is true, current TB will be interrupted and * icount should be recalculated. */ From 0ac20318ce16f4de288969b2007ef5a654176058 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Fri, 4 Aug 2017 23:46:31 -0400 Subject: [PATCH 1322/2380] tcg: remove tb_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use mmap_lock in user-mode to protect TCG state and the page descriptors. In !user-mode, each vCPU has its own TCG state, so no locks needed. Per-page locks are used to protect the page descriptors. Per-TB locks are used in both modes to protect TB jumps. Some notes: - tb_lock is removed from notdirty_mem_write by passing a locked page_collection to tb_invalidate_phys_page_fast. - tcg_tb_lookup/remove/insert/etc have their own internal lock(s), so there is no need to further serialize access to them. - do_tb_flush is run in a safe async context, meaning no other vCPU threads are running. Therefore acquiring mmap_lock there is just to please tools such as thread sanitizer. - Not visible in the diff, but tb_invalidate_phys_page already has an assert_memory_lock. - cpu_io_recompile is !user-only, so no mmap_lock there. - Added mmap_unlock()'s before all siglongjmp's that could be called in user-mode while mmap_lock is held. + Added an assert for !have_mmap_lock() after returning from the longjmp in cpu_exec, just like we do in cpu_exec_step_atomic. Performance numbers before/after: Host: AMD Opteron(tm) Processor 6376 ubuntu 17.04 ppc64 bootup+shutdown time 700 +-+--+----+------+------------+-----------+------------*--+-+ | + + + + + *B | | before ***B*** ** * | |tb lock removal ###D### *** | 600 +-+ *** +-+ | ** # | | *B* #D | | *** * ## | 500 +-+ *** ### +-+ | * *** ### | | *B* # ## | | ** * #D# | 400 +-+ ** ## +-+ | ** ### | | ** ## | | ** # ## | 300 +-+ * B* #D# +-+ | B *** ### | | * ** #### | | * *** ### | 200 +-+ B *B #D# +-+ | #B* * ## # | | #* ## | | + D##D# + + + + | 100 +-+--+----+------+------------+-----------+------------+--+-+ 1 8 16 Guest CPUs 48 64 png: https://imgur.com/HwmBHXe debian jessie aarch64 bootup+shutdown time 90 +-+--+-----+-----+------------+------------+------------+--+-+ | + + + + + + | | before ***B*** B | 80 +tb lock removal ###D### **D +-+ | **### | | **## | 70 +-+ ** # +-+ | ** ## | | ** # | 60 +-+ *B ## +-+ | ** ## | | *** #D | 50 +-+ *** ## +-+ | * ** ### | | **B* ### | 40 +-+ **** # ## +-+ | **** #D# | | ***B** ### | 30 +-+ B***B** #### +-+ | B * * # ### | | B ###D# | 20 +-+ D ##D## +-+ | D# | | + + + + + + | 10 +-+--+-----+-----+------------+------------+------------+--+-+ 1 8 16 Guest CPUs 48 64 png: https://imgur.com/iGpGFtv The gains are high for 4-8 CPUs. Beyond that point, however, unrelated lock contention significantly hurts scalability. Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Emilio G. Cota Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 34 ++------ accel/tcg/translate-all.c | 132 ++++++++++---------------------- accel/tcg/translate-all.h | 3 +- docs/devel/multi-thread-tcg.txt | 11 ++- exec.c | 26 +++---- include/exec/cpu-common.h | 2 +- include/exec/exec-all.h | 4 - include/exec/memory-internal.h | 6 +- include/exec/tb-context.h | 2 - linux-user/main.c | 3 - tcg/tcg.h | 4 +- 11 files changed, 75 insertions(+), 152 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c482008bc7..c738b7f7d6 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -212,20 +212,20 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, We only end up here when an existing TB is too long. */ cflags |= MIN(max_cycles, CF_COUNT_MASK); - tb_lock(); + mmap_lock(); tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, orig_tb->flags, cflags); tb->orig_tb = orig_tb; - tb_unlock(); + mmap_unlock(); /* execute the generated code */ trace_exec_tb_nocache(tb, tb->pc); cpu_tb_exec(cpu, tb); - tb_lock(); + mmap_lock(); tb_phys_invalidate(tb, -1); + mmap_unlock(); tcg_tb_remove(tb); - tb_unlock(); } #endif @@ -244,9 +244,7 @@ void cpu_exec_step_atomic(CPUState *cpu) tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); if (tb == NULL) { mmap_lock(); - tb_lock(); tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); - tb_unlock(); mmap_unlock(); } @@ -261,15 +259,13 @@ void cpu_exec_step_atomic(CPUState *cpu) cpu_tb_exec(cpu, tb); cc->cpu_exec_exit(cpu); } else { - /* We may have exited due to another problem here, so we need - * to reset any tb_locks we may have taken but didn't release. + /* * The mmap_lock is dropped by tb_gen_code if it runs out of * memory. */ #ifndef CONFIG_SOFTMMU tcg_debug_assert(!have_mmap_lock()); #endif - tb_lock_reset(); assert_no_pages_locked(); } @@ -398,20 +394,11 @@ static inline TranslationBlock *tb_find(CPUState *cpu, TranslationBlock *tb; target_ulong cs_base, pc; uint32_t flags; - bool acquired_tb_lock = false; tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); if (tb == NULL) { - /* mmap_lock is needed by tb_gen_code, and mmap_lock must be - * taken outside tb_lock. As system emulation is currently - * single threaded the locks are NOPs. - */ mmap_lock(); - tb_lock(); - acquired_tb_lock = true; - tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); - mmap_unlock(); /* We add the TB in the virtual pc hash table for the fast lookup */ atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); @@ -427,15 +414,8 @@ static inline TranslationBlock *tb_find(CPUState *cpu, #endif /* See if we can patch the calling TB. */ if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { - if (!acquired_tb_lock) { - tb_lock(); - acquired_tb_lock = true; - } tb_add_jump(last_tb, tb_exit, tb); } - if (acquired_tb_lock) { - tb_unlock(); - } return tb; } @@ -710,7 +690,9 @@ int cpu_exec(CPUState *cpu) g_assert(cpu == current_cpu); g_assert(cc == CPU_GET_CLASS(cpu)); #endif /* buggy compiler */ - tb_lock_reset(); +#ifndef CONFIG_SOFTMMU + tcg_debug_assert(!have_mmap_lock()); +#endif if (qemu_mutex_iothread_locked()) { qemu_mutex_unlock_iothread(); } diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index b33cd9bc98..f0c3fd4d03 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -88,13 +88,13 @@ #endif /* Access to the various translations structures need to be serialised via locks - * for consistency. This is automatic for SoftMMU based system - * emulation due to its single threaded nature. In user-mode emulation - * access to the memory related structures are protected with the - * mmap_lock. + * for consistency. + * In user-mode emulation access to the memory related structures are protected + * with mmap_lock. + * In !user-mode we use per-page locks. */ #ifdef CONFIG_SOFTMMU -#define assert_memory_lock() tcg_debug_assert(have_tb_lock) +#define assert_memory_lock() #else #define assert_memory_lock() tcg_debug_assert(have_mmap_lock()) #endif @@ -216,9 +216,6 @@ __thread TCGContext *tcg_ctx; TBContext tb_ctx; bool parallel_cpus; -/* translation block context */ -static __thread int have_tb_lock; - static void page_table_config_init(void) { uint32_t v_l1_bits; @@ -239,31 +236,6 @@ static void page_table_config_init(void) assert(v_l2_levels >= 0); } -#define assert_tb_locked() tcg_debug_assert(have_tb_lock) -#define assert_tb_unlocked() tcg_debug_assert(!have_tb_lock) - -void tb_lock(void) -{ - assert_tb_unlocked(); - qemu_mutex_lock(&tb_ctx.tb_lock); - have_tb_lock++; -} - -void tb_unlock(void) -{ - assert_tb_locked(); - have_tb_lock--; - qemu_mutex_unlock(&tb_ctx.tb_lock); -} - -void tb_lock_reset(void) -{ - if (have_tb_lock) { - qemu_mutex_unlock(&tb_ctx.tb_lock); - have_tb_lock = 0; - } -} - void cpu_gen_init(void) { tcg_context_init(&tcg_init_ctx); @@ -420,8 +392,7 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) * - fault during translation (instruction fetch) * - fault from helper (not using GETPC() macro) * - * Either way we need return early to avoid blowing up on a - * recursive tb_lock() as we can't resolve it here. + * Either way we need return early as we can't resolve it here. * * We are using unsigned arithmetic so if host_pc < * tcg_init_ctx.code_gen_buffer check_offset will wrap to way @@ -430,7 +401,6 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) check_offset = host_pc - (uintptr_t) tcg_init_ctx.code_gen_buffer; if (check_offset < tcg_init_ctx.code_gen_buffer_size) { - tb_lock(); tb = tcg_tb_lookup(host_pc); if (tb) { cpu_restore_state_from_tb(cpu, tb, host_pc, will_exit); @@ -441,7 +411,6 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) } r = true; } - tb_unlock(); } return r; @@ -1130,7 +1099,6 @@ static inline void code_gen_alloc(size_t tb_size) fprintf(stderr, "Could not allocate dynamic translator buffer\n"); exit(1); } - qemu_mutex_init(&tb_ctx.tb_lock); } static bool tb_cmp(const void *ap, const void *bp) @@ -1174,14 +1142,12 @@ void tcg_exec_init(unsigned long tb_size) /* * Allocate a new translation block. Flush the translation buffer if * too many translation blocks or too much generated code. - * - * Called with tb_lock held. */ static TranslationBlock *tb_alloc(target_ulong pc) { TranslationBlock *tb; - assert_tb_locked(); + assert_memory_lock(); tb = tcg_tb_alloc(tcg_ctx); if (unlikely(tb == NULL)) { @@ -1248,8 +1214,7 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data) /* flush all the translation blocks */ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) { - tb_lock(); - + mmap_lock(); /* If it is already been done on request of another CPU, * just retry. */ @@ -1279,7 +1244,7 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) atomic_mb_set(&tb_ctx.tb_flush_count, tb_ctx.tb_flush_count + 1); done: - tb_unlock(); + mmap_unlock(); } void tb_flush(CPUState *cpu) @@ -1313,7 +1278,7 @@ do_tb_invalidate_check(struct qht *ht, void *p, uint32_t hash, void *userp) /* verify that all the pages have correct rights for code * - * Called with tb_lock held. + * Called with mmap_lock held. */ static void tb_invalidate_check(target_ulong address) { @@ -1343,7 +1308,10 @@ static void tb_page_check(void) #endif /* CONFIG_USER_ONLY */ -/* call with @pd->lock held */ +/* + * user-mode: call with mmap_lock held + * !user-mode: call with @pd->lock held + */ static inline void tb_page_remove(PageDesc *pd, TranslationBlock *tb) { TranslationBlock *tb1; @@ -1437,7 +1405,11 @@ static inline void tb_jmp_unlink(TranslationBlock *dest) qemu_spin_unlock(&dest->jmp_lock); } -/* If @rm_from_page_list is set, call with the TB's pages' locks held */ +/* + * In user-mode, call with mmap_lock held. + * In !user-mode, if @rm_from_page_list is set, call with the TB's pages' + * locks held. + */ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) { CPUState *cpu; @@ -1445,7 +1417,7 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) uint32_t h; tb_page_addr_t phys_pc; - assert_tb_locked(); + assert_memory_lock(); /* make sure no further incoming jumps will be chained to this TB */ qemu_spin_lock(&tb->jmp_lock); @@ -1498,7 +1470,7 @@ static void tb_phys_invalidate__locked(TranslationBlock *tb) /* invalidate one TB * - * Called with tb_lock held. + * Called with mmap_lock held in user-mode. */ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) { @@ -1543,7 +1515,7 @@ static void build_page_bitmap(PageDesc *p) /* add the tb in the target page and protect it if necessary * * Called with mmap_lock held for user-mode emulation. - * Called with @p->lock held. + * Called with @p->lock held in !user-mode. */ static inline void tb_page_add(PageDesc *p, TranslationBlock *tb, unsigned int n, tb_page_addr_t page_addr) @@ -1815,10 +1787,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu, if ((pc & TARGET_PAGE_MASK) != virt_page2) { phys_page2 = get_page_addr_code(env, virt_page2); } - /* As long as consistency of the TB stuff is provided by tb_lock in user - * mode and is implicit in single-threaded softmmu emulation, no explicit - * memory barrier is required before tb_link_page() makes the TB visible - * through the physical hash table and physical page list. + /* + * No explicit memory barrier is required -- tb_link_page() makes the + * TB visible in a consistent state. */ existing_tb = tb_link_page(tb, phys_pc, phys_page2); /* if the TB already exists, discard what we just translated */ @@ -1834,8 +1805,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } /* - * Call with all @pages locked. * @p must be non-NULL. + * user-mode: call with mmap_lock held. + * !user-mode: call with all @pages locked. */ static void tb_invalidate_phys_page_range__locked(struct page_collection *pages, @@ -1920,6 +1892,7 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, page_collection_unlock(pages); /* Force execution of one insn next time. */ cpu->cflags_next_tb = 1 | curr_cflags(); + mmap_unlock(); cpu_loop_exit_noexc(cpu); } #endif @@ -1932,8 +1905,7 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, * access: the virtual CPU will exit the current TB if code is modified inside * this TB. * - * Called with tb_lock/mmap_lock held for user-mode emulation - * Called with tb_lock held for system-mode emulation + * Called with mmap_lock held for user-mode emulation */ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, int is_cpu_write_access) @@ -1942,7 +1914,6 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, PageDesc *p; assert_memory_lock(); - assert_tb_locked(); p = page_find(start >> TARGET_PAGE_BITS); if (p == NULL) { @@ -1961,14 +1932,15 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, * access: the virtual CPU will exit the current TB if code is modified inside * this TB. * - * Called with mmap_lock held for user-mode emulation, grabs tb_lock - * Called with tb_lock held for system-mode emulation + * Called with mmap_lock held for user-mode emulation. */ -static void tb_invalidate_phys_range_1(tb_page_addr_t start, tb_page_addr_t end) +void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) { struct page_collection *pages; tb_page_addr_t next; + assert_memory_lock(); + pages = page_collection_lock(start, end); for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; start < end; @@ -1984,30 +1956,16 @@ static void tb_invalidate_phys_range_1(tb_page_addr_t start, tb_page_addr_t end) page_collection_unlock(pages); } -#ifdef CONFIG_SOFTMMU -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) -{ - assert_tb_locked(); - tb_invalidate_phys_range_1(start, end); -} -#else -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) -{ - assert_memory_lock(); - tb_lock(); - tb_invalidate_phys_range_1(start, end); - tb_unlock(); -} -#endif - #ifdef CONFIG_SOFTMMU /* len must be <= 8 and start must be a multiple of len. * Called via softmmu_template.h when code areas are written to with * iothread mutex not held. + * + * Call with all @pages in the range [@start, @start + len[ locked. */ -void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) +void tb_invalidate_phys_page_fast(struct page_collection *pages, + tb_page_addr_t start, int len) { - struct page_collection *pages; PageDesc *p; #if 0 @@ -2026,7 +1984,6 @@ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) return; } - pages = page_collection_lock(start, start + len); assert_page_locked(p); if (!p->code_bitmap && ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD) { @@ -2045,7 +2002,6 @@ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) do_invalidate: tb_invalidate_phys_page_range__locked(pages, p, start, start + len, 1); } - page_collection_unlock(pages); } #else /* Called with mmap_lock held. If pc is not 0 then it indicates the @@ -2077,7 +2033,6 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) return false; } - tb_lock(); #ifdef TARGET_HAS_PRECISE_SMC if (p->first_tb && pc != 0) { current_tb = tcg_tb_lookup(pc); @@ -2110,12 +2065,9 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) if (current_tb_modified) { /* Force execution of one insn next time. */ cpu->cflags_next_tb = 1 | curr_cflags(); - /* tb_lock will be reset after cpu_loop_exit_noexc longjmps - * back into the cpu_exec loop. */ return true; } #endif - tb_unlock(); return false; } @@ -2136,18 +2088,18 @@ void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) return; } ram_addr = memory_region_get_ram_addr(mr) + addr; - tb_lock(); tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0); - tb_unlock(); rcu_read_unlock(); } #endif /* !defined(CONFIG_USER_ONLY) */ -/* Called with tb_lock held. */ +/* user-mode: call with mmap_lock held */ void tb_check_watchpoint(CPUState *cpu) { TranslationBlock *tb; + assert_memory_lock(); + tb = tcg_tb_lookup(cpu->mem_io_pc); if (tb) { /* We can use retranslation to find the PC. */ @@ -2181,7 +2133,6 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) TranslationBlock *tb; uint32_t n; - tb_lock(); tb = tcg_tb_lookup(retaddr); if (!tb) { cpu_abort(cpu, "cpu_io_recompile: could not find TB for pc=%p", @@ -2229,9 +2180,6 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) * repeating the fault, which is horribly inefficient. * Better would be to execute just this insn uncached, or generate a * second new TB. - * - * cpu_loop_exit_noexc will longjmp back to cpu_exec where the - * tb_lock gets reset. */ cpu_loop_exit_noexc(cpu); } diff --git a/accel/tcg/translate-all.h b/accel/tcg/translate-all.h index 6d1d2588b5..e6cb963d7e 100644 --- a/accel/tcg/translate-all.h +++ b/accel/tcg/translate-all.h @@ -26,7 +26,8 @@ struct page_collection *page_collection_lock(tb_page_addr_t start, tb_page_addr_t end); void page_collection_unlock(struct page_collection *set); -void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); +void tb_invalidate_phys_page_fast(struct page_collection *pages, + tb_page_addr_t start, int len); void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, int is_cpu_write_access); void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end); diff --git a/docs/devel/multi-thread-tcg.txt b/docs/devel/multi-thread-tcg.txt index df83445ccf..06530be1e9 100644 --- a/docs/devel/multi-thread-tcg.txt +++ b/docs/devel/multi-thread-tcg.txt @@ -61,6 +61,7 @@ have their block-to-block jumps patched. Global TCG State ---------------- +### User-mode emulation We need to protect the entire code generation cycle including any post generation patching of the translated code. This also implies a shared translation buffer which contains code running on all cores. Any @@ -75,9 +76,11 @@ patching. (Current solution) -Mainly as part of the linux-user work all code generation is -serialised with a tb_lock(). For the SoftMMU tb_lock() also takes the -place of mmap_lock() in linux-user. +Code generation is serialised with mmap_lock(). + +### !User-mode emulation +Each vCPU has its own TCG context and associated TCG region, thereby +requiring no locking. Translation Blocks ------------------ @@ -195,7 +198,7 @@ work as "safe work" and exiting the cpu run loop. This ensure by the time execution restarts all flush operations have completed. TLB flag updates are all done atomically and are also protected by the -tb_lock() which is used by the functions that update the TLB in bulk. +corresponding page lock. (Known limitation) diff --git a/exec.c b/exec.c index ebadc0e302..28f9bdcbf9 100644 --- a/exec.c +++ b/exec.c @@ -1031,9 +1031,7 @@ const char *parse_cpu_model(const char *cpu_model) static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { mmap_lock(); - tb_lock(); tb_invalidate_phys_page_range(pc, pc + 1, 0); - tb_unlock(); mmap_unlock(); } #else @@ -2644,21 +2642,21 @@ void memory_notdirty_write_prepare(NotDirtyInfo *ndi, ndi->ram_addr = ram_addr; ndi->mem_vaddr = mem_vaddr; ndi->size = size; - ndi->locked = false; + ndi->pages = NULL; assert(tcg_enabled()); if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) { - ndi->locked = true; - tb_lock(); - tb_invalidate_phys_page_fast(ram_addr, size); + ndi->pages = page_collection_lock(ram_addr, ram_addr + size); + tb_invalidate_phys_page_fast(ndi->pages, ram_addr, size); } } /* Called within RCU critical section. */ void memory_notdirty_write_complete(NotDirtyInfo *ndi) { - if (ndi->locked) { - tb_unlock(); + if (ndi->pages) { + page_collection_unlock(ndi->pages); + ndi->pages = NULL; } /* Set both VGA and migration bits for simplicity and to remove @@ -2745,18 +2743,16 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags) } cpu->watchpoint_hit = wp; - /* Both tb_lock and iothread_mutex will be reset when - * cpu_loop_exit or cpu_loop_exit_noexc longjmp - * back into the cpu_exec main loop. - */ - tb_lock(); + mmap_lock(); tb_check_watchpoint(cpu); if (wp->flags & BP_STOP_BEFORE_ACCESS) { cpu->exception_index = EXCP_DEBUG; + mmap_unlock(); cpu_loop_exit(cpu); } else { /* Force execution of one insn next time. */ cpu->cflags_next_tb = 1 | curr_cflags(); + mmap_unlock(); cpu_loop_exit_noexc(cpu); } } @@ -3147,9 +3143,9 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, } if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) { assert(tcg_enabled()); - tb_lock(); + mmap_lock(); tb_invalidate_phys_range(addr, addr + length); - tb_unlock(); + mmap_unlock(); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); } cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 0b58e262f3..18b40d6145 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -23,7 +23,7 @@ typedef struct CPUListState { FILE *file; } CPUListState; -/* The CPU list lock nests outside tb_lock/tb_unlock. */ +/* The CPU list lock nests outside page_(un)lock or mmap_(un)lock */ void qemu_init_cpu_list(void); void cpu_list_lock(void); void cpu_list_unlock(void); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 3c2a0efb55..25a6f28ab8 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -440,10 +440,6 @@ extern uintptr_t tci_tb_ptr; smaller than 4 bytes, so we don't worry about special-casing this. */ #define GETPC_ADJ 2 -void tb_lock(void); -void tb_unlock(void); -void tb_lock_reset(void); - #if !defined(CONFIG_USER_ONLY) && defined(CONFIG_DEBUG_TCG) void assert_no_pages_locked(void); #else diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 56c25c0ef7..bb08fa4d2f 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -49,6 +49,8 @@ void mtree_print_dispatch(fprintf_function mon, void *f, struct AddressSpaceDispatch *d, MemoryRegion *root); +struct page_collection; + /* Opaque struct for passing info from memory_notdirty_write_prepare() * to memory_notdirty_write_complete(). Callers should treat all fields * as private, with the exception of @active. @@ -60,10 +62,10 @@ void mtree_print_dispatch(fprintf_function mon, void *f, */ typedef struct { CPUState *cpu; + struct page_collection *pages; ram_addr_t ram_addr; vaddr mem_vaddr; unsigned size; - bool locked; bool active; } NotDirtyInfo; @@ -91,7 +93,7 @@ typedef struct { * * This must only be called if we are using TCG; it will assert otherwise. * - * We may take a lock in the prepare call, so callers must ensure that + * We may take locks in the prepare call, so callers must ensure that * they don't exit (via longjump or otherwise) without calling complete. * * This call must only be made inside an RCU critical section. diff --git a/include/exec/tb-context.h b/include/exec/tb-context.h index 8c9b49c98e..feb585e0a7 100644 --- a/include/exec/tb-context.h +++ b/include/exec/tb-context.h @@ -32,8 +32,6 @@ typedef struct TBContext TBContext; struct TBContext { struct qht htable; - /* any access to the tbs or the page table must use this lock */ - QemuMutex tb_lock; /* statistics */ unsigned tb_flush_count; diff --git a/linux-user/main.c b/linux-user/main.c index 78d6d3e7eb..84e9ec9335 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -120,7 +120,6 @@ void fork_start(void) { start_exclusive(); mmap_fork_start(); - qemu_mutex_lock(&tb_ctx.tb_lock); cpu_list_lock(); } @@ -136,14 +135,12 @@ void fork_end(int child) QTAILQ_REMOVE(&cpus, cpu, node); } } - qemu_mutex_init(&tb_ctx.tb_lock); qemu_init_cpu_list(); gdbserver_fork(thread_cpu); /* qemu_init_cpu_list() takes care of reinitializing the * exclusive state, so we don't need to end_exclusive() here. */ } else { - qemu_mutex_unlock(&tb_ctx.tb_lock); cpu_list_unlock(); end_exclusive(); } diff --git a/tcg/tcg.h b/tcg/tcg.h index e49b289ba1..532d2a0710 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -857,7 +857,7 @@ static inline bool tcg_op_buf_full(void) /* pool based memory allocation */ -/* user-mode: tb_lock must be held for tcg_malloc_internal. */ +/* user-mode: mmap_lock must be held for tcg_malloc_internal. */ void *tcg_malloc_internal(TCGContext *s, int size); void tcg_pool_reset(TCGContext *s); TranslationBlock *tcg_tb_alloc(TCGContext *s); @@ -875,7 +875,7 @@ TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr); void tcg_tb_foreach(GTraverseFunc func, gpointer user_data); size_t tcg_nb_tbs(void); -/* user-mode: Called with tb_lock held. */ +/* user-mode: Called with mmap_lock held. */ static inline void *tcg_malloc(int size) { TCGContext *s = tcg_ctx; From 93bc3b29fa31637f9dd062cf05bff36646070dab Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 11 Jun 2018 15:01:52 -0300 Subject: [PATCH 1323/2380] Remove COPYING.PYTHON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The COPYING.PYTHON file was added when we added the compatibility argparse.py module, which was licensed under the Python Software Foundation License Version 2. Now the compatibility argparse.py module was removed, and we are not carrying any code under that license anymore. Remove COPYING.PYTHON. Signed-off-by: Eduardo Habkost Message-Id: <20180611180152.2681-1-ehabkost@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost --- COPYING.PYTHON | 270 ------------------------------------------------- 1 file changed, 270 deletions(-) delete mode 100644 COPYING.PYTHON diff --git a/COPYING.PYTHON b/COPYING.PYTHON deleted file mode 100644 index 4d3f1ef276..0000000000 --- a/COPYING.PYTHON +++ /dev/null @@ -1,270 +0,0 @@ -A. HISTORY OF THE SOFTWARE -========================== - -Python was created in the early 1990s by Guido van Rossum at Stichting -Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands -as a successor of a language called ABC. Guido remains Python's -principal author, although it includes many contributions from others. - -In 1995, Guido continued his work on Python at the Corporation for -National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) -in Reston, Virginia where he released several versions of the -software. - -In May 2000, Guido and the Python core development team moved to -BeOpen.com to form the BeOpen PythonLabs team. In October of the same -year, the PythonLabs team moved to Digital Creations (now Zope -Corporation, see http://www.zope.com). In 2001, the Python Software -Foundation (PSF, see http://www.python.org/psf/) was formed, a -non-profit organization created specifically to own Python-related -Intellectual Property. Zope Corporation is a sponsoring member of -the PSF. - -All Python releases are Open Source (see http://www.opensource.org for -the Open Source Definition). Historically, most, but not all, Python -releases have also been GPL-compatible; the table below summarizes -the various releases. - - Release Derived Year Owner GPL- - from compatible? (1) - - 0.9.0 thru 1.2 1991-1995 CWI yes - 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes - 1.6 1.5.2 2000 CNRI no - 2.0 1.6 2000 BeOpen.com no - 1.6.1 1.6 2001 CNRI yes (2) - 2.1 2.0+1.6.1 2001 PSF no - 2.0.1 2.0+1.6.1 2001 PSF yes - 2.1.1 2.1+2.0.1 2001 PSF yes - 2.2 2.1.1 2001 PSF yes - 2.1.2 2.1.1 2002 PSF yes - 2.1.3 2.1.2 2002 PSF yes - 2.2.1 2.2 2002 PSF yes - 2.2.2 2.2.1 2002 PSF yes - 2.2.3 2.2.2 2003 PSF yes - 2.3 2.2.2 2002-2003 PSF yes - 2.3.1 2.3 2002-2003 PSF yes - 2.3.2 2.3.1 2002-2003 PSF yes - 2.3.3 2.3.2 2002-2003 PSF yes - 2.3.4 2.3.3 2004 PSF yes - 2.3.5 2.3.4 2005 PSF yes - 2.4 2.3 2004 PSF yes - 2.4.1 2.4 2005 PSF yes - 2.4.2 2.4.1 2005 PSF yes - 2.4.3 2.4.2 2006 PSF yes - 2.5 2.4 2006 PSF yes - 2.7 2.6 2010 PSF yes - -Footnotes: - -(1) GPL-compatible doesn't mean that we're distributing Python under - the GPL. All Python licenses, unlike the GPL, let you distribute - a modified version without making your changes open source. The - GPL-compatible licenses make it possible to combine Python with - other software that is released under the GPL; the others don't. - -(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, - because its license has a choice of law clause. According to - CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 - is "not incompatible" with the GPL. - -Thanks to the many outside volunteers who have worked under Guido's -direction to make these releases possible. - - -B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON -=============================================================== - -PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 --------------------------------------------- - -1. This LICENSE AGREEMENT is between the Python Software Foundation -("PSF"), and the Individual or Organization ("Licensee") accessing and -otherwise using this software ("Python") in source or binary form and -its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python -alone or in any derivative version, provided, however, that PSF's -License Agreement and PSF's notice of copyright, i.e., "Copyright (c) -2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights -Reserved" are retained in Python alone or in any derivative version -prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python. - -4. PSF is making Python available to Licensee on an "AS IS" -basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between PSF and -Licensee. This License Agreement does not grant permission to use PSF -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using Python, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 -------------------------------------------- - -BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 - -1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an -office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the -Individual or Organization ("Licensee") accessing and otherwise using -this software in source or binary form and its associated -documentation ("the Software"). - -2. Subject to the terms and conditions of this BeOpen Python License -Agreement, BeOpen hereby grants Licensee a non-exclusive, -royalty-free, world-wide license to reproduce, analyze, test, perform -and/or display publicly, prepare derivative works, distribute, and -otherwise use the Software alone or in any derivative version, -provided, however, that the BeOpen Python License is retained in the -Software, alone or in any derivative version prepared by Licensee. - -3. BeOpen is making the Software available to Licensee on an "AS IS" -basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE -SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS -AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY -DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -5. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -6. This License Agreement shall be governed by and interpreted in all -respects by the law of the State of California, excluding conflict of -law provisions. Nothing in this License Agreement shall be deemed to -create any relationship of agency, partnership, or joint venture -between BeOpen and Licensee. This License Agreement does not grant -permission to use BeOpen trademarks or trade names in a trademark -sense to endorse or promote products or services of Licensee, or any -third party. As an exception, the "BeOpen Python" logos available at -http://www.pythonlabs.com/logos.html may be used according to the -permissions granted on that web page. - -7. By copying, installing or otherwise using the software, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 ---------------------------------------- - -1. This LICENSE AGREEMENT is between the Corporation for National -Research Initiatives, having an office at 1895 Preston White Drive, -Reston, VA 20191 ("CNRI"), and the Individual or Organization -("Licensee") accessing and otherwise using Python 1.6.1 software in -source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, CNRI -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python 1.6.1 -alone or in any derivative version, provided, however, that CNRI's -License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) -1995-2001 Corporation for National Research Initiatives; All Rights -Reserved" are retained in Python 1.6.1 alone or in any derivative -version prepared by Licensee. Alternately, in lieu of CNRI's License -Agreement, Licensee may substitute the following text (omitting the -quotes): "Python 1.6.1 is made available subject to the terms and -conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the Internet using the following -unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the Internet -using the following URL: http://hdl.handle.net/1895.22/1013". - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python 1.6.1 or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python 1.6.1. - -4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" -basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. This License Agreement shall be governed by the federal -intellectual property law of the United States, including without -limitation the federal copyright law, and, to the extent such -U.S. federal law does not apply, by the law of the Commonwealth of -Virginia, excluding Virginia's conflict of law provisions. -Notwithstanding the foregoing, with regard to derivative works based -on Python 1.6.1 that incorporate non-separable material that was -previously distributed under the GNU General Public License (GPL), the -law of the Commonwealth of Virginia shall govern this License -Agreement only as to issues arising under or with respect to -Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this -License Agreement shall be deemed to create any relationship of -agency, partnership, or joint venture between CNRI and Licensee. This -License Agreement does not grant permission to use CNRI trademarks or -trade name in a trademark sense to endorse or promote products or -services of Licensee, or any third party. - -8. By clicking on the "ACCEPT" button where indicated, or by copying, -installing or otherwise using Python 1.6.1, Licensee agrees to be -bound by the terms and conditions of this License Agreement. - - ACCEPT - - -CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 --------------------------------------------------- - -Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, -The Netherlands. All rights reserved. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the name of Stichting Mathematisch -Centrum or CWI not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE -FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. From c3d7e8c90db208b1d876f8d6458c2dfca169137f Mon Sep 17 00:00:00 2001 From: Cleber Rosa Date: Wed, 30 May 2018 14:41:52 -0400 Subject: [PATCH 1324/2380] Add functional/acceptance tests infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the very minimum infrastructure necessary for writing and running functional/acceptance tests, including: * Documentation * The avocado_qemu.Test base test class * One example tests (version.py) Additional functionality is expected to be added along the tests that require them. Signed-off-by: Cleber Rosa Message-Id: <20180530184156.15634-2-crosa@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé [ehabkost: fix typo on testing.rst] Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost --- docs/devel/testing.rst | 192 ++++++++++++++++++++++ tests/acceptance/README.rst | 10 ++ tests/acceptance/avocado_qemu/__init__.py | 54 ++++++ tests/acceptance/version.py | 24 +++ 4 files changed, 280 insertions(+) create mode 100644 tests/acceptance/README.rst create mode 100644 tests/acceptance/avocado_qemu/__init__.py create mode 100644 tests/acceptance/version.py diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 0ca1a2d4b5..f33e5a8423 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -484,3 +484,195 @@ supported. To start the fuzzer, run Alternatively, some command different from "qemu-img info" can be tested, by changing the ``-c`` option. + +Acceptance tests using the Avocado Framework +============================================ + +The ``tests/acceptance`` directory hosts functional tests, also known +as acceptance level tests. They're usually higher level tests, and +may interact with external resources and with various guest operating +systems. + +These tests are written using the Avocado Testing Framework (which must +be installed separately) in conjunction with a the ``avocado_qemu.Test`` +class, implemented at ``tests/acceptance/avocado_qemu``. + +Tests based on ``avocado_qemu.Test`` can easily: + + * Customize the command line arguments given to the convenience + ``self.vm`` attribute (a QEMUMachine instance) + + * Interact with the QEMU monitor, send QMP commands and check + their results + + * Interact with the guest OS, using the convenience console device + (which may be useful to assert the effectiveness and correctness of + command line arguments or QMP commands) + + * Interact with external data files that accompany the test itself + (see ``self.get_data()``) + + * Download (and cache) remote data files, such as firmware and kernel + images + + * Have access to a library of guest OS images (by means of the + ``avocado.utils.vmimage`` library) + + * Make use of various other test related utilities available at the + test class itself and at the utility library: + + - http://avocado-framework.readthedocs.io/en/latest/api/test/avocado.html#avocado.Test + - http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html + +Installation +------------ + +To install Avocado and its dependencies, run: + +.. code:: + + pip install --user avocado-framework + +Alternatively, follow the instructions on this link: + + http://avocado-framework.readthedocs.io/en/latest/GetStartedGuide.html#installing-avocado + +Overview +-------- + +This directory provides the ``avocado_qemu`` Python module, containing +the ``avocado_qemu.Test`` class. Here's a simple usage example: + +.. code:: + + from avocado_qemu import Test + + + class Version(Test): + """ + :avocado: enable + :avocado: tags=quick + """ + def test_qmp_human_info_version(self): + self.vm.launch() + res = self.vm.command('human-monitor-command', + command_line='info version') + self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)') + +To execute your test, run: + +.. code:: + + avocado run version.py + +Tests may be classified according to a convention by using docstring +directives such as ``:avocado: tags=TAG1,TAG2``. To run all tests +in the current directory, tagged as "quick", run: + +.. code:: + + avocado run -t quick . + +The ``avocado_qemu.Test`` base test class +----------------------------------------- + +The ``avocado_qemu.Test`` class has a number of characteristics that +are worth being mentioned right away. + +First of all, it attempts to give each test a ready to use QEMUMachine +instance, available at ``self.vm``. Because many tests will tweak the +QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) +is left to the test writer. + +At test "tear down", ``avocado_qemu.Test`` handles the QEMUMachine +shutdown. + +QEMUMachine +~~~~~~~~~~~ + +The QEMUMachine API is already widely used in the Python iotests, +device-crash-test and other Python scripts. It's a wrapper around the +execution of a QEMU binary, giving its users: + + * the ability to set command line arguments to be given to the QEMU + binary + + * a ready to use QMP connection and interface, which can be used to + send commands and inspect its results, as well as asynchronous + events + + * convenience methods to set commonly used command line arguments in + a more succinct and intuitive way + +QEMU binary selection +~~~~~~~~~~~~~~~~~~~~~ + +The QEMU binary used for the ``self.vm`` QEMUMachine instance will +primarily depend on the value of the ``qemu_bin`` parameter. If it's +not explicitly set, its default value will be the result of a dynamic +probe in the same source tree. A suitable binary will be one that +targets the architecture matching host machine. + +Based on this description, test writers will usually rely on one of +the following approaches: + +1) Set ``qemu_bin``, and use the given binary + +2) Do not set ``qemu_bin``, and use a QEMU binary named like + "${arch}-softmmu/qemu-system-${arch}", either in the current + working directory, or in the current source tree. + +The resulting ``qemu_bin`` value will be preserved in the +``avocado_qemu.Test`` as an attribute with the same name. + +Attribute reference +------------------- + +Besides the attributes and methods that are part of the base +``avocado.Test`` class, the following attributes are available on any +``avocado_qemu.Test`` instance. + +vm +~~ + +A QEMUMachine instance, initially configured according to the given +``qemu_bin`` parameter. + +qemu_bin +~~~~~~~~ + +The preserved value of the ``qemu_bin`` parameter or the result of the +dynamic probe for a QEMU binary in the current working directory or +source tree. + +Parameter reference +------------------- + +To understand how Avocado parameters are accessed by tests, and how +they can be passed to tests, please refer to:: + + http://avocado-framework.readthedocs.io/en/latest/WritingTests.html#accessing-test-parameters + +Parameter values can be easily seen in the log files, and will look +like the following: + +.. code:: + + PARAMS (key=qemu_bin, path=*, default=x86_64-softmmu/qemu-system-x86_64) => 'x86_64-softmmu/qemu-system-x86_64 + +qemu_bin +~~~~~~~~ + +The exact QEMU binary to be used on QEMUMachine. + +Uninstalling Avocado +-------------------- + +If you've followed the installation instructions above, you can easily +uninstall Avocado. Start by listing the packages you have installed:: + + pip list --user + +And remove any package you want with:: + + pip uninstall diff --git a/tests/acceptance/README.rst b/tests/acceptance/README.rst new file mode 100644 index 0000000000..89260faed6 --- /dev/null +++ b/tests/acceptance/README.rst @@ -0,0 +1,10 @@ +============================================ +Acceptance tests using the Avocado Framework +============================================ + +This directory contains functional tests, also known as acceptance +level tests. They're usually higher level, and may interact with +external resources and with various guest operating systems. + +For more information, please refer to ``docs/devel/testing.rst``, +section "Acceptance tests using the Avocado Framework". diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py new file mode 100644 index 0000000000..1e54fd5932 --- /dev/null +++ b/tests/acceptance/avocado_qemu/__init__.py @@ -0,0 +1,54 @@ +# Test class and utilities for functional tests +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +import sys + +import avocado + +SRC_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) +SRC_ROOT_DIR = os.path.abspath(os.path.dirname(SRC_ROOT_DIR)) +sys.path.append(os.path.join(SRC_ROOT_DIR, 'scripts')) + +from qemu import QEMUMachine + +def is_readable_executable_file(path): + return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) + + +def pick_default_qemu_bin(): + """ + Picks the path of a QEMU binary, starting either in the current working + directory or in the source tree root directory. + """ + arch = os.uname()[4] + qemu_bin_relative_path = os.path.join("%s-softmmu" % arch, + "qemu-system-%s" % arch) + if is_readable_executable_file(qemu_bin_relative_path): + return qemu_bin_relative_path + + qemu_bin_from_src_dir_path = os.path.join(SRC_ROOT_DIR, + qemu_bin_relative_path) + if is_readable_executable_file(qemu_bin_from_src_dir_path): + return qemu_bin_from_src_dir_path + + +class Test(avocado.Test): + def setUp(self): + self.vm = None + self.qemu_bin = self.params.get('qemu_bin', + default=pick_default_qemu_bin()) + if self.qemu_bin is None: + self.cancel("No QEMU binary defined or found in the source tree") + self.vm = QEMUMachine(self.qemu_bin) + + def tearDown(self): + if self.vm is not None: + self.vm.shutdown() diff --git a/tests/acceptance/version.py b/tests/acceptance/version.py new file mode 100644 index 0000000000..13b0a7440d --- /dev/null +++ b/tests/acceptance/version.py @@ -0,0 +1,24 @@ +# Version check example test +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + + +from avocado_qemu import Test + + +class Version(Test): + """ + :avocado: enable + :avocado: tags=quick + """ + def test_qmp_human_info_version(self): + self.vm.launch() + res = self.vm.command('human-monitor-command', + command_line='info version') + self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)') From 572a82438308216fbfc615c924b6e94e4b68dfaa Mon Sep 17 00:00:00 2001 From: Cleber Rosa Date: Wed, 30 May 2018 14:41:53 -0400 Subject: [PATCH 1325/2380] scripts/qemu.py: allow adding to the list of extra arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests will often need to add extra arguments to QEMU command line arguments. Signed-off-by: Cleber Rosa Reviewed-by: Stefan Hajnoczi Message-Id: <20180530184156.15634-3-crosa@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Eduardo Habkost --- scripts/qemu.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/qemu.py b/scripts/qemu.py index 08a3e9af5a..7cd8193df8 100644 --- a/scripts/qemu.py +++ b/scripts/qemu.py @@ -359,3 +359,9 @@ class QEMUMachine(object): of the qemu process. ''' return self._iolog + + def add_args(self, *args): + ''' + Adds to the list of extra arguments to be given to the QEMU binary + ''' + self._args.extend(args) From 7b1bd11cff0915a1266c34bdfb66d70f6372340d Mon Sep 17 00:00:00 2001 From: Cleber Rosa Date: Wed, 30 May 2018 14:41:54 -0400 Subject: [PATCH 1326/2380] Acceptance tests: add quick VNC tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a few simple behavior tests for VNC. Signed-off-by: Cleber Rosa Reviewed-by: Stefan Hajnoczi Message-Id: <20180530184156.15634-4-crosa@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Eduardo Habkost --- tests/acceptance/vnc.py | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 tests/acceptance/vnc.py diff --git a/tests/acceptance/vnc.py b/tests/acceptance/vnc.py new file mode 100644 index 0000000000..b1ef9d71b1 --- /dev/null +++ b/tests/acceptance/vnc.py @@ -0,0 +1,60 @@ +# Simple functional tests for VNC functionality +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado_qemu import Test + + +class Vnc(Test): + """ + :avocado: enable + :avocado: tags=vnc,quick + """ + def test_no_vnc(self): + self.vm.add_args('-nodefaults', '-S') + self.vm.launch() + self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) + + def test_no_vnc_change_password(self): + self.vm.add_args('-nodefaults', '-S') + self.vm.launch() + self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) + set_password_response = self.vm.qmp('change', + device='vnc', + target='password', + arg='new_password') + self.assertIn('error', set_password_response) + self.assertEqual(set_password_response['error']['class'], + 'GenericError') + self.assertEqual(set_password_response['error']['desc'], + 'Could not set password') + + def test_vnc_change_password_requires_a_password(self): + self.vm.add_args('-nodefaults', '-S', '-vnc', ':0') + self.vm.launch() + self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) + set_password_response = self.vm.qmp('change', + device='vnc', + target='password', + arg='new_password') + self.assertIn('error', set_password_response) + self.assertEqual(set_password_response['error']['class'], + 'GenericError') + self.assertEqual(set_password_response['error']['desc'], + 'Could not set password') + + def test_vnc_change_password(self): + self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password') + self.vm.launch() + self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) + set_password_response = self.vm.qmp('change', + device='vnc', + target='password', + arg='new_password') + self.assertEqual(set_password_response['return'], {}) From 22dea9db2baf72a79782c748c57e2d87b06234d5 Mon Sep 17 00:00:00 2001 From: Cleber Rosa Date: Wed, 30 May 2018 14:41:55 -0400 Subject: [PATCH 1327/2380] scripts/qemu.py: introduce set_console() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The set_console() method is intended to ease higher level use cases that require a console device. The amount of intelligence is limited on purpose, requiring either the device type explicitly, or the existence of a machine (pattern) definition. Because of the console device type selection criteria (by machine type), users should also be able to define that. It'll then be used for both '-machine' and for the console device type selection. Users of the set_console() method will certainly be interested in accessing the console device, and for that a console_socket property has been added. Signed-off-by: Cleber Rosa Message-Id: <20180530184156.15634-5-crosa@redhat.com> Tested-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost --- scripts/qemu.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/scripts/qemu.py b/scripts/qemu.py index 7cd8193df8..f099ce7278 100644 --- a/scripts/qemu.py +++ b/scripts/qemu.py @@ -17,19 +17,41 @@ import logging import os import subprocess import qmp.qmp +import re import shutil +import socket import tempfile LOG = logging.getLogger(__name__) +#: Maps machine types to the preferred console device types +CONSOLE_DEV_TYPES = { + r'^clipper$': 'isa-serial', + r'^malta': 'isa-serial', + r'^(pc.*|q35.*|isapc)$': 'isa-serial', + r'^(40p|powernv|prep)$': 'isa-serial', + r'^pseries.*': 'spapr-vty', + r'^s390-ccw-virtio.*': 'sclpconsole', + } + + class QEMUMachineError(Exception): """ Exception called when an error in QEMUMachine happens. """ +class QEMUMachineAddDeviceError(QEMUMachineError): + """ + Exception raised when a request to add a device can not be fulfilled + + The failures are caused by limitations, lack of information or conflicting + requests on the QEMUMachine methods. This exception does not represent + failures reported by the QEMU binary itself. + """ + class MonitorResponseError(qmp.qmp.QMPError): ''' Represents erroneous QMP monitor reply @@ -91,6 +113,10 @@ class QEMUMachine(object): self._test_dir = test_dir self._temp_dir = None self._launched = False + self._machine = None + self._console_device_type = None + self._console_address = None + self._console_socket = None # just in case logging wasn't configured by the main script: logging.basicConfig() @@ -175,9 +201,19 @@ class QEMUMachine(object): self._monitor_address[1]) else: moncdev = 'socket,id=mon,path=%s' % self._vm_monitor - return ['-chardev', moncdev, + args = ['-chardev', moncdev, '-mon', 'chardev=mon,mode=control', '-display', 'none', '-vga', 'none'] + if self._machine is not None: + args.extend(['-machine', self._machine]) + if self._console_device_type is not None: + self._console_address = os.path.join(self._temp_dir, + self._name + "-console.sock") + chardev = ('socket,id=console,path=%s,server,nowait' % + self._console_address) + device = '%s,chardev=console' % self._console_device_type + args.extend(['-chardev', chardev, '-device', device]) + return args def _pre_launch(self): self._temp_dir = tempfile.mkdtemp(dir=self._test_dir) @@ -202,6 +238,10 @@ class QEMUMachine(object): self._qemu_log_path = None + if self._console_socket is not None: + self._console_socket.close() + self._console_socket = None + if self._temp_dir is not None: shutil.rmtree(self._temp_dir) self._temp_dir = None @@ -365,3 +405,58 @@ class QEMUMachine(object): Adds to the list of extra arguments to be given to the QEMU binary ''' self._args.extend(args) + + def set_machine(self, machine_type): + ''' + Sets the machine type + + If set, the machine type will be added to the base arguments + of the resulting QEMU command line. + ''' + self._machine = machine_type + + def set_console(self, device_type=None): + ''' + Sets the device type for a console device + + If set, the console device and a backing character device will + be added to the base arguments of the resulting QEMU command + line. + + This is a convenience method that will either use the provided + device type, of if not given, it will used the device type set + on CONSOLE_DEV_TYPES. + + The actual setting of command line arguments will be be done at + machine launch time, as it depends on the temporary directory + to be created. + + @param device_type: the device type, such as "isa-serial" + @raises: QEMUMachineAddDeviceError if the device type is not given + and can not be determined. + ''' + if device_type is None: + if self._machine is None: + raise QEMUMachineAddDeviceError("Can not add a console device:" + " QEMU instance without a " + "defined machine type") + for regex, device in CONSOLE_DEV_TYPES.items(): + if re.match(regex, self._machine): + device_type = device + break + if device_type is None: + raise QEMUMachineAddDeviceError("Can not add a console device:" + " no matching console device " + "type definition") + self._console_device_type = device_type + + @property + def console_socket(self): + """ + Returns a socket connected to the console + """ + if self._console_socket is None: + self._console_socket = socket.socket(socket.AF_UNIX, + socket.SOCK_STREAM) + self._console_socket.connect(self._console_address) + return self._console_socket From c1cc73f407b890c4e7ab5bf520c0637e0364e92a Mon Sep 17 00:00:00 2001 From: Cleber Rosa Date: Wed, 30 May 2018 14:41:56 -0400 Subject: [PATCH 1328/2380] Acceptance tests: add Linux kernel boot and console checking test This test boots a Linux kernel, and checks that the given command line was effective in two ways: * It makes the kernel use the set "console device" as a console * The kernel records the command line as expected in the console Given that way too many error conditions may occur, and detecting the kernel boot progress status may not be trivial, this test relies on a timeout to handle unexpected situations. Also, it's *not* tagged as a quick test for obvious reasons. It may be useful, while interactively running/debugging this test, or tests similar to this one, to show some of the logging channels. Example: $ avocado --show=QMP,console run boot_linux_console.py Signed-off-by: Cleber Rosa Message-Id: <20180530184156.15634-6-crosa@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Eduardo Habkost --- tests/acceptance/boot_linux_console.py | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tests/acceptance/boot_linux_console.py diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py new file mode 100644 index 0000000000..98324f7591 --- /dev/null +++ b/tests/acceptance/boot_linux_console.py @@ -0,0 +1,47 @@ +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import logging + +from avocado_qemu import Test + + +class BootLinuxConsole(Test): + """ + Boots a x86_64 Linux kernel and checks that the console is operational + and the kernel command line is properly passed from QEMU to the kernel + + :avocado: enable + :avocado: tags=x86_64 + """ + + timeout = 60 + + def test(self): + kernel_url = ('https://mirrors.kernel.org/fedora/releases/28/' + 'Everything/x86_64/os/images/pxeboot/vmlinuz') + kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + self.vm.set_machine('pc') + self.vm.set_console() + kernel_command_line = 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console = self.vm.console_socket.makefile() + console_logger = logging.getLogger('console') + while True: + msg = console.readline() + console_logger.debug(msg.strip()) + if 'Kernel command line: %s' % kernel_command_line in msg: + break + if 'Kernel panic - not syncing' in msg: + self.fail("Kernel panic reached") From 1b145d59b74a5880d82954ee940a219e73851f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Jun 2018 14:34:37 -0300 Subject: [PATCH 1329/2380] configure: Enable out-of-tree acceptance tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently to run Avocado acceptance tests in an out-of-tree build directory, we need to use the full path to the test: build_dir$ avocado run /full/path/to/sources/qemu/tests/acceptance/boot_linux_console.py This patch adds a symlink in the build tree to simplify the tests invocation, allowing the same command than in in-tree builds: build_dir$ avocado run tests/acceptance/boot_linux_console.py Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180612173437.14462-1-f4bug@amsat.org> Signed-off-by: Eduardo Habkost --- configure | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/configure b/configure index a8c4094c87..d095ed45a4 100755 --- a/configure +++ b/configure @@ -7239,9 +7239,11 @@ for rom in seabios vgabios ; do done # set up tests data directory -if [ ! -e tests/data ]; then - symlink "$source_path/tests/data" tests/data -fi +for tests_subdir in acceptance data; do + if [ ! -e tests/$tests_subdir ]; then + symlink "$source_path/tests/$tests_subdir" tests/$tests_subdir + fi +done # set up qemu-iotests in this build directory iotests_common_env="tests/qemu-iotests/common.env" From 9f754620651d3432114f4bb89c7f12cbea814b3e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Jun 2018 19:57:03 -1000 Subject: [PATCH 1330/2380] tcg: Reduce max TB opcode count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, assert that we don't overflow any of two different offsets into the TB. Both unwind and goto_tb both record a uint16_t for later use. This fixes an arm-softmmu test case utilizing NEON in which there is a TB generated that runs to 7800 opcodes, and compiles to 96k on an x86_64 host. This overflows the 16-bit offset in which we record the goto_tb reset offset. Because of that overflow, we install a jump destination that goes to neverland. Boom. With this reduced op count, the same TB compiles to about 48k for aarch64, ppc64le, and x86_64 hosts, and neither assertion fires. Cc: qemu-stable@nongnu.org Reported-by: "Jason A. Donenfeld" Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.inc.c | 2 +- tcg/arm/tcg-target.inc.c | 2 +- tcg/i386/tcg-target.inc.c | 2 +- tcg/mips/tcg-target.inc.c | 2 +- tcg/ppc/tcg-target.inc.c | 4 ++-- tcg/s390/tcg-target.inc.c | 2 +- tcg/sparc/tcg-target.inc.c | 4 ++-- tcg/tcg.c | 13 ++++++++++++- tcg/tcg.h | 6 ++++-- tcg/tci/tcg-target.inc.c | 2 +- 10 files changed, 26 insertions(+), 13 deletions(-) diff --git a/tcg/aarch64/tcg-target.inc.c b/tcg/aarch64/tcg-target.inc.c index be3192078d..4562d36d1b 100644 --- a/tcg/aarch64/tcg-target.inc.c +++ b/tcg/aarch64/tcg-target.inc.c @@ -1733,7 +1733,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_insn(s, 3305, LDR, offset, TCG_REG_TMP); } tcg_out_insn(s, 3207, BR, TCG_REG_TMP); - s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s); + set_jmp_reset_offset(s, a0); break; case INDEX_op_goto_ptr: diff --git a/tcg/arm/tcg-target.inc.c b/tcg/arm/tcg-target.inc.c index 56a32a470f..e1fbf465cb 100644 --- a/tcg/arm/tcg-target.inc.c +++ b/tcg/arm/tcg-target.inc.c @@ -1822,7 +1822,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_movi32(s, COND_AL, base, ptr - dil); } tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, base, dil); - s->tb_jmp_reset_offset[args[0]] = tcg_current_code_size(s); + set_jmp_reset_offset(s, args[0]); } break; case INDEX_op_goto_ptr: diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index 0d0ff524b7..e87b0d445e 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -2245,7 +2245,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, -1, (intptr_t)(s->tb_jmp_target_addr + a0)); } - s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s); + set_jmp_reset_offset(s, a0); break; case INDEX_op_goto_ptr: /* jmp to the given host address (could be epilogue) */ diff --git a/tcg/mips/tcg-target.inc.c b/tcg/mips/tcg-target.inc.c index ca5f1d4894..cff525373b 100644 --- a/tcg/mips/tcg-target.inc.c +++ b/tcg/mips/tcg-target.inc.c @@ -1744,7 +1744,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0); } tcg_out_nop(s); - s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s); + set_jmp_reset_offset(s, a0); break; case INDEX_op_goto_ptr: /* jmp to the given host address (could be epilogue) */ diff --git a/tcg/ppc/tcg-target.inc.c b/tcg/ppc/tcg-target.inc.c index 86f7de5f7e..c2f729ee8f 100644 --- a/tcg/ppc/tcg-target.inc.c +++ b/tcg/ppc/tcg-target.inc.c @@ -2025,10 +2025,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, } tcg_out32(s, MTSPR | RS(TCG_REG_TB) | CTR); tcg_out32(s, BCCTR | BO_ALWAYS); - s->tb_jmp_reset_offset[args[0]] = c = tcg_current_code_size(s); + set_jmp_reset_offset(s, args[0]); if (USE_REG_TB) { /* For the unlinked case, need to reset TCG_REG_TB. */ - c = -c; + c = -tcg_current_code_size(s); assert(c == (int16_t)c); tcg_out32(s, ADDI | TAI(TCG_REG_TB, TCG_REG_TB, c)); } diff --git a/tcg/s390/tcg-target.inc.c b/tcg/s390/tcg-target.inc.c index 9af6dcef05..17c435ade5 100644 --- a/tcg/s390/tcg-target.inc.c +++ b/tcg/s390/tcg-target.inc.c @@ -1783,7 +1783,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, /* and go there */ tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_TB); } - s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s); + set_jmp_reset_offset(s, a0); /* For the unlinked path of goto_tb, we need to reset TCG_REG_TB to the beginning of this TB. */ diff --git a/tcg/sparc/tcg-target.inc.c b/tcg/sparc/tcg-target.inc.c index bc673bd8c6..04bdc3df5e 100644 --- a/tcg/sparc/tcg-target.inc.c +++ b/tcg/sparc/tcg-target.inc.c @@ -1388,12 +1388,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_arithi(s, TCG_REG_G0, TCG_REG_TB, 0, JMPL); tcg_out_nop(s); } - s->tb_jmp_reset_offset[a0] = c = tcg_current_code_size(s); + set_jmp_reset_offset(s, a0); /* For the unlinked path of goto_tb, we need to reset TCG_REG_TB to the beginning of this TB. */ if (USE_REG_TB) { - c = -c; + c = -tcg_current_code_size(s); if (check_fit_i32(c, 13)) { tcg_out_arithi(s, TCG_REG_TB, TCG_REG_TB, c, ARITH_ADD); } else { diff --git a/tcg/tcg.c b/tcg/tcg.c index 1d1dfd7f7c..f27b22bd3c 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -306,6 +306,14 @@ TCGLabel *gen_new_label(void) return l; } +static void set_jmp_reset_offset(TCGContext *s, int which) +{ + size_t off = tcg_current_code_size(s); + s->tb_jmp_reset_offset[which] = off; + /* Make sure that we didn't overflow the stored offset. */ + assert(s->tb_jmp_reset_offset[which] == off); +} + #include "tcg-target.inc.c" /* compare a pointer @ptr and a tb_tc @s */ @@ -3532,7 +3540,10 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) break; case INDEX_op_insn_start: if (num_insns >= 0) { - s->gen_insn_end_off[num_insns] = tcg_current_code_size(s); + size_t off = tcg_current_code_size(s); + s->gen_insn_end_off[num_insns] = off; + /* Assert that we do not overflow our stored offset. */ + assert(s->gen_insn_end_off[num_insns] == off); } num_insns++; for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { diff --git a/tcg/tcg.h b/tcg/tcg.h index 532d2a0710..f9f12378e9 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -850,9 +850,11 @@ static inline bool tcg_op_buf_full(void) /* This is not a hard limit, it merely stops translation when * we have produced "enough" opcodes. We want to limit TB size * such that a RISC host can reasonably use a 16-bit signed - * branch within the TB. + * branch within the TB. We also need to be mindful of the + * 16-bit unsigned offsets, TranslationBlock.jmp_reset_offset[] + * and TCGContext.gen_insn_end_off[]. */ - return tcg_ctx->nb_ops >= 8000; + return tcg_ctx->nb_ops >= 4000; } /* pool based memory allocation */ diff --git a/tcg/tci/tcg-target.inc.c b/tcg/tci/tcg-target.inc.c index cc949bea85..62ed097254 100644 --- a/tcg/tci/tcg-target.inc.c +++ b/tcg/tci/tcg-target.inc.c @@ -574,7 +574,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, /* Indirect jump method. */ TODO(); } - s->tb_jmp_reset_offset[args[0]] = tcg_current_code_size(s); + set_jmp_reset_offset(s, args[0]); break; case INDEX_op_br: tci_out_label(s, arg_label(args[0])); From 072f416a53ead5211c987cb2068ee9dbd7ba06cc Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Tue, 12 Jun 2018 15:16:29 +1000 Subject: [PATCH 1331/2380] target/ppc: Don't require private l1d cache on POWER8 for cap_ppc_safe_cache For cap_ppc_safe_cache to be set to workaround, we require both a l1d cache flush instruction and private l1d cache. On POWER8 don't require private l1d cache. This means a guest on a POWER8 machine can make use of the cache flush workarounds. Signed-off-by: Suraj Jitindar Singh Signed-off-by: David Gibson --- target/ppc/kvm.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 2c0c34e125..7fe9d0126b 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2412,11 +2412,28 @@ bool kvmppc_has_cap_mmu_hash_v3(void) return cap_mmu_hash_v3; } +static bool kvmppc_power8_host(void) +{ + bool ret = false; +#ifdef TARGET_PPC64 + { + uint32_t base_pvr = CPU_POWERPC_POWER_SERVER_MASK & mfpvr(); + ret = (base_pvr == CPU_POWERPC_POWER8E_BASE) || + (base_pvr == CPU_POWERPC_POWER8NVL_BASE) || + (base_pvr == CPU_POWERPC_POWER8_BASE); + } +#endif /* TARGET_PPC64 */ + return ret; +} + static int parse_cap_ppc_safe_cache(struct kvm_ppc_cpu_char c) { + bool l1d_thread_priv_req = !kvmppc_power8_host(); + if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_L1D_FLUSH_PR) { return 2; - } else if ((c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && + } else if ((!l1d_thread_priv_req || + c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && (c.character & c.character_mask & (H_CPU_CHAR_L1D_FLUSH_ORI30 | H_CPU_CHAR_L1D_FLUSH_TRIG2))) { return 1; From b2540203bdf4a390c3489146eae82ce237303653 Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Tue, 12 Jun 2018 15:16:30 +1000 Subject: [PATCH 1332/2380] ppc/spapr_caps: Don't disable cap_cfpc on POWER8 by default In default_caps_with_cpu() we set spapr_cap_cfpc to broken for POWER8 processors and before. Since we no longer require private l1d cache on POWER8 for this cap to be set to workaround change this to default to broken for POWER7 processors and before. Signed-off-by: Suraj Jitindar Singh Reviewed-by: David Gibson Signed-off-by: David Gibson --- hw/ppc/spapr_caps.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 531e145114..00e43a9ba7 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -335,14 +335,10 @@ static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, caps = smc->default_caps; - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, - 0, spapr->max_compat_pvr)) { - caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; - } - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_07, 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; + caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; } if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06_PLUS, From e493786c9585d3f1d1a951c46c656821a47f5b34 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 12 Jun 2018 18:27:54 +0200 Subject: [PATCH 1333/2380] target/ppc: drop empty #if/#endif block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 9d6f106552fa moved the last line in this block to somewhere else, but it forgot to remove the now useless #if/#endif. Signed-off-by: Greg Kurz Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- target/ppc/cpu.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 0247c1f04c..a91f1a8777 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1300,8 +1300,6 @@ void ppc_store_ptcr(CPUPPCState *env, target_ulong value); void ppc_store_msr (CPUPPCState *env, target_ulong value); void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf); -#if defined(TARGET_PPC64) -#endif /* Time-base and decrementer management */ #ifndef NO_CPU_IO_DEFS From 2c9dfdacc5a9f4db941c8f80597abae4658954ac Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 12 Jun 2018 19:01:26 +0200 Subject: [PATCH 1334/2380] spapr: fix leak in h_client_architecture_support() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the negotiated compat mode can't be set, but raw mode is supported, we decide to ignore the error. An so, we should free it to prevent a memory leak. Signed-off-by: Greg Kurz Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/ppc/spapr_hcall.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 022f6d8101..8b9a4b577f 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1547,6 +1547,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, error_report_err(local_err); return H_HARDWARE; } + error_free(local_err); local_err = NULL; } } From 06fe3a5bf107e93a4478e9cb5ef306ca65a0dcc1 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:43:56 +0100 Subject: [PATCH 1335/2380] ppc: introduce Core99MachinesState for the mac99 machine This is in preparation for adding configuration controlled via machine options. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/ppc/mac.h | 11 +++++++++++ hw/ppc/mac_newworld.c | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 89fa8bbed7..8046cd8a2f 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -27,6 +27,7 @@ #define PPC_MAC_H #include "exec/memory.h" +#include "hw/boards.h" #include "hw/sysbus.h" #include "hw/ide/internal.h" #include "hw/input/adb.h" @@ -65,6 +66,16 @@ #define NEWWORLD_IDE1_IRQ 0xe #define NEWWORLD_IDE1_DMA_IRQ 0x3 +/* Core99 machine */ +#define TYPE_CORE99_MACHINE MACHINE_TYPE_NAME("mac99") +#define CORE99_MACHINE(obj) OBJECT_CHECK(Core99MachineState, (obj), \ + TYPE_CORE99_MACHINE) + +typedef struct Core99MachineState { + /*< private >*/ + MachineState parent; +} Core99MachineState; + /* MacIO */ #define TYPE_MACIO_IDE "macio-ide" #define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 744acdfd2e..5331aa002c 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -515,10 +515,17 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) #endif } +static void core99_instance_init(Object *obj) +{ + return; +} + static const TypeInfo core99_machine_info = { .name = MACHINE_TYPE_NAME("mac99"), .parent = TYPE_MACHINE, .class_init = core99_machine_class_init, + .instance_init = core99_instance_init, + .instance_size = sizeof(Core99MachineState) }; static void mac_machine_register_types(void) From f1114c17eeda907d0a290f25f970d695fc3b16de Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:43:57 +0100 Subject: [PATCH 1336/2380] mac_newworld: add via machine option to control mac99 VIA/ADB configuration This option allows the VIA configuration to be controlled between 3 different possible setups: cuda, pmu-adb and pmu with USB rather than ADB keyboard/mouse. For the moment we don't do anything with the configuration except to pass it to the macio device (the via-cuda parent) and also to the firmware via the fw_cfg interface so that it can present the correct device tree. The default is cuda which is the current default and so will have no change in behaviour. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/macio.c | 7 ++++ hw/ppc/mac.h | 6 +++ hw/ppc/mac_newworld.c | 69 +++++++++++++++++++++++++++++++---- include/hw/misc/macio/macio.h | 2 + include/hw/ppc/ppc.h | 1 + 5 files changed, 78 insertions(+), 7 deletions(-) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index f9a40eea81..dddf743bcb 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -399,6 +399,12 @@ static const VMStateDescription vmstate_macio_newworld = { } }; +static Property macio_newworld_properties[] = { + DEFINE_PROP_BOOL("has-pmu", NewWorldMacIOState, has_pmu, false), + DEFINE_PROP_BOOL("has-adb", NewWorldMacIOState, has_adb, false), + DEFINE_PROP_END_OF_LIST() +}; + static void macio_newworld_class_init(ObjectClass *oc, void *data) { PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); @@ -407,6 +413,7 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data) pdc->realize = macio_newworld_realize; pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; dc->vmsd = &vmstate_macio_newworld; + dc->props = macio_newworld_properties; } static Property macio_properties[] = { diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 8046cd8a2f..4c08f52b87 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -71,9 +71,15 @@ #define CORE99_MACHINE(obj) OBJECT_CHECK(Core99MachineState, (obj), \ TYPE_CORE99_MACHINE) +#define CORE99_VIA_CONFIG_CUDA 0x0 +#define CORE99_VIA_CONFIG_PMU 0x1 +#define CORE99_VIA_CONFIG_PMU_ADB 0x2 + typedef struct Core99MachineState { /*< private >*/ MachineState parent; + + uint8_t via_config; } Core99MachineState; /* MacIO */ diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 5331aa002c..ca21d47234 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -111,6 +111,7 @@ static void ppc_core99_init(MachineState *machine) const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; const char *boot_device = machine->boot_order; + Core99MachineState *core99_machine = CORE99_MACHINE(machine); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; @@ -122,6 +123,7 @@ static void ppc_core99_init(MachineState *machine) UNINHostState *uninorth_pci; PCIBus *pci_bus; NewWorldMacIOState *macio; + bool has_pmu, has_adb; MACIOIDEState *macio_ide; BusState *adb_bus; MacIONVRAMState *nvr; @@ -361,6 +363,9 @@ static void ppc_core99_init(MachineState *machine) } machine->usb |= defaults_enabled() && !machine->usb_disabled; + has_pmu = (core99_machine->via_config != CORE99_VIA_CONFIG_CUDA); + has_adb = (core99_machine->via_config == CORE99_VIA_CONFIG_CUDA || + core99_machine->via_config == CORE99_VIA_CONFIG_PMU_ADB); /* Timebase Frequency */ if (kvm_enabled()) { @@ -376,6 +381,8 @@ static void ppc_core99_init(MachineState *machine) macio = NEWWORLD_MACIO(pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO)); dev = DEVICE(macio); qdev_prop_set_uint64(dev, "frequency", tbfreq); + qdev_prop_set_bit(dev, "has-pmu", has_pmu); + qdev_prop_set_bit(dev, "has-adb", has_adb); object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", &error_abort); qdev_init_nofail(dev); @@ -391,19 +398,21 @@ static void ppc_core99_init(MachineState *machine) "ide[1]")); macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); - adb_bus = qdev_get_child_bus(dev, "adb.0"); - dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); - qdev_init_nofail(dev); - dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); - qdev_init_nofail(dev); + if (has_adb) { + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + adb_bus = qdev_get_child_bus(dev, "adb.0"); + dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_init_nofail(dev); + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_init_nofail(dev); + } if (machine->usb) { pci_create_simple(pci_bus, -1, "pci-ohci"); /* U3 needs to use USB for input because Linux doesn't support via-cuda on PPC64 */ - if (machine_arch == ARCH_MAC99_U3) { + if (!has_adb || machine_arch == ARCH_MAC99_U3) { USBBus *usb_bus = usb_bus_find(-1); usb_create_simple(usb_bus, "usb-kbd"); @@ -459,6 +468,8 @@ static void ppc_core99_init(MachineState *machine) fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_VIACONFIG, core99_machine->via_config); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); if (kvm_enabled()) { #ifdef CONFIG_KVM @@ -515,8 +526,52 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) #endif } +static char *core99_get_via_config(Object *obj, Error **errp) +{ + Core99MachineState *cms = CORE99_MACHINE(obj); + + switch (cms->via_config) { + default: + case CORE99_VIA_CONFIG_CUDA: + return g_strdup("cuda"); + + case CORE99_VIA_CONFIG_PMU: + return g_strdup("pmu"); + + case CORE99_VIA_CONFIG_PMU_ADB: + return g_strdup("pmu-adb"); + } +} + +static void core99_set_via_config(Object *obj, const char *value, Error **errp) +{ + Core99MachineState *cms = CORE99_MACHINE(obj); + + if (!strcmp(value, "cuda")) { + cms->via_config = CORE99_VIA_CONFIG_CUDA; + } else if (!strcmp(value, "pmu")) { + cms->via_config = CORE99_VIA_CONFIG_PMU; + } else if (!strcmp(value, "pmu-adb")) { + cms->via_config = CORE99_VIA_CONFIG_PMU_ADB; + } else { + error_setg(errp, "Invalid via value"); + error_append_hint(errp, "Valid values are cuda, pmu, pmu-adb.\n"); + } +} + static void core99_instance_init(Object *obj) { + Core99MachineState *cms = CORE99_MACHINE(obj); + + /* Default via_config is CORE99_VIA_CONFIG_CUDA */ + cms->via_config = CORE99_VIA_CONFIG_CUDA; + object_property_add_str(obj, "via", core99_get_via_config, + core99_set_via_config, NULL); + object_property_set_description(obj, "via", + "Set VIA configuration. " + "Valid values are cuda, pmu and pmu-adb", + NULL); + return; } diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 838eaf1db0..9529073ba8 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -70,6 +70,8 @@ typedef struct NewWorldMacIOState { MacIOState parent_obj; /*< public >*/ + bool has_pmu; + bool has_adb; OpenPICState *pic; MACIOIDEState ide[2]; } NewWorldMacIOState; diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index b18ef3eefb..298ec354a8 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -101,6 +101,7 @@ enum { #define FW_CFG_PPC_NVRAM_ADDR (FW_CFG_ARCH_LOCAL + 0x08) #define FW_CFG_PPC_BUSFREQ (FW_CFG_ARCH_LOCAL + 0x09) #define FW_CFG_PPC_NVRAM_FLAT (FW_CFG_ARCH_LOCAL + 0x0a) +#define FW_CFG_PPC_VIACONFIG (FW_CFG_ARCH_LOCAL + 0x0b) #define PPC_SERIAL_MM_BAUDBASE 399193 From 7c4166a971b54a65900d9624ccd9669ba99d75ad Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:43:58 +0100 Subject: [PATCH 1337/2380] mac_newworld: add gpios to macio devices with PMU enabled PMU-enabled New World Macs expose their GPIOs via a separate memory region within the macio device. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- default-configs/ppc-softmmu.mak | 1 + hw/misc/macio/Makefile.objs | 1 + hw/misc/macio/gpio.c | 218 ++++++++++++++++++++++++++++++++ hw/misc/macio/macio.c | 13 ++ hw/misc/macio/trace-events | 7 + hw/ppc/mac.h | 2 + include/hw/misc/macio/gpio.h | 47 +++++++ include/hw/misc/macio/macio.h | 3 + 8 files changed, 292 insertions(+) create mode 100644 hw/misc/macio/gpio.c create mode 100644 include/hw/misc/macio/gpio.h diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 4d7be45ac5..38197e39eb 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -31,6 +31,7 @@ CONFIG_I2C=y CONFIG_MAC=y CONFIG_ESCC=y CONFIG_MACIO=y +CONFIG_MACIO_GPIO=y CONFIG_SUNGEM=y CONFIG_MOS6522=y CONFIG_CUDA=y diff --git a/hw/misc/macio/Makefile.objs b/hw/misc/macio/Makefile.objs index ef7ac249ec..fb9dbf91b5 100644 --- a/hw/misc/macio/Makefile.objs +++ b/hw/misc/macio/Makefile.objs @@ -1,3 +1,4 @@ common-obj-y += macio.o common-obj-$(CONFIG_CUDA) += cuda.o common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o +common-obj-$(CONFIG_MACIO_GPIO) += gpio.o diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c new file mode 100644 index 0000000000..5630afdf18 --- /dev/null +++ b/hw/misc/macio/gpio.c @@ -0,0 +1,218 @@ +/* + * PowerMac NewWorld MacIO GPIO emulation + * + * Copyright (c) 2016 Benjamin Herrenschmidt + * Copyright (c) 2018 Mark Cave-Ayland + * + * 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 "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/ppc/mac.h" +#include "hw/misc/macio/macio.h" +#include "hw/misc/macio/gpio.h" +#include "qemu/log.h" +#include "trace.h" + + +void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state) +{ + uint8_t new_reg; + + trace_macio_set_gpio(gpio, state); + + if (s->gpio_regs[gpio] & 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "GPIO: Setting GPIO %d while it's an output\n", gpio); + } + + new_reg = s->gpio_regs[gpio] & ~2; + if (state) { + new_reg |= 2; + } + + if (new_reg == s->gpio_regs[gpio]) { + return; + } + + s->gpio_regs[gpio] = new_reg; + + /* This is will work until we fix the binding between MacIO and + * the MPIC properly so we can route all GPIOs and avoid going + * via the top level platform code. + * + * Note that we probably need to get access to the MPIC config to + * decode polarity since qemu always use "raise" regardless. + * + * For now, we hard wire known GPIOs + */ + + switch (gpio) { + case 1: + /* Level low */ + if (!state) { + trace_macio_gpio_irq_assert(gpio); + qemu_irq_raise(s->gpio_extirqs[gpio]); + } else { + trace_macio_gpio_irq_deassert(gpio); + qemu_irq_lower(s->gpio_extirqs[gpio]); + } + break; + + case 9: + /* Edge, triggered by NMI below */ + if (state) { + trace_macio_gpio_irq_assert(gpio); + qemu_irq_raise(s->gpio_extirqs[gpio]); + } else { + trace_macio_gpio_irq_deassert(gpio); + qemu_irq_lower(s->gpio_extirqs[gpio]); + } + break; + + default: + qemu_log_mask(LOG_UNIMP, "GPIO: setting unimplemented GPIO %d", gpio); + } +} + +static void macio_gpio_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + MacIOGPIOState *s = opaque; + uint8_t ibit; + + trace_macio_gpio_write(addr, value); + + /* Levels regs are read-only */ + if (addr < 8) { + return; + } + + addr -= 8; + if (addr < 36) { + value &= ~2; + + if (value & 4) { + ibit = (value & 1) << 1; + } else { + ibit = s->gpio_regs[addr] & 2; + } + + s->gpio_regs[addr] = value | ibit; + } +} + +static uint64_t macio_gpio_read(void *opaque, hwaddr addr, unsigned size) +{ + MacIOGPIOState *s = opaque; + uint64_t val = 0; + + /* Levels regs */ + if (addr < 8) { + val = s->gpio_levels[addr]; + } else { + addr -= 8; + + if (addr < 36) { + val = s->gpio_regs[addr]; + } + } + + trace_macio_gpio_write(addr, val); + return val; +} + +static const MemoryRegionOps macio_gpio_ops = { + .read = macio_gpio_read, + .write = macio_gpio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static void macio_gpio_realize(DeviceState *dev, Error **errp) +{ + MacIOGPIOState *s = MACIO_GPIO(dev); + + s->gpio_extirqs[1] = qdev_get_gpio_in(DEVICE(s->pic), + NEWWORLD_EXTING_GPIO1); + s->gpio_extirqs[9] = qdev_get_gpio_in(DEVICE(s->pic), + NEWWORLD_EXTING_GPIO9); +} + +static void macio_gpio_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MacIOGPIOState *s = MACIO_GPIO(obj); + + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + memory_region_init_io(&s->gpiomem, OBJECT(s), &macio_gpio_ops, obj, + "gpio", 0x30); + sysbus_init_mmio(sbd, &s->gpiomem); +} + +static const VMStateDescription vmstate_macio_gpio = { + .name = "macio_gpio", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(gpio_levels, MacIOGPIOState, 8), + VMSTATE_UINT8_ARRAY(gpio_regs, MacIOGPIOState, 36), + VMSTATE_END_OF_LIST() + } +}; + +static void macio_gpio_reset(DeviceState *dev) +{ + MacIOGPIOState *s = MACIO_GPIO(dev); + + /* GPIO 1 is up by default */ + macio_set_gpio(s, 1, true); +} + +static void macio_gpio_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = macio_gpio_realize; + dc->reset = macio_gpio_reset; + dc->vmsd = &vmstate_macio_gpio; +} + +static const TypeInfo macio_gpio_init_info = { + .name = TYPE_MACIO_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MacIOGPIOState), + .instance_init = macio_gpio_init, + .class_init = macio_gpio_class_init, +}; + +static void macio_gpio_register_types(void) +{ + type_register_static(&macio_gpio_init_info); +} + +type_init(macio_gpio_register_types) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index dddf743bcb..8dfcbc3d9b 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -332,6 +332,16 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer", 0x1000); memory_region_add_subregion(&s->bar, 0x15000, timer_memory); + + if (ns->has_pmu) { + /* GPIOs */ + sysbus_dev = SYS_BUS_DEVICE(&ns->gpio); + object_property_set_link(OBJECT(&ns->gpio), OBJECT(pic_dev), "pic", + &error_abort); + memory_region_add_subregion(&s->bar, 0x50, + sysbus_mmio_get_region(sysbus_dev, 0)); + object_property_set_bool(OBJECT(&ns->gpio), true, "realized", &err); + } } static void macio_newworld_init(Object *obj) @@ -345,6 +355,9 @@ static void macio_newworld_init(Object *obj) qdev_prop_allow_set_link_before_realize, 0, NULL); + object_initialize(&ns->gpio, sizeof(ns->gpio), TYPE_MACIO_GPIO); + qdev_set_parent_bus(DEVICE(&ns->gpio), sysbus_get_default()); + for (i = 0; i < 2; i++) { macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i); } diff --git a/hw/misc/macio/trace-events b/hw/misc/macio/trace-events index d499d78c99..71c47520eb 100644 --- a/hw/misc/macio/trace-events +++ b/hw/misc/macio/trace-events @@ -13,3 +13,10 @@ cuda_packet_send_data(int i, const uint8_t data) "[%d] 0x%02x" # hw/misc/macio/macio.c macio_timer_write(uint64_t addr, unsigned len, uint64_t val) "write addr 0x%"PRIx64 " len %d val 0x%"PRIx64 macio_timer_read(uint64_t addr, unsigned len, uint32_t val) "read addr 0x%"PRIx64 " len %d val 0x%"PRIx32 + +# hw/misc/macio/gpio.c +macio_set_gpio(int gpio, bool state) "setting GPIO %d to %d" +macio_gpio_irq_assert(int gpio) "asserting GPIO %d" +macio_gpio_irq_deassert(int gpio) "deasserting GPIO %d" +macio_gpio_write(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 +macio_gpio_read(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 4c08f52b87..b3b7f9d8ae 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -65,6 +65,8 @@ #define NEWWORLD_IDE0_DMA_IRQ 0x2 #define NEWWORLD_IDE1_IRQ 0xe #define NEWWORLD_IDE1_DMA_IRQ 0x3 +#define NEWWORLD_EXTING_GPIO1 0x2f +#define NEWWORLD_EXTING_GPIO9 0x37 /* Core99 machine */ #define TYPE_CORE99_MACHINE MACHINE_TYPE_NAME("mac99") diff --git a/include/hw/misc/macio/gpio.h b/include/hw/misc/macio/gpio.h new file mode 100644 index 0000000000..2838ae5fde --- /dev/null +++ b/include/hw/misc/macio/gpio.h @@ -0,0 +1,47 @@ +/* + * PowerMac NewWorld MacIO GPIO emulation + * + * Copyright (c) 2016 Benjamin Herrenschmidt + * Copyright (c) 2018 Mark Cave-Ayland + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MACIO_GPIO_H +#define MACIO_GPIO_H + +#define TYPE_MACIO_GPIO "macio-gpio" +#define MACIO_GPIO(obj) OBJECT_CHECK(MacIOGPIOState, (obj), TYPE_MACIO_GPIO) + +typedef struct MacIOGPIOState { + /*< private >*/ + SysBusDevice parent; + /*< public >*/ + + OpenPICState *pic; + + MemoryRegion gpiomem; + qemu_irq gpio_extirqs[10]; + uint8_t gpio_levels[8]; + uint8_t gpio_regs[36]; /* XXX Check count */ +} MacIOGPIOState; + +void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state); + +#endif diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 9529073ba8..d43883a893 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -26,8 +26,10 @@ #ifndef MACIO_H #define MACIO_H +#include "hw/char/escc.h" #include "hw/intc/heathrow_pic.h" #include "hw/misc/macio/cuda.h" +#include "hw/misc/macio/gpio.h" #include "hw/ppc/mac_dbdma.h" #include "hw/ppc/openpic.h" @@ -74,6 +76,7 @@ typedef struct NewWorldMacIOState { bool has_adb; OpenPICState *pic; MACIOIDEState ide[2]; + MacIOGPIOState gpio; } NewWorldMacIOState; #endif /* MACIO_H */ From 8f55ac13049f3c737373d9de8598a2a03e6a03f9 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:43:59 +0100 Subject: [PATCH 1338/2380] mac_newworld: wire up programmer switch to NMI handler The programmer switch is wired up via an external GPIO pin and can be used to aid debugging Mac guests. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/gpio.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index 5630afdf18..9317df759c 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -28,6 +28,7 @@ #include "hw/ppc/mac.h" #include "hw/misc/macio/macio.h" #include "hw/misc/macio/gpio.h" +#include "hw/nmi.h" #include "qemu/log.h" #include "trace.h" @@ -193,13 +194,21 @@ static void macio_gpio_reset(DeviceState *dev) macio_set_gpio(s, 1, true); } +static void macio_gpio_nmi(NMIState *n, int cpu_index, Error **errp) +{ + macio_set_gpio(MACIO_GPIO(n), 9, true); + macio_set_gpio(MACIO_GPIO(n), 9, false); +} + static void macio_gpio_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); dc->realize = macio_gpio_realize; dc->reset = macio_gpio_reset; dc->vmsd = &vmstate_macio_gpio; + nc->nmi_monitor_handler = macio_gpio_nmi; } static const TypeInfo macio_gpio_init_info = { @@ -208,6 +217,10 @@ static const TypeInfo macio_gpio_init_info = { .instance_size = sizeof(MacIOGPIOState), .instance_init = macio_gpio_init, .class_init = macio_gpio_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, }; static void macio_gpio_register_types(void) From fb6649f172d6ea1a8d8980b7f93d31808eb06ff8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:44:00 +0100 Subject: [PATCH 1339/2380] adb: fix read reg 3 byte ordering According to the Apple ADB documentation, register 3 is a 2-byte register with the device address in the first byte, and the handler ID in the second byte. This is currently the opposite away to which QEMU returns them so switch the order around. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/input/adb-kbd.c | 4 ++-- hw/input/adb-mouse.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index 50b62712c8..0ad384dc89 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -290,8 +290,8 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, olen = 2; break; case 3: - obuf[0] = d->handler; - obuf[1] = d->devaddr; + obuf[0] = d->devaddr; + obuf[1] = d->handler; olen = 2; break; } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index 3ba6027d33..473045fbac 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -172,8 +172,8 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, case 1: break; case 3: - obuf[0] = d->handler; - obuf[1] = d->devaddr; + obuf[0] = d->devaddr; + obuf[1] = d->handler; olen = 2; break; } From 84051eb400495745035b52e27fe67b962b7a58fa Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:44:01 +0100 Subject: [PATCH 1340/2380] adb: add property to disable direct reg 3 writes MacOS 9 has a bug in its PMU driver whereby after configuring the ADB bus devices it sends another write to reg 3 on both devices resetting them both back to the same address. Add a new disable_direct_reg3_writes property to ADBDevice to disable these direct writes which can enabled just for the upcoming pmu-adb support. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/input/adb-kbd.c | 25 ++++++++++++++----------- hw/input/adb-mouse.c | 37 ++++++++++++++++++++----------------- hw/input/adb.c | 7 +++++++ include/hw/input/adb.h | 1 + 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index 0ad384dc89..b026e9d49f 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -261,18 +261,21 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, trace_adb_kbd_request_change_addr(d->devaddr); break; default: - d->devaddr = buf[1] & 0xf; - /* we support handlers: - * 1: Apple Standard Keyboard - * 2: Apple Extended Keyboard (LShift = RShift) - * 3: Apple Extended Keyboard (LShift != RShift) - */ - if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { - d->handler = buf[2]; - } + if (!d->disable_direct_reg3_writes) { + d->devaddr = buf[1] & 0xf; - trace_adb_kbd_request_change_addr_and_handler(d->devaddr, - d->handler); + /* we support handlers: + * 1: Apple Standard Keyboard + * 2: Apple Extended Keyboard (LShift = RShift) + * 3: Apple Extended Keyboard (LShift != RShift) + */ + if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { + d->handler = buf[2]; + } + + trace_adb_kbd_request_change_addr_and_handler(d->devaddr, + d->handler); + } break; } } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index 473045fbac..83833b0035 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -142,24 +142,27 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, trace_adb_mouse_request_change_addr(d->devaddr); break; default: - d->devaddr = buf[1] & 0xf; - /* we support handlers: - * 0x01: Classic Apple Mouse Protocol / 100 cpi operations - * 0x02: Classic Apple Mouse Protocol / 200 cpi operations - * we don't support handlers (at least): - * 0x03: Mouse systems A3 trackball - * 0x04: Extended Apple Mouse Protocol - * 0x2f: Microspeed mouse - * 0x42: Macally - * 0x5f: Microspeed mouse - * 0x66: Microspeed mouse - */ - if (buf[2] == 1 || buf[2] == 2) { - d->handler = buf[2]; - } + if (!d->disable_direct_reg3_writes) { + d->devaddr = buf[1] & 0xf; - trace_adb_mouse_request_change_addr_and_handler(d->devaddr, - d->handler); + /* we support handlers: + * 0x01: Classic Apple Mouse Protocol / 100 cpi operations + * 0x02: Classic Apple Mouse Protocol / 200 cpi operations + * we don't support handlers (at least): + * 0x03: Mouse systems A3 trackball + * 0x04: Extended Apple Mouse Protocol + * 0x2f: Microspeed mouse + * 0x42: Macally + * 0x5f: Microspeed mouse + * 0x66: Microspeed mouse + */ + if (buf[2] == 1 || buf[2] == 2) { + d->handler = buf[2]; + } + + trace_adb_mouse_request_change_addr_and_handler( + d->devaddr, d->handler); + } break; } } diff --git a/hw/input/adb.c b/hw/input/adb.c index 23ae6f0d75..bbb40aeef1 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -113,11 +113,18 @@ static void adb_device_realizefn(DeviceState *dev, Error **errp) bus->devices[bus->nb_devices++] = d; } +static Property adb_device_properties[] = { + DEFINE_PROP_BOOL("disable-direct-reg3-writes", ADBDevice, + disable_direct_reg3_writes, false), + DEFINE_PROP_END_OF_LIST(), +}; + static void adb_device_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = adb_device_realizefn; + dc->props = adb_device_properties; dc->bus_type = TYPE_ADB_BUS; } diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h index 3ae8445e95..f99d478252 100644 --- a/include/hw/input/adb.h +++ b/include/hw/input/adb.h @@ -49,6 +49,7 @@ struct ADBDevice { int devaddr; int handler; + bool disable_direct_reg3_writes; }; #define ADB_DEVICE_CLASS(cls) \ From d811d61fbc6ca5f2be2185fd7cfa916e7ba613ce Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:44:02 +0100 Subject: [PATCH 1341/2380] mac_newworld: add PMU device The PMU device supercedes the CUDA device found on older New World Macs and is supported by a larger number of guest OSs from OS 9 to OS X 10.5. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- default-configs/ppc-softmmu.mak | 1 + hw/misc/macio/Makefile.objs | 1 + hw/misc/macio/macio.c | 69 ++- hw/misc/macio/pmu.c | 871 ++++++++++++++++++++++++++++++++ hw/misc/macio/trace-events | 21 + hw/ppc/mac.h | 1 + hw/ppc/mac_newworld.c | 10 +- include/hw/misc/macio/macio.h | 2 + include/hw/misc/macio/pmu.h | 237 +++++++++ 9 files changed, 1193 insertions(+), 20 deletions(-) create mode 100644 hw/misc/macio/pmu.c create mode 100644 include/hw/misc/macio/pmu.h diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 38197e39eb..abeeb0418a 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -38,6 +38,7 @@ CONFIG_CUDA=y CONFIG_ADB=y CONFIG_MAC_NVRAM=y CONFIG_MAC_DBDMA=y +CONFIG_MAC_PMU=y CONFIG_HEATHROW_PIC=y CONFIG_GRACKLE_PCI=y CONFIG_UNIN_PCI=y diff --git a/hw/misc/macio/Makefile.objs b/hw/misc/macio/Makefile.objs index fb9dbf91b5..07fdb320d4 100644 --- a/hw/misc/macio/Makefile.objs +++ b/hw/misc/macio/Makefile.objs @@ -1,4 +1,5 @@ common-obj-y += macio.o common-obj-$(CONFIG_CUDA) += cuda.o +common-obj-$(CONFIG_MAC_PMU) += pmu.o common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o common-obj-$(CONFIG_MACIO_GPIO) += gpio.o diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 8dfcbc3d9b..d135e3bc2b 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -105,17 +105,6 @@ static void macio_common_realize(PCIDevice *d, Error **errp) memory_region_add_subregion(&s->bar, 0x08000, sysbus_mmio_get_region(sysbus_dev, 0)); - qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", - s->frequency); - object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - memory_region_add_subregion(&s->bar, 0x16000, - sysbus_mmio_get_region(sysbus_dev, 0)); - qdev_prop_set_uint32(DEVICE(&s->escc), "disabled", 0); qdev_prop_set_uint32(DEVICE(&s->escc), "frequency", ESCC_CLOCK); qdev_prop_set_uint32(DEVICE(&s->escc), "it_shift", 4); @@ -163,7 +152,16 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) return; } + qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", + s->frequency); + object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, OLDWORLD_CUDA_IRQ)); @@ -234,6 +232,10 @@ static void macio_oldworld_init(Object *obj) qdev_prop_allow_set_link_before_realize, 0, NULL); + object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); + qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); + object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); + object_initialize(&os->nvram, sizeof(os->nvram), TYPE_MACIO_NVRAM); dev = DEVICE(&os->nvram); qdev_prop_set_uint32(dev, "size", 0x2000); @@ -293,10 +295,6 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) return; } - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, - NEWWORLD_CUDA_IRQ)); - sysbus_dev = SYS_BUS_DEVICE(&s->escc); sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, NEWWORLD_ESCCB_IRQ)); @@ -341,6 +339,43 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) memory_region_add_subregion(&s->bar, 0x50, sysbus_mmio_get_region(sysbus_dev, 0)); object_property_set_bool(OBJECT(&ns->gpio), true, "realized", &err); + + /* PMU */ + object_initialize(&s->pmu, sizeof(s->pmu), TYPE_VIA_PMU); + object_property_set_link(OBJECT(&s->pmu), OBJECT(sysbus_dev), "gpio", + &error_abort); + qdev_prop_set_bit(DEVICE(&s->pmu), "has-adb", ns->has_adb); + qdev_set_parent_bus(DEVICE(&s->pmu), sysbus_get_default()); + object_property_add_child(OBJECT(s), "pmu", OBJECT(&s->pmu), NULL); + + object_property_set_bool(OBJECT(&s->pmu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_dev = SYS_BUS_DEVICE(&s->pmu); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + NEWWORLD_PMU_IRQ)); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); + } else { + /* CUDA */ + object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); + qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); + object_property_add_child(OBJECT(s), "cuda", OBJECT(&s->cuda), NULL); + qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", + s->frequency); + + object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + NEWWORLD_CUDA_IRQ)); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); } } @@ -369,10 +404,6 @@ static void macio_instance_init(Object *obj) memory_region_init(&s->bar, obj, "macio", 0x80000); - object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); - qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); - object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); - object_initialize(&s->dbdma, sizeof(s->dbdma), TYPE_MAC_DBDMA); qdev_set_parent_bus(DEVICE(&s->dbdma), sysbus_get_default()); object_property_add_child(obj, "dbdma", OBJECT(&s->dbdma), NULL); diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c new file mode 100644 index 0000000000..e246b0fd41 --- /dev/null +++ b/hw/misc/macio/pmu.c @@ -0,0 +1,871 @@ +/* + * QEMU PowerMac PMU device support + * + * Copyright (c) 2016 Benjamin Herrenschmidt, IBM Corp. + * Copyright (c) 2018 Mark Cave-Ayland + * + * Based on the CUDA device by: + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * 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 "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/ppc/mac.h" +#include "hw/input/adb.h" +#include "hw/misc/mos6522.h" +#include "hw/misc/macio/gpio.h" +#include "hw/misc/macio/pmu.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "trace.h" + + +/* Bits in B data register: all active low */ +#define TACK 0x08 /* Transfer request (input) */ +#define TREQ 0x10 /* Transfer acknowledge (output) */ + +/* PMU returns time_t's offset from Jan 1, 1904, not 1970 */ +#define RTC_OFFSET 2082844800 + +#define VIA_TIMER_FREQ (4700000 / 6) + +static void via_update_irq(PMUState *s) +{ + MOS6522PMUState *mps = MOS6522_PMU(&s->mos6522_pmu); + MOS6522State *ms = MOS6522(mps); + + bool new_state = !!(ms->ifr & ms->ier & (SR_INT | T1_INT | T2_INT)); + + if (new_state != s->via_irq_state) { + s->via_irq_state = new_state; + qemu_set_irq(s->via_irq, new_state); + } +} + +static void via_set_sr_int(void *opaque) +{ + PMUState *s = opaque; + MOS6522PMUState *mps = MOS6522_PMU(&s->mos6522_pmu); + MOS6522State *ms = MOS6522(mps); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); + + mdc->set_sr_int(ms); +} + +static void pmu_update_extirq(PMUState *s) +{ + if ((s->intbits & s->intmask) != 0) { + macio_set_gpio(s->gpio, 1, false); + } else { + macio_set_gpio(s->gpio, 1, true); + } +} + +static void pmu_adb_poll(void *opaque) +{ + PMUState *s = opaque; + int olen; + + if (!(s->intbits & PMU_INT_ADB)) { + olen = adb_poll(&s->adb_bus, s->adb_reply, s->adb_poll_mask); + trace_pmu_adb_poll(olen); + + if (olen > 0) { + s->adb_reply_size = olen; + s->intbits |= PMU_INT_ADB | PMU_INT_ADB_AUTO; + pmu_update_extirq(s); + } + } + + timer_mod(s->adb_poll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); +} + +static void pmu_one_sec_timer(void *opaque) +{ + PMUState *s = opaque; + + trace_pmu_one_sec_timer(); + + s->intbits |= PMU_INT_TICK; + pmu_update_extirq(s); + s->one_sec_target += 1000; + + timer_mod(s->one_sec_timer, s->one_sec_target); +} + +static void pmu_cmd_int_ack(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: INT_ACK command, invalid len: %d want: 0\n", + in_len); + return; + } + + /* Make appropriate reply packet */ + if (s->intbits & PMU_INT_ADB) { + if (!s->adb_reply_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "Odd, PMU_INT_ADB set with no reply in buffer\n"); + } + + memcpy(out_data + 1, s->adb_reply, s->adb_reply_size); + out_data[0] = s->intbits & (PMU_INT_ADB | PMU_INT_ADB_AUTO); + *out_len = s->adb_reply_size + 1; + s->intbits &= ~(PMU_INT_ADB | PMU_INT_ADB_AUTO); + s->adb_reply_size = 0; + } else { + out_data[0] = s->intbits; + s->intbits = 0; + *out_len = 1; + } + + pmu_update_extirq(s); +} + +static void pmu_cmd_set_int_mask(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SET_INT_MASK command, invalid len: %d want: 1\n", + in_len); + return; + } + + trace_pmu_cmd_set_int_mask(s->intmask); + s->intmask = in_data[0]; + + pmu_update_extirq(s); +} + +static void pmu_cmd_set_adb_autopoll(PMUState *s, uint16_t mask) +{ + trace_pmu_cmd_set_adb_autopoll(mask); + + if (s->autopoll_mask == mask) { + return; + } + + s->autopoll_mask = mask; + if (mask) { + timer_mod(s->adb_poll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); + } else { + timer_del(s->adb_poll_timer); + } +} + +static void pmu_cmd_adb(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + int len, adblen; + uint8_t adb_cmd[255]; + + if (in_len < 2) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB PACKET, invalid len: %d want at least 2\n", + in_len); + return; + } + + *out_len = 0; + + if (!s->has_adb) { + trace_pmu_cmd_adb_nobus(); + return; + } + + /* Set autopoll is a special form of the command */ + if (in_data[0] == 0 && in_data[1] == 0x86) { + uint16_t mask = in_data[2]; + mask = (mask << 8) | in_data[3]; + if (in_len != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB Autopoll requires 4 bytes, got %d\n", + in_len); + return; + } + + pmu_cmd_set_adb_autopoll(s, mask); + return; + } + + trace_pmu_cmd_adb_request(in_len, in_data[0], in_data[1], in_data[2], + in_data[3], in_data[4]); + + *out_len = 0; + + /* Check ADB len */ + adblen = in_data[2]; + if (adblen > (in_len - 3)) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB len is %d > %d (in_len -3)...erroring\n", + adblen, in_len - 3); + len = -1; + } else if (adblen > 252) { + qemu_log_mask(LOG_GUEST_ERROR, "PMU: ADB command too big!\n"); + len = -1; + } else { + /* Format command */ + adb_cmd[0] = in_data[0]; + memcpy(&adb_cmd[1], &in_data[3], in_len - 3); + len = adb_request(&s->adb_bus, s->adb_reply + 2, adb_cmd, in_len - 2); + + trace_pmu_cmd_adb_reply(len); + } + + if (len > 0) { + /* XXX Check this */ + s->adb_reply_size = len + 2; + s->adb_reply[0] = 0x01; + s->adb_reply[1] = len; + } else { + /* XXX Check this */ + s->adb_reply_size = 1; + s->adb_reply[0] = 0x00; + } + + s->intbits |= PMU_INT_ADB; + pmu_update_extirq(s); +} + +static void pmu_cmd_adb_poll_off(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB POLL OFF command, invalid len: %d want: 0\n", + in_len); + return; + } + + if (s->has_adb && s->autopoll_mask) { + timer_del(s->adb_poll_timer); + s->autopoll_mask = false; + } +} + +static void pmu_cmd_shutdown(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SHUTDOWN command, invalid len: %d want: 4\n", + in_len); + return; + } + + *out_len = 1; + out_data[0] = 0; + + if (in_data[0] != 'M' || in_data[1] != 'A' || in_data[2] != 'T' || + in_data[3] != 'T') { + + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SHUTDOWN command, Bad MATT signature\n"); + return; + } + + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); +} + +static void pmu_cmd_reset(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: RESET command, invalid len: %d want: 0\n", + in_len); + return; + } + + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); +} + +static void pmu_cmd_get_rtc(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + uint32_t ti; + + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: GET_RTC command, invalid len: %d want: 0\n", + in_len); + return; + } + + ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / NANOSECONDS_PER_SECOND); + out_data[0] = ti >> 24; + out_data[1] = ti >> 16; + out_data[2] = ti >> 8; + out_data[3] = ti; + *out_len = 4; +} + +static void pmu_cmd_set_rtc(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + uint32_t ti; + + if (in_len != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SET_RTC command, invalid len: %d want: 4\n", + in_len); + return; + } + + ti = (((uint32_t)in_data[0]) << 24) + (((uint32_t)in_data[1]) << 16) + + (((uint32_t)in_data[2]) << 8) + in_data[3]; + + s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / NANOSECONDS_PER_SECOND); +} + +static void pmu_cmd_system_ready(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + /* Do nothing */ +} + +static void pmu_cmd_get_version(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + *out_len = 1; + *out_data = 1; /* ??? Check what Apple does */ +} + +static void pmu_cmd_power_events(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len < 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: POWER EVENTS command, invalid len %d, want at least 1\n", + in_len); + return; + } + + switch (in_data[0]) { + /* Dummies for now */ + case PMU_PWR_GET_POWERUP_EVENTS: + *out_len = 2; + out_data[0] = 0; + out_data[1] = 0; + break; + case PMU_PWR_SET_POWERUP_EVENTS: + case PMU_PWR_CLR_POWERUP_EVENTS: + break; + case PMU_PWR_GET_WAKEUP_EVENTS: + *out_len = 2; + out_data[0] = 0; + out_data[1] = 0; + break; + case PMU_PWR_SET_WAKEUP_EVENTS: + case PMU_PWR_CLR_WAKEUP_EVENTS: + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: POWER EVENTS unknown subcommand 0x%02x\n", + in_data[0]); + } +} + +static void pmu_cmd_get_cover(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + /* Not 100% sure here, will have to check what a real Mac + * returns other than byte 0 bit 0 is LID closed on laptops + */ + *out_len = 1; + *out_data = 0x00; +} + +static void pmu_cmd_download_status(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + /* This has to do with PMU firmware updates as far as I can tell. + * + * We return 0x62 which is what OpenPMU expects + */ + *out_len = 1; + *out_data = 0x62; +} + +static void pmu_cmd_read_pmu_ram(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len < 3) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: READ_PMU_RAM command, invalid len %d, expected 3\n", + in_len); + return; + } + + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: Unsupported READ_PMU_RAM, args: %02x %02x %02x\n", + in_data[0], in_data[1], in_data[2]); + + *out_len = 0; +} + +/* description of commands */ +typedef struct PMUCmdHandler { + uint8_t command; + const char *name; + void (*handler)(PMUState *s, + const uint8_t *in_args, uint8_t in_len, + uint8_t *out_args, uint8_t *out_len); +} PMUCmdHandler; + +static const PMUCmdHandler PMUCmdHandlers[] = { + { PMU_INT_ACK, "INT ACK", pmu_cmd_int_ack }, + { PMU_SET_INTR_MASK, "SET INT MASK", pmu_cmd_set_int_mask }, + { PMU_ADB_CMD, "ADB COMMAND", pmu_cmd_adb }, + { PMU_ADB_POLL_OFF, "ADB POLL OFF", pmu_cmd_adb_poll_off }, + { PMU_RESET, "REBOOT", pmu_cmd_reset }, + { PMU_SHUTDOWN, "SHUTDOWN", pmu_cmd_shutdown }, + { PMU_READ_RTC, "GET RTC", pmu_cmd_get_rtc }, + { PMU_SET_RTC, "SET RTC", pmu_cmd_set_rtc }, + { PMU_SYSTEM_READY, "SYSTEM READY", pmu_cmd_system_ready }, + { PMU_GET_VERSION, "GET VERSION", pmu_cmd_get_version }, + { PMU_POWER_EVENTS, "POWER EVENTS", pmu_cmd_power_events }, + { PMU_GET_COVER, "GET_COVER", pmu_cmd_get_cover }, + { PMU_DOWNLOAD_STATUS, "DOWNLOAD STATUS", pmu_cmd_download_status }, + { PMU_READ_PMU_RAM, "READ PMGR RAM", pmu_cmd_read_pmu_ram }, +}; + +static void pmu_dispatch_cmd(PMUState *s) +{ + unsigned int i; + + /* No response by default */ + s->cmd_rsp_sz = 0; + + for (i = 0; i < ARRAY_SIZE(PMUCmdHandlers); i++) { + const PMUCmdHandler *desc = &PMUCmdHandlers[i]; + + if (desc->command != s->cmd) { + continue; + } + + trace_pmu_dispatch_cmd(desc->name); + desc->handler(s, s->cmd_buf, s->cmd_buf_pos, + s->cmd_rsp, &s->cmd_rsp_sz); + + if (s->rsplen != -1 && s->rsplen != s->cmd_rsp_sz) { + trace_pmu_debug_protocol_string("QEMU internal cmd resp mismatch!"); + } else { + trace_pmu_debug_protocol_resp_size(s->cmd_rsp_sz); + } + + return; + } + + trace_pmu_dispatch_unknown_cmd(s->cmd); + + /* Manufacture fake response with 0's */ + if (s->rsplen == -1) { + s->cmd_rsp_sz = 0; + } else { + s->cmd_rsp_sz = s->rsplen; + memset(s->cmd_rsp, 0, s->rsplen); + } +} + +static void pmu_update(PMUState *s) +{ + MOS6522PMUState *mps = &s->mos6522_pmu; + MOS6522State *ms = MOS6522(mps); + + /* Only react to changes in reg B */ + if (ms->b == s->last_b) { + return; + } + s->last_b = ms->b; + + /* Check the TREQ / TACK state */ + switch (ms->b & (TREQ | TACK)) { + case TREQ: + /* This is an ack release, handle it and bail out */ + ms->b |= TACK; + s->last_b = ms->b; + + trace_pmu_debug_protocol_string("handshake: TREQ high, setting TACK"); + return; + case TACK: + /* This is a valid request, handle below */ + break; + case TREQ | TACK: + /* This is an idle state */ + return; + default: + /* Invalid state, log and ignore */ + trace_pmu_debug_protocol_error(ms->b); + return; + } + + /* If we wanted to handle commands asynchronously, this is where + * we would delay the clearing of TACK until we are ready to send + * the response + */ + + /* We have a request, handshake TACK so we don't stay in + * an invalid state. If we were concurrent with the OS we + * should only do this after we grabbed the SR but that isn't + * a problem here. + */ + + trace_pmu_debug_protocol_clear_treq(s->cmd_state); + + ms->b &= ~TACK; + s->last_b = ms->b; + + /* Act according to state */ + switch (s->cmd_state) { + case pmu_state_idle: + if (!(ms->acr & SR_OUT)) { + trace_pmu_debug_protocol_string("protocol error! " + "state idle, ACR reading"); + break; + } + + s->cmd = ms->sr; + via_set_sr_int(s); + s->cmdlen = pmu_data_len[s->cmd][0]; + s->rsplen = pmu_data_len[s->cmd][1]; + s->cmd_buf_pos = 0; + s->cmd_rsp_pos = 0; + s->cmd_state = pmu_state_cmd; + + trace_pmu_debug_protocol_cmd(s->cmd, s->cmdlen, s->rsplen); + break; + + case pmu_state_cmd: + if (!(ms->acr & SR_OUT)) { + trace_pmu_debug_protocol_string("protocol error! " + "state cmd, ACR reading"); + break; + } + + if (s->cmdlen == -1) { + trace_pmu_debug_protocol_cmdlen(ms->sr); + + s->cmdlen = ms->sr; + if (s->cmdlen > sizeof(s->cmd_buf)) { + trace_pmu_debug_protocol_cmd_toobig(s->cmdlen); + } + } else if (s->cmd_buf_pos < sizeof(s->cmd_buf)) { + s->cmd_buf[s->cmd_buf_pos++] = ms->sr; + } + + via_set_sr_int(s); + break; + + case pmu_state_rsp: + if (ms->acr & SR_OUT) { + trace_pmu_debug_protocol_string("protocol error! " + "state resp, ACR writing"); + break; + } + + if (s->rsplen == -1) { + trace_pmu_debug_protocol_cmd_send_resp_size(s->cmd_rsp_sz); + + ms->sr = s->cmd_rsp_sz; + s->rsplen = s->cmd_rsp_sz; + } else if (s->cmd_rsp_pos < s->cmd_rsp_sz) { + trace_pmu_debug_protocol_cmd_send_resp(s->cmd_rsp_pos, s->rsplen); + + ms->sr = s->cmd_rsp[s->cmd_rsp_pos++]; + } + + via_set_sr_int(s); + break; + } + + /* Check for state completion */ + if (s->cmd_state == pmu_state_cmd && s->cmdlen == s->cmd_buf_pos) { + trace_pmu_debug_protocol_string("Command reception complete, " + "dispatching..."); + + pmu_dispatch_cmd(s); + s->cmd_state = pmu_state_rsp; + } + + if (s->cmd_state == pmu_state_rsp && s->rsplen == s->cmd_rsp_pos) { + trace_pmu_debug_protocol_cmd_resp_complete(ms->ier); + + s->cmd_state = pmu_state_idle; + } +} + +static uint64_t mos6522_pmu_read(void *opaque, hwaddr addr, unsigned size) +{ + PMUState *s = opaque; + MOS6522PMUState *mps = &s->mos6522_pmu; + MOS6522State *ms = MOS6522(mps); + + addr = (addr >> 9) & 0xf; + return mos6522_read(ms, addr, size); +} + +static void mos6522_pmu_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PMUState *s = opaque; + MOS6522PMUState *mps = &s->mos6522_pmu; + MOS6522State *ms = MOS6522(mps); + + addr = (addr >> 9) & 0xf; + mos6522_write(ms, addr, val, size); +} + +static const MemoryRegionOps mos6522_pmu_ops = { + .read = mos6522_pmu_read, + .write = mos6522_pmu_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static bool pmu_adb_state_needed(void *opaque) +{ + PMUState *s = opaque; + + return s->has_adb; +} + +static const VMStateDescription vmstate_pmu_adb = { + .name = "pmu/adb", + .version_id = 0, + .minimum_version_id = 0, + .needed = pmu_adb_state_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT16(adb_poll_mask, PMUState), + VMSTATE_TIMER_PTR(adb_poll_timer, PMUState), + VMSTATE_UINT8(adb_reply_size, PMUState), + VMSTATE_BUFFER(adb_reply, PMUState), + } +}; + +static const VMStateDescription vmstate_pmu = { + .name = "pmu", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(mos6522_pmu.parent_obj, PMUState, 0, vmstate_mos6522, + MOS6522State), + VMSTATE_UINT8(last_b, PMUState), + VMSTATE_UINT8(cmd, PMUState), + VMSTATE_UINT32(cmdlen, PMUState), + VMSTATE_UINT32(rsplen, PMUState), + VMSTATE_UINT8(cmd_buf_pos, PMUState), + VMSTATE_BUFFER(cmd_buf, PMUState), + VMSTATE_UINT8(cmd_rsp_pos, PMUState), + VMSTATE_UINT8(cmd_rsp_sz, PMUState), + VMSTATE_BUFFER(cmd_rsp, PMUState), + VMSTATE_UINT8(intbits, PMUState), + VMSTATE_UINT8(intmask, PMUState), + VMSTATE_UINT8(autopoll_rate_ms, PMUState), + VMSTATE_UINT8(autopoll_mask, PMUState), + VMSTATE_UINT32(tick_offset, PMUState), + VMSTATE_TIMER_PTR(one_sec_timer, PMUState), + VMSTATE_INT64(one_sec_target, PMUState), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_pmu_adb, + } +}; + +static void pmu_reset(DeviceState *dev) +{ + PMUState *s = VIA_PMU(dev); + + /* OpenBIOS needs to do this? MacOS 9 needs it */ + s->intmask = PMU_INT_ADB | PMU_INT_TICK; + s->intbits = 0; + + s->cmd_state = pmu_state_idle; + s->autopoll_mask = 0; +} + +static void pmu_realize(DeviceState *dev, Error **errp) +{ + PMUState *s = VIA_PMU(dev); + SysBusDevice *sbd; + MOS6522State *ms; + DeviceState *d; + struct tm tm; + + /* Pass IRQ from 6522 */ + d = DEVICE(&s->mos6522_pmu); + ms = MOS6522(d); + sbd = SYS_BUS_DEVICE(s); + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(ms)); + + qemu_get_timedate(&tm, 0); + s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; + s->one_sec_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_one_sec_timer, s); + s->one_sec_target = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000; + timer_mod(s->one_sec_timer, s->one_sec_target); + + if (s->has_adb) { + qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, + DEVICE(dev), "adb.0"); + s->adb_poll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_adb_poll, s); + s->adb_poll_mask = 0xffff; + s->autopoll_rate_ms = 20; + } +} + +static void pmu_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + PMUState *s = VIA_PMU(obj); + + object_property_add_link(obj, "gpio", TYPE_MACIO_GPIO, + (Object **) &s->gpio, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + object_initialize(&s->mos6522_pmu, sizeof(s->mos6522_pmu), + TYPE_MOS6522_PMU); + qdev_set_parent_bus(DEVICE(&s->mos6522_pmu), sysbus_get_default()); + + memory_region_init_io(&s->mem, obj, &mos6522_pmu_ops, s, "via-pmu", + 0x2000); + sysbus_init_mmio(d, &s->mem); +} + +static Property pmu_properties[] = { + DEFINE_PROP_BOOL("has-adb", PMUState, has_adb, true), + DEFINE_PROP_END_OF_LIST() +}; + +static void pmu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = pmu_realize; + dc->reset = pmu_reset; + dc->vmsd = &vmstate_pmu; + dc->props = pmu_properties; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +static const TypeInfo pmu_type_info = { + .name = TYPE_VIA_PMU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PMUState), + .instance_init = pmu_init, + .class_init = pmu_class_init, +}; + +static void mos6522_pmu_portB_write(MOS6522State *s) +{ + MOS6522PMUState *mps = container_of(s, MOS6522PMUState, parent_obj); + PMUState *ps = container_of(mps, PMUState, mos6522_pmu); + + if ((s->pcr & 0xe0) == 0x20 || (s->pcr & 0xe0) == 0x60) { + s->ifr &= ~CB2_INT; + } + s->ifr &= ~CB1_INT; + + via_update_irq(ps); + pmu_update(ps); +} + +static void mos6522_pmu_portA_write(MOS6522State *s) +{ + MOS6522PMUState *mps = container_of(s, MOS6522PMUState, parent_obj); + PMUState *ps = container_of(mps, PMUState, mos6522_pmu); + + if ((s->pcr & 0x0e) == 0x02 || (s->pcr & 0x0e) == 0x06) { + s->ifr &= ~CA2_INT; + } + s->ifr &= ~CA1_INT; + + via_update_irq(ps); +} + +static void mos6522_pmu_reset(DeviceState *dev) +{ + MOS6522State *ms = MOS6522(dev); + MOS6522PMUState *mps = container_of(ms, MOS6522PMUState, parent_obj); + PMUState *s = container_of(mps, PMUState, mos6522_pmu); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); + + mdc->parent_reset(dev); + + ms->timers[0].frequency = VIA_TIMER_FREQ; + ms->timers[1].frequency = (SCALE_US * 6000) / 4700; + + s->last_b = ms->b = TACK | TREQ; +} + +static void mos6522_pmu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); + + dc->reset = mos6522_pmu_reset; + mdc->portB_write = mos6522_pmu_portB_write; + mdc->portA_write = mos6522_pmu_portA_write; +} + +static const TypeInfo mos6522_pmu_type_info = { + .name = TYPE_MOS6522_PMU, + .parent = TYPE_MOS6522, + .instance_size = sizeof(MOS6522PMUState), + .class_init = mos6522_pmu_class_init, +}; + +static void pmu_register_types(void) +{ + type_register_static(&pmu_type_info); + type_register_static(&mos6522_pmu_type_info); +} + +type_init(pmu_register_types) diff --git a/hw/misc/macio/trace-events b/hw/misc/macio/trace-events index 71c47520eb..05019262fa 100644 --- a/hw/misc/macio/trace-events +++ b/hw/misc/macio/trace-events @@ -20,3 +20,24 @@ macio_gpio_irq_assert(int gpio) "asserting GPIO %d" macio_gpio_irq_deassert(int gpio) "deasserting GPIO %d" macio_gpio_write(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 macio_gpio_read(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 + +# hw/misc/macio/pmu.c +pmu_adb_poll(int olen) "ADB autopoll, olen=%d" +pmu_one_sec_timer(void) "PMU one sec..." +pmu_cmd_set_int_mask(int intmask) "Setting PMU int mask to 0x%02x" +pmu_cmd_set_adb_autopoll(int mask) "ADB set autopoll, mask=0x%04x" +pmu_cmd_adb_nobus(void) "ADB PACKET with no ADB bus!" +pmu_cmd_adb_request(int inlen, int indata0, int indata1, int indata2, int indata3, int indata4) "ADB request: len=%d, cmd=0x%02x, pflags=0x%02x, adblen=%d: 0x%02x 0x%02x..." +pmu_cmd_adb_reply(int len) "ADB reply is %d bytes" +pmu_dispatch_cmd(const char *name) "handling command %s" +pmu_dispatch_unknown_cmd(int cmd) "Unknown PMU command 0x%02x" +pmu_debug_protocol_string(const char *str) "%s" +pmu_debug_protocol_resp_size(int size) "sending %d resp bytes" +pmu_debug_protocol_error(int portB) "protocol error! portB=0x%02x" +pmu_debug_protocol_clear_treq(int state) "TREQ cleared, clearing TACK, state: %d" +pmu_debug_protocol_cmd(int cmd, int cmdlen, int rsplen) "Got command byte 0x%02x, clen=%d, rlen=%d" +pmu_debug_protocol_cmdlen(int len) "got cmd length byte: %d" +pmu_debug_protocol_cmd_toobig(int len) "command too big (%d bytes)" +pmu_debug_protocol_cmd_send_resp_size(int len) "sending length byte: %d" +pmu_debug_protocol_cmd_send_resp(int pos, int len) "sending byte: %d/%d" +pmu_debug_protocol_cmd_resp_complete(int ier) "Response send complete. IER=0x%02x" diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index b3b7f9d8ae..c0217e66f2 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -59,6 +59,7 @@ /* New World IRQs */ #define NEWWORLD_CUDA_IRQ 0x19 +#define NEWWORLD_PMU_IRQ 0x19 #define NEWWORLD_ESCCB_IRQ 0x24 #define NEWWORLD_ESCCA_IRQ 0x25 #define NEWWORLD_IDE0_IRQ 0xd diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index ca21d47234..ff715ffffd 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -399,11 +399,19 @@ static void ppc_core99_init(MachineState *machine) macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); if (has_adb) { - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + if (has_pmu) { + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "pmu")); + } else { + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + } + adb_bus = qdev_get_child_bus(dev, "adb.0"); dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_prop_set_bit(dev, "disable-direct-reg3-writes", has_pmu); qdev_init_nofail(dev); + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_prop_set_bit(dev, "disable-direct-reg3-writes", has_pmu); qdev_init_nofail(dev); } diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index d43883a893..cfaa145500 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -30,6 +30,7 @@ #include "hw/intc/heathrow_pic.h" #include "hw/misc/macio/cuda.h" #include "hw/misc/macio/gpio.h" +#include "hw/misc/macio/pmu.h" #include "hw/ppc/mac_dbdma.h" #include "hw/ppc/openpic.h" @@ -43,6 +44,7 @@ typedef struct MacIOState { MemoryRegion bar; CUDAState cuda; + PMUState pmu; DBDMAState dbdma; ESCCState escc; uint64_t frequency; diff --git a/include/hw/misc/macio/pmu.h b/include/hw/misc/macio/pmu.h new file mode 100644 index 0000000000..d10895ba5f --- /dev/null +++ b/include/hw/misc/macio/pmu.h @@ -0,0 +1,237 @@ +/* + * Definitions for talking to the PMU. The PMU is a microcontroller + * which controls battery charging and system power on PowerBook 3400 + * and 2400 models as well as the RTC and various other things. + * + * Copyright (C) 1998 Paul Mackerras. + * Copyright (C) 2016 Ben Herrenschmidt + */ + +#ifndef PMU_H +#define PMU_H + +/* + * PMU commands + */ + +#define PMU_POWER_CTRL0 0x10 /* control power of some devices */ +#define PMU_POWER_CTRL 0x11 /* control power of some devices */ +#define PMU_ADB_CMD 0x20 /* send ADB packet */ +#define PMU_ADB_POLL_OFF 0x21 /* disable ADB auto-poll */ +#define PMU_WRITE_NVRAM 0x33 /* write non-volatile RAM */ +#define PMU_READ_NVRAM 0x3b /* read non-volatile RAM */ +#define PMU_SET_RTC 0x30 /* set real-time clock */ +#define PMU_READ_RTC 0x38 /* read real-time clock */ +#define PMU_SET_VOLBUTTON 0x40 /* set volume up/down position */ +#define PMU_BACKLIGHT_BRIGHT 0x41 /* set backlight brightness */ +#define PMU_GET_VOLBUTTON 0x48 /* get volume up/down position */ +#define PMU_PCEJECT 0x4c /* eject PC-card from slot */ +#define PMU_BATTERY_STATE 0x6b /* report battery state etc. */ +#define PMU_SMART_BATTERY_STATE 0x6f /* report battery state (new way) */ +#define PMU_SET_INTR_MASK 0x70 /* set PMU interrupt mask */ +#define PMU_INT_ACK 0x78 /* read interrupt bits */ +#define PMU_SHUTDOWN 0x7e /* turn power off */ +#define PMU_CPU_SPEED 0x7d /* control CPU speed on some models */ +#define PMU_SLEEP 0x7f /* put CPU to sleep */ +#define PMU_POWER_EVENTS 0x8f /* Send power-event commands to PMU */ +#define PMU_I2C_CMD 0x9a /* I2C operations */ +#define PMU_RESET 0xd0 /* reset CPU */ +#define PMU_GET_BRIGHTBUTTON 0xd9 /* report brightness up/down pos */ +#define PMU_GET_COVER 0xdc /* report cover open/closed */ +#define PMU_SYSTEM_READY 0xdf /* tell PMU we are awake */ +#define PMU_DOWNLOAD_STATUS 0xe2 /* Called by MacOS during boot... */ +#define PMU_READ_PMU_RAM 0xe8 /* read the PMU RAM... ??? */ +#define PMU_GET_VERSION 0xea /* read the PMU version */ + +/* Bits to use with the PMU_POWER_CTRL0 command */ +#define PMU_POW0_ON 0x80 /* OR this to power ON the device */ +#define PMU_POW0_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ +#define PMU_POW0_HARD_DRIVE 0x04 /* Hard drive power + * (on wallstreet/lombard ?) */ + +/* Bits to use with the PMU_POWER_CTRL command */ +#define PMU_POW_ON 0x80 /* OR this to power ON the device */ +#define PMU_POW_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ +#define PMU_POW_BACKLIGHT 0x01 /* backlight power */ +#define PMU_POW_CHARGER 0x02 /* battery charger power */ +#define PMU_POW_IRLED 0x04 /* IR led power (on wallstreet) */ +#define PMU_POW_MEDIABAY 0x08 /* media bay power + * (wallstreet/lombard ?) */ + +/* Bits in PMU interrupt and interrupt mask bytes */ +#define PMU_INT_PCEJECT 0x04 /* PC-card eject buttons */ +#define PMU_INT_SNDBRT 0x08 /* sound/brightness up/down buttons */ +#define PMU_INT_ADB 0x10 /* ADB autopoll or reply data */ +#define PMU_INT_BATTERY 0x20 /* Battery state change */ +#define PMU_INT_ENVIRONMENT 0x40 /* Environment interrupts */ +#define PMU_INT_TICK 0x80 /* 1-second tick interrupt */ + +/* Other bits in PMU interrupt valid when PMU_INT_ADB is set */ +#define PMU_INT_ADB_AUTO 0x04 /* ADB autopoll, when PMU_INT_ADB */ +#define PMU_INT_WAITING_CHARGER 0x01 /* ??? */ +#define PMU_INT_AUTO_SRQ_POLL 0x02 /* ??? */ + +/* Bits in the environement message (either obtained via PMU_GET_COVER, + * or via PMU_INT_ENVIRONMENT on core99 */ +#define PMU_ENV_LID_CLOSED 0x01 /* The lid is closed */ + +/* I2C related definitions */ +#define PMU_I2C_MODE_SIMPLE 0 +#define PMU_I2C_MODE_STDSUB 1 +#define PMU_I2C_MODE_COMBINED 2 + +#define PMU_I2C_BUS_STATUS 0 +#define PMU_I2C_BUS_SYSCLK 1 +#define PMU_I2C_BUS_POWER 2 + +#define PMU_I2C_STATUS_OK 0 +#define PMU_I2C_STATUS_DATAREAD 1 +#define PMU_I2C_STATUS_BUSY 0xfe + +/* Kind of PMU (model) */ +enum { + PMU_UNKNOWN, + PMU_OHARE_BASED, /* 2400, 3400, 3500 (old G3 powerbook) */ + PMU_HEATHROW_BASED, /* PowerBook G3 series */ + PMU_PADDINGTON_BASED, /* 1999 PowerBook G3 */ + PMU_KEYLARGO_BASED, /* Core99 motherboard (PMU99) */ + PMU_68K_V1, /* 68K PMU, version 1 */ + PMU_68K_V2, /* 68K PMU, version 2 */ +}; + +/* PMU PMU_POWER_EVENTS commands */ +enum { + PMU_PWR_GET_POWERUP_EVENTS = 0x00, + PMU_PWR_SET_POWERUP_EVENTS = 0x01, + PMU_PWR_CLR_POWERUP_EVENTS = 0x02, + PMU_PWR_GET_WAKEUP_EVENTS = 0x03, + PMU_PWR_SET_WAKEUP_EVENTS = 0x04, + PMU_PWR_CLR_WAKEUP_EVENTS = 0x05, +}; + +/* Power events wakeup bits */ +enum { + PMU_PWR_WAKEUP_KEY = 0x01, /* Wake on key press */ + PMU_PWR_WAKEUP_AC_INSERT = 0x02, /* Wake on AC adapter plug */ + PMU_PWR_WAKEUP_AC_CHANGE = 0x04, + PMU_PWR_WAKEUP_LID_OPEN = 0x08, + PMU_PWR_WAKEUP_RING = 0x10, +}; + +/* + * This table indicates for each PMU opcode: + * - the number of data bytes to be sent with the command, or -1 + * if a length byte should be sent, + * - the number of response bytes which the PMU will return, or + * -1 if it will send a length byte. + */ + +static const int8_t pmu_data_len[256][2] = { +/* 0 1 2 3 4 5 6 7 */ + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 1},{ 0, 1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{ 0, 0}, + {-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, -1},{ 0, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{ 0, -1}, + { 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 4},{ 0, 20},{ 2, -1},{ 2, 1},{ 3, -1},{-1, -1},{-1, -1},{ 4, 0}, + { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 1},{ 0, 1},{-1, -1},{ 1, 0},{ 1, 0},{-1, -1},{-1, -1},{-1, -1}, + { 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0}, + { 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0, -1},{-1, -1},{-1, -1},{-1, -1}, + { 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0, -1},{ 0, -1},{-1, -1},{-1, -1}, + { 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, -1},{ 0, -1},{-1, -1},{-1, -1},{-1, -1},{ 5, 1},{ 4, 1},{ 4, 1}, + { 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 5},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + { 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 1},{ 0, 1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + { 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0}, + { 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + { 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 1, 1},{ 1, 1},{-1, -1},{-1, -1},{ 0, 1},{ 0, -1},{-1, -1},{-1, -1}, + {-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0}, + { 3, -1},{-1, -1},{ 0, 1},{-1, -1},{ 0, -1},{-1, -1},{-1, -1},{ 0, 0}, + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, +}; + +/* Command protocol state machine */ +typedef enum { + pmu_state_idle, /* Waiting for command */ + pmu_state_cmd, /* Receiving command */ + pmu_state_rsp, /* Responding to command */ +} PMUCmdState; + +/* MOS6522 PMU */ +typedef struct MOS6522PMUState { + /*< private >*/ + MOS6522State parent_obj; +} MOS6522PMUState; + +#define TYPE_MOS6522_PMU "mos6522-pmu" +#define MOS6522_PMU(obj) OBJECT_CHECK(MOS6522PMUState, (obj), \ + TYPE_MOS6522_PMU) +/** + * PMUState: + * @last_b: last value of B register + */ + +typedef struct PMUState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion mem; + uint64_t frequency; + qemu_irq via_irq; + bool via_irq_state; + + /* PMU state */ + MOS6522PMUState mos6522_pmu; + + /* PMU low level protocol state */ + PMUCmdState cmd_state; + uint8_t last_b; + uint8_t cmd; + uint32_t cmdlen; + uint32_t rsplen; + uint8_t cmd_buf_pos; + uint8_t cmd_buf[128]; + uint8_t cmd_rsp_pos; + uint8_t cmd_rsp_sz; + uint8_t cmd_rsp[128]; + + /* PMU events/interrupts */ + uint8_t intbits; + uint8_t intmask; + + /* ADB */ + bool has_adb; + ADBBusState adb_bus; + uint16_t adb_poll_mask; + uint8_t autopoll_rate_ms; + uint8_t autopoll_mask; + QEMUTimer *adb_poll_timer; + uint8_t adb_reply_size; + uint8_t adb_reply[ADB_MAX_OUT_LEN]; + + /* RTC */ + uint32_t tick_offset; + QEMUTimer *one_sec_timer; + int64_t one_sec_target; + + /* GPIO */ + MacIOGPIOState *gpio; +} PMUState; + +#define TYPE_VIA_PMU "via-pmu" +#define VIA_PMU(obj) OBJECT_CHECK(PMUState, (obj), TYPE_VIA_PMU) + +#endif /* PMU_H */ From 52b438815ecc1cb799d943ef4fd710fe67cc7702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Jun 2018 12:11:35 +0200 Subject: [PATCH 1342/2380] xics_kvm: fix a build break MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On CentOS 7.5, gcc-4.8.5-28.el7_5.1.ppc64le fails to build QEMU due to : hw/intc/xics_kvm.c: In function ‘ics_set_kvm_state’: hw/intc/xics_kvm.c:281:13: error: ‘ret’ may be used uninitialized in this function [-Werror=maybe-uninitialized] return ret; Fix the breakage and also remove the extra error reporting as kvm_device_access() already provides a substantial error message. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics_kvm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 8bdf6afe82..8dba2f84e7 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -186,8 +186,7 @@ static void ics_get_kvm_state(ICSState *ics) kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, i + ics->offset, &state, false, &local_err); if (local_err) { - error_report("Unable to retrieve KVM interrupt controller state" - " for IRQ %d: %s", i + ics->offset, strerror(errno)); + error_report_err(local_err); exit(1); } @@ -273,11 +272,10 @@ static int ics_set_kvm_state(ICSState *ics, int version_id) state |= KVM_XICS_QUEUED; } - kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, - i + ics->offset, &state, true, &local_err); + ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, + i + ics->offset, &state, true, &local_err); if (local_err) { - error_report("Unable to restore KVM interrupt controller state" - " for IRQs %d: %s", i + ics->offset, strerror(errno)); + error_report_err(local_err); return ret; } } From 7f5d6517e303e39d79f57ca92919725e03c9fad8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 13 Jun 2018 09:30:13 +0100 Subject: [PATCH 1343/2380] mos6522: only clear the shift register interrupt upon write According to the 6522 datasheet the shift register (SR) interrupt flag is cleared upon write with no mention of any other interrupt flags. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/mos6522.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 44eb306cf1..ad5041d8c0 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -241,7 +241,7 @@ uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) break; case VIA_REG_SR: val = s->sr; - s->ifr &= ~(SR_INT | CB1_INT | CB2_INT); + s->ifr &= ~SR_INT; mos6522_update_irq(s); break; case VIA_REG_ACR: From 32a8c27b5dfc834abf7ada7c55fcc69c97ae0140 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 13 Jun 2018 09:30:14 +0100 Subject: [PATCH 1344/2380] mos6522: remove additional interrupt flag filter from mos6522_update_irq() The datasheet indicates that the interrupt is generated by ANDing the interrupt flags register (IFR) with the interrupt enable register (IER) but currently there is an extra filter for the SR and timer interrupts. Remove this extra filter to allow interrupts to be generated by external inputs on bits 1 and 2 of ports A and B. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/mos6522.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index ad5041d8c0..8d5b419825 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -40,7 +40,7 @@ static void mos6522_timer_update(MOS6522State *s, MOS6522Timer *ti, static void mos6522_update_irq(MOS6522State *s) { - if (s->ifr & s->ier & (SR_INT | T1_INT | T2_INT)) { + if (s->ifr & s->ier) { qemu_irq_raise(s->irq); } else { qemu_irq_lower(s->irq); From b6c7e42f74ab244545e157a0f90a31c70a66f3eb Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 13 Jun 2018 09:30:15 +0100 Subject: [PATCH 1345/2380] mos6522: expose mos6522_update_irq() through MOS6522DeviceClass In the case where we have an interrupt generated externally from inputs to bits 1 and 2 of port A and/or port B, it is necessary to expose mos6522_update_irq() so it can be called by the interrupt source. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/mos6522.c | 1 + include/hw/misc/mos6522.h | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 8d5b419825..14cff26c61 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -463,6 +463,7 @@ static void mos6522_class_init(ObjectClass *oc, void *data) mdc->set_sr_int = mos6522_set_sr_int; mdc->portB_write = mos6522_portB_write; mdc->portA_write = mos6522_portA_write; + mdc->update_irq = mos6522_update_irq; mdc->get_timer1_counter_value = mos6522_get_counter_value; mdc->get_timer2_counter_value = mos6522_get_counter_value; mdc->get_timer1_load_time = mos6522_get_load_time; diff --git a/include/hw/misc/mos6522.h b/include/hw/misc/mos6522.h index f52b41920b..03d9f0c059 100644 --- a/include/hw/misc/mos6522.h +++ b/include/hw/misc/mos6522.h @@ -134,6 +134,7 @@ typedef struct MOS6522DeviceClass { void (*set_sr_int)(MOS6522State *dev); void (*portB_write)(MOS6522State *dev); void (*portA_write)(MOS6522State *dev); + void (*update_irq)(MOS6522State *dev); /* These are used to influence the CUDA MacOS timebase calibration */ uint64_t (*get_timer1_counter_value)(MOS6522State *dev, MOS6522Timer *ti); uint64_t (*get_timer2_counter_value)(MOS6522State *dev, MOS6522Timer *ti); From 2100b6b21ef9c64a3cca1582dbd573c17c97bc4a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 14 Jun 2018 02:17:00 +0200 Subject: [PATCH 1346/2380] sm501: Do not clear read only bits when writing registers When writing registers that have read only bits we have to avoid changing these bits as they may have non zero values. Make sure we use the correct masks to mask out read only and reserved bits when changing registers. Also remove extra spaces from dram_control and arbitration_control assignments. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/display/sm501.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index e47be99451..ca0840f6fa 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -836,27 +836,30 @@ static void sm501_system_config_write(void *opaque, hwaddr addr, switch (addr) { case SM501_SYSTEM_CONTROL: - s->system_control = value & 0xE300B8F7; + s->system_control &= 0x10DB0000; + s->system_control |= value & 0xEF00B8F7; break; case SM501_MISC_CONTROL: - s->misc_control = value & 0xFF7FFF20; + s->misc_control &= 0xEF; + s->misc_control |= value & 0xFF7FFF10; break; case SM501_GPIO31_0_CONTROL: s->gpio_31_0_control = value; break; case SM501_GPIO63_32_CONTROL: - s->gpio_63_32_control = value; + s->gpio_63_32_control = value & 0xFF80FFFF; break; case SM501_DRAM_CONTROL: s->local_mem_size_index = (value >> 13) & 0x7; /* TODO : check validity of size change */ - s->dram_control |= value & 0x7FFFFFC3; + s->dram_control &= 0x80000000; + s->dram_control |= value & 0x7FFFFFC3; break; case SM501_ARBTRTN_CONTROL: - s->arbitration_control = value & 0x37777777; + s->arbitration_control = value & 0x37777777; break; case SM501_IRQ_MASK: - s->irq_mask = value; + s->irq_mask = value & 0xFFDF3F5F; break; case SM501_MISC_TIMING: s->misc_timing = value & 0xF31F1FFF; From b1d40d6e09c205dac108e0c21ec2fdaeb1bbaae8 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 11:48:26 +1000 Subject: [PATCH 1347/2380] spapr: Clean up cpu realize/unrealize paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spapr_cpu_init() and spapr_cpu_destroy() are only called from the spapr cpu core realize/unrealize paths, and really can only be called from there. Those are all short functions, so fold the pairs together for simplicity. While we're there rename some functions and change some parameter types for brevity and clarity. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/spapr_cpu_core.c | 69 +++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index f3e9b879b2..7fdb3b6667 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -83,26 +83,6 @@ void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r ppc_store_lpcr(cpu, env->spr[SPR_LPCR] | pcc->lpcr_pm); } -static void spapr_cpu_destroy(PowerPCCPU *cpu) -{ - qemu_unregister_reset(spapr_cpu_reset, cpu); -} - -static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, - Error **errp) -{ - CPUPPCState *env = &cpu->env; - - /* Set time-base frequency to 512 MHz */ - cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); - - cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); - kvmppc_set_papr(cpu); - - qemu_register_reset(spapr_cpu_reset, cpu); - spapr_cpu_reset(cpu); -} - /* * Return the sPAPR CPU core type for @model which essentially is the CPU * model specified with -cpu cmdline option. @@ -122,44 +102,47 @@ const char *spapr_get_cpu_core_type(const char *cpu_type) return object_class_get_name(oc); } -static void spapr_cpu_core_unrealizefn(DeviceState *dev, Error **errp) +static void spapr_unrealize_vcpu(PowerPCCPU *cpu) +{ + qemu_unregister_reset(spapr_cpu_reset, cpu); + object_unparent(cpu->intc); + cpu_remove_sync(CPU(cpu)); + object_unparent(OBJECT(cpu)); +} + +static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp) { sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); int i; for (i = 0; i < cc->nr_threads; i++) { - Object *obj = OBJECT(sc->threads[i]); - DeviceState *dev = DEVICE(obj); - CPUState *cs = CPU(dev); - PowerPCCPU *cpu = POWERPC_CPU(cs); - - spapr_cpu_destroy(cpu); - object_unparent(cpu->intc); - cpu_remove_sync(cs); - object_unparent(obj); + spapr_unrealize_vcpu(sc->threads[i]); } g_free(sc->threads); } -static void spapr_cpu_core_realize_child(Object *child, - sPAPRMachineState *spapr, Error **errp) +static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr, + Error **errp) { + CPUPPCState *env = &cpu->env; Error *local_err = NULL; - CPUState *cs = CPU(child); - PowerPCCPU *cpu = POWERPC_CPU(cs); - object_property_set_bool(child, true, "realized", &local_err); + object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); if (local_err) { goto error; } - spapr_cpu_init(spapr, cpu, &local_err); - if (local_err) { - goto error; - } + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); - cpu->intc = icp_create(child, spapr->icp_type, XICS_FABRIC(spapr), + cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); + kvmppc_set_papr(cpu); + + qemu_register_reset(spapr_cpu_reset, cpu); + spapr_cpu_reset(cpu); + + cpu->intc = icp_create(OBJECT(cpu), spapr->icp_type, XICS_FABRIC(spapr), &local_err); if (local_err) { goto error; @@ -220,9 +203,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) } for (j = 0; j < cc->nr_threads; j++) { - obj = OBJECT(sc->threads[j]); - - spapr_cpu_core_realize_child(obj, spapr, &local_err); + spapr_realize_vcpu(sc->threads[j], spapr, &local_err); if (local_err) { goto err; } @@ -249,7 +230,7 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_CLASS(oc); dc->realize = spapr_cpu_core_realize; - dc->unrealize = spapr_cpu_core_unrealizefn; + dc->unrealize = spapr_cpu_core_unrealize; dc->props = spapr_cpu_core_properties; scc->cpu_type = data; } From 937c2146a6694b5bc987c2fa89917db4acc9ae39 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 11:55:31 +1000 Subject: [PATCH 1348/2380] pnv: Fix some error handling cpu realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In pnv_core_realize() we call two functions with an Error * parameter in succession, which will go badly if they both cause errors. In fact, a failure in either of them indicates a qemu internal error, so we can just use &error_abort in both cases. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/pnv_core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 13ad7d9e04..01f47c8037 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -172,12 +172,9 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) object_initialize(obj, size, typename); snprintf(name, sizeof(name), "thread[%d]", i); - object_property_add_child(OBJECT(pc), name, obj, &local_err); + object_property_add_child(OBJECT(pc), name, obj, &error_abort); object_property_add_alias(obj, "core-pir", OBJECT(pc), - "pir", &local_err); - if (local_err) { - goto err; - } + "pir", &error_abort); object_unref(obj); } From 08304a8689ef726fe1e3f61645a870fb53f67895 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 11:57:37 +1000 Subject: [PATCH 1349/2380] pnv_core: Allocate cpu thread objects individually MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, we allocate space for all the cpu objects within a single core in one big block. This was copied from an older version of the spapr code and requires some ugly pointer manipulation to extract the individual objects. This design was due to a misunderstanding of qemu lifetime conventions and has already been changed in spapr (in 94ad93bd "spapr_cpu_core: instantiate CPUs separately". Make an equivalent change in pnv_core to get rid of the nasty pointer arithmetic. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/pnv.c | 4 ++-- hw/ppc/pnv_core.c | 11 +++++------ include/hw/ppc/pnv_core.h | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 0314881316..0b9508d94d 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -121,9 +121,9 @@ static int get_cpus_node(void *fdt) */ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) { - CPUState *cs = CPU(DEVICE(pc->threads)); + PowerPCCPU *cpu = pc->threads[0]; + CPUState *cs = CPU(cpu); DeviceClass *dc = DEVICE_GET_CLASS(cs); - PowerPCCPU *cpu = POWERPC_CPU(cs); int smt_threads = CPU_CORE(pc)->nr_threads; CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 01f47c8037..1e40f01e98 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -151,7 +151,6 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) PnvCore *pc = PNV_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(OBJECT(dev)); const char *typename = pnv_core_cpu_typename(pc); - size_t size = object_type_get_instance_size(typename); Error *local_err = NULL; void *obj; int i, j; @@ -165,11 +164,11 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) return; } - pc->threads = g_malloc0(size * cc->nr_threads); + pc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { - obj = pc->threads + i * size; + obj = object_new(typename); - object_initialize(obj, size, typename); + pc->threads[i] = POWERPC_CPU(obj); snprintf(name, sizeof(name), "thread[%d]", i); object_property_add_child(OBJECT(pc), name, obj, &error_abort); @@ -179,7 +178,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) } for (j = 0; j < cc->nr_threads; j++) { - obj = pc->threads + j * size; + obj = OBJECT(pc->threads[j]); pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err); if (local_err) { @@ -194,7 +193,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) err: while (--i >= 0) { - obj = pc->threads + i * size; + obj = OBJECT(pc->threads[i]); object_unparent(obj); } g_free(pc->threads); diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index e337af7a3a..447ae761f7 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -34,7 +34,7 @@ typedef struct PnvCore { CPUCore parent_obj; /*< public >*/ - void *threads; + PowerPCCPU **threads; uint32_t pir; MemoryRegion xscom_regs; From 3a24752112a046a5f9745b6b72e16646b4b0bcfd Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 13:34:36 +1000 Subject: [PATCH 1350/2380] pnv: Clean up cpu realize path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pnv_cpu_init() is only called from the the pnv cpu core realize path, and really only can be called from there. So fold it into its caller, which we also rename for brevity. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/pnv_core.c | 56 ++++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 1e40f01e98..f4c41d89d6 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -54,28 +54,6 @@ static void pnv_cpu_reset(void *opaque) env->msr |= MSR_HVB; /* Hypervisor mode */ } -static void pnv_cpu_init(PowerPCCPU *cpu, Error **errp) -{ - CPUPPCState *env = &cpu->env; - int core_pir; - int thread_index = 0; /* TODO: TCG supports only one thread */ - ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; - - core_pir = object_property_get_uint(OBJECT(cpu), "core-pir", &error_abort); - - /* - * The PIR of a thread is the core PIR + the thread index. We will - * need to find a way to get the thread index when TCG supports - * more than 1. We could use the object name ? - */ - pir->default_value = core_pir + thread_index; - - /* Set time-base frequency to 512 MHz */ - cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); - - qemu_register_reset(pnv_cpu_reset, cpu); -} - /* * These values are read by the PowerNV HW monitors under Linux */ @@ -121,29 +99,39 @@ static const MemoryRegionOps pnv_core_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp) +static void pnv_realize_vcpu(PowerPCCPU *cpu, XICSFabric *xi, Error **errp) { + CPUPPCState *env = &cpu->env; + int core_pir; + int thread_index = 0; /* TODO: TCG supports only one thread */ + ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; Error *local_err = NULL; - CPUState *cs = CPU(child); - PowerPCCPU *cpu = POWERPC_CPU(cs); - object_property_set_bool(child, true, "realized", &local_err); + object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); return; } - cpu->intc = icp_create(child, TYPE_PNV_ICP, xi, &local_err); + cpu->intc = icp_create(OBJECT(cpu), TYPE_PNV_ICP, xi, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - pnv_cpu_init(cpu, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } + core_pir = object_property_get_uint(OBJECT(cpu), "core-pir", &error_abort); + + /* + * The PIR of a thread is the core PIR + the thread index. We will + * need to find a way to get the thread index when TCG supports + * more than 1. We could use the object name ? + */ + pir->default_value = core_pir + thread_index; + + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); + + qemu_register_reset(pnv_cpu_reset, cpu); } static void pnv_core_realize(DeviceState *dev, Error **errp) @@ -178,9 +166,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) } for (j = 0; j < cc->nr_threads; j++) { - obj = OBJECT(pc->threads[j]); - - pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err); + pnv_realize_vcpu(pc->threads[j], XICS_FABRIC(xi), &local_err); if (local_err) { goto err; } From 5e22e29201d80124bca0124f2034e72b698cbb6f Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 12:08:42 +1000 Subject: [PATCH 1351/2380] pnv: Add cpu unrealize path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we don't have any unrealize path for pnv cpu cores. We get away with this because we don't yet support cpu hotplug for pnv. However, we're going to want it eventually, and in the meantime, it makes it non-obvious why there are a bunch of allocations on the realize() path that don't have matching frees. So, implement the missing unrealize path. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/pnv_core.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index f4c41d89d6..f7cf33f547 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -186,6 +186,26 @@ err: error_propagate(errp, local_err); } +static void pnv_unrealize_vcpu(PowerPCCPU *cpu) +{ + qemu_unregister_reset(pnv_cpu_reset, cpu); + object_unparent(cpu->intc); + cpu_remove_sync(CPU(cpu)); + object_unparent(OBJECT(cpu)); +} + +static void pnv_core_unrealize(DeviceState *dev, Error **errp) +{ + PnvCore *pc = PNV_CORE(dev); + CPUCore *cc = CPU_CORE(dev); + int i; + + for (i = 0; i < cc->nr_threads; i++) { + pnv_unrealize_vcpu(pc->threads[i]); + } + g_free(pc->threads); +} + static Property pnv_core_properties[] = { DEFINE_PROP_UINT32("pir", PnvCore, pir, 0), DEFINE_PROP_END_OF_LIST(), @@ -196,6 +216,7 @@ static void pnv_core_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = pnv_core_realize; + dc->unrealize = pnv_core_unrealize; dc->props = pnv_core_properties; } From dbb3e8d5da028a6cc4c576c6a0960bcf740cb035 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 14 Jun 2018 23:50:11 +0200 Subject: [PATCH 1352/2380] spapr_cpu_core: convert last snprintf() to g_strdup_printf() Because this is the preferred practice in QEMU. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr_cpu_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 7fdb3b6667..ad404d13ec 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -176,7 +176,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) sc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { - char id[32]; + char *id; CPUState *cs; PowerPCCPU *cpu; @@ -194,8 +194,9 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) /* Set NUMA node for the threads belonged to core */ cpu->node_id = sc->node_id; - snprintf(id, sizeof(id), "thread[%d]", i); + id = g_strdup_printf("thread[%d]", i); object_property_add_child(OBJECT(sc), id, obj, &local_err); + g_free(id); if (local_err) { goto err; } From 27607c1cdc0d2939cc3059106f919bf6271ae652 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 14 Jun 2018 23:50:27 +0200 Subject: [PATCH 1353/2380] spapr_cpu_core: fix potential leak in spapr_cpu_core_realize() Commit 94ad93bd97684 (QEMU 2.12) switched to instantiate CPUs separately but it missed to adapt the error path accordingly. If something fails in the CPU creation loop, then the CPU object that was just created is leaked. The error paths in this function are a bit obfuscated, and adding yet another label to free this CPU object makes it worse. We should move the block of the loop to a separate function, with a proper rollback path, but this is a bigger cleanup. For now, let's just fix the bug by adding the missing calls to object_unref(). This will allow easier backport to older QEMU versions. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr_cpu_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index ad404d13ec..a9bb2d61e8 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -187,6 +187,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) cs->cpu_index = cc->core_id + i; spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err); if (local_err) { + object_unref(obj); goto err; } @@ -198,6 +199,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) object_property_add_child(OBJECT(sc), id, obj, &local_err); g_free(id); if (local_err) { + object_unref(obj); goto err; } object_unref(obj); From 9986ddec4ccdc1f6320c158be50274fe390b7bb2 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 14 Jun 2018 23:50:42 +0200 Subject: [PATCH 1354/2380] spapr_cpu_core: add missing rollback on realization path The spapr_realize_vcpu() function doesn't rollback in case of error. This isn't a problem with coldplugged CPUs because the machine won't start and QEMU will exit. Hotplug is a different story though: the CPU thread is started under object_property_set_bool() and it assumes it can access the CPU object. If icp_create() fails, we return an error without unregistering the reset handler for this CPU, and we let the underlying QEMU thread for this CPU alive. Since spapr_cpu_core_realize() doesn't care to unrealize already realized CPUs either, but happily frees all of them anyway, the CPU thread crashes instantly: (qemu) device_add host-spapr-cpu-core,core-id=1,id=gku GKU: failing icp_create (cpu 0x11497fd0) ^^^^^^^^^^ Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7fffee3feaa0 (LWP 24725)] 0x00000000104c8374 in object_dynamic_cast_assert (obj=0x11497fd0, ^^^^^^^^^^^^^^ pointer to the CPU object 623 trace_object_dynamic_cast_assert(obj ? obj->class->type->name (gdb) p obj->class->type $1 = (Type) 0x0 (gdb) p * obj $2 = {class = 0x10ea9c10, free = 0x11244620, ^^^^^^^^^^ should be g_free (gdb) p g_free $3 = {} 0x7ffff282bef0 obj is a dangling pointer to the CPU that was just destroyed in spapr_cpu_core_realize(). This patch adds proper rollback to both spapr_realize_vcpu() and spapr_cpu_core_realize(). Signed-off-by: Greg Kurz [dwg: Fixed a conflict due to a change in my tree] Signed-off-by: David Gibson --- hw/ppc/spapr_cpu_core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index a9bb2d61e8..30a7cf9e03 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -145,11 +145,14 @@ static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr, cpu->intc = icp_create(OBJECT(cpu), spapr->icp_type, XICS_FABRIC(spapr), &local_err); if (local_err) { - goto error; + goto error_unregister; } return; +error_unregister: + qemu_unregister_reset(spapr_cpu_reset, cpu); + cpu_remove_sync(CPU(cpu)); error: error_propagate(errp, local_err); } @@ -208,11 +211,15 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) for (j = 0; j < cc->nr_threads; j++) { spapr_realize_vcpu(sc->threads[j], spapr, &local_err); if (local_err) { - goto err; + goto err_unrealize; } } return; +err_unrealize: + while (--j >= 0) { + spapr_unrealize_vcpu(sc->threads[j]); + } err: while (--i >= 0) { obj = OBJECT(sc->threads[i]); From d9f0e34cb7174aefa2a1f4a7d6631c62b1e0d05d Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 14 Jun 2018 23:50:57 +0200 Subject: [PATCH 1355/2380] spapr_cpu_core: introduce spapr_create_vcpu() This moves some code out from spapr_cpu_core_realize() for clarity. No functional change. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr_cpu_core.c | 73 +++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 30a7cf9e03..98a018c8d0 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -157,6 +157,49 @@ error: error_propagate(errp, local_err); } +static PowerPCCPU *spapr_create_vcpu(sPAPRCPUCore *sc, int i, Error **errp) +{ + sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(sc); + CPUCore *cc = CPU_CORE(sc); + Object *obj; + char *id; + CPUState *cs; + PowerPCCPU *cpu; + Error *local_err = NULL; + + obj = object_new(scc->cpu_type); + + cs = CPU(obj); + cpu = POWERPC_CPU(obj); + cs->cpu_index = cc->core_id + i; + spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err); + if (local_err) { + goto err; + } + + cpu->node_id = sc->node_id; + + id = g_strdup_printf("thread[%d]", i); + object_property_add_child(OBJECT(sc), id, obj, &local_err); + g_free(id); + if (local_err) { + goto err; + } + + object_unref(obj); + return cpu; + +err: + object_unref(obj); + error_propagate(errp, local_err); + return NULL; +} + +static void spapr_delete_vcpu(PowerPCCPU *cpu) +{ + object_unparent(OBJECT(cpu)); +} + static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) { /* We don't use SPAPR_MACHINE() in order to exit gracefully if the user @@ -166,10 +209,8 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) (sPAPRMachineState *) object_dynamic_cast(qdev_get_machine(), TYPE_SPAPR_MACHINE); sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); - sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(OBJECT(dev)); CPUCore *cc = CPU_CORE(OBJECT(dev)); Error *local_err = NULL; - Object *obj; int i, j; if (!spapr) { @@ -179,33 +220,10 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) sc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { - char *id; - CPUState *cs; - PowerPCCPU *cpu; - - obj = object_new(scc->cpu_type); - - cs = CPU(obj); - cpu = sc->threads[i] = POWERPC_CPU(obj); - cs->cpu_index = cc->core_id + i; - spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err); + sc->threads[i] = spapr_create_vcpu(sc, i, &local_err); if (local_err) { - object_unref(obj); goto err; } - - - /* Set NUMA node for the threads belonged to core */ - cpu->node_id = sc->node_id; - - id = g_strdup_printf("thread[%d]", i); - object_property_add_child(OBJECT(sc), id, obj, &local_err); - g_free(id); - if (local_err) { - object_unref(obj); - goto err; - } - object_unref(obj); } for (j = 0; j < cc->nr_threads; j++) { @@ -222,8 +240,7 @@ err_unrealize: } err: while (--i >= 0) { - obj = OBJECT(sc->threads[i]); - object_unparent(obj); + spapr_delete_vcpu(sc->threads[i]); } g_free(sc->threads); error_propagate(errp, local_err); From 51c047283c567ea27084827c979681f3e28b920e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 14 Jun 2018 16:00:41 +0200 Subject: [PATCH 1356/2380] ppc/pnv: introduce a pnv_chip_core_realize() routine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This extracts from the PvChip realize routine the part creating the cores. On Power9, we will need to create the cores after the Xive interrupt controller is created. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/pnv.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 0b9508d94d..0d2b79f798 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -849,9 +849,8 @@ static void pnv_chip_icp_realize(PnvChip *chip, Error **errp) } } -static void pnv_chip_realize(DeviceState *dev, Error **errp) +static void pnv_chip_core_realize(PnvChip *chip, Error **errp) { - PnvChip *chip = PNV_CHIP(dev); Error *error = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); const char *typename = pnv_chip_core_typename(chip); @@ -863,14 +862,6 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) return; } - /* XSCOM bridge */ - pnv_xscom_realize(chip, &error); - if (error) { - error_propagate(errp, error); - return; - } - sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip)); - /* Cores */ pnv_chip_core_sanitize(chip, &error); if (error) { @@ -918,6 +909,27 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) &PNV_CORE(pnv_core)->xscom_regs); i++; } +} + +static void pnv_chip_realize(DeviceState *dev, Error **errp) +{ + PnvChip *chip = PNV_CHIP(dev); + Error *error = NULL; + + /* XSCOM bridge */ + pnv_xscom_realize(chip, &error); + if (error) { + error_propagate(errp, error); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip)); + + /* Cores */ + pnv_chip_core_realize(chip, &error); + if (error) { + error_propagate(errp, error); + return; + } /* Create LPC controller */ object_property_set_bool(OBJECT(&chip->lpc), true, "realized", From 7388efafc27c2f45d22c8edbc14b3154c0381c2e Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 16:22:18 +1000 Subject: [PATCH 1357/2380] target/ppc, spapr: Move VPA information to machine_data CPUPPCState currently contains a number of fields containing the state of the VPA. The VPA is a PAPR specific concept covering several guest/host shared memory areas used to communicate some information with the hypervisor. As a PAPR concept this is really machine specific information, although it is per-cpu, so it doesn't really belong in the core CPU state structure. There's also other information that's per-cpu, but platform/machine specific. So create a (void *)machine_data in PowerPCCPU which can be used by the machine to locate per-cpu data. Intialization, lifetime and cleanup of machine_data is entirely up to the machine type. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Tested-by: Greg Kurz --- hw/ppc/spapr_cpu_core.c | 13 ++++++ hw/ppc/spapr_hcall.c | 77 ++++++++++++++++++--------------- include/hw/ppc/spapr_cpu_core.h | 11 +++++ target/ppc/cpu.h | 7 +-- target/ppc/kvm.c | 39 +++++++++-------- target/ppc/translate_init.inc.c | 8 ---- 6 files changed, 88 insertions(+), 67 deletions(-) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 98a018c8d0..aef3be33a3 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -28,6 +28,7 @@ static void spapr_cpu_reset(void *opaque) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); target_ulong lpcr; cpu_reset(cs); @@ -69,6 +70,12 @@ static void spapr_cpu_reset(void *opaque) /* Set a full AMOR so guest can use the AMR as it sees fit */ env->spr[SPR_AMOR] = 0xffffffffffffffffull; + + spapr_cpu->vpa_addr = 0; + spapr_cpu->slb_shadow_addr = 0; + spapr_cpu->slb_shadow_size = 0; + spapr_cpu->dtl_addr = 0; + spapr_cpu->dtl_size = 0; } void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) @@ -186,6 +193,8 @@ static PowerPCCPU *spapr_create_vcpu(sPAPRCPUCore *sc, int i, Error **errp) goto err; } + cpu->machine_data = g_new0(sPAPRCPUState, 1); + object_unref(obj); return cpu; @@ -197,6 +206,10 @@ err: static void spapr_delete_vcpu(PowerPCCPU *cpu) { + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + cpu->machine_data = NULL; + g_free(spapr_cpu); object_unparent(OBJECT(cpu)); } diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 8b9a4b577f..ae913d070f 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -8,6 +8,7 @@ #include "exec/exec-all.h" #include "helper_regs.h" #include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_cpu_core.h" #include "mmu-hash64.h" #include "cpu-models.h" #include "trace.h" @@ -908,9 +909,11 @@ unmap_out: #define VPA_SHARED_PROC_OFFSET 0x9 #define VPA_SHARED_PROC_VAL 0x2 -static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) +static target_ulong register_vpa(PowerPCCPU *cpu, target_ulong vpa) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); uint16_t size; uint8_t tmp; @@ -935,32 +938,34 @@ static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) return H_PARAMETER; } - env->vpa_addr = vpa; + spapr_cpu->vpa_addr = vpa; - tmp = ldub_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET); + tmp = ldub_phys(cs->as, spapr_cpu->vpa_addr + VPA_SHARED_PROC_OFFSET); tmp |= VPA_SHARED_PROC_VAL; - stb_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp); + stb_phys(cs->as, spapr_cpu->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp); return H_SUCCESS; } -static target_ulong deregister_vpa(CPUPPCState *env, target_ulong vpa) +static target_ulong deregister_vpa(PowerPCCPU *cpu, target_ulong vpa) { - if (env->slb_shadow_addr) { + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + if (spapr_cpu->slb_shadow_addr) { return H_RESOURCE; } - if (env->dtl_addr) { + if (spapr_cpu->dtl_addr) { return H_RESOURCE; } - env->vpa_addr = 0; + spapr_cpu->vpa_addr = 0; return H_SUCCESS; } -static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) +static target_ulong register_slb_shadow(PowerPCCPU *cpu, target_ulong addr) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); uint32_t size; if (addr == 0) { @@ -968,7 +973,7 @@ static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) return H_HARDWARE; } - size = ldl_be_phys(cs->as, addr + 0x4); + size = ldl_be_phys(CPU(cpu)->as, addr + 0x4); if (size < 0x8) { return H_PARAMETER; } @@ -977,26 +982,28 @@ static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) return H_PARAMETER; } - if (!env->vpa_addr) { + if (!spapr_cpu->vpa_addr) { return H_RESOURCE; } - env->slb_shadow_addr = addr; - env->slb_shadow_size = size; + spapr_cpu->slb_shadow_addr = addr; + spapr_cpu->slb_shadow_size = size; return H_SUCCESS; } -static target_ulong deregister_slb_shadow(CPUPPCState *env, target_ulong addr) +static target_ulong deregister_slb_shadow(PowerPCCPU *cpu, target_ulong addr) { - env->slb_shadow_addr = 0; - env->slb_shadow_size = 0; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + spapr_cpu->slb_shadow_addr = 0; + spapr_cpu->slb_shadow_size = 0; return H_SUCCESS; } -static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) +static target_ulong register_dtl(PowerPCCPU *cpu, target_ulong addr) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); uint32_t size; if (addr == 0) { @@ -1004,26 +1011,28 @@ static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) return H_HARDWARE; } - size = ldl_be_phys(cs->as, addr + 0x4); + size = ldl_be_phys(CPU(cpu)->as, addr + 0x4); if (size < 48) { return H_PARAMETER; } - if (!env->vpa_addr) { + if (!spapr_cpu->vpa_addr) { return H_RESOURCE; } - env->dtl_addr = addr; - env->dtl_size = size; + spapr_cpu->dtl_addr = addr; + spapr_cpu->dtl_size = size; return H_SUCCESS; } -static target_ulong deregister_dtl(CPUPPCState *env, target_ulong addr) +static target_ulong deregister_dtl(PowerPCCPU *cpu, target_ulong addr) { - env->dtl_addr = 0; - env->dtl_size = 0; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + spapr_cpu->dtl_addr = 0; + spapr_cpu->dtl_size = 0; return H_SUCCESS; } @@ -1035,38 +1044,36 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong procno = args[1]; target_ulong vpa = args[2]; target_ulong ret = H_PARAMETER; - CPUPPCState *tenv; PowerPCCPU *tcpu; tcpu = spapr_find_cpu(procno); if (!tcpu) { return H_PARAMETER; } - tenv = &tcpu->env; switch (flags) { case FLAGS_REGISTER_VPA: - ret = register_vpa(tenv, vpa); + ret = register_vpa(tcpu, vpa); break; case FLAGS_DEREGISTER_VPA: - ret = deregister_vpa(tenv, vpa); + ret = deregister_vpa(tcpu, vpa); break; case FLAGS_REGISTER_SLBSHADOW: - ret = register_slb_shadow(tenv, vpa); + ret = register_slb_shadow(tcpu, vpa); break; case FLAGS_DEREGISTER_SLBSHADOW: - ret = deregister_slb_shadow(tenv, vpa); + ret = deregister_slb_shadow(tcpu, vpa); break; case FLAGS_REGISTER_DTL: - ret = register_dtl(tenv, vpa); + ret = register_dtl(tcpu, vpa); break; case FLAGS_DEREGISTER_DTL: - ret = deregister_dtl(tenv, vpa); + ret = deregister_dtl(tcpu, vpa); break; } diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h index 47dcfda12b..8ceea2973a 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -41,4 +41,15 @@ typedef struct sPAPRCPUCoreClass { const char *spapr_get_cpu_core_type(const char *cpu_type); void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3); +typedef struct sPAPRCPUState { + uint64_t vpa_addr; + uint64_t slb_shadow_addr, slb_shadow_size; + uint64_t dtl_addr, dtl_size; +} sPAPRCPUState; + +static inline sPAPRCPUState *spapr_cpu_state(PowerPCCPU *cpu) +{ + return (sPAPRCPUState *)cpu->machine_data; +} + #endif diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index a91f1a8777..874da6efbc 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1091,12 +1091,6 @@ struct CPUPPCState { target_ulong rmls; #endif -#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) - uint64_t vpa_addr; - uint64_t slb_shadow_addr, slb_shadow_size; - uint64_t dtl_addr, dtl_size; -#endif /* TARGET_PPC64 */ - int error_code; uint32_t pending_interrupts; #if !defined(CONFIG_USER_ONLY) @@ -1205,6 +1199,7 @@ struct PowerPCCPU { uint32_t compat_pvr; PPCVirtualHypervisor *vhyp; Object *intc; + void *machine_data; int32_t node_id; /* NUMA node this CPU belongs to */ PPCHash64Options *hash64_opts; diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 7fe9d0126b..5c0e313ca6 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -829,22 +829,22 @@ static int kvm_get_fp(CPUState *cs) static int kvm_get_vpa(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); struct kvm_one_reg reg; int ret; reg.id = KVM_REG_PPC_VPA_ADDR; - reg.addr = (uintptr_t)&env->vpa_addr; + reg.addr = (uintptr_t)&spapr_cpu->vpa_addr; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to get VPA address from KVM: %s\n", strerror(errno)); return ret; } - assert((uintptr_t)&env->slb_shadow_size - == ((uintptr_t)&env->slb_shadow_addr + 8)); + assert((uintptr_t)&spapr_cpu->slb_shadow_size + == ((uintptr_t)&spapr_cpu->slb_shadow_addr + 8)); reg.id = KVM_REG_PPC_VPA_SLB; - reg.addr = (uintptr_t)&env->slb_shadow_addr; + reg.addr = (uintptr_t)&spapr_cpu->slb_shadow_addr; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to get SLB shadow state from KVM: %s\n", @@ -852,9 +852,10 @@ static int kvm_get_vpa(CPUState *cs) return ret; } - assert((uintptr_t)&env->dtl_size == ((uintptr_t)&env->dtl_addr + 8)); + assert((uintptr_t)&spapr_cpu->dtl_size + == ((uintptr_t)&spapr_cpu->dtl_addr + 8)); reg.id = KVM_REG_PPC_VPA_DTL; - reg.addr = (uintptr_t)&env->dtl_addr; + reg.addr = (uintptr_t)&spapr_cpu->dtl_addr; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to get dispatch trace log state from KVM: %s\n", @@ -868,7 +869,7 @@ static int kvm_get_vpa(CPUState *cs) static int kvm_put_vpa(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); struct kvm_one_reg reg; int ret; @@ -876,11 +877,12 @@ static int kvm_put_vpa(CPUState *cs) * registered. That means when restoring state, if a VPA *is* * registered, we need to set that up first. If not, we need to * deregister the others before deregistering the master VPA */ - assert(env->vpa_addr || !(env->slb_shadow_addr || env->dtl_addr)); + assert(spapr_cpu->vpa_addr + || !(spapr_cpu->slb_shadow_addr || spapr_cpu->dtl_addr)); - if (env->vpa_addr) { + if (spapr_cpu->vpa_addr) { reg.id = KVM_REG_PPC_VPA_ADDR; - reg.addr = (uintptr_t)&env->vpa_addr; + reg.addr = (uintptr_t)&spapr_cpu->vpa_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to set VPA address to KVM: %s\n", strerror(errno)); @@ -888,19 +890,20 @@ static int kvm_put_vpa(CPUState *cs) } } - assert((uintptr_t)&env->slb_shadow_size - == ((uintptr_t)&env->slb_shadow_addr + 8)); + assert((uintptr_t)&spapr_cpu->slb_shadow_size + == ((uintptr_t)&spapr_cpu->slb_shadow_addr + 8)); reg.id = KVM_REG_PPC_VPA_SLB; - reg.addr = (uintptr_t)&env->slb_shadow_addr; + reg.addr = (uintptr_t)&spapr_cpu->slb_shadow_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to set SLB shadow state to KVM: %s\n", strerror(errno)); return ret; } - assert((uintptr_t)&env->dtl_size == ((uintptr_t)&env->dtl_addr + 8)); + assert((uintptr_t)&spapr_cpu->dtl_size + == ((uintptr_t)&spapr_cpu->dtl_addr + 8)); reg.id = KVM_REG_PPC_VPA_DTL; - reg.addr = (uintptr_t)&env->dtl_addr; + reg.addr = (uintptr_t)&spapr_cpu->dtl_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to set dispatch trace log state to KVM: %s\n", @@ -908,9 +911,9 @@ static int kvm_put_vpa(CPUState *cs) return ret; } - if (!env->vpa_addr) { + if (!spapr_cpu->vpa_addr) { reg.id = KVM_REG_PPC_VPA_ADDR; - reg.addr = (uintptr_t)&env->vpa_addr; + reg.addr = (uintptr_t)&spapr_cpu->vpa_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to set VPA address to KVM: %s\n", strerror(errno)); diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index bb9296f5a3..76d6f3fd5e 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -10316,14 +10316,6 @@ static void ppc_cpu_reset(CPUState *s) s->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; -#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) - env->vpa_addr = 0; - env->slb_shadow_addr = 0; - env->slb_shadow_size = 0; - env->dtl_addr = 0; - env->dtl_size = 0; -#endif /* TARGET_PPC64 */ - for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { ppc_spr_t *spr = &env->spr_cb[i]; From 75cacb128b2d39863a72f60cbefc0987cbcaf641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Jun 2018 20:39:35 -0300 Subject: [PATCH 1358/2380] hw/isa/smc37c669: Change the parallel I/O base to 378H MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the Alpha DP264 machine, the Cirrus VGA is I/O mapped in the 3C0H-3CFH range, thus I/O base used by the parallel device clashes, and since a4cb773928e the VGA is not working: (qemu) info mtree address-space: memory 0000000000000000-ffffffffffffffff (prio 0, i/o): system 00000801fc000000-00000801fdffffff (prio 0, i/o): pci0-io ... 00000801fc0003b4-00000801fc0003b5 (prio 0, i/o): vga 00000801fc0003ba-00000801fc0003ba (prio 0, i/o): vga 00000801fc0003bc-00000801fc0003c3 (prio 0, i/o): parallel ^^^ ^^^^^^^^ 00000801fc0003c0-00000801fc0003cf (prio 0, i/o): vga ^^^ 00000801fc0003d4-00000801fc0003d5 (prio 0, i/o): vga 00000801fc0003da-00000801fc0003da (prio 0, i/o): vga ... As there is no particular reason to use this base address (introduced in 7bea0dd434e), change to 378H which is the default on PC machines. Reported-by: Emilio G. Cota Suggested-by: Paolo Bonzini Tested-by: Emilio G. Cota Reviewed-by: Richard Henderson Reviewed-by: Mark Cave-Ayland Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180614233935.26585-1-f4bug@amsat.org> Signed-off-by: Richard Henderson --- hw/isa/smc37c669-superio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/isa/smc37c669-superio.c b/hw/isa/smc37c669-superio.c index aa233c6967..64466a9373 100644 --- a/hw/isa/smc37c669-superio.c +++ b/hw/isa/smc37c669-superio.c @@ -37,7 +37,7 @@ static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index) static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index) { - return 0x3bc; + return 0x378; } static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index) From 92b19880f7f9cf7f4a9e00dc3258b8d3aa363e9d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 5 Apr 2018 11:32:30 +0200 Subject: [PATCH 1359/2380] hw/sparc64/sun4u: Fix introspection by converting prom instance_init to realize The instance_init function of devices should always succeed to be able to introspect the device. However, the instance_init function of the "openprom" device can currently fail, for example like this: $ echo "{'execute':'qmp_capabilities'}"\ "{'execute':'device-list-properties',"\ " 'arguments':{'typename':'openprom'}}" \ | sparc64-softmmu/qemu-system-sparc64 -M sun4v,accel=qtest -qmp stdio {"QMP": {"version": {"qemu": {"micro": 91, "minor": 11, "major": 2}, "package": "build-all"}, "capabilities": []}} {"return": {}} RAMBlock "sun4u.prom" already registered, abort! Aborted (core dumped) This should not happen. Fix this problem by moving the affected code from instance_init into a realize function instead. Signed-off-by: Thomas Huth Signed-off-by: Mark Cave-Ayland --- hw/sparc64/sun4u.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 1bede85370..3975a7b65a 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -425,13 +425,19 @@ static void prom_init(hwaddr addr, const char *bios_name) } } -static void prom_init1(Object *obj) +static void prom_realize(DeviceState *ds, Error **errp) { - PROMState *s = OPENPROM(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); + PROMState *s = OPENPROM(ds); + SysBusDevice *dev = SYS_BUS_DEVICE(ds); + Error *local_err = NULL; + + memory_region_init_ram_nomigrate(&s->prom, OBJECT(ds), "sun4u.prom", + PROM_SIZE_MAX, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - memory_region_init_ram_nomigrate(&s->prom, obj, "sun4u.prom", PROM_SIZE_MAX, - &error_fatal); vmstate_register_ram_global(&s->prom); memory_region_set_readonly(&s->prom, true); sysbus_init_mmio(dev, &s->prom); @@ -446,6 +452,7 @@ static void prom_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->props = prom_properties; + dc->realize = prom_realize; } static const TypeInfo prom_info = { @@ -453,7 +460,6 @@ static const TypeInfo prom_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PROMState), .class_init = prom_class_init, - .instance_init = prom_init1, }; From a2a5a7b5e21da225e91304dd0816d0c8f7d40bb9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 5 Apr 2018 12:43:03 +0200 Subject: [PATCH 1360/2380] hw/sparc/sun4m: Fix problems with device introspection Several devices of the sun4m machines are using &error_fatal in their instance_init function and thus can cause QEMU to abort unexpectedly: $ echo "{'execute':'qmp_capabilities'}"\ "{'execute':'device-list-properties',"\ " 'arguments':{'typename':'openprom'}}" \ | sparc-softmmu/qemu-system-sparc -M SS-10 -S -qmp stdio {"QMP": {"version": {"qemu": {"micro": 91, "minor": 11, "major": 2}, "package": "build-all"}, "capabilities": []}} {"return": {}} RAMBlock "sun4m.prom" already registered, abort! Aborted (core dumped) $ echo "{'execute':'qmp_capabilities'}"\ "{'execute':'device-list-properties',"\ " 'arguments':{'typename':'macio_idreg'}}" \ | sparc-softmmu/qemu-system-sparc -M SS-10 -S -qmp stdio {"QMP": {"version": {"qemu": {"micro": 91, "minor": 11, "major": 2}, "package": "build-all"}, "capabilities": []}} {"return": {}} RAMBlock "sun4m.idreg" already registered, abort! Aborted (core dumped) $ echo "{'execute':'qmp_capabilities'}"\ "{'execute':'device-list-properties',"\ " 'arguments':{'typename':'tcx_afx'}}" \ | sparc-softmmu/qemu-system-sparc -M SS-5 -S -qmp stdio {"QMP": {"version": {"qemu": {"micro": 91, "minor": 11, "major": 2}, "package": "build-all"}, "capabilities": []}} {"return": {}} RAMBlock "sun4m.afx" already registered, abort! Aborted (core dumped) Fix the issues by converting the instance_init functions into realize() functions instead, which are allowed to fail (and not called during device introspection). Signed-off-by: Thomas Huth Signed-off-by: Mark Cave-Ayland --- hw/sparc/sun4m.c | 67 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 0ee779fafe..b984d2da0e 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -572,23 +572,36 @@ typedef struct IDRegState { MemoryRegion mem; } IDRegState; -static void idreg_init1(Object *obj) +static void idreg_realize(DeviceState *ds, Error **errp) { - IDRegState *s = MACIO_ID_REGISTER(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); + IDRegState *s = MACIO_ID_REGISTER(ds); + SysBusDevice *dev = SYS_BUS_DEVICE(ds); + Error *local_err = NULL; + + memory_region_init_ram_nomigrate(&s->mem, OBJECT(ds), "sun4m.idreg", + sizeof(idreg_data), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - memory_region_init_ram_nomigrate(&s->mem, obj, - "sun4m.idreg", sizeof(idreg_data), &error_fatal); vmstate_register_ram_global(&s->mem); memory_region_set_readonly(&s->mem, true); sysbus_init_mmio(dev, &s->mem); } +static void idreg_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = idreg_realize; +} + static const TypeInfo idreg_info = { .name = TYPE_MACIO_ID_REGISTER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IDRegState), - .instance_init = idreg_init1, + .class_init = idreg_class_init, }; #define TYPE_TCX_AFX "tcx_afx" @@ -613,21 +626,35 @@ static void afx_init(hwaddr addr) sysbus_mmio_map(s, 0, addr); } -static void afx_init1(Object *obj) +static void afx_realize(DeviceState *ds, Error **errp) { - AFXState *s = TCX_AFX(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); + AFXState *s = TCX_AFX(ds); + SysBusDevice *dev = SYS_BUS_DEVICE(ds); + Error *local_err = NULL; + + memory_region_init_ram_nomigrate(&s->mem, OBJECT(ds), "sun4m.afx", 4, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - memory_region_init_ram_nomigrate(&s->mem, obj, "sun4m.afx", 4, &error_fatal); vmstate_register_ram_global(&s->mem); sysbus_init_mmio(dev, &s->mem); } +static void afx_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = afx_realize; +} + static const TypeInfo afx_info = { .name = TYPE_TCX_AFX, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AFXState), - .instance_init = afx_init1, + .class_init = afx_class_init, }; #define TYPE_OPENPROM "openprom" @@ -680,13 +707,19 @@ static void prom_init(hwaddr addr, const char *bios_name) } } -static void prom_init1(Object *obj) +static void prom_realize(DeviceState *ds, Error **errp) { - PROMState *s = OPENPROM(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); + PROMState *s = OPENPROM(ds); + SysBusDevice *dev = SYS_BUS_DEVICE(ds); + Error *local_err = NULL; + + memory_region_init_ram_nomigrate(&s->prom, OBJECT(ds), "sun4m.prom", + PROM_SIZE_MAX, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - memory_region_init_ram_nomigrate(&s->prom, obj, "sun4m.prom", PROM_SIZE_MAX, - &error_fatal); vmstate_register_ram_global(&s->prom); memory_region_set_readonly(&s->prom, true); sysbus_init_mmio(dev, &s->prom); @@ -701,6 +734,7 @@ static void prom_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->props = prom_properties; + dc->realize = prom_realize; } static const TypeInfo prom_info = { @@ -708,7 +742,6 @@ static const TypeInfo prom_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PROMState), .class_init = prom_class_init, - .instance_init = prom_init1, }; #define TYPE_SUN4M_MEMORY "memory" From 46bb0137b8266ab8dedc3b9712f3d246bbd686ba Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 15 Jun 2018 08:33:03 +0100 Subject: [PATCH 1361/2380] SPARC64: add icount support This patch adds gen_io_start()/gen_io_end() to various instructions as required in order to boot my OpenBIOS test images on qemu-system-sparc64 with icount enabled. Signed-off-by: Mark Cave-Ayland Reviewed-by: Richard Henderson Signed-off-by: Mark Cave-Ayland --- target/sparc/translate.c | 111 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index f3d430c1b2..74315cdf09 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -41,6 +41,8 @@ #define JUMP_PC 2 /* dynamic pc value which takes only two values according to jump_pc[T2] */ +#define DISAS_EXIT DISAS_TARGET_0 + /* global register indexes */ static TCGv_ptr cpu_regwptr; static TCGv cpu_cc_src, cpu_cc_src2, cpu_cc_dst; @@ -3400,11 +3402,17 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_const = tcg_const_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, r_const); tcg_temp_free_ptr(r_tickptr); tcg_temp_free_i32(r_const); gen_store_gpr(dc, rd, cpu_dst); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } } break; case 0x5: /* V9 rdpc */ @@ -3447,11 +3455,17 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_const = tcg_const_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, stick)); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, r_const); tcg_temp_free_ptr(r_tickptr); tcg_temp_free_i32(r_const); gen_store_gpr(dc, rd, cpu_dst); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } } break; case 0x19: /* System tick compare */ @@ -3576,10 +3590,16 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_const = tcg_const_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_get_count(cpu_tmp0, cpu_env, r_tickptr, r_const); tcg_temp_free_ptr(r_tickptr); tcg_temp_free_i32(r_const); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } } break; case 5: // tba @@ -4385,9 +4405,19 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_set_limit(r_tickptr, cpu_tick_cmpr); tcg_temp_free_ptr(r_tickptr); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_end(); + } + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; } break; case 0x18: /* System tick */ @@ -4403,9 +4433,19 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, stick)); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_set_count(r_tickptr, cpu_tmp0); tcg_temp_free_ptr(r_tickptr); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_end(); + } + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; } break; case 0x19: /* System tick compare */ @@ -4421,9 +4461,19 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, stick)); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_set_limit(r_tickptr, cpu_stick_cmpr); tcg_temp_free_ptr(r_tickptr); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_end(); + } + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; } break; @@ -4531,9 +4581,19 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_set_count(r_tickptr, cpu_tmp0); tcg_temp_free_ptr(r_tickptr); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_end(); + } + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; } break; case 5: // tba @@ -4541,7 +4601,13 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) break; case 6: // pstate save_state(dc); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_wrpstate(cpu_env, cpu_tmp0); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } dc->npc = DYNAMIC_PC; break; case 7: // tl @@ -4551,7 +4617,13 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) dc->npc = DYNAMIC_PC; break; case 8: // pil + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_wrpil(cpu_env, cpu_tmp0); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } break; case 9: // cwp gen_helper_wrcwp(cpu_env, cpu_tmp0); @@ -4642,9 +4714,19 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, hstick)); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_set_limit(r_tickptr, cpu_hstick_cmpr); tcg_temp_free_ptr(r_tickptr); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_end(); + } + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; } break; case 6: // hver readonly @@ -5265,14 +5347,26 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) goto priv_insn; dc->npc = DYNAMIC_PC; dc->pc = DYNAMIC_PC; + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_done(cpu_env); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } goto jmp_insn; case 1: if (!supervisor(dc)) goto priv_insn; dc->npc = DYNAMIC_PC; dc->pc = DYNAMIC_PC; + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_retry(cpu_env); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } goto jmp_insn; default: goto illegal_insn; @@ -5822,7 +5916,9 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - if (dc->base.is_jmp != DISAS_NORETURN) { + switch (dc->base.is_jmp) { + case DISAS_NEXT: + case DISAS_TOO_MANY: if (dc->pc != DYNAMIC_PC && (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) { /* static PC and NPC: we can use direct chaining */ @@ -5834,6 +5930,19 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) save_npc(dc); tcg_gen_exit_tb(NULL, 0); } + break; + + case DISAS_NORETURN: + break; + + case DISAS_EXIT: + /* Exit TB */ + save_state(dc); + tcg_gen_exit_tb(NULL, 0); + break; + + default: + g_assert_not_reached(); } } From 844afc54ae229515a37f63519855661ad2d01d19 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Fri, 15 Jun 2018 18:58:00 +0200 Subject: [PATCH 1362/2380] spapr: fix xics_system_init() error path Commit 3d85885a1b1f3 tried to fix error handling, but it actually went into the wrong direction by dropping the local Error *. In the default KVM case, the rationale is to try the in-kernel XICS first, and if not possible, to fallback to userland XICS. Passing errp everywhere makes this fallback impossible if errp is &error_fatal (which happens to be the case). And anyway, if the caller would pass a regular &local_err, things would be worse: we could possibly pass an already set *errp to error_setg() and crash, or return an error even in case of success. So we definitely need a local Error * and only propagate it when we're done with the fallback logic. This is what this patch does. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index f59999daac..db0fb385d4 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -186,27 +186,33 @@ static int xics_max_server_number(sPAPRMachineState *spapr) static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + Error *local_err = NULL; if (kvm_enabled()) { if (machine_kernel_irqchip_allowed(machine) && - !xics_kvm_init(spapr, errp)) { + !xics_kvm_init(spapr, &local_err)) { spapr->icp_type = TYPE_KVM_ICP; - spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, errp); + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, + &local_err); } if (machine_kernel_irqchip_required(machine) && !spapr->ics) { - error_prepend(errp, "kernel_irqchip requested but unavailable: "); - return; + error_prepend(&local_err, + "kernel_irqchip requested but unavailable: "); + goto error; } + error_free(local_err); + local_err = NULL; } if (!spapr->ics) { xics_spapr_init(spapr); spapr->icp_type = TYPE_ICP; - spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, errp); - if (!spapr->ics) { - return; - } + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, + &local_err); } + +error: + error_propagate(errp, local_err); } static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, From a1738cd8c58afe43dc610ca778f52f1549ff3a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 13 Jun 2018 19:28:13 +0200 Subject: [PATCH 1363/2380] Revert "usb-ccid: fix bus leak" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bus is not owned by the device. This reverts commit 410a096adf991ce437d4d7dabc59b6557e6d488d. Signed-off-by: Marc-André Lureau Message-id: 20180613172815.32738-2-marcandre.lureau@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/dev-smartcard-reader.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index fa546fb3ce..13d0befd9c 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1147,7 +1147,6 @@ static void ccid_unrealize(USBDevice *dev, Error **errp) USBCCIDState *s = USB_CCID_DEV(dev); ccid_bulk_in_clear(s); - object_unref(OBJECT(&s->bus)); } static void ccid_flush_pending_answers(USBCCIDState *s) From 9b5c2fd53feb574036747d0284fd7f73dfedc89c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 13 Jun 2018 19:28:14 +0200 Subject: [PATCH 1364/2380] Revert "usb: release the created buses" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The USB device don't hold the bus. There is no ASAN related reports anymore. This reverts commit cd7bc87868d534f95e928cad98e2a52df7695771. Signed-off-by: Marc-André Lureau Message-id: 20180613172815.32738-3-marcandre.lureau@redhat.com Signed-off-by: Gerd Hoffmann --- hw/usb/dev-storage.c | 16 ---------------- hw/usb/dev-uas.c | 2 -- 2 files changed, 18 deletions(-) diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 47b992f403..c99398b7f6 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -588,13 +588,6 @@ static const struct SCSIBusInfo usb_msd_scsi_info_bot = { .load_request = usb_msd_load_request, }; -static void usb_msd_unrealize_storage(USBDevice *dev, Error **errp) -{ - MSDState *s = USB_STORAGE_DEV(dev); - - object_unref(OBJECT(&s->bus)); -} - static void usb_msd_storage_realize(USBDevice *dev, Error **errp) { MSDState *s = USB_STORAGE_DEV(dev); @@ -642,13 +635,6 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) s->scsi_dev = scsi_dev; } -static void usb_msd_bot_unrealize(USBDevice *dev, Error **errp) -{ - MSDState *s = USB_STORAGE_DEV(dev); - - object_unref(OBJECT(&s->bus)); -} - static void usb_msd_bot_realize(USBDevice *dev, Error **errp) { MSDState *s = USB_STORAGE_DEV(dev); @@ -712,7 +698,6 @@ static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data) USBDeviceClass *uc = USB_DEVICE_CLASS(klass); uc->realize = usb_msd_storage_realize; - uc->unrealize = usb_msd_unrealize_storage; dc->props = msd_properties; } @@ -775,7 +760,6 @@ static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data) USBDeviceClass *uc = USB_DEVICE_CLASS(klass); uc->realize = usb_msd_bot_realize; - uc->unrealize = usb_msd_bot_unrealize; uc->attached_settable = true; } diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index aaf5a88095..be566cad02 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -896,8 +896,6 @@ static void usb_uas_unrealize(USBDevice *dev, Error **errp) UASDevice *uas = USB_UAS(dev); qemu_bh_delete(uas->status_bh); - - object_unref(OBJECT(&uas->bus)); } static void usb_uas_realize(USBDevice *dev, Error **errp) From 47479c55b094291efa2c85fcaf97cd9241e5a8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 25 May 2018 17:36:09 +0200 Subject: [PATCH 1365/2380] configure: print virglrenderer version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Ján Tomko Message-id: 20180525153609.13187-1-marcandre.lureau@redhat.com Signed-off-by: Gerd Hoffmann --- configure | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure b/configure index a8c4094c87..6b2bcb3a0d 100755 --- a/configure +++ b/configure @@ -4599,6 +4599,7 @@ int main(void) { virgl_renderer_poll(); return 0; } EOF virgl_cflags=$($pkg_config --cflags virglrenderer 2>/dev/null) virgl_libs=$($pkg_config --libs virglrenderer 2>/dev/null) + virgl_version=$($pkg_config --modversion virglrenderer 2>/dev/null) if $pkg_config virglrenderer >/dev/null 2>&1 && \ compile_prog "$virgl_cflags" "$virgl_libs" ; then virglrenderer="yes" @@ -5827,7 +5828,7 @@ echo "nettle $nettle $(echo_version $nettle $nettle_version)" echo "nettle kdf $nettle_kdf" echo "libtasn1 $tasn1" echo "curses support $curses" -echo "virgl support $virglrenderer" +echo "virgl support $virglrenderer $(echo_version $virglrenderer $virgl_version)" echo "curl support $curl" echo "mingw32 support $mingw32" echo "Audio drivers $audio_drv_list" From 0d1e8d6f4a2ced75f673edfe4d6cb78f3b361144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 13 Jun 2018 19:28:15 +0200 Subject: [PATCH 1366/2380] Revert "bus: do not unref the added child bus on realize" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is wrong. object_finalize_child_property()'s unref balances the ref in object_property_add_child(). qbus_realize's unref balances the ref that was initially placed by object_new/object_initialize. This reverts commit f3d58385a6d3d82f65db602c5506e2d3d8c82394. Reported-by: Paolo Bonzini Signed-off-by: Marc-André Lureau Message-id: 20180613172815.32738-4-marcandre.lureau@redhat.com Signed-off-by: Gerd Hoffmann --- hw/core/bus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/core/bus.c b/hw/core/bus.c index ad0c9df335..4651f24486 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -102,6 +102,7 @@ static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); bus->parent->num_child_bus++; object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL); + object_unref(OBJECT(bus)); } else if (bus != sysbus_get_default()) { /* TODO: once all bus devices are qdevified, only reset handler for main_system_bus should be registered here. */ From 7a5342e7ccf2deb5553951024d57ec1327c926dd Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Wed, 16 May 2018 15:27:57 +0200 Subject: [PATCH 1367/2380] virtio-ccw: clean up notify Coverity recently started complaining about virtio_ccw_notify(). Turns out, there is a couple of things that can be cleaned up. Let's clean! Reported-by: Peter Maydell Fixes: CID 1390619 Signed-off-by: Halil Pasic Message-Id: <20180516132757.68558-1-pasic@linux.ibm.com> Signed-off-by: Cornelia Huck --- hw/s390x/virtio-ccw.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 0a9bec484b..b92a85d0b0 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1001,10 +1001,15 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) SubchDev *sch = ccw_dev->sch; uint64_t indicators; - /* queue indicators + secondary indicators */ - if (vector >= VIRTIO_QUEUE_MAX + 64) { + if (vector == VIRTIO_NO_VECTOR) { return; } + /* + * vector < VIRTIO_QUEUE_MAX: notification for a virtqueue + * vector == VIRTIO_QUEUE_MAX: configuration change notification + * bits beyond that are unused and should never be notified for + */ + assert(vector <= VIRTIO_QUEUE_MAX); if (vector < VIRTIO_QUEUE_MAX) { if (!dev->indicators) { @@ -1027,6 +1032,7 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) css_adapter_interrupt(CSS_IO_ADAPTER_VIRTIO, dev->thinint_isc); } } else { + assert(vector < NR_CLASSIC_INDICATOR_BITS); indicators = address_space_ldq(&address_space_memory, dev->indicators->addr, MEMTXATTRS_UNSPECIFIED, @@ -1040,12 +1046,11 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) if (!dev->indicators2) { return; } - vector = 0; indicators = address_space_ldq(&address_space_memory, dev->indicators2->addr, MEMTXATTRS_UNSPECIFIED, NULL); - indicators |= 1ULL << vector; + indicators |= 1ULL; address_space_stq(&address_space_memory, dev->indicators2->addr, indicators, MEMTXATTRS_UNSPECIFIED, NULL); css_conditional_io_interrupt(sch); From 9a51c9ee6ca6610e4ab89945e6c75eda3a265ceb Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Thu, 24 May 2018 19:58:27 +0200 Subject: [PATCH 1368/2380] vfio-ccw: add force unlimited prefetch property There is at least one guest (OS) such that although it does not rely on the guarantees provided by ORB 1 word 9 bit (aka unlimited prefetch, aka P bit) not being set, it fails to tell this to the machine. Usually this ain't a big deal, as the original purpose of the P bit is to allow for performance optimizations. vfio-ccw however can not provide the guarantees required if the bit is not set. It is not possible to implement support for the P bit not set without transitioning to lower level protocols for vfio-ccw. So let's give the user the opportunity to force setting the P bit, if the user knows this is safe. For self modifying channel programs forcing the P bit is not safe. If the P bit is forced for a self modifying channel program things are expected to break in strange ways. Let's also avoid warning multiple about P bit not set in the ORB in case P bit is not told to be forced, and designate the affected vfio-ccw device. Signed-off-by: Halil Pasic Suggested-by: Dong Jia Shi Acked-by: Jason J. Herne Tested-by: Jason J. Herne Message-Id: <20180524175828.3143-2-pasic@linux.ibm.com> Signed-off-by: Cornelia Huck --- hw/s390x/css.c | 3 +-- hw/vfio/ccw.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 56c3fa8c89..39ae5bbf7e 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -1204,8 +1204,7 @@ static IOInstEnding sch_handle_start_func_passthrough(SubchDev *sch) * Only support prefetch enable mode. * Only support 64bit addressing idal. */ - if (!(orb->ctrl0 & ORB_CTRL0_MASK_PFCH) || - !(orb->ctrl0 & ORB_CTRL0_MASK_C64)) { + if (!(orb->ctrl0 & ORB_CTRL0_MASK_C64)) { warn_report("vfio-ccw requires PFCH and C64 flags set"); sch_gen_unit_exception(sch); css_inject_io_interrupt(sch); diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 76e4e8c652..351b305e1a 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -33,8 +33,30 @@ typedef struct VFIOCCWDevice { uint64_t io_region_offset; struct ccw_io_region *io_region; EventNotifier io_notifier; + bool force_orb_pfch; + bool warned_orb_pfch; } VFIOCCWDevice; +static inline void warn_once(bool *warned, const char *fmt, ...) +{ + va_list ap; + + if (!warned || *warned) { + return; + } + *warned = true; + va_start(ap, fmt); + warn_vreport(fmt, ap); + va_end(ap); +} + +static inline void warn_once_pfch(VFIOCCWDevice *vcdev, SubchDev *sch, + const char *msg) +{ + warn_once(&vcdev->warned_orb_pfch, "vfio-ccw (devno %x.%x.%04x): %s", + sch->cssid, sch->ssid, sch->devno, msg); +} + static void vfio_ccw_compute_needs_reset(VFIODevice *vdev) { vdev->needs_reset = false; @@ -55,6 +77,18 @@ static IOInstEnding vfio_ccw_handle_request(SubchDev *sch) struct ccw_io_region *region = vcdev->io_region; int ret; + if (!(sch->orb.ctrl0 & ORB_CTRL0_MASK_PFCH)) { + if (!(vcdev->force_orb_pfch)) { + warn_once_pfch(vcdev, sch, "requires PFCH flag set"); + sch_gen_unit_exception(sch); + css_inject_io_interrupt(sch); + return IOINST_CC_EXPECTED; + } else { + sch->orb.ctrl0 |= ORB_CTRL0_MASK_PFCH; + warn_once_pfch(vcdev, sch, "PFCH flag forced"); + } + } + QEMU_BUILD_BUG_ON(sizeof(region->orb_area) != sizeof(ORB)); QEMU_BUILD_BUG_ON(sizeof(region->scsw_area) != sizeof(SCSW)); QEMU_BUILD_BUG_ON(sizeof(region->irb_area) != sizeof(IRB)); @@ -430,6 +464,7 @@ static void vfio_ccw_unrealize(DeviceState *dev, Error **errp) static Property vfio_ccw_properties[] = { DEFINE_PROP_STRING("sysfsdev", VFIOCCWDevice, vdev.sysfsdev), + DEFINE_PROP_BOOL("force-orb-pfch", VFIOCCWDevice, force_orb_pfch, false), DEFINE_PROP_END_OF_LIST(), }; From ea2dd691e21f2ca4896d3a88835b52f78012801d Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Thu, 24 May 2018 19:58:28 +0200 Subject: [PATCH 1369/2380] vfio-ccw: remove orb.c64 (64 bit data addresses) check The vfio-ccw module does the check too, and there is actually no technical obstacle for supporting fmt 1 idaws. Let us be ready for the beautiful day when fmt 1 idaws become supported by the vfio-ccw kernel module. QEMU does not have to do a thing for that, except not insisting on this check. Signed-off-by: Halil Pasic Acked-by: Jason J. Herne Tested-by: Jason J. Herne Message-Id: <20180524175828.3143-3-pasic@linux.ibm.com> Signed-off-by: Cornelia Huck --- hw/s390x/css.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 39ae5bbf7e..5424ea4562 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -1199,17 +1199,6 @@ static IOInstEnding sch_handle_start_func_passthrough(SubchDev *sch) assert(orb != NULL); p->intparm = orb->intparm; } - - /* - * Only support prefetch enable mode. - * Only support 64bit addressing idal. - */ - if (!(orb->ctrl0 & ORB_CTRL0_MASK_C64)) { - warn_report("vfio-ccw requires PFCH and C64 flags set"); - sch_gen_unit_exception(sch); - css_inject_io_interrupt(sch); - return IOINST_CC_EXPECTED; - } return s390_ccw_cmd_request(sch); } From acd7ef837d8987ad4ef2ab8f8e8c0f13ab413dd5 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 12 Jun 2018 14:59:33 +0200 Subject: [PATCH 1370/2380] s390x/ipl: Try to detect Linux vs non Linux for initial IPL PSW Right now the IPL device always starts from address 0x10000 (the usual Linux entry point). To run other guests (e.g. test programs) it is useful to use the IPL PSW from address 0. We can use the Linux magic at 0x10008 to decide. Signed-off-by: Christian Borntraeger Message-Id: <20180612125933.262679-1-borntraeger@de.ibm.com> Reviewed-by: David Hildenbrand Reviewed-by: Thomas Huth Signed-off-by: Cornelia Huck --- hw/s390x/ipl.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 04245b5258..0d67349004 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -29,6 +29,7 @@ #include "exec/exec-all.h" #define KERN_IMAGE_START 0x010000UL +#define LINUX_MAGIC_ADDR 0x010008UL #define KERN_PARM_AREA 0x010480UL #define INITRD_START 0x800000UL #define INITRD_PARM_START 0x010408UL @@ -105,7 +106,9 @@ static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr) static void s390_ipl_realize(DeviceState *dev, Error **errp) { S390IPLState *ipl = S390_IPL(dev); - uint64_t pentry = KERN_IMAGE_START; + uint32_t *ipl_psw; + uint64_t pentry; + char *magic; int kernel_size; Error *err = NULL; @@ -157,10 +160,24 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) NULL, 1, EM_S390, 0, 0); if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); - } - if (kernel_size < 0) { - error_setg(&err, "could not load kernel '%s'", ipl->kernel); - goto error; + if (kernel_size < 0) { + error_setg(&err, "could not load kernel '%s'", ipl->kernel); + goto error; + } + /* if this is Linux use KERN_IMAGE_START */ + magic = rom_ptr(LINUX_MAGIC_ADDR); + if (magic && !memcmp(magic, "S390EP", 6)) { + pentry = KERN_IMAGE_START; + } else { + /* if not Linux load the address of the (short) IPL PSW */ + ipl_psw = rom_ptr(4); + if (ipl_psw) { + pentry = be32_to_cpu(*ipl_psw) & 0x7fffffffUL; + } else { + error_setg(&err, "Could not get IPL PSW"); + goto error; + } + } } /* * Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the From 23ad956bff98d949057156ea3f68a9763c2dda0e Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Wed, 13 Jun 2018 10:18:19 +0200 Subject: [PATCH 1371/2380] s390x/cpumodels: add z14 Model ZR1 Introduce the new z14 Model ZR1 cpu model. Mostly identical to z14, only the cpu type differs (3906 vs. 3907) Signed-off-by: Christian Borntraeger Message-Id: <20180613081819.147178-1-borntraeger@de.ibm.com> Reviewed-by: David Hildenbrand Signed-off-by: Cornelia Huck --- target/s390x/cpu_models.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index e10035aaa8..cfdbccf46d 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -79,6 +79,7 @@ static S390CPUDef s390_cpu_defs[] = { CPUDEF_INIT(0x2964, 13, 2, 47, 0x08000000U, "z13.2", "IBM z13 GA2"), CPUDEF_INIT(0x2965, 13, 2, 47, 0x08000000U, "z13s", "IBM z13s GA1"), CPUDEF_INIT(0x3906, 14, 1, 47, 0x08000000U, "z14", "IBM z14 GA1"), + CPUDEF_INIT(0x3907, 14, 1, 47, 0x08000000U, "z14ZR1", "IBM z14 Model ZR1 GA1"), }; #define QEMU_MAX_CPU_TYPE 0x2827 From 995b30179bdc97a01ff2e4e0dce07f3e9b7d7d7d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jun 2018 14:29:45 +0200 Subject: [PATCH 1372/2380] hw/display: add ramfb, a simple boot framebuffer living in guest ram The boot framebuffer is expected to be configured by the firmware, so it uses fw_cfg as interface. Initialization goes as follows: (1) Check whenever etc/ramfb is present. (2) Allocate framebuffer from RAM. (3) Fill struct RAMFBCfg, write it to etc/ramfb. Done. You can write stuff to the framebuffer now, and it should appear automagically on the screen. Note that this isn't very efficient because it does a full display update on each refresh. No dirty tracking. Dirty tracking would have to be active for the whole ram slot, so that wouldn't be very efficient either. For a boot display which is active for a short time only this isn't a big deal. As permanent guest display something better should be used (if possible). This is the ramfb core code. Some windup is needed for display devices which want have a ramfb boot display. Signed-off-by: Gerd Hoffmann Tested-by: Laszlo Ersek Message-id: 20180613122948.18149-2-kraxel@redhat.com Signed-off-by: Gerd Hoffmann --- hw/display/Makefile.objs | 2 + hw/display/ramfb.c | 95 ++++++++++++++++++++++++++++++++++++++ include/hw/display/ramfb.h | 9 ++++ 3 files changed, 106 insertions(+) create mode 100644 hw/display/ramfb.c create mode 100644 include/hw/display/ramfb.h diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index b5d97ab26d..0af04985d2 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -1,3 +1,5 @@ +common-obj-y += ramfb.o + common-obj-$(CONFIG_ADS7846) += ads7846.o common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o common-obj-$(CONFIG_G364FB) += g364fb.o diff --git a/hw/display/ramfb.c b/hw/display/ramfb.c new file mode 100644 index 0000000000..6867bce8ae --- /dev/null +++ b/hw/display/ramfb.c @@ -0,0 +1,95 @@ +/* + * early boot framebuffer in guest ram + * configured using fw_cfg + * + * Copyright Red Hat, Inc. 2017 + * + * Author: + * Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/loader.h" +#include "hw/display/ramfb.h" +#include "ui/console.h" +#include "sysemu/sysemu.h" + +struct QEMU_PACKED RAMFBCfg { + uint64_t addr; + uint32_t fourcc; + uint32_t flags; + uint32_t width; + uint32_t height; + uint32_t stride; +}; + +struct RAMFBState { + DisplaySurface *ds; + uint32_t width, height; + struct RAMFBCfg cfg; +}; + +static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len) +{ + RAMFBState *s = dev; + void *framebuffer; + uint32_t stride, fourcc, format; + hwaddr addr, length; + + s->width = be32_to_cpu(s->cfg.width); + s->height = be32_to_cpu(s->cfg.height); + stride = be32_to_cpu(s->cfg.stride); + fourcc = be32_to_cpu(s->cfg.fourcc); + addr = be64_to_cpu(s->cfg.addr); + length = stride * s->height; + format = qemu_drm_format_to_pixman(fourcc); + + fprintf(stderr, "%s: %dx%d @ 0x%" PRIx64 "\n", __func__, + s->width, s->height, addr); + framebuffer = address_space_map(&address_space_memory, + addr, &length, false, + MEMTXATTRS_UNSPECIFIED); + if (!framebuffer || length < stride * s->height) { + s->width = 0; + s->height = 0; + return; + } + s->ds = qemu_create_displaysurface_from(s->width, s->height, + format, stride, framebuffer); +} + +void ramfb_display_update(QemuConsole *con, RAMFBState *s) +{ + if (!s->width || !s->height) { + return; + } + + if (s->ds) { + dpy_gfx_replace_surface(con, s->ds); + s->ds = NULL; + } + + /* simple full screen update */ + dpy_gfx_update_full(con); +} + +RAMFBState *ramfb_setup(Error **errp) +{ + FWCfgState *fw_cfg = fw_cfg_find(); + RAMFBState *s; + + if (!fw_cfg || !fw_cfg->dma_enabled) { + error_setg(errp, "ramfb device requires fw_cfg with DMA"); + return NULL; + } + + s = g_new0(RAMFBState, 1); + + fw_cfg_add_file_callback(fw_cfg, "etc/ramfb", + NULL, ramfb_fw_cfg_write, s, + &s->cfg, sizeof(s->cfg), false); + return s; +} diff --git a/include/hw/display/ramfb.h b/include/hw/display/ramfb.h new file mode 100644 index 0000000000..a3d4c79942 --- /dev/null +++ b/include/hw/display/ramfb.h @@ -0,0 +1,9 @@ +#ifndef RAMFB_H +#define RAMFB_H + +/* ramfb.c */ +typedef struct RAMFBState RAMFBState; +void ramfb_display_update(QemuConsole *con, RAMFBState *s); +RAMFBState *ramfb_setup(Error **errp); + +#endif /* RAMFB_H */ From 94692dcd71473622f3a14416dae67e73e7e733ec Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jun 2018 14:29:46 +0200 Subject: [PATCH 1373/2380] hw/display: add standalone ramfb device Signed-off-by: Gerd Hoffmann Tested-by: Laszlo Ersek Message-id: 20180613122948.18149-3-kraxel@redhat.com Signed-off-by: Gerd Hoffmann --- hw/arm/sysbus-fdt.c | 7 ++++ hw/arm/virt.c | 2 ++ hw/display/Makefile.objs | 1 + hw/display/ramfb-standalone.c | 62 +++++++++++++++++++++++++++++++++++ hw/i386/pc_piix.c | 2 ++ hw/i386/pc_q35.c | 2 ++ include/hw/display/ramfb.h | 3 ++ 7 files changed, 79 insertions(+) create mode 100644 hw/display/ramfb-standalone.c diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c index e4c492ea44..277ed872e7 100644 --- a/hw/arm/sysbus-fdt.c +++ b/hw/arm/sysbus-fdt.c @@ -36,6 +36,7 @@ #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" #include "hw/vfio/vfio-amd-xgbe.h" +#include "hw/display/ramfb.h" #include "hw/arm/fdt.h" /* @@ -406,12 +407,18 @@ static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) #endif /* CONFIG_LINUX */ +static int no_fdt_node(SysBusDevice *sbdev, void *opaque) +{ + return 0; +} + /* list of supported dynamic sysbus devices */ static const NodeCreationPair add_fdt_node_functions[] = { #ifdef CONFIG_LINUX {TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node}, {TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node}, #endif + {TYPE_RAMFB_DEVICE, no_fdt_node}, {"", NULL}, /* last element */ }; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index f0a4fa004c..98b99cf236 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -36,6 +36,7 @@ #include "hw/arm/virt.h" #include "hw/vfio/vfio-calxeda-xgmac.h" #include "hw/vfio/vfio-amd-xgbe.h" +#include "hw/display/ramfb.h" #include "hw/devices.h" #include "net/net.h" #include "sysemu/device_tree.h" @@ -1659,6 +1660,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 255; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_CALXEDA_XGMAC); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->pci_allow_0_address = true; diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 0af04985d2..fb8408c6d0 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -1,4 +1,5 @@ common-obj-y += ramfb.o +common-obj-y += ramfb-standalone.o common-obj-$(CONFIG_ADS7846) += ads7846.o common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c new file mode 100644 index 0000000000..c0d241ba01 --- /dev/null +++ b/hw/display/ramfb-standalone.c @@ -0,0 +1,62 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/loader.h" +#include "hw/isa/isa.h" +#include "hw/display/ramfb.h" +#include "ui/console.h" +#include "sysemu/sysemu.h" + +#define RAMFB(obj) OBJECT_CHECK(RAMFBStandaloneState, (obj), TYPE_RAMFB_DEVICE) + +typedef struct RAMFBStandaloneState { + SysBusDevice parent_obj; + QemuConsole *con; + RAMFBState *state; +} RAMFBStandaloneState; + +static void display_update_wrapper(void *dev) +{ + RAMFBStandaloneState *ramfb = RAMFB(dev); + + if (0 /* native driver active */) { + /* non-standalone device would run native display update here */; + } else { + ramfb_display_update(ramfb->con, ramfb->state); + } +} + +static const GraphicHwOps wrapper_ops = { + .gfx_update = display_update_wrapper, +}; + +static void ramfb_realizefn(DeviceState *dev, Error **errp) +{ + RAMFBStandaloneState *ramfb = RAMFB(dev); + + ramfb->con = graphic_console_init(dev, 0, &wrapper_ops, dev); + ramfb->state = ramfb_setup(errp); +} + +static void ramfb_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + dc->realize = ramfb_realizefn; + dc->desc = "ram framebuffer standalone device"; + dc->user_creatable = true; +} + +static const TypeInfo ramfb_info = { + .name = TYPE_RAMFB_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RAMFBStandaloneState), + .class_init = ramfb_class_initfn, +}; + +static void ramfb_register_types(void) +{ + type_register_static(&ramfb_info); +} + +type_init(ramfb_register_types) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 3b87f3cedb..e9b6f064fb 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -28,6 +28,7 @@ #include "hw/loader.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" +#include "hw/display/ramfb.h" #include "hw/smbios/smbios.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" @@ -423,6 +424,7 @@ static void pc_i440fx_machine_options(MachineClass *m) m->desc = "Standard PC (i440FX + PIIX, 1996)"; m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; + machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); } static void pc_i440fx_3_0_machine_options(MachineClass *m) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 087f2630f9..1a73e1848a 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -45,6 +45,7 @@ #include "hw/i386/ich9.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" +#include "hw/display/ramfb.h" #include "hw/smbios/smbios.h" #include "hw/ide/pci.h" #include "hw/ide/ahci.h" @@ -305,6 +306,7 @@ static void pc_q35_machine_options(MachineClass *m) m->no_floppy = 1; machine_class_allow_dynamic_sysbus_dev(m, TYPE_AMD_IOMMU_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_INTEL_IOMMU_DEVICE); + machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); m->max_cpus = 288; } diff --git a/include/hw/display/ramfb.h b/include/hw/display/ramfb.h index a3d4c79942..b33a2c467b 100644 --- a/include/hw/display/ramfb.h +++ b/include/hw/display/ramfb.h @@ -6,4 +6,7 @@ typedef struct RAMFBState RAMFBState; void ramfb_display_update(QemuConsole *con, RAMFBState *s); RAMFBState *ramfb_setup(Error **errp); +/* ramfb-standalone.c */ +#define TYPE_RAMFB_DEVICE "ramfb" + #endif /* RAMFB_H */ From 58d632c7ce66c272868b057491cec7ee5a6a1050 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jun 2018 14:29:48 +0200 Subject: [PATCH 1374/2380] Add ramfb MAINTAINERS entry Signed-off-by: Gerd Hoffmann Reviewed-by: Laszlo Ersek Message-id: 20180613122948.18149-5-kraxel@redhat.com Signed-off-by: Gerd Hoffmann --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0fb5f38f9f..da91501c7a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1331,6 +1331,12 @@ F: hw/display/bochs-display.c F: include/hw/display/vga.h F: include/hw/display/bochs-vbe.h +ramfb +M: Gerd Hoffmann +S: Maintained +F: hw/display/ramfb*.c +F: include/hw/display/ramfb.h + virtio-gpu M: Gerd Hoffmann S: Maintained From b55a06df4f03f21e8a32e9d067a8a5782ac3ce0d Mon Sep 17 00:00:00 2001 From: liujunjie Date: Thu, 7 Jun 2018 16:02:37 +0800 Subject: [PATCH 1375/2380] ps2: check PS2Queue wptr pointer in post_load routine In commit 802cbcb7300, most issues have been fixed when qemu guest migration. But the queue size still need to check whether is equal to PS2_QUEUE_SIZE. If yes, the wptr should set as 0. Or, wptr would larger than PS2_QUEUE_SIZE and never come back when ps2_queue_noirq is called. This could lead to OOB access, add check to avoid it. Signed-off-by: liujunjie Reviewed-by: Gonglei Message-id: 20180607080237.12360-1-liujunjie23@huawei.com Signed-off-by: Gerd Hoffmann --- hw/input/ps2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/input/ps2.c b/hw/input/ps2.c index eeec6180d0..fdfcadf9a1 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -927,7 +927,7 @@ static void ps2_common_post_load(PS2State *s) /* reset rptr/wptr/count */ q->rptr = 0; - q->wptr = size; + q->wptr = (size == PS2_QUEUE_SIZE) ? 0 : size; q->count = size; s->update_irq(s->update_arg, q->count != 0); } From bb6756895459f181e2f25e877d3d7a10c297b5c8 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Sun, 10 Dec 2017 00:11:13 +0100 Subject: [PATCH 1376/2380] test-bdrv-drain: bdrv_drain() works with cross-AioContext events As long as nobody keeps the other I/O thread from working, there is no reason why bdrv_drain() wouldn't work with cross-AioContext events. The key is that the root request we're waiting for is in the AioContext we're polling (which it always is for bdrv_drain()) so that aio_poll() is woken up in the end. Add a test case that shows that it works. Remove the comment in bdrv_drain() that claims otherwise. Signed-off-by: Kevin Wolf --- block/io.c | 4 - tests/test-bdrv-drain.c | 187 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 186 insertions(+), 5 deletions(-) diff --git a/block/io.c b/block/io.c index b7beaeeb9f..5f286bcedb 100644 --- a/block/io.c +++ b/block/io.c @@ -370,10 +370,6 @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) * * Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState * AioContext. - * - * Only this BlockDriverState's AioContext is run, so in-flight requests must - * not depend on events in other AioContexts. In that case, use - * bdrv_drain_all() instead. */ void coroutine_fn bdrv_co_drain(BlockDriverState *bs) { diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index a11c4cfbf2..fb68539d17 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -27,9 +27,13 @@ #include "block/blockjob_int.h" #include "sysemu/block-backend.h" #include "qapi/error.h" +#include "iothread.h" + +static QemuEvent done_event; typedef struct BDRVTestState { int drain_count; + AioContext *bh_indirection_ctx; } BDRVTestState; static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) @@ -50,16 +54,29 @@ static void bdrv_test_close(BlockDriverState *bs) g_assert_cmpint(s->drain_count, >, 0); } +static void co_reenter_bh(void *opaque) +{ + aio_co_wake(opaque); +} + static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { + BDRVTestState *s = bs->opaque; + /* We want this request to stay until the polling loop in drain waits for * it to complete. We need to sleep a while as bdrv_drain_invoke() comes * first and polls its result, too, but it shouldn't accidentally complete * this request yet. */ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + if (s->bh_indirection_ctx) { + aio_bh_schedule_oneshot(s->bh_indirection_ctx, co_reenter_bh, + qemu_coroutine_self()); + qemu_coroutine_yield(); + } + return 0; } @@ -490,6 +507,164 @@ static void test_graph_change(void) blk_unref(blk_b); } +struct test_iothread_data { + BlockDriverState *bs; + enum drain_type drain_type; + int *aio_ret; +}; + +static void test_iothread_drain_entry(void *opaque) +{ + struct test_iothread_data *data = opaque; + + aio_context_acquire(bdrv_get_aio_context(data->bs)); + do_drain_begin(data->drain_type, data->bs); + g_assert_cmpint(*data->aio_ret, ==, 0); + do_drain_end(data->drain_type, data->bs); + aio_context_release(bdrv_get_aio_context(data->bs)); + + qemu_event_set(&done_event); +} + +static void test_iothread_aio_cb(void *opaque, int ret) +{ + int *aio_ret = opaque; + *aio_ret = ret; + qemu_event_set(&done_event); +} + +/* + * Starts an AIO request on a BDS that runs in the AioContext of iothread 1. + * The request involves a BH on iothread 2 before it can complete. + * + * @drain_thread = 0 means that do_drain_begin/end are called from the main + * thread, @drain_thread = 1 means that they are called from iothread 1. Drain + * for this BDS cannot be called from iothread 2 because only the main thread + * may do cross-AioContext polling. + */ +static void test_iothread_common(enum drain_type drain_type, int drain_thread) +{ + BlockBackend *blk; + BlockDriverState *bs; + BDRVTestState *s; + BlockAIOCB *acb; + int aio_ret; + struct test_iothread_data data; + + IOThread *a = iothread_new(); + IOThread *b = iothread_new(); + AioContext *ctx_a = iothread_get_aio_context(a); + AioContext *ctx_b = iothread_get_aio_context(b); + + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = NULL, + .iov_len = 0, + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + /* bdrv_drain_all() may only be called from the main loop thread */ + if (drain_type == BDRV_DRAIN_ALL && drain_thread != 0) { + goto out; + } + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + s = bs->opaque; + blk_insert_bs(blk, bs, &error_abort); + + blk_set_aio_context(blk, ctx_a); + aio_context_acquire(ctx_a); + + s->bh_indirection_ctx = ctx_b; + + aio_ret = -EINPROGRESS; + if (drain_thread == 0) { + acb = blk_aio_preadv(blk, 0, &qiov, 0, test_iothread_aio_cb, &aio_ret); + } else { + acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret); + } + g_assert(acb != NULL); + g_assert_cmpint(aio_ret, ==, -EINPROGRESS); + + aio_context_release(ctx_a); + + data = (struct test_iothread_data) { + .bs = bs, + .drain_type = drain_type, + .aio_ret = &aio_ret, + }; + + switch (drain_thread) { + case 0: + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_acquire(ctx_a); + } + + /* The request is running on the IOThread a. Draining its block device + * will make sure that it has completed as far as the BDS is concerned, + * but the drain in this thread can continue immediately after + * bdrv_dec_in_flight() and aio_ret might be assigned only slightly + * later. */ + qemu_event_reset(&done_event); + do_drain_begin(drain_type, bs); + g_assert_cmpint(bs->in_flight, ==, 0); + + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_release(ctx_a); + } + qemu_event_wait(&done_event); + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_acquire(ctx_a); + } + + g_assert_cmpint(aio_ret, ==, 0); + do_drain_end(drain_type, bs); + + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_release(ctx_a); + } + break; + case 1: + qemu_event_reset(&done_event); + aio_bh_schedule_oneshot(ctx_a, test_iothread_drain_entry, &data); + qemu_event_wait(&done_event); + break; + default: + g_assert_not_reached(); + } + + aio_context_acquire(ctx_a); + blk_set_aio_context(blk, qemu_get_aio_context()); + aio_context_release(ctx_a); + + bdrv_unref(bs); + blk_unref(blk); + +out: + iothread_join(a); + iothread_join(b); +} + +static void test_iothread_drain_all(void) +{ + test_iothread_common(BDRV_DRAIN_ALL, 0); + test_iothread_common(BDRV_DRAIN_ALL, 1); +} + +static void test_iothread_drain(void) +{ + test_iothread_common(BDRV_DRAIN, 0); + test_iothread_common(BDRV_DRAIN, 1); +} + +static void test_iothread_drain_subtree(void) +{ + test_iothread_common(BDRV_SUBTREE_DRAIN, 0); + test_iothread_common(BDRV_SUBTREE_DRAIN, 1); +} + typedef struct TestBlockJob { BlockJob common; @@ -618,10 +793,13 @@ static void test_blockjob_drain_subtree(void) int main(int argc, char **argv) { + int ret; + bdrv_init(); qemu_init_main_loop(&error_abort); g_test_init(&argc, &argv, NULL); + qemu_event_init(&done_event, false); g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); @@ -648,10 +826,17 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/multiparent", test_multiparent); g_test_add_func("/bdrv-drain/graph-change", test_graph_change); + g_test_add_func("/bdrv-drain/iothread/drain_all", test_iothread_drain_all); + g_test_add_func("/bdrv-drain/iothread/drain", test_iothread_drain); + g_test_add_func("/bdrv-drain/iothread/drain_subtree", + test_iothread_drain_subtree); + g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); g_test_add_func("/bdrv-drain/blockjob/drain_subtree", test_blockjob_drain_subtree); - return g_test_run(); + ret = g_test_run(); + qemu_event_destroy(&done_event); + return ret; } From 79ab8b21dc19c08adc407504e456ff64b9dacb66 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 14 Dec 2017 10:27:23 +0100 Subject: [PATCH 1377/2380] block: Use bdrv_do_drain_begin/end in bdrv_drain_all() bdrv_do_drain_begin/end() implement already everything that bdrv_drain_all_begin/end() need and currently still do manually: Disable external events, call parent drain callbacks, call block driver callbacks. It also does two more things: The first is incrementing bs->quiesce_counter. bdrv_drain_all() already stood out in the test case by behaving different from the other drain variants. Adding this is not only safe, but in fact a bug fix. The second is calling bdrv_drain_recurse(). We already do that later in the same function in a loop, so basically doing an early first iteration doesn't hurt. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/io.c | 10 ++-------- tests/test-bdrv-drain.c | 14 ++++---------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/block/io.c b/block/io.c index 5f286bcedb..b355009f2c 100644 --- a/block/io.c +++ b/block/io.c @@ -413,11 +413,8 @@ void bdrv_drain_all_begin(void) for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { AioContext *aio_context = bdrv_get_aio_context(bs); - /* Stop things in parent-to-child order */ aio_context_acquire(aio_context); - aio_disable_external(aio_context); - bdrv_parent_drained_begin(bs, NULL); - bdrv_drain_invoke(bs, true, true); + bdrv_do_drained_begin(bs, true, NULL); aio_context_release(aio_context); if (!g_slist_find(aio_ctxs, aio_context)) { @@ -458,11 +455,8 @@ void bdrv_drain_all_end(void) for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { AioContext *aio_context = bdrv_get_aio_context(bs); - /* Re-enable things in child-to-parent order */ aio_context_acquire(aio_context); - bdrv_drain_invoke(bs, false, true); - bdrv_parent_drained_end(bs, NULL); - aio_enable_external(aio_context); + bdrv_do_drained_end(bs, true, NULL); aio_context_release(aio_context); } } diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index fb68539d17..c7f107142c 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -276,8 +276,7 @@ static void test_quiesce_common(enum drain_type drain_type, bool recursive) static void test_quiesce_drain_all(void) { - // XXX drain_all doesn't quiesce - //test_quiesce_common(BDRV_DRAIN_ALL, true); + test_quiesce_common(BDRV_DRAIN_ALL, true); } static void test_quiesce_drain(void) @@ -319,12 +318,7 @@ static void test_nested(void) for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) { for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) { - /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */ - int bs_quiesce = (outer != BDRV_DRAIN_ALL) + - (inner != BDRV_DRAIN_ALL); - int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) + - (inner == BDRV_SUBTREE_DRAIN); - int backing_cb_cnt = (outer != BDRV_DRAIN) + + int backing_quiesce = (outer != BDRV_DRAIN) + (inner != BDRV_DRAIN); g_assert_cmpint(bs->quiesce_counter, ==, 0); @@ -335,10 +329,10 @@ static void test_nested(void) do_drain_begin(outer, bs); do_drain_begin(inner, bs); - g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce); + g_assert_cmpint(bs->quiesce_counter, ==, 2); g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce); g_assert_cmpint(s->drain_count, ==, 2); - g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt); + g_assert_cmpint(backing_s->drain_count, ==, backing_quiesce); do_drain_end(inner, bs); do_drain_end(outer, bs); From 7d40d9ef9dfb4948a857bfc6ec8408eed1d1d9e7 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 14 Dec 2017 10:40:20 +0100 Subject: [PATCH 1378/2380] block: Remove 'recursive' parameter from bdrv_drain_invoke() All callers pass false for the 'recursive' parameter now. Remove it. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/io.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/block/io.c b/block/io.c index b355009f2c..b75d68886a 100644 --- a/block/io.c +++ b/block/io.c @@ -168,9 +168,8 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) } /* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ -static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive) +static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) { - BdrvChild *child, *tmp; BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) || @@ -181,12 +180,6 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive) data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data); bdrv_coroutine_enter(bs, data.co); BDRV_POLL_WHILE(bs, !data.done); - - if (recursive) { - QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { - bdrv_drain_invoke(child->bs, begin, true); - } - } } static bool bdrv_drain_recurse(BlockDriverState *bs) @@ -287,7 +280,7 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, } bdrv_parent_drained_begin(bs, parent); - bdrv_drain_invoke(bs, true, false); + bdrv_drain_invoke(bs, true); bdrv_drain_recurse(bs); if (recursive) { @@ -322,7 +315,7 @@ void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter); /* Re-enable things in child-to-parent order */ - bdrv_drain_invoke(bs, false, false); + bdrv_drain_invoke(bs, false); bdrv_parent_drained_end(bs, parent); if (old_quiesce_counter == 1) { aio_enable_external(bdrv_get_aio_context(bs)); From c13ad59f012cbbccb866a10477458e69bc868dbb Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 14 Dec 2017 11:25:16 +0100 Subject: [PATCH 1379/2380] block: Don't manually poll in bdrv_drain_all() All involved nodes are already idle, we called bdrv_do_drain_begin() on them. The comment in the code suggested that this was not correct because the completion of a request on one node could spawn a new request on a different node (which might have been drained before, so we wouldn't drain the new request). In reality, new requests to different nodes aren't spawned out of nothing, but only in the context of a parent request, and they aren't submitted to random nodes, but only to child nodes. As long as we still poll for the completion of the parent request (which we do), draining each root node separately is good enough. Remove the additional polling code from bdrv_drain_all_begin() and replace it with an assertion that all nodes are already idle after we drained them separately. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/io.c | 41 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/block/io.c b/block/io.c index b75d68886a..983307cf03 100644 --- a/block/io.c +++ b/block/io.c @@ -377,6 +377,16 @@ void bdrv_drain(BlockDriverState *bs) bdrv_drained_end(bs); } +static void bdrv_drain_assert_idle(BlockDriverState *bs) +{ + BdrvChild *child, *next; + + assert(atomic_read(&bs->in_flight) == 0); + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { + bdrv_drain_assert_idle(child->bs); + } +} + /* * Wait for pending requests to complete across all BlockDriverStates * @@ -391,11 +401,8 @@ void bdrv_drain(BlockDriverState *bs) */ void bdrv_drain_all_begin(void) { - /* Always run first iteration so any pending completion BHs run */ - bool waited = true; BlockDriverState *bs; BdrvNextIterator it; - GSList *aio_ctxs = NULL, *ctx; /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on @@ -409,35 +416,11 @@ void bdrv_drain_all_begin(void) aio_context_acquire(aio_context); bdrv_do_drained_begin(bs, true, NULL); aio_context_release(aio_context); - - if (!g_slist_find(aio_ctxs, aio_context)) { - aio_ctxs = g_slist_prepend(aio_ctxs, aio_context); - } } - /* Note that completion of an asynchronous I/O operation can trigger any - * number of other I/O operations on other devices---for example a - * coroutine can submit an I/O request to another device in response to - * request completion. Therefore we must keep looping until there was no - * more activity rather than simply draining each device independently. - */ - while (waited) { - waited = false; - - for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) { - AioContext *aio_context = ctx->data; - - aio_context_acquire(aio_context); - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - if (aio_context == bdrv_get_aio_context(bs)) { - waited |= bdrv_drain_recurse(bs); - } - } - aio_context_release(aio_context); - } + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + bdrv_drain_assert_idle(bs); } - - g_slist_free(aio_ctxs); } void bdrv_drain_all_end(void) From 6d0252f2f9cb49925deb1c41101462c9481dfc90 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 4 Apr 2018 13:26:16 +0200 Subject: [PATCH 1380/2380] tests/test-bdrv-drain: bdrv_drain_all() works in coroutines now Since we use bdrv_do_drained_begin/end() for bdrv_drain_all_begin/end(), coroutine context is automatically left with a BH, preventing the deadlocks that made bdrv_drain_all*() unsafe in coroutine context. Now that we even removed the old polling code as dead code, it's obvious that it's compatible now. Enable the coroutine test cases for bdrv_drain_all(). Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- tests/test-bdrv-drain.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index c7f107142c..cc03bc171b 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -233,6 +233,11 @@ static void test_drv_cb_drain_subtree(void) test_drv_cb_common(BDRV_SUBTREE_DRAIN, true); } +static void test_drv_cb_co_drain_all(void) +{ + call_in_coroutine(test_drv_cb_drain_all); +} + static void test_drv_cb_co_drain(void) { call_in_coroutine(test_drv_cb_drain); @@ -289,6 +294,11 @@ static void test_quiesce_drain_subtree(void) test_quiesce_common(BDRV_SUBTREE_DRAIN, true); } +static void test_quiesce_co_drain_all(void) +{ + call_in_coroutine(test_quiesce_drain_all); +} + static void test_quiesce_co_drain(void) { call_in_coroutine(test_quiesce_drain); @@ -800,7 +810,8 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/driver-cb/drain_subtree", test_drv_cb_drain_subtree); - // XXX bdrv_drain_all() doesn't work in coroutine context + g_test_add_func("/bdrv-drain/driver-cb/co/drain_all", + test_drv_cb_co_drain_all); g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain); g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree", test_drv_cb_co_drain_subtree); @@ -811,7 +822,8 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/quiesce/drain_subtree", test_quiesce_drain_subtree); - // XXX bdrv_drain_all() doesn't work in coroutine context + g_test_add_func("/bdrv-drain/quiesce/co/drain_all", + test_quiesce_co_drain_all); g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain); g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree", test_quiesce_co_drain_subtree); From 1cc8e54ada97f7ac479554e15ca9e426c895b158 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 22 Mar 2018 10:57:14 +0100 Subject: [PATCH 1381/2380] block: Avoid unnecessary aio_poll() in AIO_WAIT_WHILE() Commit 91af091f923 added an additional aio_poll() to BDRV_POLL_WHILE() in order to make sure that all pending BHs are executed on drain. This was the wrong place to make the fix, as it is useless overhead for all other users of the macro and unnecessarily complicates the mechanism. This patch effectively reverts said commit (the context has changed a bit and the code has moved to AIO_WAIT_WHILE()) and instead polls in the loop condition for drain. The effect is probably hard to measure in any real-world use case because actual I/O will dominate, but if I run only the initialisation part of 'qemu-img convert' where it calls bdrv_block_status() for the whole image to find out how much data there is copy, this phase actually needs only roughly half the time after this patch. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/io.c | 11 ++++++++++- include/block/aio-wait.h | 22 ++++++++-------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/block/io.c b/block/io.c index 983307cf03..bc7a2d78b8 100644 --- a/block/io.c +++ b/block/io.c @@ -182,13 +182,22 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) BDRV_POLL_WHILE(bs, !data.done); } +/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ +static bool bdrv_drain_poll(BlockDriverState *bs) +{ + /* Execute pending BHs first and check everything else only after the BHs + * have executed. */ + while (aio_poll(bs->aio_context, false)); + return atomic_read(&bs->in_flight); +} + static bool bdrv_drain_recurse(BlockDriverState *bs) { BdrvChild *child, *tmp; bool waited; /* Wait for drained requests to finish */ - waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); + waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll(bs)); QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { BlockDriverState *bs = child->bs; diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h index 8c90a2e66e..783d3678dd 100644 --- a/include/block/aio-wait.h +++ b/include/block/aio-wait.h @@ -73,29 +73,23 @@ typedef struct { */ #define AIO_WAIT_WHILE(wait, ctx, cond) ({ \ bool waited_ = false; \ - bool busy_ = true; \ AioWait *wait_ = (wait); \ AioContext *ctx_ = (ctx); \ if (in_aio_context_home_thread(ctx_)) { \ - while ((cond) || busy_) { \ - busy_ = aio_poll(ctx_, (cond)); \ - waited_ |= !!(cond) | busy_; \ + while ((cond)) { \ + aio_poll(ctx_, true); \ + waited_ = true; \ } \ } else { \ assert(qemu_get_current_aio_context() == \ qemu_get_aio_context()); \ /* Increment wait_->num_waiters before evaluating cond. */ \ atomic_inc(&wait_->num_waiters); \ - while (busy_) { \ - if ((cond)) { \ - waited_ = busy_ = true; \ - aio_context_release(ctx_); \ - aio_poll(qemu_get_aio_context(), true); \ - aio_context_acquire(ctx_); \ - } else { \ - busy_ = aio_poll(ctx_, false); \ - waited_ |= busy_; \ - } \ + while ((cond)) { \ + aio_context_release(ctx_); \ + aio_poll(qemu_get_aio_context(), true); \ + aio_context_acquire(ctx_); \ + waited_ = true; \ } \ atomic_dec(&wait_->num_waiters); \ } \ From 89bd030533e3592ca0a995450dcfc5d53e459e20 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 22 Mar 2018 14:11:20 +0100 Subject: [PATCH 1382/2380] block: Really pause block jobs on drain We already requested that block jobs be paused in .bdrv_drained_begin, but no guarantee was made that the job was actually inactive at the point where bdrv_drained_begin() returned. This introduces a new callback BdrvChildRole.bdrv_drained_poll() and uses it to make bdrv_drain_poll() consider block jobs using the node to be drained. For the test case to work as expected, we have to switch from block_job_sleep_ns() to qemu_co_sleep_ns() so that the test job is even considered active and must be waited for when draining the node. Signed-off-by: Kevin Wolf --- block.c | 9 ++++++++ block/io.c | 40 ++++++++++++++++++++++++++++++------ block/mirror.c | 8 ++++++++ blockjob.c | 23 +++++++++++++++++++++ include/block/block.h | 8 ++++++++ include/block/block_int.h | 7 +++++++ include/block/blockjob_int.h | 8 ++++++++ tests/test-bdrv-drain.c | 18 ++++++++-------- 8 files changed, 107 insertions(+), 14 deletions(-) diff --git a/block.c b/block.c index afe30caac3..8cf9cd8855 100644 --- a/block.c +++ b/block.c @@ -821,6 +821,12 @@ static void bdrv_child_cb_drained_begin(BdrvChild *child) bdrv_drained_begin(bs); } +static bool bdrv_child_cb_drained_poll(BdrvChild *child) +{ + BlockDriverState *bs = child->opaque; + return bdrv_drain_poll(bs, NULL); +} + static void bdrv_child_cb_drained_end(BdrvChild *child) { BlockDriverState *bs = child->opaque; @@ -905,6 +911,7 @@ const BdrvChildRole child_file = { .get_parent_desc = bdrv_child_get_parent_desc, .inherit_options = bdrv_inherited_options, .drained_begin = bdrv_child_cb_drained_begin, + .drained_poll = bdrv_child_cb_drained_poll, .drained_end = bdrv_child_cb_drained_end, .attach = bdrv_child_cb_attach, .detach = bdrv_child_cb_detach, @@ -929,6 +936,7 @@ const BdrvChildRole child_format = { .get_parent_desc = bdrv_child_get_parent_desc, .inherit_options = bdrv_inherited_fmt_options, .drained_begin = bdrv_child_cb_drained_begin, + .drained_poll = bdrv_child_cb_drained_poll, .drained_end = bdrv_child_cb_drained_end, .attach = bdrv_child_cb_attach, .detach = bdrv_child_cb_detach, @@ -1048,6 +1056,7 @@ const BdrvChildRole child_backing = { .detach = bdrv_backing_detach, .inherit_options = bdrv_backing_options, .drained_begin = bdrv_child_cb_drained_begin, + .drained_poll = bdrv_child_cb_drained_poll, .drained_end = bdrv_child_cb_drained_end, .inactivate = bdrv_child_cb_inactivate, .update_filename = bdrv_backing_update_filename, diff --git a/block/io.c b/block/io.c index bc7a2d78b8..5820e73bb2 100644 --- a/block/io.c +++ b/block/io.c @@ -69,6 +69,23 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) } } +static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore) +{ + BdrvChild *c, *next; + bool busy = false; + + QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { + if (c == ignore) { + continue; + } + if (c->role->drained_poll) { + busy |= c->role->drained_poll(c); + } + } + + return busy; +} + static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) { dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer); @@ -183,21 +200,32 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) } /* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ -static bool bdrv_drain_poll(BlockDriverState *bs) +bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent) +{ + if (bdrv_parent_drained_poll(bs, ignore_parent)) { + return true; + } + + return atomic_read(&bs->in_flight); +} + +static bool bdrv_drain_poll_top_level(BlockDriverState *bs, + BdrvChild *ignore_parent) { /* Execute pending BHs first and check everything else only after the BHs * have executed. */ while (aio_poll(bs->aio_context, false)); - return atomic_read(&bs->in_flight); + + return bdrv_drain_poll(bs, ignore_parent); } -static bool bdrv_drain_recurse(BlockDriverState *bs) +static bool bdrv_drain_recurse(BlockDriverState *bs, BdrvChild *parent) { BdrvChild *child, *tmp; bool waited; /* Wait for drained requests to finish */ - waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll(bs)); + waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { BlockDriverState *bs = child->bs; @@ -214,7 +242,7 @@ static bool bdrv_drain_recurse(BlockDriverState *bs) */ bdrv_ref(bs); } - waited |= bdrv_drain_recurse(bs); + waited |= bdrv_drain_recurse(bs, child); if (in_main_loop) { bdrv_unref(bs); } @@ -290,7 +318,7 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, bdrv_parent_drained_begin(bs, parent); bdrv_drain_invoke(bs, true); - bdrv_drain_recurse(bs); + bdrv_drain_recurse(bs, parent); if (recursive) { bs->recursive_quiesce_counter++; diff --git a/block/mirror.c b/block/mirror.c index 435268bbbf..c2146c1ab3 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -964,6 +964,12 @@ static void mirror_pause(Job *job) mirror_wait_for_all_io(s); } +static bool mirror_drained_poll(BlockJob *job) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + return !!s->in_flight; +} + static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); @@ -997,6 +1003,7 @@ static const BlockJobDriver mirror_job_driver = { .pause = mirror_pause, .complete = mirror_complete, }, + .drained_poll = mirror_drained_poll, .attached_aio_context = mirror_attached_aio_context, .drain = mirror_drain, }; @@ -1012,6 +1019,7 @@ static const BlockJobDriver commit_active_job_driver = { .pause = mirror_pause, .complete = mirror_complete, }, + .drained_poll = mirror_drained_poll, .attached_aio_context = mirror_attached_aio_context, .drain = mirror_drain, }; diff --git a/blockjob.c b/blockjob.c index 0306533a2e..be5903aa96 100644 --- a/blockjob.c +++ b/blockjob.c @@ -155,6 +155,28 @@ static void child_job_drained_begin(BdrvChild *c) job_pause(&job->job); } +static bool child_job_drained_poll(BdrvChild *c) +{ + BlockJob *bjob = c->opaque; + Job *job = &bjob->job; + const BlockJobDriver *drv = block_job_driver(bjob); + + /* An inactive or completed job doesn't have any pending requests. Jobs + * with !job->busy are either already paused or have a pause point after + * being reentered, so no job driver code will run before they pause. */ + if (!job->busy || job_is_completed(job) || job->deferred_to_main_loop) { + return false; + } + + /* Otherwise, assume that it isn't fully stopped yet, but allow the job to + * override this assumption. */ + if (drv->drained_poll) { + return drv->drained_poll(bjob); + } else { + return true; + } +} + static void child_job_drained_end(BdrvChild *c) { BlockJob *job = c->opaque; @@ -164,6 +186,7 @@ static void child_job_drained_end(BdrvChild *c) static const BdrvChildRole child_job = { .get_parent_desc = child_job_get_parent_desc, .drained_begin = child_job_drained_begin, + .drained_poll = child_job_drained_poll, .drained_end = child_job_drained_end, .stay_at_node = true, }; diff --git a/include/block/block.h b/include/block/block.h index e677080c4e..cebbb39c6c 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -567,6 +567,14 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore); */ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); +/** + * bdrv_drain_poll: + * + * Poll for pending requests in @bs and its parents (except for + * @ignore_parent). This is part of bdrv_drained_begin. + */ +bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent); + /** * bdrv_drained_begin: * diff --git a/include/block/block_int.h b/include/block/block_int.h index 327e478a73..1b811db8ec 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -605,6 +605,13 @@ struct BdrvChildRole { void (*drained_begin)(BdrvChild *child); void (*drained_end)(BdrvChild *child); + /* + * Returns whether the parent has pending requests for the child. This + * callback is polled after .drained_begin() has been called until all + * activity on the child has stopped. + */ + bool (*drained_poll)(BdrvChild *child); + /* Notifies the parent that the child has been activated/inactivated (e.g. * when migration is completing) and it can start/stop requesting * permissions and doing I/O on it. */ diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 5cd50c6639..e4a318dd15 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -38,6 +38,14 @@ struct BlockJobDriver { /** Generic JobDriver callbacks and settings */ JobDriver job_driver; + /* + * Returns whether the job has pending requests for the child or will + * submit new requests before the next pause point. This callback is polled + * in the context of draining a job node after requesting that the job be + * paused, until all activity on the child has stopped. + */ + bool (*drained_poll)(BlockJob *job); + /* * If the callback is not NULL, it will be invoked before the job is * resumed in a new AioContext. This is the place to move any resources diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index cc03bc171b..22d31c953e 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -686,7 +686,11 @@ static void coroutine_fn test_job_start(void *opaque) job_transition_to_ready(&s->common.job); while (!s->should_complete) { - job_sleep_ns(&s->common.job, 100000); + /* Avoid block_job_sleep_ns() because it marks the job as !busy. We + * want to emulate some actual activity (probably some I/O) here so + * that drain has to wait for this acitivity to stop. */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + job_pause_point(&s->common.job); } job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); @@ -733,7 +737,7 @@ static void test_blockjob_common(enum drain_type drain_type) g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ + g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ do_drain_begin(drain_type, src); @@ -743,15 +747,14 @@ static void test_blockjob_common(enum drain_type drain_type) } else { g_assert_cmpint(job->job.pause_count, ==, 1); } - /* XXX We don't wait until the job is actually paused. Is this okay? */ - /* g_assert_true(job->job.paused); */ + g_assert_true(job->job.paused); g_assert_false(job->job.busy); /* The job is paused */ do_drain_end(drain_type, src); g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ do_drain_begin(drain_type, target); @@ -761,15 +764,14 @@ static void test_blockjob_common(enum drain_type drain_type) } else { g_assert_cmpint(job->job.pause_count, ==, 1); } - /* XXX We don't wait until the job is actually paused. Is this okay? */ - /* g_assert_true(job->job.paused); */ + g_assert_true(job->job.paused); g_assert_false(job->job.busy); /* The job is paused */ do_drain_end(drain_type, target); g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_false(job->job.busy); /* We're in job_sleep_ns() */ + g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ ret = job_complete_sync(&job->job, &error_abort); g_assert_cmpint(ret, ==, 0); From d30b8e64b7b282da785307504ada59efa8096fb1 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 22 Mar 2018 14:35:58 +0100 Subject: [PATCH 1383/2380] block: Remove bdrv_drain_recurse() For bdrv_drain(), recursively waiting for child node requests is pointless because we didn't quiesce their parents, so new requests could come in anyway. Letting the function work only on a single node makes it more consistent. For subtree drains and drain_all, we already have the recursion in bdrv_do_drained_begin(), so the extra recursion doesn't add anything either. Remove the useless code. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/io.c | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/block/io.c b/block/io.c index 5820e73bb2..5f6d5eed52 100644 --- a/block/io.c +++ b/block/io.c @@ -219,38 +219,6 @@ static bool bdrv_drain_poll_top_level(BlockDriverState *bs, return bdrv_drain_poll(bs, ignore_parent); } -static bool bdrv_drain_recurse(BlockDriverState *bs, BdrvChild *parent) -{ - BdrvChild *child, *tmp; - bool waited; - - /* Wait for drained requests to finish */ - waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); - - QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { - BlockDriverState *bs = child->bs; - bool in_main_loop = - qemu_get_current_aio_context() == qemu_get_aio_context(); - assert(bs->refcnt > 0); - if (in_main_loop) { - /* In case the recursive bdrv_drain_recurse processes a - * block_job_defer_to_main_loop BH and modifies the graph, - * let's hold a reference to bs until we are done. - * - * IOThread doesn't have such a BH, and it is not safe to call - * bdrv_unref without BQL, so skip doing it there. - */ - bdrv_ref(bs); - } - waited |= bdrv_drain_recurse(bs, child); - if (in_main_loop) { - bdrv_unref(bs); - } - } - - return waited; -} - static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, BdrvChild *parent); static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, @@ -318,7 +286,9 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, bdrv_parent_drained_begin(bs, parent); bdrv_drain_invoke(bs, true); - bdrv_drain_recurse(bs, parent); + + /* Wait for drained requests to finish */ + BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); if (recursive) { bs->recursive_quiesce_counter++; From 4c8158e359d194394c64acd21caf5e3f3f3141c2 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 28 Feb 2018 19:04:54 +0100 Subject: [PATCH 1384/2380] test-bdrv-drain: Add test for node deletion This patch adds two bdrv-drain tests for what happens if some BDS goes away during the drainage. The basic idea is that you have a parent BDS with some child nodes. Then, you drain one of the children. Because of that, the party who actually owns the parent decides to (A) delete it, or (B) detach all its children from it -- both while the child is still being drained. A real-world case where this can happen is the mirror block job, which may exit if you drain one of its children. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 169 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 22d31c953e..e8a5515b08 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -797,6 +797,172 @@ static void test_blockjob_drain_subtree(void) test_blockjob_common(BDRV_SUBTREE_DRAIN); } + +typedef struct BDRVTestTopState { + BdrvChild *wait_child; +} BDRVTestTopState; + +static void bdrv_test_top_close(BlockDriverState *bs) +{ + BdrvChild *c, *next_c; + QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { + bdrv_unref_child(bs, c); + } +} + +static int coroutine_fn bdrv_test_top_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + BDRVTestTopState *tts = bs->opaque; + return bdrv_co_preadv(tts->wait_child, offset, bytes, qiov, flags); +} + +static BlockDriver bdrv_test_top_driver = { + .format_name = "test_top_driver", + .instance_size = sizeof(BDRVTestTopState), + + .bdrv_close = bdrv_test_top_close, + .bdrv_co_preadv = bdrv_test_top_co_preadv, + + .bdrv_child_perm = bdrv_format_default_perms, +}; + +typedef struct TestCoDeleteByDrainData { + BlockBackend *blk; + bool detach_instead_of_delete; + bool done; +} TestCoDeleteByDrainData; + +static void coroutine_fn test_co_delete_by_drain(void *opaque) +{ + TestCoDeleteByDrainData *dbdd = opaque; + BlockBackend *blk = dbdd->blk; + BlockDriverState *bs = blk_bs(blk); + BDRVTestTopState *tts = bs->opaque; + void *buffer = g_malloc(65536); + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = buffer, + .iov_len = 65536, + }; + + qemu_iovec_init_external(&qiov, &iov, 1); + + /* Pretend some internal write operation from parent to child. + * Important: We have to read from the child, not from the parent! + * Draining works by first propagating it all up the tree to the + * root and then waiting for drainage from root to the leaves + * (protocol nodes). If we have a request waiting on the root, + * everything will be drained before we go back down the tree, but + * we do not want that. We want to be in the middle of draining + * when this following requests returns. */ + bdrv_co_preadv(tts->wait_child, 0, 65536, &qiov, 0); + + g_assert_cmpint(bs->refcnt, ==, 1); + + if (!dbdd->detach_instead_of_delete) { + blk_unref(blk); + } else { + BdrvChild *c, *next_c; + QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { + bdrv_unref_child(bs, c); + } + } + + dbdd->done = true; +} + +/** + * Test what happens when some BDS has some children, you drain one of + * them and this results in the BDS being deleted. + * + * If @detach_instead_of_delete is set, the BDS is not going to be + * deleted but will only detach all of its children. + */ +static void do_test_delete_by_drain(bool detach_instead_of_delete) +{ + BlockBackend *blk; + BlockDriverState *bs, *child_bs, *null_bs; + BDRVTestTopState *tts; + TestCoDeleteByDrainData dbdd; + Coroutine *co; + + bs = bdrv_new_open_driver(&bdrv_test_top_driver, "top", BDRV_O_RDWR, + &error_abort); + bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; + tts = bs->opaque; + + null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + &error_abort); + bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort); + + /* This child will be the one to pass to requests through to, and + * it will stall until a drain occurs */ + child_bs = bdrv_new_open_driver(&bdrv_test, "child", BDRV_O_RDWR, + &error_abort); + child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; + /* Takes our reference to child_bs */ + tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", &child_file, + &error_abort); + + /* This child is just there to be deleted + * (for detach_instead_of_delete == true) */ + null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + &error_abort); + bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort); + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk, bs, &error_abort); + + /* Referenced by blk now */ + bdrv_unref(bs); + + g_assert_cmpint(bs->refcnt, ==, 1); + g_assert_cmpint(child_bs->refcnt, ==, 1); + g_assert_cmpint(null_bs->refcnt, ==, 1); + + + dbdd = (TestCoDeleteByDrainData){ + .blk = blk, + .detach_instead_of_delete = detach_instead_of_delete, + .done = false, + }; + co = qemu_coroutine_create(test_co_delete_by_drain, &dbdd); + qemu_coroutine_enter(co); + + /* Drain the child while the read operation is still pending. + * This should result in the operation finishing and + * test_co_delete_by_drain() resuming. Thus, @bs will be deleted + * and the coroutine will exit while this drain operation is still + * in progress. */ + bdrv_ref(child_bs); + bdrv_drain(child_bs); + bdrv_unref(child_bs); + + while (!dbdd.done) { + aio_poll(qemu_get_aio_context(), true); + } + + if (detach_instead_of_delete) { + /* Here, the reference has not passed over to the coroutine, + * so we have to delete the BB ourselves */ + blk_unref(blk); + } +} + + +static void test_delete_by_drain(void) +{ + do_test_delete_by_drain(false); +} + +static void test_detach_by_drain(void) +{ + do_test_delete_by_drain(true); +} + + int main(int argc, char **argv) { int ret; @@ -844,6 +1010,9 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/blockjob/drain_subtree", test_blockjob_drain_subtree); + g_test_add_func("/bdrv-drain/deletion", test_delete_by_drain); + g_test_add_func("/bdrv-drain/detach", test_detach_by_drain); + ret = g_test_run(); qemu_event_destroy(&done_event); return ret; From fe4f0614ef9e361dae12012d3c400657444836cf Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 23 Mar 2018 12:40:41 +0100 Subject: [PATCH 1385/2380] block: Drain recursively with a single BDRV_POLL_WHILE() Anything can happen inside BDRV_POLL_WHILE(), including graph changes that may interfere with its callers (e.g. child list iteration in recursive callers of bdrv_do_drained_begin). Switch to a single BDRV_POLL_WHILE() call for the whole subtree at the end of bdrv_do_drained_begin() to avoid such effects. The recursion happens now inside the loop condition. As the graph can only change between bdrv_drain_poll() calls, but not inside of it, doing the recursion here is safe. Signed-off-by: Kevin Wolf --- block.c | 2 +- block/io.c | 63 ++++++++++++++++++++++++++++++------------- include/block/block.h | 9 ++++--- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/block.c b/block.c index 8cf9cd8855..80abd3c2ae 100644 --- a/block.c +++ b/block.c @@ -824,7 +824,7 @@ static void bdrv_child_cb_drained_begin(BdrvChild *child) static bool bdrv_child_cb_drained_poll(BdrvChild *child) { BlockDriverState *bs = child->opaque; - return bdrv_drain_poll(bs, NULL); + return bdrv_drain_poll(bs, false, NULL); } static void bdrv_child_cb_drained_end(BdrvChild *child) diff --git a/block/io.c b/block/io.c index 5f6d5eed52..a413841bfc 100644 --- a/block/io.c +++ b/block/io.c @@ -165,6 +165,7 @@ typedef struct { bool done; bool begin; bool recursive; + bool poll; BdrvChild *parent; } BdrvCoDrainData; @@ -200,27 +201,42 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) } /* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ -bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent) +bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, + BdrvChild *ignore_parent) { + BdrvChild *child, *next; + if (bdrv_parent_drained_poll(bs, ignore_parent)) { return true; } - return atomic_read(&bs->in_flight); + if (atomic_read(&bs->in_flight)) { + return true; + } + + if (recursive) { + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { + if (bdrv_drain_poll(child->bs, recursive, child)) { + return true; + } + } + } + + return false; } -static bool bdrv_drain_poll_top_level(BlockDriverState *bs, +static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive, BdrvChild *ignore_parent) { /* Execute pending BHs first and check everything else only after the BHs * have executed. */ while (aio_poll(bs->aio_context, false)); - return bdrv_drain_poll(bs, ignore_parent); + return bdrv_drain_poll(bs, recursive, ignore_parent); } static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, - BdrvChild *parent); + BdrvChild *parent, bool poll); static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, BdrvChild *parent); @@ -232,7 +248,7 @@ static void bdrv_co_drain_bh_cb(void *opaque) bdrv_dec_in_flight(bs); if (data->begin) { - bdrv_do_drained_begin(bs, data->recursive, data->parent); + bdrv_do_drained_begin(bs, data->recursive, data->parent, data->poll); } else { bdrv_do_drained_end(bs, data->recursive, data->parent); } @@ -243,7 +259,7 @@ static void bdrv_co_drain_bh_cb(void *opaque) static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, bool begin, bool recursive, - BdrvChild *parent) + BdrvChild *parent, bool poll) { BdrvCoDrainData data; @@ -258,6 +274,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, .begin = begin, .recursive = recursive, .parent = parent, + .poll = poll, }; bdrv_inc_in_flight(bs); aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), @@ -270,12 +287,12 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, } void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, - BdrvChild *parent) + BdrvChild *parent, bool poll) { BdrvChild *child, *next; if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, true, recursive, parent); + bdrv_co_yield_to_drain(bs, true, recursive, parent, poll); return; } @@ -287,25 +304,35 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, bdrv_parent_drained_begin(bs, parent); bdrv_drain_invoke(bs, true); - /* Wait for drained requests to finish */ - BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); - if (recursive) { bs->recursive_quiesce_counter++; QLIST_FOREACH_SAFE(child, &bs->children, next, next) { - bdrv_do_drained_begin(child->bs, true, child); + bdrv_do_drained_begin(child->bs, true, child, false); } } + + /* + * Wait for drained requests to finish. + * + * Calling BDRV_POLL_WHILE() only once for the top-level node is okay: The + * call is needed so things in this AioContext can make progress even + * though we don't return to the main AioContext loop - this automatically + * includes other nodes in the same AioContext and therefore all child + * nodes. + */ + if (poll) { + BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, recursive, parent)); + } } void bdrv_drained_begin(BlockDriverState *bs) { - bdrv_do_drained_begin(bs, false, NULL); + bdrv_do_drained_begin(bs, false, NULL, true); } void bdrv_subtree_drained_begin(BlockDriverState *bs) { - bdrv_do_drained_begin(bs, true, NULL); + bdrv_do_drained_begin(bs, true, NULL, true); } void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, @@ -315,7 +342,7 @@ void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, int old_quiesce_counter; if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, false, recursive, parent); + bdrv_co_yield_to_drain(bs, false, recursive, parent, false); return; } assert(bs->quiesce_counter > 0); @@ -351,7 +378,7 @@ void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) int i; for (i = 0; i < new_parent->recursive_quiesce_counter; i++) { - bdrv_do_drained_begin(child->bs, true, child); + bdrv_do_drained_begin(child->bs, true, child, true); } } @@ -421,7 +448,7 @@ void bdrv_drain_all_begin(void) AioContext *aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - bdrv_do_drained_begin(bs, true, NULL); + bdrv_do_drained_begin(bs, true, NULL, true); aio_context_release(aio_context); } diff --git a/include/block/block.h b/include/block/block.h index cebbb39c6c..254ed2e4c9 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -570,10 +570,13 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); /** * bdrv_drain_poll: * - * Poll for pending requests in @bs and its parents (except for - * @ignore_parent). This is part of bdrv_drained_begin. + * Poll for pending requests in @bs, its parents (except for @ignore_parent), + * and if @recursive is true its children as well. + * + * This is part of bdrv_drained_begin. */ -bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent); +bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, + BdrvChild *ignore_parent); /** * bdrv_drained_begin: From ebd31837618cdc7bda83090773dcdd87475d55b7 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 23 Mar 2018 12:40:21 +0100 Subject: [PATCH 1386/2380] test-bdrv-drain: Test node deletion in subtree recursion If bdrv_do_drained_begin() polls during its subtree recursion, the graph can change and mess up the bs->children iteration. Test that this doesn't happen. Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index e8a5515b08..0c8f632f2d 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -880,7 +880,8 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque) * If @detach_instead_of_delete is set, the BDS is not going to be * deleted but will only detach all of its children. */ -static void do_test_delete_by_drain(bool detach_instead_of_delete) +static void do_test_delete_by_drain(bool detach_instead_of_delete, + enum drain_type drain_type) { BlockBackend *blk; BlockDriverState *bs, *child_bs, *null_bs; @@ -936,9 +937,23 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete) * test_co_delete_by_drain() resuming. Thus, @bs will be deleted * and the coroutine will exit while this drain operation is still * in progress. */ - bdrv_ref(child_bs); - bdrv_drain(child_bs); - bdrv_unref(child_bs); + switch (drain_type) { + case BDRV_DRAIN: + bdrv_ref(child_bs); + bdrv_drain(child_bs); + bdrv_unref(child_bs); + break; + case BDRV_SUBTREE_DRAIN: + /* Would have to ref/unref bs here for !detach_instead_of_delete, but + * then the whole test becomes pointless because the graph changes + * don't occur during the drain any more. */ + assert(detach_instead_of_delete); + bdrv_subtree_drained_begin(bs); + bdrv_subtree_drained_end(bs); + break; + default: + g_assert_not_reached(); + } while (!dbdd.done) { aio_poll(qemu_get_aio_context(), true); @@ -951,15 +966,19 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete) } } - static void test_delete_by_drain(void) { - do_test_delete_by_drain(false); + do_test_delete_by_drain(false, BDRV_DRAIN); } static void test_detach_by_drain(void) { - do_test_delete_by_drain(true); + do_test_delete_by_drain(true, BDRV_DRAIN); +} + +static void test_detach_by_drain_subtree(void) +{ + do_test_delete_by_drain(true, BDRV_SUBTREE_DRAIN); } @@ -1010,8 +1029,9 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/blockjob/drain_subtree", test_blockjob_drain_subtree); - g_test_add_func("/bdrv-drain/deletion", test_delete_by_drain); - g_test_add_func("/bdrv-drain/detach", test_detach_by_drain); + g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); + g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); ret = g_test_run(); qemu_event_destroy(&done_event); From dcf94a23b1add0f856db51e9ff5ba0774e096076 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 23 Mar 2018 15:57:20 +0100 Subject: [PATCH 1387/2380] block: Don't poll in parent drain callbacks bdrv_do_drained_begin() is only safe if we have a single BDRV_POLL_WHILE() after quiescing all affected nodes. We cannot allow that parent callbacks introduce a nested polling loop that could cause graph changes while we're traversing the graph. Split off bdrv_do_drained_begin_quiesce(), which only quiesces a single node without waiting for its requests to complete. These requests will be waited for in the BDRV_POLL_WHILE() call down the call chain. Signed-off-by: Kevin Wolf --- block.c | 2 +- block/io.c | 24 ++++++++++++++++-------- include/block/block.h | 9 +++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/block.c b/block.c index 80abd3c2ae..50f8e3dc3b 100644 --- a/block.c +++ b/block.c @@ -818,7 +818,7 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c) static void bdrv_child_cb_drained_begin(BdrvChild *child) { BlockDriverState *bs = child->opaque; - bdrv_drained_begin(bs); + bdrv_do_drained_begin_quiesce(bs, NULL); } static bool bdrv_child_cb_drained_poll(BdrvChild *child) diff --git a/block/io.c b/block/io.c index a413841bfc..ffb273708a 100644 --- a/block/io.c +++ b/block/io.c @@ -286,15 +286,10 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, assert(data.done); } -void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, - BdrvChild *parent, bool poll) +void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, + BdrvChild *parent) { - BdrvChild *child, *next; - - if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, true, recursive, parent, poll); - return; - } + assert(!qemu_in_coroutine()); /* Stop things in parent-to-child order */ if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { @@ -303,6 +298,19 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, bdrv_parent_drained_begin(bs, parent); bdrv_drain_invoke(bs, true); +} + +static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + BdrvChild *parent, bool poll) +{ + BdrvChild *child, *next; + + if (qemu_in_coroutine()) { + bdrv_co_yield_to_drain(bs, true, recursive, parent, poll); + return; + } + + bdrv_do_drained_begin_quiesce(bs, parent); if (recursive) { bs->recursive_quiesce_counter++; diff --git a/include/block/block.h b/include/block/block.h index 254ed2e4c9..067d24cc4a 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -590,6 +590,15 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, */ void bdrv_drained_begin(BlockDriverState *bs); +/** + * bdrv_do_drained_begin_quiesce: + * + * Quiesces a BDS like bdrv_drained_begin(), but does not wait for already + * running requests to complete. + */ +void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, + BdrvChild *parent); + /** * Like bdrv_drained_begin, but recursively begins a quiesced section for * exclusive access to all child nodes as well. From 231281ab42dad2b407b941e36ad11cbc6586e937 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 23 Mar 2018 18:48:39 +0100 Subject: [PATCH 1388/2380] test-bdrv-drain: Graph change through parent callback Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 130 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 0c8f632f2d..abb287e597 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -982,6 +982,135 @@ static void test_detach_by_drain_subtree(void) } +struct detach_by_parent_data { + BlockDriverState *parent_b; + BdrvChild *child_b; + BlockDriverState *c; + BdrvChild *child_c; +}; + +static void detach_by_parent_aio_cb(void *opaque, int ret) +{ + struct detach_by_parent_data *data = opaque; + + g_assert_cmpint(ret, ==, 0); + bdrv_unref_child(data->parent_b, data->child_b); + + bdrv_ref(data->c); + data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C", + &child_file, &error_abort); +} + +/* + * Initial graph: + * + * PA PB + * \ / \ + * A B C + * + * PA has a pending write request whose callback changes the child nodes of PB: + * It removes B and adds C instead. The subtree of PB is drained, which will + * indirectly drain the write request, too. + */ +static void test_detach_by_parent_cb(void) +{ + BlockBackend *blk; + BlockDriverState *parent_a, *parent_b, *a, *b, *c; + BdrvChild *child_a, *child_b; + BlockAIOCB *acb; + struct detach_by_parent_data data; + + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = NULL, + .iov_len = 0, + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + /* Create all involved nodes */ + parent_a = bdrv_new_open_driver(&bdrv_test, "parent-a", BDRV_O_RDWR, + &error_abort); + parent_b = bdrv_new_open_driver(&bdrv_test, "parent-b", 0, + &error_abort); + + a = bdrv_new_open_driver(&bdrv_test, "a", BDRV_O_RDWR, &error_abort); + b = bdrv_new_open_driver(&bdrv_test, "b", BDRV_O_RDWR, &error_abort); + c = bdrv_new_open_driver(&bdrv_test, "c", BDRV_O_RDWR, &error_abort); + + /* blk is a BB for parent-a */ + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk, parent_a, &error_abort); + bdrv_unref(parent_a); + + /* Set child relationships */ + bdrv_ref(b); + bdrv_ref(a); + child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_file, &error_abort); + child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_backing, &error_abort); + + bdrv_ref(a); + bdrv_attach_child(parent_a, a, "PA-A", &child_file, &error_abort); + + g_assert_cmpint(parent_a->refcnt, ==, 1); + g_assert_cmpint(parent_b->refcnt, ==, 1); + g_assert_cmpint(a->refcnt, ==, 3); + g_assert_cmpint(b->refcnt, ==, 2); + g_assert_cmpint(c->refcnt, ==, 1); + + g_assert(QLIST_FIRST(&parent_b->children) == child_a); + g_assert(QLIST_NEXT(child_a, next) == child_b); + g_assert(QLIST_NEXT(child_b, next) == NULL); + + /* Start the evil write request */ + data = (struct detach_by_parent_data) { + .parent_b = parent_b, + .child_b = child_b, + .c = c, + }; + acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, &data); + g_assert(acb != NULL); + + /* Drain and check the expected result */ + bdrv_subtree_drained_begin(parent_b); + + g_assert(data.child_c != NULL); + + g_assert_cmpint(parent_a->refcnt, ==, 1); + g_assert_cmpint(parent_b->refcnt, ==, 1); + g_assert_cmpint(a->refcnt, ==, 3); + g_assert_cmpint(b->refcnt, ==, 1); + g_assert_cmpint(c->refcnt, ==, 2); + + g_assert(QLIST_FIRST(&parent_b->children) == data.child_c); + g_assert(QLIST_NEXT(data.child_c, next) == child_a); + g_assert(QLIST_NEXT(child_a, next) == NULL); + + g_assert_cmpint(parent_a->quiesce_counter, ==, 1); + g_assert_cmpint(parent_b->quiesce_counter, ==, 1); + g_assert_cmpint(a->quiesce_counter, ==, 1); + g_assert_cmpint(b->quiesce_counter, ==, 0); + g_assert_cmpint(c->quiesce_counter, ==, 1); + + bdrv_subtree_drained_end(parent_b); + + bdrv_unref(parent_b); + blk_unref(blk); + + /* XXX Once bdrv_close() unref's children instead of just detaching them, + * this won't be necessary any more. */ + bdrv_unref(a); + bdrv_unref(a); + bdrv_unref(c); + + g_assert_cmpint(a->refcnt, ==, 1); + g_assert_cmpint(b->refcnt, ==, 1); + g_assert_cmpint(c->refcnt, ==, 1); + bdrv_unref(a); + bdrv_unref(b); + bdrv_unref(c); +} + + int main(int argc, char **argv) { int ret; @@ -1032,6 +1161,7 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); + g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); ret = g_test_run(); qemu_event_destroy(&done_event); From 0109e7e6f83ae5166b81bbd9a4319d60be49985a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 23 Mar 2018 20:29:24 +0100 Subject: [PATCH 1389/2380] block: Defer .bdrv_drain_begin callback to polling phase We cannot allow aio_poll() in bdrv_drain_invoke(begin=true) until we're done with propagating the drain through the graph and are doing the single final BDRV_POLL_WHILE(). Just schedule the coroutine with the callback and increase bs->in_flight to make sure that the polling phase will wait for it. Signed-off-by: Kevin Wolf --- block/io.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/block/io.c b/block/io.c index ffb273708a..e08d53c8a5 100644 --- a/block/io.c +++ b/block/io.c @@ -182,22 +182,40 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) /* Set data->done before reading bs->wakeup. */ atomic_mb_set(&data->done, true); - bdrv_wakeup(bs); + bdrv_dec_in_flight(bs); + + if (data->begin) { + g_free(data); + } } /* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) { - BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; + BdrvCoDrainData *data; if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) || (!begin && !bs->drv->bdrv_co_drain_end)) { return; } - data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data); - bdrv_coroutine_enter(bs, data.co); - BDRV_POLL_WHILE(bs, !data.done); + data = g_new(BdrvCoDrainData, 1); + *data = (BdrvCoDrainData) { + .bs = bs, + .done = false, + .begin = begin + }; + + /* Make sure the driver callback completes during the polling phase for + * drain_begin. */ + bdrv_inc_in_flight(bs); + data->co = qemu_coroutine_create(bdrv_drain_invoke_entry, data); + aio_co_schedule(bdrv_get_aio_context(bs), data->co); + + if (!begin) { + BDRV_POLL_WHILE(bs, !data->done); + g_free(data); + } } /* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ From 57320ca961c2e8488e1884b4ebbcb929b6901dc6 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 23 Mar 2018 20:10:52 +0100 Subject: [PATCH 1390/2380] test-bdrv-drain: Test that bdrv_drain_invoke() doesn't poll This adds a test case that goes wrong if bdrv_drain_invoke() calls aio_poll(). Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 102 ++++++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 14 deletions(-) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index abb287e597..5cc179a778 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -34,12 +34,16 @@ static QemuEvent done_event; typedef struct BDRVTestState { int drain_count; AioContext *bh_indirection_ctx; + bool sleep_in_drain_begin; } BDRVTestState; static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) { BDRVTestState *s = bs->opaque; s->drain_count++; + if (s->sleep_in_drain_begin) { + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + } } static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs) @@ -80,6 +84,22 @@ static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, return 0; } +static void bdrv_test_child_perm(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole *role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + /* bdrv_format_default_perms() accepts only these two, so disguise + * detach_by_driver_cb_role as one of them. */ + if (role != &child_file && role != &child_backing) { + role = &child_file; + } + + bdrv_format_default_perms(bs, c, role, reopen_queue, perm, shared, + nperm, nshared); +} + static BlockDriver bdrv_test = { .format_name = "test", .instance_size = sizeof(BDRVTestState), @@ -90,7 +110,7 @@ static BlockDriver bdrv_test = { .bdrv_co_drain_begin = bdrv_test_co_drain_begin, .bdrv_co_drain_end = bdrv_test_co_drain_end, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_test_child_perm, }; static void aio_ret_cb(void *opaque, int ret) @@ -987,13 +1007,14 @@ struct detach_by_parent_data { BdrvChild *child_b; BlockDriverState *c; BdrvChild *child_c; + bool by_parent_cb; }; +static struct detach_by_parent_data detach_by_parent_data; -static void detach_by_parent_aio_cb(void *opaque, int ret) +static void detach_indirect_bh(void *opaque) { struct detach_by_parent_data *data = opaque; - g_assert_cmpint(ret, ==, 0); bdrv_unref_child(data->parent_b, data->child_b); bdrv_ref(data->c); @@ -1001,6 +1022,25 @@ static void detach_by_parent_aio_cb(void *opaque, int ret) &child_file, &error_abort); } +static void detach_by_parent_aio_cb(void *opaque, int ret) +{ + struct detach_by_parent_data *data = &detach_by_parent_data; + + g_assert_cmpint(ret, ==, 0); + if (data->by_parent_cb) { + detach_indirect_bh(data); + } +} + +static void detach_by_driver_cb_drained_begin(BdrvChild *child) +{ + aio_bh_schedule_oneshot(qemu_get_current_aio_context(), + detach_indirect_bh, &detach_by_parent_data); + child_file.drained_begin(child); +} + +static BdrvChildRole detach_by_driver_cb_role; + /* * Initial graph: * @@ -1008,17 +1048,25 @@ static void detach_by_parent_aio_cb(void *opaque, int ret) * \ / \ * A B C * - * PA has a pending write request whose callback changes the child nodes of PB: - * It removes B and adds C instead. The subtree of PB is drained, which will - * indirectly drain the write request, too. + * by_parent_cb == true: Test that parent callbacks don't poll + * + * PA has a pending write request whose callback changes the child nodes of + * PB: It removes B and adds C instead. The subtree of PB is drained, which + * will indirectly drain the write request, too. + * + * by_parent_cb == false: Test that bdrv_drain_invoke() doesn't poll + * + * PA's BdrvChildRole has a .drained_begin callback that schedules a BH + * that does the same graph change. If bdrv_drain_invoke() calls it, the + * state is messed up, but if it is only polled in the single + * BDRV_POLL_WHILE() at the end of the drain, this should work fine. */ -static void test_detach_by_parent_cb(void) +static void test_detach_indirect(bool by_parent_cb) { BlockBackend *blk; BlockDriverState *parent_a, *parent_b, *a, *b, *c; BdrvChild *child_a, *child_b; BlockAIOCB *acb; - struct detach_by_parent_data data; QEMUIOVector qiov; struct iovec iov = { @@ -1027,6 +1075,12 @@ static void test_detach_by_parent_cb(void) }; qemu_iovec_init_external(&qiov, &iov, 1); + if (!by_parent_cb) { + detach_by_driver_cb_role = child_file; + detach_by_driver_cb_role.drained_begin = + detach_by_driver_cb_drained_begin; + } + /* Create all involved nodes */ parent_a = bdrv_new_open_driver(&bdrv_test, "parent-a", BDRV_O_RDWR, &error_abort); @@ -1042,6 +1096,13 @@ static void test_detach_by_parent_cb(void) blk_insert_bs(blk, parent_a, &error_abort); bdrv_unref(parent_a); + /* If we want to get bdrv_drain_invoke() to call aio_poll(), the driver + * callback must not return immediately. */ + if (!by_parent_cb) { + BDRVTestState *s = parent_a->opaque; + s->sleep_in_drain_begin = true; + } + /* Set child relationships */ bdrv_ref(b); bdrv_ref(a); @@ -1049,7 +1110,9 @@ static void test_detach_by_parent_cb(void) child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_backing, &error_abort); bdrv_ref(a); - bdrv_attach_child(parent_a, a, "PA-A", &child_file, &error_abort); + bdrv_attach_child(parent_a, a, "PA-A", + by_parent_cb ? &child_file : &detach_by_driver_cb_role, + &error_abort); g_assert_cmpint(parent_a->refcnt, ==, 1); g_assert_cmpint(parent_b->refcnt, ==, 1); @@ -1062,18 +1125,19 @@ static void test_detach_by_parent_cb(void) g_assert(QLIST_NEXT(child_b, next) == NULL); /* Start the evil write request */ - data = (struct detach_by_parent_data) { + detach_by_parent_data = (struct detach_by_parent_data) { .parent_b = parent_b, .child_b = child_b, .c = c, + .by_parent_cb = by_parent_cb, }; - acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, &data); + acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, NULL); g_assert(acb != NULL); /* Drain and check the expected result */ bdrv_subtree_drained_begin(parent_b); - g_assert(data.child_c != NULL); + g_assert(detach_by_parent_data.child_c != NULL); g_assert_cmpint(parent_a->refcnt, ==, 1); g_assert_cmpint(parent_b->refcnt, ==, 1); @@ -1081,8 +1145,8 @@ static void test_detach_by_parent_cb(void) g_assert_cmpint(b->refcnt, ==, 1); g_assert_cmpint(c->refcnt, ==, 2); - g_assert(QLIST_FIRST(&parent_b->children) == data.child_c); - g_assert(QLIST_NEXT(data.child_c, next) == child_a); + g_assert(QLIST_FIRST(&parent_b->children) == detach_by_parent_data.child_c); + g_assert(QLIST_NEXT(detach_by_parent_data.child_c, next) == child_a); g_assert(QLIST_NEXT(child_a, next) == NULL); g_assert_cmpint(parent_a->quiesce_counter, ==, 1); @@ -1110,6 +1174,15 @@ static void test_detach_by_parent_cb(void) bdrv_unref(c); } +static void test_detach_by_parent_cb(void) +{ + test_detach_indirect(true); +} + +static void test_detach_by_driver_cb(void) +{ + test_detach_indirect(false); +} int main(int argc, char **argv) { @@ -1162,6 +1235,7 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); + g_test_add_func("/bdrv-drain/detach/driver_cb", test_detach_by_driver_cb); ret = g_test_run(); qemu_event_destroy(&done_event); From 4d22bbf4ef72583eefdf44db6bf9fc7683fbc4c2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 10 Apr 2018 15:51:52 +0200 Subject: [PATCH 1391/2380] block: Allow AIO_WAIT_WHILE with NULL ctx bdrv_drain_all() wants to have a single polling loop for draining the in-flight requests of all nodes. This means that the AIO_WAIT_WHILE() condition relies on activity in multiple AioContexts, which is polled from the mainloop context. We must therefore call AIO_WAIT_WHILE() from the mainloop thread and use the AioWait notification mechanism. Just randomly picking the AioContext of any non-mainloop thread would work, but instead of bothering to find such a context in the caller, we can just as well accept NULL for ctx. Signed-off-by: Kevin Wolf --- include/block/aio-wait.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h index 783d3678dd..c85a62f798 100644 --- a/include/block/aio-wait.h +++ b/include/block/aio-wait.h @@ -57,7 +57,8 @@ typedef struct { /** * AIO_WAIT_WHILE: * @wait: the aio wait object - * @ctx: the aio context + * @ctx: the aio context, or NULL if multiple aio contexts (for which the + * caller does not hold a lock) are involved in the polling condition. * @cond: wait while this conditional expression is true * * Wait while a condition is true. Use this to implement synchronous @@ -75,7 +76,7 @@ typedef struct { bool waited_ = false; \ AioWait *wait_ = (wait); \ AioContext *ctx_ = (ctx); \ - if (in_aio_context_home_thread(ctx_)) { \ + if (ctx_ && in_aio_context_home_thread(ctx_)) { \ while ((cond)) { \ aio_poll(ctx_, true); \ waited_ = true; \ @@ -86,9 +87,13 @@ typedef struct { /* Increment wait_->num_waiters before evaluating cond. */ \ atomic_inc(&wait_->num_waiters); \ while ((cond)) { \ - aio_context_release(ctx_); \ + if (ctx_) { \ + aio_context_release(ctx_); \ + } \ aio_poll(qemu_get_aio_context(), true); \ - aio_context_acquire(ctx_); \ + if (ctx_) { \ + aio_context_acquire(ctx_); \ + } \ waited_ = true; \ } \ atomic_dec(&wait_->num_waiters); \ From c8ca33d06def97d909a8511377b82994ae4e5981 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 10 Apr 2018 16:07:55 +0200 Subject: [PATCH 1392/2380] block: Move bdrv_drain_all_begin() out of coroutine context Before we can introduce a single polling loop for all nodes in bdrv_drain_all_begin(), we must make sure to run it outside of coroutine context like we already do for bdrv_do_drained_begin(). Signed-off-by: Kevin Wolf --- block/io.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/block/io.c b/block/io.c index e08d53c8a5..1a9974c136 100644 --- a/block/io.c +++ b/block/io.c @@ -264,11 +264,16 @@ static void bdrv_co_drain_bh_cb(void *opaque) Coroutine *co = data->co; BlockDriverState *bs = data->bs; - bdrv_dec_in_flight(bs); - if (data->begin) { - bdrv_do_drained_begin(bs, data->recursive, data->parent, data->poll); + if (bs) { + bdrv_dec_in_flight(bs); + if (data->begin) { + bdrv_do_drained_begin(bs, data->recursive, data->parent, data->poll); + } else { + bdrv_do_drained_end(bs, data->recursive, data->parent); + } } else { - bdrv_do_drained_end(bs, data->recursive, data->parent); + assert(data->begin); + bdrv_drain_all_begin(); } data->done = true; @@ -294,7 +299,9 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, .parent = parent, .poll = poll, }; - bdrv_inc_in_flight(bs); + if (bs) { + bdrv_inc_in_flight(bs); + } aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), bdrv_co_drain_bh_cb, &data); @@ -464,6 +471,11 @@ void bdrv_drain_all_begin(void) BlockDriverState *bs; BdrvNextIterator it; + if (qemu_in_coroutine()) { + bdrv_co_yield_to_drain(NULL, true, false, NULL, true); + return; + } + /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on * nodes in several different AioContexts, so make sure we're in the main From 6cd5c9d7b2df93ef54144f170d4c908934a4767f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 29 May 2018 17:17:45 +0200 Subject: [PATCH 1393/2380] block: ignore_bds_parents parameter for drain functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the future, bdrv_drained_all_begin/end() will drain all invidiual nodes separately rather than whole subtrees. This means that we don't want to propagate the drain to all parents any more: If the parent is a BDS, it will already be drained separately. Recursing to all parents is unnecessary work and would make it an O(n²) operation. Prepare the drain function for the changed drain_all by adding an ignore_bds_parents parameter to the internal implementation that prevents the propagation of the drain to BDS parents. We still (have to) propagate it to non-BDS parents like BlockBackends or Jobs because those are not drained separately. Signed-off-by: Kevin Wolf --- block.c | 11 +++-- block/io.c | 88 +++++++++++++++++++++++---------------- block/vvfat.c | 1 + include/block/block.h | 16 ++++--- include/block/block_int.h | 6 +++ 5 files changed, 78 insertions(+), 44 deletions(-) diff --git a/block.c b/block.c index 50f8e3dc3b..c8586f41ba 100644 --- a/block.c +++ b/block.c @@ -818,13 +818,13 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c) static void bdrv_child_cb_drained_begin(BdrvChild *child) { BlockDriverState *bs = child->opaque; - bdrv_do_drained_begin_quiesce(bs, NULL); + bdrv_do_drained_begin_quiesce(bs, NULL, false); } static bool bdrv_child_cb_drained_poll(BdrvChild *child) { BlockDriverState *bs = child->opaque; - return bdrv_drain_poll(bs, false, NULL); + return bdrv_drain_poll(bs, false, NULL, false); } static void bdrv_child_cb_drained_end(BdrvChild *child) @@ -908,6 +908,7 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options, } const BdrvChildRole child_file = { + .parent_is_bds = true, .get_parent_desc = bdrv_child_get_parent_desc, .inherit_options = bdrv_inherited_options, .drained_begin = bdrv_child_cb_drained_begin, @@ -933,6 +934,7 @@ static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options, } const BdrvChildRole child_format = { + .parent_is_bds = true, .get_parent_desc = bdrv_child_get_parent_desc, .inherit_options = bdrv_inherited_fmt_options, .drained_begin = bdrv_child_cb_drained_begin, @@ -1051,6 +1053,7 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, } const BdrvChildRole child_backing = { + .parent_is_bds = true, .get_parent_desc = bdrv_child_get_parent_desc, .attach = bdrv_backing_attach, .detach = bdrv_backing_detach, @@ -4957,7 +4960,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) AioContext *ctx = bdrv_get_aio_context(bs); aio_disable_external(ctx); - bdrv_parent_drained_begin(bs, NULL); + bdrv_parent_drained_begin(bs, NULL, false); bdrv_drain(bs); /* ensure there are no in-flight requests */ while (aio_poll(ctx, false)) { @@ -4971,7 +4974,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) */ aio_context_acquire(new_context); bdrv_attach_aio_context(bs, new_context); - bdrv_parent_drained_end(bs, NULL); + bdrv_parent_drained_end(bs, NULL, false); aio_enable_external(ctx); aio_context_release(new_context); } diff --git a/block/io.c b/block/io.c index 1a9974c136..1834a14aa6 100644 --- a/block/io.c +++ b/block/io.c @@ -41,12 +41,13 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags); -void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore) +void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents) { BdrvChild *c, *next; QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { - if (c == ignore) { + if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { continue; } if (c->role->drained_begin) { @@ -55,12 +56,13 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore) } } -void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) +void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents) { BdrvChild *c, *next; QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { - if (c == ignore) { + if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { continue; } if (c->role->drained_end) { @@ -69,13 +71,14 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) } } -static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore) +static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents) { BdrvChild *c, *next; bool busy = false; QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { - if (c == ignore) { + if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { continue; } if (c->role->drained_poll) { @@ -167,6 +170,7 @@ typedef struct { bool recursive; bool poll; BdrvChild *parent; + bool ignore_bds_parents; } BdrvCoDrainData; static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) @@ -220,11 +224,11 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) /* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, - BdrvChild *ignore_parent) + BdrvChild *ignore_parent, bool ignore_bds_parents) { BdrvChild *child, *next; - if (bdrv_parent_drained_poll(bs, ignore_parent)) { + if (bdrv_parent_drained_poll(bs, ignore_parent, ignore_bds_parents)) { return true; } @@ -233,8 +237,9 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, } if (recursive) { + assert(!ignore_bds_parents); QLIST_FOREACH_SAFE(child, &bs->children, next, next) { - if (bdrv_drain_poll(child->bs, recursive, child)) { + if (bdrv_drain_poll(child->bs, recursive, child, false)) { return true; } } @@ -250,13 +255,14 @@ static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive, * have executed. */ while (aio_poll(bs->aio_context, false)); - return bdrv_drain_poll(bs, recursive, ignore_parent); + return bdrv_drain_poll(bs, recursive, ignore_parent, false); } static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, - BdrvChild *parent, bool poll); + BdrvChild *parent, bool ignore_bds_parents, + bool poll); static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, - BdrvChild *parent); + BdrvChild *parent, bool ignore_bds_parents); static void bdrv_co_drain_bh_cb(void *opaque) { @@ -267,9 +273,11 @@ static void bdrv_co_drain_bh_cb(void *opaque) if (bs) { bdrv_dec_in_flight(bs); if (data->begin) { - bdrv_do_drained_begin(bs, data->recursive, data->parent, data->poll); + bdrv_do_drained_begin(bs, data->recursive, data->parent, + data->ignore_bds_parents, data->poll); } else { - bdrv_do_drained_end(bs, data->recursive, data->parent); + bdrv_do_drained_end(bs, data->recursive, data->parent, + data->ignore_bds_parents); } } else { assert(data->begin); @@ -282,7 +290,9 @@ static void bdrv_co_drain_bh_cb(void *opaque) static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, bool begin, bool recursive, - BdrvChild *parent, bool poll) + BdrvChild *parent, + bool ignore_bds_parents, + bool poll) { BdrvCoDrainData data; @@ -297,6 +307,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, .begin = begin, .recursive = recursive, .parent = parent, + .ignore_bds_parents = ignore_bds_parents, .poll = poll, }; if (bs) { @@ -312,7 +323,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, } void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, - BdrvChild *parent) + BdrvChild *parent, bool ignore_bds_parents) { assert(!qemu_in_coroutine()); @@ -321,26 +332,30 @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, aio_disable_external(bdrv_get_aio_context(bs)); } - bdrv_parent_drained_begin(bs, parent); + bdrv_parent_drained_begin(bs, parent, ignore_bds_parents); bdrv_drain_invoke(bs, true); } static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, - BdrvChild *parent, bool poll) + BdrvChild *parent, bool ignore_bds_parents, + bool poll) { BdrvChild *child, *next; if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, true, recursive, parent, poll); + bdrv_co_yield_to_drain(bs, true, recursive, parent, ignore_bds_parents, + poll); return; } - bdrv_do_drained_begin_quiesce(bs, parent); + bdrv_do_drained_begin_quiesce(bs, parent, ignore_bds_parents); if (recursive) { + assert(!ignore_bds_parents); bs->recursive_quiesce_counter++; QLIST_FOREACH_SAFE(child, &bs->children, next, next) { - bdrv_do_drained_begin(child->bs, true, child, false); + bdrv_do_drained_begin(child->bs, true, child, ignore_bds_parents, + false); } } @@ -354,28 +369,30 @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, * nodes. */ if (poll) { + assert(!ignore_bds_parents); BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, recursive, parent)); } } void bdrv_drained_begin(BlockDriverState *bs) { - bdrv_do_drained_begin(bs, false, NULL, true); + bdrv_do_drained_begin(bs, false, NULL, false, true); } void bdrv_subtree_drained_begin(BlockDriverState *bs) { - bdrv_do_drained_begin(bs, true, NULL, true); + bdrv_do_drained_begin(bs, true, NULL, false, true); } -void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, - BdrvChild *parent) +static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, + BdrvChild *parent, bool ignore_bds_parents) { BdrvChild *child, *next; int old_quiesce_counter; if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, false, recursive, parent, false); + bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents, + false); return; } assert(bs->quiesce_counter > 0); @@ -383,27 +400,28 @@ void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, /* Re-enable things in child-to-parent order */ bdrv_drain_invoke(bs, false); - bdrv_parent_drained_end(bs, parent); + bdrv_parent_drained_end(bs, parent, ignore_bds_parents); if (old_quiesce_counter == 1) { aio_enable_external(bdrv_get_aio_context(bs)); } if (recursive) { + assert(!ignore_bds_parents); bs->recursive_quiesce_counter--; QLIST_FOREACH_SAFE(child, &bs->children, next, next) { - bdrv_do_drained_end(child->bs, true, child); + bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents); } } } void bdrv_drained_end(BlockDriverState *bs) { - bdrv_do_drained_end(bs, false, NULL); + bdrv_do_drained_end(bs, false, NULL, false); } void bdrv_subtree_drained_end(BlockDriverState *bs) { - bdrv_do_drained_end(bs, true, NULL); + bdrv_do_drained_end(bs, true, NULL, false); } void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) @@ -411,7 +429,7 @@ void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) int i; for (i = 0; i < new_parent->recursive_quiesce_counter; i++) { - bdrv_do_drained_begin(child->bs, true, child, true); + bdrv_do_drained_begin(child->bs, true, child, false, true); } } @@ -420,7 +438,7 @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) int i; for (i = 0; i < old_parent->recursive_quiesce_counter; i++) { - bdrv_do_drained_end(child->bs, true, child); + bdrv_do_drained_end(child->bs, true, child, false); } } @@ -472,7 +490,7 @@ void bdrv_drain_all_begin(void) BdrvNextIterator it; if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(NULL, true, false, NULL, true); + bdrv_co_yield_to_drain(NULL, true, false, NULL, false, true); return; } @@ -486,7 +504,7 @@ void bdrv_drain_all_begin(void) AioContext *aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - bdrv_do_drained_begin(bs, true, NULL, true); + bdrv_do_drained_begin(bs, true, NULL, false, true); aio_context_release(aio_context); } @@ -504,7 +522,7 @@ void bdrv_drain_all_end(void) AioContext *aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - bdrv_do_drained_end(bs, true, NULL); + bdrv_do_drained_end(bs, true, NULL, false); aio_context_release(aio_context); } } diff --git a/block/vvfat.c b/block/vvfat.c index 4595f335b8..c7d2ed2d96 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -3134,6 +3134,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options, } static const BdrvChildRole child_vvfat_qcow = { + .parent_is_bds = true, .inherit_options = vvfat_qcow_options, }; diff --git a/include/block/block.h b/include/block/block.h index 067d24cc4a..836746e4e1 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -557,7 +557,8 @@ void bdrv_io_unplug(BlockDriverState *bs); * Begin a quiesced section of all users of @bs. This is part of * bdrv_drained_begin. */ -void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore); +void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents); /** * bdrv_parent_drained_end: @@ -565,18 +566,23 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore); * End a quiesced section of all users of @bs. This is part of * bdrv_drained_end. */ -void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); +void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents); /** * bdrv_drain_poll: * * Poll for pending requests in @bs, its parents (except for @ignore_parent), - * and if @recursive is true its children as well. + * and if @recursive is true its children as well (used for subtree drain). + * + * If @ignore_bds_parents is true, parents that are BlockDriverStates must + * ignore the drain request because they will be drained separately (used for + * drain_all). * * This is part of bdrv_drained_begin. */ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, - BdrvChild *ignore_parent); + BdrvChild *ignore_parent, bool ignore_bds_parents); /** * bdrv_drained_begin: @@ -597,7 +603,7 @@ void bdrv_drained_begin(BlockDriverState *bs); * running requests to complete. */ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, - BdrvChild *parent); + BdrvChild *parent, bool ignore_bds_parents); /** * Like bdrv_drained_begin, but recursively begins a quiesced section for diff --git a/include/block/block_int.h b/include/block/block_int.h index 1b811db8ec..1abfc26d76 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -577,6 +577,12 @@ struct BdrvChildRole { * points to. */ bool stay_at_node; + /* If true, the parent is a BlockDriverState and bdrv_next_all_states() + * will return it. This information is used for drain_all, where every node + * will be drained separately, so the drain only needs to be propagated to + * non-BDS parents. */ + bool parent_is_bds; + void (*inherit_options)(int *child_flags, QDict *child_options, int parent_flags, QDict *parent_options); From 0f12264e7a41458179ad10276a7c33c72024861a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 28 Mar 2018 18:29:18 +0200 Subject: [PATCH 1394/2380] block: Allow graph changes in bdrv_drain_all_begin/end sections bdrv_drain_all_*() used bdrv_next() to iterate over all root nodes and did a subtree drain for each of them. This works fine as long as the graph is static, but sadly, reality looks different. If the graph changes so that root nodes are added or removed, we would have to compensate for this. bdrv_next() returns each root node only once even if it's the root node for multiple BlockBackends or for a monitor-owned block driver tree, which would only complicate things. The much easier and more obviously correct way is to fundamentally change the way the functions work: Iterate over all BlockDriverStates, no matter who owns them, and drain them individually. Compensation is only necessary when a new BDS is created inside a drain_all section. Removal of a BDS doesn't require any action because it's gone afterwards anyway. Signed-off-by: Kevin Wolf --- block.c | 34 ++++++++++++++++++++-- block/io.c | 60 ++++++++++++++++++++++++++++++--------- include/block/block.h | 1 + include/block/block_int.h | 1 + 4 files changed, 79 insertions(+), 17 deletions(-) diff --git a/block.c b/block.c index c8586f41ba..6c128007fd 100644 --- a/block.c +++ b/block.c @@ -333,6 +333,10 @@ BlockDriverState *bdrv_new(void) qemu_co_queue_init(&bs->flush_queue); + for (i = 0; i < bdrv_drain_all_count; i++) { + bdrv_drained_begin(bs); + } + QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list); return bs; @@ -1164,7 +1168,7 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, int open_flags, Error **errp) { Error *local_err = NULL; - int ret; + int i, ret; bdrv_assign_node_name(bs, node_name, &local_err); if (local_err) { @@ -1212,6 +1216,12 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, assert(bdrv_min_mem_align(bs) != 0); assert(is_power_of_2(bs->bl.request_alignment)); + for (i = 0; i < bs->quiesce_counter; i++) { + if (drv->bdrv_co_drain_begin) { + drv->bdrv_co_drain_begin(bs); + } + } + return 0; open_failed: bs->drv = NULL; @@ -2033,7 +2043,12 @@ static void bdrv_replace_child_noperm(BdrvChild *child, child->role->detach(child); } if (old_bs->quiesce_counter && child->role->drained_end) { - for (i = 0; i < old_bs->quiesce_counter; i++) { + int num = old_bs->quiesce_counter; + if (child->role->parent_is_bds) { + num -= bdrv_drain_all_count; + } + assert(num >= 0); + for (i = 0; i < num; i++) { child->role->drained_end(child); } } @@ -2045,7 +2060,12 @@ static void bdrv_replace_child_noperm(BdrvChild *child, if (new_bs) { QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); if (new_bs->quiesce_counter && child->role->drained_begin) { - for (i = 0; i < new_bs->quiesce_counter; i++) { + int num = new_bs->quiesce_counter; + if (child->role->parent_is_bds) { + num -= bdrv_drain_all_count; + } + assert(num >= 0); + for (i = 0; i < num; i++) { child->role->drained_begin(child); } } @@ -4049,6 +4069,14 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs) return QTAILQ_NEXT(bs, node_list); } +BlockDriverState *bdrv_next_all_states(BlockDriverState *bs) +{ + if (!bs) { + return QTAILQ_FIRST(&all_bdrv_states); + } + return QTAILQ_NEXT(bs, bs_list); +} + const char *bdrv_get_node_name(const BlockDriverState *bs) { return bs->node_name; diff --git a/block/io.c b/block/io.c index 1834a14aa6..ef4fedd364 100644 --- a/block/io.c +++ b/block/io.c @@ -38,6 +38,8 @@ /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) +static AioWait drain_all_aio_wait; + static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags); @@ -472,6 +474,29 @@ static void bdrv_drain_assert_idle(BlockDriverState *bs) } } +unsigned int bdrv_drain_all_count = 0; + +static bool bdrv_drain_all_poll(void) +{ + BlockDriverState *bs = NULL; + bool result = false; + + /* Execute pending BHs first (may modify the graph) and check everything + * else only after the BHs have executed. */ + while (aio_poll(qemu_get_aio_context(), false)); + + /* bdrv_drain_poll() can't make changes to the graph and we are holding the + * main AioContext lock, so iterating bdrv_next_all_states() is safe. */ + while ((bs = bdrv_next_all_states(bs))) { + AioContext *aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + result |= bdrv_drain_poll(bs, false, NULL, true); + aio_context_release(aio_context); + } + + return result; +} + /* * Wait for pending requests to complete across all BlockDriverStates * @@ -486,45 +511,51 @@ static void bdrv_drain_assert_idle(BlockDriverState *bs) */ void bdrv_drain_all_begin(void) { - BlockDriverState *bs; - BdrvNextIterator it; + BlockDriverState *bs = NULL; if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(NULL, true, false, NULL, false, true); + bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true); return; } - /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread - * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on - * nodes in several different AioContexts, so make sure we're in the main - * context. */ + /* AIO_WAIT_WHILE() with a NULL context can only be called from the main + * loop AioContext, so make sure we're in the main context. */ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + assert(bdrv_drain_all_count < INT_MAX); + bdrv_drain_all_count++; - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + /* Quiesce all nodes, without polling in-flight requests yet. The graph + * cannot change during this loop. */ + while ((bs = bdrv_next_all_states(bs))) { AioContext *aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - bdrv_do_drained_begin(bs, true, NULL, false, true); + bdrv_do_drained_begin(bs, false, NULL, true, false); aio_context_release(aio_context); } - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + /* Now poll the in-flight requests */ + AIO_WAIT_WHILE(&drain_all_aio_wait, NULL, bdrv_drain_all_poll()); + + while ((bs = bdrv_next_all_states(bs))) { bdrv_drain_assert_idle(bs); } } void bdrv_drain_all_end(void) { - BlockDriverState *bs; - BdrvNextIterator it; + BlockDriverState *bs = NULL; - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + while ((bs = bdrv_next_all_states(bs))) { AioContext *aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - bdrv_do_drained_end(bs, true, NULL, false); + bdrv_do_drained_end(bs, false, NULL, true); aio_context_release(aio_context); } + + assert(bdrv_drain_all_count > 0); + bdrv_drain_all_count--; } void bdrv_drain_all(void) @@ -647,6 +678,7 @@ void bdrv_inc_in_flight(BlockDriverState *bs) void bdrv_wakeup(BlockDriverState *bs) { aio_wait_kick(bdrv_get_aio_wait(bs)); + aio_wait_kick(&drain_all_aio_wait); } void bdrv_dec_in_flight(BlockDriverState *bs) diff --git a/include/block/block.h b/include/block/block.h index 836746e4e1..b1d6fdb97a 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -421,6 +421,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device, Error **errp); bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); BlockDriverState *bdrv_next_node(BlockDriverState *bs); +BlockDriverState *bdrv_next_all_states(BlockDriverState *bs); typedef struct BdrvNextIterator { enum { diff --git a/include/block/block_int.h b/include/block/block_int.h index 1abfc26d76..7cd7eed83b 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -854,6 +854,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); +extern unsigned int bdrv_drain_all_count; void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent); void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent); From 19f7a7e574a099dca13120441fbe723cea9c1dc2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 28 Mar 2018 18:29:06 +0200 Subject: [PATCH 1395/2380] test-bdrv-drain: Test graph changes in drain_all section This tests both adding and remove a node between bdrv_drain_all_begin() and bdrv_drain_all_end(), and enabled the existing detach test for drain_all. Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 75 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 5cc179a778..291a050f86 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -452,7 +452,7 @@ static void test_multiparent(void) blk_unref(blk_b); } -static void test_graph_change(void) +static void test_graph_change_drain_subtree(void) { BlockBackend *blk_a, *blk_b; BlockDriverState *bs_a, *bs_b, *backing; @@ -531,6 +531,63 @@ static void test_graph_change(void) blk_unref(blk_b); } +static void test_graph_change_drain_all(void) +{ + BlockBackend *blk_a, *blk_b; + BlockDriverState *bs_a, *bs_b; + BDRVTestState *a_s, *b_s; + + /* Create node A with a BlockBackend */ + blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, + &error_abort); + a_s = bs_a->opaque; + blk_insert_bs(blk_a, bs_a, &error_abort); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + + /* Call bdrv_drain_all_begin() */ + bdrv_drain_all_begin(); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + + /* Create node B with a BlockBackend */ + blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, + &error_abort); + b_s = bs_b->opaque; + blk_insert_bs(blk_b, bs_b, &error_abort); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + + /* Unref and finally delete node A */ + blk_unref(blk_a); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + + bdrv_unref(bs_a); + + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + + /* End the drained section */ + bdrv_drain_all_end(); + + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + + bdrv_unref(bs_b); + blk_unref(blk_b); +} + struct test_iothread_data { BlockDriverState *bs; enum drain_type drain_type; @@ -971,6 +1028,10 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, bdrv_subtree_drained_begin(bs); bdrv_subtree_drained_end(bs); break; + case BDRV_DRAIN_ALL: + bdrv_drain_all_begin(); + bdrv_drain_all_end(); + break; default: g_assert_not_reached(); } @@ -991,6 +1052,11 @@ static void test_delete_by_drain(void) do_test_delete_by_drain(false, BDRV_DRAIN); } +static void test_detach_by_drain_all(void) +{ + do_test_delete_by_drain(true, BDRV_DRAIN_ALL); +} + static void test_detach_by_drain(void) { do_test_delete_by_drain(true, BDRV_DRAIN); @@ -1219,7 +1285,11 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/nested", test_nested); g_test_add_func("/bdrv-drain/multiparent", test_multiparent); - g_test_add_func("/bdrv-drain/graph-change", test_graph_change); + + g_test_add_func("/bdrv-drain/graph-change/drain_subtree", + test_graph_change_drain_subtree); + g_test_add_func("/bdrv-drain/graph-change/drain_all", + test_graph_change_drain_all); g_test_add_func("/bdrv-drain/iothread/drain_all", test_iothread_drain_all); g_test_add_func("/bdrv-drain/iothread/drain", test_iothread_drain); @@ -1232,6 +1302,7 @@ int main(int argc, char **argv) test_blockjob_drain_subtree); g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_all", test_detach_by_drain_all); g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); From f45280cbf66d8e58224f6a253d0ae2aa72cc6280 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Mon, 28 May 2018 14:03:59 +0200 Subject: [PATCH 1396/2380] block: fix QEMU crash with scsi-hd and drive_del Removing a drive with drive_del while it is being used to run an I/O intensive workload can cause QEMU to crash. An AIO flush can yield at some point: blk_aio_flush_entry() blk_co_flush(blk) bdrv_co_flush(blk->root->bs) ... qemu_coroutine_yield() and let the HMP command to run, free blk->root and give control back to the AIO flush: hmp_drive_del() blk_remove_bs() bdrv_root_unref_child(blk->root) child_bs = blk->root->bs bdrv_detach_child(blk->root) bdrv_replace_child(blk->root, NULL) blk->root->bs = NULL g_free(blk->root) <============== blk->root becomes stale bdrv_unref(child_bs) bdrv_delete(child_bs) bdrv_close() bdrv_drained_begin() bdrv_do_drained_begin() bdrv_drain_recurse() aio_poll() ... qemu_coroutine_switch() and the AIO flush completion ends up dereferencing blk->root: blk_aio_complete() scsi_aio_complete() blk_get_aio_context(blk) bs = blk_bs(blk) ie, bs = blk->root ? blk->root->bs : NULL ^^^^^ stale The problem is that we should avoid making block driver graph changes while we have in-flight requests. Let's drain all I/O for this BB before calling bdrv_root_unref_child(). Signed-off-by: Greg Kurz Signed-off-by: Kevin Wolf --- block/block-backend.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/block/block-backend.c b/block/block-backend.c index 2d1a3463e8..6b75bca317 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -767,6 +767,11 @@ void blk_remove_bs(BlockBackend *blk) blk_update_root_state(blk); + /* bdrv_root_unref_child() will cause blk->root to become stale and may + * switch to a completion coroutine later on. Let's drain all I/O here + * to avoid that and a potential QEMU crash. + */ + blk_drain(blk); bdrv_root_unref_child(blk->root); blk->root = NULL; } From a0e11b617b9ef41cefe8739dff4d6a7b01ca967f Mon Sep 17 00:00:00 2001 From: Collin Walling Date: Tue, 29 May 2018 00:40:09 -0400 Subject: [PATCH 1397/2380] pc-bios/s390-ccw: define loadparm length Loadparm is defined by the s390 architecture to be 8 bytes in length. Let's define this size in the s390-ccw bios. Suggested-by: Laszlo Ersek Signed-off-by: Collin Walling Reviewed-by: Laszlo Ersek Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/iplb.h | 4 +++- pc-bios/s390-ccw/main.c | 8 ++++---- pc-bios/s390-ccw/sclp.c | 2 +- pc-bios/s390-ccw/sclp.h | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index ded20c834e..772d5c57c9 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -12,6 +12,8 @@ #ifndef IPLB_H #define IPLB_H +#define LOADPARM_LEN 8 + struct IplBlockCcw { uint8_t reserved0[85]; uint8_t ssid; @@ -61,7 +63,7 @@ struct IplParameterBlock { uint8_t pbt; uint8_t flags; uint16_t reserved01; - uint8_t loadparm[8]; + uint8_t loadparm[LOADPARM_LEN]; union { IplBlockCcw ccw; IplBlockFcp fcp; diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 26f9adf84a..544851d672 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -15,7 +15,7 @@ char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); static SubChannelId blk_schid = { .one = 1 }; IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); -static char loadparm_str[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static char loadparm_str[LOADPARM_LEN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; QemuIplParameters qipl; #define LOADPARM_PROMPT "PROMPT " @@ -80,13 +80,13 @@ static bool find_dev(Schib *schib, int dev_no) static void menu_setup(void) { - if (memcmp(loadparm_str, LOADPARM_PROMPT, 8) == 0) { + if (memcmp(loadparm_str, LOADPARM_PROMPT, LOADPARM_LEN) == 0) { menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0); return; } /* If loadparm was set to any other value, then do not enable menu */ - if (memcmp(loadparm_str, LOADPARM_EMPTY, 8) != 0) { + if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) { return; } @@ -117,7 +117,7 @@ static void virtio_setup(void) enable_mss_facility(); sclp_get_loadparm_ascii(loadparm_str); - memcpy(ldp + 10, loadparm_str, 8); + memcpy(ldp + 10, loadparm_str, LOADPARM_LEN); sclp_print(ldp); memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c index 3836cb4716..c0223fab0b 100644 --- a/pc-bios/s390-ccw/sclp.c +++ b/pc-bios/s390-ccw/sclp.c @@ -114,7 +114,7 @@ void sclp_get_loadparm_ascii(char *loadparm) memset((char *)_sccb, 0, sizeof(ReadInfo)); sccb->h.length = sizeof(ReadInfo); if (!sclp_service_call(SCLP_CMDW_READ_SCP_INFO, sccb)) { - ebcdic_to_ascii((char *) sccb->loadparm, loadparm, 8); + ebcdic_to_ascii((char *) sccb->loadparm, loadparm, LOADPARM_LEN); } } diff --git a/pc-bios/s390-ccw/sclp.h b/pc-bios/s390-ccw/sclp.h index 0dd987ff5d..8450161ba7 100644 --- a/pc-bios/s390-ccw/sclp.h +++ b/pc-bios/s390-ccw/sclp.h @@ -56,7 +56,7 @@ typedef struct ReadInfo { uint16_t rnmax; uint8_t rnsize; uint8_t reserved[13]; - uint8_t loadparm[8]; + uint8_t loadparm[LOADPARM_LEN]; } __attribute__((packed)) ReadInfo; typedef struct SCCB { From 4248981d5190eaf2b0ab74d8c287b6070ffc300a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 5 Jun 2018 12:41:49 +0200 Subject: [PATCH 1398/2380] roms: Update SLOF submodule to current status We need the latest version of SLOF's libnet for adding pxelinux.cfg support in the s390-ccw bios, too. Signed-off-by: Thomas Huth --- roms/SLOF | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roms/SLOF b/roms/SLOF index fa981320a1..2317427ce7 160000 --- a/roms/SLOF +++ b/roms/SLOF @@ -1 +1 @@ -Subproject commit fa981320a1e0968d6fc1b8de319723ff8212b337 +Subproject commit 2317427ce76006723f7ae103a6998ab41dd79c68 From 134f0b3d7ca5fbbd17f21fea87066967ce1d6de5 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 18 May 2018 11:31:27 +0200 Subject: [PATCH 1399/2380] pc-bios/s390-ccw/net: Update code for the latest changes in SLOF The ip_version information now has to be stored in the filename_ip_t structure, and there is now a common function called tftp_get_error_info() which can be used to get the error string for a TFTP error code. We can also get rid of some superfluous "(char *)" casts now. Acked-by: Christian Borntraeger Tested-by: Viktor Mihajlovski Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/netboot.mak | 2 +- pc-bios/s390-ccw/netmain.c | 86 +++++++----------------------------- 2 files changed, 18 insertions(+), 70 deletions(-) diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak index 4f64128c6c..a73be367e6 100644 --- a/pc-bios/s390-ccw/netboot.mak +++ b/pc-bios/s390-ccw/netboot.mak @@ -34,7 +34,7 @@ STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o %.o : $(SLOF_DIR)/lib/libc/stdlib/%.c $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") -STDIO_OBJS = sprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \ +STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \ printf.o putc.o puts.o putchar.o stdchnls.o fileno.o %.o : $(SLOF_DIR)/lib/libc/stdio/%.c $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 600024155b..d007fb7a86 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -47,7 +47,6 @@ IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); static char cfgbuf[2048]; static SubChannelId net_schid = { .one = 1 }; -static int ip_version = 4; static uint64_t dest_timer; static uint64_t get_timer_ms(void) @@ -100,10 +99,10 @@ static int dhcp(struct filename_ip *fn_ip, int retries) printf("\nGiving up after %d DHCP requests\n", retries); return -1; } - ip_version = 4; + fn_ip->ip_version = 4; rc = dhcpv4(NULL, fn_ip); if (rc == -1) { - ip_version = 6; + fn_ip->ip_version = 6; set_ipv6_address(fn_ip->fd, 0); rc = dhcpv6(NULL, fn_ip); if (rc == 0) { @@ -137,8 +136,7 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len) tftp_err_t tftp_err; int rc; - rc = tftp(fnip, buffer, len, DEFAULT_TFTP_RETRIES, &tftp_err, 1, 1428, - ip_version); + rc = tftp(fnip, buffer, len, DEFAULT_TFTP_RETRIES, &tftp_err); if (rc < 0) { /* Make sure that error messages are put into a new line */ @@ -149,61 +147,11 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len) printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, rc / 1024); } else if (rc > 0) { printf(" TFTP: Received %s (%d Bytes)\n", fnip->filename, rc); - } else if (rc == -1) { - puts("unknown TFTP error"); - } else if (rc == -2) { - printf("TFTP buffer of %d bytes is too small for %s\n", - len, fnip->filename); - } else if (rc == -3) { - printf("file not found: %s\n", fnip->filename); - } else if (rc == -4) { - puts("TFTP access violation"); - } else if (rc == -5) { - puts("illegal TFTP operation"); - } else if (rc == -6) { - puts("unknown TFTP transfer ID"); - } else if (rc == -7) { - puts("no such TFTP user"); - } else if (rc == -8) { - puts("TFTP blocksize negotiation failed"); - } else if (rc == -9) { - puts("file exceeds maximum TFTP transfer size"); - } else if (rc <= -10 && rc >= -15) { - const char *icmp_err_str; - switch (rc) { - case -ICMP_NET_UNREACHABLE - 10: - icmp_err_str = "net unreachable"; - break; - case -ICMP_HOST_UNREACHABLE - 10: - icmp_err_str = "host unreachable"; - break; - case -ICMP_PROTOCOL_UNREACHABLE - 10: - icmp_err_str = "protocol unreachable"; - break; - case -ICMP_PORT_UNREACHABLE - 10: - icmp_err_str = "port unreachable"; - break; - case -ICMP_FRAGMENTATION_NEEDED - 10: - icmp_err_str = "fragmentation needed and DF set"; - break; - case -ICMP_SOURCE_ROUTE_FAILED - 10: - icmp_err_str = "source route failed"; - break; - default: - icmp_err_str = " UNKNOWN"; - break; - } - printf("ICMP ERROR \"%s\"\n", icmp_err_str); - } else if (rc == -40) { - printf("TFTP error occurred after %d bad packets received", - tftp_err.bad_tftp_packets); - } else if (rc == -41) { - printf("TFTP error occurred after missing %d responses", - tftp_err.no_packets); - } else if (rc == -42) { - printf("TFTP error missing block %d, expected block was %d", - tftp_err.blocks_missed, - tftp_err.blocks_received); + } else { + const char *errstr = NULL; + int ecode; + tftp_get_error_info(fnip, &tftp_err, rc, &errstr, &ecode); + printf("TFTP error: %s\n", errstr ? errstr : "unknown error"); } return rc; @@ -231,7 +179,7 @@ static int net_init(filename_ip_t *fn_ip) rc = dhcp(fn_ip, DEFAULT_BOOT_RETRIES); if (rc >= 0) { - if (ip_version == 4) { + if (fn_ip->ip_version == 4) { set_ipv4_address(fn_ip->own_ip); } } else { @@ -239,11 +187,11 @@ static int net_init(filename_ip_t *fn_ip) return -101; } - if (ip_version == 4) { + if (fn_ip->ip_version == 4) { printf(" Using IPv4 address: %d.%d.%d.%d\n", (fn_ip->own_ip >> 24) & 0xFF, (fn_ip->own_ip >> 16) & 0xFF, (fn_ip->own_ip >> 8) & 0xFF, fn_ip->own_ip & 0xFF); - } else if (ip_version == 6) { + } else if (fn_ip->ip_version == 6) { char ip6_str[40]; ipv6_to_str(fn_ip->own_ip6.addr, ip6_str); printf(" Using IPv6 address: %s\n", ip6_str); @@ -261,17 +209,17 @@ static int net_init(filename_ip_t *fn_ip) } printf(" Using TFTP server: "); - if (ip_version == 4) { + if (fn_ip->ip_version == 4) { printf("%d.%d.%d.%d\n", (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); - } else if (ip_version == 6) { + } else if (fn_ip->ip_version == 6) { char ip6_str[40]; ipv6_to_str(fn_ip->server_ip6.addr, ip6_str); printf("%s\n", ip6_str); } - if (strlen((char *)fn_ip->filename) > 0) { + if (strlen(fn_ip->filename) > 0) { printf(" Bootfile name: '%s'\n", fn_ip->filename); } @@ -280,7 +228,7 @@ static int net_init(filename_ip_t *fn_ip) static void net_release(filename_ip_t *fn_ip) { - if (ip_version == 4) { + if (fn_ip->ip_version == 4) { dhcp_send_release(fn_ip->fd); } } @@ -322,7 +270,7 @@ static int handle_ins_cfg(filename_ip_t *fn_ip, char *cfg, int cfgsize) return -1; } *ptr = 0; - strncpy((char *)fn_ip->filename, insbuf, sizeof(fn_ip->filename)); + strncpy(fn_ip->filename, insbuf, sizeof(fn_ip->filename)); destaddr = (char *)atol(ptr + 1); rc = tftp_load(fn_ip, destaddr, (long)_start - (long)destaddr); if (rc <= 0) { @@ -455,7 +403,7 @@ void main(void) panic("Network initialization failed. Halting.\n"); } - fnlen = strlen((char *)fn_ip.filename); + fnlen = strlen(fn_ip.filename); if (fnlen > 0 && fn_ip.filename[fnlen - 1] != '/') { rc = net_try_direct_tftp_load(&fn_ip); } From ec623990b34ab0e271141356af96d67a0c4e980d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 22 May 2018 11:37:29 +0200 Subject: [PATCH 1400/2380] pc-bios/s390-ccw/net: Add support for pxelinux-style config files Since it is quite cumbersome to manually create a combined kernel with initrd image for network booting, we now support loading via pxelinux configuration files, too. In these files, the kernel, initrd and command line parameters can be specified seperately, and the firmware then takes care of glueing everything together in memory after the files have been downloaded. See this URL for details about the config file layout: https://www.syslinux.org/wiki/index.php?title=PXELINUX The user can either specify a config file directly as bootfile via DHCP (but in this case, the file has to start either with "default" or a "#" comment so we can distinguish it from binary kernels), or a folder (i.e. the bootfile name must end with "/") where the firmware should look for the typical pxelinux.cfg file names, e.g. based on MAC or IP address. We also support the pxelinux.cfg DHCP options 209 and 210 from RFC 5071. Acked-by: Christian Borntraeger Tested-by: Viktor Mihajlovski Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/netboot.mak | 7 +-- pc-bios/s390-ccw/netmain.c | 86 +++++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak index a73be367e6..8af0cfd2da 100644 --- a/pc-bios/s390-ccw/netboot.mak +++ b/pc-bios/s390-ccw/netboot.mak @@ -25,8 +25,9 @@ CTYPE_OBJS = isdigit.o isxdigit.o toupper.o %.o : $(SLOF_DIR)/lib/libc/ctype/%.c $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") -STRING_OBJS = strcat.o strchr.o strcmp.o strcpy.o strlen.o strncmp.o strncpy.o \ - strstr.o memset.o memcpy.o memmove.o memcmp.o +STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \ + strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \ + memset.o memcpy.o memmove.o memcmp.o %.o : $(SLOF_DIR)/lib/libc/string/%.c $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") @@ -50,7 +51,7 @@ libc.a: $(LIBCOBJS) # libnet files: LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \ - dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o + dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o LIBNETCFLAGS := $(QEMU_CFLAGS) -DDHCPARCH=0x1F $(LIBC_INC) $(LIBNET_INC) %.o : $(SLOF_DIR)/lib/libnet/%.c diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index d007fb7a86..c059546480 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "s390-ccw.h" #include "virtio.h" @@ -41,12 +42,14 @@ extern char _start[]; #define KERNEL_ADDR ((void *)0L) #define KERNEL_MAX_SIZE ((long)_start) +#define ARCH_COMMAND_LINE_SIZE 896 /* Taken from Linux kernel */ char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE))); IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); static char cfgbuf[2048]; static SubChannelId net_schid = { .one = 1 }; +static uint8_t mac[6]; static uint64_t dest_timer; static uint64_t get_timer_ms(void) @@ -159,7 +162,6 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len) static int net_init(filename_ip_t *fn_ip) { - uint8_t mac[6]; int rc; memset(fn_ip, 0, sizeof(filename_ip_t)); @@ -233,6 +235,66 @@ static void net_release(filename_ip_t *fn_ip) } } +/** + * Load a kernel with initrd (i.e. with the information that we've got from + * a pxelinux.cfg config file) + */ +static int load_kernel_with_initrd(filename_ip_t *fn_ip, + struct pl_cfg_entry *entry) +{ + int rc; + + printf("Loading pxelinux.cfg entry '%s'\n", entry->label); + + if (!entry->kernel) { + printf("Kernel entry is missing!\n"); + return -1; + } + + strncpy(fn_ip->filename, entry->kernel, sizeof(fn_ip->filename)); + rc = tftp_load(fn_ip, KERNEL_ADDR, KERNEL_MAX_SIZE); + if (rc < 0) { + return rc; + } + + if (entry->initrd) { + uint64_t iaddr = (rc + 0xfff) & ~0xfffUL; + + strncpy(fn_ip->filename, entry->initrd, sizeof(fn_ip->filename)); + rc = tftp_load(fn_ip, (void *)iaddr, KERNEL_MAX_SIZE - iaddr); + if (rc < 0) { + return rc; + } + /* Patch location and size: */ + *(uint64_t *)0x10408 = iaddr; + *(uint64_t *)0x10410 = rc; + rc += iaddr; + } + + if (entry->append) { + strncpy((char *)0x10480, entry->append, ARCH_COMMAND_LINE_SIZE); + } + + return rc; +} + +#define MAX_PXELINUX_ENTRIES 16 + +static int net_try_pxelinux_cfg(filename_ip_t *fn_ip) +{ + struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES]; + int num_ent, def_ent = 0; + + num_ent = pxelinux_load_parse_cfg(fn_ip, mac, NULL, DEFAULT_TFTP_RETRIES, + cfgbuf, sizeof(cfgbuf), + entries, MAX_PXELINUX_ENTRIES, &def_ent); + if (num_ent > 0) { + return load_kernel_with_initrd(fn_ip, &entries[def_ent]); + } + + return -1; +} + /** * Load via information from a .INS file (which can be found on CD-ROMs * for example) @@ -302,6 +364,25 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip) if (!strncmp("* ", cfgbuf, 2)) { return handle_ins_cfg(fn_ip, cfgbuf, rc); } + /* + * pxelinux.cfg support via bootfile name is just here for developers' + * convenience (it eases testing with the built-in DHCP server of QEMU + * that does not support RFC 5071). The official way to configure a + * pxelinux.cfg file name is to use DHCP options 209 and 210 instead. + * So only use the pxelinux.cfg parser here for files that start with + * a magic comment string. + */ + if (!strncasecmp("# pxelinux", cfgbuf, 10)) { + struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES]; + int num_ent, def_ent = 0; + + num_ent = pxelinux_parse_cfg(cfgbuf, sizeof(cfgbuf), entries, + MAX_PXELINUX_ENTRIES, &def_ent); + if (num_ent <= 0) { + return -1; + } + return load_kernel_with_initrd(fn_ip, &entries[def_ent]); + } } /* Move kernel to right location */ @@ -407,6 +488,9 @@ void main(void) if (fnlen > 0 && fn_ip.filename[fnlen - 1] != '/') { rc = net_try_direct_tftp_load(&fn_ip); } + if (rc <= 0) { + rc = net_try_pxelinux_cfg(&fn_ip); + } net_release(&fn_ip); From 0d8261b506933c245b79ca6a57422dc81d8989c1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 22 May 2018 11:53:51 +0200 Subject: [PATCH 1401/2380] pc-bios/s390-ccw/net: Try to load pxelinux.cfg file accoring to the UUID With the STSI instruction, we can get the UUID of the current VM instance, so we can support loading pxelinux config files via UUID in the file name, too. Acked-by: Christian Borntraeger Tested-by: Viktor Mihajlovski Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/netmain.c | 56 +++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index c059546480..0392131c27 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -44,6 +44,9 @@ extern char _start[]; #define KERNEL_MAX_SIZE ((long)_start) #define ARCH_COMMAND_LINE_SIZE 896 /* Taken from Linux kernel */ +/* STSI 3.2.2 offset of first vmdb + offset of uuid inside vmdb */ +#define STSI322_VMDB_UUID_OFFSET ((8 + 12) * 4) + char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE))); IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); static char cfgbuf[2048]; @@ -235,6 +238,56 @@ static void net_release(filename_ip_t *fn_ip) } } +/** + * Retrieve the Universally Unique Identifier of the VM. + * @return UUID string, or NULL in case of errors + */ +static const char *get_uuid(void) +{ + register int r0 asm("0"); + register int r1 asm("1"); + uint8_t *mem, *buf, uuid[16]; + int i, cc, chk = 0; + static char uuid_str[37]; + + mem = malloc(2 * PAGE_SIZE); + if (!mem) { + puts("Out of memory ... can not get UUID."); + return NULL; + } + buf = (uint8_t *)(((uint64_t)mem + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)); + memset(buf, 0, PAGE_SIZE); + + /* Get SYSIB 3.2.2 */ + r0 = (3 << 28) | 2; + r1 = 2; + asm volatile(" stsi 0(%[addr])\n" + " ipm %[cc]\n" + " srl %[cc],28\n" + : [cc] "=d" (cc) + : "d" (r0), "d" (r1), [addr] "a" (buf) + : "cc", "memory"); + if (cc) { + return NULL; + } + + for (i = 0; i < 16; i++) { + uuid[i] = buf[STSI322_VMDB_UUID_OFFSET + i]; + chk |= uuid[i]; + } + free(mem); + if (!chk) { + return NULL; + } + + sprintf(uuid_str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x", uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], + uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); + + return uuid_str; +} + /** * Load a kernel with initrd (i.e. with the information that we've got from * a pxelinux.cfg config file) @@ -285,7 +338,8 @@ static int net_try_pxelinux_cfg(filename_ip_t *fn_ip) struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES]; int num_ent, def_ent = 0; - num_ent = pxelinux_load_parse_cfg(fn_ip, mac, NULL, DEFAULT_TFTP_RETRIES, + num_ent = pxelinux_load_parse_cfg(fn_ip, mac, get_uuid(), + DEFAULT_TFTP_RETRIES, cfgbuf, sizeof(cfgbuf), entries, MAX_PXELINUX_ENTRIES, &def_ent); if (num_ent > 0) { From 63c93fac18546ef9468c7b522bad0ae43f9f58ba Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 14 Jun 2018 10:38:22 +0200 Subject: [PATCH 1402/2380] pc-bios/s390-ccw: Optimize the s390-netboot.img for size The -O2 optimization flag is passed via CFLAGS to the firmware Makefile, but in netbook.mak, we've got some rules that only use QEMU_CFLAGS for compiling the libc and libnet from SLOF, so these files get compiled without optimization so far. Use CFLAGS here, too, to create faster and smaller code. We can additionally save some more bytes in the firmware images by compi- ling the code with -fno-asynchronous-unwind-tables. This will omit some ELF sections (used for stack unwinding for example) from the image that we do not need in the firmware. Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/Makefile | 1 + pc-bios/s390-ccw/netboot.mak | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 439e3cc9c9..1eb316b02f 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -15,6 +15,7 @@ OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \ QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS)) QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing +QEMU_CFLAGS += -fno-asynchronous-unwind-tables QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS), -fno-stack-protector) LDFLAGS += -Wl,-pie -nostdlib diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak index 8af0cfd2da..14e96b2aa6 100644 --- a/pc-bios/s390-ccw/netboot.mak +++ b/pc-bios/s390-ccw/netboot.mak @@ -19,7 +19,7 @@ s390-netboot.img: s390-netboot.elf # libc files: -LIBC_CFLAGS := $(QEMU_CFLAGS) $(LIBC_INC) $(LIBNET_INC) +LIBC_CFLAGS := $(QEMU_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) CTYPE_OBJS = isdigit.o isxdigit.o toupper.o %.o : $(SLOF_DIR)/lib/libc/ctype/%.c @@ -52,7 +52,7 @@ libc.a: $(LIBCOBJS) LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \ dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o -LIBNETCFLAGS := $(QEMU_CFLAGS) -DDHCPARCH=0x1F $(LIBC_INC) $(LIBNET_INC) +LIBNETCFLAGS := $(QEMU_CFLAGS) $(CFLAGS) -DDHCPARCH=0x1F $(LIBC_INC) $(LIBNET_INC) %.o : $(SLOF_DIR)/lib/libnet/%.c $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") From 4046826d46618ef7eb11df41fe8878669ce6991c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 18 Jun 2018 15:24:44 +0200 Subject: [PATCH 1403/2380] pc-bios/s390-ccw: Update the s390-netboot.img binary This binary now contains the support for pxelinux.cfg-style network booting. Signed-off-by: Thomas Huth --- pc-bios/s390-netboot.img | Bin 87872 -> 54944 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img index ef561efd2e222e03138705e9cf30d9e125a7970e..2c6886efb8320ff711589119ffbf077e68ec8784 100644 GIT binary patch literal 54944 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 literal 87872 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
{1(In6k7z!>oQEm`Oap9K+mm-v_)8%p{)kL)j!p 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 From dc7cbcd8fae9b94b453f3a04298d52a69c228b4d Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 8 Jun 2018 11:55:05 +0800 Subject: [PATCH 1404/2380] monitor: rename out_lock to mon_lock The out_lock is protecting a few Monitor fields. In the future the monitor code will start to run in multiple threads. We are going to turn it into a bigger lock to protect not only the out buffer but also most of the rest. Since at it, rearrange the Monitor struct a bit. Reviewed-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180608035511.7439-2-peterx@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 53 +++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/monitor.c b/monitor.c index 6d0cec552e..5bc9b2dcd0 100644 --- a/monitor.c +++ b/monitor.c @@ -206,15 +206,6 @@ struct Monitor { int suspend_cnt; /* Needs to be accessed atomically */ bool skip_flush; bool use_io_thr; - - /* We can't access guest memory when holding the lock */ - QemuMutex out_lock; - QString *outbuf; - guint out_watch; - - /* Read under either BQL or out_lock, written with BQL+out_lock. */ - int mux_out; - ReadLineState *rs; MonitorQMP qmp; gchar *mon_cpu_path; @@ -223,6 +214,20 @@ struct Monitor { mon_cmd_t *cmd_table; QLIST_HEAD(,mon_fd_t) fds; QTAILQ_ENTRY(Monitor) entry; + + /* + * The per-monitor lock. We can't access guest memory when holding + * the lock. + */ + QemuMutex mon_lock; + + /* + * Fields that are protected by the per-monitor lock. + */ + QString *outbuf; + guint out_watch; + /* Read under either BQL or mon_lock, written with BQL+mon_lock. */ + int mux_out; }; /* Let's add monitor global variables to this struct. */ @@ -365,14 +370,14 @@ static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond, { Monitor *mon = opaque; - qemu_mutex_lock(&mon->out_lock); + qemu_mutex_lock(&mon->mon_lock); mon->out_watch = 0; monitor_flush_locked(mon); - qemu_mutex_unlock(&mon->out_lock); + qemu_mutex_unlock(&mon->mon_lock); return FALSE; } -/* Called with mon->out_lock held. */ +/* Called with mon->mon_lock held. */ static void monitor_flush_locked(Monitor *mon) { int rc; @@ -410,9 +415,9 @@ static void monitor_flush_locked(Monitor *mon) void monitor_flush(Monitor *mon) { - qemu_mutex_lock(&mon->out_lock); + qemu_mutex_lock(&mon->mon_lock); monitor_flush_locked(mon); - qemu_mutex_unlock(&mon->out_lock); + qemu_mutex_unlock(&mon->mon_lock); } /* flush at every end of line */ @@ -420,7 +425,7 @@ static void monitor_puts(Monitor *mon, const char *str) { char c; - qemu_mutex_lock(&mon->out_lock); + qemu_mutex_lock(&mon->mon_lock); for(;;) { c = *str++; if (c == '\0') @@ -433,7 +438,7 @@ static void monitor_puts(Monitor *mon, const char *str) monitor_flush_locked(mon); } } - qemu_mutex_unlock(&mon->out_lock); + qemu_mutex_unlock(&mon->mon_lock); } void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) @@ -724,7 +729,7 @@ static void monitor_data_init(Monitor *mon, bool skip_flush, bool use_io_thr) { memset(mon, 0, sizeof(Monitor)); - qemu_mutex_init(&mon->out_lock); + qemu_mutex_init(&mon->mon_lock); qemu_mutex_init(&mon->qmp.qmp_queue_lock); mon->outbuf = qstring_new(); /* Use *mon_cmds by default. */ @@ -744,7 +749,7 @@ static void monitor_data_destroy(Monitor *mon) } readline_free(mon->rs); qobject_unref(mon->outbuf); - qemu_mutex_destroy(&mon->out_lock); + qemu_mutex_destroy(&mon->mon_lock); qemu_mutex_destroy(&mon->qmp.qmp_queue_lock); monitor_qmp_cleanup_req_queue_locked(mon); monitor_qmp_cleanup_resp_queue_locked(mon); @@ -776,13 +781,13 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, handle_hmp_command(&hmp, command_line); cur_mon = old_mon; - qemu_mutex_lock(&hmp.out_lock); + qemu_mutex_lock(&hmp.mon_lock); if (qstring_get_length(hmp.outbuf) > 0) { output = g_strdup(qstring_get_str(hmp.outbuf)); } else { output = g_strdup(""); } - qemu_mutex_unlock(&hmp.out_lock); + qemu_mutex_unlock(&hmp.mon_lock); out: monitor_data_destroy(&hmp); @@ -4381,9 +4386,9 @@ static void monitor_event(void *opaque, int event) switch (event) { case CHR_EVENT_MUX_IN: - qemu_mutex_lock(&mon->out_lock); + qemu_mutex_lock(&mon->mon_lock); mon->mux_out = 0; - qemu_mutex_unlock(&mon->out_lock); + qemu_mutex_unlock(&mon->mon_lock); if (mon->reset_seen) { readline_restart(mon->rs); monitor_resume(mon); @@ -4403,9 +4408,9 @@ static void monitor_event(void *opaque, int event) } else { atomic_inc(&mon->suspend_cnt); } - qemu_mutex_lock(&mon->out_lock); + qemu_mutex_lock(&mon->mon_lock); mon->mux_out = 1; - qemu_mutex_unlock(&mon->out_lock); + qemu_mutex_unlock(&mon->mon_lock); break; case CHR_EVENT_OPENED: From 9409fc05fe2cec3c54bb04b133ea9e3df35073cd Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 8 Jun 2018 11:55:06 +0800 Subject: [PATCH 1405/2380] monitor: protect mon->fds with mon_lock mon->fds were protected by BQL. Now protect it by mon_lock so that it can even be used in monitor iothread. Reviewed-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180608035511.7439-3-peterx@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/monitor.c b/monitor.c index 5bc9b2dcd0..0fba3ccc20 100644 --- a/monitor.c +++ b/monitor.c @@ -212,7 +212,6 @@ struct Monitor { BlockCompletionFunc *password_completion_cb; void *password_opaque; mon_cmd_t *cmd_table; - QLIST_HEAD(,mon_fd_t) fds; QTAILQ_ENTRY(Monitor) entry; /* @@ -224,6 +223,7 @@ struct Monitor { /* * Fields that are protected by the per-monitor lock. */ + QLIST_HEAD(, mon_fd_t) fds; QString *outbuf; guint out_watch; /* Read under either BQL or mon_lock, written with BQL+mon_lock. */ @@ -2187,7 +2187,7 @@ static void hmp_acl_remove(Monitor *mon, const QDict *qdict) void qmp_getfd(const char *fdname, Error **errp) { mon_fd_t *monfd; - int fd; + int fd, tmp_fd; fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); if (fd == -1) { @@ -2202,13 +2202,17 @@ void qmp_getfd(const char *fdname, Error **errp) return; } + qemu_mutex_lock(&cur_mon->mon_lock); QLIST_FOREACH(monfd, &cur_mon->fds, next) { if (strcmp(monfd->name, fdname) != 0) { continue; } - close(monfd->fd); + tmp_fd = monfd->fd; monfd->fd = fd; + qemu_mutex_unlock(&cur_mon->mon_lock); + /* Make sure close() is out of critical section */ + close(tmp_fd); return; } @@ -2217,24 +2221,31 @@ void qmp_getfd(const char *fdname, Error **errp) monfd->fd = fd; QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next); + qemu_mutex_unlock(&cur_mon->mon_lock); } void qmp_closefd(const char *fdname, Error **errp) { mon_fd_t *monfd; + int tmp_fd; + qemu_mutex_lock(&cur_mon->mon_lock); QLIST_FOREACH(monfd, &cur_mon->fds, next) { if (strcmp(monfd->name, fdname) != 0) { continue; } QLIST_REMOVE(monfd, next); - close(monfd->fd); + tmp_fd = monfd->fd; g_free(monfd->name); g_free(monfd); + qemu_mutex_unlock(&cur_mon->mon_lock); + /* Make sure close() is out of critical section */ + close(tmp_fd); return; } + qemu_mutex_unlock(&cur_mon->mon_lock); error_setg(errp, QERR_FD_NOT_FOUND, fdname); } @@ -2242,6 +2253,7 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) { mon_fd_t *monfd; + qemu_mutex_lock(&mon->mon_lock); QLIST_FOREACH(monfd, &mon->fds, next) { int fd; @@ -2255,10 +2267,12 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) QLIST_REMOVE(monfd, next); g_free(monfd->name); g_free(monfd); + qemu_mutex_unlock(&mon->mon_lock); return fd; } + qemu_mutex_unlock(&mon->mon_lock); error_setg(errp, "File descriptor named '%s' has not been found", fdname); return -1; } From d9f252809ebb8761549f2ac5e67ece1928f42bb6 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 8 Jun 2018 11:55:07 +0800 Subject: [PATCH 1406/2380] monitor: more comments on lock-free elements Add some explicit comments for both Readline and cpu_set/cpu_get helpers that they do not need the mon_lock protection. Reviewed-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180608035511.7439-4-peterx@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/monitor.c b/monitor.c index 0fba3ccc20..5c60bf08cc 100644 --- a/monitor.c +++ b/monitor.c @@ -206,7 +206,15 @@ struct Monitor { int suspend_cnt; /* Needs to be accessed atomically */ bool skip_flush; bool use_io_thr; + + /* + * State used only in the thread "owning" the monitor. + * If @use_io_thr, this is mon_global.mon_iothread. + * Else, it's the main thread. + * These members can be safely accessed without locks. + */ ReadLineState *rs; + MonitorQMP qmp; gchar *mon_cpu_path; BlockCompletionFunc *password_completion_cb; @@ -1311,7 +1319,7 @@ void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, cur_mon->qmp.commands = &qmp_commands; } -/* set the current CPU defined by the user */ +/* Set the current CPU defined by the user. Callers must hold BQL. */ int monitor_set_cpu(int cpu_index) { CPUState *cpu; @@ -1325,6 +1333,7 @@ int monitor_set_cpu(int cpu_index) return 0; } +/* Callers must hold BQL. */ static CPUState *mon_get_cpu_sync(bool synchronize) { CPUState *cpu; From 095cb1bffc8e785471cf1acf98ae495cbf4599a6 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 8 Jun 2018 11:55:08 +0800 Subject: [PATCH 1407/2380] monitor: fix comment for monitor_lock Fix typo in d622cb5879c. Meanwhile move these variables close to each other. monitor_qapi_event_state can be declared static, add that. Reported-by: Markus Armbruster Reviewed-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180608035511.7439-5-peterx@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/monitor.c b/monitor.c index 5c60bf08cc..fa9c35631f 100644 --- a/monitor.c +++ b/monitor.c @@ -266,10 +266,11 @@ typedef struct QMPRequest QMPRequest; /* QMP checker flags */ #define QMP_ACCEPT_UNKNOWNS 1 -/* Protects mon_list, monitor_event_state. */ +/* Protects mon_list, monitor_qapi_event_state. */ static QemuMutex monitor_lock; - +static GHashTable *monitor_qapi_event_state; static QTAILQ_HEAD(mon_list, Monitor) mon_list; + static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets; static int mon_refcount; @@ -571,8 +572,6 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS }, }; -GHashTable *monitor_qapi_event_state; - /* * Emits the event to every monitor instance, @event is only used for trace * Called with monitor_lock held. From 6e8c5f4db7cb74f6f00942a285b4ed6a130c244b Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 8 Jun 2018 11:55:09 +0800 Subject: [PATCH 1408/2380] monitor: remove event_clock_type Instead, use a dynamic function to detect which clock we'll use. The problem is that the old code will let monitor initialization depend on configure_accelerator() (that's where qtest_enabled() start to take effect). After this change, we don't have such a dependency any more. We just need to make sure configure_accelerator() is called when we start to use it. Now it's only used in monitor_qapi_event_queue() and monitor_qapi_event_handler(), so we're good. Suggested-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180608035511.7439-6-peterx@redhat.com> Reviewed-by: Markus Armbruster [monitor_get_event_clock() name and comment tweaked] Reviewed-by: Stefan Hajnoczi Signed-off-by: Markus Armbruster --- monitor.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/monitor.c b/monitor.c index fa9c35631f..e805e40799 100644 --- a/monitor.c +++ b/monitor.c @@ -281,8 +281,6 @@ QmpCommandList qmp_commands, qmp_cap_negotiation_commands; Monitor *cur_mon; -static QEMUClockType event_clock_type = QEMU_CLOCK_REALTIME; - static void monitor_command_cb(void *opaque, const char *cmdline, void *readline_opaque); @@ -309,6 +307,19 @@ static inline bool monitor_is_hmp_non_interactive(const Monitor *mon) return !monitor_is_qmp(mon) && !monitor_uses_readline(mon); } +/* + * Return the clock to use for recording an event's time. + * Beware: result is invalid before configure_accelerator(). + */ +static inline QEMUClockType monitor_get_event_clock(void) +{ + /* + * This allows us to perform tests on the monitor queues to verify + * that the rate limits are enforced. + */ + return qtest_enabled() ? QEMU_CLOCK_VIRTUAL : QEMU_CLOCK_REALTIME; +} + /** * Is the current monitor, if any, a QMP monitor? */ @@ -632,7 +643,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) * monitor_qapi_event_handler() in evconf->rate ns. Any * events arriving before then will be delayed until then. */ - int64_t now = qemu_clock_get_ns(event_clock_type); + int64_t now = qemu_clock_get_ns(monitor_get_event_clock()); monitor_qapi_event_emit(event, qdict); @@ -640,7 +651,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) evstate->event = event; evstate->data = qobject_ref(data); evstate->qdict = NULL; - evstate->timer = timer_new_ns(event_clock_type, + evstate->timer = timer_new_ns(monitor_get_event_clock(), monitor_qapi_event_handler, evstate); g_hash_table_add(monitor_qapi_event_state, evstate); @@ -665,7 +676,7 @@ static void monitor_qapi_event_handler(void *opaque) qemu_mutex_lock(&monitor_lock); if (evstate->qdict) { - int64_t now = qemu_clock_get_ns(event_clock_type); + int64_t now = qemu_clock_get_ns(monitor_get_event_clock()); monitor_qapi_event_emit(evstate->event, evstate->qdict); qobject_unref(evstate->qdict); @@ -721,10 +732,6 @@ static gboolean qapi_event_throttle_equal(const void *a, const void *b) static void monitor_qapi_event_init(void) { - if (qtest_enabled()) { - event_clock_type = QEMU_CLOCK_VIRTUAL; - } - monitor_qapi_event_state = g_hash_table_new(qapi_event_throttle_hash, qapi_event_throttle_equal); qmp_event_set_func_emit(monitor_qapi_event_queue); From d32749deb61513c5456901f20e19887e1bc3d7f3 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 8 Jun 2018 11:55:10 +0800 Subject: [PATCH 1409/2380] monitor: move init global earlier Before this patch, monitor fd helpers might be called even earlier than monitor_init_globals(). This can be problematic. After previous work, now monitor_init_globals() does not depend on accelerator initialization any more. Call it earlier (before CLI parsing; that's where the monitor APIs might be called) to make sure it is called before any of the monitor APIs. Suggested-by: Markus Armbruster Reviewed-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180608035511.7439-7-peterx@redhat.com> Signed-off-by: Markus Armbruster --- vl.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/vl.c b/vl.c index 6e34fb348d..b3426e03d0 100644 --- a/vl.c +++ b/vl.c @@ -2978,6 +2978,7 @@ int main(int argc, char **argv, char **envp) runstate_init(); postcopy_infrastructure_init(); + monitor_init_globals(); if (qcrypto_init(&err) < 0) { error_reportf_err(err, "cannot initialize crypto: "); @@ -4412,12 +4413,6 @@ int main(int argc, char **argv, char **envp) default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS); default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS); - /* - * Note: qtest_enabled() (which is used in monitor_qapi_event_init()) - * depends on configure_accelerator() above. - */ - monitor_init_globals(); - if (qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, NULL)) { exit(1); From 474514668b47c9d2148b526e379ffbb6764d9a9e Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 8 Jun 2018 11:55:11 +0800 Subject: [PATCH 1410/2380] monitor: add lock to protect mon_fdsets Introduce a new global big lock for mon_fdsets. Take it where needed. The monitor_fdset_get_fd() handling is a bit tricky: now we need to call qemu_mutex_unlock() which might pollute errno, so we need to make sure the correct errno be passed up to the callers. To make things simpler, we let monitor_fdset_get_fd() return the -errno directly when error happens, then in qemu_open() we move it back into errno. Reviewed-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180608035511.7439-8-peterx@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 52 +++++++++++++++++++++++++++++++++++++++++---------- stubs/fdset.c | 2 +- util/osdep.c | 3 ++- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/monitor.c b/monitor.c index e805e40799..885e000f9b 100644 --- a/monitor.c +++ b/monitor.c @@ -271,7 +271,10 @@ static QemuMutex monitor_lock; static GHashTable *monitor_qapi_event_state; static QTAILQ_HEAD(mon_list, Monitor) mon_list; +/* Protects mon_fdsets */ +static QemuMutex mon_fdsets_lock; static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets; + static int mon_refcount; static mon_cmd_t mon_cmds[]; @@ -2319,9 +2322,11 @@ static void monitor_fdsets_cleanup(void) MonFdset *mon_fdset; MonFdset *mon_fdset_next; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) { monitor_fdset_cleanup(mon_fdset); } + qemu_mutex_unlock(&mon_fdsets_lock); } AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque, @@ -2356,6 +2361,7 @@ void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) MonFdsetFd *mon_fdset_fd; char fd_str[60]; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { if (mon_fdset->id != fdset_id) { continue; @@ -2375,10 +2381,12 @@ void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) goto error; } monitor_fdset_cleanup(mon_fdset); + qemu_mutex_unlock(&mon_fdsets_lock); return; } error: + qemu_mutex_unlock(&mon_fdsets_lock); if (has_fd) { snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64, fdset_id, fd); @@ -2394,6 +2402,7 @@ FdsetInfoList *qmp_query_fdsets(Error **errp) MonFdsetFd *mon_fdset_fd; FdsetInfoList *fdset_list = NULL; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { FdsetInfoList *fdset_info = g_malloc0(sizeof(*fdset_info)); FdsetFdInfoList *fdsetfd_list = NULL; @@ -2423,6 +2432,7 @@ FdsetInfoList *qmp_query_fdsets(Error **errp) fdset_info->next = fdset_list; fdset_list = fdset_info; } + qemu_mutex_unlock(&mon_fdsets_lock); return fdset_list; } @@ -2435,6 +2445,7 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, MonFdsetFd *mon_fdset_fd; AddfdInfo *fdinfo; + qemu_mutex_lock(&mon_fdsets_lock); if (has_fdset_id) { QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { /* Break if match found or match impossible due to ordering by ID */ @@ -2455,6 +2466,7 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, if (fdset_id < 0) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id", "a non-negative value"); + qemu_mutex_unlock(&mon_fdsets_lock); return NULL; } /* Use specified fdset ID */ @@ -2505,16 +2517,21 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, fdinfo->fdset_id = mon_fdset->id; fdinfo->fd = mon_fdset_fd->fd; + qemu_mutex_unlock(&mon_fdsets_lock); return fdinfo; } int monitor_fdset_get_fd(int64_t fdset_id, int flags) { -#ifndef _WIN32 +#ifdef _WIN32 + return -ENOENT; +#else MonFdset *mon_fdset; MonFdsetFd *mon_fdset_fd; int mon_fd_flags; + int ret; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { if (mon_fdset->id != fdset_id) { continue; @@ -2522,20 +2539,24 @@ int monitor_fdset_get_fd(int64_t fdset_id, int flags) QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL); if (mon_fd_flags == -1) { - return -1; + ret = -errno; + goto out; } if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) { - return mon_fdset_fd->fd; + ret = mon_fdset_fd->fd; + goto out; } } - errno = EACCES; - return -1; + ret = -EACCES; + goto out; } -#endif + ret = -ENOENT; - errno = ENOENT; - return -1; +out: + qemu_mutex_unlock(&mon_fdsets_lock); + return ret; +#endif } int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd) @@ -2543,20 +2564,25 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd) MonFdset *mon_fdset; MonFdsetFd *mon_fdset_fd_dup; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { if (mon_fdset->id != fdset_id) { continue; } QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) { if (mon_fdset_fd_dup->fd == dup_fd) { - return -1; + goto err; } } mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup)); mon_fdset_fd_dup->fd = dup_fd; QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next); + qemu_mutex_unlock(&mon_fdsets_lock); return 0; } + +err: + qemu_mutex_unlock(&mon_fdsets_lock); return -1; } @@ -2565,6 +2591,7 @@ static int monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) MonFdset *mon_fdset; MonFdsetFd *mon_fdset_fd_dup; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) { if (mon_fdset_fd_dup->fd == dup_fd) { @@ -2573,13 +2600,17 @@ static int monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) if (QLIST_EMPTY(&mon_fdset->dup_fds)) { monitor_fdset_cleanup(mon_fdset); } - return -1; + goto err; } else { + qemu_mutex_unlock(&mon_fdsets_lock); return mon_fdset->id; } } } } + +err: + qemu_mutex_unlock(&mon_fdsets_lock); return -1; } @@ -4519,6 +4550,7 @@ void monitor_init_globals(void) monitor_qapi_event_init(); sortcmdlist(); qemu_mutex_init(&monitor_lock); + qemu_mutex_init(&mon_fdsets_lock); monitor_iothread_init(); } diff --git a/stubs/fdset.c b/stubs/fdset.c index 6020cf28c8..4f3edf2ea4 100644 --- a/stubs/fdset.c +++ b/stubs/fdset.c @@ -14,7 +14,7 @@ int monitor_fdset_dup_fd_find(int dup_fd) int monitor_fdset_get_fd(int64_t fdset_id, int flags) { - return -1; + return -ENOENT; } void monitor_fdset_dup_fd_remove(int dupfd) diff --git a/util/osdep.c b/util/osdep.c index a73de0e1ba..ea51d500b6 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -302,7 +302,8 @@ int qemu_open(const char *name, int flags, ...) } fd = monitor_fdset_get_fd(fdset_id, flags); - if (fd == -1) { + if (fd < 0) { + errno = -fd; return -1; } From 4295c5fc613a6dae55c804b689fdbbeb0c4af816 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:10 +0200 Subject: [PATCH 1411/2380] block/mirror: Pull out mirror_perform() When converting mirror's I/O to coroutines, we are going to need a point where these coroutines are created. mirror_perform() is going to be that point. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Jeff Cody Reviewed-by: Alberto Garcia Message-id: 20180613181823.13618-2-mreitz@redhat.com Signed-off-by: Max Reitz --- block/mirror.c | 51 ++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index c2146c1ab3..043bb7beaf 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -80,6 +80,12 @@ typedef struct MirrorOp { uint64_t bytes; } MirrorOp; +typedef enum MirrorMethod { + MIRROR_METHOD_COPY, + MIRROR_METHOD_ZERO, + MIRROR_METHOD_DISCARD, +} MirrorMethod; + static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, int error) { @@ -319,6 +325,22 @@ static void mirror_do_zero_or_discard(MirrorBlockJob *s, } } +static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, + unsigned bytes, MirrorMethod mirror_method) +{ + switch (mirror_method) { + case MIRROR_METHOD_COPY: + return mirror_do_read(s, offset, bytes); + case MIRROR_METHOD_ZERO: + case MIRROR_METHOD_DISCARD: + mirror_do_zero_or_discard(s, offset, bytes, + mirror_method == MIRROR_METHOD_DISCARD); + return bytes; + default: + abort(); + } +} + static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) { BlockDriverState *source = s->source; @@ -385,11 +407,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) int ret; int64_t io_bytes; int64_t io_bytes_acct; - enum MirrorMethod { - MIRROR_METHOD_COPY, - MIRROR_METHOD_ZERO, - MIRROR_METHOD_DISCARD - } mirror_method = MIRROR_METHOD_COPY; + MirrorMethod mirror_method = MIRROR_METHOD_COPY; assert(!(offset % s->granularity)); ret = bdrv_block_status_above(source, NULL, offset, @@ -427,22 +445,11 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) } io_bytes = mirror_clip_bytes(s, offset, io_bytes); - switch (mirror_method) { - case MIRROR_METHOD_COPY: - io_bytes = io_bytes_acct = mirror_do_read(s, offset, io_bytes); - break; - case MIRROR_METHOD_ZERO: - case MIRROR_METHOD_DISCARD: - mirror_do_zero_or_discard(s, offset, io_bytes, - mirror_method == MIRROR_METHOD_DISCARD); - if (write_zeroes_ok) { - io_bytes_acct = 0; - } else { - io_bytes_acct = io_bytes; - } - break; - default: - abort(); + io_bytes = mirror_perform(s, offset, io_bytes, mirror_method); + if (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok) { + io_bytes_acct = 0; + } else { + io_bytes_acct = io_bytes; } assert(io_bytes); offset += io_bytes; @@ -635,7 +642,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) continue; } - mirror_do_zero_or_discard(s, offset, bytes, false); + mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO); offset += bytes; } From 2e1990b26e5aa1ba1a730aa6281df123bb7a71b6 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:11 +0200 Subject: [PATCH 1412/2380] block/mirror: Convert to coroutines In order to talk to the source BDS (and maybe in the future to the target BDS as well) directly, we need to convert our existing AIO requests into coroutine I/O requests. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Message-id: 20180613181823.13618-3-mreitz@redhat.com Signed-off-by: Max Reitz --- block/mirror.c | 152 +++++++++++++++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 62 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 043bb7beaf..dcc19ae288 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -78,6 +78,10 @@ typedef struct MirrorOp { QEMUIOVector qiov; int64_t offset; uint64_t bytes; + + /* The pointee is set by mirror_co_read(), mirror_co_zero(), and + * mirror_co_discard() before yielding for the first time */ + int64_t *bytes_handled; } MirrorOp; typedef enum MirrorMethod { @@ -99,7 +103,7 @@ static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, } } -static void mirror_iteration_done(MirrorOp *op, int ret) +static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) { MirrorBlockJob *s = op->s; struct iovec *iov; @@ -136,9 +140,8 @@ static void mirror_iteration_done(MirrorOp *op, int ret) } } -static void mirror_write_complete(void *opaque, int ret) +static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret) { - MirrorOp *op = opaque; MirrorBlockJob *s = op->s; aio_context_acquire(blk_get_aio_context(s->common.blk)); @@ -155,9 +158,8 @@ static void mirror_write_complete(void *opaque, int ret) aio_context_release(blk_get_aio_context(s->common.blk)); } -static void mirror_read_complete(void *opaque, int ret) +static void coroutine_fn mirror_read_complete(MirrorOp *op, int ret) { - MirrorOp *op = opaque; MirrorBlockJob *s = op->s; aio_context_acquire(blk_get_aio_context(s->common.blk)); @@ -172,8 +174,9 @@ static void mirror_read_complete(void *opaque, int ret) mirror_iteration_done(op, ret); } else { - blk_aio_pwritev(s->target, op->offset, &op->qiov, - 0, mirror_write_complete, op); + ret = blk_co_pwritev(s->target, op->offset, + op->qiov.size, &op->qiov, 0); + mirror_write_complete(op, ret); } aio_context_release(blk_get_aio_context(s->common.blk)); } @@ -230,60 +233,57 @@ static inline void mirror_wait_for_io(MirrorBlockJob *s) s->waiting_for_io = false; } -/* Submit async read while handling COW. - * Returns: The number of bytes copied after and including offset, - * excluding any bytes copied prior to offset due to alignment. - * This will be @bytes if no alignment is necessary, or - * (new_end - offset) if tail is rounded up or down due to - * alignment or buffer limit. +/* Perform a mirror copy operation. + * + * *op->bytes_handled is set to the number of bytes copied after and + * including offset, excluding any bytes copied prior to offset due + * to alignment. This will be op->bytes if no alignment is necessary, + * or (new_end - op->offset) if the tail is rounded up or down due to + * alignment or buffer limit. */ -static uint64_t mirror_do_read(MirrorBlockJob *s, int64_t offset, - uint64_t bytes) +static void coroutine_fn mirror_co_read(void *opaque) { + MirrorOp *op = opaque; + MirrorBlockJob *s = op->s; BlockBackend *source = s->common.blk; int nb_chunks; uint64_t ret; - MirrorOp *op; uint64_t max_bytes; max_bytes = s->granularity * s->max_iov; /* We can only handle as much as buf_size at a time. */ - bytes = MIN(s->buf_size, MIN(max_bytes, bytes)); - assert(bytes); - assert(bytes < BDRV_REQUEST_MAX_BYTES); - ret = bytes; + op->bytes = MIN(s->buf_size, MIN(max_bytes, op->bytes)); + assert(op->bytes); + assert(op->bytes < BDRV_REQUEST_MAX_BYTES); + *op->bytes_handled = op->bytes; if (s->cow_bitmap) { - ret += mirror_cow_align(s, &offset, &bytes); + *op->bytes_handled += mirror_cow_align(s, &op->offset, &op->bytes); } - assert(bytes <= s->buf_size); + /* Cannot exceed BDRV_REQUEST_MAX_BYTES + INT_MAX */ + assert(*op->bytes_handled <= UINT_MAX); + assert(op->bytes <= s->buf_size); /* The offset is granularity-aligned because: * 1) Caller passes in aligned values; * 2) mirror_cow_align is used only when target cluster is larger. */ - assert(QEMU_IS_ALIGNED(offset, s->granularity)); + assert(QEMU_IS_ALIGNED(op->offset, s->granularity)); /* The range is sector-aligned, since bdrv_getlength() rounds up. */ - assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE)); - nb_chunks = DIV_ROUND_UP(bytes, s->granularity); + assert(QEMU_IS_ALIGNED(op->bytes, BDRV_SECTOR_SIZE)); + nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity); while (s->buf_free_count < nb_chunks) { - trace_mirror_yield_in_flight(s, offset, s->in_flight); + trace_mirror_yield_in_flight(s, op->offset, s->in_flight); mirror_wait_for_io(s); } - /* Allocate a MirrorOp that is used as an AIO callback. */ - op = g_new(MirrorOp, 1); - op->s = s; - op->offset = offset; - op->bytes = bytes; - /* Now make a QEMUIOVector taking enough granularity-sized chunks * from s->buf_free. */ qemu_iovec_init(&op->qiov, nb_chunks); while (nb_chunks-- > 0) { MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free); - size_t remaining = bytes - op->qiov.size; + size_t remaining = op->bytes - op->qiov.size; QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next); s->buf_free_count--; @@ -292,53 +292,81 @@ static uint64_t mirror_do_read(MirrorBlockJob *s, int64_t offset, /* Copy the dirty cluster. */ s->in_flight++; - s->bytes_in_flight += bytes; - trace_mirror_one_iteration(s, offset, bytes); + s->bytes_in_flight += op->bytes; + trace_mirror_one_iteration(s, op->offset, op->bytes); - blk_aio_preadv(source, offset, &op->qiov, 0, mirror_read_complete, op); - return ret; + ret = blk_co_preadv(source, op->offset, op->bytes, &op->qiov, 0); + mirror_read_complete(op, ret); } -static void mirror_do_zero_or_discard(MirrorBlockJob *s, - int64_t offset, - uint64_t bytes, - bool is_discard) +static void coroutine_fn mirror_co_zero(void *opaque) { - MirrorOp *op; + MirrorOp *op = opaque; + int ret; - /* Allocate a MirrorOp that is used as an AIO callback. The qiov is zeroed - * so the freeing in mirror_iteration_done is nop. */ - op = g_new0(MirrorOp, 1); - op->s = s; - op->offset = offset; - op->bytes = bytes; + op->s->in_flight++; + op->s->bytes_in_flight += op->bytes; + *op->bytes_handled = op->bytes; - s->in_flight++; - s->bytes_in_flight += bytes; - if (is_discard) { - blk_aio_pdiscard(s->target, offset, - op->bytes, mirror_write_complete, op); - } else { - blk_aio_pwrite_zeroes(s->target, offset, - op->bytes, s->unmap ? BDRV_REQ_MAY_UNMAP : 0, - mirror_write_complete, op); - } + ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes, + op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0); + mirror_write_complete(op, ret); +} + +static void coroutine_fn mirror_co_discard(void *opaque) +{ + MirrorOp *op = opaque; + int ret; + + op->s->in_flight++; + op->s->bytes_in_flight += op->bytes; + *op->bytes_handled = op->bytes; + + ret = blk_co_pdiscard(op->s->target, op->offset, op->bytes); + mirror_write_complete(op, ret); } static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, unsigned bytes, MirrorMethod mirror_method) { + MirrorOp *op; + Coroutine *co; + int64_t bytes_handled = -1; + + op = g_new(MirrorOp, 1); + *op = (MirrorOp){ + .s = s, + .offset = offset, + .bytes = bytes, + .bytes_handled = &bytes_handled, + }; + switch (mirror_method) { case MIRROR_METHOD_COPY: - return mirror_do_read(s, offset, bytes); + co = qemu_coroutine_create(mirror_co_read, op); + break; case MIRROR_METHOD_ZERO: + co = qemu_coroutine_create(mirror_co_zero, op); + break; case MIRROR_METHOD_DISCARD: - mirror_do_zero_or_discard(s, offset, bytes, - mirror_method == MIRROR_METHOD_DISCARD); - return bytes; + co = qemu_coroutine_create(mirror_co_discard, op); + break; default: abort(); } + + qemu_coroutine_enter(co); + /* At this point, ownership of op has been moved to the coroutine + * and the object may already be freed */ + + /* Assert that this value has been set */ + assert(bytes_handled >= 0); + + /* Same assertion as in mirror_co_read() (and for mirror_co_read() + * and mirror_co_discard(), bytes_handled == op->bytes, which + * is the @bytes parameter given to this function) */ + assert(bytes_handled <= UINT_MAX); + return bytes_handled; } static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) From 12aa40822daf0ab13059b27b29a83ded43bae3bb Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:12 +0200 Subject: [PATCH 1413/2380] block/mirror: Use CoQueue to wait on in-flight ops Attach a CoQueue to each in-flight operation so if we need to wait for any we can use it to wait instead of just blindly yielding and hoping for some operation to wake us. A later patch will use this infrastructure to allow requests accessing the same area of the virtual disk to specifically wait for each other. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Message-id: 20180613181823.13618-4-mreitz@redhat.com Signed-off-by: Max Reitz --- block/mirror.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index dcc19ae288..e2348b818a 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/coroutine.h" #include "trace.h" #include "block/blockjob_int.h" #include "block/block_int.h" @@ -33,6 +34,8 @@ typedef struct MirrorBuffer { QSIMPLEQ_ENTRY(MirrorBuffer) next; } MirrorBuffer; +typedef struct MirrorOp MirrorOp; + typedef struct MirrorBlockJob { BlockJob common; BlockBackend *target; @@ -65,15 +68,15 @@ typedef struct MirrorBlockJob { unsigned long *in_flight_bitmap; int in_flight; int64_t bytes_in_flight; + QTAILQ_HEAD(MirrorOpList, MirrorOp) ops_in_flight; int ret; bool unmap; - bool waiting_for_io; int target_cluster_size; int max_iov; bool initial_zeroing_ongoing; } MirrorBlockJob; -typedef struct MirrorOp { +struct MirrorOp { MirrorBlockJob *s; QEMUIOVector qiov; int64_t offset; @@ -82,7 +85,11 @@ typedef struct MirrorOp { /* The pointee is set by mirror_co_read(), mirror_co_zero(), and * mirror_co_discard() before yielding for the first time */ int64_t *bytes_handled; -} MirrorOp; + + CoQueue waiting_requests; + + QTAILQ_ENTRY(MirrorOp) next; +}; typedef enum MirrorMethod { MIRROR_METHOD_COPY, @@ -123,7 +130,9 @@ static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) chunk_num = op->offset / s->granularity; nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity); + bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); + QTAILQ_REMOVE(&s->ops_in_flight, op, next); if (ret >= 0) { if (s->cow_bitmap) { bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); @@ -133,11 +142,9 @@ static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) } } qemu_iovec_destroy(&op->qiov); - g_free(op); - if (s->waiting_for_io) { - qemu_coroutine_enter(s->common.job.co); - } + qemu_co_queue_restart_all(&op->waiting_requests); + g_free(op); } static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret) @@ -227,10 +234,11 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset, static inline void mirror_wait_for_io(MirrorBlockJob *s) { - assert(!s->waiting_for_io); - s->waiting_for_io = true; - qemu_coroutine_yield(); - s->waiting_for_io = false; + MirrorOp *op; + + op = QTAILQ_FIRST(&s->ops_in_flight); + assert(op); + qemu_co_queue_wait(&op->waiting_requests, NULL); } /* Perform a mirror copy operation. @@ -340,6 +348,7 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, .bytes = bytes, .bytes_handled = &bytes_handled, }; + qemu_co_queue_init(&op->waiting_requests); switch (mirror_method) { case MIRROR_METHOD_COPY: @@ -355,6 +364,7 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, abort(); } + QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); qemu_coroutine_enter(co); /* At this point, ownership of op has been moved to the coroutine * and the object may already be freed */ @@ -1290,6 +1300,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, } } + QTAILQ_INIT(&s->ops_in_flight); + trace_mirror_start(bs, s, opaque); job_start(&s->common.job); return; From 1181e19a6d6986a08b889a32438d0ceeee9b2ef3 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:13 +0200 Subject: [PATCH 1414/2380] block/mirror: Wait for in-flight op conflicts This patch makes the mirror code differentiate between simply waiting for any operation to complete (mirror_wait_for_free_in_flight_slot()) and specifically waiting for all operations touching a certain range of the virtual disk to complete (mirror_wait_on_conflicts()). Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Message-id: 20180613181823.13618-5-mreitz@redhat.com Signed-off-by: Max Reitz --- block/mirror.c | 102 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 18 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index e2348b818a..5df6515731 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/coroutine.h" +#include "qemu/range.h" #include "trace.h" #include "block/blockjob_int.h" #include "block/block_int.h" @@ -86,6 +87,7 @@ struct MirrorOp { * mirror_co_discard() before yielding for the first time */ int64_t *bytes_handled; + bool is_pseudo_op; CoQueue waiting_requests; QTAILQ_ENTRY(MirrorOp) next; @@ -110,6 +112,41 @@ static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, } } +static void coroutine_fn mirror_wait_on_conflicts(MirrorOp *self, + MirrorBlockJob *s, + uint64_t offset, + uint64_t bytes) +{ + uint64_t self_start_chunk = offset / s->granularity; + uint64_t self_end_chunk = DIV_ROUND_UP(offset + bytes, s->granularity); + uint64_t self_nb_chunks = self_end_chunk - self_start_chunk; + + while (find_next_bit(s->in_flight_bitmap, self_end_chunk, + self_start_chunk) < self_end_chunk && + s->ret >= 0) + { + MirrorOp *op; + + QTAILQ_FOREACH(op, &s->ops_in_flight, next) { + uint64_t op_start_chunk = op->offset / s->granularity; + uint64_t op_nb_chunks = DIV_ROUND_UP(op->offset + op->bytes, + s->granularity) - + op_start_chunk; + + if (op == self) { + continue; + } + + if (ranges_overlap(self_start_chunk, self_nb_chunks, + op_start_chunk, op_nb_chunks)) + { + qemu_co_queue_wait(&op->waiting_requests, NULL); + break; + } + } + } +} + static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) { MirrorBlockJob *s = op->s; @@ -232,13 +269,22 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset, return ret; } -static inline void mirror_wait_for_io(MirrorBlockJob *s) +static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) { MirrorOp *op; - op = QTAILQ_FIRST(&s->ops_in_flight); - assert(op); - qemu_co_queue_wait(&op->waiting_requests, NULL); + QTAILQ_FOREACH(op, &s->ops_in_flight, next) { + /* Do not wait on pseudo ops, because it may in turn wait on + * some other operation to start, which may in fact be the + * caller of this function. Since there is only one pseudo op + * at any given time, we will always find some real operation + * to wait on. */ + if (!op->is_pseudo_op) { + qemu_co_queue_wait(&op->waiting_requests, NULL); + return; + } + } + abort(); } /* Perform a mirror copy operation. @@ -282,7 +328,7 @@ static void coroutine_fn mirror_co_read(void *opaque) while (s->buf_free_count < nb_chunks) { trace_mirror_yield_in_flight(s, op->offset, s->in_flight); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); } /* Now make a QEMUIOVector taking enough granularity-sized chunks @@ -382,8 +428,9 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) { BlockDriverState *source = s->source; - int64_t offset, first_chunk; - uint64_t delay_ns = 0; + MirrorOp *pseudo_op; + int64_t offset; + uint64_t delay_ns = 0, ret = 0; /* At least the first dirty chunk is mirrored in one iteration. */ int nb_chunks = 1; bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target)); @@ -399,11 +446,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) } bdrv_dirty_bitmap_unlock(s->dirty_bitmap); - first_chunk = offset / s->granularity; - while (test_bit(first_chunk, s->in_flight_bitmap)) { - trace_mirror_yield_in_flight(s, offset, s->in_flight); - mirror_wait_for_io(s); - } + mirror_wait_on_conflicts(NULL, s, offset, 1); job_pause_point(&s->common.job); @@ -440,6 +483,21 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) nb_chunks * s->granularity); bdrv_dirty_bitmap_unlock(s->dirty_bitmap); + /* Before claiming an area in the in-flight bitmap, we have to + * create a MirrorOp for it so that conflicting requests can wait + * for it. mirror_perform() will create the real MirrorOps later, + * for now we just create a pseudo operation that will wake up all + * conflicting requests once all real operations have been + * launched. */ + pseudo_op = g_new(MirrorOp, 1); + *pseudo_op = (MirrorOp){ + .offset = offset, + .bytes = nb_chunks * s->granularity, + .is_pseudo_op = true, + }; + qemu_co_queue_init(&pseudo_op->waiting_requests); + QTAILQ_INSERT_TAIL(&s->ops_in_flight, pseudo_op, next); + bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks); while (nb_chunks > 0 && offset < s->bdev_length) { int ret; @@ -475,11 +533,12 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) while (s->in_flight >= MAX_IN_FLIGHT) { trace_mirror_yield_in_flight(s, offset, s->in_flight); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); } if (s->ret < 0) { - return 0; + ret = 0; + goto fail; } io_bytes = mirror_clip_bytes(s, offset, io_bytes); @@ -494,7 +553,14 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity); delay_ns = block_job_ratelimit_get_delay(&s->common, io_bytes_acct); } - return delay_ns; + + ret = delay_ns; +fail: + QTAILQ_REMOVE(&s->ops_in_flight, pseudo_op, next); + qemu_co_queue_restart_all(&pseudo_op->waiting_requests); + g_free(pseudo_op); + + return ret; } static void mirror_free_init(MirrorBlockJob *s) @@ -521,7 +587,7 @@ static void mirror_free_init(MirrorBlockJob *s) static void mirror_wait_for_all_io(MirrorBlockJob *s) { while (s->in_flight > 0) { - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); } } @@ -676,7 +742,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) if (s->in_flight >= MAX_IN_FLIGHT) { trace_mirror_yield(s, UINT64_MAX, s->buf_free_count, s->in_flight); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); continue; } @@ -849,7 +915,7 @@ static void coroutine_fn mirror_run(void *opaque) if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || (cnt == 0 && s->in_flight > 0)) { trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); continue; } else if (cnt != 0) { delay_ns = mirror_iteration(s); From 138f9fffb809451ef80f5be4647558b72f2339ad Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:14 +0200 Subject: [PATCH 1415/2380] block/mirror: Use source as a BdrvChild With this, the mirror_top_bs is no longer just a technically required node in the BDS graph but actually represents the block job operation. Also, drop MirrorBlockJob.source, as we can reach it through mirror_top_bs->backing. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: Alberto Garcia Message-id: 20180613181823.13618-6-mreitz@redhat.com Signed-off-by: Max Reitz --- block/mirror.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 5df6515731..abf231c478 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -41,7 +41,6 @@ typedef struct MirrorBlockJob { BlockJob common; BlockBackend *target; BlockDriverState *mirror_top_bs; - BlockDriverState *source; BlockDriverState *base; /* The name of the graph node to replace */ @@ -299,7 +298,6 @@ static void coroutine_fn mirror_co_read(void *opaque) { MirrorOp *op = opaque; MirrorBlockJob *s = op->s; - BlockBackend *source = s->common.blk; int nb_chunks; uint64_t ret; uint64_t max_bytes; @@ -349,7 +347,8 @@ static void coroutine_fn mirror_co_read(void *opaque) s->bytes_in_flight += op->bytes; trace_mirror_one_iteration(s, op->offset, op->bytes); - ret = blk_co_preadv(source, op->offset, op->bytes, &op->qiov, 0); + ret = bdrv_co_preadv(s->mirror_top_bs->backing, op->offset, op->bytes, + &op->qiov, 0); mirror_read_complete(op, ret); } @@ -427,7 +426,7 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) { - BlockDriverState *source = s->source; + BlockDriverState *source = s->mirror_top_bs->backing->bs; MirrorOp *pseudo_op; int64_t offset; uint64_t delay_ns = 0, ret = 0; @@ -601,7 +600,7 @@ static void mirror_exit(Job *job, void *opaque) BlockJob *bjob = &s->common; MirrorExitData *data = opaque; AioContext *replace_aio_context = NULL; - BlockDriverState *src = s->source; + BlockDriverState *src = s->mirror_top_bs->backing->bs; BlockDriverState *target_bs = blk_bs(s->target); BlockDriverState *mirror_top_bs = s->mirror_top_bs; Error *local_err = NULL; @@ -716,7 +715,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) { int64_t offset; BlockDriverState *base = s->base; - BlockDriverState *bs = s->source; + BlockDriverState *bs = s->mirror_top_bs->backing->bs; BlockDriverState *target_bs = blk_bs(s->target); int ret; int64_t count; @@ -798,7 +797,7 @@ static void coroutine_fn mirror_run(void *opaque) { MirrorBlockJob *s = opaque; MirrorExitData *data; - BlockDriverState *bs = s->source; + BlockDriverState *bs = s->mirror_top_bs->backing->bs; BlockDriverState *target_bs = blk_bs(s->target); bool need_drain = true; int64_t length; @@ -1293,7 +1292,6 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, /* The block job now has a reference to this node */ bdrv_unref(mirror_top_bs); - s->source = bs; s->mirror_top_bs = mirror_top_bs; /* No resize for the target either; while the mirror is still running, a From ec9f10fe064f2abb9dc60a9fa580d8d0933f2acf Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:15 +0200 Subject: [PATCH 1416/2380] block: Generalize should_update_child() rule Currently, bdrv_replace_node() refuses to create loops from one BDS to itself if the BDS to be replaced is the backing node of the BDS to replace it: Say there is a node A and a node B. Replacing B by A means making all references to B point to A. If B is a child of A (i.e. A has a reference to B), that would mean we would have to make this reference point to A itself -- so we'd create a loop. bdrv_replace_node() (through should_update_child()) refuses to do so if B is the backing node of A. There is no reason why we should create loops if B is not the backing node of A, though. The BDS graph should never contain loops, so we should always refuse to create them. If B is a child of A and B is to be replaced by A, we should simply leave B in place there because it is the most sensible choice. A more specific argument would be: Putting filter drivers into the BDS graph is basically the same as appending an overlay to a backing chain. But the main child BDS of a filter driver is not "backing" but "file", so restricting the no-loop rule to backing nodes would fail here. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: Alberto Garcia Message-id: 20180613181823.13618-7-mreitz@redhat.com Signed-off-by: Max Reitz --- block.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/block.c b/block.c index 6c128007fd..1b8147c1b3 100644 --- a/block.c +++ b/block.c @@ -3427,16 +3427,39 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) return false; } - if (c->role == &child_backing) { - /* If @from is a backing file of @to, ignore the child to avoid - * creating a loop. We only want to change the pointer of other - * parents. */ - QLIST_FOREACH(to_c, &to->children, next) { - if (to_c == c) { - break; - } - } - if (to_c) { + /* If the child @c belongs to the BDS @to, replacing the current + * c->bs by @to would mean to create a loop. + * + * Such a case occurs when appending a BDS to a backing chain. + * For instance, imagine the following chain: + * + * guest device -> node A -> further backing chain... + * + * Now we create a new BDS B which we want to put on top of this + * chain, so we first attach A as its backing node: + * + * node B + * | + * v + * guest device -> node A -> further backing chain... + * + * Finally we want to replace A by B. When doing that, we want to + * replace all pointers to A by pointers to B -- except for the + * pointer from B because (1) that would create a loop, and (2) + * that pointer should simply stay intact: + * + * guest device -> node B + * | + * v + * node A -> further backing chain... + * + * In general, when replacing a node A (c->bs) by a node B (@to), + * if A is a child of B, that means we cannot replace A by B there + * because that would create a loop. Silently detaching A from B + * is also not really an option. So overall just leaving A in + * place there is the most sensible choice. */ + QLIST_FOREACH(to_c, &to->children, next) { + if (to_c == c) { return false; } } @@ -3462,6 +3485,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, /* Put all parents into @list and calculate their cumulative permissions */ QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { + assert(c->bs == from); if (!should_update_child(c, to)) { continue; } From a33fbb4f8b64226becf502a123733776ce319b24 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:16 +0200 Subject: [PATCH 1417/2380] hbitmap: Add @advance param to hbitmap_iter_next() This new parameter allows the caller to just query the next dirty position without moving the iterator. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: John Snow Message-id: 20180613181823.13618-8-mreitz@redhat.com Signed-off-by: Max Reitz --- block/backup.c | 2 +- block/dirty-bitmap.c | 2 +- include/qemu/hbitmap.h | 5 ++++- tests/test-hbitmap.c | 26 +++++++++++++------------- util/hbitmap.c | 10 +++++++--- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/block/backup.c b/block/backup.c index 5661435675..d18be40caf 100644 --- a/block/backup.c +++ b/block/backup.c @@ -354,7 +354,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) HBitmapIter hbi; hbitmap_iter_init(&hbi, job->copy_bitmap, 0); - while ((cluster = hbitmap_iter_next(&hbi)) != -1) { + while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) { do { if (yield_and_check(job)) { return 0; diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 383d742cdb..cedb971765 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -519,7 +519,7 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) { - return hbitmap_iter_next(&iter->hbi); + return hbitmap_iter_next(&iter->hbi, true); } /* Called within bdrv_dirty_bitmap_lock..unlock */ diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index 6b6490ecad..ddca52c48e 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -324,11 +324,14 @@ void hbitmap_free_meta(HBitmap *hb); /** * hbitmap_iter_next: * @hbi: HBitmapIter to operate on. + * @advance: If true, advance the iterator. Otherwise, the next call + * of this function will return the same result (if that + * position is still dirty). * * Return the next bit that is set in @hbi's associated HBitmap, * or -1 if all remaining bits are zero. */ -int64_t hbitmap_iter_next(HBitmapIter *hbi); +int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance); /** * hbitmap_iter_next_word: diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index f29631f939..f2158f767d 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -46,7 +46,7 @@ static void hbitmap_test_check(TestHBitmapData *data, i = first; for (;;) { - next = hbitmap_iter_next(&hbi); + next = hbitmap_iter_next(&hbi, true); if (next < 0) { next = data->size; } @@ -435,25 +435,25 @@ static void test_hbitmap_iter_granularity(TestHBitmapData *data, /* Note that hbitmap_test_check has to be invoked manually in this test. */ hbitmap_test_init(data, 131072 << 7, 7); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); hbitmap_test_set(data, (131072 << 7) - 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); } static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t diff) @@ -893,7 +893,7 @@ static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, for (i = 0; i < num_positions; i++) { hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); hbitmap_iter_init(&iter, data->hb, 0); - next = hbitmap_iter_next(&iter); + next = hbitmap_iter_next(&iter, true); if (i == num_positions - 1) { g_assert_cmpint(next, ==, -1); } else { @@ -919,10 +919,10 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); - hbitmap_iter_next(&hbi); + hbitmap_iter_next(&hbi, true); hbitmap_reset_all(data->hb); - hbitmap_iter_next(&hbi); + hbitmap_iter_next(&hbi, true); } static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) diff --git a/util/hbitmap.c b/util/hbitmap.c index 58a2c93842..bcd304041a 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -141,7 +141,7 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) return cur; } -int64_t hbitmap_iter_next(HBitmapIter *hbi) +int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance) { unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1] & hbi->hb->levels[HBITMAP_LEVELS - 1][hbi->pos]; @@ -154,8 +154,12 @@ int64_t hbitmap_iter_next(HBitmapIter *hbi) } } - /* The next call will resume work from the next bit. */ - hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); + if (advance) { + /* The next call will resume work from the next bit. */ + hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); + } else { + hbi->cur[HBITMAP_LEVELS - 1] = cur; + } item = ((uint64_t)hbi->pos << BITS_PER_LEVEL) + ctzl(cur); return item << hbi->granularity; From 269576848ec3d57d2d958cf5ac69b08c44adf816 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:17 +0200 Subject: [PATCH 1418/2380] test-hbitmap: Add non-advancing iter_next tests Add a function that wraps hbitmap_iter_next() and always calls it in non-advancing mode first, and in advancing mode next. The result should always be the same. By using this function everywhere we called hbitmap_iter_next() before, we should get good test coverage for non-advancing hbitmap_iter_next(). Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: John Snow Message-id: 20180613181823.13618-9-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/test-hbitmap.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index f2158f767d..5e67ac1d3a 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -30,6 +30,18 @@ typedef struct TestHBitmapData { } TestHBitmapData; +static int64_t check_hbitmap_iter_next(HBitmapIter *hbi) +{ + int next0, next1; + + next0 = hbitmap_iter_next(hbi, false); + next1 = hbitmap_iter_next(hbi, true); + + g_assert_cmpint(next0, ==, next1); + + return next0; +} + /* Check that the HBitmap and the shadow bitmap contain the same data, * ignoring the same "first" bits. */ @@ -46,7 +58,7 @@ static void hbitmap_test_check(TestHBitmapData *data, i = first; for (;;) { - next = hbitmap_iter_next(&hbi, true); + next = check_hbitmap_iter_next(&hbi); if (next < 0) { next = data->size; } @@ -435,25 +447,25 @@ static void test_hbitmap_iter_granularity(TestHBitmapData *data, /* Note that hbitmap_test_check has to be invoked manually in this test. */ hbitmap_test_init(data, 131072 << 7, 7); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); hbitmap_test_set(data, (131072 << 7) - 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, (L2 + L1 + 1) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), ==, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); } static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t diff) @@ -893,7 +905,7 @@ static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, for (i = 0; i < num_positions; i++) { hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); hbitmap_iter_init(&iter, data->hb, 0); - next = hbitmap_iter_next(&iter, true); + next = check_hbitmap_iter_next(&iter); if (i == num_positions - 1) { g_assert_cmpint(next, ==, -1); } else { @@ -919,10 +931,10 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); - hbitmap_iter_next(&hbi, true); + check_hbitmap_iter_next(&hbi); hbitmap_reset_all(data->hb); - hbitmap_iter_next(&hbi, true); + check_hbitmap_iter_next(&hbi); } static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) From 72d10a94213a954ad569095cb4491f2ae0853c40 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:18 +0200 Subject: [PATCH 1419/2380] block/dirty-bitmap: Add bdrv_dirty_iter_next_area This new function allows to look for a consecutively dirty area in a dirty bitmap. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: John Snow Message-id: 20180613181823.13618-10-mreitz@redhat.com Signed-off-by: Max Reitz --- block/dirty-bitmap.c | 55 ++++++++++++++++++++++++++++++++++++ include/block/dirty-bitmap.h | 2 ++ 2 files changed, 57 insertions(+) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index cedb971765..db1782ec1f 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -522,6 +522,61 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) return hbitmap_iter_next(&iter->hbi, true); } +/** + * Return the next consecutively dirty area in the dirty bitmap + * belonging to the given iterator @iter. + * + * @max_offset: Maximum value that may be returned for + * *offset + *bytes + * @offset: Will contain the start offset of the next dirty area + * @bytes: Will contain the length of the next dirty area + * + * Returns: True if a dirty area could be found before max_offset + * (which means that *offset and *bytes then contain valid + * values), false otherwise. + * + * Note that @iter is never advanced if false is returned. If an area + * is found (which means that true is returned), it will be advanced + * past that area. + */ +bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, + uint64_t *offset, int *bytes) +{ + uint32_t granularity = bdrv_dirty_bitmap_granularity(iter->bitmap); + uint64_t gran_max_offset; + int64_t ret; + int size; + + if (max_offset == iter->bitmap->size) { + /* If max_offset points to the image end, round it up by the + * bitmap granularity */ + gran_max_offset = ROUND_UP(max_offset, granularity); + } else { + gran_max_offset = max_offset; + } + + ret = hbitmap_iter_next(&iter->hbi, false); + if (ret < 0 || ret + granularity > gran_max_offset) { + return false; + } + + *offset = ret; + size = 0; + + assert(granularity <= INT_MAX); + + do { + /* Advance iterator */ + ret = hbitmap_iter_next(&iter->hbi, true); + size += granularity; + } while (ret + granularity <= gran_max_offset && + hbitmap_iter_next(&iter->hbi, false) == ret + granularity && + size <= INT_MAX - granularity); + + *bytes = MIN(size, max_offset - *offset); + return true; +} + /* Called within bdrv_dirty_bitmap_lock..unlock */ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, int64_t offset, int64_t bytes) diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 02e0cbabd2..288dc6adb6 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -82,6 +82,8 @@ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, int64_t offset, int64_t bytes); int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); +bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, + uint64_t *offset, int *bytes); void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset); int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap); From 429076e88dec48ce22a6fb3ba11e5ccb6134f62d Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:19 +0200 Subject: [PATCH 1420/2380] block/mirror: Add MirrorBDSOpaque This will allow us to access the block job data when the mirror block driver becomes more complex. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Message-id: 20180613181823.13618-11-mreitz@redhat.com Signed-off-by: Max Reitz --- block/mirror.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/block/mirror.c b/block/mirror.c index abf231c478..7da5e43c0d 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -76,6 +76,10 @@ typedef struct MirrorBlockJob { bool initial_zeroing_ongoing; } MirrorBlockJob; +typedef struct MirrorBDSOpaque { + MirrorBlockJob *job; +} MirrorBDSOpaque; + struct MirrorOp { MirrorBlockJob *s; QEMUIOVector qiov; @@ -599,6 +603,7 @@ static void mirror_exit(Job *job, void *opaque) MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); BlockJob *bjob = &s->common; MirrorExitData *data = opaque; + MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque; AioContext *replace_aio_context = NULL; BlockDriverState *src = s->mirror_top_bs->backing->bs; BlockDriverState *target_bs = blk_bs(s->target); @@ -691,6 +696,7 @@ static void mirror_exit(Job *job, void *opaque) blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); + bs_opaque->job = NULL; job_completed(job, data->ret, NULL); g_free(data); @@ -1230,6 +1236,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, Error **errp) { MirrorBlockJob *s; + MirrorBDSOpaque *bs_opaque; BlockDriverState *mirror_top_bs; bool target_graph_mod; bool target_is_backing; @@ -1265,6 +1272,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, mirror_top_bs->total_sectors = bs->total_sectors; mirror_top_bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; + bs_opaque = g_new0(MirrorBDSOpaque, 1); + mirror_top_bs->opaque = bs_opaque; bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs)); /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep @@ -1289,6 +1298,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, if (!s) { goto fail; } + bs_opaque->job = s; + /* The block job now has a reference to this node */ bdrv_unref(mirror_top_bs); @@ -1378,6 +1389,7 @@ fail: g_free(s->replaces); blk_unref(s->target); + bs_opaque->job = NULL; job_early_fail(&s->common.job); } From 62f13600593322b8e796f15fd6742064fba6ab65 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:20 +0200 Subject: [PATCH 1421/2380] job: Add job_progress_increase_remaining() Signed-off-by: Max Reitz Message-id: 20180613181823.13618-12-mreitz@redhat.com Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- include/qemu/job.h | 15 +++++++++++++++ job.c | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/include/qemu/job.h b/include/qemu/job.h index 1d820530fa..18c9223e31 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -335,6 +335,21 @@ void job_progress_update(Job *job, uint64_t done); */ void job_progress_set_remaining(Job *job, uint64_t remaining); +/** + * @job: The job whose expected progress end value is updated + * @delta: Value which is to be added to the current expected end + * value + * + * Increases the expected end value of the progress counter of a job. + * This is useful for parenthesis operations: If a job has to + * conditionally perform a high-priority operation as part of its + * progress, it calls this function with the expected operation's + * length before, and job_progress_update() afterwards. + * (So the operation acts as a parenthesis in regards to the main job + * operation running in background.) + */ +void job_progress_increase_remaining(Job *job, uint64_t delta); + /** To be called when a cancelled job is finalised. */ void job_event_cancelled(Job *job); diff --git a/job.c b/job.c index 84e140238b..fa671b431a 100644 --- a/job.c +++ b/job.c @@ -385,6 +385,11 @@ void job_progress_set_remaining(Job *job, uint64_t remaining) job->progress_total = job->progress_current + remaining; } +void job_progress_increase_remaining(Job *job, uint64_t delta) +{ + job->progress_total += delta; +} + void job_event_cancelled(Job *job) { notifier_list_notify(&job->on_finalize_cancelled, job); From d06107ade0ce74dc39739bac80de84b51ec18546 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:21 +0200 Subject: [PATCH 1422/2380] block/mirror: Add active mirroring This patch implements active synchronous mirroring. In active mode, the passive mechanism will still be in place and is used to copy all initially dirty clusters off the source disk; but every write request will write data both to the source and the target disk, so the source cannot be dirtied faster than data is mirrored to the target. Also, once the block job has converged (BLOCK_JOB_READY sent), source and target are guaranteed to stay in sync (unless an error occurs). Active mode is completely optional and currently disabled at runtime. A later patch will add a way for users to enable it. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Message-id: 20180613181823.13618-13-mreitz@redhat.com Signed-off-by: Max Reitz --- block/mirror.c | 252 ++++++++++++++++++++++++++++++++++++++++++- qapi/block-core.json | 18 ++++ 2 files changed, 265 insertions(+), 5 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 7da5e43c0d..99b9b92c30 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -51,8 +51,12 @@ typedef struct MirrorBlockJob { Error *replace_blocker; bool is_none_mode; BlockMirrorBackingMode backing_mode; + MirrorCopyMode copy_mode; BlockdevOnError on_source_error, on_target_error; bool synced; + /* Set when the target is synced (dirty bitmap is clean, nothing + * in flight) and the job is running in active mode */ + bool actively_synced; bool should_complete; int64_t granularity; size_t buf_size; @@ -74,6 +78,7 @@ typedef struct MirrorBlockJob { int target_cluster_size; int max_iov; bool initial_zeroing_ongoing; + int in_active_write_counter; } MirrorBlockJob; typedef struct MirrorBDSOpaque { @@ -91,6 +96,7 @@ struct MirrorOp { int64_t *bytes_handled; bool is_pseudo_op; + bool is_active_write; CoQueue waiting_requests; QTAILQ_ENTRY(MirrorOp) next; @@ -106,6 +112,7 @@ static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, int error) { s->synced = false; + s->actively_synced = false; if (read) { return block_job_error_action(&s->common, s->on_source_error, true, error); @@ -272,7 +279,7 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset, return ret; } -static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) +static inline void mirror_wait_for_any_operation(MirrorBlockJob *s, bool active) { MirrorOp *op; @@ -282,7 +289,7 @@ static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) * caller of this function. Since there is only one pseudo op * at any given time, we will always find some real operation * to wait on. */ - if (!op->is_pseudo_op) { + if (!op->is_pseudo_op && op->is_active_write == active) { qemu_co_queue_wait(&op->waiting_requests, NULL); return; } @@ -290,6 +297,12 @@ static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) abort(); } +static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) +{ + /* Only non-active operations use up in-flight slots */ + mirror_wait_for_any_operation(s, false); +} + /* Perform a mirror copy operation. * * *op->bytes_handled is set to the number of bytes copied after and @@ -846,6 +859,7 @@ static void coroutine_fn mirror_run(void *opaque) /* Transition to the READY state and wait for complete. */ job_transition_to_ready(&s->common.job); s->synced = true; + s->actively_synced = true; while (!job_is_cancelled(&s->common.job) && !s->should_complete) { job_yield(&s->common.job); } @@ -897,6 +911,12 @@ static void coroutine_fn mirror_run(void *opaque) int64_t cnt, delta; bool should_complete; + /* Do not start passive operations while there are active + * writes in progress */ + while (s->in_active_write_counter) { + mirror_wait_for_any_operation(s, true); + } + if (s->ret < 0) { ret = s->ret; goto immediate_exit; @@ -942,6 +962,9 @@ static void coroutine_fn mirror_run(void *opaque) */ job_transition_to_ready(&s->common.job); s->synced = true; + if (s->copy_mode != MIRROR_COPY_MODE_BACKGROUND) { + s->actively_synced = true; + } } should_complete = s->should_complete || @@ -1140,16 +1163,232 @@ static const BlockJobDriver commit_active_job_driver = { .drain = mirror_drain, }; +static void do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + BdrvDirtyBitmapIter *iter; + QEMUIOVector target_qiov; + uint64_t dirty_offset; + int dirty_bytes; + + if (qiov) { + qemu_iovec_init(&target_qiov, qiov->niov); + } + + iter = bdrv_dirty_iter_new(job->dirty_bitmap); + bdrv_set_dirty_iter(iter, offset); + + while (true) { + bool valid_area; + int ret; + + bdrv_dirty_bitmap_lock(job->dirty_bitmap); + valid_area = bdrv_dirty_iter_next_area(iter, offset + bytes, + &dirty_offset, &dirty_bytes); + if (!valid_area) { + bdrv_dirty_bitmap_unlock(job->dirty_bitmap); + break; + } + + bdrv_reset_dirty_bitmap_locked(job->dirty_bitmap, + dirty_offset, dirty_bytes); + bdrv_dirty_bitmap_unlock(job->dirty_bitmap); + + job_progress_increase_remaining(&job->common.job, dirty_bytes); + + assert(dirty_offset - offset <= SIZE_MAX); + if (qiov) { + qemu_iovec_reset(&target_qiov); + qemu_iovec_concat(&target_qiov, qiov, + dirty_offset - offset, dirty_bytes); + } + + switch (method) { + case MIRROR_METHOD_COPY: + ret = blk_co_pwritev(job->target, dirty_offset, dirty_bytes, + qiov ? &target_qiov : NULL, flags); + break; + + case MIRROR_METHOD_ZERO: + assert(!qiov); + ret = blk_co_pwrite_zeroes(job->target, dirty_offset, dirty_bytes, + flags); + break; + + case MIRROR_METHOD_DISCARD: + assert(!qiov); + ret = blk_co_pdiscard(job->target, dirty_offset, dirty_bytes); + break; + + default: + abort(); + } + + if (ret >= 0) { + job_progress_update(&job->common.job, dirty_bytes); + } else { + BlockErrorAction action; + + bdrv_set_dirty_bitmap(job->dirty_bitmap, dirty_offset, dirty_bytes); + job->actively_synced = false; + + action = mirror_error_action(job, false, -ret); + if (action == BLOCK_ERROR_ACTION_REPORT) { + if (!job->ret) { + job->ret = ret; + } + break; + } + } + } + + bdrv_dirty_iter_free(iter); + if (qiov) { + qemu_iovec_destroy(&target_qiov); + } +} + +static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s, + uint64_t offset, + uint64_t bytes) +{ + MirrorOp *op; + uint64_t start_chunk = offset / s->granularity; + uint64_t end_chunk = DIV_ROUND_UP(offset + bytes, s->granularity); + + op = g_new(MirrorOp, 1); + *op = (MirrorOp){ + .s = s, + .offset = offset, + .bytes = bytes, + .is_active_write = true, + }; + qemu_co_queue_init(&op->waiting_requests); + QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); + + s->in_active_write_counter++; + + mirror_wait_on_conflicts(op, s, offset, bytes); + + bitmap_set(s->in_flight_bitmap, start_chunk, end_chunk - start_chunk); + + return op; +} + +static void coroutine_fn active_write_settle(MirrorOp *op) +{ + uint64_t start_chunk = op->offset / op->s->granularity; + uint64_t end_chunk = DIV_ROUND_UP(op->offset + op->bytes, + op->s->granularity); + + if (!--op->s->in_active_write_counter && op->s->actively_synced) { + BdrvChild *source = op->s->mirror_top_bs->backing; + + if (QLIST_FIRST(&source->bs->parents) == source && + QLIST_NEXT(source, next_parent) == NULL) + { + /* Assert that we are back in sync once all active write + * operations are settled. + * Note that we can only assert this if the mirror node + * is the source node's only parent. */ + assert(!bdrv_get_dirty_count(op->s->dirty_bitmap)); + } + } + bitmap_clear(op->s->in_flight_bitmap, start_chunk, end_chunk - start_chunk); + QTAILQ_REMOVE(&op->s->ops_in_flight, op, next); + qemu_co_queue_restart_all(&op->waiting_requests); + g_free(op); +} + static int coroutine_fn bdrv_mirror_top_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } +static int coroutine_fn bdrv_mirror_top_do_write(BlockDriverState *bs, + MirrorMethod method, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, + int flags) +{ + MirrorOp *op = NULL; + MirrorBDSOpaque *s = bs->opaque; + int ret = 0; + bool copy_to_target; + + copy_to_target = s->job->ret >= 0 && + s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; + + if (copy_to_target) { + op = active_write_prepare(s->job, offset, bytes); + } + + switch (method) { + case MIRROR_METHOD_COPY: + ret = bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); + break; + + case MIRROR_METHOD_ZERO: + ret = bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); + break; + + case MIRROR_METHOD_DISCARD: + ret = bdrv_co_pdiscard(bs->backing->bs, offset, bytes); + break; + + default: + abort(); + } + + if (ret < 0) { + goto out; + } + + if (copy_to_target) { + do_sync_target_write(s->job, method, offset, bytes, qiov, flags); + } + +out: + if (copy_to_target) { + active_write_settle(op); + } + return ret; +} + static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { - return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); + MirrorBDSOpaque *s = bs->opaque; + QEMUIOVector bounce_qiov; + void *bounce_buf; + int ret = 0; + bool copy_to_target; + + copy_to_target = s->job->ret >= 0 && + s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; + + if (copy_to_target) { + /* The guest might concurrently modify the data to write; but + * the data on source and destination must match, so we have + * to use a bounce buffer if we are going to write to the + * target now. */ + bounce_buf = qemu_blockalign(bs, bytes); + iov_to_buf_full(qiov->iov, qiov->niov, 0, bounce_buf, bytes); + + qemu_iovec_init(&bounce_qiov, 1); + qemu_iovec_add(&bounce_qiov, bounce_buf, bytes); + qiov = &bounce_qiov; + } + + ret = bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, offset, bytes, qiov, + flags); + + if (copy_to_target) { + qemu_iovec_destroy(&bounce_qiov); + qemu_vfree(bounce_buf); + } + + return ret; } static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) @@ -1164,13 +1403,15 @@ static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { - return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, offset, bytes, NULL, + flags); } static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { - return bdrv_co_pdiscard(bs->backing->bs, offset, bytes); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, offset, bytes, + NULL, 0); } static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs, QDict *opts) @@ -1340,6 +1581,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, s->on_target_error = on_target_error; s->is_none_mode = is_none_mode; s->backing_mode = backing_mode; + s->copy_mode = MIRROR_COPY_MODE_BACKGROUND; s->base = base; s->granularity = granularity; s->buf_size = ROUND_UP(buf_size, granularity); diff --git a/qapi/block-core.json b/qapi/block-core.json index ab629d1647..96f8da1322 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1050,6 +1050,24 @@ { 'enum': 'MirrorSyncMode', 'data': ['top', 'full', 'none', 'incremental'] } +## +# @MirrorCopyMode: +# +# An enumeration whose values tell the mirror block job when to +# trigger writes to the target. +# +# @background: copy data in background only. +# +# @write-blocking: when data is written to the source, write it +# (synchronously) to the target as well. In +# addition, data is copied in background just like in +# @background mode. +# +# Since: 3.0 +## +{ 'enum': 'MirrorCopyMode', + 'data': ['background', 'write-blocking'] } + ## # @BlockJobInfo: # From 481debaa3270fb276dcf27205aa27ad52cc34590 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:22 +0200 Subject: [PATCH 1423/2380] block/mirror: Add copy mode QAPI interface This patch allows the user to specify whether to use active or only background mode for mirror block jobs. Currently, this setting will remain constant for the duration of the entire block job. Signed-off-by: Max Reitz Reviewed-by: Alberto Garcia Message-id: 20180613181823.13618-14-mreitz@redhat.com Signed-off-by: Max Reitz --- block/mirror.c | 12 +++++++----- blockdev.c | 9 ++++++++- include/block/block_int.h | 4 +++- qapi/block-core.json | 11 +++++++++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 99b9b92c30..61bd9f3cf1 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1473,7 +1473,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, const BlockJobDriver *driver, bool is_none_mode, BlockDriverState *base, bool auto_complete, const char *filter_node_name, - bool is_mirror, + bool is_mirror, MirrorCopyMode copy_mode, Error **errp) { MirrorBlockJob *s; @@ -1581,7 +1581,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, s->on_target_error = on_target_error; s->is_none_mode = is_none_mode; s->backing_mode = backing_mode; - s->copy_mode = MIRROR_COPY_MODE_BACKGROUND; + s->copy_mode = copy_mode; s->base = base; s->granularity = granularity; s->buf_size = ROUND_UP(buf_size, granularity); @@ -1648,7 +1648,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, - bool unmap, const char *filter_node_name, Error **errp) + bool unmap, const char *filter_node_name, + MirrorCopyMode copy_mode, Error **errp) { bool is_none_mode; BlockDriverState *base; @@ -1663,7 +1664,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, speed, granularity, buf_size, backing_mode, on_source_error, on_target_error, unmap, NULL, NULL, &mirror_job_driver, is_none_mode, base, false, - filter_node_name, true, errp); + filter_node_name, true, copy_mode, errp); } void commit_active_start(const char *job_id, BlockDriverState *bs, @@ -1686,7 +1687,8 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, MIRROR_LEAVE_BACKING_CHAIN, on_error, on_error, true, cb, opaque, &commit_active_job_driver, false, base, auto_complete, - filter_node_name, false, &local_err); + filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, + &local_err); if (local_err) { error_propagate(errp, local_err); goto error_restore_flags; diff --git a/blockdev.c b/blockdev.c index 7f65cd7497..58d7570932 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3586,6 +3586,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, bool has_unmap, bool unmap, bool has_filter_node_name, const char *filter_node_name, + bool has_copy_mode, MirrorCopyMode copy_mode, Error **errp) { @@ -3610,6 +3611,9 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, if (!has_filter_node_name) { filter_node_name = NULL; } + if (!has_copy_mode) { + copy_mode = MIRROR_COPY_MODE_BACKGROUND; + } if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", @@ -3640,7 +3644,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, has_replaces ? replaces : NULL, speed, granularity, buf_size, sync, backing_mode, on_source_error, on_target_error, unmap, filter_node_name, - errp); + copy_mode, errp); } void qmp_drive_mirror(DriveMirror *arg, Error **errp) @@ -3786,6 +3790,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) arg->has_on_target_error, arg->on_target_error, arg->has_unmap, arg->unmap, false, NULL, + arg->has_copy_mode, arg->copy_mode, &local_err); bdrv_unref(target_bs); error_propagate(errp, local_err); @@ -3806,6 +3811,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, BlockdevOnError on_target_error, bool has_filter_node_name, const char *filter_node_name, + bool has_copy_mode, MirrorCopyMode copy_mode, Error **errp) { BlockDriverState *bs; @@ -3838,6 +3844,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, has_on_target_error, on_target_error, true, true, has_filter_node_name, filter_node_name, + has_copy_mode, copy_mode, &local_err); error_propagate(errp, local_err); diff --git a/include/block/block_int.h b/include/block/block_int.h index 7cd7eed83b..74646ed722 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -1031,6 +1031,7 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, * @filter_node_name: The node name that should be assigned to the filter * driver that the mirror job inserts into the graph above @bs. NULL means that * a node name should be autogenerated. + * @copy_mode: When to trigger writes to the target. * @errp: Error object. * * Start a mirroring operation on @bs. Clusters that are allocated @@ -1044,7 +1045,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, - bool unmap, const char *filter_node_name, Error **errp); + bool unmap, const char *filter_node_name, + MirrorCopyMode copy_mode, Error **errp); /* * backup_job_create: diff --git a/qapi/block-core.json b/qapi/block-core.json index 96f8da1322..cc3ede0630 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1710,6 +1710,9 @@ # written. Both will result in identical contents. # Default is true. (Since 2.4) # +# @copy-mode: when to copy data to the destination; defaults to 'background' +# (Since: 3.0) +# # Since: 1.3 ## { 'struct': 'DriveMirror', @@ -1719,7 +1722,7 @@ '*speed': 'int', '*granularity': 'uint32', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', - '*unmap': 'bool' } } + '*unmap': 'bool', '*copy-mode': 'MirrorCopyMode' } } ## # @BlockDirtyBitmap: @@ -1982,6 +1985,9 @@ # above @device. If this option is not given, a node name is # autogenerated. (Since: 2.9) # +# @copy-mode: when to copy data to the destination; defaults to 'background' +# (Since: 3.0) +# # Returns: nothing on success. # # Since: 2.6 @@ -2002,7 +2008,8 @@ '*speed': 'int', '*granularity': 'uint32', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', - '*filter-node-name': 'str' } } + '*filter-node-name': 'str', + '*copy-mode': 'MirrorCopyMode' } } ## # @block_set_io_throttle: From e38da02091eeed56bb370ec9d72c4367d4e9ada3 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 13 Jun 2018 20:18:23 +0200 Subject: [PATCH 1424/2380] iotests: Add test for active mirroring Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: Alberto Garcia Message-id: 20180613181823.13618-15-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/151 | 120 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/151.out | 5 ++ tests/qemu-iotests/group | 1 + 3 files changed, 126 insertions(+) create mode 100755 tests/qemu-iotests/151 create mode 100644 tests/qemu-iotests/151.out diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151 new file mode 100755 index 0000000000..fe53b9f446 --- /dev/null +++ b/tests/qemu-iotests/151 @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# +# Tests for active mirroring +# +# Copyright (C) 2018 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 . +# + +import os +import iotests +from iotests import qemu_img + +source_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt) +target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) + +class TestActiveMirror(iotests.QMPTestCase): + image_len = 128 * 1024 * 1024 # MB + potential_writes_in_flight = True + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, source_img, '128M') + qemu_img('create', '-f', iotests.imgfmt, target_img, '128M') + + blk_source = {'id': 'source', + 'if': 'none', + 'node-name': 'source-node', + 'driver': iotests.imgfmt, + 'file': {'driver': 'file', + 'filename': source_img}} + + blk_target = {'node-name': 'target-node', + 'driver': iotests.imgfmt, + 'file': {'driver': 'file', + 'filename': target_img}} + + self.vm = iotests.VM() + self.vm.add_drive_raw(self.vm.qmp_to_opts(blk_source)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk_target)) + self.vm.add_device('virtio-blk,drive=source') + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + + if not self.potential_writes_in_flight: + self.assertTrue(iotests.compare_images(source_img, target_img), + 'mirror target does not match source') + + os.remove(source_img) + os.remove(target_img) + + def doActiveIO(self, sync_source_and_target): + # Fill the source image + self.vm.hmp_qemu_io('source', + 'write -P 1 0 %i' % self.image_len); + + # Start some background requests + for offset in range(1 * self.image_len / 8, 3 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -P 2 %i 1M' % offset) + for offset in range(2 * self.image_len / 8, 3 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) + + # Start the block job + result = self.vm.qmp('blockdev-mirror', + job_id='mirror', + filter_node_name='mirror-node', + device='source-node', + target='target-node', + sync='full', + copy_mode='write-blocking') + self.assert_qmp(result, 'return', {}) + + # Start some more requests + for offset in range(3 * self.image_len / 8, 5 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) + for offset in range(4 * self.image_len / 8, 5 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) + + # Wait for the READY event + self.wait_ready(drive='mirror') + + # Now start some final requests; all of these (which land on + # the source) should be settled using the active mechanism. + # The mirror code itself asserts that the source BDS's dirty + # bitmap will stay clean between READY and COMPLETED. + for offset in range(5 * self.image_len / 8, 7 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) + for offset in range(6 * self.image_len / 8, 7 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) + + if sync_source_and_target: + # If source and target should be in sync after the mirror, + # we have to flush before completion + self.vm.hmp_qemu_io('source', 'aio_flush') + self.potential_writes_in_flight = False + + self.complete_and_wait(drive='mirror', wait_ready=False) + + def testActiveIO(self): + self.doActiveIO(False) + + def testActiveIOFlushed(self): + self.doActiveIO(True) + + + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2', 'raw']) diff --git a/tests/qemu-iotests/151.out b/tests/qemu-iotests/151.out new file mode 100644 index 0000000000..fbc63e62f8 --- /dev/null +++ b/tests/qemu-iotests/151.out @@ -0,0 +1,5 @@ +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 937a3d0a4d..eea75819d2 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -157,6 +157,7 @@ 148 rw auto quick 149 rw auto sudo 150 rw auto quick +151 rw auto 152 rw auto quick 153 rw auto quick 154 rw auto backing quick From 5b20e4cacecb62fb2bdc6867c11d44cddd77c4ff Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Mon, 18 Jun 2018 19:21:43 +0100 Subject: [PATCH 1425/2380] Update OpenBIOS images to 8fe6f5f96f built from submodule. Signed-off-by: Mark Cave-Ayland --- pc-bios/openbios-ppc | Bin 754936 -> 763128 bytes pc-bios/openbios-sparc32 | Bin 382048 -> 382048 bytes pc-bios/openbios-sparc64 | Bin 1593408 -> 1593408 bytes roms/openbios | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index af70c365eeaa0778ec62748f6c16f74d6c78b81c..a39cbe57ca336c3567444eaefef2b05d707f45d8 100644 GIT binary patch literal 763128 zcmeFa4R};VmOosbG}>U?3A3#mG&r3hXaWI(#M$n25;}sjO@ceYNhau^lWc<$6^#nJ z&2XFBO@f;+I-3}9Kto9QT5#2W*wJ}$Qr6*j+x)L#8^b%zZhZ1yT#}XGATn`vtd48 z@a7OPB1Mc4!|{t1{6?n`{1ex#uM%;KY(unJFmx*}IAvXJyghz8&=4$g}HfIHmv(CzA8@1}#&3_xTI{r_i z4#u6l@QwZF6^^I=oSn}%dY1n=+eiOX{+#XS8{L1_{-gbGH2>!tSH9u>zx<8vzxLR) z&DxTf%_TmE(3Zq*)*ick^GqS;{yBBRpR(|EI?1Pu*U=(fJMU|L5QE{(trj@Bf)^c>hm- z!~6f~H@yF+zR~^vQ_r{2_M#MsQXoo!CdO|Yufu~;^E`J9#i+qZ9^r;efkuA z`V@Wo6n**>efkuAYEKfjKi8hi|GDG8!twvN@&Dt;!f38BWf4znO|G!jl!~~Tkch8o z684%_kx+9?B-UDlqc%w-*XD|p+ES5L8xrZ1AJoa9{NU-N{NR~I`N7jOl>B&wX#D>c zZvA5m+^m%EAo~RQ#?wjp#xslZji-n5ji(5LPC?Kq2s#Bpry%GQ1f3v`ju7Y+0-Zvj zQwVelCJ8HL(g_uvRf6~+-wlipiqnK;F50x=nG8MC>GTQV4C1eGYW_EJCYG62ZD$QCU|is_H`Gsk$anQ`ah1)SgF z@oN>10M2X{@Mz+KZ;+%=tyyQZh(uIahBYq}D5O>e|q(|d5&^a%icTJzbUDLR8J6I{wvM^pwk)F=G!*SnoQohk! zne8#UL*ts$T*D2cC^bH_c0_X;(_DEcTC@kf6L~Ll;wJn~HszUH~Rn}mJpQKNfm3vj%z**dim&#CLn(ROkm6)>}#dFS}N z8hw4aS6n|UbZ@f=<=Pc6aAw2**&Q;V4+@XCy zXiIPOJ4hZS`e_Qak9e;c9cK~@Yb;ohH`f=Q?ejInJ zAIIJ5$8op%aq%Mln$g}Xc<#Wn1Li|3d6gyCEm= z|4i@0+>5j+#-gDd1|ueiDi{p=2nSYKXuYJGViC1!Ii?t#xY zE`JolGmrwk@!t$UEeHB9Y%FpbS#Zv<0{Sm-ZC? zUcz5&itsEmI+-3oUG0Gz{it_LJK?lzZ&+61{R6Gi8L#;!kM!UU|3DbV;+V~aK5+VUc?FgB0k4}iy%(K2X&FBa02e1hs&0x8iros_3Uni-T-a127D&~ zbv$c@12DEhHe;q57|$Gh2S&fa4jROJV?6JB#goG)RTze+!RVZ{4l;yto*lx5=YuS( zAHTxBy&rDJ#Ho~YgS1ZN=6SrC{pYGNRE znxFWaI_m)YM_r-?fa^MmYlgOCyr2A=JYzr`aM7NXM4V7B-unF>jd(?pI(#-UQYeOUzAsKgG*39JJV;4 za5uYKgb3t$o#>w}@Z-b&Kx*vf;{A^Ax!Z&#kouuLlq#%6sr!5N=e(ZW2I0ha08X&1 ztT#m2$5Mp_-t0m52XGO z?N0L-uej=Q{C*|k_r>C>tDXJ7ul_0Di|eo6<-3RY9rxoK3~}x@!vJsFi`yLE(+_`P z*EUqXpzT`zf+kMQrM!4!a!wa6`O&W8{m%5l2RG(Cup}t-CHek9-WPuTP|W6lD`s@YG9MoX$6T-ep~<&oGJ&adV+B!Krs2_v=eXJyO>otl+7_fjrfHN%Ht$x$BpY z-muK@n;zt&@E>vE`w&kH(`za)cb5qHj;EDruoLEV!nZXw;xTiD0!F~;YVQ}D_4(C4r%gpm3Lb1ZEZ_S+jXegS1xTO7OD zqm6#-A~2^ZvqypwNS841})AS}`jJYofm)Vh!P4U{Xx z(GKmxPNdxymJhyhFLB9>`rQJj~~u=Bym+aOJ_~z*kqY8fQXxS%h}VQBd-x zm|L<*BxTs#VsmM#NDnwH$s7}JjQctPl772GA|7)|5pc(cV$w}JBOc#$Sz-yNIZNbkm$L?!TuQt_&!C!T(B9a--z69m@zZ-{8Pw-E z1;LXX3w)nN&P(`4i=A@-zSEL`@3e6KbO3H6Y<;mZ=}I$ssW!<@Kxt zxiM$Cj|zdb;jSd>Y}%?dA#0*DXcg`f$X=7xq0K`d-Z9wRxkNo|0`o}A5XUzJEEKgm4 zF^KDjlX_6`yYqy^8|_BwFVuhOzp3A4*54@YEUtSX)3tc=H`hOE-7`b3d-}@!fwfT> z5B8sW1vGt!_B)hE5cyIv%-U;O4!;6CrmtfRe&yiabmLr0<<)`U0z_}M_}-M`cJrxyfnG2c6!1!#L= zbqaKpO~hBtH_ir9Z?#wLGrrJ#s59dRdvywU@y#3TfxJxo-D=nN-^#S}d^gKp2;>od z9_wA?yY^-t%X}G8zAK{qrHJyEBg$WiDBm4Xz9*u5Z$x?6WeOkd)UB{3Z6)I8wVD8Mb2YPC#{i(_2g9f+qn*FV4rU~ewPOEp(d1@A?vThc=&uj zK)U+v^Zgik6U@9gJV#yVZd<$vWrmyh{JmFX zpFtTr^$%d3MSF3W(|eGXpmb->M`qb&DBFtkM6>K(l;!;YF47!k*==Uoe?r++NKZD) zu0dJQrF#?7Qp~c;&9e07qU_U1FEq>6a*izV1!KJy&t9%GYRcAYsLL|5 z&2|=;bw5MhSxBE_)-5vYT2U8%hbwm@uf(k5Qg!A+e`k$G`dqV4l38as>J*{QSmez& z>kKpNjPwe32htx_b+XQw>EA*6yYhXVD0lki-U8U_w{Pxk$a@ua7MgWl<2(WzVlUGC z3Vzlm6OJ2YtD%pQk*Bdd=hUI+)2#BZ>bdUMC^PH_;LoGnm^cN&?+*c|Z1OhK$lJiP z5bcdZTa_kmqs_Xh<}=BxUxl)>QMSj#bDmdZ9z(sSR6X}evs?|zJ&bZ6WMigud16Ir zsv{;_ct!0vAxdrEjd3}wq84_RCW`UxydS(s{TKM_{LM;_-#RngCr!UB=Ot4I*x47@ zFtuhoD^-8o=J>N8nX{2GpY9#}~H z@g2YV@H~LN*dpt3t^{w)>Zfy8PXwJC6<(Pp{}V6QYbMScoxlfpzGmXNr5{hU*@tK5 z>d5vmjwhib&wLG@FZSb^SrS=K&7)n*)x6gQzggE}-MGQd`Hwm@->AKlF!?U|4E^F9 z7>1W`)Eou8zI6lYyPS>jf_9Fxl)ktHtE8RKAnhdZ+m{yrFB$!C5C-j&Aj*NpYCaxd zAI<%#8iNbc{$W|lcVpSR$@6_8M)Qe`Lf@gKk?G|=qX&7G!Ff%o?7sB?>Aij7ReDR7GpT`C4YLNO}J_K#zl$SAozy?e|wnz zXs3qwhxvkdNA^$g;l)jW)r7ybYMgt|$~G`2wEI{;()L5Sf-F6Vu~K6~8E4<2W3Q2Y zx47?RouO#PwudM`q0o2r>a-C4?k14|c{xXW9^X52 z(&T*641P-ViE{FNT~BjX2yKOCh-C6w?TlvdW=|_&0j>l5laBQBuGGdY+Sa)*XdC9i zXZ*KK;~U`DX%G%`9NY7abGfwH&9e2N<@@=@*<)TYafS4F!9X(Ri~Jqw#?-oGF%MEE&JWx4HB zJuG)yM@|dI2nYnXH|@~J zyoL9SSDU7aX6h|Z!?0KM*f*G8i~N?QzUP@Q>4mlL9)O;^mG;k3Rd&i5mWBR+&GgsU3p;xjc_&nzDeua4wz!q{UpvcB z738fcaz1n?Jn#r`DgnGT&UKbVh0~O^3a2GKNDm?1VYahe(dgC%NJ~~Uobs5$^K3xY zS!YQBJlN<{?p`{r4)fbt&osANVzWp?@2+ zCRNm1_L%ftM*m8_@i}~2SXVrG-0$4OTk9KS+`=)4!t5?zP z*4s>)&CGoDfe&G`VEuG!lH$|J!_B+`q+|Vb>u{Bxu5{8mOCi#+cABhy`yPPZp8f$~ zn>P96LuS7}NBT~r&rxGLxfSmvY78d7s`xa0Ez(T;dvX);H)XXPJOw?TaW8C$a?p-E zGGC45Os0`8&9^F^t~E*-G=(ZkXNPZrz`u{H~+6t2WuOPX&0xzW5R+pJPGn|sZq2@ zf7MJMhjjLBrQ-8+#sBLptCby{{xr)`Cp&%fZ*wo14}a7o$h)Opjbl3Hq0z*l6gvEA z?WeFimWhSb%g)OAdEOYi^e53)0se7b%apC)8((fi%y#T0pL5yf{SEmtaP>5Q;419R zY?}Xre%}1525@nx8`YR^PS|#%n7b9)ESysuqZ~hvTEk*hcv+O)c z&vlj-!1-s;s0ef1vHp4Q=S<&)^y>b!e~L%i9;7YmPg~!Gw0}ohX@A-;Fz3$4I(E{X zSfd<(y!vxM+su^@Oj}y90lIO*A?G@ATIubHADL-;k=Ci`HF3YfeFDZ`oHx_B9_&*5 zHqq?=1CX@|=)dST%haNO!YpIzzX=>~tC{~8-fd?7JS9&P?nGLgqRGTripL(94Y*-> zN1rLX6K^wl8G3ti8SD}l>e`pbNTa+v)woaCt6-+RgtV-FdRR=~!h}s6Ykf8RvLdY-Wu~h>Ou!oL zN}36K3T&;7CeKcX?Z&m@0cpE~uVG_l>`sFHZb{#dvFoNiEODuP593$%BxsO3ew|nV zIXD2@jca6E#eT#uSklX+%&ZsNkp|gLznlJYyn{CBcM=ZXN8mk|F$Z|R2Jg4&_aFA_ zcl_4RJ>Rg;O5gaYA8oCK42-6)5I)=j;-Gw1Whans&ZCrz49cq9tMSHs`>eJ&{66wp z;D1gZ`9QekNmv>F>>zdnM~SMeYuf3>eAeFZxxuMu&eHdulk0r1@V!bnL*PZfhNPLI zRqtr7zr5mpi;M$M`yY_;o@1mJ_{I@l*CFj4%%Pw|&t50|=O+C- zl19XjWg&m8w@p8J#jo$7AGKuU(D?Pt5BHOOG+{0_VLmnl%zOJ`{zUT9SNq&!55pup zuVvD5wvE0?-+)&f<#_bbguDb?hpGLu?jA+s?h6Ws?lb!4PyPLLE|qIZ%AwMM4?;dq zUMFb>9WWHHc|~eFk=Zy+0wVinlSY!{1c#^+jw8DjX75OCs+K=-Pp4f zQ}#Ly;J+LD2od$jTgcn0jlIT?{FGnpMI46^!@_XQ{A%PCpl->ay71ObS%AE`$Xhrl zk8sU-g*t4~e$aw`4yV%BH@`#OYwF!Br|-d=yyBMq(BZArY23SmKD&j!3CuymRNrr! zE&V)F554D+`*aQ1*Ku%9={*nSD$@7C-)ih`ws7pe&R0QR4DV|d`mV|`>zqZK=KlNW zzWuL$n4^dP--r2~Q4ug}!u-__bL`zx?$^t`efT!U7De*h(0xk`4Xl$WW0AsqYSxi_ z%JsqSkcfxv)eTmel{@!3ivz_A@JkkuC;lbY!mbMXlM^^4V{XUy>sLbEmoWgg?La@3y z4P|?fKbm7pdH2R7mXo(p=6tpgmQvd$4}2`IWP4P+)o!er=@%lth?|UOI{N~@klmF! z>_vCmi5BcFCS-dM`{hB9ee#N%$cty)#(UG9zBQ}8;^xv&R&yG4CT*5lymLQN zTRLMRadrA0f?s~Dx&C_yzIMx6!sPr}Q`YQ&?9Nefh1~aVn=1@t%F{%$k^mI0m+-Y^l*ZgQyQ!ql1BF=#sYU6kQm{c4Hyi4y88RwX%c) z*EGX$jW7(Y%=5B2pQHam=NK`Tgkv5UF%9b)tXCi|24-v+vpwd($oJ$4>JT_MPx&gEn&L!DG@Uf%+h~L2L}`N5Uh&Qzut+kd9Y>MEi^L0mq4Q zr(lfgQgJZ7Yn9$M?$9fa3az)j0Ps8&2JKhlZN%*E9$zI!Q3gP7M>Tv&PQ-v=Ec(;) zuxBUfYrL(sO+h??Q^gZB7<-&@e2p5mJAf7@jwatcVbWnj2xDdTiN51=Tr+_$L$q0d z&$aHhhpH+nFfR37olO3YDbsV_Y1je~B)B>>JIIsTk0 zkPjY9;64)P?;z+Io{L~t$k+_jZ%(s<&dU&UT`9yQ{DDUCdWFN|SXN(K@{i6j{*e^EIcH;NF_^tdP zzK0Ir_np;K#Rzk5TFtr4S^3Z!v;1-7PeA@PW`3oaztPOUi2Q4jA7|#556pjfc)oGu zOb~m|s{F&)qdoEweoOu9^gTQozu%!eG9T#)Nc%o*LXIcp6ZtQiFjryhA2#0`@g9tL zUpc-~Bzo3b62r0tJKQE^sR8nebxzo?N?t`9_jqHhqbQfu86Nn@^$yq_LBvRX{Dobw z?)1QxPBD4Kw0R!(0hc7!p$w{c_Up^xbCSH%Zt$Ci`Kkr;2i8tC zyP6TRGwcq`QH+VV1lyY91KuC{F{i}`#^u@dD$G?oL}F;FNMJkxc^rQH1eZhFpf!PU zo`OK?_2q$ECYEcTAnr8vs@}p+ASXSUc5SOSUHc?`Vj$J&Vw`3Ou@i(@izi|zpreg? zse=&DMn2E>{Lie5vW^eX>U%`R;Y6YCY~vBz8i&P=7e^$P-q1_5o?8KI&U{5O0M~Zy9wy`+&BN5r!-E zy_t3U0i&-Ef64t#S=X#{8)Zx@Q!=I2I#$-FyvTB4{8*lPgm_jQXZ(p=)4Z$Zn;yBh z7rABwZ_NRm#xR_4edyg*wU+344f!s%PZ$I1_71V{*HdlQ3lS%MU4jTj&kocvpvbDsiw?vZpFDB ztXsOnblHtHXoQWO<~B~Wa2&%jsp+!(JJ3~Wqyf{p{*>~Kc9|ch3*burAF54hpCK*{ zWrBn8>T65kRzloZhHD+n@qK5GBwoX%zUX25MbI^J-a|e8l!yHqJ2W3$i3|6aFi$HfxQ=>)e;nXwTkz3 zd4wfx1lHZ32&)S*#-8;eQI6e~850NhhkkPHN#`RqPa55jZTg1>`3x>~D89aQO!*Ej z9b$i!t#)avvU4u6Z^=^Ln}+g3T?!5Xx7LJb%KxPh+cDwJGU3knZ{VgxjtBV~aWyUz z-^mhBX=h%N>uZT0%8+;a?Ts$3o%{O?+Y<317Zh$?XK0V(_enK|T^~st5p#>QmlUpD zn-s2HT-PcZ00!k$$|!tt{c_sHvyBmYy6fr4@c`~(VE!fgT>AJAZ92&trmUrK&XeOR zWvDAr@^V=-Vluko2g2zZ8|HJ&Y4n@Mt2sf%s$h+kf|!>h1M7TwHlj{SWSz)zltt7v zV_U!6Nql)0C#)aX4)Qtvef>7;)GC^OS*_|M6b!0EpZMT9_nLL2-$Bzy{ccblo~IgI zC)KQzq}mx==i(6Kf6?@deVI%DuKr(F{MuUBmEZ+$%r%d3{ebwBmzd7I0O>17Id9B} z0NO|%5a-sXqc83W<3_p;)^Qw1(nUYzoha9TJE%7=E>Jj&0|WbcFke$^!oIK8y&&a3-?;Futc$hg&~xR5 zr-rC&#`auTrSR-ZAJpFLA?lPVTrZTWI^BcoBo9&NHnYwIRi}4Qo%5YT)WHTSW&ON7 zv!fqRa1O-n{1CeRyy+J{znAUk$FYvb81~cteAQRmIlsnir&iUuVQ`&6XNb;A|AppD z=!$@w9)`Oc`b>@e`Bl_SJa>jZD4AO4kmIQ>9VLC{{W|ZwbBOvy5%sBe?b!0fi*Js;`r_P!7-#Hl*$n}@z>w1=Tw8~3;a*BAOA?SF{jO{x|`LUaIuOFiB z06hX&cd|VFM*XxoH^48BGN~+6HK5EF1I8a^hOtcJz%p-RK3w_Letu!{{}=Dp=yGj{ zHH2;M5aqV1asFZ})_6iIyTKk&K4XaTA+!AA)%|eehA4NpS#DNyU%9JK0M{X8=L^Jy zlc&EhZKQr)ONakU($_Z%^WE8_Uv1~Cxi56KgS634^_CZweXAE^s(6CqS~g40P4j&+ zmJz;%v#W=IW7a%wfP8bh4?&t&ig7y=JsAuZ` zv!?!6?GCKhNuMr#z)~+eeJ9ksioLviqq9$MD>>^t^i^KzZ2W4wo#yy=ZY?Z(GY4a8 zSpz;$di$yYon)T&Z5Gy?-OP$VHTEE=HO{5Ec-Q|F{+JWa&I{7 zKgE0l|0(eha>Zem~Eh&HE3)>FFX^CHF9pWhjVQEcFB3vktqJUiuwI4C+;hnT}YH|M|4 zua&k`UbC&ZgY~UAUke{~$qytRX8j>__-7xe_CI?kvc9T&XJp-(j8i3!kv1FFKE3^R z+h zmp&Da2YuJjKiP7hXPH17s6Qr8p82BRY~Y-DW}yrILzF-9hvD^}848~CQ*cg#ypu=r zjWfxE;VC&iGnsf|A4mGuw915n8tfs+w5`o?la@<*CF_+bTnozcWuWmn!i2vtp!CT; z2W244i;+GWw&4|H9*fLJ?8yNAhx}Pw1LrmvU3ix^YZw;FJ{<19!uND25*Er^BlCC5 zwasZ${!h!f8ZhMi$2}tM-IC6c^R(B}xp4>>qd4xUJ5urR>EUdb^*QgdzFa3o&=_#~ z^c%;UyhJ`C-tg5)KUBW)soXP$zs4T=34B}Q;FE+b_4}wS9^fMRk+Oig$-A4N$GP`$ zs__-hk$NxsfwejI3cUCoXWErt@p;saYkUQCgnj%*4Se)i_qMZ6Jl^AQz8}2x#^Q=L z__UV7PYwEGPv{8YnX+V;vN*0e5ocy&upgHQc>#Zm0b|$K&z}IdaeZ~oGkRiS@IlG* z@Mkk-Yw1{-)YD8K zNNrM#d8QP1T)+ptCMgEzNMmN#gko@p6!%Ir#mMuch-tIcw8r54Xp9YYaDFrf=SO1- zcbCRwah&MqsW}#t;%<9yCif<5EipJ#8iO;XG4Wo@`B7Zc5JrX0HDJdfzI!caitzqU z7UjX`yB~Z1xiw3L75jeZ)2VywKEn7;ZBEgv4~pEKJck;i&Y@Zi=sRPiDcb|q)EpNY^H_xT7;&SN>! zm$-4<5&Oe;r2*>`^p|OON_mCP%Ea+7 ze3_524Z;rdOQf8dI^ihFpXazaea}{MAEJ-;;PWu;E7p<5$1n8LvSVaFEsv6x$$&j@ z{W!pX&<@{g&o@lFhG*~vF8+}|I`ktBt zJ-c{kMK{kwy-%Hz10JsUK#0sI7EDsQN{D3*&<(H=CaskGHJ7xYEaD8c#j`7>GvD}R zv8;>!_0?Z_!UGsymdo^DKOV3{;5nsJVze-Bo}I@0FfP#hLEteP<$!krViPQHn$yKx1oM)ZiD3&_B#{4N%{pO%mxuuJh&c1gXOrw~2T<7xvc#>n&Zr0kM1wzKc4W%rJ%p~2+{#pRCOPQyk2ev{!XOdkNj7_D||1)yBE*7;E#i0x&fzq*@m1W z5bK0CUHlE1??TyysFRH6B0S^pd>r@}fd6BmYit#5oM(Er<97_wJ4ti$1=`P*yzq>q zQQXooKLb6C^hwV|(&zH%AI&i7^Bm}Nm#l}qQ)3_bQjO<{3no3jmv4OB8oF2QC78CK zz`g+X91M+j7qCC{4SRMWCQ|Z3+oFNI@bQGDl^gtOY%VoDb6+3+W{i@>3qJ48@q}ds zynfe$A7nzufM3G$x03P#S_IiY&MU!%LLjEa!}IIpU+R#+Jq0|wE@L=Qb~}8L*Rs5{ zcYn%#g`<$oOD6}VEmnAcv@?vi@~O5hA`KW@2R?h~_pv*emV04!JMX=Yzue z4cHXQC(80HtL#lf#vOA0#W+b@%WFA#FJTO{*9~vjZVS(Av>8HE-z%3s(Orm*!JMV^ zV0fQ@!A8aj)adWwE{d>TW()@X=0`5z9xM^Wp9_24%03opkhx<)lm~CX_UOH&mC4wD zWvh+Ao=uylpzb%=(+cm!TcqE%&({n3^Q;-y@aN?GT=`x(#x92O9^jo9`qv9aMkpC} z1m|$v|rH| zxwa4E<^*1nzP<&&1a1*NHG{DF)@zJ8;#isRk{PQ5eX~>TIrO}$__60Dvkm$-cyEd9 zzib1xHTt~+_Iv``k?SS2Ywi~sBFqyC*B+ix?5m4<`%otyxJzG0|C*(zWKg{c;d*^E z_boVxx_xUVz!=~|gWO&U*CC#0s}1E7P;P;Qf%TVKdv!}cMPylNUm@-Tbv`p`>>5<3 zh4`6u&J(^_$HBF;S!dXwI(>FvBp(3JcNA^AXGG$Fw!45YbZ-x0Yr56?4(oHm94I&4 z8Q@jg*pwg1r$g|F^sRBuW?KX6OMd9%Z?6C7+a>RsvX-dUamGklU+!}mYli0AieK8_Q0T<2l>M;e=tOyk*Jg2F9%m7pOLrB zcACO<6i+{R6Ut`*_L_mbcG)oyze{Jr^&;?NU1vmF1L@gMn+b|PFKrFOP;Ebwg8Er# zdo*DV07v(x{tg88klIg zkoxqId{du#2g1EXd6G18wjjPR^%^-RuH1_I_{Qd=9fS8d3^;ed@&VjkOCG8X3H!uh z@*c7T%Cxpy&8fN1aP5VA@y24l!n^`}T?)@HUyY<0Xf+#SHWPCv?2s>aDxU}K*)O*N zhK+I?LL0<^_y>X^2Weu;jRWPXn9sSD_5ky}me2e2q8bNdmg$d6pr0DB`gADu7FMfQn04gECtF%8B*s~CLCXC;y40EcJQ zBm1=;ZI^)7(jMsVf4B6}MV2-DIT!f;QuXso@=pYg7Y>?ZYy&QP2DW=)O(ZP9coXf; zN4wV!g0Yh0iMoBVO?ytt(t5^G7z<$I<8GBlev9{2lrzMUz}JKLVa!$V5zUu&c_YWY z=Qikd%DbKN6vgV83{-jXVzD!FjX=9oah?ekq){`^8$ut7Gr- z5b>ujaPNHNV|TK6*nL8j(?1Oz6R#(~CHLGx-)xk9mv>y6=S0EBJro&gY6cMxlSx-_#lD#KCL%bS7xl!ZF@O|5poq zc^FU7Zo2jy==Lgf?(aaeOKyK4VH)nEqOISZ#Ie`@0cV2P-!|dLn18O2^4!xgI(JGwKZCQ{n59Yvy#qdMu z7rYY@vrN0G5Hy(1xbjT2$2B|ogf^+RQ1W*iX@I^3QP-tk^@g8k7xn4C^^h`xi-rcITw0{cNRmQzgWO_yq33c_Y52qSSy0&iS9<8nZTJ?%YoO>Ub@l~ z9d_2`7&v{8UNg1vnwKC8ANY2jy-A&8H4aQYs&pv!!#a6?Hp{xf|7ZvDZD z`k`}v_=0A#4}{CJpw4w%|IIyY#uUrALbXOe(W2+gfIs5w1;DwMw8V25&#ppdddRzO z4|wt@{4tGy{eAF&#Q)!MMiu>n>_Z@b*dDXqRD~ z^gQIL0edSymS@Yu=bO@qC*v>C|Irdh#9R_TeX7JQN%jM2$j=y%ugiQ3^O5%h?Bk*z zDlV64at`r5%Cj24qw^5*WE=$No%ufTZQeaZc?#c61fGkSx7Qyd49Fkss7HU^KUa0u zupD?z!EY2JoRyERV;f$}f694{aRsuxxhGHCAgkn>mr8tRZ&JSUE7B*a@SCsgY_)E8 zW6y!U>Hs!I)X!C=Ua_!-<3haB^ zT!~ic3+Me7fggKcp0q;F842T8h)j%Cd|*bdyq6I75uONSVSf|jyJm)}vn|Ue^s>YL z>KV`zs$M$lIm_r1wk-Tvhp^zxbsFF#;M_o(*YZB@n3nKu&v&ElT+}t!X7Fzo!k3&0 z8MHw5PiG6>O;Gx9f4)czc)c&f4{r6Ww>VHXjl99~&PJ=rOYc1`c`qpDkhh-ijy3c; z^4M<=X^Z4s8=tvISf0NC-du${PcdH9xs(ygOuq5iNyhIoUGdl7opsGnG51TSdMDQ2 z$2h^yT3LoZ%5a{xPUWGU*R)3|3tLh(KijiRSH8?%oT;VXJk}b-c=4=z2xAyJX5At6 zv&Gu7EXlfOS*~^8vQq2Y%R<%z%bKk3FKe|PTz1TQq{3o7T9ITuQITtHuPC*it_WE> zE1In5D_X5x7~^hd(lE&OKW~TshjO@(G{JLUCS)+PMB11Ml0OvhwKz+y*suB^JQj_a zEhqJ+4HLy`_kUm{ciLhc`3n= zwH|H7nC*--ZH;)&FOY>CuZT%TnMRbkN|pKT8CeGRbGqk%w>X#a-X6<=k@76Q<=ryg zDFqt8jd~j;JtR$ktLzw@^>oj%vF&}x-x86(%7mM+bmPC6F#lsG?^i+D7g5f()LMbP zMUKyJd4_@ZV*-7zs{Q|{RsL8bC}IBm(PL)WY_m@Zlqs|9JT?Csh3MnWTw|_rK`)1_ zHqC#7-HkE#TK*#!@e3BchdQD7io<$t&wSUo#a`hF^>LRX8fZE0q@_lTVR|eS+}4a=fno^(yw4! z|0iv)o@D;ceb(@TKir;Y4F|?$bp$_4-cY>!?pfV=nf5kl-?Pu;ku1^gu2n9=hg>^HjT!==8gx6I*p7Vtg-`&?{tJ9Z_Du)((Z?@SubYFc6Vc6678Ygzsxr_ZBlIlF6<@cZ~pk; z=y5m`Tm3Usd>EHKQ54&I}!8%Tw@{Zc`N#Dp$) zS0Cm6U1A0L2!86(Uq59p-U+(zK4ph*KF+;F!g~7;x7AxU1gvdC!IJGWR$#4h065Cn z2q(sd_CWXv-pBq?<92NW?yK86?*-;D4ST@b!?6+O?;V4FVlSDR;x=i;|jHwp^m{*gLw{1j-9^UkoTX}^m2wUXa<2@mX`42}=i zju@K!w=xbvIU@|_5>zt{80scS7yYcku-$UpB&gNcH{=EC1@}Ik1 zxy4Lm!gY^3E*8AH?zeBk-%Ceg(cRymzX#1mYM}xm1+H z9)NMs)V)W-Hlpd*rQgjCot{G4I2(au5cZ3&{9p2o^&ON?^dnyJ?MKt|jdjOpx1kJl z_1o8~`Q11=V6DUb4*2E(H(9P3Ump*Af3x58FJ`$n5WcWt%&dh=K@03#1rqPFkFUZR zmz^dazVbb2zl>u@or`uC01wk=ab!#5I>4lZHEWU#;yT?}o##+qbz^?@F zvQNJ9gMh`g?-otJ%g#Oprp}s(GwN~rrceEdN3n8_s>4{Te!f+t?d&Wd49#~6_UT(= zDUX5F-y;r6SetO}mNxu0JOT6J&=?^WjMMKR+(|2Dejnplh%_svv6!N9>b^Fd2ZOC8 zvZ|+vtH1}aMMXMzD}Dhc`bkxwe*@0HPZQL;%(FL*5#!2m_B)U&GKx|UEhS!*r7)dI zTkhj={lfW)G@|Vt)DBxQhE}caRfU>uh8 zN%i4sp1s&`LZlTNDH&=VyS6s?kw}DGb(8lfx52<+t9Im8(7p9;zRPt2?`5&HwCWXI z0{xNeP4?UAd&~=5b0x0Q7yaQ%v|GTj;M!zotC%l+7*DA6t?3(k#oY@!ajn}VyxRLy z{2W*OLDr8iQft~BJ$0>OCg+oyrH6UnLZQ3GFx`_Z2jr`G-T z`!QG8Bt5YXz&9OU!*6TE@2$YInd^101_ z9N=X+c4lAUS6EjB+6J8=!tZUu__O?cj(t|6(b?1r9g@{-#hpgNxyd4&Z(=^({;6Nv zI!@lVPCVTukZJtsKWE&Re zAN$C-m3CLmbdzUrX1!r?KW$s?p7fkaTj>jYegX8ljc20|k>xpD7|Z{bMlY^04OeX~8V`&#hVh(ElekCJcnZJ+vAxkZ+1+~c>J zeJ^x@7H-$$vonA9ctz%`k8jSrNyKE{^>_{NEzqkEfQ~WqgU^TWD|!5dd}HlP%GVkD zBJ@ZG$9ew>kgCFe9-3oB86 z8@`poGWZUS*YbKP;6pA8sAHwxA$+Ir@wxfNb00z9r0{&LzT^fw@AgC5{?JIoS%aU0 zTC){(;{Xq5Iy?nMc`-PHfEfHODS-RNN#t4e27Du@9`QY>!}Id`iXiQzG!?`1T!8gM ziOp8l4cIyhSO#USJX5{xH}LbyHR*FjZhUVlb27)ZyE5c7d3+bl7C-7k_XSt;yaU?D z9X9yJ-rBGXNxdtsI*B@VDMxL2cEWNeNLVj2E{f}#sU_03>*l(~z2u2>_<>TKB^Kzn zXHN!yDdMs{fAO8tyymNE`!N0-@tVTrK@8_K%=sRE$4=NVCy*}Si;EpB2VK*R-!Z`# z*30h`A^lzQGty#%Th}kd?^f`xm-8RjAgGV;Fy_d6A%6}9pdE8640Kl<}O zuKmO5#X9U8<2^g(Bsf+JIWacQK!~ysA~4f|v=*%xzGSa$owLf@M? zfiXAwo1qV2ry%C#-9n7z;5z2HN269N>0ulf%cVt@gPvLx#wQ?dlHcM19qo*#XF6#t zzsG}d42+mDk$z^=w(V;RFedlINc^K=unuv_7K$EOCLg+N>k*^$g!CKN41o*r$Ttox zhF$Rm>o_YvAirS^a;O7oXI0vl1xWhXw{b?6gBCP{yFGZy7L)zy^ zqeeV6~7c-E4g?vuhAb&3V6z&Us!|a0^!-IRX|G?Pu9I~>rKany8 z{gJ@2z}{M4Y$4*L&AqTs)Lz(-W!mNbIQF8hGwsfhfV*zbiF_Ic`2sYmerjl-bL$~#CGwfFRQpX3|=d4}{LOqW31 ztGRc?GP2LUz@4P4)PaNeG0cmAv1L%Z*MGI$I(4oBa?0^Q9U0>a8*@C~C$oL*S;h6Y zyYTz{`!E0L%{QKMxUBH?Lbp<%2ZqOP#y6P~ipIQ|Q9~QUoF?U@kJ?>If0Zsdr_)2hdwmO&5rUi?4?&ay0lFxU4@l654Xvh zgSz&@t#9TOzAzp2f9Nk7i}Gk!n>)^Keh*O4?!q@b4caL@ryOpd^!y`qOaEH02|8Ns z!|iAPasOT{$1LCYAeX-}ZjhPz*w_4EGJmVFd*m1S#_xCzXO*l2eTVhgY@~7B9)do; zBdY~>XJ8#jdl+T9oo_a$JALO{u!lJE;j;60O{zY76Zm^@{LpypB@c=p`k;nBO=S;U z_cYg(Tt~zH+Nb89-*@0%IO+F9ntAv0UoK!d{N9)idVsfJ+hYELA7HRvBOif(9q^CD zfsZ6zc8Anj9detTZ>-pgJ9-l655XKqKM~5l#j{N~Q-HHA^pjPQLN;dqY1Far9frI+*JNsqF>&N1X+AZ{Y06RMh1?Qt)peKfVrgDc<85KU86` zzENe~t!8(PfdLKw6YUt{f;KGVxzHVeJyyY?KZ@UPg$-|qZAhC<#xAg&{Jj%)=nDE` zjMKqQ68DnQzxkK=K;LZYPNj&Zg#N9dL5?tWZ zWM_*Q2Dz|k`!PSzVMCfcwLy%+l*sc-oiTNTK17I5vX$BF|> zyYc>pVPqb`TKxgY$TPwd$iY2bQ{TdO&sv=N8muqSCeOzNzK1=A>Vxc0y89@Y4ESIy zJk(DrcIr3j`Y*hmrN-;>TWYMMf2YgIJC=EWzS66`uPIx_*n_r`<=no@cm;9(HpEQB zXN0@>?}dEd`$O2v#zH;sw|=Y%U|$uWi7Mmxq9F;7n=fky;+2Yex8 z%8<_a9z3{z_JB5XVXv@F$TIB~z59yUZqHuW&-8cCajSM$6MwxyuHB|%-3Q*iUGXl{ zK;Mcy*pRaQpEbeH#`bN%!y3wh9L)e8Wuy_l?~QkUFFQ0xu5&1FTl8J0>|CRmb4nW4 zwX!X&CDQ1hC5>et;S1-Q7yJ(%|5-NsM4QxW*=NRD7(2rK+YKE>zYz5j{omYMm9&M= zlrrj?0o}}Vg~;Q5nzUK8-LocYAJa}aKs!OjCLrHgc^Kb$SU3|pKUwMn)?sSGl^O~{N2*dufD^4-p0Ml;7;&EHpYJ^=A}(}cFI!miTF(L zKr>;w#u^56P(1KV<2QdjIA`ZP3_SsOfL)+qRh>1T#|TsTC&Oo)7Cr;~I6q_*{}u=BYK3;-sl8=;buaw`3tk~V`#GRRtx5}M3W9c;4boBWO z`02V`4$ywn;S`RG^Cadv-iM4ko}8PYOJ%%U&oTN$E85^&TicwGZ~P3?`4!`#RU836L8Y^TyM?3pNsKvSjc`s?eS^3`$h8_|jN`XO*#P`bcLX?1LKcKYTyD zmj(1+c7ZY?+lCGfZh+24UJzx1C=WhQQ}aO=zhxnE8xR`{{%3iWe)%?x$Gh;$nQ??& z$HMtC4hijn&yOhjEdpJtx!;k_y6~U0aDGDHJNtN!x|#SAPxP61YlopTF1s-{%VrQ) z>NDUO2i(&8@#qT2Sd^gsxc>A@tAHQ6SO)_olbmBKBC-&u!m`Ne=M%nb#VaajBI)v_S&$9 z*4qv`O&XW8o_X)*Y}bA9JVQpkFdH_k&25lom1lU5mF7F>#QRCkD^BoQ@CC@+CZo%1 z**1e|xHnML;#u=kIZv7M;jcZEo1jnXwHnkPNtiMXxUmhSJskmmd0@;J@?0+W03kbx zqz~!o^ew7@%&fs2NS@zF+rYWY=prt(^J?*%a$Gyc=&E~zd+W{H>MqN67ly}N`hnpO zqAywLnqK$~Aa`7^A&tC`HHL%y{mVGK_zM?F+OPwp{xarGr7cqI^?ZD}y!FFb`VoBVnD>?qcL@cL-`^lS{mpX{CgBaCgR1)A=9;n$8J4nvHXs}O z>7$fS$V|SmbWwy%bH6aHUv|&mX3E1H$f%qHl+L-pGqh5kft%3(1~ScWgh+WlUm7kO zAJ2Ptr!(ZGk>!Ti8cNTNzwCAeN2ol(K#Oz7M56 zq3`$(==xvs-{sqQ%R zfyS}z8U>uvX(t1(WZ0rs%!eMxM?84Yt3Q`Oo#TN_C+I7%E?5WK1oxh!4H1NYfZrJf zznxWM65K>wz@PIYWyf5X71G8w-=!^HUiK~A?}T{!5NKf9hmuEVmm`kYgL{Lu>hb=^ zQom(zjqf(TWfZrGn~Qhi&k#3Hs>d1#>kB0p!O7HJ{N9(88^#h#JFIsNpONXn_YRJO z>hn%dAFmP*oE0S;iHFqVOAbjpYnOPR^P(IR#P=#bJG;izQF4u<#&$<%kaJIenMIO+ zckvF6B~L^DU_R%%!8iaO5}bo*r-sYx4YX~#E|HIh%17P=eeH4o6Z<7i;(3>lzO}u8 zba|pfro~A*fj-U_tZk`7`f2fWam6s~UC_U36y_U0?HS*Ic?El1)Dv>O+8EDvEz)m+ z^$*%x5XOaL6p1g+Ycx%2;3gH~jCUDW!(cobjQQA0Ij@zC@rUWk@!L(ALOJR}(iY_M@qcEVx61Y63_sBRfQ=UHGq_&)Q07_Pk?sD z@9l`;;Ms~~@*CHoZUgkf*dq2mN%Df!H-UG;bVjU{U+%w1-s>JMd5?O@9EW^E+fRHo zjjbr%2w+??I0oQ1oF~Kj32P_vsFWqO z&It6s8#Cm)92dg^TBLg|uMuu%wgYo7*Q;gCPQE)!Yy$T%Ww;zRdG0#Ac0hZKk>MZx zP>GP|ZCoR;Jl;3q9b+Tk*PuVVOOJMW#63`7yomPt^*+X1*_h#T39TQn|6a@U_sV&T za@8QZVe`xNIM(7biHnS%E}_q{^&_mC(%efBn-3l0Y(b1a`rWthbQt?iKWRoBc*7ZI zqso)>)rXAHXqYvrB1S-e)b_=+(FI>4cqkkee^3ON*LHPWj@kJ zN_oe<7Shf|3<+_OHeuVZ8I!Nh3LuUC0{t+33oRFz&vJcxZsB~l%>O{qV^JH*?pN)s zo50w7ju+$ey_Wwg$n_A&1OB17H?N zImdTtDd&V~o=17bjj_u&cFS{}ZqVOrS);}ul4ez;J0MF zuXqgpA;Q;Y7ap}N&3UDhhf6tQsiBeJEwX9z(>4Ug6=&d=-hmBVP zIh=ssC0KvV(c9pMsU*)`=TfoRMxl%gQT1JBeFy8w`herLJa;CPgx~OCV+`Jw@dm2A zT&IG5+gYCLB(~wTY*>SG(l%sY6i=Ib%#D$I%sXF_d(7E`_L%Pt?=kn35g+XB&&R$n z;$-Bw#^e3l99Nu^D8M`K?epT?$aK8h)ficvh!K#npHl^B*-IZN#^@f5kqcv#fHBGu zG3Gw=!cdMGCj(i>d?VnCQF{h>Qsx74uX(41eNkhw1N(uG@{CUg)98PrO#N~D&O5e~ zeroI*_nY+2fDOuhWyX2Lsr}>ii};T8c&*~Qp!;7|a~~Q0m=w@Km-@6X?^@9!!`B-QQl6clAH;TSdtK z7W7AA;e#K%QdqX*LGFze`abLBK6rQy&1ZN{;(WS0iMEGvuiF9Nqga8>%oyEl?rFKeKh8JNkDGxrcm#3Ai2H8ub)Ldr#|rUR z*f01C?US3z3oj=)^=pob+~-6U=P25o&~skMtW8@XeZ*I5TXCQ3XwS=fT30$_!ZdtJ z4Qm7&eFvUZBCF;F_#5y&0{StqpK?&Ee9w+=jaYO&;f zHu-&&r#x##Tr=vTZ+lj&=MFWd+Y&$m4g?jE=92?^AMrrCwCZbzHsBf z!MHfR3TsBJ%j#C3ZXRQN5T9KIpHYKxpyokI=hJ|B)_ou5&MLw?W!GoKZic@124;L{ zzujZJ*d*c1mT;Q!4miBSs$H)VgmZM838z|?OJOJ_pa&RQ6!QFcDQ8_lxk%`t5s zFqT;BA+BeU8q4klYV5k_sj=(6SMhdt5zBmyJO<72lHbC@evLf;%`u`qgl7q8p~lJv znpESP8I;`-7^^IdRezcj@@y;K9G{6iE5-ol37|`n&+*}2Q}GsCmb2aHC0w^!^O|Gy zawY0WSk5Y=y>Pi4qcrC(&~*5%_y&*McekUAO~L~#j?v2ba!lG?Hsfrd>=a_t5r>F5 zNXMQe#voDE=Qn&%-(&1~Wxk|+m-bN7%RK7_`u=a;-aS6*>fHDLev<$JgT^+tL4r-t$R;;?QTe`Is2(5Ue zt=+n-9<5wL3l+Rns8E3pRJSwx|2I`}+McuUX%9zdY-?t!F*!81)!lR(Raf zsk=rQ`~iPnmH3ar((iKyg*|H5nVP@A=65Ey%sTRc@&|x@=)w5F?l~`4C4Rb>yS^X7 zD`zN;_!{C~A?|JQ(_UPZxaW!6PF%JZhaTsr>xmN&p5w(mOI#~)Yl+MB;&u=hA+DLY zT;dM<>vCS@dupFt{8M`_Uw0RM`RNp!=ZnvgXS9DUa+i@AetNrC-(m8=ld7JxfU^^W z$&=~jxyt6b8$IYx2iowPK73vr^y0ZQv*|EABY5Pv$Lror`lel8+%_-Y7~*!IPs-vQ zS6{J{Hl1`wx|W){m_vMaD{v2v%p{znF#kpu6LOw6Z%ZgYd3itb`69ux_npS~5byjn zXyA99lhiklLZ>4=m3*~jFW+GPd0z;;Q0EK?*PSz$_{?zK;WrhherkQObr>7iefO1B ziOmoK&CeOU+_h) z=H#o;KmAkQ7jl0*Dv2Yuq6ZKzkbYLUUuVQ2chz3-gly7Z6<>tCLZH5Y{0C(waZX`$MUtcGlku6SI@G@A z&+=v)j_6-w47opOW)8Whs`CnXu-)dI2fqqtjH{Syqx+!0#@|v)mTNV0-c;IG#oQ;SF$$pd1CY~A@_0Kt~K+^nU}e{^r<-(94HUvL`TpGJudqj za8ja{-@asf=xMYE>>a8$?7V4T>mZp~GBkM~qa5d1i6UF4(f=K?b^ho`vYu1>9kuU5 zoBF~H_T7+$8W(k{Zt9Csug{Jn4lU?=x0yAXtoppZ)ywvWyi=+Y-y9Bp*FINx2>A;6 z&fvQcT@8KwWu)8*&V?mRT?MmcQ=q73DS3&HUtGc@R8T;|wbI**m!KhAw=y^Wl^FSdSe&0bv1ensaZn#y|2(3l#FHWsM; zM*2qkKh-=oqPM%(b$s{cI|cXRf8<{LkKEhnjJmfS+lOtA|6cr$Ab$msy~Zu9bV3Vj zok^T4Pv#t}nzfkDI^Tfpm)_KY9{*dZp&97?{SV+j)!3^4TkS)o^uLW?$~fScUO}!J zB;J_*z6#ItEZ-U2<%kh3AdFwv^y!sr(>n;`YdO8GxWmi|{C@gxi0fauE@M>Xnv6Wc z*qXbK@jo!K6Q1+sJ9Hn+hc4mO#;*hK-E(=1>;Sx0nm@XDuA}+6ba&MoM~wa|wqw7#UAP%qp7tdD8NxUZ z(YG*R>_y@)UcAcSXJ1NxhFO0I+>>>&d?R;LlnMNx@ma6WwP@Q>@nf-c22tb zaVFCZ#Z``Im zyR4jh;`Rd!xZ+<7UoY4Be8e@--MUM!9~EY1T2 zVSnIi?n$2oZYt#;ovw2Q>23U@8;W=K!N>6r1smGy$mInM=?hIcyC>Y=2)0x|=S8#R z3j&*_b9wvp$&0~PLH4lOuiC4d-Bram(Jru}{V8~8tpxY8Kf~dxfr*y4qc`A>0{Cep zK62AFYR9#E^!O~`VY=OG$3C<^A-Y~n9b0fsVzI`iHdHVDS=LuiVm9~rGQw*(3(;8k zh1_LNg`?nxo-Vax-$gDxZulwm;;!z{NFSm539b3+=d0)oTgEmHwQ@lu^W~>kpJT>- zqwV9d?TzG%l*`Vsr&{+1(F4C4j^<9LFB{nJxfXvuk#G0dvEjTEqqJq@pR2tL%q{k> zO2qAc{p&?qgTPyYjbpPczk6(hE&BX)pu2Omm*`6QyKeEqotB4}o>}`L z`X+A@q4RIp{_m{$noa&TcwEUT_%36f1e+irG{OstyW0QgTtVqSjZ&Gg5$S(prv_tv4 z=Eofc^beKi0F$%F?`@8rJ@O;tkS1lnfBt>gjdwmQ1vC*}Yqn_vU&wcxBtY zva78>nL~=6FkV1M?YqD<@jCBWF2lv79vlj_afAHmaYq z8%xox+{E}Vnee9Z-_1NlCS0TY)LGyTm%e9V7wz-Ll8YWWvtj?wbq_c@yi#|WmK=<3 z{uyUgKQXqN^7#XvZX$m=cpp2L_y@qnJmDe!pH0FZYv~H6W#2~s*9-3Yc6me%8XswBi-v6ZfbrzLSoN2lnFL=lh^!OX4`o?A1Nj z6#LQ~mtEKT z0(?ZYGw+2JoyHE9^!PTV85tOV(fHSBCGIt~>3`Vj;>?=hm#z<{9-ZygR~T~doj`uw zAv|U6;!98MTWleC82rPYny$XtIwDuo$7SppaI|=Yq;HJ+>X6$gI|OqN<7BvqXMMW8 zAI`2yJSN+NQ~L3nNxW0@s^qq<`Dy0PPM}l!C2b6FIeK{EVc zG0B_VhXZTgv2&|?Cw2As?s;q`@jdtpln!QuDIeKZqs3NW*&XJ!REhys(4!M`N}Pfi9Jf$fxP9zdD{=d9*)QaM}$2bA)VU@izA-Q z_0pva?C{c4bb&uUNw0z}(|E}(^y#UPd+o}QJ90wE{nn0X`pT4X8@fG=9ElD17^R|pa&0~w3F|V~T z1Kj;+qvyvj*zCO_g#0=VUBRnUC-VNxea=95IPOj2Z(!%Y*NdNI?ZeiO!I};1hY{cm z>CRK<`4j77Y%M&}Bik+S{K#f_*ZG|Dc{F^^*pRz$yZBl0ZmJg?UeH|Li}!N$w+((@ml)Gq-?~Jh zv9m(QB^m@wj%v*3Ewv!L_8xpG4Ltm2>rI>i-2}f{bm$AM=Ndb;Y^@nEcYnOaXX$9+W)N2!)K8;ux}jPhsc+`iQxx2ZNHrA zhJq#dVRZZG!;{$$Q{==<;fAALx9Zc0SKq z)JHGCpQP@mpc7)P_cdSO)Q3L~Jw5kbbddL=2ge`P)S~|7tBIHGHuc3?D>L$1-HcrN zbg+?dBjHH#WZsc8cMdzKKa;X~4da%p{TTU1d%R3$bo%e=8{*Fy9~=!U+jAH*{$XAP ze^`ATb1bvrQjD*Q4H?eJp~mlacVNLgEfo$v z7=hV98}iuRjLedsOP5#Q9(}OQVXS#}-(NEXcs#be-PX^TJM`XFbi;Ea!|M*d8D1El zi@d+muHV|*3D30acf8oH-|^9Q{nkz;Z9=S8_B4mz4A&j{0y595T0_B{U`_DHp3Q7| zm^($t2ZFuePkI(pZY+LO_M*4gZ~Si>AERs7C&!M5?|@Hu#CiSxD89Ok4kNONbH?H1-t-v)>=qgZ~FbPcrEejKLAI3vMC0wgI71W`qRAn4?4WLX$ZM@ zG0smGDgVj*^e1zni8?PG1dsF^e}~c8Oa=!Rg7?5tkGx(M{O9mB;HlG_#hajOm+#|c zm-8>XlV(Rg?c^8G*lncvf-^GF)@{&ILkkVRxZ2b4wCen$vFM#AfjibX6a)rEt9_!) zW}KAMSo>&f!q1q5=X=k$Ox>KNC3zpUL;6_NExxmO!aI-04)Xhzy}u-#V(z4 zgC6lwZ13&eA#;|czJFTB6z>=@i2JtKXrn*g>0fN_-nuR8xD!1f`1I<2rcb@H|H*xd zd+$=m+yBXZ8u7RKw3s{Y;U85>|M{%i#24I?@DAOnFswdu_TP=4xyON1WDW6xJ#Q0+ zckFqs4_4>DQk7U)to4XByz_W+Jv2vt!#`fVQEm8lyz_V?bIram`xYFQEx$v8LCNQB z2c5L)cYT+_myA+BRKK-3nZMVr-IQU;byvMwbCqlCuRH1`@)cm}GJct`nYs!%0ru+i zix_L&kdyqggcqGxZ`OcwbM}GJd#a6)JHLm%ehdt7W_ynt-hdC#CuTko-Tmdc@)ZdF zh_0@H5883XT>jI9iyqn|eAY8?Rg^Uce}>%qq+i@9T%-P@EA(j|ksYzMeCKi6sji?6 zaA;DzAntD}n$yV;eTz}6{{P#Uh`UY^(VKeVdCCIAxoR061 z!GYp?Mh0^4xvu1sd#)~d^PXGfCr)kS|IItYiW?iq%FWJX_zB$`@@ymAplCt3^fdOv z*R~;}XRx;Ho%k+OzSP=1@IBtp;LX@08{Gc2mC)FIoFisgyxTn(eCWy-9(u2h9$ub{ zO;(fW-J?65G@St~e;E6x-M&k(4>-g==1yl~hCeKr>RM#=d0i1hKaBm{{Re@~ae^U_ z59JIhG4IiJ)K!y}J#CHel8e`5oxzz2KIHybFFHw|=VDKhvAP^MXmhgeXYYxJxu0`M ze%Pekxjj1T%X1&34z))<)~#zwbj@4ND(o2S)H|5G?-?hJN{}Z>{eV&-5z0X?I@-JEbI3wqE{max=QZI5cOg%6YL<`ye{;8FF5` zmGfE;pW3&+^<3U@YF*&GwL!%eJ)4iW+da zq3>y90t@EAFRoZAnMH40==_SkIbQOo7cOpwSrHH5@rF$)GVlQSh-}Hg(b8Y*?5EW7 zv&%gBxBK~1;7{@|xb=I;zso)Oce&)>e^A!FZKY&gW9tbXu>G62+Vs`f3tqWe^T)hP z-o-9M`oq>`;#a)2?Z=~>cG41D9#iwl!jaUwTm>Rk&K6N7Dt;h!FLf3E7y%gsI;S+F1s5l58#aW$r z!sZE1#?%mPET)Zh?l|-sXYgN&Y?h79=|1x3fb(;D`++R1esGS`En5c8YjdNk-ohwh z(unBwKg+4y9N#x>T*%W0cDZ(LTNZJK`I?>E6YH4==Jdo`JGU*f2ycR>;rliIc;8&c z*YwRL=SB~8IN_f%KU)@QZs*b`Z;s;gm;*B((arxQ-OmT2C!+CHiSf1$zJUkmFF3KiD)Ifn#Ek~Fq(>dYyyf=FGn{8yx_aLmR>yICzgL#KNs?!l zykcQ~{4m;XW8N|Y4z#Xw(uk66?n@0Pbj}t(1b!6HKhXc~o}kC`hk!$!t-$j?(CMY4 zH{G+;OHa|8KG0G2Poha)J*C{k&#dMx65UG(*Nh(#cKSbwF156}X}}h2p|iqG6LS{N zVBZt3w~v!&Hp=sN%Dxce-nH)RgkxVebyhF5KF7p!hMU0Q6DU6s`64#ewpp~X)Rd{o zGvEJUJH35e^_cDBKUnUCXd9*q599jo2cWGk+V5UU-&py6X2+H#kf0%$a)qP51o1D$#P4^2+}W`E!c% zSp)oZQO{_TubuBbXXp5vW>iSTR7pQGQC%N%e+ zrm0{2XrAhn&4BLnhj-wc;D;C1+d38up`Ej+TmE{E_}80up0xGdCEUJ|x{efQE=N8| zJnOYTskeRGUMSgq_}2Jb#$?)z?!LTU@ z8jvwahsMkzy-n$X7?nZB}T$xnoAanQ-u(8oFS<+*>PkB#}32Y}w{9jUoy z?ARw()CA4gQpXT-XFk>2k5Bb~W@~TSr}{th&fc`X{-^czf96!{=OPiL@7OK58K&>d zS<3vHzM1)@Z=af9)`I>G?rrB&{hM*PH|4c3fwY}r0e*!-m7#K;{xx^KcKv=1&)IarF88%e7kxtw)M;Q{sr+F;&q5m zS9~LRXBNehWu#-zwsAodM?UWNCSK$}P5B9Hp9jrZzw5M#2iJVC*Wt15s||F!d4W84 zjMg%seH7%rExdGKD|{O?v~r!(#C?c7;cmk9({^w!xXo$dj>=-_+S2IPb-y@)uQBEm z-xlzAE;{3uX!kLEfkVT#IZMM`SLZ|ugZ1pK#Z!wqPM1%|*iIM!M_*yByXqqW-sFH! z&OgTd6;~wj(I}Jow351y|Fcd*+Tz(}F4sOWzg!>j=5^l1F># zF#Rw9#+?c>p1 z`0qe|J^F@Vj=V*&Psyc6SM-iIA3v|V`j;EOjliw=X)|8#oQAst^^vnLua6YV?((ns z(+)C!$dS5xZSpm^8IsXRYwzR3j~*}fC3=c4m;Vgo19Z*YZ?d*9$Iz?(z+EPDme+Va zw2||?8unkQaB!P*270b+(M0&;7~gLA)B3RLmIk+!^Wx0#FTYZ+ z`UCIL4u1P0`0|V%aQN{;4pkHwl=0c#vY#SkI@eN zRQPDX_*9EjUjx3GnLjNtz3|!t!I9C#<@MKYiJcRTL&L~>%bu9IKhK+&t$e42znxtB zBaOxXqPO}o%JJ9RgY2Cb=wR>C4zlPW;Sc-f>Ya~=Zy&0<23eRnGJ){G>RG=iudeT3 z-kP7bSn>C{X-n|Ge7NEKCG`zgEH{2|WwS0jWBe))q0H%`pSF&VcgV(7`y8GnN51VV zkp-(~^&izbhCWayG^KxV%b*Tx8$U?=!p99aKLoFI^EG~eOmk4uiM@+^`;-e?oc?+{ zV)-^VGq`ThK9$i}kgp4*tMhj{r=g=;WzKp$9Ub^m_l)Xg6Nx=zq2*D(Q)FmD8}fD2 zf z^WB=M6Q7Rj96{$MWkGcF-`rR_wP?VW$a%mxbGW40nJvGM%;Qzd8{HY<(Vo6{bXvHC zwJZrTu10rW@ewC9mHp9hBffmcIFp2nB4rbE?w^4FnEbRbb(XW2xwFAJ(CeJ9@aAN+ z@YnTP8}Z`}iylpRtF?RP)Q^NO)9{y7KT_@c8`I_?!wzoDcjr-0y7rFF#Po}{v^n@% zjM1)Sf5)_aNMcpHj#+#UY^hCK>V03w_aMGoyzfPXS5$t_sgB*_Wa`dN zxz9USuSt9?no_djot-74XPjShb=|ckcg(-Da$V+g_=p+S(DZ1%z3&muw|77m4D_!+ zMz4E_bqnh)@{c3i@8Fzsf!Ra&Uc1V~?dROPX~7J^5OKw^_Ozvn*EjLE5jUK*6VIQF zyJza~;7Hps$-XFkg9TpTJ>dE>vYX~9a{zsA+1S9s~eQqmvx(&v-j-%Bqx>GOuxSI_*9IPL$~ z%QN-c%}z`{Rh15&b-=UQSI^n>SUc&y&q)83m)=Nv@H5ii^3rSRgXh=sqfPW*=g~Q8 zOX;+OOc{HPJf4sFk6J$?&k6E8MIPfXsVec&oW8M!2YBr)15SInC zPZ@c41^k<>h*;r>U#pZ#aYeg+O*Wbj?e`0x2Oe$rkeE%!6hkl~}7 zNXz?lTKJ2kt&TnmE&DfcY>>S`dnG>b^2Khcfj{`I*uT0ya7NhQo;DAB0el|aCERoE z72JdWn(GU9|4UAzn^qs07=outn`e2d%j+9m7oW(N*B5^tU16qt7eMn%M~^K27kDXr z5foqTqzk8rFT4MI&i}w`ksGhh;k}%G_g~=7rtZ?#3GVEYJJ6AQ1kPw#h1{FCm3LHy z?+vV}4+f7lg$wN(HlUZT=;!T+ja7;A?OymX*58ftrjl^E@QKD-)(LLD-Sv5ERoytc zcif?q$9x34lC*iG`;f!nDbvD{q9A-)zl_GO8QoNYaQ(H6MeqG+9IcGul5Bi{k48s% zZ(_~g=yyQ(+wgT33I4?CWZ!3+z0>H%{54M6{d-;0PiV?K!eQQheZJ&8C%th@Z$Il} z^V|%tuO2=>Qbu~lwFTgM>Y(4@aQByT=tnyJo)@`bvev0S_N)s{691dBR$okAtNfAD zyWbD*E8oUiGukh17=3n5$tQ+JWJH%gr!|?^w-;wrU;AadCg8{ohEFnh@Z)0kR$gO? z_0bEx$`&2=-V&5v^@x%2>X!c(dtsZ_16mZ+p0M$EzeGH?V65nijipU!(E8jPaz4GFf z^p*Qy+C+^a($Kj6*fq%Rx_50HCCd|K{+ zKP@NRMjZ6<655;6_H$Eyl*P;MSH|pjp1!NszeJw*jv6?MIsfA28OHY^`t8~EzW2tx zjqdFGYu$MzZQz#;*p%Tjvf%*sV&8>lkRQC^@DVTmyL45|=g~^>2ql}0F2(S0jE{31 z-X#BlbFSb4IC$2ZJ2N-T+gv+u@68gQPTOBnU&=TmK<62UcX_jwc+F37(HZ*wljbJI zIPtBTs9xWsam@6_X2#*gvlj1N<;9!v*m#ZmU=Qth{Kdg$wHv-0USX}ywVj-~GkE~F zH~qbVYCF2hrd_)~qqHyW;D>hHVV7~w2EH-{TUuM~MdIf2&pOWs&W=3iy%CokJHfg9 zcUgDt>=-__5FYu;<@J`IU2t`hpGBXvNbAnLj_Tug7kncneI~L)r^nN}y*%wp_!PE$ zt;!Y_`w|KIF@tc2>WRF&pg!^vJjwb6Q%zmQUh8J>j_bE4K)cLaVpWMduChGMx5pC4 z+1e-gdEGT7qU`W@Jf%>^GKOLk>?crX`! z@>nCbujU>t|D1tIA3fRFQS5b=2HKnv0mhCz_$+jEPB}O@uuU*_@G6#r{P$cZ`JwUB zjivH8!r2u>%$?%jXo|^W~37vYj8?D8059U(5O04cIu~rxRIEZ%8s`-BmI^+P51%qpB)# z_Z!G}8!3xE=T^zfF;6eu#F;nvIWYv7Sgn0t9CYKVqs+1Q8(Z_ynRYln$^V4$3qGS- zvUO{vlUo+#y{>%6SM1{-xth2sH;q8)X#LxEP`>Af?lXW)P_`5zV_6<0cY?tt#$&;=22!GT!h6VTW zy-;iSF+oxZUhGdxh;Ezh?w2jKff zlFL+2$epTli6gSbu)avXbGe1VyW}4>+pDjT^Xg8cTWaSX33H2F%^W5;GjyHm$p4O_ zC)me2>n*}p!G~wCm+)V>e}~ff?$7t0iVo~|It7ROp9$;^KT?LpnCuy9i!zPvG-1ty z?&i_QyZ18Ie7^IPueK;x`o3KCjW{293-}e1cLaGik=IB381f)n-hI0__oKY^{83fn zPJ9?S3ESQ=-OV#?QEmX`)Sg*qt%X-+6J>aJG|gw{;AS7=5T0jT`>^XZfPBBYDo0 zJe%_1-_sY}wN`k@8`nVp`tZxF`)gK*G;eiES|(|`N&Az9w|NoLj;LG`Zv5+;2atZK zDlxCNZ;fY$Qv0uYvUT4z+_c%|{6NL8o^4kE!yD?ZN$w zF@K|sv9s+fbM2gzGEU=p@UqgQZgGw!=VWUmI!d!|gnKr<_xwpLmOj$JQw)1{ z^pXp=y44Gtb#L(xH!_|KWO&Z)^*-Sru=|>~^1`*^4;=YBz9drK`E+>8A9U~GtI*-< z=HZ5q;4Zu~if|hJ&>ffp!s&#sC7c`E4jx<2*%|ZCe1EPg@$Ks@eu%J_W>4*#v${g+ zorrDOKCj+?1%JQg)jOZ?Z@hZ*33o()_&#(y@uXl`Fr55P&VgBPA8_6Y4tLUzv7bb% z3h2YMt?j(m(#ClzbJTd_#?n9s{K~JKf{-(#7@vxzls!@$#7`t;RgN=n%H8)J_OHVk znZeaMD+@VaALj2&zsuj@&h@vY{}oz&Nb4JNd~f%S>wi@z*VXLRh2XHJw2?EKVw@vA zF#qrEN3Dm-2!i~N$Be~}a`F zbk7j!N`c@0!F3tD{n@|V$SdnIEDY06aML3pchY{3uQR$z&M zOQ-+x4W@r-noDc}3d5TVCsWqwQAL--4YC86_iXje$bUwEf7^zt#Kkun*?LQwaQhL) zhhAve^s2<|Z_9`!B#~~dbxLobB}hXp$Tii<8=+l zOgGMFfEV_Q&c*_{3+^lVWJYyK8@2&!oPv@!8@?r5J7~>P`97h)lZE@I zNq-?evnl)|3&-EuTLw?2H2669z+;a1FT#d5Q(pF@c$H1c&z^!_f9omcX?}5SdWT@1 zyQwp1>s;ATBP+E8h(j)_HZsf>d~P=Ayem=2ex7Ii|DYRz#=XW~o)<>nYUng?Sb1|4 zR6f~7TfM{Ve&B|Ak~=Hc**n_&=fxhxF6-SxTM&Vr;(45BeK#1aS!eB` zXL2U~c8}&rHfK`@f&A=}z!oCpc|5_nV5H0g;NLk&&A&;El&8;w{{>h+H@^ zhyP5SYuwK}+K~e8KmE$dZZ2|W-~WV@iw{KkbnVBuWE=b{IDNi&MZF^ye!{+?u{9qX z!+WtGY+_!ZDZ*{^H+|Tx=6y}xXzZ+sM6b_T_+zJS;jf*?8o%W{zIX}xlqUS^u5(93 zoAkzo8*IGL%7=_QGg8J|Fc(_;c=L^a7rwI>F0psn7JkdgrA+R^_tDWl3~bOJ@^xZ6 z?_Atu?oGb_XazWSHSdLOaVI@`6Sn3L`fWQEDfOzIij;QMPDQVND>G7RzLWQdG9v-b zAn|cll^8bM_!zv`8AtxCe?JdgeIDmI+{gO&%Q+|KJ}coIy#qoxhj6amsUw_AIFI-8 ze1!9s*YVDqIcvN=55J)DssA|tp9`*yzgPj>8(g`&VR2w{LyPuEau2ajeq5C}cctKx zwdugeZXWCYB=UOh;#thCd>3zt@6(%#@Jkobh*n7IvazObI}%-(O_;NUyE_xlC& zZFSVj&s!-A4@$ofUxi%G*yC$V-#XcsyBi+WK9DVbAg1&B&KlCgWzg||#{F#3GSwyD z(v1^0X0{hN=sK^?(HK+h6RX#I`5wK%<{NJFt%5gJKJkc+tP%Exet3vbxNgFm%-aoK zT!`>w!kN`gzDqvK{?VkZCylq{R-wC3)+s-}$#Uqg;>I@c%g$%o2DXp0Y8LvEjJs?=HHhD9r+pui%W=`_ru}7O}^sd}Z!6t9!7%%>-gbTre-F#!KwiBE7 z=I5#sbx)oWhrA~{-5T+aC&^h=iCY^hI#~~RDN{#X>%l-`CwXsr#>FP?gA9620YUouX^Y=^6ZH>Nt=0)BZb9&or=N<-Q)BaPNf!#B&geNcX(>Co6 zVH~~h%CXkwel9q9y4Oal+Az9!wPEPfj2&K@?(!EXow+)qK44>%t31p>p1-Mizs8Th zLixt=RV8M$*uG7j_jmd>uCH%5M^5S6S)zk|eY?zz|CGL6Wczm6+bMnf_|tv6b>hi= zt2<)*R{a)x)9;;67#m#CF0BLoGIEvX68`~{*i*VsH%V!PUr7n;o_I3e-T{TbPt`|Y zFENRB#)uy3K3inmHOVuP-Zs@H0!G&8Kv}y+a z&A2poeR~PNXvl>j_ZsOxtlqDdblDiK@q7V8uL2#PUre2q_;*XzUxXc-(dVEKL_SJC z5OQ+2Uy9FL&gsnk3;2P|hkDo#iB7-ZVN{fRE_KIg>+0u{V-QR-?pW&j@CKXU;ZBdf zOJ;R#AFp1j`E34%vk&_sc+JEInp@SOw)mH?CH1er?hR@F_+OT6!+s}*S^vX{Csh|~ zv5&SM>m7T$-QU_b#2dMb`D^j&#V32uXzDk2NbK76SE%EQ?tUNpOXS{l>`!=5!>^K$bXRvR?cSP# z3{L+mjPKh+$^J7|>0h+l|T?a!`n~X zYt$a#oi1m(+IVP!qjBu_#!(6k$gWxNQ(*gV-x49e*?++N1}}auyw_yj;f04yzJc+z z176x#`5Xf7bw79#^TxT6c#JOj(a`r3eCv+ojVa$szbYla(#ul5m3~o5ex)bpmb4Qa zRA=AZo>K;@9aHxAac@IS$_aWc~IL;QYBVJB?oM#h>?l=*LCptYck^2;XgJ)Xb z558xR<(sJI!2j2J+Pyk9QJ?Bz4Li&kLq2|~SU>Nbv~i5_px~YPe^I(8-ahm2&GF}1 zx3pEzZq`ftY#zFye<3gH;mo~oZTbP$Fr2jIWqtm%k1AE*s0~(Y0>{(d%YLBk$DTu>pFs)s7WeQNDLd zr~U9)1M7=Dt?qVbk}nCW-qf|rYfk0i$3egVEZ z9!Zp%`+DmV;GzWhy~prrz@txfbFKqTOfzRj!V%c{9*A5n`^Zwam_E0bI|t+kPj8A6 z{(!xbhK`uIcbkfRi4VY?pNQ7lJ;0kE_=*oc;?AY)&B&iQvKQ668S4jfPCeik zcO{=(Q(s&O4U1KesD2Oo%y-9)48MC#{nSCfD1V0hQ!~enY-x02k<4rA+bf-%jVgZy z{7QY>Hs|SQNUPpX`Ii{q55H2skvQU?Jtgkh#e2}vf5%B<{jr&TDGrRK zm-(WJTJA2fhGX>p8vh~selGuh)=BR-mHVSkzmV%c)5G@{$rhJ&5Ut1wbci;lnKvOr zZq;nor5&7gtImxtbK;L{F8MyByDa=~)EuoYG(K`ZpgtcyOvb1mT4%3q^~Dd&e#^d{ zn&m z2b@G(NoS(Dq$9Doq|He$i8-Gusc}9BU2yA)FAEA@!2|FZpF|9#G?&2CZ<09D_W9^hY@cN#m?hlCItwPiW6$;CG3pghYsLe;DKzuZft{ny z`Kcc~7EIzVTjvpXA!Y6@w{j+VJf7ibvxkSP4IVXfc63 zfsV@t{LPE0 zWkT-Qx5+=8{CUazq8lpHT8_VQpJ<4iyS$cjeq;*x!HYbeo^yWZ+*9$M=CYCU#^wcl zNLc=rpwoTl&Ejb*r2mjE8oh^f49r1+|6zwa=gsfqzXLh^!y>-(#8+6oz??r5-)_PW zOUFHjbN#R37vMt|8ftWltWWN_yPe6!<<2wd z4bHge4&Dc!b++zkUlHiwj$6d+1@X1$HsaSAKX46iH>i*Fr;z?(8#iT}?Nj|TRf%g4 z5? zPcQHQdRfa`a86qT@50**jkMbcpDup=fBF^!ygqu&_e-Ot2c)|l6PfmW4)^1o%FT%t z;I^#jYVNOWPAnErqJ0TncpV!mhwlv1bE2zT^!`-Vg=^$*DQy&cF&!K=ec>L1a}EDf za*lJr+_!MQa^|$=(1?e&I6eM0^vLi!)0%%#4lZ!vak{5%z&@eeiBH?mR6cF<6$&RF zTIX~<1Y!EgI@f)$!TDr)gJa|&Y@+8Q!*^5G(;0j;Yccuw_H+gxUB|b}cZP5S`6uw5 z#TbLFOb?2k}== z2k}?(ac1s#I*31SC0?)-&(e=VQ8kMm7}K?2c1IJV({v3xS7+QxT0M2lJKBuPc#{u zeZr?rdxACmhMP6*KJ$OZwELzPg154lH@G>i-*9su+~T6Y|702M`h>UD?zHcPMojyD z|MHUE&H+Q;63#fmhu~fKF8rrJeal|yPJ%-k;fs=KG2$9BE`K07Hs#a!;3YFv4%;-| zQVecxhbHs=SMZ5+!H#&MEtUA+ zZ8Psy9&T~nj0%UhGT`BlfbZLu3pcPF;y~ z$ezXHEizbNy`RY&U)K(9tVnG4cR*XX8`%;~JTD*f;XAQgxhb)~mAjAh*X4gHzg2|O zmNz;rnosNoRuH!ae9r#kExTqze^+TQHvHGWi#PCrpR$v8k?x(~_^r>(DN~HCD}L3@ zxxLrdkQ-P{KTk6@umR#FD=1@=HMjZXc8f%gByDc*N8`I2&h@~yD(@9*{U6?yq;p;My2+_%%7E058B zw)O+_p1ygn^CautE&Fr&a>VS3>yIC64Ya+O+!NXl;nkd*Zo06(q6z<5ZL$r&?N+lN z?y>tJy7qCW@&#!(Pni9{KG1&9-LF{JXTyBREuI1#4TxUxyOZF(DsfFd z?HfCyszlZs7T=~lseQwq$uEs@cGH1wyVq`c(wU9SEpUx2lh)$p8{_4}w(sUY*>$Tr zs5Ih_P@c1x`!2witHeutrP$Z+%SvOfXfG`)39@(CPvrISJzV>a{1b?4Q5pWVuU6e( z>8#<`#_zGJMAahMqtcHY{*jB*@~lllS^;$oCco-V$_}1 zX>-ASULT995~mqCs}dg3gays>HlfGa8E?3D-`%4Egn%2g0+4yjc}L7W3DozsviS2f}q#576dY zd3EUr_VvDmeK;xE|~@SEdzhr4zl6Aye6lq@p5E~bnb%= z{6%^AJ#57$`bI{$Lu7P>;&buCjs7Y-GN^hK?;FP>6DE>|P09Hab4ok0PeT@&dO;ex zEa&uSvtP2^vz42B?IvcIcDlU5YU0-fI&nPZIK-tV<7SsR8G54?9zU^9b_dD>o(a*< zEZrpw@8%BohRGwXEcxZP4Zj1<)4fNnd&oV%hdRw&Ij?Ni>MK-_IjivrFQLbHD_J+b zD7ojAI3Pcl_LP+o=M-^^Kg zRpQDWG3Z$`UT1KIx8n@@FY+scCQBu>J6O9R|;%3JMvSMSvE2=0Ls`7X$j-n%M( z;yO<*&ca^BS^h_SZWaPlY)+au-|h+2 zhj-$)2AdJ16GPTx+<7C>qiUThe~L~QeYRbnOACw7Ps9}uKhht0%INniwx2B%y$uoJ`u!Y>=&A`XF zhuS4yC)%1zWH;rz1bN0?op0inYAmGrF5u2x@PWi^jYDz4wF&7FGMUdB^(}%Q0l|X! z2WuaB-@iVC-nz_}IZ*hhwrJ34Teyo7Udww%`3|%;OXs@5+M?6t7xJ;dE;F_pihcMG zui;)nqaz)Pfq(MXU@zyQhm2F_4CJ6)r5(`EPAA>}QkSu@pe~PptJ(l;=?u-G-B8Fa zSVTVAV(8r==?9%;8aPV#(NgnXr##ZRM2h~9vt)uZ*}Fs4GE)8=9&jg5`-J;>s&AWf zD`nFk+>7pl`@3q3`qt~d66Zyk@S@ex^KwVco#h@y=a=iR0j3!fu+nDz$&WdLPE)$g zQvTD>hZUH1dA}Op;*&l8HtWInoXLR&20y#-fx6o^LpYJSf3~^Oxpm|qaNdOe-0&Q^)vmWZ&+C(O zrumY5g6`JgtS=LL+YHW-XutdU*gEtB1nO zVqebFeyxfh9azDBQy&7my9$6+$?YNSqmY|FoW0m>Xu?ZvFB9kKl8=>XFE8)0KHY^Q zkU0yqe#_PzJrMVX+13wLBc#**RReVM)Y)w`#!d_(C~iOUD7oal(Z12|oO z-*@~?xBbrYJ;B#_FD5g2H-_{K-DO)j9KDK}!{9TeHIy@UMVD#l zy<@&&uO0JcXQhm}zkmIbi_Ms!+1x4Lg|76ZF{{5Cv&K%k==l-hDSy)vbI-LZ5iIMQ zhsx(g`|aGHGyddxSaPB2q7LkZotlt4s>bZQf5Q8F@M=!;1!p@`3;Q>4){wD!(AiGq zy@?Mzy57g<)nYdz9?7G@fey#9WxwEnTe4SQ@CBWTIIEg-ym-ga`6aY^KJPF%oEJT( zF{m6czO3;k^h|s=Q;vKener9%WkL==$Els@jM977R&4TCj>U_~F-!Jm@L%sP|)3*8r&Ua>+G11Pt zPWaWt>-1xs`lNEUj`$9>A^2>jZPvxdJ43QxYV+jwGl6^QK3(kt!I*!qlga(AkoH|{ z*3KaKwjEw`KV^+h;^^~eph zIi;ucYaqDlw9?MR$I!Jb|GQXdSpkQ=TGP3A7j1JT`+Czk~sw|3%`nBbPmjn}NiDx$w*npm&>m$9qmX>z9i!q&LIw1?q=olJDW5>?nMQi!F*%+OSNw zMe)L~#aB9`s4oj2JKcd8xU~kp2pwP3d*GsXSd;FOoalG44PV23ws+tuy94#;{PsoH zgeF5z*V}W7?zI;Gbg%S$pfB@7|4CnD;io$yycBXryvVx0YH)Tt=QYf&;ZJv2AB)WM z7}@Rt3&3;tQ-Mwn92=q4vZ-3@1a*$5Z^c8mHLek&8H^*3v_XMCawomfIj#8{z?BU*wS{v60E==Cz?B`$KaHi3pn6#4D%6$0OqMq8K8u5FUS8iur zbT=7%sQGJ!XRb<=tOq`a5GKv&EwZZOT@%CyRi3PCK$nc(!q$H~d4_v=PS#u4^!>;k zAD)u_Im&zMRYpuPdeFa?Su@`294*|(xiWjG=zC8^cclL?nwan=aWg6sUGD@EZ%>B5 zcpy^r`<%`}r8{NRKV@j*+OpUGxFpoj8gLgAZdD;{J2 z#JM+X{S$7~9X`=iVDn?>=`7J>XkaL?yR2IJPUNCU;QCvh_e;Lm&3RnA;dMOuqGE#X z-v`##u&+wyYra)B>+XOqy4+`J-7p3gI7WUT@Rf*9T zN%klU{&VhiSiZ#YAMh2{zE8Y~_yF+(ob?~`cnrf2=)Oft8GKYGj){IYrc*mKCp%E z{Tg=A;Fe2Md@wKNe!|Fh<}j%nnq=|z#cPQ>1#get<;A7o?Xf$&^%LKY&U)#aeRzB9 zkg|UmZ;!$MgEPd`e-eJzezSNQoDO~;!(Bt?Rq`FFS^pLMt1j{EamQ8?=i%VTYJh{+ zxC=Vm!@+wPqxKs-)o|n=%hL$wMv`wc@3r^8s#j$q>y_=(y(9D#$WbQE|~y*)}@h1=*cv zfYXGt3;Z#sAu?ga6Ok(}dP4gc{11;5H+MSVU42R36`f~867N&)*{$u)V~oY8v4kJH zkU3uZt*ZFJGR?7YX~;cq1!Y|H7Ebk6e1U4+;F&~&G$*1#=*<$MLGVdA^5LWNWbo5z zA@_6F`L`PW8XnN=GyTRhCwk!3!Ff#L1!VTc7e4Hyq5qMd!SH6&60{2+ru7QBXKuE9N+M75 zrT%Ho%)Ius^D1jKN-*u=508)N%C~$(*ARu8}YXc+rdiX3}LOjGO)=6vW@e#&PsMfL%hPp>< zpP{#Cni{e^1n+3ULtG{vq9r*uz(PjK`uMeW;<>bL+T#Ie5c{H(wan_PyXy_{)spFv zR|X=}?>j|?8I&T!oF-Z3|1TNF|EDzPBpC)iPjPnLQr72BtWQw#SBeZXA^nGo1~4DU zFp<2=c(bfehKb}|o+85>+hk>!V^7)raBRJmVUDe}GR!gWzWK3bb`5+E^UYpP`(Y`2 zY^&>Z2>#eFn&W+e$Ka3Re^_SuwzRyH`L?CMWZxwDwtbdw8@j!ZZ%aFcZyVccd6QA* zJyP~FJlo(Yq7jC_Gd$bU|FZoZxrlc&X#3D+kDo6P-O&E>H$6J3D)H_j@oL(mst4PE z9?`czc^du?PUgu%?$FT{;3C8KeEHWW^JLIYXw~t+f+g@>xBZT~;DJUBhmSLK()fge zuQ7hwH{UY2Dsj<=Y6rN?f`+4DcE&-8bK?dQc2uP=sA6aQs#$3+vo_|xd)@B8S~Mdq%( znQxtYuWz{2-?VhUg%k8(tb@YDY3%3G_r+C-5k1i49ok>;ap3=v(r0<}`%%a8ap%tj zmO3quJnS*$Z7kZD8Ls2ak2=eT^}hL$Oyj+aRWodwr?W8pl=Asw=`R9%!Ub>mYtpx) z>(KqcY|$IybgwBj?{B@lyb+ZCpQq%lKPB($Ufu|K-#8`jq-0)rFQfA!&+onbquCEJ z+Af;*N7KI!!o{Y1P5QQz?Y$Ej)YDStyXkA%Q?*53ySEj5ROY*SOL3)x%(3;{t?kHb zo#w0@yybi?kO;1VUp#}h<^|e~%xriA%5ksd2Lo%1zF!#L%3C#0EP4VwtTU(FGT*u1 zmtKSV9_1T*c0(_HqMzaSLQhAAasLIH&mDpx=r<0P`CR-xr2m3;u#a+Xp-hnT9|s(o zR$Fw2&ge(0EdOIUyVv=0G2e-2@|`kh{#9BN%E5QUD+zD+!ehO7c#3$D7oSgf*tB2T zbtHUdr0g#_N?Sb3;T@5+6VE*L48iswTXipvQhzLc2YJ$o zV|?8`i=*gqh$DQMFt&{*yq~%{sOMF_U!op#dfl(tzI8uk+v{FWS_fetX`2XZU6}vw z4TQBW3a|Cb%m+s2CG&IlYzF7Pw`v~&v$NS#bICjRf3ja`FT2H)fv9Vnco^Gu!s~A$ z+23sT%qH?Pzlm1%(Ix6vO`YLK>92{O&p$ToT}L%8TbI5Qk-9(1CtLb<;Rn9wkPrE| z>y?yzd?%)oFC!)2c=F+AqDwIJTdzL(ow(?ZKRO=~_n(CGl}0}9`z6j&oGp_WNLVm9 z2$;n-)xaQg9zX2GpGJ7U7teaf-zG19bGurV?zP2te4F;B@=d8I93%WeoaP32?wXmB zm+$yW<52_{*$p6!GM zliJhI^UoTc7@~AgSu9^B)=EbvSy6bAzk~_&g8b51v+~AwOHR~D78fbsML3?e$br1Pi zpW_-^a&7tU-mkUt@=;$mcuIJr`th z)nC@X%Ul0rr>uWh%KCS$^xC7ZUCk-&MZCTaRC*s=bj@VHXDgq@3tdya^6cfV39JD! ztc8t*%=N-X%R_(%%^h6!jd%aHD=(aw{B$@zua*V>-#RR0NzcDV{xKVte~oDi%qF}z&-P2H>b8+Yv!)2XXNyvY{M2g*Cp!FC#3 z*Ks9pE@R#cj&cX3T5xH70Oehze&`OWpK}87`1q+QDvc2*Ua?JjANd(SZ&pg2(K``0 z6%LW$$P?^bTtR=qZ8uUr z%gRT2Gl|FU^cG+~KEA@o+B}S|M)!t_7vLX1Z+u0EiOZ2Z64)*O?RlfE4tD4e^5&Yk zPUH!%3U|B2<#}}vG`^^6)aKbA67RElhaP4wk1*cz{dLPIbM|Yb6<8hh&^PRSpS_v5 zLN9I`;Sp9I8oEjHg`NMiR|l@2F`G0qKa6`d;W1YJ7`jaJqj@FHAINREhV=P%&d#nS zu2geF+)OX-D&m4(dsDR5{yO=tKYJ|ko_+JsV#WyX)-#N_kR9Vt;ZEV!fy7Pn;?5*I z+3RmVJ6A(A{%Sk^vkw!VZu@z*&Jlv>OlGITc2#`uMXbBAapOEJS8deP#Ri3(VMoo~ zqgih^2)0w-BlYBm&NB9(;9B-5z?|N0<39L;uM4+gOJeOv25lUne3GAvC+_PA|0*;R z`Rhz8PYtOwI3q>3mD)GS1?=^y+Gkbq&PBz(pd;BdQWPJBK2N-Sn!=1L+uq~IewOkr z>JzyB26Ikf$0OO=*mW?jVbsSRWAwf8&e0W}PJh+O*t6J|hw2hRWZ=#r6*2Av+4#&r zEa(86-Hg%b3VFBfi;OiG-FyN*bZ523vV4nkL&(i4w&VJ4nf=ed%IJ{dZ|WTEh<)#2 zna{4>xf`^98Dq%XhsJ&Zn_RQD^aWTx=Wrl#kK{zjhy9Hm8@3;|{5fw_#rGudJo?l( zFCTL9vh$6unY~3FPgxkdHC&bOZ8mg={#GTvxZT9BgG+?FB!7cT$SeOhb3^VKvYYFp!<-?(tGj{~ za78z8JtxoxjoY0&D$pq#ot@QH@i&Yd5nXtUJ6oJF8$I>u9g(5Oo(`bzZM?y`Au{OT z)ABP0oV@=3sDJQh)Nf>rQ|hlV^-ers>WzH;xM?d{e<|xC`0~G7c`Gz|fd5C_v1S}w zp>cv!W9tH*13oe|HgKq&XJdnNZkgua$n8$o4y~72Q=^B8)Y^5;>yiH)tJmB&yf55S zlnjTQFAmqfNq&pILKo7IHBRw!@t+0ENl^VYeWjn!pjCn`tyv&Zc`N%}Yw%+9UDj-? zFmR0;AYAgzY@|9wdNzrliD)FMwAVT1FjCvJHDT?rih!Olg}_q5KsciH~d6uNZ{QfE(C7V>V?(XXe^9 z$??wY^)Bh>mk~eD(zKjb!u6J}*o=?(BLzCE_6~MFIdpqCK zys-BQEU(w{e~Ekd_^7Kp|Nl%fzJ9c9mb*QmT5HZ%cJIUlS5wsJ6-B6Q7Th!2C zS6tj5e%0O3&M-43T7s8N1T9o9i9&ziQh%}PK5)w=tjdDb6}#%96$lCn)b66vmW#~i z`+S|xXTpTq@9zE{yT9Kb^Z1-OpYu7__x-%j`@F=w$sa;;1U@1lo&^D-IoJ@ucVQ{1f&E=cI|hco{x9q?dT`@=#;upXVW) z$cM53|L{`E{ypEscnGnPZJgzk|9C+6Wj{t=uGr)_$&DZD{FUXk;OS!C$sV$g zC;GJWF5l?+2JL3=k`Iz+h1=@aO_dj0;@i3Po1CMHnREY(2Ap`n2hny>b}!BPsJ1=0sxkBGrL$7gCdaG?Ra9u1~0ewHx=oGd@Ha_?^d7)Eux3E6) zt+tz8JU{DVk4BeBesyVd;W)jUc&_07tPy-$>e86P(aBBggQ8){@9#w>Yx_&952P&5 zkPJP+rDJF9)%UIrg04Lz&!4rObcHKNpS6)WK|VUMpFGP;XiNN{+Izp8yX$7T^7{$t z3sJA`dr43B%FJ`eaH5Yq&lmTsTS!m!-rwN0ktVOn`~GUu&EES;@f7hv>1vUU^X_)r z8!LJLdT)NtZFIiyd^KZ)K3bhn_ZPy)Ed3|X>V)ncY3J6YW}2OhN#DWbYk2P${J{r-hcE88e0ABv`)H$#dgr=)W$dMj!%-X6 zTB0|=d)zf2d#>=X>YMRsxGgcAGkCw@UAwF6oOz6Tff4Ll;zRI9zTNN6VZp29rQH6X ziLF;Y563kAPOscXckBhL$y?x+TkG<>W3Q9X#24d zJmDutPY%yN`fK3pu=u)rX8OUmvhbkwjv2?EF^(*||1!=zRx*c6-1RP~A-&Ai%?mDf z&oiHK9(ld)x)+@9uFn}G$Xn&Ecfn}E5q-FvyfyCnobelXy~pe#Z=Jh7XY6&?`{-!$ z`n__my7i5Dj=X2Ra?iW#{ky&7t@p~UcGr81;D3WxPVhhA!v7ifrlE)M9DHZe@MGWM zn&^Lzd?C8;_S=yy)AIQ2o|(%7oQ>?w$~PKb?d#_Z*O+WM@rkTnqOwt)<4zf5QfC)u zIP$FQG8#Av<)7po&%BempH#j9UHEYcuJGjw;g53!zQVp&{!zU{Ua;@HHp6^(lsXU4 zt}7>`vHjtX85y)^O-gv@`B^w;TU}!1Vc9ZE;XNgqU)c(!$Jo6RMdjD_8;mLm} zcHuh7M3qsKKeOyn>iw?!q`Bmtim$Xebn(LEh{AK&)1pw4nyf4m0%e<;5XGivGAhw8ekGMfKc>L7JZsS3gy&RTNl zRQV|Olp$CQa8I=L8!=-N_+@0_Qf(?;3+)zmF;KN7-*VxK*1gi?9RpUHc?ve?aPA&E zgVQaz;kl-H7JH_aPkgq6^=!2=dc9eobC1xiPwg4V;}Naxs`w8sa?d8O*4<#P4wyK9 z68iA*t3BQ}BE0pgIhIa)d=35cY{^WPuQ|JxH98}$z0b4lh8vwx(?mxkx4@gxCzL^_Uxy89_;x}EBo#>?@AV{9A^e18?Ra%f9DEd z!skc+-5Fnxyvx0I@s0QktiAWi_+uAI7cUPJmxHmyV&+%pah}p28Nl|PLMK{b<06e9 zZE=xz`MgsaJ0tu(*upCMQ~ zd$r~cIpnnoL+78Gi{Dxs8gyv1ty^&=+J33>&qp8jz1X&~++4UD zU%Gnl?xF$WXZ4x(^Q+;3IvYXRMA;|An4A(MHhz?|m6OxNl~3G!C@7y&;wH3hne106ejNh)vr2;$&>16FzY+En+=uUGAa4JRo3z>i%wJwhRP^+ z%GJBECwM{+MxPBo!nVEL)d95z;1=W2y>a*j5qH6kCvb3Fwe>?$*@U+ir)AGdgagbo@1AEu;y+Z`Fgr(*4oV*;r&0PXO>56 zZN-OE^ntZ5Vr{H^OaH#aT)@K|WOnmyo^SJv@kD1cU*fsl%iofs9O)@pI|=XBW%>EW zy=F$uAa{*N(WU==*Ea4J4^!`|T4;?uJ9Y;#?w)I#+?dHc2Vaw|*e^Lhvh1N78Z*w* zvImnFkRF0<;?KxiF5V@bm~UMEihY87SO5LW{i0ESo~2P$cYg2ZG4shd(ZJf2c<-o6 zaLPA-)??iLPTPxR=N+-Uc7XdS!P}W-S9p_nAU$wZ-*!W(bsy@nLcP6GKchy9SZoiT}*T%Y5`^|&c z80PTaBuBhp?XJm^pL=qG|BlGA#QluFQ1~w%$e5RN@6$@k=gGEYbyLQM?)5r(`8H4I zV%)rqHMNZOqWg(2R37TNj5W@j6#uvPno`#^%`0o6c`vGZ?xC6F)mWV`gRh8W4m-CQ zYd@dw&aLDxj~5=^?zS^x|InOrMu&O0R&%NOnpqaI^O&7OXkg6M8S@{ZzXkJG|DC;C z*UQscJI{vy(CzkKAGPn~&mNi|=Ef`ALjOXRE@n=4@!mgtK7O}_yb;6a<99!C<(E%i z`A^9&;6~*LTJFKQ;l;kghT8iTWF!lK&yF|528{F(*3j zC_AC_FWq}!<1JK1@#9TRoe;l`pKI?6<Zi$f#*msX}6cL?XZ39!>6SW9h#T`^iT0tD%2OsWOcU*vYlEv zI`izCT5r*5(d^!t+Kb54*fwfx8}7ML^NBw@ckbGld73}!e9-b_bTsU_d$G-tcM`g1 z&*i+HV*PUGzs4LknxC105ru-0p3%ELstFzDocM@yxbw-cxz-Qb&AmqXy_Ef=vh2-w zNw4$L?WFrv27J+(6&vGGJT^9Ve3SRxGOui}``z!AQyc|Lvl+{few}K|@AbwKXv{o= zPoC`fIen|~WYteycX)Abf*O0ep+DL^ zyFaR!K+MlDTe-?zdkCzCcBCRH$h}P)5Cv_J285az0*r$M*hRciZa}_l%~F( zhM_o2!^T%P=y3mLf7F8~?Mth#;q*m$#o1WAtmR;Kjnz-B^X5M6><9U7rIq(9BJ5Q} zHs=L8=ezzF=m3Rj)+1X-a>W7lWnYJ5;q4XL3B}C65BVl-2JR#$-~;!p=EjW!^wVi} zE-%VoFx?@J$3^)MB#E2Bv)fz8IJTk!{;xS5E)+M&U1JL;7B<|m{rk}V58vcBZyh8mcw#|y>t^Qipt|Y$slJ@59ZjAONX=Hxh+dd3xtO3nOKx0+A>W{{( z_37Wjxz5wZ4GiFqZGxPg`D4koti598>;KuDdnzQ4SSI*9j5FsX2fs1F>c5%(yRE-K zih7Y9V&>iDydRsSeceN;J$#*^Q(M_Sh%V*M0qa>Vc_3zfoyx)|b38h`p5AxXKT`ED zo&Yepia7Zrq{rBKXa0MbdyOx*?$29Gtt0jO{)b`4l^F-Dt??8MLY7OlLDM?j`Gp_n zF+S1m*@7$WE4uLgQJlH3^q%}=%A+48auZG$sjrk`GV#tbY3T!YcUJ*OCtB214w;IW}fY zaM9-c1&eOZe_&BEzkAWj{Kdp4^|KGDz0E>?eEan4>^+*H_43wQ>mXbcoMds!(kIua z>uY?s)UG}7fK7riSUbnzEDSJKOP`uo5Vwu-CSUkOHpv2yhF=d1tP>B+)~$DqnV%fA z^H>Wk)a6CJ`IsO$uxDWCi)_031|B)gH{}~Ce~A7-&kh}>4+DY|?KRSm$(#5_vE!^< za38U=(#W6q#6VxeI=3xZA=?k+hk3;PYMs8E_(IPg3U&~C$D^-Pna6H$(v5?)GJgSU zFTb1=bNhhiY0;ki#pqfmHHPkFey`3E!F!gVPo1r?fPW$WFNmbrgr^VmH!9iR)lUzF zvUO8$p6YdNR40ByU90ZTH~#wtdrROcZSMr5#B)d7@acX@!65!G{wn%b1uP}+9grNcH8x>ckB9WCh^HUIP>k~U4HsTVv?=!z2VaG_m=dG&tDTWzGYoE5npi=aYC+? zUDu3&o)jc%o1U8g+>~Jag)3;2yJ=IBmv+v6cu#((;7T#e(l_TXrj1dTjWKx-doake zhhpO#aRa`HPX zFW_EEbIpXxuNq(RhursDm*1JFs|qeUX#DgwuV*x|qL>eOY=HfR)WZi&A!jQCVtzm?le-mj+KH)rC38@}iMVytlP%Bx9lY-zR^KM*#u$q~ z&wAx9q|BPragVzjtI^M)llg11@S3i1{T!kzPWB_)t?7!$Cp=T?@#beqmrE`ICx*!! z!b$2`mI5w4dNwPa&pclQt#7q*i{d?&D%QMb6Q4o4kTi2*nn?Reb7sa&E^c%0eSv07 zU@rXXTRi#2AUMe#G5O0XqpUUcfXBg9GJn}HzktkIe0-QI?r>(z4@i%xG~mY{h>?^N zyU^0tO?&aP9KesD*UCf{jm~d(MKqsPp{qsUI(#9oAoQg}|~XbR51qTl%B3CQFknJ;HBY{T1zr_^h24-!%HkS(QkXSjPZl`iA^v z%x8J(Hutr$ImiAN=$VO+{M-4*U!jqp&)Lu=T2&Wu4l`e2r{c`N54aV0}7N}0tVB@^en4f~fi*B;E znO7DevvNM#FPKR!N0#daG3T4JQoA@e_N$u1iSYO!vV-F5i=GBu*mM?m^Iq@+ofsWI zP}HQmdLB3cE&PGq&zd3KP50W`cDc`XZ5eSw?lBdYv6qD%x|%z1Y~PxREzX_YzQm2+ z3_i_V(GOq%UOAt88Hp*059>v#cK z$D<28N78S}X%(bLdFfK&A~;vnnECO3%_;PL8D)y9Td^IqJ@nU&nYCNQYhz|#vE+f8 z=(sa;E+cn0Z$92w2>Z0gSwOi0Fhrq7IhTJlxhaQRAQsB4VsAx73H)upo8?>u?U z%HPoXJbf>klq;i27g0Y`U47-ORbII5zSm=BhZn0^=O3{{MZxK^N&cSkyNg7Z(ii4; zGG6q}4GPj?*bC*2Po2GE)Q%d{t~SSP*cYbj)(Jr;Mk+hRB^QeOpEiqqKBuKRsgvBmMqFEx1xTP&=*kstcx-UQ=uFdJMGYLhdI%L`*OC z>~$w}ZW+GyVlRsQ`NL^fIF}74B zt-0(w!+GH2hgF~IRsGFvr|NAfqu%_1vGF$i-`co$y0+in+(wdUb`jvu?me*9C7Y3NBLl`@1OE&@|2GpT*L15I9OX?&6BllCoG+g|#DjK8Na74P@Q z+m1GMyw99X;)mQ~>&`%an* zaJEx?x31MaW6|-mem}6FJ5Yq9eqagM1FsyO0p@wiRTp%?Tbm9FM!}VP(K~?EQoT2UmL5i_s!q$B-)Vx1OArk%chp!T}$S6y)3_G@aHhX8FU9<<$~kC|Q4 zcZaRLm2a?Bq|axaphcSh#N?|ijJ13ny7G1U)0I~-?d#B%D0Z_3Y|+)!Sw@gPU@y5a ztobWU&p>vca#b)9y>ZBKm>dGg@9^Eu-gVYGlTSQ;akZhHR| zEwFUp)?7NUVlDNTSX#{!czKt3*IkT-&9et;n`ZOSI=1fyDythbtF>n8rZGf=<52b>pODqA&m7Q3+c@`%eG0}txi zckjhkn#OLv&=i3K#p#RTRqT}y<3nEpE`a-RYrudDrul=KxthcDt@#VMyHWBNI!>WU z9mrqo-Q|d%QphkL+SsVtI(;hLbqtIdmpgO{iMQ+EG!F- zS;qq2`NM-lIurGpyyWgk`}CK=pO?X(m%*RO^aWuOy5>ty$*V{`3BFDdoKxQ<+L}aL zlUS$8{J&Pwo&pD4LNTM+~)1*iUM?12fWy543P8lS)r#4jaB1Y>Cl!jCNx5kvW7my{kGn zQ|rGf3T->Ydi|itAnShA{jzH_QVb#J<%P(?w{l;m=w_jnYn&6Df2It&3U@)HFT5So zf2F^Xa(PAZYVIvS*2>boy4F38XsPH$Wc8>W;vH?=4Je%XpO6*mSNJTyb@9-yUqE~6 z`j2k#k!@FSbD>8A_CRAio|T>f-PkT#itJ(UNHA53d1~o(orS;8{qFzoZ-}KdGTF5= ziv6g-y^q9{%f{>Sm!sn>?Vu=p^_H~->3pW`}w#oy8;D@DI zZIy=yFMHI=&bANr8-35x59Fag>Y)#X**?(zZ0KB}+9p1^D>sh^kH*v1cz9c=@co`| zS-ivM6?h*yFMF31C+UU7S zxO&bp;r*#^Uwl11BY$za8k)Bd{jL<93f;Xj3Z1J)Z)U!s8SpmIJJteOAh%A91v({~ z>(Zqh+CW~g$B%9Wy-J`<=h6-Mwc%Yr^jNelt3N>B4EjMRjZS?_S)u9SSrg)ZSKSG` z0@`(Aphuv4?9jO>y%- zc)0c>57(fd*tm6?u zsJrg1MA@{rk}G4-dvsaKWZ{r`Er|cg8H}y)UgmuWUh53;|5M`s|JWi}t)i_e=H@$# z4-9_i&clO`{0VpDuJ89Jf4I|N$1j|CA${54TcY*MsT*0xzgzY~>c3d)>b;}hEhp(4 z{58M@$>4RYW-k8&sTVAayL^4H5IG;d4$SAkd*LZq7hj*$o-PKiq22Ij>}%Jxi+2r! z|F8@H$&JXqPeLo6gjPIhdAA?Y8t`99C;pQqyiy2bwi4@zA;2zhwmck$q1@ZdP> z;^6^4FuP9Sz>>04H~{<)!vph=E*>lyq4`4QvpSKd>wx3Pn3Iq(@l$Mc4&Pz-GoW)W zp6x%Ey@;YL-ne@auRlOrf%yxc&|bEk*ZH?S)}H2^lD$2jXjJ`9GRauUe>pUEU_`owF^>Z- zPosUwwVctsQFwlGUGm`3Cdp*Nv*aNc$10=X*m!X45I9Er$=&k(rVr4kEN$vJ-{P2b z7;7UxO@F?g^PQumE2m~_Zu8e=?Xl!peLm;E%CL{M2tJCwmRWRO%IE3)dl@JFm(L3M zl8ex(cQi{D(vP;S-EcF!Q+u)SOyuA_idA4^3t%I{7T8_Zn0e$mzV-Y*cF1Ri`$r!B ziEB5^tB7uL?S`6f%5&!Zk$a&Zif{6X_)g`N{e|cQvK^*+Zq6%EzWjhHr~GzXPhDO$ zd4;y@ln>Dx@}jcWkr%XiWxwJ+u;0EyzTICSzsSu$*iC*j`4hbI#cuwAS1PA`a+v&b z+4U$Na_j#01*A*d^gF$z!oi6SP75 zm;UbAz{Ew!&e#_`Shwjr#+w1Ej~ z#$77D!#BhEP7(Cff6EkT3Qwh5rueQ=nmJDKEUb(=NuEKT4$lJ0h^PL+UuwJeZnVL7 z0Xdl`Hm|2r2e|_my*Z5SRe7WzZT=9O!&=@4c+WEtK-LRf@#y(e%xGd~j+PywB_Mr! zQaf!&)xYeI`i9cYAC9b5{;UsEhPwUw!51dd7vzpedp56mYu*J)s~@z7t;4p@bKoKU z_u66ZA3lg|TOjyEKc2vSR`N|@uhYMu{qz1T{9xPvn&1h#m(sI-)5(u4i~&RTneRRM z-x8pYJZ}j^Wn1##kUqXoyW%lkUh{|ak+k?t>IIF1KH4XJLA9S=wi9w9>)Q)1W~?XvZC*LD;{*kzb~P$rSvFb5X=It;_Bu8~U2R z%G%9VnDQ=ox9Bv_5%A%BXi_ohk)*{-3faH0dmr%6V(@(fFs-}Z?Y^T+7k}=ZlRa<; z?aOEDlx+!lLpq}9TOGKVnvbm?d`x!Pm>%~0R|jSOV63RjgR+~M%A1M#*K4lOy>in} z6joV&3eKzFi@KP9%ACr3CkQMy182>i-LsiFZN8(yXkV@1Ptk{>?8|w+esf)V(tRy` z-;ky2qUW2(VFTp+f&7GNdsg$Z!m7GZp^3nw(vjTu-uu1wu(SHqKJBHDOPd-0wxMrE z)9+IE8*s?>QBS@p)RS+x8wokjp`V+$Pp`o=e_lCs-ja(PvyHNUBpH`IJbXzC⋙e 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 z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs 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�_&4`lopraEd#;K<+e=T?zn5pX z7dOQFKB9m4syB5P$?{e2N0Mt-vh;PVp}&&f`E4_GdG@3yke@xd`Tc4A`#5}t?4W{O zNnWLUyX957pZE*B%3aWo|AJTPp1G1eEXjv(FGTIT@K7g+H++Q0pIBZ=d<6Qa7Q;t$ z2fVf?Nhf0Xhz|Iq)8y~lS}Xcq-5!r(OPZ`3-*fb5U6O|o9npRlr%t0k?dPLTJuNu4 zEbOS%ym434&Kdv3c}`koQ+iRLpb1A!7d>F+ka&MnqOY=fXh~($OC?_Zk>3B=YnL;J zwvJT&+i+sH7dOQFKBE8RUaa=DAIIqfe3SIfMt?znF8~G_#{t6MneUX(B-CknY5Wu&3 zZojP)m<~}~MdED9zd07R=a_xOgKb9=wqfV(WwxC!t>}m8_q9KqcTBJpyyh493>=d% zguaDso#$22-wfSv7yP`qGVLqGqeH*#H^zrHIwI;TAkM-E*m!w04xgoc=gcBK?R0p2 z`YdrnPKU>*dc%hO^WC+76(7Ue;2j1xP~K4wpHd&$n)=hm8BW@h$TqA;=c*9?q2O<$ zY319zv)-eFY03X)?)~GVuCDz5JCgwij5hW=cB73tDNz$lG1hcP0X2!&pP`Zy~K^H1Nk^)_E)ep3~s})G7Xu;agVplC%egs7Ys=H{l zMTMEq^L5|vcM{Nc_w(8B_wo4tF^_xS@44^$^AfFT zRWafarKgo^FO|4$TDkUBXM7hJ@m)*(vdpE4X9LIa5M?(%<0wlkyF2e znPxDCm|!5k>8^`_M=gHQV{Wi;kpGr+Am9c%(O-HodWz_iJ#)1s%6dZodL~*w{eq}t z`+9&j4fC@gZeA~ef9$dQffBP?;$r| zPJXkki!$Jmjh&)3HGXbvwfo&_w=A-8-+*Lh>p!?VVn95Y4nv$S6<;`|u#iefQ*!`{kJUr)Vr zURmY5et2W78O(7nOLxp2BX#J3xxGOs`V;GpCL{9_(}sz zJy$T%!hKxcvQ+lN5Odh=&S!;eA3Q&BCVuGbtuxmaUo4K$W|21*%I7sdGkP(5Zxb1G zWTjR3Uc+C;CVKz8oHQRg4kps%nRj@$e@n4BS8!iRe_k10R zw(CAv)uS^Lf}w4TXX+fQwro4pPdmQEz@L}f`qfq+ZPAbaq^&)`DlK|XblP@CtNv#0 zi?8zGS8_`|w5s5p{(KJUAStgVPxedyZopG^&Zsod^|vpZk6w)8ahA}hwt zzuw9J=cZ_1cb}x*#BX6TFsCiA58BUA+&58#=Uj6jniIZwG$uY3Lno%X z`A%oRgX*Kj1@Hr%MY%_JjMeg~=C#DvVZ5oQ-j#0ByUUmR1o9rzn&{Q7{~EJDy+LpU ze>wl^kGb$D5e~@z-|Neh@W0_TED)xB_}Tugv^^jir0u%xl(Bj^$p+sZXJf&`Un4q; zAX&j{7yQ4gHtpX!eJkfySG^kQIO*JJQM=av0C4LTZ$b_;f+ILBUzIelT&BJ(KbW@y z*pE`5yHk0+I+Ezg>G&;%yZcB<=f^xVE>T{9@Rmvd>z#;ueDvMWc7Shvsc&;Wi&zKgTwZ6@#k{-R_=?`-4yc;E&g88V zL(7ku0QLI2A7s5IZf-I>wVYIa_P3P%YnHbBJm>SAM_Sl*3p(d-o1)r{HeLeq4RMCQ zZ{oF_u_V8U*aXsb<&zSF)67mb(BHAvw*aLmsuUkYvjo=Hd1ACqK%`VDR`K#S|4mir!Z@-id6gz8u?gieL@@-ybro9uhz*$e9Q>;yV^6Z*9w`Y9b1L*{` z!20UpYo?n0B6}>iUH0Xc{ut*gt3OG`5bg2(>NNOxWv7)F7G?67vvHm;@S{579C9zC!vovgvmkB+_NI_@3tcYh4KMMEy9Gxv&}ld5l;Kzy`7YPI!~ zEl6)?%*1J7oMG;R)0y@P>;FmKo@rl)-=}%G?w`PQ-C-+VN$qpH`7UmD-IXfhtov$Y z2hPVcX33nE7j2hsO)tJR@E!4`=(cU?s1Lr$7*hX2tWW&cqcLn1AYA6=yw&jCiu7%= zS&WR);aehEG6Ss9?FD<-8+5#!x0*FRxbSl5spe0<$(i|J8~c!jtRrsT&wmAOKEQkI zZf=>&xg*}m`|m!0jBuaDi_!jhqz;3s1eM+%z~NArHT>3&YJk1V)NK`xG!oGF?twBHNKVW zTFORei8Flxyt9&d-!Sy{uTP~1Ms0p-Gj-=*FFgSc3+8Y3%lJ5#A~r* zXeK6~=sh|8#?Thv2tNB*i-3%mhGHCPmTmCz1i)3!M9o6Ht zwWaps^@--x_jFgLf`0iUZL@w5H&69hoMBx2|65DnWtRkd#&6poo|d^JuQSrh_{w(e zWu0*MkI%M}-&|FL-u&adXyyoB)HH$@)p@)qGJs6ni#(7fKHfn~Kc#2#CdOve-|ofY zPutRG@TX;>^|*OazE2AzKS+n;##K=4h;(F@)q$))pS~<^p1-zw03DC@8M(yz?nRyn z1Slt+gHL<9#@v{V`X-v&mL|5v96QI@t~htRt1)}Zd2Yj>Bzv;n{jc1fNzR^fo^1?? zfb~mPJ#q6>>;Eb}g5zsS3<=~9&JRCN-`V57u*>EBR-e)@*=H?s&o*=-Y38xlp4}}G z4l+kw(6g19s%|xbOqmH1Kfov3t!uY0omA#yj)H^802XeXR}6@*li`-~%q^Vbg^$V} z!T0%kC*L;7lk_9v?U#Y2m7gcYdmDuI)qcC|M~H}U92$ayb@=4Yl(PNz5@Ken;GW!^OxD{g%A(w!5U=MJ2UEqP+%XH(j3xd*SgSoJD4 zyw1_9et1(Ib=o#mzuHMJ?zU~{8`aso+o{vmMO*;(GeO{p{^DPxZ;2;>B*+0={`ZoHmG1Je+>_6KxpTesrcYtTWT=3_;b7YDvO zgP6CkYwptc14^Fv)-2xHdoxBns|@n}KXZ47zmimUFxfJmJm9v~1U`dD&!152w#?>PBdlmG%oncYv7aWnd0Ep z^xQQU3#Nj-@JIDf&c+C_>ojY1%lPa1wKgeE4(BHy=vdEPa+9pgCp&FqTj3X@i(=R=bxyohl>cGzB zw!1{KKuxQ2wgG$#fZOsv4OqIX0rr=O=5|i#cv83poPq=ADi(g?2>zF%CF~A@S!x?G zGmvBP7r0)!1@S2Hs<_#HG!yL(EYGqYAa76K9h-d9?tyb}+P!lE@DVL3o?BQp6!aDY zr;P(Vu(B4qlFUWS;kt2lUXpd!Sf0{yU2O6V*U_IYf8`C=?VJG52}}QrOdVhjPTm8r zNiB_7SV}IJZ%8tBz2uG~@Fsl2{^vrE#ymRqXzc%n*QAs5@sIEu!Bn*Dwed-G?9nec z5f-jGyynMa;8!JH+$iRKT4VM{+?6i*<412hJ`^YWC}VwzTgCyGaRcYxFmC4rTTdDH z*z+G{cbriVdM@x4`160Mc%suc=o4P3pZ4x?^S{ck>?~kk;aB_!M-Dzj?05G2iO11- zNY~hF%Awsl=3M>``H6#9gA1j#=&PmoD;F*x8-ov9Urr=$zO(=l=2x`-k*)_`{e<3&;mqPYcE`!O!{Wyd^CS>5J2I@>V1p zuC_EHS*m6H)XA}b_EpZ~OkT@nJ0}ze*_S>8-wD%aOlDEzcT!v-@GQo*i98S+SKZ6LW#la%c1N1tLn9`%Xbeso|teg zjQmKIvMZ;`HGZ#+pLYKc6)Y@;3Zg9#e${^IKayW!QGh3pc|3R!ScYOjd@4x zIs)$1v>wa}KgBzu8_(LKHKmRFAO7E@`;j#7t>44}Eb-{cqoq5?6CmJjU%uBiGR*1n z&Au~ak4?j}NAfac5ApCEI93Brp7HHzyyX7}-+@=7IkT#GMMa9CCE5|sjc$|OY7?<) zvT1PCU6X$QdOF442R>wB#`v1suFEPPH@mg>-b_r+oMicAsrHNOCvRt;)>5>7vi9Mr z)p5zB&53_-`O%B`>*b>D#HF!k1ZufgA$9+2?UDzgy|yp;sjYjP6x$YhF96TPvqCj7 z$@=5I2QQExquvwuR{EpIFTHz*qhm|F_|wp=vcXq-{uDg^9qY_zkZC3&Pq#FL63;Y* zZad0;Gd8Kbn7K*}Tw9(h+)*&S(3I8`GWNpbgMx+ECa~E|yTOSsNp2o@zU4t~yK(a` zTQarGHFZahNxp^On8(VKW8MudhrW_#Qu*{ zvLBdKp8SiOBx}U!Yr$>A1#ZlJZXSIt@L;6=(_bI9&w|hW-Wi|mw*%Mwr8-p&!b+g7Z&z$^Un_h1Mj)O)(O0{S4oE7FXx=aAAgx<=cmd{qj;Ij==+N%Q2o+ENtYX^4}(;wlH_$~c3cJ3SKPwd)q{=@&2Jh}|w zK;~xtZ}3-Qqc_Y;So47F8kkd7V9=2UtgMPI723#0Z`Bmpif+Wl&B4zH`3-sjCO`az zbV;He@nz&dqdHp3@H-^s|Az$wV*ES?{WMA5kDF&&EzOjG1DvZ#UJ7nPJ09IMZ-Z{| zn@g>6=?3124`Vp76+Qao#&%0D+tEpiUYH--ze&fNv|cI}uF_Q1xeHQNz7_mel!;;! zyS4Ep)|!`6M;qFO`})tFM@Js`;@RL(_$vD9&8J=aCk}|$Kl*fs&*V@1eRtp=&d0ap zzFlU*CF~h%O#y5EAoT~UE@W++@o(*!={7!|Pi;83WZ2(I<~}|s`+c&S^&L3Nw{Q7} z=&U|uFVF>@0Q1y3^iFG#Ekq|1f%(Y~L&;ljbZ7**5E_Z3j-F^rt#05BZpt5Z=tFVD zb>3J1C5xaN{#QIU`joGE_r~e?1ZgfVsb`!c=>tE+A0Nv68FR;&9Q=I|{LK|+A`ZQ{ zJcxa6;6M-WsACj$jKJ+5_hmk70^BX?$Qqx8H+;pFHC}UNji)4QMB9b$!d1n!mcNDoi z3QUj}PM19-Z~S4|gF3Y@vvP;lICPJtNrP?z`F&19SbBxPq*%b}7dSssklKH|DYD(h zy_F6PSe+O%OfT!#&=&L;Me|`0xw(|W7-j}-X3!V4> zwyUb)`EuTuyYGwK_q4YkUk!f+y1gLvgq^xy-bcQ~$?t3>AE}BN!<{IS`A%vLb#z+? z=VWg6S4y^KUC(D8f|8}tH3gDo$D4TOtO+vbnBiYbeCpMD2b|FH=e^?K!NCc$8rm-> zUpXmeK23bfvE<`DW6U&Qh#&3LFQ^Xo>1nSnc+>)0x9ZoA|Gj6O4d9OJp=WIR6VgXV zJiqC`SY;DG!{>lKujJyE2J?4H=QPBOQptSk8}l=}G1vQ!bSTo3@r>RUKDOIb@;`i` z_eJSTBE9^V_SsmZvbV6u2PZyzzP@w*V<7WPAa4HW!_GUq9*U0!sSBQe;Zc|87~wJH z?$JI4JP2pL${rLPNM3$psHGHny%br!6gizW_TDkhoMbMcoiut@`kN_5cYOpICdfF% z)5e%WyOuGYISrQPENogDd{Z?><~B`QP!-FiNJFGK(tKc~wds%kfNLeNw?E)k3EYRC znd`~T$96uVHMjX^O2;Yg=9rfFH5WHOICXO37(B~+=jv)`H>SB6`Y}E)|9>~HQ@3kA z!Kt)TPbWk>z6N?YM_DiVs!lQeyz={^^3G8Kt{iM`bPy5J$VOygUrwVl0Gf(0=AT~ z|5+K}73s{H!7JxiW|~{-uiGl!BK<2WzsT8hFn;+tK%Z-F04AK@uzUYbx=`|@WRha{ zELpZsu(bO~+uvH|5TBapfSKLAx?o~WjJq&e3kRgj+W-h|$G-i%mXQ?ZnHo2!_>Nk|SeK<`YJ1G`3{epq?iLF-mUlMg`9$$gg zY6rTIZr7yO`zr-^*$!h{xj!am*XWA$)7S;~Gk>EwJ5kC!dA}fYRW{{-zM=m%hMX6_ zh}Zznflq^1uEl2tI85*2`^%XBdDsZKN0asfiLN6P+?eU$`Tm<_vxZgzsrsW6SYw>| z86&xdc9{qGkNr`m`ObRHPcAl{IkkQ`OJ245>*Wd2de-OQ%KbOkv0>xj8{{CT9?err z12O~Y@VsT;QycR6gs(N*c`7r_&CjIR$6~Kvm$#$?+@+tNPb7{`pbb}d`;(#AFAi^c zeKoQ|NbnZ^4g1`^SxSC0{lfOJ*2a)^ZH{5#a5c8OC;2YYHt#pmy$hckvwaT<4w5^f zz1Zcoe_$Nxc}6|~@#Z#k;H12Z%a+#MRxpveSCJ2BUR`*a)LKiMrJlbMJ~D^wfAuCX zeK$6F{F_n_)-$G@^od5eh7E9u^*0>y+kE1!q5G2u0><8(3|;BmRHRq-e}9Q|G;z~g z7U}gH`Mye@95?H}*H>(US9D!d7sF@ayM3IoUxUvtd-~{hWb%#pmS@c8oqf*(Yt(+S z=IA5fPWcsG@hdbRd{Urq+u6L&ck`chzS&6pVZZhFh?^%A2iEe@#KwvWYac<6 zDLzU+M!7NciS_BkSE9VNTWF7^b+D?{(NVCMDwq(+0lD57prJU6YyZ<@9z*}=iH14>0?0bwSsB?tK zpgOw}7Tb|DI(EUggWwdr8#@Pk*Y0V_vPe_+8{Ku{{lvnyx`<>6d+btYefpC1L+PFd zesgCacnn@YxdS<;*v6EQ-F~DU%UkcR70!1Yr9I*LI-A<{PW3@!CsiLd&}pg<`Pteg zojS9J!3}E%k92O^9BI3)xG{UM(`!d#boi&GA=bMeKCim;t+jV?Z^b8+4M1dk?r468x&lwrxhQ zE8B$QU)^eqH!ltxPMs!?IJj}@Ve8vNKk&P@cWdZgjnuOrze6K+O!3fGPEuRygWBt! zhwlsP0J3u7@}s&RY0W5jp>6++wufwiraIm^}?KMvh*{DR{vRBLh7eA%V&2z5LGS-sjws?7h80zT* z*ho6UA#lDhvC+2zpU8s5&WccCV?F%j{#xr>fnS;4+j!{sM8?hc*Cg6L9HLp#x7rtN zqbGSleHR>|VSDE*e6S#W1RCnX&c*$Qz_w2Ft^Xr~&ir$3x~}_JceB1tmc^Pf4ci`1 zb%mvy4eAb2^ix66oor{&NVs~?#`qc=d2?JzWB?uMKid0iw7((#+nWgsNAv*c+t|Z_ z2g$O+8+7MT#XR_5TYZ7}jQXrpI7(agOkSpB=)SG|Or0r2t~R{$V(RE!5$}q+XQNZ% zJM;pDN;MD6eSVemO;LA;O-RY_ra03QC_1&$^;j+k6pE?eXr4=hkdk6kUo?xARje4G9Uu77t^bhzJ z<6m46c`!H7eJuA@xcSjsVCTWT^33tc7tR^meLbQJ%QtEq|3bbk7aq89v2t>M z2E5ii&w4(_`m3;ZrrWH3X20l|Ih>hVTNuOEBmYR^9o@DZ*i`DQB6v2&(g5dl z(B~xG^5w**i>VKMQ{sN}i14^)yoIq}@D_|EYluc{%tNy!v7-23ukhB9T zOQz}eg=c5x0Q#a>CfVm z?E~N0Crjq(J{C4rqmAzh?%#kP2C!v4r0)`)@uo!QXBKqdWAXNJ{`Ykp3QOOV&w3OD z?<&&$tnbj4VmQI;i8*26)2+3urR=N^XQ9&+e+546Ui|X}al|S6J-6&^?V+N5;o$V6 z1rw(qj-y*YGNGy#eu4aySo%5QXJLnAuZT=>kIqTqKesn^6#Y<)HtHEm1Gb8b;uBbJ zhub6%J{{H^SIr88yPJg{F{{HWHXE4Vos7fUnVHXW@sZkX&fHkOT6WvcGv|geEa-R& z9*Cc|>vs_2%xJc%EPx)HH6Y7Q3Xv`L362(a z79OXP@=aCTk3`!A;I_X)bVphW-<7V}*MYntJEVN>qdd!|-OW5Wur}3#GdL!A2cYpd z??c#eIY)=Q&s{62rJVm+W7=EB(&q;HtUG28pl80NX@cZ`;84Q7jb_1XPM^sKQ{Rc{ zeI4IBeTLUz`#B3eRS+DCqeH6h-h>>)`{@rNLt~3dj<41El3?Q5+6nNr5c8EletMTN z1Ue=|BhUQDd>c1^7U$oXpSbyl+oNgvS$(8wdM9y3i#cOnOuS6aZ40j)Tf)f?gfHM+ zFbZ5Ul#%{AwfYD$bZ*y`_k(xXRxg>vnpkV%)3+5&l%6Sd+p&=9POT?Bz`K_ddQ~e zs4P#vmH&>y+vV57cd4Z(pyk|r$KHewj*Y|jaI6oWo7i+kdupl0XOBO>6GQC%r~)s*+0zk=j%MA_C^jL{q4O~F@mv=W8b5@V774ve}Huc zSfkIw<}Tj(;qC5xyJPq|vXYqr-d`9UJUbR0fM=wLd+vT`;~F~O5p%}!pty7<({sV< z)Z$;a%Z|uie2+V}Db$rOpJ>u$9v>DDhNt))K5XZ5!4+%It^wy}c14yyiezci$oaN#_@w!!uC1cq#0K`b&-^YXU#Ykm z^SnEz-o)gy+avIye?Yc$_IIOZ@oeL?#5qny-pbyHr4N%o&nXj7?5a<@d7X*2@h~a# z#QJ{o=_}Tlao~kF_xMHUW>4Q3tPXGVWfNP+hq{F;Yd9N*Ot16&iFKbt77c8aUE0ny z=L#L(FMZ~N@cvV+b{(BfAfoKi?UD9^=omv1J&;rQ}OpxLiQK+|8duzQWD>$XB}GZXy4tZhj+q>wkiM zg}7pQHdZV7=PfP*EAeNZFXdi8t)p-C)#Rb;@{7lh9K###81fhcGO!PM`Fr*3sbuR> zInjx4`R=>Z%Db}Rd%f$-zDTdn)WrOzh8P%a-0P^=-fdOB58?5`?>3y^RMom zu#eiW|B41SD*9h@T*45Ih@ z-qIQA#_X~_ouN9dznf0$Z+UOVo*SP}pWjL(UkQmewcgOzi#U_ko$j;y^BJm_`mU!< z;$`M76JC*b_cQ+9xcSE+-ItXi*+)r!Os~%?amN|5=jx-F)~#Q4n2EmbJsJI z$@>&@L+_iretO0;_a=218?U3e>)VRy;nO#yey98&{~y`R>VLJT!cJgTu}{=@fAGD^ zjpxTYZ^wr(+7`aC<#nda?I-Kzf2|zlGqlTI&Z{FZRL6hvpY+!1_sXu1_xY3FH7^A& z$ZsRZ_Q~bAcj~`XZtBO%H7{ij&iMAf-tK?DKGsqzlV~5+kG{k1^U&E^I_)s#yN8i$ zSySPIrKxqLu^4`w_AHf+$u6F`e2gnE1hrp*|5^QTvi^fdWD`m4#-C}w_`>?fO{UR!woc;SY^#@76nbb}19k~;UomaOne@ecufA_@w2Td7u_^U24-zHzK zzLS1_`9V{`ukyzSO@!Y>e%1UYb6zAyTDSb5iSwIAzKOJ%-wb{;`L*(!{o{jXF2CFO z&F6OqzXklhvHYM}MB2UzKV7)D}kDI;(booGefO#Bc%mjR4G>`D`QudYkk=M*OyTM(~q~=%U;Xgo`>+fSe z+pHf!iCgB|Bg!<7C^Mt`FC88+Bx@EsHXEG*2TvfDm)|&0f4{;O57H4Gy*Y~bul>2P;Ry{SrlFVfK4DOLHrLV!|M}sBH8AqljSSb7j z{^%Zpg@VWOquch-U;Cc+4mrB~jxmCb@Ee;>FYvK;50&rV!Mq?}f^&)?G84H?@IFcW zEY+j>miy>Z;Q{Rz3RZ$4u(7f}PLUFHm>i90k&r(-$<05-A-G?WOZy= zE9RjGLr;kOKgJ#J2K+y5J)Ak68aGStgeU8qsy)97|M0VqFY3F+RPer1zJkCs%(Kp; zde6WLUm2Z6m~0}X@?%es>YFraCA>V$9(Plw7@n1a4a}3z|A(vG=W3p3^W5b=#{@U% zuaj@<9Qdt~?PG!VH0XMkC2GzVy{=f1GVR?N*rJsQPkoWKCE<=MF5PToklz5mK4*+^ zQ-V$t7;@HH=XRN2A2N3d<1~UFd_GNnl#}mn72X5qP^Q-UAsG6H&rmQ#pSsb3?DghU zaMoN7!}sJSw;u%>Q+i%dGAuGQ>l@{u#RAdie{HT#epPkBC-S2Mwm;SK9}+*!j0aJ6mG4}1S4ZJpz`wNJW2+LP|aNOqua1=xBEojuzU-M8JX`NEd6f<{4xb)~_(0`ceS-rR3he`I4^e%HO|E#N<& z`5@8@%rd~r#tl2?9^kes(hol9U0Yxge!yGYHY-&Rbu7?0GWEeQzIlt3x^hi$`7!?j z=F+u&qiYcEXBX>kIPsc6Y*DhYR#>+7XAl@d%>%qqicHG@@C;Xyn_FHk>#YU6I$~u%)I^Ldacp7JR5R! zlKBs#Lks}7gxX@Cydkl8*CW67eljG02GnV0;`cmVEAL#s^ zYyoll5@vo9sUPB_&}Z9A$IYM26MXsaA@$kn(C9aO0(;2(nV+;b6kh0mcMTna8ROXJ z(T}SG^gDQW$!?d1c3Rv#1a9o0{XT3sX?!)5 zA>Sd^l9fdsj}m|4-0uSI$+WIPm-(4I|Dv8T;MX|+q7m;F@NPk7JUG@7*F0gfrX0_W z*~Ayq;$OhxlXzHvAM(^N9hX4IPF%Iz?^ii=4Boezt&Q1l9sN(}xYxoU$M_H8quI|s zWr#j6xxl5BMZ^JB{0_w&Ig>s|dpw+Zm#t(eK&HJ?fvFmym**?)pETYcQlLc$));)nAIlv=#E zDOvVzi1VTkfsYFup3i)?N^jzi#m&8GV7F&z?0v|JtUX$XT)NQtph{=#zkM4%8q-U&{;3ngoe_IbL?JXX7gScRz@wH7zoGPT(CwqmbF zt{yHIQ#l*|Loo~}d!~FaBm=whL6OCuGTP`ME*_A&02o-J7GJ?qa! zfPcKBt3m24Tt54k`- zg>kcZ&i(!DRrC)2%loUaIYQTqujjqu&o}G)<$Oz<#XQe&>r>2C?m4&b$B=Jz^1nGn zezu!`hx}YO|2p~kZvJKR3*7uK$S-p9JIEtnsm|X#rvB2O1bwr8rC-c*@-h0gO#MMd z`MYS&KU(9^H~i0ipTGHhYVC;_`1_VKKmH0%TD|4irW{y{-+8bWjN#Azo?^c}3zqIc z&LysL@>1=u2Jdm}Hbrh;ZH3*u=C#DluOeUS=H~#PBA&DL_c?z|qmnY=r%$41QSv0C1{^Lp%y(SB^%!*Iv9_Sh&lHs)L2`^MZ+ z^<$~i-qCI3m$~^ZywFVzO&b3^Mo?;zq#cK9+j!*mE5wha9)ut;`4y`qOw|diL$!<;8Q-9prfUPb5 zTU{<+o14K_j{eD)T8Eu^Yi_e*a9RHx#_7n`A6Z=pIQ}^G#josYzRC0NV=Z;yWE*>*=yDSP6u;=@^=s1V}}E*TlJy78rzKZwO_$$1Yr(YX#DUT|zrF_i@e=rL%xrqZk<0KU z;alpe@V@7HwUzVL2mG_pa}}e%zNg#+>E8nR)6%~X{j>UUYrhh|Ck`a@ zXwP~{x4Ht_T*6u*zMtqki~ZS~tCk>tjCk%!uVoJTzcF{@T9*e}Jqpk7$S%tmLS5DH zdDo7m*unGx_$<@D*J)q68MVLG)z74mr)-^XECe5IdyTm_=8$*WdgJ{w+Pc84Gd@%& zdr@2Ge>$c&o<4m{Z#*_UrZ*S%%D(ONnf@w|TTi1A!X-fidg z7oD+uSH6mY3~>>X^-VXR8-uTcXXv|uZ74bACL3Gev@&-S=WmMD|NGF*-+(VzVK3$T zIHQQ4V-xsneM2RexH=cRcJ|l0@ONNUX>AwyJAHiqUq561U+*H87rubl8twkWH@|k9 z&+3z6YyEFM;LcZSKQ`I}rmivfdZu@6?xzksUtg16o7?Hab`1Gl?)!(yKj-HA$nSCU z_dY;;G&f&G{v|j6dGdSRd@=b~-2D0E_qq8o7m(y7_m=zv|{+Cx6(@zfArO zH~$OzaKy>Keje}Na^F8j{@>mFrcPqLx%sz&v;12X-%)Xb0>s1^9LA}`)aT9h;T`1j z-MUA)IPL7Oa<6Y;&;J1Rx;TIMUY#9?t<4?heK$C3-&@!&HgShOU;BXfqg0>!d~`%x zEyHacu4iuCc3yj%{HL8hympAZJxkJz?i72$ z<-CWMk)OUJz4z(7_G7&65O+6wyzbClXVBGxk#sf8Hx6aUk9PWTXw5JWIrJiV7q1RI zOx~UAL*hT~{2mhjap&|<75NL@dOlCyo!>*nqBG6f6jS7c#6C`?}P7< zzs!CAI(Zid245yW-hKZI@-99cI*+`I4}*`9|AJd)6M2gp%xMHU@+ka6I;oG*73Ey| z7<`(1(Ea`i^6vNtcb`sIhoDd21D*}Und8AOm$u&Gzh%R99@Ka&JvHVIYCK;3l9Rmp zuP5)q*Oqr_$(DCveNf|fY3ZQG@4|g>1gsD4=e-NxLBZOCsbKBG@u0@#!p^qk!p^qk z($c|glyPb4;1=>OEgjrQ-le63^}zKS&ZaetfCIAs!K>t330+x#8V8mKFXFptZhjW| zCO7|izPB{z*_!zNf(Um)>3`ynLOTb?xb+QE_BOAr#@vB-@Z)mISsFWVD79u#?G94^ ze5d{c?>p;Y=X_|#S*K)e3YC(KeHhFOt1)1X)sTPwHI<-CA7@ z<639=#h`RP@MVWq4?wHPCf1I>&!g3j6GODx=j!p+IdVyKgUPqLJnRaNUj9HCa=6u7 zI&B`fAhpiIZ_w#`$D^m!B^hx26`mg2>UrX3;T+a+u>+m_C2E_`5bdNy(EatFVMZag2mS_SzG`2zBlcbAV1^qPX zMAFZZR+E;HUPD?)T1R>z=~U7p(m3hG)ZdQW6~_MOrw_6L=3_@iw=5qmQ_VaCXh-dz zLp!qTseK>$h|~Uo_sLfiS2j+a>O%pk`tWH|^#Q%;ug|AUkTTbkA45JLoGFN#Oy_AZ z*4YHkA|#%2_E4I)xVimB@TGLb8X@?s5PV{5%}L(loBpY-J&ZjwnX@X4+3nZoX{Ve# zJoP{^`3myY*@1?y|b7x?J{9;%GxL0@nudoAK9k}*mz%qm%NGp2Lr9Dv#RV*c9EY=y+?X#0`v7B z?cmQ)Sk3#nqBGtvh@@oO+OIQVd`tW<=DDiuGbhMnn-6M_$+`T(LY{$DP&PE;q6b4f z>s=8)d_%Ci9}JepO=|W?deInU&*DyU?h|+XxLsSz@hCdUPuM?KFRbyIS(MjVq@Fwn zzoUazXB?L7-5`;|x!dGMHXfL|llM1XTKvH{}1&i~Bs z9{0Z|(y5?v_xdWk$tf3T1!fkuj*Vz>HM&ESn9Ui|j^Q(;@;&l&hWgIxu`Dk5E74&U z02Ar4LN&e4om$*0eFL~$!h7)d`HR#pxLOd~ZdUMqtM=-Y=Z+44fz^A;J_C%1N4j5o zc#Zjr4q&n-x0mwEGCtEvyo&4^?H)SV-q$gTE#U^(*R%*1Z|VwP6O7tPf}+>ZW} z`}&A!)pH4F1ZvO^!%v`fLm#{{ms6%l@1--jqHA54u2KxgUtNchYqc;uCwWoEpQE4zOAtB>nUZvQI%aCIiPC(qECl+(Y; zh2wp>)A987m7ml%>gjGN`^n;}Tg_(19Ezmv8u&tAjoG}V##cyP5B1deiuhUEDye*G zN=PfKYJ6Yve2*G)FW<{I1JpAhI#fMdY66c_7XBE}Z-6|!EwIP=KJY?SP2k^0eBUCN zI=uN?Zw>Kg;x4!_Dp~)OsaW4;%KOHd>ga(_f!}|*AQ>g5D1QFr;&^wNG=QzLS=S-iB%B);op!!U55jXv^W9-#qEz!s$40YRLb=)%UwNo^||( z;1jF?FEr-%^jLlGc5J(r=k3Y3&(DE7rLs#~yal&sHQ<-TGkq­%R4#-@2@jU9al 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­0!eYU+cN-?Otrp4sl;#g$XqSq}R7>}f4WIX&BIb$IfB7VW39b;U`l>gTgJ zv@FMu-Bl=$I(Os8c9$P$X5E2a&PV!5?N?%lkk4nmZfOrmu zvfKsP2(92x^;$wc(r%x$F_3lano|i^93n=___->@M_qS6jx+2782{N8g73Ndu-@ky zrnjLl6GuO38=YJ!NTe>4fwWHz>INR^ClSK+>ih{jK6gm#qO2$bj++T)+qnDO>5vxo zFV-V)C8XB^9z1{iBygjDK}J(J{n+G_5&QhyB`q)gHf_98PS6jb8Tahx?*8Lric4f^ z^-)$rSEf(ob1BPpB594r_d%9D2Vf0+XwJdAYC1#rBXoBtj>VQH`L z$KKn-u<(rpx?^@{=-fXX!sc2A?{w`JZB$^U(PpLx-cp+ry2iVoyI+QW&v= zC{I4%lS@481#AD^*O<0K{PY)^6n;;HX^j%^d!b#^gdc$|HZHbycwuvxXJr;sZ(kVk z($Plrja{7g1=@J%(w+{c=T{^gt_e-24X4bM zy60$hb?iPpLpsYLUG&JVm_LVfbcZxcKWPSE!0&J~?GWf-J2LH~N3{;Nx6})ooW4mN ztk2r};1PvY-F+8X7yUkT+IR=c^gnk{-Qx{K!scoSXo@-WZs zDZ_DA>!2=4|Ea`L9pXR*)^E~fIDTWc(RohW?Kf(11+ToKj}89{&h?_6{6@FJ`Sr*> z>R{gr=(c(oj{&{upuE{gpI0dlKK=4e4qCn^EN^z=H$V&Rrd+S!dRpq~=}V_%-2aVN zD`YZ*3@T|QF8Bn)4##KaSKyeTEgTLy0mYQ2W_oYg-pnV|_v11uZB12`%X`uVi`T zPx6L>ycbQc-TchrQ^|zc*ca=qEcpM_0WB}E&?g;Va0Wu|L9rn(nmi6)q%7nNE=@Q4 z|6+rr13s_m2NXBur|_13Ow$iT2AZz?zjzwyx8qmU@(9P37meq!D%hRlgk7QOX7f2_ zXnKRBzl5UBZ=ruTI@!?jh_&TQbHcnAuWBCac&RB&*Dnl}dft#lW|awjbhdarqWFG6k3OXoFD#)?fC1Av$Pj^h#Q zppDQnJvcW6N6lN?zI08?IiKaF5@~Ieb-B;hX&!CniwA*Yzv9%`d!wwseYP!xVcm?u zuug%;J$#E{=XZV9WU}Wd@Bk-m+Gs@SbMpbD;X@n1F~YKpEor-d_F%{#WlFoodR~B> zMoY-stT-88e)>4*z*XjXs7G*vf3}^ghy2RC12i-4Ls>o7U}=Ek*hn4T#km9N#q;Ro ze9@uLo->}$AbpdLy@D6zo`=3LJ}0=Vr>=kYJoqDxlUQsrJv*{l%M*_EYCJk4>pn-U z{l`InnAd|>@U}^RIbu3+@FeO$-I#ymFe}nL>kFcDk~b_iHg9cxgmQw-5t)I1krQ~M z9hLh_@EOSK9Po^LXXjn{&(+RQ{y55~t^8G#KT%e`tp3k+-=sWnNd42w;{}l30XbTb zG49VDMENTG_})|Rfbjge^NMp{3);I1zk0oX?t<#8q;;SypHD$%hmntVa?iZyk8>5u z4KYm|(le~M&4w_hAnn^~<@Te@*6DbpzmW#eLVa<#IcFq&A3aU{SYyN~cX-D^)>~d? z{~=FRnBOwh+Un(Fl78>h6u%)6!y)Shg+HzE3u^g-2n&p(6HQ%z+1j4^$iDURg!23H zMZNB7M%zq+rV->{u{6N4pGtqSG>#1~15R~PU;e_`V?u{sN$;UteN%V&vo;Fo!%mu<#!;^X;wfRVgYdL=yJm?rm@?w50IEA|KUk)NR4 z8GR1;qu!5e`#D<};60}0Fejn@1$uV0LFE+Ss8$@56YF|;LTMBo0EvFrZyl6)Qy+Yds`e5=3bm6e#b9Kxz{5PwE{(-bx3(B1d z>2U28v>n^w3VyC#KrijLlPT#FJw<(v{>nOox}fJS{^DTVvo8nN&d)ca-f>UHHC}t- z-j#LjDp$})eq5TfRlKqt*++!$YVA9&Z5{2doa^@i?Wm732y9Lr=t>(d;B@7jTxfGK zeqlR}j`6>_3GIn>u(1KA^KEKP|NIHnxnQiXwebSwKYtQ+T6_<4e(n*G!vHy6eE#gs z%dw8ITs~9G_2Q#R39+%qXpiY*Mp8ZW|BkijbJacbQav_?AEVy~xLJ`E!M?0xTQx0m zo8seK@K^z9uq($p@k^)94d!TcuHFS1!>*AHQiSa1Cu@{qi!>IUM{*`WLZqtBI( z8|;T~QXagHPV&4qIrB!wwJ*?LLfbf-5RA>Jk9?D6-wueKWEtpAnH`v|utEPn&boSK zKk*l9wT|bjb!>KHa`Z{&#)I^G^z1xlm@jvi)S7 zX-8Ts+YMvHWowIHM;>GujN?~CF5Dk8gw2Kdr1k0JZHiauMOn%|=zE8zu782Fqikgw zWBk(pK?}=1*MMj8UJZDTRVOST{4=Hzw4=`%XDJUVayR@R@j6pYO|) z4p*M_1v&>_iAQh{-v#^*Dc!WEtz=ts+3 z_8;NDgV)hXeCmO=YuD8E->07l`TVRn%4|Jo<(S1E$0}J@7oAc2`}-YQ2le0!fF-SN ztm7CY@vu$U%X!Mu(GFZc3v?W9uX@N+n{oIj{a@04T=7n?+xE29qpt(*gz>0!S)TdO zCmIw7^+*Ol@Hbd8d zpM1V>+<2WcgX}yxiVT_*=1Ztg+VozN&FtXBZ25v(G@|X!w*TTzz)s@ucRhhiY{7}6 zps5N!%n39Zv_kj3)U33!9lq28{z0Et7KhU3libJmrA~!oyo-DYPiQO3%ZKOD83J$P zK_$Gd6F3_10h_q)_x;i_#V2?&Ur7_AWf2iAkaE>&mWPi$+H)(#EP6HR~le!v|o-d!^6|jMx!e7H#Ej(z32G7^i(Mi5y zqjuuUlm%l1BdinY=3jl8ZO!y?rpJ5v)n3}nFJDu7I3~OC_RG)}%91`vFuu(V+BGCMh;_}M0b&j)(w$M|jXm3HC- zY|tj&Nmpw&b}Wjh?NGZRa?barQnL5yD>YijS3oCaauTw`7(ybR+6(*omF!IUc8IFk z>aQFKVK@(^zeRs4#24?u%Q(`{zGD6${hXIc_oU?A&%K6Uxukd~n{eM2(){@f@@NAq z<%lw}Z^oq)x4Zv1Xwo?fT_;_q{K~6Z*S-tt-_U-P?Lll(ouO|<-En@R3=ZN}%o zw?REBV;uXF!kaTZK-Tt(|KR}YyB)tsHl8ru|3m6N^k-~B+iw=yiMEP!OT^JKa=^Kb zWq){8VM&X$^&3zQIwN!;?GG<{;|_F0V9*A{C-<-S?i=iWvSl9QWM7@5xc~5)@~UIR z08YA6J^X5+B7eWOG5aR%FX7pzFxSLOZC2)>mU#)h2#t=W80tGj*m>gTeD#dN?HvOS z;FbM5X8;4-1}FCEt7Z@A&wsUqdYb`XuYOUPKR-E7)^Sf6PxLJVzJQ-JGgb#%y~xe~ zPqixzUqw3QOW%O|f@nid5uSCP#eE0M6-8#JOeT)*Pi<0Jgmf3@x%Wg*9aH?I{nUik zL4Piw8!{wbw>Ew1yuwTUQfFOp5%>M0t-e+j(t7Fwo~v1Qk;w9EQO!FAKAfJSO%}0@ zzt*C8Uu%`RG;NcmfzJ?rk<16bmexGBFYz(%Oqu(!Fo}gA?~B)veh%=E8*LbIfuBWg ze)zTV5ZBjG*Uye?Q9^hyxj31Vwtd~;4LnKODUrnhVZT0C>nwu>trf7MTVJ>KmwwU$ z`cIp^Fo@-FK#z|S$t$^PrC1Nts?7*5i)oBDDCSM&?g z;o$d-YQ3b@PuoMf0nc&e*m2M9?gWo`uWArqm`@F^6o2@Stj`59S_GOA&;Ez2)!uxM`#n`A`fh{ zKyGbFHyo!syt?Lub-DSlt8!S5D8^UbHEHe8uQ#qP|RB|Iff7ZHqaJ@H^X` z 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 diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 index 9e736e88382bdf397382f451bf50629496908c01..7163ba8b3b4d26729f01276d22db116f83dcb410 100644 GIT binary patch literal 382048 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 z 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 OFkjU1!G-Ch-2Dw3gLqy5 literal 382048 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 eMvY=>x_}Qc(u)q$A7a3?e7=F9>Q`VKz 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 K{Nwpfd+{#<6(5TL diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 index 82ea0f8be66d999a4e8eb17857d285e8f3652bb2..0a9a338f78211f67d4abdc0aa0a41220c137f8f6 100644 GIT binary patch literal 1593408 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 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2>gpBP+NZfXIyx)t75;f#o7P8vh3{hg$}l1KmY_l;Qto_wdHSb=dzF;{!=Vd zZFynN7vni`&(o}3;yT-Y^Tnw>7}nj9Y*m>wwX zlD~(irV6`Ae%HYC^kn()%yen$%_rJ8#wPOST(DWse`>Du{Hurm6bfr*UX~yBx5s|4 zeFgou_-ojsX0xrJhn-}8XGLuPH}Jm-g>CfP;;&`@T9yBjY434XA-Jqte)zx1_}7!K zxh4Mjb`dN-Eb&?we2G+p$+L6Ec&+xg>R)eJe9B3EfX^>`bKYILDVai{`0xSs+n)-v H@)rItToH~` literal 1593408 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ǡ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

O1IJEt*$t~ZFI6g6Kg{iP*Ls!@eH|h&oXR)wK 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 zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@AOSS%^IzJf?k09ir7L1_-}~RkDay4+Cg)0B^kaSZou#eyvHI-H?8w2=*4gU( zY-#Jv)S;2tk#$(wV*$9nz~ySrBYHOPNT zr8VV^(vHh$)J Date: Wed, 20 Jun 2018 15:45:42 +0100 Subject: [PATCH 1426/2380] docker: docker.py wrap StringIO import for python3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although the docker.py is nominally python2 we actually invoke it with the configured python from the configure script. Signed-off-by: Alex Bennée --- tests/docker/docker.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 306e14cf69..e4095270eb 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -26,7 +26,10 @@ import tempfile import re import signal from tarfile import TarFile, TarInfo -from StringIO import StringIO +try: + from StringIO import StringIO +except ImportError: + from io import StringIO from shutil import copy, rmtree from pwd import getpwuid From d75402b5ee297291555ac9ce806507e79bbf6bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 4 Apr 2018 20:27:05 +0100 Subject: [PATCH 1427/2380] configure: add support for --cross-cc-FOO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to specify cross compilers for our guests. This is useful for building test images/programs. Currently we re-run the compile test for each target. I couldn't think of a way to cache the value for a given arch without getting messier configure code. The cross compiler for the guest is visible to each target as CROSS_CC_GUEST in config-target.mak. This is quoted to handle the case of --cc="ccache gcc". Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson --- configure | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/configure b/configure index a5fd46c9d4..ab189067cb 100755 --- a/configure +++ b/configure @@ -458,6 +458,13 @@ vxhs="" libxml2="" docker="no" +# cross compilers defaults, can be overridden with --cross-cc-ARCH +cross_cc_aarch64="aarch64-linux-gnu-gcc" +cross_cc_arm="arm-linux-gnueabihf-gcc" +cross_cc_powerpc="powerpc-linux-gnu-gcc" + +enabled_cross_compilers="" + supported_cpu="no" supported_os="no" bogus_os="no" @@ -488,6 +495,11 @@ for opt do ;; --disable-debug-info) debug_info="no" ;; + --cross-cc-*[!a-zA-Z0-9_-]*=*) error_exit "Passed bad --cross-cc-FOO option" + ;; + --cross-cc-*) cc_arch=${opt#--cross-cc-}; cc_arch=${cc_arch%%=*} + eval "cross_cc_${cc_arch}=\$optarg" + ;; esac done # OS specific @@ -676,30 +688,37 @@ case "$cpu" in ppc|ppc64|s390|s390x|sparc64|x32) cpu="$cpu" supported_cpu="yes" + eval "cross_cc_${cpu}=\$host_cc" ;; i386|i486|i586|i686|i86pc|BePC) cpu="i386" supported_cpu="yes" + cross_cc_i386=$host_cc ;; x86_64|amd64) cpu="x86_64" supported_cpu="yes" + cross_cc_x86_64=$host_cc ;; armv*b|armv*l|arm) cpu="arm" supported_cpu="yes" + cross_cc_arm=$host_cc ;; aarch64) cpu="aarch64" supported_cpu="yes" + cross_cc_aarch64=$host_cc ;; mips*) cpu="mips" supported_cpu="yes" + cross_cc_mips=$host_cc ;; sparc|sun4[cdmuv]) cpu="sparc" supported_cpu="yes" + cross_cc_sparc=$host_cc ;; *) # This will result in either an error or falling back to TCI later @@ -917,6 +936,8 @@ for opt do ;; --disable-debug-info) ;; + --cross-cc-*) + ;; --enable-modules) modules="yes" ;; @@ -1501,6 +1522,7 @@ Advanced options (experts only): --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS --extra-cxxflags=CXXFLAGS append extra C++ compiler flags QEMU_CXXFLAGS --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS + --cross-cc-ARCH=CC use compiler when building ARCH guest test cases --make=MAKE use specified make [$make] --install=INSTALL use specified install [$install] --python=PYTHON use specified python [$python] @@ -6821,6 +6843,9 @@ case "$target" in ;; esac +target_compiler="" +target_compiler_static="" + mkdir -p $target_dir echo "# Automatically generated by configure - do not modify" > $config_target_mak @@ -6836,19 +6861,23 @@ TARGET_ABI_DIR="" case "$target_name" in i386) gdb_xml_files="i386-32bit.xml i386-32bit-core.xml i386-32bit-sse.xml" + target_compiler=$cross_cc_i386 ;; x86_64) TARGET_BASE_ARCH=i386 gdb_xml_files="i386-64bit.xml i386-64bit-core.xml i386-64bit-sse.xml" + target_compiler=$cross_cc_x86_64 ;; alpha) mttcg="yes" + target_compiler=$cross_cc_alpha ;; arm|armeb) TARGET_ARCH=arm bflt="yes" mttcg="yes" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" + target_compiler=$cross_cc_arm ;; aarch64|aarch64_be) TARGET_ARCH=aarch64 @@ -6856,59 +6885,74 @@ case "$target_name" in bflt="yes" mttcg="yes" gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" + target_compiler=$cross_cc_aarch64 ;; cris) + target_compiler=$cross_cc_cris ;; hppa) mttcg="yes" + target_compiler=$cross_cc_hppa ;; lm32) + target_compiler=$cross_cc_lm32 ;; m68k) bflt="yes" gdb_xml_files="cf-core.xml cf-fp.xml m68k-fp.xml" + target_compiler=$cross_cc_m68k ;; microblaze|microblazeel) TARGET_ARCH=microblaze bflt="yes" echo "TARGET_ABI32=y" >> $config_target_mak + target_compiler=$cross_cc_microblaze ;; mips|mipsel) TARGET_ARCH=mips + target_compiler=$cross_cc_mips echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak ;; mipsn32|mipsn32el) TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips + target_compiler=$cross_cc_mipsn32 echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak echo "TARGET_ABI32=y" >> $config_target_mak ;; mips64|mips64el) TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips + target_compiler=$cross_cc_mips64 echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak ;; moxie) + target_compiler=$cross_cc_moxie ;; nios2) + target_compiler=$cross_cc_nios2 ;; or1k) + target_compiler=$cross_cc_or1k TARGET_ARCH=openrisc TARGET_BASE_ARCH=openrisc ;; ppc) gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + target_compiler=$cross_cc_powerpc ;; ppcemb) TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + target_compiler=$cross_cc_ppcemb ;; ppc64) TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc mttcg=yes gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" + target_compiler=$cross_cc_ppc64 ;; ppc64le) TARGET_ARCH=ppc64 @@ -6916,6 +6960,7 @@ case "$target_name" in TARGET_ABI_DIR=ppc mttcg=yes gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" + target_compiler=$cross_cc_ppc64le ;; ppc64abi32) TARGET_ARCH=ppc64 @@ -6923,45 +6968,57 @@ case "$target_name" in TARGET_ABI_DIR=ppc echo "TARGET_ABI32=y" >> $config_target_mak gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" + target_compiler=$cross_cc_ppc64abi32 ;; riscv32) TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv mttcg=yes + target_compiler=$cross_cc_riscv32 ;; riscv64) TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv mttcg=yes + target_compiler=$cross_cc_riscv64 ;; sh4|sh4eb) TARGET_ARCH=sh4 bflt="yes" + target_compiler=$cross_cc_sh4 ;; sparc) + target_compiler=$cross_cc_sparc ;; sparc64) TARGET_BASE_ARCH=sparc + target_compiler=$cross_cc_sparc64 ;; sparc32plus) TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_ABI_DIR=sparc + target_compiler=$cross_cc_sparc32plus echo "TARGET_ABI32=y" >> $config_target_mak ;; s390x) mttcg=yes gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml s390-cr.xml s390-virt.xml s390-gs.xml" + target_compiler=$cross_cc_s390x ;; tilegx) + target_compiler=$cross_cc_tilegx ;; tricore) + target_compiler=$cross_cc_tricore ;; unicore32) + target_compiler=$cross_cc_unicore32 ;; xtensa|xtensaeb) TARGET_ARCH=xtensa mttcg="yes" + target_compiler=$cross_cc_xtensa ;; *) error_exit "Unsupported target CPU" @@ -6972,6 +7029,27 @@ if [ "$TARGET_BASE_ARCH" = "" ]; then TARGET_BASE_ARCH=$TARGET_ARCH fi +# Do we have a cross compiler for this target? +if has $target_compiler; then + + write_c_skeleton + + if ! do_compiler "$target_compiler" -o $TMPE $TMPC -static ; then + # For host systems we might get away with building without -static + if ! do_compiler "$target_compiler" -o $TMPE $TMPC ; then + target_compiler="" + else + enabled_cross_compilers="${enabled_cross_compilers} '${target_compiler}'" + target_compiler_static="n" + fi + else + enabled_cross_compilers="${enabled_cross_compilers} '${target_compiler}'" + target_compiler_static="y" + fi +else + target_compiler="" +fi + symlink "$source_path/Makefile.target" "$target_dir/Makefile" upper() { @@ -7045,6 +7123,14 @@ if test "$target_bsd_user" = "yes" ; then echo "CONFIG_BSD_USER=y" >> $config_target_mak fi +if test -n "$target_compiler"; then + echo "CROSS_CC_GUEST=\"$target_compiler\"" >> $config_target_mak + + if test -n "$target_compiler_static"; then + echo "CROSS_CC_GUEST_STATIC=$target_compiler_static" >> $config_target_mak + fi +fi + # generate QEMU_CFLAGS/LDFLAGS for targets cflags="" @@ -7167,6 +7253,11 @@ echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak done # for target in $targets +if test -n "$enabled_cross_compilers"; then + echo + echo "NOTE: cross-compilers enabled: $enabled_cross_compilers" +fi + if [ "$fdt" = "git" ]; then echo "config-host.h: subdir-dtc" >> $config_host_mak fi From 716a507cc08e7950f213f72b958cffbdb18a1c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 10 Apr 2018 12:19:40 +0100 Subject: [PATCH 1428/2380] configure: move i386_cc to cross_cc_i386 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also dont assume x86_64 compiler can build i386 binaries. Signed-off-by: Alex Bennée Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- configure | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/configure b/configure index ab189067cb..a5676cf15f 100755 --- a/configure +++ b/configure @@ -289,7 +289,6 @@ libs_softmmu="" libs_tools="" audio_pt_int="" audio_win_int="" -cc_i386=i386-pc-linux-gnu-gcc libs_qga="" debug_info="yes" stack_protector="" @@ -461,6 +460,8 @@ docker="no" # cross compilers defaults, can be overridden with --cross-cc-ARCH cross_cc_aarch64="aarch64-linux-gnu-gcc" cross_cc_arm="arm-linux-gnueabihf-gcc" +cross_cc_i386="i386-pc-linux-gnu-gcc" +cross_cc_cflags_i386="" cross_cc_powerpc="powerpc-linux-gnu-gcc" enabled_cross_compilers="" @@ -1447,7 +1448,8 @@ case "$cpu" in i386) CPU_CFLAGS="-m32" LDFLAGS="-m32 $LDFLAGS" - cc_i386='$(CC) -m32' + cross_cc_i386=$cc + cross_cc_cflags_i386=$CPU_CFLAGS ;; x86_64) # ??? Only extremely old AMD cpus do not have cmpxchg16b. @@ -1455,12 +1457,14 @@ case "$cpu" in # runtime and generate the fallback to serial emulation. CPU_CFLAGS="-m64 -mcx16" LDFLAGS="-m64 $LDFLAGS" - cc_i386='$(CC) -m32' + cross_cc_x86_64=$cc + cross_cc_cflags_x86_64=$CPU_CFLAGS ;; x32) CPU_CFLAGS="-mx32" LDFLAGS="-mx32 $LDFLAGS" - cc_i386='$(CC) -m32' + cross_cc_i386=$cc + cross_cc_cflags_i386="-m32" ;; # No special flags required for other host CPUs esac @@ -6724,7 +6728,6 @@ echo "CC=$cc" >> $config_host_mak if $iasl -h > /dev/null 2>&1; then echo "IASL=$iasl" >> $config_host_mak fi -echo "CC_I386=$cc_i386" >> $config_host_mak echo "HOST_CC=$host_cc" >> $config_host_mak echo "CXX=$cxx" >> $config_host_mak echo "OBJCC=$objcc" >> $config_host_mak @@ -6845,6 +6848,7 @@ esac target_compiler="" target_compiler_static="" +target_compiler_cflags="" mkdir -p $target_dir echo "# Automatically generated by configure - do not modify" > $config_target_mak @@ -6862,6 +6866,7 @@ case "$target_name" in i386) gdb_xml_files="i386-32bit.xml i386-32bit-core.xml i386-32bit-sse.xml" target_compiler=$cross_cc_i386 + target_compiler_cflags=$cross_cc_ccflags_i386 ;; x86_64) TARGET_BASE_ARCH=i386 @@ -7034,9 +7039,9 @@ if has $target_compiler; then write_c_skeleton - if ! do_compiler "$target_compiler" -o $TMPE $TMPC -static ; then + if ! do_compiler "$target_compiler" $target_compiler_cflags -o $TMPE $TMPC -static ; then # For host systems we might get away with building without -static - if ! do_compiler "$target_compiler" -o $TMPE $TMPC ; then + if ! do_compiler "$target_compiler" $target_compiler_cflags -o $TMPE $TMPC ; then target_compiler="" else enabled_cross_compilers="${enabled_cross_compilers} '${target_compiler}'" @@ -7129,8 +7134,13 @@ if test -n "$target_compiler"; then if test -n "$target_compiler_static"; then echo "CROSS_CC_GUEST_STATIC=$target_compiler_static" >> $config_target_mak fi + + if test -n "$target_compiler_cflags"; then + echo "CROSS_CC_GUEST_CFLAGS=$target_compiler_cflags" >> $config_target_mak + fi fi + # generate QEMU_CFLAGS/LDFLAGS for targets cflags="" From d422b2bc23bfdc04b16dfc8dc761a6d1fe79b08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Apr 2018 11:07:58 +0100 Subject: [PATCH 1429/2380] configure: allow user to specify --cross-cc-cflags-foo= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As an individual compiler may be able to support several targets with the appropriate flags we need to expose this to the user as well. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson --- configure | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/configure b/configure index a5676cf15f..259938e177 100755 --- a/configure +++ b/configure @@ -459,10 +459,14 @@ docker="no" # cross compilers defaults, can be overridden with --cross-cc-ARCH cross_cc_aarch64="aarch64-linux-gnu-gcc" +cross_cc_aarch64_be="$cross_cc_aarch64" +cross_cc_cflags_aarch64_be="-mbig-endian" cross_cc_arm="arm-linux-gnueabihf-gcc" +cross_cc_cflags_armeb="-mbig-endian" cross_cc_i386="i386-pc-linux-gnu-gcc" cross_cc_cflags_i386="" cross_cc_powerpc="powerpc-linux-gnu-gcc" +cross_cc_powerpc="powerpc-linux-gnu-gcc" enabled_cross_compilers="" @@ -498,6 +502,9 @@ for opt do ;; --cross-cc-*[!a-zA-Z0-9_-]*=*) error_exit "Passed bad --cross-cc-FOO option" ;; + --cross-cc-cflags-*) cc_arch=${opt#--cross-cc-flags-}; cc_arch=${cc_arch%%=*} + eval "cross_cc_cflags_${cc_arch}=\$optarg" + ;; --cross-cc-*) cc_arch=${opt#--cross-cc-}; cc_arch=${cc_arch%%=*} eval "cross_cc_${cc_arch}=\$optarg" ;; @@ -1527,6 +1534,7 @@ Advanced options (experts only): --extra-cxxflags=CXXFLAGS append extra C++ compiler flags QEMU_CXXFLAGS --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS --cross-cc-ARCH=CC use compiler when building ARCH guest test cases + --cross-cc-flags-ARCH= use compiler flags when building ARCH guest tests --make=MAKE use specified make [$make] --install=INSTALL use specified install [$install] --python=PYTHON use specified python [$python] @@ -6883,6 +6891,7 @@ case "$target_name" in mttcg="yes" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" target_compiler=$cross_cc_arm + eval "target_compiler_cflags=\$cross_cc_cflags_${target_name}" ;; aarch64|aarch64_be) TARGET_ARCH=aarch64 @@ -6891,6 +6900,7 @@ case "$target_name" in mttcg="yes" gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" target_compiler=$cross_cc_aarch64 + eval "target_compiler_cflags=\$cross_cc_cflags_${target_name}" ;; cris) target_compiler=$cross_cc_cris From 13a5abe2b880bf1fa557c47ff7a4e0c44156ece3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Apr 2018 16:15:07 +0100 Subject: [PATCH 1430/2380] configure: set cross_cc_FOO for host compiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can build tests for the host system with the compiler that we have selected. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson --- configure | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 259938e177..4d12cfbe3f 100755 --- a/configure +++ b/configure @@ -1431,26 +1431,38 @@ case "$cpu" in ppc) CPU_CFLAGS="-m32" LDFLAGS="-m32 $LDFLAGS" + cross_cc_powerpc=$cc + cross_cc_cflags_powerpc=$CPU_CFLAGS ;; ppc64) CPU_CFLAGS="-m64" LDFLAGS="-m64 $LDFLAGS" + cross_cc_ppc64=$cc + cross_cc_cflags_ppc64=$CPU_CFLAGS ;; sparc) CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc" LDFLAGS="-m32 -mv8plus $LDFLAGS" + cross_cc_sparc=$cc + cross_cc_cflags_sparc=$CPU_CFLAGS ;; sparc64) CPU_CFLAGS="-m64 -mcpu=ultrasparc" LDFLAGS="-m64 $LDFLAGS" + cross_cc_sparc64=$cc + cross_cc_cflags_sparc64=$CPU_CFLAGS ;; s390) CPU_CFLAGS="-m31" LDFLAGS="-m31 $LDFLAGS" + cross_cc_s390=$cc + cross_cc_cflags_s390=$CPU_CFLAGS ;; s390x) CPU_CFLAGS="-m64" LDFLAGS="-m64 $LDFLAGS" + cross_cc_s390x=$cc + cross_cc_cflags_s390x=$CPU_CFLAGS ;; i386) CPU_CFLAGS="-m32" @@ -1471,7 +1483,7 @@ case "$cpu" in CPU_CFLAGS="-mx32" LDFLAGS="-mx32 $LDFLAGS" cross_cc_i386=$cc - cross_cc_cflags_i386="-m32" + cross_cc_cflags_i386=$CPU_CFLAGS ;; # No special flags required for other host CPUs esac From 5e03c2d816284e1033814fa6e638ed0df0a58d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 12 Apr 2018 16:49:11 +0100 Subject: [PATCH 1431/2380] docker: Add "cc" subcommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fam Zheng [AJB: add if args.paths check] Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/docker.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index e4095270eb..42267bb94d 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -412,6 +412,31 @@ class ProbeCommand(SubCommand): return +class CcCommand(SubCommand): + """Compile sources with cc in images""" + name = "cc" + + def args(self, parser): + parser.add_argument("--image", "-i", required=True, + help="The docker image in which to run cc") + parser.add_argument("--source-path", "-s", nargs="*", dest="paths", + help="""Extra paths to (ro) mount into container for + reading sources""") + + def run(self, args, argv): + if argv and argv[0] == "--": + argv = argv[1:] + cwd = os.getcwd() + cmd = ["--rm", "-w", cwd, + "-v", "%s:%s:rw" % (cwd, cwd)] + if args.paths: + for p in args.paths: + cmd += ["-v", "%s:%s:ro,z" % (p, p)] + cmd += [args.image, "cc"] + cmd += argv + return Docker().command("run", cmd, args.quiet) + + def main(): parser = argparse.ArgumentParser(description="A Docker helper", usage="%s ..." % os.path.basename(sys.argv[0])) From 99cfdb866274ae6f68059181528c7d6dd86cc4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 12 Apr 2018 17:18:12 +0100 Subject: [PATCH 1432/2380] docker: extend "cc" command to accept compiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When calling our cross-compilation images we want to call something other than the default cc. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Fam Zheng --- tests/docker/docker.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 42267bb94d..db1e2537f5 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -419,6 +419,8 @@ class CcCommand(SubCommand): def args(self, parser): parser.add_argument("--image", "-i", required=True, help="The docker image in which to run cc") + parser.add_argument("--cc", default="cc", + help="The compiler executable to call") parser.add_argument("--source-path", "-s", nargs="*", dest="paths", help="""Extra paths to (ro) mount into container for reading sources""") @@ -432,7 +434,7 @@ class CcCommand(SubCommand): if args.paths: for p in args.paths: cmd += ["-v", "%s:%s:ro,z" % (p, p)] - cmd += [args.image, "cc"] + cmd += [args.image, args.cc] cmd += argv return Docker().command("run", cmd, args.quiet) From 50b7273854773a20b7e889a18c8c3c5035441857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 6 Apr 2018 16:23:26 +0100 Subject: [PATCH 1433/2380] docker: allow "cc" command to run in user context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Reviewed-by: Fam Zheng Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/docker.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index db1e2537f5..b28ad87034 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -421,6 +421,8 @@ class CcCommand(SubCommand): help="The docker image in which to run cc") parser.add_argument("--cc", default="cc", help="The compiler executable to call") + parser.add_argument("--user", + help="The user-id to run under") parser.add_argument("--source-path", "-s", nargs="*", dest="paths", help="""Extra paths to (ro) mount into container for reading sources""") @@ -434,6 +436,8 @@ class CcCommand(SubCommand): if args.paths: for p in args.paths: cmd += ["-v", "%s:%s:ro,z" % (p, p)] + if args.user: + cmd += ["-u", args.user] cmd += [args.image, args.cc] cmd += argv return Docker().command("run", cmd, args.quiet) From e27cae268fc18a22b7842fae1fe8257df704741a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 6 Apr 2018 16:25:39 +0100 Subject: [PATCH 1434/2380] docker: Makefile.include introduce DOCKER_SCRIPT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define this in one place to make it easy to re-use. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 74fd51c22c..8afb383478 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -15,6 +15,8 @@ DOCKER_TESTS := $(notdir $(shell \ DOCKER_TOOLS := travis +DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py + TESTS ?= % IMAGES ?= % @@ -38,7 +40,7 @@ docker-image-%: $(DOCKER_FILES_DIR)/%.docker echo WARNING: EXECUTABLE is not set, debootstrap may fail. 2>&1 ; \ fi $(call quiet-command,\ - $(SRC_PATH)/tests/docker/docker.py build qemu:$* $< \ + $(DOCKER_SCRIPT) build qemu:$* $< \ $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \ $(if $(NOUSER),,--add-current-user) \ $(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES))\ @@ -133,11 +135,11 @@ docker-run: docker-qemu-src fi $(if $(EXECUTABLE), \ $(call quiet-command, \ - $(SRC_PATH)/tests/docker/docker.py update \ + $(DOCKER_SCRIPT) update \ $(IMAGE) $(EXECUTABLE), \ " COPYING $(EXECUTABLE) to $(IMAGE)")) $(call quiet-command, \ - $(SRC_PATH)/tests/docker/docker.py run \ + $(DOCKER_SCRIPT) run \ $(if $(NOUSER),,-u $(shell id -u)) \ --security-opt seccomp=unconfined \ $(if $V,,--rm) \ @@ -167,4 +169,4 @@ docker-run-%: @$(MAKE) docker-run TEST=$(CMD) IMAGE=qemu:$(IMAGE) docker-clean: - $(call quiet-command, $(SRC_PATH)/tests/docker/docker.py clean) + $(call quiet-command, $(DOCKER_SCRIPT) clean) From dd28bebd024b4c7cfb6352126cd010d389a8d374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 5 Apr 2018 15:03:22 +0100 Subject: [PATCH 1435/2380] tests/tcg: move architecture independent tests into subdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will want to build these for all supported guest architectures so lets move them all into one place. We also drop test_path at this point because it needs qemu utils and glib bits which is hard to support for cross compiling. Signed-off-by: Alex Bennée Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson --- MAINTAINERS | 4 + tests/tcg/Makefile | 31 ----- tests/tcg/README | 10 +- tests/tcg/multiarch/Makefile.target | 36 ++++++ tests/tcg/multiarch/README | 1 + tests/tcg/{ => multiarch}/linux-test.c | 0 tests/tcg/{ => multiarch}/sha1.c | 0 tests/tcg/{ => multiarch}/test-mmap.c | 0 tests/tcg/{ => multiarch}/testthread.c | 0 tests/tcg/test_path.c | 157 ------------------------- 10 files changed, 45 insertions(+), 194 deletions(-) create mode 100644 tests/tcg/multiarch/Makefile.target create mode 100644 tests/tcg/multiarch/README rename tests/tcg/{ => multiarch}/linux-test.c (100%) rename tests/tcg/{ => multiarch}/sha1.c (100%) rename tests/tcg/{ => multiarch}/test-mmap.c (100%) rename tests/tcg/{ => multiarch}/testthread.c (100%) delete mode 100644 tests/tcg/test_path.c diff --git a/MAINTAINERS b/MAINTAINERS index da91501c7a..28dc787617 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -303,6 +303,10 @@ F: target/tricore/ F: hw/tricore/ F: include/hw/tricore/ +Multiarch Linux User Tests +M: Alex Bennée +F: tests/tcg/multiarch/ + Guest CPU Cores (KVM): ---------------------- diff --git a/tests/tcg/Makefile b/tests/tcg/Makefile index 89e3342f3d..e12395117a 100644 --- a/tests/tcg/Makefile +++ b/tests/tcg/Makefile @@ -18,12 +18,9 @@ LDFLAGS= # also, pi_10.com runs indefinitely I386_TESTS=hello-i386 \ - linux-test \ - testthread \ sha1-i386 \ test-i386 \ test-i386-fprem \ - test-mmap \ # runcom # native i386 compilers sometimes are not biarch. assume cross-compilers are @@ -47,8 +44,6 @@ run-%: % -$(QEMU) ./$* run-hello-i386: hello-i386 -run-linux-test: linux-test -run-testthread: testthread run-sha1-i386: sha1-i386 run-test-i386: test-i386 @@ -66,11 +61,6 @@ run-test-x86_64: test-x86_64 -$(QEMU_X86_64) test-x86_64 > test-x86_64.out @if diff -u test-x86_64.ref test-x86_64.out ; then echo "Auto Test OK"; fi -run-test-mmap: test-mmap - -$(QEMU) ./test-mmap - -$(QEMU) -p 8192 ./test-mmap 8192 - -$(QEMU) -p 16384 ./test-mmap 16384 - -$(QEMU) -p 32768 ./test-mmap 32768 run-runcom: runcom -$(QEMU) ./runcom $(SRC_PATH)/tests/pi_10.com @@ -80,17 +70,10 @@ run-test_path: test_path # rules to compile tests -test_path: test_path.o - -test_path.o: test_path.c - hello-i386: hello-i386.c $(CC_I386) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $< strip $@ -testthread: testthread.c - $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread - # i386/x86_64 emulation test (test various opcodes) */ test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \ test-i386.h test-i386-shift.h test-i386-muldiv.h @@ -104,28 +87,14 @@ test-x86_64: test-i386.c \ test-i386.h test-i386-shift.h test-i386-muldiv.h $(CC_X86_64) $(QEMU_INCLUDES) $(CFLAGS) $(LDFLAGS) -o $@ $( test-mmap.out, "TEST", \ + "$< (default) on $(TARGET_NAME)") + +# additional page sizes (defined by each architecture adding to EXTRA_RUNS) +run-test-mmap-%: test-mmap + $(call quiet-command, $(QEMU) -p $* $< > test-mmap-$*.out, "TEST", \ + "$< ($* byte pages) on $(TARGET_NAME)") diff --git a/tests/tcg/multiarch/README b/tests/tcg/multiarch/README new file mode 100644 index 0000000000..522c9d2ea3 --- /dev/null +++ b/tests/tcg/multiarch/README @@ -0,0 +1 @@ +Multi-architecture linux-user tests diff --git a/tests/tcg/linux-test.c b/tests/tcg/multiarch/linux-test.c similarity index 100% rename from tests/tcg/linux-test.c rename to tests/tcg/multiarch/linux-test.c diff --git a/tests/tcg/sha1.c b/tests/tcg/multiarch/sha1.c similarity index 100% rename from tests/tcg/sha1.c rename to tests/tcg/multiarch/sha1.c diff --git a/tests/tcg/test-mmap.c b/tests/tcg/multiarch/test-mmap.c similarity index 100% rename from tests/tcg/test-mmap.c rename to tests/tcg/multiarch/test-mmap.c diff --git a/tests/tcg/testthread.c b/tests/tcg/multiarch/testthread.c similarity index 100% rename from tests/tcg/testthread.c rename to tests/tcg/multiarch/testthread.c diff --git a/tests/tcg/test_path.c b/tests/tcg/test_path.c deleted file mode 100644 index 1c29bce263..0000000000 --- a/tests/tcg/test_path.c +++ /dev/null @@ -1,157 +0,0 @@ -/* Test path override code */ -#include "config-host.h" -#include "util/cutils.c" -#include "util/hexdump.c" -#include "util/iov.c" -#include "util/path.c" -#include "util/qemu-timer-common.c" -#include -#include -#include - -void qemu_log(const char *fmt, ...); - -/* Any log message kills the test. */ -void qemu_log(const char *fmt, ...) -{ - va_list ap; - - fprintf(stderr, "FATAL: "); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - exit(1); -} - -#define NO_CHANGE(_path) \ - do { \ - if (strcmp(path(_path), _path) != 0) return __LINE__; \ - } while(0) - -#define CHANGE_TO(_path, _newpath) \ - do { \ - if (strcmp(path(_path), _newpath) != 0) return __LINE__; \ - } while(0) - -static void cleanup(void) -{ - unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE"); - unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE2"); - unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE3"); - unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE4"); - unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE5"); - rmdir("/tmp/qemu-test_path/DIR1/DIR2"); - rmdir("/tmp/qemu-test_path/DIR1/DIR3"); - rmdir("/tmp/qemu-test_path/DIR1"); - rmdir("/tmp/qemu-test_path"); -} - -static unsigned int do_test(void) -{ - if (mkdir("/tmp/qemu-test_path", 0700) != 0) - return __LINE__; - - if (mkdir("/tmp/qemu-test_path/DIR1", 0700) != 0) - return __LINE__; - - if (mkdir("/tmp/qemu-test_path/DIR1/DIR2", 0700) != 0) - return __LINE__; - - if (mkdir("/tmp/qemu-test_path/DIR1/DIR3", 0700) != 0) - return __LINE__; - - if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE", 0600)) != 0) - return __LINE__; - - if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE2", 0600)) != 0) - return __LINE__; - - if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE3", 0600)) != 0) - return __LINE__; - - if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE4", 0600)) != 0) - return __LINE__; - - if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE5", 0600)) != 0) - return __LINE__; - - init_paths("/tmp/qemu-test_path"); - - NO_CHANGE("/tmp"); - NO_CHANGE("/tmp/"); - NO_CHANGE("/tmp/qemu-test_path"); - NO_CHANGE("/tmp/qemu-test_path/"); - NO_CHANGE("/tmp/qemu-test_path/D"); - NO_CHANGE("/tmp/qemu-test_path/DI"); - NO_CHANGE("/tmp/qemu-test_path/DIR"); - NO_CHANGE("/tmp/qemu-test_path/DIR1"); - NO_CHANGE("/tmp/qemu-test_path/DIR1/"); - - NO_CHANGE("/D"); - NO_CHANGE("/DI"); - NO_CHANGE("/DIR"); - NO_CHANGE("/DIR2"); - NO_CHANGE("/DIR1."); - - CHANGE_TO("/DIR1", "/tmp/qemu-test_path/DIR1"); - CHANGE_TO("/DIR1/", "/tmp/qemu-test_path/DIR1"); - - NO_CHANGE("/DIR1/D"); - NO_CHANGE("/DIR1/DI"); - NO_CHANGE("/DIR1/DIR"); - NO_CHANGE("/DIR1/DIR1"); - - CHANGE_TO("/DIR1/DIR2", "/tmp/qemu-test_path/DIR1/DIR2"); - CHANGE_TO("/DIR1/DIR2/", "/tmp/qemu-test_path/DIR1/DIR2"); - - CHANGE_TO("/DIR1/DIR3", "/tmp/qemu-test_path/DIR1/DIR3"); - CHANGE_TO("/DIR1/DIR3/", "/tmp/qemu-test_path/DIR1/DIR3"); - - NO_CHANGE("/DIR1/DIR2/F"); - NO_CHANGE("/DIR1/DIR2/FI"); - NO_CHANGE("/DIR1/DIR2/FIL"); - NO_CHANGE("/DIR1/DIR2/FIL."); - - CHANGE_TO("/DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/DIR2/FILE2", "/tmp/qemu-test_path/DIR1/DIR2/FILE2"); - CHANGE_TO("/DIR1/DIR2/FILE3", "/tmp/qemu-test_path/DIR1/DIR2/FILE3"); - CHANGE_TO("/DIR1/DIR2/FILE4", "/tmp/qemu-test_path/DIR1/DIR2/FILE4"); - CHANGE_TO("/DIR1/DIR2/FILE5", "/tmp/qemu-test_path/DIR1/DIR2/FILE5"); - - NO_CHANGE("/DIR1/DIR2/FILE6"); - NO_CHANGE("/DIR1/DIR2/FILE/X"); - - CHANGE_TO("/DIR1/../DIR1", "/tmp/qemu-test_path/DIR1"); - CHANGE_TO("/DIR1/../DIR1/", "/tmp/qemu-test_path/DIR1"); - CHANGE_TO("/../DIR1", "/tmp/qemu-test_path/DIR1"); - CHANGE_TO("/../DIR1/", "/tmp/qemu-test_path/DIR1"); - CHANGE_TO("/DIR1/DIR2/../DIR2", "/tmp/qemu-test_path/DIR1/DIR2"); - CHANGE_TO("/DIR1/DIR2/../DIR2/../../DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/DIR2/../DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - - NO_CHANGE("/DIR1/DIR2/../DIR1"); - NO_CHANGE("/DIR1/DIR2/../FILE"); - - CHANGE_TO("/./DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/././DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/./DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/././DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/DIR2/././FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/./DIR1/./DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - - return 0; -} - -int main(int argc, char *argv[]) -{ - int ret; - - ret = do_test(); - cleanup(); - if (ret) { - fprintf(stderr, "test_path: failed on line %i\n", ret); - return 1; - } - return 0; -} From 5e13cbd51d9f65c2c3bd04ee40d2f810f3b9eaed Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 8 Sep 2017 17:16:58 +0800 Subject: [PATCH 1436/2380] tests/tcg/multiarch: Build fix for linux-test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To keep the compiler happy, and to fit in our buildsys flags: - Make local functions "static" - #ifdef out unused functions - drop cutils/osdep dependencies Signed-off-by: Fam Zheng [AJB: drop cutils/osdep dependencies] Signed-off-by: Alex Bennée Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- tests/tcg/multiarch/linux-test.c | 68 ++++++++++---------------------- 1 file changed, 21 insertions(+), 47 deletions(-) diff --git a/tests/tcg/multiarch/linux-test.c b/tests/tcg/multiarch/linux-test.c index 5070d31446..4457bd04ba 100644 --- a/tests/tcg/multiarch/linux-test.c +++ b/tests/tcg/multiarch/linux-test.c @@ -16,7 +16,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#define _GNU_SOURCE #include #include #include @@ -31,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -39,13 +39,12 @@ #include #include #include -#include "qemu/cutils.h" #define TESTPATH "/tmp/linux-test.tmp" #define TESTPORT 7654 #define STACK_SIZE 16384 -void error1(const char *filename, int line, const char *fmt, ...) +static void error1(const char *filename, int line, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -56,7 +55,7 @@ void error1(const char *filename, int line, const char *fmt, ...) exit(1); } -int __chk_error(const char *filename, int line, int ret) +static int __chk_error(const char *filename, int line, int ret) { if (ret < 0) { error1(filename, line, "%m (ret=%d, errno=%d)", @@ -73,7 +72,7 @@ int __chk_error(const char *filename, int line, int ret) #define FILE_BUF_SIZE 300 -void test_file(void) +static void test_file(void) { int fd, i, len, ret; uint8_t buf[FILE_BUF_SIZE]; @@ -210,7 +209,7 @@ void test_file(void) chk_error(rmdir(TESTPATH)); } -void test_fork(void) +static void test_fork(void) { int pid, status; @@ -224,7 +223,7 @@ void test_fork(void) error("waitpid status=0x%x", status); } -void test_time(void) +static void test_time(void) { struct timeval tv, tv2; struct timespec ts, rem; @@ -251,34 +250,7 @@ void test_time(void) error("getrusage"); } -void pstrcpy(char *buf, int buf_size, const char *str) -{ - int c; - char *q = buf; - - if (buf_size <= 0) - return; - - for(;;) { - c = *str++; - if (c == 0 || q >= buf + buf_size - 1) - break; - *q++ = c; - } - *q = '\0'; -} - -/* strcat and truncate. */ -char *pstrcat(char *buf, int buf_size, const char *s) -{ - int len; - len = strlen(buf); - if (len < buf_size) - pstrcpy(buf + len, buf_size - len, s); - return buf; -} - -int server_socket(void) +static int server_socket(void) { int val, fd; struct sockaddr_in sockaddr; @@ -298,7 +270,7 @@ int server_socket(void) } -int client_socket(void) +static int client_socket(void) { int fd; struct sockaddr_in sockaddr; @@ -312,9 +284,9 @@ int client_socket(void) return fd; } -const char socket_msg[] = "hello socket\n"; +static const char socket_msg[] = "hello socket\n"; -void test_socket(void) +static void test_socket(void) { int server_fd, client_fd, fd, pid, ret, val; struct sockaddr_in sockaddr; @@ -348,9 +320,10 @@ void test_socket(void) chk_error(close(server_fd)); } +#if 0 #define WCOUNT_MAX 512 -void test_pipe(void) +static void test_pipe(void) { fd_set rfds, wfds; int fds[2], fd_max, ret; @@ -391,10 +364,10 @@ void test_pipe(void) chk_error(close(fds[1])); } -int thread1_res; -int thread2_res; +static int thread1_res; +static int thread2_res; -int thread1_func(void *arg) +static int thread1_func(void *arg) { int i; for(i=0;i<5;i++) { @@ -404,7 +377,7 @@ int thread1_func(void *arg) return 0; } -int thread2_func(void *arg) +static int thread2_func(void *arg) { int i; for(i=0;i<6;i++) { @@ -435,27 +408,28 @@ void test_clone(void) thread2_res != 6) error("clone"); } +#endif /***********************************/ volatile int alarm_count; jmp_buf jmp_env; -void sig_alarm(int sig) +static void sig_alarm(int sig) { if (sig != SIGALRM) error("signal"); alarm_count++; } -void sig_segv(int sig, siginfo_t *info, void *puc) +static void sig_segv(int sig, siginfo_t *info, void *puc) { if (sig != SIGSEGV) error("signal"); longjmp(jmp_env, 1); } -void test_signal(void) +static void test_signal(void) { struct sigaction act; struct itimerval it, oit; @@ -510,7 +484,7 @@ void test_signal(void) #define SHM_SIZE 32768 -void test_shm(void) +static void test_shm(void) { void *ptr; int shmid; From 875fcce66139967887fbcfb322540d912a7a2b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 19 Apr 2018 14:02:04 +0100 Subject: [PATCH 1437/2380] tests/tcg/multiarch: enable additional linux-test tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Un-comment the remaining tests. I removed the itimer value tests because I'm fairly sure a re-arming timer will always have a different value in it when you grab it. I've also fixed up the clone thread flags as QEMU will only allow a clone to use flags which match glibc. However the test is still racey so it remains disabled by default - it can be run by passing any additional parameters on the command line. Signed-off-by: Alex Bennée Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- tests/tcg/multiarch/linux-test.c | 51 +++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/tests/tcg/multiarch/linux-test.c b/tests/tcg/multiarch/linux-test.c index 4457bd04ba..6f2c531474 100644 --- a/tests/tcg/multiarch/linux-test.c +++ b/tests/tcg/multiarch/linux-test.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +#define _GNU_SOURCE #include #include #include @@ -58,8 +59,8 @@ static void error1(const char *filename, int line, const char *fmt, ...) static int __chk_error(const char *filename, int line, int ret) { if (ret < 0) { - error1(filename, line, "%m (ret=%d, errno=%d)", - ret, errno); + error1(filename, line, "%m (ret=%d, errno=%d/%s)", + ret, errno, strerror(errno)); } return ret; } @@ -216,6 +217,7 @@ static void test_fork(void) pid = chk_error(fork()); if (pid == 0) { /* child */ + sleep(2); exit(2); } chk_error(waitpid(pid, &status, 0)); @@ -320,7 +322,6 @@ static void test_socket(void) chk_error(close(server_fd)); } -#if 0 #define WCOUNT_MAX 512 static void test_pipe(void) @@ -355,7 +356,7 @@ static void test_pipe(void) } if (FD_ISSET(fds[1], &wfds)) { ch = 'a'; - chk_error(write(fds[0], &ch, 1)); + chk_error(write(fds[1], &ch, 1)); wcount++; } } @@ -387,28 +388,41 @@ static int thread2_func(void *arg) return 0; } -void test_clone(void) +static void wait_for_child(pid_t pid) +{ + int status; + chk_error(waitpid(pid, &status, 0)); +} + +/* For test_clone we must match the clone flags used by glibc, see + * CLONE_THREAD_FLAGS in the QEMU source code. + */ +static void test_clone(void) { uint8_t *stack1, *stack2; - int pid1, pid2, status1, status2; + pid_t pid1, pid2; stack1 = malloc(STACK_SIZE); pid1 = chk_error(clone(thread1_func, stack1 + STACK_SIZE, - CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello1")); + CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM, + "hello1")); stack2 = malloc(STACK_SIZE); pid2 = chk_error(clone(thread2_func, stack2 + STACK_SIZE, - CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2")); + CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM, + "hello2")); - while (waitpid(pid1, &status1, 0) != pid1); + wait_for_child(pid1); free(stack1); - while (waitpid(pid2, &status2, 0) != pid2); + wait_for_child(pid2); free(stack2); + if (thread1_res != 5 || thread2_res != 6) error("clone"); } -#endif /***********************************/ @@ -449,12 +463,10 @@ static void test_signal(void) it.it_value.tv_usec = 10 * 1000; chk_error(setitimer(ITIMER_REAL, &it, NULL)); chk_error(getitimer(ITIMER_REAL, &oit)); - if (oit.it_value.tv_sec != it.it_value.tv_sec || - oit.it_value.tv_usec != it.it_value.tv_usec) - error("itimer"); while (alarm_count < 5) { usleep(10 * 1000); + getitimer(ITIMER_REAL, &oit); } it.it_interval.tv_sec = 0; @@ -463,9 +475,6 @@ static void test_signal(void) it.it_value.tv_usec = 0; memset(&oit, 0xff, sizeof(oit)); chk_error(setitimer(ITIMER_REAL, &it, &oit)); - if (oit.it_value.tv_sec != 0 || - oit.it_value.tv_usec != 10 * 1000) - error("setitimer"); /* SIGSEGV test */ act.sa_sigaction = sig_segv; @@ -503,10 +512,16 @@ static void test_shm(void) int main(int argc, char **argv) { test_file(); + test_pipe(); test_fork(); test_time(); test_socket(); - // test_clone(); + + if (argc > 1) { + printf("test_clone still considered buggy\n"); + test_clone(); + } + test_signal(); test_shm(); return 0; From 5aa632d5d98fc2907cebec753181fc8a56c7d476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 8 Jun 2018 20:15:03 +0100 Subject: [PATCH 1438/2380] tests/tcg/multiarch: don't hard code paths/ports for linux-test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fixed path and ports get in the way of running our tests and builds in parallel. Instead of using TESTPATH we use mkdtemp() and instead of a fixed port we allow the kernel to assign one and query it afterwards. Ideally test directory creation should be common functionally across all TCG tests but this could complicate an already huge patch series so we mark it as a TODO for next time. Signed-off-by: Alex Bennée --- tests/tcg/multiarch/linux-test.c | 39 +++++++++++++++++--------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/tests/tcg/multiarch/linux-test.c b/tests/tcg/multiarch/linux-test.c index 6f2c531474..e80eccc0ce 100644 --- a/tests/tcg/multiarch/linux-test.c +++ b/tests/tcg/multiarch/linux-test.c @@ -40,9 +40,8 @@ #include #include #include +#include -#define TESTPATH "/tmp/linux-test.tmp" -#define TESTPORT 7654 #define STACK_SIZE 16384 static void error1(const char *filename, int line, const char *fmt, ...) @@ -85,19 +84,16 @@ static void test_file(void) struct iovec vecs[2]; DIR *dir; struct dirent *de; + /* TODO: make common tempdir creation for tcg tests */ + char template[] = "/tmp/linux-test-XXXXXX"; + char *tmpdir = mkdtemp(template); - /* clean up, just in case */ - unlink(TESTPATH "/file1"); - unlink(TESTPATH "/file2"); - unlink(TESTPATH "/file3"); - rmdir(TESTPATH); + assert(tmpdir); if (getcwd(cur_dir, sizeof(cur_dir)) == NULL) error("getcwd"); - chk_error(mkdir(TESTPATH, 0755)); - - chk_error(chdir(TESTPATH)); + chk_error(chdir(tmpdir)); /* open/read/write/close/readv/writev/lseek */ @@ -163,7 +159,7 @@ static void test_file(void) st.st_mtime != 1000) error("stat time"); - chk_error(stat(TESTPATH, &st)); + chk_error(stat(tmpdir, &st)); if (!S_ISDIR(st.st_mode)) error("stat mode"); @@ -185,7 +181,7 @@ static void test_file(void) error("stat mode"); /* getdents */ - dir = opendir(TESTPATH); + dir = opendir(tmpdir); if (!dir) error("opendir"); len = 0; @@ -207,7 +203,7 @@ static void test_file(void) chk_error(unlink("file3")); chk_error(unlink("file2")); chk_error(chdir(cur_dir)); - chk_error(rmdir(TESTPATH)); + chk_error(rmdir(tmpdir)); } static void test_fork(void) @@ -264,7 +260,7 @@ static int server_socket(void) chk_error(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))); sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(TESTPORT); + sockaddr.sin_port = htons(0); /* choose random ephemeral port) */ sockaddr.sin_addr.s_addr = 0; chk_error(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))); chk_error(listen(fd, 0)); @@ -272,7 +268,7 @@ static int server_socket(void) } -static int client_socket(void) +static int client_socket(uint16_t port) { int fd; struct sockaddr_in sockaddr; @@ -280,7 +276,7 @@ static int client_socket(void) /* server socket */ fd = chk_error(socket(PF_INET, SOCK_STREAM, 0)); sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(TESTPORT); + sockaddr.sin_port = htons(port); inet_aton("127.0.0.1", &sockaddr.sin_addr); chk_error(connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))); return fd; @@ -292,10 +288,17 @@ static void test_socket(void) { int server_fd, client_fd, fd, pid, ret, val; struct sockaddr_in sockaddr; - socklen_t len; + struct sockaddr_in server_addr; + socklen_t len, socklen; + uint16_t server_port; char buf[512]; server_fd = server_socket(); + /* find out what port we got */ + socklen = sizeof(server_addr); + ret = getsockname(server_fd, &server_addr, &socklen); + chk_error(ret); + server_port = ntohs(server_addr.sin_port); /* test a few socket options */ len = sizeof(val); @@ -305,7 +308,7 @@ static void test_socket(void) pid = chk_error(fork()); if (pid == 0) { - client_fd = client_socket(); + client_fd = client_socket(server_port); send(client_fd, socket_msg, sizeof(socket_msg), 0); close(client_fd); exit(0); From 07c85b696ae01c35f3ede53d48cbacf342403ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 24 Apr 2018 14:00:46 +0100 Subject: [PATCH 1439/2380] tests/tcg/multiarch: move most output to stdout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default test run outputs to stdout so it can be re-directed. Errors are still reported to stderr. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth --- tests/tcg/multiarch/test-mmap.c | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/tcg/multiarch/test-mmap.c b/tests/tcg/multiarch/test-mmap.c index cdefadfa4c..5c0afe6e49 100644 --- a/tests/tcg/multiarch/test-mmap.c +++ b/tests/tcg/multiarch/test-mmap.c @@ -36,7 +36,7 @@ do \ { \ if (!(x)) { \ - fprintf (stderr, "FAILED at %s:%d\n", __FILE__, __LINE__); \ + fprintf(stderr, "FAILED at %s:%d\n", __FILE__, __LINE__); \ exit (EXIT_FAILURE); \ } \ } while (0) @@ -57,7 +57,7 @@ void check_aligned_anonymous_unfixed_mmaps(void) uintptr_t p; int i; - fprintf (stderr, "%s", __func__); + fprintf(stdout, "%s", __func__); for (i = 0; i < 0x1fff; i++) { size_t len; @@ -106,7 +106,7 @@ void check_aligned_anonymous_unfixed_mmaps(void) munmap (p4, len); munmap (p5, len); } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_large_anonymous_unfixed_mmap(void) @@ -115,7 +115,7 @@ void check_large_anonymous_unfixed_mmap(void) uintptr_t p; size_t len; - fprintf (stderr, "%s", __func__); + fprintf(stdout, "%s", __func__); len = 0x02000000; p1 = mmap(NULL, len, PROT_READ, @@ -130,7 +130,7 @@ void check_large_anonymous_unfixed_mmap(void) /* Make sure we can read from the entire area. */ memcpy (dummybuf, p1, pagesize); munmap (p1, len); - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_aligned_anonymous_unfixed_colliding_mmaps(void) @@ -141,7 +141,7 @@ void check_aligned_anonymous_unfixed_colliding_mmaps(void) uintptr_t p; int i; - fprintf (stderr, "%s", __func__); + fprintf(stdout, "%s", __func__); for (i = 0; i < 0x2fff; i++) { int nlen; @@ -180,7 +180,7 @@ void check_aligned_anonymous_unfixed_colliding_mmaps(void) munmap (p2, pagesize); munmap (p3, nlen); } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_aligned_anonymous_fixed_mmaps(void) @@ -194,7 +194,7 @@ void check_aligned_anonymous_fixed_mmaps(void) addr = mmap(NULL, pagesize * 40, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - fprintf (stderr, "%s addr=%p", __func__, addr); + fprintf(stdout, "%s addr=%p", __func__, addr); fail_unless (addr != MAP_FAILED); for (i = 0; i < 40; i++) @@ -212,7 +212,7 @@ void check_aligned_anonymous_fixed_mmaps(void) munmap (p1, pagesize); addr += pagesize; } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_aligned_anonymous_fixed_mmaps_collide_with_host(void) @@ -225,8 +225,8 @@ void check_aligned_anonymous_fixed_mmaps_collide_with_host(void) /* Find a suitable address to start with. Right were the x86 hosts stack is. */ addr = ((void *)0x80000000); - fprintf (stderr, "%s addr=%p", __func__, addr); - fprintf (stderr, "FIXME: QEMU fails to track pages used by the host."); + fprintf(stdout, "%s addr=%p", __func__, addr); + fprintf(stdout, "FIXME: QEMU fails to track pages used by the host."); for (i = 0; i < 20; i++) { @@ -243,7 +243,7 @@ void check_aligned_anonymous_fixed_mmaps_collide_with_host(void) munmap (p1, pagesize); addr += pagesize; } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_file_unfixed_mmaps(void) @@ -252,7 +252,7 @@ void check_file_unfixed_mmaps(void) uintptr_t p; int i; - fprintf (stderr, "%s", __func__); + fprintf(stdout, "%s", __func__); for (i = 0; i < 0x10; i++) { size_t len; @@ -294,7 +294,7 @@ void check_file_unfixed_mmaps(void) munmap (p2, len); munmap (p3, len); } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_file_unfixed_eof_mmaps(void) @@ -304,7 +304,7 @@ void check_file_unfixed_eof_mmaps(void) uintptr_t p; int i; - fprintf (stderr, "%s", __func__); + fprintf(stdout, "%s", __func__); for (i = 0; i < 0x10; i++) { p1 = mmap(NULL, pagesize, PROT_READ, @@ -327,7 +327,7 @@ void check_file_unfixed_eof_mmaps(void) fail_unless (cp[pagesize - 4] == 0); munmap (p1, pagesize); } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_file_fixed_eof_mmaps(void) @@ -343,7 +343,7 @@ void check_file_fixed_eof_mmaps(void) MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - fprintf (stderr, "%s addr=%p", __func__, (void *)addr); + fprintf(stdout, "%s addr=%p", __func__, (void *)addr); fail_unless (addr != MAP_FAILED); for (i = 0; i < 0x10; i++) @@ -371,7 +371,7 @@ void check_file_fixed_eof_mmaps(void) munmap (p1, pagesize); addr += pagesize; } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_file_fixed_mmaps(void) @@ -384,7 +384,7 @@ void check_file_fixed_mmaps(void) addr = mmap(NULL, pagesize * 40 * 4, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - fprintf (stderr, "%s addr=%p", __func__, (void *)addr); + fprintf(stdout, "%s addr=%p", __func__, (void *)addr); fail_unless (addr != MAP_FAILED); for (i = 0; i < 40; i++) @@ -426,7 +426,7 @@ void check_file_fixed_mmaps(void) munmap (p4, pagesize); addr += pagesize * 4; } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void checked_write(int fd, const void *buf, size_t count) From 4132431f249ac89f413ff326ec4f960992806255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 5 Apr 2018 15:35:11 +0100 Subject: [PATCH 1440/2380] tests/tcg: move i386 specific tests into subdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These only need to be built for i386 guests. This includes a stub tests/tcg/i386/Makfile.target which absorbs some of what was in tests/tcg/Makefile. Signed-off-by: Alex Bennée Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- MAINTAINERS | 1 + tests/tcg/README | 39 ------------------------ tests/tcg/i386/Makefile.target | 30 ++++++++++++++++++ tests/tcg/i386/README | 38 +++++++++++++++++++++++ tests/tcg/{ => i386}/hello-i386.c | 0 tests/tcg/{ => i386}/pi_10.com | Bin tests/tcg/{ => i386}/runcom.c | 0 tests/tcg/{ => i386}/test-i386-code16.S | 0 tests/tcg/{ => i386}/test-i386-fprem.c | 0 tests/tcg/{ => i386}/test-i386-muldiv.h | 0 tests/tcg/{ => i386}/test-i386-shift.h | 0 tests/tcg/{ => i386}/test-i386-ssse3.c | 0 tests/tcg/{ => i386}/test-i386-vm86.S | 0 tests/tcg/{ => i386}/test-i386.c | 0 tests/tcg/{ => i386}/test-i386.h | 0 15 files changed, 69 insertions(+), 39 deletions(-) create mode 100644 tests/tcg/i386/Makefile.target create mode 100644 tests/tcg/i386/README rename tests/tcg/{ => i386}/hello-i386.c (100%) rename tests/tcg/{ => i386}/pi_10.com (100%) rename tests/tcg/{ => i386}/runcom.c (100%) rename tests/tcg/{ => i386}/test-i386-code16.S (100%) rename tests/tcg/{ => i386}/test-i386-fprem.c (100%) rename tests/tcg/{ => i386}/test-i386-muldiv.h (100%) rename tests/tcg/{ => i386}/test-i386-shift.h (100%) rename tests/tcg/{ => i386}/test-i386-ssse3.c (100%) rename tests/tcg/{ => i386}/test-i386-vm86.S (100%) rename tests/tcg/{ => i386}/test-i386.c (100%) rename tests/tcg/{ => i386}/test-i386.h (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 28dc787617..590c24377c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -283,6 +283,7 @@ M: Richard Henderson M: Eduardo Habkost S: Maintained F: target/i386/ +F: tests/tcg/i386/ F: hw/i386/ F: disas/i386.c T: git git://github.com/ehabkost/qemu.git x86-next diff --git a/tests/tcg/README b/tests/tcg/README index 0890044cf0..469504c4cb 100644 --- a/tests/tcg/README +++ b/tests/tcg/README @@ -3,45 +3,6 @@ regression testing. Tests are either multi-arch, meaning they can be built for all guest architectures that support linux-user executable, or they are architecture specific. -i386 -==== - -test-i386 ---------- - -This program executes most of the 16 bit and 32 bit x86 instructions and -generates a text output, for comparison with the output obtained with -a real CPU or another emulator. - -The Linux system call modify_ldt() is used to create x86 selectors -to test some 16 bit addressing and 32 bit with segmentation cases. - -The Linux system call vm86() is used to test vm86 emulation. - -Various exceptions are raised to test most of the x86 user space -exception reporting. - -linux-test ----------- - -This program tests various Linux system calls. It is used to verify -that the system call parameters are correctly converted between target -and host CPUs. - -test-i386-fprem ---------------- - -runcom ------- - -test-mmap ---------- - -sha1 ----- - -hello-i386 ----------- ARM diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target new file mode 100644 index 0000000000..2f27b65e2d --- /dev/null +++ b/tests/tcg/i386/Makefile.target @@ -0,0 +1,30 @@ +# i386 cross compile notes + +I386_SRC=$(SRC_PATH)/tests/tcg/i386 + +# Set search path for all sources +VPATH += $(I386_SRC) + +I386_SRCS=$(notdir $(wildcard $(I386_SRC)/*.c)) +I386_TESTS=$(I386_SRCS:.c=) + +# Update TESTS +TESTS+=$(I386_TESTS) + +ifneq ($(TARGET_NAME),x86_64) +CFLAGS+=-m32 +endif + +# +# hello-i386 is a barebones app +# +hello-i386: CFLAGS+=-ffreestanding +hello-i386: LDFLAGS+=-nostdlib + +# +# test-386 includes a couple of additional objects that need to be linked together +# + +test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S test-i386.h test-i386-shift.h test-i386-muldiv.h + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ + $( Date: Tue, 17 Apr 2018 14:56:39 +0100 Subject: [PATCH 1441/2380] tests/tcg: enable building for i386 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While you can construct a compile command that does work using the x86_64 host compiler that most people use this is flakey. Different distros handle this is different ways so we default to using a known good i386 compiler via docker. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/i386/Makefile.include | 9 +++++++++ tests/tcg/i386/Makefile.target | 7 +++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/tcg/i386/Makefile.include diff --git a/tests/tcg/i386/Makefile.include b/tests/tcg/i386/Makefile.include new file mode 100644 index 0000000000..be1c3008dd --- /dev/null +++ b/tests/tcg/i386/Makefile.include @@ -0,0 +1,9 @@ +# +# Makefile.include for all i386 +# +# There is enough brokeness in x86_64 compilers that we don't default +# to using the x86_64 system compiler for i386 binaries. +# + +DOCKER_IMAGE=fedora-i386-cross +DOCKER_CROSS_COMPILER=gcc diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index 2f27b65e2d..8b46cc021e 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -28,3 +28,10 @@ hello-i386: LDFLAGS+=-nostdlib test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S test-i386.h test-i386-shift.h test-i386-muldiv.h $(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ $( runcom.out, "TEST", "$< on $(TARGET_NAME)") + +# On i386 and x86_64 Linux only supports 4k pages (large pages are a different hack) +EXTRA_RUNS+=run-test-mmap-4096 From 553a5a6046df3a7a9826ba7c955670094d12802b Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 8 Sep 2017 17:16:57 +0800 Subject: [PATCH 1442/2380] tests/tcg/i386: Build fix for hello-i386 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have -Werror=missing-prototype, add a dummy prototype to avoid that warning. Signed-off-by: Fam Zheng Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée --- tests/tcg/i386/hello-i386.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/tcg/i386/hello-i386.c b/tests/tcg/i386/hello-i386.c index fa00380de2..cfeb24b2f5 100644 --- a/tests/tcg/i386/hello-i386.c +++ b/tests/tcg/i386/hello-i386.c @@ -20,6 +20,7 @@ static inline int write(int fd, const char * buf, int len) return status; } +void _start(void); void _start(void) { write(1, "Hello World\n", 12); From 9ae02bf889649f39d45ecfdfbb6ee308465cfd05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 10 Apr 2018 15:16:45 +0100 Subject: [PATCH 1443/2380] tests/tcg/i386: fix test-i386 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't include anything from qemu itself for the build. Signed-off-by: Alex Bennée Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- tests/tcg/i386/test-i386.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/tcg/i386/test-i386.c b/tests/tcg/i386/test-i386.c index 9599204895..cae6a7773a 100644 --- a/tests/tcg/i386/test-i386.c +++ b/tests/tcg/i386/test-i386.c @@ -17,7 +17,6 @@ * along with this program; if not, see . */ #define _GNU_SOURCE -#include "qemu/compiler.h" #include #include #include From 25f9e7e8d722c283deb29decc01ab1a55f62b662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 10 Apr 2018 15:23:29 +0100 Subject: [PATCH 1444/2380] tests/tcg/i386: add runner for test-i386-fprem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The runner needs to compare against a reference run. We also only run this test when SPEED=slow as it takes a while. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/i386/Makefile.target | 16 ++++++++++++++++ tests/tcg/i386/test-i386-fprem.c | 12 +++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index 8b46cc021e..64d241cfdf 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -33,5 +33,21 @@ test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S test-i386.h test-i386 run-runcom: runcom pi_10.com $(call quiet-command, $(QEMU) ./runcom $(I386_SRC)/pi_10.com > runcom.out, "TEST", "$< on $(TARGET_NAME)") +ifeq ($(SPEED), slow) + +test-i386-fprem.ref: test-i386-fprem + $(call quiet-command, ./$< > $@,"GENREF","generating $@") + +run-test-i386-fprem: TIMEOUT=60 +run-test-i386-fprem: test-i386-fprem + $(call quiet-command, \ + $(QEMU) $< > $<.out && \ + diff -u $(I386_SRC)/$<.ref $<.out, \ + "TEST", "$< (default) on $(TARGET_NAME)") +else +run-test-i386-fprem: test-i386-fprem + $(call quiet-command, /bin/true, "SLOW TEST", "$< SKIPPED on $(TARGET_NAME)") +endif + # On i386 and x86_64 Linux only supports 4k pages (large pages are a different hack) EXTRA_RUNS+=run-test-mmap-4096 diff --git a/tests/tcg/i386/test-i386-fprem.c b/tests/tcg/i386/test-i386-fprem.c index 1a71623204..66f5a9657d 100644 --- a/tests/tcg/i386/test-i386-fprem.c +++ b/tests/tcg/i386/test-i386-fprem.c @@ -23,7 +23,10 @@ * along with this program; if not, see . */ -#include "qemu/osdep.h" +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) /* * Inspired by 's union ieee854_long_double, but with single @@ -39,7 +42,7 @@ union float80u { unsigned int exponent:15; unsigned int negative:1; unsigned int empty:16; - } QEMU_PACKED ieee; + } __attribute__((packed)) ieee; /* This is for NaNs in the IEEE 854 double-extended-precision format. */ struct { @@ -49,7 +52,7 @@ union float80u { unsigned int exponent:15; unsigned int negative:1; unsigned int empty:16; - } QEMU_PACKED ieee_nan; + } __attribute__((packed)) ieee_nan; }; #define IEEE854_LONG_DOUBLE_BIAS 0x3fff @@ -229,6 +232,7 @@ static void test_fprem_cases(void) do_fprem_stack_underflow(); printf("= invalid operation =\n"); + do_fprem(q_nan.d, 1.0); do_fprem(s_nan.d, 1.0); do_fprem(1.0, 0.0); do_fprem(pos_inf.d, 1.0); @@ -238,6 +242,8 @@ static void test_fprem_cases(void) do_fprem(pos_denorm.d, 1.0); do_fprem(1.0, pos_denorm.d); + do_fprem(smallest_positive_norm.d, smallest_positive_norm.d); + /* printf("= underflow =\n"); */ /* TODO: Is there a case where FPREM raises underflow? */ } From 3a082ec01b3993ff6575127042337614ca914e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 17 Apr 2018 11:14:37 +0100 Subject: [PATCH 1445/2380] tests/tcg/x86_64: add Makefile.target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sources for x86_64 are shared in the i386 directory which will be included thanks to TARGET_BASE_ARCH. However not all sources build so we need to filter out the ones we can't build in the 64 bit world and those that can't be built for 32 bit. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + tests/tcg/i386/Makefile.target | 4 ++-- tests/tcg/x86_64/Makefile.target | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/tcg/x86_64/Makefile.target diff --git a/MAINTAINERS b/MAINTAINERS index 590c24377c..096b5bbf61 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -284,6 +284,7 @@ M: Eduardo Habkost S: Maintained F: target/i386/ F: tests/tcg/i386/ +F: tests/tcg/x86_64/ F: hw/i386/ F: disas/i386.c T: git git://github.com/ehabkost/qemu.git x86-next diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index 64d241cfdf..cd173363ee 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -7,9 +7,9 @@ VPATH += $(I386_SRC) I386_SRCS=$(notdir $(wildcard $(I386_SRC)/*.c)) I386_TESTS=$(I386_SRCS:.c=) - +I386_ONLY_TESTS=$(filter-out test-i386-ssse3, $(I386_TESTS)) # Update TESTS -TESTS+=$(I386_TESTS) +TESTS+=$(I386_ONLY_TESTS) ifneq ($(TARGET_NAME),x86_64) CFLAGS+=-m32 diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target new file mode 100644 index 0000000000..74f170b9ed --- /dev/null +++ b/tests/tcg/x86_64/Makefile.target @@ -0,0 +1,15 @@ +# -*- Mode: makefile -*- +# +# x86_64 tests - included from tests/tcg/Makefile.target +# +# Currently we only build test-x86_64 and test-i386-ssse3 from +# $(SRC)/tests/tcg/i386/ +# + +X86_64_TESTS=$(filter-out $(I386_ONLY_TESTS), $(TESTS)) +X86_64_TESTS+=test-x86_64 +TESTS:=$(X86_64_TESTS) + +test-x86_64: LDFLAGS+=-lm -lc +test-x86_64: test-i386.c test-i386.h test-i386-shift.h test-i386-muldiv.h + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) From 9b8381d1ed9f7c73a5a3f0dbff635a49ddc3f232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Apr 2018 17:16:00 +0100 Subject: [PATCH 1446/2380] tests/tcg/i386/test-i386: use modern vector_size attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compiler complains about the old __mode__ style attributes. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/i386/test-i386.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tcg/i386/test-i386.c b/tests/tcg/i386/test-i386.c index cae6a7773a..caef4da176 100644 --- a/tests/tcg/i386/test-i386.c +++ b/tests/tcg/i386/test-i386.c @@ -2106,8 +2106,8 @@ static void test_enter(void) #ifdef TEST_SSE -typedef int __m64 __attribute__ ((__mode__ (__V2SI__))); -typedef float __m128 __attribute__ ((__mode__(__V4SF__))); +typedef int __m64 __attribute__ ((vector_size(8))); +typedef float __m128 __attribute__ ((vector_size(16))); typedef union { double d[2]; From e571ba67cbc6b641f0ee0a21e0e2b9432e9eb3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Apr 2018 17:17:37 +0100 Subject: [PATCH 1447/2380] tests/tcg/i386/test-i386: fix printf format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/i386/test-i386.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tcg/i386/test-i386.c b/tests/tcg/i386/test-i386.c index caef4da176..a29b41e764 100644 --- a/tests/tcg/i386/test-i386.c +++ b/tests/tcg/i386/test-i386.c @@ -2258,7 +2258,7 @@ SSE_OP(a ## sd); "pop %0\n"\ : "=rm" (eflags)\ : "x" (a.dq), "x" (b.dq));\ - printf("%-9s: a=%f b=%f cc=%04x\n",\ + printf("%-9s: a=%f b=%f cc=%04lx\n",\ #op, a1, b1,\ eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\ } From c348722c53a098d6a409ddc94b7ca20299815234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 5 Apr 2018 15:45:32 +0100 Subject: [PATCH 1448/2380] tests/tcg: move ARM specific tests into subdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These only need to be built for ARM guests. Signed-off-by: Alex Bennée Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- MAINTAINERS | 1 + tests/tcg/README | 9 --------- tests/tcg/arm/Makefile.target | 13 +++++++++++++ tests/tcg/arm/README | 11 +++++++++++ tests/tcg/{ => arm}/hello-arm.c | 0 tests/tcg/{ => arm}/test-arm-iwmmxt.s | 0 6 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 tests/tcg/arm/Makefile.target create mode 100644 tests/tcg/arm/README rename tests/tcg/{ => arm}/hello-arm.c (100%) rename tests/tcg/{ => arm}/test-arm-iwmmxt.s (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 096b5bbf61..a6d233a43a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -135,6 +135,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: target/arm/ +F: tests/tcg/arm/ F: hw/arm/ F: hw/cpu/a*mpcore.c F: include/hw/cpu/a*mpcore.h diff --git a/tests/tcg/README b/tests/tcg/README index 469504c4cb..625f2326e6 100644 --- a/tests/tcg/README +++ b/tests/tcg/README @@ -5,15 +5,6 @@ or they are architecture specific. -ARM -=== - -hello-arm ---------- - -test-arm-iwmmxt ---------------- - MIPS ==== diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target new file mode 100644 index 0000000000..bc6962ecc6 --- /dev/null +++ b/tests/tcg/arm/Makefile.target @@ -0,0 +1,13 @@ +# -*- Mode: makefile -*- +# +# ARM - included from tests/tcg/Makefile.target +# + +ARM_SRC=$(SRC_PATH)/tests/tcg/arm + +# Set search path for all sources +VPATH += $(ARM_SRC) + + +hello-arm: CFLAGS+=-marm -ffreestanding +hello-arm: LDFLAGS+=-nostdlib diff --git a/tests/tcg/arm/README b/tests/tcg/arm/README new file mode 100644 index 0000000000..e6307116e2 --- /dev/null +++ b/tests/tcg/arm/README @@ -0,0 +1,11 @@ +These are ARM specific guest programs + +hello-arm +--------- + +A very simple inline assembly, write syscall based hello world + +test-arm-iwmmxt +--------------- + +A simple test case for older iwmmxt extended ARMs diff --git a/tests/tcg/hello-arm.c b/tests/tcg/arm/hello-arm.c similarity index 100% rename from tests/tcg/hello-arm.c rename to tests/tcg/arm/hello-arm.c diff --git a/tests/tcg/test-arm-iwmmxt.s b/tests/tcg/arm/test-arm-iwmmxt.s similarity index 100% rename from tests/tcg/test-arm-iwmmxt.s rename to tests/tcg/arm/test-arm-iwmmxt.s From ab4aac50231473515eb20430e4002841b6505d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 7 May 2018 21:53:43 +0100 Subject: [PATCH 1449/2380] tests/tcg: enable building for ARM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to use the docker cross compiler image to build these tests. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/arm/Makefile.include | 8 ++++++++ tests/tcg/arm/Makefile.target | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/arm/Makefile.include diff --git a/tests/tcg/arm/Makefile.include b/tests/tcg/arm/Makefile.include new file mode 100644 index 0000000000..8e7eac008f --- /dev/null +++ b/tests/tcg/arm/Makefile.include @@ -0,0 +1,8 @@ +# Makefile.include for all ARM targets +# +# We don't have any bigendian build tools so we only use this for armhf + +ifeq ($(TARGET_NAME),arm) +DOCKER_IMAGE=debian-armhf-cross +DOCKER_CROSS_COMPILER=arm-linux-gnueabihf-gcc +endif diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index bc6962ecc6..0312293dca 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -1,6 +1,6 @@ # -*- Mode: makefile -*- # -# ARM - included from tests/tcg/Makefile.target +# ARM - included from tests/tcg/Makefile # ARM_SRC=$(SRC_PATH)/tests/tcg/arm @@ -11,3 +11,6 @@ VPATH += $(ARM_SRC) hello-arm: CFLAGS+=-marm -ffreestanding hello-arm: LDFLAGS+=-nostdlib + +# On ARM Linux only supports 4k pages +EXTRA_RUNS+=run-test-mmap-4096 From 65eab0f8cb2b13e03b8608fd255e4bc0c5df5a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Apr 2018 14:56:17 +0100 Subject: [PATCH 1450/2380] tests/tcg/arm: fix up test-arm-iwmmxt test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to rename the source file to a .S so we can do a single-line assemble and link invocation. We also specify the additional CFLAGS for the compile as it's a non-standard ARM binary. Signed-off-by: Alex Bennée [rth: force fpu configuration] Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- tests/tcg/arm/Makefile.target | 5 +++++ tests/tcg/arm/{test-arm-iwmmxt.s => test-arm-iwmmxt.S} | 0 2 files changed, 5 insertions(+) rename tests/tcg/arm/{test-arm-iwmmxt.s => test-arm-iwmmxt.S} (100%) diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 0312293dca..cadb385890 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -8,9 +8,14 @@ ARM_SRC=$(SRC_PATH)/tests/tcg/arm # Set search path for all sources VPATH += $(ARM_SRC) +TESTS += hello-arm test-arm-iwmmxt hello-arm: CFLAGS+=-marm -ffreestanding hello-arm: LDFLAGS+=-nostdlib +test-arm-iwmmxt: CFLAGS+=-marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16 +test-arm-iwmmxt: test-arm-iwmmxt.S + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + # On ARM Linux only supports 4k pages EXTRA_RUNS+=run-test-mmap-4096 diff --git a/tests/tcg/arm/test-arm-iwmmxt.s b/tests/tcg/arm/test-arm-iwmmxt.S similarity index 100% rename from tests/tcg/arm/test-arm-iwmmxt.s rename to tests/tcg/arm/test-arm-iwmmxt.S From 29e0436e3d86f750bed6ab2ff3132aeabb72c92a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 9 May 2018 10:31:30 +0100 Subject: [PATCH 1451/2380] tests/tcg: enable building for AArch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only have compilers for the (default) little endian variants. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + tests/tcg/aarch64/Makefile.include | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 tests/tcg/aarch64/Makefile.include diff --git a/MAINTAINERS b/MAINTAINERS index a6d233a43a..de1ae8423e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -136,6 +136,7 @@ L: qemu-arm@nongnu.org S: Maintained F: target/arm/ F: tests/tcg/arm/ +F: tests/tcg/aarch64/ F: hw/arm/ F: hw/cpu/a*mpcore.c F: include/hw/cpu/a*mpcore.h diff --git a/tests/tcg/aarch64/Makefile.include b/tests/tcg/aarch64/Makefile.include new file mode 100644 index 0000000000..de32c91235 --- /dev/null +++ b/tests/tcg/aarch64/Makefile.include @@ -0,0 +1,8 @@ +# Makefile.include for AArch64 targets +# +# We don't have any bigendian build tools so we only use this for AArch64 + +ifeq ($(TARGET_NAME),aarch64) +DOCKER_IMAGE=debian-arm64-cross +DOCKER_CROSS_COMPILER=aarch64-linux-gnu-gcc +endif From 8ec8a55e3fc9769c9904ba0dea81414784a6d527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Apr 2018 17:08:26 +0100 Subject: [PATCH 1452/2380] tests/tcg/arm: add fcvt test cases for AArch32/64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This runs through the usual float to float conversions and crucially also runs with ARM Alternative Half Precision Format. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/aarch64/Makefile.target | 19 + tests/tcg/aarch64/fcvt.ref | 3268 +++++++++++++++++++++++++++++ tests/tcg/arm/Makefile.target | 15 +- tests/tcg/arm/fcvt.c | 458 ++++ tests/tcg/arm/fcvt.ref | 3268 +++++++++++++++++++++++++++++ 5 files changed, 7027 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/aarch64/Makefile.target create mode 100644 tests/tcg/aarch64/fcvt.ref create mode 100644 tests/tcg/arm/fcvt.c create mode 100644 tests/tcg/arm/fcvt.ref diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target new file mode 100644 index 0000000000..7dba32138d --- /dev/null +++ b/tests/tcg/aarch64/Makefile.target @@ -0,0 +1,19 @@ +# -*- Mode: makefile -*- +# +# AArch64 specific tweaks + +AARCH64_SRC=$(SRC_PATH)/tests/tcg/aarch64 +VPATH += $(AARCH64_SRC) + +# we don't build any of the ARM tests +AARCH64_TESTS=$(filter-out $(ARM_TESTS), $(TESTS)) +AARCH64_TESTS+=fcvt +TESTS:=$(AARCH64_TESTS) + +fcvt: LDFLAGS+=-lm + +run-fcvt: fcvt + $(call quiet-command, \ + $(QEMU) $< > fcvt.out && \ + diff -u $(AARCH64_SRC)/fcvt.ref fcvt.out, \ + "TEST", "$< (default) on $(TARGET_NAME)") diff --git a/tests/tcg/aarch64/fcvt.ref b/tests/tcg/aarch64/fcvt.ref new file mode 100644 index 0000000000..e7af24dc58 --- /dev/null +++ b/tests/tcg/aarch64/fcvt.ref @@ -0,0 +1,3268 @@ +#### Enabling IEEE Half Precision +### Rounding to nearest +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0x7f00 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x4170 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bff (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7c00 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0x7e00 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0x7e00 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0x7f00 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -nan / 0x00fffffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -nan / 0x00fffbfc0000000000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -nan / 0x00fff8040000000000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: nan / 0x007ff8040000000000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: nan / 0x007ffbfc0000000000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: nan / 0x007ffffc0000000000 (0 => OK) +### Rounding upwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x401 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4249 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0x7f00 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x401 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x4170 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4249 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bff (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7c00 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0x7e00 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0x7e00 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0x7f00 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -nan / 0x00fffffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -nan / 0x00fffbfc0000000000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -nan / 0x00fff8040000000000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: nan / 0x007ff8040000000000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: nan / 0x007ffbfc0000000000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: nan / 0x007ffffc0000000000 (0 => OK) +### Rounding downwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730512e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0x7f00 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x416f (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bfe (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7c00 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0x7e00 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0x7e00 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0x7f00 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -nan / 0x00fffffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -nan / 0x00fffbfc0000000000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -nan / 0x00fff8040000000000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: nan / 0x007ff8040000000000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: nan / 0x007ffbfc0000000000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: nan / 0x007ffffc0000000000 (0 => OK) +### Rounding to zero +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0x7f00 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x416f (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bfe (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7c00 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0x7e00 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0x7e00 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0x7f00 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -nan / 0x00fffffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -nan / 0x00fffbfc0000000000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -nan / 0x00fff8040000000000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: nan / 0x007ff8040000000000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: nan / 0x007ffbfc0000000000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: nan / 0x007ffffc0000000000 (0 => OK) +Converting single-precision to integer +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 INT64: 1/0x000000000000000001 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 INT64: 2/0x000000000000000002 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 INT64: 65503/0x00000000000000ffdf (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 INT64: 65504/0x00000000000000ffe0 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 INT64: 65505/0x00000000000000ffe1 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 INT64: 131007/0x00000000000001ffbf (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 INT64: 131008/0x00000000000001ffc0 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 INT64: 131009/0x00000000000001ffc1 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 INT64: 0/00000000000000000000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting double-precision to integer +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 INT64: -2/0x00fffffffffffffffe (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 INT64: -1/0x00ffffffffffffffff (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 INT64: 0/00000000000000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 INT64: 1/0x000000000000000001 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 INT64: 1/0x000000000000000001 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 INT64: 2/0x000000000000000002 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 INT64: 65503/0x00000000000000ffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 INT64: 65504/0x00000000000000ffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 INT64: 65505/0x00000000000000ffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 INT64: 131007/0x00000000000001ffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 INT64: 131008/0x00000000000001ffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 INT64: 131009/0x00000000000001ffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 INT64: 2147483647/0x00000000007fffffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 INT64: 0/00000000000000000000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 INT64: 0/00000000000000000000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting half-precision to integer +00 HALF: 0xffff (0 => OK) +00 INT64: 4294959104/0x0000000000ffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 INT64: 4292861952/0x0000000000ffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 INT64: 4290781184/0x0000000000ffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 INT64: 4286578688/0x0000000000ff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 INT64: 3347046400/0x0000000000c77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 INT64: 3221225472/0x0000000000c0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 INT64: 3212836864/0x0000000000bf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 INT64: 3011510272/0x0000000000b3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 INT64: 2147483648/0x000000000080000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 INT64: 864026624/0x000000000033800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 INT64: 1065353216/0x00000000003f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 INT64: 1199562752/0x0000000000477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 INT64: 2139095040/0x00000000007f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 INT64: 2143297536/0x00000000007fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 INT64: 2145378304/0x00000000007fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 INT64: 2147475456/0x00000000007fffe000 (0 => OK) +#### Enabling ARM Alternative Half Precision +### Rounding to nearest +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7fff (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xffff (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xffff (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x4170 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bff (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7fff (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7fff (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7fff (0x10 => INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7fff (0x1 => INVALID) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7fff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7fff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7fff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7fff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -1.31008000000000000000e+05 / 0x00c0fffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -8.18560000000000000000e+04 / 0x00c0f3fc0000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -6.56000000000000000000e+04 / 0x00c0f0040000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -6.55360000000000000000e+04 / 0x00c0f0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 6.55360000000000000000e+04 / 0x0040f0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 6.56000000000000000000e+04 / 0x0040f0040000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 8.18560000000000000000e+04 / 0x0040f3fc0000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +### Rounding upwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x401 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4249 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7c00 (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7fff (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x1 => INVALID) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xffff (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xffff (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x401 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x4170 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4249 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bff (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7c00 (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7fff (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7fff (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7fff (0x1 => INVALID) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7fff (0x1 => INVALID) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7fff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7fff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7fff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7fff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -1.31008000000000000000e+05 / 0x00c0fffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -8.18560000000000000000e+04 / 0x00c0f3fc0000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -6.56000000000000000000e+04 / 0x00c0f0040000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -6.55360000000000000000e+04 / 0x00c0f0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 6.55360000000000000000e+04 / 0x0040f0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 6.56000000000000000000e+04 / 0x0040f0040000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 8.18560000000000000000e+04 / 0x0040f3fc0000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +### Rounding downwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7ffe (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730512e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xffff (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xffff (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x416f (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bfe (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7ffe (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7fff (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7fff (0x10 => INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7fff (0x1 => INVALID) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7fff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7fff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7fff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7fff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -1.31008000000000000000e+05 / 0x00c0fffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -8.18560000000000000000e+04 / 0x00c0f3fc0000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -6.56000000000000000000e+04 / 0x00c0f0040000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -6.55360000000000000000e+04 / 0x00c0f0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 6.55360000000000000000e+04 / 0x0040f0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 6.56000000000000000000e+04 / 0x0040f0040000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 8.18560000000000000000e+04 / 0x0040f3fc0000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +### Rounding to zero +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7ffe (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xffff (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xffff (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x416f (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bfe (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7ffe (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7fff (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7fff (0x10 => INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7fff (0x1 => INVALID) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7fff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7fff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7fff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7fff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -1.31008000000000000000e+05 / 0x00c0fffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -8.18560000000000000000e+04 / 0x00c0f3fc0000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -6.56000000000000000000e+04 / 0x00c0f0040000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -6.55360000000000000000e+04 / 0x00c0f0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 6.55360000000000000000e+04 / 0x0040f0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 6.56000000000000000000e+04 / 0x0040f0040000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 8.18560000000000000000e+04 / 0x0040f3fc0000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +Converting single-precision to integer +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 INT64: 1/0x000000000000000001 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 INT64: 2/0x000000000000000002 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 INT64: 65503/0x00000000000000ffdf (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 INT64: 65504/0x00000000000000ffe0 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 INT64: 65505/0x00000000000000ffe1 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 INT64: 131007/0x00000000000001ffbf (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 INT64: 131008/0x00000000000001ffc0 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 INT64: 131009/0x00000000000001ffc1 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 INT64: 0/00000000000000000000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting double-precision to integer +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 INT64: -2/0x00fffffffffffffffe (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 INT64: -1/0x00ffffffffffffffff (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 INT64: 0/00000000000000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 INT64: 1/0x000000000000000001 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 INT64: 1/0x000000000000000001 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 INT64: 2/0x000000000000000002 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 INT64: 65503/0x00000000000000ffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 INT64: 65504/0x00000000000000ffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 INT64: 65505/0x00000000000000ffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 INT64: 131007/0x00000000000001ffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 INT64: 131008/0x00000000000001ffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 INT64: 131009/0x00000000000001ffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 INT64: 2147483647/0x00000000007fffffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 INT64: 0/00000000000000000000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 INT64: 0/00000000000000000000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting half-precision to integer +00 HALF: 0xffff (0 => OK) +00 INT64: 3355435008/0x0000000000c7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 INT64: 3349143552/0x0000000000c79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 INT64: 3347062784/0x0000000000c7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 INT64: 3347054592/0x0000000000c7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 INT64: 3347046400/0x0000000000c77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 INT64: 3221225472/0x0000000000c0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 INT64: 3212836864/0x0000000000bf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 INT64: 3011510272/0x0000000000b3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 INT64: 2147483648/0x000000000080000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 INT64: 864026624/0x000000000033800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 INT64: 1065353216/0x00000000003f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 INT64: 1199562752/0x0000000000477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 INT64: 1199570944/0x000000000047800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 INT64: 1199579136/0x000000000047802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 INT64: 1201659904/0x0000000000479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 INT64: 1207951360/0x000000000047ffe000 (0 => OK) diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index cadb385890..14b5435fc6 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -8,7 +8,9 @@ ARM_SRC=$(SRC_PATH)/tests/tcg/arm # Set search path for all sources VPATH += $(ARM_SRC) -TESTS += hello-arm test-arm-iwmmxt +ARM_TESTS=hello-arm test-arm-iwmmxt + +TESTS += $(ARM_TESTS) fcvt hello-arm: CFLAGS+=-marm -ffreestanding hello-arm: LDFLAGS+=-nostdlib @@ -17,5 +19,16 @@ test-arm-iwmmxt: CFLAGS+=-marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16 test-arm-iwmmxt: test-arm-iwmmxt.S $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) +ifeq ($(TARGET_NAME), arm) +fcvt: LDFLAGS+=-lm +# fcvt: CFLAGS+=-march=armv8.2-a+fp16 -mfpu=neon-fp-armv8 + +run-fcvt: fcvt + $(call quiet-command, \ + $(QEMU) $< > fcvt.out && \ + diff -u $(ARM_SRC)/fcvt.ref fcvt.out, \ + "TEST", "$< (default) on $(TARGET_NAME)") +endif + # On ARM Linux only supports 4k pages EXTRA_RUNS+=run-test-mmap-4096 diff --git a/tests/tcg/arm/fcvt.c b/tests/tcg/arm/fcvt.c new file mode 100644 index 0000000000..617626bc63 --- /dev/null +++ b/tests/tcg/arm/fcvt.c @@ -0,0 +1,458 @@ +/* + * Test Floating Point Conversion + */ + +/* we want additional float type definitions */ +#define __STDC_WANT_IEC_60559_BFP_EXT__ +#define __STDC_WANT_IEC_60559_TYPES_EXT__ + +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +static char flag_str[256]; + +static char *get_flag_state(int flags) +{ + if (flags) { + snprintf(flag_str, sizeof(flag_str), "%s %s %s %s %s", + flags & FE_OVERFLOW ? "OVERFLOW" : "", + flags & FE_UNDERFLOW ? "UNDERFLOW" : "", + flags & FE_DIVBYZERO ? "DIV0" : "", + flags & FE_INEXACT ? "INEXACT" : "", + flags & FE_INVALID ? "INVALID" : ""); + } else { + snprintf(flag_str, sizeof(flag_str), "OK"); + } + + return flag_str; +} + +static void print_double_number(int i, double num) +{ + uint64_t double_as_hex = *(uint64_t *) # + int flags = fetestexcept(FE_ALL_EXCEPT); + char *fstr = get_flag_state(flags); + + printf("%02d DOUBLE: %02.20e / %#020" PRIx64 " (%#x => %s)\n", + i, num, double_as_hex, flags, fstr); +} + +static void print_single_number(int i, float num) +{ + uint32_t single_as_hex = *(uint32_t *) # + int flags = fetestexcept(FE_ALL_EXCEPT); + char *fstr = get_flag_state(flags); + + printf("%02d SINGLE: %02.20e / %#010x (%#x => %s)\n", + i, num, single_as_hex, flags, fstr); +} + +static void print_half_number(int i, uint16_t num) +{ + int flags = fetestexcept(FE_ALL_EXCEPT); + char *fstr = get_flag_state(flags); + + printf("%02d HALF: %#04x (%#x => %s)\n", + i, num, flags, fstr); +} + +static void print_int64(int i, int64_t num) +{ + uint64_t int64_as_hex = *(uint64_t *) # + int flags = fetestexcept(FE_ALL_EXCEPT); + char *fstr = get_flag_state(flags); + + printf("%02d INT64: %20" PRId64 "/%#020" PRIx64 " (%#x => %s)\n", + i, num, int64_as_hex, flags, fstr); +} + +#ifndef SNANF +/* Signaling NaN macros, if supported. */ +# if __GNUC_PREREQ(3, 3) +# define SNANF (__builtin_nansf ("")) +# define SNAN (__builtin_nans ("")) +# define SNANL (__builtin_nansl ("")) +# endif +#endif + +float single_numbers[] = { -SNANF, + -NAN, + -INFINITY, + -FLT_MAX, + -1.111E+31, + -1.111E+30, + -1.08700982e-12, + -1.78051176e-20, + -FLT_MIN, + 0.0, + FLT_MIN, + 2.98023224e-08, + 5.96046E-8, /* min positive FP16 subnormal */ + 6.09756E-5, /* max subnormal FP16 */ + 6.10352E-5, /* min positive normal FP16 */ + 1.0, + 1.0009765625, /* smallest float after 1.0 FP16 */ + 2.0, + M_E, M_PI, + 65503.0, + 65504.0, /* max FP16 */ + 65505.0, + 131007.0, + 131008.0, /* max AFP */ + 131009.0, + 1.111E+30, + FLT_MAX, + INFINITY, + NAN, + SNANF }; + +static void convert_single_to_half(void) +{ + int i; + + printf("Converting single-precision to half-precision\n"); + + for (i = 0; i < ARRAY_SIZE(single_numbers); ++i) { + float input = single_numbers[i]; + + feclearexcept(FE_ALL_EXCEPT); + + print_single_number(i, input); +#if defined(__arm__) + uint32_t output; + asm("vcvtb.f16.f32 %0, %1" : "=t" (output) : "x" (input)); +#else + uint16_t output; + asm("fcvt %h0, %s1" : "=w" (output) : "x" (input)); +#endif + print_half_number(i, output); + } +} + +static void convert_single_to_double(void) +{ + int i; + + printf("Converting single-precision to double-precision\n"); + + for (i = 0; i < ARRAY_SIZE(single_numbers); ++i) { + float input = single_numbers[i]; + /* uint64_t output; */ + double output; + + feclearexcept(FE_ALL_EXCEPT); + + print_single_number(i, input); +#if defined(__arm__) + asm("vcvt.f64.f32 %P0, %1" : "=w" (output) : "t" (input)); +#else + asm("fcvt %d0, %s1" : "=w" (output) : "x" (input)); +#endif + print_double_number(i, output); + } +} + +static void convert_single_to_integer(void) +{ + int i; + + printf("Converting single-precision to integer\n"); + + for (i = 0; i < ARRAY_SIZE(single_numbers); ++i) { + float input = single_numbers[i]; + int64_t output; + + feclearexcept(FE_ALL_EXCEPT); + + print_single_number(i, input); +#if defined(__arm__) + /* asm("vcvt.s32.f32 %s0, %s1" : "=t" (output) : "t" (input)); */ + output = input; +#else + asm("fcvtzs %0, %s1" : "=r" (output) : "w" (input)); +#endif + print_int64(i, output); + } +} + +/* This allows us to initialise some doubles as pure hex */ +typedef union { + double d; + uint64_t h; +} test_doubles; + +test_doubles double_numbers[] = { + {SNAN}, + {-NAN}, + {-INFINITY}, + {-DBL_MAX}, + {-FLT_MAX-1.0}, + {-FLT_MAX}, + {-1.111E+31}, + {-1.111E+30}, /* half prec */ + {-2.0}, {-1.0}, + {-DBL_MIN}, + {-FLT_MIN}, + {0.0}, + {FLT_MIN}, + {2.98023224e-08}, + {5.96046E-8}, /* min positive FP16 subnormal */ + {6.09756E-5}, /* max subnormal FP16 */ + {6.10352E-5}, /* min positive normal FP16 */ + {1.0}, + {1.0009765625}, /* smallest float after 1.0 FP16 */ + {DBL_MIN}, + {1.3789972848607228e-308}, + {1.4914738736681624e-308}, + {1.0}, {2.0}, + {M_E}, {M_PI}, + {65503.0}, + {65504.0}, /* max FP16 */ + {65505.0}, + {131007.0}, + {131008.0}, /* max AFP */ + {131009.0}, + {.h = 0x41dfffffffc00000 }, /* to int = 0x7fffffff */ + {FLT_MAX}, + {FLT_MAX + 1.0}, + {DBL_MAX}, + {INFINITY}, + {NAN}, + {.h = 0x7ff0000000000001}, /* SNAN */ + {SNAN}, +}; + +static void convert_double_to_half(void) +{ + int i; + + printf("Converting double-precision to half-precision\n"); + + for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) { + double input = double_numbers[i].d; + uint16_t output; + + feclearexcept(FE_ALL_EXCEPT); + + print_double_number(i, input); + + /* as we don't have _Float16 support */ +#if defined(__arm__) + /* asm("vcvtb.f16.f64 %0, %P1" : "=t" (output) : "x" (input)); */ + output = input; +#else + asm("fcvt %h0, %d1" : "=w" (output) : "x" (input)); +#endif + print_half_number(i, output); + } +} + +static void convert_double_to_single(void) +{ + int i; + + printf("Converting double-precision to single-precision\n"); + + for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) { + double input = double_numbers[i].d; + uint32_t output; + + feclearexcept(FE_ALL_EXCEPT); + + print_double_number(i, input); + +#if defined(__arm__) + asm("vcvt.f32.f64 %0, %P1" : "=w" (output) : "x" (input)); +#else + asm("fcvt %s0, %d1" : "=w" (output) : "x" (input)); +#endif + + print_single_number(i, output); + } +} + +static void convert_double_to_integer(void) +{ + int i; + + printf("Converting double-precision to integer\n"); + + for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) { + double input = double_numbers[i].d; + int64_t output; + + feclearexcept(FE_ALL_EXCEPT); + + print_double_number(i, input); +#if defined(__arm__) + /* asm("vcvt.s32.f32 %s0, %s1" : "=t" (output) : "t" (input)); */ + output = input; +#else + asm("fcvtzs %0, %d1" : "=r" (output) : "w" (input)); +#endif + print_int64(i, output); + } +} + +/* no handy defines for these numbers */ +uint16_t half_numbers[] = { + 0xffff, /* -NaN / AHP -Max */ + 0xfcff, /* -NaN / AHP */ + 0xfc01, /* -NaN / AHP */ + 0xfc00, /* -Inf */ + 0xfbff, /* -Max */ + 0xc000, /* -2 */ + 0xbc00, /* -1 */ + 0x8001, /* -MIN subnormal */ + 0x8000, /* -0 */ + 0x0000, /* +0 */ + 0x0001, /* MIN subnormal */ + 0x3c00, /* 1 */ + 0x7bff, /* Max */ + 0x7c00, /* Inf */ + 0x7c01, /* NaN / AHP */ + 0x7cff, /* NaN / AHP */ + 0x7fff, /* NaN / AHP +Max*/ +}; + +static void convert_half_to_double(void) +{ + int i; + + printf("Converting half-precision to double-precision\n"); + + for (i = 0; i < ARRAY_SIZE(half_numbers); ++i) { + uint16_t input = half_numbers[i]; + double output; + + feclearexcept(FE_ALL_EXCEPT); + + print_half_number(i, input); +#if defined(__arm__) + /* asm("vcvtb.f64.f16 %P0, %1" : "=w" (output) : "t" (input)); */ + output = input; +#else + asm("fcvt %d0, %h1" : "=w" (output) : "x" (input)); +#endif + print_double_number(i, output); + } +} + +static void convert_half_to_single(void) +{ + int i; + + printf("Converting half-precision to single-precision\n"); + + for (i = 0; i < ARRAY_SIZE(half_numbers); ++i) { + uint16_t input = half_numbers[i]; + float output; + + feclearexcept(FE_ALL_EXCEPT); + + print_half_number(i, input); +#if defined(__arm__) + asm("vcvtb.f32.f16 %0, %1" : "=w" (output) : "x" ((uint32_t)input)); +#else + asm("fcvt %s0, %h1" : "=w" (output) : "x" (input)); +#endif + print_single_number(i, output); + } +} + +static void convert_half_to_integer(void) +{ + int i; + + printf("Converting half-precision to integer\n"); + + for (i = 0; i < ARRAY_SIZE(half_numbers); ++i) { + uint16_t input = half_numbers[i]; + int64_t output; + + feclearexcept(FE_ALL_EXCEPT); + + print_half_number(i, input); +#if defined(__arm__) + /* asm("vcvt.s32.f16 %0, %1" : "=t" (output) : "t" (input)); v8.2*/ + output = input; +#else + asm("fcvt %s0, %h1" : "=w" (output) : "x" (input)); +#endif + print_int64(i, output); + } +} + +typedef struct { + int flag; + char *desc; +} float_mapping; + +float_mapping round_flags[] = { + { FE_TONEAREST, "to nearest" }, + { FE_UPWARD, "upwards" }, + { FE_DOWNWARD, "downwards" }, + { FE_TOWARDZERO, "to zero" } +}; + +int main(int argc, char *argv[argc]) +{ + int i; + + printf("#### Enabling IEEE Half Precision\n"); + + for (i = 0; i < ARRAY_SIZE(round_flags); ++i) { + fesetround(round_flags[i].flag); + printf("### Rounding %s\n", round_flags[i].desc); + convert_single_to_half(); + convert_single_to_double(); + convert_double_to_half(); + convert_double_to_single(); + convert_half_to_single(); + convert_half_to_double(); + } + + /* convert to integer */ + convert_single_to_integer(); + convert_double_to_integer(); + convert_half_to_integer(); + + /* And now with ARM alternative FP16 */ +#if defined(__arm__) + /* See glibc sysdeps/arm/fpu_control.h */ + asm("mrc p10, 7, r1, cr1, cr0, 0\n\t" + "orr r1, r1, %[flags]\n\t" + "mcr p10, 7, r1, cr1, cr0, 0\n\t" + : /* no output */ : [flags] "n" (1 << 26) : "r1" ); +#else + asm("mrs x1, fpcr\n\t" + "orr x1, x1, %[flags]\n\t" + "msr fpcr, x1\n\t" + : /* no output */ : [flags] "n" (1 << 26) : "x1" ); +#endif + + printf("#### Enabling ARM Alternative Half Precision\n"); + + for (i = 0; i < ARRAY_SIZE(round_flags); ++i) { + fesetround(round_flags[i].flag); + printf("### Rounding %s\n", round_flags[i].desc); + convert_single_to_half(); + convert_single_to_double(); + convert_double_to_half(); + convert_double_to_single(); + convert_half_to_single(); + convert_half_to_double(); + } + + /* convert to integer */ + convert_single_to_integer(); + convert_double_to_integer(); + convert_half_to_integer(); + + return 0; +} diff --git a/tests/tcg/arm/fcvt.ref b/tests/tcg/arm/fcvt.ref new file mode 100644 index 0000000000..f052b6d7e5 --- /dev/null +++ b/tests/tcg/arm/fcvt.ref @@ -0,0 +1,3268 @@ +#### Enabling IEEE Half Precision +### Rounding to nearest +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding upwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x401 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4249 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding downwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730512e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding to zero +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +Converting single-precision to integer +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 INT64: 1/0x000000000000000001 (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 INT64: 65503/0x00000000000000ffdf (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 INT64: 65504/0x00000000000000ffe0 (0x10 => INEXACT ) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 INT64: 65505/0x00000000000000ffe1 (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 INT64: 131007/0x00000000000001ffbf (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 INT64: 131008/0x00000000000001ffc0 (0x10 => INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 INT64: 131009/0x00000000000001ffc1 (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 INT64: -1/0x00ffffffffffffffff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 INT64: 0/00000000000000000000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting double-precision to integer +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 INT64: 1/0x000000000000000001 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 INT64: -2/0x00fffffffffffffffe (0x10 => INEXACT ) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 INT64: -1/0x00ffffffffffffffff (0x10 => INEXACT ) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 INT64: 0/00000000000000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 INT64: 0/00000000000000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 INT64: 0/00000000000000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 INT64: 65503/0x00000000000000ffdf (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 INT64: 65504/0x00000000000000ffe0 (0x10 => INEXACT ) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 INT64: 65505/0x00000000000000ffe1 (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 INT64: 131007/0x00000000000001ffbf (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 INT64: 131008/0x00000000000001ffc0 (0x10 => INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 INT64: 131009/0x00000000000001ffc1 (0x10 => INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 INT64: 2147483647/0x00000000007fffffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 INT64: -1/0x00ffffffffffffffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 INT64: 0/00000000000000000000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 INT64: 0/00000000000000000000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting half-precision to integer +00 HALF: 0xffff (0 => OK) +00 INT64: 65535/0x00000000000000ffff (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 INT64: 64767/0x00000000000000fcff (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 INT64: 64513/0x00000000000000fc01 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 INT64: 64512/0x00000000000000fc00 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 INT64: 64511/0x00000000000000fbff (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 INT64: 49152/0x00000000000000c000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 INT64: 48128/0x00000000000000bc00 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 INT64: 32769/0x000000000000008001 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 INT64: 32768/0x000000000000008000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 INT64: 1/0x000000000000000001 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 INT64: 15360/0x000000000000003c00 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 INT64: 31743/0x000000000000007bff (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 INT64: 31744/0x000000000000007c00 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 INT64: 31745/0x000000000000007c01 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 INT64: 31999/0x000000000000007cff (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 INT64: 32767/0x000000000000007fff (0 => OK) +#### Enabling ARM Alternative Half Precision +### Rounding to nearest +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7fff (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding upwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x401 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4249 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7c00 (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7fff (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x1 => INVALID) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding downwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7ffe (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730512e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding to zero +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7ffe (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +Converting single-precision to integer +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 INT64: 1/0x000000000000000001 (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 INT64: 65503/0x00000000000000ffdf (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 INT64: 65504/0x00000000000000ffe0 (0x10 => INEXACT ) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 INT64: 65505/0x00000000000000ffe1 (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 INT64: 131007/0x00000000000001ffbf (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 INT64: 131008/0x00000000000001ffc0 (0x10 => INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 INT64: 131009/0x00000000000001ffc1 (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 INT64: -1/0x00ffffffffffffffff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 INT64: 0/00000000000000000000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting double-precision to integer +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 INT64: 1/0x000000000000000001 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 INT64: -2/0x00fffffffffffffffe (0x10 => INEXACT ) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 INT64: -1/0x00ffffffffffffffff (0x10 => INEXACT ) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 INT64: 0/00000000000000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 INT64: 0/00000000000000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 INT64: 0/00000000000000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 INT64: 65503/0x00000000000000ffdf (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 INT64: 65504/0x00000000000000ffe0 (0x10 => INEXACT ) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 INT64: 65505/0x00000000000000ffe1 (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 INT64: 131007/0x00000000000001ffbf (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 INT64: 131008/0x00000000000001ffc0 (0x10 => INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 INT64: 131009/0x00000000000001ffc1 (0x10 => INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 INT64: 2147483647/0x00000000007fffffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 INT64: -1/0x00ffffffffffffffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 INT64: 0/00000000000000000000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 INT64: 0/00000000000000000000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting half-precision to integer +00 HALF: 0xffff (0 => OK) +00 INT64: 65535/0x00000000000000ffff (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 INT64: 64767/0x00000000000000fcff (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 INT64: 64513/0x00000000000000fc01 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 INT64: 64512/0x00000000000000fc00 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 INT64: 64511/0x00000000000000fbff (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 INT64: 49152/0x00000000000000c000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 INT64: 48128/0x00000000000000bc00 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 INT64: 32769/0x000000000000008001 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 INT64: 32768/0x000000000000008000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 INT64: 1/0x000000000000000001 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 INT64: 15360/0x000000000000003c00 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 INT64: 31743/0x000000000000007bff (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 INT64: 31744/0x000000000000007c00 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 INT64: 31745/0x000000000000007c01 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 INT64: 31999/0x000000000000007cff (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 INT64: 32767/0x000000000000007fff (0 => OK) From 106ea2db123ff4ed57b38b4c0084b46833a3ae4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 5 Apr 2018 15:50:08 +0100 Subject: [PATCH 1453/2380] tests/tcg: move MIPS specific tests into subdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These only need to be built for MIPS guests. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/README | 11 ----------- tests/tcg/mips/README | 7 +++++++ tests/tcg/{ => mips}/hello-mips.c | 0 3 files changed, 7 insertions(+), 11 deletions(-) create mode 100644 tests/tcg/mips/README rename tests/tcg/{ => mips}/hello-mips.c (100%) diff --git a/tests/tcg/README b/tests/tcg/README index 625f2326e6..a5643d33e7 100644 --- a/tests/tcg/README +++ b/tests/tcg/README @@ -3,17 +3,6 @@ regression testing. Tests are either multi-arch, meaning they can be built for all guest architectures that support linux-user executable, or they are architecture specific. - - -MIPS -==== - -hello-mips ----------- - -hello-mipsel ------------- - CRIS ==== The testsuite for CRIS is in tests/tcg/cris. You can run it diff --git a/tests/tcg/mips/README b/tests/tcg/mips/README new file mode 100644 index 0000000000..e5bbc58ec5 --- /dev/null +++ b/tests/tcg/mips/README @@ -0,0 +1,7 @@ +MIPS +==== + +hello-mips +---------- + +A very simple inline assembly, write syscall based hello world diff --git a/tests/tcg/hello-mips.c b/tests/tcg/mips/hello-mips.c similarity index 100% rename from tests/tcg/hello-mips.c rename to tests/tcg/mips/hello-mips.c From 7086ffbb933bda7b1fa8bff2ce87c031a9d9acf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 17 Apr 2018 14:55:17 +0100 Subject: [PATCH 1454/2380] tests/tcg: enable building for MIPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This doesn't add any additional tests but enables building the multiarch tests for MIPS using docker cross compilers. We don't have a cross compiler for mips64 big endian though. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/mips/Makefile.include | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/tcg/mips/Makefile.include diff --git a/tests/tcg/mips/Makefile.include b/tests/tcg/mips/Makefile.include new file mode 100644 index 0000000000..a9beceb623 --- /dev/null +++ b/tests/tcg/mips/Makefile.include @@ -0,0 +1,17 @@ +# +# Makefile.include for all MIPs targets +# +# As Debian doesn't support mip64 in big endian mode the only way to +# build BE is to pass a working cross compiler to ./configure +# + +ifeq ($(TARGET_NAME),mips64el) +DOCKER_IMAGE=debian-mips64el-cross +DOCKER_CROSS_COMPILER=mips64el-linux-gnuabi64-gcc +else ifeq ($(TARGET_NAME),mipsel) +DOCKER_IMAGE=debian-mipsel-cross +DOCKER_CROSS_COMPILER=mipsel-linux-gnu-gcc +else ifeq ($(TARGET_NAME),mips) +DOCKER_IMAGE=debian-mips-cross +DOCKER_CROSS_COMPILER=mips-linux-gnu-gcc +endif From 6bf77518ddc37cec317334e057cc6e2876aa7f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 11 Jun 2018 11:17:13 +0100 Subject: [PATCH 1455/2380] tests/docker/Makefile.include: fix mipsel-cross dependancy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This got broken in commit 4319db7 but generally only shows up when you try and do massive parallel builds on fresh machines. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 8afb383478..4a2b028a3b 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -57,6 +57,7 @@ docker-image-debian-armel-cross: docker-image-debian9 docker-image-debian-armhf-cross: docker-image-debian9 docker-image-debian-arm64-cross: docker-image-debian9 docker-image-debian-mips-cross: docker-image-debian9 +docker-image-debian-mipsel-cross: docker-image-debian9 docker-image-debian-mips64el-cross: docker-image-debian9 docker-image-debian-powerpc-cross: docker-image-debian8 docker-image-debian-ppc64el-cross: docker-image-debian9 From 2ac93f19b69d7729841f9c6f1d3d4688f7a2ebca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 17 Apr 2018 11:31:02 +0100 Subject: [PATCH 1456/2380] tests/tcg/mips: include common mips hello-mips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/mips/Makefile.target | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/tcg/mips/Makefile.target diff --git a/tests/tcg/mips/Makefile.target b/tests/tcg/mips/Makefile.target new file mode 100644 index 0000000000..71f0c2dd53 --- /dev/null +++ b/tests/tcg/mips/Makefile.target @@ -0,0 +1,19 @@ +# -*- Mode: makefile -*- +# +# MIPS - included from tests/tcg/Makefile.target +# + +MIPS_SRC=$(SRC_PATH)/tests/tcg/mips + +# Set search path for all sources +VPATH += $(MIPS_SRC) + +MIPS_TESTS=hello-mips + +TESTS += $(MIPS_TESTS) + +hello-mips: CFLAGS+=-ffreestanding +hello-mips: LDFLAGS+=-nostdlib + +# For MIPS32 and 64 we have a bunch of extra tests in sub-directories +# however they are intended for system tests. From 78353f49d2de0e9626276231315631bf286c8ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 6 Apr 2018 21:43:57 +0100 Subject: [PATCH 1457/2380] tests/tcg: enable building for s390x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This doesn't add any additional tests but enables building the multiarch tests for s390x. Signed-off-by: Alex Bennée Acked-by: Cornelia Huck Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand --- tests/tcg/s390x/Makefile.include | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/tcg/s390x/Makefile.include diff --git a/tests/tcg/s390x/Makefile.include b/tests/tcg/s390x/Makefile.include new file mode 100644 index 0000000000..1f58115d96 --- /dev/null +++ b/tests/tcg/s390x/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-s390x-cross +DOCKER_CROSS_COMPILER=s390x-linux-gnu-gcc From 1e2107092dda50f111a7886d968877a97b71465a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 10 Apr 2018 17:45:56 +0100 Subject: [PATCH 1458/2380] tests/tcg: enable building for ppc64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently this just enables building the multiarch tests. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/ppc64le/Makefile.include | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/tcg/ppc64le/Makefile.include diff --git a/tests/tcg/ppc64le/Makefile.include b/tests/tcg/ppc64le/Makefile.include new file mode 100644 index 0000000000..d71cfc9aa7 --- /dev/null +++ b/tests/tcg/ppc64le/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-ppc64el-cross +DOCKER_CROSS_COMPILER=powerpc64le-linux-gnu-gcc From 409c1c9cbd9a6715d5f5663a8b3404ea193d0137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 23 Apr 2018 13:54:09 +0100 Subject: [PATCH 1459/2380] tests/tcg: enable building for Alpha MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can't use our normal Debian based compilers as Alpha isn't an officially supported architecture. However it is available as a port and fortunately cross compilers for all these targets are included in Debian Sid, the perpetual rolling/unstable/testing version of Debian. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 7 ++++ .../dockerfiles/debian-alpha-cross.docker | 12 +++++++ tests/docker/dockerfiles/debian-sid.docker | 32 +++++++++++++++++++ tests/tcg/alpha/Makefile.include | 2 ++ tests/tcg/alpha/Makefile.target | 6 ++++ 5 files changed, 59 insertions(+) create mode 100644 tests/docker/dockerfiles/debian-alpha-cross.docker create mode 100644 tests/docker/dockerfiles/debian-sid.docker create mode 100644 tests/tcg/alpha/Makefile.include create mode 100644 tests/tcg/alpha/Makefile.target diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 4a2b028a3b..8c24f8807e 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -64,6 +64,13 @@ docker-image-debian-ppc64el-cross: docker-image-debian9 docker-image-debian-s390x-cross: docker-image-debian9 docker-image-debian-win32-cross: docker-image-debian8-mxe docker-image-debian-win64-cross: docker-image-debian8-mxe + +# Debian SID images - we are tracking a rolling distro so we want to +# force a re-build of the base image if we ever need to build one of +# its children. +docker-image-debian-sid: NOCACHE=1 + +docker-image-debian-alpha-cross: docker-image-debian-sid docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools diff --git a/tests/docker/dockerfiles/debian-alpha-cross.docker b/tests/docker/dockerfiles/debian-alpha-cross.docker new file mode 100644 index 0000000000..29a25d0dfd --- /dev/null +++ b/tests/docker/dockerfiles/debian-alpha-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-alpha-linux-gnu \ + libc6.1-dev-alpha-cross || { echo "Failed to build - see debian-sid.docker notes"; exit 1; } diff --git a/tests/docker/dockerfiles/debian-sid.docker b/tests/docker/dockerfiles/debian-sid.docker new file mode 100644 index 0000000000..9a3d168705 --- /dev/null +++ b/tests/docker/dockerfiles/debian-sid.docker @@ -0,0 +1,32 @@ +# +# Debian Sid Base +# +# A number of our guests exist as ports only. We can either use the +# ports repo or get everything from Sid. However Sid is a rolling +# distro which may be broken at any particular time. If you are +# unlucky and try and build your images while gcc is in the process of +# being uploaded this can fail. Your only recourse is to try again in +# a few hours when the repos have re-synced. Once built however you +# won't be affected by repo changes unless the docker recipies are +# updated and trigger a re-build. +# + +FROM debian:sid-slim + +# Duplicate deb line as deb-src +RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list + +# Install common build utilities +RUN apt update +RUN DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + bison \ + build-essential \ + ca-certificates \ + flex \ + git \ + pkg-config \ + psmisc \ + python \ + texinfo || { echo "Failed to build - see debian-sid.docker notes"; exit 1; } diff --git a/tests/tcg/alpha/Makefile.include b/tests/tcg/alpha/Makefile.include new file mode 100644 index 0000000000..c7dc48eadb --- /dev/null +++ b/tests/tcg/alpha/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-alpha-cross +DOCKER_CROSS_COMPILER=alpha-linux-gnu-gcc diff --git a/tests/tcg/alpha/Makefile.target b/tests/tcg/alpha/Makefile.target new file mode 100644 index 0000000000..bedf1b6444 --- /dev/null +++ b/tests/tcg/alpha/Makefile.target @@ -0,0 +1,6 @@ +# -*- Mode: makefile -*- +# +# Alpha specific tweaks + +# On Alpha Linux only supports 8k pages +EXTRA_RUNS+=run-test-mmap-8192 From 4f2cbcfb4dc398b10ef7ede2069b1e05704981b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 23 Apr 2018 15:48:06 +0100 Subject: [PATCH 1460/2380] tests/tcg/alpha: add Alpha specific tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These tests did use their own crt.o stub however that is a little stone age so we drop crt.S and just statically link to the cross compilers libraries. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/alpha/Makefile | 35 --------------------------------- tests/tcg/alpha/Makefile.target | 12 +++++++++++ tests/tcg/alpha/crt.s | 26 ------------------------ tests/tcg/alpha/hello-alpha.c | 2 ++ tests/tcg/alpha/test-cond.c | 1 + tests/tcg/alpha/test-ovf.c | 2 ++ 6 files changed, 17 insertions(+), 61 deletions(-) delete mode 100644 tests/tcg/alpha/Makefile delete mode 100644 tests/tcg/alpha/crt.s diff --git a/tests/tcg/alpha/Makefile b/tests/tcg/alpha/Makefile deleted file mode 100644 index 2b1f03d048..0000000000 --- a/tests/tcg/alpha/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -CROSS=alpha-linux-gnu- -CC=$(CROSS)gcc -AS=$(CROSS)as - -SIM=../../alpha-linux-user/qemu-alpha - -CFLAGS=-O -LINK=$(CC) -o $@ crt.o $< -nostdlib - -TESTS=test-cond test-cmov - -all: hello-alpha $(TESTS) - -hello-alpha: hello-alpha.o crt.o - $(LINK) - -test-cond: test-cond.o crt.o - $(LINK) - -test-cmov.o: test-cond.c - $(CC) -c $(CFLAGS) -DTEST_CMOV -o $@ $< - -test-cmov: test-cmov.o crt.o - $(LINK) - -test-ovf: test-ovf.o crt.o - $(LINK) - -check: $(TESTS) - for f in $(TESTS); do $(SIM) $$f || exit 1; done - -clean: - $(RM) *.o *~ hello-alpha $(TESTS) - -.PHONY: clean all check diff --git a/tests/tcg/alpha/Makefile.target b/tests/tcg/alpha/Makefile.target index bedf1b6444..a585080328 100644 --- a/tests/tcg/alpha/Makefile.target +++ b/tests/tcg/alpha/Makefile.target @@ -2,5 +2,17 @@ # # Alpha specific tweaks +ALPHA_SRC=$(SRC_PATH)/tests/tcg/alpha +VPATH+=$(ALPHA_SRC) + +ALPHA_TESTS=hello-alpha test-cond test-cmov test-ovf +TESTS+=$(ALPHA_TESTS) + +test-cmov: EXTRA_CFLAGS=-DTEST_CMOV +test-cmov: test-cond.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +run-test-cmov: test-cmov + # On Alpha Linux only supports 8k pages EXTRA_RUNS+=run-test-mmap-8192 diff --git a/tests/tcg/alpha/crt.s b/tests/tcg/alpha/crt.s deleted file mode 100644 index 31af8825bc..0000000000 --- a/tests/tcg/alpha/crt.s +++ /dev/null @@ -1,26 +0,0 @@ - .text - - .globl _start - .ent _start,0 -_start: - .frame $15,0,$15 - br $29,1f -1: ldgp $29, 0($29) - .prologue 0 - ldq $27,main($29) !literal!1 - jsr $26,($27) - or $0,$0,$16 - .end _start - - .globl _exit -_exit: - lda $0,1 - callsys - - call_pal 0 - - .globl write -write: - lda $0,4 - callsys - ret diff --git a/tests/tcg/alpha/hello-alpha.c b/tests/tcg/alpha/hello-alpha.c index 79892e6522..84e43b2fc4 100644 --- a/tests/tcg/alpha/hello-alpha.c +++ b/tests/tcg/alpha/hello-alpha.c @@ -1,3 +1,5 @@ +#include + int main (void) { write (1, "hello\n", 6); diff --git a/tests/tcg/alpha/test-cond.c b/tests/tcg/alpha/test-cond.c index 74adffaa69..e625313b3e 100644 --- a/tests/tcg/alpha/test-cond.c +++ b/tests/tcg/alpha/test-cond.c @@ -1,3 +1,4 @@ +#include #ifdef TEST_CMOV diff --git a/tests/tcg/alpha/test-ovf.c b/tests/tcg/alpha/test-ovf.c index 01c80e7525..17892f1e89 100644 --- a/tests/tcg/alpha/test-ovf.c +++ b/tests/tcg/alpha/test-ovf.c @@ -1,3 +1,5 @@ +#include + static long test_subqv (long a, long b) { long res; From 3ae8a1000b4a8fb8d4081925c55929ea1c3c2938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 23 Apr 2018 16:00:13 +0100 Subject: [PATCH 1461/2380] tests/tcg: enable building for HPPA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 1 + tests/docker/dockerfiles/debian-hppa-cross.docker | 12 ++++++++++++ tests/tcg/hppa/Makefile.include | 2 ++ tests/tcg/hppa/Makefile.target | 6 ++++++ 4 files changed, 21 insertions(+) create mode 100644 tests/docker/dockerfiles/debian-hppa-cross.docker create mode 100644 tests/tcg/hppa/Makefile.include create mode 100644 tests/tcg/hppa/Makefile.target diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 8c24f8807e..65e3dee741 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -71,6 +71,7 @@ docker-image-debian-win64-cross: docker-image-debian8-mxe docker-image-debian-sid: NOCACHE=1 docker-image-debian-alpha-cross: docker-image-debian-sid +docker-image-debian-hppa-cross: docker-image-debian-sid docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools diff --git a/tests/docker/dockerfiles/debian-hppa-cross.docker b/tests/docker/dockerfiles/debian-hppa-cross.docker new file mode 100644 index 0000000000..ad443defac --- /dev/null +++ b/tests/docker/dockerfiles/debian-hppa-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-hppa-linux-gnu \ + libc6-dev-hppa-cross diff --git a/tests/tcg/hppa/Makefile.include b/tests/tcg/hppa/Makefile.include new file mode 100644 index 0000000000..da2353430e --- /dev/null +++ b/tests/tcg/hppa/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-hppa-cross +DOCKER_CROSS_COMPILER=hppa-linux-gnu-gcc diff --git a/tests/tcg/hppa/Makefile.target b/tests/tcg/hppa/Makefile.target new file mode 100644 index 0000000000..8bf01966bd --- /dev/null +++ b/tests/tcg/hppa/Makefile.target @@ -0,0 +1,6 @@ +# -*- Mode: makefile -*- +# +# HPPA specific tweaks - specifically masking out broken tests + +# On parisc Linux supports 4K/16K/64K (but currently only 4k works) +EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-16384 run-test-mmap-65536 From 21d71c8c414a6c322a2babd5bcf58d3e20ce308c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 23 Apr 2018 16:08:36 +0100 Subject: [PATCH 1462/2380] tests/tcg: enable building for m68k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As before, using Debian SID compilers. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 1 + tests/docker/dockerfiles/debian-m68k-cross.docker | 12 ++++++++++++ tests/tcg/m68k/Makefile.include | 2 ++ tests/tcg/m68k/Makefile.target | 7 +++++++ 4 files changed, 22 insertions(+) create mode 100644 tests/docker/dockerfiles/debian-m68k-cross.docker create mode 100644 tests/tcg/m68k/Makefile.include create mode 100644 tests/tcg/m68k/Makefile.target diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 65e3dee741..59dd8e314d 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -72,6 +72,7 @@ docker-image-debian-sid: NOCACHE=1 docker-image-debian-alpha-cross: docker-image-debian-sid docker-image-debian-hppa-cross: docker-image-debian-sid +docker-image-debian-m68k-cross: docker-image-debian-sid docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools diff --git a/tests/docker/dockerfiles/debian-m68k-cross.docker b/tests/docker/dockerfiles/debian-m68k-cross.docker new file mode 100644 index 0000000000..21ba3b0132 --- /dev/null +++ b/tests/docker/dockerfiles/debian-m68k-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-m68k-linux-gnu \ + libc6-dev-m68k-cross diff --git a/tests/tcg/m68k/Makefile.include b/tests/tcg/m68k/Makefile.include new file mode 100644 index 0000000000..cd7c6bf50d --- /dev/null +++ b/tests/tcg/m68k/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-m68k-cross +DOCKER_CROSS_COMPILER=m68k-linux-gnu-gcc diff --git a/tests/tcg/m68k/Makefile.target b/tests/tcg/m68k/Makefile.target new file mode 100644 index 0000000000..62f109eef4 --- /dev/null +++ b/tests/tcg/m68k/Makefile.target @@ -0,0 +1,7 @@ +# -*- Mode: makefile -*- +# +# m68k specific tweaks - specifically masking out broken tests +# + +# On m68k Linux supports 4k and 8k pages (but 8k is currently broken) +EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192 From a5ec3e36dc5a1ce7c4a368516e5dba369f3468ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 23 Apr 2018 16:25:06 +0100 Subject: [PATCH 1463/2380] tests/tcg: enable building for sh4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As before, using Debian SID compilers. While the compiler can be coerced into generating big-endian code it seems the linker can't deal with it so we only enable the building for little endian SH4. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 1 + tests/docker/dockerfiles/debian-sh4-cross.docker | 12 ++++++++++++ tests/tcg/sh4/Makefile.include | 4 ++++ tests/tcg/sh4/Makefile.target | 7 +++++++ 4 files changed, 24 insertions(+) create mode 100644 tests/docker/dockerfiles/debian-sh4-cross.docker create mode 100644 tests/tcg/sh4/Makefile.include create mode 100644 tests/tcg/sh4/Makefile.target diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 59dd8e314d..a2dd307a8e 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -73,6 +73,7 @@ docker-image-debian-sid: NOCACHE=1 docker-image-debian-alpha-cross: docker-image-debian-sid docker-image-debian-hppa-cross: docker-image-debian-sid docker-image-debian-m68k-cross: docker-image-debian-sid +docker-image-debian-sh4-cross: docker-image-debian-sid docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools diff --git a/tests/docker/dockerfiles/debian-sh4-cross.docker b/tests/docker/dockerfiles/debian-sh4-cross.docker new file mode 100644 index 0000000000..88a2423094 --- /dev/null +++ b/tests/docker/dockerfiles/debian-sh4-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-sh4-linux-gnu \ + libc6-dev-sh4-cross diff --git a/tests/tcg/sh4/Makefile.include b/tests/tcg/sh4/Makefile.include new file mode 100644 index 0000000000..ad21594d9d --- /dev/null +++ b/tests/tcg/sh4/Makefile.include @@ -0,0 +1,4 @@ +ifneq ($(TARGET_NAME), sh4eb) +DOCKER_IMAGE=debian-sh4-cross +DOCKER_CROSS_COMPILER=sh4-linux-gnu-gcc +endif diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target new file mode 100644 index 0000000000..9d18d44612 --- /dev/null +++ b/tests/tcg/sh4/Makefile.target @@ -0,0 +1,7 @@ +# -*- Mode: makefile -*- +# +# SuperH specific tweaks +# + +# On sh Linux supports 4k, 8k, 16k and 64k pages (but only 4k currently works) +EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192 run-test-mmap-16384 run-test-mmap-65536 From cc6c7365b88d5d99b95e0b4f07518c244f9d8059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 23 Apr 2018 16:49:09 +0100 Subject: [PATCH 1464/2380] tests/tcg: enable building for sparc64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As before, using Debian SID compilers. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 1 + tests/docker/dockerfiles/debian-sparc64-cross.docker | 12 ++++++++++++ tests/tcg/sparc64/Makefile.include | 2 ++ tests/tcg/sparc64/Makefile.target | 6 ++++++ 4 files changed, 21 insertions(+) create mode 100644 tests/docker/dockerfiles/debian-sparc64-cross.docker create mode 100644 tests/tcg/sparc64/Makefile.include create mode 100644 tests/tcg/sparc64/Makefile.target diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index a2dd307a8e..fb465e729e 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -74,6 +74,7 @@ docker-image-debian-alpha-cross: docker-image-debian-sid docker-image-debian-hppa-cross: docker-image-debian-sid docker-image-debian-m68k-cross: docker-image-debian-sid docker-image-debian-sh4-cross: docker-image-debian-sid +docker-image-debian-sparc64-cross: docker-image-debian-sid docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools diff --git a/tests/docker/dockerfiles/debian-sparc64-cross.docker b/tests/docker/dockerfiles/debian-sparc64-cross.docker new file mode 100644 index 0000000000..1e2c809274 --- /dev/null +++ b/tests/docker/dockerfiles/debian-sparc64-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-sparc64-linux-gnu \ + libc6-dev-sparc64-cross diff --git a/tests/tcg/sparc64/Makefile.include b/tests/tcg/sparc64/Makefile.include new file mode 100644 index 0000000000..95fc8dee9f --- /dev/null +++ b/tests/tcg/sparc64/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-sparc64-cross +DOCKER_CROSS_COMPILER=sparc64-linux-gnu-gcc diff --git a/tests/tcg/sparc64/Makefile.target b/tests/tcg/sparc64/Makefile.target new file mode 100644 index 0000000000..408dace783 --- /dev/null +++ b/tests/tcg/sparc64/Makefile.target @@ -0,0 +1,6 @@ +# -*- Mode: makefile -*- +# +# sparc specific tweaks + +# On Sparc64 Linux support 8k pages +EXTRA_RUNS+=run-test-mmap-8192 From 33b2c4b50f1c104accb3c650f2096c78f0f6f3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 23 Apr 2018 16:57:48 +0100 Subject: [PATCH 1465/2380] tests/tcg: enable building for mips64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As before, using Debian SID compilers. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 1 + tests/docker/dockerfiles/debian-mips64-cross.docker | 12 ++++++++++++ tests/tcg/mips/Makefile.include | 3 +++ 3 files changed, 16 insertions(+) create mode 100644 tests/docker/dockerfiles/debian-mips64-cross.docker diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index fb465e729e..8fd4041716 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -75,6 +75,7 @@ docker-image-debian-hppa-cross: docker-image-debian-sid docker-image-debian-m68k-cross: docker-image-debian-sid docker-image-debian-sh4-cross: docker-image-debian-sid docker-image-debian-sparc64-cross: docker-image-debian-sid +docker-image-debian-mips64-cross: docker-image-debian-sid docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools diff --git a/tests/docker/dockerfiles/debian-mips64-cross.docker b/tests/docker/dockerfiles/debian-mips64-cross.docker new file mode 100644 index 0000000000..ed1ce0e919 --- /dev/null +++ b/tests/docker/dockerfiles/debian-mips64-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-mips64-linux-gnuabi64 \ + libc6-dev-mips64-cross diff --git a/tests/tcg/mips/Makefile.include b/tests/tcg/mips/Makefile.include index a9beceb623..4a14fc078d 100644 --- a/tests/tcg/mips/Makefile.include +++ b/tests/tcg/mips/Makefile.include @@ -8,6 +8,9 @@ ifeq ($(TARGET_NAME),mips64el) DOCKER_IMAGE=debian-mips64el-cross DOCKER_CROSS_COMPILER=mips64el-linux-gnuabi64-gcc +else ifeq ($(TARGET_NAME),mips64) +DOCKER_IMAGE=debian-mips64-cross +DOCKER_CROSS_COMPILER=mips64-linux-gnuabi64-gcc else ifeq ($(TARGET_NAME),mipsel) DOCKER_IMAGE=debian-mipsel-cross DOCKER_CROSS_COMPILER=mipsel-linux-gnu-gcc From e100a967551d0c337214a0f7a8ee840350e0ec23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 24 Apr 2018 16:41:18 +0100 Subject: [PATCH 1466/2380] tests/tcg: enable building for RISCV64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As before, using Debian SID compilers. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 1 + tests/docker/dockerfiles/debian-riscv64-cross.docker | 12 ++++++++++++ tests/tcg/riscv/Makefile.include | 10 ++++++++++ 3 files changed, 23 insertions(+) create mode 100644 tests/docker/dockerfiles/debian-riscv64-cross.docker create mode 100644 tests/tcg/riscv/Makefile.include diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 8fd4041716..a680743ae8 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -76,6 +76,7 @@ docker-image-debian-m68k-cross: docker-image-debian-sid docker-image-debian-sh4-cross: docker-image-debian-sid docker-image-debian-sparc64-cross: docker-image-debian-sid docker-image-debian-mips64-cross: docker-image-debian-sid +docker-image-debian-riscv64-cross: docker-image-debian-sid docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker new file mode 100644 index 0000000000..2b2e64cee6 --- /dev/null +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-riscv64-linux-gnu \ + libc6-dev-riscv64-cross diff --git a/tests/tcg/riscv/Makefile.include b/tests/tcg/riscv/Makefile.include new file mode 100644 index 0000000000..d92ac6c89f --- /dev/null +++ b/tests/tcg/riscv/Makefile.include @@ -0,0 +1,10 @@ +# +# Makefile.include for all RISCV targets +# +# Debian only really cares about 64 bit going forward +# + +ifeq ($(TARGET_NAME),riscv64) +DOCKER_IMAGE=debian-riscv64-cross +DOCKER_CROSS_COMPILER=riscv64-linux-gnu-gcc +endif From 08f56d8c9c901ec78a442541ddde63d57d2266c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 24 May 2018 22:12:15 +0100 Subject: [PATCH 1467/2380] docker: move debian-powerpc-cross to sid based build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original Jessie based cross builder hasn't worked for a while. The state of the libraries is still perilous for cross-building QEMU but we can use it for building TCG tests. The debian-apt-fake.sh script can also be dropped as it is no longer used. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 4 +- tests/docker/dockerfiles/debian-apt-fake.sh | 46 ------------------- .../dockerfiles/debian-powerpc-cross.docker | 39 +++------------- tests/docker/dockerfiles/debian8.docker | 3 -- 4 files changed, 7 insertions(+), 85 deletions(-) delete mode 100755 tests/docker/dockerfiles/debian-apt-fake.sh diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index a680743ae8..87f449264f 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -47,8 +47,6 @@ docker-image-%: $(DOCKER_FILES_DIR)/%.docker $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)),\ "BUILD","$*") -docker-image-debian-powerpc-cross: EXTRA_FILES:=$(SRC_PATH)/tests/docker/dockerfiles/debian-apt-fake.sh - # Enforce dependencies for composite images docker-image-debian: docker-image-debian9 docker-image-debian8-mxe: docker-image-debian8 @@ -59,7 +57,6 @@ docker-image-debian-arm64-cross: docker-image-debian9 docker-image-debian-mips-cross: docker-image-debian9 docker-image-debian-mipsel-cross: docker-image-debian9 docker-image-debian-mips64el-cross: docker-image-debian9 -docker-image-debian-powerpc-cross: docker-image-debian8 docker-image-debian-ppc64el-cross: docker-image-debian9 docker-image-debian-s390x-cross: docker-image-debian9 docker-image-debian-win32-cross: docker-image-debian8-mxe @@ -77,6 +74,7 @@ docker-image-debian-sh4-cross: docker-image-debian-sid docker-image-debian-sparc64-cross: docker-image-debian-sid docker-image-debian-mips64-cross: docker-image-debian-sid docker-image-debian-riscv64-cross: docker-image-debian-sid +docker-image-debian-powerpc-cross: docker-image-debian-sid docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools diff --git a/tests/docker/dockerfiles/debian-apt-fake.sh b/tests/docker/dockerfiles/debian-apt-fake.sh deleted file mode 100755 index 2ec0fdf47a..0000000000 --- a/tests/docker/dockerfiles/debian-apt-fake.sh +++ /dev/null @@ -1,46 +0,0 @@ -#! /bin/sh -# -# Generate fake debian package to resolve unimportant unmet dependencies held -# by upstream multiarch broken packages. -# -# Copyright (c) 2017 Philippe Mathieu-Daudé -# -# 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. - -test $1 = "install" && shift 1 - -fake_install() -{ - echo "Generating fake $2 $1 $3 ..." - (cd /var/cache/apt/archives - (cat << 'EOF' -Section: misc -Priority: optional -Standards-Version: 3.9.2 - -Package: NAME -Version: VERSION -Maintainer: qemu-devel@nongnu.org -Architecture: any -Multi-Arch: same -Description: fake NAME -EOF - ) | sed s/NAME/$2/g | sed s/VERSION/$3/g > $2.control - equivs-build -a $1 $2.control 1>/dev/null 2>/dev/null - dpkg -i --force-overwrite $2_$3_$1.deb - ) -} - -try_install() -{ - name=$(echo $1|sed "s/\(.*\):\(.*\)=\(.*\)/\1/") - arch=$(echo $1|sed "s/\(.*\):\(.*\)=\(.*\)/\2/") - vers=$(echo $1|sed "s/\(.*\):\(.*\)=\(.*\)/\3/") - apt-get install -q -yy $1 || fake_install $arch $name $vers -} - -for package in $*; do - try_install $package -done diff --git a/tests/docker/dockerfiles/debian-powerpc-cross.docker b/tests/docker/dockerfiles/debian-powerpc-cross.docker index a5dd46b4ac..5e62ca0df1 100644 --- a/tests/docker/dockerfiles/debian-powerpc-cross.docker +++ b/tests/docker/dockerfiles/debian-powerpc-cross.docker @@ -1,40 +1,13 @@ # # Docker powerpc cross-compiler target # -# This docker target builds on the debian Jessie base image. +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. The original +# Jessie based no longer builds. # -FROM qemu:debian8 -MAINTAINER Philippe Mathieu-Daudé - -# Add the foreign architecture we want and install dependencies -RUN dpkg --add-architecture powerpc -RUN apt-get update -RUN DEBIAN_FRONTEND=noninteractive eatmydata \ - apt-get install -y --no-install-recommends \ - crossbuild-essential-powerpc - -# to fix "following packages have unmet dependencies" ... -ADD debian-apt-fake.sh /usr/local/bin/apt-fake -RUN apt-get install -y --no-install-recommends \ - equivs \ - pkg-config -RUN apt-fake install \ - pkg-config:powerpc=0.28-1.1-fake && \ - ln -s pkg-config /usr/bin/powerpc-linux-gnu-pkg-config -ENV PKG_CONFIG_PATH /usr/lib/powerpc-linux-gnu/pkgconfig -# - -# Specify the cross prefix for this image (see tests/docker/common.rc) -ENV QEMU_CONFIGURE_OPTS --cross-prefix=powerpc-linux-gnu- +FROM qemu:debian-sid RUN DEBIAN_FRONTEND=noninteractive eatmydata \ - apt-get build-dep -yy -a powerpc qemu -RUN DEBIAN_FRONTEND=noninteractive \ apt-get install -y --no-install-recommends \ - glusterfs-common:powerpc \ - libbz2-dev:powerpc \ - liblzo2-dev:powerpc \ - libncursesw5-dev:powerpc \ - libnfs-dev:powerpc \ - librdmacm-dev:powerpc \ - libsnappy-dev:powerpc + gcc-powerpc-linux-gnu \ + libc6-dev-powerpc-cross || { echo "Failed to build - see debian-sid.docker notes"; exit 1; } diff --git a/tests/docker/dockerfiles/debian8.docker b/tests/docker/dockerfiles/debian8.docker index 1bcf2e3d2f..52945631cd 100644 --- a/tests/docker/dockerfiles/debian8.docker +++ b/tests/docker/dockerfiles/debian8.docker @@ -32,6 +32,3 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ pkg-config \ python-minimal -# Setup Emdebian [emdebian-archive-keyring] -RUN echo "deb http://emdebian.org/tools/debian/ jessie main" > /etc/apt/sources.list.d/emdebian.list && \ - curl http://emdebian.org/tools/debian/emdebian-toolchain-archive.key | apt-key add - From 97103c256e7e79f2cd0e6336040b08386bad1c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 24 May 2018 22:28:45 +0100 Subject: [PATCH 1468/2380] tests/tcg: enable building for PowerPC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have restored debian-image-powerpc-cross using Debian SID compilers we can build for 32 bit powerpc. Although PPC32 supports a range of pages sizes currently only 4k works so the others are commented out for now. We can also merge the ppc64 support under the base architecture directory to avoid too much proliferation of directories. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/ppc/Makefile.include | 7 +++++++ tests/tcg/ppc/Makefile.target | 12 ++++++++++++ tests/tcg/ppc64le/Makefile.include | 2 -- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/tcg/ppc/Makefile.include create mode 100644 tests/tcg/ppc/Makefile.target delete mode 100644 tests/tcg/ppc64le/Makefile.include diff --git a/tests/tcg/ppc/Makefile.include b/tests/tcg/ppc/Makefile.include new file mode 100644 index 0000000000..b062c30dd3 --- /dev/null +++ b/tests/tcg/ppc/Makefile.include @@ -0,0 +1,7 @@ +ifeq ($(TARGET_NAME),ppc) +DOCKER_IMAGE=debian-powerpc-cross +DOCKER_CROSS_COMPILER=powerpc-linux-gnu-gcc +else ifeq ($(TARGET_NAME),ppc64le) +DOCKER_IMAGE=debian-ppc64el-cross +DOCKER_CROSS_COMPILER=powerpc64le-linux-gnu-gcc +endif diff --git a/tests/tcg/ppc/Makefile.target b/tests/tcg/ppc/Makefile.target new file mode 100644 index 0000000000..f5e08c7376 --- /dev/null +++ b/tests/tcg/ppc/Makefile.target @@ -0,0 +1,12 @@ +# -*- Mode: makefile -*- +# +# PPC - included from tests/tcg/Makefile +# + +ifneq (,$(findstring 64,$(TARGET_NAME))) +# On PPC64 Linux can be configured with 4k (default) or 64k pages (currently broken) +EXTRA_RUNS+=run-test-mmap-4096 #run-test-mmap-65536 +else +# On PPC32 Linux supports 4K/16K/64K/256K (but currently only 4k works) +EXTRA_RUNS+=run-test-mmap-4096 #run-test-mmap-16384 run-test-mmap-65536 run-test-mmap-262144 +endif diff --git a/tests/tcg/ppc64le/Makefile.include b/tests/tcg/ppc64le/Makefile.include deleted file mode 100644 index d71cfc9aa7..0000000000 --- a/tests/tcg/ppc64le/Makefile.include +++ /dev/null @@ -1,2 +0,0 @@ -DOCKER_IMAGE=debian-ppc64el-cross -DOCKER_CROSS_COMPILER=powerpc64le-linux-gnu-gcc From 7e97017e7db3c0ce552cf1a58df8bb97bde29c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 6 Apr 2018 13:32:18 +0100 Subject: [PATCH 1469/2380] tests/tcg/Makefile: update to be called from Makefile.target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This make is now invoked from each individual target make with the appropriate CC and EXTRA_CFLAGS set for each guest. It then includes additional Makefile.targets from: - tests/tcg/multiarch (always) - tests/tcg/$(TARGET_BASE_ARCH) (if available) - tests/tcg/$(TARGET_NAME) The order is important as the later Makefile's may want to suppress TESTS from its base arch profile. Each included Makefile.target is responsible for adding TESTS as well as defining any special build instructions for individual tests. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- MAINTAINERS | 6 ++ tests/tcg/Makefile | 185 +++++++++++++++++++-------------------------- 2 files changed, 83 insertions(+), 108 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index de1ae8423e..4c955425b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2156,6 +2156,12 @@ W: https://travis-ci.org/qemu/qemu W: https://app.shippable.com/github/qemu/qemu W: http://patchew.org/QEMU/ +Guest Test Compilation Support +M: Alex Bennée +R: Philippe Mathieu-Daudé +F: tests/tcg/Makefile +L: qemu-devel@nongnu.org + Documentation ------------- Build system architecture diff --git a/tests/tcg/Makefile b/tests/tcg/Makefile index e12395117a..e7dbcdb5bf 100644 --- a/tests/tcg/Makefile +++ b/tests/tcg/Makefile @@ -1,125 +1,94 @@ +# -*- Mode: makefile -*- +# +# TCG tests +# +# These are complicated by the fact we want to build them for guest +# systems. This requires knowing what guests we are building and which +# ones we have cross-compilers for or docker images with +# cross-compilers. +# +# The tests themselves should be as minimal as possible as +# cross-compilers don't always have a large amount of libraries +# available. +# +# We only include the host build system for SRC_PATH and we don't +# bother with the common rules.mk. We expect the following: +# +# CC - the C compiler command +# EXTRA_CFLAGS - any extra CFLAGS +# BUILD_STATIC - are we building static binaries +# +# By default all tests are statically compiled but some host systems +# may not package static libraries by default. If an external +# cross-compiler can only build dynamic libraries the user might need +# to make extra efforts to ensure ld.so can link at runtime when the +# tests are run. +# +# We also accept SPEED=slow to enable slower running tests +# +# We also expect to be in the tests build dir for the FOO-linux-user. +# + -include ../../config-host.mak --include $(SRC_PATH)/rules.mak +-include ../config-target.mak -$(call set-vpath, $(SRC_PATH)/tests/tcg) +quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) -QEMU=../../i386-linux-user/qemu-i386 -QEMU_X86_64=../../x86_64-linux-user/qemu-x86_64 -CC_X86_64=$(CC_I386) -m64 +# Tests we are building +TESTS= -QEMU_INCLUDES += -I../.. -CFLAGS=-Wall -O2 -g -fno-strict-aliasing -#CFLAGS+=-msse2 +# Start with a blank slate, the build targets get to add stuff first +CFLAGS= +QEMU_CFLAGS= LDFLAGS= -# TODO: automatically detect ARM and MIPS compilers, and run those too +# The QEMU for this TARGET +QEMU=../qemu-$(TARGET_NAME) -# runcom maps page 0, so it requires root privileges -# also, pi_10.com runs indefinitely - -I386_TESTS=hello-i386 \ - sha1-i386 \ - test-i386 \ - test-i386-fprem \ - # runcom - -# native i386 compilers sometimes are not biarch. assume cross-compilers are -ifneq ($(ARCH),i386) -I386_TESTS+=run-test-x86_64 +# If TCG debugging is enabled things are a lot slower +ifeq ($(CONFIG_DEBUG_TCG),y) +TIMEOUT=45 +else +TIMEOUT=15 endif -TESTS = test_path -ifneq ($(call find-in-path, $(CC_I386)),) -TESTS += $(I386_TESTS) +# The order we include is important. We include multiarch, base arch +# and finally arch if it's not the same as base arch. +-include $(SRC_PATH)/tests/tcg/multiarch/Makefile.target +-include $(SRC_PATH)/tests/tcg/$(TARGET_BASE_ARCH)/Makefile.target +ifneq ($(TARGET_BASE_ARCH),$(TARGET_NAME)) +-include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.target endif -all: $(patsubst %,run-%,$(TESTS)) -test: all +# Add the common build options +CFLAGS+=-Wall -O0 -g -fno-strict-aliasing +ifeq ($(BUILD_STATIC),y) +LDFLAGS+=-static +endif -# rules to run tests +%: %.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -.PHONY: $(patsubst %,run-%,$(TESTS)) +all: $(TESTS) + +# +# Test Runners +# +# By default we just run the test with the appropriate QEMU for the +# target. More advanced tests may want to override the runner in their +# specific make rules. Additional runners for the same binary should +# be added to EXTRA_RUNS. +# + +RUN_TESTS=$(patsubst %,run-%, $(TESTS)) +RUN_TESTS+=$(EXTRA_RUNS) run-%: % - -$(QEMU) ./$* + $(call quiet-command, \ + timeout $(TIMEOUT) $(QEMU) $< > $<.out, \ + "TEST", "$< on $(TARGET_NAME)") -run-hello-i386: hello-i386 -run-sha1-i386: sha1-i386 +.PHONY: run +run: $(RUN_TESTS) -run-test-i386: test-i386 - ./test-i386 > test-i386.ref - -$(QEMU) test-i386 > test-i386.out - @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi - -run-test-i386-fprem: test-i386-fprem - ./test-i386-fprem > test-i386-fprem.ref - -$(QEMU) test-i386-fprem > test-i386-fprem.out - @if diff -u test-i386-fprem.ref test-i386-fprem.out ; then echo "Auto Test OK"; fi - -run-test-x86_64: test-x86_64 - ./test-x86_64 > test-x86_64.ref - -$(QEMU_X86_64) test-x86_64 > test-x86_64.out - @if diff -u test-x86_64.ref test-x86_64.out ; then echo "Auto Test OK"; fi - - -run-runcom: runcom - -$(QEMU) ./runcom $(SRC_PATH)/tests/pi_10.com - -run-test_path: test_path - ./test_path - -# rules to compile tests - -hello-i386: hello-i386.c - $(CC_I386) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $< - strip $@ - -# i386/x86_64 emulation test (test various opcodes) */ -test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \ - test-i386.h test-i386-shift.h test-i386-muldiv.h - $(CC_I386) $(QEMU_INCLUDES) $(CFLAGS) $(LDFLAGS) -o $@ \ - $( Date: Fri, 6 Apr 2018 11:08:37 +0100 Subject: [PATCH 1470/2380] Makefile.target: add (clean-/build-)guest-tests targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now all the build infrastructure is in place we can build tests for each guest that we support. That support mainly depends on having cross compilers installed or docker setup. To keep all the logic for that together we put the rules in tests/tcg/Makefile.include and include it from the main Makefile.target. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + Makefile.target | 5 +++ tests/tcg/Makefile.include | 88 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 tests/tcg/Makefile.include diff --git a/MAINTAINERS b/MAINTAINERS index 4c955425b9..6c747ff69e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2160,6 +2160,7 @@ Guest Test Compilation Support M: Alex Bennée R: Philippe Mathieu-Daudé F: tests/tcg/Makefile +F: tests/tcg/Makefile.include L: qemu-devel@nongnu.org Documentation diff --git a/Makefile.target b/Makefile.target index dad2cf8778..a9d8928f96 100644 --- a/Makefile.target +++ b/Makefile.target @@ -36,6 +36,11 @@ endif PROGS=$(QEMU_PROG) $(QEMU_PROGW) STPFILES= +# Makefile Tests +ifdef CONFIG_USER_ONLY +include $(SRC_PATH)/tests/tcg/Makefile.include +endif + config-target.h: config-target.h-timestamp config-target.h-timestamp: config-target.mak diff --git a/tests/tcg/Makefile.include b/tests/tcg/Makefile.include new file mode 100644 index 0000000000..57470b2a2c --- /dev/null +++ b/tests/tcg/Makefile.include @@ -0,0 +1,88 @@ +# -*- Mode: makefile -*- +# +# TCG tests (per-target rules) +# +# This Makefile fragement is included from the per-target +# Makefile.target so will be invoked for each linux-user program we +# build. We have two options for compiling, either using a configured +# guest compiler or calling one of our docker images to do it for us. +# + +# The per ARCH makefile, if it exists, holds extra information about +# useful docker images or alternative compiler flags. + +-include $(SRC_PATH)/tests/tcg/$(TARGET_BASE_ARCH)/Makefile.include +-include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.include + +GUEST_BUILD= +TCG_MAKE=$(SRC_PATH)/tests/tcg/Makefile +# Support installed Cross Compilers + +ifdef CROSS_CC_GUEST + +.PHONY: cross-build-guest-tests +cross-build-guest-tests: + $(call quiet-command, \ + (mkdir -p tests && cd tests && \ + $(MAKE) -f $(TCG_MAKE) CC=$(CROSS_CC_GUEST) \ + BUILD_STATIC=$(CROSS_CC_GUEST_STATIC) \ + EXTRA_CFLAGS=$(CROSS_CC_GUEST_CFLAGS)), \ + "BUILD","$(TARGET_NAME) guest-tests with $(CROSS_CC_GUEST)") + +GUEST_BUILD=cross-build-guest-tests + +endif + +# Support building with Docker + +ifeq ($(HAVE_USER_DOCKER)$(GUEST_BUILD),y) +ifneq ($(DOCKER_IMAGE),) + +# We also need the Docker make rules to depend on +include $(SRC_PATH)/tests/docker/Makefile.include + +DOCKER_COMPILE_CMD="$(DOCKER_SCRIPT) cc --user $(shell id -u) \ + --cc $(DOCKER_CROSS_COMPILER) \ + -i qemu:$(DOCKER_IMAGE) \ + -s $(SRC_PATH) -- " +DOCKER_PREREQ=docker-image-$(DOCKER_IMAGE) + +.PHONY: docker-build-guest-tests +docker-build-guest-tests: $(DOCKER_PREREQ) + $(call quiet-command, \ + (mkdir -p tests && cd tests && \ + $(MAKE) -f $(TCG_MAKE) CC=$(DOCKER_COMPILE_CMD) \ + BUILD_STATIC=y \ + EXTRA_CFLAGS=$(DOCKER_CROSS_COMPILER_CFLAGS)), \ + "BUILD","$(TARGET_NAME) guest-tests with docker qemu:$(DOCKER_IMAGE)") + +GUEST_BUILD=docker-build-guest-tests + +endif +endif + +# Final targets +.PHONY: guest-tests + +ifneq ($(GUEST_BUILD),) +guest-tests: $(GUEST_BUILD) + +run-guest-tests: guest-tests qemu-$(TARGET_NAME) + $(call quiet-command, \ + (cd tests && $(MAKE) -f $(TCG_MAKE) SPEED=$(SPEED) run), \ + "RUN", "tests for $(TARGET_NAME)") + +else +guest-tests: + $(call quiet-command, /bin/true, "BUILD", \ + "$(TARGET_NAME) guest-tests SKIPPED") + +run-guest-tests: + $(call quiet-command, /bin/true, "RUN", \ + "tests for $(TARGET_NAME) SKIPPED") +endif + +# It doesn't matter if these don't exits +.PHONY: clean-guest-tests +clean-guest-tests: + rm -rf tests || echo "no $(TARGET_NAME) tests to remove" From 8ba0a8206a3f7b9870890c99d3222eceffd018a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 6 Apr 2018 22:08:36 +0100 Subject: [PATCH 1471/2380] tests/Makefile.include: add [build|clean|check]-tcg targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will ensure all linux-user targets build their guest test programs and ensure check-tcg will run the respective tests. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/Makefile.include | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index ca91da26cb..5c401874e8 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -10,6 +10,7 @@ check-help: @echo " $(MAKE) check-speed Run qobject speed tests" @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" @echo " $(MAKE) check-block Run block tests" + @echo " $(MAKE) check-tcg Run TCG tests" @echo " $(MAKE) check-report.html Generates an HTML test report" @echo " $(MAKE) check-clean Clean the tests" @echo @@ -930,6 +931,30 @@ check-report.xml: $(patsubst %,check-report-qtest-%.xml, $(QTEST_TARGETS)) check check-report.html: check-report.xml $(call quiet-command,gtester-report $< > $@,"GEN","$@") +# Per guest TCG tests + +LINUX_USER_TARGETS=$(filter %-linux-user,$(TARGET_LIST)) +BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(LINUX_USER_TARGETS)) +CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(LINUX_USER_TARGETS)) +RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(LINUX_USER_TARGETS)) + +build-tcg-tests-%: + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" guest-tests,) + +run-tcg-tests-%: % build-tcg-tests-% + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" run-guest-tests,) + +clean-tcg-tests-%: + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" clean-guest-tests,) + +.PHONY: build-tcg +build-tcg: $(BUILD_TCG_TARGET_RULES) + +.PHONY: check-tcg +check-tcg: $(RUN_TCG_TARGET_RULES) + +.PHONY: clean-tcg +clean-tcg: $(CLEAN_TCG_TARGET_RULES) # Other tests @@ -972,7 +997,6 @@ check-speed: $(patsubst %,check-%, $(check-speed-y)) check-block: $(patsubst %,check-%, $(check-block-y)) check: check-qapi-schema check-unit check-qtest check-decodetree check-clean: - $(MAKE) -C tests/tcg clean rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y) rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y)) $(check-qtest-generic-y)) rm -f tests/test-qapi-gen-timestamp From 607bf9b5a5245563a80cb9484f2fd85d58c46fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 21 May 2018 10:38:37 +0100 Subject: [PATCH 1472/2380] tests/tcg: add run, diff, and skip helper macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we aren't using the default runners for all the test cases it is easy to miss out things like timeouts. To help with this we add some helpers and use them so we only need to make core changes in one place. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/tcg/Makefile | 13 ++++++++++--- tests/tcg/aarch64/Makefile.target | 6 ++---- tests/tcg/arm/Makefile.target | 6 ++---- tests/tcg/i386/Makefile.target | 10 ++++------ tests/tcg/multiarch/Makefile.target | 4 ++-- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/tests/tcg/Makefile b/tests/tcg/Makefile index e7dbcdb5bf..bf06415390 100644 --- a/tests/tcg/Makefile +++ b/tests/tcg/Makefile @@ -34,6 +34,15 @@ quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) +# $1 = test name, $2 = cmd, $3 = desc +run-test = $(call quiet-command, timeout $(TIMEOUT) $2 > $1.out,"TEST",$3) + +# $1 = test name, $2 = reference +diff-out = $(call quiet-command, diff -u $1.out $2 | head -n 10,"DIFF","$1.out with $2") + +# $1 = test name, $2 = reason +skip-test = @printf " SKIPPED %s on $(TARGET_NAME) because %s\n" $1 $2 + # Tests we are building TESTS= @@ -84,9 +93,7 @@ RUN_TESTS=$(patsubst %,run-%, $(TESTS)) RUN_TESTS+=$(EXTRA_RUNS) run-%: % - $(call quiet-command, \ - timeout $(TIMEOUT) $(QEMU) $< > $<.out, \ - "TEST", "$< on $(TARGET_NAME)") + $(call run-test, $<, $(QEMU) $<, "$< on $(TARGET_NAME)") .PHONY: run run: $(RUN_TESTS) diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 7dba32138d..08c45b8470 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -13,7 +13,5 @@ TESTS:=$(AARCH64_TESTS) fcvt: LDFLAGS+=-lm run-fcvt: fcvt - $(call quiet-command, \ - $(QEMU) $< > fcvt.out && \ - diff -u $(AARCH64_SRC)/fcvt.ref fcvt.out, \ - "TEST", "$< (default) on $(TARGET_NAME)") + $(call run-test,$<,$(QEMU) $<, "$< on $(TARGET_NAME)") + $(call diff-out,$<,$(AARCH64_SRC)/fcvt.ref) diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 14b5435fc6..aa4e4e3782 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -24,10 +24,8 @@ fcvt: LDFLAGS+=-lm # fcvt: CFLAGS+=-march=armv8.2-a+fp16 -mfpu=neon-fp-armv8 run-fcvt: fcvt - $(call quiet-command, \ - $(QEMU) $< > fcvt.out && \ - diff -u $(ARM_SRC)/fcvt.ref fcvt.out, \ - "TEST", "$< (default) on $(TARGET_NAME)") + $(call run-test,fcvt,$(QEMU) $<,"$< on $(TARGET_NAME)") + $(call diff-out,fcvt,$(ARM_SRC)/fcvt.ref) endif # On ARM Linux only supports 4k pages diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index cd173363ee..97b7c23cf1 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -31,7 +31,7 @@ test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S test-i386.h test-i386 # Specialist test runners run-runcom: runcom pi_10.com - $(call quiet-command, $(QEMU) ./runcom $(I386_SRC)/pi_10.com > runcom.out, "TEST", "$< on $(TARGET_NAME)") + $(call run-test,$<,$(QEMU) ./runcom $(I386_SRC)/pi_10.com,"$< on $(TARGET_NAME)") ifeq ($(SPEED), slow) @@ -40,13 +40,11 @@ test-i386-fprem.ref: test-i386-fprem run-test-i386-fprem: TIMEOUT=60 run-test-i386-fprem: test-i386-fprem - $(call quiet-command, \ - $(QEMU) $< > $<.out && \ - diff -u $(I386_SRC)/$<.ref $<.out, \ - "TEST", "$< (default) on $(TARGET_NAME)") + $(call run-test,test-i386-fprem, $(QEMU) $<,"$< on $(TARGET_NAME)") + $(call diff-out,test-i386-fprem, $(I386_SRC)/$<.ref) else run-test-i386-fprem: test-i386-fprem - $(call quiet-command, /bin/true, "SLOW TEST", "$< SKIPPED on $(TARGET_NAME)") + $(call skip-test, $<, "SLOW") endif # On i386 and x86_64 Linux only supports 4k pages (large pages are a different hack) diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 90e45a881e..b77084c146 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -27,10 +27,10 @@ testthread: LDFLAGS+=-lpthread # default case (host page size) run-test-mmap: test-mmap - $(call quiet-command, $(QEMU) $< > test-mmap.out, "TEST", \ + $(call run-test, test-mmap, $(QEMU) $<, \ "$< (default) on $(TARGET_NAME)") # additional page sizes (defined by each architecture adding to EXTRA_RUNS) run-test-mmap-%: test-mmap - $(call quiet-command, $(QEMU) -p $* $< > test-mmap-$*.out, "TEST", \ + $(call run-test, test-mmap-$*, $(QEMU) -p $* $<,\ "$< ($* byte pages) on $(TARGET_NAME)") From 83749e649e169c7cb2c0088fda51d26c33b80af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 24 Apr 2018 15:21:50 +0100 Subject: [PATCH 1473/2380] tests/tcg: override runners for broken tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To get a clean run of check-tcg these tests are currently skipped: - hello-mips for mips - linux-test for sparc Signed-off-by: Alex Bennée Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé --- tests/tcg/mips/Makefile.target | 3 +++ tests/tcg/sparc64/Makefile.target | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/tcg/mips/Makefile.target b/tests/tcg/mips/Makefile.target index 71f0c2dd53..086625f533 100644 --- a/tests/tcg/mips/Makefile.target +++ b/tests/tcg/mips/Makefile.target @@ -17,3 +17,6 @@ hello-mips: LDFLAGS+=-nostdlib # For MIPS32 and 64 we have a bunch of extra tests in sub-directories # however they are intended for system tests. + +run-hello-mips: hello-mips + $(call skip-test, $<, "BROKEN") diff --git a/tests/tcg/sparc64/Makefile.target b/tests/tcg/sparc64/Makefile.target index 408dace783..5bd7f90583 100644 --- a/tests/tcg/sparc64/Makefile.target +++ b/tests/tcg/sparc64/Makefile.target @@ -1,6 +1,11 @@ # -*- Mode: makefile -*- # -# sparc specific tweaks +# sparc specific tweaks and masking out broken tests + +# different from the other hangs: +# tests/tcg/multiarch/linux-test.c:264: Value too large for defined data type (ret=-1, errno=92/Value too large for defined data type) +run-linux-test: linux-test + $(call skip-test, $<, "BROKEN") # On Sparc64 Linux support 8k pages EXTRA_RUNS+=run-test-mmap-8192 From ab93256144487a5f151635028080f1be5c9ccc6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 18 Jun 2018 10:34:20 +0100 Subject: [PATCH 1474/2380] tests/tcg/i386: extend timeout for runcom test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Travis hardware can be a little slow and the runcom test is fairly heavy in calculating pi. Lets double the timeout so we don't trip up during CI by mistake. Signed-off-by: Alex Bennée --- tests/tcg/i386/Makefile.target | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index 97b7c23cf1..c1997a1624 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -30,6 +30,7 @@ test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S test-i386.h test-i386 $( Date: Fri, 8 Jun 2018 12:12:46 +0100 Subject: [PATCH 1475/2380] tests: add top-level make dependency for docker builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One problem with satisfying your docker dependencies in a sub-make it you might end up trying to satisfy the dependency multiple times. This is especially a problem with debian-sid based cross compilers and CI setups. We solve this by doing a docker build pass at the top level before any sub-makes are called. We still need to satisfy dependencies in the Makefile.target call so people can run tests from individual target directories. We introduce a new Makefile.probe which gets called for each PROBE_TARGET and allows us to build up the list. It does require multiply including config-target.mak which shouldn't cause any issues as it shouldn't define anything that clashes with config-host.mak. However we undefine a few key variables each time around. Signed-off-by: Alex Bennée --- tests/Makefile.include | 8 ++++++++ tests/tcg/Makefile.probe | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/tcg/Makefile.probe diff --git a/tests/Makefile.include b/tests/Makefile.include index 5c401874e8..da647324a2 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -938,6 +938,14 @@ BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(LINUX_USER_TARGETS)) CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(LINUX_USER_TARGETS)) RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(LINUX_USER_TARGETS)) +ifeq ($(HAVE_USER_DOCKER),y) +# Probe for the Docker Builds needed for each build +$(foreach PROBE_TARGET,$(TARGET_LIST), \ + $(eval -include $(SRC_PATH)/tests/tcg/Makefile.probe) \ + $(if $(DOCKER_PREREQ), \ + $(eval build-tcg-tests-$(PROBE_TARGET): $(DOCKER_PREREQ)))) +endif + build-tcg-tests-%: $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" guest-tests,) diff --git a/tests/tcg/Makefile.probe b/tests/tcg/Makefile.probe new file mode 100644 index 0000000000..15c0412657 --- /dev/null +++ b/tests/tcg/Makefile.probe @@ -0,0 +1,31 @@ +# -*- Mode: makefile -*- +# +# TCG Compiler Probe +# +# This Makefile fragement is included multiple times in the main make +# script to probe for available compilers. This is used to build up a +# selection of required docker targets before we invoke a sub-make for +# each target. + +# First we need the target makefile which tells us the target architecture +-include $(BUILD_DIR)/$(PROBE_TARGET)/config-target.mak + +# Then we load up the target architecture makefiles which tell us +# about the compilers +CROSS_CC_GUEST:= +DOCKER_IMAGE:= +DOCKER_PREREQ:= + +-include $(SRC_PATH)/tests/tcg/$(TARGET_BASE_ARCH)/Makefile.include +-include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.include + +ifndef CROSS_CC_GUEST +ifneq ($(DOCKER_IMAGE),) +DOCKER_PREREQ:=docker-image-$(DOCKER_IMAGE) +endif +endif + +# Clean-up +# undefine TARGET_NAME +# undefine TARGET_BASE_ARCH +# undefine TARGET_ABI_DIR From 83405c4517679722257b1560f75fb7488b671374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 18 Jun 2018 11:14:40 +0100 Subject: [PATCH 1476/2380] docker: docker.py use "version" to probe usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "images" command is a fairly heavyweight command to run as it involves searching the whole docker file-system inventory. On a machine with a lot of images this makes start-up fairly expensive. Signed-off-by: Alex Bennée --- tests/docker/docker.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index b28ad87034..e6437d64a7 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -52,7 +52,9 @@ def _guess_docker_command(): commands = [["docker"], ["sudo", "-n", "docker"]] for cmd in commands: try: - if subprocess.call(cmd + ["images"], + # docker version will return the client details in stdout + # but still report a status of 1 if it can't contact the daemon + if subprocess.call(cmd + ["version"], stdout=DEVNULL, stderr=DEVNULL) == 0: return cmd except OSError: From f9172822e7a07db346e21d461e2a61ffcee7c77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 8 Jun 2018 15:20:25 +0100 Subject: [PATCH 1477/2380] docker: docker.py don't conflate checksums for extra_files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This just gets confusing especially as the helper function doesn't even take into account any extra files (or the executable). Currently the actual check just ignores them and also passes the result through _dockerfile_preprocess so we fix that too. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/docker.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index e6437d64a7..6a9f2f224b 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -206,8 +206,10 @@ class Docker(object): tmp_df.write("\n") tmp_df.write("LABEL com.qemu.dockerfile-checksum=%s" % - _text_checksum("\n".join([dockerfile] + - extra_files_cksum))) + _text_checksum(_dockerfile_preprocess(dockerfile))) + for f, c in extra_files_cksum: + tmp_df.write("LABEL com.qemu.%s-checksum=%s" % (f, c)) + tmp_df.flush() self._do_check(["build", "-t", tag, "-f", tmp_df.name] + argv + \ @@ -322,7 +324,7 @@ class BuildCommand(SubCommand): _copy_binary_with_libs(args.include_executable, docker_dir) for filename in args.extra_files or []: _copy_with_mkdir(filename, docker_dir) - cksum += [_file_checksum(filename)] + cksum += [(filename, _file_checksum(filename))] argv += ["--build-arg=" + k.lower() + "=" + v for k, v in os.environ.iteritems() From f97da1f71700a3417953fa85bf1394307e5dd013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 8 Jun 2018 16:20:48 +0100 Subject: [PATCH 1478/2380] docker: docker.py add check sub-command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This command allows you to check if we need to re-build a docker image. If the image isn't in the repository or the checksums don't match then we return false and some text (for processing in makefiles). Signed-off-by: Alex Bennée --- tests/docker/docker.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 6a9f2f224b..e4f37820e0 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -184,8 +184,14 @@ class Docker(object): stderr=subprocess.STDOUT, **kwargs) + def inspect_tag(self, tag): + try: + return self._output(["inspect", tag]) + except subprocess.CalledProcessError: + return None + def get_image_dockerfile_checksum(self, tag): - resp = self._output(["inspect", tag]) + resp = self.inspect_tag(tag) labels = json.loads(resp)[0]["Config"].get("Labels", {}) return labels.get("com.qemu.dockerfile-checksum", "") @@ -447,6 +453,36 @@ class CcCommand(SubCommand): return Docker().command("run", cmd, args.quiet) +class CheckCommand(SubCommand): + """Check if we need to re-build a docker image out of a dockerfile. + Arguments: """ + name = "check" + + def args(self, parser): + parser.add_argument("tag", + help="Image Tag") + parser.add_argument("dockerfile", + help="Dockerfile name") + + def run(self, args, argv): + dockerfile = open(args.dockerfile, "rb").read() + tag = args.tag + + dkr = Docker() + info = dkr.inspect_tag(tag) + if info is None: + print("Image does not exist") + return 1 + + if dkr.image_matches_dockerfile(tag, dockerfile): + if not args.quiet: + print("Image is up to date") + return 0 + else: + print("Image needs updating") + return 1 + + def main(): parser = argparse.ArgumentParser(description="A Docker helper", usage="%s ..." % os.path.basename(sys.argv[0])) From cff5477330429cfb154bf00ec8224bdceb8f8bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 15 Jun 2018 19:20:55 +0100 Subject: [PATCH 1479/2380] tests/Makefile: call sub-makes with SKIP_DOCKER_BUILD=1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we now ensure all the images we are going to use are built in the top level make file lets not over complicate things by running the full script again. We do run the check script just in case someone deletes the docker image while we are running. Signed-off-by: Alex Bennée --- tests/Makefile.include | 8 ++++++-- tests/docker/Makefile.include | 13 ++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index da647324a2..7c48cfe14e 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -947,10 +947,14 @@ $(foreach PROBE_TARGET,$(TARGET_LIST), \ endif build-tcg-tests-%: - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" guest-tests,) + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" \ + SKIP_DOCKER_BUILD=1 TARGET_DIR="$*/" guest-tests, \ + "BUILD", "TCG tests for $*") run-tcg-tests-%: % build-tcg-tests-% - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" run-guest-tests,) + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" \ + SKIP_DOCKER_BUILD=1 TARGET_DIR="$*/" run-guest-tests, \ + "RUN", "TCG tests for $*") clean-tcg-tests-%: $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" clean-guest-tests,) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 87f449264f..c0f09505bb 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -34,7 +34,15 @@ docker-qemu-src: $(DOCKER_SRC_COPY) docker-image: ${DOCKER_TARGETS} -# General rule for building docker images +# General rule for building docker images. If we are a sub-make +# invoked with SKIP_DOCKER_BUILD we still check the image is upto date +# though +ifdef SKIP_DOCKER_BUILD +docker-image-%: $(DOCKER_FILES_DIR)/%.docker + $(call quiet-command, \ + $(DOCKER_SCRIPT) check --quiet qemu:$* $<, \ + "CHECK", "$*") +else docker-image-%: $(DOCKER_FILES_DIR)/%.docker @if test "$@" = docker-image-debian-bootstrap -a -z "$(EXECUTABLE)"; then \ echo WARNING: EXECUTABLE is not set, debootstrap may fail. 2>&1 ; \ @@ -46,6 +54,7 @@ docker-image-%: $(DOCKER_FILES_DIR)/%.docker $(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES))\ $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)),\ "BUILD","$*") +endif # Enforce dependencies for composite images docker-image-debian: docker-image-debian9 @@ -65,7 +74,9 @@ docker-image-debian-win64-cross: docker-image-debian8-mxe # Debian SID images - we are tracking a rolling distro so we want to # force a re-build of the base image if we ever need to build one of # its children. +ifndef SKIP_DOCKER_BUILD docker-image-debian-sid: NOCACHE=1 +endif docker-image-debian-alpha-cross: docker-image-debian-sid docker-image-debian-hppa-cross: docker-image-debian-sid From 7b882245f48dca6e1cf9087b24e41275793be88f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 12 Jun 2018 21:28:45 +0100 Subject: [PATCH 1480/2380] docker: docker.py adding age check command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is useful for querying if an image is too old. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/docker.py | 46 +++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index e4f37820e0..b279836154 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -32,6 +32,7 @@ except ImportError: from io import StringIO from shutil import copy, rmtree from pwd import getpwuid +from datetime import datetime,timedelta FILTERED_ENV_NAMES = ['ftp_proxy', 'http_proxy', 'https_proxy'] @@ -190,6 +191,9 @@ class Docker(object): except subprocess.CalledProcessError: return None + def get_image_creation_time(self, info): + return json.loads(info)[0]["Created"] + def get_image_dockerfile_checksum(self, tag): resp = self.inspect_tag(tag) labels = json.loads(resp)[0]["Config"].get("Labels", {}) @@ -461,11 +465,14 @@ class CheckCommand(SubCommand): def args(self, parser): parser.add_argument("tag", help="Image Tag") - parser.add_argument("dockerfile", - help="Dockerfile name") + parser.add_argument("dockerfile", default=None, + help="Dockerfile name", nargs='?') + parser.add_argument("--checktype", choices=["checksum", "age"], + default="checksum", help="check type") + parser.add_argument("--olderthan", default=60, type=int, + help="number of minutes") def run(self, args, argv): - dockerfile = open(args.dockerfile, "rb").read() tag = args.tag dkr = Docker() @@ -474,13 +481,32 @@ class CheckCommand(SubCommand): print("Image does not exist") return 1 - if dkr.image_matches_dockerfile(tag, dockerfile): - if not args.quiet: - print("Image is up to date") - return 0 - else: - print("Image needs updating") - return 1 + if args.checktype == "checksum": + if not args.dockerfile: + print("Need a dockerfile for tag:%s" % (tag)) + return 1 + + dockerfile = open(args.dockerfile, "rb").read() + + if dkr.image_matches_dockerfile(tag, dockerfile): + if not args.quiet: + print("Image is up to date") + return 0 + else: + print("Image needs updating") + return 1 + elif args.checktype == "age": + timestr = dkr.get_image_creation_time(info).split(".")[0] + created = datetime.strptime(timestr, "%Y-%m-%dT%H:%M:%S") + past = datetime.now() - timedelta(minutes=args.olderthan) + if created < past: + print ("Image created @ %s more than %d minutes old" % + (timestr, args.olderthan)) + return 1 + else: + if not args.quiet: + print ("Image less than %d minutes old" % (args.olderthan)) + return 0 def main(): From 5499fa9e449b9620b72e670af4bedbc8590c9b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 12 Jun 2018 21:29:40 +0100 Subject: [PATCH 1481/2380] tests/docker/Makefile.include: only force SID to NOCACHE if old MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we can check the age of a docker image we can be a little more intelligent about re-building Sid images and only force NOCACHE if it is "old". Signed-off-by: Alex Bennée --- tests/docker/Makefile.include | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index c0f09505bb..91d9665517 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -75,8 +75,14 @@ docker-image-debian-win64-cross: docker-image-debian8-mxe # force a re-build of the base image if we ever need to build one of # its children. ifndef SKIP_DOCKER_BUILD +ifeq ($(HAVE_USER_DOCKER),y) +SID_AGE=$(shell $(DOCKER_SCRIPT) check --checktype=age --olderthan=180 --quiet qemu:debian-sid) +ifeq ($(SID_AGE),) +else docker-image-debian-sid: NOCACHE=1 endif +endif +endif docker-image-debian-alpha-cross: docker-image-debian-sid docker-image-debian-hppa-cross: docker-image-debian-sid From 65b26da485540ffcc1c46c9a0898f01912ee6b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 18 Apr 2018 16:55:37 +0100 Subject: [PATCH 1482/2380] .travis.yml: add check-tcg test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index fabfe9ec34..bd66c18fed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -153,3 +153,11 @@ matrix: - TEST_CMD="" before_script: - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; } + - env: + - CONFIG="--disable-system --disable-docs" + - TEST_CMD="make check-tcg" + script: + - make ${MAKEFLAGS} && ${TEST_CMD} ${MAKEFLAGS} + sudo: required + dist: trusty + compiler: gcc From 317c52cc6aa0d7b47fa156eb84857a339d0b4406 Mon Sep 17 00:00:00 2001 From: Collin Walling Date: Mon, 7 May 2018 10:30:54 -0400 Subject: [PATCH 1483/2380] monitor: report entirety of hmp command on error When a user incorrectly provides an hmp command, an error response will be printed that prompts the user to try "help ". However, when the command contains multiple parts e.g. "info uuid xyz", only the last whitespace delimited string will be reported (in this example "info" will be dropped and the message will read "Try "help uuid" for more information", which is incorrect). Let's correct this by capturing the entirety of the command from the command line -- excluding any extraneous characters. Reported-by: Mikhail Fokin Signed-off-by: Collin Walling Message-Id: Reviewed-by: Eric Blake Signed-off-by: Dr. David Alan Gilbert --- monitor.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/monitor.c b/monitor.c index 885e000f9b..2a8187f5d7 100644 --- a/monitor.c +++ b/monitor.c @@ -3431,6 +3431,7 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline) { QDict *qdict; const mon_cmd_t *cmd; + const char *cmd_start = cmdline; trace_handle_hmp_command(mon, cmdline); @@ -3447,8 +3448,11 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline) qdict = monitor_parse_arguments(mon, &cmdline, cmd); if (!qdict) { - monitor_printf(mon, "Try \"help %s\" for more information\n", - cmd->name); + while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) { + cmdline--; + } + monitor_printf(mon, "Try \"help %.*s\" for more information\n", + (int)(cmdline - cmd_start), cmd_start); return; } From d35aefa9ae150a8a5943ca3d9102020a5382de0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 15 Jun 2018 17:25:33 +0200 Subject: [PATCH 1484/2380] ppc/pnv: introduce a new intc_create() operation to the chip model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Power9, the thread interrupt presenter has a different type and is linked to the chip owning the cores. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/pnv.c | 21 +++++++++++++++++++-- hw/ppc/pnv_core.c | 18 +++++++++--------- include/hw/ppc/pnv.h | 1 + 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 0d2b79f798..c7e127ae97 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -671,6 +671,13 @@ static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id) return (chip->chip_id << 7) | (core_id << 3); } +static Object *pnv_chip_power8_intc_create(PnvChip *chip, Object *child, + Error **errp) +{ + return icp_create(child, TYPE_PNV_ICP, XICS_FABRIC(qdev_get_machine()), + errp); +} + /* * 0:48 Reserved - Read as zeroes * 49:52 Node ID @@ -686,6 +693,12 @@ static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id) return (chip->chip_id << 8) | (core_id << 2); } +static Object *pnv_chip_power9_intc_create(PnvChip *chip, Object *child, + Error **errp) +{ + return NULL; +} + /* Allowed core identifiers on a POWER8 Processor Chip : * * @@ -721,6 +734,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */ k->cores_mask = POWER8E_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; + k->intc_create = pnv_chip_power8_intc_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8E"; } @@ -734,6 +748,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */ k->cores_mask = POWER8_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; + k->intc_create = pnv_chip_power8_intc_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8"; } @@ -747,6 +762,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */ k->cores_mask = POWER8_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; + k->intc_create = pnv_chip_power8_intc_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8NVL"; } @@ -760,6 +776,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */ k->cores_mask = POWER9_CORE_MASK; k->core_pir = pnv_chip_core_pir_p9; + k->intc_create = pnv_chip_power9_intc_create; k->xscom_base = 0x00603fc00000000ull; dc->desc = "PowerNV Chip POWER9"; } @@ -892,8 +909,8 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) object_property_set_int(OBJECT(pnv_core), pcc->core_pir(chip, core_hwid), "pir", &error_fatal); - object_property_add_const_link(OBJECT(pnv_core), "xics", - qdev_get_machine(), &error_fatal); + object_property_add_const_link(OBJECT(pnv_core), "chip", + OBJECT(chip), &error_fatal); object_property_set_bool(OBJECT(pnv_core), true, "realized", &error_fatal); object_unref(OBJECT(pnv_core)); diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index f7cf33f547..a9f129fc2c 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -99,13 +99,14 @@ static const MemoryRegionOps pnv_core_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_realize_vcpu(PowerPCCPU *cpu, XICSFabric *xi, Error **errp) +static void pnv_realize_vcpu(PowerPCCPU *cpu, PnvChip *chip, Error **errp) { CPUPPCState *env = &cpu->env; int core_pir; int thread_index = 0; /* TODO: TCG supports only one thread */ ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; Error *local_err = NULL; + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); if (local_err) { @@ -113,7 +114,7 @@ static void pnv_realize_vcpu(PowerPCCPU *cpu, XICSFabric *xi, Error **errp) return; } - cpu->intc = icp_create(OBJECT(cpu), TYPE_PNV_ICP, xi, &local_err); + cpu->intc = pcc->intc_create(chip, OBJECT(cpu), &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -143,13 +144,12 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) void *obj; int i, j; char name[32]; - Object *xi; + Object *chip; - xi = object_property_get_link(OBJECT(dev), "xics", &local_err); - if (!xi) { - error_setg(errp, "%s: required link 'xics' not found: %s", - __func__, error_get_pretty(local_err)); - return; + chip = object_property_get_link(OBJECT(dev), "chip", &local_err); + if (!chip) { + error_propagate(errp, local_err); + error_prepend(errp, "required link 'chip' not found: "); } pc->threads = g_new(PowerPCCPU *, cc->nr_threads); @@ -166,7 +166,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) } for (j = 0; j < cc->nr_threads; j++) { - pnv_realize_vcpu(pc->threads[j], XICS_FABRIC(xi), &local_err); + pnv_realize_vcpu(pc->threads[j], PNV_CHIP(chip), &local_err); if (local_err) { goto err; } diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 90759240a7..e934e84f55 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -76,6 +76,7 @@ typedef struct PnvChipClass { hwaddr xscom_base; uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); + Object *(*intc_create)(PnvChip *chip, Object *child, Error **errp); } PnvChipClass; #define PNV_CHIP_TYPE_SUFFIX "-" TYPE_PNV_CHIP From 04026890f2d71afb36dbef24c370171cd0c42913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 15 Jun 2018 17:25:34 +0200 Subject: [PATCH 1485/2380] ppc/pnv: introduce a new isa_create() operation to the chip model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This moves the details of the ISA bus creation under the LPC model but more important, the new PnvChip operation will let us choose the chip class to use when we introduce the different chip classes for Power9 and Power8. It hides away the processor chip controllers from the machine. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/pnv.c | 34 +++++++++++++++++++--------------- hw/ppc/pnv_lpc.c | 30 +++++++++++++++++++++++++----- include/hw/ppc/pnv.h | 1 + include/hw/ppc/pnv_lpc.h | 3 +-- 4 files changed, 46 insertions(+), 22 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index c7e127ae97..ac828d1331 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -529,24 +529,24 @@ static void pnv_reset(void) cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); } -static ISABus *pnv_isa_create(PnvChip *chip) +static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp) { - PnvLpcController *lpc = &chip->lpc; - ISABus *isa_bus; - qemu_irq *irqs; - PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + return pnv_lpc_isa_create(&chip->lpc, true, errp); +} - /* let isa_bus_new() create its own bridge on SysBus otherwise - * devices speficied on the command line won't find the bus and - * will fail to create. - */ - isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, - &error_fatal); +static ISABus *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp) +{ + return pnv_lpc_isa_create(&chip->lpc, false, errp); +} - irqs = pnv_lpc_isa_irq_create(lpc, pcc->chip_type, ISA_NUM_IRQS); +static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp) +{ + return NULL; +} - isa_bus_irqs(isa_bus, irqs); - return isa_bus; +static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) +{ + return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); } static void pnv_init(MachineState *machine) @@ -646,7 +646,7 @@ static void pnv_init(MachineState *machine) g_free(chip_typename); /* Instantiate ISA bus on chip 0 */ - pnv->isa_bus = pnv_isa_create(pnv->chips[0]); + pnv->isa_bus = pnv_isa_create(pnv->chips[0], &error_fatal); /* Create serial port */ serial_hds_isa_init(pnv->isa_bus, 0, MAX_ISA_SERIAL_PORTS); @@ -735,6 +735,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) k->cores_mask = POWER8E_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; + k->isa_create = pnv_chip_power8_isa_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8E"; } @@ -749,6 +750,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) k->cores_mask = POWER8_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; + k->isa_create = pnv_chip_power8_isa_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8"; } @@ -763,6 +765,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) k->cores_mask = POWER8_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; + k->isa_create = pnv_chip_power8nvl_isa_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8NVL"; } @@ -777,6 +780,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) k->cores_mask = POWER9_CORE_MASK; k->core_pir = pnv_chip_core_pir_p9; k->intc_create = pnv_chip_power9_intc_create; + k->isa_create = pnv_chip_power9_isa_create; k->xscom_base = 0x00603fc00000000ull; dc->desc = "PowerNV Chip POWER9"; } diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 402c4fefa8..d7721320a2 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -22,6 +22,7 @@ #include "target/ppc/cpu.h" #include "qapi/error.h" #include "qemu/log.h" +#include "hw/isa/isa.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_lpc.h" @@ -535,16 +536,35 @@ static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) } } -qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type, - int nirqs) +ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) { + Error *local_err = NULL; + ISABus *isa_bus; + qemu_irq *irqs; + qemu_irq_handler handler; + + /* let isa_bus_new() create its own bridge on SysBus otherwise + * devices speficied on the command line won't find the bus and + * will fail to create. + */ + isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return NULL; + } + /* Not all variants have a working serial irq decoder. If not, * handling of LPC interrupts becomes a platform issue (some * platforms have a CPLD to do it). */ - if (chip_type == PNV_CHIP_POWER8NVL) { - return qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, nirqs); + if (use_cpld) { + handler = pnv_lpc_isa_irq_handler_cpld; } else { - return qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, lpc, nirqs); + handler = pnv_lpc_isa_irq_handler; } + + irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS); + + isa_bus_irqs(isa_bus, irqs); + return isa_bus; } diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index e934e84f55..563279f3e0 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -77,6 +77,7 @@ typedef struct PnvChipClass { uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); Object *(*intc_create)(PnvChip *chip, Object *child, Error **errp); + ISABus *(*isa_create)(PnvChip *chip, Error **errp); } PnvChipClass; #define PNV_CHIP_TYPE_SUFFIX "-" TYPE_PNV_CHIP diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index 53fdd5bb64..d657489b07 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -70,7 +70,6 @@ typedef struct PnvLpcController { PnvPsi *psi; } PnvLpcController; -qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type, - int nirqs); +ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp); #endif /* _PPC_PNV_LPC_H */ From b94020268e0b6659499e250d25346baaa9888fed Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Mon, 18 Jun 2018 14:26:35 +0200 Subject: [PATCH 1486/2380] spapr_cpu_core: migrate per-CPU data A per-CPU machine data pointer was recently added to PowerPCCPU. The motivation is to to hide platform specific details from the core CPU code. This per-CPU data can hold state which is relevant to the guest though, eg, Virtual Processor Areas, and we should migrate this state. This patch adds the plumbing so that we can migrate the per-CPU data for PAPR guests. We only do this for newer machine types for the sake of backward compatibility. No state is migrated for the moment: the vmstate_spapr_cpu_state structure will be populated by subsequent patches. Signed-off-by: Greg Kurz [dwg: Fix some trivial spelling and spacing errors] Signed-off-by: David Gibson --- hw/ppc/spapr.c | 7 ++++++- hw/ppc/spapr_cpu_core.c | 22 ++++++++++++++++++++-- include/hw/ppc/spapr_cpu_core.h | 1 + 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index db0fb385d4..3174468fc5 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4115,7 +4115,12 @@ DEFINE_SPAPR_MACHINE(3_0, "3.0", true); HW_COMPAT_2_12 \ { \ .driver = TYPE_POWERPC_CPU, \ - .property = "pre-3.0-migration", \ + .property = "pre-3.0-migration", \ + .value = "on", \ + }, \ + { \ + .driver = TYPE_SPAPR_CPU_CORE, \ + .property = "pre-3.0-migration", \ .value = "on", \ }, diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index aef3be33a3..f129ac884e 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -129,6 +129,15 @@ static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp) g_free(sc->threads); } +static const VMStateDescription vmstate_spapr_cpu_state = { + .name = "spapr_cpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + }, +}; + static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr, Error **errp) { @@ -194,6 +203,10 @@ static PowerPCCPU *spapr_create_vcpu(sPAPRCPUCore *sc, int i, Error **errp) } cpu->machine_data = g_new0(sPAPRCPUState, 1); + if (!sc->pre_3_0_migration) { + vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state, + cpu->machine_data); + } object_unref(obj); return cpu; @@ -204,10 +217,13 @@ err: return NULL; } -static void spapr_delete_vcpu(PowerPCCPU *cpu) +static void spapr_delete_vcpu(PowerPCCPU *cpu, sPAPRCPUCore *sc) { sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + if (!sc->pre_3_0_migration) { + vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data); + } cpu->machine_data = NULL; g_free(spapr_cpu); object_unparent(OBJECT(cpu)); @@ -253,7 +269,7 @@ err_unrealize: } err: while (--i >= 0) { - spapr_delete_vcpu(sc->threads[i]); + spapr_delete_vcpu(sc->threads[i], sc); } g_free(sc->threads); error_propagate(errp, local_err); @@ -261,6 +277,8 @@ err: static Property spapr_cpu_core_properties[] = { DEFINE_PROP_INT32("node-id", sPAPRCPUCore, node_id, CPU_UNSET_NUMA_NODE_ID), + DEFINE_PROP_BOOL("pre-3.0-migration", sPAPRCPUCore, pre_3_0_migration, + false), DEFINE_PROP_END_OF_LIST() }; diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h index 8ceea2973a..9e2821e4b3 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -31,6 +31,7 @@ typedef struct sPAPRCPUCore { /*< public >*/ PowerPCCPU **threads; int node_id; + bool pre_3_0_migration; /* older machine don't know about sPAPRCPUState */ } sPAPRCPUCore; typedef struct sPAPRCPUCoreClass { From 7f9fe3f02d9e796e8bcd37628e17127dec515991 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Mon, 18 Jun 2018 14:26:49 +0200 Subject: [PATCH 1487/2380] spapr_cpu_core: migrate VPA related state QEMU implements the "Shared Processor LPAR" (SPLPAR) option, which allows the hypervisor to time-slice a physical processor into multiple virtual processor. The intent is to allow more guests to run, and to optimize processor utilization. The guest OS can cede idle VCPUs, so that their processing capacity may be used by other VCPUs, with the H_CEDE hcall. The guest OS can also optimize spinlocks, by confering the time-slice of a spinning VCPU to the spinlock holder if it's currently notrunning, with the H_CONFER hcall. Both hcalls depend on a "Virtual Processor Area" (VPA) to be registered by the guest OS, generally during early boot. Other per-VCPU areas can be registered: the "SLB Shadow Buffer" which allows a more efficient dispatching of VCPUs, and the "Dispatch Trace Log Buffer" (DTL) which is used to compute time stolen by the hypervisor. Both DTL and SLB Shadow areas depend on the VPA to be registered. The VPA/SLB Shadow/DTL are state that QEMU should migrate, but this doesn't happen, for no apparent reason other than it was just never coded. This causes the features listed above to stop working after migration, and it breaks the logic of the H_REGISTER_VPA hcall in the destination. The VPA is set at the guest request, ie, we don't have to migrate it before the guest has actually set it. This patch hence adds an "spapr_cpu/vpa" subsection to the recently introduced per-CPU machine data migration stream. Since DTL and SLB Shadow are optional and both depend on VPA, they get their own subsections "spapr_cpu/vpa/slb_shadow" and "spapr_cpu/vpa/dtl" hanging from the "spapr_cpu/vpa" subsection. Note that this won't break migration to older QEMUs. Is is already handled by only registering the vmstate handler for per-CPU data with newer machine types. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr_cpu_core.c | 65 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index f129ac884e..67f1596c57 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -129,6 +129,67 @@ static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp) g_free(sc->threads); } +static bool slb_shadow_needed(void *opaque) +{ + sPAPRCPUState *spapr_cpu = opaque; + + return spapr_cpu->slb_shadow_addr != 0; +} + +static const VMStateDescription vmstate_spapr_cpu_slb_shadow = { + .name = "spapr_cpu/vpa/slb_shadow", + .version_id = 1, + .minimum_version_id = 1, + .needed = slb_shadow_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(slb_shadow_addr, sPAPRCPUState), + VMSTATE_UINT64(slb_shadow_size, sPAPRCPUState), + VMSTATE_END_OF_LIST() + } +}; + +static bool dtl_needed(void *opaque) +{ + sPAPRCPUState *spapr_cpu = opaque; + + return spapr_cpu->dtl_addr != 0; +} + +static const VMStateDescription vmstate_spapr_cpu_dtl = { + .name = "spapr_cpu/vpa/dtl", + .version_id = 1, + .minimum_version_id = 1, + .needed = dtl_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(dtl_addr, sPAPRCPUState), + VMSTATE_UINT64(dtl_size, sPAPRCPUState), + VMSTATE_END_OF_LIST() + } +}; + +static bool vpa_needed(void *opaque) +{ + sPAPRCPUState *spapr_cpu = opaque; + + return spapr_cpu->vpa_addr != 0; +} + +static const VMStateDescription vmstate_spapr_cpu_vpa = { + .name = "spapr_cpu/vpa", + .version_id = 1, + .minimum_version_id = 1, + .needed = vpa_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(vpa_addr, sPAPRCPUState), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_spapr_cpu_slb_shadow, + &vmstate_spapr_cpu_dtl, + NULL + } +}; + static const VMStateDescription vmstate_spapr_cpu_state = { .name = "spapr_cpu", .version_id = 1, @@ -136,6 +197,10 @@ static const VMStateDescription vmstate_spapr_cpu_state = { .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription * []) { + &vmstate_spapr_cpu_vpa, + NULL + } }; static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr, From 77864267c3c82a4938d628b9313bce8fcce6373d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 18 Jun 2018 19:05:39 +0200 Subject: [PATCH 1488/2380] ppc/pnv: introduce Pnv8Chip and Pnv9Chip models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It introduces a base PnvChip class from which the specific processor chip classes, Pnv8Chip and Pnv9Chip, inherit. Each of them needs to define an init and a realize routine which will create the controllers of the target processor. For the moment, the base PnvChip class handles the XSCOM bus and the cores. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/pnv.c | 281 +++++++++++++++++++++++++++---------------- include/hw/ppc/pnv.h | 24 +++- 2 files changed, 202 insertions(+), 103 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index ac828d1331..a29ea996b4 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -531,12 +531,14 @@ static void pnv_reset(void) static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp) { - return pnv_lpc_isa_create(&chip->lpc, true, errp); + Pnv8Chip *chip8 = PNV8_CHIP(chip); + return pnv_lpc_isa_create(&chip8->lpc, true, errp); } static ISABus *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp) { - return pnv_lpc_isa_create(&chip->lpc, false, errp); + Pnv8Chip *chip8 = PNV8_CHIP(chip); + return pnv_lpc_isa_create(&chip8->lpc, false, errp); } static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp) @@ -725,6 +727,103 @@ static Object *pnv_chip_power9_intc_create(PnvChip *chip, Object *child, */ #define POWER9_CORE_MASK (0xffffffffffffffull) +static void pnv_chip_power8_instance_init(Object *obj) +{ + Pnv8Chip *chip8 = PNV8_CHIP(obj); + + object_initialize(&chip8->psi, sizeof(chip8->psi), TYPE_PNV_PSI); + object_property_add_child(obj, "psi", OBJECT(&chip8->psi), NULL); + object_property_add_const_link(OBJECT(&chip8->psi), "xics", + OBJECT(qdev_get_machine()), &error_abort); + + object_initialize(&chip8->lpc, sizeof(chip8->lpc), TYPE_PNV_LPC); + object_property_add_child(obj, "lpc", OBJECT(&chip8->lpc), NULL); + object_property_add_const_link(OBJECT(&chip8->lpc), "psi", + OBJECT(&chip8->psi), &error_abort); + + object_initialize(&chip8->occ, sizeof(chip8->occ), TYPE_PNV_OCC); + object_property_add_child(obj, "occ", OBJECT(&chip8->occ), NULL); + object_property_add_const_link(OBJECT(&chip8->occ), "psi", + OBJECT(&chip8->psi), &error_abort); +} + +static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) + { + PnvChip *chip = PNV_CHIP(chip8); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + const char *typename = pnv_chip_core_typename(chip); + size_t typesize = object_type_get_instance_size(typename); + int i, j; + char *name; + XICSFabric *xi = XICS_FABRIC(qdev_get_machine()); + + name = g_strdup_printf("icp-%x", chip->chip_id); + memory_region_init(&chip8->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip8->icp_mmio); + g_free(name); + + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip)); + + /* Map the ICP registers for each thread */ + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + int core_hwid = CPU_CORE(pnv_core)->core_id; + + for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { + uint32_t pir = pcc->core_pir(chip, core_hwid) + j; + PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir)); + + memory_region_add_subregion(&chip8->icp_mmio, pir << 12, + &icp->mmio); + } + } +} + +static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); + PnvChip *chip = PNV_CHIP(dev); + Pnv8Chip *chip8 = PNV8_CHIP(dev); + Error *local_err = NULL; + + pcc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Processor Service Interface (PSI) Host Bridge */ + object_property_set_int(OBJECT(&chip8->psi), PNV_PSIHB_BASE(chip), + "bar", &error_fatal); + object_property_set_bool(OBJECT(&chip8->psi), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip8->psi.xscom_regs); + + /* Create LPC controller */ + object_property_set_bool(OBJECT(&chip8->lpc), true, "realized", + &error_fatal); + pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip8->lpc.xscom_regs); + + /* Interrupt Management Area. This is the memory region holding + * all the Interrupt Control Presenter (ICP) registers */ + pnv_chip_icp_realize(chip8, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Create the simplified OCC model */ + object_property_set_bool(OBJECT(&chip8->occ), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip8->occ.xscom_regs); +} + static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -738,6 +837,9 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) k->isa_create = pnv_chip_power8_isa_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8E"; + + device_class_set_parent_realize(dc, pnv_chip_power8_realize, + &k->parent_realize); } static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) @@ -753,6 +855,9 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) k->isa_create = pnv_chip_power8_isa_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8"; + + device_class_set_parent_realize(dc, pnv_chip_power8_realize, + &k->parent_realize); } static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) @@ -768,6 +873,25 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) k->isa_create = pnv_chip_power8nvl_isa_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8NVL"; + + device_class_set_parent_realize(dc, pnv_chip_power8_realize, + &k->parent_realize); +} + +static void pnv_chip_power9_instance_init(Object *obj) +{ +} + +static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); + Error *local_err = NULL; + + pcc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } } static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) @@ -783,6 +907,9 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) k->isa_create = pnv_chip_power9_isa_create; k->xscom_base = 0x00603fc00000000ull; dc->desc = "PowerNV Chip POWER9"; + + device_class_set_parent_realize(dc, pnv_chip_power9_realize, + &k->parent_realize); } static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) @@ -815,59 +942,9 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) } } -static void pnv_chip_init(Object *obj) +static void pnv_chip_instance_init(Object *obj) { - PnvChip *chip = PNV_CHIP(obj); - PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); - - chip->xscom_base = pcc->xscom_base; - - object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC); - object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL); - - object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI); - object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL); - object_property_add_const_link(OBJECT(&chip->psi), "xics", - OBJECT(qdev_get_machine()), &error_abort); - - object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC); - object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL); - object_property_add_const_link(OBJECT(&chip->occ), "psi", - OBJECT(&chip->psi), &error_abort); - - /* The LPC controller needs PSI to generate interrupts */ - object_property_add_const_link(OBJECT(&chip->lpc), "psi", - OBJECT(&chip->psi), &error_abort); -} - -static void pnv_chip_icp_realize(PnvChip *chip, Error **errp) -{ - PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); - const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); - int i, j; - char *name; - XICSFabric *xi = XICS_FABRIC(qdev_get_machine()); - - name = g_strdup_printf("icp-%x", chip->chip_id); - memory_region_init(&chip->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip->icp_mmio); - g_free(name); - - sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip)); - - /* Map the ICP registers for each thread */ - for (i = 0; i < chip->nr_cores; i++) { - PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); - int core_hwid = CPU_CORE(pnv_core)->core_id; - - for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { - uint32_t pir = pcc->core_pir(chip, core_hwid) + j; - PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir)); - - memory_region_add_subregion(&chip->icp_mmio, pir << 12, &icp->mmio); - } - } + PNV_CHIP(obj)->xscom_base = PNV_CHIP_GET_CLASS(obj)->xscom_base; } static void pnv_chip_core_realize(PnvChip *chip, Error **errp) @@ -951,37 +1028,6 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) error_propagate(errp, error); return; } - - /* Create LPC controller */ - object_property_set_bool(OBJECT(&chip->lpc), true, "realized", - &error_fatal); - pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs); - - /* Interrupt Management Area. This is the memory region holding - * all the Interrupt Control Presenter (ICP) registers */ - pnv_chip_icp_realize(chip, &error); - if (error) { - error_propagate(errp, error); - return; - } - - /* Processor Service Interface (PSI) Host Bridge */ - object_property_set_int(OBJECT(&chip->psi), PNV_PSIHB_BASE(chip), - "bar", &error_fatal); - object_property_set_bool(OBJECT(&chip->psi), true, "realized", &error); - if (error) { - error_propagate(errp, error); - return; - } - pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip->psi.xscom_regs); - - /* Create the simplified OCC model */ - object_property_set_bool(OBJECT(&chip->occ), true, "realized", &error); - if (error) { - error_propagate(errp, error); - return; - } - pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip->occ.xscom_regs); } static Property pnv_chip_properties[] = { @@ -1009,8 +1055,10 @@ static ICSState *pnv_ics_get(XICSFabric *xi, int irq) int i; for (i = 0; i < pnv->num_chips; i++) { - if (ics_valid_irq(&pnv->chips[i]->psi.ics, irq)) { - return &pnv->chips[i]->psi.ics; + Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); + + if (ics_valid_irq(&chip8->psi.ics, irq)) { + return &chip8->psi.ics; } } return NULL; @@ -1022,7 +1070,8 @@ static void pnv_ics_resend(XICSFabric *xi) int i; for (i = 0; i < pnv->num_chips; i++) { - ics_resend(&pnv->chips[i]->psi.ics); + Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); + ics_resend(&chip8->psi.ics); } } @@ -1063,7 +1112,8 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, } for (i = 0; i < pnv->num_chips; i++) { - ics_pic_print_info(&pnv->chips[i]->psi.ics, mon); + Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); + ics_pic_print_info(&chip8->psi.ics, mon); } } @@ -1098,7 +1148,7 @@ static void pnv_set_num_chips(Object *obj, Visitor *v, const char *name, pnv->num_chips = num_chips; } -static void pnv_machine_initfn(Object *obj) +static void pnv_machine_instance_init(Object *obj) { PnvMachineState *pnv = PNV_MACHINE(obj); pnv->num_chips = 1; @@ -1138,11 +1188,18 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) pnv_machine_class_props_init(oc); } -#define DEFINE_PNV_CHIP_TYPE(type, class_initfn) \ - { \ - .name = type, \ - .class_init = class_initfn, \ - .parent = TYPE_PNV_CHIP, \ +#define DEFINE_PNV8_CHIP_TYPE(type, class_initfn) \ + { \ + .name = type, \ + .class_init = class_initfn, \ + .parent = TYPE_PNV8_CHIP, \ + } + +#define DEFINE_PNV9_CHIP_TYPE(type, class_initfn) \ + { \ + .name = type, \ + .class_init = class_initfn, \ + .parent = TYPE_PNV9_CHIP, \ } static const TypeInfo types[] = { @@ -1150,7 +1207,7 @@ static const TypeInfo types[] = { .name = TYPE_PNV_MACHINE, .parent = TYPE_MACHINE, .instance_size = sizeof(PnvMachineState), - .instance_init = pnv_machine_initfn, + .instance_init = pnv_machine_instance_init, .class_init = pnv_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_XICS_FABRIC }, @@ -1162,16 +1219,36 @@ static const TypeInfo types[] = { .name = TYPE_PNV_CHIP, .parent = TYPE_SYS_BUS_DEVICE, .class_init = pnv_chip_class_init, - .instance_init = pnv_chip_init, + .instance_init = pnv_chip_instance_init, .instance_size = sizeof(PnvChip), .class_size = sizeof(PnvChipClass), .abstract = true, }, - DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER9, pnv_chip_power9_class_init), - DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8, pnv_chip_power8_class_init), - DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8E, pnv_chip_power8e_class_init), - DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8NVL, - pnv_chip_power8nvl_class_init), + + /* + * P9 chip and variants + */ + { + .name = TYPE_PNV9_CHIP, + .parent = TYPE_PNV_CHIP, + .instance_init = pnv_chip_power9_instance_init, + .instance_size = sizeof(Pnv9Chip), + }, + DEFINE_PNV9_CHIP_TYPE(TYPE_PNV_CHIP_POWER9, pnv_chip_power9_class_init), + + /* + * P8 chip and variants + */ + { + .name = TYPE_PNV8_CHIP, + .parent = TYPE_PNV_CHIP, + .instance_init = pnv_chip_power8_instance_init, + .instance_size = sizeof(Pnv8Chip), + }, + DEFINE_PNV8_CHIP_TYPE(TYPE_PNV_CHIP_POWER8, pnv_chip_power8_class_init), + DEFINE_PNV8_CHIP_TYPE(TYPE_PNV_CHIP_POWER8E, pnv_chip_power8e_class_init), + DEFINE_PNV8_CHIP_TYPE(TYPE_PNV_CHIP_POWER8NVL, + pnv_chip_power8nvl_class_init), }; DEFINE_TYPES(types) diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 563279f3e0..86d5f54e54 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -57,12 +57,32 @@ typedef struct PnvChip { MemoryRegion xscom_mmio; MemoryRegion xscom; AddressSpace xscom_as; +} PnvChip; + +#define TYPE_PNV8_CHIP "pnv8-chip" +#define PNV8_CHIP(obj) OBJECT_CHECK(Pnv8Chip, (obj), TYPE_PNV8_CHIP) + +typedef struct Pnv8Chip { + /*< private >*/ + PnvChip parent_obj; + + /*< public >*/ MemoryRegion icp_mmio; PnvLpcController lpc; PnvPsi psi; PnvOCC occ; -} PnvChip; +} Pnv8Chip; + +#define TYPE_PNV9_CHIP "pnv9-chip" +#define PNV9_CHIP(obj) OBJECT_CHECK(Pnv9Chip, (obj), TYPE_PNV9_CHIP) + +typedef struct Pnv9Chip { + /*< private >*/ + PnvChip parent_obj; + + /*< public >*/ +} Pnv9Chip; typedef struct PnvChipClass { /*< private >*/ @@ -75,6 +95,8 @@ typedef struct PnvChipClass { hwaddr xscom_base; + DeviceRealize parent_realize; + uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); Object *(*intc_create)(PnvChip *chip, Object *child, Error **errp); ISABus *(*isa_create)(PnvChip *chip, Error **errp); From bb7ab95c96756663cf76914b171971d9582f828d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 18 Jun 2018 19:05:40 +0200 Subject: [PATCH 1489/2380] ppc/pnv: consolidate the creation of the ISA bus device tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The device tree node of the ISA bus was being partially done in different places. Move all the nodes creation under the same routine. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/pnv.c | 51 +++++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index a29ea996b4..7401ffe5b0 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -265,18 +265,6 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir, g_free(reg); } -static int pnv_chip_lpc_offset(PnvChip *chip, void *fdt) -{ - char *name; - int offset; - - name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x", - (uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE); - offset = fdt_path_offset(fdt, name); - g_free(name); - return offset; -} - static void pnv_dt_chip(PnvChip *chip, void *fdt) { const char *typename = pnv_chip_core_typename(chip); @@ -285,16 +273,6 @@ static void pnv_dt_chip(PnvChip *chip, void *fdt) pnv_dt_xscom(chip, fdt, 0); - /* The default LPC bus of a multichip system is on chip 0. It's - * recognized by the firmware (skiboot) using a "primary" - * property. - */ - if (chip->chip_id == 0x0) { - int lpc_offset = pnv_chip_lpc_offset(chip, fdt); - - _FDT((fdt_setprop(fdt, lpc_offset, "primary", NULL, 0))); - } - for (i = 0; i < chip->nr_cores; i++) { PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); @@ -418,16 +396,35 @@ static int pnv_dt_isa_device(DeviceState *dev, void *opaque) return 0; } -static void pnv_dt_isa(ISABus *bus, void *fdt, int lpc_offset) +static int pnv_chip_isa_offset(PnvChip *chip, void *fdt) { + char *name; + int offset; + + name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x", + (uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE); + offset = fdt_path_offset(fdt, name); + g_free(name); + return offset; +} + +/* The default LPC bus of a multichip system is on chip 0. It's + * recognized by the firmware (skiboot) using a "primary" property. + */ +static void pnv_dt_isa(PnvMachineState *pnv, void *fdt) +{ + int isa_offset = pnv_chip_isa_offset(pnv->chips[0], fdt); ForeachPopulateArgs args = { .fdt = fdt, - .offset = lpc_offset, + .offset = isa_offset, }; + _FDT((fdt_setprop(fdt, isa_offset, "primary", NULL, 0))); + /* ISA devices are not necessarily parented to the ISA bus so we * can not use object_child_foreach() */ - qbus_walk_children(BUS(bus), pnv_dt_isa_device, NULL, NULL, NULL, &args); + qbus_walk_children(BUS(pnv->isa_bus), pnv_dt_isa_device, NULL, NULL, NULL, + &args); } static void *pnv_dt_create(MachineState *machine) @@ -438,7 +435,6 @@ static void *pnv_dt_create(MachineState *machine) char *buf; int off; int i; - int lpc_offset; fdt = g_malloc0(FDT_MAX_SIZE); _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); @@ -480,8 +476,7 @@ static void *pnv_dt_create(MachineState *machine) } /* Populate ISA devices on chip 0 */ - lpc_offset = pnv_chip_lpc_offset(pnv->chips[0], fdt); - pnv_dt_isa(pnv->isa_bus, fdt, lpc_offset); + pnv_dt_isa(pnv, fdt); if (pnv->bmc) { pnv_dt_bmc_sensors(pnv->bmc, fdt); From ad99d04c76debb0d60c0bbb5d4f6ebb3509ddc91 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 14 Jun 2018 16:33:58 +1000 Subject: [PATCH 1490/2380] target/ppc: Allow cpu compatiblity checks based on type, not instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ppc_check_compat() is used in a number of places to check if a cpu object supports a certain compatiblity mode, subject to various constraints. It takes a PowerPCCPU *, however it really only depends on the cpu's class. We have upcoming cases where it would be useful to make compatibility checks before we fully instantiate the cpu objects. ppc_type_check_compat() will now make an equivalent check, but based on a CPU's QOM typename instead of an instantiated CPU object. We make use of the new interface in several places in spapr, where we're essentially making a global check, rather than one specific to a particular cpu. This avoids some ugly uses of first_cpu to grab a "representative" instance. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Cédric Le Goater --- hw/ppc/spapr.c | 10 ++++------ hw/ppc/spapr_caps.c | 19 +++++++++---------- target/ppc/compat.c | 27 +++++++++++++++++++++------ target/ppc/cpu.h | 4 ++++ 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 3174468fc5..bc179f6f89 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1616,8 +1616,8 @@ static void spapr_machine_reset(void) first_ppc_cpu = POWERPC_CPU(first_cpu); if (kvm_enabled() && kvmppc_has_cap_mmu_radix() && - ppc_check_compat(first_ppc_cpu, CPU_POWERPC_LOGICAL_3_00, 0, - spapr->max_compat_pvr)) { + ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0, + spapr->max_compat_pvr)) { /* If using KVM with radix mode available, VCPUs can be started * without a HPT because KVM will start them in radix mode. * Set the GR bit in PATB so that we know there is no HPT. */ @@ -2520,7 +2520,6 @@ static void spapr_machine_init(MachineState *machine) long load_limit, fw_size; char *filename; Error *resize_hpt_err = NULL; - PowerPCCPU *first_ppc_cpu; msi_nonbroken = true; @@ -2618,10 +2617,9 @@ static void spapr_machine_init(MachineState *machine) /* init CPUs */ spapr_init_cpus(spapr); - first_ppc_cpu = POWERPC_CPU(first_cpu); if ((!kvm_enabled() || kvmppc_has_cap_mmu_radix()) && - ppc_check_compat(first_ppc_cpu, CPU_POWERPC_LOGICAL_3_00, 0, - spapr->max_compat_pvr)) { + ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0, + spapr->max_compat_pvr)) { /* KVM and TCG always allow GTSE with radix... */ spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE); } diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 00e43a9ba7..469f38f0ef 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -327,27 +327,26 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { }; static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, - CPUState *cs) + const char *cputype) { sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - PowerPCCPU *cpu = POWERPC_CPU(cs); sPAPRCapabilities caps; caps = smc->default_caps; - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_07, - 0, spapr->max_compat_pvr)) { + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_07, + 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; } - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06_PLUS, - 0, spapr->max_compat_pvr)) { + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_06_PLUS, + 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; } - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06, - 0, spapr->max_compat_pvr)) { + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_06, + 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_VSX] = SPAPR_CAP_OFF; caps.caps[SPAPR_CAP_DFP] = SPAPR_CAP_OFF; caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; @@ -384,7 +383,7 @@ int spapr_caps_post_migration(sPAPRMachineState *spapr) sPAPRCapabilities dstcaps = spapr->eff; sPAPRCapabilities srccaps; - srccaps = default_caps_with_cpu(spapr, first_cpu); + srccaps = default_caps_with_cpu(spapr, MACHINE(spapr)->cpu_type); for (i = 0; i < SPAPR_CAP_NUM; i++) { /* If not default value then assume came in with the migration */ if (spapr->mig.caps[i] != spapr->def.caps[i]) { @@ -446,7 +445,7 @@ void spapr_caps_reset(sPAPRMachineState *spapr) int i; /* First compute the actual set of caps we're running with.. */ - default_caps = default_caps_with_cpu(spapr, first_cpu); + default_caps = default_caps_with_cpu(spapr, MACHINE(spapr)->cpu_type); for (i = 0; i < SPAPR_CAP_NUM; i++) { /* Store the defaults */ diff --git a/target/ppc/compat.c b/target/ppc/compat.c index 807c906f68..7de4bf3122 100644 --- a/target/ppc/compat.c +++ b/target/ppc/compat.c @@ -105,17 +105,13 @@ static const CompatInfo *compat_by_pvr(uint32_t pvr) return NULL; } -bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, - uint32_t min_compat_pvr, uint32_t max_compat_pvr) +static bool pcc_compat(PowerPCCPUClass *pcc, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr) { - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); const CompatInfo *compat = compat_by_pvr(compat_pvr); const CompatInfo *min = compat_by_pvr(min_compat_pvr); const CompatInfo *max = compat_by_pvr(max_compat_pvr); -#if !defined(CONFIG_USER_ONLY) - g_assert(cpu->vhyp); -#endif g_assert(!min_compat_pvr || min); g_assert(!max_compat_pvr || max); @@ -134,6 +130,25 @@ bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, return true; } +bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + +#if !defined(CONFIG_USER_ONLY) + g_assert(cpu->vhyp); +#endif + + return pcc_compat(pcc, compat_pvr, min_compat_pvr, max_compat_pvr); +} + +bool ppc_type_check_compat(const char *cputype, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(object_class_by_name(cputype)); + return pcc_compat(pcc, compat_pvr, min_compat_pvr, max_compat_pvr); +} + void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp) { const CompatInfo *compat = compat_by_pvr(compat_pvr); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 874da6efbc..c7f3fb6b73 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1369,7 +1369,11 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #if defined(TARGET_PPC64) bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, uint32_t min_compat_pvr, uint32_t max_compat_pvr); +bool ppc_type_check_compat(const char *cputype, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr); + void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp); + #if !defined(CONFIG_USER_ONLY) void ppc_set_compat_all(uint32_t compat_pvr, Error **errp); #endif From 9f6edd066e92f61543ea561b7c3cb340f30a7017 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 14 Jun 2018 16:37:28 +1000 Subject: [PATCH 1491/2380] spapr: Compute effective capability values earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the effective values of the various spapr capability flags were only determined at machine reset time. That was a lazy way of making sure it was after cpu initialization so it could use the cpu object to inform the defaults. But we've now improved the compat checking code so that we don't need to instantiate the cpus to use it. That lets us move the resolution of the capability defaults much earlier. This is going to be necessary for some future capabilities. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Cédric Le Goater --- hw/ppc/spapr.c | 6 ++++-- hw/ppc/spapr_caps.c | 9 ++++++--- include/hw/ppc/spapr.h | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index bc179f6f89..4a0b679166 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1612,7 +1612,7 @@ static void spapr_machine_reset(void) void *fdt; int rc; - spapr_caps_reset(spapr); + spapr_caps_apply(spapr); first_ppc_cpu = POWERPC_CPU(first_cpu); if (kvm_enabled() && kvmppc_has_cap_mmu_radix() && @@ -2526,7 +2526,9 @@ static void spapr_machine_init(MachineState *machine) QLIST_INIT(&spapr->phbs); QTAILQ_INIT(&spapr->pending_dimm_unplugs); - /* Check HPT resizing availability */ + /* Determine capabilities to run with */ + spapr_caps_init(spapr); + kvmppc_check_papr_resize_hpt(&resize_hpt_err); if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DEFAULT) { /* diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 469f38f0ef..dabed817d1 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -439,12 +439,12 @@ SPAPR_CAP_MIG_STATE(cfpc, SPAPR_CAP_CFPC); SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC); SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS); -void spapr_caps_reset(sPAPRMachineState *spapr) +void spapr_caps_init(sPAPRMachineState *spapr) { sPAPRCapabilities default_caps; int i; - /* First compute the actual set of caps we're running with.. */ + /* Compute the actual set of caps we should run with */ default_caps = default_caps_with_cpu(spapr, MACHINE(spapr)->cpu_type); for (i = 0; i < SPAPR_CAP_NUM; i++) { @@ -455,8 +455,11 @@ void spapr_caps_reset(sPAPRMachineState *spapr) spapr->eff.caps[i] = default_caps.caps[i]; } } +} - /* .. then apply those caps to the virtual hardware */ +void spapr_caps_apply(sPAPRMachineState *spapr) +{ + int i; for (i = 0; i < SPAPR_CAP_NUM; i++) { sPAPRCapabilityInfo *info = &capability_table[i]; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 3388750fc7..9dbd6010f5 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -798,7 +798,8 @@ static inline uint8_t spapr_get_cap(sPAPRMachineState *spapr, int cap) return spapr->eff.caps[cap]; } -void spapr_caps_reset(sPAPRMachineState *spapr); +void spapr_caps_init(sPAPRMachineState *spapr); +void spapr_caps_apply(sPAPRMachineState *spapr); void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp); int spapr_caps_post_migration(sPAPRMachineState *spapr); From e2e4f64118d128c41bd3c787afd2b9822ab758cd Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 28 Mar 2018 14:45:44 +1100 Subject: [PATCH 1492/2380] spapr: Add cpu_apply hook to capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spapr capabilities have an apply hook to actually activate (or deactivate) the feature in the system at reset time. However, a number of capabilities affect the setup of cpus, and need to be applied to each of them - including hotplugged cpus for extra complication. To make this simpler, add an optional cpu_apply hook that is called from spapr_cpu_reset(). Signed-off-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Cédric Le Goater --- hw/ppc/spapr_caps.c | 19 +++++++++++++++++++ hw/ppc/spapr_cpu_core.c | 2 ++ include/hw/ppc/spapr.h | 1 + 3 files changed, 22 insertions(+) diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index dabed817d1..68a4243efc 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -59,6 +59,8 @@ typedef struct sPAPRCapabilityInfo { sPAPRCapPossible *possible; /* Make sure the virtual hardware can support this capability */ void (*apply)(sPAPRMachineState *spapr, uint8_t val, Error **errp); + void (*cpu_apply)(sPAPRMachineState *spapr, PowerPCCPU *cpu, + uint8_t val, Error **errp); } sPAPRCapabilityInfo; static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name, @@ -472,6 +474,23 @@ void spapr_caps_apply(sPAPRMachineState *spapr) } } +void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu) +{ + int i; + + for (i = 0; i < SPAPR_CAP_NUM; i++) { + sPAPRCapabilityInfo *info = &capability_table[i]; + + /* + * If the apply function can't set the desired level and thinks it's + * fatal, it should cause that. + */ + if (info->cpu_apply) { + info->cpu_apply(spapr, cpu, spapr->eff.caps[i], &error_fatal); + } + } +} + void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp) { Error *local_err = NULL; diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 67f1596c57..bfb94f650c 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -76,6 +76,8 @@ static void spapr_cpu_reset(void *opaque) spapr_cpu->slb_shadow_size = 0; spapr_cpu->dtl_addr = 0; spapr_cpu->dtl_size = 0; + + spapr_caps_cpu_apply(SPAPR_MACHINE(qdev_get_machine()), cpu); } void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 9dbd6010f5..9dd46a72f6 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -800,6 +800,7 @@ static inline uint8_t spapr_get_cap(sPAPRMachineState *spapr, int cap) void spapr_caps_init(sPAPRMachineState *spapr); void spapr_caps_apply(sPAPRMachineState *spapr); +void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu); void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp); int spapr_caps_post_migration(sPAPRMachineState *spapr); From 24c6863c7b61efcfe4a0f0916ed4e8b797c8d281 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 14 Jun 2018 12:11:08 +1000 Subject: [PATCH 1493/2380] target/ppc: Add kvmppc_hpt_needs_host_contiguous_pages() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM HV has a restriction that for HPT mode guests, guest pages must be hpa contiguous as well as gpa contiguous. We have to account for that in various places. We determine whether we're subject to this restriction from the SMMU information exposed by KVM. Planned cleanups to the way we handle this will require knowing whether this restriction is in play in wider parts of the code. So, expose a helper function which returns it. This does mean some redundant calls to kvm_get_smmu_info(), but they'll go away again with future cleanups. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Cédric Le Goater --- target/ppc/kvm.c | 17 +++++++++++++++-- target/ppc/kvm_ppc.h | 6 ++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 5c0e313ca6..50b5d01432 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -406,9 +406,22 @@ target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, } } +bool kvmppc_hpt_needs_host_contiguous_pages(void) +{ + PowerPCCPU *cpu = POWERPC_CPU(first_cpu); + static struct kvm_ppc_smmu_info smmu_info; + + if (!kvm_enabled()) { + return false; + } + + kvm_get_smmu_info(cpu, &smmu_info); + return !!(smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL); +} + static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) { - if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) { + if (!kvmppc_hpt_needs_host_contiguous_pages()) { return true; } @@ -445,7 +458,7 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) /* If we have HV KVM, we need to forbid CI large pages if our * host page size is smaller than 64K. */ - if (smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL) { + if (kvmppc_hpt_needs_host_contiguous_pages()) { if (getpagesize() >= 0x10000) { cpu->hash64_opts->flags |= PPC_HASH64_CI_LARGEPAGE; } else { diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index e2840e1d33..a7ddb8a5d6 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -70,6 +70,7 @@ int kvmppc_resize_hpt_prepare(PowerPCCPU *cpu, target_ulong flags, int shift); int kvmppc_resize_hpt_commit(PowerPCCPU *cpu, target_ulong flags, int shift); bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu); +bool kvmppc_hpt_needs_host_contiguous_pages(void); bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path); #else @@ -222,6 +223,11 @@ static inline uint64_t kvmppc_rma_size(uint64_t current_size, return ram_size; } +static inline bool kvmppc_hpt_needs_host_contiguous_pages(void) +{ + return false; +} + static inline bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path) { return true; From 4fe75a8ccd8005f8d0322c5b85ebee6243b2e753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 18 Jun 2018 19:34:00 +0200 Subject: [PATCH 1494/2380] spapr: split the IRQ allocation sequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Today, when a device requests for IRQ number in a sPAPR machine, the spapr_irq_alloc() routine first scans the ICSState status array to find an empty slot and then performs the assignement of the selected numbers. Split this sequence in two distinct routines : spapr_irq_find() for lookups and spapr_irq_claim() for claiming the IRQ numbers. This will ease the introduction of a static layout of IRQ numbers. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/spapr.c | 50 ++++++++++++++++++++++++++++++++++++++++++ hw/ppc/spapr_events.c | 18 +++++++++++---- hw/ppc/spapr_pci.c | 23 ++++++++++++++++--- hw/ppc/spapr_vio.c | 10 ++++++++- include/hw/ppc/spapr.h | 4 ++++ 5 files changed, 97 insertions(+), 8 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 4a0b679166..b7705c3944 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3816,6 +3816,36 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum) return -1; } +int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp) +{ + ICSState *ics = spapr->ics; + int first = -1; + + assert(ics); + + /* + * MSIMesage::data is used for storing VIRQ so + * it has to be aligned to num to support multiple + * MSI vectors. MSI-X is not affected by this. + * The hint is used for the first IRQ, the rest should + * be allocated continuously. + */ + if (align) { + assert((num == 1) || (num == 2) || (num == 4) || + (num == 8) || (num == 16) || (num == 32)); + first = ics_find_free_block(ics, num, num); + } else { + first = ics_find_free_block(ics, num, 1); + } + + if (first < 0) { + error_setg(errp, "can't find a free %d-IRQ block", num); + return -1; + } + + return first + ics->offset; +} + /* * Allocate the IRQ number and set the IRQ type, LSI or MSI */ @@ -3894,6 +3924,26 @@ int spapr_irq_alloc_block(sPAPRMachineState *spapr, int num, bool lsi, return first; } +int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp) +{ + ICSState *ics = spapr->ics; + + assert(ics); + + if (!ics_valid_irq(ics, irq)) { + error_setg(errp, "IRQ %d is invalid", irq); + return -1; + } + + if (!ICS_IRQ_FREE(ics, irq - ics->offset)) { + error_setg(errp, "IRQ %d is not free", irq); + return -1; + } + + spapr_irq_set_lsi(spapr, irq, lsi); + return 0; +} + void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num) { ICSState *ics = spapr->ics; diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 86836f0626..e4f5946a21 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -707,13 +707,18 @@ void spapr_clear_pending_events(sPAPRMachineState *spapr) void spapr_events_init(sPAPRMachineState *spapr) { + int epow_irq; + + epow_irq = spapr_irq_findone(spapr, &error_fatal); + + spapr_irq_claim(spapr, epow_irq, false, &error_fatal); + QTAILQ_INIT(&spapr->pending_events); spapr->event_sources = spapr_event_sources_new(); spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_EPOW, - spapr_irq_alloc(spapr, 0, false, - &error_fatal)); + epow_irq); /* NOTE: if machine supports modern/dedicated hotplug event source, * we add it to the device-tree unconditionally. This means we may @@ -724,9 +729,14 @@ void spapr_events_init(sPAPRMachineState *spapr) * checking that it's enabled. */ if (spapr->use_hotplug_event_source) { + int hp_irq; + + hp_irq = spapr_irq_findone(spapr, &error_fatal); + + spapr_irq_claim(spapr, hp_irq, false, &error_fatal); + spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_HOT_PLUG, - spapr_irq_alloc(spapr, 0, false, - &error_fatal)); + hp_irq); } spapr->epow_notifier.notify = spapr_powerdown_req; diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index f936ce63ef..497b896c7d 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -279,6 +279,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, spapr_pci_msi *msi; int *config_addr_key; Error *err = NULL; + int i; /* Fins sPAPRPHBState */ phb = spapr_pci_find_phb(spapr, buid); @@ -371,8 +372,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, } /* Allocate MSIs */ - irq = spapr_irq_alloc_block(spapr, req_num, false, - ret_intr_type == RTAS_TYPE_MSI, &err); + irq = spapr_irq_find(spapr, req_num, ret_intr_type == RTAS_TYPE_MSI, &err); if (err) { error_reportf_err(err, "Can't allocate MSIs for device %x: ", config_addr); @@ -380,6 +380,16 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } + for (i = 0; i < req_num; i++) { + spapr_irq_claim(spapr, irq + i, false, &err); + if (err) { + error_reportf_err(err, "Can't allocate MSIs for device %x: ", + config_addr); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); + return; + } + } + /* Release previous MSIs */ if (msi) { spapr_irq_free(spapr, msi->first_irq, msi->num); @@ -1698,7 +1708,14 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) uint32_t irq; Error *local_err = NULL; - irq = spapr_irq_alloc_block(spapr, 1, true, false, &local_err); + irq = spapr_irq_findone(spapr, &local_err); + if (local_err) { + error_propagate(errp, local_err); + error_prepend(errp, "can't allocate LSIs: "); + return; + } + + spapr_irq_claim(spapr, irq, true, &local_err); if (local_err) { error_propagate(errp, local_err); error_prepend(errp, "can't allocate LSIs: "); diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 4555c648a8..daf85130b5 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -475,7 +475,15 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) dev->qdev.id = id; } - dev->irq = spapr_irq_alloc(spapr, dev->irq, false, &local_err); + if (!dev->irq) { + dev->irq = spapr_irq_findone(spapr, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + spapr_irq_claim(spapr, dev->irq, false, &local_err); if (local_err) { error_propagate(errp, local_err); return; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 9dd46a72f6..6bfdf5a2fb 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -776,6 +776,10 @@ int spapr_irq_alloc(sPAPRMachineState *spapr, int irq_hint, bool lsi, Error **errp); int spapr_irq_alloc_block(sPAPRMachineState *spapr, int num, bool lsi, bool align, Error **errp); +int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, + Error **errp); +#define spapr_irq_findone(spapr, errp) spapr_irq_find(spapr, 1, false, errp) +int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp); void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num); qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq); From 71b5c8d26e35549db4b83d907b2886623d8dd99a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 18 Jun 2018 19:34:01 +0200 Subject: [PATCH 1495/2380] spapr: remove unused spapr_irq routines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spapr_irq_alloc_block and spapr_irq_alloc() are now deprecated. Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Signed-off-by: David Gibson --- hw/ppc/spapr.c | 80 +----------------------------------------- include/hw/ppc/spapr.h | 4 --- 2 files changed, 1 insertion(+), 83 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b7705c3944..78186500e9 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3846,84 +3846,6 @@ int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp) return first + ics->offset; } -/* - * Allocate the IRQ number and set the IRQ type, LSI or MSI - */ -static void spapr_irq_set_lsi(sPAPRMachineState *spapr, int irq, bool lsi) -{ - ics_set_irq_type(spapr->ics, irq - spapr->ics->offset, lsi); -} - -int spapr_irq_alloc(sPAPRMachineState *spapr, int irq_hint, bool lsi, - Error **errp) -{ - ICSState *ics = spapr->ics; - int irq; - - assert(ics); - - if (irq_hint) { - if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) { - error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); - return -1; - } - irq = irq_hint; - } else { - irq = ics_find_free_block(ics, 1, 1); - if (irq < 0) { - error_setg(errp, "can't allocate IRQ: no IRQ left"); - return -1; - } - irq += ics->offset; - } - - spapr_irq_set_lsi(spapr, irq, lsi); - trace_spapr_irq_alloc(irq); - - return irq; -} - -/* - * Allocate block of consecutive IRQs, and return the number of the first IRQ in - * the block. If align==true, aligns the first IRQ number to num. - */ -int spapr_irq_alloc_block(sPAPRMachineState *spapr, int num, bool lsi, - bool align, Error **errp) -{ - ICSState *ics = spapr->ics; - int i, first = -1; - - assert(ics); - - /* - * MSIMesage::data is used for storing VIRQ so - * it has to be aligned to num to support multiple - * MSI vectors. MSI-X is not affected by this. - * The hint is used for the first IRQ, the rest should - * be allocated continuously. - */ - if (align) { - assert((num == 1) || (num == 2) || (num == 4) || - (num == 8) || (num == 16) || (num == 32)); - first = ics_find_free_block(ics, num, num); - } else { - first = ics_find_free_block(ics, num, 1); - } - if (first < 0) { - error_setg(errp, "can't find a free %d-IRQ block", num); - return -1; - } - - first += ics->offset; - for (i = first; i < first + num; ++i) { - spapr_irq_set_lsi(spapr, i, lsi); - } - - trace_spapr_irq_alloc_block(first, num, lsi, align); - - return first; -} - int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp) { ICSState *ics = spapr->ics; @@ -3940,7 +3862,7 @@ int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp) return -1; } - spapr_irq_set_lsi(spapr, irq, lsi); + ics_set_irq_type(ics, irq - ics->offset, lsi); return 0; } diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 6bfdf5a2fb..8a9142244f 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -772,10 +772,6 @@ int spapr_get_vcpu_id(PowerPCCPU *cpu); void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp); PowerPCCPU *spapr_find_cpu(int vcpu_id); -int spapr_irq_alloc(sPAPRMachineState *spapr, int irq_hint, bool lsi, - Error **errp); -int spapr_irq_alloc_block(sPAPRMachineState *spapr, int num, bool lsi, - bool align, Error **errp); int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp); #define spapr_irq_findone(spapr, errp) spapr_irq_find(spapr, 1, false, errp) From 88d8d5555d0257a31b5891a328d957cc9b579eb2 Mon Sep 17 00:00:00 2001 From: John Arbuckle Date: Mon, 18 Jun 2018 11:50:24 -0400 Subject: [PATCH 1496/2380] fpu_helper.c: fix helper_fpscr_clrbit() function Fix the helper_fpscr_clrbit() function so it correctly sets the FEX and VX bits. Determining the value for the Floating Point Status and Control Register's (FPSCR) FEX bit is suppose to be done like this: FEX = (VX & VE) | (OX & OE) | (UX & UE) | (ZX & ZE) | (XX & XE)) It is described as "the logical OR of all the floating-point exception bits masked by their respective enable bits". It was not implemented correctly. The value of FEX would stay on even when all other bits were set to off. The VX bit is described as "the logical OR of all of the invalid operation exceptions". This bit was also not implemented correctly. It too would stay on when all the other bits were set to off. My main source of information is an IBM document called: PowerPC Microprocessor Family: The Programming Environments for 32-Bit Microprocessors Page 62 is where the FPSCR information is located. This is an older copy than the one I use but it is still very useful: https://www.pdfdrive.net/powerpc-microprocessor-family-the-programming-environments-for-32-e3087633.html I use a G3 and G5 iMac to compare bit values with QEMU. This patch fixed all the problems I was having with these bits. Signed-off-by: John Arbuckle [dwg: Re-wrapped commit message] Signed-off-by: David Gibson --- target/ppc/fpu_helper.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index d31a933cbb..7714bfe0f9 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -325,6 +325,34 @@ void helper_fpscr_clrbit(CPUPPCState *env, uint32_t bit) case FPSCR_RN: fpscr_set_rounding_mode(env); break; + case FPSCR_VXSNAN: + case FPSCR_VXISI: + case FPSCR_VXIDI: + case FPSCR_VXZDZ: + case FPSCR_VXIMZ: + case FPSCR_VXVC: + case FPSCR_VXSOFT: + case FPSCR_VXSQRT: + case FPSCR_VXCVI: + if (!fpscr_ix) { + /* Set VX bit to zero */ + env->fpscr &= ~(1 << FPSCR_VX); + } + break; + case FPSCR_OX: + case FPSCR_UX: + case FPSCR_ZX: + case FPSCR_XX: + case FPSCR_VE: + case FPSCR_OE: + case FPSCR_UE: + case FPSCR_ZE: + case FPSCR_XE: + if (!fpscr_eex) { + /* Set the FEX bit */ + env->fpscr &= ~(1 << FPSCR_FEX); + } + break; default: break; } From a69232e2a36b9fcee46ddbf140ab2de1583abf39 Mon Sep 17 00:00:00 2001 From: Sebastian Bauer Date: Mon, 18 Jun 2018 23:38:16 +0200 Subject: [PATCH 1497/2380] sm501: Fix hardware cursor color conversion According to the sm501 specs the hardware cursor colors are to be given in the rgb565 format, but the code currently interprets them as bgr565. Therefore, the colors of the hardware cursors are wrong in the QEMU display, e.g., the standard mouse pointer of AmigaOS appears blue instead of red. This change fixes this issue by replacing the existing naive bgr565 => rgb888 conversion with a standard rgb565 => rgb888 one that also scales the color component values properly. Signed-off-by: Sebastian Bauer Signed-off-by: David Gibson --- hw/display/sm501.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index ca0840f6fa..8206ae81a1 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -652,9 +652,9 @@ static inline void get_hwc_palette(SM501State *state, int crt, uint8_t *palette) } else { rgb565 = color_reg & 0xFFFF; } - palette[i * 3 + 0] = (rgb565 << 3) & 0xf8; /* red */ - palette[i * 3 + 1] = (rgb565 >> 3) & 0xfc; /* green */ - palette[i * 3 + 2] = (rgb565 >> 8) & 0xf8; /* blue */ + palette[i * 3 + 0] = ((rgb565 >> 11) * 527 + 23) >> 6; /* r */ + palette[i * 3 + 1] = (((rgb565 >> 5) & 0x3f) * 259 + 33) >> 6; /* g */ + palette[i * 3 + 2] = ((rgb565 & 0x1f) * 527 + 23) >> 6; /* b */ } } From 39aeba6caa4b9de8b195fddddae5cc5835d19b04 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 19 Jun 2018 10:52:15 +0200 Subject: [PATCH 1498/2380] ppc4xx_i2c: Remove unimplemented sdata and intr registers We don't emulate slave mode so related registers are not needed. [lh]sadr are only retained to avoid too many warnings and simplify debugging but sdata is not even correct because device has a 4 byte FIFO instead so just remove this unimplemented register for now. The intr register is also not implemented correctly, it is for diagnostics and normally not even visible on device without explicitly enabling it. As no guests are known to need this remove it as well. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/i2c/ppc4xx_i2c.c | 16 +--------------- include/hw/i2c/ppc4xx_i2c.h | 4 +--- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index d1936dbdca..4e0aaae1fc 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 Jocelyn Mayer * Copyright (c) 2012 François Revol - * Copyright (c) 2016 BALATON Zoltan + * Copyright (c) 2016-2018 BALATON Zoltan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -63,7 +63,6 @@ static void ppc4xx_i2c_reset(DeviceState *s) i2c->mdcntl = 0; i2c->sts = 0; i2c->extsts = 0x8f; - i2c->sdata = 0; i2c->lsadr = 0; i2c->hsadr = 0; i2c->clkdiv = 0; @@ -71,7 +70,6 @@ static void ppc4xx_i2c_reset(DeviceState *s) i2c->xfrcnt = 0; i2c->xtcntlss = 0; i2c->directcntl = 0xf; - i2c->intr = 0; } static inline bool ppc4xx_i2c_is_master(PPC4xxI2CState *i2c) @@ -139,9 +137,6 @@ static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size) TYPE_PPC4xx_I2C, __func__); } break; - case 2: - ret = i2c->sdata; - break; case 4: ret = i2c->lmadr; break; @@ -181,9 +176,6 @@ static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size) case 16: ret = i2c->directcntl; break; - case 17: - ret = i2c->intr; - break; default: if (addr < PPC4xx_I2C_MEM_SIZE) { qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register 0x%" @@ -229,9 +221,6 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, } } break; - case 2: - i2c->sdata = value; - break; case 4: i2c->lmadr = value; if (i2c_bus_busy(i2c->bus)) { @@ -302,9 +291,6 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, case 16: i2c->directcntl = value & 0x7; break; - case 17: - i2c->intr = value; - break; default: if (addr < PPC4xx_I2C_MEM_SIZE) { qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register 0x%" diff --git a/include/hw/i2c/ppc4xx_i2c.h b/include/hw/i2c/ppc4xx_i2c.h index 3c603071bd..e4b6ded855 100644 --- a/include/hw/i2c/ppc4xx_i2c.h +++ b/include/hw/i2c/ppc4xx_i2c.h @@ -3,7 +3,7 @@ * * Copyright (c) 2007 Jocelyn Mayer * Copyright (c) 2012 François Revol - * Copyright (c) 2016 BALATON Zoltan + * Copyright (c) 2016-2018 BALATON Zoltan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -49,7 +49,6 @@ typedef struct PPC4xxI2CState { uint8_t mdcntl; uint8_t sts; uint8_t extsts; - uint8_t sdata; uint8_t lsadr; uint8_t hsadr; uint8_t clkdiv; @@ -57,7 +56,6 @@ typedef struct PPC4xxI2CState { uint8_t xfrcnt; uint8_t xtcntlss; uint8_t directcntl; - uint8_t intr; } PPC4xxI2CState; #endif /* PPC4XX_I2C_H */ From ef9173a5c086b5114343a86943f9b26a9c72d7d6 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 19 Jun 2018 10:52:15 +0200 Subject: [PATCH 1499/2380] ppc4xx_i2c: Implement directcntl register As well as being able to generate its own i2c transactions, the ppc4xx i2c controller has a DIRECTCNTL register which allows explicit control of the i2c lines. Using this register an OS can directly bitbang i2c operations. In order to let emulated i2c devices respond to this, we need to wire up the DIRECTCNTL register to qemu's bitbanged i2c handling code. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- default-configs/ppc-softmmu.mak | 1 + default-configs/ppcemb-softmmu.mak | 1 + hw/i2c/ppc4xx_i2c.c | 14 +++++++++++++- include/hw/i2c/ppc4xx_i2c.h | 4 ++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index abeeb0418a..851b4afc21 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -26,6 +26,7 @@ CONFIG_USB_EHCI_SYSBUS=y CONFIG_SM501=y CONFIG_IDE_SII3112=y CONFIG_I2C=y +CONFIG_BITBANG_I2C=y # For Macs CONFIG_MAC=y diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak index 67d18b2e0e..37af1930b3 100644 --- a/default-configs/ppcemb-softmmu.mak +++ b/default-configs/ppcemb-softmmu.mak @@ -19,3 +19,4 @@ CONFIG_USB_EHCI_SYSBUS=y CONFIG_SM501=y CONFIG_IDE_SII3112=y CONFIG_I2C=y +CONFIG_BITBANG_I2C=y diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index 4e0aaae1fc..fca80d695a 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -30,6 +30,7 @@ #include "cpu.h" #include "hw/hw.h" #include "hw/i2c/ppc4xx_i2c.h" +#include "bitbang_i2c.h" #define PPC4xx_I2C_MEM_SIZE 18 @@ -46,6 +47,11 @@ #define IIC_XTCNTLSS_SRST (1 << 0) +#define IIC_DIRECTCNTL_SDAC (1 << 3) +#define IIC_DIRECTCNTL_SCLC (1 << 2) +#define IIC_DIRECTCNTL_MSDA (1 << 1) +#define IIC_DIRECTCNTL_MSCL (1 << 0) + static void ppc4xx_i2c_reset(DeviceState *s) { PPC4xxI2CState *i2c = PPC4xx_I2C(s); @@ -289,7 +295,12 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, i2c->xtcntlss = value; break; case 16: - i2c->directcntl = value & 0x7; + i2c->directcntl = value & (IIC_DIRECTCNTL_SDAC & IIC_DIRECTCNTL_SCLC); + i2c->directcntl |= (value & IIC_DIRECTCNTL_SCLC ? 1 : 0); + bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SCL, + i2c->directcntl & IIC_DIRECTCNTL_MSCL); + i2c->directcntl |= bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SDA, + (value & IIC_DIRECTCNTL_SDAC) != 0) << 1; break; default: if (addr < PPC4xx_I2C_MEM_SIZE) { @@ -322,6 +333,7 @@ static void ppc4xx_i2c_init(Object *o) sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); s->bus = i2c_init_bus(DEVICE(s), "i2c"); + s->bitbang = bitbang_i2c_init(s->bus); } static void ppc4xx_i2c_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/i2c/ppc4xx_i2c.h b/include/hw/i2c/ppc4xx_i2c.h index e4b6ded855..ea6c8e1a58 100644 --- a/include/hw/i2c/ppc4xx_i2c.h +++ b/include/hw/i2c/ppc4xx_i2c.h @@ -31,6 +31,9 @@ #include "hw/sysbus.h" #include "hw/i2c/i2c.h" +/* from hw/i2c/bitbang_i2c.h */ +typedef struct bitbang_i2c_interface bitbang_i2c_interface; + #define TYPE_PPC4xx_I2C "ppc4xx-i2c" #define PPC4xx_I2C(obj) OBJECT_CHECK(PPC4xxI2CState, (obj), TYPE_PPC4xx_I2C) @@ -42,6 +45,7 @@ typedef struct PPC4xxI2CState { I2CBus *bus; qemu_irq irq; MemoryRegion iomem; + bitbang_i2c_interface *bitbang; uint8_t mdata; uint8_t lmadr; uint8_t hmadr; From 0c8d8c8b8fd6db3110a5be3886b1ed9ba89be8b1 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 19 Jun 2018 10:52:15 +0200 Subject: [PATCH 1500/2380] target/ppc: Add missing opcode for icbt on PPC440 According to PPC440 User Manual PPC440 has multiple opcodes for icbt instruction: one for compatibility with older cores and two 440 specific opcodes one of which is defined in BookE. QEMU only implements two of these, add the missing one. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- target/ppc/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 5fe1ba6555..3a215a1dc6 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -6707,6 +6707,8 @@ GEN_HANDLER_E(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, GEN_HANDLER(msync_4xx, 0x1F, 0x16, 0x12, 0x03FFF801, PPC_BOOKE), GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE, PPC2_BOOKE206), +GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, + PPC_440_SPEC), GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), From 65fa57b09bec6f638c77bdf031698189a1ad050c Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Wed, 20 Jun 2018 10:32:02 +1000 Subject: [PATCH 1501/2380] hmp-commands: use long for begin and length in dump-guest-memory The dump-guest-memory command is used to dump an area of guest memory to a file, the piece of memory is specified by a begin address and a length. These parameters are specified as ints and thus have a maximum value of 4GB. This means you can't dump the guest memory past the first 4GB and instead get: (qemu) dump-guest-memory tmp 0x100000000 0x100000000 'dump-guest-memory' has failed: integer is for 32-bit values Try "help dump-guest-memory" for more information This limitation is imposed in monitor_parse_arguments() since they are both ints. hmp_dump_guest_memory() uses 64 bit quantities to store both the begin and length values. Thus specify begin and length as long so that the entire guest memory space can be dumped. Signed-off-by: Suraj Jitindar Singh Message-Id: <20180620003202.10546-1-sjitindarsingh@gmail.com> Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Dr. David Alan Gilbert --- hmp-commands.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 0de7c4c29e..754620e411 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1116,7 +1116,7 @@ ETEXI { .name = "dump-guest-memory", - .args_type = "paging:-p,detach:-d,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?", + .args_type = "paging:-p,detach:-d,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:l?,length:l?", .params = "[-p] [-d] [-z|-l|-s] filename [begin length]", .help = "dump guest memory into file 'filename'.\n\t\t\t" "-p: do paging to get guest's memory mapping.\n\t\t\t" From c3120f715db9dc351bde89b8a9d992cfeccb4680 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 20 Jun 2018 16:39:41 +0100 Subject: [PATCH 1502/2380] hmp: Add flag for preconfig commands Add a flag to command definitions to allow them to be used in preconfig and check it. If users try to use commands that aren't available, tell them to use the exit_preconfig comand we're adding in a few patches. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Markus Armbruster Reviewed-by: Igor Mammedov Message-Id: <20180620153947.30834-2-dgilbert@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- monitor.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/monitor.c b/monitor.c index 2a8187f5d7..ff26d863db 100644 --- a/monitor.c +++ b/monitor.c @@ -128,6 +128,7 @@ typedef struct mon_cmd_t { const char *args_type; const char *params; const char *help; + const char *flags; /* p=preconfig */ void (*cmd)(Monitor *mon, const QDict *qdict); /* @sub_table is a list of 2nd level of commands. If it does not exist, * cmd should be used. If it exists, sub_table[?].cmd should be @@ -958,6 +959,19 @@ static int parse_cmdline(const char *cmdline, return -1; } +/* + * Returns true if the command can be executed in preconfig mode + * i.e. it has the 'p' flag. + */ +static bool cmd_can_preconfig(const mon_cmd_t *cmd) +{ + if (!cmd->flags) { + return false; + } + + return strchr(cmd->flags, 'p'); +} + static void help_cmd_dump_one(Monitor *mon, const mon_cmd_t *cmd, char **prefix_args, @@ -3041,6 +3055,12 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon, (int)(p - cmdp_start), cmdp_start); return NULL; } + if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) { + monitor_printf(mon, "Command '%.*s' not available with -preconfig " + "until after exit_preconfig.\n", + (int)(p - cmdp_start), cmdp_start); + return NULL; + } /* filter out following useless space */ while (qemu_isspace(*p)) { From 31785f1b030b62ec84e1c852eaeeabbae1232345 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 20 Jun 2018 16:39:42 +0100 Subject: [PATCH 1503/2380] hmp: Allow help on preconfig commands Allow the 'help' command in preconfig state but make it only list the preconfig commands. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Peter Xu Reviewed-by: Igor Mammedov Message-Id: <20180620153947.30834-3-dgilbert@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- hmp-commands.hx | 1 + monitor.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 754620e411..7ce991f4dd 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -15,6 +15,7 @@ ETEXI .params = "[cmd]", .help = "show the help", .cmd = do_help_cmd, + .flags = "p", }, STEXI diff --git a/monitor.c b/monitor.c index ff26d863db..18c2207e6d 100644 --- a/monitor.c +++ b/monitor.c @@ -979,6 +979,10 @@ static void help_cmd_dump_one(Monitor *mon, { int i; + if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) { + return; + } + for (i = 0; i < prefix_args_nb; i++) { monitor_printf(mon, "%s ", prefix_args[i]); } @@ -1001,7 +1005,9 @@ static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds, /* Find one entry to dump */ for (cmd = cmds; cmd->name != NULL; cmd++) { - if (compare_cmd(args[arg_index], cmd->name)) { + if (compare_cmd(args[arg_index], cmd->name) && + ((!runstate_check(RUN_STATE_PRECONFIG) || + cmd_can_preconfig(cmd)))) { if (cmd->sub_table) { /* continue with next arg */ help_cmd_dump(mon, cmd->sub_table, From 6d9f7839b7e6c364536d22beb2ca1277772813a6 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 20 Jun 2018 16:39:43 +0100 Subject: [PATCH 1504/2380] hmp: Restrict auto-complete in preconfig Don't show the commands that aren't available. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Peter Xu Reviewed-by: Igor Mammedov Message-Id: <20180620153947.30834-4-dgilbert@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- monitor.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/monitor.c b/monitor.c index 18c2207e6d..068c094a9a 100644 --- a/monitor.c +++ b/monitor.c @@ -4020,12 +4020,17 @@ static void monitor_find_completion_by_table(Monitor *mon, cmdname = args[0]; readline_set_completion_index(mon->rs, strlen(cmdname)); for (cmd = cmd_table; cmd->name != NULL; cmd++) { - cmd_completion(mon, cmdname, cmd->name); + if (!runstate_check(RUN_STATE_PRECONFIG) || + cmd_can_preconfig(cmd)) { + cmd_completion(mon, cmdname, cmd->name); + } } } else { /* find the command */ for (cmd = cmd_table; cmd->name != NULL; cmd++) { - if (compare_cmd(args[0], cmd->name)) { + if (compare_cmd(args[0], cmd->name) && + (!runstate_check(RUN_STATE_PRECONFIG) || + cmd_can_preconfig(cmd))) { break; } } From a87706c83a0101d59eb8dafe998152bbd66aeec8 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 20 Jun 2018 16:39:44 +0100 Subject: [PATCH 1505/2380] qmp: Enable a few commands in preconfig state Commands query-chardev, query-version, query-name, query-uuid, query-iothreads, query-memdev are informational and do not depend on the machine being initialized. Make them available in preconfig runstate to make the latter a little bit more useful. The generic qom commands don't depend on the machine being initialized either; so enabled qom-list, qom-get, qom-set, qom-list-types, qom-list-properties. Signed-off-by: Igor Mammedov Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Eric Blake Message-Id: <20180620153947.30834-5-dgilbert@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- qapi/char.json | 3 ++- qapi/misc.json | 27 +++++++++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/qapi/char.json b/qapi/char.json index ae19dcd1ed..60f31d83fc 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -62,7 +62,8 @@ # } # ## -{ 'command': 'query-chardev', 'returns': ['ChardevInfo'] } +{ 'command': 'query-chardev', 'returns': ['ChardevInfo'], + 'allow-preconfig': true } ## # @ChardevBackendInfo: diff --git a/qapi/misc.json b/qapi/misc.json index f83a63a0ab..fa86831ec3 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -117,7 +117,8 @@ # } # ## -{ 'command': 'query-version', 'returns': 'VersionInfo' } +{ 'command': 'query-version', 'returns': 'VersionInfo', + 'allow-preconfig': true } ## # @CommandInfo: @@ -241,7 +242,7 @@ # <- { "return": { "name": "qemu-name" } } # ## -{ 'command': 'query-name', 'returns': 'NameInfo' } +{ 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true } ## # @KvmInfo: @@ -301,7 +302,7 @@ # <- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } } # ## -{ 'command': 'query-uuid', 'returns': 'UuidInfo' } +{ 'command': 'query-uuid', 'returns': 'UuidInfo', 'allow-preconfig': true } ## # @EventInfo: @@ -710,7 +711,8 @@ # } # ## -{ 'command': 'query-iothreads', 'returns': ['IOThreadInfo'] } +{ 'command': 'query-iothreads', 'returns': ['IOThreadInfo'], + 'allow-preconfig': true } ## # @BalloonInfo: @@ -1408,7 +1410,8 @@ ## { 'command': 'qom-list', 'data': { 'path': 'str' }, - 'returns': [ 'ObjectPropertyInfo' ] } + 'returns': [ 'ObjectPropertyInfo' ], + 'allow-preconfig': true } ## # @qom-get: @@ -1444,7 +1447,8 @@ ## { 'command': 'qom-get', 'data': { 'path': 'str', 'property': 'str' }, - 'returns': 'any' } + 'returns': 'any', + 'allow-preconfig': true } ## # @qom-set: @@ -1461,7 +1465,8 @@ # Since: 1.2 ## { 'command': 'qom-set', - 'data': { 'path': 'str', 'property': 'str', 'value': 'any' } } + 'data': { 'path': 'str', 'property': 'str', 'value': 'any' }, + 'allow-preconfig': true } ## # @change: @@ -1543,7 +1548,8 @@ ## { 'command': 'qom-list-types', 'data': { '*implements': 'str', '*abstract': 'bool' }, - 'returns': [ 'ObjectTypeInfo' ] } + 'returns': [ 'ObjectTypeInfo' ], + 'allow-preconfig': true } ## # @device-list-properties: @@ -1581,7 +1587,8 @@ ## { 'command': 'qom-list-properties', 'data': { 'typename': 'str'}, - 'returns': [ 'ObjectPropertyInfo' ] } + 'returns': [ 'ObjectPropertyInfo' ], + 'allow-preconfig': true } ## # @xen-set-global-dirty-log: @@ -2902,7 +2909,7 @@ # } # ## -{ 'command': 'query-memdev', 'returns': ['Memdev'] } +{ 'command': 'query-memdev', 'returns': ['Memdev'], 'allow-preconfig': true } ## # @PCDIMMDeviceInfo: From 8c7c7ecbb1e5e9325b736906a4368b76a68d0eae Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 20 Jun 2018 16:39:45 +0100 Subject: [PATCH 1506/2380] hmp: Add commands for preconfig Allow a bunch of the info commands to be used in preconfig. version, chardev, name, uuid,memdev, iothreads Were enabled in QMP in the previous patch from Igor status, hotpluggable_cpus Was enabled in the original allow-preconfig series history is HMP specific qom-tree Don't have a QMP equivalent Also enable the qom commands qom-list and qom-set. Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180620153947.30834-6-dgilbert@redhat.com> Reviewed-by: Igor Mammedov Signed-off-by: Dr. David Alan Gilbert Dropped info numa as per Igor's 2018-06-21 review --- hmp-commands-info.hx | 10 ++++++++++ hmp-commands.hx | 3 +++ 2 files changed, 13 insertions(+) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index ddfcd5adcc..6db3457a78 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -19,6 +19,7 @@ ETEXI .params = "", .help = "show the version of QEMU", .cmd = hmp_info_version, + .flags = "p", }, STEXI @@ -47,6 +48,7 @@ ETEXI .params = "", .help = "show the character devices", .cmd = hmp_info_chardev, + .flags = "p", }, STEXI @@ -165,6 +167,7 @@ ETEXI .params = "", .help = "show the command line history", .cmd = hmp_info_history, + .flags = "p", }, STEXI @@ -399,6 +402,7 @@ ETEXI .params = "", .help = "show the current VM status (running|paused)", .cmd = hmp_info_status, + .flags = "p", }, STEXI @@ -457,6 +461,7 @@ ETEXI .params = "", .help = "show the current VM name", .cmd = hmp_info_name, + .flags = "p", }, STEXI @@ -471,6 +476,7 @@ ETEXI .params = "", .help = "show the current VM UUID", .cmd = hmp_info_uuid, + .flags = "p", }, STEXI @@ -613,6 +619,7 @@ ETEXI .params = "[path]", .help = "show QOM composition tree", .cmd = hmp_info_qom_tree, + .flags = "p", }, STEXI @@ -671,6 +678,7 @@ ETEXI .params = "", .help = "show memory backends", .cmd = hmp_info_memdev, + .flags = "p", }, STEXI @@ -699,6 +707,7 @@ ETEXI .params = "", .help = "show iothreads", .cmd = hmp_info_iothreads, + .flags = "p", }, STEXI @@ -829,6 +838,7 @@ ETEXI .params = "", .help = "Show information about hotpluggable CPUs", .cmd = hmp_hotpluggable_cpus, + .flags = "p", }, STEXI diff --git a/hmp-commands.hx b/hmp-commands.hx index 7ce991f4dd..4acf6a3222 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1828,6 +1828,7 @@ ETEXI .params = "path", .help = "list QOM properties", .cmd = hmp_qom_list, + .flags = "p", }, STEXI @@ -1841,6 +1842,7 @@ ETEXI .params = "path property value", .help = "set QOM property", .cmd = hmp_qom_set, + .flags = "p", }, STEXI @@ -1855,6 +1857,7 @@ ETEXI .help = "show various information about the system state", .cmd = hmp_info_help, .sub_table = info_cmds, + .flags = "p", }, STEXI From 8e8581e6b1b77e878520a2334f8e74b1001f1578 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 20 Jun 2018 16:39:46 +0100 Subject: [PATCH 1507/2380] hmp: add exit_preconfig Add the exit_preconfig command to return to normality. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Peter Xu Reviewed-by: Igor Mammedov Message-Id: <20180620153947.30834-7-dgilbert@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- hmp-commands.hx | 19 +++++++++++++++++++ hmp.c | 8 ++++++++ hmp.h | 1 + 3 files changed, 28 insertions(+) diff --git a/hmp-commands.hx b/hmp-commands.hx index 4acf6a3222..ba9cdb8800 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -55,6 +55,25 @@ STEXI @item q or quit @findex quit Quit the emulator. +ETEXI + + { + .name = "exit_preconfig", + .args_type = "", + .params = "", + .help = "exit the preconfig state", + .cmd = hmp_exit_preconfig, + .flags = "p", + }, + +STEXI +@item exit_preconfig +@findex exit_preconfig +This command makes QEMU exit the preconfig state and proceed with +VM initialization using configuration data provided on the command line +and via the QMP monitor during the preconfig state. The command is only +available during the preconfig state (i.e. when the --preconfig command +line option was in use). ETEXI { diff --git a/hmp.c b/hmp.c index f40d8279cf..f601099f90 100644 --- a/hmp.c +++ b/hmp.c @@ -1068,6 +1068,14 @@ void hmp_system_powerdown(Monitor *mon, const QDict *qdict) qmp_system_powerdown(NULL); } +void hmp_exit_preconfig(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_exit_preconfig(&err); + hmp_handle_error(mon, &err); +} + void hmp_cpu(Monitor *mon, const QDict *qdict) { int64_t cpu_index; diff --git a/hmp.h b/hmp.h index 20f27439d3..33354f1bdd 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ void hmp_quit(Monitor *mon, const QDict *qdict); void hmp_stop(Monitor *mon, const QDict *qdict); void hmp_system_reset(Monitor *mon, const QDict *qdict); void hmp_system_powerdown(Monitor *mon, const QDict *qdict); +void hmp_exit_preconfig(Monitor *mon, const QDict *qdict); void hmp_cpu(Monitor *mon, const QDict *qdict); void hmp_memsave(Monitor *mon, const QDict *qdict); void hmp_pmemsave(Monitor *mon, const QDict *qdict); From 13163a93b729b9ec4b49501d03d3f50af4b05758 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 20 Jun 2018 16:39:47 +0100 Subject: [PATCH 1508/2380] hmp: Allow HMP in preconfig state again Now we can cope with preconfig in HMP, reenable by reverting commit 71dc578e116599ea73c8a2a4e693134702ec0e83. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Peter Xu Reviewed-by: Igor Mammedov Message-Id: <20180620153947.30834-8-dgilbert@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- monitor.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/monitor.c b/monitor.c index 068c094a9a..0730a27172 100644 --- a/monitor.c +++ b/monitor.c @@ -3461,12 +3461,6 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline) trace_handle_hmp_command(mon, cmdline); - if (runstate_check(RUN_STATE_PRECONFIG)) { - monitor_printf(mon, "HMP not available in preconfig state, " - "use QMP instead\n"); - return; - } - cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table); if (!cmd) { return; From ac5de4984df282d64feb4af33b92e0a75652e2b6 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 19 Jun 2018 15:39:18 -0500 Subject: [PATCH 1509/2380] tests: Simplify .gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 0bcc8e5b was yet another instance of 'git status' reporting dirty files after an in-tree build, thanks to the new binary tests/check-block-qdict. Instead of piecemeal exemptions of each new binary as they are added, let's use git's negative globbing feature to exempt ALL files that have a 'test-' or 'check-' prefix, except for the ones ending in '.c' or '.sh'. We still have a couple of generated files that then need (re-)exclusion, but the overall list is a LOT shorter, and less prone to needing future edits. Signed-off-by: Eric Blake Message-Id: <20180619203918.65450-1-eblake@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- tests/.gitignore | 93 +++--------------------------------------------- 1 file changed, 5 insertions(+), 88 deletions(-) diff --git a/tests/.gitignore b/tests/.gitignore index 2bc61a9a58..08e2df1ce1 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -2,101 +2,18 @@ atomic_add-bench benchmark-crypto-cipher benchmark-crypto-hash benchmark-crypto-hmac -check-qdict -check-qnum -check-qjson -check-qlist -check-qlit -check-qnull -check-qobject -check-qstring -check-qom-interface -check-qom-proplist +check-* +!check-*.c +!check-*.sh qht-bench rcutorture -test-aio -test-aio-multithread -test-arm-mptimer -test-base64 -test-bdrv-drain -test-bitops -test-bitcnt -test-block-backend -test-blockjob -test-blockjob-txn -test-bufferiszero -test-char -test-clone-visitor -test-coroutine -test-crypto-afsplit -test-crypto-block -test-crypto-cipher -test-crypto-hash -test-crypto-hmac -test-crypto-ivgen -test-crypto-pbkdf -test-crypto-secret -test-crypto-tlscredsx509 -test-crypto-tlscredsx509-work/ -test-crypto-tlscredsx509-certs/ -test-crypto-tlssession -test-crypto-tlssession-work/ -test-crypto-tlssession-client/ -test-crypto-tlssession-server/ -test-crypto-xts -test-cutils -test-hbitmap -test-hmp -test-int128 -test-iov -test-io-channel-buffer -test-io-channel-command -test-io-channel-command.fifo -test-io-channel-file -test-io-channel-file.txt -test-io-channel-socket -test-io-channel-tls -test-io-task -test-keyval -test-logging -test-mul64 -test-opts-visitor +test-* +!test-*.c test-qapi-commands.[ch] test-qapi-events.[ch] test-qapi-types.[ch] -test-qapi-util test-qapi-visit.[ch] -test-qdev-global-props -test-qemu-opts -test-qdist -test-qga -test-qht -test-qht-par -test-qmp-cmds -test-qmp-event -test-qobject-input-strict -test-qobject-input-visitor test-qapi-introspect.[ch] -test-qobject-output-visitor -test-rcu-list -test-replication -test-shift128 -test-string-input-visitor -test-string-output-visitor -test-thread-pool -test-throttle -test-timed-average -test-uuid -test-util-sockets -test-visitor-serialization -test-vmstate -test-write-threshold -test-x86-cpuid -test-x86-cpuid-compat -test-xbzrle -test-netfilter -test-filter-mirror -test-filter-redirector *-test qapi-schema/*.test.* vm/*.img From d8b20291cba6aa9bb295885a34f2b5f05d59d1b2 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 21 Jun 2018 07:49:37 -0500 Subject: [PATCH 1510/2380] nbd/server: Reject 0-length block status request The NBD spec says that behavior is unspecified if the client requests 0 length for block status; but since the structured reply is documenting as returning a non-zero length, it's easier to just diagnose this with an EINVAL error than to figure out what to return. CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake Message-Id: <20180621124937.166549-1-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- nbd/server.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nbd/server.c b/nbd/server.c index 9e1f227178..493a926e06 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -2007,6 +2007,10 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, "discard failed", errp); case NBD_CMD_BLOCK_STATUS: + if (!request->len) { + return nbd_send_generic_reply(client, request->handle, -EINVAL, + "need non-zero length", errp); + } if (client->export_meta.valid && client->export_meta.base_allocation) { return nbd_co_send_block_status(client, request->handle, blk_bs(exp->blk), request->from, From dbb8b396bb46388cee92e9094c563297d04c43ed Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Sat, 9 Jun 2018 18:17:53 +0300 Subject: [PATCH 1511/2380] nbd/server: fix trace Return code = 1 doesn't mean that we parsed base:allocation. Use correct traces in both -parsed and -skipped cases. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180609151758.17343-2-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: comment tweaks] Signed-off-by: Eric Blake --- nbd/server.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 493a926e06..942c016c2a 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -736,12 +736,16 @@ static int nbd_negotiate_send_meta_context(NBDClient *client, /* nbd_meta_base_query * - * Handle query to 'base' namespace. For now, only base:allocation context is - * available in it. 'len' is the amount of text remaining to be read from + * Handle queries to 'base' namespace. For now, only the base:allocation + * context is available. 'len' is the amount of text remaining to be read from * the current name, after the 'base:' portion has been stripped. * * Return -errno on I/O error, 0 if option was completely handled by - * sending a reply about inconsistent lengths, or 1 on success. */ + * sending a reply about inconsistent lengths, or 1 on success. + * + * Note: return code = 1 doesn't mean that we've parsed the "base:allocation" + * namespace. It only means that there are no errors. + */ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, uint32_t len, Error **errp) { @@ -768,10 +772,12 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, } if (strncmp(query, "allocation", alen) == 0) { + trace_nbd_negotiate_meta_query_parse("base:allocation"); meta->base_allocation = true; + } else { + trace_nbd_negotiate_meta_query_skip("not base:allocation"); } - trace_nbd_negotiate_meta_query_parse("base:allocation"); return 1; } From af736e546717d832168dd332a328bfcf74a0ab3d Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Sat, 9 Jun 2018 18:17:54 +0300 Subject: [PATCH 1512/2380] nbd/server: refactor NBDExportMetaContexts Use NBDExport pointer instead of just export name: there is no need to store a duplicated name in the struct; moreover, NBDExport will be used further. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180609151758.17343-3-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: commit message grammar tweak] Signed-off-by: Eric Blake --- nbd/server.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 942c016c2a..26cc41ad75 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -88,7 +88,7 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); * as selected by NBD_OPT_SET_META_CONTEXT. Also used for * NBD_OPT_LIST_META_CONTEXT. */ typedef struct NBDExportMetaContexts { - char export_name[NBD_MAX_NAME_SIZE + 1]; + NBDExport *exp; bool valid; /* means that negotiation of the option finished without errors */ bool base_allocation; /* export base:allocation context (block status) */ @@ -399,10 +399,9 @@ static int nbd_negotiate_handle_list(NBDClient *client, Error **errp) return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); } -static void nbd_check_meta_export_name(NBDClient *client) +static void nbd_check_meta_export(NBDClient *client) { - client->export_meta.valid &= !strcmp(client->exp->name, - client->export_meta.export_name); + client->export_meta.valid &= client->exp == client->export_meta.exp; } /* Send a reply to NBD_OPT_EXPORT_NAME. @@ -456,7 +455,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); nbd_export_get(client->exp); - nbd_check_meta_export_name(client); + nbd_check_meta_export(client); return 0; } @@ -650,7 +649,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, client->exp = exp; QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); nbd_export_get(client->exp); - nbd_check_meta_export_name(client); + nbd_check_meta_export(client); rc = 1; } return rc; @@ -835,7 +834,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client, NBDExportMetaContexts *meta, Error **errp) { int ret; - NBDExport *exp; + char export_name[NBD_MAX_NAME_SIZE + 1]; NBDExportMetaContexts local_meta; uint32_t nb_queries; int i; @@ -854,15 +853,15 @@ static int nbd_negotiate_meta_queries(NBDClient *client, memset(meta, 0, sizeof(*meta)); - ret = nbd_opt_read_name(client, meta->export_name, NULL, errp); + ret = nbd_opt_read_name(client, export_name, NULL, errp); if (ret <= 0) { return ret; } - exp = nbd_export_find(meta->export_name); - if (exp == NULL) { + meta->exp = nbd_export_find(export_name); + if (meta->exp == NULL) { return nbd_opt_drop(client, NBD_REP_ERR_UNKNOWN, errp, - "export '%s' not present", meta->export_name); + "export '%s' not present", export_name); } ret = nbd_opt_read(client, &nb_queries, sizeof(nb_queries), errp); @@ -871,7 +870,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client, } cpu_to_be32s(&nb_queries); trace_nbd_negotiate_meta_context(nbd_opt_lookup(client->opt), - meta->export_name, nb_queries); + export_name, nb_queries); if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) { /* enable all known contexts */ From b0769d8f8df0b51881f1f15c9e29722cf6191a43 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 19 Jun 2018 16:55:09 -0500 Subject: [PATCH 1513/2380] nbd/server: add nbd_meta_empty_or_pattern helper Add nbd_meta_pattern() and nbd_meta_empty_or_pattern() helpers for metadata query parsing. nbd_meta_pattern() will be reused for the "qemu" namespace in following patches. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180609151758.17343-4-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: comment tweaks] Signed-off-by: Eric Blake --- nbd/server.c | 101 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 33 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 26cc41ad75..9171cd4168 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -733,6 +733,71 @@ static int nbd_negotiate_send_meta_context(NBDClient *client, return qio_channel_writev_all(client->ioc, iov, 2, errp) < 0 ? -EIO : 0; } +/* Read strlen(@pattern) bytes, and set @match to true if they match @pattern. + * @match is never set to false. + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. + * + * Note: return code = 1 doesn't mean that we've read exactly @pattern. + * It only means that there are no errors. + */ +static int nbd_meta_pattern(NBDClient *client, const char *pattern, bool *match, + Error **errp) +{ + int ret; + char *query; + size_t len = strlen(pattern); + + assert(len); + + query = g_malloc(len); + ret = nbd_opt_read(client, query, len, errp); + if (ret <= 0) { + g_free(query); + return ret; + } + + if (strncmp(query, pattern, len) == 0) { + trace_nbd_negotiate_meta_query_parse(pattern); + *match = true; + } else { + trace_nbd_negotiate_meta_query_skip("pattern not matched"); + } + g_free(query); + + return 1; +} + +/* + * Read @len bytes, and set @match to true if they match @pattern, or if @len + * is 0 and the client is performing _LIST_. @match is never set to false. + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. + * + * Note: return code = 1 doesn't mean that we've read exactly @pattern. + * It only means that there are no errors. + */ +static int nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern, + uint32_t len, bool *match, Error **errp) +{ + if (len == 0) { + if (client->opt == NBD_OPT_LIST_META_CONTEXT) { + *match = true; + } + trace_nbd_negotiate_meta_query_parse("empty"); + return 1; + } + + if (len != strlen(pattern)) { + trace_nbd_negotiate_meta_query_skip("different lengths"); + return nbd_opt_skip(client, len, errp); + } + + return nbd_meta_pattern(client, pattern, match, errp); +} + /* nbd_meta_base_query * * Handle queries to 'base' namespace. For now, only the base:allocation @@ -741,43 +806,12 @@ static int nbd_negotiate_send_meta_context(NBDClient *client, * * Return -errno on I/O error, 0 if option was completely handled by * sending a reply about inconsistent lengths, or 1 on success. - * - * Note: return code = 1 doesn't mean that we've parsed the "base:allocation" - * namespace. It only means that there are no errors. */ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, uint32_t len, Error **errp) { - int ret; - char query[sizeof("allocation") - 1]; - size_t alen = strlen("allocation"); - - if (len == 0) { - if (client->opt == NBD_OPT_LIST_META_CONTEXT) { - meta->base_allocation = true; - } - trace_nbd_negotiate_meta_query_parse("base:"); - return 1; - } - - if (len != alen) { - trace_nbd_negotiate_meta_query_skip("not base:allocation"); - return nbd_opt_skip(client, len, errp); - } - - ret = nbd_opt_read(client, query, len, errp); - if (ret <= 0) { - return ret; - } - - if (strncmp(query, "allocation", alen) == 0) { - trace_nbd_negotiate_meta_query_parse("base:allocation"); - meta->base_allocation = true; - } else { - trace_nbd_negotiate_meta_query_skip("not base:allocation"); - } - - return 1; + return nbd_meta_empty_or_pattern(client, "allocation", len, + &meta->base_allocation, errp); } /* nbd_negotiate_meta_query @@ -823,6 +857,7 @@ static int nbd_negotiate_meta_query(NBDClient *client, return nbd_opt_skip(client, len, errp); } + trace_nbd_negotiate_meta_query_parse("base:"); return nbd_meta_base_query(client, meta, len, errp); } From 3d068aff16d6dbf066328977c5152847a62f2a0a Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Sat, 9 Jun 2018 18:17:56 +0300 Subject: [PATCH 1514/2380] nbd/server: implement dirty bitmap export Handle a new NBD meta namespace: "qemu", and corresponding queries: "qemu:dirty-bitmap:". With the new metadata context negotiated, BLOCK_STATUS query will reply with dirty-bitmap data, converted to extents. The new public function nbd_export_bitmap selects which bitmap to export. For now, only one bitmap may be exported. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180609151758.17343-5-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: wording tweaks, minor cleanups, additional tracing] Signed-off-by: Eric Blake --- include/block/nbd.h | 8 +- nbd/server.c | 278 ++++++++++++++++++++++++++++++++++++++++---- nbd/trace-events | 1 + 3 files changed, 262 insertions(+), 25 deletions(-) diff --git a/include/block/nbd.h b/include/block/nbd.h index fcdcd54502..8bb9606c39 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -229,11 +229,13 @@ enum { #define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1) #define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2) -/* Flags for extents (NBDExtent.flags) of NBD_REPLY_TYPE_BLOCK_STATUS, - * for base:allocation meta context */ +/* Extent flags for base:allocation in NBD_REPLY_TYPE_BLOCK_STATUS */ #define NBD_STATE_HOLE (1 << 0) #define NBD_STATE_ZERO (1 << 1) +/* Extent flags for qemu:dirty-bitmap in NBD_REPLY_TYPE_BLOCK_STATUS */ +#define NBD_STATE_DIRTY (1 << 0) + static inline bool nbd_reply_type_is_error(int type) { return type & (1 << 15); @@ -315,6 +317,8 @@ void nbd_client_put(NBDClient *client); void nbd_server_start(SocketAddress *addr, const char *tls_creds, Error **errp); +void nbd_export_bitmap(NBDExport *exp, const char *bitmap, + const char *bitmap_export_name, Error **errp); /* nbd_read * Reads @size bytes from @ioc. Returns 0 on success. diff --git a/nbd/server.c b/nbd/server.c index 9171cd4168..2c2d62c636 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -23,6 +23,13 @@ #include "nbd-internal.h" #define NBD_META_ID_BASE_ALLOCATION 0 +#define NBD_META_ID_DIRTY_BITMAP 1 + +/* NBD_MAX_BITMAP_EXTENTS: 1 mb of extents data. An empirical + * constant. If an increase is needed, note that the NBD protocol + * recommends no larger than 32 mb, so that the client won't consider + * the reply as a denial of service attack. */ +#define NBD_MAX_BITMAP_EXTENTS (0x100000 / 8) static int system_errno_to_nbd_errno(int err) { @@ -80,6 +87,9 @@ struct NBDExport { BlockBackend *eject_notifier_blk; Notifier eject_notifier; + + BdrvDirtyBitmap *export_bitmap; + char *export_bitmap_context; }; static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); @@ -92,6 +102,7 @@ typedef struct NBDExportMetaContexts { bool valid; /* means that negotiation of the option finished without errors */ bool base_allocation; /* export base:allocation context (block status) */ + bool bitmap; /* export qemu:dirty-bitmap: */ } NBDExportMetaContexts; struct NBDClient { @@ -814,6 +825,56 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, &meta->base_allocation, errp); } +/* nbd_meta_bitmap_query + * + * Handle query to 'qemu:' namespace. + * @len is the amount of text remaining to be read from the current name, after + * the 'qemu:' portion has been stripped. + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. */ +static int nbd_meta_qemu_query(NBDClient *client, NBDExportMetaContexts *meta, + uint32_t len, Error **errp) +{ + bool dirty_bitmap = false; + size_t dirty_bitmap_len = strlen("dirty-bitmap:"); + int ret; + + if (!meta->exp->export_bitmap) { + trace_nbd_negotiate_meta_query_skip("no dirty-bitmap exported"); + return nbd_opt_skip(client, len, errp); + } + + if (len == 0) { + if (client->opt == NBD_OPT_LIST_META_CONTEXT) { + meta->bitmap = true; + } + trace_nbd_negotiate_meta_query_parse("empty"); + return 1; + } + + if (len < dirty_bitmap_len) { + trace_nbd_negotiate_meta_query_skip("not dirty-bitmap:"); + return nbd_opt_skip(client, len, errp); + } + + len -= dirty_bitmap_len; + ret = nbd_meta_pattern(client, "dirty-bitmap:", &dirty_bitmap, errp); + if (ret <= 0) { + return ret; + } + if (!dirty_bitmap) { + trace_nbd_negotiate_meta_query_skip("not dirty-bitmap:"); + return nbd_opt_skip(client, len, errp); + } + + trace_nbd_negotiate_meta_query_parse("dirty-bitmap:"); + + return nbd_meta_empty_or_pattern( + client, meta->exp->export_bitmap_context + + strlen("qemu:dirty_bitmap:"), len, &meta->bitmap, errp); +} + /* nbd_negotiate_meta_query * * Parse namespace name and call corresponding function to parse body of the @@ -829,9 +890,14 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, static int nbd_negotiate_meta_query(NBDClient *client, NBDExportMetaContexts *meta, Error **errp) { + /* + * Both 'qemu' and 'base' namespaces have length = 5 including a + * colon. If another length namespace is later introduced, this + * should certainly be refactored. + */ int ret; - char query[sizeof("base:") - 1]; - size_t baselen = strlen("base:"); + size_t ns_len = 5; + char ns[5]; uint32_t len; ret = nbd_opt_read(client, &len, sizeof(len), errp); @@ -840,25 +906,27 @@ static int nbd_negotiate_meta_query(NBDClient *client, } cpu_to_be32s(&len); - /* The only supported namespace for now is 'base'. So query should start - * with 'base:'. Otherwise, we can ignore it and skip the remainder. */ - if (len < baselen) { + if (len < ns_len) { trace_nbd_negotiate_meta_query_skip("length too short"); return nbd_opt_skip(client, len, errp); } - len -= baselen; - ret = nbd_opt_read(client, query, baselen, errp); + len -= ns_len; + ret = nbd_opt_read(client, ns, ns_len, errp); if (ret <= 0) { return ret; } - if (strncmp(query, "base:", baselen) != 0) { - trace_nbd_negotiate_meta_query_skip("not for base: namespace"); - return nbd_opt_skip(client, len, errp); + + if (!strncmp(ns, "base:", ns_len)) { + trace_nbd_negotiate_meta_query_parse("base:"); + return nbd_meta_base_query(client, meta, len, errp); + } else if (!strncmp(ns, "qemu:", ns_len)) { + trace_nbd_negotiate_meta_query_parse("qemu:"); + return nbd_meta_qemu_query(client, meta, len, errp); } - trace_nbd_negotiate_meta_query_parse("base:"); - return nbd_meta_base_query(client, meta, len, errp); + trace_nbd_negotiate_meta_query_skip("unknown namespace"); + return nbd_opt_skip(client, len, errp); } /* nbd_negotiate_meta_queries @@ -928,6 +996,16 @@ static int nbd_negotiate_meta_queries(NBDClient *client, } } + if (meta->bitmap) { + ret = nbd_negotiate_send_meta_context(client, + meta->exp->export_bitmap_context, + NBD_META_ID_DIRTY_BITMAP, + errp); + if (ret < 0) { + return ret; + } + } + ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); if (ret == 0) { meta->valid = true; @@ -1556,6 +1634,11 @@ void nbd_export_put(NBDExport *exp) exp->blk = NULL; } + if (exp->export_bitmap) { + bdrv_dirty_bitmap_set_qmp_locked(exp->export_bitmap, false); + g_free(exp->export_bitmap_context); + } + g_free(exp); } } @@ -1797,9 +1880,15 @@ static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset, } /* nbd_co_send_extents - * @extents should be in big-endian */ + * + * @length is only for tracing purposes (and may be smaller or larger + * than the client's original request). @last controls whether + * NBD_REPLY_FLAG_DONE is sent. @extents should already be in + * big-endian format. + */ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, - NBDExtent *extents, unsigned nb_extents, + NBDExtent *extents, unsigned int nb_extents, + uint64_t length, bool last, uint32_t context_id, Error **errp) { NBDStructuredMeta chunk; @@ -1809,7 +1898,9 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, {.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])} }; - set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_BLOCK_STATUS, + trace_nbd_co_send_extents(handle, nb_extents, context_id, length, last); + set_be_chunk(&chunk.h, last ? NBD_REPLY_FLAG_DONE : 0, + NBD_REPLY_TYPE_BLOCK_STATUS, handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); stl_be_p(&chunk.context_id, context_id); @@ -1819,8 +1910,8 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, /* Get block status from the exported device and send it to the client */ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, BlockDriverState *bs, uint64_t offset, - uint64_t length, uint32_t context_id, - Error **errp) + uint64_t length, bool last, + uint32_t context_id, Error **errp) { int ret; NBDExtent extent; @@ -1831,7 +1922,84 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, client, handle, -ret, "can't get block status", errp); } - return nbd_co_send_extents(client, handle, &extent, 1, context_id, errp); + return nbd_co_send_extents(client, handle, &extent, 1, length, last, + context_id, errp); +} + +/* + * Populate @extents from a dirty bitmap. Unless @dont_fragment, the + * final extent may exceed the original @length. Store in @length the + * byte length encoded (which may be smaller or larger than the + * original), and return the number of extents used. + */ +static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, + uint64_t *length, NBDExtent *extents, + unsigned int nb_extents, + bool dont_fragment) +{ + uint64_t begin = offset, end; + uint64_t overall_end = offset + *length; + unsigned int i = 0; + BdrvDirtyBitmapIter *it; + bool dirty; + + bdrv_dirty_bitmap_lock(bitmap); + + it = bdrv_dirty_iter_new(bitmap); + dirty = bdrv_get_dirty_locked(NULL, bitmap, offset); + + assert(begin < overall_end && nb_extents); + while (begin < overall_end && i < nb_extents) { + if (dirty) { + end = bdrv_dirty_bitmap_next_zero(bitmap, begin); + } else { + bdrv_set_dirty_iter(it, begin); + end = bdrv_dirty_iter_next(it); + } + if (end == -1 || end - begin > UINT32_MAX) { + /* Cap to an aligned value < 4G beyond begin. */ + end = MIN(bdrv_dirty_bitmap_size(bitmap), + begin + UINT32_MAX + 1 - + bdrv_dirty_bitmap_granularity(bitmap)); + } + if (dont_fragment && end > overall_end) { + end = overall_end; + } + + extents[i].length = cpu_to_be32(end - begin); + extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0); + i++; + begin = end; + dirty = !dirty; + } + + bdrv_dirty_iter_free(it); + + bdrv_dirty_bitmap_unlock(bitmap); + + *length = end - offset; + return i; +} + +static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle, + BdrvDirtyBitmap *bitmap, uint64_t offset, + uint32_t length, bool dont_fragment, bool last, + uint32_t context_id, Error **errp) +{ + int ret; + unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BITMAP_EXTENTS; + NBDExtent *extents = g_new(NBDExtent, nb_extents); + uint64_t final_length = length; + + nb_extents = bitmap_to_extents(bitmap, offset, &final_length, extents, + nb_extents, dont_fragment); + + ret = nbd_co_send_extents(client, handle, extents, nb_extents, + final_length, last, context_id, errp); + + g_free(extents); + + return ret; } /* nbd_co_receive_request @@ -2051,11 +2219,34 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, return nbd_send_generic_reply(client, request->handle, -EINVAL, "need non-zero length", errp); } - if (client->export_meta.valid && client->export_meta.base_allocation) { - return nbd_co_send_block_status(client, request->handle, - blk_bs(exp->blk), request->from, - request->len, - NBD_META_ID_BASE_ALLOCATION, errp); + if (client->export_meta.valid && + (client->export_meta.base_allocation || + client->export_meta.bitmap)) + { + if (client->export_meta.base_allocation) { + ret = nbd_co_send_block_status(client, request->handle, + blk_bs(exp->blk), request->from, + request->len, + !client->export_meta.bitmap, + NBD_META_ID_BASE_ALLOCATION, + errp); + if (ret < 0) { + return ret; + } + } + + if (client->export_meta.bitmap) { + ret = nbd_co_send_bitmap(client, request->handle, + client->exp->export_bitmap, + request->from, request->len, + request->flags & NBD_CMD_FLAG_REQ_ONE, + true, NBD_META_ID_DIRTY_BITMAP, errp); + if (ret < 0) { + return ret; + } + } + + return ret; } else { return nbd_send_generic_reply(client, request->handle, -EINVAL, "CMD_BLOCK_STATUS not negotiated", @@ -2207,3 +2398,44 @@ void nbd_client_new(NBDExport *exp, co = qemu_coroutine_create(nbd_co_client_start, client); qemu_coroutine_enter(co); } + +void nbd_export_bitmap(NBDExport *exp, const char *bitmap, + const char *bitmap_export_name, Error **errp) +{ + BdrvDirtyBitmap *bm = NULL; + BlockDriverState *bs = blk_bs(exp->blk); + + if (exp->export_bitmap) { + error_setg(errp, "Export bitmap is already set"); + return; + } + + while (true) { + bm = bdrv_find_dirty_bitmap(bs, bitmap); + if (bm != NULL || bs->backing == NULL) { + break; + } + + bs = bs->backing->bs; + } + + if (bm == NULL) { + error_setg(errp, "Bitmap '%s' is not found", bitmap); + return; + } + + if (bdrv_dirty_bitmap_enabled(bm)) { + error_setg(errp, "Bitmap '%s' is enabled", bitmap); + return; + } + + if (bdrv_dirty_bitmap_qmp_locked(bm)) { + error_setg(errp, "Bitmap '%s' is locked", bitmap); + return; + } + + bdrv_dirty_bitmap_set_qmp_locked(bm, true); + exp->export_bitmap = bm; + exp->export_bitmap_context = + g_strdup_printf("qemu:dirty-bitmap:%s", bitmap_export_name); +} diff --git a/nbd/trace-events b/nbd/trace-events index dee081e775..5e1d4afe8e 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -64,6 +64,7 @@ nbd_co_send_simple_reply(uint64_t handle, uint32_t error, const char *errname, i nbd_co_send_structured_done(uint64_t handle) "Send structured reply done: handle = %" PRIu64 nbd_co_send_structured_read(uint64_t handle, uint64_t offset, void *data, size_t size) "Send structured read data reply: handle = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %zu" nbd_co_send_structured_read_hole(uint64_t handle, uint64_t offset, size_t size) "Send structured read hole reply: handle = %" PRIu64 ", offset = %" PRIu64 ", len = %zu" +nbd_co_send_extents(uint64_t handle, unsigned int extents, uint32_t id, uint64_t length, int last) "Send block status reply: handle = %" PRIu64 ", extents = %u, context = %d (extents cover %" PRIu64 " bytes, last chunk = %d)" nbd_co_send_structured_error(uint64_t handle, int err, const char *errname, const char *msg) "Send structured error reply: handle = %" PRIu64 ", error = %d (%s), msg = '%s'" nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)" nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32 From 767f0c7d6cddedbc97ad700bd1e0229cc2ce5eb5 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Sat, 9 Jun 2018 18:17:57 +0300 Subject: [PATCH 1515/2380] qapi: new qmp command nbd-server-add-bitmap For now, the actual command ix x-nbd-server-add-bitmap, reflecting the fact that we are still working on libvirt code that proves the command works as needed, and also the fact that we may remove bitmap-export-name (and just require that the exported name be the bitmap name). Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180609151758.17343-6-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: make the command experimental by adding x- prefix] Signed-off-by: Eric Blake --- blockdev-nbd.c | 23 +++++++++++++++++++++++ qapi/block.json | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 65a84739ed..1ef11041a7 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -220,3 +220,26 @@ void qmp_nbd_server_stop(Error **errp) nbd_server_free(nbd_server); nbd_server = NULL; } + +void qmp_x_nbd_server_add_bitmap(const char *name, const char *bitmap, + bool has_bitmap_export_name, + const char *bitmap_export_name, + Error **errp) +{ + NBDExport *exp; + + if (!nbd_server) { + error_setg(errp, "NBD server not running"); + return; + } + + exp = nbd_export_find(name); + if (exp == NULL) { + error_setg(errp, "Export '%s' is not found", name); + return; + } + + nbd_export_bitmap(exp, bitmap, + has_bitmap_export_name ? bitmap_export_name : bitmap, + errp); +} diff --git a/qapi/block.json b/qapi/block.json index c694524002..ca807f176a 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -268,6 +268,29 @@ { 'command': 'nbd-server-remove', 'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} } +## +# @x-nbd-server-add-bitmap: +# +# Expose a dirty bitmap associated with the selected export. The bitmap search +# starts at the device attached to the export, and includes all backing files. +# The exported bitmap is then locked until the NBD export is removed. +# +# @name: Export name. +# +# @bitmap: Bitmap name to search for. +# +# @bitmap-export-name: How the bitmap will be seen by nbd clients +# (default @bitmap) +# +# Note: the client must use NBD_OPT_SET_META_CONTEXT with a query of +# "qemu:dirty-bitmap:NAME" (where NAME matches @bitmap-export-name) to access +# the exposed bitmap. +# +# Since: 3.0 +## + { 'command': 'x-nbd-server-add-bitmap', + 'data': {'name': 'str', 'bitmap': 'str', '*bitmap-export-name': 'str'} } + ## # @nbd-server-stop: # From 3229a835a3c574a8ebc605e007785c4e01c61623 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Sat, 9 Jun 2018 18:17:58 +0300 Subject: [PATCH 1516/2380] docs/interop: add nbd.txt Describe new metadata namespace: "qemu". Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180609151758.17343-7-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: grammar tweaks] Signed-off-by: Eric Blake --- MAINTAINERS | 1 + docs/interop/nbd.txt | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 docs/interop/nbd.txt diff --git a/MAINTAINERS b/MAINTAINERS index da91501c7a..efb17e6ac0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1972,6 +1972,7 @@ F: nbd/ F: include/block/nbd* F: qemu-nbd.* F: blockdev-nbd.c +F: docs/interop/nbd.txt T: git git://repo.or.cz/qemu/ericb.git nbd NFS diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt new file mode 100644 index 0000000000..77b5f45911 --- /dev/null +++ b/docs/interop/nbd.txt @@ -0,0 +1,38 @@ +Qemu supports the NBD protocol, and has an internal NBD client (see +block/nbd.c), an internal NBD server (see blockdev-nbd.c), and an +external NBD server tool (see qemu-nbd.c). The common code is placed +in nbd/*. + +The NBD protocol is specified here: +https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md + +The following paragraphs describe some specific properties of NBD +protocol realization in Qemu. + += Metadata namespaces = + +Qemu supports the "base:allocation" metadata context as defined in the +NBD protocol specification, and also defines an additional metadata +namespace "qemu". + + +== "qemu" namespace == + +The "qemu" namespace currently contains only one type of context, +related to exposing the contents of a dirty bitmap alongside the +associated disk contents. That context has the following form: + + qemu:dirty-bitmap: + +Each dirty-bitmap metadata context defines only one flag for extents +in reply for NBD_CMD_BLOCK_STATUS: + + bit 0: NBD_STATE_DIRTY, means that the extent is "dirty" + +For NBD_OPT_LIST_META_CONTEXT the following queries are supported +in addition to "qemu:dirty-bitmap:": + +* "qemu:" - returns list of all available metadata contexts in the + namespace. +* "qemu:dirty-bitmap:" - returns list of all available dirty-bitmap + metadata contexts. From bc37b06a5cde24fb24c2a2cc44dd86756034ba9d Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 13 Apr 2018 17:31:56 +0300 Subject: [PATCH 1517/2380] nbd/server: introduce NBD_CMD_CACHE Handle nbd CACHE command. Just do read, without sending read data back. Cache mechanism should be done by exported node driver chain. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180413143156.11409-1-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: fix two missing case labels in switch statements] Signed-off-by: Eric Blake --- include/block/nbd.h | 3 ++- nbd/common.c | 2 ++ nbd/server.c | 11 +++++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/block/nbd.h b/include/block/nbd.h index 8bb9606c39..daaeae61bf 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -135,6 +135,7 @@ typedef struct NBDExtent { #define NBD_FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */ #define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send WRITE_ZEROES */ #define NBD_FLAG_SEND_DF (1 << 7) /* Send DF (Do not Fragment) */ +#define NBD_FLAG_SEND_CACHE (1 << 8) /* Send CACHE (prefetch) */ /* New-style handshake (global) flags, sent from server to client, and control what will happen during handshake phase. */ @@ -195,7 +196,7 @@ enum { NBD_CMD_DISC = 2, NBD_CMD_FLUSH = 3, NBD_CMD_TRIM = 4, - /* 5 reserved for failed experiment NBD_CMD_CACHE */ + NBD_CMD_CACHE = 5, NBD_CMD_WRITE_ZEROES = 6, NBD_CMD_BLOCK_STATUS = 7, }; diff --git a/nbd/common.c b/nbd/common.c index 8c95c1d606..41f5ed8d9f 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -148,6 +148,8 @@ const char *nbd_cmd_lookup(uint16_t cmd) return "flush"; case NBD_CMD_TRIM: return "trim"; + case NBD_CMD_CACHE: + return "cache"; case NBD_CMD_WRITE_ZEROES: return "write zeroes"; case NBD_CMD_BLOCK_STATUS: diff --git a/nbd/server.c b/nbd/server.c index 2c2d62c636..274604609f 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1252,7 +1252,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) int ret; const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM | NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA | - NBD_FLAG_SEND_WRITE_ZEROES); + NBD_FLAG_SEND_WRITE_ZEROES | NBD_FLAG_SEND_CACHE); bool oldStyle; /* Old style negotiation header, no room for options @@ -2034,7 +2034,9 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, return -EIO; } - if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE) { + if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE || + request->type == NBD_CMD_CACHE) + { if (request->len > NBD_MAX_BUFFER_SIZE) { error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)", request->len, NBD_MAX_BUFFER_SIZE); @@ -2119,7 +2121,7 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, int ret; NBDExport *exp = client->exp; - assert(request->type == NBD_CMD_READ); + assert(request->type == NBD_CMD_READ || request->type == NBD_CMD_CACHE); /* XXX: NBD Protocol only documents use of FUA with WRITE */ if (request->flags & NBD_CMD_FLAG_FUA) { @@ -2138,7 +2140,7 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, ret = blk_pread(exp->blk, request->from + exp->dev_offset, data, request->len); - if (ret < 0) { + if (ret < 0 || request->type == NBD_CMD_CACHE) { return nbd_send_generic_reply(client, request->handle, ret, "reading from file failed", errp); } @@ -2171,6 +2173,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, switch (request->type) { case NBD_CMD_READ: + case NBD_CMD_CACHE: return nbd_do_cmd_read(client, request, data, errp); case NBD_CMD_WRITE: From d21bbcc655b4f611f3d9246d50dba0b6a404d784 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 21 Jun 2018 19:05:58 +1000 Subject: [PATCH 1518/2380] pseries: Update SLOF firmware image to qemu-slof-20180621 The changes are: 1. fixed broken_sc1; 2. added switching between boot consoles; 3. added PXE boot. The full list is: > lib/libnet/pxelinux: Fix two off-by-one bugs in the pxelinux.cfg parser > lib/libnet/pxelinux: Make the size handling for pxelinux_load_cfg more logical > libc: Add a simple implementation of an assert() function > libnet: Support UUID-based pxelinux.cfg file names > slof: Add a helper function to get the contents of a property in C code > libnet: Add support for DHCPv4 options 209 and 210 > libnet: Wire up pxelinux.cfg network booting > libnet: Add functions for downloading and parsing pxelinux.cfg files > libnet: Put code for determing TFTP error strings into a separate function > libc: Add the snprintf() function > libnet: Pass ip_version via struct filename_ip > resolve ihandle and xt handle in the input command (like for the output) > Fix output word > obp-tftp: Make sure to not overwrite paflof in memory > libnet: Get rid of unused huge_load and block_size parameters > libc: Check for NULL pointers in free() > libc: Implement strrchr() > libnet: Get rid of unnecessary (char *) casts > broken_sc1: check for H_PRIVILEGE > OF: Use new property "stdout-path" for boot console Signed-off-by: Alexey Kardashevskiy Signed-off-by: David Gibson --- pc-bios/README | 2 +- pc-bios/slof.bin | Bin 913880 -> 924840 bytes roms/SLOF | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pc-bios/README b/pc-bios/README index a843e1e8b1..99e15a737b 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -17,7 +17,7 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20171214. + built from git tag qemu-slof-20180621. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin index d46c83efb706e4664fec834234ba69d96ede3473..4e0e33f8297888ca8e143be784c30ce404f072cd 100644 GIT binary patch delta 231859 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 delta 221666 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 U&)nGEW5@N|C;#^jU!U~<0rFLY<^TWy diff --git a/roms/SLOF b/roms/SLOF index 2317427ce7..7d37babcfa 160000 --- a/roms/SLOF +++ b/roms/SLOF @@ -1 +1 @@ -Subproject commit 2317427ce76006723f7ae103a6998ab41dd79c68 +Subproject commit 7d37babcfa48a6eb08e726a8d13b745cb2eebe1c From 2309832afdaf8d6451ebc2e81bace8eb8ea41293 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 16 Mar 2018 19:19:13 +1100 Subject: [PATCH 1519/2380] spapr: Maximum (HPT) pagesize property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The way the POWER Hash Page Table (HPT) MMU is virtualized by KVM HV means that every page that the guest puts in the pagetables must be truly physically contiguous, not just GPA-contiguous. In effect this means that an HPT guest can't use any pagesizes greater than the host page size used to back its memory. At present we handle this by changing what we advertise to the guest based on the backing pagesizes. This is pretty bad, because it means the guest sees a different environment depending on what should be host configuration details. As a start on fixing this, we add a new capability parameter to the pseries machine type which gives the maximum allowed pagesizes for an HPT guest. For now we just create and validate the parameter without making it do anything. For backwards compatibility, on older machine types we set it to the max available page size for the host. For the 3.0 machine type, we fix it to 16, the intention being to only allow HPT pagesizes up to 64kiB by default in future. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/spapr.c | 12 +++++++++ hw/ppc/spapr_caps.c | 56 ++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/spapr.h | 4 ++- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 78186500e9..70b150b098 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -63,6 +63,7 @@ #include "hw/virtio/vhost-scsi-common.h" #include "exec/address-spaces.h" +#include "exec/ram_addr.h" #include "hw/usb.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -4015,6 +4016,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; + smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 16; /* 64kiB */ spapr_caps_add_properties(smc, &error_abort); } @@ -4103,8 +4105,18 @@ static void spapr_machine_2_12_instance_options(MachineState *machine) static void spapr_machine_2_12_class_options(MachineClass *mc) { + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + uint8_t mps; + spapr_machine_3_0_class_options(mc); SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_12); + + if (kvmppc_hpt_needs_host_contiguous_pages()) { + mps = ctz64(qemu_getrampagesize()); + } else { + mps = 34; /* allow everything up to 16GiB, i.e. everything */ + } + smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = mps; } DEFINE_SPAPR_MACHINE(2_12, "2.12", false); diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 68a4243efc..6cdc0c94e7 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -27,6 +27,7 @@ #include "qapi/visitor.h" #include "sysemu/hw_accel.h" #include "target/ppc/cpu.h" +#include "target/ppc/mmu-hash64.h" #include "cpu-models.h" #include "kvm_ppc.h" @@ -144,6 +145,42 @@ out: g_free(val); } +static void spapr_cap_get_pagesize(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + sPAPRCapabilityInfo *cap = opaque; + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + uint8_t val = spapr_get_cap(spapr, cap->index); + uint64_t pagesize = (1ULL << val); + + visit_type_size(v, name, &pagesize, errp); +} + +static void spapr_cap_set_pagesize(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + sPAPRCapabilityInfo *cap = opaque; + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + uint64_t pagesize; + uint8_t val; + Error *local_err = NULL; + + visit_type_size(v, name, &pagesize, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (!is_power_of_2(pagesize)) { + error_setg(errp, "cap-%s must be a power of 2", cap->name); + return; + } + + val = ctz64(pagesize); + spapr->cmd_line_caps[cap->index] = true; + spapr->eff.caps[cap->index] = val; +} + static void cap_htm_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { if (!val) { @@ -267,6 +304,16 @@ static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr, #define VALUE_DESC_TRISTATE " (broken, workaround, fixed)" +static void cap_hpt_maxpagesize_apply(sPAPRMachineState *spapr, + uint8_t val, Error **errp) +{ + if (val < 12) { + error_setg(errp, "Require at least 4kiB hpt-max-page-size"); + } else if (val < 16) { + warn_report("Many guests require at least 64kiB hpt-max-page-size"); + } +} + sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_HTM] = { .name = "htm", @@ -326,6 +373,15 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .possible = &cap_ibs_possible, .apply = cap_safe_indirect_branch_apply, }, + [SPAPR_CAP_HPT_MAXPAGESIZE] = { + .name = "hpt-max-page-size", + .description = "Maximum page size for Hash Page Table guests", + .index = SPAPR_CAP_HPT_MAXPAGESIZE, + .get = spapr_cap_get_pagesize, + .set = spapr_cap_set_pagesize, + .type = "int", + .apply = cap_hpt_maxpagesize_apply, + }, }; static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 8a9142244f..4bc9dbff96 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -66,8 +66,10 @@ typedef enum { #define SPAPR_CAP_SBBC 0x04 /* Indirect Branch Serialisation */ #define SPAPR_CAP_IBS 0x05 +/* HPT Maximum Page Size (encoded as a shift) */ +#define SPAPR_CAP_HPT_MAXPAGESIZE 0x06 /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_IBS + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_HPT_MAXPAGESIZE + 1) /* * Capability Values From 123eec655287e43e0e86154e8093a394aefa3958 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 18 Apr 2018 14:21:45 +1000 Subject: [PATCH 1520/2380] spapr: Use maximum page size capability to simplify memory backend checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The way we used to handle KVM allowable guest pagesizes for PAPR guests required some convoluted checking of memory attached to the guest. The allowable pagesizes advertised to the guest cpus depended on the memory which was attached at boot, but then we needed to ensure that any memory later hotplugged didn't change which pagesizes were allowed. Now that we have an explicit machine option to control the allowable maximum pagesize we can simplify this. We just check all memory backends against that declared pagesize. We check base and cold-plugged memory at reset time, and hotplugged memory at pre_plug() time. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/spapr.c | 17 +++++++---------- hw/ppc/spapr_caps.c | 21 +++++++++++++++++++++ include/hw/ppc/spapr.h | 3 +++ target/ppc/kvm.c | 14 -------------- target/ppc/kvm_ppc.h | 6 ------ 5 files changed, 31 insertions(+), 30 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 70b150b098..0d032a1ad0 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3192,11 +3192,13 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { const sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(hotplug_dev); + sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr; uint64_t size; - char *mem_dev; + Object *memdev; + hwaddr pagesize; if (!smc->dr_lmb_enabled) { error_setg(errp, "Memory hotplug not supported for this machine"); @@ -3215,15 +3217,10 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - mem_dev = object_property_get_str(OBJECT(dimm), PC_DIMM_MEMDEV_PROP, NULL); - if (mem_dev && !kvmppc_is_mem_backend_page_size_ok(mem_dev)) { - error_setg(errp, "Memory backend has bad page size. " - "Use 'memory-backend-file' with correct mem-path."); - goto out; - } - -out: - g_free(mem_dev); + memdev = object_property_get_link(OBJECT(dimm), PC_DIMM_MEMDEV_PROP, + &error_abort); + pagesize = host_memory_backend_pagesize(MEMORY_BACKEND(memdev)); + spapr_check_pagesize(spapr, pagesize, errp); } struct sPAPRDIMMState { diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 6cdc0c94e7..722b213d9a 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "sysemu/hw_accel.h" +#include "exec/ram_addr.h" #include "target/ppc/cpu.h" #include "target/ppc/mmu-hash64.h" #include "cpu-models.h" @@ -304,14 +305,34 @@ static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr, #define VALUE_DESC_TRISTATE " (broken, workaround, fixed)" +void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize, + Error **errp) +{ + hwaddr maxpagesize = (1ULL << spapr->eff.caps[SPAPR_CAP_HPT_MAXPAGESIZE]); + + if (!kvmppc_hpt_needs_host_contiguous_pages()) { + return; + } + + if (maxpagesize > pagesize) { + error_setg(errp, + "Can't support %"HWADDR_PRIu" kiB guest pages with %" + HWADDR_PRIu" kiB host pages with this KVM implementation", + maxpagesize >> 10, pagesize >> 10); + } +} + static void cap_hpt_maxpagesize_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { if (val < 12) { error_setg(errp, "Require at least 4kiB hpt-max-page-size"); + return; } else if (val < 16) { warn_report("Many guests require at least 64kiB hpt-max-page-size"); } + + spapr_check_pagesize(spapr, qemu_getrampagesize(), errp); } sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 4bc9dbff96..7e028164ba 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -806,4 +806,7 @@ void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu); void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp); int spapr_caps_post_migration(sPAPRMachineState *spapr); +void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize, + Error **errp); + #endif /* HW_SPAPR_H */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 50b5d01432..9cfbd388ad 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -500,26 +500,12 @@ static void kvm_fixup_page_sizes(PowerPCCPU *cpu) cpu->hash64_opts->flags &= ~PPC_HASH64_1TSEG; } } - -bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path) -{ - Object *mem_obj = object_resolve_path(obj_path, NULL); - long pagesize = host_memory_backend_pagesize(MEMORY_BACKEND(mem_obj)); - - return pagesize >= max_cpu_page_size; -} - #else /* defined (TARGET_PPC64) */ static inline void kvm_fixup_page_sizes(PowerPCCPU *cpu) { } -bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path) -{ - return true; -} - #endif /* !defined (TARGET_PPC64) */ unsigned long kvm_arch_vcpu_id(CPUState *cpu) diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index a7ddb8a5d6..443fca0a4e 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -71,7 +71,6 @@ int kvmppc_resize_hpt_commit(PowerPCCPU *cpu, target_ulong flags, int shift); bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu); bool kvmppc_hpt_needs_host_contiguous_pages(void); -bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path); #else @@ -228,11 +227,6 @@ static inline bool kvmppc_hpt_needs_host_contiguous_pages(void) return false; } -static inline bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path) -{ - return true; -} - static inline bool kvmppc_has_cap_spapr_vfio(void) { return false; From 27f00f0a108484a975dc075086f8461ff279e1b3 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 26 Mar 2018 15:01:22 +1100 Subject: [PATCH 1521/2380] target/ppc: Add ppc_hash64_filter_pagesizes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The paravirtualized PAPR platform sometimes needs to restrict the guest to using only some of the page sizes actually supported by the host's MMU. At the moment this is handled in KVM specific code, but for consistency we want to apply the same limitations to all accelerators. This makes a start on this by providing a helper function in the cpu code to allow platform code to remove some of the cpu's page size definitions via a caller supplied callback. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- target/ppc/mmu-hash64.c | 59 +++++++++++++++++++++++++++++++++++++++++ target/ppc/mmu-hash64.h | 3 +++ 2 files changed, 62 insertions(+) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index aa200cba4c..276d9015e7 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -1166,3 +1166,62 @@ const PPCHash64Options ppc_hash64_opts_POWER7 = { }, } }; + +void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu, + bool (*cb)(void *, uint32_t, uint32_t), + void *opaque) +{ + PPCHash64Options *opts = cpu->hash64_opts; + int i; + int n = 0; + bool ci_largepage = false; + + assert(opts); + + n = 0; + for (i = 0; i < ARRAY_SIZE(opts->sps); i++) { + PPCHash64SegmentPageSizes *sps = &opts->sps[i]; + int j; + int m = 0; + + assert(n <= i); + + if (!sps->page_shift) { + break; + } + + for (j = 0; j < ARRAY_SIZE(sps->enc); j++) { + PPCHash64PageSize *ps = &sps->enc[j]; + + assert(m <= j); + if (!ps->page_shift) { + break; + } + + if (cb(opaque, sps->page_shift, ps->page_shift)) { + if (ps->page_shift >= 16) { + ci_largepage = true; + } + sps->enc[m++] = *ps; + } + } + + /* Clear rest of the row */ + for (j = m; j < ARRAY_SIZE(sps->enc); j++) { + memset(&sps->enc[j], 0, sizeof(sps->enc[j])); + } + + if (m) { + n++; + } + } + + /* Clear the rest of the table */ + for (i = n; i < ARRAY_SIZE(opts->sps); i++) { + memset(&opts->sps[i], 0, sizeof(opts->sps[i])); + } + + if (!ci_largepage) { + opts->flags &= ~PPC_HASH64_CI_LARGEPAGE; + } +} diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 53dcec5b93..f11efc9cbc 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -20,6 +20,9 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val); void ppc_hash64_init(PowerPCCPU *cpu); void ppc_hash64_finalize(PowerPCCPU *cpu); +void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu, + bool (*cb)(void *, uint32_t, uint32_t), + void *opaque); #endif /* From 9dceda5fc34a5868012260ee7271c7a6f36cc1f4 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 16 Apr 2018 16:47:19 +1000 Subject: [PATCH 1522/2380] spapr: Limit available pagesizes to provide a consistent guest environment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM HV has some limitations (deriving from the hardware) that mean not all host-cpu supported pagesizes may be usable in the guest. At present this means that KVM guests and TCG guests may see different available page sizes even if they notionally have the same vcpu model. This is confusing and also prevents migration between TCG and KVM. This patch makes the environment consistent by always allowing the same set of pagesizes. Since we can't remove the KVM limitations, we do this by always applying the same limitations it has, even to TCG guests. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/spapr_caps.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 722b213d9a..62663ebdf5 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -335,6 +335,39 @@ static void cap_hpt_maxpagesize_apply(sPAPRMachineState *spapr, spapr_check_pagesize(spapr, qemu_getrampagesize(), errp); } +static bool spapr_pagesize_cb(void *opaque, uint32_t seg_pshift, + uint32_t pshift) +{ + unsigned maxshift = *((unsigned *)opaque); + + assert(pshift >= seg_pshift); + + /* Don't allow the guest to use pages bigger than the configured + * maximum size */ + if (pshift > maxshift) { + return false; + } + + /* For whatever reason, KVM doesn't allow multiple pagesizes + * within a segment, *except* for the case of 16M pages in a 4k or + * 64k segment. Always exclude other cases, so that TCG and KVM + * guests see a consistent environment */ + if ((pshift != seg_pshift) && (pshift != 24)) { + return false; + } + + return true; +} + +static void cap_hpt_maxpagesize_cpu_apply(sPAPRMachineState *spapr, + PowerPCCPU *cpu, + uint8_t val, Error **errp) +{ + unsigned maxshift = val; + + ppc_hash64_filter_pagesizes(cpu, spapr_pagesize_cb, &maxshift); +} + sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_HTM] = { .name = "htm", @@ -402,6 +435,7 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .set = spapr_cap_set_pagesize, .type = "int", .apply = cap_hpt_maxpagesize_apply, + .cpu_apply = cap_hpt_maxpagesize_cpu_apply, }, }; From e5ca28ecab5c69b7578e22391a66c97c3979ffd8 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 16 Apr 2018 16:19:52 +1000 Subject: [PATCH 1523/2380] spapr: Don't rewrite mmu capabilities in KVM mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently during KVM initialization on POWER, kvm_fixup_page_sizes() rewrites a bunch of information in the cpu state to reflect the capabilities of the host MMU and KVM. This overwrites the information that's already there reflecting how the TCG implementation of the MMU will operate. This means that we can get guest-visibly different behaviour between KVM and TCG (and between different KVM implementations). That's bad. It also prevents migration between KVM and TCG. The pseries machine type now has filtering of the pagesizes it allows the guest to use which means it can present a consistent model of the MMU across all accelerators. So, we can now replace kvm_fixup_page_sizes() with kvm_check_mmu() which merely verifies that the expected cpu model can be faithfully handled by KVM, rather than updating the cpu model to match KVM. We call kvm_check_mmu() from the spapr cpu reset code. This is a hack: conceptually it makes more sense where fixup_page_sizes() was - in the KVM cpu init path. However, doing that would require moving the platform's pagesize filtering much earlier, which would require a lot of work making further adjustments. There wouldn't be a lot of concrete point to doing that, since the only KVM implementation which has the awkward MMU restrictions is KVM HV, which can only work with an spapr guest anyway. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater --- hw/ppc/spapr_cpu_core.c | 2 + target/ppc/kvm.c | 135 ++++++++++++++++++++-------------------- target/ppc/kvm_ppc.h | 5 ++ 3 files changed, 73 insertions(+), 69 deletions(-) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index bfb94f650c..993759db47 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -78,6 +78,8 @@ static void spapr_cpu_reset(void *opaque) spapr_cpu->dtl_size = 0; spapr_caps_cpu_apply(SPAPR_MACHINE(qdev_get_machine()), cpu); + + kvm_check_mmu(cpu, &error_fatal); } void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 9cfbd388ad..4df4ff6cbf 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -419,93 +419,93 @@ bool kvmppc_hpt_needs_host_contiguous_pages(void) return !!(smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL); } -static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) +void kvm_check_mmu(PowerPCCPU *cpu, Error **errp) { - if (!kvmppc_hpt_needs_host_contiguous_pages()) { - return true; - } - - return (1ul << shift) <= rampgsize; -} - -static long max_cpu_page_size; - -static void kvm_fixup_page_sizes(PowerPCCPU *cpu) -{ - static struct kvm_ppc_smmu_info smmu_info; - static bool has_smmu_info; - CPUPPCState *env = &cpu->env; + struct kvm_ppc_smmu_info smmu_info; int iq, ik, jq, jk; - /* We only handle page sizes for 64-bit server guests for now */ - if (!(env->mmu_model & POWERPC_MMU_64)) { + /* For now, we only have anything to check on hash64 MMUs */ + if (!cpu->hash64_opts || !kvm_enabled()) { return; } - /* Collect MMU info from kernel if not already */ - if (!has_smmu_info) { - kvm_get_smmu_info(cpu, &smmu_info); - has_smmu_info = true; + kvm_get_smmu_info(cpu, &smmu_info); + + if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG) + && !(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) { + error_setg(errp, + "KVM does not support 1TiB segments which guest expects"); + return; } - if (!max_cpu_page_size) { - max_cpu_page_size = qemu_getrampagesize(); - } - - /* Convert to QEMU form */ - memset(cpu->hash64_opts->sps, 0, sizeof(*cpu->hash64_opts->sps)); - - /* If we have HV KVM, we need to forbid CI large pages if our - * host page size is smaller than 64K. - */ - if (kvmppc_hpt_needs_host_contiguous_pages()) { - if (getpagesize() >= 0x10000) { - cpu->hash64_opts->flags |= PPC_HASH64_CI_LARGEPAGE; - } else { - cpu->hash64_opts->flags &= ~PPC_HASH64_CI_LARGEPAGE; - } + if (smmu_info.slb_size < cpu->hash64_opts->slb_size) { + error_setg(errp, "KVM only supports %u SLB entries, but guest needs %u", + smmu_info.slb_size, cpu->hash64_opts->slb_size); + return; } /* - * XXX This loop should be an entry wide AND of the capabilities that - * the selected CPU has with the capabilities that KVM supports. + * Verify that every pagesize supported by the cpu model is + * supported by KVM with the same encodings */ - for (ik = iq = 0; ik < KVM_PPC_PAGE_SIZES_MAX_SZ; ik++) { + for (iq = 0; iq < ARRAY_SIZE(cpu->hash64_opts->sps); iq++) { PPCHash64SegmentPageSizes *qsps = &cpu->hash64_opts->sps[iq]; - struct kvm_ppc_one_seg_page_size *ksps = &smmu_info.sps[ik]; + struct kvm_ppc_one_seg_page_size *ksps; - if (!kvm_valid_page_size(smmu_info.flags, max_cpu_page_size, - ksps->page_shift)) { - continue; - } - qsps->page_shift = ksps->page_shift; - qsps->slb_enc = ksps->slb_enc; - for (jk = jq = 0; jk < KVM_PPC_PAGE_SIZES_MAX_SZ; jk++) { - if (!kvm_valid_page_size(smmu_info.flags, max_cpu_page_size, - ksps->enc[jk].page_shift)) { - continue; - } - qsps->enc[jq].page_shift = ksps->enc[jk].page_shift; - qsps->enc[jq].pte_enc = ksps->enc[jk].pte_enc; - if (++jq >= PPC_PAGE_SIZES_MAX_SZ) { + for (ik = 0; ik < ARRAY_SIZE(smmu_info.sps); ik++) { + if (qsps->page_shift == smmu_info.sps[ik].page_shift) { break; } } - if (++iq >= PPC_PAGE_SIZES_MAX_SZ) { - break; + if (ik >= ARRAY_SIZE(smmu_info.sps)) { + error_setg(errp, "KVM doesn't support for base page shift %u", + qsps->page_shift); + return; + } + + ksps = &smmu_info.sps[ik]; + if (ksps->slb_enc != qsps->slb_enc) { + error_setg(errp, +"KVM uses SLB encoding 0x%x for page shift %u, but guest expects 0x%x", + ksps->slb_enc, ksps->page_shift, qsps->slb_enc); + return; + } + + for (jq = 0; jq < ARRAY_SIZE(qsps->enc); jq++) { + for (jk = 0; jk < ARRAY_SIZE(ksps->enc); jk++) { + if (qsps->enc[jq].page_shift == ksps->enc[jk].page_shift) { + break; + } + } + + if (jk >= ARRAY_SIZE(ksps->enc)) { + error_setg(errp, "KVM doesn't support page shift %u/%u", + qsps->enc[jq].page_shift, qsps->page_shift); + return; + } + if (qsps->enc[jq].pte_enc != ksps->enc[jk].pte_enc) { + error_setg(errp, +"KVM uses PTE encoding 0x%x for page shift %u/%u, but guest expects 0x%x", + ksps->enc[jk].pte_enc, qsps->enc[jq].page_shift, + qsps->page_shift, qsps->enc[jq].pte_enc); + return; + } } } - cpu->hash64_opts->slb_size = smmu_info.slb_size; - if (!(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) { - cpu->hash64_opts->flags &= ~PPC_HASH64_1TSEG; + + if (ppc_hash64_has(cpu, PPC_HASH64_CI_LARGEPAGE)) { + /* Mostly what guest pagesizes we can use are related to the + * host pages used to map guest RAM, which is handled in the + * platform code. Cache-Inhibited largepages (64k) however are + * used for I/O, so if they're mapped to the host at all it + * will be a normal mapping, not a special hugepage one used + * for RAM. */ + if (getpagesize() < 0x10000) { + error_setg(errp, + "KVM can't supply 64kiB CI pages, which guest expects"); + } } } -#else /* defined (TARGET_PPC64) */ - -static inline void kvm_fixup_page_sizes(PowerPCCPU *cpu) -{ -} - #endif /* !defined (TARGET_PPC64) */ unsigned long kvm_arch_vcpu_id(CPUState *cpu) @@ -551,9 +551,6 @@ int kvm_arch_init_vcpu(CPUState *cs) CPUPPCState *cenv = &cpu->env; int ret; - /* Gather server mmu info from KVM and update the CPU state */ - kvm_fixup_page_sizes(cpu); - /* Synchronize sregs with kvm */ ret = kvm_arch_sync_sregs(cpu); if (ret) { diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 443fca0a4e..657582bb32 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -71,6 +71,7 @@ int kvmppc_resize_hpt_commit(PowerPCCPU *cpu, target_ulong flags, int shift); bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu); bool kvmppc_hpt_needs_host_contiguous_pages(void); +void kvm_check_mmu(PowerPCCPU *cpu, Error **errp); #else @@ -227,6 +228,10 @@ static inline bool kvmppc_hpt_needs_host_contiguous_pages(void) return false; } +static inline void kvm_check_mmu(PowerPCCPU *cpu, Error **errp) +{ +} + static inline bool kvmppc_has_cap_spapr_vfio(void) { return false; From d419890c0448281d0cf11d9384d9a30c8808f648 Mon Sep 17 00:00:00 2001 From: Amol Surati Date: Fri, 22 Jun 2018 13:28:34 +0100 Subject: [PATCH 1524/2380] hw/intc/arm_gicv3: fix an extra left-shift when reading IPRIORITYR When either GICD_IPRIORITYR or GICR_IPRIORITYR is read as a 32-bit register, the post left-shift operator in the for loop causes an extra shift after the least significant byte has been placed. The 32-bit value actually returned is therefore the expected value shifted left by 8 bits. Signed-off-by: Amol Surati Message-id: 20180614054857.26248-1-suratiamol@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_dist.c | 3 ++- hw/intc/arm_gicv3_redist.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index 93fe936862..53c55c5729 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -441,7 +441,8 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, int i, irq = offset - GICD_IPRIORITYR; uint32_t value = 0; - for (i = irq + 3; i >= irq; i--, value <<= 8) { + for (i = irq + 3; i >= irq; i--) { + value <<= 8; value |= gicd_read_ipriorityr(s, attrs, i); } *data = value; diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 8a8684d76e..3b0ba6de1a 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -192,7 +192,8 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset, int i, irq = offset - GICR_IPRIORITYR; uint32_t value = 0; - for (i = irq + 3; i >= irq; i--, value <<= 8) { + for (i = irq + 3; i >= irq; i--) { + value <<= 8; value |= gicr_read_ipriorityr(cs, attrs, i); } *data = value; From 8297cb13e407db8a96cc7ed6b6a6c318a150759a Mon Sep 17 00:00:00 2001 From: Julia Suvorova Date: Fri, 22 Jun 2018 13:28:34 +0100 Subject: [PATCH 1525/2380] target/arm: Minor cleanup for ARMv6-M 32-bit instructions The arrays were made static, "if" was simplified because V7M and V8M define V6 feature. Signed-off-by: Julia Suvorova Reviewed-by: Stefan Hajnoczi Message-id: 20180618214604.6777-1-jusual@mail.ru Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index f405c82fb2..b988d379e7 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -10095,18 +10095,18 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) !arm_dc_feature(s, ARM_FEATURE_V7)) { int i; bool found = false; - const uint32_t armv6m_insn[] = {0xf3808000 /* msr */, - 0xf3b08040 /* dsb */, - 0xf3b08050 /* dmb */, - 0xf3b08060 /* isb */, - 0xf3e08000 /* mrs */, - 0xf000d000 /* bl */}; - const uint32_t armv6m_mask[] = {0xffe0d000, - 0xfff0d0f0, - 0xfff0d0f0, - 0xfff0d0f0, - 0xffe0d000, - 0xf800d000}; + static const uint32_t armv6m_insn[] = {0xf3808000 /* msr */, + 0xf3b08040 /* dsb */, + 0xf3b08050 /* dmb */, + 0xf3b08060 /* isb */, + 0xf3e08000 /* mrs */, + 0xf000d000 /* bl */}; + static const uint32_t armv6m_mask[] = {0xffe0d000, + 0xfff0d0f0, + 0xfff0d0f0, + 0xfff0d0f0, + 0xffe0d000, + 0xf800d000}; for (i = 0; i < ARRAY_SIZE(armv6m_insn); i++) { if ((insn & armv6m_mask[i]) == armv6m_insn[i]) { @@ -11039,8 +11039,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) break; case 3: /* Special control operations. */ if (!arm_dc_feature(s, ARM_FEATURE_V7) && - !(arm_dc_feature(s, ARM_FEATURE_V6) && - arm_dc_feature(s, ARM_FEATURE_M))) { + !arm_dc_feature(s, ARM_FEATURE_M)) { goto illegal_op; } op = (insn >> 4) & 0xf; From 5ff9aaabdc6aec367ba139bf3f2ccacd918a495f Mon Sep 17 00:00:00 2001 From: Zheng Xiang Date: Fri, 22 Jun 2018 13:28:35 +0100 Subject: [PATCH 1526/2380] target-arm: fix a segmentation fault due to illegal memory access The elements of kvm_devices_head list are freed in kvm_arm_machine_init_done(), but we still access these illegal memory in kvm_arm_devlistener_del(). This will cause segment fault when booting guest with MALLOC_PERTURB_=1. Signed-off-by: Zheng Xiang Message-id: 20180619075821.9884-1-zhengxiang9@huawei.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/kvm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 98f5006323..5bf41e151c 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -256,6 +256,7 @@ static void kvm_arm_machine_init_done(Notifier *notifier, void *data) kvm_arm_set_device_addr(kd); } memory_region_unref(kd->mr); + QSLIST_REMOVE_HEAD(&kvm_devices_head, entries); g_free(kd); } memory_listener_unregister(&devlistener); From 77d361b13c19fdf881bff044a5bec99108cf2da2 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:35 +0100 Subject: [PATCH 1527/2380] linux-headers: Update to kernel mainline commit b357bf602 Update our kernel headers to mainline commit b357bf6023a948cf6a9472f07a1b0caac0e4f8e8 ("Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm") Signed-off-by: Eric Auger Message-id: 1529072910-16156-2-git-send-email-eric.auger@redhat.com [PMM: clarified commit message] Signed-off-by: Peter Maydell --- include/standard-headers/linux/pci_regs.h | 8 ++++++++ include/standard-headers/linux/virtio_gpu.h | 1 + include/standard-headers/linux/virtio_net.h | 3 +++ linux-headers/LICENSES/exceptions/Linux-syscall-note | 2 +- linux-headers/LICENSES/preferred/GPL-2.0 | 6 ++++++ linux-headers/asm-arm/kvm.h | 1 + linux-headers/asm-arm/unistd-common.h | 1 + linux-headers/asm-arm64/kvm.h | 1 + linux-headers/asm-generic/unistd.h | 4 +++- linux-headers/asm-powerpc/unistd.h | 1 + linux-headers/asm-x86/unistd_32.h | 2 ++ linux-headers/asm-x86/unistd_64.h | 2 ++ linux-headers/asm-x86/unistd_x32.h | 2 ++ linux-headers/linux/kvm.h | 5 +++-- linux-headers/linux/psp-sev.h | 12 ++++++++++++ 15 files changed, 47 insertions(+), 4 deletions(-) diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 103ba797a8..4da87e2ef8 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -506,6 +506,8 @@ #define PCI_EXP_DEVCTL_READRQ_256B 0x1000 /* 256 Bytes */ #define PCI_EXP_DEVCTL_READRQ_512B 0x2000 /* 512 Bytes */ #define PCI_EXP_DEVCTL_READRQ_1024B 0x3000 /* 1024 Bytes */ +#define PCI_EXP_DEVCTL_READRQ_2048B 0x4000 /* 2048 Bytes */ +#define PCI_EXP_DEVCTL_READRQ_4096B 0x5000 /* 4096 Bytes */ #define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ #define PCI_EXP_DEVSTA 10 /* Device Status */ #define PCI_EXP_DEVSTA_CED 0x0001 /* Correctable Error Detected */ @@ -655,6 +657,11 @@ #define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ +#define PCI_EXP_LNKCTL2_TLS 0x000f +#define PCI_EXP_LNKCTL2_TLS_2_5GT 0x0001 /* Supported Speed 2.5GT/s */ +#define PCI_EXP_LNKCTL2_TLS_5_0GT 0x0002 /* Supported Speed 5GT/s */ +#define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Supported Speed 8GT/s */ +#define PCI_EXP_LNKCTL2_TLS_16_0GT 0x0004 /* Supported Speed 16GT/s */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */ #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ @@ -981,6 +988,7 @@ #define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */ #define PCI_EXP_DPC_CTL 6 /* DPC control */ +#define PCI_EXP_DPC_CTL_EN_FATAL 0x0001 /* Enable trigger on ERR_FATAL message */ #define PCI_EXP_DPC_CTL_EN_NONFATAL 0x0002 /* Enable trigger on ERR_NONFATAL message */ #define PCI_EXP_DPC_CTL_INT_EN 0x0008 /* DPC Interrupt Enable */ diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard-headers/linux/virtio_gpu.h index c1c8f0751d..52a830dcf8 100644 --- a/include/standard-headers/linux/virtio_gpu.h +++ b/include/standard-headers/linux/virtio_gpu.h @@ -260,6 +260,7 @@ struct virtio_gpu_cmd_submit { }; #define VIRTIO_GPU_CAPSET_VIRGL 1 +#define VIRTIO_GPU_CAPSET_VIRGL2 2 /* VIRTIO_GPU_CMD_GET_CAPSET_INFO */ struct virtio_gpu_get_capset_info { diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h index e9f255ea3f..260c3681d7 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -57,6 +57,9 @@ * Steering */ #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ +#define VIRTIO_NET_F_STANDBY 62 /* Act as standby for another device + * with the same MAC. + */ #define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */ #ifndef VIRTIO_NET_NO_LEGACY diff --git a/linux-headers/LICENSES/exceptions/Linux-syscall-note b/linux-headers/LICENSES/exceptions/Linux-syscall-note index 6b60b61be4..9abdad71fa 100644 --- a/linux-headers/LICENSES/exceptions/Linux-syscall-note +++ b/linux-headers/LICENSES/exceptions/Linux-syscall-note @@ -1,6 +1,6 @@ SPDX-Exception-Identifier: Linux-syscall-note SPDX-URL: https://spdx.org/licenses/Linux-syscall-note.html -SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-1.0+, LGPL-2.0, LGPL-2.0+, LGPL-2.1, LGPL-2.1+ +SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-1.0+, LGPL-2.0, LGPL-2.0+, LGPL-2.1, LGPL-2.1+, GPL-2.0-only, GPL-2.0-or-later Usage-Guide: This exception is used together with one of the above SPDX-Licenses to mark user space API (uapi) header files so they can be included diff --git a/linux-headers/LICENSES/preferred/GPL-2.0 b/linux-headers/LICENSES/preferred/GPL-2.0 index b8db91d3a1..ff0812fd89 100644 --- a/linux-headers/LICENSES/preferred/GPL-2.0 +++ b/linux-headers/LICENSES/preferred/GPL-2.0 @@ -1,5 +1,7 @@ Valid-License-Identifier: GPL-2.0 +Valid-License-Identifier: GPL-2.0-only Valid-License-Identifier: GPL-2.0+ +Valid-License-Identifier: GPL-2.0-or-later SPDX-URL: https://spdx.org/licenses/GPL-2.0.html Usage-Guide: To use this license in source code, put one of the following SPDX @@ -7,8 +9,12 @@ Usage-Guide: guidelines in the licensing rules documentation. For 'GNU General Public License (GPL) version 2 only' use: SPDX-License-Identifier: GPL-2.0 + or + SPDX-License-Identifier: GPL-2.0-only For 'GNU General Public License (GPL) version 2 or any later version' use: SPDX-License-Identifier: GPL-2.0+ + or + SPDX-License-Identifier: GPL-2.0-or-later License-Text: GNU GENERAL PUBLIC LICENSE diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index 670b43c9e9..72aa226e6c 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -91,6 +91,7 @@ struct kvm_regs { #define KVM_VGIC_V3_ADDR_TYPE_DIST 2 #define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 #define KVM_VGIC_ITS_ADDR_TYPE 4 +#define KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 5 #define KVM_VGIC_V3_DIST_SIZE SZ_64K #define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) diff --git a/linux-headers/asm-arm/unistd-common.h b/linux-headers/asm-arm/unistd-common.h index 8d5ceaee1a..60c2d931d0 100644 --- a/linux-headers/asm-arm/unistd-common.h +++ b/linux-headers/asm-arm/unistd-common.h @@ -354,5 +354,6 @@ #define __NR_pkey_alloc (__NR_SYSCALL_BASE + 395) #define __NR_pkey_free (__NR_SYSCALL_BASE + 396) #define __NR_statx (__NR_SYSCALL_BASE + 397) +#define __NR_rseq (__NR_SYSCALL_BASE + 398) #endif /* _ASM_ARM_UNISTD_COMMON_H */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 17315aba6a..99cb9ad14a 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -91,6 +91,7 @@ struct kvm_regs { #define KVM_VGIC_V3_ADDR_TYPE_DIST 2 #define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 #define KVM_VGIC_ITS_ADDR_TYPE 4 +#define KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 5 #define KVM_VGIC_V3_DIST_SIZE SZ_64K #define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index 8bcb186c6f..42990676a5 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -732,9 +732,11 @@ __SYSCALL(__NR_pkey_alloc, sys_pkey_alloc) __SYSCALL(__NR_pkey_free, sys_pkey_free) #define __NR_statx 291 __SYSCALL(__NR_statx, sys_statx) +#define __NR_io_pgetevents 292 +__SC_COMP(__NR_io_pgetevents, sys_io_pgetevents, compat_sys_io_pgetevents) #undef __NR_syscalls -#define __NR_syscalls 292 +#define __NR_syscalls 293 /* * 32 bit systems traditionally used different diff --git a/linux-headers/asm-powerpc/unistd.h b/linux-headers/asm-powerpc/unistd.h index 0c08edcfcd..3629858142 100644 --- a/linux-headers/asm-powerpc/unistd.h +++ b/linux-headers/asm-powerpc/unistd.h @@ -398,5 +398,6 @@ #define __NR_pkey_alloc 384 #define __NR_pkey_free 385 #define __NR_pkey_mprotect 386 +#define __NR_rseq 387 #endif /* _ASM_POWERPC_UNISTD_H_ */ diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index 8a206df454..c1b30a0cf4 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -382,5 +382,7 @@ #define __NR_pkey_free 382 #define __NR_statx 383 #define __NR_arch_prctl 384 +#define __NR_io_pgetevents 385 +#define __NR_rseq 386 #endif /* _ASM_X86_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index 336c2e4aaa..c2e464c115 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -334,5 +334,7 @@ #define __NR_pkey_alloc 330 #define __NR_pkey_free 331 #define __NR_statx 332 +#define __NR_io_pgetevents 333 +#define __NR_rseq 334 #endif /* _ASM_X86_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index cb98a52998..37229021f0 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -287,6 +287,8 @@ #define __NR_pkey_alloc (__X32_SYSCALL_BIT + 330) #define __NR_pkey_free (__X32_SYSCALL_BIT + 331) #define __NR_statx (__X32_SYSCALL_BIT + 332) +#define __NR_io_pgetevents (__X32_SYSCALL_BIT + 333) +#define __NR_rseq (__X32_SYSCALL_BIT + 334) #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) #define __NR_ioctl (__X32_SYSCALL_BIT + 514) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index cdb148e959..98f389a5a3 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -677,10 +677,10 @@ struct kvm_ioeventfd { }; #define KVM_X86_DISABLE_EXITS_MWAIT (1 << 0) -#define KVM_X86_DISABLE_EXITS_HTL (1 << 1) +#define KVM_X86_DISABLE_EXITS_HLT (1 << 1) #define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2) #define KVM_X86_DISABLE_VALID_EXITS (KVM_X86_DISABLE_EXITS_MWAIT | \ - KVM_X86_DISABLE_EXITS_HTL | \ + KVM_X86_DISABLE_EXITS_HLT | \ KVM_X86_DISABLE_EXITS_PAUSE) /* for KVM_ENABLE_CAP */ @@ -948,6 +948,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_BPB 152 #define KVM_CAP_GET_MSR_FEATURES 153 #define KVM_CAP_HYPERV_EVENTFD 154 +#define KVM_CAP_HYPERV_TLBFLUSH 155 #ifdef KVM_CAP_IRQ_ROUTING diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h index 33e247471a..b7b933ffaa 100644 --- a/linux-headers/linux/psp-sev.h +++ b/linux-headers/linux/psp-sev.h @@ -30,6 +30,7 @@ enum { SEV_PDH_GEN, SEV_PDH_CERT_EXPORT, SEV_PEK_CERT_IMPORT, + SEV_GET_ID, SEV_MAX, }; @@ -123,6 +124,17 @@ struct sev_user_data_pdh_cert_export { __u32 cert_chain_len; /* In/Out */ } __attribute__((packed)); +/** + * struct sev_user_data_get_id - GET_ID command parameters + * + * @socket1: Buffer to pass unique ID of first socket + * @socket2: Buffer to pass unique ID of second socket + */ +struct sev_user_data_get_id { + __u8 socket1[64]; /* Out */ + __u8 socket2[64]; /* Out */ +} __attribute__((packed)); + /** * struct sev_issue_cmd - SEV ioctl parameters * From 19d1bd0b586a537805c6fe3b590f4ca8ec7a4912 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:35 +0100 Subject: [PATCH 1528/2380] target/arm: Allow KVM device address overwriting for KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION attribute, the attribute data pointed to by kvm_device_attr.addr is a OR of the redistributor region address and other fields such as the index of the redistributor region and the number of redistributors the region can contain. The existing machine init done notifier framework sets the address field to the actual address of the device and does not allow to OR this value with other fields. This patch extends the KVMDevice struct with a new kda_addr_ormask member. Its value is passed at registration time and OR'ed with the resolved address on kvm_arm_set_device_addr(). Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Message-id: 1529072910-16156-3-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/intc/arm_gic_kvm.c | 4 ++-- hw/intc/arm_gicv3_its_kvm.c | 2 +- hw/intc/arm_gicv3_kvm.c | 4 ++-- target/arm/kvm.c | 10 +++++++++- target/arm/kvm_arm.h | 3 ++- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 204369d0e2..86665080bd 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -558,7 +558,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) | KVM_VGIC_V2_ADDR_TYPE_DIST, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_DIST, - s->dev_fd); + s->dev_fd, 0); /* CPU interface for current core. Unlike arm_gic, we don't * provide the "interface for core #N" memory regions, because * cores with a VGIC don't have those. @@ -568,7 +568,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) | KVM_VGIC_V2_ADDR_TYPE_CPU, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_CPU, - s->dev_fd); + s->dev_fd, 0); if (kvm_has_gsi_routing()) { /* set up irq routing */ diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index eea6a73df2..271ebe461c 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -103,7 +103,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp) /* register the base address */ kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd); + KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd, 0); gicv3_its_init_mmio(s, NULL); diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index d8d3b25403..95491df72a 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -807,9 +807,9 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort); kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd); + KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd, 0); kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd); + KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd, 0); if (kvm_has_gsi_routing()) { /* set up irq routing */ diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 5bf41e151c..65f867d569 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -184,10 +184,15 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu) * We use a MemoryListener to track mapping and unmapping of * the regions during board creation, so the board models don't * need to do anything special for the KVM case. + * + * Sometimes the address must be OR'ed with some other fields + * (for example for KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION). + * @kda_addr_ormask aims at storing the value of those fields. */ typedef struct KVMDevice { struct kvm_arm_device_addr kda; struct kvm_device_attr kdattr; + uint64_t kda_addr_ormask; MemoryRegion *mr; QSLIST_ENTRY(KVMDevice) entries; int dev_fd; @@ -234,6 +239,8 @@ static void kvm_arm_set_device_addr(KVMDevice *kd) */ if (kd->dev_fd >= 0) { uint64_t addr = kd->kda.addr; + + addr |= kd->kda_addr_ormask; attr->addr = (uintptr_t)&addr; ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr); } else { @@ -267,7 +274,7 @@ static Notifier notify = { }; void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, - uint64_t attr, int dev_fd) + uint64_t attr, int dev_fd, uint64_t addr_ormask) { KVMDevice *kd; @@ -287,6 +294,7 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, kd->kdattr.group = group; kd->kdattr.attr = attr; kd->dev_fd = dev_fd; + kd->kda_addr_ormask = addr_ormask; QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries); memory_region_ref(kd->mr); } diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 1e2364007d..863f205822 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -34,6 +34,7 @@ int kvm_arm_vcpu_init(CPUState *cs); * @group: device control API group for setting addresses * @attr: device control API address type * @dev_fd: device control device file descriptor (or -1 if not supported) + * @addr_ormask: value to be OR'ed with resolved address * * Remember the memory region @mr, and when it is mapped by the * machine model, tell the kernel that base address using the @@ -45,7 +46,7 @@ int kvm_arm_vcpu_init(CPUState *cs); * address at the point where machine init is complete. */ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, - uint64_t attr, int dev_fd); + uint64_t attr, int dev_fd, uint64_t addr_ormask); /** * kvm_arm_init_cpreg_list: From 1e575b66643a4311b9a6cbf0744f7f5aeba5e181 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:36 +0100 Subject: [PATCH 1529/2380] hw/intc/arm_gicv3: Introduce redist-region-count array property To prepare for multiple redistributor regions, we introduce an array of uint32_t properties that stores the redistributor count of each redistributor region. Non accelerated VGICv3 only supports a single redistributor region. The capacity of all redist regions is checked against the number of vcpus. Machvirt is updated to set those properties, ie. a single redistributor region with count set to the number of vcpus capped by 123. Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Message-id: 1529072910-16156-4-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 11 ++++++++- hw/intc/arm_gicv3.c | 12 +++++++++- hw/intc/arm_gicv3_common.c | 38 ++++++++++++++++++++++++++---- hw/intc/arm_gicv3_kvm.c | 9 +++++-- include/hw/intc/arm_gicv3_common.h | 8 +++++-- 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 98b99cf236..de189c7296 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -523,6 +523,15 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) if (!kvm_irqchip_in_kernel()) { qdev_prop_set_bit(gicdev, "has-security-extensions", vms->secure); } + + if (type == 3) { + uint32_t redist0_capacity = + vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); + + qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1); + qdev_prop_set_uint32(gicdev, "redist-region-count[0]", redist0_count); + } qdev_init_nofail(gicdev); gicbusdev = SYS_BUS_DEVICE(gicdev); sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); @@ -1322,7 +1331,7 @@ static void machvirt_init(MachineState *machine) * many redistributors we can fit into the memory map. */ if (vms->gic_version == 3) { - virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / 0x20000; + virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; } else { virt_max_cpus = GIC_NCPU; } diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 479c66733c..7044133e2d 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -373,7 +373,17 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) return; } - gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops); + if (s->nb_redist_regions != 1) { + error_setg(errp, "VGICv3 redist region number(%d) not equal to 1", + s->nb_redist_regions); + return; + } + + gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } gicv3_init_cpuif(s); } diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 864b7c6515..ff326b374a 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -247,11 +247,22 @@ static const VMStateDescription vmstate_gicv3 = { }; void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, - const MemoryRegionOps *ops) + const MemoryRegionOps *ops, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(s); + int rdist_capacity = 0; int i; + for (i = 0; i < s->nb_redist_regions; i++) { + rdist_capacity += s->redist_region_count[i]; + } + if (rdist_capacity < s->num_cpu) { + error_setg(errp, "Capacity of the redist regions(%d) " + "is less than number of vcpus(%d)", + rdist_capacity, s->num_cpu); + return; + } + /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. * GPIO array layout is thus: * [0..N-1] spi @@ -277,11 +288,18 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s, "gicv3_dist", 0x10000); - memory_region_init_io(&s->iomem_redist, OBJECT(s), ops ? &ops[1] : NULL, s, - "gicv3_redist", 0x20000 * s->num_cpu); - sysbus_init_mmio(sbd, &s->iomem_dist); - sysbus_init_mmio(sbd, &s->iomem_redist); + + s->iomem_redist = g_new0(MemoryRegion, s->nb_redist_regions); + for (i = 0; i < s->nb_redist_regions; i++) { + char *name = g_strdup_printf("gicv3_redist_region[%d]", i); + + memory_region_init_io(&s->iomem_redist[i], OBJECT(s), + ops ? &ops[1] : NULL, s, name, + s->redist_region_count[i] * GICV3_REDIST_SIZE); + sysbus_init_mmio(sbd, &s->iomem_redist[i]); + g_free(name); + } } static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) @@ -363,6 +381,13 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) } } +static void arm_gicv3_finalize(Object *obj) +{ + GICv3State *s = ARM_GICV3_COMMON(obj); + + g_free(s->redist_region_count); +} + static void arm_gicv3_common_reset(DeviceState *dev) { GICv3State *s = ARM_GICV3_COMMON(dev); @@ -467,6 +492,8 @@ static Property arm_gicv3_common_properties[] = { DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32), DEFINE_PROP_UINT32("revision", GICv3State, revision, 3), DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0), + DEFINE_PROP_ARRAY("redist-region-count", GICv3State, nb_redist_regions, + redist_region_count, qdev_prop_uint32, uint32_t), DEFINE_PROP_END_OF_LIST(), }; @@ -488,6 +515,7 @@ static const TypeInfo arm_gicv3_common_type = { .instance_size = sizeof(GICv3State), .class_size = sizeof(ARMGICv3CommonClass), .class_init = arm_gicv3_common_class_init, + .instance_finalize = arm_gicv3_finalize, .abstract = true, .interfaces = (InterfaceInfo []) { { TYPE_ARM_LINUX_BOOT_IF }, diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 95491df72a..2751687264 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -784,7 +784,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } - gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); + gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } for (i = 0; i < s->num_cpu; i++) { ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i)); @@ -808,7 +812,8 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd, 0); - kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, + kvm_arm_register_device(&s->iomem_redist[0], -1, + KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd, 0); if (kvm_has_gsi_routing()) { diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index d75b49d558..b798486ecf 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -35,6 +35,8 @@ #define GICV3_MAXIRQ 1020 #define GICV3_MAXSPI (GICV3_MAXIRQ - GIC_INTERNAL) +#define GICV3_REDIST_SIZE 0x20000 + /* Number of SGI target-list bits */ #define GICV3_TARGETLIST_BITS 16 @@ -210,7 +212,9 @@ struct GICv3State { /*< public >*/ MemoryRegion iomem_dist; /* Distributor */ - MemoryRegion iomem_redist; /* Redistributors */ + MemoryRegion *iomem_redist; /* Redistributor Regions */ + uint32_t *redist_region_count; /* redistributor count within each region */ + uint32_t nb_redist_regions; /* number of redist regions */ uint32_t num_cpu; uint32_t num_irq; @@ -292,6 +296,6 @@ typedef struct ARMGICv3CommonClass { } ARMGICv3CommonClass; void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, - const MemoryRegionOps *ops); + const MemoryRegionOps *ops, Error **errp); #endif From 80d673338959c813770bd5b903e3061db58b1999 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:36 +0100 Subject: [PATCH 1530/2380] hw/intc/arm_gicv3_kvm: Get prepared to handle multiple redist regions Let's check if KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION is supported. If not, we check the number of redist region is equal to 1 and use the legacy KVM_VGIC_V3_ADDR_TYPE_REDIST attribute. Otherwise we use the new attribute and allow to register multiple regions to the KVM device. Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Reviewed-by: Andrew Jones Message-id: 1529072910-16156-5-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 2751687264..1e11200fe2 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -767,6 +767,7 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) { GICv3State *s = KVM_ARM_GICV3(dev); KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s); + bool multiple_redist_region_allowed; Error *local_err = NULL; int i; @@ -803,6 +804,18 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } + multiple_redist_region_allowed = + kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION); + + if (!multiple_redist_region_allowed && s->nb_redist_regions > 1) { + error_setg(errp, "Multiple VGICv3 redistributor regions are not " + "supported by this host kernel"); + error_append_hint(errp, "A maximum of %d VCPUs can be used", + s->redist_region_count[0]); + return; + } + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &s->num_irq, true, &error_abort); @@ -812,9 +825,27 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd, 0); - kvm_arm_register_device(&s->iomem_redist[0], -1, - KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd, 0); + + if (!multiple_redist_region_allowed) { + kvm_arm_register_device(&s->iomem_redist[0], -1, + KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd, 0); + } else { + /* we register regions in reverse order as "devices" are inserted at + * the head of a QSLIST and the list is then popped from the head + * onwards by kvm_arm_machine_init_done() + */ + for (i = s->nb_redist_regions - 1; i >= 0; i--) { + /* Address mask made of the rdist region index and count */ + uint64_t addr_ormask = + i | ((uint64_t)s->redist_region_count[i] << 52); + + kvm_arm_register_device(&s->iomem_redist[i], -1, + KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, + s->dev_fd, addr_ormask); + } + } if (kvm_has_gsi_routing()) { /* set up irq routing */ From f90747c4e8fb689a8cea9c104ed6c13bd8e5086d Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:36 +0100 Subject: [PATCH 1531/2380] hw/arm/virt: GICv3 DT node with one or two redistributor regions This patch allows the creation of a GICv3 node with 1 or 2 redistributor regions depending on the number of smu_cpus. The second redistributor region is located just after the existing RAM region, at 256GB and contains up to up to 512 vcpus. Please refer to kernel documentation for further node details: Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Message-id: 1529072910-16156-6-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 29 ++++++++++++++++++++++++----- include/hw/arm/virt.h | 14 ++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index de189c7296..e5e7606946 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -149,6 +149,8 @@ static const MemMapEntry a15memmap[] = { [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, [VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES }, + /* Additional 64 MB redist region (can contain up to 512 redistributors) */ + [VIRT_GIC_REDIST2] = { 0x4000000000ULL, 0x4000000 }, /* Second PCIe window, 512GB wide at the 512GB boundary */ [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL }, }; @@ -402,13 +404,30 @@ static void fdt_add_gic_node(VirtMachineState *vms) qemu_fdt_setprop_cell(vms->fdt, "/intc", "#size-cells", 0x2); qemu_fdt_setprop(vms->fdt, "/intc", "ranges", NULL, 0); if (vms->gic_version == 3) { + int nb_redist_regions = virt_gicv3_redist_region_count(vms); + qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible", "arm,gic-v3"); - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", - 2, vms->memmap[VIRT_GIC_DIST].base, - 2, vms->memmap[VIRT_GIC_DIST].size, - 2, vms->memmap[VIRT_GIC_REDIST].base, - 2, vms->memmap[VIRT_GIC_REDIST].size); + + qemu_fdt_setprop_cell(vms->fdt, "/intc", + "#redistributor-regions", nb_redist_regions); + + if (nb_redist_regions == 1) { + qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_REDIST].base, + 2, vms->memmap[VIRT_GIC_REDIST].size); + } else { + qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_REDIST].base, + 2, vms->memmap[VIRT_GIC_REDIST].size, + 2, vms->memmap[VIRT_GIC_REDIST2].base, + 2, vms->memmap[VIRT_GIC_REDIST2].size); + } + if (vms->virt) { qemu_fdt_setprop_cells(vms->fdt, "/intc", "interrupts", GIC_FDT_IRQ_TYPE_PPI, ARCH_GICV3_MAINT_IRQ, diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 4ac7ef6a37..308156f0cd 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -35,6 +35,8 @@ #include "qemu/notify.h" #include "hw/boards.h" #include "hw/arm/arm.h" +#include "sysemu/kvm.h" +#include "hw/intc/arm_gicv3_common.h" #define NUM_GICV2M_SPIS 64 #define NUM_VIRTIO_TRANSPORTS 32 @@ -60,6 +62,7 @@ enum { VIRT_GIC_V2M, VIRT_GIC_ITS, VIRT_GIC_REDIST, + VIRT_GIC_REDIST2, VIRT_SMMU, VIRT_UART, VIRT_MMIO, @@ -130,4 +133,15 @@ typedef struct { void virt_acpi_setup(VirtMachineState *vms); +/* Return the number of used redistributor regions */ +static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) +{ + uint32_t redist0_capacity = + vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + + assert(vms->gic_version == 3); + + return vms->smp_cpus > redist0_capacity ? 2 : 1; +} + #endif /* QEMU_ARM_VIRT_H */ From a1de312f5690a1a4f3ff37319045f6ec7bdfcd31 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:36 +0100 Subject: [PATCH 1532/2380] hw/arm/virt-acpi-build: Advertise one or two GICR structures Depending on the number of smp_cpus we now register one or two GICR structures. Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Message-id: 1529072910-16156-7-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 74f5744e87..eefd1d48f7 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -670,6 +670,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (vms->gic_version == 3) { AcpiMadtGenericTranslator *gic_its; + int nb_redist_regions = virt_gicv3_redist_region_count(vms); AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data, sizeof *gicr); @@ -678,6 +679,14 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base); gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size); + if (nb_redist_regions == 2) { + gicr = acpi_data_push(table_data, sizeof(*gicr)); + gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR; + gicr->length = sizeof(*gicr); + gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST2].base); + gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST2].size); + } + if (its_class_name() && !vmc->no_its) { gic_its = acpi_data_push(table_data, sizeof *gic_its); gic_its->type = ACPI_APIC_GENERIC_TRANSLATOR; From 03d72fa13cef2454aeaedea387a2e505c77481b4 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:37 +0100 Subject: [PATCH 1533/2380] hw/arm/virt: Register two redistributor regions when necessary With a VGICv3 KVM device, if the number of vcpus exceeds the capacity of the legacy redistributor region (123 redistributors), we now attempt to register a second redistributor region. Up to 512 redistributors can fit in this latter on top of the 123 allowed by the legacy redistributor region. Registering this second redistributor region is possible if the host kernel supports the following VGICv3 KVM device group/attribute: KVM_DEV_ARM_VGIC_GRP_ADDR/KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION. In case the host kernel does not support the registration of several redistributor regions and the requested number of vcpus exceeds the capacity of the legacy redistributor region, the GICv3 device initialization fails with a proper error message and qemu exits. At the moment the max number of vcpus still is capped by the virt machine class max_cpus. Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Message-id: 1529072910-16156-8-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index e5e7606946..9b9bc5091e 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -529,6 +529,7 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) SysBusDevice *gicbusdev; const char *gictype; int type = vms->gic_version, i; + uint32_t nb_redist_regions = 0; gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); @@ -548,14 +549,28 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); - qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1); + nb_redist_regions = virt_gicv3_redist_region_count(vms); + + qdev_prop_set_uint32(gicdev, "len-redist-region-count", + nb_redist_regions); qdev_prop_set_uint32(gicdev, "redist-region-count[0]", redist0_count); + + if (nb_redist_regions == 2) { + uint32_t redist1_capacity = + vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE; + + qdev_prop_set_uint32(gicdev, "redist-region-count[1]", + MIN(smp_cpus - redist0_count, redist1_capacity)); + } } qdev_init_nofail(gicdev); gicbusdev = SYS_BUS_DEVICE(gicdev); sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); if (type == 3) { sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base); + if (nb_redist_regions == 2) { + sysbus_mmio_map(gicbusdev, 2, vms->memmap[VIRT_GIC_REDIST2].base); + } } else { sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_CPU].base); } @@ -1351,6 +1366,7 @@ static void machvirt_init(MachineState *machine) */ if (vms->gic_version == 3) { virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + virt_max_cpus += vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE; } else { virt_max_cpus = GIC_NCPU; } From 601d626d148a6e50b4a0b5ae38f98682c5bf5e2a Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:37 +0100 Subject: [PATCH 1534/2380] hw/arm/virt: Add a new 256MB ECAM region This patch defines a new ECAM region located after the 256GB limit. The virt machine state is augmented with a new highmem_ecam field which guards the usage of this new ECAM region instead of the legacy 16MB one. With the highmem ECAM region, up to 256 PCIe buses can be used. Signed-off-by: Eric Auger Reviewed-by: Laszlo Ersek Reviewed-by: Andrew Jones Message-id: 1529072910-16156-9-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 21 +++++++++++++-------- hw/arm/virt.c | 12 ++++++++---- include/hw/arm/virt.h | 4 ++++ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index eefd1d48f7..6ea47e2588 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -150,16 +150,17 @@ static void acpi_dsdt_add_virtio(Aml *scope, } static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, - uint32_t irq, bool use_highmem) + uint32_t irq, bool use_highmem, bool highmem_ecam) { + int ecam_id = VIRT_ECAM_ID(highmem_ecam); Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf; int i, bus_no; hwaddr base_mmio = memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = memmap[VIRT_PCIE_MMIO].size; hwaddr base_pio = memmap[VIRT_PCIE_PIO].base; hwaddr size_pio = memmap[VIRT_PCIE_PIO].size; - hwaddr base_ecam = memmap[VIRT_PCIE_ECAM].base; - hwaddr size_ecam = memmap[VIRT_PCIE_ECAM].size; + hwaddr base_ecam = memmap[ecam_id].base; + hwaddr size_ecam = memmap[ecam_id].size; int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; Aml *dev = aml_device("%s", "PCI0"); @@ -173,7 +174,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_append(dev, aml_name_decl("_CCA", aml_int(1))); /* Declare the PCI Routing Table. */ - Aml *rt_pkg = aml_package(nr_pcie_buses * PCI_NUM_PINS); + Aml *rt_pkg = aml_varpackage(nr_pcie_buses * PCI_NUM_PINS); for (bus_no = 0; bus_no < nr_pcie_buses; bus_no++) { for (i = 0; i < PCI_NUM_PINS; i++) { int gsi = (i + bus_no) % PCI_NUM_PINS; @@ -316,7 +317,10 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, Aml *dev_res0 = aml_device("%s", "RES0"); aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(base_ecam, size_ecam, AML_READ_WRITE)); + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, base_ecam, + base_ecam + size_ecam - 1, 0x0000, size_ecam)); aml_append(dev_res0, aml_name_decl("_CRS", crs)); aml_append(dev, dev_res0); aml_append(scope, dev); @@ -573,16 +577,17 @@ build_mcfg(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { AcpiTableMcfg *mcfg; const MemMapEntry *memmap = vms->memmap; + int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam); int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]); int mcfg_start = table_data->len; mcfg = acpi_data_push(table_data, len); - mcfg->allocation[0].address = cpu_to_le64(memmap[VIRT_PCIE_ECAM].base); + mcfg->allocation[0].address = cpu_to_le64(memmap[ecam_id].base); /* Only a single allocation so no need to play with segments */ mcfg->allocation[0].pci_segment = cpu_to_le16(0); mcfg->allocation[0].start_bus_number = 0; - mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size + mcfg->allocation[0].end_bus_number = (memmap[ecam_id].size / PCIE_MMCFG_SIZE_MIN) - 1; build_header(linker, table_data, (void *)(table_data->data + mcfg_start), @@ -766,7 +771,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO], (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS); acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE), - vms->highmem); + vms->highmem, vms->highmem_ecam); acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO], (irqmap[VIRT_GPIO] + ARM_SPI_BASE)); acpi_dsdt_add_power_button(scope); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 9b9bc5091e..933e60612d 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -151,6 +151,7 @@ static const MemMapEntry a15memmap[] = { [VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES }, /* Additional 64 MB redist region (can contain up to 512 redistributors) */ [VIRT_GIC_REDIST2] = { 0x4000000000ULL, 0x4000000 }, + [VIRT_PCIE_ECAM_HIGH] = { 0x4010000000ULL, 0x10000000 }, /* Second PCIe window, 512GB wide at the 512GB boundary */ [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL }, }; @@ -1044,10 +1045,9 @@ static void create_pcie(VirtMachineState *vms, qemu_irq *pic) hwaddr size_mmio_high = vms->memmap[VIRT_PCIE_MMIO_HIGH].size; hwaddr base_pio = vms->memmap[VIRT_PCIE_PIO].base; hwaddr size_pio = vms->memmap[VIRT_PCIE_PIO].size; - hwaddr base_ecam = vms->memmap[VIRT_PCIE_ECAM].base; - hwaddr size_ecam = vms->memmap[VIRT_PCIE_ECAM].size; + hwaddr base_ecam, size_ecam; hwaddr base = base_mmio; - int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; + int nr_pcie_buses; int irq = vms->irqmap[VIRT_PCIE]; MemoryRegion *mmio_alias; MemoryRegion *mmio_reg; @@ -1055,12 +1055,16 @@ static void create_pcie(VirtMachineState *vms, qemu_irq *pic) MemoryRegion *ecam_reg; DeviceState *dev; char *nodename; - int i; + int i, ecam_id; PCIHostState *pci; dev = qdev_create(NULL, TYPE_GPEX_HOST); qdev_init_nofail(dev); + ecam_id = VIRT_ECAM_ID(vms->highmem_ecam); + base_ecam = vms->memmap[ecam_id].base; + size_ecam = vms->memmap[ecam_id].size; + nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; /* Map only the first size_ecam bytes of ECAM space */ ecam_alias = g_new0(MemoryRegion, 1); ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 308156f0cd..085fdcc287 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -72,6 +72,7 @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PCIE_ECAM, + VIRT_PCIE_ECAM_HIGH, VIRT_PLATFORM_BUS, VIRT_PCIE_MMIO_HIGH, VIRT_GPIO, @@ -106,6 +107,7 @@ typedef struct { FWCfgState *fw_cfg; bool secure; bool highmem; + bool highmem_ecam; bool its; bool virt; int32_t gic_version; @@ -123,6 +125,8 @@ typedef struct { int psci_conduit; } VirtMachineState; +#define VIRT_ECAM_ID(high) (high ? VIRT_PCIE_ECAM_HIGH : VIRT_PCIE_ECAM) + #define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt") #define VIRT_MACHINE(obj) \ OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE) From 8ae9a1ca5b9bb0b3be06d51914ffba7c20dd62f8 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:37 +0100 Subject: [PATCH 1535/2380] hw/arm/virt: Add virt-3.0 machine type Add virt-3.0 machine type. Signed-off-by: Eric Auger Reviewed-by: Laszlo Ersek Reviewed-by: Andrew Jones Message-id: 1529072910-16156-10-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 933e60612d..d8abf89e8c 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1745,7 +1745,7 @@ type_init(machvirt_machine_init); #define VIRT_COMPAT_2_12 \ HW_COMPAT_2_12 -static void virt_2_12_instance_init(Object *obj) +static void virt_3_0_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); @@ -1813,11 +1813,22 @@ static void virt_2_12_instance_init(Object *obj) vms->irqmap = a15irqmap; } +static void virt_machine_3_0_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(3, 0) + +static void virt_2_12_instance_init(Object *obj) +{ + virt_3_0_instance_init(obj); +} + static void virt_machine_2_12_options(MachineClass *mc) { + virt_machine_3_0_options(mc); SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12); } -DEFINE_VIRT_MACHINE_AS_LATEST(2, 12) +DEFINE_VIRT_MACHINE(2, 12) #define VIRT_COMPAT_2_11 \ HW_COMPAT_2_11 From 17ec075a651a3f9613429c2d97018fce459ed943 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:37 +0100 Subject: [PATCH 1536/2380] hw/arm/virt: Use 256MB ECAM region by default With this patch, virt-3.0 machine uses a new 256MB ECAM region by default instead of the legacy 16MB one, if highmem is set (LPAE supported by the guest) and (!firmware_loaded || aarch64). Indeed aarch32 mode FW may not support this high ECAM region. Signed-off-by: Eric Auger Reviewed-by: Laszlo Ersek Reviewed-by: Andrew Jones Message-id: 1529072910-16156-11-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 10 ++++++++++ include/hw/arm/virt.h | 1 + 2 files changed, 11 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index d8abf89e8c..0f8bfa57d7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1318,6 +1318,7 @@ static void machvirt_init(MachineState *machine) int n, virt_max_cpus; MemoryRegion *ram = g_new(MemoryRegion, 1); bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); + bool aarch64 = true; /* We can probe only here because during property set * KVM is not available yet @@ -1433,6 +1434,8 @@ static void machvirt_init(MachineState *machine) numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj), &error_fatal); + aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL); + if (!vms->secure) { object_property_set_bool(cpuobj, false, "has_el3", NULL); } @@ -1491,6 +1494,8 @@ static void machvirt_init(MachineState *machine) create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hd(1)); } + vms->highmem_ecam &= vms->highmem && (!firmware_loaded || aarch64); + create_rtc(vms, pic); create_pcie(vms, pic); @@ -1788,6 +1793,8 @@ static void virt_3_0_instance_init(Object *obj) "Set GIC version. " "Valid values are 2, 3 and host", NULL); + vms->highmem_ecam = !vmc->no_highmem_ecam; + if (vmc->no_its) { vms->its = false; } else { @@ -1825,8 +1832,11 @@ static void virt_2_12_instance_init(Object *obj) static void virt_machine_2_12_options(MachineClass *mc) { + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + virt_machine_3_0_options(mc); SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12); + vmc->no_highmem_ecam = true; } DEFINE_VIRT_MACHINE(2, 12) diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 085fdcc287..9a870ccb6a 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -98,6 +98,7 @@ typedef struct { bool no_pmu; bool claim_edge_triggered_timers; bool smbios_old_sys_ver; + bool no_highmem_ecam; } VirtMachineClass; typedef struct { From b10fbd5363e58a9996ad9af0f8f456d89770b0a9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:38 +0100 Subject: [PATCH 1537/2380] hw/arm/virt: Increase max_cpus to 512 virt 3.0 now allows up to 512 vcpus whereas for earlier machine types, max_cpus was set to 255 and any attempt to start the machine with vcpus > 255 was rejected at a very early stage, in vl.c/main level. 512 is the max supported by KVM. Anyway the actual vcpu count that can be achieved depends on other parameters such as the acceleration mode, the vgic version, the host kernel version. Those are discovered later on. Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Message-id: 1529072910-16156-12-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 0f8bfa57d7..742f68afca 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1706,11 +1706,11 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); mc->init = machvirt_init; - /* Start max_cpus at the maximum QEMU supports. We'll further restrict - * it later in machvirt_init, where we have more information about the + /* Start with max_cpus set to 512, which is the maximum supported by KVM. + * The value may be reduced later when we have more information about the * configuration of the particular instance. */ - mc->max_cpus = 255; + mc->max_cpus = 512; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_CALXEDA_XGMAC); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); @@ -1837,6 +1837,7 @@ static void virt_machine_2_12_options(MachineClass *mc) virt_machine_3_0_options(mc); SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12); vmc->no_highmem_ecam = true; + mc->max_cpus = 255; } DEFINE_VIRT_MACHINE(2, 12) From ebac5458c7517ed7b8ee06eb90beacc7472b295d Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 22 Jun 2018 13:28:38 +0100 Subject: [PATCH 1538/2380] target-arm: Add the Cortex-R5F MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the Cortex-R5F with the optional FPU enabled. Reviewed-by: KONRAD Frederic Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias Message-id: 20180529124707.3025-2-edgar.iglesias@gmail.com Signed-off-by: Peter Maydell --- target/arm/cpu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index e1de45e904..81c1d22b14 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1361,6 +1361,14 @@ static void cortex_r5_initfn(Object *obj) define_arm_cp_regs(cpu, cortexr5_cp_reginfo); } +static void cortex_r5f_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cortex_r5_initfn(obj); + set_feature(&cpu->env, ARM_FEATURE_VFP3); +} + static const ARMCPRegInfo cortexa8_cp_reginfo[] = { { .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -1821,6 +1829,7 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "cortex-m33", .initfn = cortex_m33_initfn, .class_init = arm_v7m_class_init }, { .name = "cortex-r5", .initfn = cortex_r5_initfn }, + { .name = "cortex-r5f", .initfn = cortex_r5f_initfn }, { .name = "cortex-a7", .initfn = cortex_a7_initfn }, { .name = "cortex-a8", .initfn = cortex_a8_initfn }, { .name = "cortex-a9", .initfn = cortex_a9_initfn }, From eb24d4d38e0e177b2ef8189a9930eeca9277f097 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 22 Jun 2018 13:28:38 +0100 Subject: [PATCH 1539/2380] xlnx-zynqmp: Swap Cortex-R5 for Cortex-R5F MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ZynqMP has Cortex-R5Fs with the optional FPU enabled. Reviewed-by: KONRAD Frederic Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias Message-id: 20180529124707.3025-3-edgar.iglesias@gmail.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zcu102.c | 2 +- hw/arm/xlnx-zynqmp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index f26fd8eb91..b6bc6a93b8 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -208,7 +208,7 @@ static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "Xilinx ZynqMP ZCU102 board with 4xA53s and 2xR5s based on " \ + mc->desc = "Xilinx ZynqMP ZCU102 board with 4xA53s and 2xR5Fs based on " \ "the value of smp"; mc->init = xlnx_zcu102_init; mc->block_default_type = IF_IDE; diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 2045b9d71e..29df35fb75 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -134,7 +134,7 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu, char *name; object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]), - "cortex-r5-" TYPE_ARM_CPU); + "cortex-r5f-" TYPE_ARM_CPU); object_property_add_child(OBJECT(s), "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]), &error_abort); From 344f4b1581f3d629954a1623736677827a0af750 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:39 +0100 Subject: [PATCH 1540/2380] hw/misc/tz-mpc.c: Implement the Arm TrustZone Memory Protection Controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the Arm TrustZone Memory Protection Controller, which sits in front of RAM and allows secure software to configure it to either pass through or reject transactions. We implement the MPC as a QEMU IOMMU, which will direct transactions either through to the devices and memory behind it or to a special "never works" AddressSpace if they are blocked. This initial commit implements the skeleton of the device: * it always permits accesses * it doesn't implement most of the registers * it doesn't implement the interrupt or other behaviour for blocked transactions Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Eric Auger Message-id: 20180620132032.28865-2-peter.maydell@linaro.org --- MAINTAINERS | 2 + default-configs/arm-softmmu.mak | 1 + hw/misc/Makefile.objs | 1 + hw/misc/trace-events | 7 + hw/misc/tz-mpc.c | 399 ++++++++++++++++++++++++++++++++ include/hw/misc/tz-mpc.h | 70 ++++++ 6 files changed, 480 insertions(+) create mode 100644 hw/misc/tz-mpc.c create mode 100644 include/hw/misc/tz-mpc.h diff --git a/MAINTAINERS b/MAINTAINERS index f222bf8b16..b8b4e60c06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -457,6 +457,8 @@ F: hw/char/cmsdk-apb-uart.c F: include/hw/char/cmsdk-apb-uart.h F: hw/misc/tz-ppc.c F: include/hw/misc/tz-ppc.h +F: hw/misc/tz-mpc.c +F: include/hw/misc/tz-mpc.h ARM cores M: Peter Maydell diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 7cf73d2f27..834d45cfaf 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -108,6 +108,7 @@ CONFIG_CMSDK_APB_UART=y CONFIG_MPS2_FPGAIO=y CONFIG_MPS2_SCC=y +CONFIG_TZ_MPC=y CONFIG_TZ_PPC=y CONFIG_IOTKIT=y CONFIG_IOTKIT_SECCTL=y diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index ecd8d61098..9350900845 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -62,6 +62,7 @@ obj-$(CONFIG_MIPS_ITU) += mips_itu.o obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o obj-$(CONFIG_MPS2_SCC) += mps2-scc.o +obj-$(CONFIG_TZ_MPC) += tz-mpc.o obj-$(CONFIG_TZ_PPC) += tz-ppc.o obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o diff --git a/hw/misc/trace-events b/hw/misc/trace-events index ec5a9f0da1..72bf9d5700 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -84,6 +84,13 @@ mos6522_set_sr_int(void) "set sr_int" mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x" +# hw/misc/tz-mpc.c +tz_mpc_reg_read(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs read: offset 0x%x data 0x%" PRIx64 " size %u" +tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs write: offset 0x%x data 0x%" PRIx64 " size %u" +tz_mpc_mem_blocked_read(uint64_t addr, unsigned size, bool secure) "TZ MPC blocked read: offset 0x%" PRIx64 " size %u secure %d" +tz_mpc_mem_blocked_write(uint64_t addr, uint64_t data, unsigned size, bool secure) "TZ MPC blocked write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" +tz_mpc_translate(uint64_t addr, int flags, const char *idx, const char *res) "TZ MPC translate: addr 0x%" PRIx64 " flags 0x%x iommu_idx %s: %s" + # hw/misc/tz-ppc.c tz_ppc_reset(void) "TZ PPC: reset" tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d" diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c new file mode 100644 index 0000000000..8a8617bdbd --- /dev/null +++ b/hw/misc/tz-mpc.c @@ -0,0 +1,399 @@ +/* + * ARM AHB5 TrustZone Memory Protection Controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/tz-mpc.h" + +/* Our IOMMU has two IOMMU indexes, one for secure transactions and one for + * non-secure transactions. + */ +enum { + IOMMU_IDX_S, + IOMMU_IDX_NS, + IOMMU_NUM_INDEXES, +}; + +/* Config registers */ +REG32(CTRL, 0x00) +REG32(BLK_MAX, 0x10) +REG32(BLK_CFG, 0x14) +REG32(BLK_IDX, 0x18) +REG32(BLK_LUT, 0x1c) +REG32(INT_STAT, 0x20) +REG32(INT_CLEAR, 0x24) +REG32(INT_EN, 0x28) +REG32(INT_INFO1, 0x2c) +REG32(INT_INFO2, 0x30) +REG32(INT_SET, 0x34) +REG32(PIDR4, 0xfd0) +REG32(PIDR5, 0xfd4) +REG32(PIDR6, 0xfd8) +REG32(PIDR7, 0xfdc) +REG32(PIDR0, 0xfe0) +REG32(PIDR1, 0xfe4) +REG32(PIDR2, 0xfe8) +REG32(PIDR3, 0xfec) +REG32(CIDR0, 0xff0) +REG32(CIDR1, 0xff4) +REG32(CIDR2, 0xff8) +REG32(CIDR3, 0xffc) + +static const uint8_t tz_mpc_idregs[] = { + 0x04, 0x00, 0x00, 0x00, + 0x60, 0xb8, 0x1b, 0x00, + 0x0d, 0xf0, 0x05, 0xb1, +}; + +static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr, + uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + uint64_t r; + uint32_t offset = addr & ~0x3; + + if (!attrs.secure && offset < A_PIDR4) { + /* NS accesses can only see the ID registers */ + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register read: NS access to offset 0x%x\n", + offset); + r = 0; + goto read_out; + } + + switch (offset) { + case A_PIDR4: + case A_PIDR5: + case A_PIDR6: + case A_PIDR7: + case A_PIDR0: + case A_PIDR1: + case A_PIDR2: + case A_PIDR3: + case A_CIDR0: + case A_CIDR1: + case A_CIDR2: + case A_CIDR3: + r = tz_mpc_idregs[(offset - A_PIDR4) / 4]; + break; + case A_INT_CLEAR: + case A_INT_SET: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register read: write-only offset 0x%x\n", + offset); + r = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register read: bad offset 0x%x\n", offset); + r = 0; + break; + } + + if (size != 4) { + /* None of our registers are read-sensitive (except BLK_LUT, + * which can special case the "size not 4" case), so just + * pull the right bytes out of the word read result. + */ + r = extract32(r, (addr & 3) * 8, size * 8); + } + +read_out: + trace_tz_mpc_reg_read(addr, r, size); + *pdata = r; + return MEMTX_OK; +} + +static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, + uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + uint32_t offset = addr & ~0x3; + + trace_tz_mpc_reg_write(addr, value, size); + + if (!attrs.secure && offset < A_PIDR4) { + /* NS accesses can only see the ID registers */ + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register write: NS access to offset 0x%x\n", + offset); + return MEMTX_OK; + } + + if (size != 4) { + /* Expand the byte or halfword write to a full word size. + * In most cases we can do this with zeroes; the exceptions + * are CTRL, BLK_IDX and BLK_LUT. + */ + uint32_t oldval; + + switch (offset) { + /* As we add support for registers which need expansions + * other than zeroes we'll fill in cases here. + */ + default: + oldval = 0; + break; + } + value = deposit32(oldval, (addr & 3) * 8, size * 8, value); + } + + switch (offset) { + case A_PIDR4: + case A_PIDR5: + case A_PIDR6: + case A_PIDR7: + case A_PIDR0: + case A_PIDR1: + case A_PIDR2: + case A_PIDR3: + case A_CIDR0: + case A_CIDR1: + case A_CIDR2: + case A_CIDR3: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register write: read-only offset 0x%x\n", offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register write: bad offset 0x%x\n", offset); + break; + } + + return MEMTX_OK; +} + +static const MemoryRegionOps tz_mpc_reg_ops = { + .read_with_attrs = tz_mpc_reg_read, + .write_with_attrs = tz_mpc_reg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 4, +}; + +/* Accesses only reach these read and write functions if the MPC is + * blocking them; non-blocked accesses go directly to the downstream + * memory region without passing through this code. + */ +static MemTxResult tz_mpc_mem_blocked_read(void *opaque, hwaddr addr, + uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + trace_tz_mpc_mem_blocked_read(addr, size, attrs.secure); + + *pdata = 0; + return MEMTX_OK; +} + +static MemTxResult tz_mpc_mem_blocked_write(void *opaque, hwaddr addr, + uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + trace_tz_mpc_mem_blocked_write(addr, value, size, attrs.secure); + + return MEMTX_OK; +} + +static const MemoryRegionOps tz_mpc_mem_blocked_ops = { + .read_with_attrs = tz_mpc_mem_blocked_read, + .write_with_attrs = tz_mpc_mem_blocked_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .impl.min_access_size = 1, + .impl.max_access_size = 8, +}; + +static IOMMUTLBEntry tz_mpc_translate(IOMMUMemoryRegion *iommu, + hwaddr addr, IOMMUAccessFlags flags, + int iommu_idx) +{ + TZMPC *s = TZ_MPC(container_of(iommu, TZMPC, upstream)); + bool ok; + + IOMMUTLBEntry ret = { + .iova = addr & ~(s->blocksize - 1), + .translated_addr = addr & ~(s->blocksize - 1), + .addr_mask = s->blocksize - 1, + .perm = IOMMU_RW, + }; + + /* Look at the per-block configuration for this address, and + * return a TLB entry directing the transaction at either + * downstream_as or blocked_io_as, as appropriate. + * For the moment, always permit accesses. + */ + ok = true; + + trace_tz_mpc_translate(addr, flags, + iommu_idx == IOMMU_IDX_S ? "S" : "NS", + ok ? "pass" : "block"); + + ret.target_as = ok ? &s->downstream_as : &s->blocked_io_as; + return ret; +} + +static int tz_mpc_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs) +{ + /* We treat unspecified attributes like secure. Transactions with + * unspecified attributes come from places like + * cpu_physical_memory_write_rom() for initial image load, and we want + * those to pass through the from-reset "everything is secure" config. + * All the real during-emulation transactions from the CPU will + * specify attributes. + */ + return (attrs.unspecified || attrs.secure) ? IOMMU_IDX_S : IOMMU_IDX_NS; +} + +static int tz_mpc_num_indexes(IOMMUMemoryRegion *iommu) +{ + return IOMMU_NUM_INDEXES; +} + +static void tz_mpc_reset(DeviceState *dev) +{ +} + +static void tz_mpc_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + TZMPC *s = TZ_MPC(obj); + + qdev_init_gpio_out_named(dev, &s->irq, "irq", 1); +} + +static void tz_mpc_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + TZMPC *s = TZ_MPC(dev); + uint64_t size; + + /* We can't create the upstream end of the port until realize, + * as we don't know the size of the MR used as the downstream until then. + * We insist on having a downstream, to avoid complicating the code + * with handling the "don't know how big this is" case. It's easy + * enough for the user to create an unimplemented_device as downstream + * if they have nothing else to plug into this. + */ + if (!s->downstream) { + error_setg(errp, "MPC 'downstream' link not set"); + return; + } + + size = memory_region_size(s->downstream); + + memory_region_init_iommu(&s->upstream, sizeof(s->upstream), + TYPE_TZ_MPC_IOMMU_MEMORY_REGION, + obj, "tz-mpc-upstream", size); + + /* In real hardware the block size is configurable. In QEMU we could + * make it configurable but will need it to be at least as big as the + * target page size so we can execute out of the resulting MRs. Guest + * software is supposed to check the block size using the BLK_CFG + * register, so make it fixed at the page size. + */ + s->blocksize = memory_region_iommu_get_min_page_size(&s->upstream); + if (size % s->blocksize != 0) { + error_setg(errp, + "MPC 'downstream' size %" PRId64 + " is not a multiple of %" HWADDR_PRIx " bytes", + size, s->blocksize); + object_unref(OBJECT(&s->upstream)); + return; + } + + /* BLK_MAX is the max value of BLK_IDX, which indexes an array of 32-bit + * words, each bit of which indicates one block. + */ + s->blk_max = DIV_ROUND_UP(size / s->blocksize, 32); + + memory_region_init_io(&s->regmr, obj, &tz_mpc_reg_ops, + s, "tz-mpc-regs", 0x1000); + sysbus_init_mmio(sbd, &s->regmr); + + sysbus_init_mmio(sbd, MEMORY_REGION(&s->upstream)); + + /* This memory region is not exposed to users of this device as a + * sysbus MMIO region, but is instead used internally as something + * that our IOMMU translate function might direct accesses to. + */ + memory_region_init_io(&s->blocked_io, obj, &tz_mpc_mem_blocked_ops, + s, "tz-mpc-blocked-io", size); + + address_space_init(&s->downstream_as, s->downstream, + "tz-mpc-downstream"); + address_space_init(&s->blocked_io_as, &s->blocked_io, + "tz-mpc-blocked-io"); +} + +static const VMStateDescription tz_mpc_vmstate = { + .name = "tz-mpc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + +static Property tz_mpc_properties[] = { + DEFINE_PROP_LINK("downstream", TZMPC, downstream, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tz_mpc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = tz_mpc_realize; + dc->vmsd = &tz_mpc_vmstate; + dc->reset = tz_mpc_reset; + dc->props = tz_mpc_properties; +} + +static const TypeInfo tz_mpc_info = { + .name = TYPE_TZ_MPC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TZMPC), + .instance_init = tz_mpc_init, + .class_init = tz_mpc_class_init, +}; + +static void tz_mpc_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = tz_mpc_translate; + imrc->attrs_to_index = tz_mpc_attrs_to_index; + imrc->num_indexes = tz_mpc_num_indexes; +} + +static const TypeInfo tz_mpc_iommu_memory_region_info = { + .name = TYPE_TZ_MPC_IOMMU_MEMORY_REGION, + .parent = TYPE_IOMMU_MEMORY_REGION, + .class_init = tz_mpc_iommu_memory_region_class_init, +}; + +static void tz_mpc_register_types(void) +{ + type_register_static(&tz_mpc_info); + type_register_static(&tz_mpc_iommu_memory_region_info); +} + +type_init(tz_mpc_register_types); diff --git a/include/hw/misc/tz-mpc.h b/include/hw/misc/tz-mpc.h new file mode 100644 index 0000000000..d1a65fd9a3 --- /dev/null +++ b/include/hw/misc/tz-mpc.h @@ -0,0 +1,70 @@ +/* + * ARM AHB5 TrustZone Memory Protection Controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the TrustZone memory protection controller (MPC). + * It is documented in the ARM CoreLink SIE-200 System IP for Embedded TRM + * (DDI 0571G): + * https://developer.arm.com/products/architecture/m-profile/docs/ddi0571/g + * + * The MPC sits in front of memory and allows secure software to + * configure it to either pass through or reject transactions. + * Rejected transactions may be configured to either be aborted, or to + * behave as RAZ/WI. An interrupt can be signalled for a rejected transaction. + * + * The MPC has a register interface which the guest uses to configure it. + * + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion for the MPC's config registers + * + sysbus MMIO region 1: MemoryRegion for the upstream end of the MPC + * + Property "downstream": MemoryRegion defining the downstream memory + * + Named GPIO output "irq": set for a transaction-failed interrupt + */ + +#ifndef TZ_MPC_H +#define TZ_MPC_H + +#include "hw/sysbus.h" + +#define TYPE_TZ_MPC "tz-mpc" +#define TZ_MPC(obj) OBJECT_CHECK(TZMPC, (obj), TYPE_TZ_MPC) + +#define TZ_NUM_PORTS 16 + +#define TYPE_TZ_MPC_IOMMU_MEMORY_REGION "tz-mpc-iommu-memory-region" + +typedef struct TZMPC TZMPC; + +struct TZMPC { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + + qemu_irq irq; + + /* Properties */ + MemoryRegion *downstream; + + hwaddr blocksize; + uint32_t blk_max; + + /* MemoryRegions exposed to user */ + MemoryRegion regmr; + IOMMUMemoryRegion upstream; + + /* MemoryRegion used internally */ + MemoryRegion blocked_io; + + AddressSpace downstream_as; + AddressSpace blocked_io_as; +}; + +#endif From cdb6099818a78e80b307432f6f9c143452135c59 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:39 +0100 Subject: [PATCH 1541/2380] hw/misc/tz-mpc.c: Implement registers Implement the missing registers for the TZ MPC. Signed-off-by: Peter Maydell Reviewed-by: Eric Auger Message-id: 20180620132032.28865-3-peter.maydell@linaro.org --- hw/misc/tz-mpc.c | 140 ++++++++++++++++++++++++++++++++++++++- include/hw/misc/tz-mpc.h | 10 +++ 2 files changed, 147 insertions(+), 3 deletions(-) diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index 8a8617bdbd..e5b91bf81a 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -28,16 +28,23 @@ enum { /* Config registers */ REG32(CTRL, 0x00) + FIELD(CTRL, SEC_RESP, 4, 1) + FIELD(CTRL, AUTOINC, 8, 1) + FIELD(CTRL, LOCKDOWN, 31, 1) REG32(BLK_MAX, 0x10) REG32(BLK_CFG, 0x14) REG32(BLK_IDX, 0x18) REG32(BLK_LUT, 0x1c) REG32(INT_STAT, 0x20) + FIELD(INT_STAT, IRQ, 0, 1) REG32(INT_CLEAR, 0x24) + FIELD(INT_CLEAR, IRQ, 0, 1) REG32(INT_EN, 0x28) + FIELD(INT_EN, IRQ, 0, 1) REG32(INT_INFO1, 0x2c) REG32(INT_INFO2, 0x30) REG32(INT_SET, 0x34) + FIELD(INT_SET, IRQ, 0, 1) REG32(PIDR4, 0xfd0) REG32(PIDR5, 0xfd4) REG32(PIDR6, 0xfd8) @@ -57,10 +64,25 @@ static const uint8_t tz_mpc_idregs[] = { 0x0d, 0xf0, 0x05, 0xb1, }; +static void tz_mpc_irq_update(TZMPC *s) +{ + qemu_set_irq(s->irq, s->int_stat && s->int_en); +} + +static void tz_mpc_autoinc_idx(TZMPC *s, unsigned access_size) +{ + /* Auto-increment BLK_IDX if necessary */ + if (access_size == 4 && (s->ctrl & R_CTRL_AUTOINC_MASK)) { + s->blk_idx++; + s->blk_idx %= s->blk_max; + } +} + static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr, uint64_t *pdata, unsigned size, MemTxAttrs attrs) { + TZMPC *s = TZ_MPC(opaque); uint64_t r; uint32_t offset = addr & ~0x3; @@ -74,6 +96,38 @@ static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr, } switch (offset) { + case A_CTRL: + r = s->ctrl; + break; + case A_BLK_MAX: + r = s->blk_max; + break; + case A_BLK_CFG: + /* We are never in "init in progress state", so this just indicates + * the block size. s->blocksize == (1 << BLK_CFG + 5), so + * BLK_CFG == ctz32(s->blocksize) - 5 + */ + r = ctz32(s->blocksize) - 5; + break; + case A_BLK_IDX: + r = s->blk_idx; + break; + case A_BLK_LUT: + r = s->blk_lut[s->blk_idx]; + tz_mpc_autoinc_idx(s, size); + break; + case A_INT_STAT: + r = s->int_stat; + break; + case A_INT_EN: + r = s->int_en; + break; + case A_INT_INFO1: + r = s->int_info1; + break; + case A_INT_INFO2: + r = s->int_info2; + break; case A_PIDR4: case A_PIDR5: case A_PIDR6: @@ -120,6 +174,7 @@ static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) { + TZMPC *s = TZ_MPC(opaque); uint32_t offset = addr & ~0x3; trace_tz_mpc_reg_write(addr, value, size); @@ -140,9 +195,15 @@ static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, uint32_t oldval; switch (offset) { - /* As we add support for registers which need expansions - * other than zeroes we'll fill in cases here. - */ + case A_CTRL: + oldval = s->ctrl; + break; + case A_BLK_IDX: + oldval = s->blk_idx; + break; + case A_BLK_LUT: + oldval = s->blk_lut[s->blk_idx]; + break; default: oldval = 0; break; @@ -150,7 +211,48 @@ static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, value = deposit32(oldval, (addr & 3) * 8, size * 8, value); } + if ((s->ctrl & R_CTRL_LOCKDOWN_MASK) && + (offset == A_CTRL || offset == A_BLK_LUT || offset == A_INT_EN)) { + /* Lockdown mode makes these three registers read-only, and + * the only way out of it is to reset the device. + */ + qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register write to offset 0x%x " + "while MPC is in lockdown mode\n", offset); + return MEMTX_OK; + } + switch (offset) { + case A_CTRL: + /* We don't implement the 'data gating' feature so all other bits + * are reserved and we make them RAZ/WI. + */ + s->ctrl = value & (R_CTRL_SEC_RESP_MASK | + R_CTRL_AUTOINC_MASK | + R_CTRL_LOCKDOWN_MASK); + break; + case A_BLK_IDX: + s->blk_idx = value % s->blk_max; + break; + case A_BLK_LUT: + s->blk_lut[s->blk_idx] = value; + tz_mpc_autoinc_idx(s, size); + break; + case A_INT_CLEAR: + if (value & R_INT_CLEAR_IRQ_MASK) { + s->int_stat = 0; + tz_mpc_irq_update(s); + } + break; + case A_INT_EN: + s->int_en = value & R_INT_EN_IRQ_MASK; + tz_mpc_irq_update(s); + break; + case A_INT_SET: + if (value & R_INT_SET_IRQ_MASK) { + s->int_stat = R_INT_STAT_IRQ_MASK; + tz_mpc_irq_update(s); + } + break; case A_PIDR4: case A_PIDR5: case A_PIDR6: @@ -266,6 +368,16 @@ static int tz_mpc_num_indexes(IOMMUMemoryRegion *iommu) static void tz_mpc_reset(DeviceState *dev) { + TZMPC *s = TZ_MPC(dev); + + s->ctrl = 0x00000100; + s->blk_idx = 0; + s->int_stat = 0; + s->int_en = 1; + s->int_info1 = 0; + s->int_info2 = 0; + + memset(s->blk_lut, 0, s->blk_max * sizeof(uint32_t)); } static void tz_mpc_init(Object *obj) @@ -339,13 +451,35 @@ static void tz_mpc_realize(DeviceState *dev, Error **errp) "tz-mpc-downstream"); address_space_init(&s->blocked_io_as, &s->blocked_io, "tz-mpc-blocked-io"); + + s->blk_lut = g_new(uint32_t, s->blk_max); +} + +static int tz_mpc_post_load(void *opaque, int version_id) +{ + TZMPC *s = TZ_MPC(opaque); + + /* Check the incoming data doesn't point blk_idx off the end of blk_lut. */ + if (s->blk_idx >= s->blk_max) { + return -1; + } + return 0; } static const VMStateDescription tz_mpc_vmstate = { .name = "tz-mpc", .version_id = 1, .minimum_version_id = 1, + .post_load = tz_mpc_post_load, .fields = (VMStateField[]) { + VMSTATE_UINT32(ctrl, TZMPC), + VMSTATE_UINT32(blk_idx, TZMPC), + VMSTATE_UINT32(int_stat, TZMPC), + VMSTATE_UINT32(int_en, TZMPC), + VMSTATE_UINT32(int_info1, TZMPC), + VMSTATE_UINT32(int_info2, TZMPC), + VMSTATE_VARRAY_UINT32(blk_lut, TZMPC, blk_max, + 0, vmstate_info_uint32, uint32_t), VMSTATE_END_OF_LIST() } }; diff --git a/include/hw/misc/tz-mpc.h b/include/hw/misc/tz-mpc.h index d1a65fd9a3..6f15945410 100644 --- a/include/hw/misc/tz-mpc.h +++ b/include/hw/misc/tz-mpc.h @@ -48,6 +48,16 @@ struct TZMPC { /*< public >*/ + /* State */ + uint32_t ctrl; + uint32_t blk_idx; + uint32_t int_stat; + uint32_t int_en; + uint32_t int_info1; + uint32_t int_info2; + + uint32_t *blk_lut; + qemu_irq irq; /* Properties */ From 57c49a6e8773bcce0d407ff4d9b2de7a904f1b0d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:40 +0100 Subject: [PATCH 1542/2380] hw/misc/tz-mpc.c: Implement correct blocked-access behaviour The MPC is guest-configurable for whether blocked accesses: * should be RAZ/WI or cause a bus error * should generate an interrupt or not Implement this behaviour in the blocked-access handlers. Signed-off-by: Peter Maydell Reviewed-by: Eric Auger Message-id: 20180620132032.28865-4-peter.maydell@linaro.org --- hw/misc/tz-mpc.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index e5b91bf81a..fded5922a2 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -43,6 +43,9 @@ REG32(INT_EN, 0x28) FIELD(INT_EN, IRQ, 0, 1) REG32(INT_INFO1, 0x2c) REG32(INT_INFO2, 0x30) + FIELD(INT_INFO2, HMASTER, 0, 16) + FIELD(INT_INFO2, HNONSEC, 16, 1) + FIELD(INT_INFO2, CFG_NS, 17, 1) REG32(INT_SET, 0x34) FIELD(INT_SET, IRQ, 0, 1) REG32(PIDR4, 0xfd0) @@ -287,6 +290,45 @@ static const MemoryRegionOps tz_mpc_reg_ops = { .impl.max_access_size = 4, }; +static inline bool tz_mpc_cfg_ns(TZMPC *s, hwaddr addr) +{ + /* Return the cfg_ns bit from the LUT for the specified address */ + hwaddr blknum = addr / s->blocksize; + hwaddr blkword = blknum / 32; + uint32_t blkbit = 1U << (blknum % 32); + + /* This would imply the address was larger than the size we + * defined this memory region to be, so it can't happen. + */ + assert(blkword < s->blk_max); + return s->blk_lut[blkword] & blkbit; +} + +static MemTxResult tz_mpc_handle_block(TZMPC *s, hwaddr addr, MemTxAttrs attrs) +{ + /* Handle a blocked transaction: raise IRQ, capture info, etc */ + if (!s->int_stat) { + /* First blocked transfer: capture information into INT_INFO1 and + * INT_INFO2. Subsequent transfers are still blocked but don't + * capture information until the guest clears the interrupt. + */ + + s->int_info1 = addr; + s->int_info2 = 0; + s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HMASTER, + attrs.requester_id & 0xffff); + s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HNONSEC, + ~attrs.secure); + s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, CFG_NS, + tz_mpc_cfg_ns(s, addr)); + s->int_stat |= R_INT_STAT_IRQ_MASK; + tz_mpc_irq_update(s); + } + + /* Generate bus error if desired; otherwise RAZ/WI */ + return (s->ctrl & R_CTRL_SEC_RESP_MASK) ? MEMTX_ERROR : MEMTX_OK; +} + /* Accesses only reach these read and write functions if the MPC is * blocking them; non-blocked accesses go directly to the downstream * memory region without passing through this code. @@ -295,19 +337,23 @@ static MemTxResult tz_mpc_mem_blocked_read(void *opaque, hwaddr addr, uint64_t *pdata, unsigned size, MemTxAttrs attrs) { + TZMPC *s = TZ_MPC(opaque); + trace_tz_mpc_mem_blocked_read(addr, size, attrs.secure); *pdata = 0; - return MEMTX_OK; + return tz_mpc_handle_block(s, addr, attrs); } static MemTxResult tz_mpc_mem_blocked_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) { + TZMPC *s = TZ_MPC(opaque); + trace_tz_mpc_mem_blocked_write(addr, value, size, attrs.secure); - return MEMTX_OK; + return tz_mpc_handle_block(s, addr, attrs); } static const MemoryRegionOps tz_mpc_mem_blocked_ops = { From dd29d0687d472be0c92b554383d2d8549fe37584 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:40 +0100 Subject: [PATCH 1543/2380] hw/misc/tz_mpc.c: Honour the BLK_LUT settings in translate The final part of the Memory Protection Controller we need to implement is actually using the BLK_LUT data programmed by the guest to determine whether to block the transaction or not. Since this means we now change transaction mappings when the guest writes to BLK_LUT, we must also call the IOMMU notifiers at that point. Signed-off-by: Peter Maydell Reviewed-by: Eric Auger Message-id: 20180620132032.28865-5-peter.maydell@linaro.org --- hw/misc/trace-events | 1 + hw/misc/tz-mpc.c | 53 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 72bf9d5700..c956e1419b 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -90,6 +90,7 @@ tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs wri tz_mpc_mem_blocked_read(uint64_t addr, unsigned size, bool secure) "TZ MPC blocked read: offset 0x%" PRIx64 " size %u secure %d" tz_mpc_mem_blocked_write(uint64_t addr, uint64_t data, unsigned size, bool secure) "TZ MPC blocked write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" tz_mpc_translate(uint64_t addr, int flags, const char *idx, const char *res) "TZ MPC translate: addr 0x%" PRIx64 " flags 0x%x iommu_idx %s: %s" +tz_mpc_iommu_notify(uint64_t addr) "TZ MPC iommu: notifying UNMAP/MAP for 0x%" PRIx64 # hw/misc/tz-ppc.c tz_ppc_reset(void) "TZ PPC: reset" diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index fded5922a2..8316079b4b 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -72,6 +72,53 @@ static void tz_mpc_irq_update(TZMPC *s) qemu_set_irq(s->irq, s->int_stat && s->int_en); } +static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx, + uint32_t oldlut, uint32_t newlut) +{ + /* Called when the LUT word at lutidx has changed from oldlut to newlut; + * must call the IOMMU notifiers for the changed blocks. + */ + IOMMUTLBEntry entry = { + .addr_mask = s->blocksize - 1, + }; + hwaddr addr = lutidx * s->blocksize * 32; + int i; + + for (i = 0; i < 32; i++, addr += s->blocksize) { + bool block_is_ns; + + if (!((oldlut ^ newlut) & (1 << i))) { + continue; + } + /* This changes the mappings for both the S and the NS space, + * so we need to do four notifies: an UNMAP then a MAP for each. + */ + block_is_ns = newlut & (1 << i); + + trace_tz_mpc_iommu_notify(addr); + entry.iova = addr; + entry.translated_addr = addr; + + entry.perm = IOMMU_NONE; + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry); + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry); + + entry.perm = IOMMU_RW; + if (block_is_ns) { + entry.target_as = &s->blocked_io_as; + } else { + entry.target_as = &s->downstream_as; + } + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry); + if (block_is_ns) { + entry.target_as = &s->downstream_as; + } else { + entry.target_as = &s->blocked_io_as; + } + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry); + } +} + static void tz_mpc_autoinc_idx(TZMPC *s, unsigned access_size) { /* Auto-increment BLK_IDX if necessary */ @@ -237,6 +284,7 @@ static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, s->blk_idx = value % s->blk_max; break; case A_BLK_LUT: + tz_mpc_iommu_notify(s, s->blk_idx, s->blk_lut[s->blk_idx], value); s->blk_lut[s->blk_idx] = value; tz_mpc_autoinc_idx(s, size); break; @@ -383,9 +431,10 @@ static IOMMUTLBEntry tz_mpc_translate(IOMMUMemoryRegion *iommu, /* Look at the per-block configuration for this address, and * return a TLB entry directing the transaction at either * downstream_as or blocked_io_as, as appropriate. - * For the moment, always permit accesses. + * If the LUT cfg_ns bit is 1, only non-secure transactions + * may pass. If the bit is 0, only secure transactions may pass. */ - ok = true; + ok = tz_mpc_cfg_ns(s, addr) == (iommu_idx == IOMMU_IDX_NS); trace_tz_mpc_translate(addr, flags, iommu_idx == IOMMU_IDX_S ? "S" : "NS", From 3fd3cb2f6f1b3ada95a728e2bf0a0cfa4c84a8e1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:40 +0100 Subject: [PATCH 1544/2380] hw/misc/iotkit-secctl.c: Implement SECMPCINTSTATUS Implement the SECMPCINTSTATUS register. This is the only register in the security controller that deals with Memory Protection Controllers, and it simply provides a read-only view of the interrupt lines from the various MPCs in the system. Signed-off-by: Peter Maydell Message-id: 20180620132032.28865-6-peter.maydell@linaro.org --- hw/misc/iotkit-secctl.c | 38 +++++++++++++++++++++++++++++++-- include/hw/misc/iotkit-secctl.h | 8 +++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index ddd1584d34..de4fd8e36d 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -139,6 +139,9 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, case A_NSCCFG: r = s->nsccfg; break; + case A_SECMPCINTSTATUS: + r = s->mpcintstatus; + break; case A_SECPPCINTSTAT: r = s->secppcintstat; break; @@ -186,7 +189,6 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, case A_APBSPPPCEXP3: r = s->apbexp[offset_to_ppc_idx(offset)].sp; break; - case A_SECMPCINTSTATUS: case A_SECMSCINTSTAT: case A_SECMSCINTEN: case A_NSMSCEXP: @@ -572,6 +574,20 @@ static void iotkit_secctl_reset(DeviceState *dev) foreach_ppc(s, iotkit_secctl_reset_ppc); } +static void iotkit_secctl_mpc_status(void *opaque, int n, int level) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + + s->mpcintstatus = deposit32(s->mpcintstatus, 0, 1, !!level); +} + +static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + + s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level); +} + static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level) { IoTKitSecCtlPPC *ppc = opaque; @@ -640,6 +656,10 @@ static void iotkit_secctl_init(Object *obj) qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1); + qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", 1); + qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status, + "mpcexp_status", IOTS_NUM_EXP_MPC); + memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops, s, "iotkit-secctl-s-regs", 0x1000); memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops, @@ -660,6 +680,16 @@ static const VMStateDescription iotkit_secctl_ppc_vmstate = { } }; +static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = { + .name = "iotkit-secctl-mpcintstatus", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(mpcintstatus, IoTKitSecCtl), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription iotkit_secctl_vmstate = { .name = "iotkit-secctl", .version_id = 1, @@ -677,7 +707,11 @@ static const VMStateDescription iotkit_secctl_vmstate = { VMSTATE_STRUCT_ARRAY(ahbexp, IoTKitSecCtl, IOTS_NUM_AHB_EXP_PPC, 1, iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), VMSTATE_END_OF_LIST() - } + }, + .subsections = (const VMStateDescription*[]) { + &iotkit_secctl_mpcintstatus_vmstate, + NULL + }, }; static void iotkit_secctl_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/misc/iotkit-secctl.h b/include/hw/misc/iotkit-secctl.h index faad0c9190..082c14c925 100644 --- a/include/hw/misc/iotkit-secctl.h +++ b/include/hw/misc/iotkit-secctl.h @@ -39,6 +39,11 @@ * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status + * Controlling the MPC in the IoTKit: + * + named GPIO input mpc_status + * Controlling each of the 16 expansion MPCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mpcexp_status[0..15] */ #ifndef IOTKIT_SECCTL_H @@ -55,6 +60,8 @@ #define IOTS_NUM_APB_PPC 2 #define IOTS_NUM_APB_EXP_PPC 4 #define IOTS_NUM_AHB_EXP_PPC 4 +#define IOTS_NUM_EXP_MPC 16 +#define IOTS_NUM_MPC 1 typedef struct IoTKitSecCtl IoTKitSecCtl; @@ -94,6 +101,7 @@ struct IoTKitSecCtl { uint32_t secrespcfg; uint32_t nsccfg; uint32_t brginten; + uint32_t mpcintstatus; IoTKitSecCtlPPC apb[IOTS_NUM_APB_PPC]; IoTKitSecCtlPPC apbexp[IOTS_NUM_APB_EXP_PPC]; From af60b29183208123d9aab25acf47e44d54d12a9d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:40 +0100 Subject: [PATCH 1545/2380] hw/arm/iotkit: Instantiate MPC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up the one MPC that is part of the IoTKit itself. For the moment we don't wire up its interrupt line. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180620132032.28865-7-peter.maydell@linaro.org --- hw/arm/iotkit.c | 38 +++++++++++++++++++++++++++----------- include/hw/arm/iotkit.h | 2 ++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 234185e8f7..160e40c744 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -130,6 +130,7 @@ static void iotkit_init(Object *obj) TYPE_TZ_PPC); init_sysbus_child(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), TYPE_TZ_PPC); + init_sysbus_child(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC); init_sysbus_child(obj, "timer0", &s->timer0, sizeof(s->timer0), TYPE_CMSDK_APB_TIMER); init_sysbus_child(obj, "timer1", &s->timer1, sizeof(s->timer1), @@ -266,15 +267,6 @@ static void iotkit_realize(DeviceState *dev, Error **errp) */ make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); - /* This RAM should be behind a Memory Protection Controller, but we - * don't implement that yet. - */ - memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); - if (err) { - error_propagate(errp, err); - return; - } - memory_region_add_subregion(&s->container, 0x20000000, &s->sram0); /* Security controller */ object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); @@ -310,6 +302,32 @@ static void iotkit_realize(DeviceState *dev, Error **errp) qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, qdev_get_gpio_in(dev_splitter, 0)); + /* This RAM lives behind the Memory Protection Controller */ + memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_link(OBJECT(&s->mpc), OBJECT(&s->sram0), + "downstream", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->mpc), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* Map the upstream end of the MPC into the right place... */ + memory_region_add_subregion(&s->container, 0x20000000, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), + 1)); + /* ...and its register interface */ + memory_region_add_subregion(&s->container, 0x50083000, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), + 0)); + /* Devices behind APB PPC0: * 0x40000000: timer0 * 0x40001000: timer1 @@ -473,8 +491,6 @@ static void iotkit_realize(DeviceState *dev, Error **errp) create_unimplemented_device("NS watchdog", 0x40081000, 0x1000); create_unimplemented_device("S watchdog", 0x50081000, 0x1000); - create_unimplemented_device("SRAM0 MPC", 0x50083000, 0x1000); - for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h index c6129d926b..b21cf1ab9d 100644 --- a/include/hw/arm/iotkit.h +++ b/include/hw/arm/iotkit.h @@ -51,6 +51,7 @@ #include "hw/arm/armv7m.h" #include "hw/misc/iotkit-secctl.h" #include "hw/misc/tz-ppc.h" +#include "hw/misc/tz-mpc.h" #include "hw/timer/cmsdk-apb-timer.h" #include "hw/misc/unimp.h" #include "hw/or-irq.h" @@ -74,6 +75,7 @@ typedef struct IoTKit { IoTKitSecCtl secctl; TZPPC apb_ppc0; TZPPC apb_ppc1; + TZMPC mpc; CMSDKAPBTIMER timer0; CMSDKAPBTIMER timer1; qemu_or_irq ppc_irq_orgate; From bb75e16d5e67a764e008d0b73b4ec9f905e5b408 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:40 +0100 Subject: [PATCH 1546/2380] hw/arm/iotkit: Wire up MPC interrupt lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interrupt outputs from the MPC in the IoTKit and the expansion MPCs in the board must be wired up to the security controller, and also all ORed together to produce a single line to the NVIC. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180620132032.28865-8-peter.maydell@linaro.org --- hw/arm/iotkit.c | 74 +++++++++++++++++++++++++++++++++++++++++ include/hw/arm/iotkit.h | 6 ++++ 2 files changed, 80 insertions(+) diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 160e40c744..133d5bb34f 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -131,6 +131,18 @@ static void iotkit_init(Object *obj) init_sysbus_child(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), TYPE_TZ_PPC); init_sysbus_child(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC); + object_initialize(&s->mpc_irq_orgate, sizeof(s->mpc_irq_orgate), + TYPE_OR_IRQ); + object_property_add_child(obj, "mpc-irq-orgate", + OBJECT(&s->mpc_irq_orgate), &error_abort); + for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) { + char *name = g_strdup_printf("mpc-irq-splitter-%d", i); + SplitIRQ *splitter = &s->mpc_irq_splitter[i]; + + object_initialize(splitter, sizeof(*splitter), TYPE_SPLIT_IRQ); + object_property_add_child(obj, name, OBJECT(splitter), &error_abort); + g_free(name); + } init_sysbus_child(obj, "timer0", &s->timer0, sizeof(s->timer0), TYPE_CMSDK_APB_TIMER); init_sysbus_child(obj, "timer1", &s->timer1, sizeof(s->timer1), @@ -163,6 +175,12 @@ static void iotkit_exp_irq(void *opaque, int n, int level) qemu_set_irq(s->exp_irqs[n], level); } +static void iotkit_mpcexp_status(void *opaque, int n, int level) +{ + IoTKit *s = IOTKIT(opaque); + qemu_set_irq(s->mpcexp_status_in[n], level); +} + static void iotkit_realize(DeviceState *dev, Error **errp) { IoTKit *s = IOTKIT(dev); @@ -328,6 +346,22 @@ static void iotkit_realize(DeviceState *dev, Error **errp) sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), 0)); + /* We must OR together lines from the MPC splitters to go to the NVIC */ + object_property_set_int(OBJECT(&s->mpc_irq_orgate), + IOTS_NUM_EXP_MPC + IOTS_NUM_MPC, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 9)); + /* Devices behind APB PPC0: * 0x40000000: timer0 * 0x40001000: timer1 @@ -536,6 +570,46 @@ static void iotkit_realize(DeviceState *dev, Error **errp) g_free(gpioname); } + /* Wire up the splitters for the MPC IRQs */ + for (i = 0; i < IOTS_NUM_EXP_MPC + IOTS_NUM_MPC; i++) { + SplitIRQ *splitter = &s->mpc_irq_splitter[i]; + DeviceState *dev_splitter = DEVICE(splitter); + + object_property_set_int(OBJECT(splitter), 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(splitter), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (i < IOTS_NUM_EXP_MPC) { + /* Splitter input is from GPIO input line */ + s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + "mpcexp_status", i)); + } else { + /* Splitter input is from our own MPC */ + qdev_connect_gpio_out_named(DEVICE(&s->mpc), "irq", 0, + qdev_get_gpio_in(dev_splitter, 0)); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + "mpc_status", 0)); + } + + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i)); + } + /* Create GPIO inputs which will pass the line state for our + * mpcexp_irq inputs to the correct splitter devices. + */ + qdev_init_gpio_in_named(dev, iotkit_mpcexp_status, "mpcexp_status", + IOTS_NUM_EXP_MPC); + iotkit_forward_sec_resp_cfg(s); system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h index b21cf1ab9d..2cddde55dd 100644 --- a/include/hw/arm/iotkit.h +++ b/include/hw/arm/iotkit.h @@ -42,6 +42,9 @@ * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status + * Controlling each of the 16 expansion MPCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mpcexp_status[0..15] */ #ifndef IOTKIT_H @@ -81,6 +84,8 @@ typedef struct IoTKit { qemu_or_irq ppc_irq_orgate; SplitIRQ sec_resp_splitter; SplitIRQ ppc_irq_splitter[NUM_PPCS]; + SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC]; + qemu_or_irq mpc_irq_orgate; UnimplementedDeviceState dualtimer; UnimplementedDeviceState s32ktimer; @@ -99,6 +104,7 @@ typedef struct IoTKit { qemu_irq nsc_cfg_in; qemu_irq irq_status_in[NUM_EXTERNAL_PPCS]; + qemu_irq mpcexp_status_in[IOTS_NUM_EXP_MPC]; uint32_t nsccfg; From 665670aa3ae85444e70888654e7af1eda19598b9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:41 +0100 Subject: [PATCH 1547/2380] hw/arm/mps2-tz.c: Instantiate MPCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instantiate and wire up the Memory Protection Controllers in the MPS2 board itself. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180620132032.28865-9-peter.maydell@linaro.org --- hw/arm/mps2-tz.c | 71 ++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index c5ef95e4cc..22180c56fb 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -44,6 +44,7 @@ #include "hw/timer/cmsdk-apb-timer.h" #include "hw/misc/mps2-scc.h" #include "hw/misc/mps2-fpgaio.h" +#include "hw/misc/tz-mpc.h" #include "hw/arm/iotkit.h" #include "hw/devices.h" #include "net/net.h" @@ -64,13 +65,12 @@ typedef struct { IoTKit iotkit; MemoryRegion psram; - MemoryRegion ssram1; + MemoryRegion ssram[3]; MemoryRegion ssram1_m; - MemoryRegion ssram23; MPS2SCC scc; MPS2FPGAIO fpgaio; TZPPC ppc[5]; - UnimplementedDeviceState ssram_mpc[3]; + TZMPC ssram_mpc[3]; UnimplementedDeviceState spi[5]; UnimplementedDeviceState i2c[4]; UnimplementedDeviceState i2s_audio; @@ -96,16 +96,6 @@ typedef struct { /* Main SYSCLK frequency in Hz */ #define SYSCLK_FRQ 20000000 -/* Initialize the auxiliary RAM region @mr and map it into - * the memory map at @base. - */ -static void make_ram(MemoryRegion *mr, const char *name, - hwaddr base, hwaddr size) -{ - memory_region_init_ram(mr, NULL, name, size, &error_fatal); - memory_region_add_subregion(get_system_memory(), base, mr); -} - /* Create an alias of an entire original MemoryRegion @orig * located at @base in the memory map. */ @@ -245,6 +235,44 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, return sysbus_mmio_get_region(s, 0); } +static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + TZMPC *mpc = opaque; + int i = mpc - &mms->ssram_mpc[0]; + MemoryRegion *ssram = &mms->ssram[i]; + MemoryRegion *upstream; + char *mpcname = g_strdup_printf("%s-mpc", name); + static uint32_t ramsize[] = { 0x00400000, 0x00200000, 0x00200000 }; + static uint32_t rambase[] = { 0x00000000, 0x28000000, 0x28200000 }; + + memory_region_init_ram(ssram, NULL, name, ramsize[i], &error_fatal); + + init_sysbus_child(OBJECT(mms), mpcname, mpc, + sizeof(mms->ssram_mpc[0]), TYPE_TZ_MPC); + object_property_set_link(OBJECT(mpc), OBJECT(ssram), + "downstream", &error_fatal); + object_property_set_bool(OBJECT(mpc), true, "realized", &error_fatal); + /* Map the upstream end of the MPC into system memory */ + upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 1); + memory_region_add_subregion(get_system_memory(), rambase[i], upstream); + /* and connect its interrupt to the IoTKit */ + qdev_connect_gpio_out_named(DEVICE(mpc), "irq", 0, + qdev_get_gpio_in_named(DEVICE(&mms->iotkit), + "mpcexp_status", i)); + + /* The first SSRAM is a special case as it has an alias; accesses to + * the alias region at 0x00400000 must also go to the MPC upstream. + */ + if (i == 0) { + make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", upstream, 0x00400000); + } + + g_free(mpcname); + /* Return the register interface MR for our caller to map behind the PPC */ + return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0); +} + static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -306,14 +334,6 @@ static void mps2tz_common_init(MachineState *machine) NULL, "mps.ram", 0x01000000); memory_region_add_subregion(system_memory, 0x80000000, &mms->psram); - /* The SSRAM memories should all be behind Memory Protection Controllers, - * but we don't implement that yet. - */ - make_ram(&mms->ssram1, "mps.ssram1", 0x00000000, 0x00400000); - make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x00400000); - - make_ram(&mms->ssram23, "mps.ssram23", 0x28000000, 0x00400000); - /* The overflow IRQs for all UARTs are ORed together. * Tx, Rx and "combined" IRQs are sent to the NVIC separately. * Create the OR gate for this. @@ -343,12 +363,9 @@ static void mps2tz_common_init(MachineState *machine) const PPCInfo ppcs[] = { { .name = "apb_ppcexp0", .ports = { - { "ssram-mpc0", make_unimp_dev, &mms->ssram_mpc[0], - 0x58007000, 0x1000 }, - { "ssram-mpc1", make_unimp_dev, &mms->ssram_mpc[1], - 0x58008000, 0x1000 }, - { "ssram-mpc2", make_unimp_dev, &mms->ssram_mpc[2], - 0x58009000, 0x1000 }, + { "ssram-0", make_mpc, &mms->ssram_mpc[0], 0x58007000, 0x1000 }, + { "ssram-1", make_mpc, &mms->ssram_mpc[1], 0x58008000, 0x1000 }, + { "ssram-2", make_mpc, &mms->ssram_mpc[2], 0x58009000, 0x1000 }, }, }, { .name = "apb_ppcexp1", From cc2ae7c9de14efd72c6205825eb7cd980ac09c11 Mon Sep 17 00:00:00 2001 From: Julia Suvorova Date: Fri, 22 Jun 2018 13:28:41 +0100 Subject: [PATCH 1548/2380] target/arm: Introduce ARM_FEATURE_M_MAIN This feature is intended to distinguish ARMv8-M variants: Baseline and Mainline. ARMv7-M compatibility requires the Main Extension. ARMv6-M compatibility is provided by all ARMv8-M implementations. Signed-off-by: Julia Suvorova Message-id: 20180622080138.17702-2-jusual@mail.ru Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu.c | 3 +++ target/arm/cpu.h | 1 + 2 files changed, 4 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 81c1d22b14..2ae4fffafb 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1238,6 +1238,7 @@ static void cortex_m3_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); cpu->midr = 0x410fc231; cpu->pmsav7_dregion = 8; cpu->id_pfr0 = 0x00000030; @@ -1262,6 +1263,7 @@ static void cortex_m4_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); cpu->midr = 0x410fc240; /* r0p0 */ cpu->pmsav7_dregion = 8; @@ -1287,6 +1289,7 @@ static void cortex_m33_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); cpu->midr = 0x410fd213; /* r0p3 */ diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 8488273c5b..a4507a2d6f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1482,6 +1482,7 @@ enum arm_features { ARM_FEATURE_V8_RDM, /* implements v8.1 simd round multiply */ ARM_FEATURE_V8_FP16, /* implements v8.2 half-precision float */ ARM_FEATURE_V8_FCMA, /* has complex number part of v8.3 extensions. */ + ARM_FEATURE_M_MAIN, /* M profile Main Extension */ }; static inline int arm_feature(CPUARMState *env, int feature) From 2aeba0d007d33efa12a6339bb140aa634e0d52eb Mon Sep 17 00:00:00 2001 From: Julia Suvorova Date: Fri, 22 Jun 2018 13:28:41 +0100 Subject: [PATCH 1549/2380] target/arm: Strict alignment for ARMv6-M and ARMv8-M Baseline Unlike ARMv7-M, ARMv6-M and ARMv8-M Baseline only supports naturally aligned memory accesses for load/store instructions. Signed-off-by: Julia Suvorova Message-id: 20180622080138.17702-3-jusual@mail.ru Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index b988d379e7..2a3e4f5d4c 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -1100,7 +1100,14 @@ static inline TCGv gen_aa32_addr(DisasContext *s, TCGv_i32 a32, TCGMemOp op) static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, int index, TCGMemOp opc) { - TCGv addr = gen_aa32_addr(s, a32, opc); + TCGv addr; + + if (arm_dc_feature(s, ARM_FEATURE_M) && + !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) { + opc |= MO_ALIGN; + } + + addr = gen_aa32_addr(s, a32, opc); tcg_gen_qemu_ld_i32(val, addr, index, opc); tcg_temp_free(addr); } @@ -1108,7 +1115,14 @@ static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, static void gen_aa32_st_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, int index, TCGMemOp opc) { - TCGv addr = gen_aa32_addr(s, a32, opc); + TCGv addr; + + if (arm_dc_feature(s, ARM_FEATURE_M) && + !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) { + opc |= MO_ALIGN; + } + + addr = gen_aa32_addr(s, a32, opc); tcg_gen_qemu_st_i32(val, addr, index, opc); tcg_temp_free(addr); } From b8d880ba6dcca098dc3a5d345a13bebdcce89c90 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:42 +0100 Subject: [PATCH 1550/2380] vl.c: Don't zero-initialize statics for serial_hds checkpatch reminds us that statics shouldn't be zero-initialized: ERROR: do not initialise statics to 0 or NULL #35: FILE: vl.c:157: +static int num_serial_hds = 0; ERROR: do not initialise statics to 0 or NULL #36: FILE: vl.c:158: +static Chardev **serial_hds = NULL; I forgot to fix this in 6af2692e86f9fdfb3d; do so now. Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Message-id: 20180426140253.3918-1-peter.maydell@linaro.org --- vl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vl.c b/vl.c index b3426e03d0..d451f45dc5 100644 --- a/vl.c +++ b/vl.c @@ -151,8 +151,8 @@ QEMUClockType rtc_clock; int vga_interface_type = VGA_NONE; static DisplayOptions dpy; int no_frame; -static int num_serial_hds = 0; -static Chardev **serial_hds = NULL; +static int num_serial_hds; +static Chardev **serial_hds; Chardev *parallel_hds[MAX_PARALLEL_PORTS]; Chardev *virtcon_hds[MAX_VIRTIO_CONSOLES]; int win2k_install_hack = 0; From 6dad8260e82b69bd278685ee25209f5824360455 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:42 +0100 Subject: [PATCH 1551/2380] xen: Don't use memory_region_init_ram_nomigrate() in pci_assign_dev_load_option_rom() The xen pci_assign_dev_load_option_rom() currently creates a RAM memory region with memory_region_init_ram_nomigrate(), and then manually registers it with vmstate_register_ram(). In fact for its only callsite, the 'owner' pointer we use for the init call and the '&dev->qdev' pointer we use for the vmstate_register_ram() call refer to the same object. Simplify the function to only take a pointer to the device once instead of twice, and use memory_region_init_ram() which automatically does the vmstate register for us. Acked-by: Anthony PERARD Signed-off-by: Peter Maydell --- hw/xen/xen_pt.h | 2 +- hw/xen/xen_pt_graphics.c | 2 +- hw/xen/xen_pt_load_rom.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index aa39a9aa5f..dbee3308fd 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -319,7 +319,7 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) } extern void *pci_assign_dev_load_option_rom(PCIDevice *dev, - struct Object *owner, int *size, + int *size, unsigned int domain, unsigned int bus, unsigned int slot, unsigned int function); diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index 0f4c8d77e2..135c8df1e7 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -132,7 +132,7 @@ int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev) static void *get_vgabios(XenPCIPassthroughState *s, int *size, XenHostPCIDevice *dev) { - return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size, + return pci_assign_dev_load_option_rom(&s->dev, size, dev->domain, dev->bus, dev->dev, dev->func); } diff --git a/hw/xen/xen_pt_load_rom.c b/hw/xen/xen_pt_load_rom.c index 71063c4d79..e6a86ca818 100644 --- a/hw/xen/xen_pt_load_rom.c +++ b/hw/xen/xen_pt_load_rom.c @@ -19,7 +19,7 @@ * load the corresponding ROM data to RAM. If an error occurs while loading an * option ROM, we just ignore that option ROM and continue with the next one. */ -void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, +void *pci_assign_dev_load_option_rom(PCIDevice *dev, int *size, unsigned int domain, unsigned int bus, unsigned int slot, unsigned int function) @@ -29,6 +29,7 @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, uint8_t val; struct stat st; void *ptr = NULL; + Object *owner = OBJECT(dev); /* If loading ROM from file, pci handles it */ if (dev->romfile || !dev->rom_bar) { @@ -59,8 +60,7 @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, fseek(fp, 0, SEEK_SET); snprintf(name, sizeof(name), "%s.rom", object_get_typename(owner)); - memory_region_init_ram_nomigrate(&dev->rom, owner, name, st.st_size, &error_abort); - vmstate_register_ram(&dev->rom, &dev->qdev); + memory_region_init_ram(&dev->rom, owner, name, st.st_size, &error_abort); ptr = memory_region_get_ram_ptr(&dev->rom); memset(ptr, 0xff, st.st_size); From 45eb6fb6cea28cdc937764aac6585751047bb294 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 22 Jun 2018 07:58:14 -0500 Subject: [PATCH 1552/2380] nbd/server: Silence gcc false positive The code has a while() loop that always initialized 'end', and the loop always executes at least once (as evidenced by the assert() just prior to the loop). But some versions of gcc still complain that 'end' is used uninitialized, so silence them. Signed-off-by: Eric Blake Reviewed-by: Peter Maydell Message-id: 20180622125814.345274-1-eblake@redhat.com Signed-off-by: Peter Maydell --- nbd/server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nbd/server.c b/nbd/server.c index 274604609f..50ac8bfafc 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1937,7 +1937,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, unsigned int nb_extents, bool dont_fragment) { - uint64_t begin = offset, end; + uint64_t begin = offset, end = offset; uint64_t overall_end = offset + *length; unsigned int i = 0; BdrvDirtyBitmapIter *it; @@ -1977,6 +1977,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, bdrv_dirty_bitmap_unlock(bitmap); + assert(offset > end); *length = end - offset; return i; } From a48e7542be9ef6dab3c8d52f563298d06ef872c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 21 Mar 2018 12:51:23 +0100 Subject: [PATCH 1553/2380] qapi/visit: remove useless prefix argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Message-Id: <20180321115211.17937-2-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/visit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 5d72d8936c..3c5ea1289e 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -293,7 +293,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): #include "qapi/qmp/qerror.h" #include "%(visit)s.h" ''', - visit=visit, prefix=self._prefix)) + visit=visit)) self._genh.preamble_add(mcgen(''' #include "qapi/qapi-builtin-visit.h" #include "%(types)s.h" From f030ffd39d6c1ea8fff281be5e4b19c819d7ce10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 21 Mar 2018 12:51:24 +0100 Subject: [PATCH 1554/2380] qapi/events: generate event enum in main module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The event generator produces an enum, and put it in the last visited module. It fits better in the main module, since it's the set of all visited events, from all modules. Signed-off-by: Marc-André Lureau Message-Id: <20180321115211.17937-3-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/events.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 4426861ff1..5657524688 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -180,8 +180,9 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): types=types)) def visit_end(self): - self._genh.add(gen_enum(self._enum_name, self._event_names)) - self._genc.add(gen_enum_lookup(self._enum_name, self._event_names)) + (genc, genh) = self._module[self._main_module] + genh.add(gen_enum(self._enum_name, self._event_names)) + genc.add(gen_enum_lookup(self._enum_name, self._event_names)) def visit_event(self, name, info, arg_type, boxed): self._genh.add(gen_event_send_decl(name, arg_type, boxed)) From bf6e6a37ee8c2d80af258cabdd476498b68188ad Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 11 Jun 2018 22:51:59 +0200 Subject: [PATCH 1555/2380] qdict: Make qdict_flatten() shallow-clone-friendly In its current form, qdict_flatten() removes all entries from nested QDicts that are moved to the root QDict. It is completely sufficient to remove all old entries from the root QDict, however. If the nested dicts have a refcount of 1, this will automatically delete them, too. And if they have a greater refcount, we probably do not want to modify them in the first place. The latter observation means that it was currently (in general) impossible to qdict_flatten() a shallowly cloned dict because that would empty nested QDicts in the original dict as well. This patch changes this, so you can now use qdict_flatten(qdict_shallow_clone(dict)) to get a flattened copy without disturbing the original. Signed-off-by: Max Reitz Message-Id: <20180611205203.2624-7-mreitz@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qobject/block-qdict.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index df833083a7..36129e7379 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -114,19 +114,30 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) /* * Flatten non-empty QDict and QList recursively into @target, - * copy other objects to @target + * copy other objects to @target. + * On the root level (if @qdict == @target), remove flattened + * nested QDicts and QLists from @qdict. + * + * (Note that we do not need to remove entries from nested + * dicts or lists. Their reference count is decremented on + * the root level, so there are no leaks. In fact, if they + * have a reference count greater than one, we are probably + * well advised not to modify them altogether.) */ if (dict_val && qdict_size(dict_val)) { qdict_flatten_qdict(dict_val, target, new_key ? new_key : entry->key); - qdict_del(qdict, entry->key); + if (target == qdict) { + qdict_del(qdict, entry->key); + } } else if (list_val && !qlist_empty(list_val)) { qdict_flatten_qlist(list_val, target, new_key ? new_key : entry->key); - qdict_del(qdict, entry->key); + if (target == qdict) { + qdict_del(qdict, entry->key); + } } else if (target != qdict) { qdict_put_obj(target, new_key, qobject_ref(value)); - qdict_del(qdict, entry->key); } g_free(new_key); From fe170d8bfaa12d63117cad8707ac18c3b2f3bb8e Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 11 Jun 2018 22:52:00 +0200 Subject: [PATCH 1556/2380] tests: Add QDict clone-flatten test This new test verifies that qdict_flatten() does not modify a shallow clone of the given QDict. Reviewed-by: Eric Blake Signed-off-by: Max Reitz Message-Id: <20180611205203.2624-8-mreitz@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- tests/check-block-qdict.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c index 1d20fccbd4..478807f839 100644 --- a/tests/check-block-qdict.c +++ b/tests/check-block-qdict.c @@ -125,6 +125,38 @@ static void qdict_flatten_test(void) qobject_unref(root); } +static void qdict_clone_flatten_test(void) +{ + QDict *dict1 = qdict_new(); + QDict *dict2 = qdict_new(); + QDict *cloned_dict1; + + /* + * Test that we can clone and flatten + * { "a": { "b": 42 } } + * without modifying the clone. + */ + + qdict_put_int(dict2, "b", 42); + qdict_put(dict1, "a", dict2); + + cloned_dict1 = qdict_clone_shallow(dict1); + + qdict_flatten(dict1); + + g_assert(qdict_size(dict1) == 1); + g_assert(qdict_get_int(dict1, "a.b") == 42); + + g_assert(qdict_size(cloned_dict1) == 1); + g_assert(qdict_get_qdict(cloned_dict1, "a") == dict2); + + g_assert(qdict_size(dict2) == 1); + g_assert(qdict_get_int(dict2, "b") == 42); + + qobject_unref(dict1); + qobject_unref(cloned_dict1); +} + static void qdict_array_split_test(void) { QDict *test_dict = qdict_new(); @@ -674,6 +706,7 @@ int main(int argc, char **argv) g_test_add_func("/public/defaults", qdict_defaults_test); g_test_add_func("/public/flatten", qdict_flatten_test); + g_test_add_func("/public/clone_flatten", qdict_clone_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); From 800877bb1639d38ffaebe312a37b61c66bb10c83 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Mon, 18 Jun 2018 11:40:05 +0300 Subject: [PATCH 1557/2380] qapi: allow empty branches in flat unions It often happens that just a few discriminator values imply extra data in a flat union. Existing checks did not make possible to leave other values uncovered. Such cases had to be worked around by either stating a dummy (empty) type or introducing another (subset) discriminator enumeration. Both options create redundant entities in qapi files for little profit. With this patch it is not necessary anymore to add designated union fields for every possible value of a discriminator enumeration. Signed-off-by: Anton Nefedov Message-Id: <1529311206-76847-2-git-send-email-anton.nefedov@virtuozzo.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/devel/qapi-code-gen.txt | 8 +++++--- scripts/qapi/common.py | 15 ++++++++------- scripts/qapi/types.py | 2 ++ scripts/qapi/visit.py | 19 ++++++++++++++----- tests/Makefile.include | 1 - .../flat-union-incomplete-branch.err | 1 - .../flat-union-incomplete-branch.exit | 1 - .../flat-union-incomplete-branch.json | 9 --------- .../flat-union-incomplete-branch.out | 0 tests/qapi-schema/qapi-schema-test.json | 6 ++++-- tests/qapi-schema/qapi-schema-test.out | 3 ++- 11 files changed, 35 insertions(+), 30 deletions(-) delete mode 100644 tests/qapi-schema/flat-union-incomplete-branch.err delete mode 100644 tests/qapi-schema/flat-union-incomplete-branch.exit delete mode 100644 tests/qapi-schema/flat-union-incomplete-branch.json delete mode 100644 tests/qapi-schema/flat-union-incomplete-branch.out diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 1366228b2a..88a70e4d45 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -496,9 +496,11 @@ Resulting in these JSON objects: Notice that in a flat union, the discriminator name is controlled by the user, but because it must map to a base member with enum type, the -code generator can ensure that branches exist for all values of the -enum (although the order of the keys need not match the declaration of -the enum). In the resulting generated C data types, a flat union is +code generator ensures that branches match the existing values of the +enum. The order of the keys need not match the declaration of the enum. +The keys need not cover all possible enum values. Omitted enum values +are still valid branches that add no additional members to the data type. +In the resulting generated C data types, a flat union is represented as a struct with the base members included directly, and then a union of structures for each branch of the struct. diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 2462fc0291..4b53f08627 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -779,13 +779,6 @@ def check_union(expr, info): "enum '%s'" % (key, enum_define['enum'])) - # If discriminator is user-defined, ensure all values are covered - if enum_define: - for value in enum_define['data']: - if value not in members.keys(): - raise QAPISemError(info, "Union '%s' data missing '%s' branch" - % (name, value)) - def check_alternate(expr, info): name = expr['alternate'] @@ -1357,6 +1350,14 @@ class QAPISchemaObjectTypeVariants(object): self.tag_member = seen[c_name(self._tag_name)] assert self._tag_name == self.tag_member.name assert isinstance(self.tag_member.type, QAPISchemaEnumType) + if self._tag_name: # flat union + # branches that are not explicitly covered get an empty type + cases = set([v.name for v in self.variants]) + for val in self.tag_member.type.values: + if val.name not in cases: + v = QAPISchemaObjectTypeVariant(val.name, 'q_empty') + v.set_owner(self.tag_member.owner) + self.variants.append(v) for v in self.variants: v.check(schema) # Union names must match enum values; alternate names are diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 64d9c0fb37..a599352e59 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -125,6 +125,8 @@ def gen_variants(variants): c_name=c_name(variants.tag_member.name)) for var in variants.variants: + if var.type.name == 'q_empty': + continue ret += mcgen(''' %(c_type)s %(c_name)s; ''', diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 3c5ea1289e..bdcafb64ee 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -81,15 +81,24 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) c_name=c_name(variants.tag_member.name)) for var in variants.variants: - ret += mcgen(''' + case_str = c_enum_const(variants.tag_member.type.name, + var.name, + variants.tag_member.type.prefix) + if var.type.name == 'q_empty': + # valid variant and nothing to do + ret += mcgen(''' + case %(case)s: + break; +''', + case=case_str) + else: + ret += mcgen(''' case %(case)s: visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err); break; ''', - case=c_enum_const(variants.tag_member.type.name, - var.name, - variants.tag_member.type.prefix), - c_type=var.type.c_name(), c_name=c_name(var.name)) + case=case_str, + c_type=var.type.c_name(), c_name=c_name(var.name)) ret += mcgen(''' default: diff --git a/tests/Makefile.include b/tests/Makefile.include index 7c48cfe14e..8b45f01d25 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -499,7 +499,6 @@ qapi-schema += flat-union-base-any.json qapi-schema += flat-union-base-union.json qapi-schema += flat-union-clash-member.json qapi-schema += flat-union-empty.json -qapi-schema += flat-union-incomplete-branch.json qapi-schema += flat-union-inline.json qapi-schema += flat-union-int-branch.json qapi-schema += flat-union-invalid-branch-key.json diff --git a/tests/qapi-schema/flat-union-incomplete-branch.err b/tests/qapi-schema/flat-union-incomplete-branch.err deleted file mode 100644 index e826bf0789..0000000000 --- a/tests/qapi-schema/flat-union-incomplete-branch.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/flat-union-incomplete-branch.json:6: Union 'TestUnion' data missing 'value2' branch diff --git a/tests/qapi-schema/flat-union-incomplete-branch.exit b/tests/qapi-schema/flat-union-incomplete-branch.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-incomplete-branch.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-incomplete-branch.json b/tests/qapi-schema/flat-union-incomplete-branch.json deleted file mode 100644 index 25a411bc83..0000000000 --- a/tests/qapi-schema/flat-union-incomplete-branch.json +++ /dev/null @@ -1,9 +0,0 @@ -# we require all branches of the union to be covered -{ 'enum': 'TestEnum', - 'data': [ 'value1', 'value2' ] } -{ 'struct': 'TestTypeA', - 'data': { 'string': 'str' } } -{ 'union': 'TestUnion', - 'base': { 'type': 'TestEnum' }, - 'discriminator': 'type', - 'data': { 'value1': 'TestTypeA' } } diff --git a/tests/qapi-schema/flat-union-incomplete-branch.out b/tests/qapi-schema/flat-union-incomplete-branch.out deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 46c7282945..7b59817f04 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -39,7 +39,7 @@ '*enum1': 'EnumOne' } } # intentional forward reference { 'enum': 'EnumOne', - 'data': [ 'value1', 'value2', 'value3' ] } + 'data': [ 'value1', 'value2', 'value3', 'value4' ] } { 'struct': 'UserDefZero', 'data': { 'integer': 'int' } } @@ -76,7 +76,9 @@ 'discriminator': 'enum1', 'data': { 'value1' : 'UserDefA', 'value2' : 'UserDefB', - 'value3' : 'UserDefB' } } + 'value3' : 'UserDefB' + # 'value4' defaults to empty + } } { 'struct': 'UserDefUnionBase', 'base': 'UserDefZero', diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 542a19c407..0dbcdafa3c 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -23,7 +23,7 @@ object UserDefOne base UserDefZero member string: str optional=False member enum1: EnumOne optional=True -enum EnumOne ['value1', 'value2', 'value3'] +enum EnumOne ['value1', 'value2', 'value3', 'value4'] object UserDefZero member integer: int optional=False object UserDefTwoDictDict @@ -52,6 +52,7 @@ object UserDefFlatUnion case value1: UserDefA case value2: UserDefB case value3: UserDefB + case value4: q_empty object UserDefUnionBase base UserDefZero member string: str optional=False From 29cd0403f19e4be928b50ce6247cee02c3dfd46b Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Mon, 18 Jun 2018 11:40:06 +0300 Subject: [PATCH 1558/2380] qapi: remove empty flat union branches and types Flat unions may now have uncovered branches, so it is possible to get rid of empty types defined for that purpose only. Signed-off-by: Anton Nefedov Reviewed-by: Markus Armbruster Message-Id: <1529311206-76847-3-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Markus Armbruster --- block/qcow2.c | 1 - cpus.c | 2 -- qapi/block-core.json | 52 +++----------------------------------------- qapi/crypto.json | 13 +---------- qapi/misc.json | 46 ++------------------------------------- qapi/net.json | 12 ---------- qapi/ui.json | 19 +--------------- 7 files changed, 7 insertions(+), 138 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 945132f692..a3a3aa2a97 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -4166,7 +4166,6 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) switch (encrypt_info->format) { case Q_CRYPTO_BLOCK_FORMAT_QCOW: qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES; - qencrypt->u.aes = encrypt_info->u.qcow; break; case Q_CRYPTO_BLOCK_FORMAT_LUKS: qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS; diff --git a/cpus.c b/cpus.c index d1f16296de..19c5d37108 100644 --- a/cpus.c +++ b/cpus.c @@ -2273,8 +2273,6 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) info->value->target = target; if (target == SYS_EMU_TARGET_S390X) { cpustate_to_cpuinfo_s390(&info->value->u.s390x, cpu); - } else { - /* do nothing for @CpuInfoOther */ } if (!cur_item) { diff --git a/qapi/block-core.json b/qapi/block-core.json index cc3ede0630..577ce5e999 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -52,8 +52,7 @@ { 'union': 'ImageInfoSpecificQCow2Encryption', 'base': 'ImageInfoSpecificQCow2EncryptionBase', 'discriminator': 'format', - 'data': { 'aes': 'QCryptoBlockInfoQCow', - 'luks': 'QCryptoBlockInfoLUKS' } } + 'data': { 'luks': 'QCryptoBlockInfoLUKS' } } ## # @ImageInfoSpecificQCow2: @@ -2877,16 +2876,6 @@ 'data': { 'type': 'SshHostKeyCheckHashType', 'hash': 'str' }} -## -# @SshHostKeyDummy: -# -# For those union branches that don't need additional fields. -# -# Since: 2.12 -## -{ 'struct': 'SshHostKeyDummy', - 'data': {} } - ## # @SshHostKeyCheck: # @@ -2895,9 +2884,7 @@ { 'union': 'SshHostKeyCheck', 'base': { 'mode': 'SshHostKeyCheckMode' }, 'discriminator': 'mode', - 'data': { 'none': 'SshHostKeyDummy', - 'hash': 'SshHostKeyHash', - 'known_hosts': 'SshHostKeyDummy' } } + 'data': { 'hash': 'SshHostKeyHash' } } ## # @BlockdevOptionsSsh: @@ -4075,15 +4062,6 @@ '*subformat': 'BlockdevVpcSubformat', '*force-size': 'bool' } } -## -# @BlockdevCreateNotSupported: -# -# This is used for all drivers that don't support creating images. -# -# Since: 2.12 -## -{ 'struct': 'BlockdevCreateNotSupported', 'data': {}} - ## # @BlockdevCreateOptions: # @@ -4098,44 +4076,20 @@ 'driver': 'BlockdevDriver' }, 'discriminator': 'driver', 'data': { - 'blkdebug': 'BlockdevCreateNotSupported', - 'blkverify': 'BlockdevCreateNotSupported', - 'bochs': 'BlockdevCreateNotSupported', - 'cloop': 'BlockdevCreateNotSupported', - 'copy-on-read': 'BlockdevCreateNotSupported', - 'dmg': 'BlockdevCreateNotSupported', 'file': 'BlockdevCreateOptionsFile', - 'ftp': 'BlockdevCreateNotSupported', - 'ftps': 'BlockdevCreateNotSupported', 'gluster': 'BlockdevCreateOptionsGluster', - 'host_cdrom': 'BlockdevCreateNotSupported', - 'host_device': 'BlockdevCreateNotSupported', - 'http': 'BlockdevCreateNotSupported', - 'https': 'BlockdevCreateNotSupported', - 'iscsi': 'BlockdevCreateNotSupported', 'luks': 'BlockdevCreateOptionsLUKS', - 'nbd': 'BlockdevCreateNotSupported', 'nfs': 'BlockdevCreateOptionsNfs', - 'null-aio': 'BlockdevCreateNotSupported', - 'null-co': 'BlockdevCreateNotSupported', - 'nvme': 'BlockdevCreateNotSupported', 'parallels': 'BlockdevCreateOptionsParallels', 'qcow': 'BlockdevCreateOptionsQcow', 'qcow2': 'BlockdevCreateOptionsQcow2', 'qed': 'BlockdevCreateOptionsQed', - 'quorum': 'BlockdevCreateNotSupported', - 'raw': 'BlockdevCreateNotSupported', 'rbd': 'BlockdevCreateOptionsRbd', - 'replication': 'BlockdevCreateNotSupported', 'sheepdog': 'BlockdevCreateOptionsSheepdog', 'ssh': 'BlockdevCreateOptionsSsh', - 'throttle': 'BlockdevCreateNotSupported', 'vdi': 'BlockdevCreateOptionsVdi', 'vhdx': 'BlockdevCreateOptionsVhdx', - 'vmdk': 'BlockdevCreateNotSupported', - 'vpc': 'BlockdevCreateOptionsVpc', - 'vvfat': 'BlockdevCreateNotSupported', - 'vxhs': 'BlockdevCreateNotSupported' + 'vpc': 'BlockdevCreateOptionsVpc' } } ## diff --git a/qapi/crypto.json b/qapi/crypto.json index 288bc056ef..a51b434412 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -297,16 +297,6 @@ 'uuid': 'str', 'slots': [ 'QCryptoBlockInfoLUKSSlot' ] }} -## -# @QCryptoBlockInfoQCow: -# -# Information about the QCow block encryption options -# -# Since: 2.7 -## -{ 'struct': 'QCryptoBlockInfoQCow', - 'data': { }} - ## # @QCryptoBlockInfo: @@ -318,5 +308,4 @@ { 'union': 'QCryptoBlockInfo', 'base': 'QCryptoBlockInfoBase', 'discriminator': 'format', - 'data': { 'qcow': 'QCryptoBlockInfoQCow', - 'luks': 'QCryptoBlockInfoLUKS' } } + 'data': { 'luks': 'QCryptoBlockInfoLUKS' } } diff --git a/qapi/misc.json b/qapi/misc.json index fa86831ec3..c6bc18a859 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -396,8 +396,7 @@ 'mips': 'CpuInfoMIPS', 'tricore': 'CpuInfoTricore', 's390': 'CpuInfoS390', - 'riscv': 'CpuInfoRISCV', - 'other': 'CpuInfoOther' } } + 'riscv': 'CpuInfoRISCV' } } ## # @CpuInfoX86: @@ -467,16 +466,6 @@ ## { 'struct': 'CpuInfoRISCV', 'data': { 'pc': 'int' } } -## -# @CpuInfoOther: -# -# No additional information is available about the virtual CPU -# -# Since: 2.6 -# -## -{ 'struct': 'CpuInfoOther', 'data': { } } - ## # @CpuS390State: # @@ -578,38 +567,7 @@ 'arch' : 'CpuInfoArch', 'target' : 'SysEmuTarget' }, 'discriminator' : 'target', - 'data' : { 'aarch64' : 'CpuInfoOther', - 'alpha' : 'CpuInfoOther', - 'arm' : 'CpuInfoOther', - 'cris' : 'CpuInfoOther', - 'hppa' : 'CpuInfoOther', - 'i386' : 'CpuInfoOther', - 'lm32' : 'CpuInfoOther', - 'm68k' : 'CpuInfoOther', - 'microblaze' : 'CpuInfoOther', - 'microblazeel' : 'CpuInfoOther', - 'mips' : 'CpuInfoOther', - 'mips64' : 'CpuInfoOther', - 'mips64el' : 'CpuInfoOther', - 'mipsel' : 'CpuInfoOther', - 'moxie' : 'CpuInfoOther', - 'nios2' : 'CpuInfoOther', - 'or1k' : 'CpuInfoOther', - 'ppc' : 'CpuInfoOther', - 'ppc64' : 'CpuInfoOther', - 'ppcemb' : 'CpuInfoOther', - 'riscv32' : 'CpuInfoOther', - 'riscv64' : 'CpuInfoOther', - 's390x' : 'CpuInfoS390', - 'sh4' : 'CpuInfoOther', - 'sh4eb' : 'CpuInfoOther', - 'sparc' : 'CpuInfoOther', - 'sparc64' : 'CpuInfoOther', - 'tricore' : 'CpuInfoOther', - 'unicore32' : 'CpuInfoOther', - 'x86_64' : 'CpuInfoOther', - 'xtensa' : 'CpuInfoOther', - 'xtensaeb' : 'CpuInfoOther' } } + 'data' : { 's390x' : 'CpuInfoS390' } } ## # @query-cpus-fast: diff --git a/qapi/net.json b/qapi/net.json index 6b7d93cb59..c86f351161 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -88,16 +88,6 @@ ## { 'command': 'netdev_del', 'data': {'id': 'str'} } -## -# @NetdevNoneOptions: -# -# Use it alone to have zero network devices. -# -# Since: 1.2 -## -{ 'struct': 'NetdevNoneOptions', - 'data': { } } - ## # @NetLegacyNicOptions: # @@ -477,7 +467,6 @@ 'base': { 'id': 'str', 'type': 'NetClientDriver' }, 'discriminator': 'type', 'data': { - 'none': 'NetdevNoneOptions', 'nic': 'NetLegacyNicOptions', 'user': 'NetdevUserOptions', 'tap': 'NetdevTapOptions', @@ -530,7 +519,6 @@ 'base': { 'type': 'NetLegacyOptionsType' }, 'discriminator': 'type', 'data': { - 'none': 'NetdevNoneOptions', 'nic': 'NetLegacyNicOptions', 'user': 'NetdevUserOptions', 'tap': 'NetdevTapOptions', diff --git a/qapi/ui.json b/qapi/ui.json index fc18a05f0f..f48d2a0afb 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -995,17 +995,6 @@ 'events' : [ 'InputEvent' ] } } -## -# @DisplayNoOpts: -# -# Empty struct for displays without config options. -# -# Since: 2.12 -# -## -{ 'struct' : 'DisplayNoOpts', - 'data' : { } } - ## # @DisplayGTK: # @@ -1068,10 +1057,4 @@ '*window-close' : 'bool', '*gl' : 'DisplayGLMode' }, 'discriminator' : 'type', - 'data' : { 'default' : 'DisplayNoOpts', - 'none' : 'DisplayNoOpts', - 'gtk' : 'DisplayGTK', - 'sdl' : 'DisplayNoOpts', - 'egl-headless' : 'DisplayNoOpts', - 'curses' : 'DisplayNoOpts', - 'cocoa' : 'DisplayNoOpts' } } + 'data' : { 'gtk' : 'DisplayGTK' } } From de685ae5e9a4b523513033bd6cadc8187a227170 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 18 Jun 2018 19:59:57 +0200 Subject: [PATCH 1559/2380] qapi: Open files with encoding='utf-8' Python 2 happily reads UTF-8 files in text mode, but Python 3 requires either UTF-8 locale or an explicit encoding passed to open(). Commit d4e5ec877ca fixed this by setting the en_US.UTF-8 locale. Falls apart when the locale isn't be available. Matthias Maier and Arfrever Frehtes Taifersar Arahesis proposed to use binary mode instead, with manual conversion from bytes to str. Works, but opening with an explicit encoding is simpler, so do that. Since Python 2's open() doesn't support the encoding parameter, we need to suppress it with a version check. Reported-by: Arfrever Frehtes Taifersar Arahesis Reported-by: Matthias Maier Signed-off-by: Markus Armbruster Message-Id: <20180618175958.29073-2-armbru@redhat.com> Reviewed-by: Eduardo Habkost Reviewed-by: Eric Blake --- scripts/qapi/common.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 4b53f08627..8b6708dbf1 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -16,6 +16,7 @@ import errno import os import re import string +import sys from collections import OrderedDict builtin_types = { @@ -340,7 +341,10 @@ class QAPISchemaParser(object): return None try: - fobj = open(incl_fname, 'r') + if sys.version_info[0] >= 3: + fobj = open(incl_fname, 'r', encoding='utf-8') + else: + fobj = open(incl_fname, 'r') except IOError as e: raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname)) return QAPISchemaParser(fobj, previously_included, info) @@ -1493,7 +1497,11 @@ class QAPISchemaEvent(QAPISchemaEntity): class QAPISchema(object): def __init__(self, fname): self._fname = fname - parser = QAPISchemaParser(open(fname, 'r')) + if sys.version_info[0] >= 3: + f = open(fname, 'r', encoding='utf-8') + else: + f = open(fname, 'r') + parser = QAPISchemaParser(f) exprs = check_exprs(parser.exprs) self.docs = parser.docs self._entity_list = [] @@ -2007,7 +2015,10 @@ class QAPIGen(object): if e.errno != errno.EEXIST: raise fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) - f = os.fdopen(fd, 'r+') + if sys.version_info[0] >= 3: + f = open(fd, 'r+', encoding='utf-8') + else: + f = os.fdopen(fd, 'r+') text = (self._top(fname) + self._preamble + self._body + self._bottom(fname)) oldtext = f.read(len(text) + 1) From 0d6b93deeeb3cc190692d629f5927befdc8b1fb8 Mon Sep 17 00:00:00 2001 From: Matthias Maier Date: Mon, 18 Jun 2018 19:59:58 +0200 Subject: [PATCH 1560/2380] Revert commit d4e5ec877ca This commit removes the PYTHON_UTF8 workaround. The problem with setting LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 is that the en_US.UTF-8 locale might not be available. In this case setting above locales results in build errors even though another UTF-8 locale was originally set [1]. The only stable way of fixing the encoding problem is by specifying the encoding in Python, like the previous commit does. [1] https://bugs.gentoo.org/657766 Signed-off-by: Arfrever Frehtes Taifersar Arahesis Signed-off-by: Matthias Maier Message-Id: <20180618175958.29073-3-armbru@redhat.com> Reviewed-by: Eduardo Habkost Reviewed-by: Eric Blake [Commit message tweaked] Signed-off-by: Markus Armbruster --- Makefile | 6 ++---- tests/Makefile.include | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index e46f2b625a..7ed9cc4a21 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,6 @@ ifneq ($(wildcard config-host.mak),) all: include config-host.mak -PYTHON_UTF8 = LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 $(PYTHON) - git-submodule-update: .PHONY: git-submodule-update @@ -576,7 +574,7 @@ qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \ qga/qapi-generated/qga-qapi-doc.texi: \ qga/qapi-generated/qapi-gen-timestamp ; qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) - $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o qga/qapi-generated -p "qga-" $<, \ "GEN","$(@:%-timestamp=%)") @>$@ @@ -676,7 +674,7 @@ qapi/qapi-introspect.h qapi/qapi-introspect.c \ qapi/qapi-doc.texi: \ qapi-gen-timestamp ; qapi-gen-timestamp: $(qapi-modules) $(qapi-py) - $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o "qapi" -b $<, \ "GEN","$(@:%-timestamp=%)") @>$@ diff --git a/tests/Makefile.include b/tests/Makefile.include index 8b45f01d25..e8bb2d8f66 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -678,13 +678,13 @@ tests/test-qapi-events.c tests/test-qapi-events.h \ tests/test-qapi-introspect.c tests/test-qapi-introspect.h: \ tests/test-qapi-gen-timestamp ; tests/test-qapi-gen-timestamp: $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(qapi-py) - $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o tests -p "test-" $<, \ "GEN","$(@:%-timestamp=%)") @>$@ tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.json $(qapi-py) - $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o tests/qapi-schema -p "doc-good-" $<, \ "GEN","$@") @mv tests/qapi-schema/doc-good-qapi-doc.texi $@ @@ -978,7 +978,7 @@ check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) .PHONY: $(patsubst %, check-%, $(check-qapi-schema-y)) $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json $(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts \ - $(PYTHON_UTF8) $(SRC_PATH)/tests/qapi-schema/test-qapi.py \ + $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py \ $^ >$*.test.out 2>$*.test.err; \ echo $$? >$*.test.exit, \ "TEST","$*.out") From da112e83c188ebe0fe5946c78ae7793341c93955 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Jun 2018 14:47:42 +0200 Subject: [PATCH 1561/2380] qapi/introspect: Eliminate pointless variable in .visit_end() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1a9a507b2e3 "qapi-introspect: Hide type names" added local variable @jsons to improve sorting, but also removed the sorting. It was part of a big series that went to v8, and it made sense until v2 or so... Commit 7d0f982bfbb replaced @jsons by @qlits, preserving the uselessness. Get rid of it. Signed-off-by: Markus Armbruster Message-Id: <20180620124742.16979-1-armbru@redhat.com> Reviewed-by: Marc-André Lureau --- scripts/qapi/introspect.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 5b6c72c7b2..6ad198ae5b 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -75,13 +75,10 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): def visit_end(self): # visit the types that are actually used - qlits = self._qlits - self._qlits = [] for typ in self._used_types: typ.visit(self) # generate C # TODO can generate awfully long lines - qlits.extend(self._qlits) name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit' self._genh.add(mcgen(''' #include "qapi/qmp/qlit.h" @@ -93,7 +90,7 @@ extern const QLitObject %(c_name)s; const QLitObject %(c_name)s = %(c_string)s; ''', c_name=c_name(name), - c_string=to_qlit(qlits))) + c_string=to_qlit(self._qlits))) self._schema = None self._qlits = [] self._used_types = [] From be25fcc4d2faeb3ffa8db813272963bae659c4c2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Jun 2018 14:48:27 +0200 Subject: [PATCH 1562/2380] MAINTAINERS: Update QAPI stanza for commit fb0bc835e56 Commit fb0bc835e56 moved code from scripts/qapi-*.py to scripts/qapi/*.py. It neglected to update MAINTAINERS: scripts/qapi* matches only the former, not the latter. Do that now. Signed-off-by: Markus Armbruster Message-Id: <20180620124827.17106-1-armbru@redhat.com> Reviewed-by: Eric Blake --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index f222bf8b16..8270863f3f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1623,7 +1623,8 @@ F: tests/test-*-visitor.c F: tests/test-qapi-*.c F: tests/test-qmp-*.c F: tests/test-visitor-serialization.c -F: scripts/qapi* +F: scripts/qapi-gen.py +F: scripts/qapi/* F: docs/devel/qapi* T: git git://repo.or.cz/qemu/armbru.git qapi-next From f28d0dfdce5754d80d2a5993fff2f5312b32cac1 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Fri, 22 Jun 2018 13:45:31 -0400 Subject: [PATCH 1563/2380] tcg: fix --disable-tcg build breakage Fix the --disable-tcg breakage introduced by tb_lock's removal by relying on the fact that tcg_enabled() is set to 0 at compile-time under --disable-tcg. While at it, add further asserts to fix builds that enable both --disable-tcg and --enable-debug, which were broken even before tb_lock's removal. Tested to build x86_64-softmmu and i386-softmmu targets. Reported-by: Peter Maydell Signed-off-by: Emilio G. Cota Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell --- accel/stubs/tcg-stub.c | 4 ---- cpus.c | 4 ++++ exec.c | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index ee575a8718..76ae461749 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -21,10 +21,6 @@ void tb_flush(CPUState *cpu) { } -void tb_unlock(void) -{ -} - void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) { } diff --git a/cpus.c b/cpus.c index 19c5d37108..b5844b7103 100644 --- a/cpus.c +++ b/cpus.c @@ -1355,6 +1355,7 @@ static int tcg_cpu_exec(CPUState *cpu) int64_t ti; #endif + assert(tcg_enabled()); #ifdef CONFIG_PROFILER ti = profile_getclock(); #endif @@ -1397,6 +1398,7 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) { CPUState *cpu = arg; + assert(tcg_enabled()); rcu_register_thread(); tcg_register_thread(); @@ -1631,6 +1633,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) { CPUState *cpu = arg; + assert(tcg_enabled()); g_assert(!use_icount); rcu_register_thread(); @@ -1854,6 +1857,7 @@ static void qemu_tcg_init_vcpu(CPUState *cpu) static QemuThread *single_tcg_cpu_thread; static int tcg_region_inited; + assert(tcg_enabled()); /* * Initialize TCG regions--once. Now is a good time, because: * (1) TCG's init context, prologue and target globals have been set up. diff --git a/exec.c b/exec.c index 28f9bdcbf9..88edb59060 100644 --- a/exec.c +++ b/exec.c @@ -1323,6 +1323,7 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) RAMBlock *block; ram_addr_t end; + assert(tcg_enabled()); end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; @@ -2655,6 +2656,7 @@ void memory_notdirty_write_prepare(NotDirtyInfo *ndi, void memory_notdirty_write_complete(NotDirtyInfo *ndi) { if (ndi->pages) { + assert(tcg_enabled()); page_collection_unlock(ndi->pages); ndi->pages = NULL; } @@ -3046,6 +3048,7 @@ static void tcg_commit(MemoryListener *listener) CPUAddressSpace *cpuas; AddressSpaceDispatch *d; + assert(tcg_enabled()); /* since each CPU stores ram addresses in its TLB cache, we must reset the modified entries */ cpuas = container_of(listener, CPUAddressSpace, tcg_as_listener); From ed78467a214595a63af7800a073a03ffe37cd7db Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Fri, 8 Jun 2018 18:56:17 -0400 Subject: [PATCH 1564/2380] i386: Add support for CPUID_8000_001E for AMD Add support for cpuid leaf CPUID_8000_001E. Build the config that closely match the underlying hardware. Please refer to the Processor Programming Reference (PPR) for AMD Family 17h Model for more details. Signed-off-by: Babu Moger Message-Id: <1528498581-131037-2-git-send-email-babu.moger@amd.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1e69e68f25..86fb1a4fb8 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -427,6 +427,87 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, CPUState *cs, (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); } +/* Data structure to hold the configuration info for a given core index */ +struct core_topology { + /* core complex id of the current core index */ + int ccx_id; + /* + * Adjusted core index for this core in the topology + * This can be 0,1,2,3 with max 4 cores in a core complex + */ + int core_id; + /* Node id for this core index */ + int node_id; + /* Number of nodes in this config */ + int num_nodes; +}; + +/* + * Build the configuration closely match the EPYC hardware. Using the EPYC + * hardware configuration values (MAX_CCX, MAX_CORES_IN_CCX, MAX_CORES_IN_NODE) + * right now. This could change in future. + * nr_cores : Total number of cores in the config + * core_id : Core index of the current CPU + * topo : Data structure to hold all the config info for this core index + */ +static void build_core_topology(int nr_cores, int core_id, + struct core_topology *topo) +{ + int nodes, cores_in_ccx; + + /* First get the number of nodes required */ + nodes = nodes_in_socket(nr_cores); + + cores_in_ccx = cores_in_core_complex(nr_cores); + + topo->node_id = core_id / (cores_in_ccx * MAX_CCX); + topo->ccx_id = (core_id % (cores_in_ccx * MAX_CCX)) / cores_in_ccx; + topo->core_id = core_id % cores_in_ccx; + topo->num_nodes = nodes; +} + +/* Encode cache info for CPUID[8000001E] */ +static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + struct core_topology topo = {0}; + + build_core_topology(cs->nr_cores, cpu->core_id, &topo); + *eax = cpu->apic_id; + /* + * CPUID_Fn8000001E_EBX + * 31:16 Reserved + * 15:8 Threads per core (The number of threads per core is + * Threads per core + 1) + * 7:0 Core id (see bit decoding below) + * SMT: + * 4:3 node id + * 2 Core complex id + * 1:0 Core id + * Non SMT: + * 5:4 node id + * 3 Core complex id + * 1:0 Core id + */ + if (cs->nr_threads - 1) { + *ebx = ((cs->nr_threads - 1) << 8) | (topo.node_id << 3) | + (topo.ccx_id << 2) | topo.core_id; + } else { + *ebx = (topo.node_id << 4) | (topo.ccx_id << 3) | topo.core_id; + } + /* + * CPUID_Fn8000001E_ECX + * 31:11 Reserved + * 10:8 Nodes per processor (Nodes per processor is number of nodes + 1) + * 7:0 Node id (see bit decoding below) + * 2 Socket id + * 1:0 Node id + */ + *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << 2) | topo.node_id; + *edx = 0; +} + /* * Definitions of the hardcoded cache entries we expose: * These are legacy cache values. If there is a need to change any @@ -4120,6 +4201,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; } break; + case 0x8000001E: + assert(cpu->core_id <= 255); + encode_topo_cpuid8000001e(cs, cpu, + eax, ebx, ecx, edx); + break; case 0xC0000000: *eax = env->cpuid_xlevel2; *ebx = 0; From 081492ca8ab23f90d56414595ecfd09ddbacb8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 6 Jun 2018 17:55:25 +0100 Subject: [PATCH 1565/2380] i386: improve alignment of CPU model listing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the addition of the -IBRS CPU model variants, the descriptions shown by '-cpu help' are not well aligned, as several model names overflow the space allowed. Right aligning the CPU model names is also not attractive, because it obscures the common name prefixes of many models. The CPU model name field needs to be 4 characters larger, and be left aligned instead. Signed-off-by: Daniel P. Berrangé Message-Id: <20180606165527.17365-2-berrange@redhat.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 86fb1a4fb8..e1d7157d8c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3380,7 +3380,7 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) desc = cc->cpu_def->model_id; } - (*s->cpu_fprintf)(s->file, "x86 %16s %-48s\n", + (*s->cpu_fprintf)(s->file, "x86 %-20s %-48s\n", name, desc); g_free(name); } From c7dbff4b3a6655f4769e6d283f34191122450384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 6 Jun 2018 17:55:26 +0100 Subject: [PATCH 1566/2380] i386: improve sorting of CPU model names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current list of CPU model names output by "-cpu help" is sorted alphabetically based on the internal QOM class name. The text that is displayed, however, uses the CPU model name, which is equivalent to the QOM class name, minus a suffix. Unfortunately that suffix has an effect on the sort ordering, for example, causing the various Broadwell variants to appear reversed: x86 486 x86 Broadwell-IBRS Intel Core Processor (Broadwell, IBRS) x86 Broadwell-noTSX-IBRS Intel Core Processor (Broadwell, no TSX, IBRS x86 Broadwell-noTSX Intel Core Processor (Broadwell, no TSX) x86 Broadwell Intel Core Processor (Broadwell) x86 Conroe Intel Celeron_4x0 (Conroe/Merom Class Core 2) By sorting on the actual CPU model name text that is displayed, the result is x86 486 x86 Broadwell Intel Core Processor (Broadwell) x86 Broadwell-IBRS Intel Core Processor (Broadwell, IBRS) x86 Broadwell-noTSX Intel Core Processor (Broadwell, no TSX) x86 Broadwell-noTSX-IBRS Intel Core Processor (Broadwell, no TSX, IBRS) x86 Conroe Intel Celeron_4x0 (Conroe/Merom Class Core 2) This requires extra string allocations during sorting, but this is not a concern given the usage scenario and the number of CPU models that exist. Signed-off-by: Daniel P. Berrangé Message-Id: <20180606165527.17365-3-berrange@redhat.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index e1d7157d8c..19ac1e6569 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3351,15 +3351,19 @@ static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b) ObjectClass *class_b = (ObjectClass *)b; X86CPUClass *cc_a = X86_CPU_CLASS(class_a); X86CPUClass *cc_b = X86_CPU_CLASS(class_b); - const char *name_a, *name_b; + char *name_a, *name_b; + int ret; if (cc_a->ordering != cc_b->ordering) { - return cc_a->ordering - cc_b->ordering; + ret = cc_a->ordering - cc_b->ordering; } else { - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); + name_a = x86_cpu_class_get_model_name(cc_a); + name_b = x86_cpu_class_get_model_name(cc_b); + ret = strcmp(name_a, name_b); + g_free(name_a); + g_free(name_b); } + return ret; } static GSList *get_sorted_cpu_model_list(void) From cc643b1e7898414b56f551bbd42d4ed8c2ae127a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 6 Jun 2018 17:55:27 +0100 Subject: [PATCH 1567/2380] i386: display known CPUID features linewrapped, in alphabetical order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using '-cpu help' the list of CPUID features is grouped according to the internal low level CPUID grouping. The data printed results in very long lines too. This combines to make it hard for users to read the output and identify if QEMU knows about the feature they wish to use. This change gets rid of the grouping of features and treats all flags as single list. The list is sorted into alphabetical order and the printing with line wrapping at the 77th column. Signed-off-by: Daniel P. Berrangé Message-Id: <20180606165527.17365-4-berrange@redhat.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 19ac1e6569..9da4920421 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3331,17 +3331,21 @@ static void x86_cpu_class_check_missing_features(X86CPUClass *xcc, /* Print all cpuid feature names in featureset */ -static void listflags(FILE *f, fprintf_function print, const char **featureset) +static void listflags(FILE *f, fprintf_function print, GList *features) { - int bit; - bool first = true; + size_t len = 0; + GList *tmp; - for (bit = 0; bit < 32; bit++) { - if (featureset[bit]) { - print(f, "%s%s", first ? "" : " ", featureset[bit]); - first = false; + for (tmp = features; tmp; tmp = tmp->next) { + const char *name = tmp->data; + if ((len + strlen(name) + 1) >= 75) { + print(f, "\n"); + len = 0; } + print(f, "%s%s", len == 0 ? " " : " ", name); + len += strlen(name) + 1; } + print(f, "\n"); } /* Sort alphabetically by type name, respecting X86CPUClass::ordering. */ @@ -3392,26 +3396,35 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) /* list available CPU models and flags */ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf) { - int i; + int i, j; CPUListState s = { .file = f, .cpu_fprintf = cpu_fprintf, }; GSList *list; + GList *names = NULL; (*cpu_fprintf)(f, "Available CPUs:\n"); list = get_sorted_cpu_model_list(); g_slist_foreach(list, x86_cpu_list_entry, &s); g_slist_free(list); - (*cpu_fprintf)(f, "\nRecognized CPUID flags:\n"); + names = NULL; for (i = 0; i < ARRAY_SIZE(feature_word_info); i++) { FeatureWordInfo *fw = &feature_word_info[i]; - - (*cpu_fprintf)(f, " "); - listflags(f, cpu_fprintf, fw->feat_names); - (*cpu_fprintf)(f, "\n"); + for (j = 0; j < 32; j++) { + if (fw->feat_names[j]) { + names = g_list_append(names, (gpointer)fw->feat_names[j]); + } + } } + + names = g_list_sort(names, (GCompareFunc)strcmp); + + (*cpu_fprintf)(f, "\nRecognized CPUID flags:\n"); + listflags(f, cpu_fprintf, names); + (*cpu_fprintf)(f, "\n"); + g_list_free(names); } static void x86_cpu_definition_entry(gpointer data, gpointer user_data) From f1a23522b03a569f13aad49294bb4c4b1a9500c7 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 11 Jun 2018 17:38:55 -0300 Subject: [PATCH 1568/2380] i386: Remove osxsave CPUID flag name OSXAVE is not a static feature flag: it changes dynamically at runtime depending on CR4, and it was never configurable: KVM never returned OSXSAVE on GET_SUPPORTED_CPUID, and it is not included in TCG_EXT_FEATURES. Remove OSXSAVE from the feature name array so users don't try to configure it manually. Signed-off-by: Eduardo Habkost Message-Id: <20180611203855.13269-1-ehabkost@redhat.com> Reviewed-by: Richard Henderson Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 9da4920421..60deae3100 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -788,7 +788,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "fma", "cx16", "xtpr", "pdcm", NULL, "pcid", "dca", "sse4.1", "sse4.2", "x2apic", "movbe", "popcnt", - "tsc-deadline", "aes", "xsave", "osxsave", + "tsc-deadline", "aes", "xsave", NULL /* osxsave */, "avx", "f16c", "rdrand", "hypervisor", }, .cpuid_eax = 1, .cpuid_reg = R_ECX, From 9ccb9784b57804f5c74434ad6ccb66650a015ffc Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 11 Jun 2018 17:37:12 -0300 Subject: [PATCH 1569/2380] i386: Remove ospke CPUID flag name OSPKE is not a static feature flag: it changes dynamically at runtime depending on CR4, and it was never configurable: KVM never returned OSPKE on GET_SUPPORTED_CPUID, and on TCG enables it automatically if CR4_PKE_MASK is set. Remove OSPKE from the feature name array so users don't try to configure it manually. Signed-off-by: Eduardo Habkost Message-Id: <20180611203712.12086-1-ehabkost@redhat.com> Reviewed-by: Richard Henderson Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 60deae3100..d7dfefcde0 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -738,7 +738,8 @@ static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM, CPUID_7_0_EBX_RDSEED */ -#define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_OSPKE | \ +#define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_PKU | \ + /* CPUID_7_0_ECX_OSPKE is dynamic */ \ CPUID_7_0_ECX_LA57) #define TCG_7_0_EDX_FEATURES 0 #define TCG_APM_FEATURES 0 @@ -955,7 +956,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_7_0_ECX] = { .feat_names = { NULL, "avx512vbmi", "umip", "pku", - "ospke", NULL, "avx512vbmi2", NULL, + NULL /* ospke */, NULL, "avx512vbmi2", NULL, "gfni", "vaes", "vpclmulqdq", "avx512vnni", "avx512bitalg", NULL, "avx512-vpopcntdq", NULL, "la57", NULL, NULL, NULL, From a764f3f7197f4d7ad8fe8424269933de912224cb Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 1 Jun 2018 11:38:08 -0400 Subject: [PATCH 1570/2380] i386: define the AMD 'amd-ssbd' CPUID feature bit AMD future CPUs expose _two_ ways to utilize the Intel equivalant of the Speculative Store Bypass Disable. The first is via the virtualized VIRT_SPEC CTRL MSR (0xC001_011f) and the second is via the SPEC_CTRL MSR (0x48). The document titled: 124441_AMD64_SpeculativeStoreBypassDisable_Whitepaper_final.pdf gives priority of SPEC CTRL MSR over the VIRT SPEC CTRL MSR. A copy of this document is available at https://bugzilla.kernel.org/show_bug.cgi?id=199889 Anyhow, this means that on future AMD CPUs there will be _two_ ways to deal with SSBD. Signed-off-by: Konrad Rzeszutek Wilk Message-Id: <20180601153809.15259-2-konrad.wilk@oracle.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d7dfefcde0..7234bebfcb 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1009,7 +1009,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "ibpb", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, "virt-ssbd", NULL, NULL, + "amd-ssbd", "virt-ssbd", NULL, NULL, NULL, NULL, NULL, NULL, }, .cpuid_eax = 0x80000008, From 254790a909a2f153d689bfa7d8e8f0386cda870d Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 1 Jun 2018 11:38:09 -0400 Subject: [PATCH 1571/2380] i386: Define AMD's no SSB mitigation needed. AMD future CPUs expose a mechanism to tell the guest that the Speculative Store Bypass Disable is not needed and that the CPU is all good. This is exposed via the CPUID 8000_0008.EBX[26] bit. See 124441_AMD64_SpeculativeStoreBypassDisable_Whitepaper_final.pdf A copy of this document is available at https://bugzilla.kernel.org/show_bug.cgi?id=199889 Signed-off-by: Konrad Rzeszutek Wilk Message-Id: <20180601153809.15259-3-konrad.wilk@oracle.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 7234bebfcb..7a4484bb06 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1009,7 +1009,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "ibpb", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - "amd-ssbd", "virt-ssbd", NULL, NULL, + "amd-ssbd", "virt-ssbd", "amd-no-ssb", NULL, NULL, NULL, NULL, NULL, }, .cpuid_eax = 0x80000008, From f98bbd8304112187cafc3e636c31b2a3865d2717 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Wed, 13 Jun 2018 21:18:22 -0400 Subject: [PATCH 1572/2380] i386: Allow TOPOEXT to be enabled on older kernels Enabling TOPOEXT feature might cause compatibility issues if older kernels does not set this feature. Lets set this feature unconditionally. Signed-off-by: Babu Moger Message-Id: <1528939107-17193-2-git-send-email-babu.moger@amd.com> [ehabkost: rewrite comment and commit message] Signed-off-by: Eduardo Habkost --- target/i386/kvm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 445e0e0b11..2d174f3a91 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -372,6 +372,13 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, if (host_tsx_blacklisted()) { ret &= ~(CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_HLE); } + } else if (function == 0x80000001 && reg == R_ECX) { + /* + * It's safe to enable TOPOEXT even if it's not returned by + * GET_SUPPORTED_CPUID. Unconditionally enabling TOPOEXT here allows + * us to keep CPU models including TOPOEXT runnable on older kernels. + */ + ret |= CPUID_EXT3_TOPOEXT; } else if (function == 0x80000001 && reg == R_EDX) { /* On Intel, kvm returns cpuid according to the Intel spec, * so add missing bits according to the AMD spec: From 631be32155dbafa1fe886f2488127956c9120ba6 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Tue, 19 Jun 2018 17:31:57 -0400 Subject: [PATCH 1573/2380] i386: Fix up the Node id for CPUID_8000_001E This is part of topoext support. To keep the compatibility, it is better we support all the combination of nr_cores and nr_threads currently supported. By allowing more nr_cores and nr_threads, we might end up with more nodes than we can actually support with the real hardware. We need to fix up the node id to make this work. We can achieve this by shifting the socket_id bits left to address more nodes. Signed-off-by: Babu Moger Message-Id: <1529443919-67509-2-git-send-email-babu.moger@amd.com> Reviewed-by: Eduardo Habkost Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 7a4484bb06..130391c840 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/bitops.h" #include "cpu.h" #include "exec/exec-all.h" @@ -472,6 +473,8 @@ static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, uint32_t *ecx, uint32_t *edx) { struct core_topology topo = {0}; + unsigned long nodes; + int shift; build_core_topology(cs->nr_cores, cpu->core_id, &topo); *eax = cpu->apic_id; @@ -504,7 +507,28 @@ static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, * 2 Socket id * 1:0 Node id */ - *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << 2) | topo.node_id; + if (topo.num_nodes <= 4) { + *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << 2) | + topo.node_id; + } else { + /* + * Node id fix up. Actual hardware supports up to 4 nodes. But with + * more than 32 cores, we may end up with more than 4 nodes. + * Node id is a combination of socket id and node id. Only requirement + * here is that this number should be unique accross the system. + * Shift the socket id to accommodate more nodes. We dont expect both + * socket id and node id to be big number at the same time. This is not + * an ideal config but we need to to support it. Max nodes we can have + * is 32 (255/8) with 8 cores per node and 255 max cores. We only need + * 5 bits for nodes. Find the left most set bit to represent the total + * number of nodes. find_last_bit returns last set bit(0 based). Left + * shift(+1) the socket id to represent all the nodes. + */ + nodes = topo.num_nodes - 1; + shift = find_last_bit(&nodes, 8); + *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << (shift + 1)) | + topo.node_id; + } *edx = 0; } From e00516475c270dcb6705753da96063f95699abf2 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Tue, 19 Jun 2018 17:31:58 -0400 Subject: [PATCH 1574/2380] i386: Enable TOPOEXT feature on AMD EPYC CPU Enable TOPOEXT feature on EPYC CPU. This is required to support hyperthreading on VM guests. Also extend xlevel to 0x8000001E. Disable topoext on PC_COMPAT_2_12 and keep xlevel 0x8000000a. Signed-off-by: Babu Moger Message-Id: <1529443919-67509-3-git-send-email-babu.moger@amd.com> [ehabkost: Added EPYC-IBPB.xlevel to PC_COMPAT_2_12] Signed-off-by: Eduardo Habkost --- include/hw/i386/pc.h | 12 ++++++++++++ target/i386/cpu.c | 10 ++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index fc8dedca12..316230e570 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -303,6 +303,18 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .driver = TYPE_X86_CPU,\ .property = "legacy-cache",\ .value = "on",\ + },{\ + .driver = TYPE_X86_CPU,\ + .property = "topoext",\ + .value = "off",\ + },{\ + .driver = "EPYC-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "EPYC-IBPB" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ }, #define PC_COMPAT_2_11 \ diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 130391c840..d6ed29b484 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2579,7 +2579,8 @@ static X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_8000_0001_ECX] = CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | - CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, + CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | + CPUID_EXT3_TOPOEXT, .features[FEAT_7_0_EBX] = CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | @@ -2594,7 +2595,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_XSAVE_XGETBV1, .features[FEAT_6_EAX] = CPUID_6_EAX_ARAT, - .xlevel = 0x8000000A, + .xlevel = 0x8000001E, .model_id = "AMD EPYC Processor", .cache_info = &epyc_cache_info, }, @@ -2624,7 +2625,8 @@ static X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_8000_0001_ECX] = CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | - CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, + CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | + CPUID_EXT3_TOPOEXT, .features[FEAT_8000_0008_EBX] = CPUID_8000_0008_EBX_IBPB, .features[FEAT_7_0_EBX] = @@ -2641,7 +2643,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_XSAVE_XGETBV1, .features[FEAT_6_EAX] = CPUID_6_EAX_ARAT, - .xlevel = 0x8000000A, + .xlevel = 0x8000001E, .model_id = "AMD EPYC Processor (with IBPB)", .cache_info = &epyc_cache_info, }, From 6b2942f966d5e54c37d305c80f5f98d504c2bc55 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Tue, 19 Jun 2018 17:31:59 -0400 Subject: [PATCH 1575/2380] i386: Remove generic SMT thread check Remove generic non-intel check while validating hyperthreading support. Certain AMD CPUs can support hyperthreading now. CPU family with TOPOEXT feature can support hyperthreading now. Signed-off-by: Babu Moger Tested-by: Geoffrey McRae Reviewed-by: Eduardo Habkost Message-Id: <1529443919-67509-4-git-send-email-babu.moger@amd.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d6ed29b484..e6c2f8a22a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -4985,17 +4985,22 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) qemu_init_vcpu(cs); - /* Only Intel CPUs support hyperthreading. Even though QEMU fixes this - * issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX - * based on inputs (sockets,cores,threads), it is still better to gives + /* + * Most Intel and certain AMD CPUs support hyperthreading. Even though QEMU + * fixes this issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX + * based on inputs (sockets,cores,threads), it is still better to give * users a warning. * * NOTE: the following code has to follow qemu_init_vcpu(). Otherwise * cs->nr_threads hasn't be populated yet and the checking is incorrect. */ - if (!IS_INTEL_CPU(env) && cs->nr_threads > 1 && !ht_warned) { - error_report("AMD CPU doesn't support hyperthreading. Please configure" - " -smp options properly."); + if (IS_AMD_CPU(env) && + !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && + cs->nr_threads > 1 && !ht_warned) { + error_report("This family of AMD CPU doesn't support " + "hyperthreading(%d). Please configure -smp " + "options properly or try enabling topoext feature.", + cs->nr_threads); ht_warned = true; } From bf20b675cc5a2568f1105c3577663978c5a13108 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 21 Jun 2018 14:54:51 -0300 Subject: [PATCH 1576/2380] Partially revert "python: futurize -f libfuturize.fixes.fix_absolute_import" Since commit 068cf7a44cd4d65c05aa877dbebced295be5ce44, qmp-shell is broken: $ ./scripts/qmp/qmp-shell Traceback (most recent call last): File "./scripts/qmp/qmp-shell", line 70, in from . import qmp ValueError: Attempted relative import in non-package Relative imports don't work on scripts that are executed directly, so revert the change on the scripts inside scripts/qmp. Fixes: 068cf7a44cd4d65c05aa877dbebced295be5ce44 Reported-by: John Snow Signed-off-by: Eduardo Habkost Message-Id: <20180621175451.7948-1-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/qmp/qemu-ga-client | 3 +-- scripts/qmp/qmp | 3 +-- scripts/qmp/qmp-shell | 3 +-- scripts/qmp/qom-fuse | 3 +-- scripts/qmp/qom-get | 3 +-- scripts/qmp/qom-list | 3 +-- scripts/qmp/qom-set | 3 +-- scripts/qmp/qom-tree | 3 +-- 8 files changed, 8 insertions(+), 16 deletions(-) diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client index 976e69e05f..e8cb7646a0 100755 --- a/scripts/qmp/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client @@ -37,11 +37,10 @@ # from __future__ import print_function -from __future__ import absolute_import import base64 import random -from . import qmp +import qmp class QemuGuestAgent(qmp.QEMUMonitorProtocol): diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp index 33a0d6b73a..6cb46fdae2 100755 --- a/scripts/qmp/qmp +++ b/scripts/qmp/qmp @@ -11,9 +11,8 @@ # See the COPYING file in the top-level directory. from __future__ import print_function -from __future__ import absolute_import import sys, os -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol def print_response(rsp, prefix=[]): if type(rsp) == list: diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index 26418dab95..a42306dd89 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -66,8 +66,7 @@ # sent to QEMU, which is useful for debugging and documentation generation. from __future__ import print_function -from __future__ import absolute_import -from . import qmp +import qmp import json import ast import readline diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse index e524e798fc..4d85970a78 100755 --- a/scripts/qmp/qom-fuse +++ b/scripts/qmp/qom-fuse @@ -11,12 +11,11 @@ # the COPYING file in the top-level directory. ## -from __future__ import absolute_import import fuse, stat from fuse import Fuse import os, posix from errno import * -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol fuse.fuse_python_api = (0, 2) diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get index a3f5d7660e..ec5275d53a 100755 --- a/scripts/qmp/qom-get +++ b/scripts/qmp/qom-get @@ -12,10 +12,9 @@ ## from __future__ import print_function -from __future__ import absolute_import import sys import os -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list index 2ba25e1792..0f97440973 100755 --- a/scripts/qmp/qom-list +++ b/scripts/qmp/qom-list @@ -12,10 +12,9 @@ ## from __future__ import print_function -from __future__ import absolute_import import sys import os -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set index 0352668812..26ed9e3263 100755 --- a/scripts/qmp/qom-set +++ b/scripts/qmp/qom-set @@ -12,10 +12,9 @@ ## from __future__ import print_function -from __future__ import absolute_import import sys import os -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree index 32e708a13e..31603c681f 100755 --- a/scripts/qmp/qom-tree +++ b/scripts/qmp/qom-tree @@ -14,10 +14,9 @@ ## from __future__ import print_function -from __future__ import absolute_import import sys import os -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None From 280c1e1cdb24d80ecdfcdfc679ccc5e8ed7af45d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Jun 2018 13:11:56 +0200 Subject: [PATCH 1577/2380] audio/hda: create millisecond timers that handle IO Currently, the HDA device tries to sync itself with the QEMU audio backend by waiting for the guest driver to handle buffer completion interrupts. This causes the backend to often read too much data from the device, as well as running out of data whenever the guest takes too long to handle the interrupt. According to the HDA specification, the guest is also not required to use interrupts, but can also sync itself by polling the LPIB registers. This patch will introduce high frequency (1000Hz) timers that interface with the device and allow for much smoother emulation of the LPIB registers. Since the timing is now provided by these timers, the need to wait for buffer completion interrupts also ceases. Signed-off-by: Martin Schrodt Signed-off-by: Gerd Hoffmann Message-id: 20180622111200.30561-2-kraxel@redhat.com Message-id: 20171015184033.2951-3-martin@schrodt.org [ kraxel: keep old code for compatibility with older qemu versions, add property to switch code paths at runtime ] [ kraxel: new code is disabled by default, use-timer=on enables it ] Signed-off-by: Gerd Hoffmann --- hw/audio/hda-codec.c | 275 ++++++++++++++++++++++++++++++++++++++----- hw/audio/intel-hda.c | 7 -- 2 files changed, 243 insertions(+), 39 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index e8aa7842e6..c62e78c859 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/atomic.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "intel-hda.h" @@ -126,6 +127,11 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) #define PARAM nomixemu #include "hda-codec-common.h" +#define HDA_TIMER_TICKS (SCALE_MS) +#define MAX_CORR (SCALE_US * 100) +#define B_SIZE sizeof(st->buf) +#define B_MASK (sizeof(st->buf) - 1) + /* -------------------------------------------------------------------------- */ static const char *fmt2name[] = { @@ -154,8 +160,13 @@ struct HDAAudioStream { SWVoiceIn *in; SWVoiceOut *out; } voice; - uint8_t buf[HDA_BUFFER_SIZE]; - uint32_t bpos; + uint8_t compat_buf[HDA_BUFFER_SIZE]; + uint32_t compat_bpos; + uint8_t buf[8192]; /* size must be power of two */ + int64_t rpos; + int64_t wpos; + QEMUTimer *buft; + int64_t buft_start; }; #define TYPE_HDA_AUDIO "hda-audio" @@ -174,55 +185,201 @@ struct HDAAudioState { /* properties */ uint32_t debug; bool mixer; + bool use_timer; }; +static inline int64_t hda_bytes_per_second(HDAAudioStream *st) +{ + return 2 * st->as.nchannels * st->as.freq; +} + +static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) +{ + int64_t corr = + NANOSECONDS_PER_SECOND * target_pos / hda_bytes_per_second(st); + if (corr > MAX_CORR) { + corr = MAX_CORR; + } else if (corr < -MAX_CORR) { + corr = -MAX_CORR; + } + atomic_fetch_add(&st->buft_start, corr); +} + +static void hda_audio_input_timer(void *opaque) +{ + HDAAudioStream *st = opaque; + + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + int64_t buft_start = atomic_fetch_add(&st->buft_start, 0); + int64_t wpos = atomic_fetch_add(&st->wpos, 0); + int64_t rpos = atomic_fetch_add(&st->rpos, 0); + + int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start) + / NANOSECONDS_PER_SECOND; + wanted_rpos &= -4; /* IMPORTANT! clip to frames */ + + if (wanted_rpos <= rpos) { + /* we already transmitted the data */ + goto out_timer; + } + + int64_t to_transfer = audio_MIN(wpos - rpos, wanted_rpos - rpos); + while (to_transfer) { + uint32_t start = (rpos & B_MASK); + uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer); + int rc = hda_codec_xfer( + &st->state->hda, st->stream, false, st->buf + start, chunk); + if (!rc) { + break; + } + rpos += chunk; + to_transfer -= chunk; + atomic_fetch_add(&st->rpos, chunk); + } + +out_timer: + + if (st->running) { + timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); + } +} + static void hda_audio_input_cb(void *opaque, int avail) { HDAAudioStream *st = opaque; - int recv = 0; - int len; - bool rc; - while (avail - recv >= sizeof(st->buf)) { - if (st->bpos != sizeof(st->buf)) { - len = AUD_read(st->voice.in, st->buf + st->bpos, - sizeof(st->buf) - st->bpos); - st->bpos += len; - recv += len; - if (st->bpos != sizeof(st->buf)) { - break; - } + int64_t wpos = atomic_fetch_add(&st->wpos, 0); + int64_t rpos = atomic_fetch_add(&st->rpos, 0); + + int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail); + + hda_timer_sync_adjust(st, -((wpos - rpos) + to_transfer - (B_SIZE >> 1))); + + while (to_transfer) { + uint32_t start = (uint32_t) (wpos & B_MASK); + uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer); + uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk); + wpos += read; + to_transfer -= read; + atomic_fetch_add(&st->wpos, read); + if (chunk != read) { + break; } - rc = hda_codec_xfer(&st->state->hda, st->stream, false, - st->buf, sizeof(st->buf)); + } +} + +static void hda_audio_output_timer(void *opaque) +{ + HDAAudioStream *st = opaque; + + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + int64_t buft_start = atomic_fetch_add(&st->buft_start, 0); + int64_t wpos = atomic_fetch_add(&st->wpos, 0); + int64_t rpos = atomic_fetch_add(&st->rpos, 0); + + int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start) + / NANOSECONDS_PER_SECOND; + wanted_wpos &= -4; /* IMPORTANT! clip to frames */ + + if (wanted_wpos <= wpos) { + /* we already received the data */ + goto out_timer; + } + + int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos); + while (to_transfer) { + uint32_t start = (wpos & B_MASK); + uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer); + int rc = hda_codec_xfer( + &st->state->hda, st->stream, true, st->buf + start, chunk); if (!rc) { break; } - st->bpos = 0; + wpos += chunk; + to_transfer -= chunk; + atomic_fetch_add(&st->wpos, chunk); + } + +out_timer: + + if (st->running) { + timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); } } static void hda_audio_output_cb(void *opaque, int avail) +{ + HDAAudioStream *st = opaque; + + int64_t wpos = atomic_fetch_add(&st->wpos, 0); + int64_t rpos = atomic_fetch_add(&st->rpos, 0); + + int64_t to_transfer = audio_MIN(wpos - rpos, avail); + + hda_timer_sync_adjust(st, (wpos - rpos) - to_transfer - (B_SIZE >> 1)); + + while (to_transfer) { + uint32_t start = (uint32_t) (rpos & B_MASK); + uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer); + uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk); + rpos += written; + to_transfer -= written; + atomic_fetch_add(&st->rpos, written); + if (chunk != written) { + break; + } + } +} + +static void hda_audio_compat_input_cb(void *opaque, int avail) +{ + HDAAudioStream *st = opaque; + int recv = 0; + int len; + bool rc; + + while (avail - recv >= sizeof(st->compat_buf)) { + if (st->compat_bpos != sizeof(st->compat_buf)) { + len = AUD_read(st->voice.in, st->compat_buf + st->compat_bpos, + sizeof(st->compat_buf) - st->compat_bpos); + st->compat_bpos += len; + recv += len; + if (st->compat_bpos != sizeof(st->compat_buf)) { + break; + } + } + rc = hda_codec_xfer(&st->state->hda, st->stream, false, + st->compat_buf, sizeof(st->compat_buf)); + if (!rc) { + break; + } + st->compat_bpos = 0; + } +} + +static void hda_audio_compat_output_cb(void *opaque, int avail) { HDAAudioStream *st = opaque; int sent = 0; int len; bool rc; - while (avail - sent >= sizeof(st->buf)) { - if (st->bpos == sizeof(st->buf)) { + while (avail - sent >= sizeof(st->compat_buf)) { + if (st->compat_bpos == sizeof(st->compat_buf)) { rc = hda_codec_xfer(&st->state->hda, st->stream, true, - st->buf, sizeof(st->buf)); + st->compat_buf, sizeof(st->compat_buf)); if (!rc) { break; } - st->bpos = 0; + st->compat_bpos = 0; } - len = AUD_write(st->voice.out, st->buf + st->bpos, - sizeof(st->buf) - st->bpos); - st->bpos += len; + len = AUD_write(st->voice.out, st->compat_buf + st->compat_bpos, + sizeof(st->compat_buf) - st->compat_bpos); + st->compat_bpos += len; sent += len; - if (st->bpos != sizeof(st->buf)) { + if (st->compat_bpos != sizeof(st->compat_buf)) { break; } } @@ -239,6 +396,17 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running) st->running = running; dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name, st->running ? "on" : "off", st->stream); + if (st->state->use_timer) { + if (running) { + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + st->rpos = 0; + st->wpos = 0; + st->buft_start = now; + timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); + } else { + timer_del(st->buft); + } + } if (st->output) { AUD_set_active_out(st->voice.out, st->running); } else { @@ -274,6 +442,9 @@ static void hda_audio_set_amp(HDAAudioStream *st) static void hda_audio_setup(HDAAudioStream *st) { + bool use_timer = st->state->use_timer; + audio_callback_fn cb; + if (st->node == NULL) { return; } @@ -283,13 +454,25 @@ static void hda_audio_setup(HDAAudioStream *st) fmt2name[st->as.fmt], st->as.freq); if (st->output) { + if (use_timer) { + cb = hda_audio_output_cb; + st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, + hda_audio_output_timer, st); + } else { + cb = hda_audio_compat_output_cb; + } st->voice.out = AUD_open_out(&st->state->card, st->voice.out, - st->node->name, st, - hda_audio_output_cb, &st->as); + st->node->name, st, cb, &st->as); } else { + if (use_timer) { + cb = hda_audio_input_cb; + st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, + hda_audio_input_timer, st); + } else { + cb = hda_audio_compat_input_cb; + } st->voice.in = AUD_open_in(&st->state->card, st->voice.in, - st->node->name, st, - hda_audio_input_cb, &st->as); + st->node->name, st, cb, &st->as); } } @@ -505,7 +688,7 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) /* unmute output by default */ st->gain_left = QEMU_HDA_AMP_STEPS; st->gain_right = QEMU_HDA_AMP_STEPS; - st->bpos = sizeof(st->buf); + st->compat_bpos = sizeof(st->compat_buf); st->output = true; } else { st->output = false; @@ -532,6 +715,9 @@ static void hda_audio_exit(HDACodecDevice *hda) if (st->node == NULL) { continue; } + if (a->use_timer) { + timer_del(st->buft); + } if (st->output) { AUD_close_out(&a->card, st->voice.out); } else { @@ -581,6 +767,26 @@ static void hda_audio_reset(DeviceState *dev) } } +static bool vmstate_hda_audio_stream_buf_needed(void *opaque) +{ + HDAAudioStream *st = opaque; + return st->state->use_timer; +} + +static const VMStateDescription vmstate_hda_audio_stream_buf = { + .name = "hda-audio-stream/buffer", + .version_id = 1, + .needed = vmstate_hda_audio_stream_buf_needed, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(buf, HDAAudioStream), + VMSTATE_INT64(rpos, HDAAudioStream), + VMSTATE_INT64(wpos, HDAAudioStream), + VMSTATE_TIMER_PTR(buft, HDAAudioStream), + VMSTATE_INT64(buft_start, HDAAudioStream), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_hda_audio_stream = { .name = "hda-audio-stream", .version_id = 1, @@ -592,9 +798,13 @@ static const VMStateDescription vmstate_hda_audio_stream = { VMSTATE_UINT32(gain_right, HDAAudioStream), VMSTATE_BOOL(mute_left, HDAAudioStream), VMSTATE_BOOL(mute_right, HDAAudioStream), - VMSTATE_UINT32(bpos, HDAAudioStream), - VMSTATE_BUFFER(buf, HDAAudioStream), + VMSTATE_UINT32(compat_bpos, HDAAudioStream), + VMSTATE_BUFFER(compat_buf, HDAAudioStream), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_hda_audio_stream_buf, + NULL } }; @@ -615,6 +825,7 @@ static const VMStateDescription vmstate_hda_audio = { static Property hda_audio_properties[] = { DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), + DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 948268afd8..23a2cf6484 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -407,13 +407,6 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, if (st->bpl == NULL) { return false; } - if (st->ctl & (1 << 26)) { - /* - * Wait with the next DMA xfer until the guest - * has acked the buffer completion interrupt - */ - return false; - } left = len; s = st->bentries; From 0a373bb310c1533e24aa5e3edbf206507fb342ea Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Jun 2018 13:11:57 +0200 Subject: [PATCH 1578/2380] audio/hda: turn some dprintfs into trace points MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180622111200.30561-3-kraxel@redhat.com --- hw/audio/hda-codec.c | 9 ++++----- hw/audio/trace-events | 4 ++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index c62e78c859..a08516cbf8 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -24,6 +24,7 @@ #include "intel-hda.h" #include "intel-hda-defs.h" #include "audio/audio.h" +#include "trace.h" /* -------------------------------------------------------------------------- */ @@ -394,8 +395,7 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running) return; } st->running = running; - dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name, - st->running ? "on" : "off", st->stream); + trace_hda_audio_running(st->node->name, st->stream, st->running); if (st->state->use_timer) { if (running) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); @@ -449,9 +449,8 @@ static void hda_audio_setup(HDAAudioStream *st) return; } - dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n", - st->node->name, st->as.nchannels, - fmt2name[st->as.fmt], st->as.freq); + trace_hda_audio_format(st->node->name, st->as.nchannels, + fmt2name[st->as.fmt], st->as.freq); if (st->output) { if (use_timer) { diff --git a/hw/audio/trace-events b/hw/audio/trace-events index fa1646d169..03340b9359 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -17,3 +17,7 @@ milkymist_ac97_in_cb(int avail, uint32_t remaining) "avail %d remaining %u" milkymist_ac97_in_cb_transferred(int transferred) "transferred %d" milkymist_ac97_out_cb(int free, uint32_t remaining) "free %d remaining %u" milkymist_ac97_out_cb_transferred(int transferred) "transferred %d" + +# hw/audio/hda-codec.c +hda_audio_running(const char *stream, int nr, bool running) "st %s, nr %d, run %d" +hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s, %d x %s @ %d Hz" From 8ced0669237b2bbedac3e4ce6fcf7aaaafaae663 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Jun 2018 13:11:58 +0200 Subject: [PATCH 1579/2380] audio/hda: tweak timer adjust logic We have some jitter in the audio timer call frequency and buffer sizes. So it is rather pointless trying to be very exact, effect is a constant up+down adjustment. So adjust only in case we are off too much. Signed-off-by: Gerd Hoffmann Message-id: 20180622111200.30561-4-kraxel@redhat.com --- hw/audio/hda-codec.c | 20 +++++++++++++------- hw/audio/trace-events | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index a08516cbf8..870448a687 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -129,7 +129,6 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) #include "hda-codec-common.h" #define HDA_TIMER_TICKS (SCALE_MS) -#define MAX_CORR (SCALE_US * 100) #define B_SIZE sizeof(st->buf) #define B_MASK (sizeof(st->buf) - 1) @@ -196,13 +195,20 @@ static inline int64_t hda_bytes_per_second(HDAAudioStream *st) static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) { - int64_t corr = - NANOSECONDS_PER_SECOND * target_pos / hda_bytes_per_second(st); - if (corr > MAX_CORR) { - corr = MAX_CORR; - } else if (corr < -MAX_CORR) { - corr = -MAX_CORR; + int64_t limit = B_SIZE / 8; + int64_t corr = 0; + + if (target_pos > limit) { + corr = HDA_TIMER_TICKS; } + if (target_pos < -limit) { + corr = -HDA_TIMER_TICKS; + } + if (corr == 0) { + return; + } + + trace_hda_audio_adjust(st->node->name, target_pos); atomic_fetch_add(&st->buft_start, corr); } diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 03340b9359..30112d97c4 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -21,3 +21,4 @@ milkymist_ac97_out_cb_transferred(int transferred) "transferred %d" # hw/audio/hda-codec.c hda_audio_running(const char *stream, int nr, bool running) "st %s, nr %d, run %d" hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s, %d x %s @ %d Hz" +hda_audio_adjust(const char *stream, int pos) "st %s, pos %d" From 4501ee16c76e89e0a2b2beb95f3b93f965997391 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Jun 2018 13:11:59 +0200 Subject: [PATCH 1580/2380] audio/hda: detect output buffer overruns If some event caused some larger playback hickup the fine-grained timer adjust isn't able to recover. Use a buffer overruns as indicator for that. Reset timer adjust logic in case we detected one. Signed-off-by: Gerd Hoffmann Message-id: 20180622111200.30561-5-kraxel@redhat.com --- hw/audio/hda-codec.c | 9 +++++++++ hw/audio/trace-events | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 870448a687..ac67b9aada 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -325,6 +325,15 @@ static void hda_audio_output_cb(void *opaque, int avail) int64_t to_transfer = audio_MIN(wpos - rpos, avail); + if (wpos - rpos == B_SIZE) { + /* drop buffer, reset timer adjust */ + st->rpos = 0; + st->wpos = 0; + st->buft_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + trace_hda_audio_overrun(st->node->name); + return; + } + hda_timer_sync_adjust(st, (wpos - rpos) - to_transfer - (B_SIZE >> 1)); while (to_transfer) { diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 30112d97c4..5891b4e2b9 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -22,3 +22,4 @@ milkymist_ac97_out_cb_transferred(int transferred) "transferred %d" hda_audio_running(const char *stream, int nr, bool running) "st %s, nr %d, run %d" hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s, %d x %s @ %d Hz" hda_audio_adjust(const char *stream, int pos) "st %s, pos %d" +hda_audio_overrun(const char *stream) "st %s" From bc753dc09ff33d99bc9004d7286c50de1d5bece6 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Jun 2018 13:12:00 +0200 Subject: [PATCH 1581/2380] audio/hda: enable new timer code by default. Also add a compat property to disable it for old machine types, needed for live migration compatibility. Signed-off-by: Gerd Hoffmann Message-id: 20180622111200.30561-6-kraxel@redhat.com --- hw/audio/hda-codec.c | 2 +- include/hw/compat.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index ac67b9aada..fc4945086b 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -839,7 +839,7 @@ static const VMStateDescription vmstate_hda_audio = { static Property hda_audio_properties[] = { DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), - DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, false), + DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/compat.h b/include/hw/compat.h index 563908b874..44d5964060 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -6,6 +6,10 @@ .driver = "migration",\ .property = "decompress-error-check",\ .value = "off",\ + },{\ + .driver = "hda-audio",\ + .property = "use-timer",\ + .value = "false",\ }, #define HW_COMPAT_2_11 \ From 441b345313f1355fb2464f3b37a07a1ded85b61a Mon Sep 17 00:00:00 2001 From: Nia Alarie Date: Fri, 16 Mar 2018 14:40:47 +0000 Subject: [PATCH 1582/2380] audio: Convert use of atoi to qemu_strtoi If qemu_strtoi indicates an error, return the default value. Signed-off-by: Nia Alarie Message-Id: <20180316144047.30904-1-nia.alarie@gmail.com> Signed-off-by: Gerd Hoffmann --- audio/audio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index 6eccdb17ee..d6e91901aa 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -335,9 +335,8 @@ static int audio_get_conf_int (const char *key, int defval, int *defaultp) char *strval; strval = getenv (key); - if (strval) { + if (strval && !qemu_strtoi(strval, NULL, 10, &val)) { *defaultp = 0; - val = atoi (strval); return val; } else { From 2ee3518b53cd82bca10448283bd8fe5a259c27b1 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 20 Jun 2018 15:24:19 +0200 Subject: [PATCH 1583/2380] vl.c: do not allow --daemonize in combination with --preconfig CLI option some users when using --daemonize expect that QEMU will parse CLI options, initialize VM and only then complete daemonzation by signalling lead process to exit and start listening on monitor socket. So users treat parent process exit as sync point to connect to QEMU's monitor. That however doesn't work when --preconfig options is used, since it provides monitor before completing daemonization and expects user to issue exit-preconfig command when additional configuration via monitor is finished. We also can't move completing daemonization before preconfig monitor becomes available, since that would imply: * partially loosing ability to configure QEMU instance in --preconfig mode since QEMU might drop privileges, chroot and do other things when daemonization is completed * lead to loss of error messages in case they would happen after daemonization Be proactive now and make options mutually exclusive, so users would get clear error message instead of waiting for lead process exit indefinitely before connecting to monitor. PS: In case someone would come up with usecase where both options should be enabled at the same time we could drop this restriction as far as daemonization point is left where it is now (os_setup_post). Signed-off-by: Igor Mammedov Message-Id: <1529501059-163139-1-git-send-email-imammedo@redhat.com> Signed-off-by: Eduardo Habkost --- vl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vl.c b/vl.c index d451f45dc5..a37dfe4684 100644 --- a/vl.c +++ b/vl.c @@ -4134,6 +4134,12 @@ int main(int argc, char **argv, char **envp) } if (is_daemonized()) { + if (!preconfig_exit_requested) { + error_report("'preconfig' and 'daemonize' options are " + "mutually exclusive"); + exit(EXIT_FAILURE); + } + /* According to documentation and historically, -nographic redirects * serial port, parallel port and monitor to stdio, which does not work * with -daemonize. We can redirect these to null instead, but since From 08fe68244eb44f3c8ccecd35066eca8392d1345a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 25 Jun 2018 11:05:12 +0200 Subject: [PATCH 1584/2380] hw/i386: Deprecate the machine types pc-0.10 and pc-0.11 The oldest machine type which is still used in a still maintained distro is a pc-0.12 based machine type in RHEL6, so everything that is older than pc-0.12 should not be used anymore. Thus let's deprecate pc-0.10 and pc-0.11 so that we can finally remove them in a future release. Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Message-Id: <1529917512-10528-1-git-send-email-thuth@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Eduardo Habkost --- hw/i386/pc_piix.c | 1 + include/hw/boards.h | 3 +++ qemu-doc.texi | 5 +++++ vl.c | 10 ++++++++-- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index e9b6f064fb..d357907b0b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -956,6 +956,7 @@ static void pc_i440fx_0_11_machine_options(MachineClass *m) { pc_i440fx_0_12_machine_options(m); m->hw_version = "0.11"; + m->deprecation_reason = "use a newer machine type instead"; SET_MACHINE_COMPAT(m, PC_COMPAT_0_11); } diff --git a/include/hw/boards.h b/include/hw/boards.h index ef7457f5dd..79069ddcbe 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -107,6 +107,8 @@ typedef struct { /** * MachineClass: + * @deprecation_reason: If set, the machine is marked as deprecated. The + * string should provide some clear information about what to use instead. * @max_cpus: maximum number of CPUs supported. Default: 1 * @min_cpus: minimum number of CPUs supported. Default: 1 * @default_cpus: number of CPUs instantiated if none are specified. Default: 1 @@ -166,6 +168,7 @@ struct MachineClass { char *name; const char *alias; const char *desc; + const char *deprecation_reason; void (*init)(MachineState *state); void (*reset)(void); diff --git a/qemu-doc.texi b/qemu-doc.texi index 282bc3dc35..16fcb47901 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2943,6 +2943,11 @@ support page sizes < 4096 any longer. @section System emulator machines +@subsection pc-0.10 and pc-0.11 (since 3.0) + +These machine types are very old and likely can not be used for live migration +from old QEMU versions anymore. A newer machine type should be used instead. + @section Device options @subsection Block device options diff --git a/vl.c b/vl.c index a37dfe4684..d26f19b06d 100644 --- a/vl.c +++ b/vl.c @@ -2560,8 +2560,9 @@ static gint machine_class_cmp(gconstpointer a, gconstpointer b) if (mc->alias) { printf("%-20s %s (alias of %s)\n", mc->alias, mc->desc, mc->name); } - printf("%-20s %s%s\n", mc->name, mc->desc, - mc->is_default ? " (default)" : ""); + printf("%-20s %s%s%s\n", mc->name, mc->desc, + mc->is_default ? " (default)" : "", + mc->deprecation_reason ? " (deprecated)" : ""); } } @@ -4263,6 +4264,11 @@ int main(int argc, char **argv, char **envp) configure_accelerator(current_machine); + if (!qtest_enabled() && machine_class->deprecation_reason) { + error_report("Machine type '%s' is deprecated: %s", + machine_class->name, machine_class->deprecation_reason); + } + /* * Register all the global properties, including accel properties, * machine properties, and user-specified ones. From ae79c2db150e17757ee1be080481be675a15ccea Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Jun 2018 16:50:48 -0400 Subject: [PATCH 1585/2380] ahci: fix FIS I bit and PIO Setup FIS interrupt The "I" bit in PIO Setup and D2H FISes is exclusively a device concept and the irqstatus register in the controller does not matter. The SATA spec says when it should be one; for D2H FISes in practice it is always set, while the PIO Setup FIS has several subcases that are documented in the patch. Also, the PIO Setup FIS interrupt is actually generated _after_ data has been received. Someone should probably spend some time reading the SATA specification and figuring out the more obscure fields in the PIO Setup FIS, but this is enough to fix SeaBIOS booting from ATAPI CD-ROMs over an AHCI controller. Fixes: 956556e131e35f387ac482ad7b41151576fef057 Reported-by: Gerd Hoffmann Signed-off-by: Paolo Bonzini Reviewed-by: John Snow Message-id: 20180622165159.19863-1-pbonzini@redhat.com [Minor edit to avoid ATAPI comment ambiguity. --js] Signed-off-by: John Snow --- hw/ide/ahci.c | 37 +++++++++++++++++++++++++------------ hw/ide/ahci_internal.h | 2 +- tests/libqos/ahci.c | 25 ++++++++++++++++--------- tests/libqos/ahci.h | 2 +- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 2ec24cad9f..d700ca973b 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -801,7 +801,7 @@ static void ahci_write_fis_sdb(AHCIState *s, NCQTransferState *ncq_tfs) } } -static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) +static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len, bool pio_fis_i) { AHCIPortRegs *pr = &ad->port_regs; uint8_t *pio_fis; @@ -814,7 +814,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) pio_fis = &ad->res_fis[RES_FIS_PSFIS]; pio_fis[0] = SATA_FIS_TYPE_PIO_SETUP; - pio_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); + pio_fis[1] = (pio_fis_i ? (1 << 6) : 0); pio_fis[2] = s->status; pio_fis[3] = s->error; @@ -842,8 +842,6 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) if (pio_fis[2] & ERR_STAT) { ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES); } - - ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS); } static bool ahci_write_fis_d2h(AHCIDevice *ad) @@ -860,7 +858,7 @@ static bool ahci_write_fis_d2h(AHCIDevice *ad) d2h_fis = &ad->res_fis[RES_FIS_RFIS]; d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H; - d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); + d2h_fis[1] = (1 << 6); /* interrupt bit */ d2h_fis[2] = s->status; d2h_fis[3] = s->error; @@ -1258,11 +1256,10 @@ static void handle_reg_h2d_fis(AHCIState *s, int port, trace_handle_reg_h2d_fis_dump(s, port, pretty_fis); g_free(pretty_fis); } - s->dev[port].done_atapi_packet = false; } ide_state->error = 0; - + s->dev[port].done_first_drq = false; /* Reset transferred byte counter */ cmd->status = 0; @@ -1351,13 +1348,23 @@ static void ahci_pio_transfer(IDEDMA *dma) int is_write = opts & AHCI_CMD_WRITE; int is_atapi = opts & AHCI_CMD_ATAPI; int has_sglist = 0; + bool pio_fis_i; - /* PIO FIS gets written prior to transfer */ - ahci_write_fis_pio(ad, size); + /* The PIO Setup FIS is received prior to transfer, but the interrupt + * is only triggered after data is received. + * + * The device only sets the 'I' bit in the PIO Setup FIS for device->host + * requests (see "DPIOI1" in the SATA spec), or for host->device DRQs after + * the first (see "DPIOO1"). The latter is consistent with the spec's + * description of the PACKET protocol, where the command part of ATAPI requests + * ("DPKT0") has the 'I' bit clear, while the data part of PIO ATAPI requests + * ("DPKT4a" and "DPKT7") has the 'I' bit set for both directions for all DRQs. + */ + pio_fis_i = ad->done_first_drq || (!is_atapi && !is_write); + ahci_write_fis_pio(ad, size, pio_fis_i); - if (is_atapi && !ad->done_atapi_packet) { + if (is_atapi && !ad->done_first_drq) { /* already prepopulated iobuffer */ - ad->done_atapi_packet = true; goto out; } @@ -1379,9 +1386,15 @@ static void ahci_pio_transfer(IDEDMA *dma) /* Update number of transferred bytes, destroy sglist */ dma_buf_commit(s, size); + out: /* declare that we processed everything */ s->data_ptr = s->data_end; + + ad->done_first_drq = true; + if (pio_fis_i) { + ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS); + } } static void ahci_start_dma(IDEDMA *dma, IDEState *s, @@ -1627,7 +1640,7 @@ static const VMStateDescription vmstate_ahci_device = { VMSTATE_UINT32(port_regs.scr_err, AHCIDevice), VMSTATE_UINT32(port_regs.scr_act, AHCIDevice), VMSTATE_UINT32(port_regs.cmd_issue, AHCIDevice), - VMSTATE_BOOL(done_atapi_packet, AHCIDevice), + VMSTATE_BOOL(done_first_drq, AHCIDevice), VMSTATE_INT32(busy_slot, AHCIDevice), VMSTATE_BOOL(init_d2h_sent, AHCIDevice), VMSTATE_STRUCT_ARRAY(ncq_tfs, AHCIDevice, AHCI_MAX_CMDS, diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index 2953243929..9b7fa8fc7d 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -315,7 +315,7 @@ struct AHCIDevice { QEMUBH *check_bh; uint8_t *lst; uint8_t *res_fis; - bool done_atapi_packet; + bool done_first_drq; int32_t busy_slot; bool init_d2h_sent; AHCICmdHdr *cur_cmd; diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index 7264e085d0..42d3f76933 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -651,10 +651,7 @@ void ahci_exec(AHCIQState *ahci, uint8_t port, /* Command creation */ if (opts->atapi) { uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE; - cmd = ahci_atapi_command_create(op, bcl); - if (opts->atapi_dma) { - ahci_command_enable_atapi_dma(cmd); - } + cmd = ahci_atapi_command_create(op, bcl, opts->atapi_dma); } else { cmd = ahci_command_create(op); } @@ -874,7 +871,6 @@ AHCICommand *ahci_command_create(uint8_t command_name) /* cmd->interrupts |= props->data ? AHCI_PX_IS_DPS : 0; */ /* BUG: We expect the DMA Setup interrupt for DMA commands */ /* cmd->interrupts |= props->dma ? AHCI_PX_IS_DSS : 0; */ - cmd->interrupts |= props->pio ? AHCI_PX_IS_PSS : 0; cmd->interrupts |= props->ncq ? AHCI_PX_IS_SDBS : 0; command_header_init(cmd); @@ -883,19 +879,24 @@ AHCICommand *ahci_command_create(uint8_t command_name) return cmd; } -AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl) +AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl, bool dma) { AHCICommand *cmd = ahci_command_create(CMD_PACKET); cmd->atapi_cmd = g_malloc0(16); cmd->atapi_cmd[0] = scsi_cmd; stw_le_p(&cmd->fis.lba_lo[1], bcl); + if (dma) { + ahci_command_enable_atapi_dma(cmd); + } else { + cmd->interrupts |= bcl ? AHCI_PX_IS_PSS : 0; + } return cmd; } void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port, bool ready, uint8_t expected_sense) { - AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0); + AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0, false); ahci_command_set_size(cmd, 0); if (!ready) { cmd->interrupts |= AHCI_PX_IS_TFES; @@ -937,7 +938,7 @@ void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port, void ahci_atapi_eject(AHCIQState *ahci, uint8_t port) { - AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0); + AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0, false); ahci_command_set_size(cmd, 0); cmd->atapi_cmd[4] = 0x02; /* loej = true */ @@ -949,7 +950,7 @@ void ahci_atapi_eject(AHCIQState *ahci, uint8_t port) void ahci_atapi_load(AHCIQState *ahci, uint8_t port) { - AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0); + AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0, false); ahci_command_set_size(cmd, 0); cmd->atapi_cmd[4] = 0x03; /* loej,start = true */ @@ -1098,6 +1099,12 @@ void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, } else if (cmd->props->atapi) { ahci_atapi_set_size(cmd, xbytes); } else { + /* For writes, the PIO Setup FIS interrupt only comes from DRQs + * after the first. + */ + if (cmd->props->pio && sect_count > (cmd->props->read ? 0 : 1)) { + cmd->interrupts |= AHCI_PX_IS_PSS; + } cmd->fis.count = sect_count; } cmd->header.prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size); diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index 13f6d87b75..f05b3e5fce 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -622,7 +622,7 @@ void ahci_atapi_load(AHCIQState *ahci, uint8_t port); /* Command: Fine-grained lifecycle */ AHCICommand *ahci_command_create(uint8_t command_name); -AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl); +AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl, bool dma); void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port); void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd); void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd); From 54d208ffdae20fd2bee745a037607a762d3a82a9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 18 Jun 2018 13:21:41 +0200 Subject: [PATCH 1586/2380] Add gles support to egl-helpers, wire up in egl-headless and gtk. Add support for OpenGL ES to egl-helpers. Wire up the new option for egl-headless and gtk UIs. egl-headless actually works fine. gtk hits a not-yet implemented code path in libEGL when trying to use gles mode: libEGL warning: FIXME: egl/x11 doesn't support front buffer rendering. (This is mesa 17.2.3). Cc: Tomeu Vizoso Signed-off-by: Gerd Hoffmann Reviewed-by: Tomeu Vizoso Tested-by: Tomeu Vizoso Message-id: 20180618112141.23398-1-kraxel@redhat.com --- include/ui/egl-helpers.h | 7 ++--- include/ui/gtk.h | 2 +- ui/egl-context.c | 11 ++++++-- ui/egl-headless.c | 3 ++- ui/egl-helpers.c | 55 ++++++++++++++++++++++++++++------------ ui/gtk-egl.c | 4 +-- ui/gtk.c | 7 ++--- ui/spice-core.c | 3 ++- 8 files changed, 61 insertions(+), 31 deletions(-) diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index 747233ce58..9db7293bdb 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -7,6 +7,7 @@ extern EGLDisplay *qemu_egl_display; extern EGLConfig qemu_egl_config; +extern DisplayGLMode qemu_egl_mode; typedef struct egl_fb { int width; @@ -34,7 +35,7 @@ extern int qemu_egl_rn_fd; extern struct gbm_device *qemu_egl_rn_gbm_dev; extern EGLContext qemu_egl_rn_ctx; -int egl_rendernode_init(const char *rendernode); +int egl_rendernode_init(const char *rendernode, DisplayGLMode mode); int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc); void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf); @@ -44,8 +45,8 @@ void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf); EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win); -int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy); -int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy); +int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode); +int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode); EGLContext qemu_egl_init_ctx(void); #endif /* EGL_HELPERS_H */ diff --git a/include/ui/gtk.h b/include/ui/gtk.h index 2922fc64b2..a79780afc7 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -127,7 +127,7 @@ void gd_egl_release_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf); void gd_egl_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h); -void gtk_egl_init(void); +void gtk_egl_init(DisplayGLMode mode); int gd_egl_make_current(DisplayChangeListener *dcl, QEMUGLContext ctx); diff --git a/ui/egl-context.c b/ui/egl-context.c index 2161969abe..78e6c7ab7c 100644 --- a/ui/egl-context.c +++ b/ui/egl-context.c @@ -6,15 +6,22 @@ QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl, QEMUGLParams *params) { EGLContext ctx; - EGLint ctx_att[] = { + EGLint ctx_att_core[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, EGL_CONTEXT_CLIENT_VERSION, params->major_ver, EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver, EGL_NONE }; + EGLint ctx_att_gles[] = { + EGL_CONTEXT_CLIENT_VERSION, params->major_ver, + EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver, + EGL_NONE + }; + bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES); ctx = eglCreateContext(qemu_egl_display, qemu_egl_config, - eglGetCurrentContext(), ctx_att); + eglGetCurrentContext(), + gles ? ctx_att_gles : ctx_att_core); return ctx; } diff --git a/ui/egl-headless.c b/ui/egl-headless.c index 7c877122d3..42a41310b0 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -171,11 +171,12 @@ static void early_egl_headless_init(DisplayOptions *opts) static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) { + DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON; QemuConsole *con; egl_dpy *edpy; int idx; - if (egl_rendernode_init(NULL) < 0) { + if (egl_rendernode_init(NULL, mode) < 0) { error_report("egl: render node init failed"); exit(1); } diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 16dc3ded36..71b6a97bd1 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -24,6 +24,7 @@ EGLDisplay *qemu_egl_display; EGLConfig qemu_egl_config; +DisplayGLMode qemu_egl_mode; /* ------------------------------------------------------------------ */ @@ -191,7 +192,7 @@ static int qemu_egl_rendernode_open(const char *rendernode) return fd; } -int egl_rendernode_init(const char *rendernode) +int egl_rendernode_init(const char *rendernode, DisplayGLMode mode) { qemu_egl_rn_fd = -1; int rc; @@ -208,7 +209,8 @@ int egl_rendernode_init(const char *rendernode) goto err; } - rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev); + rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, + mode); if (rc != 0) { /* qemu_egl_init_dpy_mesa reports error */ goto err; @@ -392,9 +394,10 @@ static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native, } static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, - EGLenum platform) + EGLenum platform, + DisplayGLMode mode) { - static const EGLint conf_att_gl[] = { + static const EGLint conf_att_core[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_RED_SIZE, 5, @@ -403,9 +406,19 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, EGL_ALPHA_SIZE, 0, EGL_NONE, }; + static const EGLint conf_att_gles[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 5, + EGL_BLUE_SIZE, 5, + EGL_ALPHA_SIZE, 0, + EGL_NONE, + }; EGLint major, minor; EGLBoolean b; EGLint n; + bool gles = (mode == DISPLAYGL_MODE_ES); qemu_egl_display = qemu_egl_get_display(dpy, platform); if (qemu_egl_display == EGL_NO_DISPLAY) { @@ -419,50 +432,60 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, return -1; } - b = eglBindAPI(EGL_OPENGL_API); + b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API); if (b == EGL_FALSE) { - error_report("egl: eglBindAPI failed"); + error_report("egl: eglBindAPI failed (%s mode)", + gles ? "gles" : "core"); return -1; } - b = eglChooseConfig(qemu_egl_display, conf_att_gl, + b = eglChooseConfig(qemu_egl_display, + gles ? conf_att_gles : conf_att_core, &qemu_egl_config, 1, &n); if (b == EGL_FALSE || n != 1) { - error_report("egl: eglChooseConfig failed"); + error_report("egl: eglChooseConfig failed (%s mode)", + gles ? "gles" : "core"); return -1; } + + qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE; return 0; } -int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy) +int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode) { #ifdef EGL_KHR_platform_x11 - return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR); + return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode); #else - return qemu_egl_init_dpy(dpy, 0); + return qemu_egl_init_dpy(dpy, 0, mode); #endif } -int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy) +int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode) { #ifdef EGL_MESA_platform_gbm - return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA); + return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode); #else - return qemu_egl_init_dpy(dpy, 0); + return qemu_egl_init_dpy(dpy, 0, mode); #endif } EGLContext qemu_egl_init_ctx(void) { - static const EGLint ctx_att_gl[] = { + static const EGLint ctx_att_core[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, EGL_NONE }; + static const EGLint ctx_att_gles[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES); EGLContext ectx; EGLBoolean b; ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT, - ctx_att_gl); + gles ? ctx_att_gles : ctx_att_core); if (ectx == EGL_NO_CONTEXT) { error_report("egl: eglCreateContext failed"); return NULL; diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 9390c6762e..fb00ad12ec 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -280,12 +280,12 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); } -void gtk_egl_init(void) +void gtk_egl_init(DisplayGLMode mode) { GdkDisplay *gdk_display = gdk_display_get_default(); Display *x11_display = gdk_x11_display_get_xdisplay(gdk_display); - if (qemu_egl_init_dpy_x11(x11_display) < 0) { + if (qemu_egl_init_dpy_x11(x11_display, mode) < 0) { return; } diff --git a/ui/gtk.c b/ui/gtk.c index 903f136b8f..5cce6ed42d 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -2485,10 +2485,6 @@ static void early_gtk_display_init(DisplayOptions *opts) assert(opts->type == DISPLAY_TYPE_GTK); if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) { - if (opts->gl == DISPLAYGL_MODE_ES) { - error_report("gtk: opengl es not supported"); - return; - } #if defined(CONFIG_OPENGL) #if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND) if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { @@ -2497,7 +2493,8 @@ static void early_gtk_display_init(DisplayOptions *opts) } else #endif { - gtk_egl_init(); + DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON; + gtk_egl_init(mode); } #endif } diff --git a/ui/spice-core.c b/ui/spice-core.c index ae8921a201..f8c0878529 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -829,7 +829,8 @@ void qemu_spice_init(void) "incompatible with -spice port/tls-port"); exit(1); } - if (egl_rendernode_init(qemu_opt_get(opts, "rendernode")) != 0) { + if (egl_rendernode_init(qemu_opt_get(opts, "rendernode"), + DISPLAYGL_MODE_ON) != 0) { error_report("Failed to initialize EGL render node for SPICE GL"); exit(1); } From 48db08cf1619afde2ad0f7df11ba3ba54c49112b Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Tue, 26 Jun 2018 09:40:17 +0300 Subject: [PATCH 1587/2380] sdl2: fix copypaste issues This patch fixes text and mouse event processing. These functions used 'key' field of the event instead of appropriate 'text', 'motion', 'button', or 'wheel'. Signed-off-by: Pavel Dovgalyuk Signed-off-by: Maria Klimushenkova Message-id: 20180626064017.17031.47954.stgit@pasha-VirtualBox Signed-off-by: Gerd Hoffmann --- ui/sdl2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/sdl2.c b/ui/sdl2.c index 3ae4719c32..3fff9f5b63 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -424,7 +424,7 @@ static void handle_keyup(SDL_Event *ev) static void handle_textinput(SDL_Event *ev) { - struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->text.windowID); QemuConsole *con = scon ? scon->dcl.con : NULL; if (qemu_console_is_graphic(con)) { @@ -436,7 +436,7 @@ static void handle_textinput(SDL_Event *ev) static void handle_mousemotion(SDL_Event *ev) { int max_x, max_y; - struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->motion.windowID); if (!qemu_console_is_graphic(scon->dcl.con)) { return; @@ -468,7 +468,7 @@ static void handle_mousebutton(SDL_Event *ev) { int buttonstate = SDL_GetMouseState(NULL, NULL); SDL_MouseButtonEvent *bev; - struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->button.windowID); if (!qemu_console_is_graphic(scon->dcl.con)) { return; @@ -492,7 +492,7 @@ static void handle_mousebutton(SDL_Event *ev) static void handle_mousewheel(SDL_Event *ev) { - struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->wheel.windowID); SDL_MouseWheelEvent *wev = &ev->wheel; InputButton btn; From 49213b721f4620364aa09142b5f4d559fed26b53 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Tue, 26 Jun 2018 09:47:29 +0300 Subject: [PATCH 1588/2380] sdl2: add checking for NULL Sometimes SDL2 console can't be retrieved on events and scon variable becomes NULL. This patch prevents processing of the events when the console is not available. Signed-off-by: Pavel Dovgalyuk Signed-off-by: Maria Klimushenkova Message-id: 20180626064729.18070.47600.stgit@pasha-VirtualBox Signed-off-by: Gerd Hoffmann --- ui/sdl2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/sdl2.c b/ui/sdl2.c index 3fff9f5b63..76e59427cc 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -438,7 +438,7 @@ static void handle_mousemotion(SDL_Event *ev) int max_x, max_y; struct sdl2_console *scon = get_scon_from_window(ev->motion.windowID); - if (!qemu_console_is_graphic(scon->dcl.con)) { + if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } @@ -470,7 +470,7 @@ static void handle_mousebutton(SDL_Event *ev) SDL_MouseButtonEvent *bev; struct sdl2_console *scon = get_scon_from_window(ev->button.windowID); - if (!qemu_console_is_graphic(scon->dcl.con)) { + if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } @@ -496,7 +496,7 @@ static void handle_mousewheel(SDL_Event *ev) SDL_MouseWheelEvent *wev = &ev->wheel; InputButton btn; - if (!qemu_console_is_graphic(scon->dcl.con)) { + if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } From 93abfc88bd649de1933588bfc7175605331b3ea9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 26 Jun 2018 08:09:41 +0200 Subject: [PATCH 1589/2380] vga: set owner for mmio regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes sure the regions are properly cleaned when unplugging -device seconday-vga. Reported-by: Thomas Huth Signed-off-by: Gerd Hoffmann Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Thomas Huth Message-id: 20180626060941.8326-1-kraxel@redhat.com --- hw/display/vga-pci.c | 11 ++++++----- hw/display/vga_int.h | 1 + hw/display/virtio-vga.c | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index 700ac58c69..1ea559762a 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -192,22 +192,23 @@ static const MemoryRegionOps pci_vga_qext_ops = { }; void pci_std_vga_mmio_region_init(VGACommonState *s, + Object *owner, MemoryRegion *parent, MemoryRegion *subs, bool qext) { - memory_region_init_io(&subs[0], NULL, &pci_vga_ioport_ops, s, + memory_region_init_io(&subs[0], owner, &pci_vga_ioport_ops, s, "vga ioports remapped", PCI_VGA_IOPORT_SIZE); memory_region_add_subregion(parent, PCI_VGA_IOPORT_OFFSET, &subs[0]); - memory_region_init_io(&subs[1], NULL, &pci_vga_bochs_ops, s, + memory_region_init_io(&subs[1], owner, &pci_vga_bochs_ops, s, "bochs dispi interface", PCI_VGA_BOCHS_SIZE); memory_region_add_subregion(parent, PCI_VGA_BOCHS_OFFSET, &subs[1]); if (qext) { - memory_region_init_io(&subs[2], NULL, &pci_vga_qext_ops, s, + memory_region_init_io(&subs[2], owner, &pci_vga_qext_ops, s, "qemu extended regs", PCI_VGA_QEXT_SIZE); memory_region_add_subregion(parent, PCI_VGA_QEXT_OFFSET, &subs[2]); @@ -239,7 +240,7 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) qext = true; pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2); } - pci_std_vga_mmio_region_init(s, &d->mmio, d->mrs, qext); + pci_std_vga_mmio_region_init(s, OBJECT(dev), &d->mmio, d->mrs, qext); pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); } @@ -275,7 +276,7 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) qext = true; pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2); } - pci_std_vga_mmio_region_init(s, &d->mmio, d->mrs, qext); + pci_std_vga_mmio_region_init(s, OBJECT(dev), &d->mmio, d->mrs, qext); pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 313cff84fc..f8fcf62a56 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -193,6 +193,7 @@ extern const MemoryRegionOps vga_mem_ops; /* vga-pci.c */ void pci_std_vga_mmio_region_init(VGACommonState *s, + Object *owner, MemoryRegion *parent, MemoryRegion *subs, bool qext); diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index baa74ba82c..97db6c3372 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -152,7 +152,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } /* add stdvga mmio regions */ - pci_std_vga_mmio_region_init(vga, &vpci_dev->modern_bar, + pci_std_vga_mmio_region_init(vga, OBJECT(vvga), &vpci_dev->modern_bar, vvga->vga_mrs, true); vga->con = g->scanout[0].con; From 6b9b3c1e303cc0dda99e326031d2b1b612b80e3b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 26 Jun 2018 10:31:19 +0200 Subject: [PATCH 1590/2380] ramfb: fix overflow > CID 1393621: (OVERFLOW_BEFORE_WIDEN) > Potentially overflowing expression "stride * s->height" with type "unsigned > int" (32 bits, unsigned) is evaluated using +32-bit arithmetic, and then used > in a context that expects an expression of type "hwaddr" (64 bits, unsigned). Fix by changing stride from uint32_t to hwaddr. Signed-off-by: Gerd Hoffmann Message-id: 20180626083120.19515-1-kraxel@redhat.com --- hw/display/ramfb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/display/ramfb.c b/hw/display/ramfb.c index 6867bce8ae..30f5c8da20 100644 --- a/hw/display/ramfb.c +++ b/hw/display/ramfb.c @@ -36,8 +36,8 @@ static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len) { RAMFBState *s = dev; void *framebuffer; - uint32_t stride, fourcc, format; - hwaddr addr, length; + uint32_t fourcc, format; + hwaddr stride, addr, length; s->width = be32_to_cpu(s->cfg.width); s->height = be32_to_cpu(s->cfg.height); From fd1cfb875be551064a27d909edb406316f011f2c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 18:32:49 +0100 Subject: [PATCH 1591/2380] virtio-gpu-3d: Drop workaround for VIRTIO_GPU_CAPSET_VIRGL2 define In commit a8bff79e9f27df we added a definition to hw/virtio/virtio-gpu.h for VIRTIO_GPU_CAPSET_VIRGL2, as a workaround for it not yet being in the Linux kernel headers. In commit 77d361b13c19 we updated our kernel headers to a version which does define the macro, so we can now remove our workaround. Signed-off-by: Peter Maydell Message-id: 20180622173249.29963-1-peter.maydell@linaro.org Signed-off-by: Gerd Hoffmann --- include/hw/virtio/virtio-gpu.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index d6ba61f2f1..9780f755ef 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -22,11 +22,6 @@ #include "standard-headers/linux/virtio_gpu.h" -/* Not yet(?) defined in standard-headers, remove when possible */ -#ifndef VIRTIO_GPU_CAPSET_VIRGL2 -#define VIRTIO_GPU_CAPSET_VIRGL2 2 -#endif - #define TYPE_VIRTIO_GPU "virtio-gpu-device" #define VIRTIO_GPU(obj) \ OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU) From 0721309ed77100e857a7149dd563a4d1a0d07d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 26 Jun 2018 17:50:39 +0100 Subject: [PATCH 1592/2380] aspeed/smc: fix dummy cycles count when in dual IO mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When configured in dual I/O mode, address and data are sent in dual mode, including the dummy byte cycles in between. Adapt the count to the IO setting. Signed-off-by: Cédric Le Goater Reviewed-by: Andrew Jeffery Message-id: 20180612065716.10587-2-clg@kaod.org Signed-off-by: Peter Maydell --- hw/ssi/aspeed_smc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 5059396bc6..fce126e6ee 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -66,6 +66,8 @@ /* CEx Control Register */ #define R_CTRL0 (0x10 / 4) +#define CTRL_IO_DUAL_DATA (1 << 29) +#define CTRL_IO_DUAL_ADDR_DATA (1 << 28) /* Includes dummies */ #define CTRL_CMD_SHIFT 16 #define CTRL_CMD_MASK 0xff #define CTRL_DUMMY_HIGH_SHIFT 14 @@ -492,8 +494,13 @@ static int aspeed_smc_flash_dummies(const AspeedSMCFlash *fl) uint32_t r_ctrl0 = s->regs[s->r_ctrl0 + fl->id]; uint32_t dummy_high = (r_ctrl0 >> CTRL_DUMMY_HIGH_SHIFT) & 0x1; uint32_t dummy_low = (r_ctrl0 >> CTRL_DUMMY_LOW_SHIFT) & 0x3; + uint32_t dummies = ((dummy_high << 2) | dummy_low) * 8; - return ((dummy_high << 2) | dummy_low) * 8; + if (r_ctrl0 & CTRL_IO_DUAL_ADDR_DATA) { + dummies /= 2; + } + + return dummies; } static void aspeed_smc_flash_send_addr(AspeedSMCFlash *fl, uint32_t addr) From a57baeb45e1471f88db4a8da11a307603cfc8657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 26 Jun 2018 17:50:39 +0100 Subject: [PATCH 1593/2380] aspeed/smc: fix HW strapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only the flash type is strapped by HW. The 4BYTE mode is set by firmware when the flash device is detected. Signed-off-by: Cédric Le Goater Reviewed-by: Andrew Jeffery Message-id: 20180612065716.10587-3-clg@kaod.org Signed-off-by: Peter Maydell --- hw/ssi/aspeed_smc.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index fce126e6ee..b153708935 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -639,23 +639,17 @@ static void aspeed_smc_reset(DeviceState *d) aspeed_smc_segment_to_reg(&s->ctrl->segments[i]); } - /* HW strapping for AST2500 FMC controllers */ + /* HW strapping flash type for FMC controllers */ if (s->ctrl->segments == aspeed_segments_ast2500_fmc) { /* flash type is fixed to SPI for CE0 and CE1 */ s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1); - - /* 4BYTE mode is autodetected for CE0. Let's force it to 1 for - * now */ - s->regs[s->r_ce_ctrl] |= (1 << (CTRL_EXTENDED0)); } /* HW strapping for AST2400 FMC controllers (SCU70). Let's use the * configuration of the palmetto-bmc machine */ if (s->ctrl->segments == aspeed_segments_fmc) { s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); - - s->regs[s->r_ce_ctrl] |= (1 << (CTRL_EXTENDED0)); } } From 96c4be955b6ee37544d10bb8a123226bb0f014cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 26 Jun 2018 17:50:39 +0100 Subject: [PATCH 1594/2380] aspeed/smc: rename aspeed_smc_flash_send_addr() to aspeed_smc_flash_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also handle the fake transfers for dummy bytes in this setup routine. It will be useful when we activate MMIO execution. Signed-off-by: Cédric Le Goater Reviewed-by: Andrew Jeffery Message-id: 20180612065716.10587-4-clg@kaod.org Signed-off-by: Peter Maydell --- hw/ssi/aspeed_smc.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index b153708935..b29bfd3124 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -503,10 +503,11 @@ static int aspeed_smc_flash_dummies(const AspeedSMCFlash *fl) return dummies; } -static void aspeed_smc_flash_send_addr(AspeedSMCFlash *fl, uint32_t addr) +static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr) { const AspeedSMCState *s = fl->controller; uint8_t cmd = aspeed_smc_flash_cmd(fl); + int i; /* Flash access can not exceed CS segment */ addr = aspeed_smc_check_segment_addr(fl, addr); @@ -519,6 +520,18 @@ static void aspeed_smc_flash_send_addr(AspeedSMCFlash *fl, uint32_t addr) ssi_transfer(s->spi, (addr >> 16) & 0xff); ssi_transfer(s->spi, (addr >> 8) & 0xff); ssi_transfer(s->spi, (addr & 0xff)); + + /* + * Use fake transfers to model dummy bytes. The value should + * be configured to some non-zero value in fast read mode and + * zero in read mode. But, as the HW allows inconsistent + * settings, let's check for fast read mode. + */ + if (aspeed_smc_flash_mode(fl) == CTRL_FREADMODE) { + for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) { + ssi_transfer(fl->controller->spi, 0xFF); + } + } } static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) @@ -537,19 +550,7 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) case CTRL_READMODE: case CTRL_FREADMODE: aspeed_smc_flash_select(fl); - aspeed_smc_flash_send_addr(fl, addr); - - /* - * Use fake transfers to model dummy bytes. The value should - * be configured to some non-zero value in fast read mode and - * zero in read mode. But, as the HW allows inconsistent - * settings, let's check for fast read mode. - */ - if (aspeed_smc_flash_mode(fl) == CTRL_FREADMODE) { - for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) { - ssi_transfer(fl->controller->spi, 0xFF); - } - } + aspeed_smc_flash_setup(fl, addr); for (i = 0; i < size; i++) { ret |= ssi_transfer(s->spi, 0x0) << (8 * i); @@ -586,7 +587,7 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, break; case CTRL_WRITEMODE: aspeed_smc_flash_select(fl); - aspeed_smc_flash_send_addr(fl, addr); + aspeed_smc_flash_setup(fl, addr); for (i = 0; i < size; i++) { ssi_transfer(s->spi, (data >> (8 * i)) & 0xff); From 1f7161d2c83a4ebba296ffece26f992be5754471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:39 +0100 Subject: [PATCH 1595/2380] MAINTAINERS: Adopt the Gumstix computers-on-module machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These COMs are hard to find, and the companie dropped the support few years ago. Per the "Gumstix Product Changes, Known Issues, and EOL" pdf: - Phasing out: PXA270-based Verdex product line September 2012 - Phasing out: PXA255-based Basix & Connex September 2009 However there are still booting SD card image availables, very convenient to stress test the QEMU SD card implementation. Therefore I volunteer to keep an eye on this file, while it is useful for testing. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180606144706.29732-1-f4bug@amsat.org Signed-off-by: Peter Maydell --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 2874ddce60..1676b1ba71 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -501,9 +501,10 @@ F: include/hw/arm/digic.h F: hw/*/digic* Gumstix +M: Philippe Mathieu-Daudé L: qemu-devel@nongnu.org L: qemu-arm@nongnu.org -S: Orphan +S: Odd Fixes F: hw/arm/gumstix.c i.MX31 From 21d887cde954ca683f37d9c7d20371cdf26686be Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu Date: Tue, 26 Jun 2018 17:50:39 +0100 Subject: [PATCH 1596/2380] xilinx_spips: Make dma transactions as per dma_burst_size Qspi dma has a burst length of 64 bytes, So limit the transactions w.r.t dma-burst-size property. Signed-off-by: Sai Pavan Boddu Reviewed-by: Edgar E. Iglesias Message-id: 1529660880-30376-1-git-send-email-sai.pavan.boddu@xilinx.com Signed-off-by: Peter Maydell --- hw/ssi/xilinx_spips.c | 23 ++++++++++++++++++++--- include/hw/ssi/xilinx_spips.h | 5 ++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index f599025956..c052bfc4b3 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -851,12 +851,17 @@ static void xlnx_zynqmp_qspips_notify(void *opaque) { size_t ret; uint32_t num; - const void *rxd = pop_buf(recv_fifo, 4, &num); + const void *rxd; + int len; + + len = recv_fifo->num >= rq->dma_burst_size ? rq->dma_burst_size : + recv_fifo->num; + rxd = pop_buf(recv_fifo, len, &num); memcpy(rq->dma_buf, rxd, num); - ret = stream_push(rq->dma, rq->dma_buf, 4); - assert(ret == 4); + ret = stream_push(rq->dma, rq->dma_buf, num); + assert(ret == num); xlnx_zynqmp_qspips_check_flush(rq); } } @@ -1333,6 +1338,12 @@ static void xlnx_zynqmp_qspips_realize(DeviceState *dev, Error **errp) XlnxZynqMPQSPIPS *s = XLNX_ZYNQMP_QSPIPS(dev); XilinxSPIPSClass *xsc = XILINX_SPIPS_GET_CLASS(s); + if (s->dma_burst_size > QSPI_DMA_MAX_BURST_SIZE) { + error_setg(errp, + "qspi dma burst size %u exceeds maximum limit %d", + s->dma_burst_size, QSPI_DMA_MAX_BURST_SIZE); + return; + } xilinx_qspips_realize(dev, errp); fifo8_create(&s->rx_fifo_g, xsc->rx_fifo_size); fifo8_create(&s->tx_fifo_g, xsc->tx_fifo_size); @@ -1411,6 +1422,11 @@ static const VMStateDescription vmstate_xlnx_zynqmp_qspips = { } }; +static Property xilinx_zynqmp_qspips_properties[] = { + DEFINE_PROP_UINT32("dma-burst-size", XlnxZynqMPQSPIPS, dma_burst_size, 64), + DEFINE_PROP_END_OF_LIST(), +}; + static Property xilinx_qspips_properties[] = { /* We had to turn this off for 2.10 as it is not compatible with migration. * It can be enabled but will prevent the device to be migrated. @@ -1463,6 +1479,7 @@ static void xlnx_zynqmp_qspips_class_init(ObjectClass *klass, void * data) dc->realize = xlnx_zynqmp_qspips_realize; dc->reset = xlnx_zynqmp_qspips_reset; dc->vmsd = &vmstate_xlnx_zynqmp_qspips; + dc->props = xilinx_zynqmp_qspips_properties; xsc->reg_ops = &xlnx_zynqmp_qspips_ops; xsc->rx_fifo_size = RXFF_A_Q; xsc->tx_fifo_size = TXFF_A_Q; diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h index d398a4e81c..a0a0ae7584 100644 --- a/include/hw/ssi/xilinx_spips.h +++ b/include/hw/ssi/xilinx_spips.h @@ -37,6 +37,8 @@ typedef struct XilinxSPIPS XilinxSPIPS; /* Bite off 4k chunks at a time */ #define LQSPI_CACHE_SIZE 1024 +#define QSPI_DMA_MAX_BURST_SIZE 2048 + typedef enum { READ = 0x3, READ_4 = 0x13, FAST_READ = 0xb, FAST_READ_4 = 0x0c, @@ -95,7 +97,6 @@ typedef struct { XilinxQSPIPS parent_obj; StreamSlave *dma; - uint8_t dma_buf[4]; int gqspi_irqline; uint32_t regs[XLNX_ZYNQMP_SPIPS_R_MAX]; @@ -113,6 +114,8 @@ typedef struct { uint8_t rx_fifo_g_align; uint8_t tx_fifo_g_align; bool man_start_com_g; + uint32_t dma_burst_size; + uint8_t dma_buf[QSPI_DMA_MAX_BURST_SIZE]; } XlnxZynqMPQSPIPS; typedef struct XilinxSPIPSClass { From f87db1babe9fc3853657c43309f06e08527ad66f Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 26 Jun 2018 17:50:39 +0100 Subject: [PATCH 1597/2380] MAINTAINERS: Add ASPEED BMCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds Cedric as the maintainer, with Andrew and I as reviewers, for the ASPEED boards and the peripherals we have developed. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Andrew Jeffery Acked-by: Cédric Le Goater Signed-off-by: Joel Stanley Message-id: 20180625140055.32223-1-joel@jms.id.au Signed-off-by: Peter Maydell --- MAINTAINERS | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1676b1ba71..8c626f6a07 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -645,6 +645,17 @@ M: Subbaraya Sundeep S: Maintained F: hw/arm/msf2-som.c +ASPEED BMCs +M: Cédric Le Goater +R: Andrew Jeffery +R: Joel Stanley +L: qemu-arm@nongnu.org +S: Maintained +F: hw/*/*aspeed* +F: include/hw/*/*aspeed* +F: hw/net/ftgmac100.c +F: include/hw/net/ftgmac100.h + CRIS Machines ------------- Axis Dev88 From c2e846bba52301b7410a527937987b63c67aad3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:40 +0100 Subject: [PATCH 1598/2380] hw/input/pckbd: Use qemu_log_mask(GUEST_ERROR) instead of fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-2-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/input/pckbd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index f33e3fc63d..07c8801387 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/isa/isa.h" #include "hw/i386/pc.h" @@ -308,7 +309,8 @@ static void kbd_write_command(void *opaque, hwaddr addr, /* ignore that */ break; default: - fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", (int)val); + qemu_log_mask(LOG_GUEST_ERROR, + "unsupported keyboard cmd=0x%02" PRIx64 "\n", val); break; } } From 56112168abcdaa145fe8463d38cab218c11891dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:40 +0100 Subject: [PATCH 1599/2380] hw/input/tsc2005: Use qemu_log_mask(GUEST_ERROR) instead of fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-3-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/input/tsc2005.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c index 7990954b6c..4dd95596ab 100644 --- a/hw/input/tsc2005.c +++ b/hw/input/tsc2005.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "qemu/timer.h" #include "ui/console.h" @@ -208,9 +209,10 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data) } s->nextprecision = (data >> 13) & 1; s->timing[0] = data & 0x1fff; - if ((s->timing[0] >> 11) == 3) - fprintf(stderr, "%s: illegal conversion clock setting\n", - __func__); + if ((s->timing[0] >> 11) == 3) { + qemu_log_mask(LOG_GUEST_ERROR, + "tsc2005_write: illegal conversion clock setting\n"); + } break; case 0xd: /* CFR1 */ s->timing[1] = data & 0xf07; @@ -221,8 +223,9 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data) break; default: - fprintf(stderr, "%s: write into read-only register %x\n", - __func__, reg); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write into read-only register 0x%x\n", + __func__, reg); } } From f3724bf5e635f9cecaa37668b7aa3ea010a1ffb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:40 +0100 Subject: [PATCH 1600/2380] hw/dma/omap_dma: Use qemu_log_mask(UNIMP) instead of printf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-4-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/dma/omap_dma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index abd18c67ea..ab3a1b0451 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu-common.h" #include "qemu/timer.h" #include "hw/arm/omap.h" @@ -1439,8 +1440,9 @@ static int omap_dma_sys_read(struct omap_dma_s *s, int offset, case 0x480: /* DMA_PCh0_SR */ case 0x482: /* DMA_PCh1_SR */ case 0x4c0: /* DMA_PChD_SR_0 */ - printf("%s: Physical Channel Status Registers not implemented.\n", - __func__); + qemu_log_mask(LOG_UNIMP, + "%s: Physical Channel Status Registers not implemented\n", + __func__); *ret = 0xff; break; From e26745d5573461bb9e2e9ad70b1fc6acd8fe040a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:40 +0100 Subject: [PATCH 1601/2380] hw/dma/omap_dma: Use qemu_log_mask(GUEST_ERROR) instead of fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-5-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/dma/omap_dma.c | 64 +++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index ab3a1b0451..cbb920f31d 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -879,15 +879,18 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s, ch->burst[0] = (value & 0x0180) >> 7; ch->pack[0] = (value & 0x0040) >> 6; ch->port[0] = (enum omap_dma_port) ((value & 0x003c) >> 2); - if (ch->port[0] >= __omap_dma_port_last) - printf("%s: invalid DMA port %i\n", __func__, - ch->port[0]); - if (ch->port[1] >= __omap_dma_port_last) - printf("%s: invalid DMA port %i\n", __func__, - ch->port[1]); + if (ch->port[0] >= __omap_dma_port_last) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid DMA port %i\n", + __func__, ch->port[0]); + } + if (ch->port[1] >= __omap_dma_port_last) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid DMA port %i\n", + __func__, ch->port[1]); + } ch->data_type = 1 << (value & 3); if ((value & 3) == 3) { - printf("%s: bad data_type for DMA channel\n", __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad data_type for DMA channel\n", __func__); ch->data_type >>= 1; } break; @@ -1899,14 +1902,18 @@ static void omap_dma4_write(void *opaque, hwaddr addr, if (value & 2) /* SOFTRESET */ omap_dma_reset(s->dma); s->ocp = value & 0x3321; - if (((s->ocp >> 12) & 3) == 3) /* MIDLEMODE */ - fprintf(stderr, "%s: invalid DMA power mode\n", __func__); + if (((s->ocp >> 12) & 3) == 3) { /* MIDLEMODE */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid DMA power mode\n", + __func__); + } return; case 0x78: /* DMA4_GCR */ s->gcr = value & 0x00ff00ff; - if ((value & 0xff) == 0x00) /* MAX_CHANNEL_FIFO_DEPTH */ - fprintf(stderr, "%s: wrong FIFO depth in GCR\n", __func__); + if ((value & 0xff) == 0x00) { /* MAX_CHANNEL_FIFO_DEPTH */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: wrong FIFO depth in GCR\n", + __func__); + } return; case 0x80 ... 0xfff: @@ -1935,9 +1942,11 @@ static void omap_dma4_write(void *opaque, hwaddr addr, case 0x00: /* DMA4_CCR */ ch->buf_disable = (value >> 25) & 1; ch->src_sync = (value >> 24) & 1; /* XXX For CamDMA must be 1 */ - if (ch->buf_disable && !ch->src_sync) - fprintf(stderr, "%s: Buffering disable is not allowed in " - "destination synchronised mode\n", __func__); + if (ch->buf_disable && !ch->src_sync) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Buffering disable is not allowed in " + "destination synchronised mode\n", __func__); + } ch->prefetch = (value >> 23) & 1; ch->bs = (value >> 18) & 1; ch->transparent_copy = (value >> 17) & 1; @@ -1947,9 +1956,11 @@ static void omap_dma4_write(void *opaque, hwaddr addr, ch->suspend = (value & 0x0100) >> 8; ch->priority = (value & 0x0040) >> 6; ch->fs = (value & 0x0020) >> 5; - if (ch->fs && ch->bs && ch->mode[0] && ch->mode[1]) - fprintf(stderr, "%s: For a packet transfer at least one port " - "must be constant-addressed\n", __func__); + if (ch->fs && ch->bs && ch->mode[0] && ch->mode[1]) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: For a packet transfer at least one port " + "must be constant-addressed\n", __func__); + } ch->sync = (value & 0x001f) | ((value >> 14) & 0x0060); /* XXX must be 0x01 for CamDMA */ @@ -1978,9 +1989,11 @@ static void omap_dma4_write(void *opaque, hwaddr addr, ch->endian_lock[0] =(value >> 20) & 1; ch->endian[1] =(value >> 19) & 1; ch->endian_lock[1] =(value >> 18) & 1; - if (ch->endian[0] != ch->endian[1]) - fprintf(stderr, "%s: DMA endianness conversion enable attempt\n", - __func__); + if (ch->endian[0] != ch->endian[1]) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: DMA endianness conversion enable attempt\n", + __func__); + } ch->write_mode = (value >> 16) & 3; ch->burst[1] = (value & 0xc000) >> 14; ch->pack[1] = (value & 0x2000) >> 13; @@ -1988,12 +2001,15 @@ static void omap_dma4_write(void *opaque, hwaddr addr, ch->burst[0] = (value & 0x0180) >> 7; ch->pack[0] = (value & 0x0040) >> 6; ch->translate[0] = (value & 0x003c) >> 2; - if (ch->translate[0] | ch->translate[1]) - fprintf(stderr, "%s: bad MReqAddressTranslate sideband signal\n", - __func__); + if (ch->translate[0] | ch->translate[1]) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad MReqAddressTranslate sideband signal\n", + __func__); + } ch->data_type = 1 << (value & 3); if ((value & 3) == 3) { - printf("%s: bad data_type for DMA channel\n", __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad data_type for DMA channel\n", __func__); ch->data_type >>= 1; } break; From 31a1246df674c83b46445b93206898da65104524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:40 +0100 Subject: [PATCH 1602/2380] hw/ssi/omap_spi: Use qemu_log_mask(GUEST_ERROR) instead of fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20180624040609.17572-6-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/ssi/omap_spi.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/ssi/omap_spi.c b/hw/ssi/omap_spi.c index 34163e5646..f278a55160 100644 --- a/hw/ssi/omap_spi.c +++ b/hw/ssi/omap_spi.c @@ -20,6 +20,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/arm/omap.h" @@ -294,11 +295,15 @@ static void omap_mcspi_write(void *opaque, hwaddr addr, case 0x2c: /* MCSPI_CHCONF */ if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ omap_mcspi_dmarequest_update(s->ch + ch); - if (((value >> 12) & 3) == 3) /* TRM */ - fprintf(stderr, "%s: invalid TRM value (3)\n", __func__); - if (((value >> 7) & 0x1f) < 3) /* WL */ - fprintf(stderr, "%s: invalid WL value (%" PRIx64 ")\n", - __func__, (value >> 7) & 0x1f); + if (((value >> 12) & 3) == 3) { /* TRM */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid TRM value (3)\n", + __func__); + } + if (((value >> 7) & 0x1f) < 3) { /* WL */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid WL value (%" PRIx64 ")\n", + __func__, (value >> 7) & 0x1f); + } s->ch[ch].config = value & 0x7fffff; break; From 25b98b96af0df0ca224d42e2b0dadaa06f5d49af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:40 +0100 Subject: [PATCH 1603/2380] hw/sd/omap_mmc: Use qemu_log_mask(UNIMP) instead of printf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-7-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/sd/omap_mmc.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 5b47cadf11..aa2a816f76 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -17,6 +17,7 @@ * with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/arm/omap.h" #include "hw/sd/sd.h" @@ -449,10 +450,14 @@ static void omap_mmc_write(void *opaque, hwaddr offset, s->enable = (value >> 11) & 1; s->be = (value >> 10) & 1; s->clkdiv = (value >> 0) & (s->rev >= 2 ? 0x3ff : 0xff); - if (s->mode != 0) - printf("SD mode %i unimplemented!\n", s->mode); - if (s->be != 0) - printf("SD FIFO byte sex unimplemented!\n"); + if (s->mode != 0) { + qemu_log_mask(LOG_UNIMP, + "omap_mmc_wr: mode #%i unimplemented\n", s->mode); + } + if (s->be != 0) { + qemu_log_mask(LOG_UNIMP, + "omap_mmc_wr: Big Endian not implemented\n"); + } if (s->dw != 0 && s->lines < 4) printf("4-bit SD bus enabled\n"); if (!s->enable) From 8d2774f0ff953f945167dcf7259e1f7a6091d19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:40 +0100 Subject: [PATCH 1604/2380] hw/i2c/omap_i2c: Use qemu_log_mask(UNIMP) instead of fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-8-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/i2c/omap_i2c.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c index 26e3e5ebf6..d02e734ea8 100644 --- a/hw/i2c/omap_i2c.c +++ b/hw/i2c/omap_i2c.c @@ -17,6 +17,7 @@ * with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/i2c/i2c.h" #include "hw/arm/omap.h" @@ -339,14 +340,15 @@ static void omap_i2c_write(void *opaque, hwaddr addr, } break; } - if ((value & (1 << 15)) && !(value & (1 << 10))) { /* MST */ - fprintf(stderr, "%s: I^2C slave mode not supported\n", - __func__); + if ((value & (1 << 15)) && !(value & (1 << 10))) { /* MST */ + qemu_log_mask(LOG_UNIMP, "%s: I^2C slave mode not supported\n", + __func__); break; } - if ((value & (1 << 15)) && value & (1 << 8)) { /* XA */ - fprintf(stderr, "%s: 10-bit addressing mode not supported\n", - __func__); + if ((value & (1 << 15)) && value & (1 << 8)) { /* XA */ + qemu_log_mask(LOG_UNIMP, + "%s: 10-bit addressing mode not supported\n", + __func__); break; } if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */ @@ -392,8 +394,10 @@ static void omap_i2c_write(void *opaque, hwaddr addr, s->stat |= 0x3f; omap_i2c_interrupts_update(s); } - if (value & (1 << 15)) /* ST_EN */ - fprintf(stderr, "%s: System Test not supported\n", __func__); + if (value & (1 << 15)) { /* ST_EN */ + qemu_log_mask(LOG_UNIMP, + "%s: System Test not supported\n", __func__); + } break; default: From 415202d4c98e660b333a6dfefe5d4b33b7e67595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:40 +0100 Subject: [PATCH 1605/2380] hw/arm/omap1: Use qemu_log_mask(GUEST_ERROR) instead of fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCMI_VERBOSE is no more used, drop the OMAP_8/16/32B_REG macros. Suggested-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-9-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/arm/omap1.c | 18 ++++++++++++------ include/hw/arm/omap.h | 18 ------------------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 9af04728e3..539d29ef9c 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -34,12 +34,18 @@ #include "qemu/cutils.h" #include "qemu/bcd.h" +static inline void omap_log_badwidth(const char *funcname, hwaddr addr, int sz) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: %d-bit register %#08" HWADDR_PRIx "\n", + funcname, 8 * sz, addr); +} + /* Should signal the TCMI/GPMC */ uint32_t omap_badwidth_read8(void *opaque, hwaddr addr) { uint8_t ret; - OMAP_8B_REG(addr); + omap_log_badwidth(__func__, addr, 1); cpu_physical_memory_read(addr, &ret, 1); return ret; } @@ -49,7 +55,7 @@ void omap_badwidth_write8(void *opaque, hwaddr addr, { uint8_t val8 = value; - OMAP_8B_REG(addr); + omap_log_badwidth(__func__, addr, 1); cpu_physical_memory_write(addr, &val8, 1); } @@ -57,7 +63,7 @@ uint32_t omap_badwidth_read16(void *opaque, hwaddr addr) { uint16_t ret; - OMAP_16B_REG(addr); + omap_log_badwidth(__func__, addr, 2); cpu_physical_memory_read(addr, &ret, 2); return ret; } @@ -67,7 +73,7 @@ void omap_badwidth_write16(void *opaque, hwaddr addr, { uint16_t val16 = value; - OMAP_16B_REG(addr); + omap_log_badwidth(__func__, addr, 2); cpu_physical_memory_write(addr, &val16, 2); } @@ -75,7 +81,7 @@ uint32_t omap_badwidth_read32(void *opaque, hwaddr addr) { uint32_t ret; - OMAP_32B_REG(addr); + omap_log_badwidth(__func__, addr, 4); cpu_physical_memory_read(addr, &ret, 4); return ret; } @@ -83,7 +89,7 @@ uint32_t omap_badwidth_read32(void *opaque, hwaddr addr) void omap_badwidth_write32(void *opaque, hwaddr addr, uint32_t value) { - OMAP_32B_REG(addr); + omap_log_badwidth(__func__, addr, 4); cpu_physical_memory_write(addr, &value, 4); } diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index b398607b06..39abba753d 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -993,24 +993,6 @@ enum { #define OMAP_GPIOSW_INVERTED 0x0001 #define OMAP_GPIOSW_OUTPUT 0x0002 -# define TCMI_VERBOSE 1 - -# ifdef TCMI_VERBOSE -# define OMAP_8B_REG(paddr) \ - fprintf(stderr, "%s: 8-bit register " OMAP_FMT_plx "\n", \ - __func__, paddr) -# define OMAP_16B_REG(paddr) \ - fprintf(stderr, "%s: 16-bit register " OMAP_FMT_plx "\n", \ - __func__, paddr) -# define OMAP_32B_REG(paddr) \ - fprintf(stderr, "%s: 32-bit register " OMAP_FMT_plx "\n", \ - __func__, paddr) -# else -# define OMAP_8B_REG(paddr) -# define OMAP_16B_REG(paddr) -# define OMAP_32B_REG(paddr) -# endif - # define OMAP_MPUI_REG_MASK 0x000007ff #endif /* hw_omap_h */ From 84b335c6d00a89265cc7f1eaf2a381706c87ac9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:40 +0100 Subject: [PATCH 1606/2380] hw/arm/omap: Use qemu_log_mask(GUEST_ERROR) instead of fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-10-f4bug@amsat.org Signed-off-by: Peter Maydell --- include/hw/arm/omap.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index 39abba753d..e7fbd340f3 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -21,6 +21,7 @@ # define hw_omap_h "omap.h" #include "hw/irq.h" #include "target/arm/cpu-qom.h" +#include "qemu/log.h" # define OMAP_EMIFS_BASE 0x00000000 # define OMAP2_Q0_BASE 0x00000000 @@ -944,8 +945,6 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, unsigned long sdram_size, const char *core); -#define OMAP_FMT_plx "%#08" HWADDR_PRIx - uint32_t omap_badwidth_read8(void *opaque, hwaddr addr); void omap_badwidth_write8(void *opaque, hwaddr addr, uint32_t value); @@ -959,11 +958,12 @@ void omap_badwidth_write32(void *opaque, hwaddr addr, void omap_mpu_wakeup(void *opaque, int irq, int req); # define OMAP_BAD_REG(paddr) \ - fprintf(stderr, "%s: Bad register " OMAP_FMT_plx "\n", \ - __func__, paddr) + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad register %#08"HWADDR_PRIx"\n", \ + __func__, paddr) # define OMAP_RO_REG(paddr) \ - fprintf(stderr, "%s: Read-only register " OMAP_FMT_plx "\n", \ - __func__, paddr) + qemu_log_mask(LOG_GUEST_ERROR, "%s: Read-only register %#08" \ + HWADDR_PRIx "\n", \ + __func__, paddr) /* OMAP-specific Linux bootloader tags for the ATAG_BOARD area (Board-specifc tags are not here) */ From 9194524b0d555dbf0b1cefb303cc362a0458586f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:41 +0100 Subject: [PATCH 1607/2380] hw/arm/stellaris: Use qemu_log_mask(UNIMP) instead of fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-11-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index a8f1f6a912..d06e366402 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -560,7 +560,7 @@ static void ssys_write(void *opaque, hwaddr offset, case 0x040: /* SRCR0 */ case 0x044: /* SRCR1 */ case 0x048: /* SRCR2 */ - fprintf(stderr, "Peripheral reset not implemented\n"); + qemu_log_mask(LOG_UNIMP, "Peripheral reset not implemented\n"); break; case 0x054: /* IMC */ s->int_mask = value & 0x7f; From 5786e35da7beb10e7267e9762b56dfff95a7f0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:41 +0100 Subject: [PATCH 1608/2380] hw/net/stellaris_enet: Fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180624040609.17572-12-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/net/stellaris_enet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 04bd10ada3..1e7329517c 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -340,7 +340,7 @@ static uint64_t stellaris_enet_read(void *opaque, hwaddr offset, return s->np; case 0x38: /* TR */ return 0; - case 0x3c: /* Undocuented: Timestamp? */ + case 0x3c: /* Undocumented: Timestamp? */ return 0; default: hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset); From f6de99571437fca0ead566a4a1f26a1bdaa2316a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:41 +0100 Subject: [PATCH 1609/2380] hw/net/stellaris_enet: Use qemu_log_mask(GUEST_ERROR) instead of hw_error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hw_error() finally calls abort(), but there is no need to abort here. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180624040609.17572-13-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/net/stellaris_enet.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 1e7329517c..165562d788 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" +#include "qemu/log.h" #include //#define DEBUG_STELLARIS_ENET 1 @@ -343,7 +344,9 @@ static uint64_t stellaris_enet_read(void *opaque, hwaddr offset, case 0x3c: /* Undocumented: Timestamp? */ return 0; default: - hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "stellaris_enet_rd%d: Illegal register" + " 0x02%" HWADDR_PRIx "\n", + size * 8, offset); return 0; } } @@ -442,7 +445,9 @@ static void stellaris_enet_write(void *opaque, hwaddr offset, /* Ignored. */ break; default: - hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "stellaris_enet_wr%d: Illegal register " + "0x02%" HWADDR_PRIx " = 0x%" PRIx64 "\n", + size * 8, offset, value); } } From b9992d122da35f433b0d825523e4b722509e824e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:41 +0100 Subject: [PATCH 1610/2380] hw/net/smc91c111: Use qemu_log_mask(GUEST_ERROR) instead of hw_error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hw_error() finally calls abort(), but there is no need to abort here. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-14-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/net/smc91c111.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index c8cc5379b7..9094c0b47c 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -11,6 +11,7 @@ #include "hw/sysbus.h" #include "net/net.h" #include "hw/devices.h" +#include "qemu/log.h" /* For crc32 */ #include @@ -478,7 +479,9 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, } break; } - hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "smc91c111_write(bank:%d) Illegal register" + " 0x%" HWADDR_PRIx " = 0x%x\n", + s->bank, offset, value); } static uint32_t smc91c111_readb(void *opaque, hwaddr offset) @@ -621,7 +624,9 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset) } break; } - hw_error("smc91c111_read: Bad reg %d:%x\n", s->bank, (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "smc91c111_read(bank:%d) Illegal register" + " 0x%" HWADDR_PRIx "\n", + s->bank, offset); return 0; } From 637e5d86fce4859c3b0bb7204bd06d803caf803c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:41 +0100 Subject: [PATCH 1611/2380] hw/net/smc91c111: Use qemu_log_mask(UNIMP) instead of fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180624040609.17572-15-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/net/smc91c111.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 9094c0b47c..d2fd2040e8 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -362,10 +362,14 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, SET_HIGH(gpr, value); return; case 12: /* Control */ - if (value & 1) - fprintf(stderr, "smc91c111:EEPROM store not implemented\n"); - if (value & 2) - fprintf(stderr, "smc91c111:EEPROM reload not implemented\n"); + if (value & 1) { + qemu_log_mask(LOG_UNIMP, + "smc91c111: EEPROM store not implemented\n"); + } + if (value & 2) { + qemu_log_mask(LOG_UNIMP, + "smc91c111: EEPROM reload not implemented\n"); + } value &= ~3; SET_LOW(ctr, value); return; From bc281efff60f81fdde6014daa24ca81484b42814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:41 +0100 Subject: [PATCH 1612/2380] hw/arm/stellaris: Fix gptm_write() error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed in df3692e04b2. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180624040609.17572-16-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index d06e366402..42baa768b2 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -294,7 +294,7 @@ static void gptm_write(void *opaque, hwaddr offset, break; default: qemu_log_mask(LOG_GUEST_ERROR, - "GPTM: read at bad offset 0x%x\n", (int)offset); + "GPTM: write at bad offset 0x%x\n", (int)offset); } gptm_update_irq(s); } From d29183d3c0174e248b31bb2ee58b889f7baa3cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Jun 2018 17:50:41 +0100 Subject: [PATCH 1613/2380] hw/arm/stellaris: Use HWADDR_PRIx to display register address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180624040609.17572-17-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 42baa768b2..dc521b4a5a 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -212,7 +212,8 @@ static uint64_t gptm_read(void *opaque, hwaddr offset, return 0; default: qemu_log_mask(LOG_GUEST_ERROR, - "GPTM: read at bad offset 0x%x\n", (int)offset); + "GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n", + offset); return 0; } } @@ -294,7 +295,8 @@ static void gptm_write(void *opaque, hwaddr offset, break; default: qemu_log_mask(LOG_GUEST_ERROR, - "GPTM: write at bad offset 0x%x\n", (int)offset); + "GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n", + offset); } gptm_update_irq(s); } From 55df6fcf5476b44bc1b95554e686ab3e91d725c5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Jun 2018 17:50:41 +0100 Subject: [PATCH 1614/2380] tcg: Support MMU protection regions smaller than TARGET_PAGE_SIZE Add support for MMU protection regions that are smaller than TARGET_PAGE_SIZE. We do this by marking the TLB entry for those pages with a flag TLB_RECHECK. This flag causes us to always take the slow-path for accesses. In the slow path we can then special case them to always call tlb_fill() again, so we have the correct information for the exact address being accessed. This change allows us to handle reading and writing from small regions; we cannot deal with execution from the small region. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180620130619.11362-2-peter.maydell@linaro.org --- accel/tcg/cputlb.c | 131 +++++++++++++++++++++++++++++------ accel/tcg/softmmu_template.h | 24 ++++--- include/exec/cpu-all.h | 5 +- 3 files changed, 130 insertions(+), 30 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 719cca2268..eebe97dabb 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -613,27 +613,42 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, target_ulong code_address; uintptr_t addend; CPUTLBEntry *te, *tv, tn; - hwaddr iotlb, xlat, sz; + hwaddr iotlb, xlat, sz, paddr_page; + target_ulong vaddr_page; unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; int asidx = cpu_asidx_from_attrs(cpu, attrs); assert_cpu_is_self(cpu); - assert(size >= TARGET_PAGE_SIZE); - if (size != TARGET_PAGE_SIZE) { - tlb_add_large_page(env, vaddr, size); - } - sz = size; - section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz, - attrs, &prot); + if (size < TARGET_PAGE_SIZE) { + sz = TARGET_PAGE_SIZE; + } else { + if (size > TARGET_PAGE_SIZE) { + tlb_add_large_page(env, vaddr, size); + } + sz = size; + } + vaddr_page = vaddr & TARGET_PAGE_MASK; + paddr_page = paddr & TARGET_PAGE_MASK; + + section = address_space_translate_for_iotlb(cpu, asidx, paddr_page, + &xlat, &sz, attrs, &prot); assert(sz >= TARGET_PAGE_SIZE); tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx " prot=%x idx=%d\n", vaddr, paddr, prot, mmu_idx); - address = vaddr; - if (!memory_region_is_ram(section->mr) && !memory_region_is_romd(section->mr)) { + address = vaddr_page; + if (size < TARGET_PAGE_SIZE) { + /* + * Slow-path the TLB entries; we will repeat the MMU check and TLB + * fill on every access. + */ + address |= TLB_RECHECK; + } + if (!memory_region_is_ram(section->mr) && + !memory_region_is_romd(section->mr)) { /* IO memory case */ address |= TLB_MMIO; addend = 0; @@ -643,10 +658,10 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, } code_address = address; - iotlb = memory_region_section_get_iotlb(cpu, section, vaddr, paddr, xlat, - prot, &address); + iotlb = memory_region_section_get_iotlb(cpu, section, vaddr_page, + paddr_page, xlat, prot, &address); - index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + index = (vaddr_page >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); te = &env->tlb_table[mmu_idx][index]; /* do not discard the translation in te, evict it into a victim tlb */ tv = &env->tlb_v_table[mmu_idx][vidx]; @@ -662,18 +677,18 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, * TARGET_PAGE_BITS, and either * + the ram_addr_t of the page base of the target RAM (if NOTDIRTY or ROM) * + the offset within section->mr of the page base (otherwise) - * We subtract the vaddr (which is page aligned and thus won't + * We subtract the vaddr_page (which is page aligned and thus won't * disturb the low bits) to give an offset which can be added to the * (non-page-aligned) vaddr of the eventual memory access to get * the MemoryRegion offset for the access. Note that the vaddr we * subtract here is that of the page base, and not the same as the * vaddr we add back in io_readx()/io_writex()/get_page_addr_code(). */ - env->iotlb[mmu_idx][index].addr = iotlb - vaddr; + env->iotlb[mmu_idx][index].addr = iotlb - vaddr_page; env->iotlb[mmu_idx][index].attrs = attrs; /* Now calculate the new entry */ - tn.addend = addend - vaddr; + tn.addend = addend - vaddr_page; if (prot & PAGE_READ) { tn.addr_read = address; } else { @@ -694,7 +709,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, tn.addr_write = address | TLB_MMIO; } else if (memory_region_is_ram(section->mr) && cpu_physical_memory_is_clean( - memory_region_get_ram_addr(section->mr) + xlat)) { + memory_region_get_ram_addr(section->mr) + xlat)) { tn.addr_write = address | TLB_NOTDIRTY; } else { tn.addr_write = address; @@ -767,7 +782,8 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, int mmu_idx, - target_ulong addr, uintptr_t retaddr, int size) + target_ulong addr, uintptr_t retaddr, + bool recheck, int size) { CPUState *cpu = ENV_GET_CPU(env); hwaddr mr_offset; @@ -777,6 +793,29 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, bool locked = false; MemTxResult r; + if (recheck) { + /* + * This is a TLB_RECHECK access, where the MMU protection + * covers a smaller range than a target page, and we must + * repeat the MMU check here. This tlb_fill() call might + * longjump out if this access should cause a guest exception. + */ + int index; + target_ulong tlb_addr; + + tlb_fill(cpu, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); + + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_addr = env->tlb_table[mmu_idx][index].addr_read; + if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { + /* RAM access */ + uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend; + + return ldn_p((void *)haddr, size); + } + /* Fall through for handling IO accesses */ + } + section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); mr = section->mr; mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; @@ -811,7 +850,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, int mmu_idx, uint64_t val, target_ulong addr, - uintptr_t retaddr, int size) + uintptr_t retaddr, bool recheck, int size) { CPUState *cpu = ENV_GET_CPU(env); hwaddr mr_offset; @@ -820,6 +859,30 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, bool locked = false; MemTxResult r; + if (recheck) { + /* + * This is a TLB_RECHECK access, where the MMU protection + * covers a smaller range than a target page, and we must + * repeat the MMU check here. This tlb_fill() call might + * longjump out if this access should cause a guest exception. + */ + int index; + target_ulong tlb_addr; + + tlb_fill(cpu, addr, size, MMU_DATA_STORE, mmu_idx, retaddr); + + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_addr = env->tlb_table[mmu_idx][index].addr_write; + if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { + /* RAM access */ + uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend; + + stn_p((void *)haddr, size, val); + return; + } + /* Fall through for handling IO accesses */ + } + section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); mr = section->mr; mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; @@ -903,6 +966,32 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0); } } + + if (unlikely(env->tlb_table[mmu_idx][index].addr_code & TLB_RECHECK)) { + /* + * This is a TLB_RECHECK access, where the MMU protection + * covers a smaller range than a target page, and we must + * repeat the MMU check here. This tlb_fill() call might + * longjump out if this access should cause a guest exception. + */ + int index; + target_ulong tlb_addr; + + tlb_fill(cpu, addr, 0, MMU_INST_FETCH, mmu_idx, 0); + + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_addr = env->tlb_table[mmu_idx][index].addr_code; + if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { + /* RAM access. We can't handle this, so for now just stop */ + cpu_abort(cpu, "Unable to handle guest executing from RAM within " + "a small MPU region at 0x" TARGET_FMT_lx, addr); + } + /* + * Fall through to handle IO accesses (which will almost certainly + * also result in failure) + */ + } + iotlbentry = &env->iotlb[mmu_idx][index]; section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); mr = section->mr; @@ -1011,8 +1100,8 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, tlb_addr = tlbe->addr_write & ~TLB_INVALID_MASK; } - /* Notice an IO access */ - if (unlikely(tlb_addr & TLB_MMIO)) { + /* Notice an IO access or a needs-MMU-lookup access */ + if (unlikely(tlb_addr & (TLB_MMIO | TLB_RECHECK))) { /* There's really nothing that can be done to support this apart from stop-the-world. */ goto stop_the_world; diff --git a/accel/tcg/softmmu_template.h b/accel/tcg/softmmu_template.h index 239ea6692b..c47591c970 100644 --- a/accel/tcg/softmmu_template.h +++ b/accel/tcg/softmmu_template.h @@ -98,10 +98,12 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, size_t mmu_idx, size_t index, target_ulong addr, - uintptr_t retaddr) + uintptr_t retaddr, + bool recheck) { CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; - return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE); + return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck, + DATA_SIZE); } #endif @@ -138,7 +140,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ - res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); + res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, + tlb_addr & TLB_RECHECK); res = TGT_LE(res); return res; } @@ -205,7 +208,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ - res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); + res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, + tlb_addr & TLB_RECHECK); res = TGT_BE(res); return res; } @@ -259,10 +263,12 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env, size_t mmu_idx, size_t index, DATA_TYPE val, target_ulong addr, - uintptr_t retaddr) + uintptr_t retaddr, + bool recheck) { CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; - return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE); + return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, + recheck, DATA_SIZE); } void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, @@ -298,7 +304,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ val = TGT_LE(val); - glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); + glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, + retaddr, tlb_addr & TLB_RECHECK); return; } @@ -375,7 +382,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ val = TGT_BE(val); - glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); + glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr, + tlb_addr & TLB_RECHECK); return; } diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 7fa726b8e3..7338f57062 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -330,11 +330,14 @@ CPUArchState *cpu_copy(CPUArchState *env); #define TLB_NOTDIRTY (1 << (TARGET_PAGE_BITS - 2)) /* Set if TLB entry is an IO callback. */ #define TLB_MMIO (1 << (TARGET_PAGE_BITS - 3)) +/* Set if TLB entry must have MMU lookup repeated for every access */ +#define TLB_RECHECK (1 << (TARGET_PAGE_BITS - 4)) /* Use this mask to check interception with an alignment mask * in a TCG backend. */ -#define TLB_FLAGS_MASK (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO) +#define TLB_FLAGS_MASK (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO \ + | TLB_RECHECK) void dump_exec_info(FILE *f, fprintf_function cpu_fprintf); void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf); From e5e40999b5e03567ef654546e3d448431643f8f3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Jun 2018 17:50:41 +0100 Subject: [PATCH 1615/2380] target/arm: Set page (region) size in get_phys_addr_pmsav7() We want to handle small MPU region sizes for ARMv7M. To do this, make get_phys_addr_pmsav7() set the page size to the region size if it is less that TARGET_PAGE_SIZE, rather than working only in TARGET_PAGE_SIZE chunks. Since the core TCG code con't handle execution from small MPU regions, we strip the exec permission from them so that any execution attempts will cause an MPU exception, rather than allowing it to end up with a cpu_abort() in get_page_addr_code(). (The previous code's intention was to make any small page be treated as having no permissions, but unfortunately errors in the implementation meant that it didn't behave that way. It's possible that some binaries using small regions were accidentally working with our old behaviour and won't now.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180620130619.11362-3-peter.maydell@linaro.org --- target/arm/helper.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 1248d84e6f..a7edeb6663 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9596,6 +9596,7 @@ static inline bool m_is_system_region(CPUARMState *env, uint32_t address) static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, + target_ulong *page_size, ARMMMUFaultInfo *fi) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -9603,6 +9604,7 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, bool is_user = regime_is_user(env, mmu_idx); *phys_ptr = address; + *page_size = TARGET_PAGE_SIZE; *prot = 0; if (regime_translation_disabled(env, mmu_idx) || @@ -9675,16 +9677,12 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, rsize++; } } - if (rsize < TARGET_PAGE_BITS) { - qemu_log_mask(LOG_UNIMP, - "DRSR[%d]: No support for MPU (sub)region size of" - " %" PRIu32 " bytes. Minimum is %d.\n", - n, (1 << rsize), TARGET_PAGE_SIZE); - continue; - } if (srdis) { continue; } + if (rsize < TARGET_PAGE_BITS) { + *page_size = 1 << rsize; + } break; } @@ -9765,6 +9763,17 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, fi->type = ARMFault_Permission; fi->level = 1; + /* + * Core QEMU code can't handle execution from small pages yet, so + * don't try it. This way we'll get an MPU exception, rather than + * eventually causing QEMU to exit in get_page_addr_code(). + */ + if (*page_size < TARGET_PAGE_SIZE && (*prot & PAGE_EXEC)) { + qemu_log_mask(LOG_UNIMP, + "MPU: No support for execution from regions " + "smaller than 1K\n"); + *prot &= ~PAGE_EXEC; + } return !(*prot & (1 << access_type)); } @@ -10334,7 +10343,7 @@ static bool get_phys_addr(CPUARMState *env, target_ulong address, } else if (arm_feature(env, ARM_FEATURE_V7)) { /* PMSAv7 */ ret = get_phys_addr_pmsav7(env, address, access_type, mmu_idx, - phys_ptr, prot, fi); + phys_ptr, prot, page_size, fi); } else { /* Pre-v7 MPU */ ret = get_phys_addr_pmsav5(env, address, access_type, mmu_idx, @@ -10396,9 +10405,15 @@ bool arm_tlb_fill(CPUState *cs, vaddr address, core_to_arm_mmu_idx(env, mmu_idx), &phys_addr, &attrs, &prot, &page_size, fi, NULL); if (!ret) { - /* Map a single [sub]page. */ - phys_addr &= TARGET_PAGE_MASK; - address &= TARGET_PAGE_MASK; + /* + * Map a single [sub]page. Regions smaller than our declared + * target page size are handled specially, so for those we + * pass in the exact addresses. + */ + if (page_size >= TARGET_PAGE_SIZE) { + phys_addr &= TARGET_PAGE_MASK; + address &= TARGET_PAGE_MASK; + } tlb_set_page_with_attrs(cs, address, phys_addr, attrs, prot, mmu_idx, page_size); return 0; From 720424359917887c926a33d248131fbff84c9c28 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Jun 2018 17:50:42 +0100 Subject: [PATCH 1616/2380] target/arm: Handle small regions in get_phys_addr_pmsav8() Allow ARMv8M to handle small MPU and SAU region sizes, by making get_phys_add_pmsav8() set the page size to the 1 if the MPU or SAU region covers less than a TARGET_PAGE_SIZE. We choose to use a size of 1 because it makes no difference to the core code, and avoids having to track both the base and limit for SAU and MPU and then convert into an artificially restricted "page size" that the core code will then ignore. Since the core TCG code can't handle execution from small MPU regions, we strip the exec permission from them so that any execution attempts will cause an MPU exception, rather than allowing it to end up with a cpu_abort() in get_page_addr_code(). (The previous code's intention was to make any small page be treated as having no permissions, but unfortunately errors in the implementation meant that it didn't behave that way. It's possible that some binaries using small regions were accidentally working with our old behaviour and won't now.) We also retain an existing bug, where we ignored the possibility that the SAU region might not cover the entire page, in the case of executable regions. This is necessary because some currently-working guest code images rely on being able to execute from addresses which are covered by a page-sized MPU region but a smaller SAU region. We can remove this workaround if we ever support execution from small regions. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180620130619.11362-4-peter.maydell@linaro.org --- target/arm/helper.c | 78 ++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index a7edeb6663..3c6a4c565b 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -41,6 +41,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, /* Security attributes for an address, as returned by v8m_security_lookup. */ typedef struct V8M_SAttributes { + bool subpage; /* true if these attrs don't cover the whole TARGET_PAGE */ bool ns; bool nsc; uint8_t sregion; @@ -9804,6 +9805,8 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, int r; bool idau_exempt = false, idau_ns = true, idau_nsc = true; int idau_region = IREGION_NOTVALID; + uint32_t addr_page_base = address & TARGET_PAGE_MASK; + uint32_t addr_page_limit = addr_page_base + (TARGET_PAGE_SIZE - 1); if (cpu->idau) { IDAUInterfaceClass *iic = IDAU_INTERFACE_GET_CLASS(cpu->idau); @@ -9841,6 +9844,9 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, uint32_t limit = env->sau.rlar[r] | 0x1f; if (base <= address && limit >= address) { + if (base > addr_page_base || limit < addr_page_limit) { + sattrs->subpage = true; + } if (sattrs->srvalid) { /* If we hit in more than one region then we must report * as Secure, not NS-Callable, with no valid region @@ -9880,13 +9886,16 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, MemTxAttrs *txattrs, - int *prot, ARMMMUFaultInfo *fi, uint32_t *mregion) + int *prot, bool *is_subpage, + ARMMMUFaultInfo *fi, uint32_t *mregion) { /* Perform a PMSAv8 MPU lookup (without also doing the SAU check * that a full phys-to-virt translation does). * mregion is (if not NULL) set to the region number which matched, * or -1 if no region number is returned (MPU off, address did not * hit a region, address hit in multiple regions). + * We set is_subpage to true if the region hit doesn't cover the + * entire TARGET_PAGE the address is within. */ ARMCPU *cpu = arm_env_get_cpu(env); bool is_user = regime_is_user(env, mmu_idx); @@ -9894,7 +9903,10 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, int n; int matchregion = -1; bool hit = false; + uint32_t addr_page_base = address & TARGET_PAGE_MASK; + uint32_t addr_page_limit = addr_page_base + (TARGET_PAGE_SIZE - 1); + *is_subpage = false; *phys_ptr = address; *prot = 0; if (mregion) { @@ -9932,6 +9944,10 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, continue; } + if (base > addr_page_base || limit < addr_page_limit) { + *is_subpage = true; + } + if (hit) { /* Multiple regions match -- always a failure (unlike * PMSAv7 where highest-numbered-region wins) @@ -9943,23 +9959,6 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, matchregion = n; hit = true; - - if (base & ~TARGET_PAGE_MASK) { - qemu_log_mask(LOG_UNIMP, - "MPU_RBAR[%d]: No support for MPU region base" - "address of 0x%" PRIx32 ". Minimum alignment is " - "%d\n", - n, base, TARGET_PAGE_BITS); - continue; - } - if ((limit + 1) & ~TARGET_PAGE_MASK) { - qemu_log_mask(LOG_UNIMP, - "MPU_RBAR[%d]: No support for MPU region limit" - "address of 0x%" PRIx32 ". Minimum alignment is " - "%d\n", - n, limit, TARGET_PAGE_BITS); - continue; - } } } @@ -9995,6 +9994,18 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, fi->type = ARMFault_Permission; fi->level = 1; + /* + * Core QEMU code can't handle execution from small pages yet, so + * don't try it. This means any attempted execution will generate + * an MPU exception, rather than eventually causing QEMU to exit in + * get_page_addr_code(). + */ + if (*is_subpage && (*prot & PAGE_EXEC)) { + qemu_log_mask(LOG_UNIMP, + "MPU: No support for execution from regions " + "smaller than 1K\n"); + *prot &= ~PAGE_EXEC; + } return !(*prot & (1 << access_type)); } @@ -10002,10 +10013,13 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, MemTxAttrs *txattrs, - int *prot, ARMMMUFaultInfo *fi) + int *prot, target_ulong *page_size, + ARMMMUFaultInfo *fi) { uint32_t secure = regime_is_secure(env, mmu_idx); V8M_SAttributes sattrs = {}; + bool ret; + bool mpu_is_subpage; if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { v8m_security_lookup(env, address, access_type, mmu_idx, &sattrs); @@ -10033,6 +10047,7 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, } else { fi->type = ARMFault_QEMU_SFault; } + *page_size = sattrs.subpage ? 1 : TARGET_PAGE_SIZE; *phys_ptr = address; *prot = 0; return true; @@ -10055,6 +10070,7 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, * for M_FAKE_FSR_SFAULT in arm_v7m_cpu_do_interrupt(). */ fi->type = ARMFault_QEMU_SFault; + *page_size = sattrs.subpage ? 1 : TARGET_PAGE_SIZE; *phys_ptr = address; *prot = 0; return true; @@ -10062,8 +10078,22 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, } } - return pmsav8_mpu_lookup(env, address, access_type, mmu_idx, phys_ptr, - txattrs, prot, fi, NULL); + ret = pmsav8_mpu_lookup(env, address, access_type, mmu_idx, phys_ptr, + txattrs, prot, &mpu_is_subpage, fi, NULL); + /* + * TODO: this is a temporary hack to ignore the fact that the SAU region + * is smaller than a page if this is an executable region. We never + * supported small MPU regions, but we did (accidentally) allow small + * SAU regions, and if we now made small SAU regions not be executable + * then this would break previously working guest code. We can't + * remove this until/unless we implement support for execution from + * small regions. + */ + if (*prot & PAGE_EXEC) { + sattrs.subpage = false; + } + *page_size = sattrs.subpage || mpu_is_subpage ? 1 : TARGET_PAGE_SIZE; + return ret; } static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address, @@ -10339,7 +10369,7 @@ static bool get_phys_addr(CPUARMState *env, target_ulong address, if (arm_feature(env, ARM_FEATURE_V8)) { /* PMSAv8 */ ret = get_phys_addr_pmsav8(env, address, access_type, mmu_idx, - phys_ptr, attrs, prot, fi); + phys_ptr, attrs, prot, page_size, fi); } else if (arm_feature(env, ARM_FEATURE_V7)) { /* PMSAv7 */ ret = get_phys_addr_pmsav7(env, address, access_type, mmu_idx, @@ -10757,6 +10787,7 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) uint32_t mregion; bool targetpriv; bool targetsec = env->v7m.secure; + bool is_subpage; /* Work out what the security state and privilege level we're * interested in is... @@ -10786,7 +10817,8 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) if (arm_current_el(env) != 0 || alt) { /* We can ignore the return value as prot is always set */ pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, - &phys_addr, &attrs, &prot, &fi, &mregion); + &phys_addr, &attrs, &prot, &is_subpage, + &fi, &mregion); if (mregion == -1) { mrvalid = false; mregion = 0; From 9122bea9862edc0e665c796f79d99319b6638929 Mon Sep 17 00:00:00 2001 From: Jia He Date: Tue, 26 Jun 2018 17:50:42 +0100 Subject: [PATCH 1617/2380] hw/arm/smmuv3: Fix translate error handling In case the STE's config is "Bypass" we currently don't set the IOMMUTLBEntry perm flags and the access does not succeed. Also if the config is 0b0xx (Aborted/Reserved), decode_ste and smmuv3_decode_config currently returns -EINVAL and we don't enter the expected code path: we record an event whereas we should not. This patch fixes those bugs and simplifies the error handling. decode_ste and smmuv3_decode_config now return 0 if aborted or bypassed config was found. Only bad config info produces negative error values. In smmuv3_translate we more clearly differentiate errors, bypass/smmu disabled, aborted and success cases. Also trace points are differentiated. Fixes: 9bde7f0674fe ("hw/arm/smmuv3: Implement translate callback") Reported-by: jia.he@hxt-semitech.com Signed-off-by: jia.he@hxt-semitech.com Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Message-id: 1529653501-15358-2-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/smmuv3-internal.h | 12 ++++- hw/arm/smmuv3.c | 96 +++++++++++++++++++++++++++------------- hw/arm/trace-events | 7 +-- 3 files changed, 80 insertions(+), 35 deletions(-) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index a9d714b56e..bab25d640e 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -23,6 +23,14 @@ #include "hw/arm/smmu-common.h" +typedef enum SMMUTranslationStatus { + SMMU_TRANS_DISABLE, + SMMU_TRANS_ABORT, + SMMU_TRANS_BYPASS, + SMMU_TRANS_ERROR, + SMMU_TRANS_SUCCESS, +} SMMUTranslationStatus; + /* MMIO Registers */ REG32(IDR0, 0x0) @@ -315,7 +323,7 @@ enum { /* Command completion notification */ /* Events */ typedef enum SMMUEventType { - SMMU_EVT_OK = 0x00, + SMMU_EVT_NONE = 0x00, SMMU_EVT_F_UUT , SMMU_EVT_C_BAD_STREAMID , SMMU_EVT_F_STE_FETCH , @@ -337,7 +345,7 @@ typedef enum SMMUEventType { } SMMUEventType; static const char *event_stringify[] = { - [SMMU_EVT_OK] = "SMMU_EVT_OK", + [SMMU_EVT_NONE] = "no recorded event", [SMMU_EVT_F_UUT] = "SMMU_EVT_F_UUT", [SMMU_EVT_C_BAD_STREAMID] = "SMMU_EVT_C_BAD_STREAMID", [SMMU_EVT_F_STE_FETCH] = "SMMU_EVT_F_STE_FETCH", diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 978330900d..70b8f295aa 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -23,6 +23,7 @@ #include "hw/qdev-core.h" #include "hw/pci/pci.h" #include "exec/address-spaces.h" +#include "cpu.h" #include "trace.h" #include "qemu/log.h" #include "qemu/error-report.h" @@ -154,7 +155,7 @@ void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info) EVT_SET_SID(&evt, info->sid); switch (info->type) { - case SMMU_EVT_OK: + case SMMU_EVT_NONE: return; case SMMU_EVT_F_UUT: EVT_SET_SSID(&evt, info->u.f_uut.ssid); @@ -312,12 +313,11 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid, return 0; } -/* Returns <0 if the caller has no need to continue the translation */ +/* Returns < 0 in case of invalid STE, 0 otherwise */ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, STE *ste, SMMUEventInfo *event) { uint32_t config; - int ret = -EINVAL; if (!STE_VALID(ste)) { goto bad_ste; @@ -326,13 +326,13 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, config = STE_CONFIG(ste); if (STE_CFG_ABORT(config)) { - cfg->aborted = true; /* abort but don't record any event */ - return ret; + cfg->aborted = true; + return 0; } if (STE_CFG_BYPASS(config)) { cfg->bypassed = true; - return ret; + return 0; } if (STE_CFG_S2_ENABLED(config)) { @@ -509,7 +509,7 @@ bad_cd: * the different configuration decoding steps * @event: must be zero'ed by the caller * - * return < 0 if the translation needs to be aborted (@event is filled + * return < 0 in case of config decoding error (@event is filled * accordingly). Return 0 otherwise. */ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, @@ -518,19 +518,26 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); uint32_t sid = smmu_get_sid(sdev); SMMUv3State *s = sdev->smmu; - int ret = -EINVAL; + int ret; STE ste; CD cd; - if (smmu_find_ste(s, sid, &ste, event)) { + ret = smmu_find_ste(s, sid, &ste, event); + if (ret) { return ret; } - if (decode_ste(s, cfg, &ste, event)) { + ret = decode_ste(s, cfg, &ste, event); + if (ret) { return ret; } - if (smmu_get_cd(s, &ste, 0 /* ssid */, &cd, event)) { + if (cfg->aborted || cfg->bypassed) { + return 0; + } + + ret = smmu_get_cd(s, &ste, 0 /* ssid */, &cd, event); + if (ret) { return ret; } @@ -543,8 +550,9 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); SMMUv3State *s = sdev->smmu; uint32_t sid = smmu_get_sid(sdev); - SMMUEventInfo event = {.type = SMMU_EVT_OK, .sid = sid}; + SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid}; SMMUPTWEventInfo ptw_info = {}; + SMMUTranslationStatus status; SMMUTransCfg cfg = {}; IOMMUTLBEntry entry = { .target_as = &address_space_memory, @@ -553,23 +561,28 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, .addr_mask = ~(hwaddr)0, .perm = IOMMU_NONE, }; - int ret = 0; if (!smmu_enabled(s)) { - goto out; + status = SMMU_TRANS_DISABLE; + goto epilogue; } - ret = smmuv3_decode_config(mr, &cfg, &event); - if (ret) { - goto out; + if (smmuv3_decode_config(mr, &cfg, &event)) { + status = SMMU_TRANS_ERROR; + goto epilogue; } if (cfg.aborted) { - goto out; + status = SMMU_TRANS_ABORT; + goto epilogue; } - ret = smmu_ptw(&cfg, addr, flag, &entry, &ptw_info); - if (ret) { + if (cfg.bypassed) { + status = SMMU_TRANS_BYPASS; + goto epilogue; + } + + if (smmu_ptw(&cfg, addr, flag, &entry, &ptw_info)) { switch (ptw_info.type) { case SMMU_PTW_ERR_WALK_EABT: event.type = SMMU_EVT_F_WALK_EABT; @@ -609,18 +622,41 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, default: g_assert_not_reached(); } + status = SMMU_TRANS_ERROR; + } else { + status = SMMU_TRANS_SUCCESS; } -out: - if (ret) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s translation failed for iova=0x%"PRIx64"(%d)\n", - mr->parent_obj.name, addr, ret); - entry.perm = IOMMU_NONE; - smmuv3_record_event(s, &event); - } else if (!cfg.aborted) { + +epilogue: + switch (status) { + case SMMU_TRANS_SUCCESS: entry.perm = flag; - trace_smmuv3_translate(mr->parent_obj.name, sid, addr, - entry.translated_addr, entry.perm); + trace_smmuv3_translate_success(mr->parent_obj.name, sid, addr, + entry.translated_addr, entry.perm); + break; + case SMMU_TRANS_DISABLE: + entry.perm = flag; + entry.addr_mask = ~TARGET_PAGE_MASK; + trace_smmuv3_translate_disable(mr->parent_obj.name, sid, addr, + entry.perm); + break; + case SMMU_TRANS_BYPASS: + entry.perm = flag; + entry.addr_mask = ~TARGET_PAGE_MASK; + trace_smmuv3_translate_bypass(mr->parent_obj.name, sid, addr, + entry.perm); + break; + case SMMU_TRANS_ABORT: + /* no event is recorded on abort */ + trace_smmuv3_translate_abort(mr->parent_obj.name, sid, addr, + entry.perm); + break; + case SMMU_TRANS_ERROR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s translation failed for iova=0x%"PRIx64"(%s)\n", + mr->parent_obj.name, addr, smmu_event_string(event.type)); + smmuv3_record_event(s, &event); + break; } return entry; diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 2d92727602..0ab66bb4a8 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -33,9 +33,10 @@ smmuv3_record_event(const char *type, uint32_t sid) "%s sid=%d" smmuv3_find_ste(uint16_t sid, uint32_t features, uint16_t sid_split) "SID:0x%x features:0x%x, sid_split:0x%x" smmuv3_find_ste_2lvl(uint64_t strtab_base, uint64_t l1ptr, int l1_ste_offset, uint64_t l2ptr, int l2_ste_offset, int max_l2_ste) "strtab_base:0x%"PRIx64" l1ptr:0x%"PRIx64" l1_off:0x%x, l2ptr:0x%"PRIx64" l2_off:0x%x max_l2_ste:%d" smmuv3_get_ste(uint64_t addr) "STE addr: 0x%"PRIx64 -smmuv3_translate_bypass(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d bypass iova:0x%"PRIx64" is_write=%d" -smmuv3_translate_in(uint16_t sid, int pci_bus_num, uint64_t strtab_base) "SID:0x%x bus:%d strtab_base:0x%"PRIx64 +smmuv3_translate_disable(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d bypass (smmu disabled) iova:0x%"PRIx64" is_write=%d" +smmuv3_translate_bypass(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d STE bypass iova:0x%"PRIx64" is_write=%d" +smmuv3_translate_abort(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d abort on iova:0x%"PRIx64" is_write=%d" +smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm) "%s sid=%d iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x" smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64 -smmuv3_translate(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm) "%s sid=%d iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x" smmuv3_decode_cd(uint32_t oas) "oas=%d" smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d" From 32cfd7f39e0811036efd3a7a12d0f975ef57fdb3 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 26 Jun 2018 17:50:42 +0100 Subject: [PATCH 1618/2380] hw/arm/smmuv3: Cache/invalidate config data Let's cache config data to avoid fetching and parsing STE/CD structures on each translation. We invalidate them on data structure invalidation commands. We put in place a per-smmu mutex to protect the config cache. This will be useful too to protect the IOTLB cache. The caches can be accessed without BQL, ie. in IO dataplane. The same kind of mutex was put in place in the intel viommu. Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Message-id: 1529653501-15358-3-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 24 ++++++- hw/arm/smmuv3.c | 135 +++++++++++++++++++++++++++++++++-- hw/arm/trace-events | 6 ++ include/hw/arm/smmu-common.h | 5 ++ include/hw/arm/smmuv3.h | 1 + 5 files changed, 164 insertions(+), 7 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 3c5f7245b5..db242c73df 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -310,6 +310,24 @@ static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) return &sdev->as; } +IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) +{ + uint8_t bus_n, devfn; + SMMUPciBus *smmu_bus; + SMMUDevice *smmu; + + bus_n = PCI_BUS_NUM(sid); + smmu_bus = smmu_find_smmu_pcibus(s, bus_n); + if (smmu_bus) { + devfn = sid & 0x7; + smmu = smmu_bus->pbdev[devfn]; + if (smmu) { + return &smmu->iommu; + } + } + return NULL; +} + static void smmu_base_realize(DeviceState *dev, Error **errp) { SMMUState *s = ARM_SMMU(dev); @@ -321,7 +339,7 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } - + s->configs = g_hash_table_new_full(NULL, NULL, NULL, g_free); s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL); if (s->primary_bus) { @@ -333,7 +351,9 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) static void smmu_base_reset(DeviceState *dev) { - /* will be filled later on */ + SMMUState *s = ARM_SMMU(dev); + + g_hash_table_remove_all(s->configs); } static Property smmu_dev_properties[] = { diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 70b8f295aa..df704c26f8 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -544,6 +544,58 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, return decode_cd(cfg, &cd, event); } +/** + * smmuv3_get_config - Look up for a cached copy of configuration data for + * @sdev and on cache miss performs a configuration structure decoding from + * guest RAM. + * + * @sdev: SMMUDevice handle + * @event: output event info + * + * The configuration cache contains data resulting from both STE and CD + * decoding under the form of an SMMUTransCfg struct. The hash table is indexed + * by the SMMUDevice handle. + */ +static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event) +{ + SMMUv3State *s = sdev->smmu; + SMMUState *bc = &s->smmu_state; + SMMUTransCfg *cfg; + + cfg = g_hash_table_lookup(bc->configs, sdev); + if (cfg) { + sdev->cfg_cache_hits++; + trace_smmuv3_config_cache_hit(smmu_get_sid(sdev), + sdev->cfg_cache_hits, sdev->cfg_cache_misses, + 100 * sdev->cfg_cache_hits / + (sdev->cfg_cache_hits + sdev->cfg_cache_misses)); + } else { + sdev->cfg_cache_misses++; + trace_smmuv3_config_cache_miss(smmu_get_sid(sdev), + sdev->cfg_cache_hits, sdev->cfg_cache_misses, + 100 * sdev->cfg_cache_hits / + (sdev->cfg_cache_hits + sdev->cfg_cache_misses)); + cfg = g_new0(SMMUTransCfg, 1); + + if (!smmuv3_decode_config(&sdev->iommu, cfg, event)) { + g_hash_table_insert(bc->configs, sdev, cfg); + } else { + g_free(cfg); + cfg = NULL; + } + } + return cfg; +} + +static void smmuv3_flush_config(SMMUDevice *sdev) +{ + SMMUv3State *s = sdev->smmu; + SMMUState *bc = &s->smmu_state; + + trace_smmuv3_config_cache_inv(smmu_get_sid(sdev)); + g_hash_table_remove(bc->configs, sdev); +} + static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, IOMMUAccessFlags flag, int iommu_idx) { @@ -553,7 +605,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid}; SMMUPTWEventInfo ptw_info = {}; SMMUTranslationStatus status; - SMMUTransCfg cfg = {}; + SMMUTransCfg *cfg = NULL; IOMMUTLBEntry entry = { .target_as = &address_space_memory, .iova = addr, @@ -562,27 +614,30 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, .perm = IOMMU_NONE, }; + qemu_mutex_lock(&s->mutex); + if (!smmu_enabled(s)) { status = SMMU_TRANS_DISABLE; goto epilogue; } - if (smmuv3_decode_config(mr, &cfg, &event)) { + cfg = smmuv3_get_config(sdev, &event); + if (!cfg) { status = SMMU_TRANS_ERROR; goto epilogue; } - if (cfg.aborted) { + if (cfg->aborted) { status = SMMU_TRANS_ABORT; goto epilogue; } - if (cfg.bypassed) { + if (cfg->bypassed) { status = SMMU_TRANS_BYPASS; goto epilogue; } - if (smmu_ptw(&cfg, addr, flag, &entry, &ptw_info)) { + if (smmu_ptw(cfg, addr, flag, &entry, &ptw_info)) { switch (ptw_info.type) { case SMMU_PTW_ERR_WALK_EABT: event.type = SMMU_EVT_F_WALK_EABT; @@ -628,6 +683,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, } epilogue: + qemu_mutex_unlock(&s->mutex); switch (status) { case SMMU_TRANS_SUCCESS: entry.perm = flag; @@ -664,6 +720,7 @@ epilogue: static int smmuv3_cmdq_consume(SMMUv3State *s) { + SMMUState *bs = ARM_SMMU(s); SMMUCmdError cmd_error = SMMU_CERROR_NONE; SMMUQueue *q = &s->cmdq; SMMUCommandType type = 0; @@ -698,6 +755,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) trace_smmuv3_cmdq_opcode(smmu_cmd_string(type)); + qemu_mutex_lock(&s->mutex); switch (type) { case SMMU_CMD_SYNC: if (CMD_SYNC_CS(&cmd) & CMD_SYNC_SIG_IRQ) { @@ -706,10 +764,74 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) break; case SMMU_CMD_PREFETCH_CONFIG: case SMMU_CMD_PREFETCH_ADDR: + break; case SMMU_CMD_CFGI_STE: + { + uint32_t sid = CMD_SID(&cmd); + IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); + SMMUDevice *sdev; + + if (CMD_SSEC(&cmd)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + + if (!mr) { + break; + } + + trace_smmuv3_cmdq_cfgi_ste(sid); + sdev = container_of(mr, SMMUDevice, iommu); + smmuv3_flush_config(sdev); + + break; + } case SMMU_CMD_CFGI_STE_RANGE: /* same as SMMU_CMD_CFGI_ALL */ + { + uint32_t start = CMD_SID(&cmd), end, i; + uint8_t range = CMD_STE_RANGE(&cmd); + + if (CMD_SSEC(&cmd)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + + end = start + (1 << (range + 1)) - 1; + trace_smmuv3_cmdq_cfgi_ste_range(start, end); + + for (i = start; i <= end; i++) { + IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, i); + SMMUDevice *sdev; + + if (!mr) { + continue; + } + sdev = container_of(mr, SMMUDevice, iommu); + smmuv3_flush_config(sdev); + } + break; + } case SMMU_CMD_CFGI_CD: case SMMU_CMD_CFGI_CD_ALL: + { + uint32_t sid = CMD_SID(&cmd); + IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); + SMMUDevice *sdev; + + if (CMD_SSEC(&cmd)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + + if (!mr) { + break; + } + + trace_smmuv3_cmdq_cfgi_cd(sid); + sdev = container_of(mr, SMMUDevice, iommu); + smmuv3_flush_config(sdev); + break; + } case SMMU_CMD_TLBI_NH_ALL: case SMMU_CMD_TLBI_NH_ASID: case SMMU_CMD_TLBI_NH_VA: @@ -735,6 +857,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) "Illegal command type: %d\n", CMD_TYPE(&cmd)); break; } + qemu_mutex_unlock(&s->mutex); if (cmd_error) { break; } @@ -1114,6 +1237,8 @@ static void smmu_realize(DeviceState *d, Error **errp) return; } + qemu_mutex_init(&s->mutex); + memory_region_init_io(&sys->iomem, OBJECT(s), &smmu_mem_ops, sys, TYPE_ARM_SMMUV3, 0x20000); diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 0ab66bb4a8..8ee4c21dd7 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -40,3 +40,9 @@ smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t tr smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64 smmuv3_decode_cd(uint32_t oas) "oas=%d" smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d" +smmuv3_cmdq_cfgi_ste(int streamid) "streamid =%d" +smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%d - end=0x%d" +smmuv3_cmdq_cfgi_cd(uint32_t sid) "streamid = %d" +smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid %d (hits=%d, misses=%d, hit rate=%d)" +smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid %d (hits=%d, misses=%d, hit rate=%d)" +smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d" diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index c41eb5c3b0..7ce95ca0dc 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -75,6 +75,8 @@ typedef struct SMMUDevice { int devfn; IOMMUMemoryRegion iommu; AddressSpace as; + uint32_t cfg_cache_hits; + uint32_t cfg_cache_misses; } SMMUDevice; typedef struct SMMUNotifierNode { @@ -142,4 +144,7 @@ int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, */ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova); +/* Return the iommu mr associated to @sid, or NULL if none */ +IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid); + #endif /* HW_ARM_SMMU_COMMON */ diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h index 23f70363e5..36b2f45253 100644 --- a/include/hw/arm/smmuv3.h +++ b/include/hw/arm/smmuv3.h @@ -59,6 +59,7 @@ typedef struct SMMUv3State { SMMUQueue eventq, cmdq; qemu_irq irq[4]; + QemuMutex mutex; } SMMUv3State; typedef enum { From cc27ed81cf11d5b7ffc7eca9f31dfcd82c983c56 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 26 Jun 2018 17:50:42 +0100 Subject: [PATCH 1619/2380] hw/arm/smmuv3: IOTLB emulation We emulate a TLB cache of size SMMU_IOTLB_MAX_SIZE=256. It is implemented as a hash table whose key is a combination of the 16b asid and 48b IOVA (Jenkins hash). Entries are invalidated on TLB invalidation commands, either globally, or per asid, or per asid/iova. Signed-off-by: Eric Auger Message-id: 1529653501-15358-4-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 60 ++++++++++++++++++++++ hw/arm/smmuv3.c | 98 ++++++++++++++++++++++++++++++++++-- hw/arm/trace-events | 9 ++++ include/hw/arm/smmu-common.h | 13 +++++ 4 files changed, 176 insertions(+), 4 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index db242c73df..f66e444f6f 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -24,11 +24,43 @@ #include "qom/cpu.h" #include "hw/qdev-properties.h" #include "qapi/error.h" +#include "qemu/jhash.h" #include "qemu/error-report.h" #include "hw/arm/smmu-common.h" #include "smmu-internal.h" +/* IOTLB Management */ + +inline void smmu_iotlb_inv_all(SMMUState *s) +{ + trace_smmu_iotlb_inv_all(); + g_hash_table_remove_all(s->iotlb); +} + +static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value, + gpointer user_data) +{ + uint16_t asid = *(uint16_t *)user_data; + SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; + + return iotlb_key->asid == asid; +} + +inline void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova) +{ + SMMUIOTLBKey key = {.asid = asid, .iova = iova}; + + trace_smmu_iotlb_inv_iova(asid, iova); + g_hash_table_remove(s->iotlb, &key); +} + +inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid) +{ + trace_smmu_iotlb_inv_asid(asid); + g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid, &asid); +} + /* VMSAv8-64 Translation */ /** @@ -328,6 +360,31 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) return NULL; } +static guint smmu_iotlb_key_hash(gconstpointer v) +{ + SMMUIOTLBKey *key = (SMMUIOTLBKey *)v; + uint32_t a, b, c; + + /* Jenkins hash */ + a = b = c = JHASH_INITVAL + sizeof(*key); + a += key->asid; + b += extract64(key->iova, 0, 32); + c += extract64(key->iova, 32, 32); + + __jhash_mix(a, b, c); + __jhash_final(a, b, c); + + return c; +} + +static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2) +{ + const SMMUIOTLBKey *k1 = v1; + const SMMUIOTLBKey *k2 = v2; + + return (k1->asid == k2->asid) && (k1->iova == k2->iova); +} + static void smmu_base_realize(DeviceState *dev, Error **errp) { SMMUState *s = ARM_SMMU(dev); @@ -340,6 +397,8 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) return; } s->configs = g_hash_table_new_full(NULL, NULL, NULL, g_free); + s->iotlb = g_hash_table_new_full(smmu_iotlb_key_hash, smmu_iotlb_key_equal, + g_free, g_free); s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL); if (s->primary_bus) { @@ -354,6 +413,7 @@ static void smmu_base_reset(DeviceState *dev) SMMUState *s = ARM_SMMU(dev); g_hash_table_remove_all(s->configs); + g_hash_table_remove_all(s->iotlb); } static Property smmu_dev_properties[] = { diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index df704c26f8..b6dc7ed6de 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -605,6 +605,10 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid}; SMMUPTWEventInfo ptw_info = {}; SMMUTranslationStatus status; + SMMUState *bs = ARM_SMMU(s); + uint64_t page_mask, aligned_addr; + IOMMUTLBEntry *cached_entry = NULL; + SMMUTransTableInfo *tt; SMMUTransCfg *cfg = NULL; IOMMUTLBEntry entry = { .target_as = &address_space_memory, @@ -613,6 +617,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, .addr_mask = ~(hwaddr)0, .perm = IOMMU_NONE, }; + SMMUIOTLBKey key, *new_key; qemu_mutex_lock(&s->mutex); @@ -637,7 +642,57 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, goto epilogue; } - if (smmu_ptw(cfg, addr, flag, &entry, &ptw_info)) { + tt = select_tt(cfg, addr); + if (!tt) { + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_TRANSLATION; + event.u.f_translation.addr = addr; + event.u.f_translation.rnw = flag & 0x1; + } + status = SMMU_TRANS_ERROR; + goto epilogue; + } + + page_mask = (1ULL << (tt->granule_sz)) - 1; + aligned_addr = addr & ~page_mask; + + key.asid = cfg->asid; + key.iova = aligned_addr; + + cached_entry = g_hash_table_lookup(bs->iotlb, &key); + if (cached_entry) { + cfg->iotlb_hits++; + trace_smmu_iotlb_cache_hit(cfg->asid, aligned_addr, + cfg->iotlb_hits, cfg->iotlb_misses, + 100 * cfg->iotlb_hits / + (cfg->iotlb_hits + cfg->iotlb_misses)); + if ((flag & IOMMU_WO) && !(cached_entry->perm & IOMMU_WO)) { + status = SMMU_TRANS_ERROR; + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_PERMISSION; + event.u.f_permission.addr = addr; + event.u.f_permission.rnw = flag & 0x1; + } + } else { + status = SMMU_TRANS_SUCCESS; + } + goto epilogue; + } + + cfg->iotlb_misses++; + trace_smmu_iotlb_cache_miss(cfg->asid, addr & ~page_mask, + cfg->iotlb_hits, cfg->iotlb_misses, + 100 * cfg->iotlb_hits / + (cfg->iotlb_hits + cfg->iotlb_misses)); + + if (g_hash_table_size(bs->iotlb) >= SMMU_IOTLB_MAX_SIZE) { + smmu_iotlb_inv_all(bs); + } + + cached_entry = g_new0(IOMMUTLBEntry, 1); + + if (smmu_ptw(cfg, aligned_addr, flag, cached_entry, &ptw_info)) { + g_free(cached_entry); switch (ptw_info.type) { case SMMU_PTW_ERR_WALK_EABT: event.type = SMMU_EVT_F_WALK_EABT; @@ -679,6 +734,10 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, } status = SMMU_TRANS_ERROR; } else { + new_key = g_new0(SMMUIOTLBKey, 1); + new_key->asid = cfg->asid; + new_key->iova = aligned_addr; + g_hash_table_insert(bs->iotlb, new_key, cached_entry); status = SMMU_TRANS_SUCCESS; } @@ -687,6 +746,9 @@ epilogue: switch (status) { case SMMU_TRANS_SUCCESS: entry.perm = flag; + entry.translated_addr = cached_entry->translated_addr + + (addr & page_mask); + entry.addr_mask = cached_entry->addr_mask; trace_smmuv3_translate_success(mr->parent_obj.name, sid, addr, entry.translated_addr, entry.perm); break; @@ -832,10 +894,39 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) smmuv3_flush_config(sdev); break; } - case SMMU_CMD_TLBI_NH_ALL: case SMMU_CMD_TLBI_NH_ASID: - case SMMU_CMD_TLBI_NH_VA: + { + uint16_t asid = CMD_ASID(&cmd); + + trace_smmuv3_cmdq_tlbi_nh_asid(asid); + smmu_iotlb_inv_asid(bs, asid); + break; + } + case SMMU_CMD_TLBI_NH_ALL: + case SMMU_CMD_TLBI_NSNH_ALL: + trace_smmuv3_cmdq_tlbi_nh(); + smmu_iotlb_inv_all(bs); + break; case SMMU_CMD_TLBI_NH_VAA: + { + dma_addr_t addr = CMD_ADDR(&cmd); + uint16_t vmid = CMD_VMID(&cmd); + + trace_smmuv3_cmdq_tlbi_nh_vaa(vmid, addr); + smmu_iotlb_inv_all(bs); + break; + } + case SMMU_CMD_TLBI_NH_VA: + { + uint16_t asid = CMD_ASID(&cmd); + uint16_t vmid = CMD_VMID(&cmd); + dma_addr_t addr = CMD_ADDR(&cmd); + bool leaf = CMD_LEAF(&cmd); + + trace_smmuv3_cmdq_tlbi_nh_va(vmid, asid, addr, leaf); + smmu_iotlb_inv_iova(bs, asid, addr); + break; + } case SMMU_CMD_TLBI_EL3_ALL: case SMMU_CMD_TLBI_EL3_VA: case SMMU_CMD_TLBI_EL2_ALL: @@ -844,7 +935,6 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) case SMMU_CMD_TLBI_EL2_VAA: case SMMU_CMD_TLBI_S12_VMALL: case SMMU_CMD_TLBI_S2_IPA: - case SMMU_CMD_TLBI_NSNH_ALL: case SMMU_CMD_ATC_INV: case SMMU_CMD_PRI_RESP: case SMMU_CMD_RESUME: diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 8ee4c21dd7..be69c5ddfe 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -12,6 +12,11 @@ smmu_ptw_invalid_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t address) "stage=%d level=%d iova=0x%"PRIx64" base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" page address = 0x%"PRIx64 smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB" smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64 +smmu_iotlb_cache_hit(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" +smmu_iotlb_cache_miss(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" +smmu_iotlb_inv_all(void) "IOTLB invalidate all" +smmu_iotlb_inv_asid(uint16_t asid) "IOTLB invalidate asid=%d" +smmu_iotlb_inv_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 #hw/arm/smmuv3.c smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" @@ -45,4 +50,8 @@ smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%d - end=0x%d" smmuv3_cmdq_cfgi_cd(uint32_t sid) "streamid = %d" smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid %d (hits=%d, misses=%d, hit rate=%d)" smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid %d (hits=%d, misses=%d, hit rate=%d)" +smmuv3_cmdq_tlbi_nh_va(int vmid, int asid, uint64_t addr, bool leaf) "vmid =%d asid =%d addr=0x%"PRIx64" leaf=%d" +smmuv3_cmdq_tlbi_nh_vaa(int vmid, uint64_t addr) "vmid =%d addr=0x%"PRIx64 +smmuv3_cmdq_tlbi_nh(void) "" +smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d" diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 7ce95ca0dc..d173806f1a 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -67,6 +67,8 @@ typedef struct SMMUTransCfg { uint8_t tbi; /* Top Byte Ignore */ uint16_t asid; SMMUTransTableInfo tt[2]; + uint32_t iotlb_hits; /* counts IOTLB hits for this asid */ + uint32_t iotlb_misses; /* counts IOTLB misses for this asid */ } SMMUTransCfg; typedef struct SMMUDevice { @@ -89,6 +91,11 @@ typedef struct SMMUPciBus { SMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically alloc */ } SMMUPciBus; +typedef struct SMMUIOTLBKey { + uint64_t iova; + uint16_t asid; +} SMMUIOTLBKey; + typedef struct SMMUState { /* */ SysBusDevice dev; @@ -147,4 +154,10 @@ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova); /* Return the iommu mr associated to @sid, or NULL if none */ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid); +#define SMMU_IOTLB_MAX_SIZE 256 + +void smmu_iotlb_inv_all(SMMUState *s); +void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid); +void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova); + #endif /* HW_ARM_SMMU_COMMON */ From 832e4222c82071e4399cffdecd605abed5ac0c27 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 26 Jun 2018 17:50:42 +0100 Subject: [PATCH 1620/2380] hw/arm/smmuv3: Add notifications on invalidation On TLB invalidation commands, let's call registered IOMMU notifiers. Those can only be UNMAP notifiers. SMMUv3 does not support notification on MAP (VFIO). This patch allows vhost use case where IOTLB API is notified on each guest IOTLB invalidation. Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Message-id: 1529653501-15358-5-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 34 +++++++++++++ hw/arm/smmuv3.c | 99 +++++++++++++++++++++++++++++++++++- hw/arm/trace-events | 5 ++ include/hw/arm/smmu-common.h | 6 +++ 4 files changed, 142 insertions(+), 2 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index f66e444f6f..3098915d07 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -385,6 +385,40 @@ static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2) return (k1->asid == k2->asid) && (k1->iova == k2->iova); } +/* Unmap the whole notifier's range */ +static void smmu_unmap_notifier_range(IOMMUNotifier *n) +{ + IOMMUTLBEntry entry; + + entry.target_as = &address_space_memory; + entry.iova = n->start; + entry.perm = IOMMU_NONE; + entry.addr_mask = n->end - n->start; + + memory_region_notify_one(n, &entry); +} + +/* Unmap all notifiers attached to @mr */ +inline void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr) +{ + IOMMUNotifier *n; + + trace_smmu_inv_notifiers_mr(mr->parent_obj.name); + IOMMU_NOTIFIER_FOREACH(n, mr) { + smmu_unmap_notifier_range(n); + } +} + +/* Unmap all notifiers of all mr's */ +void smmu_inv_notifiers_all(SMMUState *s) +{ + SMMUNotifierNode *node; + + QLIST_FOREACH(node, &s->notifiers_list, next) { + smmu_inv_notifiers_mr(&node->sdev->iommu); + } +} + static void smmu_base_realize(DeviceState *dev, Error **errp) { SMMUState *s = ARM_SMMU(dev); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index b6dc7ed6de..39fbcbf577 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -780,6 +780,68 @@ epilogue: return entry; } +/** + * smmuv3_notify_iova - call the notifier @n for a given + * @asid and @iova tuple. + * + * @mr: IOMMU mr region handle + * @n: notifier to be called + * @asid: address space ID or negative value if we don't care + * @iova: iova + */ +static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, + IOMMUNotifier *n, + int asid, + dma_addr_t iova) +{ + SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); + SMMUEventInfo event = {}; + SMMUTransTableInfo *tt; + SMMUTransCfg *cfg; + IOMMUTLBEntry entry; + + cfg = smmuv3_get_config(sdev, &event); + if (!cfg) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s error decoding the configuration for iommu mr=%s\n", + __func__, mr->parent_obj.name); + return; + } + + if (asid >= 0 && cfg->asid != asid) { + return; + } + + tt = select_tt(cfg, iova); + if (!tt) { + return; + } + + entry.target_as = &address_space_memory; + entry.iova = iova; + entry.addr_mask = (1 << tt->granule_sz) - 1; + entry.perm = IOMMU_NONE; + + memory_region_notify_one(n, &entry); +} + +/* invalidate an asid/iova tuple in all mr's */ +static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova) +{ + SMMUNotifierNode *node; + + QLIST_FOREACH(node, &s->notifiers_list, next) { + IOMMUMemoryRegion *mr = &node->sdev->iommu; + IOMMUNotifier *n; + + trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, iova); + + IOMMU_NOTIFIER_FOREACH(n, mr) { + smmuv3_notify_iova(mr, n, asid, iova); + } + } +} + static int smmuv3_cmdq_consume(SMMUv3State *s) { SMMUState *bs = ARM_SMMU(s); @@ -899,12 +961,14 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) uint16_t asid = CMD_ASID(&cmd); trace_smmuv3_cmdq_tlbi_nh_asid(asid); + smmu_inv_notifiers_all(&s->smmu_state); smmu_iotlb_inv_asid(bs, asid); break; } case SMMU_CMD_TLBI_NH_ALL: case SMMU_CMD_TLBI_NSNH_ALL: trace_smmuv3_cmdq_tlbi_nh(); + smmu_inv_notifiers_all(&s->smmu_state); smmu_iotlb_inv_all(bs); break; case SMMU_CMD_TLBI_NH_VAA: @@ -913,6 +977,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) uint16_t vmid = CMD_VMID(&cmd); trace_smmuv3_cmdq_tlbi_nh_vaa(vmid, addr); + smmuv3_inv_notifiers_iova(bs, -1, addr); smmu_iotlb_inv_all(bs); break; } @@ -924,6 +989,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) bool leaf = CMD_LEAF(&cmd); trace_smmuv3_cmdq_tlbi_nh_va(vmid, asid, addr, leaf); + smmuv3_inv_notifiers_iova(bs, asid, addr); smmu_iotlb_inv_iova(bs, asid, addr); break; } @@ -1402,9 +1468,38 @@ static void smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { + SMMUDevice *sdev = container_of(iommu, SMMUDevice, iommu); + SMMUv3State *s3 = sdev->smmu; + SMMUState *s = &(s3->smmu_state); + SMMUNotifierNode *node = NULL; + SMMUNotifierNode *next_node = NULL; + + if (new & IOMMU_NOTIFIER_MAP) { + int bus_num = pci_bus_num(sdev->bus); + PCIDevice *pcidev = pci_find_device(sdev->bus, bus_num, sdev->devfn); + + warn_report("SMMUv3 does not support notification on MAP: " + "device %s will not function properly", pcidev->name); + } + if (old == IOMMU_NOTIFIER_NONE) { - warn_report("SMMUV3 does not support vhost/vfio integration yet: " - "devices of those types will not function properly"); + trace_smmuv3_notify_flag_add(iommu->parent_obj.name); + node = g_malloc0(sizeof(*node)); + node->sdev = sdev; + QLIST_INSERT_HEAD(&s->notifiers_list, node, next); + return; + } + + /* update notifier node with new flags */ + QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) { + if (node->sdev == sdev) { + if (new == IOMMU_NOTIFIER_NONE) { + trace_smmuv3_notify_flag_del(iommu->parent_obj.name); + QLIST_REMOVE(node, next); + g_free(node); + } + return; + } } } diff --git a/hw/arm/trace-events b/hw/arm/trace-events index be69c5ddfe..27b11d655d 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -17,6 +17,7 @@ smmu_iotlb_cache_miss(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, smmu_iotlb_inv_all(void) "IOTLB invalidate all" smmu_iotlb_inv_asid(uint16_t asid) "IOTLB invalidate asid=%d" smmu_iotlb_inv_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 +smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" #hw/arm/smmuv3.c smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" @@ -55,3 +56,7 @@ smmuv3_cmdq_tlbi_nh_vaa(int vmid, uint64_t addr) "vmid =%d addr=0x%"PRIx64 smmuv3_cmdq_tlbi_nh(void) "" smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d" +smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s" +smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s" +smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint64_t iova) "iommu mr=%s asid=%d iova=0x%"PRIx64 + diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index d173806f1a..50e2912a95 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -160,4 +160,10 @@ void smmu_iotlb_inv_all(SMMUState *s); void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid); void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova); +/* Unmap the range of all the notifiers registered to any IOMMU mr */ +void smmu_inv_notifiers_all(SMMUState *s); + +/* Unmap the range of all the notifiers registered to @mr */ +void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr); + #endif /* HW_ARM_SMMU_COMMON */ From fda9aaa60ec27dfdbc1b70605e5439a6d1b30c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 26 Jun 2018 17:50:42 +0100 Subject: [PATCH 1621/2380] aspeed/scu: introduce clock frequencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All Aspeed SoC clocks are driven by an input source clock which can have different frequencies : 24MHz or 25MHz, and also, on the Aspeed AST2400 SoC, 48MHz. The H-PLL (CPU) clock is defined from a calculation using parameters in the H-PLL Parameter register or from a predefined set of frequencies if the setting is strapped by hardware (Aspeed AST2400 SoC). The other clocks of the SoC are then defined from the H-PLL using dividers. We introduce first the APB clock because it should be used to drive the Aspeed timer model. Signed-off-by: Cédric Le Goater Reviewed-by: Andrew Jeffery Message-id: 20180622075700.5923-2-clg@kaod.org Signed-off-by: Peter Maydell --- hw/misc/aspeed_scu.c | 106 +++++++++++++++++++++++++++++++++++ include/hw/misc/aspeed_scu.h | 70 +++++++++++++++++++++-- 2 files changed, 172 insertions(+), 4 deletions(-) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 59315010db..59333b50ab 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -168,6 +168,27 @@ static uint32_t aspeed_scu_get_random(void) return num; } +static void aspeed_scu_set_apb_freq(AspeedSCUState *s) +{ + uint32_t apb_divider; + + switch (s->silicon_rev) { + case AST2400_A0_SILICON_REV: + case AST2400_A1_SILICON_REV: + apb_divider = 2; + break; + case AST2500_A0_SILICON_REV: + case AST2500_A1_SILICON_REV: + apb_divider = 4; + break; + default: + g_assert_not_reached(); + } + + s->apb_freq = s->hpll / (SCU_CLK_GET_PCLK_DIV(s->regs[CLK_SEL]) + 1) + / apb_divider; +} + static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) { AspeedSCUState *s = ASPEED_SCU(opaque); @@ -222,6 +243,10 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data, case PROT_KEY: s->regs[reg] = (data == ASPEED_SCU_PROT_KEY) ? 1 : 0; return; + case CLK_SEL: + s->regs[reg] = data; + aspeed_scu_set_apb_freq(s); + break; case FREQ_CNTR_EVAL: case VGA_SCRATCH1 ... VGA_SCRATCH8: @@ -247,19 +272,93 @@ static const MemoryRegionOps aspeed_scu_ops = { .valid.unaligned = false, }; +static uint32_t aspeed_scu_get_clkin(AspeedSCUState *s) +{ + if (s->hw_strap1 & SCU_HW_STRAP_CLK_25M_IN) { + return 25000000; + } else if (s->hw_strap1 & SCU_HW_STRAP_CLK_48M_IN) { + return 48000000; + } else { + return 24000000; + } +} + +/* + * Strapped frequencies for the AST2400 in MHz. They depend on the + * clkin frequency. + */ +static const uint32_t hpll_ast2400_freqs[][4] = { + { 384, 360, 336, 408 }, /* 24MHz or 48MHz */ + { 400, 375, 350, 425 }, /* 25MHz */ +}; + +static uint32_t aspeed_scu_calc_hpll_ast2400(AspeedSCUState *s) +{ + uint32_t hpll_reg = s->regs[HPLL_PARAM]; + uint8_t freq_select; + bool clk_25m_in; + + if (hpll_reg & SCU_AST2400_H_PLL_OFF) { + return 0; + } + + if (hpll_reg & SCU_AST2400_H_PLL_PROGRAMMED) { + uint32_t multiplier = 1; + + if (!(hpll_reg & SCU_AST2400_H_PLL_BYPASS_EN)) { + uint32_t n = (hpll_reg >> 5) & 0x3f; + uint32_t od = (hpll_reg >> 4) & 0x1; + uint32_t d = hpll_reg & 0xf; + + multiplier = (2 - od) * ((n + 2) / (d + 1)); + } + + return s->clkin * multiplier; + } + + /* HW strapping */ + clk_25m_in = !!(s->hw_strap1 & SCU_HW_STRAP_CLK_25M_IN); + freq_select = SCU_AST2400_HW_STRAP_GET_H_PLL_CLK(s->hw_strap1); + + return hpll_ast2400_freqs[clk_25m_in][freq_select] * 1000000; +} + +static uint32_t aspeed_scu_calc_hpll_ast2500(AspeedSCUState *s) +{ + uint32_t hpll_reg = s->regs[HPLL_PARAM]; + uint32_t multiplier = 1; + + if (hpll_reg & SCU_H_PLL_OFF) { + return 0; + } + + if (!(hpll_reg & SCU_H_PLL_BYPASS_EN)) { + uint32_t p = (hpll_reg >> 13) & 0x3f; + uint32_t m = (hpll_reg >> 5) & 0xff; + uint32_t n = hpll_reg & 0x1f; + + multiplier = ((m + 1) / (n + 1)) / (p + 1); + } + + return s->clkin * multiplier; +} + static void aspeed_scu_reset(DeviceState *dev) { AspeedSCUState *s = ASPEED_SCU(dev); const uint32_t *reset; + uint32_t (*calc_hpll)(AspeedSCUState *s); switch (s->silicon_rev) { case AST2400_A0_SILICON_REV: case AST2400_A1_SILICON_REV: reset = ast2400_a0_resets; + calc_hpll = aspeed_scu_calc_hpll_ast2400; break; case AST2500_A0_SILICON_REV: case AST2500_A1_SILICON_REV: reset = ast2500_a1_resets; + calc_hpll = aspeed_scu_calc_hpll_ast2500; break; default: g_assert_not_reached(); @@ -270,6 +369,13 @@ static void aspeed_scu_reset(DeviceState *dev) s->regs[HW_STRAP1] = s->hw_strap1; s->regs[HW_STRAP2] = s->hw_strap2; s->regs[PROT_KEY] = s->hw_prot_key; + + /* + * All registers are set. Now compute the frequencies of the main clocks + */ + s->clkin = aspeed_scu_get_clkin(s); + s->hpll = calc_hpll(s); + aspeed_scu_set_apb_freq(s); } static uint32_t aspeed_silicon_revs[] = { diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index d70cc0aeca..f662c38188 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -30,6 +30,10 @@ typedef struct AspeedSCUState { uint32_t hw_strap1; uint32_t hw_strap2; uint32_t hw_prot_key; + + uint32_t clkin; + uint32_t hpll; + uint32_t apb_freq; } AspeedSCUState; #define AST2400_A0_SILICON_REV 0x02000303U @@ -58,7 +62,64 @@ extern bool is_supported_silicon_rev(uint32_t silicon_rev); * 1. 2012/12/29 Ryan Chen Create */ -/* Hardware Strapping Register definition (for Aspeed AST2400 SOC) +/* SCU08 Clock Selection Register + * + * 31 Enable Video Engine clock dynamic slow down + * 30:28 Video Engine clock slow down setting + * 27 2D Engine GCLK clock source selection + * 26 2D Engine GCLK clock throttling enable + * 25:23 APB PCLK divider selection + * 22:20 LPC Host LHCLK divider selection + * 19 LPC Host LHCLK clock generation/output enable control + * 18:16 MAC AHB bus clock divider selection + * 15 SD/SDIO clock running enable + * 14:12 SD/SDIO divider selection + * 11 Reserved + * 10:8 Video port output clock delay control bit + * 7 ARM CPU/AHB clock slow down enable + * 6:4 ARM CPU/AHB clock slow down setting + * 3:2 ECLK clock source selection + * 1 CPU/AHB clock slow down idle timer + * 0 CPU/AHB clock dynamic slow down enable (defined in bit[6:4]) + */ +#define SCU_CLK_GET_PCLK_DIV(x) (((x) >> 23) & 0x7) + +/* SCU24 H-PLL Parameter Register (for Aspeed AST2400 SOC) + * + * 18 H-PLL parameter selection + * 0: Select H-PLL by strapping resistors + * 1: Select H-PLL by the programmed registers (SCU24[17:0]) + * 17 Enable H-PLL bypass mode + * 16 Turn off H-PLL + * 10:5 H-PLL Numerator + * 4 H-PLL Output Divider + * 3:0 H-PLL Denumerator + * + * (Output frequency) = 24MHz * (2-OD) * [(Numerator+2) / (Denumerator+1)] + */ + +#define SCU_AST2400_H_PLL_PROGRAMMED (0x1 << 18) +#define SCU_AST2400_H_PLL_BYPASS_EN (0x1 << 17) +#define SCU_AST2400_H_PLL_OFF (0x1 << 16) + +/* SCU24 H-PLL Parameter Register (for Aspeed AST2500 SOC) + * + * 21 Enable H-PLL reset + * 20 Enable H-PLL bypass mode + * 19 Turn off H-PLL + * 18:13 H-PLL Post Divider + * 12:5 H-PLL Numerator (M) + * 4:0 H-PLL Denumerator (N) + * + * (Output frequency) = CLKIN(24MHz) * [(M+1) / (N+1)] / (P+1) + * + * The default frequency is 792Mhz when CLKIN = 24MHz + */ + +#define SCU_H_PLL_BYPASS_EN (0x1 << 20) +#define SCU_H_PLL_OFF (0x1 << 19) + +/* SCU70 Hardware Strapping Register definition (for Aspeed AST2400 SOC) * * 31:29 Software defined strapping registers * 28:27 DRAM size setting (for VGA driver use) @@ -107,12 +168,13 @@ extern bool is_supported_silicon_rev(uint32_t silicon_rev); #define SCU_AST2400_HW_STRAP_GET_CLK_SOURCE(x) (((((x) >> 23) & 0x1) << 1) \ | (((x) >> 18) & 0x1)) #define SCU_AST2400_HW_STRAP_CLK_SOURCE_MASK ((0x1 << 23) | (0x1 << 18)) -#define AST2400_CLK_25M_IN (0x1 << 23) +#define SCU_HW_STRAP_CLK_25M_IN (0x1 << 23) #define AST2400_CLK_24M_IN 0 #define AST2400_CLK_48M_IN 1 #define AST2400_CLK_25M_IN_24M_USB_CKI 2 #define AST2400_CLK_25M_IN_48M_USB_CKI 3 +#define SCU_HW_STRAP_CLK_48M_IN (0x1 << 18) #define SCU_HW_STRAP_2ND_BOOT_WDT (0x1 << 17) #define SCU_HW_STRAP_SUPER_IO_CONFIG (0x1 << 16) #define SCU_HW_STRAP_VGA_CLASS_CODE (0x1 << 15) @@ -160,8 +222,8 @@ extern bool is_supported_silicon_rev(uint32_t silicon_rev); #define AST2400_DIS_BOOT 3 /* - * Hardware strapping register definition (for Aspeed AST2500 SoC and - * higher) + * SCU70 Hardware strapping register definition (for Aspeed AST2500 + * SoC and higher) * * 31 Enable SPI Flash Strap Auto Fetch Mode * 30 Enable GPIO Strap Mode From e2a11ca859af1ffb4eb18abd9f3a73391008e2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 26 Jun 2018 17:50:42 +0100 Subject: [PATCH 1622/2380] aspeed: initialize the SCU controller first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The System Control Unit should be initialized first as it drives all the configuration of the SoC and other device models. Signed-off-by: Cédric Le Goater Reviewed-by: Joel Stanley Acked-by: Andrew Jeffery Message-id: 20180622075700.5923-3-clg@kaod.org Signed-off-by: Peter Maydell --- hw/arm/aspeed_soc.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 1955a892f4..7cc05ee27e 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -109,18 +109,6 @@ static void aspeed_soc_init(Object *obj) object_initialize(&s->cpu, sizeof(s->cpu), sc->info->cpu_type); object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); - object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC); - object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL); - qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default()); - - object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER); - object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL); - qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default()); - - object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C); - object_property_add_child(obj, "i2c", OBJECT(&s->i2c), NULL); - qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default()); - object_initialize(&s->scu, sizeof(s->scu), TYPE_ASPEED_SCU); object_property_add_child(obj, "scu", OBJECT(&s->scu), NULL); qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); @@ -133,6 +121,18 @@ static void aspeed_soc_init(Object *obj) object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), "hw-prot-key", &error_abort); + object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC); + object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL); + qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default()); + + object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER); + object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL); + qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default()); + + object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C); + object_property_add_child(obj, "i2c", OBJECT(&s->i2c), NULL); + qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default()); + object_initialize(&s->fmc, sizeof(s->fmc), sc->info->fmc_typename); object_property_add_child(obj, "fmc", OBJECT(&s->fmc), NULL); qdev_set_parent_bus(DEVICE(&s->fmc), sysbus_get_default()); @@ -195,6 +195,14 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(get_system_memory(), ASPEED_SOC_SRAM_BASE, &s->sram); + /* SCU */ + object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, ASPEED_SOC_SCU_BASE); + /* VIC */ object_property_set_bool(OBJECT(&s->vic), true, "realized", &err); if (err) { @@ -219,14 +227,6 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); } - /* SCU */ - object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, ASPEED_SOC_SCU_BASE); - /* UART - attach an 8250 to the IO space as our UART5 */ if (serial_hd(0)) { qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]); From 9b945a9ee36a34eaeca412ef9ef35fbfe33c2c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 26 Jun 2018 17:50:42 +0100 Subject: [PATCH 1623/2380] aspeed/timer: use the APB frequency from the SCU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The timer controller can be driven by either an external 1MHz clock or by the APB clock. Today, the model makes the assumption that the APB frequency is always set to 24MHz but this is incorrect. The AST2400 SoC on the palmetto machines uses a 48MHz input clock source and the APB can be set to 48MHz. The consequence is a general system slowdown. The QEMU machines using the AST2500 SoC do not seem impacted today because the APB frequency is still set to 24MHz. We fix the timer frequency for all SoCs by linking the Timer model to the SCU model. The APB frequency driving the timers is now the one configured for the SoC. Signed-off-by: Cédric Le Goater Reviewed-by: Joel Stanley Reviewed-by: Andrew Jeffery Message-id: 20180622075700.5923-4-clg@kaod.org Signed-off-by: Peter Maydell --- hw/arm/aspeed_soc.c | 2 ++ hw/timer/aspeed_timer.c | 19 +++++++++++++++---- include/hw/timer/aspeed_timer.h | 4 ++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 7cc05ee27e..e68911af0f 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -127,6 +127,8 @@ static void aspeed_soc_init(Object *obj) object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER); object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL); + object_property_add_const_link(OBJECT(&s->timerctrl), "scu", + OBJECT(&s->scu), &error_abort); qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default()); object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C); diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 1e31e22b6f..5e3f51b66b 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -10,8 +10,10 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/timer/aspeed_timer.h" +#include "hw/misc/aspeed_scu.h" #include "qemu-common.h" #include "qemu/bitops.h" #include "qemu/timer.h" @@ -26,7 +28,6 @@ #define TIMER_CLOCK_USE_EXT true #define TIMER_CLOCK_EXT_HZ 1000000 #define TIMER_CLOCK_USE_APB false -#define TIMER_CLOCK_APB_HZ 24000000 #define TIMER_REG_STATUS 0 #define TIMER_REG_RELOAD 1 @@ -80,11 +81,11 @@ static inline bool timer_external_clock(AspeedTimer *t) return timer_ctrl_status(t, op_external_clock); } -static uint32_t clock_rates[] = { TIMER_CLOCK_APB_HZ, TIMER_CLOCK_EXT_HZ }; - static inline uint32_t calculate_rate(struct AspeedTimer *t) { - return clock_rates[timer_external_clock(t)]; + AspeedTimerCtrlState *s = timer_to_ctrl(t); + + return timer_external_clock(t) ? TIMER_CLOCK_EXT_HZ : s->scu->apb_freq; } static inline uint32_t calculate_ticks(struct AspeedTimer *t, uint64_t now_ns) @@ -449,6 +450,16 @@ static void aspeed_timer_realize(DeviceState *dev, Error **errp) int i; SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedTimerCtrlState *s = ASPEED_TIMER(dev); + Object *obj; + Error *err = NULL; + + obj = object_property_get_link(OBJECT(dev), "scu", &err); + if (!obj) { + error_propagate(errp, err); + error_prepend(errp, "required link 'scu' not found: "); + return; + } + s->scu = ASPEED_SCU(obj); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { aspeed_init_one_timer(s, i); diff --git a/include/hw/timer/aspeed_timer.h b/include/hw/timer/aspeed_timer.h index bd6c1a7f96..040a088734 100644 --- a/include/hw/timer/aspeed_timer.h +++ b/include/hw/timer/aspeed_timer.h @@ -24,6 +24,8 @@ #include "qemu/timer.h" +typedef struct AspeedSCUState AspeedSCUState; + #define ASPEED_TIMER(obj) \ OBJECT_CHECK(AspeedTimerCtrlState, (obj), TYPE_ASPEED_TIMER); #define TYPE_ASPEED_TIMER "aspeed.timer" @@ -55,6 +57,8 @@ typedef struct AspeedTimerCtrlState { uint32_t ctrl; uint32_t ctrl2; AspeedTimer timers[ASPEED_TIMER_NR_TIMERS]; + + AspeedSCUState *scu; } AspeedTimerCtrlState; #endif /* ASPEED_TIMER_H */ From 0b363b61f1b7530572d7f4d26a3cff390a2100d5 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Thu, 29 Mar 2018 14:51:28 +0200 Subject: [PATCH 1624/2380] hw/i386: Fix IVHD entry length for AMD IOMMU Counting from the IVHD ID field to the all-devices entry, we have 28 bytes, not 36. Signed-off-by: Jan Kiszka Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 9bc6d97ea1..fff1059a31 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2537,7 +2537,7 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker) (1UL << 7), /* PPRSup */ 1); /* IVHD length */ - build_append_int_noprefix(table_data, 0x24, 2); + build_append_int_noprefix(table_data, 28, 2); /* DeviceID */ build_append_int_noprefix(table_data, s->devid, 2); /* Capability offset */ From 2073bd43bd40ff6fc36f293a569bff113c0923d4 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 22 May 2018 09:07:53 +0200 Subject: [PATCH 1625/2380] hw/i386: Fix AMDVI GATS and HATS encodings We support up to 6 levels, but those are encoded as 10b according to the AMD IOMMU spec (chapter 3.3.1, Extended Feature Register). Signed-off-by: Jan Kiszka Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index aeef802364..874030582d 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -165,8 +165,8 @@ #define AMDVI_DTE_UPPER_QUAD_RESERVED 0x08f0000000000000 /* AMDVI paging mode */ -#define AMDVI_GATS_MODE (6ULL << 12) -#define AMDVI_HATS_MODE (6ULL << 10) +#define AMDVI_GATS_MODE (2ULL << 12) +#define AMDVI_HATS_MODE (2ULL << 10) /* IOTLB */ #define AMDVI_IOTLB_MAX_SIZE 1024 From 32c072341f0d17ddc7443bd4c0adb9d2ed55fbd4 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 22 May 2018 18:26:48 -0400 Subject: [PATCH 1626/2380] trace: fix misreporting of TCG access sizes for user-space trace_mem_build_info expects a size_shift for its first argument. Fix it. Signed-off-by: Emilio G. Cota Message-id: 1527028012-21888-2-git-send-email-cota@braap.org Signed-off-by: Stefan Hajnoczi --- include/exec/cpu_ldst_useronly_template.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/include/exec/cpu_ldst_useronly_template.h b/include/exec/cpu_ldst_useronly_template.h index c168f31bba..e30e58ed4a 100644 --- a/include/exec/cpu_ldst_useronly_template.h +++ b/include/exec/cpu_ldst_useronly_template.h @@ -33,20 +33,24 @@ #define SUFFIX q #define USUFFIX q #define DATA_TYPE uint64_t +#define SHIFT 3 #elif DATA_SIZE == 4 #define SUFFIX l #define USUFFIX l #define DATA_TYPE uint32_t +#define SHIFT 2 #elif DATA_SIZE == 2 #define SUFFIX w #define USUFFIX uw #define DATA_TYPE uint16_t #define DATA_STYPE int16_t +#define SHIFT 1 #elif DATA_SIZE == 1 #define SUFFIX b #define USUFFIX ub #define DATA_TYPE uint8_t #define DATA_STYPE int8_t +#define SHIFT 0 #else #error unsupported data size #endif @@ -63,7 +67,7 @@ glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) #if !defined(CODE_ACCESS) trace_guest_mem_before_exec( ENV_GET_CPU(env), ptr, - trace_mem_build_info(DATA_SIZE, false, MO_TE, false)); + trace_mem_build_info(SHIFT, false, MO_TE, false)); #endif return glue(glue(ld, USUFFIX), _p)(g2h(ptr)); } @@ -87,7 +91,7 @@ glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) #if !defined(CODE_ACCESS) trace_guest_mem_before_exec( ENV_GET_CPU(env), ptr, - trace_mem_build_info(DATA_SIZE, true, MO_TE, false)); + trace_mem_build_info(SHIFT, true, MO_TE, false)); #endif return glue(glue(lds, SUFFIX), _p)(g2h(ptr)); } @@ -113,7 +117,7 @@ glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, #if !defined(CODE_ACCESS) trace_guest_mem_before_exec( ENV_GET_CPU(env), ptr, - trace_mem_build_info(DATA_SIZE, false, MO_TE, true)); + trace_mem_build_info(SHIFT, false, MO_TE, true)); #endif glue(glue(st, SUFFIX), _p)(g2h(ptr), v); } @@ -136,3 +140,4 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, #undef SUFFIX #undef USUFFIX #undef DATA_SIZE +#undef SHIFT From 3d69b95e5e2ef4c5acfa2b2aaa93e2fad9eeeaa1 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 22 May 2018 18:26:49 -0400 Subject: [PATCH 1627/2380] trace: simplify trace_mem functions Add some defines for the mem_info bits, simplify trace_mem_build_info, and also simplify trace_mem_get_info by making it a wrapper around trace_mem_build_info. This paves the way for increasing size_shift by one bit. Signed-off-by: Emilio G. Cota Message-id: 1527028012-21888-3-git-send-email-cota@braap.org Signed-off-by: Stefan Hajnoczi --- trace/mem-internal.h | 40 ++++++++++++++++++---------------------- trace/mem.h | 2 +- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/trace/mem-internal.h b/trace/mem-internal.h index ddda934253..b684e2750c 100644 --- a/trace/mem-internal.h +++ b/trace/mem-internal.h @@ -10,37 +10,33 @@ #ifndef TRACE__MEM_INTERNAL_H #define TRACE__MEM_INTERNAL_H -static inline uint8_t trace_mem_get_info(TCGMemOp op, bool store) +#define TRACE_MEM_SZ_SHIFT_MASK 0x3 /* size shift mask */ +#define TRACE_MEM_SE (1ULL << 2) /* sign extended (y/n) */ +#define TRACE_MEM_BE (1ULL << 3) /* big endian (y/n) */ +#define TRACE_MEM_ST (1ULL << 4) /* store (y/n) */ + +static inline uint8_t trace_mem_build_info( + int size_shift, bool sign_extend, TCGMemOp endianness, bool store) { - uint8_t res = op; - bool be = (op & MO_BSWAP) == MO_BE; + uint8_t res; - /* remove untraced fields */ - res &= (1ULL << 4) - 1; - /* make endianness absolute */ - res &= ~MO_BSWAP; - if (be) { - res |= 1ULL << 3; + res = size_shift & TRACE_MEM_SZ_SHIFT_MASK; + if (sign_extend) { + res |= TRACE_MEM_SE; + } + if (endianness == MO_BE) { + res |= TRACE_MEM_BE; } - /* add fields */ if (store) { - res |= 1ULL << 4; + res |= TRACE_MEM_ST; } - return res; } -static inline uint8_t trace_mem_build_info( - TCGMemOp size, bool sign_extend, TCGMemOp endianness, bool store) +static inline uint8_t trace_mem_get_info(TCGMemOp op, bool store) { - uint8_t res = 0; - res |= size; - res |= (sign_extend << 2); - if (endianness == MO_BE) { - res |= (1ULL << 3); - } - res |= (store << 4); - return res; + return trace_mem_build_info(op & MO_SIZE, !!(op & MO_SIGN), + op & MO_BSWAP, store); } #endif /* TRACE__MEM_INTERNAL_H */ diff --git a/trace/mem.h b/trace/mem.h index 9c88bcb4e6..2b58196e53 100644 --- a/trace/mem.h +++ b/trace/mem.h @@ -25,7 +25,7 @@ static uint8_t trace_mem_get_info(TCGMemOp op, bool store); * * Return a value for the 'info' argument in guest memory access traces. */ -static uint8_t trace_mem_build_info(TCGMemOp size, bool sign_extend, +static uint8_t trace_mem_build_info(int size_shift, bool sign_extend, TCGMemOp endianness, bool store); From 706485d2e2b1e1770d0fecdae7639dc528d77583 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 22 May 2018 18:26:50 -0400 Subject: [PATCH 1628/2380] trace: expand mem_info:size_shift to 3 bits This will allow us to trace 16B-long memory accesses. Signed-off-by: Emilio G. Cota Message-id: 1527028012-21888-4-git-send-email-cota@braap.org Signed-off-by: Stefan Hajnoczi --- trace/mem-internal.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trace/mem-internal.h b/trace/mem-internal.h index b684e2750c..a9e408eb2f 100644 --- a/trace/mem-internal.h +++ b/trace/mem-internal.h @@ -10,10 +10,10 @@ #ifndef TRACE__MEM_INTERNAL_H #define TRACE__MEM_INTERNAL_H -#define TRACE_MEM_SZ_SHIFT_MASK 0x3 /* size shift mask */ -#define TRACE_MEM_SE (1ULL << 2) /* sign extended (y/n) */ -#define TRACE_MEM_BE (1ULL << 3) /* big endian (y/n) */ -#define TRACE_MEM_ST (1ULL << 4) /* store (y/n) */ +#define TRACE_MEM_SZ_SHIFT_MASK 0x7 /* size shift mask */ +#define TRACE_MEM_SE (1ULL << 3) /* sign extended (y/n) */ +#define TRACE_MEM_BE (1ULL << 4) /* big endian (y/n) */ +#define TRACE_MEM_ST (1ULL << 5) /* store (y/n) */ static inline uint8_t trace_mem_build_info( int size_shift, bool sign_extend, TCGMemOp endianness, bool store) From f9b47999afbf84ddbe7e5649a82f477bb77e3b38 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 22 May 2018 18:26:51 -0400 Subject: [PATCH 1629/2380] trace: add trace_mem_build_info_no_se_be/le These will be used by the following commit. Signed-off-by: Emilio G. Cota Message-id: 1527028012-21888-5-git-send-email-cota@braap.org Signed-off-by: Stefan Hajnoczi --- trace/mem-internal.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/trace/mem-internal.h b/trace/mem-internal.h index a9e408eb2f..f6efaf6d6b 100644 --- a/trace/mem-internal.h +++ b/trace/mem-internal.h @@ -39,4 +39,16 @@ static inline uint8_t trace_mem_get_info(TCGMemOp op, bool store) op & MO_BSWAP, store); } +static inline +uint8_t trace_mem_build_info_no_se_be(int size_shift, bool store) +{ + return trace_mem_build_info(size_shift, false, MO_BE, store); +} + +static inline +uint8_t trace_mem_build_info_no_se_le(int size_shift, bool store) +{ + return trace_mem_build_info(size_shift, false, MO_LE, store); +} + #endif /* TRACE__MEM_INTERNAL_H */ From d071f4cd5579fe82ba764e4c29f06664658e3762 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Tue, 22 May 2018 18:26:52 -0400 Subject: [PATCH 1630/2380] trace: enable tracing of TCG atomics We do not trace guest atomic accesses. Fix it. Tested with a modified atomic_add-bench so that it executes a deterministic number of instructions, i.e. fixed seeding, no threading and fixed number of loop iterations instead of running for a certain time. Before: - With parallel_cpus = false (no clone syscall so it is never set to true): 220070 memory accesses - With parallel_cpus = true (hard-coded): 212105 memory accesses <-- we're not tracing the atomics! After: 220070 memory accesses regardless of parallel_cpus. Signed-off-by: Emilio G. Cota Message-id: 1527028012-21888-6-git-send-email-cota@braap.org Signed-off-by: Stefan Hajnoczi --- accel/tcg/atomic_template.h | 87 ++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 3f41ef2782..d751bcba48 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -18,30 +18,37 @@ * License along with this library; if not, see . */ +#include "trace/mem.h" + #if DATA_SIZE == 16 # define SUFFIX o # define DATA_TYPE Int128 # define BSWAP bswap128 +# define SHIFT 4 #elif DATA_SIZE == 8 # define SUFFIX q # define DATA_TYPE uint64_t # define SDATA_TYPE int64_t # define BSWAP bswap64 +# define SHIFT 3 #elif DATA_SIZE == 4 # define SUFFIX l # define DATA_TYPE uint32_t # define SDATA_TYPE int32_t # define BSWAP bswap32 +# define SHIFT 2 #elif DATA_SIZE == 2 # define SUFFIX w # define DATA_TYPE uint16_t # define SDATA_TYPE int16_t # define BSWAP bswap16 +# define SHIFT 1 #elif DATA_SIZE == 1 # define SUFFIX b # define DATA_TYPE uint8_t # define SDATA_TYPE int8_t # define BSWAP +# define SHIFT 0 #else # error unsupported data size #endif @@ -52,14 +59,37 @@ # define ABI_TYPE uint32_t #endif +#define ATOMIC_TRACE_RMW do { \ + uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \ + \ + trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \ + trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, \ + info | TRACE_MEM_ST); \ + } while (0) + +#define ATOMIC_TRACE_LD do { \ + uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \ + \ + trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \ + } while (0) + +# define ATOMIC_TRACE_ST do { \ + uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \ + \ + trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \ + } while (0) + /* Define host-endian atomic operations. Note that END is used within the ATOMIC_NAME macro, and redefined below. */ #if DATA_SIZE == 1 # define END +# define MEND _be /* either le or be would be fine */ #elif defined(HOST_WORDS_BIGENDIAN) # define END _be +# define MEND _be #else # define END _le +# define MEND _le #endif ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, @@ -67,7 +97,10 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); + DATA_TYPE ret; + + ATOMIC_TRACE_RMW; + ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); ATOMIC_MMU_CLEANUP; return ret; } @@ -77,6 +110,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) { ATOMIC_MMU_DECLS; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; + + ATOMIC_TRACE_LD; __atomic_load(haddr, &val, __ATOMIC_RELAXED); ATOMIC_MMU_CLEANUP; return val; @@ -87,6 +122,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + + ATOMIC_TRACE_ST; __atomic_store(haddr, &val, __ATOMIC_RELAXED); ATOMIC_MMU_CLEANUP; } @@ -96,7 +133,10 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - DATA_TYPE ret = atomic_xchg__nocheck(haddr, val); + DATA_TYPE ret; + + ATOMIC_TRACE_RMW; + ret = atomic_xchg__nocheck(haddr, val); ATOMIC_MMU_CLEANUP; return ret; } @@ -107,7 +147,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ { \ ATOMIC_MMU_DECLS; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ - DATA_TYPE ret = atomic_##X(haddr, val); \ + DATA_TYPE ret; \ + \ + ATOMIC_TRACE_RMW; \ + ret = atomic_##X(haddr, val); \ ATOMIC_MMU_CLEANUP; \ return ret; \ } @@ -126,6 +169,9 @@ GEN_ATOMIC_HELPER(xor_fetch) /* These helpers are, as a whole, full barriers. Within the helper, * the leading barrier is explicit and the trailing barrier is within * cmpxchg primitive. + * + * Trace this load + RMW loop as a single RMW op. This way, regardless + * of CF_PARALLEL's value, we'll trace just a read and a write. */ #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ @@ -134,6 +180,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ATOMIC_MMU_DECLS; \ XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ XDATA_TYPE cmp, old, new, val = xval; \ + \ + ATOMIC_TRACE_RMW; \ smp_mb(); \ cmp = atomic_read__nocheck(haddr); \ do { \ @@ -158,6 +206,7 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) #endif /* DATA SIZE >= 16 */ #undef END +#undef MEND #if DATA_SIZE > 1 @@ -165,8 +214,10 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) within the ATOMIC_NAME macro. */ #ifdef HOST_WORDS_BIGENDIAN # define END _le +# define MEND _le #else # define END _be +# define MEND _be #endif ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, @@ -174,7 +225,10 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); + DATA_TYPE ret; + + ATOMIC_TRACE_RMW; + ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); ATOMIC_MMU_CLEANUP; return BSWAP(ret); } @@ -184,6 +238,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) { ATOMIC_MMU_DECLS; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; + + ATOMIC_TRACE_LD; __atomic_load(haddr, &val, __ATOMIC_RELAXED); ATOMIC_MMU_CLEANUP; return BSWAP(val); @@ -194,6 +250,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + + ATOMIC_TRACE_ST; val = BSWAP(val); __atomic_store(haddr, &val, __ATOMIC_RELAXED); ATOMIC_MMU_CLEANUP; @@ -204,7 +262,10 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val)); + ABI_TYPE ret; + + ATOMIC_TRACE_RMW; + ret = atomic_xchg__nocheck(haddr, BSWAP(val)); ATOMIC_MMU_CLEANUP; return BSWAP(ret); } @@ -215,7 +276,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ { \ ATOMIC_MMU_DECLS; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ - DATA_TYPE ret = atomic_##X(haddr, BSWAP(val)); \ + DATA_TYPE ret; \ + \ + ATOMIC_TRACE_RMW; \ + ret = atomic_##X(haddr, BSWAP(val)); \ ATOMIC_MMU_CLEANUP; \ return BSWAP(ret); \ } @@ -232,6 +296,9 @@ GEN_ATOMIC_HELPER(xor_fetch) /* These helpers are, as a whole, full barriers. Within the helper, * the leading barrier is explicit and the trailing barrier is within * cmpxchg primitive. + * + * Trace this load + RMW loop as a single RMW op. This way, regardless + * of CF_PARALLEL's value, we'll trace just a read and a write. */ #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ @@ -240,6 +307,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ATOMIC_MMU_DECLS; \ XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ XDATA_TYPE ldo, ldn, old, new, val = xval; \ + \ + ATOMIC_TRACE_RMW; \ smp_mb(); \ ldn = atomic_read__nocheck(haddr); \ do { \ @@ -271,11 +340,17 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) #endif /* DATA_SIZE >= 16 */ #undef END +#undef MEND #endif /* DATA_SIZE > 1 */ +#undef ATOMIC_TRACE_ST +#undef ATOMIC_TRACE_LD +#undef ATOMIC_TRACE_RMW + #undef BSWAP #undef ABI_TYPE #undef DATA_TYPE #undef SDATA_TYPE #undef SUFFIX #undef DATA_SIZE +#undef SHIFT From ec09f877532210e28e1d4b6b12896d3eb6d8e8d1 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 21 Jun 2018 16:02:54 +0100 Subject: [PATCH 1631/2380] trace: forbid floating point types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only one existing trace event uses a floating point type. Unfortunately float and double cannot be supported since SystemTap does not have floating point types. Remove float and double from the whitelist and document this limitation. Update the migrate_transferred trace event to use uint64_t instead of double. Cc: Dr. David Alan Gilbert Cc: Daniel P. Berrangé Cc: Peter Maydell Signed-off-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Juan Quintela Message-id: 20180621150254.4922-1-stefanha@redhat.com Signed-off-by: Stefan Hajnoczi --- docs/devel/tracing.txt | 5 +++++ migration/trace-events | 2 +- qapi/trace-events | 2 +- scripts/tracetool/__init__.py | 2 -- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.txt index 07abbb345c..6f815ecbd7 100644 --- a/docs/devel/tracing.txt +++ b/docs/devel/tracing.txt @@ -104,6 +104,11 @@ Trace events should use types as follows: * For everything else, use primitive scalar types (char, int, long) with the appropriate signedness. + * Avoid floating point types (float and double) because SystemTap does not + support them. In most cases it is possible to round to an integer type + instead. This may require scaling the value first by multiplying it by 1000 + or the like when digits after the decimal point need to be preserved. + Format strings should reflect the types defined in the trace event. Take special care to use PRId64 and PRIu64 for int64_t and uint64_t types, respectively. This ensures portability between 32- and 64-bit platforms. diff --git a/migration/trace-events b/migration/trace-events index 3f67758893..7ea522e453 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -133,7 +133,7 @@ migrate_global_state_post_load(const char *state) "loaded state: %s" migrate_global_state_pre_save(const char *state) "saved state: %s" migration_thread_low_pending(uint64_t pending) "%" PRIu64 migrate_state_too_big(void) "" -migrate_transferred(uint64_t tranferred, uint64_t time_spent, double bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %g max_size %" PRId64 +migrate_transferred(uint64_t tranferred, uint64_t time_spent, uint64_t bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %" PRIu64 " max_size %" PRId64 process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d" process_incoming_migration_co_postcopy_end_main(void) "" migration_set_incoming_channel(void *ioc, const char *ioctype) "ioc=%p ioctype=%s" diff --git a/qapi/trace-events b/qapi/trace-events index 9e9008a1dc..70e049ea80 100644 --- a/qapi/trace-events +++ b/qapi/trace-events @@ -29,6 +29,6 @@ visit_type_int64(void *v, const char *name, int64_t *obj) "v=%p name=%s obj=%p" visit_type_size(void *v, const char *name, uint64_t *obj) "v=%p name=%s obj=%p" visit_type_bool(void *v, const char *name, bool *obj) "v=%p name=%s obj=%p" visit_type_str(void *v, const char *name, char **obj) "v=%p name=%s obj=%p" -visit_type_number(void *v, const char *name, double *obj) "v=%p name=%s obj=%p" +visit_type_number(void *v, const char *name, void *obj) "v=%p name=%s obj=%p" visit_type_any(void *v, const char *name, void *obj) "v=%p name=%s obj=%p" visit_type_null(void *v, const char *name, void *obj) "v=%p name=%s obj=%p" diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index b20fac34a3..0e3c9e146c 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -53,8 +53,6 @@ ALLOWED_TYPES = [ "bool", "unsigned", "signed", - "float", - "double", "int8_t", "uint8_t", "int16_t", From 34c55a94b14323c06791395801aadaf5606761dc Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 10 Apr 2018 23:35:15 +0200 Subject: [PATCH 1632/2380] migration: Create multipage support We only create/destry the page list here. We will use it later. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/ram.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/migration/ram.c b/migration/ram.c index cd5f55117d..ed4401ee46 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -517,6 +517,20 @@ typedef struct { uint8_t id; } __attribute__((packed)) MultiFDInit_t; +typedef struct { + /* number of used pages */ + uint32_t used; + /* number of allocated pages */ + uint32_t allocated; + /* global number of generated multifd packets */ + uint64_t packet_num; + /* offset of each page */ + ram_addr_t *offset; + /* pointer to each page */ + struct iovec *iov; + RAMBlock *block; +} MultiFDPages_t; + typedef struct { /* this fields are not changed once the thread is created */ /* channel number */ @@ -535,6 +549,8 @@ typedef struct { bool running; /* should this thread finish */ bool quit; + /* array of pages to sent */ + MultiFDPages_t *pages; } MultiFDSendParams; typedef struct { @@ -555,6 +571,8 @@ typedef struct { bool running; /* should this thread finish */ bool quit; + /* array of pages to receive */ + MultiFDPages_t *pages; } MultiFDRecvParams; static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) @@ -619,10 +637,36 @@ static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) return msg.id; } +static MultiFDPages_t *multifd_pages_init(size_t size) +{ + MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1); + + pages->allocated = size; + pages->iov = g_new0(struct iovec, size); + pages->offset = g_new0(ram_addr_t, size); + + return pages; +} + +static void multifd_pages_clear(MultiFDPages_t *pages) +{ + pages->used = 0; + pages->allocated = 0; + pages->packet_num = 0; + pages->block = NULL; + g_free(pages->iov); + pages->iov = NULL; + g_free(pages->offset); + pages->offset = NULL; + g_free(pages); +} + struct { MultiFDSendParams *params; /* number of created threads */ int count; + /* array of pages to sent */ + MultiFDPages_t *pages; } *multifd_send_state; static void multifd_send_terminate_threads(Error *err) @@ -672,9 +716,13 @@ int multifd_save_cleanup(Error **errp) qemu_sem_destroy(&p->sem); g_free(p->name); p->name = NULL; + multifd_pages_clear(p->pages); + p->pages = NULL; } g_free(multifd_send_state->params); multifd_send_state->params = NULL; + multifd_pages_clear(multifd_send_state->pages); + multifd_send_state->pages = NULL; g_free(multifd_send_state); multifd_send_state = NULL; return ret; @@ -735,6 +783,7 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) int multifd_save_setup(void) { int thread_count; + uint32_t page_count = migrate_multifd_page_count(); uint8_t i; if (!migrate_use_multifd()) { @@ -744,6 +793,8 @@ int multifd_save_setup(void) multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); atomic_set(&multifd_send_state->count, 0); + multifd_send_state->pages = multifd_pages_init(page_count); + for (i = 0; i < thread_count; i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; @@ -751,6 +802,7 @@ int multifd_save_setup(void) qemu_sem_init(&p->sem, 0); p->quit = false; p->id = i; + p->pages = multifd_pages_init(page_count); p->name = g_strdup_printf("multifdsend_%d", i); socket_send_channel_create(multifd_new_send_channel_async, p); } @@ -808,6 +860,8 @@ int multifd_load_cleanup(Error **errp) qemu_sem_destroy(&p->sem); g_free(p->name); p->name = NULL; + multifd_pages_clear(p->pages); + p->pages = NULL; } g_free(multifd_recv_state->params); multifd_recv_state->params = NULL; @@ -841,6 +895,7 @@ static void *multifd_recv_thread(void *opaque) int multifd_load_setup(void) { int thread_count; + uint32_t page_count = migrate_multifd_page_count(); uint8_t i; if (!migrate_use_multifd()) { @@ -850,6 +905,7 @@ int multifd_load_setup(void) multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state)); multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count); atomic_set(&multifd_recv_state->count, 0); + for (i = 0; i < thread_count; i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; @@ -857,6 +913,7 @@ int multifd_load_setup(void) qemu_sem_init(&p->sem, 0); p->quit = false; p->id = i; + p->pages = multifd_pages_init(page_count); p->name = g_strdup_printf("multifdrecv_%d", i); } return 0; From 2a26c979b15dfedc1d7f4df7ed7e853d768e2850 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 4 Apr 2018 11:26:58 +0200 Subject: [PATCH 1633/2380] migration: Create multifd packet We still don't put anything there. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert -- fix magic (dave) check offset/ramblock (dave) s/seq/packet_num/ and make it 64bit --- migration/ram.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index ed4401ee46..fd144fdf9a 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -517,6 +517,17 @@ typedef struct { uint8_t id; } __attribute__((packed)) MultiFDInit_t; +typedef struct { + uint32_t magic; + uint32_t version; + uint32_t flags; + uint32_t size; + uint32_t used; + uint64_t packet_num; + char ramblock[256]; + uint64_t offset[]; +} __attribute__((packed)) MultiFDPacket_t; + typedef struct { /* number of used pages */ uint32_t used; @@ -551,6 +562,14 @@ typedef struct { bool quit; /* array of pages to sent */ MultiFDPages_t *pages; + /* packet allocated len */ + uint32_t packet_len; + /* pointer to the packet */ + MultiFDPacket_t *packet; + /* multifd flags for each packet */ + uint32_t flags; + /* global number of generated multifd packets */ + uint64_t packet_num; } MultiFDSendParams; typedef struct { @@ -573,6 +592,14 @@ typedef struct { bool quit; /* array of pages to receive */ MultiFDPages_t *pages; + /* packet allocated len */ + uint32_t packet_len; + /* pointer to the packet */ + MultiFDPacket_t *packet; + /* multifd flags for each packet */ + uint32_t flags; + /* global number of generated multifd packets */ + uint64_t packet_num; } MultiFDRecvParams; static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) @@ -661,6 +688,99 @@ static void multifd_pages_clear(MultiFDPages_t *pages) g_free(pages); } +static void multifd_send_fill_packet(MultiFDSendParams *p) +{ + MultiFDPacket_t *packet = p->packet; + int i; + + packet->magic = cpu_to_be32(MULTIFD_MAGIC); + packet->version = cpu_to_be32(MULTIFD_VERSION); + packet->flags = cpu_to_be32(p->flags); + packet->size = cpu_to_be32(migrate_multifd_page_count()); + packet->used = cpu_to_be32(p->pages->used); + packet->packet_num = cpu_to_be64(p->packet_num); + + if (p->pages->block) { + strncpy(packet->ramblock, p->pages->block->idstr, 256); + } + + for (i = 0; i < p->pages->used; i++) { + packet->offset[i] = cpu_to_be64(p->pages->offset[i]); + } +} + +static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) +{ + MultiFDPacket_t *packet = p->packet; + RAMBlock *block; + int i; + + /* ToDo: We can't use it until we haven't received a message */ + return 0; + + be32_to_cpus(&packet->magic); + if (packet->magic != MULTIFD_MAGIC) { + error_setg(errp, "multifd: received packet " + "magic %x and expected magic %x", + packet->magic, MULTIFD_MAGIC); + return -1; + } + + be32_to_cpus(&packet->version); + if (packet->version != MULTIFD_VERSION) { + error_setg(errp, "multifd: received packet " + "version %d and expected version %d", + packet->version, MULTIFD_VERSION); + return -1; + } + + p->flags = be32_to_cpu(packet->flags); + + be32_to_cpus(&packet->size); + if (packet->size > migrate_multifd_page_count()) { + error_setg(errp, "multifd: received packet " + "with size %d and expected maximum size %d", + packet->size, migrate_multifd_page_count()) ; + return -1; + } + + p->pages->used = be32_to_cpu(packet->used); + if (p->pages->used > packet->size) { + error_setg(errp, "multifd: received packet " + "with size %d and expected maximum size %d", + p->pages->used, packet->size) ; + return -1; + } + + p->packet_num = be64_to_cpu(packet->packet_num); + + if (p->pages->used) { + /* make sure that ramblock is 0 terminated */ + packet->ramblock[255] = 0; + block = qemu_ram_block_by_name(packet->ramblock); + if (!block) { + error_setg(errp, "multifd: unknown ram block %s", + packet->ramblock); + return -1; + } + } + + for (i = 0; i < p->pages->used; i++) { + ram_addr_t offset = be64_to_cpu(packet->offset[i]); + + if (offset > (block->used_length - TARGET_PAGE_SIZE)) { + error_setg(errp, "multifd: offset too long " RAM_ADDR_FMT + " (max " RAM_ADDR_FMT ")", + offset, block->max_length); + return -1; + } + p->pages->iov[i].iov_base = block->host + offset; + p->pages->iov[i].iov_len = TARGET_PAGE_SIZE; + } + + return 0; +} + struct { MultiFDSendParams *params; /* number of created threads */ @@ -718,6 +838,9 @@ int multifd_save_cleanup(Error **errp) p->name = NULL; multifd_pages_clear(p->pages); p->pages = NULL; + p->packet_len = 0; + g_free(p->packet); + p->packet = NULL; } g_free(multifd_send_state->params); multifd_send_state->params = NULL; @@ -739,6 +862,7 @@ static void *multifd_send_thread(void *opaque) while (true) { qemu_mutex_lock(&p->mutex); + multifd_send_fill_packet(p); if (p->quit) { qemu_mutex_unlock(&p->mutex); break; @@ -803,6 +927,9 @@ int multifd_save_setup(void) p->quit = false; p->id = i; p->pages = multifd_pages_init(page_count); + p->packet_len = sizeof(MultiFDPacket_t) + + sizeof(ram_addr_t) * page_count; + p->packet = g_malloc0(p->packet_len); p->name = g_strdup_printf("multifdsend_%d", i); socket_send_channel_create(multifd_new_send_channel_async, p); } @@ -862,6 +989,9 @@ int multifd_load_cleanup(Error **errp) p->name = NULL; multifd_pages_clear(p->pages); p->pages = NULL; + p->packet_len = 0; + g_free(p->packet); + p->packet = NULL; } g_free(multifd_recv_state->params); multifd_recv_state->params = NULL; @@ -874,10 +1004,20 @@ int multifd_load_cleanup(Error **errp) static void *multifd_recv_thread(void *opaque) { MultiFDRecvParams *p = opaque; + Error *local_err = NULL; + int ret; while (true) { qemu_mutex_lock(&p->mutex); - if (p->quit) { + if (false) { + /* ToDo: Packet reception goes here */ + + ret = multifd_recv_unfill_packet(p, &local_err); + qemu_mutex_unlock(&p->mutex); + if (ret) { + break; + } + } else if (p->quit) { qemu_mutex_unlock(&p->mutex); break; } @@ -914,6 +1054,9 @@ int multifd_load_setup(void) p->quit = false; p->id = i; p->pages = multifd_pages_init(page_count); + p->packet_len = sizeof(MultiFDPacket_t) + + sizeof(ram_addr_t) * page_count; + p->packet = g_malloc0(p->packet_len); p->name = g_strdup_printf("multifdrecv_%d", i); } return 0; From 6cde6fbe2b2ad672029ce9737659aa34f581c07a Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 26 Jun 2018 15:26:35 +0200 Subject: [PATCH 1634/2380] migration: Calculate mbps only during transfer time We used to include in this calculation the setup time, but that can be quite big in rdma or multifd. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/migration.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index e1eaa97df4..d3e6da9bfe 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2708,6 +2708,7 @@ static void migration_calculate_complete(MigrationState *s) { uint64_t bytes = qemu_ftell(s->to_dst_file); int64_t end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + int64_t transfer_time; s->total_time = end_time - s->start_time; if (!s->downtime) { @@ -2718,8 +2719,9 @@ static void migration_calculate_complete(MigrationState *s) s->downtime = end_time - s->downtime_start; } - if (s->total_time) { - s->mbps = ((double) bytes * 8.0) / s->total_time / 1000; + transfer_time = s->total_time - s->setup_time; + if (transfer_time) { + s->mbps = ((double) bytes * 8.0) / transfer_time / 1000; } } From 0c8f0efdd461b26f64fdf8785c333558315bc297 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 26 Jun 2018 15:38:00 +0200 Subject: [PATCH 1635/2380] migration: Abstract the number of bytes sent Right now we use the "position" inside the QEMUFile, but things like RDMA already do weird things to be able to maintain that counter right, and multifd will have some similar problems. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/migration.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index d3e6da9bfe..264f3ce84e 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2704,9 +2704,15 @@ static MigThrError migration_detect_error(MigrationState *s) } } +/* How many bytes have we transferred since the beggining of the migration */ +static uint64_t migration_total_bytes(MigrationState *s) +{ + return qemu_ftell(s->to_dst_file); +} + static void migration_calculate_complete(MigrationState *s) { - uint64_t bytes = qemu_ftell(s->to_dst_file); + uint64_t bytes = migration_total_bytes(s); int64_t end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); int64_t transfer_time; @@ -2729,13 +2735,15 @@ static void migration_update_counters(MigrationState *s, int64_t current_time) { uint64_t transferred, time_spent; + uint64_t current_bytes; /* bytes transferred since the beginning */ double bandwidth; if (current_time < s->iteration_start_time + BUFFER_DELAY) { return; } - transferred = qemu_ftell(s->to_dst_file) - s->iteration_initial_bytes; + current_bytes = migration_total_bytes(s); + transferred = current_bytes - s->iteration_initial_bytes; time_spent = current_time - s->iteration_start_time; bandwidth = (double)transferred / time_spent; s->threshold_size = bandwidth * s->parameters.downtime_limit; @@ -2754,7 +2762,7 @@ static void migration_update_counters(MigrationState *s, qemu_file_reset_rate_limit(s->to_dst_file); s->iteration_start_time = current_time; - s->iteration_initial_bytes = qemu_ftell(s->to_dst_file); + s->iteration_initial_bytes = current_bytes; trace_migrate_transferred(transferred, time_spent, bandwidth, s->threshold_size); From 408ea6ae4c8f94621dff64d69c7fa3f6e84ef2fb Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 6 Apr 2018 18:28:59 +0200 Subject: [PATCH 1636/2380] migration: Add multifd traces for start/end thread We want to know how many pages/packets each channel has sent. Add counters for those. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert -- sort trace-events (dave) --- migration/ram.c | 22 ++++++++++++++++++++++ migration/trace-events | 4 ++++ 2 files changed, 26 insertions(+) diff --git a/migration/ram.c b/migration/ram.c index fd144fdf9a..5c040e3ae5 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -570,6 +570,11 @@ typedef struct { uint32_t flags; /* global number of generated multifd packets */ uint64_t packet_num; + /* thread local variables */ + /* packets sent through this channel */ + uint64_t num_packets; + /* pages sent through this channel */ + uint64_t num_pages; } MultiFDSendParams; typedef struct { @@ -600,6 +605,11 @@ typedef struct { uint32_t flags; /* global number of generated multifd packets */ uint64_t packet_num; + /* thread local variables */ + /* packets sent through this channel */ + uint64_t num_packets; + /* pages sent through this channel */ + uint64_t num_pages; } MultiFDRecvParams; static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) @@ -856,9 +866,13 @@ static void *multifd_send_thread(void *opaque) MultiFDSendParams *p = opaque; Error *local_err = NULL; + trace_multifd_send_thread_start(p->id); + if (multifd_send_initial_packet(p, &local_err) < 0) { goto out; } + /* initial packet */ + p->num_packets = 1; while (true) { qemu_mutex_lock(&p->mutex); @@ -880,6 +894,8 @@ out: p->running = false; qemu_mutex_unlock(&p->mutex); + trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages); + return NULL; } @@ -1007,6 +1023,8 @@ static void *multifd_recv_thread(void *opaque) Error *local_err = NULL; int ret; + trace_multifd_recv_thread_start(p->id); + while (true) { qemu_mutex_lock(&p->mutex); if (false) { @@ -1029,6 +1047,8 @@ static void *multifd_recv_thread(void *opaque) p->running = false; qemu_mutex_unlock(&p->mutex); + trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages); + return NULL; } @@ -1094,6 +1114,8 @@ void multifd_recv_new_channel(QIOChannel *ioc) } p->c = ioc; object_ref(OBJECT(ioc)); + /* initial packet */ + p->num_packets = 1; p->running = true; qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, diff --git a/migration/trace-events b/migration/trace-events index 3f67758893..a80aaa8b3f 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -76,6 +76,10 @@ get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, unsigned migration_bitmap_sync_start(void) "" migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_throttle(void) "" +multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 +multifd_recv_thread_start(uint8_t id) "%d" +multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 +multifd_send_thread_start(uint8_t id) "%d" ram_discard_range(const char *rbname, uint64_t start, size_t len) "%s: start: %" PRIx64 " %zx" ram_load_loop(const char *rbname, uint64_t addr, int flags, void *host) "%s: addr: 0x%" PRIx64 " flags: 0x%x host: %p" ram_load_postcopy_loop(uint64_t addr, int flags) "@%" PRIx64 " %x" From d82628e4bde23aaf50273cc835c8ce28b43590bd Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 11 Apr 2018 02:44:24 +0200 Subject: [PATCH 1637/2380] migration: Multifd channels always wait on the sem Either for quit, sync or packet, we first wake them. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/ram.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 5c040e3ae5..45d6c43bfe 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -875,6 +875,7 @@ static void *multifd_send_thread(void *opaque) p->num_packets = 1; while (true) { + qemu_sem_wait(&p->sem); qemu_mutex_lock(&p->mutex); multifd_send_fill_packet(p); if (p->quit) { @@ -882,7 +883,9 @@ static void *multifd_send_thread(void *opaque) break; } qemu_mutex_unlock(&p->mutex); - qemu_sem_wait(&p->sem); + /* this is impossible */ + error_setg(&local_err, "multifd_send_thread: Unknown command"); + break; } out: @@ -1026,6 +1029,7 @@ static void *multifd_recv_thread(void *opaque) trace_multifd_recv_thread_start(p->id); while (true) { + qemu_sem_wait(&p->sem); qemu_mutex_lock(&p->mutex); if (false) { /* ToDo: Packet reception goes here */ @@ -1040,9 +1044,14 @@ static void *multifd_recv_thread(void *opaque) break; } qemu_mutex_unlock(&p->mutex); - qemu_sem_wait(&p->sem); + /* this is impossible */ + error_setg(&local_err, "multifd_recv_thread: Unknown command"); + break; } + if (local_err) { + multifd_recv_terminate_threads(local_err); + } qemu_mutex_lock(&p->mutex); p->running = false; qemu_mutex_unlock(&p->mutex); From 0beb5ed3279abf80d0475ca35f48041b02a9851a Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 11 Apr 2018 03:02:10 +0200 Subject: [PATCH 1638/2380] migration: Add block where to send/receive packets Once there add tracepoints. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/ram.c | 51 +++++++++++++++++++++++++++++++++++++----- migration/trace-events | 2 ++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 45d6c43bfe..76410f9de8 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -560,6 +560,8 @@ typedef struct { bool running; /* should this thread finish */ bool quit; + /* thread has work to do */ + int pending_job; /* array of pages to sent */ MultiFDPages_t *pages; /* packet allocated len */ @@ -595,6 +597,8 @@ typedef struct { bool running; /* should this thread finish */ bool quit; + /* thread has work to do */ + bool pending_job; /* array of pages to receive */ MultiFDPages_t *pages; /* packet allocated len */ @@ -877,8 +881,28 @@ static void *multifd_send_thread(void *opaque) while (true) { qemu_sem_wait(&p->sem); qemu_mutex_lock(&p->mutex); - multifd_send_fill_packet(p); - if (p->quit) { + + if (p->pending_job) { + uint32_t used = p->pages->used; + uint64_t packet_num = p->packet_num; + uint32_t flags = p->flags; + + multifd_send_fill_packet(p); + p->flags = 0; + p->num_packets++; + p->num_pages += used; + p->pages->used = 0; + qemu_mutex_unlock(&p->mutex); + + trace_multifd_send(p->id, packet_num, used, flags); + + /* ToDo: send packet here */ + + qemu_mutex_lock(&p->mutex); + p->pending_job--; + qemu_mutex_unlock(&p->mutex); + continue; + } else if (p->quit) { qemu_mutex_unlock(&p->mutex); break; } @@ -944,6 +968,7 @@ int multifd_save_setup(void) qemu_mutex_init(&p->mutex); qemu_sem_init(&p->sem, 0); p->quit = false; + p->pending_job = 0; p->id = i; p->pages = multifd_pages_init(page_count); p->packet_len = sizeof(MultiFDPacket_t) @@ -1031,14 +1056,27 @@ static void *multifd_recv_thread(void *opaque) while (true) { qemu_sem_wait(&p->sem); qemu_mutex_lock(&p->mutex); - if (false) { - /* ToDo: Packet reception goes here */ - - ret = multifd_recv_unfill_packet(p, &local_err); + if (p->pending_job) { + uint32_t used; + uint32_t flags; qemu_mutex_unlock(&p->mutex); + + /* ToDo: recv packet here */ + + qemu_mutex_lock(&p->mutex); + ret = multifd_recv_unfill_packet(p, &local_err); if (ret) { + qemu_mutex_unlock(&p->mutex); break; } + + used = p->pages->used; + flags = p->flags; + trace_multifd_recv(p->id, p->packet_num, used, flags); + p->pending_job = false; + p->num_packets++; + p->num_pages += used; + qemu_mutex_unlock(&p->mutex); } else if (p->quit) { qemu_mutex_unlock(&p->mutex); break; @@ -1081,6 +1119,7 @@ int multifd_load_setup(void) qemu_mutex_init(&p->mutex); qemu_sem_init(&p->sem, 0); p->quit = false; + p->pending_job = false; p->id = i; p->pages = multifd_pages_init(page_count); p->packet_len = sizeof(MultiFDPacket_t) diff --git a/migration/trace-events b/migration/trace-events index a80aaa8b3f..4aad26feed 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -76,8 +76,10 @@ get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, unsigned migration_bitmap_sync_start(void) "" migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_throttle(void) "" +multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags) "channel %d packet number %" PRIu64 " pages %d flags 0x%x" multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 multifd_recv_thread_start(uint8_t id) "%d" +multifd_send(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x" multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 multifd_send_thread_start(uint8_t id) "%d" ram_discard_range(const char *rbname, uint64_t start, size_t len) "%s: start: %" PRIx64 " %zx" From 6df264ac5a7f532adf718b55d5ef713247d857b1 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 28 Feb 2018 09:10:07 +0100 Subject: [PATCH 1639/2380] migration: Synchronize multifd threads with main thread We synchronize all threads each RAM_SAVE_FLAG_EOS. Bitmap synchronizations don't happen inside a ram section, so we are safe about two channels trying to overwrite the same memory. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert -- seq needs to be atomic now, will also be accessed from main thread. Fix the if (true || ...) leftover We are back to non-atomics --- migration/ram.c | 147 ++++++++++++++++++++++++++++++++--------- migration/trace-events | 6 ++ 2 files changed, 122 insertions(+), 31 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 76410f9de8..77c66a4391 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -510,6 +510,8 @@ exit: #define MULTIFD_MAGIC 0x11223344U #define MULTIFD_VERSION 1 +#define MULTIFD_FLAG_SYNC (1 << 0) + typedef struct { uint32_t magic; uint32_t version; @@ -577,6 +579,8 @@ typedef struct { uint64_t num_packets; /* pages sent through this channel */ uint64_t num_pages; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; } MultiFDSendParams; typedef struct { @@ -614,6 +618,8 @@ typedef struct { uint64_t num_packets; /* pages sent through this channel */ uint64_t num_pages; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; } MultiFDRecvParams; static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) @@ -801,6 +807,10 @@ struct { int count; /* array of pages to sent */ MultiFDPages_t *pages; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; + /* global number of generated multifd packets */ + uint64_t packet_num; } *multifd_send_state; static void multifd_send_terminate_threads(Error *err) @@ -848,6 +858,7 @@ int multifd_save_cleanup(Error **errp) p->c = NULL; qemu_mutex_destroy(&p->mutex); qemu_sem_destroy(&p->sem); + qemu_sem_destroy(&p->sem_sync); g_free(p->name); p->name = NULL; multifd_pages_clear(p->pages); @@ -856,6 +867,7 @@ int multifd_save_cleanup(Error **errp) g_free(p->packet); p->packet = NULL; } + qemu_sem_destroy(&multifd_send_state->sem_sync); g_free(multifd_send_state->params); multifd_send_state->params = NULL; multifd_pages_clear(multifd_send_state->pages); @@ -865,6 +877,33 @@ int multifd_save_cleanup(Error **errp) return ret; } +static void multifd_send_sync_main(void) +{ + int i; + + if (!migrate_use_multifd()) { + return; + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + trace_multifd_send_sync_main_signal(p->id); + + qemu_mutex_lock(&p->mutex); + p->flags |= MULTIFD_FLAG_SYNC; + p->pending_job++; + qemu_mutex_unlock(&p->mutex); + qemu_sem_post(&p->sem); + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + trace_multifd_send_sync_main_wait(p->id); + qemu_sem_wait(&multifd_send_state->sem_sync); + } + trace_multifd_send_sync_main(multifd_send_state->packet_num); +} + static void *multifd_send_thread(void *opaque) { MultiFDSendParams *p = opaque; @@ -901,15 +940,17 @@ static void *multifd_send_thread(void *opaque) qemu_mutex_lock(&p->mutex); p->pending_job--; qemu_mutex_unlock(&p->mutex); - continue; + + if (flags & MULTIFD_FLAG_SYNC) { + qemu_sem_post(&multifd_send_state->sem_sync); + } } else if (p->quit) { qemu_mutex_unlock(&p->mutex); break; + } else { + qemu_mutex_unlock(&p->mutex); + /* sometimes there are spurious wakeups */ } - qemu_mutex_unlock(&p->mutex); - /* this is impossible */ - error_setg(&local_err, "multifd_send_thread: Unknown command"); - break; } out: @@ -961,12 +1002,14 @@ int multifd_save_setup(void) multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); atomic_set(&multifd_send_state->count, 0); multifd_send_state->pages = multifd_pages_init(page_count); + qemu_sem_init(&multifd_send_state->sem_sync, 0); for (i = 0; i < thread_count; i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; qemu_mutex_init(&p->mutex); qemu_sem_init(&p->sem, 0); + qemu_sem_init(&p->sem_sync, 0); p->quit = false; p->pending_job = 0; p->id = i; @@ -984,6 +1027,10 @@ struct { MultiFDRecvParams *params; /* number of created threads */ int count; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; + /* global number of generated multifd packets */ + uint64_t packet_num; } *multifd_recv_state; static void multifd_recv_terminate_threads(Error *err) @@ -1029,6 +1076,7 @@ int multifd_load_cleanup(Error **errp) p->c = NULL; qemu_mutex_destroy(&p->mutex); qemu_sem_destroy(&p->sem); + qemu_sem_destroy(&p->sem_sync); g_free(p->name); p->name = NULL; multifd_pages_clear(p->pages); @@ -1037,6 +1085,7 @@ int multifd_load_cleanup(Error **errp) g_free(p->packet); p->packet = NULL; } + qemu_sem_destroy(&multifd_recv_state->sem_sync); g_free(multifd_recv_state->params); multifd_recv_state->params = NULL; g_free(multifd_recv_state); @@ -1045,6 +1094,42 @@ int multifd_load_cleanup(Error **errp) return ret; } +static void multifd_recv_sync_main(void) +{ + int i; + + if (!migrate_use_multifd()) { + return; + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + trace_multifd_recv_sync_main_signal(p->id); + qemu_mutex_lock(&p->mutex); + p->pending_job = true; + qemu_mutex_unlock(&p->mutex); + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + trace_multifd_recv_sync_main_wait(p->id); + qemu_sem_wait(&multifd_recv_state->sem_sync); + qemu_mutex_lock(&p->mutex); + if (multifd_recv_state->packet_num < p->packet_num) { + multifd_recv_state->packet_num = p->packet_num; + } + qemu_mutex_unlock(&p->mutex); + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + trace_multifd_recv_sync_main_signal(p->id); + + qemu_sem_post(&p->sem_sync); + } + trace_multifd_recv_sync_main(multifd_recv_state->packet_num); +} + static void *multifd_recv_thread(void *opaque) { MultiFDRecvParams *p = opaque; @@ -1054,37 +1139,30 @@ static void *multifd_recv_thread(void *opaque) trace_multifd_recv_thread_start(p->id); while (true) { - qemu_sem_wait(&p->sem); + uint32_t used; + uint32_t flags; + + /* ToDo: recv packet here */ + qemu_mutex_lock(&p->mutex); - if (p->pending_job) { - uint32_t used; - uint32_t flags; - qemu_mutex_unlock(&p->mutex); - - /* ToDo: recv packet here */ - - qemu_mutex_lock(&p->mutex); - ret = multifd_recv_unfill_packet(p, &local_err); - if (ret) { - qemu_mutex_unlock(&p->mutex); - break; - } - - used = p->pages->used; - flags = p->flags; - trace_multifd_recv(p->id, p->packet_num, used, flags); - p->pending_job = false; - p->num_packets++; - p->num_pages += used; - qemu_mutex_unlock(&p->mutex); - } else if (p->quit) { + ret = multifd_recv_unfill_packet(p, &local_err); + if (ret) { qemu_mutex_unlock(&p->mutex); break; } + + used = p->pages->used; + flags = p->flags; + trace_multifd_recv(p->id, p->packet_num, used, flags); + p->pending_job = false; + p->num_packets++; + p->num_pages += used; qemu_mutex_unlock(&p->mutex); - /* this is impossible */ - error_setg(&local_err, "multifd_recv_thread: Unknown command"); - break; + + if (flags & MULTIFD_FLAG_SYNC) { + qemu_sem_post(&multifd_recv_state->sem_sync); + qemu_sem_wait(&p->sem_sync); + } } if (local_err) { @@ -1112,12 +1190,14 @@ int multifd_load_setup(void) multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state)); multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count); atomic_set(&multifd_recv_state->count, 0); + qemu_sem_init(&multifd_recv_state->sem_sync, 0); for (i = 0; i < thread_count; i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; qemu_mutex_init(&p->mutex); qemu_sem_init(&p->sem, 0); + qemu_sem_init(&p->sem_sync, 0); p->quit = false; p->pending_job = false; p->id = i; @@ -2875,6 +2955,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) ram_control_before_iterate(f, RAM_CONTROL_SETUP); ram_control_after_iterate(f, RAM_CONTROL_SETUP); + multifd_send_sync_main(); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); return 0; @@ -2955,6 +3036,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) */ ram_control_after_iterate(f, RAM_CONTROL_ROUND); + multifd_send_sync_main(); out: qemu_put_be64(f, RAM_SAVE_FLAG_EOS); ram_counters.transferred += 8; @@ -3008,6 +3090,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) rcu_read_unlock(); + multifd_send_sync_main(); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); return 0; @@ -3497,6 +3580,7 @@ static int ram_load_postcopy(QEMUFile *f) break; case RAM_SAVE_FLAG_EOS: /* normal exit */ + multifd_recv_sync_main(); break; default: error_report("Unknown combination of migration flags: %#x" @@ -3685,6 +3769,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) break; case RAM_SAVE_FLAG_EOS: /* normal exit */ + multifd_recv_sync_main(); break; default: if (flags & RAM_SAVE_FLAG_HOOK) { diff --git a/migration/trace-events b/migration/trace-events index 4aad26feed..8b9edfbfef 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -77,9 +77,15 @@ migration_bitmap_sync_start(void) "" migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_throttle(void) "" multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags) "channel %d packet number %" PRIu64 " pages %d flags 0x%x" +multifd_recv_sync_main(long packet_num) "packet num %ld" +multifd_recv_sync_main_signal(uint8_t id) "channel %d" +multifd_recv_sync_main_wait(uint8_t id) "channel %d" multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 multifd_recv_thread_start(uint8_t id) "%d" multifd_send(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x" +multifd_send_sync_main(long packet_num) "packet num %ld" +multifd_send_sync_main_signal(uint8_t id) "channel %d" +multifd_send_sync_main_wait(uint8_t id) "channel %d" multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 multifd_send_thread_start(uint8_t id) "%d" ram_discard_range(const char *rbname, uint64_t start, size_t len) "%s: start: %" PRIx64 " %zx" From a61c45bd227951cd792f22b00d4e502c01ae8d9b Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 26 Jun 2018 15:20:11 +0200 Subject: [PATCH 1640/2380] migration: Create multifd_bytes ram_counter This will include how many bytes they are sent through multifd. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- hmp.c | 2 ++ migration/migration.c | 1 + qapi/migration.json | 5 ++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hmp.c b/hmp.c index f601099f90..0da0b0ac33 100644 --- a/hmp.c +++ b/hmp.c @@ -234,6 +234,8 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->ram->dirty_sync_count); monitor_printf(mon, "page size: %" PRIu64 " kbytes\n", info->ram->page_size >> 10); + monitor_printf(mon, "multifd bytes: %" PRIu64 " kbytes\n", + info->ram->multifd_bytes >> 10); if (info->ram->dirty_pages_rate) { monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n", diff --git a/migration/migration.c b/migration/migration.c index 264f3ce84e..2680ba2d47 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -708,6 +708,7 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->ram->dirty_sync_count = ram_counters.dirty_sync_count; info->ram->postcopy_requests = ram_counters.postcopy_requests; info->ram->page_size = qemu_target_page_size(); + info->ram->multifd_bytes = ram_counters.multifd_bytes; if (migrate_use_xbzrle()) { info->has_xbzrle_cache = true; diff --git a/qapi/migration.json b/qapi/migration.json index 1b4c1db670..186e8a7303 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -39,6 +39,8 @@ # @page-size: The number of bytes per page for the various page-based # statistics (since 2.10) # +# @multifd-bytes: The number of bytes sent through multifd (since 3.0) +# # Since: 0.14.0 ## { 'struct': 'MigrationStats', @@ -46,7 +48,8 @@ 'duplicate': 'int', 'skipped': 'int', 'normal': 'int', 'normal-bytes': 'int', 'dirty-pages-rate' : 'int', 'mbps' : 'number', 'dirty-sync-count' : 'int', - 'postcopy-requests' : 'int', 'page-size' : 'int' } } + 'postcopy-requests' : 'int', 'page-size' : 'int', + 'multifd-bytes' : 'uint64' } } ## # @XBZRLECacheStats: From b9ee2f7d701457f205a3329ea67b33e0b63a0f43 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 15 Jan 2016 11:40:13 +0100 Subject: [PATCH 1641/2380] migration: Create ram_save_multifd_page The function still don't use multifd, but we have simplified ram_save_page, xbzrle and RDMA stuff is gone. We have added a new counter. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert -- Add last_page parameter Add commets for done and address Remove multifd field, it is the same than normal pages Merge next patch, now we send multiple pages at a time Remove counter for multifd pages, it is identical to normal pages Use iovec's instead of creating the equivalent. Clear memory used by pages (dave) Use g_new0(danp) define MULTIFD_CONTINUE now pages member is a pointer Fix off-by-one in number of pages in one packet Remove RAM_SAVE_FLAG_MULTIFD_PAGE s/multifd_pages_t/MultiFDPages_t/ add comment explaining what it means --- migration/migration.c | 2 +- migration/ram.c | 107 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index 2680ba2d47..d075c27886 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2708,7 +2708,7 @@ static MigThrError migration_detect_error(MigrationState *s) /* How many bytes have we transferred since the beggining of the migration */ static uint64_t migration_total_bytes(MigrationState *s) { - return qemu_ftell(s->to_dst_file); + return qemu_ftell(s->to_dst_file) + ram_counters.multifd_bytes; } static void migration_calculate_complete(MigrationState *s) diff --git a/migration/ram.c b/migration/ram.c index 77c66a4391..050e2f2000 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -55,6 +55,7 @@ #include "sysemu/sysemu.h" #include "qemu/uuid.h" #include "savevm.h" +#include "qemu/iov.h" /***********************************************************/ /* ram save/restore */ @@ -811,8 +812,87 @@ struct { QemuSemaphore sem_sync; /* global number of generated multifd packets */ uint64_t packet_num; + /* send channels ready */ + QemuSemaphore channels_ready; } *multifd_send_state; +/* + * How we use multifd_send_state->pages and channel->pages? + * + * We create a pages for each channel, and a main one. Each time that + * we need to send a batch of pages we interchange the ones between + * multifd_send_state and the channel that is sending it. There are + * two reasons for that: + * - to not have to do so many mallocs during migration + * - to make easier to know what to free at the end of migration + * + * This way we always know who is the owner of each "pages" struct, + * and we don't need any loocking. It belongs to the migration thread + * or to the channel thread. Switching is safe because the migration + * thread is using the channel mutex when changing it, and the channel + * have to had finish with its own, otherwise pending_job can't be + * false. + */ + +static void multifd_send_pages(void) +{ + int i; + static int next_channel; + MultiFDSendParams *p = NULL; /* make happy gcc */ + MultiFDPages_t *pages = multifd_send_state->pages; + uint64_t transferred; + + qemu_sem_wait(&multifd_send_state->channels_ready); + for (i = next_channel;; i = (i + 1) % migrate_multifd_channels()) { + p = &multifd_send_state->params[i]; + + qemu_mutex_lock(&p->mutex); + if (!p->pending_job) { + p->pending_job++; + next_channel = (i + 1) % migrate_multifd_channels(); + break; + } + qemu_mutex_unlock(&p->mutex); + } + p->pages->used = 0; + + p->packet_num = multifd_send_state->packet_num++; + p->pages->block = NULL; + multifd_send_state->pages = p->pages; + p->pages = pages; + transferred = pages->used * TARGET_PAGE_SIZE + p->packet_len; + ram_counters.multifd_bytes += transferred; + ram_counters.transferred += transferred;; + qemu_mutex_unlock(&p->mutex); + qemu_sem_post(&p->sem); +} + +static void multifd_queue_page(RAMBlock *block, ram_addr_t offset) +{ + MultiFDPages_t *pages = multifd_send_state->pages; + + if (!pages->block) { + pages->block = block; + } + + if (pages->block == block) { + pages->offset[pages->used] = offset; + pages->iov[pages->used].iov_base = block->host + offset; + pages->iov[pages->used].iov_len = TARGET_PAGE_SIZE; + pages->used++; + + if (pages->used < pages->allocated) { + return; + } + } + + multifd_send_pages(); + + if (pages->block != block) { + multifd_queue_page(block, offset); + } +} + static void multifd_send_terminate_threads(Error *err) { int i; @@ -867,6 +947,7 @@ int multifd_save_cleanup(Error **errp) g_free(p->packet); p->packet = NULL; } + qemu_sem_destroy(&multifd_send_state->channels_ready); qemu_sem_destroy(&multifd_send_state->sem_sync); g_free(multifd_send_state->params); multifd_send_state->params = NULL; @@ -884,12 +965,17 @@ static void multifd_send_sync_main(void) if (!migrate_use_multifd()) { return; } + if (multifd_send_state->pages->used) { + multifd_send_pages(); + } for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; trace_multifd_send_sync_main_signal(p->id); qemu_mutex_lock(&p->mutex); + + p->packet_num = multifd_send_state->packet_num++; p->flags |= MULTIFD_FLAG_SYNC; p->pending_job++; qemu_mutex_unlock(&p->mutex); @@ -944,6 +1030,7 @@ static void *multifd_send_thread(void *opaque) if (flags & MULTIFD_FLAG_SYNC) { qemu_sem_post(&multifd_send_state->sem_sync); } + qemu_sem_post(&multifd_send_state->channels_ready); } else if (p->quit) { qemu_mutex_unlock(&p->mutex); break; @@ -1003,6 +1090,7 @@ int multifd_save_setup(void) atomic_set(&multifd_send_state->count, 0); multifd_send_state->pages = multifd_pages_init(page_count); qemu_sem_init(&multifd_send_state->sem_sync, 0); + qemu_sem_init(&multifd_send_state->channels_ready, 0); for (i = 0; i < thread_count; i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; @@ -1724,6 +1812,23 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) return pages; } +static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, + ram_addr_t offset) +{ + uint8_t *p; + + p = block->host + offset; + + ram_counters.transferred += save_page_header(rs, rs->f, block, + offset | RAM_SAVE_FLAG_PAGE); + multifd_queue_page(block, offset); + qemu_put_buffer(rs->f, p, TARGET_PAGE_SIZE); + ram_counters.transferred += TARGET_PAGE_SIZE; + ram_counters.normal++; + + return 1; +} + static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, ram_addr_t offset, uint8_t *source_buf) { @@ -2129,6 +2234,8 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, */ if (block == rs->last_sent_block && save_page_use_compression(rs)) { return compress_page_with_multi_thread(rs, block, offset); + } else if (migrate_use_multifd()) { + return ram_save_multifd_page(rs, block, offset); } return ram_save_page(rs, pss, last_stage); From 8b2db7f5fd9566ab9cf22b02b623c51b2075a60e Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 11 Apr 2018 12:36:13 +0200 Subject: [PATCH 1642/2380] migration: Start sending messages Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/ram.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 050e2f2000..b9c8f65059 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -736,9 +736,6 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) RAMBlock *block; int i; - /* ToDo: We can't use it until we haven't received a message */ - return 0; - be32_to_cpus(&packet->magic); if (packet->magic != MULTIFD_MAGIC) { error_setg(errp, "multifd: received packet " @@ -994,6 +991,7 @@ static void *multifd_send_thread(void *opaque) { MultiFDSendParams *p = opaque; Error *local_err = NULL; + int ret; trace_multifd_send_thread_start(p->id); @@ -1021,7 +1019,16 @@ static void *multifd_send_thread(void *opaque) trace_multifd_send(p->id, packet_num, used, flags); - /* ToDo: send packet here */ + ret = qio_channel_write_all(p->c, (void *)p->packet, + p->packet_len, &local_err); + if (ret != 0) { + break; + } + + ret = qio_channel_writev_all(p->c, p->pages->iov, used, &local_err); + if (ret != 0) { + break; + } qemu_mutex_lock(&p->mutex); p->pending_job--; @@ -1230,7 +1237,14 @@ static void *multifd_recv_thread(void *opaque) uint32_t used; uint32_t flags; - /* ToDo: recv packet here */ + ret = qio_channel_read_all_eof(p->c, (void *)p->packet, + p->packet_len, &local_err); + if (ret == 0) { /* EOF */ + break; + } + if (ret == -1) { /* Error */ + break; + } qemu_mutex_lock(&p->mutex); ret = multifd_recv_unfill_packet(p, &local_err); @@ -1247,6 +1261,11 @@ static void *multifd_recv_thread(void *opaque) p->num_pages += used; qemu_mutex_unlock(&p->mutex); + ret = qio_channel_readv_all(p->c, p->pages->iov, used, &local_err); + if (ret != 0) { + break; + } + if (flags & MULTIFD_FLAG_SYNC) { qemu_sem_post(&multifd_recv_state->sem_sync); qemu_sem_wait(&p->sem_sync); From 4d22c148c989f80e4377233bed886e96d85ef645 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Apr 2018 00:38:17 +0200 Subject: [PATCH 1643/2380] migration: Wait for blocking IO We have three conditions here: - channel fails -> error - we have to quit: we close the channel and reads fails - normal read that success, we are in bussiness So forget the complications of waiting in a semaphore. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/ram.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index b9c8f65059..5d38d699f3 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -602,8 +602,6 @@ typedef struct { bool running; /* should this thread finish */ bool quit; - /* thread has work to do */ - bool pending_job; /* array of pages to receive */ MultiFDPages_t *pages; /* packet allocated len */ @@ -1199,14 +1197,6 @@ static void multifd_recv_sync_main(void) for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; - trace_multifd_recv_sync_main_signal(p->id); - qemu_mutex_lock(&p->mutex); - p->pending_job = true; - qemu_mutex_unlock(&p->mutex); - } - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDRecvParams *p = &multifd_recv_state->params[i]; - trace_multifd_recv_sync_main_wait(p->id); qemu_sem_wait(&multifd_recv_state->sem_sync); qemu_mutex_lock(&p->mutex); @@ -1219,7 +1209,6 @@ static void multifd_recv_sync_main(void) MultiFDRecvParams *p = &multifd_recv_state->params[i]; trace_multifd_recv_sync_main_signal(p->id); - qemu_sem_post(&p->sem_sync); } trace_multifd_recv_sync_main(multifd_recv_state->packet_num); @@ -1256,7 +1245,6 @@ static void *multifd_recv_thread(void *opaque) used = p->pages->used; flags = p->flags; trace_multifd_recv(p->id, p->packet_num, used, flags); - p->pending_job = false; p->num_packets++; p->num_pages += used; qemu_mutex_unlock(&p->mutex); @@ -1306,7 +1294,6 @@ int multifd_load_setup(void) qemu_sem_init(&p->sem, 0); qemu_sem_init(&p->sem_sync, 0); p->quit = false; - p->pending_job = false; p->id = i; p->pages = multifd_pages_init(page_count); p->packet_len = sizeof(MultiFDPacket_t) From 7a5cc33c4878e998e1e2d7c9092dddf963aeb795 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Apr 2018 00:49:19 +0200 Subject: [PATCH 1644/2380] migration: Remove not needed semaphore and quit We know quit with shutdwon in the QIO. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert -- Add comment Use shutdown() instead of unref() --- migration/ram.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 5d38d699f3..61f7313093 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -594,14 +594,10 @@ typedef struct { QemuThread thread; /* communication channel */ QIOChannel *c; - /* sem where to wait for more work */ - QemuSemaphore sem; /* this mutex protects the following parameters */ QemuMutex mutex; /* is this channel thread running */ bool running; - /* should this thread finish */ - bool quit; /* array of pages to receive */ MultiFDPages_t *pages; /* packet allocated len */ @@ -1144,8 +1140,11 @@ static void multifd_recv_terminate_threads(Error *err) MultiFDRecvParams *p = &multifd_recv_state->params[i]; qemu_mutex_lock(&p->mutex); - p->quit = true; - qemu_sem_post(&p->sem); + /* We could arrive here for two reasons: + - normal quit, i.e. everything went fine, just finished + - error quit: We close the channels so the channel threads + finish the qio_channel_read_all_eof() */ + qio_channel_shutdown(p->c, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); qemu_mutex_unlock(&p->mutex); } } @@ -1168,7 +1167,6 @@ int multifd_load_cleanup(Error **errp) object_unref(OBJECT(p->c)); p->c = NULL; qemu_mutex_destroy(&p->mutex); - qemu_sem_destroy(&p->sem); qemu_sem_destroy(&p->sem_sync); g_free(p->name); p->name = NULL; @@ -1291,9 +1289,7 @@ int multifd_load_setup(void) MultiFDRecvParams *p = &multifd_recv_state->params[i]; qemu_mutex_init(&p->mutex); - qemu_sem_init(&p->sem, 0); qemu_sem_init(&p->sem_sync, 0); - p->quit = false; p->id = i; p->pages = multifd_pages_init(page_count); p->packet_len = sizeof(MultiFDPacket_t) From 35374cbdff2bf6b6fb7caae5ea8aaa3b0f53f4ca Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Apr 2018 10:13:21 +0200 Subject: [PATCH 1645/2380] migration: Stop sending whole pages through main channel We have to flush() the QEMUFile because now we sent really few data through that channel. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/ram.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 61f7313093..7d23b472cb 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1817,15 +1817,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) { - uint8_t *p; - - p = block->host + offset; - - ram_counters.transferred += save_page_header(rs, rs->f, block, - offset | RAM_SAVE_FLAG_PAGE); multifd_queue_page(block, offset); - qemu_put_buffer(rs->f, p, TARGET_PAGE_SIZE); - ram_counters.transferred += TARGET_PAGE_SIZE; ram_counters.normal++; return 1; @@ -3066,6 +3058,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) multifd_send_sync_main(); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + qemu_fflush(f); return 0; } @@ -3148,6 +3141,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) multifd_send_sync_main(); out: qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + qemu_fflush(f); ram_counters.transferred += 8; ret = qemu_file_get_error(f); @@ -3201,6 +3195,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) multifd_send_sync_main(); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + qemu_fflush(f); return 0; } From c136180c901dd84d36577edc9b7f377fb76b832d Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 20 Jun 2018 22:27:36 +0200 Subject: [PATCH 1646/2380] postcopy: drop ram_pages parameter from postcopy_ram_incoming_init() Not needed. Don't expose last_ram_page(). Signed-off-by: David Hildenbrand Message-Id: <20180620202736.21399-1-david@redhat.com> Reviewed-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Juan Quintela --- exec.c | 2 +- include/exec/ram_addr.h | 1 - migration/postcopy-ram.c | 4 ++-- migration/postcopy-ram.h | 2 +- migration/ram.c | 4 +--- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/exec.c b/exec.c index 88edb59060..1e37f7586b 100644 --- a/exec.c +++ b/exec.c @@ -1930,7 +1930,7 @@ static ram_addr_t find_ram_offset(ram_addr_t size) return offset; } -unsigned long last_ram_page(void) +static unsigned long last_ram_page(void) { RAMBlock *block; ram_addr_t last = 0; diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index cf2446a176..33c361cad5 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -71,7 +71,6 @@ static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr, } long qemu_getrampagesize(void); -unsigned long last_ram_page(void); RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, bool share, const char *mem_path, Error **errp); diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 48e51556a7..932f188949 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -500,7 +500,7 @@ static int cleanup_range(const char *block_name, void *host_addr, * postcopy later; must be called prior to any precopy. * called from arch_init's similarly named ram_postcopy_incoming_init */ -int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages) +int postcopy_ram_incoming_init(MigrationIncomingState *mis) { if (qemu_ram_foreach_migratable_block(init_range, NULL)) { return -1; @@ -1265,7 +1265,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) return false; } -int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages) +int postcopy_ram_incoming_init(MigrationIncomingState *mis) { error_report("postcopy_ram_incoming_init: No OS support"); return -1; diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h index d900d9c34f..9d55536fd1 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -27,7 +27,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis); * postcopy later; must be called prior to any precopy. * called from ram.c's similarly named ram_postcopy_incoming_init */ -int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages); +int postcopy_ram_incoming_init(MigrationIncomingState *mis); /* * At the end of a migration where postcopy_ram_incoming_init was called. diff --git a/migration/ram.c b/migration/ram.c index 7d23b472cb..1cd98d6398 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3564,9 +3564,7 @@ static int ram_load_cleanup(void *opaque) */ int ram_postcopy_incoming_init(MigrationIncomingState *mis) { - unsigned long ram_pages = last_ram_page(); - - return postcopy_ram_incoming_init(mis, ram_pages); + return postcopy_ram_incoming_init(mis); } /** From ca273df3014930f4ecb6d3ba24aa778354634b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 19 Jun 2018 17:35:52 +0100 Subject: [PATCH 1647/2380] migration: fix crash in when incoming client channel setup fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The way we determine if we can start the incoming migration was changed to use migration_has_all_channels() in: commit 428d89084c709e568f9cd301c2f6416a54c53d6d Author: Juan Quintela Date: Mon Jul 24 13:06:25 2017 +0200 migration: Create migration_has_all_channels This method in turn calls multifd_recv_all_channels_created() which is hardcoded to always return 'true' when multifd is not in use. This is a latent bug... ...activated in a following commit where that return result ends up acting as the flag to indicate whether it is possible to start processing the migration: commit 36c2f8be2c4eb0003ac77a14910842b7ddd7337e Author: Juan Quintela Date: Wed Mar 7 08:40:52 2018 +0100 migration: Delay start of migration main routines This means that if channel initialization fails with normal migration, it'll never notice and attempt to start the incoming migration regardless and crash on a NULL pointer. This can be seen, for example, if a client connects to a server requiring TLS, but has an invalid x509 certificate: qemu-system-x86_64: The certificate hasn't got a known issuer qemu-system-x86_64: migration/migration.c:386: process_incoming_migration_co: Assertion `mis->from_src_file' failed. #0 0x00007fffebd24f2b in raise () at /lib64/libc.so.6 #1 0x00007fffebd0f561 in abort () at /lib64/libc.so.6 #2 0x00007fffebd0f431 in _nl_load_domain.cold.0 () at /lib64/libc.so.6 #3 0x00007fffebd1d692 in () at /lib64/libc.so.6 #4 0x0000555555ad027e in process_incoming_migration_co (opaque=) at migration/migration.c:386 #5 0x0000555555c45e8b in coroutine_trampoline (i0=, i1=) at util/coroutine-ucontext.c:116 #6 0x00007fffebd3a6a0 in __start_context () at /lib64/libc.so.6 #7 0x0000000000000000 in () To handle the non-multifd case, we check whether mis->from_src_file is non-NULL. With this in place, the migration server drops the rejected client and stays around waiting for another, hopefully valid, client to arrive. Signed-off-by: Daniel P. Berrangé Message-Id: <20180619163552.18206-1-berrange@redhat.com> Reviewed-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Juan Quintela --- migration/migration.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index d075c27886..94d71f8b24 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -518,11 +518,12 @@ void migration_ioc_process_incoming(QIOChannel *ioc) */ bool migration_has_all_channels(void) { + MigrationIncomingState *mis = migration_incoming_get_current(); bool all_channels; all_channels = multifd_recv_all_channels_created(); - return all_channels; + return all_channels && mis->from_src_file != NULL; } /* From f18793b096e69c7acfce66cded483ba9fc01762a Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 14 Jun 2018 17:44:31 +0100 Subject: [PATCH 1648/2380] compiler: add a sizeof_field() macro Determining the size of a field is useful when you don't have a struct variable handy. Open-coding this is ugly. This patch adds the sizeof_field() macro, which is similar to typeof_field(). Existing instances are updated to use the macro. Signed-off-by: Stefan Hajnoczi Reviewed-by: John Snow Message-id: 20180614164431.29305-1-stefanha@redhat.com Signed-off-by: Stefan Hajnoczi --- accel/tcg/translate-all.c | 2 +- hw/display/xenfb.c | 4 ++-- hw/net/rocker/rocker_of_dpa.c | 2 +- hw/net/virtio-net.c | 2 +- include/hw/xen/io/ring.h | 2 +- include/qemu/compiler.h | 2 ++ target/i386/kvm.c | 2 +- target/ppc/arch_dump.c | 10 +++++----- target/s390x/arch_dump.c | 20 ++++++++++---------- 9 files changed, 24 insertions(+), 22 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index f0c3fd4d03..ad997cba28 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -191,7 +191,7 @@ struct page_collection { /* Make sure all possible CPU event bits fit in tb->trace_vcpu_dstate */ QEMU_BUILD_BUG_ON(CPU_TRACE_DSTATE_MAX_EVENTS > - sizeof(((TranslationBlock *)0)->trace_vcpu_dstate) + sizeof_field(TranslationBlock, trace_vcpu_dstate) * BITS_PER_BYTE); /* diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index f5afcc0358..911291c5c3 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -525,8 +525,8 @@ static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, int width, int height, int depth, size_t fb_len, int offset, int row_stride) { - size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); - size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; + size_t mfn_sz = sizeof_field(struct xenfb_page, pd[0]); + size_t pd_len = sizeof_field(struct xenfb_page, pd) / mfn_sz; size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; size_t fb_len_max = fb_pages * XC_PAGE_SIZE; int max_width, max_height; diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index 60046720a5..8e347d1ee4 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -104,7 +104,7 @@ typedef struct of_dpa_flow_key { /* Width of key which includes field 'f' in u64s, rounded up */ #define FLOW_KEY_WIDTH(f) \ - DIV_ROUND_UP(offsetof(OfDpaFlowKey, f) + sizeof(((OfDpaFlowKey *)0)->f), \ + DIV_ROUND_UP(offsetof(OfDpaFlowKey, f) + sizeof_field(OfDpaFlowKey, f), \ sizeof(uint64_t)) typedef struct of_dpa_flow_action { diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 90502fca7c..f154756e85 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -46,7 +46,7 @@ * 'container'. */ #define endof(container, field) \ - (offsetof(container, field) + sizeof(((container *)0)->field)) + (offsetof(container, field) + sizeof_field(container, field)) typedef struct VirtIOFeature { uint64_t flags; diff --git a/include/hw/xen/io/ring.h b/include/hw/xen/io/ring.h index abbca47687..ffa3ebadc8 100644 --- a/include/hw/xen/io/ring.h +++ b/include/hw/xen/io/ring.h @@ -65,7 +65,7 @@ typedef unsigned int RING_IDX; */ #define __CONST_RING_SIZE(_s, _sz) \ (__RD32(((_sz) - offsetof(struct _s##_sring, ring)) / \ - sizeof(((struct _s##_sring *)0)->ring[0]))) + sizeof_field(struct _s##_sring, ring[0]))) /* * The same for passing in an actual pointer instead of a name tag. */ diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 9f762695d1..5843812710 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -64,6 +64,8 @@ (type *) ((char *) __mptr - offsetof(type, member));}) #endif +#define sizeof_field(type, field) sizeof(((type *)0)->field) + /* Convert from a base type to a parent type, with compile time checking. */ #ifdef __GNUC__ #define DO_UPCAST(type, field, dev) ( __extension__ ( { \ diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 2d174f3a91..5116a8a956 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -1533,7 +1533,7 @@ static int kvm_put_fpu(X86CPU *cpu) #define XSAVE_PKRU 672 #define XSAVE_BYTE_OFFSET(word_offset) \ - ((word_offset) * sizeof(((struct kvm_xsave *)0)->region[0])) + ((word_offset) * sizeof_field(struct kvm_xsave, region[0])) #define ASSERT_OFFSET(word_offset, field) \ QEMU_BUILD_BUG_ON(XSAVE_BYTE_OFFSET(word_offset) != \ diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index 351a65b22f..cc1460e4e3 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -210,11 +210,11 @@ static const struct NoteFuncDescStruct { int contents_size; void (*note_contents_func)(NoteFuncArg *arg, PowerPCCPU *cpu); } note_func[] = { - {sizeof(((Note *)0)->contents.prstatus), ppc_write_elf_prstatus}, - {sizeof(((Note *)0)->contents.fpregset), ppc_write_elf_fpregset}, - {sizeof(((Note *)0)->contents.vmxregset), ppc_write_elf_vmxregset}, - {sizeof(((Note *)0)->contents.vsxregset), ppc_write_elf_vsxregset}, - {sizeof(((Note *)0)->contents.speregset), ppc_write_elf_speregset}, + {sizeof_field(Note, contents.prstatus), ppc_write_elf_prstatus}, + {sizeof_field(Note, contents.fpregset), ppc_write_elf_fpregset}, + {sizeof_field(Note, contents.vmxregset), ppc_write_elf_vmxregset}, + {sizeof_field(Note, contents.vsxregset), ppc_write_elf_vsxregset}, + {sizeof_field(Note, contents.speregset), ppc_write_elf_speregset}, { 0, NULL} }; diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c index 6f61ff95af..c9ef0a6e60 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -184,20 +184,20 @@ typedef struct NoteFuncDescStruct { } NoteFuncDesc; static const NoteFuncDesc note_core[] = { - {sizeof(((Note *)0)->contents.prstatus), s390x_write_elf64_prstatus}, - {sizeof(((Note *)0)->contents.fpregset), s390x_write_elf64_fpregset}, + {sizeof_field(Note, contents.prstatus), s390x_write_elf64_prstatus}, + {sizeof_field(Note, contents.fpregset), s390x_write_elf64_fpregset}, { 0, NULL} }; static const NoteFuncDesc note_linux[] = { - {sizeof(((Note *)0)->contents.prefix), s390x_write_elf64_prefix}, - {sizeof(((Note *)0)->contents.ctrs), s390x_write_elf64_ctrs}, - {sizeof(((Note *)0)->contents.timer), s390x_write_elf64_timer}, - {sizeof(((Note *)0)->contents.todcmp), s390x_write_elf64_todcmp}, - {sizeof(((Note *)0)->contents.todpreg), s390x_write_elf64_todpreg}, - {sizeof(((Note *)0)->contents.vregslo), s390x_write_elf64_vregslo}, - {sizeof(((Note *)0)->contents.vregshi), s390x_write_elf64_vregshi}, - {sizeof(((Note *)0)->contents.gscb), s390x_write_elf64_gscb}, + {sizeof_field(Note, contents.prefix), s390x_write_elf64_prefix}, + {sizeof_field(Note, contents.ctrs), s390x_write_elf64_ctrs}, + {sizeof_field(Note, contents.timer), s390x_write_elf64_timer}, + {sizeof_field(Note, contents.todcmp), s390x_write_elf64_todcmp}, + {sizeof_field(Note, contents.todpreg), s390x_write_elf64_todpreg}, + {sizeof_field(Note, contents.vregslo), s390x_write_elf64_vregslo}, + {sizeof_field(Note, contents.vregshi), s390x_write_elf64_vregshi}, + {sizeof_field(Note, contents.gscb), s390x_write_elf64_gscb}, { 0, NULL} }; From ed6e2161715c527330f936d44af4c547f25f687e Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Fri, 22 Jun 2018 12:37:00 -0700 Subject: [PATCH 1649/2380] linux-aio: properly bubble up errors from initialization laio_init() can fail for a couple of reasons, which will lead to a NULL pointer dereference in laio_attach_aio_context(). To solve this, add a aio_setup_linux_aio() function which is called early in raw_open_common. If this fails, propagate the error up. The signature of aio_get_linux_aio() was not modified, because it seems preferable to return the actual errno from the possible failing initialization calls. Additionally, when the AioContext changes, we need to associate a LinuxAioState with the new AioContext. Use the bdrv_attach_aio_context callback and call the new aio_setup_linux_aio(), which will allocate a new AioContext if needed, and return errors on failures. If it fails for any reason, fallback to threaded AIO with an error message, as the device is already in-use by the guest. Add an assert that aio_get_linux_aio() cannot return NULL. Signed-off-by: Nishanth Aravamudan Message-id: 20180622193700.6523-1-naravamudan@digitalocean.com Signed-off-by: Stefan Hajnoczi --- block/file-posix.c | 33 ++++++++++++++++++++++++++++----- block/linux-aio.c | 12 +++++++++--- include/block/aio.h | 3 +++ include/block/raw-aio.h | 2 +- stubs/linux-aio.c | 2 +- util/async.c | 14 +++++++++++--- 6 files changed, 53 insertions(+), 13 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 07bb061fe4..43b963b13e 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -545,11 +545,17 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, #ifdef CONFIG_LINUX_AIO /* Currently Linux does AIO only for files opened with O_DIRECT */ - if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) { - error_setg(errp, "aio=native was specified, but it requires " - "cache.direct=on, which was not specified."); - ret = -EINVAL; - goto fail; + if (s->use_linux_aio) { + if (!(s->open_flags & O_DIRECT)) { + error_setg(errp, "aio=native was specified, but it requires " + "cache.direct=on, which was not specified."); + ret = -EINVAL; + goto fail; + } + if (!aio_setup_linux_aio(bdrv_get_aio_context(bs), errp)) { + error_prepend(errp, "Unable to use native AIO: "); + goto fail; + } } #else if (s->use_linux_aio) { @@ -1723,6 +1729,22 @@ static BlockAIOCB *raw_aio_flush(BlockDriverState *bs, return paio_submit(bs, s->fd, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH); } +static void raw_aio_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ +#ifdef CONFIG_LINUX_AIO + BDRVRawState *s = bs->opaque; + if (s->use_linux_aio) { + Error *local_err; + if (!aio_setup_linux_aio(new_context, &local_err)) { + error_reportf_err(local_err, "Unable to use native AIO, " + "falling back to thread pool: "); + s->use_linux_aio = false; + } + } +#endif +} + static void raw_close(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; @@ -2601,6 +2623,7 @@ BlockDriver bdrv_file = { .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, .bdrv_truncate = raw_truncate, .bdrv_getlength = raw_getlength, diff --git a/block/linux-aio.c b/block/linux-aio.c index 88b8d55ec7..19eb922fdd 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -15,6 +15,7 @@ #include "block/raw-aio.h" #include "qemu/event_notifier.h" #include "qemu/coroutine.h" +#include "qapi/error.h" #include @@ -470,16 +471,21 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) qemu_laio_poll_cb); } -LinuxAioState *laio_init(void) +LinuxAioState *laio_init(Error **errp) { + int rc; LinuxAioState *s; s = g_malloc0(sizeof(*s)); - if (event_notifier_init(&s->e, false) < 0) { + rc = event_notifier_init(&s->e, false); + if (rc < 0) { + error_setg_errno(errp, -rc, "failed to to initialize event notifier"); goto out_free_state; } - if (io_setup(MAX_EVENTS, &s->ctx) != 0) { + rc = io_setup(MAX_EVENTS, &s->ctx); + if (rc < 0) { + error_setg_errno(errp, -rc, "failed to create linux AIO context"); goto out_close_efd; } diff --git a/include/block/aio.h b/include/block/aio.h index ae6f354e6c..f08630c6e5 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -381,6 +381,9 @@ GSource *aio_get_g_source(AioContext *ctx); /* Return the ThreadPool bound to this AioContext */ struct ThreadPool *aio_get_thread_pool(AioContext *ctx); +/* Setup the LinuxAioState bound to this AioContext */ +struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp); + /* Return the LinuxAioState bound to this AioContext */ struct LinuxAioState *aio_get_linux_aio(AioContext *ctx); diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index 0e717fd475..8d698ccd31 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -43,7 +43,7 @@ /* linux-aio.c - Linux native implementation */ #ifdef CONFIG_LINUX_AIO typedef struct LinuxAioState LinuxAioState; -LinuxAioState *laio_init(void); +LinuxAioState *laio_init(Error **errp); void laio_cleanup(LinuxAioState *s); int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd, uint64_t offset, QEMUIOVector *qiov, int type); diff --git a/stubs/linux-aio.c b/stubs/linux-aio.c index ed47bd443c..84d1f784ae 100644 --- a/stubs/linux-aio.c +++ b/stubs/linux-aio.c @@ -21,7 +21,7 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) abort(); } -LinuxAioState *laio_init(void) +LinuxAioState *laio_init(Error **errp) { abort(); } diff --git a/util/async.c b/util/async.c index 03f62787f2..05979f8014 100644 --- a/util/async.c +++ b/util/async.c @@ -323,14 +323,22 @@ ThreadPool *aio_get_thread_pool(AioContext *ctx) } #ifdef CONFIG_LINUX_AIO -LinuxAioState *aio_get_linux_aio(AioContext *ctx) +LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp) { if (!ctx->linux_aio) { - ctx->linux_aio = laio_init(); - laio_attach_aio_context(ctx->linux_aio, ctx); + ctx->linux_aio = laio_init(errp); + if (ctx->linux_aio) { + laio_attach_aio_context(ctx->linux_aio, ctx); + } } return ctx->linux_aio; } + +LinuxAioState *aio_get_linux_aio(AioContext *ctx) +{ + assert(ctx->linux_aio); + return ctx->linux_aio; +} #endif void aio_notify(AioContext *ctx) From c92023bfd18c968d615b715522467f7354db5877 Mon Sep 17 00:00:00 2001 From: Aleksandar Markovic Date: Mon, 18 Jun 2018 20:29:40 +0200 Subject: [PATCH 1650/2380] MAINTAINERS: update target-mips maintainers Yongbok Kim transfers duties of QEMU for target MIPS maintainer to myself as he leaves MIPS. Many thanks to Yongbok for his substantial contributing to QEMU for MIPS over many years and taking care of its maintainance for almost two years. Signed-off-by: Aleksandar Markovic Acked-by: Yongbok Kim Reviewed-by: Aleksandar Markovic --- MAINTAINERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8c626f6a07..42a1892d6a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -187,7 +187,7 @@ F: disas/microblaze.c MIPS M: Aurelien Jarno -M: Yongbok Kim +M: Aleksandar Markovic S: Maintained F: target/mips/ F: hw/mips/ @@ -718,7 +718,7 @@ S: Maintained F: hw/mips/mips_malta.c Mipssim -M: Yongbok Kim +M: Aleksandar Markovic S: Odd Fixes F: hw/mips/mips_mipssim.c F: hw/net/mipsnet.c @@ -729,7 +729,7 @@ S: Maintained F: hw/mips/mips_r4k.c Fulong 2E -M: Yongbok Kim +M: Aleksandar Markovic S: Odd Fixes F: hw/mips/mips_fulong2e.c F: hw/isa/vt82c686.c From 9581eeebe3ffd37f7fde270f92c96a2c5ebb860c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 4 Jun 2018 12:03:57 +0100 Subject: [PATCH 1651/2380] hw/mips/boston: don't make flash region 'nomigrate' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we use memory_region_init_rom_nomigrate() to create the "boston.flash" memory region, and we don't manually register it with vmstate_register_ram(). This currently means that its contents are migrated but as a ram block whose name is the empty string; in future it may mean they are not migrated at all. Use memory_region_init_ram() instead. Note that this is a a cross-version migration compatibility break for the "boston" machine. Signed-off-by: Peter Maydell Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Paul Burton Signed-off-by: Aleksandar Markovic --- hw/mips/boston.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 52cce19766..14e6f955d2 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -471,8 +471,7 @@ static void boston_mach_init(MachineState *machine) sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1); flash = g_new(MemoryRegion, 1); - memory_region_init_rom_nomigrate(flash, NULL, - "boston.flash", 128 * M_BYTE, &err); + memory_region_init_rom(flash, NULL, "boston.flash", 128 * M_BYTE, &err); memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0); ddr = g_new(MemoryRegion, 1); From 917b77f5e5a713c217ecc962fd8c491aa9b586aa Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 4 Jun 2018 12:03:58 +0100 Subject: [PATCH 1652/2380] hw/mips/mips_malta: don't make bios region 'nomigrate' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we use memory_region_init_rom_nomigrate() to create the "bios.1fc" memory region, and we don't manually register it with vmstate_register_ram(). This currently means that its contents are migrated but as a ram block whose name is the empty string; in future it may mean they are not migrated at all. Use memory_region_init_ram() instead. Note that this is a a cross-version migration compatibility break for the "malta" machine. Signed-off-by: Peter Maydell Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Paul Burton Signed-off-by: Aleksandar Markovic --- hw/mips/mips_malta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 494f84e290..b9d92bf47e 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1152,7 +1152,7 @@ void mips_malta_init(MachineState *machine) * handled by an overlapping region as the resulting ROM code subpage * regions are not executable. */ - memory_region_init_ram_nomigrate(bios_copy, NULL, "bios.1fc", BIOS_SIZE, + memory_region_init_ram(bios_copy, NULL, "bios.1fc", BIOS_SIZE, &error_fatal); if (!rom_copy(memory_region_get_ram_ptr(bios_copy), FLASH_ADDRESS, BIOS_SIZE)) { From 4f91740698ced512fdad8540eb0bd232fc70aadd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 19 Jun 2018 13:07:30 +0100 Subject: [PATCH 1653/2380] hw/pci-host/xilinx-pcie: don't make "io" region be RAM Currently we use memory_region_init_rom_nomigrate() to create the "io" memory region to pass to pci_register_root_bus(). This is a dummy region, because this PCI controller doesn't support accesses to PCI IO space. There is no reason for the dummy region to be a RAM region; it is only used as a place where PCI BARs can be mapped, and if you could get a PCI card to do a bus master access to the IO space it should not get acts-like-RAM behaviour. Use a simple container memory region instead. (We do have one PCI card model which can do bus master accesses to IO space -- the LSI53C895A SCSI adaptor.) This avoids the oddity of having a memory region which is RAM but where the RAM is not migrated. Note that the size of the region we use here has no effect on behaviour. Signed-off-by: Peter Maydell Reviewed-by: Alistair Francis Signed-off-by: Aleksandar Markovic --- hw/pci-host/xilinx-pcie.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 044e312dc1..b0a31b917d 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -120,9 +120,8 @@ static void xilinx_pcie_host_realize(DeviceState *dev, Error **errp) memory_region_init(&s->mmio, OBJECT(s), "mmio", UINT64_MAX); memory_region_set_enabled(&s->mmio, false); - /* dummy I/O region */ - memory_region_init_ram_nomigrate(&s->io, OBJECT(s), "io", 16, NULL); - memory_region_set_enabled(&s->io, false); + /* dummy PCI I/O region (not visible to the CPU) */ + memory_region_init(&s->io, OBJECT(s), "io", 16); /* interrupt out */ qdev_init_gpio_out_named(dev, &s->irq, "interrupt_out", 1); From f48a2cb21824217a61ec7be797860a0702e5325c Mon Sep 17 00:00:00 2001 From: Yongbok Kim Date: Wed, 20 Jun 2018 13:05:46 +0100 Subject: [PATCH 1654/2380] target/mips: Raise a RI when given fs is n/a from CTC1 Fix to raise a Reserved Instruction exception when given fs is not available from CTC1. Signed-off-by: Yongbok Kim Reviewed-by: Aleksandar Markovic Signed-off-by: Aleksandar Markovic --- target/mips/op_helper.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c index 9025f42366..41d3634289 100644 --- a/target/mips/op_helper.c +++ b/target/mips/op_helper.c @@ -2627,6 +2627,9 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) (env->active_fpu.fcr31 & ~(env->active_fpu.fcr31_rw_bitmask)); break; default: + if (env->insn_flags & ISA_MIPS32R6) { + do_raise_exception(env, EXCP_RI, GETPC()); + } return; } restore_fp_status(env); From 0305d194be1dbda09fa7a7c883894030d07c355f Mon Sep 17 00:00:00 2001 From: Yongbok Kim Date: Wed, 20 Jun 2018 13:05:47 +0100 Subject: [PATCH 1655/2380] target/mips: Fix microMIPS on reset Fix to activate microMIPS on reset when Config3.ISA == {1, 3} Signed-off-by: Yongbok Kim Reviewed-by: Aleksandar Markovic Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index e57d71e485..2eb211ae8c 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -20713,6 +20713,11 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_Status |= (1 << CP0St_FR); } + if (env->CP0_Config3 & (1 << CP0C3_ISA)) { + /* microMIPS on reset when Config3.ISA == {1, 3} */ + env->hflags |= MIPS_HFLAG_M16; + } + /* MSA */ if (env->CP0_Config3 & (1 << CP0C3_MSAP)) { msa_reset(env); From b52d3bfa2d54d99ef25f6d008815eecc53b67bfe Mon Sep 17 00:00:00 2001 From: Yongbok Kim Date: Wed, 20 Jun 2018 13:05:58 +0100 Subject: [PATCH 1656/2380] target/mips: Update gen_flt_ldst() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update gen_flt_ldst() in order to reuse the functions for nanoMIPS Signed-off-by: Yongbok Kim Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Aleksandar Markovic Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/target/mips/translate.c b/target/mips/translate.c index 2eb211ae8c..e923d2739b 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -2433,11 +2433,8 @@ static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, /* Load and store */ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, - int base, int16_t offset) + TCGv t0) { - TCGv t0 = tcg_temp_new(); - - gen_base_offset_addr(ctx, t0, base, offset); /* Don't do NOP if destination is zero: we must perform the actual memory access. */ switch (opc) { @@ -2480,15 +2477,15 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, default: MIPS_INVAL("flt_ldst"); generate_exception_end(ctx, EXCP_RI); - goto out; + break; } - out: - tcg_temp_free(t0); } static void gen_cop1_ldst(DisasContext *ctx, uint32_t op, int rt, int rs, int16_t imm) { + TCGv t0 = tcg_temp_new(); + if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { check_cp1_enabled(ctx); switch (op) { @@ -2497,11 +2494,13 @@ static void gen_cop1_ldst(DisasContext *ctx, uint32_t op, int rt, check_insn(ctx, ISA_MIPS2); /* Fallthrough */ default: - gen_flt_ldst(ctx, op, rt, rs, imm); + gen_base_offset_addr(ctx, t0, rs, imm); + gen_flt_ldst(ctx, op, rt, t0); } } else { generate_exception_err(ctx, EXCP_CpU, 1); } + tcg_temp_free(t0); } /* Arithmetic with immediate operand */ From 72e1f16f18fe62504f8f25d7a3f6813b24b221be Mon Sep 17 00:00:00 2001 From: Yongbok Kim Date: Wed, 20 Jun 2018 13:06:07 +0100 Subject: [PATCH 1657/2380] target/mips: Fix data type for offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Offset can be larger than 16 bit from nanoMIPS, and immediate field can be larger than 16 bits as well. Signed-off-by: Yongbok Kim Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Aleksandar Markovic Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/mips/translate.c b/target/mips/translate.c index e923d2739b..20b43c0337 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -2112,7 +2112,7 @@ OP_ST_ATOMIC(scd,st64,ld64,0x7); #undef OP_ST_ATOMIC static void gen_base_offset_addr (DisasContext *ctx, TCGv addr, - int base, int16_t offset) + int base, int offset) { if (base == 0) { tcg_gen_movi_tl(addr, offset); @@ -2140,7 +2140,7 @@ static target_ulong pc_relative_pc (DisasContext *ctx) /* Load */ static void gen_ld(DisasContext *ctx, uint32_t opc, - int rt, int base, int16_t offset) + int rt, int base, int offset) { TCGv t0, t1, t2; int mem_idx = ctx->mem_idx; @@ -2337,7 +2337,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, /* Store */ static void gen_st (DisasContext *ctx, uint32_t opc, int rt, - int base, int16_t offset) + int base, int offset) { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); @@ -2505,7 +2505,7 @@ static void gen_cop1_ldst(DisasContext *ctx, uint32_t op, int rt, /* Arithmetic with immediate operand */ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, - int rt, int rs, int16_t imm) + int rt, int rs, int imm) { target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */ From 8e0b373f8aa4b9feec7b44029455587e2e3d2b0f Mon Sep 17 00:00:00 2001 From: Yongbok Kim Date: Wed, 20 Jun 2018 13:06:18 +0100 Subject: [PATCH 1658/2380] target/mips: Fix gdbstub to read/write 64 bit FP registers Fix gdbstub to read/write 64 bit FP registers Signed-off-by: Yongbok Kim Reviewed-by: Aleksandar Markovic Signed-off-by: Aleksandar Markovic --- target/mips/gdbstub.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c index 6d1fb70f2c..18e0e6dce4 100644 --- a/target/mips/gdbstub.c +++ b/target/mips/gdbstub.c @@ -39,7 +39,7 @@ int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return gdb_get_regl(mem_buf, (int32_t)env->active_fpu.fcr0); default: if (env->CP0_Status & (1 << CP0St_FR)) { - return gdb_get_regl(mem_buf, + return gdb_get_reg64(mem_buf, env->active_fpu.fpr[n - 38].d); } else { return gdb_get_regl(mem_buf, @@ -100,6 +100,7 @@ int mips_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) break; default: if (env->CP0_Status & (1 << CP0St_FR)) { + uint64_t tmp = ldq_p(mem_buf); env->active_fpu.fpr[n - 38].d = tmp; } else { env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX] = tmp; From 5d9c9ea22ab4f3b3ee497523e34b6f4d3281f62d Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Wed, 27 Jun 2018 16:55:20 +0530 Subject: [PATCH 1659/2380] virtio-rng: process pending requests on DRIVER_OK virtio-rng device causes old guest kernels(2.6.32) to hang on latest qemu. The driver attempts to read from the virtio-rng device too early in it's initialization. Qemu detects guest is not ready and returns, resulting in hang. To fix handle pending requests when guest is running and driver status is set to 'VIRTIO_CONFIG_S_DRIVER_OK'. CC: qemu-stable@nongnu.org Reported-by: Sergio lopez Signed-off-by: Stefan Hajnoczi Signed-off-by: Pankaj Gupta Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-rng.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 289bbcac03..855f1b41d1 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -156,6 +156,19 @@ static void check_rate_limit(void *opaque) vrng->activate_timer = true; } +static void virtio_rng_set_status(VirtIODevice *vdev, uint8_t status) +{ + VirtIORNG *vrng = VIRTIO_RNG(vdev); + + if (!vdev->vm_running) { + return; + } + vdev->status = status; + + /* Something changed, try to process buffers */ + virtio_rng_process(vrng); +} + static void virtio_rng_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -261,6 +274,7 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) vdc->realize = virtio_rng_device_realize; vdc->unrealize = virtio_rng_device_unrealize; vdc->get_features = get_features; + vdc->set_status = virtio_rng_set_status; } static const TypeInfo virtio_rng_info = { From 9fc53a10f81d3a9027b23fa810147d21be29e614 Mon Sep 17 00:00:00 2001 From: linzhecheng Date: Tue, 29 May 2018 10:52:40 +0800 Subject: [PATCH 1660/2380] socket: don't free msgfds if error equals EAGAIN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we see EAGAIN, no data was sent over the socket, so we still have to retry sending of msgfds next time. Signed-off-by: linzhecheng Signed-off-by: Daniel P. Berrangé --- chardev/char-socket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 159e69c3b1..17519ec589 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -134,8 +134,8 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len) s->write_msgfds, s->write_msgfds_num); - /* free the written msgfds, no matter what */ - if (s->write_msgfds_num) { + /* free the written msgfds in any cases other than errno==EAGAIN */ + if (EAGAIN != errno && s->write_msgfds_num) { g_free(s->write_msgfds); s->write_msgfds = 0; s->write_msgfds_num = 0; From d66f78e1eaa832f73c771d9df1b606fe75d52a50 Mon Sep 17 00:00:00 2001 From: Pavel Balaev Date: Mon, 21 May 2018 19:17:35 +0300 Subject: [PATCH 1661/2380] Delete AF_UNIX socket after close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since version 2.12.0 AF_UNIX socket created for QMP exchange is not deleted on instance shutdown. This is due to the fact that function qio_channel_socket_finalize() is called after qio_channel_socket_close(). Signed-off-by: Pavel Balaev Signed-off-by: Daniel P. Berrangé --- io/channel-socket.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/io/channel-socket.c b/io/channel-socket.c index 57cfb4d3a6..b50e63a053 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -685,8 +685,10 @@ qio_channel_socket_close(QIOChannel *ioc, Error **errp) { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + int rc = 0; if (sioc->fd != -1) { + SocketAddress *addr = socket_local_address(sioc->fd, errp); #ifdef WIN32 WSAEventSelect(sioc->fd, NULL, 0); #endif @@ -697,8 +699,22 @@ qio_channel_socket_close(QIOChannel *ioc, return -1; } sioc->fd = -1; + + if (addr && addr->type == SOCKET_ADDRESS_TYPE_UNIX + && addr->u.q_unix.path) { + if (unlink(addr->u.q_unix.path) < 0 && errno != ENOENT) { + error_setg_errno(errp, errno, + "Failed to unlink socket %s", + addr->u.q_unix.path); + rc = -1; + } + } + + if (addr) { + qapi_free_SocketAddress(addr); + } } - return 0; + return rc; } static int From a99761d3c85679da380c0f597468acd3dc1b53b3 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Wed, 13 Jun 2018 15:19:06 +0200 Subject: [PATCH 1662/2380] exec: Fix MAP_RAM for cached access When an IOMMUMemoryRegion is in front of a virtio device, address_space_cache_init does not set cache->ptr as the memory region is not RAM. However when the device performs an access, we end up in glue() which performs the translation and then uses MAP_RAM. This latter uses the unset ptr and returns a wrong value which leads to a SIGSEV in address_space_lduw_internal_cached_slow, for instance. In slow path cache->ptr is NULL and MAP_RAM must redirect to qemu_map_ram_ptr((mr)->ram_block, ofs). As MAP_RAM, IS_DIRECT and INVALIDATE are the same in _cached_slow and non cached mode, let's remove those macros. This fixes the use cases featuring vIOMMU (Intel and ARM SMMU) which lead to a SIGSEV. Fixes: 48564041a73a (exec: reintroduce MemoryRegion caching) Signed-off-by: Eric Auger Message-Id: <1528895946-28677-1-git-send-email-eric.auger@redhat.com> Signed-off-by: Paolo Bonzini --- exec.c | 6 ------ memory_ldst.inc.c | 47 ++++++++++++++++++++++------------------------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/exec.c b/exec.c index 1e37f7586b..9f35e34ad2 100644 --- a/exec.c +++ b/exec.c @@ -3702,9 +3702,6 @@ void cpu_physical_memory_unmap(void *buffer, hwaddr len, #define ARG1 as #define SUFFIX #define TRANSLATE(...) address_space_translate(as, __VA_ARGS__) -#define IS_DIRECT(mr, is_write) memory_access_is_direct(mr, is_write) -#define MAP_RAM(mr, ofs) qemu_map_ram_ptr((mr)->ram_block, ofs) -#define INVALIDATE(mr, ofs, len) invalidate_and_set_dirty(mr, ofs, len) #define RCU_READ_LOCK(...) rcu_read_lock() #define RCU_READ_UNLOCK(...) rcu_read_unlock() #include "memory_ldst.inc.c" @@ -3841,9 +3838,6 @@ address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, #define ARG1 cache #define SUFFIX _cached_slow #define TRANSLATE(...) address_space_translate_cached(cache, __VA_ARGS__) -#define IS_DIRECT(mr, is_write) memory_access_is_direct(mr, is_write) -#define MAP_RAM(mr, ofs) (cache->ptr + (ofs - cache->xlat)) -#define INVALIDATE(mr, ofs, len) invalidate_and_set_dirty(mr, ofs, len) #define RCU_READ_LOCK() ((void)0) #define RCU_READ_UNLOCK() ((void)0) #include "memory_ldst.inc.c" diff --git a/memory_ldst.inc.c b/memory_ldst.inc.c index 15483987fe..acf865b900 100644 --- a/memory_ldst.inc.c +++ b/memory_ldst.inc.c @@ -34,7 +34,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (l < 4 || !IS_DIRECT(mr, false)) { + if (l < 4 || !memory_access_is_direct(mr, false)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -50,7 +50,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, #endif } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: val = ldl_le_p(ptr); @@ -110,7 +110,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (l < 8 || !IS_DIRECT(mr, false)) { + if (l < 8 || !memory_access_is_direct(mr, false)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -126,7 +126,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, #endif } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: val = ldq_le_p(ptr); @@ -184,14 +184,14 @@ uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (!IS_DIRECT(mr, false)) { + if (!memory_access_is_direct(mr, false)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ r = memory_region_dispatch_read(mr, addr1, &val, 1, attrs); } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); val = ldub_p(ptr); r = MEMTX_OK; } @@ -220,7 +220,7 @@ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (l < 2 || !IS_DIRECT(mr, false)) { + if (l < 2 || !memory_access_is_direct(mr, false)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -236,7 +236,7 @@ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, #endif } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: val = lduw_le_p(ptr); @@ -297,12 +297,12 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 4 || !IS_DIRECT(mr, true)) { + if (l < 4 || !memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, 4, attrs); } else { - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); stl_p(ptr, val); dirty_log_mask = memory_region_get_dirty_log_mask(mr); @@ -334,7 +334,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 4 || !IS_DIRECT(mr, true)) { + if (l < 4 || !memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); #if defined(TARGET_WORDS_BIGENDIAN) @@ -349,7 +349,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, r = memory_region_dispatch_write(mr, addr1, val, 4, attrs); } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: stl_le_p(ptr, val); @@ -361,7 +361,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, stl_p(ptr, val); break; } - INVALIDATE(mr, addr1, 4); + invalidate_and_set_dirty(mr, addr1, 4); r = MEMTX_OK; } if (result) { @@ -406,14 +406,14 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (!IS_DIRECT(mr, true)) { + if (!memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, 1, attrs); } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); stb_p(ptr, val); - INVALIDATE(mr, addr1, 1); + invalidate_and_set_dirty(mr, addr1, 1); r = MEMTX_OK; } if (result) { @@ -439,7 +439,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 2 || !IS_DIRECT(mr, true)) { + if (l < 2 || !memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); #if defined(TARGET_WORDS_BIGENDIAN) @@ -454,7 +454,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, r = memory_region_dispatch_write(mr, addr1, val, 2, attrs); } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: stw_le_p(ptr, val); @@ -466,7 +466,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, stw_p(ptr, val); break; } - INVALIDATE(mr, addr1, 2); + invalidate_and_set_dirty(mr, addr1, 2); r = MEMTX_OK; } if (result) { @@ -512,7 +512,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 8 || !IS_DIRECT(mr, true)) { + if (l < 8 || !memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); #if defined(TARGET_WORDS_BIGENDIAN) @@ -527,7 +527,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, r = memory_region_dispatch_write(mr, addr1, val, 8, attrs); } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: stq_le_p(ptr, val); @@ -539,7 +539,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, stq_p(ptr, val); break; } - INVALIDATE(mr, addr1, 8); + invalidate_and_set_dirty(mr, addr1, 8); r = MEMTX_OK; } if (result) { @@ -576,8 +576,5 @@ void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, #undef ARG1 #undef SUFFIX #undef TRANSLATE -#undef IS_DIRECT -#undef MAP_RAM -#undef INVALIDATE #undef RCU_READ_LOCK #undef RCU_READ_UNLOCK From 8bca9a03ec60d63b2ee6a959fe85dda4206811e0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 30 May 2018 11:58:36 +0200 Subject: [PATCH 1663/2380] move public invalidate APIs out of translate-all.{c,h}, clean up Place them in exec.c, exec-all.h and ram_addr.h. This removes knowledge of translate-all.h (which is an internal header) from several files outside accel/tcg and removes knowledge of AddressSpace from translate-all.c (as it only operates on ram_addr_t). Signed-off-by: Paolo Bonzini --- accel/tcg/translate-all.c | 28 ++++++---------------------- accel/tcg/translate-all.h | 1 - exec.c | 29 +++++++++++++++++++++++++---- include/exec/exec-all.h | 8 ++++---- include/exec/ram_addr.h | 2 ++ linux-user/mmap.c | 1 - target/xtensa/op_helper.c | 9 +-------- trace/control-target.c | 1 - 8 files changed, 38 insertions(+), 41 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index f0c3fd4d03..4b601bd48e 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -46,7 +46,7 @@ #endif #endif #else -#include "exec/address-spaces.h" +#include "exec/ram_addr.h" #endif #include "exec/cputlb.h" @@ -1934,7 +1934,11 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, * * Called with mmap_lock held for user-mode emulation. */ -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) +#ifdef CONFIG_SOFTMMU +void tb_invalidate_phys_range(ram_addr_t start, ram_addr_t end) +#else +void tb_invalidate_phys_range(target_ulong start, target_ulong end) +#endif { struct page_collection *pages; tb_page_addr_t next; @@ -2073,26 +2077,6 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) } #endif -#if !defined(CONFIG_USER_ONLY) -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) -{ - ram_addr_t ram_addr; - MemoryRegion *mr; - hwaddr l = 1; - - rcu_read_lock(); - mr = address_space_translate(as, addr, &addr, &l, false, attrs); - if (!(memory_region_is_ram(mr) - || memory_region_is_romd(mr))) { - rcu_read_unlock(); - return; - } - ram_addr = memory_region_get_ram_addr(mr) + addr; - tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0); - rcu_read_unlock(); -} -#endif /* !defined(CONFIG_USER_ONLY) */ - /* user-mode: call with mmap_lock held */ void tb_check_watchpoint(CPUState *cpu) { diff --git a/accel/tcg/translate-all.h b/accel/tcg/translate-all.h index e6cb963d7e..08e2f23a46 100644 --- a/accel/tcg/translate-all.h +++ b/accel/tcg/translate-all.h @@ -30,7 +30,6 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages, tb_page_addr_t start, int len); void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, int is_cpu_write_access); -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end); void tb_check_watchpoint(CPUState *cpu); #ifdef CONFIG_USER_ONLY diff --git a/exec.c b/exec.c index 9f35e34ad2..610d0c0746 100644 --- a/exec.c +++ b/exec.c @@ -1028,13 +1028,36 @@ const char *parse_cpu_model(const char *cpu_model) } #if defined(CONFIG_USER_ONLY) -static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) +void tb_invalidate_phys_addr(target_ulong addr) { mmap_lock(); - tb_invalidate_phys_page_range(pc, pc + 1, 0); + tb_invalidate_phys_page_range(addr, addr + 1, 0); mmap_unlock(); } + +static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) +{ + tb_invalidate_phys_addr(pc); +} #else +void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) +{ + ram_addr_t ram_addr; + MemoryRegion *mr; + hwaddr l = 1; + + rcu_read_lock(); + mr = address_space_translate(as, addr, &addr, &l, false, attrs); + if (!(memory_region_is_ram(mr) + || memory_region_is_romd(mr))) { + rcu_read_unlock(); + return; + } + ram_addr = memory_region_get_ram_addr(mr) + addr; + tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0); + rcu_read_unlock(); +} + static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { MemTxAttrs attrs; @@ -3146,9 +3169,7 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, } if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) { assert(tcg_enabled()); - mmap_lock(); tb_invalidate_phys_range(addr, addr + length); - mmap_unlock(); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); } cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 25a6f28ab8..6a7e7a866e 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -299,14 +299,14 @@ static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, static inline void tlb_flush_by_mmuidx_all_cpus(CPUState *cpu, uint16_t idxmap) { } + static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap) { } -static inline void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs) -{ -} + +void tb_invalidate_phys_addr(target_ulong addr); +void tb_invalidate_phys_range(target_ulong start, target_ulong end); #endif #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 33c361cad5..cf4ce06248 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -93,6 +93,8 @@ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp); #define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) #define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) +void tb_invalidate_phys_range(ram_addr_t start, ram_addr_t end); + static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, unsigned client) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 9168a2051c..d0c50e4888 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -20,7 +20,6 @@ #include "qemu.h" #include "qemu-common.h" -#include "translate-all.h" //#define DEBUG_MMAP diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index 8a8c763c63..bbbbb33f3c 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -36,11 +36,6 @@ #include "qemu/timer.h" #include "fpu/softfloat.h" -#ifdef CONFIG_USER_ONLY -/* tb_invalidate_phys_range */ -#include "accel/tcg/translate-all.h" -#endif - #ifndef CONFIG_USER_ONLY void xtensa_cpu_do_unaligned_access(CPUState *cs, @@ -114,9 +109,7 @@ static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) { - mmap_lock(); - tb_invalidate_phys_range(vaddr, vaddr + 1); - mmap_unlock(); + tb_invalidate_phys_addr(vaddr); } #endif diff --git a/trace/control-target.c b/trace/control-target.c index 706b2cee9d..ceb55c70ce 100644 --- a/trace/control-target.c +++ b/trace/control-target.c @@ -11,7 +11,6 @@ #include "cpu.h" #include "trace-root.h" #include "trace/control.h" -#include "translate-all.h" void trace_event_set_state_dynamic_init(TraceEvent *ev, bool state) From 1507bd136fd9a516226fce8738d361a64f45b699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 4 Jun 2018 13:30:43 +0100 Subject: [PATCH 1664/2380] chardev: don't splatter terminal settings on exit if not previously set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stdio chardev finalize method calls term_exit() to restore the original terminal settings that were saved in the "oldtty" global. If the qemu_chr_open_stdio() method exited with an error, we might not have any original terminal settings saved in "oldtty" yet. eg $ qemu-system-x86_64 -monitor stdio -daemonize qemu-system-x86_64: -monitor stdio: cannot use stdio with -daemonize will cause QEMU to splatter the terminal settings with an all-zeros "struct termios", with predictably unpleasant results. Fortunately the existing "stdio_in_use" flag is suitable witness for whether "oldtty" contains settings that need restoring. Signed-off-by: Daniel P. Berrangé Message-Id: <20180604123043.13985-1-berrange@redhat.com> Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- chardev/char-stdio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index 96375f2ab8..9624220e6d 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -46,8 +46,10 @@ static bool stdio_echo_state; static void term_exit(void) { - tcsetattr(0, TCSANOW, &oldtty); - fcntl(0, F_SETFL, old_fd0_flags); + if (stdio_in_use) { + tcsetattr(0, TCSANOW, &oldtty); + fcntl(0, F_SETFL, old_fd0_flags); + } } static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo) From d29a8a1b0758a905b148929dd14b79bfeb297a80 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Sat, 2 Jun 2018 09:52:59 +0100 Subject: [PATCH 1665/2380] main-loop: document IOCanReadHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stefan Hajnoczi Message-Id: <20180602085259.17853-1-stefanha@redhat.com> Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé --- include/qemu/main-loop.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 6b4b60bf6d..721aa2416a 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -168,6 +168,20 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); /* async I/O support */ typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); + +/** + * IOCanReadHandler: Return the number of bytes that #IOReadHandler can accept + * + * This function reports how many bytes #IOReadHandler is prepared to accept. + * #IOReadHandler may be invoked with up to this number of bytes. If this + * function returns 0 then #IOReadHandler is not invoked. + * + * This function is typically called from an event loop. If the number of + * bytes changes outside the event loop (e.g. because a vcpu thread drained the + * buffer), then it is necessary to kick the event loop so that this function + * is called again. aio_notify() or qemu_notify_event() can be used to kick + * the event loop. + */ typedef int IOCanReadHandler(void *opaque); /** From 019288bf137183bf3407c9824655b753bfafc99f Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Tue, 5 Jun 2018 03:54:55 -0400 Subject: [PATCH 1666/2380] hw/char/serial: Only retry if qemu_chr_fe_write returns 0 Only retry on serial_xmit if qemu_chr_fe_write returns 0, as this is the only recoverable error. Retrying with any other scenario, in addition to being a waste of CPU cycles, can compromise the Guest stability if by the vCPU issuing the write and the main loop thread are, by chance or explicit pinning, running on the same pCPU. Previous discussion: https://lists.nongnu.org/archive/html/qemu-devel/2018-05/msg06998.html Signed-off-by: Sergio Lopez Message-Id: <1528185295-14199-1-git-send-email-slp@redhat.com> Signed-off-by: Paolo Bonzini --- hw/char/serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/char/serial.c b/hw/char/serial.c index 605b0d02f9..6de6c29779 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -260,7 +260,7 @@ static void serial_xmit(SerialState *s) if (s->mcr & UART_MCR_LOOP) { /* in loopback mode, say that we just received a char */ serial_receive1(s, &s->tsr, 1); - } else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) != 1 && + } else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) == 0 && s->tsr_retry < MAX_XMIT_RETRY) { assert(s->watch_tag == 0); s->watch_tag = From 13672386a93fef64cfd33bd72fbf3d80f2c00e94 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Jul 2017 09:29:02 -1000 Subject: [PATCH 1667/2380] target/i386: Fix BLSR and BLSI The implementation of these two instructions was swapped. At the same time, unify the setup of eflags for the insn group. Reported-by: Ricardo Ribalda Delgado Signed-off-by: Richard Henderson Message-Id: <20170712192902.15493-1-rth@twiddle.net> Signed-off-by: Paolo Bonzini --- target/i386/translate.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/target/i386/translate.c b/target/i386/translate.c index 697a918c11..c91849417b 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -4059,34 +4059,26 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, ot = mo_64_32(s->dflag); gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + tcg_gen_mov_tl(cpu_cc_src, cpu_T0); switch (reg & 7) { case 1: /* blsr By,Ey */ + tcg_gen_subi_tl(cpu_T1, cpu_T0, 1); + tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); + break; + case 2: /* blsmsk By,Ey */ + tcg_gen_subi_tl(cpu_T1, cpu_T0, 1); + tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_T1); + break; + case 3: /* blsi By, Ey */ tcg_gen_neg_tl(cpu_T1, cpu_T0); tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); - gen_op_mov_reg_v(ot, s->vex_v, cpu_T0); - gen_op_update2_cc(); - set_cc_op(s, CC_OP_BMILGB + ot); break; - - case 2: /* blsmsk By,Ey */ - tcg_gen_mov_tl(cpu_cc_src, cpu_T0); - tcg_gen_subi_tl(cpu_T0, cpu_T0, 1); - tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_cc_src); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); - set_cc_op(s, CC_OP_BMILGB + ot); - break; - - case 3: /* blsi By, Ey */ - tcg_gen_mov_tl(cpu_cc_src, cpu_T0); - tcg_gen_subi_tl(cpu_T0, cpu_T0, 1); - tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_cc_src); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); - set_cc_op(s, CC_OP_BMILGB + ot); - break; - default: goto unknown_op; } + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + gen_op_mov_reg_v(ot, s->vex_v, cpu_T0); + set_cc_op(s, CC_OP_BMILGB + ot); break; default: From 93d1499c8119989e3eb9a6936c5a18aaaaca6330 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Jun 2018 15:41:58 +0200 Subject: [PATCH 1668/2380] whpx: commit missing file Not included by mistake in commit 327fccb288976f95808efa968082fc9d4a9ced84. Signed-off-by: Paolo Bonzini --- target/i386/whp-dispatch.h | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 target/i386/whp-dispatch.h diff --git a/target/i386/whp-dispatch.h b/target/i386/whp-dispatch.h new file mode 100644 index 0000000000..d8d3485976 --- /dev/null +++ b/target/i386/whp-dispatch.h @@ -0,0 +1,56 @@ +#include "windows.h" +#include + +#include +#include + +#ifndef WHP_DISPATCH_H +#define WHP_DISPATCH_H + + +#define LIST_WINHVPLATFORM_FUNCTIONS(X) \ + X(HRESULT, WHvGetCapability, (WHV_CAPABILITY_CODE CapabilityCode, VOID* CapabilityBuffer, UINT32 CapabilityBufferSizeInBytes, UINT32* WrittenSizeInBytes)) \ + X(HRESULT, WHvCreatePartition, (WHV_PARTITION_HANDLE* Partition)) \ + X(HRESULT, WHvSetupPartition, (WHV_PARTITION_HANDLE Partition)) \ + X(HRESULT, WHvDeletePartition, (WHV_PARTITION_HANDLE Partition)) \ + X(HRESULT, WHvGetPartitionProperty, (WHV_PARTITION_HANDLE Partition, WHV_PARTITION_PROPERTY_CODE PropertyCode, VOID* PropertyBuffer, UINT32 PropertyBufferSizeInBytes, UINT32* WrittenSizeInBytes)) \ + X(HRESULT, WHvSetPartitionProperty, (WHV_PARTITION_HANDLE Partition, WHV_PARTITION_PROPERTY_CODE PropertyCode, const VOID* PropertyBuffer, UINT32 PropertyBufferSizeInBytes)) \ + X(HRESULT, WHvMapGpaRange, (WHV_PARTITION_HANDLE Partition, VOID* SourceAddress, WHV_GUEST_PHYSICAL_ADDRESS GuestAddress, UINT64 SizeInBytes, WHV_MAP_GPA_RANGE_FLAGS Flags)) \ + X(HRESULT, WHvUnmapGpaRange, (WHV_PARTITION_HANDLE Partition, WHV_GUEST_PHYSICAL_ADDRESS GuestAddress, UINT64 SizeInBytes)) \ + X(HRESULT, WHvTranslateGva, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, WHV_GUEST_VIRTUAL_ADDRESS Gva, WHV_TRANSLATE_GVA_FLAGS TranslateFlags, WHV_TRANSLATE_GVA_RESULT* TranslationResult, WHV_GUEST_PHYSICAL_ADDRESS* Gpa)) \ + X(HRESULT, WHvCreateVirtualProcessor, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, UINT32 Flags)) \ + X(HRESULT, WHvDeleteVirtualProcessor, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex)) \ + X(HRESULT, WHvRunVirtualProcessor, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, VOID* ExitContext, UINT32 ExitContextSizeInBytes)) \ + X(HRESULT, WHvCancelRunVirtualProcessor, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, UINT32 Flags)) \ + X(HRESULT, WHvGetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, WHV_REGISTER_VALUE* RegisterValues)) \ + X(HRESULT, WHvSetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, const WHV_REGISTER_VALUE* RegisterValues)) \ + + +#define LIST_WINHVEMULATION_FUNCTIONS(X) \ + X(HRESULT, WHvEmulatorCreateEmulator, (const WHV_EMULATOR_CALLBACKS* Callbacks, WHV_EMULATOR_HANDLE* Emulator)) \ + X(HRESULT, WHvEmulatorDestroyEmulator, (WHV_EMULATOR_HANDLE Emulator)) \ + X(HRESULT, WHvEmulatorTryIoEmulation, (WHV_EMULATOR_HANDLE Emulator, VOID* Context, const WHV_VP_EXIT_CONTEXT* VpContext, const WHV_X64_IO_PORT_ACCESS_CONTEXT* IoInstructionContext, WHV_EMULATOR_STATUS* EmulatorReturnStatus)) \ + X(HRESULT, WHvEmulatorTryMmioEmulation, (WHV_EMULATOR_HANDLE Emulator, VOID* Context, const WHV_VP_EXIT_CONTEXT* VpContext, const WHV_MEMORY_ACCESS_CONTEXT* MmioInstructionContext, WHV_EMULATOR_STATUS* EmulatorReturnStatus)) \ + + +#define WHP_DEFINE_TYPE(return_type, function_name, signature) \ + typedef return_type (WINAPI *function_name ## _t) signature; + +#define WHP_DECLARE_MEMBER(return_type, function_name, signature) \ + function_name ## _t function_name; + +/* Define function typedef */ +LIST_WINHVPLATFORM_FUNCTIONS(WHP_DEFINE_TYPE) +LIST_WINHVEMULATION_FUNCTIONS(WHP_DEFINE_TYPE) + +struct WHPDispatch { + LIST_WINHVPLATFORM_FUNCTIONS(WHP_DECLARE_MEMBER) + LIST_WINHVEMULATION_FUNCTIONS(WHP_DECLARE_MEMBER) +}; + +extern struct WHPDispatch whp_dispatch; + +bool init_whp_dispatch(void); + + +#endif /* WHP_DISPATCH_H */ From 4d8938a05db15dea2c86c4ab9c5f872f160d2188 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 7 Jun 2018 17:47:04 +0200 Subject: [PATCH 1669/2380] memory-device: turn alignment assert into check The start of the address space indicates which maximum alignment is supported by our machine (e.g. ppc, x86 1GB). This is helpful to catch fragmenting guest physical memory in strange fashions. Right now we can crash QEMU by e.g. (there might be easier examples) qemu-system-x86_64 -m 256M,maxmem=20G,slots=2 \ -object memory-backend-file,id=mem0,size=8192M,mem-path=/dev/zero,align=8192M \ -device pc-dimm,id=dimm1,memdev=mem0 Signed-off-by: David Hildenbrand Message-Id: <20180607154705.6316-2-david@redhat.com> Reviewed-by: Michael S. Tsirkin Reviewed-by: Igor Mammedov Signed-off-by: Paolo Bonzini --- hw/mem/memory-device.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index 3e04f3954e..6de4f70bb4 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -116,9 +116,15 @@ uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, address_space_start = ms->device_memory->base; address_space_end = address_space_start + memory_region_size(&ms->device_memory->mr); - g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start); g_assert(address_space_end >= address_space_start); + /* address_space_start indicates the maximum alignment we expect */ + if (QEMU_ALIGN_UP(address_space_start, align) != address_space_start) { + error_setg(errp, "the alignment (0%" PRIx64 ") is not supported", + align); + return 0; + } + memory_device_check_addable(ms, size, errp); if (*errp) { return 0; From 61362b71c105ccb151ca16897a7d56534423f390 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 7 Jun 2018 17:47:05 +0200 Subject: [PATCH 1670/2380] exec: check that alignment is a power of two Right now we can crash QEMU using e.g. qemu-system-x86_64 -m 256M,maxmem=20G,slots=2 \ -object memory-backend-file,id=mem0,size=12288,mem-path=/dev/zero,align=12288 \ -device pc-dimm,id=dimm1,memdev=mem0 qemu-system-x86_64: util/mmap-alloc.c:115: qemu_ram_mmap: Assertion `is_power_of_2(align)' failed Fix this by adding a proper check. Signed-off-by: David Hildenbrand Message-Id: <20180607154705.6316-3-david@redhat.com> Reviewed-by: Michael S. Tsirkin Reviewed-by: Igor Mammedov Signed-off-by: Paolo Bonzini --- exec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/exec.c b/exec.c index 610d0c0746..cdcf769daa 100644 --- a/exec.c +++ b/exec.c @@ -1841,6 +1841,10 @@ static void *file_ram_alloc(RAMBlock *block, " must be multiples of page size 0x%zx", block->mr->align, block->page_size); return NULL; + } else if (block->mr->align && !is_power_of_2(block->mr->align)) { + error_setg(errp, "alignment 0x%" PRIx64 + " must be a power of two", block->mr->align); + return NULL; } block->mr->align = MAX(block->page_size, block->mr->align); #if defined(__s390x__) From 6c090d4a755bb6245461450869130a517e18a3dc Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Wed, 16 May 2018 17:18:34 +0800 Subject: [PATCH 1671/2380] kvm: Delete the slot if and only if the KVM_MEM_READONLY flag is changed According to KVM commit 75d61fbc, it needs to delete the slot before changing the KVM_MEM_READONLY flag. But QEMU commit 235e8982 only check whether KVM_MEM_READONLY flag is set instead of changing. It doesn't need to delete the slot if the KVM_MEM_READONLY flag is not changed. This fixes a issue that migrating a VM at the OVMF startup stage and VM is executing the codes in rom. Between the deleting and adding the slot in kvm_set_user_memory_region, there is a chance that guest access rom and trap to KVM, then KVM can't find the corresponding memslot. While KVM (on ARM) injects an abort to guest due to the broken hva, then guest will get stuck. Signed-off-by: Shannon Zhao Message-Id: <1526462314-19720-1-git-send-email-zhaoshenglong@huawei.com> Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 17 ++++++++--------- include/sysemu/kvm_int.h | 1 + 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index ffee68e603..eb7db92a5e 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -256,7 +256,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, return 0; } -static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) +static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) { KVMState *s = kvm_state; struct kvm_userspace_memory_region mem; @@ -267,7 +267,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) mem.userspace_addr = (unsigned long)slot->ram; mem.flags = slot->flags; - if (slot->memory_size && mem.flags & KVM_MEM_READONLY) { + if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) { /* Set the slot size to 0 before setting the slot to the desired * value. This is needed based on KVM commit 75d61fbc. */ mem.memory_size = 0; @@ -275,6 +275,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) } mem.memory_size = slot->memory_size; ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + slot->old_flags = mem.flags; trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, mem.memory_size, mem.userspace_addr, ret); return ret; @@ -391,17 +392,14 @@ static int kvm_mem_flags(MemoryRegion *mr) static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, MemoryRegion *mr) { - int old_flags; - - old_flags = mem->flags; mem->flags = kvm_mem_flags(mr); /* If nothing changed effectively, no need to issue ioctl */ - if (mem->flags == old_flags) { + if (mem->flags == mem->old_flags) { return 0; } - return kvm_set_user_memory_region(kml, mem); + return kvm_set_user_memory_region(kml, mem, false); } static int kvm_section_update_flags(KVMMemoryListener *kml, @@ -755,7 +753,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, /* unregister the slot */ mem->memory_size = 0; - err = kvm_set_user_memory_region(kml, mem); + mem->flags = 0; + err = kvm_set_user_memory_region(kml, mem, false); if (err) { fprintf(stderr, "%s: error unregistering slot: %s\n", __func__, strerror(-err)); @@ -771,7 +770,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, mem->ram = ram; mem->flags = kvm_mem_flags(mr); - err = kvm_set_user_memory_region(kml, mem); + err = kvm_set_user_memory_region(kml, mem, true); if (err) { fprintf(stderr, "%s: error registering slot: %s\n", __func__, strerror(-err)); diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 888557a1ca..f838412491 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -20,6 +20,7 @@ typedef struct KVMSlot void *ram; int slot; int flags; + int old_flags; } KVMSlot; typedef struct KVMMemoryListener { From 70c31264afd1f50c3b93a9007d97215ed5485e32 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Wed, 25 Apr 2018 10:54:56 +0800 Subject: [PATCH 1672/2380] tests/atomic_add-bench: add -m option to use mutexes This allows us to use atomic-add-bench as a microbenchmark for evaluating qemu_mutex_lock's performance. Signed-off-by: Emilio G. Cota [cherry picked from https://github.com/cota/qemu/commit/f04f34df] Signed-off-by: Peter Xu Message-Id: <20180425025459.5258-2-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- tests/atomic_add-bench.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/atomic_add-bench.c b/tests/atomic_add-bench.c index caa1e8e689..f96d448f77 100644 --- a/tests/atomic_add-bench.c +++ b/tests/atomic_add-bench.c @@ -8,6 +8,7 @@ struct thread_info { } QEMU_ALIGNED(64); struct count { + QemuMutex lock; unsigned long val; } QEMU_ALIGNED(64); @@ -18,11 +19,13 @@ static unsigned int n_ready_threads; static struct count *counts; static unsigned int duration = 1; static unsigned int range = 1024; +static bool use_mutex; static bool test_start; static bool test_stop; static const char commands_string[] = " -n = number of threads\n" + " -m = use mutexes instead of atomic increments\n" " -d = duration in seconds\n" " -r = range (will be rounded up to pow2)"; @@ -59,7 +62,13 @@ static void *thread_func(void *arg) info->r = xorshift64star(info->r); index = info->r & (range - 1); - atomic_inc(&counts[index].val); + if (use_mutex) { + qemu_mutex_lock(&counts[index].lock); + counts[index].val += 1; + qemu_mutex_unlock(&counts[index].lock); + } else { + atomic_inc(&counts[index].val); + } } return NULL; } @@ -91,6 +100,9 @@ static void create_threads(void) th_info = g_new(struct thread_info, n_threads); counts = qemu_memalign(64, sizeof(*counts) * range); memset(counts, 0, sizeof(*counts) * range); + for (i = 0; i < range; i++) { + qemu_mutex_init(&counts[i].lock); + } for (i = 0; i < n_threads; i++) { struct thread_info *info = &th_info[i]; @@ -131,7 +143,7 @@ static void parse_args(int argc, char *argv[]) int c; for (;;) { - c = getopt(argc, argv, "hd:n:r:"); + c = getopt(argc, argv, "hd:n:mr:"); if (c < 0) { break; } @@ -145,6 +157,9 @@ static void parse_args(int argc, char *argv[]) case 'n': n_threads = atoi(optarg); break; + case 'm': + use_mutex = true; + break; case 'r': range = pow2ceil(atoi(optarg)); break; From f1aff7aa8e6f238909bd0b0e7a1fe235802843f2 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 25 Apr 2018 10:54:57 +0800 Subject: [PATCH 1673/2380] qemu-thread: introduce qemu-thread-common.h Introduce some hooks for the shared part of qemu thread between POSIX and Windows implementations. Note that in qemu_mutex_unlock_impl() we moved the call before unlock operation which should make more sense. And we don't need qemu_mutex_post_unlock() hook. Put all these shared hooks into the header files. It should be internal to qemu-thread but not for qemu-thread users, hence put into util/ directory. Reviewed-by: Emilio G. Cota Signed-off-by: Peter Xu Message-Id: <20180425025459.5258-3-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- util/qemu-thread-common.h | 43 +++++++++++++++++++++++++++++++++++++++ util/qemu-thread-posix.c | 19 ++++++++--------- util/qemu-thread-win32.c | 17 ++++++++-------- 3 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 util/qemu-thread-common.h diff --git a/util/qemu-thread-common.h b/util/qemu-thread-common.h new file mode 100644 index 0000000000..d3292084d6 --- /dev/null +++ b/util/qemu-thread-common.h @@ -0,0 +1,43 @@ +/* + * Common qemu-thread implementation header file. + * + * Copyright Red Hat, Inc. 2018 + * + * Authors: + * Peter Xu , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_THREAD_COMMON_H +#define QEMU_THREAD_COMMON_H + +#include "qemu/typedefs.h" +#include "qemu/thread.h" +#include "trace.h" + +static inline void qemu_mutex_post_init(QemuMutex *mutex) +{ + mutex->initialized = true; +} + +static inline void qemu_mutex_pre_lock(QemuMutex *mutex, + const char *file, int line) +{ + trace_qemu_mutex_lock(mutex, file, line); +} + +static inline void qemu_mutex_post_lock(QemuMutex *mutex, + const char *file, int line) +{ + trace_qemu_mutex_locked(mutex, file, line); +} + +static inline void qemu_mutex_pre_unlock(QemuMutex *mutex, + const char *file, int line) +{ + trace_qemu_mutex_unlock(mutex, file, line); +} + +#endif diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index a1c34ba6f2..dfa66ff2fb 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -14,7 +14,7 @@ #include "qemu/thread.h" #include "qemu/atomic.h" #include "qemu/notify.h" -#include "trace.h" +#include "qemu-thread-common.h" static bool name_threads; @@ -43,7 +43,7 @@ void qemu_mutex_init(QemuMutex *mutex) err = pthread_mutex_init(&mutex->lock, NULL); if (err) error_exit(err, __func__); - mutex->initialized = true; + qemu_mutex_post_init(mutex); } void qemu_mutex_destroy(QemuMutex *mutex) @@ -62,13 +62,11 @@ void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line) int err; assert(mutex->initialized); - trace_qemu_mutex_lock(mutex, file, line); - + qemu_mutex_pre_lock(mutex, file, line); err = pthread_mutex_lock(&mutex->lock); if (err) error_exit(err, __func__); - - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); } int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) @@ -78,7 +76,7 @@ int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) assert(mutex->initialized); err = pthread_mutex_trylock(&mutex->lock); if (err == 0) { - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); return 0; } if (err != EBUSY) { @@ -92,11 +90,10 @@ void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line) int err; assert(mutex->initialized); + qemu_mutex_pre_unlock(mutex, file, line); err = pthread_mutex_unlock(&mutex->lock); if (err) error_exit(err, __func__); - - trace_qemu_mutex_unlock(mutex, file, line); } void qemu_rec_mutex_init(QemuRecMutex *mutex) @@ -160,9 +157,9 @@ void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, con int err; assert(cond->initialized); - trace_qemu_mutex_unlock(mutex, file, line); + qemu_mutex_pre_unlock(mutex, file, line); err = pthread_cond_wait(&cond->cond, &mutex->lock); - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); if (err) error_exit(err, __func__); } diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index ab60c0d557..b303188a36 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -19,7 +19,7 @@ #include "qemu-common.h" #include "qemu/thread.h" #include "qemu/notify.h" -#include "trace.h" +#include "qemu-thread-common.h" #include static bool name_threads; @@ -46,7 +46,7 @@ static void error_exit(int err, const char *msg) void qemu_mutex_init(QemuMutex *mutex) { InitializeSRWLock(&mutex->lock); - mutex->initialized = true; + qemu_mutex_post_init(mutex); } void qemu_mutex_destroy(QemuMutex *mutex) @@ -59,10 +59,9 @@ void qemu_mutex_destroy(QemuMutex *mutex) void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line) { assert(mutex->initialized); - trace_qemu_mutex_lock(mutex, file, line); - + qemu_mutex_pre_lock(mutex, file, line); AcquireSRWLockExclusive(&mutex->lock); - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); } int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) @@ -72,7 +71,7 @@ int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) assert(mutex->initialized); owned = TryAcquireSRWLockExclusive(&mutex->lock); if (owned) { - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); return 0; } return -EBUSY; @@ -81,7 +80,7 @@ int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line) { assert(mutex->initialized); - trace_qemu_mutex_unlock(mutex, file, line); + qemu_mutex_pre_unlock(mutex, file, line); ReleaseSRWLockExclusive(&mutex->lock); } @@ -145,9 +144,9 @@ void qemu_cond_broadcast(QemuCond *cond) void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, const int line) { assert(cond->initialized); - trace_qemu_mutex_unlock(mutex, file, line); + qemu_mutex_pre_unlock(mutex, file, line); SleepConditionVariableSRW(&cond->var, &mutex->lock, INFINITE, 0); - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); } void qemu_sem_init(QemuSemaphore *sem, int init) From ba59fb778ec68b072196cff9af11c7612a6e52f2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Jun 2018 14:23:08 +0200 Subject: [PATCH 1674/2380] QemuMutex: support --enable-debug-mutex We have had some tracing tools for mutex but it's not easy to use them for e.g. dead locks. Let's provide "--enable-debug-mutex" parameter when configure to allow QemuMutex to store the last owner that took specific lock. It will be easy to use this tool to debug deadlocks since we can directly know who took the lock then as long as we can have a debugger attached to the process. Reviewed-by: Emilio G. Cota Signed-off-by: Peter Xu Message-Id: <20180425025459.5258-4-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 10 ++++++++++ include/qemu/thread-posix.h | 4 ++++ include/qemu/thread-win32.h | 4 ++++ util/qemu-thread-common.h | 12 ++++++++++++ 4 files changed, 30 insertions(+) diff --git a/configure b/configure index 4d12cfbe3f..3f53aa76fd 100755 --- a/configure +++ b/configure @@ -456,6 +456,7 @@ replication="yes" vxhs="" libxml2="" docker="no" +debug_mutex="no" # cross compilers defaults, can be overridden with --cross-cc-ARCH cross_cc_aarch64="aarch64-linux-gnu-gcc" @@ -1411,6 +1412,10 @@ for opt do ;; --disable-git-update) git_update=no ;; + --enable-debug-mutex) debug_mutex=yes + ;; + --disable-debug-mutex) debug_mutex=no + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1685,6 +1690,7 @@ disabled with --disable-FEATURE, default is enabled if available: crypto-afalg Linux AF_ALG crypto backend driver vhost-user vhost-user support capstone capstone disassembler support + debug-mutex mutex debugging support NOTE: The object files are built at the place where configure is launched EOF @@ -5951,6 +5957,7 @@ echo "seccomp support $seccomp" echo "coroutine backend $coroutine" echo "coroutine pool $coroutine_pool" echo "debug stack usage $debug_stack_usage" +echo "mutex debugging $debug_mutex" echo "crypto afalg $crypto_afalg" echo "GlusterFS support $glusterfs" echo "gcov $gcov_tool" @@ -6704,6 +6711,9 @@ fi if test "$capstone" != "no" ; then echo "CONFIG_CAPSTONE=y" >> $config_host_mak fi +if test "$debug_mutex" = "yes" ; then + echo "CONFIG_DEBUG_MUTEX=y" >> $config_host_mak +fi # Hold two types of flag: # CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index f3f47e426f..fd27b34128 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -12,6 +12,10 @@ typedef QemuMutex QemuRecMutex; struct QemuMutex { pthread_mutex_t lock; +#ifdef CONFIG_DEBUG_MUTEX + const char *file; + int line; +#endif bool initialized; }; diff --git a/include/qemu/thread-win32.h b/include/qemu/thread-win32.h index 3a05e3b3aa..d668d789b4 100644 --- a/include/qemu/thread-win32.h +++ b/include/qemu/thread-win32.h @@ -5,6 +5,10 @@ struct QemuMutex { SRWLOCK lock; +#ifdef CONFIG_DEBUG_MUTEX + const char *file; + int line; +#endif bool initialized; }; diff --git a/util/qemu-thread-common.h b/util/qemu-thread-common.h index d3292084d6..a0ea7c0d92 100644 --- a/util/qemu-thread-common.h +++ b/util/qemu-thread-common.h @@ -19,6 +19,10 @@ static inline void qemu_mutex_post_init(QemuMutex *mutex) { +#ifdef CONFIG_DEBUG_MUTEX + mutex->file = NULL; + mutex->line = 0; +#endif mutex->initialized = true; } @@ -31,12 +35,20 @@ static inline void qemu_mutex_pre_lock(QemuMutex *mutex, static inline void qemu_mutex_post_lock(QemuMutex *mutex, const char *file, int line) { +#ifdef CONFIG_DEBUG_MUTEX + mutex->file = file; + mutex->line = line; +#endif trace_qemu_mutex_locked(mutex, file, line); } static inline void qemu_mutex_pre_unlock(QemuMutex *mutex, const char *file, int line) { +#ifdef CONFIG_DEBUG_MUTEX + mutex->file = NULL; + mutex->line = 0; +#endif trace_qemu_mutex_unlock(mutex, file, line); } From 1fcc6d42e78c5fbccef63f47a380361ee81d344a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 25 Apr 2018 10:54:59 +0800 Subject: [PATCH 1675/2380] configure: enable debug-mutex if debug enabled Reviewed-by: Emilio G. Cota Signed-off-by: Peter Xu Message-Id: <20180425025459.5258-5-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- configure | 1 + 1 file changed, 1 insertion(+) diff --git a/configure b/configure index 3f53aa76fd..ca7de4f55f 100755 --- a/configure +++ b/configure @@ -1042,6 +1042,7 @@ for opt do --enable-debug) # Enable debugging options that aren't excessively noisy debug_tcg="yes" + debug_mutex="yes" debug="yes" strip_opt="no" fortify_source="no" From a1d30f285ebc0ba89d8dcba0b10a6b2516c2e470 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 13 Jun 2018 07:05:19 +0200 Subject: [PATCH 1676/2380] Replace '-enable-kvm' with '-accel kvm' in docs and help texts The preferred way to select the KVM accelerator is to use "-accel kvm" these days, so let's be consistent in our documentation and help texts. Signed-off-by: Thomas Huth Message-Id: <1528866321-23886-3-git-send-email-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- docs/COLO-FT.txt | 8 ++++---- docs/can.txt | 4 ++-- docs/multi-thread-compression.txt | 2 +- docs/multiseat.txt | 2 +- docs/specs/tpm.txt | 8 ++++---- hw/block/dataplane/virtio-blk.c | 4 ++-- hw/scsi/virtio-scsi-dataplane.c | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/COLO-FT.txt b/docs/COLO-FT.txt index e289be2f41..d7c7dcda8f 100644 --- a/docs/COLO-FT.txt +++ b/docs/COLO-FT.txt @@ -113,16 +113,16 @@ by using 'x-colo-lost-heartbeat' command. == Test procedure == 1. Startup qemu Primary: -# qemu-kvm -enable-kvm -m 2048 -smp 2 -qmp stdio -vnc :7 -name primary \ - -device piix3-usb-uhci \ +# qemu-system-x86_64 -accel kvm -m 2048 -smp 2 -qmp stdio -name primary \ + -device piix3-usb-uhci -vnc :7 \ -device usb-tablet -netdev tap,id=hn0,vhost=off \ -device virtio-net-pci,id=net-pci0,netdev=hn0 \ -drive if=virtio,id=primary-disk0,driver=quorum,read-pattern=fifo,vote-threshold=1,\ children.0.file.filename=1.raw,\ children.0.driver=raw -S Secondary: -# qemu-kvm -enable-kvm -m 2048 -smp 2 -qmp stdio -vnc :7 -name secondary \ - -device piix3-usb-uhci \ +# qemu-system-x86_64 -accel kvm -m 2048 -smp 2 -qmp stdio -name secondary \ + -device piix3-usb-uhci -vnc :7 \ -device usb-tablet -netdev tap,id=hn0,vhost=off \ -device virtio-net-pci,id=net-pci0,netdev=hn0 \ -drive if=none,id=secondary-disk0,file.filename=1.raw,driver=raw,node-name=node0 \ diff --git a/docs/can.txt b/docs/can.txt index a357105762..7ba23b259a 100644 --- a/docs/can.txt +++ b/docs/can.txt @@ -52,7 +52,7 @@ The ''kvaser_pci'' board/device model is compatible with and has been tested wit The tested setup was Linux 4.9 kernel on the host and guest side. Example for qemu-system-x86_64: - qemu-system-x86_64 -enable-kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \ + qemu-system-x86_64 -accel kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \ -initrd ramdisk.cpio \ -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \ -object can-bus,id=canbus0 \ @@ -104,4 +104,4 @@ Links to other resources Slides http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can-slides.pdf (5) Linux SocketCAN utilities - https://github.com/linux-can/can-utils/ \ No newline at end of file + https://github.com/linux-can/can-utils/ diff --git a/docs/multi-thread-compression.txt b/docs/multi-thread-compression.txt index d0caaf7b3b..bb88c6bdf1 100644 --- a/docs/multi-thread-compression.txt +++ b/docs/multi-thread-compression.txt @@ -62,7 +62,7 @@ RAM: 128G NIC: Intel I350 (10/100/1000Mbps) Host OS: CentOS 7 64-bit Guest OS: RHEL 6.5 64-bit -Parameter: qemu-system-x86_64 -enable-kvm -smp 4 -m 4096 +Parameter: qemu-system-x86_64 -accel kvm -smp 4 -m 4096 /share/ia32e_rhel6u5.qcow -monitor stdio There is no additional application is running on the guest when doing diff --git a/docs/multiseat.txt b/docs/multiseat.txt index 807518c8af..dc28cdb613 100644 --- a/docs/multiseat.txt +++ b/docs/multiseat.txt @@ -18,7 +18,7 @@ or Next put together the qemu command line (sdk/gtk): -qemu -enable-kvm -usb $memory $disk $whatever \ +qemu -accel kvm -usb $memory $disk $whatever \ -display [ sdl | gtk ] \ -vga std \ -device usb-tablet diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt index c230c4c93e..70ad4a0cba 100644 --- a/docs/specs/tpm.txt +++ b/docs/specs/tpm.txt @@ -98,7 +98,7 @@ QEMU files related to the TPM passthrough device: Command line to start QEMU with the TPM passthrough device using the host's hardware TPM /dev/tpm0: -qemu-system-x86_64 -display sdl -enable-kvm \ +qemu-system-x86_64 -display sdl -accel kvm \ -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \ -device tpm-tis,tpmdev=tpm0 test.img @@ -164,7 +164,7 @@ swtpm socket --tpmstate dir=/tmp/mytpm1 \ Command line to start QEMU with the TPM emulator device communicating with the swtpm: -qemu-system-x86_64 -display sdl -enable-kvm \ +qemu-system-x86_64 -display sdl -accel kvm \ -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ -tpmdev emulator,id=tpm0,chardev=chrtpm \ @@ -222,7 +222,7 @@ swtpm socket --tpmstate dir=/tmp/mytpm1 \ In a 2nd terminal start the VM: -qemu-system-x86_64 -display sdl -enable-kvm \ +qemu-system-x86_64 -display sdl -accel kvm \ -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ -tpmdev emulator,id=tpm0,chardev=chrtpm \ @@ -255,7 +255,7 @@ swtpm socket --tpmstate dir=/tmp/mytpm1 \ In the 2nd terminal restore the state of the VM using the additonal '-incoming' option. -qemu-system-x86_64 -display sdl -enable-kvm \ +qemu-system-x86_64 -display sdl -accel kvm \ -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ -tpmdev emulator,id=tpm0,chardev=chrtpm \ diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index d648aeb73b..8c37bd314a 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -190,8 +190,8 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) /* Set up guest notifier (irq) */ r = k->set_guest_notifiers(qbus->parent, nvqs, true); if (r != 0) { - fprintf(stderr, "virtio-blk failed to set guest notifier (%d), " - "ensure -enable-kvm is set\n", r); + error_report("virtio-blk failed to set guest notifier (%d), " + "ensure -accel kvm is set.", r); goto fail_guest_notifiers; } diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 912e5005d8..b995bab3a2 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -142,8 +142,8 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) /* Set up guest notifier (irq) */ rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); if (rc != 0) { - fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " - "ensure -enable-kvm is set\n", rc); + error_report("virtio-scsi: Failed to set guest notifiers (%d), " + "ensure -accel kvm is set.", rc); goto fail_guest_notifiers; } From 0b3c5c81bf0a9e32fd08c532acde3caa446b3712 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 11 Jun 2018 16:56:07 -0300 Subject: [PATCH 1677/2380] qemu-options: Add missing newline to -accel help text The newline was removed by commit c97d6d2c, and broke -help output: Before this patch: $ qemu-system-x86_64 -help | grep smp thread=single|multi (enable multi-threaded TCG)-smp [...] After this patch: $ qemu-system-x86_64 -help | grep smp -smp [cpus=]n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets] Fixes: c97d6d2cdf97edb4aebe832fdba65d701ad7bcb6 Cc: Sergio Andres Gomez Del Real Signed-off-by: Eduardo Habkost Message-Id: <20180611195607.3015-1-ehabkost@redhat.com> Signed-off-by: Paolo Bonzini --- qemu-options.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu-options.hx b/qemu-options.hx index d5b0c26e8e..270772817a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -130,7 +130,7 @@ ETEXI DEF("accel", HAS_ARG, QEMU_OPTION_accel, "-accel [accel=]accelerator[,thread=single|multi]\n" " select accelerator (kvm, xen, hax, hvf, whpx or tcg; use 'help' for a list)\n" - " thread=single|multi (enable multi-threaded TCG)", QEMU_ARCH_ALL) + " thread=single|multi (enable multi-threaded TCG)\n", QEMU_ARCH_ALL) STEXI @item -accel @var{name}[,prop=@var{value}[,...]] @findex -accel From 1e695fd7c3147ed2fde3225f5c534bfc4774d5f2 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:30 +0200 Subject: [PATCH 1678/2380] pc-dimm: remove leftover "struct pc_dimms_capacity" Not needed anymore, let's drop it. Reviewed-by: David Gibson Reviewed-by: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-2-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/mem/pc-dimm.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 12da89d562..62b34a992e 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -27,11 +27,6 @@ #include "sysemu/numa.h" #include "trace.h" -typedef struct pc_dimms_capacity { - uint64_t size; - Error **errp; -} pc_dimms_capacity; - void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, uint64_t align, Error **errp) { From bb6e2f7a54dfa791510f64bc3a551e5a152ea5f7 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:31 +0200 Subject: [PATCH 1679/2380] pc: rename pc_dimm_(plug|unplug|...)* into pc_memory_(plug|unplug|...)* Use a similar naming scheme as spapr. This way, we can go ahead and rename e.g. pc_dimm_memory_plug to pc_dimm_plug, which avoids confusion. Reviewed-by: Igor Mammedov Reviewed-by: David Gibson Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-3-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 622e49d6bc..f9250ffae7 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1674,8 +1674,8 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) } } -static void pc_dimm_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_memory_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { HotplugHandlerClass *hhc; Error *local_err = NULL; @@ -1728,8 +1728,8 @@ out: error_propagate(errp, local_err); } -static void pc_dimm_unplug_request(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_memory_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { HotplugHandlerClass *hhc; Error *local_err = NULL; @@ -1759,8 +1759,8 @@ out: error_propagate(errp, local_err); } -static void pc_dimm_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_memory_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { PCMachineState *pcms = PC_MACHINE(hotplug_dev); HotplugHandlerClass *hhc; @@ -2015,7 +2015,7 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - pc_dimm_plug(hotplug_dev, dev, errp); + pc_memory_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_plug(hotplug_dev, dev, errp); } @@ -2025,7 +2025,7 @@ static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - pc_dimm_unplug_request(hotplug_dev, dev, errp); + pc_memory_unplug_request(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_unplug_request_cb(hotplug_dev, dev, errp); } else { @@ -2038,7 +2038,7 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - pc_dimm_unplug(hotplug_dev, dev, errp); + pc_memory_unplug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_unplug_cb(hotplug_dev, dev, errp); } else { From 284878ee98d682b1d4c859dd0e6334df421d3a50 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:32 +0200 Subject: [PATCH 1680/2380] pc-dimm: rename pc_dimm_memory_* to pc_dimm_* Let's rename it to make it look more consistent. Reviewed-by: Igor Mammedov Reviewed-by: David Gibson Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-4-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 4 ++-- hw/mem/pc-dimm.c | 6 +++--- hw/ppc/spapr.c | 6 +++--- include/hw/mem/pc-dimm.h | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f9250ffae7..f23133facc 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1713,7 +1713,7 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev, goto out; } - pc_dimm_memory_plug(dev, MACHINE(pcms), align, &local_err); + pc_dimm_plug(dev, MACHINE(pcms), align, &local_err); if (local_err) { goto out; } @@ -1773,7 +1773,7 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev, goto out; } - pc_dimm_memory_unplug(dev, MACHINE(pcms)); + pc_dimm_unplug(dev, MACHINE(pcms)); object_unparent(OBJECT(dev)); out: diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 62b34a992e..9e0c83e415 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -27,8 +27,8 @@ #include "sysemu/numa.h" #include "trace.h" -void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, - uint64_t align, Error **errp) +void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, + Error **errp) { int slot; PCDIMMDevice *dimm = PC_DIMM(dev); @@ -84,7 +84,7 @@ out: error_propagate(errp, local_err); } -void pc_dimm_memory_unplug(DeviceState *dev, MachineState *machine) +void pc_dimm_unplug(DeviceState *dev, MachineState *machine) { PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 0d032a1ad0..3a1bd733be 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3160,7 +3160,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, align = memory_region_get_alignment(mr); size = memory_region_size(mr); - pc_dimm_memory_plug(dev, MACHINE(ms), align, &local_err); + pc_dimm_plug(dev, MACHINE(ms), align, &local_err); if (local_err) { goto out; } @@ -3183,7 +3183,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; out_unplug: - pc_dimm_memory_unplug(dev, MACHINE(ms)); + pc_dimm_unplug(dev, MACHINE(ms)); out: error_propagate(errp, local_err); } @@ -3332,7 +3332,7 @@ static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); - pc_dimm_memory_unplug(dev, MACHINE(hotplug_dev)); + pc_dimm_unplug(dev, MACHINE(hotplug_dev)); object_unparent(OBJECT(dev)); spapr_pending_dimm_unplugs_remove(spapr, ds); } diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 627c8601d9..860343d64f 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -78,7 +78,7 @@ typedef struct PCDIMMDeviceClass { int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); -void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine, - uint64_t align, Error **errp); -void pc_dimm_memory_unplug(DeviceState *dev, MachineState *machine); +void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, + Error **errp); +void pc_dimm_unplug(DeviceState *dev, MachineState *machine); #endif From 9995c759510391ad9d3f7997c93c1ecdc6ed08b8 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:33 +0200 Subject: [PATCH 1681/2380] pc-dimm: remove pc_dimm_get_free_slot() from header Not used outside of pc-dimm.c and there shouldn't be other users. If other devices (e.g. memory devices) ever have to also use slots, then we will have to factor this out. Reviewed-by: Igor Mammedov Reviewed-by: David Gibson Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-5-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/mem/pc-dimm.c | 4 +++- include/hw/mem/pc-dimm.h | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 9e0c83e415..7387963cf1 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -27,6 +27,8 @@ #include "sysemu/numa.h" #include "trace.h" +static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); + void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, Error **errp) { @@ -111,7 +113,7 @@ static int pc_dimm_slot2bitmap(Object *obj, void *opaque) return 0; } -int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp) +static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp) { unsigned long *bitmap; int slot = 0; diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 860343d64f..cf71247630 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -76,8 +76,6 @@ typedef struct PCDIMMDeviceClass { MemoryRegion *(*get_vmstate_memory_region)(PCDIMMDevice *dimm); } PCDIMMDeviceClass; -int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); - void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, Error **errp); void pc_dimm_unplug(DeviceState *dev, MachineState *machine); From d468115b1c7a4d0843f18bc9da41f2c44f93877e Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:34 +0200 Subject: [PATCH 1682/2380] pc: factor out pc specific dimm checks into pc_memory_pre_plug() We can perform these checks before the device is actually realized. Reviewed-by: David Gibson Reviewed-by: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-6-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f23133facc..2db032a6df 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1674,6 +1674,29 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) } } +static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + const PCMachineState *pcms = PC_MACHINE(hotplug_dev); + const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); + + /* + * When -no-acpi is used with Q35 machine type, no ACPI is built, + * but pcms->acpi_dev is still created. Check !acpi_enabled in + * addition to cover this case. + */ + if (!pcms->acpi_dev || !acpi_enabled) { + error_setg(errp, + "memory hotplug is not enabled: missing acpi device or acpi disabled"); + return; + } + + if (is_nvdimm && !pcms->acpi_nvdimm_state.is_enabled) { + error_setg(errp, "nvdimm is not enabled: missing 'nvdimm' in '-M'"); + return; + } +} + static void pc_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -1696,23 +1719,6 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev, align = memory_region_get_alignment(mr); } - /* - * When -no-acpi is used with Q35 machine type, no ACPI is built, - * but pcms->acpi_dev is still created. Check !acpi_enabled in - * addition to cover this case. - */ - if (!pcms->acpi_dev || !acpi_enabled) { - error_setg(&local_err, - "memory hotplug is not enabled: missing acpi device or acpi disabled"); - goto out; - } - - if (is_nvdimm && !pcms->acpi_nvdimm_state.is_enabled) { - error_setg(&local_err, - "nvdimm is not enabled: missing 'nvdimm' in '-M'"); - goto out; - } - pc_dimm_plug(dev, MACHINE(pcms), align, &local_err); if (local_err) { goto out; @@ -2006,7 +2012,9 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + pc_memory_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_pre_plug(hotplug_dev, dev, errp); } } From 4ab56d04ede6e0f979fc8e4a54b381e99cf0a255 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:35 +0200 Subject: [PATCH 1683/2380] nvdimm: no need to overwrite get_vmstate_memory_region() Our parent class (PC_DIMM) provides exactly the same function. Reviewed-by: David Gibson Reviewed-by: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-7-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/mem/nvdimm.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 4087aca25e..f974accbdd 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -166,11 +166,6 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, memory_region_set_dirty(mr, backend_offset, size); } -static MemoryRegion *nvdimm_get_vmstate_memory_region(PCDIMMDevice *dimm) -{ - return host_memory_backend_get_memory(dimm->hostmem, &error_abort); -} - static void nvdimm_class_init(ObjectClass *oc, void *data) { PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); @@ -178,7 +173,6 @@ static void nvdimm_class_init(ObjectClass *oc, void *data) ddc->realize = nvdimm_realize; ddc->get_memory_region = nvdimm_get_memory_region; - ddc->get_vmstate_memory_region = nvdimm_get_vmstate_memory_region; nvc->read_label_data = nvdimm_read_label_data; nvc->write_label_data = nvdimm_write_label_data; From 7943e97b858e64eddf0f3395427e58c5cc00a7d9 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:36 +0200 Subject: [PATCH 1684/2380] hostmem: drop error variable from host_memory_backend_get_memory() Unused, so let's remove it. Reviewed-by: David Gibson Reviewed-by: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-8-david@redhat.com> Signed-off-by: Paolo Bonzini --- backends/hostmem.c | 3 +-- hw/mem/nvdimm.c | 4 ++-- hw/mem/pc-dimm.c | 4 ++-- hw/misc/ivshmem.c | 3 +-- include/sysemu/hostmem.h | 3 +-- numa.c | 3 +-- 6 files changed, 8 insertions(+), 12 deletions(-) diff --git a/backends/hostmem.c b/backends/hostmem.c index 3627e61584..4908946cd3 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -247,8 +247,7 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend) return memory_region_size(&backend->mr) != 0; } -MemoryRegion * -host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) +MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend) { return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; } diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index f974accbdd..df9716231f 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -105,7 +105,7 @@ static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) { - MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem, errp); + MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem); NVDIMMDevice *nvdimm = NVDIMM(dimm); uint64_t align, pmem_size, size = memory_region_size(mr); @@ -161,7 +161,7 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, memcpy(nvdimm->label_data + offset, buf, size); - mr = host_memory_backend_get_memory(dimm->hostmem, &error_abort); + mr = host_memory_backend_get_memory(dimm->hostmem); backend_offset = memory_region_size(mr) - nvdimm->label_size + offset; memory_region_set_dirty(mr, backend_offset, size); } diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 7387963cf1..73f0eee4c7 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -226,12 +226,12 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) return NULL; } - return host_memory_backend_get_memory(dimm->hostmem, errp); + return host_memory_backend_get_memory(dimm->hostmem); } static MemoryRegion *pc_dimm_get_vmstate_memory_region(PCDIMMDevice *dimm) { - return host_memory_backend_get_memory(dimm->hostmem, &error_abort); + return host_memory_backend_get_memory(dimm->hostmem); } static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 16f03701b7..ee01c5e66b 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -909,8 +909,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) if (s->hostmem != NULL) { IVSHMEM_DPRINTF("using hostmem\n"); - s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem, - &error_abort); + s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem); } else { Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr); assert(chr); diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index 5beb0ef8ab..6e6bd2c1cb 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -62,8 +62,7 @@ struct HostMemoryBackend { }; bool host_memory_backend_mr_inited(HostMemoryBackend *backend); -MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend, - Error **errp); +MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend); void host_memory_backend_set_mapped(HostMemoryBackend *backend, bool mapped); bool host_memory_backend_is_mapped(HostMemoryBackend *backend); diff --git a/numa.c b/numa.c index 33572bfa74..94f758c757 100644 --- a/numa.c +++ b/numa.c @@ -523,8 +523,7 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, if (!backend) { continue; } - MemoryRegion *seg = host_memory_backend_get_memory(backend, - &error_fatal); + MemoryRegion *seg = host_memory_backend_get_memory(backend); if (memory_region_is_mapped(seg)) { char *path = object_get_canonical_path_component(OBJECT(backend)); From a57d1911222bba79bda543568e925635461ead82 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:37 +0200 Subject: [PATCH 1685/2380] pc-dimm: merge get_(vmstate_)memory_region() Importantly, get_vmstate_memory_region() should also fail with a proper error if called before the device is realized. For a PCDIMM, both functions are to return the same thing, so share the implementation. All current users are called after the device has been realized, so we can expect the calls to succeed. Reviewed-by: David Gibson Reviewed-by: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-9-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/mem/pc-dimm.c | 13 +++++-------- include/hw/mem/pc-dimm.h | 3 ++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 73f0eee4c7..4ff39b59ef 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -35,7 +35,8 @@ void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, int slot; PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); + MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, + &error_abort); Error *local_err = NULL; MemoryRegion *mr; uint64_t addr; @@ -90,7 +91,8 @@ void pc_dimm_unplug(DeviceState *dev, MachineState *machine) { PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); + MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, + &error_abort); MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); memory_device_unplug_region(machine, mr); @@ -229,11 +231,6 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) return host_memory_backend_get_memory(dimm->hostmem); } -static MemoryRegion *pc_dimm_get_vmstate_memory_region(PCDIMMDevice *dimm) -{ - return host_memory_backend_get_memory(dimm->hostmem); -} - static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md) { const PCDIMMDevice *dimm = PC_DIMM(md); @@ -298,7 +295,7 @@ static void pc_dimm_class_init(ObjectClass *oc, void *data) dc->desc = "DIMM memory module"; ddc->get_memory_region = pc_dimm_get_memory_region; - ddc->get_vmstate_memory_region = pc_dimm_get_vmstate_memory_region; + ddc->get_vmstate_memory_region = pc_dimm_get_memory_region; mdc->get_addr = pc_dimm_md_get_addr; /* for a dimm plugged_size == region_size */ diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index cf71247630..5679a80465 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -73,7 +73,8 @@ typedef struct PCDIMMDeviceClass { /* public */ void (*realize)(PCDIMMDevice *dimm, Error **errp); MemoryRegion *(*get_memory_region)(PCDIMMDevice *dimm, Error **errp); - MemoryRegion *(*get_vmstate_memory_region)(PCDIMMDevice *dimm); + MemoryRegion *(*get_vmstate_memory_region)(PCDIMMDevice *dimm, + Error **errp); } PCDIMMDeviceClass; void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, From 5d10a0e12bf3d00958fee73c1b795cfab921873b Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:38 +0200 Subject: [PATCH 1686/2380] nvdimm: convert "unarmed" into a static property We don't allow to modify it after realization. So we can simply turn it into a static property. Reviewed-by: David Gibson Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-10-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/mem/nvdimm.c | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index df9716231f..7260c9c6b1 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -64,36 +64,11 @@ out: error_propagate(errp, local_err); } -static bool nvdimm_get_unarmed(Object *obj, Error **errp) -{ - NVDIMMDevice *nvdimm = NVDIMM(obj); - - return nvdimm->unarmed; -} - -static void nvdimm_set_unarmed(Object *obj, bool value, Error **errp) -{ - NVDIMMDevice *nvdimm = NVDIMM(obj); - Error *local_err = NULL; - - if (memory_region_size(&nvdimm->nvdimm_mr)) { - error_setg(&local_err, "cannot change property value"); - goto out; - } - - nvdimm->unarmed = value; - - out: - error_propagate(errp, local_err); -} - static void nvdimm_init(Object *obj) { object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "int", nvdimm_get_label_size, nvdimm_set_label_size, NULL, NULL, NULL); - object_property_add_bool(obj, NVDIMM_UNARMED_PROP, - nvdimm_get_unarmed, nvdimm_set_unarmed, NULL); } static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) @@ -166,13 +141,20 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, memory_region_set_dirty(mr, backend_offset, size); } +static Property nvdimm_properties[] = { + DEFINE_PROP_BOOL(NVDIMM_UNARMED_PROP, NVDIMMDevice, unarmed, false), + DEFINE_PROP_END_OF_LIST(), +}; + static void nvdimm_class_init(ObjectClass *oc, void *data) { PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); NVDIMMClass *nvc = NVDIMM_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); ddc->realize = nvdimm_realize; ddc->get_memory_region = nvdimm_get_memory_region; + dc->props = nvdimm_properties; nvc->read_label_data = nvdimm_read_label_data; nvc->write_label_data = nvdimm_write_label_data; From eb7fd4d0f64fcab2da9ae454a1f214174e881372 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:39 +0200 Subject: [PATCH 1687/2380] nvdimm: convert nvdimm_mr into a pointer This way we can easily check if the region has already been inititalized without having to rely on the size of an uninitialized region being 0. Free the region in nvdimm_finalize() and not in unrealize() as we will allow to create the region before realization in following patches. Reviewed-by: David Gibson Reviewed-by: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-11-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/mem/nvdimm.c | 17 +++++++++++++---- include/hw/mem/nvdimm.h | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 7260c9c6b1..afd3912d6b 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -43,7 +43,7 @@ static void nvdimm_set_label_size(Object *obj, Visitor *v, const char *name, Error *local_err = NULL; uint64_t value; - if (memory_region_size(&nvdimm->nvdimm_mr)) { + if (nvdimm->nvdimm_mr) { error_setg(&local_err, "cannot change property value"); goto out; } @@ -71,11 +71,18 @@ static void nvdimm_init(Object *obj) NULL, NULL); } +static void nvdimm_finalize(Object *obj) +{ + NVDIMMDevice *nvdimm = NVDIMM(obj); + + g_free(nvdimm->nvdimm_mr); +} + static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) { NVDIMMDevice *nvdimm = NVDIMM(dimm); - return &nvdimm->nvdimm_mr; + return nvdimm->nvdimm_mr; } static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) @@ -102,9 +109,10 @@ static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) return; } - memory_region_init_alias(&nvdimm->nvdimm_mr, OBJECT(dimm), + nvdimm->nvdimm_mr = g_new(MemoryRegion, 1); + memory_region_init_alias(nvdimm->nvdimm_mr, OBJECT(dimm), "nvdimm-memory", mr, 0, pmem_size); - nvdimm->nvdimm_mr.align = align; + nvdimm->nvdimm_mr->align = align; } /* @@ -167,6 +175,7 @@ static TypeInfo nvdimm_info = { .class_init = nvdimm_class_init, .instance_size = sizeof(NVDIMMDevice), .instance_init = nvdimm_init, + .instance_finalize = nvdimm_finalize, }; static void nvdimm_register_types(void) diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index 9340631cfc..c5c9b3c7f8 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -74,7 +74,7 @@ struct NVDIMMDevice { * it's the PMEM region in NVDIMM device, which is presented to * guest via ACPI NFIT and _FIT method if NVDIMM hotplug is supported. */ - MemoryRegion nvdimm_mr; + MemoryRegion *nvdimm_mr; /* * The 'on' value results in the unarmed flag set in ACPI NFIT, From a4659a8ef424928f654707ca637ba133cbe22396 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:40 +0200 Subject: [PATCH 1688/2380] nvdimm: make get_memory_region() perform checks and initialization We might get a call to get_memory_region() before the device has been realized. We should return a consistent value, as the return value will e.g. later on be used in the pre_plug handler. To avoid duplicating too much code, factor the initialization and checks out into a helper function. Reviewed-by: Igor Mammedov Reviewed-by: David Gibson Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-12-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/mem/nvdimm.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index afd3912d6b..021d1c3997 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -78,20 +78,22 @@ static void nvdimm_finalize(Object *obj) g_free(nvdimm->nvdimm_mr); } -static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) +static void nvdimm_prepare_memory_region(NVDIMMDevice *nvdimm, Error **errp) { - NVDIMMDevice *nvdimm = NVDIMM(dimm); + PCDIMMDevice *dimm = PC_DIMM(nvdimm); + uint64_t align, pmem_size, size; + MemoryRegion *mr; - return nvdimm->nvdimm_mr; -} + g_assert(!nvdimm->nvdimm_mr); -static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) -{ - MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem); - NVDIMMDevice *nvdimm = NVDIMM(dimm); - uint64_t align, pmem_size, size = memory_region_size(mr); + if (!dimm->hostmem) { + error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property must be set"); + return; + } + mr = host_memory_backend_get_memory(dimm->hostmem); align = memory_region_get_alignment(mr); + size = memory_region_size(mr); pmem_size = size - nvdimm->label_size; nvdimm->label_data = memory_region_get_ram_ptr(mr) + pmem_size; @@ -115,6 +117,30 @@ static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) nvdimm->nvdimm_mr->align = align; } +static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) +{ + NVDIMMDevice *nvdimm = NVDIMM(dimm); + Error *local_err = NULL; + + if (!nvdimm->nvdimm_mr) { + nvdimm_prepare_memory_region(nvdimm, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return NULL; + } + } + return nvdimm->nvdimm_mr; +} + +static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) +{ + NVDIMMDevice *nvdimm = NVDIMM(dimm); + + if (!nvdimm->nvdimm_mr) { + nvdimm_prepare_memory_region(nvdimm, errp); + } +} + /* * the caller should check the input parameters before calling * label read/write functions. From f0b7bca64dbe8a15c1f4285c6061ce3c81a4a5c7 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 19 Jun 2018 15:41:41 +0200 Subject: [PATCH 1689/2380] pc-dimm: get_memory_region() will not fail after realize Let's try to reduce error handling a bit. In the plug/unplug case, the device was realized and therefore we can assume that getting access to the memory region will not fail. For get_vmstate_memory_region() this is already handled that way. Document both cases. Reviewed-by: Igor Mammedov Reviewed-by: David Gibson Signed-off-by: David Hildenbrand Message-Id: <20180619134141.29478-13-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 7 +------ hw/mem/pc-dimm.c | 7 +------ hw/ppc/spapr.c | 12 ++---------- include/hw/mem/pc-dimm.h | 6 ++++-- 4 files changed, 8 insertions(+), 24 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 2db032a6df..f310040351 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1706,15 +1706,10 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev, PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr; + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); uint64_t align = TARGET_PAGE_SIZE; bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - mr = ddc->get_memory_region(dimm, &local_err); - if (local_err) { - goto out; - } - if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) { align = memory_region_get_alignment(mr); } diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 4ff39b59ef..65843bc52a 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -37,15 +37,10 @@ void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, &error_abort); + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); Error *local_err = NULL; - MemoryRegion *mr; uint64_t addr; - mr = ddc->get_memory_region(dimm, &local_err); - if (local_err) { - goto out; - } - addr = object_property_get_uint(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); if (local_err) { diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 3a1bd733be..b32b971a14 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3149,14 +3149,10 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr; + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); uint64_t align, size, addr; uint32_t node; - mr = ddc->get_memory_region(dimm, &local_err); - if (local_err) { - goto out; - } align = memory_region_get_alignment(mr); size = memory_region_size(mr); @@ -3344,16 +3340,12 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, Error *local_err = NULL; PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr; + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); uint32_t nr_lmbs; uint64_t size, addr_start, addr; int i; sPAPRDRConnector *drc; - mr = ddc->get_memory_region(dimm, &local_err); - if (local_err) { - goto out; - } size = memory_region_size(mr); nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 5679a80465..26ebb7d5e9 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -62,9 +62,11 @@ typedef struct PCDIMMDevice { * @realize: called after common dimm is realized so that the dimm based * devices get the chance to do specified operations. * @get_memory_region: returns #MemoryRegion associated with @dimm which - * is directly mapped into the physical address space of guest. + * is directly mapped into the physical address space of guest. Will not + * fail after the device was realized. * @get_vmstate_memory_region: returns #MemoryRegion which indicates the - * memory of @dimm should be kept during live migration. + * memory of @dimm should be kept during live migration. Will not fail + * after the device was realized. */ typedef struct PCDIMMDeviceClass { /* private */ From 178003ea49aef4273d94c3c002b8f15858070f68 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 22 Jun 2018 16:40:45 +0200 Subject: [PATCH 1690/2380] numa: report all DIMM/NVDIMMs as plugged memory Right now, there is some inconsistency between hotplugged and coldplugged memory. DIMMs added via "-device" result in different stats than DIMMs added using "device_add". E.g. [...] -numa node,nodeid=0,cpus=0-1 -numa node,nodeid=1,cpus=2-3 \ -m 4G,maxmem=20G,slots=2 \ -object memory-backend-ram,id=mem0,size=8G \ -device pc-dimm,id=dimm0,memdev=mem0 \ -object memory-backend-ram,id=mem1,size=8G \ -device nvdimm,id=dimm1,memdev=mem1,node=1 Results in NUMA info (qemu) info numa info numa 2 nodes node 0 cpus: 0 1 node 0 size: 10240 MB node 0 plugged: 0 MB node 1 cpus: 2 3 node 1 size: 10240 MB node 1 plugged: 0 MB But in memory size summary: (qemu) info memory_size_summary info memory_size_summary base memory: 4294967296 plugged memory: 17179869184 Make this consistent by reporting all hot and coldplugged memory a.k.a. DIMM and NVDIMM as "plugged". Fixes: 31959e82fb0 ("hmp: extend "info numa" with hotplugged memory information") Signed-off-by: David Hildenbrand Message-Id: <20180622144045.737-1-david@redhat.com> Signed-off-by: Paolo Bonzini --- numa.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/numa.c b/numa.c index 94f758c757..5f6367b989 100644 --- a/numa.c +++ b/numa.c @@ -566,10 +566,8 @@ static void numa_stat_memory_devices(NumaNodeMem node_mem[]) if (pcdimm_info) { node_mem[pcdimm_info->node].node_mem += pcdimm_info->size; - if (pcdimm_info->hotpluggable && pcdimm_info->hotplugged) { - node_mem[pcdimm_info->node].node_plugged_mem += - pcdimm_info->size; - } + node_mem[pcdimm_info->node].node_plugged_mem += + pcdimm_info->size; } } } From a1a98357e3fdfce92b5ed0c6728489b9992fecb5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Jun 2018 16:51:39 +0200 Subject: [PATCH 1691/2380] osdep: work around Coverity parsing errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity does not like the new _Float* types that are used by recent glibc, and croaks on every single file that includes stdlib.h. Add dummy typedefs to please it. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/qemu/osdep.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 9ed62423c0..a91068df0e 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -33,6 +33,21 @@ #else #include "exec/poison.h" #endif +#ifdef __COVERITY__ +/* Coverity does not like the new _Float* types that are used by + * recent glibc, and croaks on every single file that includes + * stdlib.h. These typedefs are enough to please it. + * + * Note that these fix parse errors so they cannot be placed in + * scripts/coverity-model.c. + */ +typedef float _Float32; +typedef double _Float32x; +typedef double _Float64; +typedef __float80 _Float64x; +typedef __float128 _Float128; +#endif + #include "qemu/compiler.h" /* Older versions of C++ don't get definitions of various macros from From c44df2ff9be326b218f6655ee17ddd914ece8d5a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 25 Jun 2018 20:22:13 +0200 Subject: [PATCH 1692/2380] Deprecate the -enable-hax option We currently have got three ways of turning on the HAX accelerator: "-machine accel=hax", "-accel hax" and "-enable-hax". That's really confusing and overloaded. Since "-accel" is our preferred way to enable an accelerator nowadays, and "-accel hax" is even less to type than "-enable-hax", let's deprecate the "-enable-hax" option now. Note: While "-enable-kvm" is available since a long time and can hardly be removed since it is used in a lot of upper layer tools and scripts, the "-enable-hax" option is still rather new and not very widespread yet, so I think that it should be OK if we remove this in a couple of releases again (we'll see whether someone complains after seeing the deprecation message - then we could still reconsider to keep it if there a well-founded reasons). Signed-off-by: Thomas Huth Message-Id: <1529950933-28347-1-git-send-email-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- qemu-doc.texi | 5 +++++ qemu-options.hx | 2 +- vl.c | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/qemu-doc.texi b/qemu-doc.texi index 16fcb47901..1cb3ba4341 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2912,6 +2912,11 @@ Option @option{-virtioconsole} has been replaced by The @code{-clock} option is ignored since QEMU version 1.7.0. There is no replacement since it is not needed anymore. +@subsection -enable-hax (since 3.0.0) + +The @option{-enable-hax} option has been replaced by @option{-accel hax}. +Both options have been introduced in QEMU version 2.9.0. + @section QEMU Machine Protocol (QMP) commands @subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) diff --git a/qemu-options.hx b/qemu-options.hx index 270772817a..3e45483834 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3421,7 +3421,7 @@ STEXI Enable HAX (Hardware-based Acceleration eXecution) support. This option is only available if HAX support is enabled when compiling. HAX is only applicable to MAC and Windows platform, and thus does not conflict with -KVM. +KVM. This option is deprecated, use @option{-accel hax} instead. ETEXI DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, diff --git a/vl.c b/vl.c index d26f19b06d..7c9f19aa31 100644 --- a/vl.c +++ b/vl.c @@ -3581,6 +3581,7 @@ int main(int argc, char **argv, char **envp) qemu_opts_parse_noisily(olist, "accel=kvm", false); break; case QEMU_OPTION_enable_hax: + warn_report("Option is deprecated, use '-accel hax' instead"); olist = qemu_find_opts("machine"); qemu_opts_parse_noisily(olist, "accel=hax", false); break; From 50fa332516d5e42695811f43396b749185e21b9c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 26 Jun 2018 13:55:04 +0200 Subject: [PATCH 1693/2380] pr-helper: fix --socket-path default in help MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently --help shows "(default '(null)')" for the -k/--socket-path option. Fix it by getting the default path in /var/run. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé --- scsi/qemu-pr-helper.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index d0f83176e1..4057cf355c 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -74,8 +74,16 @@ static int uid = -1; static int gid = -1; #endif +static void compute_default_paths(void) +{ + if (!socket_path) { + socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); + } +} + static void usage(const char *name) { + compute_default_paths(); (printf) ( "Usage: %s [OPTIONS] FILE\n" "Persistent Reservation helper program for QEMU\n" @@ -845,13 +853,6 @@ static const char *socket_activation_validate_opts(void) return NULL; } -static void compute_default_paths(void) -{ - if (!socket_path) { - socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); - } -} - static void termsig_handler(int signum) { atomic_cmpxchg(&state, RUNNING, TERMINATE); From 86933b4e7879e427e03365bf352c0964640cb37b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Jun 2018 19:36:15 +0200 Subject: [PATCH 1694/2380] pr-helper: fix assertion failure on failed multipath PERSISTENT RESERVE IN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The response size is expected to be zero if the SCSI status is not "GOOD", but nothing was resetting it. This can be reproduced simply by "sg_persist -s /dev/sdb" where /dev/sdb in the guest is a scsi-block device corresponding to a multipath device on the host. Before: PR in (Read full status): Aborted command and on the host: prh_write_response: Assertion `resp->sz == 0' failed. After: PR in (Read full status): bad field in cdb or parameter list (perhaps unsupported service action) Reported-by: Jiri Belka Reviewed-by: Michal Privoznik Signed-off-by: Paolo Bonzini Reviewed-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé --- scsi/qemu-pr-helper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index 4057cf355c..0218d65bbf 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -558,7 +558,11 @@ static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, #ifdef CONFIG_MPATH if (is_mpath(fd)) { /* multipath_pr_in fills the whole input buffer. */ - return multipath_pr_in(fd, cdb, sense, data, *resp_sz); + int r = multipath_pr_in(fd, cdb, sense, data, *resp_sz); + if (r != GOOD) { + *resp_sz = 0; + } + return r; } #endif From aad10040d411d21542dc9ae58a2854c89ccedd78 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 26 Jun 2018 15:39:18 +0200 Subject: [PATCH 1695/2380] pr-manager-helper: avoid SIGSEGV when writing to the socket fail When writing to the qemu-pr-helper socket failed, the persistent reservation manager was correctly disconnecting the socket, but it did not clear pr_mgr->ioc. So the rest of the code did not know that the socket had been disconnected, accessed pr_mgr->ioc and happily caused a crash. To reproduce, it is enough to stop qemu-pr-helper between QEMU startup and executing e.g. sg_persist -k /dev/sdb. Reviewed-by: Michal Privoznik Signed-off-by: Paolo Bonzini --- scsi/pr-manager-helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c index 82ff6b6123..0c0fe389b7 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -71,6 +71,7 @@ static int pr_manager_helper_write(PRManagerHelper *pr_mgr, if (n_written <= 0) { assert(n_written != QIO_CHANNEL_ERR_BLOCK); object_unref(OBJECT(pr_mgr->ioc)); + pr_mgr->ioc = NULL; return n_written < 0 ? -EINVAL : 0; } From 58b3017f7fba15e8c440115dfd5d380f490d0b61 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 Jun 2018 18:01:42 +0200 Subject: [PATCH 1696/2380] pr-manager: put stubs in .c file Signed-off-by: Paolo Bonzini --- include/scsi/pr-manager.h | 9 --------- scsi/Makefile.objs | 1 + scsi/pr-manager-stub.c | 24 ++++++++++++++++++++++++ 3 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 scsi/pr-manager-stub.c diff --git a/include/scsi/pr-manager.h b/include/scsi/pr-manager.h index 5d2f13a5e4..71971ae34a 100644 --- a/include/scsi/pr-manager.h +++ b/include/scsi/pr-manager.h @@ -41,15 +41,6 @@ BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, BlockCompletionFunc *complete, void *opaque); -#ifdef CONFIG_LINUX PRManager *pr_manager_lookup(const char *id, Error **errp); -#else -static inline PRManager *pr_manager_lookup(const char *id, Error **errp) -{ - /* The classes do not exist at all! */ - error_setg(errp, "No persistent reservation manager with id '%s'", id); - return NULL; -} -#endif #endif diff --git a/scsi/Makefile.objs b/scsi/Makefile.objs index 4d25e476cf..bb8789cd8b 100644 --- a/scsi/Makefile.objs +++ b/scsi/Makefile.objs @@ -1,3 +1,4 @@ block-obj-y += utils.o block-obj-$(CONFIG_LINUX) += pr-manager.o pr-manager-helper.o +block-obj-$(call lnot,$(CONFIG_LINUX)) += pr-manager-stub.o diff --git a/scsi/pr-manager-stub.c b/scsi/pr-manager-stub.c new file mode 100644 index 0000000000..632f17c7f9 --- /dev/null +++ b/scsi/pr-manager-stub.c @@ -0,0 +1,24 @@ +/* + * Persistent reservation manager - stub for non-Linux platforms + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * This code is licensed under the LGPL. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "scsi/pr-manager.h" +#include "trace.h" +#include "qapi/qapi-types-block.h" +#include "qapi/qapi-commands-block.h" + +PRManager *pr_manager_lookup(const char *id, Error **errp) +{ + /* The classes do not exist at all! */ + error_setg(errp, "No persistent reservation manager with id '%s'", id); + return NULL; +} From 5f64089416f0d77c87683401838f064c51a292ed Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 28 Feb 2018 18:47:57 +0100 Subject: [PATCH 1697/2380] pr-manager: add query-pr-managers QMP command This command lets you query the connection status of each pr-manager-helper object. Signed-off-by: Paolo Bonzini --- include/scsi/pr-manager.h | 2 ++ qapi/block.json | 28 ++++++++++++++++++++++++ scsi/pr-manager-helper.c | 13 +++++++++++ scsi/pr-manager-stub.c | 6 ++++++ scsi/pr-manager.c | 45 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+) diff --git a/include/scsi/pr-manager.h b/include/scsi/pr-manager.h index 71971ae34a..50a77b08fc 100644 --- a/include/scsi/pr-manager.h +++ b/include/scsi/pr-manager.h @@ -33,8 +33,10 @@ typedef struct PRManagerClass { /* */ int (*run)(PRManager *pr_mgr, int fd, struct sg_io_hdr *hdr); + bool (*is_connected)(PRManager *pr_mgr); } PRManagerClass; +bool pr_manager_is_connected(PRManager *pr_mgr); BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, AioContext *ctx, int fd, struct sg_io_hdr *hdr, diff --git a/qapi/block.json b/qapi/block.json index ca807f176a..8765c29a06 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -77,6 +77,34 @@ { 'struct': 'BlockdevSnapshotInternal', 'data': { 'device': 'str', 'name': 'str' } } +## +# @PRManagerInfo: +# +# Information about a persistent reservation manager +# +# @id: the identifier of the persistent reservation manager +# +# @connected: true if the persistent reservation manager is connected to +# the underlying storage or helper +# +# Since: 3.0 +## +{ 'struct': 'PRManagerInfo', + 'data': {'id': 'str', 'connected': 'bool'} } + +## +# @query-pr-managers: +# +# Returns a list of information about each persistent reservation manager. +# +# Returns: a list of @PRManagerInfo for each persistent reservation manager +# +# Since: 3.0 +## +{ 'command': 'query-pr-managers', 'returns': ['PRManagerInfo'], + 'allow-preconfig': true } + + ## # @blockdev-snapshot-internal-sync: # diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c index 0c0fe389b7..b11481be9e 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -235,6 +235,18 @@ out: return ret; } +static bool pr_manager_helper_is_connected(PRManager *p) +{ + PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(p); + bool result; + + qemu_mutex_lock(&pr_mgr->lock); + result = (pr_mgr->ioc != NULL); + qemu_mutex_unlock(&pr_mgr->lock); + + return result; +} + static void pr_manager_helper_complete(UserCreatable *uc, Error **errp) { PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(uc); @@ -284,6 +296,7 @@ static void pr_manager_helper_class_init(ObjectClass *klass, &error_abort); uc_klass->complete = pr_manager_helper_complete; prmgr_klass->run = pr_manager_helper_run; + prmgr_klass->is_connected = pr_manager_helper_is_connected; } static const TypeInfo pr_manager_helper_info = { diff --git a/scsi/pr-manager-stub.c b/scsi/pr-manager-stub.c index 632f17c7f9..738b6d7425 100644 --- a/scsi/pr-manager-stub.c +++ b/scsi/pr-manager-stub.c @@ -22,3 +22,9 @@ PRManager *pr_manager_lookup(const char *id, Error **errp) error_setg(errp, "No persistent reservation manager with id '%s'", id); return NULL; } + + +PRManagerInfoList *qmp_query_pr_managers(Error **errp) +{ + return NULL; +} diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c index 87c45db5d4..2a8f300dde 100644 --- a/scsi/pr-manager.c +++ b/scsi/pr-manager.c @@ -17,6 +17,10 @@ #include "block/thread-pool.h" #include "scsi/pr-manager.h" #include "trace.h" +#include "qapi/qapi-types-block.h" +#include "qapi/qapi-commands-block.h" + +#define PR_MANAGER_PATH "/objects" typedef struct PRManagerData { PRManager *pr_mgr; @@ -64,6 +68,14 @@ BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, data, complete, opaque); } +bool pr_manager_is_connected(PRManager *pr_mgr) +{ + PRManagerClass *pr_mgr_class = + PR_MANAGER_GET_CLASS(pr_mgr); + + return !pr_mgr_class->is_connected || pr_mgr_class->is_connected(pr_mgr); +} + static const TypeInfo pr_manager_info = { .parent = TYPE_OBJECT, .name = TYPE_PR_MANAGER, @@ -105,5 +117,38 @@ pr_manager_register_types(void) type_register_static(&pr_manager_info); } +static int query_one_pr_manager(Object *object, void *opaque) +{ + PRManagerInfoList ***prev = opaque; + PRManagerInfoList *elem; + PRManagerInfo *info; + PRManager *pr_mgr; + + pr_mgr = (PRManager *)object_dynamic_cast(object, TYPE_PR_MANAGER); + if (!pr_mgr) { + return 0; + } + + elem = g_new0(PRManagerInfoList, 1); + info = g_new0(PRManagerInfo, 1); + info->id = object_get_canonical_path_component(object); + info->connected = pr_manager_is_connected(pr_mgr); + elem->value = info; + elem->next = NULL; + + **prev = elem; + *prev = &elem->next; + return 0; +} + +PRManagerInfoList *qmp_query_pr_managers(Error **errp) +{ + PRManagerInfoList *head = NULL; + PRManagerInfoList **prev = &head; + Object *container = container_get(object_get_root(), PR_MANAGER_PATH); + + object_child_foreach(container, query_one_pr_manager, &prev); + return head; +} type_init(pr_manager_register_types); From e2c81a45101fdddfd47477a1805806f2c76639bf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 28 Feb 2018 19:01:40 +0100 Subject: [PATCH 1698/2380] pr-manager-helper: report event on connection/disconnection Let management know if there were any problems communicating with qemu-pr-helper. The event is edge-triggered, and is sent every time the connection status of the pr-manager-helper object changes. Signed-off-by: Paolo Bonzini --- qapi/block.json | 24 ++++++++++++++++++++++++ scsi/pr-manager-helper.c | 14 ++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/qapi/block.json b/qapi/block.json index 8765c29a06..11f01f28ef 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -358,6 +358,30 @@ { 'event': 'DEVICE_TRAY_MOVED', 'data': { 'device': 'str', 'id': 'str', 'tray-open': 'bool' } } +## +# @PR_MANAGER_STATUS_CHANGED: +# +# Emitted whenever the connected status of a persistent reservation +# manager changes. +# +# @id: The id of the PR manager object +# +# @connected: true if the PR manager is connected to a backend +# +# Since: 3.0 +# +# Example: +# +# <- { "event": "PR_MANAGER_STATUS_CHANGED", +# "data": { "id": "pr-helper0", +# "connected": true +# }, +# "timestamp": { "seconds": 1519840375, "microseconds": 450486 } } +# +## +{ 'event': 'PR_MANAGER_STATUS_CHANGED', + 'data': { 'id': 'str', 'connected': 'bool' } } + ## # @QuorumOpType: # diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c index b11481be9e..519a296905 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -17,6 +17,7 @@ #include "io/channel.h" #include "io/channel-socket.h" #include "pr-helper.h" +#include "qapi/qapi-events-block.h" #include @@ -38,6 +39,16 @@ typedef struct PRManagerHelper { QIOChannel *ioc; } PRManagerHelper; +static void pr_manager_send_status_changed_event(PRManagerHelper *pr_mgr) +{ + char *id = object_get_canonical_path_component(OBJECT(pr_mgr)); + + if (id) { + qapi_event_send_pr_manager_status_changed(id, !!pr_mgr->ioc, + &error_abort); + } +} + /* Called with lock held. */ static int pr_manager_helper_read(PRManagerHelper *pr_mgr, void *buf, int sz, Error **errp) @@ -47,6 +58,7 @@ static int pr_manager_helper_read(PRManagerHelper *pr_mgr, if (r < 0) { object_unref(OBJECT(pr_mgr->ioc)); pr_mgr->ioc = NULL; + pr_manager_send_status_changed_event(pr_mgr); return -EINVAL; } @@ -72,6 +84,7 @@ static int pr_manager_helper_write(PRManagerHelper *pr_mgr, assert(n_written != QIO_CHANNEL_ERR_BLOCK); object_unref(OBJECT(pr_mgr->ioc)); pr_mgr->ioc = NULL; + pr_manager_send_status_changed_event(pr_mgr); return n_written < 0 ? -EINVAL : 0; } @@ -127,6 +140,7 @@ static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr, goto out_close; } + pr_manager_send_status_changed_event(pr_mgr); return 0; out_close: From 09eb69a573521b90cfa5b2c1c02e01adceb5405f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 13 Jun 2018 10:47:26 +0100 Subject: [PATCH 1699/2380] hw/mips/jazz: create ESP device directly via qdev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MIPS jazz is the last user of the legacy esp_init() function so move creation of the ESP device over to use qdev. Note that the esp_reset and dma_enable qemu_irqs are currently unused and so we do not wire these up and instead remove the variables to prevent the compiler emitting unused variable warnings. Signed-off-by: Mark Cave-Ayland Message-Id: <20180613094727.11326-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini Tested-by: Hervé Poussineau --- hw/mips/mips_jazz.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index 90cb306f53..1afbe3ce6a 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -145,10 +145,10 @@ static void mips_jazz_init(MachineState *machine, ISABus *isa_bus; ISADevice *pit; DriveInfo *fds[MAX_FD]; - qemu_irq esp_reset, dma_enable; MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); MemoryRegion *bios2 = g_new(MemoryRegion, 1); + SysBusESPState *sysbus_esp; ESPState *esp; /* init CPUs */ @@ -281,8 +281,21 @@ static void mips_jazz_init(MachineState *machine, } /* SCSI adapter */ - esp = esp_init(0x80002000, 0, rc4030_dma_read, rc4030_dma_write, dmas[0], - qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable); + dev = qdev_create(NULL, TYPE_ESP); + sysbus_esp = ESP_STATE(dev); + esp = &sysbus_esp->esp; + esp->dma_memory_read = rc4030_dma_read; + esp->dma_memory_write = rc4030_dma_write; + esp->dma_opaque = dmas[0]; + sysbus_esp->it_shift = 0; + /* XXX for now until rc4030 has been changed to use DMA enable signal */ + esp->dma_enabled = 1; + qdev_init_nofail(dev); + + sysbus = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 5)); + sysbus_mmio_map(sysbus, 0, 0x80002000); + scsi_bus_legacy_handle_cmdline(&esp->bus); /* Floppy */ From e7d99825f018cf4e658c3eb10c0163e75e653a23 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 13 Jun 2018 10:47:27 +0100 Subject: [PATCH 1700/2380] esp: remove legacy esp_init() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the legacy esp_init() function now that there are no more remaining users. Signed-off-by: Mark Cave-Ayland Message-Id: <20180613094727.11326-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini Tested-by: Hervé Poussineau --- hw/scsi/esp.c | 30 ------------------------------ include/hw/scsi/esp.h | 5 ----- 2 files changed, 35 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 9ed9727744..630d923623 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -619,36 +619,6 @@ static const MemoryRegionOps sysbus_esp_mem_ops = { .valid.accepts = esp_mem_accepts, }; -ESPState *esp_init(hwaddr espaddr, int it_shift, - ESPDMAMemoryReadWriteFunc dma_memory_read, - ESPDMAMemoryReadWriteFunc dma_memory_write, - void *dma_opaque, qemu_irq irq, qemu_irq *reset, - qemu_irq *dma_enable) -{ - DeviceState *dev; - SysBusDevice *s; - SysBusESPState *sysbus; - ESPState *esp; - - dev = qdev_create(NULL, TYPE_ESP); - sysbus = ESP_STATE(dev); - esp = &sysbus->esp; - esp->dma_memory_read = dma_memory_read; - esp->dma_memory_write = dma_memory_write; - esp->dma_opaque = dma_opaque; - sysbus->it_shift = it_shift; - /* XXX for now until rc4030 has been changed to use DMA enable signal */ - esp->dma_enabled = 1; - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - sysbus_mmio_map(s, 0, espaddr); - *reset = qdev_get_gpio_in(dev, 0); - *dma_enable = qdev_get_gpio_in(dev, 1); - - return esp; -} - static const struct SCSIBusInfo esp_scsi_info = { .tcq = false, .max_target = ESP_MAX_DEVS, diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index 93fdaced67..682a0d2de0 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -131,11 +131,6 @@ typedef struct { #define TCHI_FAS100A 0x4 #define TCHI_AM53C974 0x12 -ESPState *esp_init(hwaddr espaddr, int it_shift, - ESPDMAMemoryReadWriteFunc dma_memory_read, - ESPDMAMemoryReadWriteFunc dma_memory_write, - void *dma_opaque, qemu_irq irq, qemu_irq *reset, - qemu_irq *dma_enable); void esp_dma_enable(ESPState *s, int irq, int level); void esp_request_cancelled(SCSIRequest *req); void esp_command_complete(SCSIRequest *req, uint32_t status, size_t resid); From e1753a7e1d8174f5861367504c5cea5fbcd85953 Mon Sep 17 00:00:00 2001 From: "Justin Terry (VM)" Date: Tue, 5 Jun 2018 22:15:27 +0000 Subject: [PATCH 1701/2380] WHPX workaround bug in OSVW handling Adds a workaround to an incorrect value setting CPUID Fn8000_0001_ECX[bit 9 OSVW] = 1. This can cause a guest linux kernel to panic when an issue to rdmsr C001_0140h returns 0. Disabling this feature correctly allows the guest to boot without accessing the osv workarounds. Signed-off-by: Justin Terry (VM) Message-Id: <20180605221500.21674-1-juterry@microsoft.com> Signed-off-by: Paolo Bonzini --- target/i386/whpx-all.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c index 6b42096698..99501bac57 100644 --- a/target/i386/whpx-all.c +++ b/target/i386/whpx-all.c @@ -961,6 +961,16 @@ static int whpx_vcpu_run(CPUState *cpu) vcpu->exit_ctx.CpuidAccess.DefaultResultRcx | CPUID_EXT_HYPERVISOR; + rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx; + rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx; + break; + case 0x80000001: + rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax; + /* Remove any support of OSVW */ + rcx = + vcpu->exit_ctx.CpuidAccess.DefaultResultRcx & + ~CPUID_EXT3_OSVW; + rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx; rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx; break; @@ -1382,12 +1392,13 @@ static int whpx_accel_init(MachineState *ms) goto error; } - UINT32 cpuidExitList[] = {1}; + UINT32 cpuidExitList[] = {1, 0x80000001}; hr = whp_dispatch.WHvSetPartitionProperty( whpx->partition, WHvPartitionPropertyCodeCpuidExitList, cpuidExitList, RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32)); + if (FAILED(hr)) { error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx", hr); From e7ca549fc8caf9b1c79814f3854622448815f2bf Mon Sep 17 00:00:00 2001 From: "Justin Terry (VM)" Date: Tue, 5 Jun 2018 22:15:28 +0000 Subject: [PATCH 1702/2380] WHPX: register for unrecognized MSR exits Some variations of Linux kernels end up accessing MSR's that the Windows Hypervisor doesn't implement which causes a GP to be returned to the guest. This fix registers QEMU for unimplemented MSR access and globally returns 0 on reads and ignores writes. This behavior is allows the Linux kernel to probe the MSR with a write/read/check sequence it does often without failing the access. Signed-off-by: Justin Terry (VM) Message-Id: <20180605221500.21674-2-juterry@microsoft.com> Signed-off-by: Paolo Bonzini --- target/i386/whpx-all.c | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c index 99501bac57..57e53e1f1f 100644 --- a/target/i386/whpx-all.c +++ b/target/i386/whpx-all.c @@ -932,6 +932,7 @@ static int whpx_vcpu_run(CPUState *cpu) case WHvRunVpExitReasonX64InterruptWindow: vcpu->window_registered = 0; + ret = 0; break; case WHvRunVpExitReasonX64Halt: @@ -943,6 +944,40 @@ static int whpx_vcpu_run(CPUState *cpu) ret = 1; break; + case WHvRunVpExitReasonX64MsrAccess: { + WHV_REGISTER_VALUE reg_values[3] = {0}; + WHV_REGISTER_NAME reg_names[3]; + UINT32 reg_count; + + reg_names[0] = WHvX64RegisterRip; + reg_names[1] = WHvX64RegisterRax; + reg_names[2] = WHvX64RegisterRdx; + + reg_values[0].Reg64 = + vcpu->exit_ctx.VpContext.Rip + + vcpu->exit_ctx.VpContext.InstructionLength; + + /* + * For all unsupported MSR access we: + * ignore writes + * return 0 on read. + */ + reg_count = vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite ? + 1 : 3; + + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, + cpu->cpu_index, + reg_names, reg_count, + reg_values); + + if (FAILED(hr)) { + error_report("WHPX: Failed to set MsrAccess state " + " registers, hr=%08lx", hr); + } + ret = 0; + break; + } case WHvRunVpExitReasonX64Cpuid: { WHV_REGISTER_VALUE reg_values[5]; WHV_REGISTER_NAME reg_names[5]; @@ -1010,7 +1045,6 @@ static int whpx_vcpu_run(CPUState *cpu) case WHvRunVpExitReasonUnrecoverableException: case WHvRunVpExitReasonInvalidVpRegisterValue: case WHvRunVpExitReasonUnsupportedFeature: - case WHvRunVpExitReasonX64MsrAccess: case WHvRunVpExitReasonException: default: error_report("WHPX: Unexpected VP exit code %d", @@ -1378,6 +1412,7 @@ static int whpx_accel_init(MachineState *ms) } memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY)); + prop.ExtendedVmExits.X64MsrExit = 1; prop.ExtendedVmExits.X64CpuidExit = 1; hr = whp_dispatch.WHvSetPartitionProperty( whpx->partition, @@ -1386,8 +1421,8 @@ static int whpx_accel_init(MachineState *ms) sizeof(WHV_PARTITION_PROPERTY)); if (FAILED(hr)) { - error_report("WHPX: Failed to enable partition extended X64CpuidExit" - " hr=%08lx", hr); + error_report("WHPX: Failed to enable partition extended X64MsrExit and" + " X64CpuidExit hr=%08lx", hr); ret = -EINVAL; goto error; } From fc051ae6c42216ca87145106b509fa3bdfa98e00 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Mon, 4 Jun 2018 13:25:11 +1000 Subject: [PATCH 1703/2380] memory/hmp: Print owners/parents in "info mtree" This adds owners/parents (which are the same, just occasionally owner==NULL) printing for memory regions; a new '-o' flag enabled new output. Signed-off-by: Alexey Kardashevskiy Message-Id: <20180604032511.6980-1-aik@ozlabs.ru> Signed-off-by: Paolo Bonzini --- hmp-commands-info.hx | 7 +++-- include/exec/memory.h | 2 +- memory.c | 72 +++++++++++++++++++++++++++++++++++++------ monitor.c | 4 ++- 4 files changed, 70 insertions(+), 15 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 6db3457a78..59bdd8f713 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -253,10 +253,11 @@ ETEXI { .name = "mtree", - .args_type = "flatview:-f,dispatch_tree:-d", - .params = "[-f][-d]", + .args_type = "flatview:-f,dispatch_tree:-d,owner:-o", + .params = "[-f][-d][-o]", .help = "show memory tree (-f: dump flat view for address spaces;" - "-d: dump dispatch tree, valid with -f only)", + "-d: dump dispatch tree, valid with -f only);" + "-o: dump region owners/parents", .cmd = hmp_info_mtree, }, diff --git a/include/exec/memory.h b/include/exec/memory.h index 050323f532..448d41a752 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1665,7 +1665,7 @@ void memory_global_dirty_log_start(void); void memory_global_dirty_log_stop(void); void mtree_info(fprintf_function mon_printf, void *f, bool flatview, - bool dispatch_tree); + bool dispatch_tree, bool owner); /** * memory_region_request_mmio_ptr: request a pointer to an mmio diff --git a/memory.c b/memory.c index 21aa57d24c..e9cd446968 100644 --- a/memory.c +++ b/memory.c @@ -2858,10 +2858,49 @@ typedef QTAILQ_HEAD(mrqueue, MemoryRegionList) MemoryRegionListHead; int128_sub((size), int128_one())) : 0) #define MTREE_INDENT " " +static void mtree_expand_owner(fprintf_function mon_printf, void *f, + const char *label, Object *obj) +{ + DeviceState *dev = (DeviceState *) object_dynamic_cast(obj, TYPE_DEVICE); + + mon_printf(f, " %s:{%s", label, dev ? "dev" : "obj"); + if (dev && dev->id) { + mon_printf(f, " id=%s", dev->id); + } else { + gchar *canonical_path = object_get_canonical_path(obj); + if (canonical_path) { + mon_printf(f, " path=%s", canonical_path); + g_free(canonical_path); + } else { + mon_printf(f, " type=%s", object_get_typename(obj)); + } + } + mon_printf(f, "}"); +} + +static void mtree_print_mr_owner(fprintf_function mon_printf, void *f, + const MemoryRegion *mr) +{ + Object *owner = mr->owner; + Object *parent = memory_region_owner((MemoryRegion *)mr); + + if (!owner && !parent) { + mon_printf(f, " orphan"); + return; + } + if (owner) { + mtree_expand_owner(mon_printf, f, "owner", owner); + } + if (parent && parent != owner) { + mtree_expand_owner(mon_printf, f, "parent", parent); + } +} + static void mtree_print_mr(fprintf_function mon_printf, void *f, const MemoryRegion *mr, unsigned int level, hwaddr base, - MemoryRegionListHead *alias_print_queue) + MemoryRegionListHead *alias_print_queue, + bool owner) { MemoryRegionList *new_ml, *ml, *next_ml; MemoryRegionListHead submr_print_queue; @@ -2907,7 +2946,7 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, } mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s): alias %s @%s " TARGET_FMT_plx - "-" TARGET_FMT_plx "%s\n", + "-" TARGET_FMT_plx "%s", cur_start, cur_end, mr->priority, memory_region_type((MemoryRegion *)mr), @@ -2916,15 +2955,22 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, mr->alias_offset, mr->alias_offset + MR_SIZE(mr->size), mr->enabled ? "" : " [disabled]"); + if (owner) { + mtree_print_mr_owner(mon_printf, f, mr); + } } else { mon_printf(f, - TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s): %s%s\n", + TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s): %s%s", cur_start, cur_end, mr->priority, memory_region_type((MemoryRegion *)mr), memory_region_name(mr), mr->enabled ? "" : " [disabled]"); + if (owner) { + mtree_print_mr_owner(mon_printf, f, mr); + } } + mon_printf(f, "\n"); QTAILQ_INIT(&submr_print_queue); @@ -2947,7 +2993,7 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, QTAILQ_FOREACH(ml, &submr_print_queue, mrqueue) { mtree_print_mr(mon_printf, f, ml->mr, level + 1, cur_start, - alias_print_queue); + alias_print_queue, owner); } QTAILQ_FOREACH_SAFE(ml, &submr_print_queue, mrqueue, next_ml) { @@ -2960,6 +3006,7 @@ struct FlatViewInfo { void *f; int counter; bool dispatch_tree; + bool owner; }; static void mtree_print_flatview(gpointer key, gpointer value, @@ -3000,7 +3047,7 @@ static void mtree_print_flatview(gpointer key, gpointer value, mr = range->mr; if (range->offset_in_region) { p(f, MTREE_INDENT TARGET_FMT_plx "-" - TARGET_FMT_plx " (prio %d, %s): %s @" TARGET_FMT_plx "\n", + TARGET_FMT_plx " (prio %d, %s): %s @" TARGET_FMT_plx, int128_get64(range->addr.start), int128_get64(range->addr.start) + MR_SIZE(range->addr.size), mr->priority, @@ -3009,13 +3056,17 @@ static void mtree_print_flatview(gpointer key, gpointer value, range->offset_in_region); } else { p(f, MTREE_INDENT TARGET_FMT_plx "-" - TARGET_FMT_plx " (prio %d, %s): %s\n", + TARGET_FMT_plx " (prio %d, %s): %s", int128_get64(range->addr.start), int128_get64(range->addr.start) + MR_SIZE(range->addr.size), mr->priority, range->readonly ? "rom" : memory_region_type(mr), memory_region_name(mr)); } + if (fvi->owner) { + mtree_print_mr_owner(p, f, mr); + } + p(f, "\n"); range++; } @@ -3041,7 +3092,7 @@ static gboolean mtree_info_flatview_free(gpointer key, gpointer value, } void mtree_info(fprintf_function mon_printf, void *f, bool flatview, - bool dispatch_tree) + bool dispatch_tree, bool owner) { MemoryRegionListHead ml_head; MemoryRegionList *ml, *ml2; @@ -3053,7 +3104,8 @@ void mtree_info(fprintf_function mon_printf, void *f, bool flatview, .mon_printf = mon_printf, .f = f, .counter = 0, - .dispatch_tree = dispatch_tree + .dispatch_tree = dispatch_tree, + .owner = owner, }; GArray *fv_address_spaces; GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal); @@ -3085,14 +3137,14 @@ void mtree_info(fprintf_function mon_printf, void *f, bool flatview, QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) { mon_printf(f, "address-space: %s\n", as->name); - mtree_print_mr(mon_printf, f, as->root, 1, 0, &ml_head); + mtree_print_mr(mon_printf, f, as->root, 1, 0, &ml_head, owner); mon_printf(f, "\n"); } /* print aliased regions */ QTAILQ_FOREACH(ml, &ml_head, mrqueue) { mon_printf(f, "memory-region: %s\n", memory_region_name(ml->mr)); - mtree_print_mr(mon_printf, f, ml->mr, 1, 0, &ml_head); + mtree_print_mr(mon_printf, f, ml->mr, 1, 0, &ml_head, owner); mon_printf(f, "\n"); } diff --git a/monitor.c b/monitor.c index 0730a27172..0988eb4788 100644 --- a/monitor.c +++ b/monitor.c @@ -2007,8 +2007,10 @@ static void hmp_info_mtree(Monitor *mon, const QDict *qdict) { bool flatview = qdict_get_try_bool(qdict, "flatview", false); bool dispatch_tree = qdict_get_try_bool(qdict, "dispatch_tree", false); + bool owner = qdict_get_try_bool(qdict, "owner", false); - mtree_info((fprintf_function)monitor_printf, mon, flatview, dispatch_tree); + mtree_info((fprintf_function)monitor_printf, mon, flatview, dispatch_tree, + owner); } static void hmp_info_numa(Monitor *mon, const QDict *qdict) From 02f7fd25a446a220905c2e5cb0fc3655d7f63b29 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 3 Apr 2018 17:36:11 +0200 Subject: [PATCH 1704/2380] target-i386: Add NMI interception to SVM Check for SVM interception prior to injecting an NMI. Tested via the Jailhouse hypervisor. Signed-off-by: Jan Kiszka Message-Id: Signed-off-by: Paolo Bonzini --- target/i386/seg_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c index 600a4d7586..00301a0c04 100644 --- a/target/i386/seg_helper.c +++ b/target/i386/seg_helper.c @@ -1337,6 +1337,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) ret = true; } else if ((interrupt_request & CPU_INTERRUPT_NMI) && !(env->hflags2 & HF2_NMI_MASK)) { + cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0); cs->interrupt_request &= ~CPU_INTERRUPT_NMI; env->hflags2 |= HF2_NMI_MASK; do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); From df2518aa587a0157bbfbc635fe47295629d9914a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 3 Apr 2018 17:36:12 +0200 Subject: [PATCH 1705/2380] target-i386: Allow interrupt injection after STGI We need to terminate the translation block after STGI so that pending interrupts can be injected. This fixes pending NMI injection for Jailhouse which uses "stgi; clgi" to open a brief injection window. Signed-off-by: Jan Kiszka Message-Id: <37939b244dda0e9cccf96ce50f2b15df1e48315d.1522769774.git.jan.kiszka@web.de> Signed-off-by: Paolo Bonzini --- target/i386/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/translate.c b/target/i386/translate.c index c91849417b..07d185e7b6 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -7444,8 +7444,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_stgi(cpu_env); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); break; case 0xdd: /* CLGI */ From 50b3de6e5cd464dcc20e3a48f5a09e0299a184ac Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 3 Apr 2018 17:36:13 +0200 Subject: [PATCH 1706/2380] target-i386: Mark cpu_vmexit noreturn It calls cpu_loop_exit in system emulation mode (and should never be called in user emulation mode). Signed-off-by: Jan Kiszka Message-Id: <6f4d44ffde55d074cbceb48309c1678600abad2f.1522769774.git.jan.kiszka@web.de> Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 4 ++-- target/i386/svm_helper.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 89c82be8d2..16c59b7099 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1840,8 +1840,8 @@ void helper_lock_init(void); /* svm_helper.c */ void cpu_svm_check_intercept_param(CPUX86State *env1, uint32_t type, uint64_t param, uintptr_t retaddr); -void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, - uintptr_t retaddr); +void QEMU_NORETURN cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, + uint64_t exit_info_1, uintptr_t retaddr); void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1); /* seg_helper.c */ diff --git a/target/i386/svm_helper.c b/target/i386/svm_helper.c index 350492359c..f245aec310 100644 --- a/target/i386/svm_helper.c +++ b/target/i386/svm_helper.c @@ -62,6 +62,7 @@ void helper_invlpga(CPUX86State *env, int aflag) void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, uintptr_t retaddr) { + assert(0); } void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, From 0cda9d876c7d4b05cac164020e8cbafa4adb3728 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 29 Dec 2017 15:31:00 +0800 Subject: [PATCH 1707/2380] doc: another fix to "info pic" Something that commit 254316fa1f ("intc: make HMP 'info irq' and 'info pic' commands available on all targets", 2016-10-04) forgot to touch up. Signed-off-by: Peter Xu Message-Id: <20171229073104.3810-2-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- hmp-commands-info.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 59bdd8f713..a482b6e56b 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -201,7 +201,7 @@ ETEXI STEXI @item info pic @findex info pic -Show i8259 (PIC) state. +Show PIC state. ETEXI { From 4a499ad295e007891d2a27ad21269aee8e698e58 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 29 Dec 2017 15:31:01 +0800 Subject: [PATCH 1708/2380] ioapic: support "info pic" People start to use "info pic" for all kinds of irqchip dumps. Let x86 ioapic join the family. It dumps the same thing as "info ioapic". Signed-off-by: Peter Xu Message-Id: <20171229073104.3810-3-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- hw/intc/ioapic_common.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index 3b3d0a7680..c62ba27018 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -24,6 +24,7 @@ #include "monitor/monitor.h" #include "hw/i386/ioapic.h" #include "hw/i386/ioapic_internal.h" +#include "hw/intc/intc.h" #include "hw/sysbus.h" /* ioapic_no count start from 0 to MAX_IOAPICS, @@ -142,6 +143,15 @@ static void ioapic_common_realize(DeviceState *dev, Error **errp) ioapic_no++; } +static void ioapic_print_info(InterruptStatsProvider *obj, + Monitor *mon) +{ + IOAPICCommonState *s = IOAPIC_COMMON(obj); + + ioapic_dispatch_pre_save(s); + ioapic_print_redtbl(mon, s); +} + static const VMStateDescription vmstate_ioapic_common = { .name = "ioapic", .version_id = 3, @@ -161,9 +171,11 @@ static const VMStateDescription vmstate_ioapic_common = { static void ioapic_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); dc->realize = ioapic_common_realize; dc->vmsd = &vmstate_ioapic_common; + ic->print_info = ioapic_print_info; } static const TypeInfo ioapic_common_type = { @@ -173,6 +185,10 @@ static const TypeInfo ioapic_common_type = { .class_size = sizeof(IOAPICCommonClass), .class_init = ioapic_common_class_init, .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_INTERRUPT_STATS_PROVIDER }, + { } + }, }; static void ioapic_common_register_types(void) From 6a218b032b2d62b3c13e9553593b75e445ce5f1a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 29 Dec 2017 15:31:02 +0800 Subject: [PATCH 1709/2380] ioapic: some proper indents when dump info So that now it looks better when with other irqchips. Signed-off-by: Peter Xu Message-Id: <20171229073104.3810-4-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- hw/intc/ioapic_common.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index c62ba27018..a02c135b24 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -59,7 +59,7 @@ void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) uint32_t remote_irr = 0; int i; - monitor_printf(mon, "ioapic ver=0x%x id=0x%02x sel=0x%02x", + monitor_printf(mon, "ioapic0: ver=0x%x id=0x%02x sel=0x%02x", s->version, s->id, s->ioregsel); if (s->ioregsel) { monitor_printf(mon, " (redir[%u])\n", @@ -71,7 +71,7 @@ void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) uint64_t entry = s->ioredtbl[i]; uint32_t delm = (uint32_t)((entry & IOAPIC_LVT_DELIV_MODE) >> IOAPIC_LVT_DELIV_MODE_SHIFT); - monitor_printf(mon, "pin %-2u 0x%016"PRIx64" dest=%"PRIx64 + monitor_printf(mon, " pin %-2u 0x%016"PRIx64" dest=%"PRIx64 " vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n", i, entry, (entry >> IOAPIC_LVT_DEST_SHIFT) & @@ -86,8 +86,8 @@ void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) remote_irr |= entry & IOAPIC_LVT_TRIGGER_MODE ? (entry & IOAPIC_LVT_REMOTE_IRR ? (1 << i) : 0) : 0; } - ioapic_irr_dump(mon, "IRR", s->irr); - ioapic_irr_dump(mon, "Remote IRR", remote_irr); + ioapic_irr_dump(mon, " IRR", s->irr); + ioapic_irr_dump(mon, " Remote IRR", remote_irr); } void ioapic_reset_common(DeviceState *dev) From cce5405e0ebce0cd400cfd3d3d218a776ac6b333 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 29 Dec 2017 15:31:03 +0800 Subject: [PATCH 1710/2380] ioapic: support "info irq" This include both userspace and in-kernel ioapic. Note that the numbers can be inaccurate for kvm-ioapic. One reason is the same with kvm-i8259, that when irqfd is used, irqs can be delivered all inside kernel without our notice. Meanwhile, kvm-ioapic is specially treated when irq numbers Message-Id: <20171229073104.3810-5-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/kvm/ioapic.c | 2 ++ hw/intc/ioapic.c | 1 + hw/intc/ioapic_common.c | 23 +++++++++++++++++++++++ include/hw/i386/ioapic_internal.h | 3 +++ 4 files changed, 29 insertions(+) diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 646f6245ee..5274dc709b 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -132,8 +132,10 @@ static void kvm_ioapic_reset(DeviceState *dev) static void kvm_ioapic_set_irq(void *opaque, int irq, int level) { KVMIOAPICState *s = opaque; + IOAPICCommonState *common = IOAPIC_COMMON(s); int delivered; + ioapic_stat_update_irq(common, irq, level); delivered = kvm_set_irq(kvm_state, s->kvm_gsi_base + irq, level); apic_report_irq_delivered(delivered); } diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index c45f073271..222f3f7d47 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -148,6 +148,7 @@ static void ioapic_set_irq(void *opaque, int vector, int level) * the cleanest way of doing it but it should work. */ trace_ioapic_set_irq(vector, level); + ioapic_stat_update_irq(s, vector, level); if (vector == 0) { vector = 2; } diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index a02c135b24..692dc37bb6 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -35,6 +35,28 @@ */ int ioapic_no; +void ioapic_stat_update_irq(IOAPICCommonState *s, int irq, int level) +{ + if (level != s->irq_level[irq]) { + s->irq_level[irq] = level; + if (level == 1) { + s->irq_count[irq]++; + } + } +} + +static bool ioapic_get_statistics(InterruptStatsProvider *obj, + uint64_t **irq_counts, + unsigned int *nb_irqs) +{ + IOAPICCommonState *s = IOAPIC_COMMON(obj); + + *irq_counts = s->irq_count; + *nb_irqs = IOAPIC_NUM_PINS; + + return true; +} + static void ioapic_irr_dump(Monitor *mon, const char *name, uint32_t bitmap) { int i; @@ -176,6 +198,7 @@ static void ioapic_common_class_init(ObjectClass *klass, void *data) dc->realize = ioapic_common_realize; dc->vmsd = &vmstate_ioapic_common; ic->print_info = ioapic_print_info; + ic->get_statistics = ioapic_get_statistics; } static const TypeInfo ioapic_common_type = { diff --git a/include/hw/i386/ioapic_internal.h b/include/hw/i386/ioapic_internal.h index a11d86de46..9848f391bb 100644 --- a/include/hw/i386/ioapic_internal.h +++ b/include/hw/i386/ioapic_internal.h @@ -109,10 +109,13 @@ struct IOAPICCommonState { uint64_t ioredtbl[IOAPIC_NUM_PINS]; Notifier machine_done; uint8_t version; + uint64_t irq_count[IOAPIC_NUM_PINS]; + int irq_level[IOAPIC_NUM_PINS]; }; void ioapic_reset_common(DeviceState *dev); void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s); +void ioapic_stat_update_irq(IOAPICCommonState *s, int irq, int level); #endif /* QEMU_IOAPIC_INTERNAL_H */ From 0c8465440d50c18a7bb13d0a866748f0593e193a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 29 Dec 2017 15:31:04 +0800 Subject: [PATCH 1711/2380] hmp: obsolete "info ioapic" Let's start to use "info pic" just like other platforms. For now we keep the command for a while so that old users can know what is the new command to use. Signed-off-by: Peter Xu Message-Id: <20171229073104.3810-6-peterx@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/kvm/ioapic.c | 9 --------- hw/intc/ioapic.c | 11 ----------- include/hw/i386/pc.h | 3 --- target/i386/monitor.c | 8 ++------ 4 files changed, 2 insertions(+), 29 deletions(-) diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 5274dc709b..5b40d75439 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -112,15 +112,6 @@ static void kvm_ioapic_put(IOAPICCommonState *s) } } -void kvm_ioapic_dump_state(Monitor *mon, const QDict *qdict) -{ - IOAPICCommonState *s = IOAPIC_COMMON(object_resolve_path("ioapic", NULL)); - - assert(s); - kvm_ioapic_get(s); - ioapic_print_redtbl(mon, s); -} - static void kvm_ioapic_reset(DeviceState *dev) { IOAPICCommonState *s = IOAPIC_COMMON(dev); diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 222f3f7d47..b3937807c2 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -234,17 +234,6 @@ void ioapic_eoi_broadcast(int vector) } } -void ioapic_dump_state(Monitor *mon, const QDict *qdict) -{ - int i; - - for (i = 0; i < MAX_IOAPICS; i++) { - if (ioapics[i] != 0) { - ioapic_print_redtbl(mon, ioapics[i]); - } - } -} - static uint64_t ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size) { diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 316230e570..4d99d69681 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -154,9 +154,6 @@ int pic_get_output(DeviceState *d); /* ioapic.c */ -void kvm_ioapic_dump_state(Monitor *mon, const QDict *qdict); -void ioapic_dump_state(Monitor *mon, const QDict *qdict); - /* Global System Interrupts */ #define GSI_NUM_PINS IOAPIC_NUM_PINS diff --git a/target/i386/monitor.c b/target/i386/monitor.c index a890b3c2ab..6bbb3a96cd 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -658,12 +658,8 @@ void hmp_info_local_apic(Monitor *mon, const QDict *qdict) void hmp_info_io_apic(Monitor *mon, const QDict *qdict) { - if (kvm_irqchip_in_kernel() && - !kvm_irqchip_is_split()) { - kvm_ioapic_dump_state(mon, qdict); - } else { - ioapic_dump_state(mon, qdict); - } + monitor_printf(mon, "This command is obsolete and will be " + "removed soon. Please use 'info pic' instead.\n"); } SevInfo *qmp_query_sev(Error **errp) From 6f131f13e68d648a8e4f083c667ab1acd88ce4cd Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 22 Jun 2018 22:22:05 +0300 Subject: [PATCH 1712/2380] kvm: support -overcommit cpu-pm=on|off With this flag, kvm allows guest to control host CPU power state. This increases latency for other processes using same host CPU in an unpredictable way, but if decreases idle entry/exit times for the running VCPU, so to use it QEMU needs a hint about whether host CPU is overcommitted, hence the flag name. Follow-up patches will expose this capability to guest (using mwait leaf). Based on a patch by Wanpeng Li . Signed-off-by: Michael S. Tsirkin Message-Id: <20180622192148.178309-2-mst@redhat.com> Signed-off-by: Paolo Bonzini --- include/sysemu/sysemu.h | 1 + qemu-options.hx | 24 ++++++++++++++++++++++++ target/i386/kvm.c | 23 +++++++++++++++++++++++ vl.c | 32 +++++++++++++++++++++++++++++++- 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index e893f72f3b..b921c6f3b7 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -128,6 +128,7 @@ extern bool boot_strict; extern uint8_t *boot_splash_filedata; extern size_t boot_splash_filedata_size; extern bool enable_mlock; +extern bool enable_cpu_pm; extern uint8_t qemu_extra_params_fw[2]; extern QEMUClockType rtc_clock; extern const char *mem_path; diff --git a/qemu-options.hx b/qemu-options.hx index 3e45483834..81b1e99d58 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3325,6 +3325,30 @@ mlocking qemu and guest memory can be enabled via @option{mlock=on} (enabled by default). ETEXI +DEF("overcommit", HAS_ARG, QEMU_OPTION_overcommit, + "--overcommit [mem-lock=on|off][cpu-pm=on|off]\n" + " run qemu with overcommit hints\n" + " mem-lock=on|off controls memory lock support (default: off)\n" + " cpu-pm=on|off controls cpu power management (default: off)\n", + QEMU_ARCH_ALL) +STEXI +@item -overcommit mem-lock=on|off +@item -overcommit cpu-pm=on|off +@findex -overcommit +Run qemu with hints about host resource overcommit. The default is +to assume that host overcommits all resources. + +Locking qemu and guest memory can be enabled via @option{mem-lock=on} (disabled +by default). This works when host memory is not overcommitted and reduces the +worst-case latency for guest. This is equivalent to @option{realtime}. + +Guest ability to manage power state of host cpus (increasing latency for other +processes on the same host cpu, but decreasing latency for guest) can be +enabled via @option{cpu-pm=on} (disabled by default). This works best when +host CPU is not overcommitted. When used, host estimates of CPU cycle and power +utilization will be incorrect, not taking into account guest idle time. +ETEXI + DEF("gdb", HAS_ARG, QEMU_OPTION_gdb, \ "-gdb dev wait for gdb connection on 'dev'\n", QEMU_ARCH_ALL) STEXI diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 2d174f3a91..dc991f6aca 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -1387,6 +1387,29 @@ int kvm_arch_init(MachineState *ms, KVMState *s) smram_machine_done.notify = register_smram_listener; qemu_add_machine_init_done_notifier(&smram_machine_done); } + + if (enable_cpu_pm) { + int disable_exits = kvm_check_extension(s, KVM_CAP_X86_DISABLE_EXITS); + int ret; + +/* Work around for kernel header with a typo. TODO: fix header and drop. */ +#if defined(KVM_X86_DISABLE_EXITS_HTL) && !defined(KVM_X86_DISABLE_EXITS_HLT) +#define KVM_X86_DISABLE_EXITS_HLT KVM_X86_DISABLE_EXITS_HTL +#endif + if (disable_exits) { + disable_exits &= (KVM_X86_DISABLE_EXITS_MWAIT | + KVM_X86_DISABLE_EXITS_HLT | + KVM_X86_DISABLE_EXITS_PAUSE); + } + + ret = kvm_vm_enable_cap(s, KVM_CAP_X86_DISABLE_EXITS, 0, + disable_exits); + if (ret < 0) { + error_report("kvm: guest stopping CPU not supported: %s", + strerror(-ret)); + } + } + return 0; } diff --git a/vl.c b/vl.c index 7c9f19aa31..ef6cfcec40 100644 --- a/vl.c +++ b/vl.c @@ -142,6 +142,7 @@ ram_addr_t ram_size; const char *mem_path = NULL; int mem_prealloc = 0; /* force preallocation of physical target memory */ bool enable_mlock = false; +bool enable_cpu_pm = false; int nb_nics; NICInfo nd_table[MAX_NICS]; int autostart; @@ -390,6 +391,22 @@ static QemuOptsList qemu_realtime_opts = { }, }; +static QemuOptsList qemu_overcommit_opts = { + .name = "overcommit", + .head = QTAILQ_HEAD_INITIALIZER(qemu_overcommit_opts.head), + .desc = { + { + .name = "mem-lock", + .type = QEMU_OPT_BOOL, + }, + { + .name = "cpu-pm", + .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +}; + static QemuOptsList qemu_msg_opts = { .name = "msg", .head = QTAILQ_HEAD_INITIALIZER(qemu_msg_opts.head), @@ -3906,7 +3923,20 @@ int main(int argc, char **argv, char **envp) if (!opts) { exit(1); } - enable_mlock = qemu_opt_get_bool(opts, "mlock", true); + /* Don't override the -overcommit option if set */ + enable_mlock = enable_mlock || + qemu_opt_get_bool(opts, "mlock", true); + break; + case QEMU_OPTION_overcommit: + opts = qemu_opts_parse_noisily(qemu_find_opts("overcommit"), + optarg, false); + if (!opts) { + exit(1); + } + /* Don't override the -realtime option if set */ + enable_mlock = enable_mlock || + qemu_opt_get_bool(opts, "mem-lock", false); + enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", false); break; case QEMU_OPTION_msg: opts = qemu_opts_parse_noisily(qemu_find_opts("msg"), optarg, From 5c99fa375da1c7cc4a42a93e002e98b9fb34754b Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 29 Jun 2018 12:32:10 +0200 Subject: [PATCH 1713/2380] cutils: Provide strchrnul strchrnul is a GNU extension and thus unavailable on a number of targets. In the review for a commit removing strchrnul from 9p, I was asked to create a qemu_strchrnul helper to factor out this functionality. Do so, and use it in a number of other places in the code base that inlined the replacement pattern in a place where strchrnul could be used. Signed-off-by: Keno Fischer Acked-by: Greg Kurz Reviewed-by: Markus Armbruster Signed-off-by: Greg Kurz --- configure | 18 ++++++++++++++++++ hmp.c | 8 ++++---- hw/9pfs/9p-local.c | 2 +- include/qemu/cutils.h | 8 ++++++++ monitor.c | 8 ++------ util/cutils.c | 15 +++++++++++++++ util/qemu-option.c | 6 +----- util/uri.c | 6 ++---- 8 files changed, 51 insertions(+), 20 deletions(-) diff --git a/configure b/configure index 4d12cfbe3f..26944a74be 100755 --- a/configure +++ b/configure @@ -4793,6 +4793,21 @@ if compile_prog "" "" ; then sem_timedwait=yes fi +########################################## +# check if we have strchrnul + +strchrnul=no +cat > $TMPC << EOF +#include +int main(void); +// Use a haystack that the compiler shouldn't be able to constant fold +char *haystack = (char*)&main; +int main(void) { return strchrnul(haystack, 'x') != &haystack[6]; } +EOF +if compile_prog "" "" ; then + strchrnul=yes +fi + ########################################## # check if trace backend exists @@ -6276,6 +6291,9 @@ fi if test "$sem_timedwait" = "yes" ; then echo "CONFIG_SEM_TIMEDWAIT=y" >> $config_host_mak fi +if test "$strchrnul" = "yes" ; then + echo "HAVE_STRCHRNUL=y" >> $config_host_mak +fi if test "$byteswap_h" = "yes" ; then echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak fi diff --git a/hmp.c b/hmp.c index 0da0b0ac33..933230d8a7 100644 --- a/hmp.c +++ b/hmp.c @@ -2140,12 +2140,12 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict) int has_hold_time = qdict_haskey(qdict, "hold-time"); int hold_time = qdict_get_try_int(qdict, "hold-time", -1); Error *err = NULL; - char *separator; + const char *separator; int keyname_len; while (1) { - separator = strchr(keys, '-'); - keyname_len = separator ? separator - keys : strlen(keys); + separator = qemu_strchrnul(keys, '-'); + keyname_len = separator - keys; /* Be compatible with old interface, convert user inputted "<" */ if (keys[0] == '<' && keyname_len == 1) { @@ -2182,7 +2182,7 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict) keylist->value->u.qcode.data = idx; } - if (!separator) { + if (!*separator) { break; } keys = separator + 1; diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 5721eff1e1..828e8d6e56 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -65,7 +65,7 @@ int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, assert(*path != '/'); head = g_strdup(path); - c = strchrnul(path, '/'); + c = qemu_strchrnul(path, '/'); if (*c) { /* Intermediate path element */ head[c - path] = 0; diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index a663340b23..274d419bb7 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -122,6 +122,14 @@ int qemu_strnlen(const char *s, int max_len); * Returns: the pointer originally in @input. */ char *qemu_strsep(char **input, const char *delim); +#ifdef HAVE_STRCHRNUL +static inline const char *qemu_strchrnul(const char *s, int c) +{ + return strchrnul(s, c); +} +#else +const char *qemu_strchrnul(const char *s, int c); +#endif time_t mktimegm(struct tm *tm); int qemu_fdatasync(int fd); int fcntl_setfl(int fd, int flag); diff --git a/monitor.c b/monitor.c index 0730a27172..80af6a9390 100644 --- a/monitor.c +++ b/monitor.c @@ -820,9 +820,7 @@ static int compare_cmd(const char *name, const char *list) p = list; for(;;) { pstart = p; - p = strchr(p, '|'); - if (!p) - p = pstart + strlen(pstart); + p = qemu_strchrnul(p, '|'); if ((p - pstart) == len && !memcmp(pstart, name, len)) return 1; if (*p == '\0') @@ -3489,9 +3487,7 @@ static void cmd_completion(Monitor *mon, const char *name, const char *list) p = list; for(;;) { pstart = p; - p = strchr(p, '|'); - if (!p) - p = pstart + strlen(pstart); + p = qemu_strchrnul(p, '|'); len = p - pstart; if (len > sizeof(cmd) - 2) len = sizeof(cmd) - 2; diff --git a/util/cutils.c b/util/cutils.c index 0de69e6db4..9205e09031 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -544,6 +544,21 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, return check_strtox_error(nptr, ep, endptr, errno); } +/** + * Searches for the first occurrence of 'c' in 's', and returns a pointer + * to the trailing null byte if none was found. + */ +#ifndef HAVE_STRCHRNUL +const char *qemu_strchrnul(const char *s, int c) +{ + const char *e = strchr(s, c); + if (!e) { + e = s + strlen(s); + } + return e; +} +#endif + /** * parse_uint: * diff --git a/util/qemu-option.c b/util/qemu-option.c index ba44a0895c..19761e3eaf 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -77,11 +77,7 @@ const char *get_opt_value(const char *p, char **value) *value = NULL; while (1) { - offset = strchr(p, ','); - if (!offset) { - offset = p + strlen(p); - } - + offset = qemu_strchrnul(p, ','); length = offset - p; if (*offset != '\0' && *(offset + 1) == ',') { length++; diff --git a/util/uri.c b/util/uri.c index 8624a7ac23..8bdef84120 100644 --- a/util/uri.c +++ b/util/uri.c @@ -52,6 +52,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu/uri.h" @@ -2266,10 +2267,7 @@ struct QueryParams *query_params_parse(const char *query) /* Find the next separator, or end of the string. */ end = strchr(query, '&'); if (!end) { - end = strchr(query, ';'); - } - if (!end) { - end = query + strlen(query); + end = qemu_strchrnul(query, ';'); } /* Find the first '=' character between here and end. */ From 230f1b31c5d58a852e1ac46e1ef05cfa7d5d9280 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 29 Jun 2018 12:32:10 +0200 Subject: [PATCH 1714/2380] 9p: darwin: Explicitly cast comparisons of mode_t with -1 Comparisons of mode_t with -1 require an explicit cast, since mode_t is unsigned on Darwin. Signed-off-by: Keno Fischer Signed-off-by: Greg Kurz --- hw/9pfs/9p-local.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 828e8d6e56..c30f4f26bd 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -308,7 +308,7 @@ update_map_file: if (credp->fc_gid != -1) { gid = credp->fc_gid; } - if (credp->fc_mode != -1) { + if (credp->fc_mode != (mode_t)-1) { mode = credp->fc_mode; } if (credp->fc_rdev != -1) { @@ -414,7 +414,7 @@ static int local_set_xattrat(int dirfd, const char *path, FsCred *credp) return err; } } - if (credp->fc_mode != -1) { + if (credp->fc_mode != (mode_t)-1) { uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0); From 2266d44311321a833d569cd4deb46cca6021d0e7 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 22 Jun 2018 22:22:05 +0300 Subject: [PATCH 1715/2380] i386/cpu: make -cpu host support monitor/mwait When guest CPU PM is enabled, and with -cpu host, expose the host CPU MWAIT leaf in the CPUID so guest can make good PM decisions. Note: the result is 100% CPU utilization reported by host as host no longer knows that the CPU is halted. Signed-off-by: Michael S. Tsirkin Reviewed-by: Eduardo Habkost Message-Id: <20180622192148.178309-3-mst@redhat.com> Signed-off-by: Paolo Bonzini --- accel/tcg/user-exec-stub.c | 3 +++ target/i386/cpu.c | 32 ++++++++++++++++++++++---------- target/i386/cpu.h | 9 +++++++++ target/i386/kvm.c | 9 +++++++++ 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/accel/tcg/user-exec-stub.c b/accel/tcg/user-exec-stub.c index dbcf1ade9c..a32b4496af 100644 --- a/accel/tcg/user-exec-stub.c +++ b/accel/tcg/user-exec-stub.c @@ -2,6 +2,9 @@ #include "qemu-common.h" #include "qom/cpu.h" #include "sysemu/replay.h" +#include "sysemu/sysemu.h" + +bool enable_cpu_pm = false; void cpu_resume(CPUState *cpu) { diff --git a/target/i386/cpu.c b/target/i386/cpu.c index e6c2f8a22a..1e6a7d0a75 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3959,11 +3959,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; case 5: - /* mwait info: needed for Core compatibility */ - *eax = 0; /* Smallest monitor-line size in bytes */ - *ebx = 0; /* Largest monitor-line size in bytes */ - *ecx = CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; - *edx = 0; + /* MONITOR/MWAIT Leaf */ + *eax = cpu->mwait.eax; /* Smallest monitor-line size in bytes */ + *ebx = cpu->mwait.ebx; /* Largest monitor-line size in bytes */ + *ecx = cpu->mwait.ecx; /* flags */ + *edx = cpu->mwait.edx; /* mwait substates */ break; case 6: /* Thermal and Power Leaf */ @@ -4804,13 +4804,25 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) Error *local_err = NULL; static bool ht_warned; - if (xcc->host_cpuid_required && !accel_uses_host_cpuid()) { - char *name = x86_cpu_class_get_model_name(xcc); - error_setg(&local_err, "CPU model '%s' requires KVM", name); - g_free(name); - goto out; + if (xcc->host_cpuid_required) { + if (!accel_uses_host_cpuid()) { + char *name = x86_cpu_class_get_model_name(xcc); + error_setg(&local_err, "CPU model '%s' requires KVM", name); + g_free(name); + goto out; + } + + if (enable_cpu_pm) { + host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, + &cpu->mwait.ecx, &cpu->mwait.edx); + env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR; + } } + /* mwait extended info: needed for Core compatibility */ + /* We always wake on interrupt even if host does not have the capability */ + cpu->mwait.ecx |= CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; + if (cpu->apic_id == UNASSIGNED_APIC_ID) { error_setg(errp, "apic-id property was not initialized properly"); return; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 16c59b7099..8eaefeee3e 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1382,6 +1382,15 @@ struct X86CPU { /* if true the CPUID code directly forward host cache leaves to the guest */ bool cache_info_passthrough; + /* if true the CPUID code directly forwards + * host monitor/mwait leaves to the guest */ + struct { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + } mwait; + /* Features that were filtered out because of missing host capabilities */ uint32_t filtered_features[FEATURE_WORDS]; diff --git a/target/i386/kvm.c b/target/i386/kvm.c index dc991f6aca..c5f72d645b 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -366,6 +366,15 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, if (!kvm_irqchip_in_kernel()) { ret &= ~CPUID_EXT_X2APIC; } + + if (enable_cpu_pm) { + int disable_exits = kvm_check_extension(s, + KVM_CAP_X86_DISABLE_EXITS); + + if (disable_exits & KVM_X86_DISABLE_EXITS_MWAIT) { + ret |= CPUID_EXT_MONITOR; + } + } } else if (function == 6 && reg == R_EAX) { ret |= CPUID_6_EAX_ARAT; /* safe to allow because of emulated APIC */ } else if (function == 7 && index == 0 && reg == R_EBX) { From 2da91b54fe98faa8676264ac6e5a3aac5b69bec2 Mon Sep 17 00:00:00 2001 From: Viktor Prutyanov Date: Thu, 17 May 2018 19:23:39 +0300 Subject: [PATCH 1716/2380] dump: add Windows dump format to dump-guest-memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds Windows crashdumping feature. Now QEMU can produce ELF-dump containing Windows crashdump header, which can help to convert to a valid WinDbg-understandable crashdump file, or immediately create such file. The crashdump will be obtained by joining physical memory dump and 8K header exposed through vmcoreinfo/fw_cfg device by guest driver at BSOD time. Option '-w' was added to dump-guest-memory command. At the moment, only x64 configuration is supported. Suitable driver can be found at https://github.com/virtio-win/kvm-guest-drivers-windows/tree/master/fwcfg64 Signed-off-by: Viktor Prutyanov Reviewed-by: Marc-André Lureau Message-Id: <20180517162342.4330-2-viktor.prutyanov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- Makefile.target | 1 + dump.c | 24 +++++- hmp-commands.hx | 13 +-- hmp.c | 9 ++- qapi/misc.json | 5 +- win_dump.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++ win_dump.h | 87 ++++++++++++++++++++ 7 files changed, 339 insertions(+), 9 deletions(-) create mode 100644 win_dump.c create mode 100644 win_dump.h diff --git a/Makefile.target b/Makefile.target index a9d8928f96..4d56298bbf 100644 --- a/Makefile.target +++ b/Makefile.target @@ -143,6 +143,7 @@ obj-y += hw/ obj-y += memory.o obj-y += memory_mapping.o obj-y += dump.o +obj-$(TARGET_X86_64) += win_dump.o obj-y += migration/ram.o LIBS := $(libs_softmmu) $(LIBS) diff --git a/dump.c b/dump.c index b54cd42b21..04467b353e 100644 --- a/dump.c +++ b/dump.c @@ -29,6 +29,10 @@ #include "qemu/error-report.h" #include "hw/misc/vmcoreinfo.h" +#ifdef TARGET_X86_64 +#include "win_dump.h" +#endif + #include #ifdef CONFIG_LZO #include @@ -1866,7 +1870,11 @@ static void dump_process(DumpState *s, Error **errp) Error *local_err = NULL; DumpQueryResult *result = NULL; - if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) { + if (s->has_format && s->format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) { +#ifdef TARGET_X86_64 + create_win_dump(s, &local_err); +#endif + } else if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) { create_kdump_vmcore(s, &local_err); } else { create_vmcore(s, &local_err); @@ -1970,6 +1978,13 @@ void qmp_dump_guest_memory(bool paging, const char *file, } #endif +#ifndef TARGET_X86_64 + if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) { + error_setg(errp, "Windows dump is only available for x86-64"); + return; + } +#endif + #if !defined(WIN32) if (strstart(file, "fd:", &p)) { fd = monitor_get_fd(cur_mon, p, errp); @@ -2044,5 +2059,12 @@ DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp) item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; #endif + /* Windows dump is available only if target is x86_64 */ +#ifdef TARGET_X86_64 + item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList)); + item = item->next; + item->value = DUMP_GUEST_MEMORY_FORMAT_WIN_DMP; +#endif + return cap; } diff --git a/hmp-commands.hx b/hmp-commands.hx index ba9cdb8800..c1fc747403 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1136,30 +1136,33 @@ ETEXI { .name = "dump-guest-memory", - .args_type = "paging:-p,detach:-d,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:l?,length:l?", - .params = "[-p] [-d] [-z|-l|-s] filename [begin length]", + .args_type = "paging:-p,detach:-d,windmp:-w,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:l?,length:l?", + .params = "[-p] [-d] [-z|-l|-s|-w] filename [begin length]", .help = "dump guest memory into file 'filename'.\n\t\t\t" "-p: do paging to get guest's memory mapping.\n\t\t\t" "-d: return immediately (do not wait for completion).\n\t\t\t" "-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t" "-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t" "-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t" + "-w: dump in Windows crashdump format (can be used instead of ELF-dump converting),\n\t\t\t" + " for Windows x64 guests with vmcoreinfo driver only.\n\t\t\t" "begin: the starting physical address.\n\t\t\t" "length: the memory size, in bytes.", .cmd = hmp_dump_guest_memory, }, - STEXI @item dump-guest-memory [-p] @var{filename} @var{begin} @var{length} -@item dump-guest-memory [-z|-l|-s] @var{filename} +@item dump-guest-memory [-z|-l|-s|-w] @var{filename} @findex dump-guest-memory Dump guest memory to @var{protocol}. The file can be processed with crash or -gdb. Without -z|-l|-s, the dump format is ELF. +gdb. Without -z|-l|-s|-w, the dump format is ELF. -p: do paging to get guest's memory mapping. -z: dump in kdump-compressed format, with zlib compression. -l: dump in kdump-compressed format, with lzo compression. -s: dump in kdump-compressed format, with snappy compression. + -w: dump in Windows crashdump format (can be used instead of ELF-dump converting), + for Windows x64 guests with vmcoreinfo driver only filename: dump file name. begin: the starting physical address. It's optional, and should be specified together with length. diff --git a/hmp.c b/hmp.c index 0da0b0ac33..41f5e39b72 100644 --- a/hmp.c +++ b/hmp.c @@ -2014,6 +2014,7 @@ void hmp_device_del(Monitor *mon, const QDict *qdict) void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) { Error *err = NULL; + bool win_dmp = qdict_get_try_bool(qdict, "windmp", false); bool paging = qdict_get_try_bool(qdict, "paging", false); bool zlib = qdict_get_try_bool(qdict, "zlib", false); bool lzo = qdict_get_try_bool(qdict, "lzo", false); @@ -2028,12 +2029,16 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF; char *prot; - if (zlib + lzo + snappy > 1) { - error_setg(&err, "only one of '-z|-l|-s' can be set"); + if (zlib + lzo + snappy + win_dmp > 1) { + error_setg(&err, "only one of '-z|-l|-s|-w' can be set"); hmp_handle_error(mon, &err); return; } + if (win_dmp) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_WIN_DMP; + } + if (zlib) { dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; } diff --git a/qapi/misc.json b/qapi/misc.json index c6bc18a859..29da7856e3 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -1677,10 +1677,13 @@ # # @kdump-snappy: kdump-compressed format with snappy-compressed # +# @win-dmp: Windows full crashdump format, +# can be used instead of ELF converting (since 2.13) +# # Since: 2.0 ## { 'enum': 'DumpGuestMemoryFormat', - 'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy' ] } + 'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', 'win-dmp' ] } ## # @dump-guest-memory: diff --git a/win_dump.c b/win_dump.c new file mode 100644 index 0000000000..58255c12ee --- /dev/null +++ b/win_dump.c @@ -0,0 +1,209 @@ +/* + * Windows crashdump + * + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "elf.h" +#include "cpu.h" +#include "exec/hwaddr.h" +#include "monitor/monitor.h" +#include "sysemu/kvm.h" +#include "sysemu/dump.h" +#include "sysemu/sysemu.h" +#include "sysemu/memory_mapping.h" +#include "sysemu/cpus.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" +#include "hw/misc/vmcoreinfo.h" +#include "win_dump.h" + +static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp) +{ + void *buf; + uint64_t addr = run->BasePage << TARGET_PAGE_BITS; + uint64_t size = run->PageCount << TARGET_PAGE_BITS; + uint64_t len = size; + + buf = cpu_physical_memory_map(addr, &len, false); + if (!buf) { + error_setg(errp, "win-dump: failed to map run"); + return 0; + } + if (len != size) { + error_setg(errp, "win-dump: failed to map entire run"); + len = 0; + goto out_unmap; + } + + len = qemu_write_full(fd, buf, len); + if (len != size) { + error_setg(errp, QERR_IO_ERROR); + } + +out_unmap: + cpu_physical_memory_unmap(buf, addr, false, len); + + return len; +} + +static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp) +{ + WinDumpPhyMemDesc64 *desc = &h->PhysicalMemoryBlock; + WinDumpPhyMemRun64 *run = desc->Run; + Error *local_err = NULL; + int i; + + for (i = 0; i < desc->NumberOfRuns; i++) { + s->written_size += write_run(run + i, s->fd, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } +} + +static void patch_mm_pfn_database(WinDumpHeader64 *h, Error **errp) +{ + if (cpu_memory_rw_debug(first_cpu, + h->KdDebuggerDataBlock + KDBG_MM_PFN_DATABASE_OFFSET64, + (uint8_t *)&h->PfnDatabase, sizeof(h->PfnDatabase), 0)) { + error_setg(errp, "win-dump: failed to read MmPfnDatabase"); + return; + } +} + +static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp) +{ + uint64_t KiBugcheckData; + + if (cpu_memory_rw_debug(first_cpu, + h->KdDebuggerDataBlock + KDBG_KI_BUGCHECK_DATA_OFFSET64, + (uint8_t *)&KiBugcheckData, sizeof(KiBugcheckData), 0)) { + error_setg(errp, "win-dump: failed to read KiBugcheckData"); + return; + } + + if (cpu_memory_rw_debug(first_cpu, + KiBugcheckData, + h->BugcheckData, sizeof(h->BugcheckData), 0)) { + error_setg(errp, "win-dump: failed to read bugcheck data"); + return; + } +} + +/* + * This routine tries to correct mistakes in crashdump header. + */ +static void patch_header(WinDumpHeader64 *h) +{ + Error *local_err = NULL; + + h->RequiredDumpSpace = sizeof(WinDumpHeader64) + + (h->PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS); + h->PhysicalMemoryBlock.unused = 0; + h->unused1 = 0; + + /* + * We assume h->DirectoryBase and current CR3 are the same when we access + * memory by virtual address. In other words, we suppose current context + * is system context. It is definetely true in case of BSOD. + */ + + patch_mm_pfn_database(h, &local_err); + if (local_err) { + warn_report_err(local_err); + local_err = NULL; + } + patch_bugcheck_data(h, &local_err); + if (local_err) { + warn_report_err(local_err); + } +} + +static void check_header(WinDumpHeader64 *h, Error **errp) +{ + const char Signature[] = "PAGE"; + const char ValidDump[] = "DU64"; + + if (memcmp(h->Signature, Signature, sizeof(h->Signature))) { + error_setg(errp, "win-dump: invalid header, expected '%.4s'," + " got '%.4s'", Signature, h->Signature); + return; + } + + if (memcmp(h->ValidDump, ValidDump, sizeof(h->ValidDump))) { + error_setg(errp, "win-dump: invalid header, expected '%.4s'," + " got '%.4s'", ValidDump, h->ValidDump); + return; + } +} + +static void check_kdbg(WinDumpHeader64 *h, Error **errp) +{ + const char OwnerTag[] = "KDBG"; + char read_OwnerTag[4]; + + if (cpu_memory_rw_debug(first_cpu, + h->KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64, + (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) { + error_setg(errp, "win-dump: failed to read OwnerTag"); + return; + } + + if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) { + error_setg(errp, "win-dump: invalid KDBG OwnerTag," + " expected '%.4s', got '%.4s'," + " KdDebuggerDataBlock seems to be encrypted", + OwnerTag, read_OwnerTag); + return; + } +} + +void create_win_dump(DumpState *s, Error **errp) +{ + WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note + + VMCOREINFO_ELF_NOTE_HDR_SIZE); + Error *local_err = NULL; + + if (s->guest_note_size != sizeof(WinDumpHeader64) + + VMCOREINFO_ELF_NOTE_HDR_SIZE) { + error_setg(errp, "win-dump: invalid vmcoreinfo note size"); + return; + } + + check_header(h, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + check_kdbg(h, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + patch_header(h); + + s->total_size = h->RequiredDumpSpace; + + s->written_size = qemu_write_full(s->fd, h, sizeof(*h)); + if (s->written_size != sizeof(*h)) { + error_setg(errp, QERR_IO_ERROR); + return; + } + + write_runs(s, h, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} diff --git a/win_dump.h b/win_dump.h new file mode 100644 index 0000000000..281241881e --- /dev/null +++ b/win_dump.h @@ -0,0 +1,87 @@ +/* + * Windows crashdump + * + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +typedef struct WinDumpPhyMemRun64 { + uint64_t BasePage; + uint64_t PageCount; +} QEMU_PACKED WinDumpPhyMemRun64; + +typedef struct WinDumpPhyMemDesc64 { + uint32_t NumberOfRuns; + uint32_t unused; + uint64_t NumberOfPages; + WinDumpPhyMemRun64 Run[43]; +} QEMU_PACKED WinDumpPhyMemDesc64; + +typedef struct WinDumpExceptionRecord { + uint32_t ExceptionCode; + uint32_t ExceptionFlags; + uint64_t ExceptionRecord; + uint64_t ExceptionAddress; + uint32_t NumberParameters; + uint32_t unused; + uint64_t ExceptionInformation[15]; +} QEMU_PACKED WinDumpExceptionRecord; + +typedef struct WinDumpHeader64 { + char Signature[4]; + char ValidDump[4]; + uint32_t MajorVersion; + uint32_t MinorVersion; + uint64_t DirectoryTableBase; + uint64_t PfnDatabase; + uint64_t PsLoadedModuleList; + uint64_t PsActiveProcessHead; + uint32_t MachineImageType; + uint32_t NumberProcessors; + union { + struct { + uint32_t BugcheckCode; + uint32_t unused0; + uint64_t BugcheckParameter1; + uint64_t BugcheckParameter2; + uint64_t BugcheckParameter3; + uint64_t BugcheckParameter4; + }; + uint8_t BugcheckData[40]; + }; + uint8_t VersionUser[32]; + uint64_t KdDebuggerDataBlock; + union { + WinDumpPhyMemDesc64 PhysicalMemoryBlock; + uint8_t PhysicalMemoryBlockBuffer[704]; + }; + union { + uint8_t ContextBuffer[3000]; + }; + WinDumpExceptionRecord Exception; + uint32_t DumpType; + uint32_t unused1; + uint64_t RequiredDumpSpace; + uint64_t SystemTime; + char Comment[128]; + uint64_t SystemUpTime; + uint32_t MiniDumpFields; + uint32_t SecondaryDataState; + uint32_t ProductType; + uint32_t SuiteMask; + uint32_t WriterStatus; + uint8_t unused2; + uint8_t KdSecondaryVersion; + uint8_t reserved[4018]; +} QEMU_PACKED WinDumpHeader64; + +void create_win_dump(DumpState *s, Error **errp); + +#define KDBG_OWNER_TAG_OFFSET64 0x10 +#define KDBG_KI_BUGCHECK_DATA_OFFSET64 0x88 +#define KDBG_MM_PFN_DATABASE_OFFSET64 0xC0 + +#define VMCOREINFO_ELF_NOTE_HDR_SIZE 24 From 92d1b3d5086c182bab66fd1814c4a04ba1e59337 Mon Sep 17 00:00:00 2001 From: Viktor Prutyanov Date: Thu, 17 May 2018 19:23:40 +0300 Subject: [PATCH 1717/2380] dump: use system context in Windows dump We use CPU #0 to access guest virtual memory, but it can execute user thread at that moment. So, switch CR3 to PageDirectoryBase from header and restore original value at the end. Signed-off-by: Viktor Prutyanov Message-Id: <20180517162342.4330-3-viktor.prutyanov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- win_dump.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/win_dump.c b/win_dump.c index 58255c12ee..7d956ca996 100644 --- a/win_dump.c +++ b/win_dump.c @@ -111,12 +111,6 @@ static void patch_header(WinDumpHeader64 *h) h->PhysicalMemoryBlock.unused = 0; h->unused1 = 0; - /* - * We assume h->DirectoryBase and current CR3 are the same when we access - * memory by virtual address. In other words, we suppose current context - * is system context. It is definetely true in case of BSOD. - */ - patch_mm_pfn_database(h, &local_err); if (local_err) { warn_report_err(local_err); @@ -171,6 +165,8 @@ void create_win_dump(DumpState *s, Error **errp) { WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note + VMCOREINFO_ELF_NOTE_HDR_SIZE); + X86CPU *first_x86_cpu = X86_CPU(first_cpu); + uint64_t saved_cr3 = first_x86_cpu->env.cr[3]; Error *local_err = NULL; if (s->guest_note_size != sizeof(WinDumpHeader64) + @@ -185,10 +181,17 @@ void create_win_dump(DumpState *s, Error **errp) return; } + /* + * Further access to kernel structures by virtual addresses + * should be made from system context. + */ + + first_x86_cpu->env.cr[3] = h->DirectoryTableBase; + check_kdbg(h, &local_err); if (local_err) { error_propagate(errp, local_err); - return; + goto out_cr3; } patch_header(h); @@ -198,12 +201,17 @@ void create_win_dump(DumpState *s, Error **errp) s->written_size = qemu_write_full(s->fd, h, sizeof(*h)); if (s->written_size != sizeof(*h)) { error_setg(errp, QERR_IO_ERROR); - return; + goto out_cr3; } write_runs(s, h, &local_err); if (local_err) { error_propagate(errp, local_err); - return; + goto out_cr3; } + +out_cr3: + first_x86_cpu->env.cr[3] = saved_cr3; + + return; } From 2ababfcc0e5e778c9005abb57f4bf6a036145a57 Mon Sep 17 00:00:00 2001 From: Viktor Prutyanov Date: Thu, 17 May 2018 19:23:41 +0300 Subject: [PATCH 1718/2380] dump: add fallback KDBG using in Windows dump KdDebuggerDataBlock may be encrypted in guest memory and dump will be useless in this case. But guest driver can obtain decrypted KDBG and expose its address through BugcheckParameter1 field in raw header. After this patch, QEMU will be able to use fallback KdDebuggerDataBlock. Signed-off-by: Viktor Prutyanov Message-Id: <20180517162342.4330-4-viktor.prutyanov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- win_dump.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/win_dump.c b/win_dump.c index 7d956ca996..2d9afb514e 100644 --- a/win_dump.c +++ b/win_dump.c @@ -144,21 +144,37 @@ static void check_kdbg(WinDumpHeader64 *h, Error **errp) { const char OwnerTag[] = "KDBG"; char read_OwnerTag[4]; + uint64_t KdDebuggerDataBlock = h->KdDebuggerDataBlock; + bool try_fallback = true; +try_again: if (cpu_memory_rw_debug(first_cpu, - h->KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64, + KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64, (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) { error_setg(errp, "win-dump: failed to read OwnerTag"); return; } if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) { - error_setg(errp, "win-dump: invalid KDBG OwnerTag," - " expected '%.4s', got '%.4s'," - " KdDebuggerDataBlock seems to be encrypted", - OwnerTag, read_OwnerTag); - return; + if (try_fallback) { + /* + * If attempt to use original KDBG failed + * (most likely because of its encryption), + * we try to use KDBG obtained by guest driver. + */ + + KdDebuggerDataBlock = h->BugcheckParameter1; + try_fallback = false; + goto try_again; + } else { + error_setg(errp, "win-dump: invalid KDBG OwnerTag," + " expected '%.4s', got '%.4s'", + OwnerTag, read_OwnerTag); + return; + } } + + h->KdDebuggerDataBlock = KdDebuggerDataBlock; } void create_win_dump(DumpState *s, Error **errp) From 2ad9b50f713053dcd6c44b2b5e3bbdb0cfe8a52d Mon Sep 17 00:00:00 2001 From: Viktor Prutyanov Date: Thu, 17 May 2018 19:23:42 +0300 Subject: [PATCH 1719/2380] dump: add Windows live system dump Unlike dying Windows, live system memory doesn't contain correct register contexts. But they can be populated with QEMU register values. After this patch, QEMU will be able to produce guest Windows live system dump. Signed-off-by: Viktor Prutyanov Message-Id: <20180517162342.4330-5-viktor.prutyanov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- win_dump.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++- win_dump.h | 95 ++++++++++++++++++++++++++++++-- 2 files changed, 246 insertions(+), 5 deletions(-) diff --git a/win_dump.c b/win_dump.c index 2d9afb514e..b15c191ad7 100644 --- a/win_dump.c +++ b/win_dump.c @@ -97,6 +97,14 @@ static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp) error_setg(errp, "win-dump: failed to read bugcheck data"); return; } + + /* + * If BugcheckCode wasn't saved, we consider guest OS as alive. + */ + + if (!h->BugcheckCode) { + h->BugcheckCode = LIVE_SYSTEM_DUMP; + } } /* @@ -177,12 +185,139 @@ try_again: h->KdDebuggerDataBlock = KdDebuggerDataBlock; } +struct saved_context { + WinContext ctx; + uint64_t addr; +}; + +static void patch_and_save_context(WinDumpHeader64 *h, + struct saved_context *saved_ctx, + Error **errp) +{ + uint64_t KiProcessorBlock; + uint16_t OffsetPrcbContext; + CPUState *cpu; + int i = 0; + + if (cpu_memory_rw_debug(first_cpu, + h->KdDebuggerDataBlock + KDBG_KI_PROCESSOR_BLOCK_OFFSET64, + (uint8_t *)&KiProcessorBlock, sizeof(KiProcessorBlock), 0)) { + error_setg(errp, "win-dump: failed to read KiProcessorBlock"); + return; + } + + if (cpu_memory_rw_debug(first_cpu, + h->KdDebuggerDataBlock + KDBG_OFFSET_PRCB_CONTEXT_OFFSET64, + (uint8_t *)&OffsetPrcbContext, sizeof(OffsetPrcbContext), 0)) { + error_setg(errp, "win-dump: failed to read OffsetPrcbContext"); + return; + } + + CPU_FOREACH(cpu) { + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + uint64_t Prcb; + uint64_t Context; + WinContext ctx; + + if (cpu_memory_rw_debug(first_cpu, + KiProcessorBlock + i * sizeof(uint64_t), + (uint8_t *)&Prcb, sizeof(Prcb), 0)) { + error_setg(errp, "win-dump: failed to read" + " CPU #%d PRCB location", i); + return; + } + + if (cpu_memory_rw_debug(first_cpu, + Prcb + OffsetPrcbContext, + (uint8_t *)&Context, sizeof(Context), 0)) { + error_setg(errp, "win-dump: failed to read" + " CPU #%d ContextFrame location", i); + return; + } + + saved_ctx[i].addr = Context; + + ctx = (WinContext){ + .ContextFlags = WIN_CTX_ALL, + .MxCsr = env->mxcsr, + + .SegEs = env->segs[0].selector, + .SegCs = env->segs[1].selector, + .SegSs = env->segs[2].selector, + .SegDs = env->segs[3].selector, + .SegFs = env->segs[4].selector, + .SegGs = env->segs[5].selector, + .EFlags = cpu_compute_eflags(env), + + .Dr0 = env->dr[0], + .Dr1 = env->dr[1], + .Dr2 = env->dr[2], + .Dr3 = env->dr[3], + .Dr6 = env->dr[6], + .Dr7 = env->dr[7], + + .Rax = env->regs[R_EAX], + .Rbx = env->regs[R_EBX], + .Rcx = env->regs[R_ECX], + .Rdx = env->regs[R_EDX], + .Rsp = env->regs[R_ESP], + .Rbp = env->regs[R_EBP], + .Rsi = env->regs[R_ESI], + .Rdi = env->regs[R_EDI], + .R8 = env->regs[8], + .R9 = env->regs[9], + .R10 = env->regs[10], + .R11 = env->regs[11], + .R12 = env->regs[12], + .R13 = env->regs[13], + .R14 = env->regs[14], + .R15 = env->regs[15], + + .Rip = env->eip, + .FltSave = { + .MxCsr = env->mxcsr, + }, + }; + + if (cpu_memory_rw_debug(first_cpu, Context, + (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 0)) { + error_setg(errp, "win-dump: failed to save CPU #%d context", i); + return; + } + + if (cpu_memory_rw_debug(first_cpu, Context, + (uint8_t *)&ctx, sizeof(WinContext), 1)) { + error_setg(errp, "win-dump: failed to write CPU #%d context", i); + return; + } + + i++; + } +} + +static void restore_context(WinDumpHeader64 *h, + struct saved_context *saved_ctx) +{ + int i; + Error *err = NULL; + + for (i = 0; i < h->NumberProcessors; i++) { + if (cpu_memory_rw_debug(first_cpu, saved_ctx[i].addr, + (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 1)) { + error_setg(&err, "win-dump: failed to restore CPU #%d context", i); + warn_report_err(err); + } + } +} + void create_win_dump(DumpState *s, Error **errp) { WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note + VMCOREINFO_ELF_NOTE_HDR_SIZE); X86CPU *first_x86_cpu = X86_CPU(first_cpu); uint64_t saved_cr3 = first_x86_cpu->env.cr[3]; + struct saved_context *saved_ctx = NULL; Error *local_err = NULL; if (s->guest_note_size != sizeof(WinDumpHeader64) + @@ -212,20 +347,37 @@ void create_win_dump(DumpState *s, Error **errp) patch_header(h); + saved_ctx = g_new(struct saved_context, h->NumberProcessors); + + /* + * Always patch context because there is no way + * to determine if the system-saved context is valid + */ + + patch_and_save_context(h, saved_ctx, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out_free; + } + s->total_size = h->RequiredDumpSpace; s->written_size = qemu_write_full(s->fd, h, sizeof(*h)); if (s->written_size != sizeof(*h)) { error_setg(errp, QERR_IO_ERROR); - goto out_cr3; + goto out_restore; } write_runs(s, h, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out_cr3; + goto out_restore; } +out_restore: + restore_context(h, saved_ctx); +out_free: + g_free(saved_ctx); out_cr3: first_x86_cpu->env.cr[3] = saved_cr3; diff --git a/win_dump.h b/win_dump.h index 281241881e..f9e1faf8eb 100644 --- a/win_dump.h +++ b/win_dump.h @@ -80,8 +80,97 @@ typedef struct WinDumpHeader64 { void create_win_dump(DumpState *s, Error **errp); -#define KDBG_OWNER_TAG_OFFSET64 0x10 -#define KDBG_KI_BUGCHECK_DATA_OFFSET64 0x88 -#define KDBG_MM_PFN_DATABASE_OFFSET64 0xC0 +#define KDBG_OWNER_TAG_OFFSET64 0x10 +#define KDBG_MM_PFN_DATABASE_OFFSET64 0xC0 +#define KDBG_KI_BUGCHECK_DATA_OFFSET64 0x88 +#define KDBG_KI_PROCESSOR_BLOCK_OFFSET64 0x218 +#define KDBG_OFFSET_PRCB_CONTEXT_OFFSET64 0x338 #define VMCOREINFO_ELF_NOTE_HDR_SIZE 24 + +#define WIN_CTX_X64 0x00100000L + +#define WIN_CTX_CTL 0x00000001L +#define WIN_CTX_INT 0x00000002L +#define WIN_CTX_SEG 0x00000004L +#define WIN_CTX_FP 0x00000008L +#define WIN_CTX_DBG 0x00000010L + +#define WIN_CTX_FULL (WIN_CTX_X64 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_FP) +#define WIN_CTX_ALL (WIN_CTX_FULL | WIN_CTX_SEG | WIN_CTX_DBG) + +#define LIVE_SYSTEM_DUMP 0x00000161 + +typedef struct WinM128A { + uint64_t low; + int64_t high; +} QEMU_ALIGNED(16) WinM128A; + +typedef struct WinContext { + uint64_t PHome[6]; + + uint32_t ContextFlags; + uint32_t MxCsr; + + uint16_t SegCs; + uint16_t SegDs; + uint16_t SegEs; + uint16_t SegFs; + uint16_t SegGs; + uint16_t SegSs; + uint32_t EFlags; + + uint64_t Dr0; + uint64_t Dr1; + uint64_t Dr2; + uint64_t Dr3; + uint64_t Dr6; + uint64_t Dr7; + + uint64_t Rax; + uint64_t Rcx; + uint64_t Rdx; + uint64_t Rbx; + uint64_t Rsp; + uint64_t Rbp; + uint64_t Rsi; + uint64_t Rdi; + uint64_t R8; + uint64_t R9; + uint64_t R10; + uint64_t R11; + uint64_t R12; + uint64_t R13; + uint64_t R14; + uint64_t R15; + + uint64_t Rip; + + struct { + uint16_t ControlWord; + uint16_t StatusWord; + uint8_t TagWord; + uint8_t Reserved1; + uint16_t ErrorOpcode; + uint32_t ErrorOffset; + uint16_t ErrorSelector; + uint16_t Reserved2; + uint32_t DataOffset; + uint16_t DataSelector; + uint16_t Reserved3; + uint32_t MxCsr; + uint32_t MxCsr_Mask; + WinM128A FloatRegisters[8]; + WinM128A XmmRegisters[16]; + uint8_t Reserved4[96]; + } FltSave; + + WinM128A VectorRegister[26]; + uint64_t VectorControl; + + uint64_t DebugControl; + uint64_t LastBranchToRip; + uint64_t LastBranchFromRip; + uint64_t LastExceptionToRip; + uint64_t LastExceptionFromRip; +} QEMU_ALIGNED(16) WinContext; From 0a96ca2437646bad197b0108c5f4a93e7ead05a9 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 27 Jun 2018 14:24:30 -0300 Subject: [PATCH 1720/2380] hw/scsi: cleanups before VPD BL emulation To add support for the emulation of Block Limits VPD page for passthrough devices, a few adjustments in the current code base is required to avoid repetition and improve clarity. In scsi-generic.c, detach the Inquiry handling from scsi_read_complete and put it into a new function called scsi_handle_inquiry_reply. This change aims to avoid cluttering of scsi_read_complete when we more logic in the Inquiry response handling is added in the next patches, centralizing the changes in the new function. In scsi-disk.c, take the build of all emulated VPD pages from scsi_disk_emulate_inquiry and make it available to other files into a non-static function called scsi_disk_emulate_vpd_page. Making it public will allow the future VPD BL emulation code for passthrough devices to use it from scsi-generic.c, avoiding copy/pasting this code solely for that purpose. It also has the advantage of providing emulation of all VPD pages in case we need to emulate other pages in other scenarios. As a bonus, scsi_disk_emulate_inquiry got tidier. Signed-off-by: Daniel Henrique Barboza Message-Id: <20180627172432.11120-2-danielhb413@gmail.com> Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 425 +++++++++++++++++++++-------------------- hw/scsi/scsi-generic.c | 71 +++---- include/hw/scsi/scsi.h | 1 + 3 files changed, 258 insertions(+), 239 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index aeaf611854..664d634c98 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -585,219 +585,228 @@ static uint8_t *scsi_get_buf(SCSIRequest *req) return (uint8_t *)r->iov.iov_base; } +int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + uint8_t page_code = req->cmd.buf[2]; + int start, buflen = 0; + + outbuf[buflen++] = s->qdev.type & 0x1f; + outbuf[buflen++] = page_code; + outbuf[buflen++] = 0x00; + outbuf[buflen++] = 0x00; + start = buflen; + + switch (page_code) { + case 0x00: /* Supported page codes, mandatory */ + { + DPRINTF("Inquiry EVPD[Supported pages] " + "buffer size %zd\n", req->cmd.xfer); + outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ + if (s->serial) { + outbuf[buflen++] = 0x80; /* unit serial number */ + } + outbuf[buflen++] = 0x83; /* device identification */ + if (s->qdev.type == TYPE_DISK) { + outbuf[buflen++] = 0xb0; /* block limits */ + outbuf[buflen++] = 0xb1; /* block device characteristics */ + outbuf[buflen++] = 0xb2; /* thin provisioning */ + } + break; + } + case 0x80: /* Device serial number, optional */ + { + int l; + + if (!s->serial) { + DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); + return -1; + } + + l = strlen(s->serial); + if (l > 36) { + l = 36; + } + + DPRINTF("Inquiry EVPD[Serial number] " + "buffer size %zd\n", req->cmd.xfer); + memcpy(outbuf + buflen, s->serial, l); + buflen += l; + break; + } + + case 0x83: /* Device identification page, mandatory */ + { + const char *str = s->serial ?: blk_name(s->qdev.conf.blk); + int max_len = s->serial ? 20 : 255 - 8; + int id_len = strlen(str); + + if (id_len > max_len) { + id_len = max_len; + } + DPRINTF("Inquiry EVPD[Device identification] " + "buffer size %zd\n", req->cmd.xfer); + + outbuf[buflen++] = 0x2; /* ASCII */ + outbuf[buflen++] = 0; /* not officially assigned */ + outbuf[buflen++] = 0; /* reserved */ + outbuf[buflen++] = id_len; /* length of data following */ + memcpy(outbuf + buflen, str, id_len); + buflen += id_len; + + if (s->qdev.wwn) { + outbuf[buflen++] = 0x1; /* Binary */ + outbuf[buflen++] = 0x3; /* NAA */ + outbuf[buflen++] = 0; /* reserved */ + outbuf[buflen++] = 8; + stq_be_p(&outbuf[buflen], s->qdev.wwn); + buflen += 8; + } + + if (s->qdev.port_wwn) { + outbuf[buflen++] = 0x61; /* SAS / Binary */ + outbuf[buflen++] = 0x93; /* PIV / Target port / NAA */ + outbuf[buflen++] = 0; /* reserved */ + outbuf[buflen++] = 8; + stq_be_p(&outbuf[buflen], s->qdev.port_wwn); + buflen += 8; + } + + if (s->port_index) { + outbuf[buflen++] = 0x61; /* SAS / Binary */ + + /* PIV/Target port/relative target port */ + outbuf[buflen++] = 0x94; + + outbuf[buflen++] = 0; /* reserved */ + outbuf[buflen++] = 4; + stw_be_p(&outbuf[buflen + 2], s->port_index); + buflen += 4; + } + break; + } + case 0xb0: /* block limits */ + { + unsigned int unmap_sectors = + s->qdev.conf.discard_granularity / s->qdev.blocksize; + unsigned int min_io_size = + s->qdev.conf.min_io_size / s->qdev.blocksize; + unsigned int opt_io_size = + s->qdev.conf.opt_io_size / s->qdev.blocksize; + unsigned int max_unmap_sectors = + s->max_unmap_size / s->qdev.blocksize; + unsigned int max_io_sectors = + s->max_io_size / s->qdev.blocksize; + + if (s->qdev.type == TYPE_ROM) { + DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", + page_code); + return -1; + } + if (s->qdev.type == TYPE_DISK) { + int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk); + int max_io_sectors_blk = + max_transfer_blk / s->qdev.blocksize; + + max_io_sectors = + MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors); + + /* min_io_size and opt_io_size can't be greater than + * max_io_sectors */ + if (min_io_size) { + min_io_size = MIN(min_io_size, max_io_sectors); + } + if (opt_io_size) { + opt_io_size = MIN(opt_io_size, max_io_sectors); + } + } + /* required VPD size with unmap support */ + buflen = 0x40; + memset(outbuf + 4, 0, buflen - 4); + + outbuf[4] = 0x1; /* wsnz */ + + /* optimal transfer length granularity */ + outbuf[6] = (min_io_size >> 8) & 0xff; + outbuf[7] = min_io_size & 0xff; + + /* maximum transfer length */ + outbuf[8] = (max_io_sectors >> 24) & 0xff; + outbuf[9] = (max_io_sectors >> 16) & 0xff; + outbuf[10] = (max_io_sectors >> 8) & 0xff; + outbuf[11] = max_io_sectors & 0xff; + + /* optimal transfer length */ + outbuf[12] = (opt_io_size >> 24) & 0xff; + outbuf[13] = (opt_io_size >> 16) & 0xff; + outbuf[14] = (opt_io_size >> 8) & 0xff; + outbuf[15] = opt_io_size & 0xff; + + /* max unmap LBA count, default is 1GB */ + outbuf[20] = (max_unmap_sectors >> 24) & 0xff; + outbuf[21] = (max_unmap_sectors >> 16) & 0xff; + outbuf[22] = (max_unmap_sectors >> 8) & 0xff; + outbuf[23] = max_unmap_sectors & 0xff; + + /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */ + outbuf[24] = 0; + outbuf[25] = 0; + outbuf[26] = 0; + outbuf[27] = 255; + + /* optimal unmap granularity */ + outbuf[28] = (unmap_sectors >> 24) & 0xff; + outbuf[29] = (unmap_sectors >> 16) & 0xff; + outbuf[30] = (unmap_sectors >> 8) & 0xff; + outbuf[31] = unmap_sectors & 0xff; + + /* max write same size */ + outbuf[36] = 0; + outbuf[37] = 0; + outbuf[38] = 0; + outbuf[39] = 0; + + outbuf[40] = (max_io_sectors >> 24) & 0xff; + outbuf[41] = (max_io_sectors >> 16) & 0xff; + outbuf[42] = (max_io_sectors >> 8) & 0xff; + outbuf[43] = max_io_sectors & 0xff; + break; + } + case 0xb1: /* block device characteristics */ + { + buflen = 8; + outbuf[4] = (s->rotation_rate >> 8) & 0xff; + outbuf[5] = s->rotation_rate & 0xff; + outbuf[6] = 0; + outbuf[7] = 0; + break; + } + case 0xb2: /* thin provisioning */ + { + buflen = 8; + outbuf[4] = 0; + outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */ + outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; + outbuf[7] = 0; + break; + } + default: + return -1; + } + /* done with EVPD */ + assert(buflen - start <= 255); + outbuf[start - 1] = buflen - start; + return buflen; +} + static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); int buflen = 0; - int start; if (req->cmd.buf[1] & 0x1) { /* Vital product data */ - uint8_t page_code = req->cmd.buf[2]; - - outbuf[buflen++] = s->qdev.type & 0x1f; - outbuf[buflen++] = page_code ; // this page - outbuf[buflen++] = 0x00; - outbuf[buflen++] = 0x00; - start = buflen; - - switch (page_code) { - case 0x00: /* Supported page codes, mandatory */ - { - DPRINTF("Inquiry EVPD[Supported pages] " - "buffer size %zd\n", req->cmd.xfer); - outbuf[buflen++] = 0x00; // list of supported pages (this page) - if (s->serial) { - outbuf[buflen++] = 0x80; // unit serial number - } - outbuf[buflen++] = 0x83; // device identification - if (s->qdev.type == TYPE_DISK) { - outbuf[buflen++] = 0xb0; // block limits - outbuf[buflen++] = 0xb1; /* block device characteristics */ - outbuf[buflen++] = 0xb2; // thin provisioning - } - break; - } - case 0x80: /* Device serial number, optional */ - { - int l; - - if (!s->serial) { - DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); - return -1; - } - - l = strlen(s->serial); - if (l > 36) { - l = 36; - } - - DPRINTF("Inquiry EVPD[Serial number] " - "buffer size %zd\n", req->cmd.xfer); - memcpy(outbuf+buflen, s->serial, l); - buflen += l; - break; - } - - case 0x83: /* Device identification page, mandatory */ - { - const char *str = s->serial ?: blk_name(s->qdev.conf.blk); - int max_len = s->serial ? 20 : 255 - 8; - int id_len = strlen(str); - - if (id_len > max_len) { - id_len = max_len; - } - DPRINTF("Inquiry EVPD[Device identification] " - "buffer size %zd\n", req->cmd.xfer); - - outbuf[buflen++] = 0x2; // ASCII - outbuf[buflen++] = 0; // not officially assigned - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = id_len; // length of data following - memcpy(outbuf+buflen, str, id_len); - buflen += id_len; - - if (s->qdev.wwn) { - outbuf[buflen++] = 0x1; // Binary - outbuf[buflen++] = 0x3; // NAA - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = 8; - stq_be_p(&outbuf[buflen], s->qdev.wwn); - buflen += 8; - } - - if (s->qdev.port_wwn) { - outbuf[buflen++] = 0x61; // SAS / Binary - outbuf[buflen++] = 0x93; // PIV / Target port / NAA - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = 8; - stq_be_p(&outbuf[buflen], s->qdev.port_wwn); - buflen += 8; - } - - if (s->port_index) { - outbuf[buflen++] = 0x61; // SAS / Binary - outbuf[buflen++] = 0x94; // PIV / Target port / relative target port - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = 4; - stw_be_p(&outbuf[buflen + 2], s->port_index); - buflen += 4; - } - break; - } - case 0xb0: /* block limits */ - { - unsigned int unmap_sectors = - s->qdev.conf.discard_granularity / s->qdev.blocksize; - unsigned int min_io_size = - s->qdev.conf.min_io_size / s->qdev.blocksize; - unsigned int opt_io_size = - s->qdev.conf.opt_io_size / s->qdev.blocksize; - unsigned int max_unmap_sectors = - s->max_unmap_size / s->qdev.blocksize; - unsigned int max_io_sectors = - s->max_io_size / s->qdev.blocksize; - - if (s->qdev.type == TYPE_ROM) { - DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", - page_code); - return -1; - } - if (s->qdev.type == TYPE_DISK) { - int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk); - int max_io_sectors_blk = - max_transfer_blk / s->qdev.blocksize; - - max_io_sectors = - MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors); - - /* min_io_size and opt_io_size can't be greater than - * max_io_sectors */ - if (min_io_size) { - min_io_size = MIN(min_io_size, max_io_sectors); - } - if (opt_io_size) { - opt_io_size = MIN(opt_io_size, max_io_sectors); - } - } - /* required VPD size with unmap support */ - buflen = 0x40; - memset(outbuf + 4, 0, buflen - 4); - - outbuf[4] = 0x1; /* wsnz */ - - /* optimal transfer length granularity */ - outbuf[6] = (min_io_size >> 8) & 0xff; - outbuf[7] = min_io_size & 0xff; - - /* maximum transfer length */ - outbuf[8] = (max_io_sectors >> 24) & 0xff; - outbuf[9] = (max_io_sectors >> 16) & 0xff; - outbuf[10] = (max_io_sectors >> 8) & 0xff; - outbuf[11] = max_io_sectors & 0xff; - - /* optimal transfer length */ - outbuf[12] = (opt_io_size >> 24) & 0xff; - outbuf[13] = (opt_io_size >> 16) & 0xff; - outbuf[14] = (opt_io_size >> 8) & 0xff; - outbuf[15] = opt_io_size & 0xff; - - /* max unmap LBA count, default is 1GB */ - outbuf[20] = (max_unmap_sectors >> 24) & 0xff; - outbuf[21] = (max_unmap_sectors >> 16) & 0xff; - outbuf[22] = (max_unmap_sectors >> 8) & 0xff; - outbuf[23] = max_unmap_sectors & 0xff; - - /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header. */ - outbuf[24] = 0; - outbuf[25] = 0; - outbuf[26] = 0; - outbuf[27] = 255; - - /* optimal unmap granularity */ - outbuf[28] = (unmap_sectors >> 24) & 0xff; - outbuf[29] = (unmap_sectors >> 16) & 0xff; - outbuf[30] = (unmap_sectors >> 8) & 0xff; - outbuf[31] = unmap_sectors & 0xff; - - /* max write same size */ - outbuf[36] = 0; - outbuf[37] = 0; - outbuf[38] = 0; - outbuf[39] = 0; - - outbuf[40] = (max_io_sectors >> 24) & 0xff; - outbuf[41] = (max_io_sectors >> 16) & 0xff; - outbuf[42] = (max_io_sectors >> 8) & 0xff; - outbuf[43] = max_io_sectors & 0xff; - break; - } - case 0xb1: /* block device characteristics */ - { - buflen = 8; - outbuf[4] = (s->rotation_rate >> 8) & 0xff; - outbuf[5] = s->rotation_rate & 0xff; - outbuf[6] = 0; - outbuf[7] = 0; - break; - } - case 0xb2: /* thin provisioning */ - { - buflen = 8; - outbuf[4] = 0; - outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */ - outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; - outbuf[7] = 0; - break; - } - default: - return -1; - } - /* done with EVPD */ - assert(buflen - start <= 255); - outbuf[start - 1] = buflen - start; - return buflen; + return scsi_disk_emulate_vpd_page(req, outbuf); } /* Standard INQUIRY data */ @@ -3039,6 +3048,10 @@ static Property scsi_block_properties[] = { DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk), DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false), DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0), + DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size, + DEFAULT_MAX_UNMAP_SIZE), + DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size, + DEFAULT_MAX_IO_SIZE), DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version, -1), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 03bce8ff39..a04a704bbf 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -142,6 +142,43 @@ static int execute_command(BlockBackend *blk, return 0; } +static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) +{ + /* + * EVPD set to zero returns the standard INQUIRY data. + * + * Check if scsi_version is unset (-1) to avoid re-defining it + * each time an INQUIRY with standard data is received. + * scsi_version is initialized with -1 in scsi_generic_reset + * and scsi_disk_reset, making sure that we'll set the + * scsi_version after a reset. If the version field of the + * INQUIRY response somehow changes after a guest reboot, + * we'll be able to keep track of it. + * + * On SCSI-2 and older, first 3 bits of byte 2 is the + * ANSI-approved version, while on later versions the + * whole byte 2 contains the version. Check if we're dealing + * with a newer version and, in that case, assign the + * whole byte. + */ + if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) { + s->scsi_version = r->buf[2] & 0x07; + if (s->scsi_version > 2) { + s->scsi_version = r->buf[2]; + } + } + if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) { + uint32_t max_transfer = + blk_get_max_transfer(s->conf.blk) / s->blocksize; + + assert(max_transfer); + stl_be_p(&r->buf[8], max_transfer); + /* Also take care of the opt xfer len. */ + stl_be_p(&r->buf[12], + MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); + } +} + static void scsi_read_complete(void * opaque, int ret) { SCSIGenericReq *r = (SCSIGenericReq *)opaque; @@ -194,39 +231,7 @@ static void scsi_read_complete(void * opaque, int ret) } } if (r->req.cmd.buf[0] == INQUIRY) { - /* - * EVPD set to zero returns the standard INQUIRY data. - * - * Check if scsi_version is unset (-1) to avoid re-defining it - * each time an INQUIRY with standard data is received. - * scsi_version is initialized with -1 in scsi_generic_reset - * and scsi_disk_reset, making sure that we'll set the - * scsi_version after a reset. If the version field of the - * INQUIRY response somehow changes after a guest reboot, - * we'll be able to keep track of it. - * - * On SCSI-2 and older, first 3 bits of byte 2 is the - * ANSI-approved version, while on later versions the - * whole byte 2 contains the version. Check if we're dealing - * with a newer version and, in that case, assign the - * whole byte. - */ - if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) { - s->scsi_version = r->buf[2] & 0x07; - if (s->scsi_version > 2) { - s->scsi_version = r->buf[2]; - } - } - if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) { - uint32_t max_transfer = - blk_get_max_transfer(s->conf.blk) / s->blocksize; - - assert(max_transfer); - stl_be_p(&r->buf[8], max_transfer); - /* Also take care of the opt xfer len. */ - stl_be_p(&r->buf[12], - MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); - } + scsi_handle_inquiry_reply(r, s); } scsi_req_data(&r->req, len); scsi_req_unref(&r->req); diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index e35137ea78..138eb79a5f 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -186,6 +186,7 @@ void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); void scsi_device_unit_attention_reported(SCSIDevice *dev); void scsi_generic_read_device_identification(SCSIDevice *dev); int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); +int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf); SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); /* scsi-generic.c. */ From a0c7e35b17b3d2cade8a5fc8e57904e02fb91fe4 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 27 Jun 2018 14:24:31 -0300 Subject: [PATCH 1721/2380] hw/scsi: centralize SG_IO calls into single function For the VPD Block Limits emulation with SCSI passthrough, we'll issue an Inquiry request with EVPD set to retrieve the available VPD pages of the device. This would be done in a way similar of what scsi_generic_read_device_identification does: create a SCSI command and a reply buffer, fill in the sg_io_hdr_t structure, call blk_ioctl, check if an error occurred, process the response. This same process is done in other 2 functions, get_device_type and get_stream_blocksize. They differ in the command/reply buffer and post-processing, everything else is almost a copy/paste. Instead of adding a forth copy/pasted-ish code when adding the passthrough VPD BL emulation, this patch extirpates this repetition of those 3 functions and put it into a new one called scsi_SG_IO_FROM_DEV. Any future code that wants to execute an SG_DXFER_FROM_DEV to the device can use it, avoiding filling sg_io_hdr_t again and et cetera. Signed-off-by: Daniel Henrique Barboza Message-Id: <20180627172432.11120-3-danielhb413@gmail.com> Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 18 +++---------- hw/scsi/scsi-generic.c | 61 +++++++++++++++++++++--------------------- include/hw/scsi/scsi.h | 2 ++ 3 files changed, 36 insertions(+), 45 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 664d634c98..b0b39f1e92 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2578,8 +2578,6 @@ static int get_device_type(SCSIDiskState *s) { uint8_t cmd[16]; uint8_t buf[36]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; int ret; memset(cmd, 0, sizeof(cmd)); @@ -2587,19 +2585,9 @@ static int get_device_type(SCSIDiskState *s) cmd[0] = INQUIRY; cmd[4] = sizeof(buf); - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = SG_DXFER_FROM_DEV; - io_header.dxfer_len = sizeof(buf); - io_header.dxferp = buf; - io_header.cmdp = cmd; - io_header.cmd_len = sizeof(cmd); - io_header.mx_sb_len = sizeof(sensebuf); - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ - - ret = blk_ioctl(s->qdev.conf.blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { + ret = scsi_SG_IO_FROM_DEV(s->qdev.conf.blk, cmd, sizeof(cmd), + buf, sizeof(buf)); + if (ret < 0) { return -1; } s->qdev.type = buf[0]; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index a04a704bbf..61abc2763a 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -409,12 +409,35 @@ static int read_naa_id(const uint8_t *p, uint64_t *p_wwn) return -EINVAL; } +int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, + uint8_t *buf, uint8_t buf_size) +{ + sg_io_hdr_t io_header; + uint8_t sensebuf[8]; + int ret; + + memset(&io_header, 0, sizeof(io_header)); + io_header.interface_id = 'S'; + io_header.dxfer_direction = SG_DXFER_FROM_DEV; + io_header.dxfer_len = buf_size; + io_header.dxferp = buf; + io_header.cmdp = cmd; + io_header.cmd_len = cmd_size; + io_header.mx_sb_len = sizeof(sensebuf); + io_header.sbp = sensebuf; + io_header.timeout = 6000; /* XXX */ + + ret = blk_ioctl(blk, SG_IO, &io_header); + if (ret < 0 || io_header.driver_status || io_header.host_status) { + return -1; + } + return 0; +} + void scsi_generic_read_device_identification(SCSIDevice *s) { uint8_t cmd[6]; uint8_t buf[250]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; int ret; int i, len; @@ -425,19 +448,9 @@ void scsi_generic_read_device_identification(SCSIDevice *s) cmd[2] = 0x83; cmd[4] = sizeof(buf); - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = SG_DXFER_FROM_DEV; - io_header.dxfer_len = sizeof(buf); - io_header.dxferp = buf; - io_header.cmdp = cmd; - io_header.cmd_len = sizeof(cmd); - io_header.mx_sb_len = sizeof(sensebuf); - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ - - ret = blk_ioctl(s->conf.blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { + ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd), + buf, sizeof(buf)); + if (ret < 0) { return; } @@ -470,8 +483,6 @@ static int get_stream_blocksize(BlockBackend *blk) { uint8_t cmd[6]; uint8_t buf[12]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; int ret; memset(cmd, 0, sizeof(cmd)); @@ -479,21 +490,11 @@ static int get_stream_blocksize(BlockBackend *blk) cmd[0] = MODE_SENSE; cmd[4] = sizeof(buf); - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = SG_DXFER_FROM_DEV; - io_header.dxfer_len = sizeof(buf); - io_header.dxferp = buf; - io_header.cmdp = cmd; - io_header.cmd_len = sizeof(cmd); - io_header.mx_sb_len = sizeof(sensebuf); - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ - - ret = blk_ioctl(blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { + ret = scsi_SG_IO_FROM_DEV(blk, cmd, sizeof(cmd), buf, sizeof(buf)); + if (ret < 0) { return -1; } + return (buf[9] << 16) | (buf[10] << 8) | buf[11]; } diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 138eb79a5f..75eced34d3 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -187,6 +187,8 @@ void scsi_device_unit_attention_reported(SCSIDevice *dev); void scsi_generic_read_device_identification(SCSIDevice *dev); int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf); +int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, + uint8_t *buf, uint8_t buf_size); SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); /* scsi-generic.c. */ From a71c775b24ebc664129eb1d9b4c360590353efd5 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 27 Jun 2018 14:24:32 -0300 Subject: [PATCH 1722/2380] hw/scsi: add VPD Block Limits emulation The VPD Block Limits Inquiry page is optional, allowing SCSI devices to not implement it. This is the case for devices like the MegaRAID SAS 9361-8i and Microsemi PM8069. In case of SCSI passthrough, the response of this request is used by the QEMU SCSI layer to set the max_io_sectors that the guest device will support, based on the value of the max_sectors_kb that the device has set in the host at that time. Without this response, the guest kernel is free to assume any value of max_io_sectors for the SCSI device. If this value is greater than the value from the host, SCSI Sense errors will occur because the guest will send read/write requests that are larger than the underlying host device is configured to support. An example of this behavior can be seen in [1]. A workaround is to set the max_sectors_kb host value back in the guest kernel (a process that can be automated using rc.local startup scripts and the like), but this has several drawbacks: - it can be troublesome if the guest has many passthrough devices that needs this tuning; - if a change in max_sectors_kb is made in the host side, manual change in the guests will also be required; - during an OS install it is difficult, and sometimes not possible, to go to a terminal and change the max_sectors_kb prior to the installation. This means that the disk can't be used during the install process. The easiest alternative here is to roll back to scsi-hd, install the guest and then go back to SCSI passthrough when the installation is done and max_sectors_kb can be set. An easier way would be to QEMU handle the absence of the Block Limits VPD device response, setting max_io_sectors accordingly and allowing the guest to use the device without the hassle. This patch adds emulation of the Block Limits VPD response for SCSI passthrough devices of type TYPE_DISK that doesn't support it. The following changes were made: - scsi_handle_inquiry_reply will now check the available VPD pages from the Inquiry EVPD reply. In case the device does not - a new function called scsi_generic_set_vpd_bl_emulation, that is called during device realize, was created to set a new flag 'needs_vpd_bl_emulation' of the device. This function retrieves the Inquiry EVPD response of the device to check for VPD BL support. - scsi_handle_inquiry_reply will now check the available VPD pages from the Inquiry EVPD reply in case the device needs VPD BL emulation, adding the Block Limits page (0xb0) to the list. This will make the guest kernel aware of the support that we're now providing by emulation. - a new function scsi_emulate_block_limits creates the emulated Block Limits response. This function is called inside scsi_read_complete in case the device requires Block Limits VPD emulation and we detected a SCSI Sense error in the VPD Block Limits reply that was issued from the guest kernel to the device. This error is expected: we're reporting support from our side, but the device isn't aware of it. With this patch, the guest now queries the Block Limits page during the device configuration because it is being advertised in the Supported Pages response. It will either receive the Block Limits page from the hardware, if it supports it, or will receive an emulated response from QEMU. At any rate, the guest now has the information to set the max_sectors_kb parameter accordingly, sparing the user of SCSI sense errors that would happen without the emulated response and in the absence of Block Limits support from the hardware. [1] https://bugzilla.redhat.com/show_bug.cgi?id=1566195 Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1566195 Reported-by: Dac Nguyen Signed-off-by: Daniel Henrique Barboza Message-Id: <20180627172432.11120-4-danielhb413@gmail.com> Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 2 +- hw/scsi/scsi-generic.c | 132 +++++++++++++++++++++++++++++++++++++---- include/hw/scsi/scsi.h | 3 +- 3 files changed, 125 insertions(+), 12 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index b0b39f1e92..55a34b3895 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2645,7 +2645,7 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) s->features |= (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS); scsi_realize(&s->qdev, errp); - scsi_generic_read_device_identification(&s->qdev); + scsi_generic_read_device_inquiry(&s->qdev); } typedef struct SCSIBlockReq { diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 61abc2763a..d60c4d0fcf 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -144,6 +144,8 @@ static int execute_command(BlockBackend *blk, static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) { + uint8_t page, page_len; + /* * EVPD set to zero returns the standard INQUIRY data. * @@ -167,22 +169,57 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) s->scsi_version = r->buf[2]; } } - if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) { - uint32_t max_transfer = - blk_get_max_transfer(s->conf.blk) / s->blocksize; - assert(max_transfer); - stl_be_p(&r->buf[8], max_transfer); - /* Also take care of the opt xfer len. */ - stl_be_p(&r->buf[12], - MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); + if (s->type == TYPE_DISK && (r->req.cmd.buf[1] & 0x01)) { + page = r->req.cmd.buf[2]; + if (page == 0xb0) { + uint32_t max_transfer = + blk_get_max_transfer(s->conf.blk) / s->blocksize; + + assert(max_transfer); + stl_be_p(&r->buf[8], max_transfer); + /* Also take care of the opt xfer len. */ + stl_be_p(&r->buf[12], + MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); + } else if (page == 0x00 && s->needs_vpd_bl_emulation) { + /* + * Now we're capable of supplying the VPD Block Limits + * response if the hardware can't. Add it in the INQUIRY + * Supported VPD pages response in case we are using the + * emulation for this device. + * + * This way, the guest kernel will be aware of the support + * and will use it to proper setup the SCSI device. + */ + page_len = r->buf[3]; + r->buf[page_len + 4] = 0xb0; + r->buf[3] = ++page_len; + } } } +static int scsi_emulate_block_limits(SCSIGenericReq *r) +{ + r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf); + r->io_header.sb_len_wr = 0; + + /* + * We have valid contents in the reply buffer but the + * io_header can report a sense error coming from + * the hardware in scsi_command_complete_noio. Clean + * up the io_header to avoid reporting it. + */ + r->io_header.driver_status = 0; + r->io_header.status = 0; + + return r->buflen; +} + static void scsi_read_complete(void * opaque, int ret) { SCSIGenericReq *r = (SCSIGenericReq *)opaque; SCSIDevice *s = r->req.dev; + SCSISense sense; int len; assert(r->req.aiocb != NULL); @@ -199,6 +236,27 @@ static void scsi_read_complete(void * opaque, int ret) DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); r->len = -1; + + /* + * Check if this is a VPD Block Limits request that + * resulted in sense error but would need emulation. + * In this case, emulate a valid VPD response. + */ + if (s->needs_vpd_bl_emulation) { + int is_vpd_bl = r->req.cmd.buf[0] == INQUIRY && + r->req.cmd.buf[1] & 0x01 && + r->req.cmd.buf[2] == 0xb0; + + if (is_vpd_bl && sg_io_sense_from_errno(-ret, &r->io_header, &sense)) { + len = scsi_emulate_block_limits(r); + /* + * No need to let scsi_read_complete go on and handle an + * INQUIRY VPD BL request we created manually. + */ + goto req_complete; + } + } + if (len == 0) { scsi_command_complete_noio(r, 0); goto done; @@ -233,6 +291,8 @@ static void scsi_read_complete(void * opaque, int ret) if (r->req.cmd.buf[0] == INQUIRY) { scsi_handle_inquiry_reply(r, s); } + +req_complete: scsi_req_data(&r->req, len); scsi_req_unref(&r->req); @@ -434,7 +494,49 @@ int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, return 0; } -void scsi_generic_read_device_identification(SCSIDevice *s) +/* + * Executes an INQUIRY request with EVPD set to retrieve the + * available VPD pages of the device. If the device does + * not support the Block Limits page (page 0xb0), set + * the needs_vpd_bl_emulation flag for future use. + */ +static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s) +{ + uint8_t cmd[6]; + uint8_t buf[250]; + uint8_t page_len; + int ret, i; + + memset(cmd, 0, sizeof(cmd)); + memset(buf, 0, sizeof(buf)); + cmd[0] = INQUIRY; + cmd[1] = 1; + cmd[2] = 0x00; + cmd[4] = sizeof(buf); + + ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd), + buf, sizeof(buf)); + if (ret < 0) { + /* + * Do not assume anything if we can't retrieve the + * INQUIRY response to assert the VPD Block Limits + * support. + */ + s->needs_vpd_bl_emulation = false; + return; + } + + page_len = buf[3]; + for (i = 4; i < page_len + 4; i++) { + if (buf[i] == 0xb0) { + s->needs_vpd_bl_emulation = false; + return; + } + } + s->needs_vpd_bl_emulation = true; +} + +static void scsi_generic_read_device_identification(SCSIDevice *s) { uint8_t cmd[6]; uint8_t buf[250]; @@ -479,6 +581,16 @@ void scsi_generic_read_device_identification(SCSIDevice *s) } } +void scsi_generic_read_device_inquiry(SCSIDevice *s) +{ + scsi_generic_read_device_identification(s); + if (s->type == TYPE_DISK) { + scsi_generic_set_vpd_bl_emulation(s); + } else { + s->needs_vpd_bl_emulation = false; + } +} + static int get_stream_blocksize(BlockBackend *blk) { uint8_t cmd[6]; @@ -580,7 +692,7 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp) /* Only used by scsi-block, but initialize it nevertheless to be clean. */ s->default_scsi_version = -1; - scsi_generic_read_device_identification(s); + scsi_generic_read_device_inquiry(s); } const SCSIReqOps scsi_generic_req_ops = { diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 75eced34d3..21a3a6fec2 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -87,6 +87,7 @@ struct SCSIDevice uint64_t port_wwn; int scsi_version; int default_scsi_version; + bool needs_vpd_bl_emulation; }; extern const VMStateDescription vmstate_scsi_device; @@ -184,7 +185,7 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense); void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense); void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); void scsi_device_unit_attention_reported(SCSIDevice *dev); -void scsi_generic_read_device_identification(SCSIDevice *dev); +void scsi_generic_read_device_inquiry(SCSIDevice *dev); int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf); int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, From 28a3cfc10b2e1a34985797357b4aa7558a63d08f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 22 May 2018 10:30:31 +0200 Subject: [PATCH 1723/2380] tests/boot-serial: Do not delete the output file in case of errors Peter reported that the boot-serial tester sometimes runs into timeouts with SPARC guests. It's currently completely unclear whether this is due to too much load on the host machine (so that the guest really just ran too slow), or whether there is something wrong with the guest's firmware boot. For further debugging, we need the serial output of the guest in case of errors, so instead of unlinking the file immediately, this is now only done in case of success. In case of error, print the name of the file with the serial output via g_error() (which then also calls abort() internally to mark the test as failed). Signed-off-by: Thomas Huth Message-Id: <1526977831-31129-1-git-send-email-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- tests/boot-serial-test.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c index 4d6815c3e0..952a2e7ead 100644 --- a/tests/boot-serial-test.c +++ b/tests/boot-serial-test.c @@ -111,9 +111,8 @@ static testdef_t tests[] = { { NULL } }; -static void check_guest_output(const testdef_t *test, int fd) +static bool check_guest_output(const testdef_t *test, int fd) { - bool output_ok = false; int i, nbr = 0, pos = 0, ccnt; char ch; @@ -125,8 +124,7 @@ static void check_guest_output(const testdef_t *test, int fd) pos += 1; if (test->expect[pos] == '\0') { /* We've reached the end of the expected string! */ - output_ok = true; - goto done; + return true; } } else { pos = 0; @@ -136,8 +134,7 @@ static void check_guest_output(const testdef_t *test, int fd) g_usleep(10000); } -done: - g_assert(output_ok); + return false; } static void test_machine(const void *data) @@ -180,12 +177,16 @@ static void test_machine(const void *data) "-no-shutdown -serial chardev:serial0 %s", codeparam, code ? codetmp : "", test->machine, serialtmp, test->extra); - unlink(serialtmp); if (code) { unlink(codetmp); } - check_guest_output(test, ser_fd); + if (!check_guest_output(test, ser_fd)) { + g_error("Failed to find expected string. Please check '%s'", + serialtmp); + } + unlink(serialtmp); + qtest_quit(global_qtest); close(ser_fd); From c5f1d0c493f29f0137fbd79a2c6653f8546f2044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 6 Jun 2018 18:15:38 +0100 Subject: [PATCH 1724/2380] util: remove redundant include of glib.h and add osdep.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code must only ever include glib.h indirectly via the glib-compat.h header file, because we will need some macros set before glib.h is pulled in. Adding extra includes of glib.h will (soon) cause compile failures such as: In file included from /home/berrange/src/virt/qemu/include/qemu/osdep.h:107, from /home/berrange/src/virt/qemu/include/qemu/iova-tree.h:26, from util/iova-tree.c:13: /home/berrange/src/virt/qemu/include/glib-compat.h:22: error: "GLIB_VERSION_MIN_REQUIRED" redefined [-Werror] #define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_40 In file included from /usr/include/glib-2.0/glib/gtypes.h:34, from /usr/include/glib-2.0/glib/galloca.h:32, from /usr/include/glib-2.0/glib.h:30, from util/iova-tree.c:12: /usr/include/glib-2.0/glib/gversionmacros.h:237: note: this is the location of the previous definition # define GLIB_VERSION_MIN_REQUIRED (GLIB_VERSION_CUR_STABLE) Furthermore, the osdep.h include should always be done directly from the .c file rather than indirectly via any .h file. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- include/qemu/iova-tree.h | 1 - util/iova-tree.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h index b061932097..b66cf93c4b 100644 --- a/include/qemu/iova-tree.h +++ b/include/qemu/iova-tree.h @@ -23,7 +23,6 @@ * for the thread safety issue. */ -#include "qemu/osdep.h" #include "exec/memory.h" #include "exec/hwaddr.h" diff --git a/util/iova-tree.c b/util/iova-tree.c index 2d9cebfc89..7990692cbd 100644 --- a/util/iova-tree.c +++ b/util/iova-tree.c @@ -9,7 +9,7 @@ * This work is licensed under the terms of the GNU GPL, version 2 or later. */ -#include +#include "qemu/osdep.h" #include "qemu/iova-tree.h" struct IOVATree { From e7b3af81597db1a6b55f2c15d030d703c6b2c6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 4 May 2018 15:34:46 +0100 Subject: [PATCH 1725/2380] glib: bump min required glib library version to 2.40 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per supported platforms doc[1], the various min glib on relevant distros is: RHEL-7: 2.50.3 Debian (Stretch): 2.50.3 Debian (Jessie): 2.42.1 OpenBSD (Ports): 2.54.3 FreeBSD (Ports): 2.50.3 OpenSUSE Leap 15: 2.54.3 SLE12-SP2: 2.48.2 Ubuntu (Xenial): 2.48.0 macOS (Homebrew): 2.56.0 This suggests that a minimum glib of 2.42 is a reasonable target. The GLibC compile farm, however, uses Ubuntu 14.04 (Trusty) which only has glib 2.40.0, and this is needed for testing during merge. Thus an exception is made to the documented platform support policy to allow for all three current LTS releases to be supported. Docker jobs that not longer satisfy this new min version are removed. [1] https://qemu.weilnetz.de/doc/qemu-doc.html#Supported-build-platforms Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé --- configure | 6 +- crypto/hash-glib.c | 4 - crypto/hmac-glib.c | 36 --- include/glib-compat.h | 319 ----------------------- qga/commands.c | 11 +- tests/docker/dockerfiles/centos6.docker | 30 --- tests/docker/dockerfiles/min-glib.docker | 8 - tests/ivshmem-test.c | 6 - tests/test-qmp-event.c | 8 +- tests/tpm-emu.h | 4 +- tests/vhost-user-test.c | 26 +- trace/simple.c | 6 +- util/osdep.c | 14 - 13 files changed, 11 insertions(+), 467 deletions(-) delete mode 100644 tests/docker/dockerfiles/centos6.docker delete mode 100644 tests/docker/dockerfiles/min-glib.docker diff --git a/configure b/configure index 4d12cfbe3f..109ed030fd 100755 --- a/configure +++ b/configure @@ -3433,11 +3433,7 @@ fi ########################################## # glib support probe -if test "$mingw32" = yes; then - glib_req_ver=2.30 -else - glib_req_ver=2.22 -fi +glib_req_ver=2.40 glib_modules=gthread-2.0 if test "$modules" = yes; then glib_modules="$glib_modules gmodule-export-2.0" diff --git a/crypto/hash-glib.c b/crypto/hash-glib.c index a5871cc72f..a0096c7c47 100644 --- a/crypto/hash-glib.c +++ b/crypto/hash-glib.c @@ -30,11 +30,7 @@ static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { [QCRYPTO_HASH_ALG_SHA224] = -1, [QCRYPTO_HASH_ALG_SHA256] = G_CHECKSUM_SHA256, [QCRYPTO_HASH_ALG_SHA384] = -1, -#if GLIB_CHECK_VERSION(2, 36, 0) [QCRYPTO_HASH_ALG_SHA512] = G_CHECKSUM_SHA512, -#else - [QCRYPTO_HASH_ALG_SHA512] = -1, -#endif [QCRYPTO_HASH_ALG_RIPEMD160] = -1, }; diff --git a/crypto/hmac-glib.c b/crypto/hmac-glib.c index a6c1730291..7df627329d 100644 --- a/crypto/hmac-glib.c +++ b/crypto/hmac-glib.c @@ -17,9 +17,6 @@ #include "crypto/hmac.h" #include "hmacpriv.h" -/* Support for HMAC Algos has been added in GLib 2.30 */ -#if GLIB_CHECK_VERSION(2, 30, 0) - static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = { [QCRYPTO_HASH_ALG_MD5] = G_CHECKSUM_MD5, [QCRYPTO_HASH_ALG_SHA1] = G_CHECKSUM_SHA1, @@ -126,39 +123,6 @@ qcrypto_glib_hmac_bytesv(QCryptoHmac *hmac, return 0; } -#else - -bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) -{ - return false; -} - -void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, - const uint8_t *key, size_t nkey, - Error **errp) -{ - return NULL; -} - -static void -qcrypto_glib_hmac_ctx_free(QCryptoHmac *hmac) -{ - return; -} - -static int -qcrypto_glib_hmac_bytesv(QCryptoHmac *hmac, - const struct iovec *iov, - size_t niov, - uint8_t **result, - size_t *resultlen, - Error **errp) -{ - return -1; -} - -#endif - QCryptoHmacDriver qcrypto_hmac_lib_driver = { .hmac_bytesv = qcrypto_glib_hmac_bytesv, .hmac_free = qcrypto_glib_hmac_ctx_free, diff --git a/include/glib-compat.h b/include/glib-compat.h index c49cf87196..3b340ab33c 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -18,27 +18,6 @@ #include -/* GLIB version compatibility flags */ -#if !GLIB_CHECK_VERSION(2, 26, 0) -#define G_TIME_SPAN_SECOND (G_GINT64_CONSTANT(1000000)) -#endif - -#if !GLIB_CHECK_VERSION(2, 28, 0) -static inline gint64 qemu_g_get_monotonic_time(void) -{ - /* g_get_monotonic_time() is best-effort so we can use the wall clock as a - * fallback. - */ - - GTimeVal time; - g_get_current_time(&time); - - return time.tv_sec * G_TIME_SPAN_SECOND + time.tv_usec; -} -/* work around distro backports of this interface */ -#define g_get_monotonic_time() qemu_g_get_monotonic_time() -#endif - #if defined(_WIN32) && !GLIB_CHECK_VERSION(2, 50, 0) /* * g_poll has a problem on Windows when using @@ -48,228 +27,6 @@ static inline gint64 qemu_g_get_monotonic_time(void) gint g_poll_fixed(GPollFD *fds, guint nfds, gint timeout); #endif -#if !GLIB_CHECK_VERSION(2, 30, 0) -/* Not a 100% compatible implementation, but good enough for most - * cases. Placeholders are only supported at the end of the - * template. */ -static inline gchar *qemu_g_dir_make_tmp(gchar const *tmpl, GError **error) -{ - gchar *path = g_build_filename(g_get_tmp_dir(), tmpl ?: ".XXXXXX", NULL); - - if (mkdtemp(path) != NULL) { - return path; - } - /* Error occurred, clean up. */ - g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), - "mkdtemp() failed"); - g_free(path); - return NULL; -} -#define g_dir_make_tmp(tmpl, error) qemu_g_dir_make_tmp(tmpl, error) -#endif /* glib 2.30 */ - -#if !GLIB_CHECK_VERSION(2, 31, 0) -/* before glib-2.31, GMutex and GCond was dynamic-only (there was a separate - * GStaticMutex, but it didn't work with condition variables). - * - * Our implementation uses GOnce to fake a static implementation that does - * not require separate initialization. - * We need to rename the types to avoid passing our CompatGMutex/CompatGCond - * by mistake to a function that expects GMutex/GCond. However, for ease - * of use we keep the GLib function names. GLib uses macros for the - * implementation, we use inline functions instead and undefine the macros. - */ - -typedef struct CompatGMutex { - GOnce once; -} CompatGMutex; - -typedef struct CompatGCond { - GOnce once; -} CompatGCond; - -static inline gpointer do_g_mutex_new(gpointer unused) -{ - return (gpointer) g_mutex_new(); -} - -static inline void g_mutex_init(CompatGMutex *mutex) -{ - mutex->once = (GOnce) G_ONCE_INIT; -} - -static inline void g_mutex_clear(CompatGMutex *mutex) -{ - g_assert(mutex->once.status != G_ONCE_STATUS_PROGRESS); - if (mutex->once.retval) { - g_mutex_free((GMutex *) mutex->once.retval); - } - mutex->once = (GOnce) G_ONCE_INIT; -} - -static inline void (g_mutex_lock)(CompatGMutex *mutex) -{ - g_once(&mutex->once, do_g_mutex_new, NULL); - g_mutex_lock((GMutex *) mutex->once.retval); -} -#undef g_mutex_lock - -static inline gboolean (g_mutex_trylock)(CompatGMutex *mutex) -{ - g_once(&mutex->once, do_g_mutex_new, NULL); - return g_mutex_trylock((GMutex *) mutex->once.retval); -} -#undef g_mutex_trylock - - -static inline void (g_mutex_unlock)(CompatGMutex *mutex) -{ - g_mutex_unlock((GMutex *) mutex->once.retval); -} -#undef g_mutex_unlock - -static inline gpointer do_g_cond_new(gpointer unused) -{ - return (gpointer) g_cond_new(); -} - -static inline void g_cond_init(CompatGCond *cond) -{ - cond->once = (GOnce) G_ONCE_INIT; -} - -static inline void g_cond_clear(CompatGCond *cond) -{ - g_assert(cond->once.status != G_ONCE_STATUS_PROGRESS); - if (cond->once.retval) { - g_cond_free((GCond *) cond->once.retval); - } - cond->once = (GOnce) G_ONCE_INIT; -} - -static inline void (g_cond_wait)(CompatGCond *cond, CompatGMutex *mutex) -{ - g_assert(mutex->once.status != G_ONCE_STATUS_PROGRESS); - g_once(&cond->once, do_g_cond_new, NULL); - g_cond_wait((GCond *) cond->once.retval, (GMutex *) mutex->once.retval); -} -#undef g_cond_wait - -static inline void (g_cond_broadcast)(CompatGCond *cond) -{ - g_once(&cond->once, do_g_cond_new, NULL); - g_cond_broadcast((GCond *) cond->once.retval); -} -#undef g_cond_broadcast - -static inline void (g_cond_signal)(CompatGCond *cond) -{ - g_once(&cond->once, do_g_cond_new, NULL); - g_cond_signal((GCond *) cond->once.retval); -} -#undef g_cond_signal - -static inline gboolean (g_cond_timed_wait)(CompatGCond *cond, - CompatGMutex *mutex, - GTimeVal *time) -{ - g_assert(mutex->once.status != G_ONCE_STATUS_PROGRESS); - g_once(&cond->once, do_g_cond_new, NULL); - return g_cond_timed_wait((GCond *) cond->once.retval, - (GMutex *) mutex->once.retval, time); -} -#undef g_cond_timed_wait - -/* This is not a macro, because it didn't exist until 2.32. */ -static inline gboolean g_cond_wait_until(CompatGCond *cond, CompatGMutex *mutex, - gint64 end_time) -{ - GTimeVal time; - - /* Convert from monotonic to CLOCK_REALTIME. */ - end_time -= g_get_monotonic_time(); - g_get_current_time(&time); - end_time += time.tv_sec * G_TIME_SPAN_SECOND + time.tv_usec; - - time.tv_sec = end_time / G_TIME_SPAN_SECOND; - time.tv_usec = end_time % G_TIME_SPAN_SECOND; - return g_cond_timed_wait(cond, mutex, &time); -} - -/* before 2.31 there was no g_thread_new() */ -static inline GThread *g_thread_new(const char *name, - GThreadFunc func, gpointer data) -{ - GThread *thread = g_thread_create(func, data, TRUE, NULL); - if (!thread) { - g_error("creating thread"); - } - return thread; -} -#else -#define CompatGMutex GMutex -#define CompatGCond GCond -#endif /* glib 2.31 */ - -#if !GLIB_CHECK_VERSION(2, 32, 0) -/* Beware, function returns gboolean since 2.39.2, see GLib commit 9101915 */ -static inline void g_hash_table_add(GHashTable *hash_table, gpointer key) -{ - g_hash_table_replace(hash_table, key, key); -} - -static inline gboolean g_hash_table_contains(GHashTable *hash_table, - gpointer key) -{ - return g_hash_table_lookup_extended(hash_table, key, NULL, NULL); -} -#define G_SOURCE_CONTINUE TRUE -#define G_SOURCE_REMOVE FALSE -#endif - -#ifndef g_assert_true -#define g_assert_true(expr) \ - do { \ - if (G_LIKELY(expr)) { \ - } else { \ - g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - "'" #expr "' should be TRUE"); \ - } \ - } while (0) -#endif - -#ifndef g_assert_false -#define g_assert_false(expr) \ - do { \ - if (G_LIKELY(!(expr))) { \ - } else { \ - g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - "'" #expr "' should be FALSE"); \ - } \ - } while (0) -#endif - -#ifndef g_assert_null -#define g_assert_null(expr) \ - do { \ - if (G_LIKELY((expr) == NULL)) { \ - } else { \ - g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - "'" #expr "' should be NULL"); \ - } \ - } while (0) -#endif - -#ifndef g_assert_nonnull -#define g_assert_nonnull(expr) \ - do { \ - if (G_LIKELY((expr) != NULL)) { \ - } else { \ - g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - "'" #expr "' should not be NULL"); \ - } \ - } while (0) -#endif #ifndef g_assert_cmpmem #define g_assert_cmpmem(m1, l1, m2, l2) \ @@ -288,80 +45,4 @@ static inline gboolean g_hash_table_contains(GHashTable *hash_table, } while (0) #endif -#if !GLIB_CHECK_VERSION(2, 28, 0) -static inline void g_list_free_full(GList *list, GDestroyNotify free_func) -{ - GList *l; - - for (l = list; l; l = l->next) { - free_func(l->data); - } - - g_list_free(list); -} - -static inline void g_slist_free_full(GSList *list, GDestroyNotify free_func) -{ - GSList *l; - - for (l = list; l; l = l->next) { - free_func(l->data); - } - - g_slist_free(list); -} -#endif - -#if !GLIB_CHECK_VERSION(2, 26, 0) -static inline void g_source_set_name(GSource *source, const char *name) -{ - /* This is just a debugging aid, so leaving it a no-op */ -} -static inline void g_source_set_name_by_id(guint tag, const char *name) -{ - /* This is just a debugging aid, so leaving it a no-op */ -} -#endif - -#if !GLIB_CHECK_VERSION(2, 36, 0) -/* Always fail. This will not include error_report output in the test log, - * sending it instead to stderr. - */ -#define g_test_initialized() (0) -#endif -#if !GLIB_CHECK_VERSION(2, 38, 0) -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS -#error schizophrenic detection of glib subprocess testing -#endif -#define g_test_subprocess() (0) -#endif - - -#if !GLIB_CHECK_VERSION(2, 34, 0) -static inline void -g_test_add_data_func_full(const char *path, - gpointer data, - gpointer fn, - gpointer data_free_func) -{ -#if GLIB_CHECK_VERSION(2, 26, 0) - /* back-compat casts, remove this once we can require new-enough glib */ - g_test_add_vtable(path, 0, data, NULL, - (GTestFixtureFunc)fn, (GTestFixtureFunc) data_free_func); -#else - /* back-compat casts, remove this once we can require new-enough glib */ - g_test_add_vtable(path, 0, data, NULL, - (void (*)(void)) fn, (void (*)(void)) data_free_func); -#endif -} -#endif - -/* Small compat shim from glib 2.32 */ -#ifndef G_SOURCE_CONTINUE -#define G_SOURCE_CONTINUE TRUE -#endif -#ifndef G_SOURCE_REMOVE -#define G_SOURCE_REMOVE FALSE -#endif - #endif diff --git a/qga/commands.c b/qga/commands.c index cce3010f0f..0c7d1385c2 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -414,10 +414,8 @@ GuestExec *qmp_guest_exec(const char *path, argv = guest_exec_get_args(&arglist, true); envp = has_env ? guest_exec_get_args(env, false) : NULL; - flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD; -#if GLIB_CHECK_VERSION(2, 33, 2) - flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP; -#endif + flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD | + G_SPAWN_SEARCH_PATH_FROM_ENVP; if (!has_output) { flags |= G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL; } @@ -514,7 +512,6 @@ GuestHostName *qmp_guest_get_host_name(Error **err) GuestTimezone *qmp_guest_get_timezone(Error **errp) { -#if GLIB_CHECK_VERSION(2, 28, 0) GuestTimezone *info = NULL; GTimeZone *tz = NULL; gint64 now = 0; @@ -544,8 +541,4 @@ GuestTimezone *qmp_guest_get_timezone(Error **errp) error: g_free(info); return NULL; -#else - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -#endif } diff --git a/tests/docker/dockerfiles/centos6.docker b/tests/docker/dockerfiles/centos6.docker deleted file mode 100644 index ad24319582..0000000000 --- a/tests/docker/dockerfiles/centos6.docker +++ /dev/null @@ -1,30 +0,0 @@ -FROM centos:6 -RUN yum install -y epel-release centos-release-xen -ENV PACKAGES \ - bison \ - bzip2-devel \ - ccache \ - csnappy-devel \ - flex \ - g++ \ - gcc \ - gettext \ - git \ - glib2-devel \ - libepoxy-devel \ - libfdt-devel \ - librdmacm-devel \ - lzo-devel \ - make \ - mesa-libEGL-devel \ - mesa-libgbm-devel \ - pixman-devel \ - SDL-devel \ - spice-glib-devel \ - spice-server-devel \ - tar \ - vte-devel \ - xen-devel \ - zlib-devel -RUN yum install -y $PACKAGES -RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/min-glib.docker b/tests/docker/dockerfiles/min-glib.docker deleted file mode 100644 index f2eed97d35..0000000000 --- a/tests/docker/dockerfiles/min-glib.docker +++ /dev/null @@ -1,8 +0,0 @@ -FROM centos:6 -RUN yum install -y \ - tar gettext git make gcc g++ \ - zlib-devel SDL-devel pixman-devel \ - epel-release -RUN yum install -y libfdt-devel ccache -RUN yum downgrade -y http://vault.centos.org/6.0/os/x86_64/Packages/glib2-2.22.5-5.el6.x86_64.rpm -RUN yum install -y http://vault.centos.org/6.0/os/x86_64/Packages/glib2-devel-2.22.5-5.el6.x86_64.rpm diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index 8af16ee79a..9b407a3e42 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -504,12 +504,6 @@ int main(int argc, char **argv) const char *arch = qtest_get_arch(); gchar dir[] = "/tmp/ivshmem-test.XXXXXX"; -#if !GLIB_CHECK_VERSION(2, 31, 0) - if (!g_thread_supported()) { - g_thread_init(NULL); - } -#endif - g_test_init(&argc, &argv, NULL); qtest_add_abrt_handler(abrt_handler, NULL); diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index 3a7c227a1d..8677094ad1 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -32,7 +32,7 @@ typedef struct QDictCmpData { } QDictCmpData; TestEventData *test_event_data; -static CompatGMutex test_event_lock; +static GMutex test_event_lock; /* Only compares bool, int, string */ static @@ -242,12 +242,6 @@ static void test_event_d(TestEventData *data, int main(int argc, char **argv) { -#if !GLIB_CHECK_VERSION(2, 31, 0) - if (!g_thread_supported()) { - g_thread_init(NULL); - } -#endif - qmp_event_set_func_emit(event_test_emit); g_test_init(&argc, &argv, NULL); diff --git a/tests/tpm-emu.h b/tests/tpm-emu.h index ef4bfa8800..08f902485e 100644 --- a/tests/tpm-emu.h +++ b/tests/tpm-emu.h @@ -24,8 +24,8 @@ struct tpm_hdr { } QEMU_PACKED; typedef struct TestState { - CompatGMutex data_mutex; - CompatGCond data_cond; + GMutex data_mutex; + GCond data_cond; SocketAddress *addr; QIOChannel *tpm_ioc; GThread *emu_tpm_thread; diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index bbc8091286..8ff2106d32 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -32,14 +32,6 @@ #include #include -/* GLIB version compatibility flags */ -#if !GLIB_CHECK_VERSION(2, 26, 0) -#define G_TIME_SPAN_SECOND (G_GINT64_CONSTANT(1000000)) -#endif - -#if GLIB_CHECK_VERSION(2, 28, 0) -#define HAVE_MONOTONIC_TIME -#endif #define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM," \ "mem-path=%s,share=on -numa node,memdev=mem" @@ -150,8 +142,8 @@ typedef struct TestServer { int fds_num; int fds[VHOST_MEMORY_MAX_NREGIONS]; VhostUserMemory memory; - CompatGMutex data_mutex; - CompatGCond data_cond; + GMutex data_mutex; + GCond data_cond; int log_fd; uint64_t rings; bool test_fail; @@ -642,21 +634,7 @@ test_migrate_source_check(GSource *source) return FALSE; } -#if !GLIB_CHECK_VERSION(2,36,0) -/* this callback is unnecessary with glib >2.36, the default - * prepare for the source does the same */ -static gboolean -test_migrate_source_prepare(GSource *source, gint *timeout) -{ - *timeout = -1; - return FALSE; -} -#endif - GSourceFuncs test_migrate_source_funcs = { -#if !GLIB_CHECK_VERSION(2,36,0) - .prepare = test_migrate_source_prepare, -#endif .check = test_migrate_source_check, }; diff --git a/trace/simple.c b/trace/simple.c index e82018d923..701dec639c 100644 --- a/trace/simple.c +++ b/trace/simple.c @@ -36,9 +36,9 @@ * Trace records are written out by a dedicated thread. The thread waits for * records to become available, writes them out, and then waits again. */ -static CompatGMutex trace_lock; -static CompatGCond trace_available_cond; -static CompatGCond trace_empty_cond; +static GMutex trace_lock; +static GCond trace_available_cond; +static GCond trace_empty_cond; static bool trace_available; static bool trace_writeout_enabled; diff --git a/util/osdep.c b/util/osdep.c index ea51d500b6..1c8d1e2ee0 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -504,20 +504,6 @@ int socket_init(void) return 0; } -#if !GLIB_CHECK_VERSION(2, 31, 0) -/* Ensure that glib is running in multi-threaded mode - * Old versions of glib require explicit initialization. Failure to do - * this results in the single-threaded code paths being taken inside - * glib. For example, the g_slice allocator will not be thread-safe - * and cause crashes. - */ -static void __attribute__((constructor)) thread_init(void) -{ - if (!g_thread_supported()) { - g_thread_init(NULL); - } -} -#endif #ifndef CONFIG_IOVEC /* helper function for iov_send_recv() */ From e71e8cc035558eabd6b3e19f6d3254c754c027ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 4 May 2018 16:25:00 +0100 Subject: [PATCH 1726/2380] glib: enforce the minimum required version and warn about old APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two useful macros that can be defined before including glib.h that are related to the min required glib version - GLIB_VERSION_MIN_REQUIRED When this is defined, if code uses an API that was deprecated in this version, or older, a compiler warning will be emitted. This alerts maintainers to update their code to whatever new replacement API is now recommended best practice. - GLIB_VERSION_MAX_ALLOWED When this is defined, if code uses an API that was introduced in a version that is newer than the declared version, a compiler warning will be emitted. This alerts maintainers if new code accidentally uses functionality that won't be available on some supported platforms. The GLIB_VERSION_MAX_ALLOWED constant makes it a bit harder to opt in to using specific new APIs with a GLIB_CHECK_VERSION conditional. To workaround this Pragmas can be used to temporarily turn off the -Wdeprecated-declarations compiler warning, while a static inline compat function is implemented. This workaround is illustrated with the implementation of the g_strv_contains method to satisfy the test suite. Signed-off-by: Daniel P. Berrangé --- include/glib-compat.h | 68 +++++++++++++++++++++++++++++++++++++++++++ tests/test-qga.c | 2 -- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/include/glib-compat.h b/include/glib-compat.h index 3b340ab33c..fdf95a255d 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -16,8 +16,74 @@ #ifndef QEMU_GLIB_COMPAT_H #define QEMU_GLIB_COMPAT_H +/* Ask for warnings for anything that was marked deprecated in + * the defined version, or before. It is a candidate for rewrite. + */ +#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_40 + +/* Ask for warnings if code tries to use function that did not + * exist in the defined version. These risk breaking builds + */ +#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_40 + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #include +/* + * Note that because of the GLIB_VERSION_MAX_ALLOWED constant above, allowing + * use of functions from newer GLib via this compat header needs a little + * trickery to prevent warnings being emitted. + * + * Consider a function from newer glib-X.Y that we want to use + * + * int g_foo(const char *wibble) + * + * We must define a static inline function with the same signature that does + * what we need, but with a "_qemu" suffix e.g. + * + * static inline void g_foo_qemu(const char *wibble) + * { + * #if GLIB_CHECK_VERSION(X, Y, 0) + * g_foo(wibble) + * #else + * g_something_equivalent_in_older_glib(wibble); + * #endif + * } + * + * The #pragma at the top of this file turns off -Wdeprecated-declarations, + * ensuring this wrapper function impl doesn't trigger the compiler warning + * about using too new glib APIs. Finally we can do + * + * #define g_foo(a) g_foo_qemu(a) + * + * So now the code elsewhere in QEMU, which *does* have the + * -Wdeprecated-declarations warning active, can call g_foo(...) as normal, + * without generating warnings. + */ + +static inline gboolean g_strv_contains_qemu(const gchar *const *strv, + const gchar *str) +{ +#if GLIB_CHECK_VERSION(2, 44, 0) + return g_strv_contains(strv, str); +#else + g_return_val_if_fail(strv != NULL, FALSE); + g_return_val_if_fail(str != NULL, FALSE); + + for (; *strv != NULL; strv++) { + if (g_str_equal(str, *strv)) { + return TRUE; + } + } + + return FALSE; +#endif +} +#define g_strv_contains(a, b) g_strv_contains_qemu(a, b) + + #if defined(_WIN32) && !GLIB_CHECK_VERSION(2, 50, 0) /* * g_poll has a problem on Windows when using @@ -45,4 +111,6 @@ gint g_poll_fixed(GPollFD *fds, guint nfds, gint timeout); } while (0) #endif +#pragma GCC diagnostic pop + #endif diff --git a/tests/test-qga.c b/tests/test-qga.c index 18e63cb533..30c9643257 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -744,12 +744,10 @@ static void test_qga_config(gconstpointer data) strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, &error); g_assert_cmpint(n, ==, 2); -#if GLIB_CHECK_VERSION(2, 44, 0) g_assert_true(g_strv_contains((const char * const *)strv, "guest-ping")); g_assert_true(g_strv_contains((const char * const *)strv, "guest-get-time")); -#endif g_assert_no_error(error); g_strfreev(strv); From 17e9aa3f220df64fc288a175dbd62324e92d850c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 26 Jun 2018 10:05:45 +0200 Subject: [PATCH 1727/2380] block-qdict: Pacify Coverity after commit f1b34a248e9 Commit f1b34a248e9 replaced less-than-obvious test in qdict_flatten_qdict() by the obvious one. Sadly, it made something else non-obvious: the fact that @new_key passed to qdict_put_obj() can't be null, because that depends on the function's precondition (target == qdict) == !prefix. Tweak the function some more to help Coverity and human readers alike. Fixes: CID 1393620 Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- qobject/block-qdict.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index 36129e7379..80c653013f 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -97,7 +97,7 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) const QDictEntry *entry, *next; QDict *dict_val; QList *list_val; - char *new_key; + char *key, *new_key; entry = qdict_first(qdict); @@ -106,10 +106,12 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) value = qdict_entry_value(entry); dict_val = qobject_to(QDict, value); list_val = qobject_to(QList, value); - new_key = NULL; if (prefix) { - new_key = g_strdup_printf("%s.%s", prefix, entry->key); + key = new_key = g_strdup_printf("%s.%s", prefix, entry->key); + } else { + key = entry->key; + new_key = NULL; } /* @@ -125,19 +127,17 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) * well advised not to modify them altogether.) */ if (dict_val && qdict_size(dict_val)) { - qdict_flatten_qdict(dict_val, target, - new_key ? new_key : entry->key); + qdict_flatten_qdict(dict_val, target, key); if (target == qdict) { qdict_del(qdict, entry->key); } } else if (list_val && !qlist_empty(list_val)) { - qdict_flatten_qlist(list_val, target, - new_key ? new_key : entry->key); + qdict_flatten_qlist(list_val, target, key); if (target == qdict) { qdict_del(qdict, entry->key); } } else if (target != qdict) { - qdict_put_obj(target, new_key, qobject_ref(value)); + qdict_put_obj(target, key, qobject_ref(value)); } g_free(new_key); From e6af90f3fb7b87f18f46f6588fbf3e781d6f4ef6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 26 Jun 2018 10:05:46 +0200 Subject: [PATCH 1728/2380] block/crypto: Pacify Coverity after commit f853465aacb Coverity can't see that qobject_input_visitor_new_flat_confused() returns non-null when it doesn't set @local_err. Check the return value instead, like all the other callers do. Fixes: CID 1393615 Fixes: CID 1393616 Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- block/crypto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/crypto.c b/block/crypto.c index 82091c5f70..aaa8fb7530 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -160,7 +160,7 @@ block_crypto_open_opts_init(QCryptoBlockFormat format, ret->format = format; v = qobject_input_visitor_new_flat_confused(opts, &local_err); - if (local_err) { + if (!v) { goto out; } @@ -214,7 +214,7 @@ block_crypto_create_opts_init(QCryptoBlockFormat format, ret->format = format; v = qobject_input_visitor_new_flat_confused(opts, &local_err); - if (local_err) { + if (!v) { goto out; } From 0ab1c41d1cdf50c90ff976d29ac943e727334668 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 26 Jun 2018 10:37:02 +0200 Subject: [PATCH 1729/2380] qapi/job: The next release will be 3.0 Commit 51f63ec7d tried to change all references to 2.13 into 3.0, but it failed to achieve this because it was not properly rebased on top of the series introducing qapi/job.json. Change the references now. Signed-off-by: Kevin Wolf Reviewed-by: Markus Armbruster Reviewed-by: Eric Blake --- qapi/job.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/qapi/job.json b/qapi/job.json index 9d074eb8d2..a121b615fb 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -104,7 +104,7 @@ # @id: The job identifier # @status: The new job status # -# Since: 2.13 +# Since: 3.0 ## { 'event': 'JOB_STATUS_CHANGE', 'data': { 'id': 'str', @@ -126,7 +126,7 @@ # # @id: The job identifier. # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'job-pause', 'data': { 'id': 'str' } } @@ -140,7 +140,7 @@ # # @id : The job identifier. # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'job-resume', 'data': { 'id': 'str' } } @@ -159,7 +159,7 @@ # # @id: The job identifier. # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'job-cancel', 'data': { 'id': 'str' } } @@ -171,7 +171,7 @@ # # @id: The job identifier. # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'job-complete', 'data': { 'id': 'str' } } @@ -187,7 +187,7 @@ # # @id: The job identifier. # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'job-dismiss', 'data': { 'id': 'str' } } @@ -205,7 +205,7 @@ # @id: The identifier of any job in the transaction, or of a job that is not # part of any transaction. # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'job-finalize', 'data': { 'id': 'str' } } @@ -237,7 +237,7 @@ # the reason for the job failure. It should not be parsed # by applications. # -# Since: 2.13 +# Since: 3.0 ## { 'struct': 'JobInfo', 'data': { 'id': 'str', 'type': 'JobType', 'status': 'JobStatus', @@ -251,6 +251,6 @@ # # Returns: a list with a @JobInfo for each active job # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'query-jobs', 'returns': ['JobInfo'] } From b8efb36b9e99dbea7370139c0866b97a933f78d4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 25 Jun 2018 18:39:00 +0200 Subject: [PATCH 1730/2380] usb-storage: Add rerror/werror properties The error handling policy was traditionally set with -drive, but with -blockdev it is no longer possible to set frontend options. scsi-disk (and other block devices) have long supported qdev properties to configure the error handling policy, so let's add these options to usb-storage as well and just forward them to the internal scsi-disk instance. Signed-off-by: Kevin Wolf Reviewed-by: Markus Armbruster --- hw/scsi/scsi-bus.c | 11 ++++++++++- hw/usb/dev-storage.c | 2 ++ include/hw/scsi/scsi.h | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 9646743a7d..5905f6bf29 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -226,6 +226,8 @@ static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp) SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, int unit, bool removable, int bootindex, bool share_rw, + BlockdevOnError rerror, + BlockdevOnError werror, const char *serial, Error **errp) { const char *driver; @@ -262,6 +264,10 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, object_unparent(OBJECT(dev)); return NULL; } + + qdev_prop_set_enum(dev, "rerror", rerror); + qdev_prop_set_enum(dev, "werror", werror); + object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); @@ -285,7 +291,10 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus) } qemu_opts_loc_restore(dinfo->opts); scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), - unit, false, -1, false, NULL, &error_fatal); + unit, false, -1, false, + BLOCKDEV_ON_ERROR_AUTO, + BLOCKDEV_ON_ERROR_AUTO, + NULL, &error_fatal); } loc_pop(&loc); } diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index c99398b7f6..cd5551d94f 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -625,6 +625,7 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) &usb_msd_scsi_info_storage, NULL); scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, s->conf.bootindex, s->conf.share_rw, + s->conf.rerror, s->conf.werror, dev->serial, errp); blk_unref(blk); @@ -671,6 +672,7 @@ static const VMStateDescription vmstate_usb_msd = { static Property msd_properties[] = { DEFINE_BLOCK_PROPERTIES(MSDState, conf), + DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf), DEFINE_PROP_BIT("removable", MSDState, removable, 0, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index e35137ea78..1a7290d563 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -154,6 +154,8 @@ static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, int unit, bool removable, int bootindex, bool share_rw, + BlockdevOnError rerror, + BlockdevOnError werror, const char *serial, Error **errp); void scsi_bus_legacy_handle_cmdline(SCSIBus *bus); void scsi_legacy_handle_cmdline(void); From 7c8952697e9c44931090251e142c1d3108c22be4 Mon Sep 17 00:00:00 2001 From: Weiping Zhang Date: Tue, 26 Jun 2018 09:44:56 +0800 Subject: [PATCH 1731/2380] hw/block/nvme: add optional parameter num_queues for nvme device Add an optional paramter num_queues for device, and set it to 64 by default. Signed-off-by: Weiping Zhang Acked-by: Keith Busch Signed-off-by: Kevin Wolf --- hw/block/nvme.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/block/nvme.c b/hw/block/nvme.c index d5bf95b79b..156ecf3c41 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -18,7 +18,8 @@ * Usage: add options: * -drive file=,if=none,id= * -device nvme,drive=,serial=,id=, \ - * cmb_size_mb= + * cmb_size_mb=, \ + * num_queues= * * Note cmb_size_mb denotes size of CMB in MB. CMB is assumed to be at * offset 0 in BAR2 and supports only WDS, RDS and SQS for now. @@ -1232,7 +1233,6 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) pcie_endpoint_cap_init(&n->parent_obj, 0x80); n->num_namespaces = 1; - n->num_queues = 64; n->reg_size = pow2ceil(0x1004 + 2 * (n->num_queues + 1) * 4); n->ns_size = bs_size / (uint64_t)n->num_namespaces; @@ -1341,6 +1341,7 @@ static Property nvme_props[] = { DEFINE_BLOCK_PROPERTIES(NvmeCtrl, conf), DEFINE_PROP_STRING("serial", NvmeCtrl, serial), DEFINE_PROP_UINT32("cmb_size_mb", NvmeCtrl, cmb_size_mb, 0), + DEFINE_PROP_UINT32("num_queues", NvmeCtrl, num_queues, 64), DEFINE_PROP_END_OF_LIST(), }; From ae5475e82fd1ebb24f4f77cf28f59ca6548c6136 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 26 Jun 2018 13:22:45 +0200 Subject: [PATCH 1732/2380] qcow2: Fix qcow2_truncate() error return value If qcow2_alloc_clusters_at() returns an error, we do need to negate it to get back the positive errno code for error_setg_errno(), but we still need to return the negative error code. Fixes: 772d1f973f87269f6a4a4ea4b880680f3779bbdf Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/qcow2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/qcow2.c b/block/qcow2.c index a3a3aa2a97..6b2e1e1058 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3597,7 +3597,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (clusters_allocated < 0) { error_setg_errno(errp, -clusters_allocated, "Failed to allocate data clusters"); - return -clusters_allocated; + return clusters_allocated; } assert(clusters_allocated == nb_new_data_clusters); From 061ca8a368165fae300748c17971824a089f521f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 21 Jun 2018 17:54:35 +0200 Subject: [PATCH 1733/2380] block: Convert .bdrv_truncate callback to coroutine_fn bdrv_truncate() is an operation that can block (even for a quite long time, depending on the PreallocMode) in I/O paths that shouldn't block. Convert it to a coroutine_fn so that we have the infrastructure for drivers to make their .bdrv_co_truncate implementation asynchronous. This change could potentially introduce new race conditions because bdrv_truncate() isn't necessarily executed atomically any more. Whether this is a problem needs to be evaluated for each block driver that supports truncate: * file-posix/win32, gluster, iscsi, nfs, rbd, ssh, sheepdog: The protocol drivers are trivially safe because they don't actually yield yet, so there is no change in behaviour. * copy-on-read, crypto, raw-format: Essentially just filter drivers that pass the request to a child node, no problem. * qcow2: The implementation modifies metadata, so it needs to hold s->lock to be safe with concurrent I/O requests. In order to avoid double locking, this requires pulling the locking out into preallocate_co() and using qcow2_write_caches() instead of bdrv_flush(). * qed: Does a single header update, this is fine without locking. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block.c | 63 ++++++++++++++++++++++++++++----- block/copy-on-read.c | 8 ++--- block/crypto.c | 9 ++--- block/file-posix.c | 12 +++---- block/file-win32.c | 6 ++-- block/gluster.c | 14 ++++---- block/iscsi.c | 8 ++--- block/nfs.c | 7 ++-- block/qcow2.c | 74 +++++++++++++++++++++++---------------- block/qed.c | 8 +++-- block/raw-format.c | 8 ++--- block/rbd.c | 8 +++-- block/sheepdog.c | 12 +++---- block/ssh.c | 6 ++-- include/block/block.h | 4 +++ include/block/block_int.h | 4 +-- 16 files changed, 162 insertions(+), 89 deletions(-) diff --git a/block.c b/block.c index 1b8147c1b3..f761bc85d5 100644 --- a/block.c +++ b/block.c @@ -3788,8 +3788,8 @@ exit: /** * Truncate file to 'offset' bytes (needed only for file protocols) */ -int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, - Error **errp) +int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, + PreallocMode prealloc, Error **errp) { BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; @@ -3807,23 +3807,28 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, return -EINVAL; } - if (!drv->bdrv_truncate) { + bdrv_inc_in_flight(bs); + + if (!drv->bdrv_co_truncate) { if (bs->file && drv->is_filter) { - return bdrv_truncate(bs->file, offset, prealloc, errp); + ret = bdrv_co_truncate(bs->file, offset, prealloc, errp); + goto out; } error_setg(errp, "Image format driver does not support resize"); - return -ENOTSUP; + ret = -ENOTSUP; + goto out; } if (bs->read_only) { error_setg(errp, "Image is read-only"); - return -EACCES; + ret = -EACCES; + goto out; } assert(!(bs->open_flags & BDRV_O_INACTIVE)); - ret = drv->bdrv_truncate(bs, offset, prealloc, errp); + ret = drv->bdrv_co_truncate(bs, offset, prealloc, errp); if (ret < 0) { - return ret; + goto out; } ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); if (ret < 0) { @@ -3834,9 +3839,51 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, bdrv_dirty_bitmap_truncate(bs, offset); bdrv_parent_cb_resize(bs); atomic_inc(&bs->write_gen); + +out: + bdrv_dec_in_flight(bs); return ret; } +typedef struct TruncateCo { + BdrvChild *child; + int64_t offset; + PreallocMode prealloc; + Error **errp; + int ret; +} TruncateCo; + +static void coroutine_fn bdrv_truncate_co_entry(void *opaque) +{ + TruncateCo *tco = opaque; + tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc, + tco->errp); +} + +int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, + Error **errp) +{ + Coroutine *co; + TruncateCo tco = { + .child = child, + .offset = offset, + .prealloc = prealloc, + .errp = errp, + .ret = NOT_DONE, + }; + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_truncate_co_entry(&tco); + } else { + co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco); + qemu_coroutine_enter(co); + BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE); + } + + return tco.ret; +} + /** * Length of a allocated file in bytes. Sparse files are counted by actual * allocated space. Return < 0 if error or unknown. diff --git a/block/copy-on-read.c b/block/copy-on-read.c index 6a97208888..1dcdaeed69 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -80,10 +80,10 @@ static int64_t cor_getlength(BlockDriverState *bs) } -static int cor_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { - return bdrv_truncate(bs->file, offset, prealloc, errp); + return bdrv_co_truncate(bs->file, offset, prealloc, errp); } @@ -147,7 +147,7 @@ BlockDriver bdrv_copy_on_read = { .bdrv_child_perm = cor_child_perm, .bdrv_getlength = cor_getlength, - .bdrv_truncate = cor_truncate, + .bdrv_co_truncate = cor_co_truncate, .bdrv_co_preadv = cor_co_preadv, .bdrv_co_pwritev = cor_co_pwritev, diff --git a/block/crypto.c b/block/crypto.c index aaa8fb7530..210c80d5c7 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -357,8 +357,9 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, return ret; } -static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn +block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BlockCrypto *crypto = bs->opaque; uint64_t payload_offset = @@ -371,7 +372,7 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, offset += payload_offset; - return bdrv_truncate(bs->file, offset, prealloc, errp); + return bdrv_co_truncate(bs->file, offset, prealloc, errp); } static void block_crypto_close(BlockDriverState *bs) @@ -700,7 +701,7 @@ BlockDriver bdrv_crypto_luks = { .bdrv_child_perm = bdrv_format_default_perms, .bdrv_co_create = block_crypto_co_create_luks, .bdrv_co_create_opts = block_crypto_co_create_opts_luks, - .bdrv_truncate = block_crypto_truncate, + .bdrv_co_truncate = block_crypto_co_truncate, .create_opts = &block_crypto_create_opts_luks, .bdrv_reopen_prepare = block_crypto_reopen_prepare, diff --git a/block/file-posix.c b/block/file-posix.c index 43b963b13e..53c5833659 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1878,8 +1878,8 @@ out: return result; } -static int raw_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVRawState *s = bs->opaque; struct stat st; @@ -2625,7 +2625,7 @@ BlockDriver bdrv_file = { .bdrv_io_unplug = raw_aio_unplug, .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_truncate = raw_truncate, + .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, .bdrv_get_info = raw_get_info, .bdrv_get_allocated_file_size @@ -3105,7 +3105,7 @@ static BlockDriver bdrv_host_device = { .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, - .bdrv_truncate = raw_truncate, + .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, .bdrv_get_info = raw_get_info, .bdrv_get_allocated_file_size @@ -3227,7 +3227,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, - .bdrv_truncate = raw_truncate, + .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, .has_variable_length = true, .bdrv_get_allocated_file_size @@ -3357,7 +3357,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, - .bdrv_truncate = raw_truncate, + .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, .has_variable_length = true, .bdrv_get_allocated_file_size diff --git a/block/file-win32.c b/block/file-win32.c index 3c67db4336..0411fe80fd 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -467,8 +467,8 @@ static void raw_close(BlockDriverState *bs) } } -static int raw_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVRawState *s = bs->opaque; LONG low, high; @@ -640,7 +640,7 @@ BlockDriver bdrv_file = { .bdrv_aio_pwritev = raw_aio_pwritev, .bdrv_aio_flush = raw_aio_flush, - .bdrv_truncate = raw_truncate, + .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, .bdrv_get_allocated_file_size = raw_get_allocated_file_size, diff --git a/block/gluster.c b/block/gluster.c index b5fe7f3e87..a4e1c8ecd8 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -1177,8 +1177,10 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, return acb.ret; } -static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs, + int64_t offset, + PreallocMode prealloc, + Error **errp) { BDRVGlusterState *s = bs->opaque; return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); @@ -1499,7 +1501,7 @@ static BlockDriver bdrv_gluster = { .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, - .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, @@ -1528,7 +1530,7 @@ static BlockDriver bdrv_gluster_tcp = { .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, - .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, @@ -1557,7 +1559,7 @@ static BlockDriver bdrv_gluster_unix = { .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, - .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, @@ -1592,7 +1594,7 @@ static BlockDriver bdrv_gluster_rdma = { .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, - .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, diff --git a/block/iscsi.c b/block/iscsi.c index 9f00fb47a5..bc84b14e20 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2085,8 +2085,8 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state) } } -static int iscsi_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { IscsiLun *iscsilun = bs->opaque; Error *local_err = NULL; @@ -2431,7 +2431,7 @@ static BlockDriver bdrv_iscsi = { .bdrv_getlength = iscsi_getlength, .bdrv_get_info = iscsi_get_info, - .bdrv_truncate = iscsi_truncate, + .bdrv_co_truncate = iscsi_co_truncate, .bdrv_refresh_limits = iscsi_refresh_limits, .bdrv_co_block_status = iscsi_co_block_status, @@ -2468,7 +2468,7 @@ static BlockDriver bdrv_iser = { .bdrv_getlength = iscsi_getlength, .bdrv_get_info = iscsi_get_info, - .bdrv_truncate = iscsi_truncate, + .bdrv_co_truncate = iscsi_co_truncate, .bdrv_refresh_limits = iscsi_refresh_limits, .bdrv_co_block_status = iscsi_co_block_status, diff --git a/block/nfs.c b/block/nfs.c index 743ca0450e..eab1a2c408 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -743,8 +743,9 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) return (task.ret < 0 ? task.ret : st.st_blocks * 512); } -static int nfs_file_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn +nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { NFSClient *client = bs->opaque; int ret; @@ -873,7 +874,7 @@ static BlockDriver bdrv_nfs = { .bdrv_has_zero_init = nfs_has_zero_init, .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, - .bdrv_truncate = nfs_file_truncate, + .bdrv_co_truncate = nfs_file_co_truncate, .bdrv_file_open = nfs_file_open, .bdrv_close = nfs_file_close, diff --git a/block/qcow2.c b/block/qcow2.c index 6b2e1e1058..9e0ccbbfaf 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2543,15 +2543,12 @@ static void coroutine_fn preallocate_co(void *opaque) BlockDriverState *bs = params->bs; uint64_t offset = params->offset; uint64_t new_length = params->new_length; - BDRVQcow2State *s = bs->opaque; uint64_t bytes; uint64_t host_offset = 0; unsigned int cur_bytes; int ret; QCowL2Meta *meta; - qemu_co_mutex_lock(&s->lock); - assert(offset <= new_length); bytes = new_length - offset; @@ -2604,7 +2601,6 @@ static void coroutine_fn preallocate_co(void *opaque) ret = 0; done: - qemu_co_mutex_unlock(&s->lock); params->ret = ret; } @@ -3041,7 +3037,11 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) /* And if we're supposed to preallocate metadata, do that now */ if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) { + BDRVQcow2State *s = blk_bs(blk)->opaque; + qemu_co_mutex_lock(&s->lock); ret = preallocate(blk_bs(blk), 0, qcow2_opts->size); + qemu_co_mutex_unlock(&s->lock); + if (ret < 0) { error_setg_errno(errp, -ret, "Could not preallocate metadata"); goto out; @@ -3437,8 +3437,8 @@ fail: return ret; } -static int qcow2_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVQcow2State *s = bs->opaque; uint64_t old_length; @@ -3458,17 +3458,21 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, return -EINVAL; } + qemu_co_mutex_lock(&s->lock); + /* cannot proceed if image has snapshots */ if (s->nb_snapshots) { error_setg(errp, "Can't resize an image which has snapshots"); - return -ENOTSUP; + ret = -ENOTSUP; + goto fail; } /* cannot proceed if image has bitmaps */ if (s->nb_bitmaps) { /* TODO: resize bitmaps in the image */ error_setg(errp, "Can't resize an image which has bitmaps"); - return -ENOTSUP; + ret = -ENOTSUP; + goto fail; } old_length = bs->total_sectors * 512; @@ -3479,7 +3483,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (prealloc != PREALLOC_MODE_OFF) { error_setg(errp, "Preallocation can't be used for shrinking an image"); - return -EINVAL; + ret = -EINVAL; + goto fail; } ret = qcow2_cluster_discard(bs, ROUND_UP(offset, s->cluster_size), @@ -3488,40 +3493,42 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, QCOW2_DISCARD_ALWAYS, true); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to discard cropped clusters"); - return ret; + goto fail; } ret = qcow2_shrink_l1_table(bs, new_l1_size); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to reduce the number of L2 tables"); - return ret; + goto fail; } ret = qcow2_shrink_reftable(bs); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to discard unused refblocks"); - return ret; + goto fail; } old_file_size = bdrv_getlength(bs->file->bs); if (old_file_size < 0) { error_setg_errno(errp, -old_file_size, "Failed to inquire current file length"); - return old_file_size; + ret = old_file_size; + goto fail; } last_cluster = qcow2_get_last_cluster(bs, old_file_size); if (last_cluster < 0) { error_setg_errno(errp, -last_cluster, "Failed to find the last cluster"); - return last_cluster; + ret = last_cluster; + goto fail; } if ((last_cluster + 1) * s->cluster_size < old_file_size) { Error *local_err = NULL; - bdrv_truncate(bs->file, (last_cluster + 1) * s->cluster_size, - PREALLOC_MODE_OFF, &local_err); + bdrv_co_truncate(bs->file, (last_cluster + 1) * s->cluster_size, + PREALLOC_MODE_OFF, &local_err); if (local_err) { warn_reportf_err(local_err, "Failed to truncate the tail of the image: "); @@ -3531,7 +3538,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, ret = qcow2_grow_l1_table(bs, new_l1_size, true); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to grow the L1 table"); - return ret; + goto fail; } } @@ -3543,7 +3550,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, ret = preallocate(bs, old_length, offset); if (ret < 0) { error_setg_errno(errp, -ret, "Preallocation failed"); - return ret; + goto fail; } break; @@ -3559,7 +3566,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (old_file_size < 0) { error_setg_errno(errp, -old_file_size, "Failed to inquire current file length"); - return old_file_size; + ret = old_file_size; + goto fail; } old_file_size = ROUND_UP(old_file_size, s->cluster_size); @@ -3589,7 +3597,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (allocation_start < 0) { error_setg_errno(errp, -allocation_start, "Failed to resize refcount structures"); - return allocation_start; + ret = allocation_start; + goto fail; } clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start, @@ -3597,7 +3606,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (clusters_allocated < 0) { error_setg_errno(errp, -clusters_allocated, "Failed to allocate data clusters"); - return clusters_allocated; + ret = clusters_allocated; + goto fail; } assert(clusters_allocated == nb_new_data_clusters); @@ -3605,13 +3615,13 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, /* Allocate the data area */ new_file_size = allocation_start + nb_new_data_clusters * s->cluster_size; - ret = bdrv_truncate(bs->file, new_file_size, prealloc, errp); + ret = bdrv_co_truncate(bs->file, new_file_size, prealloc, errp); if (ret < 0) { error_prepend(errp, "Failed to resize underlying file: "); qcow2_free_clusters(bs, allocation_start, nb_new_data_clusters * s->cluster_size, QCOW2_DISCARD_OTHER); - return ret; + goto fail; } /* Create the necessary L2 entries */ @@ -3634,7 +3644,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, qcow2_free_clusters(bs, host_offset, nb_new_data_clusters * s->cluster_size, QCOW2_DISCARD_OTHER); - return ret; + goto fail; } guest_offset += nb_clusters * s->cluster_size; @@ -3650,11 +3660,11 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (prealloc != PREALLOC_MODE_OFF) { /* Flush metadata before actually changing the image size */ - ret = bdrv_flush(bs); + ret = qcow2_write_caches(bs); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to flush the preallocated area to disk"); - return ret; + goto fail; } } @@ -3664,11 +3674,14 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, &offset, sizeof(uint64_t)); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to update the image size"); - return ret; + goto fail; } s->l1_vm_state_index = new_l1_size; - return 0; + ret = 0; +fail: + qemu_co_mutex_unlock(&s->lock); + return ret; } /* XXX: put compressed sectors first, then all the cluster aligned @@ -3692,7 +3705,8 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, if (cluster_offset < 0) { return cluster_offset; } - return bdrv_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, NULL); + return bdrv_co_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, + NULL); } if (offset_into_cluster(s, offset)) { @@ -4696,7 +4710,7 @@ BlockDriver bdrv_qcow2 = { .bdrv_co_pdiscard = qcow2_co_pdiscard, .bdrv_co_copy_range_from = qcow2_co_copy_range_from, .bdrv_co_copy_range_to = qcow2_co_copy_range_to, - .bdrv_truncate = qcow2_truncate, + .bdrv_co_truncate = qcow2_co_truncate, .bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed, .bdrv_make_empty = qcow2_make_empty, diff --git a/block/qed.c b/block/qed.c index 2363814538..689ea9d4d5 100644 --- a/block/qed.c +++ b/block/qed.c @@ -1467,8 +1467,10 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, QED_AIOCB_WRITE | QED_AIOCB_ZERO); } -static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs, + int64_t offset, + PreallocMode prealloc, + Error **errp) { BDRVQEDState *s = bs->opaque; uint64_t old_image_size; @@ -1678,7 +1680,7 @@ static BlockDriver bdrv_qed = { .bdrv_co_readv = bdrv_qed_co_readv, .bdrv_co_writev = bdrv_qed_co_writev, .bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes, - .bdrv_truncate = bdrv_qed_truncate, + .bdrv_co_truncate = bdrv_qed_co_truncate, .bdrv_getlength = bdrv_qed_getlength, .bdrv_get_info = bdrv_qed_get_info, .bdrv_refresh_limits = bdrv_qed_refresh_limits, diff --git a/block/raw-format.c b/block/raw-format.c index f2e468df6f..b78da564d4 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -366,8 +366,8 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) } } -static int raw_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVRawState *s = bs->opaque; @@ -383,7 +383,7 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset, s->size = offset; offset += s->offset; - return bdrv_truncate(bs->file, offset, prealloc, errp); + return bdrv_co_truncate(bs->file, offset, prealloc, errp); } static void raw_eject(BlockDriverState *bs, bool eject_flag) @@ -545,7 +545,7 @@ BlockDriver bdrv_raw = { .bdrv_co_block_status = &raw_co_block_status, .bdrv_co_copy_range_from = &raw_co_copy_range_from, .bdrv_co_copy_range_to = &raw_co_copy_range_to, - .bdrv_truncate = &raw_truncate, + .bdrv_co_truncate = &raw_co_truncate, .bdrv_getlength = &raw_getlength, .has_variable_length = true, .bdrv_measure = &raw_measure, diff --git a/block/rbd.c b/block/rbd.c index f2c6965418..ca8e5bbace 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -990,8 +990,10 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs) return info.size; } -static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs, + int64_t offset, + PreallocMode prealloc, + Error **errp) { BDRVRBDState *s = bs->opaque; int r; @@ -1184,7 +1186,7 @@ static BlockDriver bdrv_rbd = { .bdrv_get_info = qemu_rbd_getinfo, .create_opts = &qemu_rbd_create_opts, .bdrv_getlength = qemu_rbd_getlength, - .bdrv_truncate = qemu_rbd_truncate, + .bdrv_co_truncate = qemu_rbd_co_truncate, .protocol_name = "rbd", .bdrv_aio_preadv = qemu_rbd_aio_preadv, diff --git a/block/sheepdog.c b/block/sheepdog.c index 665b1763eb..b229a664d9 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -2292,8 +2292,8 @@ static int64_t sd_getlength(BlockDriverState *bs) return s->inode.vdi_size; } -static int sd_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVSheepdogState *s = bs->opaque; int ret, fd; @@ -2609,7 +2609,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, assert(!flags); if (offset > s->inode.vdi_size) { - ret = sd_truncate(bs, offset, PREALLOC_MODE_OFF, NULL); + ret = sd_co_truncate(bs, offset, PREALLOC_MODE_OFF, NULL); if (ret < 0) { return ret; } @@ -3231,7 +3231,7 @@ static BlockDriver bdrv_sheepdog = { .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, .bdrv_get_allocated_file_size = sd_get_allocated_file_size, - .bdrv_truncate = sd_truncate, + .bdrv_co_truncate = sd_co_truncate, .bdrv_co_readv = sd_co_readv, .bdrv_co_writev = sd_co_writev, @@ -3268,7 +3268,7 @@ static BlockDriver bdrv_sheepdog_tcp = { .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, .bdrv_get_allocated_file_size = sd_get_allocated_file_size, - .bdrv_truncate = sd_truncate, + .bdrv_co_truncate = sd_co_truncate, .bdrv_co_readv = sd_co_readv, .bdrv_co_writev = sd_co_writev, @@ -3305,7 +3305,7 @@ static BlockDriver bdrv_sheepdog_unix = { .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, .bdrv_get_allocated_file_size = sd_get_allocated_file_size, - .bdrv_truncate = sd_truncate, + .bdrv_co_truncate = sd_co_truncate, .bdrv_co_readv = sd_co_readv, .bdrv_co_writev = sd_co_writev, diff --git a/block/ssh.c b/block/ssh.c index da7bbf73e2..7fbc27abdf 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -1243,8 +1243,8 @@ static int64_t ssh_getlength(BlockDriverState *bs) return length; } -static int ssh_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVSSHState *s = bs->opaque; @@ -1279,7 +1279,7 @@ static BlockDriver bdrv_ssh = { .bdrv_co_readv = ssh_co_readv, .bdrv_co_writev = ssh_co_writev, .bdrv_getlength = ssh_getlength, - .bdrv_truncate = ssh_truncate, + .bdrv_co_truncate = ssh_co_truncate, .bdrv_co_flush_to_disk = ssh_co_flush, .create_opts = &ssh_create_opts, }; diff --git a/include/block/block.h b/include/block/block.h index b1d6fdb97a..42e59ff585 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -300,8 +300,12 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, const char *backing_file); void bdrv_refresh_filename(BlockDriverState *bs); + +int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, + PreallocMode prealloc, Error **errp); int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, Error **errp); + int64_t bdrv_nb_sectors(BlockDriverState *bs); int64_t bdrv_getlength(BlockDriverState *bs); int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index 74646ed722..c653ee663a 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -289,8 +289,8 @@ struct BlockDriver { * bdrv_parse_filename. */ const char *protocol_name; - int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp); + int coroutine_fn (*bdrv_co_truncate)(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp); int64_t (*bdrv_getlength)(BlockDriverState *bs); bool has_variable_length; From 47e86b868d57ffe13162ca44e5f6a51c15c4a769 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 26 Jun 2018 15:52:13 +0200 Subject: [PATCH 1734/2380] qcow2: Remove coroutine trampoline for preallocate_co() All callers are coroutine_fns now, so we can just directly call preallocate_co(). Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/qcow2.c | 51 ++++++++------------------------------------------- 1 file changed, 8 insertions(+), 43 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 9e0ccbbfaf..4a0d92860d 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2521,15 +2521,6 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, return ret; } - -typedef struct PreallocCo { - BlockDriverState *bs; - uint64_t offset; - uint64_t new_length; - - int ret; -} PreallocCo; - /** * Preallocates metadata structures for data clusters between @offset (in the * guest disk) and @new_length (which is thus generally the new guest disk @@ -2537,12 +2528,9 @@ typedef struct PreallocCo { * * Returns: 0 on success, -errno on failure. */ -static void coroutine_fn preallocate_co(void *opaque) +static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, + uint64_t new_length) { - PreallocCo *params = opaque; - BlockDriverState *bs = params->bs; - uint64_t offset = params->offset; - uint64_t new_length = params->new_length; uint64_t bytes; uint64_t host_offset = 0; unsigned int cur_bytes; @@ -2557,7 +2545,7 @@ static void coroutine_fn preallocate_co(void *opaque) ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes, &host_offset, &meta); if (ret < 0) { - goto done; + return ret; } while (meta) { @@ -2567,7 +2555,7 @@ static void coroutine_fn preallocate_co(void *opaque) if (ret < 0) { qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters, QCOW2_DISCARD_NEVER); - goto done; + return ret; } /* There are no dependent requests, but we need to remove our @@ -2594,34 +2582,11 @@ static void coroutine_fn preallocate_co(void *opaque) ret = bdrv_pwrite(bs->file, (host_offset + cur_bytes) - 1, &data, 1); if (ret < 0) { - goto done; + return ret; } } - ret = 0; - -done: - params->ret = ret; -} - -static int preallocate(BlockDriverState *bs, - uint64_t offset, uint64_t new_length) -{ - PreallocCo params = { - .bs = bs, - .offset = offset, - .new_length = new_length, - .ret = -EINPROGRESS, - }; - - if (qemu_in_coroutine()) { - preallocate_co(¶ms); - } else { - Coroutine *co = qemu_coroutine_create(preallocate_co, ¶ms); - bdrv_coroutine_enter(bs, co); - BDRV_POLL_WHILE(bs, params.ret == -EINPROGRESS); - } - return params.ret; + return 0; } /* qcow2_refcount_metadata_size: @@ -3039,7 +3004,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) { BDRVQcow2State *s = blk_bs(blk)->opaque; qemu_co_mutex_lock(&s->lock); - ret = preallocate(blk_bs(blk), 0, qcow2_opts->size); + ret = preallocate_co(blk_bs(blk), 0, qcow2_opts->size); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { @@ -3547,7 +3512,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, break; case PREALLOC_MODE_METADATA: - ret = preallocate(bs, old_length, offset); + ret = preallocate_co(bs, old_length, offset); if (ret < 0) { error_setg_errno(errp, -ret, "Preallocation failed"); goto fail; From 3d9f2d2af63fda5f0404fb85ea80161837a4e4e3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 26 Jun 2018 13:55:20 +0200 Subject: [PATCH 1735/2380] block: Move bdrv_truncate() implementation to io.c This moves the bdrv_truncate() implementation from block.c to block/io.c so it can have access to the tracked requests infrastructure. This involves making refresh_total_sectors() public (in block_int.h). Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block.c | 111 +------------------------------------- block/io.c | 109 +++++++++++++++++++++++++++++++++++++ include/block/block_int.h | 2 + 3 files changed, 112 insertions(+), 110 deletions(-) diff --git a/block.c b/block.c index f761bc85d5..70a46fdd84 100644 --- a/block.c +++ b/block.c @@ -725,7 +725,7 @@ static int find_image_format(BlockBackend *file, const char *filename, * Set the current 'total_sectors' value * Return 0 on success, -errno on error. */ -static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) +int refresh_total_sectors(BlockDriverState *bs, int64_t hint) { BlockDriver *drv = bs->drv; @@ -2226,16 +2226,6 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) } } -static void bdrv_parent_cb_resize(BlockDriverState *bs) -{ - BdrvChild *c; - QLIST_FOREACH(c, &bs->parents, next_parent) { - if (c->role->resize) { - c->role->resize(c); - } - } -} - /* * Sets the backing file link of a BDS. A new reference is created; callers * which don't need their own reference any more must call bdrv_unref(). @@ -3785,105 +3775,6 @@ exit: return ret; } -/** - * Truncate file to 'offset' bytes (needed only for file protocols) - */ -int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, - PreallocMode prealloc, Error **errp) -{ - BlockDriverState *bs = child->bs; - BlockDriver *drv = bs->drv; - int ret; - - assert(child->perm & BLK_PERM_RESIZE); - - /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ - if (!drv) { - error_setg(errp, "No medium inserted"); - return -ENOMEDIUM; - } - if (offset < 0) { - error_setg(errp, "Image size cannot be negative"); - return -EINVAL; - } - - bdrv_inc_in_flight(bs); - - if (!drv->bdrv_co_truncate) { - if (bs->file && drv->is_filter) { - ret = bdrv_co_truncate(bs->file, offset, prealloc, errp); - goto out; - } - error_setg(errp, "Image format driver does not support resize"); - ret = -ENOTSUP; - goto out; - } - if (bs->read_only) { - error_setg(errp, "Image is read-only"); - ret = -EACCES; - goto out; - } - - assert(!(bs->open_flags & BDRV_O_INACTIVE)); - - ret = drv->bdrv_co_truncate(bs, offset, prealloc, errp); - if (ret < 0) { - goto out; - } - ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); - if (ret < 0) { - error_setg_errno(errp, -ret, "Could not refresh total sector count"); - } else { - offset = bs->total_sectors * BDRV_SECTOR_SIZE; - } - bdrv_dirty_bitmap_truncate(bs, offset); - bdrv_parent_cb_resize(bs); - atomic_inc(&bs->write_gen); - -out: - bdrv_dec_in_flight(bs); - return ret; -} - -typedef struct TruncateCo { - BdrvChild *child; - int64_t offset; - PreallocMode prealloc; - Error **errp; - int ret; -} TruncateCo; - -static void coroutine_fn bdrv_truncate_co_entry(void *opaque) -{ - TruncateCo *tco = opaque; - tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc, - tco->errp); -} - -int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, - Error **errp) -{ - Coroutine *co; - TruncateCo tco = { - .child = child, - .offset = offset, - .prealloc = prealloc, - .errp = errp, - .ret = NOT_DONE, - }; - - if (qemu_in_coroutine()) { - /* Fast-path if already in coroutine context */ - bdrv_truncate_co_entry(&tco); - } else { - co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco); - qemu_coroutine_enter(co); - BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE); - } - - return tco.ret; -} - /** * Length of a allocated file in bytes. Sparse files are counted by actual * allocated space. Return < 0 if error or unknown. diff --git a/block/io.c b/block/io.c index ef4fedd364..7e87a42b8e 100644 --- a/block/io.c +++ b/block/io.c @@ -3020,3 +3020,112 @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, bdrv_dec_in_flight(dst_bs); return ret; } + +static void bdrv_parent_cb_resize(BlockDriverState *bs) +{ + BdrvChild *c; + QLIST_FOREACH(c, &bs->parents, next_parent) { + if (c->role->resize) { + c->role->resize(c); + } + } +} + +/** + * Truncate file to 'offset' bytes (needed only for file protocols) + */ +int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, + PreallocMode prealloc, Error **errp) +{ + BlockDriverState *bs = child->bs; + BlockDriver *drv = bs->drv; + int ret; + + assert(child->perm & BLK_PERM_RESIZE); + + /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ + if (!drv) { + error_setg(errp, "No medium inserted"); + return -ENOMEDIUM; + } + if (offset < 0) { + error_setg(errp, "Image size cannot be negative"); + return -EINVAL; + } + + bdrv_inc_in_flight(bs); + + if (!drv->bdrv_co_truncate) { + if (bs->file && drv->is_filter) { + ret = bdrv_co_truncate(bs->file, offset, prealloc, errp); + goto out; + } + error_setg(errp, "Image format driver does not support resize"); + ret = -ENOTSUP; + goto out; + } + if (bs->read_only) { + error_setg(errp, "Image is read-only"); + ret = -EACCES; + goto out; + } + + assert(!(bs->open_flags & BDRV_O_INACTIVE)); + + ret = drv->bdrv_co_truncate(bs, offset, prealloc, errp); + if (ret < 0) { + goto out; + } + ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not refresh total sector count"); + } else { + offset = bs->total_sectors * BDRV_SECTOR_SIZE; + } + bdrv_dirty_bitmap_truncate(bs, offset); + bdrv_parent_cb_resize(bs); + atomic_inc(&bs->write_gen); + +out: + bdrv_dec_in_flight(bs); + return ret; +} + +typedef struct TruncateCo { + BdrvChild *child; + int64_t offset; + PreallocMode prealloc; + Error **errp; + int ret; +} TruncateCo; + +static void coroutine_fn bdrv_truncate_co_entry(void *opaque) +{ + TruncateCo *tco = opaque; + tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc, + tco->errp); +} + +int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, + Error **errp) +{ + Coroutine *co; + TruncateCo tco = { + .child = child, + .offset = offset, + .prealloc = prealloc, + .errp = errp, + .ret = NOT_DONE, + }; + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_truncate_co_entry(&tco); + } else { + co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco); + qemu_coroutine_enter(co); + BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE); + } + + return tco.ret; +} diff --git a/include/block/block_int.h b/include/block/block_int.h index c653ee663a..740166a996 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -1157,4 +1157,6 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, BdrvRequestFlags flags); +int refresh_total_sectors(BlockDriverState *bs, int64_t hint); + #endif /* BLOCK_INT_H */ From 1bc5f09f2e1b2be8f6f737b8d5352b438fc41492 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 26 Jun 2018 14:23:23 +0200 Subject: [PATCH 1736/2380] block: Use tracked request for truncate When growing an image, block drivers (especially protocol drivers) may initialise the newly added area. I/O requests to the same area need to wait for this initialisation to be completed so that data writes don't get overwritten and reads don't read uninitialised data. To avoid overhead in the fast I/O path by adding new locking in the protocol drivers and to restrict the impact to requests that actually touch the new area, reuse the existing tracked request infrastructure in block/io.c and mark all discard requests as serialising. With this change, it is safe for protocol drivers to make .bdrv_co_truncate actually asynchronous. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/io.c | 25 +++++++++++++++++++++++++ include/block/block_int.h | 1 + 2 files changed, 26 insertions(+) diff --git a/block/io.c b/block/io.c index 7e87a42b8e..01a3c4eac5 100644 --- a/block/io.c +++ b/block/io.c @@ -3039,6 +3039,8 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, { BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; + BdrvTrackedRequest req; + int64_t old_size, new_bytes; int ret; assert(child->perm & BLK_PERM_RESIZE); @@ -3053,7 +3055,28 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, return -EINVAL; } + old_size = bdrv_getlength(bs); + if (old_size < 0) { + error_setg_errno(errp, -old_size, "Failed to get old image size"); + return old_size; + } + + if (offset > old_size) { + new_bytes = offset - old_size; + } else { + new_bytes = 0; + } + bdrv_inc_in_flight(bs); + tracked_request_begin(&req, bs, offset, new_bytes, BDRV_TRACKED_TRUNCATE); + + /* If we are growing the image and potentially using preallocation for the + * new area, we need to make sure that no write requests are made to it + * concurrently or they might be overwritten by preallocation. */ + if (new_bytes) { + mark_request_serialising(&req, 1); + wait_serialising_requests(&req); + } if (!drv->bdrv_co_truncate) { if (bs->file && drv->is_filter) { @@ -3087,7 +3110,9 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, atomic_inc(&bs->write_gen); out: + tracked_request_end(&req); bdrv_dec_in_flight(bs); + return ret; } diff --git a/include/block/block_int.h b/include/block/block_int.h index 740166a996..af71b414be 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -63,6 +63,7 @@ enum BdrvTrackedRequestType { BDRV_TRACKED_READ, BDRV_TRACKED_WRITE, BDRV_TRACKED_DISCARD, + BDRV_TRACKED_TRUNCATE, }; typedef struct BdrvTrackedRequest { From 93f4e2ff4b31205d8bab0856631a52ed442b8b1c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 21 Jun 2018 18:23:16 +0200 Subject: [PATCH 1737/2380] file-posix: Make .bdrv_co_truncate asynchronous This moves the code to resize an image file to the thread pool to avoid blocking. Creating large images with preallocation with blockdev-create is now actually a background job instead of blocking the monitor (and most other things) until the preallocation has completed. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block/file-posix.c | 266 +++++++++++++++++++++++----------------- include/block/raw-aio.h | 4 +- 2 files changed, 154 insertions(+), 116 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 53c5833659..639265d53b 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -188,8 +188,16 @@ typedef struct RawPosixAIOData { #define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */ off_t aio_offset; int aio_type; - int aio_fd2; - off_t aio_offset2; + union { + struct { + int aio_fd2; + off_t aio_offset2; + }; + struct { + PreallocMode prealloc; + Error **errp; + }; + }; } RawPosixAIOData; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) @@ -1539,6 +1547,122 @@ static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) return ret; } +static int handle_aiocb_truncate(RawPosixAIOData *aiocb) +{ + int result = 0; + int64_t current_length = 0; + char *buf = NULL; + struct stat st; + int fd = aiocb->aio_fildes; + int64_t offset = aiocb->aio_offset; + Error **errp = aiocb->errp; + + if (fstat(fd, &st) < 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not stat file"); + return result; + } + + current_length = st.st_size; + if (current_length > offset && aiocb->prealloc != PREALLOC_MODE_OFF) { + error_setg(errp, "Cannot use preallocation for shrinking files"); + return -ENOTSUP; + } + + switch (aiocb->prealloc) { +#ifdef CONFIG_POSIX_FALLOCATE + case PREALLOC_MODE_FALLOC: + /* + * Truncating before posix_fallocate() makes it about twice slower on + * file systems that do not support fallocate(), trying to check if a + * block is allocated before allocating it, so don't do that here. + */ + if (offset != current_length) { + result = -posix_fallocate(fd, current_length, + offset - current_length); + if (result != 0) { + /* posix_fallocate() doesn't set errno. */ + error_setg_errno(errp, -result, + "Could not preallocate new data"); + } + } else { + result = 0; + } + goto out; +#endif + case PREALLOC_MODE_FULL: + { + int64_t num = 0, left = offset - current_length; + off_t seek_result; + + /* + * Knowing the final size from the beginning could allow the file + * system driver to do less allocations and possibly avoid + * fragmentation of the file. + */ + if (ftruncate(fd, offset) != 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not resize file"); + goto out; + } + + buf = g_malloc0(65536); + + seek_result = lseek(fd, current_length, SEEK_SET); + if (seek_result < 0) { + result = -errno; + error_setg_errno(errp, -result, + "Failed to seek to the old end of file"); + goto out; + } + + while (left > 0) { + num = MIN(left, 65536); + result = write(fd, buf, num); + if (result < 0) { + result = -errno; + error_setg_errno(errp, -result, + "Could not write zeros for preallocation"); + goto out; + } + left -= result; + } + if (result >= 0) { + result = fsync(fd); + if (result < 0) { + result = -errno; + error_setg_errno(errp, -result, + "Could not flush file to disk"); + goto out; + } + } + goto out; + } + case PREALLOC_MODE_OFF: + if (ftruncate(fd, offset) != 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not resize file"); + } + return result; + default: + result = -ENOTSUP; + error_setg(errp, "Unsupported preallocation mode: %s", + PreallocMode_str(aiocb->prealloc)); + return result; + } + +out: + if (result < 0) { + if (ftruncate(fd, current_length) < 0) { + error_report("Failed to restore old file length: %s", + strerror(errno)); + } + } + + g_free(buf); + return result; +} + static int aio_worker(void *arg) { RawPosixAIOData *aiocb = arg; @@ -1582,6 +1706,9 @@ static int aio_worker(void *arg) case QEMU_AIO_COPY_RANGE: ret = handle_aiocb_copy_range(aiocb); break; + case QEMU_AIO_TRUNCATE: + ret = handle_aiocb_truncate(aiocb); + break; default: fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); ret = -EINVAL; @@ -1765,117 +1892,25 @@ static void raw_close(BlockDriverState *bs) * * Returns: 0 on success, -errno on failure. */ -static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc, - Error **errp) +static int coroutine_fn +raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, + PreallocMode prealloc, Error **errp) { - int result = 0; - int64_t current_length = 0; - char *buf = NULL; - struct stat st; + RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); + ThreadPool *pool; - if (fstat(fd, &st) < 0) { - result = -errno; - error_setg_errno(errp, -result, "Could not stat file"); - return result; - } + *acb = (RawPosixAIOData) { + .bs = bs, + .aio_fildes = fd, + .aio_type = QEMU_AIO_TRUNCATE, + .aio_offset = offset, + .prealloc = prealloc, + .errp = errp, + }; - current_length = st.st_size; - if (current_length > offset && prealloc != PREALLOC_MODE_OFF) { - error_setg(errp, "Cannot use preallocation for shrinking files"); - return -ENOTSUP; - } - - switch (prealloc) { -#ifdef CONFIG_POSIX_FALLOCATE - case PREALLOC_MODE_FALLOC: - /* - * Truncating before posix_fallocate() makes it about twice slower on - * file systems that do not support fallocate(), trying to check if a - * block is allocated before allocating it, so don't do that here. - */ - if (offset != current_length) { - result = -posix_fallocate(fd, current_length, offset - current_length); - if (result != 0) { - /* posix_fallocate() doesn't set errno. */ - error_setg_errno(errp, -result, - "Could not preallocate new data"); - } - } else { - result = 0; - } - goto out; -#endif - case PREALLOC_MODE_FULL: - { - int64_t num = 0, left = offset - current_length; - off_t seek_result; - - /* - * Knowing the final size from the beginning could allow the file - * system driver to do less allocations and possibly avoid - * fragmentation of the file. - */ - if (ftruncate(fd, offset) != 0) { - result = -errno; - error_setg_errno(errp, -result, "Could not resize file"); - goto out; - } - - buf = g_malloc0(65536); - - seek_result = lseek(fd, current_length, SEEK_SET); - if (seek_result < 0) { - result = -errno; - error_setg_errno(errp, -result, - "Failed to seek to the old end of file"); - goto out; - } - - while (left > 0) { - num = MIN(left, 65536); - result = write(fd, buf, num); - if (result < 0) { - result = -errno; - error_setg_errno(errp, -result, - "Could not write zeros for preallocation"); - goto out; - } - left -= result; - } - if (result >= 0) { - result = fsync(fd); - if (result < 0) { - result = -errno; - error_setg_errno(errp, -result, - "Could not flush file to disk"); - goto out; - } - } - goto out; - } - case PREALLOC_MODE_OFF: - if (ftruncate(fd, offset) != 0) { - result = -errno; - error_setg_errno(errp, -result, "Could not resize file"); - } - return result; - default: - result = -ENOTSUP; - error_setg(errp, "Unsupported preallocation mode: %s", - PreallocMode_str(prealloc)); - return result; - } - -out: - if (result < 0) { - if (ftruncate(fd, current_length) < 0) { - error_report("Failed to restore old file length: %s", - strerror(errno)); - } - } - - g_free(buf); - return result; + /* @bs can be NULL, bdrv_get_aio_context() returns the main context then */ + pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + return thread_pool_submit_co(pool, aio_worker, acb); } static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, @@ -1892,7 +1927,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, } if (S_ISREG(st.st_mode)) { - return raw_regular_truncate(s->fd, offset, prealloc, errp); + return raw_regular_truncate(bs, s->fd, offset, prealloc, errp); } if (prealloc != PREALLOC_MODE_OFF) { @@ -2094,7 +2129,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) return (int64_t)st.st_blocks * 512; } -static int raw_co_create(BlockdevCreateOptions *options, Error **errp) +static int coroutine_fn +raw_co_create(BlockdevCreateOptions *options, Error **errp) { BlockdevCreateOptionsFile *file_opts; int fd; @@ -2146,7 +2182,7 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) } /* Clear the file by truncating it to 0 */ - result = raw_regular_truncate(fd, 0, PREALLOC_MODE_OFF, errp); + result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, errp); if (result < 0) { goto out_close; } @@ -2168,8 +2204,8 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) /* Resize and potentially preallocate the file to the desired * final size */ - result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation, - errp); + result = raw_regular_truncate(NULL, fd, file_opts->size, + file_opts->preallocation, errp); if (result < 0) { goto out_close; } diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index 8d698ccd31..6799614e56 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -26,6 +26,7 @@ #define QEMU_AIO_DISCARD 0x0010 #define QEMU_AIO_WRITE_ZEROES 0x0020 #define QEMU_AIO_COPY_RANGE 0x0040 +#define QEMU_AIO_TRUNCATE 0x0080 #define QEMU_AIO_TYPE_MASK \ (QEMU_AIO_READ | \ QEMU_AIO_WRITE | \ @@ -33,7 +34,8 @@ QEMU_AIO_FLUSH | \ QEMU_AIO_DISCARD | \ QEMU_AIO_WRITE_ZEROES | \ - QEMU_AIO_COPY_RANGE) + QEMU_AIO_COPY_RANGE | \ + QEMU_AIO_TRUNCATE) /* AIO flags */ #define QEMU_AIO_MISALIGNED 0x1000 From 354d930dc6e90e97599459b79c071ff1b93e433b Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Wed, 27 Jun 2018 11:57:51 +0800 Subject: [PATCH 1738/2380] qcow2: Remove dead check on !ret MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the beginning of the function, we initialize the local variable to 0, and in the body of the function, we check the assigned values and exit the loop immediately. So here it can never be non-zero. Reported-by: Kevin Wolf Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Kevin Wolf --- block/qcow2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/qcow2.c b/block/qcow2.c index 4a0d92860d..2d190aa00b 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1772,7 +1772,7 @@ static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, while (l2meta != NULL) { QCowL2Meta *next; - if (!ret && link_l2) { + if (link_l2) { ret = qcow2_alloc_cluster_link_l2(bs, l2meta); if (ret) { goto out; From 37aec7d75eb0d035a0db4f2cf9ad8b1b0c10f91b Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Wed, 27 Jun 2018 11:57:52 +0800 Subject: [PATCH 1739/2380] block: Move request tracking to children in copy offloading in_flight and tracked requests need to be tracked in every layer during recursion. For now the only user is qemu-img convert where overlapping requests and IOThreads don't exist, therefore this change doesn't make much difference form user point of view, but it is incorrect as part of the API. Fix it. Reported-by: Kevin Wolf Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/io.c | 59 ++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/block/io.c b/block/io.c index 01a3c4eac5..b63822280a 100644 --- a/block/io.c +++ b/block/io.c @@ -2932,6 +2932,9 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, BdrvRequestFlags flags, bool recurse_src) { + BdrvTrackedRequest src_req, dst_req; + BlockDriverState *src_bs = src->bs; + BlockDriverState *dst_bs = dst->bs; int ret; if (!src || !dst || !src->bs || !dst->bs) { @@ -2955,17 +2958,31 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, || src->bs->encrypted || dst->bs->encrypted) { return -ENOTSUP; } + bdrv_inc_in_flight(src_bs); + bdrv_inc_in_flight(dst_bs); + tracked_request_begin(&src_req, src_bs, src_offset, + bytes, BDRV_TRACKED_READ); + tracked_request_begin(&dst_req, dst_bs, dst_offset, + bytes, BDRV_TRACKED_WRITE); + + wait_serialising_requests(&src_req); + wait_serialising_requests(&dst_req); if (recurse_src) { - return src->bs->drv->bdrv_co_copy_range_from(src->bs, - src, src_offset, - dst, dst_offset, - bytes, flags); + ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, + src, src_offset, + dst, dst_offset, + bytes, flags); } else { - return dst->bs->drv->bdrv_co_copy_range_to(dst->bs, - src, src_offset, - dst, dst_offset, - bytes, flags); + ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, + src, src_offset, + dst, dst_offset, + bytes, flags); } + tracked_request_end(&src_req); + tracked_request_end(&dst_req); + bdrv_dec_in_flight(src_bs); + bdrv_dec_in_flight(dst_bs); + return ret; } /* Copy range from @src to @dst. @@ -2996,29 +3013,9 @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, BdrvRequestFlags flags) { - BdrvTrackedRequest src_req, dst_req; - BlockDriverState *src_bs = src->bs; - BlockDriverState *dst_bs = dst->bs; - int ret; - - bdrv_inc_in_flight(src_bs); - bdrv_inc_in_flight(dst_bs); - tracked_request_begin(&src_req, src_bs, src_offset, - bytes, BDRV_TRACKED_READ); - tracked_request_begin(&dst_req, dst_bs, dst_offset, - bytes, BDRV_TRACKED_WRITE); - - wait_serialising_requests(&src_req); - wait_serialising_requests(&dst_req); - ret = bdrv_co_copy_range_from(src, src_offset, - dst, dst_offset, - bytes, flags); - - tracked_request_end(&src_req); - tracked_request_end(&dst_req); - bdrv_dec_in_flight(src_bs); - bdrv_dec_in_flight(dst_bs); - return ret; + return bdrv_co_copy_range_from(src, src_offset, + dst, dst_offset, + bytes, flags); } static void bdrv_parent_cb_resize(BlockDriverState *bs) From 796d32394516d624986e9275f56bfcbffe945491 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 26 Jun 2018 19:41:19 +0200 Subject: [PATCH 1740/2380] block/crypto: Simplify block_crypto_{open,create}_opts_init() block_crypto_open_opts_init() and block_crypto_create_opts_init() contain a virtual visit of QCryptoBlockOptions and QCryptoBlockCreateOptions less member "format", respectively. Change their callers to put member "format" in the QDict, so they can use the generated visitors for these types instead. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/crypto.c | 99 +++++++------------------------------------------- block/crypto.h | 8 +--- block/qcow.c | 5 +-- block/qcow2.c | 10 ++--- 4 files changed, 22 insertions(+), 100 deletions(-) diff --git a/block/crypto.c b/block/crypto.c index 210c80d5c7..994172a3de 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -148,108 +148,36 @@ static QemuOptsList block_crypto_create_opts_luks = { QCryptoBlockOpenOptions * -block_crypto_open_opts_init(QCryptoBlockFormat format, - QDict *opts, - Error **errp) +block_crypto_open_opts_init(QDict *opts, Error **errp) { Visitor *v; - QCryptoBlockOpenOptions *ret = NULL; - Error *local_err = NULL; + QCryptoBlockOpenOptions *ret; - ret = g_new0(QCryptoBlockOpenOptions, 1); - ret->format = format; - - v = qobject_input_visitor_new_flat_confused(opts, &local_err); + v = qobject_input_visitor_new_flat_confused(opts, errp); if (!v) { - goto out; + return NULL; } - visit_start_struct(v, NULL, NULL, 0, &local_err); - if (local_err) { - goto out; - } + visit_type_QCryptoBlockOpenOptions(v, NULL, &ret, errp); - switch (format) { - case Q_CRYPTO_BLOCK_FORMAT_LUKS: - visit_type_QCryptoBlockOptionsLUKS_members( - v, &ret->u.luks, &local_err); - break; - - case Q_CRYPTO_BLOCK_FORMAT_QCOW: - visit_type_QCryptoBlockOptionsQCow_members( - v, &ret->u.qcow, &local_err); - break; - - default: - error_setg(&local_err, "Unsupported block format %d", format); - break; - } - if (!local_err) { - visit_check_struct(v, &local_err); - } - - visit_end_struct(v, NULL); - - out: - if (local_err) { - error_propagate(errp, local_err); - qapi_free_QCryptoBlockOpenOptions(ret); - ret = NULL; - } visit_free(v); return ret; } QCryptoBlockCreateOptions * -block_crypto_create_opts_init(QCryptoBlockFormat format, - QDict *opts, - Error **errp) +block_crypto_create_opts_init(QDict *opts, Error **errp) { Visitor *v; - QCryptoBlockCreateOptions *ret = NULL; - Error *local_err = NULL; + QCryptoBlockCreateOptions *ret; - ret = g_new0(QCryptoBlockCreateOptions, 1); - ret->format = format; - - v = qobject_input_visitor_new_flat_confused(opts, &local_err); + v = qobject_input_visitor_new_flat_confused(opts, errp); if (!v) { - goto out; + return NULL; } - visit_start_struct(v, NULL, NULL, 0, &local_err); - if (local_err) { - goto out; - } + visit_type_QCryptoBlockCreateOptions(v, NULL, &ret, errp); - switch (format) { - case Q_CRYPTO_BLOCK_FORMAT_LUKS: - visit_type_QCryptoBlockCreateOptionsLUKS_members( - v, &ret->u.luks, &local_err); - break; - - case Q_CRYPTO_BLOCK_FORMAT_QCOW: - visit_type_QCryptoBlockOptionsQCow_members( - v, &ret->u.qcow, &local_err); - break; - - default: - error_setg(&local_err, "Unsupported block format %d", format); - break; - } - if (!local_err) { - visit_check_struct(v, &local_err); - } - - visit_end_struct(v, NULL); - - out: - if (local_err) { - error_propagate(errp, local_err); - qapi_free_QCryptoBlockCreateOptions(ret); - ret = NULL; - } visit_free(v); return ret; } @@ -287,8 +215,9 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, } cryptoopts = qemu_opts_to_qdict(opts, NULL); + qdict_put_str(cryptoopts, "format", QCryptoBlockFormat_str(format)); - open_opts = block_crypto_open_opts_init(format, cryptoopts, errp); + open_opts = block_crypto_open_opts_init(cryptoopts, errp); if (!open_opts) { goto cleanup; } @@ -612,8 +541,8 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, &block_crypto_create_opts_luks, true); - create_opts = block_crypto_create_opts_init(Q_CRYPTO_BLOCK_FORMAT_LUKS, - cryptoopts, errp); + qdict_put_str(cryptoopts, "format", "luks"); + create_opts = block_crypto_create_opts_init(cryptoopts, errp); if (!create_opts) { ret = -EINVAL; goto fail; diff --git a/block/crypto.h b/block/crypto.h index 0f985ea4e2..dd7d47903c 100644 --- a/block/crypto.h +++ b/block/crypto.h @@ -89,13 +89,9 @@ } QCryptoBlockCreateOptions * -block_crypto_create_opts_init(QCryptoBlockFormat format, - QDict *opts, - Error **errp); +block_crypto_create_opts_init(QDict *opts, Error **errp); QCryptoBlockOpenOptions * -block_crypto_open_opts_init(QCryptoBlockFormat format, - QDict *opts, - Error **errp); +block_crypto_open_opts_init(QDict *opts, Error **errp); #endif /* BLOCK_CRYPTO_H__ */ diff --git a/block/qcow.c b/block/qcow.c index 5532731b9f..8546fe5bb7 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -203,9 +203,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } - qdict_del(encryptopts, "format"); - crypto_opts = block_crypto_open_opts_init( - Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); + qdict_put_str(encryptopts, "format", "qcow"); + crypto_opts = block_crypto_open_opts_init(encryptopts, errp); if (!crypto_opts) { ret = -EINVAL; goto fail; diff --git a/block/qcow2.c b/block/qcow2.c index 2d190aa00b..78aeb1f9ea 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1040,9 +1040,8 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, ret = -EINVAL; goto fail; } - qdict_del(encryptopts, "format"); - r->crypto_opts = block_crypto_open_opts_init( - Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); + qdict_put_str(encryptopts, "format", "qcow"); + r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); break; case QCOW_CRYPT_LUKS: @@ -1053,9 +1052,8 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, ret = -EINVAL; goto fail; } - qdict_del(encryptopts, "format"); - r->crypto_opts = block_crypto_open_opts_init( - Q_CRYPTO_BLOCK_FORMAT_LUKS, encryptopts, errp); + qdict_put_str(encryptopts, "format", "luks"); + r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); break; default: From 93a3642efcadf4ad6045ccea38a05ff5297dfe26 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 28 Jun 2018 17:36:14 +0200 Subject: [PATCH 1741/2380] qemu-iotests: Update 026.out.nocache reference output Commit abf754fe406 updated 026.out, but forgot to also update 026.out.nocache. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- tests/qemu-iotests/026.out.nocache | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/026.out.nocache b/tests/qemu-iotests/026.out.nocache index ea2e166a4e..650ccd8b01 100644 --- a/tests/qemu-iotests/026.out.nocache +++ b/tests/qemu-iotests/026.out.nocache @@ -541,7 +541,7 @@ Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device -11 leaked clusters were found on the image. +10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -569,7 +569,7 @@ Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device -11 leaked clusters were found on the image. +10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -597,7 +597,7 @@ Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device -11 leaked clusters were found on the image. +10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 From 8b24cd141549b5b264baeddd4e72902cfb5de23b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 28 Jun 2018 17:05:45 +0200 Subject: [PATCH 1742/2380] qcow2: Free allocated clusters on write error If we managed to allocate the clusters, but then failed to write the data, there's a good chance that we'll still be able to free the clusters again in order to avoid cluster leaks (the refcounts are cached, so even if we can't write them out right now, we may be able to do so when the VM is resumed after a werror=stop/enospc pause). Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: Eric Blake Tested-by: Eric Blake --- block/qcow2-cluster.c | 11 +++++++++++ block/qcow2.c | 2 ++ block/qcow2.h | 1 + 3 files changed, 14 insertions(+) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 0d74584c9b..d37fe08b3d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -994,6 +994,17 @@ err: return ret; } +/** + * Frees the allocated clusters because the request failed and they won't + * actually be linked. + */ +void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) +{ + BDRVQcow2State *s = bs->opaque; + qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits, + QCOW2_DISCARD_NEVER); +} + /* * Returns the number of contiguous clusters that can be used for an allocating * write, but require COW to be performed (this includes yet unallocated space, diff --git a/block/qcow2.c b/block/qcow2.c index 78aeb1f9ea..286640c0e3 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1775,6 +1775,8 @@ static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, if (ret) { goto out; } + } else { + qcow2_alloc_cluster_abort(bs, l2meta); } /* Take the request off the list of running requests */ diff --git a/block/qcow2.h b/block/qcow2.h index 01b5250415..1c9c0d3631 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -614,6 +614,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int compressed_size); int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); +void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, uint64_t bytes, enum qcow2_discard_type type, bool full_discard); From ae376c6255d0eee4b3c4d60acc4679aa99c0d2c8 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 28 Jun 2018 17:18:51 +0200 Subject: [PATCH 1743/2380] qemu-iotests: Test qcow2 not leaking clusters on write error This adds a test for a temporary write failure, which simulates the situation after werror=stop/enospc has stopped the VM. We shouldn't leave leaked clusters behind in such cases. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- tests/qemu-iotests/026 | 17 +++++++++++++++++ tests/qemu-iotests/026.out | 8 ++++++++ tests/qemu-iotests/026.out.nocache | 8 ++++++++ 3 files changed, 33 insertions(+) diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index 7fadfbace5..582d254195 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -200,6 +200,23 @@ done done done +echo +echo === Avoid cluster leaks after temporary failure === +echo + +cat > "$TEST_DIR/blkdebug.conf" < Date: Thu, 21 Jun 2018 19:07:32 +0200 Subject: [PATCH 1744/2380] file-posix: Implement co versions of discard/flush This simplifies file-posix by implementing the coroutine variants of the discard and flush BlockDriver callbacks. These were the last remaining users of paio_submit(), which can be removed now. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake --- block/file-posix.c | 72 ++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 48 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 639265d53b..e3f1b045d1 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1754,31 +1754,6 @@ static inline int paio_submit_co(BlockDriverState *bs, int fd, return paio_submit_co_full(bs, fd, offset, -1, 0, qiov, bytes, type); } -static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd, - int64_t offset, QEMUIOVector *qiov, int bytes, - BlockCompletionFunc *cb, void *opaque, int type) -{ - RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); - ThreadPool *pool; - - acb->bs = bs; - acb->aio_type = type; - acb->aio_fildes = fd; - - acb->aio_nbytes = bytes; - acb->aio_offset = offset; - - if (qiov) { - acb->aio_iov = qiov->iov; - acb->aio_niov = qiov->niov; - assert(qiov->size == acb->aio_nbytes); - } - - trace_paio_submit(acb, opaque, offset, bytes, type); - pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); - return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); -} - static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int type) { @@ -1845,15 +1820,17 @@ static void raw_aio_unplug(BlockDriverState *bs) #endif } -static BlockAIOCB *raw_aio_flush(BlockDriverState *bs, - BlockCompletionFunc *cb, void *opaque) +static int raw_co_flush_to_disk(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; + int ret; - if (fd_open(bs) < 0) - return NULL; + ret = fd_open(bs); + if (ret < 0) { + return ret; + } - return paio_submit(bs, s->fd, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH); + return paio_submit_co(bs, s->fd, 0, NULL, 0, QEMU_AIO_FLUSH); } static void raw_aio_attach_aio_context(BlockDriverState *bs, @@ -2526,14 +2503,12 @@ static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs, #endif /* !__linux__ */ } -static coroutine_fn BlockAIOCB *raw_aio_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes, - BlockCompletionFunc *cb, void *opaque) +static coroutine_fn int +raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { BDRVRawState *s = bs->opaque; - return paio_submit(bs, s->fd, offset, NULL, bytes, - cb, opaque, QEMU_AIO_DISCARD); + return paio_submit_co(bs, s->fd, offset, NULL, bytes, QEMU_AIO_DISCARD); } static int coroutine_fn raw_co_pwrite_zeroes( @@ -2652,8 +2627,8 @@ BlockDriver bdrv_file = { .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, - .bdrv_aio_flush = raw_aio_flush, - .bdrv_aio_pdiscard = raw_aio_pdiscard, + .bdrv_co_flush_to_disk = raw_co_flush_to_disk, + .bdrv_co_pdiscard = raw_co_pdiscard, .bdrv_co_copy_range_from = raw_co_copy_range_from, .bdrv_co_copy_range_to = raw_co_copy_range_to, .bdrv_refresh_limits = raw_refresh_limits, @@ -3019,17 +2994,18 @@ static int fd_open(BlockDriverState *bs) return -EIO; } -static coroutine_fn BlockAIOCB *hdev_aio_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes, - BlockCompletionFunc *cb, void *opaque) +static coroutine_fn int +hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { BDRVRawState *s = bs->opaque; + int ret; - if (fd_open(bs) < 0) { - return NULL; + ret = fd_open(bs); + if (ret < 0) { + return ret; } - return paio_submit(bs, s->fd, offset, NULL, bytes, - cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); + return paio_submit_co(bs, s->fd, offset, NULL, bytes, + QEMU_AIO_DISCARD | QEMU_AIO_BLKDEV); } static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, @@ -3133,8 +3109,8 @@ static BlockDriver bdrv_host_device = { .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, - .bdrv_aio_flush = raw_aio_flush, - .bdrv_aio_pdiscard = hdev_aio_pdiscard, + .bdrv_co_flush_to_disk = raw_co_flush_to_disk, + .bdrv_co_pdiscard = hdev_co_pdiscard, .bdrv_co_copy_range_from = raw_co_copy_range_from, .bdrv_co_copy_range_to = raw_co_copy_range_to, .bdrv_refresh_limits = raw_refresh_limits, @@ -3258,7 +3234,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, - .bdrv_aio_flush = raw_aio_flush, + .bdrv_co_flush_to_disk = raw_co_flush_to_disk, .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, @@ -3388,7 +3364,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, - .bdrv_aio_flush = raw_aio_flush, + .bdrv_co_flush_to_disk = raw_co_flush_to_disk, .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, From e06f4639d8a93703eecc3aad06c8a3e9b2ef4968 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 29 Jun 2018 14:03:26 +0800 Subject: [PATCH 1745/2380] qcow2: Fix src_offset in copy offloading Not updating src_offset will result in wrong data being written to dst image. Reported-by: Max Reitz Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/qcow2.c | 1 + tests/qemu-iotests/063 | 9 +++++++++ tests/qemu-iotests/063.out | 12 ++++++++++++ 3 files changed, 22 insertions(+) diff --git a/block/qcow2.c b/block/qcow2.c index 286640c0e3..2f9e58e0c4 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3387,6 +3387,7 @@ qcow2_co_copy_range_to(BlockDriverState *bs, } bytes -= cur_bytes; + src_offset += cur_bytes; dst_offset += cur_bytes; } ret = 0; diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063 index e4f6ea9385..adc037c1f5 100755 --- a/tests/qemu-iotests/063 +++ b/tests/qemu-iotests/063 @@ -91,6 +91,15 @@ if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG.orig" "$TEST_IMG" >/dev exit 1 fi +echo "== Regression testing for copy offloading bug ==" + +_make_test_img 1M +TEST_IMG="$TEST_IMG.target" _make_test_img 1M +$QEMU_IO -c 'write -P 1 0 512k' -c 'write -P 2 512k 512k' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'write -P 4 512k 512k' -c 'write -P 3 0 512k' "$TEST_IMG.target" | _filter_qemu_io +$QEMU_IMG convert -n -O $IMGFMT "$TEST_IMG" "$TEST_IMG.target" +$QEMU_IMG compare "$TEST_IMG" "$TEST_IMG.target" + echo "*** done" rm -f $seq.full status=0 diff --git a/tests/qemu-iotests/063.out b/tests/qemu-iotests/063.out index de1c99afd8..7b691b2c9e 100644 --- a/tests/qemu-iotests/063.out +++ b/tests/qemu-iotests/063.out @@ -7,4 +7,16 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 No errors were found on the image. == Testing conversion to a smaller file fails == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2097152 +== Regression testing for copy offloading bug == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT.target', fmt=IMGFMT size=1048576 +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Images are identical. *** done From 1439b9c11002348eb80fcd3c90f07bf0f4f088dc Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 29 Jun 2018 14:03:27 +0800 Subject: [PATCH 1746/2380] iscsi: Don't blindly use designator length in response for memcpy Per SCSI definition the designator_length we receive from INQUIRY is 8, 12 or at most 16, but we should be careful because the remote iscsi target may misbehave, otherwise we could have a buffer overflow. Reported-by: Max Reitz Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/iscsi.c b/block/iscsi.c index bc84b14e20..9beb06d498 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2226,7 +2226,7 @@ static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun) desc[5] = (dd->designator_type & 0xF) | ((dd->association & 3) << 4); desc[7] = dd->designator_length; - memcpy(desc + 8, dd->designator, dd->designator_length); + memcpy(desc + 8, dd->designator, MIN(dd->designator_length, 20)); desc[28] = 0; desc[29] = (lun->block_size >> 16) & 0xFF; From c436e3d0145a3952aacf1a4014434b82d7157633 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 29 Jun 2018 14:03:28 +0800 Subject: [PATCH 1747/2380] file-posix: Fix EINTR handling EINTR should be checked against errno, not ret. While fixing the bug, collect the branches with a switch block. Also, change the return value from -ENOSTUP to -ENOSPC when the actual issue is request range passes EOF, which should be distinguishable from the case of error == ENOSYS by the caller, so that it could still retry with other byte ranges, whereas it shouldn't retry anymore upon ENOSYS. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/file-posix.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index e3f1b045d1..829ee538d8 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1488,20 +1488,21 @@ static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb) ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, aiocb->aio_fd2, &out_off, bytes, 0); - if (ret == -EINTR) { - continue; + if (ret == 0) { + /* No progress (e.g. when beyond EOF), let the caller fall back to + * buffer I/O. */ + return -ENOSPC; } if (ret < 0) { - if (errno == ENOSYS) { + switch (errno) { + case ENOSYS: return -ENOTSUP; - } else { + case EINTR: + continue; + default: return -errno; } } - if (!ret) { - /* No progress (e.g. when beyond EOF), fall back to buffer I/O. */ - return -ENOTSUP; - } bytes -= ret; } return 0; From d08c2a245feb6ab82b5a044f72c75964eedeaef5 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 28 Jun 2018 15:15:18 -0500 Subject: [PATCH 1748/2380] parallels: Switch to byte-based calls We are gradually moving away from sector-based interfaces, towards byte-based. Make the change for the last few sector-based calls into the block layer from the parallels driver. Ideally, the parallels driver should switch to doing everything byte-based, but that's a more invasive change that requires a bit more auditing. Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Reviewed-by: Denis V. Lunev Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- block/parallels.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index fd215e202a..cc9445879d 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -227,14 +227,15 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, }; qemu_iovec_init_external(&qiov, &iov, 1); - ret = bdrv_co_readv(bs->backing, idx * s->tracks, nb_cow_sectors, - &qiov); + ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE, + nb_cow_bytes, &qiov, 0); if (ret < 0) { qemu_vfree(iov.iov_base); return ret; } - ret = bdrv_co_writev(bs->file, s->data_end, nb_cow_sectors, &qiov); + ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE, + nb_cow_bytes, &qiov, 0); qemu_vfree(iov.iov_base); if (ret < 0) { return ret; @@ -340,7 +341,8 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs, qemu_iovec_reset(&hd_qiov); qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); - ret = bdrv_co_writev(bs->file, position, n, &hd_qiov); + ret = bdrv_co_pwritev(bs->file, position * BDRV_SECTOR_SIZE, nbytes, + &hd_qiov, 0); if (ret < 0) { break; } @@ -379,7 +381,8 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, if (position < 0) { if (bs->backing) { - ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); + ret = bdrv_co_preadv(bs->backing, sector_num * BDRV_SECTOR_SIZE, + nbytes, &hd_qiov, 0); if (ret < 0) { break; } @@ -387,7 +390,8 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, qemu_iovec_memset(&hd_qiov, 0, 0, nbytes); } } else { - ret = bdrv_co_readv(bs->file, position, n, &hd_qiov); + ret = bdrv_co_preadv(bs->file, position * BDRV_SECTOR_SIZE, nbytes, + &hd_qiov, 0); if (ret < 0) { break; } From 787993a5435289e90479f80f81681c804a9d22ce Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 28 Jun 2018 15:15:19 -0500 Subject: [PATCH 1749/2380] qcow: Switch get_cluster_offset to be byte-based We are gradually moving away from sector-based interfaces, towards byte-based. Make the change for the internal helper function get_cluster_offset(), by changing n_start and n_end to be byte offsets rather than sector indices within the cluster being allocated. However, assert that these values are still sector-aligned (at least qcrypto_block_encrypt() still wants that). For now we get that alignment for free because we still use sector-based driver callbacks. A later patch will then switch the qcow driver as a whole over to byte-based operation; but will still leave things at sector alignments as it is not worth auditing the qcow image format to worry about sub-sector requests. Signed-off-by: Eric Blake Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- block/qcow.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/block/qcow.c b/block/qcow.c index 8546fe5bb7..d7c385a91d 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -345,8 +345,8 @@ static int qcow_reopen_prepare(BDRVReopenState *state, * * 0 to not allocate. * - * 1 to allocate a normal cluster (for sector indexes 'n_start' to - * 'n_end') + * 1 to allocate a normal cluster (for sector-aligned byte offsets 'n_start' + * to 'n_end' within the cluster) * * 2 to allocate a compressed cluster of size * 'compressed_size'. 'compressed_size' must be > 0 and < @@ -440,9 +440,10 @@ static int get_cluster_offset(BlockDriverState *bs, if (!allocate) return 0; BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); + assert(QEMU_IS_ALIGNED(n_start | n_end, BDRV_SECTOR_SIZE)); /* allocate a new cluster */ if ((cluster_offset & QCOW_OFLAG_COMPRESSED) && - (n_end - n_start) < s->cluster_sectors) { + (n_end - n_start) < s->cluster_size) { /* if the cluster is already compressed, we must decompress it in the case it is not completely overwritten */ @@ -480,16 +481,15 @@ static int get_cluster_offset(BlockDriverState *bs, /* if encrypted, we must initialize the cluster content which won't be written */ if (bs->encrypted && - (n_end - n_start) < s->cluster_sectors) { - uint64_t start_sect; + (n_end - n_start) < s->cluster_size) { + uint64_t start_offset; assert(s->crypto); - start_sect = (offset & ~(s->cluster_size - 1)) >> 9; - for(i = 0; i < s->cluster_sectors; i++) { + start_offset = offset & ~(s->cluster_size - 1); + for (i = 0; i < s->cluster_size; i += BDRV_SECTOR_SIZE) { if (i < n_start || i >= n_end) { - memset(s->cluster_data, 0x00, 512); + memset(s->cluster_data, 0x00, BDRV_SECTOR_SIZE); if (qcrypto_block_encrypt(s->crypto, - (start_sect + i) * - BDRV_SECTOR_SIZE, + start_offset + i, s->cluster_data, BDRV_SECTOR_SIZE, NULL) < 0) { @@ -497,8 +497,9 @@ static int get_cluster_offset(BlockDriverState *bs, } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = bdrv_pwrite(bs->file, - cluster_offset + i * 512, - s->cluster_data, 512); + cluster_offset + i, + s->cluster_data, + BDRV_SECTOR_SIZE); if (ret < 0) { return ret; } @@ -758,8 +759,8 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, n = nb_sectors; } ret = get_cluster_offset(bs, sector_num << 9, 1, 0, - index_in_cluster, - index_in_cluster + n, &cluster_offset); + index_in_cluster << 9, + (index_in_cluster + n) << 9, &cluster_offset); if (ret < 0) { break; } From a15312b0172292273303b3690f9eb69f3c01339c Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 28 Jun 2018 15:15:20 -0500 Subject: [PATCH 1750/2380] qcow: Switch qcow_co_readv to byte-based calls We are gradually moving away from sector-based interfaces, towards byte-based. Make the change for the internals of the qcow driver read function, by iterating over offset/bytes instead of sector_num/nb_sectors, and with a rename of index_in_cluster and repurposing of n to track bytes instead of sectors. A later patch will then switch the qcow driver as a whole over to byte-based operation. Signed-off-by: Eric Blake Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- block/qcow.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/block/qcow.c b/block/qcow.c index d7c385a91d..436ad518be 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -617,13 +617,15 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; - int index_in_cluster; + int offset_in_cluster; int ret = 0, n; uint64_t cluster_offset; struct iovec hd_iov; QEMUIOVector hd_qiov; uint8_t *buf; void *orig_buf; + int64_t offset = sector_num * BDRV_SECTOR_SIZE; + int64_t bytes = nb_sectors * BDRV_SECTOR_SIZE; if (qiov->niov > 1) { buf = orig_buf = qemu_try_blockalign(bs, qiov->size); @@ -637,36 +639,35 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_co_mutex_lock(&s->lock); - while (nb_sectors != 0) { + while (bytes != 0) { /* prepare next request */ - ret = get_cluster_offset(bs, sector_num << 9, - 0, 0, 0, 0, &cluster_offset); + ret = get_cluster_offset(bs, offset, 0, 0, 0, 0, &cluster_offset); if (ret < 0) { break; } - index_in_cluster = sector_num & (s->cluster_sectors - 1); - n = s->cluster_sectors - index_in_cluster; - if (n > nb_sectors) { - n = nb_sectors; + offset_in_cluster = offset & (s->cluster_size - 1); + n = s->cluster_size - offset_in_cluster; + if (n > bytes) { + n = bytes; } if (!cluster_offset) { if (bs->backing) { /* read from the base image */ hd_iov.iov_base = (void *)buf; - hd_iov.iov_len = n * 512; + hd_iov.iov_len = n; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); /* qcow2 emits this on bs->file instead of bs->backing */ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); - ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); + ret = bdrv_co_preadv(bs->backing, offset, n, &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { break; } } else { /* Note: in this case, no need to wait */ - memset(buf, 0, 512 * n); + memset(buf, 0, n); } } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { /* add AIO support for compressed blocks ? */ @@ -674,21 +675,19 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, ret = -EIO; break; } - memcpy(buf, - s->cluster_cache + index_in_cluster * 512, 512 * n); + memcpy(buf, s->cluster_cache + offset_in_cluster, n); } else { if ((cluster_offset & 511) != 0) { ret = -EIO; break; } hd_iov.iov_base = (void *)buf; - hd_iov.iov_len = n * 512; + hd_iov.iov_len = n; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - ret = bdrv_co_readv(bs->file, - (cluster_offset >> 9) + index_in_cluster, - n, &hd_qiov); + ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster, + n, &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { break; @@ -696,8 +695,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, if (bs->encrypted) { assert(s->crypto); if (qcrypto_block_decrypt(s->crypto, - sector_num * BDRV_SECTOR_SIZE, buf, - n * BDRV_SECTOR_SIZE, NULL) < 0) { + offset, buf, n, NULL) < 0) { ret = -EIO; break; } @@ -705,9 +703,9 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, } ret = 0; - nb_sectors -= n; - sector_num += n; - buf += n * 512; + bytes -= n; + offset += n; + buf += n; } qemu_co_mutex_unlock(&s->lock); From d1326a786d471840b9055af53a7aa57c2e6d858c Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 28 Jun 2018 15:15:21 -0500 Subject: [PATCH 1751/2380] qcow: Switch qcow_co_writev to byte-based calls We are gradually moving away from sector-based interfaces, towards byte-based. Make the change for the internals of the qcow driver write function, by iterating over offset/bytes instead of sector_num/nb_sectors, and with a rename of index_in_cluster and repurposing of n to track bytes instead of sectors. A later patch will then switch the qcow driver as a whole over to byte-based operation. Signed-off-by: Eric Blake Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- block/qcow.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/block/qcow.c b/block/qcow.c index 436ad518be..8fe82ed58d 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -723,13 +723,15 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, int flags) { BDRVQcowState *s = bs->opaque; - int index_in_cluster; + int offset_in_cluster; uint64_t cluster_offset; int ret = 0, n; struct iovec hd_iov; QEMUIOVector hd_qiov; uint8_t *buf; void *orig_buf; + int64_t offset = sector_num * BDRV_SECTOR_SIZE; + int64_t bytes = nb_sectors * BDRV_SECTOR_SIZE; assert(!flags); s->cluster_cache_offset = -1; /* disable compressed cache */ @@ -749,16 +751,14 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, qemu_co_mutex_lock(&s->lock); - while (nb_sectors != 0) { - - index_in_cluster = sector_num & (s->cluster_sectors - 1); - n = s->cluster_sectors - index_in_cluster; - if (n > nb_sectors) { - n = nb_sectors; + while (bytes != 0) { + offset_in_cluster = offset & (s->cluster_size - 1); + n = s->cluster_size - offset_in_cluster; + if (n > bytes) { + n = bytes; } - ret = get_cluster_offset(bs, sector_num << 9, 1, 0, - index_in_cluster << 9, - (index_in_cluster + n) << 9, &cluster_offset); + ret = get_cluster_offset(bs, offset, 1, 0, offset_in_cluster, + offset_in_cluster + n, &cluster_offset); if (ret < 0) { break; } @@ -768,30 +768,28 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, } if (bs->encrypted) { assert(s->crypto); - if (qcrypto_block_encrypt(s->crypto, sector_num * BDRV_SECTOR_SIZE, - buf, n * BDRV_SECTOR_SIZE, NULL) < 0) { + if (qcrypto_block_encrypt(s->crypto, offset, buf, n, NULL) < 0) { ret = -EIO; break; } } hd_iov.iov_base = (void *)buf; - hd_iov.iov_len = n * 512; + hd_iov.iov_len = n; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - ret = bdrv_co_writev(bs->file, - (cluster_offset >> 9) + index_in_cluster, - n, &hd_qiov); + ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster, + n, &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { break; } ret = 0; - nb_sectors -= n; - sector_num += n; - buf += n * 512; + bytes -= n; + offset += n; + buf += n; } qemu_co_mutex_unlock(&s->lock); From 609841a3f8cb1761380032b7bbccb438abb8290f Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 28 Jun 2018 15:15:22 -0500 Subject: [PATCH 1752/2380] qcow: Switch to a byte-based driver We are gradually moving away from sector-based interfaces, towards byte-based. The qcow driver is now ready to fully utilize the byte-based callback interface, as long as we override the default alignment to still be 512 (needed at least for asserts present because of encryption, but easier to do everywhere than to audit which sub-sector requests are handled correctly, especially since we no longer recommend qcow for new disk images). Signed-off-by: Eric Blake Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- block/qcow.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/block/qcow.c b/block/qcow.c index 8fe82ed58d..102d058d1c 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -70,7 +70,6 @@ typedef struct QCowHeader { typedef struct BDRVQcowState { int cluster_bits; int cluster_size; - int cluster_sectors; int l2_bits; int l2_size; unsigned int l1_size; @@ -235,7 +234,6 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, } s->cluster_bits = header.cluster_bits; s->cluster_size = 1 << s->cluster_bits; - s->cluster_sectors = 1 << (s->cluster_bits - 9); s->l2_bits = header.l2_bits; s->l2_size = 1 << s->l2_bits; bs->total_sectors = header.size / 512; @@ -613,8 +611,18 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) return 0; } -static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) +static void qcow_refresh_limits(BlockDriverState *bs, Error **errp) +{ + /* At least encrypted images require 512-byte alignment. Apply the + * limit universally, rather than just on encrypted images, as + * it's easier to let the block layer handle rounding than to + * audit this code further. */ + bs->bl.request_alignment = BDRV_SECTOR_SIZE; +} + +static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, + int flags) { BDRVQcowState *s = bs->opaque; int offset_in_cluster; @@ -624,9 +632,8 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, QEMUIOVector hd_qiov; uint8_t *buf; void *orig_buf; - int64_t offset = sector_num * BDRV_SECTOR_SIZE; - int64_t bytes = nb_sectors * BDRV_SECTOR_SIZE; + assert(!flags); if (qiov->niov > 1) { buf = orig_buf = qemu_try_blockalign(bs, qiov->size); if (buf == NULL) { @@ -718,9 +725,9 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, return ret; } -static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov, - int flags) +static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, + int flags) { BDRVQcowState *s = bs->opaque; int offset_in_cluster; @@ -730,8 +737,6 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector hd_qiov; uint8_t *buf; void *orig_buf; - int64_t offset = sector_num * BDRV_SECTOR_SIZE; - int64_t bytes = nb_sectors * BDRV_SECTOR_SIZE; assert(!flags); s->cluster_cache_offset = -1; /* disable compressed cache */ @@ -1104,8 +1109,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, if (ret != Z_STREAM_END || out_len >= s->cluster_size) { /* could not compress: write normal cluster */ - ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS, - bytes >> BDRV_SECTOR_BITS, qiov, 0); + ret = qcow_co_pwritev(bs, offset, bytes, qiov, 0); if (ret < 0) { goto fail; } @@ -1190,9 +1194,10 @@ static BlockDriver bdrv_qcow = { .bdrv_co_create_opts = qcow_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .supports_backing = true, + .bdrv_refresh_limits = qcow_refresh_limits, - .bdrv_co_readv = qcow_co_readv, - .bdrv_co_writev = qcow_co_writev, + .bdrv_co_preadv = qcow_co_preadv, + .bdrv_co_pwritev = qcow_co_pwritev, .bdrv_co_block_status = qcow_co_block_status, .bdrv_make_empty = qcow_make_empty, From 04a11d87d1dfdc5f8081398694db8849d010c12d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 28 Jun 2018 15:15:23 -0500 Subject: [PATCH 1753/2380] replication: Switch to byte-based calls We are gradually moving away from sector-based interfaces, towards byte-based. Make the change for the last few sector-based calls into the block layer from the replication driver. Ideally, the replication driver should switch to doing everything byte-based, but that's a more invasive change that requires a bit more auditing. Signed-off-by: Eric Blake Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- block/replication.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/block/replication.c b/block/replication.c index 826db7b304..6349d6958e 100644 --- a/block/replication.c +++ b/block/replication.c @@ -246,13 +246,14 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs, backup_cow_request_begin(&req, child->bs->job, sector_num * BDRV_SECTOR_SIZE, remaining_bytes); - ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, - qiov); + ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE, + remaining_bytes, qiov, 0); backup_cow_request_end(&req); goto out; } - ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, qiov); + ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE, + remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0); out: return replication_return_value(s, ret); } @@ -279,8 +280,8 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, } if (ret == 0) { - ret = bdrv_co_writev(top, sector_num, - remaining_sectors, qiov); + ret = bdrv_co_pwritev(top, sector_num * BDRV_SECTOR_SIZE, + remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0); return replication_return_value(s, ret); } @@ -306,7 +307,8 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count); target = ret ? top : base; - ret = bdrv_co_writev(target, sector_num, n, &hd_qiov); + ret = bdrv_co_pwritev(target, sector_num * BDRV_SECTOR_SIZE, + n * BDRV_SECTOR_SIZE, &hd_qiov, 0); if (ret < 0) { goto out1; } From 3a7404b31e96156ea35be6fec938e162517e28d9 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 28 Jun 2018 15:15:24 -0500 Subject: [PATCH 1754/2380] vhdx: Switch to byte-based calls We are gradually moving away from sector-based interfaces, towards byte-based. Make the change for the last few sector-based calls into the block layer from the vhdx driver. Ideally, the vhdx driver should switch to doing everything byte-based, but that's a more invasive change that requires a bit more auditing. Signed-off-by: Eric Blake Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- block/vhdx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/block/vhdx.c b/block/vhdx.c index a677703a9e..4d0819750f 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1127,9 +1127,9 @@ static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num, break; case PAYLOAD_BLOCK_FULLY_PRESENT: qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_readv(bs->file, - sinfo.file_offset >> BDRV_SECTOR_BITS, - sinfo.sectors_avail, &hd_qiov); + ret = bdrv_co_preadv(bs->file, sinfo.file_offset, + sinfo.sectors_avail * BDRV_SECTOR_SIZE, + &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { goto exit; @@ -1349,9 +1349,9 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, } /* block exists, so we can just overwrite it */ qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_writev(bs->file, - sinfo.file_offset >> BDRV_SECTOR_BITS, - sectors_to_write, &hd_qiov); + ret = bdrv_co_pwritev(bs->file, sinfo.file_offset, + sectors_to_write * BDRV_SECTOR_SIZE, + &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { goto error_bat_restore; From 583c99d39368526dfb57a715b04a6ceea27dbe1e Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 28 Jun 2018 15:15:25 -0500 Subject: [PATCH 1755/2380] block: Remove unused sector-based vectored I/O We are gradually moving away from sector-based interfaces, towards byte-based. Now that all callers of vectored I/O have been converted to use our preferred byte-based bdrv_co_p{read,write}v(), we can delete the unused bdrv_co_{read,write}v(). Furthermore, this gets rid of the signature difference between the public bdrv_co_writev() and the callback .bdrv_co_writev (the latter still exists, because some drivers still need more work before they are fully byte-based). Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- block/io.c | 36 ------------------------------------ include/block/block.h | 4 ---- 2 files changed, 40 deletions(-) diff --git a/block/io.c b/block/io.c index b63822280a..7035b78a20 100644 --- a/block/io.c +++ b/block/io.c @@ -1429,24 +1429,6 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child, return ret; } -static int coroutine_fn bdrv_co_do_readv(BdrvChild *child, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, - BdrvRequestFlags flags) -{ - if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { - return -EINVAL; - } - - return bdrv_co_preadv(child, sector_num << BDRV_SECTOR_BITS, - nb_sectors << BDRV_SECTOR_BITS, qiov, flags); -} - -int coroutine_fn bdrv_co_readv(BdrvChild *child, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) -{ - return bdrv_co_do_readv(child, sector_num, nb_sectors, qiov, 0); -} - static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { @@ -1889,24 +1871,6 @@ out: return ret; } -static int coroutine_fn bdrv_co_do_writev(BdrvChild *child, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, - BdrvRequestFlags flags) -{ - if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { - return -EINVAL; - } - - return bdrv_co_pwritev(child, sector_num << BDRV_SECTOR_BITS, - nb_sectors << BDRV_SECTOR_BITS, qiov, flags); -} - -int coroutine_fn bdrv_co_writev(BdrvChild *child, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) -{ - return bdrv_co_do_writev(child, sector_num, nb_sectors, qiov, 0); -} - int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, int bytes, BdrvRequestFlags flags) { diff --git a/include/block/block.h b/include/block/block.h index 42e59ff585..2ffc1c64c6 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -285,10 +285,6 @@ int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes); int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov); int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, const void *buf, int count); -int coroutine_fn bdrv_co_readv(BdrvChild *child, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); -int coroutine_fn bdrv_co_writev(BdrvChild *child, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); /* * Efficiently zero a region of the disk image. Note that this is a regular * I/O request like read or write and should have a reasonable size. This From 749c1d8e3e12c44247927d8f72f68ec0c0266f8b Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 19 Jun 2018 16:45:49 -0300 Subject: [PATCH 1756/2380] simpletrace: Convert name from mapping record to str The rest of the code assumes that idtoname is a (int -> str) dictionary, so convert the data accordingly. This is necessary to make the script work with Python 3 (where reads from a binary file return 'bytes' objects, not 'str'). Fixes the following error: $ python3 ./scripts/simpletrace.py trace-events-all trace-27445 b'object_class_dynamic_cast_assert' event is logged but is not \ declared in the trace events file, try using trace-events-all instead. Signed-off-by: Eduardo Habkost Message-id: 20180619194549.15584-1-ehabkost@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/simpletrace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py index d4a50a1e2b..4ad34f90cd 100755 --- a/scripts/simpletrace.py +++ b/scripts/simpletrace.py @@ -70,7 +70,7 @@ def get_record(edict, idtoname, rechdr, fobj): def get_mapping(fobj): (event_id, ) = struct.unpack('=Q', fobj.read(8)) (len, ) = struct.unpack('=L', fobj.read(4)) - name = fobj.read(len) + name = fobj.read(len).decode() return (event_id, name) From 82c4f87e0ed1701c1f51a46a3146643ed89b0bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 14:12:47 -0300 Subject: [PATCH 1757/2380] trace: Fix format string for the struct timeval members casted to size_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes when using GCC with -Wformat-signedness: migration/trace.h: In function ‘_nocheck__trace_dirty_bitmap_load_success’: migration/trace.h:6368:24: error: format ‘%zd’ expects argument of type ‘signed size_t’, but argument 3 has type ‘long unsigned int’ [-Werror=format=] qemu_log("%d@%zd.%06zd:dirty_bitmap_load_success " "" "\n", ~~^ %ld migration/trace.h:6370:18: (size_t)_now.tv_sec, (size_t)_now.tv_usec ~~~~~~~~~~~~~~~~~~~ migration/trace.h:6368:30: error: format ‘%zd’ expects argument of type ‘signed size_t’, but argument 4 has type ‘long unsigned int’ [-Werror=format=] qemu_log("%d@%zd.%06zd:dirty_bitmap_load_success " "" "\n", ~~~~^ %06ld migration/trace.h:6370:39: (size_t)_now.tv_sec, (size_t)_now.tv_usec ~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 78933d03ad..6751f41bc5 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -38,7 +38,7 @@ def generate_h(event, group): out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', ' struct timeval _now;', ' gettimeofday(&_now, NULL);', - ' qemu_log("%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",', + ' qemu_log("%%d@%%zu.%%06zu:%(name)s " %(fmt)s "\\n",', ' getpid(),', ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', ' %(argnames)s);', From dcfebcf9a3f350034d5acff6fe6019ce3586438a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 14:12:48 -0300 Subject: [PATCH 1758/2380] sdcard: Reduce sdcard_set_blocklen() trace digits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the Physical Layer Simplified Spec. "5.3 CSD Register": "The maximum block length might therefore be in the range 512...2048 bytes" Therefore 3 hexdigits are enough to report the block length. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi --- hw/sd/trace-events | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/trace-events b/hw/sd/trace-events index bfd1d62efc..d7880bcea5 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -37,7 +37,7 @@ sdcard_powerup(void) "" sdcard_inquiry_cmd41(void) "" sdcard_set_enable(bool current_state, bool new_state) "%u -> %u" sdcard_reset(void) "" -sdcard_set_blocklen(uint16_t length) "0x%04x" +sdcard_set_blocklen(uint16_t length) "0x%03x" sdcard_inserted(bool readonly) "read_only: %u" sdcard_ejected(void) "" sdcard_erase(void) "" From 0060918661faac174c7e75103ebd4d964c91c02e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 14:12:49 -0300 Subject: [PATCH 1759/2380] hw/char/serial: Convert from DPRINTF macro to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi --- hw/char/serial.c | 5 +++-- hw/char/trace-events | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/char/serial.c b/hw/char/serial.c index 605b0d02f9..26f53ba02f 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -29,6 +29,7 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "qemu/error-report.h" +#include "trace.h" //#define DEBUG_SERIAL @@ -335,7 +336,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, SerialState *s = opaque; addr &= 7; - DPRINTF("write addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 "\n", addr, val); + trace_serial_ioport_write(addr, val); switch(addr) { default: case 0: @@ -548,7 +549,7 @@ static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size) ret = s->scr; break; } - DPRINTF("read addr=0x%" HWADDR_PRIx " val=0x%02x\n", addr, ret); + trace_serial_ioport_read(addr, ret); return ret; } diff --git a/hw/char/trace-events b/hw/char/trace-events index ebd8a92450..158957627e 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -1,5 +1,9 @@ # See docs/devel/tracing.txt for syntax documentation. +# hw/char/serial.c +serial_ioport_read(uint16_t addr, uint8_t value) "read addr 0x%02x val 0x%02x" +serial_ioport_write(uint16_t addr, uint8_t value) "write addr 0x%02x val 0x%02x" + # hw/char/virtio-serial-bus.c virtio_serial_send_control_event(unsigned int port, uint16_t event, uint16_t value) "port %u, event %u, value %u" virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, throttle %d" From cb2d721cb3a2a83084141952f09a7363e2f52705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 14:12:50 -0300 Subject: [PATCH 1760/2380] hw/char/parallel: Convert from pdebug() macro to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi --- hw/char/parallel.c | 16 +++++++++++++--- hw/char/trace-events | 4 ++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 35748e6c1b..a80da47ecf 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -30,6 +30,7 @@ #include "hw/isa/isa.h" #include "hw/char/parallel.h" #include "sysemu/sysemu.h" +#include "trace.h" //#define DEBUG_PARALLEL @@ -110,9 +111,8 @@ parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val) { ParallelState *s = opaque; - pdebug("write addr=0x%02x val=0x%02x\n", addr, val); - addr &= 7; + trace_parallel_ioport_write("SW", addr, val); switch(addr) { case PARA_REG_DATA: s->dataw = val; @@ -157,6 +157,7 @@ static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val) s->last_read_offset = ~0U; addr &= 7; + trace_parallel_ioport_write("HW", addr, val); switch(addr) { case PARA_REG_DATA: if (s->dataw == val) @@ -230,6 +231,8 @@ parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val) struct ParallelIOArg ioarg = { .buffer = &eppdata, .count = sizeof(eppdata) }; + + trace_parallel_ioport_write("EPP", addr, val); if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) { /* Controls not correct for EPP data cycle, so do nothing */ pdebug("we%04x s\n", val); @@ -253,6 +256,8 @@ parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val) struct ParallelIOArg ioarg = { .buffer = &eppdata, .count = sizeof(eppdata) }; + + trace_parallel_ioport_write("EPP", addr, val); if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) { /* Controls not correct for EPP data cycle, so do nothing */ pdebug("we%08x s\n", val); @@ -299,7 +304,7 @@ static uint32_t parallel_ioport_read_sw(void *opaque, uint32_t addr) ret = s->control; break; } - pdebug("read addr=0x%02x val=0x%02x\n", addr, ret); + trace_parallel_ioport_read("SW", addr, ret); return ret; } @@ -371,6 +376,7 @@ static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr) } break; } + trace_parallel_ioport_read("HW", addr, ret); s->last_read_offset = addr; return ret; } @@ -399,6 +405,7 @@ parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr) } else pdebug("re%04x\n", ret); + trace_parallel_ioport_read("EPP", addr, ret); return ret; } @@ -426,11 +433,13 @@ parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr) } else pdebug("re%08x\n", ret); + trace_parallel_ioport_read("EPP", addr, ret); return ret; } static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val) { + trace_parallel_ioport_write("ECP", addr & 7, val); pdebug("wecp%d=%02x\n", addr & 7, val); } @@ -438,6 +447,7 @@ static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr) { uint8_t ret = 0xff; + trace_parallel_ioport_read("ECP", addr & 7, ret); pdebug("recp%d:%02x\n", addr & 7, ret); return ret; } diff --git a/hw/char/trace-events b/hw/char/trace-events index 158957627e..b64213d4dd 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -1,5 +1,9 @@ # See docs/devel/tracing.txt for syntax documentation. +# hw/char/parallel.c +parallel_ioport_read(const char *desc, uint16_t addr, uint8_t value) "read [%s] addr 0x%02x val 0x%02x" +parallel_ioport_write(const char *desc, uint16_t addr, uint8_t value) "write [%s] addr 0x%02x val 0x%02x" + # hw/char/serial.c serial_ioport_read(uint16_t addr, uint8_t value) "read addr 0x%02x val 0x%02x" serial_ioport_write(uint16_t addr, uint8_t value) "write addr 0x%02x val 0x%02x" From 349632a29ddb7d2ac7a7dc26499640ab2123fc7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 14:12:52 -0300 Subject: [PATCH 1761/2380] hw/input/tsc2005: Convert a fprintf() call to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi --- hw/input/trace-events | 3 +++ hw/input/tsc2005.c | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/input/trace-events b/hw/input/trace-events index db72484a25..3965a842ae 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -41,5 +41,8 @@ milkymist_softusb_pulse_irq(void) "Pulse IRQ" hid_kbd_queue_full(void) "queue full" hid_kbd_queue_empty(void) "queue empty" +# hw/input/tsc2005.c +tsc2005_sense(const char *state) "touchscreen sense %s" + # hw/input/virtio virtio_input_queue_full(void) "queue full" diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c index 4dd95596ab..2b9108a193 100644 --- a/hw/input/tsc2005.c +++ b/hw/input/tsc2005.c @@ -24,6 +24,7 @@ #include "qemu/timer.h" #include "ui/console.h" #include "hw/devices.h" +#include "trace.h" #define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10))) @@ -201,8 +202,7 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data) s->host_mode = (data >> 15) != 0; if (s->enabled != !(data & 0x4000)) { s->enabled = !(data & 0x4000); - fprintf(stderr, "%s: touchscreen sense %sabled\n", - __func__, s->enabled ? "en" : "dis"); + trace_tsc2005_sense(s->enabled ? "enabled" : "disabled"); if (s->busy && !s->enabled) timer_del(s->timer); s->busy = s->busy && s->enabled; @@ -340,8 +340,7 @@ static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value) s->nextprecision = (value >> 2) & 1; if (s->enabled != !(value & 1)) { s->enabled = !(value & 1); - fprintf(stderr, "%s: touchscreen sense %sabled\n", - __func__, s->enabled ? "en" : "dis"); + trace_tsc2005_sense(s->enabled ? "enabled" : "disabled"); if (s->busy && !s->enabled) timer_del(s->timer); s->busy = s->busy && s->enabled; From cd4479a91a0f56d59dbc4be6341812009ce141f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 14:12:53 -0300 Subject: [PATCH 1762/2380] hw/net/ne2000: Add trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi --- hw/net/ne2000.c | 17 ++++++++++++----- hw/net/trace-events | 4 ++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 3a9fc89e48..26b234b7eb 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -26,6 +26,7 @@ #include "net/eth.h" #include "ne2000.h" #include "sysemu/sysemu.h" +#include "trace.h" /* debug NE2000 card */ //#define DEBUG_NE2000 @@ -662,19 +663,24 @@ static uint64_t ne2000_read(void *opaque, hwaddr addr, unsigned size) { NE2000State *s = opaque; + uint64_t val; if (addr < 0x10 && size == 1) { - return ne2000_ioport_read(s, addr); + val = ne2000_ioport_read(s, addr); } else if (addr == 0x10) { if (size <= 2) { - return ne2000_asic_ioport_read(s, addr); + val = ne2000_asic_ioport_read(s, addr); } else { - return ne2000_asic_ioport_readl(s, addr); + val = ne2000_asic_ioport_readl(s, addr); } } else if (addr == 0x1f && size == 1) { - return ne2000_reset_ioport_read(s, addr); + val = ne2000_reset_ioport_read(s, addr); + } else { + val = ((uint64_t)1 << (size * 8)) - 1; } - return ((uint64_t)1 << (size * 8)) - 1; + trace_ne2000_read(addr, val); + + return val; } static void ne2000_write(void *opaque, hwaddr addr, @@ -682,6 +688,7 @@ static void ne2000_write(void *opaque, hwaddr addr, { NE2000State *s = opaque; + trace_ne2000_write(addr, data); if (addr < 0x10 && size == 1) { ne2000_ioport_write(s, addr, data); } else if (addr == 0x10) { diff --git a/hw/net/trace-events b/hw/net/trace-events index 45c4e9fba0..95a1de01e9 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -23,6 +23,10 @@ mipsnet_read(uint64_t addr, uint32_t val) "read addr=0x%" PRIx64 " val=0x%x" mipsnet_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 mipsnet_irq(uint32_t isr, uint32_t intctl) "set irq to %d (0x%02x)" +# hw/net/ne2000.c +ne2000_read(uint64_t addr, uint64_t val) "read addr=0x%" PRIx64 " val=0x%" PRIx64 +ne2000_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 + # hw/net/opencores_eth.c open_eth_mii_write(unsigned idx, uint16_t v) "MII[0x%02x] <- 0x%04x" open_eth_mii_read(unsigned idx, uint16_t v) "MII[0x%02x] -> 0x%04x" From a816b62583acf36d78559eca483f32dd2c20c593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 14:12:54 -0300 Subject: [PATCH 1763/2380] hw/net/ne2000: Convert printf() calls to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi --- hw/net/ne2000.c | 8 ++------ hw/net/trace-events | 2 ++ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 26b234b7eb..07d79e317f 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -277,9 +277,7 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) int offset, page, index; addr &= 0xf; -#ifdef DEBUG_NE2000 - printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val); -#endif + trace_ne2000_ioport_write(addr, val); if (addr == E8390_CMD) { /* control register */ s->cmd = val; @@ -442,9 +440,7 @@ static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) break; } } -#ifdef DEBUG_NE2000 - printf("NE2000: read addr=0x%x val=%02x\n", addr, ret); -#endif + trace_ne2000_ioport_read(addr, ret); return ret; } diff --git a/hw/net/trace-events b/hw/net/trace-events index 95a1de01e9..f57010df37 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -26,6 +26,8 @@ mipsnet_irq(uint32_t isr, uint32_t intctl) "set irq to %d (0x%02x)" # hw/net/ne2000.c ne2000_read(uint64_t addr, uint64_t val) "read addr=0x%" PRIx64 " val=0x%" PRIx64 ne2000_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 +ne2000_ioport_read(uint64_t addr, uint64_t val) "io read addr=0x%02" PRIx64 " val=0x%02" PRIx64 +ne2000_ioport_write(uint64_t addr, uint64_t val) "io write addr=0x%02" PRIx64 " val=0x%02" PRIx64 # hw/net/opencores_eth.c open_eth_mii_write(unsigned idx, uint16_t v) "MII[0x%02x] <- 0x%04x" From 4b46fdd0d4ad2dcbdefd09d410cc90d84452ee2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 14:12:55 -0300 Subject: [PATCH 1764/2380] hw/net/etraxfs_eth: Convert printf() calls to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Alistair Francis Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi --- hw/net/etraxfs_eth.c | 8 ++++---- hw/net/trace-events | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c index 013c8d0a41..a6932432b1 100644 --- a/hw/net/etraxfs_eth.c +++ b/hw/net/etraxfs_eth.c @@ -27,6 +27,7 @@ #include "net/net.h" #include "hw/cris/etraxfs.h" #include "qemu/error-report.h" +#include "trace.h" #define D(x) @@ -106,7 +107,7 @@ static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) r = phy->regs[regnum]; break; } - D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum)); + trace_mdio_phy_read(regnum, r); return r; } @@ -116,7 +117,7 @@ tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data) int regnum; regnum = req & 0x1f; - D(printf("%s reg[%d] = %x\n", __func__, regnum, data)); + trace_mdio_phy_write(regnum, data); switch (regnum) { default: phy->regs[regnum] = data; @@ -206,8 +207,7 @@ static void mdio_cycle(struct qemu_mdio *bus) { bus->cnt++; - D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n", - bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive)); + trace_mdio_bitbang(bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive); #if 0 if (bus->mdc) { printf("%d", bus->mdio); diff --git a/hw/net/trace-events b/hw/net/trace-events index f57010df37..663bea1b74 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -1,5 +1,10 @@ # See docs/devel/tracing.txt for syntax documentation. +# hw/net/etraxfs_eth.c +mdio_phy_read(int regnum, uint16_t value) "read phy_reg:%d value:0x%04x" +mdio_phy_write(int regnum, uint16_t value) "write phy_reg:%d value:0x%04x" +mdio_bitbang(bool mdc, bool mdio, int state, uint16_t cnt, unsigned int drive) "bitbang mdc=%u mdio=%u state=%d cnt=%u drv=%d" + # hw/net/lance.c lance_mem_readw(uint64_t addr, uint32_t ret) "addr=0x%"PRIx64"val=0x%04x" lance_mem_writew(uint64_t addr, uint32_t val) "addr=0x%"PRIx64"val=0x%04x" From 1a5396d961c8c90fea4cd2f740ccfd2633a8e10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 14:12:56 -0300 Subject: [PATCH 1765/2380] hw/block/fdc: Convert from FLOPPY_DPRINTF() macro to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: John Snow Signed-off-by: Stefan Hajnoczi --- hw/block/fdc.c | 6 +++--- hw/block/trace-events | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/block/fdc.c b/hw/block/fdc.c index cd29e27d8f..c7b4fe9b3e 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -40,6 +40,7 @@ #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "qemu/log.h" +#include "trace.h" /********************************************************/ /* debug Floppy devices */ @@ -934,7 +935,7 @@ static uint32_t fdctrl_read (void *opaque, uint32_t reg) retval = (uint32_t)(-1); break; } - FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval); + trace_fdc_ioport_read(reg, retval); return retval; } @@ -943,9 +944,8 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) { FDCtrl *fdctrl = opaque; - FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); - reg &= 7; + trace_fdc_ioport_write(reg, value); switch (reg) { case FD_REG_DOR: fdctrl_write_dor(fdctrl, value); diff --git a/hw/block/trace-events b/hw/block/trace-events index 6b9e733412..d842c45409 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -1,5 +1,9 @@ # See docs/devel/tracing.txt for syntax documentation. +# hw/block/fdc.c +fdc_ioport_read(uint8_t reg, uint8_t value) "read reg 0x%02x val 0x%02x" +fdc_ioport_write(uint8_t reg, uint8_t value) "write reg 0x%02x val 0x%02x" + # hw/block/virtio-blk.c virtio_blk_req_complete(void *vdev, void *req, int status) "vdev %p req %p status %d" virtio_blk_rw_complete(void *vdev, void *req, int ret) "vdev %p req %p ret %d" From 13019f1fd6c683b243120b7eb999f1b50a224940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 14:12:57 -0300 Subject: [PATCH 1766/2380] hw/block/pflash_cfi: Convert from DPRINTF() macro to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé [Fixed lx -> PRIx64 as suggested by Philippe. --Stefan] Signed-off-by: Stefan Hajnoczi --- hw/block/pflash_cfi01.c | 42 +++++++++++++++-------------------------- hw/block/pflash_cfi02.c | 18 +++++++++--------- hw/block/trace-events | 13 +++++++++++++ 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index e4b5b3c273..bffb4c40e7 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -47,6 +47,7 @@ #include "qemu/log.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" +#include "trace.h" #define PFLASH_BUG(fmt, ...) \ do { \ @@ -120,7 +121,7 @@ static void pflash_timer (void *opaque) { pflash_t *pfl = opaque; - DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); + trace_pflash_timer_expired(pfl->cmd); /* Reset flash */ pfl->status ^= 0x80; memory_region_rom_device_set_romd(&pfl->mem, true); @@ -218,15 +219,14 @@ static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) switch (boff & 0xFF) { case 0: resp = pfl->ident0; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, resp); + trace_pflash_manufacturer_id(resp); break; case 1: resp = pfl->ident1; - DPRINTF("%s: Device ID Code %04x\n", __func__, resp); + trace_pflash_device_id(resp); break; default: - DPRINTF("%s: Read Device Information offset=%x\n", __func__, - (unsigned)offset); + trace_pflash_device_info(offset); return 0; break; } @@ -251,8 +251,7 @@ static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, switch (width) { case 1: ret = p[offset]; - DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n", - __func__, offset, ret); + trace_pflash_data_read8(offset, ret); break; case 2: if (be) { @@ -262,8 +261,7 @@ static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, ret = p[offset]; ret |= p[offset + 1] << 8; } - DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n", - __func__, offset, ret); + trace_pflash_data_read16(offset, ret); break; case 4: if (be) { @@ -277,8 +275,7 @@ static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, ret |= p[offset + 2] << 16; ret |= p[offset + 3] << 24; } - DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n", - __func__, offset, ret); + trace_pflash_data_read32(offset, ret); break; default: DPRINTF("BUG in %s\n", __func__); @@ -294,11 +291,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, uint32_t ret; ret = -1; - -#if 0 - DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n", - __func__, offset, pfl->cmd, width); -#endif + trace_pflash_read(offset, pfl->cmd, width, pfl->wcycle); switch (pfl->cmd) { default: /* This should never happen : reset state & treat it as a read */ @@ -349,15 +342,14 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, switch (boff) { case 0: ret = pfl->ident0 << 8 | pfl->ident1; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); + trace_pflash_manufacturer_id(ret); break; case 1: ret = pfl->ident2 << 8 | pfl->ident3; - DPRINTF("%s: Device ID Code %04x\n", __func__, ret); + trace_pflash_device_id(ret); break; default: - DPRINTF("%s: Read Device Information boff=%x\n", __func__, - (unsigned)boff); + trace_pflash_device_info(boff); ret = 0; break; } @@ -425,9 +417,7 @@ static inline void pflash_data_write(pflash_t *pfl, hwaddr offset, { uint8_t *p = pfl->storage; - DPRINTF("%s: block write offset " TARGET_FMT_plx - " value %x counter %016" PRIx64 "\n", - __func__, offset, value, pfl->counter); + trace_pflash_data_write(offset, value, width, pfl->counter); switch (width) { case 1: p[offset] = value; @@ -466,9 +456,7 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, cmd = value; - DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n", - __func__, offset, value, width, pfl->wcycle); - + trace_pflash_write(offset, value, width, pfl->wcycle); if (!pfl->wcycle) { /* Set the device in I/O access mode */ memory_region_rom_device_set_romd(&pfl->mem, false); @@ -656,8 +644,8 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, "\n", __func__, offset, pfl->wcycle, pfl->cmd, value); reset_flash: + trace_pflash_reset(); memory_region_rom_device_set_romd(&pfl->mem, true); - pfl->wcycle = 0; pfl->cmd = 0; } diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 6c18e5e578..0f8b7b8c7b 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -43,6 +43,7 @@ #include "sysemu/block-backend.h" #include "qemu/host-utils.h" #include "hw/sysbus.h" +#include "trace.h" //#define PFLASH_DEBUG #ifdef PFLASH_DEBUG @@ -124,7 +125,7 @@ static void pflash_timer (void *opaque) { pflash_t *pfl = opaque; - DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); + trace_pflash_timer_expired(pfl->cmd); /* Reset flash */ pfl->status ^= 0x80; if (pfl->bypass) { @@ -143,8 +144,8 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, uint32_t ret; uint8_t *p; - DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset); ret = -1; + trace_pflash_read(offset, pfl->cmd, width, pfl->wcycle); /* Lazy reset to ROMD mode after a certain amount of read accesses */ if (!pfl->rom_mode && pfl->wcycle == 0 && ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) { @@ -172,7 +173,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, switch (width) { case 1: ret = p[offset]; -// DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret); + trace_pflash_data_read8(offset, ret); break; case 2: if (be) { @@ -182,7 +183,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, ret = p[offset]; ret |= p[offset + 1] << 8; } -// DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret); + trace_pflash_data_read16(offset, ret); break; case 4: if (be) { @@ -196,7 +197,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, ret |= p[offset + 2] << 16; ret |= p[offset + 3] << 24; } -// DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret); + trace_pflash_data_read32(offset, ret); break; } break; @@ -274,8 +275,7 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, #endif goto reset_flash; } - DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d %d\n", __func__, - offset, value, width, pfl->wcycle); + trace_pflash_write(offset, value, width, pfl->wcycle); offset &= pfl->chip_len - 1; DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__, @@ -345,8 +345,7 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, /* We need another unlock sequence */ goto check_unlock0; case 0xA0: - DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n", - __func__, offset, value, width); + trace_pflash_data_write(offset, value, width, 0); p = pfl->storage; if (!pfl->ro) { switch (width) { @@ -483,6 +482,7 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, /* Reset flash */ reset_flash: + trace_pflash_reset(); pfl->bypass = 0; pfl->wcycle = 0; pfl->cmd = 0; diff --git a/hw/block/trace-events b/hw/block/trace-events index d842c45409..335c092450 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -4,6 +4,19 @@ fdc_ioport_read(uint8_t reg, uint8_t value) "read reg 0x%02x val 0x%02x" fdc_ioport_write(uint8_t reg, uint8_t value) "write reg 0x%02x val 0x%02x" +# hw/block/pflash_cfi0?.c +pflash_reset(void) "reset" +pflash_read(uint64_t offset, uint8_t cmd, int width, uint8_t wcycle) "offset:0x%04"PRIx64" cmd:0x%02x width:%d wcycle:%u" +pflash_write(uint64_t offset, uint32_t value, int width, uint8_t wcycle) "offset:0x%04"PRIx64" value:0x%03x width:%d wcycle:%u" +pflash_timer_expired(uint8_t cmd) "command 0x%02x done" +pflash_data_read8(uint64_t offset, uint32_t value) "data offset:0x%04"PRIx64" value:0x%02x" +pflash_data_read16(uint64_t offset, uint32_t value) "data offset:0x%04"PRIx64" value:0x%04x" +pflash_data_read32(uint64_t offset, uint32_t value) "data offset:0x%04"PRIx64" value:0x%08x" +pflash_data_write(uint64_t offset, uint32_t value, int width, uint64_t counter) "data offset:0x%04"PRIx64" value:0x%08x width:%d counter:0x%016"PRIx64 +pflash_manufacturer_id(uint16_t id) "Read Manufacturer ID: 0x%04x" +pflash_device_id(uint16_t id) "Read Device ID: 0x%04x" +pflash_device_info(uint64_t offset) "Read Device Information offset:0x%04"PRIx64 + # hw/block/virtio-blk.c virtio_blk_req_complete(void *vdev, void *req, int status) "vdev %p req %p status %d" virtio_blk_rw_complete(void *vdev, void *req, int ret) "vdev %p req %p ret %d" From 329b72916f54a6ef2842ae172c086986af19c677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 29 Jun 2018 15:11:00 +0100 Subject: [PATCH 1767/2380] hw/block/fdc: Replace error_setg(&error_abort) by assert() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use assert() instead of error_setg(&error_abort), as suggested by the "qapi/error.h" documentation: Please don't error_setg(&error_fatal, ...), use error_report() and exit(), because that's more obvious. Likewise, don't error_setg(&error_abort, ...), use assert(). Signed-off-by: Philippe Mathieu-Daudé Acked-by: John Snow Message-id: 20180625165749.3910-2-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/block/fdc.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/hw/block/fdc.c b/hw/block/fdc.c index cd29e27d8f..7c1c57f57f 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -396,16 +396,9 @@ static int pick_geometry(FDrive *drv) nb_sectors, FloppyDriveType_str(parse->drive)); } + assert(type_match != -1 && "misconfigured fd_format"); match = type_match; } - - /* No match of any kind found -- fd_format is misconfigured, abort. */ - if (match == -1) { - error_setg(&error_abort, "No candidate geometries present in table " - " for floppy drive type '%s'", - FloppyDriveType_str(drv->drive)); - } - parse = &(fd_formats[match]); out: From 88bbd3fb60bf16ed3e974276814df4e9c4b2b92f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 29 Jun 2018 15:11:00 +0100 Subject: [PATCH 1768/2380] hw/arm/sysbus-fdt: Replace error_setg(&error_fatal) by error_report() + exit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use error_report() + exit() instead of error_setg(&error_fatal), as suggested by the "qapi/error.h" documentation: Please don't error_setg(&error_fatal, ...), use error_report() and exit(), because that's more obvious. This fixes CID 1352173: "Passing null pointer dt_name to qemu_fdt_node_path, which dereferences it." And this also fixes: hw/arm/sysbus-fdt.c:322:9: warning: Array access (from variable 'node_path') results in a null pointer dereference if (node_path[1]) { ^~~~~~~~~~~~ Fixes: Coverity CID 1352173 (Dereference after null check) Suggested-by: Eric Blake Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Eric Auger Message-id: 20180625165749.3910-3-f4bug@amsat.org Signed-off-by: Peter Maydell --- hw/arm/sysbus-fdt.c | 53 +++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c index 277ed872e7..0d4c75702c 100644 --- a/hw/arm/sysbus-fdt.c +++ b/hw/arm/sysbus-fdt.c @@ -92,16 +92,20 @@ static void copy_properties_from_host(HostProperty *props, int nb_props, r = qemu_fdt_getprop(host_fdt, node_path, props[i].name, &prop_len, - props[i].optional ? &err : &error_fatal); + &err); if (r) { qemu_fdt_setprop(guest_fdt, nodename, props[i].name, r, prop_len); } else { - if (prop_len != -FDT_ERR_NOTFOUND) { - /* optional property not returned although property exists */ - error_report_err(err); - } else { + if (props[i].optional && prop_len == -FDT_ERR_NOTFOUND) { + /* optional property does not exist */ error_free(err); + } else { + error_report_err(err); + } + if (!props[i].optional) { + /* mandatory property not found: bail out */ + exit(1); } } } @@ -138,9 +142,9 @@ static void fdt_build_clock_node(void *host_fdt, void *guest_fdt, node_offset = fdt_node_offset_by_phandle(host_fdt, host_phandle); if (node_offset <= 0) { - error_setg(&error_fatal, - "not able to locate clock handle %d in host device tree", - host_phandle); + error_report("not able to locate clock handle %d in host device tree", + host_phandle); + exit(1); } node_path = g_malloc(path_len); while ((ret = fdt_get_path(host_fdt, node_offset, node_path, path_len)) @@ -149,16 +153,16 @@ static void fdt_build_clock_node(void *host_fdt, void *guest_fdt, node_path = g_realloc(node_path, path_len); } if (ret < 0) { - error_setg(&error_fatal, - "not able to retrieve node path for clock handle %d", - host_phandle); + error_report("not able to retrieve node path for clock handle %d", + host_phandle); + exit(1); } r = qemu_fdt_getprop(host_fdt, node_path, "compatible", &prop_len, &error_fatal); if (strcmp(r, "fixed-clock")) { - error_setg(&error_fatal, - "clock handle %d is not a fixed clock", host_phandle); + error_report("clock handle %d is not a fixed clock", host_phandle); + exit(1); } nodename = strrchr(node_path, '/'); @@ -301,34 +305,37 @@ static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) dt_name = sysfs_to_dt_name(vbasedev->name); if (!dt_name) { - error_setg(&error_fatal, "%s incorrect sysfs device name %s", - __func__, vbasedev->name); + error_report("%s incorrect sysfs device name %s", + __func__, vbasedev->name); + exit(1); } node_path = qemu_fdt_node_path(host_fdt, dt_name, vdev->compat, &error_fatal); if (!node_path || !node_path[0]) { - error_setg(&error_fatal, "%s unable to retrieve node path for %s/%s", - __func__, dt_name, vdev->compat); + error_report("%s unable to retrieve node path for %s/%s", + __func__, dt_name, vdev->compat); + exit(1); } if (node_path[1]) { - error_setg(&error_fatal, "%s more than one node matching %s/%s!", - __func__, dt_name, vdev->compat); + error_report("%s more than one node matching %s/%s!", + __func__, dt_name, vdev->compat); + exit(1); } g_free(dt_name); if (vbasedev->num_regions != 5) { - error_setg(&error_fatal, "%s Does the host dt node combine XGBE/PHY?", - __func__); + error_report("%s Does the host dt node combine XGBE/PHY?", __func__); + exit(1); } /* generate nodes for DMA_CLK and PTP_CLK */ r = qemu_fdt_getprop(host_fdt, node_path[0], "clocks", &prop_len, &error_fatal); if (prop_len != 8) { - error_setg(&error_fatal, "%s clocks property should contain 2 handles", - __func__); + error_report("%s clocks property should contain 2 handles", __func__); + exit(1); } host_clock_phandles = (uint32_t *)r; guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt); From 38754e43f605be90f0603631f257b3bc2d115126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 29 Jun 2018 15:11:00 +0100 Subject: [PATCH 1769/2380] device_tree: Replace error_setg(&error_fatal) by error_report() + exit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use error_report() + exit() instead of error_setg(&error_fatal), as suggested by the "qapi/error.h" documentation: Please don't error_setg(&error_fatal, ...), use error_report() and exit(), because that's more obvious. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Eric Auger Reviewed-by: Markus Armbruster Reviewed-by: David Gibson Message-id: 20180625165749.3910-4-f4bug@amsat.org Signed-off-by: Peter Maydell --- device_tree.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/device_tree.c b/device_tree.c index 52c3358a55..3553819257 100644 --- a/device_tree.c +++ b/device_tree.c @@ -140,15 +140,16 @@ static void read_fstree(void *fdt, const char *dirname) const char *parent_node; if (strstr(dirname, root_dir) != dirname) { - error_setg(&error_fatal, "%s: %s must be searched within %s", - __func__, dirname, root_dir); + error_report("%s: %s must be searched within %s", + __func__, dirname, root_dir); + exit(1); } parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)]; d = opendir(dirname); if (!d) { - error_setg(&error_fatal, "%s cannot open %s", __func__, dirname); - return; + error_report("%s cannot open %s", __func__, dirname); + exit(1); } while ((de = readdir(d)) != NULL) { @@ -162,7 +163,8 @@ static void read_fstree(void *fdt, const char *dirname) tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name); if (lstat(tmpnam, &st) < 0) { - error_setg(&error_fatal, "%s cannot lstat %s", __func__, tmpnam); + error_report("%s cannot lstat %s", __func__, tmpnam); + exit(1); } if (S_ISREG(st.st_mode)) { @@ -170,8 +172,9 @@ static void read_fstree(void *fdt, const char *dirname) gsize len; if (!g_file_get_contents(tmpnam, &val, &len, NULL)) { - error_setg(&error_fatal, "%s not able to extract info from %s", - __func__, tmpnam); + error_report("%s not able to extract info from %s", + __func__, tmpnam); + exit(1); } if (strlen(parent_node) > 0) { @@ -206,9 +209,9 @@ void *load_device_tree_from_sysfs(void) host_fdt = create_device_tree(&host_fdt_size); read_fstree(host_fdt, SYSFS_DT_BASEDIR); if (fdt_check_header(host_fdt)) { - error_setg(&error_fatal, - "%s host device tree extracted into memory is invalid", - __func__); + error_report("%s host device tree extracted into memory is invalid", + __func__); + exit(1); } return host_fdt; } From f963cc26dfd7832b6371c312a817a1950bfe398a Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 29 Jun 2018 15:11:01 +0100 Subject: [PATCH 1770/2380] device_tree: Add qemu_fdt_node_unit_path This helper allows to retrieve the paths of nodes whose name match node-name or node-name@unit-address patterns. Signed-off-by: Eric Auger Message-id: 1530044492-24921-2-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- device_tree.c | 55 ++++++++++++++++++++++++++++++++++++ include/sysemu/device_tree.h | 16 +++++++++++ 2 files changed, 71 insertions(+) diff --git a/device_tree.c b/device_tree.c index 3553819257..6d9c9726f6 100644 --- a/device_tree.c +++ b/device_tree.c @@ -232,6 +232,61 @@ static int findnode_nofail(void *fdt, const char *node_path) return offset; } +char **qemu_fdt_node_unit_path(void *fdt, const char *name, Error **errp) +{ + char *prefix = g_strdup_printf("%s@", name); + unsigned int path_len = 16, n = 0; + GSList *path_list = NULL, *iter; + const char *iter_name; + int offset, len, ret; + char **path_array; + + offset = fdt_next_node(fdt, -1, NULL); + + while (offset >= 0) { + iter_name = fdt_get_name(fdt, offset, &len); + if (!iter_name) { + offset = len; + break; + } + if (!strcmp(iter_name, name) || g_str_has_prefix(iter_name, prefix)) { + char *path; + + path = g_malloc(path_len); + while ((ret = fdt_get_path(fdt, offset, path, path_len)) + == -FDT_ERR_NOSPACE) { + path_len += 16; + path = g_realloc(path, path_len); + } + path_list = g_slist_prepend(path_list, path); + n++; + } + offset = fdt_next_node(fdt, offset, NULL); + } + g_free(prefix); + + if (offset < 0 && offset != -FDT_ERR_NOTFOUND) { + error_setg(errp, "%s: abort parsing dt for %s node units: %s", + __func__, name, fdt_strerror(offset)); + for (iter = path_list; iter; iter = iter->next) { + g_free(iter->data); + } + g_slist_free(path_list); + return NULL; + } + + path_array = g_new(char *, n + 1); + path_array[n--] = NULL; + + for (iter = path_list; iter; iter = iter->next) { + path_array[n--] = iter->data; + } + + g_slist_free(path_list); + + return path_array; +} + char **qemu_fdt_node_path(void *fdt, const char *name, char *compat, Error **errp) { diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h index e22e5bec9c..c16fd69bc0 100644 --- a/include/sysemu/device_tree.h +++ b/include/sysemu/device_tree.h @@ -43,6 +43,22 @@ void *load_device_tree_from_sysfs(void); char **qemu_fdt_node_path(void *fdt, const char *name, char *compat, Error **errp); +/** + * qemu_fdt_node_unit_path: return the paths of nodes matching a given + * node-name, ie. node-name and node-name@unit-address + * @fdt: pointer to the dt blob + * @name: node name + * @errp: handle to an error object + * + * returns a newly allocated NULL-terminated array of node paths. + * Use g_strfreev() to free it. If one or more nodes were found, the + * array contains the path of each node and the last element equals to + * NULL. If there is no error but no matching node was found, the + * returned array contains a single element equal to NULL. If an error + * was encountered when parsing the blob, the function returns NULL + */ +char **qemu_fdt_node_unit_path(void *fdt, const char *name, Error **errp); + int qemu_fdt_setprop(void *fdt, const char *node_path, const char *property, const void *val, int size); int qemu_fdt_setprop_cell(void *fdt, const char *node_path, From bb2a33486fa33fc9e8bc557c54a485ed373845c9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 29 Jun 2018 15:11:01 +0100 Subject: [PATCH 1771/2380] hw/arm/virt: Silence dtc /intc warnings When running dtc on the guest /proc/device-tree we get the following warnings: "Warning (unit_address_vs_reg): Node has a reg or ranges property, but no unit name", with name: /intc, /intc/its, /intc/v2m. Nodes should have a name in the form [@] where unit-address is the primary address used to access the device, listed in the node's reg property. This fix seems to make dtc happy. Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Message-id: 1530044492-24921-3-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 63 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 742f68afca..6cce2828f7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -369,58 +369,72 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) static void fdt_add_its_gic_node(VirtMachineState *vms) { + char *nodename; + vms->msi_phandle = qemu_fdt_alloc_phandle(vms->fdt); - qemu_fdt_add_subnode(vms->fdt, "/intc/its"); - qemu_fdt_setprop_string(vms->fdt, "/intc/its", "compatible", + nodename = g_strdup_printf("/intc/its@%" PRIx64, + vms->memmap[VIRT_GIC_ITS].base); + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "arm,gic-v3-its"); - qemu_fdt_setprop(vms->fdt, "/intc/its", "msi-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc/its", "reg", + qemu_fdt_setprop(vms->fdt, nodename, "msi-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_ITS].base, 2, vms->memmap[VIRT_GIC_ITS].size); - qemu_fdt_setprop_cell(vms->fdt, "/intc/its", "phandle", vms->msi_phandle); + qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", vms->msi_phandle); + g_free(nodename); } static void fdt_add_v2m_gic_node(VirtMachineState *vms) { + char *nodename; + + nodename = g_strdup_printf("/intc/v2m@%" PRIx64, + vms->memmap[VIRT_GIC_V2M].base); vms->msi_phandle = qemu_fdt_alloc_phandle(vms->fdt); - qemu_fdt_add_subnode(vms->fdt, "/intc/v2m"); - qemu_fdt_setprop_string(vms->fdt, "/intc/v2m", "compatible", + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "arm,gic-v2m-frame"); - qemu_fdt_setprop(vms->fdt, "/intc/v2m", "msi-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc/v2m", "reg", + qemu_fdt_setprop(vms->fdt, nodename, "msi-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_V2M].base, 2, vms->memmap[VIRT_GIC_V2M].size); - qemu_fdt_setprop_cell(vms->fdt, "/intc/v2m", "phandle", vms->msi_phandle); + qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", vms->msi_phandle); + g_free(nodename); } static void fdt_add_gic_node(VirtMachineState *vms) { + char *nodename; + vms->gic_phandle = qemu_fdt_alloc_phandle(vms->fdt); qemu_fdt_setprop_cell(vms->fdt, "/", "interrupt-parent", vms->gic_phandle); - qemu_fdt_add_subnode(vms->fdt, "/intc"); - qemu_fdt_setprop_cell(vms->fdt, "/intc", "#interrupt-cells", 3); - qemu_fdt_setprop(vms->fdt, "/intc", "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(vms->fdt, "/intc", "#address-cells", 0x2); - qemu_fdt_setprop_cell(vms->fdt, "/intc", "#size-cells", 0x2); - qemu_fdt_setprop(vms->fdt, "/intc", "ranges", NULL, 0); + nodename = g_strdup_printf("/intc@%" PRIx64, + vms->memmap[VIRT_GIC_DIST].base); + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_cell(vms->fdt, nodename, "#interrupt-cells", 3); + qemu_fdt_setprop(vms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 0x2); + qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 0x2); + qemu_fdt_setprop(vms->fdt, nodename, "ranges", NULL, 0); if (vms->gic_version == 3) { int nb_redist_regions = virt_gicv3_redist_region_count(vms); - qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible", + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "arm,gic-v3"); - qemu_fdt_setprop_cell(vms->fdt, "/intc", + qemu_fdt_setprop_cell(vms->fdt, nodename, "#redistributor-regions", nb_redist_regions); if (nb_redist_regions == 1) { - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_DIST].base, 2, vms->memmap[VIRT_GIC_DIST].size, 2, vms->memmap[VIRT_GIC_REDIST].base, 2, vms->memmap[VIRT_GIC_REDIST].size); } else { - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_DIST].base, 2, vms->memmap[VIRT_GIC_DIST].size, 2, vms->memmap[VIRT_GIC_REDIST].base, @@ -430,22 +444,23 @@ static void fdt_add_gic_node(VirtMachineState *vms) } if (vms->virt) { - qemu_fdt_setprop_cells(vms->fdt, "/intc", "interrupts", + qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_PPI, ARCH_GICV3_MAINT_IRQ, GIC_FDT_IRQ_FLAGS_LEVEL_HI); } } else { /* 'cortex-a15-gic' means 'GIC v2' */ - qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible", + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "arm,cortex-a15-gic"); - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_DIST].base, 2, vms->memmap[VIRT_GIC_DIST].size, 2, vms->memmap[VIRT_GIC_CPU].base, 2, vms->memmap[VIRT_GIC_CPU].size); } - qemu_fdt_setprop_cell(vms->fdt, "/intc", "phandle", vms->gic_phandle); + qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", vms->gic_phandle); + g_free(nodename); } static void fdt_add_pmu_nodes(const VirtMachineState *vms) From e2eb3d29d711d510a51b5483fe818650325a7d3a Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 29 Jun 2018 15:11:01 +0100 Subject: [PATCH 1772/2380] hw/arm/virt: Silence dtc /memory warning When running dtc on the guest /proc/device-tree we get the following warning: Warning (unit_address_vs_reg): Node /memory has a reg or ranges property, but no unit name". Let's fix that by adding the unit address to the node name. We also don't create the /memory node anymore in create_fdt(). We directly create it in load_dtb. /chosen still needs to be created in create_fdt as the uart needs it. In case the user provided his own dtb, we nop all memory nodes found in root and create new one(s). Signed-off-by: Eric Auger Message-id: 1530044492-24921-4-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/boot.c | 41 +++++++++++++++++++++++------------------ hw/arm/virt.c | 7 +------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 1e481662ad..e09201cc97 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -490,11 +490,13 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, hwaddr addr_limit, AddressSpace *as) { void *fdt = NULL; - int size, rc; + int size, rc, n = 0; uint32_t acells, scells; char *nodename; unsigned int i; hwaddr mem_base, mem_len; + char **node_path; + Error *err = NULL; if (binfo->dtb_filename) { char *filename; @@ -546,12 +548,21 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, goto fail; } + /* nop all root nodes matching /memory or /memory@unit-address */ + node_path = qemu_fdt_node_unit_path(fdt, "memory", &err); + if (err) { + error_report_err(err); + goto fail; + } + while (node_path[n]) { + if (g_str_has_prefix(node_path[n], "/memory")) { + qemu_fdt_nop_node(fdt, node_path[n]); + } + n++; + } + g_strfreev(node_path); + if (nb_numa_nodes > 0) { - /* - * Turn the /memory node created before into a NOP node, then create - * /memory@addr nodes for all numa nodes respectively. - */ - qemu_fdt_nop_node(fdt, "/memory"); mem_base = binfo->loader_start; for (i = 0; i < nb_numa_nodes; i++) { mem_len = numa_info[i].node_mem; @@ -572,24 +583,18 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, g_free(nodename); } } else { - Error *err = NULL; + nodename = g_strdup_printf("/memory@%" PRIx64, binfo->loader_start); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); - rc = fdt_path_offset(fdt, "/memory"); - if (rc < 0) { - qemu_fdt_add_subnode(fdt, "/memory"); - } - - if (!qemu_fdt_getprop(fdt, "/memory", "device_type", NULL, &err)) { - qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); - } - - rc = qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg", + rc = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", acells, binfo->loader_start, scells, binfo->ram_size); if (rc < 0) { - fprintf(stderr, "couldn't set /memory/reg\n"); + fprintf(stderr, "couldn't set %s reg\n", nodename); goto fail; } + g_free(nodename); } rc = fdt_path_offset(fdt, "/chosen"); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 6cce2828f7..281ddcdf6e 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -204,13 +204,8 @@ static void create_fdt(VirtMachineState *vms) qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); - /* - * /chosen and /memory nodes must exist for load_dtb - * to fill in necessary properties later - */ + /* /chosen must exist for load_dtb to fill in necessary properties later */ qemu_fdt_add_subnode(fdt, "/chosen"); - qemu_fdt_add_subnode(fdt, "/memory"); - qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); /* Clock node, for the benefit of the UART. The kernel device tree * binding documentation claims the PL011 node clock properties are From c4e7c493da2fdd2c92a7958e592e38200af2f1ba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:02 +0100 Subject: [PATCH 1773/2380] target/arm: Implement SVE Memory Contiguous Load Group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 35 +++++++++ target/arm/sve.decode | 34 +++++++++ target/arm/sve_helper.c | 153 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 121 +++++++++++++++++++++++++++++ 4 files changed, 343 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 2e76084992..fcc9ba5f50 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -719,3 +719,38 @@ DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_ld1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1hsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 6f436f9096..cfb12da639 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -45,6 +45,9 @@ # Unsigned 8-bit immediate, optionally shifted left by 8. %sh8_i8u 5:9 !function=expand_imm_sh8u +# Unsigned load of msz into esz=2, represented as a dtype. +%msz_dtype 23:2 !function=msz_dtype + # Either a copy of rd (at bit 0), or a different source # as propagated via the MOVPRFX instruction. %reg_movprfx 0:5 @@ -71,6 +74,8 @@ &incdec2_cnt rd rn pat esz imm d u &incdec_pred rd pg esz d u &incdec2_pred rd rn pg esz d u +&rprr_load rd pg rn rm dtype nreg +&rpri_load rd pg rn imm dtype nreg ########################################################################### # Named instruction formats. These are generally used to @@ -170,6 +175,15 @@ @incdec2_pred ........ esz:2 .... .. ..... .. pg:4 rd:5 \ &incdec2_pred rn=%reg_movprfx +# Loads; user must fill in NREG. +@rprr_load_dt ....... dtype:4 rm:5 ... pg:3 rn:5 rd:5 &rprr_load +@rpri_load_dt ....... dtype:4 . imm:s4 ... pg:3 rn:5 rd:5 &rpri_load + +@rprr_load_msz ....... .... rm:5 ... pg:3 rn:5 rd:5 \ + &rprr_load dtype=%msz_dtype +@rpri_load_msz ....... .... . imm:s4 ... pg:3 rn:5 rd:5 \ + &rpri_load dtype=%msz_dtype + ########################################################################### # Instruction patterns. Grouped according to the SVE encodingindex.xhtml. @@ -665,3 +679,23 @@ LDR_pri 10000101 10 ...... 000 ... ..... 0 .... @pd_rn_i9 # SVE load vector register LDR_zri 10000101 10 ...... 010 ... ..... ..... @rd_rn_i9 + +### SVE Memory Contiguous Load Group + +# SVE contiguous load (scalar plus scalar) +LD_zprr 1010010 .... ..... 010 ... ..... ..... @rprr_load_dt nreg=0 + +# SVE contiguous load (scalar plus immediate) +LD_zpri 1010010 .... 0.... 101 ... ..... ..... @rpri_load_dt nreg=0 + +# SVE contiguous non-temporal load (scalar plus scalar) +# LDNT1B, LDNT1H, LDNT1W, LDNT1D +# SVE load multiple structures (scalar plus scalar) +# LD2B, LD2H, LD2W, LD2D; etc. +LD_zprr 1010010 .. nreg:2 ..... 110 ... ..... ..... @rprr_load_msz + +# SVE contiguous non-temporal load (scalar plus immediate) +# LDNT1B, LDNT1H, LDNT1W, LDNT1D +# SVE load multiple structures (scalar plus immediate) +# LD2B, LD2H, LD2W, LD2D; etc. +LD_zpri 1010010 .. nreg:2 0.... 111 ... ..... ..... @rpri_load_msz diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 128bbf9b04..4e6ad282f9 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2810,3 +2810,156 @@ uint32_t HELPER(sve_while)(void *vd, uint32_t count, uint32_t pred_desc) return predtest_ones(d, oprsz, esz_mask); } + +/* + * Load contiguous data, protected by a governing predicate. + */ +#define DO_LD1(NAME, FN, TYPEE, TYPEM, H) \ +static void do_##NAME(CPUARMState *env, void *vd, void *vg, \ + target_ulong addr, intptr_t oprsz, \ + uintptr_t ra) \ +{ \ + intptr_t i = 0; \ + do { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m = 0; \ + if (pg & 1) { \ + m = FN(env, addr, ra); \ + } \ + *(TYPEE *)(vd + H(i)) = m; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += sizeof(TYPEM); \ + } while (i & 15); \ + } while (i < oprsz); \ +} \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + do_##NAME(env, &env->vfp.zregs[simd_data(desc)], vg, \ + addr, simd_oprsz(desc), GETPC()); \ +} + +#define DO_LD2(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m1 = 0, m2 = 0; \ + if (pg & 1) { \ + m1 = FN(env, addr, ra); \ + m2 = FN(env, addr + sizeof(TYPEM), ra); \ + } \ + *(TYPEE *)(d1 + H(i)) = m1; \ + *(TYPEE *)(d2 + H(i)) = m2; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 2 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +#define DO_LD3(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m1 = 0, m2 = 0, m3 = 0; \ + if (pg & 1) { \ + m1 = FN(env, addr, ra); \ + m2 = FN(env, addr + sizeof(TYPEM), ra); \ + m3 = FN(env, addr + 2 * sizeof(TYPEM), ra); \ + } \ + *(TYPEE *)(d1 + H(i)) = m1; \ + *(TYPEE *)(d2 + H(i)) = m2; \ + *(TYPEE *)(d3 + H(i)) = m3; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 3 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +#define DO_LD4(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ + void *d4 = &env->vfp.zregs[(rd + 3) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m1 = 0, m2 = 0, m3 = 0, m4 = 0; \ + if (pg & 1) { \ + m1 = FN(env, addr, ra); \ + m2 = FN(env, addr + sizeof(TYPEM), ra); \ + m3 = FN(env, addr + 2 * sizeof(TYPEM), ra); \ + m4 = FN(env, addr + 3 * sizeof(TYPEM), ra); \ + } \ + *(TYPEE *)(d1 + H(i)) = m1; \ + *(TYPEE *)(d2 + H(i)) = m2; \ + *(TYPEE *)(d3 + H(i)) = m3; \ + *(TYPEE *)(d4 + H(i)) = m4; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 4 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +DO_LD1(sve_ld1bhu_r, cpu_ldub_data_ra, uint16_t, uint8_t, H1_2) +DO_LD1(sve_ld1bhs_r, cpu_ldsb_data_ra, uint16_t, int8_t, H1_2) +DO_LD1(sve_ld1bsu_r, cpu_ldub_data_ra, uint32_t, uint8_t, H1_4) +DO_LD1(sve_ld1bss_r, cpu_ldsb_data_ra, uint32_t, int8_t, H1_4) +DO_LD1(sve_ld1bdu_r, cpu_ldub_data_ra, uint64_t, uint8_t, ) +DO_LD1(sve_ld1bds_r, cpu_ldsb_data_ra, uint64_t, int8_t, ) + +DO_LD1(sve_ld1hsu_r, cpu_lduw_data_ra, uint32_t, uint16_t, H1_4) +DO_LD1(sve_ld1hss_r, cpu_ldsw_data_ra, uint32_t, int8_t, H1_4) +DO_LD1(sve_ld1hdu_r, cpu_lduw_data_ra, uint64_t, uint16_t, ) +DO_LD1(sve_ld1hds_r, cpu_ldsw_data_ra, uint64_t, int16_t, ) + +DO_LD1(sve_ld1sdu_r, cpu_ldl_data_ra, uint64_t, uint32_t, ) +DO_LD1(sve_ld1sds_r, cpu_ldl_data_ra, uint64_t, int32_t, ) + +DO_LD1(sve_ld1bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) +DO_LD2(sve_ld2bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) +DO_LD3(sve_ld3bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) +DO_LD4(sve_ld4bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) + +DO_LD1(sve_ld1hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) +DO_LD2(sve_ld2hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) +DO_LD3(sve_ld3hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) +DO_LD4(sve_ld4hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) + +DO_LD1(sve_ld1ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) +DO_LD2(sve_ld2ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) +DO_LD3(sve_ld3ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) +DO_LD4(sve_ld4ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) + +DO_LD1(sve_ld1dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) +DO_LD2(sve_ld2dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) +DO_LD3(sve_ld3dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) +DO_LD4(sve_ld4dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) + +#undef DO_LD1 +#undef DO_LD2 +#undef DO_LD3 +#undef DO_LD4 diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 226c97579c..3543daff48 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -42,6 +42,8 @@ typedef void gen_helper_gvec_flags_3(TCGv_i32, TCGv_ptr, TCGv_ptr, typedef void gen_helper_gvec_flags_4(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); +typedef void gen_helper_gvec_mem(TCGv_env, TCGv_ptr, TCGv_i64, TCGv_i32); + /* * Helpers for extracting complex instruction fields. */ @@ -82,6 +84,15 @@ static inline int expand_imm_sh8u(int x) return (uint8_t)x << (x & 0x100 ? 8 : 0); } +/* Convert a 2-bit memory size (msz) to a 4-bit data type (dtype) + * with unsigned data. C.f. SVE Memory Contiguous Load Group. + */ +static inline int msz_dtype(int msz) +{ + static const uint8_t dtype[4] = { 0, 5, 10, 15 }; + return dtype[msz]; +} + /* * Include the generated decoder. */ @@ -3526,3 +3537,113 @@ static bool trans_LDR_pri(DisasContext *s, arg_rri *a, uint32_t insn) } return true; } + +/* + *** SVE Memory - Contiguous Load Group + */ + +/* The memory mode of the dtype. */ +static const TCGMemOp dtype_mop[16] = { + MO_UB, MO_UB, MO_UB, MO_UB, + MO_SL, MO_UW, MO_UW, MO_UW, + MO_SW, MO_SW, MO_UL, MO_UL, + MO_SB, MO_SB, MO_SB, MO_Q +}; + +#define dtype_msz(x) (dtype_mop[x] & MO_SIZE) + +/* The vector element size of dtype. */ +static const uint8_t dtype_esz[16] = { + 0, 1, 2, 3, + 3, 1, 2, 3, + 3, 2, 2, 3, + 3, 2, 1, 3 +}; + +static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, + gen_helper_gvec_mem *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_pg; + TCGv_i32 desc; + + /* For e.g. LD4, there are not enough arguments to pass all 4 + * registers as pointers, so encode the regno into the data field. + * For consistency, do this even for LD1. + */ + desc = tcg_const_i32(simd_desc(vsz, vsz, zt)); + t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + fn(cpu_env, t_pg, addr, desc); + + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(desc); +} + +static void do_ld_zpa(DisasContext *s, int zt, int pg, + TCGv_i64 addr, int dtype, int nreg) +{ + static gen_helper_gvec_mem * const fns[16][4] = { + { gen_helper_sve_ld1bb_r, gen_helper_sve_ld2bb_r, + gen_helper_sve_ld3bb_r, gen_helper_sve_ld4bb_r }, + { gen_helper_sve_ld1bhu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bsu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bdu_r, NULL, NULL, NULL }, + + { gen_helper_sve_ld1sds_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hh_r, gen_helper_sve_ld2hh_r, + gen_helper_sve_ld3hh_r, gen_helper_sve_ld4hh_r }, + { gen_helper_sve_ld1hsu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hdu_r, NULL, NULL, NULL }, + + { gen_helper_sve_ld1hds_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hss_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1ss_r, gen_helper_sve_ld2ss_r, + gen_helper_sve_ld3ss_r, gen_helper_sve_ld4ss_r }, + { gen_helper_sve_ld1sdu_r, NULL, NULL, NULL }, + + { gen_helper_sve_ld1bds_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bss_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bhs_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1dd_r, gen_helper_sve_ld2dd_r, + gen_helper_sve_ld3dd_r, gen_helper_sve_ld4dd_r }, + }; + gen_helper_gvec_mem *fn = fns[dtype][nreg]; + + /* While there are holes in the table, they are not + * accessible via the instruction encoding. + */ + assert(fn != NULL); + do_mem_zpa(s, zt, pg, addr, fn); +} + +static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a, uint32_t insn) +{ + if (a->rm == 31) { + return false; + } + if (sve_access_check(s)) { + TCGv_i64 addr = new_tmp_a64(s); + tcg_gen_muli_i64(addr, cpu_reg(s, a->rm), + (a->nreg + 1) << dtype_msz(a->dtype)); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + do_ld_zpa(s, a->rd, a->pg, addr, a->dtype, a->nreg); + } + return true; +} + +static bool trans_LD_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int vsz = vec_full_reg_size(s); + int elements = vsz >> dtype_esz[a->dtype]; + TCGv_i64 addr = new_tmp_a64(s); + + tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), + (a->imm * elements * (a->nreg + 1)) + << dtype_msz(a->dtype)); + do_ld_zpa(s, a->rd, a->pg, addr, a->dtype, a->nreg); + } + return true; +} From e2654d757598d6c06d1ceb25c62ddf7d63cac32f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:02 +0100 Subject: [PATCH 1774/2380] target/arm: Implement SVE Contiguous Load, first-fault and no-fault MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 20180627043328.11531-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 40 ++++++++++ target/arm/sve.decode | 6 ++ target/arm/sve_helper.c | 157 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 69 ++++++++++++++++ 4 files changed, 272 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index fcc9ba5f50..7338abbbcf 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -754,3 +754,43 @@ DEF_HELPER_FLAGS_4(sve_ld1hds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldff1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldff1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldff1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldff1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldnf1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldnf1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldnf1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldnf1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index cfb12da639..afbed57de1 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -685,9 +685,15 @@ LDR_zri 10000101 10 ...... 010 ... ..... ..... @rd_rn_i9 # SVE contiguous load (scalar plus scalar) LD_zprr 1010010 .... ..... 010 ... ..... ..... @rprr_load_dt nreg=0 +# SVE contiguous first-fault load (scalar plus scalar) +LDFF1_zprr 1010010 .... ..... 011 ... ..... ..... @rprr_load_dt nreg=0 + # SVE contiguous load (scalar plus immediate) LD_zpri 1010010 .... 0.... 101 ... ..... ..... @rpri_load_dt nreg=0 +# SVE contiguous non-fault load (scalar plus immediate) +LDNF1_zpri 1010010 .... 1.... 101 ... ..... ..... @rpri_load_dt nreg=0 + # SVE contiguous non-temporal load (scalar plus scalar) # LDNT1B, LDNT1H, LDNT1W, LDNT1D # SVE load multiple structures (scalar plus scalar) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 4e6ad282f9..0d22a57a22 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2963,3 +2963,160 @@ DO_LD4(sve_ld4dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) #undef DO_LD2 #undef DO_LD3 #undef DO_LD4 + +/* + * Load contiguous data, first-fault and no-fault. + */ + +#ifdef CONFIG_USER_ONLY + +/* Fault on byte I. All bits in FFR from I are cleared. The vector + * result from I is CONSTRAINED UNPREDICTABLE; we choose the MERGE + * option, which leaves subsequent data unchanged. + */ +static void record_fault(CPUARMState *env, uintptr_t i, uintptr_t oprsz) +{ + uint64_t *ffr = env->vfp.pregs[FFR_PRED_NUM].p; + + if (i & 63) { + ffr[i / 64] &= MAKE_64BIT_MASK(0, i & 63); + i = ROUND_UP(i, 64); + } + for (; i < oprsz; i += 64) { + ffr[i / 64] = 0; + } +} + +/* Hold the mmap lock during the operation so that there is no race + * between page_check_range and the load operation. We expect the + * usual case to have no faults at all, so we check the whole range + * first and if successful defer to the normal load operation. + * + * TODO: Change mmap_lock to a rwlock so that multiple readers + * can run simultaneously. This will probably help other uses + * within QEMU as well. + */ +#define DO_LDFF1(PART, FN, TYPEE, TYPEM, H) \ +static void do_sve_ldff1##PART(CPUARMState *env, void *vd, void *vg, \ + target_ulong addr, intptr_t oprsz, \ + bool first, uintptr_t ra) \ +{ \ + intptr_t i = 0; \ + do { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m = 0; \ + if (pg & 1) { \ + if (!first && \ + unlikely(page_check_range(addr, sizeof(TYPEM), \ + PAGE_READ))) { \ + record_fault(env, i, oprsz); \ + return; \ + } \ + m = FN(env, addr, ra); \ + first = false; \ + } \ + *(TYPEE *)(vd + H(i)) = m; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += sizeof(TYPEM); \ + } while (i & 15); \ + } while (i < oprsz); \ +} \ +void HELPER(sve_ldff1##PART)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + unsigned rd = simd_data(desc); \ + void *vd = &env->vfp.zregs[rd]; \ + mmap_lock(); \ + if (likely(page_check_range(addr, oprsz, PAGE_READ) == 0)) { \ + do_sve_ld1##PART(env, vd, vg, addr, oprsz, GETPC()); \ + } else { \ + do_sve_ldff1##PART(env, vd, vg, addr, oprsz, true, GETPC()); \ + } \ + mmap_unlock(); \ +} + +/* No-fault loads are like first-fault loads without the + * first faulting special case. + */ +#define DO_LDNF1(PART) \ +void HELPER(sve_ldnf1##PART)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + unsigned rd = simd_data(desc); \ + void *vd = &env->vfp.zregs[rd]; \ + mmap_lock(); \ + if (likely(page_check_range(addr, oprsz, PAGE_READ) == 0)) { \ + do_sve_ld1##PART(env, vd, vg, addr, oprsz, GETPC()); \ + } else { \ + do_sve_ldff1##PART(env, vd, vg, addr, oprsz, false, GETPC()); \ + } \ + mmap_unlock(); \ +} + +#else + +/* TODO: System mode is not yet supported. + * This would probably use tlb_vaddr_to_host. + */ +#define DO_LDFF1(PART, FN, TYPEE, TYPEM, H) \ +void HELPER(sve_ldff1##PART)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + g_assert_not_reached(); \ +} + +#define DO_LDNF1(PART) \ +void HELPER(sve_ldnf1##PART)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + g_assert_not_reached(); \ +} + +#endif + +DO_LDFF1(bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) +DO_LDFF1(bhu_r, cpu_ldub_data_ra, uint16_t, uint8_t, H1_2) +DO_LDFF1(bhs_r, cpu_ldsb_data_ra, uint16_t, int8_t, H1_2) +DO_LDFF1(bsu_r, cpu_ldub_data_ra, uint32_t, uint8_t, H1_4) +DO_LDFF1(bss_r, cpu_ldsb_data_ra, uint32_t, int8_t, H1_4) +DO_LDFF1(bdu_r, cpu_ldub_data_ra, uint64_t, uint8_t, ) +DO_LDFF1(bds_r, cpu_ldsb_data_ra, uint64_t, int8_t, ) + +DO_LDFF1(hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) +DO_LDFF1(hsu_r, cpu_lduw_data_ra, uint32_t, uint16_t, H1_4) +DO_LDFF1(hss_r, cpu_ldsw_data_ra, uint32_t, int8_t, H1_4) +DO_LDFF1(hdu_r, cpu_lduw_data_ra, uint64_t, uint16_t, ) +DO_LDFF1(hds_r, cpu_ldsw_data_ra, uint64_t, int16_t, ) + +DO_LDFF1(ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) +DO_LDFF1(sdu_r, cpu_ldl_data_ra, uint64_t, uint32_t, ) +DO_LDFF1(sds_r, cpu_ldl_data_ra, uint64_t, int32_t, ) + +DO_LDFF1(dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) + +#undef DO_LDFF1 + +DO_LDNF1(bb_r) +DO_LDNF1(bhu_r) +DO_LDNF1(bhs_r) +DO_LDNF1(bsu_r) +DO_LDNF1(bss_r) +DO_LDNF1(bdu_r) +DO_LDNF1(bds_r) + +DO_LDNF1(hh_r) +DO_LDNF1(hsu_r) +DO_LDNF1(hss_r) +DO_LDNF1(hdu_r) +DO_LDNF1(hds_r) + +DO_LDNF1(ss_r) +DO_LDNF1(sdu_r) +DO_LDNF1(sds_r) + +DO_LDNF1(dd_r) + +#undef DO_LDNF1 diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 3543daff48..09f77b5405 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3647,3 +3647,72 @@ static bool trans_LD_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) } return true; } + +static bool trans_LDFF1_zprr(DisasContext *s, arg_rprr_load *a, uint32_t insn) +{ + static gen_helper_gvec_mem * const fns[16] = { + gen_helper_sve_ldff1bb_r, + gen_helper_sve_ldff1bhu_r, + gen_helper_sve_ldff1bsu_r, + gen_helper_sve_ldff1bdu_r, + + gen_helper_sve_ldff1sds_r, + gen_helper_sve_ldff1hh_r, + gen_helper_sve_ldff1hsu_r, + gen_helper_sve_ldff1hdu_r, + + gen_helper_sve_ldff1hds_r, + gen_helper_sve_ldff1hss_r, + gen_helper_sve_ldff1ss_r, + gen_helper_sve_ldff1sdu_r, + + gen_helper_sve_ldff1bds_r, + gen_helper_sve_ldff1bss_r, + gen_helper_sve_ldff1bhs_r, + gen_helper_sve_ldff1dd_r, + }; + + if (sve_access_check(s)) { + TCGv_i64 addr = new_tmp_a64(s); + tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), dtype_msz(a->dtype)); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + do_mem_zpa(s, a->rd, a->pg, addr, fns[a->dtype]); + } + return true; +} + +static bool trans_LDNF1_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +{ + static gen_helper_gvec_mem * const fns[16] = { + gen_helper_sve_ldnf1bb_r, + gen_helper_sve_ldnf1bhu_r, + gen_helper_sve_ldnf1bsu_r, + gen_helper_sve_ldnf1bdu_r, + + gen_helper_sve_ldnf1sds_r, + gen_helper_sve_ldnf1hh_r, + gen_helper_sve_ldnf1hsu_r, + gen_helper_sve_ldnf1hdu_r, + + gen_helper_sve_ldnf1hds_r, + gen_helper_sve_ldnf1hss_r, + gen_helper_sve_ldnf1ss_r, + gen_helper_sve_ldnf1sdu_r, + + gen_helper_sve_ldnf1bds_r, + gen_helper_sve_ldnf1bss_r, + gen_helper_sve_ldnf1bhs_r, + gen_helper_sve_ldnf1dd_r, + }; + + if (sve_access_check(s)) { + int vsz = vec_full_reg_size(s); + int elements = vsz >> dtype_esz[a->dtype]; + int off = (a->imm * elements) << dtype_msz(a->dtype); + TCGv_i64 addr = new_tmp_a64(s); + + tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), off); + do_mem_zpa(s, a->rd, a->pg, addr, fns[a->dtype]); + } + return true; +} From 1a039c7e58533d5f9431a2064771113d5961c964 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:03 +0100 Subject: [PATCH 1775/2380] target/arm: Implement SVE Memory Contiguous Store Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 29 +++++ target/arm/sve.decode | 38 +++++++ target/arm/sve_helper.c | 211 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 65 ++++++++++++ 4 files changed, 343 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 7338abbbcf..b768128951 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -794,3 +794,32 @@ DEF_HELPER_FLAGS_4(sve_ldnf1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldnf1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldnf1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1bh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1bs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1bd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1hs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1hd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1sd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index afbed57de1..6e159faaec 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -27,6 +27,7 @@ %imm7_22_16 22:2 16:5 %imm8_16_10 16:5 10:3 %imm9_16_10 16:s6 10:3 +%size_23 23:2 # A combination of tsz:imm3 -- extract esize. %tszimm_esz 22:2 5:5 !function=tszimm_esz @@ -76,6 +77,8 @@ &incdec2_pred rd rn pg esz d u &rprr_load rd pg rn rm dtype nreg &rpri_load rd pg rn imm dtype nreg +&rprr_store rd pg rn rm msz esz nreg +&rpri_store rd pg rn imm msz esz nreg ########################################################################### # Named instruction formats. These are generally used to @@ -184,6 +187,12 @@ @rpri_load_msz ....... .... . imm:s4 ... pg:3 rn:5 rd:5 \ &rpri_load dtype=%msz_dtype +# Stores; user must fill in ESZ, MSZ, NREG as needed. +@rprr_store ....... .. .. rm:5 ... pg:3 rn:5 rd:5 &rprr_store +@rpri_store_msz ....... msz:2 .. . imm:s4 ... pg:3 rn:5 rd:5 &rpri_store +@rprr_store_esz_n0 ....... .. esz:2 rm:5 ... pg:3 rn:5 rd:5 \ + &rprr_store nreg=0 + ########################################################################### # Instruction patterns. Grouped according to the SVE encodingindex.xhtml. @@ -705,3 +714,32 @@ LD_zprr 1010010 .. nreg:2 ..... 110 ... ..... ..... @rprr_load_msz # SVE load multiple structures (scalar plus immediate) # LD2B, LD2H, LD2W, LD2D; etc. LD_zpri 1010010 .. nreg:2 0.... 111 ... ..... ..... @rpri_load_msz + +### SVE Memory Store Group + +# SVE contiguous store (scalar plus immediate) +# ST1B, ST1H, ST1W, ST1D; require msz <= esz +ST_zpri 1110010 .. esz:2 0.... 111 ... ..... ..... \ + @rpri_store_msz nreg=0 + +# SVE contiguous store (scalar plus scalar) +# ST1B, ST1H, ST1W, ST1D; require msz <= esz +# Enumerate msz lest we conflict with STR_zri. +ST_zprr 1110010 00 .. ..... 010 ... ..... ..... \ + @rprr_store_esz_n0 msz=0 +ST_zprr 1110010 01 .. ..... 010 ... ..... ..... \ + @rprr_store_esz_n0 msz=1 +ST_zprr 1110010 10 .. ..... 010 ... ..... ..... \ + @rprr_store_esz_n0 msz=2 +ST_zprr 1110010 11 11 ..... 010 ... ..... ..... \ + @rprr_store msz=3 esz=3 nreg=0 + +# SVE contiguous non-temporal store (scalar plus immediate) (nreg == 0) +# SVE store multiple structures (scalar plus immediate) (nreg != 0) +ST_zpri 1110010 .. nreg:2 1.... 111 ... ..... ..... \ + @rpri_store_msz esz=%size_23 + +# SVE contiguous non-temporal store (scalar plus scalar) (nreg == 0) +# SVE store multiple structures (scalar plus scalar) (nreg != 0) +ST_zprr 1110010 msz:2 nreg:2 ..... 011 ... ..... ..... \ + @rprr_store esz=%size_23 diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 0d22a57a22..bd874e6fa2 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3120,3 +3120,214 @@ DO_LDNF1(sds_r) DO_LDNF1(dd_r) #undef DO_LDNF1 + +/* + * Store contiguous data, protected by a governing predicate. + */ +#define DO_ST1(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *vd = &env->vfp.zregs[rd]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPEM m = *(TYPEE *)(vd + H(i)); \ + FN(env, addr, m, ra); \ + } \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +#define DO_ST1_D(NAME, FN, TYPEM) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc) / 8; \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + uint64_t *d = &env->vfp.zregs[rd].d[0]; \ + uint8_t *pg = vg; \ + for (i = 0; i < oprsz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + FN(env, addr, d[i], ra); \ + } \ + addr += sizeof(TYPEM); \ + } \ +} + +#define DO_ST2(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPEM m1 = *(TYPEE *)(d1 + H(i)); \ + TYPEM m2 = *(TYPEE *)(d2 + H(i)); \ + FN(env, addr, m1, ra); \ + FN(env, addr + sizeof(TYPEM), m2, ra); \ + } \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 2 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +#define DO_ST3(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPEM m1 = *(TYPEE *)(d1 + H(i)); \ + TYPEM m2 = *(TYPEE *)(d2 + H(i)); \ + TYPEM m3 = *(TYPEE *)(d3 + H(i)); \ + FN(env, addr, m1, ra); \ + FN(env, addr + sizeof(TYPEM), m2, ra); \ + FN(env, addr + 2 * sizeof(TYPEM), m3, ra); \ + } \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 3 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +#define DO_ST4(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ + void *d4 = &env->vfp.zregs[(rd + 3) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPEM m1 = *(TYPEE *)(d1 + H(i)); \ + TYPEM m2 = *(TYPEE *)(d2 + H(i)); \ + TYPEM m3 = *(TYPEE *)(d3 + H(i)); \ + TYPEM m4 = *(TYPEE *)(d4 + H(i)); \ + FN(env, addr, m1, ra); \ + FN(env, addr + sizeof(TYPEM), m2, ra); \ + FN(env, addr + 2 * sizeof(TYPEM), m3, ra); \ + FN(env, addr + 3 * sizeof(TYPEM), m4, ra); \ + } \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 4 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +DO_ST1(sve_st1bh_r, cpu_stb_data_ra, uint16_t, uint8_t, H1_2) +DO_ST1(sve_st1bs_r, cpu_stb_data_ra, uint32_t, uint8_t, H1_4) +DO_ST1_D(sve_st1bd_r, cpu_stb_data_ra, uint8_t) + +DO_ST1(sve_st1hs_r, cpu_stw_data_ra, uint32_t, uint16_t, H1_4) +DO_ST1_D(sve_st1hd_r, cpu_stw_data_ra, uint16_t) + +DO_ST1_D(sve_st1sd_r, cpu_stl_data_ra, uint32_t) + +DO_ST1(sve_st1bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) +DO_ST2(sve_st2bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) +DO_ST3(sve_st3bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) +DO_ST4(sve_st4bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) + +DO_ST1(sve_st1hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) +DO_ST2(sve_st2hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) +DO_ST3(sve_st3hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) +DO_ST4(sve_st4hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) + +DO_ST1(sve_st1ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) +DO_ST2(sve_st2ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) +DO_ST3(sve_st3ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) +DO_ST4(sve_st4ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) + +DO_ST1_D(sve_st1dd_r, cpu_stq_data_ra, uint64_t) + +void HELPER(sve_st2dd_r)(CPUARMState *env, void *vg, + target_ulong addr, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc) / 8; + intptr_t ra = GETPC(); + unsigned rd = simd_data(desc); + uint64_t *d1 = &env->vfp.zregs[rd].d[0]; + uint64_t *d2 = &env->vfp.zregs[(rd + 1) & 31].d[0]; + uint8_t *pg = vg; + + for (i = 0; i < oprsz; i += 1) { + if (pg[H1(i)] & 1) { + cpu_stq_data_ra(env, addr, d1[i], ra); + cpu_stq_data_ra(env, addr + 8, d2[i], ra); + } + addr += 2 * 8; + } +} + +void HELPER(sve_st3dd_r)(CPUARMState *env, void *vg, + target_ulong addr, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc) / 8; + intptr_t ra = GETPC(); + unsigned rd = simd_data(desc); + uint64_t *d1 = &env->vfp.zregs[rd].d[0]; + uint64_t *d2 = &env->vfp.zregs[(rd + 1) & 31].d[0]; + uint64_t *d3 = &env->vfp.zregs[(rd + 2) & 31].d[0]; + uint8_t *pg = vg; + + for (i = 0; i < oprsz; i += 1) { + if (pg[H1(i)] & 1) { + cpu_stq_data_ra(env, addr, d1[i], ra); + cpu_stq_data_ra(env, addr + 8, d2[i], ra); + cpu_stq_data_ra(env, addr + 16, d3[i], ra); + } + addr += 3 * 8; + } +} + +void HELPER(sve_st4dd_r)(CPUARMState *env, void *vg, + target_ulong addr, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc) / 8; + intptr_t ra = GETPC(); + unsigned rd = simd_data(desc); + uint64_t *d1 = &env->vfp.zregs[rd].d[0]; + uint64_t *d2 = &env->vfp.zregs[(rd + 1) & 31].d[0]; + uint64_t *d3 = &env->vfp.zregs[(rd + 2) & 31].d[0]; + uint64_t *d4 = &env->vfp.zregs[(rd + 3) & 31].d[0]; + uint8_t *pg = vg; + + for (i = 0; i < oprsz; i += 1) { + if (pg[H1(i)] & 1) { + cpu_stq_data_ra(env, addr, d1[i], ra); + cpu_stq_data_ra(env, addr + 8, d2[i], ra); + cpu_stq_data_ra(env, addr + 16, d3[i], ra); + cpu_stq_data_ra(env, addr + 24, d4[i], ra); + } + addr += 4 * 8; + } +} diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 09f77b5405..b25fe96b77 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3716,3 +3716,68 @@ static bool trans_LDNF1_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) } return true; } + +static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, + int msz, int esz, int nreg) +{ + static gen_helper_gvec_mem * const fn_single[4][4] = { + { gen_helper_sve_st1bb_r, gen_helper_sve_st1bh_r, + gen_helper_sve_st1bs_r, gen_helper_sve_st1bd_r }, + { NULL, gen_helper_sve_st1hh_r, + gen_helper_sve_st1hs_r, gen_helper_sve_st1hd_r }, + { NULL, NULL, + gen_helper_sve_st1ss_r, gen_helper_sve_st1sd_r }, + { NULL, NULL, NULL, gen_helper_sve_st1dd_r }, + }; + static gen_helper_gvec_mem * const fn_multiple[3][4] = { + { gen_helper_sve_st2bb_r, gen_helper_sve_st2hh_r, + gen_helper_sve_st2ss_r, gen_helper_sve_st2dd_r }, + { gen_helper_sve_st3bb_r, gen_helper_sve_st3hh_r, + gen_helper_sve_st3ss_r, gen_helper_sve_st3dd_r }, + { gen_helper_sve_st4bb_r, gen_helper_sve_st4hh_r, + gen_helper_sve_st4ss_r, gen_helper_sve_st4dd_r }, + }; + gen_helper_gvec_mem *fn; + + if (nreg == 0) { + /* ST1 */ + fn = fn_single[msz][esz]; + } else { + /* ST2, ST3, ST4 -- msz == esz, enforced by encoding */ + assert(msz == esz); + fn = fn_multiple[nreg - 1][msz]; + } + assert(fn != NULL); + do_mem_zpa(s, zt, pg, addr, fn); +} + +static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a, uint32_t insn) +{ + if (a->rm == 31 || a->msz > a->esz) { + return false; + } + if (sve_access_check(s)) { + TCGv_i64 addr = new_tmp_a64(s); + tcg_gen_muli_i64(addr, cpu_reg(s, a->rm), (a->nreg + 1) << a->msz); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + do_st_zpa(s, a->rd, a->pg, addr, a->msz, a->esz, a->nreg); + } + return true; +} + +static bool trans_ST_zpri(DisasContext *s, arg_rpri_store *a, uint32_t insn) +{ + if (a->msz > a->esz) { + return false; + } + if (sve_access_check(s)) { + int vsz = vec_full_reg_size(s); + int elements = vsz >> a->esz; + TCGv_i64 addr = new_tmp_a64(s); + + tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), + (a->imm * elements * (a->nreg + 1)) << a->msz); + do_st_zpa(s, a->rd, a->pg, addr, a->msz, a->esz, a->nreg); + } + return true; +} From 05abe304be2987cb3576729a14dab96e9ccfaec9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:03 +0100 Subject: [PATCH 1776/2380] target/arm: Implement SVE load and broadcast quadword Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 9 +++++++ target/arm/translate-sve.c | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 6e159faaec..606c4f623c 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -715,6 +715,15 @@ LD_zprr 1010010 .. nreg:2 ..... 110 ... ..... ..... @rprr_load_msz # LD2B, LD2H, LD2W, LD2D; etc. LD_zpri 1010010 .. nreg:2 0.... 111 ... ..... ..... @rpri_load_msz +# SVE load and broadcast quadword (scalar plus scalar) +LD1RQ_zprr 1010010 .. 00 ..... 000 ... ..... ..... \ + @rprr_load_msz nreg=0 + +# SVE load and broadcast quadword (scalar plus immediate) +# LD1RQB, LD1RQH, LD1RQS, LD1RQD +LD1RQ_zpri 1010010 .. 00 0.... 001 ... ..... ..... \ + @rpri_load_msz nreg=0 + ### SVE Memory Store Group # SVE contiguous store (scalar plus immediate) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index b25fe96b77..83de87ee0e 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3717,6 +3717,58 @@ static bool trans_LDNF1_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) return true; } +static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int msz) +{ + static gen_helper_gvec_mem * const fns[4] = { + gen_helper_sve_ld1bb_r, gen_helper_sve_ld1hh_r, + gen_helper_sve_ld1ss_r, gen_helper_sve_ld1dd_r, + }; + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_pg; + TCGv_i32 desc; + + /* Load the first quadword using the normal predicated load helpers. */ + desc = tcg_const_i32(simd_desc(16, 16, zt)); + t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + fns[msz](cpu_env, t_pg, addr, desc); + + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(desc); + + /* Replicate that first quadword. */ + if (vsz > 16) { + unsigned dofs = vec_full_reg_offset(s, zt); + tcg_gen_gvec_dup_mem(4, dofs + 16, dofs, vsz - 16, vsz - 16); + } +} + +static bool trans_LD1RQ_zprr(DisasContext *s, arg_rprr_load *a, uint32_t insn) +{ + if (a->rm == 31) { + return false; + } + if (sve_access_check(s)) { + int msz = dtype_msz(a->dtype); + TCGv_i64 addr = new_tmp_a64(s); + tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), msz); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + do_ldrq(s, a->rd, a->pg, addr, msz); + } + return true; +} + +static bool trans_LD1RQ_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 addr = new_tmp_a64(s); + tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), a->imm * 16); + do_ldrq(s, a->rd, a->pg, addr, dtype_msz(a->dtype)); + } + return true; +} + static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, int msz, int esz, int nreg) { From 8092c6a314c9625d8976fb6c5b6969f2a1105cd8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:03 +0100 Subject: [PATCH 1777/2380] target/arm: Implement SVE integer convert to floating-point Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson [PMM: fixed typo] Message-id: 20180627043328.11531-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 30 +++++++++++++ target/arm/sve.decode | 22 ++++++++++ target/arm/sve_helper.c | 38 ++++++++++++++++ target/arm/translate-sve.c | 90 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index b768128951..185112e1d2 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -720,6 +720,36 @@ DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_hh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_sh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_dh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_ss, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_sd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_ds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_dd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_ucvt_hh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_sh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_dh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_ss, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_sd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_ds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_dd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_ld1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 606c4f623c..3abdb87cf5 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -133,6 +133,9 @@ @rd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 &rpr_esz @rd_pg4_pn ........ esz:2 ... ... .. pg:4 . rn:4 rd:5 &rpr_esz +# One register operand, with governing predicate, no vector element size +@rd_pg_rn_e0 ........ .. ... ... ... pg:3 rn:5 rd:5 &rpr_esz esz=0 + # Two register operands with a 6-bit signed immediate. @rd_rn_i6 ........ ... rn:5 ..... imm:s6 rd:5 &rri @@ -681,6 +684,25 @@ FTSMUL 01100101 .. 0 ..... 000 011 ..... ..... @rd_rn_rm FRECPS 01100101 .. 0 ..... 000 110 ..... ..... @rd_rn_rm FRSQRTS 01100101 .. 0 ..... 000 111 ..... ..... @rd_rn_rm +### SVE FP Unary Operations Predicated Group + +# SVE integer convert to floating-point +SCVTF_hh 01100101 01 010 01 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_sh 01100101 01 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_dh 01100101 01 010 11 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_ss 01100101 10 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_sd 01100101 11 010 00 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_ds 01100101 11 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_dd 01100101 11 010 11 0 101 ... ..... ..... @rd_pg_rn_e0 + +UCVTF_hh 01100101 01 010 01 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_sh 01100101 01 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_dh 01100101 01 010 11 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_ss 01100101 10 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_sd 01100101 11 010 00 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_ds 01100101 11 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_dd 01100101 11 010 11 1 101 ... ..... ..... @rd_pg_rn_e0 + ### SVE Memory - 32-bit Gather and Unsized Contiguous Group # SVE load predicate register diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index bd874e6fa2..031bec22df 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2811,6 +2811,44 @@ uint32_t HELPER(sve_while)(void *vd, uint32_t count, uint32_t pred_desc) return predtest_ones(d, oprsz, esz_mask); } +/* Fully general two-operand expander, controlled by a predicate, + * With the extra float_status parameter. + */ +#define DO_ZPZ_FP(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, void *status, uint32_t desc) \ +{ \ + intptr_t i = simd_oprsz(desc); \ + uint64_t *g = vg; \ + do { \ + uint64_t pg = g[(i - 1) >> 6]; \ + do { \ + i -= sizeof(TYPE); \ + if (likely((pg >> (i & 63)) & 1)) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, status); \ + } \ + } while (i & 63); \ + } while (i != 0); \ +} + +DO_ZPZ_FP(sve_scvt_hh, uint16_t, H1_2, int16_to_float16) +DO_ZPZ_FP(sve_scvt_sh, uint32_t, H1_4, int32_to_float16) +DO_ZPZ_FP(sve_scvt_ss, uint32_t, H1_4, int32_to_float32) +DO_ZPZ_FP(sve_scvt_sd, uint64_t, , int32_to_float64) +DO_ZPZ_FP(sve_scvt_dh, uint64_t, , int64_to_float16) +DO_ZPZ_FP(sve_scvt_ds, uint64_t, , int64_to_float32) +DO_ZPZ_FP(sve_scvt_dd, uint64_t, , int64_to_float64) + +DO_ZPZ_FP(sve_ucvt_hh, uint16_t, H1_2, uint16_to_float16) +DO_ZPZ_FP(sve_ucvt_sh, uint32_t, H1_4, uint32_to_float16) +DO_ZPZ_FP(sve_ucvt_ss, uint32_t, H1_4, uint32_to_float32) +DO_ZPZ_FP(sve_ucvt_sd, uint64_t, , uint32_to_float64) +DO_ZPZ_FP(sve_ucvt_dh, uint64_t, , uint64_to_float16) +DO_ZPZ_FP(sve_ucvt_ds, uint64_t, , uint64_to_float32) +DO_ZPZ_FP(sve_ucvt_dd, uint64_t, , uint64_to_float64) + +#undef DO_ZPZ_FP + /* * Load contiguous data, protected by a governing predicate. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 83de87ee0e..5cb13393ba 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3425,6 +3425,96 @@ DO_FP3(FRSQRTS, rsqrts) #undef DO_FP3 + +/* + *** SVE Floating Point Unary Operations Predicated Group + */ + +static bool do_zpz_ptr(DisasContext *s, int rd, int rn, int pg, + bool is_fp16, gen_helper_gvec_3_ptr *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(is_fp16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + pred_full_reg_offset(s, pg), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); + } + return true; +} + +static bool trans_SCVTF_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_hh); +} + +static bool trans_SCVTF_sh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_sh); +} + +static bool trans_SCVTF_dh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_dh); +} + +static bool trans_SCVTF_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_ss); +} + +static bool trans_SCVTF_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_ds); +} + +static bool trans_SCVTF_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_sd); +} + +static bool trans_SCVTF_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_dd); +} + +static bool trans_UCVTF_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_ucvt_hh); +} + +static bool trans_UCVTF_sh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_ucvt_sh); +} + +static bool trans_UCVTF_dh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_ucvt_dh); +} + +static bool trans_UCVTF_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_ss); +} + +static bool trans_UCVTF_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_ds); +} + +static bool trans_UCVTF_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_sd); +} + +static bool trans_UCVTF_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_dd); +} + /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group */ From ec3b87c28eb120b6575cc1ed7bfbfbf1b0060163 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:04 +0100 Subject: [PATCH 1778/2380] target/arm: Implement SVE floating-point arithmetic (predicated) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 77 +++++++++++++++++++++++++++++++++ target/arm/sve.decode | 17 ++++++++ target/arm/sve_helper.c | 89 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 46 ++++++++++++++++++++ 4 files changed, 229 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 185112e1d2..4097b55f0e 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -720,6 +720,83 @@ DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fsub_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsub_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsub_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmul_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmul_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmul_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fdiv_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fdiv_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fdiv_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmin_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmin_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmin_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmax_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmax_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmax_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fminnum_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fminnum_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fminnum_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmaxnum_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxnum_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxnum_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fabd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fabd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fabd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fscalbn_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fscalbn_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fscalbn_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmulx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmulx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmulx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_scvt_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_scvt_sh, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 3abdb87cf5..636212a638 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -684,6 +684,23 @@ FTSMUL 01100101 .. 0 ..... 000 011 ..... ..... @rd_rn_rm FRECPS 01100101 .. 0 ..... 000 110 ..... ..... @rd_rn_rm FRSQRTS 01100101 .. 0 ..... 000 111 ..... ..... @rd_rn_rm +### SVE FP Arithmetic Predicated Group + +# SVE floating-point arithmetic (predicated) +FADD_zpzz 01100101 .. 00 0000 100 ... ..... ..... @rdn_pg_rm +FSUB_zpzz 01100101 .. 00 0001 100 ... ..... ..... @rdn_pg_rm +FMUL_zpzz 01100101 .. 00 0010 100 ... ..... ..... @rdn_pg_rm +FSUB_zpzz 01100101 .. 00 0011 100 ... ..... ..... @rdm_pg_rn # FSUBR +FMAXNM_zpzz 01100101 .. 00 0100 100 ... ..... ..... @rdn_pg_rm +FMINNM_zpzz 01100101 .. 00 0101 100 ... ..... ..... @rdn_pg_rm +FMAX_zpzz 01100101 .. 00 0110 100 ... ..... ..... @rdn_pg_rm +FMIN_zpzz 01100101 .. 00 0111 100 ... ..... ..... @rdn_pg_rm +FABD 01100101 .. 00 1000 100 ... ..... ..... @rdn_pg_rm +FSCALE 01100101 .. 00 1001 100 ... ..... ..... @rdn_pg_rm +FMULX 01100101 .. 00 1010 100 ... ..... ..... @rdn_pg_rm +FDIV 01100101 .. 00 1100 100 ... ..... ..... @rdm_pg_rn # FDIVR +FDIV 01100101 .. 00 1101 100 ... ..... ..... @rdn_pg_rm + ### SVE FP Unary Operations Predicated Group # SVE integer convert to floating-point diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 031bec22df..3401662397 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2811,6 +2811,95 @@ uint32_t HELPER(sve_while)(void *vd, uint32_t count, uint32_t pred_desc) return predtest_ones(d, oprsz, esz_mask); } +/* Fully general three-operand expander, controlled by a predicate, + * With the extra float_status parameter. + */ +#define DO_ZPZZ_FP(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ + void *status, uint32_t desc) \ +{ \ + intptr_t i = simd_oprsz(desc); \ + uint64_t *g = vg; \ + do { \ + uint64_t pg = g[(i - 1) >> 6]; \ + do { \ + i -= sizeof(TYPE); \ + if (likely((pg >> (i & 63)) & 1)) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + TYPE mm = *(TYPE *)(vm + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, mm, status); \ + } \ + } while (i & 63); \ + } while (i != 0); \ +} + +DO_ZPZZ_FP(sve_fadd_h, uint16_t, H1_2, float16_add) +DO_ZPZZ_FP(sve_fadd_s, uint32_t, H1_4, float32_add) +DO_ZPZZ_FP(sve_fadd_d, uint64_t, , float64_add) + +DO_ZPZZ_FP(sve_fsub_h, uint16_t, H1_2, float16_sub) +DO_ZPZZ_FP(sve_fsub_s, uint32_t, H1_4, float32_sub) +DO_ZPZZ_FP(sve_fsub_d, uint64_t, , float64_sub) + +DO_ZPZZ_FP(sve_fmul_h, uint16_t, H1_2, float16_mul) +DO_ZPZZ_FP(sve_fmul_s, uint32_t, H1_4, float32_mul) +DO_ZPZZ_FP(sve_fmul_d, uint64_t, , float64_mul) + +DO_ZPZZ_FP(sve_fdiv_h, uint16_t, H1_2, float16_div) +DO_ZPZZ_FP(sve_fdiv_s, uint32_t, H1_4, float32_div) +DO_ZPZZ_FP(sve_fdiv_d, uint64_t, , float64_div) + +DO_ZPZZ_FP(sve_fmin_h, uint16_t, H1_2, float16_min) +DO_ZPZZ_FP(sve_fmin_s, uint32_t, H1_4, float32_min) +DO_ZPZZ_FP(sve_fmin_d, uint64_t, , float64_min) + +DO_ZPZZ_FP(sve_fmax_h, uint16_t, H1_2, float16_max) +DO_ZPZZ_FP(sve_fmax_s, uint32_t, H1_4, float32_max) +DO_ZPZZ_FP(sve_fmax_d, uint64_t, , float64_max) + +DO_ZPZZ_FP(sve_fminnum_h, uint16_t, H1_2, float16_minnum) +DO_ZPZZ_FP(sve_fminnum_s, uint32_t, H1_4, float32_minnum) +DO_ZPZZ_FP(sve_fminnum_d, uint64_t, , float64_minnum) + +DO_ZPZZ_FP(sve_fmaxnum_h, uint16_t, H1_2, float16_maxnum) +DO_ZPZZ_FP(sve_fmaxnum_s, uint32_t, H1_4, float32_maxnum) +DO_ZPZZ_FP(sve_fmaxnum_d, uint64_t, , float64_maxnum) + +static inline float16 abd_h(float16 a, float16 b, float_status *s) +{ + return float16_abs(float16_sub(a, b, s)); +} + +static inline float32 abd_s(float32 a, float32 b, float_status *s) +{ + return float32_abs(float32_sub(a, b, s)); +} + +static inline float64 abd_d(float64 a, float64 b, float_status *s) +{ + return float64_abs(float64_sub(a, b, s)); +} + +DO_ZPZZ_FP(sve_fabd_h, uint16_t, H1_2, abd_h) +DO_ZPZZ_FP(sve_fabd_s, uint32_t, H1_4, abd_s) +DO_ZPZZ_FP(sve_fabd_d, uint64_t, , abd_d) + +static inline float64 scalbn_d(float64 a, int64_t b, float_status *s) +{ + int b_int = MIN(MAX(b, INT_MIN), INT_MAX); + return float64_scalbn(a, b_int, s); +} + +DO_ZPZZ_FP(sve_fscalbn_h, int16_t, H1_2, float16_scalbn) +DO_ZPZZ_FP(sve_fscalbn_s, int32_t, H1_4, float32_scalbn) +DO_ZPZZ_FP(sve_fscalbn_d, int64_t, , scalbn_d) + +DO_ZPZZ_FP(sve_fmulx_h, uint16_t, H1_2, helper_advsimd_mulxh) +DO_ZPZZ_FP(sve_fmulx_s, uint32_t, H1_4, helper_vfp_mulxs) +DO_ZPZZ_FP(sve_fmulx_d, uint64_t, , helper_vfp_mulxd) + +#undef DO_ZPZZ_FP + /* Fully general two-operand expander, controlled by a predicate, * With the extra float_status parameter. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 5cb13393ba..55717e5e0b 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3425,6 +3425,52 @@ DO_FP3(FRSQRTS, rsqrts) #undef DO_FP3 +/* + *** SVE Floating Point Arithmetic - Predicated Group + */ + +static bool do_zpzz_fp(DisasContext *s, arg_rprr_esz *a, + gen_helper_gvec_4_ptr *fn) +{ + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); + } + return true; +} + +#define DO_FP3(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rprr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_4_ptr * const fns[4] = { \ + NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d \ + }; \ + return do_zpzz_fp(s, a, fns[a->esz]); \ +} + +DO_FP3(FADD_zpzz, fadd) +DO_FP3(FSUB_zpzz, fsub) +DO_FP3(FMUL_zpzz, fmul) +DO_FP3(FMIN_zpzz, fmin) +DO_FP3(FMAX_zpzz, fmax) +DO_FP3(FMINNM_zpzz, fminnum) +DO_FP3(FMAXNM_zpzz, fmaxnum) +DO_FP3(FABD, fabd) +DO_FP3(FSCALE, fscalbn) +DO_FP3(FDIV, fdiv) +DO_FP3(FMULX, fmulx) + +#undef DO_FP3 /* *** SVE Floating Point Unary Operations Predicated Group From 6ceabaad1101b0b33490b0fd4bed5b6445b0a34e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:04 +0100 Subject: [PATCH 1779/2380] target/arm: Implement SVE FP Multiply-Add Group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180627043328.11531-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 16 ++++ target/arm/sve.decode | 18 +++++ target/arm/sve_helper.c | 158 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 49 ++++++++++++ 4 files changed, 241 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 4097b55f0e..eb0645dd43 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -827,6 +827,22 @@ DEF_HELPER_FLAGS_5(sve_ucvt_ds, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_ucvt_dd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_fmls_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fmls_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fmls_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_fnmla_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fnmla_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fnmla_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) + DEF_HELPER_FLAGS_4(sve_ld1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 636212a638..e8531e28cd 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -128,6 +128,8 @@ &rprrr_esz ra=%reg_movprfx @rdn_pg_ra_rm ........ esz:2 . rm:5 ... pg:3 ra:5 rd:5 \ &rprrr_esz rn=%reg_movprfx +@rdn_pg_rm_ra ........ esz:2 . ra:5 ... pg:3 rm:5 rd:5 \ + &rprrr_esz rn=%reg_movprfx # One register operand, with governing predicate, vector element size @rd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 &rpr_esz @@ -701,6 +703,22 @@ FMULX 01100101 .. 00 1010 100 ... ..... ..... @rdn_pg_rm FDIV 01100101 .. 00 1100 100 ... ..... ..... @rdm_pg_rn # FDIVR FDIV 01100101 .. 00 1101 100 ... ..... ..... @rdn_pg_rm +### SVE FP Multiply-Add Group + +# SVE floating-point multiply-accumulate writing addend +FMLA_zpzzz 01100101 .. 1 ..... 000 ... ..... ..... @rda_pg_rn_rm +FMLS_zpzzz 01100101 .. 1 ..... 001 ... ..... ..... @rda_pg_rn_rm +FNMLA_zpzzz 01100101 .. 1 ..... 010 ... ..... ..... @rda_pg_rn_rm +FNMLS_zpzzz 01100101 .. 1 ..... 011 ... ..... ..... @rda_pg_rn_rm + +# SVE floating-point multiply-accumulate writing multiplicand +# Alter the operand extraction order and reuse the helpers from above. +# FMAD, FMSB, FNMAD, FNMS +FMLA_zpzzz 01100101 .. 1 ..... 100 ... ..... ..... @rdn_pg_rm_ra +FMLS_zpzzz 01100101 .. 1 ..... 101 ... ..... ..... @rdn_pg_rm_ra +FNMLA_zpzzz 01100101 .. 1 ..... 110 ... ..... ..... @rdn_pg_rm_ra +FNMLS_zpzzz 01100101 .. 1 ..... 111 ... ..... ..... @rdn_pg_rm_ra + ### SVE FP Unary Operations Predicated Group # SVE integer convert to floating-point diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 3401662397..2f416e5e28 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2938,6 +2938,164 @@ DO_ZPZ_FP(sve_ucvt_dd, uint64_t, , uint64_to_float64) #undef DO_ZPZ_FP +/* 4-operand predicated multiply-add. This requires 7 operands to pass + * "properly", so we need to encode some of the registers into DESC. + */ +QEMU_BUILD_BUG_ON(SIMD_DATA_SHIFT + 20 > 32); + +static void do_fmla_zpzzz_h(CPUARMState *env, void *vg, uint32_t desc, + uint16_t neg1, uint16_t neg3) +{ + intptr_t i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + i -= 2; + if (likely((pg >> (i & 63)) & 1)) { + float16 e1, e2, e3, r; + + e1 = *(uint16_t *)(vn + H1_2(i)) ^ neg1; + e2 = *(uint16_t *)(vm + H1_2(i)); + e3 = *(uint16_t *)(va + H1_2(i)) ^ neg3; + r = float16_muladd(e1, e2, e3, 0, &env->vfp.fp_status); + *(uint16_t *)(vd + H1_2(i)) = r; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fmla_zpzzz_h)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_h(env, vg, desc, 0, 0); +} + +void HELPER(sve_fmls_zpzzz_h)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_h(env, vg, desc, 0x8000, 0); +} + +void HELPER(sve_fnmla_zpzzz_h)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_h(env, vg, desc, 0x8000, 0x8000); +} + +void HELPER(sve_fnmls_zpzzz_h)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_h(env, vg, desc, 0, 0x8000); +} + +static void do_fmla_zpzzz_s(CPUARMState *env, void *vg, uint32_t desc, + uint32_t neg1, uint32_t neg3) +{ + intptr_t i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + i -= 4; + if (likely((pg >> (i & 63)) & 1)) { + float32 e1, e2, e3, r; + + e1 = *(uint32_t *)(vn + H1_4(i)) ^ neg1; + e2 = *(uint32_t *)(vm + H1_4(i)); + e3 = *(uint32_t *)(va + H1_4(i)) ^ neg3; + r = float32_muladd(e1, e2, e3, 0, &env->vfp.fp_status); + *(uint32_t *)(vd + H1_4(i)) = r; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fmla_zpzzz_s)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_s(env, vg, desc, 0, 0); +} + +void HELPER(sve_fmls_zpzzz_s)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_s(env, vg, desc, 0x80000000, 0); +} + +void HELPER(sve_fnmla_zpzzz_s)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_s(env, vg, desc, 0x80000000, 0x80000000); +} + +void HELPER(sve_fnmls_zpzzz_s)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_s(env, vg, desc, 0, 0x80000000); +} + +static void do_fmla_zpzzz_d(CPUARMState *env, void *vg, uint32_t desc, + uint64_t neg1, uint64_t neg3) +{ + intptr_t i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + i -= 8; + if (likely((pg >> (i & 63)) & 1)) { + float64 e1, e2, e3, r; + + e1 = *(uint64_t *)(vn + i) ^ neg1; + e2 = *(uint64_t *)(vm + i); + e3 = *(uint64_t *)(va + i) ^ neg3; + r = float64_muladd(e1, e2, e3, 0, &env->vfp.fp_status); + *(uint64_t *)(vd + i) = r; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fmla_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_d(env, vg, desc, 0, 0); +} + +void HELPER(sve_fmls_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_d(env, vg, desc, INT64_MIN, 0); +} + +void HELPER(sve_fnmla_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_d(env, vg, desc, INT64_MIN, INT64_MIN); +} + +void HELPER(sve_fnmls_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_d(env, vg, desc, 0, INT64_MIN); +} + /* * Load contiguous data, protected by a governing predicate. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 55717e5e0b..369ce98dc6 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3472,6 +3472,55 @@ DO_FP3(FMULX, fmulx) #undef DO_FP3 +typedef void gen_helper_sve_fmla(TCGv_env, TCGv_ptr, TCGv_i32); + +static bool do_fmla(DisasContext *s, arg_rprrr_esz *a, gen_helper_sve_fmla *fn) +{ + if (fn == NULL) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = vec_full_reg_size(s); + unsigned desc; + TCGv_i32 t_desc; + TCGv_ptr pg = tcg_temp_new_ptr(); + + /* We would need 7 operands to pass these arguments "properly". + * So we encode all the register numbers into the descriptor. + */ + desc = deposit32(a->rd, 5, 5, a->rn); + desc = deposit32(desc, 10, 5, a->rm); + desc = deposit32(desc, 15, 5, a->ra); + desc = simd_desc(vsz, vsz, desc); + + t_desc = tcg_const_i32(desc); + tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); + fn(cpu_env, pg, t_desc); + tcg_temp_free_i32(t_desc); + tcg_temp_free_ptr(pg); + return true; +} + +#define DO_FMLA(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rprrr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_sve_fmla * const fns[4] = { \ + NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d \ + }; \ + return do_fmla(s, a, fns[a->esz]); \ +} + +DO_FMLA(FMLA_zpzzz, fmla_zpzzz) +DO_FMLA(FMLS_zpzzz, fmls_zpzzz) +DO_FMLA(FNMLA_zpzzz, fnmla_zpzzz) +DO_FMLA(FNMLS_zpzzz, fnmls_zpzzz) + +#undef DO_FMLA + /* *** SVE Floating Point Unary Operations Predicated Group */ From 7f9ddf64d5fe5bfaa91ae0ec52217d86f4d86452 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:04 +0100 Subject: [PATCH 1780/2380] target/arm: Implement SVE Floating Point Accumulating Reduction Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 7 +++++ target/arm/sve.decode | 5 ++++ target/arm/sve_helper.c | 56 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 45 ++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index eb0645dd43..68e55a8d03 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -720,6 +720,13 @@ DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fadda_h, TCG_CALL_NO_RWG, + i64, i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fadda_s, TCG_CALL_NO_RWG, + i64, i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fadda_d, TCG_CALL_NO_RWG, + i64, i64, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_6(sve_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_6(sve_fadd_s, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index e8531e28cd..675b81aaa0 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -676,6 +676,11 @@ UMIN_zzi 00100101 .. 101 011 110 ........ ..... @rdn_i8u # SVE integer multiply immediate (unpredicated) MUL_zzi 00100101 .. 110 000 110 ........ ..... @rdn_i8s +### SVE FP Accumulating Reduction Group + +# SVE floating-point serial reduction (predicated) +FADDA 01100101 .. 011 000 001 ... ..... ..... @rdn_pg_rm + ### SVE Floating Point Arithmetic - Unpredicated Group # SVE floating-point arithmetic (unpredicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 2f416e5e28..2d08b7dcd3 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2811,6 +2811,62 @@ uint32_t HELPER(sve_while)(void *vd, uint32_t count, uint32_t pred_desc) return predtest_ones(d, oprsz, esz_mask); } +uint64_t HELPER(sve_fadda_h)(uint64_t nn, void *vm, void *vg, + void *status, uint32_t desc) +{ + intptr_t i = 0, opr_sz = simd_oprsz(desc); + float16 result = nn; + + do { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + float16 mm = *(float16 *)(vm + H1_2(i)); + result = float16_add(result, mm, status); + } + i += sizeof(float16), pg >>= sizeof(float16); + } while (i & 15); + } while (i < opr_sz); + + return result; +} + +uint64_t HELPER(sve_fadda_s)(uint64_t nn, void *vm, void *vg, + void *status, uint32_t desc) +{ + intptr_t i = 0, opr_sz = simd_oprsz(desc); + float32 result = nn; + + do { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + float32 mm = *(float32 *)(vm + H1_2(i)); + result = float32_add(result, mm, status); + } + i += sizeof(float32), pg >>= sizeof(float32); + } while (i & 15); + } while (i < opr_sz); + + return result; +} + +uint64_t HELPER(sve_fadda_d)(uint64_t nn, void *vm, void *vg, + void *status, uint32_t desc) +{ + intptr_t i = 0, opr_sz = simd_oprsz(desc) / 8; + uint64_t *m = vm; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i++) { + if (pg[H1(i)] & 1) { + nn = float64_add(nn, m[i], status); + } + } + + return nn; +} + /* Fully general three-operand expander, controlled by a predicate, * With the extra float_status parameter. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 369ce98dc6..ceb46895e0 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3383,6 +3383,51 @@ DO_ZZI(UMIN, umin) #undef DO_ZZI +/* + *** SVE Floating Point Accumulating Reduction Group + */ + +static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + typedef void fadda_fn(TCGv_i64, TCGv_i64, TCGv_ptr, + TCGv_ptr, TCGv_ptr, TCGv_i32); + static fadda_fn * const fns[3] = { + gen_helper_sve_fadda_h, + gen_helper_sve_fadda_s, + gen_helper_sve_fadda_d, + }; + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_rm, t_pg, t_fpst; + TCGv_i64 t_val; + TCGv_i32 t_desc; + + if (a->esz == 0) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + t_val = load_esz(cpu_env, vec_reg_offset(s, a->rn, 0, a->esz), a->esz); + t_rm = tcg_temp_new_ptr(); + t_pg = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(t_rm, cpu_env, vec_full_reg_offset(s, a->rm)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->pg)); + t_fpst = get_fpstatus_ptr(a->esz == MO_16); + t_desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + + fns[a->esz - 1](t_val, t_val, t_rm, t_pg, t_fpst, t_desc); + + tcg_temp_free_i32(t_desc); + tcg_temp_free_ptr(t_fpst); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_ptr(t_rm); + + write_fp_dreg(s, a->rd, t_val); + tcg_temp_free_i64(t_val); + return true; +} + /* *** SVE Floating Point Arithmetic - Unpredicated Group */ From 684598640dc3b28f86ccc28cc9af50ba257f4cc8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:05 +0100 Subject: [PATCH 1781/2380] target/arm: Implement SVE load and broadcast element Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 5 +++ target/arm/sve.decode | 5 +++ target/arm/sve_helper.c | 41 +++++++++++++++++++++++++ target/arm/translate-sve.c | 62 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 68e55a8d03..a5d3bb121c 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -274,6 +274,11 @@ DEF_HELPER_FLAGS_3(sve_clr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_clr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_clr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_movz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_movz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_movz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_movz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_asr_zpzi_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_asr_zpzi_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_asr_zpzi_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 675b81aaa0..765e7e479b 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -28,6 +28,7 @@ %imm8_16_10 16:5 10:3 %imm9_16_10 16:s6 10:3 %size_23 23:2 +%dtype_23_13 23:2 13:2 # A combination of tsz:imm3 -- extract esize. %tszimm_esz 22:2 5:5 !function=tszimm_esz @@ -751,6 +752,10 @@ LDR_pri 10000101 10 ...... 000 ... ..... 0 .... @pd_rn_i9 # SVE load vector register LDR_zri 10000101 10 ...... 010 ... ..... ..... @rd_rn_i9 +# SVE load and broadcast element +LD1R_zpri 1000010 .. 1 imm:6 1.. pg:3 rn:5 rd:5 \ + &rpri_load dtype=%dtype_23_13 nreg=0 + ### SVE Memory Contiguous Load Group # SVE contiguous load (scalar plus scalar) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 2d08b7dcd3..93f2942590 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -995,6 +995,47 @@ void HELPER(sve_clr_d)(void *vd, void *vg, uint32_t desc) } } +/* Copy Zn into Zd, and store zero into inactive elements. */ +void HELPER(sve_movz_b)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] & expand_pred_b(pg[H1(i)]); + } +} + +void HELPER(sve_movz_h)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] & expand_pred_h(pg[H1(i)]); + } +} + +void HELPER(sve_movz_s)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] & expand_pred_s(pg[H1(i)]); + } +} + +void HELPER(sve_movz_d)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[1] & -(uint64_t)(pg[H1(i)] & 1); + } +} + /* Three-operand expander, immediate operand, controlled by a predicate. */ #define DO_ZPZI(NAME, TYPE, H, OP) \ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index ceb46895e0..a0d5b0fd58 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -606,6 +606,20 @@ static bool do_clr_zp(DisasContext *s, int rd, int pg, int esz) return true; } +/* Copy Zn into Zd, storing zeros into inactive elements. */ +static void do_movz_zpz(DisasContext *s, int rd, int rn, int pg, int esz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_movz_b, gen_helper_sve_movz_h, + gen_helper_sve_movz_s, gen_helper_sve_movz_d, + }; + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + pred_full_reg_offset(s, pg), + vsz, vsz, 0, fns[esz]); +} + static bool do_zpzi_ool(DisasContext *s, arg_rpri_esz *a, gen_helper_gvec_3 *fn) { @@ -3999,6 +4013,54 @@ static bool trans_LD1RQ_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) return true; } +/* Load and broadcast element. */ +static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = vec_full_reg_size(s); + unsigned psz = pred_full_reg_size(s); + unsigned esz = dtype_esz[a->dtype]; + TCGLabel *over = gen_new_label(); + TCGv_i64 temp; + + /* If the guarding predicate has no bits set, no load occurs. */ + if (psz <= 8) { + /* Reduce the pred_esz_masks value simply to reduce the + * size of the code generated here. + */ + uint64_t psz_mask = MAKE_64BIT_MASK(0, psz * 8); + temp = tcg_temp_new_i64(); + tcg_gen_ld_i64(temp, cpu_env, pred_full_reg_offset(s, a->pg)); + tcg_gen_andi_i64(temp, temp, pred_esz_masks[esz] & psz_mask); + tcg_gen_brcondi_i64(TCG_COND_EQ, temp, 0, over); + tcg_temp_free_i64(temp); + } else { + TCGv_i32 t32 = tcg_temp_new_i32(); + find_last_active(s, t32, esz, a->pg); + tcg_gen_brcondi_i32(TCG_COND_LT, t32, 0, over); + tcg_temp_free_i32(t32); + } + + /* Load the data. */ + temp = tcg_temp_new_i64(); + tcg_gen_addi_i64(temp, cpu_reg_sp(s, a->rn), a->imm << esz); + tcg_gen_qemu_ld_i64(temp, temp, get_mem_index(s), + s->be_data | dtype_mop[a->dtype]); + + /* Broadcast to *all* elements. */ + tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd), + vsz, vsz, temp); + tcg_temp_free_i64(temp); + + /* Zero the inactive elements. */ + gen_set_label(over); + do_movz_zpz(s, a->rd, a->rd, a->pg, esz); + return true; +} + static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, int msz, int esz, int nreg) { From 5047c204d0d4a0fff616a24963b2b45c7d9ba4c4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:05 +0100 Subject: [PATCH 1782/2380] target/arm: Implement SVE store vector/predicate register Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 6 +++ target/arm/translate-sve.c | 103 +++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 765e7e479b..6a76010f51 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -793,6 +793,12 @@ LD1RQ_zpri 1010010 .. 00 0.... 001 ... ..... ..... \ ### SVE Memory Store Group +# SVE store predicate register +STR_pri 1110010 11 0. ..... 000 ... ..... 0 .... @pd_rn_i9 + +# SVE store vector register +STR_zri 1110010 11 0. ..... 010 ... ..... ..... @rd_rn_i9 + # SVE contiguous store (scalar plus immediate) # ST1B, ST1H, ST1W, ST1D; require msz <= esz ST_zpri 1110010 .. esz:2 0.... 111 ... ..... ..... \ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index a0d5b0fd58..01de315f7c 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3762,6 +3762,89 @@ static void do_ldr(DisasContext *s, uint32_t vofs, uint32_t len, tcg_temp_free_i64(t0); } +/* Similarly for stores. */ +static void do_str(DisasContext *s, uint32_t vofs, uint32_t len, + int rn, int imm) +{ + uint32_t len_align = QEMU_ALIGN_DOWN(len, 8); + uint32_t len_remain = len % 8; + uint32_t nparts = len / 8 + ctpop8(len_remain); + int midx = get_mem_index(s); + TCGv_i64 addr, t0; + + addr = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + + /* Note that unpredicated load/store of vector/predicate registers + * are defined as a stream of bytes, which equates to little-endian + * operations on larger quantities. There is no nice way to force + * a little-endian store for aarch64_be-linux-user out of line. + * + * Attempt to keep code expansion to a minimum by limiting the + * amount of unrolling done. + */ + if (nparts <= 4) { + int i; + + for (i = 0; i < len_align; i += 8) { + tcg_gen_ld_i64(t0, cpu_env, vofs + i); + tcg_gen_addi_i64(addr, cpu_reg_sp(s, rn), imm + i); + tcg_gen_qemu_st_i64(t0, addr, midx, MO_LEQ); + } + } else { + TCGLabel *loop = gen_new_label(); + TCGv_ptr t2, i = tcg_const_local_ptr(0); + + gen_set_label(loop); + + t2 = tcg_temp_new_ptr(); + tcg_gen_add_ptr(t2, cpu_env, i); + tcg_gen_ld_i64(t0, t2, vofs); + + /* Minimize the number of local temps that must be re-read from + * the stack each iteration. Instead, re-compute values other + * than the loop counter. + */ + tcg_gen_addi_ptr(t2, i, imm); + tcg_gen_extu_ptr_i64(addr, t2); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, rn)); + tcg_temp_free_ptr(t2); + + tcg_gen_qemu_st_i64(t0, addr, midx, MO_LEQ); + + tcg_gen_addi_ptr(i, i, 8); + + tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); + tcg_temp_free_ptr(i); + } + + /* Predicate register stores can be any multiple of 2. */ + if (len_remain) { + tcg_gen_ld_i64(t0, cpu_env, vofs + len_align); + tcg_gen_addi_i64(addr, cpu_reg_sp(s, rn), imm + len_align); + + switch (len_remain) { + case 2: + case 4: + case 8: + tcg_gen_qemu_st_i64(t0, addr, midx, MO_LE | ctz32(len_remain)); + break; + + case 6: + tcg_gen_qemu_st_i64(t0, addr, midx, MO_LEUL); + tcg_gen_addi_i64(addr, addr, 4); + tcg_gen_shri_i64(t0, t0, 32); + tcg_gen_qemu_st_i64(t0, addr, midx, MO_LEUW); + break; + + default: + g_assert_not_reached(); + } + } + tcg_temp_free_i64(addr); + tcg_temp_free_i64(t0); +} + static bool trans_LDR_zri(DisasContext *s, arg_rri *a, uint32_t insn) { if (sve_access_check(s)) { @@ -3782,6 +3865,26 @@ static bool trans_LDR_pri(DisasContext *s, arg_rri *a, uint32_t insn) return true; } +static bool trans_STR_zri(DisasContext *s, arg_rri *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int size = vec_full_reg_size(s); + int off = vec_full_reg_offset(s, a->rd); + do_str(s, off, size, a->rn, a->imm * size); + } + return true; +} + +static bool trans_STR_pri(DisasContext *s, arg_rri *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int size = pred_full_reg_size(s); + int off = pred_full_reg_offset(s, a->rd); + do_str(s, off, size, a->rn, a->imm * size); + } + return true; +} + /* *** SVE Memory - Contiguous Load Group */ From f6dbf62a7e3d00e9a1dcc7fe3e53b32c3ed93e24 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:05 +0100 Subject: [PATCH 1783/2380] target/arm: Implement SVE scatter stores Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 41 +++++++++++++++++++++ target/arm/sve.decode | 39 ++++++++++++++++++++ target/arm/sve_helper.c | 61 +++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 75 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 216 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index a5d3bb121c..8880128f9c 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -958,3 +958,44 @@ DEF_HELPER_FLAGS_4(sve_st1hs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1hd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1sd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_stbs_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_sths_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stss_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_stbs_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_sths_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stss_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_stbd_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_sthd_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stsd_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_stbd_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_sthd_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stsd_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_stbd_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_sthd_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stsd_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 6a76010f51..7d24c2bdc4 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -80,6 +80,7 @@ &rpri_load rd pg rn imm dtype nreg &rprr_store rd pg rn rm msz esz nreg &rpri_store rd pg rn imm msz esz nreg +&rprr_scatter_store rd pg rn rm esz msz xs scale ########################################################################### # Named instruction formats. These are generally used to @@ -198,6 +199,8 @@ @rpri_store_msz ....... msz:2 .. . imm:s4 ... pg:3 rn:5 rd:5 &rpri_store @rprr_store_esz_n0 ....... .. esz:2 rm:5 ... pg:3 rn:5 rd:5 \ &rprr_store nreg=0 +@rprr_scatter_store ....... msz:2 .. rm:5 ... pg:3 rn:5 rd:5 \ + &rprr_scatter_store ########################################################################### # Instruction patterns. Grouped according to the SVE encodingindex.xhtml. @@ -825,3 +828,39 @@ ST_zpri 1110010 .. nreg:2 1.... 111 ... ..... ..... \ # SVE store multiple structures (scalar plus scalar) (nreg != 0) ST_zprr 1110010 msz:2 nreg:2 ..... 011 ... ..... ..... \ @rprr_store esz=%size_23 + +# SVE 32-bit scatter store (scalar plus 32-bit scaled offsets) +# Require msz > 0 && msz <= esz. +ST1_zprz 1110010 .. 11 ..... 100 ... ..... ..... \ + @rprr_scatter_store xs=0 esz=2 scale=1 +ST1_zprz 1110010 .. 11 ..... 110 ... ..... ..... \ + @rprr_scatter_store xs=1 esz=2 scale=1 + +# SVE 32-bit scatter store (scalar plus 32-bit unscaled offsets) +# Require msz <= esz. +ST1_zprz 1110010 .. 10 ..... 100 ... ..... ..... \ + @rprr_scatter_store xs=0 esz=2 scale=0 +ST1_zprz 1110010 .. 10 ..... 110 ... ..... ..... \ + @rprr_scatter_store xs=1 esz=2 scale=0 + +# SVE 64-bit scatter store (scalar plus 64-bit scaled offset) +# Require msz > 0 +ST1_zprz 1110010 .. 01 ..... 101 ... ..... ..... \ + @rprr_scatter_store xs=2 esz=3 scale=1 + +# SVE 64-bit scatter store (scalar plus 64-bit unscaled offset) +ST1_zprz 1110010 .. 00 ..... 101 ... ..... ..... \ + @rprr_scatter_store xs=2 esz=3 scale=0 + +# SVE 64-bit scatter store (scalar plus unpacked 32-bit scaled offset) +# Require msz > 0 +ST1_zprz 1110010 .. 01 ..... 100 ... ..... ..... \ + @rprr_scatter_store xs=0 esz=3 scale=1 +ST1_zprz 1110010 .. 01 ..... 110 ... ..... ..... \ + @rprr_scatter_store xs=1 esz=3 scale=1 + +# SVE 64-bit scatter store (scalar plus unpacked 32-bit unscaled offset) +ST1_zprz 1110010 .. 00 ..... 100 ... ..... ..... \ + @rprr_scatter_store xs=0 esz=3 scale=0 +ST1_zprz 1110010 .. 00 ..... 110 ... ..... ..... \ + @rprr_scatter_store xs=1 esz=3 scale=0 diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 93f2942590..7622bb2af0 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3713,3 +3713,64 @@ void HELPER(sve_st4dd_r)(CPUARMState *env, void *vg, addr += 4 * 8; } } + +/* Stores with a vector index. */ + +#define DO_ST1_ZPZ_S(NAME, TYPEI, FN) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + unsigned scale = simd_data(desc); \ + uintptr_t ra = GETPC(); \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (likely(pg & 1)) { \ + target_ulong off = *(TYPEI *)(vm + H1_4(i)); \ + uint32_t d = *(uint32_t *)(vd + H1_4(i)); \ + FN(env, base + (off << scale), d, ra); \ + } \ + i += sizeof(uint32_t), pg >>= sizeof(uint32_t); \ + } while (i & 15); \ + } \ +} + +#define DO_ST1_ZPZ_D(NAME, TYPEI, FN) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc) / 8; \ + unsigned scale = simd_data(desc); \ + uintptr_t ra = GETPC(); \ + uint64_t *d = vd, *m = vm; uint8_t *pg = vg; \ + for (i = 0; i < oprsz; i++) { \ + if (likely(pg[H1(i)] & 1)) { \ + target_ulong off = (target_ulong)(TYPEI)m[i] << scale; \ + FN(env, base + off, d[i], ra); \ + } \ + } \ +} + +DO_ST1_ZPZ_S(sve_stbs_zsu, uint32_t, cpu_stb_data_ra) +DO_ST1_ZPZ_S(sve_sths_zsu, uint32_t, cpu_stw_data_ra) +DO_ST1_ZPZ_S(sve_stss_zsu, uint32_t, cpu_stl_data_ra) + +DO_ST1_ZPZ_S(sve_stbs_zss, int32_t, cpu_stb_data_ra) +DO_ST1_ZPZ_S(sve_sths_zss, int32_t, cpu_stw_data_ra) +DO_ST1_ZPZ_S(sve_stss_zss, int32_t, cpu_stl_data_ra) + +DO_ST1_ZPZ_D(sve_stbd_zsu, uint32_t, cpu_stb_data_ra) +DO_ST1_ZPZ_D(sve_sthd_zsu, uint32_t, cpu_stw_data_ra) +DO_ST1_ZPZ_D(sve_stsd_zsu, uint32_t, cpu_stl_data_ra) +DO_ST1_ZPZ_D(sve_stdd_zsu, uint32_t, cpu_stq_data_ra) + +DO_ST1_ZPZ_D(sve_stbd_zss, int32_t, cpu_stb_data_ra) +DO_ST1_ZPZ_D(sve_sthd_zss, int32_t, cpu_stw_data_ra) +DO_ST1_ZPZ_D(sve_stsd_zss, int32_t, cpu_stl_data_ra) +DO_ST1_ZPZ_D(sve_stdd_zss, int32_t, cpu_stq_data_ra) + +DO_ST1_ZPZ_D(sve_stbd_zd, uint64_t, cpu_stb_data_ra) +DO_ST1_ZPZ_D(sve_sthd_zd, uint64_t, cpu_stw_data_ra) +DO_ST1_ZPZ_D(sve_stsd_zd, uint64_t, cpu_stl_data_ra) +DO_ST1_ZPZ_D(sve_stdd_zd, uint64_t, cpu_stq_data_ra) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 01de315f7c..11541b1a50 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -43,6 +43,8 @@ typedef void gen_helper_gvec_flags_4(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void gen_helper_gvec_mem(TCGv_env, TCGv_ptr, TCGv_i64, TCGv_i32); +typedef void gen_helper_gvec_mem_scatter(TCGv_env, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i64, TCGv_i32); /* * Helpers for extracting complex instruction fields. @@ -4228,3 +4230,76 @@ static bool trans_ST_zpri(DisasContext *s, arg_rpri_store *a, uint32_t insn) } return true; } + +/* + *** SVE gather loads / scatter stores + */ + +static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, int scale, + TCGv_i64 scalar, gen_helper_gvec_mem_scatter *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, scale)); + TCGv_ptr t_zm = tcg_temp_new_ptr(); + TCGv_ptr t_pg = tcg_temp_new_ptr(); + TCGv_ptr t_zt = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + tcg_gen_addi_ptr(t_zm, cpu_env, vec_full_reg_offset(s, zm)); + tcg_gen_addi_ptr(t_zt, cpu_env, vec_full_reg_offset(s, zt)); + fn(cpu_env, t_zt, t_pg, t_zm, scalar, desc); + + tcg_temp_free_ptr(t_zt); + tcg_temp_free_ptr(t_zm); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(desc); +} + +static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a, uint32_t insn) +{ + /* Indexed by [xs][msz]. */ + static gen_helper_gvec_mem_scatter * const fn32[2][3] = { + { gen_helper_sve_stbs_zsu, + gen_helper_sve_sths_zsu, + gen_helper_sve_stss_zsu, }, + { gen_helper_sve_stbs_zss, + gen_helper_sve_sths_zss, + gen_helper_sve_stss_zss, }, + }; + /* Note that we overload xs=2 to indicate 64-bit offset. */ + static gen_helper_gvec_mem_scatter * const fn64[3][4] = { + { gen_helper_sve_stbd_zsu, + gen_helper_sve_sthd_zsu, + gen_helper_sve_stsd_zsu, + gen_helper_sve_stdd_zsu, }, + { gen_helper_sve_stbd_zss, + gen_helper_sve_sthd_zss, + gen_helper_sve_stsd_zss, + gen_helper_sve_stdd_zss, }, + { gen_helper_sve_stbd_zd, + gen_helper_sve_sthd_zd, + gen_helper_sve_stsd_zd, + gen_helper_sve_stdd_zd, }, + }; + gen_helper_gvec_mem_scatter *fn; + + if (a->esz < a->msz || (a->msz == 0 && a->scale)) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + switch (a->esz) { + case MO_32: + fn = fn32[a->xs][a->msz]; + break; + case MO_64: + fn = fn64[a->xs][a->msz]; + break; + default: + g_assert_not_reached(); + } + do_mem_zpz(s, a->rd, a->pg, a->rm, a->scale * a->msz, + cpu_reg_sp(s, a->rn), fn); + return true; +} From dec6cf6b43a1e3b18626852064d1e6e863c9b681 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:06 +0100 Subject: [PATCH 1784/2380] target/arm: Implement SVE prefetches Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 23 +++++++++++++++++++++++ target/arm/translate-sve.c | 21 +++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 7d24c2bdc4..80b955ff84 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -794,6 +794,29 @@ LD1RQ_zprr 1010010 .. 00 ..... 000 ... ..... ..... \ LD1RQ_zpri 1010010 .. 00 0.... 001 ... ..... ..... \ @rpri_load_msz nreg=0 +# SVE 32-bit gather prefetch (scalar plus 32-bit scaled offsets) +PRF 1000010 00 -1 ----- 0-- --- ----- 0 ---- + +# SVE 32-bit gather prefetch (vector plus immediate) +PRF 1000010 -- 00 ----- 111 --- ----- 0 ---- + +# SVE contiguous prefetch (scalar plus immediate) +PRF 1000010 11 1- ----- 0-- --- ----- 0 ---- + +# SVE contiguous prefetch (scalar plus scalar) +PRF_rr 1000010 -- 00 rm:5 110 --- ----- 0 ---- + +### SVE Memory 64-bit Gather Group + +# SVE 64-bit gather prefetch (scalar plus 64-bit scaled offsets) +PRF 1100010 00 11 ----- 1-- --- ----- 0 ---- + +# SVE 64-bit gather prefetch (scalar plus unpacked 32-bit scaled offsets) +PRF 1100010 00 -1 ----- 0-- --- ----- 0 ---- + +# SVE 64-bit gather prefetch (vector plus immediate) +PRF 1100010 -- 00 ----- 111 --- ----- 0 ---- + ### SVE Memory Store Group # SVE store predicate register diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 11541b1a50..c73c3fc215 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -4303,3 +4303,24 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a, uint32_t insn) cpu_reg_sp(s, a->rn), fn); return true; } + +/* + * Prefetches + */ + +static bool trans_PRF(DisasContext *s, arg_PRF *a, uint32_t insn) +{ + /* Prefetch is a nop within QEMU. */ + sve_access_check(s); + return true; +} + +static bool trans_PRF_rr(DisasContext *s, arg_PRF_rr *a, uint32_t insn) +{ + if (a->rm == 31) { + return false; + } + /* Prefetch is a nop within QEMU. */ + sve_access_check(s); + return true; +} From 673e9fa6c29e030f4ab6ceae5d0f50bd36fe0ee0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:06 +0100 Subject: [PATCH 1785/2380] target/arm: Implement SVE gather loads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180627043328.11531-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 67 +++++++++++++++++++++++++ target/arm/sve.decode | 57 +++++++++++++++++++++ target/arm/sve_helper.c | 77 ++++++++++++++++++++++++++++ target/arm/translate-sve.c | 100 +++++++++++++++++++++++++++++++++++++ 4 files changed, 301 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 8880128f9c..aeb62afc34 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -959,6 +959,73 @@ DEF_HELPER_FLAGS_4(sve_st1hd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1sd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbsu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhsu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldssu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbss_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhss_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldbsu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhsu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldssu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbss_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhss_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldbdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldddu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldbdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldddu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldbdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldddu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + DEF_HELPER_FLAGS_6(sve_stbs_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_sths_zsu, TCG_CALL_NO_WG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 80b955ff84..45016c6042 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -80,6 +80,8 @@ &rpri_load rd pg rn imm dtype nreg &rprr_store rd pg rn rm msz esz nreg &rpri_store rd pg rn imm msz esz nreg +&rprr_gather_load rd pg rn rm esz msz u ff xs scale +&rpri_gather_load rd pg rn imm esz msz u ff &rprr_scatter_store rd pg rn rm esz msz xs scale ########################################################################### @@ -194,6 +196,22 @@ @rpri_load_msz ....... .... . imm:s4 ... pg:3 rn:5 rd:5 \ &rpri_load dtype=%msz_dtype +# Gather Loads. +@rprr_g_load_u ....... .. . . rm:5 . u:1 ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load xs=2 +@rprr_g_load_xs_u ....... .. xs:1 . rm:5 . u:1 ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load +@rprr_g_load_xs_u_sc ....... .. xs:1 scale:1 rm:5 . u:1 ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load +@rprr_g_load_xs_sc ....... .. xs:1 scale:1 rm:5 . . ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load +@rprr_g_load_u_sc ....... .. . scale:1 rm:5 . u:1 ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load xs=2 +@rprr_g_load_sc ....... .. . scale:1 rm:5 . . ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load xs=2 +@rpri_g_load ....... msz:2 .. imm:5 . u:1 ff:1 pg:3 rn:5 rd:5 \ + &rpri_gather_load + # Stores; user must fill in ESZ, MSZ, NREG as needed. @rprr_store ....... .. .. rm:5 ... pg:3 rn:5 rd:5 &rprr_store @rpri_store_msz ....... msz:2 .. . imm:s4 ... pg:3 rn:5 rd:5 &rpri_store @@ -759,6 +777,19 @@ LDR_zri 10000101 10 ...... 010 ... ..... ..... @rd_rn_i9 LD1R_zpri 1000010 .. 1 imm:6 1.. pg:3 rn:5 rd:5 \ &rpri_load dtype=%dtype_23_13 nreg=0 +# SVE 32-bit gather load (scalar plus 32-bit unscaled offsets) +# SVE 32-bit gather load (scalar plus 32-bit scaled offsets) +LD1_zprz 1000010 00 .0 ..... 0.. ... ..... ..... \ + @rprr_g_load_xs_u esz=2 msz=0 scale=0 +LD1_zprz 1000010 01 .. ..... 0.. ... ..... ..... \ + @rprr_g_load_xs_u_sc esz=2 msz=1 +LD1_zprz 1000010 10 .. ..... 01. ... ..... ..... \ + @rprr_g_load_xs_sc esz=2 msz=2 u=1 + +# SVE 32-bit gather load (vector plus immediate) +LD1_zpiz 1000010 .. 01 ..... 1.. ... ..... ..... \ + @rpri_g_load esz=2 + ### SVE Memory Contiguous Load Group # SVE contiguous load (scalar plus scalar) @@ -808,6 +839,32 @@ PRF_rr 1000010 -- 00 rm:5 110 --- ----- 0 ---- ### SVE Memory 64-bit Gather Group +# SVE 64-bit gather load (scalar plus 32-bit unpacked unscaled offsets) +# SVE 64-bit gather load (scalar plus 32-bit unpacked scaled offsets) +LD1_zprz 1100010 00 .0 ..... 0.. ... ..... ..... \ + @rprr_g_load_xs_u esz=3 msz=0 scale=0 +LD1_zprz 1100010 01 .. ..... 0.. ... ..... ..... \ + @rprr_g_load_xs_u_sc esz=3 msz=1 +LD1_zprz 1100010 10 .. ..... 0.. ... ..... ..... \ + @rprr_g_load_xs_u_sc esz=3 msz=2 +LD1_zprz 1100010 11 .. ..... 01. ... ..... ..... \ + @rprr_g_load_xs_sc esz=3 msz=3 u=1 + +# SVE 64-bit gather load (scalar plus 64-bit unscaled offsets) +# SVE 64-bit gather load (scalar plus 64-bit scaled offsets) +LD1_zprz 1100010 00 10 ..... 1.. ... ..... ..... \ + @rprr_g_load_u esz=3 msz=0 scale=0 +LD1_zprz 1100010 01 1. ..... 1.. ... ..... ..... \ + @rprr_g_load_u_sc esz=3 msz=1 +LD1_zprz 1100010 10 1. ..... 1.. ... ..... ..... \ + @rprr_g_load_u_sc esz=3 msz=2 +LD1_zprz 1100010 11 1. ..... 11. ... ..... ..... \ + @rprr_g_load_sc esz=3 msz=3 u=1 + +# SVE 64-bit gather load (vector plus immediate) +LD1_zpiz 1100010 .. 01 ..... 1.. ... ..... ..... \ + @rpri_g_load esz=3 + # SVE 64-bit gather prefetch (scalar plus 64-bit scaled offsets) PRF 1100010 00 11 ----- 1-- --- ----- 0 ---- diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 7622bb2af0..24f75a32d3 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3714,6 +3714,83 @@ void HELPER(sve_st4dd_r)(CPUARMState *env, void *vg, } } +/* Loads with a vector index. */ + +#define DO_LD1_ZPZ_S(NAME, TYPEI, TYPEM, FN) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + unsigned scale = simd_data(desc); \ + uintptr_t ra = GETPC(); \ + for (i = 0; i < oprsz; i++) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m = 0; \ + if (pg & 1) { \ + target_ulong off = *(TYPEI *)(vm + H1_4(i)); \ + m = FN(env, base + (off << scale), ra); \ + } \ + *(uint32_t *)(vd + H1_4(i)) = m; \ + i += 4, pg >>= 4; \ + } while (i & 15); \ + } \ +} + +#define DO_LD1_ZPZ_D(NAME, TYPEI, TYPEM, FN) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc) / 8; \ + unsigned scale = simd_data(desc); \ + uintptr_t ra = GETPC(); \ + uint64_t *d = vd, *m = vm; uint8_t *pg = vg; \ + for (i = 0; i < oprsz; i++) { \ + TYPEM mm = 0; \ + if (pg[H1(i)] & 1) { \ + target_ulong off = (TYPEI)m[i]; \ + mm = FN(env, base + (off << scale), ra); \ + } \ + d[i] = mm; \ + } \ +} + +DO_LD1_ZPZ_S(sve_ldbsu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_S(sve_ldhsu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_S(sve_ldssu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) +DO_LD1_ZPZ_S(sve_ldbss_zsu, uint32_t, int8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_S(sve_ldhss_zsu, uint32_t, int16_t, cpu_lduw_data_ra) + +DO_LD1_ZPZ_S(sve_ldbsu_zss, int32_t, uint8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_S(sve_ldhsu_zss, int32_t, uint16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_S(sve_ldssu_zss, int32_t, uint32_t, cpu_ldl_data_ra) +DO_LD1_ZPZ_S(sve_ldbss_zss, int32_t, int8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_S(sve_ldhss_zss, int32_t, int16_t, cpu_lduw_data_ra) + +DO_LD1_ZPZ_D(sve_ldbdu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhdu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsdu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) +DO_LD1_ZPZ_D(sve_ldddu_zsu, uint32_t, uint64_t, cpu_ldq_data_ra) +DO_LD1_ZPZ_D(sve_ldbds_zsu, uint32_t, int8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhds_zsu, uint32_t, int16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsds_zsu, uint32_t, int32_t, cpu_ldl_data_ra) + +DO_LD1_ZPZ_D(sve_ldbdu_zss, int32_t, uint8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhdu_zss, int32_t, uint16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsdu_zss, int32_t, uint32_t, cpu_ldl_data_ra) +DO_LD1_ZPZ_D(sve_ldddu_zss, int32_t, uint64_t, cpu_ldq_data_ra) +DO_LD1_ZPZ_D(sve_ldbds_zss, int32_t, int8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhds_zss, int32_t, int16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsds_zss, int32_t, int32_t, cpu_ldl_data_ra) + +DO_LD1_ZPZ_D(sve_ldbdu_zd, uint64_t, uint8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhdu_zd, uint64_t, uint16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsdu_zd, uint64_t, uint32_t, cpu_ldl_data_ra) +DO_LD1_ZPZ_D(sve_ldddu_zd, uint64_t, uint64_t, cpu_ldq_data_ra) +DO_LD1_ZPZ_D(sve_ldbds_zd, uint64_t, int8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhds_zd, uint64_t, int16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsds_zd, uint64_t, int32_t, cpu_ldl_data_ra) + /* Stores with a vector index. */ #define DO_ST1_ZPZ_S(NAME, TYPEI, FN) \ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index c73c3fc215..b7adc0c8ec 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -4255,6 +4255,106 @@ static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, int scale, tcg_temp_free_i32(desc); } +/* Indexed by [ff][xs][u][msz]. */ +static gen_helper_gvec_mem_scatter * const gather_load_fn32[2][2][2][3] = { + { { { gen_helper_sve_ldbss_zsu, + gen_helper_sve_ldhss_zsu, + NULL, }, + { gen_helper_sve_ldbsu_zsu, + gen_helper_sve_ldhsu_zsu, + gen_helper_sve_ldssu_zsu, } }, + { { gen_helper_sve_ldbss_zss, + gen_helper_sve_ldhss_zss, + NULL, }, + { gen_helper_sve_ldbsu_zss, + gen_helper_sve_ldhsu_zss, + gen_helper_sve_ldssu_zss, } } }, + /* TODO fill in first-fault handlers */ +}; + +/* Note that we overload xs=2 to indicate 64-bit offset. */ +static gen_helper_gvec_mem_scatter * const gather_load_fn64[2][3][2][4] = { + { { { gen_helper_sve_ldbds_zsu, + gen_helper_sve_ldhds_zsu, + gen_helper_sve_ldsds_zsu, + NULL, }, + { gen_helper_sve_ldbdu_zsu, + gen_helper_sve_ldhdu_zsu, + gen_helper_sve_ldsdu_zsu, + gen_helper_sve_ldddu_zsu, } }, + { { gen_helper_sve_ldbds_zss, + gen_helper_sve_ldhds_zss, + gen_helper_sve_ldsds_zss, + NULL, }, + { gen_helper_sve_ldbdu_zss, + gen_helper_sve_ldhdu_zss, + gen_helper_sve_ldsdu_zss, + gen_helper_sve_ldddu_zss, } }, + { { gen_helper_sve_ldbds_zd, + gen_helper_sve_ldhds_zd, + gen_helper_sve_ldsds_zd, + NULL, }, + { gen_helper_sve_ldbdu_zd, + gen_helper_sve_ldhdu_zd, + gen_helper_sve_ldsdu_zd, + gen_helper_sve_ldddu_zd, } } }, + /* TODO fill in first-fault handlers */ +}; + +static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a, uint32_t insn) +{ + gen_helper_gvec_mem_scatter *fn = NULL; + + if (!sve_access_check(s)) { + return true; + } + + switch (a->esz) { + case MO_32: + fn = gather_load_fn32[a->ff][a->xs][a->u][a->msz]; + break; + case MO_64: + fn = gather_load_fn64[a->ff][a->xs][a->u][a->msz]; + break; + } + assert(fn != NULL); + + do_mem_zpz(s, a->rd, a->pg, a->rm, a->scale * a->msz, + cpu_reg_sp(s, a->rn), fn); + return true; +} + +static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a, uint32_t insn) +{ + gen_helper_gvec_mem_scatter *fn = NULL; + TCGv_i64 imm; + + if (a->esz < a->msz || (a->esz == a->msz && !a->u)) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + switch (a->esz) { + case MO_32: + fn = gather_load_fn32[a->ff][0][a->u][a->msz]; + break; + case MO_64: + fn = gather_load_fn64[a->ff][2][a->u][a->msz]; + break; + } + assert(fn != NULL); + + /* Treat LD1_zpiz (zn[x] + imm) the same way as LD1_zprz (rn + zm[x]) + * by loading the immediate into the scalar parameter. + */ + imm = tcg_const_i64(a->imm << a->msz); + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, imm, fn); + tcg_temp_free_i64(imm); + return true; +} + static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a, uint32_t insn) { /* Indexed by [xs][msz]. */ From ed67eb7fa2a63b6709ec94397d833bc3686f7833 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:06 +0100 Subject: [PATCH 1786/2380] target/arm: Implement SVE first-fault gather loads Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 67 +++++++++++++++++++++++++++++ target/arm/sve_helper.c | 88 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 40 ++++++++++++++++- 3 files changed, 193 insertions(+), 2 deletions(-) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index aeb62afc34..55e8a908d4 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -1026,6 +1026,73 @@ DEF_HELPER_FLAGS_6(sve_ldhds_zd, TCG_CALL_NO_WG, DEF_HELPER_FLAGS_6(sve_ldsds_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbsu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhsu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffssu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbss_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhss_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldffbsu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhsu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffssu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbss_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhss_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldffbdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffddu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldffbdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffddu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldffbdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffddu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + DEF_HELPER_FLAGS_6(sve_stbs_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_sths_zsu, TCG_CALL_NO_WG, diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 24f75a32d3..81fc968087 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3791,6 +3791,94 @@ DO_LD1_ZPZ_D(sve_ldbds_zd, uint64_t, int8_t, cpu_ldub_data_ra) DO_LD1_ZPZ_D(sve_ldhds_zd, uint64_t, int16_t, cpu_lduw_data_ra) DO_LD1_ZPZ_D(sve_ldsds_zd, uint64_t, int32_t, cpu_ldl_data_ra) +/* First fault loads with a vector index. */ + +#ifdef CONFIG_USER_ONLY + +#define DO_LDFF1_ZPZ(NAME, TYPEE, TYPEI, TYPEM, FN, H) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + unsigned scale = simd_data(desc); \ + uintptr_t ra = GETPC(); \ + bool first = true; \ + mmap_lock(); \ + for (i = 0; i < oprsz; i++) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m = 0; \ + if (pg & 1) { \ + target_ulong off = *(TYPEI *)(vm + H(i)); \ + target_ulong addr = base + (off << scale); \ + if (!first && \ + page_check_range(addr, sizeof(TYPEM), PAGE_READ)) { \ + record_fault(env, i, oprsz); \ + goto exit; \ + } \ + m = FN(env, addr, ra); \ + first = false; \ + } \ + *(TYPEE *)(vd + H(i)) = m; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + } while (i & 15); \ + } \ + exit: \ + mmap_unlock(); \ +} + +#else + +#define DO_LDFF1_ZPZ(NAME, TYPEE, TYPEI, TYPEM, FN, H) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + g_assert_not_reached(); \ +} + +#endif + +#define DO_LDFF1_ZPZ_S(NAME, TYPEI, TYPEM, FN) \ + DO_LDFF1_ZPZ(NAME, uint32_t, TYPEI, TYPEM, FN, H1_4) +#define DO_LDFF1_ZPZ_D(NAME, TYPEI, TYPEM, FN) \ + DO_LDFF1_ZPZ(NAME, uint64_t, TYPEI, TYPEM, FN, ) + +DO_LDFF1_ZPZ_S(sve_ldffbsu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffhsu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffssu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffbss_zsu, uint32_t, int8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffhss_zsu, uint32_t, int16_t, cpu_lduw_data_ra) + +DO_LDFF1_ZPZ_S(sve_ldffbsu_zss, int32_t, uint8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffhsu_zss, int32_t, uint16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffssu_zss, int32_t, uint32_t, cpu_ldl_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffbss_zss, int32_t, int8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffhss_zss, int32_t, int16_t, cpu_lduw_data_ra) + +DO_LDFF1_ZPZ_D(sve_ldffbdu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhdu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsdu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffddu_zsu, uint32_t, uint64_t, cpu_ldq_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffbds_zsu, uint32_t, int8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhds_zsu, uint32_t, int16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsds_zsu, uint32_t, int32_t, cpu_ldl_data_ra) + +DO_LDFF1_ZPZ_D(sve_ldffbdu_zss, int32_t, uint8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhdu_zss, int32_t, uint16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsdu_zss, int32_t, uint32_t, cpu_ldl_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffddu_zss, int32_t, uint64_t, cpu_ldq_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffbds_zss, int32_t, int8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhds_zss, int32_t, int16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsds_zss, int32_t, int32_t, cpu_ldl_data_ra) + +DO_LDFF1_ZPZ_D(sve_ldffbdu_zd, uint64_t, uint8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhdu_zd, uint64_t, uint16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsdu_zd, uint64_t, uint32_t, cpu_ldl_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffddu_zd, uint64_t, uint64_t, cpu_ldq_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffbds_zd, uint64_t, int8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhds_zd, uint64_t, int16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsds_zd, uint64_t, int32_t, cpu_ldl_data_ra) + /* Stores with a vector index. */ #define DO_ST1_ZPZ_S(NAME, TYPEI, FN) \ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index b7adc0c8ec..66d8c94c58 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -4269,7 +4269,19 @@ static gen_helper_gvec_mem_scatter * const gather_load_fn32[2][2][2][3] = { { gen_helper_sve_ldbsu_zss, gen_helper_sve_ldhsu_zss, gen_helper_sve_ldssu_zss, } } }, - /* TODO fill in first-fault handlers */ + + { { { gen_helper_sve_ldffbss_zsu, + gen_helper_sve_ldffhss_zsu, + NULL, }, + { gen_helper_sve_ldffbsu_zsu, + gen_helper_sve_ldffhsu_zsu, + gen_helper_sve_ldffssu_zsu, } }, + { { gen_helper_sve_ldffbss_zss, + gen_helper_sve_ldffhss_zss, + NULL, }, + { gen_helper_sve_ldffbsu_zss, + gen_helper_sve_ldffhsu_zss, + gen_helper_sve_ldffssu_zss, } } } }; /* Note that we overload xs=2 to indicate 64-bit offset. */ @@ -4298,7 +4310,31 @@ static gen_helper_gvec_mem_scatter * const gather_load_fn64[2][3][2][4] = { gen_helper_sve_ldhdu_zd, gen_helper_sve_ldsdu_zd, gen_helper_sve_ldddu_zd, } } }, - /* TODO fill in first-fault handlers */ + + { { { gen_helper_sve_ldffbds_zsu, + gen_helper_sve_ldffhds_zsu, + gen_helper_sve_ldffsds_zsu, + NULL, }, + { gen_helper_sve_ldffbdu_zsu, + gen_helper_sve_ldffhdu_zsu, + gen_helper_sve_ldffsdu_zsu, + gen_helper_sve_ldffddu_zsu, } }, + { { gen_helper_sve_ldffbds_zss, + gen_helper_sve_ldffhds_zss, + gen_helper_sve_ldffsds_zss, + NULL, }, + { gen_helper_sve_ldffbdu_zss, + gen_helper_sve_ldffhdu_zss, + gen_helper_sve_ldffsdu_zss, + gen_helper_sve_ldffddu_zss, } }, + { { gen_helper_sve_ldffbds_zd, + gen_helper_sve_ldffhds_zd, + gen_helper_sve_ldffsds_zd, + NULL, }, + { gen_helper_sve_ldffbdu_zd, + gen_helper_sve_ldffhdu_zd, + gen_helper_sve_ldffsdu_zd, + gen_helper_sve_ldffddu_zd, } } } }; static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a, uint32_t insn) From 408ecde97bd30f8ec13f831976d0a9a6535bb569 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:07 +0100 Subject: [PATCH 1787/2380] target/arm: Implement SVE scatter store vector immediate Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 11 +++++ target/arm/translate-sve.c | 85 ++++++++++++++++++++++++++------------ 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 45016c6042..75133ce659 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -83,6 +83,7 @@ &rprr_gather_load rd pg rn rm esz msz u ff xs scale &rpri_gather_load rd pg rn imm esz msz u ff &rprr_scatter_store rd pg rn rm esz msz xs scale +&rpri_scatter_store rd pg rn imm esz msz ########################################################################### # Named instruction formats. These are generally used to @@ -219,6 +220,8 @@ &rprr_store nreg=0 @rprr_scatter_store ....... msz:2 .. rm:5 ... pg:3 rn:5 rd:5 \ &rprr_scatter_store +@rpri_scatter_store ....... msz:2 .. imm:5 ... pg:3 rn:5 rd:5 \ + &rpri_scatter_store ########################################################################### # Instruction patterns. Grouped according to the SVE encodingindex.xhtml. @@ -932,6 +935,14 @@ ST1_zprz 1110010 .. 01 ..... 101 ... ..... ..... \ ST1_zprz 1110010 .. 00 ..... 101 ... ..... ..... \ @rprr_scatter_store xs=2 esz=3 scale=0 +# SVE 64-bit scatter store (vector plus immediate) +ST1_zpiz 1110010 .. 10 ..... 101 ... ..... ..... \ + @rpri_scatter_store esz=3 + +# SVE 32-bit scatter store (vector plus immediate) +ST1_zpiz 1110010 .. 11 ..... 101 ... ..... ..... \ + @rpri_scatter_store esz=2 + # SVE 64-bit scatter store (scalar plus unpacked 32-bit scaled offset) # Require msz > 0 ST1_zprz 1110010 .. 01 ..... 100 ... ..... ..... \ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 66d8c94c58..7591358539 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -4391,32 +4391,34 @@ static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a, uint32_t insn) return true; } +/* Indexed by [xs][msz]. */ +static gen_helper_gvec_mem_scatter * const scatter_store_fn32[2][3] = { + { gen_helper_sve_stbs_zsu, + gen_helper_sve_sths_zsu, + gen_helper_sve_stss_zsu, }, + { gen_helper_sve_stbs_zss, + gen_helper_sve_sths_zss, + gen_helper_sve_stss_zss, }, +}; + +/* Note that we overload xs=2 to indicate 64-bit offset. */ +static gen_helper_gvec_mem_scatter * const scatter_store_fn64[3][4] = { + { gen_helper_sve_stbd_zsu, + gen_helper_sve_sthd_zsu, + gen_helper_sve_stsd_zsu, + gen_helper_sve_stdd_zsu, }, + { gen_helper_sve_stbd_zss, + gen_helper_sve_sthd_zss, + gen_helper_sve_stsd_zss, + gen_helper_sve_stdd_zss, }, + { gen_helper_sve_stbd_zd, + gen_helper_sve_sthd_zd, + gen_helper_sve_stsd_zd, + gen_helper_sve_stdd_zd, }, +}; + static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a, uint32_t insn) { - /* Indexed by [xs][msz]. */ - static gen_helper_gvec_mem_scatter * const fn32[2][3] = { - { gen_helper_sve_stbs_zsu, - gen_helper_sve_sths_zsu, - gen_helper_sve_stss_zsu, }, - { gen_helper_sve_stbs_zss, - gen_helper_sve_sths_zss, - gen_helper_sve_stss_zss, }, - }; - /* Note that we overload xs=2 to indicate 64-bit offset. */ - static gen_helper_gvec_mem_scatter * const fn64[3][4] = { - { gen_helper_sve_stbd_zsu, - gen_helper_sve_sthd_zsu, - gen_helper_sve_stsd_zsu, - gen_helper_sve_stdd_zsu, }, - { gen_helper_sve_stbd_zss, - gen_helper_sve_sthd_zss, - gen_helper_sve_stsd_zss, - gen_helper_sve_stdd_zss, }, - { gen_helper_sve_stbd_zd, - gen_helper_sve_sthd_zd, - gen_helper_sve_stsd_zd, - gen_helper_sve_stdd_zd, }, - }; gen_helper_gvec_mem_scatter *fn; if (a->esz < a->msz || (a->msz == 0 && a->scale)) { @@ -4427,10 +4429,10 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a, uint32_t insn) } switch (a->esz) { case MO_32: - fn = fn32[a->xs][a->msz]; + fn = scatter_store_fn32[a->xs][a->msz]; break; case MO_64: - fn = fn64[a->xs][a->msz]; + fn = scatter_store_fn64[a->xs][a->msz]; break; default: g_assert_not_reached(); @@ -4440,6 +4442,37 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a, uint32_t insn) return true; } +static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a, uint32_t insn) +{ + gen_helper_gvec_mem_scatter *fn = NULL; + TCGv_i64 imm; + + if (a->esz < a->msz) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + switch (a->esz) { + case MO_32: + fn = scatter_store_fn32[0][a->msz]; + break; + case MO_64: + fn = scatter_store_fn64[2][a->msz]; + break; + } + assert(fn != NULL); + + /* Treat ST1_zpiz (zn[x] + imm) the same way as ST1_zprz (rn + zm[x]) + * by loading the immediate into the scalar parameter. + */ + imm = tcg_const_i64(a->imm << a->msz); + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, imm, fn); + tcg_temp_free_i64(imm); + return true; +} + /* * Prefetches */ From abfdefd5bd444b629d16dcefc2b60ac8da37e87d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:07 +0100 Subject: [PATCH 1788/2380] target/arm: Implement SVE floating-point compare vectors Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 49 ++++++++++++++++++++++++++++++ target/arm/sve.decode | 11 +++++++ target/arm/sve_helper.c | 62 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 40 ++++++++++++++++++++++++ 4 files changed, 162 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 55e8a908d4..6089b3a53f 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -839,6 +839,55 @@ DEF_HELPER_FLAGS_5(sve_ucvt_ds, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_ucvt_dd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmge_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmge_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmge_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fcmgt_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmgt_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmgt_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fcmeq_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmeq_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmeq_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fcmne_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmne_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmne_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fcmuo_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmuo_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmuo_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_facge_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_facge_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_facge_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_facgt_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_facgt_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_facgt_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 75133ce659..a1bc6cb395 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -324,6 +324,17 @@ UXTH 00000100 .. 010 011 101 ... ..... ..... @rd_pg_rn SXTW 00000100 .. 010 100 101 ... ..... ..... @rd_pg_rn UXTW 00000100 .. 010 101 101 ... ..... ..... @rd_pg_rn +### SVE Floating Point Compare - Vectors Group + +# SVE floating-point compare vectors +FCMGE_ppzz 01100101 .. 0 ..... 010 ... ..... 0 .... @pd_pg_rn_rm +FCMGT_ppzz 01100101 .. 0 ..... 010 ... ..... 1 .... @pd_pg_rn_rm +FCMEQ_ppzz 01100101 .. 0 ..... 011 ... ..... 0 .... @pd_pg_rn_rm +FCMNE_ppzz 01100101 .. 0 ..... 011 ... ..... 1 .... @pd_pg_rn_rm +FCMUO_ppzz 01100101 .. 0 ..... 110 ... ..... 0 .... @pd_pg_rn_rm +FACGE_ppzz 01100101 .. 0 ..... 110 ... ..... 1 .... @pd_pg_rn_rm +FACGT_ppzz 01100101 .. 0 ..... 111 ... ..... 1 .... @pd_pg_rn_rm + ### SVE Integer Multiply-Add Group # SVE integer multiply-add writing addend (predicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 81fc968087..41d8ce6b54 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3193,6 +3193,68 @@ void HELPER(sve_fnmls_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) do_fmla_zpzzz_d(env, vg, desc, 0, INT64_MIN); } +/* Two operand floating-point comparison controlled by a predicate. + * Unlike the integer version, we are not allowed to optimistically + * compare operands, since the comparison may have side effects wrt + * the FPSR. + */ +#define DO_FPCMP_PPZZ(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ + void *status, uint32_t desc) \ +{ \ + intptr_t i = simd_oprsz(desc), j = (i - 1) >> 6; \ + uint64_t *d = vd, *g = vg; \ + do { \ + uint64_t out = 0, pg = g[j]; \ + do { \ + i -= sizeof(TYPE), out <<= sizeof(TYPE); \ + if (likely((pg >> (i & 63)) & 1)) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + TYPE mm = *(TYPE *)(vm + H(i)); \ + out |= OP(TYPE, nn, mm, status); \ + } \ + } while (i & 63); \ + d[j--] = out; \ + } while (i > 0); \ +} + +#define DO_FPCMP_PPZZ_H(NAME, OP) \ + DO_FPCMP_PPZZ(NAME##_h, float16, H1_2, OP) +#define DO_FPCMP_PPZZ_S(NAME, OP) \ + DO_FPCMP_PPZZ(NAME##_s, float32, H1_4, OP) +#define DO_FPCMP_PPZZ_D(NAME, OP) \ + DO_FPCMP_PPZZ(NAME##_d, float64, , OP) + +#define DO_FPCMP_PPZZ_ALL(NAME, OP) \ + DO_FPCMP_PPZZ_H(NAME, OP) \ + DO_FPCMP_PPZZ_S(NAME, OP) \ + DO_FPCMP_PPZZ_D(NAME, OP) + +#define DO_FCMGE(TYPE, X, Y, ST) TYPE##_compare(Y, X, ST) <= 0 +#define DO_FCMGT(TYPE, X, Y, ST) TYPE##_compare(Y, X, ST) < 0 +#define DO_FCMEQ(TYPE, X, Y, ST) TYPE##_compare_quiet(X, Y, ST) == 0 +#define DO_FCMNE(TYPE, X, Y, ST) TYPE##_compare_quiet(X, Y, ST) != 0 +#define DO_FCMUO(TYPE, X, Y, ST) \ + TYPE##_compare_quiet(X, Y, ST) == float_relation_unordered +#define DO_FACGE(TYPE, X, Y, ST) \ + TYPE##_compare(TYPE##_abs(Y), TYPE##_abs(X), ST) <= 0 +#define DO_FACGT(TYPE, X, Y, ST) \ + TYPE##_compare(TYPE##_abs(Y), TYPE##_abs(X), ST) < 0 + +DO_FPCMP_PPZZ_ALL(sve_fcmge, DO_FCMGE) +DO_FPCMP_PPZZ_ALL(sve_fcmgt, DO_FCMGT) +DO_FPCMP_PPZZ_ALL(sve_fcmeq, DO_FCMEQ) +DO_FPCMP_PPZZ_ALL(sve_fcmne, DO_FCMNE) +DO_FPCMP_PPZZ_ALL(sve_fcmuo, DO_FCMUO) +DO_FPCMP_PPZZ_ALL(sve_facge, DO_FACGE) +DO_FPCMP_PPZZ_ALL(sve_facgt, DO_FACGT) + +#undef DO_FPCMP_PPZZ_ALL +#undef DO_FPCMP_PPZZ_D +#undef DO_FPCMP_PPZZ_S +#undef DO_FPCMP_PPZZ_H +#undef DO_FPCMP_PPZZ + /* * Load contiguous data, protected by a governing predicate. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 7591358539..0397109778 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3533,6 +3533,46 @@ DO_FP3(FMULX, fmulx) #undef DO_FP3 +static bool do_fp_cmp(DisasContext *s, arg_rprr_esz *a, + gen_helper_gvec_4_ptr *fn) +{ + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_4_ptr(pred_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); + } + return true; +} + +#define DO_FPCMP(NAME, name) \ +static bool trans_##NAME##_ppzz(DisasContext *s, arg_rprr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_4_ptr * const fns[4] = { \ + NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d \ + }; \ + return do_fp_cmp(s, a, fns[a->esz]); \ +} + +DO_FPCMP(FCMGE, fcmge) +DO_FPCMP(FCMGT, fcmgt) +DO_FPCMP(FCMEQ, fcmeq) +DO_FPCMP(FCMNE, fcmne) +DO_FPCMP(FCMUO, fcmuo) +DO_FPCMP(FACGE, facge) +DO_FPCMP(FACGT, facgt) + +#undef DO_FPCMP + typedef void gen_helper_sve_fmla(TCGv_env, TCGv_ptr, TCGv_i32); static bool do_fmla(DisasContext *s, arg_rprrr_esz *a, gen_helper_sve_fmla *fn) From cc48affe83fff4b2886c064265d7103dee5e4a14 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:08 +0100 Subject: [PATCH 1789/2380] target/arm: Implement SVE floating-point arithmetic with immediate Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 56 ++++++++++++++++++++++++++++ target/arm/sve.decode | 14 +++++++ target/arm/sve_helper.c | 69 +++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 75 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 6089b3a53f..087819ec2b 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -809,6 +809,62 @@ DEF_HELPER_FLAGS_6(sve_fmulx_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fmulx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fadds_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fadds_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fadds_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fsubs_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsubs_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsubs_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmuls_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmuls_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmuls_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fsubrs_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsubrs_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsubrs_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmaxnms_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxnms_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxnms_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fminnms_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fminnms_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fminnms_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmaxs_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxs_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxs_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmins_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmins_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmins_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + DEF_HELPER_FLAGS_5(sve_scvt_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_scvt_sh, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index a1bc6cb395..267eb2dcfc 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -160,6 +160,10 @@ @rdn_pg4 ........ esz:2 .. pg:4 ... ........ rd:5 \ &rpri_esz rn=%reg_movprfx +# Two register operand, one one-bit floating-point operand. +@rdn_i1 ........ esz:2 ......... pg:3 .... imm:1 rd:5 \ + &rpri_esz rn=%reg_movprfx + # Two register operand, one encoded bitmask. @rdn_dbm ........ .. .... dbm:13 rd:5 \ &rr_dbm rn=%reg_movprfx @@ -744,6 +748,16 @@ FMULX 01100101 .. 00 1010 100 ... ..... ..... @rdn_pg_rm FDIV 01100101 .. 00 1100 100 ... ..... ..... @rdm_pg_rn # FDIVR FDIV 01100101 .. 00 1101 100 ... ..... ..... @rdn_pg_rm +# SVE floating-point arithmetic with immediate (predicated) +FADD_zpzi 01100101 .. 011 000 100 ... 0000 . ..... @rdn_i1 +FSUB_zpzi 01100101 .. 011 001 100 ... 0000 . ..... @rdn_i1 +FMUL_zpzi 01100101 .. 011 010 100 ... 0000 . ..... @rdn_i1 +FSUBR_zpzi 01100101 .. 011 011 100 ... 0000 . ..... @rdn_i1 +FMAXNM_zpzi 01100101 .. 011 100 100 ... 0000 . ..... @rdn_i1 +FMINNM_zpzi 01100101 .. 011 101 100 ... 0000 . ..... @rdn_i1 +FMAX_zpzi 01100101 .. 011 110 100 ... 0000 . ..... @rdn_i1 +FMIN_zpzi 01100101 .. 011 111 100 ... 0000 . ..... @rdn_i1 + ### SVE FP Multiply-Add Group # SVE floating-point multiply-accumulate writing addend diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 41d8ce6b54..bc23c66221 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2997,6 +2997,75 @@ DO_ZPZZ_FP(sve_fmulx_d, uint64_t, , helper_vfp_mulxd) #undef DO_ZPZZ_FP +/* Three-operand expander, with one scalar operand, controlled by + * a predicate, with the extra float_status parameter. + */ +#define DO_ZPZS_FP(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint64_t scalar, \ + void *status, uint32_t desc) \ +{ \ + intptr_t i = simd_oprsz(desc); \ + uint64_t *g = vg; \ + TYPE mm = scalar; \ + do { \ + uint64_t pg = g[(i - 1) >> 6]; \ + do { \ + i -= sizeof(TYPE); \ + if (likely((pg >> (i & 63)) & 1)) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, mm, status); \ + } \ + } while (i & 63); \ + } while (i != 0); \ +} + +DO_ZPZS_FP(sve_fadds_h, float16, H1_2, float16_add) +DO_ZPZS_FP(sve_fadds_s, float32, H1_4, float32_add) +DO_ZPZS_FP(sve_fadds_d, float64, , float64_add) + +DO_ZPZS_FP(sve_fsubs_h, float16, H1_2, float16_sub) +DO_ZPZS_FP(sve_fsubs_s, float32, H1_4, float32_sub) +DO_ZPZS_FP(sve_fsubs_d, float64, , float64_sub) + +DO_ZPZS_FP(sve_fmuls_h, float16, H1_2, float16_mul) +DO_ZPZS_FP(sve_fmuls_s, float32, H1_4, float32_mul) +DO_ZPZS_FP(sve_fmuls_d, float64, , float64_mul) + +static inline float16 subr_h(float16 a, float16 b, float_status *s) +{ + return float16_sub(b, a, s); +} + +static inline float32 subr_s(float32 a, float32 b, float_status *s) +{ + return float32_sub(b, a, s); +} + +static inline float64 subr_d(float64 a, float64 b, float_status *s) +{ + return float64_sub(b, a, s); +} + +DO_ZPZS_FP(sve_fsubrs_h, float16, H1_2, subr_h) +DO_ZPZS_FP(sve_fsubrs_s, float32, H1_4, subr_s) +DO_ZPZS_FP(sve_fsubrs_d, float64, , subr_d) + +DO_ZPZS_FP(sve_fmaxnms_h, float16, H1_2, float16_maxnum) +DO_ZPZS_FP(sve_fmaxnms_s, float32, H1_4, float32_maxnum) +DO_ZPZS_FP(sve_fmaxnms_d, float64, , float64_maxnum) + +DO_ZPZS_FP(sve_fminnms_h, float16, H1_2, float16_minnum) +DO_ZPZS_FP(sve_fminnms_s, float32, H1_4, float32_minnum) +DO_ZPZS_FP(sve_fminnms_d, float64, , float64_minnum) + +DO_ZPZS_FP(sve_fmaxs_h, float16, H1_2, float16_max) +DO_ZPZS_FP(sve_fmaxs_s, float32, H1_4, float32_max) +DO_ZPZS_FP(sve_fmaxs_d, float64, , float64_max) + +DO_ZPZS_FP(sve_fmins_h, float16, H1_2, float16_min) +DO_ZPZS_FP(sve_fmins_s, float32, H1_4, float32_min) +DO_ZPZS_FP(sve_fmins_d, float64, , float64_min) + /* Fully general two-operand expander, controlled by a predicate, * With the extra float_status parameter. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 0397109778..1b467a5ddc 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -32,6 +32,7 @@ #include "exec/log.h" #include "trace-tcg.h" #include "translate-a64.h" +#include "fpu/softfloat.h" typedef void GVecGen2sFn(unsigned, uint32_t, uint32_t, @@ -3533,6 +3534,80 @@ DO_FP3(FMULX, fmulx) #undef DO_FP3 +typedef void gen_helper_sve_fp2scalar(TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_i64, TCGv_ptr, TCGv_i32); + +static void do_fp_scalar(DisasContext *s, int zd, int zn, int pg, bool is_fp16, + TCGv_i64 scalar, gen_helper_sve_fp2scalar *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_zd, t_zn, t_pg, status; + TCGv_i32 desc; + + t_zd = tcg_temp_new_ptr(); + t_zn = tcg_temp_new_ptr(); + t_pg = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, zd)); + tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, zn)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + + status = get_fpstatus_ptr(is_fp16); + desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + fn(t_zd, t_zn, t_pg, scalar, status, desc); + + tcg_temp_free_i32(desc); + tcg_temp_free_ptr(status); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_ptr(t_zn); + tcg_temp_free_ptr(t_zd); +} + +static void do_fp_imm(DisasContext *s, arg_rpri_esz *a, uint64_t imm, + gen_helper_sve_fp2scalar *fn) +{ + TCGv_i64 temp = tcg_const_i64(imm); + do_fp_scalar(s, a->rd, a->rn, a->pg, a->esz == MO_16, temp, fn); + tcg_temp_free_i64(temp); +} + +#define DO_FP_IMM(NAME, name, const0, const1) \ +static bool trans_##NAME##_zpzi(DisasContext *s, arg_rpri_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_sve_fp2scalar * const fns[3] = { \ + gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, \ + gen_helper_sve_##name##_d \ + }; \ + static uint64_t const val[3][2] = { \ + { float16_##const0, float16_##const1 }, \ + { float32_##const0, float32_##const1 }, \ + { float64_##const0, float64_##const1 }, \ + }; \ + if (a->esz == 0) { \ + return false; \ + } \ + if (sve_access_check(s)) { \ + do_fp_imm(s, a, val[a->esz - 1][a->imm], fns[a->esz - 1]); \ + } \ + return true; \ +} + +#define float16_two make_float16(0x4000) +#define float32_two make_float32(0x40000000) +#define float64_two make_float64(0x4000000000000000ULL) + +DO_FP_IMM(FADD, fadds, half, one) +DO_FP_IMM(FSUB, fsubs, half, one) +DO_FP_IMM(FMUL, fmuls, half, two) +DO_FP_IMM(FSUBR, fsubrs, half, one) +DO_FP_IMM(FMAXNM, fmaxnms, zero, one) +DO_FP_IMM(FMINNM, fminnms, zero, one) +DO_FP_IMM(FMAX, fmaxs, zero, one) +DO_FP_IMM(FMIN, fmins, zero, one) + +#undef DO_FP_IMM + static bool do_fp_cmp(DisasContext *s, arg_rprr_esz *a, gen_helper_gvec_4_ptr *fn) { From ca40a6e6e390eb1cad7ade881dc7c622793f9324 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:08 +0100 Subject: [PATCH 1790/2380] target/arm: Implement SVE Floating Point Multiply Indexed Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 14 +++++++++++ target/arm/sve.decode | 19 +++++++++++++++ target/arm/translate-sve.c | 50 ++++++++++++++++++++++++++++++++++++++ target/arm/vec_helper.c | 48 ++++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+) diff --git a/target/arm/helper.h b/target/arm/helper.h index 879a7229e9..56439ac1e4 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -620,6 +620,20 @@ DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_ftsmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(gvec_fmla_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(gvec_fmla_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(gvec_fmla_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "helper-a64.h" #include "helper-sve.h" diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 267eb2dcfc..15fa790d5b 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -29,6 +29,7 @@ %imm9_16_10 16:s6 10:3 %size_23 23:2 %dtype_23_13 23:2 13:2 +%index3_22_19 22:1 19:2 # A combination of tsz:imm3 -- extract esize. %tszimm_esz 22:2 5:5 !function=tszimm_esz @@ -716,6 +717,24 @@ UMIN_zzi 00100101 .. 101 011 110 ........ ..... @rdn_i8u # SVE integer multiply immediate (unpredicated) MUL_zzi 00100101 .. 110 000 110 ........ ..... @rdn_i8s +### SVE FP Multiply-Add Indexed Group + +# SVE floating-point multiply-add (indexed) +FMLA_zzxz 01100100 0.1 .. rm:3 00000 sub:1 rn:5 rd:5 \ + ra=%reg_movprfx index=%index3_22_19 esz=1 +FMLA_zzxz 01100100 101 index:2 rm:3 00000 sub:1 rn:5 rd:5 \ + ra=%reg_movprfx esz=2 +FMLA_zzxz 01100100 111 index:1 rm:4 00000 sub:1 rn:5 rd:5 \ + ra=%reg_movprfx esz=3 + +### SVE FP Multiply Indexed Group + +# SVE floating-point multiply (indexed) +FMUL_zzx 01100100 0.1 .. rm:3 001000 rn:5 rd:5 \ + index=%index3_22_19 esz=1 +FMUL_zzx 01100100 101 index:2 rm:3 001000 rn:5 rd:5 esz=2 +FMUL_zzx 01100100 111 index:1 rm:4 001000 rn:5 rd:5 esz=3 + ### SVE FP Accumulating Reduction Group # SVE floating-point serial reduction (predicated) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 1b467a5ddc..e90f593f6c 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3400,6 +3400,56 @@ DO_ZZI(UMIN, umin) #undef DO_ZZI +/* + *** SVE Floating Point Multiply-Add Indexed Group + */ + +static bool trans_FMLA_zzxz(DisasContext *s, arg_FMLA_zzxz *a, uint32_t insn) +{ + static gen_helper_gvec_4_ptr * const fns[3] = { + gen_helper_gvec_fmla_idx_h, + gen_helper_gvec_fmla_idx_s, + gen_helper_gvec_fmla_idx_d, + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vec_full_reg_offset(s, a->ra), + status, vsz, vsz, (a->index << 1) | a->sub, + fns[a->esz - 1]); + tcg_temp_free_ptr(status); + } + return true; +} + +/* + *** SVE Floating Point Multiply Indexed Group + */ + +static bool trans_FMUL_zzx(DisasContext *s, arg_FMUL_zzx *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[3] = { + gen_helper_gvec_fmul_idx_h, + gen_helper_gvec_fmul_idx_s, + gen_helper_gvec_fmul_idx_d, + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + status, vsz, vsz, a->index, fns[a->esz - 1]); + tcg_temp_free_ptr(status); + } + return true; +} + /* *** SVE Floating Point Accumulating Reduction Group */ diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index f504dd53c8..97af75a61b 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -495,3 +495,51 @@ DO_3OP(gvec_rsqrts_d, helper_rsqrtsf_f64, float64) #endif #undef DO_3OP + +/* For the indexed ops, SVE applies the index per 128-bit vector segment. + * For AdvSIMD, there is of course only one such vector segment. + */ + +#define DO_MUL_IDX(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +{ \ + intptr_t i, j, oprsz = simd_oprsz(desc), segment = 16 / sizeof(TYPE); \ + intptr_t idx = simd_data(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ + TYPE mm = m[H(i + idx)]; \ + for (j = 0; j < segment; j++) { \ + d[i + j] = TYPE##_mul(n[i + j], mm, stat); \ + } \ + } \ +} + +DO_MUL_IDX(gvec_fmul_idx_h, float16, H2) +DO_MUL_IDX(gvec_fmul_idx_s, float32, H4) +DO_MUL_IDX(gvec_fmul_idx_d, float64, ) + +#undef DO_MUL_IDX + +#define DO_FMLA_IDX(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, \ + void *stat, uint32_t desc) \ +{ \ + intptr_t i, j, oprsz = simd_oprsz(desc), segment = 16 / sizeof(TYPE); \ + TYPE op1_neg = extract32(desc, SIMD_DATA_SHIFT, 1); \ + intptr_t idx = desc >> (SIMD_DATA_SHIFT + 1); \ + TYPE *d = vd, *n = vn, *m = vm, *a = va; \ + op1_neg <<= (8 * sizeof(TYPE) - 1); \ + for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ + TYPE mm = m[H(i + idx)]; \ + for (j = 0; j < segment; j++) { \ + d[i + j] = TYPE##_muladd(n[i + j] ^ op1_neg, \ + mm, a[i + j], 0, stat); \ + } \ + } \ +} + +DO_FMLA_IDX(gvec_fmla_idx_h, float16, H2) +DO_FMLA_IDX(gvec_fmla_idx_s, float32, H4) +DO_FMLA_IDX(gvec_fmla_idx_d, float64, ) + +#undef DO_FMLA_IDX From 23fbe79faa38cb4acc59f956a63feba3c2cc73ac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:08 +0100 Subject: [PATCH 1791/2380] target/arm: Implement SVE FP Fast Reduction Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-20-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 35 ++++++++++++++++++++++ target/arm/sve.decode | 8 +++++ target/arm/sve_helper.c | 61 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 57 +++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 087819ec2b..ff69d143a0 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -725,6 +725,41 @@ DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_faddv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_faddv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_faddv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fmaxnmv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fmaxnmv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fmaxnmv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fminnmv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fminnmv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fminnmv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fmaxv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fmaxv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fmaxv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fminv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fminv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fminv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_fadda_h, TCG_CALL_NO_RWG, i64, i64, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_fadda_s, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 15fa790d5b..66b0fd0cc4 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -735,6 +735,14 @@ FMUL_zzx 01100100 0.1 .. rm:3 001000 rn:5 rd:5 \ FMUL_zzx 01100100 101 index:2 rm:3 001000 rn:5 rd:5 esz=2 FMUL_zzx 01100100 111 index:1 rm:4 001000 rn:5 rd:5 esz=3 +### SVE FP Fast Reduction Group + +FADDV 01100101 .. 000 000 001 ... ..... ..... @rd_pg_rn +FMAXNMV 01100101 .. 000 100 001 ... ..... ..... @rd_pg_rn +FMINNMV 01100101 .. 000 101 001 ... ..... ..... @rd_pg_rn +FMAXV 01100101 .. 000 110 001 ... ..... ..... @rd_pg_rn +FMINV 01100101 .. 000 111 001 ... ..... ..... @rd_pg_rn + ### SVE FP Accumulating Reduction Group # SVE floating-point serial reduction (predicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index bc23c66221..4c44d52a23 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -2852,6 +2852,67 @@ uint32_t HELPER(sve_while)(void *vd, uint32_t count, uint32_t pred_desc) return predtest_ones(d, oprsz, esz_mask); } +/* Recursive reduction on a function; + * C.f. the ARM ARM function ReducePredicated. + * + * While it would be possible to write this without the DATA temporary, + * it is much simpler to process the predicate register this way. + * The recursion is bounded to depth 7 (128 fp16 elements), so there's + * little to gain with a more complex non-recursive form. + */ +#define DO_REDUCE(NAME, TYPE, H, FUNC, IDENT) \ +static TYPE NAME##_reduce(TYPE *data, float_status *status, uintptr_t n) \ +{ \ + if (n == 1) { \ + return *data; \ + } else { \ + uintptr_t half = n / 2; \ + TYPE lo = NAME##_reduce(data, status, half); \ + TYPE hi = NAME##_reduce(data + half, status, half); \ + return TYPE##_##FUNC(lo, hi, status); \ + } \ +} \ +uint64_t HELPER(NAME)(void *vn, void *vg, void *vs, uint32_t desc) \ +{ \ + uintptr_t i, oprsz = simd_oprsz(desc), maxsz = simd_maxsz(desc); \ + TYPE data[sizeof(ARMVectorReg) / sizeof(TYPE)]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)((void *)data + i) = (pg & 1 ? nn : IDENT); \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ + for (; i < maxsz; i += sizeof(TYPE)) { \ + *(TYPE *)((void *)data + i) = IDENT; \ + } \ + return NAME##_reduce(data, vs, maxsz / sizeof(TYPE)); \ +} + +DO_REDUCE(sve_faddv_h, float16, H1_2, add, float16_zero) +DO_REDUCE(sve_faddv_s, float32, H1_4, add, float32_zero) +DO_REDUCE(sve_faddv_d, float64, , add, float64_zero) + +/* Identity is floatN_default_nan, without the function call. */ +DO_REDUCE(sve_fminnmv_h, float16, H1_2, minnum, 0x7E00) +DO_REDUCE(sve_fminnmv_s, float32, H1_4, minnum, 0x7FC00000) +DO_REDUCE(sve_fminnmv_d, float64, , minnum, 0x7FF8000000000000ULL) + +DO_REDUCE(sve_fmaxnmv_h, float16, H1_2, maxnum, 0x7E00) +DO_REDUCE(sve_fmaxnmv_s, float32, H1_4, maxnum, 0x7FC00000) +DO_REDUCE(sve_fmaxnmv_d, float64, , maxnum, 0x7FF8000000000000ULL) + +DO_REDUCE(sve_fminv_h, float16, H1_2, min, float16_infinity) +DO_REDUCE(sve_fminv_s, float32, H1_4, min, float32_infinity) +DO_REDUCE(sve_fminv_d, float64, , min, float64_infinity) + +DO_REDUCE(sve_fmaxv_h, float16, H1_2, max, float16_chs(float16_infinity)) +DO_REDUCE(sve_fmaxv_s, float32, H1_4, max, float32_chs(float32_infinity)) +DO_REDUCE(sve_fmaxv_d, float64, , max, float64_chs(float64_infinity)) + +#undef DO_REDUCE + uint64_t HELPER(sve_fadda_h)(uint64_t nn, void *vm, void *vg, void *status, uint32_t desc) { diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index e90f593f6c..b026ee3420 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3450,6 +3450,63 @@ static bool trans_FMUL_zzx(DisasContext *s, arg_FMUL_zzx *a, uint32_t insn) return true; } +/* + *** SVE Floating Point Fast Reduction Group + */ + +typedef void gen_helper_fp_reduce(TCGv_i64, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i32); + +static void do_reduce(DisasContext *s, arg_rpr_esz *a, + gen_helper_fp_reduce *fn) +{ + unsigned vsz = vec_full_reg_size(s); + unsigned p2vsz = pow2ceil(vsz); + TCGv_i32 t_desc = tcg_const_i32(simd_desc(vsz, p2vsz, 0)); + TCGv_ptr t_zn, t_pg, status; + TCGv_i64 temp; + + temp = tcg_temp_new_i64(); + t_zn = tcg_temp_new_ptr(); + t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->pg)); + status = get_fpstatus_ptr(a->esz == MO_16); + + fn(temp, t_zn, t_pg, status, t_desc); + tcg_temp_free_ptr(t_zn); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_ptr(status); + tcg_temp_free_i32(t_desc); + + write_fp_dreg(s, a->rd, temp); + tcg_temp_free_i64(temp); +} + +#define DO_VPZ(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_fp_reduce * const fns[3] = { \ + gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, \ + gen_helper_sve_##name##_d, \ + }; \ + if (a->esz == 0) { \ + return false; \ + } \ + if (sve_access_check(s)) { \ + do_reduce(s, a, fns[a->esz - 1]); \ + } \ + return true; \ +} + +DO_VPZ(FADDV, faddv) +DO_VPZ(FMINNMV, fminnmv) +DO_VPZ(FMAXNMV, fmaxnmv) +DO_VPZ(FMINV, fminv) +DO_VPZ(FMAXV, fmaxv) + /* *** SVE Floating Point Accumulating Reduction Group */ From 3887c0388d39930ab419d4ae6e8ca5ea67a74ad5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:09 +0100 Subject: [PATCH 1792/2380] target/arm: Implement SVE Floating Point Unary Operations - Unpredicated Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-21-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 8 +++++++ target/arm/sve.decode | 5 ++++ target/arm/translate-sve.c | 47 ++++++++++++++++++++++++++++++++++++++ target/arm/vec_helper.c | 20 ++++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/target/arm/helper.h b/target/arm/helper.h index 56439ac1e4..ad9cb6c7d5 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -601,6 +601,14 @@ DEF_HELPER_FLAGS_5(gvec_fcmlas_idx, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_fcmlad, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_frsqrte_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 66b0fd0cc4..ca93bdb2b3 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -743,6 +743,11 @@ FMINNMV 01100101 .. 000 101 001 ... ..... ..... @rd_pg_rn FMAXV 01100101 .. 000 110 001 ... ..... ..... @rd_pg_rn FMINV 01100101 .. 000 111 001 ... ..... ..... @rd_pg_rn +## SVE Floating Point Unary Operations - Unpredicated Group + +FRECPE 01100101 .. 001 110 001100 ..... ..... @rd_rn +FRSQRTE 01100101 .. 001 111 001100 ..... ..... @rd_rn + ### SVE FP Accumulating Reduction Group # SVE floating-point serial reduction (predicated) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index b026ee3420..b801909502 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3507,6 +3507,53 @@ DO_VPZ(FMAXNMV, fmaxnmv) DO_VPZ(FMINV, fminv) DO_VPZ(FMAXV, fmaxv) +/* + *** SVE Floating Point Unary Operations - Unpredicated Group + */ + +static void do_zz_fp(DisasContext *s, arg_rr_esz *a, gen_helper_gvec_2_ptr *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + + tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); +} + +static bool trans_FRECPE(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_2_ptr * const fns[3] = { + gen_helper_gvec_frecpe_h, + gen_helper_gvec_frecpe_s, + gen_helper_gvec_frecpe_d, + }; + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + do_zz_fp(s, a, fns[a->esz - 1]); + } + return true; +} + +static bool trans_FRSQRTE(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_2_ptr * const fns[3] = { + gen_helper_gvec_frsqrte_h, + gen_helper_gvec_frsqrte_s, + gen_helper_gvec_frsqrte_d, + }; + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + do_zz_fp(s, a, fns[a->esz - 1]); + } + return true; +} + /* *** SVE Floating Point Accumulating Reduction Group */ diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index 97af75a61b..073e5c58e7 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -427,6 +427,26 @@ void HELPER(gvec_fcmlad)(void *vd, void *vn, void *vm, clear_tail(d, opr_sz, simd_maxsz(desc)); } +#define DO_2OP(NAME, FUNC, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + TYPE *d = vd, *n = vn; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = FUNC(n[i], stat); \ + } \ +} + +DO_2OP(gvec_frecpe_h, helper_recpe_f16, float16) +DO_2OP(gvec_frecpe_s, helper_recpe_f32, float32) +DO_2OP(gvec_frecpe_d, helper_recpe_f64, float64) + +DO_2OP(gvec_frsqrte_h, helper_rsqrte_f16, float16) +DO_2OP(gvec_frsqrte_s, helper_rsqrte_f32, float32) +DO_2OP(gvec_frsqrte_d, helper_rsqrte_f64, float64) + +#undef DO_2OP + /* Floating-point trigonometric starting value. * See the ARM ARM pseudocode function FPTrigSMul. */ From 4d2e2a03384a43c641e0cbca7ac79d7d0c50f666 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:09 +0100 Subject: [PATCH 1793/2380] target/arm: Implement SVE FP Compare with Zero Group Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-22-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 42 +++++++++++++++++++++++++++++++++++++ target/arm/sve.decode | 10 +++++++++ target/arm/sve_helper.c | 43 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 43 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index ff69d143a0..44a98440c9 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -767,6 +767,48 @@ DEF_HELPER_FLAGS_5(sve_fadda_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_fadda_d, TCG_CALL_NO_RWG, i64, i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmge0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmge0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmge0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmgt0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmgt0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmgt0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmlt0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmlt0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmlt0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmle0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmle0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmle0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmeq0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmeq0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmeq0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmne0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmne0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmne0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_6(sve_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_6(sve_fadd_s, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index ca93bdb2b3..a774becd6c 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -140,6 +140,7 @@ # One register operand, with governing predicate, vector element size @rd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 &rpr_esz @rd_pg4_pn ........ esz:2 ... ... .. pg:4 . rn:4 rd:5 &rpr_esz +@pd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 . rd:4 &rpr_esz # One register operand, with governing predicate, no vector element size @rd_pg_rn_e0 ........ .. ... ... ... pg:3 rn:5 rd:5 &rpr_esz esz=0 @@ -748,6 +749,15 @@ FMINV 01100101 .. 000 111 001 ... ..... ..... @rd_pg_rn FRECPE 01100101 .. 001 110 001100 ..... ..... @rd_rn FRSQRTE 01100101 .. 001 111 001100 ..... ..... @rd_rn +### SVE FP Compare with Zero Group + +FCMGE_ppz0 01100101 .. 0100 00 001 ... ..... 0 .... @pd_pg_rn +FCMGT_ppz0 01100101 .. 0100 00 001 ... ..... 1 .... @pd_pg_rn +FCMLT_ppz0 01100101 .. 0100 01 001 ... ..... 0 .... @pd_pg_rn +FCMLE_ppz0 01100101 .. 0100 01 001 ... ..... 1 .... @pd_pg_rn +FCMEQ_ppz0 01100101 .. 0100 10 001 ... ..... 0 .... @pd_pg_rn +FCMNE_ppz0 01100101 .. 0100 11 001 ... ..... 0 .... @pd_pg_rn + ### SVE FP Accumulating Reduction Group # SVE floating-point serial reduction (predicated) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 4c44d52a23..0486cb1e5e 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3362,6 +3362,8 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ #define DO_FCMGE(TYPE, X, Y, ST) TYPE##_compare(Y, X, ST) <= 0 #define DO_FCMGT(TYPE, X, Y, ST) TYPE##_compare(Y, X, ST) < 0 +#define DO_FCMLE(TYPE, X, Y, ST) TYPE##_compare(X, Y, ST) <= 0 +#define DO_FCMLT(TYPE, X, Y, ST) TYPE##_compare(X, Y, ST) < 0 #define DO_FCMEQ(TYPE, X, Y, ST) TYPE##_compare_quiet(X, Y, ST) == 0 #define DO_FCMNE(TYPE, X, Y, ST) TYPE##_compare_quiet(X, Y, ST) != 0 #define DO_FCMUO(TYPE, X, Y, ST) \ @@ -3385,6 +3387,47 @@ DO_FPCMP_PPZZ_ALL(sve_facgt, DO_FACGT) #undef DO_FPCMP_PPZZ_H #undef DO_FPCMP_PPZZ +/* One operand floating-point comparison against zero, controlled + * by a predicate. + */ +#define DO_FPCMP_PPZ0(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, \ + void *status, uint32_t desc) \ +{ \ + intptr_t i = simd_oprsz(desc), j = (i - 1) >> 6; \ + uint64_t *d = vd, *g = vg; \ + do { \ + uint64_t out = 0, pg = g[j]; \ + do { \ + i -= sizeof(TYPE), out <<= sizeof(TYPE); \ + if ((pg >> (i & 63)) & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + out |= OP(TYPE, nn, 0, status); \ + } \ + } while (i & 63); \ + d[j--] = out; \ + } while (i > 0); \ +} + +#define DO_FPCMP_PPZ0_H(NAME, OP) \ + DO_FPCMP_PPZ0(NAME##_h, float16, H1_2, OP) +#define DO_FPCMP_PPZ0_S(NAME, OP) \ + DO_FPCMP_PPZ0(NAME##_s, float32, H1_4, OP) +#define DO_FPCMP_PPZ0_D(NAME, OP) \ + DO_FPCMP_PPZ0(NAME##_d, float64, , OP) + +#define DO_FPCMP_PPZ0_ALL(NAME, OP) \ + DO_FPCMP_PPZ0_H(NAME, OP) \ + DO_FPCMP_PPZ0_S(NAME, OP) \ + DO_FPCMP_PPZ0_D(NAME, OP) + +DO_FPCMP_PPZ0_ALL(sve_fcmge0, DO_FCMGE) +DO_FPCMP_PPZ0_ALL(sve_fcmgt0, DO_FCMGT) +DO_FPCMP_PPZ0_ALL(sve_fcmle0, DO_FCMLE) +DO_FPCMP_PPZ0_ALL(sve_fcmlt0, DO_FCMLT) +DO_FPCMP_PPZ0_ALL(sve_fcmeq0, DO_FCMEQ) +DO_FPCMP_PPZ0_ALL(sve_fcmne0, DO_FCMNE) + /* * Load contiguous data, protected by a governing predicate. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index b801909502..3ef9cd21e0 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3554,6 +3554,49 @@ static bool trans_FRSQRTE(DisasContext *s, arg_rr_esz *a, uint32_t insn) return true; } +/* + *** SVE Floating Point Compare with Zero Group + */ + +static void do_ppz_fp(DisasContext *s, arg_rpr_esz *a, + gen_helper_gvec_3_ptr *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + + tcg_gen_gvec_3_ptr(pred_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + pred_full_reg_offset(s, a->pg), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); +} + +#define DO_PPZ(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_3_ptr * const fns[3] = { \ + gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, \ + gen_helper_sve_##name##_d, \ + }; \ + if (a->esz == 0) { \ + return false; \ + } \ + if (sve_access_check(s)) { \ + do_ppz_fp(s, a, fns[a->esz - 1]); \ + } \ + return true; \ +} + +DO_PPZ(FCMGE_ppz0, fcmge0) +DO_PPZ(FCMGT_ppz0, fcmgt0) +DO_PPZ(FCMLE_ppz0, fcmle0) +DO_PPZ(FCMLT_ppz0, fcmlt0) +DO_PPZ(FCMEQ_ppz0, fcmeq0) +DO_PPZ(FCMNE_ppz0, fcmne0) + +#undef DO_PPZ + /* *** SVE Floating Point Accumulating Reduction Group */ From 67fcd9ad35d2b38630ee34e8ced8878d334c74fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:09 +0100 Subject: [PATCH 1794/2380] target/arm: Implement SVE floating-point trig multiply-add coefficient Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-23-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 4 +++ target/arm/sve.decode | 3 ++ target/arm/sve_helper.c | 70 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 27 +++++++++++++++ 4 files changed, 104 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 44a98440c9..aca137fc37 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -1037,6 +1037,10 @@ DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_ld1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index a774becd6c..fdcc252eaa 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -800,6 +800,9 @@ FMINNM_zpzi 01100101 .. 011 101 100 ... 0000 . ..... @rdn_i1 FMAX_zpzi 01100101 .. 011 110 100 ... 0000 . ..... @rdn_i1 FMIN_zpzi 01100101 .. 011 111 100 ... 0000 . ..... @rdn_i1 +# SVE floating-point trig multiply-add coefficient +FTMAD 01100101 esz:2 010 imm:3 100000 rm:5 rd:5 rn=%reg_movprfx + ### SVE FP Multiply-Add Group # SVE floating-point multiply-accumulate writing addend diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 0486cb1e5e..79358c804b 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3428,6 +3428,76 @@ DO_FPCMP_PPZ0_ALL(sve_fcmlt0, DO_FCMLT) DO_FPCMP_PPZ0_ALL(sve_fcmeq0, DO_FCMEQ) DO_FPCMP_PPZ0_ALL(sve_fcmne0, DO_FCMNE) +/* FP Trig Multiply-Add. */ + +void HELPER(sve_ftmad_h)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +{ + static const float16 coeff[16] = { + 0x3c00, 0xb155, 0x2030, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x3c00, 0xb800, 0x293a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float16); + intptr_t x = simd_data(desc); + float16 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { + float16 mm = m[i]; + intptr_t xx = x; + if (float16_is_neg(mm)) { + mm = float16_abs(mm); + xx += 8; + } + d[i] = float16_muladd(n[i], mm, coeff[xx], 0, vs); + } +} + +void HELPER(sve_ftmad_s)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +{ + static const float32 coeff[16] = { + 0x3f800000, 0xbe2aaaab, 0x3c088886, 0xb95008b9, + 0x36369d6d, 0x00000000, 0x00000000, 0x00000000, + 0x3f800000, 0xbf000000, 0x3d2aaaa6, 0xbab60705, + 0x37cd37cc, 0x00000000, 0x00000000, 0x00000000, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float32); + intptr_t x = simd_data(desc); + float32 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { + float32 mm = m[i]; + intptr_t xx = x; + if (float32_is_neg(mm)) { + mm = float32_abs(mm); + xx += 8; + } + d[i] = float32_muladd(n[i], mm, coeff[xx], 0, vs); + } +} + +void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +{ + static const float64 coeff[16] = { + 0x3ff0000000000000ull, 0xbfc5555555555543ull, + 0x3f8111111110f30cull, 0xbf2a01a019b92fc6ull, + 0x3ec71de351f3d22bull, 0xbe5ae5e2b60f7b91ull, + 0x3de5d8408868552full, 0x0000000000000000ull, + 0x3ff0000000000000ull, 0xbfe0000000000000ull, + 0x3fa5555555555536ull, 0xbf56c16c16c13a0bull, + 0x3efa01a019b1e8d8ull, 0xbe927e4f7282f468ull, + 0x3e21ee96d2641b13ull, 0xbda8f76380fbb401ull, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float64); + intptr_t x = simd_data(desc); + float64 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { + float64 mm = m[i]; + intptr_t xx = x; + if (float64_is_neg(mm)) { + mm = float64_abs(mm); + xx += 8; + } + d[i] = float64_muladd(n[i], mm, coeff[xx], 0, vs); + } +} + /* * Load contiguous data, protected by a governing predicate. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 3ef9cd21e0..50c54c171b 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3597,6 +3597,33 @@ DO_PPZ(FCMNE_ppz0, fcmne0) #undef DO_PPZ +/* + *** SVE floating-point trig multiply-add coefficient + */ + +static bool trans_FTMAD(DisasContext *s, arg_FTMAD *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[3] = { + gen_helper_sve_ftmad_h, + gen_helper_sve_ftmad_s, + gen_helper_sve_ftmad_d, + }; + + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + status, vsz, vsz, a->imm, fns[a->esz - 1]); + tcg_temp_free_ptr(status); + } + return true; +} + /* *** SVE Floating Point Accumulating Reduction Group */ From 46d33d1e3c9c5d56d57056db55010de52c173902 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:10 +0100 Subject: [PATCH 1795/2380] target/arm: Implement SVE floating-point convert precision Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-24-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 13 +++++++++ target/arm/sve.decode | 8 ++++++ target/arm/sve_helper.c | 55 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 30 +++++++++++++++++++++ 4 files changed, 106 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index aca137fc37..4c379dbb05 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -942,6 +942,19 @@ DEF_HELPER_FLAGS_6(sve_fmins_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fmins_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_sh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_dh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_hs, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_ds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_hd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_sd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_scvt_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_scvt_sh, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index fdcc252eaa..18c174e92d 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -821,6 +821,14 @@ FNMLS_zpzzz 01100101 .. 1 ..... 111 ... ..... ..... @rdn_pg_rm_ra ### SVE FP Unary Operations Predicated Group +# SVE floating-point convert precision +FCVT_sh 01100101 10 0010 00 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_hs 01100101 10 0010 01 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_dh 01100101 11 0010 00 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_hd 01100101 11 0010 01 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_ds 01100101 11 0010 10 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_sd 01100101 11 0010 11 101 ... ..... ..... @rd_pg_rn_e0 + # SVE integer convert to floating-point SCVTF_hh 01100101 01 010 01 0 101 ... ..... ..... @rd_pg_rn_e0 SCVTF_sh 01100101 01 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 79358c804b..4b36c1eecf 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3147,6 +3147,61 @@ void HELPER(NAME)(void *vd, void *vn, void *vg, void *status, uint32_t desc) \ } while (i != 0); \ } +/* SVE fp16 conversions always use IEEE mode. Like AdvSIMD, they ignore + * FZ16. When converting from fp16, this affects flushing input denormals; + * when converting to fp16, this affects flushing output denormals. + */ +static inline float32 sve_f16_to_f32(float16 f, float_status *fpst) +{ + flag save = get_flush_inputs_to_zero(fpst); + float32 ret; + + set_flush_inputs_to_zero(false, fpst); + ret = float16_to_float32(f, true, fpst); + set_flush_inputs_to_zero(save, fpst); + return ret; +} + +static inline float64 sve_f16_to_f64(float16 f, float_status *fpst) +{ + flag save = get_flush_inputs_to_zero(fpst); + float64 ret; + + set_flush_inputs_to_zero(false, fpst); + ret = float16_to_float64(f, true, fpst); + set_flush_inputs_to_zero(save, fpst); + return ret; +} + +static inline float16 sve_f32_to_f16(float32 f, float_status *fpst) +{ + flag save = get_flush_to_zero(fpst); + float16 ret; + + set_flush_to_zero(false, fpst); + ret = float32_to_float16(f, true, fpst); + set_flush_to_zero(save, fpst); + return ret; +} + +static inline float16 sve_f64_to_f16(float64 f, float_status *fpst) +{ + flag save = get_flush_to_zero(fpst); + float16 ret; + + set_flush_to_zero(false, fpst); + ret = float64_to_float16(f, true, fpst); + set_flush_to_zero(save, fpst); + return ret; +} + +DO_ZPZ_FP(sve_fcvt_sh, uint32_t, H1_4, sve_f32_to_f16) +DO_ZPZ_FP(sve_fcvt_hs, uint32_t, H1_4, sve_f16_to_f32) +DO_ZPZ_FP(sve_fcvt_dh, uint64_t, , sve_f64_to_f16) +DO_ZPZ_FP(sve_fcvt_hd, uint64_t, , sve_f16_to_f64) +DO_ZPZ_FP(sve_fcvt_ds, uint64_t, , float64_to_float32) +DO_ZPZ_FP(sve_fcvt_sd, uint64_t, , float32_to_float64) + DO_ZPZ_FP(sve_scvt_hh, uint16_t, H1_2, int16_to_float16) DO_ZPZ_FP(sve_scvt_sh, uint32_t, H1_4, int32_to_float16) DO_ZPZ_FP(sve_scvt_ss, uint32_t, H1_4, int32_to_float32) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 50c54c171b..f2a9057369 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3940,6 +3940,36 @@ static bool do_zpz_ptr(DisasContext *s, int rd, int rn, int pg, return true; } +static bool trans_FCVT_sh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvt_sh); +} + +static bool trans_FCVT_hs(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_hs); +} + +static bool trans_FCVT_dh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvt_dh); +} + +static bool trans_FCVT_hd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_hd); +} + +static bool trans_FCVT_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_ds); +} + +static bool trans_FCVT_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_sd); +} + static bool trans_SCVTF_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_hh); From df4de1affc440d6f2cdaeea329b90c0b88ece5a1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:10 +0100 Subject: [PATCH 1796/2380] target/arm: Implement SVE floating-point convert to integer Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-25-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 30 +++++++++++++ target/arm/helper.c | 2 +- target/arm/helper.h | 12 +++--- target/arm/sve.decode | 16 +++++++ target/arm/sve_helper.c | 88 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 70 ++++++++++++++++++++++++++++++ 6 files changed, 211 insertions(+), 7 deletions(-) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 4c379dbb05..37fa9eb9bb 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -955,6 +955,36 @@ DEF_HELPER_FLAGS_5(sve_fcvt_hd, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_fcvt_sd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_hh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_hs, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_ss, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_ds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_hd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_sd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_dd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcvtzu_hh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_hs, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_ss, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_ds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_hd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_sd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_dd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_scvt_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_scvt_sh, TCG_CALL_NO_RWG, diff --git a/target/arm/helper.c b/target/arm/helper.c index 3c6a4c565b..b19c7ace78 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11407,7 +11407,7 @@ ftype HELPER(name)(uint32_t x, void *fpstp) \ } #define CONV_FTOI(name, ftype, fsz, sign, round) \ -uint32_t HELPER(name)(ftype x, void *fpstp) \ +sign##int32_t HELPER(name)(ftype x, void *fpstp) \ { \ float_status *fpst = fpstp; \ if (float##fsz##_is_any_nan(x)) { \ diff --git a/target/arm/helper.h b/target/arm/helper.h index ad9cb6c7d5..8607077dda 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -134,12 +134,12 @@ DEF_HELPER_2(vfp_touid, i32, f64, ptr) DEF_HELPER_2(vfp_touizh, i32, f16, ptr) DEF_HELPER_2(vfp_touizs, i32, f32, ptr) DEF_HELPER_2(vfp_touizd, i32, f64, ptr) -DEF_HELPER_2(vfp_tosih, i32, f16, ptr) -DEF_HELPER_2(vfp_tosis, i32, f32, ptr) -DEF_HELPER_2(vfp_tosid, i32, f64, ptr) -DEF_HELPER_2(vfp_tosizh, i32, f16, ptr) -DEF_HELPER_2(vfp_tosizs, i32, f32, ptr) -DEF_HELPER_2(vfp_tosizd, i32, f64, ptr) +DEF_HELPER_2(vfp_tosih, s32, f16, ptr) +DEF_HELPER_2(vfp_tosis, s32, f32, ptr) +DEF_HELPER_2(vfp_tosid, s32, f64, ptr) +DEF_HELPER_2(vfp_tosizh, s32, f16, ptr) +DEF_HELPER_2(vfp_tosizs, s32, f32, ptr) +DEF_HELPER_2(vfp_tosizd, s32, f64, ptr) DEF_HELPER_3(vfp_toshs_round_to_zero, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosls_round_to_zero, i32, f32, i32, ptr) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 18c174e92d..ddfb5316c9 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -829,6 +829,22 @@ FCVT_hd 01100101 11 0010 01 101 ... ..... ..... @rd_pg_rn_e0 FCVT_ds 01100101 11 0010 10 101 ... ..... ..... @rd_pg_rn_e0 FCVT_sd 01100101 11 0010 11 101 ... ..... ..... @rd_pg_rn_e0 +# SVE floating-point convert to integer +FCVTZS_hh 01100101 01 011 01 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hh 01100101 01 011 01 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_hs 01100101 01 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hs 01100101 01 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_hd 01100101 01 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hd 01100101 01 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_ss 01100101 10 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_ss 01100101 10 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_ds 01100101 11 011 00 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_ds 01100101 11 011 00 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_sd 01100101 11 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_sd 01100101 11 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_dd 01100101 11 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_dd 01100101 11 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 + # SVE integer convert to floating-point SCVTF_hh 01100101 01 010 01 0 101 ... ..... ..... @rd_pg_rn_e0 SCVTF_sh 01100101 01 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 4b36c1eecf..b6421ec19c 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3195,6 +3195,78 @@ static inline float16 sve_f64_to_f16(float64 f, float_status *fpst) return ret; } +static inline int16_t vfp_float16_to_int16_rtz(float16 f, float_status *s) +{ + if (float16_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float16_to_int16_round_to_zero(f, s); +} + +static inline int64_t vfp_float16_to_int64_rtz(float16 f, float_status *s) +{ + if (float16_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float16_to_int64_round_to_zero(f, s); +} + +static inline int64_t vfp_float32_to_int64_rtz(float32 f, float_status *s) +{ + if (float32_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float32_to_int64_round_to_zero(f, s); +} + +static inline int64_t vfp_float64_to_int64_rtz(float64 f, float_status *s) +{ + if (float64_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float64_to_int64_round_to_zero(f, s); +} + +static inline uint16_t vfp_float16_to_uint16_rtz(float16 f, float_status *s) +{ + if (float16_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float16_to_uint16_round_to_zero(f, s); +} + +static inline uint64_t vfp_float16_to_uint64_rtz(float16 f, float_status *s) +{ + if (float16_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float16_to_uint64_round_to_zero(f, s); +} + +static inline uint64_t vfp_float32_to_uint64_rtz(float32 f, float_status *s) +{ + if (float32_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float32_to_uint64_round_to_zero(f, s); +} + +static inline uint64_t vfp_float64_to_uint64_rtz(float64 f, float_status *s) +{ + if (float64_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float64_to_uint64_round_to_zero(f, s); +} + DO_ZPZ_FP(sve_fcvt_sh, uint32_t, H1_4, sve_f32_to_f16) DO_ZPZ_FP(sve_fcvt_hs, uint32_t, H1_4, sve_f16_to_f32) DO_ZPZ_FP(sve_fcvt_dh, uint64_t, , sve_f64_to_f16) @@ -3202,6 +3274,22 @@ DO_ZPZ_FP(sve_fcvt_hd, uint64_t, , sve_f16_to_f64) DO_ZPZ_FP(sve_fcvt_ds, uint64_t, , float64_to_float32) DO_ZPZ_FP(sve_fcvt_sd, uint64_t, , float32_to_float64) +DO_ZPZ_FP(sve_fcvtzs_hh, uint16_t, H1_2, vfp_float16_to_int16_rtz) +DO_ZPZ_FP(sve_fcvtzs_hs, uint32_t, H1_4, helper_vfp_tosizh) +DO_ZPZ_FP(sve_fcvtzs_ss, uint32_t, H1_4, helper_vfp_tosizs) +DO_ZPZ_FP(sve_fcvtzs_hd, uint64_t, , vfp_float16_to_int64_rtz) +DO_ZPZ_FP(sve_fcvtzs_sd, uint64_t, , vfp_float32_to_int64_rtz) +DO_ZPZ_FP(sve_fcvtzs_ds, uint64_t, , helper_vfp_tosizd) +DO_ZPZ_FP(sve_fcvtzs_dd, uint64_t, , vfp_float64_to_int64_rtz) + +DO_ZPZ_FP(sve_fcvtzu_hh, uint16_t, H1_2, vfp_float16_to_uint16_rtz) +DO_ZPZ_FP(sve_fcvtzu_hs, uint32_t, H1_4, helper_vfp_touizh) +DO_ZPZ_FP(sve_fcvtzu_ss, uint32_t, H1_4, helper_vfp_touizs) +DO_ZPZ_FP(sve_fcvtzu_hd, uint64_t, , vfp_float16_to_uint64_rtz) +DO_ZPZ_FP(sve_fcvtzu_sd, uint64_t, , vfp_float32_to_uint64_rtz) +DO_ZPZ_FP(sve_fcvtzu_ds, uint64_t, , helper_vfp_touizd) +DO_ZPZ_FP(sve_fcvtzu_dd, uint64_t, , vfp_float64_to_uint64_rtz) + DO_ZPZ_FP(sve_scvt_hh, uint16_t, H1_2, int16_to_float16) DO_ZPZ_FP(sve_scvt_sh, uint32_t, H1_4, int32_to_float16) DO_ZPZ_FP(sve_scvt_ss, uint32_t, H1_4, int32_to_float32) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index f2a9057369..969afa55e4 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3970,6 +3970,76 @@ static bool trans_FCVT_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_sd); } +static bool trans_FCVTZS_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzs_hh); +} + +static bool trans_FCVTZU_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzu_hh); +} + +static bool trans_FCVTZS_hs(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzs_hs); +} + +static bool trans_FCVTZU_hs(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzu_hs); +} + +static bool trans_FCVTZS_hd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzs_hd); +} + +static bool trans_FCVTZU_hd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzu_hd); +} + +static bool trans_FCVTZS_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_ss); +} + +static bool trans_FCVTZU_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_ss); +} + +static bool trans_FCVTZS_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_sd); +} + +static bool trans_FCVTZU_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_sd); +} + +static bool trans_FCVTZS_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_ds); +} + +static bool trans_FCVTZU_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_ds); +} + +static bool trans_FCVTZS_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_dd); +} + +static bool trans_FCVTZU_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_dd); +} + static bool trans_SCVTF_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_hh); From cda3c75322c6fae1cc5b367ee6d7acf2cbdbcf2b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:10 +0100 Subject: [PATCH 1797/2380] target/arm: Implement SVE floating-point round to integral value Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-26-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 14 +++++++ target/arm/sve.decode | 9 +++++ target/arm/sve_helper.c | 8 ++++ target/arm/translate-sve.c | 77 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 37fa9eb9bb..36168c5bb2 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -985,6 +985,20 @@ DEF_HELPER_FLAGS_5(sve_fcvtzu_sd, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_fcvtzu_dd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frint_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frint_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frint_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_frintx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frintx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frintx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_scvt_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_scvt_sh, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index ddfb5316c9..e45faaec3a 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -845,6 +845,15 @@ FCVTZU_sd 01100101 11 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 FCVTZS_dd 01100101 11 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 FCVTZU_dd 01100101 11 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 +# SVE floating-point round to integral value +FRINTN 01100101 .. 000 000 101 ... ..... ..... @rd_pg_rn +FRINTP 01100101 .. 000 001 101 ... ..... ..... @rd_pg_rn +FRINTM 01100101 .. 000 010 101 ... ..... ..... @rd_pg_rn +FRINTZ 01100101 .. 000 011 101 ... ..... ..... @rd_pg_rn +FRINTA 01100101 .. 000 100 101 ... ..... ..... @rd_pg_rn +FRINTX 01100101 .. 000 110 101 ... ..... ..... @rd_pg_rn +FRINTI 01100101 .. 000 111 101 ... ..... ..... @rd_pg_rn + # SVE integer convert to floating-point SCVTF_hh 01100101 01 010 01 0 101 ... ..... ..... @rd_pg_rn_e0 SCVTF_sh 01100101 01 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index b6421ec19c..af8221c714 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3290,6 +3290,14 @@ DO_ZPZ_FP(sve_fcvtzu_sd, uint64_t, , vfp_float32_to_uint64_rtz) DO_ZPZ_FP(sve_fcvtzu_ds, uint64_t, , helper_vfp_touizd) DO_ZPZ_FP(sve_fcvtzu_dd, uint64_t, , vfp_float64_to_uint64_rtz) +DO_ZPZ_FP(sve_frint_h, uint16_t, H1_2, helper_advsimd_rinth) +DO_ZPZ_FP(sve_frint_s, uint32_t, H1_4, helper_rints) +DO_ZPZ_FP(sve_frint_d, uint64_t, , helper_rintd) + +DO_ZPZ_FP(sve_frintx_h, uint16_t, H1_2, float16_round_to_int) +DO_ZPZ_FP(sve_frintx_s, uint32_t, H1_4, float32_round_to_int) +DO_ZPZ_FP(sve_frintx_d, uint64_t, , float64_round_to_int) + DO_ZPZ_FP(sve_scvt_hh, uint16_t, H1_2, int16_to_float16) DO_ZPZ_FP(sve_scvt_sh, uint32_t, H1_4, int32_to_float16) DO_ZPZ_FP(sve_scvt_ss, uint32_t, H1_4, int32_to_float32) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 969afa55e4..02ff41fb70 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -4040,6 +4040,83 @@ static bool trans_FCVTZU_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_dd); } +static gen_helper_gvec_3_ptr * const frint_fns[3] = { + gen_helper_sve_frint_h, + gen_helper_sve_frint_s, + gen_helper_sve_frint_d +}; + +static bool trans_FRINTI(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + return do_zpz_ptr(s, a->rd, a->rn, a->pg, a->esz == MO_16, + frint_fns[a->esz - 1]); +} + +static bool trans_FRINTX(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[3] = { + gen_helper_sve_frintx_h, + gen_helper_sve_frintx_s, + gen_helper_sve_frintx_d + }; + if (a->esz == 0) { + return false; + } + return do_zpz_ptr(s, a->rd, a->rn, a->pg, a->esz == MO_16, fns[a->esz - 1]); +} + +static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, int mode) +{ + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_i32 tmode = tcg_const_i32(mode); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + + gen_helper_set_rmode(tmode, tmode, status); + + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + pred_full_reg_offset(s, a->pg), + status, vsz, vsz, 0, frint_fns[a->esz - 1]); + + gen_helper_set_rmode(tmode, tmode, status); + tcg_temp_free_i32(tmode); + tcg_temp_free_ptr(status); + } + return true; +} + +static bool trans_FRINTN(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_frint_mode(s, a, float_round_nearest_even); +} + +static bool trans_FRINTP(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_frint_mode(s, a, float_round_up); +} + +static bool trans_FRINTM(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_frint_mode(s, a, float_round_down); +} + +static bool trans_FRINTZ(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_frint_mode(s, a, float_round_to_zero); +} + +static bool trans_FRINTA(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_frint_mode(s, a, float_round_ties_away); +} + static bool trans_SCVTF_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_hh); From ec5b375bb5a0e35c0c21dc9dd1d82894269ce215 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:11 +0100 Subject: [PATCH 1798/2380] target/arm: Implement SVE floating-point unary operations Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-27-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 14 ++++++++++++++ target/arm/sve.decode | 4 ++++ target/arm/sve_helper.c | 8 ++++++++ target/arm/translate-sve.c | 26 ++++++++++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 36168c5bb2..891346a5ac 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -999,6 +999,20 @@ DEF_HELPER_FLAGS_5(sve_frintx_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_frintx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frecpx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frecpx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frecpx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fsqrt_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fsqrt_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fsqrt_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sve_scvt_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_scvt_sh, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index e45faaec3a..2aca9f0bb0 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -854,6 +854,10 @@ FRINTA 01100101 .. 000 100 101 ... ..... ..... @rd_pg_rn FRINTX 01100101 .. 000 110 101 ... ..... ..... @rd_pg_rn FRINTI 01100101 .. 000 111 101 ... ..... ..... @rd_pg_rn +# SVE floating-point unary operations +FRECPX 01100101 .. 001 100 101 ... ..... ..... @rd_pg_rn +FSQRT 01100101 .. 001 101 101 ... ..... ..... @rd_pg_rn + # SVE integer convert to floating-point SCVTF_hh 01100101 01 010 01 0 101 ... ..... ..... @rd_pg_rn_e0 SCVTF_sh 01100101 01 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index af8221c714..83bd8c4269 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3298,6 +3298,14 @@ DO_ZPZ_FP(sve_frintx_h, uint16_t, H1_2, float16_round_to_int) DO_ZPZ_FP(sve_frintx_s, uint32_t, H1_4, float32_round_to_int) DO_ZPZ_FP(sve_frintx_d, uint64_t, , float64_round_to_int) +DO_ZPZ_FP(sve_frecpx_h, uint16_t, H1_2, helper_frecpx_f16) +DO_ZPZ_FP(sve_frecpx_s, uint32_t, H1_4, helper_frecpx_f32) +DO_ZPZ_FP(sve_frecpx_d, uint64_t, , helper_frecpx_f64) + +DO_ZPZ_FP(sve_fsqrt_h, uint16_t, H1_2, float16_sqrt) +DO_ZPZ_FP(sve_fsqrt_s, uint32_t, H1_4, float32_sqrt) +DO_ZPZ_FP(sve_fsqrt_d, uint64_t, , float64_sqrt) + DO_ZPZ_FP(sve_scvt_hh, uint16_t, H1_2, int16_to_float16) DO_ZPZ_FP(sve_scvt_sh, uint32_t, H1_4, int32_to_float16) DO_ZPZ_FP(sve_scvt_ss, uint32_t, H1_4, int32_to_float32) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 02ff41fb70..b11b6326b9 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -4117,6 +4117,32 @@ static bool trans_FRINTA(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_frint_mode(s, a, float_round_ties_away); } +static bool trans_FRECPX(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[3] = { + gen_helper_sve_frecpx_h, + gen_helper_sve_frecpx_s, + gen_helper_sve_frecpx_d + }; + if (a->esz == 0) { + return false; + } + return do_zpz_ptr(s, a->rd, a->rn, a->pg, a->esz == MO_16, fns[a->esz - 1]); +} + +static bool trans_FSQRT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[3] = { + gen_helper_sve_fsqrt_h, + gen_helper_sve_fsqrt_s, + gen_helper_sve_fsqrt_d + }; + if (a->esz == 0) { + return false; + } + return do_zpz_ptr(s, a->rd, a->rn, a->pg, a->esz == MO_16, fns[a->esz - 1]); +} + static bool trans_SCVTF_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_hh); From a21035822e67000b4849e31935d0ecc39a96bb9f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:11 +0100 Subject: [PATCH 1799/2380] target/arm: Implement SVE MOVPRFX Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-28-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 7 +++++ target/arm/translate-sve.c | 60 +++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 2aca9f0bb0..c725ee2584 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -270,6 +270,10 @@ ORV 00000100 .. 011 000 001 ... ..... ..... @rd_pg_rn EORV 00000100 .. 011 001 001 ... ..... ..... @rd_pg_rn ANDV 00000100 .. 011 010 001 ... ..... ..... @rd_pg_rn +# SVE constructive prefix (predicated) +MOVPRFX_z 00000100 .. 010 000 001 ... ..... ..... @rd_pg_rn +MOVPRFX_m 00000100 .. 010 001 001 ... ..... ..... @rd_pg_rn + # SVE integer add reduction (predicated) # Note that saddv requires size != 3. UADDV 00000100 .. 000 001 001 ... ..... ..... @rd_pg_rn @@ -418,6 +422,9 @@ ADR_p64 00000100 11 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm ### SVE Integer Misc - Unpredicated Group +# SVE constructive prefix (unpredicated) +MOVPRFX 00000100 00 1 00000 101111 rn:5 rd:5 + # SVE floating-point exponential accelerator # Note esz != 0 FEXPA 00000100 .. 1 00000 101110 ..... ..... @rd_rn diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index b11b6326b9..812823777a 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -351,6 +351,23 @@ static bool do_zpzz_ool(DisasContext *s, arg_rprr_esz *a, gen_helper_gvec_4 *fn) return true; } +/* Select active elememnts from Zn and inactive elements from Zm, + * storing the result in Zd. + */ +static void do_sel_z(DisasContext *s, int rd, int rn, int rm, int pg, int esz) +{ + static gen_helper_gvec_4 * const fns[4] = { + gen_helper_sve_sel_zpzz_b, gen_helper_sve_sel_zpzz_h, + gen_helper_sve_sel_zpzz_s, gen_helper_sve_sel_zpzz_d + }; + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_4_ool(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), + pred_full_reg_offset(s, pg), + vsz, vsz, 0, fns[esz]); +} + #define DO_ZPZZ(NAME, name) \ static bool trans_##NAME##_zpzz(DisasContext *s, arg_rprr_esz *a, \ uint32_t insn) \ @@ -401,7 +418,13 @@ static bool trans_UDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) return do_zpzz_ool(s, a, fns[a->esz]); } -DO_ZPZZ(SEL, sel) +static bool trans_SEL_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_sel_z(s, a->rd, a->rn, a->rm, a->pg, a->esz); + } + return true; +} #undef DO_ZPZZ @@ -5035,3 +5058,38 @@ static bool trans_PRF_rr(DisasContext *s, arg_PRF_rr *a, uint32_t insn) sve_access_check(s); return true; } + +/* + * Move Prefix + * + * TODO: The implementation so far could handle predicated merging movprfx. + * The helper functions as written take an extra source register to + * use in the operation, but the result is only written when predication + * succeeds. For unpredicated movprfx, we need to rearrange the helpers + * to allow the final write back to the destination to be unconditional. + * For predicated zeroing movprfx, we need to rearrange the helpers to + * allow the final write back to zero inactives. + * + * In the meantime, just emit the moves. + */ + +static bool trans_MOVPRFX(DisasContext *s, arg_MOVPRFX *a, uint32_t insn) +{ + return do_mov_z(s, a->rd, a->rn); +} + +static bool trans_MOVPRFX_m(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_sel_z(s, a->rd, a->rn, a->rd, a->pg, a->esz); + } + return true; +} + +static bool trans_MOVPRFX_z(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_movz_zpz(s, a->rd, a->rn, a->pg, a->esz); + } + return true; +} From 76a9d9cdc481ed79f1c2ec16eeed185f13e6a8ae Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:11 +0100 Subject: [PATCH 1800/2380] target/arm: Implement SVE floating-point complex add Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-29-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 7 +++ target/arm/sve.decode | 4 ++ target/arm/sve_helper.c | 100 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 24 +++++++++ 4 files changed, 135 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 891346a5ac..0bd9fe2f28 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -1092,6 +1092,13 @@ DEF_HELPER_FLAGS_6(sve_facgt_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_facgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index c725ee2584..e5f8f43254 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -725,6 +725,10 @@ UMIN_zzi 00100101 .. 101 011 110 ........ ..... @rdn_i8u # SVE integer multiply immediate (unpredicated) MUL_zzi 00100101 .. 110 000 110 ........ ..... @rdn_i8s +# SVE floating-point complex add (predicated) +FCADD 01100100 esz:2 00000 rot:1 100 pg:3 rm:5 rd:5 \ + rn=%reg_movprfx + ### SVE FP Multiply-Add Indexed Group # SVE floating-point multiply-add (indexed) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 83bd8c4269..bdb7565779 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3657,6 +3657,106 @@ void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) } } +/* + * FP Complex Add + */ + +void HELPER(sve_fcadd_h)(void *vd, void *vn, void *vm, void *vg, + void *vs, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + uint64_t *g = vg; + float16 neg_imag = float16_set_sign(0, simd_data(desc)); + float16 neg_real = float16_chs(neg_imag); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float16 e0, e1, e2, e3; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float16); + i -= 2 * sizeof(float16); + + e0 = *(float16 *)(vn + H1_2(i)); + e1 = *(float16 *)(vm + H1_2(j)) ^ neg_real; + e2 = *(float16 *)(vn + H1_2(j)); + e3 = *(float16 *)(vm + H1_2(i)) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + *(float16 *)(vd + H1_2(i)) = float16_add(e0, e1, vs); + } + if (likely((pg >> (j & 63)) & 1)) { + *(float16 *)(vd + H1_2(j)) = float16_add(e2, e3, vs); + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fcadd_s)(void *vd, void *vn, void *vm, void *vg, + void *vs, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + uint64_t *g = vg; + float32 neg_imag = float32_set_sign(0, simd_data(desc)); + float32 neg_real = float32_chs(neg_imag); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float32 e0, e1, e2, e3; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float32); + i -= 2 * sizeof(float32); + + e0 = *(float32 *)(vn + H1_2(i)); + e1 = *(float32 *)(vm + H1_2(j)) ^ neg_real; + e2 = *(float32 *)(vn + H1_2(j)); + e3 = *(float32 *)(vm + H1_2(i)) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + *(float32 *)(vd + H1_2(i)) = float32_add(e0, e1, vs); + } + if (likely((pg >> (j & 63)) & 1)) { + *(float32 *)(vd + H1_2(j)) = float32_add(e2, e3, vs); + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, + void *vs, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + uint64_t *g = vg; + float64 neg_imag = float64_set_sign(0, simd_data(desc)); + float64 neg_real = float64_chs(neg_imag); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float64 e0, e1, e2, e3; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float64); + i -= 2 * sizeof(float64); + + e0 = *(float64 *)(vn + H1_2(i)); + e1 = *(float64 *)(vm + H1_2(j)) ^ neg_real; + e2 = *(float64 *)(vn + H1_2(j)); + e3 = *(float64 *)(vm + H1_2(i)) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + *(float64 *)(vd + H1_2(i)) = float64_add(e0, e1, vs); + } + if (likely((pg >> (j & 63)) & 1)) { + *(float64 *)(vd + H1_2(j)) = float64_add(e2, e3, vs); + } + } while (i & 63); + } while (i != 0); +} + /* * Load contiguous data, protected by a governing predicate. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 812823777a..1b71986e2d 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3895,6 +3895,30 @@ DO_FPCMP(FACGT, facgt) #undef DO_FPCMP +static bool trans_FCADD(DisasContext *s, arg_FCADD *a, uint32_t insn) +{ + static gen_helper_gvec_4_ptr * const fns[3] = { + gen_helper_sve_fcadd_h, + gen_helper_sve_fcadd_s, + gen_helper_sve_fcadd_d + }; + + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + status, vsz, vsz, a->rot, fns[a->esz - 1]); + tcg_temp_free_ptr(status); + } + return true; +} + typedef void gen_helper_sve_fmla(TCGv_env, TCGv_ptr, TCGv_i32); static bool do_fmla(DisasContext *s, arg_rprrr_esz *a, gen_helper_sve_fmla *fn) From 05f48bab3080fb876fbad8d8f14e6ba545432d67 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:12 +0100 Subject: [PATCH 1801/2380] target/arm: Implement SVE fp complex multiply add Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-30-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-sve.h | 4 + target/arm/sve.decode | 4 + target/arm/sve_helper.c | 162 +++++++++++++++++++++++++++++++++++++ target/arm/translate-sve.c | 37 +++++++++ 4 files changed, 207 insertions(+) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 0bd9fe2f28..023952a9a4 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -1115,6 +1115,10 @@ DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fcmla_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fcmla_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fcmla_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) + DEF_HELPER_FLAGS_5(sve_ftmad_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_ftmad_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_ftmad_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index e5f8f43254..e342cfdf14 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -729,6 +729,10 @@ MUL_zzi 00100101 .. 110 000 110 ........ ..... @rdn_i8s FCADD 01100100 esz:2 00000 rot:1 100 pg:3 rm:5 rd:5 \ rn=%reg_movprfx +# SVE floating-point complex multiply-add (predicated) +FCMLA_zpzzz 01100100 esz:2 0 rm:5 0 rot:2 pg:3 rn:5 rd:5 \ + ra=%reg_movprfx + ### SVE FP Multiply-Add Indexed Group # SVE floating-point multiply-add (indexed) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index bdb7565779..790cbacd14 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -3757,6 +3757,168 @@ void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, } while (i != 0); } +/* + * FP Complex Multiply + */ + +QEMU_BUILD_BUG_ON(SIMD_DATA_SHIFT + 22 > 32); + +void HELPER(sve_fcmla_zpzzz_h)(CPUARMState *env, void *vg, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + unsigned rot = extract32(desc, SIMD_DATA_SHIFT + 20, 2); + bool flip = rot & 1; + float16 neg_imag, neg_real; + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + neg_imag = float16_set_sign(0, (rot & 2) != 0); + neg_real = float16_set_sign(0, rot == 1 || rot == 2); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float16 e1, e2, e3, e4, nr, ni, mr, mi, d; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float16); + i -= 2 * sizeof(float16); + + nr = *(float16 *)(vn + H1_2(i)); + ni = *(float16 *)(vn + H1_2(j)); + mr = *(float16 *)(vm + H1_2(i)); + mi = *(float16 *)(vm + H1_2(j)); + + e2 = (flip ? ni : nr); + e1 = (flip ? mi : mr) ^ neg_real; + e4 = e2; + e3 = (flip ? mr : mi) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + d = *(float16 *)(va + H1_2(i)); + d = float16_muladd(e2, e1, d, 0, &env->vfp.fp_status_f16); + *(float16 *)(vd + H1_2(i)) = d; + } + if (likely((pg >> (j & 63)) & 1)) { + d = *(float16 *)(va + H1_2(j)); + d = float16_muladd(e4, e3, d, 0, &env->vfp.fp_status_f16); + *(float16 *)(vd + H1_2(j)) = d; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fcmla_zpzzz_s)(CPUARMState *env, void *vg, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + unsigned rot = extract32(desc, SIMD_DATA_SHIFT + 20, 2); + bool flip = rot & 1; + float32 neg_imag, neg_real; + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + neg_imag = float32_set_sign(0, (rot & 2) != 0); + neg_real = float32_set_sign(0, rot == 1 || rot == 2); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float32 e1, e2, e3, e4, nr, ni, mr, mi, d; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float32); + i -= 2 * sizeof(float32); + + nr = *(float32 *)(vn + H1_2(i)); + ni = *(float32 *)(vn + H1_2(j)); + mr = *(float32 *)(vm + H1_2(i)); + mi = *(float32 *)(vm + H1_2(j)); + + e2 = (flip ? ni : nr); + e1 = (flip ? mi : mr) ^ neg_real; + e4 = e2; + e3 = (flip ? mr : mi) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + d = *(float32 *)(va + H1_2(i)); + d = float32_muladd(e2, e1, d, 0, &env->vfp.fp_status); + *(float32 *)(vd + H1_2(i)) = d; + } + if (likely((pg >> (j & 63)) & 1)) { + d = *(float32 *)(va + H1_2(j)); + d = float32_muladd(e4, e3, d, 0, &env->vfp.fp_status); + *(float32 *)(vd + H1_2(j)) = d; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fcmla_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + unsigned rot = extract32(desc, SIMD_DATA_SHIFT + 20, 2); + bool flip = rot & 1; + float64 neg_imag, neg_real; + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + neg_imag = float64_set_sign(0, (rot & 2) != 0); + neg_real = float64_set_sign(0, rot == 1 || rot == 2); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float64 e1, e2, e3, e4, nr, ni, mr, mi, d; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float64); + i -= 2 * sizeof(float64); + + nr = *(float64 *)(vn + H1_2(i)); + ni = *(float64 *)(vn + H1_2(j)); + mr = *(float64 *)(vm + H1_2(i)); + mi = *(float64 *)(vm + H1_2(j)); + + e2 = (flip ? ni : nr); + e1 = (flip ? mi : mr) ^ neg_real; + e4 = e2; + e3 = (flip ? mr : mi) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + d = *(float64 *)(va + H1_2(i)); + d = float64_muladd(e2, e1, d, 0, &env->vfp.fp_status); + *(float64 *)(vd + H1_2(i)) = d; + } + if (likely((pg >> (j & 63)) & 1)) { + d = *(float64 *)(va + H1_2(j)); + d = float64_muladd(e4, e3, d, 0, &env->vfp.fp_status); + *(float64 *)(vd + H1_2(j)) = d; + } + } while (i & 63); + } while (i != 0); +} + /* * Load contiguous data, protected by a governing predicate. */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 1b71986e2d..c47bcec534 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3968,6 +3968,43 @@ DO_FMLA(FNMLS_zpzzz, fnmls_zpzzz) #undef DO_FMLA +static bool trans_FCMLA_zpzzz(DisasContext *s, + arg_FCMLA_zpzzz *a, uint32_t insn) +{ + static gen_helper_sve_fmla * const fns[3] = { + gen_helper_sve_fcmla_zpzzz_h, + gen_helper_sve_fcmla_zpzzz_s, + gen_helper_sve_fcmla_zpzzz_d, + }; + + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + unsigned desc; + TCGv_i32 t_desc; + TCGv_ptr pg = tcg_temp_new_ptr(); + + /* We would need 7 operands to pass these arguments "properly". + * So we encode all the register numbers into the descriptor. + */ + desc = deposit32(a->rd, 5, 5, a->rn); + desc = deposit32(desc, 10, 5, a->rm); + desc = deposit32(desc, 15, 5, a->ra); + desc = deposit32(desc, 20, 2, a->rot); + desc = sextract32(desc, 0, 22); + desc = simd_desc(vsz, vsz, desc); + + t_desc = tcg_const_i32(desc); + tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); + fns[a->esz - 1](cpu_env, pg, t_desc); + tcg_temp_free_i32(t_desc); + tcg_temp_free_ptr(pg); + } + return true; +} + /* *** SVE Floating Point Unary Operations Predicated Group */ From 2cc99919a81a62589a4a6b0f365eabfead1db1a7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:12 +0100 Subject: [PATCH 1802/2380] target/arm: Pass index to AdvSIMD FCMLA (indexed) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For aa64 advsimd, we had been passing the pre-indexed vector. However, sve applies the index to each 128-bit segment, so we need to pass in the index separately. For aa32 advsimd, the fp32 operation always has index 0, but we failed to interpret the fp16 index correctly. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180627043328.11531-31-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-a64.c | 21 ++++++++++++--------- target/arm/translate.c | 32 +++++++++++++++++++++++--------- target/arm/vec_helper.c | 10 ++++++---- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 8d8a4cecb0..eb3a4ab2f0 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -12669,15 +12669,18 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x13: /* FCMLA #90 */ case 0x15: /* FCMLA #180 */ case 0x17: /* FCMLA #270 */ - tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_reg_offset(s, rm, index, size), fpst, - is_q ? 16 : 8, vec_full_reg_size(s), - extract32(insn, 13, 2), /* rot */ - size == MO_64 - ? gen_helper_gvec_fcmlas_idx - : gen_helper_gvec_fcmlah_idx); - tcg_temp_free_ptr(fpst); + { + int rot = extract32(insn, 13, 2); + int data = (index << 2) | rot; + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), fpst, + is_q ? 16 : 8, vec_full_reg_size(s), data, + size == MO_64 + ? gen_helper_gvec_fcmlas_idx + : gen_helper_gvec_fcmlah_idx); + tcg_temp_free_ptr(fpst); + } return; } diff --git a/target/arm/translate.c b/target/arm/translate.c index 2a3e4f5d4c..a7a980b1f2 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -7826,26 +7826,42 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) { - int rd, rn, rm, rot, size, opr_sz; + gen_helper_gvec_3_ptr *fn_gvec_ptr; + int rd, rn, rm, opr_sz, data; TCGv_ptr fpst; bool q; q = extract32(insn, 6, 1); VFP_DREG_D(rd, insn); VFP_DREG_N(rn, insn); - VFP_DREG_M(rm, insn); if ((rd | rn) & q) { return 1; } if ((insn & 0xff000f10) == 0xfe000800) { /* VCMLA (indexed) -- 1111 1110 S.RR .... .... 1000 ...0 .... */ - rot = extract32(insn, 20, 2); - size = extract32(insn, 23, 1); - if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) - || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { + int rot = extract32(insn, 20, 2); + int size = extract32(insn, 23, 1); + int index; + + if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA)) { return 1; } + if (size == 0) { + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + return 1; + } + /* For fp16, rm is just Vm, and index is M. */ + rm = extract32(insn, 0, 4); + index = extract32(insn, 5, 1); + } else { + /* For fp32, rm is the usual M:Vm, and index is 0. */ + VFP_DREG_M(rm, insn); + index = 0; + } + data = (index << 2) | rot; + fn_gvec_ptr = (size ? gen_helper_gvec_fcmlas_idx + : gen_helper_gvec_fcmlah_idx); } else { return 1; } @@ -7864,9 +7880,7 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), vfp_reg_offset(1, rn), vfp_reg_offset(1, rm), fpst, - opr_sz, opr_sz, rot, - size ? gen_helper_gvec_fcmlas_idx - : gen_helper_gvec_fcmlah_idx); + opr_sz, opr_sz, data, fn_gvec_ptr); tcg_temp_free_ptr(fpst); return 0; } diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index 073e5c58e7..8f2dc4b989 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -317,10 +317,11 @@ void HELPER(gvec_fcmlah_idx)(void *vd, void *vn, void *vm, float_status *fpst = vfpst; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); uint32_t neg_real = flip ^ neg_imag; uintptr_t i; - float16 e1 = m[H2(flip)]; - float16 e3 = m[H2(1 - flip)]; + float16 e1 = m[H2(2 * index + flip)]; + float16 e3 = m[H2(2 * index + 1 - flip)]; /* Shift boolean to the sign bit so we can xor to negate. */ neg_real <<= 15; @@ -377,10 +378,11 @@ void HELPER(gvec_fcmlas_idx)(void *vd, void *vn, void *vm, float_status *fpst = vfpst; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); uint32_t neg_real = flip ^ neg_imag; uintptr_t i; - float32 e1 = m[H4(flip)]; - float32 e3 = m[H4(1 - flip)]; + float32 e1 = m[H4(2 * index + flip)]; + float32 e3 = m[H4(2 * index + 1 - flip)]; /* Shift boolean to the sign bit so we can xor to negate. */ neg_real <<= 31; From 18fc24057815bf3d956cfab892a2bc2344bd1dcb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:12 +0100 Subject: [PATCH 1803/2380] target/arm: Implement SVE fp complex multiply add (indexed) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance the existing helpers to support SVE, which takes the index from each 128-bit segment. The change has no effect for AdvSIMD, since there is only one such segment. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180627043328.11531-32-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve.decode | 6 +++++ target/arm/translate-sve.c | 23 ++++++++++++++++++ target/arm/vec_helper.c | 50 +++++++++++++++++++++++--------------- 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index e342cfdf14..62365ed90f 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -733,6 +733,12 @@ FCADD 01100100 esz:2 00000 rot:1 100 pg:3 rm:5 rd:5 \ FCMLA_zpzzz 01100100 esz:2 0 rm:5 0 rot:2 pg:3 rn:5 rd:5 \ ra=%reg_movprfx +# SVE floating-point complex multiply-add (indexed) +FCMLA_zzxz 01100100 10 1 index:2 rm:3 0001 rot:2 rn:5 rd:5 \ + ra=%reg_movprfx esz=1 +FCMLA_zzxz 01100100 11 1 index:1 rm:4 0001 rot:2 rn:5 rd:5 \ + ra=%reg_movprfx esz=2 + ### SVE FP Multiply-Add Indexed Group # SVE floating-point multiply-add (indexed) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index c47bcec534..7912bceb1e 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -4005,6 +4005,29 @@ static bool trans_FCMLA_zpzzz(DisasContext *s, return true; } +static bool trans_FCMLA_zzxz(DisasContext *s, arg_FCMLA_zzxz *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[2] = { + gen_helper_gvec_fcmlah_idx, + gen_helper_gvec_fcmlas_idx, + }; + + tcg_debug_assert(a->esz == 1 || a->esz == 2); + tcg_debug_assert(a->rd == a->ra); + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + status, vsz, vsz, + a->index * 4 + a->rot, + fns[a->esz - 1]); + tcg_temp_free_ptr(status); + } + return true; +} + /* *** SVE Floating Point Unary Operations Predicated Group */ diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index 8f2dc4b989..db5aeb9f24 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -319,22 +319,27 @@ void HELPER(gvec_fcmlah_idx)(void *vd, void *vn, void *vm, uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); uint32_t neg_real = flip ^ neg_imag; - uintptr_t i; - float16 e1 = m[H2(2 * index + flip)]; - float16 e3 = m[H2(2 * index + 1 - flip)]; + intptr_t elements = opr_sz / sizeof(float16); + intptr_t eltspersegment = 16 / sizeof(float16); + intptr_t i, j; /* Shift boolean to the sign bit so we can xor to negate. */ neg_real <<= 15; neg_imag <<= 15; - e1 ^= neg_real; - e3 ^= neg_imag; - for (i = 0; i < opr_sz / 2; i += 2) { - float16 e2 = n[H2(i + flip)]; - float16 e4 = e2; + for (i = 0; i < elements; i += eltspersegment) { + float16 mr = m[H2(i + 2 * index + 0)]; + float16 mi = m[H2(i + 2 * index + 1)]; + float16 e1 = neg_real ^ (flip ? mi : mr); + float16 e3 = neg_imag ^ (flip ? mr : mi); - d[H2(i)] = float16_muladd(e2, e1, d[H2(i)], 0, fpst); - d[H2(i + 1)] = float16_muladd(e4, e3, d[H2(i + 1)], 0, fpst); + for (j = i; j < i + eltspersegment; j += 2) { + float16 e2 = n[H2(j + flip)]; + float16 e4 = e2; + + d[H2(j)] = float16_muladd(e2, e1, d[H2(j)], 0, fpst); + d[H2(j + 1)] = float16_muladd(e4, e3, d[H2(j + 1)], 0, fpst); + } } clear_tail(d, opr_sz, simd_maxsz(desc)); } @@ -380,22 +385,27 @@ void HELPER(gvec_fcmlas_idx)(void *vd, void *vn, void *vm, uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); uint32_t neg_real = flip ^ neg_imag; - uintptr_t i; - float32 e1 = m[H4(2 * index + flip)]; - float32 e3 = m[H4(2 * index + 1 - flip)]; + intptr_t elements = opr_sz / sizeof(float32); + intptr_t eltspersegment = 16 / sizeof(float32); + intptr_t i, j; /* Shift boolean to the sign bit so we can xor to negate. */ neg_real <<= 31; neg_imag <<= 31; - e1 ^= neg_real; - e3 ^= neg_imag; - for (i = 0; i < opr_sz / 4; i += 2) { - float32 e2 = n[H4(i + flip)]; - float32 e4 = e2; + for (i = 0; i < elements; i += eltspersegment) { + float32 mr = m[H4(i + 2 * index + 0)]; + float32 mi = m[H4(i + 2 * index + 1)]; + float32 e1 = neg_real ^ (flip ? mi : mr); + float32 e3 = neg_imag ^ (flip ? mr : mi); - d[H4(i)] = float32_muladd(e2, e1, d[H4(i)], 0, fpst); - d[H4(i + 1)] = float32_muladd(e4, e3, d[H4(i + 1)], 0, fpst); + for (j = i; j < i + eltspersegment; j += 2) { + float32 e2 = n[H4(j + flip)]; + float32 e4 = e2; + + d[H4(j)] = float32_muladd(e2, e1, d[H4(j)], 0, fpst); + d[H4(j + 1)] = float32_muladd(e4, e3, d[H4(j + 1)], 0, fpst); + } } clear_tail(d, opr_sz, simd_maxsz(desc)); } From d730ecaae77ac696515207a5ef99509240fc792b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:13 +0100 Subject: [PATCH 1804/2380] target/arm: Implement SVE dot product (vectors) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-33-richard.henderson@linaro.org [PMM: moved 'ra=%reg_movprfx' here from following patch] Signed-off-by: Peter Maydell --- target/arm/helper.h | 5 +++ target/arm/sve.decode | 3 ++ target/arm/translate-sve.c | 17 ++++++++++ target/arm/vec_helper.c | 67 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+) diff --git a/target/arm/helper.h b/target/arm/helper.h index 8607077dda..e23ce7ff19 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -583,6 +583,11 @@ DEF_HELPER_FLAGS_5(gvec_qrdmlah_s32, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_udot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sdot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_udot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcadds, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 62365ed90f..7b7b29ae64 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -725,6 +725,9 @@ UMIN_zzi 00100101 .. 101 011 110 ........ ..... @rdn_i8u # SVE integer multiply immediate (unpredicated) MUL_zzi 00100101 .. 110 000 110 ........ ..... @rdn_i8s +# SVE integer dot product (unpredicated) +DOT_zzz 01000100 1 sz:1 0 rm:5 00000 u:1 rn:5 rd:5 ra=%reg_movprfx + # SVE floating-point complex add (predicated) FCADD 01100100 esz:2 00000 rot:1 100 pg:3 rm:5 rd:5 \ rn=%reg_movprfx diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 7912bceb1e..cf9c652e54 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3423,6 +3423,23 @@ DO_ZZI(UMIN, umin) #undef DO_ZZI +static bool trans_DOT_zzz(DisasContext *s, arg_DOT_zzz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[2][2] = { + { gen_helper_gvec_sdot_b, gen_helper_gvec_sdot_h }, + { gen_helper_gvec_udot_b, gen_helper_gvec_udot_h } + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, 0, fns[a->u][a->sz]); + } + return true; +} + /* *** SVE Floating Point Multiply-Add Indexed Group */ diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index db5aeb9f24..c16a30c3b5 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -194,6 +194,73 @@ void HELPER(gvec_qrdmlsh_s32)(void *vd, void *vn, void *vm, clear_tail(d, opr_sz, simd_maxsz(desc)); } +/* Integer 8 and 16-bit dot-product. + * + * Note that for the loops herein, host endianness does not matter + * with respect to the ordering of data within the 64-bit lanes. + * All elements are treated equally, no matter where they are. + */ + +void HELPER(gvec_sdot_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint32_t *d = vd; + int8_t *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] += n[i * 4 + 0] * m[i * 4 + 0] + + n[i * 4 + 1] * m[i * 4 + 1] + + n[i * 4 + 2] * m[i * 4 + 2] + + n[i * 4 + 3] * m[i * 4 + 3]; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_udot_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint32_t *d = vd; + uint8_t *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] += n[i * 4 + 0] * m[i * 4 + 0] + + n[i * 4 + 1] * m[i * 4 + 1] + + n[i * 4 + 2] * m[i * 4 + 2] + + n[i * 4 + 3] * m[i * 4 + 3]; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_sdot_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd; + int16_t *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] += (int64_t)n[i * 4 + 0] * m[i * 4 + 0] + + (int64_t)n[i * 4 + 1] * m[i * 4 + 1] + + (int64_t)n[i * 4 + 2] * m[i * 4 + 2] + + (int64_t)n[i * 4 + 3] * m[i * 4 + 3]; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_udot_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd; + uint16_t *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] += (uint64_t)n[i * 4 + 0] * m[i * 4 + 0] + + (uint64_t)n[i * 4 + 1] * m[i * 4 + 1] + + (uint64_t)n[i * 4 + 2] * m[i * 4 + 2] + + (uint64_t)n[i * 4 + 3] * m[i * 4 + 3]; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, void *vfpst, uint32_t desc) { From 16fcfdc7325649b187ac489f3ae0b0d2a20b6230 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:15 +0100 Subject: [PATCH 1805/2380] target/arm: Implement SVE dot product (indexed) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180627043328.11531-34-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 5 ++ target/arm/sve.decode | 6 ++ target/arm/translate-sve.c | 18 ++++++ target/arm/vec_helper.c | 124 +++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+) diff --git a/target/arm/helper.h b/target/arm/helper.h index e23ce7ff19..59e8c3bd1b 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -588,6 +588,11 @@ DEF_HELPER_FLAGS_4(gvec_udot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_sdot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_udot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sdot_idx_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_udot_idx_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sdot_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_udot_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcadds, TCG_CALL_NO_RWG, diff --git a/target/arm/sve.decode b/target/arm/sve.decode index 7b7b29ae64..e10b689454 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -728,6 +728,12 @@ MUL_zzi 00100101 .. 110 000 110 ........ ..... @rdn_i8s # SVE integer dot product (unpredicated) DOT_zzz 01000100 1 sz:1 0 rm:5 00000 u:1 rn:5 rd:5 ra=%reg_movprfx +# SVE integer dot product (indexed) +DOT_zzx 01000100 101 index:2 rm:3 00000 u:1 rn:5 rd:5 \ + sz=0 ra=%reg_movprfx +DOT_zzx 01000100 111 index:1 rm:4 00000 u:1 rn:5 rd:5 \ + sz=1 ra=%reg_movprfx + # SVE floating-point complex add (predicated) FCADD 01100100 esz:2 00000 rot:1 100 pg:3 rm:5 rd:5 \ rn=%reg_movprfx diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index cf9c652e54..c080345b9c 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -3440,6 +3440,24 @@ static bool trans_DOT_zzz(DisasContext *s, arg_DOT_zzz *a, uint32_t insn) return true; } +static bool trans_DOT_zzx(DisasContext *s, arg_DOT_zzx *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[2][2] = { + { gen_helper_gvec_sdot_idx_b, gen_helper_gvec_sdot_idx_h }, + { gen_helper_gvec_udot_idx_b, gen_helper_gvec_udot_idx_h } + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, a->index, fns[a->u][a->sz]); + } + return true; +} + + /* *** SVE Floating Point Multiply-Add Indexed Group */ diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index c16a30c3b5..37f338732e 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -261,6 +261,130 @@ void HELPER(gvec_udot_h)(void *vd, void *vn, void *vm, uint32_t desc) clear_tail(d, opr_sz, simd_maxsz(desc)); } +void HELPER(gvec_sdot_idx_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, segend, opr_sz = simd_oprsz(desc), opr_sz_4 = opr_sz / 4; + intptr_t index = simd_data(desc); + uint32_t *d = vd; + int8_t *n = vn; + int8_t *m_indexed = (int8_t *)vm + index * 4; + + /* Notice the special case of opr_sz == 8, from aa64/aa32 advsimd. + * Otherwise opr_sz is a multiple of 16. + */ + segend = MIN(4, opr_sz_4); + i = 0; + do { + int8_t m0 = m_indexed[i * 4 + 0]; + int8_t m1 = m_indexed[i * 4 + 1]; + int8_t m2 = m_indexed[i * 4 + 2]; + int8_t m3 = m_indexed[i * 4 + 3]; + + do { + d[i] += n[i * 4 + 0] * m0 + + n[i * 4 + 1] * m1 + + n[i * 4 + 2] * m2 + + n[i * 4 + 3] * m3; + } while (++i < segend); + segend = i + 4; + } while (i < opr_sz_4); + + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_udot_idx_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, segend, opr_sz = simd_oprsz(desc), opr_sz_4 = opr_sz / 4; + intptr_t index = simd_data(desc); + uint32_t *d = vd; + uint8_t *n = vn; + uint8_t *m_indexed = (uint8_t *)vm + index * 4; + + /* Notice the special case of opr_sz == 8, from aa64/aa32 advsimd. + * Otherwise opr_sz is a multiple of 16. + */ + segend = MIN(4, opr_sz_4); + i = 0; + do { + uint8_t m0 = m_indexed[i * 4 + 0]; + uint8_t m1 = m_indexed[i * 4 + 1]; + uint8_t m2 = m_indexed[i * 4 + 2]; + uint8_t m3 = m_indexed[i * 4 + 3]; + + do { + d[i] += n[i * 4 + 0] * m0 + + n[i * 4 + 1] * m1 + + n[i * 4 + 2] * m2 + + n[i * 4 + 3] * m3; + } while (++i < segend); + segend = i + 4; + } while (i < opr_sz_4); + + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_sdot_idx_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc), opr_sz_8 = opr_sz / 8; + intptr_t index = simd_data(desc); + uint64_t *d = vd; + int16_t *n = vn; + int16_t *m_indexed = (int16_t *)vm + index * 4; + + /* This is supported by SVE only, so opr_sz is always a multiple of 16. + * Process the entire segment all at once, writing back the results + * only after we've consumed all of the inputs. + */ + for (i = 0; i < opr_sz_8 ; i += 2) { + uint64_t d0, d1; + + d0 = n[i * 4 + 0] * (int64_t)m_indexed[i * 4 + 0]; + d0 += n[i * 4 + 1] * (int64_t)m_indexed[i * 4 + 1]; + d0 += n[i * 4 + 2] * (int64_t)m_indexed[i * 4 + 2]; + d0 += n[i * 4 + 3] * (int64_t)m_indexed[i * 4 + 3]; + d1 = n[i * 4 + 4] * (int64_t)m_indexed[i * 4 + 0]; + d1 += n[i * 4 + 5] * (int64_t)m_indexed[i * 4 + 1]; + d1 += n[i * 4 + 6] * (int64_t)m_indexed[i * 4 + 2]; + d1 += n[i * 4 + 7] * (int64_t)m_indexed[i * 4 + 3]; + + d[i + 0] += d0; + d[i + 1] += d1; + } + + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_udot_idx_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc), opr_sz_8 = opr_sz / 8; + intptr_t index = simd_data(desc); + uint64_t *d = vd; + uint16_t *n = vn; + uint16_t *m_indexed = (uint16_t *)vm + index * 4; + + /* This is supported by SVE only, so opr_sz is always a multiple of 16. + * Process the entire segment all at once, writing back the results + * only after we've consumed all of the inputs. + */ + for (i = 0; i < opr_sz_8 ; i += 2) { + uint64_t d0, d1; + + d0 = n[i * 4 + 0] * (uint64_t)m_indexed[i * 4 + 0]; + d0 += n[i * 4 + 1] * (uint64_t)m_indexed[i * 4 + 1]; + d0 += n[i * 4 + 2] * (uint64_t)m_indexed[i * 4 + 2]; + d0 += n[i * 4 + 3] * (uint64_t)m_indexed[i * 4 + 3]; + d1 = n[i * 4 + 4] * (uint64_t)m_indexed[i * 4 + 0]; + d1 += n[i * 4 + 5] * (uint64_t)m_indexed[i * 4 + 1]; + d1 += n[i * 4 + 6] * (uint64_t)m_indexed[i * 4 + 2]; + d1 += n[i * 4 + 7] * (uint64_t)m_indexed[i * 4 + 3]; + + d[i + 0] += d0; + d[i + 1] += d1; + } + + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, void *vfpst, uint32_t desc) { From 802ac0e1e956f3b34a7cb0eda4ec28a60aa87a0a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:15 +0100 Subject: [PATCH 1806/2380] target/arm: Enable SVE for aarch64-linux-user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable ARM_FEATURE_SVE for the generic "max" cpu. Tested-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-35-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/elfload.c | 1 + target/arm/cpu.c | 7 +++++++ target/arm/cpu64.c | 1 + 3 files changed, 9 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 13bc78d0c8..d1231ad07a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -584,6 +584,7 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE(ARM_FEATURE_V8_ATOMICS, ARM_HWCAP_A64_ATOMICS); GET_FEATURE(ARM_FEATURE_V8_RDM, ARM_HWCAP_A64_ASIMDRDM); GET_FEATURE(ARM_FEATURE_V8_FCMA, ARM_HWCAP_A64_FCMA); + GET_FEATURE(ARM_FEATURE_SVE, ARM_HWCAP_A64_SVE); #undef GET_FEATURE return hwcaps; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 2ae4fffafb..6dcc552e14 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -164,6 +164,13 @@ static void arm_cpu_reset(CPUState *s) env->cp15.sctlr_el[1] |= SCTLR_UCT | SCTLR_UCI | SCTLR_DZE; /* and to the FP/Neon instructions */ env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 20, 2, 3); + /* and to the SVE instructions */ + env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 16, 2, 3); + env->cp15.cptr_el[3] |= CPTR_EZ; + /* with maximum vector length */ + env->vfp.zcr_el[1] = ARM_MAX_VQ - 1; + env->vfp.zcr_el[2] = ARM_MAX_VQ - 1; + env->vfp.zcr_el[3] = ARM_MAX_VQ - 1; #else /* Reset into the highest available EL */ if (arm_feature(env, ARM_FEATURE_EL3)) { diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index c50dcd4077..0360d7efc5 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -252,6 +252,7 @@ static void aarch64_max_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_RDM); set_feature(&cpu->env, ARM_FEATURE_V8_FP16); set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); + set_feature(&cpu->env, ARM_FEATURE_SVE); /* For usermode -cpu max we can use a larger and more efficient DCZ * blocksize since we don't have to follow what the hardware does. */ From 26c470a7bb4233454137de1062341ad48947f252 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 15:11:15 +0100 Subject: [PATCH 1807/2380] target/arm: Implement ARMv8.2-DotProd We've already added the helpers with an SVE patch, all that remains is to wire up the aa64 and aa32 translators. Enable the feature within -cpu max for CONFIG_USER_ONLY. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20180627043328.11531-36-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/elfload.c | 1 + target/arm/cpu.c | 1 + target/arm/cpu.h | 1 + target/arm/cpu64.c | 1 + target/arm/translate-a64.c | 36 +++++++++++++++++++ target/arm/translate.c | 74 +++++++++++++++++++++++++++----------- 6 files changed, 93 insertions(+), 21 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d1231ad07a..942a1b661f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -583,6 +583,7 @@ static uint32_t get_elf_hwcap(void) ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); GET_FEATURE(ARM_FEATURE_V8_ATOMICS, ARM_HWCAP_A64_ATOMICS); GET_FEATURE(ARM_FEATURE_V8_RDM, ARM_HWCAP_A64_ASIMDRDM); + GET_FEATURE(ARM_FEATURE_V8_DOTPROD, ARM_HWCAP_A64_ASIMDDP); GET_FEATURE(ARM_FEATURE_V8_FCMA, ARM_HWCAP_A64_FCMA); GET_FEATURE(ARM_FEATURE_SVE, ARM_HWCAP_A64_SVE); #undef GET_FEATURE diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 6dcc552e14..aa62315cea 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1805,6 +1805,7 @@ static void arm_max_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); set_feature(&cpu->env, ARM_FEATURE_V8_RDM); + set_feature(&cpu->env, ARM_FEATURE_V8_DOTPROD); set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); #endif } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index a4507a2d6f..6a8441c2dd 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1480,6 +1480,7 @@ enum arm_features { ARM_FEATURE_V8_SM4, /* implements SM4 part of v8 Crypto Extensions */ ARM_FEATURE_V8_ATOMICS, /* ARMv8.1-Atomics feature */ ARM_FEATURE_V8_RDM, /* implements v8.1 simd round multiply */ + ARM_FEATURE_V8_DOTPROD, /* implements v8.2 simd dot product */ ARM_FEATURE_V8_FP16, /* implements v8.2 half-precision float */ ARM_FEATURE_V8_FCMA, /* has complex number part of v8.3 extensions. */ ARM_FEATURE_M_MAIN, /* M profile Main Extension */ diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 0360d7efc5..3b4bc73ffa 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -250,6 +250,7 @@ static void aarch64_max_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_CRC); set_feature(&cpu->env, ARM_FEATURE_V8_ATOMICS); set_feature(&cpu->env, ARM_FEATURE_V8_RDM); + set_feature(&cpu->env, ARM_FEATURE_V8_DOTPROD); set_feature(&cpu->env, ARM_FEATURE_V8_FP16); set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); set_feature(&cpu->env, ARM_FEATURE_SVE); diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index eb3a4ab2f0..f986340832 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -640,6 +640,16 @@ static void gen_gvec_op3(DisasContext *s, bool is_q, int rd, vec_full_reg_size(s), gvec_op); } +/* Expand a 3-operand operation using an out-of-line helper. */ +static void gen_gvec_op3_ool(DisasContext *s, bool is_q, int rd, + int rn, int rm, int data, gen_helper_gvec_3 *fn) +{ + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), + is_q ? 16 : 8, vec_full_reg_size(s), data, fn); +} + /* Expand a 3-operand + env pointer operation using * an out-of-line helper. */ @@ -11336,6 +11346,14 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } feature = ARM_FEATURE_V8_RDM; break; + case 0x02: /* SDOT (vector) */ + case 0x12: /* UDOT (vector) */ + if (size != MO_32) { + unallocated_encoding(s); + return; + } + feature = ARM_FEATURE_V8_DOTPROD; + break; case 0x8: /* FCMLA, #0 */ case 0x9: /* FCMLA, #90 */ case 0xa: /* FCMLA, #180 */ @@ -11389,6 +11407,11 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } return; + case 0x2: /* SDOT / UDOT */ + gen_gvec_op3_ool(s, is_q, rd, rn, rm, 0, + u ? gen_helper_gvec_udot_b : gen_helper_gvec_sdot_b); + return; + case 0x8: /* FCMLA, #0 */ case 0x9: /* FCMLA, #90 */ case 0xa: /* FCMLA, #180 */ @@ -12568,6 +12591,13 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) return; } break; + case 0x0e: /* SDOT */ + case 0x1e: /* UDOT */ + if (size != MO_32 || !arm_dc_feature(s, ARM_FEATURE_V8_DOTPROD)) { + unallocated_encoding(s); + return; + } + break; case 0x11: /* FCMLA #0 */ case 0x13: /* FCMLA #90 */ case 0x15: /* FCMLA #180 */ @@ -12665,6 +12695,12 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } switch (16 * u + opcode) { + case 0x0e: /* SDOT */ + case 0x1e: /* UDOT */ + gen_gvec_op3_ool(s, is_q, rd, rn, rm, index, + u ? gen_helper_gvec_udot_idx_b + : gen_helper_gvec_sdot_idx_b); + return; case 0x11: /* FCMLA #0 */ case 0x13: /* FCMLA #90 */ case 0x15: /* FCMLA #180 */ diff --git a/target/arm/translate.c b/target/arm/translate.c index a7a980b1f2..f845da7c63 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -7762,9 +7762,10 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) */ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) { - gen_helper_gvec_3_ptr *fn_gvec_ptr; - int rd, rn, rm, rot, size, opr_sz; - TCGv_ptr fpst; + gen_helper_gvec_3 *fn_gvec = NULL; + gen_helper_gvec_3_ptr *fn_gvec_ptr = NULL; + int rd, rn, rm, opr_sz; + int data = 0; bool q; q = extract32(insn, 6, 1); @@ -7777,8 +7778,8 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) if ((insn & 0xfe200f10) == 0xfc200800) { /* VCMLA -- 1111 110R R.1S .... .... 1000 ...0 .... */ - size = extract32(insn, 20, 1); - rot = extract32(insn, 23, 2); + int size = extract32(insn, 20, 1); + data = extract32(insn, 23, 2); /* rot */ if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { return 1; @@ -7786,13 +7787,20 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) fn_gvec_ptr = size ? gen_helper_gvec_fcmlas : gen_helper_gvec_fcmlah; } else if ((insn & 0xfea00f10) == 0xfc800800) { /* VCADD -- 1111 110R 1.0S .... .... 1000 ...0 .... */ - size = extract32(insn, 20, 1); - rot = extract32(insn, 24, 1); + int size = extract32(insn, 20, 1); + data = extract32(insn, 24, 1); /* rot */ if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { return 1; } fn_gvec_ptr = size ? gen_helper_gvec_fcadds : gen_helper_gvec_fcaddh; + } else if ((insn & 0xfeb00f00) == 0xfc200d00) { + /* V[US]DOT -- 1111 1100 0.10 .... .... 1101 .Q.U .... */ + bool u = extract32(insn, 4, 1); + if (!arm_dc_feature(s, ARM_FEATURE_V8_DOTPROD)) { + return 1; + } + fn_gvec = u ? gen_helper_gvec_udot_b : gen_helper_gvec_sdot_b; } else { return 1; } @@ -7807,12 +7815,19 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) } opr_sz = (1 + q) * 8; - fpst = get_fpstatus_ptr(1); - tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), - vfp_reg_offset(1, rn), - vfp_reg_offset(1, rm), fpst, - opr_sz, opr_sz, rot, fn_gvec_ptr); - tcg_temp_free_ptr(fpst); + if (fn_gvec_ptr) { + TCGv_ptr fpst = get_fpstatus_ptr(1); + tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), fpst, + opr_sz, opr_sz, data, fn_gvec_ptr); + tcg_temp_free_ptr(fpst); + } else { + tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), + opr_sz, opr_sz, data, fn_gvec); + } return 0; } @@ -7826,9 +7841,9 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) { - gen_helper_gvec_3_ptr *fn_gvec_ptr; + gen_helper_gvec_3 *fn_gvec = NULL; + gen_helper_gvec_3_ptr *fn_gvec_ptr = NULL; int rd, rn, rm, opr_sz, data; - TCGv_ptr fpst; bool q; q = extract32(insn, 6, 1); @@ -7862,6 +7877,16 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) data = (index << 2) | rot; fn_gvec_ptr = (size ? gen_helper_gvec_fcmlas_idx : gen_helper_gvec_fcmlah_idx); + } else if ((insn & 0xffb00f00) == 0xfe200d00) { + /* V[US]DOT -- 1111 1110 0.10 .... .... 1101 .Q.U .... */ + int u = extract32(insn, 4, 1); + if (!arm_dc_feature(s, ARM_FEATURE_V8_DOTPROD)) { + return 1; + } + fn_gvec = u ? gen_helper_gvec_udot_idx_b : gen_helper_gvec_sdot_idx_b; + /* rm is just Vm, and index is M. */ + data = extract32(insn, 5, 1); /* index */ + rm = extract32(insn, 0, 4); } else { return 1; } @@ -7876,12 +7901,19 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) } opr_sz = (1 + q) * 8; - fpst = get_fpstatus_ptr(1); - tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), - vfp_reg_offset(1, rn), - vfp_reg_offset(1, rm), fpst, - opr_sz, opr_sz, data, fn_gvec_ptr); - tcg_temp_free_ptr(fpst); + if (fn_gvec_ptr) { + TCGv_ptr fpst = get_fpstatus_ptr(1); + tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), fpst, + opr_sz, opr_sz, data, fn_gvec_ptr); + tcg_temp_free_ptr(fpst); + } else { + tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), + opr_sz, opr_sz, data, fn_gvec); + } return 0; } From 26c4a83bd4707797868174332a540f7d61288d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 29 Jun 2018 15:11:16 +0100 Subject: [PATCH 1808/2380] target/arm: support reading of CNT[VCT|FRQ]_EL0 from user-space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since kernel commit a86bd139f2 (arm64: arch_timer: Enable CNTVCT_EL0 trap..), released in kernel version v4.12, user-space has been able to read these system registers. As we can't use QEMUTimer's in linux-user mode we just directly call cpu_get_clock(). Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20180625160009.17437-2-alex.bennee@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/helper.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index b19c7ace78..d0786e0c21 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2167,11 +2167,32 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }; #else -/* In user-mode none of the generic timer registers are accessible, - * and their implementation depends on QEMU_CLOCK_VIRTUAL and qdev gpio outputs, - * so instead just don't register any of them. + +/* In user-mode most of the generic timer registers are inaccessible + * however modern kernels (4.12+) allow access to cntvct_el0 */ + +static uint64_t gt_virt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* Currently we have no support for QEMUTimer in linux-user so we + * can't call gt_get_countervalue(env), instead we directly + * call the lower level functions. + */ + return cpu_get_clock() / GTIMER_SCALE; +} + static const ARMCPRegInfo generic_timer_cp_reginfo[] = { + { .name = "CNTFRQ_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0, + .type = ARM_CP_CONST, .access = PL0_R /* no PL1_RW in linux-user */, + .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq), + .resetvalue = NANOSECONDS_PER_SECOND / GTIMER_SCALE, + }, + { .name = "CNTVCT_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 2, + .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .readfn = gt_virt_cnt_read, + }, REGINFO_SENTINEL }; From 0844f025a8fb963a6c0843168eb197dd81602be5 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois Date: Fri, 29 Jun 2018 15:11:16 +0100 Subject: [PATCH 1809/2380] i.mx7d: Remove unused header files Signed-off-by: Jean-Christophe Dubois Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/mcimx7d-sabre.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index 95fb409d9c..9c5f0e70c3 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -18,10 +18,8 @@ #include "hw/arm/fsl-imx7.h" #include "hw/boards.h" #include "sysemu/sysemu.h" -#include "sysemu/device_tree.h" #include "qemu/error-report.h" #include "sysemu/qtest.h" -#include "net/net.h" typedef struct { FslIMX7State soc; From b4cf3e6f267da6461d97a4ec154fd048eddbb430 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois Date: Fri, 29 Jun 2018 15:11:16 +0100 Subject: [PATCH 1810/2380] i.mx7d: Change SRC unimplemented device name from sdma to src Signed-off-by: Jean-Christophe Dubois Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/fsl-imx7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 26c1d27f7c..e15aadb587 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -459,7 +459,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) /* * SRC */ - create_unimplemented_device("sdma", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE); + create_unimplemented_device("src", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE); /* * Watchdog From d82fa734229c9416a0111f49bde054e594679368 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois Date: Fri, 29 Jun 2018 15:11:17 +0100 Subject: [PATCH 1811/2380] i.mx7d: Change IRQ number type from hwaddr to int MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qdev_get_gpio_in() function accept an int as second parameter. Signed-off-by: Jean-Christophe Dubois Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/fsl-imx7.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index e15aadb587..44fde03cbe 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -324,7 +324,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_ECSPI4_ADDR, }; - static const hwaddr FSL_IMX7_SPIn_IRQ[FSL_IMX7_NUM_ECSPIS] = { + static const int FSL_IMX7_SPIn_IRQ[FSL_IMX7_NUM_ECSPIS] = { FSL_IMX7_ECSPI1_IRQ, FSL_IMX7_ECSPI2_IRQ, FSL_IMX7_ECSPI3_IRQ, @@ -349,7 +349,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_I2C4_ADDR, }; - static const hwaddr FSL_IMX7_I2Cn_IRQ[FSL_IMX7_NUM_I2CS] = { + static const int FSL_IMX7_I2Cn_IRQ[FSL_IMX7_NUM_I2CS] = { FSL_IMX7_I2C1_IRQ, FSL_IMX7_I2C2_IRQ, FSL_IMX7_I2C3_IRQ, @@ -515,7 +515,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_USB3_ADDR, }; - static const hwaddr FSL_IMX7_USBn_IRQ[FSL_IMX7_NUM_USBS] = { + static const int FSL_IMX7_USBn_IRQ[FSL_IMX7_NUM_USBS] = { FSL_IMX7_USB1_IRQ, FSL_IMX7_USB2_IRQ, FSL_IMX7_USB3_IRQ, From 5110e6836b7fa698b0c383fbd5dffbee035935d3 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Fri, 29 Jun 2018 15:11:17 +0100 Subject: [PATCH 1812/2380] target/arm: Add ARM_FEATURE_V7VE for v7 Virtualization Extensions Signed-off-by: Aaron Lindsay Message-id: 1529699547-17044-5-git-send-email-alindsay@codeaurora.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 21 ++++++++++++++------- target/arm/cpu.h | 1 + target/arm/kvm32.c | 8 ++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index aa62315cea..4584cd01bc 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -800,9 +800,20 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) /* Some features automatically imply others: */ if (arm_feature(env, ARM_FEATURE_V8)) { - set_feature(env, ARM_FEATURE_V7); + set_feature(env, ARM_FEATURE_V7VE); + } + if (arm_feature(env, ARM_FEATURE_V7VE)) { + /* v7 Virtualization Extensions. In real hardware this implies + * EL2 and also the presence of the Security Extensions. + * For QEMU, for backwards-compatibility we implement some + * CPUs or CPU configs which have no actual EL2 or EL3 but do + * include the various other features that V7VE implies. + * Presence of EL2 itself is ARM_FEATURE_EL2, and of the + * Security Extensions is ARM_FEATURE_EL3. + */ set_feature(env, ARM_FEATURE_ARM_DIV); set_feature(env, ARM_FEATURE_LPAE); + set_feature(env, ARM_FEATURE_V7); } if (arm_feature(env, ARM_FEATURE_V7)) { set_feature(env, ARM_FEATURE_VAPA); @@ -1524,15 +1535,13 @@ static void cortex_a7_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cpu->dtb_compatible = "arm,cortex-a7"; - set_feature(&cpu->env, ARM_FEATURE_V7); + set_feature(&cpu->env, ARM_FEATURE_V7VE); set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); - set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_LPAE); set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A7; cpu->midr = 0x410fc075; @@ -1569,15 +1578,13 @@ static void cortex_a15_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cpu->dtb_compatible = "arm,cortex-a15"; - set_feature(&cpu->env, ARM_FEATURE_V7); + set_feature(&cpu->env, ARM_FEATURE_V7VE); set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); - set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_LPAE); set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15; cpu->midr = 0x412fc0f1; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 6a8441c2dd..7ac909448e 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1442,6 +1442,7 @@ enum arm_features { ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */ ARM_FEATURE_THUMB2EE, ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */ + ARM_FEATURE_V7VE, /* v7 Virtualization Extensions (non-EL2 parts) */ ARM_FEATURE_V4T, ARM_FEATURE_V5, ARM_FEATURE_STRONGARM, diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c index 1740cda47d..fb9ea37a31 100644 --- a/target/arm/kvm32.c +++ b/target/arm/kvm32.c @@ -98,12 +98,12 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) /* Now we've retrieved all the register information we can * set the feature bits based on the ID register fields. * We can assume any KVM supporting CPU is at least a v7 - * with VFPv3, LPAE and the generic timers; this in turn implies - * most of the other feature bits, but a few must be tested. + * with VFPv3, virtualization extensions, and the generic + * timers; this in turn implies most of the other feature + * bits, but a few must be tested. */ - set_feature(&features, ARM_FEATURE_V7); + set_feature(&features, ARM_FEATURE_V7VE); set_feature(&features, ARM_FEATURE_VFP3); - set_feature(&features, ARM_FEATURE_LPAE); set_feature(&features, ARM_FEATURE_GENERIC_TIMER); switch (extract32(id_isar0, 24, 4)) { From a6070648aaea33c6ca831b2352b92051f914ef74 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Fri, 29 Jun 2018 15:11:18 +0100 Subject: [PATCH 1813/2380] target/arm: Remove redundant DIV detection for KVM KVM implies V7VE, which implies ARM_DIV and THUMB_DIV. The conditional detection here is therefore unnecessary. Because V7VE is already unconditionally specified for all KVM hosts, ARM_DIV and THUMB_DIV are already indirectly specified and do not need to be included here at all. Signed-off-by: Aaron Lindsay Message-id: 1529699547-17044-6-git-send-email-alindsay@codeaurora.org Signed-off-by: Peter Maydell --- target/arm/kvm32.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c index fb9ea37a31..4e91c11796 100644 --- a/target/arm/kvm32.c +++ b/target/arm/kvm32.c @@ -36,7 +36,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * and then query that CPU for the relevant ID registers. */ int i, ret, fdarray[3]; - uint32_t midr, id_pfr0, id_isar0, mvfr1; + uint32_t midr, id_pfr0, mvfr1; uint64_t features = 0; /* Old kernels may not know about the PREFERRED_TARGET ioctl: however * we know these will only support creating one kind of guest CPU, @@ -58,11 +58,6 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) | ENCODE_CP_REG(15, 0, 0, 0, 1, 0, 0), .addr = (uintptr_t)&id_pfr0, }, - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 0, 2, 0, 0), - .addr = (uintptr_t)&id_isar0, - }, { .id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1, @@ -106,18 +101,6 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) set_feature(&features, ARM_FEATURE_VFP3); set_feature(&features, ARM_FEATURE_GENERIC_TIMER); - switch (extract32(id_isar0, 24, 4)) { - case 1: - set_feature(&features, ARM_FEATURE_THUMB_DIV); - break; - case 2: - set_feature(&features, ARM_FEATURE_ARM_DIV); - set_feature(&features, ARM_FEATURE_THUMB_DIV); - break; - default: - break; - } - if (extract32(id_pfr0, 12, 4) == 1) { set_feature(&features, ARM_FEATURE_THUMB2EE); } From b7d793ad3db06401bc817c0ca355a2d160c802d4 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Fri, 29 Jun 2018 15:11:18 +0100 Subject: [PATCH 1814/2380] target/arm: Mark PMINTENSET accesses as possibly doing IO This makes it match its AArch64 equivalent, PMINTENSET_EL1 Signed-off-by: Aaron Lindsay Message-id: 1529699547-17044-13-git-send-email-alindsay@codeaurora.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index d0786e0c21..60589b7eaf 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -1404,7 +1404,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .writefn = pmuserenr_write, .raw_writefn = raw_write }, { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tpm, - .type = ARM_CP_ALIAS, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pminten), .resetvalue = 0, .writefn = pmintenset_write, .raw_writefn = raw_write }, From 13606b99515e8c5f81eab7fd88a70fb2ad506cd8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 29 Jun 2018 15:11:19 +0100 Subject: [PATCH 1815/2380] sd: Don't trace SDRequest crc field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't actually implement SD command CRC checking, because for almost all of our SD controllers the CRC generation is done in hardware, and so modelling CRC generation and checking would be a bit pointless. (The exception is that milkymist-memcard makes the guest software compute the CRC.) As a result almost all of our SD controller models don't bother to set the SDRequest crc field, and the SD card model doesn't check it. So the tracing of it in sdbus_do_command() provokes Coverity warnings about use of uninitialized data. Drop the CRC field from the trace; we can always add it back if and when we do anything useful with the CRC. Fixes Coverity issues 1386072, 1386074, 1386076, 1390571. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180626180324.5537-1-peter.maydell@linaro.org --- hw/sd/core.c | 2 +- hw/sd/trace-events | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/sd/core.c b/hw/sd/core.c index 820345f704..107e6d71dd 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -91,7 +91,7 @@ int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) { SDState *card = get_card(sdbus); - trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg, req->crc); + trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg); if (card) { SDCardClass *sc = SD_CARD_GET_CLASS(card); diff --git a/hw/sd/trace-events b/hw/sd/trace-events index bfd1d62efc..43cffab8b1 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -7,7 +7,7 @@ bcm2835_sdhost_edm_change(const char *why, uint32_t edm) "(%s) EDM now 0x%x" bcm2835_sdhost_update_irq(uint32_t irq) "IRQ bits 0x%x\n" # hw/sd/core.c -sdbus_command(const char *bus_name, uint8_t cmd, uint32_t arg, uint8_t crc) "@%s CMD%02d arg 0x%08x crc 0x%02x" +sdbus_command(const char *bus_name, uint8_t cmd, uint32_t arg) "@%s CMD%02d arg 0x%08x" sdbus_read(const char *bus_name, uint8_t value) "@%s value 0x%02x" sdbus_write(const char *bus_name, uint8_t value) "@%s value 0x%02x" sdbus_set_voltage(const char *bus_name, uint16_t millivolts) "@%s %u (mV)" From b3141c0625a18d35c45c175a20826271b3241d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 29 Jun 2018 15:11:20 +0100 Subject: [PATCH 1816/2380] sdcard: Use the ldst API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The load/store API will ease further code movement. Per the Physical Layer Simplified Spec. "3.6 Bus Protocol": "In the CMD line the Most Significant Bit (MSB) is transmitted first, the Least Significant Bit (LSB) is the last." Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sd/bcm2835_sdhost.c | 13 +++++-------- hw/sd/milkymist-memcard.c | 3 +-- hw/sd/omap_mmc.c | 6 ++---- hw/sd/pl181.c | 11 ++++------- hw/sd/sdhci.c | 15 +++++---------- hw/sd/ssi-sd.c | 6 ++---- 6 files changed, 19 insertions(+), 35 deletions(-) diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index ebf3b926c2..4df4de7d67 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -118,8 +118,6 @@ static void bcm2835_sdhost_send_command(BCM2835SDHostState *s) goto error; } if (!(s->cmd & SDCMD_NO_RESPONSE)) { -#define RWORD(n) (((uint32_t)rsp[n] << 24) | (rsp[n + 1] << 16) \ - | (rsp[n + 2] << 8) | rsp[n + 3]) if (rlen == 0 || (rlen == 4 && (s->cmd & SDCMD_LONG_RESPONSE))) { goto error; } @@ -127,15 +125,14 @@ static void bcm2835_sdhost_send_command(BCM2835SDHostState *s) goto error; } if (rlen == 4) { - s->rsp[0] = RWORD(0); + s->rsp[0] = ldl_be_p(&rsp[0]); s->rsp[1] = s->rsp[2] = s->rsp[3] = 0; } else { - s->rsp[0] = RWORD(12); - s->rsp[1] = RWORD(8); - s->rsp[2] = RWORD(4); - s->rsp[3] = RWORD(0); + s->rsp[0] = ldl_be_p(&rsp[12]); + s->rsp[1] = ldl_be_p(&rsp[8]); + s->rsp[2] = ldl_be_p(&rsp[4]); + s->rsp[3] = ldl_be_p(&rsp[0]); } -#undef RWORD } /* We never really delay commands, so if this was a 'busywait' command * then we've completed it now and can raise the interrupt. diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c index fcbccf54ea..df42aa1c54 100644 --- a/hw/sd/milkymist-memcard.c +++ b/hw/sd/milkymist-memcard.c @@ -100,8 +100,7 @@ static void memcard_sd_command(MilkymistMemcardState *s) SDRequest req; req.cmd = s->command[0] & 0x3f; - req.arg = (s->command[1] << 24) | (s->command[2] << 16) - | (s->command[3] << 8) | s->command[4]; + req.arg = ldl_be_p(s->command + 1); req.crc = s->command[5]; s->response[0] = req.cmd; diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index aa2a816f76..671264b650 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -163,8 +163,7 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, CID_CSD_OVERWRITE; if (host->sdio & (1 << 13)) mask |= AKE_SEQ_ERROR; - rspstatus = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | (response[3] << 0); + rspstatus = ldl_be_p(response); break; case sd_r2: @@ -182,8 +181,7 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, } rsplen = 4; - rspstatus = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | (response[3] << 0); + rspstatus = ldl_be_p(response); if (rspstatus & 0x80000000) host->status &= 0xe000; else diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 1cc94dbfdf..3ad7e925c5 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -182,23 +182,20 @@ static void pl181_send_command(PL181State *s) if (rlen < 0) goto error; if (s->cmd & PL181_CMD_RESPONSE) { -#define RWORD(n) (((uint32_t)response[n] << 24) | (response[n + 1] << 16) \ - | (response[n + 2] << 8) | response[n + 3]) if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) goto error; if (rlen != 4 && rlen != 16) goto error; - s->response[0] = RWORD(0); + s->response[0] = ldl_be_p(&response[0]); if (rlen == 4) { s->response[1] = s->response[2] = s->response[3] = 0; } else { - s->response[1] = RWORD(4); - s->response[2] = RWORD(8); - s->response[3] = RWORD(12) & ~1; + s->response[1] = ldl_be_p(&response[4]); + s->response[2] = ldl_be_p(&response[8]); + s->response[3] = ldl_be_p(&response[12]) & ~1; } DPRINTF("Response received\n"); s->status |= PL181_STATUS_CMDRESPEND; -#undef RWORD } else { DPRINTF("Command sent\n"); s->status |= PL181_STATUS_CMDSENT; diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 3017e5a95a..321d02d75a 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -342,17 +342,13 @@ static void sdhci_send_command(SDHCIState *s) if (s->cmdreg & SDHC_CMD_RESPONSE) { if (rlen == 4) { - s->rspreg[0] = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | response[3]; + s->rspreg[0] = ldl_be_p(response); s->rspreg[1] = s->rspreg[2] = s->rspreg[3] = 0; trace_sdhci_response4(s->rspreg[0]); } else if (rlen == 16) { - s->rspreg[0] = (response[11] << 24) | (response[12] << 16) | - (response[13] << 8) | response[14]; - s->rspreg[1] = (response[7] << 24) | (response[8] << 16) | - (response[9] << 8) | response[10]; - s->rspreg[2] = (response[3] << 24) | (response[4] << 16) | - (response[5] << 8) | response[6]; + s->rspreg[0] = ldl_be_p(&response[11]); + s->rspreg[1] = ldl_be_p(&response[7]); + s->rspreg[2] = ldl_be_p(&response[3]); s->rspreg[3] = (response[0] << 16) | (response[1] << 8) | response[2]; trace_sdhci_response16(s->rspreg[3], s->rspreg[2], @@ -396,8 +392,7 @@ static void sdhci_end_transfer(SDHCIState *s) trace_sdhci_end_transfer(request.cmd, request.arg); sdbus_do_command(&s->sdbus, &request, response); /* Auto CMD12 response goes to the upper Response register */ - s->rspreg[3] = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | response[3]; + s->rspreg[3] = ldl_be_p(response); } s->prnsts &= ~(SDHC_DOING_READ | SDHC_DOING_WRITE | diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 96542ecd62..95a143bfba 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -96,8 +96,7 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val) uint8_t longresp[16]; /* FIXME: Check CRC. */ request.cmd = s->cmd; - request.arg = (s->cmdarg[0] << 24) | (s->cmdarg[1] << 16) - | (s->cmdarg[2] << 8) | s->cmdarg[3]; + request.arg = ldl_be_p(s->cmdarg); DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg); s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); if (s->arglen <= 0) { @@ -122,8 +121,7 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val) /* CMD13 returns a 2-byte statuse work. Other commands only return the first byte. */ s->arglen = (s->cmd == 13) ? 2 : 1; - cardstatus = (longresp[0] << 24) | (longresp[1] << 16) - | (longresp[2] << 8) | longresp[3]; + cardstatus = ldl_be_p(longresp); status = 0; if (((cardstatus >> 9) & 0xf) < 4) status |= SSI_SDR_IDLE; From 7e8fafbfd0537937ba8fb366a90ea6548cc31576 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Jun 2018 17:15:33 -0700 Subject: [PATCH 1817/2380] target/arm: Fix SVE signed division vs x86 overflow exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already check for the same condition within the normal integer sdiv and sdiv64 helpers. Use a slightly different formation that does not require deducing the expression type. Fixes: f97cfd596ed Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20180629001538.11415-2-richard.henderson@linaro.org [PMM: reworded a comment] Signed-off-by: Peter Maydell --- target/arm/sve_helper.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 790cbacd14..a03ca77354 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -369,7 +369,17 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ #define DO_MIN(N, M) ((N) >= (M) ? (M) : (N)) #define DO_ABD(N, M) ((N) >= (M) ? (N) - (M) : (M) - (N)) #define DO_MUL(N, M) (N * M) -#define DO_DIV(N, M) (M ? N / M : 0) + + +/* + * We must avoid the C undefined behaviour cases: division by + * zero and signed division of INT_MIN by -1. Both of these + * have architecturally defined required results for Arm. + * We special case all signed divisions by -1 to avoid having + * to deduce the minimum integer for the type involved. + */ +#define DO_SDIV(N, M) (unlikely(M == 0) ? 0 : unlikely(M == -1) ? -N : N / M) +#define DO_UDIV(N, M) (unlikely(M == 0) ? 0 : N / M) DO_ZPZZ(sve_and_zpzz_b, uint8_t, H1, DO_AND) DO_ZPZZ(sve_and_zpzz_h, uint16_t, H1_2, DO_AND) @@ -477,11 +487,11 @@ DO_ZPZZ(sve_umulh_zpzz_h, uint16_t, H1_2, do_mulh_h) DO_ZPZZ(sve_umulh_zpzz_s, uint32_t, H1_4, do_mulh_s) DO_ZPZZ_D(sve_umulh_zpzz_d, uint64_t, do_umulh_d) -DO_ZPZZ(sve_sdiv_zpzz_s, int32_t, H1_4, DO_DIV) -DO_ZPZZ_D(sve_sdiv_zpzz_d, int64_t, DO_DIV) +DO_ZPZZ(sve_sdiv_zpzz_s, int32_t, H1_4, DO_SDIV) +DO_ZPZZ_D(sve_sdiv_zpzz_d, int64_t, DO_SDIV) -DO_ZPZZ(sve_udiv_zpzz_s, uint32_t, H1_4, DO_DIV) -DO_ZPZZ_D(sve_udiv_zpzz_d, uint64_t, DO_DIV) +DO_ZPZZ(sve_udiv_zpzz_s, uint32_t, H1_4, DO_UDIV) +DO_ZPZZ_D(sve_udiv_zpzz_d, uint64_t, DO_UDIV) /* Note that all bits of the shift are significant and not modulo the element size. */ From 11d7870b1b4d038d7beb827f3afa72e284701351 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Jun 2018 17:15:34 -0700 Subject: [PATCH 1818/2380] target/arm: Fix SVE system register access checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leave ARM_CP_SVE, removing ARM_CP_FPU; the sve_access_check produced by the flag already includes fp_access_check. If we also check ARM_CP_FPU the double fp_access_check asserts. Reported-by: Laurent Desnogues Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Laurent Desnogues Message-id: 20180629001538.11415-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 8 ++++---- target/arm/translate-a64.c | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 60589b7eaf..ae70b874c7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4414,7 +4414,7 @@ static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo zcr_el1_reginfo = { .name = "ZCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_SVE | ARM_CP_FPU, + .access = PL1_RW, .type = ARM_CP_SVE, .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[1]), .writefn = zcr_write, .raw_writefn = raw_write }; @@ -4422,7 +4422,7 @@ static const ARMCPRegInfo zcr_el1_reginfo = { static const ARMCPRegInfo zcr_el2_reginfo = { .name = "ZCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 0, - .access = PL2_RW, .type = ARM_CP_SVE | ARM_CP_FPU, + .access = PL2_RW, .type = ARM_CP_SVE, .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[2]), .writefn = zcr_write, .raw_writefn = raw_write }; @@ -4430,14 +4430,14 @@ static const ARMCPRegInfo zcr_el2_reginfo = { static const ARMCPRegInfo zcr_no_el2_reginfo = { .name = "ZCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 0, - .access = PL2_RW, .type = ARM_CP_SVE | ARM_CP_FPU, + .access = PL2_RW, .type = ARM_CP_SVE, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }; static const ARMCPRegInfo zcr_el3_reginfo = { .name = "ZCR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 2, .opc2 = 0, - .access = PL3_RW, .type = ARM_CP_SVE | ARM_CP_FPU, + .access = PL3_RW, .type = ARM_CP_SVE, .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[3]), .writefn = zcr_write, .raw_writefn = raw_write }; diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index f986340832..45a6c2a3aa 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -1633,11 +1633,10 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, default: break; } - if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { - return; - } if ((ri->type & ARM_CP_FPU) && !fp_access_check(s)) { return; + } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { + return; } if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { From 156a7065365578deb3d63c2b5b69a4b5999a8fcc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Jun 2018 17:15:35 -0700 Subject: [PATCH 1819/2380] target/arm: Prune a57 features from max MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to re-set these 9 features already implied by the call to aarch64_a57_initfn. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20180629001538.11415-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu64.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 3b4bc73ffa..8040493d5c 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -235,19 +235,10 @@ static void aarch64_max_initfn(Object *obj) * whereas the architecture requires them to be present in both if * present in either. */ - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_VFP4); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - set_feature(&cpu->env, ARM_FEATURE_V8_AES); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); set_feature(&cpu->env, ARM_FEATURE_V8_SHA512); set_feature(&cpu->env, ARM_FEATURE_V8_SHA3); set_feature(&cpu->env, ARM_FEATURE_V8_SM3); set_feature(&cpu->env, ARM_FEATURE_V8_SM4); - set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); - set_feature(&cpu->env, ARM_FEATURE_CRC); set_feature(&cpu->env, ARM_FEATURE_V8_ATOMICS); set_feature(&cpu->env, ARM_FEATURE_V8_RDM); set_feature(&cpu->env, ARM_FEATURE_V8_DOTPROD); From 0b33968e7f4cf998f678b2d1a5be3d6f3f3513d8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Jun 2018 17:15:36 -0700 Subject: [PATCH 1820/2380] target/arm: Prune a15 features from max MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to re-set these 3 features already implied by the call to aarch64_a15_initfn. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20180629001538.11415-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 4584cd01bc..646b122e16 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1803,9 +1803,6 @@ static void arm_max_initfn(Object *obj) * since we don't correctly set the ID registers to advertise them, */ set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_VFP4); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_V8_AES); set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); From 802abf4024d23e48d45373ac3f2b580124b54b47 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Jun 2018 17:15:37 -0700 Subject: [PATCH 1821/2380] target/arm: Add ID_ISAR6 This register was added to aa32 state by ARMv8.2. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20180629001538.11415-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 4 ++++ target/arm/cpu.h | 1 + target/arm/cpu64.c | 2 ++ target/arm/helper.c | 5 ++--- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 646b122e16..82ff450f9a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1273,6 +1273,7 @@ static void cortex_m3_initfn(Object *obj) cpu->id_isar3 = 0x01111110; cpu->id_isar4 = 0x01310102; cpu->id_isar5 = 0x00000000; + cpu->id_isar6 = 0x00000000; } static void cortex_m4_initfn(Object *obj) @@ -1299,6 +1300,7 @@ static void cortex_m4_initfn(Object *obj) cpu->id_isar3 = 0x01111110; cpu->id_isar4 = 0x01310102; cpu->id_isar5 = 0x00000000; + cpu->id_isar6 = 0x00000000; } static void cortex_m33_initfn(Object *obj) @@ -1327,6 +1329,7 @@ static void cortex_m33_initfn(Object *obj) cpu->id_isar3 = 0x01111131; cpu->id_isar4 = 0x01310132; cpu->id_isar5 = 0x00000000; + cpu->id_isar6 = 0x00000000; cpu->clidr = 0x00000000; cpu->ctr = 0x8000c000; } @@ -1377,6 +1380,7 @@ static void cortex_r5_initfn(Object *obj) cpu->id_isar3 = 0x01112131; cpu->id_isar4 = 0x0010142; cpu->id_isar5 = 0x0; + cpu->id_isar6 = 0x0; cpu->mp_is_up = true; cpu->pmsav7_dregion = 16; define_arm_cp_regs(cpu, cortexr5_cp_reginfo); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 7ac909448e..e310ffc29d 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -813,6 +813,7 @@ struct ARMCPU { uint32_t id_isar3; uint32_t id_isar4; uint32_t id_isar5; + uint32_t id_isar6; uint64_t id_aa64pfr0; uint64_t id_aa64pfr1; uint64_t id_aa64dfr0; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 8040493d5c..d0581d59d8 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -139,6 +139,7 @@ static void aarch64_a57_initfn(Object *obj) cpu->id_isar3 = 0x01112131; cpu->id_isar4 = 0x00011142; cpu->id_isar5 = 0x00011121; + cpu->id_isar6 = 0; cpu->id_aa64pfr0 = 0x00002222; cpu->id_aa64dfr0 = 0x10305106; cpu->pmceid0 = 0x00000000; @@ -199,6 +200,7 @@ static void aarch64_a53_initfn(Object *obj) cpu->id_isar3 = 0x01112131; cpu->id_isar4 = 0x00011142; cpu->id_isar5 = 0x00011121; + cpu->id_isar6 = 0; cpu->id_aa64pfr0 = 0x00002222; cpu->id_aa64dfr0 = 0x10305106; cpu->id_aa64isar0 = 0x00011120; diff --git a/target/arm/helper.c b/target/arm/helper.c index ae70b874c7..a2ac96084e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4872,11 +4872,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_mmfr4 }, - /* 7 is as yet unallocated and must RAZ */ - { .name = "ID_ISAR7_RESERVED", .state = ARM_CP_STATE_BOTH, + { .name = "ID_ISAR6", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = 0 }, + .resetvalue = cpu->id_isar6 }, REGINFO_SENTINEL }; define_arm_cp_regs(cpu, v6_idregs); From d88610111b40bca19925ece0fa81710d425725a8 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 20 Jun 2018 15:32:17 +0800 Subject: [PATCH 1822/2380] chardev: comment details for CLOSED event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was unclear before on what does the CLOSED event mean. Meanwhile we add a TODO to fix up the CLOSED event in the future when the in/out ports are different for a chardev. CC: Paolo Bonzini CC: "Marc-André Lureau" CC: Stefan Hajnoczi CC: Markus Armbruster Reviewed-by: Stefan Hajnoczi Signed-off-by: Peter Xu Message-Id: <20180620073223.31964-2-peterx@redhat.com> Signed-off-by: Markus Armbruster --- include/chardev/char.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/chardev/char.h b/include/chardev/char.h index 04de45795e..6f0576e214 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -22,7 +22,16 @@ typedef enum { CHR_EVENT_OPENED, /* new connection established */ CHR_EVENT_MUX_IN, /* mux-focus was set to this terminal */ CHR_EVENT_MUX_OUT, /* mux-focus will move on */ - CHR_EVENT_CLOSED /* connection closed */ + CHR_EVENT_CLOSED /* connection closed. NOTE: currently this event + * is only bound to the read port of the chardev. + * Normally the read port and write port of a + * chardev should be the same, but it can be + * different, e.g., for fd chardevs, when the two + * fds are different. So when we received the + * CLOSED event it's still possible that the out + * port is still open. TODO: we should only send + * the CLOSED event when both ports are closed. + */ } QEMUChrEvent; #define CHR_READ_BUF_LEN 4096 From 40687eb741a974c47326ef3cf7f6b25cc0680552 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 20 Jun 2018 15:32:18 +0800 Subject: [PATCH 1823/2380] monitor: rename *_pop_one to *_pop_any The old names are confusing since both of the old functions are popping an item from multiple queues rather than a single queue. In that sense, *_pop_any() suites better than *_pop_one(). Since at it, touch up the function monitor_qmp_response_pop_any() a bit to let the callers pass in a QMPResponse struct instead of returning a struct. Change the return value to boolean to mark whether we have popped a valid response instead. Suggested-by: Markus Armbruster Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180620073223.31964-3-peterx@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/monitor.c b/monitor.c index 7b473aad1f..2881345758 100644 --- a/monitor.c +++ b/monitor.c @@ -542,10 +542,10 @@ struct QMPResponse { typedef struct QMPResponse QMPResponse; /* - * Return one QMPResponse. The response is only valid if - * response.data is not NULL. + * Pop a QMPResponse from any monitor's response queue into @response. + * Return false if all the queues are empty; else true. */ -static QMPResponse monitor_qmp_response_pop_one(void) +static bool monitor_qmp_response_pop_any(QMPResponse *response) { Monitor *mon; QObject *data = NULL; @@ -556,22 +556,20 @@ static QMPResponse monitor_qmp_response_pop_one(void) data = g_queue_pop_head(mon->qmp.qmp_responses); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); if (data) { + response->mon = mon; + response->data = data; break; } } qemu_mutex_unlock(&monitor_lock); - return (QMPResponse) { .mon = mon, .data = data }; + return data != NULL; } static void monitor_qmp_bh_responder(void *opaque) { QMPResponse response; - while (true) { - response = monitor_qmp_response_pop_one(); - if (!response.data) { - break; - } + while (monitor_qmp_response_pop_any(&response)) { monitor_json_emitter_raw(response.mon, response.data); qobject_unref(response.data); } @@ -4199,7 +4197,7 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) * when we process one request on a specific monitor, we put that * monitor to the end of mon_list queue. */ -static QMPRequest *monitor_qmp_requests_pop_one(void) +static QMPRequest *monitor_qmp_requests_pop_any(void) { QMPRequest *req_obj = NULL; Monitor *mon; @@ -4231,7 +4229,7 @@ static QMPRequest *monitor_qmp_requests_pop_one(void) static void monitor_qmp_bh_dispatcher(void *data) { - QMPRequest *req_obj = monitor_qmp_requests_pop_one(); + QMPRequest *req_obj = monitor_qmp_requests_pop_any(); if (req_obj) { trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); From c73a843b4a822dc0bd5df5ef38dbd681dd96ad25 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 20 Jun 2018 15:32:19 +0800 Subject: [PATCH 1824/2380] monitor: flush qmp responses when CLOSED Previously we clean up the queues when we got CLOSED event. It was used to make sure we won't send leftover replies/events of a old client to a new client which makes perfect sense. However this will also drop the replies/events even if the output port of the previous chardev backend is still open, which can lead to missing of the last replies/events. Now this patch does an extra operation to flush the response queue before cleaning up. In most cases, a QMP session will be based on a bidirectional channel (a TCP port, for example, we read/write to the same socket handle), so in port and out port of the backend chardev are fundamentally the same port. In these cases, it does not really matter much on whether we'll flush the response queue since flushing will fail anyway. However there can be cases where in & out ports of the QMP monitor's backend chardev are separated. Here is an example: cat $QMP_COMMANDS | qemu -qmp stdio ... | filter_commands In this case, the backend is fd-typed, and it is connected to stdio where in port is stdin and out port is stdout. Now if we drop all the events on the response queue then filter_command process might miss some events that it might expect. The thing is that, when stdin closes, stdout might still be there alive! In practice, I encountered SHUTDOWN event missing when running test with iotest 087 with Out-Of-Band enabled. Here is one of the ways that this can happen (after "quit" command is executed and QEMU quits the main loop): 1. [main thread] QEMU queues a SHUTDOWN event into response queue. 2. "cat" terminates (to distinguish it from the animal, I quote it). 3. [monitor iothread] QEMU's monitor iothread reads EOF from stdin. 4. [monitor iothread] QEMU's monitor iothread calls the CLOSED event hook for the monitor, which will destroy the response queue of the monitor, then the SHUTDOWN event is dropped. 5. [main thread] QEMU's main thread cleans up the monitors in monitor_cleanup(). When trying to flush pending responses, it sees nothing. SHUTDOWN is lost forever. Note that before the monitor iothread was introduced, step [4]/[5] could never happen since the main loop was the only place to detect the EOF event of stdin and run the CLOSED event hooks. Now things can happen in parallel in the iothread. Without this patch, iotest 087 will have ~10% chance to miss the SHUTDOWN event and fail when with Out-Of-Band enabled: --- /home/peterx/git/qemu/tests/qemu-iotests/087.out +++ /home/peterx/git/qemu/bin/tests/qemu-iotests/087.out.bad @@ -8,7 +8,6 @@ {"return": {}} {"error": {"class": "GenericError", "desc": "'node-name' must be specified for the root node"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} === Duplicate ID === @@ -53,7 +52,6 @@ {"return": {}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} This patch fixes the problem. Fixes: 6d2d563f8c ("qmp: cleanup qmp queues properly", 2018-03-27) Suggested-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180620073223.31964-4-peterx@redhat.com> Reviewed-by: Markus Armbruster [Commit message and a comment touched up] Signed-off-by: Markus Armbruster --- monitor.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/monitor.c b/monitor.c index 2881345758..567668a0e7 100644 --- a/monitor.c +++ b/monitor.c @@ -541,6 +541,27 @@ struct QMPResponse { }; typedef struct QMPResponse QMPResponse; +static QObject *monitor_qmp_response_pop_one(Monitor *mon) +{ + QObject *data; + + qemu_mutex_lock(&mon->qmp.qmp_queue_lock); + data = g_queue_pop_head(mon->qmp.qmp_responses); + qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); + + return data; +} + +static void monitor_qmp_response_flush(Monitor *mon) +{ + QObject *data; + + while ((data = monitor_qmp_response_pop_one(mon))) { + monitor_json_emitter_raw(mon, data); + qobject_unref(data); + } +} + /* * Pop a QMPResponse from any monitor's response queue into @response. * Return false if all the queues are empty; else true. @@ -552,9 +573,7 @@ static bool monitor_qmp_response_pop_any(QMPResponse *response) qemu_mutex_lock(&monitor_lock); QTAILQ_FOREACH(mon, &mon_list, entry) { - qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - data = g_queue_pop_head(mon->qmp.qmp_responses); - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); + data = monitor_qmp_response_pop_one(mon); if (data) { response->mon = mon; response->data = data; @@ -4456,6 +4475,13 @@ static void monitor_qmp_event(void *opaque, int event) mon_refcount++; break; case CHR_EVENT_CLOSED: + /* + * Note: this is only useful when the output of the chardev + * backend is still open. For example, when the backend is + * stdio, it's possible that stdout is still open when stdin + * is closed. + */ + monitor_qmp_response_flush(mon); monitor_qmp_cleanup_queues(mon); json_message_parser_destroy(&mon->qmp.parser); json_message_parser_init(&mon->qmp.parser, handle_qmp_command); From cbc4ae2d1a9f4a9fd6c2f5000d3845f6d28cccc1 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 20 Jun 2018 15:32:20 +0800 Subject: [PATCH 1825/2380] tests: iotests: drop some stderr line In my Out-Of-Band test, "check -qcow2 060" fail with this: --- /home/peterx/git/qemu/tests/qemu-iotests/060.out +++ /home/peterx/git/qemu/bin/tests/qemu-iotests/060.out.bad @@ -427,8 +427,8 @@ QMP_VERSION {"return": {}} qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a0 0 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}} read failed: Input/output error +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a0 0 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}} {"return": ""} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} The order of the event and the in/out error line is swapped. I didn't dig up the reason, but AFAIU what we want to verify is the event rather than stderr. Let's drop the stderr line directly for this test. Signed-off-by: Peter Xu Message-Id: <20180620073223.31964-5-peterx@redhat.com> [Commit message touched up] Signed-off-by: Markus Armbruster --- tests/qemu-iotests/060 | 10 +++++++++- tests/qemu-iotests/060.out | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index 7bdf609f3f..74ad371885 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -33,6 +33,14 @@ _cleanup() } trap "_cleanup; exit \$status" 0 1 2 3 15 +# Sometimes the error line might be dumped before/after an event +# randomly. Mask it out for specific test that may trigger this +# uncertainty for current test for now. +_filter_io_error() +{ + sed '/Input\/output error/d' +} + # get standard environment, filters and checks . ./common.rc . ./common.filter @@ -464,7 +472,7 @@ echo "{'execute': 'qmp_capabilities'} }}" \ -incoming exec:'cat /dev/null' \ 2>&1 \ - | _filter_qmp | _filter_qemu_io + | _filter_qmp | _filter_qemu_io | _filter_io_error echo # Image should not have been marked corrupt diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index bff023d889..d67c6234a4 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -428,7 +428,6 @@ QMP_VERSION {"return": {}} qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}} -read failed: Input/output error {"return": ""} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} From 4bfa7974d90a0cbad29c0a27334d02cbd37bb23d Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 20 Jun 2018 15:32:21 +0800 Subject: [PATCH 1826/2380] docs: mention shared state protect for OOB Out-Of-Band handlers need to protect shared state if there is any. Mention it in the document. Meanwhile, touch up some other places too, either with better English, or reordering of bullets. Suggested-by: Markus Armbruster Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180620073223.31964-6-peterx@redhat.com> Signed-off-by: Markus Armbruster --- docs/devel/qapi-code-gen.txt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 88a70e4d45..94a7e8f4d0 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -666,22 +666,27 @@ command: - They are executed in order, - They run only in main thread of QEMU, -- They have the BQL taken during execution. +- They run with the BQL held. When a command is executed with OOB, the following changes occur: - They can be completed before a pending in-band command, - They run in a dedicated monitor thread, -- They do not take the BQL during execution. +- They run with the BQL not held. OOB command handlers must satisfy the following conditions: -- It executes extremely fast, -- It does not take any lock, or, it can take very small locks if all - critical regions also follow the rules for OOB command handler code, +- It terminates quickly, - It does not invoke system calls that may block, - It does not access guest RAM that may block when userfaultfd is - enabled for postcopy live migration. + enabled for postcopy live migration, +- It takes only "fast" locks, i.e. all critical sections protected by + any lock it takes also satisfy the conditions for OOB command + handler code. + +The restrictions on locking limit access to shared state. Such access +requires synchronization, but OOB commands can't take the BQL or any +other "slow" lock. If in doubt, do not implement OOB execution support. From f40385c959d01bf33a0e3c12ef0fdb402ad98a1c Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 27 Apr 2018 13:07:53 -0700 Subject: [PATCH 1827/2380] target/xtensa: check zero overhead loop alignment ISA book documents that the first instruction of zero overhead loop must fit completely into naturally aligned region of an instruction fetch unit size. Check that condition and log a message if it's violated. Signed-off-by: Max Filippov --- target/xtensa/cpu.h | 1 + target/xtensa/overlay_tool.h | 1 + target/xtensa/translate.c | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index e9d2e109f7..51b4551464 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -369,6 +369,7 @@ struct XtensaConfig { unsigned nareg; int excm_level; int ndepc; + unsigned inst_fetch_width; uint32_t vecbase; uint32_t exception_vector[EXC_MAX]; unsigned ninterrupt; diff --git a/target/xtensa/overlay_tool.h b/target/xtensa/overlay_tool.h index b24ad11fec..ee37a04a17 100644 --- a/target/xtensa/overlay_tool.h +++ b/target/xtensa/overlay_tool.h @@ -456,6 +456,7 @@ .options = XTENSA_OPTIONS, \ .nareg = XCHAL_NUM_AREGS, \ .ndepc = (XCHAL_XEA_VERSION >= 2), \ + .inst_fetch_width = XCHAL_INST_FETCH_WIDTH, \ EXCEPTIONS_SECTION, \ INTERRUPTS_SECTION, \ TLB_SECTION, \ diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index a11162eebe..7dd8b55d4a 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -970,6 +970,13 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) } dc->next_pc = dc->pc + len; + if (xtensa_option_enabled(dc->config, XTENSA_OPTION_LOOP) && + dc->lbeg == dc->pc && + ((dc->pc ^ (dc->next_pc - 1)) & -dc->config->inst_fetch_width)) { + qemu_log_mask(LOG_GUEST_ERROR, + "unaligned first instruction of a loop (pc = %08x)\n", + dc->pc); + } for (i = 1; i < len; ++i) { b[i] = cpu_ldub_code(env, dc->pc + i); } From f3531da588b47a240a6c9f8bbd84835efed4e902 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 12 May 2018 10:57:21 -0700 Subject: [PATCH 1828/2380] target/xtensa: Replace DISAS_UPDATE with DISAS_NORETURN The usage of DISAS_UPDATE is after noreturn helpers. It is thus indistinguishable from DISAS_NORETURN. Signed-off-by: Richard Henderson Signed-off-by: Max Filippov --- target/xtensa/translate.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 7dd8b55d4a..0f4997b11f 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -47,9 +47,6 @@ #include "exec/log.h" -/* is_jmp field values */ -#define DISAS_UPDATE DISAS_TARGET_0 /* cpu state was modified dynamically */ - struct DisasContext { const XtensaConfig *config; TranslationBlock *tb; @@ -317,7 +314,7 @@ static void gen_exception_cause(DisasContext *dc, uint32_t cause) tcg_temp_free(tcause); if (cause == ILLEGAL_INSTRUCTION_CAUSE || cause == SYSCALL_CAUSE) { - dc->is_jmp = DISAS_UPDATE; + dc->is_jmp = DISAS_NORETURN; } } @@ -339,7 +336,7 @@ static void gen_debug_exception(DisasContext *dc, uint32_t cause) tcg_temp_free(tpc); tcg_temp_free(tcause); if (cause & (DEBUGCAUSE_IB | DEBUGCAUSE_BI | DEBUGCAUSE_BN)) { - dc->is_jmp = DISAS_UPDATE; + dc->is_jmp = DISAS_NORETURN; } } @@ -351,7 +348,7 @@ static bool gen_check_privilege(DisasContext *dc) } #endif gen_exception_cause(dc, PRIVILEGED_CAUSE); - dc->is_jmp = DISAS_UPDATE; + dc->is_jmp = DISAS_NORETURN; return false; } @@ -360,7 +357,7 @@ static bool gen_check_cpenable(DisasContext *dc, unsigned cp) if (option_enabled(dc, XTENSA_OPTION_COPROCESSOR) && !(dc->cpenable & (1 << cp))) { gen_exception_cause(dc, COPROCESSOR0_DISABLED + cp); - dc->is_jmp = DISAS_UPDATE; + dc->is_jmp = DISAS_NORETURN; return false; } return true; @@ -382,7 +379,7 @@ static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot) tcg_gen_exit_tb(NULL, 0); } } - dc->is_jmp = DISAS_UPDATE; + dc->is_jmp = DISAS_NORETURN; } static void gen_jump(DisasContext *dc, TCGv dest) @@ -918,7 +915,7 @@ static bool gen_window_check1(DisasContext *dc, unsigned r1) TCGv_i32 w = tcg_const_i32(r1 / 4); gen_helper_window_check(cpu_env, pc, w); - dc->is_jmp = DISAS_UPDATE; + dc->is_jmp = DISAS_NORETURN; return false; } return true; @@ -1110,14 +1107,14 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) tcg_gen_insn_start(dc.pc); ++insn_count; gen_exception(&dc, EXCP_YIELD); - dc.is_jmp = DISAS_UPDATE; + dc.is_jmp = DISAS_NORETURN; goto done; } if (tb->flags & XTENSA_TBFLAG_EXCEPTION) { tcg_gen_insn_start(dc.pc); ++insn_count; gen_exception(&dc, EXCP_DEBUG); - dc.is_jmp = DISAS_UPDATE; + dc.is_jmp = DISAS_NORETURN; goto done; } @@ -1128,7 +1125,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) if (unlikely(cpu_breakpoint_test(cs, dc.pc, BP_ANY))) { tcg_gen_movi_i32(cpu_pc, dc.pc); gen_exception(&dc, EXCP_DEBUG); - dc.is_jmp = DISAS_UPDATE; + dc.is_jmp = DISAS_NORETURN; /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that From 3cc18eec0a6f22458955a718388e4b6a4d6a2d1d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 12 May 2018 10:57:22 -0700 Subject: [PATCH 1829/2380] target/xtensa: Convert to DisasContextBase Signed-off-by: Richard Henderson Signed-off-by: Max Filippov --- target/xtensa/translate.c | 91 +++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 47 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 0f4997b11f..721e3343a7 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -48,16 +48,13 @@ struct DisasContext { + DisasContextBase base; const XtensaConfig *config; - TranslationBlock *tb; uint32_t pc; - uint32_t next_pc; int cring; int ring; uint32_t lbeg; uint32_t lend; - int is_jmp; - int singlestep_enabled; bool sar_5bit; bool sar_m32_5bit; @@ -314,7 +311,7 @@ static void gen_exception_cause(DisasContext *dc, uint32_t cause) tcg_temp_free(tcause); if (cause == ILLEGAL_INSTRUCTION_CAUSE || cause == SYSCALL_CAUSE) { - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; } } @@ -336,7 +333,7 @@ static void gen_debug_exception(DisasContext *dc, uint32_t cause) tcg_temp_free(tpc); tcg_temp_free(tcause); if (cause & (DEBUGCAUSE_IB | DEBUGCAUSE_BI | DEBUGCAUSE_BN)) { - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; } } @@ -348,7 +345,7 @@ static bool gen_check_privilege(DisasContext *dc) } #endif gen_exception_cause(dc, PRIVILEGED_CAUSE); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; return false; } @@ -357,7 +354,7 @@ static bool gen_check_cpenable(DisasContext *dc, unsigned cp) if (option_enabled(dc, XTENSA_OPTION_COPROCESSOR) && !(dc->cpenable & (1 << cp))) { gen_exception_cause(dc, COPROCESSOR0_DISABLED + cp); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; return false; } return true; @@ -369,17 +366,17 @@ static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot) if (dc->icount) { tcg_gen_mov_i32(cpu_SR[ICOUNT], dc->next_icount); } - if (dc->singlestep_enabled) { + if (dc->base.singlestep_enabled) { gen_exception(dc, EXCP_DEBUG); } else { if (slot >= 0) { tcg_gen_goto_tb(slot); - tcg_gen_exit_tb(dc->tb, slot); + tcg_gen_exit_tb(dc->base.tb, slot); } else { tcg_gen_exit_tb(NULL, 0); } } - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; } static void gen_jump(DisasContext *dc, TCGv dest) @@ -391,7 +388,7 @@ static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot) { TCGv_i32 tmp = tcg_const_i32(dest); #ifndef CONFIG_USER_ONLY - if (((dc->tb->pc ^ dest) & TARGET_PAGE_MASK) != 0) { + if (((dc->base.pc_first ^ dest) & TARGET_PAGE_MASK) != 0) { slot = -1; } #endif @@ -408,7 +405,7 @@ static void gen_callw_slot(DisasContext *dc, int callinc, TCGv_i32 dest, tcallinc, PS_CALLINC_SHIFT, PS_CALLINC_LEN); tcg_temp_free(tcallinc); tcg_gen_movi_i32(cpu_R[callinc << 2], - (callinc << 30) | (dc->next_pc & 0x3fffffff)); + (callinc << 30) | (dc->base.pc_next & 0x3fffffff)); gen_jump_slot(dc, dest, slot); } @@ -421,7 +418,7 @@ static void gen_callwi(DisasContext *dc, int callinc, uint32_t dest, int slot) { TCGv_i32 tmp = tcg_const_i32(dest); #ifndef CONFIG_USER_ONLY - if (((dc->tb->pc ^ dest) & TARGET_PAGE_MASK) != 0) { + if (((dc->base.pc_first ^ dest) & TARGET_PAGE_MASK) != 0) { slot = -1; } #endif @@ -432,15 +429,15 @@ static void gen_callwi(DisasContext *dc, int callinc, uint32_t dest, int slot) static bool gen_check_loop_end(DisasContext *dc, int slot) { if (option_enabled(dc, XTENSA_OPTION_LOOP) && - !(dc->tb->flags & XTENSA_TBFLAG_EXCM) && - dc->next_pc == dc->lend) { + !(dc->base.tb->flags & XTENSA_TBFLAG_EXCM) && + dc->base.pc_next == dc->lend) { TCGLabel *label = gen_new_label(); tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_SR[LCOUNT], 0, label); tcg_gen_subi_i32(cpu_SR[LCOUNT], cpu_SR[LCOUNT], 1); gen_jumpi(dc, dc->lbeg, slot); gen_set_label(label); - gen_jumpi(dc, dc->next_pc, -1); + gen_jumpi(dc, dc->base.pc_next, -1); return true; } return false; @@ -449,7 +446,7 @@ static bool gen_check_loop_end(DisasContext *dc, int slot) static void gen_jumpi_check_loop_end(DisasContext *dc, int slot) { if (!gen_check_loop_end(dc, slot)) { - gen_jumpi(dc, dc->next_pc, slot); + gen_jumpi(dc, dc->base.pc_next, slot); } } @@ -500,12 +497,12 @@ static bool gen_check_sr(DisasContext *dc, uint32_t sr, unsigned access) #ifndef CONFIG_USER_ONLY static bool gen_rsr_ccount(DisasContext *dc, TCGv_i32 d, uint32_t sr) { - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_update_ccount(cpu_env); tcg_gen_mov_i32(d, cpu_SR[sr]); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); return true; } @@ -689,11 +686,11 @@ static bool gen_wsr_cpenable(DisasContext *dc, uint32_t sr, TCGv_i32 v) static void gen_check_interrupts(DisasContext *dc) { - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_check_interrupts(cpu_env); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } } @@ -747,11 +744,11 @@ static bool gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) static bool gen_wsr_ccount(DisasContext *dc, uint32_t sr, TCGv_i32 v) { - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_wsr_ccount(cpu_env, v); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); gen_jumpi_check_loop_end(dc, 0); return true; @@ -788,11 +785,11 @@ static bool gen_wsr_ccompare(DisasContext *dc, uint32_t sr, TCGv_i32 v) tcg_gen_mov_i32(cpu_SR[sr], v); tcg_gen_andi_i32(cpu_SR[INTSET], cpu_SR[INTSET], ~int_bit); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_update_ccompare(cpu_env, tmp); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); gen_jumpi_check_loop_end(dc, 0); ret = true; @@ -892,14 +889,14 @@ static void gen_load_store_alignment(DisasContext *dc, int shift, #ifndef CONFIG_USER_ONLY static void gen_waiti(DisasContext *dc, uint32_t imm4) { - TCGv_i32 pc = tcg_const_i32(dc->next_pc); + TCGv_i32 pc = tcg_const_i32(dc->base.pc_next); TCGv_i32 intlevel = tcg_const_i32(imm4); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_waiti(cpu_env, pc, intlevel); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } tcg_temp_free(pc); @@ -915,7 +912,7 @@ static bool gen_window_check1(DisasContext *dc, unsigned r1) TCGv_i32 w = tcg_const_i32(r1 / 4); gen_helper_window_check(cpu_env, pc, w); - dc->is_jmp = DISAS_NORETURN; + dc->base.is_jmp = DISAS_NORETURN; return false; } return true; @@ -966,10 +963,10 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) return; } - dc->next_pc = dc->pc + len; + dc->base.pc_next = dc->pc + len; if (xtensa_option_enabled(dc->config, XTENSA_OPTION_LOOP) && dc->lbeg == dc->pc && - ((dc->pc ^ (dc->next_pc - 1)) & -dc->config->inst_fetch_width)) { + ((dc->pc ^ (dc->base.pc_next - 1)) & -dc->config->inst_fetch_width)) { qemu_log_mask(LOG_GUEST_ERROR, "unaligned first instruction of a loop (pc = %08x)\n", dc->pc); @@ -1033,10 +1030,10 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) return; } } - if (dc->is_jmp == DISAS_NEXT) { + if (dc->base.is_jmp == DISAS_NEXT) { gen_check_loop_end(dc, 0); } - dc->pc = dc->next_pc; + dc->pc = dc->base.pc_next; } static inline unsigned xtensa_insn_len(CPUXtensaState *env, DisasContext *dc) @@ -1075,14 +1072,14 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) } dc.config = env->config; - dc.singlestep_enabled = cs->singlestep_enabled; - dc.tb = tb; + dc.base.singlestep_enabled = cs->singlestep_enabled; + dc.base.tb = tb; dc.pc = pc_start; dc.ring = tb->flags & XTENSA_TBFLAG_RING_MASK; dc.cring = (tb->flags & XTENSA_TBFLAG_EXCM) ? 0 : dc.ring; dc.lbeg = env->sregs[LBEG]; dc.lend = env->sregs[LEND]; - dc.is_jmp = DISAS_NEXT; + dc.base.is_jmp = DISAS_NEXT; dc.debug = tb->flags & XTENSA_TBFLAG_DEBUG; dc.icount = tb->flags & XTENSA_TBFLAG_ICOUNT; dc.cpenable = (tb->flags & XTENSA_TBFLAG_CPENABLE_MASK) >> @@ -1107,14 +1104,14 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) tcg_gen_insn_start(dc.pc); ++insn_count; gen_exception(&dc, EXCP_YIELD); - dc.is_jmp = DISAS_NORETURN; + dc.base.is_jmp = DISAS_NORETURN; goto done; } if (tb->flags & XTENSA_TBFLAG_EXCEPTION) { tcg_gen_insn_start(dc.pc); ++insn_count; gen_exception(&dc, EXCP_DEBUG); - dc.is_jmp = DISAS_NORETURN; + dc.base.is_jmp = DISAS_NORETURN; goto done; } @@ -1125,7 +1122,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) if (unlikely(cpu_breakpoint_test(cs, dc.pc, BP_ANY))) { tcg_gen_movi_i32(cpu_pc, dc.pc); gen_exception(&dc, EXCP_DEBUG); - dc.is_jmp = DISAS_NORETURN; + dc.base.is_jmp = DISAS_NORETURN; /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that @@ -1163,7 +1160,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) gen_exception(&dc, EXCP_DEBUG); break; } - } while (dc.is_jmp == DISAS_NEXT && + } while (dc.base.is_jmp == DISAS_NEXT && insn_count < max_insns && dc.pc - page_start < TARGET_PAGE_SIZE && dc.pc - page_start + xtensa_insn_len(env, &dc) <= TARGET_PAGE_SIZE @@ -1182,7 +1179,7 @@ done: gen_io_end(); } - if (dc.is_jmp == DISAS_NEXT) { + if (dc.base.is_jmp == DISAS_NEXT) { gen_jumpi(&dc, dc.pc, 0); } gen_tb_end(tb, insn_count); @@ -1485,7 +1482,7 @@ static void translate_break(DisasContext *dc, const uint32_t arg[], static void translate_call0(DisasContext *dc, const uint32_t arg[], const uint32_t par[]) { - tcg_gen_movi_i32(cpu_R[0], dc->next_pc); + tcg_gen_movi_i32(cpu_R[0], dc->base.pc_next); gen_jumpi(dc, arg[0], 0); } @@ -1503,7 +1500,7 @@ static void translate_callx0(DisasContext *dc, const uint32_t arg[], if (gen_window_check1(dc, arg[0])) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_mov_i32(tmp, cpu_R[arg[0]]); - tcg_gen_movi_i32(cpu_R[0], dc->next_pc); + tcg_gen_movi_i32(cpu_R[0], dc->base.pc_next); gen_jump(dc, tmp); tcg_temp_free(tmp); } @@ -1703,7 +1700,7 @@ static void translate_l32r(DisasContext *dc, const uint32_t arg[], if (gen_window_check1(dc, arg[0])) { TCGv_i32 tmp; - if (dc->tb->flags & XTENSA_TBFLAG_LITBASE) { + if (dc->base.tb->flags & XTENSA_TBFLAG_LITBASE) { tmp = tcg_const_i32(dc->raw_arg[1] - 1); tcg_gen_add_i32(tmp, cpu_SR[LITBASE], tmp); } else { @@ -1722,7 +1719,7 @@ static void translate_loop(DisasContext *dc, const uint32_t arg[], TCGv_i32 tmp = tcg_const_i32(lend); tcg_gen_subi_i32(cpu_SR[LCOUNT], cpu_R[arg[0]], 1); - tcg_gen_movi_i32(cpu_SR[LBEG], dc->next_pc); + tcg_gen_movi_i32(cpu_SR[LBEG], dc->base.pc_next); gen_helper_wsr_lend(cpu_env, tmp); tcg_temp_free(tmp); @@ -1733,7 +1730,7 @@ static void translate_loop(DisasContext *dc, const uint32_t arg[], gen_set_label(label); } - gen_jumpi(dc, dc->next_pc, 0); + gen_jumpi(dc, dc->base.pc_next, 0); } } From 1d38a7011f05e6fdc7c6bc38e4cae789b00745e3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 12 May 2018 10:57:23 -0700 Subject: [PATCH 1830/2380] target/xtensa: Change gen_intermediate_code dc to pointer This will reduce the size of the patch in the next patch, where the context will have to be a pointer. Signed-off-by: Richard Henderson Signed-off-by: Max Filippov --- target/xtensa/translate.c | 122 +++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 721e3343a7..60375d923b 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -1058,7 +1058,7 @@ static void gen_ibreak_check(CPUXtensaState *env, DisasContext *dc) void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) { CPUXtensaState *env = cs->env_ptr; - DisasContext dc; + DisasContext dc1, *dc = &dc1; int insn_count = 0; int max_insns = tb_cflags(tb) & CF_COUNT_MASK; uint32_t pc_start = tb->pc; @@ -1071,63 +1071,63 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) max_insns = TCG_MAX_INSNS; } - dc.config = env->config; - dc.base.singlestep_enabled = cs->singlestep_enabled; - dc.base.tb = tb; - dc.pc = pc_start; - dc.ring = tb->flags & XTENSA_TBFLAG_RING_MASK; - dc.cring = (tb->flags & XTENSA_TBFLAG_EXCM) ? 0 : dc.ring; - dc.lbeg = env->sregs[LBEG]; - dc.lend = env->sregs[LEND]; - dc.base.is_jmp = DISAS_NEXT; - dc.debug = tb->flags & XTENSA_TBFLAG_DEBUG; - dc.icount = tb->flags & XTENSA_TBFLAG_ICOUNT; - dc.cpenable = (tb->flags & XTENSA_TBFLAG_CPENABLE_MASK) >> + dc->config = env->config; + dc->base.singlestep_enabled = cs->singlestep_enabled; + dc->base.tb = tb; + dc->pc = pc_start; + dc->ring = tb->flags & XTENSA_TBFLAG_RING_MASK; + dc->cring = (tb->flags & XTENSA_TBFLAG_EXCM) ? 0 : dc->ring; + dc->lbeg = env->sregs[LBEG]; + dc->lend = env->sregs[LEND]; + dc->base.is_jmp = DISAS_NEXT; + dc->debug = tb->flags & XTENSA_TBFLAG_DEBUG; + dc->icount = tb->flags & XTENSA_TBFLAG_ICOUNT; + dc->cpenable = (tb->flags & XTENSA_TBFLAG_CPENABLE_MASK) >> XTENSA_TBFLAG_CPENABLE_SHIFT; - dc.window = ((tb->flags & XTENSA_TBFLAG_WINDOW_MASK) >> + dc->window = ((tb->flags & XTENSA_TBFLAG_WINDOW_MASK) >> XTENSA_TBFLAG_WINDOW_SHIFT); - if (dc.config->isa) { - dc.insnbuf = xtensa_insnbuf_alloc(dc.config->isa); - dc.slotbuf = xtensa_insnbuf_alloc(dc.config->isa); + if (dc->config->isa) { + dc->insnbuf = xtensa_insnbuf_alloc(dc->config->isa); + dc->slotbuf = xtensa_insnbuf_alloc(dc->config->isa); } - init_sar_tracker(&dc); - if (dc.icount) { - dc.next_icount = tcg_temp_local_new_i32(); + init_sar_tracker(dc); + if (dc->icount) { + dc->next_icount = tcg_temp_local_new_i32(); } gen_tb_start(tb); if ((tb_cflags(tb) & CF_USE_ICOUNT) && (tb->flags & XTENSA_TBFLAG_YIELD)) { - tcg_gen_insn_start(dc.pc); + tcg_gen_insn_start(dc->pc); ++insn_count; - gen_exception(&dc, EXCP_YIELD); - dc.base.is_jmp = DISAS_NORETURN; + gen_exception(dc, EXCP_YIELD); + dc->base.is_jmp = DISAS_NORETURN; goto done; } if (tb->flags & XTENSA_TBFLAG_EXCEPTION) { - tcg_gen_insn_start(dc.pc); + tcg_gen_insn_start(dc->pc); ++insn_count; - gen_exception(&dc, EXCP_DEBUG); - dc.base.is_jmp = DISAS_NORETURN; + gen_exception(dc, EXCP_DEBUG); + dc->base.is_jmp = DISAS_NORETURN; goto done; } do { - tcg_gen_insn_start(dc.pc); + tcg_gen_insn_start(dc->pc); ++insn_count; - if (unlikely(cpu_breakpoint_test(cs, dc.pc, BP_ANY))) { - tcg_gen_movi_i32(cpu_pc, dc.pc); - gen_exception(&dc, EXCP_DEBUG); - dc.base.is_jmp = DISAS_NORETURN; + if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { + tcg_gen_movi_i32(cpu_pc, dc->pc); + gen_exception(dc, EXCP_DEBUG); + dc->base.is_jmp = DISAS_NORETURN; /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that the logic setting tb->size below does the right thing. */ - dc.pc += 2; + dc->pc += 2; break; } @@ -1135,52 +1135,52 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) gen_io_start(); } - if (dc.icount) { + if (dc->icount) { TCGLabel *label = gen_new_label(); - tcg_gen_addi_i32(dc.next_icount, cpu_SR[ICOUNT], 1); - tcg_gen_brcondi_i32(TCG_COND_NE, dc.next_icount, 0, label); - tcg_gen_mov_i32(dc.next_icount, cpu_SR[ICOUNT]); - if (dc.debug) { - gen_debug_exception(&dc, DEBUGCAUSE_IC); + tcg_gen_addi_i32(dc->next_icount, cpu_SR[ICOUNT], 1); + tcg_gen_brcondi_i32(TCG_COND_NE, dc->next_icount, 0, label); + tcg_gen_mov_i32(dc->next_icount, cpu_SR[ICOUNT]); + if (dc->debug) { + gen_debug_exception(dc, DEBUGCAUSE_IC); } gen_set_label(label); } - if (dc.debug) { - gen_ibreak_check(env, &dc); + if (dc->debug) { + gen_ibreak_check(env, dc); } - disas_xtensa_insn(env, &dc); - if (dc.icount) { - tcg_gen_mov_i32(cpu_SR[ICOUNT], dc.next_icount); + disas_xtensa_insn(env, dc); + if (dc->icount) { + tcg_gen_mov_i32(cpu_SR[ICOUNT], dc->next_icount); } - if (cs->singlestep_enabled) { - tcg_gen_movi_i32(cpu_pc, dc.pc); - gen_exception(&dc, EXCP_DEBUG); + if (dc->base.singlestep_enabled) { + tcg_gen_movi_i32(cpu_pc, dc->pc); + gen_exception(dc, EXCP_DEBUG); break; } - } while (dc.base.is_jmp == DISAS_NEXT && - insn_count < max_insns && - dc.pc - page_start < TARGET_PAGE_SIZE && - dc.pc - page_start + xtensa_insn_len(env, &dc) <= TARGET_PAGE_SIZE - && !tcg_op_buf_full()); + } while (dc->base.is_jmp == DISAS_NEXT && + insn_count < max_insns && + dc->pc - page_start < TARGET_PAGE_SIZE && + dc->pc - page_start + xtensa_insn_len(env, dc) <= TARGET_PAGE_SIZE + && !tcg_op_buf_full()); done: - reset_sar_tracker(&dc); - if (dc.icount) { - tcg_temp_free(dc.next_icount); + reset_sar_tracker(dc); + if (dc->icount) { + tcg_temp_free(dc->next_icount); } - if (dc.config->isa) { - xtensa_insnbuf_free(dc.config->isa, dc.insnbuf); - xtensa_insnbuf_free(dc.config->isa, dc.slotbuf); + if (dc->config->isa) { + xtensa_insnbuf_free(dc->config->isa, dc->insnbuf); + xtensa_insnbuf_free(dc->config->isa, dc->slotbuf); } if (tb_cflags(tb) & CF_LAST_IO) { gen_io_end(); } - if (dc.base.is_jmp == DISAS_NEXT) { - gen_jumpi(&dc, dc.pc, 0); + if (dc->base.is_jmp == DISAS_NEXT) { + gen_jumpi(dc, dc->pc, 0); } gen_tb_end(tb, insn_count); @@ -1190,12 +1190,12 @@ done: qemu_log_lock(); qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc.pc - pc_start); + log_target_disas(cs, pc_start, dc->pc - pc_start); qemu_log("\n"); qemu_log_unlock(); } #endif - tb->size = dc.pc - pc_start; + tb->size = dc->pc - pc_start; tb->icount = insn_count; } From 9c509ff94e58823624b9617590134bc05d674279 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 12 May 2018 10:57:24 -0700 Subject: [PATCH 1831/2380] target/xtensa: Convert to TranslatorOps Signed-off-by: Richard Henderson Signed-off-by: Max Filippov --- target/xtensa/translate.c | 229 ++++++++++++++++++++------------------ 1 file changed, 122 insertions(+), 107 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 60375d923b..d22cdcdb16 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -1055,148 +1055,163 @@ static void gen_ibreak_check(CPUXtensaState *env, DisasContext *dc) } } -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +static void xtensa_tr_init_disas_context(DisasContextBase *dcbase, + CPUState *cpu) { - CPUXtensaState *env = cs->env_ptr; - DisasContext dc1, *dc = &dc1; - int insn_count = 0; - int max_insns = tb_cflags(tb) & CF_COUNT_MASK; - uint32_t pc_start = tb->pc; - uint32_t page_start = pc_start & TARGET_PAGE_MASK; - - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; - } + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUXtensaState *env = cpu->env_ptr; + uint32_t tb_flags = dc->base.tb->flags; dc->config = env->config; - dc->base.singlestep_enabled = cs->singlestep_enabled; - dc->base.tb = tb; - dc->pc = pc_start; - dc->ring = tb->flags & XTENSA_TBFLAG_RING_MASK; - dc->cring = (tb->flags & XTENSA_TBFLAG_EXCM) ? 0 : dc->ring; + dc->pc = dc->base.pc_first; + dc->ring = tb_flags & XTENSA_TBFLAG_RING_MASK; + dc->cring = (tb_flags & XTENSA_TBFLAG_EXCM) ? 0 : dc->ring; dc->lbeg = env->sregs[LBEG]; dc->lend = env->sregs[LEND]; - dc->base.is_jmp = DISAS_NEXT; - dc->debug = tb->flags & XTENSA_TBFLAG_DEBUG; - dc->icount = tb->flags & XTENSA_TBFLAG_ICOUNT; - dc->cpenable = (tb->flags & XTENSA_TBFLAG_CPENABLE_MASK) >> + dc->debug = tb_flags & XTENSA_TBFLAG_DEBUG; + dc->icount = tb_flags & XTENSA_TBFLAG_ICOUNT; + dc->cpenable = (tb_flags & XTENSA_TBFLAG_CPENABLE_MASK) >> XTENSA_TBFLAG_CPENABLE_SHIFT; - dc->window = ((tb->flags & XTENSA_TBFLAG_WINDOW_MASK) >> + dc->window = ((tb_flags & XTENSA_TBFLAG_WINDOW_MASK) >> XTENSA_TBFLAG_WINDOW_SHIFT); if (dc->config->isa) { dc->insnbuf = xtensa_insnbuf_alloc(dc->config->isa); dc->slotbuf = xtensa_insnbuf_alloc(dc->config->isa); } - init_sar_tracker(dc); +} + +static void xtensa_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + if (dc->icount) { dc->next_icount = tcg_temp_local_new_i32(); } +} - gen_tb_start(tb); +static void xtensa_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) +{ + tcg_gen_insn_start(dcbase->pc_next); +} - if ((tb_cflags(tb) & CF_USE_ICOUNT) && - (tb->flags & XTENSA_TBFLAG_YIELD)) { - tcg_gen_insn_start(dc->pc); - ++insn_count; +static bool xtensa_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next); + gen_exception(dc, EXCP_DEBUG); + dc->base.is_jmp = DISAS_NORETURN; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->base.pc_next += 2; + return true; +} + +static void xtensa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUXtensaState *env = cpu->env_ptr; + target_ulong page_start; + + /* These two conditions only apply to the first insn in the TB, + but this is the first TranslateOps hook that allows exiting. */ + if ((tb_cflags(dc->base.tb) & CF_USE_ICOUNT) + && (dc->base.tb->flags & XTENSA_TBFLAG_YIELD)) { gen_exception(dc, EXCP_YIELD); dc->base.is_jmp = DISAS_NORETURN; - goto done; + return; } - if (tb->flags & XTENSA_TBFLAG_EXCEPTION) { - tcg_gen_insn_start(dc->pc); - ++insn_count; + if (dc->base.tb->flags & XTENSA_TBFLAG_EXCEPTION) { gen_exception(dc, EXCP_DEBUG); dc->base.is_jmp = DISAS_NORETURN; - goto done; + return; } - do { - tcg_gen_insn_start(dc->pc); - ++insn_count; - - if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { - tcg_gen_movi_i32(cpu_pc, dc->pc); - gen_exception(dc, EXCP_DEBUG); - dc->base.is_jmp = DISAS_NORETURN; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - dc->pc += 2; - break; - } - - if (insn_count == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } - - if (dc->icount) { - TCGLabel *label = gen_new_label(); - - tcg_gen_addi_i32(dc->next_icount, cpu_SR[ICOUNT], 1); - tcg_gen_brcondi_i32(TCG_COND_NE, dc->next_icount, 0, label); - tcg_gen_mov_i32(dc->next_icount, cpu_SR[ICOUNT]); - if (dc->debug) { - gen_debug_exception(dc, DEBUGCAUSE_IC); - } - gen_set_label(label); - } - - if (dc->debug) { - gen_ibreak_check(env, dc); - } - - disas_xtensa_insn(env, dc); - if (dc->icount) { - tcg_gen_mov_i32(cpu_SR[ICOUNT], dc->next_icount); - } - if (dc->base.singlestep_enabled) { - tcg_gen_movi_i32(cpu_pc, dc->pc); - gen_exception(dc, EXCP_DEBUG); - break; - } - } while (dc->base.is_jmp == DISAS_NEXT && - insn_count < max_insns && - dc->pc - page_start < TARGET_PAGE_SIZE && - dc->pc - page_start + xtensa_insn_len(env, dc) <= TARGET_PAGE_SIZE - && !tcg_op_buf_full()); -done: - reset_sar_tracker(dc); if (dc->icount) { - tcg_temp_free(dc->next_icount); + TCGLabel *label = gen_new_label(); + + tcg_gen_addi_i32(dc->next_icount, cpu_SR[ICOUNT], 1); + tcg_gen_brcondi_i32(TCG_COND_NE, dc->next_icount, 0, label); + tcg_gen_mov_i32(dc->next_icount, cpu_SR[ICOUNT]); + if (dc->debug) { + gen_debug_exception(dc, DEBUGCAUSE_IC); + } + gen_set_label(label); } + + if (dc->debug) { + gen_ibreak_check(env, dc); + } + + disas_xtensa_insn(env, dc); + + if (dc->icount) { + tcg_gen_mov_i32(cpu_SR[ICOUNT], dc->next_icount); + } + + /* End the TB if the next insn will cross into the next page. */ + page_start = dc->base.pc_first & TARGET_PAGE_MASK; + if (dc->base.is_jmp == DISAS_NEXT && + (dc->pc - page_start >= TARGET_PAGE_SIZE || + dc->pc - page_start + xtensa_insn_len(env, dc) > TARGET_PAGE_SIZE)) { + dc->base.is_jmp = DISAS_TOO_MANY; + } +} + +static void xtensa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + reset_sar_tracker(dc); if (dc->config->isa) { xtensa_insnbuf_free(dc->config->isa, dc->insnbuf); xtensa_insnbuf_free(dc->config->isa, dc->slotbuf); } - - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); + if (dc->icount) { + tcg_temp_free(dc->next_icount); } - if (dc->base.is_jmp == DISAS_NEXT) { - gen_jumpi(dc, dc->pc, 0); + switch (dc->base.is_jmp) { + case DISAS_NORETURN: + break; + case DISAS_TOO_MANY: + if (dc->base.singlestep_enabled) { + tcg_gen_movi_i32(cpu_pc, dc->pc); + gen_exception(dc, EXCP_DEBUG); + } else { + gen_jumpi(dc, dc->pc, 0); + } + break; + default: + g_assert_not_reached(); } - gen_tb_end(tb, insn_count); +} -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - qemu_log("----------------\n"); - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc->pc - pc_start); - qemu_log("\n"); - qemu_log_unlock(); - } -#endif - tb->size = dc->pc - pc_start; - tb->icount = insn_count; +static void xtensa_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); +} + +static const TranslatorOps xtensa_translator_ops = { + .init_disas_context = xtensa_tr_init_disas_context, + .tb_start = xtensa_tr_tb_start, + .insn_start = xtensa_tr_insn_start, + .breakpoint_check = xtensa_tr_breakpoint_check, + .translate_insn = xtensa_tr_translate_insn, + .tb_stop = xtensa_tr_tb_stop, + .disas_log = xtensa_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb) +{ + DisasContext dc = {}; + translator_loop(&xtensa_translator_ops, &dc.base, cpu, tb); } void xtensa_cpu_dump_state(CPUState *cs, FILE *f, From 0f02251a30ea8c4ce64d9a240795e10bb3c5852c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 14:58:23 +0100 Subject: [PATCH 1832/2380] xtensa: Avoid calling get_page_addr_code() from helper function The xtensa frontend calls get_page_addr_code() from its itlb_hit_test helper function. This function is really part of the TCG core's internals, and calling it from a target helper makes it awkward to make changes to that core code. It also means that we don't pass the correct retaddr to tlb_fill(), so we won't correctly handle the case where an exception is generated. The helper is used for the instructions IHI, IHU and IPFL. Change it to call cpu_ldb_code_ra() instead. Signed-off-by: Peter Maydell --- target/xtensa/op_helper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index bbbbb33f3c..d4c942d879 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -458,7 +458,11 @@ void HELPER(check_interrupts)(CPUXtensaState *env) void HELPER(itlb_hit_test)(CPUXtensaState *env, uint32_t vaddr) { - get_page_addr_code(env, vaddr); + /* + * Attempt the memory load; we don't care about the result but + * only the side-effects (ie any MMU or other exception) + */ + cpu_ldub_code_ra(env, vaddr, GETPC()); } /*! From 76ed4b18debfe597329d1f6a9eb2ec9ffa751ecd Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 22 Jun 2018 12:29:28 +0200 Subject: [PATCH 1833/2380] s390/ipl: fix ipl with -no-reboot kexec/kdump as well as the bootloader use a subcode of diagnose 308 that is supposed to reset the I/O subsystem but not comprise a full "reboot". With the latest refactoring this is now broken when -no-reboot is used or when libvirt acts on a reboot QMP event, for example a virt-install from iso images. We need to mark these "subsystem resets" as special. Fixes: a30fb811cbe9 (s390x: refactor reset/reipl handling) Signed-off-by: Christian Borntraeger Reviewed-by: David Hildenbrand Message-Id: <20180622102928.173420-1-borntraeger@de.ibm.com> Acked-by: Paolo Bonzini Signed-off-by: Cornelia Huck --- hw/s390x/ipl.c | 8 +++++++- include/sysemu/sysemu.h | 4 ++++ vl.c | 4 ++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 0d67349004..f278036fa7 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -535,7 +535,13 @@ void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type) ipl->iplb_valid = s390_gen_initial_iplb(ipl); } } - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + if (reset_type == S390_RESET_MODIFIED_CLEAR || + reset_type == S390_RESET_LOAD_NORMAL) { + /* ignore -no-reboot, send no event */ + qemu_system_reset_request(SHUTDOWN_CAUSE_SUBSYSTEM_RESET); + } else { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } /* as this is triggered by a CPU, make sure to exit the loop */ if (tcg_enabled()) { cpu_loop_exit(cs); diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index b921c6f3b7..76ef6196a7 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -44,6 +44,10 @@ typedef enum ShutdownCause { turns that into a shutdown */ SHUTDOWN_CAUSE_GUEST_PANIC, /* Guest panicked, and command line turns that into a shutdown */ + SHUTDOWN_CAUSE_SUBSYSTEM_RESET,/* Partial guest reset that does not trigger + QMP events and ignores --no-reboot. This + is useful for sanitize hypercalls on s390 + that are used during kexec/kdump/boot */ SHUTDOWN_CAUSE__MAX, } ShutdownCause; diff --git a/vl.c b/vl.c index ef6cfcec40..9442beee21 100644 --- a/vl.c +++ b/vl.c @@ -1645,7 +1645,7 @@ void qemu_system_reset(ShutdownCause reason) } else { qemu_devices_reset(); } - if (reason) { + if (reason != SHUTDOWN_CAUSE_SUBSYSTEM_RESET) { qapi_event_send_reset(shutdown_caused_by_guest(reason), &error_abort); } @@ -1691,7 +1691,7 @@ void qemu_system_guest_panicked(GuestPanicInformation *info) void qemu_system_reset_request(ShutdownCause reason) { - if (no_reboot) { + if (no_reboot && reason != SHUTDOWN_CAUSE_SUBSYSTEM_RESET) { shutdown_requested = reason; } else { reset_requested = reason; From 0f0f8b611eeea663c8d3b6021918033e257411a1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 26 Jun 2018 11:35:40 +0200 Subject: [PATCH 1834/2380] loader: Check access size when calling rom_ptr() to avoid crashes The rom_ptr() function allows direct access to the ROM blobs that we load during startup. However, there are currently no checks for the size of the accesses, so it's currently possible to crash QEMU for example with: $ echo "Insane in the mainframe" > /tmp/test.txt $ s390x-softmmu/qemu-system-s390x -kernel /tmp/test.txt -append xyz Segmentation fault (core dumped) $ s390x-softmmu/qemu-system-s390x -kernel /tmp/test.txt -initrd /tmp/test.txt Segmentation fault (core dumped) $ echo -n HdrS > /tmp/hdr.txt $ sparc64-softmmu/qemu-system-sparc64 -kernel /tmp/hdr.txt -initrd /tmp/hdr.txt Segmentation fault (core dumped) We need a possibility to check the size of the ROM area that we want to access, thus let's add a size parameter to the rom_ptr() function to avoid these problems. Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth Message-Id: <1530005740-25254-1-git-send-email-thuth@redhat.com> Signed-off-by: Cornelia Huck --- hw/core/loader.c | 10 +++++----- hw/mips/mips_malta.c | 6 ++++-- hw/s390x/ipl.c | 18 ++++++++++++------ hw/sparc/sun4m.c | 4 ++-- hw/sparc64/sun4u.c | 4 ++-- include/hw/loader.h | 2 +- target/arm/cpu.c | 2 +- 7 files changed, 27 insertions(+), 19 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index 06bdbca537..bbb6e65bb5 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -191,7 +191,7 @@ void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size, rom_add_blob_fixed(name, source, (nulp - source) + 1, dest); } else { rom_add_blob_fixed(name, source, buf_size, dest); - ptr = rom_ptr(dest + buf_size - 1); + ptr = rom_ptr(dest + buf_size - 1, sizeof(*ptr)); *ptr = 0; } } @@ -1165,7 +1165,7 @@ void rom_reset_order_override(void) fw_cfg_reset_order_override(fw_cfg); } -static Rom *find_rom(hwaddr addr) +static Rom *find_rom(hwaddr addr, size_t size) { Rom *rom; @@ -1179,7 +1179,7 @@ static Rom *find_rom(hwaddr addr) if (rom->addr > addr) { continue; } - if (rom->addr + rom->romsize < addr) { + if (rom->addr + rom->romsize < addr + size) { continue; } return rom; @@ -1249,11 +1249,11 @@ int rom_copy(uint8_t *dest, hwaddr addr, size_t size) return (d + l) - dest; } -void *rom_ptr(hwaddr addr) +void *rom_ptr(hwaddr addr, size_t size) { Rom *rom; - rom = find_rom(addr); + rom = find_rom(addr, size); if (!rom || !rom->data) return NULL; return rom->data + (addr - rom->addr); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index b9d92bf47e..1b4e32e58e 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1133,11 +1133,13 @@ void mips_malta_init(MachineState *machine) a neat trick which allows bi-endian firmware. */ #ifndef TARGET_WORDS_BIGENDIAN { - uint32_t *end, *addr = rom_ptr(FLASH_ADDRESS); + uint32_t *end, *addr; + const size_t swapsize = MIN(bios_size, 0x3e0000); + addr = rom_ptr(FLASH_ADDRESS, swapsize); if (!addr) { addr = memory_region_get_ram_ptr(bios); } - end = (void *)addr + MIN(bios_size, 0x3e0000); + end = (void *)addr + swapsize; while (addr < end) { bswap32s(addr); addr++; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index f278036fa7..21f64ad26a 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -33,7 +33,6 @@ #define KERN_PARM_AREA 0x010480UL #define INITRD_START 0x800000UL #define INITRD_PARM_START 0x010408UL -#define INITRD_PARM_SIZE 0x010410UL #define PARMFILE_START 0x001000UL #define ZIPL_IMAGE_START 0x009000UL #define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) @@ -165,12 +164,12 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) goto error; } /* if this is Linux use KERN_IMAGE_START */ - magic = rom_ptr(LINUX_MAGIC_ADDR); + magic = rom_ptr(LINUX_MAGIC_ADDR, 6); if (magic && !memcmp(magic, "S390EP", 6)) { pentry = KERN_IMAGE_START; } else { /* if not Linux load the address of the (short) IPL PSW */ - ipl_psw = rom_ptr(4); + ipl_psw = rom_ptr(4, 4); if (ipl_psw) { pentry = be32_to_cpu(*ipl_psw) & 0x7fffffffUL; } else { @@ -186,9 +185,12 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) * loader) and it won't work. For this case we force it to 0x10000, too. */ if (pentry == KERN_IMAGE_START || pentry == 0x800) { + char *parm_area = rom_ptr(KERN_PARM_AREA, strlen(ipl->cmdline) + 1); ipl->start_addr = KERN_IMAGE_START; /* Overwrite parameters in the kernel image, which are "rom" */ - strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline); + if (parm_area) { + strcpy(parm_area, ipl->cmdline); + } } else { ipl->start_addr = pentry; } @@ -196,6 +198,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) if (ipl->initrd) { ram_addr_t initrd_offset; int initrd_size; + uint64_t *romptr; initrd_offset = INITRD_START; while (kernel_size + 0x100000 > initrd_offset) { @@ -212,8 +215,11 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) * we have to overwrite values in the kernel image, * which are "rom" */ - stq_p(rom_ptr(INITRD_PARM_START), initrd_offset); - stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size); + romptr = rom_ptr(INITRD_PARM_START, 16); + if (romptr) { + stq_p(romptr, initrd_offset); + stq_p(romptr + 1, initrd_size); + } } } /* diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index b984d2da0e..21078cc121 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -272,8 +272,8 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, } if (initrd_size > 0) { for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { - ptr = rom_ptr(KERNEL_LOAD_ADDR + i); - if (ldl_p(ptr) == 0x48647253) { // HdrS + ptr = rom_ptr(KERNEL_LOAD_ADDR + i, 24); + if (ptr && ldl_p(ptr) == 0x48647253) { /* HdrS */ stl_p(ptr + 16, INITRD_LOAD_ADDR); stl_p(ptr + 20, initrd_size); break; diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 3975a7b65a..334dd7008e 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -186,8 +186,8 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, } if (*initrd_size > 0) { for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { - ptr = rom_ptr(*kernel_addr + i); - if (ldl_p(ptr + 8) == 0x48647253) { /* HdrS */ + ptr = rom_ptr(*kernel_addr + i, 32); + if (ptr && ldl_p(ptr + 8) == 0x48647253) { /* HdrS */ stl_p(ptr + 24, *initrd_addr + *kernel_addr); stl_p(ptr + 28, *initrd_size); break; diff --git a/include/hw/loader.h b/include/hw/loader.h index 5ed3fd8ae6..e98b84b8f9 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -226,7 +226,7 @@ void rom_set_fw(FWCfgState *f); void rom_set_order_override(int order); void rom_reset_order_override(void); int rom_copy(uint8_t *dest, hwaddr addr, size_t size); -void *rom_ptr(hwaddr addr); +void *rom_ptr(hwaddr addr, size_t size); void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 82ff450f9a..64a8005a4b 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -239,7 +239,7 @@ static void arm_cpu_reset(CPUState *s) /* Load the initial SP and PC from offset 0 and 4 in the vector table */ vecbase = env->v7m.vecbase[env->v7m.secure]; - rom = rom_ptr(vecbase); + rom = rom_ptr(vecbase, 8); if (rom) { /* Address zero is covered by ROM which hasn't yet been * copied into physical memory. From 87273151110b7736b34ae3e37ad778c28c36d643 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 26 Jun 2018 14:38:30 +0200 Subject: [PATCH 1835/2380] s390x/cpumodel: default enable bpb and ppa15 for z196 and later Most systems and host kernels provide the necessary building blocks for bpb and ppa15. We can reverse the logic and default enable those features, while still allowing to disable it via cpu model. So let us add bpb and ppa15 to z196 and later default CPU model for the qemu 3.0 machine. (like -cpu z13). Older machine types (e.g. s390-ccw-virtio-2.12) will retain the old value and not provide those bits in the default model. Signed-off-by: Christian Borntraeger Message-Id: <20180626123830.18282-1-borntraeger@de.ibm.com> Reviewed-by: David Hildenbrand Signed-off-by: Cornelia Huck --- hw/s390x/s390-virtio-ccw.c | 2 ++ target/s390x/gen-features.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 7ae5fb38dd..f8f58c8acb 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -824,6 +824,8 @@ DEFINE_CCW_MACHINE(3_0, "3.0", true); static void ccw_machine_2_12_instance_options(MachineState *machine) { ccw_machine_3_0_instance_options(machine); + s390_cpudef_featoff_greater(11, 1, S390_FEAT_PPA15); + s390_cpudef_featoff_greater(11, 1, S390_FEAT_BPB); } static void ccw_machine_2_12_class_options(MachineClass *mc) diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 0cdbc15378..6626b6f565 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -512,6 +512,8 @@ static uint16_t default_GEN11_GA1[] = { S390_FEAT_IPTE_RANGE, S390_FEAT_ACCESS_EXCEPTION_FS_INDICATION, S390_FEAT_GROUP_MSA_EXT_4, + S390_FEAT_PPA15, + S390_FEAT_BPB, }; #define default_GEN11_GA2 EmptyFeat From 14055ce53c2d901d826ffad7fb7d6bb8ab46bdfd Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:02 +0200 Subject: [PATCH 1836/2380] s390x/tcg: avoid overflows in time2tod/tod2time Big values for the TOD/ns clock can result in some overflows that can be avoided. Not all overflows can be handled however, as the conversion either multiplies by 4.096 or divided by 4.096. Apply the trick used in the Linux kernel in arch/s390/include/asm/timex.h for tod_to_ns() and use the same trick also for the conversion in the other direction. Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-2-david@redhat.com> Signed-off-by: Cornelia Huck --- target/s390x/internal.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/s390x/internal.h b/target/s390x/internal.h index e392a02d12..6cf63340bf 100644 --- a/target/s390x/internal.h +++ b/target/s390x/internal.h @@ -243,13 +243,14 @@ enum cc_op { /* Converts ns to s390's clock format */ static inline uint64_t time2tod(uint64_t ns) { - return (ns << 9) / 125; + return (ns << 9) / 125 + (((ns & 0xff10000000000000ull) / 125) << 9); + } /* Converts s390's clock format to ns */ static inline uint64_t tod2time(uint64_t t) { - return (t * 125) >> 9; + return ((t >> 9) * 125) + (((t & 0x1ff) * 125) >> 9); } static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb, From 4ab6a1feac0a142045d3b7bdbb8182a99c0b8980 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:03 +0200 Subject: [PATCH 1837/2380] s390x/kvm: pass values instead of pointers to kvm_s390_set_clock_*() We are going to factor out the TOD into a separate device and use const pointers for device class functions where possible. We are passing right now ordinary pointers that should never be touched when setting the TOD. Let's just pass the values directly. Note that s390_set_clock() will be removed in a follow-on patch and therefore its calling convention is not changed. Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-3-david@redhat.com> Signed-off-by: Cornelia Huck --- target/s390x/cpu.c | 4 ++-- target/s390x/kvm-stub.c | 4 ++-- target/s390x/kvm.c | 12 ++++++------ target/s390x/kvm_s390x.h | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index c268065887..68512e3e54 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -413,9 +413,9 @@ int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) int r = 0; if (kvm_enabled()) { - r = kvm_s390_set_clock_ext(tod_high, tod_low); + r = kvm_s390_set_clock_ext(*tod_high, *tod_low); if (r == -ENXIO) { - return kvm_s390_set_clock(tod_high, tod_low); + return kvm_s390_set_clock(*tod_high, *tod_low); } } /* Fixme TCG */ diff --git a/target/s390x/kvm-stub.c b/target/s390x/kvm-stub.c index 29b10542cc..bf7795e47a 100644 --- a/target/s390x/kvm-stub.c +++ b/target/s390x/kvm-stub.c @@ -60,12 +60,12 @@ int kvm_s390_get_clock_ext(uint8_t *tod_high, uint64_t *tod_low) return -ENOSYS; } -int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) +int kvm_s390_set_clock(uint8_t tod_high, uint64_t tod_low) { return -ENOSYS; } -int kvm_s390_set_clock_ext(uint8_t *tod_high, uint64_t *tod_low) +int kvm_s390_set_clock_ext(uint8_t tod_high, uint64_t tod_low) { return -ENOSYS; } diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index ac370da281..8bcd832123 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -666,13 +666,13 @@ int kvm_s390_get_clock_ext(uint8_t *tod_high, uint64_t *tod_low) return r; } -int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) +int kvm_s390_set_clock(uint8_t tod_high, uint64_t tod_low) { int r; struct kvm_device_attr attr = { .group = KVM_S390_VM_TOD, .attr = KVM_S390_VM_TOD_LOW, - .addr = (uint64_t)tod_low, + .addr = (uint64_t)&tod_low, }; r = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); @@ -681,15 +681,15 @@ int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) } attr.attr = KVM_S390_VM_TOD_HIGH; - attr.addr = (uint64_t)tod_high; + attr.addr = (uint64_t)&tod_high; return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); } -int kvm_s390_set_clock_ext(uint8_t *tod_high, uint64_t *tod_low) +int kvm_s390_set_clock_ext(uint8_t tod_high, uint64_t tod_low) { struct kvm_s390_vm_tod_clock gtod = { - .epoch_idx = *tod_high, - .tod = *tod_low, + .epoch_idx = tod_high, + .tod = tod_low, }; struct kvm_device_attr attr = { .group = KVM_S390_VM_TOD, diff --git a/target/s390x/kvm_s390x.h b/target/s390x/kvm_s390x.h index c383bf4ee9..36eb34bf99 100644 --- a/target/s390x/kvm_s390x.h +++ b/target/s390x/kvm_s390x.h @@ -25,8 +25,8 @@ int kvm_s390_get_ri(void); int kvm_s390_get_gs(void); int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_clock); int kvm_s390_get_clock_ext(uint8_t *tod_high, uint64_t *tod_clock); -int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_clock); -int kvm_s390_set_clock_ext(uint8_t *tod_high, uint64_t *tod_clock); +int kvm_s390_set_clock(uint8_t tod_high, uint64_t tod_clock); +int kvm_s390_set_clock_ext(uint8_t tod_high, uint64_t tod_clock); void kvm_s390_enable_css_support(S390CPU *cpu); int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, int vq, bool assign); From 8046f374a64b81fdf4f71f7a433bf4035d501521 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:04 +0200 Subject: [PATCH 1838/2380] s390x/tod: factor out TOD into separate device Let's treat this like a separate device. TCG will have to store the actual state/time later on. Include cpu-qom.h in kvm_s390x.h (due to S390CPU) to compile tod-kvm.c. Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-4-david@redhat.com> Signed-off-by: Cornelia Huck --- hw/s390x/Makefile.objs | 3 + hw/s390x/s390-virtio-ccw.c | 57 +----------------- hw/s390x/tod-kvm.c | 64 ++++++++++++++++++++ hw/s390x/tod-qemu.c | 49 +++++++++++++++ hw/s390x/tod.c | 119 +++++++++++++++++++++++++++++++++++++ include/hw/s390x/tod.h | 46 ++++++++++++++ target/s390x/cpu.c | 32 ---------- target/s390x/cpu.h | 2 - target/s390x/kvm_s390x.h | 2 + 9 files changed, 286 insertions(+), 88 deletions(-) create mode 100644 hw/s390x/tod-kvm.c create mode 100644 hw/s390x/tod-qemu.c create mode 100644 hw/s390x/tod.c create mode 100644 include/hw/s390x/tod.h diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index dc704b57d6..93282f7c59 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -14,6 +14,9 @@ obj-$(CONFIG_PCI) += s390-pci-bus.o s390-pci-inst.o obj-$(call lnot,$(CONFIG_PCI)) += s390-pci-stub.o obj-y += s390-skeys.o obj-y += s390-stattrib.o +obj-y += tod.o +obj-$(CONFIG_KVM) += tod-kvm.o +obj-$(CONFIG_TCG) += tod-qemu.o obj-$(CONFIG_KVM) += s390-skeys-kvm.o obj-$(CONFIG_KVM) += s390-stattrib-kvm.o obj-y += s390-ccw.o diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index f8f58c8acb..7983185d04 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -35,6 +35,7 @@ #include "migration/register.h" #include "cpu_models.h" #include "hw/nmi.h" +#include "hw/s390x/tod.h" S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) { @@ -187,58 +188,6 @@ static void s390_memory_init(ram_addr_t mem_size) s390_stattrib_init(); } -#define S390_TOD_CLOCK_VALUE_MISSING 0x00 -#define S390_TOD_CLOCK_VALUE_PRESENT 0x01 - -static void gtod_save(QEMUFile *f, void *opaque) -{ - uint64_t tod_low; - uint8_t tod_high; - int r; - - r = s390_get_clock(&tod_high, &tod_low); - if (r) { - warn_report("Unable to get guest clock for migration: %s", - strerror(-r)); - error_printf("Guest clock will not be migrated " - "which could cause the guest to hang."); - qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); - return; - } - - qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); - qemu_put_byte(f, tod_high); - qemu_put_be64(f, tod_low); -} - -static int gtod_load(QEMUFile *f, void *opaque, int version_id) -{ - uint64_t tod_low; - uint8_t tod_high; - int r; - - if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { - warn_report("Guest clock was not migrated. This could " - "cause the guest to hang."); - return 0; - } - - tod_high = qemu_get_byte(f); - tod_low = qemu_get_be64(f); - - r = s390_set_clock(&tod_high, &tod_low); - if (r) { - error_report("Unable to set KVM guest TOD clock: %s", strerror(-r)); - } - - return r; -} - -static SaveVMHandlers savevm_gtod = { - .save_state = gtod_save, - .load_state = gtod_load, -}; - static void s390_init_ipl_dev(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *firmware, @@ -363,8 +312,8 @@ static void ccw_init(MachineState *machine) s390_create_sclpconsole("sclplmconsole", serial_hd(1)); } - /* Register savevm handler for guest TOD clock */ - register_savevm_live(NULL, "todclock", 0, 1, &savevm_gtod, NULL); + /* init the TOD clock */ + s390_init_tod(); } static void s390_cpu_plug(HotplugHandler *hotplug_dev, diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c new file mode 100644 index 0000000000..df564ab89c --- /dev/null +++ b/hw/s390x/tod-kvm.c @@ -0,0 +1,64 @@ +/* + * TOD (Time Of Day) clock - KVM implementation + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/s390x/tod.h" +#include "kvm_s390x.h" + +static void kvm_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) +{ + int r; + + r = kvm_s390_get_clock_ext(&tod->high, &tod->low); + if (r == -ENXIO) { + r = kvm_s390_get_clock(&tod->high, &tod->low); + } + if (r) { + error_setg(errp, "Unable to get KVM guest TOD clock: %s", + strerror(-r)); + } +} + +static void kvm_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp) +{ + int r; + + r = kvm_s390_set_clock_ext(tod->high, tod->low); + if (r == -ENXIO) { + r = kvm_s390_set_clock(tod->high, tod->low); + } + if (r) { + error_setg(errp, "Unable to set KVM guest TOD clock: %s", + strerror(-r)); + } +} + +static void kvm_s390_tod_class_init(ObjectClass *oc, void *data) +{ + S390TODClass *tdc = S390_TOD_CLASS(oc); + + tdc->get = kvm_s390_tod_get; + tdc->set = kvm_s390_tod_set; +} + +static TypeInfo kvm_s390_tod_info = { + .name = TYPE_KVM_S390_TOD, + .parent = TYPE_S390_TOD, + .instance_size = sizeof(S390TODState), + .class_init = kvm_s390_tod_class_init, + .class_size = sizeof(S390TODClass), +}; + +static void register_types(void) +{ + type_register_static(&kvm_s390_tod_info); +} +type_init(register_types); diff --git a/hw/s390x/tod-qemu.c b/hw/s390x/tod-qemu.c new file mode 100644 index 0000000000..03ea1ce4e8 --- /dev/null +++ b/hw/s390x/tod-qemu.c @@ -0,0 +1,49 @@ +/* + * TOD (Time Of Day) clock - QEMU implementation + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/s390x/tod.h" + +static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod, + Error **errp) +{ + /* FIXME */ + tod->high = 0; + tod->low = 0; +} + +static void qemu_s390_tod_set(S390TODState *td, const S390TOD *tod, + Error **errp) +{ + /* FIXME */ +} + +static void qemu_s390_tod_class_init(ObjectClass *oc, void *data) +{ + S390TODClass *tdc = S390_TOD_CLASS(oc); + + tdc->get = qemu_s390_tod_get; + tdc->set = qemu_s390_tod_set; +} + +static TypeInfo qemu_s390_tod_info = { + .name = TYPE_QEMU_S390_TOD, + .parent = TYPE_S390_TOD, + .instance_size = sizeof(S390TODState), + .class_init = qemu_s390_tod_class_init, + .class_size = sizeof(S390TODClass), +}; + +static void register_types(void) +{ + type_register_static(&qemu_s390_tod_info); +} +type_init(register_types); diff --git a/hw/s390x/tod.c b/hw/s390x/tod.c new file mode 100644 index 0000000000..0501affa7f --- /dev/null +++ b/hw/s390x/tod.c @@ -0,0 +1,119 @@ +/* + * TOD (Time Of Day) clock + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/s390x/tod.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "sysemu/kvm.h" +#include "migration/register.h" + +void s390_init_tod(void) +{ + Object *obj; + + if (kvm_enabled()) { + obj = object_new(TYPE_KVM_S390_TOD); + } else { + obj = object_new(TYPE_QEMU_S390_TOD); + } + object_property_add_child(qdev_get_machine(), TYPE_S390_TOD, obj, NULL); + object_unref(obj); + + qdev_init_nofail(DEVICE(obj)); +} + +#define S390_TOD_CLOCK_VALUE_MISSING 0x00 +#define S390_TOD_CLOCK_VALUE_PRESENT 0x01 + +static void s390_tod_save(QEMUFile *f, void *opaque) +{ + S390TODState *td = opaque; + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + Error *err = NULL; + S390TOD tod; + + tdc->get(td, &tod, &err); + if (err) { + warn_report_err(err); + error_printf("Guest clock will not be migrated " + "which could cause the guest to hang."); + qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); + return; + } + + qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); + qemu_put_byte(f, tod.high); + qemu_put_be64(f, tod.low); +} + +static int s390_tod_load(QEMUFile *f, void *opaque, int version_id) +{ + S390TODState *td = opaque; + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + Error *err = NULL; + S390TOD tod; + + if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { + warn_report("Guest clock was not migrated. This could " + "cause the guest to hang."); + return 0; + } + + tod.high = qemu_get_byte(f); + tod.low = qemu_get_be64(f); + + tdc->set(td, &tod, &err); + if (err) { + error_report_err(err); + return -1; + } + return 0; +} + +static SaveVMHandlers savevm_tod = { + .save_state = s390_tod_save, + .load_state = s390_tod_load, +}; + +static void s390_tod_realize(DeviceState *dev, Error **errp) +{ + S390TODState *td = S390_TOD(dev); + + /* Legacy migration interface */ + register_savevm_live(NULL, "todclock", 0, 1, &savevm_tod, td); +} + +static void s390_tod_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->desc = "TOD (Time Of Day) Clock"; + dc->realize = s390_tod_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + /* We only have one TOD clock in the system attached to the machine */ + dc->user_creatable = false; +} + +static TypeInfo s390_tod_info = { + .name = TYPE_S390_TOD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(S390TODState), + .class_init = s390_tod_class_init, + .class_size = sizeof(S390TODClass), + .abstract = true, +}; + +static void register_types(void) +{ + type_register_static(&s390_tod_info); +} +type_init(register_types); diff --git a/include/hw/s390x/tod.h b/include/hw/s390x/tod.h new file mode 100644 index 0000000000..7096b574ae --- /dev/null +++ b/include/hw/s390x/tod.h @@ -0,0 +1,46 @@ +/* + * TOD (Time Of Day) clock + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_S390_TOD_H +#define HW_S390_TOD_H + +#include "hw/qdev.h" + +typedef struct S390TOD { + uint8_t high; + uint64_t low; +} S390TOD; + +#define TYPE_S390_TOD "s390-tod" +#define S390_TOD(obj) OBJECT_CHECK(S390TODState, (obj), TYPE_S390_TOD) +#define S390_TOD_CLASS(oc) OBJECT_CLASS_CHECK(S390TODClass, (oc), \ + TYPE_S390_TOD) +#define S390_TOD_GET_CLASS(obj) OBJECT_GET_CLASS(S390TODClass, (obj), \ + TYPE_S390_TOD) +#define TYPE_KVM_S390_TOD TYPE_S390_TOD "-kvm" +#define TYPE_QEMU_S390_TOD TYPE_S390_TOD "-qemu" + +typedef struct S390TODState { + /* private */ + DeviceState parent_obj; +} S390TODState; + +typedef struct S390TODClass { + /* private */ + DeviceClass parent_class; + + /* public */ + void (*get)(const S390TODState *td, S390TOD *tod, Error **errp); + void (*set)(S390TODState *td, const S390TOD *tod, Error **errp); +} S390TODClass; + +void s390_init_tod(void); + +#endif diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 68512e3e54..03ea6eafa7 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -390,38 +390,6 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) return s390_count_running_cpus(); } -int s390_get_clock(uint8_t *tod_high, uint64_t *tod_low) -{ - int r = 0; - - if (kvm_enabled()) { - r = kvm_s390_get_clock_ext(tod_high, tod_low); - if (r == -ENXIO) { - return kvm_s390_get_clock(tod_high, tod_low); - } - } else { - /* Fixme TCG */ - *tod_high = 0; - *tod_low = 0; - } - - return r; -} - -int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) -{ - int r = 0; - - if (kvm_enabled()) { - r = kvm_s390_set_clock_ext(*tod_high, *tod_low); - if (r == -ENXIO) { - return kvm_s390_set_clock(*tod_high, *tod_low); - } - } - /* Fixme TCG */ - return r; -} - int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) { if (kvm_enabled()) { diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 6629a533f3..ac51c17fb4 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -714,8 +714,6 @@ static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg) /* cpu.c */ -int s390_get_clock(uint8_t *tod_high, uint64_t *tod_low); -int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low); void s390_crypto_reset(void); bool s390_get_squash_mcss(void); int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit); diff --git a/target/s390x/kvm_s390x.h b/target/s390x/kvm_s390x.h index 36eb34bf99..6e52287da3 100644 --- a/target/s390x/kvm_s390x.h +++ b/target/s390x/kvm_s390x.h @@ -10,6 +10,8 @@ #ifndef KVM_S390X_H #define KVM_S390X_H +#include "cpu-qom.h" + struct kvm_s390_irq; void kvm_s390_floating_interrupt_legacy(struct kvm_s390_irq *irq); From f777b20544fe5db3de179a83374cbf9f1e454427 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:05 +0200 Subject: [PATCH 1839/2380] s390x/tcg: drop tod_basetime Never set to anything but 0. Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-5-david@redhat.com> Signed-off-by: Cornelia Huck --- target/s390x/cpu.c | 1 - target/s390x/cpu.h | 1 - target/s390x/misc_helper.c | 4 ++-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 03ea6eafa7..a41b3f34ab 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -289,7 +289,6 @@ static void s390_cpu_initfn(Object *obj) qemu_get_timedate(&tm, 0); env->tod_offset = TOD_UNIX_EPOCH + (time2tod(mktimegm(&tm)) * 1000000000ULL); - env->tod_basetime = 0; env->tod_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu); env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index ac51c17fb4..4abfe88a3d 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -131,7 +131,6 @@ struct CPUS390XState { #endif uint64_t tod_offset; - uint64_t tod_basetime; QEMUTimer *tod_timer; QEMUTimer *cpu_timer; diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index de1ced2082..dd5273949b 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -141,7 +141,7 @@ uint64_t HELPER(stck)(CPUS390XState *env) uint64_t time; time = env->tod_offset + - time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - env->tod_basetime); + time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); return time; } @@ -161,7 +161,7 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time) /* nanoseconds */ time = tod2time(time); - timer_mod(env->tod_timer, env->tod_basetime + time); + timer_mod(env->tod_timer, time); } /* Set Tod Programmable Field */ From 7de3b1cdc67dcb572c1761c2051252e91a438b22 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:06 +0200 Subject: [PATCH 1840/2380] s390x/tcg: properly implement the TOD Right now, each CPU has its own TOD. Especially, the TOD will differ based on creation time of a CPU - e.g. when hotplugging a CPU the times will differ quite a lot, resulting in stall warnings in the guest. Let's use a single TOD by implementing our new TOD device. Prepare it for TOD-clock epoch extension. Most importantly, whenever we set the TOD, we have to update the CKC timer. Introduce "tcg_s390x.h" just like "kvm_s390x.h" for tcg specific function declarations that should not go into cpu.h. Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-6-david@redhat.com> Signed-off-by: Cornelia Huck --- hw/s390x/tod-qemu.c | 46 ++++++++++++++++++++++++++++++++++---- hw/s390x/tod.c | 11 +++++++++ include/hw/s390x/tod.h | 19 ++++++++++++++++ target/s390x/cpu.c | 7 ------ target/s390x/cpu.h | 1 - target/s390x/internal.h | 16 ------------- target/s390x/misc_helper.c | 25 ++++++++++++++++----- target/s390x/tcg_s390x.h | 18 +++++++++++++++ 8 files changed, 109 insertions(+), 34 deletions(-) create mode 100644 target/s390x/tcg_s390x.h diff --git a/hw/s390x/tod-qemu.c b/hw/s390x/tod-qemu.c index 03ea1ce4e8..59c015c69d 100644 --- a/hw/s390x/tod-qemu.c +++ b/hw/s390x/tod-qemu.c @@ -11,19 +11,43 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/s390x/tod.h" +#include "qemu/timer.h" +#include "qemu/cutils.h" +#include "cpu.h" +#include "tcg_s390x.h" static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) { - /* FIXME */ - tod->high = 0; - tod->low = 0; + *tod = td->base; + + tod->low += time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + if (tod->low < td->base.low) { + tod->high++; + } } static void qemu_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp) { - /* FIXME */ + CPUState *cpu; + + td->base = *tod; + + td->base.low -= time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + if (td->base.low > tod->low) { + td->base.high--; + } + + /* + * The TOD has been changed and we have to recalculate the CKC values + * for all CPUs. We do this asynchronously, as "SET CLOCK should be + * issued only while all other activity on all CPUs .. has been + * suspended". + */ + CPU_FOREACH(cpu) { + async_run_on_cpu(cpu, tcg_s390_tod_updated, RUN_ON_CPU_NULL); + } } static void qemu_s390_tod_class_init(ObjectClass *oc, void *data) @@ -34,10 +58,24 @@ static void qemu_s390_tod_class_init(ObjectClass *oc, void *data) tdc->set = qemu_s390_tod_set; } +static void qemu_s390_tod_init(Object *obj) +{ + S390TODState *td = S390_TOD(obj); + struct tm tm; + + qemu_get_timedate(&tm, 0); + td->base.high = 0; + td->base.low = TOD_UNIX_EPOCH + (time2tod(mktimegm(&tm)) * 1000000000ULL); + if (td->base.low < TOD_UNIX_EPOCH) { + td->base.high += 1; + } +} + static TypeInfo qemu_s390_tod_info = { .name = TYPE_QEMU_S390_TOD, .parent = TYPE_S390_TOD, .instance_size = sizeof(S390TODState), + .instance_init = qemu_s390_tod_init, .class_init = qemu_s390_tod_class_init, .class_size = sizeof(S390TODClass), }; diff --git a/hw/s390x/tod.c b/hw/s390x/tod.c index 0501affa7f..1c63f411e6 100644 --- a/hw/s390x/tod.c +++ b/hw/s390x/tod.c @@ -30,6 +30,17 @@ void s390_init_tod(void) qdev_init_nofail(DEVICE(obj)); } +S390TODState *s390_get_todstate(void) +{ + static S390TODState *ts; + + if (!ts) { + ts = S390_TOD(object_resolve_path_type("", TYPE_S390_TOD, NULL)); + } + + return ts; +} + #define S390_TOD_CLOCK_VALUE_MISSING 0x00 #define S390_TOD_CLOCK_VALUE_PRESENT 0x01 diff --git a/include/hw/s390x/tod.h b/include/hw/s390x/tod.h index 7096b574ae..413c0d7c02 100644 --- a/include/hw/s390x/tod.h +++ b/include/hw/s390x/tod.h @@ -30,6 +30,9 @@ typedef struct S390TOD { typedef struct S390TODState { /* private */ DeviceState parent_obj; + + /* unused by KVM implementation */ + S390TOD base; } S390TODState; typedef struct S390TODClass { @@ -41,6 +44,22 @@ typedef struct S390TODClass { void (*set)(S390TODState *td, const S390TOD *tod, Error **errp); } S390TODClass; +/* The value of the TOD clock for 1.1.1970. */ +#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL + +/* Converts ns to s390's clock format */ +static inline uint64_t time2tod(uint64_t ns) +{ + return (ns << 9) / 125 + (((ns & 0xff10000000000000ull) / 125) << 9); +} + +/* Converts s390's clock format to ns */ +static inline uint64_t tod2time(uint64_t t) +{ + return ((t >> 9) * 125) + (((t & 0x1ff) * 125) >> 9); +} + void s390_init_tod(void); +S390TODState *s390_get_todstate(void); #endif diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index a41b3f34ab..40d6980229 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -30,7 +30,6 @@ #include "kvm_s390x.h" #include "sysemu/kvm.h" #include "qemu-common.h" -#include "qemu/cutils.h" #include "qemu/timer.h" #include "qemu/error-report.h" #include "trace.h" @@ -275,9 +274,6 @@ static void s390_cpu_initfn(Object *obj) CPUState *cs = CPU(obj); S390CPU *cpu = S390_CPU(obj); CPUS390XState *env = &cpu->env; -#if !defined(CONFIG_USER_ONLY) - struct tm tm; -#endif cs->env_ptr = env; cs->halted = 1; @@ -286,9 +282,6 @@ static void s390_cpu_initfn(Object *obj) s390_cpu_get_crash_info_qom, NULL, NULL, NULL, NULL); s390_cpu_model_register_props(obj); #if !defined(CONFIG_USER_ONLY) - qemu_get_timedate(&tm, 0); - env->tod_offset = TOD_UNIX_EPOCH + - (time2tod(mktimegm(&tm)) * 1000000000ULL); env->tod_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu); env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 4abfe88a3d..2c3dd2d189 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -130,7 +130,6 @@ struct CPUS390XState { uint64_t cpuid; #endif - uint64_t tod_offset; QEMUTimer *tod_timer; QEMUTimer *cpu_timer; diff --git a/target/s390x/internal.h b/target/s390x/internal.h index 6cf63340bf..f2a771e2b4 100644 --- a/target/s390x/internal.h +++ b/target/s390x/internal.h @@ -237,22 +237,6 @@ enum cc_op { CC_OP_MAX }; -/* The value of the TOD clock for 1.1.1970. */ -#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL - -/* Converts ns to s390's clock format */ -static inline uint64_t time2tod(uint64_t ns) -{ - return (ns << 9) / 125 + (((ns & 0xff10000000000000ull) / 125) << 9); - -} - -/* Converts s390's clock format to ns */ -static inline uint64_t tod2time(uint64_t t) -{ - return ((t >> 9) * 125) + (((t & 0x1ff) * 125) >> 9); -} - static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb, uint8_t *ar) { diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index dd5273949b..229f14d3da 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -28,6 +28,8 @@ #include "qemu/timer.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" +#include "qapi/error.h" +#include "tcg_s390x.h" #if !defined(CONFIG_USER_ONLY) #include "sysemu/cpus.h" @@ -39,6 +41,7 @@ #include "hw/s390x/ioinst.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/boards.h" +#include "hw/s390x/tod.h" #endif /* #define DEBUG_HELPER */ @@ -138,17 +141,19 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1) /* Store Clock */ uint64_t HELPER(stck)(CPUS390XState *env) { - uint64_t time; + S390TODState *td = s390_get_todstate(); + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + S390TOD tod; - time = env->tod_offset + - time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - - return time; + tdc->get(td, &tod, &error_abort); + return tod.low; } /* Set Clock Comparator */ void HELPER(sckc)(CPUS390XState *env, uint64_t time) { + S390TODState *td = s390_get_todstate(); + if (time == -1ULL) { return; } @@ -156,7 +161,7 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time) env->ckc = time; /* difference between origins */ - time -= env->tod_offset; + time -= td->base.low; /* nanoseconds */ time = tod2time(time); @@ -164,6 +169,14 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time) timer_mod(env->tod_timer, time); } +void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) +{ + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + + helper_sckc(env, env->ckc); +} + /* Set Tod Programmable Field */ void HELPER(sckpf)(CPUS390XState *env, uint64_t r0) { diff --git a/target/s390x/tcg_s390x.h b/target/s390x/tcg_s390x.h new file mode 100644 index 0000000000..4e308aa0ce --- /dev/null +++ b/target/s390x/tcg_s390x.h @@ -0,0 +1,18 @@ +/* + * QEMU TCG support -- s390x specific functions. + * + * Copyright 2018 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TCG_S390X_H +#define TCG_S390X_H + +void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque); + +#endif /* TCG_S390X_H */ From 345f1ab96e8279a537f32ae7447296d23308c7d1 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:07 +0200 Subject: [PATCH 1841/2380] s390x/tcg: SET CLOCK COMPARATOR can clear CKC interrupts Let's stop the timer and delete any pending CKC IRQ before doing anything else. While at it, add a comment why the check for ckc == -1ULL is needed. Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-7-david@redhat.com> Signed-off-by: Cornelia Huck --- target/s390x/misc_helper.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index 229f14d3da..e4c1522140 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -154,6 +154,13 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time) { S390TODState *td = s390_get_todstate(); + /* stop the timer and remove pending CKC IRQs */ + timer_del(env->tod_timer); + qemu_mutex_lock_iothread(); + env->pending_int &= ~INTERRUPT_EXT_CLOCK_COMPARATOR; + qemu_mutex_unlock_iothread(); + + /* the tod has to exceed the ckc, this can never happen if ckc is all 1's */ if (time == -1ULL) { return; } From 9dc6753718d4c0fe327729fea94e4d9f3f5a3d17 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:08 +0200 Subject: [PATCH 1842/2380] s390x/tcg: implement SET CLOCK This allows a guest to change its TOD. We already take care of updating all CKC timers from within S390TODClass. Use MO_ALIGN to load the operand manually - this will properly trigger a SPECIFICATION exception. Acked-by: Thomas Huth Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-8-david@redhat.com> Signed-off-by: Cornelia Huck --- target/s390x/helper.h | 1 + target/s390x/insn-data.def | 3 +-- target/s390x/misc_helper.c | 16 ++++++++++++++++ target/s390x/translate.c | 9 +++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 59cba86a27..97c60ca7bc 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -127,6 +127,7 @@ DEF_HELPER_4(diag, void, env, i32, i32, i32) DEF_HELPER_3(load_psw, noreturn, env, i64, i64) DEF_HELPER_FLAGS_2(spx, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_1(stck, TCG_CALL_NO_RWG_SE, i64, env) +DEF_HELPER_FLAGS_2(sck, TCG_CALL_NO_RWG, i32, env, i64) DEF_HELPER_FLAGS_2(sckc, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_2(sckpf, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_1(stckc, TCG_CALL_NO_RWG, i64, env) diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def index 157619403d..5c6f33ed9c 100644 --- a/target/s390x/insn-data.def +++ b/target/s390x/insn-data.def @@ -997,8 +997,7 @@ /* SET ADDRESS SPACE CONTROL FAST */ C(0xb279, SACF, S, Z, 0, a2, 0, 0, sacf, 0) /* SET CLOCK */ - /* ??? Not implemented - is it necessary? */ - C(0xb204, SCK, S, Z, 0, 0, 0, 0, 0, 0) + C(0xb204, SCK, S, Z, la2, 0, 0, 0, sck, 0) /* SET CLOCK COMPARATOR */ C(0xb206, SCKC, S, Z, 0, m2_64a, 0, 0, sckc, 0) /* SET CLOCK PROGRAMMABLE FIELD */ diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index e4c1522140..7656a9b90a 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -184,6 +184,22 @@ void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) helper_sckc(env, env->ckc); } +/* Set Clock */ +uint32_t HELPER(sck)(CPUS390XState *env, uint64_t tod_low) +{ + S390TODState *td = s390_get_todstate(); + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + S390TOD tod = { + .high = 0, + .low = tod_low, + }; + + qemu_mutex_lock_iothread(); + tdc->set(td, &tod, &error_abort); + qemu_mutex_unlock_iothread(); + return 0; +} + /* Set Tod Programmable Field */ void HELPER(sckpf)(CPUS390XState *env, uint64_t r0) { diff --git a/target/s390x/translate.c b/target/s390x/translate.c index fdfec7feba..57c03cbf58 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -4016,6 +4016,15 @@ static DisasJumpType op_stcke(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static DisasJumpType op_sck(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + tcg_gen_qemu_ld_i64(o->in1, o->addr1, get_mem_index(s), MO_TEQ | MO_ALIGN); + gen_helper_sck(cc_op, cpu_env, o->in1); + set_cc_static(s); + return DISAS_NEXT; +} + static DisasJumpType op_sckc(DisasContext *s, DisasOps *o) { check_privileged(s); From 7c12f710bad60dc7e509da4e80c77e952ef0490c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:09 +0200 Subject: [PATCH 1843/2380] s390x/tcg: rearm the CKC timer during migration If the CPU data is migrated after the TOD clock, the CKC timer of a CPU is not rearmed. Let's rearm it when loading the CPU state. Introduce tcg-stub.c just like kvm-stub.c for tcg specific stubs. Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-9-david@redhat.com> Signed-off-by: Cornelia Huck --- target/s390x/Makefile.objs | 1 + target/s390x/machine.c | 6 ++++++ target/s390x/tcg-stub.c | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 target/s390x/tcg-stub.c diff --git a/target/s390x/Makefile.objs b/target/s390x/Makefile.objs index 31932de9cf..22a9a9927a 100644 --- a/target/s390x/Makefile.objs +++ b/target/s390x/Makefile.objs @@ -5,6 +5,7 @@ obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o arch_dump.o mmu_helper.o diag.o obj-$(CONFIG_SOFTMMU) += sigp.o obj-$(CONFIG_KVM) += kvm.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o +obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o # build and run feature list generator feat-src = $(SRC_PATH)/target/$(TARGET_BASE_ARCH)/ diff --git a/target/s390x/machine.c b/target/s390x/machine.c index 84b4928755..bd3230d027 100644 --- a/target/s390x/machine.c +++ b/target/s390x/machine.c @@ -19,6 +19,7 @@ #include "cpu.h" #include "internal.h" #include "kvm_s390x.h" +#include "tcg_s390x.h" #include "sysemu/kvm.h" static int cpu_post_load(void *opaque, int version_id) @@ -34,6 +35,11 @@ static int cpu_post_load(void *opaque, int version_id) return kvm_s390_vcpu_interrupt_post_load(cpu); } + if (tcg_enabled()) { + /* Rearm the CKC timer if necessary */ + tcg_s390_tod_updated(CPU(cpu), RUN_ON_CPU_NULL); + } + return 0; } diff --git a/target/s390x/tcg-stub.c b/target/s390x/tcg-stub.c new file mode 100644 index 0000000000..c93501db0b --- /dev/null +++ b/target/s390x/tcg-stub.c @@ -0,0 +1,20 @@ +/* + * QEMU TCG support -- s390x specific function stubs. + * + * Copyright (C) 2018 Red Hat Inc + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "tcg_s390x.h" + +void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) +{ +} From d66b43c896f89d610f8738169db8e10e63a3a9e6 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:10 +0200 Subject: [PATCH 1844/2380] s390x/tcg: fix CPU hotplug with single-threaded TCG run_on_cpu() doesn't seem to work reliably until the CPU has been fully created if the single-threaded TCG main loop is already running. Therefore, hotplugging a CPU under single-threaded TCG does currently not work. We should use the direct call instead of going via run_on_cpu(). So let's use run_on_cpu() for KVM only - KVM requires it due to the initial CPU reset ioctl. As a nice side effect, we get rid of the ifdef. Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-10-david@redhat.com> Signed-off-by: Cornelia Huck --- target/s390x/cpu.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 40d6980229..271c5ce652 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -218,11 +218,18 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) #endif s390_cpu_gdb_init(cs); qemu_init_vcpu(cs); -#if !defined(CONFIG_USER_ONLY) - run_on_cpu(cs, s390_do_cpu_full_reset, RUN_ON_CPU_NULL); -#else - cpu_reset(cs); -#endif + + /* + * KVM requires the initial CPU reset ioctl to be executed on the target + * CPU thread. CPU hotplug under single-threaded TCG will not work with + * run_on_cpu(), as run_on_cpu() will not work properly if called while + * the main thread is already running but the CPU hasn't been realized. + */ + if (kvm_enabled()) { + run_on_cpu(cs, s390_do_cpu_full_reset, RUN_ON_CPU_NULL); + } else { + cpu_reset(cs); + } scc->parent_realize(dev, &err); out: From 8151942151fe1cbf072414d8822b80b00abfbf42 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 28 Jun 2018 13:38:16 +0200 Subject: [PATCH 1845/2380] s390x/kvm: legacy_s390_alloc() only supports one allocation We always allocate at a fixed address, a second allocation can therefore of course never work. We would simply overwrite mappings. This can e.g. happen in s390_memory_init(), if trying to allocate more than > 8TB. Let's just bail out, as there is no need for supporting it (legacy handling for z/VM). Signed-off-by: David Hildenbrand Message-Id: <20180628113817.30814-2-david@redhat.com> Reviewed-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- target/s390x/kvm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index 8bcd832123..a9d6d606df 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -752,12 +752,20 @@ int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf, */ static void *legacy_s390_alloc(size_t size, uint64_t *align, bool shared) { - void *mem; + static void *mem; + + if (mem) { + /* we only support one allocation, which is enough for initial ram */ + return NULL; + } mem = mmap((void *) 0x800000000ULL, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); - return mem == MAP_FAILED ? NULL : mem; + if (mem == MAP_FAILED) { + mem = NULL; + } + return mem; } static uint8_t const *sw_bp_inst; From d44444b0740680d996c979c8c3f63ab1c87f1cb1 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 28 Jun 2018 13:38:17 +0200 Subject: [PATCH 1846/2380] s390x/kvm: indicate alignment in legacy_s390_alloc() Let's do this for completeness reason, although we don't support e.g. PCDIMM/NVDIMM, which would use the alignment for placing the memory region in guest physical memory. But maybe someday we would want to support something like this - then we don't forget about this if allowing multiple allocations in legacy_s390_alloc(). Use the same alignment as we would set in qemu_anon_ram_alloc(). Our fixed address satisfies this alignment (1MB). This implicitly sets the alignment of the underlying memory region. Signed-off-by: David Hildenbrand Message-Id: <20180628113817.30814-3-david@redhat.com> Signed-off-by: Cornelia Huck --- target/s390x/kvm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index a9d6d606df..d923cf4240 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -765,6 +765,9 @@ static void *legacy_s390_alloc(size_t size, uint64_t *align, bool shared) if (mem == MAP_FAILED) { mem = NULL; } + if (mem && align) { + *align = QEMU_VMALLOC_ALIGN; + } return mem; } From 30c8db0e219a3c1d8b39c19e8b858830cb141738 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 29 Jun 2018 19:05:20 +0200 Subject: [PATCH 1847/2380] s390x/tcg: fix locking problem with tcg_s390_tod_updated tcg_s390_tod_updated() is always called with the iothread being locked (e.g. from S390TODClass->set() e.g. via HELPER(sck) or on incoming migration). The helper we call takes the lock itself - bad. Let's change that by factoring out updating the ckc timer. This now looks much nicer than having to call a helper from another function. While touching it we also make sure that env->ckc is updated even if the new value is -1ULL, for now it would not have been modified in that case. Reported-by: Christian Borntraeger Signed-off-by: David Hildenbrand Message-Id: <20180629170520.13671-1-david@redhat.com> Reviewed-by: Richard Henderson Signed-off-by: Cornelia Huck --- target/s390x/misc_helper.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index 7656a9b90a..3f91579570 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -149,26 +149,23 @@ uint64_t HELPER(stck)(CPUS390XState *env) return tod.low; } -/* Set Clock Comparator */ -void HELPER(sckc)(CPUS390XState *env, uint64_t time) +static void update_ckc_timer(CPUS390XState *env) { S390TODState *td = s390_get_todstate(); + uint64_t time; /* stop the timer and remove pending CKC IRQs */ timer_del(env->tod_timer); - qemu_mutex_lock_iothread(); + g_assert(qemu_mutex_iothread_locked()); env->pending_int &= ~INTERRUPT_EXT_CLOCK_COMPARATOR; - qemu_mutex_unlock_iothread(); /* the tod has to exceed the ckc, this can never happen if ckc is all 1's */ - if (time == -1ULL) { + if (env->ckc == -1ULL) { return; } - env->ckc = time; - /* difference between origins */ - time -= td->base.low; + time = env->ckc - td->base.low; /* nanoseconds */ time = tod2time(time); @@ -176,12 +173,21 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time) timer_mod(env->tod_timer, time); } +/* Set Clock Comparator */ +void HELPER(sckc)(CPUS390XState *env, uint64_t ckc) +{ + env->ckc = ckc; + + qemu_mutex_lock_iothread(); + update_ckc_timer(env); + qemu_mutex_unlock_iothread(); +} + void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) { S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; - helper_sckc(env, env->ckc); + update_ckc_timer(&cpu->env); } /* Set Clock */ From 3b84967c192fba485340f7c34ceb4f877d27b36d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 27 Jun 2018 13:19:36 +0200 Subject: [PATCH 1848/2380] audio/hda: drop atomics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doesn't build on 32bit clang. And because we run under qemu mutex anyway they are not needed. Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20180627111936.31019-1-kraxel@redhat.com Signed-off-by: Peter Maydell --- hw/audio/hda-codec.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index fc4945086b..31c66d4255 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu/atomic.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "intel-hda.h" @@ -209,7 +208,7 @@ static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) } trace_hda_audio_adjust(st->node->name, target_pos); - atomic_fetch_add(&st->buft_start, corr); + st->buft_start += corr; } static void hda_audio_input_timer(void *opaque) @@ -218,9 +217,9 @@ static void hda_audio_input_timer(void *opaque) int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - int64_t buft_start = atomic_fetch_add(&st->buft_start, 0); - int64_t wpos = atomic_fetch_add(&st->wpos, 0); - int64_t rpos = atomic_fetch_add(&st->rpos, 0); + int64_t buft_start = st->buft_start; + int64_t wpos = st->wpos; + int64_t rpos = st->rpos; int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start) / NANOSECONDS_PER_SECOND; @@ -242,7 +241,7 @@ static void hda_audio_input_timer(void *opaque) } rpos += chunk; to_transfer -= chunk; - atomic_fetch_add(&st->rpos, chunk); + st->rpos += chunk; } out_timer: @@ -256,8 +255,8 @@ static void hda_audio_input_cb(void *opaque, int avail) { HDAAudioStream *st = opaque; - int64_t wpos = atomic_fetch_add(&st->wpos, 0); - int64_t rpos = atomic_fetch_add(&st->rpos, 0); + int64_t wpos = st->wpos; + int64_t rpos = st->rpos; int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail); @@ -269,7 +268,7 @@ static void hda_audio_input_cb(void *opaque, int avail) uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk); wpos += read; to_transfer -= read; - atomic_fetch_add(&st->wpos, read); + st->wpos += read; if (chunk != read) { break; } @@ -282,9 +281,9 @@ static void hda_audio_output_timer(void *opaque) int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - int64_t buft_start = atomic_fetch_add(&st->buft_start, 0); - int64_t wpos = atomic_fetch_add(&st->wpos, 0); - int64_t rpos = atomic_fetch_add(&st->rpos, 0); + int64_t buft_start = st->buft_start; + int64_t wpos = st->wpos; + int64_t rpos = st->rpos; int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start) / NANOSECONDS_PER_SECOND; @@ -306,7 +305,7 @@ static void hda_audio_output_timer(void *opaque) } wpos += chunk; to_transfer -= chunk; - atomic_fetch_add(&st->wpos, chunk); + st->wpos += chunk; } out_timer: @@ -320,8 +319,8 @@ static void hda_audio_output_cb(void *opaque, int avail) { HDAAudioStream *st = opaque; - int64_t wpos = atomic_fetch_add(&st->wpos, 0); - int64_t rpos = atomic_fetch_add(&st->rpos, 0); + int64_t wpos = st->wpos; + int64_t rpos = st->rpos; int64_t to_transfer = audio_MIN(wpos - rpos, avail); @@ -342,7 +341,7 @@ static void hda_audio_output_cb(void *opaque, int avail) uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk); rpos += written; to_transfer -= written; - atomic_fetch_add(&st->rpos, written); + st->rpos += written; if (chunk != written) { break; } From d9345f1e1b1936b9ed7026b83091cf701f7ce07e Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 25 Jun 2018 22:35:59 +0200 Subject: [PATCH 1849/2380] target/m68k: correctly disassemble move16 "move16 %a0@+,%a1@" and "fmovel (cpid=3) %a0@-,%fpcr" share the same opcode. To fix that, backport the fix from binutils: 2005-11-10 Andreas Schwab * m68k-dis.c (print_insn_m68k): Only match FPU insns with coprocessor ID 1. Reported-by: Thomas Huth Signed-off-by: Laurent Vivier Tested-by: Thomas Huth Message-Id: <20180625203559.21370-2-laurent@vivier.eu> --- disas/m68k.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/disas/m68k.c b/disas/m68k.c index 61b689ef3e..a687df437c 100644 --- a/disas/m68k.c +++ b/disas/m68k.c @@ -2017,6 +2017,20 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info) } } + /* Don't match FPU insns with non-default coprocessor ID. */ + if (*d == '\0') + { + for (d = opc->args; *d; d += 2) + { + if (d[0] == 'I') + { + val = fetch_arg (buffer, 'd', 3, info); + if (val != 1) + break; + } + } + } + if (*d == '\0') if ((val = match_insn_m68k (memaddr, info, opc, & priv))) return val; From 646f34fa5482e495483de230b4cf0f2ae4fd2781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 29 Jun 2018 17:07:10 -0300 Subject: [PATCH 1850/2380] tcg: Fix --disable-tcg build breakage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the --disable-tcg breakage introduced by 8bca9a03ec60d: $ configure --disable-tcg [...] $ make -C i386-softmmu exec.o make: Entering directory 'i386-softmmu' CC exec.o In file included from source/qemu/exec.c:62:0: source/qemu/include/exec/ram_addr.h:96:6: error: conflicting types for ‘tb_invalidate_phys_range’ void tb_invalidate_phys_range(ram_addr_t start, ram_addr_t end); ^~~~~~~~~~~~~~~~~~~~~~~~ In file included from source/qemu/exec.c:24:0: source/qemu/include/exec/exec-all.h:309:6: note: previous declaration of ‘tb_invalidate_phys_range’ was here void tb_invalidate_phys_range(target_ulong start, target_ulong end); ^~~~~~~~~~~~~~~~~~~~~~~~ source/qemu/exec.c:1043:6: error: conflicting types for ‘tb_invalidate_phys_addr’ void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) ^~~~~~~~~~~~~~~~~~~~~~~ In file included from source/qemu/exec.c:24:0: source/qemu/include/exec/exec-all.h:308:6: note: previous declaration of ‘tb_invalidate_phys_addr’ was here void tb_invalidate_phys_addr(target_ulong addr); ^~~~~~~~~~~~~~~~~~~~~~~ make: *** [source/qemu/rules.mak:69: exec.o] Error 1 make: Leaving directory 'i386-softmmu' Tested to build x86_64-softmmu and i386-softmmu targets. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180629200710.27626-1-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- accel/stubs/tcg-stub.c | 6 ++++++ exec.c | 2 +- include/exec/exec-all.h | 13 +++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index 76ae461749..8ee85ed665 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -16,6 +16,7 @@ #include "tcg/tcg.h" #include "exec/cpu-common.h" #include "exec/exec-all.h" +#include "translate-all.h" void tb_flush(CPUState *cpu) { @@ -24,3 +25,8 @@ void tb_flush(CPUState *cpu) void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) { } + +void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, + int is_cpu_write_access) +{ +} diff --git a/exec.c b/exec.c index cdcf769daa..ee726888a5 100644 --- a/exec.c +++ b/exec.c @@ -1027,7 +1027,7 @@ const char *parse_cpu_model(const char *cpu_model) return cpu_type; } -#if defined(CONFIG_USER_ONLY) +#if defined(CONFIG_USER_ONLY) || !defined(CONFIG_TCG) void tb_invalidate_phys_addr(target_ulong addr) { mmap_lock(); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 6a7e7a866e..cb497dee0b 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -255,7 +255,6 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, void tlb_set_page(CPUState *cpu, target_ulong vaddr, hwaddr paddr, int prot, int mmu_idx, target_ulong size); -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs); void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, uintptr_t retaddr); #else @@ -304,9 +303,6 @@ static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap) { } - -void tb_invalidate_phys_addr(target_ulong addr); -void tb_invalidate_phys_range(target_ulong start, target_ulong end); #endif #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ @@ -415,6 +411,15 @@ static inline uint32_t curr_cflags(void) | (use_icount ? CF_USE_ICOUNT : 0); } +/* TranslationBlock invalidate API */ +#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) +void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs); +#else +void tb_invalidate_phys_addr(target_ulong addr); +#endif +#if defined(CONFIG_USER_ONLY) +void tb_invalidate_phys_range(target_ulong start, target_ulong end); +#endif void tb_flush(CPUState *cpu); void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, From 475120099d446fe106ad970a225327990d747a61 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Sun, 10 Jun 2018 20:49:27 +0200 Subject: [PATCH 1851/2380] i386/kvm: add support for Hyper-V TLB flush Add support for Hyper-V TLB flush which recently got added to KVM. Just like regular Hyper-V we announce HV_EX_PROCESSOR_MASKS_RECOMMENDED regardless of how many vCPUs we have. Windows is 'smart' and uses less expensive non-EX Hypercall whenever possible (when it wants to flush TLB for all vCPUs or the maximum vCPU index in the vCPU set requires flushing is less than 64). Signed-off-by: Vitaly Kuznetsov Message-Id: <20180610184927.19309-1-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 1 + target/i386/cpu.h | 1 + target/i386/hyperv-proto.h | 1 + target/i386/kvm.c | 15 ++++++++++++++- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1e6a7d0a75..d57f3104e1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5415,6 +5415,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("hv-stimer", X86CPU, hyperv_stimer, false), DEFINE_PROP_BOOL("hv-frequencies", X86CPU, hyperv_frequencies, false), DEFINE_PROP_BOOL("hv-reenlightenment", X86CPU, hyperv_reenlightenment, false), + DEFINE_PROP_BOOL("hv-tlbflush", X86CPU, hyperv_tlbflush, false), DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true), DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false), DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8eaefeee3e..8b1ddfe932 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1367,6 +1367,7 @@ struct X86CPU { bool hyperv_stimer; bool hyperv_frequencies; bool hyperv_reenlightenment; + bool hyperv_tlbflush; bool check_cpuid; bool enforce_cpuid; bool expose_kvm; diff --git a/target/i386/hyperv-proto.h b/target/i386/hyperv-proto.h index 93352ebd2a..d6d5a79293 100644 --- a/target/i386/hyperv-proto.h +++ b/target/i386/hyperv-proto.h @@ -58,6 +58,7 @@ #define HV_APIC_ACCESS_RECOMMENDED (1u << 3) #define HV_SYSTEM_RESET_RECOMMENDED (1u << 4) #define HV_RELAXED_TIMING_RECOMMENDED (1u << 5) +#define HV_EX_PROCESSOR_MASKS_RECOMMENDED (1u << 11) /* * Basic virtualized MSRs diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 032f0ad2fc..ebb2d23aa4 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -601,7 +601,8 @@ static bool hyperv_enabled(X86CPU *cpu) cpu->hyperv_runtime || cpu->hyperv_synic || cpu->hyperv_stimer || - cpu->hyperv_reenlightenment); + cpu->hyperv_reenlightenment || + cpu->hyperv_tlbflush); } static int kvm_arch_set_tsc_khz(CPUState *cs) @@ -839,6 +840,18 @@ int kvm_arch_init_vcpu(CPUState *cs) if (cpu->hyperv_vapic) { c->eax |= HV_APIC_ACCESS_RECOMMENDED; } + if (cpu->hyperv_tlbflush) { + if (kvm_check_extension(cs->kvm_state, + KVM_CAP_HYPERV_TLBFLUSH) <= 0) { + fprintf(stderr, "Hyper-V TLB flush support " + "(requested by 'hv-tlbflush' cpu flag) " + " is not supported by kernel\n"); + return -ENOSYS; + } + c->eax |= HV_REMOTE_TLB_FLUSH_RECOMMENDED; + c->eax |= HV_EX_PROCESSOR_MASKS_RECOMMENDED; + } + c->ebx = cpu->hyperv_spinlock_attempts; c = &cpuid_data.entries[cpuid_i++]; From 5c0ef67ac406b6373b2d32f901752cd0f28e0f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 18 Apr 2018 18:11:51 +0100 Subject: [PATCH 1852/2380] configure: add sanity check to catch builds from "git archive" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "git archive" feature creates tarballs which are missing all submodule content. GitHub unhelpfully provides users with "Download" links that claim to give them valid source release tarballs. These GitHub archives will not be buildable as they are created by the "git archive" feature and so are missing content. The user gets unhelpful messages from make such as: fatal error: ui/input-keymap-atset1-to-qcode.c: No such file or directory By adding a sanity check we can give users an informative message about what they've done wrong. Signed-off-by: Daniel P. Berrangé Message-Id: <20180418171151.5263-1-berrange@redhat.com> Reviewed-by: Cornelia Huck Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- configure | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/configure b/configure index 65548df1fc..dcb605d7a2 100755 --- a/configure +++ b/configure @@ -300,6 +300,24 @@ then else git_update=no git_submodules="" + + if ! test -f "$source_path/ui/keycodemapdb/README" + then + echo + echo "ERROR: missing file $source_path/ui/keycodemapdb/README" + echo + echo "This is not a GIT checkout but module content appears to" + echo "be missing. Do not use 'git archive' or GitHub download links" + echo "to acquire QEMU source archives. Non-GIT builds are only" + echo "supported with source archives linked from:" + echo + echo " https://www.qemu.org/download/" + echo + echo "Developers working with GIT can use scripts/archive-source.sh" + echo "if they need to create valid source archives." + echo + exit 1 + fi fi git="git" From 7ecdc94c40f4958a66893c0eac423c6a80f376d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:41:53 -0300 Subject: [PATCH 1853/2380] include: Add IEC binary prefixes in "qemu/units.h" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loosely based on 076b35b5a56. Suggested-by: Stefan Weil Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-2-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- include/qemu/units.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 include/qemu/units.h diff --git a/include/qemu/units.h b/include/qemu/units.h new file mode 100644 index 0000000000..692db3fbb2 --- /dev/null +++ b/include/qemu/units.h @@ -0,0 +1,20 @@ +/* + * IEC binary prefixes definitions + * + * Copyright (C) 2015 Nikunj A Dadhania, IBM Corporation + * Copyright (C) 2018 Philippe Mathieu-Daudé + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_UNITS_H +#define QEMU_UNITS_H + +#define KiB (INT64_C(1) << 10) +#define MiB (INT64_C(1) << 20) +#define GiB (INT64_C(1) << 30) +#define TiB (INT64_C(1) << 40) +#define PiB (INT64_C(1) << 50) +#define EiB (INT64_C(1) << 60) + +#endif From f043568f548cfc6becf94f9582c330270580376d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:41:54 -0300 Subject: [PATCH 1854/2380] vdi: Use definitions from "qemu/units.h" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Weil Message-Id: <20180625124238.25339-3-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- block/vdi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/block/vdi.c b/block/vdi.c index 1d8ed67dbf..6555cffb88 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -50,6 +50,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" @@ -83,9 +84,6 @@ /* Command line option for static images. */ #define BLOCK_OPT_STATIC "static" -#define KiB 1024 -#define MiB (KiB * KiB) - #define SECTOR_SIZE 512 #define DEFAULT_CLUSTER_SIZE (1 * MiB) @@ -434,7 +432,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } else if (header.block_size != DEFAULT_CLUSTER_SIZE) { error_setg(errp, "unsupported VDI image (block size %" PRIu32 - " is not %u)", header.block_size, DEFAULT_CLUSTER_SIZE); + " is not %" PRIu64 ")", + header.block_size, DEFAULT_CLUSTER_SIZE); ret = -ENOTSUP; goto fail; } else if (header.disk_size > From 6a4e0614c3e2ca01ac030e9c486c77d4d7225021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:41:55 -0300 Subject: [PATCH 1855/2380] x86/cpu: Use definitions from "qemu/units.h" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Acked-by: Eduardo Habkost Message-Id: <20180625124238.25339-4-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d57f3104e1..bdbd446b88 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/cutils.h" #include "qemu/bitops.h" @@ -65,9 +66,6 @@ struct CPUID2CacheDescriptorInfo { int associativity; }; -#define KiB 1024 -#define MiB (1024 * 1024) - /* * Known CPUID 2 cache descriptors. * From Intel SDM Volume 2A, CPUID instruction From 5fa96cad01bf408064aeea19812c0692ddfbd2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:41:56 -0300 Subject: [PATCH 1856/2380] checkpatch: Recognize IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not match the IEC binary prefix as camelcase typedefs. This fixes: ERROR: "foo * bar" should be "foo *bar" #310: FILE: hw/ppc/ppc440_uc.c:564: + size = 8 * MiB * sh; total: 1 errors, 0 warnings, 433 lines checked Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-5-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- scripts/checkpatch.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index e3d8c2cdfc..223681bfd0 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -242,6 +242,7 @@ our $UTF8 = qr{ # There are still some false positives, but this catches most # common cases. our $typeTypedefs = qr{(?x: + (?![KMGTPE]iB) # IEC binary prefix (do not match) [A-Z][A-Z\d_]*[a-z][A-Za-z\d_]* # camelcase | [A-Z][A-Z\d_]*AIOCB # all uppercase | [A-Z][A-Z\d_]*CPU # all uppercase From c3513c836e4c19cd7a2a7f691995fdef587cec72 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Jun 2018 08:40:23 -0700 Subject: [PATCH 1857/2380] target/openrisc: Fix mtspr shadow gprs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missing break when this feature was added in 89e71e873d ("target/openrisc: implement shadow registers"). This was causing strange issues as we get writes into the translation block jump cache and other bits of state. Fixes: 89e71e873d ("target/openrisc: implement shadow registers") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/sys_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index b284064381..2f337363ec 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -98,6 +98,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, case TO_SPR(0, 1024) ... TO_SPR(0, 1024 + (16 * 32)): /* Shadow GPRs */ idx = (spr - 1024); env->shadow_gpr[idx / 32][idx % 32] = rb; + break; case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); From d23b6caadbfaf56092593e8ff22fb5797db38488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:41:57 -0300 Subject: [PATCH 1858/2380] hw: Use IEC binary prefix definitions from "qemu/units.h" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code change produced with: $ git ls-files | egrep '\.[ch]$' | \ xargs sed -i -e 's/\(\W[KMGTPE]\)_BYTE/\1iB/g' Suggested-by: Stefan Weil Signed-off-by: Philippe Mathieu-Daudé Acked-by: David Gibson (ppc parts) Message-Id: <20180625124238.25339-6-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/arm/msf2-soc.c | 4 ++-- hw/arm/msf2-som.c | 6 +++--- hw/core/loader-fit.c | 3 ++- hw/core/machine.c | 2 +- hw/display/sm501.c | 14 +++++++------- hw/hppa/machine.c | 2 +- hw/mips/boston.c | 28 ++++++++++++++-------------- hw/ppc/pnv.c | 4 ++-- hw/ppc/ppc440_uc.c | 26 +++++++++++++------------- hw/ppc/prep.c | 2 +- hw/ppc/sam460ex.c | 2 +- hw/ppc/spapr.c | 10 +++++----- hw/ppc/spapr_rtas.c | 2 +- hw/sd/sd.c | 4 ++-- hw/sd/sdhci.c | 2 +- include/qemu/cutils.h | 8 +------- tests/test-cutils.c | 21 +++++++++++---------- tests/test-keyval.c | 7 ++++--- tests/test-qemu-opts.c | 9 ++++----- 19 files changed, 76 insertions(+), 80 deletions(-) diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index 75c44adf7d..530e461c42 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -40,14 +40,14 @@ #define SRAM_BASE_ADDRESS 0x20000000 -#define MSF2_ENVM_MAX_SIZE (512 * K_BYTE) +#define MSF2_ENVM_MAX_SIZE (512 * KiB) /* * eSRAM max size is 80k without SECDED(Single error correction and * dual error detection) feature and 64k with SECDED. * We do not support SECDED now. */ -#define MSF2_ESRAM_MAX_SIZE (80 * K_BYTE) +#define MSF2_ESRAM_MAX_SIZE (80 * KiB) static const uint32_t spi_addr[MSF2_NUM_SPIS] = { 0x40001000 , 0x40011000 }; static const uint32_t uart_addr[MSF2_NUM_UARTS] = { 0x40000000 , 0x40010000 }; diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index 0795a3a3a1..ed533bbde1 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -33,10 +33,10 @@ #include "cpu.h" #define DDR_BASE_ADDRESS 0xA0000000 -#define DDR_SIZE (64 * M_BYTE) +#define DDR_SIZE (64 * MiB) -#define M2S010_ENVM_SIZE (256 * K_BYTE) -#define M2S010_ESRAM_SIZE (64 * K_BYTE) +#define M2S010_ENVM_SIZE (256 * KiB) +#define M2S010_ESRAM_SIZE (64 * KiB) static void emcraft_sf2_s2s010_init(MachineState *machine) { diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 6387854b54..447f60857d 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "exec/memory.h" #include "hw/loader.h" #include "hw/loader-fit.h" @@ -194,7 +195,7 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, err = fit_image_addr(itb, img_off, "load", &load_addr); if (err == -ENOENT) { - load_addr = ROUND_UP(kernel_end, 64 * K_BYTE) + (10 * M_BYTE); + load_addr = ROUND_UP(kernel_end, 64 * KiB) + (10 * MiB); } else if (err) { ret = err; goto out; diff --git a/hw/core/machine.c b/hw/core/machine.c index 617e5f8d75..ccf3a4b9c7 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -522,7 +522,7 @@ static void machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); /* Default 128 MB as guest ram size */ - mc->default_ram_size = 128 * M_BYTE; + mc->default_ram_size = 128 * MiB; mc->rom_file_has_mr = true; /* numa node memory size aligned on 8MB by default. diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 8206ae81a1..3bd871630e 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -452,12 +452,12 @@ /* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */ static const uint32_t sm501_mem_local_size[] = { - [0] = 4 * M_BYTE, - [1] = 8 * M_BYTE, - [2] = 16 * M_BYTE, - [3] = 32 * M_BYTE, - [4] = 64 * M_BYTE, - [5] = 2 * M_BYTE, + [0] = 4 * MiB, + [1] = 8 * MiB, + [2] = 16 * MiB, + [3] = 32 * MiB, + [4] = 64 * MiB, + [5] = 2 * MiB, }; #define get_local_mem_size(s) sm501_mem_local_size[(s)->local_mem_size_index] @@ -1829,7 +1829,7 @@ static void sm501_realize_pci(PCIDevice *dev, Error **errp) } static Property sm501_pci_properties[] = { - DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * M_BYTE), + DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * MiB), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index aba269bb85..2ba26962f9 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -275,7 +275,7 @@ static void machine_hppa_machine_init(MachineClass *mc) mc->max_cpus = HPPA_MAX_CPUS; mc->default_cpus = 1; mc->is_default = 1; - mc->default_ram_size = 512 * M_BYTE; + mc->default_ram_size = 512 * MiB; mc->default_boot_order = "cd"; } diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 14e6f955d2..939cef5596 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -200,7 +200,7 @@ static uint64_t boston_platreg_read(void *opaque, hwaddr addr, val |= PLAT_BUILD_CFG_PCIE2_EN; return val; case PLAT_DDR_CFG: - val = s->mach->ram_size / G_BYTE; + val = s->mach->ram_size / GiB; assert(!(val & ~PLAT_DDR_CFG_SIZE)); val |= PLAT_DDR_CFG_MHZ; return val; @@ -355,7 +355,7 @@ static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, return NULL; } - ram_low_sz = MIN(256 * M_BYTE, machine->ram_size); + ram_low_sz = MIN(256 * MiB, machine->ram_size); ram_high_sz = machine->ram_size - ram_low_sz; qemu_fdt_setprop_sized_cells(fdt, "/memory@0", "reg", 1, 0x00000000, 1, ram_low_sz, @@ -436,8 +436,8 @@ static void boston_mach_init(MachineState *machine) int fw_size, fit_err; bool is_64b; - if ((machine->ram_size % G_BYTE) || - (machine->ram_size > (2 * G_BYTE))) { + if ((machine->ram_size % GiB) || + (machine->ram_size > (2 * GiB))) { error_report("Memory size must be 1GB or 2GB"); exit(1); } @@ -471,7 +471,7 @@ static void boston_mach_init(MachineState *machine) sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1); flash = g_new(MemoryRegion, 1); - memory_region_init_rom(flash, NULL, "boston.flash", 128 * M_BYTE, &err); + memory_region_init_rom(flash, NULL, "boston.flash", 128 * MiB, &err); memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0); ddr = g_new(MemoryRegion, 1); @@ -481,22 +481,22 @@ static void boston_mach_init(MachineState *machine) ddr_low_alias = g_new(MemoryRegion, 1); memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr", - ddr, 0, MIN(machine->ram_size, (256 * M_BYTE))); + ddr, 0, MIN(machine->ram_size, (256 * MiB))); memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0); xilinx_pcie_init(sys_mem, 0, - 0x10000000, 32 * M_BYTE, - 0x40000000, 1 * G_BYTE, + 0x10000000, 32 * MiB, + 0x40000000, 1 * GiB, get_cps_irq(s->cps, 2), false); xilinx_pcie_init(sys_mem, 1, - 0x12000000, 32 * M_BYTE, - 0x20000000, 512 * M_BYTE, + 0x12000000, 32 * MiB, + 0x20000000, 512 * MiB, get_cps_irq(s->cps, 1), false); pcie2 = xilinx_pcie_init(sys_mem, 2, - 0x14000000, 32 * M_BYTE, - 0x16000000, 1 * M_BYTE, + 0x14000000, 32 * MiB, + 0x16000000, 1 * MiB, get_cps_irq(s->cps, 0), true); platreg = g_new(MemoryRegion, 1); @@ -526,7 +526,7 @@ static void boston_mach_init(MachineState *machine) if (machine->firmware) { fw_size = load_image_targphys(machine->firmware, - 0x1fc00000, 4 * M_BYTE); + 0x1fc00000, 4 * MiB); if (fw_size == -1) { error_printf("unable to load firmware image '%s'\n", machine->firmware); @@ -552,7 +552,7 @@ static void boston_mach_class_init(MachineClass *mc) mc->desc = "MIPS Boston"; mc->init = boston_mach_init; mc->block_default_type = IF_IDE; - mc->default_ram_size = 1 * G_BYTE; + mc->default_ram_size = 1 * GiB; mc->max_cpus = 16; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("I6400"); } diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 7401ffe5b0..5fdac62311 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -556,7 +556,7 @@ static void pnv_init(MachineState *machine) char *chip_typename; /* allocate RAM */ - if (machine->ram_size < (1 * G_BYTE)) { + if (machine->ram_size < (1 * GiB)) { warn_report("skiboot may not work with < 1GB of RAM"); } @@ -1174,7 +1174,7 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) * storage */ mc->no_parallel = 1; mc->default_boot_order = NULL; - mc->default_ram_size = 1 * G_BYTE; + mc->default_ram_size = 1 * GiB; xic->icp_get = pnv_icp_get; xic->ics_get = pnv_ics_get; xic->ics_resend = pnv_ics_resend; diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 123f4ac09d..b5b31b4b9b 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -215,13 +215,13 @@ void ppc4xx_l2sram_init(CPUPPCState *env) l2sram = g_malloc0(sizeof(*l2sram)); /* XXX: Size is 4*64kB for 460ex, cf. U-Boot, ppc4xx-isram.h */ memory_region_init_ram(&l2sram->bank[0], NULL, "ppc4xx.l2sram_bank0", - 64 * K_BYTE, &error_abort); + 64 * KiB, &error_abort); memory_region_init_ram(&l2sram->bank[1], NULL, "ppc4xx.l2sram_bank1", - 64 * K_BYTE, &error_abort); + 64 * KiB, &error_abort); memory_region_init_ram(&l2sram->bank[2], NULL, "ppc4xx.l2sram_bank2", - 64 * K_BYTE, &error_abort); + 64 * KiB, &error_abort); memory_region_init_ram(&l2sram->bank[3], NULL, "ppc4xx.l2sram_bank3", - 64 * K_BYTE, &error_abort); + 64 * KiB, &error_abort); qemu_register_reset(&l2sram_reset, l2sram); ppc_dcr_register(env, DCR_L2CACHE_CFG, l2sram, &dcr_read_l2sram, &dcr_write_l2sram); @@ -513,28 +513,28 @@ static uint32_t sdram_bcr(hwaddr ram_base, hwaddr ram_size) uint32_t bcr; switch (ram_size) { - case (8 * M_BYTE): + case (8 * MiB): bcr = 0xffc0; break; - case (16 * M_BYTE): + case (16 * MiB): bcr = 0xff80; break; - case (32 * M_BYTE): + case (32 * MiB): bcr = 0xff00; break; - case (64 * M_BYTE): + case (64 * MiB): bcr = 0xfe00; break; - case (128 * M_BYTE): + case (128 * MiB): bcr = 0xfc00; break; - case (256 * M_BYTE): + case (256 * MiB): bcr = 0xf800; break; - case (512 * M_BYTE): + case (512 * MiB): bcr = 0xf000; break; - case (1 * G_BYTE): + case (1 * GiB): bcr = 0xe000; break; default: @@ -561,7 +561,7 @@ static target_ulong sdram_size(uint32_t bcr) if (sh == 0) { size = -1; } else { - size = 8 * M_BYTE * sh; + size = 8 * MiB * sh; } return size; diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 5ed0bcd862..25ae53304c 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -884,7 +884,7 @@ static void ibm_40p_machine_init(MachineClass *mc) mc->desc = "IBM RS/6000 7020 (40p)", mc->init = ibm_40p_init; mc->max_cpus = 1; - mc->default_ram_size = 128 * M_BYTE; + mc->default_ram_size = 128 * MiB; mc->block_default_type = IF_SCSI; mc->default_boot_order = "c"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("604"); diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index bdc53d2603..2a98c10664 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -597,7 +597,7 @@ static void sam460ex_machine_init(MachineClass *mc) mc->desc = "aCube Sam460ex"; mc->init = sam460ex_init; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb"); - mc->default_ram_size = 512 * M_BYTE; + mc->default_ram_size = 512 * MiB; } DEFINE_MACHINE("sam460ex", sam460ex_machine_init) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b32b971a14..7dbdb4e745 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2324,7 +2324,7 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) error_setg(errp, "Memory size 0x" RAM_ADDR_FMT " is not aligned to %llu MiB", machine->ram_size, - SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + SPAPR_MEMORY_BLOCK_SIZE / MiB); return; } @@ -2332,7 +2332,7 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) error_setg(errp, "Maximum memory size 0x" RAM_ADDR_FMT " is not aligned to %llu MiB", machine->ram_size, - SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + SPAPR_MEMORY_BLOCK_SIZE / MiB); return; } @@ -2342,7 +2342,7 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) "Node %d memory size 0x%" PRIx64 " is not aligned to %llu MiB", i, numa_info[i].node_mem, - SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + SPAPR_MEMORY_BLOCK_SIZE / MiB); return; } } @@ -3209,7 +3209,7 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, if (size % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Hotplugged memory size must be a multiple of " - "%lld MB", SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + "%lld MB", SPAPR_MEMORY_BLOCK_SIZE / MiB); return; } @@ -3961,7 +3961,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1024; mc->no_parallel = 1; mc->default_boot_order = ""; - mc->default_ram_size = 512 * M_BYTE; + mc->default_ram_size = 512 * MiB; mc->kvm_type = spapr_kvm_type; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_SPAPR_PCI_HOST_BRIDGE); mc->pci_allow_0_address = true; diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 7f9738daed..f32740b947 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -241,7 +241,7 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, "DesProcs=%d," "MaxPlatProcs=%d", max_cpus, - current_machine->ram_size / M_BYTE, + current_machine->ram_size / MiB, smp_cpus, max_cpus); ret = sysparm_st(buffer, length, param_val, strlen(param_val) + 1); diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 540bccb8d1..9a16b768ed 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -305,7 +305,7 @@ static void sd_ocr_powerup(void *opaque) /* card power-up OK */ sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_POWER_UP, 1); - if (sd->size > 1 * G_BYTE) { + if (sd->size > 1 * GiB) { sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_CAPACITY, 1); } } @@ -377,7 +377,7 @@ static void sd_set_csd(SDState *sd, uint64_t size) uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1; uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1; - if (size <= 1 * G_BYTE) { /* Standard Capacity SD */ + if (size <= 1 * GiB) { /* Standard Capacity SD */ sd->csd[0] = 0x00; /* CSD structure */ sd->csd[1] = 0x26; /* Data read access-time-1 */ sd->csd[2] = 0x00; /* Data read access-time-2 */ diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 321d02d75a..c74025b2f4 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -409,7 +409,7 @@ static void sdhci_end_transfer(SDHCIState *s) /* * Programmed i/o data transfer */ -#define BLOCK_SIZE_MASK (4 * K_BYTE - 1) +#define BLOCK_SIZE_MASK (4 * KiB - 1) /* Fill host controller's read buffer with BLKSIZE bytes of data from card */ static void sdhci_read_block_from_card(SDHCIState *s) diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 274d419bb7..6d5ea8351e 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -2,6 +2,7 @@ #define QEMU_CUTILS_H #include "qemu/fprintf-fn.h" +#include "qemu/units.h" /** * pstrcpy: @@ -155,13 +156,6 @@ int qemu_strtosz(const char *nptr, char **end, uint64_t *result); int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result); int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result); -#define K_BYTE (1ULL << 10) -#define M_BYTE (1ULL << 20) -#define G_BYTE (1ULL << 30) -#define T_BYTE (1ULL << 40) -#define P_BYTE (1ULL << 50) -#define E_BYTE (1ULL << 60) - /* used to print char* safely */ #define STR_OR_NULL(str) ((str) ? (str) : "null") diff --git a/tests/test-cutils.c b/tests/test-cutils.c index 64a489c2e9..d85c3e0f6d 100644 --- a/tests/test-cutils.c +++ b/tests/test-cutils.c @@ -26,8 +26,9 @@ */ #include "qemu/osdep.h" - +#include "qemu/units.h" #include "qemu/cutils.h" +#include "qemu/units.h" static void test_parse_uint_null(void) { @@ -2022,7 +2023,7 @@ static void test_qemu_strtosz_units(void) /* default is M */ err = qemu_strtosz_MiB(none, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, M_BYTE); + g_assert_cmpint(res, ==, MiB); g_assert(endptr == none + 1); err = qemu_strtosz(b, &endptr, &res); @@ -2032,32 +2033,32 @@ static void test_qemu_strtosz_units(void) err = qemu_strtosz(k, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, K_BYTE); + g_assert_cmpint(res, ==, KiB); g_assert(endptr == k + 2); err = qemu_strtosz(m, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, M_BYTE); + g_assert_cmpint(res, ==, MiB); g_assert(endptr == m + 2); err = qemu_strtosz(g, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, G_BYTE); + g_assert_cmpint(res, ==, GiB); g_assert(endptr == g + 2); err = qemu_strtosz(t, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, T_BYTE); + g_assert_cmpint(res, ==, TiB); g_assert(endptr == t + 2); err = qemu_strtosz(p, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, P_BYTE); + g_assert_cmpint(res, ==, PiB); g_assert(endptr == p + 2); err = qemu_strtosz(e, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, E_BYTE); + g_assert_cmpint(res, ==, EiB); g_assert(endptr == e + 2); } @@ -2070,7 +2071,7 @@ static void test_qemu_strtosz_float(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12.345 * M_BYTE); + g_assert_cmpint(res, ==, 12.345 * MiB); g_assert(endptr == str + 7); } @@ -2106,7 +2107,7 @@ static void test_qemu_strtosz_trailing(void) str = "123xxx"; err = qemu_strtosz_MiB(str, &endptr, &res); - g_assert_cmpint(res, ==, 123 * M_BYTE); + g_assert_cmpint(res, ==, 123 * MiB); g_assert(endptr == str + 3); err = qemu_strtosz(str, NULL, &res); diff --git a/tests/test-keyval.c b/tests/test-keyval.c index 63cb14629b..09b0ae3c68 100644 --- a/tests/test-keyval.c +++ b/tests/test-keyval.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" @@ -457,11 +458,11 @@ static void test_keyval_visit_size(void) visit_type_size(v, "sz2", &sz, &error_abort); g_assert_cmpuint(sz, ==, 1536); visit_type_size(v, "sz3", &sz, &error_abort); - g_assert_cmphex(sz, ==, 2 * M_BYTE); + g_assert_cmphex(sz, ==, 2 * MiB); visit_type_size(v, "sz4", &sz, &error_abort); - g_assert_cmphex(sz, ==, G_BYTE / 10); + g_assert_cmphex(sz, ==, GiB / 10); visit_type_size(v, "sz5", &sz, &error_abort); - g_assert_cmphex(sz, ==, 16777215 * T_BYTE); + g_assert_cmphex(sz, ==, 16777215ULL * TiB); visit_check_struct(v, &error_abort); visit_end_struct(v, NULL); visit_free(v); diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c index 7092e216f7..ef96e84aed 100644 --- a/tests/test-qemu-opts.c +++ b/tests/test-qemu-opts.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "qemu/cutils.h" +#include "qemu/units.h" #include "qemu/option.h" #include "qemu/option_int.h" #include "qapi/error.h" @@ -704,13 +704,12 @@ static void test_opts_parse_size(void) g_assert_cmpuint(opts_count(opts), ==, 3); g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, 8); g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 1536); - g_assert_cmphex(qemu_opt_get_size(opts, "size3", 0), ==, 2 * M_BYTE); + g_assert_cmphex(qemu_opt_get_size(opts, "size3", 0), ==, 2 * MiB); opts = qemu_opts_parse(&opts_list_02, "size1=0.1G,size2=16777215T", false, &error_abort); g_assert_cmpuint(opts_count(opts), ==, 2); - g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, G_BYTE / 10); - g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), - ==, 16777215 * T_BYTE); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, GiB / 10); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 16777215ULL * TiB); /* Beyond limit with suffix */ opts = qemu_opts_parse(&opts_list_02, "size1=16777216T", From fc6b3cf9e8ff5290ebac39e846add08e1e3c02c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:41:58 -0300 Subject: [PATCH 1859/2380] hw: Directly use "qemu/units.h" instead of "qemu/cutils.h" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These files don't use anything exposed by "qemu/cutils.h", simplify preprocessing including directly "qemu/units.h". Signed-off-by: Philippe Mathieu-Daudé Acked-by: David Gibson (ppc parts) Message-Id: <20180625124238.25339-7-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/arm/msf2-soc.c | 2 +- hw/arm/msf2-som.c | 2 +- hw/core/machine.c | 2 +- hw/display/sm501.c | 2 +- hw/mips/boston.c | 2 +- hw/ppc/pnv.c | 2 +- hw/ppc/ppc440_uc.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index 530e461c42..edb3ba824f 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -23,13 +23,13 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "hw/arm/arm.h" #include "exec/address-spaces.h" #include "hw/char/serial.h" #include "hw/boards.h" -#include "qemu/cutils.h" #include "hw/arm/msf2-soc.h" #include "hw/misc/unimp.h" diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index ed533bbde1..2432b5e935 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -23,12 +23,12 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/boards.h" #include "hw/arm/arm.h" #include "exec/address-spaces.h" -#include "qemu/cutils.h" #include "hw/arm/msf2-soc.h" #include "cpu.h" diff --git a/hw/core/machine.c b/hw/core/machine.c index ccf3a4b9c7..2077328bcc 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/boards.h" #include "qapi/error.h" #include "qapi/qapi-visit-common.h" @@ -19,7 +20,6 @@ #include "sysemu/sysemu.h" #include "sysemu/numa.h" #include "qemu/error-report.h" -#include "qemu/cutils.h" #include "sysemu/qtest.h" static char *machine_get_accel(Object *obj, Error **errp) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 3bd871630e..9dec0d3218 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -24,7 +24,7 @@ */ #include "qemu/osdep.h" -#include "qemu/cutils.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 939cef5596..6c9c20a93e 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "exec/address-spaces.h" @@ -32,7 +33,6 @@ #include "hw/mips/cpudevs.h" #include "hw/pci-host/xilinx-pcie.h" #include "qapi/error.h" -#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "chardev/char.h" diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 5fdac62311..346f5e7aed 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "sysemu/sysemu.h" #include "sysemu/numa.h" @@ -31,7 +32,6 @@ #include "hw/ppc/pnv_core.h" #include "hw/loader.h" #include "exec/address-spaces.h" -#include "qemu/cutils.h" #include "qapi/visitor.h" #include "monitor/monitor.h" #include "hw/intc/intc.h" diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index b5b31b4b9b..1ab2235f20 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -9,8 +9,8 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" -#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "cpu.h" From 519abcdf7b957f7501dc82c5e8237792522a410d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:41:59 -0300 Subject: [PATCH 1860/2380] hw/ivshmem: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20180625124238.25339-8-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/misc/ivshmem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index ee01c5e66b..6febbabcaa 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -17,6 +17,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "hw/hw.h" @@ -1301,7 +1302,7 @@ static void ivshmem_realize(PCIDevice *dev, Error **errp) } if (s->sizearg == NULL) { - s->legacy_size = 4 << 20; /* 4 MB default */ + s->legacy_size = 4 * MiB; /* 4 MB default */ } else { int ret; uint64_t size; From a7174d7093989c0dec87bf5cd7ed152b63b42ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:00 -0300 Subject: [PATCH 1861/2380] hw/ipack: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alberto Garcia Message-Id: <20180625124238.25339-9-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/ipack/tpci200.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index da05c8589d..cd3e79139d 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -9,6 +9,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/ipack/ipack.h" #include "hw/pci/pci.h" #include "qemu/bitops.h" @@ -597,9 +598,9 @@ static void tpci200_realize(PCIDevice *pci_dev, Error **errp) memory_region_init_io(&s->las1, OBJECT(s), &tpci200_las1_ops, s, "tpci200_las1", 1024); memory_region_init_io(&s->las2, OBJECT(s), &tpci200_las2_ops, - s, "tpci200_las2", 1024*1024*32); + s, "tpci200_las2", 32 * MiB); memory_region_init_io(&s->las3, OBJECT(s), &tpci200_las3_ops, - s, "tpci200_las3", 1024*1024*16); + s, "tpci200_las3", 16 * MiB); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io); pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0); From 7e4626059824ab5d221f1048341b1951615956a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:01 -0300 Subject: [PATCH 1862/2380] hw/scsi: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-10-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 55a34b3895..32f3f96ff8 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -29,6 +29,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #endif #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/scsi/scsi.h" @@ -44,13 +45,13 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include #endif -#define SCSI_WRITE_SAME_MAX 524288 -#define SCSI_DMA_BUF_SIZE 131072 +#define SCSI_WRITE_SAME_MAX (512 * KiB) +#define SCSI_DMA_BUF_SIZE (128 * KiB) #define SCSI_MAX_INQUIRY_LEN 256 #define SCSI_MAX_MODE_LEN 256 -#define DEFAULT_DISCARD_GRANULARITY 4096 -#define DEFAULT_MAX_UNMAP_SIZE (1 << 30) /* 1 GB */ +#define DEFAULT_DISCARD_GRANULARITY (4 * KiB) +#define DEFAULT_MAX_UNMAP_SIZE (1 * GiB) #define DEFAULT_MAX_IO_SIZE INT_MAX /* 2 GB - 1 block */ #define TYPE_SCSI_DISK_BASE "scsi-disk-base" From 968dfd0516728c9b284f53d22e96637ca543f699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:02 -0300 Subject: [PATCH 1863/2380] hw/smbios: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-11-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/smbios/smbios.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 27a07e96f4..a27e54b2fa 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -16,6 +16,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -625,10 +626,6 @@ static void smbios_build_type_11_table(void) SMBIOS_BUILD_TABLE_POST; } -#define ONE_KB ((ram_addr_t)1 << 10) -#define ONE_MB ((ram_addr_t)1 << 20) -#define ONE_GB ((ram_addr_t)1 << 30) - #define MAX_T16_STD_SZ 0x80000000 /* 2T in Kilobytes */ static void smbios_build_type_16_table(unsigned dimm_cnt) @@ -640,7 +637,7 @@ static void smbios_build_type_16_table(unsigned dimm_cnt) t->location = 0x01; /* Other */ t->use = 0x03; /* System memory */ t->error_correction = 0x06; /* Multi-bit ECC (for Microsoft, per SeaBIOS) */ - size_kb = QEMU_ALIGN_UP(ram_size, ONE_KB) / ONE_KB; + size_kb = QEMU_ALIGN_UP(ram_size, KiB) / KiB; if (size_kb < MAX_T16_STD_SZ) { t->maximum_capacity = cpu_to_le32(size_kb); t->extended_maximum_capacity = cpu_to_le64(0); @@ -668,7 +665,7 @@ static void smbios_build_type_17_table(unsigned instance, uint64_t size) t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ t->total_width = cpu_to_le16(0xFFFF); /* Unknown */ t->data_width = cpu_to_le16(0xFFFF); /* Unknown */ - size_mb = QEMU_ALIGN_UP(size, ONE_MB) / ONE_MB; + size_mb = QEMU_ALIGN_UP(size, MiB) / MiB; if (size_mb < MAX_T17_STD_SZ) { t->size = cpu_to_le16(size_mb); t->extended_size = cpu_to_le32(0); @@ -707,8 +704,8 @@ static void smbios_build_type_19_table(unsigned instance, end = start + size - 1; assert(end > start); - start_kb = start / ONE_KB; - end_kb = end / ONE_KB; + start_kb = start / KiB; + end_kb = end / KiB; if (start_kb < UINT32_MAX && end_kb < UINT32_MAX) { t->starting_address = cpu_to_le32(start_kb); t->ending_address = cpu_to_le32(end_kb); @@ -869,7 +866,7 @@ void smbios_get_tables(const struct smbios_phys_mem_area *mem_array, smbios_build_type_11_table(); -#define MAX_DIMM_SZ (16ll * ONE_GB) +#define MAX_DIMM_SZ (16 * GiB) #define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \ : ((ram_size - 1) % MAX_DIMM_SZ) + 1) From 8f951a13f0dd801dcf95c822091a7db49b323ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:03 -0300 Subject: [PATCH 1864/2380] hw/xen: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alan Robinson Message-Id: <20180625124238.25339-12-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/block/xen_disk.c | 3 ++- hw/i386/xen/xen-mapcache.c | 3 ++- hw/xenpv/xen_domainbuild.c | 13 +++++++------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 9fbc0cdb87..36eff94f84 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -20,6 +20,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include #include @@ -814,7 +815,7 @@ static int blk_connect(struct XenDevice *xendev) xen_pv_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," " size %" PRId64 " (%" PRId64 " MB)\n", blkdev->type, blkdev->fileproto, blkdev->filename, - blkdev->file_size, blkdev->file_size >> 20); + blkdev->file_size, blkdev->file_size / MiB); /* Fill in number of sector size and number of sectors */ xenstore_write_be_int(xendev, "sector-size", blkdev->file_blk); diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index 628b813a11..4e4f069a24 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -9,6 +9,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include @@ -46,7 +47,7 @@ * From empirical tests I observed that qemu use 75MB more than the * max_mcache_size. */ -#define NON_MCACHE_MEMORY_SIZE (80 * 1024 * 1024) +#define NON_MCACHE_MEMORY_SIZE (80 * MiB) typedef struct MapCacheEntry { hwaddr paddr_index; diff --git a/hw/xenpv/xen_domainbuild.c b/hw/xenpv/xen_domainbuild.c index 027f76fad1..188acaca16 100644 --- a/hw/xenpv/xen_domainbuild.c +++ b/hw/xenpv/xen_domainbuild.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/xen/xen_backend.h" #include "xen_domainbuild.h" #include "qemu/timer.h" @@ -75,9 +76,9 @@ int xenstore_domain_init1(const char *kernel, const char *ramdisk, xenstore_write_str(dom, "vm", vm); /* memory */ - xenstore_write_int(dom, "memory/target", ram_size >> 10); // kB - xenstore_write_int(vm, "memory", ram_size >> 20); // MB - xenstore_write_int(vm, "maxmem", ram_size >> 20); // MB + xenstore_write_int(dom, "memory/target", ram_size / KiB); + xenstore_write_int(vm, "memory", ram_size / MiB); + xenstore_write_int(vm, "maxmem", ram_size / MiB); /* cpus */ for (i = 0; i < smp_cpus; i++) { @@ -113,7 +114,7 @@ int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, /* console */ xenstore_write_str(dom, "console/type", "ioemu"); - xenstore_write_int(dom, "console/limit", 128 * 1024); + xenstore_write_int(dom, "console/limit", 128 * KiB); xenstore_write_int(dom, "console/ring-ref", console_mfn); xenstore_write_int(dom, "console/port", console_port); xen_config_dev_console(0); @@ -260,7 +261,7 @@ int xen_domain_build_pv(const char *kernel, const char *ramdisk, } #endif - rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size >> 10); + rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size / KiB); if (rc < 0) { fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n"); goto err; @@ -269,7 +270,7 @@ int xen_domain_build_pv(const char *kernel, const char *ramdisk, xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); - rc = xc_linux_build(xen_xc, xen_domid, ram_size >> 20, + rc = xc_linux_build(xen_xc, xen_domid, ram_size / MiB, kernel, ramdisk, cmdline, 0, flags, xenstore_port, &xenstore_mfn, From 3d779b93e0a26e81066f8afc96e2d3d37d6ce1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:04 -0300 Subject: [PATCH 1865/2380] hw/tpm: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20180625124238.25339-13-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- include/hw/acpi/tpm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h index 46ac4dc581..3580ffd50c 100644 --- a/include/hw/acpi/tpm.h +++ b/include/hw/acpi/tpm.h @@ -16,6 +16,7 @@ #ifndef HW_ACPI_TPM_H #define HW_ACPI_TPM_H +#include "qemu/units.h" #include "hw/registerfields.h" #define TPM_TIS_ADDR_BASE 0xFED40000 @@ -176,7 +177,7 @@ REG32(CRB_DATA_BUFFER, 0x80) #define TPM_CRB_ADDR_CTRL (TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ) #define TPM_CRB_R_MAX R_CRB_DATA_BUFFER -#define TPM_LOG_AREA_MINIMUM_SIZE (64 * 1024) +#define TPM_LOG_AREA_MINIMUM_SIZE (64 * KiB) #define TPM_TCPA_ACPI_CLASS_CLIENT 0 #define TPM_TCPA_ACPI_CLASS_SERVER 1 From e8400cf3856a6de77d6080dfc9c84f226006f07d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:05 -0300 Subject: [PATCH 1866/2380] hw/block: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-14-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/block/m25p80.c | 5 +++-- hw/block/nvme.c | 3 ++- hw/block/tc58128.c | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index b0ed8fa418..e8dfa14b33 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -22,6 +22,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "sysemu/block-backend.h" #include "hw/ssi/ssi.h" @@ -541,12 +542,12 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) switch (cmd) { case ERASE_4K: case ERASE4_4K: - len = 4 << 10; + len = 4 * KiB; capa_to_assert = ER_4K; break; case ERASE_32K: case ERASE4_32K: - len = 32 << 10; + len = 32 * KiB; capa_to_assert = ER_32K; break; case ERASE_SECTOR: diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 156ecf3c41..fc7dacb816 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/block/block.h" #include "hw/hw.h" #include "hw/pci/msix.h" @@ -649,7 +650,7 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c) static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c) { - static const int data_len = 4096; + static const int data_len = 4 * KiB; uint32_t min_nsid = le32_to_cpu(c->nsid); uint64_t prp1 = le64_to_cpu(c->prp1); uint64_t prp2 = le64_to_cpu(c->prp2); diff --git a/hw/block/tc58128.c b/hw/block/tc58128.c index 1d9f7ee000..808ad76ba6 100644 --- a/hw/block/tc58128.c +++ b/hw/block/tc58128.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/sh4/sh.h" #include "hw/loader.h" @@ -26,7 +27,7 @@ typedef struct { static tc58128_dev tc58128_devs[2]; -#define FLASH_SIZE (16*1024*1024) +#define FLASH_SIZE (16 * MiB) static void init_dev(tc58128_dev * dev, const char *filename) { From f0353b0d10dcb2a58372fe70af09c09195ec6098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:06 -0300 Subject: [PATCH 1867/2380] hw/display: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Gerd Hoffmann Reviewed-by: Alistair Francis Message-Id: <20180625124238.25339-15-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/display/bochs-display.c | 9 +++++---- hw/display/cirrus_vga.c | 10 +++++----- hw/display/g364fb.c | 4 ++-- hw/display/qxl.c | 30 ++++++++++++++---------------- hw/display/vga-isa-mm.c | 5 +++-- hw/display/vga.c | 5 +++-- hw/display/virtio-gpu.c | 4 ++-- hw/display/vmware_vga.c | 3 ++- hw/display/xenfb.c | 3 ++- include/hw/display/xlnx_dp.h | 5 +++-- 10 files changed, 41 insertions(+), 37 deletions(-) diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 1187d77576..5e0c1f1914 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -5,6 +5,7 @@ * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/display/bochs-vbe.h" @@ -70,7 +71,7 @@ static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr, case VBE_DISPI_INDEX_ID: return VBE_DISPI_ID5; case VBE_DISPI_INDEX_VIDEO_MEMORY_64K: - return s->vgamem / (64 * 1024); + return s->vgamem / (64 * KiB); } if (index >= ARRAY_SIZE(s->vbe_regs)) { @@ -258,10 +259,10 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp) s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s); - if (s->vgamem < (4 * 1024 * 1024)) { + if (s->vgamem < 4 * MiB) { error_setg(errp, "bochs-display: video memory too small"); } - if (s->vgamem > (256 * 1024 * 1024)) { + if (s->vgamem > 256 * MiB) { error_setg(errp, "bochs-display: video memory too big"); } s->vgamem = pow2ceil(s->vgamem); @@ -323,7 +324,7 @@ static void bochs_display_exit(PCIDevice *dev) } static Property bochs_display_properties[] = { - DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * 1024 * 1024), + DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 138ae961b9..5e44f00f3f 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -27,6 +27,7 @@ * available at http://home.worldonline.dk/~finth/ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "trace.h" #include "hw/hw.h" @@ -2218,7 +2219,7 @@ static inline void cirrus_cursor_compute_yrange(CirrusVGAState *s) uint32_t content; int y, y_min, y_max; - src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024; + src = s->vga.vram_ptr + s->real_vram_size - 16 * KiB; if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) { src += (s->vga.sr[0x13] & 0x3c) * 256; y_min = 64; @@ -2347,7 +2348,7 @@ static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y) return; } - src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024; + src = s->vga.vram_ptr + s->real_vram_size - 16 * KiB; if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) { src += (s->vga.sr[0x13] & 0x3c) * 256; src += (scr_y - s->vga.hw_cursor_y) * 16; @@ -2995,8 +2996,7 @@ static void cirrus_init_common(CirrusVGAState *s, Object *owner, /* I/O handler for LFB */ memory_region_init_io(&s->cirrus_linear_io, owner, &cirrus_linear_io_ops, s, - "cirrus-linear-io", s->vga.vram_size_mb - * 1024 * 1024); + "cirrus-linear-io", s->vga.vram_size_mb * MiB); memory_region_set_flush_coalesced(&s->cirrus_linear_io); /* I/O handler for LFB */ @@ -3013,7 +3013,7 @@ static void cirrus_init_common(CirrusVGAState *s, Object *owner, memory_region_set_flush_coalesced(&s->cirrus_mmio_io); s->real_vram_size = - (s->device_id == CIRRUS_ID_CLGD5446) ? 4096 * 1024 : 2048 * 1024; + (s->device_id == CIRRUS_ID_CLGD5446) ? 4 * MiB : 2 * MiB; /* XXX: s->vga.vram_size must be a power of two */ s->cirrus_addr_mask = s->real_vram_size - 1; diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index 3d75394e77..fbc2b2422d 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "qemu/error-report.h" #include "ui/console.h" @@ -510,8 +511,7 @@ static void g364fb_sysbus_reset(DeviceState *d) } static Property g364fb_sysbus_properties[] = { - DEFINE_PROP_UINT32("vram_size", G364SysBusState, g364.vram_size, - 8 * 1024 * 1024), + DEFINE_PROP_UINT32("vram_size", G364SysBusState, g364.vram_size, 8 * MiB), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/qxl.c b/hw/display/qxl.c index a71714ccb4..b09a03997a 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include #include "qapi/error.h" @@ -2012,11 +2013,11 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl) if (qxl->vgamem_size_mb > 256) { qxl->vgamem_size_mb = 256; } - qxl->vgamem_size = qxl->vgamem_size_mb * 1024 * 1024; + qxl->vgamem_size = qxl->vgamem_size_mb * MiB; /* vga ram (bar 0, total) */ if (qxl->ram_size_mb != -1) { - qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024; + qxl->vga.vram_size = qxl->ram_size_mb * MiB; } if (qxl->vga.vram_size < qxl->vgamem_size * 2) { qxl->vga.vram_size = qxl->vgamem_size * 2; @@ -2024,7 +2025,7 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl) /* vram32 (surfaces, 32bit, bar 1) */ if (qxl->vram32_size_mb != -1) { - qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024; + qxl->vram32_size = qxl->vram32_size_mb * MiB; } if (qxl->vram32_size < 4096) { qxl->vram32_size = 4096; @@ -2032,7 +2033,7 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl) /* vram (surfaces, 64bit, bar 4+5) */ if (qxl->vram_size_mb != -1) { - qxl->vram_size = (uint64_t)qxl->vram_size_mb * 1024 * 1024; + qxl->vram_size = (uint64_t)qxl->vram_size_mb * MiB; } if (qxl->vram_size < qxl->vram32_size) { qxl->vram_size = qxl->vram32_size; @@ -2134,13 +2135,12 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) } /* print pci bar details */ - dprint(qxl, 1, "ram/%s: %d MB [region 0]\n", - qxl->id == 0 ? "pri" : "sec", - qxl->vga.vram_size / (1024*1024)); - dprint(qxl, 1, "vram/32: %" PRIx64 "d MB [region 1]\n", - qxl->vram32_size / (1024*1024)); - dprint(qxl, 1, "vram/64: %" PRIx64 "d MB %s\n", - qxl->vram_size / (1024*1024), + dprint(qxl, 1, "ram/%s: %" PRId64 " MB [region 0]\n", + qxl->id == 0 ? "pri" : "sec", qxl->vga.vram_size / MiB); + dprint(qxl, 1, "vram/32: %" PRIx64 " MB [region 1]\n", + qxl->vram32_size / MiB); + dprint(qxl, 1, "vram/64: %" PRIx64 " MB %s\n", + qxl->vram_size / MiB, qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]"); qxl->ssd.qxl.base.sif = &qxl_interface.base; @@ -2167,7 +2167,7 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp) qxl->id = 0; qxl_init_ramsize(qxl); vga->vbe_size = qxl->vgamem_size; - vga->vram_size_mb = qxl->vga.vram_size >> 20; + vga->vram_size_mb = qxl->vga.vram_size / MiB; vga_common_init(vga, OBJECT(dev), true); vga_init(vga, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), false); @@ -2391,10 +2391,8 @@ static VMStateDescription qxl_vmstate = { }; static Property qxl_properties[] = { - DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT64("vram_size", PCIQXLDevice, vram32_size, - 64 * 1024 * 1024), + DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * MiB), + DEFINE_PROP_UINT64("vram_size", PCIQXLDevice, vram32_size, 64 * MiB), DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, QXL_DEFAULT_REVISION), DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c index e887b45651..bd58141117 100644 --- a/hw/display/vga-isa-mm.c +++ b/hw/display/vga-isa-mm.c @@ -22,12 +22,13 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/display/vga.h" #include "vga_int.h" #include "ui/pixel_ops.h" -#define VGA_RAM_SIZE (8192 * 1024) +#define VGA_RAM_SIZE (8 * MiB) typedef struct ISAVGAMMState { VGACommonState vga; @@ -130,7 +131,7 @@ int isa_vga_mm_init(hwaddr vram_base, s = g_malloc0(sizeof(*s)); - s->vga.vram_size_mb = VGA_RAM_SIZE >> 20; + s->vga.vram_size_mb = VGA_RAM_SIZE / MiB; vga_common_init(&s->vga, NULL, true); vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space); diff --git a/hw/display/vga.c b/hw/display/vga.c index ed476e4e80..d7599182a8 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/display/vga.h" @@ -721,7 +722,7 @@ uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr) val = s->vbe_regs[s->vbe_index]; } } else if (s->vbe_index == VBE_DISPI_INDEX_VIDEO_MEMORY_64K) { - val = s->vbe_size / (64 * 1024); + val = s->vbe_size / (64 * KiB); } else { val = 0; } @@ -2192,7 +2193,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) s->vram_size_mb = uint_clamp(s->vram_size_mb, 1, 512); s->vram_size_mb = pow2ceil(s->vram_size_mb); - s->vram_size = s->vram_size_mb << 20; + s->vram_size = s->vram_size_mb * MiB; if (!s->vbe_size) { s->vbe_size = s->vram_size; diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 2dd3c3481a..71a00718e6 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "qemu/iov.h" #include "ui/console.h" @@ -1314,8 +1315,7 @@ static const VMStateDescription vmstate_virtio_gpu = { static Property virtio_gpu_properties[] = { DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1), - DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf.max_hostmem, - 256 * 1024 * 1024), + DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf.max_hostmem, 256 * MiB), #ifdef CONFIG_VIRGL DEFINE_PROP_BIT("virgl", VirtIOGPU, conf.flags, VIRTIO_GPU_FLAG_VIRGL_ENABLED, true), diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index bd3e8b3586..08deb08783 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/loader.h" @@ -565,7 +566,7 @@ static inline int vmsvga_fifo_length(struct vmsvga_state_s *s) s->fifo_next >= SVGA_FIFO_SIZE) { return 0; } - if (s->fifo_max < s->fifo_min + 10 * 1024) { + if (s->fifo_max < s->fifo_min + 10 * KiB) { return 0; } diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 911291c5c3..0330dc6f61 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -25,6 +25,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "ui/input.h" @@ -889,7 +890,7 @@ static int fb_initialise(struct XenDevice *xendev) return rc; fb_page = fb->c.page; - rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U, + rc = xenfb_configure_fb(fb, videoram * MiB, fb_page->width, fb_page->height, fb_page->depth, fb_page->mem_length, 0, fb_page->line_length); if (rc != 0) diff --git a/include/hw/display/xlnx_dp.h b/include/hw/display/xlnx_dp.h index ee046a5fac..26b759cd44 100644 --- a/include/hw/display/xlnx_dp.h +++ b/include/hw/display/xlnx_dp.h @@ -29,14 +29,15 @@ #include "hw/display/dpcd.h" #include "hw/i2c/i2c-ddc.h" #include "qemu/fifo8.h" +#include "qemu/units.h" #include "hw/dma/xlnx_dpdma.h" #include "audio/audio.h" #ifndef XLNX_DP_H #define XLNX_DP_H -#define AUD_CHBUF_MAX_DEPTH 32768 -#define MAX_QEMU_BUFFER_SIZE 4096 +#define AUD_CHBUF_MAX_DEPTH (32 * KiB) +#define MAX_QEMU_BUFFER_SIZE (4 * KiB) #define DP_CORE_REG_ARRAY_SIZE (0x3AF >> 2) #define DP_AVBUF_REG_ARRAY_SIZE (0x238 >> 2) From de9b602ebd7c3a2e22b79114478d1a3c43bdfb4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:07 -0300 Subject: [PATCH 1868/2380] hw/misc: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-16-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/misc/auxbus.c | 3 ++- hw/misc/edu.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index b4cacd664b..b8a8721201 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -27,6 +27,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/log.h" #include "hw/misc/auxbus.h" #include "hw/i2c/i2c.h" @@ -68,7 +69,7 @@ AUXBus *aux_init_bus(DeviceState *parent, const char *name) /* Memory related. */ bus->aux_io = g_malloc(sizeof(*bus->aux_io)); - memory_region_init(bus->aux_io, OBJECT(bus), "aux-io", (1 << 20)); + memory_region_init(bus->aux_io, OBJECT(bus), "aux-io", 1 * MiB); address_space_init(&bus->aux_addr_space, bus->aux_io, "aux-io"); return bus; } diff --git a/hw/misc/edu.c b/hw/misc/edu.c index 34eb05d213..df26a4d046 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/pci/pci.h" #include "hw/pci/msi.h" #include "qemu/timer.h" @@ -357,7 +358,7 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp) edu, QEMU_THREAD_JOINABLE); memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu, - "edu-mmio", 1 << 20); + "edu-mmio", 1 * MiB); pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio); } From 4bf46af78b7ce205f8ce1d75b70c95d7726e0a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:08 -0300 Subject: [PATCH 1869/2380] hw/riscv: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Michael Clark Message-Id: <20180625124238.25339-17-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/riscv/virt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index ad03113e0f..34d48993a2 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/log.h" #include "qemu/error-report.h" #include "qapi/error.h" @@ -84,7 +85,7 @@ static hwaddr load_initrd(const char *filename, uint64_t mem_size, * halfway into RAM, and for boards with 256MB of RAM or more we put * the initrd at 128MB. */ - *start = kernel_entry + MIN(mem_size / 2, 128 * 1024 * 1024); + *start = kernel_entry + MIN(mem_size / 2, 128 * MiB); size = load_ramdisk(filename, *start, mem_size - *start); if (size == -1) { From 4dab9c731cd930419bfec83207fcf725f8738804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:09 -0300 Subject: [PATCH 1870/2380] hw/m68k: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20180625124238.25339-18-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/m68k/mcf5208.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index ae3dcc98c3..0f2245dd81 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -6,6 +6,7 @@ * This code is licensed under the GPL */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu-common.h" @@ -241,7 +242,7 @@ static void mcf5208evb_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0x40000000, ram); /* Internal SRAM. */ - memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384, &error_fatal); + memory_region_init_ram(sram, NULL, "mcf5208.sram", 16 * KiB, &error_fatal); memory_region_add_subregion(address_space_mem, 0x80000000, sram); /* Internal peripherals. */ From 0a2e467bcebfed27bd0d1cd17992938e8a3fbb66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:10 -0300 Subject: [PATCH 1871/2380] hw/sparc: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-19-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/sparc/leon3.c | 10 ++++++---- hw/sparc/sun4m.c | 10 +++++----- hw/sparc64/niagara.c | 3 ++- hw/sparc64/sun4u.c | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 98fa6adae0..fa98ab8177 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu-common.h" @@ -139,9 +140,10 @@ static void leon3_generic_hw_init(MachineState *machine) env->qemu_irq_ack = leon3_irq_manager; /* Allocate RAM */ - if ((uint64_t)ram_size > (1UL << 30)) { - error_report("Too much memory for this machine: %d, maximum 1G", - (unsigned int)(ram_size / (1024 * 1024))); + if (ram_size > 1 * GiB) { + error_report("Too much memory for this machine: %" PRId64 "MB," + " maximum 1G", + ram_size / MiB); exit(1); } @@ -149,7 +151,7 @@ static void leon3_generic_hw_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0x40000000, ram); /* Allocate BIOS */ - prom_size = 8 * 1024 * 1024; /* 8Mb */ + prom_size = 8 * MiB; memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_fatal); memory_region_set_readonly(prom, true); memory_region_add_subregion(address_space_mem, 0x00000000, prom); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index b984d2da0e..a9d477572e 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -45,7 +46,6 @@ #include "hw/loader.h" #include "elf.h" #include "trace.h" -#include "qemu/cutils.h" /* * Sun4m architecture was used in the following machines: @@ -66,7 +66,7 @@ #define KERNEL_LOAD_ADDR 0x00004000 #define CMDLINE_ADDR 0x007ff000 #define INITRD_LOAD_ADDR 0x00800000 -#define PROM_SIZE_MAX (1024 * 1024) +#define PROM_SIZE_MAX (1 * MiB) #define PROM_VADDR 0xffd00000 #define PROM_FILENAME "openbios-sparc32" #define CFG_ADDR 0xd00000510ULL @@ -774,9 +774,9 @@ static void ram_init(hwaddr addr, ram_addr_t RAM_size, /* allocate RAM */ if ((uint64_t)RAM_size > max_mem) { - error_report("Too much memory for this machine: %d, maximum %d", - (unsigned int)(RAM_size / (1024 * 1024)), - (unsigned int)(max_mem / (1024 * 1024))); + error_report("Too much memory for this machine: %" PRId64 "," + " maximum %" PRId64, + RAM_size / MiB, max_mem / MiB); exit(1); } dev = qdev_create(NULL, "memory"); diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 22c4655fde..4fa8cb2904 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "cpu.h" #include "hw/hw.h" @@ -84,7 +85,7 @@ typedef struct NiagaraBoardState { #define NIAGARA_PROM_BASE 0xfff0000000ULL #define NIAGARA_Q_OFFSET 0x10000ULL #define NIAGARA_OBP_OFFSET 0x80000ULL -#define PROM_SIZE_MAX (4 * 1024 * 1024) +#define PROM_SIZE_MAX (4 * MiB) static void add_rom_or_fail(const char *file, const hwaddr addr) { diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 3975a7b65a..97d1269346 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu-common.h" @@ -52,11 +53,10 @@ #include "hw/loader.h" #include "elf.h" #include "trace.h" -#include "qemu/cutils.h" #define KERNEL_LOAD_ADDR 0x00404000 #define CMDLINE_ADDR 0x003ff000 -#define PROM_SIZE_MAX (4 * 1024 * 1024) +#define PROM_SIZE_MAX (4 * MiB) #define PROM_VADDR 0x000ffd00000ULL #define PBM_SPECIAL_BASE 0x1fe00000000ULL #define PBM_MEM_BASE 0x1ff00000000ULL From 393fc4c740d8d83d45bdbcd5f6a4cbc6be09b600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:11 -0300 Subject: [PATCH 1872/2380] hw/s390x: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Acked-by: Cornelia Huck Message-Id: <20180625124238.25339-20-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/s390x/s390-skeys.c | 3 ++- hw/s390x/s390-stattrib.c | 3 ++- hw/s390x/sclp.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 76241c240e..15f7ab0e53 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -10,6 +10,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/boards.h" #include "hw/s390x/storage-keys.h" #include "qapi/error.h" @@ -19,7 +20,7 @@ #include "sysemu/kvm.h" #include "migration/register.h" -#define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */ +#define S390_SKEYS_BUFFER_SIZE (128 * KiB) /* Room for 128k storage keys */ #define S390_SKEYS_SAVE_FLAG_EOS 0x01 #define S390_SKEYS_SAVE_FLAG_SKEYS 0x02 #define S390_SKEYS_SAVE_FLAG_ERROR 0x04 diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 70b95550a8..5161a1659b 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -10,6 +10,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/boards.h" #include "cpu.h" #include "migration/qemu-file.h" @@ -20,7 +21,7 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" -#define CMMA_BLOCK_SIZE (1 << 10) +#define CMMA_BLOCK_SIZE (1 * KiB) #define STATTR_FLAG_EOS 0x01ULL #define STATTR_FLAG_MORE 0x02ULL diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 047d577313..bd2a024efd 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -13,6 +13,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" #include "sysemu/sysemu.h" @@ -289,7 +290,7 @@ static void sclp_realize(DeviceState *dev, Error **errp) ret = s390_set_memory_limit(machine->maxram_size, &hw_limit); if (ret == -E2BIG) { error_setg(&err, "host supports a maximum of %" PRIu64 " GB", - hw_limit >> 30); + hw_limit / GiB); } else if (ret) { error_setg(&err, "setting the guest size failed"); } From c108cc59dc4df978a7f4e7f34f2463b232522954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:11 -0300 Subject: [PATCH 1873/2380] hw/hppa: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Acked-by: Cornelia Huck Message-Id: <20180625124238.25339-20-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/hppa/dino.c | 3 ++- hw/hppa/machine.c | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c index 26f2704cd5..564b938e3a 100644 --- a/hw/hppa/dino.c +++ b/hw/hppa/dino.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" #include "hw/hw.h" @@ -76,7 +77,7 @@ /* #define xxx 0x200 - bit 9 not used */ #define RS232INT 0x400 -#define DINO_MEM_CHUNK_SIZE (8 * 1024 * 1024) /* 8MB */ +#define DINO_MEM_CHUNK_SIZE (8 * MiB) #define DINO_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(DinoState, (obj), TYPE_DINO_PCI_HOST_BRIDGE) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 2ba26962f9..cf7c61c6cc 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -17,7 +17,7 @@ #include "hw/timer/i8254.h" #include "hw/char/serial.h" #include "hppa_sys.h" -#include "qemu/cutils.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/log.h" @@ -178,8 +178,8 @@ static void machine_hppa_init(MachineState *machine) } qemu_log_mask(CPU_LOG_PAGE, "Kernel loaded at 0x%08" PRIx64 "-0x%08" PRIx64 ", entry at 0x%08" PRIx64 - ", size %ld kB.\n", - kernel_low, kernel_high, kernel_entry, size / 1024); + ", size %" PRIu64 " kB\n", + kernel_low, kernel_high, kernel_entry, size / KiB); if (kernel_cmdline) { cpu[0]->env.gr[24] = 0x4000; @@ -203,8 +203,8 @@ static void machine_hppa_init(MachineState *machine) (1) Due to sign-extension problems and PDC, put the initrd no higher than 1G. (2) Reserve 64k for stack. */ - initrd_base = MIN(ram_size, 1024 * 1024 * 1024); - initrd_base = initrd_base - 64 * 1024; + initrd_base = MIN(ram_size, 1 * GiB); + initrd_base = initrd_base - 64 * KiB; initrd_base = (initrd_base - initrd_size) & TARGET_PAGE_MASK; if (initrd_base < kernel_high) { From b941329dc41bf677ab9f32b4ac9dabd20f4376b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:13 -0300 Subject: [PATCH 1874/2380] hw/xtensa: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ $ git grep -n '[<>][<>]= ?[1-5]0' and modified manually. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Max Filippov Message-Id: <20180625124238.25339-22-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/xtensa/xtfpga.c | 9 +++++---- target/xtensa/helper.c | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 5dc13034f9..b3161de320 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" #include "sysemu/sysemu.h" @@ -152,7 +153,7 @@ static void xtfpga_net_init(MemoryRegion *address_space, sysbus_mmio_get_region(s, 1)); ram = g_malloc(sizeof(*ram)); - memory_region_init_ram_nomigrate(ram, OBJECT(s), "open_eth.ram", 16384, + memory_region_init_ram_nomigrate(ram, OBJECT(s), "open_eth.ram", 16 * KiB, &error_fatal); vmstate_register_ram_global(ram); memory_region_add_subregion(address_space, buffers, ram); @@ -229,7 +230,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) const char *kernel_cmdline = qemu_opt_get(machine_opts, "append"); const char *dtb_filename = qemu_opt_get(machine_opts, "dtb"); const char *initrd_filename = qemu_opt_get(machine_opts, "initrd"); - const unsigned system_io_size = 224 * 1024 * 1024; + const unsigned system_io_size = 224 * MiB; int n; for (n = 0; n < smp_cpus; n++) { @@ -342,7 +343,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) cpu_physical_memory_write(cur_lowmem, fdt, fdt_size); cur_tagptr = put_tag(cur_tagptr, BP_TAG_FDT, sizeof(dtb_addr), &dtb_addr); - cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + fdt_size, 4096); + cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + fdt_size, 4 * KiB); } #else if (dtb_filename) { @@ -370,7 +371,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) initrd_location.end = tswap32(cur_lowmem + initrd_size); cur_tagptr = put_tag(cur_tagptr, BP_TAG_INITRD, sizeof(initrd_location), &initrd_location); - cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + initrd_size, 4096); + cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + initrd_size, 4 * KiB); } cur_tagptr = put_tag(cur_tagptr, BP_TAG_LAST, 0, NULL); env->regs[2] = tagptr; diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index 34844eead3..c9a6132700 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" @@ -726,10 +727,10 @@ static void dump_tlb(FILE *f, fprintf_function cpu_fprintf, bool print_header = true; if (sz >= 0x100000) { - sz >>= 20; + sz /= MiB; sz_text = "MB"; } else { - sz >>= 10; + sz /= KiB; sz_text = "KB"; } From 2b41742a8d852cb07c58b2682851fa2cc5e50bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:14 -0300 Subject: [PATCH 1875/2380] hw/alpha: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-23-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/alpha/typhoon.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index d3ed7cdbe8..d74b5b55e1 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" #include "hw/hw.h" @@ -813,8 +814,6 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, qemu_irq *p_rtc_irq, AlphaCPU *cpus[4], pci_map_irq_fn sys_map_irq) { - const uint64_t MB = 1024 * 1024; - const uint64_t GB = 1024 * MB; MemoryRegion *addr_space = get_system_memory(); DeviceState *dev; TyphoonState *s; @@ -855,30 +854,30 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, /* Pchip0 CSRs, 0x801.8000.0000, 256MB. */ memory_region_init_io(&s->pchip.region, OBJECT(s), &pchip_ops, s, "pchip0", - 256*MB); + 256 * MiB); memory_region_add_subregion(addr_space, 0x80180000000ULL, &s->pchip.region); /* Cchip CSRs, 0x801.A000.0000, 256MB. */ memory_region_init_io(&s->cchip.region, OBJECT(s), &cchip_ops, s, "cchip0", - 256*MB); + 256 * MiB); memory_region_add_subregion(addr_space, 0x801a0000000ULL, &s->cchip.region); /* Dchip CSRs, 0x801.B000.0000, 256MB. */ memory_region_init_io(&s->dchip_region, OBJECT(s), &dchip_ops, s, "dchip0", - 256*MB); + 256 * MiB); memory_region_add_subregion(addr_space, 0x801b0000000ULL, &s->dchip_region); /* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */ - memory_region_init(&s->pchip.reg_mem, OBJECT(s), "pci0-mem", 4*GB); + memory_region_init(&s->pchip.reg_mem, OBJECT(s), "pci0-mem", 4 * GiB); memory_region_add_subregion(addr_space, 0x80000000000ULL, &s->pchip.reg_mem); /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */ memory_region_init_io(&s->pchip.reg_io, OBJECT(s), &alpha_pci_ignore_ops, - NULL, "pci0-io", 32*MB); + NULL, "pci0-io", 32 * MiB); memory_region_add_subregion(addr_space, 0x801fc000000ULL, &s->pchip.reg_io); @@ -899,13 +898,13 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ memory_region_init_io(&s->pchip.reg_iack, OBJECT(s), &alpha_pci_iack_ops, - b, "pci0-iack", 64*MB); + b, "pci0-iack", 64 * MiB); memory_region_add_subregion(addr_space, 0x801f8000000ULL, &s->pchip.reg_iack); /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */ memory_region_init_io(&s->pchip.reg_conf, OBJECT(s), &alpha_pci_conf1_ops, - b, "pci0-conf", 16*MB); + b, "pci0-conf", 16 * MiB); memory_region_add_subregion(addr_space, 0x801fe000000ULL, &s->pchip.reg_conf); From b000325a3ab522339bb1122bf78ee5cae1217749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:15 -0300 Subject: [PATCH 1876/2380] hw/tricore: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Bastian Koppelmann Message-Id: <20180625124238.25339-24-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/tricore/tricore_testboard.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index 8e61dfc3e6..a58096f05e 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -72,17 +73,17 @@ static void tricore_testboard_init(MachineState *machine, int board_id) cpu = TRICORE_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", - 2 * 1024 * 1024, &error_fatal); + 2 * MiB, &error_fatal); memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", - 4 * 1024 * 1024, &error_fatal); - memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48 * 1024, + 4 * MiB, &error_fatal); + memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48 * KiB, &error_fatal); - memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48 * 1024, + memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48 * KiB, &error_fatal); memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", - 16 * 1024, &error_fatal); + 16 * KiB, &error_fatal); memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", - 32 * 1024, &error_fatal); + 32 * KiB, &error_fatal); memory_region_add_subregion(sysmem, 0x80000000, ext_cram); memory_region_add_subregion(sysmem, 0xa1000000, ext_dram); From a4fb331dab20e3da6f0189f0cbb02208673f7cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:16 -0300 Subject: [PATCH 1877/2380] hw/microblaze: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-25-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/microblaze/petalogix_ml605_mmu.c | 7 ++++--- hw/microblaze/petalogix_s3adsp1800_mmu.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 6c4a544eac..c730878d25 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -44,8 +45,8 @@ #include "hw/stream.h" -#define LMB_BRAM_SIZE (128 * 1024) -#define FLASH_SIZE (32 * 1024 * 1024) +#define LMB_BRAM_SIZE (128 * KiB) +#define FLASH_SIZE (32 * MiB) #define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb" @@ -109,7 +110,7 @@ petalogix_ml605_init(MachineState *machine) pflash_cfi01_register(FLASH_BASEADDR, NULL, "petalogix_ml605.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), FLASH_SIZE >> 16, + 64 * KiB, FLASH_SIZE >> 16, 2, 0x89, 0x18, 0x0000, 0x0, 0); diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 0da3e62102..5cf7b84c79 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -39,8 +40,8 @@ #include "boot.h" -#define LMB_BRAM_SIZE (128 * 1024) -#define FLASH_SIZE (16 * 1024 * 1024) +#define LMB_BRAM_SIZE (128 * KiB) +#define FLASH_SIZE (16 * MiB) #define BINARY_DEVICE_TREE_FILE "petalogix-s3adsp1800.dtb" @@ -87,7 +88,7 @@ petalogix_s3adsp1800_init(MachineState *machine) pflash_cfi01_register(FLASH_BASEADDR, NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), FLASH_SIZE >> 16, + 64 * KiB, FLASH_SIZE >> 16, 1, 0x89, 0x18, 0x0000, 0x0, 1); dev = qdev_create(NULL, "xlnx.xps-intc"); From a3c81ef934c1f60dd7aa3e71071492eda662facb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:17 -0300 Subject: [PATCH 1878/2380] hw/nios2: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-26-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/nios2/boot.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/nios2/boot.c b/hw/nios2/boot.c index 94f436e7fb..4bb5b601d3 100644 --- a/hw/nios2/boot.c +++ b/hw/nios2/boot.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "cpu.h" #include "qemu/option.h" @@ -38,7 +39,6 @@ #include "sysemu/sysemu.h" #include "hw/loader.h" #include "elf.h" -#include "qemu/cutils.h" #include "boot.h" @@ -177,7 +177,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, high = ddr_base + kernel_size; } - high = ROUND_UP(high, 1024 * 1024); + high = ROUND_UP(high, 1 * MiB); /* If initrd is available, it goes after the kernel, aligned to 1M. */ if (initrd_filename) { @@ -213,7 +213,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, high += fdt_size; /* Kernel command is at the end, 4k aligned. */ - boot_info.cmdline = ROUND_UP(high, 4096); + boot_info.cmdline = ROUND_UP(high, 4 * KiB); if (kernel_cmdline && strlen(kernel_cmdline)) { pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); } From a4ed5a3518a6e008625c552472d582fe99c2961d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:18 -0300 Subject: [PATCH 1879/2380] hw/cris: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-27-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/cris/axis_dev88.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index 56ee398ee5..191292eebf 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -242,7 +243,7 @@ static const MemoryRegionOps gpio_ops = { }, }; -#define INTMEM_SIZE (128 * 1024) +#define INTMEM_SIZE (128 * KiB) static struct cris_load_info li; From fc0187cb7b94d30c801d73b52eca7fe60b177043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:19 -0300 Subject: [PATCH 1880/2380] hw/lm32: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Michael Walle Message-Id: <20180625124238.25339-28-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/lm32/lm32_boards.c | 13 +++++++------ hw/lm32/milkymist.c | 10 +++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index 167058348e..fd8eccca14 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qemu-common.h" #include "cpu.h" @@ -87,10 +88,10 @@ static void lm32_evr_init(MachineState *machine) /* memory map */ hwaddr flash_base = 0x04000000; - size_t flash_sector_size = 256 * 1024; - size_t flash_size = 32 * 1024 * 1024; + size_t flash_sector_size = 256 * KiB; + size_t flash_size = 32 * MiB; hwaddr ram_base = 0x08000000; - size_t ram_size = 64 * 1024 * 1024; + size_t ram_size = 64 * MiB; hwaddr timer0_base = 0x80002000; hwaddr uart0_base = 0x80006000; hwaddr timer1_base = 0x8000a000; @@ -173,10 +174,10 @@ static void lm32_uclinux_init(MachineState *machine) /* memory map */ hwaddr flash_base = 0x04000000; - size_t flash_sector_size = 256 * 1024; - size_t flash_size = 32 * 1024 * 1024; + size_t flash_sector_size = 256 * KiB; + size_t flash_size = 32 * MiB; hwaddr ram_base = 0x08000000; - size_t ram_size = 64 * 1024 * 1024; + size_t ram_size = 64 * MiB; hwaddr uart0_base = 0x80000000; hwaddr timer0_base = 0x80002000; hwaddr timer1_base = 0x80010000; diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index c36bbc4ae2..321f184595 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qemu-common.h" #include "cpu.h" @@ -33,11 +34,10 @@ #include "milkymist-hw.h" #include "lm32.h" #include "exec/address-spaces.h" -#include "qemu/cutils.h" #define BIOS_FILENAME "mmone-bios.bin" #define BIOS_OFFSET 0x00860000 -#define BIOS_SIZE (512*1024) +#define BIOS_SIZE (512 * KiB) #define KERNEL_LOAD_ADDR 0x40000000 typedef struct { @@ -96,10 +96,10 @@ milkymist_init(MachineState *machine) /* memory map */ hwaddr flash_base = 0x00000000; - size_t flash_sector_size = 128 * 1024; - size_t flash_size = 32 * 1024 * 1024; + size_t flash_sector_size = 128 * KiB; + size_t flash_size = 32 * MiB; hwaddr sdram_base = 0x40000000; - size_t sdram_size = 128 * 1024 * 1024; + size_t sdram_size = 128 * MiB; hwaddr initrd_base = sdram_base + 0x1002000; hwaddr cmdline_base = sdram_base + 0x1000000; From e7dd191c92fb652331d4784a926b64d7095a4d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:20 -0300 Subject: [PATCH 1881/2380] hw/sh4: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-29-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/sh4/r2d.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 8fe8766eb9..6a5fc46a47 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -291,7 +292,7 @@ static void r2d_init(MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); pflash_cfi02_register(0x0, NULL, "r2d.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (16 * 1024), FLASH_SIZE >> 16, + 16 * KiB, FLASH_SIZE >> 16, 1, 4, 0x0000, 0x0000, 0x0000, 0x0000, 0x555, 0x2aa, 0); From 27773d8eeec83f78ed482fdd6031e35d99d2bb8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:21 -0300 Subject: [PATCH 1882/2380] hw/mips/r4k: Constify params_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-30-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/mips/mips_r4k.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index e5cf8ed1a3..fc38b4bf99 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -79,8 +79,9 @@ typedef struct ResetData { static int64_t load_kernel(void) { + const size_t params_size = 264; int64_t entry, kernel_high; - long kernel_size, initrd_size, params_size; + long kernel_size, initrd_size; ram_addr_t initrd_offset; uint32_t *params_buf; int big_endian; @@ -128,7 +129,6 @@ static int64_t load_kernel(void) } /* Store command line. */ - params_size = 264; params_buf = g_malloc(params_size); params_buf[0] = tswap32(ram_size); @@ -143,7 +143,7 @@ static int64_t load_kernel(void) } rom_add_blob_fixed("params", params_buf, params_size, - (16 << 20) - 264); + (16 << 20) - params_size); g_free(params_buf); return entry; From be01029e5df7c98cd4917fc8fe24845e0bed25f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:22 -0300 Subject: [PATCH 1883/2380] hw/mips: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ $ git grep -n '[<>][<>]= ?[1-5]0' and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-Id: <20180625124238.25339-31-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/mips/mips_fulong2e.c | 7 ++++--- hw/mips/mips_malta.c | 25 ++++++++++++++----------- hw/mips/mips_r4k.c | 11 ++++++----- hw/misc/mips_itu.c | 3 ++- hw/pci-host/xilinx-pcie.c | 5 +++-- include/hw/intc/mips_gic.h | 3 ++- include/hw/mips/bios.h | 3 ++- 7 files changed, 33 insertions(+), 24 deletions(-) diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 02fb2fdcc4..c1694c8254 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/i386/pc.h" @@ -159,7 +160,7 @@ static int64_t load_kernel (CPUMIPSState *env) /* Setup minimum environment variables */ prom_set(prom_buf, index++, "busclock=33000000"); prom_set(prom_buf, index++, "cpuclock=100000000"); - prom_set(prom_buf, index++, "memsize=%i", loaderparams.ram_size/1024/1024); + prom_set(prom_buf, index++, "memsize=%"PRIi64, loaderparams.ram_size / MiB); prom_set(prom_buf, index++, "modetty0=38400n8r"); prom_set(prom_buf, index++, NULL); @@ -303,10 +304,10 @@ static void mips_fulong2e_init(MachineState *machine) qemu_register_reset(main_cpu_reset, cpu); /* fulong 2e has 256M ram. */ - ram_size = 256 * 1024 * 1024; + ram_size = 256 * MiB; /* fulong 2e has a 1M flash.Winbond W39L040AP70Z */ - bios_size = 1024 * 1024; + bios_size = 1 * MiB; /* allocate RAM */ memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index b9d92bf47e..1603f9a762 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "cpu.h" #include "hw/hw.h" @@ -191,7 +192,7 @@ static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size) int i; /* work in terms of MB */ - ram_size >>= 20; + ram_size /= MiB; while ((ram_size >= 4) && (nbanks <= 2)) { int sz_log2 = MIN(31 - clz32(ram_size), 14); @@ -843,7 +844,8 @@ static int64_t load_kernel (void) /* The kernel allocates the bootmap memory in the low memory after the initrd. It takes at most 128kiB for 2GB RAM and 4kiB pages. */ - initrd_offset = (loaderparams.ram_low_size - initrd_size - 131072 + initrd_offset = (loaderparams.ram_low_size - initrd_size + - (128 * KiB) - ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK; if (kernel_high >= initrd_offset) { error_report("memory too small for initial ram disk '%s'", @@ -1021,9 +1023,9 @@ void mips_malta_init(MachineState *machine) mips_create_cpu(s, machine->cpu_type, &cbus_irq, &i8259_irq); /* allocate RAM */ - if (ram_size > (2048u << 20)) { - error_report("Too much memory for this machine: %dMB, maximum 2048MB", - ((unsigned int)ram_size / (1 << 20))); + if (ram_size > 2 * GiB) { + error_report("Too much memory for this machine: %" PRId64 "MB," + " maximum 2048MB", ram_size / MiB); exit(1); } @@ -1034,17 +1036,18 @@ void mips_malta_init(MachineState *machine) /* alias for pre IO hole access */ memory_region_init_alias(ram_low_preio, NULL, "mips_malta_low_preio.ram", - ram_high, 0, MIN(ram_size, (256 << 20))); + ram_high, 0, MIN(ram_size, 256 * MiB)); memory_region_add_subregion(system_memory, 0, ram_low_preio); /* alias for post IO hole access, if there is enough RAM */ - if (ram_size > (512 << 20)) { + if (ram_size > 512 * MiB) { ram_low_postio = g_new(MemoryRegion, 1); memory_region_init_alias(ram_low_postio, NULL, "mips_malta_low_postio.ram", - ram_high, 512 << 20, - ram_size - (512 << 20)); - memory_region_add_subregion(system_memory, 512 << 20, ram_low_postio); + ram_high, 512 * MiB, + ram_size - 512 * MiB); + memory_region_add_subregion(system_memory, 512 * MiB, + ram_low_postio); } #ifdef TARGET_WORDS_BIGENDIAN @@ -1076,7 +1079,7 @@ void mips_malta_init(MachineState *machine) bios = pflash_cfi01_get_memory(fl); fl_idx++; if (kernel_filename) { - ram_low_size = MIN(ram_size, 256 << 20); + ram_low_size = MIN(ram_size, 256 * MiB); /* For KVM we reserve 1MB of RAM for running bootloader */ if (kvm_enabled()) { ram_low_size -= 0x100000; diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index fc38b4bf99..d5725d0555 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -8,6 +8,7 @@ * the standard PC ISA addresses. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -143,7 +144,7 @@ static int64_t load_kernel(void) } rom_add_blob_fixed("params", params_buf, params_size, - (16 << 20) - params_size); + 16 * MiB - params_size); g_free(params_buf); return entry; @@ -158,7 +159,7 @@ static void main_cpu_reset(void *opaque) env->active_tc.PC = s->vector; } -static const int sector_len = 32 * 1024; +static const int sector_len = 32 * KiB; static void mips_r4k_init(MachineState *machine) { @@ -194,9 +195,9 @@ void mips_r4k_init(MachineState *machine) qemu_register_reset(main_cpu_reset, reset_info); /* allocate RAM */ - if (ram_size > (256 << 20)) { - error_report("Too much memory for this machine: %dMB, maximum 256MB", - ((unsigned int)ram_size / (1 << 20))); + if (ram_size > 256 * MiB) { + error_report("Too much memory for this machine: %" PRId64 "MB," + " maximum 256MB", ram_size / MiB); exit(1); } memory_region_allocate_system_memory(ram, NULL, "mips_r4k.ram", ram_size); diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index ccc4c7d98a..43bbec46cf 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/log.h" #include "qapi/error.h" #include "cpu.h" @@ -80,7 +81,7 @@ static void itc_reconfigure(MIPSITUState *tag) uint64_t *am = &tag->ITCAddressMap[0]; MemoryRegion *mr = &tag->storage_io; hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK; - uint64_t size = (1 << 10) + (am[1] & ITC_AM1_ADDR_MASK_MASK); + uint64_t size = (1 * KiB) + (am[1] & ITC_AM1_ADDR_MASK_MASK); bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0; memory_region_transaction_begin(); diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index b0a31b917d..60309afe9e 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/pci/pci_bridge.h" #include "hw/pci-host/xilinx-pcie.h" @@ -157,9 +158,9 @@ static void xilinx_pcie_host_init(Object *obj) static Property xilinx_pcie_host_props[] = { DEFINE_PROP_UINT32("bus_nr", XilinxPCIEHost, bus_nr, 0), DEFINE_PROP_SIZE("cfg_base", XilinxPCIEHost, cfg_base, 0), - DEFINE_PROP_SIZE("cfg_size", XilinxPCIEHost, cfg_size, 32 << 20), + DEFINE_PROP_SIZE("cfg_size", XilinxPCIEHost, cfg_size, 32 * MiB), DEFINE_PROP_SIZE("mmio_base", XilinxPCIEHost, mmio_base, 0), - DEFINE_PROP_SIZE("mmio_size", XilinxPCIEHost, mmio_size, 1 << 20), + DEFINE_PROP_SIZE("mmio_size", XilinxPCIEHost, mmio_size, 1 * MiB), DEFINE_PROP_BOOL("link_up", XilinxPCIEHost, link_up, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/intc/mips_gic.h b/include/hw/intc/mips_gic.h index b98d50094a..902a12b178 100644 --- a/include/hw/intc/mips_gic.h +++ b/include/hw/intc/mips_gic.h @@ -11,6 +11,7 @@ #ifndef MIPS_GIC_H #define MIPS_GIC_H +#include "qemu/units.h" #include "hw/timer/mips_gictimer.h" #include "cpu.h" /* @@ -19,7 +20,7 @@ /* The MIPS default location */ #define GIC_BASE_ADDR 0x1bdc0000ULL -#define GIC_ADDRSPACE_SZ (128 * 1024) +#define GIC_ADDRSPACE_SZ (128 * KiB) /* Constants */ #define GIC_POL_POS 1 diff --git a/include/hw/mips/bios.h b/include/hw/mips/bios.h index b4b88ac43d..d67ef33e83 100644 --- a/include/hw/mips/bios.h +++ b/include/hw/mips/bios.h @@ -1,6 +1,7 @@ +#include "qemu/units.h" #include "cpu.h" -#define BIOS_SIZE (4 * 1024 * 1024) +#define BIOS_SIZE (4 * MiB) #ifdef TARGET_WORDS_BIGENDIAN #define BIOS_FILENAME "mips_bios.bin" #else From ab3dd74924162f5a462b5c6514c34d918922d0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:24 -0300 Subject: [PATCH 1884/2380] hw/ppc: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Acked-by: David Gibson Message-Id: <20180625124238.25339-33-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/nvram/spapr_nvram.c | 11 +++++++---- hw/pci-host/prep.c | 3 ++- hw/ppc/e500.c | 13 +++++++------ hw/ppc/e500plat.c | 3 ++- hw/ppc/mac.h | 3 ++- hw/ppc/mac_newworld.c | 1 - hw/ppc/mac_oldworld.c | 9 ++++----- hw/ppc/ppc405_boards.c | 9 +++++---- hw/ppc/ppc405_uc.c | 7 ++++--- hw/ppc/ppc440_bamboo.c | 5 +++-- hw/ppc/ppc4xx_devs.c | 22 ++++++++++++---------- hw/ppc/ppce500_spin.c | 3 ++- hw/ppc/prep.c | 4 ++-- hw/ppc/rs6000_mc.c | 13 +++++++------ hw/ppc/sam460ex.c | 16 ++++++++-------- hw/ppc/spapr.c | 10 +++++----- hw/ppc/spapr_rtas.c | 2 +- hw/ppc/virtex_ml507.c | 7 ++++--- include/hw/ppc/spapr.h | 5 +++-- target/ppc/mmu_helper.c | 8 ++++---- 20 files changed, 84 insertions(+), 70 deletions(-) diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 4a0aec8e1d..bed1557d83 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -47,9 +48,9 @@ typedef struct sPAPRNVRAM { #define VIO_SPAPR_NVRAM(obj) \ OBJECT_CHECK(sPAPRNVRAM, (obj), TYPE_VIO_SPAPR_NVRAM) -#define MIN_NVRAM_SIZE 8192 -#define DEFAULT_NVRAM_SIZE 65536 -#define MAX_NVRAM_SIZE 1048576 +#define MIN_NVRAM_SIZE (8 * KiB) +#define DEFAULT_NVRAM_SIZE (64 * KiB) +#define MAX_NVRAM_SIZE (1 * MiB) static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, @@ -167,7 +168,9 @@ static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp) nvram->buf = g_malloc0(nvram->size); if ((nvram->size < MIN_NVRAM_SIZE) || (nvram->size > MAX_NVRAM_SIZE)) { - error_setg(errp, "spapr-nvram must be between %d and %d bytes in size", + error_setg(errp, + "spapr-nvram must be between %" PRId64 + " and %" PRId64 " bytes in size", MIN_NVRAM_SIZE, MAX_NVRAM_SIZE); return; } diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 01f67f9db1..88f035c20b 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/pci/pci.h" @@ -70,7 +71,7 @@ typedef struct PRePPCIState { int contiguous_map; } PREPPCIState; -#define BIOS_SIZE (1024 * 1024) +#define BIOS_SIZE (1 * MiB) static inline uint32_t raven_pci_io_config(hwaddr addr) { diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 826053edc8..7d19b1498c 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -15,6 +15,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "e500.h" #include "e500-ccsr.h" @@ -46,11 +47,11 @@ #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define DTC_LOAD_PAD 0x1800000 #define DTC_PAD_MASK 0xFFFFF -#define DTB_MAX_SIZE (8 * 1024 * 1024) +#define DTB_MAX_SIZE (8 * MiB) #define INITRD_LOAD_PAD 0x2000000 #define INITRD_PAD_MASK 0xFFFFFF -#define RAM_SIZES_ALIGN (64UL << 20) +#define RAM_SIZES_ALIGN (64 * MiB) /* TODO: parameterize */ #define MPC8544_CCSRBAR_SIZE 0x00100000ULL @@ -603,7 +604,7 @@ static int ppce500_prep_device_tree(PPCE500MachineState *machine, /* Create -kernel TLB entries for BookE. */ hwaddr booke206_page_size_to_tlb(uint64_t size) { - return 63 - clz64(size >> 10); + return 63 - clz64(size / KiB); } static int booke206_initial_map_tsize(CPUPPCState *env) @@ -671,7 +672,7 @@ static void ppce500_cpu_reset(void *opaque) /* Set initial guest state. */ cs->halted = 0; - env->gpr[1] = (16<<20) - 8; + env->gpr[1] = (16 * MiB) - 8; env->gpr[3] = bi->dt_base; env->gpr[4] = 0; env->gpr[5] = 0; @@ -1012,9 +1013,9 @@ void ppce500_init(MachineState *machine) } cur_base = loadaddr + payload_size; - if (cur_base < (32 * 1024 * 1024)) { + if (cur_base < 32 * MiB) { /* u-boot occupies memory up to 32MB, so load blobs above */ - cur_base = (32 * 1024 * 1024); + cur_base = 32 * MiB; } /* Load bare kernel only if no bios/u-boot has been provided */ diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index d8e3f2066e..963d429cc8 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -10,6 +10,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "e500.h" #include "hw/net/fsl_etsec/etsec.h" @@ -85,7 +86,7 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) pmc->has_mpc8xxx_gpio = true; pmc->has_platform_bus = true; pmc->platform_bus_base = 0xf00000000ULL; - pmc->platform_bus_size = (128ULL * 1024 * 1024); + pmc->platform_bus_size = 128 * MiB; pmc->platform_bus_first_irq = 5; pmc->platform_bus_num_irqs = 10; pmc->ccsrbar_base = 0xFE0000000ULL; diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index c0217e66f2..41fd289e81 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -26,6 +26,7 @@ #ifndef PPC_MAC_H #define PPC_MAC_H +#include "qemu/units.h" #include "exec/memory.h" #include "hw/boards.h" #include "hw/sysbus.h" @@ -38,7 +39,7 @@ /* SMP is not enabled, for now */ #define MAX_CPUS 1 -#define BIOS_SIZE (1024 * 1024) +#define BIOS_SIZE (1 * MiB) #define NVRAM_SIZE 0x2000 #define PROM_FILENAME "openbios-ppc" #define PROM_ADDR 0xfff00000 diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index ff715ffffd..84355b2672 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -71,7 +71,6 @@ #include "hw/usb.h" #include "exec/address-spaces.h" #include "hw/sysbus.h" -#include "qemu/cutils.h" #include "trace.h" #define MAX_IDE_BUS 2 diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 4608bab014..06ed6f660e 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" @@ -46,7 +47,6 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "exec/address-spaces.h" -#include "qemu/cutils.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -118,10 +118,9 @@ static void ppc_heathrow_init(MachineState *machine) } /* allocate RAM */ - if (ram_size > (2047 << 20)) { - fprintf(stderr, - "qemu: Too much memory for this machine: %d MB, maximum 2047 MB\n", - ((unsigned int)ram_size / (1 << 20))); + if (ram_size > 2047 * MiB) { + error_report("Too much memory for this machine: %" PRId64 " MB, " + "maximum 2047 MB", ram_size / MiB); exit(1); } diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index d301067d3b..70111075b3 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -40,7 +41,7 @@ #include "exec/address-spaces.h" #define BIOS_FILENAME "ppc405_rom.bin" -#define BIOS_SIZE (2048 * 1024) +#define BIOS_SIZE (2 * MiB) #define KERNEL_LOAD_ADDR 0x00000000 #define INITRD_LOAD_ADDR 0x01800000 @@ -216,14 +217,14 @@ static void ref405ep_init(MachineState *machine) memory_region_init(&ram_memories[1], NULL, "ef405ep.ram1", 0); ram_bases[1] = 0x00000000; ram_sizes[1] = 0x00000000; - ram_size = 128 * 1024 * 1024; + ram_size = 128 * MiB; #ifdef DEBUG_BOARD_INIT printf("%s: register cpu\n", __func__); #endif env = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, 33333333, &pic, kernel_filename == NULL ? 0 : 1); /* allocate SRAM */ - sram_size = 512 * 1024; + sram_size = 512 * KiB; memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size, &error_fatal); memory_region_add_subregion(sysmem, 0xFFF00000, sram); @@ -589,7 +590,7 @@ static void taihu_405ep_init(MachineState *machine) bios_size = blk_getlength(blk); /* XXX: should check that size is 32MB */ - bios_size = 32 * 1024 * 1024; + bios_size = 32 * MiB; fl_sectors = (bios_size + 65535) >> 16; #ifdef DEBUG_BOARD_INIT printf("Register parallel flash %d size %lx" diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 34f8d57b07..4bd9fbcc1e 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -983,10 +984,10 @@ static void ppc405_ocm_init(CPUPPCState *env) ocm = g_malloc0(sizeof(ppc405_ocm_t)); /* XXX: Size is 4096 or 0x04000000 */ - memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4096, + memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4 * KiB, &error_fatal); - memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", &ocm->isarc_ram, - 0, 4096); + memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", + &ocm->isarc_ram, 0, 4 * KiB); qemu_register_reset(&ocm_reset, ocm); ppc_dcr_register(env, OCM0_ISARC, ocm, &dcr_read_ocm, &dcr_write_ocm); diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 44e6a0c21b..3d4c43b8cc 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qemu-common.h" #include "qemu/error-report.h" @@ -49,7 +50,7 @@ #define PPC440EP_SDRAM_NR_BANKS 4 static const unsigned int ppc440ep_sdram_bank_sizes[] = { - 256<<20, 128<<20, 64<<20, 32<<20, 16<<20, 8<<20, 0 + 256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 0 }; static hwaddr entry; @@ -151,7 +152,7 @@ static void main_cpu_reset(void *opaque) CPUPPCState *env = &cpu->env; cpu_reset(CPU(cpu)); - env->gpr[1] = (16<<20) - 8; + env->gpr[1] = (16 * MiB) - 8; env->gpr[3] = FDT_ADDR; env->nip = entry; diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index 2e963894fe..8c6f3c9577 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" @@ -29,6 +30,7 @@ #include "hw/boards.h" #include "qemu/log.h" #include "exec/address-spaces.h" +#include "qemu/error-report.h" #define DEBUG_UIC @@ -353,25 +355,25 @@ static uint32_t sdram_bcr (hwaddr ram_base, uint32_t bcr; switch (ram_size) { - case (4 * 1024 * 1024): + case 4 * MiB: bcr = 0x00000000; break; - case (8 * 1024 * 1024): + case 8 * MiB: bcr = 0x00020000; break; - case (16 * 1024 * 1024): + case 16 * MiB: bcr = 0x00040000; break; - case (32 * 1024 * 1024): + case 32 * MiB: bcr = 0x00060000; break; - case (64 * 1024 * 1024): + case 64 * MiB: bcr = 0x00080000; break; - case (128 * 1024 * 1024): + case 128 * MiB: bcr = 0x000A0000; break; - case (256 * 1024 * 1024): + case 256 * MiB: bcr = 0x000C0000; break; default: @@ -399,7 +401,7 @@ static target_ulong sdram_size (uint32_t bcr) if (sh == 7) size = -1; else - size = (4 * 1024 * 1024) << sh; + size = (4 * MiB) << sh; return size; } @@ -702,8 +704,8 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, ram_size -= size_left; if (size_left) { - printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n", - (int)(ram_size >> 20)); + error_report("Truncating memory to %" PRId64 " MiB to fit SDRAM" + " controller limits", ram_size / MiB); } memory_region_allocate_system_memory(ram, NULL, "ppc4xx.sdram", ram_size); diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index 69ca2d0e42..c45fc858de 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -28,6 +28,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/hw_accel.h" @@ -89,7 +90,7 @@ static void spin_kick(CPUState *cs, run_on_cpu_data data) PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; SpinInfo *curspin = data.host_ptr; - hwaddr map_size = 64 * 1024 * 1024; + hwaddr map_size = 64 * MiB; hwaddr map_start; cpu_synchronize_state(cs); diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 25ae53304c..6689407b3d 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -50,7 +50,7 @@ #include "exec/address-spaces.h" #include "trace.h" #include "elf.h" -#include "qemu/cutils.h" +#include "qemu/units.h" #include "kvm_ppc.h" /* SMP is not enabled, for now */ @@ -60,7 +60,7 @@ #define CFG_ADDR 0xf0000510 -#define BIOS_SIZE (1024 * 1024) +#define BIOS_SIZE (1 * MiB) #define BIOS_FILENAME "ppc_rom.bin" #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c index b6135650bd..45cb95e08a 100644 --- a/hw/ppc/rs6000_mc.c +++ b/hw/ppc/rs6000_mc.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/isa/isa.h" #include "exec/address-spaces.h" #include "hw/boards.h" @@ -109,7 +110,7 @@ static void rs6000mc_port0820_write(void *opaque, uint32_t addr, uint32_t val) size = end_address - start_address; memory_region_set_enabled(&s->simm[socket - 1], size != 0); memory_region_set_address(&s->simm[socket - 1], - start_address * 8 * 1024 * 1024); + start_address * 8 * MiB); } } } @@ -140,7 +141,7 @@ static void rs6000mc_realize(DeviceState *dev, Error **errp) { RS6000MCState *s = RS6000MC_DEVICE(dev); int socket = 0; - unsigned int ram_size = s->ram_size / (1024 * 1024); + unsigned int ram_size = s->ram_size / MiB; while (socket < 6) { if (ram_size >= 64) { @@ -163,8 +164,8 @@ static void rs6000mc_realize(DeviceState *dev, Error **errp) char name[] = "simm.?"; name[5] = socket + '0'; memory_region_allocate_system_memory(&s->simm[socket], OBJECT(dev), - name, s->simm_size[socket] - * 1024 * 1024); + name, + s->simm_size[socket] * MiB); memory_region_add_subregion_overlap(get_system_memory(), 0, &s->simm[socket], socket); } @@ -172,8 +173,8 @@ static void rs6000mc_realize(DeviceState *dev, Error **errp) if (ram_size) { /* unable to push all requested RAM in SIMMs */ error_setg(errp, "RAM size incompatible with this board. " - "Try again with something else, like %d MB", - s->ram_size / 1024 / 1024 - ram_size); + "Try again with something else, like %" PRId64 " MB", + s->ram_size / MiB - ram_size); return; } diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 2a98c10664..c7c799b843 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -12,8 +12,8 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" -#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/hw.h" @@ -46,7 +46,7 @@ /* from Sam460 U-Boot include/configs/Sam460ex.h */ #define FLASH_BASE 0xfff00000 #define FLASH_BASE_H 0x4 -#define FLASH_SIZE (1 << 20) +#define FLASH_SIZE (1 * MiB) #define UBOOT_LOAD_BASE 0xfff80000 #define UBOOT_SIZE 0x00080000 #define UBOOT_ENTRY 0xfffffffc @@ -71,7 +71,7 @@ /* FIXME: See u-boot.git 8ac41e, also fix in ppc440_uc.c */ static const unsigned int ppc460ex_sdram_bank_sizes[] = { - 1024 << 20, 512 << 20, 256 << 20, 128 << 20, 64 << 20, 32 << 20, 0 + 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 0 }; struct boot_info { @@ -126,7 +126,7 @@ static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size) int i; /* work in terms of MB */ - ram_size >>= 20; + ram_size /= MiB; while ((ram_size >= 4) && (nbanks <= 2)) { int sz_log2 = MIN(31 - clz32(ram_size), 14); @@ -225,7 +225,7 @@ static int sam460ex_load_uboot(void) fl_sectors = (bios_size + 65535) >> 16; if (!pflash_cfi01_register(base, NULL, "sam460ex.flash", bios_size, - blk, (64 * 1024), fl_sectors, + blk, 64 * KiB, fl_sectors, 1, 0x89, 0x18, 0x0000, 0x0, 1)) { error_report("qemu: Error registering flash memory."); /* XXX: return an error instead? */ @@ -359,14 +359,14 @@ static void main_cpu_reset(void *opaque) /* either we have a kernel to boot or we jump to U-Boot */ if (bi->entry != UBOOT_ENTRY) { - env->gpr[1] = (16 << 20) - 8; + env->gpr[1] = (16 * MiB) - 8; env->gpr[3] = FDT_ADDR; env->nip = bi->entry; /* Create a mapping for the kernel. */ mmubooke_create_initial_mapping(env, 0, 0); env->gpr[6] = tswap32(EPAPR_MAGIC); - env->gpr[7] = (16 << 20) - 8; /*bi->ima_size;*/ + env->gpr[7] = (16 * MiB) - 8; /* bi->ima_size; */ } else { env->nip = UBOOT_ENTRY; @@ -479,7 +479,7 @@ static void sam460ex_init(MachineState *machine) /* 256K of L2 cache as memory */ ppc4xx_l2sram_init(env); /* FIXME: remove this after fixing l2sram mapping in ppc440_uc.c? */ - memory_region_init_ram(l2cache_ram, NULL, "ppc440.l2cache_ram", 256 << 10, + memory_region_init_ram(l2cache_ram, NULL, "ppc440.l2cache_ram", 256 * KiB, &error_abort); memory_region_add_subregion(address_space_mem, 0x400000000LL, l2cache_ram); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 7dbdb4e745..1602472165 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2322,7 +2322,7 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) if (machine->ram_size % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Memory size 0x" RAM_ADDR_FMT - " is not aligned to %llu MiB", + " is not aligned to %" PRIu64 " MiB", machine->ram_size, SPAPR_MEMORY_BLOCK_SIZE / MiB); return; @@ -2330,7 +2330,7 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) if (machine->maxram_size % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Maximum memory size 0x" RAM_ADDR_FMT - " is not aligned to %llu MiB", + " is not aligned to %" PRIu64 " MiB", machine->ram_size, SPAPR_MEMORY_BLOCK_SIZE / MiB); return; @@ -2340,7 +2340,7 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) if (numa_info[i].node_mem % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Node %d memory size 0x%" PRIx64 - " is not aligned to %llu MiB", + " is not aligned to %" PRIu64 " MiB", i, numa_info[i].node_mem, SPAPR_MEMORY_BLOCK_SIZE / MiB); return; @@ -2763,7 +2763,7 @@ static void spapr_machine_init(MachineState *machine) } } - if (spapr->rma_size < (MIN_RMA_SLOF << 20)) { + if (spapr->rma_size < (MIN_RMA_SLOF * MiB)) { error_report( "pSeries SLOF firmware requires >= %ldM guest RMA (Real Mode Area memory)", MIN_RMA_SLOF); @@ -3209,7 +3209,7 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, if (size % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Hotplugged memory size must be a multiple of " - "%lld MB", SPAPR_MEMORY_BLOCK_SIZE / MiB); + "%" PRIu64 " MB", SPAPR_MEMORY_BLOCK_SIZE / MiB); return; } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index f32740b947..4ac96bc94b 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -237,7 +237,7 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, switch (parameter) { case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: { char *param_val = g_strdup_printf("MaxEntCap=%d," - "DesMem=%llu," + "DesMem=%" PRIu64 "," "DesProcs=%d," "MaxPlatProcs=%d", max_cpus, diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index b4bb90d50b..7891464cd9 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "hw/sysbus.h" #include "hw/hw.h" @@ -45,7 +46,7 @@ #include "ppc405.h" #define EPAPR_MAGIC (0x45504150) -#define FLASH_SIZE (16 * 1024 * 1024) +#define FLASH_SIZE (16 * MiB) #define INTC_BASEADDR 0x81800000 #define UART16550_BASEADDR 0x83e01003 @@ -127,7 +128,7 @@ static void main_cpu_reset(void *opaque) * r8: 0 * r9: 0 */ - env->gpr[1] = (16<<20) - 8; + env->gpr[1] = (16 * MiB) - 8; /* Provide a device-tree. */ env->gpr[3] = bi->fdt; env->nip = bi->bootstrap_pc; @@ -235,7 +236,7 @@ static void virtex_init(MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); pflash_cfi01_register(PFLASH_BASEADDR, NULL, "virtex.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), FLASH_SIZE >> 16, + 64 * KiB, FLASH_SIZE >> 16, 1, 0x89, 0x18, 0x0000, 0x0, 1); cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT]; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 7e028164ba..7e5de1a6fd 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -1,6 +1,7 @@ #ifndef HW_SPAPR_H #define HW_SPAPR_H +#include "qemu/units.h" #include "sysemu/dma.h" #include "hw/boards.h" #include "hw/ppc/xics.h" @@ -749,8 +750,8 @@ int spapr_rng_populate_dt(void *fdt); */ #define SPAPR_MAX_RAM_SLOTS 32 -/* 1GB alignment for device memory region */ -#define SPAPR_DEVICE_MEM_ALIGN (1ULL << 30) +/* 1GB alignment for hotplug memory region */ +#define SPAPR_DEVICE_MEM_ALIGN (1 * GiB) /* * Number of 32 bit words in each LMB list entry in ibm,dynamic-memory diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 98ce17985b..e6739e6c24 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "exec/helper-proto.h" #include "sysemu/kvm.h" @@ -1090,11 +1091,10 @@ static void mmubooke_dump_mmu(FILE *f, fprintf_function cpu_fprintf, pa = entry->RPN & mask; /* Extend the physical address to 36 bits */ pa |= (hwaddr)(entry->RPN & 0xF) << 32; - size /= 1024; - if (size >= 1024) { - snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "M", size / 1024); + if (size >= 1 * MiB) { + snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "M", size / MiB); } else { - snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "k", size); + snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "k", size / KiB); } cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %s %-5u %08x %08x\n", (uint64_t)ea, (uint64_t)pa, size_buf, (uint32_t)entry->PID, From d471bf3ebbabd14f092655485f7562895b22e6b0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Jun 2018 16:22:13 +0200 Subject: [PATCH 1885/2380] hw/i386: Use the IEC binary prefix definitions It eases code review, unit is explicit. Patch generated using: $ git grep -E '[<>][<>]=? ?[1-5]0' hw/ include/hw/ and modified manually. Signed-off-by: Paolo Bonzini --- hw/i386/acpi-build.c | 4 ++-- hw/i386/pc.c | 23 ++++++++++++----------- hw/i386/pc_piix.c | 3 ++- hw/i386/pc_q35.c | 3 ++- hw/i386/pc_sysfw.c | 10 ++++------ 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 9bc6d97ea1..796de91bf3 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2248,8 +2248,8 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog) (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL); } -#define HOLE_640K_START (640 * 1024) -#define HOLE_640K_END (1024 * 1024) +#define HOLE_640K_START (640 * KiB) +#define HOLE_640K_END (1 * MiB) static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base, uint64_t len, int default_node) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f310040351..50d5553991 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/char/serial.h" @@ -448,12 +449,12 @@ void pc_cmos_init(PCMachineState *pcms, /* memory size */ /* base memory (first MiB) */ - val = MIN(pcms->below_4g_mem_size / 1024, 640); + val = MIN(pcms->below_4g_mem_size / KiB, 640); rtc_set_memory(s, 0x15, val); rtc_set_memory(s, 0x16, val >> 8); /* extended memory (next 64MiB) */ - if (pcms->below_4g_mem_size > 1024 * 1024) { - val = (pcms->below_4g_mem_size - 1024 * 1024) / 1024; + if (pcms->below_4g_mem_size > 1 * MiB) { + val = (pcms->below_4g_mem_size - 1 * MiB) / KiB; } else { val = 0; } @@ -464,8 +465,8 @@ void pc_cmos_init(PCMachineState *pcms, rtc_set_memory(s, 0x30, val); rtc_set_memory(s, 0x31, val >> 8); /* memory between 16MiB and 4GiB */ - if (pcms->below_4g_mem_size > 16 * 1024 * 1024) { - val = (pcms->below_4g_mem_size - 16 * 1024 * 1024) / 65536; + if (pcms->below_4g_mem_size > 16 * MiB) { + val = (pcms->below_4g_mem_size - 16 * MiB) / (64 * KiB); } else { val = 0; } @@ -1392,11 +1393,11 @@ void pc_memory_init(PCMachineState *pcms, } machine->device_memory->base = - ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30); + ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1 * GiB); if (pcmc->enforce_aligned_dimm) { /* size device region assuming 1G page max alignment per slot */ - device_mem_size += (1ULL << 30) * machine->ram_slots; + device_mem_size += (1 * GiB) * machine->ram_slots; } if ((machine->device_memory->base + device_mem_size) < @@ -1438,7 +1439,7 @@ void pc_memory_init(PCMachineState *pcms, if (!pcmc->broken_reserved_end) { res_mem_end += memory_region_size(&machine->device_memory->mr); } - *val = cpu_to_le64(ROUND_UP(res_mem_end, 0x1ULL << 30)); + *val = cpu_to_le64(ROUND_UP(res_mem_end, 1 * GiB)); fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val)); } @@ -1475,7 +1476,7 @@ uint64_t pc_pci_hole64_start(void) hole64_start = 0x100000000ULL + pcms->above_4g_mem_size; } - return ROUND_UP(hole64_start, 1ULL << 30); + return ROUND_UP(hole64_start, 1 * GiB); } qemu_irq pc_allocate_cpu_irq(void) @@ -2095,7 +2096,7 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, error_propagate(errp, error); return; } - if (value > (1ULL << 32)) { + if (value > 4 * GiB) { error_setg(&error, "Machine option 'max-ram-below-4g=%"PRIu64 "' expects size less than or equal to 4G", value); @@ -2103,7 +2104,7 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, return; } - if (value < (1ULL << 20)) { + if (value < 1 * MiB) { warn_report("Only %" PRIu64 " bytes of RAM below the 4GiB boundary," "BIOS may not work with less than 1MiB", value); } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index d357907b0b..dc09466b3e 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/loader.h" #include "hw/i386/pc.h" @@ -131,7 +132,7 @@ static void pc_init1(MachineState *machine, if (lowmem > 0xc0000000) { lowmem = 0xc0000000; } - if (lowmem & ((1ULL << 30) - 1)) { + if (lowmem & (1 * GiB - 1)) { warn_report("Large machine and max_ram_below_4g " "(%" PRIu64 ") not a multiple of 1G; " "possible bad performance.", diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 1a73e1848a..532241e3f8 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/loader.h" #include "sysemu/arch_init.h" @@ -105,7 +106,7 @@ static void pc_q35_init(MachineState *machine) if (lowmem > pcms->max_ram_below_4g) { lowmem = pcms->max_ram_below_4g; if (machine->ram_size - lowmem > lowmem && - lowmem & ((1ULL << 30) - 1)) { + lowmem & (1 * GiB - 1)) { warn_report("There is possibly poor performance as the ram size " " (0x%" PRIx64 ") is more then twice the size of" " max-ram-below-4g (%"PRIu64") and" diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 73ac783f20..091e22dd60 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -28,6 +28,7 @@ #include "sysemu/block-backend.h" #include "qemu/error-report.h" #include "qemu/option.h" +#include "qemu/units.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/i386/pc.h" @@ -56,7 +57,7 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, flash_size = memory_region_size(flash_mem); /* map the last 128KB of the BIOS in ISA space */ - isa_bios_size = MIN(flash_size, 128 * 1024); + isa_bios_size = MIN(flash_size, 128 * KiB); isa_bios = g_malloc(sizeof(*isa_bios)); memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size, &error_fatal); @@ -83,7 +84,7 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in * size. */ -#define FLASH_MAP_BASE_MIN ((hwaddr)(0x100000000ULL - 8*1024*1024)) +#define FLASH_MAP_BASE_MIN ((hwaddr)(4 * GiB - 8 * MiB)) /* This function maps flash drives from 4G downward, in order of their unit * numbers. The mapping starts at unit#0, with unit number increments of 1, and @@ -221,10 +222,7 @@ static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) g_free(filename); /* map the last 128KB of the BIOS in ISA space */ - isa_bios_size = bios_size; - if (isa_bios_size > (128 * 1024)) { - isa_bios_size = 128 * 1024; - } + isa_bios_size = MIN(bios_size, 128 * KiB); isa_bios = g_malloc(sizeof(*isa_bios)); memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, bios_size - isa_bios_size, isa_bios_size); From 872a2b7c4dcad2d4fa2bd34747c37a28a76cec2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:26 -0300 Subject: [PATCH 1886/2380] hw/net: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Weil Message-Id: <20180625124238.25339-35-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/net/e1000e.c | 7 ++++--- hw/net/e1000x_common.c | 3 ++- hw/net/eepro100.c | 3 +-- hw/net/ne2000.h | 5 +++-- include/hw/net/allwinner_emac.h | 5 +++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index cda8d48333..510ddb3897 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -34,6 +34,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "net/net.h" #include "net/tap.h" #include "qemu/range.h" @@ -81,10 +82,10 @@ typedef struct E1000EState { #define E1000E_IO_IDX 2 #define E1000E_MSIX_IDX 3 -#define E1000E_MMIO_SIZE (128 * 1024) -#define E1000E_FLASH_SIZE (128 * 1024) +#define E1000E_MMIO_SIZE (128 * KiB) +#define E1000E_FLASH_SIZE (128 * KiB) #define E1000E_IO_SIZE (32) -#define E1000E_MSIX_SIZE (16 * 1024) +#define E1000E_MSIX_SIZE (16 * KiB) #define E1000E_MSIX_TABLE (0x0000) #define E1000E_MSIX_PBA (0x2000) diff --git a/hw/net/e1000x_common.c b/hw/net/e1000x_common.c index eb0e097137..09047806f2 100644 --- a/hw/net/e1000x_common.c +++ b/hw/net/e1000x_common.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -111,7 +112,7 @@ bool e1000x_is_oversized(uint32_t *mac, size_t size) static const int maximum_ethernet_vlan_size = 1522; /* this is the size past which hardware will drop packets when setting LPE=1 */ - static const int maximum_ethernet_lpe_size = 16384; + static const int maximum_ethernet_lpe_size = 16 * KiB; if ((size > maximum_ethernet_lpe_size || (size > maximum_ethernet_vlan_size diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index a07a63247e..e761daf551 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -41,6 +41,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -60,8 +61,6 @@ * changed to pad short packets itself. */ #define CONFIG_PAD_RECEIVED_FRAMES -#define KiB 1024 - /* Debug EEPRO100 card. */ #if 0 # define DEBUG_EEPRO100 diff --git a/hw/net/ne2000.h b/hw/net/ne2000.h index adb8021bd1..2cd193e4c6 100644 --- a/hw/net/ne2000.h +++ b/hw/net/ne2000.h @@ -1,11 +1,12 @@ #ifndef HW_NE2000_H #define HW_NE2000_H +#include "qemu/units.h" #include "hw/hw.h" #include "net/net.h" -#define NE2000_PMEM_SIZE (32*1024) -#define NE2000_PMEM_START (16*1024) +#define NE2000_PMEM_SIZE (32 * KiB) +#define NE2000_PMEM_START (16 * KiB) #define NE2000_PMEM_END (NE2000_PMEM_SIZE+NE2000_PMEM_START) #define NE2000_MEM_SIZE NE2000_PMEM_END diff --git a/include/hw/net/allwinner_emac.h b/include/hw/net/allwinner_emac.h index 4cc8aab7ec..905a43deb4 100644 --- a/include/hw/net/allwinner_emac.h +++ b/include/hw/net/allwinner_emac.h @@ -23,6 +23,7 @@ #ifndef ALLWINNER_EMAC_H #define ALLWINNER_EMAC_H +#include "qemu/units.h" #include "net/net.h" #include "qemu/fifo8.h" #include "hw/net/mii.h" @@ -125,8 +126,8 @@ #define EMAC_INT_RX (1 << 8) /* Due to lack of specifications, size of fifos is chosen arbitrarily */ -#define TX_FIFO_SIZE (4 * 1024) -#define RX_FIFO_SIZE (32 * 1024) +#define TX_FIFO_SIZE (4 * KiB) +#define RX_FIFO_SIZE (32 * KiB) #define NUM_TX_FIFOS 2 #define RX_HDR_SIZE 8 From 246e195b521b02860914754fd39f7809f96be67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:27 -0300 Subject: [PATCH 1887/2380] hw/usb: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-36-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/usb/ccid-card-passthru.c | 9 +++++---- hw/usb/combined-packet.c | 3 ++- hw/usb/dev-smartcard-reader.c | 3 ++- hw/usb/redirect.c | 3 ++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index 25fb19b0d7..0a6c657228 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -9,6 +9,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include #include "chardev/char-fe.h" #include "qemu/error-report.h" @@ -40,7 +41,7 @@ static const uint8_t DEFAULT_ATR[] = { 0x13, 0x08 }; -#define VSCARD_IN_SIZE 65536 +#define VSCARD_IN_SIZE (64 * KiB) /* maximum size of ATR - from 7816-3 */ #define MAX_ATR_SIZE 40 @@ -275,9 +276,9 @@ static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size) VSCMsgHeader *hdr; if (card->vscard_in_pos + size > VSCARD_IN_SIZE) { - error_report( - "no room for data: pos %d + size %d > %d. dropping connection.", - card->vscard_in_pos, size, VSCARD_IN_SIZE); + error_report("no room for data: pos %u + size %d > %" PRId64 "." + " dropping connection.", + card->vscard_in_pos, size, VSCARD_IN_SIZE); ccid_card_vscard_drop_connection(card); return; } diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c index 48cac87f6a..01a7ed0848 100644 --- a/hw/usb/combined-packet.c +++ b/hw/usb/combined-packet.c @@ -20,6 +20,7 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "hw/usb.h" #include "qemu/iov.h" @@ -171,7 +172,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep) if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok || next == NULL || /* Work around for Linux usbfs bulk splitting + migration */ - (totalsize == 16348 && p->int_req)) { + (totalsize == (16 * KiB - 36) && p->int_req)) { usb_device_handle_data(ep->dev, first); assert(first->status == USB_RET_ASYNC); if (first->combined) { diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 13d0befd9c..8f716fc165 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -35,6 +35,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" @@ -63,7 +64,7 @@ do { \ * or handle the migration complexity - VMState doesn't handle this case. * sizes are expected never to be exceeded, unless guest misbehaves. */ -#define BULK_OUT_DATA_SIZE 65536 +#define BULK_OUT_DATA_SIZE (64 * KiB) #define PENDING_ANSWERS_NUM 128 #define BULK_IN_BUF_SIZE 384 diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 58e8f7f5bd..99094a721e 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "qemu/timer.h" @@ -1298,7 +1299,7 @@ static int usbredir_chardev_can_read(void *opaque) } /* usbredir_parser_do_read will consume *all* data we give it */ - return 1024 * 1024; + return 1 * MiB; } static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) From 4c8f9735da6111b9b1e52f12ee0ef4f481bc3b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:28 -0300 Subject: [PATCH 1888/2380] hw/sd: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-37-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/sd/sd.c | 4 ++-- hw/sd/sdhci.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 9a16b768ed..d4356e9b73 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -31,6 +31,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/qdev.h" #include "hw/hw.h" #include "hw/registerfields.h" @@ -38,7 +39,6 @@ #include "hw/sd/sd.h" #include "qapi/error.h" #include "qemu/bitmap.h" -#include "qemu/cutils.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" #include "qemu/timer.h" @@ -403,7 +403,7 @@ static void sd_set_csd(SDState *sd, uint64_t size) ((HWBLOCK_SHIFT << 6) & 0xc0); sd->csd[14] = 0x00; /* File format group */ } else { /* SDHC */ - size /= 512 * 1024; + size /= 512 * KiB; size -= 1; sd->csd[0] = 0x40; sd->csd[1] = 0x0e; diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index c74025b2f4..8f58c31265 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/hw.h" @@ -32,7 +33,6 @@ #include "hw/sd/sdhci.h" #include "sdhci-internal.h" #include "qemu/log.h" -#include "qemu/cutils.h" #include "trace.h" #define TYPE_SDHCI_BUS "sdhci-bus" @@ -737,7 +737,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr) if ((dscr->attr & SDHC_ADMA_ATTR_ACT_MASK) == SDHC_ADMA_ATTR_SET_LEN) { dscr->length = (uint16_t)extract32(adma1, 12, 16); } else { - dscr->length = 4096; + dscr->length = 4 * KiB; } break; case SDHC_CTRL_ADMA2_64: @@ -785,7 +785,7 @@ static void sdhci_do_adma(SDHCIState *s) return; } - length = dscr.length ? dscr.length : 65536; + length = dscr.length ? dscr.length : 64 * KiB; switch (dscr.attr & SDHC_ADMA_ATTR_ACT_MASK) { case SDHC_ADMA_ATTR_ACT_TRAN: /* data transfer */ From e0255bb1acc3c100a02c0f708a144e77165b99d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:29 -0300 Subject: [PATCH 1889/2380] hw/vfio: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-38-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/vfio/pci-quirks.c | 9 +++++---- hw/vfio/pci.c | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 061259b86b..481fd08df7 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/range.h" @@ -1448,9 +1449,9 @@ static int vfio_igd_gtt_max(VFIOPCIDevice *vdev) ggms = 1 << ggms; } - ggms *= 1024 * 1024; + ggms *= MiB; - return (ggms / (4 * 1024)) * (gen < 8 ? 4 : 8); + return (ggms / (4 * KiB)) * (gen < 8 ? 4 : 8); } /* @@ -1705,7 +1706,7 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) igd->vdev = vdev; igd->index = ~0; igd->bdsm = vfio_pci_read_config(&vdev->pdev, IGD_BDSM, 4); - igd->bdsm &= ~((1 << 20) - 1); /* 1MB aligned */ + igd->bdsm &= ~((1 * MiB) - 1); /* 1MB aligned */ memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_index_quirk, igd, "vfio-igd-index-quirk", 4); @@ -1752,7 +1753,7 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * config offset 0x5C. */ bdsm_size = g_malloc(sizeof(*bdsm_size)); - *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * 1024 * 1024); + *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * MiB); fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size", bdsm_size, sizeof(*bdsm_size)); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 18c493b49e..a1577dea7f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -28,6 +28,7 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/range.h" +#include "qemu/units.h" #include "sysemu/kvm.h" #include "sysemu/sysemu.h" #include "pci.h" @@ -1417,7 +1418,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) } /* 2GB max size for 32-bit BARs, cannot double if already > 1G */ - if (vdev->bars[target_bar].size > (1 * 1024 * 1024 * 1024) && + if (vdev->bars[target_bar].size > 1 * GiB && !vdev->bars[target_bar].mem64) { error_setg(errp, "Invalid MSI-X relocation BAR %d, " "no space to extend 32-bit BAR", target_bar); From c9ad15d71eb6de8f54060bc7a25e446c0f7a83ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:30 -0300 Subject: [PATCH 1890/2380] hw/virtio: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-39-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- include/hw/virtio/virtio-net.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 02484dc94c..4d7f3c82ca 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -14,6 +14,7 @@ #ifndef QEMU_VIRTIO_NET_H #define QEMU_VIRTIO_NET_H +#include "qemu/units.h" #include "standard-headers/linux/virtio_net.h" #include "hw/virtio/virtio.h" @@ -44,7 +45,7 @@ typedef struct virtio_net_conf } virtio_net_conf; /* Maximum packet size we can receive from tap device: header + 64k */ -#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10)) +#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 * KiB)) typedef struct VirtIONetQueue { VirtQueue *rx_vq; From 78109066e6cfbd9a0fd9fa4a5dc76834456fdded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:31 -0300 Subject: [PATCH 1891/2380] hw/rdma: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -E '(1024|2048|4096|8192|(<<|>>).?(10|20|30))' hw/ include/hw/ and modified manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marcel Apfelbaum Reviewed-by: Yuval Shaia Message-Id: <20180625124238.25339-40-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- hw/rdma/vmw/pvrdma.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/rdma/vmw/pvrdma.h b/hw/rdma/vmw/pvrdma.h index 0b46dc5a9b..81e0e0e99c 100644 --- a/hw/rdma/vmw/pvrdma.h +++ b/hw/rdma/vmw/pvrdma.h @@ -16,6 +16,7 @@ #ifndef PVRDMA_PVRDMA_H #define PVRDMA_PVRDMA_H +#include "qemu/units.h" #include "hw/pci/pci.h" #include "hw/pci/msix.h" @@ -30,7 +31,7 @@ #define RDMA_MSIX_BAR_IDX 0 #define RDMA_REG_BAR_IDX 1 #define RDMA_UAR_BAR_IDX 2 -#define RDMA_BAR0_MSIX_SIZE (16 * 1024) +#define RDMA_BAR0_MSIX_SIZE (16 * KiB) #define RDMA_BAR1_REGS_SIZE 64 #define RDMA_BAR2_UAR_SIZE (0x1000 * MAX_UCS) /* each uc gets page */ From af5ecb4739ac880dc6fc9ca34f46b2ab37a3b583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:32 -0300 Subject: [PATCH 1892/2380] cutils: Do not include "qemu/units.h" directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All files using "qemu/units.h" definitions already include it directly, we can now remove it from "qemu/cutils.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marcel Apfelbaum Message-Id: <20180625124238.25339-41-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- include/qemu/cutils.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 6d5ea8351e..47aaa3b0b9 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -2,7 +2,6 @@ #define QEMU_CUTILS_H #include "qemu/fprintf-fn.h" -#include "qemu/units.h" /** * pstrcpy: From 8ec338acfc86cf0360585fb899aca42661616011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:34 -0300 Subject: [PATCH 1893/2380] monitor: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -n '[<>][<>]= ?[1-5]0' and modified manually. Suggested-by: Eric Blake Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-43-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- monitor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monitor.c b/monitor.c index 567668a0e7..3a0ea0c602 100644 --- a/monitor.c +++ b/monitor.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include #include "cpu.h" #include "hw/hw.h" @@ -3303,7 +3304,7 @@ static QDict *monitor_parse_arguments(Monitor *mon, monitor_printf(mon, "enter a positive value\n"); goto fail; } - val <<= 20; + val *= MiB; } qdict_put_int(qdict, key, val); } From 3dc54b0eda03a3400e714f095d17b2f747ed109b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:35 -0300 Subject: [PATCH 1894/2380] vl: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -n '[<>][<>]= ?[1-5]0' and modified manually. Suggested-by: Eric Blake Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-44-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- vl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vl.c b/vl.c index ef6cfcec40..93795c1b9f 100644 --- a/vl.c +++ b/vl.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-version.h" #include "qemu/cutils.h" @@ -2808,8 +2809,8 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, if (g_ascii_isdigit(mem_str[strlen(mem_str) - 1])) { uint64_t overflow_check = sz; - sz <<= 20; - if ((sz >> 20) != overflow_check) { + sz *= MiB; + if (sz / MiB != overflow_check) { error_report("too large 'size' option value"); exit(EXIT_FAILURE); } From 68dbb6d05db59fe39af0c192005490576d9f5b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:36 -0300 Subject: [PATCH 1895/2380] tests/crypto: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -n '[<>][<>]= ?[1-5]0' and modified manually. Suggested-by: Eric Blake Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-45-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- tests/benchmark-crypto-cipher.c | 6 +++--- tests/benchmark-crypto-hash.c | 5 +++-- tests/benchmark-crypto-hmac.c | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/benchmark-crypto-cipher.c b/tests/benchmark-crypto-cipher.c index cf98443468..f5a0d0bc32 100644 --- a/tests/benchmark-crypto-cipher.c +++ b/tests/benchmark-crypto-cipher.c @@ -11,6 +11,7 @@ * top-level directory. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "crypto/init.h" #include "crypto/cipher.h" @@ -56,8 +57,7 @@ static void test_cipher_speed(const void *opaque) total += chunk_size; } while (g_test_timer_elapsed() < 5.0); - total /= 1024 * 1024; /* to MB */ - + total /= MiB; g_print("cbc(aes128): "); g_print("Testing chunk_size %zu bytes ", chunk_size); g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); @@ -78,7 +78,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_assert(qcrypto_init(NULL) == 0); - for (i = 512; i <= (64 * 1204); i *= 2) { + for (i = 512; i <= 64 * KiB; i *= 2) { memset(name, 0 , sizeof(name)); snprintf(name, sizeof(name), "/crypto/cipher/speed-%zu", i); g_test_add_data_func(name, (void *)i, test_cipher_speed); diff --git a/tests/benchmark-crypto-hash.c b/tests/benchmark-crypto-hash.c index 122bfb6b85..9b6f7a9155 100644 --- a/tests/benchmark-crypto-hash.c +++ b/tests/benchmark-crypto-hash.c @@ -11,6 +11,7 @@ * top-level directory. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "crypto/init.h" #include "crypto/hash.h" @@ -39,7 +40,7 @@ static void test_hash_speed(const void *opaque) total += chunk_size; } while (g_test_timer_elapsed() < 5.0); - total /= 1024 * 1024; /* to MB */ + total /= MiB; g_print("sha256: "); g_print("Testing chunk_size %zu bytes ", chunk_size); g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); @@ -57,7 +58,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_assert(qcrypto_init(NULL) == 0); - for (i = 512; i <= (64 * 1204); i *= 2) { + for (i = 512; i <= 64 * KiB; i *= 2) { memset(name, 0 , sizeof(name)); snprintf(name, sizeof(name), "/crypto/hash/speed-%zu", i); g_test_add_data_func(name, (void *)i, test_hash_speed); diff --git a/tests/benchmark-crypto-hmac.c b/tests/benchmark-crypto-hmac.c index c30250df3e..f1dfa240cb 100644 --- a/tests/benchmark-crypto-hmac.c +++ b/tests/benchmark-crypto-hmac.c @@ -11,6 +11,7 @@ * top-level directory. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "crypto/init.h" #include "crypto/hmac.h" @@ -53,8 +54,7 @@ static void test_hmac_speed(const void *opaque) total += chunk_size; } while (g_test_timer_elapsed() < 5.0); - total /= 1024 * 1024; /* to MB */ - + total /= MiB; g_print("hmac(sha256): "); g_print("Testing chunk_size %zu bytes ", chunk_size); g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); @@ -72,7 +72,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_assert(qcrypto_init(NULL) == 0); - for (i = 512; i <= (64 * 1204); i *= 2) { + for (i = 512; i <= 64 * KiB; i *= 2) { memset(name, 0 , sizeof(name)); snprintf(name, sizeof(name), "/crypto/hmac/speed-%zu", i); g_test_add_data_func(name, (void *)i, test_hmac_speed); From b52713c1d5fb3f05b07bdcaa52d0a665d26eaff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:37 -0300 Subject: [PATCH 1896/2380] linux-user: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -n '[<>][<>]= ?[1-5]0' and modified manually. Suggested-by: Eric Blake Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-46-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- linux-user/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 84e9ec9335..52b5a618fe 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-version.h" #include #include @@ -274,9 +275,9 @@ static void handle_arg_stack_size(const char *arg) } if (*p == 'M') { - guest_stack_size *= 1024 * 1024; + guest_stack_size *= MiB; } else if (*p == 'k' || *p == 'K') { - guest_stack_size *= 1024; + guest_stack_size *= KiB; } } From 66d26ddb235439c72ff2b5fc0b42e242a57ba315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Jun 2018 09:42:38 -0300 Subject: [PATCH 1897/2380] bsd-user: Use the IEC binary prefix definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It eases code review, unit is explicit. Patch generated using: $ git grep -n '[<>][<>]= ?[1-5]0' and modified manually. Suggested-by: Eric Blake Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180625124238.25339-47-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- bsd-user/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index da3b833975..0d3156974c 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-version.h" #include @@ -795,9 +796,9 @@ int main(int argc, char **argv) if (x86_stack_size <= 0) usage(); if (*r == 'M') - x86_stack_size *= 1024 * 1024; + x86_stack_size *= MiB; else if (*r == 'k' || *r == 'K') - x86_stack_size *= 1024; + x86_stack_size *= KiB; } else if (!strcmp(r, "L")) { interp_prefix = argv[optind++]; } else if (!strcmp(r, "p")) { From 76b004d10da09bb77a4343edf7b98b2d662fb4f2 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Mon, 26 Feb 2018 13:04:52 +0000 Subject: [PATCH 1898/2380] serial: Open non-block On a real serial device, the open can block if the handshake lines are in a particular state. If a QEMU is passing the serial device to the guest, the QEMU startup is blocked opening the device (with a symptom seen as a timeout from libvirt). Open the serial port with O_NONBLOCK. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Paolo Bonzini --- chardev/char-serial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chardev/char-serial.c b/chardev/char-serial.c index ae548d28da..3299b46853 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -265,7 +265,8 @@ static void qmp_chardev_open_serial(Chardev *chr, ChardevHostdev *serial = backend->u.serial.data; int fd; - fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp); + fd = qmp_chardev_open_file_source(serial->device, O_RDWR | O_NONBLOCK, + errp); if (fd < 0) { return; } From fe441054bb3f0c75ff23335790342c0408e11c3a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sat, 30 Jun 2018 08:08:23 +0200 Subject: [PATCH 1899/2380] target-i386: Add NPT support This implements NPT suport for SVM by hooking into x86_cpu_handle_mmu_fault where it reads the stage-1 page table. Whether we need to perform this 2nd stage translation, and how, is decided during vmrun and stored in hflags2, along with nested_cr3 and nested_pg_mode. As get_hphys performs a direct cpu_vmexit in case of NPT faults, we need retaddr in that function. To avoid changing the signature of cpu_handle_mmu_fault, this passes the value from tlb_fill to get_hphys via the CPU state. This was tested successfully via the Jailhouse hypervisor. Signed-off-by: Jan Kiszka Message-Id: <567473a0-6005-5843-4c73-951f476085ca@web.de> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- target/i386/cpu.h | 6 ++ target/i386/excp_helper.c | 216 +++++++++++++++++++++++++++++++++++++- target/i386/machine.c | 21 ++++ target/i386/mem_helper.c | 6 +- target/i386/svm.h | 14 +++ target/i386/svm_helper.c | 22 ++++ 7 files changed, 281 insertions(+), 6 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index bdbd446b88..b0b87c3d81 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -749,7 +749,7 @@ static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A) #define TCG_EXT4_FEATURES 0 -#define TCG_SVM_FEATURES 0 +#define TCG_SVM_FEATURES CPUID_SVM_NPT #define TCG_KVM_FEATURES 0 #define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \ diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8b1ddfe932..2c5a0d90a6 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -211,6 +211,7 @@ typedef enum X86Seg { #define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */ #define HF2_SMM_INSIDE_NMI_SHIFT 4 /* CPU serving SMI nested inside NMI */ #define HF2_MPX_PR_SHIFT 5 /* BNDCFGx.BNDPRESERVE */ +#define HF2_NPT_SHIFT 6 /* Nested Paging enabled */ #define HF2_GIF_MASK (1 << HF2_GIF_SHIFT) #define HF2_HIF_MASK (1 << HF2_HIF_SHIFT) @@ -218,6 +219,7 @@ typedef enum X86Seg { #define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT) #define HF2_SMM_INSIDE_NMI_MASK (1 << HF2_SMM_INSIDE_NMI_SHIFT) #define HF2_MPX_PR_MASK (1 << HF2_MPX_PR_SHIFT) +#define HF2_NPT_MASK (1 << HF2_NPT_SHIFT) #define CR0_PE_SHIFT 0 #define CR0_MP_SHIFT 1 @@ -1265,12 +1267,16 @@ typedef struct CPUX86State { uint16_t intercept_dr_read; uint16_t intercept_dr_write; uint32_t intercept_exceptions; + uint64_t nested_cr3; + uint32_t nested_pg_mode; uint8_t v_tpr; /* KVM states, automatically cleared on reset */ uint8_t nmi_injected; uint8_t nmi_pending; + uintptr_t retaddr; + /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; diff --git a/target/i386/excp_helper.c b/target/i386/excp_helper.c index cb4d1b7d33..37a33d5ae0 100644 --- a/target/i386/excp_helper.c +++ b/target/i386/excp_helper.c @@ -157,6 +157,209 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, #else +static hwaddr get_hphys(CPUState *cs, hwaddr gphys, MMUAccessType access_type, + int *prot) +{ + CPUX86State *env = &X86_CPU(cs)->env; + uint64_t rsvd_mask = PG_HI_RSVD_MASK; + uint64_t ptep, pte; + uint64_t exit_info_1 = 0; + target_ulong pde_addr, pte_addr; + uint32_t page_offset; + int page_size; + + if (likely(!(env->hflags2 & HF2_NPT_MASK))) { + return gphys; + } + + if (!(env->nested_pg_mode & SVM_NPT_NXE)) { + rsvd_mask |= PG_NX_MASK; + } + + if (env->nested_pg_mode & SVM_NPT_PAE) { + uint64_t pde, pdpe; + target_ulong pdpe_addr; + +#ifdef TARGET_X86_64 + if (env->nested_pg_mode & SVM_NPT_LMA) { + uint64_t pml5e; + uint64_t pml4e_addr, pml4e; + + pml5e = env->nested_cr3; + ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; + + pml4e_addr = (pml5e & PG_ADDRESS_MASK) + + (((gphys >> 39) & 0x1ff) << 3); + pml4e = x86_ldq_phys(cs, pml4e_addr); + if (!(pml4e & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pml4e & (rsvd_mask | PG_PSE_MASK)) { + goto do_fault_rsvd; + } + if (!(pml4e & PG_ACCESSED_MASK)) { + pml4e |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pml4e_addr, pml4e); + } + ptep &= pml4e ^ PG_NX_MASK; + pdpe_addr = (pml4e & PG_ADDRESS_MASK) + + (((gphys >> 30) & 0x1ff) << 3); + pdpe = x86_ldq_phys(cs, pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pdpe & rsvd_mask) { + goto do_fault_rsvd; + } + ptep &= pdpe ^ PG_NX_MASK; + if (!(pdpe & PG_ACCESSED_MASK)) { + pdpe |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pdpe_addr, pdpe); + } + if (pdpe & PG_PSE_MASK) { + /* 1 GB page */ + page_size = 1024 * 1024 * 1024; + pte_addr = pdpe_addr; + pte = pdpe; + goto do_check_protect; + } + } else +#endif + { + pdpe_addr = (env->nested_cr3 & ~0x1f) + ((gphys >> 27) & 0x18); + pdpe = x86_ldq_phys(cs, pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + goto do_fault; + } + rsvd_mask |= PG_HI_USER_MASK; + if (pdpe & (rsvd_mask | PG_NX_MASK)) { + goto do_fault_rsvd; + } + ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; + } + + pde_addr = (pdpe & PG_ADDRESS_MASK) + (((gphys >> 21) & 0x1ff) << 3); + pde = x86_ldq_phys(cs, pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pde & rsvd_mask) { + goto do_fault_rsvd; + } + ptep &= pde ^ PG_NX_MASK; + if (pde & PG_PSE_MASK) { + /* 2 MB page */ + page_size = 2048 * 1024; + pte_addr = pde_addr; + pte = pde; + goto do_check_protect; + } + /* 4 KB page */ + if (!(pde & PG_ACCESSED_MASK)) { + pde |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pde_addr, pde); + } + pte_addr = (pde & PG_ADDRESS_MASK) + (((gphys >> 12) & 0x1ff) << 3); + pte = x86_ldq_phys(cs, pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pte & rsvd_mask) { + goto do_fault_rsvd; + } + /* combine pde and pte nx, user and rw protections */ + ptep &= pte ^ PG_NX_MASK; + page_size = 4096; + } else { + uint32_t pde; + + /* page directory entry */ + pde_addr = (env->nested_cr3 & ~0xfff) + ((gphys >> 20) & 0xffc); + pde = x86_ldl_phys(cs, pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + goto do_fault; + } + ptep = pde | PG_NX_MASK; + + /* if PSE bit is set, then we use a 4MB page */ + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + page_size = 4096 * 1024; + pte_addr = pde_addr; + + /* Bits 20-13 provide bits 39-32 of the address, bit 21 is reserved. + * Leave bits 20-13 in place for setting accessed/dirty bits below. + */ + pte = pde | ((pde & 0x1fe000LL) << (32 - 13)); + rsvd_mask = 0x200000; + goto do_check_protect_pse36; + } + + if (!(pde & PG_ACCESSED_MASK)) { + pde |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pde_addr, pde); + } + + /* page directory entry */ + pte_addr = (pde & ~0xfff) + ((gphys >> 10) & 0xffc); + pte = x86_ldl_phys(cs, pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + goto do_fault; + } + /* combine pde and pte user and rw protections */ + ptep &= pte | PG_NX_MASK; + page_size = 4096; + rsvd_mask = 0; + } + + do_check_protect: + rsvd_mask |= (page_size - 1) & PG_ADDRESS_MASK & ~PG_PSE_PAT_MASK; + do_check_protect_pse36: + if (pte & rsvd_mask) { + goto do_fault_rsvd; + } + ptep ^= PG_NX_MASK; + + if (!(ptep & PG_USER_MASK)) { + goto do_fault_protect; + } + if (ptep & PG_NX_MASK) { + if (access_type == MMU_INST_FETCH) { + goto do_fault_protect; + } + *prot &= ~PAGE_EXEC; + } + if (!(ptep & PG_RW_MASK)) { + if (access_type == MMU_DATA_STORE) { + goto do_fault_protect; + } + *prot &= ~PAGE_WRITE; + } + + pte &= PG_ADDRESS_MASK & ~(page_size - 1); + page_offset = gphys & (page_size - 1); + return pte + page_offset; + + do_fault_rsvd: + exit_info_1 |= SVM_NPTEXIT_RSVD; + do_fault_protect: + exit_info_1 |= SVM_NPTEXIT_P; + do_fault: + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), + gphys); + exit_info_1 |= SVM_NPTEXIT_US; + if (access_type == MMU_DATA_STORE) { + exit_info_1 |= SVM_NPTEXIT_RW; + } else if (access_type == MMU_INST_FETCH) { + exit_info_1 |= SVM_NPTEXIT_ID; + } + if (prot) { + exit_info_1 |= SVM_NPTEXIT_GPA; + } else { /* page table access */ + exit_info_1 |= SVM_NPTEXIT_GPT; + } + cpu_vmexit(env, SVM_EXIT_NPF, exit_info_1, env->retaddr); +} + /* return value: * -1 = cannot handle fault * 0 = nothing more to do @@ -224,6 +427,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, if (la57) { pml5e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 48) & 0x1ff) << 3)) & a20_mask; + pml5e_addr = get_hphys(cs, pml5e_addr, MMU_DATA_STORE, NULL); pml5e = x86_ldq_phys(cs, pml5e_addr); if (!(pml5e & PG_PRESENT_MASK)) { goto do_fault; @@ -243,6 +447,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, pml4e_addr = ((pml5e & PG_ADDRESS_MASK) + (((addr >> 39) & 0x1ff) << 3)) & a20_mask; + pml4e_addr = get_hphys(cs, pml4e_addr, MMU_DATA_STORE, false); pml4e = x86_ldq_phys(cs, pml4e_addr); if (!(pml4e & PG_PRESENT_MASK)) { goto do_fault; @@ -257,6 +462,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, ptep &= pml4e ^ PG_NX_MASK; pdpe_addr = ((pml4e & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3)) & a20_mask; + pdpe_addr = get_hphys(cs, pdpe_addr, MMU_DATA_STORE, NULL); pdpe = x86_ldq_phys(cs, pdpe_addr); if (!(pdpe & PG_PRESENT_MASK)) { goto do_fault; @@ -282,6 +488,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, /* XXX: load them when cr3 is loaded ? */ pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) & a20_mask; + pdpe_addr = get_hphys(cs, pdpe_addr, MMU_DATA_STORE, false); pdpe = x86_ldq_phys(cs, pdpe_addr); if (!(pdpe & PG_PRESENT_MASK)) { goto do_fault; @@ -295,6 +502,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, pde_addr = ((pdpe & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3)) & a20_mask; + pde_addr = get_hphys(cs, pde_addr, MMU_DATA_STORE, NULL); pde = x86_ldq_phys(cs, pde_addr); if (!(pde & PG_PRESENT_MASK)) { goto do_fault; @@ -317,6 +525,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, } pte_addr = ((pde & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3)) & a20_mask; + pte_addr = get_hphys(cs, pte_addr, MMU_DATA_STORE, NULL); pte = x86_ldq_phys(cs, pte_addr); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; @@ -333,6 +542,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, /* page directory entry */ pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & a20_mask; + pde_addr = get_hphys(cs, pde_addr, MMU_DATA_STORE, NULL); pde = x86_ldl_phys(cs, pde_addr); if (!(pde & PG_PRESENT_MASK)) { goto do_fault; @@ -360,6 +570,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, /* page directory entry */ pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & a20_mask; + pte_addr = get_hphys(cs, pte_addr, MMU_DATA_STORE, NULL); pte = x86_ldl_phys(cs, pte_addr); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; @@ -442,12 +653,13 @@ do_check_protect_pse36: /* align to page_size */ pte &= PG_ADDRESS_MASK & ~(page_size - 1); + page_offset = addr & (page_size - 1); + paddr = get_hphys(cs, pte + page_offset, is_write1, &prot); /* Even if 4MB pages, we map only one 4KB page in the cache to avoid filling it too fast */ vaddr = addr & TARGET_PAGE_MASK; - page_offset = vaddr & (page_size - 1); - paddr = pte + page_offset; + paddr &= TARGET_PAGE_MASK; assert(prot & (1 << is_write1)); tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env), diff --git a/target/i386/machine.c b/target/i386/machine.c index 4d98d367c1..8b64dff487 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -935,6 +935,26 @@ static const VMStateDescription vmstate_msr_virt_ssbd = { } }; +static bool svm_npt_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return !!(env->hflags2 & HF2_NPT_MASK); +} + +static const VMStateDescription vmstate_svm_npt = { + .name = "cpu/svn_npt", + .version_id = 1, + .minimum_version_id = 1, + .needed = svm_npt_needed, + .fields = (VMStateField[]){ + VMSTATE_UINT64(env.nested_cr3, X86CPU), + VMSTATE_UINT32(env.nested_pg_mode, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + VMStateDescription vmstate_x86_cpu = { .name = "cpu", .version_id = 12, @@ -1059,6 +1079,7 @@ VMStateDescription vmstate_x86_cpu = { &vmstate_mcg_ext_ctl, &vmstate_msr_intel_pt, &vmstate_msr_virt_ssbd, + &vmstate_svm_npt, NULL } }; diff --git a/target/i386/mem_helper.c b/target/i386/mem_helper.c index a8ae694a9c..30c26b9d9c 100644 --- a/target/i386/mem_helper.c +++ b/target/i386/mem_helper.c @@ -202,13 +202,13 @@ void helper_boundl(CPUX86State *env, target_ulong a0, int v) void tlb_fill(CPUState *cs, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; int ret; + env->retaddr = retaddr; ret = x86_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx); if (ret) { - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - raise_exception_err_ra(env, cs->exception_index, env->error_code, retaddr); } } diff --git a/target/i386/svm.h b/target/i386/svm.h index 922c8fd39c..23a3a040b8 100644 --- a/target/i386/svm.h +++ b/target/i386/svm.h @@ -130,6 +130,20 @@ #define SVM_CR0_SELECTIVE_MASK (1 << 3 | 1) /* TS and MP */ +#define SVM_NPT_ENABLED (1 << 0) + +#define SVM_NPT_PAE (1 << 0) +#define SVM_NPT_LMA (1 << 1) +#define SVM_NPT_NXE (1 << 2) + +#define SVM_NPTEXIT_P (1ULL << 0) +#define SVM_NPTEXIT_RW (1ULL << 1) +#define SVM_NPTEXIT_US (1ULL << 2) +#define SVM_NPTEXIT_RSVD (1ULL << 3) +#define SVM_NPTEXIT_ID (1ULL << 4) +#define SVM_NPTEXIT_GPA (1ULL << 32) +#define SVM_NPTEXIT_GPT (1ULL << 33) + struct QEMU_PACKED vmcb_control_area { uint16_t intercept_cr_read; uint16_t intercept_cr_write; diff --git a/target/i386/svm_helper.c b/target/i386/svm_helper.c index f245aec310..342ece082f 100644 --- a/target/i386/svm_helper.c +++ b/target/i386/svm_helper.c @@ -124,6 +124,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) { CPUState *cs = CPU(x86_env_get_cpu(env)); target_ulong addr; + uint64_t nested_ctl; uint32_t event_inj; uint32_t int_ctl; @@ -206,6 +207,26 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) control.intercept_exceptions )); + nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.nested_ctl)); + if (nested_ctl & SVM_NPT_ENABLED) { + env->nested_cr3 = x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, + control.nested_cr3)); + env->hflags2 |= HF2_NPT_MASK; + + env->nested_pg_mode = 0; + if (env->cr[4] & CR4_PAE_MASK) { + env->nested_pg_mode |= SVM_NPT_PAE; + } + if (env->hflags & HF_LMA_MASK) { + env->nested_pg_mode |= SVM_NPT_LMA; + } + if (env->efer & MSR_EFER_NXE) { + env->nested_pg_mode |= SVM_NPT_NXE; + } + } + /* enable intercepts */ env->hflags |= HF_SVMI_MASK; @@ -616,6 +637,7 @@ void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0); } + env->hflags2 &= ~HF2_NPT_MASK; /* Save the VM state in the vmcb */ svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.es), From 3afc969a6ee1b4982796c47f75ca06e18159c52c Mon Sep 17 00:00:00 2001 From: Doug Gale Date: Sun, 17 Jun 2018 04:40:25 -0400 Subject: [PATCH 1900/2380] i386/monitor.c: make addresses canonical for "info mem" and "info tlb" Correct the output of the "info mem" and "info tlb" monitor commands to correctly show canonical addresses. In 48-bit addressing mode, the upper 16 bits of linear addresses are equal to bit 47. In 57-bit addressing mode (LA57), the upper 7 bits of linear addresses are equal to bit 56. Signed-off-by: Doug Gale Message-Id: <20180617084025.29198-1-doug16k@gmail.com> Signed-off-by: Paolo Bonzini --- target/i386/monitor.c | 76 +++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 6bbb3a96cd..74a13c571b 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -35,21 +35,28 @@ #include "sev_i386.h" #include "qapi/qapi-commands-misc.h" - -static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr, - hwaddr pte, hwaddr mask) +/* Perform linear address sign extension */ +static hwaddr addr_canonical(CPUArchState *env, hwaddr addr) { #ifdef TARGET_X86_64 if (env->cr[4] & CR4_LA57_MASK) { if (addr & (1ULL << 56)) { - addr |= -1LL << 57; + addr |= (hwaddr)-(1LL << 57); } } else { if (addr & (1ULL << 47)) { - addr |= -1LL << 48; + addr |= (hwaddr)-(1LL << 48); } } #endif + return addr; +} + +static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr, + hwaddr pte, hwaddr mask) +{ + addr = addr_canonical(env, addr); + monitor_printf(mon, TARGET_FMT_plx ": " TARGET_FMT_plx " %c%c%c%c%c%c%c%c%c\n", addr, @@ -243,8 +250,8 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict) } } -static void mem_print(Monitor *mon, hwaddr *pstart, - int *plast_prot, +static void mem_print(Monitor *mon, CPUArchState *env, + hwaddr *pstart, int *plast_prot, hwaddr end, int prot) { int prot1; @@ -253,7 +260,9 @@ static void mem_print(Monitor *mon, hwaddr *pstart, if (*pstart != -1) { monitor_printf(mon, TARGET_FMT_plx "-" TARGET_FMT_plx " " TARGET_FMT_plx " %c%c%c\n", - *pstart, end, end - *pstart, + addr_canonical(env, *pstart), + addr_canonical(env, end), + addr_canonical(env, end - *pstart), prot1 & PG_USER_MASK ? 'u' : '-', 'r', prot1 & PG_RW_MASK ? 'w' : '-'); @@ -283,7 +292,7 @@ static void mem_info_32(Monitor *mon, CPUArchState *env) if (pde & PG_PRESENT_MASK) { if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } else { for(l2 = 0; l2 < 1024; l2++) { cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4); @@ -295,16 +304,16 @@ static void mem_info_32(Monitor *mon, CPUArchState *env) } else { prot = 0; } - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 32, 0); + mem_print(mon, env, &start, &last_prot, (hwaddr)1 << 32, 0); } static void mem_info_pae32(Monitor *mon, CPUArchState *env) @@ -332,7 +341,7 @@ static void mem_info_pae32(Monitor *mon, CPUArchState *env) if (pde & PG_PSE_MASK) { prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } else { pt_addr = pde & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { @@ -345,21 +354,21 @@ static void mem_info_pae32(Monitor *mon, CPUArchState *env) } else { prot = 0; } - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 32, 0); + mem_print(mon, env, &start, &last_prot, (hwaddr)1 << 32, 0); } @@ -389,7 +398,7 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env) prot = pdpe & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); prot &= pml4e; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } else { pd_addr = pdpe & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { @@ -401,7 +410,8 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env) prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); prot &= pml4e & pdpe; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, + &last_prot, end, prot); } else { pt_addr = pde & 0x3fffffffff000ULL; for (l4 = 0; l4 < 512; l4++) { @@ -418,27 +428,29 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env) } else { prot = 0; } - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, + &last_prot, end, prot); } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, + &last_prot, end, prot); } } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 48, 0); + mem_print(mon, env, &start, &last_prot, (hwaddr)1 << 48, 0); } static void mem_info_la57(Monitor *mon, CPUArchState *env) @@ -457,7 +469,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) end = l0 << 48; if (!(pml5e & PG_PRESENT_MASK)) { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -468,7 +480,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) end = (l0 << 48) + (l1 << 39); if (!(pml4e & PG_PRESENT_MASK)) { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -479,7 +491,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) end = (l0 << 48) + (l1 << 39) + (l2 << 30); if (pdpe & PG_PRESENT_MASK) { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -487,7 +499,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) prot = pdpe & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); prot &= pml5e & pml4e; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -498,7 +510,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) end = (l0 << 48) + (l1 << 39) + (l2 << 30) + (l3 << 21); if (pde & PG_PRESENT_MASK) { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -506,7 +518,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); prot &= pml5e & pml4e & pdpe; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -523,14 +535,14 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) } else { prot = 0; } - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } } } } /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 57, 0); + mem_print(mon, env, &start, &last_prot, (hwaddr)1 << 57, 0); } #endif /* TARGET_X86_64 */ From c40d479207b1bb6569ffde06e9a58e85cd529de0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 2 Jul 2018 14:45:25 +0200 Subject: [PATCH 1901/2380] tcg: simplify !CONFIG_TCG handling of tb_invalidate_* There is no need for a stub, since tb_invalidate_phys_addr can be excised altogether when TCG is disabled. This is a bit cleaner since it avoids using code that is clearly specific to user-mode emulation (it calls mmap_lock/unlock) for the !CONFIG_TCG case. Signed-off-by: Paolo Bonzini --- accel/stubs/tcg-stub.c | 6 ------ exec.c | 6 +++++- include/exec/exec-all.h | 8 +++----- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index 8ee85ed665..76ae461749 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -16,7 +16,6 @@ #include "tcg/tcg.h" #include "exec/cpu-common.h" #include "exec/exec-all.h" -#include "translate-all.h" void tb_flush(CPUState *cpu) { @@ -25,8 +24,3 @@ void tb_flush(CPUState *cpu) void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) { } - -void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, - int is_cpu_write_access) -{ -} diff --git a/exec.c b/exec.c index ee726888a5..4f5df07b6a 100644 --- a/exec.c +++ b/exec.c @@ -1027,7 +1027,7 @@ const char *parse_cpu_model(const char *cpu_model) return cpu_type; } -#if defined(CONFIG_USER_ONLY) || !defined(CONFIG_TCG) +#if defined(CONFIG_USER_ONLY) void tb_invalidate_phys_addr(target_ulong addr) { mmap_lock(); @@ -1046,6 +1046,10 @@ void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) MemoryRegion *mr; hwaddr l = 1; + if (!tcg_enabled()) { + return; + } + rcu_read_lock(); mr = address_space_translate(as, addr, &addr, &l, false, attrs); if (!(memory_region_is_ram(mr) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index cb497dee0b..da73e3bfed 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -412,13 +412,11 @@ static inline uint32_t curr_cflags(void) } /* TranslationBlock invalidate API */ -#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs); -#else -void tb_invalidate_phys_addr(target_ulong addr); -#endif #if defined(CONFIG_USER_ONLY) +void tb_invalidate_phys_addr(target_ulong addr); void tb_invalidate_phys_range(target_ulong start, target_ulong end); +#else +void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs); #endif void tb_flush(CPUState *cpu); void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); From a688e73ba8c2e3b6f3e5e683c4a9c37d2ddc8984 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Mon, 25 Jun 2018 12:31:42 -0400 Subject: [PATCH 1902/2380] translate-all: fix locking of TBs whose two pages share the same physical page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 0b5c91f ("translate-all: use per-page locking in !user-mode", 2018-06-15) introduced per-page locking. It assumed that the physical pages corresponding to a TB (at most two pages) are always distinct, which is wrong. For instance, an xtensa test provided by Max Filippov is broken by the commit, since the test maps two virtual pages to the same physical page: virt1: 7fff, virt2: 8000 phys1 6000fff, phys2 6000000 Fix it by removing the assumption from page_lock_pair. If the two physical page addresses are equal, we only lock the PageDesc once. Note that the two callers of page_lock_pair, namely page_unlock_tb and tb_link_page, are also updated so that we do not try to unlock the same PageDesc twice. Fixes: 0b5c91f74f3c83a36f37740969df8c775c997e69 Reported-by: Max Filippov Tested-by: Max Filippov Tested-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Emilio G. Cota Message-Id: <1529944302-14186-1-git-send-email-cota@braap.org> Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index e8228bf3e6..170b95793f 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -669,9 +669,15 @@ static inline void page_lock_tb(const TranslationBlock *tb) static inline void page_unlock_tb(const TranslationBlock *tb) { - page_unlock(page_find(tb->page_addr[0] >> TARGET_PAGE_BITS)); + PageDesc *p1 = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); + + page_unlock(p1); if (unlikely(tb->page_addr[1] != -1)) { - page_unlock(page_find(tb->page_addr[1] >> TARGET_PAGE_BITS)); + PageDesc *p2 = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); + + if (p2 != p1) { + page_unlock(p2); + } } } @@ -850,22 +856,34 @@ static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, PageDesc **ret_p2, tb_page_addr_t phys2, int alloc) { PageDesc *p1, *p2; + tb_page_addr_t page1; + tb_page_addr_t page2; assert_memory_lock(); - g_assert(phys1 != -1 && phys1 != phys2); - p1 = page_find_alloc(phys1 >> TARGET_PAGE_BITS, alloc); + g_assert(phys1 != -1); + + page1 = phys1 >> TARGET_PAGE_BITS; + page2 = phys2 >> TARGET_PAGE_BITS; + + p1 = page_find_alloc(page1, alloc); if (ret_p1) { *ret_p1 = p1; } if (likely(phys2 == -1)) { page_lock(p1); return; + } else if (page1 == page2) { + page_lock(p1); + if (ret_p2) { + *ret_p2 = p1; + } + return; } - p2 = page_find_alloc(phys2 >> TARGET_PAGE_BITS, alloc); + p2 = page_find_alloc(page2, alloc); if (ret_p2) { *ret_p2 = p2; } - if (phys1 < phys2) { + if (page1 < page2) { page_lock(p1); page_lock(p2); } else { @@ -1623,7 +1641,7 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, tb = existing_tb; } - if (p2) { + if (p2 && p2 != p) { page_unlock(p2); } page_unlock(p); From 334692bce7f0653a93b8d84ecde8c847b08dec38 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 29 Jun 2018 17:21:21 +0100 Subject: [PATCH 1903/2380] tcg: Define and use new tlb_hit() and tlb_hit_page() functions The condition to check whether an address has hit against a particular TLB entry is not completely trivial. We do this in various places, and in fact in one place (get_page_addr_code()) we have got the condition wrong. Abstract it out into new tlb_hit() and tlb_hit_page() inline functions (one for a known-page-aligned address and one for an arbitrary address), and use them in all the places where we had the condition correct. This is a no-behaviour-change patch; we leave fixing the buggy code in get_page_addr_code() to a subsequent patch. Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell Message-Id: <20180629162122.19376-2-peter.maydell@linaro.org> Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 15 +++++---------- accel/tcg/softmmu_template.h | 16 ++++++---------- include/exec/cpu-all.h | 23 +++++++++++++++++++++++ include/exec/cpu_ldst.h | 3 +-- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index eebe97dabb..adb711963b 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -239,12 +239,9 @@ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) { - if (addr == (tlb_entry->addr_read & - (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || - addr == (tlb_entry->addr_write & - (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || - addr == (tlb_entry->addr_code & - (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (tlb_hit_page(tlb_entry->addr_read, addr) || + tlb_hit_page(tlb_entry->addr_write, addr) || + tlb_hit_page(tlb_entry->addr_code, addr)) { memset(tlb_entry, -1, sizeof(*tlb_entry)); } } @@ -1046,8 +1043,7 @@ void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, 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))) { + if (!tlb_hit(tlb_addr, addr)) { /* TLB entry is for a different page */ if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE, @@ -1091,8 +1087,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, } /* Check TLB entry and enforce page permissions. */ - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, 1 << s_bits, MMU_DATA_STORE, mmu_idx, retaddr); diff --git a/accel/tcg/softmmu_template.h b/accel/tcg/softmmu_template.h index c47591c970..badbf14880 100644 --- a/accel/tcg/softmmu_template.h +++ b/accel/tcg/softmmu_template.h @@ -123,8 +123,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, } /* If the TLB entry is for a different page, reload and try again. */ - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, mmu_idx, retaddr); @@ -191,8 +190,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, } /* If the TLB entry is for a different page, reload and try again. */ - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, mmu_idx, retaddr); @@ -286,8 +284,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } /* If the TLB entry is for a different page, reload and try again. */ - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); @@ -322,7 +319,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; - if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) + if (!tlb_hit_page(tlb_addr2, page2) && !VICTIM_TLB_HIT(addr_write, page2)) { tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); @@ -364,8 +361,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } /* If the TLB entry is for a different page, reload and try again. */ - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); @@ -400,7 +396,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; - if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) + if (!tlb_hit_page(tlb_addr2, page2) && !VICTIM_TLB_HIT(addr_write, page2)) { tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 7338f57062..117d2fbbca 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -339,6 +339,29 @@ CPUArchState *cpu_copy(CPUArchState *env); #define TLB_FLAGS_MASK (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO \ | TLB_RECHECK) +/** + * tlb_hit_page: return true if page aligned @addr is a hit against the + * TLB entry @tlb_addr + * + * @addr: virtual address to test (must be page aligned) + * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) + */ +static inline bool tlb_hit_page(target_ulong tlb_addr, target_ulong addr) +{ + return addr == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); +} + +/** + * tlb_hit: return true if @addr is a hit against the TLB entry @tlb_addr + * + * @addr: virtual address to test (need not be page aligned) + * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) + */ +static inline bool tlb_hit(target_ulong tlb_addr, target_ulong addr) +{ + return tlb_hit_page(tlb_addr, addr & TARGET_PAGE_MASK); +} + void dump_exec_info(FILE *f, fprintf_function cpu_fprintf); void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf); #endif /* !CONFIG_USER_ONLY */ diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 5de8c8a5af..0f2cb717b1 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -422,8 +422,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, target_ulong addr, g_assert_not_reached(); } - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { /* TLB entry is for a different page */ return NULL; } From e4c967a7201400d7f76e5847d5b4c4ac9e2566e0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 29 Jun 2018 17:21:22 +0100 Subject: [PATCH 1904/2380] accel/tcg: Correct "is this a TLB miss" check in get_page_addr_code() In commit 71b9a45330fe220d1 we changed the condition we use to determine whether we need to refill the TLB in get_page_addr_code() to if (unlikely(env->tlb_table[mmu_idx][index].addr_code != (addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)))) { This isn't the right check (it will falsely fail if the input addr happens to have the low bit corresponding to TLB_INVALID_MASK set, for instance). Replace it with a use of the new tlb_hit() function, which is the correct test. Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell Message-Id: <20180629162122.19376-3-peter.maydell@linaro.org> Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index adb711963b..3ae1198c24 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -957,8 +957,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); mmu_idx = cpu_mmu_index(env, true); - if (unlikely(env->tlb_table[mmu_idx][index].addr_code != - (addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)))) { + if (unlikely(!tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr))) { if (!VICTIM_TLB_HIT(addr_read, addr)) { tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0); } From 4b1a3e1e34ad971b58783d4a24448ccbf5bd8fd4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 29 Jun 2018 17:17:31 +0100 Subject: [PATCH 1905/2380] accel/tcg: Don't treat invalid TLB entries as needing recheck In get_page_addr_code() when we check whether the TLB entry is marked as TLB_RECHECK, we should not go down that code path if the TLB entry is not valid at all (ie the TLB_INVALID bit is set). Tested-by: Laurent Vivier Reported-by: Laurent Vivier Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell Message-Id: <20180629161731.16239-1-peter.maydell@linaro.org> Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 3ae1198c24..cc90a5fe92 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -963,7 +963,8 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) } } - if (unlikely(env->tlb_table[mmu_idx][index].addr_code & TLB_RECHECK)) { + if (unlikely((env->tlb_table[mmu_idx][index].addr_code & + (TLB_RECHECK | TLB_INVALID_MASK)) == TLB_RECHECK)) { /* * This is a TLB_RECHECK access, where the MMU protection * covers a smaller range than a target page, and we must From 68fea038553039e53d425c6399202d13f1e5cfb8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 13:07:08 -0700 Subject: [PATCH 1906/2380] accel/tcg: Avoid caching overwritten tlb entries When installing a TLB entry, remove any cached version of the same page in the VTLB. If the existing TLB entry matches, do not copy into the VTLB, but overwrite it. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 63 ++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index cc90a5fe92..20c147d655 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -235,17 +235,30 @@ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); } - - -static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) +static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry, + target_ulong page) { - if (tlb_hit_page(tlb_entry->addr_read, addr) || - tlb_hit_page(tlb_entry->addr_write, addr) || - tlb_hit_page(tlb_entry->addr_code, addr)) { + return tlb_hit_page(tlb_entry->addr_read, page) || + tlb_hit_page(tlb_entry->addr_write, page) || + tlb_hit_page(tlb_entry->addr_code, page); +} + +static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong page) +{ + if (tlb_hit_page_anyprot(tlb_entry, page)) { memset(tlb_entry, -1, sizeof(*tlb_entry)); } } +static inline void tlb_flush_vtlb_page(CPUArchState *env, int mmu_idx, + target_ulong page) +{ + int k; + for (k = 0; k < CPU_VTLB_SIZE; k++) { + tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], page); + } +} + static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data) { CPUArchState *env = cpu->env_ptr; @@ -271,14 +284,7 @@ static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data) i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); - } - - /* check whether there are entries that need to be flushed in the vtlb */ - for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { - int k; - for (k = 0; k < CPU_VTLB_SIZE; k++) { - tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr); - } + tlb_flush_vtlb_page(env, mmu_idx, addr); } tb_flush_jmp_cache(cpu, addr); @@ -310,7 +316,6 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS; int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); int mmu_idx; - int i; assert_cpu_is_self(cpu); @@ -320,11 +325,7 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { if (test_bit(mmu_idx, &mmu_idx_bitmap)) { tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr); - - /* check whether there are vltb entries that need to be flushed */ - for (i = 0; i < CPU_VTLB_SIZE; i++) { - tlb_flush_entry(&env->tlb_v_table[mmu_idx][i], addr); - } + tlb_flush_vtlb_page(env, mmu_idx, addr); } } @@ -609,10 +610,9 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, target_ulong address; target_ulong code_address; uintptr_t addend; - CPUTLBEntry *te, *tv, tn; + CPUTLBEntry *te, tn; hwaddr iotlb, xlat, sz, paddr_page; target_ulong vaddr_page; - unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; int asidx = cpu_asidx_from_attrs(cpu, attrs); assert_cpu_is_self(cpu); @@ -654,19 +654,28 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat; } + /* Make sure there's no cached translation for the new page. */ + tlb_flush_vtlb_page(env, mmu_idx, vaddr_page); + code_address = address; iotlb = memory_region_section_get_iotlb(cpu, section, vaddr_page, paddr_page, xlat, prot, &address); index = (vaddr_page >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); te = &env->tlb_table[mmu_idx][index]; - /* do not discard the translation in te, evict it into a victim tlb */ - tv = &env->tlb_v_table[mmu_idx][vidx]; - /* addr_write can race with tlb_reset_dirty_range */ - copy_tlb_helper(tv, te, true); + /* + * Only evict the old entry to the victim tlb if it's for a + * different page; otherwise just overwrite the stale data. + */ + if (!tlb_hit_page_anyprot(te, vaddr_page)) { + unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; + CPUTLBEntry *tv = &env->tlb_v_table[mmu_idx][vidx]; - env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; + /* Evict the old entry into the victim tlb. */ + copy_tlb_helper(tv, te, true); + env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; + } /* refill the tlb */ /* From d5cabcce62aeef63afd2b45ec634334df53c70c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 May 2018 08:14:46 -0700 Subject: [PATCH 1907/2380] target/openrisc: Add print_insn_or1k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than emit disassembly while translating, reuse the generated decoder to build a separate disassembler. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/Makefile.objs | 3 +- target/openrisc/cpu.c | 6 ++ target/openrisc/cpu.h | 1 + target/openrisc/disas.c | 170 ++++++++++++++++++++++++++++++++++ target/openrisc/translate.c | 114 ----------------------- 5 files changed, 179 insertions(+), 115 deletions(-) create mode 100644 target/openrisc/disas.c diff --git a/target/openrisc/Makefile.objs b/target/openrisc/Makefile.objs index 1b98a911ea..8b8a890c59 100644 --- a/target/openrisc/Makefile.objs +++ b/target/openrisc/Makefile.objs @@ -1,5 +1,5 @@ obj-$(CONFIG_SOFTMMU) += machine.o -obj-y += cpu.o exception.o interrupt.o mmu.o translate.o +obj-y += cpu.o exception.o interrupt.o mmu.o translate.o disas.o obj-y += exception_helper.o fpu_helper.o \ interrupt_helper.o mmu_helper.o sys_helper.o obj-y += gdbstub.o @@ -12,3 +12,4 @@ target/openrisc/decode.inc.c: \ $(PYTHON) $(DECODETREE) -o $@ $<, "GEN", $(TARGET_DIR)$@) target/openrisc/translate.o: target/openrisc/decode.inc.c +target/openrisc/disas.o: target/openrisc/decode.inc.c diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index a692a98ec0..fa8e342ff7 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -35,6 +35,11 @@ static bool openrisc_cpu_has_work(CPUState *cs) CPU_INTERRUPT_TIMER); } +static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->print_insn = print_insn_or1k; +} + /* CPUClass::reset() */ static void openrisc_cpu_reset(CPUState *s) { @@ -152,6 +157,7 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) #endif cc->gdb_num_core_regs = 32 + 3; cc->tcg_initialize = openrisc_translate_init; + cc->disas_set_info = openrisc_disas_set_info; } /* Sort alphabetically by type name, except for "any". */ diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 35cab65f11..c871d6bfe1 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -358,6 +358,7 @@ void openrisc_translate_init(void); int openrisc_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, int mmu_idx); int cpu_openrisc_signal_handler(int host_signum, void *pinfo, void *puc); +int print_insn_or1k(bfd_vma addr, disassemble_info *info); #define cpu_list cpu_openrisc_list #define cpu_signal_handler cpu_openrisc_signal_handler diff --git a/target/openrisc/disas.c b/target/openrisc/disas.c new file mode 100644 index 0000000000..4bfd2dd8a6 --- /dev/null +++ b/target/openrisc/disas.c @@ -0,0 +1,170 @@ +/* + * OpenRISC disassembler + * + * Copyright (c) 2018 Richard Henderson + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "disas/bfd.h" +#include "qemu/bitops.h" +#include "cpu.h" + +typedef disassemble_info DisasContext; + +/* Include the auto-generated decoder. */ +#include "decode.inc.c" + +#define output(mnemonic, format, ...) \ + (info->fprintf_func(info->stream, "%-9s " format, \ + mnemonic, ##__VA_ARGS__)) + +int print_insn_or1k(bfd_vma addr, disassemble_info *info) +{ + bfd_byte buffer[4]; + uint32_t insn; + int status; + + status = info->read_memory_func(addr, buffer, 4, info); + if (status != 0) { + info->memory_error_func(status, addr, info); + return -1; + } + insn = bfd_getb32(buffer); + + if (!decode(info, insn)) { + output(".long", "%#08x", insn); + } + return 4; +} + +#define INSN(opcode, format, ...) \ +static bool trans_l_##opcode(disassemble_info *info, \ + arg_l_##opcode *a, uint32_t insn) \ +{ \ + output("l." #opcode, format, ##__VA_ARGS__); \ + return true; \ +} + +INSN(add, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(addc, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(sub, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(and, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(or, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(xor, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(sll, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(srl, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(sra, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(ror, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(exths, "r%d, r%d", a->d, a->a) +INSN(extbs, "r%d, r%d", a->d, a->a) +INSN(exthz, "r%d, r%d", a->d, a->a) +INSN(extbz, "r%d, r%d", a->d, a->a) +INSN(cmov, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(ff1, "r%d, r%d", a->d, a->a) +INSN(fl1, "r%d, r%d", a->d, a->a) +INSN(mul, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(mulu, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(div, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(divu, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(muld, "r%d, r%d", a->a, a->b) +INSN(muldu, "r%d, r%d", a->a, a->b) +INSN(j, "%d", a->n) +INSN(jal, "%d", a->n) +INSN(bf, "%d", a->n) +INSN(bnf, "%d", a->n) +INSN(jr, "r%d", a->b) +INSN(jalr, "r%d", a->b) +INSN(lwa, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lwz, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lws, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lbz, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lbs, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lhz, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lhs, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(swa, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(sw, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(sb, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(sh, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(nop, "") +INSN(addi, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(addic, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(muli, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(maci, "r%d, %d", a->a, a->i) +INSN(andi, "r%d, r%d, %d", a->d, a->a, a->k) +INSN(ori, "r%d, r%d, %d", a->d, a->a, a->k) +INSN(xori, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(mfspr, "r%d, r%d, %d", a->d, a->a, a->k) +INSN(mtspr, "r%d, r%d, %d", a->a, a->b, a->k) +INSN(mac, "r%d, r%d", a->a, a->b) +INSN(msb, "r%d, r%d", a->a, a->b) +INSN(macu, "r%d, r%d", a->a, a->b) +INSN(msbu, "r%d, r%d", a->a, a->b) +INSN(slli, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(srli, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(srai, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(rori, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(movhi, "r%d, %d", a->d, a->k) +INSN(macrc, "r%d", a->d) +INSN(sfeq, "r%d, r%d", a->a, a->b) +INSN(sfne, "r%d, r%d", a->a, a->b) +INSN(sfgtu, "r%d, r%d", a->a, a->b) +INSN(sfgeu, "r%d, r%d", a->a, a->b) +INSN(sfltu, "r%d, r%d", a->a, a->b) +INSN(sfleu, "r%d, r%d", a->a, a->b) +INSN(sfgts, "r%d, r%d", a->a, a->b) +INSN(sfges, "r%d, r%d", a->a, a->b) +INSN(sflts, "r%d, r%d", a->a, a->b) +INSN(sfles, "r%d, r%d", a->a, a->b) +INSN(sfeqi, "r%d, %d", a->a, a->i) +INSN(sfnei, "r%d, %d", a->a, a->i) +INSN(sfgtui, "r%d, %d", a->a, a->i) +INSN(sfgeui, "r%d, %d", a->a, a->i) +INSN(sfltui, "r%d, %d", a->a, a->i) +INSN(sfleui, "r%d, %d", a->a, a->i) +INSN(sfgtsi, "r%d, %d", a->a, a->i) +INSN(sfgesi, "r%d, %d", a->a, a->i) +INSN(sfltsi, "r%d, %d", a->a, a->i) +INSN(sflesi, "r%d, %d", a->a, a->i) +INSN(sys, "%d", a->k) +INSN(trap, "%d", a->k) +INSN(msync, "") +INSN(psync, "") +INSN(csync, "") +INSN(rfe, "") + +#define FP_INSN(opcode, suffix, format, ...) \ +static bool trans_lf_##opcode##_##suffix(disassemble_info *info, \ + arg_lf_##opcode##_##suffix *a, uint32_t insn) \ +{ \ + output("lf." #opcode "." #suffix, format, ##__VA_ARGS__); \ + return true; \ +} + +FP_INSN(add, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(sub, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(mul, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(div, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(rem, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(itof, s, "r%d, r%d", a->d, a->a) +FP_INSN(ftoi, s, "r%d, r%d", a->d, a->a) +FP_INSN(madd, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(sfeq, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfne, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfgt, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfge, s, "r%d, r%d", a->a, a->b) +FP_INSN(sflt, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfle, s, "r%d, r%d", a->a, a->b) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index d69f8d0422..fbdc2058dc 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -36,10 +36,6 @@ #include "trace-tcg.h" #include "exec/log.h" -#define LOG_DIS(str, ...) \ - qemu_log_mask(CPU_LOG_TB_IN_ASM, "%08x: " str, dc->base.pc_next, \ - ## __VA_ARGS__) - /* is_jmp field values */ #define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ @@ -457,7 +453,6 @@ static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb) static bool trans_l_add(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.add r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); gen_add(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -465,7 +460,6 @@ static bool trans_l_add(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_addc(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.addc r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); gen_addc(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -473,7 +467,6 @@ static bool trans_l_addc(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_sub(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.sub r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); gen_sub(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -481,7 +474,6 @@ static bool trans_l_sub(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_and(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.and r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_and_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -489,7 +481,6 @@ static bool trans_l_and(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_or(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.or r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_or_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -497,7 +488,6 @@ static bool trans_l_or(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_xor(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.xor r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_xor_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -505,7 +495,6 @@ static bool trans_l_xor(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_sll(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.sll r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_shl_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -513,7 +502,6 @@ static bool trans_l_sll(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_srl(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.srl r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_shr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -521,7 +509,6 @@ static bool trans_l_srl(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_sra(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.sra r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_sar_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -529,7 +516,6 @@ static bool trans_l_sra(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_ror(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.ror r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_rotr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -537,7 +523,6 @@ static bool trans_l_ror(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_exths(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.exths r%d, r%d\n", a->d, a->a); check_r0_write(a->d); tcg_gen_ext16s_tl(cpu_R[a->d], cpu_R[a->a]); return true; @@ -545,7 +530,6 @@ static bool trans_l_exths(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_extbs(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.extbs r%d, r%d\n", a->d, a->a); check_r0_write(a->d); tcg_gen_ext8s_tl(cpu_R[a->d], cpu_R[a->a]); return true; @@ -553,7 +537,6 @@ static bool trans_l_extbs(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_exthz(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.exthz r%d, r%d\n", a->d, a->a); check_r0_write(a->d); tcg_gen_ext16u_tl(cpu_R[a->d], cpu_R[a->a]); return true; @@ -561,7 +544,6 @@ static bool trans_l_exthz(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_extbz(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.extbz r%d, r%d\n", a->d, a->a); check_r0_write(a->d); tcg_gen_ext8u_tl(cpu_R[a->d], cpu_R[a->a]); return true; @@ -570,7 +552,6 @@ static bool trans_l_extbz(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_cmov(DisasContext *dc, arg_dab *a, uint32_t insn) { TCGv zero; - LOG_DIS("l.cmov r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); zero = tcg_const_tl(0); @@ -582,8 +563,6 @@ static bool trans_l_cmov(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_ff1(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.ff1 r%d, r%d\n", a->d, a->a); - check_r0_write(a->d); tcg_gen_ctzi_tl(cpu_R[a->d], cpu_R[a->a], -1); tcg_gen_addi_tl(cpu_R[a->d], cpu_R[a->d], 1); @@ -592,8 +571,6 @@ static bool trans_l_ff1(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_fl1(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.fl1 r%d, r%d\n", a->d, a->a); - check_r0_write(a->d); tcg_gen_clzi_tl(cpu_R[a->d], cpu_R[a->a], TARGET_LONG_BITS); tcg_gen_subfi_tl(cpu_R[a->d], TARGET_LONG_BITS, cpu_R[a->d]); @@ -602,8 +579,6 @@ static bool trans_l_fl1(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_mul(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.mul r%d, r%d, r%d\n", a->d, a->a, a->b); - check_r0_write(a->d); gen_mul(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -611,8 +586,6 @@ static bool trans_l_mul(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_mulu(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.mulu r%d, r%d, r%d\n", a->d, a->a, a->b); - check_r0_write(a->d); gen_mulu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -620,8 +593,6 @@ static bool trans_l_mulu(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_div(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.div r%d, r%d, r%d\n", a->d, a->a, a->b); - check_r0_write(a->d); gen_div(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -629,8 +600,6 @@ static bool trans_l_div(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_divu(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.divu r%d, r%d, r%d\n", a->d, a->a, a->b); - check_r0_write(a->d); gen_divu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -638,14 +607,12 @@ static bool trans_l_divu(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_muld(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.muld r%d, r%d\n", a->a, a->b); gen_muld(dc, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_muldu(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.muldu r%d, r%d\n", a->a, a->b); gen_muldu(dc, cpu_R[a->a], cpu_R[a->b]); return true; } @@ -654,7 +621,6 @@ static bool trans_l_j(DisasContext *dc, arg_l_j *a, uint32_t insn) { target_ulong tmp_pc = dc->base.pc_next + a->n * 4; - LOG_DIS("l.j %d\n", a->n); tcg_gen_movi_tl(jmp_pc, tmp_pc); dc->delayed_branch = 2; return true; @@ -665,7 +631,6 @@ static bool trans_l_jal(DisasContext *dc, arg_l_jal *a, uint32_t insn) target_ulong tmp_pc = dc->base.pc_next + a->n * 4; target_ulong ret_pc = dc->base.pc_next + 8; - LOG_DIS("l.jal %d\n", a->n); tcg_gen_movi_tl(cpu_R[9], ret_pc); /* Optimize jal being used to load the PC for PIC. */ if (tmp_pc != ret_pc) { @@ -692,21 +657,18 @@ static void do_bf(DisasContext *dc, arg_l_bf *a, TCGCond cond) static bool trans_l_bf(DisasContext *dc, arg_l_bf *a, uint32_t insn) { - LOG_DIS("l.bf %d\n", a->n); do_bf(dc, a, TCG_COND_NE); return true; } static bool trans_l_bnf(DisasContext *dc, arg_l_bf *a, uint32_t insn) { - LOG_DIS("l.bnf %d\n", a->n); do_bf(dc, a, TCG_COND_EQ); return true; } static bool trans_l_jr(DisasContext *dc, arg_l_jr *a, uint32_t insn) { - LOG_DIS("l.jr r%d\n", a->b); tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]); dc->delayed_branch = 2; return true; @@ -714,7 +676,6 @@ static bool trans_l_jr(DisasContext *dc, arg_l_jr *a, uint32_t insn) static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a, uint32_t insn) { - LOG_DIS("l.jalr r%d\n", a->b); tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]); tcg_gen_movi_tl(cpu_R[9], dc->base.pc_next + 8); dc->delayed_branch = 2; @@ -725,8 +686,6 @@ static bool trans_l_lwa(DisasContext *dc, arg_load *a, uint32_t insn) { TCGv ea; - LOG_DIS("l.lwa r%d, r%d, %d\n", a->d, a->a, a->i); - check_r0_write(a->d); ea = tcg_temp_new(); tcg_gen_addi_tl(ea, cpu_R[a->a], a->i); @@ -750,42 +709,36 @@ static void do_load(DisasContext *dc, arg_load *a, TCGMemOp mop) static bool trans_l_lwz(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lwz r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_TEUL); return true; } static bool trans_l_lws(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lws r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_TESL); return true; } static bool trans_l_lbz(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lbz r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_UB); return true; } static bool trans_l_lbs(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lbs r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_SB); return true; } static bool trans_l_lhz(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lhz r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_TEUW); return true; } static bool trans_l_lhs(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lhs r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_TESW); return true; } @@ -795,8 +748,6 @@ static bool trans_l_swa(DisasContext *dc, arg_store *a, uint32_t insn) TCGv ea, val; TCGLabel *lab_fail, *lab_done; - LOG_DIS("l.swa r%d, r%d, %d\n", a->a, a->b, a->i); - ea = tcg_temp_new(); tcg_gen_addi_tl(ea, cpu_R[a->a], a->i); @@ -837,28 +788,24 @@ static void do_store(DisasContext *dc, arg_store *a, TCGMemOp mop) static bool trans_l_sw(DisasContext *dc, arg_store *a, uint32_t insn) { - LOG_DIS("l.sw r%d, r%d, %d\n", a->a, a->b, a->i); do_store(dc, a, MO_TEUL); return true; } static bool trans_l_sb(DisasContext *dc, arg_store *a, uint32_t insn) { - LOG_DIS("l.sb r%d, r%d, %d\n", a->a, a->b, a->i); do_store(dc, a, MO_UB); return true; } static bool trans_l_sh(DisasContext *dc, arg_store *a, uint32_t insn) { - LOG_DIS("l.sh r%d, r%d, %d\n", a->a, a->b, a->i); do_store(dc, a, MO_TEUW); return true; } static bool trans_l_nop(DisasContext *dc, arg_l_nop *a, uint32_t insn) { - LOG_DIS("l.nop %d\n", a->k); return true; } @@ -866,7 +813,6 @@ static bool trans_l_addi(DisasContext *dc, arg_rri *a, uint32_t insn) { TCGv t0; - LOG_DIS("l.addi r%d, r%d, %d\n", a->d, a->a, a->i); check_r0_write(a->d); t0 = tcg_const_tl(a->i); gen_add(dc, cpu_R[a->d], cpu_R[a->a], t0); @@ -878,7 +824,6 @@ static bool trans_l_addic(DisasContext *dc, arg_rri *a, uint32_t insn) { TCGv t0; - LOG_DIS("l.addic r%d, r%d, %d\n", a->d, a->a, a->i); check_r0_write(a->d); t0 = tcg_const_tl(a->i); gen_addc(dc, cpu_R[a->d], cpu_R[a->a], t0); @@ -890,7 +835,6 @@ static bool trans_l_muli(DisasContext *dc, arg_rri *a, uint32_t insn) { TCGv t0; - LOG_DIS("l.muli r%d, r%d, %d\n", a->d, a->a, a->i); check_r0_write(a->d); t0 = tcg_const_tl(a->i); gen_mul(dc, cpu_R[a->d], cpu_R[a->a], t0); @@ -902,7 +846,6 @@ static bool trans_l_maci(DisasContext *dc, arg_l_maci *a, uint32_t insn) { TCGv t0; - LOG_DIS("l.maci r%d, %d\n", a->a, a->i); t0 = tcg_const_tl(a->i); gen_mac(dc, cpu_R[a->a], t0); tcg_temp_free(t0); @@ -911,7 +854,6 @@ static bool trans_l_maci(DisasContext *dc, arg_l_maci *a, uint32_t insn) static bool trans_l_andi(DisasContext *dc, arg_rrk *a, uint32_t insn) { - LOG_DIS("l.andi r%d, r%d, %d\n", a->d, a->a, a->k); check_r0_write(a->d); tcg_gen_andi_tl(cpu_R[a->d], cpu_R[a->a], a->k); return true; @@ -919,7 +861,6 @@ static bool trans_l_andi(DisasContext *dc, arg_rrk *a, uint32_t insn) static bool trans_l_ori(DisasContext *dc, arg_rrk *a, uint32_t insn) { - LOG_DIS("l.ori r%d, r%d, %d\n", a->d, a->a, a->k); check_r0_write(a->d); tcg_gen_ori_tl(cpu_R[a->d], cpu_R[a->a], a->k); return true; @@ -927,7 +868,6 @@ static bool trans_l_ori(DisasContext *dc, arg_rrk *a, uint32_t insn) static bool trans_l_xori(DisasContext *dc, arg_rri *a, uint32_t insn) { - LOG_DIS("l.xori r%d, r%d, %d\n", a->d, a->a, a->i); check_r0_write(a->d); tcg_gen_xori_tl(cpu_R[a->d], cpu_R[a->a], a->i); return true; @@ -935,7 +875,6 @@ static bool trans_l_xori(DisasContext *dc, arg_rri *a, uint32_t insn) static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a, uint32_t insn) { - LOG_DIS("l.mfspr r%d, r%d, %d\n", a->d, a->a, a->k); check_r0_write(a->d); #ifdef CONFIG_USER_ONLY @@ -954,8 +893,6 @@ static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a, uint32_t insn) static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) { - LOG_DIS("l.mtspr r%d, r%d, %d\n", a->a, a->b, a->k); - #ifdef CONFIG_USER_ONLY gen_illegal_exception(dc); #else @@ -972,35 +909,30 @@ static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) static bool trans_l_mac(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.mac r%d, r%d\n", a->a, a->b); gen_mac(dc, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_msb(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.msb r%d, r%d\n", a->a, a->b); gen_msb(dc, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_macu(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.mac r%d, r%d\n", a->a, a->b); gen_macu(dc, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_msbu(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.msb r%d, r%d\n", a->a, a->b); gen_msbu(dc, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_slli(DisasContext *dc, arg_dal *a, uint32_t insn) { - LOG_DIS("l.slli r%d, r%d, %d\n", a->d, a->a, a->l); check_r0_write(a->d); tcg_gen_shli_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; @@ -1008,7 +940,6 @@ static bool trans_l_slli(DisasContext *dc, arg_dal *a, uint32_t insn) static bool trans_l_srli(DisasContext *dc, arg_dal *a, uint32_t insn) { - LOG_DIS("l.srli r%d, r%d, %d\n", a->d, a->a, a->l); check_r0_write(a->d); tcg_gen_shri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; @@ -1016,7 +947,6 @@ static bool trans_l_srli(DisasContext *dc, arg_dal *a, uint32_t insn) static bool trans_l_srai(DisasContext *dc, arg_dal *a, uint32_t insn) { - LOG_DIS("l.srai r%d, r%d, %d\n", a->d, a->a, a->l); check_r0_write(a->d); tcg_gen_sari_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; @@ -1024,7 +954,6 @@ static bool trans_l_srai(DisasContext *dc, arg_dal *a, uint32_t insn) static bool trans_l_rori(DisasContext *dc, arg_dal *a, uint32_t insn) { - LOG_DIS("l.rori r%d, r%d, %d\n", a->d, a->a, a->l); check_r0_write(a->d); tcg_gen_rotri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; @@ -1032,7 +961,6 @@ static bool trans_l_rori(DisasContext *dc, arg_dal *a, uint32_t insn) static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a, uint32_t insn) { - LOG_DIS("l.movhi r%d, %d\n", a->d, a->k); check_r0_write(a->d); tcg_gen_movi_tl(cpu_R[a->d], a->k << 16); return true; @@ -1040,7 +968,6 @@ static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a, uint32_t insn) static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn) { - LOG_DIS("l.macrc r%d\n", a->d); check_r0_write(a->d); tcg_gen_trunc_i64_tl(cpu_R[a->d], cpu_mac); tcg_gen_movi_i64(cpu_mac, 0); @@ -1049,147 +976,126 @@ static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn) static bool trans_l_sfeq(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfeq r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfne(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfne r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfgtu(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfgtu r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfgeu(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfgeu r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfltu(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfltu r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfleu(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfleu r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfgts(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfgts r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfges(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfges r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sflts(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sflts r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfles(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfles r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfeqi(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfeqi r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfnei(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfnei r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfgtui(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfgtui r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfgeui(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfgeui r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfltui(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfltui r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfleui(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfleui r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfgtsi(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfgtsi r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfgesi(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfgesi r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfltsi(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfltsi r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sflesi(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sflesi r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn) { - LOG_DIS("l.sys %d\n", a->k); tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_SYSCALL); dc->base.is_jmp = DISAS_NORETURN; @@ -1198,7 +1104,6 @@ static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn) static bool trans_l_trap(DisasContext *dc, arg_l_trap *a, uint32_t insn) { - LOG_DIS("l.trap %d\n", a->k); tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_TRAP); dc->base.is_jmp = DISAS_NORETURN; @@ -1207,27 +1112,22 @@ static bool trans_l_trap(DisasContext *dc, arg_l_trap *a, uint32_t insn) static bool trans_l_msync(DisasContext *dc, arg_l_msync *a, uint32_t insn) { - LOG_DIS("l.msync\n"); tcg_gen_mb(TCG_MO_ALL); return true; } static bool trans_l_psync(DisasContext *dc, arg_l_psync *a, uint32_t insn) { - LOG_DIS("l.psync\n"); return true; } static bool trans_l_csync(DisasContext *dc, arg_l_csync *a, uint32_t insn) { - LOG_DIS("l.csync\n"); return true; } static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a, uint32_t insn) { - LOG_DIS("l.rfe\n"); - #ifdef CONFIG_USER_ONLY gen_illegal_exception(dc); #else @@ -1274,56 +1174,48 @@ static void do_fpcmp(DisasContext *dc, arg_ab *a, static bool trans_lf_add_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.add.s r%d, r%d, r%d\n", a->d, a->a, a->b); do_fp3(dc, a, gen_helper_float_add_s); return true; } static bool trans_lf_sub_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.sub.s r%d, r%d, r%d\n", a->d, a->a, a->b); do_fp3(dc, a, gen_helper_float_sub_s); return true; } static bool trans_lf_mul_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.mul.s r%d, r%d, r%d\n", a->d, a->a, a->b); do_fp3(dc, a, gen_helper_float_mul_s); return true; } static bool trans_lf_div_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.div.s r%d, r%d, r%d\n", a->d, a->a, a->b); do_fp3(dc, a, gen_helper_float_div_s); return true; } static bool trans_lf_rem_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.rem.s r%d, r%d, r%d\n", a->d, a->a, a->b); do_fp3(dc, a, gen_helper_float_rem_s); return true; } static bool trans_lf_itof_s(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("lf.itof.s r%d, r%d\n", a->d, a->a); do_fp2(dc, a, gen_helper_itofs); return true; } static bool trans_lf_ftoi_s(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("lf.ftoi.s r%d, r%d\n", a->d, a->a); do_fp2(dc, a, gen_helper_ftois); return true; } static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.madd.s r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); gen_helper_float_madd_s(cpu_R[a->d], cpu_env, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); @@ -1333,42 +1225,36 @@ static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_lf_sfeq_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sfeq.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_eq_s, false, false); return true; } static bool trans_lf_sfne_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sfne.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_eq_s, true, false); return true; } static bool trans_lf_sfgt_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sfgt.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_lt_s, false, true); return true; } static bool trans_lf_sfge_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sfge.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_le_s, false, true); return true; } static bool trans_lf_sflt_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sflt.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_lt_s, false, false); return true; } static bool trans_lf_sfle_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sfle.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_le_s, false, false); return true; } From 378cd36f3cfbc601381439a852fc68126b60131e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 21:34:38 -0700 Subject: [PATCH 1908/2380] target/openrisc: Log interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/interrupt.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index 3959671c59..25351d5de3 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -32,6 +32,7 @@ void openrisc_cpu_do_interrupt(CPUState *cs) #ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = OPENRISC_CPU(cs); CPUOpenRISCState *env = &cpu->env; + int exception = cs->exception_index; env->epcr = env->pc; if (env->dflag) { @@ -41,12 +42,12 @@ void openrisc_cpu_do_interrupt(CPUState *cs) } else { env->sr &= ~SR_DSX; } - if (cs->exception_index == EXCP_SYSCALL) { + if (exception == EXCP_SYSCALL) { env->epcr += 4; } /* When we have an illegal instruction the error effective address shall be set to the illegal instruction address. */ - if (cs->exception_index == EXCP_ILLEGAL) { + if (exception == EXCP_ILLEGAL) { env->eear = env->pc; } @@ -66,8 +67,27 @@ void openrisc_cpu_do_interrupt(CPUState *cs) env->tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; env->lock_addr = -1; - if (cs->exception_index > 0 && cs->exception_index < EXCP_NR) { - hwaddr vect_pc = cs->exception_index << 8; + if (exception > 0 && exception < EXCP_NR) { + static const char * const int_name[EXCP_NR] = { + [EXCP_RESET] = "RESET", + [EXCP_BUSERR] = "BUSERR (bus error)", + [EXCP_DPF] = "DFP (data protection fault)", + [EXCP_IPF] = "IPF (code protection fault)", + [EXCP_TICK] = "TICK (timer interrupt)", + [EXCP_ALIGN] = "ALIGN", + [EXCP_ILLEGAL] = "ILLEGAL", + [EXCP_INT] = "INT (device interrupt)", + [EXCP_DTLBMISS] = "DTLBMISS (data tlb miss)", + [EXCP_ITLBMISS] = "ITLBMISS (code tlb miss)", + [EXCP_RANGE] = "RANGE", + [EXCP_SYSCALL] = "SYSCALL", + [EXCP_FPE] = "FPE", + [EXCP_TRAP] = "TRAP", + }; + + qemu_log_mask(CPU_LOG_INT, "INT: %s\n", int_name[exception]); + + hwaddr vect_pc = exception << 8; if (env->cpucfgr & CPUCFGR_EVBARP) { vect_pc |= env->evbar; } @@ -76,7 +96,7 @@ void openrisc_cpu_do_interrupt(CPUState *cs) } env->pc = vect_pc; } else { - cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); + cpu_abort(cs, "Unhandled exception 0x%x\n", exception); } #endif From c86395c850d43047e65892ea41942c18eb29a39d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 14:46:10 -0700 Subject: [PATCH 1909/2380] target/openrisc: Remove DISAS_JUMP & DISAS_TB_JUMP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These values are unused. Reviewed-by: Stafford Horne Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/translate.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index fbdc2058dc..f5af515979 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -37,9 +37,7 @@ #include "exec/log.h" /* is_jmp field values */ -#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ -#define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ typedef struct DisasContext { DisasContextBase base; @@ -1353,8 +1351,6 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) gen_goto_tb(dc, 0, dc->base.pc_next); break; case DISAS_NORETURN: - case DISAS_JUMP: - case DISAS_TB_JUMP: break; case DISAS_UPDATE: /* indicate that the hash table must be used From 64e46c958162d986cc6e0cdfd6fb8f11b6f31748 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 15:02:23 -0700 Subject: [PATCH 1910/2380] target/openrisc: Use exit_tb instead of CPU_INTERRUPT_EXITTB No need to use the interrupt mechanisms when we can simply exit the tb directly. Reviewed-by: Stafford Horne Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/interrupt_helper.c | 3 +-- target/openrisc/translate.c | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/target/openrisc/interrupt_helper.c b/target/openrisc/interrupt_helper.c index 56620e0571..b865738f8b 100644 --- a/target/openrisc/interrupt_helper.c +++ b/target/openrisc/interrupt_helper.c @@ -26,7 +26,6 @@ void HELPER(rfe)(CPUOpenRISCState *env) { OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - CPUState *cs = CPU(cpu); #ifndef CONFIG_USER_ONLY int need_flush_tlb = (cpu->env.sr & (SR_SM | SR_IME | SR_DME)) ^ (cpu->env.esr & (SR_SM | SR_IME | SR_DME)); @@ -53,8 +52,8 @@ void HELPER(rfe)(CPUOpenRISCState *env) } if (need_flush_tlb) { + CPUState *cs = CPU(cpu); tlb_flush(cs); } #endif - cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index f5af515979..43bdf378eb 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -37,6 +37,7 @@ #include "exec/log.h" /* is_jmp field values */ +#define DISAS_EXIT DISAS_TARGET_0 /* force exit to main loop */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ typedef struct DisasContext { @@ -1133,7 +1134,7 @@ static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a, uint32_t insn) gen_illegal_exception(dc); } else { gen_helper_rfe(cpu_env); - dc->base.is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_EXIT; } #endif return true; @@ -1353,8 +1354,7 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) case DISAS_NORETURN: break; case DISAS_UPDATE: - /* indicate that the hash table must be used - to find the next TB */ + case DISAS_EXIT: tcg_gen_exit_tb(NULL, 0); break; default: From e0a369cf88041a71cf5d902f1d78c9a645a6709e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 15:15:12 -0700 Subject: [PATCH 1911/2380] target/openrisc: Fix singlestep_enabled We failed to store to cpu_pc before raising the exception, which caused us to re-execute the same insn that we stepped. Reviewed-by: Stafford Horne Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/translate.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 43bdf378eb..22848b17ad 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1335,31 +1335,30 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); + /* If we have already exited the TB, nothing following has effect. */ + if (dc->base.is_jmp == DISAS_NORETURN) { + return; + } + if ((dc->tb_flags & TB_FLAGS_DFLAG ? 1 : 0) != (dc->delayed_branch != 0)) { tcg_gen_movi_i32(cpu_dflag, dc->delayed_branch != 0); } tcg_gen_movi_tl(cpu_ppc, dc->base.pc_next - 4); - if (dc->base.is_jmp == DISAS_NEXT) { - dc->base.is_jmp = DISAS_UPDATE; - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - } - if (unlikely(dc->base.singlestep_enabled)) { - gen_exception(dc, EXCP_DEBUG); - } else { - switch (dc->base.is_jmp) { - case DISAS_TOO_MANY: - gen_goto_tb(dc, 0, dc->base.pc_next); - break; - case DISAS_NORETURN: - break; - case DISAS_UPDATE: - case DISAS_EXIT: + switch (dc->base.is_jmp) { + case DISAS_TOO_MANY: + gen_goto_tb(dc, 0, dc->base.pc_next); + break; + case DISAS_UPDATE: + case DISAS_EXIT: + if (unlikely(dc->base.singlestep_enabled)) { + gen_exception(dc, EXCP_DEBUG); + } else { tcg_gen_exit_tb(NULL, 0); - break; - default: - g_assert_not_reached(); } + break; + default: + g_assert_not_reached(); } } From 8000ba56cc534dd917ae54f6ef0e898813c12b24 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 15:46:31 -0700 Subject: [PATCH 1912/2380] target/openrisc: Link more translation blocks Track direct jumps via dc->jmp_pc_imm. Use that in preference to jmp_pc when possible. Emit goto_tb in that case, and lookup_and_goto_tb otherwise. Reviewed-by: Stafford Horne Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/translate.c | 96 +++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 22848b17ad..a618d39242 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -38,13 +38,16 @@ /* is_jmp field values */ #define DISAS_EXIT DISAS_TARGET_0 /* force exit to main loop */ -#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ +#define DISAS_JUMP DISAS_TARGET_1 /* exit via jmp_pc/jmp_pc_imm */ typedef struct DisasContext { DisasContextBase base; uint32_t mem_idx; uint32_t tb_flags; uint32_t delayed_branch; + + /* If not -1, jmp_pc contains this value and so is a direct jump. */ + target_ulong jmp_pc_imm; } DisasContext; /* Include the auto-generated decoder. */ @@ -160,34 +163,6 @@ static void check_ov64s(DisasContext *dc) } \ } while (0) -static inline bool use_goto_tb(DisasContext *dc, target_ulong dest) -{ - if (unlikely(dc->base.singlestep_enabled)) { - return false; - } - -#ifndef CONFIG_USER_ONLY - return (dc->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif -} - -static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) -{ - if (use_goto_tb(dc, dest)) { - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_goto_tb(n); - tcg_gen_exit_tb(dc->base.tb, n); - } else { - tcg_gen_movi_tl(cpu_pc, dest); - if (dc->base.singlestep_enabled) { - gen_exception(dc, EXCP_DEBUG); - } - tcg_gen_exit_tb(NULL, 0); - } -} - static void gen_ove_cy(DisasContext *dc) { if (dc->tb_flags & SR_OVE) { @@ -621,6 +596,7 @@ static bool trans_l_j(DisasContext *dc, arg_l_j *a, uint32_t insn) target_ulong tmp_pc = dc->base.pc_next + a->n * 4; tcg_gen_movi_tl(jmp_pc, tmp_pc); + dc->jmp_pc_imm = tmp_pc; dc->delayed_branch = 2; return true; } @@ -634,6 +610,7 @@ static bool trans_l_jal(DisasContext *dc, arg_l_jal *a, uint32_t insn) /* Optimize jal being used to load the PC for PIC. */ if (tmp_pc != ret_pc) { tcg_gen_movi_tl(jmp_pc, tmp_pc); + dc->jmp_pc_imm = tmp_pc; dc->delayed_branch = 2; } return true; @@ -1267,6 +1244,8 @@ static void openrisc_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) dc->mem_idx = cpu_mmu_index(env, false); dc->tb_flags = dc->base.tb->flags; dc->delayed_branch = (dc->tb_flags & TB_FLAGS_DFLAG) != 0; + dc->jmp_pc_imm = -1; + bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; dc->base.max_insns = MIN(dc->base.max_insns, bound); } @@ -1319,37 +1298,72 @@ static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) } dc->base.pc_next += 4; - /* delay slot */ - if (dc->delayed_branch) { - dc->delayed_branch--; - if (!dc->delayed_branch) { - tcg_gen_mov_tl(cpu_pc, jmp_pc); - tcg_gen_discard_tl(jmp_pc); - dc->base.is_jmp = DISAS_UPDATE; - return; - } + /* When exiting the delay slot normally, exit via jmp_pc. + * For DISAS_NORETURN, we have raised an exception and already exited. + * For DISAS_EXIT, we found l.rfe in a delay slot. There's nothing + * in the manual saying this is illegal, but it surely it should. + * At least or1ksim overrides pcnext and ignores the branch. + */ + if (dc->delayed_branch + && --dc->delayed_branch == 0 + && dc->base.is_jmp == DISAS_NEXT) { + dc->base.is_jmp = DISAS_JUMP; } } static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); + target_ulong jmp_dest; /* If we have already exited the TB, nothing following has effect. */ if (dc->base.is_jmp == DISAS_NORETURN) { return; } + /* Adjust the delayed branch state for the next TB. */ if ((dc->tb_flags & TB_FLAGS_DFLAG ? 1 : 0) != (dc->delayed_branch != 0)) { tcg_gen_movi_i32(cpu_dflag, dc->delayed_branch != 0); } - tcg_gen_movi_tl(cpu_ppc, dc->base.pc_next - 4); + /* For DISAS_TOO_MANY, jump to the next insn. */ + jmp_dest = dc->base.pc_next; + tcg_gen_movi_tl(cpu_ppc, jmp_dest - 4); + switch (dc->base.is_jmp) { + case DISAS_JUMP: + jmp_dest = dc->jmp_pc_imm; + if (jmp_dest == -1) { + /* The jump destination is indirect/computed; use jmp_pc. */ + tcg_gen_mov_tl(cpu_pc, jmp_pc); + tcg_gen_discard_tl(jmp_pc); + if (unlikely(dc->base.singlestep_enabled)) { + gen_exception(dc, EXCP_DEBUG); + } else { + tcg_gen_lookup_and_goto_ptr(); + } + break; + } + /* The jump destination is direct; use jmp_pc_imm. + However, we will have stored into jmp_pc as well; + we know now that it wasn't needed. */ + tcg_gen_discard_tl(jmp_pc); + /* fallthru */ + case DISAS_TOO_MANY: - gen_goto_tb(dc, 0, dc->base.pc_next); + if (unlikely(dc->base.singlestep_enabled)) { + tcg_gen_movi_tl(cpu_pc, jmp_dest); + gen_exception(dc, EXCP_DEBUG); + } else if ((dc->base.pc_first ^ jmp_dest) & TARGET_PAGE_MASK) { + tcg_gen_movi_tl(cpu_pc, jmp_dest); + tcg_gen_lookup_and_goto_ptr(); + } else { + tcg_gen_goto_tb(0); + tcg_gen_movi_tl(cpu_pc, jmp_dest); + tcg_gen_exit_tb(dc->base.tb, 0); + } break; - case DISAS_UPDATE: + case DISAS_EXIT: if (unlikely(dc->base.singlestep_enabled)) { gen_exception(dc, EXCP_DEBUG); From 2ba6541792782fc0d20dab6678f5f90a3c8978c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 18:26:18 -0700 Subject: [PATCH 1913/2380] target/openrisc: Split out is_user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to limit the amount of ifdefs and isolate the test for usermode. Reviewed-by: Stafford Horne Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/translate.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index a618d39242..db149986af 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -50,6 +50,15 @@ typedef struct DisasContext { target_ulong jmp_pc_imm; } DisasContext; +static inline bool is_user(DisasContext *dc) +{ +#ifdef CONFIG_USER_ONLY + return true; +#else + return dc->mem_idx == MMU_USER_IDX; +#endif +} + /* Include the auto-generated decoder. */ #include "decode.inc.c" @@ -853,33 +862,25 @@ static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a, uint32_t insn) { check_r0_write(a->d); -#ifdef CONFIG_USER_ONLY - gen_illegal_exception(dc); -#else - if (dc->mem_idx == MMU_USER_IDX) { + if (is_user(dc)) { gen_illegal_exception(dc); } else { TCGv_i32 ti = tcg_const_i32(a->k); gen_helper_mfspr(cpu_R[a->d], cpu_env, cpu_R[a->d], cpu_R[a->a], ti); tcg_temp_free_i32(ti); } -#endif return true; } static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) { -#ifdef CONFIG_USER_ONLY - gen_illegal_exception(dc); -#else - if (dc->mem_idx == MMU_USER_IDX) { + if (is_user(dc)) { gen_illegal_exception(dc); } else { TCGv_i32 ti = tcg_const_i32(a->k); gen_helper_mtspr(cpu_env, cpu_R[a->a], cpu_R[a->b], ti); tcg_temp_free_i32(ti); } -#endif return true; } @@ -1104,16 +1105,12 @@ static bool trans_l_csync(DisasContext *dc, arg_l_csync *a, uint32_t insn) static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a, uint32_t insn) { -#ifdef CONFIG_USER_ONLY - gen_illegal_exception(dc); -#else - if (dc->mem_idx == MMU_USER_IDX) { + if (is_user(dc)) { gen_illegal_exception(dc); } else { gen_helper_rfe(cpu_env); dc->base.is_jmp = DISAS_EXIT; } -#endif return true; } From 01ec3ec930c90374a8870e99e0da63c17d708d47 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 18:42:13 -0700 Subject: [PATCH 1914/2380] target/openrisc: Exit the TB after l.mtspr A store to SR changes interrupt state, which should return to the main loop to recognize that state. Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/translate.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index db149986af..59605aacca 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -877,7 +877,22 @@ static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) if (is_user(dc)) { gen_illegal_exception(dc); } else { - TCGv_i32 ti = tcg_const_i32(a->k); + TCGv_i32 ti; + + /* For SR, we will need to exit the TB to recognize the new + * exception state. For NPC, in theory this counts as a branch + * (although the SPR only exists for use by an ICE). Save all + * of the cpu state first, allowing it to be overwritten. + */ + if (dc->delayed_branch) { + tcg_gen_mov_tl(cpu_pc, jmp_pc); + tcg_gen_discard_tl(jmp_pc); + } else { + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4); + } + dc->base.is_jmp = DISAS_EXIT; + + ti = tcg_const_i32(a->k); gen_helper_mtspr(cpu_env, cpu_R[a->a], cpu_R[a->b], ti); tcg_temp_free_i32(ti); } From c28fa81f915b03834b00187e43604e42768f15fa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 May 2018 07:13:26 -0700 Subject: [PATCH 1915/2380] target/openrisc: Form the spr index from tcg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than pass base+offset to the helper, pass the full index. In most cases the base is r0 and optimization yields a constant. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/helper.h | 4 ++-- target/openrisc/sys_helper.c | 9 +++------ target/openrisc/translate.c | 16 +++++++++------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/target/openrisc/helper.h b/target/openrisc/helper.h index e37dabc77a..9db9bf3963 100644 --- a/target/openrisc/helper.h +++ b/target/openrisc/helper.h @@ -56,5 +56,5 @@ FOP_CMP(le) DEF_HELPER_FLAGS_1(rfe, 0, void, env) /* sys */ -DEF_HELPER_FLAGS_4(mtspr, 0, void, env, tl, tl, tl) -DEF_HELPER_FLAGS_4(mfspr, TCG_CALL_NO_WG, tl, env, tl, tl, tl) +DEF_HELPER_FLAGS_3(mtspr, 0, void, env, tl, tl) +DEF_HELPER_FLAGS_3(mfspr, TCG_CALL_NO_WG, tl, env, tl, tl) diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 2f337363ec..2c959f63f4 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -27,13 +27,11 @@ #define TO_SPR(group, number) (((group) << 11) + (number)) -void HELPER(mtspr)(CPUOpenRISCState *env, - target_ulong ra, target_ulong rb, target_ulong offset) +void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) { #ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = openrisc_env_get_cpu(env); CPUState *cs = CPU(cpu); - int spr = (ra | offset); int idx; switch (spr) { @@ -202,13 +200,12 @@ void HELPER(mtspr)(CPUOpenRISCState *env, #endif } -target_ulong HELPER(mfspr)(CPUOpenRISCState *env, - target_ulong rd, target_ulong ra, uint32_t offset) +target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, + target_ulong spr) { #ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = openrisc_env_get_cpu(env); CPUState *cs = CPU(cpu); - int spr = (ra | offset); int idx; switch (spr) { diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 59605aacca..64b5e84630 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -865,9 +865,10 @@ static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a, uint32_t insn) if (is_user(dc)) { gen_illegal_exception(dc); } else { - TCGv_i32 ti = tcg_const_i32(a->k); - gen_helper_mfspr(cpu_R[a->d], cpu_env, cpu_R[a->d], cpu_R[a->a], ti); - tcg_temp_free_i32(ti); + TCGv spr = tcg_temp_new(); + tcg_gen_ori_tl(spr, cpu_R[a->a], a->k); + gen_helper_mfspr(cpu_R[a->d], cpu_env, cpu_R[a->d], spr); + tcg_temp_free(spr); } return true; } @@ -877,7 +878,7 @@ static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) if (is_user(dc)) { gen_illegal_exception(dc); } else { - TCGv_i32 ti; + TCGv spr; /* For SR, we will need to exit the TB to recognize the new * exception state. For NPC, in theory this counts as a branch @@ -892,9 +893,10 @@ static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) } dc->base.is_jmp = DISAS_EXIT; - ti = tcg_const_i32(a->k); - gen_helper_mtspr(cpu_env, cpu_R[a->a], cpu_R[a->b], ti); - tcg_temp_free_i32(ti); + spr = tcg_temp_new(); + tcg_gen_ori_tl(spr, cpu_R[a->a], a->k); + gen_helper_mtspr(cpu_env, spr, cpu_R[a->b]); + tcg_temp_free(spr); } return true; } From 455d45d22cc3b2c29c7840f2478647a0a3d9d8b4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 16:28:33 -0700 Subject: [PATCH 1916/2380] target/openrisc: Merge tlb allocation into CPUOpenRISCState There is no reason to allocate this separately. This was probably copied from target/mips which makes the same mistake. While doing so, move tlb into the clear-on-reset range. While not all of the TLB bits are guaranteed zero on reset, all of the valid bits are cleared, and the rest of the bits are unspecified. Therefore clearing the whole of the TLB is correct. Reviewed-by: Stafford Horne Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/cpu.h | 6 ++++-- target/openrisc/interrupt.c | 4 ++-- target/openrisc/interrupt_helper.c | 8 +++---- target/openrisc/machine.c | 15 ++++++------- target/openrisc/mmu.c | 34 ++++++++++++++---------------- target/openrisc/sys_helper.c | 28 ++++++++++++------------ 6 files changed, 46 insertions(+), 49 deletions(-) diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index c871d6bfe1..96b7f58659 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -301,6 +301,10 @@ typedef struct CPUOpenRISCState { uint32_t dflag; /* In delay slot (boolean) */ +#ifndef CONFIG_USER_ONLY + CPUOpenRISCTLBContext tlb; +#endif + /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; @@ -310,8 +314,6 @@ typedef struct CPUOpenRISCState { uint32_t cpucfgr; /* CPU configure register */ #ifndef CONFIG_USER_ONLY - CPUOpenRISCTLBContext * tlb; - QEMUTimer *timer; uint32_t ttmr; /* Timer tick mode register */ int is_counting; diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index 25351d5de3..2d0b55afa9 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -63,8 +63,8 @@ void openrisc_cpu_do_interrupt(CPUState *cs) env->sr &= ~SR_TEE; env->pmr &= ~PMR_DME; env->pmr &= ~PMR_SME; - env->tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; - env->tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; + env->tlb.cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; + env->tlb.cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; env->lock_addr = -1; if (exception > 0 && exception < EXCP_NR) { diff --git a/target/openrisc/interrupt_helper.c b/target/openrisc/interrupt_helper.c index b865738f8b..dc97b38704 100644 --- a/target/openrisc/interrupt_helper.c +++ b/target/openrisc/interrupt_helper.c @@ -36,18 +36,18 @@ void HELPER(rfe)(CPUOpenRISCState *env) #ifndef CONFIG_USER_ONLY if (cpu->env.sr & SR_DME) { - cpu->env.tlb->cpu_openrisc_map_address_data = + cpu->env.tlb.cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_data; } else { - cpu->env.tlb->cpu_openrisc_map_address_data = + cpu->env.tlb.cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; } if (cpu->env.sr & SR_IME) { - cpu->env.tlb->cpu_openrisc_map_address_code = + cpu->env.tlb.cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_code; } else { - cpu->env.tlb->cpu_openrisc_map_address_code = + cpu->env.tlb.cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; } diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index 0a793eb14d..c10d28b055 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -30,18 +30,18 @@ static int env_post_load(void *opaque, int version_id) /* Restore MMU handlers */ if (env->sr & SR_DME) { - env->tlb->cpu_openrisc_map_address_data = + env->tlb.cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_data; } else { - env->tlb->cpu_openrisc_map_address_data = + env->tlb.cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; } if (env->sr & SR_IME) { - env->tlb->cpu_openrisc_map_address_code = + env->tlb.cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_code; } else { - env->tlb->cpu_openrisc_map_address_code = + env->tlb.cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; } @@ -77,10 +77,6 @@ static const VMStateDescription vmstate_cpu_tlb = { } }; -#define VMSTATE_CPU_TLB(_f, _s) \ - VMSTATE_STRUCT_POINTER(_f, _s, vmstate_cpu_tlb, CPUOpenRISCTLBContext) - - static int get_sr(QEMUFile *f, void *opaque, size_t size, VMStateField *field) { CPUOpenRISCState *env = opaque; @@ -143,7 +139,8 @@ static const VMStateDescription vmstate_env = { VMSTATE_UINT32(fpcsr, CPUOpenRISCState), VMSTATE_UINT64(mac, CPUOpenRISCState), - VMSTATE_CPU_TLB(tlb, CPUOpenRISCState), + VMSTATE_STRUCT(tlb, CPUOpenRISCState, 1, + vmstate_cpu_tlb, CPUOpenRISCTLBContext), VMSTATE_TIMER_PTR(timer, CPUOpenRISCState), VMSTATE_UINT32(ttmr, CPUOpenRISCState), diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index 2bd782f89b..5665bb7cc9 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -46,19 +46,19 @@ int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, int idx = vpn & ITLB_MASK; int right = 0; - if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { + if ((cpu->env.tlb.itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { return TLBRET_NOMATCH; } - if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) { + if (!(cpu->env.tlb.itlb[0][idx].mr & 1)) { return TLBRET_INVALID; } if (cpu->env.sr & SR_SM) { /* supervisor mode */ - if (cpu->env.tlb->itlb[0][idx].tr & SXE) { + if (cpu->env.tlb.itlb[0][idx].tr & SXE) { right |= PAGE_EXEC; } } else { - if (cpu->env.tlb->itlb[0][idx].tr & UXE) { + if (cpu->env.tlb.itlb[0][idx].tr & UXE) { right |= PAGE_EXEC; } } @@ -67,7 +67,7 @@ int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, return TLBRET_BADADDR; } - *physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) | + *physical = (cpu->env.tlb.itlb[0][idx].tr & TARGET_PAGE_MASK) | (address & (TARGET_PAGE_SIZE-1)); *prot = right; return TLBRET_MATCH; @@ -81,25 +81,25 @@ int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, int idx = vpn & DTLB_MASK; int right = 0; - if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { + if ((cpu->env.tlb.dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { return TLBRET_NOMATCH; } - if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) { + if (!(cpu->env.tlb.dtlb[0][idx].mr & 1)) { return TLBRET_INVALID; } if (cpu->env.sr & SR_SM) { /* supervisor mode */ - if (cpu->env.tlb->dtlb[0][idx].tr & SRE) { + if (cpu->env.tlb.dtlb[0][idx].tr & SRE) { right |= PAGE_READ; } - if (cpu->env.tlb->dtlb[0][idx].tr & SWE) { + if (cpu->env.tlb.dtlb[0][idx].tr & SWE) { right |= PAGE_WRITE; } } else { - if (cpu->env.tlb->dtlb[0][idx].tr & URE) { + if (cpu->env.tlb.dtlb[0][idx].tr & URE) { right |= PAGE_READ; } - if (cpu->env.tlb->dtlb[0][idx].tr & UWE) { + if (cpu->env.tlb.dtlb[0][idx].tr & UWE) { right |= PAGE_WRITE; } } @@ -111,7 +111,7 @@ int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, return TLBRET_BADADDR; } - *physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) | + *physical = (cpu->env.tlb.dtlb[0][idx].tr & TARGET_PAGE_MASK) | (address & (TARGET_PAGE_SIZE-1)); *prot = right; return TLBRET_MATCH; @@ -126,10 +126,10 @@ static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu, if (rw == MMU_INST_FETCH) { /* ITLB */ *physical = 0; - ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical, + ret = cpu->env.tlb.cpu_openrisc_map_address_code(cpu, physical, prot, address, rw); } else { /* DTLB */ - ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical, + ret = cpu->env.tlb.cpu_openrisc_map_address_data(cpu, physical, prot, address, rw); } @@ -247,9 +247,7 @@ hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) void cpu_openrisc_mmu_init(OpenRISCCPU *cpu) { - cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext)); - - cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; - cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; + cpu->env.tlb.cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; + cpu->env.tlb.cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; } #endif diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 2c959f63f4..ff315f6f1a 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -61,18 +61,18 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) } cpu_set_sr(env, rb); if (env->sr & SR_DME) { - env->tlb->cpu_openrisc_map_address_data = + env->tlb.cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_data; } else { - env->tlb->cpu_openrisc_map_address_data = + env->tlb.cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; } if (env->sr & SR_IME) { - env->tlb->cpu_openrisc_map_address_code = + env->tlb.cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_code; } else { - env->tlb->cpu_openrisc_map_address_code = + env->tlb.cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; } break; @@ -101,14 +101,14 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); if (!(rb & 1)) { - tlb_flush_page(cs, env->tlb->dtlb[0][idx].mr & TARGET_PAGE_MASK); + tlb_flush_page(cs, env->tlb.dtlb[0][idx].mr & TARGET_PAGE_MASK); } - env->tlb->dtlb[0][idx].mr = rb; + env->tlb.dtlb[0][idx].mr = rb; break; case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); - env->tlb->dtlb[0][idx].tr = rb; + env->tlb.dtlb[0][idx].tr = rb; break; case TO_SPR(1, 768) ... TO_SPR(1, 895): /* DTLBW1MR 0-127 */ case TO_SPR(1, 896) ... TO_SPR(1, 1023): /* DTLBW1TR 0-127 */ @@ -120,14 +120,14 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); if (!(rb & 1)) { - tlb_flush_page(cs, env->tlb->itlb[0][idx].mr & TARGET_PAGE_MASK); + tlb_flush_page(cs, env->tlb.itlb[0][idx].mr & TARGET_PAGE_MASK); } - env->tlb->itlb[0][idx].mr = rb; + env->tlb.itlb[0][idx].mr = rb; break; case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); - env->tlb->itlb[0][idx].tr = rb; + env->tlb.itlb[0][idx].tr = rb; break; case TO_SPR(2, 768) ... TO_SPR(2, 895): /* ITLBW1MR 0-127 */ case TO_SPR(2, 896) ... TO_SPR(2, 1023): /* ITLBW1TR 0-127 */ @@ -259,11 +259,11 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); - return env->tlb->dtlb[0][idx].mr; + return env->tlb.dtlb[0][idx].mr; case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); - return env->tlb->dtlb[0][idx].tr; + return env->tlb.dtlb[0][idx].tr; case TO_SPR(1, 768) ... TO_SPR(1, 895): /* DTLBW1MR 0-127 */ case TO_SPR(1, 896) ... TO_SPR(1, 1023): /* DTLBW1TR 0-127 */ @@ -275,11 +275,11 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); - return env->tlb->itlb[0][idx].mr; + return env->tlb.itlb[0][idx].mr; case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); - return env->tlb->itlb[0][idx].tr; + return env->tlb.itlb[0][idx].tr; case TO_SPR(2, 768) ... TO_SPR(2, 895): /* ITLBW1MR 0-127 */ case TO_SPR(2, 896) ... TO_SPR(2, 1023): /* ITLBW1TR 0-127 */ From 23d45ebdb198378ae580c98ac898aa4b615059fd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 16:51:19 -0700 Subject: [PATCH 1917/2380] target/openrisc: Remove indirect function calls for mmu There is no reason to use an indirect branch instead of simply testing the SR bits that control mmu state. Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/cpu.c | 4 -- target/openrisc/cpu.h | 11 ----- target/openrisc/interrupt.c | 2 - target/openrisc/interrupt_helper.c | 25 ++--------- target/openrisc/machine.c | 26 ------------ target/openrisc/mmu.c | 66 +++++++++++++----------------- target/openrisc/sys_helper.c | 15 ------- 7 files changed, 31 insertions(+), 118 deletions(-) diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index fa8e342ff7..b92de51ecf 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -92,10 +92,6 @@ static void openrisc_cpu_initfn(Object *obj) OpenRISCCPU *cpu = OPENRISC_CPU(obj); cs->env_ptr = &cpu->env; - -#ifndef CONFIG_USER_ONLY - cpu_openrisc_mmu_init(cpu); -#endif } /* CPU models */ diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 96b7f58659..a27adad085 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -379,17 +379,6 @@ void cpu_openrisc_count_update(OpenRISCCPU *cpu); void cpu_openrisc_timer_update(OpenRISCCPU *cpu); void cpu_openrisc_count_start(OpenRISCCPU *cpu); void cpu_openrisc_count_stop(OpenRISCCPU *cpu); - -void cpu_openrisc_mmu_init(OpenRISCCPU *cpu); -int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw); -int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw); -int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw); #endif #define OPENRISC_CPU_TYPE_SUFFIX "-" TYPE_OPENRISC_CPU diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index 2d0b55afa9..23abcf29ed 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -63,8 +63,6 @@ void openrisc_cpu_do_interrupt(CPUState *cs) env->sr &= ~SR_TEE; env->pmr &= ~PMR_DME; env->pmr &= ~PMR_SME; - env->tlb.cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; - env->tlb.cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; env->lock_addr = -1; if (exception > 0 && exception < EXCP_NR) { diff --git a/target/openrisc/interrupt_helper.c b/target/openrisc/interrupt_helper.c index dc97b38704..a2e9003969 100644 --- a/target/openrisc/interrupt_helper.c +++ b/target/openrisc/interrupt_helper.c @@ -29,31 +29,12 @@ void HELPER(rfe)(CPUOpenRISCState *env) #ifndef CONFIG_USER_ONLY int need_flush_tlb = (cpu->env.sr & (SR_SM | SR_IME | SR_DME)) ^ (cpu->env.esr & (SR_SM | SR_IME | SR_DME)); -#endif - cpu->env.pc = cpu->env.epcr; - cpu_set_sr(&cpu->env, cpu->env.esr); - cpu->env.lock_addr = -1; - -#ifndef CONFIG_USER_ONLY - if (cpu->env.sr & SR_DME) { - cpu->env.tlb.cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_data; - } else { - cpu->env.tlb.cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_nommu; - } - - if (cpu->env.sr & SR_IME) { - cpu->env.tlb.cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_code; - } else { - cpu->env.tlb.cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_nommu; - } - if (need_flush_tlb) { CPUState *cs = CPU(cpu); tlb_flush(cs); } #endif + cpu->env.pc = cpu->env.epcr; + cpu->env.lock_addr = -1; + cpu_set_sr(&cpu->env, cpu->env.esr); } diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index c10d28b055..73e0abcfd7 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -24,31 +24,6 @@ #include "hw/boards.h" #include "migration/cpu.h" -static int env_post_load(void *opaque, int version_id) -{ - CPUOpenRISCState *env = opaque; - - /* Restore MMU handlers */ - if (env->sr & SR_DME) { - env->tlb.cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_data; - } else { - env->tlb.cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_nommu; - } - - if (env->sr & SR_IME) { - env->tlb.cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_code; - } else { - env->tlb.cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_nommu; - } - - - return 0; -} - static const VMStateDescription vmstate_tlb_entry = { .name = "tlb_entry", .version_id = 1, @@ -102,7 +77,6 @@ static const VMStateDescription vmstate_env = { .name = "env", .version_id = 6, .minimum_version_id = 6, - .post_load = env_post_load, .fields = (VMStateField[]) { VMSTATE_UINTTL_2DARRAY(shadow_gpr, CPUOpenRISCState, 16, 32), VMSTATE_UINTTL(pc, CPUOpenRISCState), diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index 5665bb7cc9..b2effaa6d7 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -29,18 +29,16 @@ #endif #ifndef CONFIG_USER_ONLY -int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw) +static inline int get_phys_nommu(hwaddr *physical, int *prot, + target_ulong address) { *physical = address; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TLBRET_MATCH; } -int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw) +static int get_phys_code(OpenRISCCPU *cpu, hwaddr *physical, int *prot, + target_ulong address, int rw, bool supervisor) { int vpn = address >> TARGET_PAGE_BITS; int idx = vpn & ITLB_MASK; @@ -52,8 +50,7 @@ int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, if (!(cpu->env.tlb.itlb[0][idx].mr & 1)) { return TLBRET_INVALID; } - - if (cpu->env.sr & SR_SM) { /* supervisor mode */ + if (supervisor) { if (cpu->env.tlb.itlb[0][idx].tr & SXE) { right |= PAGE_EXEC; } @@ -62,7 +59,6 @@ int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, right |= PAGE_EXEC; } } - if ((rw & 2) && ((right & PAGE_EXEC) == 0)) { return TLBRET_BADADDR; } @@ -73,9 +69,8 @@ int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, return TLBRET_MATCH; } -int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw) +static int get_phys_data(OpenRISCCPU *cpu, hwaddr *physical, int *prot, + target_ulong address, int rw, bool supervisor) { int vpn = address >> TARGET_PAGE_BITS; int idx = vpn & DTLB_MASK; @@ -87,8 +82,7 @@ int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, if (!(cpu->env.tlb.dtlb[0][idx].mr & 1)) { return TLBRET_INVALID; } - - if (cpu->env.sr & SR_SM) { /* supervisor mode */ + if (supervisor) { if (cpu->env.tlb.dtlb[0][idx].tr & SRE) { right |= PAGE_READ; } @@ -117,20 +111,24 @@ int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, return TLBRET_MATCH; } -static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, - int rw) +static int get_phys_addr(OpenRISCCPU *cpu, hwaddr *physical, + int *prot, target_ulong address, int rw) { - int ret = TLBRET_MATCH; + bool supervisor = (cpu->env.sr & SR_SM) != 0; + int ret; - if (rw == MMU_INST_FETCH) { /* ITLB */ - *physical = 0; - ret = cpu->env.tlb.cpu_openrisc_map_address_code(cpu, physical, - prot, address, rw); - } else { /* DTLB */ - ret = cpu->env.tlb.cpu_openrisc_map_address_data(cpu, physical, - prot, address, rw); + /* Assume nommu results for a moment. */ + ret = get_phys_nommu(physical, prot, address); + + /* Overwrite with TLB lookup if enabled. */ + if (rw == MMU_INST_FETCH) { + if (cpu->env.sr & SR_IME) { + ret = get_phys_code(cpu, physical, prot, address, rw, supervisor); + } + } else { + if (cpu->env.sr & SR_DME) { + ret = get_phys_data(cpu, physical, prot, address, rw, supervisor); + } } return ret; @@ -186,8 +184,7 @@ int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, hwaddr physical = 0; int prot = 0; - ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot, - address, rw); + ret = get_phys_addr(cpu, &physical, &prot, address, rw); if (ret == TLBRET_MATCH) { tlb_set_page(cs, address & TARGET_PAGE_MASK, @@ -225,17 +222,16 @@ hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) /* Check memory for any kind of address, since during debug the gdb can ask for anything, check data tlb for address */ - miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0); + miss = get_phys_addr(cpu, &phys_addr, &prot, addr, 0); /* Check instruction tlb */ if (miss) { - miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, - MMU_INST_FETCH); + miss = get_phys_addr(cpu, &phys_addr, &prot, addr, MMU_INST_FETCH); } /* Last, fall back to a plain address */ if (miss) { - miss = cpu_openrisc_get_phys_nommu(cpu, &phys_addr, &prot, addr, 0); + miss = get_phys_nommu(&phys_addr, &prot, addr); } if (miss) { @@ -244,10 +240,4 @@ hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return phys_addr; } } - -void cpu_openrisc_mmu_init(OpenRISCCPU *cpu) -{ - cpu->env.tlb.cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; - cpu->env.tlb.cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; -} #endif diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index ff315f6f1a..9b4339b34e 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -60,21 +60,6 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) tlb_flush(cs); } cpu_set_sr(env, rb); - if (env->sr & SR_DME) { - env->tlb.cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_data; - } else { - env->tlb.cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_nommu; - } - - if (env->sr & SR_IME) { - env->tlb.cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_code; - } else { - env->tlb.cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_nommu; - } break; case TO_SPR(0, 18): /* PPC */ From fd992ee7e3fb12aea888744313a2869c8848ef9d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 18:21:21 -0700 Subject: [PATCH 1918/2380] target/openrisc: Merge mmu_helper.c into mmu.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With tlb_fill in mmu.c, we can simplify things further. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/Makefile.objs | 2 +- target/openrisc/mmu.c | 11 ++++++++++ target/openrisc/mmu_helper.c | 40 ----------------------------------- 3 files changed, 12 insertions(+), 41 deletions(-) delete mode 100644 target/openrisc/mmu_helper.c diff --git a/target/openrisc/Makefile.objs b/target/openrisc/Makefile.objs index 8b8a890c59..b5432f4684 100644 --- a/target/openrisc/Makefile.objs +++ b/target/openrisc/Makefile.objs @@ -1,7 +1,7 @@ obj-$(CONFIG_SOFTMMU) += machine.o obj-y += cpu.o exception.o interrupt.o mmu.o translate.o disas.o obj-y += exception_helper.o fpu_helper.o \ - interrupt_helper.o mmu_helper.o sys_helper.o + interrupt_helper.o sys_helper.o obj-y += gdbstub.o DECODETREE = $(SRC_PATH)/scripts/decodetree.py diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index b2effaa6d7..9b4b5cf04f 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -240,4 +240,15 @@ hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return phys_addr; } } + +void tlb_fill(CPUState *cs, target_ulong addr, int size, + MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) +{ + int ret = openrisc_cpu_handle_mmu_fault(cs, addr, size, + access_type, mmu_idx); + if (ret) { + /* Raise Exception. */ + cpu_loop_exit_restore(cs, retaddr); + } +} #endif diff --git a/target/openrisc/mmu_helper.c b/target/openrisc/mmu_helper.c deleted file mode 100644 index 97e1d17b5a..0000000000 --- a/target/openrisc/mmu_helper.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * OpenRISC MMU helper routines - * - * Copyright (c) 2011-2012 Jia Liu - * Zhizhou Zhang - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" - -#ifndef CONFIG_USER_ONLY - -void tlb_fill(CPUState *cs, target_ulong addr, int size, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) -{ - int ret; - - ret = openrisc_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx); - - if (ret) { - /* Raise Exception. */ - cpu_loop_exit_restore(cs, retaddr); - } -} -#endif From 2acaa2331b96ee92f0df213784f9b6454c3d5edc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 19:36:27 -0700 Subject: [PATCH 1919/2380] target/openrisc: Reduce tlb to a single dimension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While we had defines for *_WAYS, we didn't define more than 1. Reduce the complexity by eliminating this unused dimension. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/cpu.h | 6 ++---- target/openrisc/machine.c | 6 ++---- target/openrisc/mmu.c | 30 ++++++++++++++++-------------- target/openrisc/sys_helper.c | 20 ++++++++++---------- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index a27adad085..eaf6cdd40e 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -222,10 +222,8 @@ enum { /* TLB size */ enum { - DTLB_WAYS = 1, DTLB_SIZE = 64, DTLB_MASK = (DTLB_SIZE-1), - ITLB_WAYS = 1, ITLB_SIZE = 64, ITLB_MASK = (ITLB_SIZE-1), }; @@ -256,8 +254,8 @@ typedef struct OpenRISCTLBEntry { #ifndef CONFIG_USER_ONLY typedef struct CPUOpenRISCTLBContext { - OpenRISCTLBEntry itlb[ITLB_WAYS][ITLB_SIZE]; - OpenRISCTLBEntry dtlb[DTLB_WAYS][DTLB_SIZE]; + OpenRISCTLBEntry itlb[ITLB_SIZE]; + OpenRISCTLBEntry dtlb[DTLB_SIZE]; int (*cpu_openrisc_map_address_code)(struct OpenRISCCPU *cpu, hwaddr *physical, diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index 73e0abcfd7..b795b56dc6 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -42,11 +42,9 @@ static const VMStateDescription vmstate_cpu_tlb = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField[]) { - VMSTATE_STRUCT_2DARRAY(itlb, CPUOpenRISCTLBContext, - ITLB_WAYS, ITLB_SIZE, 0, + VMSTATE_STRUCT_ARRAY(itlb, CPUOpenRISCTLBContext, ITLB_SIZE, 0, vmstate_tlb_entry, OpenRISCTLBEntry), - VMSTATE_STRUCT_2DARRAY(dtlb, CPUOpenRISCTLBContext, - DTLB_WAYS, DTLB_SIZE, 0, + VMSTATE_STRUCT_ARRAY(dtlb, CPUOpenRISCTLBContext, DTLB_SIZE, 0, vmstate_tlb_entry, OpenRISCTLBEntry), VMSTATE_END_OF_LIST() } diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index 9b4b5cf04f..856969a7f2 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -43,19 +43,21 @@ static int get_phys_code(OpenRISCCPU *cpu, hwaddr *physical, int *prot, int vpn = address >> TARGET_PAGE_BITS; int idx = vpn & ITLB_MASK; int right = 0; + uint32_t mr = cpu->env.tlb.itlb[idx].mr; + uint32_t tr = cpu->env.tlb.itlb[idx].tr; - if ((cpu->env.tlb.itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { + if ((mr >> TARGET_PAGE_BITS) != vpn) { return TLBRET_NOMATCH; } - if (!(cpu->env.tlb.itlb[0][idx].mr & 1)) { + if (!(mr & 1)) { return TLBRET_INVALID; } if (supervisor) { - if (cpu->env.tlb.itlb[0][idx].tr & SXE) { + if (tr & SXE) { right |= PAGE_EXEC; } } else { - if (cpu->env.tlb.itlb[0][idx].tr & UXE) { + if (tr & UXE) { right |= PAGE_EXEC; } } @@ -63,8 +65,7 @@ static int get_phys_code(OpenRISCCPU *cpu, hwaddr *physical, int *prot, return TLBRET_BADADDR; } - *physical = (cpu->env.tlb.itlb[0][idx].tr & TARGET_PAGE_MASK) | - (address & (TARGET_PAGE_SIZE-1)); + *physical = (tr & TARGET_PAGE_MASK) | (address & ~TARGET_PAGE_MASK); *prot = right; return TLBRET_MATCH; } @@ -75,25 +76,27 @@ static int get_phys_data(OpenRISCCPU *cpu, hwaddr *physical, int *prot, int vpn = address >> TARGET_PAGE_BITS; int idx = vpn & DTLB_MASK; int right = 0; + uint32_t mr = cpu->env.tlb.dtlb[idx].mr; + uint32_t tr = cpu->env.tlb.dtlb[idx].tr; - if ((cpu->env.tlb.dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { + if ((mr >> TARGET_PAGE_BITS) != vpn) { return TLBRET_NOMATCH; } - if (!(cpu->env.tlb.dtlb[0][idx].mr & 1)) { + if (!(mr & 1)) { return TLBRET_INVALID; } if (supervisor) { - if (cpu->env.tlb.dtlb[0][idx].tr & SRE) { + if (tr & SRE) { right |= PAGE_READ; } - if (cpu->env.tlb.dtlb[0][idx].tr & SWE) { + if (tr & SWE) { right |= PAGE_WRITE; } } else { - if (cpu->env.tlb.dtlb[0][idx].tr & URE) { + if (tr & URE) { right |= PAGE_READ; } - if (cpu->env.tlb.dtlb[0][idx].tr & UWE) { + if (tr & UWE) { right |= PAGE_WRITE; } } @@ -105,8 +108,7 @@ static int get_phys_data(OpenRISCCPU *cpu, hwaddr *physical, int *prot, return TLBRET_BADADDR; } - *physical = (cpu->env.tlb.dtlb[0][idx].tr & TARGET_PAGE_MASK) | - (address & (TARGET_PAGE_SIZE-1)); + *physical = (tr & TARGET_PAGE_MASK) | (address & ~TARGET_PAGE_MASK); *prot = right; return TLBRET_MATCH; } diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 9b4339b34e..7f458b0d17 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -86,14 +86,14 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); if (!(rb & 1)) { - tlb_flush_page(cs, env->tlb.dtlb[0][idx].mr & TARGET_PAGE_MASK); + tlb_flush_page(cs, env->tlb.dtlb[idx].mr & TARGET_PAGE_MASK); } - env->tlb.dtlb[0][idx].mr = rb; + env->tlb.dtlb[idx].mr = rb; break; case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); - env->tlb.dtlb[0][idx].tr = rb; + env->tlb.dtlb[idx].tr = rb; break; case TO_SPR(1, 768) ... TO_SPR(1, 895): /* DTLBW1MR 0-127 */ case TO_SPR(1, 896) ... TO_SPR(1, 1023): /* DTLBW1TR 0-127 */ @@ -105,14 +105,14 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); if (!(rb & 1)) { - tlb_flush_page(cs, env->tlb.itlb[0][idx].mr & TARGET_PAGE_MASK); + tlb_flush_page(cs, env->tlb.itlb[idx].mr & TARGET_PAGE_MASK); } - env->tlb.itlb[0][idx].mr = rb; + env->tlb.itlb[idx].mr = rb; break; case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); - env->tlb.itlb[0][idx].tr = rb; + env->tlb.itlb[idx].tr = rb; break; case TO_SPR(2, 768) ... TO_SPR(2, 895): /* ITLBW1MR 0-127 */ case TO_SPR(2, 896) ... TO_SPR(2, 1023): /* ITLBW1TR 0-127 */ @@ -244,11 +244,11 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); - return env->tlb.dtlb[0][idx].mr; + return env->tlb.dtlb[idx].mr; case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); - return env->tlb.dtlb[0][idx].tr; + return env->tlb.dtlb[idx].tr; case TO_SPR(1, 768) ... TO_SPR(1, 895): /* DTLBW1MR 0-127 */ case TO_SPR(1, 896) ... TO_SPR(1, 1023): /* DTLBW1TR 0-127 */ @@ -260,11 +260,11 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); - return env->tlb.itlb[0][idx].mr; + return env->tlb.itlb[idx].mr; case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); - return env->tlb.itlb[0][idx].tr; + return env->tlb.itlb[idx].tr; case TO_SPR(2, 768) ... TO_SPR(2, 895): /* ITLBW1MR 0-127 */ case TO_SPR(2, 896) ... TO_SPR(2, 1023): /* ITLBW1TR 0-127 */ From fffde6695f4be3cf484f068f24e894280d7360ea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 19:45:51 -0700 Subject: [PATCH 1920/2380] target/openrisc: Fix tlb flushing in mtspr The previous code was confused, avoiding the flush of the old entry if the new entry is invalid. We need to flush the old page if the old entry is valid and the new page if the new entry is valid. This bug was masked by over-flushing elsewhere. Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/sys_helper.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 7f458b0d17..c9702cd26c 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -32,6 +32,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) #ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = openrisc_env_get_cpu(env); CPUState *cs = CPU(cpu); + target_ulong mr; int idx; switch (spr) { @@ -85,12 +86,15 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); - if (!(rb & 1)) { - tlb_flush_page(cs, env->tlb.dtlb[idx].mr & TARGET_PAGE_MASK); + mr = env->tlb.dtlb[idx].mr; + if (mr & 1) { + tlb_flush_page(cs, mr & TARGET_PAGE_MASK); + } + if (rb & 1) { + tlb_flush_page(cs, rb & TARGET_PAGE_MASK); } env->tlb.dtlb[idx].mr = rb; break; - case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); env->tlb.dtlb[idx].tr = rb; @@ -102,14 +106,18 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) case TO_SPR(1, 1280) ... TO_SPR(1, 1407): /* DTLBW3MR 0-127 */ case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */ break; + case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); - if (!(rb & 1)) { - tlb_flush_page(cs, env->tlb.itlb[idx].mr & TARGET_PAGE_MASK); + mr = env->tlb.itlb[idx].mr; + if (mr & 1) { + tlb_flush_page(cs, mr & TARGET_PAGE_MASK); + } + if (rb & 1) { + tlb_flush_page(cs, rb & TARGET_PAGE_MASK); } env->tlb.itlb[idx].mr = rb; break; - case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); env->tlb.itlb[idx].tr = rb; @@ -121,6 +129,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) case TO_SPR(2, 1280) ... TO_SPR(2, 1407): /* ITLBW3MR 0-127 */ case TO_SPR(2, 1408) ... TO_SPR(2, 1535): /* ITLBW3TR 0-127 */ break; + case TO_SPR(5, 1): /* MACLO */ env->mac = deposit64(env->mac, 0, 32, rb); break; From b9bed1b9ab37a6ae62e88a52cbcbd2ad81aa1056 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 19:51:00 -0700 Subject: [PATCH 1921/2380] target/openrisc: Fix cpu_mmu_index The code in cpu_mmu_index does not properly honor SR_DME. This bug has workarounds elsewhere in that we flush the tlb more often than necessary, on the state changes that should be reflected in a change of mmu_index. Fixing this means that we can respect the mmu_index that is given to tlb_flush. Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/cpu.h | 23 +++++++++++++-------- target/openrisc/interrupt.c | 4 ---- target/openrisc/interrupt_helper.c | 15 +++----------- target/openrisc/mmu.c | 33 +++++++++++++++++++++++++++--- target/openrisc/sys_helper.c | 4 ---- target/openrisc/translate.c | 2 +- 6 files changed, 49 insertions(+), 32 deletions(-) diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index eaf6cdd40e..c3a968ec4d 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -385,9 +385,12 @@ void cpu_openrisc_count_stop(OpenRISCCPU *cpu); #include "exec/cpu-all.h" -#define TB_FLAGS_DFLAG 1 -#define TB_FLAGS_R0_0 2 +#define TB_FLAGS_SM SR_SM +#define TB_FLAGS_DME SR_DME +#define TB_FLAGS_IME SR_IME #define TB_FLAGS_OVE SR_OVE +#define TB_FLAGS_DFLAG 2 /* reuse SR_TEE */ +#define TB_FLAGS_R0_0 4 /* reuse SR_IEE */ static inline uint32_t cpu_get_gpr(const CPUOpenRISCState *env, int i) { @@ -405,17 +408,21 @@ static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env, { *pc = env->pc; *cs_base = 0; - *flags = (env->dflag - | (cpu_get_gpr(env, 0) == 0 ? TB_FLAGS_R0_0 : 0) - | (env->sr & SR_OVE)); + *flags = (env->dflag ? TB_FLAGS_DFLAG : 0) + | (cpu_get_gpr(env, 0) ? 0 : TB_FLAGS_R0_0) + | (env->sr & (SR_SM | SR_DME | SR_IME | SR_OVE)); } static inline int cpu_mmu_index(CPUOpenRISCState *env, bool ifetch) { - if (!(env->sr & SR_IME)) { - return MMU_NOMMU_IDX; + int ret = MMU_NOMMU_IDX; /* mmu is disabled */ + + if (env->sr & (ifetch ? SR_IME : SR_DME)) { + /* The mmu is enabled; test supervisor state. */ + ret = env->sr & SR_SM ? MMU_SUPERVISOR_IDX : MMU_USER_IDX; } - return (env->sr & SR_SM) == 0 ? MMU_USER_IDX : MMU_SUPERVISOR_IDX; + + return ret; } static inline uint32_t cpu_get_sr(const CPUOpenRISCState *env) diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index 23abcf29ed..138ad17f00 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -51,10 +51,6 @@ void openrisc_cpu_do_interrupt(CPUState *cs) env->eear = env->pc; } - /* For machine-state changed between user-mode and supervisor mode, - we need flush TLB when we enter&exit EXCP. */ - tlb_flush(cs); - env->esr = cpu_get_sr(env); env->sr &= ~SR_DME; env->sr &= ~SR_IME; diff --git a/target/openrisc/interrupt_helper.c b/target/openrisc/interrupt_helper.c index a2e9003969..9c5489f5f7 100644 --- a/target/openrisc/interrupt_helper.c +++ b/target/openrisc/interrupt_helper.c @@ -25,16 +25,7 @@ void HELPER(rfe)(CPUOpenRISCState *env) { - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); -#ifndef CONFIG_USER_ONLY - int need_flush_tlb = (cpu->env.sr & (SR_SM | SR_IME | SR_DME)) ^ - (cpu->env.esr & (SR_SM | SR_IME | SR_DME)); - if (need_flush_tlb) { - CPUState *cs = CPU(cpu); - tlb_flush(cs); - } -#endif - cpu->env.pc = cpu->env.epcr; - cpu->env.lock_addr = -1; - cpu_set_sr(&cpu->env, cpu->env.esr); + env->pc = env->epcr; + env->lock_addr = -1; + cpu_set_sr(env, env->esr); } diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index 856969a7f2..b293b64e98 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -246,9 +246,36 @@ hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) void tlb_fill(CPUState *cs, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - int ret = openrisc_cpu_handle_mmu_fault(cs, addr, size, - access_type, mmu_idx); - if (ret) { + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + int ret, prot = 0; + hwaddr physical = 0; + + if (mmu_idx == MMU_NOMMU_IDX) { + ret = get_phys_nommu(&physical, &prot, addr); + } else { + bool super = mmu_idx == MMU_SUPERVISOR_IDX; + if (access_type == MMU_INST_FETCH) { + ret = get_phys_code(cpu, &physical, &prot, addr, 2, super); + } else { + ret = get_phys_data(cpu, &physical, &prot, addr, + access_type == MMU_DATA_STORE, super); + } + } + + if (ret == TLBRET_MATCH) { + tlb_set_page(cs, addr & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot, + mmu_idx, TARGET_PAGE_SIZE); + } else if (ret < 0) { + int rw; + if (access_type == MMU_INST_FETCH) { + rw = 2; + } else if (access_type == MMU_DATA_STORE) { + rw = 1; + } else { + rw = 0; + } + cpu_openrisc_raise_mmu_exception(cpu, addr, rw, ret); /* Raise Exception. */ cpu_loop_exit_restore(cs, retaddr); } diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index c9702cd26c..852b219f9b 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -56,10 +56,6 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) break; case TO_SPR(0, 17): /* SR */ - if ((env->sr & (SR_IME | SR_DME | SR_SM)) ^ - (rb & (SR_IME | SR_DME | SR_SM))) { - tlb_flush(cs); - } cpu_set_sr(env, rb); break; diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 64b5e84630..a271cd3903 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -55,7 +55,7 @@ static inline bool is_user(DisasContext *dc) #ifdef CONFIG_USER_ONLY return true; #else - return dc->mem_idx == MMU_USER_IDX; + return !(dc->tb_flags & TB_FLAGS_SM); #endif } From 56c3a1415653bd485b06e718c6f14e32dcfe59b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 20:18:20 -0700 Subject: [PATCH 1922/2380] target/openrisc: Use identical sizes for ITLB and DTLB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sizes are already the same, however, we can improve things if they are identical by design. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/cpu.h | 10 ++++------ target/openrisc/machine.c | 4 ++-- target/openrisc/mmu.c | 4 ++-- target/openrisc/sys_helper.c | 16 ++++++++-------- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index c3a968ec4d..47e94659e1 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -222,10 +222,8 @@ enum { /* TLB size */ enum { - DTLB_SIZE = 64, - DTLB_MASK = (DTLB_SIZE-1), - ITLB_SIZE = 64, - ITLB_MASK = (ITLB_SIZE-1), + TLB_SIZE = 64, + TLB_MASK = TLB_SIZE - 1, }; /* TLB prot */ @@ -254,8 +252,8 @@ typedef struct OpenRISCTLBEntry { #ifndef CONFIG_USER_ONLY typedef struct CPUOpenRISCTLBContext { - OpenRISCTLBEntry itlb[ITLB_SIZE]; - OpenRISCTLBEntry dtlb[DTLB_SIZE]; + OpenRISCTLBEntry itlb[TLB_SIZE]; + OpenRISCTLBEntry dtlb[TLB_SIZE]; int (*cpu_openrisc_map_address_code)(struct OpenRISCCPU *cpu, hwaddr *physical, diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index b795b56dc6..3fc837b925 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -42,9 +42,9 @@ static const VMStateDescription vmstate_cpu_tlb = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(itlb, CPUOpenRISCTLBContext, ITLB_SIZE, 0, + VMSTATE_STRUCT_ARRAY(itlb, CPUOpenRISCTLBContext, TLB_SIZE, 0, vmstate_tlb_entry, OpenRISCTLBEntry), - VMSTATE_STRUCT_ARRAY(dtlb, CPUOpenRISCTLBContext, DTLB_SIZE, 0, + VMSTATE_STRUCT_ARRAY(dtlb, CPUOpenRISCTLBContext, TLB_SIZE, 0, vmstate_tlb_entry, OpenRISCTLBEntry), VMSTATE_END_OF_LIST() } diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index b293b64e98..a4613e9ae4 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -41,7 +41,7 @@ static int get_phys_code(OpenRISCCPU *cpu, hwaddr *physical, int *prot, target_ulong address, int rw, bool supervisor) { int vpn = address >> TARGET_PAGE_BITS; - int idx = vpn & ITLB_MASK; + int idx = vpn & TLB_MASK; int right = 0; uint32_t mr = cpu->env.tlb.itlb[idx].mr; uint32_t tr = cpu->env.tlb.itlb[idx].tr; @@ -74,7 +74,7 @@ static int get_phys_data(OpenRISCCPU *cpu, hwaddr *physical, int *prot, target_ulong address, int rw, bool supervisor) { int vpn = address >> TARGET_PAGE_BITS; - int idx = vpn & DTLB_MASK; + int idx = vpn & TLB_MASK; int right = 0; uint32_t mr = cpu->env.tlb.dtlb[idx].mr; uint32_t tr = cpu->env.tlb.dtlb[idx].tr; diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 852b219f9b..541615bfb3 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -80,7 +80,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) env->shadow_gpr[idx / 32][idx % 32] = rb; break; - case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ + case TO_SPR(1, 512) ... TO_SPR(1, 512 + TLB_SIZE - 1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); mr = env->tlb.dtlb[idx].mr; if (mr & 1) { @@ -91,7 +91,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) } env->tlb.dtlb[idx].mr = rb; break; - case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ + case TO_SPR(1, 640) ... TO_SPR(1, 640 + TLB_SIZE - 1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); env->tlb.dtlb[idx].tr = rb; break; @@ -103,7 +103,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */ break; - case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ + case TO_SPR(2, 512) ... TO_SPR(2, 512 + TLB_SIZE - 1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); mr = env->tlb.itlb[idx].mr; if (mr & 1) { @@ -114,7 +114,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) } env->tlb.itlb[idx].mr = rb; break; - case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ + case TO_SPR(2, 640) ... TO_SPR(2, 640 + TLB_SIZE - 1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); env->tlb.itlb[idx].tr = rb; break; @@ -247,11 +247,11 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, idx = (spr - 1024); return env->shadow_gpr[idx / 32][idx % 32]; - case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ + case TO_SPR(1, 512) ... TO_SPR(1, 512 + TLB_SIZE - 1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); return env->tlb.dtlb[idx].mr; - case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ + case TO_SPR(1, 640) ... TO_SPR(1, 640 + TLB_SIZE - 1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); return env->tlb.dtlb[idx].tr; @@ -263,11 +263,11 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */ break; - case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ + case TO_SPR(2, 512) ... TO_SPR(2, 512 + TLB_SIZE - 1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); return env->tlb.itlb[idx].mr; - case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ + case TO_SPR(2, 640) ... TO_SPR(2, 640 + TLB_SIZE - 1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); return env->tlb.itlb[idx].tr; From 5ce5dad3527e024c297f73f9eb79098235efba6b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 20:43:25 -0700 Subject: [PATCH 1923/2380] target/openrisc: Stub out handle_mmu_fault for softmmu This hook is only used by CONFIG_USER_ONLY. Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/mmu.c | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index a4613e9ae4..f4c0a3e217 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -177,42 +177,17 @@ static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu, cpu->env.lock_addr = -1; } -#ifndef CONFIG_USER_ONLY int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, int mmu_idx) { +#ifdef CONFIG_USER_ONLY OpenRISCCPU *cpu = OPENRISC_CPU(cs); - int ret = 0; - hwaddr physical = 0; - int prot = 0; - - ret = get_phys_addr(cpu, &physical, &prot, address, rw); - - if (ret == TLBRET_MATCH) { - tlb_set_page(cs, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot, - mmu_idx, TARGET_PAGE_SIZE); - ret = 0; - } else if (ret < 0) { - cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); - ret = 1; - } - - return ret; -} + cpu_openrisc_raise_mmu_exception(cpu, address, rw, 0); + return 1; #else -int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, - int rw, int mmu_idx) -{ - OpenRISCCPU *cpu = OPENRISC_CPU(cs); - int ret = 0; - - cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); - ret = 1; - - return ret; -} + g_assert_not_reached(); #endif +} #ifndef CONFIG_USER_ONLY hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) From 1cc9e5d896695091eeb126f5c578b02ddd0fc0e4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 22:04:46 -0700 Subject: [PATCH 1924/2380] target/openrisc: Increase the TLB size The architecture supports 128 TLB entries. There is no reason not to provide all of them. In the process we need to fix a bug that failed to parameterize the configuration register that tells the operating system the number of entries. Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- v2: - Change VMState version. --- target/openrisc/cpu.c | 6 ++++-- target/openrisc/cpu.h | 2 +- target/openrisc/machine.c | 5 ++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index b92de51ecf..e01ce9ed1c 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -57,8 +57,10 @@ static void openrisc_cpu_reset(CPUState *s) cpu->env.upr = UPR_UP | UPR_DMP | UPR_IMP | UPR_PICP | UPR_TTP | UPR_PMP; - cpu->env.dmmucfgr = (DMMUCFGR_NTW & (0 << 2)) | (DMMUCFGR_NTS & (6 << 2)); - cpu->env.immucfgr = (IMMUCFGR_NTW & (0 << 2)) | (IMMUCFGR_NTS & (6 << 2)); + cpu->env.dmmucfgr = (DMMUCFGR_NTW & (0 << 2)) + | (DMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2)); + cpu->env.immucfgr = (IMMUCFGR_NTW & (0 << 2)) + | (IMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2)); #ifndef CONFIG_USER_ONLY cpu->env.picmr = 0x00000000; diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 47e94659e1..b180e30e9e 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -222,7 +222,7 @@ enum { /* TLB size */ enum { - TLB_SIZE = 64, + TLB_SIZE = 128, TLB_MASK = TLB_SIZE - 1, }; diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index 3fc837b925..1eedbf3dbe 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -38,9 +38,8 @@ static const VMStateDescription vmstate_tlb_entry = { static const VMStateDescription vmstate_cpu_tlb = { .name = "cpu_tlb", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_STRUCT_ARRAY(itlb, CPUOpenRISCTLBContext, TLB_SIZE, 0, vmstate_tlb_entry, OpenRISCTLBEntry), From 9c8c334b0637bf3c592d432b0c11f3b62dd5dba3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Jun 2018 16:30:28 -0700 Subject: [PATCH 1925/2380] cpu: Assert asidx_from_attrs return value in range Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/qom/cpu.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/qom/cpu.h b/include/qom/cpu.h index cce2fd6acc..bd796579ee 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -620,11 +620,13 @@ static inline hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr) static inline int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs) { CPUClass *cc = CPU_GET_CLASS(cpu); + int ret = 0; if (cc->asidx_from_attrs) { - return cc->asidx_from_attrs(cpu, attrs); + ret = cc->asidx_from_attrs(cpu, attrs); + assert(ret < cpu->num_ases && ret >= 0); } - return 0; + return ret; } #endif From 429d3ae2c8e52d2787af10fffce71103bf0f82f0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jun 2018 12:02:28 +0200 Subject: [PATCH 1926/2380] seabios: update submodule to release 1.11.2 git shortlog rel-1.11.1..rel-1.11.2 ----------------------------------- Gerd Hoffmann (11): optionrom: enable non-vga display devices cbvga: factor out cbvga_setup_modes() qemu: add bochs-display support cbvga_setup_modes: use real mode number instead of 0x140 cbvga_list_modes: don't list current mode twice cbvga_set_mode: disable clearmem in windows x86 emulator. bochs_display_setup: return error on failure pmm: use tmp zone on oom vgasrc: add allocate_pmm() qemu: add qemu ramfb support cbvga_set_mode: refine clear display logic Signed-off-by: Gerd Hoffmann --- roms/seabios | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roms/seabios b/roms/seabios index 0551a4be2c..f9626ccb91 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit 0551a4be2ce599fb60e478b4c15e06ab6587822c +Subproject commit f9626ccb91e771f990fbb2da92e427a399d7d918 From 75056cef41b6ca304948866b27b04b3c6713dfaa Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jun 2018 12:05:55 +0200 Subject: [PATCH 1927/2380] seabios: add vga configs for bochs-display and ramfb Both bochs-display and ramfb are devices with a simple framebuffer and no vga emulation or text mode. seavgabios has support for text mode emulation (at vgabios call level), we are using that to provide some vga compatibility support for these devices. Signed-off-by: Gerd Hoffmann --- roms/Makefile | 2 +- roms/config.vga-bochs-display | 3 +++ roms/config.vga-ramfb | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 roms/config.vga-bochs-display create mode 100644 roms/config.vga-ramfb diff --git a/roms/Makefile b/roms/Makefile index 02b69fbac8..a73778f60b 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -1,5 +1,5 @@ -vgabios_variants := stdvga cirrus vmware qxl isavga virtio +vgabios_variants := stdvga cirrus vmware qxl isavga virtio bochs-display ramfb vgabios_targets := $(subst -isavga,,$(patsubst %,vgabios-%.bin,$(vgabios_variants))) pxerom_variants := e1000 e1000e eepro100 ne2k_pci pcnet rtl8139 virtio vmxnet3 pxerom_targets := 8086100e 808610d3 80861209 10500940 10222000 10ec8139 1af41000 15ad07b0 diff --git a/roms/config.vga-bochs-display b/roms/config.vga-bochs-display new file mode 100644 index 0000000000..d2adaaef66 --- /dev/null +++ b/roms/config.vga-bochs-display @@ -0,0 +1,3 @@ +CONFIG_BUILD_VGABIOS=y +CONFIG_DISPLAY_BOCHS=y +CONFIG_VGA_PCI=y diff --git a/roms/config.vga-ramfb b/roms/config.vga-ramfb new file mode 100644 index 0000000000..c809c799b9 --- /dev/null +++ b/roms/config.vga-ramfb @@ -0,0 +1,3 @@ +CONFIG_BUILD_VGABIOS=y +CONFIG_VGA_RAMFB=y +CONFIG_VGA_PCI=n From eda553a442e94dc16d424849b65c6cb7f140f318 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jun 2018 12:07:40 +0200 Subject: [PATCH 1928/2380] seabios: enable ide dma QNX reportedly requires this to boot. Should also speed up booting other guests. Note: Upstream seabios defaults this to 'n' to due to known problems on physical hardware (qemu not affected), and wouldn't flip the default to 'y'. So we adjust our local build config accordingly. Signed-off-by: Gerd Hoffmann --- roms/config.seabios-128k | 1 + roms/config.seabios-256k | 1 + 2 files changed, 2 insertions(+) diff --git a/roms/config.seabios-128k b/roms/config.seabios-128k index 486ef0e132..35b5a07d8f 100644 --- a/roms/config.seabios-128k +++ b/roms/config.seabios-128k @@ -2,6 +2,7 @@ # need to turn off features (xhci,uas) to make it fit into 128k CONFIG_QEMU=y CONFIG_ROM_SIZE=128 +CONFIG_ATA_DMA=y CONFIG_BOOTSPLASH=n CONFIG_XEN=n CONFIG_USB_OHCI=n diff --git a/roms/config.seabios-256k b/roms/config.seabios-256k index 65e5015c2f..b14b614fcc 100644 --- a/roms/config.seabios-256k +++ b/roms/config.seabios-256k @@ -1,3 +1,4 @@ # for qemu machine types 2.0 + newer CONFIG_QEMU=y CONFIG_ROM_SIZE=256 +CONFIG_ATA_DMA=y From 91b8eba9ec3f5af7dd48927811eb7ff69fc4617f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jun 2018 12:11:42 +0200 Subject: [PATCH 1929/2380] vgabios: remove submodule and build rules. It's the old, lgpl vgabios implementation. Was left in as fallback when we switched to seavgabios, so we could easily switch back in case we see regressions. It's unused since years now, reportedly doesn't even build, and lacks support for recently (and not so recently) added display devices. Zap it. Signed-off-by: Gerd Hoffmann --- .gitmodules | 3 --- roms/Makefile | 18 ++---------------- roms/vgabios | 1 - 3 files changed, 2 insertions(+), 20 deletions(-) delete mode 160000 roms/vgabios diff --git a/.gitmodules b/.gitmodules index 49e9c2e3f4..d108478e0a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "roms/vgabios"] - path = roms/vgabios - url = git://git.qemu-project.org/vgabios.git/ [submodule "roms/seabios"] path = roms/seabios url = git://git.qemu-project.org/seabios.git/ diff --git a/roms/Makefile b/roms/Makefile index a73778f60b..f1ac85ae9b 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -56,8 +56,7 @@ default: @echo "nothing is build by default" @echo "available build targets:" @echo " bios -- update bios.bin (seabios)" - @echo " seavgabios -- update vgabios binaries (seabios)" - @echo " lgplvgabios -- update vgabios binaries (lgpl)" + @echo " vgabios -- update vgabios binaries (seabios)" @echo " sgabios -- update sgabios binaries" @echo " pxerom -- update nic roms (bios only)" @echo " efirom -- update nic roms (bios+efi, this needs" @@ -71,7 +70,7 @@ bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k cp seabios/builds/seabios-128k/bios.bin ../pc-bios/bios.bin cp seabios/builds/seabios-256k/bios.bin ../pc-bios/bios-256k.bin -seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants)) +vgabios seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants)) seavgabios-isavga: build-seabios-config-vga-isavga cp seabios/builds/vga-isavga/vgabios.bin ../pc-bios/vgabios.bin @@ -94,17 +93,6 @@ build-seabios-config-%: config.% OUT=$(CURDIR)/seabios/builds/$*/ all -lgplvgabios: $(patsubst %,lgplvgabios-%,$(vgabios_variants)) - -lgplvgabios-isavga: build-lgplvgabios - cp vgabios/VGABIOS-lgpl-latest.bin ../pc-bios/vgabios.bin -lgplvgabios-%: build-lgplvgabios - cp vgabios/VGABIOS-lgpl-latest.$*.bin ../pc-bios/vgabios-$*.bin - -build-lgplvgabios: - $(MAKE) -C vgabios $(vgabios_targets) - - .PHONY: sgabios skiboot sgabios: $(MAKE) -C sgabios @@ -159,8 +147,6 @@ skiboot: clean: rm -rf seabios/.config seabios/out seabios/builds - $(MAKE) -C vgabios clean - rm -f vgabios/VGABIOS-lgpl-latest* $(MAKE) -C sgabios clean rm -f sgabios/.depend $(MAKE) -C ipxe/src veryclean diff --git a/roms/vgabios b/roms/vgabios deleted file mode 160000 index 19ea12c230..0000000000 --- a/roms/vgabios +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 19ea12c230ded95928ecaef0db47a82231c2e485 From cd1bfd5ef336166b275a09dc9842542bf5e63ae3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jun 2018 12:17:34 +0200 Subject: [PATCH 1930/2380] seabios: update bios and vgabios binaries Adds two new vgabios binaries, for ramfb and bochs-display. Signed-off-by: Gerd Hoffmann --- pc-bios/bios-256k.bin | Bin 262144 -> 262144 bytes pc-bios/bios.bin | Bin 131072 -> 131072 bytes pc-bios/vgabios-bochs-display.bin | Bin 0 -> 27648 bytes pc-bios/vgabios-cirrus.bin | Bin 38400 -> 38400 bytes pc-bios/vgabios-qxl.bin | Bin 38912 -> 38912 bytes pc-bios/vgabios-ramfb.bin | Bin 0 -> 28160 bytes pc-bios/vgabios-stdvga.bin | Bin 38912 -> 38912 bytes pc-bios/vgabios-virtio.bin | Bin 38912 -> 38912 bytes pc-bios/vgabios-vmware.bin | Bin 38912 -> 38912 bytes pc-bios/vgabios.bin | Bin 38400 -> 38400 bytes 10 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 pc-bios/vgabios-bochs-display.bin create mode 100644 pc-bios/vgabios-ramfb.bin diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin index 0061dc99288bee61e60e31d1e24a52b97b0c5fb6..6ffa6ec524bf4a3d001a68f951f2a56b95f8acbd 100644 GIT binary patch delta 48782 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 delta 48064 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�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 diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin index 69bb8635ae36e95f3b066ae16ff9d44e8a3665ba..afa450c4a002b2af86e6d80cf13713036c4d2e2e 100644 GIT binary patch delta 34128 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%0Af!CJ%yYL>FKW+ww$o$kqi)|p1q6I& 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 diff --git a/pc-bios/vgabios-bochs-display.bin b/pc-bios/vgabios-bochs-display.bin new file mode 100644 index 0000000000000000000000000000000000000000..6021d9b1991b45bc2b4b3e525aa01e425153a2ff GIT binary patch literal 27648 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 literal 0 HcmV?d00001 diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin index cec498d59e254b64c4fc7ca291409c735736b43c..c1ec6b62982a1525263f123bc47d5a92c7d5ab48 100644 GIT binary patch delta 3127 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 diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin index 82c9970df73829e3886f503369f50c294c72558e..2529ac954d72ad15fbf0feee1515e07353736928 100644 GIT binary patch delta 2783 zcmaJ?dr*|u760z;%7PGf1tm%rT**KMb{AF&LRj1SmR+o+4m z3=2r?9C1u*5)=AI#yAso)3{(oRw*J{b&QirwJAQ*?iSP?gO&QC=YBwCT3crBy&w1d z?(dxQyXT&D9F;ka$|fv6obNkTm@NF~-@#)O%yPEDwL(5Mn3?dGppeBhS~@dKGSEgJ7_Ybu^zM=GFTk` z)}Ujn@U|f-IEEanc3x&M;~Uf@6|?iM50X~0D93t15WP>ogI#SQJ<(9o&IAEU)XVw& zK0Z#Knf2=>9C7ps6v1+E`b9761Ls+&YOOFD;4_|BYeg6|U*ZSd3R335@yp4r51{ht zLt@!9jy~M3t;S}?9GEgyuX5}m1w!i1gXfkSnjN@ds>Z;h=(z>PR-axrOsd))B{SG+ z$$7~8@OkR?aZyXTR;7KU=GeOP^&UEwx&il2)5ULD zU+)2TCdm`mSSEdwUP2r+R;&l-74Ue+f(T2XWH$aV&C2|kmsZ07ze|e_=QX0*xp3g6 z!!}f=XUP;+EJ)Xk=VnlyXoaAT`ZL6*u-f39#ovaN=@Z9a1Q&XR8rM z5#K{-68r-L^&UJpJ(@M+$?4jpEtEQx2r@qJkYsrAfXe{RE;y;BT%je1&kL47=>ROk zzUlFL5vp!f_YDS%#hrk=0^YW-K#cTIO!H1y5yuNS&u(Ry$MXO9Uq{()$fXOmfxkB4$#k zs{R@ROHp{`pxtrNCY@9op{RuD5%}`&Xn9gv$ zh`3+$euQu2L?)I?={4Nu-AjtCm{1<@_|#OT*Y-dkyu-zW6}~I@)0~LlN!M&-Fpm+v zu~?atHfGlK>NB*_)}yu66i{o6z|Nc`HVdERnAu*OnXARExxu(LSFPUZ0W<7+s<3-~ zH-n(*H|Wewcs=B@t!9zNR%2F=bd`ghxu27m>++_s@9|Kco(XsKXNtFUWxW`H;Bd#onWYnt)9PAuEDM(cgw)h&HHgAc_v4ivLo48=U z_ED&wuc5ZwG;dOX)Pj%U#(5g$AgBf9^W!J-ZY_H2-IBuYKyooM)fCK|zny)6o%3T- z_^BSFtu9XnZmGw|zWg2~D0(6W|J}x0{pcYRYDK+rzjTS)uuz;GAd$?$R&l(N`{KV3 zC&f1-cyRQluZrHXuXvC5ZInaPZtN2?*v~L!!KZ=0lQ-*vd|`BCnsj^){gl!1k=cs4 zs&K8Ww;L}Ou4V^ZNsA4#fU@Y48R%S(qRwg6MMH1H%8r;sR^-nu7QWrdMGV zjFje&sRT-_zXynsSjtueW zfNBG_KGHC9Ff%ArO{A909&a zMh-qY48~&=;;>t8Z6xbHS{*bvm0F@&$rOR164S8aBe7*e*fA2eo3TK;JMuabdxv;M z!kLD!VMAC&pn5RGSAivBTxAQl=%_^KgpfwZc>)?*18F>?TQ4PpR=!L|RIhZ~O&E#E z)nwBcev)Qby{g=5u$E0Le#>AKHd)K`MuX94n4(YC+aNu4O6r<5tJ96H{)TS$FH3^p AoB#j- delta 2738 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≻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 diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin index 79333575e0a2980cee17d3677d4ab1098defcd99..8188eabb18378056c18e09ef91adff6293405e36 100644 GIT binary patch delta 2783 zcmaJ?dr*|u760z;>Vgn<0VPUST**L1b{AF+LRj1SmR+o+4m zj0;HY9C1u*5)=AI#yAso)3{(oRw*JdIQrpsZ8bJA=D-=#^(x0+QXshY0(fqzp~-<8rfUp5ik@3wZ1L%3!=$R+6J-Wl zwcNJHa&u5OliVK9mBOa!^O?>ipK+I20uJyn4N%7H#>r?#pi6{EO*$xj*ytrqm3HiJ}rQJTJw!N+; z7MxN0ach!=-NiFWI=WAOuAXvNRmpkC`|)|w_Hh%Ja;-}HNX@ae7wSB8ENKJoo2iT2 zvcAp(?lh7owxLY=O?nBj&`_}+oL9i(9Sb5Xfs(oS!%QplV@`541N<&IDxBAdYUjd% zmkwJ|nUWz>STQd}GoG74b+QEl+w0B}pTcT`^A>+AR;ElE-^sI9eRCDuyzZz3RlDD% z3g~vZQ-02-@V)TOf#CcWzP$s1*C8Et{ZnnfRofvkIqJc4TMh1~STsvN?w(NB7Hg{( zM-kscXcYVd19cueG%JcV;i*~Lge{ahln63D?~r78@qo(!&Mr8mrCgyokk186pmYEh zVc)Dcy$Dq|Dso^El$@4MeDQ!^7S|g*J&IUo)WLdio*j&0I$BK8q4!=$)}}%6oDutQ zi>ZNqftuMm_6t|m>>&2~bsosS1+jtCJes7d!1w0pfNp{4?yF;M&hjv5?j3~w0|GBm z#sn>ofaZb!+Khze@MRL|2xJsE|8fW_&%dCgJ~lv22!9C|D&nEhkI&~skKy;S=?0~4 z#kkah%t#^-y<)Fajn00@T_$=z`GH(oyi`Q?a99c@Z%G(kFV$wTuWxkSIeHMJG=Esm z$3GsSf-FN%s>o_EEG>o^Fga~E+k&UlKBUf*m)^#dxe~!ij5NQ&eve7!smhhoPAS^V zb7jwN;g{xjSTQOyW~$=;Ak}V>OnL?ln3oYpT$?j8f{t{6vrXEaiftK1VK|7~!h9Xi zrR}{q&9a$A;t@+GyNVAjvzZPP=Q$#@yx5u!P`EazvHPW7(fc=Sn^&V{XH9a|?jmMV zsH*-N42@q)&}~v?^a|5(W9CBkM{LcEXWP-A`8LbK`1zaJGHjSXh5ZRT=clT#QA}sK zUPRn4dOyN9vLfTlrS$4=^X?_ZR!k@lczkNA(rbI658mNo!V2FN{ApH1(Bx}2GMGa} zZwyvuC5NV8-*c8WT6?s%>O5*~5!jKHz|!$amYMCtIoVp=njM5|v(@UI9x%hMrwY5* zcQX(ge}m5KsjmlLwpB0E*s9Iyk*>0^Bl~j_b6w6f_B|fX(K7)r<;1cse3X;T@-Zzp zdXnx;-FZ@Ju|L+fM-eVjtL@$34y9@my&+hc8_7P#L%GHv!x?a^rQMTwJ$DMLN0v8{ zy@`|ZrjD~T@A0(wl%z_YAKd)2BzldwGB1)*ugY_<^Y|n$Njcc!t8wOnB`U`bj?p)A z!F+9_P@S)#w%oX2@@T0AAIFUgG|E9xGs^ShCh=}9dh6Vh!tMZaF*4N|n3KPqeSjVL z(KGm|9;2-`M+R=G$H%<61QQYICr!}G7nqC@k;KC ze;-bXZ$|Lo=uKZ0y=7nV9`D;IhsNF5C#JHW;f#V$1AZrQJnq691?MysQgwYL=kJE9 zOMcVVX4ZuHzGTo?h)oNpDQ`>Y@A1clF)RZoEHaV?Ig9?xwqaahRAjPrd>(zu==jKN zMO;<5R@U2%orSB}L07_JgKTu!`pxUC>+s#BJqcQaY3*Mt2)#6T1DhgD5EOxQx$Xhse`2w~8op!L>ZOyMsgu@WCQ4HfU%~V$ ztb+Wev35ZgKoOX-G?XJl zJT@R4eSc6G9wLw}kyZ^2HI3HR4bVxAVgvsR(vG3YzP}FVY?X%pt~clBe8dg zS0tQi2pcwpRRpL9LwprjBF0s=aEp#ggiZ*_bet!kp*4WUGrILsGHB(?WJL8!x7~!1 zm|TrEjo~NBhSjUetp;n^%;L8UMq!h+Om8$8jfQFZM7<4ClBOlCS+hFD=<2WUX8!^l Cd*FZo delta 2738 zcmb7F3s98T75?uo$nua~c`0V01PcM#U06jBMZoZs=Z~QHz&D)Tpp8^ve2fuPXjvh| zHe?D!+ug1Tld8qUNYDL&#M(?|%Ivv!IrqEY zdED>bt!B;=?UGF>0&%9o_lQEn5pQ;K! zAKOx>@2OTm`?E4ZaB89b3PM<@*O3*1u;~luW!Ux2IrIK7FIU1Joj!*VM{iUzjcUzr zXgs5NmBu@o1R8y{^)w#R>S?^J4fpgr@DS!ZAL<$d{P3AJnb|N=H=A9@YF#q(#NX>Q zECUC0(VovKuyXe`2J_xSb@VEB+`J=tB?~IuCVyczP-AX zs`IBW`_0?*?TnRT>`bk)^lj>ZS5r4w`&H0hij_0fI-Z5IAM~9Lt!$iBSr;VJ8TQHz z4e7n3eM^*ANAXT!`^+q+Fw0}_vh=C^(6)XYcKFQZA`R+a@&oVF`rd+_- zA^EO9rb9%N>4O6%csELaT-O-XTR2_7B7)&-8JpKHEuF-pEOMPvuvsz4&r=1#@QaWjT0jcCatkh;sMBksktn zgl-9Gvc59ROHfbY%b+~d3GTM$^TgLzW`IAY4;{f>2|t^1f>*8lLl0QE?nr{lx>8cW zIkP2UJB#E#-!1N7x&b%70{7c6AND*|Rrfbtl9) zPq8w|pNaUZB+XpvFKvIQ3OATG`#^_{qEJ_!fcsUd#Q>(w0nqWq|2U^Y$6yIQNP5+S z>$tlr3fivVl;lv4(TfRZpOs-+a$(dHDe3k1B`ukTYwt4A-gb{@)9oaIYF{V;<0QH> zqCFEklV4$3*qbb}O4O!=u?1L^Qpbw%cFH^SLKx?Fv1t(!w8u^|TyAL0RPoYx4G7OE z=dckfShV+HVd`|ZITEfOcc;ECn>>JBsms}R3{8tyru|7$-ZGga;tn*XMN)+K($YLX zzXYZ($(xS-Y0G?mH;UZK9GAY6+$S(EeG8j~pQmTC2RJcf9#ddWMs2_|e&yQ_P;>({ zRu4%x-OY~u8I2+8^NDh0-7=QwqOAYB7qso;s%n3Wpc3P9Y|327zQ(_0MzKTanKg%% zVMf*#wi!FJBG_a6M^adW&6$r^AhUk~ahvKM9rF0ekwCQe*16tL2?`nV}&^ z<+%JD+A48>UM(9y|NJ<&(Kq`zF3ex-R$9a9v+!j8&nHMP>kW*~S5J^0)Kl1$uO36( z!Qg^OH;xePA7OsMWLAJsu#+vpdj+AfoW-d(H08*^BC&-1@ZD8#aR!Y3yOclE9%D;5 zSJb*~l1}R&ZWLdqPM#L0(RfRo7Qo}v{p%6!zdp$yWyde&(6$v2c%8#1?kJXsuvL5no3%ACDO zC&QYO$?l6Y=Z9>rTC;Aw`Dn>I6KfQ|5QI)T9hVBa88qA95QLj#HZuM;MF@d{i#wgN zWkmQXtcIyh5{u>=QC?!t$^$V^54>n_j>wCG1MZ6yB5~5sxRq!N-WWlIBvf zPmmdWzwhFUt66-NY?4IPM^to@#58`A9Qze9X=t7@hV>c4y8HxooYz-ykxExcUF5_h zBib+c5gD(2n9lw#*=;;!@C|%9>^WMIsLWp))VgW$y0vRIm+Q(m#0#6sH)!=bJ$ ZT0_jNS^71xYs+V>Glv}LW7xMM;y;hb$j1Nx diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin index ba1718c7849a28ec0e0847236737d0d440358ed6..58afa79d2f98e86f2a752376f71df4fd9f82ee99 100644 GIT binary patch delta 2783 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 delta 2585 zcma)64^Wfm8UNlSH9@cme?-uL0SkvhB0&TM1SB9JBA@(|GOA+WNkl}&V3|{-1$G3H z);+Xm-EOXPcebnDaA_y7O#|Zut3~EHZ?5!)x?{=cY@FD7#g#qZ2bt_o?{>%YydTf= z%kTO7zD?&PP3I-@;-BU^KQ5fc-<2=95S$`qMYfluYdjf;C!fE=INKg&BV(hszpASJ zd`3&5^=7>S`ecribTL@vHUhkB1T-_&=*Wi_}25ntoUe6K!?Ny14IL0DVNvoqy zlfD=gLi$eBI?}${R?=^2^`x(9!#%k-9>9jl2fE|_96r^iN*)#AdR@F^x(E;HQdk^* zsEc9;@Sbk9=K>0?eDwx{^`}w2dIx*oc4YN-7F1cwalB*tHZ%`s$T*Js{1-Q(?VrOd z)3-~hzQy$M1KYRteT-FN>>91IvV(fy)o~U2@2a4$68EoB>x3*O?}Fa$)Joi>%9bFB z&eSe7wPlXYb}m_3A1U+-`_^PLl}#Eu!J@Bs37P&{%<+rIumeiLd_=T;BHHdfCQBKZ ze?qx{*;$N%aoZ$Q#aI^?wRE(UVp+PV0A=fUSZ6=(aiz6jeFFUsKj6VEpuw!gnYcsj79NUkXIqga1p5keRlZs<^Njsnj7i9lIE>hk zpbik+puA`YkKxV%ismqyU?|h|4xUd~9?&i1sr=KUlk;M$~>5xGzZ z<})JXluKw!$8e_|`X^=K*5HONunx@98t`~hsJCK9^!CwMIjY>>f|rucvQxM#Ig0JE zy_W3Bmh=(TvqDT@H7uZ#OYy?`s3dA0(LGRwo2&t?{{V(HE zxb(OL15!d33VU_c5(&Mx(2x@9F?&_uS2CHIEA4^liSTHuGwy<)ProBd27SpaUxlV+h!=35N*oSx| zeU&^-w3Old>FMkNkcAuhZASdPTG@{TLTz zyy(+B3vFP|kNzRrk77f{%aVoH@nObBHh>A4>zN7 zAE8fns%mhE<|=fCc%SeND^_GL^(hw1YrQ|aCd8DFC$a-p*oQzIH<6!!Dfkv_r~CAh z)BE=(+DL;p?^ry9!`bmZ`a5j{WEq^)nA-E`L0B+^-Z?s^$Fv*+Yr>WsP2`CIFhKLP ztlr`L(gUmy|3%C@>YlmAJfOD?Z{)06^7MwOy-;mxH>l=EUWckpKW1jMZHi$dII&5~ zTE zyg7okV%Fv$iLV`D^UCP8m)rX7PC2zH&jmexA}r)GG>3y9kL~4&i*sZ*|KuE@cQwN z#$K`YJqijnzu*_}#wq-qevRfgbVA414zd94DLfpZ&K&csdu(xe)TT z@&<9bd+~IBIwkOF{tD8JU*RvLO4s!!? z7{Jo-T;XmuhTcU+)?lkI@{njYSS(e0_gi+?nlzSunu?m5eZ~q)m8K+XgXW;6!nntF zuH@vRKvfOL*{L;NyXj8Q>7u80jARtIR{9?MMlT^Ggyy+GPUb=CAJ7bFHIgXgzVFm12VrrnXlH>yt)f`qfhjn*+K9--udW+a` z;#E->iFiKvJ`u+fP$Ph`0-g<8pmKZNRKA={tX3g(QDCA~A$CqpSR;K3)BcQFG)_`Y z4vn-_B2&;({Ew)EgmLp8Ga8%sEUR;IJ2~4U_`7yRylz*;!75!?y8D0qSqT!I9?v{PX~;IQvd(} From 7c538789dac550526897e80f80ec39b2102f06d4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 30 May 2018 15:02:16 +0200 Subject: [PATCH 1931/2380] bochs-display: enable vgabios Add vgabios binary to pci rom bar. Signed-off-by: Gerd Hoffmann --- hw/display/bochs-display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 1187d77576..12d8a66c6c 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -337,6 +337,7 @@ static void bochs_display_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_QEMU_VGA; k->realize = bochs_display_realize; + k->romfile = "vgabios-bochs-display.bin"; k->exit = bochs_display_exit; dc->vmsd = &vmstate_bochs_display; dc->props = bochs_display_properties; From 9f5d9c19c7d9c6eca21802a2777da0119bc22749 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 8 Jun 2018 12:38:47 +0200 Subject: [PATCH 1932/2380] ramfb: enable vgabios Add vgabios binary to fw_cfg vgaroms. Signed-off-by: Gerd Hoffmann --- hw/display/ramfb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/ramfb.c b/hw/display/ramfb.c index 30f5c8da20..25c8ad7c25 100644 --- a/hw/display/ramfb.c +++ b/hw/display/ramfb.c @@ -88,6 +88,7 @@ RAMFBState *ramfb_setup(Error **errp) s = g_new0(RAMFBState, 1); + rom_add_vga("vgabios-ramfb.bin"); fw_cfg_add_file_callback(fw_cfg, "etc/ramfb", NULL, ramfb_fw_cfg_write, s, &s->cfg, sizeof(s->cfg), false); From 70a77984b3ecc8adc7be42cd22fa3fd103874e49 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 27 Jun 2018 22:53:15 +0200 Subject: [PATCH 1933/2380] qemu-binfmt-conf.sh: cleanup --credential move credential value to its own variable to be able to manage more flags Signed-off-by: Laurent Vivier Message-Id: <20180627205317.10343-2-laurent@vivier.eu> --- scripts/qemu-binfmt-conf.sh | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index d7eefda0b8..9900554608 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -241,7 +241,12 @@ qemu_check_systemd() { } qemu_generate_register() { - echo ":qemu-$cpu:M::$magic:$mask:$qemu:$FLAGS" + flags="" + if [ "$CREDENTIAL" = "yes" ] ; then + flags="OC" + fi + + echo ":qemu-$cpu:M::$magic:$mask:$qemu:$flags" } qemu_register_interpreter() { @@ -260,10 +265,8 @@ package qemu-$cpu interpreter $qemu magic $magic mask $mask +credential $CREDENTIAL EOF - if [ "$FLAGS" = "OC" ] ; then - echo "credentials yes" >> "$EXPORTDIR/qemu-$cpu" - fi } qemu_set_binfmts() { @@ -300,7 +303,7 @@ SYSTEMDDIR="/etc/binfmt.d" DEBIANDIR="/usr/share/binfmts" QEMU_PATH=/usr/local/bin -FLAGS="" +CREDENTIAL=no options=$(getopt -o ds:Q:e:hc: -l debian,systemd:,qemu-path:,exportdir:,help,credential: -- "$@") eval set -- "$options" @@ -348,11 +351,7 @@ while true ; do ;; -c|--credential) shift - if [ "$1" = "yes" ] ; then - FLAGS="OC" - else - FLAGS="" - fi + CREDENTIAL="$1" ;; *) break From 01ecd22a29173168950a27551d0f3490b493467c Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 27 Jun 2018 22:53:16 +0200 Subject: [PATCH 1934/2380] qemu-binfmt-conf.sh: add persistent (F) flags Since kernel commit 948b701a607f (binfmt_misc: add persistent opened binary handler for containers) kernel allows to load the interpreter at the configuration time. In case of chroot, it allows to have the interpreter in the host root filesystem and not to copy it to the chroot filesystem. Signed-off-by: Laurent Vivier Message-Id: <20180627205317.10343-3-laurent@vivier.eu> --- scripts/qemu-binfmt-conf.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index 9900554608..00c9c3aa16 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -161,6 +161,7 @@ usage() { cat < Date: Wed, 27 Jun 2018 22:53:17 +0200 Subject: [PATCH 1935/2380] qemu-binfmt-conf.sh: allow to provide a suffix to the interpreter name some distros provide a qemu-CPU-static binary beside the qemu-CPU one. This change allows to use it by providing "--qemu-suffix -static" to the script. Signed-off-by: Laurent Vivier Message-Id: <20180627205317.10343-4-laurent@vivier.eu> --- scripts/qemu-binfmt-conf.sh | 41 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index 00c9c3aa16..23df00ae30 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -161,25 +161,26 @@ usage() { cat < Date: Wed, 27 Jun 2018 23:21:51 +0200 Subject: [PATCH 1936/2380] linux-user: add netlink CARRIER_UP_COUNT/CARRIER_DOWN_COUNT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Error is reported with libuv test suite: not ok 311 - udp_multicast_interface6 # exit code 134 # Output from process `udp_multicast_interface6`: # Unknown host QEMU_IFLA type: 47 # Unknown host QEMU_IFLA type: 48 # Unknown host QEMU_IFLA type: 47 # Unknown host QEMU_IFLA type: 48 # Unknown host QEMU_IFLA type: 47 # Unknown host QEMU_IFLA type: 48 # Unknown host QEMU_IFLA type: 47 # Unknown host QEMU_IFLA type: 48 # Unknown host QEMU_IFLA type: 47 # Unknown host QEMU_IFLA type: 48 Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180627212152.26525-2-laurent@vivier.eu> --- linux-user/syscall.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 2117fb13b4..4460f1e39a 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2522,6 +2522,8 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) case QEMU_IFLA_NUM_VF: case QEMU_IFLA_GSO_MAX_SEGS: case QEMU_IFLA_GSO_MAX_SIZE: + case QEMU_IFLA_CARRIER_UP_COUNT: + case QEMU_IFLA_CARRIER_DOWN_COUNT: u32 = RTA_DATA(rtattr); *u32 = tswap32(*u32); break; From 21749c4c2850dccd5ca8460f6c642bd12d5cb8c6 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 27 Jun 2018 23:21:52 +0200 Subject: [PATCH 1937/2380] linux-user: update do_setsockopt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add IPV6_MULTICAST_HOPS and IPV6_MULTICAST_LOOP that need 32bit value conversion Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20180627212152.26525-3-laurent@vivier.eu> --- linux-user/syscall.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 4460f1e39a..50e20fb659 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3019,6 +3019,8 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, case IPV6_V6ONLY: case IPV6_RECVPKTINFO: case IPV6_UNICAST_HOPS: + case IPV6_MULTICAST_HOPS: + case IPV6_MULTICAST_LOOP: case IPV6_RECVERR: case IPV6_RECVHOPLIMIT: case IPV6_2292HOPLIMIT: From 7606c99a0421be7e9d984766fe239f7791a2fd9c Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 22 Jun 2018 10:35:09 -0500 Subject: [PATCH 1938/2380] nbd/server: Fix dirty bitmap logic regression In my hurry to fix a build failure, I introduced a logic bug. The assertion conditional is backwards, meaning that qemu will now abort instead of reporting dirty bitmap status. The bug can only be tickled by an NBD client using an exported dirty bitmap (which is still an experimental QMP command), so it's not the end of the world for supported usage (and neither 'make check' nor qemu-iotests fails); but it also shows that we really want qemu-io support for reading dirty bitmaps if only so that I can add iotests coverage to prevent future brown-bag-of-shame events like this one. Fixes: 45eb6fb6 Signed-off-by: Eric Blake Message-Id: <20180622153509.375130-1-eblake@redhat.com> --- nbd/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nbd/server.c b/nbd/server.c index 50ac8bfafc..e52b76bd1a 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1977,7 +1977,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, bdrv_dirty_bitmap_unlock(bitmap); - assert(offset > end); + assert(offset < end); *length = end - offset; return i; } From 8ee1cef4593a7bda076891470c0620e79333c0d0 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 8 May 2018 16:27:18 -0500 Subject: [PATCH 1939/2380] iscsi: Avoid potential for get_status overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detected by Coverity: Multiplying two 32-bit int and assigning the result to a 64-bit number is a risk of overflow. Prior to the conversion to byte-based interfaces, the block layer took care of ensuring that a status request never exceeded 2G in the driver; but after that conversion, the block layer expects drivers to deal with any size request (the driver can always truncate the request size back down, as long as it makes progress). So, in the off-chance that someone makes a large request, we are at the mercy of whether iscsi_get_lba_status_task() will cap things to at most INT_MAX / iscsilun->block_size when it populates lbasd->num_blocks; since I could not easily audit that, it's better to be safe than sorry by just forcing a 64-bit multiply. Fixes: 92809c36 CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake Message-Id: <20180508212718.1482663-1-eblake@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- block/iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/iscsi.c b/block/iscsi.c index 9beb06d498..ead2bd5aa7 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -734,7 +734,7 @@ retry: goto out_unlock; } - *pnum = lbasd->num_blocks * iscsilun->block_size; + *pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size; if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { From 930fe17f9900e9c879834f2d2e5c301992623332 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 2 Jul 2018 15:46:29 -0400 Subject: [PATCH 1940/2380] blockdev: enable non-root nodes for backup source This is needed to implement the image-fleecing workflow where we create a temporary node backed by an active node, then start backupdev-backup sync=none from the active node to the temp node. In this case, the active node is now a root node AND a backing node, so it no longer qualifies as a root node, so we loosen the restriction on which nodes can be considered as the source for a backup. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Signed-off-by: John Snow Message-Id: <20180702194630.9360-2-jsnow@redhat.com> Signed-off-by: Eric Blake --- blockdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockdev.c b/blockdev.c index 58d7570932..72f5347df5 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1859,7 +1859,7 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp) assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); backup = common->action->u.blockdev_backup.data; - bs = qmp_get_root_bs(backup->device, errp); + bs = bdrv_lookup_bs(backup->device, backup->device, errp); if (!bs) { return; } @@ -3517,7 +3517,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, backup->compress = false; } - bs = qmp_get_root_bs(backup->device, errp); + bs = bdrv_lookup_bs(backup->device, backup->device, errp); if (!bs) { return NULL; } From bacebdedbf921a2c641a34486ff543089d338f32 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 2 Jul 2018 15:46:30 -0400 Subject: [PATCH 1941/2380] iotests: add 222 to test basic fleecing Signed-off-by: John Snow Message-Id: <20180702194630.9360-3-jsnow@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- tests/qemu-iotests/222 | 155 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/222.out | 67 ++++++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 223 insertions(+) create mode 100644 tests/qemu-iotests/222 create mode 100644 tests/qemu-iotests/222.out diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222 new file mode 100644 index 0000000000..ff3bfc1470 --- /dev/null +++ b/tests/qemu-iotests/222 @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# +# This test covers the basic fleecing workflow, which provides a +# point-in-time snapshot of a node that can be queried over NBD. +# +# Copyright (C) 2018 Red Hat, Inc. +# John helped, too. +# +# 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 . +# +# Creator/Owner: John Snow + +import iotests +from iotests import log, qemu_img, qemu_io, qemu_io_silent + +iotests.verify_platform(['linux']) + +patterns = [("0x5d", "0", "64k"), + ("0xd5", "1M", "64k"), + ("0xdc", "32M", "64k"), + ("0xcd", "0x3ff0000", "64k")] # 64M - 64K + +overwrite = [("0xab", "0", "64k"), # Full overwrite + ("0xad", "0x00f8000", "64k"), # Partial-left (1M-32K) + ("0x1d", "0x2008000", "64k"), # Partial-right (32M+32K) + ("0xea", "0x3fe0000", "64k")] # Adjacent-left (64M - 128K) + +zeroes = [("0", "0x00f8000", "32k"), # Left-end of partial-left (1M-32K) + ("0", "0x2010000", "32k"), # Right-end of partial-right (32M+64K) + ("0", "0x3fe0000", "64k")] # overwrite[3] + +remainder = [("0xd5", "0x108000", "32k"), # Right-end of partial-left [1] + ("0xdc", "32M", "32k"), # Left-end of partial-right [2] + ("0xcd", "0x3ff0000", "64k")] # patterns[3] + +with iotests.FilePath('base.img') as base_img_path, \ + iotests.FilePath('fleece.img') as fleece_img_path, \ + iotests.FilePath('nbd.sock') as nbd_sock_path, \ + iotests.VM() as vm: + + log('--- Setting up images ---') + log('') + + assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 + assert qemu_img('create', '-f', "qcow2", fleece_img_path, '64M') == 0 + + for p in patterns: + qemu_io('-f', iotests.imgfmt, + '-c', 'write -P%s %s %s' % p, base_img_path) + + log('Done') + + log('') + log('--- Launching VM ---') + log('') + + vm.add_drive(base_img_path) + vm.launch() + log('Done') + + log('') + log('--- Setting up Fleecing Graph ---') + log('') + + src_node = "drive0" + tgt_node = "fleeceNode" + + # create tgt_node backed by src_node + log(vm.qmp("blockdev-add", **{ + "driver": "qcow2", + "node-name": tgt_node, + "file": { + "driver": "file", + "filename": fleece_img_path, + }, + "backing": src_node, + })) + + # Establish COW from source to fleecing node + log(vm.qmp("blockdev-backup", + device=src_node, + target=tgt_node, + sync="none")) + + log('') + log('--- Setting up NBD Export ---') + log('') + + nbd_uri = 'nbd+unix:///%s?socket=%s' % (tgt_node, nbd_sock_path) + log(vm.qmp("nbd-server-start", + **{"addr": { "type": "unix", + "data": { "path": nbd_sock_path } } })) + + log(vm.qmp("nbd-server-add", device=tgt_node)) + + log('') + log('--- Sanity Check ---') + log('') + + for p in (patterns + zeroes): + cmd = "read -P%s %s %s" % p + log(cmd) + assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 + + log('') + log('--- Testing COW ---') + log('') + + for p in overwrite: + cmd = "write -P%s %s %s" % p + log(cmd) + log(vm.hmp_qemu_io(src_node, cmd)) + + log('') + log('--- Verifying Data ---') + log('') + + for p in (patterns + zeroes): + cmd = "read -P%s %s %s" % p + log(cmd) + assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 + + log('') + log('--- Cleanup ---') + log('') + + log(vm.qmp('block-job-cancel', device=src_node)) + log(vm.event_wait('BLOCK_JOB_CANCELLED'), + filters=[iotests.filter_qmp_event]) + log(vm.qmp('nbd-server-stop')) + log(vm.qmp('blockdev-del', node_name=tgt_node)) + vm.shutdown() + + log('') + log('--- Confirming writes ---') + log('') + + for p in (overwrite + remainder): + cmd = "read -P%s %s %s" % p + log(cmd) + assert qemu_io_silent(base_img_path, '-c', cmd) == 0 + + log('') + log('Done') diff --git a/tests/qemu-iotests/222.out b/tests/qemu-iotests/222.out new file mode 100644 index 0000000000..48f336a02b --- /dev/null +++ b/tests/qemu-iotests/222.out @@ -0,0 +1,67 @@ +--- Setting up images --- + +Done + +--- Launching VM --- + +Done + +--- Setting up Fleecing Graph --- + +{u'return': {}} +{u'return': {}} + +--- Setting up NBD Export --- + +{u'return': {}} +{u'return': {}} + +--- Sanity Check --- + +read -P0x5d 0 64k +read -P0xd5 1M 64k +read -P0xdc 32M 64k +read -P0xcd 0x3ff0000 64k +read -P0 0x00f8000 32k +read -P0 0x2010000 32k +read -P0 0x3fe0000 64k + +--- Testing COW --- + +write -P0xab 0 64k +{u'return': u''} +write -P0xad 0x00f8000 64k +{u'return': u''} +write -P0x1d 0x2008000 64k +{u'return': u''} +write -P0xea 0x3fe0000 64k +{u'return': u''} + +--- Verifying Data --- + +read -P0x5d 0 64k +read -P0xd5 1M 64k +read -P0xdc 32M 64k +read -P0xcd 0x3ff0000 64k +read -P0 0x00f8000 32k +read -P0 0x2010000 32k +read -P0 0x3fe0000 64k + +--- Cleanup --- + +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'drive0', u'type': u'backup', u'speed': 0, u'len': 67108864, u'offset': 393216}, u'event': u'BLOCK_JOB_CANCELLED'} +{u'return': {}} +{u'return': {}} + +--- Confirming writes --- + +read -P0xab 0 64k +read -P0xad 0x00f8000 64k +read -P0x1d 0x2008000 64k +read -P0xea 0x3fe0000 64k +read -P0xd5 0x108000 32k +read -P0xdc 32M 32k +read -P0xcd 0x3ff0000 64k + +Done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index eea75819d2..8019a9f721 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -220,3 +220,4 @@ 218 rw auto quick 219 rw auto 221 rw auto quick +222 rw auto quick From 216ee3657e14013505abe7853cecb632199fb13e Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 2 Jul 2018 14:14:57 -0500 Subject: [PATCH 1942/2380] nbd/client: Add x-dirty-bitmap to query bitmap from server In order to test that the NBD server is properly advertising dirty bitmaps, we need a bare minimum client that can request and read the context. Since feature freeze for 3.0 is imminent, this is the smallest workable patch, which replaces the qemu block status report with the results of the NBD server's dirty bitmap (making it very easy to use 'qemu-img map --output=json' to learn where the dirty portions are). Note that the NBD protocol defines a dirty section with the same bit but opposite sense that normal "base:allocation" uses to report an allocated section; so in qemu-img map output, "data":true corresponds to clean, "data":false corresponds to dirty. A more complete solution that allows dirty bitmaps to be queried at the same time as normal block status will be required before this addition can lose the x- prefix. Until then, the fact that this replaces normal status with dirty status means actions like 'qemu-img convert' will likely misbehave due to treating dirty regions of the file as if they are unallocated. The next patch adds an iotest to exercise this new code. Signed-off-by: Eric Blake Message-Id: <20180702191458.28741-2-eblake@redhat.com> --- block/nbd-client.c | 3 +++ block/nbd-client.h | 1 + block/nbd.c | 10 ++++++++-- include/block/nbd.h | 1 + nbd/client.c | 4 ++-- qapi/block-core.json | 7 ++++++- 6 files changed, 21 insertions(+), 5 deletions(-) diff --git a/block/nbd-client.c b/block/nbd-client.c index 8d69eaaa32..9686ecbd5e 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -970,6 +970,7 @@ int nbd_client_init(BlockDriverState *bs, const char *export, QCryptoTLSCreds *tlscreds, const char *hostname, + const char *x_dirty_bitmap, Error **errp) { NBDClientSession *client = nbd_get_client_session(bs); @@ -982,9 +983,11 @@ int nbd_client_init(BlockDriverState *bs, client->info.request_sizes = true; client->info.structured_reply = true; client->info.base_allocation = true; + client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap); ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, tlscreds, hostname, &client->ioc, &client->info, errp); + g_free(client->info.x_dirty_bitmap); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); return ret; diff --git a/block/nbd-client.h b/block/nbd-client.h index 0ece76e5af..cfc90550b9 100644 --- a/block/nbd-client.h +++ b/block/nbd-client.h @@ -45,6 +45,7 @@ int nbd_client_init(BlockDriverState *bs, const char *export_name, QCryptoTLSCreds *tlscreds, const char *hostname, + const char *x_dirty_bitmap, Error **errp); void nbd_client_close(BlockDriverState *bs); diff --git a/block/nbd.c b/block/nbd.c index 13db4030e6..b198ad775f 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -378,6 +378,12 @@ static QemuOptsList nbd_runtime_opts = { .type = QEMU_OPT_STRING, .help = "ID of the TLS credentials to use", }, + { + .name = "x-dirty-bitmap", + .type = QEMU_OPT_STRING, + .help = "experimental: expose named dirty bitmap in place of " + "block status", + }, { /* end of list */ } }, }; @@ -438,8 +444,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, } /* NBD handshake */ - ret = nbd_client_init(bs, sioc, s->export, - tlscreds, hostname, errp); + ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname, + qemu_opt_get(opts, "x-dirty-bitmap"), errp); error: if (sioc) { object_unref(OBJECT(sioc)); diff --git a/include/block/nbd.h b/include/block/nbd.h index daaeae61bf..4638c839f5 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -259,6 +259,7 @@ static inline bool nbd_reply_type_is_error(int type) struct NBDExportInfo { /* Set by client before nbd_receive_negotiate() */ bool request_sizes; + char *x_dirty_bitmap; /* In-out fields, set by client before nbd_receive_negotiate() and * updated by server results during nbd_receive_negotiate() */ diff --git a/nbd/client.c b/nbd/client.c index 232ff4f46d..40b74d9761 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2017 Red Hat, Inc. + * Copyright (C) 2016-2018 Red Hat, Inc. * Copyright (C) 2005 Anthony Liguori * * Network Block Device Client Side @@ -831,7 +831,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, if (info->structured_reply && base_allocation) { result = nbd_negotiate_simple_meta_context( - ioc, name, "base:allocation", + ioc, name, info->x_dirty_bitmap ?: "base:allocation", &info->meta_base_allocation_id, errp); if (result < 0) { goto fail; diff --git a/qapi/block-core.json b/qapi/block-core.json index 577ce5e999..90e554ed0f 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3471,12 +3471,17 @@ # # @tls-creds: TLS credentials ID # +# @x-dirty-bitmap: A "qemu:dirty-bitmap:NAME" string to query in place of +# traditional "base:allocation" block status (see +# NBD_OPT_LIST_META_CONTEXT in the NBD protocol) (since 3.0) +# # Since: 2.9 ## { 'struct': 'BlockdevOptionsNbd', 'data': { 'server': 'SocketAddress', '*export': 'str', - '*tls-creds': 'str' } } + '*tls-creds': 'str', + '*x-dirty-bitmap': 'str' } } ## # @BlockdevOptionsRaw: From b7d678135f0294489e0458ef97088e848d08dfb5 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 22 Jun 2018 09:00:08 +0100 Subject: [PATCH 1943/2380] mac_dbdma: only dump commands for debug enabled channels This enables us to apply the same filter in DEBUG_DBDMA_CHANMASK to the DBDMA command execution debug output. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/mac_dbdma.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index 1b2a69b3ef..87ae246d37 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -71,18 +71,19 @@ static DBDMAState *dbdma_from_ch(DBDMA_channel *ch) } #if DEBUG_DBDMA -static void dump_dbdma_cmd(dbdma_cmd *cmd) +static void dump_dbdma_cmd(DBDMA_channel *ch, dbdma_cmd *cmd) { - printf("dbdma_cmd %p\n", cmd); - printf(" req_count 0x%04x\n", le16_to_cpu(cmd->req_count)); - printf(" command 0x%04x\n", le16_to_cpu(cmd->command)); - printf(" phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr)); - printf(" cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep)); - printf(" res_count 0x%04x\n", le16_to_cpu(cmd->res_count)); - printf(" xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status)); + DBDMA_DPRINTFCH(ch, "dbdma_cmd %p\n", cmd); + DBDMA_DPRINTFCH(ch, " req_count 0x%04x\n", le16_to_cpu(cmd->req_count)); + DBDMA_DPRINTFCH(ch, " command 0x%04x\n", le16_to_cpu(cmd->command)); + DBDMA_DPRINTFCH(ch, " phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr)); + DBDMA_DPRINTFCH(ch, " cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep)); + DBDMA_DPRINTFCH(ch, " res_count 0x%04x\n", le16_to_cpu(cmd->res_count)); + DBDMA_DPRINTFCH(ch, " xfer_status 0x%04x\n", + le16_to_cpu(cmd->xfer_status)); } #else -static void dump_dbdma_cmd(dbdma_cmd *cmd) +static void dump_dbdma_cmd(DBDMA_channel *ch, dbdma_cmd *cmd) { } #endif @@ -448,7 +449,7 @@ static void channel_run(DBDMA_channel *ch) uint32_t phy_addr; DBDMA_DPRINTFCH(ch, "channel_run\n"); - dump_dbdma_cmd(current); + dump_dbdma_cmd(ch, current); /* clear WAKE flag at command fetch */ From 5107a9cb431b816af6d7e450c22b044198bc6d18 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 22 Jun 2018 09:00:09 +0100 Subject: [PATCH 1944/2380] mac_newworld: always enable disable_direct_reg3_writes for ADB machines Commit 84051eb400 "adb: add property to disable direct reg 3 writes" added a workaround for MacOS 9 incorrectly setting the mouse address during boot of PMU machines. Further testing has shown that since fb6649f172 "adb: fix read reg 3 byte ordering" this can still sometimes happen with the CUDA mac99 machine, so let's enable this workaround for all New World machines using ADB for now. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/ppc/mac_newworld.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index ff715ffffd..2b13fcdde5 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -407,11 +407,11 @@ static void ppc_core99_init(MachineState *machine) adb_bus = qdev_get_child_bus(dev, "adb.0"); dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); - qdev_prop_set_bit(dev, "disable-direct-reg3-writes", has_pmu); + qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true); qdev_init_nofail(dev); dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); - qdev_prop_set_bit(dev, "disable-direct-reg3-writes", has_pmu); + qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true); qdev_init_nofail(dev); } From 43f7868da3c84c1c7eef89631ab0bf6dc194eedb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 23 Jun 2018 14:18:05 -0700 Subject: [PATCH 1945/2380] sam460ex: Fix sam460ex device tree when booting the Linux kernel sam460ex (or at least this emulation) does not support the "ibm,cpm" power management. As a result, Linux crashes when trying to access it. Remove its device tree node. Also, if/when we boot the Linux kernel directly, serial port clock frequencies in the device tree file will be unset, and serial port initialization will fail. Add valid frequency values to the serial ports to be able to use it. Also set valid values for the other clock nodes otherwise set by u-boot. Signed-off-by: Guenter Roeck Reviewed-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/ppc/sam460ex.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index bdc53d2603..33ea51816c 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -37,6 +37,8 @@ #include "hw/i2c/smbus.h" #include "hw/usb/hcd-ehci.h" +#include + #define BINARY_DEVICE_TREE_FILE "canyonlands.dtb" #define UBOOT_FILENAME "u-boot-sam460-20100605.bin" /* to extract the official U-Boot bin from the updater: */ @@ -67,6 +69,10 @@ */ #define CPU_FREQ 1150000000 +#define PLB_FREQ 230000000 +#define OPB_FREQ 115000000 +#define EBC_FREQ 115000000 +#define UART_FREQ 11059200 #define SDRAM_NR_BANKS 4 /* FIXME: See u-boot.git 8ac41e, also fix in ppc440_uc.c */ @@ -255,6 +261,7 @@ static int sam460ex_load_device_tree(hwaddr addr, void *fdt; uint32_t tb_freq = CPU_FREQ; uint32_t clock_freq = CPU_FREQ; + int offset; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { @@ -308,6 +315,27 @@ static int sam460ex_load_device_tree(hwaddr addr, qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", tb_freq); + /* Remove cpm node if it exists (it is not emulated) */ + offset = fdt_path_offset(fdt, "/cpm"); + if (offset >= 0) { + fdt_nop_node(fdt, offset); + } + + /* set serial port clocks */ + offset = fdt_node_offset_by_compatible(fdt, -1, "ns16550"); + while (offset >= 0) { + fdt_setprop_cell(fdt, offset, "clock-frequency", UART_FREQ); + offset = fdt_node_offset_by_compatible(fdt, offset, "ns16550"); + } + + /* some more clocks */ + qemu_fdt_setprop_cell(fdt, "/plb", "clock-frequency", + PLB_FREQ); + qemu_fdt_setprop_cell(fdt, "/plb/opb", "clock-frequency", + OPB_FREQ); + qemu_fdt_setprop_cell(fdt, "/plb/opb/ebc", "clock-frequency", + EBC_FREQ); + rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); ret = fdt_size; From a028dd423ee6dfd091a8c63028240832bf10f671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Jun 2018 12:24:13 +0200 Subject: [PATCH 1946/2380] ppc/xics: introduce ICP DeviceRealize and DeviceReset handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the ICP realize and reset handlers in DeviceRealize and DeviceReset handlers. parent handlers are now called from the inheriting classes which is a cleaner object pattern. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics.c | 10 ---------- hw/intc/xics_kvm.c | 34 +++++++++++++++++++++++++++------- hw/intc/xics_pnv.c | 15 +++++++++++++-- include/hw/ppc/xics.h | 5 +++-- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index e73e623e3b..063491f387 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -294,7 +294,6 @@ static const VMStateDescription vmstate_icp_server = { static void icp_reset(void *dev) { ICPState *icp = ICP(dev); - ICPStateClass *icpc = ICP_GET_CLASS(icp); icp->xirr = 0; icp->pending_priority = 0xff; @@ -302,16 +301,11 @@ static void icp_reset(void *dev) /* Make all outputs are deasserted */ qemu_set_irq(icp->output, 0); - - if (icpc->reset) { - icpc->reset(icp); - } } static void icp_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); - ICPStateClass *icpc = ICP_GET_CLASS(dev); PowerPCCPU *cpu; CPUPPCState *env; Object *obj; @@ -351,10 +345,6 @@ static void icp_realize(DeviceState *dev, Error **errp) return; } - if (icpc->realize) { - icpc->realize(icp, errp); - } - qemu_register_reset(icp_reset, dev); vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); } diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 8dba2f84e7..f511e50a80 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -114,22 +114,38 @@ static int icp_set_kvm_state(ICPState *icp, int version_id) return 0; } -static void icp_kvm_reset(ICPState *icp) +static void icp_kvm_reset(DeviceState *dev) { - icp_set_kvm_state(icp, 1); + ICPStateClass *icpc = ICP_GET_CLASS(dev); + + icpc->parent_reset(dev); + + icp_set_kvm_state(ICP(dev), 1); } -static void icp_kvm_realize(ICPState *icp, Error **errp) +static void icp_kvm_realize(DeviceState *dev, Error **errp) { - CPUState *cs = icp->cs; + ICPState *icp = ICP(dev); + ICPStateClass *icpc = ICP_GET_CLASS(icp); + Error *local_err = NULL; + CPUState *cs; KVMEnabledICP *enabled_icp; - unsigned long vcpu_id = kvm_arch_vcpu_id(cs); + unsigned long vcpu_id; int ret; if (kernel_xics_fd == -1) { abort(); } + icpc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + cs = icp->cs; + vcpu_id = kvm_arch_vcpu_id(cs); + /* * If we are reusing a parked vCPU fd corresponding to the CPU * which was hot-removed earlier we don't have to renable @@ -154,12 +170,16 @@ static void icp_kvm_realize(ICPState *icp, Error **errp) static void icp_kvm_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); ICPStateClass *icpc = ICP_CLASS(klass); + device_class_set_parent_realize(dc, icp_kvm_realize, + &icpc->parent_realize); + device_class_set_parent_reset(dc, icp_kvm_reset, + &icpc->parent_reset); + icpc->pre_save = icp_get_kvm_state; icpc->post_load = icp_set_kvm_state; - icpc->realize = icp_kvm_realize; - icpc->reset = icp_kvm_reset; icpc->synchronize_state = icp_synchronize_state; } diff --git a/hw/intc/xics_pnv.c b/hw/intc/xics_pnv.c index c87de2189c..fa48505f36 100644 --- a/hw/intc/xics_pnv.c +++ b/hw/intc/xics_pnv.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "qemu/log.h" #include "hw/ppc/xics.h" @@ -158,9 +159,18 @@ static const MemoryRegionOps pnv_icp_ops = { }, }; -static void pnv_icp_realize(ICPState *icp, Error **errp) +static void pnv_icp_realize(DeviceState *dev, Error **errp) { + ICPState *icp = ICP(dev); PnvICPState *pnv_icp = PNV_ICP(icp); + ICPStateClass *icpc = ICP_GET_CLASS(icp); + Error *local_err = NULL; + + icpc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops, icp, "icp-thread", 0x1000); @@ -171,7 +181,8 @@ static void pnv_icp_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); ICPStateClass *icpc = ICP_CLASS(klass); - icpc->realize = pnv_icp_realize; + device_class_set_parent_realize(dc, pnv_icp_realize, + &icpc->parent_realize); dc->desc = "PowerNV ICP"; } diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 6cebff47a7..4b04b295a7 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -65,10 +65,11 @@ typedef struct XICSFabric XICSFabric; struct ICPStateClass { DeviceClass parent_class; - void (*realize)(ICPState *icp, Error **errp); + DeviceRealize parent_realize; + DeviceReset parent_reset; + void (*pre_save)(ICPState *icp); int (*post_load)(ICPState *icp, int version_id); - void (*reset)(ICPState *icp); void (*synchronize_state)(ICPState *icp); }; From 0a647b76dbf29e6a20c328cd8676a1ca49526f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 25 Jun 2018 11:17:14 +0200 Subject: [PATCH 1947/2380] ppc/xics: introduce a parent_realize in ICSStateClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes possible to move the common ICSState code of the realize handlers in the ics-base class. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics.c | 37 ++++++++++++++++++++++--------------- hw/intc/xics_kvm.c | 20 +++++++++++++++----- include/hw/ppc/xics.h | 3 ++- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 063491f387..d6066d561f 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -618,30 +618,31 @@ static void ics_simple_initfn(Object *obj) ics->offset = XICS_IRQ_BASE; } -static void ics_simple_realize(ICSState *ics, Error **errp) +static void ics_simple_realize(DeviceState *dev, Error **errp) { - if (!ics->nr_irqs) { - error_setg(errp, "Number of interrupts needs to be greater 0"); + ICSState *ics = ICS_SIMPLE(dev); + ICSStateClass *icsc = ICS_BASE_GET_CLASS(ics); + Error *local_err = NULL; + + icsc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } - ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); + ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); qemu_register_reset(ics_simple_reset, ics); } -static Property ics_simple_properties[] = { - DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), - DEFINE_PROP_END_OF_LIST(), -}; - static void ics_simple_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICSStateClass *isc = ICS_BASE_CLASS(klass); - isc->realize = ics_simple_realize; - dc->props = ics_simple_properties; + device_class_set_parent_realize(dc, ics_simple_realize, + &isc->parent_realize); + dc->vmsd = &vmstate_ics_simple; isc->reject = ics_simple_reject; isc->resend = ics_simple_resend; @@ -659,7 +660,6 @@ static const TypeInfo ics_simple_info = { static void ics_base_realize(DeviceState *dev, Error **errp) { - ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); ICSState *ics = ICS_BASE(dev); Object *obj; Error *err = NULL; @@ -672,17 +672,24 @@ static void ics_base_realize(DeviceState *dev, Error **errp) } ics->xics = XICS_FABRIC(obj); - - if (icsc->realize) { - icsc->realize(ics, errp); + if (!ics->nr_irqs) { + error_setg(errp, "Number of interrupts needs to be greater 0"); + return; } + ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); } +static Property ics_base_properties[] = { + DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void ics_base_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ics_base_realize; + dc->props = ics_base_properties; } static const TypeInfo ics_base_info = { diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index f511e50a80..1f27eb4979 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -345,13 +345,17 @@ static void ics_kvm_reset(void *dev) ics_set_kvm_state(ics, 1); } -static void ics_kvm_realize(ICSState *ics, Error **errp) +static void ics_kvm_realize(DeviceState *dev, Error **errp) { - if (!ics->nr_irqs) { - error_setg(errp, "Number of interrupts needs to be greater 0"); + ICSState *ics = ICS_KVM(dev); + ICSStateClass *icsc = ICS_BASE_GET_CLASS(ics); + Error *local_err = NULL; + + icsc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } - ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs); qemu_register_reset(ics_kvm_reset, ics); @@ -360,8 +364,14 @@ static void ics_kvm_realize(ICSState *ics, Error **errp) static void ics_kvm_class_init(ObjectClass *klass, void *data) { ICSStateClass *icsc = ICS_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + /* + * Use device_class_set_parent_realize() when ics-kvm inherits + * directly from ics-base and not from ics-simple anymore. + */ + dc->realize = ics_kvm_realize; - icsc->realize = ics_kvm_realize; icsc->pre_save = ics_get_kvm_state; icsc->post_load = ics_set_kvm_state; icsc->synchronize_state = ics_synchronize_state; diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 4b04b295a7..44e96e6400 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -115,7 +115,8 @@ struct PnvICPState { struct ICSStateClass { DeviceClass parent_class; - void (*realize)(ICSState *s, Error **errp); + DeviceRealize parent_realize; + void (*pre_save)(ICSState *s); int (*post_load)(ICSState *s, int version_id); void (*reject)(ICSState *s, uint32_t irq); From 815049a01ba187d48166f0144356bc640d4e5803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 25 Jun 2018 11:17:15 +0200 Subject: [PATCH 1948/2380] ppc/xics: move the instance_init handler under the ics-base class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index d6066d561f..83340770f7 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -611,13 +611,6 @@ static const VMStateDescription vmstate_ics_simple = { }, }; -static void ics_simple_initfn(Object *obj) -{ - ICSState *ics = ICS_SIMPLE(obj); - - ics->offset = XICS_IRQ_BASE; -} - static void ics_simple_realize(DeviceState *dev, Error **errp) { ICSState *ics = ICS_SIMPLE(dev); @@ -655,7 +648,6 @@ static const TypeInfo ics_simple_info = { .instance_size = sizeof(ICSState), .class_init = ics_simple_class_init, .class_size = sizeof(ICSStateClass), - .instance_init = ics_simple_initfn, }; static void ics_base_realize(DeviceState *dev, Error **errp) @@ -679,6 +671,13 @@ static void ics_base_realize(DeviceState *dev, Error **errp) ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); } +static void ics_base_instance_init(Object *obj) +{ + ICSState *ics = ICS_BASE(obj); + + ics->offset = XICS_IRQ_BASE; +} + static Property ics_base_properties[] = { DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), DEFINE_PROP_END_OF_LIST(), @@ -697,6 +696,7 @@ static const TypeInfo ics_base_info = { .parent = TYPE_DEVICE, .abstract = true, .instance_size = sizeof(ICSState), + .instance_init = ics_base_instance_init, .class_init = ics_base_class_init, .class_size = sizeof(ICSStateClass), }; From eeefd43b3cf342d1696128462a16e092995ff1b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 25 Jun 2018 11:17:16 +0200 Subject: [PATCH 1949/2380] ppx/xics: introduce a parent_reset in ICSStateClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just like for the realize handlers, this makes possible to move the common ICSState code of the reset handlers in the ics-base class. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics.c | 45 ++++++++++++++++++++++++++++--------------- hw/intc/xics_kvm.c | 26 ++++++++++--------------- include/hw/ppc/xics.h | 1 + 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 83340770f7..8cfe223153 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -537,23 +537,16 @@ static void ics_simple_eoi(ICSState *ics, uint32_t nr) } } -static void ics_simple_reset(void *dev) +static void ics_simple_reset(DeviceState *dev) { - ICSState *ics = ICS_SIMPLE(dev); - int i; - uint8_t flags[ics->nr_irqs]; + ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); - for (i = 0; i < ics->nr_irqs; i++) { - flags[i] = ics->irqs[i].flags; - } + icsc->parent_reset(dev); +} - memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); - - for (i = 0; i < ics->nr_irqs; i++) { - ics->irqs[i].priority = 0xff; - ics->irqs[i].saved_priority = 0xff; - ics->irqs[i].flags = flags[i]; - } +static void ics_simple_reset_handler(void *dev) +{ + ics_simple_reset(dev); } static int ics_simple_dispatch_pre_save(void *opaque) @@ -625,7 +618,7 @@ static void ics_simple_realize(DeviceState *dev, Error **errp) ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); - qemu_register_reset(ics_simple_reset, ics); + qemu_register_reset(ics_simple_reset_handler, ics); } static void ics_simple_class_init(ObjectClass *klass, void *data) @@ -635,6 +628,8 @@ static void ics_simple_class_init(ObjectClass *klass, void *data) device_class_set_parent_realize(dc, ics_simple_realize, &isc->parent_realize); + device_class_set_parent_reset(dc, ics_simple_reset, + &isc->parent_reset); dc->vmsd = &vmstate_ics_simple; isc->reject = ics_simple_reject; @@ -650,6 +645,25 @@ static const TypeInfo ics_simple_info = { .class_size = sizeof(ICSStateClass), }; +static void ics_base_reset(DeviceState *dev) +{ + ICSState *ics = ICS_BASE(dev); + int i; + uint8_t flags[ics->nr_irqs]; + + for (i = 0; i < ics->nr_irqs; i++) { + flags[i] = ics->irqs[i].flags; + } + + memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); + + for (i = 0; i < ics->nr_irqs; i++) { + ics->irqs[i].priority = 0xff; + ics->irqs[i].saved_priority = 0xff; + ics->irqs[i].flags = flags[i]; + } +} + static void ics_base_realize(DeviceState *dev, Error **errp) { ICSState *ics = ICS_BASE(dev); @@ -689,6 +703,7 @@ static void ics_base_class_init(ObjectClass *klass, void *data) dc->realize = ics_base_realize; dc->props = ics_base_properties; + dc->reset = ics_base_reset; } static const TypeInfo ics_base_info = { diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 1f27eb4979..b314eb7d16 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -324,25 +324,18 @@ static void ics_kvm_set_irq(void *opaque, int srcno, int val) } } -static void ics_kvm_reset(void *dev) +static void ics_kvm_reset(DeviceState *dev) { - ICSState *ics = ICS_SIMPLE(dev); - int i; - uint8_t flags[ics->nr_irqs]; + ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); - for (i = 0; i < ics->nr_irqs; i++) { - flags[i] = ics->irqs[i].flags; - } + icsc->parent_reset(dev); - memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); + ics_set_kvm_state(ICS_KVM(dev), 1); +} - for (i = 0; i < ics->nr_irqs; i++) { - ics->irqs[i].priority = 0xff; - ics->irqs[i].saved_priority = 0xff; - ics->irqs[i].flags = flags[i]; - } - - ics_set_kvm_state(ics, 1); +static void ics_kvm_reset_handler(void *dev) +{ + ics_kvm_reset(dev); } static void ics_kvm_realize(DeviceState *dev, Error **errp) @@ -358,7 +351,7 @@ static void ics_kvm_realize(DeviceState *dev, Error **errp) } ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs); - qemu_register_reset(ics_kvm_reset, ics); + qemu_register_reset(ics_kvm_reset_handler, ics); } static void ics_kvm_class_init(ObjectClass *klass, void *data) @@ -371,6 +364,7 @@ static void ics_kvm_class_init(ObjectClass *klass, void *data) * directly from ics-base and not from ics-simple anymore. */ dc->realize = ics_kvm_realize; + dc->reset = ics_kvm_reset; icsc->pre_save = ics_get_kvm_state; icsc->post_load = ics_set_kvm_state; diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 44e96e6400..6ac8a9392d 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -116,6 +116,7 @@ struct ICSStateClass { DeviceClass parent_class; DeviceRealize parent_realize; + DeviceReset parent_reset; void (*pre_save)(ICSState *s); int (*post_load)(ICSState *s, int version_id); From c8b1846f238ea5702ccbc0c3e2760c8fee875808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 25 Jun 2018 11:17:17 +0200 Subject: [PATCH 1950/2380] ppc/xics: move the vmstate structures under the ics-base class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics.c | 112 ++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 8cfe223153..b9f1a3c972 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -549,61 +549,6 @@ static void ics_simple_reset_handler(void *dev) ics_simple_reset(dev); } -static int ics_simple_dispatch_pre_save(void *opaque) -{ - ICSState *ics = opaque; - ICSStateClass *info = ICS_BASE_GET_CLASS(ics); - - if (info->pre_save) { - info->pre_save(ics); - } - - return 0; -} - -static int ics_simple_dispatch_post_load(void *opaque, int version_id) -{ - ICSState *ics = opaque; - ICSStateClass *info = ICS_BASE_GET_CLASS(ics); - - if (info->post_load) { - return info->post_load(ics, version_id); - } - - return 0; -} - -static const VMStateDescription vmstate_ics_simple_irq = { - .name = "ics/irq", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(server, ICSIRQState), - VMSTATE_UINT8(priority, ICSIRQState), - VMSTATE_UINT8(saved_priority, ICSIRQState), - VMSTATE_UINT8(status, ICSIRQState), - VMSTATE_UINT8(flags, ICSIRQState), - VMSTATE_END_OF_LIST() - }, -}; - -static const VMStateDescription vmstate_ics_simple = { - .name = "ics", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = ics_simple_dispatch_pre_save, - .post_load = ics_simple_dispatch_post_load, - .fields = (VMStateField[]) { - /* Sanity check */ - VMSTATE_UINT32_EQUAL(nr_irqs, ICSState, NULL), - - VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, - vmstate_ics_simple_irq, - ICSIRQState), - VMSTATE_END_OF_LIST() - }, -}; - static void ics_simple_realize(DeviceState *dev, Error **errp) { ICSState *ics = ICS_SIMPLE(dev); @@ -631,7 +576,6 @@ static void ics_simple_class_init(ObjectClass *klass, void *data) device_class_set_parent_reset(dc, ics_simple_reset, &isc->parent_reset); - dc->vmsd = &vmstate_ics_simple; isc->reject = ics_simple_reject; isc->resend = ics_simple_resend; isc->eoi = ics_simple_eoi; @@ -692,6 +636,61 @@ static void ics_base_instance_init(Object *obj) ics->offset = XICS_IRQ_BASE; } +static int ics_base_dispatch_pre_save(void *opaque) +{ + ICSState *ics = opaque; + ICSStateClass *info = ICS_BASE_GET_CLASS(ics); + + if (info->pre_save) { + info->pre_save(ics); + } + + return 0; +} + +static int ics_base_dispatch_post_load(void *opaque, int version_id) +{ + ICSState *ics = opaque; + ICSStateClass *info = ICS_BASE_GET_CLASS(ics); + + if (info->post_load) { + return info->post_load(ics, version_id); + } + + return 0; +} + +static const VMStateDescription vmstate_ics_base_irq = { + .name = "ics/irq", + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(server, ICSIRQState), + VMSTATE_UINT8(priority, ICSIRQState), + VMSTATE_UINT8(saved_priority, ICSIRQState), + VMSTATE_UINT8(status, ICSIRQState), + VMSTATE_UINT8(flags, ICSIRQState), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_ics_base = { + .name = "ics", + .version_id = 1, + .minimum_version_id = 1, + .pre_save = ics_base_dispatch_pre_save, + .post_load = ics_base_dispatch_post_load, + .fields = (VMStateField[]) { + /* Sanity check */ + VMSTATE_UINT32_EQUAL(nr_irqs, ICSState, NULL), + + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, + vmstate_ics_base_irq, + ICSIRQState), + VMSTATE_END_OF_LIST() + }, +}; + static Property ics_base_properties[] = { DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), DEFINE_PROP_END_OF_LIST(), @@ -704,6 +703,7 @@ static void ics_base_class_init(ObjectClass *klass, void *data) dc->realize = ics_base_realize; dc->props = ics_base_properties; dc->reset = ics_base_reset; + dc->vmsd = &vmstate_ics_base; } static const TypeInfo ics_base_info = { From abe82ebb2005eef846fd62652b0107d268c8e06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 25 Jun 2018 11:17:18 +0200 Subject: [PATCH 1951/2380] ppc/xics: rework the ICS classes inheritance tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the previous changes, we can now let the ICS_KVM class inherit directly from ICS_BASE class and not from the intermediate ICS_SIMPLE. It makes the class hierarchy much cleaner. What is left in the top classes is the low level interface to access the KVM XICS device in ICS_KVM and the XICS emulating handlers in ICS_SIMPLE. This should not break migration compatibility. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics_kvm.c | 12 +++++------- hw/ppc/spapr.c | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index b314eb7d16..30c3769a20 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -359,12 +359,10 @@ static void ics_kvm_class_init(ObjectClass *klass, void *data) ICSStateClass *icsc = ICS_BASE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - /* - * Use device_class_set_parent_realize() when ics-kvm inherits - * directly from ics-base and not from ics-simple anymore. - */ - dc->realize = ics_kvm_realize; - dc->reset = ics_kvm_reset; + device_class_set_parent_realize(dc, ics_kvm_realize, + &icsc->parent_realize); + device_class_set_parent_reset(dc, ics_kvm_reset, + &icsc->parent_reset); icsc->pre_save = ics_get_kvm_state; icsc->post_load = ics_set_kvm_state; @@ -373,7 +371,7 @@ static void ics_kvm_class_init(ObjectClass *klass, void *data) static const TypeInfo ics_kvm_info = { .name = TYPE_ICS_KVM, - .parent = TYPE_ICS_SIMPLE, + .parent = TYPE_ICS_BASE, .instance_size = sizeof(ICSState), .class_init = ics_kvm_class_init, }; diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b32b971a14..b2baec026f 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -137,7 +137,7 @@ static ICSState *spapr_ics_create(sPAPRMachineState *spapr, goto error; } - return ICS_SIMPLE(obj); + return ICS_BASE(obj); error: error_propagate(errp, local_err); From 56f68439213f4fc002c602cee6f4e6863609cb88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 26 Jun 2018 16:22:14 +0200 Subject: [PATCH 1952/2380] ppc/pnv: fix pnv_core_realize() error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d35aefa9ae15 ("ppc/pnv: introduce a new intc_create() operation to the chip model") changed the object link in the pnv_core_realize() routine but a return was forgotten in case of error, which can lead to more problems afterwards (segv) Signed-off-by: Cédric Le Goater Reviewed-by: Greg Kurz Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/ppc/pnv_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index a9f129fc2c..9750464bf4 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -150,6 +150,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) if (!chip) { error_propagate(errp, local_err); error_prepend(errp, "required link 'chip' not found: "); + return; } pc->threads = g_new(PowerPCCPU *, cc->nr_threads); From 0f3110fa67d3c3405202104f4833f1780e1a32bb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:09 -0700 Subject: [PATCH 1953/2380] target/ppc: Add do_unaligned_access hook This allows faults from MO_ALIGN to have the same effect as from gen_check_align. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/excp_helper.c | 18 +++++++++++++++++- target/ppc/internal.h | 5 +++++ target/ppc/translate_init.inc.c | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index c092fbead0..d6e97a90e0 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -22,7 +22,7 @@ #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" - +#include "internal.h" #include "helper_regs.h" //#define DEBUG_OP @@ -1198,3 +1198,19 @@ void helper_book3s_msgsnd(target_ulong rb) qemu_mutex_unlock_iothread(); } #endif + +void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + CPUPPCState *env = cs->env_ptr; + uint32_t insn; + + /* Restore state and reload the insn we executed, for filling in DSISR. */ + cpu_restore_state(cs, retaddr, true); + insn = cpu_ldl_code(env, env->nip); + + cs->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = insn & 0x03FF0000; + cpu_loop_exit(cs); +} diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 1f441c6483..a9bcadff42 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -252,4 +252,9 @@ static inline void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) void helper_compute_fprf_float16(CPUPPCState *env, float16 arg); void helper_compute_fprf_float32(CPUPPCState *env, float32 arg); void helper_compute_fprf_float128(CPUPPCState *env, float128 arg); + +/* Raise a data fault alignment exception for the specified virtual address */ +void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr); #endif /* PPC_INTERNAL_H */ diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index 76d6f3fd5e..7813b1b004 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -10457,6 +10457,7 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) cc->set_pc = ppc_cpu_set_pc; cc->gdb_read_register = ppc_cpu_gdb_read_register; cc->gdb_write_register = ppc_cpu_gdb_write_register; + cc->do_unaligned_access = ppc_cpu_do_unaligned_access; #ifdef CONFIG_USER_ONLY cc->handle_mmu_fault = ppc_cpu_handle_mmu_fault; #else From 94bf2658676be00b6f2b4db5d1788122217665b0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:10 -0700 Subject: [PATCH 1954/2380] target/ppc: Use atomic load for LQ and LQARX Section 1.4 of the Power ISA v3.0B states that both of these instructions are single-copy atomic. As we cannot (yet) issue 128-bit loads within TCG, use the generic helpers provided. Since TCG cannot (yet) return a 128-bit value, add a slot within CPUPPCState for returning the high half of a 128-bit return value. This solution is preferred to the helper assigning to architectural registers directly, as it avoids clobbering all TCG live values. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/cpu.h | 3 ++ target/ppc/helper.h | 5 +++ target/ppc/mem_helper.c | 20 ++++++++- target/ppc/translate.c | 93 ++++++++++++++++++++++++++++++----------- 4 files changed, 95 insertions(+), 26 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index c7f3fb6b73..973cf44cda 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1015,6 +1015,9 @@ struct CPUPPCState { /* Next instruction pointer */ target_ulong nip; + /* High part of 128-bit helper return. */ + uint64_t retxh; + int access_type; /* when a memory exception occurs, the access type is stored here */ diff --git a/target/ppc/helper.h b/target/ppc/helper.h index d751f0e219..3f451a5d7e 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -799,3 +799,8 @@ DEF_HELPER_4(dscliq, void, env, fprp, fprp, i32) DEF_HELPER_1(tbegin, void, env) DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) + +#if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128) +DEF_HELPER_FLAGS_3(lq_le_parallel, TCG_CALL_NO_WG, i64, env, tl, i32) +DEF_HELPER_FLAGS_3(lq_be_parallel, TCG_CALL_NO_WG, i64, env, tl, i32) +#endif diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index a34e604db3..44a8f3445a 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -21,9 +21,9 @@ #include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" - #include "helper_regs.h" #include "exec/cpu_ldst.h" +#include "tcg.h" #include "internal.h" //#define DEBUG_OP @@ -215,6 +215,24 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, return i; } +#if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128) +uint64_t helper_lq_le_parallel(CPUPPCState *env, target_ulong addr, + uint32_t opidx) +{ + Int128 ret = helper_atomic_ldo_le_mmu(env, addr, opidx, GETPC()); + env->retxh = int128_gethi(ret); + return int128_getlo(ret); +} + +uint64_t helper_lq_be_parallel(CPUPPCState *env, target_ulong addr, + uint32_t opidx) +{ + Int128 ret = helper_atomic_ldo_be_mmu(env, addr, opidx, GETPC()); + env->retxh = int128_gethi(ret); + return int128_getlo(ret); +} +#endif + /*****************************************************************************/ /* Altivec extension helpers */ #if defined(HOST_WORDS_BIGENDIAN) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 3a215a1dc6..0923cc24e3 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2607,7 +2607,7 @@ static void gen_ld(DisasContext *ctx) static void gen_lq(DisasContext *ctx) { int ra, rd; - TCGv EA; + TCGv EA, hi, lo; /* lq is a legal user mode instruction starting in ISA 2.07 */ bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; @@ -2633,16 +2633,35 @@ static void gen_lq(DisasContext *ctx) EA = tcg_temp_new(); gen_addr_imm_index(ctx, EA, 0x0F); - /* We only need to swap high and low halves. gen_qemu_ld64_i64 does - necessary 64-bit byteswap already. */ - if (unlikely(ctx->le_mode)) { - gen_qemu_ld64_i64(ctx, cpu_gpr[rd + 1], EA); + /* Note that the low part is always in RD+1, even in LE mode. */ + lo = cpu_gpr[rd + 1]; + hi = cpu_gpr[rd]; + + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { +#ifdef CONFIG_ATOMIC128 + TCGv_i32 oi = tcg_temp_new_i32(); + if (ctx->le_mode) { + tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx)); + gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); + } else { + tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx)); + gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); + } + tcg_temp_free_i32(oi); + tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh)); +#else + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; +#endif + } else if (ctx->le_mode) { + tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ); gen_addr_add(ctx, EA, EA, 8); - gen_qemu_ld64_i64(ctx, cpu_gpr[rd], EA); + tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_LEQ); } else { - gen_qemu_ld64_i64(ctx, cpu_gpr[rd], EA); + tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_BEQ); gen_addr_add(ctx, EA, EA, 8); - gen_qemu_ld64_i64(ctx, cpu_gpr[rd + 1], EA); + tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_BEQ); } tcg_temp_free(EA); } @@ -3236,9 +3255,8 @@ STCX(stdcx_, DEF_MEMOP(MO_Q)) /* lqarx */ static void gen_lqarx(DisasContext *ctx) { - TCGv EA; int rd = rD(ctx->opcode); - TCGv gpr1, gpr2; + TCGv EA, hi, lo; if (unlikely((rd & 1) || (rd == rA(ctx->opcode)) || (rd == rB(ctx->opcode)))) { @@ -3247,24 +3265,49 @@ static void gen_lqarx(DisasContext *ctx) } gen_set_access_type(ctx, ACCESS_RES); - EA = tcg_temp_local_new(); + EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); - gen_check_align(ctx, EA, 15); - if (unlikely(ctx->le_mode)) { - gpr1 = cpu_gpr[rd+1]; - gpr2 = cpu_gpr[rd]; - } else { - gpr1 = cpu_gpr[rd]; - gpr2 = cpu_gpr[rd+1]; - } - tcg_gen_qemu_ld_i64(gpr1, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); - tcg_gen_mov_tl(cpu_reserve, EA); - gen_addr_add(ctx, EA, EA, 8); - tcg_gen_qemu_ld_i64(gpr2, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); - tcg_gen_st_tl(gpr1, cpu_env, offsetof(CPUPPCState, reserve_val)); - tcg_gen_st_tl(gpr2, cpu_env, offsetof(CPUPPCState, reserve_val2)); + /* Note that the low part is always in RD+1, even in LE mode. */ + lo = cpu_gpr[rd + 1]; + hi = cpu_gpr[rd]; + + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { +#ifdef CONFIG_ATOMIC128 + TCGv_i32 oi = tcg_temp_new_i32(); + if (ctx->le_mode) { + tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ | MO_ALIGN_16, + ctx->mem_idx)); + gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); + } else { + tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ | MO_ALIGN_16, + ctx->mem_idx)); + gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); + } + tcg_temp_free_i32(oi); + tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh)); +#else + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + tcg_temp_free(EA); + return; +#endif + } else if (ctx->le_mode) { + tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ | MO_ALIGN_16); + tcg_gen_mov_tl(cpu_reserve, EA); + gen_addr_add(ctx, EA, EA, 8); + tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_LEQ); + } else { + tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_BEQ | MO_ALIGN_16); + tcg_gen_mov_tl(cpu_reserve, EA); + gen_addr_add(ctx, EA, EA, 8); + tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_BEQ); + } tcg_temp_free(EA); + + tcg_gen_st_tl(hi, cpu_env, offsetof(CPUPPCState, reserve_val)); + tcg_gen_st_tl(lo, cpu_env, offsetof(CPUPPCState, reserve_val2)); } /* stqcx. */ From f89ced5f556a06986d362dd41a99dfc4dc960fc1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:11 -0700 Subject: [PATCH 1955/2380] target/ppc: Use atomic store for STQ Section 1.4 of the Power ISA v3.0B states that this insn is single-copy atomic. As we cannot (yet) issue 128-bit stores within TCG, use the generic helpers provided. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/helper.h | 4 ++++ target/ppc/mem_helper.c | 14 ++++++++++++++ target/ppc/translate.c | 35 +++++++++++++++++++++++++++-------- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 3f451a5d7e..cbc1228570 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -803,4 +803,8 @@ DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) #if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128) DEF_HELPER_FLAGS_3(lq_le_parallel, TCG_CALL_NO_WG, i64, env, tl, i32) DEF_HELPER_FLAGS_3(lq_be_parallel, TCG_CALL_NO_WG, i64, env, tl, i32) +DEF_HELPER_FLAGS_5(stq_le_parallel, TCG_CALL_NO_WG, + void, env, tl, i64, i64, i32) +DEF_HELPER_FLAGS_5(stq_be_parallel, TCG_CALL_NO_WG, + void, env, tl, i64, i64, i32) #endif diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 44a8f3445a..57e301edc3 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -231,6 +231,20 @@ uint64_t helper_lq_be_parallel(CPUPPCState *env, target_ulong addr, env->retxh = int128_gethi(ret); return int128_getlo(ret); } + +void helper_stq_le_parallel(CPUPPCState *env, target_ulong addr, + uint64_t lo, uint64_t hi, uint32_t opidx) +{ + Int128 val = int128_make128(lo, hi); + helper_atomic_sto_le_mmu(env, addr, val, opidx, GETPC()); +} + +void helper_stq_be_parallel(CPUPPCState *env, target_ulong addr, + uint64_t lo, uint64_t hi, uint32_t opidx) +{ + Int128 val = int128_make128(lo, hi); + helper_atomic_sto_be_mmu(env, addr, val, opidx, GETPC()); +} #endif /*****************************************************************************/ diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 0923cc24e3..3d63a62269 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2760,6 +2760,7 @@ static void gen_std(DisasContext *ctx) if ((ctx->opcode & 0x3) == 0x2) { /* stq */ bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + TCGv hi, lo; if (!(ctx->insns_flags & PPC_64BX)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); @@ -2783,20 +2784,38 @@ static void gen_std(DisasContext *ctx) EA = tcg_temp_new(); gen_addr_imm_index(ctx, EA, 0x03); - /* We only need to swap high and low halves. gen_qemu_st64_i64 does - necessary 64-bit byteswap already. */ - if (unlikely(ctx->le_mode)) { - gen_qemu_st64_i64(ctx, cpu_gpr[rs + 1], EA); + /* Note that the low part is always in RS+1, even in LE mode. */ + lo = cpu_gpr[rs + 1]; + hi = cpu_gpr[rs]; + + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { +#ifdef CONFIG_ATOMIC128 + TCGv_i32 oi = tcg_temp_new_i32(); + if (ctx->le_mode) { + tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx)); + gen_helper_stq_le_parallel(cpu_env, EA, lo, hi, oi); + } else { + tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx)); + gen_helper_stq_be_parallel(cpu_env, EA, lo, hi, oi); + } + tcg_temp_free_i32(oi); +#else + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; +#endif + } else if (ctx->le_mode) { + tcg_gen_qemu_st_i64(lo, EA, ctx->mem_idx, MO_LEQ); gen_addr_add(ctx, EA, EA, 8); - gen_qemu_st64_i64(ctx, cpu_gpr[rs], EA); + tcg_gen_qemu_st_i64(hi, EA, ctx->mem_idx, MO_LEQ); } else { - gen_qemu_st64_i64(ctx, cpu_gpr[rs], EA); + tcg_gen_qemu_st_i64(hi, EA, ctx->mem_idx, MO_BEQ); gen_addr_add(ctx, EA, EA, 8); - gen_qemu_st64_i64(ctx, cpu_gpr[rs + 1], EA); + tcg_gen_qemu_st_i64(lo, EA, ctx->mem_idx, MO_BEQ); } tcg_temp_free(EA); } else { - /* std / stdu*/ + /* std / stdu */ if (Rc(ctx->opcode)) { if (unlikely(rA(ctx->opcode) == 0)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); From 4a9b3c5dd30cfd548d447521d4ef1fdba6f0fcf2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:12 -0700 Subject: [PATCH 1956/2380] target/ppc: Use atomic cmpxchg for STQCX When running in a parallel context, we must use a helper in order to perform the 128-bit atomic operation. When running in a serial context, do the compare before the store. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/helper.h | 2 + target/ppc/mem_helper.c | 38 +++++++++++++++++ target/ppc/translate.c | 95 ++++++++++++++++++++++++++--------------- 3 files changed, 101 insertions(+), 34 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index cbc1228570..5706c2497f 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -807,4 +807,6 @@ DEF_HELPER_FLAGS_5(stq_le_parallel, TCG_CALL_NO_WG, void, env, tl, i64, i64, i32) DEF_HELPER_FLAGS_5(stq_be_parallel, TCG_CALL_NO_WG, void, env, tl, i64, i64, i32) +DEF_HELPER_5(stqcx_le_parallel, i32, env, tl, i64, i64, i32) +DEF_HELPER_5(stqcx_be_parallel, i32, env, tl, i64, i64, i32) #endif diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 57e301edc3..8f0d86d104 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -245,6 +245,44 @@ void helper_stq_be_parallel(CPUPPCState *env, target_ulong addr, Int128 val = int128_make128(lo, hi); helper_atomic_sto_be_mmu(env, addr, val, opidx, GETPC()); } + +uint32_t helper_stqcx_le_parallel(CPUPPCState *env, target_ulong addr, + uint64_t new_lo, uint64_t new_hi, + uint32_t opidx) +{ + bool success = false; + + if (likely(addr == env->reserve_addr)) { + Int128 oldv, cmpv, newv; + + cmpv = int128_make128(env->reserve_val2, env->reserve_val); + newv = int128_make128(new_lo, new_hi); + oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, + opidx, GETPC()); + success = int128_eq(oldv, cmpv); + } + env->reserve_addr = -1; + return env->so + success * CRF_EQ_BIT; +} + +uint32_t helper_stqcx_be_parallel(CPUPPCState *env, target_ulong addr, + uint64_t new_lo, uint64_t new_hi, + uint32_t opidx) +{ + bool success = false; + + if (likely(addr == env->reserve_addr)) { + Int128 oldv, cmpv, newv; + + cmpv = int128_make128(env->reserve_val2, env->reserve_val); + newv = int128_make128(new_lo, new_hi); + oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, + opidx, GETPC()); + success = int128_eq(oldv, cmpv); + } + env->reserve_addr = -1; + return env->so + success * CRF_EQ_BIT; +} #endif /*****************************************************************************/ diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 3d63a62269..c7b9d226eb 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3332,50 +3332,77 @@ static void gen_lqarx(DisasContext *ctx) /* stqcx. */ static void gen_stqcx_(DisasContext *ctx) { - TCGv EA; - int reg = rS(ctx->opcode); - int len = 16; -#if !defined(CONFIG_USER_ONLY) - TCGLabel *l1; - TCGv gpr1, gpr2; -#endif + int rs = rS(ctx->opcode); + TCGv EA, hi, lo; - if (unlikely((rD(ctx->opcode) & 1))) { + if (unlikely(rs & 1)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } + gen_set_access_type(ctx, ACCESS_RES); - EA = tcg_temp_local_new(); + EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); - if (len > 1) { - gen_check_align(ctx, EA, (len) - 1); - } -#if defined(CONFIG_USER_ONLY) - gen_conditional_store(ctx, EA, reg, 16); + /* Note that the low part is always in RS+1, even in LE mode. */ + lo = cpu_gpr[rs + 1]; + hi = cpu_gpr[rs]; + + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_Q) | MO_ALIGN_16); +#ifdef CONFIG_ATOMIC128 + if (ctx->le_mode) { + gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi); + } else { + gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi); + } #else - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - l1 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); - - if (unlikely(ctx->le_mode)) { - gpr1 = cpu_gpr[reg + 1]; - gpr2 = cpu_gpr[reg]; - } else { - gpr1 = cpu_gpr[reg]; - gpr2 = cpu_gpr[reg + 1]; - } - tcg_gen_qemu_st_tl(gpr1, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); - gen_addr_add(ctx, EA, EA, 8); - tcg_gen_qemu_st_tl(gpr2, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); - - gen_set_label(l1); - tcg_gen_movi_tl(cpu_reserve, -1); + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; #endif - tcg_temp_free(EA); -} + tcg_temp_free(EA); + tcg_temp_free_i32(oi); + } else { + TCGLabel *lab_fail = gen_new_label(); + TCGLabel *lab_over = gen_new_label(); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lab_fail); + tcg_temp_free(EA); + + gen_qemu_ld64_i64(ctx, t0, cpu_reserve); + tcg_gen_ld_i64(t1, cpu_env, (ctx->le_mode + ? offsetof(CPUPPCState, reserve_val2) + : offsetof(CPUPPCState, reserve_val))); + tcg_gen_brcond_i64(TCG_COND_NE, t0, t1, lab_fail); + + tcg_gen_addi_i64(t0, cpu_reserve, 8); + gen_qemu_ld64_i64(ctx, t0, t0); + tcg_gen_ld_i64(t1, cpu_env, (ctx->le_mode + ? offsetof(CPUPPCState, reserve_val) + : offsetof(CPUPPCState, reserve_val2))); + tcg_gen_brcond_i64(TCG_COND_NE, t0, t1, lab_fail); + + /* Success */ + gen_qemu_st64_i64(ctx, ctx->le_mode ? lo : hi, cpu_reserve); + tcg_gen_addi_i64(t0, cpu_reserve, 8); + gen_qemu_st64_i64(ctx, ctx->le_mode ? hi : lo, t0); + + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); + tcg_gen_br(lab_over); + + gen_set_label(lab_fail); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + + gen_set_label(lab_over); + tcg_gen_movi_tl(cpu_reserve, -1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} #endif /* defined(TARGET_PPC64) */ /* sync */ From 14db18997eb29b79f6c538c1a3cd27df259f77a6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:13 -0700 Subject: [PATCH 1957/2380] target/ppc: Remove POWERPC_EXCP_STCX Always use the gen_conditional_store implementation that uses atomic_cmpxchg. Make sure and clear reserve_addr across most interrupts crossing the cpu_loop. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- linux-user/ppc/cpu_loop.c | 123 +++++++------------------------------- target/ppc/cpu.h | 5 -- target/ppc/translate.c | 14 ----- 3 files changed, 23 insertions(+), 119 deletions(-) diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 2fb516cb00..133a87f349 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -65,99 +65,23 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) return -1; } -static int do_store_exclusive(CPUPPCState *env) -{ - target_ulong addr; - target_ulong page_addr; - target_ulong val, val2 __attribute__((unused)) = 0; - int flags; - int segv = 0; - - addr = env->reserve_ea; - page_addr = addr & TARGET_PAGE_MASK; - start_exclusive(); - mmap_lock(); - flags = page_get_flags(page_addr); - if ((flags & PAGE_READ) == 0) { - segv = 1; - } else { - int reg = env->reserve_info & 0x1f; - int size = env->reserve_info >> 5; - int stored = 0; - - if (addr == env->reserve_addr) { - switch (size) { - case 1: segv = get_user_u8(val, addr); break; - case 2: segv = get_user_u16(val, addr); break; - case 4: segv = get_user_u32(val, addr); break; -#if defined(TARGET_PPC64) - case 8: segv = get_user_u64(val, addr); break; - case 16: { - segv = get_user_u64(val, addr); - if (!segv) { - segv = get_user_u64(val2, addr + 8); - } - break; - } -#endif - default: abort(); - } - if (!segv && val == env->reserve_val) { - val = env->gpr[reg]; - switch (size) { - case 1: segv = put_user_u8(val, addr); break; - case 2: segv = put_user_u16(val, addr); break; - case 4: segv = put_user_u32(val, addr); break; -#if defined(TARGET_PPC64) - case 8: segv = put_user_u64(val, addr); break; - case 16: { - if (val2 == env->reserve_val2) { - if (msr_le) { - val2 = val; - val = env->gpr[reg+1]; - } else { - val2 = env->gpr[reg+1]; - } - segv = put_user_u64(val, addr); - if (!segv) { - segv = put_user_u64(val2, addr + 8); - } - } - break; - } -#endif - default: abort(); - } - if (!segv) { - stored = 1; - } - } - } - env->crf[0] = (stored << 1) | xer_so; - env->reserve_addr = (target_ulong)-1; - } - if (!segv) { - env->nip += 4; - } - mmap_unlock(); - end_exclusive(); - return segv; -} - void cpu_loop(CPUPPCState *env) { CPUState *cs = CPU(ppc_env_get_cpu(env)); target_siginfo_t info; - int trapnr; + int trapnr, sig; target_ulong ret; for(;;) { + bool arch_interrupt; + cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); process_queued_cpu_work(cs); - switch(trapnr) { + arch_interrupt = true; + switch (trapnr) { case POWERPC_EXCP_NONE: /* Just go on */ break; @@ -524,26 +448,15 @@ void cpu_loop(CPUPPCState *env) } env->gpr[3] = ret; break; - case POWERPC_EXCP_STCX: - if (do_store_exclusive(env)) { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } else { + arch_interrupt = false; } break; case EXCP_INTERRUPT: @@ -551,12 +464,22 @@ void cpu_loop(CPUPPCState *env) break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); + arch_interrupt = false; break; default: cpu_abort(cs, "Unknown exception 0x%x. Aborting\n", trapnr); break; } process_pending_signals(env); + + /* Most of the traps imply a transition through kernel mode, + * which implies an REI instruction has been executed. Which + * means that RX and LOCK_ADDR should be cleared. But there + * are a few exceptions for traps internal to QEMU. + */ + if (arch_interrupt) { + env->reserve_addr = -1; + } } } diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 973cf44cda..4edcf62cf7 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -196,7 +196,6 @@ enum { /* QEMU exceptions: special cases we want to stop translation */ POWERPC_EXCP_SYNC = 0x202, /* context synchronizing instruction */ POWERPC_EXCP_SYSCALL_USER = 0x203, /* System call in user mode only */ - POWERPC_EXCP_STCX = 0x204 /* Conditional stores in user mode */ }; /* Exceptions error codes */ @@ -994,10 +993,6 @@ struct CPUPPCState { /* Reservation value */ target_ulong reserve_val; target_ulong reserve_val2; - /* Reservation store address */ - target_ulong reserve_ea; - /* Reserved store source register and size */ - target_ulong reserve_info; /* Those ones are used in supervisor mode only */ /* machine state register */ diff --git a/target/ppc/translate.c b/target/ppc/translate.c index c7b9d226eb..03e8c5df03 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3201,19 +3201,6 @@ ST_ATOMIC(stwat, DEF_MEMOP(MO_UL), i32, trunc_tl_i32) ST_ATOMIC(stdat, DEF_MEMOP(MO_Q), i64, mov_i64) #endif -#if defined(CONFIG_USER_ONLY) -static void gen_conditional_store(DisasContext *ctx, TCGv EA, - int reg, int memop) -{ - TCGv t0 = tcg_temp_new(); - - tcg_gen_st_tl(EA, cpu_env, offsetof(CPUPPCState, reserve_ea)); - tcg_gen_movi_tl(t0, (MEMOP_GET_SIZE(memop) << 5) | reg); - tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, reserve_info)); - tcg_temp_free(t0); - gen_exception_err(ctx, POWERPC_EXCP_STCX, 0); -} -#else static void gen_conditional_store(DisasContext *ctx, TCGv EA, int reg, int memop) { @@ -3244,7 +3231,6 @@ static void gen_conditional_store(DisasContext *ctx, TCGv EA, gen_set_label(l2); tcg_gen_movi_tl(cpu_reserve, -1); } -#endif #define STCX(name, memop) \ static void gen_##name(DisasContext *ctx) \ From d8b86898275827089c3ea10d1c83fcbab50abb3b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:14 -0700 Subject: [PATCH 1958/2380] target/ppc: Tidy gen_conditional_store Leave only the minimal amount of code within the STCX macro, moving the rest of the code into gen_conditional_store. Remove the explicit call to gen_check_align; the matching LDAX will have already checked alignment, and we verify the same address. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 03e8c5df03..e751072404 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3201,14 +3201,17 @@ ST_ATOMIC(stwat, DEF_MEMOP(MO_UL), i32, trunc_tl_i32) ST_ATOMIC(stdat, DEF_MEMOP(MO_Q), i64, mov_i64) #endif -static void gen_conditional_store(DisasContext *ctx, TCGv EA, - int reg, int memop) +static void gen_conditional_store(DisasContext *ctx, TCGMemOp memop) { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); - TCGv t0; + TCGv t0 = tcg_temp_new(); + int reg = rS(ctx->opcode); - tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1); + gen_set_access_type(ctx, ACCESS_RES); + gen_addr_reg_index(ctx, t0); + tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); + tcg_temp_free(t0); t0 = tcg_temp_new(); tcg_gen_atomic_cmpxchg_tl(t0, cpu_reserve, cpu_reserve_val, @@ -3232,19 +3235,10 @@ static void gen_conditional_store(DisasContext *ctx, TCGv EA, tcg_gen_movi_tl(cpu_reserve, -1); } -#define STCX(name, memop) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv t0; \ - int len = MEMOP_GET_SIZE(memop); \ - gen_set_access_type(ctx, ACCESS_RES); \ - t0 = tcg_temp_local_new(); \ - gen_addr_reg_index(ctx, t0); \ - if (len > 1) { \ - gen_check_align(ctx, t0, (len) - 1); \ - } \ - gen_conditional_store(ctx, t0, rS(ctx->opcode), memop); \ - tcg_temp_free(t0); \ +#define STCX(name, memop) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + gen_conditional_store(ctx, memop); \ } STCX(stbcx_, DEF_MEMOP(MO_UB)) From 2a4e6c1bffe11a982f6774fe01049debf07703fc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:15 -0700 Subject: [PATCH 1959/2380] target/ppc: Split out gen_load_locked Leave only the minimal amount of code within the LDAR macro, moving the rest of the code into gen_load_locked. Use MO_ALIGN and remove the explicit call to gen_check_align. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index e751072404..f48fcbeefb 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3070,23 +3070,24 @@ static void gen_isync(DisasContext *ctx) #define MEMOP_GET_SIZE(x) (1 << ((x) & MO_SIZE)) -#define LARX(name, memop) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv t0; \ - TCGv gpr = cpu_gpr[rD(ctx->opcode)]; \ - int len = MEMOP_GET_SIZE(memop); \ - gen_set_access_type(ctx, ACCESS_RES); \ - t0 = tcg_temp_local_new(); \ - gen_addr_reg_index(ctx, t0); \ - if ((len) > 1) { \ - gen_check_align(ctx, t0, (len)-1); \ - } \ - tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop); \ - tcg_gen_mov_tl(cpu_reserve, t0); \ - tcg_gen_mov_tl(cpu_reserve_val, gpr); \ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); \ - tcg_temp_free(t0); \ +static void gen_load_locked(DisasContext *ctx, TCGMemOp memop) +{ + TCGv gpr = cpu_gpr[rD(ctx->opcode)]; + TCGv t0 = tcg_temp_new(); + + gen_set_access_type(ctx, ACCESS_RES); + gen_addr_reg_index(ctx, t0); + tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop | MO_ALIGN); + tcg_gen_mov_tl(cpu_reserve, t0); + tcg_gen_mov_tl(cpu_reserve_val, gpr); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + tcg_temp_free(t0); +} + +#define LARX(name, memop) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + gen_load_locked(ctx, memop); \ } /* lwarx */ From 20ba8504a64cb4f008bc32ed9806c56ade036663 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:16 -0700 Subject: [PATCH 1960/2380] target/ppc: Split out gen_ld_atomic Move the guts of LD_ATOMIC to a function. Use foo_tl for the operations instead of foo_i32 or foo_i64 specifically. Use MO_ALIGN instead of an explicit call to gen_check_align. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate.c | 105 ++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index f48fcbeefb..361b178db8 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3095,61 +3095,60 @@ LARX(lbarx, DEF_MEMOP(MO_UB)) LARX(lharx, DEF_MEMOP(MO_UW)) LARX(lwarx, DEF_MEMOP(MO_UL)) -#define LD_ATOMIC(name, memop, tp, op, eop) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - int len = MEMOP_GET_SIZE(memop); \ - uint32_t gpr_FC = FC(ctx->opcode); \ - TCGv EA = tcg_temp_local_new(); \ - TCGv_##tp t0, t1; \ - \ - gen_addr_register(ctx, EA); \ - if (len > 1) { \ - gen_check_align(ctx, EA, len - 1); \ - } \ - t0 = tcg_temp_new_##tp(); \ - t1 = tcg_temp_new_##tp(); \ - tcg_gen_##op(t0, cpu_gpr[rD(ctx->opcode) + 1]); \ - \ - switch (gpr_FC) { \ - case 0: /* Fetch and add */ \ - tcg_gen_atomic_fetch_add_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 1: /* Fetch and xor */ \ - tcg_gen_atomic_fetch_xor_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 2: /* Fetch and or */ \ - tcg_gen_atomic_fetch_or_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 3: /* Fetch and 'and' */ \ - tcg_gen_atomic_fetch_and_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 8: /* Swap */ \ - tcg_gen_atomic_xchg_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 4: /* Fetch and max unsigned */ \ - case 5: /* Fetch and max signed */ \ - case 6: /* Fetch and min unsigned */ \ - case 7: /* Fetch and min signed */ \ - case 16: /* compare and swap not equal */ \ - case 24: /* Fetch and increment bounded */ \ - case 25: /* Fetch and increment equal */ \ - case 28: /* Fetch and decrement bounded */ \ - gen_invalid(ctx); \ - break; \ - default: \ - /* invoke data storage error handler */ \ - gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); \ - } \ - tcg_gen_##eop(cpu_gpr[rD(ctx->opcode)], t1); \ - tcg_temp_free_##tp(t0); \ - tcg_temp_free_##tp(t1); \ - tcg_temp_free(EA); \ +static void gen_ld_atomic(DisasContext *ctx, TCGMemOp memop) +{ + uint32_t gpr_FC = FC(ctx->opcode); + TCGv EA = tcg_temp_new(); + TCGv src, dst; + + gen_addr_register(ctx, EA); + dst = cpu_gpr[rD(ctx->opcode)]; + src = cpu_gpr[rD(ctx->opcode) + 1]; + + memop |= MO_ALIGN; + switch (gpr_FC) { + case 0: /* Fetch and add */ + tcg_gen_atomic_fetch_add_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 1: /* Fetch and xor */ + tcg_gen_atomic_fetch_xor_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 2: /* Fetch and or */ + tcg_gen_atomic_fetch_or_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 3: /* Fetch and 'and' */ + tcg_gen_atomic_fetch_and_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 8: /* Swap */ + tcg_gen_atomic_xchg_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 4: /* Fetch and max unsigned */ + case 5: /* Fetch and max signed */ + case 6: /* Fetch and min unsigned */ + case 7: /* Fetch and min signed */ + case 16: /* compare and swap not equal */ + case 24: /* Fetch and increment bounded */ + case 25: /* Fetch and increment equal */ + case 28: /* Fetch and decrement bounded */ + gen_invalid(ctx); + break; + default: + /* invoke data storage error handler */ + gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); + } + tcg_temp_free(EA); } -LD_ATOMIC(lwat, DEF_MEMOP(MO_UL), i32, trunc_tl_i32, extu_i32_tl) -#if defined(TARGET_PPC64) -LD_ATOMIC(ldat, DEF_MEMOP(MO_Q), i64, mov_i64, mov_i64) +static void gen_lwat(DisasContext *ctx) +{ + gen_ld_atomic(ctx, DEF_MEMOP(MO_UL)); +} + +#ifdef TARGET_PPC64 +static void gen_ldat(DisasContext *ctx) +{ + gen_ld_atomic(ctx, DEF_MEMOP(MO_Q)); +} #endif #define ST_ATOMIC(name, memop, tp, op) \ From 9deb041cbda9eae6365c88ca42353b4a644aeb09 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:17 -0700 Subject: [PATCH 1961/2380] target/ppc: Split out gen_st_atomic Move the guts of ST_ATOMIC to a function. Use foo_tl for the operations instead of foo_i32 or foo_i64 specifically. Use MO_ALIGN instead of an explicit call to gen_check_align. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate.c | 93 +++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 361b178db8..53ca8f0114 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3151,54 +3151,55 @@ static void gen_ldat(DisasContext *ctx) } #endif -#define ST_ATOMIC(name, memop, tp, op) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - int len = MEMOP_GET_SIZE(memop); \ - uint32_t gpr_FC = FC(ctx->opcode); \ - TCGv EA = tcg_temp_local_new(); \ - TCGv_##tp t0, t1; \ - \ - gen_addr_register(ctx, EA); \ - if (len > 1) { \ - gen_check_align(ctx, EA, len - 1); \ - } \ - t0 = tcg_temp_new_##tp(); \ - t1 = tcg_temp_new_##tp(); \ - tcg_gen_##op(t0, cpu_gpr[rD(ctx->opcode) + 1]); \ - \ - switch (gpr_FC) { \ - case 0: /* add and Store */ \ - tcg_gen_atomic_add_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 1: /* xor and Store */ \ - tcg_gen_atomic_xor_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 2: /* Or and Store */ \ - tcg_gen_atomic_or_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 3: /* 'and' and Store */ \ - tcg_gen_atomic_and_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 4: /* Store max unsigned */ \ - case 5: /* Store max signed */ \ - case 6: /* Store min unsigned */ \ - case 7: /* Store min signed */ \ - case 24: /* Store twin */ \ - gen_invalid(ctx); \ - break; \ - default: \ - /* invoke data storage error handler */ \ - gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); \ - } \ - tcg_temp_free_##tp(t0); \ - tcg_temp_free_##tp(t1); \ - tcg_temp_free(EA); \ +static void gen_st_atomic(DisasContext *ctx, TCGMemOp memop) +{ + uint32_t gpr_FC = FC(ctx->opcode); + TCGv EA = tcg_temp_new(); + TCGv src, discard; + + gen_addr_register(ctx, EA); + src = cpu_gpr[rD(ctx->opcode)]; + discard = tcg_temp_new(); + + memop |= MO_ALIGN; + switch (gpr_FC) { + case 0: /* add and Store */ + tcg_gen_atomic_add_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 1: /* xor and Store */ + tcg_gen_atomic_xor_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 2: /* Or and Store */ + tcg_gen_atomic_or_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 3: /* 'and' and Store */ + tcg_gen_atomic_and_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 4: /* Store max unsigned */ + case 5: /* Store max signed */ + case 6: /* Store min unsigned */ + case 7: /* Store min signed */ + case 24: /* Store twin */ + gen_invalid(ctx); + break; + default: + /* invoke data storage error handler */ + gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); + } + tcg_temp_free(discard); + tcg_temp_free(EA); } -ST_ATOMIC(stwat, DEF_MEMOP(MO_UL), i32, trunc_tl_i32) -#if defined(TARGET_PPC64) -ST_ATOMIC(stdat, DEF_MEMOP(MO_Q), i64, mov_i64) +static void gen_stwat(DisasContext *ctx) +{ + gen_st_atomic(ctx, DEF_MEMOP(MO_UL)); +} + +#ifdef TARGET_PPC64 +static void gen_stdat(DisasContext *ctx) +{ + gen_st_atomic(ctx, DEF_MEMOP(MO_Q)); +} #endif static void gen_conditional_store(DisasContext *ctx, TCGMemOp memop) From c674a9831e8bf58928508e2db604e092c66638a4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:18 -0700 Subject: [PATCH 1962/2380] target/ppc: Use MO_ALIGN for EXIWX and ECOWX This avoids the need for gen_check_align entirely. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 53ca8f0114..c2a28be6d7 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2388,23 +2388,6 @@ static inline void gen_addr_add(DisasContext *ctx, TCGv ret, TCGv arg1, } } -static inline void gen_check_align(DisasContext *ctx, TCGv EA, int mask) -{ - TCGLabel *l1 = gen_new_label(); - TCGv t0 = tcg_temp_new(); - TCGv_i32 t1, t2; - tcg_gen_andi_tl(t0, EA, mask); - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); - t1 = tcg_const_i32(POWERPC_EXCP_ALIGN); - t2 = tcg_const_i32(ctx->opcode & 0x03FF0000); - gen_update_nip(ctx, ctx->base.pc_next - 4); - gen_helper_raise_exception_err(cpu_env, t1, t2); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); - gen_set_label(l1); - tcg_temp_free(t0); -} - static inline void gen_align_no_le(DisasContext *ctx) { gen_exception_err(ctx, POWERPC_EXCP_ALIGN, @@ -4706,8 +4689,8 @@ static void gen_eciwx(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_EXT); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x03); - gen_qemu_ld32u(ctx, cpu_gpr[rD(ctx->opcode)], t0); + tcg_gen_qemu_ld_tl(cpu_gpr[rD(ctx->opcode)], t0, ctx->mem_idx, + DEF_MEMOP(MO_UL | MO_ALIGN)); tcg_temp_free(t0); } @@ -4719,8 +4702,8 @@ static void gen_ecowx(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_EXT); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x03); - gen_qemu_st32(ctx, cpu_gpr[rD(ctx->opcode)], t0); + tcg_gen_qemu_st_tl(cpu_gpr[rD(ctx->opcode)], t0, ctx->mem_idx, + DEF_MEMOP(MO_UL | MO_ALIGN)); tcg_temp_free(t0); } From b8ce0f86782b1317f363cc90f4e1aaeb116a7cc2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:19 -0700 Subject: [PATCH 1963/2380] target/ppc: Use atomic min/max helpers These operations were previously unimplemented for ppc. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index c2a28be6d7..79285b6698 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3102,13 +3102,21 @@ static void gen_ld_atomic(DisasContext *ctx, TCGMemOp memop) case 3: /* Fetch and 'and' */ tcg_gen_atomic_fetch_and_tl(dst, EA, src, ctx->mem_idx, memop); break; + case 4: /* Fetch and max unsigned */ + tcg_gen_atomic_fetch_umax_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 5: /* Fetch and max signed */ + tcg_gen_atomic_fetch_smax_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 6: /* Fetch and min unsigned */ + tcg_gen_atomic_fetch_umin_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 7: /* Fetch and min signed */ + tcg_gen_atomic_fetch_smin_tl(dst, EA, src, ctx->mem_idx, memop); + break; case 8: /* Swap */ tcg_gen_atomic_xchg_tl(dst, EA, src, ctx->mem_idx, memop); break; - case 4: /* Fetch and max unsigned */ - case 5: /* Fetch and max signed */ - case 6: /* Fetch and min unsigned */ - case 7: /* Fetch and min signed */ case 16: /* compare and swap not equal */ case 24: /* Fetch and increment bounded */ case 25: /* Fetch and increment equal */ @@ -3159,9 +3167,17 @@ static void gen_st_atomic(DisasContext *ctx, TCGMemOp memop) tcg_gen_atomic_and_fetch_tl(discard, EA, src, ctx->mem_idx, memop); break; case 4: /* Store max unsigned */ + tcg_gen_atomic_umax_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; case 5: /* Store max signed */ + tcg_gen_atomic_smax_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; case 6: /* Store min unsigned */ + tcg_gen_atomic_umin_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; case 7: /* Store min signed */ + tcg_gen_atomic_smin_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; case 24: /* Store twin */ gen_invalid(ctx); break; From 20923c1d02b68dfd848f014a77747c0e4817682a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:20 -0700 Subject: [PATCH 1964/2380] target/ppc: Implement the rest of gen_ld_atomic These cases were stubbed out. For now, implement them only within a serial context, forcing parallel execution to synchronize. It would be possible to implement these with cmpxchg loops, if we care. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate.c | 89 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 79285b6698..597a37d3ec 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3078,16 +3078,45 @@ LARX(lbarx, DEF_MEMOP(MO_UB)) LARX(lharx, DEF_MEMOP(MO_UW)) LARX(lwarx, DEF_MEMOP(MO_UL)) +static void gen_fetch_inc_conditional(DisasContext *ctx, TCGMemOp memop, + TCGv EA, TCGCond cond, int addend) +{ + TCGv t = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv u = tcg_temp_new(); + + tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop); + tcg_gen_addi_tl(t2, EA, MEMOP_GET_SIZE(memop)); + tcg_gen_qemu_ld_tl(t2, t2, ctx->mem_idx, memop); + tcg_gen_addi_tl(u, t, addend); + + /* E.g. for fetch and increment bounded... */ + /* mem(EA,s) = (t != t2 ? u = t + 1 : t) */ + tcg_gen_movcond_tl(cond, u, t, t2, u, t); + tcg_gen_qemu_st_tl(u, EA, ctx->mem_idx, memop); + + /* RT = (t != t2 ? t : u = 1<<(s*8-1)) */ + tcg_gen_movi_tl(u, 1 << (MEMOP_GET_SIZE(memop) * 8 - 1)); + tcg_gen_movcond_tl(cond, cpu_gpr[rD(ctx->opcode)], t, t2, t, u); + + tcg_temp_free(t); + tcg_temp_free(t2); + tcg_temp_free(u); +} + static void gen_ld_atomic(DisasContext *ctx, TCGMemOp memop) { uint32_t gpr_FC = FC(ctx->opcode); TCGv EA = tcg_temp_new(); + int rt = rD(ctx->opcode); + bool need_serial; TCGv src, dst; gen_addr_register(ctx, EA); - dst = cpu_gpr[rD(ctx->opcode)]; - src = cpu_gpr[rD(ctx->opcode) + 1]; + dst = cpu_gpr[rt]; + src = cpu_gpr[(rt + 1) & 31]; + need_serial = false; memop |= MO_ALIGN; switch (gpr_FC) { case 0: /* Fetch and add */ @@ -3117,17 +3146,63 @@ static void gen_ld_atomic(DisasContext *ctx, TCGMemOp memop) case 8: /* Swap */ tcg_gen_atomic_xchg_tl(dst, EA, src, ctx->mem_idx, memop); break; - case 16: /* compare and swap not equal */ - case 24: /* Fetch and increment bounded */ - case 25: /* Fetch and increment equal */ - case 28: /* Fetch and decrement bounded */ - gen_invalid(ctx); + + case 16: /* Compare and swap not equal */ + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + need_serial = true; + } else { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + tcg_gen_qemu_ld_tl(t0, EA, ctx->mem_idx, memop); + if ((memop & MO_SIZE) == MO_64 || TARGET_LONG_BITS == 32) { + tcg_gen_mov_tl(t1, src); + } else { + tcg_gen_ext32u_tl(t1, src); + } + tcg_gen_movcond_tl(TCG_COND_NE, t1, t0, t1, + cpu_gpr[(rt + 2) & 31], t0); + tcg_gen_qemu_st_tl(t1, EA, ctx->mem_idx, memop); + tcg_gen_mov_tl(dst, t0); + + tcg_temp_free(t0); + tcg_temp_free(t1); + } break; + + case 24: /* Fetch and increment bounded */ + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + need_serial = true; + } else { + gen_fetch_inc_conditional(ctx, memop, EA, TCG_COND_NE, 1); + } + break; + case 25: /* Fetch and increment equal */ + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + need_serial = true; + } else { + gen_fetch_inc_conditional(ctx, memop, EA, TCG_COND_EQ, 1); + } + break; + case 28: /* Fetch and decrement bounded */ + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + need_serial = true; + } else { + gen_fetch_inc_conditional(ctx, memop, EA, TCG_COND_NE, -1); + } + break; + default: /* invoke data storage error handler */ gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); } tcg_temp_free(EA); + + if (need_serial) { + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + } } static void gen_lwat(DisasContext *ctx) From 7fbc2b20d2e0ca1898bfc2bd871fb674ec1039fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Jun 2018 09:19:21 -0700 Subject: [PATCH 1965/2380] target/ppc: Implement the rest of gen_st_atomic The store twin case was stubbed out. For now, implement it only within a serial context, forcing parallel execution to synchronize. It would be possible to implement with a cmpxchg loop, if we care, but the loose alignment requirements (simply no crossing 32-byte boundary) might send us back to the serial context anyway. Signed-off-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/translate.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 597a37d3ec..e120f2ed0b 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3254,7 +3254,31 @@ static void gen_st_atomic(DisasContext *ctx, TCGMemOp memop) tcg_gen_atomic_smin_fetch_tl(discard, EA, src, ctx->mem_idx, memop); break; case 24: /* Store twin */ - gen_invalid(ctx); + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + } else { + TCGv t = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv s = tcg_temp_new(); + TCGv s2 = tcg_temp_new(); + TCGv ea_plus_s = tcg_temp_new(); + + tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop); + tcg_gen_addi_tl(ea_plus_s, EA, MEMOP_GET_SIZE(memop)); + tcg_gen_qemu_ld_tl(t2, ea_plus_s, ctx->mem_idx, memop); + tcg_gen_movcond_tl(TCG_COND_EQ, s, t, t2, src, t); + tcg_gen_movcond_tl(TCG_COND_EQ, s2, t, t2, src, t2); + tcg_gen_qemu_st_tl(s, EA, ctx->mem_idx, memop); + tcg_gen_qemu_st_tl(s2, ea_plus_s, ctx->mem_idx, memop); + + tcg_temp_free(ea_plus_s); + tcg_temp_free(s2); + tcg_temp_free(s); + tcg_temp_free(t2); + tcg_temp_free(t); + } break; default: /* invoke data storage error handler */ From 9e430ca3da7b9bef4c89f8c72ebc703900f7c6b5 Mon Sep 17 00:00:00 2001 From: John Arbuckle Date: Sun, 24 Jun 2018 19:12:48 -0400 Subject: [PATCH 1966/2380] fpu_helper.c: fix setting FPSCR[FI] bit The FPSCR[FI] bit indicates if the last floating point instruction had a result that was rounded. Each consecutive floating point instruction is suppose to set this bit to the correct value. What currently happens is this bit is not set as often as it should be. I have verified that this is the behavior of a real PowerPC 950. This patch fixes that problem by deciding to set this bit after each floating point instruction. https://www.pdfdrive.net/powerpc-microprocessor-family-the-programming-environments-for-32-e3087633.html Page 63 in table 2-4 is where the description of this bit can be found. Signed-off-by: John Arbuckle Signed-off-by: David Gibson --- target/ppc/fpu_helper.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 7714bfe0f9..8675d931b6 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -274,6 +274,7 @@ static inline void float_inexact_excp(CPUPPCState *env) { CPUState *cs = CPU(ppc_env_get_cpu(env)); + env->fpscr |= 1 << FPSCR_FI; env->fpscr |= 1 << FPSCR_XX; /* Update the floating-point exception summary */ env->fpscr |= FP_FX; @@ -533,6 +534,7 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr) { CPUState *cs = CPU(ppc_env_get_cpu(env)); int status = get_float_exception_flags(&env->fp_status); + bool inexact_happened = false; if (status & float_flag_divbyzero) { float_zero_divide_excp(env, raddr); @@ -542,6 +544,12 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr) float_underflow_excp(env); } else if (status & float_flag_inexact) { float_inexact_excp(env); + inexact_happened = true; + } + + /* if the inexact flag was not set */ + if (inexact_happened == false) { + env->fpscr &= ~(1 << FPSCR_FI); /* clear the FPSCR[FI] bit */ } if (cs->exception_index == POWERPC_EXCP_PROGRAM && From 3c47beb8defb8cab04ea2e19bc3fd8c65bf9af7d Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 2 Jul 2018 11:59:35 +1000 Subject: [PATCH 1967/2380] hw/ppc: Give sam46ex its own config option At present the Sam460ex board is activated by the general CONFIG_PPC4XX option. However that includes the board for both ppc-softmmu and (deprecated) ppcemb-softmmu builds. As Sam460ex is developed, that would require adding more things into ppcemb-softmmu, which we don't want to do. Signed-off-by: David Gibson --- default-configs/ppc-softmmu.mak | 1 + hw/ppc/Makefile.objs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 851b4afc21..475120bda2 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -22,6 +22,7 @@ CONFIG_OPENPIC_KVM=$(call land,$(CONFIG_E500),$(CONFIG_KVM)) CONFIG_PLATFORM_BUS=y CONFIG_ETSEC=y # For Sam460ex +CONFIG_SAM460EX=y CONFIG_USB_EHCI_SYSBUS=y CONFIG_SM501=y CONFIG_IDE_SII3112=y diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 86d82a6ec3..bcab6323b7 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -14,7 +14,8 @@ obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o # PowerPC 4xx boards obj-y += ppc4xx_devs.o ppc405_uc.o obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o -obj-$(CONFIG_PPC4XX) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o sam460ex.o +obj-$(CONFIG_PPC4XX) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o +obj-$(CONFIG_SAM460EX) += sam460ex.o # PReP obj-$(CONFIG_PREP) += prep.o obj-$(CONFIG_PREP) += prep_systemio.o From afb6e20429d5853e79e9a8af4a68b51d14b0c0c1 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Jun 2018 00:38:33 +0200 Subject: [PATCH 1968/2380] ppc4xx_i2c: Rewrite to model hardware more closely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite to make it closer to how real device works so that guest OS drivers can access I2C devices. Previously this was only a hack to allow U-Boot to get past accessing SPD EEPROMs but to support other I2C devices and allow guests to access them we need to model real device more properly. Signed-off-by: BALATON Zoltan Reviewed-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/i2c/ppc4xx_i2c.c | 299 +++++++++++++++++++----------------- include/hw/i2c/ppc4xx_i2c.h | 3 +- 2 files changed, 159 insertions(+), 143 deletions(-) diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index fca80d695a..d6dfafab31 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -34,16 +34,50 @@ #define PPC4xx_I2C_MEM_SIZE 18 +enum { + IIC_MDBUF = 0, + /* IIC_SDBUF = 2, */ + IIC_LMADR = 4, + IIC_HMADR, + IIC_CNTL, + IIC_MDCNTL, + IIC_STS, + IIC_EXTSTS, + IIC_LSADR, + IIC_HSADR, + IIC_CLKDIV, + IIC_INTRMSK, + IIC_XFRCNT, + IIC_XTCNTLSS, + IIC_DIRECTCNTL + /* IIC_INTR */ +}; + #define IIC_CNTL_PT (1 << 0) #define IIC_CNTL_READ (1 << 1) #define IIC_CNTL_CHT (1 << 2) #define IIC_CNTL_RPST (1 << 3) +#define IIC_CNTL_AMD (1 << 6) +#define IIC_CNTL_HMT (1 << 7) + +#define IIC_MDCNTL_EINT (1 << 2) +#define IIC_MDCNTL_ESM (1 << 3) +#define IIC_MDCNTL_FMDB (1 << 6) #define IIC_STS_PT (1 << 0) +#define IIC_STS_IRQA (1 << 1) #define IIC_STS_ERR (1 << 2) +#define IIC_STS_MDBF (1 << 4) #define IIC_STS_MDBS (1 << 5) #define IIC_EXTSTS_XFRA (1 << 0) +#define IIC_EXTSTS_BCS_FREE (4 << 4) +#define IIC_EXTSTS_BCS_BUSY (5 << 4) + +#define IIC_INTRMSK_EIMTC (1 << 0) +#define IIC_INTRMSK_EITA (1 << 1) +#define IIC_INTRMSK_EIIC (1 << 2) +#define IIC_INTRMSK_EIHE (1 << 3) #define IIC_XTCNTLSS_SRST (1 << 0) @@ -56,130 +90,83 @@ static void ppc4xx_i2c_reset(DeviceState *s) { PPC4xxI2CState *i2c = PPC4xx_I2C(s); - /* FIXME: Should also reset bus? - *if (s->address != ADDR_RESET) { - * i2c_end_transfer(s->bus); - *} - */ - - i2c->mdata = 0; - i2c->lmadr = 0; - i2c->hmadr = 0; + i2c->mdidx = -1; + memset(i2c->mdata, 0, ARRAY_SIZE(i2c->mdata)); + /* [hl][ms]addr are not affected by reset */ i2c->cntl = 0; i2c->mdcntl = 0; i2c->sts = 0; - i2c->extsts = 0x8f; - i2c->lsadr = 0; - i2c->hsadr = 0; + i2c->extsts = IIC_EXTSTS_BCS_FREE; i2c->clkdiv = 0; i2c->intrmsk = 0; i2c->xfrcnt = 0; i2c->xtcntlss = 0; - i2c->directcntl = 0xf; -} - -static inline bool ppc4xx_i2c_is_master(PPC4xxI2CState *i2c) -{ - return true; + i2c->directcntl = 0xf; /* all non-reserved bits set */ } static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size) { PPC4xxI2CState *i2c = PPC4xx_I2C(opaque); uint64_t ret; + int i; switch (addr) { - case 0: - ret = i2c->mdata; - if (ppc4xx_i2c_is_master(i2c)) { + case IIC_MDBUF: + if (i2c->mdidx < 0) { ret = 0xff; - - if (!(i2c->sts & IIC_STS_MDBS)) { - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read " - "without starting transfer\n", - TYPE_PPC4xx_I2C, __func__); - } else { - int pending = (i2c->cntl >> 4) & 3; - - /* get the next byte */ - int byte = i2c_recv(i2c->bus); - - if (byte < 0) { - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed " - "for device 0x%02x\n", TYPE_PPC4xx_I2C, - __func__, i2c->lmadr); - ret = 0xff; - } else { - ret = byte; - /* Raise interrupt if enabled */ - /*ppc4xx_i2c_raise_interrupt(i2c)*/; - } - - if (!pending) { - i2c->sts &= ~IIC_STS_MDBS; - /*i2c_end_transfer(i2c->bus);*/ - /*} else if (i2c->cntl & (IIC_CNTL_RPST | IIC_CNTL_CHT)) {*/ - } else if (pending) { - /* current smbus implementation doesn't like - multibyte xfer repeated start */ - i2c_end_transfer(i2c->bus); - if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 1)) { - /* if non zero is returned, the adress is not valid */ - i2c->sts &= ~IIC_STS_PT; - i2c->sts |= IIC_STS_ERR; - i2c->extsts |= IIC_EXTSTS_XFRA; - } else { - /*i2c->sts |= IIC_STS_PT;*/ - i2c->sts |= IIC_STS_MDBS; - i2c->sts &= ~IIC_STS_ERR; - i2c->extsts = 0; - } - } - pending--; - i2c->cntl = (i2c->cntl & 0xcf) | (pending << 4); - } - } else { - qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n", - TYPE_PPC4xx_I2C, __func__); + break; + } + ret = i2c->mdata[0]; + if (i2c->mdidx == 3) { + i2c->sts &= ~IIC_STS_MDBF; + } else if (i2c->mdidx == 0) { + i2c->sts &= ~IIC_STS_MDBS; + } + for (i = 0; i < i2c->mdidx; i++) { + i2c->mdata[i] = i2c->mdata[i + 1]; + } + if (i2c->mdidx >= 0) { + i2c->mdidx--; } break; - case 4: + case IIC_LMADR: ret = i2c->lmadr; break; - case 5: + case IIC_HMADR: ret = i2c->hmadr; break; - case 6: + case IIC_CNTL: ret = i2c->cntl; break; - case 7: + case IIC_MDCNTL: ret = i2c->mdcntl; break; - case 8: + case IIC_STS: ret = i2c->sts; break; - case 9: - ret = i2c->extsts; + case IIC_EXTSTS: + ret = i2c_bus_busy(i2c->bus) ? + IIC_EXTSTS_BCS_BUSY : IIC_EXTSTS_BCS_FREE; break; - case 10: + case IIC_LSADR: ret = i2c->lsadr; break; - case 11: + case IIC_HSADR: ret = i2c->hsadr; break; - case 12: + case IIC_CLKDIV: ret = i2c->clkdiv; break; - case 13: + case IIC_INTRMSK: ret = i2c->intrmsk; break; - case 14: + case IIC_XFRCNT: ret = i2c->xfrcnt; break; - case 15: + case IIC_XTCNTLSS: ret = i2c->xtcntlss; break; - case 16: + case IIC_DIRECTCNTL: ret = i2c->directcntl; break; default: @@ -202,99 +189,127 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, PPC4xxI2CState *i2c = opaque; switch (addr) { - case 0: - i2c->mdata = value; - if (!i2c_bus_busy(i2c->bus)) { - /* assume we start a write transfer */ - if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 0)) { - /* if non zero is returned, the adress is not valid */ - i2c->sts &= ~IIC_STS_PT; - i2c->sts |= IIC_STS_ERR; - i2c->extsts |= IIC_EXTSTS_XFRA; - } else { - i2c->sts |= IIC_STS_PT; - i2c->sts &= ~IIC_STS_ERR; - i2c->extsts = 0; - } + case IIC_MDBUF: + if (i2c->mdidx >= 3) { + break; } - if (i2c_bus_busy(i2c->bus)) { - if (i2c_send(i2c->bus, i2c->mdata)) { - /* if the target return non zero then end the transfer */ - i2c->sts &= ~IIC_STS_PT; - i2c->sts |= IIC_STS_ERR; - i2c->extsts |= IIC_EXTSTS_XFRA; - i2c_end_transfer(i2c->bus); - } + i2c->mdata[++i2c->mdidx] = value; + if (i2c->mdidx == 3) { + i2c->sts |= IIC_STS_MDBF; + } else if (i2c->mdidx == 0) { + i2c->sts |= IIC_STS_MDBS; } break; - case 4: + case IIC_LMADR: i2c->lmadr = value; - if (i2c_bus_busy(i2c->bus)) { - i2c_end_transfer(i2c->bus); - } break; - case 5: + case IIC_HMADR: i2c->hmadr = value; break; - case 6: - i2c->cntl = value; - if (i2c->cntl & IIC_CNTL_PT) { - if (i2c->cntl & IIC_CNTL_READ) { - if (i2c_bus_busy(i2c->bus)) { - /* end previous transfer */ - i2c->sts &= ~IIC_STS_PT; - i2c_end_transfer(i2c->bus); + case IIC_CNTL: + i2c->cntl = value & ~IIC_CNTL_PT; + if (value & IIC_CNTL_AMD) { + qemu_log_mask(LOG_UNIMP, "%s: only 7 bit addresses supported\n", + __func__); + } + if (value & IIC_CNTL_HMT && i2c_bus_busy(i2c->bus)) { + i2c_end_transfer(i2c->bus); + if (i2c->mdcntl & IIC_MDCNTL_EINT && + i2c->intrmsk & IIC_INTRMSK_EIHE) { + i2c->sts |= IIC_STS_IRQA; + qemu_irq_raise(i2c->irq); + } + } else if (value & IIC_CNTL_PT) { + int recv = (value & IIC_CNTL_READ) >> 1; + int tct = value >> 4 & 3; + int i; + + if (recv && (i2c->lmadr >> 1) >= 0x50 && (i2c->lmadr >> 1) < 0x58) { + /* smbus emulation does not like multi byte reads w/o restart */ + value |= IIC_CNTL_RPST; + } + + for (i = 0; i <= tct; i++) { + if (!i2c_bus_busy(i2c->bus)) { + i2c->extsts = IIC_EXTSTS_BCS_FREE; + if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, recv)) { + i2c->sts |= IIC_STS_ERR; + i2c->extsts |= IIC_EXTSTS_XFRA; + break; + } else { + i2c->sts &= ~IIC_STS_ERR; + } } - if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 1)) { - /* if non zero is returned, the adress is not valid */ - i2c->sts &= ~IIC_STS_PT; + if (!(i2c->sts & IIC_STS_ERR) && + i2c_send_recv(i2c->bus, &i2c->mdata[i], !recv)) { i2c->sts |= IIC_STS_ERR; i2c->extsts |= IIC_EXTSTS_XFRA; - } else { - /*i2c->sts |= IIC_STS_PT;*/ - i2c->sts |= IIC_STS_MDBS; - i2c->sts &= ~IIC_STS_ERR; - i2c->extsts = 0; + break; } - } else { - /* we actually already did the write transfer... */ - i2c->sts &= ~IIC_STS_PT; + if (value & IIC_CNTL_RPST || !(value & IIC_CNTL_CHT)) { + i2c_end_transfer(i2c->bus); + } + } + i2c->xfrcnt = i; + i2c->mdidx = i - 1; + if (recv && i2c->mdidx >= 0) { + i2c->sts |= IIC_STS_MDBS; + } + if (recv && i2c->mdidx == 3) { + i2c->sts |= IIC_STS_MDBF; + } + if (i && i2c->mdcntl & IIC_MDCNTL_EINT && + i2c->intrmsk & IIC_INTRMSK_EIMTC) { + i2c->sts |= IIC_STS_IRQA; + qemu_irq_raise(i2c->irq); } } break; - case 7: - i2c->mdcntl = value & 0xdf; + case IIC_MDCNTL: + i2c->mdcntl = value & 0x3d; + if (value & IIC_MDCNTL_ESM) { + qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", + __func__); + } + if (value & IIC_MDCNTL_FMDB) { + i2c->mdidx = -1; + memset(i2c->mdata, 0, ARRAY_SIZE(i2c->mdata)); + i2c->sts &= ~(IIC_STS_MDBF | IIC_STS_MDBS); + } break; - case 8: - i2c->sts &= ~(value & 0xa); + case IIC_STS: + i2c->sts &= ~(value & 0x0a); + if (value & IIC_STS_IRQA && i2c->mdcntl & IIC_MDCNTL_EINT) { + qemu_irq_lower(i2c->irq); + } break; - case 9: + case IIC_EXTSTS: i2c->extsts &= ~(value & 0x8f); break; - case 10: + case IIC_LSADR: i2c->lsadr = value; break; - case 11: + case IIC_HSADR: i2c->hsadr = value; break; - case 12: + case IIC_CLKDIV: i2c->clkdiv = value; break; - case 13: + case IIC_INTRMSK: i2c->intrmsk = value; break; - case 14: + case IIC_XFRCNT: i2c->xfrcnt = value & 0x77; break; - case 15: + case IIC_XTCNTLSS: + i2c->xtcntlss &= ~(value & 0xf0); if (value & IIC_XTCNTLSS_SRST) { /* Is it actually a full reset? U-Boot sets some regs before */ ppc4xx_i2c_reset(DEVICE(i2c)); break; } - i2c->xtcntlss = value; break; - case 16: + case IIC_DIRECTCNTL: i2c->directcntl = value & (IIC_DIRECTCNTL_SDAC & IIC_DIRECTCNTL_SCLC); i2c->directcntl |= (value & IIC_DIRECTCNTL_SCLC ? 1 : 0); bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SCL, diff --git a/include/hw/i2c/ppc4xx_i2c.h b/include/hw/i2c/ppc4xx_i2c.h index ea6c8e1a58..0891a9c948 100644 --- a/include/hw/i2c/ppc4xx_i2c.h +++ b/include/hw/i2c/ppc4xx_i2c.h @@ -46,7 +46,8 @@ typedef struct PPC4xxI2CState { qemu_irq irq; MemoryRegion iomem; bitbang_i2c_interface *bitbang; - uint8_t mdata; + int mdidx; + uint8_t mdata[4]; uint8_t lmadr; uint8_t hmadr; uint8_t cntl; From c6f2594c4b19db903cd76e89578cf501056b744d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Jun 2018 00:38:33 +0200 Subject: [PATCH 1969/2380] hw/timer: Add basic M41T80 emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Basic emulation of the M41T80 serial (I2C) RTC chip. Only getting time of day is implemented. Setting time and RTC alarm are not supported. Signed-off-by: BALATON Zoltan Reviewed-by: Cédric Le Goater Signed-off-by: David Gibson --- MAINTAINERS | 1 + default-configs/ppc-softmmu.mak | 1 + hw/timer/Makefile.objs | 1 + hw/timer/m41t80.c | 117 ++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 hw/timer/m41t80.c diff --git a/MAINTAINERS b/MAINTAINERS index 42a1892d6a..6630d691d1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -851,6 +851,7 @@ M: BALATON Zoltan L: qemu-ppc@nongnu.org S: Maintained F: hw/ide/sii3112.c +F: hw/timer/m41t80.c SH4 Machines ------------ diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 475120bda2..7e1a3d8135 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -28,6 +28,7 @@ CONFIG_SM501=y CONFIG_IDE_SII3112=y CONFIG_I2C=y CONFIG_BITBANG_I2C=y +CONFIG_M41T80=y # For Macs CONFIG_MAC=y diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 8b27a4b7ef..e16b2b913c 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -6,6 +6,7 @@ common-obj-$(CONFIG_CADENCE) += cadence_ttc.o common-obj-$(CONFIG_DS1338) += ds1338.o common-obj-$(CONFIG_HPET) += hpet.o common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o +common-obj-$(CONFIG_M41T80) += m41t80.o common-obj-$(CONFIG_M48T59) += m48t59.o ifeq ($(CONFIG_ISA_BUS),y) common-obj-$(CONFIG_M48T59) += m48t59-isa.o diff --git a/hw/timer/m41t80.c b/hw/timer/m41t80.c new file mode 100644 index 0000000000..734d7d95fc --- /dev/null +++ b/hw/timer/m41t80.c @@ -0,0 +1,117 @@ +/* + * M41T80 serial rtc emulation + * + * Copyright (c) 2018 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qemu/bcd.h" +#include "hw/i2c/i2c.h" + +#define TYPE_M41T80 "m41t80" +#define M41T80(obj) OBJECT_CHECK(M41t80State, (obj), TYPE_M41T80) + +typedef struct M41t80State { + I2CSlave parent_obj; + int8_t addr; +} M41t80State; + +static void m41t80_realize(DeviceState *dev, Error **errp) +{ + M41t80State *s = M41T80(dev); + + s->addr = -1; +} + +static int m41t80_send(I2CSlave *i2c, uint8_t data) +{ + M41t80State *s = M41T80(i2c); + + if (s->addr < 0) { + s->addr = data; + } else { + s->addr++; + } + return 0; +} + +static int m41t80_recv(I2CSlave *i2c) +{ + M41t80State *s = M41T80(i2c); + struct tm now; + qemu_timeval tv; + + if (s->addr < 0) { + s->addr = 0; + } + if (s->addr >= 1 && s->addr <= 7) { + qemu_get_timedate(&now, -1); + } + switch (s->addr++) { + case 0: + qemu_gettimeofday(&tv); + return to_bcd(tv.tv_usec / 10000); + case 1: + return to_bcd(now.tm_sec); + case 2: + return to_bcd(now.tm_min); + case 3: + return to_bcd(now.tm_hour); + case 4: + return to_bcd(now.tm_wday); + case 5: + return to_bcd(now.tm_mday); + case 6: + return to_bcd(now.tm_mon + 1); + case 7: + return to_bcd(now.tm_year % 100); + case 8 ... 19: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register: %d\n", + __func__, s->addr - 1); + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid register: %d\n", + __func__, s->addr - 1); + return 0; + } +} + +static int m41t80_event(I2CSlave *i2c, enum i2c_event event) +{ + M41t80State *s = M41T80(i2c); + + if (event == I2C_START_SEND) { + s->addr = -1; + } + return 0; +} + +static void m41t80_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); + + dc->realize = m41t80_realize; + sc->send = m41t80_send; + sc->recv = m41t80_recv; + sc->event = m41t80_event; +} + +static const TypeInfo m41t80_info = { + .name = TYPE_M41T80, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(M41t80State), + .class_init = m41t80_class_init, +}; + +static void m41t80_register_types(void) +{ + type_register_static(&m41t80_info); +} + +type_init(m41t80_register_types) From d2179f70d396cf796151ebf56cc177d7c3e53749 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Jun 2018 00:38:33 +0200 Subject: [PATCH 1970/2380] sam460ex: Add RTC device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Sam460ex has an M41T80 serial RTC chip on I2C bus 0 at address 0x68. Signed-off-by: BALATON Zoltan Reviewed-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/sam460ex.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 33ea51816c..a03d12d1dc 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -485,6 +485,7 @@ static void sam460ex_init(MachineState *machine) object_property_set_bool(OBJECT(dev), true, "realized", NULL); smbus_eeprom_init(i2c[0]->bus, 8, smbus_eeprom_buf, smbus_eeprom_size); g_free(smbus_eeprom_buf); + i2c_create_slave(i2c[0]->bus, "m41t80", 0x68); dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600800, uic[0][3]); i2c[1] = PPC4xx_I2C(dev); From 3c409c1927efde2fc7ac5f6cd5d79d0784be3b5d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Jun 2018 14:04:33 +0200 Subject: [PATCH 1971/2380] ppc440_uc: Basic emulation of PPC440 DMA controller PPC440 SoCs such as the AMCC 460EX have a DMA controller which is used by AmigaOS on the sam460ex. Implement the parts used by AmigaOS so it can get further booting on the sam460ex machine. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/ppc/ppc440.h | 1 + hw/ppc/ppc440_uc.c | 222 +++++++++++++++++++++++++++++++++++++++++++++ hw/ppc/sam460ex.c | 3 + 3 files changed, 226 insertions(+) diff --git a/hw/ppc/ppc440.h b/hw/ppc/ppc440.h index ad27db12e4..7cef936125 100644 --- a/hw/ppc/ppc440.h +++ b/hw/ppc/ppc440.h @@ -21,6 +21,7 @@ void ppc440_sdram_init(CPUPPCState *env, int nbanks, hwaddr *ram_bases, hwaddr *ram_sizes, int do_init); void ppc4xx_ahb_init(CPUPPCState *env); +void ppc4xx_dma_init(CPUPPCState *env, int dcr_base); void ppc460ex_pcie_init(CPUPPCState *env); #endif /* PPC440_H */ diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 123f4ac09d..32802d741b 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -13,6 +13,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qemu/log.h" #include "cpu.h" #include "hw/hw.h" #include "exec/address-spaces.h" @@ -802,6 +803,227 @@ void ppc4xx_ahb_init(CPUPPCState *env) qemu_register_reset(ppc4xx_ahb_reset, ahb); } +/*****************************************************************************/ +/* DMA controller */ + +#define DMA0_CR_CE (1 << 31) +#define DMA0_CR_PW (1 << 26 | 1 << 25) +#define DMA0_CR_DAI (1 << 24) +#define DMA0_CR_SAI (1 << 23) +#define DMA0_CR_DEC (1 << 2) + +enum { + DMA0_CR = 0x00, + DMA0_CT, + DMA0_SAH, + DMA0_SAL, + DMA0_DAH, + DMA0_DAL, + DMA0_SGH, + DMA0_SGL, + + DMA0_SR = 0x20, + DMA0_SGC = 0x23, + DMA0_SLP = 0x25, + DMA0_POL = 0x26, +}; + +typedef struct { + uint32_t cr; + uint32_t ct; + uint64_t sa; + uint64_t da; + uint64_t sg; +} PPC4xxDmaChnl; + +typedef struct { + int base; + PPC4xxDmaChnl ch[4]; + uint32_t sr; +} PPC4xxDmaState; + +static uint32_t dcr_read_dma(void *opaque, int dcrn) +{ + PPC4xxDmaState *dma = opaque; + uint32_t val = 0; + int addr = dcrn - dma->base; + int chnl = addr / 8; + + switch (addr) { + case 0x00 ... 0x1f: + switch (addr % 8) { + case DMA0_CR: + val = dma->ch[chnl].cr; + break; + case DMA0_CT: + val = dma->ch[chnl].ct; + break; + case DMA0_SAH: + val = dma->ch[chnl].sa >> 32; + break; + case DMA0_SAL: + val = dma->ch[chnl].sa; + break; + case DMA0_DAH: + val = dma->ch[chnl].da >> 32; + break; + case DMA0_DAL: + val = dma->ch[chnl].da; + break; + case DMA0_SGH: + val = dma->ch[chnl].sg >> 32; + break; + case DMA0_SGL: + val = dma->ch[chnl].sg; + break; + } + break; + case DMA0_SR: + val = dma->sr; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register %x (%d, %x)\n", + __func__, dcrn, chnl, addr); + } + + return val; +} + +static void dcr_write_dma(void *opaque, int dcrn, uint32_t val) +{ + PPC4xxDmaState *dma = opaque; + int addr = dcrn - dma->base; + int chnl = addr / 8; + + switch (addr) { + case 0x00 ... 0x1f: + switch (addr % 8) { + case DMA0_CR: + dma->ch[chnl].cr = val; + if (val & DMA0_CR_CE) { + int count = dma->ch[chnl].ct & 0xffff; + + if (count) { + int width, i, sidx, didx; + uint8_t *rptr, *wptr; + hwaddr rlen, wlen; + + sidx = didx = 0; + width = 1 << ((val & DMA0_CR_PW) >> 25); + rptr = cpu_physical_memory_map(dma->ch[chnl].sa, &rlen, 0); + wptr = cpu_physical_memory_map(dma->ch[chnl].da, &wlen, 1); + if (rptr && wptr) { + if (!(val & DMA0_CR_DEC) && + val & DMA0_CR_SAI && val & DMA0_CR_DAI) { + /* optimise common case */ + memmove(wptr, rptr, count * width); + sidx = didx = count * width; + } else { + /* do it the slow way */ + for (sidx = didx = i = 0; i < count; i++) { + uint64_t v = ldn_le_p(rptr + sidx, width); + stn_le_p(wptr + didx, width, v); + if (val & DMA0_CR_SAI) { + sidx += width; + } + if (val & DMA0_CR_DAI) { + didx += width; + } + } + } + } + if (wptr) { + cpu_physical_memory_unmap(wptr, wlen, 1, didx); + } + if (wptr) { + cpu_physical_memory_unmap(rptr, rlen, 0, sidx); + } + } + } + break; + case DMA0_CT: + dma->ch[chnl].ct = val; + break; + case DMA0_SAH: + dma->ch[chnl].sa &= 0xffffffffULL; + dma->ch[chnl].sa |= (uint64_t)val << 32; + break; + case DMA0_SAL: + dma->ch[chnl].sa &= 0xffffffff00000000ULL; + dma->ch[chnl].sa |= val; + break; + case DMA0_DAH: + dma->ch[chnl].da &= 0xffffffffULL; + dma->ch[chnl].da |= (uint64_t)val << 32; + break; + case DMA0_DAL: + dma->ch[chnl].da &= 0xffffffff00000000ULL; + dma->ch[chnl].da |= val; + break; + case DMA0_SGH: + dma->ch[chnl].sg &= 0xffffffffULL; + dma->ch[chnl].sg |= (uint64_t)val << 32; + break; + case DMA0_SGL: + dma->ch[chnl].sg &= 0xffffffff00000000ULL; + dma->ch[chnl].sg |= val; + break; + } + break; + case DMA0_SR: + dma->sr &= ~val; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register %x (%d, %x)\n", + __func__, dcrn, chnl, addr); + } +} + +static void ppc4xx_dma_reset(void *opaque) +{ + PPC4xxDmaState *dma = opaque; + int dma_base = dma->base; + + memset(dma, 0, sizeof(*dma)); + dma->base = dma_base; +} + +void ppc4xx_dma_init(CPUPPCState *env, int dcr_base) +{ + PPC4xxDmaState *dma; + int i; + + dma = g_malloc0(sizeof(*dma)); + dma->base = dcr_base; + qemu_register_reset(&ppc4xx_dma_reset, dma); + for (i = 0; i < 4; i++) { + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_CR, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_CT, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SAH, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SAL, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_DAH, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_DAL, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SGH, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SGL, + dma, &dcr_read_dma, &dcr_write_dma); + } + ppc_dcr_register(env, dcr_base + DMA0_SR, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + DMA0_SGC, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + DMA0_SLP, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + DMA0_POL, + dma, &dcr_read_dma, &dcr_write_dma); +} + /*****************************************************************************/ /* PCI Express controller */ /* FIXME: This is not complete and does not work, only implemented partially diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index a03d12d1dc..80c888c3ef 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -505,6 +505,9 @@ static void sam460ex_init(MachineState *machine) /* MAL */ ppc4xx_mal_init(env, 4, 16, &uic[2][3]); + /* DMA */ + ppc4xx_dma_init(env, 0x200); + /* 256K of L2 cache as memory */ ppc4xx_l2sram_init(env); /* FIXME: remove this after fixing l2sram mapping in ppc440_uc.c? */ From 71d0f1eac43044ccfaf7c398a93dcdb09bf29e0f Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Fri, 29 Jun 2018 11:48:16 +0200 Subject: [PATCH 1972/2380] target/ppc/kvm: get rid of kvm_get_fallback_smmu_info() Now that we're checking our MMU configuration is supported by KVM, rather than adjusting it to KVM, it doesn't really make sense to have a fallback for kvm_get_smmu_info(). If KVM is too old or buggy to provide the details, we should rather treat this as an error. This patch thus adds error reporting to kvm_get_smmu_info() and get rid of the fallback code. QEMU will now terminate if KVM fails to provide MMU details. This may break some very old setups, but the simplification is worth the sacrifice. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- target/ppc/kvm.c | 119 +++++++++-------------------------------------- 1 file changed, 21 insertions(+), 98 deletions(-) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 4df4ff6cbf..b6000f12b9 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -248,107 +248,25 @@ static int kvm_booke206_tlb_init(PowerPCCPU *cpu) #if defined(TARGET_PPC64) -static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu, - struct kvm_ppc_smmu_info *info) -{ - CPUPPCState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - memset(info, 0, sizeof(*info)); - - /* We don't have the new KVM_PPC_GET_SMMU_INFO ioctl, so - * need to "guess" what the supported page sizes are. - * - * For that to work we make a few assumptions: - * - * - Check whether we are running "PR" KVM which only supports 4K - * and 16M pages, but supports them regardless of the backing - * store characteritics. We also don't support 1T segments. - * - * This is safe as if HV KVM ever supports that capability or PR - * KVM grows supports for more page/segment sizes, those versions - * will have implemented KVM_CAP_PPC_GET_SMMU_INFO and thus we - * will not hit this fallback - * - * - Else we are running HV KVM. This means we only support page - * sizes that fit in the backing store. Additionally we only - * advertize 64K pages if the processor is ARCH 2.06 and we assume - * P7 encodings for the SLB and hash table. Here too, we assume - * support for any newer processor will mean a kernel that - * implements KVM_CAP_PPC_GET_SMMU_INFO and thus doesn't hit - * this fallback. - */ - if (kvmppc_is_pr(cs->kvm_state)) { - /* No flags */ - info->flags = 0; - info->slb_size = 64; - - /* Standard 4k base page size segment */ - info->sps[0].page_shift = 12; - info->sps[0].slb_enc = 0; - info->sps[0].enc[0].page_shift = 12; - info->sps[0].enc[0].pte_enc = 0; - - /* Standard 16M large page size segment */ - info->sps[1].page_shift = 24; - info->sps[1].slb_enc = SLB_VSID_L; - info->sps[1].enc[0].page_shift = 24; - info->sps[1].enc[0].pte_enc = 0; - } else { - int i = 0; - - /* HV KVM has backing store size restrictions */ - info->flags = KVM_PPC_PAGE_SIZES_REAL; - - if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) { - info->flags |= KVM_PPC_1T_SEGMENTS; - } - - if (env->mmu_model == POWERPC_MMU_2_06 || - env->mmu_model == POWERPC_MMU_2_07) { - info->slb_size = 32; - } else { - info->slb_size = 64; - } - - /* Standard 4k base page size segment */ - info->sps[i].page_shift = 12; - info->sps[i].slb_enc = 0; - info->sps[i].enc[0].page_shift = 12; - info->sps[i].enc[0].pte_enc = 0; - i++; - - /* 64K on MMU 2.06 and later */ - if (env->mmu_model == POWERPC_MMU_2_06 || - env->mmu_model == POWERPC_MMU_2_07) { - info->sps[i].page_shift = 16; - info->sps[i].slb_enc = 0x110; - info->sps[i].enc[0].page_shift = 16; - info->sps[i].enc[0].pte_enc = 1; - i++; - } - - /* Standard 16M large page size segment */ - info->sps[i].page_shift = 24; - info->sps[i].slb_enc = SLB_VSID_L; - info->sps[i].enc[0].page_shift = 24; - info->sps[i].enc[0].pte_enc = 0; - } -} - -static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info) +static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info, + Error **errp) { CPUState *cs = CPU(cpu); int ret; - if (kvm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) { - ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_GET_SMMU_INFO, info); - if (ret == 0) { - return; - } + if (!kvm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) { + error_setg(errp, "KVM doesn't expose the MMU features it supports"); + error_append_hint(errp, "Consider switching to a newer KVM\n"); + return; } - kvm_get_fallback_smmu_info(cpu, info); + ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_GET_SMMU_INFO, info); + if (ret == 0) { + return; + } + + error_setg_errno(errp, -ret, + "KVM failed to provide the MMU features it supports"); } struct ppc_radix_page_info *kvm_get_radix_page_info(void) @@ -415,7 +333,7 @@ bool kvmppc_hpt_needs_host_contiguous_pages(void) return false; } - kvm_get_smmu_info(cpu, &smmu_info); + kvm_get_smmu_info(cpu, &smmu_info, &error_fatal); return !!(smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL); } @@ -423,13 +341,18 @@ void kvm_check_mmu(PowerPCCPU *cpu, Error **errp) { struct kvm_ppc_smmu_info smmu_info; int iq, ik, jq, jk; + Error *local_err = NULL; /* For now, we only have anything to check on hash64 MMUs */ if (!cpu->hash64_opts || !kvm_enabled()) { return; } - kvm_get_smmu_info(cpu, &smmu_info); + kvm_get_smmu_info(cpu, &smmu_info, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG) && !(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) { @@ -2168,7 +2091,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) /* Find the largest hardware supported page size that's less than * or equal to the (logical) backing page size of guest RAM */ - kvm_get_smmu_info(POWERPC_CPU(first_cpu), &info); + kvm_get_smmu_info(POWERPC_CPU(first_cpu), &info, &error_fatal); rampagesize = qemu_getrampagesize(); best_page_shift = 0; From ab2569600916864a9db431a7e42f08d5fc72730e Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Fri, 29 Jun 2018 11:48:32 +0200 Subject: [PATCH 1973/2380] target/ppc/kvm: don't pass cpu to kvm_get_smmu_info() In a future patch the machine code will need to retrieve the MMU information from KVM during machine initialization before the CPUs are created. Actually, KVM_PPC_GET_SMMU_INFO is a VM class ioctl, and thus, we don't need to have a CPU object around. We just need for KVM to be initialized and use the kvm_state global. This patch just does that. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- target/ppc/kvm.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index b6000f12b9..9211ee2ee1 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -248,19 +248,19 @@ static int kvm_booke206_tlb_init(PowerPCCPU *cpu) #if defined(TARGET_PPC64) -static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info, - Error **errp) +static void kvm_get_smmu_info(struct kvm_ppc_smmu_info *info, Error **errp) { - CPUState *cs = CPU(cpu); int ret; - if (!kvm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) { + assert(kvm_state != NULL); + + if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) { error_setg(errp, "KVM doesn't expose the MMU features it supports"); error_append_hint(errp, "Consider switching to a newer KVM\n"); return; } - ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_GET_SMMU_INFO, info); + ret = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_SMMU_INFO, info); if (ret == 0) { return; } @@ -326,14 +326,13 @@ target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, bool kvmppc_hpt_needs_host_contiguous_pages(void) { - PowerPCCPU *cpu = POWERPC_CPU(first_cpu); static struct kvm_ppc_smmu_info smmu_info; if (!kvm_enabled()) { return false; } - kvm_get_smmu_info(cpu, &smmu_info, &error_fatal); + kvm_get_smmu_info(&smmu_info, &error_fatal); return !!(smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL); } @@ -348,7 +347,7 @@ void kvm_check_mmu(PowerPCCPU *cpu, Error **errp) return; } - kvm_get_smmu_info(cpu, &smmu_info, &local_err); + kvm_get_smmu_info(&smmu_info, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -2091,7 +2090,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) /* Find the largest hardware supported page size that's less than * or equal to the (logical) backing page size of guest RAM */ - kvm_get_smmu_info(POWERPC_CPU(first_cpu), &info, &error_fatal); + kvm_get_smmu_info(&info, &error_fatal); rampagesize = qemu_getrampagesize(); best_page_shift = 0; From e89372951d328143ecdb3a524bf53454ad9309b0 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Mon, 2 Jul 2018 10:54:56 +0200 Subject: [PATCH 1974/2380] spapr: compute default value of "hpt-max-page-size" later It is currently not possible to run a pseries-2.12 or older machine with HV KVM. QEMU prints the following and exits right away. qemu-system-ppc64: KVM doesn't support for base page shift 34 The "hpt-max-page-size" capability was recently added to spapr to hide host configuration details from HPT mode guests. Its default value for newer machine types is 64k. For backwards compatibility, pseries-2.12 and older machine types need a different value. This is handled as usual in a class init function. The default value is 16G, ie, all page sizes supported by POWER7 and newer CPUs, but HV KVM requires guest pages to be hpa contiguous as well as gpa contiguous. The default value is the page size used to back the guest RAM in this case. Unfortunately kvmppc_hpt_needs_host_contiguous_pages()->kvm_enabled() is called way before KVM init and returns false, even if the user requested KVM. We thus end up selecting 16G, which isn't supported by HV KVM. The default value must be set during machine init, because we can safely assume that KVM is initialized at this point. We fix this by moving the logic to default_caps_with_cpu(). Since the user cannot pass cap-hpt-max-page-size=0, we set the default to 0 in the pseries-2.12 class init function and use that as a flag to do the real work. Signed-off-by: Greg Kurz Reviewed-by: Eduardo Habkost Signed-off-by: David Gibson --- hw/ppc/spapr.c | 13 ++++++------- hw/ppc/spapr_caps.c | 13 +++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b2baec026f..062d9dc346 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4095,17 +4095,16 @@ static void spapr_machine_2_12_instance_options(MachineState *machine) static void spapr_machine_2_12_class_options(MachineClass *mc) { sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - uint8_t mps; spapr_machine_3_0_class_options(mc); SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_12); - if (kvmppc_hpt_needs_host_contiguous_pages()) { - mps = ctz64(qemu_getrampagesize()); - } else { - mps = 34; /* allow everything up to 16GiB, i.e. everything */ - } - smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = mps; + /* We depend on kvm_enabled() to choose a default value for the + * hpt-max-page-size capability. Of course we can't do it here + * because this is too early and the HW accelerator isn't initialzed + * yet. Postpone this to machine init (see default_caps_with_cpu()). + */ + smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 0; } DEFINE_SPAPR_MACHINE(2_12, "2.12", false); diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 62663ebdf5..aa605cea91 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -465,6 +465,19 @@ static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; } + /* This is for pseries-2.12 and older */ + if (smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] == 0) { + uint8_t mps; + + if (kvmppc_hpt_needs_host_contiguous_pages()) { + mps = ctz64(qemu_getrampagesize()); + } else { + mps = 34; /* allow everything up to 16GiB, i.e. everything */ + } + + caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = mps; + } + return caps; } From a1532a225a183c9fa60b9c1e8ac8a00c7771f64d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 2 Jul 2018 14:14:58 -0500 Subject: [PATCH 1975/2380] iotests: New test 223 for exporting dirty bitmap over NBD Although this test is NOT a full test of image fleecing (as it intentionally uses just a single block device directly exported over NBD, rather than trying to set up a blockdev-backup job with multiple BDS involved), it DOES prove that qemu as a server is able to properly expose a dirty bitmap over NBD. When coupled with image fleecing, it is then possible for a third-party client to do an incremental backup by using qemu-img map with the x-dirty-bitmap option to learn which parts of the file are dirty (perhaps confusingly, they are the portions mapped as "data":false - which is part of the reason this is still in the x- experimental namespace), along with another normal client (perhaps 'qemu-nbd -c' to expose the server over /dev/nbd0 and then just use normal I/O on that block device) to read the dirty sections. Signed-off-by: Eric Blake Message-Id: <20180702191458.28741-3-eblake@redhat.com> Tested-by: John Snow Reviewed-by: John Snow --- tests/qemu-iotests/223 | 138 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/223.out | 49 +++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 188 insertions(+) create mode 100755 tests/qemu-iotests/223 create mode 100644 tests/qemu-iotests/223.out diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 new file mode 100755 index 0000000000..b63b7a4f9e --- /dev/null +++ b/tests/qemu-iotests/223 @@ -0,0 +1,138 @@ +#!/bin/bash +# +# Test reading dirty bitmap over NBD +# +# Copyright (C) 2018 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 . +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + _cleanup_qemu + rm -f "$TEST_DIR/nbd" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto file # uses NBD as well +_supported_os Linux + +function do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ + | _filter_actual_image_size +} + +echo +echo "=== Create partially sparse image, then add dirty bitmap ===" +echo + +_make_test_img 4M +$QEMU_IO -c 'w -P 0x11 1M 2M' "$TEST_IMG" | _filter_qemu_io +run_qemu < >(_filter_nbd) + +silent= +_send_qemu_cmd $QEMU_HANDLE '{"execute":"qmp_capabilities"}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", + "arguments":{"driver":"qcow2", "node-name":"n", + "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", + "arguments":{"node":"n", "name":"b"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", + "arguments":{"addr":{"type":"unix", + "data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", + "arguments":{"name":"n", "bitmap":"b"}}' "return" + +echo +echo "=== Contrast normal status with dirty-bitmap status ===" +echo + +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT +IMG="driver=nbd,export=n,server.type=unix,server.path=$TEST_DIR/nbd" +$QEMU_IO -r -c 'r -P 0 0 1m' -c 'r -P 0x11 1m 1m' \ + -c 'r -P 0x22 2m 2m' --image-opts "$IMG" | _filter_qemu_io +$QEMU_IMG map --output=json --image-opts \ + "$IMG" | _filter_qemu_img_map +$QEMU_IMG map --output=json --image-opts \ + "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map + +echo +echo "=== End NBD server ===" +echo + +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + "arguments":{"name":"n"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out new file mode 100644 index 0000000000..33021c8e6a --- /dev/null +++ b/tests/qemu-iotests/223.out @@ -0,0 +1,49 @@ +QA output created by 223 + +=== Create partially sparse image, then add dirty bitmap === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +wrote 2097152/2097152 bytes at offset 1048576 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Write part of the file under active bitmap === + +wrote 2097152/2097152 bytes at offset 2097152 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== End dirty bitmap, and start serving image over NBD === + +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} + +=== Contrast normal status with dirty-bitmap status === + +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2097152/2097152 bytes at offset 2097152 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false}, +{ "start": 1048576, "length": 3145728, "depth": 0, "zero": false, "data": true}] +[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true}, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] + +=== End NBD server === + +{"return": {}} +{"return": {}} +{"return": {}} +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 8019a9f721..af309ebba7 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -221,3 +221,4 @@ 219 rw auto 221 rw auto quick 222 rw auto quick +223 rw auto quick From 2a8ceefca23bc2aaafe711f8afd7585be3c27064 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Mon, 2 Jul 2018 18:02:07 -0400 Subject: [PATCH 1976/2380] target/ppc: set is_jmp on ppc_tr_breakpoint_check The use of GDB breakpoints was broken by b0c2d52 ("target/ppc: convert to TranslatorOps", 2018-02-16). Fix it by setting is_jmp, so that we break from the translation loop as originally intended. Tested-by: Mark Cave-Ayland Reported-by: Mark Cave-Ayland Signed-off-by: Emilio G. Cota Signed-off-by: David Gibson --- target/ppc/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index e120f2ed0b..65c8cc94e7 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7482,6 +7482,7 @@ static bool ppc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, DisasContext *ctx = container_of(dcbase, DisasContext, base); gen_debug_exception(ctx); + dcbase->is_jmp = DISAS_NORETURN; /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that From 0123d3cbb06600e0624fdbf2255055d9cffe9c28 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Jun 2018 00:38:33 +0200 Subject: [PATCH 1977/2380] target/ppc: Relax reserved bitmask of indexed store instructions The PPC440 User Manual says that if bit 31 is set, the contents of CR[CR0] are undefined for indexed store instructions but this form is not invalid. Other PPC variants confirming to recent ISA where this bit may be reserved should ignore reserved bits and not raise invalid instruction exception. In particular, MorphOS has an stwx instruction with bit 31 set and fails to boot currently because of this. With this patch it gets further. Signed-off-by: David Gibson --- target/ppc/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 65c8cc94e7..9eaa10b421 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7054,7 +7054,7 @@ GEN_HANDLER(stop##u, opc, 0xFF, 0xFF, 0x00000000, type), #define GEN_STUX(name, stop, opc2, opc3, type) \ GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type), #define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk) \ -GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2), +GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000000, type, type2), #define GEN_STS(name, stop, op, type) \ GEN_ST(name, stop, op | 0x20, type) \ GEN_STU(name, stop, op | 0x21, type) \ From 29f9cef39eb1ae55e82c6763eb22d7a1bdff7276 Mon Sep 17 00:00:00 2001 From: Sebastian Bauer Date: Mon, 2 Jul 2018 21:13:45 +0200 Subject: [PATCH 1978/2380] ppc: Include vga cirrus card into the compiling process Drivers for this card exists on PPC-based AmigaOS guests so it is useful to allow users to emulate the graphics card for PPC machines. As cirrus vga is currently preferred over std(vga) in absence of any user choice, this change also sets the default display of spapr machines to std as otherwise qemu refuses to start these machines. Not specifying an explicit graphics mode is for instance done by 'make check'. Signed-off-by: Sebastian Bauer Signed-off-by: David Gibson --- default-configs/ppc-softmmu.mak | 1 + hw/ppc/spapr.c | 1 + 2 files changed, 2 insertions(+) diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 7e1a3d8135..6f12bf84f0 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -29,6 +29,7 @@ CONFIG_IDE_SII3112=y CONFIG_I2C=y CONFIG_BITBANG_I2C=y CONFIG_M41T80=y +CONFIG_VGA_CIRRUS=y # For Macs CONFIG_MAC=y diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 062d9dc346..6e8723d3ba 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3962,6 +3962,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->no_parallel = 1; mc->default_boot_order = ""; mc->default_ram_size = 512 * M_BYTE; + mc->default_display = "std"; mc->kvm_type = spapr_kvm_type; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_SPAPR_PCI_HOST_BRIDGE); mc->pci_allow_0_address = true; From d4d3e5a0d53a57282955e8a3ed7acc1ca90552d9 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 3 Jul 2018 10:37:56 +0800 Subject: [PATCH 1979/2380] block: Fix parameter checking in bdrv_co_copy_range_internal src may be NULL if BDRV_REQ_ZERO_WRITE flag is set, in this case only check dst and dst->bs. This bug was introduced when moving in the request tracking code from bdrv_co_copy_range, in 37aec7d75eb. This especially fixes the possible segfault when initializing src_bs with a NULL src. Signed-off-by: Fam Zheng Message-id: 20180703023758.14422-2-famz@redhat.com Reviewed-by: Jeff Cody Signed-off-by: Jeff Cody --- block/io.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/block/io.c b/block/io.c index 7035b78a20..b8845708d7 100644 --- a/block/io.c +++ b/block/io.c @@ -2897,18 +2897,11 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, bool recurse_src) { BdrvTrackedRequest src_req, dst_req; - BlockDriverState *src_bs = src->bs; - BlockDriverState *dst_bs = dst->bs; int ret; - if (!src || !dst || !src->bs || !dst->bs) { + if (!dst || !dst->bs) { return -ENOMEDIUM; } - ret = bdrv_check_byte_request(src->bs, src_offset, bytes); - if (ret) { - return ret; - } - ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes); if (ret) { return ret; @@ -2917,16 +2910,24 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags); } + if (!src || !src->bs) { + return -ENOMEDIUM; + } + ret = bdrv_check_byte_request(src->bs, src_offset, bytes); + if (ret) { + return ret; + } + if (!src->bs->drv->bdrv_co_copy_range_from || !dst->bs->drv->bdrv_co_copy_range_to || src->bs->encrypted || dst->bs->encrypted) { return -ENOTSUP; } - bdrv_inc_in_flight(src_bs); - bdrv_inc_in_flight(dst_bs); - tracked_request_begin(&src_req, src_bs, src_offset, + bdrv_inc_in_flight(src->bs); + bdrv_inc_in_flight(dst->bs); + tracked_request_begin(&src_req, src->bs, src_offset, bytes, BDRV_TRACKED_READ); - tracked_request_begin(&dst_req, dst_bs, dst_offset, + tracked_request_begin(&dst_req, dst->bs, dst_offset, bytes, BDRV_TRACKED_WRITE); wait_serialising_requests(&src_req); @@ -2944,8 +2945,8 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, } tracked_request_end(&src_req); tracked_request_end(&dst_req); - bdrv_dec_in_flight(src_bs); - bdrv_dec_in_flight(dst_bs); + bdrv_dec_in_flight(src->bs); + bdrv_dec_in_flight(dst->bs); return ret; } From dee12de89380483656072f775f5ef99f4426f966 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 3 Jul 2018 10:37:57 +0800 Subject: [PATCH 1980/2380] block: Honour BDRV_REQ_NO_SERIALISING in copy range This semantics is needed by drive-backup so implement it before using this API there. Reviewed-by: Stefan Hajnoczi Signed-off-by: Fam Zheng Message-id: 20180703023758.14422-3-famz@redhat.com Signed-off-by: Jeff Cody --- block/io.c | 6 ++++-- include/block/block.h | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/block/io.c b/block/io.c index b8845708d7..1a2272fad3 100644 --- a/block/io.c +++ b/block/io.c @@ -2930,8 +2930,10 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, tracked_request_begin(&dst_req, dst->bs, dst_offset, bytes, BDRV_TRACKED_WRITE); - wait_serialising_requests(&src_req); - wait_serialising_requests(&dst_req); + if (!(flags & BDRV_REQ_NO_SERIALISING)) { + wait_serialising_requests(&src_req); + wait_serialising_requests(&dst_req); + } if (recurse_src) { ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, src, src_offset, diff --git a/include/block/block.h b/include/block/block.h index 2ffc1c64c6..e5c7759a0c 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -659,13 +659,14 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host); * @dst: Destination child to copy data to * @dst_offset: offset in @dst image to write data * @bytes: number of bytes to copy - * @flags: request flags. Must be one of: - * 0 - actually read data from src; + * @flags: request flags. Supported flags: * BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero * write on @dst as if bdrv_co_pwrite_zeroes is * called. Used to simplify caller code, or * during BlockDriver.bdrv_co_copy_range_from() * recursion. + * BDRV_REQ_NO_SERIALISING - do not serialize with other overlapping + * requests currently in flight. * * Returns: 0 if succeeded; negative error code if failed. **/ From 9ded4a0114968e98b41494fc035ba14f84cdf700 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 3 Jul 2018 10:37:58 +0800 Subject: [PATCH 1981/2380] backup: Use copy offloading The implementation is similar to the 'qemu-img convert'. In the beginning of the job, offloaded copy is attempted. If it fails, further I/O will go through the existing bounce buffer code path. Then, as Kevin pointed out, both this and qemu-img convert can benefit from a local check if one request fails because of, for example, the offset is beyond EOF, but another may well be accepted by the protocol layer. This will be implemented separately. Reviewed-by: Stefan Hajnoczi Signed-off-by: Fam Zheng Message-id: 20180703023758.14422-4-famz@redhat.com Signed-off-by: Jeff Cody --- block/backup.c | 150 ++++++++++++++++++++++++++++++++------------- block/trace-events | 1 + 2 files changed, 110 insertions(+), 41 deletions(-) diff --git a/block/backup.c b/block/backup.c index d18be40caf..81895ddbe2 100644 --- a/block/backup.c +++ b/block/backup.c @@ -45,6 +45,8 @@ typedef struct BackupBlockJob { QLIST_HEAD(, CowRequest) inflight_reqs; HBitmap *copy_bitmap; + bool use_copy_range; + int64_t copy_range_size; } BackupBlockJob; static const BlockJobDriver backup_job_driver; @@ -86,19 +88,101 @@ static void cow_request_end(CowRequest *req) qemu_co_queue_restart_all(&req->wait_queue); } +/* Copy range to target with a bounce buffer and return the bytes copied. If + * error occured, return a negative error number */ +static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, + int64_t start, + int64_t end, + bool is_write_notifier, + bool *error_is_read, + void **bounce_buffer) +{ + int ret; + struct iovec iov; + QEMUIOVector qiov; + BlockBackend *blk = job->common.blk; + int nbytes; + + hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); + nbytes = MIN(job->cluster_size, job->len - start); + if (!*bounce_buffer) { + *bounce_buffer = blk_blockalign(blk, job->cluster_size); + } + iov.iov_base = *bounce_buffer; + iov.iov_len = nbytes; + qemu_iovec_init_external(&qiov, &iov, 1); + + ret = blk_co_preadv(blk, start, qiov.size, &qiov, + is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); + if (ret < 0) { + trace_backup_do_cow_read_fail(job, start, ret); + if (error_is_read) { + *error_is_read = true; + } + goto fail; + } + + if (qemu_iovec_is_zero(&qiov)) { + ret = blk_co_pwrite_zeroes(job->target, start, + qiov.size, BDRV_REQ_MAY_UNMAP); + } else { + ret = blk_co_pwritev(job->target, start, + qiov.size, &qiov, + job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); + } + if (ret < 0) { + trace_backup_do_cow_write_fail(job, start, ret); + if (error_is_read) { + *error_is_read = false; + } + goto fail; + } + + return nbytes; +fail: + hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); + return ret; + +} + +/* Copy range to target and return the bytes copied. If error occured, return a + * negative error number. */ +static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, + int64_t start, + int64_t end, + bool is_write_notifier) +{ + int ret; + int nr_clusters; + BlockBackend *blk = job->common.blk; + int nbytes; + + assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size)); + nbytes = MIN(job->copy_range_size, end - start); + nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size); + hbitmap_reset(job->copy_bitmap, start / job->cluster_size, + nr_clusters); + ret = blk_co_copy_range(blk, start, job->target, start, nbytes, + is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); + if (ret < 0) { + trace_backup_do_cow_copy_range_fail(job, start, ret); + hbitmap_set(job->copy_bitmap, start / job->cluster_size, + nr_clusters); + return ret; + } + + return nbytes; +} + static int coroutine_fn backup_do_cow(BackupBlockJob *job, int64_t offset, uint64_t bytes, bool *error_is_read, bool is_write_notifier) { - BlockBackend *blk = job->common.blk; CowRequest cow_request; - struct iovec iov; - QEMUIOVector bounce_qiov; - void *bounce_buffer = NULL; int ret = 0; int64_t start, end; /* bytes */ - int n; /* bytes */ + void *bounce_buffer = NULL; qemu_co_rwlock_rdlock(&job->flush_rwlock); @@ -110,60 +194,38 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, wait_for_overlapping_requests(job, start, end); cow_request_begin(&cow_request, job, start, end); - for (; start < end; start += job->cluster_size) { + while (start < end) { if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) { trace_backup_do_cow_skip(job, start); + start += job->cluster_size; continue; /* already copied */ } - hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); trace_backup_do_cow_process(job, start); - n = MIN(job->cluster_size, job->len - start); - - if (!bounce_buffer) { - bounce_buffer = blk_blockalign(blk, job->cluster_size); - } - iov.iov_base = bounce_buffer; - iov.iov_len = n; - qemu_iovec_init_external(&bounce_qiov, &iov, 1); - - ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov, - is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); - if (ret < 0) { - trace_backup_do_cow_read_fail(job, start, ret); - if (error_is_read) { - *error_is_read = true; + if (job->use_copy_range) { + ret = backup_cow_with_offload(job, start, end, is_write_notifier); + if (ret < 0) { + job->use_copy_range = false; } - hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); - goto out; } - - if (buffer_is_zero(iov.iov_base, iov.iov_len)) { - ret = blk_co_pwrite_zeroes(job->target, start, - bounce_qiov.size, BDRV_REQ_MAY_UNMAP); - } else { - ret = blk_co_pwritev(job->target, start, - bounce_qiov.size, &bounce_qiov, - job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); + if (!job->use_copy_range) { + ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier, + error_is_read, &bounce_buffer); } if (ret < 0) { - trace_backup_do_cow_write_fail(job, start, ret); - if (error_is_read) { - *error_is_read = false; - } - hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); - goto out; + break; } /* Publish progress, guest I/O counts as progress too. Note that the * offset field is an opaque progress value, it is not a disk offset. */ - job->bytes_read += n; - job_progress_update(&job->common.job, n); + start += ret; + job->bytes_read += ret; + job_progress_update(&job->common.job, ret); + ret = 0; } -out: if (bounce_buffer) { qemu_vfree(bounce_buffer); } @@ -665,6 +727,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, } else { job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); } + job->use_copy_range = true; + job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), + blk_get_max_transfer(job->target)); + job->copy_range_size = MAX(job->cluster_size, + QEMU_ALIGN_UP(job->copy_range_size, + job->cluster_size)); /* Required permissions are already taken with target's blk_new() */ block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, diff --git a/block/trace-events b/block/trace-events index 2d59b53fd3..c35287b48a 100644 --- a/block/trace-events +++ b/block/trace-events @@ -42,6 +42,7 @@ backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64 backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64 backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" +backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" # blockdev.c qmp_block_job_cancel(void *job) "job %p" From 8bb01b257f3398eae059e93bd7c8a3f5f54c5438 Mon Sep 17 00:00:00 2001 From: Sebastian Bauer Date: Tue, 26 Jun 2018 00:27:18 +0200 Subject: [PATCH 1982/2380] ehci: Don't fetch a NULL current qtd but advance the queue instead. Fetching qtd with the NULL address most likely makes no sense so from now on, we handle it this case similarly as if the terminate (T) bit is not set, which is already an exception as according to section 3.6 of the EHCI spec there is no T bit defined for the current_qtd field. The spec is a bit vague on how an EHCI driver should initialize these fields: "The general operational model is that the host controller can detect whether the overlay area contains a description of an active transfer" (p. 49). QEMU primarily uses the QTD_TOKEN_ACTIVE bit of the queue header to infer the activity state but there are other ways conceivable. This change allows QEMU to boot further into AmigaOS. The public available version of the EHCI driver recycles queue heads in some rare conditions but only clears the current_qtd field but not the status field. This works with many available EHCI PCI cards but e.g., not with the Freescale USB controller's found on the P5040. On the emulated EHCI controller of QEMU the consequence is that some garbage was read in, which resulted in a reset of the controller. This change fixes the problem. Signed-off-by: Sebastian Bauer Tested-by: BALATON Zoltan Message-id: 20180625222718.4488-1-mail@sebastianbauer.info Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 0134232627..e5acfc5ba5 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1672,7 +1672,8 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) ehci_set_state(ehci, async, EST_HORIZONTALQH); } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && - (NLPTR_TBIT(q->qh.current_qtd) == 0)) { + (NLPTR_TBIT(q->qh.current_qtd) == 0) && + (q->qh.current_qtd != 0)) { q->qtdaddr = q->qh.current_qtd; ehci_set_state(ehci, async, EST_FETCHQTD); From 8f36ec708834dfad58af6feb0b69bb0be6077f0f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 2 Jul 2018 18:27:52 +0200 Subject: [PATCH 1983/2380] xhci: fix guest-triggerable assert Set xhci into error state instead of throwing a core dump. Signed-off-by: Gerd Hoffmann Message-id: 20180702162752.29233-1-kraxel@redhat.com --- hw/usb/hcd-xhci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 721beb5486..8f1a01a405 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1954,7 +1954,12 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) for (i = 0; i < length; i++) { TRBType type; type = xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL); - assert(type); + if (!type) { + xhci_die(xhci); + xhci_ep_free_xfer(xfer); + epctx->kick_active--; + return; + } } xfer->streamid = streamid; From 4939a1df7d60b4a24458544557ae262852e28567 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 30 Jun 2018 19:54:48 +0300 Subject: [PATCH 1984/2380] ui: do not build-depend or link with libdrm, it is not needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opengl support brings up libdrm. But actually nothing uses this library or includes any of its headers. Just remove checking for it from configure. Signed-off-by: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180630165448.30795-1-mjt@msgid.tls.msk.ru Signed-off-by: Gerd Hoffmann --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 65548df1fc..951ff022dd 100755 --- a/configure +++ b/configure @@ -3865,7 +3865,7 @@ libs_softmmu="$libs_softmmu $fdt_libs" # opengl probe (for sdl2, gtk, milkymist-tmu2) if test "$opengl" != "no" ; then - opengl_pkgs="epoxy libdrm gbm" + opengl_pkgs="epoxy gbm" if $pkg_config $opengl_pkgs; then opengl_cflags="$($pkg_config --cflags $opengl_pkgs)" opengl_libs="$($pkg_config --libs $opengl_pkgs)" From da566a18a7799e5a655cd9f87e222b672cc93e7b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 2 Jul 2018 18:24:41 +0200 Subject: [PATCH 1985/2380] virtio-gpu: tweak scanout disable. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Factor out the code to virtio_gpu_disable_scanout(). - Allow disable scanout 0, show a message then. - Clear scanout->resource_id. Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Message-id: 20180702162443.16796-2-kraxel@redhat.com --- hw/display/virtio-gpu.c | 47 +++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 2dd3c3481a..054ec73c0a 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -399,6 +399,34 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, g->hostmem += res->hostmem; } +static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) +{ + struct virtio_gpu_scanout *scanout = &g->scanout[scanout_id]; + struct virtio_gpu_simple_resource *res; + DisplaySurface *ds = NULL; + + if (scanout->resource_id == 0) { + return; + } + + res = virtio_gpu_find_resource(g, scanout->resource_id); + if (res) { + res->scanout_bitmask &= ~(1 << scanout_id); + } + + if (scanout_id == 0) { + /* primary head */ + ds = qemu_create_message_surface(scanout->width ?: 640, + scanout->height ?: 480, + "Guest disabled display."); + } + dpy_gfx_replace_surface(scanout->con, ds); + scanout->resource_id = 0; + scanout->ds = NULL; + scanout->width = 0; + scanout->height = 0; +} + static void virtio_gpu_resource_destroy(VirtIOGPU *g, struct virtio_gpu_simple_resource *res) { @@ -583,24 +611,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, g->enable = 1; if (ss.resource_id == 0) { - scanout = &g->scanout[ss.scanout_id]; - if (scanout->resource_id) { - res = virtio_gpu_find_resource(g, scanout->resource_id); - if (res) { - res->scanout_bitmask &= ~(1 << ss.scanout_id); - } - } - if (ss.scanout_id == 0) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: illegal scanout id specified %d", - __func__, ss.scanout_id); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; - return; - } - dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); - scanout->ds = NULL; - scanout->width = 0; - scanout->height = 0; + virtio_gpu_disable_scanout(g, ss.scanout_id); return; } From c806cfa036a7ec991170eb9899f3a676bfcc3253 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 2 Jul 2018 18:24:42 +0200 Subject: [PATCH 1986/2380] virtio-gpu: update old resource too. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When switching scanout from one resource to another we must update the scanout_bitmask field for both new (set bit) and old (clear bit) resource. Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Message-id: 20180702162443.16796-3-kraxel@redhat.com --- hw/display/virtio-gpu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 054ec73c0a..336dc59007 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -590,7 +590,7 @@ static void virtio_unref_resource(pixman_image_t *image, void *data) static void virtio_gpu_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { - struct virtio_gpu_simple_resource *res; + struct virtio_gpu_simple_resource *res, *ores; struct virtio_gpu_scanout *scanout; pixman_format_code_t format; uint32_t offset; @@ -664,6 +664,11 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds); } + ores = virtio_gpu_find_resource(g, scanout->resource_id); + if (ores) { + ores->scanout_bitmask &= ~(1 << ss.scanout_id); + } + res->scanout_bitmask |= (1 << ss.scanout_id); scanout->resource_id = ss.resource_id; scanout->x = ss.r.x; From 1fccd7c5a9a722a9cbf1bc91693f4618034f01ac Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 2 Jul 2018 18:24:43 +0200 Subject: [PATCH 1987/2380] virtio-gpu: disable scanout when backing resource is destroyed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Message-id: 20180702162443.16796-4-kraxel@redhat.com --- hw/display/virtio-gpu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 336dc59007..08cd567218 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -430,6 +430,16 @@ static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) static void virtio_gpu_resource_destroy(VirtIOGPU *g, struct virtio_gpu_simple_resource *res) { + int i; + + if (res->scanout_bitmask) { + for (i = 0; i < g->conf.max_outputs; i++) { + if (res->scanout_bitmask & (1 << i)) { + virtio_gpu_disable_scanout(g, i); + } + } + } + pixman_image_unref(res->image); virtio_gpu_cleanup_mapping(res); QTAILQ_REMOVE(&g->reslist, res, next); From 1fcfdc435a3e25ab9037f6f7b8ab4bdf89ba1269 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 2 Jul 2018 18:33:44 +0200 Subject: [PATCH 1988/2380] vga: disable global_vmstate for 3.0+ machine types Move global_vmstate from vga_common_init() parameter to VGACommonState field. Set global_vmstate to true for isa vga devices, so nothing changes here. virtio-vga and secondary-vga already set global_vmstate to false so no change here either. All other pci vga devices get a new global-vmstate property, defaulting to false. A compat property flips it to true for older machine types. With this in place you don't get a vmstate section naming conflict any more when adding multiple pci vga devices to your vm. Signed-off-by: Gerd Hoffmann Message-Id: <20180702163345.17892-1-kraxel@redhat.com> --- hw/display/cirrus_vga.c | 9 ++++++--- hw/display/qxl.c | 3 ++- hw/display/vga-isa-mm.c | 3 ++- hw/display/vga-isa.c | 3 ++- hw/display/vga-pci.c | 5 +++-- hw/display/vga.c | 4 ++-- hw/display/vga_int.h | 3 ++- hw/display/virtio-vga.c | 2 +- hw/display/vmware_vga.c | 4 +++- include/hw/compat.h | 16 ++++++++++++++++ 10 files changed, 39 insertions(+), 13 deletions(-) diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 138ae961b9..1d050621f1 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -3048,7 +3048,8 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) s->vram_size_mb); return; } - vga_common_init(s, OBJECT(dev), true); + s->global_vmstate = true; + vga_common_init(s, OBJECT(dev)); cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0, isa_address_space(isadev), isa_address_space_io(isadev)); @@ -3062,7 +3063,7 @@ static Property isa_cirrus_vga_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState, cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct ISACirrusVGAState, - cirrus_vga.enable_blitter, true), + cirrus_vga.enable_blitter, true), DEFINE_PROP_END_OF_LIST(), }; @@ -3105,7 +3106,7 @@ static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) return; } /* setup VGA */ - vga_common_init(&s->vga, OBJECT(dev), true); + vga_common_init(&s->vga, OBJECT(dev)); cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev), pci_address_space_io(dev)); s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga); @@ -3134,6 +3135,8 @@ static Property pci_vga_cirrus_properties[] = { cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct PCICirrusVGAState, cirrus_vga.enable_blitter, true), + DEFINE_PROP_BOOL("global-vmstate", struct PCICirrusVGAState, + cirrus_vga.vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/qxl.c b/hw/display/qxl.c index a71714ccb4..3f740d7aa3 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2168,7 +2168,7 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp) qxl_init_ramsize(qxl); vga->vbe_size = qxl->vgamem_size; vga->vram_size_mb = qxl->vga.vram_size >> 20; - vga_common_init(vga, OBJECT(dev), true); + vga_common_init(vga, OBJECT(dev)); vga_init(vga, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), false); portio_list_init(&qxl->vga_port_list, OBJECT(dev), qxl_vga_portio_list, @@ -2410,6 +2410,7 @@ static Property qxl_properties[] = { #endif DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0), DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0), + DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c index e887b45651..d2d6b32abf 100644 --- a/hw/display/vga-isa-mm.c +++ b/hw/display/vga-isa-mm.c @@ -131,7 +131,8 @@ int isa_vga_mm_init(hwaddr vram_base, s = g_malloc0(sizeof(*s)); s->vga.vram_size_mb = VGA_RAM_SIZE >> 20; - vga_common_init(&s->vga, NULL, true); + s->vga.global_vmstate = true; + vga_common_init(&s->vga, NULL); vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space); s->vga.con = graphic_console_init(NULL, 0, s->vga.hw_ops, s); diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index 469834add5..fa44242e0d 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -58,7 +58,8 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) MemoryRegion *vga_io_memory; const MemoryRegionPortio *vga_ports, *vbe_ports; - vga_common_init(s, OBJECT(dev), true); + s->global_vmstate = true; + vga_common_init(s, OBJECT(dev)); s->legacy_address_space = isa_address_space(isadev); vga_io_memory = vga_init_io(s, OBJECT(dev), &vga_ports, &vbe_ports); isa_register_portio_list(isadev, &d->portio_vga, diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index 1ea559762a..e9e62eac70 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -222,7 +222,7 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) bool qext = false; /* vga + console init */ - vga_common_init(s, OBJECT(dev), true); + vga_common_init(s, OBJECT(dev)); vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), true); @@ -265,7 +265,7 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) bool qext = false; /* vga + console init */ - vga_common_init(s, OBJECT(dev), false); + vga_common_init(s, OBJECT(dev)); s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); /* mmio bar */ @@ -308,6 +308,7 @@ static Property vga_pci_properties[] = { DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true), DEFINE_PROP_BIT("qemu-extended-regs", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), + DEFINE_PROP_BOOL("global-vmstate", PCIVGAState, vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vga.c b/hw/display/vga.c index ed476e4e80..c82e6d240a 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -2163,7 +2163,7 @@ static inline uint32_t uint_clamp(uint32_t val, uint32_t vmin, uint32_t vmax) return val; } -void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) +void vga_common_init(VGACommonState *s, Object *obj) { int i, j, v, b; @@ -2202,7 +2202,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) s->is_vbe_vmstate = 1; memory_region_init_ram_nomigrate(&s->vram, obj, "vga.vram", s->vram_size, &error_fatal); - vmstate_register_ram(&s->vram, global_vmstate ? NULL : DEVICE(obj)); + vmstate_register_ram(&s->vram, s->global_vmstate ? NULL : DEVICE(obj)); xen_register_framebuffer(&s->vram); s->vram_ptr = memory_region_get_ram_ptr(&s->vram); s->get_bpp = vga_get_bpp; diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index f8fcf62a56..339661bc01 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -133,6 +133,7 @@ typedef struct VGACommonState { bool full_update_gfx; bool big_endian_fb; bool default_endian_fb; + bool global_vmstate; /* hardware mouse cursor support */ uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32]; uint32_t hw_cursor_x; @@ -157,7 +158,7 @@ static inline int c6_to_8(int v) return (v << 2) | (b << 1) | b; } -void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate); +void vga_common_init(VGACommonState *s, Object *obj); void vga_init(VGACommonState *s, Object *obj, MemoryRegion *address_space, MemoryRegion *address_space_io, bool init_vga_ports); MemoryRegion *vga_init_io(VGACommonState *s, Object *obj, diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 97db6c3372..8d3d9e14a7 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -106,7 +106,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) /* init vga compat bits */ vga->vram_size_mb = 8; - vga_common_init(vga, OBJECT(vpci_dev), false); + vga_common_init(vga, OBJECT(vpci_dev)); vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev), pci_address_space_io(&vpci_dev->pci_dev), true); pci_register_bar(&vpci_dev->pci_dev, 0, diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index bd3e8b3586..39f4d40801 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1241,7 +1241,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, &error_fatal); s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram); - vga_common_init(&s->vga, OBJECT(dev), true); + vga_common_init(&s->vga, OBJECT(dev)); vga_init(&s->vga, OBJECT(dev), address_space, io, true); vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga); s->new_depth = 32; @@ -1321,6 +1321,8 @@ static void pci_vmsvga_realize(PCIDevice *dev, Error **errp) static Property vga_vmware_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct pci_vmsvga_state_s, chip.vga.vram_size_mb, 16), + DEFINE_PROP_BOOL("global-vmstate", struct pci_vmsvga_state_s, + chip.vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/compat.h b/include/hw/compat.h index 44d5964060..c08f4040bb 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -10,6 +10,22 @@ .driver = "hda-audio",\ .property = "use-timer",\ .value = "false",\ + },{\ + .driver = "cirrus-vga",\ + .property = "global-vmstate",\ + .value = "true",\ + },{\ + .driver = "VGA",\ + .property = "global-vmstate",\ + .value = "true",\ + },{\ + .driver = "vmware-svga",\ + .property = "global-vmstate",\ + .value = "true",\ + },{\ + .driver = "qxl-vga",\ + .property = "global-vmstate",\ + .value = "true",\ }, #define HW_COMPAT_2_11 \ From 9d340f6755fe3ee311f6758a453fae785ec05528 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 2 Jul 2018 16:55:11 +0200 Subject: [PATCH 1989/2380] audio/hda: adjust larger gaps faster Signed-off-by: Gerd Hoffmann Message-id: 20180702145513.11481-1-kraxel@redhat.com --- hw/audio/hda-codec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 31c66d4255..9f630fa37f 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -203,6 +203,9 @@ static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) if (target_pos < -limit) { corr = -HDA_TIMER_TICKS; } + if (target_pos < -(2 * limit)) { + corr = -(4 * HDA_TIMER_TICKS); + } if (corr == 0) { return; } From cabedbc24eb53c3feee612750929a7e307600e20 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 2 Jul 2018 16:55:12 +0200 Subject: [PATCH 1990/2380] audio/hda: fix CID 1393631 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180702145513.11481-2-kraxel@redhat.com --- hw/audio/hda-codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 9f630fa37f..2b58c3505b 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -189,7 +189,7 @@ struct HDAAudioState { static inline int64_t hda_bytes_per_second(HDAAudioStream *st) { - return 2 * st->as.nchannels * st->as.freq; + return 2LL * st->as.nchannels * st->as.freq; } static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) From 1a961e785f59eba1fa8e06d5b1ebc84927b4b3a3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 2 Jul 2018 18:15:24 +0200 Subject: [PATCH 1991/2380] audio: add audio timer trace points MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Track audio timer starts and stops. Also trace delayed audio timer calls. Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20180702161524.17268-1-kraxel@redhat.com --- audio/audio.c | 28 +++++++++++++++++++++++++--- audio/trace-events | 5 +++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index d6e91901aa..1ace47f510 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -29,6 +29,7 @@ #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "sysemu/replay.h" +#include "trace.h" #define AUDIO_CAP "audio" #include "audio_int.h" @@ -1129,6 +1130,10 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) /* * Timer */ + +static bool audio_timer_running; +static uint64_t audio_timer_last; + static int audio_is_timer_needed (void) { HWVoiceIn *hwi = NULL; @@ -1148,14 +1153,31 @@ static void audio_reset_timer (AudioState *s) if (audio_is_timer_needed ()) { timer_mod_anticipate_ns(s->ts, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); - } - else { - timer_del (s->ts); + if (!audio_timer_running) { + audio_timer_running = true; + audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + trace_audio_timer_start(conf.period.ticks / SCALE_MS); + } + } else { + timer_del(s->ts); + if (audio_timer_running) { + audio_timer_running = false; + trace_audio_timer_stop(); + } } } static void audio_timer (void *opaque) { + int64_t now, diff; + + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + diff = now - audio_timer_last; + if (diff > conf.period.ticks * 3 / 2) { + trace_audio_timer_delayed(diff / SCALE_MS); + } + audio_timer_last = now; + audio_run ("timer"); audio_reset_timer (opaque); } diff --git a/audio/trace-events b/audio/trace-events index d37639e611..c986469319 100644 --- a/audio/trace-events +++ b/audio/trace-events @@ -15,3 +15,8 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d" # audio/ossaudio.c oss_version(int version) "OSS version = 0x%x" oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" + +# audio/audio.c +audio_timer_start(int interval) "interval %d ms" +audio_timer_stop(void) "" +audio_timer_delayed(int interval) "interval %d ms" From c06982122036546fcbfe40f5b22ae7088d28c9a2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:27 +0200 Subject: [PATCH 1992/2380] qmp: Say "out-of-band" instead of "Out-Of-Band" Affects documentation and a few error messages. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-2-armbru@redhat.com> --- docs/devel/qapi-code-gen.txt | 2 +- docs/interop/qmp-spec.txt | 2 +- monitor.c | 14 +++++++------- qapi/misc.json | 2 +- tests/qapi-schema/qapi-schema-test.json | 2 +- tests/qmp-test.c | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 94a7e8f4d0..9625798d16 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -641,7 +641,7 @@ possible, the command expression should include the optional key 'success-response' with boolean value false. So far, only QGA makes use of this member. -A command can be declared to support Out-Of-Band (OOB) execution. By +A command can be declared to support out-of-band (OOB) execution. By default, commands do not support OOB. To declare a command that supports it, the schema includes an extra 'allow-oob' field. For example: diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt index 6fa193a80b..2bb492d1ea 100644 --- a/docs/interop/qmp-spec.txt +++ b/docs/interop/qmp-spec.txt @@ -85,7 +85,7 @@ The greeting message format is: Currently supported capabilities are: -- "oob": the QMP server supports "Out-Of-Band" (OOB) command +- "oob": the QMP server supports "out-of-band" (OOB) command execution. For more details, please see the "run-oob" parameter in the "Issuing Commands" section below. Not all commands allow this "oob" execution. The "query-qmp-schema" command can be used to diff --git a/monitor.c b/monitor.c index 3a0ea0c602..208d7c7cfd 100644 --- a/monitor.c +++ b/monitor.c @@ -1268,11 +1268,11 @@ static void qmp_caps_check(Monitor *mon, QMPCapabilityList *list, case QMP_CAPABILITY_OOB: if (!mon->use_io_thr) { /* - * Out-Of-Band only works with monitors that are + * Out-of-band only works with monitors that are * running on dedicated IOThread. */ error_setg(errp, "This monitor does not support " - "Out-Of-Band (OOB)"); + "out-of-band (OOB)"); return; } break; @@ -1320,7 +1320,7 @@ static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp) if (qmp_is_oob(req)) { if (!qmp_oob_enabled(mon)) { - error_setg(errp, "Please enable Out-Of-Band first " + error_setg(errp, "Please enable out-of-band first " "for the session during capabilities negotiation"); return false; } @@ -4294,7 +4294,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) /* When OOB is enabled, the "id" field is mandatory. */ if (qmp_oob_enabled(mon) && !id) { - error_setg(&err, "Out-Of-Band capability requires that " + error_setg(&err, "Out-of-band capability requires that " "every command contains an 'id' field"); goto err; } @@ -4308,7 +4308,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) qdict_del(qdict, "id"); if (qmp_is_oob(qdict)) { - /* Out-Of-Band (OOB) requests are executed directly in parser. */ + /* Out-of-band (OOB) requests are executed directly in parser. */ trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(req_obj->id) ?: ""); monitor_qmp_dispatch_one(req_obj); @@ -4684,12 +4684,12 @@ void monitor_init(Chardev *chr, int flags) if (use_oob) { if (CHARDEV_IS_MUX(chr)) { - error_report("Monitor Out-Of-Band is not supported with " + error_report("Monitor out-of-band is not supported with " "MUX typed chardev backend"); exit(1); } if (use_readline) { - error_report("Monitor Out-Of-band is only supported by QMP"); + error_report("Monitor out-of-band is only supported by QMP"); exit(1); } } diff --git a/qapi/misc.json b/qapi/misc.json index 29da7856e3..0446c3e48e 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -46,7 +46,7 @@ # Enumeration of capabilities to be advertised during initial client # connection, used for agreeing on particular QMP extension behaviors. # -# @oob: QMP ability to support Out-Of-Band requests. +# @oob: QMP ability to support out-of-band requests. # (Please refer to qmp-spec.txt for more information on OOB) # # Since: 2.12 diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 7b59817f04..e9e401c01f 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -141,7 +141,7 @@ { 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' } { 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true } -# Smoke test on Out-Of-Band and allow-preconfig-test +# Smoke test on out-of-band and allow-preconfig-test { 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': true } # For testing integer range flattening in opts-visitor. The following schema diff --git a/tests/qmp-test.c b/tests/qmp-test.c index a49cbc6fde..5206b14ca6 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -135,7 +135,7 @@ static void test_qmp_protocol(void) qtest_quit(qts); } -/* Tests for Out-Of-Band support. */ +/* Tests for out-of-band support. */ static void test_qmp_oob(void) { QTestState *qts; From c5f57ed026bbe6817b17dbc842e17fd23da1b607 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:28 +0200 Subject: [PATCH 1993/2380] monitor: Spell "I/O thread" consistently in comments Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-3-armbru@redhat.com> --- monitor.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/monitor.c b/monitor.c index 208d7c7cfd..4f4f309d45 100644 --- a/monitor.c +++ b/monitor.c @@ -243,7 +243,7 @@ struct Monitor { /* Let's add monitor global variables to this struct. */ static struct { IOThread *mon_iothread; - /* Bottom half to dispatch the requests received from IO thread */ + /* Bottom half to dispatch the requests received from I/O thread */ QEMUBH *qmp_dispatcher_bh; /* Bottom half to deliver the responses back to clients */ QEMUBH *qmp_respond_bh; @@ -518,7 +518,7 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) { if (mon->use_io_thr) { /* - * If using IO thread, we need to queue the item so that IO + * If using I/O thread, we need to queue the item so that I/O * thread will do the rest for us. Take refcount so that * caller won't free the data (which will be finally freed in * responder thread). @@ -529,7 +529,7 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) qemu_bh_schedule(mon_global.qmp_respond_bh); } else { /* - * If not using monitor IO thread, then we are in main thread. + * If not using monitor I/O thread, then we are in main thread. * Do the emission right away. */ monitor_json_emitter_raw(mon, data); @@ -1269,7 +1269,7 @@ static void qmp_caps_check(Monitor *mon, QMPCapabilityList *list, if (!mon->use_io_thr) { /* * Out-of-band only works with monitors that are - * running on dedicated IOThread. + * running on dedicated I/O thread. */ error_setg(errp, "This monitor does not support " "out-of-band (OOB)"); @@ -4403,7 +4403,7 @@ int monitor_suspend(Monitor *mon) if (monitor_is_qmp(mon)) { /* - * Kick iothread to make sure this takes effect. It'll be + * Kick I/O thread to make sure this takes effect. It'll be * evaluated again in prepare() of the watch object. */ aio_notify(iothread_get_aio_context(mon_global.mon_iothread)); @@ -4422,7 +4422,7 @@ void monitor_resume(Monitor *mon) if (atomic_dec_fetch(&mon->suspend_cnt) == 0) { if (monitor_is_qmp(mon)) { /* - * For QMP monitors that are running in IOThread, let's + * For QMP monitors that are running in I/O thread, let's * kick the thread in case it's sleeping. */ if (mon->use_io_thr) { @@ -4446,7 +4446,7 @@ static QObject *get_qmp_greeting(Monitor *mon) for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) { if (!mon->use_io_thr && cap == QMP_CAPABILITY_OOB) { - /* Monitors that are not using IOThread won't support OOB */ + /* Monitors that are not using I/O thread won't support OOB */ continue; } qlist_append_str(cap_list, QMPCapability_str(cap)); @@ -4587,9 +4587,9 @@ static void monitor_iothread_init(void) NULL); /* - * Unlike the dispatcher BH, this must be run on the monitor IO - * thread, so that monitors that are using IO thread will make - * sure read/write operations are all done on the IO thread. + * Unlike the dispatcher BH, this must be run on the monitor I/O + * thread, so that monitors that are using I/O thread will make + * sure read/write operations are all done on the I/O thread. */ mon_global.qmp_respond_bh = aio_bh_new(monitor_get_aio_context(), monitor_qmp_bh_responder, @@ -4661,7 +4661,7 @@ static void monitor_qmp_setup_handlers_bh(void *opaque) if (mon->use_io_thr) { /* * When use_io_thr is set, we use the global shared dedicated - * IO thread for this monitor to handle input/output. + * I/O thread for this monitor to handle input/output. */ context = monitor_get_io_context(); /* We should have inited globals before reaching here. */ @@ -4718,7 +4718,7 @@ void monitor_init(Chardev *chr, int flags) /* * We can't call qemu_chr_fe_set_handlers() directly here * since during the procedure the chardev will be active - * and running in monitor iothread, while we'll still do + * and running in monitor I/O thread, while we'll still do * something before returning from it, which is a possible * race too. To avoid that, we just create a BH to setup * the handlers. @@ -4745,16 +4745,16 @@ void monitor_cleanup(void) Monitor *mon, *next; /* - * We need to explicitly stop the iothread (but not destroy it), - * cleanup the monitor resources, then destroy the iothread since + * We need to explicitly stop the I/O thread (but not destroy it), + * cleanup the monitor resources, then destroy the I/O thread since * we need to unregister from chardev below in * monitor_data_destroy(), and chardev is not thread-safe yet */ iothread_stop(mon_global.mon_iothread); /* - * After we have IOThread to send responses, it's possible that - * when we stop the IOThread there are still replies queued in the + * After we have I/O thread to send responses, it's possible that + * when we stop the I/O thread there are still replies queued in the * responder queue. Flush all of them. Note that even after this * flush it's still possible that out buffer is not flushed. * It'll be done in below monitor_flush() as the last resort. @@ -4770,7 +4770,7 @@ void monitor_cleanup(void) } qemu_mutex_unlock(&monitor_lock); - /* QEMUBHs needs to be deleted before destroying the IOThread. */ + /* QEMUBHs needs to be deleted before destroying the I/O thread */ qemu_bh_delete(mon_global.qmp_dispatcher_bh); mon_global.qmp_dispatcher_bh = NULL; qemu_bh_delete(mon_global.qmp_respond_bh); From e1a6dc91ddb55ef77a705b62b6e62634631fd57d Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 3 Jul 2018 09:03:03 +0100 Subject: [PATCH 1994/2380] crypto: Implement TLS Pre-Shared Keys (PSK). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-Shared Keys (PSK) is a simpler mechanism for enabling TLS connections than using certificates. It requires only a simple secret key: $ mkdir -m 0700 /tmp/keys $ psktool -u rjones -p /tmp/keys/keys.psk $ cat /tmp/keys/keys.psk rjones:d543770c15ad93d76443fb56f501a31969235f47e999720ae8d2336f6a13fcbc The key can be secretly shared between clients and servers. Clients must specify the directory containing the "keys.psk" file and a username (defaults to "qemu"). Servers must specify only the directory. Example NBD client: $ qemu-img info \ --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=rjones,endpoint=client \ --image-opts \ file.driver=nbd,file.host=localhost,file.port=10809,file.tls-creds=tls0,file.export=/ Example NBD server using qemu-nbd: $ qemu-nbd -t -x / \ --object tls-creds-psk,id=tls0,endpoint=server,dir=/tmp/keys \ --tls-creds tls0 \ image.qcow2 Example NBD server using nbdkit: $ nbdkit -n -e / -fv \ --tls=on --tls-psk=/tmp/keys/keys.psk \ file file=disk.img Signed-off-by: Richard W.M. Jones Signed-off-by: Daniel P. Berrangé --- crypto/Makefile.objs | 1 + crypto/tlscredspsk.c | 308 +++++++++++++++++++++++++++++++++ crypto/tlssession.c | 56 +++++- crypto/trace-events | 3 + include/crypto/tlscredspsk.h | 106 ++++++++++++ qemu-doc.texi | 37 ++++ qemu-options.hx | 24 +++ tests/Makefile.include | 4 +- tests/crypto-tls-psk-helpers.c | 50 ++++++ tests/crypto-tls-psk-helpers.h | 29 ++++ tests/test-crypto-tlssession.c | 179 ++++++++++++++++--- 11 files changed, 774 insertions(+), 23 deletions(-) create mode 100644 crypto/tlscredspsk.c create mode 100644 include/crypto/tlscredspsk.h create mode 100644 tests/crypto-tls-psk-helpers.c create mode 100644 tests/crypto-tls-psk-helpers.h diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs index 2b99e08062..756bab111b 100644 --- a/crypto/Makefile.objs +++ b/crypto/Makefile.objs @@ -15,6 +15,7 @@ crypto-obj-$(CONFIG_AF_ALG) += cipher-afalg.o crypto-obj-$(CONFIG_AF_ALG) += hash-afalg.o crypto-obj-y += tlscreds.o crypto-obj-y += tlscredsanon.o +crypto-obj-y += tlscredspsk.o crypto-obj-y += tlscredsx509.o crypto-obj-y += tlssession.o crypto-obj-y += secret.o diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c new file mode 100644 index 0000000000..7be7c8efdd --- /dev/null +++ b/crypto/tlscredspsk.c @@ -0,0 +1,308 @@ +/* + * QEMU crypto TLS Pre-Shared Keys (PSK) support + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "crypto/tlscredspsk.h" +#include "tlscredspriv.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "trace.h" + + +#ifdef CONFIG_GNUTLS + +static int +lookup_key(const char *pskfile, const char *username, gnutls_datum_t *key, + Error **errp) +{ + const size_t ulen = strlen(username); + GError *gerr = NULL; + char *content = NULL; + char **lines = NULL; + size_t clen = 0, i; + int ret = -1; + + if (!g_file_get_contents(pskfile, &content, &clen, &gerr)) { + error_setg(errp, "Cannot read PSK file %s: %s", + pskfile, gerr->message); + g_error_free(gerr); + return -1; + } + + lines = g_strsplit(content, "\n", -1); + for (i = 0; lines[i] != NULL; ++i) { + if (strncmp(lines[i], username, ulen) == 0 && lines[i][ulen] == ':') { + key->data = (unsigned char *) g_strdup(&lines[i][ulen + 1]); + key->size = strlen(lines[i]) - ulen - 1; + ret = 0; + goto out; + } + } + error_setg(errp, "Username %s not found in PSK file %s", + username, pskfile); + + out: + free(content); + g_strfreev(lines); + return ret; +} + +static int +qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds, + Error **errp) +{ + char *pskfile = NULL, *dhparams = NULL; + const char *username; + int ret; + int rv = -1; + gnutls_datum_t key = { .data = NULL }; + + trace_qcrypto_tls_creds_psk_load(creds, + creds->parent_obj.dir ? creds->parent_obj.dir : ""); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + if (creds->username) { + error_setg(errp, "username should not be set when endpoint=server"); + goto cleanup; + } + + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_DH_PARAMS, + false, &dhparams, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_PSKFILE, + true, &pskfile, errp) < 0) { + goto cleanup; + } + + ret = gnutls_psk_allocate_server_credentials(&creds->data.server); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } + + if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams, + &creds->parent_obj.dh_params, + errp) < 0) { + goto cleanup; + } + + gnutls_psk_set_server_credentials_file(creds->data.server, pskfile); + gnutls_psk_set_server_dh_params(creds->data.server, + creds->parent_obj.dh_params); + } else { + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_PSKFILE, + true, &pskfile, errp) < 0) { + goto cleanup; + } + + if (creds->username) { + username = creds->username; + } else { + username = "qemu"; + } + if (lookup_key(pskfile, username, &key, errp) != 0) { + goto cleanup; + } + + ret = gnutls_psk_allocate_client_credentials(&creds->data.client); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } + + gnutls_psk_set_client_credentials(creds->data.client, + username, &key, GNUTLS_PSK_KEY_HEX); + } + + rv = 0; + cleanup: + g_free(key.data); + g_free(pskfile); + g_free(dhparams); + return rv; +} + + +static void +qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds) +{ + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + if (creds->data.client) { + gnutls_psk_free_client_credentials(creds->data.client); + creds->data.client = NULL; + } + } else { + if (creds->data.server) { + gnutls_psk_free_server_credentials(creds->data.server); + creds->data.server = NULL; + } + } + if (creds->parent_obj.dh_params) { + gnutls_dh_params_deinit(creds->parent_obj.dh_params); + creds->parent_obj.dh_params = NULL; + } +} + +#else /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS credentials support requires GNUTLS"); +} + + +static void +qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED) +{ + /* nada */ +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_psk_prop_set_loaded(Object *obj, + bool value, + Error **errp) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + if (value) { + qcrypto_tls_creds_psk_load(creds, errp); + } else { + qcrypto_tls_creds_psk_unload(creds); + } +} + + +#ifdef CONFIG_GNUTLS + + +static bool +qcrypto_tls_creds_psk_prop_get_loaded(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + return creds->data.server != NULL; + } else { + return creds->data.client != NULL; + } +} + + +#else /* ! CONFIG_GNUTLS */ + + +static bool +qcrypto_tls_creds_psk_prop_get_loaded(Object *obj G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return false; +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_psk_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), true, "loaded", errp); +} + + +static void +qcrypto_tls_creds_psk_finalize(Object *obj) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + qcrypto_tls_creds_psk_unload(creds); +} + +static void +qcrypto_tls_creds_psk_prop_set_username(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + creds->username = g_strdup(value); +} + + +static char * +qcrypto_tls_creds_psk_prop_get_username(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + return g_strdup(creds->username); +} + +static void +qcrypto_tls_creds_psk_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = qcrypto_tls_creds_psk_complete; + + object_class_property_add_bool(oc, "loaded", + qcrypto_tls_creds_psk_prop_get_loaded, + qcrypto_tls_creds_psk_prop_set_loaded, + NULL); + object_class_property_add_str(oc, "username", + qcrypto_tls_creds_psk_prop_get_username, + qcrypto_tls_creds_psk_prop_set_username, + NULL); +} + + +static const TypeInfo qcrypto_tls_creds_psk_info = { + .parent = TYPE_QCRYPTO_TLS_CREDS, + .name = TYPE_QCRYPTO_TLS_CREDS_PSK, + .instance_size = sizeof(QCryptoTLSCredsPSK), + .instance_finalize = qcrypto_tls_creds_psk_finalize, + .class_size = sizeof(QCryptoTLSCredsPSKClass), + .class_init = qcrypto_tls_creds_psk_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qcrypto_tls_creds_psk_register_types(void) +{ + type_register_static(&qcrypto_tls_creds_psk_info); +} + + +type_init(qcrypto_tls_creds_psk_register_types); diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 96a02deb69..66a6fbe19c 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "crypto/tlssession.h" #include "crypto/tlscredsanon.h" +#include "crypto/tlscredspsk.h" #include "crypto/tlscredsx509.h" #include "qapi/error.h" #include "qemu/acl.h" @@ -88,6 +89,14 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) return session->readFunc(buf, len, session->opaque); } +#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" + +#if GNUTLS_VERSION_MAJOR >= 3 +#define TLS_ECDHE_PSK "+ECDHE-PSK:" +#else +#define TLS_ECDHE_PSK "" +#endif +#define TLS_PRIORITY_ADDITIONAL_PSK TLS_ECDHE_PSK "+DHE-PSK:+PSK" QCryptoTLSSession * qcrypto_tls_session_new(QCryptoTLSCreds *creds, @@ -135,9 +144,12 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, char *prio; if (creds->priority != NULL) { - prio = g_strdup_printf("%s:+ANON-DH", creds->priority); + prio = g_strdup_printf("%s:%s", + creds->priority, + TLS_PRIORITY_ADDITIONAL_ANON); } else { - prio = g_strdup(CONFIG_TLS_PRIORITY ":+ANON-DH"); + prio = g_strdup(CONFIG_TLS_PRIORITY ":" + TLS_PRIORITY_ADDITIONAL_ANON); } ret = gnutls_priority_set_direct(session->handle, prio, NULL); @@ -162,6 +174,42 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, gnutls_strerror(ret)); goto error; } + } else if (object_dynamic_cast(OBJECT(creds), + TYPE_QCRYPTO_TLS_CREDS_PSK)) { + QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds); + char *prio; + + if (creds->priority != NULL) { + prio = g_strdup_printf("%s:%s", + creds->priority, + TLS_PRIORITY_ADDITIONAL_PSK); + } else { + prio = g_strdup(CONFIG_TLS_PRIORITY ":" + TLS_PRIORITY_ADDITIONAL_PSK); + } + + ret = gnutls_priority_set_direct(session->handle, prio, NULL); + if (ret < 0) { + error_setg(errp, "Unable to set TLS session priority %s: %s", + prio, gnutls_strerror(ret)); + g_free(prio); + goto error; + } + g_free(prio); + if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_PSK, + pcreds->data.server); + } else { + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_PSK, + pcreds->data.client); + } + if (ret < 0) { + error_setg(errp, "Cannot set session credentials: %s", + gnutls_strerror(ret)); + goto error; + } } else if (object_dynamic_cast(OBJECT(creds), TYPE_QCRYPTO_TLS_CREDS_X509)) { QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); @@ -353,6 +401,10 @@ qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, TYPE_QCRYPTO_TLS_CREDS_ANON)) { trace_qcrypto_tls_session_check_creds(session, "nop"); return 0; + } else if (object_dynamic_cast(OBJECT(session->creds), + TYPE_QCRYPTO_TLS_CREDS_PSK)) { + trace_qcrypto_tls_session_check_creds(session, "nop"); + return 0; } else if (object_dynamic_cast(OBJECT(session->creds), TYPE_QCRYPTO_TLS_CREDS_X509)) { if (session->creds->verifyPeer) { diff --git a/crypto/trace-events b/crypto/trace-events index e589990359..597389b73c 100644 --- a/crypto/trace-events +++ b/crypto/trace-events @@ -7,6 +7,9 @@ qcrypto_tls_creds_get_path(void *creds, const char *filename, const char *path) # crypto/tlscredsanon.c qcrypto_tls_creds_anon_load(void *creds, const char *dir) "TLS creds anon load creds=%p dir=%s" +# crypto/tlscredspsk.c +qcrypto_tls_creds_psk_load(void *creds, const char *dir) "TLS creds psk load creds=%p dir=%s" + # crypto/tlscredsx509.c qcrypto_tls_creds_x509_load(void *creds, const char *dir) "TLS creds x509 load creds=%p dir=%s" qcrypto_tls_creds_x509_check_basic_constraints(void *creds, const char *file, int status) "TLS creds x509 check basic constraints creds=%p file=%s status=%d" diff --git a/include/crypto/tlscredspsk.h b/include/crypto/tlscredspsk.h new file mode 100644 index 0000000000..306d36c67d --- /dev/null +++ b/include/crypto/tlscredspsk.h @@ -0,0 +1,106 @@ +/* + * QEMU crypto TLS Pre-Shared Key (PSK) support + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#ifndef QCRYPTO_TLSCREDSPSK_H +#define QCRYPTO_TLSCREDSPSK_H + +#include "crypto/tlscreds.h" + +#define TYPE_QCRYPTO_TLS_CREDS_PSK "tls-creds-psk" +#define QCRYPTO_TLS_CREDS_PSK(obj) \ + OBJECT_CHECK(QCryptoTLSCredsPSK, (obj), TYPE_QCRYPTO_TLS_CREDS_PSK) + +typedef struct QCryptoTLSCredsPSK QCryptoTLSCredsPSK; +typedef struct QCryptoTLSCredsPSKClass QCryptoTLSCredsPSKClass; + +#define QCRYPTO_TLS_CREDS_PSKFILE "keys.psk" + +/** + * QCryptoTLSCredsPSK: + * + * The QCryptoTLSCredsPSK object provides a representation + * of the Pre-Shared Key credential used to perform a TLS handshake. + * + * This is a user creatable object, which can be instantiated + * via object_new_propv(): + * + * + * Creating TLS-PSK credential objects in code + * + * Object *obj; + * Error *err = NULL; + * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_PSK, + * "tlscreds0", + * &err, + * "dir", "/path/to/dir", + * "endpoint", "client", + * NULL); + * + * + * + * Or via QMP: + * + * + * Creating TLS-PSK credential objects via QMP + * + * { + * "execute": "object-add", "arguments": { + * "id": "tlscreds0", + * "qom-type": "tls-creds-psk", + * "props": { + * "dir": "/path/to/dir", + * "endpoint": "client" + * } + * } + * } + * + * + * + * Or via the CLI: + * + * + * Creating TLS-PSK credential objects via CLI + * + * qemu-system-x86_64 --object tls-creds-psk,id=tlscreds0,\ + * endpoint=client,dir=/path/to/dir[,username=qemu] + * + * + * + * The PSK file can be created and managed using psktool. + */ + +struct QCryptoTLSCredsPSK { + QCryptoTLSCreds parent_obj; + char *username; +#ifdef CONFIG_GNUTLS + union { + gnutls_psk_server_credentials_t server; + gnutls_psk_client_credentials_t client; + } data; +#endif +}; + + +struct QCryptoTLSCredsPSKClass { + QCryptoTLSCredsClass parent_class; +}; + + +#endif /* QCRYPTO_TLSCREDSPSK_H */ diff --git a/qemu-doc.texi b/qemu-doc.texi index 1cb3ba4341..d3924e928e 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -1262,6 +1262,7 @@ The recommendation is for the server to keep its certificates in either * tls_generate_server:: * tls_generate_client:: * tls_creds_setup:: +* tls_psk:: @end menu @node tls_generate_ca @subsection Setup the Certificate Authority @@ -1510,6 +1511,42 @@ example with VNC: $QEMU -vnc 0.0.0.0:0,tls-creds=tls0 @end example +@node tls_psk +@subsection TLS Pre-Shared Keys (PSK) + +Instead of using certificates, you may also use TLS Pre-Shared Keys +(TLS-PSK). This can be simpler to set up than certificates but is +less scalable. + +Use the GnuTLS @code{psktool} program to generate a @code{keys.psk} +file containing one or more usernames and random keys: + +@example +mkdir -m 0700 /tmp/keys +psktool -u rich -p /tmp/keys/keys.psk +@end example + +TLS-enabled servers such as qemu-nbd can use this directory like so: + +@example +qemu-nbd \ + -t -x / \ + --object tls-creds-psk,id=tls0,endpoint=server,dir=/tmp/keys \ + --tls-creds tls0 \ + image.qcow2 +@end example + +When connecting from a qemu-based client you must specify the +directory containing @code{keys.psk} and an optional @var{username} +(defaults to ``qemu''): + +@example +qemu-img info \ + --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=rich,endpoint=client \ + --image-opts \ + file.driver=nbd,file.host=localhost,file.port=10809,file.tls-creds=tls0,file.export=/ +@end example + @node gdb_usage @section GDB usage diff --git a/qemu-options.hx b/qemu-options.hx index 81b1e99d58..16208f63f2 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4123,6 +4123,30 @@ expensive operation that consumes random pool entropy, so it is recommended that a persistent set of parameters be generated upfront and saved. +@item -object tls-creds-psk,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/keys/dir}[,username=@var{username}] + +Creates a TLS Pre-Shared Keys (PSK) credentials object, which can be used to provide +TLS support on network backends. The @option{id} parameter is a unique +ID which network backends will use to access the credentials. The +@option{endpoint} is either @option{server} or @option{client} depending +on whether the QEMU network backend that uses the credentials will be +acting as a client or as a server. For clients only, @option{username} +is the username which will be sent to the server. If omitted +it defaults to ``qemu''. + +The @var{dir} parameter tells QEMU where to find the keys file. +It is called ``@var{dir}/keys.psk'' and contains ``username:key'' +pairs. This file can most easily be created using the GnuTLS +@code{psktool} program. + +For server endpoints, @var{dir} may also contain a file +@var{dh-params.pem} providing diffie-hellman parameters to use +for the TLS server. If the file is missing, QEMU will generate +a set of DH parameters at startup. This is a computationally +expensive operation that consumes random pool entropy, so it is +recommended that a persistent set of parameters be generated +up front and saved. + @item -object tls-creds-x509,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},priority=@var{priority},verify-peer=@var{on|off},passwordid=@var{id} Creates a TLS anonymous credentials object, which can be used to provide diff --git a/tests/Makefile.include b/tests/Makefile.include index e8bb2d8f66..8859e88ffb 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -723,7 +723,9 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \ tests/test-crypto-tlssession.o-cflags := $(TASN1_CFLAGS) tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ - tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) + tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \ + tests/crypto-tls-psk-helpers.o \ + $(test-crypto-obj-y) tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \ tests/socket-helpers.o $(test-util-obj-y) tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) diff --git a/tests/crypto-tls-psk-helpers.c b/tests/crypto-tls-psk-helpers.c new file mode 100644 index 0000000000..a8395477c3 --- /dev/null +++ b/tests/crypto-tls-psk-helpers.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Richard W.M. Jones + */ + +#include "qemu/osdep.h" + +/* Include this first because it defines QCRYPTO_HAVE_TLS_TEST_SUPPORT */ +#include "crypto-tls-x509-helpers.h" + +#include "crypto-tls-psk-helpers.h" +#include "qemu/sockets.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +void test_tls_psk_init(const char *pskfile) +{ + FILE *fp; + + fp = fopen(pskfile, "w"); + if (fp == NULL) { + g_critical("Failed to create pskfile %s", pskfile); + abort(); + } + /* Don't do this in real applications! Use psktool. */ + fprintf(fp, "qemu:009d5638c40fde0c\n"); + fclose(fp); +} + +void test_tls_psk_cleanup(const char *pskfile) +{ + unlink(pskfile); +} + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/crypto-tls-psk-helpers.h b/tests/crypto-tls-psk-helpers.h new file mode 100644 index 0000000000..9aec29f1a0 --- /dev/null +++ b/tests/crypto-tls-psk-helpers.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Richard W.M. Jones + */ + +#include + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT +# include "qemu-common.h" + +void test_tls_psk_init(const char *keyfile); +void test_tls_psk_cleanup(const char *keyfile); + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index 82f21c27f2..7bd811796e 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -21,7 +21,9 @@ #include "qemu/osdep.h" #include "crypto-tls-x509-helpers.h" +#include "crypto-tls-psk-helpers.h" #include "crypto/tlscredsx509.h" +#include "crypto/tlscredspsk.h" #include "crypto/tlssession.h" #include "qom/object_interfaces.h" #include "qapi/error.h" @@ -31,20 +33,9 @@ #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT #define WORKDIR "tests/test-crypto-tlssession-work/" +#define PSKFILE WORKDIR "keys.psk" #define KEYFILE WORKDIR "key-ctx.pem" -struct QCryptoTLSSessionTestData { - const char *servercacrt; - const char *clientcacrt; - const char *servercrt; - const char *clientcrt; - bool expectServerFail; - bool expectClientFail; - const char *hostname; - const char *const *wildcards; -}; - - static ssize_t testWrite(const char *buf, size_t len, void *opaque) { int *fd = opaque; @@ -59,9 +50,150 @@ static ssize_t testRead(char *buf, size_t len, void *opaque) return read(*fd, buf, len); } -static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, - const char *certdir, - Error **errp) +static QCryptoTLSCreds *test_tls_creds_psk_create( + QCryptoTLSCredsEndpoint endpoint, + const char *dir, + Error **errp) +{ + Error *err = NULL; + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_PSK, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + &err, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", dir, + "priority", "NORMAL", + NULL + ); + + if (err) { + error_propagate(errp, err); + return NULL; + } + return QCRYPTO_TLS_CREDS(creds); +} + + +static void test_crypto_tls_session_psk(void) +{ + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QCryptoTLSSession *clientSess = NULL; + QCryptoTLSSession *serverSess = NULL; + int channel[2]; + bool clientShake = false; + bool serverShake = false; + Error *err = NULL; + int ret; + + /* We'll use this for our fake client-server connection */ + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); + g_assert(ret == 0); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + qemu_set_nonblock(channel[0]); + qemu_set_nonblock(channel[1]); + + clientCreds = test_tls_creds_psk_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + WORKDIR, + &err); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_psk_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + WORKDIR, + &err); + g_assert(serverCreds != NULL); + + /* Now the real part of the test, setup the sessions */ + clientSess = qcrypto_tls_session_new( + clientCreds, NULL, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err); + serverSess = qcrypto_tls_session_new( + serverCreds, NULL, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err); + + g_assert(clientSess != NULL); + g_assert(serverSess != NULL); + + /* For handshake to work, we need to set the I/O callbacks + * to read/write over the socketpair + */ + qcrypto_tls_session_set_callbacks(serverSess, + testWrite, testRead, + &channel[0]); + qcrypto_tls_session_set_callbacks(clientSess, + testWrite, testRead, + &channel[1]); + + /* + * Finally we loop around & around doing handshake on each + * session until we get an error, or the handshake completes. + * This relies on the socketpair being nonblocking to avoid + * deadlocking ourselves upon handshake + */ + do { + int rv; + if (!serverShake) { + rv = qcrypto_tls_session_handshake(serverSess, + &err); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(serverSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + serverShake = true; + } + } + if (!clientShake) { + rv = qcrypto_tls_session_handshake(clientSess, + &err); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(clientSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + clientShake = true; + } + } + } while (!clientShake && !serverShake); + + + /* Finally make sure the server & client validation is successful. */ + g_assert(qcrypto_tls_session_check_credentials(serverSess, &err) == 0); + g_assert(qcrypto_tls_session_check_credentials(clientSess, &err) == 0); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + + qcrypto_tls_session_free(serverSess); + qcrypto_tls_session_free(clientSess); + + close(channel[0]); + close(channel[1]); +} + + +struct QCryptoTLSSessionTestData { + const char *servercacrt; + const char *clientcacrt; + const char *servercrt; + const char *clientcrt; + bool expectServerFail; + bool expectClientFail; + const char *hostname; + const char *const *wildcards; +}; + +static QCryptoTLSCreds *test_tls_creds_x509_create( + QCryptoTLSCredsEndpoint endpoint, + const char *certdir, + Error **errp) { Error *err = NULL; Object *parent = object_get_objects_root(); @@ -104,7 +236,7 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, * initiate a TLS session across them. Finally do * do actual cert validation tests */ -static void test_crypto_tls_session(const void *opaque) +static void test_crypto_tls_session_x509(const void *opaque) { struct QCryptoTLSSessionTestData *data = (struct QCryptoTLSSessionTestData *)opaque; @@ -159,13 +291,13 @@ static void test_crypto_tls_session(const void *opaque) g_assert(link(KEYFILE, CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); - clientCreds = test_tls_creds_create( + clientCreds = test_tls_creds_x509_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, CLIENT_CERT_DIR, &err); g_assert(clientCreds != NULL); - serverCreds = test_tls_creds_create( + serverCreds = test_tls_creds_x509_create( QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, SERVER_CERT_DIR, &err); @@ -285,7 +417,13 @@ int main(int argc, char **argv) mkdir(WORKDIR, 0700); test_tls_init(KEYFILE); + test_tls_psk_init(PSKFILE); + /* Simple initial test using Pre-Shared Keys. */ + g_test_add_func("/qcrypto/tlssession/psk", + test_crypto_tls_session_psk); + + /* More complex tests using X.509 certificates. */ # define TEST_SESS_REG(name, caCrt, \ serverCrt, clientCrt, \ expectServerFail, expectClientFail, \ @@ -296,7 +434,7 @@ int main(int argc, char **argv) hostname, wildcards \ }; \ g_test_add_data_func("/qcrypto/tlssession/" # name, \ - &name, test_crypto_tls_session); \ + &name, test_crypto_tls_session_x509); \ # define TEST_SESS_REG_EXT(name, serverCaCrt, clientCaCrt, \ @@ -309,7 +447,7 @@ int main(int argc, char **argv) hostname, wildcards \ }; \ g_test_add_data_func("/qcrypto/tlssession/" # name, \ - &name, test_crypto_tls_session); \ + &name, test_crypto_tls_session_x509); \ /* A perfect CA, perfect client & perfect server */ @@ -518,6 +656,7 @@ int main(int argc, char **argv) test_tls_discard_cert(&clientcertlevel2breq); unlink(WORKDIR "cacertchain-sess.pem"); + test_tls_psk_cleanup(PSKFILE); test_tls_cleanup(KEYFILE); rmdir(WORKDIR); From f0655423ca72797fc12763c6a46b69d3eae90a1c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 22 May 2018 23:14:02 -0700 Subject: [PATCH 1995/2380] target/openrisc: Reorg tlb lookup While openrisc has a split i/d tlb, qemu does not. Perform a lookup on both i & d tlbs in parallel and put the composite rights into qemu's tlb. This avoids ping-ponging the qemu tlb between EXEC and READ. Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/cpu.h | 8 -- target/openrisc/mmu.c | 254 +++++++++++++++--------------------------- 2 files changed, 90 insertions(+), 172 deletions(-) diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index b180e30e9e..f1b31bc24a 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -237,14 +237,6 @@ enum { UXE = (1 << 7), }; -/* check if tlb available */ -enum { - TLBRET_INVALID = -3, - TLBRET_NOMATCH = -2, - TLBRET_BADADDR = -1, - TLBRET_MATCH = 0 -}; - typedef struct OpenRISCTLBEntry { uint32_t mr; uint32_t tr; diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index f4c0a3e217..e7d5219e11 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -29,148 +29,78 @@ #endif #ifndef CONFIG_USER_ONLY -static inline int get_phys_nommu(hwaddr *physical, int *prot, - target_ulong address) +static inline void get_phys_nommu(hwaddr *phys_addr, int *prot, + target_ulong address) { - *physical = address; + *phys_addr = address; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return TLBRET_MATCH; } -static int get_phys_code(OpenRISCCPU *cpu, hwaddr *physical, int *prot, - target_ulong address, int rw, bool supervisor) +static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot, + target_ulong addr, int need, bool super) { - int vpn = address >> TARGET_PAGE_BITS; - int idx = vpn & TLB_MASK; - int right = 0; - uint32_t mr = cpu->env.tlb.itlb[idx].mr; - uint32_t tr = cpu->env.tlb.itlb[idx].tr; + int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK; + uint32_t imr = cpu->env.tlb.itlb[idx].mr; + uint32_t itr = cpu->env.tlb.itlb[idx].tr; + uint32_t dmr = cpu->env.tlb.dtlb[idx].mr; + uint32_t dtr = cpu->env.tlb.dtlb[idx].tr; + int right, match, valid; - if ((mr >> TARGET_PAGE_BITS) != vpn) { - return TLBRET_NOMATCH; - } - if (!(mr & 1)) { - return TLBRET_INVALID; - } - if (supervisor) { - if (tr & SXE) { - right |= PAGE_EXEC; - } - } else { - if (tr & UXE) { - right |= PAGE_EXEC; + /* If the ITLB and DTLB indexes map to the same page, we want to + load all permissions all at once. If the destination pages do + not match, zap the one we don't need. */ + if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) { + if (need & PAGE_EXEC) { + dmr = dtr = 0; + } else { + imr = itr = 0; } } - if ((rw & 2) && ((right & PAGE_EXEC) == 0)) { - return TLBRET_BADADDR; - } - *physical = (tr & TARGET_PAGE_MASK) | (address & ~TARGET_PAGE_MASK); + /* Check if either of the entries matches the source address. */ + match = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC; + match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE; + + /* Check if either of the entries is valid. */ + valid = imr & 1 ? PAGE_EXEC : 0; + valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0; + valid &= match; + + /* Collect the permissions from the entries. */ + right = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0; + right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0; + right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0; + right &= valid; + + /* Note that above we validated that itr and dtr match on page. + So oring them together changes nothing without having to + check which one we needed. We also want to store to these + variables even on failure, as it avoids compiler warnings. */ + *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK); *prot = right; - return TLBRET_MATCH; -} -static int get_phys_data(OpenRISCCPU *cpu, hwaddr *physical, int *prot, - target_ulong address, int rw, bool supervisor) -{ - int vpn = address >> TARGET_PAGE_BITS; - int idx = vpn & TLB_MASK; - int right = 0; - uint32_t mr = cpu->env.tlb.dtlb[idx].mr; - uint32_t tr = cpu->env.tlb.dtlb[idx].tr; + qemu_log_mask(CPU_LOG_MMU, + "MMU lookup: need %d match %d valid %d right %d -> %s\n", + need, match, valid, right, (need & right) ? "OK" : "FAIL"); - if ((mr >> TARGET_PAGE_BITS) != vpn) { - return TLBRET_NOMATCH; + /* Check the collective permissions are present. */ + if (likely(need & right)) { + return 0; /* success! */ } - if (!(mr & 1)) { - return TLBRET_INVALID; - } - if (supervisor) { - if (tr & SRE) { - right |= PAGE_READ; - } - if (tr & SWE) { - right |= PAGE_WRITE; - } + + /* Determine what kind of failure we have. */ + if (need & valid) { + return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF; } else { - if (tr & URE) { - right |= PAGE_READ; - } - if (tr & UWE) { - right |= PAGE_WRITE; - } + return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS; } - - if (!(rw & 1) && ((right & PAGE_READ) == 0)) { - return TLBRET_BADADDR; - } - if ((rw & 1) && ((right & PAGE_WRITE) == 0)) { - return TLBRET_BADADDR; - } - - *physical = (tr & TARGET_PAGE_MASK) | (address & ~TARGET_PAGE_MASK); - *prot = right; - return TLBRET_MATCH; -} - -static int get_phys_addr(OpenRISCCPU *cpu, hwaddr *physical, - int *prot, target_ulong address, int rw) -{ - bool supervisor = (cpu->env.sr & SR_SM) != 0; - int ret; - - /* Assume nommu results for a moment. */ - ret = get_phys_nommu(physical, prot, address); - - /* Overwrite with TLB lookup if enabled. */ - if (rw == MMU_INST_FETCH) { - if (cpu->env.sr & SR_IME) { - ret = get_phys_code(cpu, physical, prot, address, rw, supervisor); - } - } else { - if (cpu->env.sr & SR_DME) { - ret = get_phys_data(cpu, physical, prot, address, rw, supervisor); - } - } - - return ret; } #endif -static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu, - target_ulong address, - int rw, int tlb_error) +static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address, + int exception) { CPUState *cs = CPU(cpu); - int exception = 0; - - switch (tlb_error) { - default: - if (rw == 2) { - exception = EXCP_IPF; - } else { - exception = EXCP_DPF; - } - break; -#ifndef CONFIG_USER_ONLY - case TLBRET_BADADDR: - if (rw == 2) { - exception = EXCP_IPF; - } else { - exception = EXCP_DPF; - } - break; - case TLBRET_INVALID: - case TLBRET_NOMATCH: - /* No TLB match for a mapped address */ - if (rw == 2) { - exception = EXCP_ITLBMISS; - } else { - exception = EXCP_DTLBMISS; - } - break; -#endif - } cs->exception_index = exception; cpu->env.eear = address; @@ -182,7 +112,7 @@ int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, { #ifdef CONFIG_USER_ONLY OpenRISCCPU *cpu = OPENRISC_CPU(cs); - cpu_openrisc_raise_mmu_exception(cpu, address, rw, 0); + raise_mmu_exception(cpu, address, EXCP_DPF); return 1; #else g_assert_not_reached(); @@ -193,27 +123,32 @@ int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); + int prot, excp, sr = cpu->env.sr; hwaddr phys_addr; - int prot; - int miss; - /* Check memory for any kind of address, since during debug the - gdb can ask for anything, check data tlb for address */ - miss = get_phys_addr(cpu, &phys_addr, &prot, addr, 0); + switch (sr & (SR_DME | SR_IME)) { + case SR_DME | SR_IME: + /* The mmu is definitely enabled. */ + excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, + PAGE_EXEC | PAGE_READ | PAGE_WRITE, + (sr & SR_SM) != 0); + return excp ? -1 : phys_addr; - /* Check instruction tlb */ - if (miss) { - miss = get_phys_addr(cpu, &phys_addr, &prot, addr, MMU_INST_FETCH); - } + default: + /* The mmu is partially enabled, and we don't really have + a "real" access type. Begin by trying the mmu, but if + that fails try again without. */ + excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, + PAGE_EXEC | PAGE_READ | PAGE_WRITE, + (sr & SR_SM) != 0); + if (!excp) { + return phys_addr; + } + /* fallthru */ - /* Last, fall back to a plain address */ - if (miss) { - miss = get_phys_nommu(&phys_addr, &prot, addr); - } - - if (miss) { - return -1; - } else { + case 0: + /* The mmu is definitely disabled; lookups never fail. */ + get_phys_nommu(&phys_addr, &prot, addr); return phys_addr; } } @@ -222,37 +157,28 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); - int ret, prot = 0; - hwaddr physical = 0; + int prot, excp; + hwaddr phys_addr; if (mmu_idx == MMU_NOMMU_IDX) { - ret = get_phys_nommu(&physical, &prot, addr); + /* The mmu is disabled; lookups never fail. */ + get_phys_nommu(&phys_addr, &prot, addr); + excp = 0; } else { bool super = mmu_idx == MMU_SUPERVISOR_IDX; - if (access_type == MMU_INST_FETCH) { - ret = get_phys_code(cpu, &physical, &prot, addr, 2, super); - } else { - ret = get_phys_data(cpu, &physical, &prot, addr, - access_type == MMU_DATA_STORE, super); - } + int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC + : access_type == MMU_DATA_STORE ? PAGE_WRITE + : PAGE_READ); + excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super); } - if (ret == TLBRET_MATCH) { - tlb_set_page(cs, addr & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot, - mmu_idx, TARGET_PAGE_SIZE); - } else if (ret < 0) { - int rw; - if (access_type == MMU_INST_FETCH) { - rw = 2; - } else if (access_type == MMU_DATA_STORE) { - rw = 1; - } else { - rw = 0; - } - cpu_openrisc_raise_mmu_exception(cpu, addr, rw, ret); - /* Raise Exception. */ + if (unlikely(excp)) { + raise_mmu_exception(cpu, addr, excp); cpu_loop_exit_restore(cs, retaddr); } + + tlb_set_page(cs, addr & TARGET_PAGE_MASK, + phys_addr & TARGET_PAGE_MASK, prot, + mmu_idx, TARGET_PAGE_SIZE); } #endif From ecbed7280c832220d9ac8ea7c0b6163393e3cb1a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 May 2018 08:41:53 -0500 Subject: [PATCH 1996/2380] target/openrisc: Add support in scripts/qemu-binfmt-conf.sh Reviewed-by: Laurent Vivier Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- scripts/qemu-binfmt-conf.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index d7eefda0b8..a5cb96d79a 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -1,10 +1,10 @@ #!/bin/sh -# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390/HPPA/Xtensa/microblaze -# program execution by the kernel +# Enable automatic program execution by the kernel. qemu_target_list="i386 i486 alpha arm armeb sparc32plus ppc ppc64 ppc64le m68k \ mips mipsel mipsn32 mipsn32el mips64 mips64el \ -sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb microblaze microblazeel" +sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb \ +microblaze microblazeel or1k" i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00' i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' @@ -124,6 +124,10 @@ microblazeel_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\ microblazeel_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' microblazeel_family=microblazeel +or1k_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x5c' +or1k_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +or1k_family=or1k + qemu_get_family() { cpu=${HOST_ARCH:-$(uname -m)} case "$cpu" in From e8f29049b1e904068ce874e6a40b6ad18b26e121 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 May 2018 14:02:17 -0500 Subject: [PATCH 1997/2380] linux-user: Implement signals for openrisc All of the existing code was boilerplate from elsewhere, and would crash the guest upon the first signal. Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- v2: Add a comment to the new definition of target_pt_regs. Install the signal mask into the ucontext. v3: Incorporate feedback from Laurent. --- linux-user/openrisc/signal.c | 213 +++++++++++---------------- linux-user/openrisc/target_syscall.h | 28 +--- linux-user/signal.c | 2 +- target/openrisc/cpu.c | 1 + 4 files changed, 92 insertions(+), 152 deletions(-) diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c index 8be0b74001..232ad82b98 100644 --- a/linux-user/openrisc/signal.c +++ b/linux-user/openrisc/signal.c @@ -21,124 +21,69 @@ #include "signal-common.h" #include "linux-user/trace.h" -struct target_sigcontext { +typedef struct target_sigcontext { struct target_pt_regs regs; abi_ulong oldmask; - abi_ulong usp; -}; +} target_sigcontext; -struct target_ucontext { +typedef struct target_ucontext { abi_ulong tuc_flags; abi_ulong tuc_link; target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; + target_sigcontext tuc_mcontext; target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; +} target_ucontext; -struct target_rt_sigframe { - abi_ulong pinfo; - uint64_t puc; +typedef struct target_rt_sigframe { struct target_siginfo info; - struct target_sigcontext sc; - struct target_ucontext uc; - unsigned char retcode[16]; /* trampoline code */ -}; + target_ucontext uc; + uint32_t retcode[4]; /* trampoline code */ +} target_rt_sigframe; -/* This is the asm-generic/ucontext.h version */ -#if 0 -static int restore_sigcontext(CPUOpenRISCState *regs, - struct target_sigcontext *sc) +static void restore_sigcontext(CPUOpenRISCState *env, target_sigcontext *sc) { - unsigned int err = 0; - unsigned long old_usp; + int i; + abi_ulong v; - /* Alwys make any pending restarted system call return -EINTR */ - current_thread_info()->restart_block.fn = do_no_restart_syscall; - - /* restore the regs from &sc->regs (same as sc, since regs is first) - * (sc is already checked for VERIFY_READ since the sigframe was - * checked in sys_sigreturn previously) - */ - - if (copy_from_user(regs, &sc, sizeof(struct target_pt_regs))) { - goto badframe; + for (i = 0; i < 32; ++i) { + __get_user(v, &sc->regs.gpr[i]); + cpu_set_gpr(env, i, v); } + __get_user(env->pc, &sc->regs.pc); - /* make sure the U-flag is set so user-mode cannot fool us */ - - regs->sr &= ~SR_SM; - - /* restore the old USP as it was before we stacked the sc etc. - * (we cannot just pop the sigcontext since we aligned the sp and - * stuff after pushing it) - */ - - __get_user(old_usp, &sc->usp); - phx_signal("old_usp 0x%lx", old_usp); - - __PHX__ REALLY /* ??? */ - wrusp(old_usp); - regs->gpr[1] = old_usp; - - /* TODO: the other ports use regs->orig_XX to disable syscall checks - * after this completes, but we don't use that mechanism. maybe we can - * use it now ? - */ - - return err; - -badframe: - return 1; + /* Make sure the supervisor flag is clear. */ + __get_user(v, &sc->regs.sr); + cpu_set_sr(env, v & ~SR_SM); } -#endif /* Set up a signal frame. */ -static void setup_sigcontext(struct target_sigcontext *sc, - CPUOpenRISCState *regs, - unsigned long mask) +static void setup_sigcontext(target_sigcontext *sc, CPUOpenRISCState *env) { - unsigned long usp = cpu_get_gpr(regs, 1); + int i; - /* copy the regs. they are first in sc so we can use sc directly */ + for (i = 0; i < 32; ++i) { + __put_user(cpu_get_gpr(env, i), &sc->regs.gpr[i]); + } - /*copy_to_user(&sc, regs, sizeof(struct target_pt_regs));*/ - - /* Set the frametype to CRIS_FRAME_NORMAL for the execution of - the signal handler. The frametype will be restored to its previous - value in restore_sigcontext. */ - /*regs->frametype = CRIS_FRAME_NORMAL;*/ - - /* then some other stuff */ - __put_user(mask, &sc->oldmask); - __put_user(usp, &sc->usp); -} - -static inline unsigned long align_sigframe(unsigned long sp) -{ - return sp & ~3UL; + __put_user(env->pc, &sc->regs.pc); + __put_user(cpu_get_sr(env), &sc->regs.sr); } static inline abi_ulong get_sigframe(struct target_sigaction *ka, - CPUOpenRISCState *regs, + CPUOpenRISCState *env, size_t frame_size) { - unsigned long sp = get_sp_from_cpustate(regs); - int onsigstack = on_sig_stack(sp); + target_ulong sp = get_sp_from_cpustate(env); - /* redzone */ - sp = target_sigsp(sp, ka); - - sp = align_sigframe(sp - frame_size); - - /* - * If we are on the alternate signal stack and would overflow it, don't. - * Return an always-bogus address instead so we will die with SIGSEGV. + /* Honor redzone now. If we swap to signal stack, no need to waste + * the 128 bytes by subtracting afterward. */ + sp -= 128; - if (onsigstack && !likely(on_sig_stack(sp))) { - return -1L; - } + sp = target_sigsp(sp, ka); + sp -= frame_size; + sp = QEMU_ALIGN_DOWN(sp, 4); return sp; } @@ -147,11 +92,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUOpenRISCState *env) { - int err = 0; abi_ulong frame_addr; - unsigned long return_ip; - struct target_rt_sigframe *frame; - abi_ulong info_addr, uc_addr; + target_rt_sigframe *frame; + int i; frame_addr = get_sigframe(ka, env, sizeof(*frame)); trace_user_setup_rt_frame(env, frame_addr); @@ -159,47 +102,37 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, goto give_sigsegv; } - info_addr = frame_addr + offsetof(struct target_rt_sigframe, info); - __put_user(info_addr, &frame->pinfo); - uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); - __put_user(uc_addr, &frame->puc); - if (ka->sa_flags & SA_SIGINFO) { tswap_siginfo(&frame->info, info); } - /*err |= __clear_user(&frame->uc, offsetof(ucontext_t, uc_mcontext));*/ __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); + target_save_altstack(&frame->uc.tuc_stack, env); - setup_sigcontext(&frame->sc, env, set->sig[0]); - - /*err |= copy_to_user(frame->uc.tuc_sigmask, set, sizeof(*set));*/ - - /* trampoline - the desired return ip is the retcode itself */ - return_ip = (unsigned long)&frame->retcode; - /* This is l.ori r11,r0,__NR_sigreturn, l.sys 1 */ - __put_user(0xa960, (short *)(frame->retcode + 0)); - __put_user(TARGET_NR_rt_sigreturn, (short *)(frame->retcode + 2)); - __put_user(0x20000001, (unsigned long *)(frame->retcode + 4)); - __put_user(0x15000000, (unsigned long *)(frame->retcode + 8)); - - if (err) { - goto give_sigsegv; + setup_sigcontext(&frame->uc.tuc_mcontext, env); + for (i = 0; i < TARGET_NSIG_WORDS; ++i) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } - /* TODO what is the current->exec_domain stuff and invmap ? */ + /* This is l.ori r11,r0,__NR_sigreturn; l.sys 1; l.nop; l.nop */ + __put_user(0xa9600000 | TARGET_NR_rt_sigreturn, frame->retcode + 0); + __put_user(0x20000001, frame->retcode + 1); + __put_user(0x15000000, frame->retcode + 2); + __put_user(0x15000000, frame->retcode + 3); /* Set up registers for signal handler */ - env->pc = (unsigned long)ka->_sa_handler; /* what we enter NOW */ - cpu_set_gpr(env, 9, (unsigned long)return_ip); /* what we enter LATER */ - cpu_set_gpr(env, 3, (unsigned long)sig); /* arg 1: signo */ - cpu_set_gpr(env, 4, (unsigned long)&frame->info); /* arg 2: (siginfo_t*) */ - cpu_set_gpr(env, 5, (unsigned long)&frame->uc); /* arg 3: ucontext */ - - /* actually move the usp to reflect the stacked frame */ - cpu_set_gpr(env, 1, (unsigned long)frame); + cpu_set_gpr(env, 9, frame_addr + offsetof(target_rt_sigframe, retcode)); + cpu_set_gpr(env, 3, sig); + cpu_set_gpr(env, 4, frame_addr + offsetof(target_rt_sigframe, info)); + cpu_set_gpr(env, 5, frame_addr + offsetof(target_rt_sigframe, uc)); + cpu_set_gpr(env, 1, frame_addr); + /* For debugging convenience, set ppc to the insn that faulted. */ + env->ppc = env->pc; + /* When setting the PC for the signal handler, exit delay slot. */ + env->pc = ka->_sa_handler; + env->dflag = 0; return; give_sigsegv: @@ -207,16 +140,34 @@ give_sigsegv: force_sigsegv(sig); } -long do_sigreturn(CPUOpenRISCState *env) -{ - trace_user_do_sigreturn(env, 0); - fprintf(stderr, "do_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - long do_rt_sigreturn(CPUOpenRISCState *env) { + abi_ulong frame_addr = get_sp_from_cpustate(env); + target_rt_sigframe *frame; + sigset_t set; + trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + if (frame_addr & 3) { + goto badframe; + } + + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + if (do_sigaltstack(frame_addr + offsetof(target_rt_sigframe, uc.tuc_stack), + 0, frame_addr) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return cpu_get_gpr(env, 11); + + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; } diff --git a/linux-user/openrisc/target_syscall.h b/linux-user/openrisc/target_syscall.h index 03104f80af..d586d2a018 100644 --- a/linux-user/openrisc/target_syscall.h +++ b/linux-user/openrisc/target_syscall.h @@ -1,27 +1,15 @@ #ifndef OPENRISC_TARGET_SYSCALL_H #define OPENRISC_TARGET_SYSCALL_H +/* Note that in linux/arch/openrisc/include/uapi/asm/ptrace.h, + * this is called user_regs_struct. Given that this is what + * is used within struct sigcontext we need this definition. + * However, elfload.c wants this name. + */ struct target_pt_regs { - union { - struct { - /* Named registers */ - uint32_t sr; /* Stored in place of r0 */ - target_ulong sp; /* r1 */ - }; - struct { - /* Old style */ - target_ulong offset[2]; - target_ulong gprs[30]; - }; - struct { - /* New style */ - target_ulong gpr[32]; - }; - }; - target_ulong pc; - target_ulong orig_gpr11; /* For restarting system calls */ - uint32_t syscallno; /* Syscall number (used by strace) */ - target_ulong dummy; /* Cheap alignment fix */ + abi_ulong gpr[32]; + abi_ulong pc; + abi_ulong sr; }; #define UNAME_MACHINE "openrisc" diff --git a/linux-user/signal.c b/linux-user/signal.c index be2815b45d..602b631b92 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -236,7 +236,7 @@ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) return 0; } -#if !defined(TARGET_OPENRISC) && !defined(TARGET_NIOS2) +#if !defined(TARGET_NIOS2) /* Just set the guest's signal mask to the specified value; the * caller is assumed to have called block_signals() already. */ diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index e01ce9ed1c..fb7cb5c507 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -27,6 +27,7 @@ static void openrisc_cpu_set_pc(CPUState *cs, vaddr value) OpenRISCCPU *cpu = OPENRISC_CPU(cs); cpu->env.pc = value; + cpu->env.dflag = 0; } static bool openrisc_cpu_has_work(CPUState *cs) From 4151e530192996d1f4db1f43e436a21a4c6f707d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 May 2018 21:51:34 -0500 Subject: [PATCH 1998/2380] linux-user: Fix struct sigaltstack for openrisc Reviewed-by: Laurent Vivier Signed-off-by: Richard Henderson Signed-off-by: Stafford Horne --- linux-user/openrisc/target_signal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h index c352a8b333..8283eaf544 100644 --- a/linux-user/openrisc/target_signal.h +++ b/linux-user/openrisc/target_signal.h @@ -5,8 +5,8 @@ typedef struct target_sigaltstack { abi_long ss_sp; + abi_int ss_flags; abi_ulong ss_size; - abi_long ss_flags; } target_stack_t; /* sigaltstack controls */ From 9f6e8afad7b7bd03de6474ea871fcb724630cc0b Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Sun, 1 Jul 2018 14:02:11 +0900 Subject: [PATCH 1999/2380] target/openrisc: Fix delay slot exception flag to match spec The delay slot exception flag is only set on the SR register during exception. Previously it was being set on both the ESR and SR this caused QEMU to differ from the spec. The was apparent as the linux kernel had a bug where it could boot on QEMU but not on real hardware. The fixed logic now matches hardware. Reviewed-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/interrupt.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index 138ad17f00..bbae956361 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -35,13 +35,6 @@ void openrisc_cpu_do_interrupt(CPUState *cs) int exception = cs->exception_index; env->epcr = env->pc; - if (env->dflag) { - env->dflag = 0; - env->sr |= SR_DSX; - env->epcr -= 4; - } else { - env->sr &= ~SR_DSX; - } if (exception == EXCP_SYSCALL) { env->epcr += 4; } @@ -51,7 +44,10 @@ void openrisc_cpu_do_interrupt(CPUState *cs) env->eear = env->pc; } + /* During exceptions esr is populared with the pre-exception sr. */ env->esr = cpu_get_sr(env); + /* In parallel sr is updated to disable mmu, interrupts, timers and + set the delay slot exception flag. */ env->sr &= ~SR_DME; env->sr &= ~SR_IME; env->sr |= SR_SM; @@ -61,6 +57,15 @@ void openrisc_cpu_do_interrupt(CPUState *cs) env->pmr &= ~PMR_SME; env->lock_addr = -1; + /* Set/clear dsx to indicate if we are in a delay slot exception. */ + if (env->dflag) { + env->dflag = 0; + env->sr |= SR_DSX; + env->epcr -= 4; + } else { + env->sr &= ~SR_DSX; + } + if (exception > 0 && exception < EXCP_NR) { static const char * const int_name[EXCP_NR] = { [EXCP_RESET] = "RESET", From dfc84745bbaa0fea2abc8575dd349f6e4bb7edc7 Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Sun, 1 Jul 2018 17:02:54 +0900 Subject: [PATCH 2000/2380] target/openrisc: Fix writes to interrupt mask register The interrupt controller mask register (PICMR) allows writing any value to any of the 32 interrupt mask bits. Writing a 0 masks the interrupt writing a 1 unmasks (enables) the the interrupt. For some reason the old code was or'ing the write values to the PICMR meaning it was not possible to ever mask a interrupt once it was enabled. I have tested this by running linux 4.18 and my regular checks, I don't see any issues. Reported-by: Davidson Francis Reviewed-by: Richard Henderson Signed-off-by: Stafford Horne --- target/openrisc/sys_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 541615bfb3..b66a45c1e0 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -142,7 +142,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) } break; case TO_SPR(9, 0): /* PICMR */ - env->picmr |= rb; + env->picmr = rb; break; case TO_SPR(9, 2): /* PICSR */ env->picsr &= ~rb; From ecfec98cf3a10cacbffc7bb46338464bcdc2b8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Jun 2018 00:46:42 -0300 Subject: [PATCH 2001/2380] linux-user/strace: Improve capget()/capset() output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-By: Guido Günther Reviewed-by: Laurent Vivier Message-Id: <20180628034652.24152-3-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/strace.list | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux-user/strace.list b/linux-user/strace.list index 2bc5ba04d4..afe4db07f3 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -59,10 +59,10 @@ { TARGET_NR_cacheflush, "cacheflush" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_capget -{ TARGET_NR_capget, "capget" , NULL, NULL, NULL }, +{ TARGET_NR_capget, "capget" , "%s(%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_capset -{ TARGET_NR_capset, "capset" , NULL, NULL, NULL }, +{ TARGET_NR_capset, "capset" , "%s(%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_chdir { TARGET_NR_chdir, "chdir" , NULL, print_chdir, NULL }, From 377e93e9e85e76c7fd7af900ba965743243a6618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Jun 2018 00:46:46 -0300 Subject: [PATCH 2002/2380] linux-user/strace: Improve recvmsg() output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-By: Guido Günther Reviewed-by: Laurent Vivier Message-Id: <20180628034652.24152-7-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/strace.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/strace.list b/linux-user/strace.list index afe4db07f3..ff8bb19f5f 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1107,7 +1107,7 @@ { TARGET_NR_recvmmsg, "recvmmsg" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_recvmsg -{ TARGET_NR_recvmsg, "recvmsg" , NULL, NULL, NULL }, +{ TARGET_NR_recvmsg, "recvmsg" , "%s(%d,%p,%#x)", NULL, NULL }, #endif #ifdef TARGET_NR_remap_file_pages { TARGET_NR_remap_file_pages, "remap_file_pages" , NULL, NULL, NULL }, From 65650f0182c11a000d488a3b8e8e6ad071853f31 Mon Sep 17 00:00:00 2001 From: Chen Hanxiao Date: Thu, 14 Jun 2018 16:10:13 +0800 Subject: [PATCH 2003/2380] qga: unset frozen state if no mount points are frozen If we set mountpoints to qmp_guest_fsfreeze_freeze_list, we may got nothing to freeze as all mountpoints are not valid. So call ga_unset_frozen in this senario. Also, if we return 0 frozen fs, there is no need to call guest-fsfreeze-thaw. Cc: Michael Roth Signed-off-by: Chen Hanxiao Signed-off-by: Michael Roth --- qga/commands-posix.c | 6 ++++++ qga/qapi-schema.json | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index eae817191b..594d21ef3e 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1274,6 +1274,12 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, } free_fs_mount_list(&mounts); + /* We may not issue any FIFREEZE here. + * Just unset ga_state here and ready for the next call. + */ + if (i == 0) { + ga_unset_frozen(ga_state); + } return i; error: diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 17884c7c70..1045cef386 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -435,7 +435,9 @@ # for up to 10 seconds by VSS. # # Returns: Number of file systems currently frozen. On error, all filesystems -# will be thawed. +# will be thawed. If no filesystems are frozen as a result of this call, +# then @guest-fsfreeze-status will remain "thawed" and calling +# @guest-fsfreeze-thaw is not necessary. # # Since: 0.15.0 ## From 141b197408ab398c4f474ac1a728ab316e921f2b Mon Sep 17 00:00:00 2001 From: Prasad J Pandit Date: Wed, 13 Jun 2018 11:46:57 +0530 Subject: [PATCH 2004/2380] qga: check bytes count read by guest-file-read While reading file content via 'guest-file-read' command, 'qmp_guest_file_read' routine allocates buffer of count+1 bytes. It could overflow for large values of 'count'. Add check to avoid it. Reported-by: Fakhri Zulkifli Signed-off-by: Prasad J Pandit Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth --- qga/commands-posix.c | 2 +- qga/commands-win32.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 594d21ef3e..9284e71666 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -458,7 +458,7 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, if (!has_count) { count = QGA_READ_COUNT_DEFAULT; - } else if (count < 0) { + } else if (count < 0 || count >= UINT32_MAX) { error_setg(errp, "value '%" PRId64 "' is invalid for argument count", count); return NULL; diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 70ee5379f6..73f31fa8c2 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -318,7 +318,7 @@ GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, } if (!has_count) { count = QGA_READ_COUNT_DEFAULT; - } else if (count < 0) { + } else if (count < 0 || count >= UINT32_MAX) { error_setg(errp, "value '%" PRId64 "' is invalid for argument count", count); return NULL; From 967c885108f18e5065744719f7959ba5ea0a5b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:35 +0200 Subject: [PATCH 2005/2380] qapi: add 'if' to top-level expressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accept 'if' key in top-level elements, accepted as string or list of string type. The following patches will modify the test visitor to check the value is correctly saved, and generate #if/#endif code (as a single #if/endif line or a series for a list). Example of 'if' key: { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, 'if': 'defined(TEST_IF_STRUCT)' } The generated code is for now *unconditional*. Later patches generate the conditionals. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-2-marcandre.lureau@redhat.com> [Commit message and Documentation improved] Signed-off-by: Markus Armbruster --- docs/devel/qapi-code-gen.txt | 29 ++++++++++++++++++++ scripts/qapi/common.py | 35 ++++++++++++++++++++---- tests/Makefile.include | 4 +++ tests/qapi-schema/bad-if-empty-list.err | 1 + tests/qapi-schema/bad-if-empty-list.exit | 1 + tests/qapi-schema/bad-if-empty-list.json | 3 ++ tests/qapi-schema/bad-if-empty-list.out | 0 tests/qapi-schema/bad-if-empty.err | 1 + tests/qapi-schema/bad-if-empty.exit | 1 + tests/qapi-schema/bad-if-empty.json | 3 ++ tests/qapi-schema/bad-if-empty.out | 0 tests/qapi-schema/bad-if-list.err | 1 + tests/qapi-schema/bad-if-list.exit | 1 + tests/qapi-schema/bad-if-list.json | 3 ++ tests/qapi-schema/bad-if-list.out | 0 tests/qapi-schema/bad-if.err | 1 + tests/qapi-schema/bad-if.exit | 1 + tests/qapi-schema/bad-if.json | 3 ++ tests/qapi-schema/bad-if.out | 0 tests/qapi-schema/qapi-schema-test.json | 26 ++++++++++++++++++ tests/qapi-schema/qapi-schema-test.out | 26 ++++++++++++++++++ tests/test-qmp-cmds.c | 12 ++++++++ 22 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 tests/qapi-schema/bad-if-empty-list.err create mode 100644 tests/qapi-schema/bad-if-empty-list.exit create mode 100644 tests/qapi-schema/bad-if-empty-list.json create mode 100644 tests/qapi-schema/bad-if-empty-list.out create mode 100644 tests/qapi-schema/bad-if-empty.err create mode 100644 tests/qapi-schema/bad-if-empty.exit create mode 100644 tests/qapi-schema/bad-if-empty.json create mode 100644 tests/qapi-schema/bad-if-empty.out create mode 100644 tests/qapi-schema/bad-if-list.err create mode 100644 tests/qapi-schema/bad-if-list.exit create mode 100644 tests/qapi-schema/bad-if-list.json create mode 100644 tests/qapi-schema/bad-if-list.out create mode 100644 tests/qapi-schema/bad-if.err create mode 100644 tests/qapi-schema/bad-if.exit create mode 100644 tests/qapi-schema/bad-if.json create mode 100644 tests/qapi-schema/bad-if.out diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 94a7e8f4d0..d3b0990983 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -744,6 +744,35 @@ Example: Red Hat, Inc. controls redhat.com, and may therefore add a downstream command __com.redhat_drive-mirror. +=== Configuring the schema === + +The 'struct', 'enum', 'union', 'alternate', 'command' and 'event' +top-level expressions can take an 'if' key. Its value must be a string +or a list of strings. A string is shorthand for a list containing just +that string. The code generated for the top-level expression will then +be guarded by #if COND for each COND in the list. + +Example: a conditional struct + + { 'struct': 'IfStruct', 'data': { 'foo': 'int' }, + 'if': ['defined(CONFIG_FOO)', 'defined(HAVE_BAR)'] } + +gets its generated code guarded like this: + + #if defined(CONFIG_FOO) + #if defined(HAVE_BAR) + ... generated code ... + #endif /* defined(HAVE_BAR) */ + #endif /* defined(CONFIG_FOO) */ + +Please note that you are responsible to ensure that the C code will +compile with an arbitrary combination of conditions, since the +generators are unable to check it at this point. + +The presence of 'if' keys in the schema is reflected through to the +introspection output depending on the build configuration. + + == Client JSON Protocol introspection == Clients of a Client JSON Protocol commonly need to figure out what diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 8b6708dbf1..991045a478 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -638,6 +638,27 @@ def add_name(name, info, meta, implicit=False): all_names[name] = meta +def check_if(expr, info): + + def check_if_str(ifcond, info): + if not isinstance(ifcond, str): + raise QAPISemError( + info, "'if' condition must be a string or a list of strings") + if ifcond == '': + raise QAPISemError(info, "'if' condition '' makes no sense") + + ifcond = expr.get('if') + if ifcond is None: + return + if isinstance(ifcond, list): + if ifcond == []: + raise QAPISemError(info, "'if' condition [] is useless") + for elt in ifcond: + check_if_str(elt, info) + else: + check_if_str(ifcond, info) + + def check_type(info, source, value, allow_array=False, allow_dict=False, allow_optional=False, allow_metas=[]): @@ -871,6 +892,8 @@ def check_keys(expr_elem, meta, required, optional=[]): raise QAPISemError(info, "'%s' of %s '%s' should only use true value" % (key, meta, name)) + if key == 'if': + check_if(expr, info) for key in required: if key not in expr: raise QAPISemError(info, "Key '%s' is missing from %s '%s'" @@ -899,28 +922,28 @@ def check_exprs(exprs): if 'enum' in expr: meta = 'enum' - check_keys(expr_elem, 'enum', ['data'], ['prefix']) + check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix']) enum_types[expr[meta]] = expr elif 'union' in expr: meta = 'union' check_keys(expr_elem, 'union', ['data'], - ['base', 'discriminator']) + ['base', 'discriminator', 'if']) union_types[expr[meta]] = expr elif 'alternate' in expr: meta = 'alternate' - check_keys(expr_elem, 'alternate', ['data']) + check_keys(expr_elem, 'alternate', ['data'], ['if']) elif 'struct' in expr: meta = 'struct' - check_keys(expr_elem, 'struct', ['data'], ['base']) + check_keys(expr_elem, 'struct', ['data'], ['base', 'if']) struct_types[expr[meta]] = expr elif 'command' in expr: meta = 'command' check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response', - 'boxed', 'allow-oob', 'allow-preconfig']) + 'boxed', 'allow-oob', 'allow-preconfig', 'if']) elif 'event' in expr: meta = 'event' - check_keys(expr_elem, 'event', [], ['data', 'boxed']) + check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if']) else: raise QAPISemError(expr_elem['info'], "Expression is missing metatype") diff --git a/tests/Makefile.include b/tests/Makefile.include index e8bb2d8f66..9faefd7cd2 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -442,6 +442,10 @@ qapi-schema += args-unknown.json qapi-schema += bad-base.json qapi-schema += bad-data.json qapi-schema += bad-ident.json +qapi-schema += bad-if.json +qapi-schema += bad-if-empty.json +qapi-schema += bad-if-empty-list.json +qapi-schema += bad-if-list.json qapi-schema += bad-type-bool.json qapi-schema += bad-type-dict.json qapi-schema += bad-type-int.json diff --git a/tests/qapi-schema/bad-if-empty-list.err b/tests/qapi-schema/bad-if-empty-list.err new file mode 100644 index 0000000000..75fe6497bc --- /dev/null +++ b/tests/qapi-schema/bad-if-empty-list.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if-empty-list.json:2: 'if' condition [] is useless diff --git a/tests/qapi-schema/bad-if-empty-list.exit b/tests/qapi-schema/bad-if-empty-list.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if-empty-list.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if-empty-list.json b/tests/qapi-schema/bad-if-empty-list.json new file mode 100644 index 0000000000..94f2eb8670 --- /dev/null +++ b/tests/qapi-schema/bad-if-empty-list.json @@ -0,0 +1,3 @@ +# check empty 'if' list +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': [] } diff --git a/tests/qapi-schema/bad-if-empty-list.out b/tests/qapi-schema/bad-if-empty-list.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if-empty.err b/tests/qapi-schema/bad-if-empty.err new file mode 100644 index 0000000000..358bdc3e51 --- /dev/null +++ b/tests/qapi-schema/bad-if-empty.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if-empty.json:2: 'if' condition '' makes no sense diff --git a/tests/qapi-schema/bad-if-empty.exit b/tests/qapi-schema/bad-if-empty.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if-empty.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if-empty.json b/tests/qapi-schema/bad-if-empty.json new file mode 100644 index 0000000000..fe1dd4eca6 --- /dev/null +++ b/tests/qapi-schema/bad-if-empty.json @@ -0,0 +1,3 @@ +# check empty 'if' +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': '' } diff --git a/tests/qapi-schema/bad-if-empty.out b/tests/qapi-schema/bad-if-empty.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if-list.err b/tests/qapi-schema/bad-if-list.err new file mode 100644 index 0000000000..0af6316f78 --- /dev/null +++ b/tests/qapi-schema/bad-if-list.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if-list.json:2: 'if' condition '' makes no sense diff --git a/tests/qapi-schema/bad-if-list.exit b/tests/qapi-schema/bad-if-list.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if-list.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if-list.json b/tests/qapi-schema/bad-if-list.json new file mode 100644 index 0000000000..49ced9b9ca --- /dev/null +++ b/tests/qapi-schema/bad-if-list.json @@ -0,0 +1,3 @@ +# check invalid 'if' content +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': ['foo', ''] } diff --git a/tests/qapi-schema/bad-if-list.out b/tests/qapi-schema/bad-if-list.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if.err b/tests/qapi-schema/bad-if.err new file mode 100644 index 0000000000..c2e3f5f44c --- /dev/null +++ b/tests/qapi-schema/bad-if.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if.json:2: 'if' condition must be a string or a list of strings diff --git a/tests/qapi-schema/bad-if.exit b/tests/qapi-schema/bad-if.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if.json b/tests/qapi-schema/bad-if.json new file mode 100644 index 0000000000..3edd1a0bf2 --- /dev/null +++ b/tests/qapi-schema/bad-if.json @@ -0,0 +1,3 @@ +# check invalid 'if' type +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': { 'value': 'defined(TEST_IF_STRUCT)' } } diff --git a/tests/qapi-schema/bad-if.out b/tests/qapi-schema/bad-if.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 7b59817f04..16209b57b3 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -56,6 +56,9 @@ 'data': { 'string0': 'str', 'dict1': 'UserDefTwoDict' } } +{ 'struct': 'UserDefThree', + 'data': { 'string0': 'str' } } + # dummy struct to force generation of array types not otherwise mentioned { 'struct': 'ForceArrays', 'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'], @@ -193,3 +196,26 @@ 'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'], 'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' }, 'returns': '__org.qemu_x-Union1' } + +# test 'if' condition handling + +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': 'defined(TEST_IF_STRUCT)' } + +{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ], + 'if': 'defined(TEST_IF_ENUM)' } + +{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' }, + 'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' } + +{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' }, + 'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' } + +{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' }, + 'returns': 'UserDefThree', + 'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] } + +{ 'command': 'TestCmdReturnDefThree', 'returns': 'UserDefThree' } + +{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' }, + 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 0dbcdafa3c..ed25e5b60c 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -36,6 +36,8 @@ object UserDefTwoDict object UserDefTwo member string0: str optional=False member dict1: UserDefTwoDict optional=False +object UserDefThree + member string0: str optional=False object ForceArrays member unused1: UserDefOneList optional=False member unused2: UserDefTwoList optional=False @@ -233,3 +235,27 @@ object q_obj___org.qemu_x-command-arg member d: __org.qemu_x-Alt optional=False command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 gen=True success_response=True boxed=False oob=False preconfig=False +object TestIfStruct + member foo: int optional=False +enum TestIfEnum ['foo', 'bar'] +object q_obj_TestStruct-wrapper + member data: TestStruct optional=False +enum TestIfUnionKind ['foo'] +object TestIfUnion + member type: TestIfUnionKind optional=False + tag type + case foo: q_obj_TestStruct-wrapper +alternate TestIfAlternate + tag type + case foo: int + case bar: TestStruct +object q_obj_TestIfCmd-arg + member foo: TestIfStruct optional=False +command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree + gen=True success_response=True boxed=False oob=False preconfig=False +command TestCmdReturnDefThree None -> UserDefThree + gen=True success_response=True boxed=False oob=False preconfig=False +object q_obj_TestIfEvent-arg + member foo: TestIfStruct optional=False +event TestIfEvent q_obj_TestIfEvent-arg + boxed=False diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 491b0c4a44..840530b84c 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -12,6 +12,18 @@ static QmpCommandList qmp_commands; +/* #if defined(TEST_IF_STRUCT) && defined(TEST_IF_CMD) */ +UserDefThree *qmp_TestIfCmd(TestIfStruct *foo, Error **errp) +{ + return NULL; +} +/* #endif */ + +UserDefThree *qmp_TestCmdReturnDefThree(Error **errp) +{ + return NULL; +} + void qmp_user_def_cmd(Error **errp) { } From 25b5ff1a860634c2b1463b2882ed02cd55f8ac62 Mon Sep 17 00:00:00 2001 From: Chen Hanxiao Date: Thu, 14 Jun 2018 16:06:06 +0800 Subject: [PATCH 2006/2380] qga: add mountpoint usage info to GuestFilesystemInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for getting the usage of mounted filesystem. The usage of fs stored as used_bytes and total_bytes. It's very useful when we try to monitor guest's filesystem. Cc: Michael Roth Cc: Daniel P. Berrangé Reviewed-by: Eric Blake Signed-off-by: Chen Hanxiao Signed-off-by: Michael Roth --- qga/commands-posix.c | 15 +++++++++++++++ qga/qapi-schema.json | 3 +++ 2 files changed, 18 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 9284e71666..ae8535e497 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -46,6 +46,7 @@ extern char **environ; #include #include #include +#include #ifdef FIFREEZE #define CONFIG_FSFREEZE @@ -1072,6 +1073,8 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, Error **errp) { GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs)); + struct statvfs buf; + unsigned long used, nonroot_total, fr_size; char *devpath = g_strdup_printf("/sys/dev/block/%u:%u", mount->devmajor, mount->devminor); @@ -1079,7 +1082,19 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, fs->type = g_strdup(mount->devtype); build_guest_fsinfo_for_device(devpath, fs, errp); + if (statvfs(fs->mountpoint, &buf) == 0) { + fr_size = buf.f_frsize; + used = buf.f_blocks - buf.f_bfree; + nonroot_total = used + buf.f_bavail; + fs->used_bytes = used * fr_size; + fs->total_bytes = nonroot_total * fr_size; + + fs->has_total_bytes = true; + fs->has_used_bytes = true; + } + g_free(devpath); + return fs; } diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 1045cef386..2df6356b8c 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -848,6 +848,8 @@ # @name: disk name # @mountpoint: mount point path # @type: file system type string +# @used-bytes: file system used bytes (since 3.0) +# @total-bytes: non-root file system total bytes (since 3.0) # @disk: an array of disk hardware information that the volume lies on, # which may be empty if the disk type is not supported # @@ -855,6 +857,7 @@ ## { 'struct': 'GuestFilesystemInfo', 'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str', + '*used-bytes': 'uint64', '*total-bytes': 'uint64', 'disk': ['GuestDiskAddress']} } ## From c07e5e6ef3b5a2737387f86912ac30d1e9d48909 Mon Sep 17 00:00:00 2001 From: Chen Hanxiao Date: Thu, 14 Jun 2018 16:06:07 +0800 Subject: [PATCH 2007/2380] qga-win: add driver path usage to GuestFilesystemInfo This patch adds support for getting the usage of windows driver path. The usage of fs stored as used_bytes and total_bytes. Cc: Michael Roth Signed-off-by: Chen Hanxiao Signed-off-by: Michael Roth --- qga/commands-win32.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 73f31fa8c2..318d760a74 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -670,6 +670,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) char fs_name[32]; char vol_info[MAX_PATH+1]; size_t len; + uint64_t i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes; GuestFilesystemInfo *fs = NULL; GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size); @@ -699,10 +700,21 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) fs_name[sizeof(fs_name) - 1] = 0; fs = g_malloc(sizeof(*fs)); fs->name = g_strdup(guid); + fs->has_total_bytes = false; + fs->has_used_bytes = false; if (len == 0) { fs->mountpoint = g_strdup("System Reserved"); } else { fs->mountpoint = g_strndup(mnt_point, len); + if (GetDiskFreeSpaceEx(fs->mountpoint, + (PULARGE_INTEGER) & i64FreeBytesToCaller, + (PULARGE_INTEGER) & i64TotalBytes, + (PULARGE_INTEGER) & i64FreeBytes)) { + fs->used_bytes = i64TotalBytes - i64FreeBytes; + fs->total_bytes = i64TotalBytes; + fs->has_total_bytes = true; + fs->has_used_bytes = true; + } } fs->type = g_strdup(fs_name); fs->disk = build_guest_disk_info(guid, errp); From 2cbc94376e718448699036be7f6e29ab75312b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:36 +0200 Subject: [PATCH 2008/2380] qapi: pass 'if' condition into QAPISchemaEntity objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Built-in objects remain unconditional. Explicitly defined objects use the condition specified in the schema. Implicitly defined objects inherit their condition from their users. For most of them, there is exactly one user, so the condition to use is obvious. The exception is wrapped types generated for simple union variants, which can be shared by any number of simple unions. The tight condition would be the disjunction of the conditions of these simple unions. For now, use the wrapped type's condition instead. Much simpler and good enough for now. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-3-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 97 ++++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 991045a478..4f4014b387 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1001,8 +1001,16 @@ def check_exprs(exprs): # Schema compiler frontend # +def listify_cond(ifcond): + if not ifcond: + return [] + if not isinstance(ifcond, list): + return [ifcond] + return ifcond + + class QAPISchemaEntity(object): - def __init__(self, name, info, doc): + def __init__(self, name, info, doc, ifcond=None): assert name is None or isinstance(name, str) self.name = name self.module = None @@ -1013,6 +1021,7 @@ class QAPISchemaEntity(object): # such place). self.info = info self.doc = doc + self.ifcond = listify_cond(ifcond) def c_name(self): return c_name(self.name) @@ -1145,8 +1154,8 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): - def __init__(self, name, info, doc, values, prefix): - QAPISchemaType.__init__(self, name, info, doc) + def __init__(self, name, info, doc, ifcond, values, prefix): + QAPISchemaType.__init__(self, name, info, doc, ifcond) for v in values: assert isinstance(v, QAPISchemaMember) v.set_owner(name) @@ -1181,7 +1190,7 @@ class QAPISchemaEnumType(QAPISchemaType): class QAPISchemaArrayType(QAPISchemaType): def __init__(self, name, info, element_type): - QAPISchemaType.__init__(self, name, info, None) + QAPISchemaType.__init__(self, name, info, None, None) assert isinstance(element_type, str) self._element_type_name = element_type self.element_type = None @@ -1189,6 +1198,7 @@ class QAPISchemaArrayType(QAPISchemaType): def check(self, schema): self.element_type = schema.lookup_type(self._element_type_name) assert self.element_type + self.ifcond = self.element_type.ifcond def is_implicit(self): return True @@ -1210,11 +1220,12 @@ class QAPISchemaArrayType(QAPISchemaType): class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, base, local_members, variants): + def __init__(self, name, info, doc, ifcond, + base, local_members, variants): # struct has local_members, optional base, and no variants # flat union has base, variants, and no local_members # simple union has local_members, variants, and no base - QAPISchemaType.__init__(self, name, info, doc) + QAPISchemaType.__init__(self, name, info, doc, ifcond) assert base is None or isinstance(base, str) for m in local_members: assert isinstance(m, QAPISchemaObjectTypeMember) @@ -1410,8 +1421,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): class QAPISchemaAlternateType(QAPISchemaType): - def __init__(self, name, info, doc, variants): - QAPISchemaType.__init__(self, name, info, doc) + def __init__(self, name, info, doc, ifcond, variants): + QAPISchemaType.__init__(self, name, info, doc, ifcond) assert isinstance(variants, QAPISchemaObjectTypeVariants) assert variants.tag_member variants.set_owner(name) @@ -1447,9 +1458,9 @@ class QAPISchemaAlternateType(QAPISchemaType): class QAPISchemaCommand(QAPISchemaEntity): - def __init__(self, name, info, doc, arg_type, ret_type, + def __init__(self, name, info, doc, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): - QAPISchemaEntity.__init__(self, name, info, doc) + QAPISchemaEntity.__init__(self, name, info, doc, ifcond) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) self._arg_type_name = arg_type @@ -1490,8 +1501,8 @@ class QAPISchemaCommand(QAPISchemaEntity): class QAPISchemaEvent(QAPISchemaEntity): - def __init__(self, name, info, doc, arg_type, boxed): - QAPISchemaEntity.__init__(self, name, info, doc) + def __init__(self, name, info, doc, ifcond, arg_type, boxed): + QAPISchemaEntity.__init__(self, name, info, doc, ifcond) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type = None @@ -1590,22 +1601,22 @@ class QAPISchema(object): ('null', 'null', 'QNull' + pointer_suffix)]: self._def_builtin_type(*t) self.the_empty_object_type = QAPISchemaObjectType( - 'q_empty', None, None, None, [], None) + 'q_empty', None, None, None, None, [], None) self._def_entity(self.the_empty_object_type) qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']) - self._def_entity(QAPISchemaEnumType('QType', None, None, + self._def_entity(QAPISchemaEnumType('QType', None, None, None, qtype_values, 'QTYPE')) def _make_enum_members(self, values): return [QAPISchemaMember(v) for v in values] - def _make_implicit_enum_type(self, name, info, values): + def _make_implicit_enum_type(self, name, info, ifcond, values): # See also QAPISchemaObjectTypeMember._pretty_owner() name = name + 'Kind' # Use namespace reserved by add_name() self._def_entity(QAPISchemaEnumType( - name, info, None, self._make_enum_members(values), None)) + name, info, None, ifcond, self._make_enum_members(values), None)) return name def _make_array_type(self, element_type, info): @@ -1614,22 +1625,37 @@ class QAPISchema(object): self._def_entity(QAPISchemaArrayType(name, info, element_type)) return name - def _make_implicit_object_type(self, name, info, doc, role, members): + def _make_implicit_object_type(self, name, info, doc, ifcond, + role, members): if not members: return None # See also QAPISchemaObjectTypeMember._pretty_owner() name = 'q_obj_%s-%s' % (name, role) - if not self.lookup_entity(name, QAPISchemaObjectType): - self._def_entity(QAPISchemaObjectType(name, info, doc, None, - members, None)) + typ = self.lookup_entity(name, QAPISchemaObjectType) + if typ: + # The implicit object type has multiple users. This can + # happen only for simple unions' implicit wrapper types. + # Its ifcond should be the disjunction of its user's + # ifconds. Not implemented. Instead, we always pass the + # wrapped type's ifcond, which is trivially the same for all + # users. It's also necessary for the wrapper to compile. + # But it's not tight: the disjunction need not imply it. We + # may end up compiling useless wrapper types. + # TODO kill simple unions or implement the disjunction + assert ifcond == typ.ifcond + else: + self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, + None, members, None)) return name def _def_enum_type(self, expr, info, doc): name = expr['enum'] data = expr['data'] prefix = expr.get('prefix') + ifcond = expr.get('if') self._def_entity(QAPISchemaEnumType( - name, info, doc, self._make_enum_members(data), prefix)) + name, info, doc, ifcond, + self._make_enum_members(data), prefix)) def _make_member(self, name, typ, info): optional = False @@ -1649,7 +1675,8 @@ class QAPISchema(object): name = expr['struct'] base = expr.get('base') data = expr['data'] - self._def_entity(QAPISchemaObjectType(name, info, doc, base, + ifcond = expr.get('if') + self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base, self._make_members(data, info), None)) @@ -1661,18 +1688,21 @@ class QAPISchema(object): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( - typ, info, None, 'wrapper', [self._make_member('data', typ, info)]) + typ, info, None, self.lookup_type(typ).ifcond, + 'wrapper', [self._make_member('data', typ, info)]) return QAPISchemaObjectTypeVariant(case, typ) def _def_union_type(self, expr, info, doc): name = expr['union'] data = expr['data'] base = expr.get('base') + ifcond = expr.get('if') tag_name = expr.get('discriminator') tag_member = None if isinstance(base, dict): - base = (self._make_implicit_object_type( - name, info, doc, 'base', self._make_members(base, info))) + base = self._make_implicit_object_type( + name, info, doc, ifcond, + 'base', self._make_members(base, info)) if tag_name: variants = [self._make_variant(key, value) for (key, value) in data.items()] @@ -1680,12 +1710,12 @@ class QAPISchema(object): else: variants = [self._make_simple_variant(key, value, info) for (key, value) in data.items()] - typ = self._make_implicit_enum_type(name, info, + typ = self._make_implicit_enum_type(name, info, ifcond, [v.name for v in variants]) tag_member = QAPISchemaObjectTypeMember('type', typ, False) members = [tag_member] self._def_entity( - QAPISchemaObjectType(name, info, doc, base, members, + QAPISchemaObjectType(name, info, doc, ifcond, base, members, QAPISchemaObjectTypeVariants(tag_name, tag_member, variants))) @@ -1693,11 +1723,12 @@ class QAPISchema(object): def _def_alternate_type(self, expr, info, doc): name = expr['alternate'] data = expr['data'] + ifcond = expr.get('if') variants = [self._make_variant(key, value) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) self._def_entity( - QAPISchemaAlternateType(name, info, doc, + QAPISchemaAlternateType(name, info, doc, ifcond, QAPISchemaObjectTypeVariants(None, tag_member, variants))) @@ -1711,13 +1742,14 @@ class QAPISchema(object): boxed = expr.get('boxed', False) allow_oob = expr.get('allow-oob', False) allow_preconfig = expr.get('allow-preconfig', False) + ifcond = expr.get('if') if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, doc, 'arg', self._make_members(data, info)) + name, info, doc, ifcond, 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) - self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, + self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, gen, success_response, boxed, allow_oob, allow_preconfig)) @@ -1725,10 +1757,11 @@ class QAPISchema(object): name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) + ifcond = expr.get('if') if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, doc, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) + name, info, doc, ifcond, 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) def _def_exprs(self, exprs): for expr_elem in exprs: From 4fca21c1b043cb1ef2e197ef15e7474ba668d925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:37 +0200 Subject: [PATCH 2009/2380] qapi: leave the ifcond attribute undefined until check() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We commonly initialize attributes to None in .init(), then set their real value in .check(). Accessing the attribute before .check() yields None. If we're lucky, the code that accesses the attribute prematurely chokes on None. It won't for .ifcond, because None is a legitimate value. Leave the ifcond attribute undefined until check(). Suggested-by: Markus Armbruster Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-4-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 4f4014b387..46e33e23e4 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1021,13 +1021,19 @@ class QAPISchemaEntity(object): # such place). self.info = info self.doc = doc - self.ifcond = listify_cond(ifcond) + self._ifcond = ifcond # self.ifcond is set only after .check() def c_name(self): return c_name(self.name) def check(self, schema): - pass + if isinstance(self._ifcond, QAPISchemaType): + # inherit the condition from a type + typ = self._ifcond + typ.check(schema) + self.ifcond = typ.ifcond + else: + self.ifcond = listify_cond(self._ifcond) def is_implicit(self): return not self.info @@ -1164,6 +1170,7 @@ class QAPISchemaEnumType(QAPISchemaType): self.prefix = prefix def check(self, schema): + QAPISchemaType.check(self, schema) seen = {} for v in self.values: v.check_clash(self.info, seen) @@ -1196,8 +1203,10 @@ class QAPISchemaArrayType(QAPISchemaType): self.element_type = None def check(self, schema): + QAPISchemaType.check(self, schema) self.element_type = schema.lookup_type(self._element_type_name) assert self.element_type + self.element_type.check(schema) self.ifcond = self.element_type.ifcond def is_implicit(self): @@ -1240,6 +1249,7 @@ class QAPISchemaObjectType(QAPISchemaType): self.members = None def check(self, schema): + QAPISchemaType.check(self, schema) if self.members is False: # check for cycles raise QAPISemError(self.info, "Object %s contains itself" % self.name) @@ -1430,6 +1440,7 @@ class QAPISchemaAlternateType(QAPISchemaType): self.variants = variants def check(self, schema): + QAPISchemaType.check(self, schema) self.variants.tag_member.check(schema) # Not calling self.variants.check_clash(), because there's nothing # to clash with @@ -1474,6 +1485,7 @@ class QAPISchemaCommand(QAPISchemaEntity): self.allow_preconfig = allow_preconfig def check(self, schema): + QAPISchemaEntity.check(self, schema) if self._arg_type_name: self.arg_type = schema.lookup_type(self._arg_type_name) assert (isinstance(self.arg_type, QAPISchemaObjectType) or @@ -1509,6 +1521,7 @@ class QAPISchemaEvent(QAPISchemaEntity): self.boxed = boxed def check(self, schema): + QAPISchemaEntity.check(self, schema) if self._arg_type_name: self.arg_type = schema.lookup_type(self._arg_type_name) assert (isinstance(self.arg_type, QAPISchemaObjectType) or @@ -1642,7 +1655,7 @@ class QAPISchema(object): # But it's not tight: the disjunction need not imply it. We # may end up compiling useless wrapper types. # TODO kill simple unions or implement the disjunction - assert ifcond == typ.ifcond + assert ifcond == typ._ifcond # pylint: disable=protected-access else: self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, None, members, None)) @@ -1688,7 +1701,7 @@ class QAPISchema(object): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( - typ, info, None, self.lookup_type(typ).ifcond, + typ, info, None, self.lookup_type(typ), 'wrapper', [self._make_member('data', typ, info)]) return QAPISchemaObjectTypeVariant(case, typ) From fbf09a2fa4d9460033023e56cc1b195be053b353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:38 +0200 Subject: [PATCH 2010/2380] qapi: add 'ifcond' to visitor methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify the test visitor to check correct passing of values. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-5-marcandre.lureau@redhat.com> [Accidental change to roms/seabios dropped] Signed-off-by: Markus Armbruster --- scripts/qapi/commands.py | 2 +- scripts/qapi/common.py | 31 ++++++++++++++------------ scripts/qapi/doc.py | 10 ++++----- scripts/qapi/events.py | 2 +- scripts/qapi/introspect.py | 12 +++++----- scripts/qapi/types.py | 8 +++---- scripts/qapi/visit.py | 8 +++---- tests/qapi-schema/qapi-schema-test.out | 9 ++++++++ tests/qapi-schema/test-qapi.py | 20 ++++++++++++----- 9 files changed, 62 insertions(+), 40 deletions(-) mode change 100644 => 100755 scripts/qapi/doc.py diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 3b0867c14f..dcc03c7859 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -277,7 +277,7 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); c_prefix=c_name(self._prefix, protect=False))) genc.add(gen_registry(self._regy, self._prefix)) - def visit_command(self, name, info, arg_type, ret_type, gen, + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): if not gen: return diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 46e33e23e4..feae646e09 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1062,26 +1062,26 @@ class QAPISchemaVisitor(object): def visit_builtin_type(self, name, info, json_type): pass - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): pass - def visit_array_type(self, name, info, element_type): + def visit_array_type(self, name, info, ifcond, element_type): pass - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): pass - def visit_object_type_flat(self, name, info, members, variants): + def visit_object_type_flat(self, name, info, ifcond, members, variants): pass - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): pass - def visit_command(self, name, info, arg_type, ret_type, gen, + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): pass - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): pass @@ -1191,7 +1191,7 @@ class QAPISchemaEnumType(QAPISchemaType): return 'string' def visit(self, visitor): - visitor.visit_enum_type(self.name, self.info, + visitor.visit_enum_type(self.name, self.info, self.ifcond, self.member_names(), self.prefix) @@ -1225,7 +1225,8 @@ class QAPISchemaArrayType(QAPISchemaType): return 'array of ' + elt_doc_type def visit(self, visitor): - visitor.visit_array_type(self.name, self.info, self.element_type) + visitor.visit_array_type(self.name, self.info, self.ifcond, + self.element_type) class QAPISchemaObjectType(QAPISchemaType): @@ -1307,9 +1308,9 @@ class QAPISchemaObjectType(QAPISchemaType): return 'object' def visit(self, visitor): - visitor.visit_object_type(self.name, self.info, + visitor.visit_object_type(self.name, self.info, self.ifcond, self.base, self.local_members, self.variants) - visitor.visit_object_type_flat(self.name, self.info, + visitor.visit_object_type_flat(self.name, self.info, self.ifcond, self.members, self.variants) @@ -1462,7 +1463,8 @@ class QAPISchemaAlternateType(QAPISchemaType): return 'value' def visit(self, visitor): - visitor.visit_alternate_type(self.name, self.info, self.variants) + visitor.visit_alternate_type(self.name, self.info, self.ifcond, + self.variants) def is_empty(self): return False @@ -1505,7 +1507,7 @@ class QAPISchemaCommand(QAPISchemaEntity): assert isinstance(self.ret_type, QAPISchemaType) def visit(self, visitor): - visitor.visit_command(self.name, self.info, + visitor.visit_command(self.name, self.info, self.ifcond, self.arg_type, self.ret_type, self.gen, self.success_response, self.boxed, self.allow_oob, @@ -1538,7 +1540,8 @@ class QAPISchemaEvent(QAPISchemaEntity): raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") def visit(self, visitor): - visitor.visit_event(self.name, self.info, self.arg_type, self.boxed) + visitor.visit_event(self.name, self.info, self.ifcond, + self.arg_type, self.boxed) class QAPISchema(object): diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py old mode 100644 new mode 100755 index b5630844f9..4db6674dc3 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -204,14 +204,14 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): def write(self, output_dir): self._gen.write(output_dir, self._prefix + 'qapi-doc.texi') - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): doc = self.cur_doc self._gen.add(TYPE_FMT(type='Enum', name=doc.symbol, body=texi_entity(doc, 'Values', member_func=texi_enum_value))) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): doc = self.cur_doc if base and base.is_implicit(): base = None @@ -220,13 +220,13 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): body=texi_entity(doc, 'Members', base, variants))) - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): doc = self.cur_doc self._gen.add(TYPE_FMT(type='Alternate', name=doc.symbol, body=texi_entity(doc, 'Members'))) - def visit_command(self, name, info, arg_type, ret_type, gen, + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): doc = self.cur_doc if boxed: @@ -240,7 +240,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): name=doc.symbol, body=body)) - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): doc = self.cur_doc self._gen.add(MSG_FMT(type='Event', name=doc.symbol, diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 5657524688..0a1afac134 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -184,7 +184,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): genh.add(gen_enum(self._enum_name, self._event_names)) genc.add(gen_enum_lookup(self._enum_name, self._event_names)) - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): self._genh.add(gen_event_send_decl(name, arg_type, boxed)) self._genc.add(gen_event_send(name, arg_type, boxed, self._enum_name)) self._event_names.append(name) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 6ad198ae5b..245cfdfb65 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -149,26 +149,26 @@ const QLitObject %(c_name)s = %(c_string)s; def visit_builtin_type(self, name, info, json_type): self._gen_qlit(name, 'builtin', {'json-type': json_type}) - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): self._gen_qlit(name, 'enum', {'values': values}) - def visit_array_type(self, name, info, element_type): + def visit_array_type(self, name, info, ifcond, element_type): element = self._use_type(element_type) self._gen_qlit('[' + element + ']', 'array', {'element-type': element}) - def visit_object_type_flat(self, name, info, members, variants): + def visit_object_type_flat(self, name, info, ifcond, members, variants): obj = {'members': [self._gen_member(m) for m in members]} if variants: obj.update(self._gen_variants(variants.tag_member.name, variants.variants)) self._gen_qlit(name, 'object', obj) - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): self._gen_qlit(name, 'alternate', {'members': [{'type': self._use_type(m.type)} for m in variants.variants]}) - def visit_command(self, name, info, arg_type, ret_type, gen, + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type @@ -178,7 +178,7 @@ const QLitObject %(c_name)s = %(c_string)s; 'allow-oob': allow_oob, 'allow-preconfig': allow_preconfig}) - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}) diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index a599352e59..659075f884 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -208,16 +208,16 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_type_cleanup_decl(name)) self._genc.add(gen_type_cleanup(name)) - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): self._genh.preamble_add(gen_enum(name, values, prefix)) self._genc.add(gen_enum_lookup(name, values, prefix)) - def visit_array_type(self, name, info, element_type): + def visit_array_type(self, name, info, ifcond, element_type): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_array(name, element_type)) self._gen_type_cleanup(name) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -231,7 +231,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): # implicit types won't be directly allocated/freed self._gen_type_cleanup(name) - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_object(name, None, [variants.tag_member], variants)) diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index bdcafb64ee..34fe1ef5eb 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -310,15 +310,15 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): ''', types=types)) - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): self._genh.add(gen_visit_decl(name, scalar=True)) self._genc.add(gen_visit_enum(name)) - def visit_array_type(self, name, info, element_type): + def visit_array_type(self, name, info, ifcond, element_type): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_list(name, element_type)) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -331,7 +331,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_object(name, base, members, variants)) - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_alternate(name, variants)) diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index ed25e5b60c..0da92455da 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -237,25 +237,34 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Unio gen=True success_response=True boxed=False oob=False preconfig=False object TestIfStruct member foo: int optional=False + if ['defined(TEST_IF_STRUCT)'] enum TestIfEnum ['foo', 'bar'] + if ['defined(TEST_IF_ENUM)'] object q_obj_TestStruct-wrapper member data: TestStruct optional=False enum TestIfUnionKind ['foo'] + if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)'] object TestIfUnion member type: TestIfUnionKind optional=False tag type case foo: q_obj_TestStruct-wrapper + if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)'] alternate TestIfAlternate tag type case foo: int case bar: TestStruct + if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)'] object q_obj_TestIfCmd-arg member foo: TestIfStruct optional=False + if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False + if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] command TestCmdReturnDefThree None -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_TestIfEvent-arg member foo: TestIfStruct optional=False + if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'] event TestIfEvent q_obj_TestIfEvent-arg boxed=False + if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'] diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 4512a41504..f514fe71e4 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -23,12 +23,13 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): def visit_include(self, name, info): print('include %s' % name) - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): print('enum %s %s' % (name, values)) if prefix: print(' prefix %s' % prefix) + self._print_if(ifcond) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): print('object %s' % name) if base: print(' base %s' % base.name) @@ -36,21 +37,25 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print(' member %s: %s optional=%s' % \ (m.name, m.type.name, m.optional)) self._print_variants(variants) + self._print_if(ifcond) - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): print('alternate %s' % name) self._print_variants(variants) + self._print_if(ifcond) - def visit_command(self, name, info, arg_type, ret_type, gen, + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): print('command %s %s -> %s' % \ (name, arg_type and arg_type.name, ret_type and ret_type.name)) print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' % \ (gen, success_response, boxed, allow_oob, allow_preconfig)) + self._print_if(ifcond) - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): print('event %s %s' % (name, arg_type and arg_type.name)) print(' boxed=%s' % boxed) + self._print_if(ifcond) @staticmethod def _print_variants(variants): @@ -59,6 +64,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): for v in variants.variants: print(' case %s: %s' % (v.name, v.type.name)) + @staticmethod + def _print_if(ifcond, indent=4): + if ifcond: + print('%sif %s' % (' ' * indent, ifcond)) + try: schema = QAPISchema(sys.argv[1]) From 485d948ce86f5a096dc848ec31b70cd66452d40d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:39 +0200 Subject: [PATCH 2011/2380] qapi: mcgen() shouldn't indent # lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip preprocessor lines when adding indentation, since that would likely result in invalid code. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-6-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index feae646e09..1b56065a80 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1941,8 +1941,8 @@ def cgen(code, **kwds): if indent_level: indent = genindent(indent_level) # re.subn() lacks flags support before Python 2.7, use re.compile() - raw = re.subn(re.compile(r'^.', re.MULTILINE), - indent + r'\g<0>', raw) + raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE), + indent, raw) raw = raw[0] return re.sub(re.escape(eatspace) + r' *', '', raw) From ded9fc28b5a07213f3e5e8ac7ea0494b85813de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:40 +0200 Subject: [PATCH 2012/2380] qapi: add #if/#endif helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helpers to wrap generated code with #if/#endif lines. A later patch wants to use QAPIGen for generating C snippets rather than full C files with copyright headers etc. Splice in class QAPIGenCCode between QAPIGen and QAPIGenC. Add a 'with' statement context manager that will be used to wrap generator visitor methods. The manager will check if code was generated before adding #if/#endif lines on QAPIGenCSnippet objects. Used in the following patches. Signed-off-by: Marc-André Lureau Message-Id: <20180703155648.11933-7-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 98 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 1b56065a80..9230a2a3e8 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -12,6 +12,7 @@ # See the COPYING file in the top-level directory. from __future__ import print_function +from contextlib import contextmanager import errno import os import re @@ -1974,6 +1975,40 @@ def guardend(name): name=guardname(name)) +def gen_if(ifcond): + ret = '' + for ifc in ifcond: + ret += mcgen(''' +#if %(cond)s +''', cond=ifc) + return ret + + +def gen_endif(ifcond): + ret = '' + for ifc in reversed(ifcond): + ret += mcgen(''' +#endif /* %(cond)s */ +''', cond=ifc) + return ret + + +def _wrap_ifcond(ifcond, before, after): + if before == after: + return after # suppress empty #if ... #endif + + assert after.startswith(before) + out = before + added = after[len(before):] + if added[0] == '\n': + out += '\n' + added = added[1:] + out += gen_if(ifcond) + out += added + out += gen_endif(ifcond) + return out + + def gen_enum_lookup(name, values, prefix=None): ret = mcgen(''' @@ -2071,6 +2106,10 @@ class QAPIGen(object): def add(self, text): self._body += text + def get_content(self, fname=None): + return (self._top(fname) + self._preamble + self._body + + self._bottom(fname)) + def _top(self, fname): return '' @@ -2091,8 +2130,7 @@ class QAPIGen(object): f = open(fd, 'r+', encoding='utf-8') else: f = os.fdopen(fd, 'r+') - text = (self._top(fname) + self._preamble + self._body - + self._bottom(fname)) + text = self.get_content(fname) oldtext = f.read(len(text) + 1) if text != oldtext: f.seek(0) @@ -2101,10 +2139,62 @@ class QAPIGen(object): f.close() -class QAPIGenC(QAPIGen): +@contextmanager +def ifcontext(ifcond, *args): + """A 'with' statement context manager to wrap with start_if()/end_if() + + *args: any number of QAPIGenCCode + + Example:: + + with ifcontext(ifcond, self._genh, self._genc): + modify self._genh and self._genc ... + + Is equivalent to calling:: + + self._genh.start_if(ifcond) + self._genc.start_if(ifcond) + modify self._genh and self._genc ... + self._genh.end_if() + self._genc.end_if() + """ + for arg in args: + arg.start_if(ifcond) + yield + for arg in args: + arg.end_if() + + +class QAPIGenCCode(QAPIGen): + + def __init__(self): + QAPIGen.__init__(self) + self._start_if = None + + def start_if(self, ifcond): + assert self._start_if is None + self._start_if = (ifcond, self._body, self._preamble) + + def end_if(self): + assert self._start_if + self._wrap_ifcond() + self._start_if = None + + def _wrap_ifcond(self): + self._body = _wrap_ifcond(self._start_if[0], + self._start_if[1], self._body) + self._preamble = _wrap_ifcond(self._start_if[0], + self._start_if[2], self._preamble) + + def get_content(self, fname=None): + assert self._start_if is None + return QAPIGen.get_content(self, fname) + + +class QAPIGenC(QAPIGenCCode): def __init__(self, blurb, pydoc): - QAPIGen.__init__(self) + QAPIGenCCode.__init__(self) self._blurb = blurb self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, re.MULTILINE)) From 40bb13766af789ae468a848aec7b45742497e5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:41 +0200 Subject: [PATCH 2013/2380] qapi-introspect: modify to_qlit() to append ',' on level > 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following patch is going to break list entries with #if/#endif, so they should have the trailing ',' as suffix. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-8-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/introspect.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 245cfdfb65..bd7e1219be 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -30,7 +30,7 @@ def to_qlit(obj, level=0, suppress_first_indent=False): for elt in obj] elts.append(indent(level + 1) + "{}") ret += 'QLIT_QLIST(((QLitObject[]) {\n' - ret += ',\n'.join(elts) + '\n' + ret += '\n'.join(elts) + '\n' ret += indent(level) + '}))' elif isinstance(obj, dict): elts = [] @@ -45,6 +45,8 @@ def to_qlit(obj, level=0, suppress_first_indent=False): ret += 'QLIT_QBOOL(%s)' % ('true' if obj else 'false') else: assert False # not implemented + if level > 0: + ret += ',' return ret From d626b6c1ae7c811d0cfd5f8dc042426dcd1bcf90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:42 +0200 Subject: [PATCH 2014/2380] qapi-introspect: add preprocessor conditions to generated QLit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds 'ifcond' conditions to top-level QLit objects. Future work will add them to object and enum type members, i.e. within QLit objects. Extend the QLit generator to_qlit() to accept (@obj, @cond) tuples in addition to just @obj. The tuple causes the QLit generated for objects for @obj with #if/#endif conditions for @cond. See generated tests/test-qmp-introspect.c. Example diff after this patch: --- before 2018-01-08 11:55:24.757083654 +0100 +++ tests/test-qmp-introspect.c 2018-01-08 13:08:44.477641629 +0100 @@ -51,6 +51,8 @@ { "name", QLIT_QSTR("EVENT_F"), }, {} })), +#if defined(TEST_IF_CMD) +#if defined(TEST_IF_STRUCT) QLIT_QDICT(((QLitDictEntry[]) { { "arg-type", QLIT_QSTR("5"), }, { "meta-type", QLIT_QSTR("command"), }, @@ -58,12 +60,16 @@ { "ret-type", QLIT_QSTR("0"), }, {} })), +#endif /* defined(TEST_IF_STRUCT) */ +#endif /* defined(TEST_IF_CMD) */ Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-9-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/introspect.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index bd7e1219be..71d4a779ce 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -18,6 +18,15 @@ def to_qlit(obj, level=0, suppress_first_indent=False): def indent(level): return level * 4 * ' ' + if isinstance(obj, tuple): + ifobj, ifcond = obj + ret = gen_if(ifcond) + ret += to_qlit(ifobj, level) + endif = gen_endif(ifcond) + if endif: + ret += '\n' + endif + return ret + ret = '' if not suppress_first_indent: ret += indent(level) @@ -26,7 +35,7 @@ def to_qlit(obj, level=0, suppress_first_indent=False): elif isinstance(obj, str): ret += 'QLIT_QSTR(' + to_c_string(obj) + ')' elif isinstance(obj, list): - elts = [to_qlit(elt, level + 1) + elts = [to_qlit(elt, level + 1).strip('\n') for elt in obj] elts.append(indent(level + 1) + "{}") ret += 'QLIT_QLIST(((QLitObject[]) {\n' @@ -128,12 +137,12 @@ const QLitObject %(c_name)s = %(c_string)s; return '[' + self._use_type(typ.element_type) + ']' return self._name(typ.name) - def _gen_qlit(self, name, mtype, obj): + def _gen_qlit(self, name, mtype, obj, ifcond): if mtype not in ('command', 'event', 'builtin', 'array'): name = self._name(name) obj['name'] = name obj['meta-type'] = mtype - self._qlits.append(obj) + self._qlits.append((obj, ifcond)) def _gen_member(self, member): ret = {'name': member.name, 'type': self._use_type(member.type)} @@ -149,26 +158,27 @@ const QLitObject %(c_name)s = %(c_string)s; return {'case': variant.name, 'type': self._use_type(variant.type)} def visit_builtin_type(self, name, info, json_type): - self._gen_qlit(name, 'builtin', {'json-type': json_type}) + self._gen_qlit(name, 'builtin', {'json-type': json_type}, []) def visit_enum_type(self, name, info, ifcond, values, prefix): - self._gen_qlit(name, 'enum', {'values': values}) + self._gen_qlit(name, 'enum', {'values': values}, ifcond) def visit_array_type(self, name, info, ifcond, element_type): element = self._use_type(element_type) - self._gen_qlit('[' + element + ']', 'array', {'element-type': element}) + self._gen_qlit('[' + element + ']', 'array', {'element-type': element}, + ifcond) def visit_object_type_flat(self, name, info, ifcond, members, variants): obj = {'members': [self._gen_member(m) for m in members]} if variants: obj.update(self._gen_variants(variants.tag_member.name, variants.variants)) - self._gen_qlit(name, 'object', obj) + self._gen_qlit(name, 'object', obj, ifcond) def visit_alternate_type(self, name, info, ifcond, variants): self._gen_qlit(name, 'alternate', {'members': [{'type': self._use_type(m.type)} - for m in variants.variants]}) + for m in variants.variants]}, ifcond) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): @@ -178,11 +188,12 @@ const QLitObject %(c_name)s = %(c_string)s; {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type), 'allow-oob': allow_oob, - 'allow-preconfig': allow_preconfig}) + 'allow-preconfig': allow_preconfig}, ifcond) def visit_event(self, name, info, ifcond, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type - self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}) + self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}, + ifcond) def gen_introspect(schema, output_dir, prefix, opt_unmask): From 1f7b9f3181ee137101cca66461c47e718b853240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:43 +0200 Subject: [PATCH 2015/2380] qapi/commands: add #if conditions to commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap generated code with #if/#endif using an 'ifcontext' on QAPIGenCSnippet objects. Signed-off-by: Marc-André Lureau Message-Id: <20180703155648.11933-10-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Line breaks tweaked] Signed-off-by: Markus Armbruster --- scripts/qapi/commands.py | 24 ++++++++++++++++-------- tests/test-qmp-cmds.c | 4 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index dcc03c7859..0f3c991918 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -239,7 +239,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-commands', ' * Schema-defined QAPI/QMP commands', __doc__) - self._regy = '' + self._regy = QAPIGenCCode() self._visited_ret_types = {} def _begin_module(self, name): @@ -275,20 +275,28 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); ''', c_prefix=c_name(self._prefix, protect=False))) - genc.add(gen_registry(self._regy, self._prefix)) + genc.add(gen_registry(self._regy.get_content(), self._prefix)) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): if not gen: return - self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) + # FIXME: If T is a user-defined type, the user is responsible + # for making this work, i.e. to make T's condition the + # conjunction of the T-returning commands' conditions. If T + # is a built-in type, this isn't possible: the + # qmp_marshal_output_T() will be generated unconditionally. if ret_type and ret_type not in self._visited_ret_types[self._genc]: self._visited_ret_types[self._genc].add(ret_type) - self._genc.add(gen_marshal_output(ret_type)) - self._genh.add(gen_marshal_decl(name)) - self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) - self._regy += gen_register_command(name, success_response, allow_oob, - allow_preconfig) + with ifcontext(ret_type.ifcond, + self._genh, self._genc, self._regy): + self._genc.add(gen_marshal_output(ret_type)) + with ifcontext(ifcond, self._genh, self._genc, self._regy): + self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) + self._genh.add(gen_marshal_decl(name)) + self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) + self._regy.add(gen_register_command(name, success_response, + allow_oob, allow_preconfig)) def gen_commands(schema, output_dir, prefix): diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 840530b84c..ce277c9c3b 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -12,12 +12,12 @@ static QmpCommandList qmp_commands; -/* #if defined(TEST_IF_STRUCT) && defined(TEST_IF_CMD) */ +#if defined(TEST_IF_STRUCT) && defined(TEST_IF_CMD) UserDefThree *qmp_TestIfCmd(TestIfStruct *foo, Error **errp) { return NULL; } -/* #endif */ +#endif UserDefThree *qmp_TestCmdReturnDefThree(Error **errp) { From c3cd6aa0201c126eda8dc71b60e7aa259a3e79b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:44 +0200 Subject: [PATCH 2016/2380] qapi/events: add #if conditions to events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap generated code with #if/#endif using an 'ifcontext' on QAPIGenCSnippet objects. This makes a conditional event's qapi_event_send_FOO() compile-time conditional, but its enum QAPIEvent member remains unconditional for now. A follow up patch "qapi-event: add 'if' condition to implicit event enum" will improve this. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-11-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/events.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 0a1afac134..764ef177ab 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -185,8 +185,10 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): genc.add(gen_enum_lookup(self._enum_name, self._event_names)) def visit_event(self, name, info, ifcond, arg_type, boxed): - self._genh.add(gen_event_send_decl(name, arg_type, boxed)) - self._genc.add(gen_event_send(name, arg_type, boxed, self._enum_name)) + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_event_send_decl(name, arg_type, boxed)) + self._genc.add(gen_event_send(name, arg_type, boxed, + self._enum_name)) self._event_names.append(name) From 9f88c66211342714b06c051140fd64ffd338dbe1 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 17:56:45 +0200 Subject: [PATCH 2017/2380] qapi-types: add #if conditions to types & visitors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Types & visitors are coupled and must be handled together to avoid temporary build regression. Wrap generated types/visitor code with #if/#endif using the context helpers. Derived from a patch by Marc-André. Signed-off-by: Marc-André Lureau Signed-off-by: Markus Armbruster Message-Id: <20180703155648.11933-12-marcandre.lureau@redhat.com> --- scripts/qapi/types.py | 48 ++++++++++++++++++++++++++----------------- scripts/qapi/visit.py | 33 ++++++++++++++++------------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 659075f884..fd7808103c 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -55,7 +55,7 @@ def gen_struct_members(members): return ret -def gen_object(name, base, members, variants): +def gen_object(name, ifcond, base, members, variants): if name in objects_seen: return '' objects_seen.add(name) @@ -64,11 +64,14 @@ def gen_object(name, base, members, variants): if variants: for v in variants.variants: if isinstance(v.type, QAPISchemaObjectType): - ret += gen_object(v.type.name, v.type.base, + ret += gen_object(v.type.name, v.type.ifcond, v.type.base, v.type.local_members, v.type.variants) ret += mcgen(''' +''') + ret += gen_if(ifcond) + ret += mcgen(''' struct %(c_name)s { ''', c_name=c_name(name)) @@ -101,6 +104,7 @@ struct %(c_name)s { ret += mcgen(''' }; ''') + ret += gen_endif(ifcond) return ret @@ -209,33 +213,39 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genc.add(gen_type_cleanup(name)) def visit_enum_type(self, name, info, ifcond, values, prefix): - self._genh.preamble_add(gen_enum(name, values, prefix)) - self._genc.add(gen_enum_lookup(name, values, prefix)) + with ifcontext(ifcond, self._genh, self._genc): + self._genh.preamble_add(gen_enum(name, values, prefix)) + self._genc.add(gen_enum_lookup(name, values, prefix)) def visit_array_type(self, name, info, ifcond, element_type): - self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_array(name, element_type)) - self._gen_type_cleanup(name) + with ifcontext(ifcond, self._genh, self._genc): + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_array(name, element_type)) + self._gen_type_cleanup(name) def visit_object_type(self, name, info, ifcond, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return - self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_object(name, base, members, variants)) - if base and not base.is_implicit(): - self._genh.add(gen_upcast(name, base)) - # TODO Worth changing the visitor signature, so we could - # directly use rather than repeat type.is_implicit()? - if not name.startswith('q_'): - # implicit types won't be directly allocated/freed - self._gen_type_cleanup(name) + with ifcontext(ifcond, self._genh): + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_object(name, ifcond, base, members, variants)) + with ifcontext(ifcond, self._genh, self._genc): + if base and not base.is_implicit(): + self._genh.add(gen_upcast(name, base)) + # TODO Worth changing the visitor signature, so we could + # directly use rather than repeat type.is_implicit()? + if not name.startswith('q_'): + # implicit types won't be directly allocated/freed + self._gen_type_cleanup(name) def visit_alternate_type(self, name, info, ifcond, variants): - self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_object(name, None, + with ifcontext(ifcond, self._genh): + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_object(name, ifcond, None, [variants.tag_member], variants)) - self._gen_type_cleanup(name) + with ifcontext(ifcond, self._genh, self._genc): + self._gen_type_cleanup(name) def gen_types(schema, output_dir, prefix, opt_builtins): diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 34fe1ef5eb..dd5034a66a 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -311,29 +311,34 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): types=types)) def visit_enum_type(self, name, info, ifcond, values, prefix): - self._genh.add(gen_visit_decl(name, scalar=True)) - self._genc.add(gen_visit_enum(name)) + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_decl(name, scalar=True)) + self._genc.add(gen_visit_enum(name)) def visit_array_type(self, name, info, ifcond, element_type): - self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_list(name, element_type)) + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_list(name, element_type)) def visit_object_type(self, name, info, ifcond, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return - self._genh.add(gen_visit_members_decl(name)) - self._genc.add(gen_visit_object_members(name, base, members, variants)) - # TODO Worth changing the visitor signature, so we could - # directly use rather than repeat type.is_implicit()? - if not name.startswith('q_'): - # only explicit types need an allocating visit - self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_object(name, base, members, variants)) + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_members_decl(name)) + self._genc.add(gen_visit_object_members(name, base, + members, variants)) + # TODO Worth changing the visitor signature, so we could + # directly use rather than repeat type.is_implicit()? + if not name.startswith('q_'): + # only explicit types need an allocating visit + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_object(name, base, members, variants)) def visit_alternate_type(self, name, info, ifcond, variants): - self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_alternate(name, variants)) + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_alternate(name, variants)) def gen_visit(schema, output_dir, prefix, opt_builtins): From 901a34a400a0dd5bf5056b6b9ecce48ab8eb02ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:46 +0200 Subject: [PATCH 2018/2380] qapi: add 'If:' section to generated documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation is generated only once, and doesn't know C pre-conditions. Add 'If:' sections for top-level entities. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-13-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/doc.py | 22 ++++++++++++---------- tests/qapi-schema/doc-good.json | 2 +- tests/qapi-schema/doc-good.out | 1 + tests/qapi-schema/doc-good.texi | 2 ++ 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 4db6674dc3..987fd3c943 100755 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -174,7 +174,7 @@ def texi_members(doc, what, base, variants, member_func): return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items) -def texi_sections(doc): +def texi_sections(doc, ifcond): """Format additional sections following arguments""" body = '' for section in doc.sections: @@ -185,14 +185,16 @@ def texi_sections(doc): body += texi_example(section.text) else: body += texi_format(section.text) + if ifcond: + body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond) return body -def texi_entity(doc, what, base=None, variants=None, +def texi_entity(doc, what, ifcond, base=None, variants=None, member_func=texi_member): return (texi_body(doc) + texi_members(doc, what, base, variants, member_func) - + texi_sections(doc)) + + texi_sections(doc, ifcond)) class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): @@ -208,7 +210,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): doc = self.cur_doc self._gen.add(TYPE_FMT(type='Enum', name=doc.symbol, - body=texi_entity(doc, 'Values', + body=texi_entity(doc, 'Values', ifcond, member_func=texi_enum_value))) def visit_object_type(self, name, info, ifcond, base, members, variants): @@ -217,14 +219,14 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): base = None self._gen.add(TYPE_FMT(type='Object', name=doc.symbol, - body=texi_entity(doc, 'Members', + body=texi_entity(doc, 'Members', ifcond, base, variants))) def visit_alternate_type(self, name, info, ifcond, variants): doc = self.cur_doc self._gen.add(TYPE_FMT(type='Alternate', name=doc.symbol, - body=texi_entity(doc, 'Members'))) + body=texi_entity(doc, 'Members', ifcond))) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): @@ -233,9 +235,9 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): body = texi_body(doc) body += ('\n@b{Arguments:} the members of @code{%s}\n' % arg_type.name) - body += texi_sections(doc) + body += texi_sections(doc, ifcond) else: - body = texi_entity(doc, 'Arguments') + body = texi_entity(doc, 'Arguments', ifcond) self._gen.add(MSG_FMT(type='Command', name=doc.symbol, body=body)) @@ -244,7 +246,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): doc = self.cur_doc self._gen.add(MSG_FMT(type='Event', name=doc.symbol, - body=texi_entity(doc, 'Arguments'))) + body=texi_entity(doc, 'Arguments', ifcond))) def symbol(self, doc, entity): if self._gen._body: @@ -257,7 +259,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): assert not doc.args if self._gen._body: self._gen.add('\n') - self._gen.add(texi_body(doc) + texi_sections(doc)) + self._gen.add(texi_body(doc) + texi_sections(doc, None)) def gen_doc(schema, output_dir, prefix): diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 97ab4625ff..984cd8ed06 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -55,7 +55,7 @@ # # @two is undocumented ## -{ 'enum': 'Enum', 'data': [ 'one', 'two' ] } +{ 'enum': 'Enum', 'data': [ 'one', 'two' ], 'if': 'defined(IFCOND)' } ## # @Base: diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 9c8a4838e1..35f3f1164c 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -3,6 +3,7 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE module doc-good.json enum Enum ['one', 'two'] + if ['defined(IFCOND)'] object Base member base1: Enum optional=False object Variant1 diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi index 0aed2300a5..e42eace474 100644 --- a/tests/qapi-schema/doc-good.texi +++ b/tests/qapi-schema/doc-good.texi @@ -89,6 +89,8 @@ Not documented @end table @code{two} is undocumented + +@b{If:} @code{defined(IFCOND)} @end deftp From 05eb4a25aea245575503b03bc882a64ae70fcaad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:47 +0200 Subject: [PATCH 2019/2380] qapi: add conditions to VNC type/commands/events on the schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add #if defined(CONFIG_VNC) in generated code, and adjust the qmp/hmp code accordingly. query-qmp-schema no longer reports the command/events etc as available when disabled at compile. Commands made conditional: * query-vnc, query-vnc-servers, change-vnc-password Before the patch, the commands for !CONFIG_VNC are stubs that fail like this: {"error": {"class": "GenericError", "desc": "The feature 'vnc' is not enabled"}} Afterwards, they fail like this: {"error": {"class": "CommandNotFound", "desc": "The command FOO has not been found"}} I call that an improvement, because it lets clients distinguish between command unavailable (class CommandNotFound) and command failed (class GenericError). Events made conditional: * VNC_CONNECTED, VNC_INITIALIZED, VNC_DISCONNECTED HMP change: * info vnc Will return "unknown command: 'info vnc'" when VNC is compiled out (same as error for spice when --disable-spice) Occurrences of VNC (case insensitive) in the schema that aren't covered by this change: * add_client Command has other uses, including "socket bases character devices". These are unconditional as far as I can tell. * set_password, expire_password In theory, these commands could be used for managing any service's password. In practice, they're used for VNC and SPICE services. They're documented for "remote display session" / "remote display server". The service is selected by argument @protocol. The code special-cases protocol-specific argument checking, then calls a protocol-specific function to do the work. If it fails, the command fails with "Could not set password". It does when the service isn't compiled in (it's a stub then). We could make these commands conditional on the conjunction of all services [currently: defined(CONFIG_VNC) || defined(CONFIG_SPICE)], but I doubt it's worthwhile. * change Command has other uses, namely changing media. This patch inlines a stub; no functional change. Signed-off-by: Marc-André Lureau Reviewed-by: Gerd Hoffmann Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-14-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- hmp-commands-info.hx | 2 ++ hmp.c | 9 ++++++++- qapi/ui.json | 45 +++++++++++++++++++++++++++----------------- qmp.c | 30 ++++------------------------- ui/vnc.h | 2 ++ 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index a482b6e56b..70639f656a 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -426,6 +426,7 @@ STEXI Show which guest mouse is receiving events. ETEXI +#if defined(CONFIG_VNC) { .name = "vnc", .args_type = "", @@ -433,6 +434,7 @@ ETEXI .help = "show the vnc server status", .cmd = hmp_info_vnc, }, +#endif STEXI @item info vnc diff --git a/hmp.c b/hmp.c index fe4477a8fb..4555b503ac 100644 --- a/hmp.c +++ b/hmp.c @@ -616,6 +616,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict) qapi_free_BlockStatsList(stats_list); } +#ifdef CONFIG_VNC /* Helper for hmp_info_vnc_clients, _servers */ static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info, const char *name) @@ -703,6 +704,7 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict) qapi_free_VncInfo2List(info2l); } +#endif #ifdef CONFIG_SPICE void hmp_info_spice(Monitor *mon, const QDict *qdict) @@ -1772,12 +1774,14 @@ void hmp_eject(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } +#ifdef CONFIG_VNC static void hmp_change_read_arg(void *opaque, const char *password, void *readline_opaque) { qmp_change_vnc_password(password, NULL); monitor_read_command(opaque, 1); } +#endif void hmp_change(Monitor *mon, const QDict *qdict) { @@ -1788,6 +1792,7 @@ void hmp_change(Monitor *mon, const QDict *qdict) BlockdevChangeReadOnlyMode read_only_mode = 0; Error *err = NULL; +#ifdef CONFIG_VNC if (strcmp(device, "vnc") == 0) { if (read_only) { monitor_printf(mon, @@ -1802,7 +1807,9 @@ void hmp_change(Monitor *mon, const QDict *qdict) } } qmp_change("vnc", target, !!arg, arg, &err); - } else { + } else +#endif + { if (read_only) { read_only_mode = qapi_enum_parse(&BlockdevChangeReadOnlyMode_lookup, diff --git a/qapi/ui.json b/qapi/ui.json index f48d2a0afb..ee90c1880b 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -377,7 +377,8 @@ 'data': { 'host': 'str', 'service': 'str', 'family': 'NetworkAddressFamily', - 'websocket': 'bool' } } + 'websocket': 'bool' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VncServerInfo: @@ -391,7 +392,8 @@ ## { 'struct': 'VncServerInfo', 'base': 'VncBasicInfo', - 'data': { '*auth': 'str' } } + 'data': { '*auth': 'str' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VncClientInfo: @@ -408,7 +410,8 @@ ## { 'struct': 'VncClientInfo', 'base': 'VncBasicInfo', - 'data': { '*x509_dname': 'str', '*sasl_username': 'str' } } + 'data': { '*x509_dname': 'str', '*sasl_username': 'str' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VncInfo: @@ -449,7 +452,8 @@ { 'struct': 'VncInfo', 'data': {'enabled': 'bool', '*host': 'str', '*family': 'NetworkAddressFamily', - '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} } + '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']}, + 'if': 'defined(CONFIG_VNC)' } ## # @VncPrimaryAuth: @@ -460,7 +464,8 @@ ## { 'enum': 'VncPrimaryAuth', 'data': [ 'none', 'vnc', 'ra2', 'ra2ne', 'tight', 'ultra', - 'tls', 'vencrypt', 'sasl' ] } + 'tls', 'vencrypt', 'sasl' ], + 'if': 'defined(CONFIG_VNC)' } ## # @VncVencryptSubAuth: @@ -474,8 +479,8 @@ 'tls-none', 'x509-none', 'tls-vnc', 'x509-vnc', 'tls-plain', 'x509-plain', - 'tls-sasl', 'x509-sasl' ] } - + 'tls-sasl', 'x509-sasl' ], + 'if': 'defined(CONFIG_VNC)' } ## # @VncServerInfo2: @@ -492,8 +497,8 @@ { 'struct': 'VncServerInfo2', 'base': 'VncBasicInfo', 'data': { 'auth' : 'VncPrimaryAuth', - '*vencrypt' : 'VncVencryptSubAuth' } } - + '*vencrypt' : 'VncVencryptSubAuth' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VncInfo2: @@ -525,7 +530,8 @@ 'clients' : ['VncClientInfo'], 'auth' : 'VncPrimaryAuth', '*vencrypt' : 'VncVencryptSubAuth', - '*display' : 'str' } } + '*display' : 'str' }, + 'if': 'defined(CONFIG_VNC)' } ## # @query-vnc: @@ -556,8 +562,8 @@ # } # ## -{ 'command': 'query-vnc', 'returns': 'VncInfo' } - +{ 'command': 'query-vnc', 'returns': 'VncInfo', + 'if': 'defined(CONFIG_VNC)' } ## # @query-vnc-servers: # @@ -567,7 +573,8 @@ # # Since: 2.3 ## -{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'] } +{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'], + 'if': 'defined(CONFIG_VNC)' } ## # @change-vnc-password: @@ -581,7 +588,8 @@ # Notes: An empty password in this command will set the password to the empty # string. Existing clients are unaffected by executing this command. ## -{ 'command': 'change-vnc-password', 'data': {'password': 'str'} } +{ 'command': 'change-vnc-password', 'data': {'password': 'str'}, + 'if': 'defined(CONFIG_VNC)' } ## # @VNC_CONNECTED: @@ -610,7 +618,8 @@ ## { 'event': 'VNC_CONNECTED', 'data': { 'server': 'VncServerInfo', - 'client': 'VncBasicInfo' } } + 'client': 'VncBasicInfo' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VNC_INITIALIZED: @@ -637,7 +646,8 @@ ## { 'event': 'VNC_INITIALIZED', 'data': { 'server': 'VncServerInfo', - 'client': 'VncClientInfo' } } + 'client': 'VncClientInfo' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VNC_DISCONNECTED: @@ -663,7 +673,8 @@ ## { 'event': 'VNC_DISCONNECTED', 'data': { 'server': 'VncServerInfo', - 'client': 'VncClientInfo' } } + 'client': 'VncClientInfo' }, + 'if': 'defined(CONFIG_VNC)' } ## # = Input diff --git a/qmp.c b/qmp.c index 73e46d795f..bc5537b221 100644 --- a/qmp.c +++ b/qmp.c @@ -129,22 +129,6 @@ void qmp_cpu_add(int64_t id, Error **errp) } } -#ifndef CONFIG_VNC -/* If VNC support is enabled, the "true" query-vnc command is - defined in the VNC subsystem */ -VncInfo *qmp_query_vnc(Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "vnc"); - return NULL; -}; - -VncInfo2List *qmp_query_vnc_servers(Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "vnc"); - return NULL; -}; -#endif - #ifndef CONFIG_SPICE /* * qmp_unregister_commands_hack() ensures that QMP command query-spice @@ -412,23 +396,17 @@ static void qmp_change_vnc(const char *target, bool has_arg, const char *arg, qmp_change_vnc_listen(target, errp); } } -#else -void qmp_change_vnc_password(const char *password, Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "vnc"); -} -static void qmp_change_vnc(const char *target, bool has_arg, const char *arg, - Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "vnc"); -} #endif /* !CONFIG_VNC */ void qmp_change(const char *device, const char *target, bool has_arg, const char *arg, Error **errp) { if (strcmp(device, "vnc") == 0) { +#ifdef CONFIG_VNC qmp_change_vnc(target, has_arg, arg, errp); +#else + error_setg(errp, QERR_FEATURE_DISABLED, "vnc"); +#endif } else { qmp_blockdev_change_medium(true, device, false, NULL, target, has_arg, arg, false, 0, errp); diff --git a/ui/vnc.h b/ui/vnc.h index 762632929b..a86e0610e8 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -297,7 +297,9 @@ struct VncState bool encode_ws; bool websocket; +#ifdef CONFIG_VNC VncClientInfo *info; +#endif /* Job thread bottom half has put data for a forced update * into the output buffer. This offset points to the end of From 514337c142f9522f6ab89c3d2f964f446ebeb1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 3 Jul 2018 17:56:48 +0200 Subject: [PATCH 2020/2380] qapi: add conditions to SPICE type/commands/events on the schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add #if defined(CONFIG_SPICE) in generated code, and adjust the qmp/hmp code accordingly. query-qmp-schema no longer reports the command/events etc as available when disabled at compile time. Commands made conditional: * query-spice Before the patch, the command for !CONFIG_SPICE is unregistered. It will fail with the same error. Events made conditional: * SPICE_CONNECTED, SPICE_INITIALIZED, SPICE_DISCONNECTED, SPICE_MIGRATE_COMPLETED Add TODO for conditional SPICE chardevs, delayed until the supports for conditional members lands. No HMP change, the code was already conditional. Signed-off-by: Marc-André Lureau Reviewed-by: Gerd Hoffmann Reviewed-by: Markus Armbruster Message-Id: <20180703155648.11933-15-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 3 --- qapi/char.json | 8 ++++++-- qapi/ui.json | 30 ++++++++++++++++++++---------- qmp.c | 16 ---------------- 4 files changed, 26 insertions(+), 31 deletions(-) diff --git a/monitor.c b/monitor.c index 3a0ea0c602..8e1aea7826 100644 --- a/monitor.c +++ b/monitor.c @@ -1191,9 +1191,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, */ static void qmp_unregister_commands_hack(void) { -#ifndef CONFIG_SPICE - qmp_unregister_command(&qmp_commands, "query-spice"); -#endif #ifndef CONFIG_REPLICATION qmp_unregister_command(&qmp_commands, "xen-set-replication"); qmp_unregister_command(&qmp_commands, "query-xen-replication-status"); diff --git a/qapi/char.json b/qapi/char.json index 60f31d83fc..b7b2a05766 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -320,6 +320,7 @@ ## { 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' }, 'base': 'ChardevCommon' } +# TODO: 'if': 'defined(CONFIG_SPICE)' ## # @ChardevSpicePort: @@ -332,6 +333,7 @@ ## { 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' }, 'base': 'ChardevCommon' } +# TODO: 'if': 'defined(CONFIG_SPICE)' ## # @ChardevVC: @@ -385,8 +387,10 @@ 'testdev': 'ChardevCommon', 'stdio' : 'ChardevStdio', 'console': 'ChardevCommon', - 'spicevmc' : 'ChardevSpiceChannel', - 'spiceport' : 'ChardevSpicePort', + 'spicevmc': 'ChardevSpiceChannel', +# TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' }, + 'spiceport': 'ChardevSpicePort', +# TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' }, 'vc' : 'ChardevVC', 'ringbuf': 'ChardevRingbuf', # next one is just for compatibility diff --git a/qapi/ui.json b/qapi/ui.json index ee90c1880b..4ca91bb45a 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -118,7 +118,8 @@ { 'struct': 'SpiceBasicInfo', 'data': { 'host': 'str', 'port': 'str', - 'family': 'NetworkAddressFamily' } } + 'family': 'NetworkAddressFamily' }, + 'if': 'defined(CONFIG_SPICE)' } ## # @SpiceServerInfo: @@ -131,7 +132,8 @@ ## { 'struct': 'SpiceServerInfo', 'base': 'SpiceBasicInfo', - 'data': { '*auth': 'str' } } + 'data': { '*auth': 'str' }, + 'if': 'defined(CONFIG_SPICE)' } ## # @SpiceChannel: @@ -156,7 +158,8 @@ { 'struct': 'SpiceChannel', 'base': 'SpiceBasicInfo', 'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int', - 'tls': 'bool'} } + 'tls': 'bool'}, + 'if': 'defined(CONFIG_SPICE)' } ## # @SpiceQueryMouseMode: @@ -175,7 +178,8 @@ # Since: 1.1 ## { 'enum': 'SpiceQueryMouseMode', - 'data': [ 'client', 'server', 'unknown' ] } + 'data': [ 'client', 'server', 'unknown' ], + 'if': 'defined(CONFIG_SPICE)' } ## # @SpiceInfo: @@ -212,7 +216,8 @@ { 'struct': 'SpiceInfo', 'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int', '*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str', - 'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} } + 'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']}, + 'if': 'defined(CONFIG_SPICE)' } ## # @query-spice: @@ -257,7 +262,8 @@ # } # ## -{ 'command': 'query-spice', 'returns': 'SpiceInfo' } +{ 'command': 'query-spice', 'returns': 'SpiceInfo', + 'if': 'defined(CONFIG_SPICE)' } ## # @SPICE_CONNECTED: @@ -282,7 +288,8 @@ ## { 'event': 'SPICE_CONNECTED', 'data': { 'server': 'SpiceBasicInfo', - 'client': 'SpiceBasicInfo' } } + 'client': 'SpiceBasicInfo' }, + 'if': 'defined(CONFIG_SPICE)' } ## # @SPICE_INITIALIZED: @@ -310,7 +317,8 @@ ## { 'event': 'SPICE_INITIALIZED', 'data': { 'server': 'SpiceServerInfo', - 'client': 'SpiceChannel' } } + 'client': 'SpiceChannel' }, + 'if': 'defined(CONFIG_SPICE)' } ## # @SPICE_DISCONNECTED: @@ -335,7 +343,8 @@ ## { 'event': 'SPICE_DISCONNECTED', 'data': { 'server': 'SpiceBasicInfo', - 'client': 'SpiceBasicInfo' } } + 'client': 'SpiceBasicInfo' }, + 'if': 'defined(CONFIG_SPICE)' } ## # @SPICE_MIGRATE_COMPLETED: @@ -350,7 +359,8 @@ # "event": "SPICE_MIGRATE_COMPLETED" } # ## -{ 'event': 'SPICE_MIGRATE_COMPLETED' } +{ 'event': 'SPICE_MIGRATE_COMPLETED', + 'if': 'defined(CONFIG_SPICE)' } ## # == VNC diff --git a/qmp.c b/qmp.c index bc5537b221..162bc2e30d 100644 --- a/qmp.c +++ b/qmp.c @@ -129,22 +129,6 @@ void qmp_cpu_add(int64_t id, Error **errp) } } -#ifndef CONFIG_SPICE -/* - * qmp_unregister_commands_hack() ensures that QMP command query-spice - * exists only #ifdef CONFIG_SPICE. Necessary for an accurate - * query-commands result. However, the QAPI schema is blissfully - * unaware of that, and the QAPI code generator happily generates a - * dead qmp_marshal_query_spice() that calls qmp_query_spice(). - * Provide it one, or else linking fails. FIXME Educate the QAPI - * schema on CONFIG_SPICE. - */ -SpiceInfo *qmp_query_spice(Error **errp) -{ - abort(); -}; -#endif - void qmp_exit_preconfig(Error **errp) { if (!runstate_check(RUN_STATE_PRECONFIG)) { From 509b97fd079c9666ae7318bae6e1273fd5d6b574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Golembiovsk=C3=BD?= Date: Fri, 20 Apr 2018 20:04:34 +0200 Subject: [PATCH 2021/2380] test-qga: add trivial tests for some commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These commands did not get their tests in the original commits: - guest-get-host-name - guest-get-timezone - guest-get-users Trivial tests that mostly only call the commands were added. Signed-off-by: Tomáš Golembiovský Reviewed-by: Marc-André Lureau * replace QDECREF() with qobject_unref() Signed-off-by: Michael Roth --- tests/test-qga.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/test-qga.c b/tests/test-qga.c index 30c9643257..350f27812a 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -854,6 +854,54 @@ static void test_qga_guest_exec_invalid(gconstpointer fix) qobject_unref(ret); } +static void test_qga_guest_get_host_name(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(val, "host-name")); + + qobject_unref(ret); +} + +static void test_qga_guest_get_timezone(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* Make sure there's at least offset */ + val = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(val, "offset")); + + qobject_unref(ret); +} + +static void test_qga_guest_get_users(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* There is not much to test here */ + val = qdict_get_qlist(ret, "return"); + g_assert_nonnull(val); + + qobject_unref(ret); +} + static void test_qga_guest_get_osinfo(gconstpointer data) { TestFixture fixture; @@ -946,6 +994,12 @@ int main(int argc, char **argv) test_qga_guest_exec_invalid); g_test_add_data_func("/qga/guest-get-osinfo", &fix, test_qga_guest_get_osinfo); + g_test_add_data_func("/qga/guest-get-host-name", &fix, + test_qga_guest_get_host_name); + g_test_add_data_func("/qga/guest-get-timezone", &fix, + test_qga_guest_get_timezone); + g_test_add_data_func("/qga/guest-get-users", &fix, + test_qga_guest_get_users); ret = g_test_run(); From 84ece8ef6c70ea1852ec47805531017b75d46d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Golembiovsk=C3=BD?= Date: Fri, 20 Apr 2018 20:12:45 +0200 Subject: [PATCH 2022/2380] qga/schema: fix documentation for GuestOSInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation for kernel-version and kernel-release on Windows was swapped. Signed-off-by: Tomáš Golembiovský Reviewed-by: Marc-André Lureau Signed-off-by: Michael Roth --- qga/qapi-schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 2df6356b8c..dfbc4a5e32 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1173,10 +1173,10 @@ # # @kernel-release: # * POSIX: release field returned by uname(2) -# * Windows: version number of the OS +# * Windows: build number of the OS # @kernel-version: # * POSIX: version field returned by uname(2) -# * Windows: build number of the OS +# * Windows: version number of the OS # @machine: # * POSIX: machine field returned by uname(2) # * Windows: one of x86, x86_64, arm, ia64 From a972304d187b104ebec327bc824b23a5a3589ac3 Mon Sep 17 00:00:00 2001 From: Bishara AbuHattoum Date: Thu, 28 Jun 2018 17:58:16 +0300 Subject: [PATCH 2023/2380] qga-win: Fixing msi upgrade disallow in WiX file Issue: When upgrading qemu-ga using the msi from an old version to a newer one, the upgrade is not allowed by the msi showing this error message "Another version of this product is already installed." BZ# 1536331: https://bugzilla.redhat.com/show_bug.cgi?id=1536331 Fix: For the upgrade to be allowed by the msi the WiX file must provide three things: 1. Changing product's Id. (assigning it to "*") 2. Constant product's UpgradeId. (exists) 3. Changing version. (exists) Reference: http://wixtoolset.org/documentation/manual/v3/howtos/updates/major_upgrade.html Signed-off-by: Bishara AbuHattoum Signed-off-by: Michael Roth --- qga/installer/qemu-ga.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs index 5af11627f8..f751a7e9f7 100644 --- a/qga/installer/qemu-ga.wxs +++ b/qga/installer/qemu-ga.wxs @@ -41,7 +41,7 @@ Date: Mon, 14 May 2018 14:38:55 +0200 Subject: [PATCH 2024/2380] qemu-ga: make get-fsinfo work over pci bridges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Iterate over the PCI bridges to lookup the PCI device associated with the block device. This allows to lookup the driver under the following syspath: /sys/devices/pci0000:00/0000:00:02.2/0000:03:00.0/virtio2/block/vda/vda3 It also works with an "old-style" Q35 libvirt hierarchy: root complex -> DMI-PCI bridge -> PCI-PCI bridge -> virtio controller, ex: /sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/0000:02:01.0/virtio1/block/vda/vda3 The setup can be reproduced with the following qemu command line (Thanks Marcel for help): qemu-system-x86_64 -M q35 \ -device i82801b11-bridge,id=dmi2pci_bridge,bus=pcie.0 -device pci-bridge,id=pci_bridge,bus=dmi2pci_bridge,addr=0x1,chassis_nr=1 -device virtio-blk-pci,scsi=off,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1,bus=pci_bridge,addr=0x1 For consistency with other syspath-related debug messages, replace a \"%s\" in the message with '%s'. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1567041 Signed-off-by: Marc-André Lureau Reviewed-by: Laszlo Ersek Signed-off-by: Michael Roth --- qga/commands-posix.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index ae8535e497..7b7c78d85a 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -876,13 +876,28 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, p = strstr(syspath, "/devices/pci"); if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) { - g_debug("only pci device is supported: sysfs path \"%s\"", syspath); + g_debug("only pci device is supported: sysfs path '%s'", syspath); return; } - driver = get_pci_driver(syspath, (p + 12 + pcilen) - syspath, errp); - if (!driver) { - goto cleanup; + p += 12 + pcilen; + while (true) { + driver = get_pci_driver(syspath, p - syspath, errp); + if (driver && (g_str_equal(driver, "ata_piix") || + g_str_equal(driver, "sym53c8xx") || + g_str_equal(driver, "virtio-pci") || + g_str_equal(driver, "ahci"))) { + break; + } + + if (sscanf(p, "/%x:%x:%x.%x%n", + pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) { + p += pcilen; + continue; + } + + g_debug("unsupported driver or sysfs path '%s'", syspath); + return; } p = strstr(syspath, "/target"); From 304a0fcb397efba40dffded985a6539143cca82c Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 21 Jun 2018 07:21:48 -0300 Subject: [PATCH 2025/2380] qga: refactoring qmp_guest_suspend_* functions To be able to add new suspend mechanisms we need to detach the existing QMP functions from the current implementation specifics. At this moment we have functions such as qmp_guest_suspend_ram calling bios_suspend_mode and guest_suspend passing the pmutils command and arguments as parameters. This patch removes this logic from the QMP functions, moving them to the respective functions that will have to deal with which binary to use. Signed-off-by: Daniel Henrique Barboza Signed-off-by: Michael Roth --- qga/commands-posix.c | 87 ++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 7b7c78d85a..bd2c3514d8 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1474,15 +1474,38 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) #define LINUX_SYS_STATE_FILE "/sys/power/state" #define SUSPEND_SUPPORTED 0 #define SUSPEND_NOT_SUPPORTED 1 +#define SUSPEND_MODE_DISK 1 +#define SUSPEND_MODE_RAM 2 +#define SUSPEND_MODE_HYBRID 3 -static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg, - const char *sysfile_str, Error **errp) +static void bios_supports_mode(int suspend_mode, Error **errp) { Error *local_err = NULL; + const char *pmutils_arg, *sysfile_str; + const char *pmutils_bin = "pm-is-supported"; char *pmutils_path; pid_t pid; int status; + switch (suspend_mode) { + + case SUSPEND_MODE_DISK: + pmutils_arg = "--hibernate"; + sysfile_str = "disk"; + break; + case SUSPEND_MODE_RAM: + pmutils_arg = "--suspend"; + sysfile_str = "mem"; + break; + case SUSPEND_MODE_HYBRID: + pmutils_arg = "--suspend-hybrid"; + sysfile_str = NULL; + break; + default: + error_setg(errp, "guest suspend mode not supported"); + return; + } + pmutils_path = g_find_program_in_path(pmutils_bin); pid = fork(); @@ -1559,14 +1582,39 @@ out: g_free(pmutils_path); } -static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, - Error **errp) +static void guest_suspend(int suspend_mode, Error **errp) { Error *local_err = NULL; + const char *pmutils_bin, *sysfile_str; char *pmutils_path; pid_t pid; int status; + bios_supports_mode(suspend_mode, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + switch (suspend_mode) { + + case SUSPEND_MODE_DISK: + pmutils_bin = "pm-hibernate"; + sysfile_str = "disk"; + break; + case SUSPEND_MODE_RAM: + pmutils_bin = "pm-suspend"; + sysfile_str = "mem"; + break; + case SUSPEND_MODE_HYBRID: + pmutils_bin = "pm-suspend-hybrid"; + sysfile_str = NULL; + break; + default: + error_setg(errp, "unknown guest suspend mode"); + return; + } + pmutils_path = g_find_program_in_path(pmutils_bin); pid = fork(); @@ -1629,42 +1677,17 @@ out: void qmp_guest_suspend_disk(Error **errp) { - Error *local_err = NULL; - - bios_supports_mode("pm-is-supported", "--hibernate", "disk", &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - guest_suspend("pm-hibernate", "disk", errp); + guest_suspend(SUSPEND_MODE_DISK, errp); } void qmp_guest_suspend_ram(Error **errp) { - Error *local_err = NULL; - - bios_supports_mode("pm-is-supported", "--suspend", "mem", &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - guest_suspend("pm-suspend", "mem", errp); + guest_suspend(SUSPEND_MODE_RAM, errp); } void qmp_guest_suspend_hybrid(Error **errp) { - Error *local_err = NULL; - - bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - guest_suspend("pm-suspend-hybrid", NULL, errp); + guest_suspend(SUSPEND_MODE_HYBRID, errp); } static GuestNetworkInterfaceList * From a5fcf0e3e491ede0d03e71f064e8e6d24e889aab Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 21 Jun 2018 07:21:49 -0300 Subject: [PATCH 2026/2380] qga: bios_supports_mode: decoupling pm-utils and sys logic In bios_supports_mode there is a verification to assert if the chosen suspend mode is supported by the pmutils tools and, if not, we see if the Linux sys state files supports it. This verification is done in the same function, one after the other, and it works for now. But, when adding a new suspend mechanism that will not necessarily follow the same return 0 or 1 logic of pmutils, this code will be hard to deal with. This patch decouple the two existing logics into their own functions, pmutils_supports_mode and linux_sys_state_supports_mode, which in turn are used inside bios_support_mode. The existing logic is kept but now it's easier to extend it. Signed-off-by: Daniel Henrique Barboza Signed-off-by: Michael Roth --- qga/commands-posix.c | 116 +++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index bd2c3514d8..a8299ecc77 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1478,75 +1478,43 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) #define SUSPEND_MODE_RAM 2 #define SUSPEND_MODE_HYBRID 3 -static void bios_supports_mode(int suspend_mode, Error **errp) +static bool pmutils_supports_mode(int suspend_mode, Error **errp) { Error *local_err = NULL; - const char *pmutils_arg, *sysfile_str; + const char *pmutils_arg; const char *pmutils_bin = "pm-is-supported"; char *pmutils_path; pid_t pid; int status; + bool ret = false; switch (suspend_mode) { case SUSPEND_MODE_DISK: pmutils_arg = "--hibernate"; - sysfile_str = "disk"; break; case SUSPEND_MODE_RAM: pmutils_arg = "--suspend"; - sysfile_str = "mem"; break; case SUSPEND_MODE_HYBRID: pmutils_arg = "--suspend-hybrid"; - sysfile_str = NULL; break; default: - error_setg(errp, "guest suspend mode not supported"); - return; + return ret; } pmutils_path = g_find_program_in_path(pmutils_bin); + if (!pmutils_path) { + return ret; + } pid = fork(); if (!pid) { - char buf[32]; /* hopefully big enough */ - ssize_t ret; - int fd; - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - if (pmutils_path) { - execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ); - } - + execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ); /* - * If we get here either pm-utils is not installed or execle() has - * failed. Let's try the manual method if the caller wants it. + * If we get here execle() has failed. */ - - if (!sysfile_str) { - _exit(SUSPEND_NOT_SUPPORTED); - } - - fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); - if (fd < 0) { - _exit(SUSPEND_NOT_SUPPORTED); - } - - ret = read(fd, buf, sizeof(buf)-1); - if (ret <= 0) { - _exit(SUSPEND_NOT_SUPPORTED); - } - buf[ret] = '\0'; - - if (strstr(buf, sysfile_str)) { - _exit(SUSPEND_SUPPORTED); - } - _exit(SUSPEND_NOT_SUPPORTED); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); @@ -1559,17 +1527,11 @@ static void bios_supports_mode(int suspend_mode, Error **errp) goto out; } - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; - } - switch (WEXITSTATUS(status)) { case SUSPEND_SUPPORTED: + ret = true; goto out; case SUSPEND_NOT_SUPPORTED: - error_setg(errp, - "the requested suspend mode is not supported by the guest"); goto out; default: error_setg(errp, @@ -1580,6 +1542,64 @@ static void bios_supports_mode(int suspend_mode, Error **errp) out: g_free(pmutils_path); + return ret; +} + +static bool linux_sys_state_supports_mode(int suspend_mode, Error **errp) +{ + const char *sysfile_str; + char buf[32]; /* hopefully big enough */ + int fd; + ssize_t ret; + + switch (suspend_mode) { + + case SUSPEND_MODE_DISK: + sysfile_str = "disk"; + break; + case SUSPEND_MODE_RAM: + sysfile_str = "mem"; + break; + default: + return false; + } + + fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); + if (fd < 0) { + return false; + } + + ret = read(fd, buf, sizeof(buf) - 1); + if (ret <= 0) { + return false; + } + buf[ret] = '\0'; + + if (strstr(buf, sysfile_str)) { + return true; + } + return false; +} + +static void bios_supports_mode(int suspend_mode, Error **errp) +{ + Error *local_err = NULL; + bool ret; + + ret = pmutils_supports_mode(suspend_mode, &local_err); + if (ret) { + return; + } + if (local_err) { + error_propagate(errp, local_err); + return; + } + ret = linux_sys_state_supports_mode(suspend_mode, errp); + if (!ret) { + error_setg(errp, + "the requested suspend mode is not supported by the guest"); + return; + } } static void guest_suspend(int suspend_mode, Error **errp) From 246d76eba1944d7e59affb288ec27d7fcfb5d256 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 21 Jun 2018 07:21:50 -0300 Subject: [PATCH 2027/2380] qga: guest_suspend: decoupling pm-utils and sys logic Following the same logic of the previous patch, let's also decouple the suspend logic from guest_suspend into specialized functions, one for each strategy we support at this moment. Signed-off-by: Daniel Henrique Barboza Signed-off-by: Michael Roth --- qga/commands-posix.c | 194 ++++++++++++++++++++++++++----------------- 1 file changed, 120 insertions(+), 74 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index a8299ecc77..9ebd77b894 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1545,6 +1545,65 @@ out: return ret; } +static void pmutils_suspend(int suspend_mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_bin; + char *pmutils_path; + pid_t pid; + int status; + + switch (suspend_mode) { + + case SUSPEND_MODE_DISK: + pmutils_bin = "pm-hibernate"; + break; + case SUSPEND_MODE_RAM: + pmutils_bin = "pm-suspend"; + break; + case SUSPEND_MODE_HYBRID: + pmutils_bin = "pm-suspend-hybrid"; + break; + default: + error_setg(errp, "unknown guest suspend mode"); + return; + } + + pmutils_path = g_find_program_in_path(pmutils_bin); + if (!pmutils_path) { + error_setg(errp, "the helper program '%s' was not found", pmutils_bin); + return; + } + + pid = fork(); + if (!pid) { + setsid(); + execle(pmutils_path, pmutils_bin, NULL, environ); + /* + * If we get here execle() has failed. + */ + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + goto out; + } + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + if (WEXITSTATUS(status)) { + error_setg(errp, + "the helper program '%s' returned an unexpected exit status" + " code (%d)", pmutils_path, WEXITSTATUS(status)); + } + +out: + g_free(pmutils_path); +} + static bool linux_sys_state_supports_mode(int suspend_mode, Error **errp) { const char *sysfile_str; @@ -1581,6 +1640,63 @@ static bool linux_sys_state_supports_mode(int suspend_mode, Error **errp) return false; } +static void linux_sys_state_suspend(int suspend_mode, Error **errp) +{ + Error *local_err = NULL; + const char *sysfile_str; + pid_t pid; + int status; + + switch (suspend_mode) { + + case SUSPEND_MODE_DISK: + sysfile_str = "disk"; + break; + case SUSPEND_MODE_RAM: + sysfile_str = "mem"; + break; + default: + error_setg(errp, "unknown guest suspend mode"); + return; + } + + pid = fork(); + if (!pid) { + /* child */ + int fd; + + setsid(); + reopen_fd_to_null(0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); + if (fd < 0) { + _exit(EXIT_FAILURE); + } + + if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) { + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + return; + } + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (WEXITSTATUS(status)) { + error_setg(errp, "child process has failed to suspend"); + } + +} + static void bios_supports_mode(int suspend_mode, Error **errp) { Error *local_err = NULL; @@ -1605,10 +1721,6 @@ static void bios_supports_mode(int suspend_mode, Error **errp) static void guest_suspend(int suspend_mode, Error **errp) { Error *local_err = NULL; - const char *pmutils_bin, *sysfile_str; - char *pmutils_path; - pid_t pid; - int status; bios_supports_mode(suspend_mode, &local_err); if (local_err) { @@ -1616,83 +1728,17 @@ static void guest_suspend(int suspend_mode, Error **errp) return; } - switch (suspend_mode) { - - case SUSPEND_MODE_DISK: - pmutils_bin = "pm-hibernate"; - sysfile_str = "disk"; - break; - case SUSPEND_MODE_RAM: - pmutils_bin = "pm-suspend"; - sysfile_str = "mem"; - break; - case SUSPEND_MODE_HYBRID: - pmutils_bin = "pm-suspend-hybrid"; - sysfile_str = NULL; - break; - default: - error_setg(errp, "unknown guest suspend mode"); + pmutils_suspend(suspend_mode, &local_err); + if (!local_err) { return; } - pmutils_path = g_find_program_in_path(pmutils_bin); + error_free(local_err); - pid = fork(); - if (pid == 0) { - /* child */ - int fd; - - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - if (pmutils_path) { - execle(pmutils_path, pmutils_bin, NULL, environ); - } - - /* - * If we get here either pm-utils is not installed or execle() has - * failed. Let's try the manual method if the caller wants it. - */ - - if (!sysfile_str) { - _exit(EXIT_FAILURE); - } - - fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); - if (fd < 0) { - _exit(EXIT_FAILURE); - } - - if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) { - _exit(EXIT_FAILURE); - } - - _exit(EXIT_SUCCESS); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - goto out; - } - - ga_wait_child(pid, &status, &local_err); + linux_sys_state_suspend(suspend_mode, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "child process has failed to suspend"); - goto out; - } - -out: - g_free(pmutils_path); } void qmp_guest_suspend_disk(Error **errp) From 8b020b5eb708091e723518907184e609350f6d41 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 21 Jun 2018 07:21:51 -0300 Subject: [PATCH 2028/2380] qga: removing switch statements, adding run_process_child This is a cleanup of the resulting code after detaching pmutils and Linux sys state file logic: - remove the SUSPEND_MODE_* macros and use an enumeration instead. At the same time, drop the switch statements at the start of each function and use the enumeration index to get the right binary/argument; - create a new function called run_process_child(). This function uses g_spawn_sync() to execute a shell command, returning the exit code. This is a common operation in the pmutils functions and will be used in the systemd implementation as well, so this function will avoid code repetition. There are more places inside commands-posix.c where this new run_process_child function can also be used, but one step at a time. Signed-off-by: Daniel Henrique Barboza *check/propagate local_err before setting errp directly Signed-off-by: Michael Roth --- qga/commands-posix.c | 227 ++++++++++++++++++------------------------- 1 file changed, 94 insertions(+), 133 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 9ebd77b894..1559753d85 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1474,152 +1474,117 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) #define LINUX_SYS_STATE_FILE "/sys/power/state" #define SUSPEND_SUPPORTED 0 #define SUSPEND_NOT_SUPPORTED 1 -#define SUSPEND_MODE_DISK 1 -#define SUSPEND_MODE_RAM 2 -#define SUSPEND_MODE_HYBRID 3 -static bool pmutils_supports_mode(int suspend_mode, Error **errp) +typedef enum { + SUSPEND_MODE_DISK = 0, + SUSPEND_MODE_RAM = 1, + SUSPEND_MODE_HYBRID = 2, +} SuspendMode; + +/* + * Executes a command in a child process using g_spawn_sync, + * returning an int >= 0 representing the exit status of the + * process. + * + * If the program wasn't found in path, returns -1. + * + * If a problem happened when creating the child process, + * returns -1 and errp is set. + */ +static int run_process_child(const char *command[], Error **errp) { - Error *local_err = NULL; - const char *pmutils_arg; - const char *pmutils_bin = "pm-is-supported"; - char *pmutils_path; - pid_t pid; - int status; - bool ret = false; + int exit_status, spawn_flag; + GError *g_err = NULL; + bool success; - switch (suspend_mode) { + spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | + G_SPAWN_STDERR_TO_DEV_NULL; - case SUSPEND_MODE_DISK: - pmutils_arg = "--hibernate"; - break; - case SUSPEND_MODE_RAM: - pmutils_arg = "--suspend"; - break; - case SUSPEND_MODE_HYBRID: - pmutils_arg = "--suspend-hybrid"; - break; - default: - return ret; + success = g_spawn_sync(NULL, (char **)command, environ, spawn_flag, + NULL, NULL, NULL, NULL, + &exit_status, &g_err); + + if (success) { + return WEXITSTATUS(exit_status); } - pmutils_path = g_find_program_in_path(pmutils_bin); - if (!pmutils_path) { - return ret; + if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) { + error_setg(errp, "failed to create child process, error '%s'", + g_err->message); } - pid = fork(); - if (!pid) { - setsid(); - execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ); - /* - * If we get here execle() has failed. - */ - _exit(SUSPEND_NOT_SUPPORTED); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - goto out; - } - - ga_wait_child(pid, &status, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto out; - } - - switch (WEXITSTATUS(status)) { - case SUSPEND_SUPPORTED: - ret = true; - goto out; - case SUSPEND_NOT_SUPPORTED: - goto out; - default: - error_setg(errp, - "the helper program '%s' returned an unexpected exit status" - " code (%d)", pmutils_path, WEXITSTATUS(status)); - goto out; - } - -out: - g_free(pmutils_path); - return ret; + g_error_free(g_err); + return -1; } -static void pmutils_suspend(int suspend_mode, Error **errp) +static bool pmutils_supports_mode(SuspendMode mode, Error **errp) { Error *local_err = NULL; - const char *pmutils_bin; - char *pmutils_path; - pid_t pid; + const char *pmutils_args[3] = {"--hibernate", "--suspend", + "--suspend-hybrid"}; + const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL}; int status; - switch (suspend_mode) { + status = run_process_child(cmd, &local_err); - case SUSPEND_MODE_DISK: - pmutils_bin = "pm-hibernate"; - break; - case SUSPEND_MODE_RAM: - pmutils_bin = "pm-suspend"; - break; - case SUSPEND_MODE_HYBRID: - pmutils_bin = "pm-suspend-hybrid"; - break; - default: - error_setg(errp, "unknown guest suspend mode"); + if (status == SUSPEND_SUPPORTED) { + return true; + } + + if ((status == -1) && !local_err) { + return false; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, + "the helper program '%s' returned an unexpected exit" + " status code (%d)", "pm-is-supported", status); + } + + return false; +} + +static void pmutils_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend", + "pm-suspend-hybrid"}; + const char *cmd[2] = {pmutils_binaries[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == 0) { return; } - pmutils_path = g_find_program_in_path(pmutils_bin); - if (!pmutils_path) { - error_setg(errp, "the helper program '%s' was not found", pmutils_bin); + if ((status == -1) && !local_err) { + error_setg(errp, "the helper program '%s' was not found", + pmutils_binaries[mode]); return; } - pid = fork(); - if (!pid) { - setsid(); - execle(pmutils_path, pmutils_bin, NULL, environ); - /* - * If we get here execle() has failed. - */ - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - goto out; - } - - ga_wait_child(pid, &status, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; - } - - if (WEXITSTATUS(status)) { + } else { error_setg(errp, - "the helper program '%s' returned an unexpected exit status" - " code (%d)", pmutils_path, WEXITSTATUS(status)); + "the helper program '%s' returned an unexpected exit" + " status code (%d)", pmutils_binaries[mode], status); } - -out: - g_free(pmutils_path); } -static bool linux_sys_state_supports_mode(int suspend_mode, Error **errp) +static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) { - const char *sysfile_str; + const char *sysfile_strs[3] = {"disk", "mem", NULL}; + const char *sysfile_str = sysfile_strs[mode]; char buf[32]; /* hopefully big enough */ int fd; ssize_t ret; - switch (suspend_mode) { - - case SUSPEND_MODE_DISK: - sysfile_str = "disk"; - break; - case SUSPEND_MODE_RAM: - sysfile_str = "mem"; - break; - default: + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); return false; } @@ -1640,22 +1605,15 @@ static bool linux_sys_state_supports_mode(int suspend_mode, Error **errp) return false; } -static void linux_sys_state_suspend(int suspend_mode, Error **errp) +static void linux_sys_state_suspend(SuspendMode mode, Error **errp) { Error *local_err = NULL; - const char *sysfile_str; + const char *sysfile_strs[3] = {"disk", "mem", NULL}; + const char *sysfile_str = sysfile_strs[mode]; pid_t pid; int status; - switch (suspend_mode) { - - case SUSPEND_MODE_DISK: - sysfile_str = "disk"; - break; - case SUSPEND_MODE_RAM: - sysfile_str = "mem"; - break; - default: + if (!sysfile_str) { error_setg(errp, "unknown guest suspend mode"); return; } @@ -1697,12 +1655,12 @@ static void linux_sys_state_suspend(int suspend_mode, Error **errp) } -static void bios_supports_mode(int suspend_mode, Error **errp) +static void bios_supports_mode(SuspendMode mode, Error **errp) { Error *local_err = NULL; bool ret; - ret = pmutils_supports_mode(suspend_mode, &local_err); + ret = pmutils_supports_mode(mode, &local_err); if (ret) { return; } @@ -1710,32 +1668,35 @@ static void bios_supports_mode(int suspend_mode, Error **errp) error_propagate(errp, local_err); return; } - ret = linux_sys_state_supports_mode(suspend_mode, errp); + ret = linux_sys_state_supports_mode(mode, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } if (!ret) { error_setg(errp, "the requested suspend mode is not supported by the guest"); - return; } } -static void guest_suspend(int suspend_mode, Error **errp) +static void guest_suspend(SuspendMode mode, Error **errp) { Error *local_err = NULL; - bios_supports_mode(suspend_mode, &local_err); + bios_supports_mode(mode, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - pmutils_suspend(suspend_mode, &local_err); + pmutils_suspend(mode, &local_err); if (!local_err) { return; } error_free(local_err); - linux_sys_state_suspend(suspend_mode, &local_err); + linux_sys_state_suspend(mode, &local_err); if (local_err) { error_propagate(errp, local_err); } From 067927d62e097a8a3624a926e792756ce7a353ed Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 21 Jun 2018 07:21:52 -0300 Subject: [PATCH 2029/2380] qga: systemd hibernate/suspend/hybrid-sleep support pmutils isn't being supported by newer OSes like Fedora 27 or Mint. This means that the only suspend option QGA offers for these guests are writing directly into the Linux sys state file. This also means that QGA also loses the ability to do hybrid suspend in those guests - this suspend mode is only available when using pmutils. Newer guests can use systemd facilities to do all the suspend types QGA supports. The mapping in comparison with pmutils is: - pm-hibernate -> systemctl hibernate - pm-suspend -> systemctl suspend - pm-suspend-hybrid -> systemctl hybrid-sleep To discover whether systemd supports these functions, we inspect the status of the services that implements them. With this patch, we can offer hybrid suspend again for newer guests that do not have pmutils support anymore. Signed-off-by: Daniel Henrique Barboza Signed-off-by: Michael Roth --- qga/commands-posix.c | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 1559753d85..cdb825993f 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1517,6 +1517,63 @@ static int run_process_child(const char *command[], Error **errp) return -1; } +static bool systemd_supports_mode(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend", + "systemd-hybrid-sleep"}; + const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + /* + * systemctl status uses LSB return codes so we can expect + * status > 0 and be ok. To assert if the guest has support + * for the selected suspend mode, status should be < 4. 4 is + * the code for unknown service status, the return value when + * the service does not exist. A common value is status = 3 + * (program is not running). + */ + if (status > 0 && status < 4) { + return true; + } + + if (local_err) { + error_propagate(errp, local_err); + } + + return false; +} + +static void systemd_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"}; + const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == 0) { + return; + } + + if ((status == -1) && !local_err) { + error_setg(errp, "the helper program 'systemctl %s' was not found", + systemctl_args[mode]); + return; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, "the helper program 'systemctl %s' returned an " + "unexpected exit status code (%d)", + systemctl_args[mode], status); + } +} + static bool pmutils_supports_mode(SuspendMode mode, Error **errp) { Error *local_err = NULL; @@ -1660,6 +1717,14 @@ static void bios_supports_mode(SuspendMode mode, Error **errp) Error *local_err = NULL; bool ret; + ret = systemd_supports_mode(mode, &local_err); + if (ret) { + return; + } + if (local_err) { + error_propagate(errp, local_err); + return; + } ret = pmutils_supports_mode(mode, &local_err); if (ret) { return; @@ -1689,6 +1754,13 @@ static void guest_suspend(SuspendMode mode, Error **errp) return; } + systemd_suspend(mode, &local_err); + if (!local_err) { + return; + } + + error_free(local_err); + pmutils_suspend(mode, &local_err); if (!local_err) { return; From 73e1d8eb9b738cef3dee2da26bb669b1092a4c12 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 21 Jun 2018 07:21:53 -0300 Subject: [PATCH 2030/2380] qga: removing bios_supports_mode bios_support_mode verifies if the guest has support for a certain suspend mode but it doesn't inform back which suspend tool provides it. The caller, guest_suspend, executes all suspend strategies in order again. After adding systemd suspend support, bios_support_mode now will verify for support for systemd, then pmutils, then Linux sys state file. In a worst case scenario where both systemd and pmutils isn't supported but Linux sys state is: - bios_supports_mode will check for systemd, then pmutils, then Linux sys state. It will tell guest_suspend that there is support, but it will not tell who provides it; - guest_suspend will try to execute (and fail) systemd suspend, then pmutils suspend, to only then use the Linux sys suspend. The time spent executing systemd and pmutils suspend was wasted and could be avoided, but only bios_support_mode knew it but didn't inform it back. A quicker approach is to nuke bios_supports_mode and control whether we found support at all with a bool flag inside guest_suspend. guest_suspend will search for suspend support and execute it as soon as possible. If the a given suspend mechanism fails, continue to the next. If no suspend support is found, the "not supported" message is still being sent back to the user. Signed-off-by: Daniel Henrique Barboza Signed-off-by: Michael Roth --- qga/commands-posix.c | 58 ++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 40 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index cdb825993f..233f78a406 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1712,64 +1712,42 @@ static void linux_sys_state_suspend(SuspendMode mode, Error **errp) } -static void bios_supports_mode(SuspendMode mode, Error **errp) -{ - Error *local_err = NULL; - bool ret; - - ret = systemd_supports_mode(mode, &local_err); - if (ret) { - return; - } - if (local_err) { - error_propagate(errp, local_err); - return; - } - ret = pmutils_supports_mode(mode, &local_err); - if (ret) { - return; - } - if (local_err) { - error_propagate(errp, local_err); - return; - } - ret = linux_sys_state_supports_mode(mode, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (!ret) { - error_setg(errp, - "the requested suspend mode is not supported by the guest"); - } -} - static void guest_suspend(SuspendMode mode, Error **errp) { Error *local_err = NULL; + bool mode_supported = false; - bios_supports_mode(mode, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; + if (systemd_supports_mode(mode, &local_err)) { + mode_supported = true; + systemd_suspend(mode, &local_err); } - systemd_suspend(mode, &local_err); if (!local_err) { return; } error_free(local_err); - pmutils_suspend(mode, &local_err); + if (pmutils_supports_mode(mode, &local_err)) { + mode_supported = true; + pmutils_suspend(mode, &local_err); + } + if (!local_err) { return; } error_free(local_err); - linux_sys_state_suspend(mode, &local_err); - if (local_err) { + if (linux_sys_state_supports_mode(mode, &local_err)) { + mode_supported = true; + linux_sys_state_suspend(mode, &local_err); + } + + if (!mode_supported) { + error_setg(errp, + "the requested suspend mode is not supported by the guest"); + } else if (local_err) { error_propagate(errp, local_err); } } From 71696cc66ce74a645e68844340e9fb5cfd598cc2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:29 +0200 Subject: [PATCH 2031/2380] docs/interop/qmp: Improve OOB documentation OOB documentation is spread over qmp-spec.txt sections 2.2.1 Capabilities and 2.3 Issuing Commands. The amount of detail is a bit distracting there. Move the meat of the matter to new section 2.3.1 Out of band execution. Throw in a few other improvements while there: * 2.2 Server Greeting: Drop advice to search entire capabilities array; should be obvious. * 3. QMP Examples - 3.1 Server Greeting: Update greeting to the one we expect for the release. Now shows capability "oob". Update qmp-intro.txt likewise. - 3.2 Capabilities negotiation: Show client accepting capability "oob". - 3.7 Out-of-band execution: New. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-4-armbru@redhat.com> [Whitespace tidied up] --- docs/interop/qmp-intro.txt | 13 +++---- docs/interop/qmp-spec.txt | 74 +++++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 31 deletions(-) diff --git a/docs/interop/qmp-intro.txt b/docs/interop/qmp-intro.txt index 900d69d612..9d54a718b8 100644 --- a/docs/interop/qmp-intro.txt +++ b/docs/interop/qmp-intro.txt @@ -52,13 +52,14 @@ Escape character is '^]'. "QMP": { "version": { "qemu": { - "micro": 50, - "minor": 6, - "major": 1 - }, - "package": "" - }, + "micro": 0, + "minor": 0, + "major": 3 + }, + "package": "v3.0.0" + }, "capabilities": [ + "oob" ] } } diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt index 2bb492d1ea..6b72b69cb1 100644 --- a/docs/interop/qmp-spec.txt +++ b/docs/interop/qmp-spec.txt @@ -77,8 +77,7 @@ The greeting message format is: is the same of the query-version command) - The "capabilities" member specify the availability of features beyond the baseline specification; the order of elements in this array has no - particular significance, so a client must search the entire array - when looking for a particular capability + particular significance. 2.2.1 Capabilities ------------------ @@ -86,16 +85,7 @@ The greeting message format is: Currently supported capabilities are: - "oob": the QMP server supports "out-of-band" (OOB) command - execution. For more details, please see the "run-oob" parameter in - the "Issuing Commands" section below. Not all commands allow this - "oob" execution. The "query-qmp-schema" command can be used to - inspect which commands support "oob" execution. - -QMP clients can get a list of supported QMP capabilities of the QMP -server in the greeting message mentioned above. By default, all the -capabilities are off. To enable any QMP capabilities, the QMP client -needs to send the "qmp_capabilities" command with an extra parameter -for the requested capabilities. + execution, as described in section "2.3.1 Out-of-band execution". 2.3 Issuing Commands -------------------- @@ -115,14 +105,38 @@ The format for command execution is: - The "id" member is a transaction identification associated with the command execution. It is required for all commands if the OOB - capability was enabled at startup, and optional otherwise. The same - "id" field will be part of the response if provided. The "id" member - can be any json-value, although most clients merely use a - json-number incremented for each successive command -- The "control" member is optional, and currently only used for - out-of-band execution. The handling or response of an "oob" command - can overtake prior in-band commands. To enable "oob" handling of a - particular command, just provide a control field with: { "control": - { "run-oob": true } } + "id" field will be part of the response if provided. The "id" + member can be any json-value. A json-number incremented for each + successive command works fine. +- The optional "control" member further specifies how the command is + to be executed. Currently, its only member is optional "run-oob". + See section "2.3.1 Out-of-band execution" for details. + + +2.3.1 Out-of-band execution +--------------------------- + +The server normally reads, executes and responds to one command after +the other. The client therefore receives command responses in issue +order. + +With out-of-band execution enabled via capability negotiation (section +4.), the server reads and queues commands as they arrive. It executes +commands from the queue one after the other. Commands executed +out-of-band jump the queue: the command get executed right away, +possibly overtaking prior in-band commands. The client may therefore +receive such a command's response before responses from prior in-band +commands. + +To execute a command out-of-band, the client puts "run-oob": true into +execute's member "control". + +If the client sends in-band commands faster than the server can +execute them, the server will eventually drop commands to limit the +queue length. The sever sends event COMMAND_DROPPED then. + +Only a few commands support out-of-band execution. The ones that do +have "allow-oob": true in output of query-qmp-schema. 2.4 Commands Responses ---------------------- @@ -223,12 +237,13 @@ This section provides some examples of real QMP usage, in all of them 3.1 Server greeting ------------------- -S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 }, - "package": ""}, "capabilities": []}} +S: { "QMP": {"version": {"qemu": {"micro": 0, "minor": 0, "major": 3}, + "package": "v3.0.0"}, "capabilities": ["oob"] } } -3.2 Client QMP negotiation --------------------------- -C: { "execute": "qmp_capabilities" } +3.2 Capabilities negotiation +---------------------------- + +C: { "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } } S: { "return": {}} 3.3 Simple 'stop' execution @@ -255,6 +270,15 @@ S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } } S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 }, "event": "POWERDOWN" } +3.7 Out-of-band execution +------------------------- + +C: { "execute": "migrate-pause", "id": 42, "control": { "run-oob": true } } +S: { "id": 42, + "error": { "class": "GenericError", + "desc": "migrate-pause is currently only supported during postcopy-active state" } } + + 4. Capabilities Negotiation =========================== From d621cfe0a177978b17711a712293221294430f53 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:30 +0200 Subject: [PATCH 2032/2380] qmp: Document COMMAND_DROPPED design flaw Events are broadcast to all monitors. If another monitor's client has a command with the same ID in flight, the event will incorrectly claim that command was dropped. This must be fixed before out-of-band execution can graduate from "experimental". Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-5-armbru@redhat.com> --- monitor.c | 6 ++++++ qapi/misc.json | 3 +++ 2 files changed, 9 insertions(+) diff --git a/monitor.c b/monitor.c index 4f4f309d45..3ad89fe1ba 100644 --- a/monitor.c +++ b/monitor.c @@ -4331,6 +4331,12 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) /* Drop the request if queue is full. */ if (mon->qmp.qmp_requests->length >= QMP_REQ_QUEUE_LEN_MAX) { qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); + /* + * FIXME @id's scope is just @mon, and broadcasting it is + * wrong. If another monitor's client has a command with + * the same ID in flight, the event will incorrectly claim + * that command was dropped. + */ qapi_event_send_command_dropped(id, COMMAND_DROP_REASON_QUEUE_FULL, &error_abort); diff --git a/qapi/misc.json b/qapi/misc.json index 0446c3e48e..74cd97f237 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -3454,6 +3454,9 @@ # only be dropped when the oob capability is enabled. # # @id: The dropped command's "id" field. +# FIXME Broken by design. Events are broadcast to all monitors. If +# another monitor's client has a command with the same ID in flight, +# the event will incorrectly claim that command was dropped. # # @reason: The reason why the command is dropped. # From 97ca0712c8f2b65479649693ffe01e7358418955 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:31 +0200 Subject: [PATCH 2033/2380] qmp: Get rid of x-oob-test command tests/qmp-test tests an out-of-band command overtaking a slow in-band command. To do that, it needs: 1. An in-band command that *reliably* takes long enough to be overtaken. 2. An out-of-band command to do the overtaking. 3. To avoid delays, a way to make the in-band command complete quickly after it was overtaken. To satisfy these needs, commit 469638f9cb3 provides the rather peculiar oob-capable QMP command x-oob-test: * With "lock": true, it waits for a global semaphore. * With "lock": false, it signals the global semaphore. To satisfy 1., the test runs x-oob-test in-band with "lock": true. To satisfy 2. and 3., it runs x-oob-test out-of-band with "lock": false. Note that waiting for a semaphore violates the rules for oob-capable commands. Running x-oob-test with "lock": true hangs the monitor until you run x-oob-test with "lock": false on another monitor (which you might not have set up). Having an externally visible QMP command that may hang the monitor is not nice. Let's apply a little more ingenuity to the problem. Idea: have an existing command block on reading a FIFO special file, unblock it by opening the FIFO for writing. For 1., use {"execute": "blockdev-add", "id": ID1, "arguments": { "driver": "blkdebug", "node-name": ID1, "config": FIFO, "image": { "driver": "null-co"}}} where ID1 is an arbitrary string, and FIFO is the name of the FIFO. For 2., use {"execute": "migrate-pause", "id": ID2, "control": {"run-oob": true}} where ID2 is a different arbitrary string. Since there's no migration to pause, the command will fail, but that's fine; instant failure is still a test of out-of-band responses overtaking in-band commands. For 3., open FIFO for writing. Drop QMP command x-oob-test. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-6-armbru@redhat.com> [Error checking tweaked] --- qapi/misc.json | 18 --------- qmp.c | 16 -------- tests/qmp-test.c | 95 +++++++++++++++++++++++++++++++++--------------- 3 files changed, 65 insertions(+), 64 deletions(-) diff --git a/qapi/misc.json b/qapi/misc.json index 74cd97f237..f1860418e8 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -3472,24 +3472,6 @@ { 'event': 'COMMAND_DROPPED' , 'data': { 'id': 'any', 'reason': 'CommandDropReason' } } -## -# @x-oob-test: -# -# Test OOB functionality. When sending this command with lock=true, -# it'll try to hang the dispatcher. When sending it with lock=false, -# it'll try to notify the locked thread to continue. Note: it should -# only be used by QMP test program rather than anything else. -# -# Since: 2.12 -# -# Example: -# -# { "execute": "x-oob-test", -# "arguments": { "lock": true } } -## -{ 'command': 'x-oob-test', 'data' : { 'lock': 'bool' }, - 'allow-oob': true } - ## # @set-numa-node: # diff --git a/qmp.c b/qmp.c index 73e46d795f..62325ac6aa 100644 --- a/qmp.c +++ b/qmp.c @@ -775,19 +775,3 @@ MemoryInfo *qmp_query_memory_size_summary(Error **errp) return mem_info; } - -static QemuSemaphore x_oob_test_sem; - -static void __attribute__((constructor)) x_oob_test_init(void) -{ - qemu_sem_init(&x_oob_test_sem, 0); -} - -void qmp_x_oob_test(bool lock, Error **errp) -{ - if (lock) { - qemu_sem_wait(&x_oob_test_sem); - } else { - qemu_sem_post(&x_oob_test_sem); - } -} diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 5206b14ca6..467b694aa2 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -135,16 +135,66 @@ static void test_qmp_protocol(void) qtest_quit(qts); } -/* Tests for out-of-band support. */ +/* Out-of-band tests */ + +char tmpdir[] = "/tmp/qmp-test-XXXXXX"; +char *fifo_name; + +static void setup_blocking_cmd(void) +{ + if (!mkdtemp(tmpdir)) { + g_error("mkdtemp: %s", strerror(errno)); + } + fifo_name = g_strdup_printf("%s/fifo", tmpdir); + if (mkfifo(fifo_name, 0666)) { + g_error("mkfifo: %s", strerror(errno)); + } +} + +static void cleanup_blocking_cmd(void) +{ + unlink(fifo_name); + rmdir(tmpdir); +} + +static void send_cmd_that_blocks(QTestState *s, const char *id) +{ + qtest_async_qmp(s, "{ 'execute': 'blockdev-add', 'id': %s," + " 'arguments': {" + " 'driver': 'blkdebug', 'node-name': %s," + " 'config': %s," + " 'image': { 'driver': 'null-co' } } }", + id, id, fifo_name); +} + +static void unblock_blocked_cmd(void) +{ + int fd = open(fifo_name, O_WRONLY); + g_assert(fd >= 0); + close(fd); +} + +static void send_oob_cmd_that_fails(QTestState *s, const char *id) +{ + qtest_async_qmp(s, "{ 'execute': 'migrate-pause', 'id': %s," + " 'control': { 'run-oob': true } }", id); +} + +static void recv_cmd_id(QTestState *s, const char *id) +{ + QDict *resp = qtest_qmp_receive(s); + + g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id); + qobject_unref(resp); +} + static void test_qmp_oob(void) { QTestState *qts; QDict *resp, *q; - int acks = 0; const QListEntry *entry; QList *capabilities; QString *qstr; - const char *cmd_id; qts = qtest_init_without_qmp_handshake(true, common_args); @@ -185,37 +235,20 @@ static void test_qmp_oob(void) g_assert(qdict_haskey(resp, "error")); qobject_unref(resp); - /* - * First send the "x-oob-test" command with lock=true and - * oob=false, it should hang the dispatcher and main thread; - * later, we send another lock=false with oob=true to continue - * that thread processing. Finally we should receive replies from - * both commands. - */ - qtest_async_qmp(qts, - "{ 'execute': 'x-oob-test'," - " 'arguments': { 'lock': true }, " - " 'id': 'lock-cmd'}"); - qtest_async_qmp(qts, - "{ 'execute': 'x-oob-test', " - " 'arguments': { 'lock': false }, " - " 'control': { 'run-oob': true }, " - " 'id': 'unlock-cmd' }"); - - /* Ignore all events. Wait for 2 acks */ - while (acks < 2) { - resp = qtest_qmp_receive(qts); - cmd_id = qdict_get_str(resp, "id"); - if (!g_strcmp0(cmd_id, "lock-cmd") || - !g_strcmp0(cmd_id, "unlock-cmd")) { - acks++; - } - qobject_unref(resp); - } + /* OOB command overtakes slow in-band command */ + setup_blocking_cmd(); + send_cmd_that_blocks(qts, "ib-blocks-1"); + send_oob_cmd_that_fails(qts, "oob-1"); + recv_cmd_id(qts, "oob-1"); + unblock_blocked_cmd(); + recv_cmd_id(qts, "ib-blocks-1"); + cleanup_blocking_cmd(); qtest_quit(qts); } +/* Query smoke tests */ + static int query_error_class(const char *cmd) { static struct { @@ -392,6 +425,8 @@ static void add_query_tests(QmpSchema *schema) } } +/* Preconfig tests */ + static void test_qmp_preconfig(void) { QDict *rsp, *ret; From 2970b4461f5f0dd0798774618713aadd457e8d48 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:32 +0200 Subject: [PATCH 2034/2380] tests/qmp-test: Test in-band command doesn't overtake Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-7-armbru@redhat.com> --- tests/qmp-test.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 467b694aa2..c9d01b87ca 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -238,10 +238,12 @@ static void test_qmp_oob(void) /* OOB command overtakes slow in-band command */ setup_blocking_cmd(); send_cmd_that_blocks(qts, "ib-blocks-1"); + qtest_async_qmp(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }"); send_oob_cmd_that_fails(qts, "oob-1"); recv_cmd_id(qts, "oob-1"); unblock_blocked_cmd(); recv_cmd_id(qts, "ib-blocks-1"); + recv_cmd_id(qts, "ib-quick-1"); cleanup_blocking_cmd(); qtest_quit(qts); From 80cd93bd966bbb0907caa7f1d5676342f27f8f9e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:33 +0200 Subject: [PATCH 2035/2380] qmp: Make "id" optional again even in "oob" monitors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit cf869d53172 "qmp: support out-of-band (oob) execution" made "id" mandatory for all commands when the client accepted capability "oob". This is rather onerous when you play with QMP by hand, and unnecessarily so: only out-of-band commands need an ID for reliable matching of response to command. Revert that part of commit cf869d53172 for now, but have documentation advise on the need to use "id" with out-of-band commands. Signed-off-by: Markus Armbruster Message-Id: <20180703085358.13941-8-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Eric Blake --- docs/interop/qmp-spec.txt | 13 +++++++------ monitor.c | 7 ------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt index 6b72b69cb1..a1d6f9ee06 100644 --- a/docs/interop/qmp-spec.txt +++ b/docs/interop/qmp-spec.txt @@ -103,16 +103,13 @@ The format for command execution is: required. Each command documents what contents will be considered valid when handling the json-argument - The "id" member is a transaction identification associated with the - command execution. It is required for all commands if the OOB - - capability was enabled at startup, and optional otherwise. The same - "id" field will be part of the response if provided. The "id" - member can be any json-value. A json-number incremented for each - successive command works fine. + command execution, it is optional and will be part of the response + if provided. The "id" member can be any json-value. A json-number + incremented for each successive command works fine. - The optional "control" member further specifies how the command is to be executed. Currently, its only member is optional "run-oob". See section "2.3.1 Out-of-band execution" for details. - 2.3.1 Out-of-band execution --------------------------- @@ -128,6 +125,10 @@ possibly overtaking prior in-band commands. The client may therefore receive such a command's response before responses from prior in-band commands. +To be able to match responses back to their commands, the client needs +to pass "id" with out-of-band commands. Passing it with all commands +is recommended for clients that accept capability "oob". + To execute a command out-of-band, the client puts "run-oob": true into execute's member "control". diff --git a/monitor.c b/monitor.c index 3ad89fe1ba..0dd6e22463 100644 --- a/monitor.c +++ b/monitor.c @@ -4292,13 +4292,6 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) id = qdict_get(qdict, "id"); - /* When OOB is enabled, the "id" field is mandatory. */ - if (qmp_oob_enabled(mon) && !id) { - error_setg(&err, "Out-of-band capability requires that " - "every command contains an 'id' field"); - goto err; - } - req_obj = g_new0(QMPRequest, 1); req_obj->mon = mon; req_obj->id = qobject_ref(id); From b5f8431040549da931b31892cbbde73b8724ff48 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:34 +0200 Subject: [PATCH 2036/2380] tests/test-qga: Demonstrate the guest-agent ignores "id" Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-9-armbru@redhat.com> --- tests/test-qga.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test-qga.c b/tests/test-qga.c index 30c9643257..6b632e3da4 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -227,6 +227,25 @@ static void test_qga_ping(gconstpointer fix) qobject_unref(ret); } +static void test_qga_invalid_id(gconstpointer fix) +{ + /* + * FIXME "id" is ignored; it should either be repeated in the + * reply, or rejected on input + */ + const TestFixture *fixture = fix; + QDict *ret, *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + g_assert(!qdict_haskey(val, "id")); + + qobject_unref(ret); +} + static void test_qga_invalid_args(gconstpointer fix) { const TestFixture *fixture = fix; @@ -934,6 +953,7 @@ int main(int argc, char **argv) g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); + g_test_add_data_func("/qga/invalid-id", &fix, test_qga_invalid_id); g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); g_test_add_data_func("/qga/fsfreeze-status", &fix, From 0fa39d0b0374b983535de8591e5e561401d1d5c6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:35 +0200 Subject: [PATCH 2037/2380] qmp qemu-ga: Revert change that accidentally made qemu-ga accept "id" Commit cf869d53172 "qmp: support out-of-band (oob) execution" changed how we check "id": Note that in the patch I exported qmp_dispatch_check_obj() to be used to check the request earlier, and at the same time allowed "id" field to be there since actually we always allow that. The part after "and" is ill-advised: it makes qemu-ga accept and ignore "id". Revert. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-10-armbru@redhat.com> --- monitor.c | 14 ++++++++------ qapi/qmp-dispatch.c | 2 -- tests/test-qga.c | 13 +++++-------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/monitor.c b/monitor.c index 0dd6e22463..7245ee37ce 100644 --- a/monitor.c +++ b/monitor.c @@ -4264,7 +4264,7 @@ static void monitor_qmp_bh_dispatcher(void *data) static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) { QObject *req, *id = NULL; - QDict *qdict = NULL; + QDict *qdict; MonitorQMP *mon_qmp = container_of(parser, MonitorQMP, parser); Monitor *mon = container_of(mon_qmp, Monitor, qmp); Error *err = NULL; @@ -4279,6 +4279,12 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) goto err; } + qdict = qobject_to(QDict, req); + if (qdict) { + id = qobject_ref(qdict_get(qdict, "id")); + qdict_del(qdict, "id"); + } /* else will fail qmp_dispatch() */ + /* Check against the request in general layout */ qdict = qmp_dispatch_check_obj(req, &err); if (!qdict) { @@ -4290,16 +4296,12 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) goto err; } - id = qdict_get(qdict, "id"); - req_obj = g_new0(QMPRequest, 1); req_obj->mon = mon; - req_obj->id = qobject_ref(id); + req_obj->id = id; req_obj->req = req; req_obj->need_resume = false; - qdict_del(qdict, "id"); - if (qmp_is_oob(qdict)) { /* Out-of-band (OOB) requests are executed directly in parser. */ trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(req_obj->id) diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 935f9e159c..3d5d5e110f 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -52,8 +52,6 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) "QMP input member 'arguments' must be an object"); return NULL; } - } else if (!strcmp(arg_name, "id")) { - continue; } else if (!strcmp(arg_name, "control")) { if (qobject_type(arg_obj) != QTYPE_QDICT) { error_setg(errp, diff --git a/tests/test-qga.c b/tests/test-qga.c index 6b632e3da4..564a4594b5 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -229,19 +229,16 @@ static void test_qga_ping(gconstpointer fix) static void test_qga_invalid_id(gconstpointer fix) { - /* - * FIXME "id" is ignored; it should either be repeated in the - * reply, or rejected on input - */ const TestFixture *fixture = fix; - QDict *ret, *val; + QDict *ret, *error; + const char *class; ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}"); g_assert_nonnull(ret); - qmp_assert_no_error(ret); - val = qdict_get_qdict(ret, "return"); - g_assert(!qdict_haskey(val, "id")); + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + g_assert_cmpstr(class, ==, "GenericError"); qobject_unref(ret); } From d4d7ed731ce47d10ea2a17d663cec42fc0c7d925 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:36 +0200 Subject: [PATCH 2038/2380] tests/test-qga: Demonstrate the guest-agent ignores "control" Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-11-armbru@redhat.com> --- tests/test-qga.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test-qga.c b/tests/test-qga.c index 564a4594b5..2e9e0f73bb 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -243,6 +243,22 @@ static void test_qga_invalid_id(gconstpointer fix) qobject_unref(ret); } +static void test_qga_invalid_oob(gconstpointer fix) +{ + /* FIXME "control" is ignored; it should be rejected */ + const TestFixture *fixture = fix; + QDict *ret; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'," + " 'control': {'run-oob': true}}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + qdict_get_qdict(ret, "return"); + + qobject_unref(ret); +} + static void test_qga_invalid_args(gconstpointer fix) { const TestFixture *fixture = fix; @@ -951,6 +967,7 @@ int main(int argc, char **argv) g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); g_test_add_data_func("/qga/invalid-id", &fix, test_qga_invalid_id); + g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob); g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); g_test_add_data_func("/qga/fsfreeze-status", &fix, From 674ed7228f03150d15703961ea2a59cd744f3beb Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:37 +0200 Subject: [PATCH 2039/2380] qmp qemu-ga: Fix qemu-ga not to accept "control" Commit cf869d53172 "qmp: support out-of-band (oob) execution" accidentally made qemu-ga accept and ignore "control". Fix that. Out-of-band execution in a monitor that doesn't support it now fails with {"error": {"class": "GenericError", "desc": "QMP input member 'control' is unexpected"}} instead of {"error": {"class": "GenericError", "desc": "Please enable out-of-band first for the session during capabilities negotiation"}} The old description is suboptimal when out-of-band cannot not be enabled, or the command doesn't support out-of-band execution. The new description is a bit unspecific, but it'll do. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-12-armbru@redhat.com> --- include/qapi/qmp/dispatch.h | 6 ++++-- monitor.c | 9 ++------- qapi/qmp-dispatch.c | 14 ++++++++------ qga/main.c | 2 +- tests/test-qga.c | 9 +++++---- tests/test-qmp-cmds.c | 8 ++++---- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index b366bb48bd..303a15ba84 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -41,7 +41,6 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); void qmp_unregister_command(QmpCommandList *cmds, const char *name); QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name); -QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request); void qmp_disable_command(QmpCommandList *cmds, const char *name); void qmp_enable_command(QmpCommandList *cmds, const char *name); @@ -49,7 +48,10 @@ bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); QObject *qmp_build_error_object(Error *err); -QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp); +QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, + Error **errp); +QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request, + bool allow_oob); bool qmp_is_oob(QDict *dict); typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque); diff --git a/monitor.c b/monitor.c index 7245ee37ce..2e443bba13 100644 --- a/monitor.c +++ b/monitor.c @@ -1319,11 +1319,6 @@ static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp) } if (qmp_is_oob(req)) { - if (!qmp_oob_enabled(mon)) { - error_setg(errp, "Please enable out-of-band first " - "for the session during capabilities negotiation"); - return false; - } if (!(cmd->options & QCO_ALLOW_OOB)) { error_setg(errp, "The command %s does not support OOB", command); @@ -4195,7 +4190,7 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) old_mon = cur_mon; cur_mon = mon; - rsp = qmp_dispatch(mon->qmp.commands, req); + rsp = qmp_dispatch(mon->qmp.commands, req, qmp_oob_enabled(mon)); cur_mon = old_mon; @@ -4286,7 +4281,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) } /* else will fail qmp_dispatch() */ /* Check against the request in general layout */ - qdict = qmp_dispatch_check_obj(req, &err); + qdict = qmp_dispatch_check_obj(req, qmp_oob_enabled(mon), &err); if (!qdict) { goto err; } diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 3d5d5e110f..0ad0fab8ed 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -20,7 +20,8 @@ #include "qapi/qmp/qbool.h" #include "sysemu/sysemu.h" -QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) +QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, + Error **errp) { const QDictEntry *ent; const char *arg_name; @@ -52,7 +53,7 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) "QMP input member 'arguments' must be an object"); return NULL; } - } else if (!strcmp(arg_name, "control")) { + } else if (!strcmp(arg_name, "control") && allow_oob) { if (qobject_type(arg_obj) != QTYPE_QDICT) { error_setg(errp, "QMP input member 'control' must be a dict"); @@ -74,7 +75,7 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) } static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, - Error **errp) + bool allow_oob, Error **errp) { Error *local_err = NULL; const char *command; @@ -82,7 +83,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, QmpCommand *cmd; QObject *ret = NULL; - dict = qmp_dispatch_check_obj(request, errp); + dict = qmp_dispatch_check_obj(request, allow_oob, errp); if (!dict) { return NULL; } @@ -157,13 +158,14 @@ bool qmp_is_oob(QDict *dict) return qbool_get_bool(bool_obj); } -QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request) +QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request, + bool allow_oob) { Error *err = NULL; QObject *ret; QDict *rsp; - ret = do_qmp_dispatch(cmds, request, &err); + ret = do_qmp_dispatch(cmds, request, allow_oob, &err); rsp = qdict_new(); if (err) { diff --git a/qga/main.c b/qga/main.c index ea7540edcc..d332bacce5 100644 --- a/qga/main.c +++ b/qga/main.c @@ -586,7 +586,7 @@ static void process_command(GAState *s, QDict *req) g_assert(req); g_debug("processing command"); - rsp = qmp_dispatch(&ga_commands, QOBJECT(req)); + rsp = qmp_dispatch(&ga_commands, QOBJECT(req), false); if (rsp) { ret = send_response(s, rsp); if (ret < 0) { diff --git a/tests/test-qga.c b/tests/test-qga.c index 2e9e0f73bb..febabc7ad5 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -245,16 +245,17 @@ static void test_qga_invalid_id(gconstpointer fix) static void test_qga_invalid_oob(gconstpointer fix) { - /* FIXME "control" is ignored; it should be rejected */ const TestFixture *fixture = fix; - QDict *ret; + QDict *ret, *error; + const char *class; ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'," " 'control': {'run-oob': true}}"); g_assert_nonnull(ret); - qmp_assert_no_error(ret); - qdict_get_qdict(ret, "return"); + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + g_assert_cmpstr(class, ==, "GenericError"); qobject_unref(ret); } diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 491b0c4a44..10c7ba40c1 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -102,7 +102,7 @@ static void test_dispatch_cmd(void) qdict_put_str(req, "execute", "user_def_cmd"); - resp = qmp_dispatch(&qmp_commands, QOBJECT(req)); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); assert(!qdict_haskey(qobject_to(QDict, resp), "error")); @@ -119,7 +119,7 @@ static void test_dispatch_cmd_failure(void) qdict_put_str(req, "execute", "user_def_cmd2"); - resp = qmp_dispatch(&qmp_commands, QOBJECT(req)); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); assert(qdict_haskey(qobject_to(QDict, resp), "error")); @@ -133,7 +133,7 @@ static void test_dispatch_cmd_failure(void) qdict_put_str(req, "execute", "user_def_cmd"); - resp = qmp_dispatch(&qmp_commands, QOBJECT(req)); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); assert(qdict_haskey(qobject_to(QDict, resp), "error")); @@ -147,7 +147,7 @@ static QObject *test_qmp_dispatch(QDict *req) QDict *resp; QObject *ret; - resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req)); + resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp_obj); resp = qobject_to(QDict, resp_obj); assert(resp && !qdict_haskey(resp, "error")); From 00ecec151d2323e742af94cccf2de77025f3c0c1 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:38 +0200 Subject: [PATCH 2040/2380] qmp: Redo how the client requests out-of-band execution Commit cf869d53172 "qmp: support out-of-band (oob) execution" added a general mechanism for command-independent arguments just for an out-of-band flag: The "control" key is introduced to store this extra flag. "control" field is used to store arguments that are shared by all the commands, rather than command specific arguments. Let "run-oob" be the first. However, it failed to reject unknown members of "control". For instance, in QMP command {"execute": "query-name", "id": 42, "control": {"crap": true}} "crap" gets silently ignored. Instead of fixing this, revert the general "control" mechanism (because YAGNI), and do it the way I initially proposed, with key "exec-oob". Simpler code, simpler interface. An out-of-band command {"execute": "migrate-pause", "id": 42, "control": {"run-oob": true}} becomes {"exec-oob": "migrate-pause", "id": 42} Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-13-armbru@redhat.com> [Commit message typo fixed] --- docs/devel/qapi-code-gen.txt | 10 +++---- docs/interop/qmp-spec.txt | 18 ++++++------- monitor.c | 3 +++ qapi/qmp-dispatch.c | 51 +++++++++++++++--------------------- tests/qmp-test.c | 7 ++--- tests/test-qga.c | 3 +-- 6 files changed, 39 insertions(+), 53 deletions(-) diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 9625798d16..f020f6bab2 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -649,13 +649,11 @@ example: { 'command': 'migrate_recover', 'data': { 'uri': 'str' }, 'allow-oob': true } -To execute a command with out-of-band priority, the client specifies -the "control" field in the request, with "run-oob" set to -true. Example: +To execute a command with out-of-band priority, the client uses key +"exec-oob" instead of "execute". Example: - => { "execute": "command-support-oob", - "arguments": { ... }, - "control": { "run-oob": true } } + => { "exec-oob": "migrate-recover", + "arguments": { "uri": "tcp:192.168.1.200:12345" } } <= { "return": { } } Without it, even the commands that support out-of-band execution will diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt index a1d6f9ee06..1566b8ae5e 100644 --- a/docs/interop/qmp-spec.txt +++ b/docs/interop/qmp-spec.txt @@ -92,12 +92,16 @@ Currently supported capabilities are: The format for command execution is: -{ "execute": json-string, "arguments": json-object, "id": json-value, - "control": json-object } +{ "execute": json-string, "arguments": json-object, "id": json-value } + +or + +{ "exec-oob": json-string, "arguments": json-object, "id": json-value } Where, -- The "execute" member identifies the command to be executed by the Server +- The "execute" or "exec-oob" member identifies the command to be + executed by the server. The latter requests out-of-band execution. - The "arguments" member is used to pass any arguments required for the execution of the command, it is optional when no arguments are required. Each command documents what contents will be considered @@ -106,9 +110,6 @@ The format for command execution is: command execution, it is optional and will be part of the response if provided. The "id" member can be any json-value. A json-number incremented for each successive command works fine. -- The optional "control" member further specifies how the command is - to be executed. Currently, its only member is optional "run-oob". - See section "2.3.1 Out-of-band execution" for details. 2.3.1 Out-of-band execution --------------------------- @@ -129,9 +130,6 @@ To be able to match responses back to their commands, the client needs to pass "id" with out-of-band commands. Passing it with all commands is recommended for clients that accept capability "oob". -To execute a command out-of-band, the client puts "run-oob": true into -execute's member "control". - If the client sends in-band commands faster than the server can execute them, the server will eventually drop commands to limit the queue length. The sever sends event COMMAND_DROPPED then. @@ -274,7 +272,7 @@ S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 }, 3.7 Out-of-band execution ------------------------- -C: { "execute": "migrate-pause", "id": 42, "control": { "run-oob": true } } +C: { "exec-oob": "migrate-pause", "id": 42 } S: { "id": 42, "error": { "class": "GenericError", "desc": "migrate-pause is currently only supported during postcopy-active state" } } diff --git a/monitor.c b/monitor.c index 2e443bba13..3bf3e68bdc 100644 --- a/monitor.c +++ b/monitor.c @@ -1300,6 +1300,9 @@ static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp) QmpCommand *cmd; command = qdict_get_try_str(req, "execute"); + if (!command) { + command = qdict_get_try_str(req, "exec-oob"); + } if (!command) { error_setg(errp, "Command field 'execute' missing"); return false; diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 0ad0fab8ed..12be120fe7 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -23,11 +23,11 @@ QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, Error **errp) { + const char *exec_key = NULL; const QDictEntry *ent; const char *arg_name; const QObject *arg_obj; - bool has_exec_key = false; - QDict *dict = NULL; + QDict *dict; dict = qobject_to(QDict, request); if (!dict) { @@ -40,25 +40,25 @@ QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, arg_name = qdict_entry_key(ent); arg_obj = qdict_entry_value(ent); - if (!strcmp(arg_name, "execute")) { + if (!strcmp(arg_name, "execute") + || (!strcmp(arg_name, "exec-oob") && allow_oob)) { if (qobject_type(arg_obj) != QTYPE_QSTRING) { - error_setg(errp, - "QMP input member 'execute' must be a string"); + error_setg(errp, "QMP input member '%s' must be a string", + arg_name); return NULL; } - has_exec_key = true; + if (exec_key) { + error_setg(errp, "QMP input member '%s' clashes with '%s'", + arg_name, exec_key); + return NULL; + } + exec_key = arg_name; } else if (!strcmp(arg_name, "arguments")) { if (qobject_type(arg_obj) != QTYPE_QDICT) { error_setg(errp, "QMP input member 'arguments' must be an object"); return NULL; } - } else if (!strcmp(arg_name, "control") && allow_oob) { - if (qobject_type(arg_obj) != QTYPE_QDICT) { - error_setg(errp, - "QMP input member 'control' must be a dict"); - return NULL; - } } else { error_setg(errp, "QMP input member '%s' is unexpected", arg_name); @@ -66,7 +66,7 @@ QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, } } - if (!has_exec_key) { + if (!exec_key) { error_setg(errp, "QMP input lacks member 'execute'"); return NULL; } @@ -88,7 +88,11 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, return NULL; } - command = qdict_get_str(dict, "execute"); + command = qdict_get_try_str(dict, "execute"); + if (!command) { + assert(allow_oob); + command = qdict_get_str(dict, "exec-oob"); + } cmd = qmp_find_command(cmds, command); if (cmd == NULL) { error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, @@ -137,25 +141,12 @@ QObject *qmp_build_error_object(Error *err) } /* - * Detect whether a request should be run out-of-band, by quickly - * peeking at whether we have: { "control": { "run-oob": true } }. By - * default commands are run in-band. + * Does @qdict look like a command to be run out-of-band? */ bool qmp_is_oob(QDict *dict) { - QBool *bool_obj; - - dict = qdict_get_qdict(dict, "control"); - if (!dict) { - return false; - } - - bool_obj = qobject_to(QBool, qdict_get(dict, "run-oob")); - if (!bool_obj) { - return false; - } - - return qbool_get_bool(bool_obj); + return qdict_haskey(dict, "exec-oob") + && !qdict_haskey(dict, "execute"); } QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request, diff --git a/tests/qmp-test.c b/tests/qmp-test.c index c9d01b87ca..a7b6ec98e4 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -176,8 +176,7 @@ static void unblock_blocked_cmd(void) static void send_oob_cmd_that_fails(QTestState *s, const char *id) { - qtest_async_qmp(s, "{ 'execute': 'migrate-pause', 'id': %s," - " 'control': { 'run-oob': true } }", id); + qtest_async_qmp(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id); } static void recv_cmd_id(QTestState *s, const char *id) @@ -229,9 +228,7 @@ static void test_qmp_oob(void) * Try any command that does not support OOB but with OOB flag. We * should get failure. */ - resp = qtest_qmp(qts, - "{ 'execute': 'query-cpus'," - " 'control': { 'run-oob': true } }"); + resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }"); g_assert(qdict_haskey(resp, "error")); qobject_unref(resp); diff --git a/tests/test-qga.c b/tests/test-qga.c index febabc7ad5..daadf22ea3 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -249,8 +249,7 @@ static void test_qga_invalid_oob(gconstpointer fix) QDict *ret, *error; const char *class; - ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'," - " 'control': {'run-oob': true}}"); + ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}"); g_assert_nonnull(ret); error = qdict_get_qdict(ret, "error"); From 45434ba47b3e3ccc2ac76674bb3988a152554bf2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:39 +0200 Subject: [PATCH 2041/2380] qmp: Revert change to handle_qmp_command tracepoint Commit 71da4667db6 "monitor: separate QMP parser and dispatcher" moved the handle_qmp_command tracepoint from handle_qmp_command() to monitor_qmp_dispatch_one(). This delays tracing from enqueue time to dequeue time. Revert that. Dequeue remains adequately visible via tracepoint monitor_qmp_cmd_in_band. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-14-armbru@redhat.com> --- monitor.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/monitor.c b/monitor.c index 3bf3e68bdc..3cc4b07788 100644 --- a/monitor.c +++ b/monitor.c @@ -4184,12 +4184,6 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) g_free(req_obj); - if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) { - QString *req_json = qobject_to_json(req); - trace_handle_qmp_command(mon, qstring_get_str(req_json)); - qobject_unref(req_json); - } - old_mon = cur_mon; cur_mon = mon; @@ -4283,6 +4277,12 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) qdict_del(qdict, "id"); } /* else will fail qmp_dispatch() */ + if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) { + QString *req_json = qobject_to_json(req); + trace_handle_qmp_command(mon, qstring_get_str(req_json)); + qobject_unref(req_json); + } + /* Check against the request in general layout */ qdict = qmp_dispatch_check_obj(req, qmp_oob_enabled(mon), &err); if (!qdict) { From 05f7274c8bd4a958f6673d28bf9bca0a9cf09c76 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:40 +0200 Subject: [PATCH 2042/2380] qmp: Always free QMPRequest with qmp_request_free() monitor_qmp_dispatch_one() frees a QMPRequest manually, because it needs to keep a reference to ->id. Premature optimization. Take an additional reference so we can use qmp_request_free(). Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-15-armbru@redhat.com> --- monitor.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/monitor.c b/monitor.c index 3cc4b07788..875647f992 100644 --- a/monitor.c +++ b/monitor.c @@ -4182,8 +4182,6 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) id = req_obj->id; need_resume = req_obj->need_resume; - g_free(req_obj); - old_mon = cur_mon; cur_mon = mon; @@ -4192,14 +4190,14 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) cur_mon = old_mon; /* Respond if necessary */ - monitor_qmp_respond(mon, rsp, NULL, id); + monitor_qmp_respond(mon, rsp, NULL, qobject_ref(id)); /* This pairs with the monitor_suspend() in handle_qmp_command(). */ if (need_resume) { monitor_resume(mon); } - qobject_unref(req); + qmp_request_free(req_obj); } /* From b27314567d4cd8e204a18feba60d3341fb2d1aed Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:41 +0200 Subject: [PATCH 2043/2380] qmp: Simplify code around monitor_qmp_dispatch_one() Change monitor_qmp_dispatch_one() to take its parameters unwrapped, move monitor_resume() to the one caller that needs it, rename the function to monitor_qmp_dispatch(). Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-16-armbru@redhat.com> --- monitor.c | 58 +++++++++++++++++++++++-------------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/monitor.c b/monitor.c index 875647f992..37ca4e798d 100644 --- a/monitor.c +++ b/monitor.c @@ -4167,20 +4167,10 @@ static void monitor_qmp_respond(Monitor *mon, QObject *rsp, qobject_unref(rsp); } -/* - * Dispatch one single QMP request. The function will free the req_obj - * and objects inside it before return. - */ -static void monitor_qmp_dispatch_one(QMPRequest *req_obj) +static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) { - Monitor *mon, *old_mon; - QObject *req, *rsp = NULL, *id; - bool need_resume; - - req = req_obj->req; - mon = req_obj->mon; - id = req_obj->id; - need_resume = req_obj->need_resume; + Monitor *old_mon; + QObject *rsp; old_mon = cur_mon; cur_mon = mon; @@ -4189,15 +4179,7 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) cur_mon = old_mon; - /* Respond if necessary */ monitor_qmp_respond(mon, rsp, NULL, qobject_ref(id)); - - /* This pairs with the monitor_suspend() in handle_qmp_command(). */ - if (need_resume) { - monitor_resume(mon); - } - - qmp_request_free(req_obj); } /* @@ -4241,12 +4223,20 @@ static void monitor_qmp_bh_dispatcher(void *data) { QMPRequest *req_obj = monitor_qmp_requests_pop_any(); - if (req_obj) { - trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); - monitor_qmp_dispatch_one(req_obj); - /* Reschedule instead of looping so the main loop stays responsive */ - qemu_bh_schedule(mon_global.qmp_dispatcher_bh); + if (!req_obj) { + return; } + + trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); + monitor_qmp_dispatch(req_obj->mon, req_obj->req, req_obj->id); + if (req_obj->need_resume) { + /* Pairs with the monitor_suspend() in handle_qmp_command() */ + monitor_resume(req_obj->mon); + } + qmp_request_free(req_obj); + + /* Reschedule instead of looping so the main loop stays responsive */ + qemu_bh_schedule(mon_global.qmp_dispatcher_bh); } #define QMP_REQ_QUEUE_LEN_MAX (8) @@ -4292,20 +4282,20 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) goto err; } + if (qmp_is_oob(qdict)) { + /* Out-of-band (OOB) requests are executed directly in parser. */ + trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) + ?: ""); + monitor_qmp_dispatch(mon, req, id); + return; + } + req_obj = g_new0(QMPRequest, 1); req_obj->mon = mon; req_obj->id = id; req_obj->req = req; req_obj->need_resume = false; - if (qmp_is_oob(qdict)) { - /* Out-of-band (OOB) requests are executed directly in parser. */ - trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(req_obj->id) - ?: ""); - monitor_qmp_dispatch_one(req_obj); - return; - } - /* Protect qmp_requests and fetching its length. */ qemu_mutex_lock(&mon->qmp.qmp_queue_lock); From e8f4a22168f573633f31fad3d6bfcbe5f0259b28 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:42 +0200 Subject: [PATCH 2044/2380] tests/qmp-test: Demonstrate QMP errors jumping the queue When OOB is enabled, out-of-band commands are executed right away, everything else is queued. This lets out-of-band commands "jump the queue". However, certain errors are always reported right away, and therefore can jump the queue even when the erroneous input does not request out-of-band execution. These errors are pretty unlikely to occur in production, but it's wrong all the same. Mark FIXME. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Peter Xu Message-Id: <20180703085358.13941-17-armbru@redhat.com> --- monitor.c | 1 + tests/qmp-test.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/monitor.c b/monitor.c index 37ca4e798d..c49214cd8f 100644 --- a/monitor.c +++ b/monitor.c @@ -4339,6 +4339,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) return; err: + /* FIXME overtakes queued in-band commands, wrong when !qmp_is_oob() */ monitor_qmp_respond(mon, NULL, err, NULL); qobject_unref(req); } diff --git a/tests/qmp-test.c b/tests/qmp-test.c index a7b6ec98e4..9eb20a1a18 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -241,6 +241,13 @@ static void test_qmp_oob(void) unblock_blocked_cmd(); recv_cmd_id(qts, "ib-blocks-1"); recv_cmd_id(qts, "ib-quick-1"); + + /* FIXME certain in-band errors overtake slow in-band command */ + send_cmd_that_blocks(qts, "blocks-2"); + qtest_async_qmp(qts, "{ 'id': 'err-2' }"); + recv_cmd_id(qts, NULL); + unblock_blocked_cmd(); + recv_cmd_id(qts, "blocks-2"); cleanup_blocking_cmd(); qtest_quit(qts); From 69240fe62d1ae02257bc0694a11c478b10378948 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:43 +0200 Subject: [PATCH 2045/2380] qmp: Don't let malformed in-band commands jump the queue handle_qmp_command() reports certain errors right away. This is wrong when OOB is enabled, because the errors can "jump the queue" then, as the previous commit demonstrates. To fix, we need to delay errors until dispatch. Do that for semantic errors, mostly by reverting ill-advised parts of commit cf869d53172 "qmp: support out-of-band (oob) execution". Bonus: doesn't run qmp_dispatch_check_obj() twice, once in handle_qmp_command(), and again in do_qmp_dispatch(). That's also due to commit cf869d53172. The next commit will fix queue jumping for syntax errors. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-18-armbru@redhat.com> --- include/qapi/qmp/dispatch.h | 2 - monitor.c | 79 +++++++++---------------------------- qapi/qmp-dispatch.c | 12 +++++- tests/qmp-test.c | 4 +- 4 files changed, 30 insertions(+), 67 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 303a15ba84..514bfc45b0 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -48,8 +48,6 @@ bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); QObject *qmp_build_error_object(Error *err); -QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, - Error **errp); QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request, bool allow_oob); bool qmp_is_oob(QDict *dict); diff --git a/monitor.c b/monitor.c index c49214cd8f..be2a856d1c 100644 --- a/monitor.c +++ b/monitor.c @@ -1290,48 +1290,6 @@ static void qmp_caps_apply(Monitor *mon, QMPCapabilityList *list) } } -/* - * Return true if check successful, or false otherwise. When false is - * returned, detailed error will be in errp if provided. - */ -static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp) -{ - const char *command; - QmpCommand *cmd; - - command = qdict_get_try_str(req, "execute"); - if (!command) { - command = qdict_get_try_str(req, "exec-oob"); - } - if (!command) { - error_setg(errp, "Command field 'execute' missing"); - return false; - } - - cmd = qmp_find_command(mon->qmp.commands, command); - if (!cmd) { - if (mon->qmp.commands == &qmp_cap_negotiation_commands) { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, - "Expecting capabilities negotiation " - "with 'qmp_capabilities'"); - } else { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, - "The command %s has not been found", command); - } - return false; - } - - if (qmp_is_oob(req)) { - if (!(cmd->options & QCO_ALLOW_OOB)) { - error_setg(errp, "The command %s does not support OOB", - command); - return false; - } - } - - return true; -} - void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, Error **errp) { @@ -4171,6 +4129,7 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) { Monitor *old_mon; QObject *rsp; + QDict *error; old_mon = cur_mon; cur_mon = mon; @@ -4179,6 +4138,19 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) cur_mon = old_mon; + if (mon->qmp.commands == &qmp_cap_negotiation_commands) { + error = qdict_get_qdict(qobject_to(QDict, rsp), "error"); + if (error + && !g_strcmp0(qdict_get_try_str(error, "class"), + QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) { + /* Provide a more useful error message */ + qdict_del(error, "desc"); + qdict_put_str(error, "desc", "Expecting capabilities negotiation" + " with 'qmp_capabilities'"); + } + } + + /* Respond if necessary */ monitor_qmp_respond(mon, rsp, NULL, qobject_ref(id)); } @@ -4256,7 +4228,9 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) error_setg(&err, QERR_JSON_PARSING); } if (err) { - goto err; + assert(!req); + monitor_qmp_respond(mon, NULL, err, NULL); + return; } qdict = qobject_to(QDict, req); @@ -4271,18 +4245,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) qobject_unref(req_json); } - /* Check against the request in general layout */ - qdict = qmp_dispatch_check_obj(req, qmp_oob_enabled(mon), &err); - if (!qdict) { - goto err; - } - - /* Check against OOB specific */ - if (!qmp_cmd_oob_check(mon, qdict, &err)) { - goto err; - } - - if (qmp_is_oob(qdict)) { + if (qdict && qmp_is_oob(qdict)) { /* Out-of-band (OOB) requests are executed directly in parser. */ trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: ""); @@ -4336,12 +4299,6 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) /* Kick the dispatcher routine */ qemu_bh_schedule(mon_global.qmp_dispatcher_bh); - return; - -err: - /* FIXME overtakes queued in-band commands, wrong when !qmp_is_oob() */ - monitor_qmp_respond(mon, NULL, err, NULL); - qobject_unref(req); } static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size) diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 12be120fe7..6d78f3e9f6 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -20,8 +20,8 @@ #include "qapi/qmp/qbool.h" #include "sysemu/sysemu.h" -QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, - Error **errp) +static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, + Error **errp) { const char *exec_key = NULL; const QDictEntry *ent; @@ -78,6 +78,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, bool allow_oob, Error **errp) { Error *local_err = NULL; + bool oob; const char *command; QDict *args, *dict; QmpCommand *cmd; @@ -89,9 +90,11 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, } command = qdict_get_try_str(dict, "execute"); + oob = false; if (!command) { assert(allow_oob); command = qdict_get_str(dict, "exec-oob"); + oob = true; } cmd = qmp_find_command(cmds, command); if (cmd == NULL) { @@ -104,6 +107,11 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, command); return NULL; } + if (oob && !(cmd->options & QCO_ALLOW_OOB)) { + error_setg(errp, "The command %s does not support OOB", + command); + return false; + } if (runstate_check(RUN_STATE_PRECONFIG) && !(cmd->options & QCO_ALLOW_PRECONFIG)) { diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 9eb20a1a18..ceaf4a6789 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -242,12 +242,12 @@ static void test_qmp_oob(void) recv_cmd_id(qts, "ib-blocks-1"); recv_cmd_id(qts, "ib-quick-1"); - /* FIXME certain in-band errors overtake slow in-band command */ + /* Even malformed in-band command fails in-band */ send_cmd_that_blocks(qts, "blocks-2"); qtest_async_qmp(qts, "{ 'id': 'err-2' }"); - recv_cmd_id(qts, NULL); unblock_blocked_cmd(); recv_cmd_id(qts, "blocks-2"); + recv_cmd_id(qts, "err-2"); cleanup_blocking_cmd(); qtest_quit(qts); From 1cc37471525d03f963bc71d724f0dc9eab888fc1 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:44 +0200 Subject: [PATCH 2046/2380] qmp: Don't let JSON errors jump the queue handle_qmp_command() reports JSON syntax errors right away. This is wrong when OOB is enabled, because the errors can "jump the queue" then. The previous commit fixed the same bug for semantic errors, by delaying the checking until dispatch. We can't delay the checking, so delay the reporting. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-19-armbru@redhat.com> --- monitor.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/monitor.c b/monitor.c index be2a856d1c..ea5421399a 100644 --- a/monitor.c +++ b/monitor.c @@ -254,8 +254,12 @@ struct QMPRequest { Monitor *mon; /* "id" field of the request */ QObject *id; - /* Request object to be handled */ + /* + * Request object to be handled or Error to be reported + * (exactly one of them is non-null) + */ QObject *req; + Error *err; /* * Whether we need to resume the monitor afterward. This flag is * used to emulate the old QMP server behavior that the current @@ -360,6 +364,7 @@ static void qmp_request_free(QMPRequest *req) { qobject_unref(req->id); qobject_unref(req->req); + error_free(req->err); g_free(req); } @@ -4199,8 +4204,14 @@ static void monitor_qmp_bh_dispatcher(void *data) return; } - trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); - monitor_qmp_dispatch(req_obj->mon, req_obj->req, req_obj->id); + if (req_obj->req) { + trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); + monitor_qmp_dispatch(req_obj->mon, req_obj->req, req_obj->id); + } else { + assert(req_obj->err); + monitor_qmp_respond(req_obj->mon, NULL, req_obj->err, NULL); + } + if (req_obj->need_resume) { /* Pairs with the monitor_suspend() in handle_qmp_command() */ monitor_resume(req_obj->mon); @@ -4227,11 +4238,6 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) /* json_parser_parse_err() sucks: can fail without setting @err */ error_setg(&err, QERR_JSON_PARSING); } - if (err) { - assert(!req); - monitor_qmp_respond(mon, NULL, err, NULL); - return; - } qdict = qobject_to(QDict, req); if (qdict) { @@ -4257,6 +4263,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) req_obj->mon = mon; req_obj->id = id; req_obj->req = req; + req_obj->err = err; req_obj->need_resume = false; /* Protect qmp_requests and fetching its length. */ From f91dc2a0d05eb211a18b8569ad42fb350dbf0b9f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:45 +0200 Subject: [PATCH 2047/2380] monitor: Rename use_io_thr to use_io_thread Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-20-armbru@redhat.com> --- monitor.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/monitor.c b/monitor.c index ea5421399a..b3c5dcc685 100644 --- a/monitor.c +++ b/monitor.c @@ -207,11 +207,11 @@ struct Monitor { int flags; int suspend_cnt; /* Needs to be accessed atomically */ bool skip_flush; - bool use_io_thr; + bool use_io_thread; /* * State used only in the thread "owning" the monitor. - * If @use_io_thr, this is mon_global.mon_iothread. + * If @use_io_thread, this is mon_global.mon_iothread. * Else, it's the main thread. * These members can be safely accessed without locks. */ @@ -521,7 +521,7 @@ static void monitor_json_emitter_raw(Monitor *mon, static void monitor_json_emitter(Monitor *mon, QObject *data) { - if (mon->use_io_thr) { + if (mon->use_io_thread) { /* * If using I/O thread, we need to queue the item so that I/O * thread will do the rest for us. Take refcount so that @@ -767,7 +767,7 @@ static void monitor_qapi_event_init(void) static void handle_hmp_command(Monitor *mon, const char *cmdline); static void monitor_data_init(Monitor *mon, bool skip_flush, - bool use_io_thr) + bool use_io_thread) { memset(mon, 0, sizeof(Monitor)); qemu_mutex_init(&mon->mon_lock); @@ -776,7 +776,7 @@ static void monitor_data_init(Monitor *mon, bool skip_flush, /* Use *mon_cmds by default. */ mon->cmd_table = mon_cmds; mon->skip_flush = skip_flush; - mon->use_io_thr = use_io_thr; + mon->use_io_thread = use_io_thread; mon->qmp.qmp_requests = g_queue_new(); mon->qmp.qmp_responses = g_queue_new(); } @@ -1271,7 +1271,7 @@ static void qmp_caps_check(Monitor *mon, QMPCapabilityList *list, assert(list->value < QMP_CAPABILITY__MAX); switch (list->value) { case QMP_CAPABILITY_OOB: - if (!mon->use_io_thr) { + if (!mon->use_io_thread) { /* * Out-of-band only works with monitors that are * running on dedicated I/O thread. @@ -4377,7 +4377,7 @@ void monitor_resume(Monitor *mon) * For QMP monitors that are running in I/O thread, let's * kick the thread in case it's sleeping. */ - if (mon->use_io_thr) { + if (mon->use_io_thread) { aio_notify(iothread_get_aio_context(mon_global.mon_iothread)); } } else { @@ -4397,7 +4397,7 @@ static QObject *get_qmp_greeting(Monitor *mon) qmp_marshal_query_version(NULL, &ver, NULL); for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) { - if (!mon->use_io_thr && cap == QMP_CAPABILITY_OOB) { + if (!mon->use_io_thread && cap == QMP_CAPABILITY_OOB) { /* Monitors that are not using I/O thread won't support OOB */ continue; } @@ -4610,9 +4610,9 @@ static void monitor_qmp_setup_handlers_bh(void *opaque) Monitor *mon = opaque; GMainContext *context; - if (mon->use_io_thr) { + if (mon->use_io_thread) { /* - * When use_io_thr is set, we use the global shared dedicated + * When @use_io_thread is set, we use the global shared dedicated * I/O thread for this monitor to handle input/output. */ context = monitor_get_io_context(); @@ -4661,7 +4661,7 @@ void monitor_init(Chardev *chr, int flags) if (monitor_is_qmp(mon)) { qemu_chr_fe_set_echo(&mon->chr, true); json_message_parser_init(&mon->qmp.parser, handle_qmp_command); - if (mon->use_io_thr) { + if (mon->use_io_thread) { /* * Make sure the old iowatch is gone. It's possible when * e.g. the chardev is in client mode, with wait=on. From cab5ad86b41af6deead6f9bd6edc8a2353eb3e90 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:46 +0200 Subject: [PATCH 2048/2380] monitor: Peel off @mon_global wrapper Wrapping global variables in a struct without a use for the wrapper struct buys us nothing but longer lines. Unwrap them. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-21-armbru@redhat.com> --- monitor.c | 60 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/monitor.c b/monitor.c index b3c5dcc685..26de90e4d2 100644 --- a/monitor.c +++ b/monitor.c @@ -211,7 +211,7 @@ struct Monitor { /* * State used only in the thread "owning" the monitor. - * If @use_io_thread, this is mon_global.mon_iothread. + * If @use_io_thread, this is @mon_iothread. * Else, it's the main thread. * These members can be safely accessed without locks. */ @@ -240,14 +240,13 @@ struct Monitor { int mux_out; }; -/* Let's add monitor global variables to this struct. */ -static struct { - IOThread *mon_iothread; - /* Bottom half to dispatch the requests received from I/O thread */ - QEMUBH *qmp_dispatcher_bh; - /* Bottom half to deliver the responses back to clients */ - QEMUBH *qmp_respond_bh; -} mon_global; +IOThread *mon_iothread; + +/* Bottom half to dispatch the requests received from I/O thread */ +QEMUBH *qmp_dispatcher_bh; + +/* Bottom half to deliver the responses back to clients */ +QEMUBH *qmp_respond_bh; struct QMPRequest { /* Owner of the request */ @@ -531,7 +530,7 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) qemu_mutex_lock(&mon->qmp.qmp_queue_lock); g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(data)); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); - qemu_bh_schedule(mon_global.qmp_respond_bh); + qemu_bh_schedule(qmp_respond_bh); } else { /* * If not using monitor I/O thread, then we are in main thread. @@ -4219,7 +4218,7 @@ static void monitor_qmp_bh_dispatcher(void *data) qmp_request_free(req_obj); /* Reschedule instead of looping so the main loop stays responsive */ - qemu_bh_schedule(mon_global.qmp_dispatcher_bh); + qemu_bh_schedule(qmp_dispatcher_bh); } #define QMP_REQ_QUEUE_LEN_MAX (8) @@ -4305,7 +4304,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); /* Kick the dispatcher routine */ - qemu_bh_schedule(mon_global.qmp_dispatcher_bh); + qemu_bh_schedule(qmp_dispatcher_bh); } static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size) @@ -4358,7 +4357,7 @@ int monitor_suspend(Monitor *mon) * Kick I/O thread to make sure this takes effect. It'll be * evaluated again in prepare() of the watch object. */ - aio_notify(iothread_get_aio_context(mon_global.mon_iothread)); + aio_notify(iothread_get_aio_context(mon_iothread)); } trace_monitor_suspend(mon, 1); @@ -4378,7 +4377,7 @@ void monitor_resume(Monitor *mon) * kick the thread in case it's sleeping. */ if (mon->use_io_thread) { - aio_notify(iothread_get_aio_context(mon_global.mon_iothread)); + aio_notify(iothread_get_aio_context(mon_iothread)); } } else { assert(mon->rs); @@ -4516,36 +4515,35 @@ static void sortcmdlist(void) static GMainContext *monitor_get_io_context(void) { - return iothread_get_g_main_context(mon_global.mon_iothread); + return iothread_get_g_main_context(mon_iothread); } static AioContext *monitor_get_aio_context(void) { - return iothread_get_aio_context(mon_global.mon_iothread); + return iothread_get_aio_context(mon_iothread); } static void monitor_iothread_init(void) { - mon_global.mon_iothread = iothread_create("mon_iothread", - &error_abort); + mon_iothread = iothread_create("mon_iothread", &error_abort); /* * This MUST be on main loop thread since we have commands that * have assumption to be run on main loop thread. It would be * nice that one day we can remove this assumption in the future. */ - mon_global.qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(), - monitor_qmp_bh_dispatcher, - NULL); + qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(), + monitor_qmp_bh_dispatcher, + NULL); /* * Unlike the dispatcher BH, this must be run on the monitor I/O * thread, so that monitors that are using I/O thread will make * sure read/write operations are all done on the I/O thread. */ - mon_global.qmp_respond_bh = aio_bh_new(monitor_get_aio_context(), - monitor_qmp_bh_responder, - NULL); + qmp_respond_bh = aio_bh_new(monitor_get_aio_context(), + monitor_qmp_bh_responder, + NULL); } void monitor_init_globals(void) @@ -4702,7 +4700,7 @@ void monitor_cleanup(void) * we need to unregister from chardev below in * monitor_data_destroy(), and chardev is not thread-safe yet */ - iothread_stop(mon_global.mon_iothread); + iothread_stop(mon_iothread); /* * After we have I/O thread to send responses, it's possible that @@ -4723,13 +4721,13 @@ void monitor_cleanup(void) qemu_mutex_unlock(&monitor_lock); /* QEMUBHs needs to be deleted before destroying the I/O thread */ - qemu_bh_delete(mon_global.qmp_dispatcher_bh); - mon_global.qmp_dispatcher_bh = NULL; - qemu_bh_delete(mon_global.qmp_respond_bh); - mon_global.qmp_respond_bh = NULL; + qemu_bh_delete(qmp_dispatcher_bh); + qmp_dispatcher_bh = NULL; + qemu_bh_delete(qmp_respond_bh); + qmp_respond_bh = NULL; - iothread_destroy(mon_global.mon_iothread); - mon_global.mon_iothread = NULL; + iothread_destroy(mon_iothread); + mon_iothread = NULL; } QemuOptsList qemu_mon_opts = { From a193352ff9c7cd2cd07846118bc49921d0f53af8 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:47 +0200 Subject: [PATCH 2049/2380] qobject: New qdict_from_jsonf_nofail() Many uses of qobject_from_jsonf() convert JSON objects. Create new convenience function qdict_from_jsonf_nofail() that includes the conversion to QDict. The next few commits will put it to use. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-22-armbru@redhat.com> --- include/qapi/qmp/qjson.h | 2 ++ qobject/qjson.c | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/qapi/qmp/qjson.h b/include/qapi/qmp/qjson.h index b274ac3a86..43b2ce2f33 100644 --- a/include/qapi/qmp/qjson.h +++ b/include/qapi/qmp/qjson.h @@ -19,6 +19,8 @@ QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2); QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp) GCC_FMT_ATTR(1, 0); +QDict *qdict_from_jsonf_nofail(const char *string, ...) GCC_FMT_ATTR(1, 2); + QString *qobject_to_json(const QObject *obj); QString *qobject_to_json_pretty(const QObject *obj); diff --git a/qobject/qjson.c b/qobject/qjson.c index 9816a65c7d..0df3120202 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -76,6 +76,24 @@ QObject *qobject_from_jsonf(const char *string, ...) return obj; } +/* + * Parse @string as JSON object with %-escapes interpolated. + * Abort on error. Do not use with untrusted @string. + * Return the resulting QDict. It is never null. + */ +QDict *qdict_from_jsonf_nofail(const char *string, ...) +{ + QDict *obj; + va_list ap; + + va_start(ap, string); + obj = qobject_to(QDict, qobject_from_jsonv(string, &ap, &error_abort)); + va_end(ap); + + assert(obj); + return obj; +} + typedef struct ToJsonIterState { int indent; From cee32796cadc9510ee00f029a933009df7a28ae2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:48 +0200 Subject: [PATCH 2050/2380] qmp: De-duplicate error response building All callers of qmp_build_error_object() duplicate the code to wrap it in a response object. Replace it by qmp_error_response() that captures the duplicated code, including error_free(). Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-23-armbru@redhat.com> --- include/qapi/qmp/dispatch.h | 2 +- monitor.c | 7 +------ qapi/qmp-dispatch.c | 20 +++++++++++--------- qga/main.c | 8 ++------ 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 514bfc45b0..a53e11c9b1 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -47,7 +47,7 @@ void qmp_enable_command(QmpCommandList *cmds, const char *name); bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); -QObject *qmp_build_error_object(Error *err); +QDict *qmp_error_response(Error *err); QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request, bool allow_oob); bool qmp_is_oob(QDict *dict); diff --git a/monitor.c b/monitor.c index 26de90e4d2..6284efe33e 100644 --- a/monitor.c +++ b/monitor.c @@ -4107,14 +4107,9 @@ static int monitor_can_read(void *opaque) static void monitor_qmp_respond(Monitor *mon, QObject *rsp, Error *err, QObject *id) { - QDict *qdict = NULL; - if (err) { assert(!rsp); - qdict = qdict_new(); - qdict_put_obj(qdict, "error", qmp_build_error_object(err)); - error_free(err); - rsp = QOBJECT(qdict); + rsp = QOBJECT(qmp_error_response(err)); } if (rsp) { diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 6d78f3e9f6..c85748a33f 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -141,11 +141,15 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, return ret; } -QObject *qmp_build_error_object(Error *err) +QDict *qmp_error_response(Error *err) { - return qobject_from_jsonf("{ 'class': %s, 'desc': %s }", - QapiErrorClass_str(error_get_class(err)), - error_get_pretty(err)); + QDict *rsp; + + rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }", + QapiErrorClass_str(error_get_class(err)), + error_get_pretty(err)); + error_free(err); + return rsp; } /* @@ -166,15 +170,13 @@ QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request, ret = do_qmp_dispatch(cmds, request, allow_oob, &err); - rsp = qdict_new(); if (err) { - qdict_put_obj(rsp, "error", qmp_build_error_object(err)); - error_free(err); + rsp = qmp_error_response(err); } else if (ret) { + rsp = qdict_new(); qdict_put_obj(rsp, "return", ret); } else { - qobject_unref(rsp); - return NULL; + rsp = NULL; } return QOBJECT(rsp); diff --git a/qga/main.c b/qga/main.c index d332bacce5..0e30e30248 100644 --- a/qga/main.c +++ b/qga/main.c @@ -610,15 +610,13 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens) qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err)); if (err || !qdict) { qobject_unref(qdict); - qdict = qdict_new(); if (!err) { g_warning("failed to parse event: unknown error"); error_setg(&err, QERR_JSON_PARSING); } else { g_warning("failed to parse event: %s", error_get_pretty(err)); } - qdict_put_obj(qdict, "error", qmp_build_error_object(err)); - error_free(err); + qdict = qmp_error_response(err); } /* handle host->guest commands */ @@ -627,11 +625,9 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens) } else { if (!qdict_haskey(qdict, "error")) { qobject_unref(qdict); - qdict = qdict_new(); g_warning("unrecognized payload format"); error_setg(&err, QERR_UNSUPPORTED); - qdict_put_obj(qdict, "error", qmp_build_error_object(err)); - error_free(err); + qdict = qmp_error_response(err); } ret = send_response(s, QOBJECT(qdict)); if (ret < 0) { From d43b16945afa8457b03aee543a110c4ff0b7f070 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:49 +0200 Subject: [PATCH 2051/2380] qmp: Use QDict * instead of QObject * for response objects By using the more specific type, we get fewer downcasts. The downcasts are safe, but not obviously so, at least not locally. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-24-armbru@redhat.com> --- include/qapi/qmp/dispatch.h | 4 ++-- monitor.c | 31 ++++++++++++++++--------------- qapi/qmp-dispatch.c | 6 +++--- qga/main.c | 8 ++++---- tests/test-qmp-cmds.c | 17 +++++++---------- 5 files changed, 32 insertions(+), 34 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index a53e11c9b1..4e2e749faf 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -48,8 +48,8 @@ bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); QDict *qmp_error_response(Error *err); -QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request, - bool allow_oob); +QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, + bool allow_oob); bool qmp_is_oob(QDict *dict); typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque); diff --git a/monitor.c b/monitor.c index 6284efe33e..8237b1c916 100644 --- a/monitor.c +++ b/monitor.c @@ -379,7 +379,7 @@ static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon) static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon) { while (!g_queue_is_empty(mon->qmp.qmp_responses)) { - qobject_unref((QObject *)g_queue_pop_head(mon->qmp.qmp_responses)); + qobject_unref((QDict *)g_queue_pop_head(mon->qmp.qmp_responses)); } } @@ -528,7 +528,8 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) * responder thread). */ qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(data)); + g_queue_push_tail(mon->qmp.qmp_responses, + qobject_ref(qobject_to(QDict, data))); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); qemu_bh_schedule(qmp_respond_bh); } else { @@ -542,13 +543,13 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) struct QMPResponse { Monitor *mon; - QObject *data; + QDict *data; }; typedef struct QMPResponse QMPResponse; -static QObject *monitor_qmp_response_pop_one(Monitor *mon) +static QDict *monitor_qmp_response_pop_one(Monitor *mon) { - QObject *data; + QDict *data; qemu_mutex_lock(&mon->qmp.qmp_queue_lock); data = g_queue_pop_head(mon->qmp.qmp_responses); @@ -559,10 +560,10 @@ static QObject *monitor_qmp_response_pop_one(Monitor *mon) static void monitor_qmp_response_flush(Monitor *mon) { - QObject *data; + QDict *data; while ((data = monitor_qmp_response_pop_one(mon))) { - monitor_json_emitter_raw(mon, data); + monitor_json_emitter_raw(mon, QOBJECT(data)); qobject_unref(data); } } @@ -574,7 +575,7 @@ static void monitor_qmp_response_flush(Monitor *mon) static bool monitor_qmp_response_pop_any(QMPResponse *response) { Monitor *mon; - QObject *data = NULL; + QDict *data = NULL; qemu_mutex_lock(&monitor_lock); QTAILQ_FOREACH(mon, &mon_list, entry) { @@ -594,7 +595,7 @@ static void monitor_qmp_bh_responder(void *opaque) QMPResponse response; while (monitor_qmp_response_pop_any(&response)) { - monitor_json_emitter_raw(response.mon, response.data); + monitor_json_emitter_raw(response.mon, QOBJECT(response.data)); qobject_unref(response.data); } } @@ -4104,20 +4105,20 @@ static int monitor_can_read(void *opaque) * 2. rsp, err, and id may be NULL. * 3. If err != NULL then rsp must be NULL. */ -static void monitor_qmp_respond(Monitor *mon, QObject *rsp, +static void monitor_qmp_respond(Monitor *mon, QDict *rsp, Error *err, QObject *id) { if (err) { assert(!rsp); - rsp = QOBJECT(qmp_error_response(err)); + rsp = qmp_error_response(err); } if (rsp) { if (id) { - qdict_put_obj(qobject_to(QDict, rsp), "id", qobject_ref(id)); + qdict_put_obj(rsp, "id", qobject_ref(id)); } - monitor_json_emitter(mon, rsp); + monitor_json_emitter(mon, QOBJECT(rsp)); } qobject_unref(id); @@ -4127,7 +4128,7 @@ static void monitor_qmp_respond(Monitor *mon, QObject *rsp, static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) { Monitor *old_mon; - QObject *rsp; + QDict *rsp; QDict *error; old_mon = cur_mon; @@ -4138,7 +4139,7 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) cur_mon = old_mon; if (mon->qmp.commands == &qmp_cap_negotiation_commands) { - error = qdict_get_qdict(qobject_to(QDict, rsp), "error"); + error = qdict_get_qdict(rsp, "error"); if (error && !g_strcmp0(qdict_get_try_str(error, "class"), QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) { diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index c85748a33f..761812e924 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -161,8 +161,8 @@ bool qmp_is_oob(QDict *dict) && !qdict_haskey(dict, "execute"); } -QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request, - bool allow_oob) +QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, + bool allow_oob) { Error *err = NULL; QObject *ret; @@ -179,5 +179,5 @@ QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request, rsp = NULL; } - return QOBJECT(rsp); + return rsp; } diff --git a/qga/main.c b/qga/main.c index 0e30e30248..537cc0e162 100644 --- a/qga/main.c +++ b/qga/main.c @@ -545,7 +545,7 @@ fail: #endif } -static int send_response(GAState *s, QObject *payload) +static int send_response(GAState *s, QDict *payload) { const char *buf; QString *payload_qstr, *response_qstr; @@ -553,7 +553,7 @@ static int send_response(GAState *s, QObject *payload) g_assert(payload && s->channel); - payload_qstr = qobject_to_json(payload); + payload_qstr = qobject_to_json(QOBJECT(payload)); if (!payload_qstr) { return -EINVAL; } @@ -581,7 +581,7 @@ static int send_response(GAState *s, QObject *payload) static void process_command(GAState *s, QDict *req) { - QObject *rsp = NULL; + QDict *rsp; int ret; g_assert(req); @@ -629,7 +629,7 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens) error_setg(&err, QERR_UNSUPPORTED); qdict = qmp_error_response(err); } - ret = send_response(s, QOBJECT(qdict)); + ret = send_response(s, qdict); if (ret < 0) { g_warning("error sending error response: %s", strerror(-ret)); } diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 10c7ba40c1..afb338a61e 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -98,13 +98,13 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, static void test_dispatch_cmd(void) { QDict *req = qdict_new(); - QObject *resp; + QDict *resp; qdict_put_str(req, "execute", "user_def_cmd"); resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); - assert(!qdict_haskey(qobject_to(QDict, resp), "error")); + assert(!qdict_haskey(resp, "error")); qobject_unref(resp); qobject_unref(req); @@ -115,13 +115,13 @@ static void test_dispatch_cmd_failure(void) { QDict *req = qdict_new(); QDict *args = qdict_new(); - QObject *resp; + QDict *resp; qdict_put_str(req, "execute", "user_def_cmd2"); resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); - assert(qdict_haskey(qobject_to(QDict, resp), "error")); + assert(qdict_haskey(resp, "error")); qobject_unref(resp); qobject_unref(req); @@ -135,7 +135,7 @@ static void test_dispatch_cmd_failure(void) resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); - assert(qdict_haskey(qobject_to(QDict, resp), "error")); + assert(qdict_haskey(resp, "error")); qobject_unref(resp); qobject_unref(req); @@ -143,18 +143,15 @@ static void test_dispatch_cmd_failure(void) static QObject *test_qmp_dispatch(QDict *req) { - QObject *resp_obj; QDict *resp; QObject *ret; - resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp_obj); - resp = qobject_to(QDict, resp_obj); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp && !qdict_haskey(resp, "error")); ret = qdict_get(resp, "return"); assert(ret); qobject_ref(ret); - qobject_unref(resp_obj); + qobject_unref(resp); return ret; } From 65e3fe6743af08bd0bc79b3d6158e91d572afc57 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:50 +0200 Subject: [PATCH 2052/2380] qmp: Replace monitor_json_emitter{,raw}() by qmp_{queue,send}_response() monitor_json_emitter() and monitor_json_emitter_raw() are unnecessarily general: they can send arbitrary JSON values, even though we only ever use them for QMP, which may send only JSON objects. Specialize the argument from QObject * to QDict *, and rename to qmp_queue_response(), qmp_send_response(). All callers but one lose an upcast. The lone exception gains a downcast; the next commit will get rid of it. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-25-armbru@redhat.com> --- monitor.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/monitor.c b/monitor.c index 8237b1c916..50dc57c82e 100644 --- a/monitor.c +++ b/monitor.c @@ -503,9 +503,9 @@ int monitor_fprintf(FILE *stream, const char *fmt, ...) return 0; } -static void monitor_json_emitter_raw(Monitor *mon, - QObject *data) +static void qmp_send_response(Monitor *mon, QDict *rsp) { + QObject *data = QOBJECT(rsp); QString *json; json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) : @@ -518,7 +518,7 @@ static void monitor_json_emitter_raw(Monitor *mon, qobject_unref(json); } -static void monitor_json_emitter(Monitor *mon, QObject *data) +static void qmp_queue_response(Monitor *mon, QDict *rsp) { if (mon->use_io_thread) { /* @@ -528,8 +528,7 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) * responder thread). */ qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - g_queue_push_tail(mon->qmp.qmp_responses, - qobject_ref(qobject_to(QDict, data))); + g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(rsp)); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); qemu_bh_schedule(qmp_respond_bh); } else { @@ -537,7 +536,7 @@ static void monitor_json_emitter(Monitor *mon, QObject *data) * If not using monitor I/O thread, then we are in main thread. * Do the emission right away. */ - monitor_json_emitter_raw(mon, data); + qmp_send_response(mon, rsp); } } @@ -563,7 +562,7 @@ static void monitor_qmp_response_flush(Monitor *mon) QDict *data; while ((data = monitor_qmp_response_pop_one(mon))) { - monitor_json_emitter_raw(mon, QOBJECT(data)); + qmp_send_response(mon, data); qobject_unref(data); } } @@ -595,7 +594,7 @@ static void monitor_qmp_bh_responder(void *opaque) QMPResponse response; while (monitor_qmp_response_pop_any(&response)) { - monitor_json_emitter_raw(response.mon, QOBJECT(response.data)); + qmp_send_response(response.mon, response.data); qobject_unref(response.data); } } @@ -622,7 +621,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) QTAILQ_FOREACH(mon, &mon_list, entry) { if (monitor_is_qmp(mon) && mon->qmp.commands != &qmp_cap_negotiation_commands) { - monitor_json_emitter(mon, QOBJECT(qdict)); + qmp_queue_response(mon, qdict); } } } @@ -4118,7 +4117,7 @@ static void monitor_qmp_respond(Monitor *mon, QDict *rsp, qdict_put_obj(rsp, "id", qobject_ref(id)); } - monitor_json_emitter(mon, QOBJECT(rsp)); + qmp_queue_response(mon, rsp); } qobject_unref(id); @@ -4418,7 +4417,7 @@ static void monitor_qmp_event(void *opaque, int event) mon->qmp.commands = &qmp_cap_negotiation_commands; monitor_qmp_caps_reset(mon); data = get_qmp_greeting(mon); - monitor_json_emitter(mon, data); + qmp_queue_response(mon, qobject_to(QDict, data)); qobject_unref(data); mon_refcount++; break; From 1816604b62f4c5ac1032374a941638f2fe199104 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:51 +0200 Subject: [PATCH 2053/2380] qmp: Replace get_qmp_greeting() by qmp_greeting() get_qmp_greeting() returns a QDict * as QObject *. It's caller converts it right back. Return QDict * instead. While there, rename to qmp_greeting(). Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-26-armbru@redhat.com> --- monitor.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/monitor.c b/monitor.c index 50dc57c82e..e5e9f496c2 100644 --- a/monitor.c +++ b/monitor.c @@ -4382,7 +4382,7 @@ void monitor_resume(Monitor *mon) trace_monitor_suspend(mon, -1); } -static QObject *get_qmp_greeting(Monitor *mon) +static QDict *qmp_greeting(Monitor *mon) { QList *cap_list = qlist_new(); QObject *ver = NULL; @@ -4398,8 +4398,9 @@ static QObject *get_qmp_greeting(Monitor *mon) qlist_append_str(cap_list, QMPCapability_str(cap)); } - return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': %p}}", - ver, cap_list); + return qdict_from_jsonf_nofail( + "{'QMP': {'version': %p, 'capabilities': %p}}", + ver, cap_list); } static void monitor_qmp_caps_reset(Monitor *mon) @@ -4409,15 +4410,15 @@ static void monitor_qmp_caps_reset(Monitor *mon) static void monitor_qmp_event(void *opaque, int event) { - QObject *data; + QDict *data; Monitor *mon = opaque; switch (event) { case CHR_EVENT_OPENED: mon->qmp.commands = &qmp_cap_negotiation_commands; monitor_qmp_caps_reset(mon); - data = get_qmp_greeting(mon); - qmp_queue_response(mon, qobject_to(QDict, data)); + data = qmp_greeting(mon); + qmp_queue_response(mon, data); qobject_unref(data); mon_refcount++; break; From 7cb2123f226a88fddcb9a3673c658c818ee6daed Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:52 +0200 Subject: [PATCH 2054/2380] qmp: Simplify monitor_qmp_respond() monitor_qmp_respond() takes both a response object and an error object. If an error object is non-null, the response object must be null, and the response is built from the error object. Of the two callers, one always passes a null response object, and one a null error object. Move building the response object from the error object to the latter, and drop the error object parameter. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-27-armbru@redhat.com> --- monitor.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/monitor.c b/monitor.c index e5e9f496c2..1da7b8bebf 100644 --- a/monitor.c +++ b/monitor.c @@ -4100,18 +4100,12 @@ static int monitor_can_read(void *opaque) } /* - * 1. This function takes ownership of rsp, err, and id. - * 2. rsp, err, and id may be NULL. - * 3. If err != NULL then rsp must be NULL. + * Emit QMP response @rsp with ID @id to @mon. + * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP. + * Nothing is emitted then. */ -static void monitor_qmp_respond(Monitor *mon, QDict *rsp, - Error *err, QObject *id) +static void monitor_qmp_respond(Monitor *mon, QDict *rsp, QObject *id) { - if (err) { - assert(!rsp); - rsp = qmp_error_response(err); - } - if (rsp) { if (id) { qdict_put_obj(rsp, "id", qobject_ref(id)); @@ -4119,9 +4113,6 @@ static void monitor_qmp_respond(Monitor *mon, QDict *rsp, qmp_queue_response(mon, rsp); } - - qobject_unref(id); - qobject_unref(rsp); } static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) @@ -4149,8 +4140,8 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) } } - /* Respond if necessary */ - monitor_qmp_respond(mon, rsp, NULL, qobject_ref(id)); + monitor_qmp_respond(mon, rsp, id); + qobject_unref(rsp); } /* @@ -4193,6 +4184,7 @@ static QMPRequest *monitor_qmp_requests_pop_any(void) static void monitor_qmp_bh_dispatcher(void *data) { QMPRequest *req_obj = monitor_qmp_requests_pop_any(); + QDict *rsp; if (!req_obj) { return; @@ -4203,7 +4195,9 @@ static void monitor_qmp_bh_dispatcher(void *data) monitor_qmp_dispatch(req_obj->mon, req_obj->req, req_obj->id); } else { assert(req_obj->err); - monitor_qmp_respond(req_obj->mon, NULL, req_obj->err, NULL); + rsp = qmp_error_response(req_obj->err); + monitor_qmp_respond(req_obj->mon, rsp, NULL); + qobject_unref(rsp); } if (req_obj->need_resume) { From 62e93be286626839ef2597da81f70a3ba4a31fff Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:53 +0200 Subject: [PATCH 2055/2380] qmp: Add some comments around null responses Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-28-armbru@redhat.com> --- qapi/qmp-dispatch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 761812e924..6f2d466596 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -133,6 +133,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, } else if (cmd->options & QCO_NO_SUCCESS_RESP) { g_assert(!ret); } else if (!ret) { + /* TODO turn into assertion */ ret = QOBJECT(qdict_new()); } @@ -176,6 +177,7 @@ QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, rsp = qdict_new(); qdict_put_obj(rsp, "return", ret); } else { + /* Can only happen for commands with QCO_NO_SUCCESS_RESP */ rsp = NULL; } From cd499d2058adb657b034023e9e7428c94b0f212d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:54 +0200 Subject: [PATCH 2056/2380] qmp: Switch timestamp_put() to qdict_from_jsonf_nofail() There's just one use of qobject_from_jsonf() to parse a JSON object left: timestamp_put(). Switch it to qdict_from_jsonf_nofail(). Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-29-armbru@redhat.com> --- qapi/qmp-event.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c index 9d7e88e84a..5b8854043e 100644 --- a/qapi/qmp-event.c +++ b/qapi/qmp-event.c @@ -34,15 +34,15 @@ QMPEventFuncEmit qmp_event_get_func_emit(void) static void timestamp_put(QDict *qdict) { int err; - QObject *obj; + QDict *ts; qemu_timeval tv; err = qemu_gettimeofday(&tv); /* Put -1 to indicate failure of getting host time */ - obj = qobject_from_jsonf("{ 'seconds': %lld, 'microseconds': %lld }", - err < 0 ? -1LL : (long long)tv.tv_sec, - err < 0 ? -1LL : (long long)tv.tv_usec); - qdict_put_obj(qdict, "timestamp", obj); + ts = qdict_from_jsonf_nofail("{ 'seconds': %lld, 'microseconds': %lld }", + err < 0 ? -1LL : (long long)tv.tv_sec, + err < 0 ? -1LL : (long long)tv.tv_usec); + qdict_put(qdict, "timestamp", ts); } /* From ab45015a968cc2c784a00775d52c6ea17e72c9fb Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:55 +0200 Subject: [PATCH 2057/2380] qobject: Let qobject_from_jsonf() fail instead of abort qobject_from_jsonf() aborts on error, unlike qobject_from_jsonv(), which returns null. Since all remaining users of qobject_from_jsonf() cope fine with null, change it to return null. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-30-armbru@redhat.com> --- qobject/qjson.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qobject/qjson.c b/qobject/qjson.c index 0df3120202..2f6a590e44 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -59,10 +59,6 @@ QObject *qobject_from_json(const char *string, Error **errp) return qobject_from_jsonv(string, NULL, errp); } -/* - * IMPORTANT: This function aborts on error, thus it must not - * be used with untrusted arguments. - */ QObject *qobject_from_jsonf(const char *string, ...) { QObject *obj; @@ -72,7 +68,6 @@ QObject *qobject_from_jsonf(const char *string, ...) obj = qobject_from_jsonv(string, &ap, &error_abort); va_end(ap); - assert(obj != NULL); return obj; } From 279f9e0840d08db8e0bb3806b6eb4f2f60cb104f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:56 +0200 Subject: [PATCH 2058/2380] qmp: Clean up capability negotiation after commit 02130314d8c qmp_greeting() offers capabilities to the client, and qmp_qmp_capabilities() accepts or denies capabilities requested by the client. The two compute the set of available capabilities independently. Not nice. Clean this up as follows. Compute available capabilities just once in monitor_qmp_caps_reset(), and store them in Monitor member qmp.capab_offered[]. Have qmp_greeting() and qmp_qmp_capabilities() use that. Both are now oblivious of capability details. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-31-armbru@redhat.com> --- monitor.c | 93 ++++++++++++++++++++++++------------------------------- 1 file changed, 40 insertions(+), 53 deletions(-) diff --git a/monitor.c b/monitor.c index 1da7b8bebf..f8b88ba105 100644 --- a/monitor.c +++ b/monitor.c @@ -173,7 +173,8 @@ typedef struct { * mode. */ QmpCommandList *commands; - bool qmp_caps[QMP_CAPABILITY__MAX]; + bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */ + bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */ /* * Protects qmp request/response queue. Please take monitor_lock * first when used together. @@ -1253,52 +1254,56 @@ static void monitor_init_qmp_commands(void) qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG); } -static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap) -{ - return mon->qmp.qmp_caps[cap]; -} - static bool qmp_oob_enabled(Monitor *mon) { - return qmp_cap_enabled(mon, QMP_CAPABILITY_OOB); + return mon->qmp.capab[QMP_CAPABILITY_OOB]; } -static void qmp_caps_check(Monitor *mon, QMPCapabilityList *list, - Error **errp) +static void monitor_qmp_caps_reset(Monitor *mon) { + memset(mon->qmp.capab_offered, 0, sizeof(mon->qmp.capab_offered)); + memset(mon->qmp.capab, 0, sizeof(mon->qmp.capab)); + mon->qmp.capab_offered[QMP_CAPABILITY_OOB] = mon->use_io_thread; +} + +/* + * Accept QMP capabilities in @list for @mon. + * On success, set mon->qmp.capab[], and return true. + * On error, set @errp, and return false. + */ +static bool qmp_caps_accept(Monitor *mon, QMPCapabilityList *list, + Error **errp) +{ + GString *unavailable = NULL; + bool capab[QMP_CAPABILITY__MAX]; + + memset(capab, 0, sizeof(capab)); + for (; list; list = list->next) { - assert(list->value < QMP_CAPABILITY__MAX); - switch (list->value) { - case QMP_CAPABILITY_OOB: - if (!mon->use_io_thread) { - /* - * Out-of-band only works with monitors that are - * running on dedicated I/O thread. - */ - error_setg(errp, "This monitor does not support " - "out-of-band (OOB)"); - return; + if (!mon->qmp.capab_offered[list->value]) { + if (!unavailable) { + unavailable = g_string_new(QMPCapability_str(list->value)); + } else { + g_string_append_printf(unavailable, ", %s", + QMPCapability_str(list->value)); } - break; - default: - break; } + capab[list->value] = true; } -} -/* This function should only be called after capabilities are checked. */ -static void qmp_caps_apply(Monitor *mon, QMPCapabilityList *list) -{ - for (; list; list = list->next) { - mon->qmp.qmp_caps[list->value] = true; + if (unavailable) { + error_setg(errp, "Capability %s not available", unavailable->str); + g_string_free(unavailable, true); + return false; } + + memcpy(mon->qmp.capab, capab, sizeof(capab)); + return true; } void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, Error **errp) { - Error *local_err = NULL; - if (cur_mon->qmp.commands == &qmp_commands) { error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, "Capabilities negotiation is already complete, command " @@ -1306,19 +1311,8 @@ void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, return; } - /* Enable QMP capabilities provided by the client if applicable. */ - if (has_enable) { - qmp_caps_check(cur_mon, enable, &local_err); - if (local_err) { - /* - * Failed check on any of the capabilities will fail the - * entire command (and thus not apply any of the other - * capabilities that were also requested). - */ - error_propagate(errp, local_err); - return; - } - qmp_caps_apply(cur_mon, enable); + if (!qmp_caps_accept(cur_mon, enable, errp)) { + return; } cur_mon->qmp.commands = &qmp_commands; @@ -4385,11 +4379,9 @@ static QDict *qmp_greeting(Monitor *mon) qmp_marshal_query_version(NULL, &ver, NULL); for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) { - if (!mon->use_io_thread && cap == QMP_CAPABILITY_OOB) { - /* Monitors that are not using I/O thread won't support OOB */ - continue; + if (mon->qmp.capab_offered[cap]) { + qlist_append_str(cap_list, QMPCapability_str(cap)); } - qlist_append_str(cap_list, QMPCapability_str(cap)); } return qdict_from_jsonf_nofail( @@ -4397,11 +4389,6 @@ static QDict *qmp_greeting(Monitor *mon) ver, cap_list); } -static void monitor_qmp_caps_reset(Monitor *mon) -{ - memset(mon->qmp.qmp_caps, 0, sizeof(mon->qmp.qmp_caps)); -} - static void monitor_qmp_event(void *opaque, int event) { QDict *data; From 774a6b67a409edf23a9b4b02da9ccbfc62e27cae Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:57 +0200 Subject: [PATCH 2059/2380] monitor: Improve some comments Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-32-armbru@redhat.com> --- monitor.c | 100 ++++++++++++++++++++++++------------------------------ 1 file changed, 45 insertions(+), 55 deletions(-) diff --git a/monitor.c b/monitor.c index f8b88ba105..511795365b 100644 --- a/monitor.c +++ b/monitor.c @@ -169,15 +169,16 @@ typedef struct { JSONMessageParser parser; /* * When a client connects, we're in capabilities negotiation mode. - * When command qmp_capabilities succeeds, we go into command - * mode. + * @commands is &qmp_cap_negotiation_commands then. When command + * qmp_capabilities succeeds, we go into command mode, and + * @command becomes &qmp_commands. */ QmpCommandList *commands; bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */ bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */ /* - * Protects qmp request/response queue. Please take monitor_lock - * first when used together. + * Protects qmp request/response queue. + * Take monitor_lock first when you need both. */ QemuMutex qmp_queue_lock; /* Input queue that holds all the parsed QMP requests */ @@ -232,7 +233,7 @@ struct Monitor { QemuMutex mon_lock; /* - * Fields that are protected by the per-monitor lock. + * Members that are protected by the per-monitor lock */ QLIST_HEAD(, mon_fd_t) fds; QString *outbuf; @@ -241,6 +242,7 @@ struct Monitor { int mux_out; }; +/* Shared monitor I/O thread */ IOThread *mon_iothread; /* Bottom half to dispatch the requests received from I/O thread */ @@ -302,9 +304,9 @@ static inline bool monitor_is_qmp(const Monitor *mon) } /** - * Whether @mon is using readline? Note: not all HMP monitors use - * readline, e.g., gdbserver has a non-interactive HMP monitor, so - * readline is not used there. + * Is @mon is using readline? + * Note: not all HMP monitors use readline, e.g., gdbserver has a + * non-interactive HMP monitor, so readline is not used there. */ static inline bool monitor_uses_readline(const Monitor *mon) { @@ -318,14 +320,12 @@ static inline bool monitor_is_hmp_non_interactive(const Monitor *mon) /* * Return the clock to use for recording an event's time. + * It's QEMU_CLOCK_REALTIME, except for qtests it's + * QEMU_CLOCK_VIRTUAL, to support testing rate limits. * Beware: result is invalid before configure_accelerator(). */ static inline QEMUClockType monitor_get_event_clock(void) { - /* - * This allows us to perform tests on the monitor queues to verify - * that the rate limits are enforced. - */ return qtest_enabled() ? QEMU_CLOCK_VIRTUAL : QEMU_CLOCK_REALTIME; } @@ -368,7 +368,7 @@ static void qmp_request_free(QMPRequest *req) g_free(req); } -/* Must with the mon->qmp.qmp_queue_lock held */ +/* Caller must hold mon->qmp.qmp_queue_lock */ static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon) { while (!g_queue_is_empty(mon->qmp.qmp_requests)) { @@ -376,7 +376,7 @@ static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon) } } -/* Must with the mon->qmp.qmp_queue_lock held */ +/* Caller must hold the mon->qmp.qmp_queue_lock */ static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon) { while (!g_queue_is_empty(mon->qmp.qmp_responses)) { @@ -407,7 +407,7 @@ static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond, return FALSE; } -/* Called with mon->mon_lock held. */ +/* Caller must hold mon->mon_lock */ static void monitor_flush_locked(Monitor *mon) { int rc; @@ -523,10 +523,8 @@ static void qmp_queue_response(Monitor *mon, QDict *rsp) { if (mon->use_io_thread) { /* - * If using I/O thread, we need to queue the item so that I/O - * thread will do the rest for us. Take refcount so that - * caller won't free the data (which will be finally freed in - * responder thread). + * Push a reference to the response queue. The I/O thread + * drains that queue and emits. */ qemu_mutex_lock(&mon->qmp.qmp_queue_lock); g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(rsp)); @@ -534,8 +532,8 @@ static void qmp_queue_response(Monitor *mon, QDict *rsp) qemu_bh_schedule(qmp_respond_bh); } else { /* - * If not using monitor I/O thread, then we are in main thread. - * Do the emission right away. + * Not using monitor I/O thread, i.e. we are in the main thread. + * Emit right away. */ qmp_send_response(mon, rsp); } @@ -611,8 +609,9 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { }; /* - * Emits the event to every monitor instance, @event is only used for trace - * Called with monitor_lock held. + * Broadcast an event to all monitors. + * @qdict is the event object. Its member "event" must match @event. + * Caller must hold monitor_lock. */ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) { @@ -981,8 +980,7 @@ static int parse_cmdline(const char *cmdline, } /* - * Returns true if the command can be executed in preconfig mode - * i.e. it has the 'p' flag. + * Can command @cmd be executed in preconfig state? */ static bool cmd_can_preconfig(const mon_cmd_t *cmd) { @@ -2221,7 +2219,7 @@ void qmp_getfd(const char *fdname, Error **errp) tmp_fd = monfd->fd; monfd->fd = fd; qemu_mutex_unlock(&cur_mon->mon_lock); - /* Make sure close() is out of critical section */ + /* Make sure close() is outside critical section */ close(tmp_fd); return; } @@ -2250,7 +2248,7 @@ void qmp_closefd(const char *fdname, Error **errp) g_free(monfd->name); g_free(monfd); qemu_mutex_unlock(&cur_mon->mon_lock); - /* Make sure close() is out of critical section */ + /* Make sure close() is outside critical section */ close(tmp_fd); return; } @@ -4139,7 +4137,8 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) } /* - * Pop one QMP request from monitor queues, return NULL if not found. + * Pop a QMP request from a monitor request queue. + * Return the request, or NULL all request queues are empty. * We are using round-robin fashion to pop the request, to avoid * processing commands only on a very busy monitor. To achieve that, * when we process one request on a specific monitor, we put that @@ -4234,7 +4233,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) } if (qdict && qmp_is_oob(qdict)) { - /* Out-of-band (OOB) requests are executed directly in parser. */ + /* OOB commands are executed immediately */ trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: ""); monitor_qmp_dispatch(mon, req, id); @@ -4356,8 +4355,8 @@ void monitor_resume(Monitor *mon) if (atomic_dec_fetch(&mon->suspend_cnt) == 0) { if (monitor_is_qmp(mon)) { /* - * For QMP monitors that are running in I/O thread, let's - * kick the thread in case it's sleeping. + * For QMP monitors that are running in the I/O thread, + * let's kick the thread in case it's sleeping. */ if (mon->use_io_thread) { aio_notify(iothread_get_aio_context(mon_iothread)); @@ -4505,18 +4504,18 @@ static void monitor_iothread_init(void) mon_iothread = iothread_create("mon_iothread", &error_abort); /* - * This MUST be on main loop thread since we have commands that - * have assumption to be run on main loop thread. It would be - * nice that one day we can remove this assumption in the future. + * The dispatcher BH must run in the main loop thread, since we + * have commands assuming that context. It would be nice to get + * rid of those assumptions. */ qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(), monitor_qmp_bh_dispatcher, NULL); /* - * Unlike the dispatcher BH, this must be run on the monitor I/O - * thread, so that monitors that are using I/O thread will make - * sure read/write operations are all done on the I/O thread. + * The responder BH must be run in the monitor I/O thread, so that + * monitors that are using the I/O thread have their output + * written by the I/O thread. */ qmp_respond_bh = aio_bh_new(monitor_get_aio_context(), monitor_qmp_bh_responder, @@ -4586,15 +4585,11 @@ static void monitor_qmp_setup_handlers_bh(void *opaque) GMainContext *context; if (mon->use_io_thread) { - /* - * When @use_io_thread is set, we use the global shared dedicated - * I/O thread for this monitor to handle input/output. - */ + /* Use @mon_iothread context */ context = monitor_get_io_context(); - /* We should have inited globals before reaching here. */ assert(context); } else { - /* The default main loop, which is the main thread */ + /* Use default main loop context */ context = NULL; } @@ -4644,15 +4639,12 @@ void monitor_init(Chardev *chr, int flags) remove_fd_in_watch(chr); /* * We can't call qemu_chr_fe_set_handlers() directly here - * since during the procedure the chardev will be active - * and running in monitor I/O thread, while we'll still do - * something before returning from it, which is a possible - * race too. To avoid that, we just create a BH to setup - * the handlers. + * since chardev might be running in the monitor I/O + * thread. Schedule a bottom half. */ aio_bh_schedule_oneshot(monitor_get_aio_context(), monitor_qmp_setup_handlers_bh, mon); - /* We'll add this to mon_list in the BH when setup done */ + /* The bottom half will add @mon to @mon_list */ return; } else { qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, @@ -4673,21 +4665,19 @@ void monitor_cleanup(void) /* * We need to explicitly stop the I/O thread (but not destroy it), - * cleanup the monitor resources, then destroy the I/O thread since + * clean up the monitor resources, then destroy the I/O thread since * we need to unregister from chardev below in * monitor_data_destroy(), and chardev is not thread-safe yet */ iothread_stop(mon_iothread); /* - * After we have I/O thread to send responses, it's possible that - * when we stop the I/O thread there are still replies queued in the - * responder queue. Flush all of them. Note that even after this - * flush it's still possible that out buffer is not flushed. - * It'll be done in below monitor_flush() as the last resort. + * Flush all response queues. Note that even after this flush, + * data may remain in output buffers. */ monitor_qmp_bh_responder(NULL); + /* Flush output buffers and destroy monitors */ qemu_mutex_lock(&monitor_lock); QTAILQ_FOREACH_SAFE(mon, &mon_list, entry, next) { QTAILQ_REMOVE(&mon_list, mon, entry); From 153d73f320f422ecb5807ac3a93547b9f819599b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 3 Jul 2018 10:53:58 +0200 Subject: [PATCH 2060/2380] qapi: Polish command flags documentation in qapi-code-gen.txt Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180703085358.13941-33-armbru@redhat.com> --- docs/devel/qapi-code-gen.txt | 61 +++++++++++++++--------------------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index f020f6bab2..8decd6f17d 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -624,60 +624,48 @@ its return value. In rare cases, QAPI cannot express a type-safe representation of a corresponding Client JSON Protocol command. You then have to suppress generation of a marshalling function by including a key 'gen' with -boolean value false, and instead write your own function. Please try -to avoid adding new commands that rely on this, and instead use -type-safe unions. For an example of this usage: +boolean value false, and instead write your own function. For +example: { 'command': 'netdev_add', 'data': {'type': 'str', 'id': 'str'}, 'gen': false } +Please try to avoid adding new commands that rely on this, and instead +use type-safe unions. + Normally, the QAPI schema is used to describe synchronous exchanges, where a response is expected. But in some cases, the action of a command is expected to change state in a way that a successful response is not possible (although the command will still return a normal dictionary error on failure). When a successful reply is not -possible, the command expression should include the optional key +possible, the command expression includes the optional key 'success-response' with boolean value false. So far, only QGA makes use of this member. -A command can be declared to support out-of-band (OOB) execution. By -default, commands do not support OOB. To declare a command that -supports it, the schema includes an extra 'allow-oob' field. For -example: +Key 'allow-oob' declares whether the command supports out-of-band +(OOB) execution. It defaults to false. For example: { 'command': 'migrate_recover', 'data': { 'uri': 'str' }, 'allow-oob': true } -To execute a command with out-of-band priority, the client uses key -"exec-oob" instead of "execute". Example: +See qmp-spec.txt for out-of-band execution syntax and semantics. - => { "exec-oob": "migrate-recover", - "arguments": { "uri": "tcp:192.168.1.200:12345" } } - <= { "return": { } } +Commands supporting out-of-band execution can still be executed +in-band. -Without it, even the commands that support out-of-band execution will -still be run in-band. +When a command is executed in-band, its handler runs in the main +thread with the BQL held. -Under normal QMP command execution, the following apply to each -command: +When a command is executed out-of-band, its handler runs in a +dedicated monitor I/O thread with the BQL *not* held. -- They are executed in order, -- They run only in main thread of QEMU, -- They run with the BQL held. +An OOB-capable command handler must satisfy the following conditions: -When a command is executed with OOB, the following changes occur: - -- They can be completed before a pending in-band command, -- They run in a dedicated monitor thread, -- They run with the BQL not held. - -OOB command handlers must satisfy the following conditions: - -- It terminates quickly, -- It does not invoke system calls that may block, +- It terminates quickly. +- It does not invoke system calls that may block. - It does not access guest RAM that may block when userfaultfd is - enabled for postcopy live migration, + enabled for postcopy live migration. - It takes only "fast" locks, i.e. all critical sections protected by any lock it takes also satisfy the conditions for OOB command handler code. @@ -686,17 +674,18 @@ The restrictions on locking limit access to shared state. Such access requires synchronization, but OOB commands can't take the BQL or any other "slow" lock. -If in doubt, do not implement OOB execution support. +When in doubt, do not implement OOB execution support. -A command may use the optional 'allow-preconfig' key to permit its execution -at early runtime configuration stage (preconfig runstate). -If not specified then a command defaults to 'allow-preconfig': false. +Key 'allow-preconfig' declares whether the command is available before +the machine is built. It defaults to false. For example: -An example of declaring a command that is enabled during preconfig: { 'command': 'qmp_capabilities', 'data': { '*enable': [ 'QMPCapability' ] }, 'allow-preconfig': true } +QMP is available before the machine is built only when QEMU was +started with --preconfig. + === Events === Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, From 92bcea40d3aac62853e60426bd109b748d4d1cd2 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 4 Jul 2018 02:12:49 -0400 Subject: [PATCH 2061/2380] block/dirty-bitmap: add bdrv_enable_dirty_bitmap_locked Add _locked version of bdrv_enable_dirty_bitmap, to fix dirty bitmap migration in the following patch. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-id: 20180625165745.25259-2-vsementsov@virtuozzo.com Signed-off-by: John Snow --- block/dirty-bitmap.c | 9 +++++++-- include/block/dirty-bitmap.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index db1782ec1f..93744b3565 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -241,6 +241,12 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, return 0; } +void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) +{ + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + bitmap->disabled = false; +} + /* Called with BQL taken. */ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) { @@ -424,8 +430,7 @@ void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) { bdrv_dirty_bitmap_lock(bitmap); - assert(!bdrv_dirty_bitmap_frozen(bitmap)); - bitmap->disabled = false; + bdrv_enable_dirty_bitmap_locked(bitmap); bdrv_dirty_bitmap_unlock(bitmap); } diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 288dc6adb6..259bd27c40 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -32,6 +32,7 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, Error **errp); void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap); void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap); +void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap); BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs); uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap); From 58f72b965e9e1820d246329461216c4d13140122 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 4 Jul 2018 02:12:49 -0400 Subject: [PATCH 2062/2380] dirty-bitmap: fix double lock on bitmap enabling Bitmap lock/unlock were added to bdrv_enable_dirty_bitmap in 8b1402ce80d, but some places were not updated correspondingly, which leads to trying to take this lock twice, which is dead-lock. Fix this. Actually, iotest 199 (about dirty bitmap postcopy migration) is broken now, and this fixes it. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-id: 20180625165745.25259-3-vsementsov@virtuozzo.com Signed-off-by: John Snow --- block/dirty-bitmap.c | 3 ++- migration/block-dirty-bitmap.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 93744b3565..c9b8a6fd52 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -250,8 +250,9 @@ void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) /* Called with BQL taken. */ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) { + assert(bitmap->mutex == bitmap->successor->mutex); qemu_mutex_lock(bitmap->mutex); - bdrv_enable_dirty_bitmap(bitmap->successor); + bdrv_enable_dirty_bitmap_locked(bitmap->successor); qemu_mutex_unlock(bitmap->mutex); } diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 3bafbbdc4c..477826330c 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -511,7 +511,7 @@ void dirty_bitmap_mig_before_vm_start(void) DirtyBitmapLoadBitmapState *b = item->data; if (b->migrated) { - bdrv_enable_dirty_bitmap(b->bitmap); + bdrv_enable_dirty_bitmap_locked(b->bitmap); } else { bdrv_dirty_bitmap_enable_successor(b->bitmap); } @@ -547,7 +547,7 @@ static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s) if (enabled_bitmaps == NULL) { /* in postcopy */ bdrv_reclaim_dirty_bitmap_locked(s->bs, s->bitmap, &error_abort); - bdrv_enable_dirty_bitmap(s->bitmap); + bdrv_enable_dirty_bitmap_locked(s->bitmap); } else { /* target not started, successor must be empty */ int64_t count = bdrv_get_dirty_count(s->bitmap); From 88481329c0a43373f994f0c8ed19e888a8c86830 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 20 Jun 2018 17:48:35 +0300 Subject: [PATCH 2063/2380] qemu-img: allow compressed not-in-order writes No reason to forbid them, and they are needed to improve performance with compress-threads in further patches. Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Kevin Wolf --- qemu-img.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index e1a506f7f6..7651d8172c 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -2141,11 +2141,6 @@ static int img_convert(int argc, char **argv) goto fail_getopt; } - if (!s.wr_in_order && s.compressed) { - error_report("Out of order write and compress are mutually exclusive"); - goto fail_getopt; - } - if (tgt_image_opts && !skip_create) { error_report("--target-image-opts requires use of -n flag"); goto fail_getopt; From 2714f13d69adf73638842729ccfb3bdd6d5ee98f Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 20 Jun 2018 17:48:36 +0300 Subject: [PATCH 2064/2380] qcow2: refactor data compression Make a separate function for compression to be parallelized later. - use .avail_out field instead of .next_out to calculate size of compressed data. It looks more natural and it allows to keep dest to be void pointer - set avail_out to be at least one byte less than input, to be sure avoid inefficient compression earlier Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Kevin Wolf --- block/qcow2.c | 78 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 2f9e58e0c4..9cee653d96 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -23,11 +23,14 @@ */ #include "qemu/osdep.h" + +#define ZLIB_CONST +#include + #include "block/block_int.h" #include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" -#include #include "qcow2.h" #include "qemu/error-report.h" #include "qapi/error.h" @@ -3650,6 +3653,48 @@ fail: return ret; } +/* + * qcow2_compress() + * + * @dest - destination buffer, at least of @size-1 bytes + * @src - source buffer, @size bytes + * + * Returns: compressed size on success + * -1 if compression is inefficient + * -2 on any other error + */ +static ssize_t qcow2_compress(void *dest, const void *src, size_t size) +{ + ssize_t ret; + z_stream strm; + + /* best compression, small window, no zlib header */ + memset(&strm, 0, sizeof(strm)); + ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + -12, 9, Z_DEFAULT_STRATEGY); + if (ret != 0) { + return -2; + } + + /* strm.next_in is not const in old zlib versions, such as those used on + * OpenBSD/NetBSD, so cast the const away */ + strm.avail_in = size; + strm.next_in = (void *) src; + strm.avail_out = size - 1; + strm.next_out = dest; + + ret = deflate(&strm, Z_FINISH); + if (ret == Z_STREAM_END) { + ret = size - 1 - strm.avail_out; + } else { + ret = (ret == Z_OK ? -1 : -2); + } + + deflateEnd(&strm); + + return ret; +} + /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ static coroutine_fn int @@ -3659,8 +3704,8 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, BDRVQcow2State *s = bs->opaque; QEMUIOVector hd_qiov; struct iovec iov; - z_stream strm; - int ret, out_len; + int ret; + size_t out_len; uint8_t *buf, *out_buf; int64_t cluster_offset; @@ -3694,32 +3739,11 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, out_buf = g_malloc(s->cluster_size); - /* best compression, small window, no zlib header */ - memset(&strm, 0, sizeof(strm)); - ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, - Z_DEFLATED, -12, - 9, Z_DEFAULT_STRATEGY); - if (ret != 0) { + out_len = qcow2_compress(out_buf, buf, s->cluster_size); + if (out_len == -2) { ret = -EINVAL; goto fail; - } - - strm.avail_in = s->cluster_size; - strm.next_in = (uint8_t *)buf; - strm.avail_out = s->cluster_size; - strm.next_out = out_buf; - - ret = deflate(&strm, Z_FINISH); - if (ret != Z_STREAM_END && ret != Z_OK) { - deflateEnd(&strm); - ret = -EINVAL; - goto fail; - } - out_len = strm.next_out - out_buf; - - deflateEnd(&strm); - - if (ret != Z_STREAM_END || out_len >= s->cluster_size) { + } else if (out_len == -1) { /* could not compress: write normal cluster */ ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0); if (ret < 0) { From ceb029cd6feccf9f7607833b71dd609d149421a1 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 20 Jun 2018 17:48:37 +0300 Subject: [PATCH 2065/2380] qcow2: add compress threads Do data compression in separate threads. This significantly improve performance for qemu-img convert with -W (allow async writes) and -c (compressed) options. Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Kevin Wolf --- block/qcow2.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++- block/qcow2.h | 3 +++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/block/qcow2.c b/block/qcow2.c index 9cee653d96..33b61b7480 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -44,6 +44,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" #include "crypto.h" +#include "block/thread-pool.h" /* Differences with QCOW: @@ -1544,6 +1545,9 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, qcow2_check_refcounts(bs, &result, 0); } #endif + + qemu_co_queue_init(&s->compress_wait_queue); + return ret; fail: @@ -3695,6 +3699,62 @@ static ssize_t qcow2_compress(void *dest, const void *src, size_t size) return ret; } +#define MAX_COMPRESS_THREADS 4 + +typedef struct Qcow2CompressData { + void *dest; + const void *src; + size_t size; + ssize_t ret; +} Qcow2CompressData; + +static int qcow2_compress_pool_func(void *opaque) +{ + Qcow2CompressData *data = opaque; + + data->ret = qcow2_compress(data->dest, data->src, data->size); + + return 0; +} + +static void qcow2_compress_complete(void *opaque, int ret) +{ + qemu_coroutine_enter(opaque); +} + +/* See qcow2_compress definition for parameters description */ +static ssize_t qcow2_co_compress(BlockDriverState *bs, + void *dest, const void *src, size_t size) +{ + BDRVQcow2State *s = bs->opaque; + BlockAIOCB *acb; + ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + Qcow2CompressData arg = { + .dest = dest, + .src = src, + .size = size, + }; + + while (s->nb_compress_threads >= MAX_COMPRESS_THREADS) { + qemu_co_queue_wait(&s->compress_wait_queue, NULL); + } + + s->nb_compress_threads++; + acb = thread_pool_submit_aio(pool, qcow2_compress_pool_func, &arg, + qcow2_compress_complete, + qemu_coroutine_self()); + + if (!acb) { + s->nb_compress_threads--; + return -EINVAL; + } + qemu_coroutine_yield(); + s->nb_compress_threads--; + qemu_co_queue_next(&s->compress_wait_queue); + + return arg.ret; +} + /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ static coroutine_fn int @@ -3739,7 +3799,7 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, out_buf = g_malloc(s->cluster_size); - out_len = qcow2_compress(out_buf, buf, s->cluster_size); + out_len = qcow2_co_compress(bs, out_buf, buf, s->cluster_size); if (out_len == -2) { ret = -EINVAL; goto fail; diff --git a/block/qcow2.h b/block/qcow2.h index 1c9c0d3631..d6aca687d6 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -326,6 +326,9 @@ typedef struct BDRVQcow2State { * override) */ char *image_backing_file; char *image_backing_format; + + CoQueue compress_wait_queue; + int nb_compress_threads; } BDRVQcow2State; typedef struct Qcow2COWRegion { From 7ae9f3f61b2b99e2f348d3dc4a4ef2c6af0ae9bc Mon Sep 17 00:00:00 2001 From: Ari Sundholm Date: Tue, 3 Jul 2018 17:48:47 +0300 Subject: [PATCH 2066/2380] block: Move two block permission constants to the relevant enum This allows using the two constants outside of block.c, which will happen in a subsequent patch. Signed-off-by: Ari Sundholm Signed-off-by: Kevin Wolf --- block.c | 6 ------ include/block/block.h | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/block.c b/block.c index 70a46fdd84..961ec97d26 100644 --- a/block.c +++ b/block.c @@ -1948,12 +1948,6 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, return 0; } -#define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ - | BLK_PERM_WRITE \ - | BLK_PERM_WRITE_UNCHANGED \ - | BLK_PERM_RESIZE) -#define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH) - void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, const BdrvChildRole *role, BlockReopenQueue *reopen_queue, diff --git a/include/block/block.h b/include/block/block.h index e5c7759a0c..bc76b1e59f 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -225,6 +225,13 @@ enum { BLK_PERM_GRAPH_MOD = 0x10, BLK_PERM_ALL = 0x1f, + + DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ + | BLK_PERM_WRITE + | BLK_PERM_WRITE_UNCHANGED + | BLK_PERM_RESIZE, + + DEFAULT_PERM_UNCHANGED = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH, }; char *bdrv_perm_names(uint64_t perm); From bfcc224e3cf04ee3fef0eb69984607b5764d9892 Mon Sep 17 00:00:00 2001 From: Aapo Vienamo Date: Tue, 3 Jul 2018 17:48:48 +0300 Subject: [PATCH 2067/2380] block: Add blklogwrites Implements a block device write logging system, similar to Linux kernel device mapper dm-log-writes. The write operations that are performed on a block device are logged to a file or another block device. The write log format is identical to the dm-log-writes format. Currently, log markers are not supported. This functionality can be used for crash consistency and fs consistency testing. By implementing it in qemu, tests utilizing write logs can be be used to test non-Linux drivers and older kernels. The driver accepts an optional parameter to set the sector size used for logging. This makes the driver require all requests to be aligned to this sector size and also makes offsets and sizes of writes in the log metadata to be expressed in terms of this value (the log format has a granularity of one sector for offsets and sizes). This allows accurate logging of writes to guest block devices that have unusual sector sizes. The implementation is based on the blkverify and blkdebug block drivers. Signed-off-by: Aapo Vienamo Signed-off-by: Ari Sundholm Signed-off-by: Kevin Wolf --- MAINTAINERS | 6 + block/Makefile.objs | 1 + block/blklogwrites.c | 414 +++++++++++++++++++++++++++++++++++++++++++ qapi/block-core.json | 33 +++- 4 files changed, 448 insertions(+), 6 deletions(-) create mode 100644 block/blklogwrites.c diff --git a/MAINTAINERS b/MAINTAINERS index 6630d691d1..4431a80860 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2052,6 +2052,12 @@ S: Supported F: block/quorum.c L: qemu-block@nongnu.org +blklogwrites +M: Ari Sundholm +L: qemu-block@nongnu.org +S: Supported +F: block/blklogwrites.c + blkverify M: Stefan Hajnoczi L: qemu-block@nongnu.org diff --git a/block/Makefile.objs b/block/Makefile.objs index 899bfb5e2c..c8337bf186 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -5,6 +5,7 @@ block-obj-y += qed-check.o block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o block-obj-y += quorum.o block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o +block-obj-y += blklogwrites.o block-obj-y += block-backend.o snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += file-posix.o diff --git a/block/blklogwrites.c b/block/blklogwrites.c new file mode 100644 index 0000000000..47093fadd6 --- /dev/null +++ b/block/blklogwrites.c @@ -0,0 +1,414 @@ +/* + * Write logging blk driver based on blkverify and blkdebug. + * + * Copyright (c) 2017 Tuomas Tynkkynen + * Copyright (c) 2018 Aapo Vienamo + * Copyright (c) 2018 Ari Sundholm + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/sockets.h" /* for EINPROGRESS on Windows */ +#include "block/block_int.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" +#include "qemu/cutils.h" +#include "qemu/option.h" + +/* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */ + +#define LOG_FLUSH_FLAG (1 << 0) +#define LOG_FUA_FLAG (1 << 1) +#define LOG_DISCARD_FLAG (1 << 2) +#define LOG_MARK_FLAG (1 << 3) + +#define WRITE_LOG_VERSION 1ULL +#define WRITE_LOG_MAGIC 0x6a736677736872ULL + +/* All fields are little-endian. */ +struct log_write_super { + uint64_t magic; + uint64_t version; + uint64_t nr_entries; + uint32_t sectorsize; +} QEMU_PACKED; + +struct log_write_entry { + uint64_t sector; + uint64_t nr_sectors; + uint64_t flags; + uint64_t data_len; +} QEMU_PACKED; + +/* End of disk format structures. */ + +typedef struct { + BdrvChild *log_file; + uint32_t sectorsize; + uint32_t sectorbits; + uint64_t cur_log_sector; + uint64_t nr_entries; +} BDRVBlkLogWritesState; + +static QemuOptsList runtime_opts = { + .name = "blklogwrites", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "log-sector-size", + .type = QEMU_OPT_SIZE, + .help = "Log sector size", + }, + { /* end of list */ } + }, +}; + +static inline uint32_t blk_log_writes_log2(uint32_t value) +{ + assert(value > 0); + return 31 - clz32(value); +} + +static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVBlkLogWritesState *s = bs->opaque; + QemuOpts *opts; + Error *local_err = NULL; + int ret; + int64_t log_sector_size; + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + goto fail; + } + + /* Open the file */ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, + &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + goto fail; + } + + log_sector_size = qemu_opt_get_size(opts, "log-sector-size", + BDRV_SECTOR_SIZE); + + if (log_sector_size < 0 || log_sector_size > (1ull << 23) || + !is_power_of_2(log_sector_size)) + { + ret = -EINVAL; + error_setg(errp, "Invalid log sector size %"PRId64, log_sector_size); + goto fail; + } + + s->sectorsize = log_sector_size; + s->sectorbits = blk_log_writes_log2(log_sector_size); + s->cur_log_sector = 1; + s->nr_entries = 0; + + /* Open the log file */ + s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false, + &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + goto fail; + } + + ret = 0; +fail: + if (ret < 0) { + bdrv_unref_child(bs, bs->file); + bs->file = NULL; + } + qemu_opts_del(opts); + return ret; +} + +static void blk_log_writes_close(BlockDriverState *bs) +{ + BDRVBlkLogWritesState *s = bs->opaque; + + bdrv_unref_child(bs, s->log_file); + s->log_file = NULL; +} + +static int64_t blk_log_writes_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file->bs); +} + +static void blk_log_writes_refresh_filename(BlockDriverState *bs, + QDict *options) +{ + BDRVBlkLogWritesState *s = bs->opaque; + + /* bs->file->bs has already been refreshed */ + bdrv_refresh_filename(s->log_file->bs); + + if (bs->file->bs->full_open_options + && s->log_file->bs->full_open_options) + { + QDict *opts = qdict_new(); + qdict_put_str(opts, "driver", "blklogwrites"); + + qobject_ref(bs->file->bs->full_open_options); + qdict_put_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options)); + qobject_ref(s->log_file->bs->full_open_options); + qdict_put_obj(opts, "log", + QOBJECT(s->log_file->bs->full_open_options)); + qdict_put_int(opts, "log-sector-size", s->sectorsize); + + bs->full_open_options = opts; + } +} + +static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole *role, + BlockReopenQueue *ro_q, + uint64_t perm, uint64_t shrd, + uint64_t *nperm, uint64_t *nshrd) +{ + if (!c) { + *nperm = perm & DEFAULT_PERM_PASSTHROUGH; + *nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED; + return; + } + + if (!strcmp(c->name, "log")) { + bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd); + } else { + bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd); + } +} + +static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BDRVBlkLogWritesState *s = bs->opaque; + bs->bl.request_alignment = s->sectorsize; +} + +static int coroutine_fn +blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); +} + +typedef struct BlkLogWritesFileReq { + BlockDriverState *bs; + uint64_t offset; + uint64_t bytes; + int file_flags; + QEMUIOVector *qiov; + int (*func)(struct BlkLogWritesFileReq *r); + int file_ret; +} BlkLogWritesFileReq; + +typedef struct { + BlockDriverState *bs; + QEMUIOVector *qiov; + struct log_write_entry entry; + uint64_t zero_size; + int log_ret; +} BlkLogWritesLogReq; + +static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr) +{ + BDRVBlkLogWritesState *s = lr->bs->opaque; + uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits; + + s->nr_entries++; + s->cur_log_sector += + ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits; + + lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size, + lr->qiov, 0); + + /* Logging for the "write zeroes" operation */ + if (lr->log_ret == 0 && lr->zero_size) { + cur_log_offset = s->cur_log_sector << s->sectorbits; + s->cur_log_sector += + ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits; + + lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset, + lr->zero_size, 0); + } + + /* Update super block on flush */ + if (lr->log_ret == 0 && lr->entry.flags & LOG_FLUSH_FLAG) { + struct log_write_super super = { + .magic = cpu_to_le64(WRITE_LOG_MAGIC), + .version = cpu_to_le64(WRITE_LOG_VERSION), + .nr_entries = cpu_to_le64(s->nr_entries), + .sectorsize = cpu_to_le32(s->sectorsize), + }; + void *zeroes = g_malloc0(s->sectorsize - sizeof(super)); + QEMUIOVector qiov; + + qemu_iovec_init(&qiov, 2); + qemu_iovec_add(&qiov, &super, sizeof(super)); + qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super)); + + lr->log_ret = + bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0); + if (lr->log_ret == 0) { + lr->log_ret = bdrv_co_flush(s->log_file->bs); + } + qemu_iovec_destroy(&qiov); + g_free(zeroes); + } +} + +static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr) +{ + fr->file_ret = fr->func(fr); +} + +static int coroutine_fn +blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + int (*file_func)(BlkLogWritesFileReq *r), + uint64_t entry_flags, bool is_zero_write) +{ + QEMUIOVector log_qiov; + size_t niov = qiov ? qiov->niov : 0; + BDRVBlkLogWritesState *s = bs->opaque; + BlkLogWritesFileReq fr = { + .bs = bs, + .offset = offset, + .bytes = bytes, + .file_flags = flags, + .qiov = qiov, + .func = file_func, + }; + BlkLogWritesLogReq lr = { + .bs = bs, + .qiov = &log_qiov, + .entry = { + .sector = cpu_to_le64(offset >> s->sectorbits), + .nr_sectors = cpu_to_le64(bytes >> s->sectorbits), + .flags = cpu_to_le64(entry_flags), + .data_len = 0, + }, + .zero_size = is_zero_write ? bytes : 0, + }; + void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry)); + + assert((1 << s->sectorbits) == s->sectorsize); + assert(bs->bl.request_alignment == s->sectorsize); + assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)); + assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment)); + + qemu_iovec_init(&log_qiov, niov + 2); + qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry)); + qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry)); + if (qiov) { + qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size); + } + + blk_log_writes_co_do_file(&fr); + blk_log_writes_co_do_log(&lr); + + qemu_iovec_destroy(&log_qiov); + g_free(zeroes); + + if (lr.log_ret < 0) { + return lr.log_ret; + } + + return fr.file_ret; +} + +static int coroutine_fn +blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr) +{ + return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes, + fr->qiov, fr->file_flags); +} + +static int coroutine_fn +blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr) +{ + return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes, + fr->file_flags); +} + +static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr) +{ + return bdrv_co_flush(fr->bs->file->bs); +} + +static int coroutine_fn +blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr) +{ + return bdrv_co_pdiscard(fr->bs->file->bs, fr->offset, fr->bytes); +} + +static int coroutine_fn +blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + return blk_log_writes_co_log(bs, offset, bytes, qiov, flags, + blk_log_writes_co_do_file_pwritev, 0, false); +} + +static int coroutine_fn +blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, + BdrvRequestFlags flags) +{ + return blk_log_writes_co_log(bs, offset, bytes, NULL, flags, + blk_log_writes_co_do_file_pwrite_zeroes, 0, + true); +} + +static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs) +{ + return blk_log_writes_co_log(bs, 0, 0, NULL, 0, + blk_log_writes_co_do_file_flush, + LOG_FLUSH_FLAG, false); +} + +static int coroutine_fn +blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) +{ + return blk_log_writes_co_log(bs, offset, count, NULL, 0, + blk_log_writes_co_do_file_pdiscard, + LOG_DISCARD_FLAG, false); +} + +static BlockDriver bdrv_blk_log_writes = { + .format_name = "blklogwrites", + .instance_size = sizeof(BDRVBlkLogWritesState), + + .bdrv_open = blk_log_writes_open, + .bdrv_close = blk_log_writes_close, + .bdrv_getlength = blk_log_writes_getlength, + .bdrv_refresh_filename = blk_log_writes_refresh_filename, + .bdrv_child_perm = blk_log_writes_child_perm, + .bdrv_refresh_limits = blk_log_writes_refresh_limits, + + .bdrv_co_preadv = blk_log_writes_co_preadv, + .bdrv_co_pwritev = blk_log_writes_co_pwritev, + .bdrv_co_pwrite_zeroes = blk_log_writes_co_pwrite_zeroes, + .bdrv_co_flush_to_disk = blk_log_writes_co_flush_to_disk, + .bdrv_co_pdiscard = blk_log_writes_co_pdiscard, + .bdrv_co_block_status = bdrv_co_block_status_from_file, + + .is_filter = true, +}; + +static void bdrv_blk_log_writes_init(void) +{ + bdrv_register(&bdrv_blk_log_writes); +} + +block_init(bdrv_blk_log_writes_init); diff --git a/qapi/block-core.json b/qapi/block-core.json index 90e554ed0f..a9eab8cab8 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2533,16 +2533,17 @@ # @throttle: Since 2.11 # @nvme: Since 2.12 # @copy-on-read: Since 3.0 +# @blklogwrites: Since 3.0 # # Since: 2.9 ## { 'enum': 'BlockdevDriver', - 'data': [ 'blkdebug', 'blkverify', 'bochs', 'cloop', 'copy-on-read', - 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom', - 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', - 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', 'qcow2', 'qed', - 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh', - 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } + 'data': [ 'blkdebug', 'blklogwrites', 'blkverify', 'bochs', 'cloop', + 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster', + 'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks', + 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', + 'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', + 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } ## # @BlockdevOptionsFile: @@ -3044,6 +3045,25 @@ '*inject-error': ['BlkdebugInjectErrorOptions'], '*set-state': ['BlkdebugSetStateOptions'] } } +## +# @BlockdevOptionsBlklogwrites: +# +# Driver specific block device options for blklogwrites. +# +# @file: block device +# +# @log: block device used to log writes to @file +# +# @log-sector-size: sector size used in logging writes to @file, determines +# granularity of offsets and sizes of writes (default: 512) +# +# Since: 3.0 +## +{ 'struct': 'BlockdevOptionsBlklogwrites', + 'data': { 'file': 'BlockdevRef', + 'log': 'BlockdevRef', + '*log-sector-size': 'uint32' } } + ## # @BlockdevOptionsBlkverify: # @@ -3563,6 +3583,7 @@ 'discriminator': 'driver', 'data': { 'blkdebug': 'BlockdevOptionsBlkdebug', + 'blklogwrites':'BlockdevOptionsBlklogwrites', 'blkverify': 'BlockdevOptionsBlkverify', 'bochs': 'BlockdevOptionsGenericFormat', 'cloop': 'BlockdevOptionsGenericFormat', From 824808dd77821ceba05357cb1ee4069a6a95bebd Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 4 Jul 2018 13:28:29 +0200 Subject: [PATCH 2068/2380] block: Don't silently truncate node names If the user passes a too long node name string, we silently truncate it to fit into BlockDriverState.node_name, i.e. to 31 characters. Apart from surprising the user when the node has a different name than requested, this also bypasses the check for duplicate names, so that the same name can be assigned to multiple nodes. Fix this by just making too long node names an error. Reported-by: Peter Krempa Signed-off-by: Kevin Wolf --- block.c | 6 ++++++ tests/qemu-iotests/051 | 15 +++++++++++++++ tests/qemu-iotests/051.out | 23 +++++++++++++++++++++++ tests/qemu-iotests/051.pc.out | 23 +++++++++++++++++++++++ 4 files changed, 67 insertions(+) diff --git a/block.c b/block.c index 961ec97d26..ac8b3a3511 100644 --- a/block.c +++ b/block.c @@ -1156,6 +1156,12 @@ static void bdrv_assign_node_name(BlockDriverState *bs, goto out; } + /* Make sure that the node name isn't truncated */ + if (strlen(node_name) >= sizeof(bs->node_name)) { + error_setg(errp, "Node name too long"); + goto out; + } + /* copy node name into the bs and insert it into the graph list */ pstrcpy(bs->node_name, sizeof(bs->node_name), node_name); QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list); diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index f617e25e24..ee9c820d0f 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -99,6 +99,21 @@ run_qemu -drive file="$TEST_IMG",driver=foo run_qemu -drive file="$TEST_IMG",driver=raw,format=qcow2 run_qemu -drive file="$TEST_IMG",driver=qcow2,format=qcow2 +echo +echo === Node names === +echo + +# Maximum length: 31 characters +run_qemu -drive file="$TEST_IMG",node-name=x123456789012345678901234567890 +run_qemu -drive file="$TEST_IMG",node-name=x1234567890123456789012345678901 + +# First character must be alphabetic +# Following characters alphanumeric or -._ +run_qemu -drive file="$TEST_IMG",node-name=All-Types.of_all0wed_chars +run_qemu -drive file="$TEST_IMG",node-name=123foo +run_qemu -drive file="$TEST_IMG",node-name=_foo +run_qemu -drive file="$TEST_IMG",node-name=foo#12 + echo echo === Device without drive === echo diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index dd9846d1ce..b7273505c7 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -47,6 +47,29 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2 QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format' +=== Node names === + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=x123456789012345678901234567890 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) quit + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901: Node name too long + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=All-Types.of_all0wed_chars +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) quit + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=123foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=123foo: Invalid node name + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=_foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=_foo: Invalid node name + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=foo#12 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=foo#12: Invalid node name + + === Device without drive === Testing: -device VIRTIO_SCSI -device scsi-hd diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index b01f9a90d7..e9257fe318 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -47,6 +47,29 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2 QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format' +=== Node names === + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=x123456789012345678901234567890 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) quit + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901: Node name too long + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=All-Types.of_all0wed_chars +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) quit + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=123foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=123foo: Invalid node name + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=_foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=_foo: Invalid node name + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=foo#12 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=foo#12: Invalid node name + + === Device without drive === Testing: -device VIRTIO_SCSI -device scsi-hd From 0b68589d17ef2ea78b3cf016525db13cee1d99e7 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 4 Jul 2018 14:55:06 +0200 Subject: [PATCH 2069/2380] block/crypto: Fix memory leak in create error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: Coverity CID 1393782 Signed-off-by: Kevin Wolf Reviewed-by: Daniel P. Berrangé --- block/crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/crypto.c b/block/crypto.c index 994172a3de..146d81c90a 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -551,7 +551,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, /* Create protocol layer */ ret = bdrv_create_file(filename, opts, errp); if (ret < 0) { - return ret; + goto fail; } bs = bdrv_open(filename, NULL, NULL, From 2dacaf7c82c2771d507e5e59efcea78d933baca9 Mon Sep 17 00:00:00 2001 From: Ari Sundholm Date: Wed, 4 Jul 2018 17:59:34 +0300 Subject: [PATCH 2070/2380] block/blklogwrites: Change log_sector_size from int64_t to uint64_t This was a simple oversight when working on intermediate versions of the original patch which introduced blklogwrites. Signed-off-by: Ari Sundholm Signed-off-by: Kevin Wolf --- block/blklogwrites.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 47093fadd6..272e11a021 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -79,7 +79,7 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, QemuOpts *opts; Error *local_err = NULL; int ret; - int64_t log_sector_size; + uint64_t log_sector_size; opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); @@ -101,11 +101,9 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, log_sector_size = qemu_opt_get_size(opts, "log-sector-size", BDRV_SECTOR_SIZE); - if (log_sector_size < 0 || log_sector_size > (1ull << 23) || - !is_power_of_2(log_sector_size)) - { + if (log_sector_size > (1ull << 23) || !is_power_of_2(log_sector_size)) { ret = -EINVAL; - error_setg(errp, "Invalid log sector size %"PRId64, log_sector_size); + error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size); goto fail; } From 0878b3c113145d4e01d65aadd4efaf097a3fda4b Mon Sep 17 00:00:00 2001 From: Ari Sundholm Date: Wed, 4 Jul 2018 17:59:35 +0300 Subject: [PATCH 2071/2380] block/blklogwrites: Add an option for appending to an old log Suggested by Kevin Wolf. May be useful when testing multiple batches of writes or doing long-term testing involving restarts of the VM. Signed-off-by: Ari Sundholm Signed-off-by: Kevin Wolf --- block/blklogwrites.c | 147 ++++++++++++++++++++++++++++++++++++++----- qapi/block-core.json | 3 +- 2 files changed, 135 insertions(+), 15 deletions(-) diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 272e11a021..56154e7325 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -24,6 +24,10 @@ #define LOG_FUA_FLAG (1 << 1) #define LOG_DISCARD_FLAG (1 << 2) #define LOG_MARK_FLAG (1 << 3) +#define LOG_FLAG_MASK (LOG_FLUSH_FLAG \ + | LOG_FUA_FLAG \ + | LOG_DISCARD_FLAG \ + | LOG_MARK_FLAG) #define WRITE_LOG_VERSION 1ULL #define WRITE_LOG_MAGIC 0x6a736677736872ULL @@ -57,6 +61,11 @@ static QemuOptsList runtime_opts = { .name = "blklogwrites", .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), .desc = { + { + .name = "log-append", + .type = QEMU_OPT_BOOL, + .help = "Append to an existing log", + }, { .name = "log-sector-size", .type = QEMU_OPT_SIZE, @@ -72,6 +81,53 @@ static inline uint32_t blk_log_writes_log2(uint32_t value) return 31 - clz32(value); } +static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size) +{ + return sector_size < (1ull << 24) && is_power_of_2(sector_size); +} + +static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log, + uint32_t sector_size, + uint64_t nr_entries, + Error **errp) +{ + uint64_t cur_sector = 1; + uint64_t cur_idx = 0; + uint32_t sector_bits = blk_log_writes_log2(sector_size); + struct log_write_entry cur_entry; + + while (cur_idx < nr_entries) { + int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry, + sizeof(cur_entry)); + if (read_ret < 0) { + error_setg_errno(errp, -read_ret, + "Failed to read log entry %"PRIu64, cur_idx); + return (uint64_t)-1ull; + } + + if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) { + error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64, + le64_to_cpu(cur_entry.flags), cur_idx); + return (uint64_t)-1ull; + } + + /* Account for the sector of the entry itself */ + ++cur_sector; + + /* + * Account for the data of the write. + * For discards, this data is not present. + */ + if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) { + cur_sector += le64_to_cpu(cur_entry.nr_sectors); + } + + ++cur_idx; + } + + return cur_sector; +} + static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -80,6 +136,7 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, Error *local_err = NULL; int ret; uint64_t log_sector_size; + bool log_append; opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); @@ -98,20 +155,6 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - log_sector_size = qemu_opt_get_size(opts, "log-sector-size", - BDRV_SECTOR_SIZE); - - if (log_sector_size > (1ull << 23) || !is_power_of_2(log_sector_size)) { - ret = -EINVAL; - error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size); - goto fail; - } - - s->sectorsize = log_sector_size; - s->sectorbits = blk_log_writes_log2(log_sector_size); - s->cur_log_sector = 1; - s->nr_entries = 0; - /* Open the log file */ s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false, &local_err); @@ -121,7 +164,83 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + log_append = qemu_opt_get_bool(opts, "log-append", false); + + if (log_append) { + struct log_write_super log_sb = { 0, 0, 0, 0 }; + + if (qemu_opt_find(opts, "log-sector-size")) { + ret = -EINVAL; + error_setg(errp, "log-append and log-sector-size are mutually " + "exclusive"); + goto fail_log; + } + + /* Read log superblock or fake one for an empty log */ + if (!bdrv_getlength(s->log_file->bs)) { + log_sb.magic = cpu_to_le64(WRITE_LOG_MAGIC); + log_sb.version = cpu_to_le64(WRITE_LOG_VERSION); + log_sb.nr_entries = cpu_to_le64(0); + log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE); + } else { + ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb)); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read log superblock"); + goto fail_log; + } + } + + if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) { + ret = -EINVAL; + error_setg(errp, "Invalid log superblock magic"); + goto fail_log; + } + + if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) { + ret = -EINVAL; + error_setg(errp, "Unsupported log version %"PRIu64, + le64_to_cpu(log_sb.version)); + goto fail_log; + } + + log_sector_size = le32_to_cpu(log_sb.sectorsize); + s->cur_log_sector = 1; + s->nr_entries = 0; + + if (blk_log_writes_sector_size_valid(log_sector_size)) { + s->cur_log_sector = + blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size, + le64_to_cpu(log_sb.nr_entries), &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + goto fail_log; + } + + s->nr_entries = le64_to_cpu(log_sb.nr_entries); + } + } else { + log_sector_size = qemu_opt_get_size(opts, "log-sector-size", + BDRV_SECTOR_SIZE); + s->cur_log_sector = 1; + s->nr_entries = 0; + } + + if (!blk_log_writes_sector_size_valid(log_sector_size)) { + ret = -EINVAL; + error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size); + goto fail_log; + } + + s->sectorsize = log_sector_size; + s->sectorbits = blk_log_writes_log2(log_sector_size); + ret = 0; +fail_log: + if (ret < 0) { + bdrv_unref_child(bs, s->log_file); + s->log_file = NULL; + } fail: if (ret < 0) { bdrv_unref_child(bs, bs->file); diff --git a/qapi/block-core.json b/qapi/block-core.json index a9eab8cab8..d1753a2ae7 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3062,7 +3062,8 @@ { 'struct': 'BlockdevOptionsBlklogwrites', 'data': { 'file': 'BlockdevRef', 'log': 'BlockdevRef', - '*log-sector-size': 'uint32' } } + '*log-sector-size': 'uint32', + '*log-append': 'bool' } } ## # @BlockdevOptionsBlkverify: From 1dce698ea85bb18f62e4c540d4db628bacfba6ba Mon Sep 17 00:00:00 2001 From: Ari Sundholm Date: Wed, 4 Jul 2018 17:59:36 +0300 Subject: [PATCH 2072/2380] block/blklogwrites: Add an option for the update interval of the log superblock This is a way to ensure that the log superblock is periodically updated. Before, this was only done on flush requests, which may not be enough if the VM exits abnormally, omitting the final flush. The default interval is 4096 write requests. Signed-off-by: Ari Sundholm Signed-off-by: Kevin Wolf --- block/blklogwrites.c | 20 ++++++++++++++++++-- qapi/block-core.json | 6 +++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 56154e7325..63bf6b34a9 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -55,6 +55,7 @@ typedef struct { uint32_t sectorbits; uint64_t cur_log_sector; uint64_t nr_entries; + uint64_t update_interval; } BDRVBlkLogWritesState; static QemuOptsList runtime_opts = { @@ -71,6 +72,11 @@ static QemuOptsList runtime_opts = { .type = QEMU_OPT_SIZE, .help = "Log sector size", }, + { + .name = "log-super-update-interval", + .type = QEMU_OPT_NUMBER, + .help = "Log superblock update interval (# of write requests)", + }, { /* end of list */ } }, }; @@ -234,6 +240,14 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, s->sectorsize = log_sector_size; s->sectorbits = blk_log_writes_log2(log_sector_size); + s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval", + 4096); + if (!s->update_interval) { + ret = -EINVAL; + error_setg(errp, "Invalid log superblock update interval %"PRIu64, + s->update_interval); + goto fail_log; + } ret = 0; fail_log: @@ -360,8 +374,10 @@ static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr) lr->zero_size, 0); } - /* Update super block on flush */ - if (lr->log_ret == 0 && lr->entry.flags & LOG_FLUSH_FLAG) { + /* Update super block on flush or every update interval */ + if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG) + || (s->nr_entries % s->update_interval == 0))) + { struct log_write_super super = { .magic = cpu_to_le64(WRITE_LOG_MAGIC), .version = cpu_to_le64(WRITE_LOG_VERSION), diff --git a/qapi/block-core.json b/qapi/block-core.json index d1753a2ae7..38b31250f9 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3057,13 +3057,17 @@ # @log-sector-size: sector size used in logging writes to @file, determines # granularity of offsets and sizes of writes (default: 512) # +# @log-super-update-interval: interval of write requests after which the log +# super block is updated to disk (default: 4096) +# # Since: 3.0 ## { 'struct': 'BlockdevOptionsBlklogwrites', 'data': { 'file': 'BlockdevRef', 'log': 'BlockdevRef', '*log-sector-size': 'uint32', - '*log-append': 'bool' } } + '*log-append': 'bool', + '*log-super-update-interval': 'uint64' } } ## # @BlockdevOptionsBlkverify: From d815efcaf01b1698e2fdf0f3e125201025c53191 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 4 Jul 2018 16:47:50 +0200 Subject: [PATCH 2073/2380] file-posix: Fix creation locking raw_apply_lock_bytes() takes a bit mask of "permissions that are NOT shared". Also, make the "perm" and "shared" variables uint64_t, because I do not particularly like using ~ on signed integers (and other permission masks are usually uint64_t, too). Reported-by: Kevin Wolf Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/file-posix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 829ee538d8..b57c58e80f 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2112,7 +2112,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) { BlockdevCreateOptionsFile *file_opts; int fd; - int perm, shared; + uint64_t perm, shared; int result = 0; /* Validate options and set default values */ @@ -2148,7 +2148,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; /* Step one: Take locks */ - result = raw_apply_lock_bytes(fd, perm, shared, false, errp); + result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp); if (result < 0) { goto out_close; } From 7c20c808a5cbf5d244735bc78fc3138c739c1946 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 4 Jul 2018 16:47:51 +0200 Subject: [PATCH 2074/2380] file-posix: Unlock FD after creation Closing the FD does not necessarily mean that it is unlocked. Fix this by relinquishing all permission locks before qemu_close(). Reported-by: Kevin Wolf Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/file-posix.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index b57c58e80f..98987b80f1 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2111,6 +2111,7 @@ static int coroutine_fn raw_co_create(BlockdevCreateOptions *options, Error **errp) { BlockdevCreateOptionsFile *file_opts; + Error *local_err = NULL; int fd; uint64_t perm, shared; int result = 0; @@ -2156,13 +2157,13 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) /* Step two: Check that nobody else has taken conflicting locks */ result = raw_check_lock_bytes(fd, perm, shared, errp); if (result < 0) { - goto out_close; + goto out_unlock; } /* Clear the file by truncating it to 0 */ result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, errp); if (result < 0) { - goto out_close; + goto out_unlock; } if (file_opts->nocow) { @@ -2185,7 +2186,17 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) result = raw_regular_truncate(NULL, fd, file_opts->size, file_opts->preallocation, errp); if (result < 0) { - goto out_close; + goto out_unlock; + } + +out_unlock: + raw_apply_lock_bytes(fd, 0, 0, true, &local_err); + if (local_err) { + /* The above call should not fail, and if it does, that does + * not mean the whole creation operation has failed. So + * report it the user for their convenience, but do not report + * it to the caller. */ + error_report_err(local_err); } out_close: From 19020d41759c7d34eae78d6afbec27c523d55bcd Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 26 Jun 2018 10:56:42 +0200 Subject: [PATCH 2075/2380] coverity-model: Fix replay_get_byte() Coverity 2018.06 chokes on replay_get_byte(): $ cov-make-library -of scripts/coverity-model.xmldb scripts/coverity-model.c output file: scripts/coverity-model.xmldb Compiling scripts/coverity-model.c with command /opt/cov-sa-2018.06/bin/cov-emit --dir /tmp/cov-armbru/930a6fb31e5f464fc1a53354b2deb66b/cov-make-library-emit -w --no_error_recovery --emit_header_functions --no_implicit_decl --preinclude /opt/cov-sa-2018.06/library/decls.h --c scripts/coverity-model.c "scripts/coverity-model.c", line 110: error #20: identifier "replay_file" is undefined if (replay_file) { ^ Emit for file '/work/armbru/qemu/scripts/coverity-model.c' complete. [ERROR] 1 error detected in the compilation of "scripts/coverity-model.c". ERROR: cov-emit returned with code 1 Broken in commit 04a0afe5285. Fix by dumbing down. Signed-off-by: Markus Armbruster Message-Id: <20180626085642.4973-1-armbru@redhat.com> Reviewed-by: Paolo Bonzini --- scripts/coverity-model.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/scripts/coverity-model.c b/scripts/coverity-model.c index 48b112393b..2c0346ff25 100644 --- a/scripts/coverity-model.c +++ b/scripts/coverity-model.c @@ -106,12 +106,8 @@ static int get_keysym(const name2keysym_t *table, /* Replay data is considered trusted. */ uint8_t replay_get_byte(void) { - uint8_t byte = 0; - if (replay_file) { - uint8_t c; - byte = c; - } - return byte; + uint8_t byte; + return byte; } From 2b1f35b9a85cf0232615a67e7ff523137a58795e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 4 Jul 2018 07:30:11 +0100 Subject: [PATCH 2076/2380] Revert "Makefile: Rename TARGET_DIRS to TARGET_LIST" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 208ecb3e1acc8d55dab49fdf721a86d513691688. This was causing problems by making DEF_TARGET_LIST pointless and having to jump through hoops to build on mingw with a dully enabled config. This includes a change to fix the per-guest TCG test probe which was added after 208ecb3 and used TARGET_LIST. Signed-off-by: Alex Bennée Cc: Paolo Bonzini --- Makefile | 20 ++++++++++---------- configure | 2 +- scripts/create_config | 2 +- tests/Makefile.include | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 7ed9cc4a21..39fd4c0546 100644 --- a/Makefile +++ b/Makefile @@ -60,8 +60,8 @@ seems to have been used for an in-tree build. You can fix this by running \ endif endif -CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_LIST)),y) -CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_LIST)),y) +CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_DIRS)),y) +CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_DIRS)),y) CONFIG_XEN := $(CONFIG_XEN_BACKEND) CONFIG_ALL=y -include config-all-devices.mak @@ -365,8 +365,8 @@ DOCS= endif SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR) -SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_LIST)) -SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_LIST)) +SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) +SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS)) ifeq ($(SUBDIR_DEVICES_MAK),) config-all-devices.mak: @@ -469,7 +469,7 @@ config-host.h-timestamp: config-host.mak qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@") -SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_LIST)) +SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) @@ -513,7 +513,7 @@ ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) romsubdir-%: $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pc-bios/$* V="$(V)" TARGET_DIR="$*/" CFLAGS="$(filter -O% -g%,$(CFLAGS))",) -ALL_SUBDIRS=$(TARGET_LIST) $(patsubst %,pc-bios/%, $(ROMS)) +ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS)) recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES) @@ -770,7 +770,7 @@ distclean: clean rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html rm -f docs/qemu-block-drivers.7 - for d in $(TARGET_LIST); do \ + for d in $(TARGET_DIRS); do \ rm -rf $$d || exit 1 ; \ done rm -Rf .sdk @@ -871,7 +871,7 @@ endif $(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \ done $(INSTALL_DATA) $(BUILD_DIR)/trace-events-all "$(DESTDIR)$(qemu_datadir)/trace-events-all" - for d in $(TARGET_LIST); do \ + for d in $(TARGET_DIRS); do \ $(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \ done @@ -1066,9 +1066,9 @@ endif @echo ' ctags/TAGS - Generate tags file for editors' @echo ' cscope - Generate cscope index' @echo '' - @$(if $(TARGET_LIST), \ + @$(if $(TARGET_DIRS), \ echo 'Architecture specific targets:'; \ - $(foreach t, $(TARGET_LIST), \ + $(foreach t, $(TARGET_DIRS), \ printf " %-30s - Build for %s\\n" $(patsubst %,subdir-%,$(t)) $(t);) \ echo '') @echo 'Cleaning targets:' diff --git a/configure b/configure index b99ebdde24..2a7796ea80 100755 --- a/configure +++ b/configure @@ -6212,7 +6212,7 @@ qemu_version=$(head $source_path/VERSION) echo "VERSION=$qemu_version" >>$config_host_mak echo "PKGVERSION=$pkgversion" >>$config_host_mak echo "SRC_PATH=$source_path" >> $config_host_mak -echo "TARGET_LIST=$target_list" >> $config_host_mak +echo "TARGET_DIRS=$target_list" >> $config_host_mak if [ "$docs" = "yes" ] ; then echo "BUILD_DOCS=yes" >> $config_host_mak fi diff --git a/scripts/create_config b/scripts/create_config index 58948a67a4..d727e5e36e 100755 --- a/scripts/create_config +++ b/scripts/create_config @@ -107,7 +107,7 @@ case $line in target_name=${line#*=} echo "#define TARGET_NAME \"$target_name\"" ;; - TARGET_LIST=*) + TARGET_DIRS=*) # do nothing ;; TARGET_*=y) # configuration diff --git a/tests/Makefile.include b/tests/Makefile.include index 1affc49ca3..0c0f9509f5 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -874,7 +874,7 @@ endif # QTest rules -TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_LIST))) +TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS))) ifeq ($(CONFIG_POSIX),y) QTEST_TARGETS = $(TARGETS) check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y)) @@ -938,14 +938,14 @@ check-report.html: check-report.xml # Per guest TCG tests -LINUX_USER_TARGETS=$(filter %-linux-user,$(TARGET_LIST)) +LINUX_USER_TARGETS=$(filter %-linux-user,$(TARGET_DIRS)) BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(LINUX_USER_TARGETS)) CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(LINUX_USER_TARGETS)) RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(LINUX_USER_TARGETS)) ifeq ($(HAVE_USER_DOCKER),y) # Probe for the Docker Builds needed for each build -$(foreach PROBE_TARGET,$(TARGET_LIST), \ +$(foreach PROBE_TARGET,$(TARGET_DIRS), \ $(eval -include $(SRC_PATH)/tests/tcg/Makefile.probe) \ $(if $(DOCKER_PREREQ), \ $(eval build-tcg-tests-$(PROBE_TARGET): $(DOCKER_PREREQ)))) From ebdfb6101dac71b1db2e9cd66111e865cc1f22e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 23:15:46 -0300 Subject: [PATCH 2077/2380] travis: do not waste time cloning unused submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds only require: - dtc - keycodemapdb - capstone Signed-off-by: Philippe Mathieu-Daudé [AJB: drop wget cache] Signed-off-by: Alex Bennée Reviewed-by: Daniel P. Berrangé --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bd66c18fed..134d5331fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,8 +66,7 @@ git: before_install: - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi - - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ - - git submodule update --init --recursive + - git submodule update --init --recursive capstone dtc ui/keycodemapdb before_script: - ./configure ${CONFIG} || { cat config.log && exit 1; } script: From ebf2ff659baac238b5d7522b75453da3f4901935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Jun 2018 09:26:37 -0300 Subject: [PATCH 2078/2380] travis: test out-of-tree builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Force one config to build 'out-of-tree' (object files and executables are created in a tree outside the project source code). Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Reviewed-by: Daniel P. Berrangé --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 134d5331fe..32188d51f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,6 +50,8 @@ notifications: on_failure: always env: global: + - SRC_DIR="." + - BUILD_DIR="." - TEST_CMD="make check" - MAKEFLAGS="-j3" matrix: @@ -68,11 +70,15 @@ before_install: - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi - git submodule update --init --recursive capstone dtc ui/keycodemapdb before_script: - - ./configure ${CONFIG} || { cat config.log && exit 1; } + - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} + - ${SRC_DIR}/configure ${CONFIG} || { cat config.log && exit 1; } script: - make ${MAKEFLAGS} && ${TEST_CMD} matrix: include: + # Test out-of-tree builds + - env: CONFIG="--enable-debug --enable-debug-tcg" + BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.." # Test with Clang for compile portability (Travis uses clang-5.0) - env: CONFIG="--disable-system" compiler: clang From 31d2dda3c4091c6d823c5103a982b4c08a2f4b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 20 Jun 2018 11:28:51 +0100 Subject: [PATCH 2079/2380] build-system: remove per-test GCOV reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm not entirely sure who's using this information and certainly in a CI environment it just washes over as additional noise. Later patches will provide new reporting options so a user who wants to analyse individual tests will be able to use that to get the information. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé --- docs/devel/testing.rst | 11 +++++------ tests/Makefile.include | 10 ---------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index f33e5a8423..66ef219f69 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -158,12 +158,11 @@ rarely used. See "QEMU iotests" section below for more information. GCC gcov support ---------------- -``gcov`` is a GCC tool to analyze the testing coverage by instrumenting the -tested code. To use it, configure QEMU with ``--enable-gcov`` option and build. -Then run ``make check`` as usual. There will be additional ``gcov`` output as -the testing goes on, showing the test coverage percentage numbers per analyzed -source file. More detailed reports can be obtained by running ``gcov`` command -on the output files under ``$build_dir/tests/``, please read the ``gcov`` +``gcov`` is a GCC tool to analyze the testing coverage by +instrumenting the tested code. To use it, configure QEMU with +``--enable-gcov`` option and build. Then run ``make check`` as usual. +Reports can be obtained by running ``gcov`` command on the output +files under ``$build_dir/tests/``, please read the ``gcov`` documentation for more information. QEMU iotests diff --git a/tests/Makefile.include b/tests/Makefile.include index 0c0f9509f5..a49282704e 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -897,26 +897,16 @@ GCOV_OPTIONS = -n $(if $(V),-f,) .PHONY: $(patsubst %, check-qtest-%, $(QTEST_TARGETS)) $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: subdir-%-softmmu $(check-qtest-y) - $(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,) $(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ QTEST_QEMU_IMG=qemu-img$(EXESUF) \ MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} \ gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y) $(check-qtest-generic-y),"GTESTER","$@") - $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y) $(gcov-files-generic-y); do \ - echo Gcov report for $$f:;\ - $(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \ - done,) .PHONY: $(patsubst %, check-%, $(check-unit-y) $(check-speed-y)) $(patsubst %, check-%, $(check-unit-y) $(check-speed-y)): check-%: % - $(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,) $(call quiet-command, \ MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} \ gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER","$*") - $(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y) $(gcov-files-generic-y); do \ - echo Gcov report for $$f:;\ - $(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \ - done,) # gtester tests with XML output From 6786dc3442f2f691d208d15218576136727d0890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 20 Jun 2018 11:35:47 +0100 Subject: [PATCH 2080/2380] .gitignore: add .gcov files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are temporary files generated on gcov runs and shouldn't be included in the source tree. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9da3b3e626..5668d02782 100644 --- a/.gitignore +++ b/.gitignore @@ -155,6 +155,7 @@ .sdk *.gcda *.gcno +*.gcov /pc-bios/bios-pq/status /pc-bios/vgabios-pq/status /pc-bios/optionrom/linuxboot.asm From 95f7aabf49a1223f23b0b1f66201ce22ff3695ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 25 Jun 2018 10:06:35 +0100 Subject: [PATCH 2081/2380] docker: add gcovr to travis image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Useful for debugging if nothing else as the gcovr on the Travis images are a little old. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé --- tests/docker/dockerfiles/travis.docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/travis.docker b/tests/docker/dockerfiles/travis.docker index c5ad39b533..03ebfb0ef2 100644 --- a/tests/docker/dockerfiles/travis.docker +++ b/tests/docker/dockerfiles/travis.docker @@ -5,7 +5,7 @@ ENV LC_ALL en_US.UTF-8 RUN cat /etc/apt/sources.list | sed "s/# deb-src/deb-src/" >> /etc/apt/sources.list RUN apt-get update RUN apt-get -y build-dep qemu -RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools +RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools gcovr # Travis tools require PhantomJS / Neo4j / Maven accessible # in their PATH (QEMU build won't access them). ENV PATH /usr/local/phantomjs/bin:/usr/local/phantomjs:/usr/local/neo4j-3.2.7/bin:/usr/local/maven-3.5.2/bin:/usr/local/cmake-3.9.2/bin:/usr/local/clang-5.0.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin From 0708e6476fad27c4d98c5c302a7d7ca475a41369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 20 Jun 2018 12:00:07 +0100 Subject: [PATCH 2082/2380] travis: add gcovr summary for GCOV build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gives a more useful summary, sorted by descending % coverage, after the tests have run. The final numbers will give an idea if our coverage is getting better or worse. To keep the width sane we need to post process the file that the old gcovr tool generates. This is done with a mix of sed, awk and column in the scripts/coverage-summary.sh script. As quite a lot of lines don't get covered at all we filter out all the 0% lines. If the file doesn't appear it is not being exercised. Signed-off-by: Alex Bennée Reviewed-by: Daniel P. Berrangé --- .travis.yml | 3 +++ MAINTAINERS | 1 + scripts/travis/coverage-summary.sh | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100755 scripts/travis/coverage-summary.sh diff --git a/.travis.yml b/.travis.yml index 32188d51f1..95be6ec59f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,7 @@ addons: - libvte-2.90-dev - sparse - uuid-dev + - gcovr # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu # to prevent IRC notifications from forks. This was created using: @@ -86,6 +87,8 @@ matrix: compiler: clang # gprof/gcov are GCC features - env: CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" + after_success: + - ${SRC_DIR}/scripts/travis/coverage-summary.sh compiler: gcc # We manually include builds which we disable "make check" for - env: CONFIG="--enable-debug --enable-tcg-interpreter" diff --git a/MAINTAINERS b/MAINTAINERS index 6630d691d1..6a13f70f99 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2166,6 +2166,7 @@ R: Philippe Mathieu-Daudé L: qemu-devel@nongnu.org S: Maintained F: .travis.yml +F: scripts/travis/ F: .shippable.yml F: tests/docker/ F: tests/vm/ diff --git a/scripts/travis/coverage-summary.sh b/scripts/travis/coverage-summary.sh new file mode 100755 index 0000000000..d7086cf9ca --- /dev/null +++ b/scripts/travis/coverage-summary.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Author: Alex Bennée +# +# Summerise the state of code coverage with gcovr and tweak the output +# to be more sane on Travis hosts. As we expect to be executed on a +# throw away CI instance we do spam temp files all over the shop. You +# most likely don't want to execute this script but just call gcovr +# directly. See also "make coverage-report" +# +# This code is licensed under the GPL version 2 or later. See +# the COPYING file in the top-level directory. + +# first generate the coverage report +gcovr -p -o raw-report.txt + +# strip the full-path and line markers +sed s@$PWD\/@@ raw-report.txt | sed s/[0-9]\*[,-]//g > simplified.txt + +# reflow lines that got split +awk '/.[ch]$/ { printf("%s", $0); next } 1' simplified.txt > rejoined.txt + +# columnify +column -t rejoined.txt > final.txt + +# and dump, stripping out 0% coverage +grep -v "0%" final.txt From 990e6a2754fc16501470b661c4af94ec036f1190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 20 Jun 2018 12:34:45 +0100 Subject: [PATCH 2083/2380] build-system: add clean-coverage target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This can be used to remove any stale coverage data before any particular test run. This is useful for analysing individual tests. Signed-off-by: Alex Bennée Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- --- Makefile | 11 +++++++++++ docs/devel/testing.rst | 11 ++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 39fd4c0546..9731ec1bb7 100644 --- a/Makefile +++ b/Makefile @@ -723,6 +723,14 @@ module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak $(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ "GEN","$@") +ifdef CONFIG_GCOV +.PHONY: clean-coverage +clean-coverage: + $(call quiet-command, \ + find . \( -name '*.gcda' -o -name '*.gcov' \) -type f -exec rm {} +, \ + "CLEAN", "coverage files") +endif + clean: # avoid old build problems by removing potentially incorrect old files rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h @@ -1073,6 +1081,9 @@ endif echo '') @echo 'Cleaning targets:' @echo ' clean - Remove most generated files but keep the config' +ifdef CONFIG_GCOV + @echo ' clean-coverage - Remove coverage files' +endif @echo ' distclean - Remove all generated files' @echo ' dist - Build a distributable tarball' @echo '' diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 66ef219f69..7f04ca104e 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -161,9 +161,14 @@ GCC gcov support ``gcov`` is a GCC tool to analyze the testing coverage by instrumenting the tested code. To use it, configure QEMU with ``--enable-gcov`` option and build. Then run ``make check`` as usual. -Reports can be obtained by running ``gcov`` command on the output -files under ``$build_dir/tests/``, please read the ``gcov`` -documentation for more information. + +If you want to gather coverage information on a single test the ``make +clean-coverage`` target can be used to delete any existing coverage +information before running a single test. + +Reports can be obtained by running ``gcov`` command +on the output files under ``$build_dir/tests/``, please read the +``gcov`` documentation for more information. QEMU iotests ============ From fe8bf5f62972ce9f227ae3e25767116a6d221b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 20 Jun 2018 14:04:24 +0100 Subject: [PATCH 2084/2380] build-system: add coverage-report target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will build a coverage report under the current directory in reports/coverage. At the users option a report can be generated by directly invoking something like: make foo/bar/coverage-report.html Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé --- Makefile | 13 +++++++++++++ docs/devel/testing.rst | 11 ++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 9731ec1bb7..2da686be33 100644 --- a/Makefile +++ b/Makefile @@ -986,6 +986,16 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \ docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi +# Reports/Analysis + +%/coverage-report.html: + @mkdir -p $* + $(call quiet-command,\ + gcovr -p --html --html-details -o $@, \ + "GEN", "coverage-report.html") + +.PHONY: coverage-report +coverage-report: $(CURDIR)/reports/coverage/coverage-report.html ifdef CONFIG_WIN32 @@ -1095,6 +1105,9 @@ endif @echo 'Documentation targets:' @echo ' html info pdf txt' @echo ' - Build documentation in specified format' +ifdef CONFIG_GCOV + @echo ' coverage-report - Create code coverage report' +endif @echo '' ifdef CONFIG_WIN32 @echo 'Windows targets:' diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 7f04ca104e..5e19cd50da 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -166,9 +166,14 @@ If you want to gather coverage information on a single test the ``make clean-coverage`` target can be used to delete any existing coverage information before running a single test. -Reports can be obtained by running ``gcov`` command -on the output files under ``$build_dir/tests/``, please read the -``gcov`` documentation for more information. +You can generate a HTML coverage report by executing ``make +coverage-report`` which will create +./reports/coverage/coverage-report.html. If you want to create it +elsewhere simply execute ``make /foo/bar/baz/coverage-report.html``. + +Further analysis can be conducted by running the ``gcov`` command +directly on the various .gcda output files. Please read the ``gcov`` +documentation for more information. QEMU iotests ============ From 708b6a643c7a974ffadf64e00019bdcd60edf6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 22 Jun 2018 17:09:10 +0100 Subject: [PATCH 2085/2380] linux-user: introduce preexit_cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid repeating ourselves move our preexit clean-up code into a helper function. I figured the continuing effort to split of the syscalls made it worthwhile creating a new file for it now. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Laurent Vivier --- linux-user/Makefile.objs | 2 +- linux-user/exit.c | 28 ++++++++++++++++++++++++++++ linux-user/qemu.h | 8 ++++++++ linux-user/syscall.c | 10 ++-------- 4 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 linux-user/exit.c diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs index 59a5c17354..b5dfb71f25 100644 --- a/linux-user/Makefile.objs +++ b/linux-user/Makefile.objs @@ -1,7 +1,7 @@ obj-y = main.o syscall.o strace.o mmap.o signal.o \ elfload.o linuxload.o uaccess.o uname.o \ safe-syscall.o $(TARGET_ABI_DIR)/signal.o \ - $(TARGET_ABI_DIR)/cpu_loop.o + $(TARGET_ABI_DIR)/cpu_loop.o exit.o obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_I386) += vm86.o diff --git a/linux-user/exit.c b/linux-user/exit.c new file mode 100644 index 0000000000..aed8713fae --- /dev/null +++ b/linux-user/exit.c @@ -0,0 +1,28 @@ +/* + * exit support for qemu + * + * Copyright (c) 2018 Alex Bennée + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" + +void preexit_cleanup(CPUArchState *env, int code) +{ +#ifdef TARGET_GPROF + _mcleanup(); +#endif + gdb_exit(env, code); +} diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 793cd4df04..bb85c81aa4 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -623,6 +623,14 @@ static inline int is_error(abi_long ret) return (abi_ulong)ret >= (abi_ulong)(-4096); } +/** + * preexit_cleanup: housekeeping before the guest exits + * + * env: the CPU state + * code: the exit code + */ +void preexit_cleanup(CPUArchState *env, int code); + /* Include target-specific struct and function definitions; * they may need access to the target-independent structures * above, so include them last. diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 50e20fb659..5822e03e28 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8022,10 +8022,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } cpu_list_unlock(); -#ifdef TARGET_GPROF - _mcleanup(); -#endif - gdb_exit(cpu_env, arg1); + preexit_cleanup(cpu_env, arg1); _exit(arg1); ret = 0; /* avoid warning */ break; @@ -10131,10 +10128,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef __NR_exit_group /* new thread calls */ case TARGET_NR_exit_group: -#ifdef TARGET_GPROF - _mcleanup(); -#endif - gdb_exit(cpu_env, arg1); + preexit_cleanup(cpu_env, arg1); ret = get_errno(exit_group(arg1)); break; #endif From 7781afb49cdcc41b5cd06d4daa53aa1bc45f95ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 22 Jun 2018 17:23:44 +0100 Subject: [PATCH 2086/2380] linux-user: add gcov support to preexit_cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we don't always take the normal exit path when running a guest we can skip the normal exit destructors where gcov normally dumps it's info. The GCC manual suggests long running programs use __gcov_dump() to flush out the coverage state periodically so we use that here. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- linux-user/exit.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/linux-user/exit.c b/linux-user/exit.c index aed8713fae..14e94e28fa 100644 --- a/linux-user/exit.c +++ b/linux-user/exit.c @@ -19,10 +19,17 @@ #include "qemu/osdep.h" #include "qemu.h" +#ifdef CONFIG_GCOV +extern void __gcov_dump(void); +#endif + void preexit_cleanup(CPUArchState *env, int code) { #ifdef TARGET_GPROF _mcleanup(); +#endif +#ifdef CONFIG_GCOV + __gcov_dump(); #endif gdb_exit(env, code); } From beac6a98f6eb271f2520a329ca051313afd70a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Jun 2018 13:46:36 -0300 Subject: [PATCH 2087/2380] docker: ubuntu: Update the package list before installing new ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since docker caches the different layers, updating the package list does not invalidate the previous "apt-get update" layer, and it is likely "apt-get install" hits an outdated repository. See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get This fixes: $ make docker-image-ubuntu V=1 ./tests/docker/docker.py build qemu:ubuntu tests/docker/dockerfiles/ubuntu.docker --add-current-user Sending build context to Docker daemon 3.072kB [...] E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/m/mesa/libgles2-mesa_17.0.7-0ubuntu0.16.04.2_amd64.deb 404 Not Found E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/m/mesa/libgles2-mesa-dev_17.0.7-0ubuntu0.16.04.2_amd64.deb 404 Not Found E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing? The command '/bin/sh -c apt-get -y install $PACKAGES' returned a non-zero code: 100 tests/docker/Makefile.include:40: recipe for target 'docker-image-ubuntu' failed make: *** [docker-image-ubuntu] Error 1 Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée --- tests/docker/dockerfiles/ubuntu.docker | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker index dabbf2a8a4..c03520ce3f 100644 --- a/tests/docker/dockerfiles/ubuntu.docker +++ b/tests/docker/dockerfiles/ubuntu.docker @@ -1,7 +1,6 @@ FROM ubuntu:16.04 RUN echo "deb http://archive.ubuntu.com/ubuntu/ trusty universe multiverse" >> \ /etc/apt/sources.list -RUN apt-get update ENV PACKAGES flex bison \ libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev libncursesw5-dev \ libseccomp-dev libgnutls-dev libssh2-1-dev libspice-server-dev \ @@ -13,6 +12,7 @@ ENV PACKAGES flex bison \ libjemalloc-dev libcacard-dev libusbredirhost-dev libnfs-dev libcap-dev libattr1-dev \ texinfo \ gettext git make ccache python-yaml gcc clang sparse -RUN apt-get -y install $PACKAGES +RUN apt-get update && \ + apt-get -y install $PACKAGES RUN dpkg -l $PACKAGES | sort > /packages.txt ENV FEATURES clang pyyaml From daf999f77acfbe3ae09b5b8b58c140d9fdf38409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Jun 2018 13:46:37 -0300 Subject: [PATCH 2088/2380] docker: ubuntu: Use SDL2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not test the deprecated API versions (see cabd35840749d). Debian MXE MinGW cross images are already using SDL2. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée --- tests/docker/dockerfiles/ubuntu.docker | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker index c03520ce3f..7d724e7f53 100644 --- a/tests/docker/dockerfiles/ubuntu.docker +++ b/tests/docker/dockerfiles/ubuntu.docker @@ -5,7 +5,7 @@ ENV PACKAGES flex bison \ libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev libncursesw5-dev \ libseccomp-dev libgnutls-dev libssh2-1-dev libspice-server-dev \ libspice-protocol-dev libnss3-dev libfdt-dev \ - libgtk-3-dev libvte-2.91-dev libsdl1.2-dev libpng12-dev libpixman-1-dev \ + libgtk-3-dev libvte-2.91-dev libsdl2-dev libpng12-dev libpixman-1-dev \ libvdeplug-dev liblzo2-dev libsnappy-dev libbz2-dev libxen-dev librdmacm-dev libibverbs-dev \ libsasl2-dev libjpeg-turbo8-dev xfslibs-dev libcap-ng-dev libbrlapi-dev libcurl4-gnutls-dev \ libbluetooth-dev librbd-dev libaio-dev glusterfs-common libnuma-dev libepoxy-dev libdrm-dev libgbm-dev \ @@ -15,4 +15,4 @@ ENV PACKAGES flex bison \ RUN apt-get update && \ apt-get -y install $PACKAGES RUN dpkg -l $PACKAGES | sort > /packages.txt -ENV FEATURES clang pyyaml +ENV FEATURES clang pyyaml sdl2 From 48feb682db13372c5cce42156a48afabfbbb28e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Jun 2018 13:46:38 -0300 Subject: [PATCH 2089/2380] docker: Clean the MXE base image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the duplicated same package is confusing. Reported-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée --- tests/docker/dockerfiles/debian8-mxe.docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/debian8-mxe.docker b/tests/docker/dockerfiles/debian8-mxe.docker index 9b8e577b03..2df4cc8c5c 100644 --- a/tests/docker/dockerfiles/debian8-mxe.docker +++ b/tests/docker/dockerfiles/debian8-mxe.docker @@ -14,6 +14,6 @@ RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive eatmydata \ apt-get install -y --no-install-recommends \ libpython2.7-stdlib \ - $(apt-get -s install -y --no-install-recommends gw32.shared-mingw-w64 gw32.shared-mingw-w64 | egrep "^Inst mxe-x86-64-unknown-" | cut -d\ -f2) + $(apt-get -s install -y --no-install-recommends gw32.shared-mingw-w64 | egrep "^Inst mxe-x86-64-unknown-" | cut -d\ -f2) ENV PATH $PATH:/usr/lib/mxe/usr/bin/ From 272e551b43e9a1c88cef774b24c97bcea887c291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Jun 2018 13:46:41 -0300 Subject: [PATCH 2090/2380] docker: Do not run tests in 'intermediate' images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can still build the DOCKER_INTERMEDIATE_IMAGES images, but they won't appear in 'make test*@$IMAGE'. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée --- tests/docker/Makefile.include | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 91d9665517..942d05649f 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -5,6 +5,8 @@ DOCKER_SUFFIX := .docker DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles DOCKER_DEPRECATED_IMAGES := debian +# we don't run tests on intermediate images (used as base by another image) +DOCKER_INTERMEDIATE_IMAGES := debian8 debian9 debian8-mxe debian-ports debian-sid DOCKER_IMAGES := $(filter-out $(DOCKER_DEPRECATED_IMAGES),$(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))))) DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES)) # Use a global constant ccache directory to speed up repetitive builds @@ -98,7 +100,7 @@ docker-image-travis: NOUSER=1 docker-image-tricore-cross: docker-image-debian9 # Expand all the pre-requistes for each docker image and test combination -$(foreach i,$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES), \ +$(foreach i,$(filter-out $(DOCKER_INTERMEDIATE_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES)), \ $(foreach t,$(DOCKER_TESTS) $(DOCKER_TOOLS), \ $(eval .PHONY: docker-$t@$i) \ $(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \ From e6bfdeca8b688913270334fa89c39a9c1ee2662a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 29 Jun 2018 17:46:02 +0100 Subject: [PATCH 2091/2380] docker: drop QEMU build-dep from bootstrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is best done with any child images that actually need it. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/debian-bootstrap.docker | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/docker/dockerfiles/debian-bootstrap.docker b/tests/docker/dockerfiles/debian-bootstrap.docker index 3a9125e497..14212b9cf4 100644 --- a/tests/docker/dockerfiles/debian-bootstrap.docker +++ b/tests/docker/dockerfiles/debian-bootstrap.docker @@ -17,5 +17,3 @@ RUN /debootstrap/debootstrap --second-stage # At this point we can install additional packages if we want # Duplicate deb line as deb-src RUN cat /etc/apt/sources.list | sed "s/deb/deb-src/" >> /etc/apt/sources.list -RUN apt-get update -RUN apt-get -y build-dep qemu From 300cf467fd4f520a60d3eff7e73813337165dbcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 2 Jul 2018 14:02:44 +0100 Subject: [PATCH 2092/2380] docker: debian-bootstrap.pre allow customising of variant/url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We default to the buildd variant as most of our images are for building. However lets give the user the ability to specify "minbase" if they want to create a simple base image for experimentation. Allowing the tweaking of DEB_URL means we can also bootstrap other Debian based OS's. For example: make docker-binfmt-image-debian-ubuntu-bionic-arm64 \ DEB_ARCH=arm64 DEB_TYPE=bionic \ DEB_VARIANT=minbase DEB_URL=http://ports.ubuntu.com/ \ EXECUTABLE=./aarch64-linux-user/qemu-aarch64 Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/debian-bootstrap.pre | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/debian-bootstrap.pre b/tests/docker/dockerfiles/debian-bootstrap.pre index 7c76dce663..56e1aa7a21 100755 --- a/tests/docker/dockerfiles/debian-bootstrap.pre +++ b/tests/docker/dockerfiles/debian-bootstrap.pre @@ -32,6 +32,15 @@ if [ -z "${DEB_TYPE}" ]; then fi +# The following allow finer grain control over the defaults +if [ -z "${DEB_VARIANT}" ]; then + DEB_VARIANT=buildd +fi + +if [ -z "${DEB_URL}" ]; then + DEB_URL="http://httpredir.debian.org/debian" +fi + # We check in order for # # - DEBOOTSTRAP_DIR pointing at a development checkout @@ -107,5 +116,5 @@ fi echo "Building a rootfs using ${FAKEROOT} and ${DEBOOTSTRAP} ${DEB_ARCH}/${DEB_TYPE}" -${FAKEROOT} ${DEBOOTSTRAP} --variant=buildd --foreign --arch=$DEB_ARCH $DEB_TYPE . http://httpredir.debian.org/debian || exit 1 +${FAKEROOT} ${DEBOOTSTRAP} --variant=$DEB_VARIANT --foreign --arch=$DEB_ARCH $DEB_TYPE . $DEB_URL || exit 1 exit 0 From 547cb45ea309dfdbaa8395dcc4c6330899118e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 29 Jun 2018 17:46:49 +0100 Subject: [PATCH 2093/2380] docker: add special handling for FROM:debian-%-user targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These will have been build with debootstrap so we need to check against the debian-bootstrap dockerfile. This does mean sticking to debian-FOO-user as the naming conventions for boot-strapped images. The actual cross image is built on top. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/docker.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index b279836154..69e7130db7 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -113,6 +113,10 @@ def _copy_binary_with_libs(src, dest_dir): _copy_with_mkdir(l , dest_dir, so_path) def _read_qemu_dockerfile(img_name): + # special case for Debian linux-user images + if img_name.startswith("debian") and img_name.endswith("user"): + img_name = "debian-bootstrap" + df = os.path.join(os.path.dirname(__file__), "dockerfiles", img_name + ".docker") return open(df, "r").read() From dc338fdb07cf92b102c5ecfb4a4c259229404139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 29 Jun 2018 17:57:57 +0100 Subject: [PATCH 2094/2380] docker: add special rule for deboostrapped images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We might as well have a custom rule for this. For one thing the dependencies are different. As the primary dependency for docker-image-% could never be docker-image-debian-bootstrap we can drop that test in the main rule as well. Missing EXECUTABLE, DEB_ARCH and DEB_TYPE are treated as hard faults now. We also error out if the EXECUTABLE file isn't there. We should really do this with a dependency on any source rules but currently subdir-FOO-linux-user isn't enough on a clean build. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 942d05649f..7b99df5464 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -46,9 +46,6 @@ docker-image-%: $(DOCKER_FILES_DIR)/%.docker "CHECK", "$*") else docker-image-%: $(DOCKER_FILES_DIR)/%.docker - @if test "$@" = docker-image-debian-bootstrap -a -z "$(EXECUTABLE)"; then \ - echo WARNING: EXECUTABLE is not set, debootstrap may fail. 2>&1 ; \ - fi $(call quiet-command,\ $(DOCKER_SCRIPT) build qemu:$* $< \ $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \ @@ -56,6 +53,27 @@ docker-image-%: $(DOCKER_FILES_DIR)/%.docker $(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES))\ $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)),\ "BUILD","$*") + +# Special rule for debootstraped binfmt linux-user images +docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker + $(if $(EXECUTABLE),,\ + $(error EXECUTABLE not set, debootstrap of debian-$* would fail)) + $(if $(wildcard $(EXECUTABLE)),,\ + $(error Please build $(EXECUTABLE) first)) + $(if $(DEB_ARCH),,\ + $(error DEB_ARCH not set, debootstrap of debian-$* would fail)) + $(if $(DEB_TYPE),,\ + $(error DEB_TYPE not set, debootstrap of debian-$* would fail)) + $(call quiet-command, \ + DEB_ARCH=$(DEB_ARCH) \ + DEB_TYPE=$(DEB_TYPE) \ + $(DOCKER_SCRIPT) build qemu:debian-$* $< \ + $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \ + $(if $(NOUSER),,--add-current-user) \ + $(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES)) \ + $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)), \ + "BUILD","binfmt debian-$* (debootstrapped)") + endif # Enforce dependencies for composite images From 19c9a18f45fc8d8b8e96a6b8ea6074d08b6a8612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 29 Jun 2018 20:41:26 +0100 Subject: [PATCH 2095/2380] docker: add linux-user powered cross builder for QEMU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can't use cross compilers in the current Debian stable and Debian sid is sketchy as hell. So for powerpc fall back to dog-fooding our own linux-user to do the build. As we can only build the base image with a suitably configured source tree we fall back to checking for its existence when we can't build it from scratch. However this does mean you don't have to keep a static powerpc-linux-user in your active configuration just to update the cross build image. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 42 +++++++++++++++---- .../debian-powerpc-user-cross.docker | 15 +++++++ 2 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 tests/docker/dockerfiles/debian-powerpc-user-cross.docker diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 7b99df5464..b2a7e761cc 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -64,15 +64,19 @@ docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker $(error DEB_ARCH not set, debootstrap of debian-$* would fail)) $(if $(DEB_TYPE),,\ $(error DEB_TYPE not set, debootstrap of debian-$* would fail)) - $(call quiet-command, \ - DEB_ARCH=$(DEB_ARCH) \ - DEB_TYPE=$(DEB_TYPE) \ - $(DOCKER_SCRIPT) build qemu:debian-$* $< \ - $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \ - $(if $(NOUSER),,--add-current-user) \ - $(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES)) \ - $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)), \ - "BUILD","binfmt debian-$* (debootstrapped)") + $(if $(filter $(QEMU_TARGET),$(TARGET_DIRS)), \ + $(call quiet-command, \ + DEB_ARCH=$(DEB_ARCH) \ + DEB_TYPE=$(DEB_TYPE) \ + $(DOCKER_SCRIPT) build qemu:debian-$* $< \ + $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \ + $(if $(NOUSER),,--add-current-user) \ + $(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES)) \ + $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)), \ + "BUILD","binfmt debian-$* (debootstrapped)"), \ + $(call quiet-command, \ + $(DOCKER_SCRIPT) check --quiet qemu:debian-$* $<, \ + "CHECK", "debian-$* exists")) endif @@ -117,6 +121,21 @@ docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools docker-image-tricore-cross: docker-image-debian9 +# Rules for building linux-user powered images +# +# These are slower than using native cross compiler setups but can +# work around issues with poorly working multi-arch systems and broken +# packages. + +# Jessie is the last supported release for powerpc, but multi-arch is +# broken so we need a qemu-linux-user for this target +docker-binfmt-image-debian-powerpc-user: DEB_ARCH = powerpc +docker-binfmt-image-debian-powerpc-user: DEB_TYPE = jessie +docker-binfmt-image-debian-powerpc-user: QEMU_TARGET = ppc-linux-user +docker-binfmt-image-debian-powerpc-user: EXECUTABLE = ${BUILD_DIR}/ppc-linux-user/qemu-ppc +docker-image-debian-powerpc-user-cross: docker-binfmt-image-debian-powerpc-user +DOCKER_USER_IMAGES += debian-powerpc-user + # Expand all the pre-requistes for each docker image and test combination $(foreach i,$(filter-out $(DOCKER_INTERMEDIATE_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES)), \ $(foreach t,$(DOCKER_TESTS) $(DOCKER_TOOLS), \ @@ -146,6 +165,11 @@ docker: @echo @echo 'Available container images:' @echo ' $(DOCKER_IMAGES)' +ifneq ($(DOCKER_USER_IMAGES),) + @echo + @echo 'Available linux-user images (docker-binfmt-image-debian-%):' + @echo ' $(DOCKER_USER_IMAGES)' +endif @echo @echo 'Available tests:' @echo ' $(DOCKER_TESTS)' diff --git a/tests/docker/dockerfiles/debian-powerpc-user-cross.docker b/tests/docker/dockerfiles/debian-powerpc-user-cross.docker new file mode 100644 index 0000000000..6938a845ee --- /dev/null +++ b/tests/docker/dockerfiles/debian-powerpc-user-cross.docker @@ -0,0 +1,15 @@ +# +# Docker powerpc cross-compiler target for QEMU +# +# We can't use current Debian stable cross-compilers to build powerpc +# as it has been dropped as a release architecture. Using Debian Sid +# is just far too sketchy a build environment. This leaves us the +# final option of using linux-user. This image is based of the +# debootstrapped qemu:debian-powerpc-user but doesn't need any extra +# magic once it is setup. +# +FROM qemu:debian-powerpc-user + +RUN echo man-db man-db/auto-update boolean false | debconf-set-selections +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get build-dep -yy qemu From 2308092b2b78e6e083092bd3599cec6a0769319e Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 26 Apr 2018 11:15:24 -0700 Subject: [PATCH 2096/2380] hw/riscv/sifive_u: Create a SiFive U SoC object Create a SiFive Unleashed U54 SoC and use that in the sifive_u machine. We leave the SoC, RAM, device tree and reset/fdt loading as part of the machine. All the other device creation has been moved to the SoC. Signed-off-by: Alistair Francis Reviewed-by: Michael Clark --- hw/riscv/sifive_u.c | 87 +++++++++++++++++++++++++++---------- include/hw/riscv/sifive_u.h | 16 ++++++- 2 files changed, 79 insertions(+), 24 deletions(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index c05dcbba95..e2b4f97b10 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -116,10 +116,10 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); - for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) { + for (cpu = s->soc.cpus.num_harts - 1; cpu >= 0; cpu--) { nodename = g_strdup_printf("/cpus/cpu@%d", cpu); char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); - char *isa = riscv_isa_string(&s->soc.harts[cpu]); + char *isa = riscv_isa_string(&s->soc.cpus.harts[cpu]); qemu_fdt_add_subnode(fdt, nodename); qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", SIFIVE_U_CLOCK_FREQ); @@ -140,8 +140,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(nodename); } - cells = g_new0(uint32_t, s->soc.num_harts * 4); - for (cpu = 0; cpu < s->soc.num_harts; cpu++) { + cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4); + for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) { nodename = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); @@ -159,12 +159,12 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, 0x0, memmap[SIFIVE_U_CLINT].base, 0x0, memmap[SIFIVE_U_CLINT].size); qemu_fdt_setprop(fdt, nodename, "interrupts-extended", - cells, s->soc.num_harts * sizeof(uint32_t) * 4); + cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4); g_free(cells); g_free(nodename); - cells = g_new0(uint32_t, s->soc.num_harts * 4); - for (cpu = 0; cpu < s->soc.num_harts; cpu++) { + cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4); + for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) { nodename = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); @@ -181,7 +181,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0"); qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); qemu_fdt_setprop(fdt, nodename, "interrupts-extended", - cells, s->soc.num_harts * sizeof(uint32_t) * 4); + cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4); qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0, memmap[SIFIVE_U_PLIC].base, 0x0, memmap[SIFIVE_U_PLIC].size); @@ -217,17 +217,12 @@ static void riscv_sifive_u_init(MachineState *machine) SiFiveUState *s = g_new0(SiFiveUState, 1); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *mask_rom = g_new(MemoryRegion, 1); int i; - /* Initialize SOC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + /* Initialize SoC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_U_SOC); object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), &error_abort); - object_property_set_str(OBJECT(&s->soc), SIFIVE_U_CPU, "cpu-type", - &error_abort); - object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", - &error_abort); object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort); @@ -235,17 +230,11 @@ static void riscv_sifive_u_init(MachineState *machine) memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram", machine->ram_size, &error_fatal); memory_region_add_subregion(system_memory, memmap[SIFIVE_U_DRAM].base, - main_mem); + main_mem); /* create device tree */ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); - /* boot rom */ - memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom", - memmap[SIFIVE_U_MROM].size, &error_fatal); - memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base, - mask_rom); - if (machine->kernel_filename) { load_kernel(machine->kernel_filename); } @@ -284,6 +273,36 @@ static void riscv_sifive_u_init(MachineState *machine) rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt), memmap[SIFIVE_U_MROM].base + sizeof(reset_vec), &address_space_memory); +} + +static void riscv_sifive_u_soc_init(Object *obj) +{ + SiFiveUSoCState *s = RISCV_U_SOC(obj); + + object_initialize(&s->cpus, sizeof(s->cpus), TYPE_RISCV_HART_ARRAY); + object_property_add_child(obj, "cpus", OBJECT(&s->cpus), + &error_abort); + object_property_set_str(OBJECT(&s->cpus), SIFIVE_U_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", + &error_abort); +} + +static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) +{ + SiFiveUSoCState *s = RISCV_U_SOC(dev); + const struct MemmapEntry *memmap = sifive_u_memmap; + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + + object_property_set_bool(OBJECT(&s->cpus), true, "realized", + &error_abort); + + /* boot rom */ + memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom", + memmap[SIFIVE_U_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base, + mask_rom); /* MMIO */ s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base, @@ -314,3 +333,27 @@ static void riscv_sifive_u_machine_init(MachineClass *mc) } DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init) + +static void riscv_sifive_u_soc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = riscv_sifive_u_soc_realize; + /* Reason: Uses serial_hds in realize function, thus can't be used twice */ + dc->user_creatable = false; +} + +static const TypeInfo riscv_sifive_u_soc_type_info = { + .name = TYPE_RISCV_U_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SiFiveUSoCState), + .instance_init = riscv_sifive_u_soc_init, + .class_init = riscv_sifive_u_soc_class_init, +}; + +static void riscv_sifive_u_soc_register_types(void) +{ + type_register_static(&riscv_sifive_u_soc_type_info); +} + +type_init(riscv_sifive_u_soc_register_types) diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index 94a390566e..49f1946539 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -19,13 +19,25 @@ #ifndef HW_SIFIVE_U_H #define HW_SIFIVE_U_H +#define TYPE_RISCV_U_SOC "riscv.sifive.u.soc" +#define RISCV_U_SOC(obj) \ + OBJECT_CHECK(SiFiveUSoCState, (obj), TYPE_RISCV_U_SOC) + +typedef struct SiFiveUSoCState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + RISCVHartArrayState cpus; + DeviceState *plic; +} SiFiveUSoCState; + typedef struct SiFiveUState { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ - RISCVHartArrayState soc; - DeviceState *plic; + SiFiveUSoCState soc; void *fdt; int fdt_size; } SiFiveUState; From 651cd8b7e18eda46a36cf073428452d04bb354f2 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 3 May 2018 16:54:02 -0700 Subject: [PATCH 2097/2380] hw/riscv/sifive_e: Create a SiFive E SoC object Signed-off-by: Alistair Francis Reviewed-by: Michael Clark --- hw/riscv/sifive_e.c | 94 +++++++++++++++++++++++++++---------- include/hw/riscv/sifive_e.h | 16 ++++++- 2 files changed, 83 insertions(+), 27 deletions(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index e4ecb7aa4b..cb1b6948bf 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -102,18 +102,12 @@ static void riscv_sifive_e_init(MachineState *machine) SiFiveEState *s = g_new0(SiFiveEState, 1); MemoryRegion *sys_mem = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *mask_rom = g_new(MemoryRegion, 1); - MemoryRegion *xip_mem = g_new(MemoryRegion, 1); int i; - /* Initialize SOC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + /* Initialize SoC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_E_SOC); object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), &error_abort); - object_property_set_str(OBJECT(&s->soc), SIFIVE_E_CPU, "cpu-type", - &error_abort); - object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", - &error_abort); object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort); @@ -123,6 +117,49 @@ static void riscv_sifive_e_init(MachineState *machine) memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_DTIM].base, main_mem); + /* Mask ROM reset vector */ + uint32_t reset_vec[2] = { + 0x204002b7, /* 0x1000: lui t0,0x20400 */ + 0x00028067, /* 0x1004: jr t0 */ + }; + + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[SIFIVE_E_MROM].base, &address_space_memory); + + if (machine->kernel_filename) { + load_kernel(machine->kernel_filename); + } +} + +static void riscv_sifive_e_soc_init(Object *obj) +{ + SiFiveESoCState *s = RISCV_E_SOC(obj); + + object_initialize(&s->cpus, sizeof(s->cpus), TYPE_RISCV_HART_ARRAY); + object_property_add_child(obj, "cpus", OBJECT(&s->cpus), + &error_abort); + object_property_set_str(OBJECT(&s->cpus), SIFIVE_E_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", + &error_abort); +} + +static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) +{ + const struct MemmapEntry *memmap = sifive_e_memmap; + + SiFiveESoCState *s = RISCV_E_SOC(dev); + MemoryRegion *sys_mem = get_system_memory(); + MemoryRegion *xip_mem = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + + object_property_set_bool(OBJECT(&s->cpus), true, "realized", + &error_abort); + /* Mask ROM */ memory_region_init_rom(mask_rom, NULL, "riscv.sifive.e.mrom", memmap[SIFIVE_E_MROM].size, &error_fatal); @@ -171,23 +208,6 @@ static void riscv_sifive_e_init(MachineState *machine) memmap[SIFIVE_E_XIP].size, &error_fatal); memory_region_set_readonly(xip_mem, true); memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, xip_mem); - - /* Mask ROM reset vector */ - uint32_t reset_vec[2] = { - 0x204002b7, /* 0x1000: lui t0,0x20400 */ - 0x00028067, /* 0x1004: jr t0 */ - }; - - /* copy in the reset vector in little_endian byte order */ - for (i = 0; i < sizeof(reset_vec) >> 2; i++) { - reset_vec[i] = cpu_to_le32(reset_vec[i]); - } - rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), - memmap[SIFIVE_E_MROM].base, &address_space_memory); - - if (machine->kernel_filename) { - load_kernel(machine->kernel_filename); - } } static void riscv_sifive_e_machine_init(MachineClass *mc) @@ -198,3 +218,27 @@ static void riscv_sifive_e_machine_init(MachineClass *mc) } DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init) + +static void riscv_sifive_e_soc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = riscv_sifive_e_soc_realize; + /* Reason: Uses serial_hds in realize function, thus can't be used twice */ + dc->user_creatable = false; +} + +static const TypeInfo riscv_sifive_e_soc_type_info = { + .name = TYPE_RISCV_E_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SiFiveESoCState), + .instance_init = riscv_sifive_e_soc_init, + .class_init = riscv_sifive_e_soc_class_init, +}; + +static void riscv_sifive_e_soc_register_types(void) +{ + type_register_static(&riscv_sifive_e_soc_type_info); +} + +type_init(riscv_sifive_e_soc_register_types) diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index 12ad6d2ebb..7b6d8aed96 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -19,13 +19,25 @@ #ifndef HW_SIFIVE_E_H #define HW_SIFIVE_E_H +#define TYPE_RISCV_E_SOC "riscv.sifive.e.soc" +#define RISCV_E_SOC(obj) \ + OBJECT_CHECK(SiFiveESoCState, (obj), TYPE_RISCV_E_SOC) + +typedef struct SiFiveESoCState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + RISCVHartArrayState cpus; + DeviceState *plic; +} SiFiveESoCState; + typedef struct SiFiveEState { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ - RISCVHartArrayState soc; - DeviceState *plic; + SiFiveESoCState soc; } SiFiveEState; enum { From 647a70a10f257bdeba33ff5f1bcb2b26518a9f4c Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 26 Apr 2018 13:54:12 -0700 Subject: [PATCH 2098/2380] hw/riscv/sifive_plic: Use gpios instead of irqs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of creating the interrupt in lines with qemu_allocate_irq() use qdev_init_gpio_in() as this gives us the ability to use the qdev*gpio*() helpers later on. Signed-off-by: Alistair Francis Suggested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Clark --- hw/riscv/sifive_e.c | 5 +++-- hw/riscv/sifive_plic.c | 6 +----- hw/riscv/sifive_u.c | 5 +++-- hw/riscv/virt.c | 4 ++-- include/hw/riscv/sifive_plic.h | 1 - 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index cb1b6948bf..8a8dbe1c00 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -187,13 +187,14 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) sifive_mmio_emulate(sys_mem, "riscv.sifive.e.gpio0", memmap[SIFIVE_E_GPIO0].base, memmap[SIFIVE_E_GPIO0].size); sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART0].base, - serial_hd(0), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART0_IRQ]); + serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART0_IRQ)); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi0", memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0", memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size); /* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base, - serial_hd(1), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART1_IRQ]); */ + serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), + SIFIVE_E_UART1_IRQ)); */ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1", memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1", diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index 874de2ebaf..a91aeb97ab 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -435,7 +435,6 @@ static void sifive_plic_irq_request(void *opaque, int irq, int level) static void sifive_plic_realize(DeviceState *dev, Error **errp) { SiFivePLICState *plic = SIFIVE_PLIC(dev); - int i; memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, TYPE_SIFIVE_PLIC, plic->aperture_size); @@ -448,10 +447,7 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) plic->claimed = g_new0(uint32_t, plic->bitfield_words); plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); - plic->irqs = g_new0(qemu_irq, plic->num_sources + 1); - for (i = 0; i <= plic->num_sources; i++) { - plic->irqs[i] = qemu_allocate_irq(sifive_plic_irq_request, plic, i); - } + qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources); } static void sifive_plic_class_init(ObjectClass *klass, void *data) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index e2b4f97b10..1a548b71e0 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -317,9 +317,10 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) SIFIVE_U_PLIC_CONTEXT_STRIDE, memmap[SIFIVE_U_PLIC].size); sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base, - serial_hd(0), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]); + serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ)); /* sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, - serial_hd(1), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */ + serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), + SIFIVE_U_UART1_IRQ)); */ sifive_clint_create(memmap[SIFIVE_U_CLINT].base, memmap[SIFIVE_U_CLINT].size, smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 34d48993a2..aeada2498d 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -380,11 +380,11 @@ static void riscv_virt_board_init(MachineState *machine) for (i = 0; i < VIRTIO_COUNT; i++) { sysbus_create_simple("virtio-mmio", memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, - SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]); + qdev_get_gpio_in(DEVICE(s->plic), VIRTIO_IRQ + i)); } serial_mm_init(system_memory, memmap[VIRT_UART0].base, - 0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193, + 0, qdev_get_gpio_in(DEVICE(s->plic), UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); } diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h index 11a5a98df1..2f2af7e686 100644 --- a/include/hw/riscv/sifive_plic.h +++ b/include/hw/riscv/sifive_plic.h @@ -56,7 +56,6 @@ typedef struct SiFivePLICState { uint32_t *claimed; uint32_t *enable; QemuMutex lock; - qemu_irq *irqs; /* config */ char *hart_config; From 2a1a6f6d47f192a12a3765a3558a11d619d25237 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 11 May 2018 10:22:48 -0700 Subject: [PATCH 2099/2380] hw/riscv/sifive_u: Set the soc device tree node as a simple-bus To allow Linux to ennumerate devices on the /soc/ node set it as a "simple-bus". Signed-off-by: Alistair Francis Reviewed-by: Michael Clark --- hw/riscv/sifive_u.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 1a548b71e0..d3db8ab9f5 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -97,7 +97,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_add_subnode(fdt, "/soc"); qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); - qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc"); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); From 98ceee7fdc04526b7c264823a14ef5977b90040a Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 11 May 2018 10:24:00 -0700 Subject: [PATCH 2100/2380] hw/riscv/sifive_u: Set the interrupt controller number of interrupts Set the interrupt-controller ndev to the correct number taken from the HiFive Unleashed board. Signed-off-by: Alistair Francis Reviewed-by: Michael Clark --- hw/riscv/sifive_u.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index d3db8ab9f5..4d3ba4e624 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -187,7 +187,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, 0x0, memmap[SIFIVE_U_PLIC].size); qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control"); qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7); - qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 4); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 0x35); qemu_fdt_setprop_cells(fdt, nodename, "phandle", 2); qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", 2); plic_phandle = qemu_fdt_get_phandle(fdt, nodename); From bde3ab9a9f2c2d08c7dabe77f19528b3f094b620 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 11 May 2018 10:26:23 -0700 Subject: [PATCH 2101/2380] hw/riscv/sifive_u: Move the uart device tree node under /soc/ Signed-off-by: Alistair Francis Reviewed-by: Michael Clark --- hw/riscv/sifive_u.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 4d3ba4e624..445fe4f7fb 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -194,7 +194,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(cells); g_free(nodename); - nodename = g_strdup_printf("/uart@%lx", + nodename = g_strdup_printf("/soc/uart@%lx", (long)memmap[SIFIVE_U_UART0].base); qemu_fdt_add_subnode(fdt, nodename); qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0"); From 5a7f76a3d47a75290868968682c0585d380764a4 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 26 Apr 2018 13:59:08 -0700 Subject: [PATCH 2102/2380] hw/riscv/sifive_u: Connect the Cadence GEM Ethernet device Connect the Cadence GEM ethernet device. This also requires us to expose the plic interrupt lines. Signed-off-by: Alistair Francis Reviewed-by: Michael Clark --- default-configs/riscv32-softmmu.mak | 2 ++ default-configs/riscv64-softmmu.mak | 2 ++ hw/riscv/sifive_u.c | 50 +++++++++++++++++++++++++++++ include/hw/riscv/sifive_u.h | 9 ++++-- 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak index 20e670d99c..7937c69e22 100644 --- a/default-configs/riscv32-softmmu.mak +++ b/default-configs/riscv32-softmmu.mak @@ -3,3 +3,5 @@ CONFIG_SERIAL=y CONFIG_VIRTIO_MMIO=y include virtio.mak + +CONFIG_CADENCE=y diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak index 20e670d99c..7937c69e22 100644 --- a/default-configs/riscv64-softmmu.mak +++ b/default-configs/riscv64-softmmu.mak @@ -3,3 +3,5 @@ CONFIG_SERIAL=y CONFIG_VIRTIO_MMIO=y include virtio.mak + +CONFIG_CADENCE=y diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 445fe4f7fb..3a6ffeb437 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -60,8 +60,11 @@ static const struct MemmapEntry { [SIFIVE_U_UART0] = { 0x10013000, 0x1000 }, [SIFIVE_U_UART1] = { 0x10023000, 0x1000 }, [SIFIVE_U_DRAM] = { 0x80000000, 0x0 }, + [SIFIVE_U_GEM] = { 0x100900FC, 0x2000 }, }; +#define GEM_REVISION 0x10070109 + static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; @@ -194,6 +197,27 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(cells); g_free(nodename); + nodename = g_strdup_printf("/soc/ethernet@%lx", + (long)memmap[SIFIVE_U_GEM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "cdns,macb"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_GEM].base, + 0x0, memmap[SIFIVE_U_GEM].size); + qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control"); + qemu_fdt_setprop_string(fdt, nodename, "phy-mode", "gmii"); + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", SIFIVE_U_GEM_IRQ); + qemu_fdt_setprop_cells(fdt, nodename, "#address-cells", 1); + qemu_fdt_setprop_cells(fdt, nodename, "#size-cells", 0); + g_free(nodename); + + nodename = g_strdup_printf("/soc/ethernet@%lx/ethernet-phy@0", + (long)memmap[SIFIVE_U_GEM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0); + g_free(nodename); + nodename = g_strdup_printf("/soc/uart@%lx", (long)memmap[SIFIVE_U_UART0].base); qemu_fdt_add_subnode(fdt, nodename); @@ -286,6 +310,9 @@ static void riscv_sifive_u_soc_init(Object *obj) &error_abort); object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", &error_abort); + + object_initialize(&s->gem, sizeof(s->gem), TYPE_CADENCE_GEM); + qdev_set_parent_bus(DEVICE(&s->gem), sysbus_get_default()); } static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) @@ -294,6 +321,10 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) const struct MemmapEntry *memmap = sifive_u_memmap; MemoryRegion *system_memory = get_system_memory(); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + qemu_irq plic_gpios[SIFIVE_U_PLIC_NUM_SOURCES]; + int i; + Error *err = NULL; + NICInfo *nd = &nd_table[0]; object_property_set_bool(OBJECT(&s->cpus), true, "realized", &error_abort); @@ -324,6 +355,25 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) sifive_clint_create(memmap[SIFIVE_U_CLINT].base, memmap[SIFIVE_U_CLINT].size, smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + + for (i = 0; i < SIFIVE_U_PLIC_NUM_SOURCES; i++) { + plic_gpios[i] = qdev_get_gpio_in(DEVICE(s->plic), i); + } + + if (nd->used) { + qemu_check_nic_model(nd, TYPE_CADENCE_GEM); + qdev_set_nic_properties(DEVICE(&s->gem), nd); + } + object_property_set_int(OBJECT(&s->gem), GEM_REVISION, "revision", + &error_abort); + object_property_set_bool(OBJECT(&s->gem), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[SIFIVE_U_GEM].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0, + plic_gpios[SIFIVE_U_GEM_IRQ]); } static void riscv_sifive_u_machine_init(MachineClass *mc) diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index 49f1946539..e8b4d9ffa3 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -19,6 +19,8 @@ #ifndef HW_SIFIVE_U_H #define HW_SIFIVE_U_H +#include "hw/net/cadence_gem.h" + #define TYPE_RISCV_U_SOC "riscv.sifive.u.soc" #define RISCV_U_SOC(obj) \ OBJECT_CHECK(SiFiveUSoCState, (obj), TYPE_RISCV_U_SOC) @@ -30,6 +32,7 @@ typedef struct SiFiveUSoCState { /*< public >*/ RISCVHartArrayState cpus; DeviceState *plic; + CadenceGEMState gem; } SiFiveUSoCState; typedef struct SiFiveUState { @@ -49,12 +52,14 @@ enum { SIFIVE_U_PLIC, SIFIVE_U_UART0, SIFIVE_U_UART1, - SIFIVE_U_DRAM + SIFIVE_U_DRAM, + SIFIVE_U_GEM }; enum { SIFIVE_U_UART0_IRQ = 3, - SIFIVE_U_UART1_IRQ = 4 + SIFIVE_U_UART1_IRQ = 4, + SIFIVE_U_GEM_IRQ = 0x35 }; enum { From 5fd4a9c97397bc0819a919de7a62ec972ec85260 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 6 Jul 2018 15:31:05 +0100 Subject: [PATCH 2103/2380] tests/migration: Skip tests for ppc tcg PPC tcg seems to be failing migration tests quite regularly; we believe this is TCG bugs in dirty bit updating; it's not clear why PPC fails more but lets skip for the moment. $ ./tests/migration-test /ppc64/migration/deprecated: OK /ppc64/migration/bad_dest: Skipping test: kvm_hv not available OK /ppc64/migration/postcopy/unix: Skipping test: kvm_hv not available OK /ppc64/migration/precopy/unix: Skipping test: kvm_hv not available OK Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Laurent Vivier Message-id: 20180706143105.93472-1-dgilbert@redhat.com Signed-off-by: Peter Maydell --- tests/migration-test.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/migration-test.c b/tests/migration-test.c index 3a85446f95..331efb0fe5 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -360,7 +360,7 @@ static void migrate_start_postcopy(QTestState *who) qobject_unref(rsp); } -static void test_migrate_start(QTestState **from, QTestState **to, +static int test_migrate_start(QTestState **from, QTestState **to, const char *uri, bool hide_stderr) { gchar *cmd_src, *cmd_dst; @@ -385,9 +385,13 @@ static void test_migrate_start(QTestState **from, QTestState **to, accel, tmpfs, bootpath, uri); } else if (strcmp(arch, "ppc64") == 0) { - /* On ppc64, the test only works with kvm-hv, but not with kvm-pr */ + /* On ppc64, the test only works with kvm-hv, but not with kvm-pr + * and TCG is touchy due to race conditions on dirty bits + * (especially on PPC for some reason) + */ if (access("/sys/module/kvm_hv", F_OK)) { - accel = "tcg"; + g_print("Skipping test: kvm_hv not available "); + return -1; } cmd_src = g_strdup_printf("-machine accel=%s -m 256M" " -name source,debug-threads=on" @@ -424,6 +428,7 @@ static void test_migrate_start(QTestState **from, QTestState **to, *to = qtest_init(cmd_dst); g_free(cmd_dst); + return 0; } static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) @@ -510,7 +515,9 @@ static void test_postcopy(void) char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - test_migrate_start(&from, &to, uri, false); + if (test_migrate_start(&from, &to, uri, false)) { + return; + } migrate_set_capability(from, "postcopy-ram", "true"); migrate_set_capability(to, "postcopy-ram", "true"); @@ -556,7 +563,9 @@ static void test_baddest(void) const char *status; bool failed; - test_migrate_start(&from, &to, "tcp:0:0", true); + if (test_migrate_start(&from, &to, "tcp:0:0", true)) { + return; + } migrate(from, "tcp:0:0"); do { rsp = wait_command(from, "{ 'execute': 'query-migrate' }"); @@ -585,7 +594,9 @@ static void test_precopy_unix(void) char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - test_migrate_start(&from, &to, uri, false); + if (test_migrate_start(&from, &to, uri, false)) { + return; + } /* We want to pick a speed slow enough that the test completes * quickly, but that it doesn't complete precopy even on a slow From ee8c13b81474e002db083e9692b11c0e106a9c7f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 2 Jul 2018 14:21:58 +0200 Subject: [PATCH 2104/2380] pr-helper: avoid error on PR IN command with zero request size After reading a PR IN command with zero request size in prh_read_request, the resp->result field will be uninitialized and the resp.sz field will be also uninitialized when returning to prh_co_entry. If resp->result == GOOD (from a previous successful reply or just luck), then the assert in prh_write_response might not be triggered and uninitialized response will be sent. The fix is to remove the whole handling of sz == 0 in prh_co_entry. Those errors apply only to PR OUT commands and it's perfectly okay to catch them later in do_pr_out and multipath_pr_out; the check for too-short parameters in fact doesn't apply in the easy SG_IO case, as it can be left to the target firmware even. The result is that prh_read_request does not fail requests anymore and prh_co_entry becomes simpler. Reported-by: Dima Stepanov Signed-off-by: Paolo Bonzini --- scsi/qemu-pr-helper.c | 63 +++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index 0218d65bbf..c89a446a45 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -455,6 +455,14 @@ static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, char transportids[PR_HELPER_DATA_SIZE]; int r; + if (sz < PR_OUT_FIXED_PARAM_SIZE) { + /* Illegal request, Parameter list length error. This isn't fatal; + * we have read the data, send an error without closing the socket. + */ + scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM_LEN)); + return CHECK_CONDITION; + } + switch (rq_servact) { case MPATH_PROUT_REG_SA: case MPATH_PROUT_RES_SA: @@ -574,6 +582,12 @@ static int do_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, const uint8_t *param, int sz) { int resp_sz; + + if ((fcntl(fd, F_GETFL) & O_ACCMODE) == O_RDONLY) { + scsi_build_sense(sense, SENSE_CODE(INVALID_OPCODE)); + return CHECK_CONDITION; + } + #ifdef CONFIG_MPATH if (is_mpath(fd)) { return multipath_pr_out(fd, cdb, sense, param, sz); @@ -690,21 +704,6 @@ static int coroutine_fn prh_read_request(PRHelperClient *client, errp) < 0) { goto out_close; } - if ((fcntl(client->fd, F_GETFL) & O_ACCMODE) == O_RDONLY) { - scsi_build_sense(resp->sense, SENSE_CODE(INVALID_OPCODE)); - sz = 0; - } else if (sz < PR_OUT_FIXED_PARAM_SIZE) { - /* Illegal request, Parameter list length error. This isn't fatal; - * we have read the data, send an error without closing the socket. - */ - scsi_build_sense(resp->sense, SENSE_CODE(INVALID_PARAM_LEN)); - sz = 0; - } - if (sz == 0) { - resp->result = CHECK_CONDITION; - close(client->fd); - client->fd = -1; - } } req->fd = client->fd; @@ -785,25 +784,23 @@ static void coroutine_fn prh_co_entry(void *opaque) break; } - if (sz > 0) { - num_active_sockets++; - if (req.cdb[0] == PERSISTENT_RESERVE_OUT) { - r = do_pr_out(req.fd, req.cdb, resp.sense, - client->data, sz); - resp.sz = 0; - } else { - resp.sz = sizeof(client->data); - r = do_pr_in(req.fd, req.cdb, resp.sense, - client->data, &resp.sz); - resp.sz = MIN(resp.sz, sz); - } - num_active_sockets--; - close(req.fd); - if (r == -1) { - break; - } - resp.result = r; + num_active_sockets++; + if (req.cdb[0] == PERSISTENT_RESERVE_OUT) { + r = do_pr_out(req.fd, req.cdb, resp.sense, + client->data, sz); + resp.sz = 0; + } else { + resp.sz = sizeof(client->data); + r = do_pr_in(req.fd, req.cdb, resp.sense, + client->data, &resp.sz); + resp.sz = MIN(resp.sz, sz); } + num_active_sockets--; + close(req.fd); + if (r == -1) { + break; + } + resp.result = r; if (prh_write_response(client, &req, &resp, &local_err) < 0) { break; From 2729d79d4993099782002c9a218de1fc12c32c69 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Tue, 3 Jul 2018 11:51:14 +0200 Subject: [PATCH 2105/2380] pr-helper: Rework socket path handling When reviewing Paolo's pr-helper patches I've noticed couple of problems: 1) socket_path needs to be calculated at two different places (one for printing out help, the other if socket activation is NOT used), 2) even though the default socket_path is allocated in compute_default_paths() it is the only default path the function handles. For instance, pidfile is allocated outside of this function. And yet again, at different places than 1) Signed-off-by: Michal Privoznik Message-Id: Signed-off-by: Paolo Bonzini --- scsi/qemu-pr-helper.c | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index c89a446a45..1528a712a0 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -76,14 +76,12 @@ static int gid = -1; static void compute_default_paths(void) { - if (!socket_path) { - socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); - } + socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); + pidfile = qemu_get_local_state_pathname("run/qemu-pr-helper.pid"); } static void usage(const char *name) { - compute_default_paths(); (printf) ( "Usage: %s [OPTIONS] FILE\n" "Persistent Reservation helper program for QEMU\n" @@ -841,19 +839,6 @@ static gboolean accept_client(QIOChannel *ioc, GIOCondition cond, gpointer opaqu return TRUE; } - -/* - * Check socket parameters compatibility when socket activation is used. - */ -static const char *socket_activation_validate_opts(void) -{ - if (socket_path != NULL) { - return "Unix socket can't be set when using socket activation"; - } - - return NULL; -} - static void termsig_handler(int signum) { atomic_cmpxchg(&state, RUNNING, TERMINATE); @@ -927,6 +912,7 @@ int main(int argc, char **argv) char *trace_file = NULL; bool daemonize = false; bool pidfile_specified = false; + bool socket_path_specified = false; unsigned socket_activation; struct sigaction sa_sigterm; @@ -943,12 +929,14 @@ int main(int argc, char **argv) qemu_add_opts(&qemu_trace_opts); qemu_init_exec_dir(argv[0]); - pidfile = qemu_get_local_state_pathname("run/qemu-pr-helper.pid"); + compute_default_paths(); while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { case 'k': - socket_path = optarg; + g_free(socket_path); + socket_path = g_strdup(optarg); + socket_path_specified = true; if (socket_path[0] != '/') { error_report("socket path must be absolute"); exit(EXIT_FAILURE); @@ -1039,10 +1027,9 @@ int main(int argc, char **argv) socket_activation = check_socket_activation(); if (socket_activation == 0) { SocketAddress saddr; - compute_default_paths(); saddr = (SocketAddress){ .type = SOCKET_ADDRESS_TYPE_UNIX, - .u.q_unix.path = g_strdup(socket_path) + .u.q_unix.path = socket_path, }; server_ioc = qio_channel_socket_new(); if (qio_channel_socket_listen_sync(server_ioc, &saddr, &local_err) < 0) { @@ -1050,12 +1037,10 @@ int main(int argc, char **argv) error_report_err(local_err); return 1; } - g_free(saddr.u.q_unix.path); } else { /* Using socket activation - check user didn't use -p etc. */ - const char *err_msg = socket_activation_validate_opts(); - if (err_msg != NULL) { - error_report("%s", err_msg); + if (socket_path_specified) { + error_report("Unix socket can't be set when using socket activation"); exit(EXIT_FAILURE); } @@ -1072,7 +1057,6 @@ int main(int argc, char **argv) error_get_pretty(local_err)); exit(EXIT_FAILURE); } - socket_path = NULL; } if (qemu_init_main_loop(&local_err)) { From 70da30483e78b501ff4b3a090f26c08abd0f7a7f Mon Sep 17 00:00:00 2001 From: Julia Suvorova Date: Mon, 2 Jul 2018 09:52:37 +0300 Subject: [PATCH 2106/2380] qtest: Use cpu address space instead of system memory Some devices (like nvic in armv7m) are not accessable through address_space_memory, therefore can not be tested with qtest. Signed-off-by: Julia Suvorova Message-Id: <20180702065237.27899-1-jusual@mail.ru> Signed-off-by: Paolo Bonzini --- qtest.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/qtest.c b/qtest.c index cbbfb71114..69b9e9962b 100644 --- a/qtest.c +++ b/qtest.c @@ -387,19 +387,23 @@ static void qtest_process_command(CharBackend *chr, gchar **words) if (words[0][5] == 'b') { uint8_t data = value; - cpu_physical_memory_write(addr, &data, 1); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 1, true); } else if (words[0][5] == 'w') { uint16_t data = value; tswap16s(&data); - cpu_physical_memory_write(addr, &data, 2); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &data, 2, true); } else if (words[0][5] == 'l') { uint32_t data = value; tswap32s(&data); - cpu_physical_memory_write(addr, &data, 4); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &data, 4, true); } else if (words[0][5] == 'q') { uint64_t data = value; tswap64s(&data); - cpu_physical_memory_write(addr, &data, 8); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &data, 8, true); } qtest_send_prefix(chr); qtest_send(chr, "OK\n"); @@ -417,18 +421,22 @@ static void qtest_process_command(CharBackend *chr, gchar **words) if (words[0][4] == 'b') { uint8_t data; - cpu_physical_memory_read(addr, &data, 1); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 1, false); value = data; } else if (words[0][4] == 'w') { uint16_t data; - cpu_physical_memory_read(addr, &data, 2); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &data, 2, false); value = tswap16(data); } else if (words[0][4] == 'l') { uint32_t data; - cpu_physical_memory_read(addr, &data, 4); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &data, 4, false); value = tswap32(data); } else if (words[0][4] == 'q') { - cpu_physical_memory_read(addr, &value, 8); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &value, 8, false); tswap64s(&value); } qtest_send_prefix(chr); @@ -448,7 +456,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) g_assert(len); data = g_malloc(len); - cpu_physical_memory_read(addr, data, len); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len, false); enc = g_malloc(2 * len + 1); for (i = 0; i < len; i++) { @@ -473,7 +482,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) g_assert(ret == 0); data = g_malloc(len); - cpu_physical_memory_read(addr, data, len); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len, false); b64_data = g_base64_encode(data, len); qtest_send_prefix(chr); qtest_sendf(chr, "OK %s\n", b64_data); @@ -507,7 +517,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) data[i] = 0; } } - cpu_physical_memory_write(addr, data, len); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len, true); g_free(data); qtest_send_prefix(chr); @@ -529,7 +540,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) if (len) { data = g_malloc(len); memset(data, pattern, len); - cpu_physical_memory_write(addr, data, len); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len, true); g_free(data); } @@ -562,7 +574,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) out_len = MIN(out_len, len); } - cpu_physical_memory_write(addr, data, out_len); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len, true); qtest_send_prefix(chr); qtest_send(chr, "OK\n"); From 02693cc4f4aacf82cb73559dfa9af76ce4b13d11 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Mon, 2 Jul 2018 18:56:06 +0200 Subject: [PATCH 2107/2380] i386: fix '-cpu ?' output for host cpu type Since commit d6dcc5583e7, '-cpu ?' shows the description of the X86_CPU_TYPE_NAME("max") for the host CPU model: Enables all features supported by the accelerator in the current host instead of the expected: KVM processor with all supported host features or HVF processor with all supported host features This is caused by the early use of kvm_enabled() and hvf_enabled() in a class_init function. Since the accelerator isn't configured yet, both helpers return false unconditionally. A QEMU binary will only be compiled with one of these accelerators, not both. The appropriate description can thus be decided at build time. Signed-off-by: Greg Kurz Message-Id: <153055056654.212317.4697363278304826913.stgit@bahia.lan> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b0b87c3d81..e0e2f2eea1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2836,13 +2836,13 @@ static void host_x86_cpu_class_init(ObjectClass *oc, void *data) xcc->host_cpuid_required = true; xcc->ordering = 8; - if (kvm_enabled()) { - xcc->model_description = - "KVM processor with all supported host features "; - } else if (hvf_enabled()) { - xcc->model_description = - "HVF processor with all supported host features "; - } +#if defined(CONFIG_KVM) + xcc->model_description = + "KVM processor with all supported host features "; +#elif defined(CONFIG_HVF) + xcc->model_description = + "HVF processor with all supported host features "; +#endif } static const TypeInfo host_x86_cpu_type_info = { From 81e34930ce2b605ce9dbb54a7b846379cbb1b811 Mon Sep 17 00:00:00 2001 From: "xinhua.Cao" Date: Wed, 4 Jul 2018 11:36:42 +0800 Subject: [PATCH 2108/2380] qemu-char: check errno together with ret < 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the tcp_chr_write function, we checked errno, but errno was not reset before a read or write operation. Therefore, this check of errno's actions is often incorrect after EAGAIN has occurred. we need check errno together with ret < 0. Signed-off-by: xinhua.Cao Message-Id: <20180704033642.15996-1-caoxinhua@huawei.com> Reviewed-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Fixes: 9fc53a10f81d3a9027b23fa810147d21be29e614 Signed-off-by: Paolo Bonzini --- chardev/char-socket.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 17519ec589..efbad6ee7c 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -134,8 +134,11 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len) s->write_msgfds, s->write_msgfds_num); - /* free the written msgfds in any cases other than errno==EAGAIN */ - if (EAGAIN != errno && s->write_msgfds_num) { + /* free the written msgfds in any cases + * other than ret < 0 && errno == EAGAIN + */ + if (!(ret < 0 && EAGAIN == errno) + && s->write_msgfds_num) { g_free(s->write_msgfds); s->write_msgfds = 0; s->write_msgfds_num = 0; From ea3d77c889cfa8c450da8a716c2bfd6aaea0adb2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Jul 2018 13:58:20 +0200 Subject: [PATCH 2109/2380] pr-manager-helper: fix memory leak on event Reported by Coverity. Signed-off-by: Paolo Bonzini --- scsi/pr-manager-helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c index 519a296905..3027dde60d 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -46,6 +46,7 @@ static void pr_manager_send_status_changed_event(PRManagerHelper *pr_mgr) if (id) { qapi_event_send_pr_manager_status_changed(id, !!pr_mgr->ioc, &error_abort); + g_free(id); } } From 960a479f7f94bb615991d41b8c5ff4e3c7d0088d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Jul 2018 14:03:10 +0200 Subject: [PATCH 2110/2380] ioapic: remove useless lower bounds check The vector cannot be negative. Coverity now reports this because it sees an array access before the check, in ioapic_stat_update_irq. Reviewed-by: Peter Xu Signed-off-by: Paolo Bonzini --- hw/intc/ioapic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index b3937807c2..b6896ac4ce 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -152,7 +152,7 @@ static void ioapic_set_irq(void *opaque, int vector, int level) if (vector == 0) { vector = 2; } - if (vector >= 0 && vector < IOAPIC_NUM_PINS) { + if (vector < IOAPIC_NUM_PINS) { uint32_t mask = 1 << vector; uint64_t entry = s->ioredtbl[vector]; From e20122ff0faf07cb701d35e39e106d1783c07725 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Jul 2018 18:05:43 +0200 Subject: [PATCH 2111/2380] checkpatch: handle token pasting better The mechanism to find possible type tokens can sometimes be confused and go into an infinite loop. This happens for example in QEMU for a line that looks like uint## BITS ##_t S = _S, T = _T; \ uint## BITS ##_t as, at, xs, xt, xd; \ Because the token pasting operator does not have a space before _t, it does not match $notPermitted. However, (?x) is turned on in the regular expression for modifiers, and thus ##_t matches the empty string. As a result, annotate_values goes in an infinite loop. The solution is simply to remove token pasting operators from the string before looking for modifiers. In the example above, the string uintBITS_t will be evaluated as a candidate modifier. This is not optimal, but it works as long as people do not write things like a##s##m, and it fits nicely into sub possible. For a similar reason, \# should be rejected always, even if it is not at end of line or followed by whitespace. The same patch was sent to the Linux kernel mailing list. Reported-by: Aleksandar Markovic Signed-off-by: Paolo Bonzini --- scripts/checkpatch.pl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 223681bfd0..42e1c50dd8 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1132,11 +1132,10 @@ sub possible { case| else| asm|__asm__| - do| - \#| - \#\# + do )(?:\s|$)| - ^(?:typedef|struct|enum)\b + ^(?:typedef|struct|enum)\b| + ^\# )}x; warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); if ($possible !~ $notPermitted) { @@ -1146,7 +1145,7 @@ sub possible { if ($possible =~ /^\s*$/) { } elsif ($possible =~ /\s/) { - $possible =~ s/\s*$Type\s*//g; + $possible =~ s/\s*(?:$Type|\#\#)\s*//g; for my $modifier (split(' ', $possible)) { if ($modifier !~ $notPermitted) { warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); From 3232794bcb77940dd7167abb8cc72897883523f6 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 3 Jul 2018 20:10:43 +0100 Subject: [PATCH 2112/2380] ppc: fix default VGA display for Mac machines Commit 29f9cef39e "ppc: Include vga cirrus card into the compiling process" changed the default display adapter for all PPC machines to cirrus. Unfortunately it missed setting the default display type to stdvga for both Mac machines causing the display to fail to initialise under OpenBIOS. Update the MachineClass for both Old World and New World Macs so that the default std(vga) display adapter is the default if no options are specified which fixes the display for the Mac machines. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/ppc/mac_newworld.c | 1 + hw/ppc/mac_oldworld.c | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index d11980166f..2ca294664b 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -525,6 +525,7 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_IDE; mc->max_cpus = MAX_CPUS; mc->default_boot_order = "cd"; + mc->default_display = "std"; mc->kvm_type = core99_kvm_type; #ifdef TARGET_PPC64 mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("970fx_v3.1"); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 06ed6f660e..064d7eb30a 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -383,6 +383,7 @@ static void heathrow_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->kvm_type = heathrow_kvm_type; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("750_v3.1"); + mc->default_display = "std"; } static const TypeInfo ppc_heathrow_machine_info = { From 9e3a83a15b4d94e7738464e92b6c5cb335961ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 3 Jul 2018 17:23:59 +0200 Subject: [PATCH 2113/2380] spapr/vio: quiet down the "irq" property accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit efe2add7cb7f ("spapr/vio: deprecate the "irq" property") introduced get/set accessors for the "irq" property to warn of its usage, but the warning in the get pollutes the monitor 'info qtree'. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/spapr_vio.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index daf85130b5..be9af71437 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -43,7 +43,16 @@ #include -static void spapr_vio_getset_irq(Object *obj, Visitor *v, const char *name, +static void spapr_vio_get_irq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Property *prop = opaque; + uint32_t *ptr = qdev_get_prop_ptr(DEVICE(obj), prop); + + visit_type_uint32(v, name, ptr, errp); +} + +static void spapr_vio_set_irq(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { Property *prop = opaque; @@ -57,8 +66,8 @@ static void spapr_vio_getset_irq(Object *obj, Visitor *v, const char *name, static const PropertyInfo spapr_vio_irq_propinfo = { .name = "irq", - .get = spapr_vio_getset_irq, - .set = spapr_vio_getset_irq, + .get = spapr_vio_get_irq, + .set = spapr_vio_set_irq, }; static Property spapr_vio_props[] = { From 4a1f253adb45ac6019971193d5077c4d5d55886a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 4 Jul 2018 11:40:58 +0200 Subject: [PATCH 2114/2380] sm501: Implement i2c part for reading monitor EDID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Emulate the i2c part of SM501 which is used to access the EDID info from a monitor. The vmstate structure is changed and its version is increased but SM501 is only used on SH and PPC sam460ex machines that don't support cross-version migration. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- default-configs/ppc-softmmu.mak | 1 + default-configs/ppcemb-softmmu.mak | 1 + default-configs/sh4-softmmu.mak | 2 + default-configs/sh4eb-softmmu.mak | 2 + hw/display/sm501.c | 147 ++++++++++++++++++++++++++++- 5 files changed, 149 insertions(+), 4 deletions(-) diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 6f12bf84f0..3181bbf163 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -25,6 +25,7 @@ CONFIG_ETSEC=y CONFIG_SAM460EX=y CONFIG_USB_EHCI_SYSBUS=y CONFIG_SM501=y +CONFIG_DDC=y CONFIG_IDE_SII3112=y CONFIG_I2C=y CONFIG_BITBANG_I2C=y diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak index 37af1930b3..ac44f150c6 100644 --- a/default-configs/ppcemb-softmmu.mak +++ b/default-configs/ppcemb-softmmu.mak @@ -17,6 +17,7 @@ CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y CONFIG_USB_EHCI_SYSBUS=y CONFIG_SM501=y +CONFIG_DDC=y CONFIG_IDE_SII3112=y CONFIG_I2C=y CONFIG_BITBANG_I2C=y diff --git a/default-configs/sh4-softmmu.mak b/default-configs/sh4-softmmu.mak index 546d855088..caeccd55be 100644 --- a/default-configs/sh4-softmmu.mak +++ b/default-configs/sh4-softmmu.mak @@ -9,6 +9,8 @@ CONFIG_PFLASH_CFI02=y CONFIG_SH4=y CONFIG_IDE_MMIO=y CONFIG_SM501=y +CONFIG_I2C=y +CONFIG_DDC=y CONFIG_ISA_TESTDEV=y CONFIG_I82378=y CONFIG_I8259=y diff --git a/default-configs/sh4eb-softmmu.mak b/default-configs/sh4eb-softmmu.mak index 2d3fd49663..53b9cd7b5a 100644 --- a/default-configs/sh4eb-softmmu.mak +++ b/default-configs/sh4eb-softmmu.mak @@ -9,6 +9,8 @@ CONFIG_PFLASH_CFI02=y CONFIG_SH4=y CONFIG_IDE_MMIO=y CONFIG_SM501=y +CONFIG_I2C=y +CONFIG_DDC=y CONFIG_ISA_TESTDEV=y CONFIG_I82378=y CONFIG_I8259=y diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 9dec0d3218..05c964bea1 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "qemu/log.h" #include "qemu-common.h" #include "cpu.h" #include "hw/hw.h" @@ -34,6 +35,8 @@ #include "hw/devices.h" #include "hw/sysbus.h" #include "hw/pci/pci.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/i2c-ddc.h" #include "qemu/range.h" #include "ui/pixel_ops.h" @@ -216,6 +219,14 @@ #define SM501_I2C_SLAVE_ADDRESS (0x03) #define SM501_I2C_DATA (0x04) +#define SM501_I2C_CONTROL_START (1 << 2) +#define SM501_I2C_CONTROL_ENABLE (1 << 0) + +#define SM501_I2C_STATUS_COMPLETE (1 << 3) +#define SM501_I2C_STATUS_ERROR (1 << 2) + +#define SM501_I2C_RESET_ERROR (1 << 2) + /* SSP base */ #define SM501_SSP (0x020000) @@ -471,10 +482,12 @@ typedef struct SM501State { MemoryRegion local_mem_region; MemoryRegion mmio_region; MemoryRegion system_config_region; + MemoryRegion i2c_region; MemoryRegion disp_ctrl_region; MemoryRegion twoD_engine_region; uint32_t last_width; uint32_t last_height; + I2CBus *i2c_bus; /* mmio registers */ uint32_t system_control; @@ -487,6 +500,11 @@ typedef struct SM501State { uint32_t misc_timing; uint32_t power_mode_control; + uint8_t i2c_byte_count; + uint8_t i2c_status; + uint8_t i2c_addr; + uint8_t i2c_data[16]; + uint32_t uart0_ier; uint32_t uart0_lcr; uint32_t uart0_mcr; @@ -897,6 +915,109 @@ static const MemoryRegionOps sm501_system_config_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static uint64_t sm501_i2c_read(void *opaque, hwaddr addr, unsigned size) +{ + SM501State *s = (SM501State *)opaque; + uint8_t ret = 0; + + switch (addr) { + case SM501_I2C_BYTE_COUNT: + ret = s->i2c_byte_count; + break; + case SM501_I2C_STATUS: + ret = s->i2c_status; + break; + case SM501_I2C_SLAVE_ADDRESS: + ret = s->i2c_addr; + break; + case SM501_I2C_DATA ... SM501_I2C_DATA + 15: + ret = s->i2c_data[addr - SM501_I2C_DATA]; + break; + default: + qemu_log_mask(LOG_UNIMP, "sm501 i2c : not implemented register read." + " addr=0x%" HWADDR_PRIx "\n", addr); + } + + SM501_DPRINTF("sm501 i2c regs : read addr=%" HWADDR_PRIx " val=%x\n", + addr, ret); + return ret; +} + +static void sm501_i2c_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + SM501State *s = (SM501State *)opaque; + SM501_DPRINTF("sm501 i2c regs : write addr=%" HWADDR_PRIx + " val=%" PRIx64 "\n", addr, value); + + switch (addr) { + case SM501_I2C_BYTE_COUNT: + s->i2c_byte_count = value & 0xf; + break; + case SM501_I2C_CONTROL: + if (value & SM501_I2C_CONTROL_ENABLE) { + if (value & SM501_I2C_CONTROL_START) { + int res = i2c_start_transfer(s->i2c_bus, + s->i2c_addr >> 1, + s->i2c_addr & 1); + s->i2c_status |= (res ? SM501_I2C_STATUS_ERROR : 0); + if (!res) { + int i; + SM501_DPRINTF("sm501 i2c : transferring %d bytes to 0x%x\n", + s->i2c_byte_count + 1, s->i2c_addr >> 1); + for (i = 0; i <= s->i2c_byte_count; i++) { + res = i2c_send_recv(s->i2c_bus, &s->i2c_data[i], + !(s->i2c_addr & 1)); + if (res) { + SM501_DPRINTF("sm501 i2c : transfer failed" + " i=%d, res=%d\n", i, res); + s->i2c_status |= (res ? SM501_I2C_STATUS_ERROR : 0); + return; + } + } + if (i) { + SM501_DPRINTF("sm501 i2c : transferred %d bytes\n", i); + s->i2c_status = SM501_I2C_STATUS_COMPLETE; + } + } + } else { + SM501_DPRINTF("sm501 i2c : end transfer\n"); + i2c_end_transfer(s->i2c_bus); + s->i2c_status &= ~SM501_I2C_STATUS_ERROR; + } + } + break; + case SM501_I2C_RESET: + if ((value & SM501_I2C_RESET_ERROR) == 0) { + s->i2c_status &= ~SM501_I2C_STATUS_ERROR; + } + break; + case SM501_I2C_SLAVE_ADDRESS: + s->i2c_addr = value & 0xff; + break; + case SM501_I2C_DATA ... SM501_I2C_DATA + 15: + s->i2c_data[addr - SM501_I2C_DATA] = value & 0xff; + break; + default: + qemu_log_mask(LOG_UNIMP, "sm501 i2c : not implemented register write. " + "addr=0x%" HWADDR_PRIx " val=%" PRIx64 "\n", addr, value); + } +} + +static const MemoryRegionOps sm501_i2c_ops = { + .read = sm501_i2c_read, + .write = sm501_i2c_write, + .valid = { + .min_access_size = 1, + .max_access_size = 1, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static uint32_t sm501_palette_read(void *opaque, hwaddr addr) { SM501State *s = (SM501State *)opaque; @@ -1577,6 +1698,10 @@ static void sm501_reset(SM501State *s) s->irq_mask = 0; s->misc_timing = 0; s->power_mode_control = 0; + s->i2c_byte_count = 0; + s->i2c_status = 0; + s->i2c_addr = 0; + memset(s->i2c_data, 0, 16); s->dc_panel_control = 0x00010000; /* FIFO level 3 */ s->dc_video_control = 0; s->dc_crt_control = 0x00010000; @@ -1615,6 +1740,12 @@ static void sm501_init(SM501State *s, DeviceState *dev, memory_region_set_log(&s->local_mem_region, true, DIRTY_MEMORY_VGA); s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region); + /* i2c */ + s->i2c_bus = i2c_init_bus(dev, "sm501.i2c"); + /* ddc */ + I2CDDCState *ddc = I2CDDC(qdev_create(BUS(s->i2c_bus), TYPE_I2CDDC)); + i2c_set_slave_address(I2C_SLAVE(ddc), 0x50); + /* mmio */ memory_region_init(&s->mmio_region, OBJECT(dev), "sm501.mmio", MMIO_SIZE); memory_region_init_io(&s->system_config_region, OBJECT(dev), @@ -1622,6 +1753,9 @@ static void sm501_init(SM501State *s, DeviceState *dev, "sm501-system-config", 0x6c); memory_region_add_subregion(&s->mmio_region, SM501_SYS_CONFIG, &s->system_config_region); + memory_region_init_io(&s->i2c_region, OBJECT(dev), &sm501_i2c_ops, s, + "sm501-i2c", 0x14); + memory_region_add_subregion(&s->mmio_region, SM501_I2C, &s->i2c_region); memory_region_init_io(&s->disp_ctrl_region, OBJECT(dev), &sm501_disp_ctrl_ops, s, "sm501-disp-ctrl", 0x1000); @@ -1705,6 +1839,11 @@ static const VMStateDescription vmstate_sm501_state = { VMSTATE_UINT32(twoD_destination_base, SM501State), VMSTATE_UINT32(twoD_alpha, SM501State), VMSTATE_UINT32(twoD_wrap, SM501State), + /* Added in version 2 */ + VMSTATE_UINT8(i2c_byte_count, SM501State), + VMSTATE_UINT8(i2c_status, SM501State), + VMSTATE_UINT8(i2c_addr, SM501State), + VMSTATE_UINT8_ARRAY(i2c_data, SM501State, 16), VMSTATE_END_OF_LIST() } }; @@ -1770,8 +1909,8 @@ static void sm501_reset_sysbus(DeviceState *dev) static const VMStateDescription vmstate_sm501_sysbus = { .name = TYPE_SYSBUS_SM501, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_STRUCT(state, SM501SysBusState, 1, vmstate_sm501_state, SM501State), @@ -1843,8 +1982,8 @@ static void sm501_reset_pci(DeviceState *dev) static const VMStateDescription vmstate_sm501_pci = { .name = TYPE_PCI_SM501, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, SM501PCIState), VMSTATE_STRUCT(state, SM501PCIState, 1, From d27335592a187de0ba77894b522025827907038c Mon Sep 17 00:00:00 2001 From: Sebastian Bauer Date: Wed, 4 Jul 2018 11:40:58 +0200 Subject: [PATCH 2115/2380] sm501: Perform a full update after palette change Changing the palette of a color index has as an immediate effect on all pixels with the corresponding index on real hardware. Performing a full update after a palette change is a simple way to emulate this effect. Signed-off-by: Sebastian Bauer Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/display/sm501.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 05c964bea1..b095134d6f 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -487,6 +487,7 @@ typedef struct SM501State { MemoryRegion twoD_engine_region; uint32_t last_width; uint32_t last_height; + bool do_full_update; /* perform a full update next time */ I2CBus *i2c_bus; /* mmio registers */ @@ -1042,6 +1043,7 @@ static void sm501_palette_write(void *opaque, hwaddr addr, assert(range_covers_byte(0, 0x400 * 3, addr)); *(uint32_t *)&s->dc_palette[addr] = value; + s->do_full_update = true; } static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, @@ -1630,6 +1632,12 @@ static void sm501_update_display(void *opaque) full_update = 1; } + /* someone else requested a full update */ + if (s->do_full_update) { + s->do_full_update = false; + full_update = 1; + } + /* draw each line according to conditions */ snap = memory_region_snapshot_and_clear_dirty(&s->local_mem_region, offset, width * height * src_bpp, DIRTY_MEMORY_VGA); From 54b2a4339ccae45d4264c2437978abd04ff82bca Mon Sep 17 00:00:00 2001 From: Sebastian Bauer Date: Wed, 4 Jul 2018 11:40:58 +0200 Subject: [PATCH 2116/2380] sm501: Use values from the pitch register for 2D operations Before, crt_h_total was used for src_width and dst_width. This is a property of the current display setting and not relevant for the 2D operation that also can be done off-screen. The pitch register's purpose is to describe line pitch relevant of the 2D operation. Signed-off-by: Sebastian Bauer Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/display/sm501.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index b095134d6f..d4878f01c0 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -709,8 +709,8 @@ static void sm501_2d_operation(SM501State *s) /* get frame buffer info */ uint8_t *src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); uint8_t *dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); - int src_width = (s->dc_crt_h_total & 0x00000FFF) + 1; - int dst_width = (s->dc_crt_h_total & 0x00000FFF) + 1; + int src_width = s->twoD_pitch & 0x1FFF; + int dst_width = (s->twoD_pitch >> 16) & 0x1FFF; if (addressing != 0x0) { printf("%s: only XY addressing is supported.\n", __func__); From debc7e7dad1e70c03d585d96625da0038eba375c Mon Sep 17 00:00:00 2001 From: Sebastian Bauer Date: Wed, 4 Jul 2018 11:40:58 +0200 Subject: [PATCH 2117/2380] sm501: Implement negated destination raster operation mode Add support for the negated destination operation mode. This is used e.g. by AmigaOS for the INVERSEVID drawing mode. With this change, the cursor in the shell and non-immediate window adjustment are working now. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/display/sm501.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index d4878f01c0..0b44d95169 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -705,6 +705,8 @@ static void sm501_2d_operation(SM501State *s) uint32_t color = s->twoD_foreground; int format_flags = (s->twoD_stretch >> 20) & 0x3; int addressing = (s->twoD_stretch >> 16) & 0xF; + int rop_mode = (s->twoD_control >> 15) & 0x1; /* 1 for rop2, else rop3 */ + int rop = s->twoD_control & 0xFF; /* get frame buffer info */ uint8_t *src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); @@ -729,6 +731,8 @@ static void sm501_2d_operation(SM501State *s) int y, x, index_d, index_s; \ for (y = 0; y < operation_height; y++) { \ for (x = 0; x < operation_width; x++) { \ + _pixel_type val; \ + \ if (rtl) { \ index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ @@ -736,7 +740,13 @@ static void sm501_2d_operation(SM501State *s) index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ } \ - *(_pixel_type *)&dst[index_d] = *(_pixel_type *)&src[index_s];\ + if (rop_mode == 1 && rop == 5) { \ + /* Invert dest */ \ + val = ~*(_pixel_type *)&dst[index_d]; \ + } else { \ + val = *(_pixel_type *)&src[index_s]; \ + } \ + *(_pixel_type *)&dst[index_d] = val; \ } \ } \ } From 06cb926aaa51970fc009245bb5e15700c19a56f8 Mon Sep 17 00:00:00 2001 From: Sebastian Bauer Date: Wed, 4 Jul 2018 11:40:58 +0200 Subject: [PATCH 2118/2380] sm501: Log unimplemented raster operation modes The sm501 currently implements only a very limited set of raster operation modes. After this change, unknown raster operation modes are logged so these can be easily spotted. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/display/sm501.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 0b44d95169..ebd139006e 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -706,6 +706,8 @@ static void sm501_2d_operation(SM501State *s) int format_flags = (s->twoD_stretch >> 20) & 0x3; int addressing = (s->twoD_stretch >> 16) & 0xF; int rop_mode = (s->twoD_control >> 15) & 0x1; /* 1 for rop2, else rop3 */ + /* 1 if rop2 source is the pattern, otherwise the source is the bitmap */ + int rop2_source_is_pattern = (s->twoD_control >> 14) & 0x1; int rop = s->twoD_control & 0xFF; /* get frame buffer info */ @@ -719,6 +721,27 @@ static void sm501_2d_operation(SM501State *s) abort(); } + if (rop_mode == 0) { + if (rop != 0xcc) { + /* Anything other than plain copies are not supported */ + qemu_log_mask(LOG_UNIMP, "sm501: rop3 mode with rop %x is not " + "supported.\n", rop); + } + } else { + if (rop2_source_is_pattern && rop != 0x5) { + /* For pattern source, we support only inverse dest */ + qemu_log_mask(LOG_UNIMP, "sm501: rop2 source being the pattern and " + "rop %x is not supported.\n", rop); + } else { + if (rop != 0x5 && rop != 0xc) { + /* Anything other than plain copies or inverse dest is not + * supported */ + qemu_log_mask(LOG_UNIMP, "sm501: rop mode %x is not " + "supported.\n", rop); + } + } + } + if ((s->twoD_source_base & 0x08000000) || (s->twoD_destination_base & 0x08000000)) { printf("%s: only local memory is supported.\n", __func__); From 33159dd7ce2ccd14ab31062d80632297e04e46cf Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 4 Jul 2018 11:40:58 +0200 Subject: [PATCH 2119/2380] sm501: Fix support for non-zero frame buffer start address Display updates and drawing hardware cursor did not work when frame buffer address was non-zero. Fix this by taking the frame buffer address into account in these cases. This fixes screen dragging on AmigaOS. Based on patch by Sebastian Bauer. Signed-off-by: Sebastian Bauer Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/display/sm501.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index ebd139006e..10ee008eb2 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -586,6 +586,11 @@ static uint32_t get_local_mem_size_index(uint32_t size) return index; } +static ram_addr_t get_fb_addr(SM501State *s, int crt) +{ + return (crt ? s->dc_crt_fb_addr : s->dc_panel_fb_addr) & 0x3FFFFF0; +} + static inline int get_width(SM501State *s, int crt) { int width = crt ? s->dc_crt_h_total : s->dc_panel_h_total; @@ -688,7 +693,8 @@ static inline void hwc_invalidate(SM501State *s, int crt) start *= w * bpp; end *= w * bpp; - memory_region_set_dirty(&s->local_mem_region, start, end - start); + memory_region_set_dirty(&s->local_mem_region, + get_fb_addr(s, crt) + start, end - start); } static void sm501_2d_operation(SM501State *s) @@ -1213,6 +1219,9 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, break; case SM501_DC_PANEL_FB_ADDR: s->dc_panel_fb_addr = value & 0x8FFFFFF0; + if (value & 0x8000000) { + qemu_log_mask(LOG_UNIMP, "Panel external memory not supported\n"); + } break; case SM501_DC_PANEL_FB_OFFSET: s->dc_panel_fb_offset = value & 0x3FF03FF0; @@ -1273,6 +1282,9 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, break; case SM501_DC_CRT_FB_ADDR: s->dc_crt_fb_addr = value & 0x8FFFFFF0; + if (value & 0x8000000) { + qemu_log_mask(LOG_UNIMP, "CRT external memory not supported\n"); + } break; case SM501_DC_CRT_FB_OFFSET: s->dc_crt_fb_offset = value & 0x3FF03FF0; @@ -1615,7 +1627,7 @@ static void sm501_update_display(void *opaque) draw_hwc_line_func *draw_hwc_line = NULL; int full_update = 0; int y_start = -1; - ram_addr_t offset = 0; + ram_addr_t offset; uint32_t *palette; uint8_t hwc_palette[3 * 3]; uint8_t *hwc_src = NULL; @@ -1672,9 +1684,10 @@ static void sm501_update_display(void *opaque) } /* draw each line according to conditions */ + offset = get_fb_addr(s, crt); snap = memory_region_snapshot_and_clear_dirty(&s->local_mem_region, offset, width * height * src_bpp, DIRTY_MEMORY_VGA); - for (y = 0, offset = 0; y < height; y++, offset += width * src_bpp) { + for (y = 0; y < height; y++, offset += width * src_bpp) { int update, update_hwc; /* check if hardware cursor is enabled and we're within its range */ From eb76243c9da613d0abc27eb38a0d47b82f7ca00b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 4 Jul 2018 11:40:58 +0200 Subject: [PATCH 2120/2380] sm501: Set updated region dirty after 2D operation Set the changed memory region dirty after performed a 2D operation to ensure that the screen is updated properly. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/display/sm501.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 10ee008eb2..3661a89f60 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -715,12 +715,16 @@ static void sm501_2d_operation(SM501State *s) /* 1 if rop2 source is the pattern, otherwise the source is the bitmap */ int rop2_source_is_pattern = (s->twoD_control >> 14) & 0x1; int rop = s->twoD_control & 0xFF; + uint32_t src_base = s->twoD_source_base & 0x03FFFFFF; + uint32_t dst_base = s->twoD_destination_base & 0x03FFFFFF; /* get frame buffer info */ - uint8_t *src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); - uint8_t *dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); + uint8_t *src = s->local_mem + src_base; + uint8_t *dst = s->local_mem + dst_base; int src_width = s->twoD_pitch & 0x1FFF; int dst_width = (s->twoD_pitch >> 16) & 0x1FFF; + int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0; + int fb_len = get_width(s, crt) * get_height(s, crt) * get_bpp(s, crt); if (addressing != 0x0) { printf("%s: only XY addressing is supported.\n", __func__); @@ -821,6 +825,15 @@ static void sm501_2d_operation(SM501State *s) abort(); break; } + + if (dst_base >= get_fb_addr(s, crt) && + dst_base <= get_fb_addr(s, crt) + fb_len) { + int dst_len = MIN(fb_len, ((dst_y + operation_height - 1) * dst_width + + dst_x + operation_width) * (1 << format_flags)); + if (dst_len) { + memory_region_set_dirty(&s->local_mem_region, dst_base, dst_len); + } + } } static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, From 7aeb1e5100c026f2f232e570cd4d56942785f462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 4 Jul 2018 11:44:00 -0300 Subject: [PATCH 2121/2380] ppc440_uc: Fix a copy/paste error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed in 3c409c1927e, hopefully reported by Coverity. Fixes: Coverity CID 1393788 (Copy-paste error) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/ppc/ppc440_uc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 0bbaa6844a..09ccda548f 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -935,7 +935,7 @@ static void dcr_write_dma(void *opaque, int dcrn, uint32_t val) if (wptr) { cpu_physical_memory_unmap(wptr, wlen, 1, didx); } - if (wptr) { + if (rptr) { cpu_physical_memory_unmap(rptr, rlen, 0, sidx); } } From 4fff72185b38cc9ad3162c1794428141f20366a3 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 4 Jul 2018 20:38:55 +0200 Subject: [PATCH 2122/2380] target/ppc: fix build on ppc64 host When I try to build a ppc64 target on a ppc64 host (gcc 8.1.1), I have: .../target/ppc/int_helper.c: In function 'helper_vinsertb': .../target/ppc/int_helper.c:1954:32: error: array subscript 18446744073709551608 is above array bounds of 'uint8_t[16]' {aka 'unsigned char[16]'} [-Werror=array-bounds] memmove(&r->u8[index], &b->u8[8 - sizeof(r->element)], \ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../target/ppc/int_helper.c:1965:1: note: in expansion of macro 'VINSERT' If we compare with the macro for ppc64le, we can see sizeof(r->element[0]) should be used instead of sizeof(r->element). And VINSERT uses only u8, u16, u32 and u64, so the maximum value of sizeof(r->element[0]) is 8 Suggested-by: Dr. David Alan Gilbert Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Signed-off-by: David Gibson --- target/ppc/int_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 03d37da79f..d52338ed71 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -1951,7 +1951,7 @@ VSPLT(w, u32) #define VINSERT(suffix, element) \ void helper_vinsert##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ { \ - memmove(&r->u8[index], &b->u8[8 - sizeof(r->element)], \ + memmove(&r->u8[index], &b->u8[8 - sizeof(r->element[0])], \ sizeof(r->element[0])); \ } #else From 4cb25fbb433581a0f80a8564bd2bcf574b1d84a7 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 6 Jul 2018 08:51:55 +0100 Subject: [PATCH 2123/2380] ppc: fix default VGA display for PReP machines Commit 29f9cef "ppc: Include vga cirrus card into the compiling process" changed the default display adapter for all PPC machines to cirrus. Unfortunately it missed setting the default display type to stdvga for both PReP machines causing the display to fail to initialise under OpenHackWare. Update the MachineClass for both prep and 40p machines so that the default std(vga) display adapter is the default if no options are specified which fixes the display for the PReP machines. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/ppc/prep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 6689407b3d..3401570d98 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -682,6 +682,7 @@ static void prep_machine_init(MachineClass *mc) mc->max_cpus = MAX_CPUS; mc->default_boot_order = "cad"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("602"); + mc->default_display = "std"; } static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) @@ -888,6 +889,7 @@ static void ibm_40p_machine_init(MachineClass *mc) mc->block_default_type = IF_SCSI; mc->default_boot_order = "c"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("604"); + mc->default_display = "std"; } DEFINE_MACHINE("40p", ibm_40p_machine_init) From 0c0eaed14721f8a9db334deb35316411c512059a Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 4 Jul 2018 14:23:01 +0300 Subject: [PATCH 2124/2380] nbd/server: fix nbd_co_send_block_status Call nbd_co_send_extents() with correct length parameter (extent.length may be smaller than original length). Also, switch length parameter type to uint32_t, to correspond with request->len and similar nbd_co_send_bitmap(). Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180704112302.471456-2-vsementsov@virtuozzo.com> Signed-off-by: Eric Blake --- nbd/server.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index e52b76bd1a..ea5fe0eb33 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1910,7 +1910,7 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, /* Get block status from the exported device and send it to the client */ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, BlockDriverState *bs, uint64_t offset, - uint64_t length, bool last, + uint32_t length, bool last, uint32_t context_id, Error **errp) { int ret; @@ -1922,7 +1922,8 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, client, handle, -ret, "can't get block status", errp); } - return nbd_co_send_extents(client, handle, &extent, 1, length, last, + return nbd_co_send_extents(client, handle, &extent, 1, + be32_to_cpu(extent.length), last, context_id, errp); } From 122f9c83f2ef1cd90152ea8116fe6e80c8e85994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jul 2018 12:51:25 -0300 Subject: [PATCH 2125/2380] linux-user: Do not report "Unsupported syscall" by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This can still be reported using the "-d unimp" command line option. Fixes: https://bugs.launchpad.net/qemu/+bug/1777226 Reported-by: John Paul Adrian Glaubitz Suggested-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20180706155127.7483-2-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 5822e03e28..e4b1b7d7da 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -12757,7 +12757,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, default: unimplemented: - gemu_log("qemu: Unsupported syscall: %d\n", num); + qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num); #if defined(TARGET_NR_setxattr) || defined(TARGET_NR_get_thread_area) || defined(TARGET_NR_getdomainname) || defined(TARGET_NR_set_robust_list) unimplemented_nowarn: #endif From 8f0ea816435c0f6b7bcf8bd0f46f705ccca0316a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jul 2018 12:51:26 -0300 Subject: [PATCH 2126/2380] linux-user: Do not report "syscall not implemented" by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This can still be reported using the "-d unimp" command line option. Code change produced with: git ls-files linux-user | \ xargs sed -i -E 's/fprintf\(stderr,\s?(".*not implemented\\n")\);/qemu_log_mask(LOG_UNIMP, \1);/g' Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20180706155127.7483-3-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/cris/signal.c | 4 ++-- linux-user/microblaze/signal.c | 4 ++-- linux-user/nios2/signal.c | 2 +- linux-user/sparc/signal.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/linux-user/cris/signal.c b/linux-user/cris/signal.c index 0b405247cf..1e02194377 100644 --- a/linux-user/cris/signal.c +++ b/linux-user/cris/signal.c @@ -146,7 +146,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUCRISState *env) { - fprintf(stderr, "CRIS setup_rt_frame: not implemented\n"); + qemu_log_mask(LOG_UNIMP, "setup_rt_frame: not implemented\n"); } long do_sigreturn(CPUCRISState *env) @@ -183,6 +183,6 @@ badframe: long do_rt_sigreturn(CPUCRISState *env) { trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "CRIS do_rt_sigreturn: not implemented\n"); + qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index 712ee522b2..80950c2181 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -200,7 +200,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUMBState *env) { - fprintf(stderr, "Microblaze setup_rt_frame: not implemented\n"); + qemu_log_mask(LOG_UNIMP, "setup_rt_frame: not implemented\n"); } long do_sigreturn(CPUMBState *env) @@ -240,6 +240,6 @@ badframe: long do_rt_sigreturn(CPUMBState *env) { trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "Microblaze do_rt_sigreturn: not implemented\n"); + qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c index 4985dc2212..7d535065ed 100644 --- a/linux-user/nios2/signal.c +++ b/linux-user/nios2/signal.c @@ -203,7 +203,7 @@ give_sigsegv: long do_sigreturn(CPUNios2State *env) { trace_user_do_sigreturn(env, 0); - fprintf(stderr, "do_sigreturn: not implemented\n"); + qemu_log_mask(LOG_UNIMP, "do_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 55e9d6f9b2..b4c60aa446 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -278,7 +278,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUSPARCState *env) { - fprintf(stderr, "setup_rt_frame: not implemented\n"); + qemu_log_mask(LOG_UNIMP, "setup_rt_frame: not implemented\n"); } long do_sigreturn(CPUSPARCState *env) @@ -357,7 +357,7 @@ segv_and_exit: long do_rt_sigreturn(CPUSPARCState *env) { trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "do_rt_sigreturn: not implemented\n"); + qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } From 84ca4fa99d7b6c83ce31e1e41300d55cb4e97dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jul 2018 12:51:27 -0300 Subject: [PATCH 2127/2380] linux-user: Report error message on stderr, rather than stdout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code change produced with: git ls-files linux-user | \ xargs sed -i -E 's/(\s+)printf\s*\(("Unhandled.*)\);/\1fprintf(stderr, \2);/g' Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20180706155127.7483-4-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/alpha/cpu_loop.c | 2 +- linux-user/cris/cpu_loop.c | 2 +- linux-user/microblaze/cpu_loop.c | 6 +++--- linux-user/sh4/cpu_loop.c | 2 +- linux-user/sparc/cpu_loop.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index b87fcaea87..c1a98c8cbf 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -196,7 +196,7 @@ void cpu_loop(CPUAlphaState *env) arch_interrupt = false; break; default: - printf ("Unhandled trap: 0x%x\n", trapnr); + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); exit(EXIT_FAILURE); } diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c index 1c5eca9f83..37bdcfa8cc 100644 --- a/linux-user/cris/cpu_loop.c +++ b/linux-user/cris/cpu_loop.c @@ -81,7 +81,7 @@ void cpu_loop(CPUCRISState *env) cpu_exec_step_atomic(cs); break; default: - printf ("Unhandled trap: 0x%x\n", trapnr); + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); exit(EXIT_FAILURE); } diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 5af12d5b21..2af93eb39a 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -105,8 +105,8 @@ void cpu_loop(CPUMBState *env) queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; default: - printf("Unhandled hw-exception: 0x%" PRIx64 "\n", - env->sregs[SR_ESR] & ESR_EC_MASK); + fprintf(stderr, "Unhandled hw-exception: 0x%" PRIx64 "\n", + env->sregs[SR_ESR] & ESR_EC_MASK); cpu_dump_state(cs, stderr, fprintf, 0); exit(EXIT_FAILURE); break; @@ -130,7 +130,7 @@ void cpu_loop(CPUMBState *env) cpu_exec_step_atomic(cs); break; default: - printf ("Unhandled trap: 0x%x\n", trapnr); + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); exit(EXIT_FAILURE); } diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index 418833ea25..fdd348170b 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -84,7 +84,7 @@ void cpu_loop(CPUSH4State *env) arch_interrupt = false; break; default: - printf ("Unhandled trap: 0x%x\n", trapnr); + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); exit(EXIT_FAILURE); } diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 7c4796ca23..91f714afc6 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -285,7 +285,7 @@ void cpu_loop (CPUSPARCState *env) cpu_exec_step_atomic(cs); break; default: - printf ("Unhandled trap: 0x%x\n", trapnr); + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); exit(EXIT_FAILURE); } From 9ba58cb47fbc9a5141a4b3e66924f9431ea81da0 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 9 Jul 2018 00:01:46 +0200 Subject: [PATCH 2128/2380] sam460ex: Update u-boot-sam460ex firmware Update the submodule and u-boot-sam460-20100605.bin to include following fixes from Sebastian Bauer: - Fix build with newer gcc - Decrease unnecessary delay which fixes slow booting from CD Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- pc-bios/u-boot-sam460-20100605.bin | Bin 524288 -> 524288 bytes roms/u-boot-sam460ex | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/u-boot-sam460-20100605.bin b/pc-bios/u-boot-sam460-20100605.bin index 99408f8e957b26e735e5befa98fa3d51a90d95f6..e17de77c1974382144625632c709c29a1a86edef 100755 GIT binary patch delta 84 zcmZo@P-tjSm>@3hRhpw@3hpIo9~WTaqZU}&zOt6*quWny4uV5B*DBiE%S#%+v@Tql@>JErrT gWSj-0+gF@q1Y#y2W(HywAZ7((w(ToUvPYN!08um@+W-In diff --git a/roms/u-boot-sam460ex b/roms/u-boot-sam460ex index 8ee007c421..60b3916f33 160000 --- a/roms/u-boot-sam460ex +++ b/roms/u-boot-sam460ex @@ -1 +1 @@ -Subproject commit 8ee007c4216fd6a0d760589e8405ce4494497aa0 +Subproject commit 60b3916f33e617a815973c5a6df77055b2e3a588 From ad633de6f5a08ad8e9b74e2372863101239c2cc3 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 9 Jul 2018 14:23:05 +1000 Subject: [PATCH 2129/2380] sam460ex: Check for errors from libfdt functions In a couple of places sam460ex_load_device_tree() calls "raw" libfdt functions which can fail, but doesn't check for error codes. At best, if these fail the guest will be silently started in a non-standard state, or it could fail entirely. Fix this by using the _FDT() helper macro which aborts on a libfdt failure. Signed-off-by: David Gibson --- hw/ppc/sam460ex.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 7eed2ec601..1661e036f3 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -36,6 +36,7 @@ #include "hw/i2c/ppc4xx_i2c.h" #include "hw/i2c/smbus.h" #include "hw/usb/hcd-ehci.h" +#include "hw/ppc/fdt.h" #include @@ -318,13 +319,13 @@ static int sam460ex_load_device_tree(hwaddr addr, /* Remove cpm node if it exists (it is not emulated) */ offset = fdt_path_offset(fdt, "/cpm"); if (offset >= 0) { - fdt_nop_node(fdt, offset); + _FDT(fdt_nop_node(fdt, offset)); } /* set serial port clocks */ offset = fdt_node_offset_by_compatible(fdt, -1, "ns16550"); while (offset >= 0) { - fdt_setprop_cell(fdt, offset, "clock-frequency", UART_FREQ); + _FDT(fdt_setprop_cell(fdt, offset, "clock-frequency", UART_FREQ)); offset = fdt_node_offset_by_compatible(fdt, offset, "ns16550"); } From e753f33136f6230d30aa324220be112ccddbffba Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 9 Jul 2018 14:27:36 +1000 Subject: [PATCH 2130/2380] sam460ex: Don't check for errors from qemu_fdt_*() The qemu_fdt_*() helper functions already exit with a message instead of returning errors, so we don't need to check for errors in the caller. Signed-off-by: David Gibson --- hw/ppc/sam460ex.c | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 1661e036f3..8194f32cd3 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -276,32 +276,18 @@ static int sam460ex_load_device_tree(hwaddr addr, /* Manipulate device tree in memory. */ - ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); - if (ret < 0) { - error_report("couldn't set /memory/reg"); - } + qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); /* default FDT doesn't have a /chosen node... */ qemu_fdt_add_subnode(fdt, "/chosen"); - ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); - if (ret < 0) { - error_report("couldn't set /chosen/linux,initrd-start"); - } + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", initrd_base); - ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); - if (ret < 0) { - error_report("couldn't set /chosen/linux,initrd-end"); - } + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); - ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); - if (ret < 0) { - error_report("couldn't set /chosen/bootargs"); - } + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); /* Copy data from the host device tree into the guest. Since the guest can * directly access the timebase without host involvement, we must expose From 51b0d834c414f494410cc94e56886b6584ae3c54 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 9 Jul 2018 14:27:43 +1000 Subject: [PATCH 2131/2380] sam460ex: Make sam460ex_load_device_tree() handle all errors internally sam460ex_load_device_tree() handles nearly all possible errors by simply exiting (within helper functions and macros). It handles two early error cases by returning an error. There's no particular point to this, so make it handle those directly as well, removing the need for the caller to handle a failure. As a bonus it gives us more specific error messages. Signed-off-by: David Gibson --- hw/ppc/sam460ex.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 8194f32cd3..e2b7028843 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -255,7 +255,6 @@ static int sam460ex_load_device_tree(hwaddr addr, hwaddr initrd_size, const char *kernel_cmdline) { - int ret = -1; uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) }; char *filename; int fdt_size; @@ -266,12 +265,14 @@ static int sam460ex_load_device_tree(hwaddr addr, filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { - goto out; + error_report("Couldn't find dtb file `%s'", BINARY_DEVICE_TREE_FILE); + exit(1); } fdt = load_device_tree(filename, &fdt_size); g_free(filename); - if (fdt == NULL) { - goto out; + if (!fdt) { + error_report("Couldn't load dtb file `%s'", filename); + exit(1); } /* Manipulate device tree in memory. */ @@ -325,11 +326,8 @@ static int sam460ex_load_device_tree(hwaddr addr, rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); - ret = fdt_size; -out: - - return ret; + return fdt_size; } /* Create reset TLB entries for BookE, mapping only the flash memory. */ @@ -599,10 +597,6 @@ static void sam460ex_init(MachineState *machine) dt_size = sam460ex_load_device_tree(FDT_ADDR, machine->ram_size, RAMDISK_ADDR, initrd_size, machine->kernel_cmdline); - if (dt_size < 0) { - error_report("couldn't load device tree"); - exit(1); - } boot_info->dt_base = FDT_ADDR; boot_info->dt_size = dt_size; From b78aae9bb65876baa47b73d5cf63372171630bf9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 9 Jul 2018 14:51:34 +0100 Subject: [PATCH 2132/2380] hw/arm/smmu-common: Fix devfn computation in smmu_iommu_mr smmu_iommu_mr() aims at returning the IOMMUMemoryRegion corresponding to a given sid. The function extracts both the PCIe bus number and the devfn to return this data. Current computation of devfn is wrong as it only returns the PCIe function instead of slot | function. Fixes 32cfd7f39e08 ("hw/arm/smmuv3: Cache/invalidate config data") Signed-off-by: Eric Auger Message-id: 1530775623-32399-1-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 2 +- include/hw/arm/smmu-common.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 3098915d07..55c75d65d2 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -351,7 +351,7 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) bus_n = PCI_BUS_NUM(sid); smmu_bus = smmu_find_smmu_pcibus(s, bus_n); if (smmu_bus) { - devfn = sid & 0x7; + devfn = SMMU_PCI_DEVFN(sid); smmu = smmu_bus->pbdev[devfn]; if (smmu) { return &smmu->iommu; diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 50e2912a95..b07cadd0ef 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -24,6 +24,7 @@ #define SMMU_PCI_BUS_MAX 256 #define SMMU_PCI_DEVFN_MAX 256 +#define SMMU_PCI_DEVFN(sid) (sid & 0xFF) #define SMMU_MAX_VA_BITS 48 From 086ede32afc9c70de3d75c4fb91c63db790cbd5c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 9 Jul 2018 14:51:34 +0100 Subject: [PATCH 2133/2380] ptimer: Add TRIGGER_ONLY_ON_DECREMENT policy option The CMSDK timer behaviour is that an interrupt is triggered when the counter counts down from 1 to 0; however one is not triggered if the counter is manually set to 0 by a guest write to the counter register. Currently ptimer can't handle this; add a policy option to allow a ptimer user to request this behaviour. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Tested-by: Guenter Roeck Message-id: 20180703171044.9503-2-peter.maydell@linaro.org --- hw/core/ptimer.c | 22 +++++++++++++++++++++- include/hw/ptimer.h | 9 +++++++++ tests/ptimer-test.c | 25 +++++++++++++++++++------ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index 7221c68a98..170fd34d8b 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -45,8 +45,20 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust) uint32_t period_frac = s->period_frac; uint64_t period = s->period; uint64_t delta = s->delta; + bool suppress_trigger = false; - if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { + /* + * Note that if delta_adjust is 0 then we must be here because of + * a count register write or timer start, not because of timer expiry. + * In that case the policy might require us to suppress the timer trigger + * that we would otherwise generate for a zero delta. + */ + if (delta_adjust == 0 && + (s->policy_mask & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT)) { + suppress_trigger = true; + } + if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER) + && !suppress_trigger) { ptimer_trigger(s); } @@ -353,6 +365,14 @@ ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask) s->bh = bh; s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s); s->policy_mask = policy_mask; + + /* + * These two policies are incompatible -- trigger-on-decrement implies + * a timer trigger when the count becomes 0, but no-immediate-trigger + * implies a trigger when the count stops being 0. + */ + assert(!((policy_mask & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) && + (policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER))); return s; } diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h index fc4ef5cc1d..0731d9aef1 100644 --- a/include/hw/ptimer.h +++ b/include/hw/ptimer.h @@ -69,6 +69,15 @@ * not the one less. */ #define PTIMER_POLICY_NO_COUNTER_ROUND_DOWN (1 << 4) +/* + * Starting to run with a zero counter, or setting the counter to "0" via + * ptimer_set_count() or ptimer_set_limit() will not trigger the timer + * (though it will cause a reload). Only a counter decrement to "0" + * will cause a trigger. Not compatible with NO_IMMEDIATE_TRIGGER; + * ptimer_init() will assert() that you don't set both. + */ +#define PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT (1 << 5) + /* ptimer.c */ typedef struct ptimer_state ptimer_state; typedef void (*ptimer_cb)(void *opaque); diff --git a/tests/ptimer-test.c b/tests/ptimer-test.c index 41488896f7..b30aad0737 100644 --- a/tests/ptimer-test.c +++ b/tests/ptimer-test.c @@ -208,6 +208,7 @@ static void check_periodic(gconstpointer arg) bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); triggered = false; @@ -311,7 +312,7 @@ static void check_periodic(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_immediate_reload ? 0 : 10); - if (no_immediate_trigger) { + if (no_immediate_trigger || trig_only_on_dec) { g_assert_false(triggered); } else { g_assert_true(triggered); @@ -506,6 +507,7 @@ static void check_run_with_delta_0(gconstpointer arg) bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); triggered = false; @@ -515,7 +517,7 @@ static void check_run_with_delta_0(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_immediate_reload ? 0 : 99); - if (no_immediate_trigger) { + if (no_immediate_trigger || trig_only_on_dec) { g_assert_false(triggered); } else { g_assert_true(triggered); @@ -563,7 +565,7 @@ static void check_run_with_delta_0(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_immediate_reload ? 0 : 99); - if (no_immediate_trigger) { + if (no_immediate_trigger || trig_only_on_dec) { g_assert_false(triggered); } else { g_assert_true(triggered); @@ -609,6 +611,7 @@ static void check_periodic_with_load_0(gconstpointer arg) ptimer_state *ptimer = ptimer_init(bh, *policy); bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER); bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); triggered = false; @@ -617,7 +620,7 @@ static void check_periodic_with_load_0(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - if (no_immediate_trigger) { + if (no_immediate_trigger || trig_only_on_dec) { g_assert_false(triggered); } else { g_assert_true(triggered); @@ -667,6 +670,7 @@ static void check_oneshot_with_load_0(gconstpointer arg) QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); triggered = false; @@ -675,7 +679,7 @@ static void check_oneshot_with_load_0(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - if (no_immediate_trigger) { + if (no_immediate_trigger || trig_only_on_dec) { g_assert_false(triggered); } else { g_assert_true(triggered); @@ -725,6 +729,10 @@ static void add_ptimer_tests(uint8_t policy) g_strlcat(policy_name, "no_counter_rounddown,", 256); } + if (policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) { + g_strlcat(policy_name, "trigger_only_on_decrement,", 256); + } + g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/set_count policy=%s", policy_name), g_memdup(&policy, 1), check_set_count, g_free); @@ -790,10 +798,15 @@ static void add_ptimer_tests(uint8_t policy) static void add_all_ptimer_policies_comb_tests(void) { - int last_policy = PTIMER_POLICY_NO_COUNTER_ROUND_DOWN; + int last_policy = PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT; int policy = PTIMER_POLICY_DEFAULT; for (; policy < (last_policy << 1); policy++) { + if ((policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) && + (policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { + /* Incompatible policy flag settings -- don't try to test them */ + continue; + } add_ptimer_tests(policy); } } From 6583080ed87ea218acae59f1c52b7b11ffec240d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 9 Jul 2018 14:51:34 +0100 Subject: [PATCH 2134/2380] hw/timer/cmsdk-apb-timer: Correct ptimer policy settings The CMSDK timer interrupt triggers when the counter goes from 1 to 0, so we want to trigger immediately, rather than waiting for a clock cycle. Drop the incorrect NO_IMMEDIATE_TRIGGER setting. We also do not want to get an interrupt if the guest sets the counter directly to zero, so use the new TRIGGER_ONLY_ON_DECREMENT policy. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Tested-by: Guenter Roeck Message-id: 20180703171044.9503-3-peter.maydell@linaro.org --- hw/timer/cmsdk-apb-timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c index 9878746609..1f99081db1 100644 --- a/hw/timer/cmsdk-apb-timer.c +++ b/hw/timer/cmsdk-apb-timer.c @@ -201,7 +201,7 @@ static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp) bh = qemu_bh_new(cmsdk_apb_timer_tick, s); s->timer = ptimer_init(bh, PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | - PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | + PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | PTIMER_POLICY_NO_IMMEDIATE_RELOAD | PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); From 0e256833085cc9d292e59a5bda9b886fd09a5f83 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 9 Jul 2018 14:51:34 +0100 Subject: [PATCH 2135/2380] hw/timer/cmsdk-apb-timer: Correctly identify and set one-shot mode The CMSDK APB timer is currently always configured as periodic timer. This results in the following messages when trying to boot Linux. Timer with delta zero, disabling If the timer limit set with the RELOAD command is 0, the timer needs to be enabled as one-shot timer. Signed-off-by: Guenter Roeck Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Tested-by: Guenter Roeck Signed-off-by: Peter Maydell --- hw/timer/cmsdk-apb-timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c index 1f99081db1..3ebdc7be40 100644 --- a/hw/timer/cmsdk-apb-timer.c +++ b/hw/timer/cmsdk-apb-timer.c @@ -119,7 +119,7 @@ static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value, } s->ctrl = value & 0xf; if (s->ctrl & R_CTRL_EN_MASK) { - ptimer_run(s->timer, 0); + ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0); } else { ptimer_stop(s->timer); } From 1a9b30646edd8d10caaa9727611750bf57d08c74 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 9 Jul 2018 14:51:34 +0100 Subject: [PATCH 2136/2380] hw/timer/cmsdk-apb-timer: run or stop timer on writes to RELOAD and VALUE If the CMSDK APB timer is set up with a zero RELOAD value then it will count down to zero, fire once and then stay at zero. From the point of view of the ptimer system, the timer is disabled; but the enable bit in the CTRL register is still set and if the guest subsequently writes to the RELOAD or VALUE registers this should cause the timer to start counting down again. Add code to the write paths for RELOAD and VALUE so that we correctly restart the timer in this situation. Conversely, if the new RELOAD and VALUE are both zero, we should stop the ptimer. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Tested-by: Guenter Roeck Message-id: 20180703171044.9503-5-peter.maydell@linaro.org --- hw/timer/cmsdk-apb-timer.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c index 3ebdc7be40..801d1dba74 100644 --- a/hw/timer/cmsdk-apb-timer.c +++ b/hw/timer/cmsdk-apb-timer.c @@ -126,10 +126,26 @@ static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value, break; case A_RELOAD: /* Writing to reload also sets the current timer value */ + if (!value) { + ptimer_stop(s->timer); + } ptimer_set_limit(s->timer, value, 1); + if (value && (s->ctrl & R_CTRL_EN_MASK)) { + /* + * Make sure timer is running (it might have stopped if this + * was an expired one-shot timer) + */ + ptimer_run(s->timer, 0); + } break; case A_VALUE: + if (!value && !ptimer_get_limit(s->timer)) { + ptimer_stop(s->timer); + } ptimer_set_count(s->timer, value); + if (value && (s->ctrl & R_CTRL_EN_MASK)) { + ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0); + } break; case A_INTSTATUS: /* Just one bit, which is W1C. */ From 2f95a3b09aebdcb5c9152a7ac434a5d57441fe82 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Jul 2018 14:51:34 +0100 Subject: [PATCH 2137/2380] target/arm: Suppress Coverity warning for PRF These instructions must perform the sve_access_check, but since they are implemented as NOPs there is no generated code to elide when the access check fails. Fixes: Coverity issues 1393780 & 1393779. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate-sve.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index c080345b9c..d41f1155f9 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -5164,7 +5164,7 @@ static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a, uint32_t insn) static bool trans_PRF(DisasContext *s, arg_PRF *a, uint32_t insn) { /* Prefetch is a nop within QEMU. */ - sve_access_check(s); + (void)sve_access_check(s); return true; } @@ -5174,7 +5174,7 @@ static bool trans_PRF_rr(DisasContext *s, arg_PRF_rr *a, uint32_t insn) return false; } /* Prefetch is a nop within QEMU. */ - sve_access_check(s); + (void)sve_access_check(s); return true; } From 499748d7683198a765d17b4fdf6901ab9dca920c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Jul 2018 14:51:34 +0100 Subject: [PATCH 2138/2380] tcg: Restrict check_size_impl to multiples of the line size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Normally this is automatic in the size restrictions that are placed on vector sizes coming from the implementation. However, for the legitimate size tuple [oprsz=8, maxsz=32], we need to clear the final 24 bytes of the vector register. Without this check, do_dup selects TCG_TYPE_V128 and clears only 16 bytes. Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 20180705191929.30773-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- tcg/tcg-op-gvec.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 22db1590d5..61c25f5784 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -287,8 +287,11 @@ void tcg_gen_gvec_4_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, in units of LNSZ. This limits the expansion of inline code. */ static inline bool check_size_impl(uint32_t oprsz, uint32_t lnsz) { - uint32_t lnct = oprsz / lnsz; - return lnct >= 1 && lnct <= MAX_UNROLL; + if (oprsz % lnsz == 0) { + uint32_t lnct = oprsz / lnsz; + return lnct >= 1 && lnct <= MAX_UNROLL; + } + return false; } static void expand_clr(uint32_t dofs, uint32_t maxsz); From 973558a3f869e591d2406dd8226ec0c4e32a3c3e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Jul 2018 14:51:34 +0100 Subject: [PATCH 2139/2380] target/arm: Fix do_predset for large VL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use MAKE_64BIT_MASK instead of open-coding. Remove an odd vector size check that is unlikely to be more profitable than 3 64-bit integer stores. Correct the iteration for WORD to avoid writing too much data. Fixes RISU tests of PTRUE for VL 256. Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 20180705191929.30773-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/translate-sve.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index d41f1155f9..374051cd20 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -1438,7 +1438,7 @@ static bool do_predset(DisasContext *s, int esz, int rd, int pat, bool setflag) setsz = numelem << esz; lastword = word = pred_esz_masks[esz]; if (setsz % 64) { - lastword &= ~(-1ull << (setsz % 64)); + lastword &= MAKE_64BIT_MASK(0, setsz % 64); } } @@ -1457,19 +1457,13 @@ static bool do_predset(DisasContext *s, int esz, int rd, int pat, bool setflag) tcg_gen_gvec_dup64i(ofs, oprsz, maxsz, word); goto done; } - if (oprsz * 8 == setsz + 8) { - tcg_gen_gvec_dup64i(ofs, oprsz, maxsz, word); - tcg_gen_movi_i64(t, 0); - tcg_gen_st_i64(t, cpu_env, ofs + oprsz - 8); - goto done; - } } setsz /= 8; fullsz /= 8; tcg_gen_movi_i64(t, word); - for (i = 0; i < setsz; i += 8) { + for (i = 0; i < QEMU_ALIGN_DOWN(setsz, 8); i += 8) { tcg_gen_st_i64(t, cpu_env, ofs + i); } if (lastword != word) { From 7abf56eed13a2338b8d3eda60e0d3c4e490a663f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Jul 2018 14:51:34 +0100 Subject: [PATCH 2140/2380] hw/sd/omap_mmc: Split 'pseudo-reset' from 'power-on-reset' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceClass::reset models a "cold power-on" reset which can also be used to powercycle a device; but there is no "hot reset" (a.k.a. soft-reset) method available. The OMAP MMC Power-Up Control bit is not designed to powercycle a card, but to disable it without powering it off (pseudo-reset): Multimedia Card (MMC/SD/SDIO) Interface [SPRU765A] MMC_CON[11] Power-Up Control (POW) This bit must be set to 1 before any valid transaction to either MMC/SD or SPI memory cards. When 1, the card is considered powered-up and the controller core is enabled. When 0, the card is considered powered-down (system dependent), and the controller core logic is in pseudo-reset state. This is, the MMC_STAT flags and the FIFO pointers are reset, any access to MMC_DATA[DATA] has no effect, a write into the MMC.CMD register is ignored, and a setting of MMC_SPI[STR] to 1 is ignored. By splitting the 'pseudo-reset' code out of the 'power-on' reset function, this patch fixes a latent bug in omap_mmc_write(MMC_CON)i recently exposed by ecd219f7abb. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180706162155.8432-2-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sd/omap_mmc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 671264b650..d0c98ca021 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -1,6 +1,8 @@ /* * OMAP on-chip MMC/SD host emulation. * + * Datasheet: TI Multimedia Card (MMC/SD/SDIO) Interface (SPRU765A) + * * Copyright (C) 2006-2007 Andrzej Zaborowski * * This program is free software; you can redistribute it and/or @@ -278,6 +280,12 @@ static void omap_mmc_update(void *opaque) omap_mmc_interrupts_update(s); } +static void omap_mmc_pseudo_reset(struct omap_mmc_s *host) +{ + host->status = 0; + host->fifo_len = 0; +} + void omap_mmc_reset(struct omap_mmc_s *host) { host->last_cmd = 0; @@ -286,11 +294,9 @@ void omap_mmc_reset(struct omap_mmc_s *host) host->dw = 0; host->mode = 0; host->enable = 0; - host->status = 0; host->mask = 0; host->cto = 0; host->dto = 0; - host->fifo_len = 0; host->blen = 0; host->blen_counter = 0; host->nblk = 0; @@ -305,6 +311,8 @@ void omap_mmc_reset(struct omap_mmc_s *host) qemu_set_irq(host->coverswitch, host->cdet_state); host->clkdiv = 0; + omap_mmc_pseudo_reset(host); + /* Since we're still using the legacy SD API the card is not plugged * into any bus, and we must reset it manually. When omap_mmc is * QOMified this must move into the QOM reset function. @@ -459,7 +467,7 @@ static void omap_mmc_write(void *opaque, hwaddr offset, if (s->dw != 0 && s->lines < 4) printf("4-bit SD bus enabled\n"); if (!s->enable) - omap_mmc_reset(s); + omap_mmc_pseudo_reset(s); break; case 0x10: /* MMC_STAT */ From 5bd366b467fd23d0530580573b2024d3962222c3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 9 Jul 2018 14:51:34 +0100 Subject: [PATCH 2141/2380] boards.h: Remove doc comment reference to nonexistent function commit b08199c6fbea1 accidentally added a reference to a doc comment to a nonexistent memory_region_allocate_aux_memory(). This was a leftover from a previous version of the patchset which defined memory_region_allocate_aux_memory() for "allocate RAM MemoryRegion and register it for migration" and left "memory_region_init_ram()" with its original semantics of "allocate RAM MR but do not register for migration". In the end we decided on the approach of "memory_region_init_ram() registers the MR for migration, and memory_region_init_ram_nomigrate() is a new function which does not", but this comment change got left in by mistake. Revert that part of the commit. Reported-by: Thomas Huth Signed-off-by: Peter Maydell Message-id: 20180702130605.13611-1-peter.maydell@linaro.org --- include/hw/boards.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/hw/boards.h b/include/hw/boards.h index 79069ddcbe..d139a431a6 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -35,8 +35,7 @@ * * Smaller pieces of memory (display RAM, static RAMs, etc) don't need * to be backed via the -mem-path memory backend and can simply - * be created via memory_region_allocate_aux_memory() or - * memory_region_init_ram(). + * be created via memory_region_init_ram(). */ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, const char *name, From 8fad0a65582c0a6e324580f45516461e9b6aa439 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 9 Jul 2018 14:51:35 +0100 Subject: [PATCH 2142/2380] hw/net/dp8393x: don't make prom region 'nomigrate' Currently we use memory_region_init_rom_nomigrate() to create the "dp3893x-prom" memory region, and we don't manually register it with vmstate_register_ram(). This currently means that its contents are migrated but as a ram block whose name is the empty string; in future it may mean they are not migrated at all. Use memory_region_init_ram() instead. Note that this is a a cross-version migration compatibility break for the MIPS "magnum" and "pica61" machines. Signed-off-by: Peter Maydell Reviewed-by: Aleksandar Markovic Message-id: 20180706174309.27110-1-peter.maydell@linaro.org --- hw/net/dp8393x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index f2d2ce344c..b53fcaa8bc 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -887,7 +887,7 @@ static void dp8393x_realize(DeviceState *dev, Error **errp) s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - memory_region_init_ram_nomigrate(&s->prom, OBJECT(dev), + memory_region_init_ram(&s->prom, OBJECT(dev), "dp8393x-prom", SONIC_PROM_SIZE, &local_err); if (local_err) { error_propagate(errp, local_err); From ec7eb2ae77cc207064e17ace048f7ec0c4b85d6f Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Thu, 5 Jul 2018 12:07:17 -0400 Subject: [PATCH 2143/2380] translate-all: honour CF_NOCACHE in tb_gen_code This fixes a record-replay regression introduced by 95590e2 ("translate-all: discard TB when tb_link_page returns an existing matching TB", 2018-06-15). The problem is that code using CF_NOCACHE assumes that the TB returned from tb_gen_code is always a newly-generated one. This assumption, however, was broken in the aforementioned commit. Fix it by honouring CF_NOCACHE, so that tb_gen_code always returns a newly-generated TB when CF_NOCACHE is passed to it. Do this by avoiding the TB hash table if CF_NOCACHE is set. Reported-by: Pavel Dovgalyuk Tested-by: Pavel Dovgalyuk Signed-off-by: Emilio G. Cota Reviewed-by: Richard Henderson Tested-by: Alistair Francis Message-id: 1530806837-5416-1-git-send-email-cota@braap.org Signed-off-by: Peter Maydell --- accel/tcg/translate-all.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 170b95793f..49d77fad44 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1446,7 +1446,8 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb_cflags(tb) & CF_HASH_MASK, tb->trace_vcpu_dstate); - if (!qht_remove(&tb_ctx.htable, tb, h)) { + if (!(tb->cflags & CF_NOCACHE) && + !qht_remove(&tb_ctx.htable, tb, h)) { return; } @@ -1604,8 +1605,6 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, { PageDesc *p; PageDesc *p2 = NULL; - void *existing_tb = NULL; - uint32_t h; assert_memory_lock(); @@ -1625,20 +1624,25 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, tb->page_addr[1] = -1; } - /* add in the hash table */ - h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, - tb->trace_vcpu_dstate); - qht_insert(&tb_ctx.htable, tb, h, &existing_tb); + if (!(tb->cflags & CF_NOCACHE)) { + void *existing_tb = NULL; + uint32_t h; - /* remove TB from the page(s) if we couldn't insert it */ - if (unlikely(existing_tb)) { - tb_page_remove(p, tb); - invalidate_page_bitmap(p); - if (p2) { - tb_page_remove(p2, tb); - invalidate_page_bitmap(p2); + /* add in the hash table */ + h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, + tb->trace_vcpu_dstate); + qht_insert(&tb_ctx.htable, tb, h, &existing_tb); + + /* remove TB from the page(s) if we couldn't insert it */ + if (unlikely(existing_tb)) { + tb_page_remove(p, tb); + invalidate_page_bitmap(p); + if (p2) { + tb_page_remove(p2, tb); + invalidate_page_bitmap(p2); + } + tb = existing_tb; } - tb = existing_tb; } if (p2 && p2 != p) { From 97e50dd013fbf2643a3b3f693a72159de5d25b50 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 2 Jul 2018 22:10:26 -0300 Subject: [PATCH 2144/2380] pc: Fix typo on PC_COMPAT_2_12 I forgot a hyphen when amending the compat code on commit e0051647 ("i386: Enable TOPOEXT feature on AMD EPYC CPU"). Fixes: e00516475c270dcb6705753da96063f95699abf2 Signed-off-by: Eduardo Habkost Message-Id: <20180703011026.18650-1-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- include/hw/i386/pc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 4d99d69681..654003f44c 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -309,7 +309,7 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .property = "xlevel",\ .value = stringify(0x8000000a),\ },{\ - .driver = "EPYC-IBPB" TYPE_X86_CPU,\ + .driver = "EPYC-IBPB-" TYPE_X86_CPU,\ .property = "xlevel",\ .value = stringify(0x8000000a),\ }, From be0e3d7a1e148180fe1b89c7f93720db0607614e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 1 Jun 2018 16:55:52 -0700 Subject: [PATCH 2145/2380] target/sh4: Fix translator.c assertion failure for gUSA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The translator loop does not allow the tb_start hook to set dc->base.is_jmp; the only hook allowed to do that is translate_insn. Split the work between init_disas_context where we validate the gUSA parameters, and translate_insn where we emit code. Tested-by: Alex Bennée Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- target/sh4/translate.c | 81 +++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index c716b74a0f..1b9a201d6d 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -1895,35 +1895,18 @@ static void decode_opc(DisasContext * ctx) any sequence via cpu_exec_step_atomic, we can recognize the "normal" sequences and transform them into atomic operations as seen by the host. */ -static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) +static void decode_gusa(DisasContext *ctx, CPUSH4State *env) { uint16_t insns[5]; int ld_adr, ld_dst, ld_mop; int op_dst, op_src, op_opc; int mv_src, mt_dst, st_src, st_mop; TCGv op_arg; - uint32_t pc = ctx->base.pc_next; uint32_t pc_end = ctx->base.tb->cs_base; - int backup = sextract32(ctx->tbflags, GUSA_SHIFT, 8); int max_insns = (pc_end - pc) / 2; int i; - if (pc != pc_end + backup || max_insns < 2) { - /* This is a malformed gUSA region. Don't do anything special, - since the interpreter is likely to get confused. */ - ctx->envflags &= ~GUSA_MASK; - return 0; - } - - if (ctx->tbflags & GUSA_EXCLUSIVE) { - /* Regardless of single-stepping or the end of the page, - we must complete execution of the gUSA region while - holding the exclusive lock. */ - *pmax_insns = max_insns; - return 0; - } - /* The state machine below will consume only a few insns. If there are more than that in a region, fail now. */ if (max_insns > ARRAY_SIZE(insns)) { @@ -2140,7 +2123,6 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) /* * Emit the operation. */ - tcg_gen_insn_start(pc, ctx->envflags); switch (op_opc) { case -1: /* No operation found. Look for exchange pattern. */ @@ -2235,7 +2217,8 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) /* The entire region has been translated. */ ctx->envflags &= ~GUSA_MASK; ctx->base.pc_next = pc_end; - return max_insns; + ctx->base.num_insns += max_insns - 1; + return; fail: qemu_log_mask(LOG_UNIMP, "Unrecognized gUSA sequence %08x-%08x\n", @@ -2243,7 +2226,6 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) /* Restart with the EXCLUSIVE bit set, within a TB run via cpu_exec_step_atomic holding the exclusive lock. */ - tcg_gen_insn_start(pc, ctx->envflags); ctx->envflags |= GUSA_EXCLUSIVE; gen_save_cpu_state(ctx, false); gen_helper_exclusive(cpu_env); @@ -2254,7 +2236,7 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) entire region consumed via ctx->base.pc_next so that it's immediately available in the disassembly dump. */ ctx->base.pc_next = pc_end; - return 1; + ctx->base.num_insns += max_insns - 1; } #endif @@ -2262,19 +2244,39 @@ static void sh4_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUSH4State *env = cs->env_ptr; + uint32_t tbflags; int bound; - ctx->tbflags = (uint32_t)ctx->base.tb->flags; - ctx->envflags = ctx->base.tb->flags & TB_FLAG_ENVFLAGS_MASK; - ctx->memidx = (ctx->tbflags & (1u << SR_MD)) == 0 ? 1 : 0; + ctx->tbflags = tbflags = ctx->base.tb->flags; + ctx->envflags = tbflags & TB_FLAG_ENVFLAGS_MASK; + ctx->memidx = (tbflags & (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 */ ctx->features = env->features; - ctx->has_movcal = (ctx->tbflags & TB_FLAG_PENDING_MOVCA); - ctx->gbank = ((ctx->tbflags & (1 << SR_MD)) && - (ctx->tbflags & (1 << SR_RB))) * 0x10; - ctx->fbank = ctx->tbflags & FPSCR_FR ? 0x10 : 0; + ctx->has_movcal = (tbflags & TB_FLAG_PENDING_MOVCA); + ctx->gbank = ((tbflags & (1 << SR_MD)) && + (tbflags & (1 << SR_RB))) * 0x10; + ctx->fbank = tbflags & FPSCR_FR ? 0x10 : 0; + + if (tbflags & GUSA_MASK) { + uint32_t pc = ctx->base.pc_next; + uint32_t pc_end = ctx->base.tb->cs_base; + int backup = sextract32(ctx->tbflags, GUSA_SHIFT, 8); + int max_insns = (pc_end - pc) / 2; + + if (pc != pc_end + backup || max_insns < 2) { + /* This is a malformed gUSA region. Don't do anything special, + since the interpreter is likely to get confused. */ + ctx->envflags &= ~GUSA_MASK; + } else if (tbflags & GUSA_EXCLUSIVE) { + /* Regardless of single-stepping or the end of the page, + we must complete execution of the gUSA region while + holding the exclusive lock. */ + ctx->base.max_insns = max_insns; + return; + } + } /* Since the ISA is fixed-width, we can bound by the number of instructions remaining on the page. */ @@ -2284,14 +2286,6 @@ static void sh4_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) static void sh4_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) { -#ifdef CONFIG_USER_ONLY - DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUSH4State *env = cs->env_ptr; - - if (ctx->tbflags & GUSA_MASK) { - ctx->base.num_insns = decode_gusa(ctx, env, &ctx->base.max_insns); - } -#endif } static void sh4_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) @@ -2323,6 +2317,19 @@ static void sh4_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) CPUSH4State *env = cs->env_ptr; DisasContext *ctx = container_of(dcbase, DisasContext, base); +#ifdef CONFIG_USER_ONLY + if (unlikely(ctx->envflags & GUSA_MASK) + && !(ctx->envflags & GUSA_EXCLUSIVE)) { + /* We're in an gUSA region, and we have not already fallen + back on using an exclusive region. Attempt to parse the + region into a single supported atomic operation. Failure + is handled within the parser by raising an exception to + retry using an exclusive region. */ + decode_gusa(ctx, env); + return; + } +#endif + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); decode_opc(ctx); ctx->base.pc_next += 2; From 2ff4f67cabbefb1842b06611e4f79857fc42925a Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 2 Jul 2018 11:41:52 +0200 Subject: [PATCH 2146/2380] machine: properly free device_memory Machines might have inititalized device_memory if they support memory devices, so let's properly free it. Signed-off-by: David Hildenbrand Message-Id: <20180702094152.7882-1-david@redhat.com> Reviewed-by: Igor Mammedov Signed-off-by: Eduardo Habkost --- hw/core/machine.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index 2077328bcc..3fad6f8801 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -674,6 +674,7 @@ static void machine_finalize(Object *obj) g_free(ms->dumpdtb); g_free(ms->dt_compatible); g_free(ms->firmware); + g_free(ms->device_memory); } bool machine_usb(MachineState *machine) From 7747abf114874537e898c60ff863828a4e82201d Mon Sep 17 00:00:00 2001 From: Dou Liyang Date: Wed, 4 Jul 2018 21:22:39 +0800 Subject: [PATCH 2147/2380] hw/machine: Remove the Zero check of nb_numa_nodes for numa_complete_configuration() Commit 7a3099fc9c5c("numa: postpone options post-processing till machine_run_board_init()") broke the commit 7b8be49d36fc("NUMA: Enable adding NUMA node implicitly"). The machine_run_board_init() doesn't do NUMA setup if nb_numa_nodes=0, but the numa_complete_configuration need add a new node if memory hotplug is enabled (slots > 0) even nb_numa_nodes=0. So, Remove the check for numa_complete_configuration() to fix this. Fixes 7a3099fc9c5c("numa: postpone options post-processing till machine_run_board_init()") Signed-off-by: Dou Liyang Message-Id: <20180704132239.6506-1-douly.fnst@cn.fujitsu.com> Reviewed-by: Eduardo Habkost Signed-off-by: Eduardo Habkost --- hw/core/machine.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 3fad6f8801..a9aeb22f03 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -792,10 +792,9 @@ void machine_run_board_init(MachineState *machine) { MachineClass *machine_class = MACHINE_GET_CLASS(machine); - if (nb_numa_nodes) { - numa_complete_configuration(machine); + numa_complete_configuration(machine); + if (nb_numa_nodes) machine_numa_finish_cpu_init(machine); - } /* If the machine supports the valid_cpu_types check and the user * specified a CPU with -cpu check here that the user CPU is supported. From 148546c8222e922c5dcc652b81cb97ea2ad4fd5b Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Mon, 2 Jul 2018 10:58:33 +0800 Subject: [PATCH 2148/2380] qcow2: Drop unused cluster_data Reported-by: Max Reitz Signed-off-by: Fam Zheng Message-id: 20180702025836.20957-2-famz@redhat.com Reviewed-by: Stefan Hajnoczi Signed-off-by: Max Reitz --- block/qcow2.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 33b61b7480..d4ba0f781a 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3345,7 +3345,6 @@ qcow2_co_copy_range_to(BlockDriverState *bs, int ret; unsigned int cur_bytes; /* number of sectors in current iteration */ uint64_t cluster_offset; - uint8_t *cluster_data = NULL; QCowL2Meta *l2meta = NULL; assert(!bs->encrypted); @@ -3404,7 +3403,6 @@ fail: qemu_co_mutex_unlock(&s->lock); - qemu_vfree(cluster_data); trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); return ret; From 9f850f67adc8d8b333cceae023a6462e184848cd Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Mon, 2 Jul 2018 10:58:34 +0800 Subject: [PATCH 2149/2380] file-posix: Fix fd_open check in raw_co_copy_range_to One of them is a typo. But update both to be more readable. Reported-by: Kevin Wolf Signed-off-by: Fam Zheng Message-id: 20180702025836.20957-3-famz@redhat.com Reviewed-by: Stefan Hajnoczi Signed-off-by: Max Reitz --- block/file-posix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/file-posix.c b/block/file-posix.c index 98987b80f1..349f77a3af 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2611,7 +2611,7 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, } src_s = src->bs->opaque; - if (fd_open(bs) < 0 || fd_open(bs) < 0) { + if (fd_open(src->bs) < 0 || fd_open(dst->bs) < 0) { return -EIO; } return paio_submit_co_full(bs, src_s->fd, src_offset, s->fd, dst_offset, From 65f1899e9bcd4faa147a5d907dc13fd7dbbbc0c7 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Mon, 2 Jul 2018 10:58:35 +0800 Subject: [PATCH 2150/2380] qcow2: Drop unreachable break Reported-by: Max Reitz Signed-off-by: Fam Zheng Message-id: 20180702025836.20957-4-famz@redhat.com Reviewed-by: Stefan Hajnoczi Signed-off-by: Max Reitz --- block/qcow2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/block/qcow2.c b/block/qcow2.c index d4ba0f781a..f327116e52 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3299,7 +3299,6 @@ qcow2_co_copy_range_from(BlockDriverState *bs, case QCOW2_CLUSTER_COMPRESSED: ret = -ENOTSUP; goto out; - break; case QCOW2_CLUSTER_NORMAL: child = bs->file; From 6d6bcc46b552c67aa14f25e69d518684b3e59361 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Mon, 2 Jul 2018 10:58:36 +0800 Subject: [PATCH 2151/2380] raw: Drop superfluous semicolon Reported-by: Max Reitz Signed-off-by: Fam Zheng Message-id: 20180702025836.20957-5-famz@redhat.com Reviewed-by: Stefan Hajnoczi Signed-off-by: Max Reitz --- block/raw-format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/raw-format.c b/block/raw-format.c index b78da564d4..8e648a5666 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -177,7 +177,7 @@ static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset, /* There's not enough space for the write, or the read request is * out-of-range. Don't read/write anything to prevent leaking out of * the size specified in options. */ - return is_write ? -ENOSPC : -EINVAL;; + return is_write ? -ENOSPC : -EINVAL; } if (*offset > INT64_MAX - s->offset) { From 439e89fc09ab6070f9613c4c513a4d4f133b23b7 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 2 Jul 2018 23:07:20 +0200 Subject: [PATCH 2152/2380] vmdk: Fix possible segfault with non-VMDK backing VMDK performs a probing check in vmdk_co_create_opts() to prevent the user from assigning non-VMDK files as a backing file, because it only supports VMDK backing files. However, with the @backing runtime option, it is possible to assign arbitrary nodes as backing nodes, regardless of what the image header says. Therefore, VMDK may not just access backing nodes assuming they are VMDK nodes -- which it does, because it needs to compare the backing file's CID with the overlay's parentCID value, and naturally the backing file only has a CID when it's a VMDK file. Instead, it should report the CID of non-VMDK backing files not to match the overlay because clearly a non-present CID does not match. Without this change, vmdk_read_cid() reads from the backing file's bs->file, which may be NULL (in which case we get a segfault). Also, it interprets bs->opaque as a BDRVVmdkState and then reads from the .desc_offset field, which usually will just return some arbitrary value which then results in either garbage to be read, or bdrv_pread() to return an error, both of which result in a non-matching CID to be reported. (In a very unlikely case, we could read something that looks like a VMDK descriptor, and then get a CID which might actually match. But that is highly unlikely, and the only result would be that VMDK accepts the backing file which is not too bad (albeit unintentional).) ((And in theory, the seek to .desc_offset might leak data from another block driver's opaque object. But then again, the user should realize very quickly that a non-VMDK backing file does not work (because the read will very likely fail, due to the reasons given above), so this should not be exploitable.)) Signed-off-by: Max Reitz Message-id: 20180702210721.4847-2-mreitz@redhat.com Reviewed-by: Fam Zheng Signed-off-by: Max Reitz --- block/vmdk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/block/vmdk.c b/block/vmdk.c index 84f8bbe480..a9d0084e36 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -333,6 +333,12 @@ static int vmdk_is_cid_valid(BlockDriverState *bs) if (!s->cid_checked && bs->backing) { BlockDriverState *p_bs = bs->backing->bs; + if (strcmp(p_bs->drv->format_name, "vmdk")) { + /* Backing file is not in vmdk format, so it does not have + * a CID, which makes the overlay's parent CID invalid */ + return 0; + } + if (vmdk_read_cid(p_bs, 0, &cur_pcid) != 0) { /* read failure: report as not valid */ return 0; From 85456e0d16434ffe5b33f1354cd893180f2fb235 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 2 Jul 2018 23:07:21 +0200 Subject: [PATCH 2153/2380] iotests: Add VMDK backing file correlation test This new test verifies that VMDK backing file reads fail when the backing file has a non-matching CID. This includes non-VMDK backing files. Signed-off-by: Max Reitz Message-id: 20180702210721.4847-3-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/225 | 132 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/225.out | 24 +++++++ tests/qemu-iotests/group | 1 + 3 files changed, 157 insertions(+) create mode 100755 tests/qemu-iotests/225 create mode 100644 tests/qemu-iotests/225.out diff --git a/tests/qemu-iotests/225 b/tests/qemu-iotests/225 new file mode 100755 index 0000000000..f2ee715685 --- /dev/null +++ b/tests/qemu-iotests/225 @@ -0,0 +1,132 @@ +#!/bin/bash +# +# Test vmdk backing file correlation +# +# Copyright (C) 2018 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 . +# + +# creator +owner=mreitz@redhat.com + +seq=$(basename $0) +echo "QA output created by $seq" + +here=$PWD +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_IMG.not_base" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +# This tests vmdk-specific low-level functionality +_supported_fmt vmdk +_supported_proto file +_supported_os Linux +_unsupported_imgopts "subformat=monolithicFlat" \ + "subformat=twoGbMaxExtentFlat" \ + "subformat=twoGbMaxExtentSparse" + +TEST_IMG="$TEST_IMG.base" _make_test_img 1M +TEST_IMG="$TEST_IMG.not_base" _make_test_img 1M +_make_test_img -b "$TEST_IMG.base" + +make_opts() +{ + node_name=$1 + filename=$2 + backing=$3 + + if [ -z "$backing" ]; then + backing="null" + else + backing="'$backing'" + fi + + echo "{ 'node-name': '$node_name', + 'driver': 'vmdk', + 'file': { + 'driver': 'file', + 'filename': '$filename' + }, + 'backing': $backing }" +} + +overlay_opts=$(make_opts overlay "$TEST_IMG" backing) +base_opts=$(make_opts backing "$TEST_IMG.base") +not_base_opts=$(make_opts backing "$TEST_IMG.not_base") + +not_vmdk_opts="{ 'node-name': 'backing', 'driver': 'null-co' }" + +echo +echo '=== Testing fitting VMDK backing image ===' +echo + +qemu_comm_method=monitor \ + _launch_qemu -blockdev "$base_opts" -blockdev "$overlay_opts" + +# Should not return an error +_send_qemu_cmd $QEMU_HANDLE 'qemu-io overlay "read 0 512"' 'ops' + +_cleanup_qemu + + +echo +echo '=== Testing unrelated VMDK backing image ===' +echo + +qemu_comm_method=monitor \ + _launch_qemu -blockdev "$not_base_opts" -blockdev "$overlay_opts" + +# Should fail (gracefully) +_send_qemu_cmd $QEMU_HANDLE 'qemu-io overlay "read 0 512"' 'failed' + +_cleanup_qemu + + +echo +echo '=== Testing non-VMDK backing image ===' +echo + +# FIXME: This is the reason why we have to use two -blockdev +# invocations. You can only fully override the backing file options +# if you either specify a node reference (as done here) or the new +# options contain file.filename (which in this case they do not). +# In other cases, file.filename will be set to whatever the image +# header of the overlay contains (which we do not want). I consider +# this a FIXME because with -blockdev, you cannot specify "partial" +# options, so setting file.filename but leaving the rest as specified +# by the user does not make sense. +qemu_comm_method=monitor \ + _launch_qemu -blockdev "$not_vmdk_opts" -blockdev "$overlay_opts" + +# Should fail (gracefully) +_send_qemu_cmd $QEMU_HANDLE 'qemu-io overlay "read 0 512"' 'failed' + +_cleanup_qemu + + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/225.out b/tests/qemu-iotests/225.out new file mode 100644 index 0000000000..4dc8ee282f --- /dev/null +++ b/tests/qemu-iotests/225.out @@ -0,0 +1,24 @@ +QA output created by 225 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT.not_base', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base + +=== Testing fitting VMDK backing image === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io overlay "read 0 512" +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing unrelated VMDK backing image === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io overlay "read 0 512" +read failed: Invalid argument + +=== Testing non-VMDK backing image === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io overlay "read 0 512" +read failed: Invalid argument +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index af309ebba7..1c9f679821 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -222,3 +222,4 @@ 221 rw auto quick 222 rw auto quick 223 rw auto quick +225 rw auto quick From 0e4e4318eaa56c831001bdf617094807ec6d451c Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 5 Jul 2018 18:15:15 +0300 Subject: [PATCH 2154/2380] qcow2: add overlap check for bitmap directory Signed-off-by: Vladimir Sementsov-Ogievskiy Message-id: 20180705151515.779173-1-vsementsov@virtuozzo.com Signed-off-by: Max Reitz --- block/qcow2-bitmap.c | 7 ++++++- block/qcow2-refcount.c | 10 ++++++++++ block/qcow2.c | 22 ++++++++++++++-------- block/qcow2.h | 41 ++++++++++++++++++++++------------------- qapi/block-core.json | 21 ++++++++++++--------- 5 files changed, 64 insertions(+), 37 deletions(-) diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 69485aa1de..ba978ad2aa 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -775,7 +775,12 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, } } - ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, dir_size); + /* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not + * necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating bitmap + * directory in-place (actually, turn-off the extension), which is checked + * in qcow2_check_metadata_overlap() */ + ret = qcow2_pre_write_overlap_check( + bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size); if (ret < 0) { goto fail; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 18c729aa27..1b9ecb1ca0 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -2705,6 +2705,16 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, } } + if ((chk & QCOW2_OL_BITMAP_DIRECTORY) && + (s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS)) + { + if (overlaps_with(s->bitmap_directory_offset, + s->bitmap_directory_size)) + { + return QCOW2_OL_BITMAP_DIRECTORY; + } + } + return 0; } diff --git a/block/qcow2.c b/block/qcow2.c index f327116e52..5d668fc617 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -679,6 +679,11 @@ static QemuOptsList qcow2_runtime_opts = { .type = QEMU_OPT_BOOL, .help = "Check for unintended writes into an inactive L2 table", }, + { + .name = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, + .type = QEMU_OPT_BOOL, + .help = "Check for unintended writes into the bitmap directory", + }, { .name = QCOW2_OPT_CACHE_SIZE, .type = QEMU_OPT_SIZE, @@ -712,14 +717,15 @@ static QemuOptsList qcow2_runtime_opts = { }; static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = { - [QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER, - [QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1, - [QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2, - [QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, - [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, - [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, - [QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1, - [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2, + [QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER, + [QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1, + [QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2, + [QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, + [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, + [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, + [QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1, + [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2, + [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, }; static void cache_clean_timer_cb(void *opaque) diff --git a/block/qcow2.h b/block/qcow2.h index d6aca687d6..81b844e936 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -94,6 +94,7 @@ #define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table" #define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1" #define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" +#define QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY "overlap-check.bitmap-directory" #define QCOW2_OPT_CACHE_SIZE "cache-size" #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" #define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size" @@ -400,34 +401,36 @@ typedef enum QCow2ClusterType { } QCow2ClusterType; typedef enum QCow2MetadataOverlap { - QCOW2_OL_MAIN_HEADER_BITNR = 0, - QCOW2_OL_ACTIVE_L1_BITNR = 1, - QCOW2_OL_ACTIVE_L2_BITNR = 2, - QCOW2_OL_REFCOUNT_TABLE_BITNR = 3, - QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4, - QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, - QCOW2_OL_INACTIVE_L1_BITNR = 6, - QCOW2_OL_INACTIVE_L2_BITNR = 7, + QCOW2_OL_MAIN_HEADER_BITNR = 0, + QCOW2_OL_ACTIVE_L1_BITNR = 1, + QCOW2_OL_ACTIVE_L2_BITNR = 2, + QCOW2_OL_REFCOUNT_TABLE_BITNR = 3, + QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4, + QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, + QCOW2_OL_INACTIVE_L1_BITNR = 6, + QCOW2_OL_INACTIVE_L2_BITNR = 7, + QCOW2_OL_BITMAP_DIRECTORY_BITNR = 8, - QCOW2_OL_MAX_BITNR = 8, + QCOW2_OL_MAX_BITNR = 9, - QCOW2_OL_NONE = 0, - QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR), - QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR), - QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR), - QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR), - QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR), - QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR), - QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR), + QCOW2_OL_NONE = 0, + QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR), + QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR), + QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR), + QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR), + QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR), + QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR), + QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR), /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv * reads. */ - QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR), + QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR), + QCOW2_OL_BITMAP_DIRECTORY = (1 << QCOW2_OL_BITMAP_DIRECTORY_BITNR), } QCow2MetadataOverlap; /* Perform all overlap checks which can be done in constant time */ #define QCOW2_OL_CONSTANT \ (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \ - QCOW2_OL_SNAPSHOT_TABLE) + QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_BITMAP_DIRECTORY) /* Perform all overlap checks which don't require disk access */ #define QCOW2_OL_CACHED \ diff --git a/qapi/block-core.json b/qapi/block-core.json index 38b31250f9..13798b982d 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2696,18 +2696,21 @@ # @template: Specifies a template mode which can be adjusted using the other # flags, defaults to 'cached' # +# @bitmap-directory: since 3.0 +# # Since: 2.9 ## { 'struct': 'Qcow2OverlapCheckFlags', - 'data': { '*template': 'Qcow2OverlapCheckMode', - '*main-header': 'bool', - '*active-l1': 'bool', - '*active-l2': 'bool', - '*refcount-table': 'bool', - '*refcount-block': 'bool', - '*snapshot-table': 'bool', - '*inactive-l1': 'bool', - '*inactive-l2': 'bool' } } + 'data': { '*template': 'Qcow2OverlapCheckMode', + '*main-header': 'bool', + '*active-l1': 'bool', + '*active-l2': 'bool', + '*refcount-table': 'bool', + '*refcount-block': 'bool', + '*snapshot-table': 'bool', + '*inactive-l1': 'bool', + '*inactive-l2': 'bool', + '*bitmap-directory': 'bool' } } ## # @Qcow2OverlapChecks: From 4be6a6d118123340f16fb8b3bf45220d0f8ed6d4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 29 Jun 2018 18:01:31 +0200 Subject: [PATCH 2155/2380] block: Poll after drain on attaching a node Commit dcf94a23b1 ('block: Don't poll in parent drain callbacks') removed polling in bdrv_child_cb_drained_begin() on the grounds that the original bdrv_drain() already will poll and BdrvChildRole.drained_begin calls must not cause graph changes (and therefore must not call aio_poll() or the recursion through the graph will break. This reasoning is correct for calls through bdrv_do_drained_begin(). However, BdrvChildRole.drained_begin is also called when a node that is already in a drained section (i.e. bdrv_do_drained_begin() has already returned and therefore can't poll any more) is attached to a new parent. In this case, we must explicitly poll to have all requests completed before the drained new child can be attached to the parent. In bdrv_replace_child_noperm(), we know that we're not inside the recursion of bdrv_do_drained_begin() because graph changes are not allowed there, and bdrv_replace_child_noperm() is a graph change. The call of BdrvChildRole.drained_begin() must therefore be followed by a BDRV_POLL_WHILE() that waits for the completion of requests. Reported-by: Max Reitz Signed-off-by: Kevin Wolf --- block.c | 2 +- block/io.c | 26 ++++++++++++++++++++------ include/block/block.h | 8 ++++++++ include/block/block_int.h | 3 +++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/block.c b/block.c index ac8b3a3511..a2fe05ea96 100644 --- a/block.c +++ b/block.c @@ -2066,7 +2066,7 @@ static void bdrv_replace_child_noperm(BdrvChild *child, } assert(num >= 0); for (i = 0; i < num; i++) { - child->role->drained_begin(child); + bdrv_parent_drained_begin_single(child, true); } } diff --git a/block/io.c b/block/io.c index 1a2272fad3..038449f81f 100644 --- a/block/io.c +++ b/block/io.c @@ -52,9 +52,7 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { continue; } - if (c->role->drained_begin) { - c->role->drained_begin(c); - } + bdrv_parent_drained_begin_single(c, false); } } @@ -73,6 +71,14 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, } } +static bool bdrv_parent_drained_poll_single(BdrvChild *c) +{ + if (c->role->drained_poll) { + return c->role->drained_poll(c); + } + return false; +} + static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, bool ignore_bds_parents) { @@ -83,14 +89,22 @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { continue; } - if (c->role->drained_poll) { - busy |= c->role->drained_poll(c); - } + busy |= bdrv_parent_drained_poll_single(c); } return busy; } +void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll) +{ + if (c->role->drained_begin) { + c->role->drained_begin(c); + } + if (poll) { + BDRV_POLL_WHILE(c->bs, bdrv_parent_drained_poll_single(c)); + } +} + static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) { dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer); diff --git a/include/block/block.h b/include/block/block.h index bc76b1e59f..706ef009ad 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -568,6 +568,14 @@ void bdrv_io_unplug(BlockDriverState *bs); void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, bool ignore_bds_parents); +/** + * bdrv_parent_drained_begin_single: + * + * Begin a quiesced section for the parent of @c. If @poll is true, wait for + * any pending activity to cease. + */ +void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll); + /** * bdrv_parent_drained_end: * diff --git a/include/block/block_int.h b/include/block/block_int.h index af71b414be..81cd3db7a9 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -606,6 +606,9 @@ struct BdrvChildRole { * requests after returning from .drained_begin() until .drained_end() is * called. * + * These functions must not change the graph (and therefore also must not + * call aio_poll(), which could change the graph indirectly). + * * Note that this can be nested. If drained_begin() was called twice, new * I/O is allowed only after drained_end() was called twice, too. */ From b994c5bc515fe611885113e7cfa7e87817bfd4e2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 3 Jul 2018 17:55:16 +0200 Subject: [PATCH 2156/2380] test-bdrv-drain: Test bdrv_append() to drained node Signed-off-by: Kevin Wolf --- tests/test-bdrv-drain.c | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 291a050f86..17bb8508ae 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -1250,6 +1250,47 @@ static void test_detach_by_driver_cb(void) test_detach_indirect(false); } +static void test_append_to_drained(void) +{ + BlockBackend *blk; + BlockDriverState *base, *overlay; + BDRVTestState *base_s, *overlay_s; + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + base = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); + base_s = base->opaque; + blk_insert_bs(blk, base, &error_abort); + + overlay = bdrv_new_open_driver(&bdrv_test, "overlay", BDRV_O_RDWR, + &error_abort); + overlay_s = overlay->opaque; + + do_drain_begin(BDRV_DRAIN, base); + g_assert_cmpint(base->quiesce_counter, ==, 1); + g_assert_cmpint(base_s->drain_count, ==, 1); + g_assert_cmpint(base->in_flight, ==, 0); + + /* Takes ownership of overlay, so we don't have to unref it later */ + bdrv_append(overlay, base, &error_abort); + g_assert_cmpint(base->in_flight, ==, 0); + g_assert_cmpint(overlay->in_flight, ==, 0); + + g_assert_cmpint(base->quiesce_counter, ==, 1); + g_assert_cmpint(base_s->drain_count, ==, 1); + g_assert_cmpint(overlay->quiesce_counter, ==, 1); + g_assert_cmpint(overlay_s->drain_count, ==, 1); + + do_drain_end(BDRV_DRAIN, base); + + g_assert_cmpint(base->quiesce_counter, ==, 0); + g_assert_cmpint(base_s->drain_count, ==, 0); + g_assert_cmpint(overlay->quiesce_counter, ==, 0); + g_assert_cmpint(overlay_s->drain_count, ==, 0); + + bdrv_unref(base); + blk_unref(blk); +} + int main(int argc, char **argv) { int ret; @@ -1308,6 +1349,8 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); g_test_add_func("/bdrv-drain/detach/driver_cb", test_detach_by_driver_cb); + g_test_add_func("/bdrv-drain/attach/drain", test_append_to_drained); + ret = g_test_run(); qemu_event_destroy(&done_event); return ret; From b0ddcbbb36a66a605eb232b905cb49b1cc72e74e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 6 Jul 2018 18:41:07 +0200 Subject: [PATCH 2157/2380] block: Fix copy-on-read crash with partial final cluster If the virtual disk size isn't aligned to full clusters, bdrv_co_do_copy_on_readv() may get pnum == 0 before having the full cluster completed, which will let it run into an assertion failure: qemu-io: block/io.c:1203: bdrv_co_do_copy_on_readv: Assertion `skip_bytes < pnum' failed. Check for EOF, assert that we read at least as much as the read request originally wanted to have (which is true at EOF because otherwise bdrv_check_byte_request() would already have returned an error) and return success early even though we couldn't copy the full cluster. Signed-off-by: Kevin Wolf --- block/io.c | 6 ++++++ tests/qemu-iotests/197 | 9 +++++++++ tests/qemu-iotests/197.out | 8 ++++++++ 3 files changed, 23 insertions(+) diff --git a/block/io.c b/block/io.c index 038449f81f..4c0831149c 100644 --- a/block/io.c +++ b/block/io.c @@ -1200,6 +1200,12 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, pnum = MIN(cluster_bytes, max_transfer); } + /* Stop at EOF if the image ends in the middle of the cluster */ + if (ret == 0 && pnum == 0) { + assert(progress >= bytes); + break; + } + assert(skip_bytes < pnum); if (ret <= 0) { diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 index 3ae4975eec..0369aa5cff 100755 --- a/tests/qemu-iotests/197 +++ b/tests/qemu-iotests/197 @@ -109,6 +109,15 @@ $QEMU_IO -f qcow2 -c map "$TEST_WRAP" _check_test_img $QEMU_IMG compare -f $IMGFMT -F qcow2 "$TEST_IMG" "$TEST_WRAP" +echo +echo '=== Partial final cluster ===' +echo + +_make_test_img 1024 +$QEMU_IO -f $IMGFMT -C -c 'read 0 1024' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -f $IMGFMT -c map "$TEST_IMG" +_check_test_img + # success, all done echo '*** done' status=0 diff --git a/tests/qemu-iotests/197.out b/tests/qemu-iotests/197.out index 52b4137d7b..8febda5dea 100644 --- a/tests/qemu-iotests/197.out +++ b/tests/qemu-iotests/197.out @@ -23,4 +23,12 @@ can't open device TEST_DIR/t.wrap.qcow2: Can't use copy-on-read on read-only dev 1023.938 MiB (0x3fff0000) bytes not allocated at offset 3 GiB (0xc0010000) No errors were found on the image. Images are identical. + +=== Partial final cluster === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 +read 1024/1024 bytes at offset 0 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) +No errors were found on the image. *** done From 2b83714d4ea659899069a4b94aa2dfadc847a013 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 9 Jul 2018 13:45:35 +0100 Subject: [PATCH 2158/2380] target/arm: Use correct mmu_idx for exception-return unstacking For M-profile exception returns, the mmu index to use for exception return unstacking is supposed to be that of wherever we are returning to: * if returning to handler mode, privileged * if returning to thread mode, privileged or unprivileged depending on CONTROL.nPRIV for the destination security state We were passing the wrong thing as the 'priv' argument to arm_v7m_mmu_idx_for_secstate_and_priv(). The effect was that guests which programmed the MPU to behave differently for privileged and unprivileged code could get spurious MemManage Unstack exceptions. Reported-by: Adithya Baglody Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180709124535.1116-1-peter.maydell@linaro.org --- target/arm/helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index a2ac96084e..0604a0efbe 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7171,9 +7171,11 @@ static void do_v7m_exception_exit(ARMCPU *cpu) uint32_t frameptr = *frame_sp_p; bool pop_ok = true; ARMMMUIdx mmu_idx; + bool return_to_priv = return_to_handler || + !(env->v7m.control[return_to_secure] & R_V7M_CONTROL_NPRIV_MASK); mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, return_to_secure, - !return_to_handler); + return_to_priv); if (!QEMU_IS_ALIGNED(frameptr, 8) && arm_feature(env, ARM_FEATURE_V8)) { From e79c4cd1909c05a2cab6517a9c00445bd2d880a6 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 17:38:02 +0800 Subject: [PATCH 2159/2380] iotests: 222: Don't run with luks Luks needs special parameters to operate the image. Since this test is focusing on image fleecing, skip skip that format. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- tests/qemu-iotests/222 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222 index ff3bfc1470..0ead56d574 100644 --- a/tests/qemu-iotests/222 +++ b/tests/qemu-iotests/222 @@ -25,6 +25,8 @@ import iotests from iotests import log, qemu_img, qemu_io, qemu_io_silent iotests.verify_platform(['linux']) +iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', + 'vhdx', 'raw']) patterns = [("0x5d", "0", "64k"), ("0xd5", "1M", "64k"), From 999658a05e61a8d87b65827da665302bb44ce8c9 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 9 Jul 2018 19:37:16 +0300 Subject: [PATCH 2160/2380] block/io: fix copy_range Here two things are fixed: 1. Architecture On each recursion step, we go to the child of src or dst, only for one of them. So, it's wrong to create tracked requests for both on each step. It leads to tracked requests duplication. 2. Wait for serializing requests on write path independently of BDRV_REQ_NO_SERIALISING Before commit 9ded4a01149 "backup: Use copy offloading", BDRV_REQ_NO_SERIALISING was used for only one case: read in copy-on-write operation during backup. Also, the flag was handled only on read path (in bdrv_co_preadv and bdrv_aligned_preadv). After 9ded4a01149, flag is used for not waiting serializing operations on backup target (in same case of copy-on-write operation). This behavior change is unsubstantiated and potentially dangerous, let's drop it and add additional asserts and documentation. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/io.c | 42 +++++++++++++++++++++++++++--------------- include/block/block.h | 12 ++++++++++++ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/block/io.c b/block/io.c index 4c0831149c..3a321d69d3 100644 --- a/block/io.c +++ b/block/io.c @@ -1592,6 +1592,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), align); + /* BDRV_REQ_NO_SERIALISING is only for read operation */ + assert(!(flags & BDRV_REQ_NO_SERIALISING)); waited = wait_serialising_requests(req); assert(!waited || !req->serialising); assert(req->overlap_offset <= offset); @@ -2916,7 +2918,7 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, BdrvRequestFlags flags, bool recurse_src) { - BdrvTrackedRequest src_req, dst_req; + BdrvTrackedRequest req; int ret; if (!dst || !dst->bs) { @@ -2943,32 +2945,42 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, || src->bs->encrypted || dst->bs->encrypted) { return -ENOTSUP; } - bdrv_inc_in_flight(src->bs); - bdrv_inc_in_flight(dst->bs); - tracked_request_begin(&src_req, src->bs, src_offset, - bytes, BDRV_TRACKED_READ); - tracked_request_begin(&dst_req, dst->bs, dst_offset, - bytes, BDRV_TRACKED_WRITE); - if (!(flags & BDRV_REQ_NO_SERIALISING)) { - wait_serialising_requests(&src_req); - wait_serialising_requests(&dst_req); - } if (recurse_src) { + bdrv_inc_in_flight(src->bs); + tracked_request_begin(&req, src->bs, src_offset, bytes, + BDRV_TRACKED_READ); + + if (!(flags & BDRV_REQ_NO_SERIALISING)) { + wait_serialising_requests(&req); + } + ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, src, src_offset, dst, dst_offset, bytes, flags); + + tracked_request_end(&req); + bdrv_dec_in_flight(src->bs); } else { + bdrv_inc_in_flight(dst->bs); + tracked_request_begin(&req, dst->bs, dst_offset, bytes, + BDRV_TRACKED_WRITE); + + /* BDRV_REQ_NO_SERIALISING is only for read operation, + * so we ignore it in flags. + */ + wait_serialising_requests(&req); + ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, src, src_offset, dst, dst_offset, bytes, flags); + + tracked_request_end(&req); + bdrv_dec_in_flight(dst->bs); } - tracked_request_end(&src_req); - tracked_request_end(&dst_req); - bdrv_dec_in_flight(src->bs); - bdrv_dec_in_flight(dst->bs); + return ret; } diff --git a/include/block/block.h b/include/block/block.h index 706ef009ad..f7ddff45b6 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -50,6 +50,18 @@ typedef enum { * opened with BDRV_O_UNMAP. */ BDRV_REQ_MAY_UNMAP = 0x4, + + /* + * The BDRV_REQ_NO_SERIALISING flag is only valid for reads and means that + * we don't want wait_serialising_requests() during the read operation. + * + * This flag is used for backup copy-on-write operations, when we need to + * read old data before write (write notifier triggered). It is okay since + * we already waited for other serializing requests in the initiating write + * (see bdrv_aligned_pwritev), and it is necessary if the initiating write + * is already serializing (without the flag, the read would deadlock + * waiting for the serialising write to complete). + */ BDRV_REQ_NO_SERIALISING = 0x8, BDRV_REQ_FUA = 0x10, BDRV_REQ_WRITE_COMPRESSED = 0x20, From 67b51fb998c697afb5d744066fcbde53e04fe941 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 9 Jul 2018 19:37:17 +0300 Subject: [PATCH 2161/2380] block: split flags in copy_range Pass read flags and write flags separately. This is needed to handle coming BDRV_REQ_NO_SERIALISING clearly in following patches. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/backup.c | 2 +- block/block-backend.c | 5 ++-- block/file-posix.c | 21 ++++++++++------ block/io.c | 46 ++++++++++++++++++---------------- block/iscsi.c | 9 ++++--- block/qcow2.c | 20 ++++++++------- block/raw-format.c | 24 ++++++++++++------ include/block/block.h | 3 ++- include/block/block_int.h | 14 ++++++++--- include/sysemu/block-backend.h | 3 ++- qemu-img.c | 2 +- 11 files changed, 90 insertions(+), 59 deletions(-) diff --git a/block/backup.c b/block/backup.c index 81895ddbe2..f3e4e814b6 100644 --- a/block/backup.c +++ b/block/backup.c @@ -163,7 +163,7 @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, hbitmap_reset(job->copy_bitmap, start / job->cluster_size, nr_clusters); ret = blk_co_copy_range(blk, start, job->target, start, nbytes, - is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); + is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0, 0); if (ret < 0) { trace_backup_do_cow_copy_range_fail(job, start, ret); hbitmap_set(job->copy_bitmap, start / job->cluster_size, diff --git a/block/block-backend.c b/block/block-backend.c index 6b75bca317..ac8c3e0b1c 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -2218,7 +2218,8 @@ void blk_unregister_buf(BlockBackend *blk, void *host) int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, BlockBackend *blk_out, int64_t off_out, - int bytes, BdrvRequestFlags flags) + int bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { int r; r = blk_check_byte_request(blk_in, off_in, bytes); @@ -2231,5 +2232,5 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, } return bdrv_co_copy_range(blk_in->root, off_in, blk_out->root, off_out, - bytes, flags); + bytes, read_flags, write_flags); } diff --git a/block/file-posix.c b/block/file-posix.c index 98987b80f1..4fec8cb53c 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2589,18 +2589,23 @@ static void raw_abort_perm_update(BlockDriverState *bs) raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); } -static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, - BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags) +static int coroutine_fn raw_co_copy_range_from( + BlockDriverState *bs, BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, + BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { - return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags); + return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, + read_flags, write_flags); } static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, - BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags) + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { BDRVRawState *s = bs->opaque; BDRVRawState *src_s; diff --git a/block/io.c b/block/io.c index 3a321d69d3..75ab26fd58 100644 --- a/block/io.c +++ b/block/io.c @@ -2910,13 +2910,11 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host) } } -static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, - uint64_t src_offset, - BdrvChild *dst, - uint64_t dst_offset, - uint64_t bytes, - BdrvRequestFlags flags, - bool recurse_src) +static int coroutine_fn bdrv_co_copy_range_internal( + BdrvChild *src, uint64_t src_offset, BdrvChild *dst, + uint64_t dst_offset, uint64_t bytes, + BdrvRequestFlags read_flags, BdrvRequestFlags write_flags, + bool recurse_src) { BdrvTrackedRequest req; int ret; @@ -2928,8 +2926,8 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, if (ret) { return ret; } - if (flags & BDRV_REQ_ZERO_WRITE) { - return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags); + if (write_flags & BDRV_REQ_ZERO_WRITE) { + return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags); } if (!src || !src->bs) { @@ -2951,14 +2949,15 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, tracked_request_begin(&req, src->bs, src_offset, bytes, BDRV_TRACKED_READ); - if (!(flags & BDRV_REQ_NO_SERIALISING)) { + if (!(read_flags & BDRV_REQ_NO_SERIALISING)) { wait_serialising_requests(&req); } ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, src, src_offset, dst, dst_offset, - bytes, flags); + bytes, + read_flags, write_flags); tracked_request_end(&req); bdrv_dec_in_flight(src->bs); @@ -2967,15 +2966,15 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, tracked_request_begin(&req, dst->bs, dst_offset, bytes, BDRV_TRACKED_WRITE); - /* BDRV_REQ_NO_SERIALISING is only for read operation, - * so we ignore it in flags. - */ + /* BDRV_REQ_NO_SERIALISING is only for read operation */ + assert(!(write_flags & BDRV_REQ_NO_SERIALISING)); wait_serialising_requests(&req); ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, src, src_offset, dst, dst_offset, - bytes, flags); + bytes, + read_flags, write_flags); tracked_request_end(&req); bdrv_dec_in_flight(dst->bs); @@ -2990,10 +2989,12 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, * semantics. */ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags) + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, - bytes, flags, true); + bytes, read_flags, write_flags, true); } /* Copy range from @src to @dst. @@ -3002,19 +3003,22 @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, * semantics. */ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags) + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, - bytes, flags, false); + bytes, read_flags, write_flags, false); } int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags) + uint64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { return bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, - bytes, flags); + bytes, read_flags, write_flags); } static void bdrv_parent_cb_resize(BlockDriverState *bs) diff --git a/block/iscsi.c b/block/iscsi.c index ead2bd5aa7..38b917a1e5 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2193,9 +2193,11 @@ static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, - BdrvRequestFlags flags) + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { - return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags); + return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, + read_flags, write_flags); } static struct scsi_task *iscsi_xcopy_task(int param_len) @@ -2332,7 +2334,8 @@ static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, - BdrvRequestFlags flags) + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { IscsiLun *dst_lun = dst->bs->opaque; IscsiLun *src_lun; diff --git a/block/qcow2.c b/block/qcow2.c index 33b61b7480..867ce02d50 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3253,13 +3253,14 @@ static int coroutine_fn qcow2_co_copy_range_from(BlockDriverState *bs, BdrvChild *src, uint64_t src_offset, BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags) + uint64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { BDRVQcow2State *s = bs->opaque; int ret; unsigned int cur_bytes; /* number of bytes in current iteration */ BdrvChild *child = NULL; - BdrvRequestFlags cur_flags; + BdrvRequestFlags cur_write_flags; assert(!bs->encrypted); qemu_co_mutex_lock(&s->lock); @@ -3268,7 +3269,7 @@ qcow2_co_copy_range_from(BlockDriverState *bs, uint64_t copy_offset = 0; /* prepare next request */ cur_bytes = MIN(bytes, INT_MAX); - cur_flags = flags; + cur_write_flags = write_flags; ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset); if (ret < 0) { @@ -3280,20 +3281,20 @@ qcow2_co_copy_range_from(BlockDriverState *bs, if (bs->backing && bs->backing->bs) { int64_t backing_length = bdrv_getlength(bs->backing->bs); if (src_offset >= backing_length) { - cur_flags |= BDRV_REQ_ZERO_WRITE; + cur_write_flags |= BDRV_REQ_ZERO_WRITE; } else { child = bs->backing; cur_bytes = MIN(cur_bytes, backing_length - src_offset); copy_offset = src_offset; } } else { - cur_flags |= BDRV_REQ_ZERO_WRITE; + cur_write_flags |= BDRV_REQ_ZERO_WRITE; } break; case QCOW2_CLUSTER_ZERO_PLAIN: case QCOW2_CLUSTER_ZERO_ALLOC: - cur_flags |= BDRV_REQ_ZERO_WRITE; + cur_write_flags |= BDRV_REQ_ZERO_WRITE; break; case QCOW2_CLUSTER_COMPRESSED: @@ -3317,7 +3318,7 @@ qcow2_co_copy_range_from(BlockDriverState *bs, ret = bdrv_co_copy_range_from(child, copy_offset, dst, dst_offset, - cur_bytes, cur_flags); + cur_bytes, read_flags, cur_write_flags); qemu_co_mutex_lock(&s->lock); if (ret < 0) { goto out; @@ -3338,7 +3339,8 @@ static int coroutine_fn qcow2_co_copy_range_to(BlockDriverState *bs, BdrvChild *src, uint64_t src_offset, BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags) + uint64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { BDRVQcow2State *s = bs->opaque; int offset_in_cluster; @@ -3382,7 +3384,7 @@ qcow2_co_copy_range_to(BlockDriverState *bs, ret = bdrv_co_copy_range_to(src, src_offset, bs->file, cluster_offset + offset_in_cluster, - cur_bytes, flags); + cur_bytes, read_flags, write_flags); qemu_co_mutex_lock(&s->lock); if (ret < 0) { goto fail; diff --git a/block/raw-format.c b/block/raw-format.c index b78da564d4..a3591985f6 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -498,9 +498,13 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) } static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, - BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags) + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { int ret; @@ -509,13 +513,17 @@ static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, return ret; } return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset, - bytes, flags); + bytes, read_flags, write_flags); } static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, - BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags) + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { int ret; @@ -524,7 +532,7 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, return ret; } return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes, - flags); + read_flags, write_flags); } BlockDriver bdrv_raw = { diff --git a/include/block/block.h b/include/block/block.h index f7ddff45b6..e474f2541b 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -699,5 +699,6 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host); **/ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags); + uint64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); #endif diff --git a/include/block/block_int.h b/include/block/block_int.h index 81cd3db7a9..920d3d122b 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -218,7 +218,8 @@ struct BlockDriver { BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, - BdrvRequestFlags flags); + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); /* Map [offset, offset + nbytes) range onto a child of bs to copy data to, * and invoke bdrv_co_copy_range_to(child, src, ...), or perform the copy @@ -234,7 +235,8 @@ struct BlockDriver { BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, - BdrvRequestFlags flags); + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); /* * Building block for bdrv_block_status[_above] and @@ -1156,10 +1158,14 @@ void blockdev_close_all_bdrv_states(void); int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags); + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags flags); + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); int refresh_total_sectors(BlockDriverState *bs, int64_t hint); diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 8d03d493c2..830d873f24 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -234,6 +234,7 @@ void blk_unregister_buf(BlockBackend *blk, void *host); int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, BlockBackend *blk_out, int64_t off_out, - int bytes, BdrvRequestFlags flags); + int bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); #endif diff --git a/qemu-img.c b/qemu-img.c index 7651d8172c..f4074ebf75 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1783,7 +1783,7 @@ static int coroutine_fn convert_co_copy_range(ImgConvertState *s, int64_t sector ret = blk_co_copy_range(blk, offset, s->target, sector_num << BDRV_SECTOR_BITS, - n << BDRV_SECTOR_BITS, 0); + n << BDRV_SECTOR_BITS, 0, 0); if (ret < 0) { return ret; } From 09d2f948462f4979d18f573a0734d1daae8e67a9 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 9 Jul 2018 19:37:18 +0300 Subject: [PATCH 2162/2380] block: add BDRV_REQ_SERIALISING flag Serialized writes should be used in copy-on-write of backup(sync=none) for image fleecing scheme. We need to change an assert in bdrv_aligned_pwritev, added in 28de2dcd88de. The assert may fail now, because call to wait_serialising_requests here may become first call to it for this request with serializing flag set. It occurs if the request is aligned (otherwise, we should already set serializing flag before calling bdrv_aligned_pwritev and correspondingly waited for all intersecting requests). However, for aligned requests, we should not care about outdating of previously read data, as there no such data. Therefore, let's just update an assert to not care about aligned requests. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/io.c | 28 +++++++++++++++++++++++++++- include/block/block.h | 14 +++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/block/io.c b/block/io.c index 75ab26fd58..6be9c40f23 100644 --- a/block/io.c +++ b/block/io.c @@ -637,6 +637,18 @@ static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes); } +static bool is_request_serialising_and_aligned(BdrvTrackedRequest *req) +{ + /* + * If the request is serialising, overlap_offset and overlap_bytes are set, + * so we can check if the request is aligned. Otherwise, don't care and + * return false. + */ + + return req->serialising && (req->offset == req->overlap_offset) && + (req->bytes == req->overlap_bytes); +} + /** * Round a region to cluster boundaries */ @@ -1311,6 +1323,9 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, mark_request_serialising(req, bdrv_get_cluster_size(bs)); } + /* BDRV_REQ_SERIALISING is only for write operation */ + assert(!(flags & BDRV_REQ_SERIALISING)); + if (!(flags & BDRV_REQ_NO_SERIALISING)) { wait_serialising_requests(req); } @@ -1594,8 +1609,14 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, /* BDRV_REQ_NO_SERIALISING is only for read operation */ assert(!(flags & BDRV_REQ_NO_SERIALISING)); + + if (flags & BDRV_REQ_SERIALISING) { + mark_request_serialising(req, bdrv_get_cluster_size(bs)); + } + waited = wait_serialising_requests(req); - assert(!waited || !req->serialising); + assert(!waited || !req->serialising || + is_request_serialising_and_aligned(req)); assert(req->overlap_offset <= offset); assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); if (flags & BDRV_REQ_WRITE_UNCHANGED) { @@ -2949,6 +2970,8 @@ static int coroutine_fn bdrv_co_copy_range_internal( tracked_request_begin(&req, src->bs, src_offset, bytes, BDRV_TRACKED_READ); + /* BDRV_REQ_SERIALISING is only for write operation */ + assert(!(read_flags & BDRV_REQ_SERIALISING)); if (!(read_flags & BDRV_REQ_NO_SERIALISING)) { wait_serialising_requests(&req); } @@ -2968,6 +2991,9 @@ static int coroutine_fn bdrv_co_copy_range_internal( /* BDRV_REQ_NO_SERIALISING is only for read operation */ assert(!(write_flags & BDRV_REQ_NO_SERIALISING)); + if (write_flags & BDRV_REQ_SERIALISING) { + mark_request_serialising(&req, bdrv_get_cluster_size(dst->bs)); + } wait_serialising_requests(&req); ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, diff --git a/include/block/block.h b/include/block/block.h index e474f2541b..a91f37bedf 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -70,8 +70,20 @@ typedef enum { * content. */ BDRV_REQ_WRITE_UNCHANGED = 0x40, + /* + * BDRV_REQ_SERIALISING forces request serialisation for writes. + * It is used to ensure that writes to the backing file of a backup process + * target cannot race with a read of the backup target that defers to the + * backing file. + * + * Note, that BDRV_REQ_SERIALISING is _not_ opposite in meaning to + * BDRV_REQ_NO_SERIALISING. A more descriptive name for the latter might be + * _DO_NOT_WAIT_FOR_SERIALISING, except that is too long. + */ + BDRV_REQ_SERIALISING = 0x80, + /* Mask of valid flags */ - BDRV_REQ_MASK = 0x7f, + BDRV_REQ_MASK = 0xff, } BdrvRequestFlags; typedef struct BlockSizes { From f8d59dfb40bbc6f5aeea57c8aac1e68c1d2454ee Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 9 Jul 2018 19:37:19 +0300 Subject: [PATCH 2163/2380] block/backup: fix fleecing scheme: use serialized writes Fleecing scheme works as follows: we want a kind of temporary snapshot of active drive A. We create temporary image B, with B->backing = A. Then we start backup(sync=none) from A to B. From this point, B reads as point-in-time snapshot of A (A continues to be active drive, accepting guest IO). This scheme needs some additional synchronization between reads from B and backup COW operations, otherwise, the following situation is theoretically possible: (assume B is qcow2, client is NBD client, reading from B) 1. client starts reading and take qcow2 mutex in qcow2_co_preadv, and goes up to l2 table loading (assume cache miss) 2) guest write => backup COW => qcow2 write => try to take qcow2 mutex => waiting 3. l2 table loaded, we see that cluster is UNALLOCATED, go to "case QCOW2_CLUSTER_UNALLOCATED" and unlock mutex before bdrv_co_preadv(bs->backing, ...) 4) aha, mutex unlocked, backup COW continues, and we finally finish guest write and change cluster in our active disk A 5. actually, do bdrv_co_preadv(bs->backing, ...) and read _new updated_ data. To avoid this, let's make backup writes serializing, to not intersect with reads from B. Note: we expand range of handled cases from (sync=none and B->backing = A) to just (A in backing chain of B), to finally allow safe reading from B during backup for all cases when A in backing chain of B, i.e. B formally looks like point-in-time snapshot of A. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/backup.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/block/backup.c b/block/backup.c index f3e4e814b6..319fc922e8 100644 --- a/block/backup.c +++ b/block/backup.c @@ -47,6 +47,8 @@ typedef struct BackupBlockJob { HBitmap *copy_bitmap; bool use_copy_range; int64_t copy_range_size; + + bool serialize_target_writes; } BackupBlockJob; static const BlockJobDriver backup_job_driver; @@ -102,6 +104,8 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, QEMUIOVector qiov; BlockBackend *blk = job->common.blk; int nbytes; + int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; + int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); nbytes = MIN(job->cluster_size, job->len - start); @@ -112,8 +116,7 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, iov.iov_len = nbytes; qemu_iovec_init_external(&qiov, &iov, 1); - ret = blk_co_preadv(blk, start, qiov.size, &qiov, - is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); + ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags); if (ret < 0) { trace_backup_do_cow_read_fail(job, start, ret); if (error_is_read) { @@ -124,11 +127,11 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, if (qemu_iovec_is_zero(&qiov)) { ret = blk_co_pwrite_zeroes(job->target, start, - qiov.size, BDRV_REQ_MAY_UNMAP); + qiov.size, write_flags | BDRV_REQ_MAY_UNMAP); } else { ret = blk_co_pwritev(job->target, start, - qiov.size, &qiov, - job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); + qiov.size, &qiov, write_flags | + (job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0)); } if (ret < 0) { trace_backup_do_cow_write_fail(job, start, ret); @@ -156,6 +159,8 @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, int nr_clusters; BlockBackend *blk = job->common.blk; int nbytes; + int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; + int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size)); nbytes = MIN(job->copy_range_size, end - start); @@ -163,7 +168,7 @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, hbitmap_reset(job->copy_bitmap, start / job->cluster_size, nr_clusters); ret = blk_co_copy_range(blk, start, job->target, start, nbytes, - is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0, 0); + read_flags, write_flags); if (ret < 0) { trace_backup_do_cow_copy_range_fail(job, start, ret); hbitmap_set(job->copy_bitmap, start / job->cluster_size, @@ -701,6 +706,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, sync_bitmap : NULL; job->compress = compress; + /* Detect image-fleecing (and similar) schemes */ + job->serialize_target_writes = bdrv_chain_contains(target, bs); + /* If there is no backing file on the target, we cannot rely on COW if our * backup cluster size is smaller than the target cluster size. Even for * targets with a backing file, try to avoid COW if possible. */ From 7769eaa57859362a655b3d3a78d20df7774f16cf Mon Sep 17 00:00:00 2001 From: Ari Sundholm Date: Fri, 6 Jul 2018 17:01:36 +0300 Subject: [PATCH 2164/2380] qapi/block-core.json: Add missing documentation for blklogwrites log-append option This was accidentally omitted. Thanks to Eric Blake for spotting this. Signed-off-by: Ari Sundholm Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- qapi/block-core.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qapi/block-core.json b/qapi/block-core.json index 38b31250f9..62a92fa4f4 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3057,6 +3057,8 @@ # @log-sector-size: sector size used in logging writes to @file, determines # granularity of offsets and sizes of writes (default: 512) # +# @log-append: append to an existing log (default: false) +# # @log-super-update-interval: interval of write requests after which the log # super block is updated to disk (default: 4096) # From ba814c82bbf0daf053c52dcdf0eb97b1f6ab0970 Mon Sep 17 00:00:00 2001 From: Ari Sundholm Date: Fri, 6 Jul 2018 15:00:38 +0300 Subject: [PATCH 2165/2380] block/blklogwrites: Make sure the log sector size is not too small The sector size needs to be large enough to accommodate the data structures for the log super block and log write entries. This was previously not properly checked, which made it possible to cause QEMU to badly misbehave. Signed-off-by: Ari Sundholm Signed-off-by: Kevin Wolf --- block/blklogwrites.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 63bf6b34a9..efa2c7a66a 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -89,7 +89,10 @@ static inline uint32_t blk_log_writes_log2(uint32_t value) static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size) { - return sector_size < (1ull << 24) && is_power_of_2(sector_size); + return is_power_of_2(sector_size) && + sector_size >= sizeof(struct log_write_super) && + sector_size >= sizeof(struct log_write_entry) && + sector_size < (1ull << 24); } static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log, From eed1cc7866ae35bd28926d75447dd6076bd6bb96 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 27 Jun 2018 21:22:43 +0800 Subject: [PATCH 2166/2380] migration: delay postcopy paused state Before this patch we firstly setup the postcopy-paused state then we clean up the QEMUFile handles. That can be racy if there is a very fast "migrate-recover" command running in parallel. Fix that up. Reported-by: Peter Maydell Reviewed-by: Juan Quintela Signed-off-by: Peter Xu Message-Id: <20180627132246.5576-2-peterx@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- migration/savevm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index c2f34ffc7c..851d74e8b6 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2194,9 +2194,6 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) /* Clear the triggered bit to allow one recovery */ mis->postcopy_recover_triggered = false; - migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, - MIGRATION_STATUS_POSTCOPY_PAUSED); - assert(mis->from_src_file); qemu_file_shutdown(mis->from_src_file); qemu_fclose(mis->from_src_file); @@ -2209,6 +2206,9 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) mis->to_src_file = NULL; qemu_mutex_unlock(&mis->rp_mutex); + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, + MIGRATION_STATUS_POSTCOPY_PAUSED); + /* Notify the fault thread for the invalidated file handle */ postcopy_fault_thread_notify(mis); From 81e620531fa842f760086964ca1b8657ae6c07ba Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 27 Jun 2018 21:22:44 +0800 Subject: [PATCH 2167/2380] migration: move income process out of multifd Move the call to migration_incoming_process() out of multifd code. It's a bit strange that we can migration generic calls in multifd code. Instead, let multifd_recv_new_channel() return a boolean showing whether it's ready to continue the incoming migration. Signed-off-by: Peter Xu Message-Id: <20180627132246.5576-3-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 5 ++++- migration/ram.c | 11 +++++------ migration/ram.h | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 94d71f8b24..aea6fb7444 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -507,7 +507,10 @@ void migration_ioc_process_incoming(QIOChannel *ioc) migration_incoming_setup(f); return; } - multifd_recv_new_channel(ioc); + + if (multifd_recv_new_channel(ioc)) { + migration_incoming_process(); + } } /** diff --git a/migration/ram.c b/migration/ram.c index 1cd98d6398..23cea47090 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1311,7 +1311,8 @@ bool multifd_recv_all_channels_created(void) return thread_count == atomic_read(&multifd_recv_state->count); } -void multifd_recv_new_channel(QIOChannel *ioc) +/* Return true if multifd is ready for the migration, otherwise false */ +bool multifd_recv_new_channel(QIOChannel *ioc) { MultiFDRecvParams *p; Error *local_err = NULL; @@ -1320,7 +1321,7 @@ void multifd_recv_new_channel(QIOChannel *ioc) id = multifd_recv_initial_packet(ioc, &local_err); if (id < 0) { multifd_recv_terminate_threads(local_err); - return; + return false; } p = &multifd_recv_state->params[id]; @@ -1328,7 +1329,7 @@ void multifd_recv_new_channel(QIOChannel *ioc) error_setg(&local_err, "multifd: received id '%d' already setup'", id); multifd_recv_terminate_threads(local_err); - return; + return false; } p->c = ioc; object_ref(OBJECT(ioc)); @@ -1339,9 +1340,7 @@ void multifd_recv_new_channel(QIOChannel *ioc) qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, QEMU_THREAD_JOINABLE); atomic_inc(&multifd_recv_state->count); - if (multifd_recv_state->count == migrate_multifd_channels()) { - migration_incoming_process(); - } + return multifd_recv_state->count == migrate_multifd_channels(); } /** diff --git a/migration/ram.h b/migration/ram.h index d386f4d641..457bf54b8c 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -46,7 +46,7 @@ int multifd_save_cleanup(Error **errp); int multifd_load_setup(void); int multifd_load_cleanup(Error **errp); bool multifd_recv_all_channels_created(void); -void multifd_recv_new_channel(QIOChannel *ioc); +bool multifd_recv_new_channel(QIOChannel *ioc); uint64_t ram_pagesize_summary(void); int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); From 884835fa1e38066e2596224375bb35ac6686be4d Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 27 Jun 2018 21:22:45 +0800 Subject: [PATCH 2168/2380] migration: unbreak postcopy recovery The whole postcopy recovery logic was accidentally broken. We need to fix it in two steps. This is the first step that we should do the recovery when needed. It was bypassed before after commit 36c2f8be2c. Introduce postcopy_try_recovery() helper for the postcopy recovery logic. Call it both in migration_fd_process_incoming() and migration_ioc_process_incoming(). Fixes: 36c2f8be2c ("migration: Delay start of migration main routines") Signed-off-by: Peter Xu Message-Id: <20180627132246.5576-4-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index aea6fb7444..eb3e09e899 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -466,7 +466,8 @@ void migration_incoming_process(void) qemu_coroutine_enter(co); } -void migration_fd_process_incoming(QEMUFile *f) +/* Returns true if recovered from a paused migration, otherwise false */ +static bool postcopy_try_recover(QEMUFile *f) { MigrationIncomingState *mis = migration_incoming_get_current(); @@ -491,11 +492,20 @@ void migration_fd_process_incoming(QEMUFile *f) * that source is ready to reply to page requests. */ qemu_sem_post(&mis->postcopy_pause_sem_dst); - } else { - /* New incoming migration */ - migration_incoming_setup(f); - migration_incoming_process(); + return true; } + + return false; +} + +void migration_fd_process_incoming(QEMUFile *f) +{ + if (postcopy_try_recover(f)) { + return; + } + + migration_incoming_setup(f); + migration_incoming_process(); } void migration_ioc_process_incoming(QIOChannel *ioc) @@ -504,6 +514,9 @@ void migration_ioc_process_incoming(QIOChannel *ioc) if (!mis->from_src_file) { QEMUFile *f = qemu_fopen_channel_input(ioc); + if (postcopy_try_recover(f)) { + return; + } migration_incoming_setup(f); return; } From a429e7f4887313370ed2d0d3607b7e6bdcfb0e1b Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 27 Jun 2018 21:22:46 +0800 Subject: [PATCH 2169/2380] migration: unify incoming processing This is the 2nd patch to unbreak postcopy recovery. Let's unify the migration_incoming_process() call at a single place rather than calling it in connection setup codes. This fixes a problem that we will go into incoming migration procedure even if we are trying to recovery from a paused postcopy migration. Fixes: 36c2f8be2c ("migration: Delay start of migration main routines") Signed-off-by: Peter Xu Message-Id: <20180627132246.5576-5-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/exec.c | 3 --- migration/fd.c | 3 --- migration/migration.c | 18 ++++++++++++++++-- migration/socket.c | 5 ----- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/migration/exec.c b/migration/exec.c index 0bbeb63c97..375d2e1b54 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -49,9 +49,6 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc, { migration_channel_process_incoming(ioc); object_unref(OBJECT(ioc)); - if (!migrate_use_multifd()) { - migration_incoming_process(); - } return G_SOURCE_REMOVE; } diff --git a/migration/fd.c b/migration/fd.c index fee34ffdc0..a7c13df4ad 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -49,9 +49,6 @@ static gboolean fd_accept_incoming_migration(QIOChannel *ioc, { migration_channel_process_incoming(ioc); object_unref(OBJECT(ioc)); - if (!migrate_use_multifd()) { - migration_incoming_process(); - } return G_SOURCE_REMOVE; } diff --git a/migration/migration.c b/migration/migration.c index eb3e09e899..0404c53215 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -511,17 +511,31 @@ void migration_fd_process_incoming(QEMUFile *f) void migration_ioc_process_incoming(QIOChannel *ioc) { MigrationIncomingState *mis = migration_incoming_get_current(); + bool start_migration; if (!mis->from_src_file) { + /* The first connection (multifd may have multiple) */ QEMUFile *f = qemu_fopen_channel_input(ioc); + + /* If it's a recovery, we're done */ if (postcopy_try_recover(f)) { return; } + migration_incoming_setup(f); - return; + + /* + * Common migration only needs one channel, so we can start + * right now. Multifd needs more than one channel, we wait. + */ + start_migration = !migrate_use_multifd(); + } else { + /* Multiple connections */ + assert(migrate_use_multifd()); + start_migration = multifd_recv_new_channel(ioc); } - if (multifd_recv_new_channel(ioc)) { + if (start_migration) { migration_incoming_process(); } } diff --git a/migration/socket.c b/migration/socket.c index 3456eb76e9..f4c8174400 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -168,12 +168,7 @@ static void socket_accept_incoming_migration(QIONetListener *listener, if (migration_has_all_channels()) { /* Close listening socket as its no longer needed */ qio_net_listener_disconnect(listener); - object_unref(OBJECT(listener)); - - if (!migrate_use_multifd()) { - migration_incoming_process(); - } } } From 1aa8367861645c3c0917f585fe14b1b6b23b0f66 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:18:53 +0800 Subject: [PATCH 2170/2380] migration: simplify check to use qemu file buffer Firstly, renaming the old matching_page_sizes variable to matches_target_page_size, which suites more to what it did (it only checks against target page size rather than multiple page sizes). Meanwhile, simplify the check logic a bit, and enhance the comments. Should have no functional change. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Peter Xu Message-Id: <20180710091902.28780-2-peterx@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 23cea47090..49068e86d3 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3580,7 +3580,7 @@ static int ram_load_postcopy(QEMUFile *f) { int flags = 0, ret = 0; bool place_needed = false; - bool matching_page_sizes = false; + bool matches_target_page_size = false; MigrationIncomingState *mis = migration_incoming_get_current(); /* Temporary page that is later 'placed' */ void *postcopy_host_page = postcopy_get_tmp_page(mis); @@ -3620,7 +3620,7 @@ static int ram_load_postcopy(QEMUFile *f) ret = -EINVAL; break; } - matching_page_sizes = block->page_size == TARGET_PAGE_SIZE; + matches_target_page_size = block->page_size == TARGET_PAGE_SIZE; /* * Postcopy requires that we place whole host pages atomically; * these may be huge pages for RAMBlocks that are backed by @@ -3668,12 +3668,17 @@ static int ram_load_postcopy(QEMUFile *f) case RAM_SAVE_FLAG_PAGE: all_zero = false; - if (!place_needed || !matching_page_sizes) { + if (!matches_target_page_size) { + /* For huge pages, we always use temporary buffer */ qemu_get_buffer(f, page_buffer, TARGET_PAGE_SIZE); } else { - /* Avoids the qemu_file copy during postcopy, which is - * going to do a copy later; can only do it when we - * do this read in one go (matching page sizes) + /* + * For small pages that matches target page size, we + * avoid the qemu_file copy. Instead we directly use + * the buffer of QEMUFile to place the page. Note: we + * cannot do any QEMUFile operation before using that + * buffer to make sure the buffer is valid when + * placing the page. */ qemu_get_buffer_in_place(f, (uint8_t **)&place_source, TARGET_PAGE_SIZE); From fd037a656aca23dc3c47aa8c9de261bec6b8aad0 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:18:54 +0800 Subject: [PATCH 2171/2380] migration: loosen recovery check when load vm We were checking against -EIO, assuming that it will cover all IO failures. But actually it is not. One example is that in qemu_loadvm_section_start_full() we can have tons of places that will return -EINVAL even if the error is caused by IO failures on the network. Let's loosen the recovery check logic here to cover all the error cases happened by removing the explicit check against -EIO. After all we won't lose anything here if any other failure happened. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Peter Xu Message-Id: <20180710091902.28780-3-peterx@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- migration/savevm.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 851d74e8b6..efcc795071 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2276,18 +2276,14 @@ out: qemu_file_set_error(f, ret); /* - * Detect whether it is: - * - * 1. postcopy running (after receiving all device data, which - * must be in POSTCOPY_INCOMING_RUNNING state. Note that - * POSTCOPY_INCOMING_LISTENING is still not enough, it's - * still receiving device states). - * 2. network failure (-EIO) - * - * If so, we try to wait for a recovery. + * If we are during an active postcopy, then we pause instead + * of bail out to at least keep the VM's dirty data. Note + * that POSTCOPY_INCOMING_LISTENING stage is still not enough, + * during which we're still receiving device states and we + * still haven't yet started the VM on destination. */ if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING && - ret == -EIO && postcopy_pause_incoming(mis)) { + postcopy_pause_incoming(mis)) { /* Reset f to point to the newly created channel */ f = mis->from_src_file; goto retry; From a725ef9fe36424351faf51696c3fc441ded13f35 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:18:55 +0800 Subject: [PATCH 2172/2380] migration: fix incorrect bitmap size calculation The calculation on size of received bitmap is incorrect for postcopy recovery. Here we wanted to let the size to cover all the valid bits in the bitmap, we should use DIV_ROUND_UP() instead of a division. For example, a RAMBlock with size=4K (which contains only one single 4K page) will have nbits=1, then nbits/8=0, then the real bitmap won't be sent to source at all. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Peter Xu Message-Id: <20180710091902.28780-4-peterx@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 49068e86d3..52dd678092 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -235,7 +235,7 @@ int64_t ramblock_recv_bitmap_send(QEMUFile *file, bitmap_to_le(le_bitmap, block->receivedmap, nbits); /* Size of the bitmap, in bytes */ - size = nbits / 8; + size = DIV_ROUND_UP(nbits, 8); /* * size is always aligned to 8 bytes for 64bit machines, but it @@ -3944,7 +3944,7 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) int ret = -EINVAL; QEMUFile *file = s->rp_state.from_dst_file; unsigned long *le_bitmap, nbits = block->used_length >> TARGET_PAGE_BITS; - uint64_t local_size = nbits / 8; + uint64_t local_size = DIV_ROUND_UP(nbits, 8); uint64_t size, end_mark; trace_ram_dirty_bitmap_reload_begin(block->idstr); From 3c9928d9f9b6b5717fa8e53e9441c6b041d6554a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:18:56 +0800 Subject: [PATCH 2173/2380] migration: show pause/recover state on dst host These two states will be missing when doing "query-migrate" on destination VM. Add these states so that we can get the query results as expected. Signed-off-by: Peter Xu Message-Id: <20180710091902.28780-5-peterx@redhat.com> Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index 0404c53215..8d56d56930 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -911,6 +911,8 @@ static void fill_destination_migration_info(MigrationInfo *info) case MIGRATION_STATUS_CANCELLED: case MIGRATION_STATUS_ACTIVE: case MIGRATION_STATUS_POSTCOPY_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER: case MIGRATION_STATUS_FAILED: case MIGRATION_STATUS_COLO: info->has_status = true; From 19a49c5637a3d7c2c61ba9d1149a4f6ee419988a Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 6 Jul 2018 15:06:17 +0200 Subject: [PATCH 2174/2380] Revert "block: Remove dead deprecation warning code" This reverts commit 6266e900b8083945cb766b45c124fb3c42932cb3. Some deprecated -drive options were still in use by libvirt, only fixed with libvirt commit b340c6c614 ("qemu: format serial and geometry on frontend disk device"), which is not yet in any released version of libvirt. So let's hold off removing the deprecated options for one more QEMU release. Reported-by: Christian Borntraeger Signed-off-by: Cornelia Huck Signed-off-by: Kevin Wolf --- blockdev.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/blockdev.c b/blockdev.c index 72f5347df5..37eb40670b 100644 --- a/blockdev.c +++ b/blockdev.c @@ -775,6 +775,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) const char *filename; Error *local_err = NULL; int i; + const char *deprecated[] = { + }; /* Change legacy command line options into QMP ones */ static const struct { @@ -851,6 +853,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) goto fail; } + /* Other deprecated options */ + if (!qtest_enabled()) { + for (i = 0; i < ARRAY_SIZE(deprecated); i++) { + if (qemu_opt_get(legacy_opts, deprecated[i]) != NULL) { + error_report("'%s' is deprecated, please use the corresponding " + "option of '-device' instead", deprecated[i]); + } + } + } + /* Media type */ value = qemu_opt_get(legacy_opts, "media"); if (value) { From 44e8b4689c6e3aba4df08a1201f02ac7bf3d2fdb Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 6 Jul 2018 15:06:18 +0200 Subject: [PATCH 2175/2380] Revert "block: Remove deprecated -drive option serial" This reverts commit b0083267444a5e0f28391f6c2831a539f878d424. Hold off removing this for one more QEMU release (current libvirt release still uses it.) Signed-off-by: Cornelia Huck Signed-off-by: Kevin Wolf --- block/block-backend.c | 1 + blockdev.c | 10 ++++++++++ hw/block/block.c | 13 +++++++++++++ hw/block/nvme.c | 1 + hw/block/virtio-blk.c | 1 + hw/ide/qdev.c | 1 + hw/scsi/scsi-disk.c | 1 + hw/usb/dev-storage.c | 1 + include/hw/block/block.h | 1 + include/sysemu/blockdev.h | 1 + qemu-doc.texi | 5 +++++ qemu-options.hx | 6 +++++- tests/ahci-test.c | 6 +++--- tests/ide-test.c | 8 ++++---- 14 files changed, 48 insertions(+), 8 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index ac8c3e0b1c..e94e3d5929 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -419,6 +419,7 @@ static void drive_info_del(DriveInfo *dinfo) return; } qemu_opts_del(dinfo->opts); + g_free(dinfo->serial); g_free(dinfo); } diff --git a/blockdev.c b/blockdev.c index 37eb40670b..6c530769fd 100644 --- a/blockdev.c +++ b/blockdev.c @@ -730,6 +730,10 @@ QemuOptsList qemu_legacy_drive_opts = { .name = "if", .type = QEMU_OPT_STRING, .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "disk serial number", },{ .name = "file", .type = QEMU_OPT_STRING, @@ -772,10 +776,12 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) const char *werror, *rerror; bool read_only = false; bool copy_on_read; + const char *serial; const char *filename; Error *local_err = NULL; int i; const char *deprecated[] = { + "serial" }; /* Change legacy command line options into QMP ones */ @@ -943,6 +949,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) goto fail; } + /* Serial number */ + serial = qemu_opt_get(legacy_opts, "serial"); + /* no id supplied -> create one */ if (qemu_opts_id(all_opts) == NULL) { char *new_id; @@ -1017,6 +1026,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) dinfo->type = type; dinfo->bus = bus_id; dinfo->unit = unit_id; + dinfo->serial = g_strdup(serial); blk_set_legacy_dinfo(blk, dinfo); diff --git a/hw/block/block.c b/hw/block/block.c index cf0eb826f1..b6c80ab0b7 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -15,6 +15,19 @@ #include "qapi/qapi-types-block.h" #include "qemu/error-report.h" +void blkconf_serial(BlockConf *conf, char **serial) +{ + DriveInfo *dinfo; + + if (!*serial) { + /* try to fall back to value set with legacy -drive serial=... */ + dinfo = blk_legacy_dinfo(conf->blk); + if (dinfo) { + *serial = g_strdup(dinfo->serial); + } + } +} + void blkconf_blocksizes(BlockConf *conf) { BlockBackend *blk = conf->blk; diff --git a/hw/block/nvme.c b/hw/block/nvme.c index fc7dacb816..5e508ab1b3 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -1217,6 +1217,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) return; } + blkconf_serial(&n->conf, &n->serial); if (!n->serial) { error_setg(errp, "serial property not set"); return; diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 225fe44b7a..50b5c869e3 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -935,6 +935,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) return; } + blkconf_serial(&conf->conf, &conf->serial); if (!blkconf_apply_backend_options(&conf->conf, blk_is_read_only(conf->conf.blk), true, errp)) { diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 573b022e1e..f395d24592 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -188,6 +188,7 @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp) return; } + blkconf_serial(&dev->conf, &dev->serial); if (kind != IDE_CD) { if (!blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, errp)) { diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 32f3f96ff8..d7df357029 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2378,6 +2378,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) return; } + blkconf_serial(&s->qdev.conf, &s->serial); blkconf_blocksizes(&s->qdev.conf); if (s->qdev.conf.logical_block_size > diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index cd5551d94f..45a9487cdb 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -599,6 +599,7 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) return; } + blkconf_serial(&s->conf, &dev->serial); blkconf_blocksizes(&s->conf); if (!blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true, errp)) { diff --git a/include/hw/block/block.h b/include/hw/block/block.h index e9f9e2223f..d4f4dfffab 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -72,6 +72,7 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) /* Configuration helpers */ +void blkconf_serial(BlockConf *conf, char **serial); bool blkconf_geometry(BlockConf *conf, int *trans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp); diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 24954b94e0..c0ae3700ec 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -35,6 +35,7 @@ struct DriveInfo { bool is_default; /* Added by default_drive() ? */ int media_cd; QemuOpts *opts; + char *serial; QTAILQ_ENTRY(DriveInfo) next; }; diff --git a/qemu-doc.texi b/qemu-doc.texi index d3924e928e..d343affd6d 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2887,6 +2887,11 @@ with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' (for embedded NICs). The new syntax allows different settings to be provided per NIC. +@subsection -drive serial=... (since 2.10.0) + +The drive serial argument is replaced by the the serial argument +that can be specified with the ``-device'' parameter. + @subsection -usbdevice (since 2.10.0) The ``-usbdevice DEV'' argument is now a synonym for setting diff --git a/qemu-options.hx b/qemu-options.hx index 16208f63f2..381648b9cb 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -805,7 +805,7 @@ ETEXI DEF("drive", HAS_ARG, QEMU_OPTION_drive, "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" - " [,snapshot=on|off][,rerror=ignore|stop|report]\n" + " [,snapshot=on|off][,serial=s][,rerror=ignore|stop|report]\n" " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" " [,readonly=on|off][,copy-on-read=on|off]\n" " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" @@ -879,6 +879,10 @@ The default mode is @option{cache=writeback}. Specify which disk @var{format} will be used rather than detecting the format. Can be used to specify format=raw to avoid interpreting an untrusted format header. +@item serial=@var{serial} +This option specifies the serial number to assign to the device. This +parameter is deprecated, use the corresponding parameter of @code{-device} +instead. @item werror=@var{action},rerror=@var{action} Specify which @var{action} to take on write and read errors. Valid actions are: "ignore" (ignore the error and try to continue), "stop" (pause QEMU), diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 937ed2f910..1a7b761304 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -180,12 +180,12 @@ static AHCIQState *ahci_boot(const char *cli, ...) s = ahci_vboot(cli, ap); va_end(ap); } else { - cli = "-drive if=none,id=drive0,file=%s,cache=writeback,format=%s" + cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" + ",format=%s" " -M q35 " "-device ide-hd,drive=drive0 " - "-global ide-hd.serial=%s " "-global ide-hd.ver=%s"; - s = ahci_boot(cli, tmp_path, imgfmt, "testdisk", "version"); + s = ahci_boot(cli, tmp_path, "testdisk", imgfmt, "version"); } return s; diff --git a/tests/ide-test.c b/tests/ide-test.c index f39431b1a9..2384c2c3e2 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -529,8 +529,8 @@ static void test_bmdma_no_busmaster(void) static void test_bmdma_setup(void) { ide_test_start( - "-drive file=%s,if=ide,cache=writeback,format=raw " - "-global ide-hd.serial=%s -global ide-hd.ver=%s", + "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " + "-global ide-hd.ver=%s", tmp_path, "testdisk", "version"); qtest_irq_intercept_in(global_qtest, "ioapic"); } @@ -561,8 +561,8 @@ static void test_identify(void) int ret; ide_test_start( - "-drive file=%s,if=ide,cache=writeback,format=raw " - "-global ide-hd.serial=%s -global ide-hd.ver=%s", + "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " + "-global ide-hd.ver=%s", tmp_path, "testdisk", "version"); dev = get_pci_device(&bmdma_bar, &ide_bar); From 75f4cd297922e1ac352625badf548d4a1bb96089 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 6 Jul 2018 15:06:19 +0200 Subject: [PATCH 2176/2380] Revert "block: Remove deprecated -drive option addr" This reverts commit eae3bd1eb7c6b105d30ec06008b3bc3dfc5f45bb. Reverted to avoid conflicts for geometry options revert. Signed-off-by: Cornelia Huck Signed-off-by: Kevin Wolf --- blockdev.c | 17 ++++++++++++++++- device-hotplug.c | 4 ++++ include/sysemu/blockdev.h | 1 + qemu-doc.texi | 5 +++++ qemu-options.hx | 5 ++++- 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/blockdev.c b/blockdev.c index 6c530769fd..c23587b075 100644 --- a/blockdev.c +++ b/blockdev.c @@ -730,6 +730,10 @@ QemuOptsList qemu_legacy_drive_opts = { .name = "if", .type = QEMU_OPT_STRING, .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", + },{ + .name = "addr", + .type = QEMU_OPT_STRING, + .help = "pci address (virtio only)", },{ .name = "serial", .type = QEMU_OPT_STRING, @@ -773,6 +777,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) DriveMediaType media = MEDIA_DISK; BlockInterfaceType type; int max_devs, bus_id, unit_id, index; + const char *devaddr; const char *werror, *rerror; bool read_only = false; bool copy_on_read; @@ -781,7 +786,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) Error *local_err = NULL; int i; const char *deprecated[] = { - "serial" + "serial", "addr" }; /* Change legacy command line options into QMP ones */ @@ -971,6 +976,12 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) } /* Add virtio block device */ + devaddr = qemu_opt_get(legacy_opts, "addr"); + if (devaddr && type != IF_VIRTIO) { + error_report("addr is not supported by this bus type"); + goto fail; + } + if (type == IF_VIRTIO) { QemuOpts *devopts; devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, @@ -982,6 +993,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) } qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), &error_abort); + if (devaddr) { + qemu_opt_set(devopts, "addr", devaddr, &error_abort); + } } filename = qemu_opt_get(legacy_opts, "file"); @@ -1026,6 +1040,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) dinfo->type = type; dinfo->bus = bus_id; dinfo->unit = unit_id; + dinfo->devaddr = devaddr; dinfo->serial = g_strdup(serial); blk_set_legacy_dinfo(blk, dinfo); diff --git a/device-hotplug.c b/device-hotplug.c index cd427e2c76..23fd6656f1 100644 --- a/device-hotplug.c +++ b/device-hotplug.c @@ -69,6 +69,10 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict) if (!dinfo) { goto err; } + if (dinfo->devaddr) { + monitor_printf(mon, "Parameter addr not supported\n"); + goto err; + } switch (dinfo->type) { case IF_NONE: diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index c0ae3700ec..37ea39719e 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -28,6 +28,7 @@ typedef enum { } BlockInterfaceType; struct DriveInfo { + const char *devaddr; BlockInterfaceType type; int bus; int unit; diff --git a/qemu-doc.texi b/qemu-doc.texi index d343affd6d..ae5531a053 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2892,6 +2892,11 @@ provided per NIC. The drive serial argument is replaced by the the serial argument that can be specified with the ``-device'' parameter. +@subsection -drive addr=... (since 2.10.0) + +The drive addr argument is replaced by the the addr argument +that can be specified with the ``-device'' parameter. + @subsection -usbdevice (since 2.10.0) The ``-usbdevice DEV'' argument is now a synonym for setting diff --git a/qemu-options.hx b/qemu-options.hx index 381648b9cb..df248d1568 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -805,7 +805,7 @@ ETEXI DEF("drive", HAS_ARG, QEMU_OPTION_drive, "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" - " [,snapshot=on|off][,serial=s][,rerror=ignore|stop|report]\n" + " [,snapshot=on|off][,serial=s][,addr=A][,rerror=ignore|stop|report]\n" " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" " [,readonly=on|off][,copy-on-read=on|off]\n" " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" @@ -883,6 +883,9 @@ an untrusted format header. This option specifies the serial number to assign to the device. This parameter is deprecated, use the corresponding parameter of @code{-device} instead. +@item addr=@var{addr} +Specify the controller's PCI address (if=virtio only). This parameter is +deprecated, use the corresponding parameter of @code{-device} instead. @item werror=@var{action},rerror=@var{action} Specify which @var{action} to take on write and read errors. Valid actions are: "ignore" (ignore the error and try to continue), "stop" (pause QEMU), From 6703db131f832d6af58fa629be11c5efa5a6adb8 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 6 Jul 2018 15:06:20 +0200 Subject: [PATCH 2177/2380] Revert "block: Remove deprecated -drive geometry options" This reverts commit a7aff6dd10b16b67e8b142d0c94c5d92c3fe88f6. Hold off removing this for one more QEMU release (current libvirt release still uses it.) Signed-off-by: Cornelia Huck Signed-off-by: Kevin Wolf --- blockdev.c | 75 ++++++++++++++++++++++++++++++++++++++- hmp-commands.hx | 1 + hw/block/block.c | 14 ++++++++ include/sysemu/blockdev.h | 1 + qemu-doc.texi | 5 +++ qemu-options.hx | 7 +++- tests/hd-geo-test.c | 37 +++++++++++++++---- 7 files changed, 131 insertions(+), 9 deletions(-) diff --git a/blockdev.c b/blockdev.c index c23587b075..dcf8c8d2ab 100644 --- a/blockdev.c +++ b/blockdev.c @@ -730,6 +730,22 @@ QemuOptsList qemu_legacy_drive_opts = { .name = "if", .type = QEMU_OPT_STRING, .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", + },{ + .name = "cyls", + .type = QEMU_OPT_NUMBER, + .help = "number of cylinders (ide disk geometry)", + },{ + .name = "heads", + .type = QEMU_OPT_NUMBER, + .help = "number of heads (ide disk geometry)", + },{ + .name = "secs", + .type = QEMU_OPT_NUMBER, + .help = "number of sectors (ide disk geometry)", + },{ + .name = "trans", + .type = QEMU_OPT_STRING, + .help = "chs translation (auto, lba, none)", },{ .name = "addr", .type = QEMU_OPT_STRING, @@ -776,6 +792,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) QemuOpts *legacy_opts; DriveMediaType media = MEDIA_DISK; BlockInterfaceType type; + int cyls, heads, secs, translation; int max_devs, bus_id, unit_id, index; const char *devaddr; const char *werror, *rerror; @@ -786,7 +803,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) Error *local_err = NULL; int i; const char *deprecated[] = { - "serial", "addr" + "serial", "trans", "secs", "heads", "cyls", "addr" }; /* Change legacy command line options into QMP ones */ @@ -915,6 +932,57 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) type = block_default_type; } + /* Geometry */ + cyls = qemu_opt_get_number(legacy_opts, "cyls", 0); + heads = qemu_opt_get_number(legacy_opts, "heads", 0); + secs = qemu_opt_get_number(legacy_opts, "secs", 0); + + if (cyls || heads || secs) { + if (cyls < 1) { + error_report("invalid physical cyls number"); + goto fail; + } + if (heads < 1) { + error_report("invalid physical heads number"); + goto fail; + } + if (secs < 1) { + error_report("invalid physical secs number"); + goto fail; + } + } + + translation = BIOS_ATA_TRANSLATION_AUTO; + value = qemu_opt_get(legacy_opts, "trans"); + if (value != NULL) { + if (!cyls) { + error_report("'%s' trans must be used with cyls, heads and secs", + value); + goto fail; + } + if (!strcmp(value, "none")) { + translation = BIOS_ATA_TRANSLATION_NONE; + } else if (!strcmp(value, "lba")) { + translation = BIOS_ATA_TRANSLATION_LBA; + } else if (!strcmp(value, "large")) { + translation = BIOS_ATA_TRANSLATION_LARGE; + } else if (!strcmp(value, "rechs")) { + translation = BIOS_ATA_TRANSLATION_RECHS; + } else if (!strcmp(value, "auto")) { + translation = BIOS_ATA_TRANSLATION_AUTO; + } else { + error_report("'%s' invalid translation type", value); + goto fail; + } + } + + if (media == MEDIA_CDROM) { + if (cyls || secs || heads) { + error_report("CHS can't be set with media=cdrom"); + goto fail; + } + } + /* Device address specified by bus/unit or index. * If none was specified, try to find the first free one. */ bus_id = qemu_opt_get_number(legacy_opts, "bus", 0); @@ -1037,6 +1105,11 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) dinfo = g_malloc0(sizeof(*dinfo)); dinfo->opts = all_opts; + dinfo->cyls = cyls; + dinfo->heads = heads; + dinfo->secs = secs; + dinfo->trans = translation; + dinfo->type = type; dinfo->bus = bus_id; dinfo->unit = unit_id; diff --git a/hmp-commands.hx b/hmp-commands.hx index c1fc747403..91dfe51c37 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1306,6 +1306,7 @@ ETEXI .params = "[-n] [[:]:]\n" "[file=file][,if=type][,bus=n]\n" "[,unit=m][,media=d][,index=i]\n" + "[,cyls=c,heads=h,secs=s[,trans=t]]\n" "[,snapshot=on|off][,cache=on|off]\n" "[,readonly=on|off][,copy-on-read=on|off]", .help = "add drive to PCI storage controller", diff --git a/hw/block/block.c b/hw/block/block.c index b6c80ab0b7..b91e2b6d7e 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -108,6 +108,20 @@ bool blkconf_geometry(BlockConf *conf, int *ptrans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp) { + DriveInfo *dinfo; + + if (!conf->cyls && !conf->heads && !conf->secs) { + /* try to fall back to value set with legacy -drive cyls=... */ + dinfo = blk_legacy_dinfo(conf->blk); + if (dinfo) { + conf->cyls = dinfo->cyls; + conf->heads = dinfo->heads; + conf->secs = dinfo->secs; + if (ptrans) { + *ptrans = dinfo->trans; + } + } + } if (!conf->cyls && !conf->heads && !conf->secs) { hd_geometry_guess(conf->blk, &conf->cyls, &conf->heads, &conf->secs, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 37ea39719e..ac22f2ae1f 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -35,6 +35,7 @@ struct DriveInfo { int auto_del; /* see blockdev_mark_auto_del() */ bool is_default; /* Added by default_drive() ? */ int media_cd; + int cyls, heads, secs, trans; QemuOpts *opts; char *serial; QTAILQ_ENTRY(DriveInfo) next; diff --git a/qemu-doc.texi b/qemu-doc.texi index ae5531a053..0fbf8b108b 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2887,6 +2887,11 @@ with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' (for embedded NICs). The new syntax allows different settings to be provided per NIC. +@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) + +The drive geometry arguments are replaced by the the geometry arguments +that can be specified with the ``-device'' parameter. + @subsection -drive serial=... (since 2.10.0) The drive serial argument is replaced by the the serial argument diff --git a/qemu-options.hx b/qemu-options.hx index df248d1568..654e69cc3b 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -804,8 +804,9 @@ ETEXI DEF("drive", HAS_ARG, QEMU_OPTION_drive, "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" + " [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n" " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" - " [,snapshot=on|off][,serial=s][,addr=A][,rerror=ignore|stop|report]\n" + " [,serial=s][,addr=A][,rerror=ignore|stop|report]\n" " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" " [,readonly=on|off][,copy-on-read=on|off]\n" " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" @@ -846,6 +847,10 @@ This option defines where is connected the drive by using an index in the list of available connectors of a given interface type. @item media=@var{media} This option defines the type of the media: disk or cdrom. +@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}] +Force disk physical geometry and the optional BIOS translation (trans=none or +lba). These parameters are deprecated, use the corresponding parameters +of @code{-device} instead. @item snapshot=@var{snapshot} @var{snapshot} is "on" or "off" and controls snapshot mode for the given drive (see @option{-snapshot}). diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c index ce665f1f83..24870b38f4 100644 --- a/tests/hd-geo-test.c +++ b/tests/hd-geo-test.c @@ -201,7 +201,7 @@ static void setup_mbr(int img_idx, MBRcontents mbr) static int setup_ide(int argc, char *argv[], int argv_sz, int ide_idx, const char *dev, int img_idx, - MBRcontents mbr) + MBRcontents mbr, const char *opts) { char *s1, *s2, *s3; @@ -216,7 +216,7 @@ static int setup_ide(int argc, char *argv[], int argv_sz, s3 = g_strdup(",media=cdrom"); } argc = append_arg(argc, argv, argv_sz, - g_strdup_printf("%s%s%s", s1, s2, s3)); + g_strdup_printf("%s%s%s%s", s1, s2, s3, opts)); g_free(s1); g_free(s2); g_free(s3); @@ -260,7 +260,7 @@ static void test_ide_mbr(bool use_device, MBRcontents mbr) for (i = 0; i < backend_last; i++) { cur_ide[i] = &hd_chst[i][mbr]; dev = use_device ? (is_hd(cur_ide[i]) ? "ide-hd" : "ide-cd") : NULL; - argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr); + argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr, ""); } args = g_strjoinv(" ", argv); qtest_start(args); @@ -327,12 +327,16 @@ static void test_ide_drive_user(const char *dev, bool trans) const CHST expected_chst = { secs / (4 * 32) , 4, 32, trans }; argc = setup_common(argv, ARGV_SIZE); - opts = g_strdup_printf("%s,%scyls=%d,heads=%d,secs=%d", - dev, trans ? "bios-chs-trans=lba," : "", + opts = g_strdup_printf("%s,%s%scyls=%d,heads=%d,secs=%d", + dev ?: "", + trans && dev ? "bios-chs-" : "", + trans ? "trans=lba," : "", expected_chst.cyls, expected_chst.heads, expected_chst.secs); cur_ide[0] = &expected_chst; - argc = setup_ide(argc, argv, ARGV_SIZE, 0, opts, backend_small, mbr_chs); + argc = setup_ide(argc, argv, ARGV_SIZE, + 0, dev ? opts : NULL, backend_small, mbr_chs, + dev ? "" : opts); g_free(opts); args = g_strjoinv(" ", argv); qtest_start(args); @@ -342,6 +346,22 @@ static void test_ide_drive_user(const char *dev, bool trans) qtest_end(); } +/* + * Test case: IDE device (if=ide) with explicit CHS + */ +static void test_ide_drive_user_chs(void) +{ + test_ide_drive_user(NULL, false); +} + +/* + * Test case: IDE device (if=ide) with explicit CHS and translation + */ +static void test_ide_drive_user_chst(void) +{ + test_ide_drive_user(NULL, true); +} + /* * Test case: IDE device (if=none) with explicit CHS */ @@ -372,7 +392,8 @@ static void test_ide_drive_cd_0(void) for (i = 0; i <= backend_empty; i++) { ide_idx = backend_empty - i; cur_ide[ide_idx] = &hd_chst[i][mbr_blank]; - argc = setup_ide(argc, argv, ARGV_SIZE, ide_idx, NULL, i, mbr_blank); + argc = setup_ide(argc, argv, ARGV_SIZE, + ide_idx, NULL, i, mbr_blank, ""); } args = g_strjoinv(" ", argv); qtest_start(args); @@ -401,6 +422,8 @@ int main(int argc, char **argv) qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); + qtest_add_func("hd-geo/ide/drive/user/chs", test_ide_drive_user_chs); + qtest_add_func("hd-geo/ide/drive/user/chst", test_ide_drive_user_chst); qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); From f8a30874ca4dd6560b5827433f07877e60960946 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 14:31:15 +0800 Subject: [PATCH 2178/2380] block: Prefix file driver trace points with "file_" With in one module, trace points usually have a common prefix named after the module name. paio_submit and paio_submit_co are the only two trace points so far in the two file protocol drivers. As we are adding more, having a common prefix here is better so that trace points can be enabled with a glob. Rename them. Suggested-by: Kevin Wolf Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/file-posix.c | 2 +- block/file-win32.c | 2 +- block/trace-events | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 4fec8cb53c..1185c7c5cc 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1743,7 +1743,7 @@ static int paio_submit_co_full(BlockDriverState *bs, int fd, assert(qiov->size == bytes); } - trace_paio_submit_co(offset, bytes, type); + trace_file_paio_submit_co(offset, bytes, type); pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); return thread_pool_submit_co(pool, aio_worker, acb); } diff --git a/block/file-win32.c b/block/file-win32.c index 0411fe80fd..f1e2187f3b 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -162,7 +162,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, acb->aio_nbytes = count; acb->aio_offset = offset; - trace_paio_submit(acb, opaque, offset, count, type); + trace_file_paio_submit(acb, opaque, offset, count, type); pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); } diff --git a/block/trace-events b/block/trace-events index c35287b48a..854d5525af 100644 --- a/block/trace-events +++ b/block/trace-events @@ -55,8 +55,8 @@ qmp_block_stream(void *bs, void *job) "bs %p job %p" # block/file-win32.c # block/file-posix.c -paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" -paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" +file_paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" +file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" # block/qcow2.c qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" From ecc983a507bec9d3130434702d7031bfd372ba74 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 14:31:16 +0800 Subject: [PATCH 2179/2380] block: Add copy offloading trace points A few trace points that can help reveal what is happening in a copy offloading I/O path. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/file-posix.c | 2 ++ block/io.c | 4 ++++ block/iscsi.c | 3 +++ block/trace-events | 6 ++++++ 4 files changed, 15 insertions(+) diff --git a/block/file-posix.c b/block/file-posix.c index 1185c7c5cc..09f6b938f6 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1488,6 +1488,8 @@ static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb) ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, aiocb->aio_fd2, &out_off, bytes, 0); + trace_file_copy_file_range(aiocb->bs, aiocb->aio_fildes, in_off, + aiocb->aio_fd2, out_off, bytes, 0, ret); if (ret == 0) { /* No progress (e.g. when beyond EOF), let the caller fall back to * buffer I/O. */ diff --git a/block/io.c b/block/io.c index 6be9c40f23..9b2aa04010 100644 --- a/block/io.c +++ b/block/io.c @@ -3019,6 +3019,8 @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { + trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, + read_flags, write_flags); return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, bytes, read_flags, write_flags, true); } @@ -3033,6 +3035,8 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { + trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, + read_flags, write_flags); return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, bytes, read_flags, write_flags, false); } diff --git a/block/iscsi.c b/block/iscsi.c index 38b917a1e5..bb69faf34a 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -44,6 +44,7 @@ #include "qapi/qmp/qstring.h" #include "crypto/secret.h" #include "scsi/utils.h" +#include "trace.h" /* Conflict between scsi/utils.h and libiscsi! :( */ #define SCSI_XFER_NONE ISCSI_XFER_NONE @@ -2399,6 +2400,8 @@ retry: } out_unlock: + + trace_iscsi_xcopy(src_lun, src_offset, dst_lun, dst_offset, bytes, r); g_free(iscsi_task.task); qemu_mutex_unlock(&dst_lun->mutex); g_free(iscsi_task.err_str); diff --git a/block/trace-events b/block/trace-events index 854d5525af..3e8c47bb24 100644 --- a/block/trace-events +++ b/block/trace-events @@ -15,6 +15,8 @@ bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x" bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %"PRId64 +bdrv_co_copy_range_from(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" +bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" # block/stream.c stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" @@ -57,6 +59,7 @@ qmp_block_stream(void *bs, void *job) "bs %p job %p" # block/file-posix.c file_paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" +file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64 # block/qcow2.c qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" @@ -150,3 +153,6 @@ nvme_free_req_queue_wait(void *q) "q %p" nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d" nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d" + +# block/iscsi.c +iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, uint64_t bytes, int ret) "src_lun %p offset %"PRIu64" dst_lun %p offset %"PRIu64" bytes %"PRIu64" ret %d" From 0b9fd3f467dc5ac041fa014cd28c949b25b87d25 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 14:31:17 +0800 Subject: [PATCH 2180/2380] block: Use BdrvChild to discard Other I/O functions are already using a BdrvChild pointer in the API, so make discard do the same. It makes it possible to initiate the same permission checks before doing I/O, and much easier to share the helper functions for this, which will be added and used by write, truncate and copy range paths. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/blkdebug.c | 2 +- block/blklogwrites.c | 2 +- block/blkreplay.c | 2 +- block/block-backend.c | 2 +- block/copy-on-read.c | 2 +- block/io.c | 18 +++++++++--------- block/mirror.c | 2 +- block/qcow2-refcount.c | 2 +- block/raw-format.c | 2 +- block/throttle.c | 2 +- include/block/block.h | 4 ++-- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index 526af2a808..0457bf5b66 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -625,7 +625,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, return err; } - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); + return bdrv_co_pdiscard(bs->file, offset, bytes); } static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, diff --git a/block/blklogwrites.c b/block/blklogwrites.c index efa2c7a66a..ff98cd5533 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -486,7 +486,7 @@ static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr) static int coroutine_fn blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr) { - return bdrv_co_pdiscard(fr->bs->file->bs, fr->offset, fr->bytes); + return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes); } static int coroutine_fn diff --git a/block/blkreplay.c b/block/blkreplay.c index b016dbeee7..766150ade6 100755 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -113,7 +113,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { uint64_t reqid = blkreplay_next_id(); - int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes); + int ret = bdrv_co_pdiscard(bs->file, offset, bytes); block_request_create(reqid, bs, qemu_coroutine_self()); qemu_coroutine_yield(); diff --git a/block/block-backend.c b/block/block-backend.c index e94e3d5929..f2f75a977d 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1560,7 +1560,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) return ret; } - return bdrv_co_pdiscard(blk_bs(blk), offset, bytes); + return bdrv_co_pdiscard(blk->root, offset, bytes); } int blk_co_flush(BlockBackend *blk) diff --git a/block/copy-on-read.c b/block/copy-on-read.c index 1dcdaeed69..a19164f9eb 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -116,7 +116,7 @@ static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); + return bdrv_co_pdiscard(bs->file, offset, bytes); } diff --git a/block/io.c b/block/io.c index 9b2aa04010..fbcd93304b 100644 --- a/block/io.c +++ b/block/io.c @@ -2633,7 +2633,7 @@ int bdrv_flush(BlockDriverState *bs) } typedef struct DiscardCo { - BlockDriverState *bs; + BdrvChild *child; int64_t offset; int bytes; int ret; @@ -2642,17 +2642,17 @@ static void coroutine_fn bdrv_pdiscard_co_entry(void *opaque) { DiscardCo *rwco = opaque; - rwco->ret = bdrv_co_pdiscard(rwco->bs, rwco->offset, rwco->bytes); + rwco->ret = bdrv_co_pdiscard(rwco->child, rwco->offset, rwco->bytes); } -int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, - int bytes) +int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes) { BdrvTrackedRequest req; int max_pdiscard, ret; int head, tail, align; + BlockDriverState *bs = child->bs; - if (!bs->drv) { + if (!bs || !bs->drv) { return -ENOMEDIUM; } @@ -2763,11 +2763,11 @@ out: return ret; } -int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) +int bdrv_pdiscard(BdrvChild *child, int64_t offset, int bytes) { Coroutine *co; DiscardCo rwco = { - .bs = bs, + .child = child, .offset = offset, .bytes = bytes, .ret = NOT_DONE, @@ -2778,8 +2778,8 @@ int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) bdrv_pdiscard_co_entry(&rwco); } else { co = qemu_coroutine_create(bdrv_pdiscard_co_entry, &rwco); - bdrv_coroutine_enter(bs, co); - BDRV_POLL_WHILE(bs, rwco.ret == NOT_DONE); + bdrv_coroutine_enter(child->bs, co); + BDRV_POLL_WHILE(child->bs, rwco.ret == NOT_DONE); } return rwco.ret; diff --git a/block/mirror.c b/block/mirror.c index 61bd9f3cf1..b48c3f8cf5 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1333,7 +1333,7 @@ static int coroutine_fn bdrv_mirror_top_do_write(BlockDriverState *bs, break; case MIRROR_METHOD_DISCARD: - ret = bdrv_co_pdiscard(bs->backing->bs, offset, bytes); + ret = bdrv_co_pdiscard(bs->backing, offset, bytes); break; default: diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 18c729aa27..4e1589ad7a 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -734,7 +734,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) /* Discard is optional, ignore the return value */ if (ret >= 0) { - bdrv_pdiscard(bs->file->bs, d->offset, d->bytes); + bdrv_pdiscard(bs->file, d->offset, d->bytes); } g_free(d); diff --git a/block/raw-format.c b/block/raw-format.c index a3591985f6..dee262875a 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -297,7 +297,7 @@ static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, if (ret) { return ret; } - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); + return bdrv_co_pdiscard(bs->file, offset, bytes); } static int64_t raw_getlength(BlockDriverState *bs) diff --git a/block/throttle.c b/block/throttle.c index f617f23a12..636c9764aa 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -149,7 +149,7 @@ static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs, ThrottleGroupMember *tgm = bs->opaque; throttle_group_co_io_limits_intercept(tgm, bytes, true); - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); + return bdrv_co_pdiscard(bs->file, offset, bytes); } static int throttle_co_flush(BlockDriverState *bs) diff --git a/include/block/block.h b/include/block/block.h index a91f37bedf..f85e3a6ed3 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -418,8 +418,8 @@ AioWait *bdrv_get_aio_wait(BlockDriverState *bs); bdrv_get_aio_context(bs_), \ cond); }) -int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes); -int bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes); +int bdrv_pdiscard(BdrvChild *child, int64_t offset, int bytes); +int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes); int bdrv_has_zero_init_1(BlockDriverState *bs); int bdrv_has_zero_init(BlockDriverState *bs); bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs); From 22931a15336e8b7726965c699981fd108620014b Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 14:31:18 +0800 Subject: [PATCH 2181/2380] block: Use uint64_t for BdrvTrackedRequest byte fields This matches the types used for bytes in the rest parts of block layer. In the case of bdrv_co_truncate, new_bytes can be the image size which probably doesn't fit in a 32 bit int. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/io.c | 8 +++++--- include/block/block_int.h | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/block/io.c b/block/io.c index fbcd93304b..6293612e73 100644 --- a/block/io.c +++ b/block/io.c @@ -601,9 +601,11 @@ static void tracked_request_end(BdrvTrackedRequest *req) static void tracked_request_begin(BdrvTrackedRequest *req, BlockDriverState *bs, int64_t offset, - unsigned int bytes, + uint64_t bytes, enum BdrvTrackedRequestType type) { + assert(bytes <= INT64_MAX && offset <= INT64_MAX - bytes); + *req = (BdrvTrackedRequest){ .bs = bs, .offset = offset, @@ -625,7 +627,7 @@ static void tracked_request_begin(BdrvTrackedRequest *req, static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) { int64_t overlap_offset = req->offset & ~(align - 1); - unsigned int overlap_bytes = ROUND_UP(req->offset + req->bytes, align) + uint64_t overlap_bytes = ROUND_UP(req->offset + req->bytes, align) - overlap_offset; if (!req->serialising) { @@ -683,7 +685,7 @@ static int bdrv_get_cluster_size(BlockDriverState *bs) } static bool tracked_request_overlaps(BdrvTrackedRequest *req, - int64_t offset, unsigned int bytes) + int64_t offset, uint64_t bytes) { /* aaaa bbbb */ if (offset >= req->overlap_offset + req->overlap_bytes) { diff --git a/include/block/block_int.h b/include/block/block_int.h index 920d3d122b..903b9c1034 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -69,12 +69,12 @@ enum BdrvTrackedRequestType { typedef struct BdrvTrackedRequest { BlockDriverState *bs; int64_t offset; - unsigned int bytes; + uint64_t bytes; enum BdrvTrackedRequestType type; bool serialising; int64_t overlap_offset; - unsigned int overlap_bytes; + uint64_t overlap_bytes; QLIST_ENTRY(BdrvTrackedRequest) list; Coroutine *co; /* owner, used for deadlock detection */ From 85fe24796ddf70f2b6a5045952826aedffa55ca2 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 14:31:19 +0800 Subject: [PATCH 2182/2380] block: Extract common write req handling As a mechanical refactoring patch, this is the first step towards unified and more correct write code paths. This is helpful because multiple BlockDriverState fields need to be updated after modifying image data, and it's hard to maintain in multiple places such as copy offload, discard and truncate. Suggested-by: Kevin Wolf Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/io.c | 91 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/block/io.c b/block/io.c index 6293612e73..313fe0776c 100644 --- a/block/io.c +++ b/block/io.c @@ -1575,6 +1575,61 @@ fail: return ret; } +static inline int coroutine_fn +bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, + BdrvTrackedRequest *req, int flags) +{ + BlockDriverState *bs = child->bs; + bool waited; + int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); + + if (bs->read_only) { + return -EPERM; + } + + /* BDRV_REQ_NO_SERIALISING is only for read operation */ + assert(!(flags & BDRV_REQ_NO_SERIALISING)); + assert(!(bs->open_flags & BDRV_O_INACTIVE)); + assert((bs->open_flags & BDRV_O_NO_IO) == 0); + assert(!(flags & ~BDRV_REQ_MASK)); + + if (flags & BDRV_REQ_SERIALISING) { + mark_request_serialising(req, bdrv_get_cluster_size(bs)); + } + + waited = wait_serialising_requests(req); + + assert(!waited || !req->serialising || + is_request_serialising_and_aligned(req)); + assert(req->overlap_offset <= offset); + assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); + + if (flags & BDRV_REQ_WRITE_UNCHANGED) { + assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); + } else { + assert(child->perm & BLK_PERM_WRITE); + } + assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); + return notifier_with_return_list_notify(&bs->before_write_notifiers, req); +} + +static inline void coroutine_fn +bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, + BdrvTrackedRequest *req, int ret) +{ + int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); + BlockDriverState *bs = child->bs; + + atomic_inc(&bs->write_gen); + bdrv_set_dirty(bs, offset, bytes); + + stat64_max(&bs->wr_highest_offset, offset + bytes); + + if (ret == 0) { + bs->total_sectors = MAX(bs->total_sectors, end_sector); + } +} + /* * Forwards an already correctly aligned write request to the BlockDriver, * after possibly fragmenting it. @@ -1585,10 +1640,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, { BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; - bool waited; int ret; - int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); uint64_t bytes_remaining = bytes; int max_transfer; @@ -1604,31 +1657,10 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, assert((offset & (align - 1)) == 0); assert((bytes & (align - 1)) == 0); assert(!qiov || bytes == qiov->size); - assert((bs->open_flags & BDRV_O_NO_IO) == 0); - assert(!(flags & ~BDRV_REQ_MASK)); max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), align); - /* BDRV_REQ_NO_SERIALISING is only for read operation */ - assert(!(flags & BDRV_REQ_NO_SERIALISING)); - - if (flags & BDRV_REQ_SERIALISING) { - mark_request_serialising(req, bdrv_get_cluster_size(bs)); - } - - waited = wait_serialising_requests(req); - assert(!waited || !req->serialising || - is_request_serialising_and_aligned(req)); - assert(req->overlap_offset <= offset); - assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); - if (flags & BDRV_REQ_WRITE_UNCHANGED) { - assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); - } else { - assert(child->perm & BLK_PERM_WRITE); - } - assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); - - ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); + ret = bdrv_co_write_req_prepare(child, offset, bytes, req, flags); if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF && !(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_pwrite_zeroes && @@ -1677,15 +1709,10 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, } bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE); - atomic_inc(&bs->write_gen); - bdrv_set_dirty(bs, offset, bytes); - - stat64_max(&bs->wr_highest_offset, offset + bytes); - if (ret >= 0) { - bs->total_sectors = MAX(bs->total_sectors, end_sector); ret = 0; } + bdrv_co_write_req_finish(child, offset, bytes, req, ret); return ret; } @@ -1800,10 +1827,6 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, if (!bs->drv) { return -ENOMEDIUM; } - if (bs->read_only) { - return -EPERM; - } - assert(!(bs->open_flags & BDRV_O_INACTIVE)); ret = bdrv_check_byte_request(bs, offset, bytes); if (ret < 0) { From 7f8f03ef6d5c82fee1c22be06fc9de4a16ad7059 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 14:31:20 +0800 Subject: [PATCH 2183/2380] block: Fix handling of image enlarging write Two problems exist when a write request that enlarges the image (i.e. write beyond EOF) finishes: 1) parent is not notified about size change; 2) dirty bitmap is not resized although we try to set the dirty bits; Fix them just like how bdrv_co_truncate works. Reported-by: Kevin Wolf Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/io.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/block/io.c b/block/io.c index 313fe0776c..c88b2b48d7 100644 --- a/block/io.c +++ b/block/io.c @@ -40,6 +40,7 @@ static AioWait drain_all_aio_wait; +static void bdrv_parent_cb_resize(BlockDriverState *bs); static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags); @@ -1621,13 +1622,16 @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, BlockDriverState *bs = child->bs; atomic_inc(&bs->write_gen); - bdrv_set_dirty(bs, offset, bytes); stat64_max(&bs->wr_highest_offset, offset + bytes); - if (ret == 0) { - bs->total_sectors = MAX(bs->total_sectors, end_sector); + if (ret == 0 && + end_sector > bs->total_sectors) { + bs->total_sectors = end_sector; + bdrv_parent_cb_resize(bs); + bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS); } + bdrv_set_dirty(bs, offset, bytes); } /* From d131662a1afd44ffd0ced3334fa0cf1f30f87196 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:18:57 +0800 Subject: [PATCH 2184/2380] tests: introduce migrate_postcopy_* helpers Separate the old postcopy UNIX socket test into three steps, provide a helper for each step. With these helpers, we can do more compliated tests like postcopy recovery, while keep the codes shared. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Reviewed-by: Balamuruhan S Signed-off-by: Peter Xu Message-Id: <20180710091902.28780-6-peterx@redhat.com> Signed-off-by: Dr. David Alan Gilbert Fix up merge with 2e295789 / Skip tests for ppc tcg --- tests/migration-test.c | 44 ++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/tests/migration-test.c b/tests/migration-test.c index 331efb0fe5..193c7df94e 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -351,13 +351,19 @@ static void migrate(QTestState *who, const char *uri) qobject_unref(rsp); } -static void migrate_start_postcopy(QTestState *who) +static void migrate_postcopy_start(QTestState *from, QTestState *to) { QDict *rsp; - rsp = wait_command(who, "{ 'execute': 'migrate-start-postcopy' }"); + rsp = wait_command(from, "{ 'execute': 'migrate-start-postcopy' }"); g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + + qtest_qmp_eventwait(to, "RESUME"); } static int test_migrate_start(QTestState **from, QTestState **to, @@ -510,13 +516,14 @@ static void test_deprecated(void) qtest_quit(from); } -static void test_postcopy(void) +static int migrate_postcopy_prepare(QTestState **from_ptr, + QTestState **to_ptr) { char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; if (test_migrate_start(&from, &to, uri, false)) { - return; + return -1; } migrate_set_capability(from, "postcopy-ram", "true"); @@ -534,28 +541,41 @@ static void test_postcopy(void) wait_for_serial("src_serial"); migrate(from, uri); + g_free(uri); wait_for_migration_pass(from); - migrate_start_postcopy(from); + *from_ptr = from; + *to_ptr = to; - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } + return 0; +} - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); +static void migrate_postcopy_complete(QTestState *from, QTestState *to) +{ wait_for_migration_complete(from); + /* Make sure we get at least one "B" on destination */ + wait_for_serial("dest_serial"); + if (uffd_feature_thread_id) { read_blocktime(to); } - g_free(uri); test_migrate_end(from, to, true); } +static void test_postcopy(void) +{ + QTestState *from, *to; + + if (migrate_postcopy_prepare(&from, &to)) { + return; + } + migrate_postcopy_start(from, to); + migrate_postcopy_complete(from, to); +} + static void test_baddest(void) { QTestState *from, *to; From 7e1d74271c36d12a90608db73ea72aab352b5bb0 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:18:58 +0800 Subject: [PATCH 2185/2380] tests: allow migrate() to take extra flags For example, we can pass in '"resume": true' to resume a migration. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Reviewed-by: Balamuruhan S Signed-off-by: Peter Xu Message-Id: <20180710091902.28780-7-peterx@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- tests/migration-test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/migration-test.c b/tests/migration-test.c index 193c7df94e..0a1e2f0a09 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -337,14 +337,14 @@ static void migrate_set_capability(QTestState *who, const char *capability, qobject_unref(rsp); } -static void migrate(QTestState *who, const char *uri) +static void migrate(QTestState *who, const char *uri, const char *extra) { QDict *rsp; gchar *cmd; cmd = g_strdup_printf("{ 'execute': 'migrate'," - "'arguments': { 'uri': '%s' } }", - uri); + " 'arguments': { 'uri': '%s' %s } }", + uri, extra ? extra : ""); rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); @@ -540,7 +540,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri); + migrate(from, uri, NULL); g_free(uri); wait_for_migration_pass(from); @@ -586,7 +586,7 @@ static void test_baddest(void) if (test_migrate_start(&from, &to, "tcp:0:0", true)) { return; } - migrate(from, "tcp:0:0"); + migrate(from, "tcp:0:0", NULL); do { rsp = wait_command(from, "{ 'execute': 'query-migrate' }"); rsp_return = qdict_get_qdict(rsp, "return"); @@ -630,7 +630,7 @@ static void test_precopy_unix(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri); + migrate(from, uri, NULL); wait_for_migration_pass(from); From 2f7074c6fdec5015086f5328f8427097bee86e13 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:18:59 +0800 Subject: [PATCH 2186/2380] tests: introduce migrate_query*() helpers Introduce helpers to query migration states and use it. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Reviewed-by: Balamuruhan S Signed-off-by: Peter Xu Message-Id: <20180710091902.28780-8-peterx@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- tests/migration-test.c | 64 ++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/tests/migration-test.c b/tests/migration-test.c index 0a1e2f0a09..e2697efd01 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -168,6 +168,37 @@ static QDict *wait_command(QTestState *who, const char *command) return response; } +/* + * Note: caller is responsible to free the returned object via + * qobject_unref() after use + */ +static QDict *migrate_query(QTestState *who) +{ + QDict *rsp, *rsp_return; + + rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); + rsp_return = qdict_get_qdict(rsp, "return"); + g_assert(rsp_return); + qobject_ref(rsp_return); + qobject_unref(rsp); + + return rsp_return; +} + +/* + * Note: caller is responsible to free the returned object via + * g_free() after use + */ +static gchar *migrate_query_status(QTestState *who) +{ + QDict *rsp_return = migrate_query(who); + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); + + g_assert(status); + qobject_unref(rsp_return); + + return status; +} /* * It's tricky to use qemu's migration event capability with qtest, @@ -176,11 +207,10 @@ static QDict *wait_command(QTestState *who, const char *command) static uint64_t get_migration_pass(QTestState *who) { - QDict *rsp, *rsp_return, *rsp_ram; + QDict *rsp_return, *rsp_ram; uint64_t result; - rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); - rsp_return = qdict_get_qdict(rsp, "return"); + rsp_return = migrate_query(who); if (!qdict_haskey(rsp_return, "ram")) { /* Still in setup */ result = 0; @@ -188,33 +218,29 @@ static uint64_t get_migration_pass(QTestState *who) rsp_ram = qdict_get_qdict(rsp_return, "ram"); result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0); } - qobject_unref(rsp); + qobject_unref(rsp_return); return result; } static void read_blocktime(QTestState *who) { - QDict *rsp, *rsp_return; + QDict *rsp_return; - rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); - rsp_return = qdict_get_qdict(rsp, "return"); + rsp_return = migrate_query(who); g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); - qobject_unref(rsp); + qobject_unref(rsp_return); } static void wait_for_migration_complete(QTestState *who) { while (true) { - QDict *rsp, *rsp_return; bool completed; - const char *status; + char *status; - rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); - rsp_return = qdict_get_qdict(rsp, "return"); - status = qdict_get_str(rsp_return, "status"); + status = migrate_query_status(who); completed = strcmp(status, "completed") == 0; g_assert_cmpstr(status, !=, "failed"); - qobject_unref(rsp); + g_free(status); if (completed) { return; } @@ -580,7 +606,7 @@ static void test_baddest(void) { QTestState *from, *to; QDict *rsp, *rsp_return; - const char *status; + char *status; bool failed; if (test_migrate_start(&from, &to, "tcp:0:0", true)) { @@ -588,14 +614,10 @@ static void test_baddest(void) } migrate(from, "tcp:0:0", NULL); do { - rsp = wait_command(from, "{ 'execute': 'query-migrate' }"); - rsp_return = qdict_get_qdict(rsp, "return"); - - status = qdict_get_str(rsp_return, "status"); - + status = migrate_query_status(from); g_assert(!strcmp(status, "setup") || !(strcmp(status, "failed"))); failed = !strcmp(status, "failed"); - qobject_unref(rsp); + g_free(status); } while (!failed); /* Is the machine currently running? */ From 2f6d313836591659375db97a2c1de4f28413f12c Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:19:00 +0800 Subject: [PATCH 2187/2380] tests: introduce wait_for_migration_status() It's generalized from wait_for_migration_complete() to allow us to wait for any migration status besides failure. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Reviewed-by: Balamuruhan S Signed-off-by: Peter Xu Message-Id: <20180710091902.28780-9-peterx@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- tests/migration-test.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/migration-test.c b/tests/migration-test.c index e2697efd01..4c15071893 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -231,14 +231,15 @@ static void read_blocktime(QTestState *who) qobject_unref(rsp_return); } -static void wait_for_migration_complete(QTestState *who) +static void wait_for_migration_status(QTestState *who, + const char *goal) { while (true) { bool completed; char *status; status = migrate_query_status(who); - completed = strcmp(status, "completed") == 0; + completed = strcmp(status, goal) == 0; g_assert_cmpstr(status, !=, "failed"); g_free(status); if (completed) { @@ -248,6 +249,11 @@ static void wait_for_migration_complete(QTestState *who) } } +static void wait_for_migration_complete(QTestState *who) +{ + wait_for_migration_status(who, "completed"); +} + static void wait_for_migration_pass(QTestState *who) { uint64_t initial_pass = get_migration_pass(who); From d5f496407db4444758e3374b95109a5d18eba70b Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:19:01 +0800 Subject: [PATCH 2188/2380] tests: add postcopy recovery test Test the postcopy recovery procedure by emulating a network failure using migrate-pause command. Tested-by: Balamuruhan S Signed-off-by: Peter Xu Message-Id: <20180710091902.28780-10-peterx@redhat.com> Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Dr. David Alan Gilbert --- tests/migration-test.c | 80 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/tests/migration-test.c b/tests/migration-test.c index 4c15071893..d8b2633fce 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -352,6 +352,29 @@ static void migrate_set_parameter(QTestState *who, const char *parameter, migrate_check_parameter(who, parameter, value); } +static void migrate_pause(QTestState *who) +{ + QDict *rsp; + + rsp = wait_command(who, "{ 'execute': 'migrate-pause' }"); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +static void migrate_recover(QTestState *who, const char *uri) +{ + QDict *rsp; + gchar *cmd = g_strdup_printf( + "{ 'execute': 'migrate-recover', " + " 'id': 'recover-cmd', " + " 'arguments': { 'uri': '%s' } }", uri); + + rsp = wait_command(who, cmd); + g_assert(qdict_haskey(rsp, "return")); + g_free(cmd); + qobject_unref(rsp); +} + static void migrate_set_capability(QTestState *who, const char *capability, const char *value) { @@ -608,6 +631,62 @@ static void test_postcopy(void) migrate_postcopy_complete(from, to); } +static void test_postcopy_recovery(void) +{ + QTestState *from, *to; + char *uri; + + if (migrate_postcopy_prepare(&from, &to)) { + return; + } + + /* Turn postcopy speed down, 4K/s is slow enough on any machines */ + migrate_set_parameter(from, "max-postcopy-bandwidth", "4096"); + + /* Now we start the postcopy */ + migrate_postcopy_start(from, to); + + /* + * Wait until postcopy is really started; we can only run the + * migrate-pause command during a postcopy + */ + wait_for_migration_status(from, "postcopy-active"); + + /* + * Manually stop the postcopy migration. This emulates a network + * failure with the migration socket + */ + migrate_pause(from); + + /* + * Wait for destination side to reach postcopy-paused state. The + * migrate-recover command can only succeed if destination machine + * is in the paused state + */ + wait_for_migration_status(to, "postcopy-paused"); + + /* + * Create a new socket to emulate a new channel that is different + * from the broken migration channel; tell the destination to + * listen to the new port + */ + uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); + migrate_recover(to, uri); + + /* + * Try to rebuild the migration channel using the resume flag and + * the newly created channel + */ + wait_for_migration_status(from, "postcopy-paused"); + migrate(from, uri, ", 'resume': true"); + g_free(uri); + + /* Restore the postcopy bandwidth to unlimited */ + migrate_set_parameter(from, "max-postcopy-bandwidth", "0"); + + migrate_postcopy_complete(from, to); +} + static void test_baddest(void) { QTestState *from, *to; @@ -698,6 +777,7 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); qtest_add_func("/migration/postcopy/unix", test_postcopy); + qtest_add_func("/migration/postcopy/recovery", test_postcopy_recovery); qtest_add_func("/migration/deprecated", test_deprecated); qtest_add_func("/migration/bad_dest", test_baddest); qtest_add_func("/migration/precopy/unix", test_precopy_unix); From 3e81f73c7a1286e251180c19f62829fe5c045e39 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:19:02 +0800 Subject: [PATCH 2189/2380] tests: hide stderr for postcopy recovery test We dumped something when network failure happens. We should avoid those messages to be dumped when running the tests: $ ./tests/migration-test -p /x86_64/migration/postcopy/recovery /x86_64/migration/postcopy/recovery: qemu-system-x86_64: check_section_footer: Read section footer failed: -5 qemu-system-x86_64: Detected IO failure for postcopy. Migration paused. qemu-system-x86_64: Detected IO failure for postcopy. Migration paused. OK After the patch: $ ./tests/migration-test -p /x86_64/migration/postcopy/recovery /x86_64/migration/postcopy/recovery: OK Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Peter Xu Message-Id: <20180710091902.28780-11-peterx@redhat.com> Signed-off-by: Dr. David Alan Gilbert --- tests/migration-test.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/migration-test.c b/tests/migration-test.c index d8b2633fce..086f727b34 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -572,12 +572,13 @@ static void test_deprecated(void) } static int migrate_postcopy_prepare(QTestState **from_ptr, - QTestState **to_ptr) + QTestState **to_ptr, + bool hide_error) { char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, false)) { + if (test_migrate_start(&from, &to, uri, hide_error)) { return -1; } @@ -624,7 +625,7 @@ static void test_postcopy(void) { QTestState *from, *to; - if (migrate_postcopy_prepare(&from, &to)) { + if (migrate_postcopy_prepare(&from, &to, false)) { return; } migrate_postcopy_start(from, to); @@ -636,7 +637,7 @@ static void test_postcopy_recovery(void) QTestState *from, *to; char *uri; - if (migrate_postcopy_prepare(&from, &to)) { + if (migrate_postcopy_prepare(&from, &to, true)) { return; } From 858b6d62249a9a9510fae6c808a3d2de80e689b5 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 10 Jul 2018 17:44:24 +0800 Subject: [PATCH 2190/2380] migration: reorder MIG_CMD_POSTCOPY_RESUME MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was accidently added before MIG_CMD_PACKAGED so it might break command compatibility when we run postcopy migration between old/new QEMUs. Fix that up quickly before the QEMU 3.0 release. Reported-by: Lukáš Doktor Suggested-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Message-Id: <20180710094424.30754-1-peterx@redhat.com> Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/savevm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/savevm.c b/migration/savevm.c index efcc795071..7f92567a10 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -81,8 +81,8 @@ enum qemu_vm_cmd { MIG_CMD_POSTCOPY_RAM_DISCARD, /* A list of pages to discard that were previously sent during precopy but are dirty. */ - MIG_CMD_POSTCOPY_RESUME, /* resume postcopy on dest */ MIG_CMD_PACKAGED, /* Send a wrapped stream within this stream */ + MIG_CMD_POSTCOPY_RESUME, /* resume postcopy on dest */ MIG_CMD_RECV_BITMAP, /* Request for recved bitmap on dst */ MIG_CMD_MAX }; From 00695c27a00a4b3333604aeac50c43269cc151a3 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 14:31:21 +0800 Subject: [PATCH 2191/2380] block: Use common req handling for discard Reuse the new bdrv_co_write_req_prepare/finish helpers. The variation here is that discard requests don't affect bs->wr_highest_offset, and it cannot extend the image. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/io.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/block/io.c b/block/io.c index c88b2b48d7..72cfd9bd16 100644 --- a/block/io.c +++ b/block/io.c @@ -1623,15 +1623,32 @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, atomic_inc(&bs->write_gen); - stat64_max(&bs->wr_highest_offset, offset + bytes); - + /* + * Discard cannot extend the image, but in error handling cases, such as + * when reverting a qcow2 cluster allocation, the discarded range can pass + * the end of image file, so we cannot assert about BDRV_TRACKED_DISCARD + * here. Instead, just skip it, since semantically a discard request + * beyond EOF cannot expand the image anyway. + */ if (ret == 0 && - end_sector > bs->total_sectors) { + end_sector > bs->total_sectors && + req->type != BDRV_TRACKED_DISCARD) { bs->total_sectors = end_sector; bdrv_parent_cb_resize(bs); bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS); } - bdrv_set_dirty(bs, offset, bytes); + if (req->bytes) { + switch (req->type) { + case BDRV_TRACKED_WRITE: + stat64_max(&bs->wr_highest_offset, offset + bytes); + /* fall through, to set dirty bits */ + case BDRV_TRACKED_DISCARD: + bdrv_set_dirty(bs, offset, bytes); + break; + default: + break; + } + } } /* @@ -2692,10 +2709,7 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes) ret = bdrv_check_byte_request(bs, offset, bytes); if (ret < 0) { return ret; - } else if (bs->read_only) { - return -EPERM; } - assert(!(bs->open_flags & BDRV_O_INACTIVE)); /* Do nothing if disabled. */ if (!(bs->open_flags & BDRV_O_UNMAP)) { @@ -2719,7 +2733,7 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes) bdrv_inc_in_flight(bs); tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_DISCARD); - ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req); + ret = bdrv_co_write_req_prepare(child, offset, bytes, &req, 0); if (ret < 0) { goto out; } @@ -2785,8 +2799,7 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes) } ret = 0; out: - atomic_inc(&bs->write_gen); - bdrv_set_dirty(bs, req.offset, req.bytes); + bdrv_co_write_req_finish(child, req.offset, req.bytes, &req, ret); tracked_request_end(&req); bdrv_dec_in_flight(bs); return ret; From 0eb1e891126f0cde52e88384696ad67076bdc341 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 14:31:22 +0800 Subject: [PATCH 2192/2380] block: Use common req handling in copy offloading This brings the request handling logic inline with write and discard, fixing write_gen, resize_cb, dirty bitmaps and image size refreshing. The last of these issues broke iotest case 222, which is now fixed. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/io.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/block/io.c b/block/io.c index 72cfd9bd16..2832214db4 100644 --- a/block/io.c +++ b/block/io.c @@ -3030,20 +3030,16 @@ static int coroutine_fn bdrv_co_copy_range_internal( bdrv_inc_in_flight(dst->bs); tracked_request_begin(&req, dst->bs, dst_offset, bytes, BDRV_TRACKED_WRITE); - - /* BDRV_REQ_NO_SERIALISING is only for read operation */ - assert(!(write_flags & BDRV_REQ_NO_SERIALISING)); - if (write_flags & BDRV_REQ_SERIALISING) { - mark_request_serialising(&req, bdrv_get_cluster_size(dst->bs)); + ret = bdrv_co_write_req_prepare(dst, dst_offset, bytes, &req, + write_flags); + if (!ret) { + ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, + src, src_offset, + dst, dst_offset, + bytes, + read_flags, write_flags); } - wait_serialising_requests(&req); - - ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, - src, src_offset, - dst, dst_offset, - bytes, - read_flags, write_flags); - + bdrv_co_write_req_finish(dst, dst_offset, bytes, &req, ret); tracked_request_end(&req); bdrv_dec_in_flight(dst->bs); } From 5416a11eb55d46c376dde97c429b89e9b4e1a94f Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 14:31:23 +0800 Subject: [PATCH 2193/2380] block: Fix bdrv_co_truncate overlap check If we are growing the image and potentially using preallocation for the new area, we need to make sure that no write requests are made to the "preallocated" area which is [@old_size, @offset), not [@offset, offset * 2 - @old_size). Signed-off-by: Fam Zheng Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/io.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/io.c b/block/io.c index 2832214db4..77d38ca1d3 100644 --- a/block/io.c +++ b/block/io.c @@ -3136,7 +3136,8 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, } bdrv_inc_in_flight(bs); - tracked_request_begin(&req, bs, offset, new_bytes, BDRV_TRACKED_TRUNCATE); + tracked_request_begin(&req, bs, offset - new_bytes, new_bytes, + BDRV_TRACKED_TRUNCATE); /* If we are growing the image and potentially using preallocation for the * new area, we need to make sure that no write requests are made to it From cd47d792d7a27a57f4b621e2ff1ed8f4e83de1e9 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 10 Jul 2018 14:31:24 +0800 Subject: [PATCH 2194/2380] block: Use common write req handling in truncate Truncation is the last to convert from open coded req handling to reusing helpers. This time the permission check in prepare has to adapt to the new caller: it checks a different permission bit, and doesn't trigger the before write notifier. Also, truncation should always trigger a bs->total_sectors update and in turn call parent resize_cb. Update the condition in finish helper, too. It's intended to do a duplicated bs->read_only check before calling bdrv_co_write_req_prepare() so that we can be more informative with the error message, as bdrv_co_write_req_prepare() doesn't have Error parameter. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/io.c | 57 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/block/io.c b/block/io.c index 77d38ca1d3..7100344c7b 100644 --- a/block/io.c +++ b/block/io.c @@ -1604,14 +1604,24 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, is_request_serialising_and_aligned(req)); assert(req->overlap_offset <= offset); assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); - - if (flags & BDRV_REQ_WRITE_UNCHANGED) { - assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); - } else { - assert(child->perm & BLK_PERM_WRITE); - } assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); - return notifier_with_return_list_notify(&bs->before_write_notifiers, req); + + switch (req->type) { + case BDRV_TRACKED_WRITE: + case BDRV_TRACKED_DISCARD: + if (flags & BDRV_REQ_WRITE_UNCHANGED) { + assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); + } else { + assert(child->perm & BLK_PERM_WRITE); + } + return notifier_with_return_list_notify(&bs->before_write_notifiers, + req); + case BDRV_TRACKED_TRUNCATE: + assert(child->perm & BLK_PERM_RESIZE); + return 0; + default: + abort(); + } } static inline void coroutine_fn @@ -1631,8 +1641,9 @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, * beyond EOF cannot expand the image anyway. */ if (ret == 0 && - end_sector > bs->total_sectors && - req->type != BDRV_TRACKED_DISCARD) { + (req->type == BDRV_TRACKED_TRUNCATE || + end_sector > bs->total_sectors) && + req->type != BDRV_TRACKED_DISCARD) { bs->total_sectors = end_sector; bdrv_parent_cb_resize(bs); bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS); @@ -3111,7 +3122,6 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, int64_t old_size, new_bytes; int ret; - assert(child->perm & BLK_PERM_RESIZE); /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ if (!drv) { @@ -3144,7 +3154,18 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, * concurrently or they might be overwritten by preallocation. */ if (new_bytes) { mark_request_serialising(&req, 1); - wait_serialising_requests(&req); + } + if (bs->read_only) { + error_setg(errp, "Image is read-only"); + ret = -EACCES; + goto out; + } + ret = bdrv_co_write_req_prepare(child, offset - new_bytes, new_bytes, &req, + 0); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to prepare request for truncation"); + goto out; } if (!drv->bdrv_co_truncate) { @@ -3156,13 +3177,6 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, ret = -ENOTSUP; goto out; } - if (bs->read_only) { - error_setg(errp, "Image is read-only"); - ret = -EACCES; - goto out; - } - - assert(!(bs->open_flags & BDRV_O_INACTIVE)); ret = drv->bdrv_co_truncate(bs, offset, prealloc, errp); if (ret < 0) { @@ -3174,9 +3188,10 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, } else { offset = bs->total_sectors * BDRV_SECTOR_SIZE; } - bdrv_dirty_bitmap_truncate(bs, offset); - bdrv_parent_cb_resize(bs); - atomic_inc(&bs->write_gen); + /* It's possible that truncation succeeded but refresh_total_sectors + * failed, but the latter doesn't affect how we should finish the request. + * Pass 0 as the last parameter so that dirty bitmaps etc. are handled. */ + bdrv_co_write_req_finish(child, offset - new_bytes, new_bytes, &req, 0); out: tracked_request_end(&req); From c447afd5783b9237fa51b7a85777007d8d568bfc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 10 Jul 2018 18:19:50 +0100 Subject: [PATCH 2195/2380] Update version for v3.0.0-rc0 release Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 119521432e..13565e69b4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.50 +2.12.90 From 17f4566657df51c5e0cb40f30491e058d74d63c8 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 5 Jul 2018 12:39:30 +0200 Subject: [PATCH 2196/2380] s390x/storage attributes: fix CMMA_BLOCK_SIZE usage The macro CMMA_BLOCK_SIZE was defined but not used, and a hardcoded value was instead used in the code. This patch fixes the value of CMMA_BLOCK_SIZE and uses it in the appropriate place in the code, and fixes another case of hardcoded value in the KVM backend, replacing it with the more appropriate constant KVM_S390_CMMA_SIZE_MAX. Signed-off-by: Claudio Imbrenda Message-Id: <1530787170-3101-1-git-send-email-imbrenda@linux.vnet.ibm.com> Signed-off-by: Cornelia Huck --- hw/s390x/s390-stattrib-kvm.c | 3 ++- hw/s390x/s390-stattrib.c | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index 480551c3db..c7e1f35524 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -105,7 +105,8 @@ static void kvm_s390_stattrib_synchronize(S390StAttribState *sa) KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); MachineState *machine = MACHINE(qdev_get_machine()); unsigned long max = machine->maxram_size / TARGET_PAGE_SIZE; - unsigned long cx, len = 1 << 19; + /* We do not need to reach the maximum buffer size allowed */ + unsigned long cx, len = KVM_S390_SKEYS_MAX / 2; int r; struct kvm_s390_cmma_log clog = { .flags = 0, diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 5161a1659b..766f2015a4 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -21,7 +21,8 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" -#define CMMA_BLOCK_SIZE (1 * KiB) +/* 512KiB cover 2GB of guest memory */ +#define CMMA_BLOCK_SIZE (512 * KiB) #define STATTR_FLAG_EOS 0x01ULL #define STATTR_FLAG_MORE 0x02ULL @@ -203,7 +204,7 @@ static int cmma_save(QEMUFile *f, void *opaque, int final) S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); uint8_t *buf; int r, cx, reallen = 0, ret = 0; - uint32_t buflen = 1 << 19; /* 512kB cover 2GB of guest memory */ + uint32_t buflen = CMMA_BLOCK_SIZE; uint64_t start_gfn = sas->migration_cur_gfn; buf = g_try_malloc(buflen); From 78dcf512efe72e211e6ca7783069364b01a1c05b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 5 Jul 2018 12:58:06 -0300 Subject: [PATCH 2197/2380] error: Remove NULL checks on error_propagate() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch created mechanically by rerunning: $ spatch --sp-file scripts/coccinelle/error_propagate_null.cocci \ --macro-file scripts/cocci-macro-file.h \ --dir . --in-place Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Markus Armbruster Reviewed-by: David Hildenbrand Acked-by: Michael S. Tsirkin Message-Id: <20180705155811.20366-3-f4bug@amsat.org> Signed-off-by: Cornelia Huck --- hw/s390x/virtio-ccw.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index b92a85d0b0..7ddb378d52 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1836,11 +1836,9 @@ static void vhost_vsock_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VHostVSockCCWState *dev = VHOST_VSOCK_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - error_propagate(errp, err); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) From 42eab8dbec2f3fd4a14bd63ab01aa155ce5724a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 5 Jul 2018 18:42:01 +0200 Subject: [PATCH 2198/2380] monitor: fix double-free of request error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qmp_error_response() will free the given error. Fix double-free in later qmp_request_free(). Signed-off-by: Marc-André Lureau Message-Id: <20180705164201.9853-1-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster Fixes: 1cc37471525d03f963bc71d724f0dc9eab888fc1 Signed-off-by: Markus Armbruster --- monitor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/monitor.c b/monitor.c index 3c9c97b73f..7af1f18d13 100644 --- a/monitor.c +++ b/monitor.c @@ -4186,6 +4186,7 @@ static void monitor_qmp_bh_dispatcher(void *data) } else { assert(req_obj->err); rsp = qmp_error_response(req_obj->err); + req_obj->err = NULL; monitor_qmp_respond(req_obj->mon, rsp, NULL); qobject_unref(rsp); } From 26c0ae56386edacc8b0da40264748f59afedb1bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 11 Jul 2018 13:43:57 -0600 Subject: [PATCH 2199/2380] vfio/pci: do not set the PCIDevice 'has_rom' attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI devices needing a ROM allocate an optional MemoryRegion with pci_add_option_rom(). pci_del_option_rom() does the cleanup when the device is destroyed. The only action taken by this routine is to call vmstate_unregister_ram() which clears the id string of the optional ROM RAMBlock and now, also flags the RAMBlock as non-migratable. This was recently added by commit b895de502717 ("migration: discard non-migratable RAMBlocks"), . VFIO devices do their own loading of the PCI option ROM in vfio_pci_size_rom(). The memory region is switched to an I/O region and the PCI attribute 'has_rom' is set but the RAMBlock of the ROM region is not allocated. When the associated PCI device is deleted, pci_del_option_rom() calls vmstate_unregister_ram() which tries to flag a NULL RAMBlock, leading to a SEGV. It seems that 'has_rom' was set to have memory_region_destroy() called, but since commit 469b046ead06 ("memory: remove memory_region_destroy") this is not necessary anymore as the MemoryRegion is freed automagically. Remove the PCIDevice 'has_rom' attribute setting in vfio. Fixes: b895de502717 ("migration: discard non-migratable RAMBlocks") Signed-off-by: Cédric Le Goater Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Williamson --- hw/vfio/pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index a1577dea7f..6cbb8fa054 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -990,7 +990,6 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) pci_register_bar(&vdev->pdev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_SPACE_MEMORY, &vdev->pdev.rom); - vdev->pdev.has_rom = true; vdev->rom_read_failed = false; } From 056de1e894155fbb99e7b43c1c4382d4920cf437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 10 Jul 2018 16:55:57 +0200 Subject: [PATCH 2200/2380] seccomp: allow sched_setscheduler() with SCHED_IDLE policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current and upcoming mesa releases rely on a shader disk cash. It uses a thread job queue with low priority, set with sched_setscheduler(SCHED_IDLE). However, that syscall is rejected by the "resourcecontrol" seccomp qemu filter. Since it should be safe to allow lowering thread priority, let's allow scheduling thread to idle policy. Related to: https://bugzilla.redhat.com/show_bug.cgi?id=1594456 Signed-off-by: Marc-André Lureau Acked-by: Eduardo Otubo --- qemu-seccomp.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/qemu-seccomp.c b/qemu-seccomp.c index 148e4c6f24..9cd8eb9499 100644 --- a/qemu-seccomp.c +++ b/qemu-seccomp.c @@ -34,6 +34,12 @@ struct QemuSeccompSyscall { int32_t num; uint8_t set; + uint8_t narg; + const struct scmp_arg_cmp *arg_cmp; +}; + +const struct scmp_arg_cmp sched_setscheduler_arg[] = { + SCMP_A1(SCMP_CMP_NE, SCHED_IDLE) }; static const struct QemuSeccompSyscall blacklist[] = { @@ -92,7 +98,8 @@ static const struct QemuSeccompSyscall blacklist[] = { { SCMP_SYS(setpriority), QEMU_SECCOMP_SET_RESOURCECTL }, { SCMP_SYS(sched_setparam), QEMU_SECCOMP_SET_RESOURCECTL }, { SCMP_SYS(sched_getparam), QEMU_SECCOMP_SET_RESOURCECTL }, - { SCMP_SYS(sched_setscheduler), QEMU_SECCOMP_SET_RESOURCECTL }, + { SCMP_SYS(sched_setscheduler), QEMU_SECCOMP_SET_RESOURCECTL, + ARRAY_SIZE(sched_setscheduler_arg), sched_setscheduler_arg }, { SCMP_SYS(sched_getscheduler), QEMU_SECCOMP_SET_RESOURCECTL }, { SCMP_SYS(sched_setaffinity), QEMU_SECCOMP_SET_RESOURCECTL }, { SCMP_SYS(sched_getaffinity), QEMU_SECCOMP_SET_RESOURCECTL }, @@ -118,7 +125,8 @@ static int seccomp_start(uint32_t seccomp_opts) continue; } - rc = seccomp_rule_add(ctx, SCMP_ACT_KILL, blacklist[i].num, 0); + rc = seccomp_rule_add_array(ctx, SCMP_ACT_KILL, blacklist[i].num, + blacklist[i].narg, blacklist[i].arg_cmp); if (rc < 0) { goto seccomp_return; } From dc3c89d612252fc461a65f54885a1fe108e9ec05 Mon Sep 17 00:00:00 2001 From: John Arbuckle Date: Mon, 9 Jul 2018 11:02:35 -0400 Subject: [PATCH 2201/2380] ui/cocoa.m: replace scrollingDeltaY with deltaY The NSEvent class method scrollingDeltaY is available for Mac OS 10.7 and newer. Since QEMU supports Mac OS 10.5 and up, we need to be using a method that is available on these version of Mac OS X. The deltaY method is a method that does almost the same thing as scrollingDeltaY and is available on Mac OS 10.5 and up. So we can replace scrollingDeltaY with deltaY. We only check deltaY's value if it is not zero because zero means that the scrolling increment was sufficiently fine that it was only reported in scrollingDeltaY, or that the scrolling was horizontal. Signed-off-by: John Arbuckle Message-id: 20180709150235.7573-1-programmingkidx@gmail.com Reviewed-by: Peter Maydell [PMM: tweak commit message and comment a little] Signed-off-by: Peter Maydell --- ui/cocoa.m | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index 2991ed4f19..cfc70e21a4 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -802,14 +802,20 @@ QemuCocoaView *cocoaView; * This is in-line with standard Mac OS X UI behaviour. */ + /* + * When deltaY is zero, it means that this scrolling event was + * either horizontal, or so fine that it only appears in + * scrollingDeltaY. So we drop the event. + */ + if ([event deltaY] != 0) { /* Determine if this is a scroll up or scroll down event */ - buttons = ([event scrollingDeltaY] > 0) ? - INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; - qemu_input_queue_btn(dcl->con, buttons, true); - qemu_input_event_sync(); - qemu_input_queue_btn(dcl->con, buttons, false); - qemu_input_event_sync(); - + buttons = ([event deltaY] > 0) ? + INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; + qemu_input_queue_btn(dcl->con, buttons, true); + qemu_input_event_sync(); + qemu_input_queue_btn(dcl->con, buttons, false); + qemu_input_event_sync(); + } /* * Since deltaY also reports scroll wheel events we prevent mouse * movement code from executing. From 0e1a582750269d3dde0481ca034b08a5784e430c Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Wed, 11 Jul 2018 15:05:19 +0800 Subject: [PATCH 2202/2380] iotests: 153: Fix dead code This step was left behind my mistake. As suggested by the echoed text, the intention was to test two devices with the same image, with different options. The behavior should be the same as two QEMU processes. Complete it. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- tests/qemu-iotests/153 | 2 ++ tests/qemu-iotests/153.out | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index 673813cd39..0daeb1b005 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -162,6 +162,7 @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do _cleanup_qemu done +test_opts="read-only=off read-only=on read-only=on,force-share=on" for opt1 in $test_opts; do for opt2 in $test_opts; do echo @@ -170,6 +171,7 @@ for opt1 in $test_opts; do done done +echo echo "== Creating ${TEST_IMG}.[abc] ==" | _filter_testdir ( $QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}" diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index 3492ba7a29..93eaf10486 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -369,6 +369,31 @@ _qemu_img_wrapper bench -U -w -c 1 TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': force-share=on can only be used with read-only images Round done + +== Two devices with the same image (read-only=off - read-only=off) == +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=off: Failed to get "write" lock +Is another process using the image? + +== Two devices with the same image (read-only=off - read-only=on) == +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=on: Failed to get shared "write" lock +Is another process using the image? + +== Two devices with the same image (read-only=off - read-only=on,force-share=on) == + +== Two devices with the same image (read-only=on - read-only=off) == +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=off: Failed to get "write" lock +Is another process using the image? + +== Two devices with the same image (read-only=on - read-only=on) == + +== Two devices with the same image (read-only=on - read-only=on,force-share=on) == + +== Two devices with the same image (read-only=on,force-share=on - read-only=off) == + +== Two devices with the same image (read-only=on,force-share=on - read-only=on) == + +== Two devices with the same image (read-only=on,force-share=on - read-only=on,force-share=on) == + == Creating TEST_DIR/t.qcow2.[abc] == Formatting 'TEST_DIR/t.IMGFMT.a', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT Formatting 'TEST_DIR/t.IMGFMT.b', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT From 2f9d4083f7fdafe82138e983a24ef30b81b029d7 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Wed, 11 Jul 2018 14:40:46 +0800 Subject: [PATCH 2203/2380] iotests: nbd: Stop qemu-nbd before remaking image 197 is one example where _make_test_img is used twice without stopping the NBD server in between. An error will occur like this: @@ -26,9 +26,13 @@ === Partial final cluster === +qemu-img: TEST_DIR/t.IMGFMT: Failed to get "resize" lock +Is another process using the image? Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 +Failed to find an available port: Address already in use read 1024/1024 bytes at offset 0 Patch _make_test_img to stop the old qemu-nbd before starting a new one, which fixes this problem, and similarly 215. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- tests/qemu-iotests/common.rc | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index d054cb97fc..44bee16a5e 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -195,6 +195,16 @@ _use_sample_img() fi } +_stop_nbd_server() +{ + if [ -f "${QEMU_TEST_DIR}/qemu-nbd.pid" ]; then + local QEMU_NBD_PID + read QEMU_NBD_PID < "${QEMU_TEST_DIR}/qemu-nbd.pid" + kill ${QEMU_NBD_PID} + rm -f "${QEMU_TEST_DIR}/qemu-nbd.pid" + fi +} + _make_test_img() { # extra qemu-img options can be added by tests @@ -234,6 +244,10 @@ _make_test_img() extra_img_options="-o $optstr $extra_img_options" fi + if [ $IMGPROTO = "nbd" ]; then + _stop_nbd_server + fi + # XXX(hch): have global image options? ( if [ $use_backing = 1 ]; then @@ -274,12 +288,7 @@ _cleanup_test_img() case "$IMGPROTO" in nbd) - if [ -f "${QEMU_TEST_DIR}/qemu-nbd.pid" ]; then - local QEMU_NBD_PID - read QEMU_NBD_PID < "${QEMU_TEST_DIR}/qemu-nbd.pid" - kill ${QEMU_NBD_PID} - rm -f "${QEMU_TEST_DIR}/qemu-nbd.pid" - fi + _stop_nbd_server rm -f "$TEST_IMG_FILE" ;; vxhs) From eb461485f4558e362fab905735b50987505bca44 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Wed, 11 Jul 2018 09:22:27 +0800 Subject: [PATCH 2204/2380] qemu-img: Document copy offloading implications with -S and -c Explicitly enabling zero detection or compression suppresses copy offloading during convert. Document it. Suggested-by: Kevin Wolf Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- qemu-img.texi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qemu-img.texi b/qemu-img.texi index aeb1b9e66c..5853cd18d1 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -96,7 +96,8 @@ will enumerate information about backing files in a disk image chain. Refer below for further description. @item -c -indicates that target image must be compressed (qcow format only) +indicates that target image must be compressed (qcow format only). If this +option is used, copy offloading will not be attempted. @item -h with or without a command shows help and lists the supported formats @@ -115,7 +116,8 @@ in case both @var{-q} and @var{-p} options are used. indicates the consecutive number of bytes that must contain only zeros for qemu-img to create a sparse image during conversion. This value is rounded down to the nearest 512 bytes. You may use the common size suffixes like -@code{k} for kilobytes. +@code{k} for kilobytes. If this option is used, copy offloading will not be +attempted. @item -t @var{cache} specifies the cache mode that should be used with the (destination) file. See From 230ff73904e72dde2d7718c2da407786a1c72e57 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 10 Jul 2018 13:00:40 -0400 Subject: [PATCH 2205/2380] file-posix: specify expected filetypes Adjust each caller of raw_open_common to specify if they are expecting host and character devices or not. Tighten expectations of file types upon open in the common code and refuse types that are not expected. This has two effects: (1) Character and block devices are now considered deprecated for the 'file' driver, which expects only S_IFREG, and (2) no file-posix driver (file, host_cdrom, or host_device) can open directories now. I don't think there's a legitimate reason to open directories as if they were files. This prevents QEMU from opening and attempting to probe a directory inode, which can break in exciting ways. One of those ways is lseek on ext4/xfs, which will return 0x7fffffffffffffff as the file size instead of EISDIR. This can coax QEMU into responding with a confusing "file too big" instead of "Hey, that's not a file". See: https://bugs.launchpad.net/qemu/+bug/1739304/ Signed-off-by: John Snow Signed-off-by: Kevin Wolf --- block/file-posix.c | 39 +++++++++++++++++++++++++++++++-------- qemu-doc.texi | 6 ++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 28824aae65..60af4b3d51 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -438,7 +438,8 @@ static QemuOptsList raw_runtime_opts = { }; static int raw_open_common(BlockDriverState *bs, QDict *options, - int bdrv_flags, int open_flags, Error **errp) + int bdrv_flags, int open_flags, + bool device, Error **errp) { BDRVRawState *s = bs->opaque; QemuOpts *opts; @@ -585,10 +586,32 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, error_setg_errno(errp, errno, "Could not stat file"); goto fail; } - if (S_ISREG(st.st_mode)) { - s->discard_zeroes = true; - s->has_fallocate = true; + + if (!device) { + if (S_ISBLK(st.st_mode)) { + warn_report("Opening a block device as a file using the '%s' " + "driver is deprecated", bs->drv->format_name); + } else if (S_ISCHR(st.st_mode)) { + warn_report("Opening a character device as a file using the '%s' " + "driver is deprecated", bs->drv->format_name); + } else if (!S_ISREG(st.st_mode)) { + error_setg(errp, "A regular file was expected by the '%s' driver, " + "but something else was given", bs->drv->format_name); + ret = -EINVAL; + goto fail; + } else { + s->discard_zeroes = true; + s->has_fallocate = true; + } + } else { + if (!(S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { + error_setg(errp, "'%s' driver expects either " + "a character or block device", bs->drv->format_name); + ret = -EINVAL; + goto fail; + } } + if (S_ISBLK(st.st_mode)) { #ifdef BLKDISCARDZEROES unsigned int arg; @@ -641,7 +664,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, BDRVRawState *s = bs->opaque; s->type = FTYPE_FILE; - return raw_open_common(bs, options, flags, 0, errp); + return raw_open_common(bs, options, flags, 0, false, errp); } typedef enum { @@ -2939,7 +2962,7 @@ hdev_open_Mac_error: s->type = FTYPE_FILE; - ret = raw_open_common(bs, options, flags, 0, &local_err); + ret = raw_open_common(bs, options, flags, 0, true, &local_err); if (ret < 0) { error_propagate(errp, local_err); #if defined(__APPLE__) && defined(__MACH__) @@ -3170,7 +3193,7 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, s->type = FTYPE_CD; /* open will not fail even if no CD is inserted, so add O_NONBLOCK */ - return raw_open_common(bs, options, flags, O_NONBLOCK, errp); + return raw_open_common(bs, options, flags, O_NONBLOCK, true, errp); } static int cdrom_probe_device(const char *filename) @@ -3284,7 +3307,7 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, s->type = FTYPE_CD; - ret = raw_open_common(bs, options, flags, 0, &local_err); + ret = raw_open_common(bs, options, flags, 0, true, &local_err); if (ret) { error_propagate(errp, local_err); return ret; diff --git a/qemu-doc.texi b/qemu-doc.texi index 0fbf8b108b..1047c407e7 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2969,6 +2969,12 @@ replacement since it is not needed anymore. The @option{-enable-hax} option has been replaced by @option{-accel hax}. Both options have been introduced in QEMU version 2.9.0. +@subsection -drive file=json:@{...@{'driver':'file'@}@} (since 3.0) + +The 'file' driver for drives is no longer appropriate for character or host +devices and will only accept regular files (S_IFREG). The correct driver +for these file types is 'host_cdrom' or 'host_device' as appropriate. + @section QEMU Machine Protocol (QMP) commands @subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) From 2d4cb49ddaa219ed3f5a985ecf8b188aeb2b3d6b Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 10 Jul 2018 13:00:41 -0400 Subject: [PATCH 2206/2380] iotests: add test 226 for file driver types Test that we're rejecting what we ought to for file, host_driver and host_cdrom drivers. Test that we're seeing the deprecated message for block and chardevs on the file driver. Signed-off-by: John Snow Signed-off-by: Kevin Wolf --- tests/qemu-iotests/226 | 66 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/226.out | 26 +++++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 93 insertions(+) create mode 100755 tests/qemu-iotests/226 create mode 100644 tests/qemu-iotests/226.out diff --git a/tests/qemu-iotests/226 b/tests/qemu-iotests/226 new file mode 100755 index 0000000000..460aea2fc9 --- /dev/null +++ b/tests/qemu-iotests/226 @@ -0,0 +1,66 @@ +#!/bin/bash +# +# This test covers expected filetypes for the file, host_cdrom and +# host_device drivers. +# +# Copyright (C) 2018 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 . +# + +# creator +owner=jsnow@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +_cleanup() +{ + rmdir "$TEST_IMG" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +# Generic format, but tests file-protocol specific error handling +_supported_fmt generic +_supported_proto file +_supported_os Linux + +# Create something decidedly not a file, blockdev or chardev... +mkdir "$TEST_IMG" + +for PROTO in "file" "host_device" "host_cdrom"; do + echo + echo "=== Testing with driver:$PROTO ===" + echo + echo "== Testing RO ==" + $QEMU_IO -c "open -r -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_imgfmt | _filter_testdir + $QEMU_IO -c "open -r -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt + echo "== Testing RW ==" + $QEMU_IO -c "open -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_imgfmt | _filter_testdir + $QEMU_IO -c "open -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt +done + +# success, all done +echo +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/226.out b/tests/qemu-iotests/226.out new file mode 100644 index 0000000000..8c0d060ffc --- /dev/null +++ b/tests/qemu-iotests/226.out @@ -0,0 +1,26 @@ +QA output created by 226 + +=== Testing with driver:file === + +== Testing RO == +can't open: A regular file was expected by the 'file' driver, but something else was given +warning: Opening a character device as a file using the 'file' driver is deprecated +== Testing RW == +can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory +warning: Opening a character device as a file using the 'file' driver is deprecated + +=== Testing with driver:host_device === + +== Testing RO == +can't open: 'host_device' driver expects either a character or block device +== Testing RW == +can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory + +=== Testing with driver:host_cdrom === + +== Testing RO == +can't open: 'host_cdrom' driver expects either a character or block device +== Testing RW == +can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory + +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 1c9f679821..68f66259b2 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -223,3 +223,4 @@ 222 rw auto quick 223 rw auto quick 225 rw auto quick +226 auto quick From 740842c9656cd5dbc9ccf2ea0c3a74f0ba35144a Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 11 Jul 2018 17:58:13 -0300 Subject: [PATCH 2207/2380] scsi-disk: Block Device Characteristics emulation fix The current BDC VPD page (page 0xb1) is too short. This can be seen running sg_utils: $ sg_vpd --page=bdc /dev/sda Block device characteristics VPD page (SBC): Block device characteristics VPD page length too short=8 By the SCSI spec, the expected size of the SBC page is 0x40. There is no telling how the guest will behave with a shorter message - it can ignore it, or worse, make (wrong) assumptions. This patch fixes the emulation by setting the size to 0x40. This is the output of the previous sg_vpd command after applying it: $ sg_vpd --page=bdc /dev/sda -v inquiry cdb: 12 01 b1 00 fc 00 Block device characteristics VPD page (SBC): [PQual=0 Peripheral device type: disk] Medium rotation rate is not reported Product type: Not specified WABEREQ=0 WACEREQ=0 Nominal form factor not reported FUAB=0 VBULS=0 To improve readability, this patch also adds the VBULS value explictly and add comments on the existing fields we're setting. Signed-off-by: Daniel Henrique Barboza Acked-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi/scsi-disk.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index d7df357029..5bb390773b 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -775,11 +775,12 @@ int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) } case 0xb1: /* block device characteristics */ { - buflen = 8; + buflen = 0x40; outbuf[4] = (s->rotation_rate >> 8) & 0xff; outbuf[5] = s->rotation_rate & 0xff; - outbuf[6] = 0; - outbuf[7] = 0; + outbuf[6] = 0; /* PRODUCT TYPE */ + outbuf[7] = 0; /* WABEREQ | WACEREQ | NOMINAL FORM FACTOR */ + outbuf[8] = 0; /* VBULS */ break; } case 0xb2: /* thin provisioning */ From 8dcd3c9b91a300c86e315d7e5427dce1383f7387 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 12 Jul 2018 15:00:10 +0200 Subject: [PATCH 2208/2380] qemu-img: align result of is_allocated_sectors We currently don't enforce that the sparse segments we detect during convert are aligned. This leads to unnecessary and costly read-modify-write cycles either internally in Qemu or in the background on the storage device as nearly all modern filesystems or hardware have a 4k alignment internally. This patch modifies is_allocated_sectors so that its *pnum result will always end at an alignment boundary. This way all requests will end at an alignment boundary. The start of all requests will also be aligned as long as the results of get_block_status do not lead to an unaligned offset. The number of RMW cycles when converting an example image [1] to a raw device that has 4k sector size is about 4600 4k read requests to perform a total of about 15000 write requests. With this path the additional 4600 read requests are eliminated while the number of total write requests stays constant. [1] https://cloud-images.ubuntu.com/releases/16.04/release/ubuntu-16.04-server-cloudimg-amd64-disk1.vmdk Signed-off-by: Peter Lieven Signed-off-by: Kevin Wolf --- qemu-img.c | 44 ++++++++++++++++++++++++++++++++------ tests/qemu-iotests/122.out | 18 +++++++--------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index f4074ebf75..4a7ce43dc9 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1105,11 +1105,15 @@ static int64_t find_nonzero(const uint8_t *buf, int64_t n) * * 'pnum' is set to the number of sectors (including and immediately following * the first one) that are known to be in the same allocated/unallocated state. + * The function will try to align the end offset to alignment boundaries so + * that the request will at least end aligned and consequtive requests will + * also start at an aligned offset. */ -static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum) +static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum, + int64_t sector_num, int alignment) { bool is_zero; - int i; + int i, tail; if (n <= 0) { *pnum = 0; @@ -1122,6 +1126,23 @@ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum) break; } } + + tail = (sector_num + i) & (alignment - 1); + if (tail) { + if (is_zero && i <= tail) { + /* treat unallocated areas which only consist + * of a small tail as allocated. */ + is_zero = false; + } + if (!is_zero) { + /* align up end offset of allocated areas. */ + i += alignment - tail; + i = MIN(i, n); + } else { + /* align down end offset of zero areas. */ + i -= tail; + } + } *pnum = i; return !is_zero; } @@ -1132,7 +1153,7 @@ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum) * breaking up write requests for only small sparse areas. */ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, - int min) + int min, int64_t sector_num, int alignment) { int ret; int num_checked, num_used; @@ -1141,7 +1162,7 @@ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, min = n; } - ret = is_allocated_sectors(buf, n, pnum); + ret = is_allocated_sectors(buf, n, pnum, sector_num, alignment); if (!ret) { return ret; } @@ -1149,13 +1170,15 @@ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, num_used = *pnum; buf += BDRV_SECTOR_SIZE * *pnum; n -= *pnum; + sector_num += *pnum; num_checked = num_used; while (n > 0) { - ret = is_allocated_sectors(buf, n, pnum); + ret = is_allocated_sectors(buf, n, pnum, sector_num, alignment); buf += BDRV_SECTOR_SIZE * *pnum; n -= *pnum; + sector_num += *pnum; num_checked += *pnum; if (ret) { num_used = num_checked; @@ -1560,6 +1583,7 @@ typedef struct ImgConvertState { bool wr_in_order; bool copy_range; int min_sparse; + int alignment; size_t cluster_sectors; size_t buf_sectors; long num_coroutines; @@ -1724,7 +1748,8 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, * zeroed. */ if (!s->min_sparse || (!s->compressed && - is_allocated_sectors_min(buf, n, &n, s->min_sparse)) || + is_allocated_sectors_min(buf, n, &n, s->min_sparse, + sector_num, s->alignment)) || (s->compressed && !buffer_is_zero(buf, n * BDRV_SECTOR_SIZE))) { @@ -2368,6 +2393,13 @@ static int img_convert(int argc, char **argv) out_bs->bl.pdiscard_alignment >> BDRV_SECTOR_BITS))); + /* try to align the write requests to the destination to avoid unnecessary + * RMW cycles. */ + s.alignment = MAX(pow2floor(s.min_sparse), + DIV_ROUND_UP(out_bs->bl.request_alignment, + BDRV_SECTOR_SIZE)); + assert(is_power_of_2(s.alignment)); + if (skip_create) { int64_t output_sectors = blk_nb_sectors(s.target); if (output_sectors < 0) { diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out index 6c7ee1da6c..c576705284 100644 --- a/tests/qemu-iotests/122.out +++ b/tests/qemu-iotests/122.out @@ -194,12 +194,12 @@ wrote 1024/1024 bytes at offset 17408 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) convert -S 4k -[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 1024, "length": 7168, "depth": 0, "zero": true, "data": false}, -{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false}, -{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}] +[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 4096, "length": 4096, "depth": 0, "zero": true, "data": false}, +{ "start": 8192, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 12288, "length": 4096, "depth": 0, "zero": true, "data": false}, +{ "start": 16384, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 20480, "length": 67088384, "depth": 0, "zero": true, "data": false}] convert -c -S 4k [{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}, @@ -210,10 +210,8 @@ convert -c -S 4k { "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}] convert -S 8k -[{ "start": 0, "length": 9216, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false}, -{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}] +[{ "start": 0, "length": 24576, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 24576, "length": 67084288, "depth": 0, "zero": true, "data": false}] convert -c -S 8k [{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}, From 9277d81f5c2c6f4d0b5e47c8476eb7ee7e5c0beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Tue, 12 Jun 2018 09:51:50 +0300 Subject: [PATCH 2209/2380] docs: Grammar and spelling fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ville Skyttä Reviewed-by: Peter Maydell Reviewed-by: Eric Blake Message-id: 20180612065150.21110-1-ville.skytta@iki.fi Signed-off-by: Peter Maydell --- docs/colo-proxy.txt | 2 +- docs/config/mach-virt-graphical.cfg | 2 +- docs/config/mach-virt-serial.cfg | 2 +- docs/config/q35-emulated.cfg | 2 +- docs/config/q35-virtio-graphical.cfg | 2 +- docs/config/q35-virtio-serial.cfg | 2 +- docs/devel/migration.rst | 2 +- docs/devel/multi-thread-tcg.txt | 2 +- docs/interop/qcow2.txt | 6 +++--- docs/interop/vhost-user.txt | 4 ++-- docs/memory-hotplug.txt | 2 +- docs/multiseat.txt | 2 +- docs/qemu-block-drivers.texi | 2 +- docs/qemupciserial.inf | 2 +- docs/specs/acpi_nvdimm.txt | 3 ++- docs/specs/ppc-spapr-hcalls.txt | 2 +- docs/specs/tpm.txt | 2 +- docs/usb2.txt | 2 +- 18 files changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/colo-proxy.txt b/docs/colo-proxy.txt index 8b726ea094..1f8e4b4e77 100644 --- a/docs/colo-proxy.txt +++ b/docs/colo-proxy.txt @@ -145,7 +145,7 @@ and redirect indev's packet to filter. COLO-compare, we do packet comparing job. Packets coming from the primary char indev will be sent to outdev. Packets coming from the secondary char dev will be dropped after comparing. -COLO-comapre need two input chardev and one output chardev: +COLO-compare needs two input chardevs and one output chardev: primary_in=chardev1-id (source: primary send packet) secondary_in=chardev2-id (source: secondary send packet) outdev=chardev3-id diff --git a/docs/config/mach-virt-graphical.cfg b/docs/config/mach-virt-graphical.cfg index 0fdf6846dd..d6d31b17f5 100644 --- a/docs/config/mach-virt-graphical.cfg +++ b/docs/config/mach-virt-graphical.cfg @@ -185,7 +185,7 @@ # attached to it. # # We also create an optical disk, mostly for installation -# purposes: once the guest OS has been succesfully +# purposes: once the guest OS has been successfully # installed, the guest will no longer boot from optical # media. If you don't want, or no longer want, to have an # optical disk in the guest you can safely comment out diff --git a/docs/config/mach-virt-serial.cfg b/docs/config/mach-virt-serial.cfg index aee9f1c5a1..18a7c83731 100644 --- a/docs/config/mach-virt-serial.cfg +++ b/docs/config/mach-virt-serial.cfg @@ -191,7 +191,7 @@ # attached to it. # # We also create an optical disk, mostly for installation -# purposes: once the guest OS has been succesfully +# purposes: once the guest OS has been successfully # installed, the guest will no longer boot from optical # media. If you don't want, or no longer want, to have an # optical disk in the guest you can safely comment out diff --git a/docs/config/q35-emulated.cfg b/docs/config/q35-emulated.cfg index c6416d6545..99ac918e78 100644 --- a/docs/config/q35-emulated.cfg +++ b/docs/config/q35-emulated.cfg @@ -130,7 +130,7 @@ # it to that controller so that the guest can use it. # # We also create an optical disk, mostly for installation -# purposes: once the guest OS has been succesfully +# purposes: once the guest OS has been successfully # installed, the guest will no longer boot from optical # media. If you don't want, or no longer want, to have an # optical disk in the guest you can safely comment out diff --git a/docs/config/q35-virtio-graphical.cfg b/docs/config/q35-virtio-graphical.cfg index 28bde2fc57..4207f11e4f 100644 --- a/docs/config/q35-virtio-graphical.cfg +++ b/docs/config/q35-virtio-graphical.cfg @@ -136,7 +136,7 @@ # attached to it. # # We also create an optical disk, mostly for installation -# purposes: once the guest OS has been succesfully +# purposes: once the guest OS has been successfully # installed, the guest will no longer boot from optical # media. If you don't want, or no longer want, to have an # optical disk in the guest you can safely comment out diff --git a/docs/config/q35-virtio-serial.cfg b/docs/config/q35-virtio-serial.cfg index c33c9cc07a..d2830aec5e 100644 --- a/docs/config/q35-virtio-serial.cfg +++ b/docs/config/q35-virtio-serial.cfg @@ -141,7 +141,7 @@ # attached to it. # # We also create an optical disk, mostly for installation -# purposes: once the guest OS has been succesfully +# purposes: once the guest OS has been successfully # installed, the guest will no longer boot from optical # media. If you don't want, or no longer want, to have an # optical disk in the guest you can safely comment out diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index 40f136f6be..6ed3fce061 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -37,7 +37,7 @@ over any transport. - tcp migration: do the migration using tcp sockets - unix migration: do the migration using unix sockets - exec migration: do the migration using the stdin/stdout through a process. -- fd migration: do the migration using an file descriptor that is +- fd migration: do the migration using a file descriptor that is passed to QEMU. QEMU doesn't care how this file descriptor is opened. In addition, support is included for migration using RDMA, which diff --git a/docs/devel/multi-thread-tcg.txt b/docs/devel/multi-thread-tcg.txt index 06530be1e9..782bebc28b 100644 --- a/docs/devel/multi-thread-tcg.txt +++ b/docs/devel/multi-thread-tcg.txt @@ -316,7 +316,7 @@ other cores sharing access to the memory. The classic example is the x86 cmpxchg instruction. The second type offer a pair of load/store instructions which offer a -guarantee that an region of memory has not been touched between the +guarantee that a region of memory has not been touched between the load and store instructions. An example of this is ARM's ldrex/strex pair where the strex instruction will return a flag indicating a successful store only if no other CPU has accessed the memory region diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt index 8e1547ded2..845d40a086 100644 --- a/docs/interop/qcow2.txt +++ b/docs/interop/qcow2.txt @@ -326,8 +326,8 @@ in the image file. It contains pointers to the second level structures which are called refcount blocks and are exactly one cluster in size. -Given a offset into the image file, the refcount of its cluster can be obtained -as follows: +Given an offset into the image file, the refcount of its cluster can be +obtained as follows: refcount_block_entries = (cluster_size * 8 / refcount_bits) @@ -365,7 +365,7 @@ The L1 table has a variable size (stored in the header) and may use multiple clusters, however it must be contiguous in the image file. L2 tables are exactly one cluster in size. -Given a offset into the virtual disk, the offset into the image file can be +Given an offset into the virtual disk, the offset into the image file can be obtained as follows: l2_entries = (cluster_size / sizeof(uint64_t)) diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt index d51fd58242..f59667f498 100644 --- a/docs/interop/vhost-user.txt +++ b/docs/interop/vhost-user.txt @@ -108,12 +108,12 @@ Depending on the request type, payload can be: IOVA: a 64-bit I/O virtual address programmed by the guest Size: a 64-bit size User address: a 64-bit user address - Permissions: a 8-bit value: + Permissions: an 8-bit value: - 0: No access - 1: Read access - 2: Write access - 3: Read/Write access - Type: a 8-bit IOTLB message type: + Type: an 8-bit IOTLB message type: - 1: IOTLB miss - 2: IOTLB update - 3: IOTLB invalidate diff --git a/docs/memory-hotplug.txt b/docs/memory-hotplug.txt index d96397c1af..6aa5e17e26 100644 --- a/docs/memory-hotplug.txt +++ b/docs/memory-hotplug.txt @@ -62,7 +62,7 @@ It's also possible to start a guest with memory cold-plugged into the hotpluggable memory slots. This might seem counterintuitive at first, but this allows for a lot of flexibility when using the file backend. -In the following command-line example, a 8GB guest is created where 6GB +In the following command-line example, an 8GB guest is created where 6GB comes from regular RAM, 1GB is a 1GB hugepage page and 256MB is from 2MB pages. Also, the guest has additional memory slots to hotplug more 2GB if needed: diff --git a/docs/multiseat.txt b/docs/multiseat.txt index dc28cdb613..8dde36c845 100644 --- a/docs/multiseat.txt +++ b/docs/multiseat.txt @@ -62,7 +62,7 @@ to its own window so you can see both display devices side-by-side. For vnc some additional configuration on the command line is needed. We'll create two vnc server instances, and bind the second one to the -second seat, simliar to input devices: +second seat, similar to input devices: -display vnc=:1,id=primary \ -display vnc=:2,id=secondary,display=video.2 diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi index f1793692bb..38e9f34cc9 100644 --- a/docs/qemu-block-drivers.texi +++ b/docs/qemu-block-drivers.texi @@ -524,7 +524,7 @@ You can create a cloned image from the existing snapshot. @example qemu-img create -b sheepdog:///@var{base}#@var{tag} sheepdog:///@var{image} @end example -where @var{base} is a image name of the source snapshot and @var{tag} +where @var{base} is an image name of the source snapshot and @var{tag} is its tag name. You can use an unix socket instead of an inet socket: diff --git a/docs/qemupciserial.inf b/docs/qemupciserial.inf index 6f7eef49cc..7ca766745d 100644 --- a/docs/qemupciserial.inf +++ b/docs/qemupciserial.inf @@ -1,7 +1,7 @@ ; qemupciserial.inf for QEMU, based on MSPORTS.INF ; The driver itself is shipped with Windows (serial.sys). This is -; just a inf file to tell windows which pci id the serial pci card +; just an inf file to tell windows which pci id the serial pci card ; emulated by qemu has, and to apply a name tag to it which windows ; will show in the device manager. diff --git a/docs/specs/acpi_nvdimm.txt b/docs/specs/acpi_nvdimm.txt index 3f322e6f55..3ec42ecbce 100644 --- a/docs/specs/acpi_nvdimm.txt +++ b/docs/specs/acpi_nvdimm.txt @@ -72,7 +72,8 @@ for NVDIMM ACPI. Memory: QEMU uses BIOS Linker/loader feature to ask BIOS to allocate a memory - page and dynamically patch its into a int32 object named "MEMA" in ACPI. + page and dynamically patch its address into an int32 object named "MEMA" + in ACPI. This page is RAM-based and it is used to transfer data between _DSM method and QEMU. If ACPI has control, this pages is owned by ACPI which diff --git a/docs/specs/ppc-spapr-hcalls.txt b/docs/specs/ppc-spapr-hcalls.txt index 5bd8eab78f..93fe3da91b 100644 --- a/docs/specs/ppc-spapr-hcalls.txt +++ b/docs/specs/ppc-spapr-hcalls.txt @@ -10,7 +10,7 @@ calls which are mostly used as a private interface between the firmware running in the guest and QEMU. All those hypercalls start at hcall number 0xf000 which correspond -to a implementation specific range in PAPR. +to an implementation specific range in PAPR. - H_RTAS (0xf000) diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt index 70ad4a0cba..0e9bbebe1d 100644 --- a/docs/specs/tpm.txt +++ b/docs/specs/tpm.txt @@ -252,7 +252,7 @@ swtpm socket --tpmstate dir=/tmp/mytpm1 \ --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ --log level=20 --tpm2 -In the 2nd terminal restore the state of the VM using the additonal +In the 2nd terminal restore the state of the VM using the additional '-incoming' option. qemu-system-x86_64 -display sdl -accel kvm \ diff --git a/docs/usb2.txt b/docs/usb2.txt index e2fa2cfde0..f63c8d9465 100644 --- a/docs/usb2.txt +++ b/docs/usb2.txt @@ -41,7 +41,7 @@ the PIIX3 chipset. The USB 1.1 bus will carry the name "usb-bus.0". You can use the standard -device switch to add a EHCI controller to your virtual machine. It is strongly recommended to specify an ID for -the controller so the USB 2.0 bus gets a individual name, for example +the controller so the USB 2.0 bus gets an individual name, for example '-device usb-ehci,id=ehci". This will give you a USB 2.0 bus named "ehci.0". From 4a5457616d5b3116a8ae76748f9c493a4b9e07f0 Mon Sep 17 00:00:00 2001 From: Shivaprasad G Bhat Date: Fri, 13 Jul 2018 07:34:46 -0500 Subject: [PATCH 2210/2380] linux-user: ppc64: use the correct values for F_*LK64s Qemu includes the glibc headers for the host defines and target headers are part of the qemu source themselves. The glibc has the F_GETLK64, F_SETLK64 and F_SETLKW64 defined to 12, 13 and 14 for all archs in sysdeps/unix/sysv/linux/bits/fcntl-linux.h. The linux kernel generic definition for F_*LK is 5, 6 & 7 and F_*LK64* is 12,13, and 14 as seen in include/uapi/asm-generic/fcntl.h. On 64bit machine, by default the kernel assumes all F_*LK to 64bit calls and doesnt support use of F_*LK64* as can be seen in include/linux/fcntl.h in linux source. On x86_64 host, the values for F_*LK64* are set to 5, 6 and 7 explicitly in /usr/include/x86_64-linux-gnu/bits/fcntl.h by the glibc. Whereas, a PPC64 host doesn't have such a definition in /usr/include/powerpc64le-linux-gnu/bits/fcntl.h by the glibc. So, the sources on PPC64 host sees the default value of F_*LK64* as 12, 13 & 14(fcntl-linux.h). Since the 64bit kernel doesnt support 12, 13 & 14; the glibc fcntl syscall implementation(__libc_fcntl*(), __fcntl64_nocancel) does the F_*LK64* value convertion back to F_*LK* values on PPC64 as seen in sysdeps/unix/sysv/linux/powerpc/powerpc64/sysdep.h with FCNTL_ADJUST_CMD() macro. Whereas on x86_64 host the values for F_*LK64* are set to 5, 6 and 7 and no adjustments are needed. Since qemu doesnt use the glibc fcntl, but makes the safe_syscall* on its own, the PPC64 qemu is calling the syscall with 12, 13, and 14(without adjustment) and they all fail. The fcntl calls to F_GETLK/F_SETLK|W all fail by all pplications run on PPC64 host user emulation. The fix here could be to see why on PPC64 the glibc is still keeping F_*LK64* different from F_*LK and why adjusting them to 5, 6 and 7 before the syscall for PPC only. See if we can make the /usr/include/powerpc64le-linux-gnu/bits/fcntl.h to have the values 5, 6 & 7 just like x86_64 and remove the adjustment code in glibc. That way, qemu sources see the kernel supported values in glibc headers. OR On PPC64 host, qemu sources see both F_*LK & F_*LK64* as same and set to 12, 13 and 14 because __USE_FILE_OFFSET64 is defined in qemu sources(also refer sysdeps/unix/sysv/linux/bits/fcntl-linux.h). Do the value adjustment just like it is done by glibc source by using F_GETLK value of 5. That way, we make the syscalls with the actual supported values in Qemu. The patch is taking this approach. Signed-off-by: Shivaprasad G Bhat Reviewed-by: David Gibson Reviewed-by: Laurent Vivier Message-Id: <153148521235.87746.14142430397318741182.stgit@lep8c.aus.stglabs.ibm.com> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 124 +++++++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 45 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e4b1b7d7da..b8b7bced9f 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6545,63 +6545,97 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, /* warning : doesn't handle linux specific flags... */ static int target_to_host_fcntl_cmd(int cmd) { + int ret; + switch(cmd) { - case TARGET_F_DUPFD: - case TARGET_F_GETFD: - case TARGET_F_SETFD: - case TARGET_F_GETFL: - case TARGET_F_SETFL: - return cmd; - case TARGET_F_GETLK: - return F_GETLK64; - case TARGET_F_SETLK: - return F_SETLK64; - case TARGET_F_SETLKW: - return F_SETLKW64; - case TARGET_F_GETOWN: - return F_GETOWN; - case TARGET_F_SETOWN: - return F_SETOWN; - case TARGET_F_GETSIG: - return F_GETSIG; - case TARGET_F_SETSIG: - return F_SETSIG; + case TARGET_F_DUPFD: + case TARGET_F_GETFD: + case TARGET_F_SETFD: + case TARGET_F_GETFL: + case TARGET_F_SETFL: + ret = cmd; + break; + case TARGET_F_GETLK: + ret = F_GETLK64; + break; + case TARGET_F_SETLK: + ret = F_SETLK64; + break; + case TARGET_F_SETLKW: + ret = F_SETLKW64; + break; + case TARGET_F_GETOWN: + ret = F_GETOWN; + break; + case TARGET_F_SETOWN: + ret = F_SETOWN; + break; + case TARGET_F_GETSIG: + ret = F_GETSIG; + break; + case TARGET_F_SETSIG: + ret = F_SETSIG; + break; #if TARGET_ABI_BITS == 32 - case TARGET_F_GETLK64: - return F_GETLK64; - case TARGET_F_SETLK64: - return F_SETLK64; - case TARGET_F_SETLKW64: - return F_SETLKW64; + case TARGET_F_GETLK64: + ret = F_GETLK64; + break; + case TARGET_F_SETLK64: + ret = F_SETLK64; + break; + case TARGET_F_SETLKW64: + ret = F_SETLKW64; + break; #endif - case TARGET_F_SETLEASE: - return F_SETLEASE; - case TARGET_F_GETLEASE: - return F_GETLEASE; + case TARGET_F_SETLEASE: + ret = F_SETLEASE; + break; + case TARGET_F_GETLEASE: + ret = F_GETLEASE; + break; #ifdef F_DUPFD_CLOEXEC - case TARGET_F_DUPFD_CLOEXEC: - return F_DUPFD_CLOEXEC; + case TARGET_F_DUPFD_CLOEXEC: + ret = F_DUPFD_CLOEXEC; + break; #endif - case TARGET_F_NOTIFY: - return F_NOTIFY; + case TARGET_F_NOTIFY: + ret = F_NOTIFY; + break; #ifdef F_GETOWN_EX - case TARGET_F_GETOWN_EX: - return F_GETOWN_EX; + case TARGET_F_GETOWN_EX: + ret = F_GETOWN_EX; + break; #endif #ifdef F_SETOWN_EX - case TARGET_F_SETOWN_EX: - return F_SETOWN_EX; + case TARGET_F_SETOWN_EX: + ret = F_SETOWN_EX; + break; #endif #ifdef F_SETPIPE_SZ - case TARGET_F_SETPIPE_SZ: - return F_SETPIPE_SZ; - case TARGET_F_GETPIPE_SZ: - return F_GETPIPE_SZ; + case TARGET_F_SETPIPE_SZ: + ret = F_SETPIPE_SZ; + break; + case TARGET_F_GETPIPE_SZ: + ret = F_GETPIPE_SZ; + break; #endif - default: - return -TARGET_EINVAL; + default: + ret = -TARGET_EINVAL; + break; } - return -TARGET_EINVAL; + +#if defined(__powerpc64__) + /* On PPC64, glibc headers has the F_*LK* defined to 12, 13 and 14 and + * is not supported by kernel. The glibc fcntl call actually adjusts + * them to 5, 6 and 7 before making the syscall(). Since we make the + * syscall directly, adjust to what is supported by the kernel. + */ + if (ret >= F_GETLK64 && ret <= F_SETLKW64) { + ret -= F_GETLK64 - 5; + } +#endif + + return ret; } #define FLOCK_TRANSTBL \ From af8ab2bf223e1e4406108708b47476ef0d148a52 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 13 Jul 2018 14:58:05 +0200 Subject: [PATCH 2211/2380] linux-user: convert remaining fcntl() to safe_fcntl() Commit 435da5e709 didn't convert a fcntl() call to safe_fcntl() for TARGET_NR_fcntl64 case. There is no reason to not use it in this case. Fixes: 435da5e709 linux-user: Use safe_syscall wrapper for fcntl Signed-off-by: Laurent Vivier Message-Id: <20180713125805.10749-1-laurent@vivier.eu> --- linux-user/syscall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b8b7bced9f..aa4f3eb1c8 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -11764,7 +11764,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (ret) { break; } - ret = get_errno(fcntl(arg1, cmd, &fl)); + ret = get_errno(safe_fcntl(arg1, cmd, &fl)); if (ret == 0) { ret = copyto(arg3, &fl); } From dc18baaef36d95e5a7b4fbc60daff3d92afe737c Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sat, 14 Jul 2018 21:35:53 +0200 Subject: [PATCH 2212/2380] linux-user: fix mmap_find_vma_reserved() The value given by mmap_find_vma_reserved() is used with mmap(), so it is needed to be aligned with the host page size. Since commit 18e80c55bb, reserved_va is only aligned to TARGET_PAGE_SIZE, and it works well if this size is greater or equal to the host page size. But ppc64 hosts have 64kB page size and when we start a 4kiB page size guest (like i386), it fails when it tries to mmap the stack: mmap stack: Invalid argument Fixes: 18e80c55bb (linux-user: Tidy and enforce reserved_va initialization) Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20180714193553.30846-1-laurent@vivier.eu> --- linux-user/main.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 52b5a618fe..ea00dd9057 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -78,14 +78,7 @@ int have_guest_base; # endif #endif -/* That said, reserving *too* much vm space via mmap can run into problems - with rlimits, oom due to page table creation, etc. We will still try it, - if directed by the command-line option, but not by default. */ -#if HOST_LONG_BITS == 64 && TARGET_VIRT_ADDR_SPACE_BITS <= 32 -unsigned long reserved_va = MAX_RESERVED_VA; -#else unsigned long reserved_va; -#endif static void usage(int exitcode); @@ -672,6 +665,18 @@ int main(int argc, char **argv, char **envp) /* init tcg before creating CPUs and to get qemu_host_page_size */ tcg_exec_init(0); + /* Reserving *too* much vm space via mmap can run into problems + with rlimits, oom due to page table creation, etc. We will still try it, + if directed by the command-line option, but not by default. */ + if (HOST_LONG_BITS == 64 && + TARGET_VIRT_ADDR_SPACE_BITS <= 32 && + reserved_va == 0) { + /* reserved_va must be aligned with the host page size + * as it is used with mmap() + */ + reserved_va = MAX_RESERVED_VA & qemu_host_page_mask; + } + cpu = cpu_create(cpu_type); env = cpu->env_ptr; cpu_reset(cpu); From 1d3d1b23e1c8f52ec431ddaa8deea1322bc25cbf Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 12 Jul 2018 00:12:44 +0200 Subject: [PATCH 2213/2380] Zero out the host's `msg_control` buffer If this is not done, qemu would drop any control message after the first one. This is because glibc's `CMSG_NXTHDR` macro accesses the uninitialized cmsghdr's length field in order to find out if the message fits into the `msg_control` buffer, wrongly assuming that it doesn't because the length field contains garbage. Accessing the length field is fine for completed messages we receive from the kernel, but is - as far as I know - not needed since the kernel won't return such an invalid cmsghdr in the first place. This is tracked as this glibc bug: https://sourceware.org/bugzilla/show_bug.cgi?id=13500 It's probably also a good idea to bail with an error if `CMSG_NXTHDR` returns NULL but `TARGET_CMSG_NXTHDR` doesn't (ie. we still expect cmsgs). Signed-off-by: Jonas Schievink Reviewed-by: Laurent Vivier Message-Id: <20180711221244.31869-1-jonasschievink@gmail.com> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index aa4f3eb1c8..3df3bdffb2 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3843,6 +3843,8 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, } msg.msg_controllen = 2 * tswapal(msgp->msg_controllen); msg.msg_control = alloca(msg.msg_controllen); + memset(msg.msg_control, 0, msg.msg_controllen); + msg.msg_flags = tswap32(msgp->msg_flags); count = tswapal(msgp->msg_iovlen); From 593a1cdd66ac83c1cefdd4cc5dd6338006fc202c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 9 Jul 2018 19:02:36 +0200 Subject: [PATCH 2214/2380] sm501: Update screen on frame buffer address change When the guest changes the address of the frame buffer we need to refresh the screen to correctly display the new content. This fixes display update problems when changing between screens on AmigaOS. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/display/sm501.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 3661a89f60..9ab29d35dd 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1235,6 +1235,7 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, if (value & 0x8000000) { qemu_log_mask(LOG_UNIMP, "Panel external memory not supported\n"); } + s->do_full_update = true; break; case SM501_DC_PANEL_FB_OFFSET: s->dc_panel_fb_offset = value & 0x3FF03FF0; @@ -1298,6 +1299,7 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, if (value & 0x8000000) { qemu_log_mask(LOG_UNIMP, "CRT external memory not supported\n"); } + s->do_full_update = true; break; case SM501_DC_CRT_FB_OFFSET: s->dc_crt_fb_offset = value & 0x3FF03FF0; From ccc2cef8b3f1dedd059924eb8ec1a87eff8ef607 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 10 Jul 2018 11:40:52 +1000 Subject: [PATCH 2215/2380] spapr: Correct inverted test in spapr_pc_dimm_node() This function was introduced between v2.11 and v2.12 to replace obsolete ways of specifying the NUMA nodes for DIMMs. It's used to find the correct node for an LMB, by locating which DIMM object it lies within. Unfortunately, one of the checks is inverted, so we check whether the address is less than two different things, rather than actually checking a range. This introduced a regression, meaning that after a reboot qemu will advertise incorrect node information for memory to the guest. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Reviewed-by: Igor Mammedov --- hw/ppc/spapr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 3f5e1d3ec2..421b2dd09b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -665,7 +665,7 @@ static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr) if (value && value->type == MEMORY_DEVICE_INFO_KIND_DIMM) { PCDIMMDeviceInfo *pcdimm_info = value->u.dimm.data; - if (pcdimm_info->addr >= addr && + if (addr >= pcdimm_info->addr && addr < (pcdimm_info->addr + pcdimm_info->size)) { return pcdimm_info->node; } From b585395b655a6c1f9d9ebf1f0890e76d0708eed6 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 12 Jul 2018 12:01:49 +0200 Subject: [PATCH 2216/2380] ppc/xics: fix ICP reset path Recent cleanup in commit a028dd423ee6 dropped the ICPStateClass::reset handler. It is now up to child ICP classes to call the DeviceClass::reset handler of the parent class, thanks to device_class_set_parent_reset(). This is a better object programming pattern, but unfortunately it causes QEMU to crash during CPU hotplug: (qemu) device_add host-spapr-cpu-core,id=core1,core-id=1 Segmentation fault (core dumped) When the hotplug path tries to reset the ICP device, we end up calling: static void icp_kvm_reset(DeviceState *dev) { ICPStateClass *icpc = ICP_GET_CLASS(dev); icpc->parent_reset(dev); but icpc->parent_reset is NULL... This happens because icp_kvm_class_init() calls: device_class_set_parent_reset(dc, icp_kvm_reset, &icpc->parent_reset); but dc->reset, ie, DeviceClass::reset for the TYPE_ICP type, is itself NULL. This patch hence sets DeviceClass::reset for the TYPE_ICP type to point to icp_reset(). It then registers a reset handler that calls DeviceClass::reset. If the ICP subtype has configured its own reset handler with device_class_set_parent_reset(), this ensures it will be called first and it can then call ICPStateClass::parent_reset safely. This fixes the reset path for the TYPE_KVM_ICP type, which is the only subtype that defines its own reset function. Reported-by: Satheesh Rajendran Suggested-by: David Gibson Fixes: a028dd423ee6dfd091a8c63028240832bf10f671 Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/intc/xics.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index b9f1a3c972..c90c893228 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -291,7 +291,7 @@ static const VMStateDescription vmstate_icp_server = { }, }; -static void icp_reset(void *dev) +static void icp_reset(DeviceState *dev) { ICPState *icp = ICP(dev); @@ -303,6 +303,13 @@ static void icp_reset(void *dev) qemu_set_irq(icp->output, 0); } +static void icp_reset_handler(void *dev) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + dc->reset(dev); +} + static void icp_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); @@ -345,7 +352,7 @@ static void icp_realize(DeviceState *dev, Error **errp) return; } - qemu_register_reset(icp_reset, dev); + qemu_register_reset(icp_reset_handler, dev); vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); } @@ -354,7 +361,7 @@ static void icp_unrealize(DeviceState *dev, Error **errp) ICPState *icp = ICP(dev); vmstate_unregister(NULL, &vmstate_icp_server, icp); - qemu_unregister_reset(icp_reset, dev); + qemu_unregister_reset(icp_reset_handler, dev); } static void icp_class_init(ObjectClass *klass, void *data) @@ -363,6 +370,7 @@ static void icp_class_init(ObjectClass *klass, void *data) dc->realize = icp_realize; dc->unrealize = icp_unrealize; + dc->reset = icp_reset; } static const TypeInfo icp_info = { From fd8e3381a00feb1e9878f6a3e2de11295f041f67 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 12 Jul 2018 14:00:52 -0700 Subject: [PATCH 2217/2380] etsec: fix IRQ (un)masking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Interrupt conditions occurring while masked are not being signaled when later unmasked. The fix is to raise/lower IRQs when IMASK is changed. To avoid problems like this in future, consolidate IRQ pin update logic in one function. Also fix probable typo "IEVENT_TXF | IEVENT_TXF", and update IRQ pins on reset. Signed-off-by: Michael Davidsaver Reviewed-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/net/fsl_etsec/etsec.c | 68 +++++++++++++++++++----------------- hw/net/fsl_etsec/etsec.h | 2 ++ hw/net/fsl_etsec/registers.h | 10 ++++++ hw/net/fsl_etsec/rings.c | 12 +------ 4 files changed, 49 insertions(+), 43 deletions(-) diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 9da1932970..0b66274ce3 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -49,6 +49,28 @@ static const int debug_etsec; } \ } while (0) +/* call after any change to IEVENT or IMASK */ +void etsec_update_irq(eTSEC *etsec) +{ + uint32_t ievent = etsec->regs[IEVENT].value; + uint32_t imask = etsec->regs[IMASK].value; + uint32_t active = ievent & imask; + + int tx = !!(active & IEVENT_TX_MASK); + int rx = !!(active & IEVENT_RX_MASK); + int err = !!(active & IEVENT_ERR_MASK); + + DPRINTF("%s IRQ ievent=%"PRIx32" imask=%"PRIx32" %c%c%c", + __func__, ievent, imask, + tx ? 'T' : '_', + rx ? 'R' : '_', + err ? 'E' : '_'); + + qemu_set_irq(etsec->tx_irq, tx); + qemu_set_irq(etsec->rx_irq, rx); + qemu_set_irq(etsec->err_irq, err); +} + static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size) { eTSEC *etsec = opaque; @@ -139,31 +161,6 @@ static void write_rbasex(eTSEC *etsec, etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7; } -static void write_ievent(eTSEC *etsec, - eTSEC_Register *reg, - uint32_t reg_index, - uint32_t value) -{ - /* Write 1 to clear */ - reg->value &= ~value; - - if (!(reg->value & (IEVENT_TXF | IEVENT_TXF))) { - qemu_irq_lower(etsec->tx_irq); - } - if (!(reg->value & (IEVENT_RXF | IEVENT_RXF))) { - qemu_irq_lower(etsec->rx_irq); - } - - if (!(reg->value & (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC | - IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC | - IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ | - IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE | - IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD | - IEVENT_MMRW))) { - qemu_irq_lower(etsec->err_irq); - } -} - static void write_dmactrl(eTSEC *etsec, eTSEC_Register *reg, uint32_t reg_index, @@ -178,9 +175,7 @@ static void write_dmactrl(eTSEC *etsec, } else { /* Graceful receive stop now */ etsec->regs[IEVENT].value |= IEVENT_GRSC; - if (etsec->regs[IMASK].value & IMASK_GRSCEN) { - qemu_irq_raise(etsec->err_irq); - } + etsec_update_irq(etsec); } } @@ -191,9 +186,7 @@ static void write_dmactrl(eTSEC *etsec, } else { /* Graceful transmit stop now */ etsec->regs[IEVENT].value |= IEVENT_GTSC; - if (etsec->regs[IMASK].value & IMASK_GTSCEN) { - qemu_irq_raise(etsec->err_irq); - } + etsec_update_irq(etsec); } } @@ -222,7 +215,16 @@ static void etsec_write(void *opaque, switch (reg_index) { case IEVENT: - write_ievent(etsec, reg, reg_index, value); + /* Write 1 to clear */ + reg->value &= ~value; + + etsec_update_irq(etsec); + break; + + case IMASK: + reg->value = value; + + etsec_update_irq(etsec); break; case DMACTRL: @@ -337,6 +339,8 @@ static void etsec_reset(DeviceState *d) MII_SR_EXTENDED_STATUS | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS | MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS | MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS; + + etsec_update_irq(etsec); } static ssize_t etsec_receive(NetClientState *nc, diff --git a/hw/net/fsl_etsec/etsec.h b/hw/net/fsl_etsec/etsec.h index 30c828e241..877988572e 100644 --- a/hw/net/fsl_etsec/etsec.h +++ b/hw/net/fsl_etsec/etsec.h @@ -163,6 +163,8 @@ DeviceState *etsec_create(hwaddr base, qemu_irq rx_irq, qemu_irq err_irq); +void etsec_update_irq(eTSEC *etsec); + void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr); void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr); ssize_t etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size); diff --git a/hw/net/fsl_etsec/registers.h b/hw/net/fsl_etsec/registers.h index c4ed2b9d62..f085537ecd 100644 --- a/hw/net/fsl_etsec/registers.h +++ b/hw/net/fsl_etsec/registers.h @@ -74,6 +74,16 @@ extern const eTSEC_Register_Definition eTSEC_registers_def[]; #define IEVENT_RXC (1 << 30) #define IEVENT_BABR (1 << 31) +/* Mapping between interrupt pin and interrupt flags */ +#define IEVENT_RX_MASK (IEVENT_RXF | IEVENT_RXB) +#define IEVENT_TX_MASK (IEVENT_TXF | IEVENT_TXB) +#define IEVENT_ERR_MASK (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC | \ + IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC | \ + IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ | \ + IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE | \ + IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD | \ + IEVENT_MMRW) + #define IMASK_RXFEN (1 << 7) #define IMASK_GRSCEN (1 << 8) #define IMASK_RXBEN (1 << 15) diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index d0f93eebfc..337a55fc95 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -152,17 +152,7 @@ static void ievent_set(eTSEC *etsec, { etsec->regs[IEVENT].value |= flags; - if ((flags & IEVENT_TXB && etsec->regs[IMASK].value & IMASK_TXBEN) - || (flags & IEVENT_TXF && etsec->regs[IMASK].value & IMASK_TXFEN)) { - qemu_irq_raise(etsec->tx_irq); - RING_DEBUG("%s Raise Tx IRQ\n", __func__); - } - - if ((flags & IEVENT_RXB && etsec->regs[IMASK].value & IMASK_RXBEN) - || (flags & IEVENT_RXF && etsec->regs[IMASK].value & IMASK_RXFEN)) { - qemu_irq_raise(etsec->rx_irq); - RING_DEBUG("%s Raise Rx IRQ\n", __func__); - } + etsec_update_irq(etsec); } static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len) From 3cc702d6aad603fdbe0c83db0bf803e450a6d0b8 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 15 Jul 2018 22:47:26 +0200 Subject: [PATCH 2218/2380] sam460ex: Correct use after free error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 51b0d834c changed error handling to report file name in error message but forgot to move freeing it after usage. Noticed by Coverity. Fixes: CID 1394217 Reported-by: Paolo Bonzini Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/ppc/sam460ex.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index e2b7028843..0999efcc1e 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -269,11 +269,12 @@ static int sam460ex_load_device_tree(hwaddr addr, exit(1); } fdt = load_device_tree(filename, &fdt_size); - g_free(filename); if (!fdt) { error_report("Couldn't load dtb file `%s'", filename); + g_free(filename); exit(1); } + g_free(filename); /* Manipulate device tree in memory. */ From 6730df0514d3aec35e646ff9833fbe8b05fd0776 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 15 Jul 2018 22:59:21 +0200 Subject: [PATCH 2219/2380] sm501: Fix warning about unreachable code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity warned that the false arm of conditional expression is unreachable when it is inside an if with the same condition. Remove the unreachable code to avoid the warning. Fixes: CID 1394215 Reported-by: Paolo Bonzini Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/display/sm501.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 9ab29d35dd..874260a143 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1024,7 +1024,7 @@ static void sm501_i2c_write(void *opaque, hwaddr addr, uint64_t value, if (res) { SM501_DPRINTF("sm501 i2c : transfer failed" " i=%d, res=%d\n", i, res); - s->i2c_status |= (res ? SM501_I2C_STATUS_ERROR : 0); + s->i2c_status |= SM501_I2C_STATUS_ERROR; return; } } From 1f214ee1b83afd10fd5e1b63f4ecc03f9a8115c4 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 5 Jul 2018 11:14:01 +0200 Subject: [PATCH 2220/2380] qapi: Do not expose "allow-preconfig" in query-qmp-schema According to commit 047f7038f58, option --preconfig [...] allows pausing QEMU in the new RUN_STATE_PRECONFIG state, allowing the configuration of QEMU from QMP before the machine jumps into board initialization code of machine_run_board_init() The intent is to allow management to query machine state and additionally configure it using previous query results within one QEMU instance (i.e. eliminate the need to start QEMU twice, 1st to query board specific parameters and 2nd for actual VM start using query results for additional parameters). The implementation is a bit of a hack: it splices in an additional main loop before machine creation, in special runstate preconfig. New command exit-preconfig exits that main loop. QEMU continues initializing, creates the machine, and runs the good old main loop. The replacement of the main loop is transparent to monitors. Sadly, some commands expect initialization to be complete. Running them in --preconfig's main loop violates their preconditions. Since we don't really know which commands are safe, we use a whitelist. This drags the concept of run state into the QMP core. The whitelist is done as a command flag in the QAPI schema (commit d6fe3d02e9a). Drags the concept of run state further into the QAPI language. The command flag is exposed in query-qmp-schema (also commit d6fe3d02e9a). This makes it ABI. I consider the whole thing an offensively ugly hack, but sometimes an ugly hack is the best we can do to solve a problem people have. The need described by the commit message quote above is genuine. The proper solution would be a main loop that permits complete configuration via QMP. This is out of reach, thus the hack. However, even though the need is genuine, it isn't urgent: libvirt is not going to use this anytime soon. Baking a hack into ABI before it has any users is a bad idea. This commit reverts the parts of commit d6fe3d02e9a that affect ABI via query-qmp-schema. The commit did the following: (1) Add command flag 'allow-preconfig' to the QAPI schema language (2) Pass it to code generators (3) Have the commands.py code generator pass it to the command registry (so commit 047f7038f58 can use it as whitelist) (4) Add 'allow-preconfig' to SchemaInfoCommand (neglecting to update qapi-code-gen.txt section "Client JSON Protocol introspection") (5) Set 'allow-preconfig': true for commands qmp_capabilities, query-commands, query-command-line-options, query-status Revert exactly (4), plus a bit of documentation added to qemu-tech.info in commit 047f7038f58. Shrinks query-qmp-schema's output from 126.5KiB to 121.8KiB for me. Signed-off-by: Markus Armbruster Message-Id: <20180705091402.26244-2-armbru@redhat.com> Reviewed-by: Eric Blake Acked-by: Eduardo Habkost Acked-by: Igor Mammedov [Straightforward conflict with commit d626b6c1ae7 resolved] --- qapi/introspect.json | 5 +---- qemu-tech.texi | 3 --- scripts/qapi/introspect.py | 4 ++-- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/qapi/introspect.json b/qapi/introspect.json index 80a0a3e656..c7f67b7d78 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -262,16 +262,13 @@ # @allow-oob: whether the command allows out-of-band execution. # (Since: 2.12) # -# @allow-preconfig: command can be executed in preconfig runstate, -# default: false (Since 3.0) -# # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # # Since: 2.5 ## { 'struct': 'SchemaInfoCommand', 'data': { 'arg-type': 'str', 'ret-type': 'str', - 'allow-oob': 'bool', 'allow-preconfig': 'bool' } } + 'allow-oob': 'bool' } } ## # @SchemaInfoEvent: diff --git a/qemu-tech.texi b/qemu-tech.texi index dcecba83cb..f843341ffa 100644 --- a/qemu-tech.texi +++ b/qemu-tech.texi @@ -350,9 +350,6 @@ depend on an initialized machine, including but not limited to: @item query-status @item exit-preconfig @end table -The full list of commands is in QMP schema which could be queried with -query-qmp-schema, where commands supported at preconfig state have option -'allow-preconfig' set to true. @node Bibliography @section Bibliography diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 71d4a779ce..70ca5dd876 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -187,8 +187,8 @@ const QLitObject %(c_name)s = %(c_string)s; self._gen_qlit(name, 'command', {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type), - 'allow-oob': allow_oob, - 'allow-preconfig': allow_preconfig}, ifcond) + 'allow-oob': allow_oob}, + ifcond) def visit_event(self, name, info, ifcond, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type From 361ac948a5c960ce7a093cec1744bff0d5af3dec Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 5 Jul 2018 11:14:02 +0200 Subject: [PATCH 2221/2380] cli qmp: Mark --preconfig, exit-preconfig experimental Committing to the current --preconfig / exit-preconfig interface before it has seen any use is premature. Mark both as experimental, the former in documentation, the latter by renaming it to x-exit-preconfig. See the previous commit for more detailed rationale. Signed-off-by: Markus Armbruster Message-Id: <20180705091402.26244-3-armbru@redhat.com> Reviewed-by: Eric Blake Acked-by: Eduardo Habkost Acked-by: Igor Mammedov [Straightforward conflict with commit 514337c142f resolved] --- hmp.c | 2 +- qapi/misc.json | 6 +++--- qemu-options.hx | 9 +++++---- qemu-tech.texi | 8 ++++---- qmp.c | 2 +- tests/numa-test.c | 2 +- tests/qmp-test.c | 6 +++--- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/hmp.c b/hmp.c index 4555b503ac..2aafb50e8e 100644 --- a/hmp.c +++ b/hmp.c @@ -1076,7 +1076,7 @@ void hmp_exit_preconfig(Monitor *mon, const QDict *qdict) { Error *err = NULL; - qmp_exit_preconfig(&err); + qmp_x_exit_preconfig(&err); hmp_handle_error(mon, &err); } diff --git a/qapi/misc.json b/qapi/misc.json index f1860418e8..d450cfef21 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -1205,7 +1205,7 @@ { 'command': 'cont' } ## -# @exit-preconfig: +# @x-exit-preconfig: # # Exit from "preconfig" state # @@ -1221,11 +1221,11 @@ # # Example: # -# -> { "execute": "exit-preconfig" } +# -> { "execute": "x-exit-preconfig" } # <- { "return": {} } # ## -{ 'command': 'exit-preconfig', 'allow-preconfig': true } +{ 'command': 'x-exit-preconfig', 'allow-preconfig': true } ## # @system_wakeup: diff --git a/qemu-options.hx b/qemu-options.hx index 654e69cc3b..371c4271a4 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3303,16 +3303,17 @@ Run the emulation in single step mode. ETEXI DEF("preconfig", 0, QEMU_OPTION_preconfig, \ - "--preconfig pause QEMU before machine is initialized\n", + "--preconfig pause QEMU before machine is initialized (experimental)\n", QEMU_ARCH_ALL) STEXI @item --preconfig @findex --preconfig Pause QEMU for interactive configuration before the machine is created, which allows querying and configuring properties that will affect -machine initialization. Use the QMP command 'exit-preconfig' to exit -the preconfig state and move to the next state (ie. run guest if -S -isn't used or pause the second time if -S is used). +machine initialization. Use QMP command 'x-exit-preconfig' to exit +the preconfig state and move to the next state (i.e. run guest if -S +isn't used or pause the second time if -S is used). This option is +experimental. ETEXI DEF("S", 0, QEMU_OPTION_S, \ diff --git a/qemu-tech.texi b/qemu-tech.texi index f843341ffa..7c3d1f05e1 100644 --- a/qemu-tech.texi +++ b/qemu-tech.texi @@ -336,9 +336,9 @@ additionally configure the machine (by hotplugging devices) in runtime before allowing VM code to run. However, at the -S pause point, it's impossible to configure options that affect -initial VM creation (like: -smp/-m/-numa ...) or cold plug devices. That's -when the --preconfig command line option should be used. It allows pausing QEMU -before the initial VM creation, in a new preconfig state, where additional +initial VM creation (like: -smp/-m/-numa ...) or cold plug devices. The +experimental --preconfig command line option allows pausing QEMU +before the initial VM creation, in a ``preconfig'' state, where additional queries and configuration can be performed via QMP before moving on to the resulting configuration startup. In the preconfig state, QEMU only allows a limited set of commands over the QMP monitor, where the commands do not @@ -348,7 +348,7 @@ depend on an initialized machine, including but not limited to: @item query-qmp-schema @item query-commands @item query-status -@item exit-preconfig +@item x-exit-preconfig @end table @node Bibliography diff --git a/qmp.c b/qmp.c index 5170403e5d..e7c0a2fd60 100644 --- a/qmp.c +++ b/qmp.c @@ -129,7 +129,7 @@ void qmp_cpu_add(int64_t id, Error **errp) } } -void qmp_exit_preconfig(Error **errp) +void qmp_x_exit_preconfig(Error **errp) { if (!runstate_check(RUN_STATE_PRECONFIG)) { error_setg(errp, "The command is permitted only in '%s' state", diff --git a/tests/numa-test.c b/tests/numa-test.c index b7a6ef8815..893f826acb 100644 --- a/tests/numa-test.c +++ b/tests/numa-test.c @@ -285,7 +285,7 @@ static void pc_dynamic_cpu_cfg(const void *data) " 'arguments': { 'type': 'cpu', 'node-id': 1, 'socket-id': 0 } }"))); /* let machine initialization to complete and run */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); qtest_qmp_eventwait(qs, "RESUME"); /* check that CPUs are mapped as expected */ diff --git a/tests/qmp-test.c b/tests/qmp-test.c index ceaf4a6789..b9774084f8 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -453,7 +453,7 @@ static void test_qmp_preconfig(void) qobject_unref(rsp); /* exit preconfig state */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); qtest_qmp_eventwait(qs, "RESUME"); /* check that query-status returns running state */ @@ -463,8 +463,8 @@ static void test_qmp_preconfig(void) g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running"); qobject_unref(rsp); - /* check that exit-preconfig returns error after exiting preconfig */ - g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); + /* check that x-exit-preconfig returns error after exiting preconfig */ + g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); /* enabled commands, no error expected */ g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); From 44c67847e32c91a6071fb0440c357b9489f08bc6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 16 Jul 2018 09:32:25 +0200 Subject: [PATCH 2222/2380] qemu-doc: Move appendix "Deprecated features" to its own file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consumers of QEMU need to track feature deprecation. Keeping deprecation documentation in its own file helps in two small ways: * You can track changes the easy and obvious way, with git-log. Before, you had to resort to more complex gittery like "git-log --oneline -L '/@node Deprecated features/,/@node Supported build platforms/:qemu-doc.texi'" * It lets us use MAINTAINERS to copy interested parties on deprecation patches, so they can advise or object before they're a done deal. The next commit will do that for libvirt. Signed-off-by: Markus Armbruster Message-Id: <20180716073226.21127-2-armbru@redhat.com> Reviewed-by: Thomas Huth Reviewed-by: Cornelia Huck Reviewed-by: Daniel P. Berrangé --- qemu-deprecated.texi | 234 ++++++++++++++++++++++++++++++++++++++++++ qemu-doc.texi | 235 +------------------------------------------ 2 files changed, 235 insertions(+), 234 deletions(-) create mode 100644 qemu-deprecated.texi diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi new file mode 100644 index 0000000000..9920a85adc --- /dev/null +++ b/qemu-deprecated.texi @@ -0,0 +1,234 @@ +@node Deprecated features +@appendix Deprecated features + +In general features are intended to be supported indefinitely once +introduced into QEMU. In the event that a feature needs to be removed, +it will be listed in this appendix. The feature will remain functional +for 2 releases prior to actual removal. Deprecated features may also +generate warnings on the console when QEMU starts up, or if activated +via a monitor command, however, this is not a mandatory requirement. + +Prior to the 2.10.0 release there was no official policy on how +long features would be deprecated prior to their removal, nor +any documented list of which features were deprecated. Thus +any features deprecated prior to 2.10.0 will be treated as if +they were first deprecated in the 2.10.0 release. + +What follows is a list of all features currently marked as +deprecated. + +@section Build options + +@subsection GTK 2.x + +Previously QEMU has supported building against both GTK 2.x +and 3.x series APIs. Support for the GTK 2.x builds will be +discontinued, so maintainers should switch to using GTK 3.x, +which is the default. + +@subsection SDL 1.2 + +Previously QEMU has supported building against both SDL 1.2 +and 2.0 series APIs. Support for the SDL 1.2 builds will be +discontinued, so maintainers should switch to using SDL 2.0, +which is the default. + +@section System emulator command line arguments + +@subsection -no-kvm (since 1.3.0) + +The ``-no-kvm'' argument is now a synonym for setting +``-machine accel=tcg''. + +@subsection -vnc tls (since 2.5.0) + +The ``-vnc tls'' argument is now a synonym for setting +``-object tls-creds-anon,id=tls0'' combined with +``-vnc tls-creds=tls0' + +@subsection -vnc x509 (since 2.5.0) + +The ``-vnc x509=/path/to/certs'' argument is now a +synonym for setting +``-object tls-creds-x509,dir=/path/to/certs,id=tls0,verify-peer=no'' +combined with ``-vnc tls-creds=tls0' + +@subsection -vnc x509verify (since 2.5.0) + +The ``-vnc x509verify=/path/to/certs'' argument is now a +synonym for setting +``-object tls-creds-x509,dir=/path/to/certs,id=tls0,verify-peer=yes'' +combined with ``-vnc tls-creds=tls0' + +@subsection -tftp (since 2.6.0) + +The ``-tftp /some/dir'' argument is replaced by either +``-netdev user,id=x,tftp=/some/dir '' (for pluggable NICs, accompanied +with ``-device ...,netdev=x''), or ``-nic user,tftp=/some/dir'' +(for embedded NICs). The new syntax allows different settings to be +provided per NIC. + +@subsection -bootp (since 2.6.0) + +The ``-bootp /some/file'' argument is replaced by either +``-netdev user,id=x,bootp=/some/file '' (for pluggable NICs, accompanied +with ``-device ...,netdev=x''), or ``-nic user,bootp=/some/file'' +(for embedded NICs). The new syntax allows different settings to be +provided per NIC. + +@subsection -redir (since 2.6.0) + +The ``-redir [tcp|udp]:hostport:[guestaddr]:guestport'' argument is +replaced by either +``-netdev user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' +(for pluggable NICs, accompanied with ``-device ...,netdev=x'') or +``-nic user,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' +(for embedded NICs). The new syntax allows different settings to be +provided per NIC. + +@subsection -smb (since 2.6.0) + +The ``-smb /some/dir'' argument is replaced by either +``-netdev user,id=x,smb=/some/dir '' (for pluggable NICs, accompanied +with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' +(for embedded NICs). The new syntax allows different settings to be +provided per NIC. + +@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) + +The drive geometry arguments are replaced by the the geometry arguments +that can be specified with the ``-device'' parameter. + +@subsection -drive serial=... (since 2.10.0) + +The drive serial argument is replaced by the the serial argument +that can be specified with the ``-device'' parameter. + +@subsection -drive addr=... (since 2.10.0) + +The drive addr argument is replaced by the the addr argument +that can be specified with the ``-device'' parameter. + +@subsection -usbdevice (since 2.10.0) + +The ``-usbdevice DEV'' argument is now a synonym for setting +the ``-device usb-DEV'' argument instead. The deprecated syntax +would automatically enable USB support on the machine type. +If using the new syntax, USB support must be explicitly +enabled via the ``-machine usb=on'' argument. + +@subsection -nodefconfig (since 2.11.0) + +The ``-nodefconfig`` argument is a synonym for ``-no-user-config``. + +@subsection -balloon (since 2.12.0) + +The @option{--balloon virtio} argument has been superseded by +@option{--device virtio-balloon}. + +@subsection -machine s390-squash-mcss=on|off (since 2.12.0) + +The ``s390-squash-mcss=on`` property has been obsoleted by allowing the +cssid to be chosen freely. Instead of squashing subchannels into the +default channel subsystem image for guests that do not support multiple +channel subsystems, all devices can be put into the default channel +subsystem image. + +@subsection -fsdev handle (since 2.12.0) + +The ``handle'' fsdev backend does not support symlinks and causes the 9p +filesystem in the guest to fail a fair amount of tests from the PJD POSIX +filesystem test suite. Also it requires the CAP_DAC_READ_SEARCH capability, +which is not the recommended way to run QEMU. This backend should not be +used and it will be removed with no replacement. + +@subsection -no-frame (since 2.12.0) + +The @code{--no-frame} argument works with SDL 1.2 only. The other user +interfaces never implemented this in the first place. So this will be +removed together with SDL 1.2 support. + +@subsection -rtc-td-hack (since 2.12.0) + +The @code{-rtc-td-hack} option has been replaced by +@code{-rtc driftfix=slew}. + +@subsection -localtime (since 2.12.0) + +The @code{-localtime} option has been replaced by @code{-rtc base=localtime}. + +@subsection -startdate (since 2.12.0) + +The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}. + +@subsection -virtioconsole (since 3.0.0) + +Option @option{-virtioconsole} has been replaced by +@option{-device virtconsole}. + +@subsection -clock (since 3.0.0) + +The @code{-clock} option is ignored since QEMU version 1.7.0. There is no +replacement since it is not needed anymore. + +@subsection -enable-hax (since 3.0.0) + +The @option{-enable-hax} option has been replaced by @option{-accel hax}. +Both options have been introduced in QEMU version 2.9.0. + +@subsection -drive file=json:@{...@{'driver':'file'@}@} (since 3.0) + +The 'file' driver for drives is no longer appropriate for character or host +devices and will only accept regular files (S_IFREG). The correct driver +for these file types is 'host_cdrom' or 'host_device' as appropriate. + +@section QEMU Machine Protocol (QMP) commands + +@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) + +"autoload" parameter is now ignored. All bitmaps are automatically loaded +from qcow2 images. + +@subsection query-cpus (since 2.12.0) + +The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. + +@subsection query-cpus-fast "arch" output member (since 3.0.0) + +The ``arch'' output member of the ``query-cpus-fast'' command is +replaced by the ``target'' output member. + +@section System emulator devices + +@subsection ivshmem (since 2.6.0) + +The ``ivshmem'' device type is replaced by either the ``ivshmem-plain'' +or ``ivshmem-doorbell`` device types. + +@subsection Page size support < 4k for embedded PowerPC CPUs (since 2.12.0) + +qemu-system-ppcemb will be removed. qemu-system-ppc (or qemu-system-ppc64) +should be used instead. That means that embedded 4xx PowerPC CPUs will not +support page sizes < 4096 any longer. + +@section System emulator machines + +@subsection pc-0.10 and pc-0.11 (since 3.0) + +These machine types are very old and likely can not be used for live migration +from old QEMU versions anymore. A newer machine type should be used instead. + +@section Device options + +@subsection Block device options + +@subsubsection "backing": "" (since 2.12.0) + +In order to prevent QEMU from automatically opening an image's backing +chain, use ``"backing": null'' instead. + +@subsection vio-spapr-device device options + +@subsubsection "irq": "" (since 3.0.0) + +The ``irq'' property is obsoleted. diff --git a/qemu-doc.texi b/qemu-doc.texi index 1047c407e7..abfd2db546 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2791,240 +2791,7 @@ Run the emulation in single step mode. @include qemu-tech.texi -@node Deprecated features -@appendix Deprecated features - -In general features are intended to be supported indefinitely once -introduced into QEMU. In the event that a feature needs to be removed, -it will be listed in this appendix. The feature will remain functional -for 2 releases prior to actual removal. Deprecated features may also -generate warnings on the console when QEMU starts up, or if activated -via a monitor command, however, this is not a mandatory requirement. - -Prior to the 2.10.0 release there was no official policy on how -long features would be deprecated prior to their removal, nor -any documented list of which features were deprecated. Thus -any features deprecated prior to 2.10.0 will be treated as if -they were first deprecated in the 2.10.0 release. - -What follows is a list of all features currently marked as -deprecated. - -@section Build options - -@subsection GTK 2.x - -Previously QEMU has supported building against both GTK 2.x -and 3.x series APIs. Support for the GTK 2.x builds will be -discontinued, so maintainers should switch to using GTK 3.x, -which is the default. - -@subsection SDL 1.2 - -Previously QEMU has supported building against both SDL 1.2 -and 2.0 series APIs. Support for the SDL 1.2 builds will be -discontinued, so maintainers should switch to using SDL 2.0, -which is the default. - -@section System emulator command line arguments - -@subsection -no-kvm (since 1.3.0) - -The ``-no-kvm'' argument is now a synonym for setting -``-machine accel=tcg''. - -@subsection -vnc tls (since 2.5.0) - -The ``-vnc tls'' argument is now a synonym for setting -``-object tls-creds-anon,id=tls0'' combined with -``-vnc tls-creds=tls0' - -@subsection -vnc x509 (since 2.5.0) - -The ``-vnc x509=/path/to/certs'' argument is now a -synonym for setting -``-object tls-creds-x509,dir=/path/to/certs,id=tls0,verify-peer=no'' -combined with ``-vnc tls-creds=tls0' - -@subsection -vnc x509verify (since 2.5.0) - -The ``-vnc x509verify=/path/to/certs'' argument is now a -synonym for setting -``-object tls-creds-x509,dir=/path/to/certs,id=tls0,verify-peer=yes'' -combined with ``-vnc tls-creds=tls0' - -@subsection -tftp (since 2.6.0) - -The ``-tftp /some/dir'' argument is replaced by either -``-netdev user,id=x,tftp=/some/dir '' (for pluggable NICs, accompanied -with ``-device ...,netdev=x''), or ``-nic user,tftp=/some/dir'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -bootp (since 2.6.0) - -The ``-bootp /some/file'' argument is replaced by either -``-netdev user,id=x,bootp=/some/file '' (for pluggable NICs, accompanied -with ``-device ...,netdev=x''), or ``-nic user,bootp=/some/file'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -redir (since 2.6.0) - -The ``-redir [tcp|udp]:hostport:[guestaddr]:guestport'' argument is -replaced by either -``-netdev user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' -(for pluggable NICs, accompanied with ``-device ...,netdev=x'') or -``-nic user,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -smb (since 2.6.0) - -The ``-smb /some/dir'' argument is replaced by either -``-netdev user,id=x,smb=/some/dir '' (for pluggable NICs, accompanied -with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) - -The drive geometry arguments are replaced by the the geometry arguments -that can be specified with the ``-device'' parameter. - -@subsection -drive serial=... (since 2.10.0) - -The drive serial argument is replaced by the the serial argument -that can be specified with the ``-device'' parameter. - -@subsection -drive addr=... (since 2.10.0) - -The drive addr argument is replaced by the the addr argument -that can be specified with the ``-device'' parameter. - -@subsection -usbdevice (since 2.10.0) - -The ``-usbdevice DEV'' argument is now a synonym for setting -the ``-device usb-DEV'' argument instead. The deprecated syntax -would automatically enable USB support on the machine type. -If using the new syntax, USB support must be explicitly -enabled via the ``-machine usb=on'' argument. - -@subsection -nodefconfig (since 2.11.0) - -The ``-nodefconfig`` argument is a synonym for ``-no-user-config``. - -@subsection -balloon (since 2.12.0) - -The @option{--balloon virtio} argument has been superseded by -@option{--device virtio-balloon}. - -@subsection -machine s390-squash-mcss=on|off (since 2.12.0) - -The ``s390-squash-mcss=on`` property has been obsoleted by allowing the -cssid to be chosen freely. Instead of squashing subchannels into the -default channel subsystem image for guests that do not support multiple -channel subsystems, all devices can be put into the default channel -subsystem image. - -@subsection -fsdev handle (since 2.12.0) - -The ``handle'' fsdev backend does not support symlinks and causes the 9p -filesystem in the guest to fail a fair amount of tests from the PJD POSIX -filesystem test suite. Also it requires the CAP_DAC_READ_SEARCH capability, -which is not the recommended way to run QEMU. This backend should not be -used and it will be removed with no replacement. - -@subsection -no-frame (since 2.12.0) - -The @code{--no-frame} argument works with SDL 1.2 only. The other user -interfaces never implemented this in the first place. So this will be -removed together with SDL 1.2 support. - -@subsection -rtc-td-hack (since 2.12.0) - -The @code{-rtc-td-hack} option has been replaced by -@code{-rtc driftfix=slew}. - -@subsection -localtime (since 2.12.0) - -The @code{-localtime} option has been replaced by @code{-rtc base=localtime}. - -@subsection -startdate (since 2.12.0) - -The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}. - -@subsection -virtioconsole (since 3.0.0) - -Option @option{-virtioconsole} has been replaced by -@option{-device virtconsole}. - -@subsection -clock (since 3.0.0) - -The @code{-clock} option is ignored since QEMU version 1.7.0. There is no -replacement since it is not needed anymore. - -@subsection -enable-hax (since 3.0.0) - -The @option{-enable-hax} option has been replaced by @option{-accel hax}. -Both options have been introduced in QEMU version 2.9.0. - -@subsection -drive file=json:@{...@{'driver':'file'@}@} (since 3.0) - -The 'file' driver for drives is no longer appropriate for character or host -devices and will only accept regular files (S_IFREG). The correct driver -for these file types is 'host_cdrom' or 'host_device' as appropriate. - -@section QEMU Machine Protocol (QMP) commands - -@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) - -"autoload" parameter is now ignored. All bitmaps are automatically loaded -from qcow2 images. - -@subsection query-cpus (since 2.12.0) - -The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. - -@subsection query-cpus-fast "arch" output member (since 3.0.0) - -The ``arch'' output member of the ``query-cpus-fast'' command is -replaced by the ``target'' output member. - -@section System emulator devices - -@subsection ivshmem (since 2.6.0) - -The ``ivshmem'' device type is replaced by either the ``ivshmem-plain'' -or ``ivshmem-doorbell`` device types. - -@subsection Page size support < 4k for embedded PowerPC CPUs (since 2.12.0) - -qemu-system-ppcemb will be removed. qemu-system-ppc (or qemu-system-ppc64) -should be used instead. That means that embedded 4xx PowerPC CPUs will not -support page sizes < 4096 any longer. - -@section System emulator machines - -@subsection pc-0.10 and pc-0.11 (since 3.0) - -These machine types are very old and likely can not be used for live migration -from old QEMU versions anymore. A newer machine type should be used instead. - -@section Device options - -@subsection Block device options - -@subsubsection "backing": "" (since 2.12.0) - -In order to prevent QEMU from automatically opening an image's backing -chain, use ``"backing": null'' instead. - -@subsection vio-spapr-device device options - -@subsubsection "irq": "" (since 3.0.0) - -The ``irq'' property is obsoleted. +@include qemu-deprecated.texi @node Supported build platforms @appendix Supported build platforms From b02c9bc35a03c099600693d6909f4566078880b8 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 16 Jul 2018 09:32:26 +0200 Subject: [PATCH 2223/2380] MAINTAINERS: New section "Incompatible changes", copy libvir-list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Libvirt developers would like to be copied on patches to qemu-doc appendix "Deprecated features". Do them the favor. Signed-off-by: Markus Armbruster Message-Id: <20180716073226.21127-3-armbru@redhat.com> Reviewed-by: Thomas Huth Reviewed-by: Cornelia Huck Reviewed-by: Daniel P. Berrangé --- MAINTAINERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 20eef3cb61..666e936812 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2194,6 +2194,10 @@ M: Daniel P. Berrange S: Odd Fixes F: docs/devel/build-system.txt +Incompatible changes +R: libvir-list@redhat.com +F: qemu-deprecated.texi + Build System ------------ GIT submodules From 8720e63e09fa1e48dbd244d89d6fed6b5c50889d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 16 Jul 2018 11:10:12 +0200 Subject: [PATCH 2224/2380] monitor: Fix tracepoint crash on JSON syntax error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When tracepoint handle_qmp_command is enabled, we crash on JSON syntax errors. Broken in commit 1cc37471525. Fix by skipping the tracepoint on JSON syntax error. Before the flawed commit, we skipped it by returning early. Fixes: CID 1394216 Signed-off-by: Markus Armbruster Message-Id: <20180716091012.29510-1-armbru@redhat.com> Reviewed-by: Marc-André Lureau Reviewed-by: Peter Xu --- monitor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monitor.c b/monitor.c index 7af1f18d13..be29634a00 100644 --- a/monitor.c +++ b/monitor.c @@ -4224,7 +4224,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) qdict_del(qdict, "id"); } /* else will fail qmp_dispatch() */ - if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) { + if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) { QString *req_json = qobject_to_json(req); trace_handle_qmp_command(mon, qstring_get_str(req_json)); qobject_unref(req_json); From 46fac17dca19e52506e05530ad3bd01f6d5722e3 Mon Sep 17 00:00:00 2001 From: Viktor Prutyanov Date: Sat, 14 Jul 2018 15:30:00 +0300 Subject: [PATCH 2225/2380] dump: add kernel_gs_base to QEMU CPU state This patch adds field with content of KERNEL_GS_BASE MSR to QEMU note in ELF dump. On Windows, if all vCPUs are running usermode tasks at the time the dump is created, this can be helpful in the discovery of guest system structures during conversion ELF dump to MEMORY.DMP dump. Signed-off-by: Viktor Prutyanov Message-Id: <20180714123000.11326-1-viktor.prutyanov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- target/i386/arch_dump.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/i386/arch_dump.c b/target/i386/arch_dump.c index 35b55fc200..004141fc04 100644 --- a/target/i386/arch_dump.c +++ b/target/i386/arch_dump.c @@ -258,6 +258,12 @@ struct QEMUCPUState { QEMUCPUSegment cs, ds, es, fs, gs, ss; QEMUCPUSegment ldt, tr, gdt, idt; uint64_t cr[5]; + /* + * Fields below are optional and are being added at the end without + * changing the version. External tools may identify their presence + * by checking 'size' field. + */ + uint64_t kernel_gs_base; }; typedef struct QEMUCPUState QEMUCPUState; @@ -315,6 +321,10 @@ static void qemu_get_cpustate(QEMUCPUState *s, CPUX86State *env) s->cr[2] = env->cr[2]; s->cr[3] = env->cr[3]; s->cr[4] = env->cr[4]; + +#ifdef TARGET_X86_64 + s->kernel_gs_base = env->kernelgsbase; +#endif } static inline int cpu_write_qemu_note(WriteCoreDumpFunction f, From 696c706642023e60fcce6b38f425910cd01ec0a6 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Thu, 12 Jul 2018 21:44:54 +0200 Subject: [PATCH 2226/2380] accel: Fix typo and grammar in comment The typo was found by codespell. Signed-off-by: Stefan Weil Message-Id: <20180712194454.26765-1-sw@weilnetz.de> Signed-off-by: Paolo Bonzini --- accel/tcg/translate-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 49d77fad44..1571987113 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1795,7 +1795,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tb->jmp_dest[0] = (uintptr_t)NULL; tb->jmp_dest[1] = (uintptr_t)NULL; - /* init original jump addresses wich has been set during tcg_gen_code() */ + /* init original jump addresses which have been set during tcg_gen_code() */ if (tb->jmp_reset_offset[0] != TB_JMP_RESET_OFFSET_INVALID) { tb_reset_jump(tb, 0); } From 1b2013ea5ddfd7d6a1426bd716dd6528ec91a8a7 Mon Sep 17 00:00:00 2001 From: Roman Kagan Date: Mon, 2 Jul 2018 16:41:55 +0300 Subject: [PATCH 2227/2380] hyperv: rename vcpu_id to vp_index In Hyper-V-related code, vCPUs are identified by their VP (virtual processor) index. Since it's customary for "vcpu_id" in QEMU to mean APIC id, rename the respective variables to "vp_index" to make the distinction clear. Signed-off-by: Roman Kagan Message-Id: <20180702134156.13404-2-rkagan@virtuozzo.com> Signed-off-by: Paolo Bonzini --- hw/misc/hyperv_testdev.c | 16 ++++++++-------- target/i386/hyperv.c | 6 +++--- target/i386/hyperv.h | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/hw/misc/hyperv_testdev.c b/hw/misc/hyperv_testdev.c index dbd7cdda07..bf6bbfa8cf 100644 --- a/hw/misc/hyperv_testdev.c +++ b/hw/misc/hyperv_testdev.c @@ -57,7 +57,7 @@ static void free_sint_route_index(HypervTestDev *dev, int i) dev->sint_route[i] = NULL; } -static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id, +static int find_sint_route_index(HypervTestDev *dev, uint32_t vp_index, uint32_t sint) { HvSintRoute *sint_route; @@ -65,7 +65,7 @@ static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id, for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) { sint_route = dev->sint_route[i]; - if (sint_route && sint_route->vcpu_id == vcpu_id && + if (sint_route && sint_route->vp_index == vp_index && sint_route->sint == sint) { return i; } @@ -74,7 +74,7 @@ static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id, } static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl, - uint32_t vcpu_id, uint32_t sint) + uint32_t vp_index, uint32_t sint) { int i; HvSintRoute *sint_route; @@ -83,19 +83,19 @@ static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl, case HV_TEST_DEV_SINT_ROUTE_CREATE: i = alloc_sint_route_index(dev); assert(i >= 0); - sint_route = kvm_hv_sint_route_create(vcpu_id, sint, NULL); + sint_route = kvm_hv_sint_route_create(vp_index, sint, NULL); assert(sint_route); dev->sint_route[i] = sint_route; break; case HV_TEST_DEV_SINT_ROUTE_DESTROY: - i = find_sint_route_index(dev, vcpu_id, sint); + i = find_sint_route_index(dev, vp_index, sint); assert(i >= 0); sint_route = dev->sint_route[i]; kvm_hv_sint_route_destroy(sint_route); free_sint_route_index(dev, i); break; case HV_TEST_DEV_SINT_ROUTE_SET_SINT: - i = find_sint_route_index(dev, vcpu_id, sint); + i = find_sint_route_index(dev, vp_index, sint); assert(i >= 0); sint_route = dev->sint_route[i]; kvm_hv_sint_route_set_sint(sint_route); @@ -117,8 +117,8 @@ static void hv_test_dev_control(void *opaque, hwaddr addr, uint64_t data, case HV_TEST_DEV_SINT_ROUTE_DESTROY: case HV_TEST_DEV_SINT_ROUTE_SET_SINT: { uint8_t sint = data & 0xFF; - uint8_t vcpu_id = (data >> 8ULL) & 0xFF; - hv_synic_test_dev_control(dev, ctl, vcpu_id, sint); + uint8_t vp_index = (data >> 8ULL) & 0xFF; + hv_synic_test_dev_control(dev, ctl, vp_index, sint); break; } default: diff --git a/target/i386/hyperv.c b/target/i386/hyperv.c index a050c9d2d1..7cc0fbb272 100644 --- a/target/i386/hyperv.c +++ b/target/i386/hyperv.c @@ -72,7 +72,7 @@ static void kvm_hv_sint_ack_handler(EventNotifier *notifier) } } -HvSintRoute *kvm_hv_sint_route_create(uint32_t vcpu_id, uint32_t sint, +HvSintRoute *kvm_hv_sint_route_create(uint32_t vp_index, uint32_t sint, HvSintAckClb sint_ack_clb) { HvSintRoute *sint_route; @@ -92,7 +92,7 @@ HvSintRoute *kvm_hv_sint_route_create(uint32_t vcpu_id, uint32_t sint, event_notifier_set_handler(&sint_route->sint_ack_notifier, kvm_hv_sint_ack_handler); - gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vcpu_id, sint); + gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint); if (gsi < 0) { goto err_gsi; } @@ -105,7 +105,7 @@ HvSintRoute *kvm_hv_sint_route_create(uint32_t vcpu_id, uint32_t sint, } sint_route->gsi = gsi; sint_route->sint_ack_clb = sint_ack_clb; - sint_route->vcpu_id = vcpu_id; + sint_route->vp_index = vp_index; sint_route->sint = sint; return sint_route; diff --git a/target/i386/hyperv.h b/target/i386/hyperv.h index 0c3b562018..eaf3df34b0 100644 --- a/target/i386/hyperv.h +++ b/target/i386/hyperv.h @@ -23,7 +23,7 @@ typedef void (*HvSintAckClb)(HvSintRoute *sint_route); struct HvSintRoute { uint32_t sint; - uint32_t vcpu_id; + uint32_t vp_index; int gsi; EventNotifier sint_set_notifier; EventNotifier sint_ack_notifier; @@ -32,7 +32,7 @@ struct HvSintRoute { int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit); -HvSintRoute *kvm_hv_sint_route_create(uint32_t vcpu_id, uint32_t sint, +HvSintRoute *kvm_hv_sint_route_create(uint32_t vp_index, uint32_t sint, HvSintAckClb sint_ack_clb); void kvm_hv_sint_route_destroy(HvSintRoute *sint_route); From e9688fabc32b532c9a93794c37e343facd5ecd36 Mon Sep 17 00:00:00 2001 From: Roman Kagan Date: Mon, 2 Jul 2018 16:41:56 +0300 Subject: [PATCH 2228/2380] hyperv: ensure VP index equal to QEMU cpu_index Hyper-V identifies vCPUs by Virtual Processor (VP) index which can be queried by the guest via HV_X64_MSR_VP_INDEX msr. It is defined by the spec as a sequential number which can't exceed the maximum number of vCPUs per VM. It has to be owned by QEMU in order to preserve it across migration. However, the initial implementation in KVM didn't allow to set this msr, and KVM used its own notion of VP index. Fortunately, the way vCPUs are created in QEMU/KVM makes it likely that the KVM value is equal to QEMU cpu_index. So choose cpu_index as the value for vp_index, and push that to KVM on kernels that support setting the msr. On older ones that don't, query the kernel value and assert that it's in sync with QEMU. Besides, since handling errors from vCPU init at hotplug time is impossible, disable vCPU hotplug. This patch also introduces accessor functions to encapsulate the mapping between a vCPU and its vp_index. Signed-off-by: Roman Kagan Message-Id: <20180702134156.13404-3-rkagan@virtuozzo.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 5 +++++ target/i386/hyperv.c | 10 +++++++++ target/i386/hyperv.h | 3 +++ target/i386/kvm-stub.c | 5 +++++ target/i386/kvm.c | 47 ++++++++++++++++++++++++++++++++++++++++++ target/i386/kvm_i386.h | 2 ++ 6 files changed, 72 insertions(+) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 50d5553991..83a444472b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1999,6 +1999,11 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, } cpu->thread_id = topo.smt_id; + if (cpu->hyperv_vpindex && !kvm_hv_vpindex_settable()) { + error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX"); + return; + } + cs = CPU(cpu); cs->cpu_index = idx; diff --git a/target/i386/hyperv.c b/target/i386/hyperv.c index 7cc0fbb272..3065d765ed 100644 --- a/target/i386/hyperv.c +++ b/target/i386/hyperv.c @@ -16,6 +16,16 @@ #include "hyperv.h" #include "hyperv-proto.h" +uint32_t hyperv_vp_index(X86CPU *cpu) +{ + return CPU(cpu)->cpu_index; +} + +X86CPU *hyperv_find_vcpu(uint32_t vp_index) +{ + return X86_CPU(qemu_get_cpu(vp_index)); +} + int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) { CPUX86State *env = &cpu->env; diff --git a/target/i386/hyperv.h b/target/i386/hyperv.h index eaf3df34b0..00c9b454bb 100644 --- a/target/i386/hyperv.h +++ b/target/i386/hyperv.h @@ -39,4 +39,7 @@ void kvm_hv_sint_route_destroy(HvSintRoute *sint_route); int kvm_hv_sint_route_set_sint(HvSintRoute *sint_route); +uint32_t hyperv_vp_index(X86CPU *cpu); +X86CPU *hyperv_find_vcpu(uint32_t vp_index); + #endif diff --git a/target/i386/kvm-stub.c b/target/i386/kvm-stub.c index bda4dc2f0c..e7a673e5db 100644 --- a/target/i386/kvm-stub.c +++ b/target/i386/kvm-stub.c @@ -40,3 +40,8 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, abort(); } #endif + +bool kvm_hv_vpindex_settable(void) +{ + return false; +} diff --git a/target/i386/kvm.c b/target/i386/kvm.c index ebb2d23aa4..9313602d3d 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -85,6 +85,7 @@ static bool has_msr_hv_hypercall; static bool has_msr_hv_crash; static bool has_msr_hv_reset; static bool has_msr_hv_vpindex; +static bool hv_vpindex_settable; static bool has_msr_hv_runtime; static bool has_msr_hv_synic; static bool has_msr_hv_stimer; @@ -162,6 +163,11 @@ bool kvm_enable_x2apic(void) has_x2apic_api); } +bool kvm_hv_vpindex_settable(void) +{ + return hv_vpindex_settable; +} + static int kvm_get_tsc(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); @@ -745,6 +751,37 @@ static int hyperv_handle_properties(CPUState *cs) return 0; } +static int hyperv_init_vcpu(X86CPU *cpu) +{ + if (cpu->hyperv_vpindex && !hv_vpindex_settable) { + /* + * the kernel doesn't support setting vp_index; assert that its value + * is in sync + */ + int ret; + struct { + struct kvm_msrs info; + struct kvm_msr_entry entries[1]; + } msr_data = { + .info.nmsrs = 1, + .entries[0].index = HV_X64_MSR_VP_INDEX, + }; + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, &msr_data); + if (ret < 0) { + return ret; + } + assert(ret == 1); + + if (msr_data.entries[0].data != hyperv_vp_index(cpu)) { + error_report("kernel's vp_index != QEMU's vp_index"); + return -ENXIO; + } + } + + return 0; +} + static Error *invtsc_mig_blocker; #define KVM_MAX_CPUID_ENTRIES 100 @@ -1160,6 +1197,11 @@ int kvm_arch_init_vcpu(CPUState *cs) has_msr_tsc_aux = false; } + r = hyperv_init_vcpu(cpu); + if (r) { + goto fail; + } + return 0; fail: @@ -1351,6 +1393,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) has_pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); #endif + hv_vpindex_settable = kvm_check_extension(s, KVM_CAP_HYPERV_VP_INDEX); + ret = kvm_get_supported_msrs(s); if (ret < 0) { return ret; @@ -1900,6 +1944,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_hv_runtime) { kvm_msr_entry_add(cpu, HV_X64_MSR_VP_RUNTIME, env->msr_hv_runtime); } + if (cpu->hyperv_vpindex && hv_vpindex_settable) { + kvm_msr_entry_add(cpu, HV_X64_MSR_VP_INDEX, hyperv_vp_index(cpu)); + } if (cpu->hyperv_synic) { int j; diff --git a/target/i386/kvm_i386.h b/target/i386/kvm_i386.h index e5df24cad1..3057ba4f7d 100644 --- a/target/i386/kvm_i386.h +++ b/target/i386/kvm_i386.h @@ -63,4 +63,6 @@ void kvm_put_apicbase(X86CPU *cpu, uint64_t value); bool kvm_enable_x2apic(void); bool kvm_has_x2apic_api(void); + +bool kvm_hv_vpindex_settable(void); #endif From 9ee8a692f1da77d798842fdc1e0dc2a5ff0fc0c1 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 26 Jun 2018 16:18:53 +0200 Subject: [PATCH 2229/2380] vhost-user-test: added proper TestServer *dest initialization in test_migrate() server->bus in _test_server_free() could be NULL, since TestServer *dest in test_migrate() was not properly initialized like TestServer *s. Added init_virtio_dev(dest) and uninit_virtio_dev(dest), so the fields are properly set and when test_server_free(dest); is called, they can be correctly freed. The reason for that is init_virtio_dev() calls qpci_init_pc(), that creates a QPCIBusPC * (returned as QPCIBus *), while test_server_free() calls qpci_free_pc(), that frees the QPCIBus *. Not calling init_virtio_dev() would leave the QPCIBus * of TestServer unset. Problem came out once I modified pci-pc.c and pci-pc.h, modifying QPCIBusPC by adding another field before QPCIBus bus. Re-running the tests showed vhost-user-test failing. Signed-off-by: Emanuele Giuseppe Esposito Message-Id: <1530022733-29581-1-git-send-email-esposem@usi.ch> Signed-off-by: Paolo Bonzini --- tests/vhost-user-test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 8ff2106d32..fecc832d99 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -537,6 +537,7 @@ static gboolean _test_server_free(TestServer *server) g_free(server->mig_path); g_free(server->chr_name); + g_assert(server->bus); qpci_free_pc(server->bus); g_free(server); @@ -684,6 +685,7 @@ static void test_migrate(void) g_free(cmd); init_virtio_dev(s, 1u << VIRTIO_NET_F_MAC); + init_virtio_dev(dest, 1u << VIRTIO_NET_F_MAC); wait_for_fds(s); size = get_log_size(s); g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); @@ -739,6 +741,7 @@ static void test_migrate(void) read_guest_mem_server(dest); uninit_virtio_dev(s); + uninit_virtio_dev(dest); g_source_destroy(source); g_source_unref(source); From 0147883450fe84bb8de2d4a58381881f4262ce9b Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Fri, 11 May 2018 18:05:44 -0600 Subject: [PATCH 2230/2380] PC Chipset: Improve serial divisor calculation This fixes several problems I found in the UART serial implementation. Now all divisor values are allowed, while before divisor values of zero and below the base baud rate were rejected. All changes are in reference to http://www.sci.muni.cz/docs/pc/serport.txt Signed-off-by: Calvin Lee Message-Id: <20180512000545.966-2-cyrus296@gmail.com> Signed-off-by: Paolo Bonzini --- hw/char/serial.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/hw/char/serial.c b/hw/char/serial.c index cd7d747c68..d3b75f09f6 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -150,13 +150,10 @@ static void serial_update_irq(SerialState *s) static void serial_update_parameters(SerialState *s) { - int speed, parity, data_bits, stop_bits, frame_size; + float speed; + int parity, data_bits, stop_bits, frame_size; QEMUSerialSetParams ssp; - if (s->divider == 0 || s->divider > s->baudbase) { - return; - } - /* Start bit. */ frame_size = 1; if (s->lcr & 0x08) { @@ -169,14 +166,16 @@ static void serial_update_parameters(SerialState *s) } else { parity = 'N'; } - if (s->lcr & 0x04) + if (s->lcr & 0x04) { stop_bits = 2; - else + } else { stop_bits = 1; + } data_bits = (s->lcr & 0x03) + 5; frame_size += data_bits + stop_bits; - speed = s->baudbase / s->divider; + /* Zero divisor should give about 3500 baud */ + speed = (s->divider == 0) ? 3500 : (float) s->baudbase / s->divider; ssp.speed = speed; ssp.parity = parity; ssp.data_bits = data_bits; @@ -184,7 +183,7 @@ static void serial_update_parameters(SerialState *s) s->char_transmit_time = (NANOSECONDS_PER_SECOND / speed) * frame_size; qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); - DPRINTF("speed=%d parity=%c data=%d stop=%d\n", + DPRINTF("speed=%.2f parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits); } @@ -341,7 +340,11 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, default: case 0: if (s->lcr & UART_LCR_DLAB) { - s->divider = (s->divider & 0xff00) | val; + if (size == 2) { + s->divider = (s->divider & 0xff00) | val; + } else if (size == 4) { + s->divider = val; + } serial_update_parameters(s); } else { s->thr = (uint8_t) val; From f3575af130c700cea060b51a89008a76dae22259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 16 Jul 2018 13:07:55 +0200 Subject: [PATCH 2231/2380] hw/char/serial: retry write if EAGAIN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the chardev returns -1 with EAGAIN errno on write(), it should try to send it again (EINTR is handled by the chardev itself). This fixes commit 019288bf137183bf3407c9824655b753bfafc99f "hw/char/serial: Only retry if qemu_chr_fe_write returns 0" Tested-by: Igor Mammedov Signed-off-by: Marc-André Lureau Message-Id: <20180716110755.12499-1-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini --- hw/char/serial.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/hw/char/serial.c b/hw/char/serial.c index d3b75f09f6..251f40fdac 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -260,15 +260,20 @@ static void serial_xmit(SerialState *s) if (s->mcr & UART_MCR_LOOP) { /* in loopback mode, say that we just received a char */ serial_receive1(s, &s->tsr, 1); - } else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) == 0 && - s->tsr_retry < MAX_XMIT_RETRY) { - assert(s->watch_tag == 0); - s->watch_tag = - qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, - serial_watch_cb, s); - if (s->watch_tag > 0) { - s->tsr_retry++; - return; + } else { + int rc = qemu_chr_fe_write(&s->chr, &s->tsr, 1); + + if ((rc == 0 || + (rc == -1 && errno == EAGAIN)) && + s->tsr_retry < MAX_XMIT_RETRY) { + assert(s->watch_tag == 0); + s->watch_tag = + qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + serial_watch_cb, s); + if (s->watch_tag > 0) { + s->tsr_retry++; + return; + } } } s->tsr_retry = 0; From 25e8978817a54745c44d956d8303e6be6f2c4047 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 16 Jul 2018 09:37:31 +0100 Subject: [PATCH 2232/2380] qdev: add HotplugHandler->post_plug() callback The ->pre_plug() callback is invoked before the device is realized. The ->plug() callback is invoked when the device is being realized but before it is reset. This patch adds a ->post_plug() callback which is invoked after the device has been reset. This callback is needed by HotplugHandlers that need to wait until after ->reset(). Signed-off-by: Stefan Hajnoczi Message-Id: <20180716083732.3347-2-stefanha@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/hotplug.c | 10 ++++++++++ hw/core/qdev.c | 4 ++++ include/hw/hotplug.h | 11 +++++++++++ 3 files changed, 25 insertions(+) diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c index 17ac986685..2253072d0e 100644 --- a/hw/core/hotplug.c +++ b/hw/core/hotplug.c @@ -35,6 +35,16 @@ void hotplug_handler_plug(HotplugHandler *plug_handler, } } +void hotplug_handler_post_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev) +{ + HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + + if (hdc->post_plug) { + hdc->post_plug(plug_handler, plugged_dev); + } +} + void hotplug_handler_unplug_request(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index cf0db4b6da..529b82de18 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -867,6 +867,10 @@ static void device_set_realized(Object *obj, bool value, Error **errp) device_reset(dev); } dev->pending_deleted_event = false; + + if (hotplug_ctrl) { + hotplug_handler_post_plug(hotplug_ctrl, dev); + } } else if (!value && dev->realized) { Error **local_errp = NULL; QLIST_FOREACH(bus, &dev->child_bus, sibling) { diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h index 1a0516a479..51541d63e1 100644 --- a/include/hw/hotplug.h +++ b/include/hw/hotplug.h @@ -47,6 +47,8 @@ typedef void (*hotplug_fn)(HotplugHandler *plug_handler, * @parent: Opaque parent interface. * @pre_plug: pre plug callback called at start of device.realize(true) * @plug: plug callback called at end of device.realize(true). + * @post_plug: post plug callback called after device.realize(true) and device + * reset * @unplug_request: unplug request callback. * Used as a means to initiate device unplug for devices that * require asynchronous unplug handling. @@ -61,6 +63,7 @@ typedef struct HotplugHandlerClass { /* */ hotplug_fn pre_plug; hotplug_fn plug; + void (*post_plug)(HotplugHandler *plug_handler, DeviceState *plugged_dev); hotplug_fn unplug_request; hotplug_fn unplug; } HotplugHandlerClass; @@ -83,6 +86,14 @@ void hotplug_handler_pre_plug(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp); +/** + * hotplug_handler_post_plug: + * + * Call #HotplugHandlerClass.post_plug callback of @plug_handler. + */ +void hotplug_handler_post_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev); + /** * hotplug_handler_unplug_request: * From 8449bcf94986156a1476d6647c75ec1ce3db64d0 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 16 Jul 2018 09:37:32 +0100 Subject: [PATCH 2233/2380] virtio-scsi: fix hotplug ->reset() vs event race There is a race condition during hotplug when iothread is used. It occurs because virtio-scsi may be processing command queues in the iothread while the monitor performs SCSI device hotplug. When a SCSI device is hotplugged the HotplugHandler->plug() callback is invoked and virtio-scsi emits a rescan event to the guest. If the guest submits a SCSI command at this point then it may be cancelled before hotplug completes. This happens because ->reset() is called by hw/core/qdev.c:device_set_realized() after HotplugHandler->plug() has been called and hw/scsi/scsi-disk.c:scsi_disk_reset() purges all requests. This patch uses the new HotplugHandler->post_plug() callback to emit the rescan event after ->reset(). This eliminates the race conditions where requests could be cancelled. Reported-by: l00284672 Cc: Paolo Bonzini Cc: Fam Zheng Signed-off-by: Stefan Hajnoczi Message-Id: <20180716083732.3347-3-stefanha@redhat.com> Signed-off-by: Paolo Bonzini --- hw/scsi/virtio-scsi.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 3aa99717e2..5a3057d1f8 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -797,8 +797,16 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, virtio_scsi_acquire(s); blk_set_aio_context(sd->conf.blk, s->ctx); virtio_scsi_release(s); - } +} + +/* Announce the new device after it has been plugged */ +static void virtio_scsi_post_hotplug(HotplugHandler *hotplug_dev, + DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + SCSIDevice *sd = SCSI_DEVICE(dev); if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { virtio_scsi_acquire(s); @@ -968,6 +976,7 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data) vdc->start_ioeventfd = virtio_scsi_dataplane_start; vdc->stop_ioeventfd = virtio_scsi_dataplane_stop; hc->plug = virtio_scsi_hotplug; + hc->post_plug = virtio_scsi_post_hotplug; hc->unplug = virtio_scsi_hotunplug; } From 628fc75f3a3bb115de3b445c1a18547c44613cfe Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Jul 2018 17:18:41 +0100 Subject: [PATCH 2234/2380] target/arm: Fix LD1W and LDFF1W (scalar plus vector) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'I' was being double-incremented; correctly within the inner loop and incorrectly within the outer loop. Signed-off-by: Richard Henderson Reviewed-by: Laurent Desnogues Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 20180711103957.3040-1-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/sve_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index a03ca77354..54795c9194 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -4459,7 +4459,7 @@ void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ intptr_t i, oprsz = simd_oprsz(desc); \ unsigned scale = simd_data(desc); \ uintptr_t ra = GETPC(); \ - for (i = 0; i < oprsz; i++) { \ + for (i = 0; i < oprsz; ) { \ uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ do { \ TYPEM m = 0; \ @@ -4540,7 +4540,7 @@ void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ uintptr_t ra = GETPC(); \ bool first = true; \ mmap_lock(); \ - for (i = 0; i < oprsz; i++) { \ + for (i = 0; i < oprsz; ) { \ uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ do { \ TYPEM m = 0; \ From 333b9c8a684c58f6711521e446e4b26de5addadc Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Mon, 16 Jul 2018 17:18:41 +0100 Subject: [PATCH 2235/2380] aspeed: Implement write-1-{set, clear} for AST2500 strapping The AST2500 SoC family changes the runtime behaviour of the hardware strapping register (SCU70) to write-1-set/write-1-clear, with write-1-clear implemented on the "read-only" SoC revision register (SCU7C). For the the AST2400, the hardware strapping is runtime-configured with read-modify-write semantics. Signed-off-by: Andrew Jeffery Reviewed-by: Joel Stanley Message-id: 20180709143524.17480-1-andrew@aj.id.au Signed-off-by: Peter Maydell --- hw/misc/aspeed_scu.c | 19 +++++++++++++++++-- include/hw/misc/aspeed_scu.h | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 59333b50ab..c8217740ef 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -247,11 +247,26 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data, s->regs[reg] = data; aspeed_scu_set_apb_freq(s); break; - + case HW_STRAP1: + if (ASPEED_IS_AST2500(s->regs[SILICON_REV])) { + s->regs[HW_STRAP1] |= data; + return; + } + /* Jump to assignment below */ + break; + case SILICON_REV: + if (ASPEED_IS_AST2500(s->regs[SILICON_REV])) { + s->regs[HW_STRAP1] &= ~data; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write to read-only offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + /* Avoid assignment below, we've handled everything */ + return; case FREQ_CNTR_EVAL: case VGA_SCRATCH1 ... VGA_SCRATCH8: case RNG_DATA: - case SILICON_REV: case FREE_CNTR4: case FREE_CNTR4_EXT: qemu_log_mask(LOG_GUEST_ERROR, diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index f662c38188..38996adc59 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -41,6 +41,8 @@ typedef struct AspeedSCUState { #define AST2500_A0_SILICON_REV 0x04000303U #define AST2500_A1_SILICON_REV 0x04010303U +#define ASPEED_IS_AST2500(si_rev) ((((si_rev) >> 24) & 0xff) == 0x04) + extern bool is_supported_silicon_rev(uint32_t silicon_rev); #define ASPEED_SCU_PROT_KEY 0x1688A8A8 From ee03cca88ec2e4cd1ffd319764cced1cab707ee2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 16 Jul 2018 17:18:41 +0100 Subject: [PATCH 2236/2380] hw/intc/arm_gic: Check interrupt number in gic_deactivate_irq() In gic_deactivate_irq() the interrupt number comes from the guest (on a write to the GICC_DIR register), so we need to sanity check that it isn't out of range before we use it as an array index. Handle this in a similar manner to the check we do in gic_complete_irq() for the GICC_EOI register. The array overrun is not disastrous because the calling code uses (value & 0x3ff) to extract the interrupt field, so the only out-of-range values possible are 1020..1023, which allow overrunning only from irq_state[] into the following irq_target[] array which the guest can already manipulate. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20180712154152.32183-2-peter.maydell@linaro.org --- hw/intc/arm_gic.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index ea0323f969..b0a69d6386 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -543,7 +543,21 @@ static bool gic_eoi_split(GICState *s, int cpu, MemTxAttrs attrs) static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) { int cm = 1 << cpu; - int group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); + int group; + + if (irq >= s->num_irq) { + /* + * This handles two cases: + * 1. If software writes the ID of a spurious interrupt [ie 1023] + * to the GICC_DIR, the GIC ignores that write. + * 2. If software writes the number of a non-existent interrupt + * this must be a subcase of "value written is not an active interrupt" + * and so this is UNPREDICTABLE. We choose to ignore it. + */ + return; + } + + group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); if (!gic_eoi_split(s, cpu, attrs)) { /* This is UNPREDICTABLE; we choose to ignore it */ From 7995206d057409cff9d4e850bdc8296c8fc21d38 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 16 Jul 2018 17:18:41 +0100 Subject: [PATCH 2237/2380] hw/intc/arm_gic: Fix handling of GICD_ITARGETSR The GICD_ITARGETSR implementation still has some 11MPCore behaviour that we were incorrectly using in our GICv1 and GICv2 implementations for the case where the interrupt number is less than GIC_INTERNAL. The desired behaviour here is: * for 11MPCore: RAZ/WI for irqs 0..28; read a number matching the CPU doing the read for irqs 29..31 * for GICv1 and v2: RAZ/WI if uniprocessor; otherwise read a number matching the CPU doing the read for all irqs < 32 Stop squashing GICD_ITARGETSR to 0 for IRQs 0..28 unless this is an 11MPCore GIC. Reported-by: Jan Kiszka Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20180712154152.32183-3-peter.maydell@linaro.org --- hw/intc/arm_gic.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index b0a69d6386..34dc84ae81 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -751,7 +751,9 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) if (irq >= s->num_irq) { goto bad_reg; } - if (irq >= 29 && irq <= 31) { + if (irq < 29 && s->revision == REV_11MPCORE) { + res = 0; + } else if (irq < GIC_INTERNAL) { res = cm; } else { res = GIC_TARGET(irq); @@ -1014,7 +1016,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, if (irq >= s->num_irq) { goto bad_reg; } - if (irq < 29) { + if (irq < 29 && s->revision == REV_11MPCORE) { value = 0; } else if (irq < GIC_INTERNAL) { value = ALL_CPU_MASK; From cccf96c3d4263125e6d2c23ad264001ca2e6fffa Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 17:18:41 +0100 Subject: [PATCH 2238/2380] hw/arm/bcm2836: Mark the bcm2836 / bcm2837 devices with user_creatable = false These devices are currently causing some problems when a user is trying to hot-plug or introspect them during runtime. Since these devices can not be instantiated by the user at all (they need to be wired up in code instead), we should mark them with user_creatable = false anyway, then we avoid at least the crashes with the hot-plugging. The introspection problem will be handled by a separate patch. Signed-off-by: Thomas Huth Message-id: 1531415537-26037-1-git-send-email-thuth@redhat.com Reviewed-by: Peter Maydell Reviewed-by: Markus Armbruster Signed-off-by: Peter Maydell --- hw/arm/bcm2836.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 6805a7d7c8..45d9e40c45 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -185,6 +185,8 @@ static void bcm283x_class_init(ObjectClass *oc, void *data) bc->info = data; dc->realize = bcm2836_realize; dc->props = bcm2836_props; + /* Reason: Must be wired up in code (see raspi_init() function) */ + dc->user_creatable = false; } static const TypeInfo bcm283x_type_info = { From 65e9f27f22ba273672a1960cabad0e6aae0fbba2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 16 Jul 2018 17:18:42 +0100 Subject: [PATCH 2239/2380] bcm2835_aux: Swap RX and TX interrupt assignments RX and TX interrupt bits were reversed, resulting in an endless sequence of serial interupts in the emulated system and the following repeated error message when booting Linux. serial8250: too much work for irq61 This results in a boot failure most of the time. Qemu command line used to reproduce the problem: qemu-system-aarch64 -M raspi3 -m 1024 \ -kernel arch/arm64/boot/Image \ --append "rdinit=/sbin/init console=ttyS1,115200" -initrd rootfs.cpio \ -dtb arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b.dtb \ -nographic -monitor null -serial null -serial stdio This is with arm64:defconfig. The root file system was generated using buildroot. NB that this error likely arises from an erratum in the BCM2835 datasheet where the TX and RX bits were swapped in the AU_MU_IER_REG description (but correct for IIR): https://elinux.org/BCM2835_datasheet_errata#p12 Signed-off-by: Guenter Roeck Message-id: 1529355846-25102-1-git-send-email-linux@roeck-us.net Reviewed-by: Peter Maydell [PMM: added NB about datasheet] Signed-off-by: Peter Maydell --- hw/char/bcm2835_aux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 370dc7e296..0364596c55 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -39,8 +39,8 @@ #define AUX_MU_BAUD_REG 0x68 /* bits in IER/IIR registers */ -#define TX_INT 0x1 -#define RX_INT 0x2 +#define RX_INT 0x1 +#define TX_INT 0x2 static void bcm2835_aux_update(BCM2835AuxState *s) { From b493ccf1fc82674ef73564b3c61e309105c9336b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Jul 2018 15:16:35 +0100 Subject: [PATCH 2240/2380] accel/tcg: Use correct test when looking in victim TLB for code In get_page_addr_code(), we were incorrectly looking in the victim TLB for an entry which matched the target address for reads, not for code accesses. This meant that we could hit on a victim TLB entry that indicated that the address was readable but not executable, and incorrectly bypass the call to tlb_fill() which should generate the guest MMU exception. Fix this bug. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180713141636.18665-2-peter.maydell@linaro.org --- accel/tcg/cputlb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 20c147d655..2d5fb15d9a 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -967,7 +967,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); mmu_idx = cpu_mmu_index(env, true); if (unlikely(!tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr))) { - if (!VICTIM_TLB_HIT(addr_read, addr)) { + if (!VICTIM_TLB_HIT(addr_code, addr)) { tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0); } } From 3474c98a2a2afcefa7c665f02ad2bed2a43ab0f7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Jul 2018 15:16:36 +0100 Subject: [PATCH 2241/2380] accel/tcg: Assert that tlb fill gave us a valid TLB entry In commit 4b1a3e1e34ad97 we added a check for whether the TLB entry we had following a tlb_fill had the INVALID bit set. This could happen in some circumstances because a stale or wrong TLB entry was pulled out of the victim cache. However, after commit 68fea038553039e (which prevents stale entries being in the victim cache) and the previous commit (which ensures we don't incorrectly hit in the victim cache)) this should never be possible. Drop the check on TLB_INVALID_MASK from the "is this a TLB_RECHECK?" condition, and instead assert that the tlb fill procedure has given us a valid TLB entry (or longjumped out with a guest exception). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180713141636.18665-3-peter.maydell@linaro.org --- accel/tcg/cputlb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 2d5fb15d9a..563fa30117 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -970,10 +970,10 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) if (!VICTIM_TLB_HIT(addr_code, addr)) { tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0); } + assert(tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr)); } - if (unlikely((env->tlb_table[mmu_idx][index].addr_code & - (TLB_RECHECK | TLB_INVALID_MASK)) == TLB_RECHECK)) { + if (unlikely(env->tlb_table[mmu_idx][index].addr_code & TLB_RECHECK)) { /* * This is a TLB_RECHECK access, where the MMU protection * covers a smaller range than a target page, and we must From bb23a7362a7942739f080990a53e44afc319e36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 5 Jul 2018 18:16:29 +0200 Subject: [PATCH 2242/2380] qga: fix 'driver' leak in guest-get-fsinfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'driver' is leaked when the loop is not broken. Leak introduced by commit 743c71d03c20d64f2bae5fba6f26cdf5e4b1bda6, spotted by ASAN. Signed-off-by: Marc-André Lureau Reviewed-by: Laszlo Ersek Signed-off-by: Michael Roth --- qga/commands-posix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 233f78a406..c46767b0dd 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -890,6 +890,7 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, break; } + g_free(driver); if (sscanf(p, "/%x:%x:%x.%x%n", pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) { p += pcilen; From d9c745c1768727aadd77e352c85114bc61e67bd4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 15 Jul 2018 18:21:56 +0200 Subject: [PATCH 2243/2380] qga: fix file descriptor leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The file descriptor for /sys/power/state was never closed. Reported by Coverity. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Michael Roth --- qga/commands-posix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index c46767b0dd..37e8a2d791 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1652,6 +1652,7 @@ static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) } ret = read(fd, buf, sizeof(buf) - 1); + close(fd); if (ret <= 0) { return false; } From 0210b39d0e84d5a63a3e468f177c07a3a98d88a8 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:18 +0200 Subject: [PATCH 2244/2380] qom/object: Add a new function object_initialize_child() A lot of code is using the object_initialize() function followed by a call to object_property_add_child() to add the newly initialized object as a child of the current object. Both functions increase the reference counter of the new object, but many spots that call these two functions then forget to drop one of the superfluous references. So the newly created object is often not cleaned up correctly when the parent is destroyed. In the worst case, this can cause crashes, e.g. because device objects are not correctly removed from their parent_bus. Since this is a common pattern between many code spots, let's introduce a new function that takes care of calling all three required initialization functions, first object_initialize(), then object_property_add_child() and finally object_unref(). And since the function does a similar job like object_new_with_props(), also allow to set additional properties via varargs, and use user_creatable_complete() to make sure that the functions can be used similarly. And while we're at object.h, also fix some copy-n-paste errors in the comments there ("to store the area" --> "to store the error"). Signed-off-by: Thomas Huth Reviewed-by: Eduardo Habkost Message-id: 1531745974-17187-2-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- include/qom/object.h | 45 ++++++++++++++++++++++++++++++++++-- qom/object.c | 54 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index f3d2308d56..f0b0bf39cc 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -748,6 +748,47 @@ int object_set_propv(Object *obj, */ void object_initialize(void *obj, size_t size, const char *typename); +/** + * object_initialize_child: + * @parentobj: The parent object to add a property to + * @propname: The name of the property + * @childobj: A pointer to the memory to be used for the object. + * @size: The maximum size available at @childobj for the object. + * @type: The name of the type of the object to instantiate. + * @errp: If an error occurs, a pointer to an area to store the error + * @...: list of property names and values + * + * This function will initialize an object. The memory for the object should + * have already been allocated. The object will then be added as child property + * to a parent with object_property_add_child() function. The returned object + * has a reference count of 1 (for the "child<...>" property from the parent), + * so the object will be finalized automatically when the parent gets removed. + * + * The variadic parameters are a list of pairs of (propname, propvalue) + * strings. The propname of %NULL indicates the end of the property list. + * If the object implements the user creatable interface, the object will + * be marked complete once all the properties have been processed. + */ +void object_initialize_child(Object *parentobj, const char *propname, + void *childobj, size_t size, const char *type, + Error **errp, ...) QEMU_SENTINEL; + +/** + * object_initialize_childv: + * @parentobj: The parent object to add a property to + * @propname: The name of the property + * @childobj: A pointer to the memory to be used for the object. + * @size: The maximum size available at @childobj for the object. + * @type: The name of the type of the object to instantiate. + * @errp: If an error occurs, a pointer to an area to store the error + * @vargs: list of property names and values + * + * See object_initialize_child() for documentation. + */ +void object_initialize_childv(Object *parentobj, const char *propname, + void *childobj, size_t size, const char *type, + Error **errp, va_list vargs); + /** * object_dynamic_cast: * @obj: The object to cast. @@ -1382,7 +1423,7 @@ Object *object_resolve_path_component(Object *parent, const gchar *part); * @obj: the object to add a property to * @name: the name of the property * @child: the child object - * @errp: if an error occurs, a pointer to an area to store the area + * @errp: if an error occurs, a pointer to an area to store the error * * Child properties form the composition tree. All objects need to be a child * of another object. Objects can only be a child of one object. @@ -1420,7 +1461,7 @@ void object_property_allow_set_link(const Object *, const char *, * @child: a pointer to where the link object reference is stored * @check: callback to veto setting or NULL if the property is read-only * @flags: additional options for the link - * @errp: if an error occurs, a pointer to an area to store the area + * @errp: if an error occurs, a pointer to an area to store the error * * Links establish relationships between objects. Links are unidirectional * although two links can be combined to form a bidirectional relationship diff --git a/qom/object.c b/qom/object.c index 4609e34a6a..75d1d48944 100644 --- a/qom/object.c +++ b/qom/object.c @@ -392,6 +392,60 @@ void object_initialize(void *data, size_t size, const char *typename) object_initialize_with_type(data, size, type); } +void object_initialize_child(Object *parentobj, const char *propname, + void *childobj, size_t size, const char *type, + Error **errp, ...) +{ + va_list vargs; + + va_start(vargs, errp); + object_initialize_childv(parentobj, propname, childobj, size, type, errp, + vargs); + va_end(vargs); +} + +void object_initialize_childv(Object *parentobj, const char *propname, + void *childobj, size_t size, const char *type, + Error **errp, va_list vargs) +{ + Error *local_err = NULL; + Object *obj; + + object_initialize(childobj, size, type); + obj = OBJECT(childobj); + + object_set_propv(obj, &local_err, vargs); + if (local_err) { + goto out; + } + + object_property_add_child(parentobj, propname, obj, &local_err); + if (local_err) { + goto out; + } + + if (object_dynamic_cast(obj, TYPE_USER_CREATABLE)) { + user_creatable_complete(obj, &local_err); + if (local_err) { + object_unparent(obj); + goto out; + } + } + + /* + * Since object_property_add_child added a reference to the child object, + * we can drop the reference added by object_initialize(), so the child + * property will own the only reference to the object. + */ + object_unref(obj); + +out: + if (local_err) { + error_propagate(errp, local_err); + object_unref(obj); + } +} + static inline bool object_property_is_child(ObjectProperty *prop) { return strstart(prop->type, "child<", NULL); From 046f370fb44172a34e54e341e7aeca4405af67a1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:19 +0200 Subject: [PATCH 2245/2380] hw/core/sysbus: Add a function for creating and attaching an object A lot of functions are initializing an object and attach it immediately afterwards to the system bus. Provide a common function for this, which also uses object_initialize_child() to make sure that the reference counter is correctly initialized to 1 afterwards. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Reviewed-by: Alistair Francis Message-id: 1531745974-17187-3-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/core/sysbus.c | 8 ++++++++ include/hw/sysbus.h | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index ecfb0cfc0e..3c8e53b188 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -376,6 +376,14 @@ BusState *sysbus_get_default(void) return main_system_bus; } +void sysbus_init_child_obj(Object *parent, const char *childname, void *child, + size_t childsize, const char *childtype) +{ + object_initialize_child(parent, childname, child, childsize, childtype, + &error_abort, NULL); + qdev_set_parent_bus(DEVICE(child), sysbus_get_default()); +} + static void sysbus_register_types(void) { type_register_static(&system_bus_info); diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index e88bb6dae0..0b59a3b8d6 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -96,6 +96,23 @@ void sysbus_add_io(SysBusDevice *dev, hwaddr addr, MemoryRegion *mem); MemoryRegion *sysbus_address_space(SysBusDevice *dev); +/** + * sysbus_init_child_obj: + * @parent: The parent object + * @childname: Used as name of the "child<>" property in the parent + * @child: A pointer to the memory to be used for the object. + * @childsize: The maximum size available at @child for the object. + * @childtype: The name of the type of the object to instantiate. + * + * This function will initialize an object and attach it to the main system + * bus. The memory for the object should have already been allocated. The + * object will then be added as child to the given parent. The returned object + * has a reference count of 1 (for the "child<...>" property from the parent), + * so the object will be finalized automatically when the parent gets removed. + */ +void sysbus_init_child_obj(Object *parent, const char *childname, void *child, + size_t childsize, const char *childtype); + /* Call func for every dynamically created sysbus device in the system */ void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque); From 14c520e335304ba79f6a68b1ea1d90895790b065 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:20 +0200 Subject: [PATCH 2246/2380] hw/arm/bcm2836: Fix crash with device_add bcm2837 on unsupported machines When trying to "device_add bcm2837" on a machine that is not suitable for this device, you can quickly crash QEMU afterwards, e.g. with "info qtree": echo "{'execute':'qmp_capabilities'} {'execute':'device_add', " \ "'arguments':{'driver':'bcm2837'}} {'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ aarch64-softmmu/qemu-system-aarch64 -M integratorcp,accel=qtest -S -qmp stdio {"QMP": {"version": {"qemu": {"micro": 50, "minor": 12, "major": 2}, "package": "build-all"}, "capabilities": []}} {"return": {}} {"error": {"class": "GenericError", "desc": "Device 'bcm2837' can not be hotplugged on this machine"}} Segmentation fault (core dumped) The qdev_set_parent_bus() from instance_init adds a link to the child devices which is not valid anymore after the bcm2837 instance has been destroyed. Unfortunately, the child devices do not get destroyed / unlinked correctly because both object_initialize() and object_property_add_child() increase the reference count of the child objects by one, but only one reference is dropped when the parent gets removed. So let's use the new functions object_initialize_child() and sysbus_init_child_obj() instead to create the objects, which will take care of creating the child objects with the correct reference count of one. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Message-id: 1531745974-17187-4-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/arm/bcm2836.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 45d9e40c45..6a09c339d3 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -51,25 +51,19 @@ static void bcm2836_init(Object *obj) int n; for (n = 0; n < BCM283X_NCPUS; n++) { - object_initialize(&s->cpus[n], sizeof(s->cpus[n]), - info->cpu_type); - object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]), - &error_abort); + object_initialize_child(obj, "cpu[*]", &s->cpus[n], sizeof(s->cpus[n]), + info->cpu_type, &error_abort, NULL); } - object_initialize(&s->control, sizeof(s->control), TYPE_BCM2836_CONTROL); - object_property_add_child(obj, "control", OBJECT(&s->control), NULL); - qdev_set_parent_bus(DEVICE(&s->control), sysbus_get_default()); + sysbus_init_child_obj(obj, "control", &s->control, sizeof(s->control), + TYPE_BCM2836_CONTROL); - object_initialize(&s->peripherals, sizeof(s->peripherals), - TYPE_BCM2835_PERIPHERALS); - object_property_add_child(obj, "peripherals", OBJECT(&s->peripherals), - &error_abort); + sysbus_init_child_obj(obj, "peripherals", &s->peripherals, + sizeof(s->peripherals), TYPE_BCM2835_PERIPHERALS); object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals), "board-rev", &error_abort); object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals), "vcram-size", &error_abort); - qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default()); } static void bcm2836_realize(DeviceState *dev, Error **errp) From 955cbc6b178fec1f656e702774390c2023798fb7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:21 +0200 Subject: [PATCH 2247/2380] hw/arm/armv7: Fix crash when introspecting the "iotkit" device QEMU currently crashes when introspecting the "iotkit" device and runnint "info qtree" afterwards, e.g. when running QEMU like this: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'iotkit'}}" "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio Use the new functions object_initialize_child() and sysbus_init_child_obj() to make sure that all objects get cleaned up correctly when the instances are destroyed. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Reviewed-by: Alistair Francis Message-id: 1531745974-17187-5-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/arm/armv7m.c | 7 ++-- hw/arm/iotkit.c | 74 +++++++++++++++++++------------------------ hw/intc/armv7m_nvic.c | 5 ++- 3 files changed, 37 insertions(+), 49 deletions(-) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 9e00d4037c..6b07666057 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -134,14 +134,13 @@ static void armv7m_instance_init(Object *obj) memory_region_init(&s->container, obj, "armv7m-container", UINT64_MAX); - object_initialize(&s->nvic, sizeof(s->nvic), TYPE_NVIC); - qdev_set_parent_bus(DEVICE(&s->nvic), sysbus_get_default()); + sysbus_init_child_obj(obj, "nvnic", &s->nvic, sizeof(s->nvic), TYPE_NVIC); object_property_add_alias(obj, "num-irq", OBJECT(&s->nvic), "num-irq", &error_abort); for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { - object_initialize(&s->bitband[i], sizeof(s->bitband[i]), TYPE_BITBAND); - qdev_set_parent_bus(DEVICE(&s->bitband[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "bitband[*]", &s->bitband[i], + sizeof(s->bitband[i]), TYPE_BITBAND); } } diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 133d5bb34f..c76d3ed743 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -30,15 +30,6 @@ static void make_alias(IoTKit *s, MemoryRegion *mr, const char *name, memory_region_add_subregion_overlap(&s->container, base, mr, -1500); } -static void init_sysbus_child(Object *parent, const char *childname, - void *child, size_t childsize, - const char *childtype) -{ - object_initialize(child, childsize, childtype); - object_property_add_child(parent, childname, OBJECT(child), &error_abort); - qdev_set_parent_bus(DEVICE(child), sysbus_get_default()); -} - static void irq_status_forwarder(void *opaque, int n, int level) { qemu_irq destirq = opaque; @@ -119,53 +110,52 @@ static void iotkit_init(Object *obj) memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX); - init_sysbus_child(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), - TYPE_ARMV7M); + sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), + TYPE_ARMV7M); qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", ARM_CPU_TYPE_NAME("cortex-m33")); - init_sysbus_child(obj, "secctl", &s->secctl, sizeof(s->secctl), - TYPE_IOTKIT_SECCTL); - init_sysbus_child(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), - TYPE_TZ_PPC); - init_sysbus_child(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), - TYPE_TZ_PPC); - init_sysbus_child(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC); - object_initialize(&s->mpc_irq_orgate, sizeof(s->mpc_irq_orgate), - TYPE_OR_IRQ); - object_property_add_child(obj, "mpc-irq-orgate", - OBJECT(&s->mpc_irq_orgate), &error_abort); + sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl), + TYPE_IOTKIT_SECCTL); + sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), + TYPE_TZ_PPC); + sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), + TYPE_TZ_PPC); + sysbus_init_child_obj(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC); + object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate, + sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ, + &error_abort, NULL); + for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) { char *name = g_strdup_printf("mpc-irq-splitter-%d", i); SplitIRQ *splitter = &s->mpc_irq_splitter[i]; - object_initialize(splitter, sizeof(*splitter), TYPE_SPLIT_IRQ); - object_property_add_child(obj, name, OBJECT(splitter), &error_abort); + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); g_free(name); } - init_sysbus_child(obj, "timer0", &s->timer0, sizeof(s->timer0), - TYPE_CMSDK_APB_TIMER); - init_sysbus_child(obj, "timer1", &s->timer1, sizeof(s->timer1), - TYPE_CMSDK_APB_TIMER); - init_sysbus_child(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), - TYPE_UNIMPLEMENTED_DEVICE); - object_initialize(&s->ppc_irq_orgate, sizeof(s->ppc_irq_orgate), - TYPE_OR_IRQ); - object_property_add_child(obj, "ppc-irq-orgate", - OBJECT(&s->ppc_irq_orgate), &error_abort); - object_initialize(&s->sec_resp_splitter, sizeof(s->sec_resp_splitter), - TYPE_SPLIT_IRQ); - object_property_add_child(obj, "sec-resp-splitter", - OBJECT(&s->sec_resp_splitter), &error_abort); + sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0), + TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1), + TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate, + sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ, + &error_abort, NULL); + object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter, + sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ, + &error_abort, NULL); for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { char *name = g_strdup_printf("ppc-irq-splitter-%d", i); SplitIRQ *splitter = &s->ppc_irq_splitter[i]; - object_initialize(splitter, sizeof(*splitter), TYPE_SPLIT_IRQ); - object_property_add_child(obj, name, OBJECT(splitter), &error_abort); + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); + g_free(name); } - init_sysbus_child(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), - TYPE_UNIMPLEMENTED_DEVICE); + sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), + TYPE_UNIMPLEMENTED_DEVICE); } static void iotkit_exp_irq(void *opaque, int n, int level) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 661be8878a..7a5330f201 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2296,9 +2296,8 @@ static void armv7m_nvic_instance_init(Object *obj) NVICState *nvic = NVIC(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - object_initialize(&nvic->systick[M_REG_NS], - sizeof(nvic->systick[M_REG_NS]), TYPE_SYSTICK); - qdev_set_parent_bus(DEVICE(&nvic->systick[M_REG_NS]), sysbus_get_default()); + sysbus_init_child_obj(obj, "systick-reg-ns", &nvic->systick[M_REG_NS], + sizeof(nvic->systick[M_REG_NS]), TYPE_SYSTICK); /* We can't initialize the secure systick here, as we don't know * yet if we need it. */ From fd31701214ccf01c815a29563e6c0e182676d39c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:22 +0200 Subject: [PATCH 2248/2380] hw/cpu/a15mpcore: Fix introspection problem with the a15mpcore_priv device There is a memory management problem when introspecting the a15mpcore_priv device. It can be seen with valgrind when running QEMU like this: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'a15mpcore_priv'}}"\ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio {"QMP": {"version": {"qemu": {"micro": 50, "minor": 12, "major": 2}, "package": "build-all"}, "capabilities": []}} {"return": {}} {"return": [{"name": "num-cpu", "type": "uint32"}, {"name": "num-irq", "type": "uint32"}, {"name": "a15mp-priv-container[0]", "type": "child"}]} ==24978== Invalid read of size 8 ==24978== at 0x618EBA: qdev_print (qdev-monitor.c:686) ==24978== by 0x618EBA: qbus_print (qdev-monitor.c:719) [...] Use the new sysbus_init_child_obj() function to make sure that we get the reference counting of the child objects right. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Reviewed-by: Alistair Francis Message-id: 1531745974-17187-6-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/cpu/a15mpcore.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index bc05152fd3..43c1079493 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -35,15 +35,13 @@ static void a15mp_priv_initfn(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); A15MPPrivState *s = A15MPCORE_PRIV(obj); - DeviceState *gicdev; memory_region_init(&s->container, obj, "a15mp-priv-container", 0x8000); sysbus_init_mmio(sbd, &s->container); - object_initialize(&s->gic, sizeof(s->gic), gic_class_name()); - gicdev = DEVICE(&s->gic); - qdev_set_parent_bus(gicdev, sysbus_get_default()); - qdev_prop_set_uint32(gicdev, "revision", 2); + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), + gic_class_name()); + qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); } static void a15mp_priv_realize(DeviceState *dev, Error **errp) From d473a0309c4362559ac1ba14f07dc1e78b215a33 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:23 +0200 Subject: [PATCH 2249/2380] hw/arm/msf2-soc: Fix introspection problem with the "msf2-soc" device Valgrind currently reports a problem when running QEMU like this: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'msf2-soc'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio [...] ==23097== Invalid read of size 8 ==23097== at 0x6192AA: qdev_print (qdev-monitor.c:686) ==23097== by 0x6192AA: qbus_print (qdev-monitor.c:719) [...] Use the new sysbus_init_child_obj() function to make sure that the child objects are cleaned up correctly when the parent gets destroyed. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Message-id: 1531745974-17187-7-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/arm/msf2-soc.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index edb3ba824f..dbefade644 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -68,19 +68,18 @@ static void m2sxxx_soc_initfn(Object *obj) MSF2State *s = MSF2_SOC(obj); int i; - object_initialize(&s->armv7m, sizeof(s->armv7m), TYPE_ARMV7M); - qdev_set_parent_bus(DEVICE(&s->armv7m), sysbus_get_default()); + sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), + TYPE_ARMV7M); - object_initialize(&s->sysreg, sizeof(s->sysreg), TYPE_MSF2_SYSREG); - qdev_set_parent_bus(DEVICE(&s->sysreg), sysbus_get_default()); + sysbus_init_child_obj(obj, "sysreg", &s->sysreg, sizeof(s->sysreg), + TYPE_MSF2_SYSREG); - object_initialize(&s->timer, sizeof(s->timer), TYPE_MSS_TIMER); - qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); + sysbus_init_child_obj(obj, "timer", &s->timer, sizeof(s->timer), + TYPE_MSS_TIMER); for (i = 0; i < MSF2_NUM_SPIS; i++) { - object_initialize(&s->spi[i], sizeof(s->spi[i]), + sysbus_init_child_obj(obj, "spi[*]", &s->spi[i], sizeof(s->spi[i]), TYPE_MSS_SPI); - qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); } } From 2ba486e7db9697895a9084e6dacbf522122ca9dd Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:24 +0200 Subject: [PATCH 2250/2380] hw/cpu/a9mpcore: Fix introspection problems with the "a9mpcore_priv" device Running QEMU with valgrind indicates a problem here: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'a9mpcore_priv'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio [...] ==30996== Invalid read of size 8 ==30996== at 0x6185DA: qdev_print (qdev-monitor.c:686) ==30996== by 0x6185DA: qbus_print (qdev-monitor.c:719) ==30996== by 0x452B38: handle_hmp_command (monitor.c:3446) [...] Use the new sysbus_init_child_obj() function to make sure that the objects are cleaned up correctly when the parent gets destroyed. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Reviewed-by: Alistair Francis Message-id: 1531745974-17187-8-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/cpu/a9mpcore.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index f17f292090..a5b867872c 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -27,20 +27,18 @@ static void a9mp_priv_initfn(Object *obj) memory_region_init(&s->container, obj, "a9mp-priv-container", 0x2000); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container); - object_initialize(&s->scu, sizeof(s->scu), TYPE_A9_SCU); - qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); + sysbus_init_child_obj(obj, "scu", &s->scu, sizeof(s->scu), TYPE_A9_SCU); - object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); - qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), TYPE_ARM_GIC); - object_initialize(&s->gtimer, sizeof(s->gtimer), TYPE_A9_GTIMER); - qdev_set_parent_bus(DEVICE(&s->gtimer), sysbus_get_default()); + sysbus_init_child_obj(obj, "gtimer", &s->gtimer, sizeof(s->gtimer), + TYPE_A9_GTIMER); - object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default()); + sysbus_init_child_obj(obj, "mptimer", &s->mptimer, sizeof(s->mptimer), + TYPE_ARM_MPTIMER); - object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default()); + sysbus_init_child_obj(obj, "wdt", &s->wdt, sizeof(s->wdt), + TYPE_ARM_MPTIMER); } static void a9mp_priv_realize(DeviceState *dev, Error **errp) From e9e4d4d3e180a35d244371fd724a9a67404678cf Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:25 +0200 Subject: [PATCH 2251/2380] hw/arm/fsl-imx6: Fix introspection problems with the "fsl, imx6" device Running QEMU with valgrind indicates a problem here: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'fsl,imx6'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio [...] ==32417== Invalid read of size 8 ==32417== at 0x618A7A: qdev_print (qdev-monitor.c:686) ==32417== by 0x618A7A: qbus_print (qdev-monitor.c:719) ==32417== by 0x452B38: handle_hmp_command (monitor.c:3446) [...] Use the new sysbus_init_child_obj() and object_initialize_child() to make sure that the objects are removed correctly when the parent gets destroyed. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Message-id: 1531745974-17187-9-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/arm/fsl-imx6.c | 56 +++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 4f51bd9eb5..7b7b97f74c 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -38,73 +38,57 @@ static void fsl_imx6_init(Object *obj) int i; for (i = 0; i < MIN(smp_cpus, FSL_IMX6_NUM_CPUS); i++) { - object_initialize(&s->cpu[i], sizeof(s->cpu[i]), - "cortex-a9-" TYPE_ARM_CPU); snprintf(name, NAME_SIZE, "cpu%d", i); - object_property_add_child(obj, name, OBJECT(&s->cpu[i]), NULL); + object_initialize_child(obj, name, &s->cpu[i], sizeof(s->cpu[i]), + "cortex-a9-" TYPE_ARM_CPU, &error_abort, NULL); } - object_initialize(&s->a9mpcore, sizeof(s->a9mpcore), TYPE_A9MPCORE_PRIV); - qdev_set_parent_bus(DEVICE(&s->a9mpcore), sysbus_get_default()); - object_property_add_child(obj, "a9mpcore", OBJECT(&s->a9mpcore), NULL); + sysbus_init_child_obj(obj, "a9mpcore", &s->a9mpcore, sizeof(s->a9mpcore), + TYPE_A9MPCORE_PRIV); - object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX6_CCM); - qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); - object_property_add_child(obj, "ccm", OBJECT(&s->ccm), NULL); + sysbus_init_child_obj(obj, "ccm", &s->ccm, sizeof(s->ccm), TYPE_IMX6_CCM); - object_initialize(&s->src, sizeof(s->src), TYPE_IMX6_SRC); - qdev_set_parent_bus(DEVICE(&s->src), sysbus_get_default()); - object_property_add_child(obj, "src", OBJECT(&s->src), NULL); + sysbus_init_child_obj(obj, "src", &s->src, sizeof(s->src), TYPE_IMX6_SRC); for (i = 0; i < FSL_IMX6_NUM_UARTS; i++) { - object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); - qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "uart%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->uart[i]), NULL); + sysbus_init_child_obj(obj, name, &s->uart[i], sizeof(s->uart[i]), + TYPE_IMX_SERIAL); } - object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX6_GPT); - qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); - object_property_add_child(obj, "gpt", OBJECT(&s->gpt), NULL); + sysbus_init_child_obj(obj, "gpt", &s->gpt, sizeof(s->gpt), TYPE_IMX6_GPT); for (i = 0; i < FSL_IMX6_NUM_EPITS; i++) { - object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); - qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "epit%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->epit[i]), NULL); + sysbus_init_child_obj(obj, name, &s->epit[i], sizeof(s->epit[i]), + TYPE_IMX_EPIT); } for (i = 0; i < FSL_IMX6_NUM_I2CS; i++) { - object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); - qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "i2c%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL); + sysbus_init_child_obj(obj, name, &s->i2c[i], sizeof(s->i2c[i]), + TYPE_IMX_I2C); } for (i = 0; i < FSL_IMX6_NUM_GPIOS; i++) { - object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); - qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "gpio%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->gpio[i]), NULL); + sysbus_init_child_obj(obj, name, &s->gpio[i], sizeof(s->gpio[i]), + TYPE_IMX_GPIO); } for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) { - object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_IMX_USDHC); - qdev_set_parent_bus(DEVICE(&s->esdhc[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "sdhc%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->esdhc[i]), NULL); + sysbus_init_child_obj(obj, name, &s->esdhc[i], sizeof(s->esdhc[i]), + TYPE_IMX_USDHC); } for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { - object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI); - qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "spi%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL); + sysbus_init_child_obj(obj, name, &s->spi[i], sizeof(s->spi[i]), + TYPE_IMX_SPI); } - object_initialize(&s->eth, sizeof(s->eth), TYPE_IMX_ENET); - qdev_set_parent_bus(DEVICE(&s->eth), sysbus_get_default()); - object_property_add_child(obj, "eth", OBJECT(&s->eth), NULL); + sysbus_init_child_obj(obj, "eth", &s->eth, sizeof(s->eth), TYPE_IMX_ENET); } static void fsl_imx6_realize(DeviceState *dev, Error **errp) From f8bf4b6d38e966bf7b2fd77a2ef227e0805b6d0d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:26 +0200 Subject: [PATCH 2252/2380] hw/arm/fsl-imx7: Fix introspection problems with the "fsl, imx7" device Running QEMU with valgrind indicates a problem here: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'fsl,imx7'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio [...] ==27284== Invalid read of size 8 ==27284== at 0x618F7A: qdev_print (qdev-monitor.c:686) ==27284== by 0x618F7A: qbus_print (qdev-monitor.c:719) ==27284== by 0x452B38: handle_hmp_command (monitor.c:3446) [...] Use the new sysbus_init_child_obj() and object_initialize_child() to make sure that the objects are removed correctly when the parent gets destroyed. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Message-id: 1531745974-17187-10-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/arm/fsl-imx7.c | 97 ++++++++++++++++------------------------------- 1 file changed, 33 insertions(+), 64 deletions(-) diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 44fde03cbe..d5e26855a5 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -30,157 +30,126 @@ static void fsl_imx7_init(Object *obj) { - BusState *sysbus = sysbus_get_default(); FslIMX7State *s = FSL_IMX7(obj); char name[NAME_SIZE]; int i; for (i = 0; i < MIN(smp_cpus, FSL_IMX7_NUM_CPUS); i++) { - object_initialize(&s->cpu[i], sizeof(s->cpu[i]), - ARM_CPU_TYPE_NAME("cortex-a7")); snprintf(name, NAME_SIZE, "cpu%d", i); - object_property_add_child(obj, name, OBJECT(&s->cpu[i]), - &error_fatal); + object_initialize_child(obj, name, &s->cpu[i], sizeof(s->cpu[i]), + ARM_CPU_TYPE_NAME("cortex-a7"), &error_abort, + NULL); } /* * A7MPCORE */ - object_initialize(&s->a7mpcore, sizeof(s->a7mpcore), TYPE_A15MPCORE_PRIV); - qdev_set_parent_bus(DEVICE(&s->a7mpcore), sysbus); - object_property_add_child(obj, "a7mpcore", - OBJECT(&s->a7mpcore), &error_fatal); + sysbus_init_child_obj(obj, "a7mpcore", &s->a7mpcore, sizeof(s->a7mpcore), + TYPE_A15MPCORE_PRIV); /* * GPIOs 1 to 7 */ for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) { - object_initialize(&s->gpio[i], sizeof(s->gpio[i]), - TYPE_IMX_GPIO); - qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus); snprintf(name, NAME_SIZE, "gpio%d", i); - object_property_add_child(obj, name, - OBJECT(&s->gpio[i]), &error_fatal); + sysbus_init_child_obj(obj, name, &s->gpio[i], sizeof(s->gpio[i]), + TYPE_IMX_GPIO); } /* * GPT1, 2, 3, 4 */ for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) { - object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX7_GPT); - qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus); snprintf(name, NAME_SIZE, "gpt%d", i); - object_property_add_child(obj, name, OBJECT(&s->gpt[i]), - &error_fatal); + sysbus_init_child_obj(obj, name, &s->gpt[i], sizeof(s->gpt[i]), + TYPE_IMX7_GPT); } /* * CCM */ - object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX7_CCM); - qdev_set_parent_bus(DEVICE(&s->ccm), sysbus); - object_property_add_child(obj, "ccm", OBJECT(&s->ccm), &error_fatal); + sysbus_init_child_obj(obj, "ccm", &s->ccm, sizeof(s->ccm), TYPE_IMX7_CCM); /* * Analog */ - object_initialize(&s->analog, sizeof(s->analog), TYPE_IMX7_ANALOG); - qdev_set_parent_bus(DEVICE(&s->analog), sysbus); - object_property_add_child(obj, "analog", OBJECT(&s->analog), &error_fatal); + sysbus_init_child_obj(obj, "analog", &s->analog, sizeof(s->analog), + TYPE_IMX7_ANALOG); /* * GPCv2 */ - object_initialize(&s->gpcv2, sizeof(s->gpcv2), TYPE_IMX_GPCV2); - qdev_set_parent_bus(DEVICE(&s->gpcv2), sysbus); - object_property_add_child(obj, "gpcv2", OBJECT(&s->gpcv2), &error_fatal); + sysbus_init_child_obj(obj, "gpcv2", &s->gpcv2, sizeof(s->gpcv2), + TYPE_IMX_GPCV2); for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) { - object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI); - qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "spi%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL); + sysbus_init_child_obj(obj, name, &s->spi[i], sizeof(s->spi[i]), + TYPE_IMX_SPI); } for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) { - object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); - qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "i2c%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL); + sysbus_init_child_obj(obj, name, &s->i2c[i], sizeof(s->i2c[i]), + TYPE_IMX_I2C); } /* * UART */ for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) { - object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); - qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus); snprintf(name, NAME_SIZE, "uart%d", i); - object_property_add_child(obj, name, OBJECT(&s->uart[i]), - &error_fatal); + sysbus_init_child_obj(obj, name, &s->uart[i], sizeof(s->uart[i]), + TYPE_IMX_SERIAL); } /* * Ethernet */ for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) { - object_initialize(&s->eth[i], sizeof(s->eth[i]), TYPE_IMX_ENET); - qdev_set_parent_bus(DEVICE(&s->eth[i]), sysbus); snprintf(name, NAME_SIZE, "eth%d", i); - object_property_add_child(obj, name, OBJECT(&s->eth[i]), - &error_fatal); + sysbus_init_child_obj(obj, name, &s->eth[i], sizeof(s->eth[i]), + TYPE_IMX_ENET); } /* * SDHCI */ for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) { - object_initialize(&s->usdhc[i], sizeof(s->usdhc[i]), - TYPE_IMX_USDHC); - qdev_set_parent_bus(DEVICE(&s->usdhc[i]), sysbus); snprintf(name, NAME_SIZE, "usdhc%d", i); - object_property_add_child(obj, name, OBJECT(&s->usdhc[i]), - &error_fatal); + sysbus_init_child_obj(obj, name, &s->usdhc[i], sizeof(s->usdhc[i]), + TYPE_IMX_USDHC); } /* * SNVS */ - object_initialize(&s->snvs, sizeof(s->snvs), TYPE_IMX7_SNVS); - qdev_set_parent_bus(DEVICE(&s->snvs), sysbus); - object_property_add_child(obj, "snvs", OBJECT(&s->snvs), &error_fatal); + sysbus_init_child_obj(obj, "snvs", &s->snvs, sizeof(s->snvs), + TYPE_IMX7_SNVS); /* * Watchdog */ for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) { - object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_IMX2_WDT); - qdev_set_parent_bus(DEVICE(&s->wdt[i]), sysbus); snprintf(name, NAME_SIZE, "wdt%d", i); - object_property_add_child(obj, name, OBJECT(&s->wdt[i]), - &error_fatal); + sysbus_init_child_obj(obj, name, &s->wdt[i], sizeof(s->wdt[i]), + TYPE_IMX2_WDT); } /* * GPR */ - object_initialize(&s->gpr, sizeof(s->gpr), TYPE_IMX7_GPR); - qdev_set_parent_bus(DEVICE(&s->gpr), sysbus); - object_property_add_child(obj, "gpr", OBJECT(&s->gpr), &error_fatal); + sysbus_init_child_obj(obj, "gpr", &s->gpr, sizeof(s->gpr), TYPE_IMX7_GPR); - object_initialize(&s->pcie, sizeof(s->pcie), TYPE_DESIGNWARE_PCIE_HOST); - qdev_set_parent_bus(DEVICE(&s->pcie), sysbus); - object_property_add_child(obj, "pcie", OBJECT(&s->pcie), &error_fatal); + sysbus_init_child_obj(obj, "pcie", &s->pcie, sizeof(s->pcie), + TYPE_DESIGNWARE_PCIE_HOST); for (i = 0; i < FSL_IMX7_NUM_USBS; i++) { - object_initialize(&s->usb[i], - sizeof(s->usb[i]), TYPE_CHIPIDEA); - qdev_set_parent_bus(DEVICE(&s->usb[i]), sysbus); snprintf(name, NAME_SIZE, "usb%d", i); - object_property_add_child(obj, name, - OBJECT(&s->usb[i]), &error_fatal); + sysbus_init_child_obj(obj, name, &s->usb[i], sizeof(s->usb[i]), + TYPE_CHIPIDEA); } } From 51dd12ac0e0c7c3cc95e2d97311a34a3329c13f3 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:27 +0200 Subject: [PATCH 2253/2380] hw/arm/fsl-imx25: Fix introspection problem with the "fsl, imx25" device Running QEMU with valgrind indicates a problem here: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'fsl,imx25'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio [...] ==26724== Invalid read of size 8 ==26724== at 0x6190DA: qdev_print (qdev-monitor.c:686) ==26724== by 0x6190DA: qbus_print (qdev-monitor.c:719) [...] Use the new sysbus_init_child_obj() to make sure that the objects are cleaned up correctly when the parent gets destroyed. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Message-id: 1531745974-17187-11-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/arm/fsl-imx25.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 37056f9e34..bd07040a4a 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -39,38 +39,36 @@ static void fsl_imx25_init(Object *obj) object_initialize(&s->cpu, sizeof(s->cpu), "arm926-" TYPE_ARM_CPU); - object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC); - qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default()); + sysbus_init_child_obj(obj, "avic", &s->avic, sizeof(s->avic), + TYPE_IMX_AVIC); - object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM); - qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); + sysbus_init_child_obj(obj, "ccm", &s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM); for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) { - object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); - qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "uart[*]", &s->uart[i], sizeof(s->uart[i]), + TYPE_IMX_SERIAL); } for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) { - object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX25_GPT); - qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gpt[*]", &s->gpt[i], sizeof(s->gpt[i]), + TYPE_IMX25_GPT); } for (i = 0; i < FSL_IMX25_NUM_EPITS; i++) { - object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); - qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "epit[*]", &s->epit[i], sizeof(s->epit[i]), + TYPE_IMX_EPIT); } - object_initialize(&s->fec, sizeof(s->fec), TYPE_IMX_FEC); - qdev_set_parent_bus(DEVICE(&s->fec), sysbus_get_default()); + sysbus_init_child_obj(obj, "fec", &s->fec, sizeof(s->fec), TYPE_IMX_FEC); for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) { - object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); - qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "i2c[*]", &s->i2c[i], sizeof(s->i2c[i]), + TYPE_IMX_I2C); } for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) { - object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); - qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gpio[*]", &s->gpio[i], sizeof(s->gpio[i]), + TYPE_IMX_GPIO); } } From aac409c9b09fe5df11e82fc285d4e721d857f486 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:28 +0200 Subject: [PATCH 2254/2380] hw/arm/fsl-imx31: Fix introspection problem with the "fsl, imx31" device Running QEMU with valgrind indicates a problem here: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'fsl,imx31'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio [...] ==26172== Invalid read of size 8 ==26172== at 0x6191FA: qdev_print (qdev-monitor.c:686) ==26172== by 0x6191FA: qbus_print (qdev-monitor.c:719) [...] Use the new sysbus_init_child_obj() to make sure that the objects are cleaned up correctly when the parent gets destroyed. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Message-id: 1531745974-17187-12-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/arm/fsl-imx31.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 891850cf18..ec8239a967 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -36,33 +36,31 @@ static void fsl_imx31_init(Object *obj) object_initialize(&s->cpu, sizeof(s->cpu), "arm1136-" TYPE_ARM_CPU); - object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC); - qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default()); + sysbus_init_child_obj(obj, "avic", &s->avic, sizeof(s->avic), + TYPE_IMX_AVIC); - object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM); - qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); + sysbus_init_child_obj(obj, "ccm", &s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM); for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) { - object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); - qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "uart[*]", &s->uart[i], sizeof(s->uart[i]), + TYPE_IMX_SERIAL); } - object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX31_GPT); - qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); + sysbus_init_child_obj(obj, "gpt", &s->gpt, sizeof(s->gpt), TYPE_IMX31_GPT); for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) { - object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); - qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "epit[*]", &s->epit[i], sizeof(s->epit[i]), + TYPE_IMX_EPIT); } for (i = 0; i < FSL_IMX31_NUM_I2CS; i++) { - object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); - qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "i2c[*]", &s->i2c[i], sizeof(s->i2c[i]), + TYPE_IMX_I2C); } for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) { - object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); - qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gpio[*]", &s->gpio[i], sizeof(s->gpio[i]), + TYPE_IMX_GPIO); } } From b2bc349822fa27c7da4e04535c5dda2cb035965b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:29 +0200 Subject: [PATCH 2255/2380] hw/cpu/arm11mpcore: Fix introspection problem with 'arm11mpcore_priv' Valgrind reports an error here: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'arm11mpcore_priv'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio [...] ==3145== Invalid read of size 8 ==3145== at 0x61873A: qdev_print (qdev-monitor.c:686) ==3145== by 0x61873A: qbus_print (qdev-monitor.c:719) [...] Use sysbus_init_child_obj() to fix it. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Reviewed-by: Alistair Francis Message-id: 1531745974-17187-13-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/cpu/arm11mpcore.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/hw/cpu/arm11mpcore.c b/hw/cpu/arm11mpcore.c index eb244658b9..8aead3794e 100644 --- a/hw/cpu/arm11mpcore.c +++ b/hw/cpu/arm11mpcore.c @@ -121,19 +121,17 @@ static void mpcore_priv_initfn(Object *obj) "mpcore-priv-container", 0x2000); sysbus_init_mmio(sbd, &s->container); - object_initialize(&s->scu, sizeof(s->scu), TYPE_ARM11_SCU); - qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); + sysbus_init_child_obj(obj, "scu", &s->scu, sizeof(s->scu), TYPE_ARM11_SCU); - object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); - qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), TYPE_ARM_GIC); /* Request the legacy 11MPCore GIC behaviour: */ qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 0); - object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default()); + sysbus_init_child_obj(obj, "mptimer", &s->mptimer, sizeof(s->mptimer), + TYPE_ARM_MPTIMER); - object_initialize(&s->wdtimer, sizeof(s->wdtimer), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->wdtimer), sysbus_get_default()); + sysbus_init_child_obj(obj, "wdtimer", &s->wdtimer, sizeof(s->wdtimer), + TYPE_ARM_MPTIMER); } static Property mpcore_priv_properties[] = { From 32db1b58caa72f2ae582560f1937b21ea9ee0646 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:30 +0200 Subject: [PATCH 2256/2380] hw/*/realview: Fix introspection problem with 'realview_mpcore' & 'realview_gic' echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'realview_mpcore'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio [...] ==2654== Invalid read of size 8 ==2654== at 0x61878A: qdev_print (qdev-monitor.c:686) ==2654== by 0x61878A: qbus_print (qdev-monitor.c:719) ==2654== by 0x452B38: handle_hmp_command (monitor.c:3446) ==2654== by 0x452D70: qmp_human_monitor_command (monitor.c:821) [...] Use sysbus_init_child_obj() to fix it. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Message-id: 1531745974-17187-14-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/cpu/realview_mpcore.c | 8 ++++---- hw/intc/realview_gic.c | 7 ++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/hw/cpu/realview_mpcore.c b/hw/cpu/realview_mpcore.c index 39d4ebeb1d..9d3f8378fb 100644 --- a/hw/cpu/realview_mpcore.c +++ b/hw/cpu/realview_mpcore.c @@ -101,14 +101,14 @@ static void mpcore_rirq_init(Object *obj) SysBusDevice *privbusdev; int i; - object_initialize(&s->priv, sizeof(s->priv), TYPE_ARM11MPCORE_PRIV); - qdev_set_parent_bus(DEVICE(&s->priv), sysbus_get_default()); + sysbus_init_child_obj(obj, "a11priv", &s->priv, sizeof(s->priv), + TYPE_ARM11MPCORE_PRIV); privbusdev = SYS_BUS_DEVICE(&s->priv); sysbus_init_mmio(sbd, sysbus_mmio_get_region(privbusdev, 0)); for (i = 0; i < 4; i++) { - object_initialize(&s->gic[i], sizeof(s->gic[i]), TYPE_REALVIEW_GIC); - qdev_set_parent_bus(DEVICE(&s->gic[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gic[*]", &s->gic[i], sizeof(s->gic[i]), + TYPE_REALVIEW_GIC); } } diff --git a/hw/intc/realview_gic.c b/hw/intc/realview_gic.c index 50bbab66ee..7f2ff85c83 100644 --- a/hw/intc/realview_gic.c +++ b/hw/intc/realview_gic.c @@ -54,16 +54,13 @@ static void realview_gic_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); RealViewGICState *s = REALVIEW_GIC(obj); - DeviceState *gicdev; memory_region_init(&s->container, OBJECT(s), "realview-gic-container", 0x2000); sysbus_init_mmio(sbd, &s->container); - object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); - gicdev = DEVICE(&s->gic); - qdev_set_parent_bus(gicdev, sysbus_get_default()); - qdev_prop_set_uint32(gicdev, "num-cpu", 1); + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), TYPE_ARM_GIC); + qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", 1); } static void realview_gic_class_init(ObjectClass *oc, void *data) From cf3fccfa8c3ade3da058b3eac09fc1bbaa1ce648 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:31 +0200 Subject: [PATCH 2257/2380] hw/arm/allwinner-a10: Fix introspection problem with 'allwinner-a10' Valgrind complains: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'allwinner-a10'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio [...] ==32519== Invalid read of size 8 ==32519== at 0x61869A: qdev_print (qdev-monitor.c:686) ==32519== by 0x61869A: qbus_print (qdev-monitor.c:719) ==32519== by 0x452B38: handle_hmp_command (monitor.c:3446) [...] Use object_initialize_child() and sysbus_init_child_obj() to fix the issue. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Message-id: 1531745974-17187-15-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/arm/allwinner-a10.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index c5fbc654f2..9fe875cdb5 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -27,20 +27,19 @@ static void aw_a10_init(Object *obj) { AwA10State *s = AW_A10(obj); - object_initialize(&s->cpu, sizeof(s->cpu), "cortex-a8-" TYPE_ARM_CPU); - object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); + object_initialize_child(obj, "cpu", &s->cpu, sizeof(s->cpu), + "cortex-a8-" TYPE_ARM_CPU, &error_abort, NULL); - object_initialize(&s->intc, sizeof(s->intc), TYPE_AW_A10_PIC); - qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default()); + sysbus_init_child_obj(obj, "intc", &s->intc, sizeof(s->intc), + TYPE_AW_A10_PIC); - object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT); - qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); + sysbus_init_child_obj(obj, "timer", &s->timer, sizeof(s->timer), + TYPE_AW_A10_PIT); - object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC); - qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default()); + sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac), TYPE_AW_EMAC); - object_initialize(&s->sata, sizeof(s->sata), TYPE_ALLWINNER_AHCI); - qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default()); + sysbus_init_child_obj(obj, "sata", &s->sata, sizeof(s->sata), + TYPE_ALLWINNER_AHCI); } static void aw_a10_realize(DeviceState *dev, Error **errp) From a39ae81637f19086f7358555e7cc0becea301113 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:32 +0200 Subject: [PATCH 2258/2380] hw/arm/stm32f205_soc: Fix introspection problem with 'stm32f205-soc' device Valgrind complains: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'stm32f205-soc'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio [...] ==28531== Invalid read of size 8 ==28531== at 0x6185BA: qdev_print (qdev-monitor.c:686) ==28531== by 0x6185BA: qbus_print (qdev-monitor.c:719) ==28531== by 0x452B38: handle_hmp_command (monitor.c:3446) [...] Fix it with the new sysbus_init_child_obj() function. Signed-off-by: Thomas Huth Reviewed-by: Peter Maydell Reviewed-by: Eduardo Habkost Reviewed-by: Alistair Francis Message-id: 1531745974-17187-16-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/arm/stm32f205_soc.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 2b2135d382..c486d06a8b 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -49,36 +49,32 @@ static void stm32f205_soc_initfn(Object *obj) STM32F205State *s = STM32F205_SOC(obj); int i; - object_initialize(&s->armv7m, sizeof(s->armv7m), TYPE_ARMV7M); - qdev_set_parent_bus(DEVICE(&s->armv7m), sysbus_get_default()); + sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), + TYPE_ARMV7M); - object_initialize(&s->syscfg, sizeof(s->syscfg), TYPE_STM32F2XX_SYSCFG); - qdev_set_parent_bus(DEVICE(&s->syscfg), sysbus_get_default()); + sysbus_init_child_obj(obj, "syscfg", &s->syscfg, sizeof(s->syscfg), + TYPE_STM32F2XX_SYSCFG); for (i = 0; i < STM_NUM_USARTS; i++) { - object_initialize(&s->usart[i], sizeof(s->usart[i]), - TYPE_STM32F2XX_USART); - qdev_set_parent_bus(DEVICE(&s->usart[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "usart[*]", &s->usart[i], + sizeof(s->usart[i]), TYPE_STM32F2XX_USART); } for (i = 0; i < STM_NUM_TIMERS; i++) { - object_initialize(&s->timer[i], sizeof(s->timer[i]), - TYPE_STM32F2XX_TIMER); - qdev_set_parent_bus(DEVICE(&s->timer[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "timer[*]", &s->timer[i], + sizeof(s->timer[i]), TYPE_STM32F2XX_TIMER); } s->adc_irqs = OR_IRQ(object_new(TYPE_OR_IRQ)); for (i = 0; i < STM_NUM_ADCS; i++) { - object_initialize(&s->adc[i], sizeof(s->adc[i]), - TYPE_STM32F2XX_ADC); - qdev_set_parent_bus(DEVICE(&s->adc[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "adc[*]", &s->adc[i], sizeof(s->adc[i]), + TYPE_STM32F2XX_ADC); } for (i = 0; i < STM_NUM_SPIS; i++) { - object_initialize(&s->spi[i], sizeof(s->spi[i]), - TYPE_STM32F2XX_SPI); - qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "spi[*]", &s->spi[i], sizeof(s->spi[i]), + TYPE_STM32F2XX_SPI); } } From fe04f0b4a1f4bc3d7924c914e0c6ef5222473ed0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 16 Jul 2018 14:59:33 +0200 Subject: [PATCH 2259/2380] hw/display/xlnx_dp: Move problematic code from instance_init to realize aux_create_slave() calls qdev_init_nofail() which in turn "realizes" the corresponding object. This is unlike qdev_create(), and it is wrong because qdev_init_nofail() must not be called from an instance_init function. Move qdev_init_nofail() and the subsequent aux_map_slave into the caller's realize function. There are two more bugs that needs to be fixed here, too, where the objects are created but not added as children. Therefore when you call object_unparent on them, nothing happens. In particular dpcd and edid give you an infinite loop in bus_unparent, because device_unparent is not called and does not remove them from the list of devices on the bus. Reported-by: Thomas Huth Signed-off-by: Paolo Bonzini Reviewed-by: Peter Maydell Reviewed-by: Alistair Francis Signed-off-by: Thomas Huth Message-id: 1531745974-17187-17-git-send-email-thuth@redhat.com [thuth: Added Paolo's fixup for the dpcd and edid unparenting] Signed-off-by: Thomas Huth Signed-off-by: Peter Maydell --- hw/display/xlnx_dp.c | 8 +++++++- hw/misc/auxbus.c | 18 ++++++++++++------ include/hw/misc/auxbus.h | 14 +++++++++++++- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 51301220e8..6439bd05ef 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1234,9 +1234,12 @@ static void xlnx_dp_init(Object *obj) /* * Initialize DPCD and EDID.. */ - s->dpcd = DPCD(aux_create_slave(s->aux_bus, "dpcd", 0x00000)); + s->dpcd = DPCD(aux_create_slave(s->aux_bus, "dpcd")); + object_property_add_child(OBJECT(s), "dpcd", OBJECT(s->dpcd), NULL); + s->edid = I2CDDC(qdev_create(BUS(aux_get_i2c_bus(s->aux_bus)), "i2c-ddc")); i2c_set_slave_address(I2C_SLAVE(s->edid), 0x50); + object_property_add_child(OBJECT(s), "edid", OBJECT(s->edid), NULL); fifo8_create(&s->rx_fifo, 16); fifo8_create(&s->tx_fifo, 16); @@ -1248,6 +1251,9 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp) DisplaySurface *surface; struct audsettings as; + qdev_init_nofail(DEVICE(s->dpcd)); + aux_map_slave(AUX_SLAVE(s->dpcd), 0x0000); + s->console = graphic_console_init(dev, 0, &xlnx_dp_gfx_ops, s); surface = qemu_console_surface(s->console); xlnx_dpdma_set_host_data_location(s->dpdma, DP_GRAPHIC_DMA_CHANNEL, diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index b8a8721201..0e56d9a8a4 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -32,6 +32,7 @@ #include "hw/misc/auxbus.h" #include "hw/i2c/i2c.h" #include "monitor/monitor.h" +#include "qapi/error.h" #ifndef DEBUG_AUX #define DEBUG_AUX 0 @@ -63,9 +64,14 @@ static void aux_bus_class_init(ObjectClass *klass, void *data) AUXBus *aux_init_bus(DeviceState *parent, const char *name) { AUXBus *bus; + Object *auxtoi2c; bus = AUX_BUS(qbus_create(TYPE_AUX_BUS, parent, name)); - bus->bridge = AUXTOI2C(qdev_create(BUS(bus), TYPE_AUXTOI2C)); + auxtoi2c = object_new_with_props(TYPE_AUXTOI2C, OBJECT(bus), "i2c", + &error_abort, NULL); + qdev_set_parent_bus(DEVICE(auxtoi2c), BUS(bus)); + + bus->bridge = AUXTOI2C(auxtoi2c); /* Memory related. */ bus->aux_io = g_malloc(sizeof(*bus->aux_io)); @@ -74,9 +80,11 @@ AUXBus *aux_init_bus(DeviceState *parent, const char *name) return bus; } -static void aux_bus_map_device(AUXBus *bus, AUXSlave *dev, hwaddr addr) +void aux_map_slave(AUXSlave *aux_dev, hwaddr addr) { - memory_region_add_subregion(bus->aux_io, addr, dev->mmio); + DeviceState *dev = DEVICE(aux_dev); + AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev)); + memory_region_add_subregion(bus->aux_io, addr, aux_dev->mmio); } static bool aux_bus_is_bridge(AUXBus *bus, DeviceState *dev) @@ -260,15 +268,13 @@ static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent) memory_region_size(s->mmio)); } -DeviceState *aux_create_slave(AUXBus *bus, const char *type, uint32_t addr) +DeviceState *aux_create_slave(AUXBus *bus, const char *type) { DeviceState *dev; dev = DEVICE(object_new(type)); assert(dev); qdev_set_parent_bus(dev, &bus->qbus); - qdev_init_nofail(dev); - aux_bus_map_device(AUX_BUS(qdev_get_parent_bus(dev)), AUX_SLAVE(dev), addr); return dev; } diff --git a/include/hw/misc/auxbus.h b/include/hw/misc/auxbus.h index 68ade8a90f..c15b444748 100644 --- a/include/hw/misc/auxbus.h +++ b/include/hw/misc/auxbus.h @@ -123,6 +123,18 @@ I2CBus *aux_get_i2c_bus(AUXBus *bus); */ void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio); -DeviceState *aux_create_slave(AUXBus *bus, const char *name, uint32_t addr); +/* aux_create_slave: Create a new device on an AUX bus + * + * @bus The AUX bus for the new device. + * @name The type of the device to be created. + */ +DeviceState *aux_create_slave(AUXBus *bus, const char *name); + +/* aux_map_slave: Map the mmio for an AUX slave on the bus. + * + * @dev The AUX slave. + * @addr The address for the slave's mmio. + */ +void aux_map_slave(AUXSlave *dev, hwaddr addr); #endif /* HW_MISC_AUXBUS_H */ From ccf02d73d18930a15282556e577c0777fa09081b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Jul 2018 14:59:34 +0200 Subject: [PATCH 2260/2380] hw/arm/xlnx-zynqmp: Fix crash when introspecting the "xlnx, zynqmp" device QEMU currently crashes when e.g. doing something like this: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'xlnx,zynqmp'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" \ | aarch64-softmmu/qemu-system-aarch64 -M none,accel=qtest -qmp stdio Use the new object_initialize_child() and sysbus_init_child_obj() functions to get the refernce counting of the child objects right, so that they are properly cleaned up when the parent gets destroyed. Reviewed-by: Richard Henderson Reviewed-by: Paolo Bonzini Reviewed-by: Eduardo Habkost Signed-off-by: Thomas Huth Reviewed-by: Alistair Francis Message-id: 1531745974-17187-18-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zynqmp.c | 61 ++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 29df35fb75..8de4868eb9 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -166,64 +166,59 @@ static void xlnx_zynqmp_init(Object *obj) int num_apus = MIN(smp_cpus, XLNX_ZYNQMP_NUM_APU_CPUS); for (i = 0; i < num_apus; i++) { - object_initialize(&s->apu_cpu[i], sizeof(s->apu_cpu[i]), - "cortex-a53-" TYPE_ARM_CPU); - object_property_add_child(obj, "apu-cpu[*]", OBJECT(&s->apu_cpu[i]), - &error_abort); + object_initialize_child(obj, "apu-cpu[*]", &s->apu_cpu[i], + sizeof(s->apu_cpu[i]), + "cortex-a53-" TYPE_ARM_CPU, &error_abort, NULL); } - object_initialize(&s->gic, sizeof(s->gic), gic_class_name()); - qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), + gic_class_name()); for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { - object_initialize(&s->gem[i], sizeof(s->gem[i]), TYPE_CADENCE_GEM); - qdev_set_parent_bus(DEVICE(&s->gem[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gem[*]", &s->gem[i], sizeof(s->gem[i]), + TYPE_CADENCE_GEM); } for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { - object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_CADENCE_UART); - qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "uart[*]", &s->uart[i], sizeof(s->uart[i]), + TYPE_CADENCE_UART); } - object_initialize(&s->sata, sizeof(s->sata), TYPE_SYSBUS_AHCI); - qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default()); + sysbus_init_child_obj(obj, "sata", &s->sata, sizeof(s->sata), + TYPE_SYSBUS_AHCI); for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { - object_initialize(&s->sdhci[i], sizeof(s->sdhci[i]), - TYPE_SYSBUS_SDHCI); - qdev_set_parent_bus(DEVICE(&s->sdhci[i]), - sysbus_get_default()); + sysbus_init_child_obj(obj, "sdhci[*]", &s->sdhci[i], + sizeof(s->sdhci[i]), TYPE_SYSBUS_SDHCI); } for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) { - object_initialize(&s->spi[i], sizeof(s->spi[i]), - TYPE_XILINX_SPIPS); - qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "spi[*]", &s->spi[i], sizeof(s->spi[i]), + TYPE_XILINX_SPIPS); } - object_initialize(&s->qspi, sizeof(s->qspi), TYPE_XLNX_ZYNQMP_QSPIPS); - qdev_set_parent_bus(DEVICE(&s->qspi), sysbus_get_default()); + sysbus_init_child_obj(obj, "qspi", &s->qspi, sizeof(s->qspi), + TYPE_XLNX_ZYNQMP_QSPIPS); - object_initialize(&s->dp, sizeof(s->dp), TYPE_XLNX_DP); - qdev_set_parent_bus(DEVICE(&s->dp), sysbus_get_default()); + sysbus_init_child_obj(obj, "xxxdp", &s->dp, sizeof(s->dp), TYPE_XLNX_DP); - object_initialize(&s->dpdma, sizeof(s->dpdma), TYPE_XLNX_DPDMA); - qdev_set_parent_bus(DEVICE(&s->dpdma), sysbus_get_default()); + sysbus_init_child_obj(obj, "dp-dma", &s->dpdma, sizeof(s->dpdma), + TYPE_XLNX_DPDMA); - object_initialize(&s->ipi, sizeof(s->ipi), TYPE_XLNX_ZYNQMP_IPI); - qdev_set_parent_bus(DEVICE(&s->ipi), sysbus_get_default()); + sysbus_init_child_obj(obj, "ipi", &s->ipi, sizeof(s->ipi), + TYPE_XLNX_ZYNQMP_IPI); - object_initialize(&s->rtc, sizeof(s->rtc), TYPE_XLNX_ZYNQMP_RTC); - qdev_set_parent_bus(DEVICE(&s->rtc), sysbus_get_default()); + sysbus_init_child_obj(obj, "rtc", &s->rtc, sizeof(s->rtc), + TYPE_XLNX_ZYNQMP_RTC); for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { - object_initialize(&s->gdma[i], sizeof(s->gdma[i]), TYPE_XLNX_ZDMA); - qdev_set_parent_bus(DEVICE(&s->gdma[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gdma[*]", &s->gdma[i], sizeof(s->gdma[i]), + TYPE_XLNX_ZDMA); } for (i = 0; i < XLNX_ZYNQMP_NUM_ADMA_CH; i++) { - object_initialize(&s->adma[i], sizeof(s->adma[i]), TYPE_XLNX_ZDMA); - qdev_set_parent_bus(DEVICE(&s->adma[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "adma[*]", &s->adma[i], sizeof(s->adma[i]), + TYPE_XLNX_ZDMA); } } From 6e3ad3f0e31b8e31c6c0769d0f474bcd9673e0e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 14 May 2018 18:19:11 +0100 Subject: [PATCH 2261/2380] i386: fix regression parsing multiboot initrd modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The logic for parsing the multiboot initrd modules was messed up in commit 950c4e6c94b15cd0d8b63891dddd7a8dbf458e6a Author: Daniel P. Berrangé Date: Mon Apr 16 12:17:43 2018 +0100 opts: don't silently truncate long option values Causing the length to be undercounter, and the number of modules over counted. It also passes NULL to get_opt_value() which was not robust at accepting a NULL value. Signed-off-by: Daniel P. Berrangé Message-Id: <20180514171913.17664-2-berrange@redhat.com> Reviewed-by: Eduardo Habkost Tested-by: Roman Kagan Signed-off-by: Paolo Bonzini --- hw/i386/multiboot.c | 3 +-- util/qemu-option.c | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 7a2953e26f..8e26545814 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -292,8 +292,7 @@ int load_multiboot(FWCfgState *fw_cfg, cmdline_len += strlen(kernel_cmdline) + 1; if (initrd_filename) { const char *r = get_opt_value(initrd_filename, NULL); - cmdline_len += strlen(r) + 1; - mbs.mb_mods_avail = 1; + cmdline_len += strlen(initrd_filename) + 1; while (1) { mbs.mb_mods_avail++; r = get_opt_value(r, NULL); diff --git a/util/qemu-option.c b/util/qemu-option.c index 19761e3eaf..834217fc75 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -75,7 +75,9 @@ const char *get_opt_value(const char *p, char **value) size_t capacity = 0, length; const char *offset; - *value = NULL; + if (value) { + *value = NULL; + } while (1) { offset = qemu_strchrnul(p, ','); length = offset - p; From f8da93a0ffa09268815c1942732cbc616a7db847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 14 May 2018 18:19:12 +0100 Subject: [PATCH 2262/2380] i386: only parse the initrd_filename once for multiboot modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The multiboot code parses the initrd_filename twice, first to count how many entries there are, and second to process each entry. This changes the first loop to store the parse module names in a list, and the second loop can now use these names. This avoids having to pass NULL to the get_opt_value() method which means it can safely assume a non-NULL param. Signed-off-by: Daniel P. Berrangé Message-Id: <20180514171913.17664-3-berrange@redhat.com> Reviewed-by: Eduardo Habkost Tested-by: Roman Kagan Signed-off-by: Paolo Bonzini --- hw/i386/multiboot.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 8e26545814..d519e206c5 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -161,6 +161,7 @@ int load_multiboot(FWCfgState *fw_cfg, uint8_t bootinfo[MBI_SIZE]; uint8_t *mb_bootinfo_data; uint32_t cmdline_len; + GList *mods = NULL; /* Ok, let's see if it is a multiboot image. The header is 12x32bit long, so the latest entry may be 8192 - 48. */ @@ -291,15 +292,16 @@ int load_multiboot(FWCfgState *fw_cfg, cmdline_len = strlen(kernel_filename) + 1; cmdline_len += strlen(kernel_cmdline) + 1; if (initrd_filename) { - const char *r = get_opt_value(initrd_filename, NULL); + const char *r = initrd_filename; cmdline_len += strlen(initrd_filename) + 1; - while (1) { + while (*r) { + char *value; + r = get_opt_value(r, &value); mbs.mb_mods_avail++; - r = get_opt_value(r, NULL); - if (!*r) { - break; + mods = g_list_append(mods, value); + if (*r) { + r++; } - r++; } } @@ -314,20 +316,16 @@ int load_multiboot(FWCfgState *fw_cfg, mbs.offset_cmdlines = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE; mbs.offset_bootloader = mbs.offset_cmdlines + cmdline_len; - if (initrd_filename) { - const char *next_initrd; - char not_last; - char *one_file = NULL; - + if (mods) { + GList *tmpl = mods; mbs.offset_mods = mbs.mb_buf_size; - do { + while (tmpl) { char *next_space; int mb_mod_length; uint32_t offs = mbs.mb_buf_size; + char *one_file = tmpl->data; - next_initrd = get_opt_value(initrd_filename, &one_file); - not_last = *next_initrd; /* if a space comes after the module filename, treat everything after that as parameters */ hwaddr c = mb_add_cmdline(&mbs, one_file); @@ -352,10 +350,10 @@ int load_multiboot(FWCfgState *fw_cfg, mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "TARGET_FMT_plx, (char *)mbs.mb_buf + offs, (char *)mbs.mb_buf + offs + mb_mod_length, c); - initrd_filename = next_initrd+1; g_free(one_file); - one_file = NULL; - } while (not_last); + tmpl = tmpl->next; + } + g_list_free(mods); } /* Commandline support */ From 0c2f6e7ee99517449b4ed6cf333c2d9456d8fe35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 14 May 2018 18:19:13 +0100 Subject: [PATCH 2263/2380] opts: remove redundant check for NULL parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No callers of get_opt_value() pass in a NULL for the "value" parameter, so the check is redundant. Signed-off-by: Daniel P. Berrangé Message-Id: <20180514171913.17664-4-berrange@redhat.com> Reviewed-by: Eduardo Habkost Tested-by: Roman Kagan Signed-off-by: Paolo Bonzini --- util/qemu-option.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/util/qemu-option.c b/util/qemu-option.c index 834217fc75..01886efe90 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -75,20 +75,16 @@ const char *get_opt_value(const char *p, char **value) size_t capacity = 0, length; const char *offset; - if (value) { - *value = NULL; - } + *value = NULL; while (1) { offset = qemu_strchrnul(p, ','); length = offset - p; if (*offset != '\0' && *(offset + 1) == ',') { length++; } - if (value) { - *value = g_renew(char, *value, capacity + length + 1); - strncpy(*value + capacity, p, length); - (*value)[capacity + length] = '\0'; - } + *value = g_renew(char, *value, capacity + length + 1); + strncpy(*value + capacity, p, length); + (*value)[capacity + length] = '\0'; capacity += length; if (*offset == '\0' || *(offset + 1) != ',') { From dfaa7d50b0f72060764096ffcae4a0c06ce24f9b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 16 Jul 2018 21:12:08 +0200 Subject: [PATCH 2264/2380] Document command line options with single dash QEMU options have a single dash (but also work as double dash for convenience and compatibility). Most options are listed with single dash in command line help but some were listed with two dashes. Normalize these to have the same format as the others. Left --preconfig as that is mentioned as double dash everywhere so I assume that is the preferred form for that. Signed-off-by: BALATON Zoltan Acked-by: Thomas Huth Message-Id: <20180716193312.A5BA17456B9@zero.eik.bme.hu> Signed-off-by: Paolo Bonzini --- qemu-options.hx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index 654e69cc3b..1a186fb367 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -471,7 +471,7 @@ STEXI @item -balloon virtio[,addr=@var{addr}] @findex -balloon Enable virtio balloon device, optionally with PCI address @var{addr}. This -option is deprecated, use @option{--device virtio-balloon} instead. +option is deprecated, use @option{-device virtio-balloon} instead. ETEXI DEF("device", HAS_ARG, QEMU_OPTION_device, @@ -2005,7 +2005,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, "-netdev hubport,id=str,hubid=n[,netdev=nd]\n" " configure a hub port on the hub with ID 'n'\n", QEMU_ARCH_ALL) DEF("nic", HAS_ARG, QEMU_OPTION_nic, - "--nic [tap|bridge|" + "-nic [tap|bridge|" #ifdef CONFIG_SLIRP "user|" #endif @@ -2024,7 +2024,7 @@ DEF("nic", HAS_ARG, QEMU_OPTION_nic, "socket][,option][,...][mac=macaddr]\n" " initialize an on-board / default host NIC (using MAC address\n" " macaddr) and connect it to the given host network backend\n" - "--nic none use it alone to have zero network devices (the default is to\n" + "-nic none use it alone to have zero network devices (the default is to\n" " provided a 'user' network connection)\n", QEMU_ARCH_ALL) DEF("net", HAS_ARG, QEMU_OPTION_net, @@ -3338,7 +3338,7 @@ mlocking qemu and guest memory can be enabled via @option{mlock=on} ETEXI DEF("overcommit", HAS_ARG, QEMU_OPTION_overcommit, - "--overcommit [mem-lock=on|off][cpu-pm=on|off]\n" + "-overcommit [mem-lock=on|off][cpu-pm=on|off]\n" " run qemu with overcommit hints\n" " mem-lock=on|off controls memory lock support (default: off)\n" " cpu-pm=on|off controls cpu power management (default: off)\n", From ea6abffa8a08d832feb759d359d5b935e3087cf7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Jul 2018 18:15:19 +0100 Subject: [PATCH 2265/2380] Update version for v3.0.0-rc1 release Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 13565e69b4..65be8fd280 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.90 +2.12.91 From 677ff32db12bcd1bca3a3df733d2478896d6df96 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 18 Jul 2018 11:23:30 +0200 Subject: [PATCH 2266/2380] s390x/cpumodel: fix segmentation fault when baselining models Usually, when baselining two CPU models, whereby one of them has base CPU features disabled (e.g. z14-base,msa=off), we fallback to an older model that did not have these features in the base model. We always try to create a "sane" CPU model (as far as possible), and one part of it is that removing base features is no good and to be avoided. Now, if we disable base features that were part of a z900, we're out of luck. We won't find a CPU model and QEMU will segfault. This is a scenario that should never happen in real life, but it can be used to crash QEMU. So let's properly report an error if we baseline e.g.: { "execute": "query-cpu-model-baseline", "arguments" : { "modela": { "name": "z14-base", "props": {"esan3" : false}}, "modelb": { "name": "z14"}} } Instead of segfaulting. Signed-off-by: David Hildenbrand Message-Id: <20180718092330.19465-1-david@redhat.com> Acked-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- target/s390x/cpu_models.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index cfdbccf46d..604898a882 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -716,6 +716,14 @@ CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *infoa, model.def = s390_find_cpu_def(cpu_type, max_gen, max_gen_ga, model.features); + + /* models without early base features (esan3) are bad */ + if (!model.def) { + error_setg(errp, "No compatible CPU model could be created as" + " important base features are disabled"); + return NULL; + } + /* strip off features not part of the max model */ bitmap_and(model.features, model.features, model.def->full_feat, S390_FEAT_MAX); From 6fccbb475bc6effc313ee9481726a1748b6dae57 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 4 Jul 2018 15:54:10 +0100 Subject: [PATCH 2267/2380] throttle-groups: fix hang when group member leaves Throttle groups consist of members sharing one throttling state (including bps/iops limits). Round-robin scheduling is used to ensure fairness. If a group member already has a timer pending then other groups members do not schedule their own timers. The next group member will have its turn when the existing timer expires. A hang may occur when a group member leaves while it had a timer scheduled. Although the code carefully removes the group member from the round-robin list, it does not schedule the next member. Therefore remaining members continue to wait for the removed member's timer to expire. This patch schedules the next request if a timer is pending. Unfortunately the actual bug is a race condition that I've been unable to capture in a test case. Sometimes drive2 hangs when drive1 is removed from the throttling group: $ qemu ... -drive if=none,id=drive1,cache=none,format=qcow2,file=data1.qcow2,iops=100,group=foo \ -device virtio-blk-pci,id=virtio-blk-pci0,drive=drive1 \ -drive if=none,id=drive2,cache=none,format=qcow2,file=data2.qcow2,iops=10,group=foo \ -device virtio-blk-pci,id=virtio-blk-pci1,drive=drive2 (guest-console1)# fio -filename /dev/vda 4k-seq-read.job (guest-console2)# fio -filename /dev/vdb 4k-seq-read.job (qmp) {"execute": "block_set_io_throttle", "arguments": {"device": "drive1","bps": 0,"bps_rd": 0,"bps_wr": 0,"iops": 0,"iops_rd": 0,"iops_wr": 0}} Reported-by: Nini Gu Signed-off-by: Stefan Hajnoczi Message-id: 20180704145410.794-1-stefanha@redhat.com RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1535914 Cc: Alberto Garcia Signed-off-by: Stefan Hajnoczi --- block/throttle-groups.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 36cc0430c3..e297b04e17 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -564,6 +564,10 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) qemu_mutex_lock(&tg->lock); for (i = 0; i < 2; i++) { + if (timer_pending(tgm->throttle_timers.timers[i])) { + tg->any_timer_armed[i] = false; + schedule_next_request(tgm, i); + } if (tg->tokens[i] == tgm) { token = throttle_group_next_tgm(tgm); /* Take care of the case where this is the last tgm in the group */ From db817b8c500a60873eba80cbf047900ae5b32766 Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Wed, 4 Jul 2018 11:17:27 +0800 Subject: [PATCH 2268/2380] tracing: Use double-dash spelling for trace option The '-trace' and '--trace' spellings are only both supported in qemu binary, while for qemu-nbd or qemu-img only '--trace' spelling is supported. So for the consistency of trace option invocation, we should use double-dash spelling in our documentation. This's also mentioned in https://wiki.qemu.org/BiteSizedTasks#Consistent_option_usage_in_documentation . Signed-off-by: Yaowei Bai Reviewed-by: Eric Blake Message-id: 1530674247-31200-1-git-send-email-baiyaowei@cmss.chinamobile.com Signed-off-by: Stefan Hajnoczi --- docs/devel/tracing.txt | 6 +++--- trace/control.c | 4 ++-- trace/control.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.txt index 6f815ecbd7..bc52f12485 100644 --- a/docs/devel/tracing.txt +++ b/docs/devel/tracing.txt @@ -18,7 +18,7 @@ for debugging, profiling, and observing execution. 3. Run the virtual machine to produce a trace file: - qemu -trace events=/tmp/events ... # your normal QEMU invocation + qemu --trace events=/tmp/events ... # your normal QEMU invocation 4. Pretty-print the binary trace file: @@ -157,11 +157,11 @@ The state of events can also be queried and modified through monitor commands: * trace-event NAME on|off Enable/disable a given trace event or a group of events (using wildcards). -The "-trace events=" command line argument can be used to enable the +The "--trace events=" command line argument can be used to enable the events listed in from the very beginning of the program. This file must contain one event name per line. -If a line in the "-trace events=" file begins with a '-', the trace event +If a line in the "--trace events=" file begins with a '-', the trace event will be disabled instead of enabled. This is useful when a wildcard was used to enable an entire family of events but one noisy event needs to be disabled. diff --git a/trace/control.c b/trace/control.c index e40cfca775..43fb7868db 100644 --- a/trace/control.c +++ b/trace/control.c @@ -253,7 +253,7 @@ void trace_init_file(const char *file) #ifdef CONFIG_TRACE_SIMPLE st_set_trace_file(file); #elif defined CONFIG_TRACE_LOG - /* If both the simple and the log backends are enabled, "-trace file" + /* If both the simple and the log backends are enabled, "--trace file" * only applies to the simple backend; use "-D" for the log backend. */ if (file) { @@ -261,7 +261,7 @@ void trace_init_file(const char *file) } #else if (file) { - fprintf(stderr, "error: -trace file=...: " + fprintf(stderr, "error: --trace file=...: " "option not supported by the selected tracing backends\n"); exit(1); } diff --git a/trace/control.h b/trace/control.h index eb65c8e320..0716f90f45 100644 --- a/trace/control.h +++ b/trace/control.h @@ -193,7 +193,7 @@ void trace_event_set_vcpu_state_dynamic(CPUState *vcpu, /** * trace_init_backends: * @file: Name of trace output file; may be NULL. - * Corresponds to commandline option "-trace file=...". + * Corresponds to commandline option "--trace file=...". * * Initialize the tracing backend. * @@ -204,7 +204,7 @@ bool trace_init_backends(void); /** * trace_init_file: * @file: Name of trace output file; may be NULL. - * Corresponds to commandline option "-trace file=...". + * Corresponds to commandline option "--trace file=...". * * Record the name of the output file for the tracing backend. * Exits if no selected backend does not support specifying the From 54f3141a589caf872f148fdb12c59ad79083f268 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 16 Jul 2018 15:29:01 -0700 Subject: [PATCH 2269/2380] sifive_e: Fix crash when introspecting the device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new object_initialize_child() and sysbus_init_child_obj() to fix the issue. Signed-off-by: Alistair Francis Suggested-by: Thomas Huth Reviewed-by: Michael Clark Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/riscv/sifive_e.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 8a8dbe1c00..4577d72037 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -105,9 +105,9 @@ static void riscv_sifive_e_init(MachineState *machine) int i; /* Initialize SoC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_E_SOC); - object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), - &error_abort); + object_initialize_child(OBJECT(machine), "soc", &s->soc, + sizeof(s->soc), TYPE_RISCV_E_SOC, + &error_abort, NULL); object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort); @@ -139,9 +139,9 @@ static void riscv_sifive_e_soc_init(Object *obj) { SiFiveESoCState *s = RISCV_E_SOC(obj); - object_initialize(&s->cpus, sizeof(s->cpus), TYPE_RISCV_HART_ARRAY); - object_property_add_child(obj, "cpus", OBJECT(&s->cpus), - &error_abort); + object_initialize_child(obj, "cpus", &s->cpus, + sizeof(s->cpus), TYPE_RISCV_HART_ARRAY, + &error_abort, NULL); object_property_set_str(OBJECT(&s->cpus), SIFIVE_E_CPU, "cpu-type", &error_abort); object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", From 4eea9d7deb2332c9010ddea9dcd8680adad1bfed Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 16 Jul 2018 15:30:32 -0700 Subject: [PATCH 2270/2380] sifive_u: Fix crash when introspecting the device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new object_initialize_child() and sysbus_init_child_obj() to fix the issue. Signed-off-by: Alistair Francis Suggested-by: Thomas Huth Reviewed-by: Michael Clark Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/riscv/sifive_u.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 3a6ffeb437..59ae1ce24a 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -244,9 +244,9 @@ static void riscv_sifive_u_init(MachineState *machine) int i; /* Initialize SoC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_U_SOC); - object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), - &error_abort); + object_initialize_child(OBJECT(machine), "soc", &s->soc, + sizeof(s->soc), TYPE_RISCV_U_SOC, + &error_abort, NULL); object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort); @@ -303,16 +303,15 @@ static void riscv_sifive_u_soc_init(Object *obj) { SiFiveUSoCState *s = RISCV_U_SOC(obj); - object_initialize(&s->cpus, sizeof(s->cpus), TYPE_RISCV_HART_ARRAY); - object_property_add_child(obj, "cpus", OBJECT(&s->cpus), - &error_abort); + object_initialize_child(obj, "cpus", &s->cpus, sizeof(s->cpus), + TYPE_RISCV_HART_ARRAY, &error_abort, NULL); object_property_set_str(OBJECT(&s->cpus), SIFIVE_U_CPU, "cpu-type", &error_abort); object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", &error_abort); - object_initialize(&s->gem, sizeof(s->gem), TYPE_CADENCE_GEM); - qdev_set_parent_bus(DEVICE(&s->gem), sysbus_get_default()); + sysbus_init_child_obj(obj, "gem", &s->gem, sizeof(s->gem), + TYPE_CADENCE_GEM); } static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) From a993cb150f228fc5cfee128c44ffb06bf285e7ea Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 16 Jul 2018 15:30:47 -0700 Subject: [PATCH 2271/2380] virt: Fix crash when introspecting the device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new object_initialize_child() and sysbus_init_child_obj() to fix the issue. Signed-off-by: Alistair Francis Suggested-by: Thomas Huth Reviewed-by: Michael Clark Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- hw/riscv/virt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index aeada2498d..248bbdffd3 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -274,9 +274,8 @@ static void riscv_virt_board_init(MachineState *machine) void *fdt; /* Initialize SOC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); - object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), - &error_abort); + object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), + TYPE_RISCV_HART_ARRAY, &error_abort, NULL); object_property_set_str(OBJECT(&s->soc), VIRT_CPU, "cpu-type", &error_abort); object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", From 5657c3f53ca408a0b8150ecbe70d0cdb3a264d9c Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 17 Jul 2018 11:03:05 -0700 Subject: [PATCH 2272/2380] riscv_hart: Fix crash when introspecting the device Use the new object_initialize_child() and sysbus_init_child_obj() to fix the issue. Signed-off-by: Alistair Francis Suggested-by: Thomas Huth Reviewed-by: Michael Clark Reviewed-by: Thomas Huth --- hw/riscv/riscv_hart.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index 75ba7ed579..e34a26a0ef 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -45,11 +45,10 @@ static void riscv_harts_realize(DeviceState *dev, Error **errp) s->harts = g_new0(RISCVCPU, s->num_harts); for (n = 0; n < s->num_harts; n++) { - - object_initialize(&s->harts[n], sizeof(RISCVCPU), s->cpu_type); + object_initialize_child(OBJECT(s), "harts[*]", &s->harts[n], + sizeof(RISCVCPU), s->cpu_type, + &error_abort, NULL); s->harts[n].env.mhartid = n; - object_property_add_child(OBJECT(s), "harts[*]", OBJECT(&s->harts[n]), - &error_abort); qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]); object_property_set_bool(OBJECT(&s->harts[n]), true, "realized", &err); From 8ff62f6aa067edd5455d60202041b4ae08a65b17 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 17 Jul 2018 11:06:47 -0700 Subject: [PATCH 2273/2380] spike: Fix crash when introspecting the device Use the new object_initialize_child() and sysbus_init_child_obj() to fix the issue. Signed-off-by: Alistair Francis Suggested-by: Thomas Huth Reviewed-by: Michael Clark Reviewed-by: Thomas Huth --- hw/riscv/spike.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index f94e2b6707..c8c056c50b 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -171,9 +171,8 @@ static void spike_v1_10_0_board_init(MachineState *machine) int i; /* Initialize SOC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); - object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), - &error_abort); + object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), + TYPE_RISCV_HART_ARRAY, &error_abort, NULL); object_property_set_str(OBJECT(&s->soc), SPIKE_V1_10_0_CPU, "cpu-type", &error_abort); object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", @@ -254,9 +253,8 @@ static void spike_v1_09_1_board_init(MachineState *machine) int i; /* Initialize SOC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); - object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), - &error_abort); + object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), + TYPE_RISCV_HART_ARRAY, &error_abort, NULL); object_property_set_str(OBJECT(&s->soc), SPIKE_V1_09_1_CPU, "cpu-type", &error_abort); object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", From e65a5f227d77a5dbae7a7123c3ee915ee4bd80cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 19 Jul 2018 16:42:48 +0100 Subject: [PATCH 2274/2380] tcg/aarch64: limit mul_vec size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In AdvSIMD we can only do 32x32 integer multiples although SVE is capable of larger 64 bit multiples. As a result we can end up generating invalid opcodes. Fix this by only reprting we can emit mul vector ops if the size is small enough. Fixes a crash on: sve-all-short-v8.3+sve@vq3/insn_mul_z_zi___INC.risu.bin When running on AArch64 hardware. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20180719154248.29669-1-alex.bennee@linaro.org> [rth: Removed the tcg_debug_assert -- there are plenty of other cases that we do not diagnose within the insn encoding helpers.] Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.inc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tcg/aarch64/tcg-target.inc.c b/tcg/aarch64/tcg-target.inc.c index 4562d36d1b..083592a4d7 100644 --- a/tcg/aarch64/tcg-target.inc.c +++ b/tcg/aarch64/tcg-target.inc.c @@ -2219,7 +2219,6 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) switch (opc) { case INDEX_op_add_vec: case INDEX_op_sub_vec: - case INDEX_op_mul_vec: case INDEX_op_and_vec: case INDEX_op_or_vec: case INDEX_op_xor_vec: @@ -2232,6 +2231,8 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_shri_vec: case INDEX_op_sari_vec: return 1; + case INDEX_op_mul_vec: + return vece < MO_64; default: return 0; From 4712c158c5276fd3c401152f4bb5c3fccf185946 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Thu, 5 Apr 2018 19:41:47 +0200 Subject: [PATCH 2275/2380] e1000e: Prevent MSI/MSI-X storms Only signal MSI/MSI-X events on rising edges. So far we re-triggered the interrupt sources even if the guest did no consumed the pending one, easily causing interrupt storms. Issue was observable with Linux 4.16 e1000e driver when MSI-X was used. Vector 2 was causing interrupt storms after the driver activated the device. Signed-off-by: Jan Kiszka Signed-off-by: Jason Wang --- hw/net/e1000e_core.c | 11 +++++++++++ hw/net/e1000e_core.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 950489160a..2a221c2ef9 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -2023,6 +2023,7 @@ e1000e_msix_notify_one(E1000ECore *core, uint32_t cause, uint32_t int_cfg) effective_eiac = core->mac[EIAC] & cause; core->mac[ICR] &= ~effective_eiac; + core->msi_causes_pending &= ~effective_eiac; if (!(core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { core->mac[IMS] &= ~effective_eiac; @@ -2119,6 +2120,13 @@ e1000e_send_msi(E1000ECore *core, bool msix) { uint32_t causes = core->mac[ICR] & core->mac[IMS] & ~E1000_ICR_ASSERTED; + core->msi_causes_pending &= causes; + causes ^= core->msi_causes_pending; + if (causes == 0) { + return; + } + core->msi_causes_pending |= causes; + if (msix) { e1000e_msix_notify(core, causes); } else { @@ -2156,6 +2164,9 @@ e1000e_update_interrupt_state(E1000ECore *core) core->mac[ICS] = core->mac[ICR]; interrupts_pending = (core->mac[IMS] & core->mac[ICR]) ? true : false; + if (!interrupts_pending) { + core->msi_causes_pending = 0; + } trace_e1000e_irq_pending_interrupts(core->mac[ICR] & core->mac[IMS], core->mac[ICR], core->mac[IMS]); diff --git a/hw/net/e1000e_core.h b/hw/net/e1000e_core.h index 7d8ff41890..63a15510cc 100644 --- a/hw/net/e1000e_core.h +++ b/hw/net/e1000e_core.h @@ -109,6 +109,8 @@ struct E1000Core { NICState *owner_nic; PCIDevice *owner; void (*owner_start_recv)(PCIDevice *d); + + uint32_t msi_causes_pending; }; void From 323e7c117754e4d4ce6b4282d74ad01c99d67714 Mon Sep 17 00:00:00 2001 From: Yunjian Wang Date: Thu, 31 May 2018 15:28:22 +0800 Subject: [PATCH 2276/2380] tap: fix memory leak on success to create a tap device The memory leak on success to create a tap device. And the nfds and nvhosts may not be the same and need to be processed separately. Fixes: 07825977 ("tap: fix memory leak on failure to create a multiqueue tap device") Fixes: 264986e2 ("tap: multiqueue support") Cc: qemu-stable@nongnu.org Signed-off-by: Yunjian Wang Signed-off-by: Jason Wang --- net/tap.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/net/tap.c b/net/tap.c index 2126f4882d..cc8525f154 100644 --- a/net/tap.c +++ b/net/tap.c @@ -805,7 +805,8 @@ int net_init_tap(const Netdev *netdev, const char *name, } else if (tap->has_fds) { char **fds; char **vhost_fds; - int nfds, nvhosts; + int nfds = 0, nvhosts = 0; + int ret = 0; if (tap->has_ifname || tap->has_script || tap->has_downscript || tap->has_vnet_hdr || tap->has_helper || tap->has_queues || @@ -825,6 +826,7 @@ int net_init_tap(const Netdev *netdev, const char *name, if (nfds != nvhosts) { error_setg(errp, "The number of fds passed does not match " "the number of vhostfds passed"); + ret = -1; goto free_fail; } } @@ -833,6 +835,7 @@ int net_init_tap(const Netdev *netdev, const char *name, fd = monitor_fd_param(cur_mon, fds[i], &err); if (fd == -1) { error_propagate(errp, err); + ret = -1; goto free_fail; } @@ -843,6 +846,7 @@ int net_init_tap(const Netdev *netdev, const char *name, } else if (vnet_hdr != tap_probe_vnet_hdr(fd)) { error_setg(errp, "vnet_hdr not consistent across given tap fds"); + ret = -1; goto free_fail; } @@ -852,21 +856,21 @@ int net_init_tap(const Netdev *netdev, const char *name, vnet_hdr, fd, &err); if (err) { error_propagate(errp, err); + ret = -1; goto free_fail; } } - g_free(fds); - g_free(vhost_fds); - return 0; free_fail: + for (i = 0; i < nvhosts; i++) { + g_free(vhost_fds[i]); + } for (i = 0; i < nfds; i++) { g_free(fds[i]); - g_free(vhost_fds[i]); } g_free(fds); g_free(vhost_fds); - return -1; + return ret; } else if (tap->has_helper) { if (tap->has_ifname || tap->has_script || tap->has_downscript || tap->has_vnet_hdr || tap->has_queues || tap->has_vhostfds) { From 33143c446eb88de28ea3088b279feda8f693e398 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 16 Jul 2018 21:53:49 +0200 Subject: [PATCH 2277/2380] linux-user: fix ELF load alignment error When we try to use some targets on ppc64, it can happen the target doesn't support the host page size to align ELF load sections and fails with: ELF load command alignment not page-aligned Since commit a70daba3771 ("linux-user: Tell guest about big host page sizes") the host page size is used to align ELF sections, but this doesn't work if the alignment required by the load section is smaller than the host one. For these cases, we continue to use the TARGET_PAGE_SIZE instead of the host one. I have tested this change on ppc64, and it fixes qemu linux-user for: s390x, m68k, i386, arm, aarch64, hppa and I have tested it doesn't break the following targets: x86_64, mips64el, sh4 mips and mipsel abort, but I think for another reason. Signed-off-by: Laurent Vivier Reviewed-by: Richard Henderson [lv: fixed "info->alignment = 0"] Message-Id: <20180716195349.29959-1-laurent@vivier.eu> --- linux-user/elfload.c | 10 +++++++++- linux-user/qemu.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 942a1b661f..df07055361 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1875,7 +1875,13 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff)); NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr))); NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum)); - NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(MAX(TARGET_PAGE_SIZE, getpagesize()))); + if ((info->alignment & ~qemu_host_page_mask) != 0) { + /* Target doesn't support host page size alignment */ + NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE)); + } else { + NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(MAX(TARGET_PAGE_SIZE, + qemu_host_page_size))); + } NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_info ? interp_info->load_addr : 0)); NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0); NEW_AUX_ENT(AT_ENTRY, info->entry); @@ -2202,6 +2208,7 @@ static void load_elf_image(const char *image_name, int image_fd, /* Find the maximum size of the image and allocate an appropriate amount of memory to handle that. */ loaddr = -1, hiaddr = 0; + info->alignment = 0; for (i = 0; i < ehdr->e_phnum; ++i) { if (phdr[i].p_type == PT_LOAD) { abi_ulong a = phdr[i].p_vaddr - phdr[i].p_offset; @@ -2213,6 +2220,7 @@ static void load_elf_image(const char *image_name, int image_fd, hiaddr = a; } ++info->nsegs; + info->alignment |= phdr[i].p_align; } } diff --git a/linux-user/qemu.h b/linux-user/qemu.h index bb85c81aa4..7b16a1cdea 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -51,6 +51,7 @@ struct image_info { abi_ulong file_string; uint32_t elf_flags; int personality; + abi_ulong alignment; /* The fields below are used in FDPIC mode. */ abi_ulong loadmap_addr; From fa97e38eed3bbdbc322d94092ca49450e724fff1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Jul 2018 13:06:48 -0700 Subject: [PATCH 2278/2380] linux-user/ppc: Implement swapcontext syscall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows the tests generated by debian-powerpc-user-cross to function properly, especially tests/test-coroutine. Technically this syscall is available to both ppc32 and ppc64, but only ppc32 glibc actually uses it. Thus the ppc64 path is untested. Signed-off-by: Richard Henderson Tested-by: Alex Bennée Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20180718200648.22529-1-richard.henderson@linaro.org> --- linux-user/ppc/signal.c | 56 +++++++++++++++++++++++++++++++++++++++++ linux-user/qemu.h | 2 ++ linux-user/syscall.c | 6 +++++ 3 files changed, 64 insertions(+) diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index ef4c518f11..2ae120a2bc 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -675,3 +675,59 @@ sigsegv: force_sig(TARGET_SIGSEGV); return -TARGET_QEMU_ESIGRETURN; } + +/* This syscall implements {get,set,swap}context for userland. */ +abi_long do_swapcontext(CPUArchState *env, abi_ulong uold_ctx, + abi_ulong unew_ctx, abi_long ctx_size) +{ + struct target_ucontext *uctx; + struct target_mcontext *mctx; + + /* For ppc32, ctx_size is "reserved for future use". + * For ppc64, we do not yet support the VSX extension. + */ + if (ctx_size < sizeof(struct target_ucontext)) { + return -TARGET_EINVAL; + } + + if (uold_ctx) { + TaskState *ts = (TaskState *)thread_cpu->opaque; + + if (!lock_user_struct(VERIFY_WRITE, uctx, uold_ctx, 1)) { + return -TARGET_EFAULT; + } + +#ifdef TARGET_PPC64 + mctx = &uctx->tuc_sigcontext.mcontext; +#else + /* ??? The kernel aligns the pointer down here into padding, but + * in setup_rt_frame we don't. Be self-compatible for now. + */ + mctx = &uctx->tuc_mcontext; + __put_user(h2g(mctx), &uctx->tuc_regs); +#endif + + save_user_regs(env, mctx); + host_to_target_sigset(&uctx->tuc_sigmask, &ts->signal_mask); + + unlock_user_struct(uctx, uold_ctx, 1); + } + + if (unew_ctx) { + int err; + + if (!lock_user_struct(VERIFY_READ, uctx, unew_ctx, 1)) { + return -TARGET_EFAULT; + } + err = do_setcontext(uctx, env, 0); + unlock_user_struct(uctx, unew_ctx, 1); + + if (err) { + /* We cannot return to a partially updated context. */ + force_sig(TARGET_SIGSEGV); + } + return -TARGET_QEMU_ESIGRETURN; + } + + return 0; +} diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 7b16a1cdea..b4959e41c6 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -396,6 +396,8 @@ long do_sigreturn(CPUArchState *env); long do_rt_sigreturn(CPUArchState *env); abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset); +abi_long do_swapcontext(CPUArchState *env, abi_ulong uold_ctx, + abi_ulong unew_ctx, abi_long ctx_size); /** * block_signals: block all signals while handling this guest syscall * diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 3df3bdffb2..dfc851cc35 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -12790,6 +12790,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(kcmp(arg1, arg2, arg3, arg4, arg5)); break; #endif +#ifdef TARGET_NR_swapcontext + case TARGET_NR_swapcontext: + /* PowerPC specific. */ + ret = do_swapcontext(cpu_env, arg1, arg2, arg3); + break; +#endif default: unimplemented: From 6598f0cdad6acc6674c4f060fa46e537228c2c47 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Fri, 20 Jul 2018 23:25:09 +0200 Subject: [PATCH 2279/2380] po: Don't include comments with location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those comments change often when ui/gtk.c is changed and are not really useful. Signed-off-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-id: 20180720212509.18339-1-sw@weilnetz.de Signed-off-by: Peter Maydell --- po/Makefile | 2 +- po/bg.po | 23 ++++------------------- po/de_DE.po | 23 ++++------------------- po/fr_FR.po | 23 ++++------------------- po/hu.po | 23 ++++------------------- po/it.po | 23 ++++------------------- po/messages.po | 25 +++++-------------------- po/tr.po | 23 ++++------------------- po/zh_CN.po | 23 ++++------------------- 9 files changed, 34 insertions(+), 154 deletions(-) diff --git a/po/Makefile b/po/Makefile index cc630363de..e47e262ee6 100644 --- a/po/Makefile +++ b/po/Makefile @@ -43,7 +43,7 @@ install: $(OBJS) $(PO_PATH)/messages.po: $(SRC_PATH)/ui/gtk.c $(call quiet-command, ( cd $(SRC_PATH) && \ - xgettext -o - --from-code=UTF-8 --foreign-user \ + xgettext -o - --from-code=UTF-8 --foreign-user --no-location \ --package-name=QEMU --package-version=$(VERSION) \ --msgid-bugs-address=qemu-devel@nongnu.org -k_ -C ui/gtk.c | \ sed -e s/CHARSET/UTF-8/) >$@,"GEN","$@") diff --git a/po/bg.po b/po/bg.po index 279d1b864a..3d8c353372 100644 --- a/po/bg.po +++ b/po/bg.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 2.6.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2016-06-09 15:54+0300\n" "Last-Translator: Alexander Shopov \n" "Language-Team: Bulgarian \n" @@ -17,74 +17,59 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " — натиснете Ctrl+Alt+G, за да освободите фокуса" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [пауза]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Пауза" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "_Рестартиране" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Изключване" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "_Спиране на програмата" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "На _цял екран" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "_Копиране" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "_Увеличаване" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "_Намаляване" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "По_местване" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Напас_ване" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Прихващане при посо_чване" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "Прихващане на _фокуса" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "Подпро_зорци" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "Към самостоятелен подпрозорец" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Машина" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Изглед" diff --git a/po/de_DE.po b/po/de_DE.po index de27fcf174..6f2c3cdc2f 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2012-02-28 16:00+0100\n" "Last-Translator: Kevin Wolf \n" "Language-Team: Deutsch \n" @@ -16,74 +16,59 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Strg+Alt+G drücken, um Eingabegeräte freizugeben" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [Angehalten]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Angehalten" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "_Reset" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Herunterfahren" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "_Beenden" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "_Vollbild" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "_Kopieren" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "_Heranzoomen" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "_Wegzoomen" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "_Einpassen" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Auf _Fenstergröße skalieren" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Tastatur _automatisch einfangen" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "_Eingabegeräte einfangen" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "Reiter anzeigen" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "Reiter abtrennen" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Maschine" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Ansicht" diff --git a/po/fr_FR.po b/po/fr_FR.po index 94f4a94f5c..25ad4c954a 100644 --- a/po/fr_FR.po +++ b/po/fr_FR.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2014-07-28 23:25+0200\n" "Last-Translator: Aurelien Jarno \n" "Language-Team: French \n" @@ -17,74 +17,59 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 1.4\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr "- Appuyer sur Ctrl+Alt+G pour arrêter la capture" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [En pause]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Pause" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "_Réinitialiser" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Éteindre" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "_Quitter" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "Mode _plein écran" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "_Copier" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "Zoom _avant" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "_Zoom arrière" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "Zoom _idéal" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Zoomer pour a_juster" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Capturer en _survolant" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "_Capturer les entrées" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "Montrer les _onglets" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "_Détacher l'onglet" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Machine" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Vue" diff --git a/po/hu.po b/po/hu.po index 86f78e92b9..a82d9ec230 100644 --- a/po/hu.po +++ b/po/hu.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2013-05-06 20:42+0200\n" "Last-Translator: Ákos Kovács \n" "Language-Team: Hungarian \n" @@ -15,77 +15,62 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Nyomj Ctrl+Alt+G-t a bemeneti eszközök elengedéséhez" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [Megállítva]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Megállítás" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "Új_raindítás" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Leállítás" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "" -#: ui/gtk.c:2048 #, fuzzy msgid "Zoom _In" msgstr "Ablakmérethez _igazítás" -#: ui/gtk.c:2055 #, fuzzy msgid "Zoom _Out" msgstr "Ablakmérethez _igazítás" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Ablakmérethez _igazítás" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Automatikus _elfogás" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "_Bemeneti eszközök megragadása" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "_Fülek megjelenítése" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Gép" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Nézet" diff --git a/po/it.po b/po/it.po index bfae84e797..c6d9517207 100644 --- a/po/it.po +++ b/po/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2014-07-29 08:25+0200\n" "Last-Translator: Paolo Bonzini \n" "Language-Team: Italian \n" @@ -16,74 +16,59 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Premere Ctrl+Alt+G per rilasciare l'input" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [Pausa]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Pausa" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "_Reset" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Spegni" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "_Esci" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "A t_utto schermo" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "_Copia" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "_Aumenta zoom" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "_Riduci zoom" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "A_nnulla zoom" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Adatta alla _finestra" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Cattura _automatica input" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "_Cattura input" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "Mostra _tab" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "_Sposta in una nuova finestra" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Macchina virtuale" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Visualizza" diff --git a/po/messages.po b/po/messages.po index e0a98c16a9..065bd459a0 100644 --- a/po/messages.po +++ b/po/messages.po @@ -5,9 +5,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: QEMU 2.7.93\n" +"Project-Id-Version: QEMU 2.12.91\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,74 +16,59 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr "" -#: ui/gtk.c:279 msgid " [Paused]" msgstr "" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "" -#: ui/gtk.c:2127 msgid "_View" msgstr "" diff --git a/po/tr.po b/po/tr.po index af34b52d52..632c7f3851 100644 --- a/po/tr.po +++ b/po/tr.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2013-04-22 18:35+0300\n" "Last-Translator: Ozan Çağlayan \n" "Language-Team: Türkçe <>\n" @@ -17,76 +17,61 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Gtranslator 2.91.6\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Yakalamayı durdurmak için Ctrl+Alt+G tuşlarına basın" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [Duraklatıldı]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Duraklat" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "_Sıfırla" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Kapat" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "" -#: ui/gtk.c:2048 #, fuzzy msgid "Zoom _In" msgstr "Yakınlaş ve Sığ_dır" -#: ui/gtk.c:2055 #, fuzzy msgid "Zoom _Out" msgstr "Yakınlaş ve Sığ_dır" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Yakınlaş ve Sığ_dır" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Ü_zerindeyken Yakala" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "Girdiyi _Yakala" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "Se_kmeleri Göster" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Makine" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Görüntüle" diff --git a/po/zh_CN.po b/po/zh_CN.po index d20b6c6981..b25e8e3c02 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 2.2\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2014-07-31 10:00+0800\n" "Last-Translator: Fam Zheng \n" "Language-Team: Chinese \n" @@ -17,74 +17,59 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 1.4\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - 按下 Ctrl+Alt+G 取消捕获" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [已暂停]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "暂停(_P)" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "重置(_R)" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "关闭电源(_D)" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "退出(_Q)" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "全屏(_F)" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "复制(_C)" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "放大(_I)" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "缩小(_O)" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "最合适大小(_F)" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "缩放以适应大小(_F)" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "鼠标经过时捕获(_H)" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "捕获输入(_G)" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "显示标签页(_T)" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "分离标签页" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "虚拟机(_M)" -#: ui/gtk.c:2127 msgid "_View" msgstr "视图(_V)" From 25b1ef31db30a7dace4d3a485f0a3b3c7285a795 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 18 Jul 2018 11:05:57 +0200 Subject: [PATCH 2280/2380] qapi: Make 'allow-oob' optional in SchemaInfoCommand Making 'allow-oob' optional in SchemaInfoCommand permits omitting it in the common case. Shrinks query-qmp-schema's output from 122.1KiB to 118.6KiB for me. Note that out-of-band execution is still experimental (you have to configure the monitor with x-oob=on to use it). Signed-off-by: Markus Armbruster Message-Id: <20180718090557.17248-1-armbru@redhat.com> Reviewed-by: Peter Xu --- qapi/introspect.json | 6 +++--- scripts/qapi/introspect.py | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/qapi/introspect.json b/qapi/introspect.json index c7f67b7d78..137b39b992 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -259,8 +259,8 @@ # # @ret-type: the name of the command's result type. # -# @allow-oob: whether the command allows out-of-band execution. -# (Since: 2.12) +# @allow-oob: whether the command allows out-of-band execution, +# defaults to false (Since: 2.12) # # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # @@ -268,7 +268,7 @@ ## { 'struct': 'SchemaInfoCommand', 'data': { 'arg-type': 'str', 'ret-type': 'str', - 'allow-oob': 'bool' } } + '*allow-oob': 'bool' } } ## # @SchemaInfoEvent: diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 70ca5dd876..189a4edaba 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -184,11 +184,11 @@ const QLitObject %(c_name)s = %(c_string)s; success_response, boxed, allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type - self._gen_qlit(name, 'command', - {'arg-type': self._use_type(arg_type), - 'ret-type': self._use_type(ret_type), - 'allow-oob': allow_oob}, - ifcond) + obj = {'arg-type': self._use_type(arg_type), + 'ret-type': self._use_type(ret_type) } + if allow_oob: + obj['allow-oob'] = allow_oob + self._gen_qlit(name, 'command', obj, ifcond) def visit_event(self, name, info, ifcond, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type From 62aa1d887ff9fc76adb488d31447d126a78f4b8f Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 20 Jul 2018 11:34:51 +0800 Subject: [PATCH 2281/2380] monitor: Fix unsafe sharing of @cur_mon among threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @cur_mon is null unless the main thread is running monitor code, either HMP code within monitor_read(), or QMP code within monitor_qmp_dispatch(). Use of @cur_mon outside the main thread is therefore unsafe. Most of its uses are in monitor command handlers. These run in the main thread. However, there are also uses hiding elsewhere, such as in error_vprintf(), and thus error_report(), making these functions unsafe outside the main thread. No such unsafe uses are known at this time. Regardless, this is an unnecessary trap. It's an ancient trap, though. More recently, commit cf869d53172 "qmp: support out-of-band (oob) execution" spiced things up: the monitor I/O thread assigns to @cur_mon when executing commands out-of-band. Having two threads save, set and restore @cur_mon without synchronization is definitely unsafe. We can end up with @cur_mon null while the main thread runs monitor code, or non-null while it runs non-monitor code. We could fix this by making the I/O thread not mess with @cur_mon, but that would leave the trap armed and ready. Instead, make @cur_mon thread-local. It's now reliably null unless the thread is running monitor code. Reviewed-by: Eric Blake Reviewed-by: Marc-André Lureau Reviewed-by: Stefan Hajnoczi [peterx: update subject and commit message written by Markus] Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180720033451.32710-1-peterx@redhat.com> --- include/monitor/monitor.h | 2 +- monitor.c | 2 +- stubs/monitor.c | 2 +- tests/test-util-sockets.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index d6ab70cae2..2ef5e04b37 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -6,7 +6,7 @@ #include "qapi/qapi-types-misc.h" #include "qemu/readline.h" -extern Monitor *cur_mon; +extern __thread Monitor *cur_mon; /* flags for monitor_init */ /* 0x01 unused */ diff --git a/monitor.c b/monitor.c index be29634a00..f75027b09e 100644 --- a/monitor.c +++ b/monitor.c @@ -290,7 +290,7 @@ static mon_cmd_t info_cmds[]; QmpCommandList qmp_commands, qmp_cap_negotiation_commands; -Monitor *cur_mon; +__thread Monitor *cur_mon; static void monitor_command_cb(void *opaque, const char *cmdline, void *readline_opaque); diff --git a/stubs/monitor.c b/stubs/monitor.c index e018c8f594..3890771bb5 100644 --- a/stubs/monitor.c +++ b/stubs/monitor.c @@ -3,7 +3,7 @@ #include "qemu-common.h" #include "monitor/monitor.h" -Monitor *cur_mon = NULL; +__thread Monitor *cur_mon; int monitor_get_fd(Monitor *mon, const char *name, Error **errp) { diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c index acadd85e8f..6195a3ac36 100644 --- a/tests/test-util-sockets.c +++ b/tests/test-util-sockets.c @@ -69,7 +69,7 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) * stubs/monitor.c is defined, to make sure monitor.o is discarded * otherwise we get duplicate syms at link time. */ -Monitor *cur_mon; +__thread Monitor *cur_mon; void monitor_init(Chardev *chr, int flags) {} From 7b6d7b84da328d5d1fffb862b8388d511e085812 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 23 Jul 2018 15:21:25 +0100 Subject: [PATCH 2282/2380] hw/microblaze/xlnx-zynqmp-pmu: Fix introspection problem in 'xlnx, zynqmp-pmu-soc' Valgrind complains: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'xlnx,zynqmp-pmu-soc'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q microblazeel-softmmu/qemu-system-microblazeel -M none,accel=qtest -qmp stdio [...] ==13605== Invalid read of size 8 ==13605== at 0x2AC69A: qdev_print (qdev-monitor.c:686) ==13605== by 0x2AC69A: qbus_print (qdev-monitor.c:719) ==13605== by 0x2591E8: handle_hmp_command (monitor.c:3446) Use the new object_initialize_child() and sysbus_init_child_obj() to fix the issue. Signed-off-by: Thomas Huth Reviewed-by: Edgar E. Iglesias Message-id: 1531839343-13828-1-git-send-email-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/microblaze/xlnx-zynqmp-pmu.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index 999a5657cf..57dc1ccd42 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -62,13 +62,11 @@ static void xlnx_zynqmp_pmu_soc_init(Object *obj) { XlnxZynqMPPMUSoCState *s = XLNX_ZYNQMP_PMU_SOC(obj); - object_initialize(&s->cpu, sizeof(s->cpu), - TYPE_MICROBLAZE_CPU); - object_property_add_child(obj, "pmu-cpu", OBJECT(&s->cpu), - &error_abort); + object_initialize_child(obj, "pmu-cpu", &s->cpu, sizeof(s->cpu), + TYPE_MICROBLAZE_CPU, &error_abort, NULL); - object_initialize(&s->intc, sizeof(s->intc), TYPE_XLNX_PMU_IO_INTC); - qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default()); + sysbus_init_child_obj(obj, "intc", &s->intc, sizeof(s->intc), + TYPE_XLNX_PMU_IO_INTC); } static void xlnx_zynqmp_pmu_soc_realize(DeviceState *dev, Error **errp) From 03a31776e8fb239fee98625dd83b85f5cbe3ccba Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 23 Jul 2018 15:21:26 +0100 Subject: [PATCH 2283/2380] hw/sd/bcm2835_sdhost: Fix PIO mode writes Writes in PIO mode have two requirements: - A data interrupt must be generated after a write command has been issued to indicate that the chip is ready to receive data. - A block interrupt must be generated after each block to indicate that the chip is ready to receive the next data block. Rearrange the code to make this happen. Tested on raspi3 (in PIO mode) and raspi2 (in DMA mode). Signed-off-by: Guenter Roeck Message-id: 1531779837-20557-1-git-send-email-linux@roeck-us.net Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sd/bcm2835_sdhost.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index 4df4de7d67..1b760b2a7c 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -179,9 +179,11 @@ static void bcm2835_sdhost_fifo_run(BCM2835SDHostState *s) uint32_t value = 0; int n; int is_read; + int is_write; is_read = (s->cmd & SDCMD_READ_CMD) != 0; - if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))) { + is_write = (s->cmd & SDCMD_WRITE_CMD) != 0; + if (s->datacnt != 0 && (is_write || sdbus_data_ready(&s->sdbus))) { if (is_read) { n = 0; while (s->datacnt && s->fifo_len < BCM2835_SDHOST_FIFO_LEN) { @@ -201,8 +203,11 @@ static void bcm2835_sdhost_fifo_run(BCM2835SDHostState *s) if (n != 0) { bcm2835_sdhost_fifo_push(s, value); s->status |= SDHSTS_DATA_FLAG; + if (s->config & SDHCFG_DATA_IRPT_EN) { + s->status |= SDHSTS_SDIO_IRPT; + } } - } else { /* write */ + } else if (is_write) { /* write */ n = 0; while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { if (n == 0) { @@ -223,11 +228,18 @@ static void bcm2835_sdhost_fifo_run(BCM2835SDHostState *s) s->edm &= ~SDEDM_FSM_MASK; s->edm |= SDEDM_FSM_DATAMODE; trace_bcm2835_sdhost_edm_change("datacnt 0", s->edm); - - if ((s->cmd & SDCMD_WRITE_CMD) && + } + if (is_write) { + /* set block interrupt at end of each block transfer */ + if (s->hbct && s->datacnt % s->hbct == 0 && (s->config & SDHCFG_BLOCK_IRPT_EN)) { s->status |= SDHSTS_BLOCK_IRPT; } + /* set data interrupt after each transfer */ + s->status |= SDHSTS_DATA_FLAG; + if (s->config & SDHCFG_DATA_IRPT_EN) { + s->status |= SDHSTS_SDIO_IRPT; + } } } From 9d2b5a58f85be2d8e129c4b53d6708ecf8796e54 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 23 Jul 2018 15:21:26 +0100 Subject: [PATCH 2284/2380] target/arm: Correctly handle overlapping small MPU regions To correctly handle small (less than TARGET_PAGE_SIZE) MPU regions, we must correctly handle the case where the address being looked up hits in an MPU region that is not small but the address is in the same page as a small region. For instance if MPU region 1 covers an entire page from 0x2000 to 0x2400 and MPU region 2 is small and covers only 0x2200 to 0x2280, then for an access to 0x2000 we must not return a result covering the full page even though we hit the page-sized region 1. Otherwise we will then cache that result in the TLB and accesses that should hit region 2 will incorrectly find the region 1 information. Check for the case where we miss an MPU region but it is still within the same page, and in that case narrow the size we will pass to tlb_set_page_with_attrs() for whatever the final outcome is of the MPU lookup. Reported-by: Adithya Baglody Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180716133302.25989-1-peter.maydell@linaro.org --- target/arm/helper.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 0604a0efbe..22d812240a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -17,6 +17,7 @@ #include "exec/semihost.h" #include "sysemu/kvm.h" #include "fpu/softfloat.h" +#include "qemu/range.h" #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ @@ -9669,6 +9670,20 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, } if (address < base || address > base + rmask) { + /* + * Address not in this region. We must check whether the + * region covers addresses in the same page as our address. + * In that case we must not report a size that covers the + * whole page for a subsequent hit against a different MPU + * region or the background region, because it would result in + * incorrect TLB hits for subsequent accesses to addresses that + * are in this MPU region. + */ + if (ranges_overlap(base, rmask, + address & TARGET_PAGE_MASK, + TARGET_PAGE_SIZE)) { + *page_size = 1; + } continue; } @@ -9888,6 +9903,22 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, sattrs->srvalid = true; sattrs->sregion = r; } + } else { + /* + * Address not in this region. We must check whether the + * region covers addresses in the same page as our address. + * In that case we must not report a size that covers the + * whole page for a subsequent hit against a different MPU + * region or the background region, because it would result + * in incorrect TLB hits for subsequent accesses to + * addresses that are in this MPU region. + */ + if (limit >= base && + ranges_overlap(base, limit - base + 1, + addr_page_base, + TARGET_PAGE_SIZE)) { + sattrs->subpage = true; + } } } } @@ -9963,6 +9994,21 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, } if (address < base || address > limit) { + /* + * Address not in this region. We must check whether the + * region covers addresses in the same page as our address. + * In that case we must not report a size that covers the + * whole page for a subsequent hit against a different MPU + * region or the background region, because it would result in + * incorrect TLB hits for subsequent accesses to addresses that + * are in this MPU region. + */ + if (limit >= base && + ranges_overlap(base, limit - base + 1, + addr_page_base, + TARGET_PAGE_SIZE)) { + *is_subpage = true; + } continue; } From 07bc425ea32dc5d78790c7bd23bffb77fbc727e0 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 23 Jul 2018 15:21:26 +0100 Subject: [PATCH 2285/2380] hw/arm/spitz: Move problematic nand_init() code to realize function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nand_init() does not only create the NAND device, it also realizes the device with qdev_init_nofail() already. So we must not call nand_init() from an instance_init function like sl_nand_init(), otherwise we get superfluous NAND devices in the QOM tree after introspecting the 'sl-nand' device. So move the nand_init() to the realize function of 'sl-nand' instead. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-id: 1532006134-7701-1-git-send-email-thuth@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/spitz.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index 3cc27a1e44..c4bc3deedf 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -169,16 +169,22 @@ static void sl_nand_init(Object *obj) { SLNANDState *s = SL_NAND(obj); SysBusDevice *dev = SYS_BUS_DEVICE(obj); - DriveInfo *nand; s->ctl = 0; + + memory_region_init_io(&s->iomem, obj, &sl_ops, s, "sl", 0x40); + sysbus_init_mmio(dev, &s->iomem); +} + +static void sl_nand_realize(DeviceState *dev, Error **errp) +{ + SLNANDState *s = SL_NAND(dev); + DriveInfo *nand; + /* FIXME use a qdev drive property instead of drive_get() */ nand = drive_get(IF_MTD, 0, 0); s->nand = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL, s->manf_id, s->chip_id); - - memory_region_init_io(&s->iomem, obj, &sl_ops, s, "sl", 0x40); - sysbus_init_mmio(dev, &s->iomem); } /* Spitz Keyboard */ @@ -1079,6 +1085,7 @@ static void sl_nand_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_sl_nand_info; dc->props = sl_nand_properties; + dc->realize = sl_nand_realize; /* Reason: init() method uses drive_get() */ dc->user_creatable = false; } From 1ddc9b98c3cb89fe23a55ba924000fd645253e87 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 23 Jul 2018 15:21:27 +0100 Subject: [PATCH 2286/2380] hw/intc/exynos4210_gic: Turn instance_init into realize function The instance_init function of the "exynos4210.gic" device creates a new "arm_gic" device and immediately realizes it with qdev_init_nofail(). This will leave a lot of object in the QOM tree during introspection of the "exynos4210.gic" device, e.g. reproducible by starting QEMU like this: qemu-system-aarch64 -M none -nodefaults -nographic -monitor stdio And then by running "info qom-tree" at the HMP monitor, followed by "device_add exynos4210.gic,help" and finally checking "info qom-tree" again. Also note that qdev_init_nofail() can exit QEMU in case of errors - and this must never happen during an instance_init function, otherwise QEMU could terminate unexpectedly during introspection of a device. Since most of the code that follows the qdev_init_nofail() depends on the realized "gicbusdev", the easiest solution to the problem is to turn the whole instance_init function into a realize function instead. Signed-off-by: Thomas Huth Message-id: 1532337784-334-1-git-send-email-thuth@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/exynos4210_gic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index b6b00a4f58..69f9c18d73 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -281,9 +281,9 @@ static void exynos4210_gic_set_irq(void *opaque, int irq, int level) qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); } -static void exynos4210_gic_init(Object *obj) +static void exynos4210_gic_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(obj); + Object *obj = OBJECT(dev); Exynos4210GicState *s = EXYNOS4210_GIC(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); const char cpu_prefix[] = "exynos4210-gic-alias_cpu"; @@ -347,13 +347,13 @@ static void exynos4210_gic_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->props = exynos4210_gic_properties; + dc->realize = exynos4210_gic_realize; } static const TypeInfo exynos4210_gic_info = { .name = TYPE_EXYNOS4210_GIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210GicState), - .instance_init = exynos4210_gic_init, .class_init = exynos4210_gic_class_init, }; From 4e04f3d91adf77bccf8903cb8a20ef5ddb9b2761 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 13 Jul 2018 09:05:39 +0200 Subject: [PATCH 2287/2380] qemu-iotests: Use host_device instead of file in 149 The test case uses block devices with driver=file, which causes the test to fail after commit 230ff73904 added a deprecation warning for this. Fix the test case to use driver=host_device and update the reference output accordingly. Signed-off-by: Kevin Wolf --- tests/qemu-iotests/149 | 2 +- tests/qemu-iotests/149.out | 344 ++++++++++++++++++------------------- 2 files changed, 173 insertions(+), 173 deletions(-) diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149 index d3ffa259db..9e0cad76f9 100755 --- a/tests/qemu-iotests/149 +++ b/tests/qemu-iotests/149 @@ -259,7 +259,7 @@ def qemu_io_image_args(config, dev=False): if dev: return [ "--image-opts", - "driver=file,filename=%s" % config.device_path()] + "driver=host_device,filename=%s" % config.device_path()] else: return [ "--object", diff --git a/tests/qemu-iotests/149.out b/tests/qemu-iotests/149.out index 5dea00bfa8..1407ce6dad 100644 --- a/tests/qemu-iotests/149.out +++ b/tests/qemu-iotests/149.out @@ -7,13 +7,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -43,13 +43,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -67,13 +67,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha1.img', fmt=luks size=439804651 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -103,13 +103,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -127,13 +127,13 @@ sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 512 --h sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -163,13 +163,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -187,13 +187,13 @@ Formatting 'TEST_DIR/luks-twofish-256-xts-plain64-sha1.img', fmt=luks size=43980 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -223,13 +223,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -247,13 +247,13 @@ sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 512 --h sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -283,13 +283,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -307,13 +307,13 @@ Formatting 'TEST_DIR/luks-serpent-256-xts-plain64-sha1.img', fmt=luks size=43980 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -343,13 +343,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -367,13 +367,13 @@ sudo cryptsetup -q -v luksFormat --cipher cast5-cbc-plain64 --key-size 128 --has sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -403,13 +403,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -427,13 +427,13 @@ Formatting 'TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img', fmt=luks size=4398046 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -463,13 +463,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -488,13 +488,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain --key-size 256 --hash sh sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -524,13 +524,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -548,13 +548,13 @@ Formatting 'TEST_DIR/luks-aes-256-cbc-plain-sha1.img', fmt=luks size=43980465111 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -584,13 +584,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -608,13 +608,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64 --key-size 256 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -644,13 +644,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -668,13 +668,13 @@ Formatting 'TEST_DIR/luks-aes-256-cbc-plain64-sha1.img', fmt=luks size=439804651 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -704,13 +704,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -728,13 +728,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 -- sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -764,13 +764,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -788,13 +788,13 @@ Formatting 'TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img', fmt=luks size=4398 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -824,13 +824,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -848,13 +848,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-essiv:sha256 --key-size 512 -- sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -884,13 +884,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -908,13 +908,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img', fmt=luks size=4398 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -944,13 +944,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -968,13 +968,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 256 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1004,13 +1004,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1028,13 +1028,13 @@ Formatting 'TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img', fmt=luks size=43 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1064,13 +1064,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1088,13 +1088,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 384 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1124,13 +1124,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1148,13 +1148,13 @@ Formatting 'TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img', fmt=luks size=43 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1184,13 +1184,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1208,13 +1208,13 @@ sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 256 --h sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1244,13 +1244,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1268,13 +1268,13 @@ Formatting 'TEST_DIR/luks-twofish-128-xts-plain64-sha1.img', fmt=luks size=43980 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1304,13 +1304,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1329,13 +1329,13 @@ sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 256 --h sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1365,13 +1365,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1389,13 +1389,13 @@ Formatting 'TEST_DIR/luks-serpent-128-xts-plain64-sha1.img', fmt=luks size=43980 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1425,13 +1425,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1449,13 +1449,13 @@ sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 384 --h sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1485,13 +1485,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1509,13 +1509,13 @@ Formatting 'TEST_DIR/luks-serpent-192-xts-plain64-sha1.img', fmt=luks size=43980 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1545,13 +1545,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1571,13 +1571,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1607,13 +1607,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1631,13 +1631,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha224.img', fmt=luks size=4398046 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1667,13 +1667,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1691,13 +1691,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1727,13 +1727,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1751,13 +1751,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha256.img', fmt=luks size=4398046 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1787,13 +1787,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1811,13 +1811,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1847,13 +1847,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1871,13 +1871,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha384.img', fmt=luks size=4398046 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1907,13 +1907,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1931,13 +1931,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1967,13 +1967,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1991,13 +1991,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha512.img', fmt=luks size=4398046 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2027,13 +2027,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2051,13 +2051,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2087,13 +2087,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2111,13 +2111,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img', fmt=luks size=4398 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2147,13 +2147,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2171,13 +2171,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain --key-size 512 --hash sh sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img qiotest-145-aes-256-xts-plain-sha1-pwslot3 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2207,13 +2207,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img qiotest-145-aes-256-xts-plain-sha1-pwslot3 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2245,13 +2245,13 @@ sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2281,13 +2281,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2305,13 +2305,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img', fmt=luks size= sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2341,13 +2341,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2365,13 +2365,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 -- sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2401,13 +2401,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2425,13 +2425,13 @@ Formatting 'TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img', fmt=luks size=439804 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2461,13 +2461,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2485,13 +2485,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64:sha256 --key-size 256 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2521,13 +2521,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2545,13 +2545,13 @@ Formatting 'TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img', fmt=luks size=43 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2581,13 +2581,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) From 50d6a8a352af164a3335d510af38b9545d568676 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Thu, 12 Jul 2018 21:51:20 +0200 Subject: [PATCH 2288/2380] block: Fix typos in comments (found by codespell) Signed-off-by: Stefan Weil Reviewed-by: John Snow Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- block.c | 2 +- block/backup.c | 4 ++-- block/curl.c | 2 +- block/gluster.c | 2 +- block/vhdx.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/block.c b/block.c index a2fe05ea96..39f373e035 100644 --- a/block.c +++ b/block.c @@ -3002,7 +3002,7 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, * * Reopens all BDS specified in the queue, with the appropriate * flags. All devices are prepared for reopen, and failure of any - * device will cause all device changes to be abandonded, and intermediate + * device will cause all device changes to be abandoned, and intermediate * data cleaned up. * * If all devices prepare successfully, then the changes are committed diff --git a/block/backup.c b/block/backup.c index 319fc922e8..8630d32926 100644 --- a/block/backup.c +++ b/block/backup.c @@ -91,7 +91,7 @@ static void cow_request_end(CowRequest *req) } /* Copy range to target with a bounce buffer and return the bytes copied. If - * error occured, return a negative error number */ + * error occurred, return a negative error number */ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, int64_t start, int64_t end, @@ -148,7 +148,7 @@ fail: } -/* Copy range to target and return the bytes copied. If error occured, return a +/* Copy range to target and return the bytes copied. If error occurred, return a * negative error number. */ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, int64_t start, diff --git a/block/curl.c b/block/curl.c index aa42535783..229bb84a27 100644 --- a/block/curl.c +++ b/block/curl.c @@ -804,7 +804,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, } /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not * know or the size is zero. From 7.19.4 CURL returns -1 if size is not - * known and zero if it is realy zero-length file. */ + * known and zero if it is really zero-length file. */ #if LIBCURL_VERSION_NUM >= 0x071304 if (d < 0) { pstrcpy(state->errmsg, CURL_ERROR_SIZE, diff --git a/block/gluster.c b/block/gluster.c index a4e1c8ecd8..4fd55a9cc5 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -1326,7 +1326,7 @@ static int qemu_gluster_has_zero_init(BlockDriverState *bs) * If @start is in a trailing hole or beyond EOF, return -ENXIO. * If we can't find out, return a negative errno other than -ENXIO. * - * (Shamefully copied from file-posix.c, only miniscule adaptions.) + * (Shamefully copied from file-posix.c, only minuscule adaptions.) */ static int find_allocation(BlockDriverState *bs, off_t start, off_t *data, off_t *hole) diff --git a/block/vhdx.c b/block/vhdx.c index 4d0819750f..0795ca1985 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -185,7 +185,7 @@ uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, /* Validates the checksum of the buffer, with an in-place CRC. * * Zero is substituted during crc calculation for the original crc field, - * and the crc field is restored afterwards. But the buffer will be modifed + * and the crc field is restored afterwards. But the buffer will be modified * during the calculation, so this may not be not suitable for multi-threaded * use. * From 6360ab278cc1ac3e1235e0755e4cba1f918e6f3c Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 13 Jul 2018 09:15:39 +0200 Subject: [PATCH 2289/2380] qemu-img: avoid overflow of min_sparse parameter the min_sparse convert parameter can overflow (e.g. -S 1024G) in the conversion from int64_t to int resulting in a negative min_sparse parameter. Avoid this by limiting the valid parameters to sane values. In fact anything exceeding the convert buffer size is also pointless. While at it also forbid values that are non multiple of 512 to avoid undesired behaviour. For instance, values between 1 and 511 were legal, but resulted in full allocation. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Lieven Signed-off-by: Kevin Wolf --- qemu-img.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 4a7ce43dc9..9b7506b8ae 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -2005,6 +2005,8 @@ static int convert_do_copy(ImgConvertState *s) return s->ret; } +#define MAX_BUF_SECTORS 32768 + static int img_convert(int argc, char **argv) { int c, bs_i, flags, src_flags = 0; @@ -2100,8 +2102,12 @@ static int img_convert(int argc, char **argv) int64_t sval; sval = cvtnum(optarg); - if (sval < 0) { - error_report("Invalid minimum zero buffer size for sparse output specified"); + if (sval < 0 || sval & (BDRV_SECTOR_SIZE - 1) || + sval / BDRV_SECTOR_SIZE > MAX_BUF_SECTORS) { + error_report("Invalid buffer size for sparse output specified. " + "Valid sizes are multiples of %llu up to %llu. Select " + "0 to disable sparse detection (fully allocates output).", + BDRV_SECTOR_SIZE, MAX_BUF_SECTORS * BDRV_SECTOR_SIZE); goto fail_getopt; } @@ -2385,9 +2391,9 @@ static int img_convert(int argc, char **argv) } /* increase bufsectors from the default 4096 (2M) if opt_transfer - * or discard_alignment of the out_bs is greater. Limit to 32768 (16MB) - * as maximum. */ - s.buf_sectors = MIN(32768, + * or discard_alignment of the out_bs is greater. Limit to + * MAX_BUF_SECTORS as maximum which is currently 32768 (16MB). */ + s.buf_sectors = MIN(MAX_BUF_SECTORS, MAX(s.buf_sectors, MAX(out_bs->bl.opt_transfer >> BDRV_SECTOR_BITS, out_bs->bl.pdiscard_alignment >> From e05eb9f29b762cd33d38b712c8c26a432dacf45c Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 13 Jul 2018 13:37:21 -0400 Subject: [PATCH 2290/2380] iotests: remove LUKS support from test 226 This test doesn't actually care about the format anyway, it just supports "all formats" as a convenience. LUKS however does not use a simple image filename which confuses this iotest. We can simply skip the test for formats that use IMGOPTSSYNTAX for their filenames without missing much coverage. Signed-off-by: John Snow Signed-off-by: Kevin Wolf --- tests/qemu-iotests/226 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/qemu-iotests/226 b/tests/qemu-iotests/226 index 460aea2fc9..34987d43f9 100755 --- a/tests/qemu-iotests/226 +++ b/tests/qemu-iotests/226 @@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Generic format, but tests file-protocol specific error handling _supported_fmt generic +if [ "$IMGOPTSSYNTAX" = "true" ]; then + _unsupported_fmt $IMGFMT +fi _supported_proto file _supported_os Linux From d6e4ca902148f33cfaf117396f57c7fff7c635f0 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 13 Jul 2018 21:41:00 +0200 Subject: [PATCH 2291/2380] iotest: Fix filtering order in 226 The test directory should be filtered before the image format, otherwise the test will fail if the image format is part of the test directory, like so: [...] -can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory +can't open: Could not open '/tmp/test-IMGFMT/t.IMGFMT': Is a directory [...] Signed-off-by: Max Reitz Reviewed-by: John Snow Signed-off-by: Kevin Wolf --- tests/qemu-iotests/226 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/226 b/tests/qemu-iotests/226 index 34987d43f9..211ea9888a 100755 --- a/tests/qemu-iotests/226 +++ b/tests/qemu-iotests/226 @@ -55,10 +55,10 @@ for PROTO in "file" "host_device" "host_cdrom"; do echo "=== Testing with driver:$PROTO ===" echo echo "== Testing RO ==" - $QEMU_IO -c "open -r -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_imgfmt | _filter_testdir + $QEMU_IO -c "open -r -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt $QEMU_IO -c "open -r -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt echo "== Testing RW ==" - $QEMU_IO -c "open -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_imgfmt | _filter_testdir + $QEMU_IO -c "open -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt $QEMU_IO -c "open -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt done From 092b9c408fb22010747f17e2fb19521cfafd45d6 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 13 Jul 2018 21:15:06 +0200 Subject: [PATCH 2292/2380] iotests: Disallow compat=0.10 in 223 223 tests persistent dirty bitmaps which are not supported in compat=0.10, so that option is unsupported for this test. Signed-off-by: Max Reitz Tested-by: John Snow Reviewed-by: John Snow Signed-off-by: Kevin Wolf --- tests/qemu-iotests/223 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 index b63b7a4f9e..8b1859c2dd 100755 --- a/tests/qemu-iotests/223 +++ b/tests/qemu-iotests/223 @@ -40,6 +40,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file # uses NBD as well _supported_os Linux +# Persistent dirty bitmaps require compat=1.1 +_unsupported_imgopts 'compat=0.10' function do_run_qemu() { From 3e31b4e17064d22e533071aaa57d3f01499ef910 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 18 Jul 2018 17:08:29 +0200 Subject: [PATCH 2293/2380] block/vvfat: Disable debug message by default It's annoying to see this debug message every time you use vvfat. Disable it with the DLOG() macro by default, as it is done with the other debug messages in this file. Signed-off-by: Thomas Huth Reviewed-by: John Snow Signed-off-by: Kevin Wolf --- block/vvfat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/vvfat.c b/block/vvfat.c index c7d2ed2d96..fc41841a5c 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1245,8 +1245,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, s->fat2 = NULL; s->downcase_short_names = 1; - fprintf(stderr, "vvfat %s chs %d,%d,%d\n", - dirname, cyls, heads, secs); + DLOG(fprintf(stderr, "vvfat %s chs %d,%d,%d\n", + dirname, cyls, heads, secs)); s->sector_count = cyls * heads * secs - s->offset_to_bootsector; From 5f00335aecafc9ad56592d943619d3252f8941f1 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 17 Jul 2018 16:40:10 -0300 Subject: [PATCH 2294/2380] i386: Rename enum CacheType members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename DCACHE to DATA_CACHE and ICACHE to INSTRUCTION_CACHE. This avoids conflict with Linux asm/cachectl.h macros and fixes build failure on mips hosts. Reported-by: Philippe Mathieu-Daudé Signed-off-by: Eduardo Habkost Message-Id: <20180717194010.30096-1-ehabkost@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Acked-by: Aleksandar Markovic Reviewed-by: Babu Moger Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 128 +++++++++++++++++++++++----------------------- target/i386/cpu.h | 4 +- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index e0e2f2eea1..f454d4beb3 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -71,123 +71,123 @@ struct CPUID2CacheDescriptorInfo { * From Intel SDM Volume 2A, CPUID instruction */ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { - [0x06] = { .level = 1, .type = ICACHE, .size = 8 * KiB, + [0x06] = { .level = 1, .type = INSTRUCTION_CACHE, .size = 8 * KiB, .associativity = 4, .line_size = 32, }, - [0x08] = { .level = 1, .type = ICACHE, .size = 16 * KiB, + [0x08] = { .level = 1, .type = INSTRUCTION_CACHE, .size = 16 * KiB, .associativity = 4, .line_size = 32, }, - [0x09] = { .level = 1, .type = ICACHE, .size = 32 * KiB, + [0x09] = { .level = 1, .type = INSTRUCTION_CACHE, .size = 32 * KiB, .associativity = 4, .line_size = 64, }, - [0x0A] = { .level = 1, .type = DCACHE, .size = 8 * KiB, + [0x0A] = { .level = 1, .type = DATA_CACHE, .size = 8 * KiB, .associativity = 2, .line_size = 32, }, - [0x0C] = { .level = 1, .type = DCACHE, .size = 16 * KiB, + [0x0C] = { .level = 1, .type = DATA_CACHE, .size = 16 * KiB, .associativity = 4, .line_size = 32, }, - [0x0D] = { .level = 1, .type = DCACHE, .size = 16 * KiB, + [0x0D] = { .level = 1, .type = DATA_CACHE, .size = 16 * KiB, .associativity = 4, .line_size = 64, }, - [0x0E] = { .level = 1, .type = DCACHE, .size = 24 * KiB, + [0x0E] = { .level = 1, .type = DATA_CACHE, .size = 24 * KiB, .associativity = 6, .line_size = 64, }, - [0x1D] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, + [0x1D] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, .associativity = 2, .line_size = 64, }, - [0x21] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, + [0x21] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, .associativity = 8, .line_size = 64, }, /* lines per sector is not supported cpuid2_cache_descriptor(), * so descriptors 0x22, 0x23 are not included */ - [0x24] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + [0x24] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 16, .line_size = 64, }, /* lines per sector is not supported cpuid2_cache_descriptor(), * so descriptors 0x25, 0x20 are not included */ - [0x2C] = { .level = 1, .type = DCACHE, .size = 32 * KiB, + [0x2C] = { .level = 1, .type = DATA_CACHE, .size = 32 * KiB, .associativity = 8, .line_size = 64, }, - [0x30] = { .level = 1, .type = ICACHE, .size = 32 * KiB, + [0x30] = { .level = 1, .type = INSTRUCTION_CACHE, .size = 32 * KiB, .associativity = 8, .line_size = 64, }, - [0x41] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, + [0x41] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, .associativity = 4, .line_size = 32, }, - [0x42] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, + [0x42] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, .associativity = 4, .line_size = 32, }, - [0x43] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + [0x43] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, .associativity = 4, .line_size = 32, }, - [0x44] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + [0x44] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 4, .line_size = 32, }, - [0x45] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, + [0x45] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, .associativity = 4, .line_size = 32, }, - [0x46] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, + [0x46] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, .associativity = 4, .line_size = 64, }, - [0x47] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, + [0x47] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, .associativity = 8, .line_size = 64, }, - [0x48] = { .level = 2, .type = UNIFIED_CACHE, .size = 3 * MiB, + [0x48] = { .level = 2, .type = UNIFIED_CACHE, .size = 3 * MiB, .associativity = 12, .line_size = 64, }, /* Descriptor 0x49 depends on CPU family/model, so it is not included */ - [0x4A] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, + [0x4A] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, .associativity = 12, .line_size = 64, }, - [0x4B] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, + [0x4B] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, .associativity = 16, .line_size = 64, }, - [0x4C] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB, + [0x4C] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB, .associativity = 12, .line_size = 64, }, - [0x4D] = { .level = 3, .type = UNIFIED_CACHE, .size = 16 * MiB, + [0x4D] = { .level = 3, .type = UNIFIED_CACHE, .size = 16 * MiB, .associativity = 16, .line_size = 64, }, - [0x4E] = { .level = 2, .type = UNIFIED_CACHE, .size = 6 * MiB, + [0x4E] = { .level = 2, .type = UNIFIED_CACHE, .size = 6 * MiB, .associativity = 24, .line_size = 64, }, - [0x60] = { .level = 1, .type = DCACHE, .size = 16 * KiB, + [0x60] = { .level = 1, .type = DATA_CACHE, .size = 16 * KiB, .associativity = 8, .line_size = 64, }, - [0x66] = { .level = 1, .type = DCACHE, .size = 8 * KiB, + [0x66] = { .level = 1, .type = DATA_CACHE, .size = 8 * KiB, .associativity = 4, .line_size = 64, }, - [0x67] = { .level = 1, .type = DCACHE, .size = 16 * KiB, + [0x67] = { .level = 1, .type = DATA_CACHE, .size = 16 * KiB, .associativity = 4, .line_size = 64, }, - [0x68] = { .level = 1, .type = DCACHE, .size = 32 * KiB, + [0x68] = { .level = 1, .type = DATA_CACHE, .size = 32 * KiB, .associativity = 4, .line_size = 64, }, - [0x78] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + [0x78] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 4, .line_size = 64, }, /* lines per sector is not supported cpuid2_cache_descriptor(), * so descriptors 0x79, 0x7A, 0x7B, 0x7C are not included. */ - [0x7D] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, + [0x7D] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, .associativity = 8, .line_size = 64, }, - [0x7F] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + [0x7F] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, .associativity = 2, .line_size = 64, }, - [0x80] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + [0x80] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, .associativity = 8, .line_size = 64, }, - [0x82] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, + [0x82] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, .associativity = 8, .line_size = 32, }, - [0x83] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + [0x83] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, .associativity = 8, .line_size = 32, }, - [0x84] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + [0x84] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 8, .line_size = 32, }, - [0x85] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, + [0x85] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, .associativity = 8, .line_size = 32, }, - [0x86] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + [0x86] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, .associativity = 4, .line_size = 64, }, - [0x87] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + [0x87] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 8, .line_size = 64, }, - [0xD0] = { .level = 3, .type = UNIFIED_CACHE, .size = 512 * KiB, + [0xD0] = { .level = 3, .type = UNIFIED_CACHE, .size = 512 * KiB, .associativity = 4, .line_size = 64, }, - [0xD1] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB, + [0xD1] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 4, .line_size = 64, }, - [0xD2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, + [0xD2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, .associativity = 4, .line_size = 64, }, - [0xD6] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB, + [0xD6] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 8, .line_size = 64, }, - [0xD7] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, + [0xD7] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, .associativity = 8, .line_size = 64, }, - [0xD8] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, + [0xD8] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, .associativity = 8, .line_size = 64, }, - [0xDC] = { .level = 3, .type = UNIFIED_CACHE, .size = 1.5 * MiB, + [0xDC] = { .level = 3, .type = UNIFIED_CACHE, .size = 1.5 * MiB, .associativity = 12, .line_size = 64, }, - [0xDD] = { .level = 3, .type = UNIFIED_CACHE, .size = 3 * MiB, + [0xDD] = { .level = 3, .type = UNIFIED_CACHE, .size = 3 * MiB, .associativity = 12, .line_size = 64, }, - [0xDE] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, + [0xDE] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, .associativity = 12, .line_size = 64, }, - [0xE2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, + [0xE2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, .associativity = 16, .line_size = 64, }, - [0xE3] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, + [0xE3] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, .associativity = 16, .line_size = 64, }, - [0xE4] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, + [0xE4] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, .associativity = 16, .line_size = 64, }, - [0xEA] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB, + [0xEA] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB, .associativity = 24, .line_size = 64, }, - [0xEB] = { .level = 3, .type = UNIFIED_CACHE, .size = 18 * MiB, + [0xEB] = { .level = 3, .type = UNIFIED_CACHE, .size = 18 * MiB, .associativity = 24, .line_size = 64, }, - [0xEC] = { .level = 3, .type = UNIFIED_CACHE, .size = 24 * MiB, + [0xEC] = { .level = 3, .type = UNIFIED_CACHE, .size = 24 * MiB, .associativity = 24, .line_size = 64, }, }; @@ -238,10 +238,10 @@ static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache) #define CACHE_COMPLEX_IDX (1 << 2) /* Encode CacheType for CPUID[4].EAX */ -#define CACHE_TYPE(t) (((t) == DCACHE) ? CACHE_TYPE_D : \ - ((t) == ICACHE) ? CACHE_TYPE_I : \ - ((t) == UNIFIED_CACHE) ? CACHE_TYPE_UNIFIED : \ - 0 /* Invalid value */) +#define CACHE_TYPE(t) (((t) == DATA_CACHE) ? CACHE_TYPE_D : \ + ((t) == INSTRUCTION_CACHE) ? CACHE_TYPE_I : \ + ((t) == UNIFIED_CACHE) ? CACHE_TYPE_UNIFIED : \ + 0 /* Invalid value */) /* Encode cache info for CPUID[4] */ @@ -538,7 +538,7 @@ static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, /* L1 data cache: */ static CPUCacheInfo legacy_l1d_cache = { - .type = DCACHE, + .type = DATA_CACHE, .level = 1, .size = 32 * KiB, .self_init = 1, @@ -551,7 +551,7 @@ static CPUCacheInfo legacy_l1d_cache = { /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ static CPUCacheInfo legacy_l1d_cache_amd = { - .type = DCACHE, + .type = DATA_CACHE, .level = 1, .size = 64 * KiB, .self_init = 1, @@ -565,7 +565,7 @@ static CPUCacheInfo legacy_l1d_cache_amd = { /* L1 instruction cache: */ static CPUCacheInfo legacy_l1i_cache = { - .type = ICACHE, + .type = INSTRUCTION_CACHE, .level = 1, .size = 32 * KiB, .self_init = 1, @@ -578,7 +578,7 @@ static CPUCacheInfo legacy_l1i_cache = { /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ static CPUCacheInfo legacy_l1i_cache_amd = { - .type = ICACHE, + .type = INSTRUCTION_CACHE, .level = 1, .size = 64 * KiB, .self_init = 1, @@ -1310,7 +1310,7 @@ struct X86CPUDefinition { static CPUCaches epyc_cache_info = { .l1d_cache = &(CPUCacheInfo) { - .type = DCACHE, + .type = DATA_CACHE, .level = 1, .size = 32 * KiB, .line_size = 64, @@ -1322,7 +1322,7 @@ static CPUCaches epyc_cache_info = { .no_invd_sharing = true, }, .l1i_cache = &(CPUCacheInfo) { - .type = ICACHE, + .type = INSTRUCTION_CACHE, .level = 1, .size = 64 * KiB, .line_size = 64, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 2c5a0d90a6..194e2e6b92 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1050,8 +1050,8 @@ typedef enum TPRAccess { /* Cache information data structures: */ enum CacheType { - DCACHE, - ICACHE, + DATA_CACHE, + INSTRUCTION_CACHE, UNIFIED_CACHE }; From 672189cd586ea38a2c1d8ab91eb1f9dcff5ceb05 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 22 Jul 2018 15:28:02 -0700 Subject: [PATCH 2295/2380] tcg/i386: Mark xmm registers call-clobbered When host vector registers and operations were introduced, I failed to mark the registers call clobbered as required by the ABI. Fixes: 770c2fc7bb7 Cc: qemu-stable@nongnu.org Reported-by: Jason A. Donenfeld Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.inc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index e87b0d445e..a91e4f1313 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -3532,7 +3532,7 @@ static void tcg_target_init(TCGContext *s) tcg_target_available_regs[TCG_TYPE_V256] = ALL_VECTOR_REGS; } - tcg_target_call_clobber_regs = 0; + tcg_target_call_clobber_regs = ALL_VECTOR_REGS; tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EAX); tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EDX); tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_ECX); From c5840b905e3e471833b277a27910f577d0e30d59 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Sun, 24 Jun 2018 15:45:40 +0300 Subject: [PATCH 2296/2380] qga-win: Handle fstrim for OSes lower than Win8 The defrag.exe tool which is used for executing the fstrim command on Windows doesn't support retrim for OSes lower than Win8. This commit handles this case and returns a suitable error. Output of fstrim before this commit: {"execute":"guest-fstrim"} {"return": {"paths": [{"path": "C:\\", "error": "An invalid command line option was specified. (0x89000008)"}, {"path": "F:\\", "error": "An invalid command line option was specified. (0x89000008)"}, {"path": "S:\\", "error": "An invalid command line option was specified. (0x89000008)"}]}} Reported on: https://bugzilla.redhat.com/show_bug.cgi?id=1594113 Signed-off-by: Sameeh Jubran * use alternative version query code proposed by Sameeh * fix up version check logic * avoid CamelCase variable names when possible Signed-off-by: Michael Roth --- qga/commands-win32.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 318d760a74..98d9735389 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -865,6 +865,19 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) GuestFilesystemTrimResponse *resp; HANDLE handle; WCHAR guid[MAX_PATH] = L""; + OSVERSIONINFO osvi; + BOOL win8_or_later; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + win8_or_later = (osvi.dwMajorVersion > 6 || + ((osvi.dwMajorVersion == 6) && + (osvi.dwMinorVersion >= 2))); + if (!win8_or_later) { + error_setg(errp, "fstrim is only supported for Win8+"); + return NULL; + } handle = FindFirstVolumeW(guid, ARRAYSIZE(guid)); if (handle == INVALID_HANDLE_VALUE) { From ae7da1e5f658ea21d96e565514de20ff2cf24fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 19 Jul 2018 20:40:59 +0200 Subject: [PATCH 2297/2380] qga: process_event() simplification and leak fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit json_parser_parse_err() may return something else than a QDict, in which case we loose the object. Let's keep track of the original object to avoid leaks. When an error occurs, "qdict" contains the response, but we still check the "execute" key there. Untangle a bit this code, by having a clear error path. CC: Michael Roth Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth --- qga/main.c | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/qga/main.c b/qga/main.c index 537cc0e162..87372d40ef 100644 --- a/qga/main.c +++ b/qga/main.c @@ -600,42 +600,42 @@ static void process_command(GAState *s, QDict *req) static void process_event(JSONMessageParser *parser, GQueue *tokens) { GAState *s = container_of(parser, GAState, parser); - QDict *qdict; + QObject *obj; + QDict *req, *rsp; Error *err = NULL; int ret; g_assert(s && parser); g_debug("process_event: called"); - qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err)); - if (err || !qdict) { - qobject_unref(qdict); - if (!err) { - g_warning("failed to parse event: unknown error"); - error_setg(&err, QERR_JSON_PARSING); - } else { - g_warning("failed to parse event: %s", error_get_pretty(err)); - } - qdict = qmp_error_response(err); + obj = json_parser_parse_err(tokens, NULL, &err); + if (err) { + goto err; + } + req = qobject_to(QDict, obj); + if (!req) { + error_setg(&err, QERR_JSON_PARSING); + goto err; + } + if (!qdict_haskey(req, "execute")) { + g_warning("unrecognized payload format"); + error_setg(&err, QERR_UNSUPPORTED); + goto err; } - /* handle host->guest commands */ - if (qdict_haskey(qdict, "execute")) { - process_command(s, qdict); - } else { - if (!qdict_haskey(qdict, "error")) { - qobject_unref(qdict); - g_warning("unrecognized payload format"); - error_setg(&err, QERR_UNSUPPORTED); - qdict = qmp_error_response(err); - } - ret = send_response(s, qdict); - if (ret < 0) { - g_warning("error sending error response: %s", strerror(-ret)); - } - } + process_command(s, req); + qobject_unref(obj); + return; - qobject_unref(qdict); +err: + g_warning("failed to parse event: %s", error_get_pretty(err)); + rsp = qmp_error_response(err); + ret = send_response(s, rsp); + if (ret < 0) { + g_warning("error sending error response: %s", strerror(-ret)); + } + qobject_unref(rsp); + qobject_unref(obj); } /* false return signals GAChannel to close the current client connection */ From 0c6c43955421764998eb0dd1863ee8b38a3ff1c1 Mon Sep 17 00:00:00 2001 From: John Arbuckle Date: Mon, 2 Jul 2018 22:00:17 -0400 Subject: [PATCH 2298/2380] ui/cocoa.m: prevent stuck command key when going into full screen mode When the user pushes Command-F in QEMU while the mouse is ungrabbed, QEMU goes into full screen mode. When the user finally releases the command key, it is sent to the guest as an event. The makes the guest operating system think the command key is down when it is really up. To prevent this situation from happening, we simply drop the first command key event after the user has gone into full screen mode using Command-F. Signed-off-by: John Arbuckle Message-id: 20180703020017.1032-1-programmingkidx@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- ui/cocoa.m | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index cfc70e21a4..ecf12bfc2e 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -637,6 +637,7 @@ QemuCocoaView *cocoaView; int buttons = 0; int keycode = 0; bool mouse_event = false; + static bool switched_to_fullscreen = false; NSPoint p = [event locationInWindow]; switch ([event type]) { @@ -681,7 +682,11 @@ QemuCocoaView *cocoaView; keycode == Q_KEY_CODE_NUM_LOCK) { [self toggleStatefulModifier:keycode]; } else if (qemu_console_is_graphic(NULL)) { - [self toggleModifier:keycode]; + if (switched_to_fullscreen) { + switched_to_fullscreen = false; + } else { + [self toggleModifier:keycode]; + } } } @@ -691,6 +696,13 @@ QemuCocoaView *cocoaView; // forward command key combos to the host UI unless the mouse is grabbed if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) { + /* + * Prevent the command key from being stuck down in the guest + * when using Command-F to switch to full screen mode. + */ + if (keycode == Q_KEY_CODE_F) { + switched_to_fullscreen = true; + } [NSApp sendEvent:event]; return; } From 042374c92e83da5c8f906b9b97814a21eac2a09f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 23 Jul 2018 19:03:37 +0100 Subject: [PATCH 2299/2380] hw/intc/arm_gicv3: Check correct HCR_EL2 bit when routing IRQ In icc_dir_write() we were incorrectly checking HCR_EL2.FMO when determining whether IRQ should be routed to EL2; this should be HCR_EL2.IMO (compare the GICv3 pseudocode ICC_DIR_EL1[]). Use the correct mask. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180723180337.17378-1-peter.maydell@linaro.org --- hw/intc/arm_gicv3_cpuif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 5c89be1af0..2a60568d82 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -1550,7 +1550,7 @@ static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, * tested in cases where we know !IsSecure is true. */ route_fiq_to_el2 = env->cp15.hcr_el2 & HCR_FMO; - route_irq_to_el2 = env->cp15.hcr_el2 & HCR_FMO; + route_irq_to_el2 = env->cp15.hcr_el2 & HCR_IMO; switch (arm_current_el(env)) { case 3: From 1a5182c0d2eaa8dc8e7dbd914fba8d07d5595fe0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 23 Jul 2018 13:34:57 +0100 Subject: [PATCH 2300/2380] target/arm: Escalate to correct HardFault when AIRCR.BFHFNMINS is set When we escalate a v8M exception to HardFault, if AIRCR.BFHFNMINNS is set then we need to decide whether it should become a secure HardFault or a nonsecure HardFault. We should always escalate to the same target security state as the original exception. The current code tries to test this using the 'secure' bool, which is not right because that flag indicates whether the target security state only for banked exceptions; the effect was that we were incorrectly escalating always-secure exceptions like SecureFault to a nonsecure HardFault. Fix this by defining, logging and using a new 'targets_secure' bool which tracks the condition we actually want. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180723123457.2038-1-peter.maydell@linaro.org --- hw/intc/armv7m_nvic.c | 8 ++++++-- hw/intc/trace-events | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 7a5330f201..6be7fc5266 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -525,13 +525,17 @@ static void do_armv7m_nvic_set_pending(void *opaque, int irq, bool secure, NVICState *s = (NVICState *)opaque; bool banked = exc_is_banked(irq); VecInfo *vec; + bool targets_secure; assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); assert(!secure || banked); vec = (banked && secure) ? &s->sec_vectors[irq] : &s->vectors[irq]; - trace_nvic_set_pending(irq, secure, derived, vec->enabled, vec->prio); + targets_secure = banked ? secure : exc_targets_secure(s, irq); + + trace_nvic_set_pending(irq, secure, targets_secure, + derived, vec->enabled, vec->prio); if (derived) { /* Derived exceptions are always synchronous. */ @@ -611,7 +615,7 @@ static void do_armv7m_nvic_set_pending(void *opaque, int irq, bool secure, */ irq = ARMV7M_EXCP_HARD; if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY) && - (secure || + (targets_secure || !(s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK))) { vec = &s->sec_vectors[irq]; } else { diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 55e8c2570c..5fb18e65c9 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -177,7 +177,7 @@ nvic_set_prio(int irq, bool secure, uint8_t prio) "NVIC set irq %d secure-bank % nvic_irq_update(int vectpending, int pendprio, int exception_prio, int level) "NVIC vectpending %d pending prio %d exception_prio %d: setting irq line to %d" nvic_escalate_prio(int irq, int irqprio, int runprio) "NVIC escalating irq %d to HardFault: insufficient priority %d >= %d" nvic_escalate_disabled(int irq) "NVIC escalating irq %d to HardFault: disabled" -nvic_set_pending(int irq, bool secure, bool derived, int en, int prio) "NVIC set pending irq %d secure-bank %d derived %d (enabled: %d priority %d)" +nvic_set_pending(int irq, bool secure, bool targets_secure, bool derived, int en, int prio) "NVIC set pending irq %d secure-bank %d targets_secure %d derived %d (enabled: %d priority %d)" nvic_clear_pending(int irq, bool secure, int en, int prio) "NVIC clear pending irq %d secure-bank %d (enabled: %d priority %d)" nvic_set_pending_level(int irq) "NVIC set pending: irq %d higher prio than vectpending: setting irq line to 1" nvic_acknowledge_irq(int irq, int prio) "NVIC acknowledge IRQ: %d now active (prio %d)" From 1818d50179b314a833d0d8881a4b36001cc8e189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 10:54:16 +0100 Subject: [PATCH 2301/2380] tests/.gitignore: don't ignore docker tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .gitignore was being a little over enthusiastic hiding files. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/.gitignore b/tests/.gitignore index 08e2df1ce1..72c18aaab0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -9,6 +9,7 @@ qht-bench rcutorture test-* !test-*.c +!docker/test-* test-qapi-commands.[ch] test-qapi-events.[ch] test-qapi-types.[ch] From e043d7321236d753dca7b27779fbed59c5e6ce5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 12:20:19 +0100 Subject: [PATCH 2302/2380] docker: base debian-tricore on qemu:debian9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need both git and a working compiler to build the tools. Although the qemu:debian9 image also has a bunch of extra dependencies it would be fairly unusual for a user not to already have this layer available for one of our many other docker images so lets not complicate things. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/debian-tricore-cross.docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index 898b8dd511..180ca646c8 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -7,7 +7,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later # -FROM debian:9 +FROM qemu:debian9 MAINTAINER Philippe Mathieu-Daudé From be5e1f527abb26414a3b67882669b15649ade593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Jul 2018 16:02:31 +0100 Subject: [PATCH 2303/2380] docker: par down QEMU_CONFIGURE_OPTS in debian-tricore-cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This image isn't going to build anything significant as it is just intended for building test cases. In case it does end up getting inadvertently included in a build lets aim for the minimal possible product. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/debian-tricore-cross.docker | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index 180ca646c8..4a0f7706a3 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -19,5 +19,5 @@ RUN git clone --single-branch \ make && make install && \ rm -rf /usr/src/binutils -# Specify the cross prefix for this image (see tests/docker/common.rc) -ENV QEMU_CONFIGURE_OPTS --cross-prefix=tricore- +# This image isn't designed for building QEMU but building tests +ENV QEMU_CONFIGURE_OPTS --disable-system --disable-user From 43e1b2ffec8cf1741f1ccae56503803d9b8c38d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 14:08:25 +0100 Subject: [PATCH 2304/2380] docker: fail more gracefully on docker.py check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As this is called directly from the Makefile while determining dependencies and it is possible the user was configured in one window but not have credentials in the other. Let's catch the Exceptions and deal with it quietly. Signed-off-by: Alex Bennée Reported-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/docker.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 69e7130db7..2f81c6b13b 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -479,7 +479,12 @@ class CheckCommand(SubCommand): def run(self, args, argv): tag = args.tag - dkr = Docker() + try: + dkr = Docker() + except: + print("Docker not set up") + return 1 + info = dkr.inspect_tag(tag) if info is None: print("Image does not exist") From e4ce964d94ee8648c85cbdbe149156d29a4ee53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 10:58:34 +0100 Subject: [PATCH 2305/2380] docker: split configure_qemu from build_qemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows some tests that just want to configure QEMU's source tree to do so. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/common.rc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index 046f8a5921..ba1f942328 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -21,7 +21,7 @@ requires() done } -build_qemu() +configure_qemu() { config_opts="--enable-werror \ ${TARGET_LIST:+--target-list=${TARGET_LIST}} \ @@ -32,6 +32,11 @@ build_qemu() echo $config_opts $QEMU_SRC/configure $config_opts || \ { cat config.log && test_fail "Failed to run 'configure'"; } +} + +build_qemu() +{ + configure_qemu $@ make $MAKEFLAGS } From 3f9747a73891e50d9c09d2fc3dbba6f1fdd03a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 13:27:54 +0100 Subject: [PATCH 2306/2380] docker: move make check into check_qemu helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all docker images can run the check step. Let's move everything into a common helper so we don't need to replicate checks in the future. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/common.rc | 11 +++++++++++ tests/docker/test-clang | 2 +- tests/docker/test-debug | 2 +- tests/docker/test-full | 2 +- tests/docker/test-quick | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index ba1f942328..4ff5974016 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -40,6 +40,17 @@ build_qemu() make $MAKEFLAGS } +check_qemu() +{ + # default to make check unless the caller specifies + if test -z "$@"; then + INVOCATION="check" + else + INVOCATION="$@" + fi + make $MAKEFLAGS $INVOCATION +} + test_fail() { echo "$@" diff --git a/tests/docker/test-clang b/tests/docker/test-clang index e90a793178..324e341cea 100755 --- a/tests/docker/test-clang +++ b/tests/docker/test-clang @@ -23,5 +23,5 @@ OPTS="--cxx=clang++ --cc=clang --host-cc=clang" #OPTS="$OPTS --extra-cflags=-fsanitize=undefined \ #--extra-cflags=-fno-sanitize=float-divide-by-zero" build_qemu $OPTS -make $MAKEFLAGS check +check_qemu install_qemu diff --git a/tests/docker/test-debug b/tests/docker/test-debug index d3f9f70d01..137f4f2ddc 100755 --- a/tests/docker/test-debug +++ b/tests/docker/test-debug @@ -22,5 +22,5 @@ OPTS="--cxx=clang++ --cc=clang --host-cc=clang" OPTS="--enable-debug --enable-sanitizers $OPTS" build_qemu $OPTS -make $MAKEFLAGS V=1 check +check_qemu check V=1 install_qemu diff --git a/tests/docker/test-full b/tests/docker/test-full index b4e42d25d7..aadc0f00a2 100755 --- a/tests/docker/test-full +++ b/tests/docker/test-full @@ -15,4 +15,4 @@ cd "$BUILD_DIR" -build_qemu && make check $MAKEFLAGS && install_qemu +build_qemu && check_qemu && install_qemu diff --git a/tests/docker/test-quick b/tests/docker/test-quick index 3b7bce6105..eee59c55fb 100755 --- a/tests/docker/test-quick +++ b/tests/docker/test-quick @@ -18,5 +18,5 @@ cd "$BUILD_DIR" DEF_TARGET_LIST="x86_64-softmmu,aarch64-softmmu" TARGET_LIST=${TARGET_LIST:-$DEF_TARGET_LIST} \ build_qemu -make check $MAKEFLAGS +check_qemu install_qemu From 56c115a953a17975ce277b719448e02053906124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 14:27:47 +0100 Subject: [PATCH 2307/2380] docker: gracefully skip check_qemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all our images are able to run the tests. Rather than use features we can just check for the existence and run-ability of gtester. If the image has been setup for binfmt_misc it will be able to run anyway. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/common.rc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index 4ff5974016..4011561587 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -48,7 +48,13 @@ check_qemu() else INVOCATION="$@" fi - make $MAKEFLAGS $INVOCATION + + if command -v gtester > /dev/null 2>&1 && \ + gtester --version > /dev/null 2>&1; then + make $MAKEFLAGS $INVOCATION + else + echo "No working gtester, skipping make $INVOCATION" + fi } test_fail() From 77b08f73c86d2f0f2d3c8e52c568543363658ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 11:51:57 +0100 Subject: [PATCH 2308/2380] docker: Makefile.include don't include partial images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename DOCKER_INTERMEDIATE_IMAGES to DOCKER_PARTIAL_IMAGES and add the incomplete cross compiler images that can build tests but can't build QEMU itself. We also add debian, debian-bootstrap and the tricode images to the list. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index b2a7e761cc..09fb7db7fa 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -6,7 +6,7 @@ DOCKER_SUFFIX := .docker DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles DOCKER_DEPRECATED_IMAGES := debian # we don't run tests on intermediate images (used as base by another image) -DOCKER_INTERMEDIATE_IMAGES := debian8 debian9 debian8-mxe debian-ports debian-sid +DOCKER_PARTIAL_IMAGES := debian debian8 debian9 debian8-mxe debian-ports debian-sid debian-bootstrap DOCKER_IMAGES := $(filter-out $(DOCKER_DEPRECATED_IMAGES),$(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))))) DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES)) # Use a global constant ccache directory to speed up repetitive builds @@ -121,6 +121,11 @@ docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools docker-image-tricore-cross: docker-image-debian9 +# These images may be good enough for building tests but not for test builds +DOCKER_PARTIAL_IMAGES += debian-alpha-cross debian-hppa-cross debian-m68k-cross debian-sh4-cross +DOCKER_PARTIAL_IMAGES += debian-sparc64-cross debian-mips64-cross debian-riscv64-cross +DOCKER_PARTIAL_IMAGES += debian-tricore-cross debian-powerpc-cross fedora-i386-cross + # Rules for building linux-user powered images # # These are slower than using native cross compiler setups but can @@ -137,7 +142,7 @@ docker-image-debian-powerpc-user-cross: docker-binfmt-image-debian-powerpc-user DOCKER_USER_IMAGES += debian-powerpc-user # Expand all the pre-requistes for each docker image and test combination -$(foreach i,$(filter-out $(DOCKER_INTERMEDIATE_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES)), \ +$(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES)), \ $(foreach t,$(DOCKER_TESTS) $(DOCKER_TOOLS), \ $(eval .PHONY: docker-$t@$i) \ $(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \ From d984f24c7783a288ce8cc91978521c0c2dc66d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 10:55:34 +0100 Subject: [PATCH 2309/2380] docker: add test-unit runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test doesn't even build QEMU, it just builds and runs all the unit tests. Intended to make checking unit tests on all docker images easier. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/test-unit | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 tests/docker/test-unit diff --git a/tests/docker/test-unit b/tests/docker/test-unit new file mode 100755 index 0000000000..8905d01150 --- /dev/null +++ b/tests/docker/test-unit @@ -0,0 +1,21 @@ +#!/bin/bash -e +# +# Build and run the unit tests +# +# Copyright (c) 2018 Linaro Ltd. +# +# Authors: +# Alex Bennée +# +# 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. + +. common.rc + +cd "$BUILD_DIR" + +# although we are not building QEMU itself we still need a configured +# build for the unit tests to be built and run +configure_qemu +check_qemu check-unit From ff1a390296ee5a9791d4acf155be0bcf8d922f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 13:24:52 +0100 Subject: [PATCH 2310/2380] docker: add expansion for docker-test-FOO to Makefile.include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to run a particular test on all docker images. For example: make docker-test-unit Will run the unit tests on every supported image. At the same time rename docker-test to docker-all-tests to be clearer. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 09fb7db7fa..8fbb076396 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -148,7 +148,8 @@ $(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPR $(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \ ) \ $(foreach t,$(DOCKER_TESTS), \ - $(eval docker-test: docker-$t@$i) \ + $(eval docker-all-tests: docker-$t@$i) \ + $(eval docker-$t: docker-$t@$i) \ ) \ ) @@ -158,7 +159,8 @@ docker: @echo 'Available targets:' @echo @echo ' docker: Print this help.' - @echo ' docker-test: Run all image/test combinations.' + @echo ' docker-all-tests: Run all image/test combinations.' + @echo ' docker-TEST: Run TEST on all image combinations.' @echo ' docker-clean: Kill and remove residual docker testing containers.' @echo ' docker-TEST@IMAGE: Run "TEST" in container "IMAGE".' @echo ' Note: "TEST" is one of the listed test name,' From 27963460694cdaf5ad757d77e5ec35446b4e8f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 12 Jul 2018 10:35:54 +0100 Subject: [PATCH 2311/2380] docker: drop QEMU_TARGET check, fallback in EXECUTABLE not set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The addition of QEMU_TARGET was intended to ensure we fall back to checking for the existence of an image if the build system was not currently configured to build it. However this breaks the direct use of the rule for building custom binfmt_misc images. We already check for EXECUTABLE so let us just use that as a proxy for deciding if we are just going to check the image exits. Signed-off-by: Alex Bennée Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 8fbb076396..05afeb64a7 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -58,13 +58,11 @@ docker-image-%: $(DOCKER_FILES_DIR)/%.docker docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker $(if $(EXECUTABLE),,\ $(error EXECUTABLE not set, debootstrap of debian-$* would fail)) - $(if $(wildcard $(EXECUTABLE)),,\ - $(error Please build $(EXECUTABLE) first)) $(if $(DEB_ARCH),,\ $(error DEB_ARCH not set, debootstrap of debian-$* would fail)) $(if $(DEB_TYPE),,\ $(error DEB_TYPE not set, debootstrap of debian-$* would fail)) - $(if $(filter $(QEMU_TARGET),$(TARGET_DIRS)), \ + $(if $(wildcard $(EXECUTABLE)), \ $(call quiet-command, \ DEB_ARCH=$(DEB_ARCH) \ DEB_TYPE=$(DEB_TYPE) \ @@ -136,7 +134,6 @@ DOCKER_PARTIAL_IMAGES += debian-tricore-cross debian-powerpc-cross fedora-i386-c # broken so we need a qemu-linux-user for this target docker-binfmt-image-debian-powerpc-user: DEB_ARCH = powerpc docker-binfmt-image-debian-powerpc-user: DEB_TYPE = jessie -docker-binfmt-image-debian-powerpc-user: QEMU_TARGET = ppc-linux-user docker-binfmt-image-debian-powerpc-user: EXECUTABLE = ${BUILD_DIR}/ppc-linux-user/qemu-ppc docker-image-debian-powerpc-user-cross: docker-binfmt-image-debian-powerpc-user DOCKER_USER_IMAGES += debian-powerpc-user From d19f5fc0411b101798fdfc80b9680df85b316636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 12 Jul 2018 14:36:18 +0100 Subject: [PATCH 2312/2380] docker: report hint when docker.py check fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a check fails we currently just report why we failed. This is not totally helpful to people who want to boot-strap a new image. Report a hint as to why it failed. Signed-off-by: Alex Bennée Suggested-by: Fam Zheng --- tests/docker/Makefile.include | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 05afeb64a7..1aaa795743 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -73,7 +73,8 @@ docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)), \ "BUILD","binfmt debian-$* (debootstrapped)"), \ $(call quiet-command, \ - $(DOCKER_SCRIPT) check --quiet qemu:debian-$* $<, \ + $(DOCKER_SCRIPT) check --quiet qemu:debian-$* $< || \ + { echo "You will need to build $(EXECUTABLE)"; exit 1;},\ "CHECK", "debian-$* exists")) endif From 571fef5e07a2398bf14ee1affec3cdbc58088a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 3 Jul 2018 12:35:30 -0300 Subject: [PATCH 2313/2380] docker: Update debootstrap script after Debian migration from Alioth to Salsa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This silents the following warning: Cloning into './debootstrap.git'... warning: redirecting to https://salsa.debian.org/installer-team/debootstrap.git/ See https://lists.debian.org/debian-devel-announce/2018/01/msg00004.html Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée --- tests/docker/dockerfiles/debian-bootstrap.pre | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/debian-bootstrap.pre b/tests/docker/dockerfiles/debian-bootstrap.pre index 56e1aa7a21..ea324d6e4a 100755 --- a/tests/docker/dockerfiles/debian-bootstrap.pre +++ b/tests/docker/dockerfiles/debian-bootstrap.pre @@ -62,7 +62,7 @@ if [ -z $DEBOOTSTRAP_DIR ]; then NEED_DEBOOTSTRAP=true fi if $NEED_DEBOOTSTRAP; then - DEBOOTSTRAP_SOURCE=https://anonscm.debian.org/git/d-i/debootstrap.git + DEBOOTSTRAP_SOURCE=https://salsa.debian.org/installer-team/debootstrap.git git clone ${DEBOOTSTRAP_SOURCE} ./debootstrap.git export DEBOOTSTRAP_DIR=./debootstrap.git DEBOOTSTRAP=./debootstrap.git/debootstrap From 11cc24a126d3e370c7fc8e6c9be51138713616b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Jul 2018 11:56:49 +0100 Subject: [PATCH 2314/2380] docker: add commentary to debian-bootstrap.docker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is just a note that later versions of debootstrap don't technically need this hack. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/debian-bootstrap.docker | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/docker/dockerfiles/debian-bootstrap.docker b/tests/docker/dockerfiles/debian-bootstrap.docker index 14212b9cf4..e13c26a7ed 100644 --- a/tests/docker/dockerfiles/debian-bootstrap.docker +++ b/tests/docker/dockerfiles/debian-bootstrap.docker @@ -9,6 +9,7 @@ FROM scratch ADD . / # Patch all mounts as docker already has stuff set up +# (this is not needed for later debootstraps but is harmless atm) RUN sed -i 's/in_target mount/echo not for docker in_target mount/g' /debootstrap/functions # Run stage 2 From f7b446b5defeaa249a18e261879316f27d6c899c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Jul 2018 12:23:14 +0100 Subject: [PATCH 2315/2380] docker: ignore distro versioning of debootstrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do a minimum version check for the debootstrap but if the distro has added their own minor version tick it would fail and fall-back to the SCM version. This is sub-optimal as the latest/greatest version may be broken at any one particular time. We fix that with a little sed magic on the version string before passing to our ugly shell versioning check. Signed-off-by: Alex Bennée Tested-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/debian-bootstrap.pre | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/docker/dockerfiles/debian-bootstrap.pre b/tests/docker/dockerfiles/debian-bootstrap.pre index ea324d6e4a..3b0ef95374 100755 --- a/tests/docker/dockerfiles/debian-bootstrap.pre +++ b/tests/docker/dockerfiles/debian-bootstrap.pre @@ -56,10 +56,13 @@ if [ -z $DEBOOTSTRAP_DIR ]; then if [ -z $DEBOOTSTRAP ]; then echo "No debootstrap installed, attempting to install from SCM" NEED_DEBOOTSTRAP=true - elif ! (echo "${MIN_DEBOOTSTRAP_VERSION}" ; "${DEBOOTSTRAP}" --version \ - | cut -d ' ' -f 2) | sort -t . -n -k 1,1 -k 2,2 -k 3,3 -c &>/dev/null; then - echo "debootstrap too old, attempting to install from SCM" - NEED_DEBOOTSTRAP=true + else + INSTALLED_VERSION=$(${DEBOOTSTRAP} --version | sed 's/debootstrap \([0-9\.]*\)[^0-9\.]*.*/\1/') + if ! (echo "${MIN_DEBOOTSTRAP_VERSION}" ; echo "${INSTALLED_VERSION}") \ + | sort -t . -n -k 1,1 -k 2,2 -k 3,3 -C ; then + echo "debootstrap too old, attempting to install from SCM" + NEED_DEBOOTSTRAP=true + fi fi if $NEED_DEBOOTSTRAP; then DEBOOTSTRAP_SOURCE=https://salsa.debian.org/installer-team/debootstrap.git From 15352decf8fd834627d05fb619dd523f50101977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 17 Jul 2018 17:11:26 +0100 Subject: [PATCH 2316/2380] docker: perform basic binfmt_misc validation in docker.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting up binfmt_misc is outside of the scope of the docker.py script but we can at least validate it with any given executable so we have a more useful error message than the sed line of deboostrap failing cryptically. Signed-off-by: Alex Bennée Reported-by: Richard Henderson --- tests/docker/docker.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 2f81c6b13b..d3006d4dae 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -112,6 +112,31 @@ def _copy_binary_with_libs(src, dest_dir): so_path = os.path.dirname(l) _copy_with_mkdir(l , dest_dir, so_path) + +def _check_binfmt_misc(executable): + """Check binfmt_misc has entry for executable in the right place. + + The details of setting up binfmt_misc are outside the scope of + this script but we should at least fail early with a useful + message if it won't work.""" + + binary = os.path.basename(executable) + binfmt_entry = "/proc/sys/fs/binfmt_misc/%s" % (binary) + + if not os.path.exists(binfmt_entry): + print ("No binfmt_misc entry for %s" % (binary)) + return False + + with open(binfmt_entry) as x: entry = x.read() + + qpath = "/usr/bin/%s" % (binary) + if not re.search("interpreter %s\n" % (qpath), entry): + print ("binfmt_misc for %s does not point to %s" % (binary, qpath)) + return False + + return True + + def _read_qemu_dockerfile(img_name): # special case for Debian linux-user images if img_name.startswith("debian") and img_name.endswith("user"): @@ -315,6 +340,11 @@ class BuildCommand(SubCommand): # Create a docker context directory for the build docker_dir = tempfile.mkdtemp(prefix="docker_build") + # Validate binfmt_misc will work + if args.include_executable: + if not _check_binfmt_misc(args.include_executable): + return 1 + # Is there a .pre file to run in the build context? docker_pre = os.path.splitext(args.dockerfile)[0]+".pre" if os.path.exists(docker_pre): From 1a3bdc6111482fbb41eacacf850002e4589a81ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 17 Jul 2018 17:17:53 +0100 Subject: [PATCH 2317/2380] tests/tcg: remove runcom test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The combination of being rather esoteric and needing to support mmap @ 0 means this only ever worked under translation. It has now regressed even further and is no longer useful. Kill it. Signed-off-by: Alex Bennée Reviewed-by: Peter Maydell --- tests/tcg/i386/Makefile.target | 5 - tests/tcg/i386/README | 3 - tests/tcg/i386/pi_10.com | Bin 54 -> 0 bytes tests/tcg/i386/runcom.c | 192 --------------------------------- 4 files changed, 200 deletions(-) delete mode 100644 tests/tcg/i386/pi_10.com delete mode 100644 tests/tcg/i386/runcom.c diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index c1997a1624..b4033ba3d1 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -29,11 +29,6 @@ test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S test-i386.h test-i386 $(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ $( -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -extern int vm86 (unsigned long int subfunction, - struct vm86plus_struct *info); - -#define VIF_MASK 0x00080000 - -//#define SIGTEST - -#define COM_BASE_ADDR 0x10100 - -static void usage(void) -{ - printf("runcom version 0.1 (c) 2003 Fabrice Bellard\n" - "usage: runcom file.com\n" - "VM86 Run simple .com DOS executables (linux vm86 test mode)\n"); - exit(1); -} - -static inline void set_bit(uint8_t *a, unsigned int bit) -{ - a[bit / 8] |= (1 << (bit % 8)); -} - -static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) -{ - return (uint8_t *)((seg << 4) + (reg & 0xffff)); -} - -static inline void pushw(struct vm86_regs *r, int val) -{ - r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff); - *(uint16_t *)seg_to_linear(r->ss, r->esp) = val; -} - -void dump_regs(struct vm86_regs *r) -{ - fprintf(stderr, - "EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n" - "ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n" - "EIP=%08lx EFL=%08lx\n" - "CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n", - r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp, - r->eip, r->eflags, - r->cs, r->ds, r->es, r->ss, r->fs, r->gs); -} - -#ifdef SIGTEST -void alarm_handler(int sig) -{ - fprintf(stderr, "alarm signal=%d\n", sig); - alarm(1); -} -#endif - -int main(int argc, char **argv) -{ - uint8_t *vm86_mem; - const char *filename; - int fd, ret, seg; - struct vm86plus_struct ctx; - struct vm86_regs *r; - - if (argc != 2) - usage(); - filename = argv[1]; - - vm86_mem = mmap((void *)0x00000000, 0x110000, - PROT_WRITE | PROT_READ | PROT_EXEC, - MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); - if (vm86_mem == MAP_FAILED) { - perror("mmap"); - exit(1); - } -#ifdef SIGTEST - { - struct sigaction act; - - act.sa_handler = alarm_handler; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - sigaction(SIGALRM, &act, NULL); - alarm(1); - } -#endif - - /* load the MSDOS .com executable */ - fd = open(filename, O_RDONLY); - if (fd < 0) { - perror(filename); - exit(1); - } - ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256); - if (ret < 0) { - perror("read"); - exit(1); - } - close(fd); - - memset(&ctx, 0, sizeof(ctx)); - /* init basic registers */ - r = &ctx.regs; - r->eip = 0x100; - r->esp = 0xfffe; - seg = (COM_BASE_ADDR - 0x100) >> 4; - r->cs = seg; - r->ss = seg; - r->ds = seg; - r->es = seg; - r->fs = seg; - r->gs = seg; - r->eflags = VIF_MASK; - - /* put return code */ - set_bit((uint8_t *)&ctx.int_revectored, 0x21); - *seg_to_linear(r->cs, 0) = 0xb4; /* mov ah, $0 */ - *seg_to_linear(r->cs, 1) = 0x00; - *seg_to_linear(r->cs, 2) = 0xcd; /* int $0x21 */ - *seg_to_linear(r->cs, 3) = 0x21; - pushw(&ctx.regs, 0x0000); - - /* the value of these registers seem to be assumed by pi_10.com */ - r->esi = 0x100; - r->ecx = 0xff; - r->ebp = 0x0900; - r->edi = 0xfffe; - - for(;;) { - ret = vm86(VM86_ENTER, &ctx); - switch(VM86_TYPE(ret)) { - case VM86_INTx: - { - int int_num, ah; - - int_num = VM86_ARG(ret); - if (int_num != 0x21) - goto unknown_int; - ah = (r->eax >> 8) & 0xff; - switch(ah) { - case 0x00: /* exit */ - exit(0); - case 0x02: /* write char */ - { - uint8_t c = r->edx; - write(1, &c, 1); - } - break; - case 0x09: /* write string */ - { - uint8_t c; - for(;;) { - c = *seg_to_linear(r->ds, r->edx); - if (c == '$') - break; - write(1, &c, 1); - } - r->eax = (r->eax & ~0xff) | '$'; - } - break; - default: - unknown_int: - fprintf(stderr, "unsupported int 0x%02x\n", int_num); - dump_regs(&ctx.regs); - // exit(1); - } - } - break; - case VM86_SIGNAL: - /* a signal came, we just ignore that */ - break; - case VM86_STI: - break; - default: - fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret); - dump_regs(&ctx.regs); - exit(1); - } - } -} From 042b757cc77c9580b99ef2781cfb0a2d1bf495a6 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 18 Jul 2018 14:12:56 -0700 Subject: [PATCH 2318/2380] block/file-posix: add bdrv_attach_aio_context callback for host dev and cdrom In ed6e2161 ("linux-aio: properly bubble up errors from initialzation"), I only added a bdrv_attach_aio_context callback for the bdrv_file driver. There are several other drivers that use the shared aio_plug callback, though, and they will trip the assertion added to aio_get_linux_aio because they did not call aio_setup_linux_aio first. Add the appropriate callback definition to the affected driver definitions. Fixes: ed6e2161 ("linux-aio: properly bubble up errors from initialization") Reported-by: Farhan Ali Signed-off-by: Nishanth Aravamudan Reviewed-by: John Snow Message-id: 20180718211256.29774-1-naravamudan@digitalocean.com Cc: Eric Blake Cc: Kevin Wolf Cc: John Snow Cc: Max Reitz Cc: Stefan Hajnoczi Cc: Fam Zheng Cc: Paolo Bonzini Cc: qemu-block@nongnu.org Cc: qemu-devel@nongnu.org Signed-off-by: Stefan Hajnoczi --- block/file-posix.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/block/file-posix.c b/block/file-posix.c index 60af4b3d51..ad299beb38 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -3158,6 +3158,7 @@ static BlockDriver bdrv_host_device = { .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, @@ -3280,6 +3281,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, @@ -3410,6 +3412,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, From 4fcefd44a074008e490ff54c3c28a08b8dbfb14b Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 20 Jul 2018 11:47:13 +0800 Subject: [PATCH 2319/2380] migration: fix potential overflow in multifd send I would guess it won't happen normally, but this should ease Coverity. >>> CID 1394385: Integer handling issues (OVERFLOW_BEFORE_WIDEN) >>> Potentially overflowing expression "pages->used * 8192U" with type "unsigned int" (32 bits, unsigned) is evaluated using 32-bit arithmetic, and then used in a context that expects an expression of type "uint64_t" (64 bits, unsigned). 854 transferred = pages->used * TARGET_PAGE_SIZE + p->packet_len; Fixes: CID 1394385 CC: Juan Quintela Signed-off-by: Peter Xu Message-Id: <20180720034713.11711-1-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index 52dd678092..fdd108475c 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -851,7 +851,7 @@ static void multifd_send_pages(void) p->pages->block = NULL; multifd_send_state->pages = p->pages; p->pages = pages; - transferred = pages->used * TARGET_PAGE_SIZE + p->packet_len; + transferred = ((uint64_t) pages->used) * TARGET_PAGE_SIZE + p->packet_len; ram_counters.multifd_bytes += transferred; ram_counters.transferred += transferred;; qemu_mutex_unlock(&p->mutex); From 57225e5f32bb051c9eb03758647963aeb72cebbf Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 19 Jul 2018 10:22:57 +0100 Subject: [PATCH 2320/2380] migrate: Fix cancelling state warning We've been getting the warning: migration_iteration_finish: Unknown ending state 2 on a cancel. I think that's originally due to 39b9e17905c; although I've only seen the warning, I think that in some cases that we could find the VM stays paused after a cancel where it should restart. Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180719092257.12703-1-dgilbert@redhat.com> Reviewed-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/migration.c b/migration/migration.c index 8d56d56930..db6bde7453 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2877,6 +2877,7 @@ static void migration_iteration_finish(MigrationState *s) /* Fallthrough */ case MIGRATION_STATUS_FAILED: case MIGRATION_STATUS_CANCELLED: + case MIGRATION_STATUS_CANCELLING: if (s->vm_was_running) { vm_start(); } else { From 67fa1f5700248fc66d5b1526c268737e29892b86 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Tue, 24 Jul 2018 11:22:15 +0100 Subject: [PATCH 2321/2380] audio/hda: Fix migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix outgoing migration which was crashing in vmstate_hda_audio_stream_buf_needed, I think the problem is that we have room for upto 4 streams in the array but only use 2, when we come to try and save the state of the unused streams we hit st->state == NULL. Fixes: 280c1e1cdb24d80ecdfc Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180724102215.31866-1-dgilbert@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- hw/audio/hda-codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 2b58c3505b..617a1c1016 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -786,7 +786,7 @@ static void hda_audio_reset(DeviceState *dev) static bool vmstate_hda_audio_stream_buf_needed(void *opaque) { HDAAudioStream *st = opaque; - return st->state->use_timer; + return st->state && st->state->use_timer; } static const VMStateDescription vmstate_hda_audio_stream_buf = { From 814bb08f177af8dc67e155f0ad622fb6366c3b85 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 23 Jul 2018 20:33:02 +0800 Subject: [PATCH 2322/2380] migration: update recv bitmap only on dest vm We shouldn't update the received bitmap if we're the source VM. This fixes a breakage when release-ram is enabled on postcopy. Signed-off-by: Peter Xu Message-Id: <20180723123305.24792-2-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index fdd108475c..24dea2730c 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2827,8 +2827,15 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length) goto err; } - bitmap_clear(rb->receivedmap, start >> qemu_target_page_bits(), - length >> qemu_target_page_bits()); + /* + * On source VM, we don't need to update the received bitmap since + * we don't even have one. + */ + if (rb->receivedmap) { + bitmap_clear(rb->receivedmap, start >> qemu_target_page_bits(), + length >> qemu_target_page_bits()); + } + ret = ram_block_discard_range(rb, start, length); err: From 97ca211c6216ccfcb64c46f739a0ce36042d9ea8 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 23 Jul 2018 20:33:03 +0800 Subject: [PATCH 2323/2380] migration: disallow recovery for release-ram Postcopy recovery won't work well with release-ram capability since release-ram will drop the page buffer as long as the page is put into the send buffer. So if there is a network failure happened, any page buffers that have not yet reached the destination VM but have already been sent from the source VM will be lost forever. Let's refuse the client from resuming such a postcopy migration. Luckily release-ram was designed to only be used when src and destination VMs are on the same host, so it should be fine. Signed-off-by: Peter Xu Message-Id: <20180723123305.24792-3-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index db6bde7453..bfc4d09aae 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1629,6 +1629,25 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, "paused migration"); return false; } + + /* + * Postcopy recovery won't work well with release-ram + * capability since release-ram will drop the page buffer as + * long as the page is put into the send buffer. So if there + * is a network failure happened, any page buffers that have + * not yet reached the destination VM but have already been + * sent from the source VM will be lost forever. Let's refuse + * the client from resuming such a postcopy migration. + * Luckily release-ram was designed to only be used when src + * and destination VMs are on the same host, so it should be + * fine. + */ + if (migrate_release_ram()) { + error_setg(errp, "Postcopy recovery cannot work " + "when release-ram capability is set"); + return false; + } + /* This is a resume, skip init status */ return true; } From 829db8b497d855d39f85df4de9472c0b74ec5c3f Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 23 Jul 2018 20:33:04 +0800 Subject: [PATCH 2324/2380] tests: only update last_byte when at the edge The only possible change of last_byte is when it reaches the edge. Setting it every time might let last_byte contain an invalid data when memory corruption is detected, then the check of the next byte will be incorrect. For example, a single page corruption at address 0x14ad000 will also lead to a "fake" corruption at 0x14ae000: Memory content inconsistency at 14ad000 first_byte = 44 last_byte = 44 current = ef hit_edge = 0 Memory content inconsistency at 14ae000 first_byte = 44 last_byte = ef current = 44 hit_edge = 0 After the patch, it'll only report the corrputed page: Memory content inconsistency at 14ad000 first_byte = 44 last_byte = 44 current = ef hit_edge = 0 Signed-off-by: Peter Xu Message-Id: <20180723123305.24792-4-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- tests/migration-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/migration-test.c b/tests/migration-test.c index 086f727b34..e079e0bdb6 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -300,6 +300,7 @@ static void check_guests_ram(QTestState *who) * to us yet. */ hit_edge = true; + last_byte = b; } else { fprintf(stderr, "Memory content inconsistency at %x" " first_byte = %x last_byte = %x current = %x" @@ -308,7 +309,6 @@ static void check_guests_ram(QTestState *who) bad = true; } } - last_byte = b; } g_assert_false(bad); } From 4b3fb65db973b51346e3456987ba80b15c1fc75c Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Tue, 24 Jul 2018 20:16:25 +0800 Subject: [PATCH 2325/2380] migration: fix duplicate initialization for expected_downtime and cleanup_bh migrate_fd_connect duplicate initialize expected_downtime and cleanup_bh. Signed-off-by: Lidong Chen Message-Id: <1532434585-14732-2-git-send-email-lidongchen@tencent.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index bfc4d09aae..b7d9854bda 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3052,8 +3052,6 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) } else { /* This is a fresh new migration */ rate_limit = s->parameters.max_bandwidth / XFER_LIMIT_RATIO; - s->expected_downtime = s->parameters.downtime_limit; - s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s); /* Notify before starting migration thread */ notifier_list_notify(&migration_state_notifiers, s); From dbddad702630bc2d8dd20f6d0d8076ccb8831a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Jul 2018 17:54:17 +0100 Subject: [PATCH 2326/2380] tests: call qcrypto_init instead of gnutls_global_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calling qcrypto_init ensures that all relevant initialization is done. In particular this honours the debugging settings and thread settings. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- include/crypto/init.h | 2 ++ tests/crypto-tls-x509-helpers.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/crypto/init.h b/include/crypto/init.h index 04c1edf770..f79c02266b 100644 --- a/include/crypto/init.h +++ b/include/crypto/init.h @@ -21,6 +21,8 @@ #ifndef QCRYPTO_INIT_H #define QCRYPTO_INIT_H +#include "qapi/error.h" + int qcrypto_init(Error **errp); #endif /* QCRYPTO_INIT_H */ diff --git a/tests/crypto-tls-x509-helpers.c b/tests/crypto-tls-x509-helpers.c index 173d4e28fb..9b669c2a4b 100644 --- a/tests/crypto-tls-x509-helpers.c +++ b/tests/crypto-tls-x509-helpers.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "crypto-tls-x509-helpers.h" +#include "crypto/init.h" #include "qemu/sockets.h" #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT @@ -95,7 +96,7 @@ static gnutls_x509_privkey_t test_tls_load_key(void) void test_tls_init(const char *keyfile) { - gnutls_global_init(); + qcrypto_init(&error_abort); if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) { abort(); From 977a7204ab69a3591c4c6ed698abb8a6ee673679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 18 Jul 2018 09:34:47 +0100 Subject: [PATCH 2327/2380] tests: don't silence error reporting for all tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test-vmstate test is a bit chatty because it triggers various expected failure scenarios and the code in question uses error_report instead of accepting 'Error **errp' parameters. To silence this test the stubs for error_vprintf() were changed to send errors via g_test_message() instead of stderr: commit 28017e010ddf6849cfa830e898da3e44e6610952 Author: Paolo Bonzini Date: Mon Oct 24 18:31:03 2016 +0200 tests: send error_report to test log Implement error_vprintf to send the output of error_report to the test log. This silences test-vmstate. Signed-off-by: Paolo Bonzini Message-Id: <1477326663-67817-3-git-send-email-pbonzini@redhat.com> Unfortunately this change has global impact across the entire test suite and means that when tests fail for unexpected reasons, the message is not displayed on stderr. eg when using &error_abort in a call the test merely prints Unexpected error in qcrypto_tls_session_check_certificate() at crypto/tlssession.c:280: and the actual error message is hidden, making it impossible to diagnose the failure. This is especially problematic in CI or build systems where it isn't possible to easily pass the --debug-log flag to tests and re-run with the test log visible. This change makes the previous big hammer much more nuanced, providing a flag in the stub error_vprintf() that can used on a per-test basis to silence the errors. Only the test-vmstate silences errors initially. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- stubs/error-printf.c | 3 ++- tests/test-vmstate.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/stubs/error-printf.c b/stubs/error-printf.c index ac6b92aa69..99c6406668 100644 --- a/stubs/error-printf.c +++ b/stubs/error-printf.c @@ -4,7 +4,8 @@ void error_vprintf(const char *fmt, va_list ap) { - if (g_test_initialized() && !g_test_subprocess()) { + if (g_test_initialized() && !g_test_subprocess() && + getenv("QTEST_SILENT_ERRORS")) { char *msg = g_strdup_vprintf(fmt, ap); g_test_message("%s", msg); g_free(msg); diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index 087844b6c8..37a7a93784 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -859,6 +859,8 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); + setenv("QTEST_SILENT_ERRORS", "1", 1); + g_test_init(&argc, &argv, NULL); g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); From 68db13183f2d3193476f5d5457c47fa10ba0f9e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 18 Jul 2018 10:06:43 +0100 Subject: [PATCH 2328/2380] tests: use error_abort in places expecting errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the TLS related tests are passing an in a "Error" object to methods that are expected to fail, but then ignoring any error that is set and instead asserting on a return value. This means that when an error is unexpectedly raised, no information about it is printed out, making failures hard to diagnose. Changing these tests to pass in &error_abort will make unexpected failures print messages to stderr. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- tests/test-crypto-tlscredsx509.c | 11 +---- tests/test-crypto-tlssession.c | 76 ++++++++++++-------------------- tests/test-io-channel-tls.c | 24 ++++------ 3 files changed, 39 insertions(+), 72 deletions(-) diff --git a/tests/test-crypto-tlscredsx509.c b/tests/test-crypto-tlscredsx509.c index af2f80e89c..30f9ac4bbf 100644 --- a/tests/test-crypto-tlscredsx509.c +++ b/tests/test-crypto-tlscredsx509.c @@ -54,7 +54,7 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, "sanity-check", "yes", NULL); - if (*errp) { + if (!creds) { return NULL; } return QCRYPTO_TLS_CREDS(creds); @@ -74,7 +74,6 @@ static void test_tls_creds(const void *opaque) struct QCryptoTLSCredsTestData *data = (struct QCryptoTLSCredsTestData *)opaque; QCryptoTLSCreds *creds; - Error *err = NULL; #define CERT_DIR "tests/test-crypto-tlscredsx509-certs/" mkdir(CERT_DIR, 0700); @@ -113,17 +112,11 @@ static void test_tls_creds(const void *opaque) QCRYPTO_TLS_CREDS_ENDPOINT_SERVER : QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT), CERT_DIR, - &err); + data->expectFail ? NULL : &error_abort); if (data->expectFail) { - error_free(err); g_assert(creds == NULL); } else { - if (err) { - g_printerr("Failed to generate creds: %s\n", - error_get_pretty(err)); - error_free(err); - } g_assert(creds != NULL); } diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index 7bd811796e..fd9acf9067 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -52,28 +52,21 @@ static ssize_t testRead(char *buf, size_t len, void *opaque) static QCryptoTLSCreds *test_tls_creds_psk_create( QCryptoTLSCredsEndpoint endpoint, - const char *dir, - Error **errp) + const char *dir) { - Error *err = NULL; Object *parent = object_get_objects_root(); Object *creds = object_new_with_props( TYPE_QCRYPTO_TLS_CREDS_PSK, parent, (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "testtlscredsserver" : "testtlscredsclient"), - &err, + &error_abort, "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "server" : "client"), "dir", dir, "priority", "NORMAL", NULL ); - - if (err) { - error_propagate(errp, err); - return NULL; - } return QCRYPTO_TLS_CREDS(creds); } @@ -87,7 +80,6 @@ static void test_crypto_tls_session_psk(void) int channel[2]; bool clientShake = false; bool serverShake = false; - Error *err = NULL; int ret; /* We'll use this for our fake client-server connection */ @@ -104,25 +96,23 @@ static void test_crypto_tls_session_psk(void) clientCreds = test_tls_creds_psk_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - WORKDIR, - &err); + WORKDIR); g_assert(clientCreds != NULL); serverCreds = test_tls_creds_psk_create( QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - WORKDIR, - &err); + WORKDIR); g_assert(serverCreds != NULL); /* Now the real part of the test, setup the sessions */ clientSess = qcrypto_tls_session_new( clientCreds, NULL, NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err); + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); + g_assert(clientSess != NULL); + serverSess = qcrypto_tls_session_new( serverCreds, NULL, NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err); - - g_assert(clientSess != NULL); + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); g_assert(serverSess != NULL); /* For handshake to work, we need to set the I/O callbacks @@ -145,7 +135,7 @@ static void test_crypto_tls_session_psk(void) int rv; if (!serverShake) { rv = qcrypto_tls_session_handshake(serverSess, - &err); + &error_abort); g_assert(rv >= 0); if (qcrypto_tls_session_get_handshake_status(serverSess) == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { @@ -154,7 +144,7 @@ static void test_crypto_tls_session_psk(void) } if (!clientShake) { rv = qcrypto_tls_session_handshake(clientSess, - &err); + &error_abort); g_assert(rv >= 0); if (qcrypto_tls_session_get_handshake_status(clientSess) == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { @@ -165,8 +155,10 @@ static void test_crypto_tls_session_psk(void) /* Finally make sure the server & client validation is successful. */ - g_assert(qcrypto_tls_session_check_credentials(serverSess, &err) == 0); - g_assert(qcrypto_tls_session_check_credentials(clientSess, &err) == 0); + g_assert(qcrypto_tls_session_check_credentials(serverSess, + &error_abort) == 0); + g_assert(qcrypto_tls_session_check_credentials(clientSess, + &error_abort) == 0); object_unparent(OBJECT(serverCreds)); object_unparent(OBJECT(clientCreds)); @@ -192,17 +184,15 @@ struct QCryptoTLSSessionTestData { static QCryptoTLSCreds *test_tls_creds_x509_create( QCryptoTLSCredsEndpoint endpoint, - const char *certdir, - Error **errp) + const char *certdir) { - Error *err = NULL; Object *parent = object_get_objects_root(); Object *creds = object_new_with_props( TYPE_QCRYPTO_TLS_CREDS_X509, parent, (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "testtlscredsserver" : "testtlscredsclient"), - &err, + &error_abort, "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "server" : "client"), "dir", certdir, @@ -217,11 +207,6 @@ static QCryptoTLSCreds *test_tls_creds_x509_create( "sanity-check", "no", NULL ); - - if (err) { - error_propagate(errp, err); - return NULL; - } return QCRYPTO_TLS_CREDS(creds); } @@ -249,7 +234,6 @@ static void test_crypto_tls_session_x509(const void *opaque) int channel[2]; bool clientShake = false; bool serverShake = false; - Error *err = NULL; int ret; /* We'll use this for our fake client-server connection */ @@ -293,14 +277,12 @@ static void test_crypto_tls_session_x509(const void *opaque) clientCreds = test_tls_creds_x509_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - CLIENT_CERT_DIR, - &err); + CLIENT_CERT_DIR); g_assert(clientCreds != NULL); serverCreds = test_tls_creds_x509_create( QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - SERVER_CERT_DIR, - &err); + SERVER_CERT_DIR); g_assert(serverCreds != NULL); acl = qemu_acl_init("tlssessionacl"); @@ -314,13 +296,13 @@ static void test_crypto_tls_session_x509(const void *opaque) /* Now the real part of the test, setup the sessions */ clientSess = qcrypto_tls_session_new( clientCreds, data->hostname, NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err); + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); + g_assert(clientSess != NULL); + serverSess = qcrypto_tls_session_new( serverCreds, NULL, data->wildcards ? "tlssessionacl" : NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err); - - g_assert(clientSess != NULL); + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); g_assert(serverSess != NULL); /* For handshake to work, we need to set the I/O callbacks @@ -343,7 +325,7 @@ static void test_crypto_tls_session_x509(const void *opaque) int rv; if (!serverShake) { rv = qcrypto_tls_session_handshake(serverSess, - &err); + &error_abort); g_assert(rv >= 0); if (qcrypto_tls_session_get_handshake_status(serverSess) == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { @@ -352,7 +334,7 @@ static void test_crypto_tls_session_x509(const void *opaque) } if (!clientShake) { rv = qcrypto_tls_session_handshake(clientSess, - &err); + &error_abort); g_assert(rv >= 0); if (qcrypto_tls_session_get_handshake_status(clientSess) == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { @@ -365,10 +347,9 @@ static void test_crypto_tls_session_x509(const void *opaque) /* Finally make sure the server validation does what * we were expecting */ - if (qcrypto_tls_session_check_credentials(serverSess, &err) < 0) { + if (qcrypto_tls_session_check_credentials( + serverSess, data->expectServerFail ? NULL : &error_abort) < 0) { g_assert(data->expectServerFail); - error_free(err); - err = NULL; } else { g_assert(!data->expectServerFail); } @@ -376,10 +357,9 @@ static void test_crypto_tls_session_x509(const void *opaque) /* * And the same for the client validation check */ - if (qcrypto_tls_session_check_credentials(clientSess, &err) < 0) { + if (qcrypto_tls_session_check_credentials( + clientSess, data->expectClientFail ? NULL : &error_abort) < 0) { g_assert(data->expectClientFail); - error_free(err); - err = NULL; } else { g_assert(!data->expectClientFail); } diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c index bb88ee870f..4900c6d433 100644 --- a/tests/test-io-channel-tls.c +++ b/tests/test-io-channel-tls.c @@ -30,6 +30,7 @@ #include "crypto/init.h" #include "crypto/tlscredsx509.h" #include "qemu/acl.h" +#include "qapi/error.h" #include "qom/object_interfaces.h" #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT @@ -64,8 +65,7 @@ static void test_tls_handshake_done(QIOTask *task, static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, - const char *certdir, - Error **errp) + const char *certdir) { Object *parent = object_get_objects_root(); Object *creds = object_new_with_props( @@ -73,7 +73,7 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, parent, (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "testtlscredsserver" : "testtlscredsclient"), - errp, + &error_abort, "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "server" : "client"), "dir", certdir, @@ -89,9 +89,6 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, NULL ); - if (*errp) { - return NULL; - } return QCRYPTO_TLS_CREDS(creds); } @@ -121,7 +118,6 @@ static void test_io_channel_tls(const void *opaque) int channel[2]; struct QIOChannelTLSHandshakeData clientHandshake = { false, false }; struct QIOChannelTLSHandshakeData serverHandshake = { false, false }; - Error *err = NULL; QIOChannelTest *test; GMainContext *mainloop; @@ -157,14 +153,12 @@ static void test_io_channel_tls(const void *opaque) clientCreds = test_tls_creds_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - CLIENT_CERT_DIR, - &err); + CLIENT_CERT_DIR); g_assert(clientCreds != NULL); serverCreds = test_tls_creds_create( QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - SERVER_CERT_DIR, - &err); + SERVER_CERT_DIR); g_assert(serverCreds != NULL); acl = qemu_acl_init("channeltlsacl"); @@ -176,10 +170,10 @@ static void test_io_channel_tls(const void *opaque) } clientChanSock = qio_channel_socket_new_fd( - channel[0], &err); + channel[0], &error_abort); g_assert(clientChanSock != NULL); serverChanSock = qio_channel_socket_new_fd( - channel[1], &err); + channel[1], &error_abort); g_assert(serverChanSock != NULL); /* @@ -193,12 +187,12 @@ static void test_io_channel_tls(const void *opaque) /* Now the real part of the test, setup the sessions */ clientChanTLS = qio_channel_tls_new_client( QIO_CHANNEL(clientChanSock), clientCreds, - data->hostname, &err); + data->hostname, &error_abort); g_assert(clientChanTLS != NULL); serverChanTLS = qio_channel_tls_new_server( QIO_CHANNEL(serverChanSock), serverCreds, - "channeltlsacl", &err); + "channeltlsacl", &error_abort); g_assert(serverChanTLS != NULL); qio_channel_tls_handshake(clientChanTLS, From db0a8c70f25fe497c4b786d8edac063daa744c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 18 Jul 2018 10:24:59 +0100 Subject: [PATCH 2329/2380] tests: fix TLS handshake failure with TLS 1.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When gnutls negotiates TLS 1.3 instead of 1.2, the order of messages sent by the handshake changes. This exposed a logic bug in the test suite which caused us to wait for the server to see handshake completion, but not wait for the client to see completion. The result was the client didn't receive the certificate for verification and the test failed. This is exposed in Fedora 29 rawhide which has just enabled TLS 1.3 in its GNUTLS builds. Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrangé --- tests/test-crypto-tlssession.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index fd9acf9067..6fa9950afb 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -151,7 +151,7 @@ static void test_crypto_tls_session_psk(void) clientShake = true; } } - } while (!clientShake && !serverShake); + } while (!clientShake || !serverShake); /* Finally make sure the server & client validation is successful. */ @@ -341,7 +341,7 @@ static void test_crypto_tls_session_x509(const void *opaque) clientShake = true; } } - } while (!clientShake && !serverShake); + } while (!clientShake || !serverShake); /* Finally make sure the server validation does what From 18a398f6a39df4b08ff86ac0d38384193ca5f4cc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Jul 2018 22:06:31 +0100 Subject: [PATCH 2330/2380] Update version for v3.0.0-rc2 release Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 65be8fd280..c4eae17987 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.91 +2.12.92 From ad63c549ecd4af4a22a675a815edeb06b0e7bb6e Mon Sep 17 00:00:00 2001 From: liujunjie Date: Tue, 24 Jul 2018 21:43:39 +0800 Subject: [PATCH 2331/2380] qstring: Fix qstring_from_substr() not to provoke int overflow qstring_from_substr() parameters @start and @end are of type int. blkdebug_parse_filename(), blkverify_parse_filename(), nbd_parse_uri(), and qstring_from_str() pass @end values of type size_t or ptrdiff_t. Values exceeding INT_MAX get truncated, with possibly disastrous results. Such huge substrings seem unlikely, but we found one in a core dump, where "info tlb" executed via QMP's human-monitor-command apparently produced 35 GiB of output. Fix by changing the parameters size_t. Signed-off-by: liujunjie Message-Id: <20180724134339.17832-1-liujunjie23@huawei.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- include/qapi/qmp/qstring.h | 2 +- qobject/qstring.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h index b3b3d444d2..3e83e3a95d 100644 --- a/include/qapi/qmp/qstring.h +++ b/include/qapi/qmp/qstring.h @@ -24,7 +24,7 @@ struct QString { QString *qstring_new(void); QString *qstring_from_str(const char *str); -QString *qstring_from_substr(const char *str, int start, int end); +QString *qstring_from_substr(const char *str, size_t start, size_t end); size_t qstring_get_length(const QString *qstring); const char *qstring_get_str(const QString *qstring); const char *qstring_get_try_str(const QString *qstring); diff --git a/qobject/qstring.c b/qobject/qstring.c index afca54b47a..18b8eb82f8 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -37,7 +37,7 @@ size_t qstring_get_length(const QString *qstring) * * Return string reference */ -QString *qstring_from_substr(const char *str, int start, int end) +QString *qstring_from_substr(const char *str, size_t start, size_t end) { QString *qstring; From b65ab77b3afadd7bb3051b341a5258ff7fb9d246 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 27 Jul 2018 08:22:03 +0200 Subject: [PATCH 2332/2380] qstring: Assert size calculations don't overflow Signed-off-by: Markus Armbruster Message-Id: <20180727062204.10401-2-armbru@redhat.com> Reviewed-by: Eric Blake --- qobject/qstring.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qobject/qstring.c b/qobject/qstring.c index 18b8eb82f8..1bb7784a88 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -41,17 +41,19 @@ QString *qstring_from_substr(const char *str, size_t start, size_t end) { QString *qstring; + assert(start <= end + 1); + qstring = g_malloc(sizeof(*qstring)); qobject_init(QOBJECT(qstring), QTYPE_QSTRING); qstring->length = end - start + 1; qstring->capacity = qstring->length; + assert(qstring->capacity < SIZE_MAX); qstring->string = g_malloc(qstring->capacity + 1); memcpy(qstring->string, str + start, qstring->length); qstring->string[qstring->length] = 0; - return qstring; } @@ -68,7 +70,9 @@ QString *qstring_from_str(const char *str) static void capacity_increase(QString *qstring, size_t len) { if (qstring->capacity < (qstring->length + len)) { + assert(len <= SIZE_MAX - qstring->capacity); qstring->capacity += len; + assert(qstring->capacity <= SIZE_MAX / 2); qstring->capacity *= 2; /* use exponential growth */ qstring->string = g_realloc(qstring->string, qstring->capacity + 1); From ba891d68b4ff17faaea3d3a8bfd82af3eed0a134 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 27 Jul 2018 08:22:04 +0200 Subject: [PATCH 2333/2380] qstring: Move qstring_from_substr()'s @end one to the right qstring_from_substr() takes the index of the substring's first and last character. qstring_from_substr(s, 0, SIZE_MAX) denotes an empty substring. Awkward. Shift the end index one to the right. This simplifies both qstring_from_substr() and its callers. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180727062204.10401-3-armbru@redhat.com> --- block/blkdebug.c | 2 +- block/blkverify.c | 2 +- block/nbd.c | 2 +- qobject/qstring.c | 6 +++--- tests/check-qobject.c | 2 +- tests/check-qstring.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index 0457bf5b66..0759452925 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -305,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options, if (c != filename) { QString *config_path; - config_path = qstring_from_substr(filename, 0, c - filename - 1); + config_path = qstring_from_substr(filename, 0, c - filename); qdict_put(options, "config", config_path); } diff --git a/block/blkverify.c b/block/blkverify.c index da97ee5927..89bf4386e3 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -80,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options, } /* TODO Implement option pass-through and set raw.filename here */ - raw_path = qstring_from_substr(filename, 0, c - filename - 1); + raw_path = qstring_from_substr(filename, 0, c - filename); qdict_put(options, "x-raw", raw_path); /* TODO Allow multi-level nesting and set file.filename here */ diff --git a/block/nbd.c b/block/nbd.c index b198ad775f..e87699fb73 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -109,7 +109,7 @@ static int nbd_parse_uri(const char *filename, QDict *options) /* strip braces from literal IPv6 address */ if (uri->server[0] == '[') { host = qstring_from_substr(uri->server, 1, - strlen(uri->server) - 2); + strlen(uri->server) - 1); } else { host = qstring_from_str(uri->server); } diff --git a/qobject/qstring.c b/qobject/qstring.c index 1bb7784a88..0f1510e792 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -41,12 +41,12 @@ QString *qstring_from_substr(const char *str, size_t start, size_t end) { QString *qstring; - assert(start <= end + 1); + assert(start <= end); qstring = g_malloc(sizeof(*qstring)); qobject_init(QOBJECT(qstring), QTYPE_QSTRING); - qstring->length = end - start + 1; + qstring->length = end - start; qstring->capacity = qstring->length; assert(qstring->capacity < SIZE_MAX); @@ -64,7 +64,7 @@ QString *qstring_from_substr(const char *str, size_t start, size_t end) */ QString *qstring_from_str(const char *str) { - return qstring_from_substr(str, 0, strlen(str) - 1); + return qstring_from_substr(str, 0, strlen(str)); } static void capacity_increase(QString *qstring, size_t len) diff --git a/tests/check-qobject.c b/tests/check-qobject.c index 16ccbde82c..593c3a0618 100644 --- a/tests/check-qobject.c +++ b/tests/check-qobject.c @@ -154,7 +154,7 @@ static void qobject_is_equal_string_test(void) str_case = qstring_from_str("Foo"); /* Should yield "foo" */ - str_built = qstring_from_substr("form", 0, 1); + str_built = qstring_from_substr("form", 0, 2); qstring_append_chr(str_built, 'o'); check_unequal(str_base, str_whitespace_0, str_whitespace_1, diff --git a/tests/check-qstring.c b/tests/check-qstring.c index f11a7a8605..2d079921e3 100644 --- a/tests/check-qstring.c +++ b/tests/check-qstring.c @@ -66,7 +66,7 @@ static void qstring_from_substr_test(void) { QString *qs; - qs = qstring_from_substr("virtualization", 3, 9); + qs = qstring_from_substr("virtualization", 3, 10); g_assert(qs != NULL); g_assert(strcmp(qstring_get_str(qs), "tualiza") == 0); From 990e0be2603511560168e1ad61f68294d951c39e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 24 Jul 2018 13:59:21 +0200 Subject: [PATCH 2334/2380] i386: do not migrate MSR_SMI_COUNT on machine types <2.12 MSR_SMI_COUNT started being migrated in QEMU 2.12. Do not migrate it on older machine types, or the subsection causes a load failure for guests that use SMM. Signed-off-by: Paolo Bonzini --- include/hw/i386/pc.h | 4 ++++ target/i386/cpu.c | 2 ++ target/i386/cpu.h | 1 + target/i386/machine.c | 2 +- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 654003f44c..6894f37df1 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -317,6 +317,10 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); #define PC_COMPAT_2_11 \ HW_COMPAT_2_11 \ {\ + .driver = TYPE_X86_CPU,\ + .property = "x-migrate-smi-count",\ + .value = "off",\ + },{\ .driver = "Skylake-Server" "-" TYPE_X86_CPU,\ .property = "clflushopt",\ .value = "off",\ diff --git a/target/i386/cpu.c b/target/i386/cpu.c index f454d4beb3..723e02221e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5435,6 +5435,8 @@ static Property x86_cpu_properties[] = { false), DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true), + DEFINE_PROP_BOOL("x-migrate-smi-count", X86CPU, migrate_smi_count, + true), /* * lecacy_cache defaults to true unless the CPU model provides its * own cache information (see x86_cpu_load_def()). diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 194e2e6b92..c18863ec7a 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1379,6 +1379,7 @@ struct X86CPU { bool expose_kvm; bool expose_tcg; bool migratable; + bool migrate_smi_count; bool max_features; /* Enable all supported features automatically */ uint32_t apic_id; diff --git a/target/i386/machine.c b/target/i386/machine.c index 8b64dff487..084c2c73a8 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -400,7 +400,7 @@ static bool msr_smi_count_needed(void *opaque) X86CPU *cpu = opaque; CPUX86State *env = &cpu->env; - return env->msr_smi_count != 0; + return cpu->migrate_smi_count && env->msr_smi_count != 0; } static const VMStateDescription vmstate_msr_smi_count = { From 1d3db6bdbb0b541744cc9e008371ec7a37986d8a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 24 Jul 2018 13:59:40 +0200 Subject: [PATCH 2335/2380] i386: implement MSR_SMI_COUNT for TCG This is trivial, so just do it. Signed-off-by: Paolo Bonzini --- target/i386/misc_helper.c | 3 +++ target/i386/smm_helper.c | 1 + 2 files changed, 4 insertions(+) diff --git a/target/i386/misc_helper.c b/target/i386/misc_helper.c index 628f64aad5..78f2020ef2 100644 --- a/target/i386/misc_helper.c +++ b/target/i386/misc_helper.c @@ -447,6 +447,9 @@ void helper_rdmsr(CPUX86State *env) val = env->tsc_aux; break; #endif + case MSR_SMI_COUNT: + val = env->msr_smi_count; + break; case MSR_MTRRphysBase(0): case MSR_MTRRphysBase(1): case MSR_MTRRphysBase(2): diff --git a/target/i386/smm_helper.c b/target/i386/smm_helper.c index 90621e5977..c1c34a75db 100644 --- a/target/i386/smm_helper.c +++ b/target/i386/smm_helper.c @@ -54,6 +54,7 @@ void do_smm_enter(X86CPU *cpu) qemu_log_mask(CPU_LOG_INT, "SMM: enter\n"); log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP); + env->msr_smi_count++; env->hflags |= HF_SMM_MASK; if (env->hflags2 & HF2_NMI_MASK) { env->hflags2 |= HF2_SMM_INSIDE_NMI_MASK; From e4dab9449a41d1bc61e4866c6fe2582513ddbff4 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Wed, 25 Jul 2018 15:15:26 +0300 Subject: [PATCH 2336/2380] timer: remove replay clock probe in deadline calculation Ciro Santilli reported that commit a5ed352596a8b7eb2f9acce34371b944ac3056c4 breaks the execution replay. It happens due to the probing the clock for the new instances of iothread. However, this probing was made in replay mode for the timer lists that are empty. This patch removes clock probing in replay mode. It is an artifact of the old version with another thread model. Signed-off-by: Pavel Dovgalyuk Message-Id: <20180725121526.12867.17866.stgit@pasha-VirtualBox> Signed-off-by: Paolo Bonzini --- util/qemu-timer.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 2ed1bf2778..86bfe84037 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -578,17 +578,10 @@ int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg) { int64_t deadline = -1; QEMUClockType type; - bool play = replay_mode == REPLAY_MODE_PLAY; for (type = 0; type < QEMU_CLOCK_MAX; type++) { if (qemu_clock_use_for_deadline(type)) { - if (!play || type == QEMU_CLOCK_REALTIME) { - deadline = qemu_soonest_timeout(deadline, - timerlist_deadline_ns(tlg->tl[type])); - } else { - /* Read clock from the replay file and - do not calculate the deadline, based on virtual clock. */ - qemu_clock_get_ns(type); - } + deadline = qemu_soonest_timeout(deadline, + timerlist_deadline_ns(tlg->tl[type])); } } return deadline; From cc4c77e12b422db8a685cec97866950895a684bc Mon Sep 17 00:00:00 2001 From: Jay Zhou Date: Sat, 28 Apr 2018 13:54:24 +0800 Subject: [PATCH 2337/2380] backends/cryptodev: remove dead code Fix Coverity issue 1390600. Signed-off-by: Jay Zhou Message-Id: <1524894864-7492-1-git-send-email-jianjay.zhou@huawei.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini --- backends/cryptodev-vhost-user.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index d52daccfcd..d539f14d59 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -157,7 +157,6 @@ static void cryptodev_vhost_user_event(void *opaque, int event) { CryptoDevBackendVhostUser *s = opaque; CryptoDevBackend *b = CRYPTODEV_BACKEND(s); - Error *err = NULL; int queues = b->conf.peers.queues; assert(queues < MAX_CRYPTO_QUEUE_NUM); @@ -174,10 +173,6 @@ static void cryptodev_vhost_user_event(void *opaque, int event) cryptodev_vhost_user_stop(queues, s); break; } - - if (err) { - error_report_err(err); - } } static void cryptodev_vhost_user_init( From 41b6513436d2ebb64c7df8f009f630922a8e8990 Mon Sep 17 00:00:00 2001 From: KONRAD Frederic Date: Wed, 25 Jul 2018 20:07:29 +0200 Subject: [PATCH 2338/2380] qcow: fix a reference leak Since 42a3e1ab367cdf38cce093de24eb406b99a4ef96 qemu asserts when using the vvfat driver: git clone git://qemu.org/qemu.git cd qemu ./configure --target-list=ppc-softmmu --enable-debug make -j8 mkdir foo touch foo/hello ./ppc-softmmu/qemu-system-ppc -M prep --nographic --monitor null \ -hda fat:rw:./foo "Ctrl-C" qemu-system-ppc: block.c:3368: bdrv_close_all: Assertion \ `((&all_bdrv_states)->tqh_first == ((void *)0))' failed. This is because we reference bs twice in qcow_co_create(..) one time in bdrv_open_blockdev_ref(..) and in blk_insert_bs(..) but we unref it only once in blk_unref which leads to the reference leak. Note that I didn't tested much QCOW after this change as I don't use it much. Signed-off-by: KONRAD Frederic Signed-off-by: Kevin Wolf --- block/qcow.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/qcow.c b/block/qcow.c index 102d058d1c..385d935258 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -938,6 +938,7 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, ret = 0; exit: blk_unref(qcow_blk); + bdrv_unref(bs); qcrypto_block_free(crypto); return ret; } From 308999e9d46c7db062d314df98295a38e5732d01 Mon Sep 17 00:00:00 2001 From: Leonid Bloch Date: Wed, 25 Jul 2018 17:27:55 +0300 Subject: [PATCH 2339/2380] qcow2: A grammar fix in conflicting cache sizing error message Signed-off-by: Leonid Bloch Reviewed-by: John Snow Signed-off-by: Kevin Wolf --- block/qcow2.c | 2 +- tests/qemu-iotests/103.out | 4 ++-- tests/qemu-iotests/137.out | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 6162ed8be2..ec9e6238a0 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -797,7 +797,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, if (l2_cache_size_set && refcount_cache_size_set) { error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE " and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set " - "the same time"); + "at the same time"); return; } else if (*l2_cache_size > combined_cache_size) { error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed " diff --git a/tests/qemu-iotests/103.out b/tests/qemu-iotests/103.out index bd45d3875a..bd9eec3250 100644 --- a/tests/qemu-iotests/103.out +++ b/tests/qemu-iotests/103.out @@ -5,10 +5,10 @@ wrote 65536/65536 bytes at offset 0 === Testing invalid option combinations === -can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time +can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set at the same time can't open device TEST_DIR/t.IMGFMT: l2-cache-size may not exceed cache-size can't open device TEST_DIR/t.IMGFMT: refcount-cache-size may not exceed cache-size -can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time +can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set at the same time can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out index 96724a6c33..6a2ffc71fd 100644 --- a/tests/qemu-iotests/137.out +++ b/tests/qemu-iotests/137.out @@ -16,7 +16,7 @@ read 33554432/33554432 bytes at offset 0 === Try setting some invalid values === Parameter 'lazy-refcounts' expects 'on' or 'off' -cache-size, l2-cache-size and refcount-cache-size may not be set the same time +cache-size, l2-cache-size and refcount-cache-size may not be set at the same time l2-cache-size may not exceed cache-size refcount-cache-size may not exceed cache-size L2 cache size too big From a1c81f4f16a74d0d544f5d3ac405bcaad83541fd Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 27 Jul 2018 14:53:14 +0800 Subject: [PATCH 2340/2380] file-posix: Handle EINTR in preallocation=full write Cc: qemu-stable@nongnu.org Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- block/file-posix.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/block/file-posix.c b/block/file-posix.c index ad299beb38..928b863ced 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1646,6 +1646,9 @@ static int handle_aiocb_truncate(RawPosixAIOData *aiocb) num = MIN(left, 65536); result = write(fd, buf, num); if (result < 0) { + if (errno == EINTR) { + continue; + } result = -errno; error_setg_errno(errp, -result, "Could not write zeros for preallocation"); From f4a1b6536f70d33caa9da6c091b98038943998c9 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 24 Jul 2018 16:47:38 +0800 Subject: [PATCH 2341/2380] docs: Describe using images in writing iotests Signed-off-by: Fam Zheng Reviewed-by: Eric Blake Reviewed-by: John Snow Signed-off-by: Kevin Wolf --- docs/devel/testing.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 5e19cd50da..8e1fa3a66e 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -255,6 +255,17 @@ comparable library support for invoking and interacting with QEMU programs. If you opt for Python, it is strongly recommended to write Python 3 compatible code. +Both Python and Bash frameworks in iotests provide helpers to manage test +images. They can be used to create and clean up images under the test +directory. If no I/O or any protocol specific feature is needed, it is often +more convenient to use the pseudo block driver, ``null-co://``, as the test +image, which doesn't require image creation or cleaning up. Avoid system-wide +devices or files whenever possible, such as ``/dev/null`` or ``/dev/zero``. +Otherwise, image locking implications have to be considered. For example, +another application on the host may have locked the file, possibly leading to a +test failure. If using such devices are explicitly desired, consider adding +``locking=off`` option to disable image locking. + Docker based tests ================== From ac49c189b4fd48251314a2b5d5a251bcc1687d66 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 24 Jul 2018 16:47:39 +0800 Subject: [PATCH 2342/2380] iotests: Don't lock /dev/null in 226 On my system (Fedora 28), this script reports a 'failed to get "consistent read" lock' error. Following docs/devel/testing.rst, it's better to add locking=off here. Signed-off-by: Fam Zheng Reviewed-by: Eric Blake Reviewed-by: John Snow Signed-off-by: Kevin Wolf --- tests/qemu-iotests/226 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/226 b/tests/qemu-iotests/226 index 211ea9888a..8ec3e612dd 100755 --- a/tests/qemu-iotests/226 +++ b/tests/qemu-iotests/226 @@ -56,10 +56,10 @@ for PROTO in "file" "host_device" "host_cdrom"; do echo echo "== Testing RO ==" $QEMU_IO -c "open -r -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt - $QEMU_IO -c "open -r -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt + $QEMU_IO -c "open -r -o driver=$PROTO,filename=/dev/null,locking=off" 2>&1 | _filter_imgfmt echo "== Testing RW ==" $QEMU_IO -c "open -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt - $QEMU_IO -c "open -o driver=$PROTO,filename=/dev/null" 2>&1 | _filter_imgfmt + $QEMU_IO -c "open -o driver=$PROTO,filename=/dev/null,locking=off" 2>&1 | _filter_imgfmt done # success, all done From b85504314ffb0b33707559b483143b7e8121e003 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 27 Jul 2018 11:34:00 +0800 Subject: [PATCH 2343/2380] Revert "qemu-img: Document copy offloading implications with -S and -c" This reverts commit eb461485f4558e362fab905735b50987505bca44. Now that we introduce an explicit option, these implicit rules are not used. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- qemu-img.texi | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qemu-img.texi b/qemu-img.texi index 5853cd18d1..aeb1b9e66c 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -96,8 +96,7 @@ will enumerate information about backing files in a disk image chain. Refer below for further description. @item -c -indicates that target image must be compressed (qcow format only). If this -option is used, copy offloading will not be attempted. +indicates that target image must be compressed (qcow format only) @item -h with or without a command shows help and lists the supported formats @@ -116,8 +115,7 @@ in case both @var{-q} and @var{-p} options are used. indicates the consecutive number of bytes that must contain only zeros for qemu-img to create a sparse image during conversion. This value is rounded down to the nearest 512 bytes. You may use the common size suffixes like -@code{k} for kilobytes. If this option is used, copy offloading will not be -attempted. +@code{k} for kilobytes. @item -t @var{cache} specifies the cache mode that should be used with the (destination) file. See From e11ce12f5eb26438419e486a3ae2c9bb58a23c1f Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 27 Jul 2018 11:34:01 +0800 Subject: [PATCH 2344/2380] qemu-img: Add -C option for convert with copy offloading Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- qemu-img-cmds.hx | 2 +- qemu-img.c | 21 +++++++++++++++++---- qemu-img.texi | 8 +++++++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 69758fb6e8..1526f327a5 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -44,7 +44,7 @@ STEXI ETEXI DEF("convert", img_convert, - "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") + "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") STEXI @item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI diff --git a/qemu-img.c b/qemu-img.c index 9b7506b8ae..1acddf693c 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -2024,11 +2024,12 @@ static int img_convert(int argc, char **argv) skip_create = false, progress = false, tgt_image_opts = false; int64_t ret = -EINVAL; bool force_share = false; + bool explict_min_sparse = false; ImgConvertState s = (ImgConvertState) { /* Need at least 4k of zeros for sparse detection */ .min_sparse = 8, - .copy_range = true, + .copy_range = false, .buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE, .wr_in_order = true, .num_coroutines = 8, @@ -2043,7 +2044,7 @@ static int img_convert(int argc, char **argv) {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:O:B:co:l:S:pt:T:qnm:WU", + c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU", long_options, NULL); if (c == -1) { break; @@ -2067,9 +2068,11 @@ static int img_convert(int argc, char **argv) case 'B': out_baseimg = optarg; break; + case 'C': + s.copy_range = true; + break; case 'c': s.compressed = true; - s.copy_range = false; break; case 'o': if (!is_valid_option_list(optarg)) { @@ -2112,7 +2115,7 @@ static int img_convert(int argc, char **argv) } s.min_sparse = sval / BDRV_SECTOR_SIZE; - s.copy_range = false; + explict_min_sparse = true; break; } case 'p': @@ -2172,6 +2175,16 @@ static int img_convert(int argc, char **argv) goto fail_getopt; } + if (s.compressed && s.copy_range) { + error_report("Cannot enable copy offloading when -c is used"); + goto fail_getopt; + } + + if (explict_min_sparse && s.copy_range) { + error_report("Cannot enable copy offloading when -S is used"); + goto fail_getopt; + } + if (tgt_image_opts && !skip_create) { error_report("--target-image-opts requires use of -n flag"); goto fail_getopt; diff --git a/qemu-img.texi b/qemu-img.texi index aeb1b9e66c..3b6710a580 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -169,6 +169,12 @@ Number of parallel coroutines for the convert process Allow out-of-order writes to the destination. This option improves performance, but is only recommended for preallocated devices like host devices or other raw block devices. +@item -C +Try to use copy offloading to move data from source image to target. This may +improve performance if the data is remote, such as with NFS or iSCSI backends, +but will not automatically sparsify zero sectors, and may result in a fully +allocated target image depending on the host support for getting allocation +information. @end table Parameters to dd subcommand: @@ -319,7 +325,7 @@ Error on reading data @end table -@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} Convert the disk image @var{filename} or a snapshot @var{snapshot_param} to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} From 8ba4f10fa689251facd483c3ee0ef4dd4e9bec53 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 27 Jul 2018 11:34:02 +0800 Subject: [PATCH 2345/2380] iotests: Add test for 'qemu-img convert -C' compatibility Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- tests/qemu-iotests/082 | 8 ++++++++ tests/qemu-iotests/082.out | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 index a872f771a6..3e605d52d1 100755 --- a/tests/qemu-iotests/082 +++ b/tests/qemu-iotests/082 @@ -157,6 +157,14 @@ run_qemu_img convert -o help # Try help option for a format that does not support creation run_qemu_img convert -O bochs -o help +echo +echo === convert: -C and other options === + +# Adding the help option to a command without other -o options +run_qemu_img convert -C -S 4k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target +run_qemu_img convert -C -S 8k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target +run_qemu_img convert -C -c -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target + echo echo === amend: Options specified more than once === diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index 60ef87c276..19e9fb13ff 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -508,6 +508,17 @@ size Virtual disk size Testing: convert -O bochs -o help qemu-img: Format driver 'bochs' does not support image creation +=== convert: -C and other options === + +Testing: convert -C -S 4k -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target +qemu-img: Cannot enable copy offloading when -S is used + +Testing: convert -C -S 8k -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target +qemu-img: Cannot enable copy offloading when -S is used + +Testing: convert -C -c -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target +qemu-img: Cannot enable copy offloading when -c is used + === amend: Options specified more than once === Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2 From 52ebcb268273de217510bc9ed688c23894ae32a2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 25 Jul 2018 13:20:32 +0200 Subject: [PATCH 2346/2380] block: Fix documentation for BDRV_REQ_MAY_UNMAP BDRV_REQ_MAY_UNMAP in a write_zeroes request does not only allow the driver to unmap the blocks, but it actively requests that the blocks be unmapped afterwards if at all possible. Signed-off-by: Kevin Wolf --- include/block/block.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/block/block.h b/include/block/block.h index f85e3a6ed3..4e0871aaf9 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -43,11 +43,12 @@ typedef struct BlockFragInfo { typedef enum { BDRV_REQ_COPY_ON_READ = 0x1, BDRV_REQ_ZERO_WRITE = 0x2, - /* The BDRV_REQ_MAY_UNMAP flag is used to indicate that the block driver - * is allowed to optimize a write zeroes request by unmapping (discarding) - * blocks if it is guaranteed that the result will read back as - * zeroes. The flag is only passed to the driver if the block device is - * opened with BDRV_O_UNMAP. + + /* + * The BDRV_REQ_MAY_UNMAP flag is used in write_zeroes requests to indicate + * that the block driver should unmap (discard) blocks if it is guaranteed + * that the result will read back as zeroes. The flag is only passed to the + * driver if the block device is opened with BDRV_O_UNMAP. */ BDRV_REQ_MAY_UNMAP = 0x4, From 34fa110e424e9a6a9b7e0274c3d4bfee766eb7ed Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 26 Jul 2018 11:28:30 +0200 Subject: [PATCH 2347/2380] file-posix: Fix write_zeroes with unmap on block devices The BLKDISCARD ioctl doesn't guarantee that the discarded blocks read as all-zero afterwards, so don't try to abuse it for zero writing. We try to only use this if BLKDISCARDZEROES tells us that it is safe, but this is unreliable on older kernels and a constant 0 in newer kernels. In other words, this code path is never actually used with newer kernels, so we don't even try to unmap while writing zeros. This patch removes the abuse of discard for writing zeroes from file-posix and instead adds a new function that uses interfaces that are actually meant to deallocate and zero out at the same time. Only if those fail, it falls back to zeroing out without unmap. We never fall back to a discard operation any more that may or may not result in zeros. Signed-off-by: Kevin Wolf --- block/file-posix.c | 59 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 928b863ced..fe83cbf0eb 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -648,7 +648,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } #endif - bs->supported_zero_flags = s->discard_zeroes ? BDRV_REQ_MAY_UNMAP : 0; + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP; ret = 0; fail: if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) { @@ -1487,6 +1487,35 @@ static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) return -ENOTSUP; } +static ssize_t handle_aiocb_write_zeroes_unmap(RawPosixAIOData *aiocb) +{ + BDRVRawState *s G_GNUC_UNUSED = aiocb->bs->opaque; + int ret; + + /* First try to write zeros and unmap at the same time */ + +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE + ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + aiocb->aio_offset, aiocb->aio_nbytes); + if (ret != -ENOTSUP) { + return ret; + } +#endif + +#ifdef CONFIG_XFS + if (s->is_xfs) { + /* xfs_discard() guarantees that the discarded area reads as all-zero + * afterwards, so we can use it here. */ + return xfs_discard(s, aiocb->aio_offset, aiocb->aio_nbytes); + } +#endif + + /* If we couldn't manage to unmap while guaranteed that the area reads as + * all-zero afterwards, just write zeroes without unmapping */ + ret = handle_aiocb_write_zeroes(aiocb); + return ret; +} + #ifndef HAVE_COPY_FILE_RANGE static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, off_t *out_off, size_t len, unsigned int flags) @@ -1732,6 +1761,9 @@ static int aio_worker(void *arg) case QEMU_AIO_WRITE_ZEROES: ret = handle_aiocb_write_zeroes(aiocb); break; + case QEMU_AIO_WRITE_ZEROES | QEMU_AIO_DISCARD: + ret = handle_aiocb_write_zeroes_unmap(aiocb); + break; case QEMU_AIO_COPY_RANGE: ret = handle_aiocb_copy_range(aiocb); break; @@ -2556,15 +2588,13 @@ static int coroutine_fn raw_co_pwrite_zeroes( int bytes, BdrvRequestFlags flags) { BDRVRawState *s = bs->opaque; + int operation = QEMU_AIO_WRITE_ZEROES; - if (!(flags & BDRV_REQ_MAY_UNMAP)) { - return paio_submit_co(bs, s->fd, offset, NULL, bytes, - QEMU_AIO_WRITE_ZEROES); - } else if (s->discard_zeroes) { - return paio_submit_co(bs, s->fd, offset, NULL, bytes, - QEMU_AIO_DISCARD); + if (flags & BDRV_REQ_MAY_UNMAP) { + operation |= QEMU_AIO_DISCARD; } - return -ENOTSUP; + + return paio_submit_co(bs, s->fd, offset, NULL, bytes, operation); } static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) @@ -3057,20 +3087,19 @@ static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { BDRVRawState *s = bs->opaque; + int operation = QEMU_AIO_WRITE_ZEROES | QEMU_AIO_BLKDEV; int rc; rc = fd_open(bs); if (rc < 0) { return rc; } - if (!(flags & BDRV_REQ_MAY_UNMAP)) { - return paio_submit_co(bs, s->fd, offset, NULL, bytes, - QEMU_AIO_WRITE_ZEROES|QEMU_AIO_BLKDEV); - } else if (s->discard_zeroes) { - return paio_submit_co(bs, s->fd, offset, NULL, bytes, - QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); + + if (flags & BDRV_REQ_MAY_UNMAP) { + operation |= QEMU_AIO_DISCARD; } - return -ENOTSUP; + + return paio_submit_co(bs, s->fd, offset, NULL, bytes, operation); } static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts, From 5a9cb5a97f1e519f249d9ec482d498b78296b51d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Jul 2018 16:07:07 +0200 Subject: [PATCH 2348/2380] block/qapi: Add 'qdev' field to query-blockstats result Like for query-block, the client needs to identify which BlockBackend the returned data is for. Anonymous BlockBackends are identified by the device model they are attached to. Add a 'qdev' field that contains the qdev ID or QOM path of the attached device model. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake --- block/qapi.c | 10 ++++++++++ qapi/block-core.json | 14 ++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/block/qapi.c b/block/qapi.c index e12968fec8..50f867d634 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -597,11 +597,21 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, BlockStatsList *info = g_malloc0(sizeof(*info)); AioContext *ctx = blk_get_aio_context(blk); BlockStats *s; + char *qdev; aio_context_acquire(ctx); s = bdrv_query_bds_stats(blk_bs(blk), true); s->has_device = true; s->device = g_strdup(blk_name(blk)); + + qdev = blk_get_attached_dev_id(blk); + if (qdev && *qdev) { + s->has_qdev = true; + s->qdev = qdev; + } else { + g_free(qdev); + } + bdrv_query_blk_stats(s->stats, blk); aio_context_release(ctx); diff --git a/qapi/block-core.json b/qapi/block-core.json index d40d5ecc3b..5b9084a394 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -866,6 +866,9 @@ # # @node-name: The node name of the device. (Since 2.3) # +# @qdev: The qdev ID, or if no ID is assigned, the QOM path of the block +# device. (since 3.0) +# # @stats: A @BlockDeviceStats for the device. # # @parent: This describes the file block device if it has one. @@ -879,7 +882,7 @@ # Since: 0.14.0 ## { 'struct': 'BlockStats', - 'data': {'*device': 'str', '*node-name': 'str', + 'data': {'*device': 'str', '*qdev': 'str', '*node-name': 'str', 'stats': 'BlockDeviceStats', '*parent': 'BlockStats', '*backing': 'BlockStats'} } @@ -941,7 +944,8 @@ # "idle_time_ns":2953431879, # "account_invalid":true, # "account_failed":false -# } +# }, +# "qdev": "/machine/unattached/device[23]" # }, # { # "device":"ide1-cd0", @@ -959,7 +963,8 @@ # "wr_merged":0, # "account_invalid":false, # "account_failed":false -# } +# }, +# "qdev": "/machine/unattached/device[24]" # }, # { # "device":"floppy0", @@ -977,7 +982,8 @@ # "wr_merged":0, # "account_invalid":false, # "account_failed":false -# } +# }, +# "qdev": "/machine/unattached/device[16]" # }, # { # "device":"sd0", From 567dcb31f23657fb71060067b0b1c9ac29110d16 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Jul 2018 16:09:25 +0200 Subject: [PATCH 2349/2380] block/qapi: Include anonymous BBs in query-blockstats Consistent with query-block, query-blockstats should not only include named BlockBackends, but also those that are anonymous, but belong to a device model. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake --- block/qapi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/block/qapi.c b/block/qapi.c index 50f867d634..339727f0f4 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -593,12 +593,16 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, p_next = &info->next; } } else { - for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { + for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { BlockStatsList *info = g_malloc0(sizeof(*info)); AioContext *ctx = blk_get_aio_context(blk); BlockStats *s; char *qdev; + if (!*blk_name(blk) && !blk_get_attached_dev(blk)) { + continue; + } + aio_context_acquire(ctx); s = bdrv_query_bds_stats(blk_bs(blk), true); s->has_device = true; From 1239ac241fe170bb9fcf0be74bfff04f6f1c2560 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Jul 2018 16:11:57 +0200 Subject: [PATCH 2350/2380] qemu-iotests: Test query-blockstats with -drive and -blockdev Make sure that query-blockstats returns information for every BlockBackend that is named or attached to a device model (or both). Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- tests/qemu-iotests/227 | 101 ++++++++++++++++++ tests/qemu-iotests/227.out | 205 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 307 insertions(+) create mode 100755 tests/qemu-iotests/227 create mode 100644 tests/qemu-iotests/227.out diff --git a/tests/qemu-iotests/227 b/tests/qemu-iotests/227 new file mode 100755 index 0000000000..9a5f7f9f14 --- /dev/null +++ b/tests/qemu-iotests/227 @@ -0,0 +1,101 @@ +#!/bin/bash +# +# Test query-blockstats with different ways to create a BB +# +# Copyright (C) 2018 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 . +# + +# creator +owner=kwolf@redhat.com + +seq=$(basename $0) +echo "QA output created by $seq" + +here=$PWD +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 + +function do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp-pretty stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ + | _filter_generated_node_ids +} + +echo +echo '=== blockstats with -drive if=virtio ===' +echo + +run_qemu -drive driver=null-co,if=virtio < Date: Fri, 27 Jul 2018 14:54:06 +0100 Subject: [PATCH 2351/2380] arm/smmuv3: Fix missing VMSD terminator The 'vmstate_smmuv3_queue' is missing the end-of-list marker. Fixes: 10a83cb9887 Signed-off-by: Dr. David Alan Gilbert Message-id: 20180727135406.15132-1-dgilbert@redhat.com Reviewed-by: Peter Maydell [PMM: dropped stray blank line] Signed-off-by: Peter Maydell --- hw/arm/smmuv3.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 39fbcbf577..bb6a24e9b8 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1414,6 +1414,7 @@ static const VMStateDescription vmstate_smmuv3_queue = { VMSTATE_UINT32(prod, SMMUQueue), VMSTATE_UINT32(cons, SMMUQueue), VMSTATE_UINT8(log2size, SMMUQueue), + VMSTATE_END_OF_LIST(), }, }; From d1fb710a9b88fa6e11476ba7536b1c5cc2a55b19 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 25 Jul 2018 13:30:00 +0200 Subject: [PATCH 2352/2380] hw/arm/sysbus-fdt: Fix assertion in copy_properties_from_host() When copy_properties_from_host() ignores the error for an optional property, it frees the error, but fails to reset it. Hence if two or more optional properties are missing, an assertion is triggered: util/error.c:57: error_setv: Assertion `*errp == NULL' failed. Fis this by resetting err to NULL after ignoring the error. Fixes: 9481cf2e5f2f2bb6 ("hw/arm/sysbus-fdt: helpers for clock node generation") Signed-off-by: Geert Uytterhoeven Message-id: 20180725113000.11014-1-geert+renesas@glider.be Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/sysbus-fdt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c index 0d4c75702c..43d6a7bb48 100644 --- a/hw/arm/sysbus-fdt.c +++ b/hw/arm/sysbus-fdt.c @@ -107,6 +107,7 @@ static void copy_properties_from_host(HostProperty *props, int nb_props, /* mandatory property not found: bail out */ exit(1); } + err = NULL; } } } From 942566ffc189ad1ea09d350860d08d95597537ca Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 27 Jul 2018 12:38:53 +0100 Subject: [PATCH 2353/2380] armv7m_nvic: Fix m-security subsection name The vmstate save/load code insists that subsections of a VMState must have names which include their parent VMState's name as a leading substring. Unfortunately it neither documents this nor checks it on device init or state save, but instead fails state load with a confusing error message ("Missing section footer for armv7m_nvic"). Fix the name of the m-security subsection of the NVIC, so that state save/load works correctly for the security-enabled NVIC. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20180727113854.20283-2-peter.maydell@linaro.org --- hw/intc/armv7m_nvic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 6be7fc5266..cd1e7f1729 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2064,7 +2064,7 @@ static int nvic_security_post_load(void *opaque, int version_id) } static const VMStateDescription vmstate_nvic_security = { - .name = "nvic/m-security", + .name = "armv7m_nvic/m-security", .version_id = 1, .minimum_version_id = 1, .needed = nvic_security_needed, From 984b0c100f74eb4a32b3d44eb80963ee82ebfba5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 27 Jul 2018 12:38:54 +0100 Subject: [PATCH 2354/2380] hw/arm/iotkit: Fix IRQ number for timer1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A cut-and-paste error meant we were incorrectly wiring up the timer1 IRQ to IRQ3. IRQ3 is the interrupt for timer0 -- move timer0 to IRQ4 where it belongs. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180727113854.20283-3-peter.maydell@linaro.org --- hw/arm/iotkit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index c76d3ed743..8cadc8b160 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -382,7 +382,7 @@ static void iotkit_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); + qdev_get_gpio_in(DEVICE(&s->armv7m), 4)); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); if (err) { From 218fe5ce402986cf2cf246d65bf71de9f3508fe3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Jul 2018 16:36:16 +0100 Subject: [PATCH 2355/2380] hw/misc/tz-mpc: Zero the LUT on initialization, not just reset In the tz-mpc device we allocate a data block for the LUT, which we then clear to zero in the device's reset method. This is conceptually fine, but unfortunately results in a valgrind complaint about use of uninitialized data on startup: ==30906== Conditional jump or move depends on uninitialised value(s) ==30906== at 0x503609: tz_mpc_translate (tz-mpc.c:439) ==30906== by 0x3F3D90: address_space_translate_iommu (exec.c:511) ==30906== by 0x3F3FF8: flatview_do_translate (exec.c:584) ==30906== by 0x3F4292: flatview_translate (exec.c:644) ==30906== by 0x3F2120: address_space_translate (memory.h:1962) ==30906== by 0x3FB753: address_space_ldl_internal (memory_ldst.inc.c:36) ==30906== by 0x3FB8A6: address_space_ldl (memory_ldst.inc.c:80) ==30906== by 0x619037: ldl_phys (memory_ldst_phys.inc.h:25) ==30906== by 0x61985D: arm_cpu_reset (cpu.c:255) ==30906== by 0x98791B: cpu_reset (cpu.c:249) ==30906== by 0x57FFDB: armv7m_reset (armv7m.c:265) ==30906== by 0x7B1775: qemu_devices_reset (reset.c:69) This is because of a reset ordering problem -- the TZ MPC resets after the CPU, but an M-profile CPU's reset function includes memory loads to get the initial PC and SP, which then go through an MPC that hasn't yet been reset. The simplest fix for this is to zero the LUT when we initialize the data, which will result in the MPC's translate function giving the right answers for these early memory accesses. Reported-by: Thomas Huth Signed-off-by: Peter Maydell Tested-by: Thomas Huth Message-id: 20180724153616.32352-1-peter.maydell@linaro.org --- hw/misc/tz-mpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index 8316079b4b..e0c58ba37e 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -547,7 +547,7 @@ static void tz_mpc_realize(DeviceState *dev, Error **errp) address_space_init(&s->blocked_io_as, &s->blocked_io, "tz-mpc-blocked-io"); - s->blk_lut = g_new(uint32_t, s->blk_max); + s->blk_lut = g_new0(uint32_t, s->blk_max); } static int tz_mpc_post_load(void *opaque, int version_id) From 0261fb805c00a6f97d143235e7b06b0906bdf898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 27 Jul 2018 10:23:11 -0300 Subject: [PATCH 2356/2380] target/arm: Remove duplicate 'host' entry in '-cpu ?' output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 86f0a186d6f the TYPE_ARM_HOST_CPU is only compiled when CONFIG_KVM is enabled. Remove the now redundant special-case introduced in a96c0514ab7, to avoid: $ qemu-system-aarch64 -machine virt -cpu \? | fgrep host host host (only available in KVM mode) Signed-off-by: Philippe Mathieu-Daudé Message-id: 20180727132311.2777-1-f4bug@amsat.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/helper.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 22d812240a..66afb08ee0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5591,12 +5591,6 @@ void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf) (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, arm_cpu_list_entry, &s); g_slist_free(list); -#ifdef CONFIG_KVM - /* The 'host' CPU type is dynamically registered only if KVM is - * enabled, so we have to special-case it here: - */ - (*cpu_fprintf)(f, " host (only available in KVM mode)\n"); -#endif } static void arm_cpu_add_definition(gpointer data, gpointer user_data) From 408e5ace517ff18c9c7cd918fc93358162e6e26d Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 30 Jul 2018 16:09:26 +0200 Subject: [PATCH 2357/2380] s390x/sclp: fix maxram calculation We clamp down ram_size to match the sclp increment size. We do not do the same for maxram_size, which means for large guests with some sizes (e.g. -m 50000) maxram_size differs from ram_size. This can break other code (e.g. CMMA migration) which uses maxram_size to calculate the number of pages and then throws some errors. Fixes: 82fab5c5b90e468f3e9d54c ("s390x/sclp: remove memory hotplug support") Signed-off-by: Christian Borntraeger CC: qemu-stable@nongnu.org CC: David Hildenbrand Message-Id: <1532959766-53343-1-git-send-email-borntraeger@de.ibm.com> Reviewed-by: David Hildenbrand Signed-off-by: Cornelia Huck --- hw/s390x/sclp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index bd2a024efd..4510a800cb 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -320,6 +320,7 @@ static void sclp_memory_init(SCLPDevice *sclp) initial_mem = initial_mem >> increment_size << increment_size; machine->ram_size = initial_mem; + machine->maxram_size = initial_mem; /* let's propagate the changed ram size into the global variable. */ ram_size = initial_mem; } From 38138fab93584ad3560ddfcd70efbd5bb6b4a6f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 30 Jul 2018 14:43:20 +0100 Subject: [PATCH 2358/2380] linux-user/mmap.c: handle invalid len maps correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've slightly re-organised the check to more closely match the sequence that the kernel uses in do_mmap(). We check for both the zero case (EINVAL) and the overflow length case (ENOMEM). Signed-off-by: Alex Bennée Cc: umarcor <1783362@bugs.launchpad.net> Reviewed-by: Laurent Vivier Message-Id: <20180730134321.19898-2-alex.bennee@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/mmap.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index d0c50e4888..41e0983ce8 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -391,14 +391,23 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, } #endif + if (!len) { + errno = EINVAL; + goto fail; + } + + /* Also check for overflows... */ + len = TARGET_PAGE_ALIGN(len); + if (!len) { + errno = ENOMEM; + goto fail; + } + if (offset & ~TARGET_PAGE_MASK) { errno = EINVAL; goto fail; } - len = TARGET_PAGE_ALIGN(len); - if (len == 0) - goto the_end; real_start = start & qemu_host_page_mask; host_offset = offset & qemu_host_page_mask; From 28cbb997d66e4d1904a231bef1ce15c2cbb6bf73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 30 Jul 2018 14:43:21 +0100 Subject: [PATCH 2359/2380] tests: add check_invalid_maps to test-mmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a test to make sure we fail properly for a 0 length mmap. There are most likely other failure conditions we should also check. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Cc: umarcor <1783362@bugs.launchpad.net> Message-Id: <20180730134321.19898-3-alex.bennee@linaro.org> Signed-off-by: Laurent Vivier --- tests/tcg/multiarch/test-mmap.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/tcg/multiarch/test-mmap.c b/tests/tcg/multiarch/test-mmap.c index 5c0afe6e49..11d0e777b1 100644 --- a/tests/tcg/multiarch/test-mmap.c +++ b/tests/tcg/multiarch/test-mmap.c @@ -27,7 +27,7 @@ #include #include #include - +#include #include #define D(x) @@ -435,6 +435,25 @@ void checked_write(int fd, const void *buf, size_t count) fail_unless(rc == count); } +void check_invalid_mmaps(void) +{ + unsigned char *addr; + + /* Attempt to map a zero length page. */ + addr = mmap(NULL, 0, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + fprintf(stdout, "%s addr=%p", __func__, (void *)addr); + fail_unless(addr == MAP_FAILED); + fail_unless(errno == EINVAL); + + /* Attempt to map a over length page. */ + addr = mmap(NULL, -4, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + fprintf(stdout, "%s addr=%p", __func__, (void *)addr); + fail_unless(addr == MAP_FAILED); + fail_unless(errno == ENOMEM); + + fprintf(stdout, " passed\n"); +} + int main(int argc, char **argv) { char tempname[] = "/tmp/.cmmapXXXXXX"; @@ -476,6 +495,7 @@ int main(int argc, char **argv) check_file_fixed_mmaps(); check_file_fixed_eof_mmaps(); check_file_unfixed_eof_mmaps(); + check_invalid_mmaps(); /* Fails at the moment. */ /* check_aligned_anonymous_fixed_mmaps_collide_with_host(); */ From 5d9f3ea0817215ad4baac5aa30414e9ebbaaf0d6 Mon Sep 17 00:00:00 2001 From: Shivaprasad G Bhat Date: Tue, 31 Jul 2018 11:12:24 +0530 Subject: [PATCH 2360/2380] linux-user: ppc64: don't use volatile register during safe_syscall r11 is a volatile register on PPC as per calling conventions. The safe_syscall code uses it to check if the signal_pending is set during the safe_syscall. When a syscall is interrupted on return from signal handling, the r11 might be corrupted before we retry the syscall leading to a crash. The registers r0-r13 are not to be used here as they have volatile/designated/reserved usages. Change the code to use r14 which is non-volatile. Use SP+16 which is a slot for LR, for save/restore of previous value of r14. SP+16 can be used, as LR is preserved across the syscall. Steps to reproduce: On PPC host, issue `qemu-x86_64 /usr/bin/cc -E -` Attempt Ctrl-C, the issue is reproduced. Reference: https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#REG https://openpowerfoundation.org/wp-content/uploads/2016/03/ABI64BitOpenPOWERv1.1_16July2015_pub4.pdf Signed-off-by: Shivaprasad G Bhat Tested-by: Richard Henderson Tested-by: Laurent Vivier Reviewed-by: Richard Henderson Reviewed-by: Laurent Vivier Message-Id: <153301568965.30312.10498134581068746871.stgit@dhcp-9-109-246-16> Signed-off-by: Laurent Vivier --- linux-user/host/ppc64/safe-syscall.inc.S | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/linux-user/host/ppc64/safe-syscall.inc.S b/linux-user/host/ppc64/safe-syscall.inc.S index d30050a67c..8ed73a5b86 100644 --- a/linux-user/host/ppc64/safe-syscall.inc.S +++ b/linux-user/host/ppc64/safe-syscall.inc.S @@ -49,7 +49,9 @@ safe_syscall_base: * and returns the result in r3 * Shuffle everything around appropriately. */ - mr 11, 3 /* signal_pending */ + std 14, 16(1) /* Preserve r14 in SP+16 */ + .cfi_offset 14, 16 + mr 14, 3 /* signal_pending */ mr 0, 4 /* syscall number */ mr 3, 5 /* syscall arguments */ mr 4, 6 @@ -67,12 +69,13 @@ safe_syscall_base: */ safe_syscall_start: /* if signal_pending is non-zero, don't do the call */ - lwz 12, 0(11) + lwz 12, 0(14) cmpwi 0, 12, 0 bne- 0f sc safe_syscall_end: /* code path when we did execute the syscall */ + ld 14, 16(1) /* restore r14 to its original value */ bnslr+ /* syscall failed; return negative errno */ @@ -81,6 +84,7 @@ safe_syscall_end: /* code path when we didn't execute the syscall */ 0: addi 3, 0, -TARGET_ERESTARTSYS + ld 14, 16(1) /* restore r14 to its orginal value */ blr .cfi_endproc From 9a1054061c62311f6efcd88e4df0aa5203c7b39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 31 Jul 2018 17:01:44 +0200 Subject: [PATCH 2361/2380] monitor: temporary fix for dead-lock on event recursion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With a Spice port chardev, it is possible to reenter monitor_qapi_event_queue() (when the client disconnects for example). This will dead-lock on monitor_lock. Instead, use some TLS variables to check for recursion and queue the events. Fixes: (gdb) bt #0 0x00007fa69e7217fd in __lll_lock_wait () at /lib64/libpthread.so.0 #1 0x00007fa69e71acf4 in pthread_mutex_lock () at /lib64/libpthread.so.0 #2 0x0000563303567619 in qemu_mutex_lock_impl (mutex=0x563303d3e220 , file=0x5633036589a8 "/home/elmarco/src/qq/monitor.c", line=645) at /home/elmarco/src/qq/util/qemu-thread-posix.c:66 #3 0x0000563302fa6c25 in monitor_qapi_event_queue (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x56330602bde0, errp=0x7ffc6ab5e728) at /home/elmarco/src/qq/monitor.c:645 #4 0x0000563303549aca in qapi_event_send_spice_disconnected (server=0x563305afd630, client=0x563305745360, errp=0x563303d8d0f0 ) at qapi/qapi-events-ui.c:149 #5 0x00005633033e600f in channel_event (event=3, info=0x5633061b0050) at /home/elmarco/src/qq/ui/spice-core.c:235 #6 0x00007fa69f6c86bb in reds_handle_channel_event (reds=, event=3, info=0x5633061b0050) at reds.c:316 #7 0x00007fa69f6b193b in main_dispatcher_self_handle_channel_event (info=0x5633061b0050, event=3, self=0x563304e088c0) at main-dispatcher.c:197 #8 0x00007fa69f6b193b in main_dispatcher_channel_event (self=0x563304e088c0, event=event@entry=3, info=0x5633061b0050) at main-dispatcher.c:197 #9 0x00007fa69f6d0833 in red_stream_push_channel_event (s=s@entry=0x563305ad8f50, event=event@entry=3) at red-stream.c:414 #10 0x00007fa69f6d086b in red_stream_free (s=0x563305ad8f50) at red-stream.c:388 #11 0x00007fa69f6b7ddc in red_channel_client_finalize (object=0x563304df2360) at red-channel-client.c:347 #12 0x00007fa6a56b7fb9 in g_object_unref () at /lib64/libgobject-2.0.so.0 #13 0x00007fa69f6ba212 in red_channel_client_push (rcc=0x563304df2360) at red-channel-client.c:1341 #14 0x00007fa69f68b259 in red_char_device_send_msg_to_client (client=, msg=0x5633059b6310, dev=0x563304e08bc0) at char-device.c:305 #15 0x00007fa69f68b259 in red_char_device_send_msg_to_clients (msg=0x5633059b6310, dev=0x563304e08bc0) at char-device.c:305 #16 0x00007fa69f68b259 in red_char_device_read_from_device (dev=0x563304e08bc0) at char-device.c:353 #17 0x000056330317d01d in spice_chr_write (chr=0x563304cafe20, buf=0x563304cc50b0 "{\"timestamp\": {\"seconds\": 1532944763, \"microseconds\": 326636}, \"event\": \"SHUTDOWN\", \"data\": {\"guest\": false}}\r\n", len=111) at /home/elmarco/src/qq/chardev/spice.c:199 #18 0x00005633034deee7 in qemu_chr_write_buffer (s=0x563304cafe20, buf=0x563304cc50b0 "{\"timestamp\": {\"seconds\": 1532944763, \"microseconds\": 326636}, \"event\": \"SHUTDOWN\", \"data\": {\"guest\": false}}\r\n", len=111, offset=0x7ffc6ab5ea70, write_all=false) at /home/elmarco/src/qq/chardev/char.c:112 #19 0x00005633034df054 in qemu_chr_write (s=0x563304cafe20, buf=0x563304cc50b0 "{\"timestamp\": {\"seconds\": 1532944763, \"microseconds\": 326636}, \"event\": \"SHUTDOWN\", \"data\": {\"guest\": false}}\r\n", len=111, write_all=false) at /home/elmarco/src/qq/chardev/char.c:147 #20 0x00005633034e1e13 in qemu_chr_fe_write (be=0x563304dbb800, buf=0x563304cc50b0 "{\"timestamp\": {\"seconds\": 1532944763, \"microseconds\": 326636}, \"event\": \"SHUTDOWN\", \"data\": {\"guest\": false}}\r\n", len=111) at /home/elmarco/src/qq/chardev/char-fe.c:42 #21 0x0000563302fa6334 in monitor_flush_locked (mon=0x563304dbb800) at /home/elmarco/src/qq/monitor.c:425 #22 0x0000563302fa6520 in monitor_puts (mon=0x563304dbb800, str=0x563305de7e9e "") at /home/elmarco/src/qq/monitor.c:468 #23 0x0000563302fa680c in qmp_send_response (mon=0x563304dbb800, rsp=0x563304df5730) at /home/elmarco/src/qq/monitor.c:517 #24 0x0000563302fa6905 in qmp_queue_response (mon=0x563304dbb800, rsp=0x563304df5730) at /home/elmarco/src/qq/monitor.c:538 #25 0x0000563302fa6b5b in monitor_qapi_event_emit (event=QAPI_EVENT_SHUTDOWN, qdict=0x563304df5730) at /home/elmarco/src/qq/monitor.c:624 #26 0x0000563302fa6c4b in monitor_qapi_event_queue (event=QAPI_EVENT_SHUTDOWN, qdict=0x563304df5730, errp=0x7ffc6ab5ed00) at /home/elmarco/src/qq/monitor.c:649 #27 0x0000563303548cce in qapi_event_send_shutdown (guest=false, errp=0x563303d8d0f0 ) at qapi/qapi-events-run-state.c:58 #28 0x000056330313bcd7 in main_loop_should_exit () at /home/elmarco/src/qq/vl.c:1822 #29 0x000056330313bde3 in main_loop () at /home/elmarco/src/qq/vl.c:1862 #30 0x0000563303143781 in main (argc=3, argv=0x7ffc6ab5f068, envp=0x7ffc6ab5f088) at /home/elmarco/src/qq/vl.c:4644 Note that error report is now moved to the first caller, which may receive an error for a recursed event. This is probably fine (95% of callers use &error_abort, the rest have NULL error and ignore it) Signed-off-by: Marc-André Lureau Message-Id: <20180731150144.14022-1-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [*_no_recurse renamed to *_no_reenter, local variables reordered] Signed-off-by: Markus Armbruster --- monitor.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/monitor.c b/monitor.c index f75027b09e..77861e96af 100644 --- a/monitor.c +++ b/monitor.c @@ -633,7 +633,7 @@ static void monitor_qapi_event_handler(void *opaque); * applying any rate limiting if required. */ static void -monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) +monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict) { MonitorQAPIEventConf *evconf; MonitorQAPIEventState *evstate; @@ -688,6 +688,48 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) qemu_mutex_unlock(&monitor_lock); } +static void +monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) +{ + /* + * monitor_qapi_event_queue_no_reenter() is not reentrant: it + * would deadlock on monitor_lock. Work around by queueing + * events in thread-local storage. + * TODO: remove this, make it re-enter safe. + */ + typedef struct MonitorQapiEvent { + QAPIEvent event; + QDict *qdict; + QSIMPLEQ_ENTRY(MonitorQapiEvent) entry; + } MonitorQapiEvent; + static __thread QSIMPLEQ_HEAD(, MonitorQapiEvent) event_queue; + static __thread bool reentered; + MonitorQapiEvent *ev; + + if (!reentered) { + QSIMPLEQ_INIT(&event_queue); + } + + ev = g_new(MonitorQapiEvent, 1); + ev->qdict = qobject_ref(qdict); + ev->event = event; + QSIMPLEQ_INSERT_TAIL(&event_queue, ev, entry); + if (reentered) { + return; + } + + reentered = true; + + while ((ev = QSIMPLEQ_FIRST(&event_queue)) != NULL) { + QSIMPLEQ_REMOVE_HEAD(&event_queue, entry); + monitor_qapi_event_queue_no_reenter(ev->event, ev->qdict); + qobject_unref(ev->qdict); + g_free(ev); + } + + reentered = false; +} + /* * This function runs evconf->rate ns after sending a throttled * event. From f7502360397d291be04bc040e9f96c92ff2d8030 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 31 Jul 2018 19:30:17 +0100 Subject: [PATCH 2362/2380] Update version for v3.0.0-rc3 release Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c4eae17987..9613415721 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.92 +2.12.93 From 1069a3c6e1176001116116629427550f138d68a4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 17 Jul 2018 16:51:54 +0200 Subject: [PATCH 2363/2380] hw/misc/macio: Fix device introspection problems in macio devices Valgrind reports an error when introspecting the macio devices, e.g.: echo "{'execute':'qmp_capabilities'} {'execute':'device-list-properties'," \ "'arguments':{'typename':'macio-newworld'}}" \ "{'execute': 'human-monitor-command', " \ "'arguments': {'command-line': 'info qtree'}}" | \ valgrind -q ppc64-softmmu/qemu-system-ppc64 -M none,accel=qtest -qmp stdio [...] ==30768== Invalid read of size 8 ==30768== at 0x5BC1EA: qdev_print (qdev-monitor.c:686) ==30768== by 0x5BC1EA: qbus_print (qdev-monitor.c:719) ==30768== by 0x43E458: handle_hmp_command (monitor.c:3446) [...] Use the new function sysbus_init_child_obj() to initialize the objects here, to get the reference counting of the objects right, so that they are cleaned up correctly when the parent gets removed. Signed-off-by: Thomas Huth Signed-off-by: David Gibson --- hw/misc/macio/cuda.c | 5 ++--- hw/misc/macio/macio.c | 24 ++++++++---------------- hw/misc/macio/pmu.c | 5 ++--- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 9651ed9744..c4f7a2f39b 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -554,9 +554,8 @@ static void cuda_init(Object *obj) CUDAState *s = CUDA(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - object_initialize(&s->mos6522_cuda, sizeof(s->mos6522_cuda), - TYPE_MOS6522_CUDA); - qdev_set_parent_bus(DEVICE(&s->mos6522_cuda), sysbus_get_default()); + sysbus_init_child_obj(obj, "mos6522-cuda", &s->mos6522_cuda, + sizeof(s->mos6522_cuda), TYPE_MOS6522_CUDA); memory_region_init_io(&s->mem, obj, &mos6522_cuda_ops, s, "cuda", 0x2000); sysbus_init_mmio(sbd, &s->mem); diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index d135e3bc2b..52aa3775f4 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -209,14 +209,11 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size, int index) { - gchar *name; + gchar *name = g_strdup_printf("ide[%i]", index); - object_initialize(ide, ide_size, TYPE_MACIO_IDE); - qdev_set_parent_bus(DEVICE(ide), sysbus_get_default()); + sysbus_init_child_obj(OBJECT(s), name, ide, ide_size, TYPE_MACIO_IDE); memory_region_add_subregion(&s->bar, 0x1f000 + ((index + 1) * 0x1000), &ide->mem); - name = g_strdup_printf("ide[%i]", index); - object_property_add_child(OBJECT(s), name, OBJECT(ide), NULL); g_free(name); } @@ -232,9 +229,7 @@ static void macio_oldworld_init(Object *obj) qdev_prop_allow_set_link_before_realize, 0, NULL); - object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); - qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); - object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); + sysbus_init_child_obj(obj, "cuda", &s->cuda, sizeof(s->cuda), TYPE_CUDA); object_initialize(&os->nvram, sizeof(os->nvram), TYPE_MACIO_NVRAM); dev = DEVICE(&os->nvram); @@ -390,8 +385,8 @@ static void macio_newworld_init(Object *obj) qdev_prop_allow_set_link_before_realize, 0, NULL); - object_initialize(&ns->gpio, sizeof(ns->gpio), TYPE_MACIO_GPIO); - qdev_set_parent_bus(DEVICE(&ns->gpio), sysbus_get_default()); + sysbus_init_child_obj(obj, "gpio", &ns->gpio, sizeof(ns->gpio), + TYPE_MACIO_GPIO); for (i = 0; i < 2; i++) { macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i); @@ -404,13 +399,10 @@ static void macio_instance_init(Object *obj) memory_region_init(&s->bar, obj, "macio", 0x80000); - object_initialize(&s->dbdma, sizeof(s->dbdma), TYPE_MAC_DBDMA); - qdev_set_parent_bus(DEVICE(&s->dbdma), sysbus_get_default()); - object_property_add_child(obj, "dbdma", OBJECT(&s->dbdma), NULL); + sysbus_init_child_obj(obj, "dbdma", &s->dbdma, sizeof(s->dbdma), + TYPE_MAC_DBDMA); - object_initialize(&s->escc, sizeof(s->escc), TYPE_ESCC); - qdev_set_parent_bus(DEVICE(&s->escc), sysbus_get_default()); - object_property_add_child(obj, "escc", OBJECT(&s->escc), NULL); + sysbus_init_child_obj(obj, "escc", &s->escc, sizeof(s->escc), TYPE_ESCC); } static const VMStateDescription vmstate_macio_oldworld = { diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index e246b0fd41..d25344f888 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -770,9 +770,8 @@ static void pmu_init(Object *obj) qdev_prop_allow_set_link_before_realize, 0, NULL); - object_initialize(&s->mos6522_pmu, sizeof(s->mos6522_pmu), - TYPE_MOS6522_PMU); - qdev_set_parent_bus(DEVICE(&s->mos6522_pmu), sysbus_get_default()); + sysbus_init_child_obj(obj, "mos6522-pmu", &s->mos6522_pmu, + sizeof(s->mos6522_pmu), TYPE_MOS6522_PMU); memory_region_init_io(&s->mem, obj, &mos6522_pmu_ops, s, "via-pmu", 0x2000); From 6484ab3dffadc79020a71376010f517d60b81b83 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 31 Jul 2018 13:08:00 +0200 Subject: [PATCH 2364/2380] sam460ex: Fix PCI interrupts with multiple devices The four interrupts of the PCI bus are connected to the same UIC pin on the real Sam460ex. Evidence for this can be found in the UBoot source for the Sam460ex in the Sam460ex.c file where PCI_INTERRUPT_LINE is written. Change the ppc440_pcix model to behave more like this. This fixes the problem that can be observed when adding further PCI cards that got their interrupt rotated to other interrupts than PCI INT A. In particular, the bug was observed with an additional OHCI PCI card or an ES1370 sound device. Signed-off-by: Sebastian Bauer Signed-off-by: BALATON Zoltan Tested-by: Sebastian Bauer Signed-off-by: David Gibson --- hw/ppc/ppc440_pcix.c | 21 ++++++++------------- hw/ppc/sam460ex.c | 6 ++---- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c index d8af04b70f..64ed07afa6 100644 --- a/hw/ppc/ppc440_pcix.c +++ b/hw/ppc/ppc440_pcix.c @@ -57,7 +57,7 @@ typedef struct PPC440PCIXState { struct PLBOutMap pom[PPC440_PCIX_NR_POMS]; struct PLBInMap pim[PPC440_PCIX_NR_PIMS]; uint32_t sts; - qemu_irq irq[PCI_NUM_PINS]; + qemu_irq irq; AddressSpace bm_as; MemoryRegion bm; @@ -418,21 +418,20 @@ static void ppc440_pcix_reset(DeviceState *dev) * This may need further refactoring for other boards. */ static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num) { - int slot = pci_dev->devfn >> 3; - trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, slot); - return slot - 1; + trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, 0); + return 0; } static void ppc440_pcix_set_irq(void *opaque, int irq_num, int level) { - qemu_irq *pci_irqs = opaque; + qemu_irq *pci_irq = opaque; trace_ppc440_pcix_set_irq(irq_num); if (irq_num < 0) { error_report("%s: PCI irq %d", __func__, irq_num); return; } - qemu_set_irq(pci_irqs[irq_num], level); + qemu_set_irq(*pci_irq, level); } static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn) @@ -471,19 +470,15 @@ static int ppc440_pcix_initfn(SysBusDevice *dev) { PPC440PCIXState *s; PCIHostState *h; - int i; h = PCI_HOST_BRIDGE(dev); s = PPC440_PCIX_HOST_BRIDGE(dev); - for (i = 0; i < ARRAY_SIZE(s->irq); i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - + sysbus_init_irq(dev, &s->irq); memory_region_init(&s->busmem, OBJECT(dev), "pci bus memory", UINT64_MAX); h->bus = pci_register_root_bus(DEVICE(dev), NULL, ppc440_pcix_set_irq, - ppc440_pcix_map_irq, s->irq, &s->busmem, - get_system_io(), PCI_DEVFN(0, 0), 4, TYPE_PCI_BUS); + ppc440_pcix_map_irq, &s->irq, &s->busmem, + get_system_io(), PCI_DEVFN(0, 0), 1, TYPE_PCI_BUS); s->dev = pci_create_simple(h->bus, PCI_DEVFN(0, 0), "ppc4xx-host-bridge"); diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 0999efcc1e..9c77183006 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -515,10 +515,8 @@ static void sam460ex_init(MachineState *machine) /* PCI bus */ ppc460ex_pcie_init(env); - /* FIXME: is this correct? */ - dev = sysbus_create_varargs("ppc440-pcix-host", 0xc0ec00000, - uic[1][0], uic[1][20], uic[1][21], uic[1][22], - NULL); + /* All PCI irqs are connected to the same UIC pin (cf. UBoot source) */ + dev = sysbus_create_simple("ppc440-pcix-host", 0xc0ec00000, uic[1][0]); pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); if (!pci_bus) { error_report("couldn't create PCI controller!"); From 388a86df9c59334e94ede099526509d8f466b0c8 Mon Sep 17 00:00:00 2001 From: Tiwei Bie Date: Fri, 20 Jul 2018 16:36:44 +0800 Subject: [PATCH 2365/2380] vhost: check region type before casting Check region type first before casting the memory region to IOMMUMemoryRegion. Otherwise QEMU will abort with below error message when casting non-IOMMU memory region: vhost_iommu_region_add: Object 0x561f28bce4f0 is not an instance of type qemu:iommu-memory-region Fixes: cb1efcf462a2 ("iommu: Add IOMMU index argument to notifier APIs") Cc: Peter Maydell Signed-off-by: Tiwei Bie Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index b129cb9ddd..d4cb5894a8 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -663,12 +663,14 @@ static void vhost_iommu_region_add(MemoryListener *listener, struct vhost_iommu *iommu; Int128 end; int iommu_idx; - IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); + IOMMUMemoryRegion *iommu_mr; if (!memory_region_is_iommu(section->mr)) { return; } + iommu_mr = IOMMU_MEMORY_REGION(section->mr); + iommu = g_malloc0(sizeof(*iommu)); end = int128_add(int128_make64(section->offset_within_region), section->size); From 16b4226363aad6f0bb4589ed8a744df019cd2b1d Mon Sep 17 00:00:00 2001 From: Dou Liyang Date: Tue, 10 Jul 2018 16:58:01 +0800 Subject: [PATCH 2366/2380] hw/acpi-build: Add a check for memory-less NUMA nodes Currently, Qemu ACPI builder doesn't consider the memory-less NUMA nodes, eg: -m 4G,slots=4,maxmem=8G \ -numa node,nodeid=0 \ -numa node,nodeid=1,mem=2G \ -numa node,nodeid=2,mem=2G \ -numa node,nodeid=3\ Guest Linux will report [ 0.000000] ACPI: SRAT: Node 0 PXM 0 [mem 0x00000000-0xffffffffffffffff] [ 0.000000] ACPI: SRAT: Node 1 PXM 1 [mem 0x00000000-0x0009ffff] [ 0.000000] ACPI: SRAT: Node 1 PXM 1 [mem 0x00100000-0x7fffffff] [ 0.000000] ACPI: SRAT: Node 2 PXM 2 [mem 0x80000000-0xbfffffff] [ 0.000000] ACPI: SRAT: Node 2 PXM 2 [mem 0x100000000-0x13fffffff] [ 0.000000] ACPI: SRAT: Node 3 PXM 3 [mem 0x140000000-0x13fffffff] [ 0.000000] ACPI: SRAT: Node 3 PXM 3 [mem 0x140000000-0x33fffffff] hotplug [mem 0x00000000-0xffffffffffffffff] and [mem 0x140000000-0x13fffffff] are bogus. Add a check to avoid building srat memory for memory-less NUMA nodes, also update the test file. Now the info in guest linux will be [ 0.000000] ACPI: SRAT: Node 1 PXM 1 [mem 0x00000000-0x0009ffff] [ 0.000000] ACPI: SRAT: Node 1 PXM 1 [mem 0x00100000-0x7fffffff] [ 0.000000] ACPI: SRAT: Node 2 PXM 2 [mem 0x80000000-0xbfffffff] [ 0.000000] ACPI: SRAT: Node 2 PXM 2 [mem 0x100000000-0x13fffffff] [ 0.000000] ACPI: SRAT: Node 3 PXM 3 [mem 0x140000000-0x33fffffff] hotplug Signed-off-by: Dou Liyang Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 9e8350c55d..c584642e4e 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2392,9 +2392,12 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) mem_len = next_base - pcms->below_4g_mem_size; next_base = mem_base + mem_len; } - numamem = acpi_data_push(table_data, sizeof *numamem); - build_srat_memory(numamem, mem_base, mem_len, i - 1, - MEM_AFFINITY_ENABLED); + + if (mem_len > 0) { + numamem = acpi_data_push(table_data, sizeof *numamem); + build_srat_memory(numamem, mem_base, mem_len, i - 1, + MEM_AFFINITY_ENABLED); + } } slots = (table_data->len - numa_start) / sizeof *numamem; for (; slots < pcms->numa_nodes + 2; slots++) { From 16e2841d2032347b9d22e5b37ac852771da9cc3b Mon Sep 17 00:00:00 2001 From: Dou Liyang Date: Tue, 10 Jul 2018 16:58:02 +0800 Subject: [PATCH 2367/2380] tests/acpi-test: update ACPI tables test blobs Now, QEmu adds a new check for memory-less NUMA nodes in build_srat(). It effects the ACPI test. So, Update ACPI tables test blobs. Signed-off-by: Dou Liyang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/acpi-test-data/pc/SRAT.numamem | Bin 224 -> 224 bytes tests/acpi-test-data/q35/SRAT.numamem | Bin 224 -> 224 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/acpi-test-data/pc/SRAT.numamem b/tests/acpi-test-data/pc/SRAT.numamem index dbc595d9cb85d3fcb5a4243153f42bb431c9de8f..119922f4973f621602047d1dc160519f810922a3 100644 GIT binary patch delta 24 gcmaFB_<)fsILI;N0RsaAt<8 diff --git a/tests/acpi-test-data/q35/SRAT.numamem b/tests/acpi-test-data/q35/SRAT.numamem index dbc595d9cb85d3fcb5a4243153f42bb431c9de8f..119922f4973f621602047d1dc160519f810922a3 100644 GIT binary patch delta 24 gcmaFB_<)fsILI;N0RsaAt<8 From 10efd7e1088855dc019e6a248ac7f9b24af8dd26 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Mon, 30 Jul 2018 11:41:41 +0200 Subject: [PATCH 2368/2380] pc: acpi: fix memory hotplug regression by reducing stub SRAT entry size Commit 848a1cc1e (hw/acpi-build: build SRAT memory affinity structures for DIMM devices) broke the first dimm hotplug in following cases: 1: there is no coldplugged dimm in the last numa node but there is a coldplugged dimm in another node -m 4096,slots=4,maxmem=32G \ -object memory-backend-ram,id=m0,size=2G \ -device pc-dimm,memdev=m0,node=0 \ -numa node,nodeid=0 \ -numa node,nodeid=1 2: if order of dimms on CLI is: 1st plugged dimm in node1 2nd plugged dimm in node0 -m 4096,slots=4,maxmem=32G \ -object memory-backend-ram,size=2G,id=m0 \ -device pc-dimm,memdev=m0,node=1 \ -object memory-backend-ram,id=m1,size=2G \ -device pc-dimm,memdev=m1,node=0 \ -numa node,nodeid=0 \ -numa node,nodeid=1 (qemu) object_add memory-backend-ram,id=m2,size=1G (qemu) device_add pc-dimm,memdev=m2,node=0 the first DIMM hotplug to any node except the last one fails (Windows is unable to online it). Length reduction of stub hotplug memory SRAT entry, fixes issue for some reason. RHBZ: 1609234 Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index c584642e4e..e1ee8ae9e0 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2269,7 +2269,16 @@ static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base, numamem = acpi_data_push(table_data, sizeof *numamem); if (!info) { - build_srat_memory(numamem, cur, end - cur, default_node, + /* + * Entry is required for Windows to enable memory hotplug in OS + * and for Linux to enable SWIOTLB when booted with less than + * 4G of RAM. Windows works better if the entry sets proximity + * to the highest NUMA node in the machine at the end of the + * reserved space. + * Memory devices may override proximity set by this entry, + * providing _PXM method if necessary. + */ + build_srat_memory(numamem, end - 1, 1, default_node, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); break; } @@ -2405,14 +2414,6 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); } - /* - * Entry is required for Windows to enable memory hotplug in OS - * and for Linux to enable SWIOTLB when booted with less than - * 4G of RAM. Windows works better if the entry sets proximity - * to the highest NUMA node in the machine. - * Memory devices may override proximity set by this entry, - * providing _PXM method if necessary. - */ if (hotplugabble_address_space_size) { build_srat_hotpluggable_memory(table_data, machine->device_memory->base, hotplugabble_address_space_size, From 1c707d6990eaf3db0c9dc4557bba5458977e9f8d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 3 Aug 2018 11:34:12 +0300 Subject: [PATCH 2369/2380] tests/acpi: update tables after memory hotplug changes Previous patch changes acpi tables, update expected files accordingly. Signed-off-by: Michael S. Tsirkin --- tests/acpi-test-data/pc/SRAT.dimmpxm | Bin 472 -> 472 bytes tests/acpi-test-data/pc/SRAT.memhp | Bin 264 -> 264 bytes tests/acpi-test-data/q35/SRAT.dimmpxm | Bin 472 -> 472 bytes tests/acpi-test-data/q35/SRAT.memhp | Bin 264 -> 264 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/acpi-test-data/pc/SRAT.dimmpxm b/tests/acpi-test-data/pc/SRAT.dimmpxm index 3b10a607d5bba6cebb97d9174c2a54a577bba9a8..5aa6f693ef9819b3d30cbd76cafb0ee8b5fc5534 100644 GIT binary patch delta 52 qcmcb?e1n-QILI;N1|tIllhXO|Ns9T$YTIu7+{7mfHVMsX$lem delta 52 scmcb?e1n-QILI;N1|tIllhUn7#IYAJP>#Rp%@sLVGJM*038_z5&!@I diff --git a/tests/acpi-test-data/pc/SRAT.memhp b/tests/acpi-test-data/pc/SRAT.memhp index e508b4ae3cd9e3000209a4f9597913faa4206ec1..5de8a100a4adf968b79a7b154a7f98123d583474 100644 GIT binary patch delta 51 pcmeBR>R{pu4ss0PU}RumTso2Kp2GkC|Gxt{3?K{x%rFL!1^_r@2$}!@ delta 51 pcmeBR>R{pu4ss0PU}RumTr`pEo&p09068G=141z{FvA!?8UWLX1)2Z= diff --git a/tests/acpi-test-data/q35/SRAT.dimmpxm b/tests/acpi-test-data/q35/SRAT.dimmpxm index 3b10a607d5bba6cebb97d9174c2a54a577bba9a8..5aa6f693ef9819b3d30cbd76cafb0ee8b5fc5534 100644 GIT binary patch delta 52 qcmcb?e1n-QILI;N1|tIllhXO|Ns9T$YTIu7+{7mfHVMsX$lem delta 52 scmcb?e1n-QILI;N1|tIllhUn7#IYAJP>#Rp%@sLVGJM*038_z5&!@I diff --git a/tests/acpi-test-data/q35/SRAT.memhp b/tests/acpi-test-data/q35/SRAT.memhp index e508b4ae3cd9e3000209a4f9597913faa4206ec1..5de8a100a4adf968b79a7b154a7f98123d583474 100644 GIT binary patch delta 51 pcmeBR>R{pu4ss0PU}RumTso2Kp2GkC|Gxt{3?K{x%rFL!1^_r@2$}!@ delta 51 pcmeBR>R{pu4ss0PU}RumTr`pEo&p09068G=141z{FvA!?8UWLX1)2Z= From 1fb57da72ae0886eba1234a2d98ddd10e88a9efc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 5 Aug 2018 16:32:58 -0700 Subject: [PATCH 2370/2380] tcg/optimize: Do not skip default processing of dup_vec If we do not opimize away dup_vec, we must mark its output as changed. Fixes: 170ba88f45b Reported-by: Laurent Desnogues Signed-off-by: Richard Henderson Reviewed-by: Laurent Desnogues Tested-by: Laurent Desnogues Message-id: 20180805233258.31892-1-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- tcg/optimize.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index d4ea67e541..5dbe11c3c8 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1094,9 +1094,9 @@ void tcg_optimize(TCGContext *s) tmp = arg_info(op->args[1])->val; tmp = dup_const(TCGOP_VECE(op), tmp); tcg_opt_gen_movi(s, op, op->args[0], tmp); - continue; + break; } - break; + goto do_default; CASE_OP_32_64(not): CASE_OP_32_64(neg): From 78e9ddd75e79159ccb5f3506ac0359ce0df28183 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Aug 2018 13:34:41 +0100 Subject: [PATCH 2371/2380] hw/intc/arm_gicv3_common: Give no-migration-shift-bug subsection a needed function Currently the migration code incorrectly treats a subsection with no .needed function pointer as if it was the subsection list terminator -- it is ignored and so is everything after it. Work around this by giving vmstate_gicv3_gicd_no_migration_shift_bug a 'needed' function that always returns true. Signed-off-by: Peter Maydell Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Richard Henderson Message-id: 20180806123445.1459-2-peter.maydell@linaro.org --- hw/intc/arm_gicv3_common.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index ff326b374a..e58bc8b810 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -203,10 +203,16 @@ static int gicv3_gicd_no_migration_shift_bug_post_load(void *opaque, return 0; } +static bool needed_always(void *opaque) +{ + return true; +} + const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = { .name = "arm_gicv3/gicd_no_migration_shift_bug", .version_id = 1, .minimum_version_id = 1, + .needed = needed_always, .pre_load = gicv3_gicd_no_migration_shift_bug_pre_load, .post_load = gicv3_gicd_no_migration_shift_bug_post_load, .fields = (VMStateField[]) { From adc4fda6d564a9a7e3fff4bdafcee131fdac133a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Aug 2018 13:34:42 +0100 Subject: [PATCH 2372/2380] hw/intc/arm_gicv3_common: Combine duplicate .subsections in vmstate_gicv3_cpu Commit 6692aac411199064 accidentally introduced a second initialization of the .subsections field of vmstate_gicv3_cpu, instead of adding the new subsection to the existing list. The effect of this was probably that migration of GICv3 with virtualization enabled was broken (or alternatively that migration of ICC_SRE_EL1 was broken, depending on which of the two initializers the compiler used). Combine the two into a single list. Signed-off-by: Peter Maydell Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Richard Henderson Message-id: 20180806123445.1459-3-peter.maydell@linaro.org --- hw/intc/arm_gicv3_common.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index e58bc8b810..e1a8999cf5 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -134,9 +134,6 @@ static const VMStateDescription vmstate_gicv3_cpu = { }, .subsections = (const VMStateDescription * []) { &vmstate_gicv3_cpu_virt, - NULL - }, - .subsections = (const VMStateDescription * []) { &vmstate_gicv3_cpu_sre_el1, NULL } From 7b69454a12510dc84b71c292214c364e499609e7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Aug 2018 13:34:43 +0100 Subject: [PATCH 2373/2380] target/arm: Add dummy needed functions to M profile vmstate subsections Currently the migration code incorrectly treats a subsection with no .needed function pointer as if it was the subsection list terminator -- it is ignored and so is everything after it. Work around this by giving various M profile vmstate structs a 'needed' function that always returns true. We reuse m_needed() for this, since it's always true here. Signed-off-by: Peter Maydell Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Richard Henderson Message-id: 20180806123445.1459-4-peter.maydell@linaro.org --- target/arm/machine.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/machine.c b/target/arm/machine.c index 2e28d086bd..ff4ec22bf7 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -184,6 +184,7 @@ static const VMStateDescription vmstate_m_faultmask_primask = { .name = "cpu/m/faultmask-primask", .version_id = 1, .minimum_version_id = 1, + .needed = m_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.v7m.faultmask[M_REG_NS], ARMCPU), VMSTATE_UINT32(env.v7m.primask[M_REG_NS], ARMCPU), @@ -230,6 +231,7 @@ static const VMStateDescription vmstate_m_scr = { .name = "cpu/m/scr", .version_id = 1, .minimum_version_id = 1, + .needed = m_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.v7m.scr[M_REG_NS], ARMCPU), VMSTATE_END_OF_LIST() @@ -240,6 +242,7 @@ static const VMStateDescription vmstate_m_other_sp = { .name = "cpu/m/other-sp", .version_id = 1, .minimum_version_id = 1, + .needed = m_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.v7m.other_sp, ARMCPU), VMSTATE_END_OF_LIST() From 326049cc8e6079d1ea49dc4b415b8112f0c995bd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Aug 2018 13:34:44 +0100 Subject: [PATCH 2374/2380] hw/intc/arm_gicv3_common: Move post_load hooks to top-level VMSD Contrary to the the impression given in docs/devel/migration.rst, the migration code does not run the pre_load hook for a subsection unless the subsection appears on the wire, and so this is not a place where you can set the default value for state for the "subsection not present" case. Instead this needs to be done in a pre_load hook for whatever is the parent VMSD of the subsection. We got this wrong in two of the subsection definitions in the GICv3 migration structs; fix this. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Dr. David Alan Gilbert Message-id: 20180806123445.1459-5-peter.maydell@linaro.org --- hw/intc/arm_gicv3_common.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index e1a8999cf5..8175889f1e 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -73,7 +73,7 @@ static const VMStateDescription vmstate_gicv3_cpu_virt = { } }; -static int icc_sre_el1_reg_pre_load(void *opaque) +static int vmstate_gicv3_cpu_pre_load(void *opaque) { GICv3CPUState *cs = opaque; @@ -97,7 +97,6 @@ const VMStateDescription vmstate_gicv3_cpu_sre_el1 = { .name = "arm_gicv3_cpu/sre_el1", .version_id = 1, .minimum_version_id = 1, - .pre_load = icc_sre_el1_reg_pre_load, .needed = icc_sre_el1_reg_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(icc_sre_el1, GICv3CPUState), @@ -109,6 +108,7 @@ static const VMStateDescription vmstate_gicv3_cpu = { .name = "arm_gicv3_cpu", .version_id = 1, .minimum_version_id = 1, + .pre_load = vmstate_gicv3_cpu_pre_load, .fields = (VMStateField[]) { VMSTATE_UINT32(level, GICv3CPUState), VMSTATE_UINT32(gicr_ctlr, GICv3CPUState), @@ -139,7 +139,7 @@ static const VMStateDescription vmstate_gicv3_cpu = { } }; -static int gicv3_gicd_no_migration_shift_bug_pre_load(void *opaque) +static int gicv3_pre_load(void *opaque) { GICv3State *cs = opaque; @@ -210,7 +210,6 @@ const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = { .version_id = 1, .minimum_version_id = 1, .needed = needed_always, - .pre_load = gicv3_gicd_no_migration_shift_bug_pre_load, .post_load = gicv3_gicd_no_migration_shift_bug_post_load, .fields = (VMStateField[]) { VMSTATE_BOOL(gicd_no_migration_shift_bug, GICv3State), @@ -222,6 +221,7 @@ static const VMStateDescription vmstate_gicv3 = { .name = "arm_gicv3", .version_id = 1, .minimum_version_id = 1, + .pre_load = gicv3_pre_load, .pre_save = gicv3_pre_save, .post_load = gicv3_post_load, .priority = MIG_PRI_GICV3, From 341823c172168a6d2765937aaea635f5136c5aa9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Aug 2018 13:34:45 +0100 Subject: [PATCH 2375/2380] hw/intc/arm_gicv3_common: Move gicd shift bug handling to gicv3_post_load The code currently in gicv3_gicd_no_migration_shift_bug_post_load() that handles migration from older QEMU versions with a particular bug is misplaced. We need to run this after migration in all cases, not just the cases where the "arm_gicv3/gicd_no_migration_shift_bug" subsection is present, so it must go in a post_load hook for the top level VMSD, not for the subsection. Move it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Dr. David Alan Gilbert Message-id: 20180806123445.1459-6-peter.maydell@linaro.org --- hw/intc/arm_gicv3_common.c | 77 ++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 8175889f1e..52480c3b4c 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -29,6 +29,41 @@ #include "hw/arm/linux-boot-if.h" #include "sysemu/kvm.h" + +static void gicv3_gicd_no_migration_shift_bug_post_load(GICv3State *cs) +{ + if (cs->gicd_no_migration_shift_bug) { + return; + } + + /* Older versions of QEMU had a bug in the handling of state save/restore + * to the KVM GICv3: they got the offset in the bitmap arrays wrong, + * so that instead of the data for external interrupts 32 and up + * starting at bit position 32 in the bitmap, it started at bit + * position 64. If we're receiving data from a QEMU with that bug, + * we must move the data down into the right place. + */ + memmove(cs->group, (uint8_t *)cs->group + GIC_INTERNAL / 8, + sizeof(cs->group) - GIC_INTERNAL / 8); + memmove(cs->grpmod, (uint8_t *)cs->grpmod + GIC_INTERNAL / 8, + sizeof(cs->grpmod) - GIC_INTERNAL / 8); + memmove(cs->enabled, (uint8_t *)cs->enabled + GIC_INTERNAL / 8, + sizeof(cs->enabled) - GIC_INTERNAL / 8); + memmove(cs->pending, (uint8_t *)cs->pending + GIC_INTERNAL / 8, + sizeof(cs->pending) - GIC_INTERNAL / 8); + memmove(cs->active, (uint8_t *)cs->active + GIC_INTERNAL / 8, + sizeof(cs->active) - GIC_INTERNAL / 8); + memmove(cs->edge_trigger, (uint8_t *)cs->edge_trigger + GIC_INTERNAL / 8, + sizeof(cs->edge_trigger) - GIC_INTERNAL / 8); + + /* + * While this new version QEMU doesn't have this kind of bug as we fix it, + * so it needs to set the flag to true to indicate that and it's necessary + * for next migration to work from this new version QEMU. + */ + cs->gicd_no_migration_shift_bug = true; +} + static int gicv3_pre_save(void *opaque) { GICv3State *s = (GICv3State *)opaque; @@ -46,6 +81,8 @@ static int gicv3_post_load(void *opaque, int version_id) GICv3State *s = (GICv3State *)opaque; ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s); + gicv3_gicd_no_migration_shift_bug_post_load(s); + if (c->post_load) { c->post_load(s); } @@ -161,45 +198,6 @@ static int gicv3_pre_load(void *opaque) return 0; } -static int gicv3_gicd_no_migration_shift_bug_post_load(void *opaque, - int version_id) -{ - GICv3State *cs = opaque; - - if (cs->gicd_no_migration_shift_bug) { - return 0; - } - - /* Older versions of QEMU had a bug in the handling of state save/restore - * to the KVM GICv3: they got the offset in the bitmap arrays wrong, - * so that instead of the data for external interrupts 32 and up - * starting at bit position 32 in the bitmap, it started at bit - * position 64. If we're receiving data from a QEMU with that bug, - * we must move the data down into the right place. - */ - memmove(cs->group, (uint8_t *)cs->group + GIC_INTERNAL / 8, - sizeof(cs->group) - GIC_INTERNAL / 8); - memmove(cs->grpmod, (uint8_t *)cs->grpmod + GIC_INTERNAL / 8, - sizeof(cs->grpmod) - GIC_INTERNAL / 8); - memmove(cs->enabled, (uint8_t *)cs->enabled + GIC_INTERNAL / 8, - sizeof(cs->enabled) - GIC_INTERNAL / 8); - memmove(cs->pending, (uint8_t *)cs->pending + GIC_INTERNAL / 8, - sizeof(cs->pending) - GIC_INTERNAL / 8); - memmove(cs->active, (uint8_t *)cs->active + GIC_INTERNAL / 8, - sizeof(cs->active) - GIC_INTERNAL / 8); - memmove(cs->edge_trigger, (uint8_t *)cs->edge_trigger + GIC_INTERNAL / 8, - sizeof(cs->edge_trigger) - GIC_INTERNAL / 8); - - /* - * While this new version QEMU doesn't have this kind of bug as we fix it, - * so it needs to set the flag to true to indicate that and it's necessary - * for next migration to work from this new version QEMU. - */ - cs->gicd_no_migration_shift_bug = true; - - return 0; -} - static bool needed_always(void *opaque) { return true; @@ -210,7 +208,6 @@ const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = { .version_id = 1, .minimum_version_id = 1, .needed = needed_always, - .post_load = gicv3_gicd_no_migration_shift_bug_post_load, .fields = (VMStateField[]) { VMSTATE_BOOL(gicd_no_migration_shift_bug, GICv3State), VMSTATE_END_OF_LIST() From 09d98b69804cfd9eccadb97951e8df64ada3bc7a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 19 Jul 2018 15:02:00 +0200 Subject: [PATCH 2376/2380] target/xtensa/cpu: Set owner of memory region in xtensa_cpu_initfn The instance_init function of the xtensa CPUs creates a memory region, but does not set an owner, so the memory region is not destroyed correctly when the CPU object is removed. This can happen when introspecting the CPU devices, so introspecting the CPU device will leave a dangling memory region object in the QOM tree. Make sure to set the right owner here to fix this issue. Signed-off-by: Thomas Huth Acked-by: Max Filippov Message-id: 1532005320-17794-1-git-send-email-thuth@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/xtensa/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index b50c840e09..590813d4f7 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -149,7 +149,7 @@ static void xtensa_cpu_initfn(Object *obj) #ifndef CONFIG_USER_ONLY env->address_space_er = g_malloc(sizeof(*env->address_space_er)); env->system_er = g_malloc(sizeof(*env->system_er)); - memory_region_init_io(env->system_er, NULL, NULL, env, "er", + memory_region_init_io(env->system_er, obj, NULL, env, "er", UINT64_C(0x100000000)); address_space_init(env->address_space_er, env->system_er, "ER"); #endif From 09b94ac0f29db3b022a77a5aa50dc9e37032689d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Aug 2018 12:45:01 +0100 Subject: [PATCH 2377/2380] slirp: Correct size check in m_inc() The data in an mbuf buffer is not necessarily at the start of the allocated buffer. (For instance m_adj() allows data to be trimmed from the start by just advancing the pointer and reducing the length.) This means that the allocated buffer size (m->m_size) and the amount of space from the m_data pointer to the end of the buffer (M_ROOM(m)) are not necessarily the same. Commit 864036e251f54c9 tried to change the m_inc() function from taking the new allocated-buffer-size to taking the new room-size, but forgot to change the initial "do we already have enough space" check. This meant that if we were trying to extend a buffer which had a leading gap between the buffer start and the data, we might incorrectly decide it didn't need to be extended, and then overrun the end of the buffer, causing memory corruption and an eventual crash. Change the "already big enough?" condition from checking the argument against m->m_size to checking against M_ROOM(). This only makes a difference for the callsite in m_cat(); the other three callsites all start with a freshly allocated mbuf from m_get(), which will have m->m_size == M_ROOM(m). Fixes: 864036e251f54c9 Fixes: https://bugs.launchpad.net/qemu/+bug/1785670 Signed-off-by: Peter Maydell Reviewed-by: Samuel Thibault Message-id: 20180807114501.12370-1-peter.maydell@linaro.org Tested-by: Dr. David Alan Gilbert --- slirp/mbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slirp/mbuf.c b/slirp/mbuf.c index 0c189e1a7b..1b7868355a 100644 --- a/slirp/mbuf.c +++ b/slirp/mbuf.c @@ -154,7 +154,7 @@ m_inc(struct mbuf *m, int size) int datasize; /* some compilers throw up on gotos. This one we can fake. */ - if (m->m_size > size) { + if (M_ROOM(m) > size) { return; } From 93f874fe9dbe0b997b5a9459840957efd13d7191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 3 Aug 2018 17:32:35 +0200 Subject: [PATCH 2378/2380] virtio-gpu: fix crashes upon warm reboot with vga mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With vga=775 on the Linux command line a first boot of the VM running Linux works fine. After a warm reboot it crashes during Linux boot. Before that, valgrind points out bad memory write to console surface. The VGA code is not aware that virtio-gpu got a message surface scanout when the display is disabled. Let's reset VGA graphic mode when it is the case, so that a new display surface is created when doing further VGA operations. https://bugs.launchpad.net/qemu/+bug/1784900/ Reported-by: Stefan Berger Signed-off-by: Marc-André Lureau Reviewed-by: Michael S. Tsirkin Reviewed-by: Gerd Hoffmann Tested-by: Stefan Berger Message-id: 20180803153235.4134-1-marcandre.lureau@redhat.com Signed-off-by: Peter Maydell --- hw/display/virtio-gpu.c | 5 +++++ hw/display/virtio-vga.c | 11 +++++++++++ include/hw/virtio/virtio-gpu.h | 1 + 3 files changed, 17 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index ec366f4c35..3ddd29c0de 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -421,6 +421,11 @@ static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) scanout->height ?: 480, "Guest disabled display."); } + + if (g->disable_scanout) { + g->disable_scanout(g, scanout_id); + } + dpy_gfx_replace_surface(scanout->con, ds); scanout->resource_id = 0; scanout->ds = NULL; diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 8d3d9e14a7..701d980872 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -75,6 +75,16 @@ static void virtio_vga_gl_block(void *opaque, bool block) } } +static void virtio_vga_disable_scanout(VirtIOGPU *g, int scanout_id) +{ + VirtIOVGA *vvga = container_of(g, VirtIOVGA, vdev); + + if (scanout_id == 0) { + /* reset surface if needed */ + vvga->vga.graphic_mode = -1; + } +} + static const GraphicHwOps virtio_vga_ops = { .invalidate = virtio_vga_invalidate_display, .gfx_update = virtio_vga_update_display, @@ -156,6 +166,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) vvga->vga_mrs, true); vga->con = g->scanout[0].con; + g->disable_scanout = virtio_vga_disable_scanout; graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga); for (i = 0; i < g->conf.max_outputs; i++) { diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 9780f755ef..d0321672f4 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -125,6 +125,7 @@ typedef struct VirtIOGPU { uint32_t bytes_3d; } stats; + void (*disable_scanout)(struct VirtIOGPU *g, int scanout_id); Error *migration_blocker; } VirtIOGPU; From 6ad90805383e6d04b3ff49681b8519a48c9f4410 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Aug 2018 17:26:17 +0100 Subject: [PATCH 2379/2380] Update version for v3.0.0-rc4 release Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 9613415721..e04ef0cdd5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.93 +2.12.94 From 38441756b70eec5807b5f60dad11a93a91199866 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 14 Aug 2018 16:38:43 +0100 Subject: [PATCH 2380/2380] Update version for v3.0.0 release Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index e04ef0cdd5..4a36342fca 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.94 +3.0.0